/** ****************************************************************************** * @file SMARTCARD_T0/Src/smartcard.c * @author MCD Application Team * @brief This file provides all the Smartcard firmware functions. ****************************************************************************** * @attention * *

© Copyright (c) 2016 STMicroelectronics. * All rights reserved.

* * This software component is licensed by ST under BSD 3-Clause license, * the "License"; You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause * ****************************************************************************** */ /** @addtogroup STM32F1xx_HAL_Examples * @{ */ /** @addtogroup SMARTCARD_T0 * @{ */ /* Includes ------------------------------------------------------------------*/ #include "smartcard.h" /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ /* Global variables definition and initialization ----------------------------*/ SC_ATR SC_A2R; uint8_t SC_ATR_Table[40] = {0}; static __IO uint8_t SCData = 0; static uint32_t F_Table[16] = {372, 372, 558, 744, 1116, 1488, 1860, 0, 0, 512, 768, 1024, 1536, 2048, 0, 0}; static uint32_t D_Table[16] = {0, 1, 2, 4, 8, 16, 32, 0, 12, 20, 0, 0, 0, 0, 0, 0}; SMARTCARD_HandleTypeDef SCHandle; /* Private function prototypes -----------------------------------------------*/ /* Transport Layer -----------------------------------------------------------*/ /*--------------APDU-----------*/ void SC_SendData(SC_ADPU_Commands *SC_ADPU, SC_ADPU_Response *SC_ResponseStatus); /*------------ ATR ------------*/ static void SC_AnswerReq(SC_State *SCState, uint8_t *card, uint8_t length); /* Ask ATR */ static uint8_t SC_decode_Answer2reset(uint8_t *card); /* Decode ATR */ /* Physical Port Layer -------------------------------------------------------*/ static void SC_Init(void); static void SC_DeInit(void); static void SC_VoltageConfig(uint32_t SC_Voltage); static uint8_t SC_Detect(void); /* Private functions ---------------------------------------------------------*/ /** * @brief Handles all Smartcard states and serves to send and receive all * communication data between Smartcard and reader. * @param SCState: pointer to an SC_State enumeration that will contain the * Smartcard state. * @param SC_ADPU: pointer to an SC_ADPU_Commands structure that will be initialized. * @param SC_Response: pointer to a SC_ADPU_Response structure which will be initialized. * @retval None */ void SC_Handler(SC_State *SCState, SC_ADPU_Commands *SC_ADPU, SC_ADPU_Response *SC_Response) { uint32_t i = 0; switch(*SCState) { case SC_POWER_ON: if (SC_ADPU->Header.INS == SC_GET_A2R) { /* Smartcard initialization --------------------------------------------*/ SC_Init(); /* Reset Data from SC buffer -------------------------------------------*/ for (i = 0; i < 40; i++) { SC_ATR_Table[i] = 0; } /* Reset SC_A2R Structure ----------------------------------------------*/ SC_A2R.TS = 0; SC_A2R.T0 = 0; for (i = 0; i < SETUP_LENGTH; i++) { SC_A2R.T[i] = 0; } for (i = 0; i < HIST_LENGTH; i++) { SC_A2R.H[i] = 0; } SC_A2R.Tlength = 0; SC_A2R.Hlength = 0; /* Next State ----------------------------------------------------------*/ *SCState = SC_RESET_LOW; } break; case SC_RESET_LOW: if(SC_ADPU->Header.INS == SC_GET_A2R) { /* If card is detected then Power ON, Card Reset and wait for an answer) */ if (SC_Detect()) { while(((*SCState) != SC_POWER_OFF) && ((*SCState) != SC_ACTIVE)) { SC_AnswerReq(SCState, (uint8_t *)&SC_ATR_Table, 40); /* Check for answer to reset */ } } else { (*SCState) = SC_POWER_OFF; } } break; case SC_ACTIVE: if (SC_ADPU->Header.INS == SC_GET_A2R) { if(SC_decode_Answer2reset(&SC_ATR_Table[0]) == T0_PROTOCOL) { (*SCState) = SC_ACTIVE_ON_T0; } else { (*SCState) = SC_POWER_OFF; } } break; case SC_ACTIVE_ON_T0: SC_SendData(SC_ADPU, SC_Response); break; case SC_POWER_OFF: SC_DeInit(); /* Disable Smartcard interface */ break; default: (*SCState) = SC_POWER_OFF; } } /** * @brief Enables or disables the power to the Smartcard. * @param NewState: new state of the Smartcard power supply. * This parameter can be: ENABLE or DISABLE. * @retval None */ void SC_PowerCmd(FunctionalState NewState) { if(NewState != DISABLE) { HAL_GPIO_WritePin(SC_CMDVCC_GPIO_PORT, SC_CMDVCC_PIN, GPIO_PIN_RESET); } else { HAL_GPIO_WritePin(SC_CMDVCC_GPIO_PORT, SC_CMDVCC_PIN, GPIO_PIN_SET); } } /** * @brief Sets or clears the Smartcard reset pin. * @param ResetState: this parameter specifies the state of the Smartcard * reset pin. BitVal must be one of the GPIO_PinState enum values: * @arg GPIO_PIN_RESET: to clear the port pin. * @arg GPIO_PIN_SET: to set the port pin. * @retval None */ void SC_Reset(GPIO_PinState ResetState) { HAL_GPIO_WritePin(SC_RESET_GPIO_PORT, SC_RESET_PIN, ResetState); } /** * @brief Resends the byte that failed to be received (by the Smartcard) correctly. * @param None * @retval None */ void SC_ParityErrorHandler(void) { HAL_SMARTCARD_Transmit(&SCHandle, (uint8_t *)&SCData, 1, SC_TRANSMIT_TIMEOUT); } /** * @brief Configures the IO speed (BaudRate) communication. * @param None * @retval None */ void SC_PTSConfig(void) { uint32_t workingbaudrate = 0, apbclock = 0; uint8_t PPSConfirmStatus = 1; uint8_t ptscmd[4] = {0x00}; uint8_t ptsanswer[4] = {0x00}; /* Reconfigure the USART Baud Rate -----------------------------------------*/ apbclock = HAL_RCC_GetPCLK1Freq(); apbclock /= ((SC_USART->GTPR & (uint16_t)0x00FF) * 2); /* Enable the DMA Receive (Set DMAR bit only) to enable interrupt generation in case of a framing error FE */ __HAL_SMARTCARD_DMA_REQUEST_ENABLE(&SCHandle, SMARTCARD_DMAREQ_RX); if((SC_A2R.T0 & (uint8_t)0x10) == 0x10) { if(SC_A2R.T[0] != 0x11) { /* PPSS identifies the PPS request or response and is equal to 0xFF */ ptscmd[0] = 0xFF; /* PPS0 indicates by the bits b5, b6, b7 equal to 1 the presence of the optional bytes PPSI1, PPS2, PPS3 respectively */ ptscmd[1] = 0x10; /* PPS1 allows the interface device to propose value of F and D to the card */ ptscmd[2] = SC_A2R.T[0]; /* PCK check character */ ptscmd[3] = (uint8_t)0xFF^(uint8_t)0x10^(uint8_t)SC_A2R.T[0]; /* Start the receive IT process: to receive the command answer from the card */ HAL_SMARTCARD_Receive_IT(&SCHandle, (uint8_t *)ptsanswer, 4); /* Send command */ HAL_SMARTCARD_Transmit(&SCHandle, (uint8_t *)ptscmd, 4, SC_TRANSMIT_TIMEOUT); /* Disable the DMA Receive Request */ __HAL_SMARTCARD_DMA_REQUEST_DISABLE(&SCHandle, SMARTCARD_DMAREQ_RX); /* Wait until receiving the answer from the card */ while(HAL_SMARTCARD_GetState(&SCHandle) != HAL_SMARTCARD_STATE_READY) {} /* Check the received command answer */ if((ptsanswer[0] != 0xFF) || (ptsanswer[1] != 0x10) || (ptsanswer[2] != SC_A2R.T[0]) || (ptsanswer[3] != ((uint8_t)0xFF^(uint8_t)0x10^(uint8_t)SC_A2R.T[0]))) { /* PPSS, PPS0, PPS1 and PCK exchange unsuccessful */ PPSConfirmStatus = 0x00; } /* PPS exchange successful */ if(PPSConfirmStatus == 0x01) { workingbaudrate = apbclock * D_Table[(SC_A2R.T[0] & (uint8_t)0x0F)]; workingbaudrate /= F_Table[((SC_A2R.T[0] >> 4) & (uint8_t)0x0F)]; SCHandle.Init.BaudRate = workingbaudrate; SCHandle.Init.WordLength = SMARTCARD_WORDLENGTH_9B; SCHandle.Init.StopBits = SMARTCARD_STOPBITS_1_5; SCHandle.Init.Parity = SMARTCARD_PARITY_EVEN; SCHandle.Init.Mode = SMARTCARD_MODE_TX_RX; SCHandle.Init.CLKPolarity = SMARTCARD_POLARITY_LOW; SCHandle.Init.CLKPhase = SMARTCARD_PHASE_1EDGE; SCHandle.Init.CLKLastBit = SMARTCARD_LASTBIT_ENABLE; SCHandle.Init.Prescaler = SMARTCARD_PRESCALER_SYSCLK_DIV10; SCHandle.Init.GuardTime = 16; SCHandle.Init.NACKState = SMARTCARD_NACK_ENABLE; SCHandle.Instance = SC_USART; if(HAL_SMARTCARD_Init(&SCHandle) != HAL_OK) { while (1); } } } } } /** * @brief Manages the Smartcard transport layer: send APDU commands and receives * the APDU response. * @param SC_ADPU: pointer to a SC_ADPU_Commands structure which will be initialized. * @param SC_ADPU_Response: pointer to a SC_ADPU_Response structure which will be initialized. * @retval None */ void SC_SendData(SC_ADPU_Commands *SC_ADPU, SC_ADPU_Response *SC_ResponseStatus) { uint32_t i = 0; uint8_t command[5] = {0x00}; uint8_t answer[40] = {0x00}; /* Reset response buffer ---------------------------------------------------*/ for(i = 0; i < LC_MAX; i++) { SC_ResponseStatus->Data[i] = 0; } SC_ResponseStatus->SW1 = 0; SC_ResponseStatus->SW2 = 0; /* Enable the DMA Receive (Set DMAR bit only) to enable interrupt generation in case of a framing error FE */ __HAL_SMARTCARD_DMA_REQUEST_ENABLE(&SCHandle, SMARTCARD_DMAREQ_RX); /* Send header -------------------------------------------------------------*/ command[0] = SC_ADPU->Header.CLA; command[1] = SC_ADPU->Header.INS; command[2] = SC_ADPU->Header.P1; command[3] = SC_ADPU->Header.P2; /* Send body length to/from SC ---------------------------------------------*/ if(SC_ADPU->Body.LC) { command[4] = SC_ADPU->Body.LC; HAL_SMARTCARD_Transmit(&SCHandle, (uint8_t *)command, 5, SC_TRANSMIT_TIMEOUT); } else if(SC_ADPU->Body.LE) { command[4] = SC_ADPU->Body.LE; HAL_SMARTCARD_Transmit(&SCHandle, (uint8_t *)command, 5, SC_TRANSMIT_TIMEOUT); } /* Flush the SC_USART DR */ __HAL_SMARTCARD_FLUSH_DRREGISTER(&SCHandle); /* Start the receive IT process: to receive the command answer from the card */ HAL_SMARTCARD_Receive_IT(&SCHandle, (uint8_t *)&answer[0], 1); /* Wait until receiving the answer from the card */ while(HAL_SMARTCARD_GetState(&SCHandle) != HAL_SMARTCARD_STATE_READY) {} /* -------------------------------------------------------- Wait Procedure byte from card: 1 - ACK 2 - NULL 3 - SW1; SW2 -------------------------------------------------------- */ if(((answer[0] & (uint8_t)0xF0) == 0x60) || ((answer[0] & (uint8_t)0xF0) == 0x90)) { /* SW1 received */ SC_ResponseStatus->SW1 = answer[0]; if(HAL_SMARTCARD_Receive_IT(&SCHandle, (uint8_t *)&answer[1], 1) == HAL_OK) { /* SW2 received */ SC_ResponseStatus->SW2 = answer[1]; } } else if(((answer[0] & (uint8_t)0xFE) == (((uint8_t)~(SC_ADPU->Header.INS)) & (uint8_t)0xFE)) || \ ((answer[0] & (uint8_t)0xFE) == (SC_ADPU->Header.INS & (uint8_t)0xFE))) { SC_ResponseStatus->Data[0] = answer[0];/* ACK received */ } /* If no status bytes received ---------------------------------------------*/ if(SC_ResponseStatus->SW1 == 0x00) { /* Send body data to SC --------------------------------------------------*/ if(SC_ADPU->Body.LC) { /* Send body data */ HAL_SMARTCARD_Transmit(&SCHandle, (uint8_t *)&SC_ADPU->Body.Data[0], SC_ADPU->Body.LC, SC_TRANSMIT_TIMEOUT); /* Flush the SC_USART DR */ __HAL_SMARTCARD_FLUSH_DRREGISTER(&SCHandle); /* Disable the DMA Receive (Reset DMAR bit only) */ __HAL_SMARTCARD_DMA_REQUEST_DISABLE(&SCHandle, SMARTCARD_DMAREQ_RX); /* Start the receive IT process: to receive the command answer from the card */ HAL_SMARTCARD_Receive_IT(&SCHandle, (uint8_t *)&answer[0], 2); /* Wait until receiving the answer from the card */ while(HAL_SMARTCARD_GetState(&SCHandle) != HAL_SMARTCARD_STATE_READY) {} /* Decode the SW1 */ SC_ResponseStatus->SW1 = answer[0]; /* Decode the SW2 */ SC_ResponseStatus->SW2 = answer[1]; } /* Or receive body data from SC ------------------------------------------*/ else if(SC_ADPU->Body.LE) { /* Start the receive IT process: to receive the command answer from the card */ HAL_SMARTCARD_Receive_IT(&SCHandle, (uint8_t *)&answer[0], (SC_ADPU->Body.LE +2)); /* Wait until receiving the answer from the card */ while(HAL_SMARTCARD_GetState(&SCHandle) != HAL_SMARTCARD_STATE_READY) {} /* Decode the body data */ for(i = 0; i < SC_ADPU->Body.LE; i++) { SC_ResponseStatus->Data[i] = answer[i]; } /* Decode the SW1 */ SC_ResponseStatus->SW1 = answer[SC_ADPU->Body.LE]; /* Decode the SW2 */ SC_ResponseStatus->SW2 = answer[SC_ADPU->Body.LE +1]; } } } /** * @brief Requests the reset answer from card. * @param SCState: pointer to an SC_State enumeration that will contain the Smartcard state. * @param card: pointer to a buffer which will contain the card ATR. * @param length: maximum ATR length * @retval None */ static void SC_AnswerReq(SC_State *SCState, uint8_t *card, uint8_t length) { switch(*SCState) { case SC_RESET_LOW: /* Check response with reset low -----------------------------------------*/ HAL_SMARTCARD_Receive(&SCHandle, card, length, SC_RECEIVE_TIMEOUT); if(card[0] != 0x00) { (*SCState) = SC_ACTIVE; SC_Reset(GPIO_PIN_SET); } else { (*SCState) = SC_RESET_HIGH; } break; case SC_RESET_HIGH: /* Check response with reset high ----------------------------------------*/ SC_Reset(GPIO_PIN_SET); /* Reset High */ HAL_SMARTCARD_Receive(&SCHandle, card, length, SC_RECEIVE_TIMEOUT); if(card[0]) { (*SCState) = SC_ACTIVE; } else { (*SCState) = SC_POWER_OFF; } break; case SC_ACTIVE: break; case SC_POWER_OFF: /* Close Connection if no answer received --------------------------------*/ SC_Reset(GPIO_PIN_SET); /* Reset high - a bit is used as level shifter from 3.3 to 5 V */ SC_PowerCmd(DISABLE); break; default: (*SCState) = SC_RESET_LOW; break; } } /** * @brief Decodes the Answer to reset received from card. * @param card: pointer to the buffer containing the card ATR. * @retval Smartcard communication Protocol */ static uint8_t SC_decode_Answer2reset(uint8_t *card) { uint32_t i = 0, flag = 0, buf = 0, protocol = 0; SC_A2R.TS = card[0]; /* Initial character */ SC_A2R.T0 = card[1]; /* Format character */ SC_A2R.Hlength = SC_A2R.T0 & (uint8_t)0x0F; if ((SC_A2R.T0 & (uint8_t)0x80) == 0x80) { flag = 1; } for (i = 0; i < 4; i++) { SC_A2R.Tlength = SC_A2R.Tlength + (((SC_A2R.T0 & (uint8_t)0xF0) >> (4 + i)) & (uint8_t)0x1); } for (i = 0; i < SC_A2R.Tlength; i++) { SC_A2R.T[i] = card[i + 2]; } if ((SC_A2R.T0 & (uint8_t)0x80) == 0x00) { protocol = 0; } else { protocol = SC_A2R.T[SC_A2R.Tlength - 1] & (uint8_t)0x0F; } while (flag) { if ((SC_A2R.T[SC_A2R.Tlength - 1] & (uint8_t)0x80) == 0x80) { flag = 1; } else { flag = 0; } buf = SC_A2R.Tlength; SC_A2R.Tlength = 0; for (i = 0; i < 4; i++) { SC_A2R.Tlength = SC_A2R.Tlength + (((SC_A2R.T[buf - 1] & (uint8_t)0xF0) >> (4 + i)) & (uint8_t)0x1); } for (i = 0;i < SC_A2R.Tlength; i++) { SC_A2R.T[buf + i] = card[i + 2 + buf]; } SC_A2R.Tlength += (uint8_t)buf; } for (i = 0; i < SC_A2R.Hlength; i++) { SC_A2R.H[i] = card[i + 2 + SC_A2R.Tlength]; } return (uint8_t)protocol; } /** * @brief Initializes all peripheral used for Smartcard interface. * @param None * @retval None */ static void SC_Init(void) { /* SC_USART configuration --------------------------------------------------*/ /* SC_USART configured as follows: - Word Length = 9 Bits - 1.5 Stop Bit - Even parity - BaudRate = 8064 baud - Hardware flow control disabled (RTS and CTS signals) - Tx and Rx enabled - USART Clock enabled */ /* USART Clock set to 3 MHz (PCLK1 (36 MHz) / 12) => prescaler set to 0x06 */ SCHandle.Instance = SC_USART; SCHandle.Init.BaudRate = 9408; /* Starting baudrate = 3MHz / 372etu */ SCHandle.Init.WordLength = SMARTCARD_WORDLENGTH_9B; SCHandle.Init.StopBits = SMARTCARD_STOPBITS_1_5; SCHandle.Init.Parity = SMARTCARD_PARITY_EVEN; SCHandle.Init.Mode = SMARTCARD_MODE_TX_RX; SCHandle.Init.CLKPolarity = SMARTCARD_POLARITY_LOW; SCHandle.Init.CLKPhase = SMARTCARD_PHASE_1EDGE; SCHandle.Init.CLKLastBit = SMARTCARD_LASTBIT_ENABLE; SCHandle.Init.Prescaler = SMARTCARD_PRESCALER_SYSCLK_DIV10; SCHandle.Init.GuardTime = 16; SCHandle.Init.NACKState = SMARTCARD_NACK_ENABLE; HAL_SMARTCARD_Init(&SCHandle); /* Set RSTIN HIGH */ SC_Reset(GPIO_PIN_SET); } /** * @brief Deinitializes all resources used by the Smartcard interface. * @param None * @retval None */ static void SC_DeInit(void) { /* Disable CMDVCC */ SC_PowerCmd(DISABLE); /* Deinitializes the SCHandle */ HAL_SMARTCARD_DeInit(&SCHandle); } /** * @brief Configures the card power voltage. * @param SC_Voltage: specifies the card power voltage. * This parameter can be one of the following values: * @arg SC_VOLTAGE_5V: 5V cards. * @arg SC_VOLTAGE_3V: 3V cards. * @retval None */ static void SC_VoltageConfig(uint32_t SC_Voltage) { if(SC_Voltage == SC_VOLTAGE_5V) { /* Select Smartcard 5V */ HAL_GPIO_WritePin(SC_3_5V_GPIO_PORT, SC_3_5V_PIN, GPIO_PIN_SET); } else { /* Select Smartcard 3V */ HAL_GPIO_WritePin(SC_3_5V_GPIO_PORT, SC_3_5V_PIN, GPIO_PIN_RESET); } } /** * @brief Configures GPIO hardware resources used for Samrtcard. * @param None * @retval None */ void SC_IOConfig(void) { GPIO_InitTypeDef GPIO_InitStruct; /* Enable GPIO clocks */ SC_3_5V_CLK_ENABLE(); SC_RESET_CLK_ENABLE(); SC_CMDVCC_CLK_ENABLE(); SC_OFF_CLK_ENABLE(); /* Configure Smartcard CMDVCC pin */ GPIO_InitStruct.Pin = SC_CMDVCC_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(SC_CMDVCC_GPIO_PORT, &GPIO_InitStruct); /* Configure Smartcard Reset pin */ GPIO_InitStruct.Pin = SC_RESET_PIN; HAL_GPIO_Init(SC_RESET_GPIO_PORT, &GPIO_InitStruct); /* Configure Smartcard 3/5V pin */ GPIO_InitStruct.Pin = SC_3_5V_PIN; HAL_GPIO_Init(SC_3_5V_GPIO_PORT, &GPIO_InitStruct); /* Select 5V */ SC_VoltageConfig(SC_VOLTAGE_5V); /* Disable CMDVCC */ SC_PowerCmd(DISABLE); /* Set RSTIN HIGH */ SC_Reset(GPIO_PIN_SET); /* Configure Smartcard OFF pin */ GPIO_InitStruct.Pin = SC_OFF_PIN; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(SC_OFF_GPIO_PORT, &GPIO_InitStruct); /* Configure the NVIC for Smartcard OFF pin */ HAL_NVIC_SetPriority(SC_OFF_EXTI_IRQn, 0, 0); HAL_NVIC_EnableIRQ(SC_OFF_EXTI_IRQn); } /** * @brief Detects whether the Smartcard is present or not. * @param None. * @retval 0 - Smartcard inserted * 1 - Smartcard not inserted */ static uint8_t SC_Detect(void) { return HAL_GPIO_ReadPin(SC_OFF_GPIO_PORT, SC_OFF_PIN); } /** * @} */ /** * @} */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/