Files
simtrace2/firmware/libcommon/source/cciddriver.c
Kévin Redon 33d1eb73fd replace leading spaces with tabs
Change-Id: I86783eba0827b58303b10310e9f6b9625e1a27f1
2018-07-11 22:01:58 +02:00

1033 lines
32 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
/// \unit
///
/// !Purpose
///
/// CCID driver
///
/// !Usage
///
/// Explanation on the usage of the code made available through the header file.
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "board.h"
#include "simtrace.h"
#ifdef HAVE_CCID
#include <USBDDriver.h>
#include <USBRequests.h>
#include <USBDescriptors.h>
//#include <usb/device/dfu/dfu.h>
#include <cciddriverdescriptors.h>
// FIXME: Remove DFU related stuff
/* no DFU bootloader is being used */
#define DFU_NUM_IF 0
#define DFU_IF_DESCRIPTORS_STRUCT
#define DFU_IF_DESCRIPTORS
#define DFU_NUM_STRINGS 0
#define DFU_STRING_DESCRIPTORS
//------------------------------------------------------------------------------
// Local definition
//------------------------------------------------------------------------------
/// Constants: IDs: Device product ID.
//#define CCIDDriverDescriptors_PRODUCTID 0x6129
#define CCIDDriverDescriptors_PRODUCTID SIMTRACE_PRODUCT_ID
/// Constants: IDs: Device vendor ID.
#define CCIDDriverDescriptors_VENDORID ATMEL_VENDOR_ID
//#define CCIDDriverDescriptors_VENDORID 0x03EB
/// Constants: IDs: Device release number.
#define CCIDDriverDescriptors_RELEASE 0x0100
//------------------------------------------------------------------------------
// Types
//------------------------------------------------------------------------------
/// Driver structure for an CCID device
typedef struct {
/// CCID message
S_ccid_bulk_in_header sCcidMessage;
/// CCID command
S_ccid_bulk_out_header sCcidCommand;
/// Interrupt message answer
unsigned char BufferINT[4];
/// Buffer data of message
unsigned char ProtocolDataStructure[10];
/// Protocol used
unsigned char bProtocol;
/// SlotStatus
/// Bit 0 = Slot 0 current state
/// Bit 1 = Slot 0 changed status
/// Bit 2 = Slot 1 current state
/// Bit 3 = Slot 1 changed status
/// Bit 4 = Slot 2 current state
/// Bit 5 = Slot 2 changed status
unsigned char SlotStatus;
} CCIDDriver;
//------------------------------------------------------------------------------
// Local variables
//------------------------------------------------------------------------------
/// Static instance of the CCID device driver.
static CCIDDriver ccidDriver;
static CCIDDriverConfigurationDescriptors *configurationDescriptorsFS;
//------------------------------------------------------------------------------
// Internal functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Initializes the CCID device driver.
//------------------------------------------------------------------------------
void CCIDDriver_Initialize( void )
{
configurationDescriptorsFS = (CCIDDriverConfigurationDescriptors *) configurationDescriptorsArr[CFG_NUM_CCID-1];
}
//------------------------------------------------------------------------------
/// Response Pipe, Bulk-IN Messages
/// Return the Slot Status to the host
/// Answer to:
/// PC_to_RDR_IccPowerOff
/// PC_to_RDR_GetSlotStatus
/// PC_to_RDR_IccClock
/// PC_to_RDR_T0APDU
/// PC_to_RDR_Mechanical
/// PC_to_RDR_Abort and Class specific ABORT request
//------------------------------------------------------------------------------
static void RDRtoPCSlotStatus( void )
{
// Header fields settings
ccidDriver.sCcidMessage.bMessageType = RDR_TO_PC_SLOTSTATUS;
ccidDriver.sCcidMessage.wLength = 0;
if (ccidDriver.SlotStatus == ICC_INSERTED_EVENT) {
ccidDriver.sCcidMessage.bStatus = 0; /* ICC present and active card */
} else if (ccidDriver.SlotStatus == ICC_NOT_PRESENT) {
ccidDriver.sCcidMessage.bStatus = 2; /* No ICC present*/
} else{
TRACE_ERROR("Strange bStatus");
ccidDriver.sCcidMessage.bStatus = 0;
}
ccidDriver.sCcidMessage.bError = 0;
// 00h Clock running
// 01h Clock stopped in state L
// 02h Clock stopped in state H
// 03h Clock stopped in an unknown state
// All other values are Reserved for Future Use.
ccidDriver.sCcidMessage.bSpecific = 0;
}
//------------------------------------------------------------------------------
/// Response Pipe, Bulk-IN Messages
/// Answer to PC_to_RDR_IccPowerOn
//------------------------------------------------------------------------------
static void RDRtoPCDatablock_ATR( void )
{
unsigned char i;
unsigned char Atr[ATR_SIZE_MAX];
unsigned char length;
uint32_t status;
TRACE_DEBUG(".");
status = ISO7816_Datablock_ATR( Atr, &length );
ISO7816_Decode_ATR( Atr );
if (status == 0) {
TRACE_DEBUG("Timeout occured while reading ATR");
// FIXME: react properly to timeout..
// return;
}
// FIXME: More tests? Is bProtocol = Atr[3] ?
if( length > 5 ) {
ccidDriver.ProtocolDataStructure[1] = Atr[3]&0x0F; // TD(1)
ccidDriver.bProtocol = Atr[3]&0x0F; // TD(1)
TRACE_INFO("Protocol data structure: 0x%x\n\r",
ccidDriver.ProtocolDataStructure[1]);
}
// S_ccid_protocol_t0
// bmFindexDindex
ccidDriver.ProtocolDataStructure[0] = Atr[2]; // TA(1)
// bmTCCKST0
// For T=0 ,B0 0b, B7-2 000000b
// B1 Convention used (b1=0 for direct, b1=1 for inverse)
// bGuardTimeT0
// Extra Guardtime between two characters. Add 0 to 254 etu to the normal
// guardtime of 12etu. FFh is the same as 00h.
ccidDriver.ProtocolDataStructure[2] = Atr[4]; // TC(1)
// AT91C_BASE_US0->US_TTGR = 0; // TC1
// bWaitingIntegerT0
// WI for T=0 used to define WWT
ccidDriver.ProtocolDataStructure[3] = Atr[7]; // TC(2)
// bClockStop
// ICC Clock Stop Support
// 00 = Stopping the Clock is not allowed
// 01 = Stop with Clock signal Low
// 02 = Stop with Clock signal High
// 03 = Stop with Clock either High or Low
ccidDriver.ProtocolDataStructure[4] = 0x00; // 0 to 3
// Header fields settings
ccidDriver.sCcidMessage.bMessageType = RDR_TO_PC_DATABLOCK;
ccidDriver.sCcidMessage.wLength = length; // Size of ATR
ccidDriver.sCcidMessage.bSizeToSend += length; // Size of ATR
// bChainParameter: 00 the response APDU begins and ends in this command
ccidDriver.sCcidMessage.bSpecific = 0;
for( i=0; i<length; i++ ) {
ccidDriver.sCcidMessage.abData[i] = Atr[i];
}
// Set the slot to an active status
ccidDriver.sCcidMessage.bStatus = 0;
ccidDriver.sCcidMessage.bError = 0;
}
//------------------------------------------------------------------------------
/// Response Pipe, Bulk-IN Messages
/// In other cases, the response message has the following format:
/// The response data will contain the optional data returned by the ICC,
/// followed by the 2 byte-size status words SW1-SW2.
///
/// Answer to:
/// PC_to_RDR_XfrBlock
/// PC_to_RDR_Secure
//------------------------------------------------------------------------------
static void RDRtoPCDatablock( void )
{
//TRACE_DEBUG(".");
// Header fields settings
ccidDriver.sCcidMessage.bMessageType = RDR_TO_PC_DATABLOCK;
ccidDriver.sCcidMessage.bSizeToSend += ccidDriver.sCcidMessage.wLength;
// bChainParameter: 00 the response APDU begins and ends in this command
ccidDriver.sCcidMessage.bSpecific = 0;
// Set the slot to an active status
ccidDriver.sCcidMessage.bStatus = 0;
ccidDriver.sCcidMessage.bError = 0;
}
//------------------------------------------------------------------------------
/// Response Pipe, Bulk-IN Messages
/// Answer to:
/// PC_to_RDR_GetParameters
/// PC_to_RDR_ResetParameters
/// PC_to_RDR_SetParameters
//------------------------------------------------------------------------------
static void RDRtoPCParameters( void )
{
unsigned int i;
TRACE_DEBUG(".");
// Header fields settings
ccidDriver.sCcidMessage.bMessageType = RDR_TO_PC_PARAMETERS;
//ccidDriver.sCcidMessage.bStatus = 0;
ccidDriver.sCcidMessage.bError = 0;
if( ccidDriver.ProtocolDataStructure[1] == PROTOCOL_TO ) {
// T=0
ccidDriver.sCcidMessage.wLength = sizeof(S_ccid_protocol_t0);
ccidDriver.sCcidMessage.bSpecific = PROTOCOL_TO;
}
else {
// T=1
ccidDriver.sCcidMessage.wLength = sizeof(S_ccid_protocol_t1);
ccidDriver.sCcidMessage.bSpecific = PROTOCOL_T1;
}
ccidDriver.sCcidMessage.bSizeToSend += ccidDriver.sCcidMessage.wLength;
for( i=0; i<ccidDriver.sCcidMessage.wLength; i++ ) {
ccidDriver.sCcidMessage.abData[i] = ccidDriver.ProtocolDataStructure[i];
}
}
//------------------------------------------------------------------------------
/// Response Pipe, Bulk-IN Messages
/// Answer to:
/// PC_to_RDR_Escape
//------------------------------------------------------------------------------
static void RDRtoPCEscape( unsigned char length, unsigned char *data_send_from_CCID )
{
unsigned int i;
TRACE_DEBUG(".");
// Header fields settings
ccidDriver.sCcidMessage.bMessageType = RDR_TO_PC_ESCAPE;
ccidDriver.sCcidMessage.wLength = length;
ccidDriver.sCcidMessage.bStatus = 0;
ccidDriver.sCcidMessage.bError = 0;
ccidDriver.sCcidMessage.bSpecific = 0; // bRFU
for( i=0; i<length; i++ ) {
ccidDriver.sCcidMessage.abData[i] = data_send_from_CCID[i];
}
}
//------------------------------------------------------------------------------
/// Response Pipe, Bulk-IN Messages
/// Answer to:
/// PC_to_RDR_SetDataRateAndClockFrequency
//------------------------------------------------------------------------------
static void RDRtoPCDataRateAndClockFrequency( unsigned int dwClockFrequency,
unsigned int dwDataRate )
{
TRACE_DEBUG(".");
// Header fields settings
ccidDriver.sCcidMessage.bMessageType = RDR_TO_PC_DATARATEANDCLOCKFREQUENCY;
ccidDriver.sCcidMessage.wLength = 8;
ccidDriver.sCcidMessage.bStatus = 0;
ccidDriver.sCcidMessage.bError = 0;
ccidDriver.sCcidMessage.bSpecific = 0; // bRFU
ccidDriver.sCcidMessage.abData[0] = dwClockFrequency;
ccidDriver.sCcidMessage.abData[4] = dwDataRate;
}
//------------------------------------------------------------------------------
/// Command Pipe, Bulk-OUT Messages
/// Power On Command - Cold Reset & Warm Reset
/// Return the ATR to the host
//------------------------------------------------------------------------------
static void PCtoRDRIccPowerOn( void )
{
TRACE_DEBUG(".");
if( CCID_FEATURES_AUTO_VOLT == (configurationDescriptorsFS->ccid.dwFeatures & CCID_FEATURES_AUTO_VOLT) ) {
//bPowerSelect = ccidDriver.sCcidCommand.bSpecific_0;
ccidDriver.sCcidCommand.bSpecific_0 = VOLTS_AUTO;
}
ISO7816_warm_reset();
// ISO7816_cold_reset();
// for emulation only //JCB
if ( ccidDriver.sCcidCommand.bSpecific_0 != VOLTS_5_0 ) {
TRACE_ERROR("POWER_NOT_SUPPORTED\n\r");
}
else {
RDRtoPCDatablock_ATR();
}
}
//------------------------------------------------------------------------------
/// Command Pipe, Bulk-OUT Messages
/// Power Off Command - Set the ICC in an inactive state
/// Return the slot status to the host
//------------------------------------------------------------------------------
static void PCtoRDRIccPowerOff( void )
{
unsigned char bStatus;
TRACE_DEBUG(".");
ISO7816_IccPowerOff();
//JCB stub
bStatus = ICC_BS_PRESENT_NOTACTIVATED;
// Set the slot to an inactive status
ccidDriver.sCcidMessage.bStatus = 0;
ccidDriver.sCcidMessage.bError = 0;
// if error, see Table 6.1-2 errors
// Return the slot status to the host
RDRtoPCSlotStatus();
}
//------------------------------------------------------------------------------
/// Command Pipe, Bulk-OUT Messages
/// Get slot status
//------------------------------------------------------------------------------
static void PCtoRDRGetSlotStatus( void )
{
TRACE_DEBUG(".");
ccidDriver.sCcidMessage.bStatus = 0;
ccidDriver.sCcidMessage.bError = 0;
// Return the slot status to the host
RDRtoPCSlotStatus();
}
//------------------------------------------------------------------------------
/// Command Pipe, Bulk-OUT Messages
/// If the command header is valid, an APDU command is received and can be read
/// by the application
//------------------------------------------------------------------------------
static void PCtoRDRXfrBlock( void )
{
uint16_t msglen = 0;
uint32_t ret;
TRACE_DEBUG("PCtoRDRXfrBlock\n");
// Check the block length
if ( ccidDriver.sCcidCommand.wLength > (configurationDescriptorsFS->ccid.dwMaxCCIDMessageLength-10) ) {
TRACE_DEBUG("Err block/msg len");
ccidDriver.sCcidMessage.bStatus = 1;
ccidDriver.sCcidMessage.bError = 0;
}
// check bBWI
else if ( 0 != ccidDriver.sCcidCommand.bSpecific_0 ) {
TRACE_ERROR("Bad bBWI\n\r");
}
else {
// APDU or TPDU
switch(configurationDescriptorsFS->ccid.dwFeatures
& (CCID_FEATURES_EXC_TPDU|CCID_FEATURES_EXC_SAPDU|CCID_FEATURES_EXC_APDU)) {
case CCID_FEATURES_EXC_TPDU:
if (ccidDriver.ProtocolDataStructure[1] == PROTOCOL_TO) {
TRACE_DEBUG("APDU cmd: %x %x %x ..", ccidDriver.sCcidCommand.APDU[0], ccidDriver.sCcidCommand.APDU[1],ccidDriver.sCcidCommand.APDU[2] );
// Send commande APDU
ret = ISO7816_XfrBlockTPDU_T0( ccidDriver.sCcidCommand.APDU ,
ccidDriver.sCcidMessage.abData,
ccidDriver.sCcidCommand.wLength,
&msglen );
if (ret != 0) {
TRACE_ERROR("APDU could not be sent: (US_CSR = 0x%x)", ret);
return;
}
}
else {
if (ccidDriver.ProtocolDataStructure[1] == PROTOCOL_T1) {
TRACE_DEBUG("Not supported T=1\n\r");
}
else {
TRACE_DEBUG("Not supported 0x%x\n\r", ccidDriver.ProtocolDataStructure[1]);
}
}
break;
case CCID_FEATURES_EXC_APDU:
TRACE_DEBUG("Not supported CCID_FEATURES_EXC_APDU\n\r");
break;
default:
break;
}
}
ccidDriver.sCcidMessage.wLength = msglen;
TRACE_DEBUG("USB: 0x%X, 0x%X, 0x%X, 0x%X, 0x%X\n\r", ccidDriver.sCcidMessage.abData[0],
ccidDriver.sCcidMessage.abData[1],
ccidDriver.sCcidMessage.abData[2],
ccidDriver.sCcidMessage.abData[3],
ccidDriver.sCcidMessage.abData[4] );
RDRtoPCDatablock();
}
//------------------------------------------------------------------------------
/// Command Pipe, Bulk-OUT Messages
/// return parameters by the command: RDR_to_PC_Parameters
//------------------------------------------------------------------------------
static void PCtoRDRGetParameters( void )
{
TRACE_DEBUG(".");
// We support only one slot
// bmIccStatus
if( ISO7816_StatusReset() ) {
// 0: An ICC is present and active (power is on and stable, RST is inactive
ccidDriver.sCcidMessage.bStatus = 0;
}
else {
// 1: An ICC is present and inactive (not activated or shut down by hardware error)
ccidDriver.sCcidMessage.bStatus = 1;
}
RDRtoPCParameters();
}
//------------------------------------------------------------------------------
/// Command Pipe, Bulk-OUT Messages
/// This command resets the slot parameters to their default values
//------------------------------------------------------------------------------
static void PCtoRDRResetParameters( void )
{
TRACE_DEBUG(".");
ccidDriver.SlotStatus = ICC_NOT_PRESENT;
ccidDriver.sCcidMessage.bStatus = ccidDriver.SlotStatus;
RDRtoPCParameters();
}
//------------------------------------------------------------------------------
/// Command Pipe, Bulk-OUT Messages
/// This command is used to change the parameters for a given slot.
//------------------------------------------------------------------------------
static void PCtoRDRSetParameters( void )
{
TRACE_DEBUG(".");
ccidDriver.SlotStatus = ccidDriver.sCcidCommand.bSlot;
ccidDriver.sCcidMessage.bStatus = ccidDriver.SlotStatus;
// Not all feature supported
RDRtoPCParameters();
}
//------------------------------------------------------------------------------
/// Command Pipe, Bulk-OUT Messages
/// This command allows the CCID manufacturer to define and access extended
/// features.
/// Information sent via this command is processed by the CCID control logic.
//------------------------------------------------------------------------------
static void PCtoRDREscape( void )
{
TRACE_DEBUG(".");
// If needed by the user
ISO7816_Escape();
// stub, return all value send
RDRtoPCEscape( ccidDriver.sCcidCommand.wLength, ccidDriver.sCcidCommand.APDU);
}
//------------------------------------------------------------------------------
/// Command Pipe, Bulk-OUT Messages
/// This command stops or restarts the clock.
//------------------------------------------------------------------------------
static void PCtoRDRICCClock( void )
{
TRACE_DEBUG(".");
if( 0 == ccidDriver.sCcidCommand.bSpecific_0 ) {
// restarts the clock
ISO7816_RestartClock();
}
else {
// stop clock in the state shown in the bClockStop field
ISO7816_StopClock();
}
RDRtoPCSlotStatus( );
}
//------------------------------------------------------------------------------
/// Command Pipe, Bulk-OUT Messages
/// This command changes the parameters used to perform the transportation of
/// APDU messages by the T=0 protocol.
//------------------------------------------------------------------------------
static void PCtoRDRtoAPDU( void )
{
unsigned char bmChanges;
unsigned char bClassGetResponse;
unsigned char bClassEnvelope;
TRACE_INFO(".");
if( configurationDescriptorsFS->ccid.dwFeatures == (CCID_FEATURES_EXC_SAPDU|CCID_FEATURES_EXC_APDU) ) {
bmChanges = ccidDriver.sCcidCommand.bSpecific_0;
bClassGetResponse = ccidDriver.sCcidCommand.bSpecific_1;
bClassEnvelope = ccidDriver.sCcidCommand.bSpecific_2;
ISO7816_toAPDU();
}
RDRtoPCSlotStatus();
}
//------------------------------------------------------------------------------
/// Command Pipe, Bulk-OUT Messages
/// This is a command message to allow entering the PIN for verification or
/// modification.
//------------------------------------------------------------------------------
static void PCtoRDRSecure( void )
{
TRACE_DEBUG(".");
TRACE_DEBUG("For user\n\r");
}
//------------------------------------------------------------------------------
/// Command Pipe, Bulk-OUT Messages
/// This command is used to manage motorized type CCID functionality.
/// The Lock Card function is used to hold the ICC.
/// This prevents an ICC from being easily removed from the CCID.
/// The Unlock Card function is used to remove the hold initiated by the Lock
/// Card function
//------------------------------------------------------------------------------
static void PCtoRDRMechanical( void )
{
TRACE_DEBUG(".");
TRACE_DEBUG("Not implemented\n\r");
RDRtoPCSlotStatus();
}
//------------------------------------------------------------------------------
/// Command Pipe, Bulk-OUT Messages
/// This command is used with the Control pipe Abort request to tell the CCID
/// to stop any current transfer at the specified slot and return to a state
/// where the slot is ready to accept a new command pipe Bulk-OUT message.
//------------------------------------------------------------------------------
static void PCtoRDRAbort( void )
{
TRACE_DEBUG(".");
RDRtoPCSlotStatus();
}
//------------------------------------------------------------------------------
/// Command Pipe, Bulk-OUT Messages
/// This command is used to manually set the data rate and clock frequency of
/// a specific slot.
//------------------------------------------------------------------------------
static void PCtoRDRSetDataRateAndClockFrequency( void )
{
unsigned int dwClockFrequency;
unsigned int dwDataRate;
TRACE_DEBUG(".");
dwClockFrequency = ccidDriver.sCcidCommand.APDU[0]
+ (ccidDriver.sCcidCommand.APDU[1]<<8)
+ (ccidDriver.sCcidCommand.APDU[2]<<16)
+ (ccidDriver.sCcidCommand.APDU[3]<<24);
dwDataRate = ccidDriver.sCcidCommand.APDU[4]
+ (ccidDriver.sCcidCommand.APDU[5]<<8)
+ (ccidDriver.sCcidCommand.APDU[6]<<16)
+ (ccidDriver.sCcidCommand.APDU[7]<<24);
ISO7816_SetDataRateandClockFrequency( dwClockFrequency, dwDataRate );
RDRtoPCDataRateAndClockFrequency( dwClockFrequency, dwDataRate );
}
//------------------------------------------------------------------------------
/// Report the CMD_NOT_SUPPORTED error to the host
//------------------------------------------------------------------------------
static void vCCIDCommandNotSupported( void )
{
// Command not supported
// vCCIDReportError(CMD_NOT_SUPPORTED);
TRACE_DEBUG("CMD_NOT_SUPPORTED\n\r");
// Header fields settings
ccidDriver.sCcidMessage.bMessageType = RDR_TO_PC_SLOTSTATUS;
ccidDriver.sCcidMessage.wLength = 0;
ccidDriver.sCcidMessage.bSpecific = 0;
ccidDriver.sCcidMessage.bStatus |= ICC_CS_FAILED;
// Send the response to the host
//vCCIDSendResponse();
}
//------------------------------------------------------------------------------
/// Sent CCID response on USB
//------------------------------------------------------------------------------
static void vCCIDSendResponse( void )
{
unsigned char bStatus;
TRACE_DEBUG(".");
do {
bStatus = CCID_Write((void*)&ccidDriver.sCcidMessage,
ccidDriver.sCcidMessage.bSizeToSend, 0, 0 );
} while (bStatus != USBD_STATUS_SUCCESS);
TRACE_DEBUG("bStatus: 0x%x\n\r", bStatus);
}
//------------------------------------------------------------------------------
/// Description: CCID Command dispatcher
//------------------------------------------------------------------------------
static void CCIDCommandDispatcher( void *pArg, uint8_t status, uint32_t transferred, uint32_t remaining )
{
unsigned char MessageToSend = 0;
if (status != USBD_STATUS_SUCCESS) {
TRACE_ERROR("USB error: %d", status);
return;
}
TRACE_DEBUG("Command: 0x%X 0x%x 0x%X 0x%X 0x%X 0x%X 0x%X\n\r\n\r",
(unsigned int)ccidDriver.sCcidCommand.bMessageType,
(unsigned int)ccidDriver.sCcidCommand.wLength,
(unsigned int)ccidDriver.sCcidCommand.bSlot,
(unsigned int)ccidDriver.sCcidCommand.bSeq,
(unsigned int)ccidDriver.sCcidCommand.bSpecific_0,
(unsigned int)ccidDriver.sCcidCommand.bSpecific_1,
(unsigned int)ccidDriver.sCcidCommand.bSpecific_2);
// Check the slot number
if ( ccidDriver.sCcidCommand.bSlot > 0 ) {
TRACE_ERROR("BAD_SLOT_NUMBER\n\r");
}
TRACE_INFO("typ=0x%X\n\r", ccidDriver.sCcidCommand.bMessageType);
ccidDriver.sCcidMessage.bStatus = 0;
ccidDriver.sCcidMessage.bSeq = ccidDriver.sCcidCommand.bSeq;
ccidDriver.sCcidMessage.bSlot = ccidDriver.sCcidCommand.bSlot;
ccidDriver.sCcidMessage.bSizeToSend = sizeof(S_ccid_bulk_in_header)-(ABDATA_SIZE+1);
// Command dispatcher
switch ( ccidDriver.sCcidCommand.bMessageType ) {
case PC_TO_RDR_ICCPOWERON:
PCtoRDRIccPowerOn();
MessageToSend = 1;
break;
case PC_TO_RDR_ICCPOWEROFF:
PCtoRDRIccPowerOff();
MessageToSend = 1;
break;
case PC_TO_RDR_GETSLOTSTATUS:
PCtoRDRGetSlotStatus();
MessageToSend = 1;
break;
case PC_TO_RDR_XFRBLOCK:
PCtoRDRXfrBlock();
MessageToSend = 1;
break;
case PC_TO_RDR_GETPARAMETERS:
PCtoRDRGetParameters();
MessageToSend = 1;
break;
case PC_TO_RDR_RESETPARAMETERS:
PCtoRDRResetParameters();
MessageToSend = 1;
break;
case PC_TO_RDR_SETPARAMETERS:
PCtoRDRSetParameters();
MessageToSend = 1;
break;
case PC_TO_RDR_ESCAPE:
PCtoRDREscape();
MessageToSend = 1;
break;
case PC_TO_RDR_ICCCLOCK:
PCtoRDRICCClock();
MessageToSend = 1;
break;
case PC_TO_RDR_T0APDU:
// Only CCIDs reporting a short or extended APDU level in the dwFeatures
// field of the CCID class descriptor may take this command into account.
if( (CCID_FEATURES_EXC_SAPDU == (CCID_FEATURES_EXC_SAPDU&configurationDescriptorsFS->ccid.dwFeatures))
|| (CCID_FEATURES_EXC_APDU == (CCID_FEATURES_EXC_APDU &configurationDescriptorsFS->ccid.dwFeatures)) ) {
// command supported
PCtoRDRtoAPDU();
}
else {
// command not supported
TRACE_INFO("Not supported: PC_TO_RDR_T0APDU\n\r");
vCCIDCommandNotSupported();
}
MessageToSend = 1;
break;
case PC_TO_RDR_SECURE:
PCtoRDRSecure();
MessageToSend = 1;
break;
case PC_TO_RDR_MECHANICAL:
PCtoRDRMechanical();
MessageToSend = 1;
break;
case PC_TO_RDR_ABORT:
PCtoRDRAbort();
MessageToSend = 1;
break;
case PC_TO_RDR_SETDATARATEANDCLOCKFREQUENCY:
PCtoRDRSetDataRateAndClockFrequency();
MessageToSend = 1;
break;
default:
TRACE_DEBUG("default: Not supported: 0x%X\n\r", ccidDriver.sCcidCommand.bMessageType);
vCCIDCommandNotSupported();
MessageToSend = 1;
break;
}
if( MessageToSend == 1 ) {
vCCIDSendResponse();
}
}
//------------------------------------------------------------------------------
/// SETUP request handler for a CCID device
/// \param pRequest Pointer to a USBGenericRequest instance
//------------------------------------------------------------------------------
static void CCID_RequestHandler(const USBGenericRequest *pRequest)
{
TRACE_DEBUG("CCID_RHl\n\r");
// Check if this is a class request
if (USBGenericRequest_GetType(pRequest) == USBGenericRequest_CLASS) {
// Check if the request is supported
switch (USBGenericRequest_GetRequest(pRequest)) {
case CCIDGenericRequest_ABORT:
TRACE_DEBUG("CCIDGenericRequest_ABORT\n\r");
break;
case CCIDGenericRequest_GET_CLOCK_FREQUENCIES:
TRACE_DEBUG("Not supported: CCIDGenericRequest_GET_CLOCK_FREQUENCIES\n\r");
// A CCID with bNumClockSupported equal to 00h does not have
// to support this request
break;
case CCIDGenericRequest_GET_DATA_RATES:
TRACE_DEBUG("Not supported: CCIDGenericRequest_GET_DATA_RATES\n\r");
// A CCID with bNumDataRatesSupported equal to 00h does not have
// to support this request.
break;
default:
TRACE_WARNING( "CCIDDriver_RequestHandler: Unsupported request (%d)\n\r",
USBGenericRequest_GetRequest(pRequest));
USBD_Stall(0);
}
}
else if (USBGenericRequest_GetType(pRequest) == USBGenericRequest_STANDARD) {
// Forward request to the standard handler
USBDDriver_RequestHandler(USBD_GetDriver(), pRequest);
}
else {
// Unsupported request type
TRACE_WARNING( "CCIDDriver_RequestHandler: Unsupported request type (%d)\n\r",
USBGenericRequest_GetType(pRequest));
USBD_Stall(0);
}
}
//------------------------------------------------------------------------------
// Exported functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Optional callback re-implementation
//------------------------------------------------------------------------------
#if !defined(NOAUTOCALLBACK)
// not static function
void USBDCallbacks_RequestReceived(const USBGenericRequest *request)
{
CCID_RequestHandler(request);
}
#endif
//------------------------------------------------------------------------------
/// Handles SmartCart request
//------------------------------------------------------------------------------
void CCID_SmartCardRequest( void )
{
unsigned char bStatus;
TRACE_DEBUG("CCID_req\n");
do {
bStatus = CCID_Read( (void*)&ccidDriver.sCcidCommand,
sizeof(S_ccid_bulk_out_header),
(TransferCallback)&CCIDCommandDispatcher,
(void*)0 );
}
while (0);
}
//------------------------------------------------------------------------------
/// Reads data from the Data OUT endpoint
/// \param pBuffer Buffer to store the received data
/// \param dLength data buffer length
/// \param fCallback Optional callback function
/// \param pArgument Optional parameter for the callback function
/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS
//------------------------------------------------------------------------------
unsigned char CCID_Read(void *pBuffer,
unsigned int dLength,
TransferCallback fCallback,
void *pArgument)
{
return USBD_Read(CCID_EPT_DATA_OUT, pBuffer, dLength, fCallback, pArgument);
}
//------------------------------------------------------------------------------
/// Sends data through the Data IN endpoint
/// \param pBuffer Buffer holding the data to transmit
/// \param dLength Length of data buffer
/// \param fCallback Optional callback function
/// \param pArgument Optional parameter for the callback function
/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS
//------------------------------------------------------------------------------
unsigned char CCID_Write(void *pBuffer,
unsigned int dLength,
TransferCallback fCallback,
void *pArgument)
{
return USBD_Write(CCID_EPT_DATA_IN, pBuffer, dLength, fCallback, pArgument);
}
//------------------------------------------------------------------------------
/// Sends data through the interrupt endpoint, ICC insertion event
/// RDR_to_PC_NotifySlotChange
/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS
//------------------------------------------------------------------------------
unsigned char CCID_Insertion( void )
{
TRACE_DEBUG(".");
// Build the Interrupt-IN message
ccidDriver.BufferINT[0] = RDR_TO_PC_NOTIFYSLOTCHANGE;
ccidDriver.BufferINT[1] = ICC_INSERTED_EVENT;
ccidDriver.SlotStatus = ICC_INSERTED_EVENT;
// Notify the host that a ICC is inserted
return USBD_Write( CCID_EPT_NOTIFICATION, ccidDriver.BufferINT, 2, 0, 0 );
}
//------------------------------------------------------------------------------
/// Sends data through the interrupt endpoint, ICC removal event
/// RDR_to_PC_NotifySlotChange
/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS
//------------------------------------------------------------------------------
unsigned char CCID_Removal( void )
{
TRACE_DEBUG(".");
// Build the Interrupt-IN message
ccidDriver.BufferINT[0] = RDR_TO_PC_NOTIFYSLOTCHANGE;
ccidDriver.BufferINT[1] = ICC_NOT_PRESENT;
ccidDriver.SlotStatus = ICC_NOT_PRESENT;
// Notify the host that a ICC is inserted
return USBD_Write( CCID_EPT_NOTIFICATION, ccidDriver.BufferINT, 2, 0, 0 );
}
//------------------------------------------------------------------------------
/// Interrupt-IN Messages
/// This message is sent when any bit in the bHardwareErrorCode field is set.
/// If this message is sent when there is no “outstanding” command, the bSeq
/// field will be undefined.
/// \param bSlot ICC slot number
/// \param bSeq Sequence number of the bulk OUT command when the hardware error
/// occured
/// \param bHardwareErrorCode Hardware error code
/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS
//------------------------------------------------------------------------------
unsigned char RDRtoPCHardwareError( unsigned char bSlot,
unsigned char bSeq,
unsigned char bHardwareErrorCode )
{
TRACE_DEBUG(".");
// Build the Interrupt-IN message
ccidDriver.BufferINT[0] = RDR_TO_PC_HARDWAREERROR;
ccidDriver.BufferINT[1] = bSlot;
ccidDriver.BufferINT[2] = bSeq;
ccidDriver.BufferINT[3] = bHardwareErrorCode;
// Notify the host that a ICC is inserted
return USBD_Write( CCID_EPT_NOTIFICATION, ccidDriver.BufferINT, 4, 0, 0 );
}
#endif /* HAVE_CCID */