在IC卡应用这块折腾了很久。
之前先在淘宝上买了一个明华的URF-R330,然后折腾了2周多的libsub+pcsc+ccid,后来才发现R330不支持CCID。
上周五在淘宝上重新买了一个ACS-ACR122U,今天早上收到,连上am335x,运行测试程序成功。
前一篇日记已经讲述了libusb的编译和应用,这里不再重复。
pcsc和ccid是由微软公司牵头建立的一个非接触式IC卡的标准:PC/SC,它定义了非接触IC卡读卡器与计算机的连接,数据交换包定义,以及API。
首先在ubuntu下执行sudo apt-get install flex,安装flex服务,这个在编译pcsc时需要用到。然后下载以下的源代码:
A、到http://pcsclite.alioth.debian.org/pcsclite.html,下载pcsclite版本;
B、到http://pcsclite.alioth.debian.org/ccid.html,下载ccid;
C、到adobe的flex官网下载使用的flex版本的源程序。
配置flex并交叉编译:
#!/bin/sh
./configure --host=arm-linux-gnueabihf \
--prefix=/opt/flex/ \
CC="arm-linux-gnueabihf-gcc -ldl -fPIC" \
CXX="arm-linux-gnueabihf-g++ -ldl -fPIC"
make得到libfl.a,执行以下的过程得到libfl.so: 将libfl.a拷贝到一个空目录中,然后执行下面的命令:
arm-linux-gnueabihf-ar -x libfl.a
arm-linux-gnueabihf-gcc -shared *.o -o libfl.so
然后将这个so拷贝到nfs文件的opt/flex/lib目录下并修改文件名为libfl.so.2,并ln建立链接:libfl.so
配置pcsc并交叉编译:
#!/bin/sh
./configure --host=arm-linux-gnueabihf \
--prefix=/opt/ccid/ \
CC="arm-linux-gnueabihf-gcc -fPIC -ldl" \
CXX="arm-linux-gnueabihf-g++ -fPIC -ldl" \
--enable-libusb \
--disable-libudev \
LIBUSB_LIBS="-L/home/XXX/emdlinux/nfs/opt/libusb/lib/ -lusb-1.0" \
LIBUSB_CFLAGS="-I/home/XXX/emdlinux/nfs/opt/libusb/include/libusb-1.0/" \
--disable-static
执行这个配置sh文件后,修改/pcsc/config.h文件,将这个文件里的所有关于目录的定义,全部修改为正确的am335x目标文件系统的目录。
make并make install. 然后将计算机的/opt/ccid目录拷贝到am335x目标文件系统的/opt/ccid目录。
配置ccid并交叉编译:
#!/bin/sh
./configure --host=arm-linux-gnueabihf \
--prefix=/opt/ccid/ \
CC="arm-linux-gnueabihf-gcc -ldl -fPIC" \
CXX="arm-linux-gnueabihf-g++ -ldl -fPIC" \
--enable-libusb \
LIBUSB_LIBS="-L/home/XXX/emdlinux/nfs/opt/libusb/lib/ -lusb-1.0" \
LIBUSB_CFLAGS="-I/home/XXX/emdlinux/nfs/opt/libusb/include/libusb-1.0/" \
PCSC_CFLAGS="-I/home/XXX/emdlinux/nfs/opt/ccid/include/PCSC/" \
PCSC_LIBS="-L/home/XXX/emdlinux/nfs/opt/ccid/lib/ -lpcsclite" \
--enable-usbdropdir="/opt/ccid/pcsc/drivers" \
--disable-static
同样修改/ccid/config.h文件中关于目录的定义,然后编译并安装ccid。将计算机的/opt/ccid拷贝到am335x目标文件系统的/opt/ccid目录。
修改目标文件系统中的/etc/profile文件,在LD_LIBRARY_PATH环境变量中添加库的目录,并且启动pcscd服务程序:
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$T_ROOT/lib:$QT_ROOT/lib:/opt/ccid/lib:/opt/ccid/pcsc/drivers/ifd-ccid.bundle/Contents/Linux:
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/flex/lib:/opt/libusb/lib:/opt/libiconv/lib:
#Start the PC/SC service
/opt/ccid/sbin/pcscd &
保存profile文件后重新启动。
现在,pcsc和ccid已经可以工作,编写一个测试程序,检查是否能正确操作读卡器读卡:
#include "winscard.h"
#define SCARD_ATTR_VALUE(Class, Tag) ((((ULONG)(Class)) << 16) | ((ULONG)(Tag)))
#define SCARD_CLASS_ICC_STATE 9 /**< ICC State specific definitions */
#define SCARD_ATTR_ATR_STRING SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0303) /**< Answer to reset (ATR) string. */
//卡密码
#define Psw1 0xff
#define Psw2 0xff
#define Psw3 0xff
#define Psw4 0xff
#define Psw5 0xff
#define Psw6 0xff
#define BLOCKNUM 0x04
SCARDCONTEXT hContext;
LONG rtn;
SCARDHANDLE hCard;
DWORD dwActiveProtocol;
char mszReaders[1024];
DWORD dwReaders = sizeof(mszReaders);
unsigned char pbAtr[265]; /* 264 is OK */
unsigned char ucCmd[265];
DWORD dwAtrLen,i;
unsigned char ucCh;
获得pcsc的context:
rtn=SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
if(rtn!=SCARD_S_SUCCESS){
printf("SCardEstablishContext failed: %s\n", pcsc_stringify_error(rtn));
return -1;
}
获得读卡器的名称,读卡器的名称在mszReaders中返回,如果有多个读卡器,每一个读卡器的名称都保存在mszReader中,以‘\0’隔开。
rtn=SCardListReaders(hContext, NULL, mszReaders, &dwReaders);
if(rtn!=SCARD_S_SUCCESS){
printf("SCardListReaders failed: %s\n", pcsc_stringify_error(rtn));
SCardReleaseContext(hContext);
return -1;
} else {
printf("Scard list is %s.\n",mszReaders);
}
在一个while循环中不断调用相应的函数来获取卡的信息,如果读卡器正确读到卡,这自动连接,并加载授权码获得授权,然后读取指定扇区的数据:
rtn=SCardConnect(hContext, mszReaders, SCARD_SHARE_EXCLUSIVE,
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard,
&dwActiveProtocol);
if(rtn!=SCARD_S_SUCCESS){
printf("SCardConnect failed: %s\n", pcsc_stringify_error(rtn));
} else {
//已经正确连接到卡,读取属性
rtn=SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, NULL, &dwAtrLen);
printf("Attrib len is %d.\n",dwAtrLen);
//缓冲区的长度不能大于或者小于要求,必须用上面的函数先获取缓冲区长度
//dwAtrLen = sizeof(pbAtr);
rtn=SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, pbAtr, &dwAtrLen);
if(rtn!=SCARD_S_SUCCESS){
printf("SCardGetAttrib failed: %s\n", pcsc_stringify_error(rtn));
} else {
for(i=0;i<dwAtrLen;i++){
printf("0x%x\t",pbAtr[i]);
if(i%5==0){
printf("\n");
}
}
printf("\n");
}
//加载授权码到读卡器
ucCmd[0]=0xff;
ucCmd[1]=0x82;
ucCmd[2]=0x00;
ucCmd[3]=0x00;
ucCmd[4]=0x06;
ucCmd[5]=Psw1;
ucCmd[6]=Psw2;
ucCmd[7]=Psw3;
ucCmd[8]=Psw4;
ucCmd[9]=Psw5;
ucCmd[10]=Psw6;
i=11;
dwAtrLen=2;
rtn=SCardTransmit(hCard,SCARD_PCI_T1,ucCmd,i,NULL,pbAtr,&dwAtrLen);
if(rtn!=SCARD_S_SUCCESS){
printf("SCardTransmit failed: %s\n", pcsc_stringify_error(rtn));
} else {
if((pbAtr[0]==0x90)&&(pbAtr[1]==0x00)){
printf("Load authentication keys success.\n");
} else if((pbAtr[0]==0x63)&&(pbAtr[1]==0x00)){
printf("Load authentication keys fault.\n");
} else {
printf("Unknow return code of the Load authentication keys.\n");
}
}
//获得对BLOCKNUM扇区的读写授权
ucCmd[0]=0xff;
ucCmd[1]=0x88;
ucCmd[2]=0x00;
ucCmd[3]=BLOCKNUM; //block number
ucCmd[4]=0x60;
ucCmd[5]=0x00; //key location
i=6;
dwAtrLen=2;
rtn=SCardTransmit(hCard,SCARD_PCI_T1,ucCmd,i,NULL,pbAtr,&dwAtrLen);
if(rtn!=SCARD_S_SUCCESS){
printf("SCardTransmit failed: %s\n", pcsc_stringify_error(rtn));
} else {
if((pbAtr[0]==0x90)&&(pbAtr[1]==0x00)){
printf("Authentication success.\n");
} else if((pbAtr[0]==0x63)&&(pbAtr[1]==0x00)){
printf("Authentication fault.\n");
} else {
printf("Unknow return code of the authentication.\n");
}
}
//读取BLOCKNUM指定的扇区的数据
ucCmd[0]=0xff;
ucCmd[1]=0xb0;
ucCmd[2]=0x00;
ucCmd[3]=BLOCKNUM; //block number
ucCmd[4]=16; //16 bytes
i=5;
dwAtrLen=18; //16 bytes + 2 bytes rtn code
rtn=SCardTransmit(hCard,SCARD_PCI_T1,ucCmd,i,NULL,pbAtr,&dwAtrLen);
if(rtn!=SCARD_S_SUCCESS){
printf("SCardTransmit failed: %s\n", pcsc_stringify_error(rtn));
} else {
if((pbAtr[16]==0x90)&&(pbAtr[17]==0x00)){
printf("Read block success.\n");
printf("Block data:");
for(i=0;i<16;i++){
printf("%2x,",pbAtr[i]);
}
printf("\n");
} else if((pbAtr[16]==0x63)&&(pbAtr[17]==0x00)){
printf("Read block fault.\n");
} else {
printf("Unknow return code of the block read.\n");
}
}
//读卡结束,断开与卡的连接
rtn=SCardDisconnect(hCard,SCARD_LEAVE_CARD);
if(rtn!=SCARD_S_SUCCESS){
printf("SCardDisconnect failed: %s\n", pcsc_stringify_error(rtn));
}
最后,结束程序是调用SCardReleaseContext(hContext)释放context。