From 98f9d442b44dbe2e3e4b3c8296be7e78d5d05450 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 24 Jul 2011 09:39:28 +0200 Subject: initial import of the usb ccid example for the sam7s --- at91lib/components/iso7816/iso7816_4.c | 620 +++++++++++++++++++++++++++++++++ 1 file changed, 620 insertions(+) create mode 100644 at91lib/components/iso7816/iso7816_4.c (limited to 'at91lib/components/iso7816/iso7816_4.c') diff --git a/at91lib/components/iso7816/iso7816_4.c b/at91lib/components/iso7816/iso7816_4.c new file mode 100644 index 0000000..f30f898 --- /dev/null +++ b/at91lib/components/iso7816/iso7816_4.c @@ -0,0 +1,620 @@ +/* ---------------------------------------------------------------------------- + * 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 +/// +/// ISO 7816 driver +/// +/// !Usage +/// +/// Explanation on the usage of the code made available through the header file. +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include "iso7816_4.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 +//------------------------------------------------------------------------------ +/// Variable for state of send and receive froom USART +static unsigned char StateUsartGlobal = USART_RCV; +/// Pin reset master card +static Pin st_pinIso7816RstMC; + +//----------------------------------------------------------------------------- +// 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 unsigned int ISO7816_GetChar( unsigned char *pCharToReceive ) +{ + unsigned int status; + unsigned int timeout=0; + + if( StateUsartGlobal == USART_SEND ) { + while((AT91C_BASE_US0->US_CSR & AT91C_US_TXEMPTY) == 0) {} + AT91C_BASE_US0->US_CR = AT91C_US_RSTSTA | AT91C_US_RSTIT | AT91C_US_RSTNACK; + StateUsartGlobal = USART_RCV; + } + + // Wait USART ready for reception + while( ((AT91C_BASE_US0->US_CSR & AT91C_US_RXRDY) == 0) ) { + if(timeout++ >6000) { + TRACE_DEBUG("TimeOut\n\r"); + return( 0 ); + } + } + + TRACE_DEBUG("T: %d\n\r", timeout); + + + // At least one complete character has been received and US_RHR has not yet been read. + + // Get a char + *pCharToReceive = ((AT91C_BASE_US0->US_RHR) & 0xFF); + + status = (AT91C_BASE_US0->US_CSR&(AT91C_US_OVRE|AT91C_US_FRAME| + AT91C_US_PARE|AT91C_US_TIMEOUT|AT91C_US_NACK| + (1<<10))); + + if (status != 0 ) { + // TRACE_DEBUG("R:0x%X\n\r", status); + TRACE_DEBUG("R:0x%X\n\r", AT91C_BASE_US0->US_CSR); + TRACE_DEBUG("Nb:0x%X\n\r", AT91C_BASE_US0->US_NER ); + AT91C_BASE_US0->US_CR = AT91C_US_RSTSTA; + } + + // Return status + return( status ); +} + + +//------------------------------------------------------------------------------ +/// Send a char to ISO7816 +/// \param CharToSend char to be send +/// \return status of US_CSR +//------------------------------------------------------------------------------ +static unsigned int ISO7816_SendChar( unsigned char CharToSend ) +{ + unsigned int status; + + if( StateUsartGlobal == USART_RCV ) { + AT91C_BASE_US0->US_CR = AT91C_US_RSTSTA | AT91C_US_RSTIT | AT91C_US_RSTNACK; + StateUsartGlobal = USART_SEND; + } + + // Wait USART ready for transmit + while((AT91C_BASE_US0->US_CSR & AT91C_US_TXRDY) == 0) {} + // There is no character in the US_THR + + // Transmit a char + AT91C_BASE_US0->US_THR = CharToSend; + + status = (AT91C_BASE_US0->US_CSR&(AT91C_US_OVRE|AT91C_US_FRAME| + AT91C_US_PARE|AT91C_US_TIMEOUT|AT91C_US_NACK| + (1<<10))); + + if (status != 0 ) { + TRACE_DEBUG("E:0x%X\n\r", AT91C_BASE_US0->US_CSR); + TRACE_DEBUG("Nb:0x%X\n\r", AT91C_BASE_US0->US_NER ); + AT91C_BASE_US0->US_CR = AT91C_US_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 +//------------------------------------------------------------------------------ +unsigned short ISO7816_XfrBlockTPDU_T0(const unsigned char *pAPDU, + unsigned char *pMessage, + unsigned short wLength ) +{ + unsigned short NeNc; + unsigned short indexApdu = 4; + unsigned short indexMessage = 0; + unsigned char SW1 = 0; + unsigned char procByte; + unsigned char cmdCase; + + 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); + // 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] == (procByte ^ 0xff)) { + 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"); + AT91C_BASE_US0->US_BRGR = 13; +} + +//------------------------------------------------------------------------------ +/// Stop clock ISO7816 +//------------------------------------------------------------------------------ +void ISO7816_StopClock( void ) +{ + TRACE_DEBUG("ISO7816_StopClock\n\r"); + AT91C_BASE_US0->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( unsigned char* pAtr, unsigned char* pLength ) +{ + unsigned int i; + unsigned int j; + unsigned int 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++]); + } + + TRACE_DEBUG_WP("Length = %d", i); + TRACE_DEBUG_WP("ATR = "); + + for (j=0; j < i; j++) { + TRACE_DEBUG_WP("%02x ", pAtr[j]); + } + + + TRACE_DEBUG_WP("\n\r"); + + *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( unsigned int dwClockFrequency, unsigned int dwDataRate ) +{ + unsigned char 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 + AT91C_BASE_US0->US_BRGR = BOARD_MCK / (dwClockFrequency*1000); + + ClockFrequency = BOARD_MCK / AT91C_BASE_US0->US_BRGR; + + AT91C_BASE_US0->US_FIDI = (ClockFrequency)/dwDataRate; + +} + +//------------------------------------------------------------------------------ +/// Pin status for ISO7816 RESET +/// \return 1 if the Pin RstMC is high; otherwise 0. +//------------------------------------------------------------------------------ +unsigned char ISO7816_StatusReset( void ) +{ + return PIO_Get(&st_pinIso7816RstMC); +} + +//------------------------------------------------------------------------------ +/// cold reset +//------------------------------------------------------------------------------ +void ISO7816_cold_reset( void ) +{ + volatile unsigned int i; + + // tb: wait 400 cycles, 3.58MHz => 80µs 48000000Hz (3840) + for( i=0; i<(120*(BOARD_MCK/1000000)); i++ ) { + } + + AT91C_BASE_US0->US_RHR; + AT91C_BASE_US0->US_CR = AT91C_US_RSTSTA | AT91C_US_RSTIT | AT91C_US_RSTNACK; + + ISO7816_IccPowerOn(); +} + +//------------------------------------------------------------------------------ +/// Warm reset +//------------------------------------------------------------------------------ +void ISO7816_warm_reset( void ) +{ + volatile unsigned int i; + + ISO7816_IccPowerOff(); + + // tb: wait 400 cycles, 3.58MHz => 80µs 48000000Hz (3840) + for( i=0; i<(120*(BOARD_MCK/1000000)); i++ ) { + } + + AT91C_BASE_US0->US_RHR; + AT91C_BASE_US0->US_CR = AT91C_US_RSTSTA | AT91C_US_RSTIT | AT91C_US_RSTNACK; + + ISO7816_IccPowerOn(); +} + +//---------------------------------------------------------------------- +/// Decode ATR trace +/// \param pAtr pointer on ATR buffer +//---------------------------------------------------------------------- +void ISO7816_Decode_ATR( unsigned char* pAtr ) +{ + unsigned int i; + unsigned int j; + unsigned int y; + unsigned char offset; + + TRACE_INFO_WP("ATR: Answer To Reset:\n\r"); + TRACE_INFO_WP("TS = 0x%X Initial caracter ",pAtr[0]); + if( pAtr[0] == 0x3B ) { + + TRACE_INFO_WP("Direct Convention\n\r"); + } + else { + if( pAtr[0] == 0x3F ) { + + TRACE_INFO_WP("Inverse Convention\n\r"); + } + else { + TRACE_INFO_WP("BAD Convention\n\r"); + } + } + + TRACE_INFO_WP("T0 = 0x%X Format caracter\n\r",pAtr[1]); + TRACE_INFO_WP(" Number of historical bytes: K = %d\n\r", pAtr[1]&0x0F); + TRACE_INFO_WP(" Presence further interface byte:\n\r"); + if( pAtr[1]&0x80 ) { + TRACE_INFO_WP("TA "); + } + if( pAtr[1]&0x40 ) { + TRACE_INFO_WP("TB "); + } + if( pAtr[1]&0x20 ) { + TRACE_INFO_WP("TC "); + } + if( pAtr[1]&0x10 ) { + TRACE_INFO_WP("TD "); + } + if( pAtr[1] != 0 ) { + TRACE_INFO_WP(" present\n\r"); + } + + i = 2; + y = pAtr[1] & 0xF0; + + // Read ATR Ti + offset = 1; + while (y) { + + if (y & 0x10) { // TA[i] + TRACE_INFO_WP("TA[%d] = 0x%X ", offset, pAtr[i]); + if( offset == 1 ) { + TRACE_INFO_WP("FI = %d ", (pAtr[i]>>8)); + TRACE_INFO_WP("DI = %d", (pAtr[i]&0x0F)); + } + TRACE_INFO_WP("\n\r"); + i++; + } + if (y & 0x20) { // TB[i] + TRACE_INFO_WP("TB[%d] = 0x%X\n\r", offset, pAtr[i]); + i++; + } + if (y & 0x40) { // TC[i] + TRACE_INFO_WP("TC[%d] = 0x%X ", offset, pAtr[i]); + if( offset == 1 ) { + TRACE_INFO_WP("Extra Guard Time: N = %d", pAtr[i]); + } + TRACE_INFO_WP("\n\r"); + i++; + } + if (y & 0x80) { // TD[i] + TRACE_INFO_WP("TD[%d] = 0x%X\n\r", offset, pAtr[i]); + y = pAtr[i++] & 0xF0; + } + else { + y = 0; + } + offset++; + } + + // Historical Bytes + TRACE_INFO_WP("Historical bytes:\n\r"); + y = pAtr[1] & 0x0F; + for( j=0; j < y; j++ ) { + + TRACE_INFO_WP(" 0x%X", pAtr[i]); + if( (pAtr[i] > 0x21) && (pAtr[i] < 0x7D) ) { // ASCII + TRACE_INFO_WP("(%c) ", pAtr[i]); + } + i++; + } + TRACE_INFO_WP("\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( AT91C_BASE_US0, + AT91C_US_USMODE_ISO7816_0 + | AT91C_US_CLKS_CLOCK + | AT91C_US_NBSTOP_1_BIT + | AT91C_US_PAR_EVEN + | AT91C_US_CHRL_8_BITS + | AT91C_US_CKLO + | (3<<24), // MAX_ITERATION + 1, + 0); + + // Configure USART0 + AT91C_BASE_PMC->PMC_PCER = ((unsigned int) 1 << AT91C_ID_US0); + // Disable interrupts + AT91C_BASE_US0->US_IDR = (unsigned int) -1; + + AT91C_BASE_US0->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 + AT91C_BASE_US0->US_BRGR = BOARD_MCK / (372*9600); + + // Write the Timeguard Register + AT91C_BASE_US0->US_TTGR = 5; + + USART_SetTransmitterEnabled(AT91C_BASE_US0, 1); + USART_SetReceiverEnabled(AT91C_BASE_US0, 1); + +} + -- cgit v1.2.3