分类: 嵌入式
2013-10-29 10:50:21
原文地址:PBOC2.0 Level 1 测试程序 作者:luozhiyong131
/*
* 摘要:PBOC2.0 Level 1 测试程序
* 分为二部分:电气测试、协议测试
* Created on: 2013-1-10
* Author: lzy
*/
#include "Common.h"
#include "AU9540.h"
#include "lib.h"
#define TIME_Level1 7 /* 电气循环测试时间间隔*/
#define TIME 1 /* 协议循环测试时间间隔*/
#define APDU_DEBUG 1
static FILE *g_LogoFd;
#define LOGO_PRINT(fmt,args...) PR_DEBUG(fmt,##args) /*fprintf(g_LogoFd, fmt,##args)*/
/**
* 功 能:打开AU9540模块,和LCD
* 入口参数:
* 出口参数:
* 返回值:0 成功
* 开发人员:Lzy 2013-1-12
* 修改记录:
*/
LONG DevOpen(void)
{
LONG ret;
gpio_dev_open();
hapi_set_dir(8, 5, 0);
hapi_set_dir(4, 0, 1);
hapi_gpio_write(4, 0, 1);
ret = fb_init(); /* 打开LCD */
EXAM_ASSERT(ret, "LCD Open fail ErrNO=%d\n", ret);
ret = OpenAU9540();
EXAM_ASSERT(ret, "Open Init AU9540 ErrNO=%d\n", ret);
F1850_init();
LcdBlackCol(1);
#if 0
g_LogoFd = fopen("logo.txt", "w+");
time_t timep;
time(&timep);
LOGO_PRINT("\n%s\n",ctime(&timep))
#endif
return ret;
}
/**
* 功 能:电气测试菜单
* 入口参数:i 复位次数
* 出口参数:
* 返回值:
* 开发人员:Lzy 2013-1-12
* 修改记录
*/
LONG MenuElectTest(void)
{
LONG ret = 0;
ST_Cls();
ST_Display(1, "终端Level_1电特性测试");
ST_Display(2, " 1 单次触发模式");
ST_Display(3, " 2 循环触发模式");
ST_Display(4, " 3 返回");
return ret;
}
LONG MenuMain(void)
{
ST_Cls();
ST_Display(1, " 终端 Level 1 测试");
ST_Display(2, " 1 电特性测试");
ST_Display(3, " 2 协议测试");
ST_Display(4, " 3 与PC机连接");
}
/**************************电特性测试******************************/
/**
* 函数功能:发送命令至IC卡
* 入口参数:
* unsigned char *pszCmd --> 命令
* unsigned char ucLgcmd --> 命令长度
* 出口参数:
* unsigned char *szRep --> 返回报文
* unsigned char ucLgRep --> 响应数据长度
* 返 回:0-->成功 或返回错误码
*
* 开发人员:
* 修改记录:Lzy 2012-11-27
*/
int SendCmd(unsigned char *pszCmd, unsigned int ucLgcmd, unsigned char *szRep, unsigned int *ucLgRep)
{
int i, iOffset = 0, ret;
unsigned int rtn;
#if APDU_DEBUG
/* 打印C-APDU 与log信息保存 */
LOGO_PRINT("\nC_APDU ==>[len:%d] ", ucLgcmd);
for (i = 0; i < ucLgcmd; i++)
LOGO_PRINT( "%02X ", pszCmd[i]);
#endif
ret = SendAPDUCommand(pszCmd, ucLgcmd, szRep, &rtn); /* 命令发送与接收 */
if (ret == 0)
{
#if APDU_DEBUG /* 打印R-APDU */
*ucLgRep = rtn;
if (*szRep != 0x61 && *szRep != 0x6c)
{
LOGO_PRINT( "\nR_APDU <==[len:%d] ", *ucLgRep);
for (i = 0; i < *ucLgRep; i++)
LOGO_PRINT("%02X ", szRep[i]);
LOGO_PRINT( "\n");
}
#endif
}
return ret;
}
/**
* 功 能: 文件选择命令
* 输入参数: char *file_id : 文件名称
* int filelen :文件长度
* 输出参数:unsigned char *recvbuf : 接收字符串
* 返 回: 0--成功; <0: 失败
* 开发人员: yangxh
* 修改记录: Lzy 2012-11-27
*/
int IC_CMD_Select(unsigned char *file_id, int filelen, unsigned char *recvbuf)
{
unsigned int recvlen = 0;
unsigned char SendCommand[128], *ptr;
unsigned int sendlen;
int ret, i;
/*
* 00 A4 发送选择命令
* P1=0x04表示选择DF,这里数据域为DF的文件名
* P2=0x00表示选择满足条件的第一个或者是仅有的一个文件;
*/
memset((char *) SendCommand, 0x00, sizeof(SendCommand));
sprintf((char *) SendCommand, "%c%c%c%c%c", 0x00, 0xA4, 0x04, 0x00, filelen);
memcpy(SendCommand + 5, file_id, filelen);
sendlen = filelen + 5;
SendCommand[sendlen] = 0;
sendlen++;
ret = SendCmd(SendCommand, sendlen, recvbuf, &recvlen);
if (ret != 0)
{
ST_Display(4, " R-APDU[错误]");
PR_ERR("PbocICSelect fail");
// return -1;
}
else
{
ST_Display(3, " C-APDU[发送]");
ST_Display(4, " R-APDU[正确]");
}
return 0;
}
/**
* 功 能: 电气测试 命令发送流程
* 输入参数: 无
* 输出参数:
* 返 回: 0--成功; <0: 失败
* 开发人员: yangxh
* 修改记录: Lzy 2012-11-27
*/
int Pb_SelectPSE(void)
{
char chPSE[0x80];
unsigned char recvbuf[256];
INT sfi, ret, i, j;
memset(recvbuf, '\0', 256);
memset(chPSE, '\0', 50);
memcpy(chPSE, "1PAY.SYS.DDF01", 14);
sfi = IC_CMD_Select(chPSE, strlen(chPSE), recvbuf);
if (sfi < 0)
{
PR_ERR("PSE 出错\n ");
return -1;
}
memset(recvbuf, 0x00, sizeof(recvbuf));
bzero(chPSE, sizeof(chPSE));
for (j = 1; j < 64; j++)
{
ST_Disp(1, 16, "%d", j);
for (i = 0; i < 0x80; i++)
chPSE[i] = i;
IC_CMD_Select(chPSE, 0x80, recvbuf);
}
return ret;
}
/**
* 功 能:电气特性测试
* 入口参娄:
* 出口参数:
* 返回值:
* 开发人员:
* 修改记录:
*/
LONG ElectricalTest(void)
{
LONG ret;
INT iKey, i = 1, rtn = 0, flag;
UCHAR cRecvbuf[256], Buf[50] = "ATR:";
while (1)
{
MenuElectTest();
iKey = ST_GetkeyMs(0) - '0'; /*库文件返回的 'BCD'*/
if (iKey == 3)
break;
else if (iKey == 1)
flag = 0;
else if (iKey == 2)
flag = 1;
else
continue;
ST_Cls();
do
{
if (0 == flag)
ST_Display(1, " 单次触发模式");
else
ST_Display(1, " 循环触发模式%d", flag++);
bzero(cRecvbuf, sizeof(cRecvbuf));
ret = MSG_PC_to_RDR_IccPowerOn(cRecvbuf, &rtn); /* 冷复位 */
if (ret)
{
bzero(cRecvbuf, sizeof(cRecvbuf));
ret = MSG_PC_to_RDR_IccPowerOff(cRecvbuf, &rtn);
PR_ERR( "卡已下电");
}
else
{
/* 打印至屏幕*/
ST_Display(2, " ");
for (i = 0; i < rtn && i < 7; i++) /* 由于屏幕的关第不能全部显示 */
sprintf(&Buf[4 + i * 3], "%02X ", cRecvbuf[i + 10]);
ST_Displays(2, 0, Buf);
PR_DEBUG("\nATR=[len:%d] ", rtn-11);
/* 这里数据长度减去11才是ATR长度*/
for (i = 10; i < rtn - 1; i++)
PR_DEBUG("%02X ", cRecvbuf[i]);
PR_DEBUG("\n");
Pb_SelectPSE();
ret = MSG_PC_to_RDR_IccPowerOff();
EXAM_ASSERT(ret, "卡下电失败");
}
S_Delay(TIME_Level1);
} while (flag);
}
return ret;
}
/***************************************************************/
/*********************************协议测试***********************/
typedef struct
{
unsigned char data[261]; // ‘CLA INS P1 P2 [Lc + Lc data] [Le]’ for command or
unsigned int length; // ‘[Le data] SW1 SW2’ for response ( max = 256+2 )
} T_APDU;
#define TypeAPDU R_Apdu.data[3]
#define LC R_Apdu.data[4]
#define LE R_Apdu.data[5]
#define OK 0
// Proprietary card activation and APDU Command/Response functions
char PBOC_PowerOn(void)
{
LONG ret;
INT iKey, i = 1, rtn = 0, flag;
UCHAR cRecvbuf[256];
bzero(cRecvbuf, sizeof(cRecvbuf));
ret = MSG_PC_to_RDR_IccPowerOn(cRecvbuf, &rtn); /* 冷复位 */
if (ret) /*上电未成功,需要发送下电命令*/
{
bzero(cRecvbuf, sizeof(cRecvbuf));
ret = MSG_PC_to_RDR_IccPowerOff(); /*下电*/
PR_DEBUG("已下电\n");
return -1;
}
/* 这里数据长度减去11才是ATR长度*/
if ((rtn - 11) == cRecvbuf[1] && 0x80 == cRecvbuf[0])
{
PR_DEBUG("\nATR=[len:%d] ", rtn-11);
for (i = 10; i < rtn - 1; i++)
PR_DEBUG("%02X ", cRecvbuf[i]);
PR_DEBUG("\n");
}
else
{
PR_ERR("Read ATR fail\n");
return -1;
}
return ret;
}
char PBOC_apdu(T_APDU *C_Apdu, T_APDU *R_Apdu)
{
char ret;
ret = SendCmd(C_Apdu->data, C_Apdu->length, R_Apdu->data, &(R_Apdu->length));
if (ret)
PR_ERR("SendCmd error!");
return ret;
}
const unsigned char selectVisaCredit[] = { 0x00, 0xA4, 0x04, 0x00, 7, 0xA0, 0, 0, 0, 3, 0x10, 0x10, 0 };
T_APDU C_Apdu, R_Apdu; // C_Apdu sent to the Card, R_Apdu received from the Card
char ComputeLrc(int ln, unsigned char *p)
{
char lrc;
int i;
lrc = 0;
i = 0;
while (i < ln)
lrc ^= p[i++];
return (lrc);
}
static void InitSelectVisa(T_APDU *apdu)
{
memcpy(apdu->data, selectVisaCredit, 13);
apdu->length = 13;
}
void MenuMainTestVisa(void)
{
ST_Cls();
ST_Display(1, "终端Level_1协议测试");
ST_Display(2, " 1 单次触发模式");
ST_Display(3, " 2 循环触发模式");
ST_Display(4, " 3 返回");
}
void MenuTestVisa(void)
{
ST_Cls();
ST_Display(1, "终端Level_1协议测试");
ST_Display(3, " 正在进行协议测试");
}
/* Main function */
int TestVisa(void)
{
unsigned char RetCode, profil;
unsigned short DataInSize;
unsigned char *ptr, Historical[15];
RetCode = PBOC_PowerOn();
if (RetCode == OK)
{
InitSelectVisa(&C_Apdu); // First APDU Command = Select Visa Credit
while (1)
{
RetCode = PBOC_apdu(&C_Apdu, &R_Apdu); // Proprietary Command/Response function
if ((RetCode != OK) || !memcmp(&R_Apdu.data[R_Apdu.length - 2], "\x6A" "\x82", 2))
break;
// Building new APDU command
if (R_Apdu.length < 8) // Not enough data to create new command
InitSelectVisa(&C_Apdu); // -> default APDU Command = Select PSE
else
{ // R-APDU : R_Apdu.data -> CLA/INS/P1/Type APDU/Lc/Le/[data]/Me1/Me2
memcpy(C_Apdu.data, R_Apdu.data, 3); // CLA/INS/P1
C_Apdu.length = 4;
ptr = C_Apdu.data + 4;
if ((TypeAPDU> 2) && LC) // Case 3 or 4 : Lc field is present
{
DataInSize = LC;
C_Apdu.length += (1+DataInSize);
*ptr++ = DataInSize;
profil = 0;
while(DataInSize--)/* Building data string*/
*ptr++ = profil++; // -> Lc data = 0,1,2...,Lc-1
}
if ((TypeAPDU== 2) || (TypeAPDU == 4) ) // Le is present
{
C_Apdu.length++;
*ptr++ = LE;
}
}
C_Apdu.data[3] = ComputeLrc(R_Apdu.length, R_Apdu.data); // P2
}
MSG_PC_to_RDR_IccPowerOff();
}
S_Delay(TIME);
return (RetCode);
}
/**
* 功 能:协议测试入口
*/
void AgreementTest(void)
{
INT iKey = 0;
while (1)
{
MenuMainTestVisa();
iKey = ST_GetkeyMs(0) - '0'; /*库文件返回的 'BCD'*/
if (iKey == 3)
break;
MenuTestVisa();
if (iKey == 1)
TestVisa();
else if (iKey == 2)
{
while (1)
TestVisa();
}
}
}
void ConectPC(void)
{
ST_Cls();
hapi_gpio_write(4, 0, 0);
ST_Display(2, " 已连接PC机");
while (1)
;
}
int main(void)
{
INT iKey = 0;
DevOpen();
ST_Cls();
while (InitAU9540()) /* 检查卡状态*/
ST_Display(2, " 亲,没插卡");
while (1)
{
MenuMain();
iKey = ST_GetkeyMs(0) - '0'; /*库文件返回的 'BCD'*/
switch (iKey)
{
case 1:
CMD_SET_PCSC_EMV_SPEED(3); // 2 默认 3 低速
ElectricalTest();
break;
case 2:
CMD_SET_PCSC_EMV_SPEED(2);
AgreementTest();
break;
case 3:
ConectPC();
break;
default:
break;
}
}
return 0;
}
项目源码:
loopback.rar