STM32CubeF1/Projects/STM3210E_EVAL/Examples/SMARTCARD/SMARTCARD_T0/Src/smartcard.c

678 lines
20 KiB
C

/**
******************************************************************************
* @file SMARTCARD_T0/Src/smartcard.c
* @author MCD Application Team
* @brief This file provides all the Smartcard firmware functions.
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2016 STMicroelectronics.
* All rights reserved.</center></h2>
*
* 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****/