Chinaunix首页 | 论坛 | 博客
  • 博客访问: 18573
  • 博文数量: 8
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 20
  • 用 户 组: 普通用户
  • 注册时间: 2012-07-06 11:35
文章分类
文章存档

2014年(3)

2013年(3)

2012年(2)

我的朋友

分类: 嵌入式

2013-10-29 10:50:21

/*
 * 摘要: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
阅读(672) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~