jerrywu75的笔记 https://passport2.21ic.com/?62511 [收藏] [复制] [RSS]

日志

嵌入式linux应用开发-非接触式IC卡应用

已有 1087 次阅读2015-9-15 16:15 |个人分类:嵌入式开发|系统分类:嵌入式系统| 接触式IC卡, linux, 嵌入式, 开发

   在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:
        export LD_LIBRARY_PATH
        #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。

    


路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)