| /* ---------------------------------------------------------------------------- |
| * ATMEL Microcontroller Software Support |
| * ---------------------------------------------------------------------------- |
| * Copyright (c) 2009, 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 |
| |
| /*----------------------------------------------------------------------------- |
| * Internal variables |
| *-----------------------------------------------------------------------------*/ |
| /** Pin reset master card */ |
| static Pin *st_pinIso7816RstMC; |
| |
| struct Usart_info usart_sim = {.base = USART_SIM, .id = ID_USART_SIM, .state = USART_RCV}; |
| |
| /*---------------------------------------------------------------------------- |
| * Internal functions |
| *----------------------------------------------------------------------------*/ |
| |
| /** |
| * Get a character from ISO7816 |
| * \param pCharToReceive Pointer for store the received char |
| * \return 0: if timeout else status of US_CSR |
| */ |
| uint32_t ISO7816_GetChar( uint8_t *pCharToReceive, Usart_info *usart) |
| { |
| uint32_t status; |
| uint32_t timeout=0; |
| |
| Usart *us_base = usart->base; |
| uint32_t us_id = usart->id; |
| |
| if( usart->state == USART_SEND ) { |
| while((us_base->US_CSR & US_CSR_TXEMPTY) == 0) {} |
| us_base->US_CR = US_CR_RSTSTA | US_CR_RSTIT | US_CR_RSTNACK; |
| usart->state = USART_RCV; |
| } |
| |
| /* Wait USART ready for reception */ |
| while( ((us_base->US_CSR & US_CSR_RXRDY) == 0) ) { |
| WDT_Restart(WDT); |
| if(timeout++ > 12000 * (BOARD_MCK/1000000)) { |
| TRACE_WARNING("TimeOut\n\r"); |
| return( 0 ); |
| } |
| } |
| |
| /* At least one complete character has been received and US_RHR has not yet been read. */ |
| |
| /* Get a char */ |
| *pCharToReceive = ((us_base->US_RHR) & 0xFF); |
| |
| status = (us_base->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%" PRIX32 "\n\r", status); |
| TRACE_DEBUG("R:0x%" PRIX32 "\n\r", us_base->US_CSR); |
| TRACE_DEBUG("Nb:0x%" PRIX32 "\n\r", us_base->US_NER ); |
| us_base->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 |
| */ |
| uint32_t ISO7816_SendChar( uint8_t CharToSend, Usart_info *usart ) |
| { |
| uint32_t status; |
| |
| Usart *us_base = usart->base; |
| uint32_t us_id = usart->id; |
| |
| if( usart->state == USART_RCV ) { |
| us_base->US_CR = US_CR_RSTSTA | US_CR_RSTIT | US_CR_RSTNACK; |
| usart->state = USART_SEND; |
| } |
| |
| /* Wait USART ready for transmit */ |
| int i = 0; |
| while((us_base->US_CSR & (US_CSR_TXRDY)) == 0) { |
| i++; |
| if (!(i%1000000)) { |
| printf("s: %x ", us_base->US_CSR); |
| printf("s: %x\r\n", us_base->US_RHR & 0xFF); |
| us_base->US_CR = US_CR_RSTTX; |
| us_base->US_CR = US_CR_RSTRX; |
| } |
| } |
| /* There is no character in the US_THR */ |
| |
| /* Transmit a char */ |
| us_base->US_THR = CharToSend; |
| |
| TRACE_ERROR("Sx%02X\r\n", CharToSend); |
| |
| status = (us_base->US_CSR&(US_CSR_OVRE|US_CSR_FRAME| |
| US_CSR_PARE|US_CSR_TIMEOUT|US_CSR_NACK| |
| (1<<10))); |
| |
| if (status != 0 ) { |
| TRACE_INFO("******* status: 0x%" PRIX32 " (Overrun: %" PRIX32 |
| ", NACK: %" PRIX32 ", Timeout: %" PRIX32 ", underrun: %" PRIX32 ")\n\r", |
| status, ((status & US_CSR_OVRE)>> 5), ((status & US_CSR_NACK) >> 13), |
| ((status & US_CSR_TIMEOUT) >> 8), ((status & (1 << 10)) >> 10)); |
| TRACE_INFO("E (USART CSR reg):0x%" PRIX32 "\n\r", us_base->US_CSR); |
| TRACE_INFO("Nb (Number of errors):0x%" PRIX32 "\n\r", us_base->US_NER ); |
| us_base->US_CR = US_CR_RSTSTA; |
| } |
| |
| /* Return status */ |
| return( status ); |
| } |
| |
| |
| /** |
| * Iso 7816 ICC power on |
| */ |
| static void ISO7816_IccPowerOn( void ) |
| { |
| /* Set RESET Master Card */ |
| if (st_pinIso7816RstMC) { |
| PIO_Set(st_pinIso7816RstMC); |
| } |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * Exported functions |
| *----------------------------------------------------------------------------*/ |
| |
| /** |
| * Iso 7816 ICC power off |
| */ |
| void ISO7816_IccPowerOff( void ) |
| { |
| /* Clear RESET Master Card */ |
| if (st_pinIso7816RstMC) { |
| PIO_Clear(st_pinIso7816RstMC); |
| } |
| } |
| |
| /** |
| * Transfert Block TPDU T=0 |
| * \param pAPDU APDU buffer |
| * \param pMessage Message buffer |
| * \param wLength Block length |
| * \param indexMsg Message index |
| * \return 0 on success, content of US_CSR otherwise |
| */ |
| uint32_t ISO7816_XfrBlockTPDU_T0(const uint8_t *pAPDU, |
| uint8_t *pMessage, |
| uint16_t wLength, |
| uint16_t *retlen ) |
| { |
| uint16_t NeNc; |
| uint16_t indexApdu = 4; |
| uint16_t indexMsg = 0; |
| uint8_t SW1 = 0; |
| uint8_t procByte; |
| uint8_t cmdCase; |
| uint32_t status = 0; |
| |
| TRACE_INFO("pAPDU[0]=0x%X\n\r",pAPDU[0]); |
| TRACE_INFO("pAPDU[1]=0x%X\n\r",pAPDU[1]); |
| TRACE_INFO("pAPDU[2]=0x%X\n\r",pAPDU[2]); |
| TRACE_INFO("pAPDU[3]=0x%X\n\r",pAPDU[3]); |
| TRACE_INFO("pAPDU[4]=0x%X\n\r",pAPDU[4]); |
| TRACE_INFO("pAPDU[5]=0x%X\n\r",pAPDU[5]); |
| TRACE_INFO("wlength=%d\n\r",wLength); |
| |
| ISO7816_SendChar( pAPDU[0], &usart_sim ); /* CLA */ |
| ISO7816_SendChar( pAPDU[1], &usart_sim ); /* INS */ |
| ISO7816_SendChar( pAPDU[2], &usart_sim ); /* P1 */ |
| ISO7816_SendChar( pAPDU[3], &usart_sim ); /* P2 */ |
| ISO7816_SendChar( pAPDU[4], &usart_sim ); /* P3 */ |
| |
| /* Handle the four structures of command APDU */ |
| indexApdu = 5; |
| |
| 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 { |
| status = ISO7816_GetChar(&procByte, &usart_sim); |
| if (status != 0) { |
| return status; |
| } |
| TRACE_INFO("procByte: 0x%X\n\r", procByte); |
| /* Handle NULL */ |
| if ( procByte == ISO_NULL_VAL ) { |
| TRACE_INFO("INS\n\r"); |
| continue; |
| } |
| /* Handle SW1 */ |
| else if ( ((procByte & 0xF0) ==0x60) || ((procByte & 0xF0) ==0x90) ) { |
| TRACE_INFO("SW1\n\r"); |
| SW1 = 1; |
| } |
| /* Handle INS */ |
| else if ( pAPDU[1] == procByte) { |
| TRACE_INFO("HdlINS\n\r"); |
| if (cmdCase == CASE2) { |
| /* receive data from card */ |
| do { |
| status = ISO7816_GetChar(&pMessage[indexMsg++], &usart_sim); |
| } while(( 0 != --NeNc) && (status == 0) ); |
| if (status != 0) { |
| return status; |
| } |
| } |
| else { |
| /* Send data */ |
| do { |
| TRACE_INFO("Send %X", pAPDU[indexApdu]); |
| ISO7816_SendChar(pAPDU[indexApdu++], &usart_sim); |
| } while( 0 != --NeNc ); |
| } |
| } |
| /* Handle INS ^ 0xff */ |
| else |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wsign-compare" |
| if ( pAPDU[1] == (procByte ^ 0xff)) { |
| #pragma GCC diagnostic pop |
| TRACE_INFO("HdlINS+\n\r"); |
| if (cmdCase == CASE2) { |
| /* receive data from card */ |
| status = ISO7816_GetChar(&pMessage[indexMsg++], &usart_sim); |
| if (status != 0) { |
| return status; |
| } |
| TRACE_INFO("Rcv: 0x%X\n\r", pMessage[indexMsg-1]); |
| } |
| else { |
| status = ISO7816_SendChar(pAPDU[indexApdu++], &usart_sim); |
| if (status != 0) { |
| return status; |
| } |
| } |
| NeNc--; |
| } |
| else { |
| /* ?? */ |
| TRACE_INFO("procByte=0x%X\n\r", procByte); |
| break; |
| } |
| } while (NeNc != 0); |
| |
| /* Status Bytes */ |
| if (SW1 == 0) { |
| status = ISO7816_GetChar(&pMessage[indexMsg++], &usart_sim); /* SW1 */ |
| if (status != 0) { |
| return status; |
| } |
| } |
| else { |
| pMessage[indexMsg++] = procByte; |
| } |
| status = ISO7816_GetChar(&pMessage[indexMsg++], &usart_sim); /* SW2 */ |
| if (status != 0) { |
| return status; |
| } |
| |
| TRACE_WARNING("SW1=0x%X, SW2=0x%X\n\r", pMessage[indexMsg-2], pMessage[indexMsg-1]); |
| |
| *retlen = indexMsg; |
| return status; |
| |
| } |
| |
| /** |
| * 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"); |
| USART_SIM->US_BRGR = 13; |
| } |
| |
| /** |
| * Stop clock ISO7816 |
| */ |
| void ISO7816_StopClock( void ) |
| { |
| TRACE_DEBUG("ISO7816_StopClock\n\r"); |
| USART_SIM->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 |
| * \return 0: if timeout else status of US_CSR |
| */ |
| uint32_t ISO7816_Datablock_ATR( uint8_t* pAtr, uint8_t* pLength ) |
| { |
| uint32_t i; |
| uint32_t j; |
| uint32_t y; |
| uint32_t status = 0; |
| |
| *pLength = 0; |
| |
| /* Read ATR TS */ |
| // FIXME: There should always be a check for the GetChar return value..0 means timeout |
| status = ISO7816_GetChar(&pAtr[0], &usart_sim); |
| if (status != 0) { |
| return status; |
| } |
| |
| /* Read ATR T0 */ |
| status = ISO7816_GetChar(&pAtr[1], &usart_sim); |
| if (status != 0) { |
| return status; |
| } |
| y = pAtr[1] & 0xF0; |
| i = 2; |
| |
| /* Read ATR Ti */ |
| while (y && (status == 0)) { |
| |
| if (y & 0x10) { /* TA[i] */ |
| status = ISO7816_GetChar(&pAtr[i++], &usart_sim); |
| } |
| if (y & 0x20) { /* TB[i] */ |
| status = ISO7816_GetChar(&pAtr[i++], &usart_sim); |
| } |
| if (y & 0x40) { /* TC[i] */ |
| status = ISO7816_GetChar(&pAtr[i++], &usart_sim); |
| } |
| if (y & 0x80) { /* TD[i] */ |
| status = ISO7816_GetChar(&pAtr[i], &usart_sim); |
| y = pAtr[i++] & 0xF0; |
| } |
| else { |
| y = 0; |
| } |
| } |
| if (status != 0) { |
| return status; |
| } |
| |
| /* Historical Bytes */ |
| y = pAtr[1] & 0x0F; |
| for( j=0; (j < y) && (status == 0); j++ ) { |
| status = ISO7816_GetChar(&pAtr[i++], &usart_sim); |
| } |
| |
| if (status != 0) { |
| return status; |
| } |
| |
| *pLength = i; |
| return status; |
| } |
| |
| /** |
| * 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 */ |
| USART_SIM->US_BRGR = BOARD_MCK / (dwClockFrequency*1000); |
| |
| ClockFrequency = BOARD_MCK / USART_SIM->US_BRGR; |
| |
| USART_SIM->US_FIDI = (ClockFrequency)/dwDataRate; |
| |
| } |
| |
| /** |
| * Pin status for ISO7816 RESET |
| * \return 1 if the Pin RstMC is high; otherwise 0. |
| */ |
| uint8_t ISO7816_StatusReset( void ) |
| { |
| if (st_pinIso7816RstMC) { |
| return PIO_Get(st_pinIso7816RstMC); |
| } |
| return 0; |
| } |
| |
| /** |
| * cold reset |
| */ |
| void ISO7816_cold_reset( void ) |
| { |
| volatile uint32_t i; |
| |
| /* tb: wait ??? cycles*/ |
| for( i=0; i<(400*(BOARD_MCK/1000000)); i++ ) { |
| } |
| |
| USART_SIM->US_RHR; |
| USART_SIM->US_CR = US_CR_RSTSTA | US_CR_RSTIT | US_CR_RSTNACK; |
| |
| ISO7816_IccPowerOn(); |
| } |
| |
| /** |
| * Warm reset |
| */ |
| void ISO7816_warm_reset( void ) |
| { |
| volatile uint32_t i; |
| |
| // Clears Reset |
| ISO7816_IccPowerOff(); |
| |
| /* tb: wait ??? cycles */ |
| for( i=0; i<(400*(BOARD_MCK/1000000)); i++ ) { |
| } |
| |
| USART_SIM->US_RHR; |
| USART_SIM->US_CR = US_CR_RSTSTA | US_CR_RSTIT | US_CR_RSTNACK; |
| |
| // Sets Reset |
| 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]); |
| i++; |
| } |
| printf("\n\r\n\r"); |
| |
| } |
| |
| void ISO7816_Set_Reset_Pin(const Pin *pPinIso7816RstMC) { |
| /* Pin ISO7816 initialize */ |
| st_pinIso7816RstMC = (Pin *)pPinIso7816RstMC; |
| } |
| |
| /** Initializes a ISO driver |
| * \param pPinIso7816RstMC Pin ISO 7816 Rst MC |
| */ |
| void ISO7816_Init( Usart_info *usart, bool master_clock ) |
| { |
| uint32_t clk; |
| TRACE_DEBUG("ISO_Init\n\r"); |
| |
| Usart *us_base = usart->base; |
| uint32_t us_id = usart->id; |
| |
| if (master_clock == true) { |
| clk = US_MR_USCLKS_MCK; |
| } else { |
| clk = US_MR_USCLKS_SCK; |
| } |
| |
| USART_Configure( us_base, |
| US_MR_USART_MODE_IS07816_T_0 |
| | clk |
| | US_MR_NBSTOP_1_BIT |
| | US_MR_PAR_EVEN |
| | US_MR_CHRL_8_BIT |
| | US_MR_CLKO |
| | US_MR_INACK /* Inhibit errors */ |
| | (3<<24), /* MAX_ITERATION */ |
| 1, |
| 0); |
| |
| /* Disable interrupts */ |
| us_base->US_IDR = (uint32_t) -1; |
| |
| /* Configure USART */ |
| PMC_EnablePeripheral(us_id); |
| |
| us_base->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 */ |
| if (master_clock == true) { |
| us_base->US_BRGR = BOARD_MCK / (372*9600); |
| } else { |
| us_base->US_BRGR = US_BRGR_CD(1); |
| } |
| } |
| |