You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openpgp-card-app/sdk/usbd_ccid_if.c

591 lines
19 KiB
C

/**
******************************************************************************
* @file usbd_ccid_if.c
* @author MCD Application Team
* @version V1.0.1
* @date 31-January-2014
* @brief This file provides all the functions for USB Interface for CCID
******************************************************************************
* @attention
*
* <h2><center>&copy; COPYRIGHT 2014 STMicroelectronics</center></h2>
*
* Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.st.com/software_license_agreement_liberty_v2
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "usbd_ccid_if.h"
#ifdef HAVE_USB_CLASS_CCID
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
uint8_t Ccid_BulkState;
uint8_t ccid_card_inserted;
uint8_t UsbIntMessageBuffer[INTR_MAX_PACKET_SIZE]; /* data buffer*/
__IO uint8_t PrevXferComplete_IntrIn;
usb_ccid_param_t usb_ccid_param;
uint8_t* pUsbMessageBuffer;
static uint32_t UsbMessageLength;
Ccid_SlotStatus_t Ccid_SlotStatus;
Protocol0_DataStructure_t Protocol0_DataStructure;
Ccid_bulk_data_t Ccid_bulk_data;
/* Private function prototypes -----------------------------------------------*/
static void CCID_Response_SendData (USBD_HandleTypeDef *pdev,
uint8_t* pbuf,
uint16_t len);
/* Private function ----------------------------------------------------------*/
/**
* @brief CCID_Init
* Initialize the CCID USB Layer
* @param pdev: device instance
* @retval None
*/
void CCID_Init (USBD_HandleTypeDef *pdev)
{
memset(&Ccid_BulkState, 0, sizeof(Ccid_BulkState));
memset(&UsbIntMessageBuffer, 0, sizeof(UsbIntMessageBuffer));
memset(&PrevXferComplete_IntrIn, 0, sizeof(PrevXferComplete_IntrIn));
memset(&usb_ccid_param, 0, sizeof(usb_ccid_param));
memset(&pUsbMessageBuffer, 0, sizeof(pUsbMessageBuffer));
memset(&UsbMessageLength, 0, sizeof(UsbMessageLength));
memset(&Ccid_SlotStatus, 0, sizeof(Ccid_SlotStatus));
memset(&Protocol0_DataStructure, 0, sizeof(Protocol0_DataStructure));
memset(&Ccid_bulk_data, 0, sizeof(Ccid_bulk_data));
ccid_card_inserted = 0;
/* CCID Related Initialization */
CCID_SetIntrTransferStatus(1); /* Transfer Complete Status */
CCID_UpdSlotChange(1);
SC_InitParams();
/* Prepare Out endpoint to receive 1st packet */
Ccid_BulkState = CCID_STATE_IDLE;
USBD_LL_PrepareReceive(pdev, CCID_BULK_OUT_EP, CCID_BULK_EPOUT_SIZE);
// send the smartcard as inserted state at boot time
io_usb_ccid_set_card_inserted(1);
}
/**
* @brief CCID_DeInit
* Uninitialize the CCID Machine
* @param pdev: device instance
* @retval None
*/
void CCID_DeInit (USBD_HandleTypeDef *pdev)
{
UNUSED(pdev);
Ccid_BulkState = CCID_STATE_IDLE;
}
/**
* @brief CCID_Message_In
* Handle Bulk IN & Intr IN data stage
* @param pdev: device instance
* @param uint8_t epnum: endpoint index
* @retval None
*/
void CCID_BulkMessage_In (USBD_HandleTypeDef *pdev,
uint8_t epnum)
{
if (epnum == (CCID_BULK_IN_EP & 0x7F))
{/* Filter the epnum by masking with 0x7f (mask of IN Direction) */
/*************** Handle Bulk Transfer IN data completion *****************/
switch (Ccid_BulkState)
{
case CCID_STATE_SEND_RESP: {
unsigned int remLen = UsbMessageLength;
// advance with acknowledged sent chunk
pUsbMessageBuffer += MIN(CCID_BULK_EPIN_SIZE, UsbMessageLength);
UsbMessageLength -= MIN(CCID_BULK_EPIN_SIZE, UsbMessageLength);
// if remaining length is > EPIN_SIZE: send a filled bulk packet
if (UsbMessageLength >= CCID_BULK_EPIN_SIZE) {
CCID_Response_SendData(pdev, pUsbMessageBuffer,
// use the header declared size packet must be well formed
CCID_BULK_EPIN_SIZE);
}
// if remaining length is 0; send an empty packet and prepare to receive a new command
else if (UsbMessageLength == 0 && remLen == CCID_BULK_EPIN_SIZE) {
CCID_Response_SendData(pdev, pUsbMessageBuffer,
// use the header declared size packet must be well formed
0);
goto last_xfer; // won't wait ack to avoid missing a command
}
// else if no more data, then last packet sent, go back to idle (done on transfer ack)
else if (UsbMessageLength == 0) { // robustness only
last_xfer:
Ccid_BulkState = CCID_STATE_IDLE;
/* Prepare EP to Receive First Cmd */
USBD_LL_PrepareReceive(pdev, CCID_BULK_OUT_EP, CCID_BULK_EPOUT_SIZE);
}
// if remaining length is < EPIN_SIZE: send packet and prepare to receive a new command
else if (UsbMessageLength < CCID_BULK_EPIN_SIZE) {
CCID_Response_SendData(pdev, pUsbMessageBuffer,
// use the header declared size packet must be well formed
UsbMessageLength);
goto last_xfer; // won't wait ack to avoid missing a command
}
break;
}
default:
break;
}
}
else if (epnum == (CCID_INTR_IN_EP & 0x7F))
{
/* Filter the epnum by masking with 0x7f (mask of IN Direction) */
CCID_SetIntrTransferStatus(1); /* Transfer Complete Status */
}
}
/**
* @brief CCID_BulkMessage_Out
* Proccess CCID OUT data
* @param pdev: device instance
* @param uint8_t epnum: endpoint index
* @retval None
*/
void CCID_BulkMessage_Out (USBD_HandleTypeDef *pdev,
uint8_t epnum, uint8_t* buffer, uint16_t dataLen)
{
switch (Ccid_BulkState)
{
case CCID_STATE_IDLE:
if (dataLen == 0x00)
{ /* Zero Length Packet Received */
Ccid_BulkState = CCID_STATE_IDLE;
}
else if (dataLen >= CCID_MESSAGE_HEADER_SIZE)
{
UsbMessageLength = dataLen; /* Store for future use */
/* Expected Data Length Packet Received */
pUsbMessageBuffer = (uint8_t*) &Ccid_bulk_data;
/* Fill CCID_BulkOut Data Buffer from USB Buffer */
memmove(pUsbMessageBuffer, buffer, dataLen);
/*
Refer : 6 CCID Messages
The response messages always contain the exact same slot number,
and sequence number fields from the header that was contained in
the Bulk-OUT command message.
*/
Ccid_bulk_data.header.bulkin.bSlot = Ccid_bulk_data.header.bulkout.bSlot;
Ccid_bulk_data.header.bulkin.bSeq = Ccid_bulk_data.header.bulkout.bSeq;
if (dataLen < CCID_BULK_EPOUT_SIZE)
{/* Short message, less than the EP Out Size, execute the command,
if parameter like dwLength is too big, the appropriate command will
give an error */
CCID_CmdDecode(pdev);
}
else
{ /* Long message, receive additional data with command */
/* (u8dataLen == CCID_BULK_EPOUT_SIZE) */
if (Ccid_bulk_data.header.bulkout.dwLength > ABDATA_SIZE)
{ /* Check if length of data to be sent by host is > buffer size */
/* Too long data received.... Error ! */
Ccid_BulkState = CCID_STATE_UNCORRECT_LENGTH;
}
else
{ /* Expect more data on OUT EP */
Ccid_BulkState = CCID_STATE_RECEIVE_DATA;
pUsbMessageBuffer += dataLen; /* Point to new offset */
/* Prepare EP to Receive next Cmd */
USBD_LL_PrepareReceive(pdev, CCID_BULK_OUT_EP, CCID_BULK_EPOUT_SIZE);
} /* if (dataLen == CCID_BULK_EPOUT_SIZE) ends */
} /* if (dataLen >= CCID_BULK_EPOUT_SIZE) ends */
} /* if (dataLen >= CCID_MESSAGE_HEADER_SIZE) ends */
break;
case CCID_STATE_RECEIVE_DATA:
UsbMessageLength += dataLen;
if (dataLen < CCID_BULK_EPOUT_SIZE)
{/* Short message, less than the EP Out Size, execute the command,
if parameter like dwLength is too big, the appropriate command will
give an error */
/* Full command is received, process the Command */
memmove(pUsbMessageBuffer, buffer, dataLen);
CCID_CmdDecode(pdev);
}
else if (dataLen == CCID_BULK_EPOUT_SIZE)
{
if (UsbMessageLength < (Ccid_bulk_data.header.bulkout.dwLength + CCID_CMD_HEADER_SIZE))
{
memmove(pUsbMessageBuffer, buffer, dataLen);
pUsbMessageBuffer += dataLen;
/* Increment the pointer to receive more data */
/* Prepare EP to Receive next Cmd */
USBD_LL_PrepareReceive(pdev, CCID_BULK_OUT_EP, CCID_BULK_EPOUT_SIZE);
}
else if (UsbMessageLength == (Ccid_bulk_data.header.bulkout.dwLength + CCID_CMD_HEADER_SIZE))
{
/* Full command is received, process the Command */
memmove(pUsbMessageBuffer, buffer, dataLen);
CCID_CmdDecode(pdev);
}
else
{
/* Too long data received.... Error ! */
Ccid_BulkState = CCID_STATE_UNCORRECT_LENGTH;
}
}
break;
case CCID_STATE_UNCORRECT_LENGTH:
Ccid_BulkState = CCID_STATE_IDLE;
break;
default:
break;
}
}
void CCID_Send_Reply(USBD_HandleTypeDef *pdev) {
/********** Decide for all commands ***************/
if (Ccid_BulkState == CCID_STATE_SEND_RESP)
{
UsbMessageLength = Ccid_bulk_data.header.bulkin.dwLength+CCID_MESSAGE_HEADER_SIZE; /* Store for future use */
/* Expected Data Length Packet Received */
pUsbMessageBuffer = (uint8_t*) &Ccid_bulk_data;
CCID_Response_SendData(pdev, pUsbMessageBuffer,
// use the header declared size packet must be well formed
MIN(CCID_BULK_EPIN_SIZE, UsbMessageLength));
}
}
/**
* @brief CCID_CmdDecode
* Parse the commands and Proccess command
* @param pdev: device instance
* @retval None
*/
void CCID_CmdDecode(USBD_HandleTypeDef *pdev)
{
uint8_t errorCode;
switch (Ccid_bulk_data.header.bulkout.bMessageType)
{
case PC_TO_RDR_ICCPOWERON:
errorCode = PC_to_RDR_IccPowerOn();
RDR_to_PC_DataBlock(errorCode);
break;
case PC_TO_RDR_ICCPOWEROFF:
errorCode = PC_to_RDR_IccPowerOff();
RDR_to_PC_SlotStatus(errorCode);
break;
case PC_TO_RDR_GETSLOTSTATUS:
errorCode = PC_to_RDR_GetSlotStatus();
RDR_to_PC_SlotStatus(errorCode);
break;
case PC_TO_RDR_XFRBLOCK:
errorCode = PC_to_RDR_XfrBlock();
// asynchronous // RDR_to_PC_DataBlock(errorCode);
break;
case PC_TO_RDR_GETPARAMETERS:
errorCode = PC_to_RDR_GetParameters();
RDR_to_PC_Parameters(errorCode);
break;
case PC_TO_RDR_RESETPARAMETERS:
errorCode = PC_to_RDR_ResetParameters();
RDR_to_PC_Parameters(errorCode);
break;
case PC_TO_RDR_SETPARAMETERS:
errorCode = PC_to_RDR_SetParameters();
RDR_to_PC_Parameters(errorCode);
break;
case PC_TO_RDR_ESCAPE:
errorCode = PC_to_RDR_Escape();
RDR_to_PC_Escape(errorCode);
break;
case PC_TO_RDR_ICCCLOCK:
errorCode = PC_to_RDR_IccClock();
RDR_to_PC_SlotStatus(errorCode);
break;
case PC_TO_RDR_ABORT:
errorCode = PC_to_RDR_Abort();
RDR_to_PC_SlotStatus(errorCode);
break;
case PC_TO_RDR_T0APDU:
errorCode = PC_TO_RDR_T0Apdu();
RDR_to_PC_SlotStatus(errorCode);
break;
case PC_TO_RDR_MECHANICAL:
errorCode = PC_TO_RDR_Mechanical();
RDR_to_PC_SlotStatus(errorCode);
break;
case PC_TO_RDR_SETDATARATEANDCLOCKFREQUENCY:
errorCode = PC_TO_RDR_SetDataRateAndClockFrequency();
RDR_to_PC_DataRateAndClockFrequency(errorCode);
break;
case PC_TO_RDR_SECURE:
errorCode = PC_TO_RDR_Secure();
// asynchronous // RDR_to_PC_DataBlock(errorCode);
break;
default:
RDR_to_PC_SlotStatus(SLOTERROR_CMD_NOT_SUPPORTED);
break;
}
CCID_Send_Reply(pdev);
}
/**
* @brief Transfer_Data_Request
* Prepare the request response to be sent to the host
* @param uint8_t* dataPointer: Pointer to the data buffer to send
* @param uint16_t dataLen : number of bytes to send
* @retval None
*/
void Transfer_Data_Request(void)
{
/********** Update Global Variables ***************/
Ccid_BulkState = CCID_STATE_SEND_RESP;
}
/**
* @brief CCID_Response_SendData
* Send the data on bulk-in EP
* @param pdev: device instance
* @param uint8_t* buf: pointer to data buffer
* @param uint16_t len: Data Length
* @retval None
*/
static void CCID_Response_SendData(USBD_HandleTypeDef *pdev,
uint8_t* buf,
uint16_t len)
{
// don't ask the MCU to perform bulk split, we could quickly get into a buffer overflow
if (len > CCID_BULK_EPIN_SIZE) {
THROW(EXCEPTION_IO_OVERFLOW);
}
G_io_seproxyhal_spi_buffer[0] = SEPROXYHAL_TAG_USB_EP_PREPARE;
G_io_seproxyhal_spi_buffer[1] = (3+len)>>8;
G_io_seproxyhal_spi_buffer[2] = (3+len);
G_io_seproxyhal_spi_buffer[3] = CCID_BULK_IN_EP;
G_io_seproxyhal_spi_buffer[4] = SEPROXYHAL_TAG_USB_EP_PREPARE_DIR_IN;
G_io_seproxyhal_spi_buffer[5] = len;
io_seproxyhal_spi_send(G_io_seproxyhal_spi_buffer, 6);
io_seproxyhal_spi_send(buf, len);
}
/**
* @brief CCID_IntMessage
* Send the Interrupt-IN data to the host
* @param pdev: device instance
* @retval None
*/
void CCID_IntMessage(USBD_HandleTypeDef *pdev)
{
/* Check if there us change in Smartcard Slot status */
if ( CCID_IsSlotStatusChange() && CCID_IsIntrTransferComplete() )
{
/* Check Slot Status is changed. Card is Removed/ Fitted */
RDR_to_PC_NotifySlotChange();
CCID_SetIntrTransferStatus(0); /* Reset the Status */
CCID_UpdSlotChange(0); /* Reset the Status of Slot Change */
G_io_seproxyhal_spi_buffer[0] = SEPROXYHAL_TAG_USB_EP_PREPARE;
G_io_seproxyhal_spi_buffer[1] = (3+2)>>8;
G_io_seproxyhal_spi_buffer[2] = (3+2);
G_io_seproxyhal_spi_buffer[3] = CCID_INTR_IN_EP;
G_io_seproxyhal_spi_buffer[4] = SEPROXYHAL_TAG_USB_EP_PREPARE_DIR_IN;
G_io_seproxyhal_spi_buffer[5] = 2;
io_seproxyhal_spi_send(G_io_seproxyhal_spi_buffer, 6);
io_seproxyhal_spi_send(UsbIntMessageBuffer, 2);
}
}
/**
* @brief CCID_IsIntrTransferComplete
* Provides the status of previous Interrupt transfer status
* @param None
* @retval uint8_t PrevXferComplete_IntrIn: Value of the previous transfer status
*/
uint8_t CCID_IsIntrTransferComplete (void)
{
return PrevXferComplete_IntrIn;
}
/**
* @brief CCID_IsIntrTransferComplete
* Set the value of the Interrupt transfer status
* @param uint8_t xfer_Status: Value of the Interrupt transfer status to set
* @retval None
*/
void CCID_SetIntrTransferStatus (uint8_t xfer_Status)
{
PrevXferComplete_IntrIn = xfer_Status;
}
uint8_t SC_Detect(void) {
return ccid_card_inserted;
}
void SC_Poweroff(void) {
// nothing to do
}
void SC_InitParams (void) {
// nothing to do
}
uint8_t SC_SetParams (Protocol0_DataStructure_t* pt0) {
return SLOT_NO_ERROR;
}
uint8_t SC_ExecuteEscape (uint8_t* escapePtr, uint32_t escapeLen,
uint8_t* responseBuff,
uint16_t* responseLen) {
io_seproxyhal_se_reset();
}
uint8_t SC_SetClock (uint8_t bClockCommand) {
return SLOT_NO_ERROR;
}
uint8_t SC_Request_GetClockFrequencies(uint8_t* pbuf, uint16_t* len);
uint8_t SC_Request_GetDataRates(uint8_t* pbuf, uint16_t* len);
uint8_t SC_T0Apdu(uint8_t bmChanges, uint8_t bClassGetResponse,
uint8_t bClassEnvelope) {
return SLOTERROR_CMD_NOT_SUPPORTED;
}
uint8_t SC_Mechanical(uint8_t bFunction) {
return SLOTERROR_CMD_NOT_SUPPORTED;
}
uint8_t SC_SetDataRateAndClockFrequency(uint32_t dwClockFrequency,
uint32_t dwDataRate) {
return SLOT_NO_ERROR;
}
uint8_t SC_Secure(uint32_t dwLength, uint8_t bBWI, uint16_t wLevelParameter,
uint8_t* pbuf, uint32_t* returnLen ) {
// return SLOTERROR_CMD_NOT_SUPPORTED;
uint16_t ret_len,off;
switch(pbuf[0]) {
case 0: // verify pin
//ret_len = dwLength - 15;
ret_len = 5;
os_memmove(G_io_apdu_buffer, pbuf+15, 5);
break;
case 1: // modify pin
switch(pbuf[11]) {
case 3:
off = 20;
break;
case 2:
case 1:
off = 19;
break;
default:
off = 18;
break;
}
//ret_len = dwLength - off;
ret_len = 5;
os_memmove(G_io_apdu_buffer, pbuf+off, 5);
break;
default: // unsupported
Ccid_bulk_data.header.bulkin.dwLength = 0;
RDR_to_PC_DataBlock(SLOTERROR_CMD_NOT_SUPPORTED);
CCID_Send_Reply(&USBD_Device);
return SLOTERROR_CMD_NOT_SUPPORTED;
}
return SC_XferBlock(G_io_apdu_buffer, ret_len, &ret_len);
}
// prepare the apdu to be processed by the application
uint8_t SC_XferBlock (uint8_t* ptrBlock, uint32_t blockLen, uint16_t* expectedLen) {
// check for overflow
if (blockLen > IO_APDU_BUFFER_SIZE) {
return SLOTERROR_BAD_LENTGH;
}
// copy received apdu
memmove(G_io_apdu_buffer, ptrBlock, blockLen);
G_io_apdu_length = blockLen;
G_io_apdu_media = IO_APDU_MEDIA_USB_CCID; // for application code
G_io_apdu_state = APDU_USB_CCID; // for next call to io_exchange
return SLOT_NO_ERROR;
}
void io_usb_ccid_reply(unsigned char* buffer, unsigned short length) {
// avoid memory overflow
if (length > sizeof(Ccid_bulk_data.abData)) {
THROW(EXCEPTION_IO_OVERFLOW);
}
// copy the responde apdu
memmove(Ccid_bulk_data.abData, buffer, length);
Ccid_bulk_data.header.bulkin.dwLength = length;
// forge reply
RDR_to_PC_DataBlock(SLOT_NO_ERROR);
// start sending rpely
CCID_Send_Reply(&USBD_Device);
}
// ask for power on
void io_usb_ccid_set_card_inserted(unsigned int inserted) {
ccid_card_inserted = inserted;
CCID_UpdSlotChange(1);
CCID_IntMessage(&USBD_Device);
}
#endif // HAVE_USB_CLASS_CCID
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/