/* ---------------------------------------------------------------------------- | |
* SAM Software Package License | |
* ---------------------------------------------------------------------------- | |
* Copyright (c) 2014, 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. | |
* ---------------------------------------------------------------------------- | |
*/ | |
/** | |
* \file | |
* | |
* \section Purpose | |
* | |
* ISO 7816 driver | |
* | |
* \section Usage | |
* | |
* Explanation on the usage of the code made available through the header file. | |
*/ | |
/*------------------------------------------------------------------------------ | |
* Headers | |
*------------------------------------------------------------------------------*/ | |
#include "board.h" | |
/*------------------------------------------------------------------------------ | |
* Definitions | |
*------------------------------------------------------------------------------*/ | |
/** Case for APDU commands*/ | |
#define CASE1 1 | |
#define CASE2 2 | |
#define CASE3 3 | |
/** Flip flop for send and receive char */ | |
#define USART_SEND 0 | |
#define USART_RCV 1 | |
#if !defined(BOARD_ISO7816_BASE_USART) | |
#define BOARD_ISO7816_BASE_USART USART1 | |
#define BOARD_ISO7816_ID_USART ID_USART1 | |
#endif | |
/*----------------------------------------------------------------------------- | |
* Internal variables | |
*-----------------------------------------------------------------------------*/ | |
/** Variable for state of send and receive froom USART */ | |
static uint8_t StateUsartGlobal = USART_RCV; | |
/** Pin reset master card */ | |
static Pin st_pinIso7816RstMC; | |
static uint32_t maxMck; | |
/*---------------------------------------------------------------------------- | |
* Internal functions | |
*----------------------------------------------------------------------------*/ | |
/** | |
* Get a character from ISO7816 | |
* \param pCharToReceive Pointer for store the received char | |
* \return 0: if timeout else status of US_CSR | |
*/ | |
static uint32_t ISO7816_GetChar( uint8_t *pCharToReceive ) | |
{ | |
uint32_t status; | |
uint32_t timeout=0; | |
if( StateUsartGlobal == USART_SEND ) { | |
while((BOARD_ISO7816_BASE_USART->US_CSR & US_CSR_TXEMPTY) == 0) {} | |
BOARD_ISO7816_BASE_USART->US_CR = US_CR_RSTSTA | US_CR_RSTIT | US_CR_RSTNACK; | |
StateUsartGlobal = USART_RCV; | |
} | |
/* Wait USART ready for reception */ | |
while( ((BOARD_ISO7816_BASE_USART->US_CSR & US_CSR_RXRDY) == 0) ) { | |
if(timeout++ > 12000 * (BOARD_MCK/1000000)) { | |
TRACE_DEBUG("TimeOut\n\r"); | |
return( 0 ); | |
} | |
} | |
TRACE_DEBUG("T: %u\n\r", timeout); | |
/* At least one complete character has been received and US_RHR has not yet been read. */ | |
/* Get a char */ | |
*pCharToReceive = ((BOARD_ISO7816_BASE_USART->US_RHR) & 0xFF); | |
status = (BOARD_ISO7816_BASE_USART->US_CSR&(US_CSR_OVRE|US_CSR_FRAME| | |
US_CSR_PARE|US_CSR_TIMEOUT|US_CSR_NACK| | |
(1<<10))); | |
if (status != 0 ) { | |
/* TRACE_DEBUG("R:0x%X\n\r", status); */ | |
TRACE_DEBUG("R:0x%X\n\r", BOARD_ISO7816_BASE_USART->US_CSR); | |
TRACE_DEBUG("Nb:0x%X\n\r", BOARD_ISO7816_BASE_USART->US_NER ); | |
BOARD_ISO7816_BASE_USART->US_CR = US_CR_RSTSTA; | |
} | |
/* Return status */ | |
return( status ); | |
} | |
/** | |
* Send a char to ISO7816 | |
* \param CharToSend char to be send | |
* \return status of US_CSR | |
*/ | |
static uint32_t ISO7816_SendChar( uint8_t CharToSend ) | |
{ | |
uint32_t status; | |
if( StateUsartGlobal == USART_RCV ) { | |
BOARD_ISO7816_BASE_USART->US_CR = US_CR_RSTSTA | US_CR_RSTIT | US_CR_RSTNACK; | |
StateUsartGlobal = USART_SEND; | |
} | |
/* Wait USART ready for transmit */ | |
while((BOARD_ISO7816_BASE_USART->US_CSR & US_CSR_TXRDY) == 0) {} | |
/* There is no character in the US_THR */ | |
/* Transmit a char */ | |
BOARD_ISO7816_BASE_USART->US_THR = CharToSend; | |
status = (BOARD_ISO7816_BASE_USART->US_CSR&(US_CSR_OVRE|US_CSR_FRAME| | |
US_CSR_PARE|US_CSR_TIMEOUT|US_CSR_NACK| | |
(1<<10))); | |
if (status != 0 ) { | |
TRACE_DEBUG("E:0x%X\n\r", BOARD_ISO7816_BASE_USART->US_CSR); | |
TRACE_DEBUG("Nb:0x%X\n\r", BOARD_ISO7816_BASE_USART->US_NER ); | |
BOARD_ISO7816_BASE_USART->US_CR = US_CR_RSTSTA; | |
} | |
/* Return status */ | |
return( status ); | |
} | |
/** | |
* Iso 7816 ICC power on | |
*/ | |
static void ISO7816_IccPowerOn( void ) | |
{ | |
/* Set RESET Master Card */ | |
PIO_Set(&st_pinIso7816RstMC); | |
} | |
/*---------------------------------------------------------------------------- | |
* Exported functions | |
*----------------------------------------------------------------------------*/ | |
/** | |
* Iso 7816 ICC power off | |
*/ | |
void ISO7816_IccPowerOff( void ) | |
{ | |
/* Clear RESET Master Card */ | |
PIO_Clear(&st_pinIso7816RstMC); | |
} | |
/** | |
* Transfert Block TPDU T=0 | |
* \param pAPDU APDU buffer | |
* \param pMessage Message buffer | |
* \param wLength Block length | |
* \return Message index | |
*/ | |
uint16_t ISO7816_XfrBlockTPDU_T0(const uint8_t *pAPDU, | |
uint8_t *pMessage, | |
uint16_t wLength ) | |
{ | |
uint16_t NeNc; | |
uint16_t indexApdu = 4; | |
uint16_t indexMessage = 0; | |
uint8_t SW1 = 0; | |
uint8_t procByte; | |
uint8_t cmdCase; | |
uint8_t ins; | |
TRACE_DEBUG("pAPDU[0]=0x%X\n\r",pAPDU[0]); | |
TRACE_DEBUG("pAPDU[1]=0x%X\n\r",pAPDU[1]); | |
TRACE_DEBUG("pAPDU[2]=0x%X\n\r",pAPDU[2]); | |
TRACE_DEBUG("pAPDU[3]=0x%X\n\r",pAPDU[3]); | |
TRACE_DEBUG("pAPDU[4]=0x%X\n\r",pAPDU[4]); | |
TRACE_DEBUG("pAPDU[5]=0x%X\n\r",pAPDU[5]); | |
TRACE_DEBUG("wlength=%d\n\r",wLength); | |
ISO7816_SendChar( pAPDU[0] ); /* CLA */ | |
ISO7816_SendChar( pAPDU[1] ); /* INS */ | |
ISO7816_SendChar( pAPDU[2] ); /* P1 */ | |
ISO7816_SendChar( pAPDU[3] ); /* P2 */ | |
ISO7816_SendChar( pAPDU[4] ); /* P3 */ | |
/* Handle the four structures of command APDU */ | |
indexApdu = 4; | |
if( wLength == 4 ) { | |
cmdCase = CASE1; | |
NeNc = 0; | |
} | |
else if( wLength == 5) { | |
cmdCase = CASE2; | |
NeNc = pAPDU[4]; /* C5 */ | |
if (NeNc == 0) { | |
NeNc = 256; | |
} | |
} | |
else if( wLength == 6) { | |
NeNc = pAPDU[4]; /* C5 */ | |
cmdCase = CASE3; | |
} | |
else if( wLength == 7) { | |
NeNc = pAPDU[4]; /* C5 */ | |
if( NeNc == 0 ) { | |
cmdCase = CASE2; | |
NeNc = (pAPDU[5]<<8)+pAPDU[6]; | |
} | |
else { | |
cmdCase = CASE3; | |
} | |
} | |
else { | |
NeNc = pAPDU[4]; /* C5 */ | |
if( NeNc == 0 ) { | |
cmdCase = CASE3; | |
NeNc = (pAPDU[5]<<8)+pAPDU[6]; | |
} | |
else { | |
cmdCase = CASE3; | |
} | |
} | |
TRACE_DEBUG("CASE=0x%X NeNc=0x%X\n\r", cmdCase, NeNc); | |
/* Handle Procedure Bytes */ | |
do { | |
ISO7816_GetChar(&procByte); | |
ins = procByte ^ 0xff; | |
/* Handle NULL */ | |
if ( procByte == ISO_NULL_VAL ) { | |
TRACE_DEBUG("INS\n\r"); | |
continue; | |
} | |
/* Handle SW1 */ | |
else if ( ((procByte & 0xF0) ==0x60) || ((procByte & 0xF0) ==0x90) ) { | |
TRACE_DEBUG("SW1\n\r"); | |
SW1 = 1; | |
} | |
/* Handle INS */ | |
else if ( pAPDU[1] == procByte) { | |
TRACE_DEBUG("HdlINS\n\r"); | |
if (cmdCase == CASE2) { | |
/* receive data from card */ | |
do { | |
ISO7816_GetChar(&pMessage[indexMessage++]); | |
} while( 0 != --NeNc ); | |
} | |
else { | |
/* Send data */ | |
do { | |
ISO7816_SendChar(pAPDU[indexApdu++]); | |
} while( 0 != --NeNc ); | |
} | |
} | |
/* Handle INS ^ 0xff */ | |
else if ( pAPDU[1] == ins) { | |
TRACE_DEBUG("HdlINS+\n\r"); | |
if (cmdCase == CASE2) { | |
/* receive data from card */ | |
ISO7816_GetChar(&pMessage[indexMessage++]); | |
} | |
else { | |
ISO7816_SendChar(pAPDU[indexApdu++]); | |
} | |
NeNc--; | |
} | |
else { | |
/* ?? */ | |
TRACE_DEBUG("procByte=0x%X\n\r", procByte); | |
break; | |
} | |
} while (NeNc != 0); | |
/* Status Bytes */ | |
if (SW1 == 0) { | |
ISO7816_GetChar(&pMessage[indexMessage++]); /* SW1 */ | |
} | |
else { | |
pMessage[indexMessage++] = procByte; | |
} | |
ISO7816_GetChar(&pMessage[indexMessage++]); /* SW2 */ | |
return( indexMessage ); | |
} | |
/** | |
* Escape ISO7816 | |
*/ | |
void ISO7816_Escape( void ) | |
{ | |
TRACE_DEBUG("For user, if needed\n\r"); | |
} | |
/** | |
* Restart clock ISO7816 | |
*/ | |
void ISO7816_RestartClock( void ) | |
{ | |
TRACE_DEBUG("ISO7816_RestartClock\n\r"); | |
BOARD_ISO7816_BASE_USART->US_BRGR = 13; | |
} | |
/** | |
* Stop clock ISO7816 | |
*/ | |
void ISO7816_StopClock( void ) | |
{ | |
TRACE_DEBUG("ISO7816_StopClock\n\r"); | |
BOARD_ISO7816_BASE_USART->US_BRGR = 0; | |
} | |
/** | |
* T0 APDU | |
*/ | |
void ISO7816_toAPDU( void ) | |
{ | |
TRACE_DEBUG("ISO7816_toAPDU\n\r"); | |
TRACE_DEBUG("Not supported at this time\n\r"); | |
} | |
/** | |
* Answer To Reset (ATR) | |
* \param pAtr ATR buffer | |
* \param pLength Pointer for store the ATR length | |
*/ | |
void ISO7816_Datablock_ATR( uint8_t* pAtr, uint8_t* pLength ) | |
{ | |
uint32_t i; | |
uint32_t j; | |
uint32_t y; | |
*pLength = 0; | |
/* Read ATR TS */ | |
ISO7816_GetChar(&pAtr[0]); | |
/* Read ATR T0 */ | |
ISO7816_GetChar(&pAtr[1]); | |
y = pAtr[1] & 0xF0; | |
i = 2; | |
/* Read ATR Ti */ | |
while (y) { | |
if (y & 0x10) { /* TA[i] */ | |
ISO7816_GetChar(&pAtr[i++]); | |
} | |
if (y & 0x20) { /* TB[i] */ | |
ISO7816_GetChar(&pAtr[i++]); | |
} | |
if (y & 0x40) { /* TC[i] */ | |
ISO7816_GetChar(&pAtr[i++]); | |
} | |
if (y & 0x80) { /* TD[i] */ | |
ISO7816_GetChar(&pAtr[i]); | |
y = pAtr[i++] & 0xF0; | |
} | |
else { | |
y = 0; | |
} | |
} | |
/* Historical Bytes */ | |
y = pAtr[1] & 0x0F; | |
for( j=0; j < y; j++ ) { | |
ISO7816_GetChar(&pAtr[i++]); | |
} | |
*pLength = i; | |
} | |
/** | |
* Set data rate and clock frequency | |
* \param dwClockFrequency ICC clock frequency in KHz. | |
* \param dwDataRate ICC data rate in bpd | |
*/ | |
void ISO7816_SetDataRateandClockFrequency( uint32_t dwClockFrequency, uint32_t dwDataRate ) | |
{ | |
uint8_t ClockFrequency; | |
/* Define the baud rate divisor register */ | |
/* CD = MCK / SCK */ | |
/* SCK = FIDI x BAUD = 372 x 9600 */ | |
/* BOARD_MCK */ | |
/* CD = MCK/(FIDI x BAUD) = 48000000 / (372x9600) = 13 */ | |
BOARD_ISO7816_BASE_USART->US_BRGR = maxMck / (dwClockFrequency*1000); | |
ClockFrequency = maxMck / BOARD_ISO7816_BASE_USART->US_BRGR; | |
BOARD_ISO7816_BASE_USART->US_FIDI = (ClockFrequency)/dwDataRate; | |
} | |
/** | |
* Pin status for ISO7816 RESET | |
* \return 1 if the Pin RstMC is high; otherwise 0. | |
*/ | |
uint8_t ISO7816_StatusReset( void ) | |
{ | |
return PIO_Get(&st_pinIso7816RstMC); | |
} | |
/** | |
* cold reset | |
*/ | |
void ISO7816_cold_reset( void ) | |
{ | |
volatile uint32_t i; | |
/* tb: wait 400 cycles*/ | |
for( i=0; i<(120*(BOARD_MCK/1000000)); i++ ) { | |
} | |
BOARD_ISO7816_BASE_USART->US_RHR; | |
BOARD_ISO7816_BASE_USART->US_CR = US_CR_RSTSTA | US_CR_RSTIT | US_CR_RSTNACK; | |
ISO7816_IccPowerOn(); | |
} | |
/** | |
* Warm reset | |
*/ | |
void ISO7816_warm_reset( void ) | |
{ | |
volatile uint32_t i; | |
ISO7816_IccPowerOff(); | |
/* tb: wait 400 cycles */ | |
for( i=0; i<(120*(BOARD_MCK/1000000)); i++ ) { | |
} | |
BOARD_ISO7816_BASE_USART->US_RHR; | |
BOARD_ISO7816_BASE_USART->US_CR = US_CR_RSTSTA | US_CR_RSTIT | US_CR_RSTNACK; | |
ISO7816_IccPowerOn(); | |
} | |
/** | |
* Decode ATR trace | |
* \param pAtr pointer on ATR buffer | |
*/ | |
void ISO7816_Decode_ATR( uint8_t* pAtr ) | |
{ | |
uint32_t i; | |
uint32_t j; | |
uint32_t y; | |
uint8_t offset; | |
printf("\n\r"); | |
printf("ATR: Answer To Reset:\n\r"); | |
printf("TS = 0x%X Initial character ",pAtr[0]); | |
if( pAtr[0] == 0x3B ) { | |
printf("Direct Convention\n\r"); | |
} | |
else { | |
if( pAtr[0] == 0x3F ) { | |
printf("Inverse Convention\n\r"); | |
} | |
else { | |
printf("BAD Convention\n\r"); | |
} | |
} | |
printf("T0 = 0x%X Format caracter\n\r",pAtr[1]); | |
printf(" Number of historical bytes: K = %d\n\r", pAtr[1]&0x0F); | |
printf(" Presence further interface byte:\n\r"); | |
if( pAtr[1]&0x80 ) { | |
printf("TA "); | |
} | |
if( pAtr[1]&0x40 ) { | |
printf("TB "); | |
} | |
if( pAtr[1]&0x20 ) { | |
printf("TC "); | |
} | |
if( pAtr[1]&0x10 ) { | |
printf("TD "); | |
} | |
if( pAtr[1] != 0 ) { | |
printf(" present\n\r"); | |
} | |
i = 2; | |
y = pAtr[1] & 0xF0; | |
/* Read ATR Ti */ | |
offset = 1; | |
while (y) { | |
if (y & 0x10) { /* TA[i] */ | |
printf("TA[%d] = 0x%X ", offset, pAtr[i]); | |
if( offset == 1 ) { | |
printf("FI = %d ", (pAtr[i]>>8)); | |
printf("DI = %d", (pAtr[i]&0x0F)); | |
} | |
printf("\n\r"); | |
i++; | |
} | |
if (y & 0x20) { /* TB[i] */ | |
printf("TB[%d] = 0x%X\n\r", offset, pAtr[i]); | |
i++; | |
} | |
if (y & 0x40) { /* TC[i] */ | |
printf("TC[%d] = 0x%X ", offset, pAtr[i]); | |
if( offset == 1 ) { | |
printf("Extra Guard Time: N = %d", pAtr[i]); | |
} | |
printf("\n\r"); | |
i++; | |
} | |
if (y & 0x80) { /* TD[i] */ | |
printf("TD[%d] = 0x%X\n\r", offset, pAtr[i]); | |
y = pAtr[i++] & 0xF0; | |
} | |
else { | |
y = 0; | |
} | |
offset++; | |
} | |
/* Historical Bytes */ | |
printf("Historical bytes:\n\r"); | |
y = pAtr[1] & 0x0F; | |
for( j=0; j < y; j++ ) { | |
printf(" 0x%X", pAtr[i]); | |
if( (pAtr[i] > 0x21) && (pAtr[i] < 0x7D) ) { /* ASCII */ | |
printf("(%c) ", pAtr[i]); | |
} | |
i++; | |
} | |
printf("\n\r\n\r"); | |
} | |
/** Initializes a ISO driver | |
* \param pPinIso7816RstMC Pin ISO 7816 Rst MC | |
*/ | |
void ISO7816_Init( const Pin pPinIso7816RstMC ) | |
{ | |
TRACE_DEBUG("ISO_Init\n\r"); | |
/* Pin ISO7816 initialize */ | |
st_pinIso7816RstMC = pPinIso7816RstMC; | |
USART_Configure( BOARD_ISO7816_BASE_USART, | |
US_MR_USART_MODE_IS07816_T_0 | |
| US_MR_USCLKS_MCK | |
| US_MR_NBSTOP_1_BIT | |
| US_MR_PAR_EVEN | |
| US_MR_CHRL_8_BIT | |
| US_MR_CLKO | |
| (3<<24), /* MAX_ITERATION */ | |
1, | |
0); | |
/* Configure USART */ | |
//PMC_EnablePeripheral(BOARD_ISO7816_ID_USART); | |
maxMck = PMC_SetPeriMaxClock(BOARD_ISO7816_ID_USART, BOARD_MCK); | |
/* Disable interrupts */ | |
BOARD_ISO7816_BASE_USART->US_IDR = (uint32_t) -1; | |
BOARD_ISO7816_BASE_USART->US_FIDI = 372; /* by default */ | |
/* Define the baud rate divisor register */ | |
/* CD = MCK / SCK */ | |
/* SCK = FIDI x BAUD = 372 x 9600 */ | |
/* BOARD_MCK */ | |
/* CD = MCK/(FIDI x BAUD) = 48000000 / (372x9600) = 13 */ | |
BOARD_ISO7816_BASE_USART->US_BRGR = maxMck / (372*9600); | |
/* Write the Timeguard Register */ | |
BOARD_ISO7816_BASE_USART->US_TTGR = 5; | |
USART_SetTransmitterEnabled(BOARD_ISO7816_BASE_USART, 1); | |
USART_SetReceiverEnabled(BOARD_ISO7816_BASE_USART, 1); | |
} | |