T6S项目IC卡驱动总结
程序员文章站
2023-12-28 22:15:16
...
IC卡消费是支付行业必须要支持的消费手段,实现IC卡的读卡操作流程相对较简单,现在总结一下,方便以后调试。
1. 硬件连接
主控集成了IC卡接口,我们配置使用即可,如下所示。
#define ICC_DEV_NSLOT 0 //IC卡对应的物理端口
#define ICC_DET_PIN GPIO_PIN_PTA6
#define ICC_CMD_PIN GPIO_PIN_PTA7
#define ICC_CLK_PIN GPIO_PIN_PTA8
#define ICC_RSTN_PIN GPIO_PIN_PTA9
#define ICC_IO_PIN GPIO_PIN_PTA10
2. 初始化设备
drv_icc_init()函数的实现如下所示:
void drv_icc_init(void)
{
/*SCI0 VCCEN信号有效电平选择*/
SYSCTRL->PHER_CTRL |= BIT(20); //高电平有效
//SYSCTRL->PHER_CTRL &= (~BIT(20));
}
void dev_icc_init(void)
{
drv_icc_init(); //硬件相关的初始化
g_psam_existflg = 0;
/*判断有没有PSAM卡*/
if(g_psam_existflg)
{
drv_psam_init();
}
}
3. 打开设备
drv_icc_open()函数的实现如下所示:
s32 drv_icc_open(void)
{
s32 i;
/*使能SCI0时钟*/
SYSCTRL_APBPeriphClockCmd(SYSCTRL_APBPeriph_SCI0, ENABLE);
SYSCTRL_APBPeriphResetCmd(SYSCTRL_APBPeriph_SCI0, ENABLE);
/*SCI0卡检测信号有效电平选择*/
SYSCTRL->PHER_CTRL &= ~BIT(16); //高有效
/*SCI0 VCCEN信号有效电平选择*/
SYSCTRL->PHER_CTRL |= BIT(20); //低有效
/*SCI1卡检测信号有效电平选择*/
SYSCTRL->PHER_CTRL &= ~BIT(17); //高有效
/*SCI1 VCCEN信号有效电平选择*/
SYSCTRL->PHER_CTRL |= BIT(21); //低有效
/*SCI2卡检测信号有效电平选择*/
SYSCTRL->PHER_CTRL &= ~BIT(18); //高有效
/*SCI2 VCCEN信号有效电平选择*/
SYSCTRL->PHER_CTRL |= BIT(22); //低有效
/*配置SCI0管脚*/
dev_gpio_config_mux(ICC_CLK_PIN, MUX_CONFIG_ALT0);
dev_gpio_config_mux(ICC_RSTN_PIN, MUX_CONFIG_ALT0);
dev_gpio_config_mux(ICC_IO_PIN, MUX_CONFIG_ALT0);
dev_gpio_config_mux(ICC_DET_PIN, MUX_CONFIG_ALT0);
#if 1
/*使用外部芯片控制,SCI0_VCCEN*/
dev_gpio_config_mux(ICC_CMD_PIN, MUX_CONFIG_ALT1);
dev_gpio_set_pad(ICC_CMD_PIN, PAD_CTL_PULL_UP);
dev_gpio_direction_output(ICC_CMD_PIN, 1);
#else
dev_gpio_config_mux(ICC_CMD_PIN, MUX_CONFIG_ALT0);
#endif
/*库函数初始化*/
SCI_ConfigEMV(0x01, 3000000);
iso7816_device_init();
g_icc_active = 0; //上电成功的标志位
/*使能SCI0中断*/
NVIC_ClearPendingIRQ(SCI0_IRQn);
NVIC_EnableIRQ(SCI0_IRQn);
i = iso7816_getlibversion(); //读取库的版本信息
ICC_DEBUG("icc_ver=%08X\r\n", i);
return 0;
#if 0
//GPIO_PinRemapConfig(GPIOA, GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_14, GPIO_Remap_0);
SYSCTRL->SCI_GLF = SYSCTRL->SCI_GLF & ~BIT(29) | BIT(28); //5V
//SYSCTRL->SCI_GLF = SYSCTRL->SCI_GLF & ~(0x03 << 28) | (0x02 << 28); //3V
//虑毛刺使能
//SYSCTRL->SCI_GLF |= BIT(31);
SYSCTRL->SCI_GLF &= ~0xFFFFF;
SYSCTRL->SCI_GLF |= SYSCTRL->PCLK_1MS_VAL>>2;
//SYSCTRL->CARD_RSVD &= ~BIT(1);
//SYSCTRL->CARD_RSVD |= BIT(1); //High TH(400mA protect)
//PA6-CARD_DECTECT PA8-CLK PA9-RSTN PA10-IO PA14-VCC_EN
//GPIO_GROUP[0].PUE |= BIT(10) | BIT(9);
//GPIO_GROUP[0].PUE &= ~BIT(10);
#endif
}
s32 dev_icc_open(s32 nslot)
{
s32 ret = DEVSTATUS_ERR_PARAM_ERR;
if(nslot >= ICC_SLOT_MAX) //3
{
return DEVSTATUS_ERR_PARAM_ERR;
}
if(g_icc_fd[nslot]<0)
{
switch(nslot)
{
case ICC_SLOT_ICCARD: //0
ret = drv_icc_open();
break;
case ICC_SLOT_PSAM1:
case ICC_SLOT_PSAM2:
if(g_psam_existflg)
{
ret = drv_psam_open(nslot-1);
}
break;
}
if(ret==0)
{
g_icc_fd[nslot] = 0;
}
}
else
{
return 0;
}
return ret;
}
typedef enum _ICC_SLOT
{
ICC_SLOT_ICCARD = 0,
ICC_SLOT_PSAM1 = 1,
ICC_SLOT_PSAM2 = 2,
ICC_SLOT_MAX = 3,
}icc_slot_t;
4. 关闭设备
drv_icc_close()函数的实现如下所示:
s32 drv_icc_close(void)
{
iso7816_close(ICC_DEV_NSLOT); //库函数关闭,0
dev_gpio_direction_output(ICC_CMD_PIN, 0); //SCI0_VCCEN拉低
/*SCI0 VCCEN信号有效电平选择*/
SYSCTRL->PHER_CTRL |= (BIT(20)); //低有效,待验证能不能删除掉
g_icc_active = 0; //上电成功的标志位
return 0;
}
s32 dev_icc_close(s32 nslot)
{
s32 ret = DEVSTATUS_ERR_PARAM_ERR;
if(nslot >= ICC_SLOT_MAX) //3
{
return DEVSTATUS_ERR_PARAM_ERR;
}
if(g_icc_fd[nslot]==0)
{
switch(nslot)
{
case ICC_SLOT_ICCARD:
ret = drv_icc_close();
break;
case ICC_SLOT_PSAM1:
case ICC_SLOT_PSAM2:
if(g_psam_existflg)
{
ret = drv_psam_close(nslot-1);
}
break;
}
g_icc_fd[nslot] = -1;
}
return ret;
}
5. 检测有没有IC卡插入
drv_icc_getstatus()函数的实现如下所示:
s32 drv_icc_getstatus(void)
{
/*检测有没有IC卡插入*/
if(0 != iso7816_detect(ICC_DEV_NSLOT)) //没有卡插入,0
{
if(g_icc_active==1)
{
g_icc_active = 0;
drv_icc_poweroff();
}
return 0;
}
else //有卡插入
{
return 1;
}
}
s32 dev_icc_getstatus(s32 nslot)
{
if(ICC_SLOT_ICCARD == nslot) //0
{
return drv_icc_getstatus();
}
else
{
return 1;
}
}
6. IC卡复位
drv_icc_reset()函数的实现如下所示:
s32 drv_icc_reset(u8 *lpAtr)
{
s32 ret;
u8 atr[65];
/*检测有没有IC卡插入*/
if(0 == drv_icc_getstatus()) //没有卡插入
{
ICC_DEBUG("NO CARD!\r\n");
return DEVSTATUS_ERR_FAIL;
}
/*对IC卡复位*/
ret = iso7816_init(ICC_DEV_NSLOT, VCC_3000mV | SPD_1X, atr); //VCC_5000mV
if(ret == 0) //复位成功
{
g_icc_active = 1; //上电成功标志位
memcpy(lpAtr, &atr[1], atr[0]); //拷贝IC卡复位的数据
return atr[0];
}
else //复位失败
{
ICC_DEBUG("reset fail!\r\n");
return DEVSTATUS_ERR_FAIL;
}
}
s32 dev_icc_reset(s32 nslot, u8 *lpAtr)
{
s32 ret = DEVSTATUS_ERR_PARAM_ERR;
if(nslot >= ICC_SLOT_MAX) //3
{
ICC_DEBUG("PARAM Err!(nslot=%d)\r\n", nslot);
return DEVSTATUS_ERR_PARAM_ERR;
}
if(g_icc_fd[nslot]<0)
{
ICC_DEBUG("DEVICE_NOTOPEN!\r\n");
return DEVSTATUS_ERR_DEVICE_NOTOPEN;
}
switch(nslot)
{
case ICC_SLOT_ICCARD:
ret = drv_icc_reset(lpAtr);
break;
case ICC_SLOT_PSAM1:
case ICC_SLOT_PSAM2:
if(g_psam_existflg)
{
ret = drv_psam_reset(nslot-1, lpAtr);
}
break;
}
return ret;
}
7. APDU交互
drv_icc_exchange_apdu()函数实现如下所示:
s32 drv_icc_exchange_apdu(u8 *wbuf, u32 wlen, u8 *rbuf, u32 *rlen, u32 rxmax)
{
ST_APDU_RSP rsp;
ST_APDU_REQ apdu_req;
s32 ret;
*rlen = 0;
/*检测有没有IC卡插入*/
if(0 == drv_icc_getstatus()) //没有卡插入
{
ICC_DEBUG("NO CARD!\r\n");
return DEVSTATUS_ERR_FAIL;
}
/*判断IC卡是否复位*/
if(g_icc_active == 0) //没有复位
{
ICC_DEBUG("NOT RESET!\r\n");
return DEVSTATUS_ERR_FAIL;
}
/*判断数据长度*/
if((wlen > (255+6)) || (wlen < 4)) //卡片没有复位
{
ICC_DEBUG("wlen=%d\r\n", wlen);
return DEVSTATUS_ERR_PARAM_ERR;
}
if(rxmax < 2) //期望的长度错
{
ICC_DEBUG("rxmax=%d\r\n", rxmax);
return DEVSTATUS_ERR_PARAM_ERR;
}
memcpy(apdu_req.cmd, wbuf, 4);
if(4 == wlen) //case 1
{
apdu_req.lc = 0;
apdu_req.le = 0;
}
else if(5 == wlen) //case 2
{
apdu_req.lc = 0;
apdu_req.le = wbuf[4];
if(apdu_req.le == 0)
{
apdu_req.le = 256;
}
}
else if(wlen > 5)
{
apdu_req.lc = wbuf[4];
memcpy(apdu_req.data_in, &wbuf[5], apdu_req.lc);
if(wlen == (apdu_req.lc+5)) //case3
{
apdu_req.le = 0;
}
else //case4
{
apdu_req.le = wbuf[apdu_req.lc+5];
if(apdu_req.le == 0)
{
apdu_req.le = 256;
}
}
}
/*使用库函数接口执行APDU交互*/
ret = iso7816_exchange(ICC_DEV_NSLOT, AUTO_GET_RSP, &apdu_req, &rsp);
if(ret != 0) //出错
{
ICC_DEBUG("exchange err!(%d)\r\n", ret);
ret = DEVSTATUS_ERR_PARAM_ERR;
}
else
{
ret = DEVSTATUS_SUCCESS;
if((rsp.len_out+2) > rxmax) //出错
{
ICC_DEBUG("len_out over!(len_out=%d, rxmax=%d)\r\n", rsp.len_out, rxmax);
ret = DEVSTATUS_ERR_PARAM_ERR;
}
else
{
memcpy(rbuf, rsp.data_out, rsp.len_out);
rbuf[rsp.len_out++] = rsp.swa;
rbuf[rsp.len_out++] = rsp.swb;
*rlen = rsp.len_out;
ret = DEVSTATUS_SUCCESS;
}
}
return ret;
}
s32 dev_icc_exchange_apdu(s32 nslot, const u8* lpCApdu, u32 nCApduLen, u8*lpRApdu, u32* lpRApduLen, u32 nRApduSize)
{
s32 ret = DEVSTATUS_ERR_PARAM_ERR;
if(nslot >= ICC_SLOT_MAX) //3
{
ICC_DEBUG("PARAM Err!(nslot=%d)\r\n", nslot);
return DEVSTATUS_ERR_PARAM_ERR;
}
if(g_icc_fd[nslot] < 0)
{
ICC_DEBUG("DEVICE_NOTOPEN!\r\n");
return DEVSTATUS_ERR_DEVICE_NOTOPEN;
}
switch(nslot)
{
case ICC_SLOT_ICCARD: //0
ret = drv_icc_exchange_apdu((u8*)lpCApdu, nCApduLen, lpRApdu, lpRApduLen, nRApduSize);
break;
case ICC_SLOT_PSAM1:
case ICC_SLOT_PSAM2:
if(g_psam_existflg)
{
ret = drv_psam_exchange_apdu(nslot-1, (u8*)lpCApdu, nCApduLen, lpRApdu, lpRApduLen, nRApduSize);
}
break;
}
return ret;
}