Linux下对PC/SC智能卡接口编程

PC/SC(Personal Computer/Smart Card)规范,PC/SC 规范作为读卡器和卡与计算机之间有一个标准接口,实现不同生产商的卡和读卡器之间的交互操作

Linux下运行的源码pcsc-lite 在http://pcsclite.alioth.debian.org/
其中有demo例程

1.建立资源管理器上下文


LONG SCardEstablishContext(DWORD dwScope, LPCVOID pvReserved1,LPCVOID pvReserved2, LPSCARDCONTEXT phContext);

说明:这必须是在PC / SC应用程序中调用的第一个 SCard函数,所有的应用进程都必须建立自己的上下文环境
参数:
    dwScope:        输入类型:表示上下文建立的范围,建立本地连接或远程连接,目前支持 SCARD_SCOPE_SYSTEM(在系统域中完成设备数据库操作)
    pvReserved1:    输入类型:保留,为NULL
    pvReserved2:    输入类型:保留,为NULL
    phContext:      输出类型:返回创建的资源管理器上下文
返回:
    成功返回 SCARD_S_SUCCESS
    失败返回其他值,定义在 pcsclite.h

//@code
SCARDCONTEXT hContext;
LONG rv;

rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &posContext);
if(rv != SCARD_S_SUCCESS)
{
    LOGE("SCardEstablishContext::failed!\n");
    ret = ERROR_APP_CONTEXT;
    return ret;
}

2.获取系统可用读卡器列表

LONG SCardListReaders(SCARDCONTEXT hContext, /*@unused@*/ LPCSTR mszGroups,LPSTR mszReaders, LPDWORD pcchReaders);
说明:
    创建上下文后,就可在上下文中获取系统安装的可用的读卡器列表

参数:
    hContext:       输入类型;ScardEstablishContext()建立的资源管理器上下文,不能为NULL
    mszGroups:      输入类型;读卡器组名,为 NULL 时,表示列出所有读卡器。
    mszReaders:     输出类型;系统中安装的读卡器的名字,各个名字之间用'\0'分隔,最后一个名字后面为两个连续的'\0'
    pcchReaders:    输入输出类型;mszReaders 的长度

    这个函数在使用时有多种用法,主要是为了给读卡器列表创建空间的方法不同
    1).当mszReaders为 NULL 时,调用一次SCardListReaders会给pcchReaders输出mszReaders所需要的空间大小,然后自己malloc申请内存空间
    2).当mszReaders为 NULL 且pcchReaders = SCARD_AUTOALLOCATE 时,SCardListReaders会自动申请内存,但是用完后必须要使用SCardFreeMemory(hContext, mszReaders);释放
    3).当mszReaders 不为 NULL 时,需用pcchReaders传入其长度,SCardListReaders会在已申请的空间内存放读卡器列表

返回:
    成功返回 SCARD_S_SUCCESS
    失败返回其他值,定义在 pcsclite.h

这里对3种创建方法做一下代码示例:

//@code1
SCARDCONTEXT hContext;
LPSTR mszReaders;
DWORD dwReaders;
LONG rv;
...
rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
rv = SCardListReaders(hContext, NULL, NULL, &dwReaders);
mszReaders = malloc(sizeof(char)*dwReaders);
rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders);
...
free(mszReaders);

//@code2
SCARDCONTEXT hContext;
LPSTR mszReaders;
DWORD dwReaders;
LONG rv;
...
rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
dwReaders = SCARD_AUTOALLOCATE;
rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders);
...
rv = SCardFreeMemory(hContext, mszReaders);

//@code3
SCARDCONTEXT hContext;
LPSTR mszReaders[512];
DWORD dwReaders;
LONG rv;
...
rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
dwReaders = sizeof(mszReaders);
rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders);
...

下边是对读卡器名字的拾取代码
char name[128][3];
ptr = mszReaders;
int count = 0;
while ((*ptr != '\0') && (dwReaders > 0) && (count < 3))
{
    strcpy(&name[0][count], ptr);
    lens = strlen(&name[0][count]);
    ptr += (lens + 1);
    dwReaders -= (lens + 1);
    count++;
    LOGD("SCardListReaders::Reader Count %d Name:%s\n", count, &name[0][count]);
}
01-17 10:56:44.727: D/smartcardjni(2393): SCardListReaders::Reader Count 1 Name:ZNG Terminal2012-1402 00 00
01-17 10:56:44.727: D/smartcardjni(2393): SCardListReaders::Reader Count 2 Name:ZNG Terminal2012-1402 01 00

3.监测卡片插入

LONG SCardGetStatusChange(SCARDCONTEXT hContext, DWORD dwTimeout,SCARD_READERSTATE *rgReaderStates, DWORD cReaders);

说明:
    阻塞监听卡插入或状态发生改变
参数:
    hContext:       输入类型;ScardEstablishContext()建立的资源管理器上下文
    dwTimeout:      输入类型;监听等待时间,0 或 INFINITE(0xFFFFFFFF)表示永久等待
    rgReaderStates: 输入输出类型;SCARD_READERSTATE 结构体
    cReaders:       输入类型;卡通道数
返回:
    成功返回 SCARD_S_SUCCESS
    失败返回其他值,定义在 pcsclite.h
typedef struct
{
    const char *szReader;   //读卡器名
    void *pvUserData;       //私有数据
    DWORD dwCurrentState;   //读卡器当前状态
    DWORD dwEventState;     //读卡器状态改变后的事件
    DWORD cbAtr;            //ATR长度
    unsigned char rgbAtr[MAX_ATR_SIZE];//ATR
}
SCARD_READERSTATE, *LPSCARD_READERSTATE;

//@code
SCARDCONTEXT hContext;
SCARD_READERSTATE rgReaderStates[2];
LONG rv;
...
rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
...
rgReaderStates[0].szReader = "Reader X";
rgReaderStates[0].dwCurrentState = SCARD_STATE_UNAWARE;

rgReaderStates[1].szReader = "\\\\?PnP?\\Notification";
rgReaderStates[1].dwCurrentState = SCARD_STATE_UNAWARE;
...
rv = SCardGetStatusChange(hContext, INFINITE, rgReaderStates, 2);
printf("reader state: 0x%04X\n", rgReaderStates[0].dwEventState);
printf("reader state: 0x%04X\n", rgReaderStates[1].dwEventState);
if(rgReaderStates[0].dwEventState & SCARD_STATE_PRESENT)//卡片插入
{

}
if(rgReaderStates[0].dwEventState & SCARD_STATE_EMPTY)//卡片移除
{

}
//@endcode

4.连接读卡器

LONG SCardConnect(SCARDCONTEXT hContext, LPCSTR szReader,DWORD dwShareMode, DWORD dwPreferredProtocols, LPSCARDHANDLE phCard,LPDWORD pdwActiveProtocol);
说明:使用该接口后,读卡器上电读取ATR

参数:
    hContext:               输入类型;ScardEstablishContext()建立的资源管理器上下文
    szReader:               输入类型:要连接的读卡器名
    dwShareMode:            输入类型:连接类型   SCARD_SHARE_SHARED      多个应用共享同一个智能卡
                                                SCARD_SHARE_EXCLUSIVE   应用独占智能卡
                                                SCARD_SHARE_DIRECT      私有类型,不允许其他应用访问
    dwPreferredProtocols:   输入类型:使用的协议类型 
                                        SCARD_PROTOCOL_T0       T=0协议
                                        SCARD_PROTOCOL_T1       T=1协议
                                        SCARD_PROTOCOL_RAW      原始协议
    phCard:                 输出类型:卡的连接句柄
    pdwActiveProtocol:      输出类型:实际使用的协议
                                        SCARD_PROTOCOL_T0   Use the T=0 protocol.
                                        SCARD_PROTOCOL_T1   Use the T=1 protocol.
返回:
    成功返回 SCARD_S_SUCCESS
    失败返回其他值,定义在 pcsclite.h

    rv = SCardConnect(hContext, "Reader X", SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);

5.获取卡片状态和ATR

LONG SCardStatus(SCARDHANDLE hCard, LPSTR mszReaderName,LPDWORD pcchReaderLen, LPDWORD pdwState,LPDWORD pdwProtocol, LPBYTE pbAtr, LPDWORD pcbAtrLen);
说明:
参数:
    hCard:          输入类型:SCardConnect()建立的卡连接句柄
    mszReaderName:  输入输出类型:连接的读卡器名
    pcchReaderLen:  输入输出类型:mszReaderName 的长度,使用方法同 SCardListReaders 中的 pcchReaders
    pdwState:       输出类型:读卡器的当前状态
                                SCARD_UNKNOWN           0x0001  /**< Unknown state */
                                SCARD_ABSENT            0x0002  /**< Card is absent */
                                SCARD_PRESENT           0x0004  /**< Card is present */
                                SCARD_SWALLOWED         0x0008  /**< Card not powered */
                                SCARD_POWERED           0x0010  /**< Card is powered */
                                SCARD_NEGOTIABLE        0x0020  /**< Ready for PTS */
                                SCARD_SPECIFIC          0x0040  /**< PTS has been set */
    pdwProtocol:    输出类型:读卡器当前协议类型
                                SCARD_PROTOCOL_T0   Use the T=0 protocol.
                                SCARD_PROTOCOL_T1   Use the T=1 protocol.
    pbAtr:          输出类型:当前卡片的ATR
    pcbAtrLen:      输入输出类型:pbAtr 的长度,使用方法同 SCardListReaders 中的 pcchReaders
返回:
    成功返回 SCARD_S_SUCCESS
    失败返回其他值,定义在 pcsclite.h
//@code
...
dwAtrLen = sizeof(pbAtr);
dwReaderLen = sizeof(pbReader);
rv = SCardStatus(posReaderList.readerList[using_reader_id].hCard, pbReader, &dwReaderLen, &dwState, &dwProt, pbAtr, &dwAtrLen);
if (rv != SCARD_S_SUCCESS)
{
    LOGE("SCardStatus %s (0x%lX)\n" , pcsc_stringify_error(rv), rv);
    ret = SCARD_STATUS_FALSE;
}
LOGD("pbReader: %s\n", pbReader);
LOGD("pbAtr: %s\n", pbAtr);

6.读卡器数据传输

LONG SCardTransmit(SCARDHANDLE hCard, const SCARD_IO_REQUEST *pioSendPci,LPCBYTE pbSendBuffer, DWORD cbSendLength, SCARD_IO_REQUEST *pioRecvPci, LPBYTE pbRecvBuffer,LPDWORD pcbRecvLength);

说明:从卡连接成功后就能进行APDU数据传输
参数:
    hCard:          输入类型:SCardConnect()建立的卡连接句柄
    pioSendPci:     输入输出类型:发送的指令协议头结构的指针,由 SCARD_IO_REQUEST 结构定义
    pbSendBuffer:   输入类型:发送给卡的APDU指令
    cbSendLength:   输入类型:APDU的长度
    pioRecvPci:     输入输出类型:接收的指令协议头结构的指针,由 SCARD_IO_REQUEST 结构定义,如果不返回可设置为NULL
    pbRecvBuffer:   输出类型:从卡返回的数据
    pcbRecvLength:  输入输出类型:pbRecvBuffer 的实际大小
返回:
    成功返回 SCARD_S_SUCCESS
    失败返回其他值,定义在 pcsclite.h


typedef struct
{
    unsigned long dwProtocol;   /**< Protocol identifier */
    unsigned long cbPciLength;  /**< Protocol Control Inf Length */
}SCARD_IO_REQUEST, *PSCARD_IO_REQUEST, *LPSCARD_IO_REQUEST;

//@code
rv = SCardConnect(hContext, "Reader X", SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0, &hCard, &dwActiveProtocol);
dwSendLength = sizeof(pbSendBuffer);
dwRecvLength = sizeof(pbRecvBuffer);
rv = SCardTransmit(hCard, SCARD_PCI_T0, pbSendBuffer, dwSendLength,&pioRecvPci, pbRecvBuffer, &dwRecvLength);

7.独占访问

这组操作一遍用于SCardTransmit() 前后,让传输操作更加安全可靠
LONG SCardBeginTransaction(SCARDHANDLE hCard);
说明:启动一个事务,阻止其它应用程序访问智能卡
参数:
    hCard:          输入类型:SCardConnect()建立的卡连接句柄


LONG SCardEndTransaction(SCARDHANDLE hCard, DWORD dwDisposition);
说明:结束一个事务,允许其它引用程序访问智能卡
参数:
    hCard:          输入类型:SCardConnect()建立的卡连接句柄
    dwDisposition   输入类型:结束时对读卡器的操作

8.重新连接读卡器

LONG SCardReconnect(SCARDHANDLE hCard, DWORD dwShareMode,DWORD dwPreferredProtocols, DWORD dwInitialization, LPDWORD pdwActiveProtocol);
说明:将正在连接的读卡器重新连接,等于热复位
参数:
    hCard:                  输入类型:SCardConnect()建立的卡连接句柄
    dwShareMode:            输入类型:连接类型   SCARD_SHARE_SHARED      多个应用共享同一个智能卡
                                        SCARD_SHARE_EXCLUSIVE   应用独占智能卡
                                        SCARD_SHARE_DIRECT      私有类型,不允许其他应用访问
    dwPreferredProtocols:   输入类型:使用的协议类型 
                                        SCARD_PROTOCOL_T0       T=0协议
                                        SCARD_PROTOCOL_T1       T=1协议
                                        SCARD_PROTOCOL_RAW      原始协议
    dwInitialization:       输入类型:读卡器指定的操作,常用作重启
                                        SCARD_LEAVE_CARD     - Do nothing.
                                        SCARD_RESET_CARD     - Reset the card (warm reset).
                                        SCARD_UNPOWER_CARD   - Power down the card (cold reset).
                                        SCARD_EJECT_CARD     - Eject the card.
    pdwActiveProtocol:      输出类型:实际使用的协议

返回:
    成功返回 SCARD_S_SUCCESS
    失败返回其他值,定义在 pcsclite.h

9.断开读卡器

LONG SCardDisconnect(SCARDHANDLE hCard, DWORD dwDisposition);
说明:断开读卡器连接
参数:
    hCard:          输入类型:SCardConnect()建立的卡连接句柄
    dwDisposition   输入类型:断开连接时对读卡器的操作
                                SCARD_LEAVE_CARD        0x0000  /**< Do nothing on close */
                                SCARD_RESET_CARD        0x0001  /**< Reset on close */
                                SCARD_UNPOWER_CARD      0x0002  /**< Power down on close */
                                SCARD_EJECT_CARD        0x0003  /**< Eject on close */
返回:
    成功返回 SCARD_S_SUCCESS
    失败返回其他值,定义在 pcsclite.h

//@code
rv = SCardDisconnect(hCard, SCARD_UNPOWER_CARD);

10.释放上下文

LONG SCardReleaseContext(SCARDCONTEXT hContext);
说明:应用程序终止前,释放资源管理器上下文
参数:
    hContext:       输入类型;ScardEstablishContext()建立的资源管理器上下文
返回:
    成功返回 SCARD_S_SUCCESS
    失败返回其他值,定义在 pcsclite.h

示例代码:

   //***********************************
   #define MAX_READER_COUNTS    3

   typedef struct _strReaderListInfo
   {
    char reader[128];
    int trans_begin;
    SCARDHANDLE hCard;
    DWORD dwActiveProtocol;
    SCARD_IO_REQUEST pioSendPci;
    SCARD_IO_REQUEST pioRecvPci;
   }StrReaderListInfo, *pStrReaderListInfo;

   typedef struct _strReaderList
   {
    int count;
    StrReaderListInfo readerList[MAX_READER_COUNTS];
   }StrReaderList, *pStrReaderList;

   unsigned int posReaderId = 1;    //readerid
   StrReaderList posReaderList = {0};
   SCARDCONTEXT posContext = 0;
   DWORD posActiveProtocol[MAX_READER_COUNTS];



   //上下文初始化
   unsigned int Pcscd_Init()
   {
    LOGD("%s",__FUNCTION__);
    unsigned int ret = ERROR_NONE;
    LONG rv = 0;
    unsigned int lens = 0;
    DWORD dwReaders;
    unsigned char reader_list_buf[512];
    pStrReaderListInfo pStrInfo;
    unsigned char *ptr = NULL;
    unsigned int tryAgain = 0;

   START:
    if(posContext != 0)
    {
        LOGE("SCardEstablishContext::Context has already been created,Release!\n");
        rv = SCardReleaseContext(hContext);
        if(rv != SCARD_S_SUCCESS)
        {
            LOGE("SCardReleaseContext::failed!\n");
        }
        hContext = 0;
    }
    //建立上下文
    memset(&posReaderList, 0, sizeof(StrReaderList));
    rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &posContext);
    if(rv != SCARD_S_SUCCESS)
    {
        LOGE("SCardEstablishContext::failed!\n");
        ret = ERROR_APP_CONTEXT;
        return ret;
    }
    LOGD("SCardEstablishContext::success!\n");

    //获取读卡器列表
    dwReaders = sizeof(reader_list_buf);
    rv = SCardListReaders(posContext, NULL, reader_list_buf, &dwReaders);               
    if(rv != SCARD_S_SUCCESS)
    {
        LOGE("SCardListReaders::native failed!(0x%x)\n",rv);
        if(posContext != 0)
        {
            SCardReleaseContext(posContext);
            posContext = 0;

        }
        if(rv == SCARD_E_NO_READERS_AVAILABLE)
        {
            ret = ERROR_APP_REBOOT_POS;
        }
        else
        {
            ret = ERROR_APP_LISTREADER;
        }
        return ret;
    }
    reader_list_buf[dwReaders] = 0x00;
    reader_list_buf[dwReaders + 1] = 0x00;

    posReaderList.count = 0;
    ptr = reader_list_buf;
    while ((*ptr != '\0') && (dwReaders > 0))
    {
        pStrInfo = &posReaderList.readerList[posReaderList.count];
        strcpy(pStrInfo->reader, ptr);
        lens = strlen(pStrInfo->reader);
        ptr += (lens + 1);
        dwReaders -= (lens + 1);
   //       pStrInfo->hCard = 0;
        posReaderList.count++;
        LOGD("SCardListReaders::Reader Count %d Name:%s\n", posReaderList.count, pStrInfo->reader);
        if(posReaderList.count >= MAX_READER_COUNTS)
        {
            break;
        }
    }
    if (posReaderList.count < 2)
    {
        LOGE("[%d]::reader count = %d!!!\n",__LINE__,posReaderList.count);
        SCardStopDeamon(posContext);
        sleep(3);
        if(posContext != 0)
        {
            SCardReleaseContext(posContext);
            posContext = 0;
        }
        if(tryAgain == 0)
        {
            ret = ERROR_NONE;
            tryAgain = 1;
            LOGE("init try again");
            goto START;
        }
        else
        {
            ret = ERROR_APP_LISTREADER;
            return ret;
        }
    }
    LOGD("SCardListReaders::success!\r\n");


    ret = ERROR_NONE;
    return ret;
   }

   //释放上下文
   unsigned int Pcscd_Release()
   {
    LOGD("%s",__FUNCTION__);
    unsigned int ret = ERROR_NONE;
    int rv;
    pStrReaderListInfo pStrInfo;

    if(posContext == 0)
    {
        LOGE("SCardReleaseContext::Context has not been created!\n");
        ret = ERROR_NONE;
        return ret;
    }
    memset(&posReaderList, 0, sizeof(StrReaderList));
    rv = SCardReleaseContext(posContext);
    if(rv != SCARD_S_SUCCESS)
    {
        LOGE("SCardReleaseContext::native failed!\n");
        posContext = 0;
        ret = ERROR_APP_CONTEXT;
        return ret;
    }
    posContext = 0;

    LOGD("SCardReleaseContext::success!\n");
    ret = ERROR_NONE;
    return ret;
   }

   //断开卡连接
   int PBOC_DisConnect(unsigned int using_reader_id)
   {
    LOGD("%s(%d)",__FUNCTION__,using_reader_id);
    LONG rv;
    pStrReaderListInfo pStrInfo;

    if(using_reader_id < 0 || using_reader_id >= posReaderList.count)
    {
        LOGE("[%d]reader id is wrong(%d)\n",__LINE__, using_reader_id);
   //       Nok_Reason = ErrorReason_PCSCD_READERS_ID;
        return FALSE;
    }

    pStrInfo = &posReaderList.readerList[using_reader_id];

    if(pStrInfo->trans_begin == 1)
    {
        rv = SCardEndTransaction(pStrInfo->hCard,SCARD_LEAVE_CARD);
        if (rv != SCARD_S_SUCCESS)
        {
            if(rv == SCARD_E_NO_SERVICE || rv == SCARD_E_UNKNOWN_READER)
            {
                Reset_Reader();
            }
            LOGE("[%d]::SCardEndTransaction: %s (0x%lX)\n",__LINE__ ,pcsc_stringify_error(rv),rv);
        }
        pStrInfo->trans_begin = 0;
    }

    if(pStrInfo->hCard != 0)
    {
        rv = SCardDisconnect(pStrInfo->hCard,SCARD_UNPOWER_CARD);
        if (rv != SCARD_S_SUCCESS)
        {
            if(rv == SCARD_E_NO_SERVICE || rv == SCARD_E_UNKNOWN_READER)
            {
                Reset_Reader();
            }
            LOGE("[%d]::SCardDisconnect: %s (0x%lX)\n",__LINE__ ,pcsc_stringify_error(rv),rv);
        }
        pStrInfo->hCard = 0;
    }


    return TRUE;
   }
   int PBOC_OpenCard(unsigned char* pAtr,unsigned int * pAtrlen)
   {
    int ret;

    LOGD("%s",__FUNCTION__);

    //检卡
    DWORD dwReaderLen, dwState, dwProt, dwAtrLen;
    BYTE pbAtr[MAX_ATR_SIZE] = "";
    char pbReader[MAX_READERNAME] = "";

    pStrReaderListInfo pStrInfo;
    SCARD_READERSTATE rgReaderStates[1];

    pStrInfo = &posReaderList.readerList[posReaderId];
    dwAtrLen = sizeof(pbAtr);
    dwReaderLen = sizeof(pbReader);

    smart_card_type = 0;
    rgReaderStates[0].szReader =  posReaderList.readerList[posReaderId].reader;
    rgReaderStates[0].dwCurrentState = SCARD_STATE_UNAWARE;
    //检测是否有卡插入
    ret = SCardGetStatusChange(posContext,INFINITE,rgReaderStates,1);
    if (ret != SCARD_S_SUCCESS)
    {
        printf("SCardGetStatusChange err\r\n");
        return -1;
    }
   LOGD("SCardGetStatusChange ok\r\n");
   LOGD("rgReaderStates[0].dwEventState == %ld\r\n",rgReaderStates[0].dwEventState);        
    if(rgReaderStates[0].dwEventState & SCARD_STATE_PRESENT)//卡在
    {
        ret= SCardConnect(posContext, pStrInfo->reader, SCARD_SHARE_SHARED,
                    SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &pStrInfo->hCard, &posActiveProtocol[posReaderId]);
        if (ret == SCARD_S_SUCCESS) 
        {
            ret = SCardStatus(pStrInfo->hCard, pbReader, &dwReaderLen, &dwState, &dwProt,pbAtr, &dwAtrLen);
            if (ret != SCARD_S_SUCCESS)
            {
                return -1;
            }   
            MyPrintfBuff("CardAtr:",pbAtr, dwAtrLen);
            //判断ATR是否是非接触卡  
            if((pbAtr[2] == 0x81 && pbAtr[3] == 0x01)||(pbAtr[2] == 0x11 && pbAtr[3] == 0x01) )
            {
                smart_card_type = 2;
            }
            //判断ATR 多张非接触卡  
            else if((pbAtr[1] == 0x70 && pbAtr[2] == 0x11) )
            {
                smart_card_type = 3;
            }
            //判断ATR是否是接触卡   
            else if(!((pbAtr[2] == 0x81 && pbAtr[3] == 0x01)||(pbAtr[2] == 0x11 && pbAtr[3] == 0x01) ||(pbAtr[1] == 0x60 && pbAtr[2] == 0x00))) 
            {
                smart_card_type = 1;    
            }
            else
            {
                smart_card_type = -1;
            }
            switch(posActiveProtocol[posReaderId])
            {
                case SCARD_PROTOCOL_T0:
                    pStrInfo->pioSendPci = *SCARD_PCI_T0;
                    LOGD("using T0");
                    break;
                case SCARD_PROTOCOL_T1:
                    pStrInfo->pioSendPci = *SCARD_PCI_T1;
                    LOGD("using T1");
                    break;
                default:
                    LOGE("Unknown protocol. No card present?\n");
            }
            MyMemcpy(pAtr,pbAtr, dwAtrLen);
            *pAtrlen = dwAtrLen;
        }
        else
        {

        }

    }

    return smart_card_type;

   }

   //建立卡连接,并做好传输准备
   int PBOC_Connect(int using_reader_id)
   {
    LOGD("%s(%d)",__FUNCTION__,using_reader_id);
    LONG rv;
    int ret = TRUE;
    DWORD dwReaderLen, dwState, dwProt, dwAtrLen;
    BYTE pbAtr[MAX_ATR_SIZE] = "";
    char pbReader[MAX_READERNAME] = "";
    pStrReaderListInfo pStrInfo;
    unsigned int i;
    unsigned int counts = 0;

    if(using_reader_id < 0 || using_reader_id >= posReaderList.count)
    {
        LOGE("[%d]reader id is wrong(%d)\n",__LINE__, using_reader_id);
        ret = FALSE;
   //       Nok_Reason = ErrorReason_PCSCD_READERS_ID;
        goto ERROR;
    }
    pStrInfo = &posReaderList.readerList[using_reader_id];

    /* connect to a reader (even without a card) */
    LOGD("[%d]Using reader: %s\n",__LINE__, posReaderList.readerList[using_reader_id].reader);

    while(1)
    {
        if(posReaderList.readerList[using_reader_id].hCard != 0)
        {
            LOGD("connected(%d)",(int)posActiveProtocol[using_reader_id]);
            rv = SCARD_S_SUCCESS;
            LOGD("SCardReconnect");
            rv = SCardReconnect(posReaderList.readerList[using_reader_id].hCard, /*SCARD_SHARE_SHARED*/SCARD_SHARE_SHARED, 
                SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,SCARD_RESET_CARD, (DWORD*)&posActiveProtocol);
        }
        else
        {
            LOGD("connect");
            rv = SCardConnect(posContext, posReaderList.readerList[using_reader_id].reader, /*SCARD_SHARE_EXCLUSIVE*/SCARD_SHARE_SHARED,
                SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &posReaderList.readerList[using_reader_id].hCard, &posActiveProtocol[using_reader_id]);
        }
        if (rv != SCARD_S_SUCCESS)
        {
            LOGE("SCardConnect %s (0x%lX)\n" , pcsc_stringify_error(rv), rv);
            counts++;
            if(rv == SCARD_E_SHARING_VIOLATION)
            {
                if(counts < 10)
                {
                    usleep(500*1000);
                    LOGE("reader is busy,wait 500ms try again(%d)",counts);
                    continue;
                }
            }
            else if(rv == SCARD_E_NO_SERVICE || rv == SCARD_E_UNKNOWN_READER)
            {
                Reset_Reader();
                if(counts < 2)
                {
                    LOGE("try again(%d)",counts);
                    continue;
                }
            }
            ret = FALSE;
   //           Nok_Reason = ErrorReason_PCSCD_CONNECT_READERS;
            goto ERROR_NOTHING;
        }
        else
        {
            break;
        }
    }
    pStrInfo->dwActiveProtocol = posActiveProtocol[using_reader_id];
    /* get card status */
    dwAtrLen = sizeof(pbAtr);
    dwReaderLen = sizeof(pbReader);

    rv = SCardStatus(posReaderList.readerList[using_reader_id].hCard, pbReader, &dwReaderLen, &dwState, &dwProt,
        pbAtr, &dwAtrLen);
    if (rv != SCARD_S_SUCCESS)
    {
        LOGE("SCardStatus %s (0x%lX)\n" , pcsc_stringify_error(rv), rv);
        ret = FALSE;
   //       Nok_Reason = ErrorReason_PCSCD_GET_READERS_STATUS;
        goto ERROR;
    }
    LOGD(" State: 0x%04lX\n", dwState);

    if (dwState & SCARD_ABSENT)
    {
        LOGE("No card inserted\n");
        ret = FALSE;
   //       Nok_Reason = ErrorReason_PCSCD_NO_CARDS_INSIDE;
        goto ERROR;
    }

    switch(posActiveProtocol[using_reader_id])
    {
        case SCARD_PROTOCOL_T0:
            pStrInfo->pioSendPci = *SCARD_PCI_T0;
            LOGD("using T0");
            break;
        case SCARD_PROTOCOL_T1:
            pStrInfo->pioSendPci = *SCARD_PCI_T1;
            LOGD("using T1");
            break;
        default:
            LOGE("Unknown protocol. No card present?\n");
            ret = FALSE;
   //           Nok_Reason = ErrorReason_PCSCD_NO_PROTOCOLS_SELECT;
            goto ERROR;
    }
    rv = SCardBeginTransaction(posReaderList.readerList[using_reader_id].hCard);
    if (rv != SCARD_S_SUCCESS)
    {
        if(rv == SCARD_E_NO_SERVICE || rv == SCARD_E_UNKNOWN_READER)
        {
            Reset_Reader();
        }
        LOGE("SCardBeginTransaction %s (0x%lX)\n" , pcsc_stringify_error(rv), rv);
        ret = FALSE;
   //       Nok_Reason = ErrorReason_PCSCD_BEGIN_TRANSACTION;
        goto ERROR;
    }
    pStrInfo->trans_begin = 1;
    LOGD("SCardBeginTransaction::success!\n");
    return ret;
   ERROR:
    rv = SCardDisconnect(posReaderList.readerList[using_reader_id].hCard,SCARD_UNPOWER_CARD);
    if (rv != SCARD_S_SUCCESS)
    {
        if(rv == SCARD_E_NO_SERVICE || rv == SCARD_E_UNKNOWN_READER)
        {
            Reset_Reader();
        }
        LOGE("[%d]::SCardDisconnect: %s (0x%lX)\n",__LINE__ ,pcsc_stringify_error(rv),rv);
    }
   ERROR_NOTHING:
    return ret;
   }
   //卡上电连接并获取ATR
   int PBOC_PowerOn(int using_reader_id,unsigned char* pAtr,unsigned int * pAtrlen)
   {
    LOGD("%s",__FUNCTION__);
    LONG rv;
    int ret = TRUE;
    DWORD dwReaderLen, dwState, dwProt, dwAtrLen;
    BYTE pbAtr[MAX_ATR_SIZE] = "";
    char pbReader[MAX_READERNAME] = "";
    pStrReaderListInfo pStrInfo;
    unsigned int i;
    unsigned int counts = 0;

    if(using_reader_id < 0 || using_reader_id >= posReaderList.count)
    {
        LOGE("[%d]reader id is wrong(%d)\n",__LINE__, using_reader_id);
        ret = FALSE;
        return ret;
    }
    pStrInfo = &posReaderList.readerList[using_reader_id];

    /* connect to a reader (even without a card) */
    LOGD("[%d]Using reader: %s\n",__LINE__, posReaderList.readerList[using_reader_id].reader);

    while(1)
    {
        if(pStrInfo->hCard != 0)
        {
            LOGD("SCardConnect(%d)",(int)posActiveProtocol[using_reader_id]);
            rv = SCARD_S_SUCCESS;
            //LOGD("SCardReconnect");
            //rv = SCardReconnect(pStrInfo->hCard, /*SCARD_SHARE_SHARED*/SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
            //      SCARD_RESET_CARD, &dwposActiveProtocol);
        }
        else
        {
            LOGD("SCardConnect");
            rv = SCardConnect(posContext, pStrInfo->reader, /*SCARD_SHARE_SHARED*/SCARD_SHARE_SHARED,
                SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &pStrInfo->hCard, &posActiveProtocol[using_reader_id]);
        }
        if (rv != SCARD_S_SUCCESS)
        {
            LOGE("SCardConnect %s (0x%lX)\n" , pcsc_stringify_error(rv), rv);
            counts++;
            if(rv == SCARD_E_SHARING_VIOLATION)
            {
                if(counts < 10)
                {
                    usleep(500*1000);
                    LOGE("reader is busy,wait 500ms try again(%d)",counts);
                    continue;
                }
            }
            else if(rv == SCARD_E_NO_SERVICE || rv == SCARD_E_UNKNOWN_READER)
            {
                usleep(2000*1000);
                Pcscd_Init();
                if(counts < 2)
                {
                    LOGE("try again(%d)",counts);
                    continue;
                }
            }
            ret = FALSE;
            goto ERROR_NOTHING;
        }
        else
        {
            break;
        }
    }
    pStrInfo->dwActiveProtocol = posActiveProtocol[using_reader_id];
    /* get card status */
    dwAtrLen = sizeof(pbAtr);
    dwReaderLen = sizeof(pbReader);
    //获取卡状态信息
    rv = SCardStatus(pStrInfo->hCard, pbReader, &dwReaderLen, &dwState, &dwProt,
        pbAtr, &dwAtrLen);
    if (rv != SCARD_S_SUCCESS)
    {
        LOGE("SCardStatus %s (0x%lX)\n" , pcsc_stringify_error(rv), rv);
        ret = FALSE;
        goto ERROR;
    }
    LOGD(" State: 0x%04lX\n", dwState);

    if (dwState & SCARD_ABSENT)
    {
        LOGE("No card inserted\n");
        ret = FALSE;
        goto ERROR;
    }

    switch(posActiveProtocol[using_reader_id])
    {
        case SCARD_PROTOCOL_T0:
            pStrInfo->pioSendPci = *SCARD_PCI_T0;
            LOGD("using T0");
            break;
        case SCARD_PROTOCOL_T1:
            pStrInfo->pioSendPci = *SCARD_PCI_T1;
            LOGD("using T1");
            break;
        default:
            LOGE("Unknown protocol. No card present?\n");
            ret = FALSE;
            goto ERROR;
    }
    MyMemcpy(pbAtr,pAtr,dwAtrLen);
    *pAtrlen = dwAtrLen;
    //准备传输状态
    rv = SCardBeginTransaction(pStrInfo->hCard);
    if (rv != SCARD_S_SUCCESS)
    {
        if(rv == SCARD_E_NO_SERVICE || rv == SCARD_E_UNKNOWN_READER)
        {
            Reset_Reader();
        }
        LOGE("SCardBeginTransaction %s (0x%lX)\n" , pcsc_stringify_error(rv), rv);
        ret = FALSE;
        goto ERROR;
    }
    pStrInfo->trans_begin = 1;
    LOGD("SCardBeginTransaction::success!\n");
    return ret;
   ERROR:
    rv = SCardDisconnect(pStrInfo->hCard,SCARD_UNPOWER_CARD);
    if (rv != SCARD_S_SUCCESS)
    {
        if(rv == SCARD_E_NO_SERVICE || rv == SCARD_E_UNKNOWN_READER)
        {
            Reset_Reader();
        }
        LOGE("[%d]::SCardDisconnect: %s (0x%lX)\n",__LINE__ ,pcsc_stringify_error(rv),rv);
    }
   ERROR_NOTHING:
    return ret;


   }
   int PBOC_PowerOff(int using_reader_id)
   {
    LOGD("%s",__FUNCTION__);
    LONG rv;
    pStrReaderListInfo pStrInfo;

    pStrInfo = &posReaderList.readerList[using_reader_id];
    //结束传输
    if(pStrInfo->trans_begin == 1)
    {
        rv = SCardEndTransaction(pStrInfo->hCard,SCARD_LEAVE_CARD);
        if (rv != SCARD_S_SUCCESS)
        {
            if(rv == SCARD_E_NO_SERVICE || rv == SCARD_E_UNKNOWN_READER)
            {
                ResetReader();
            }
            LOGE("[%d]::SCardEndTransaction: %s (0x%lX)\n",__LINE__ ,pcsc_stringify_error(rv),rv);
        }
        pStrInfo->trans_begin = 0;
    }
    //断开连接
    if(posReaderList.readerList[using_reader_id].hCard != 0)
    {
        LOGD("SCardDisconnect");
        rv = SCardDisconnect(pStrInfo->hCard,SCARD_UNPOWER_CARD);
        if (rv != SCARD_S_SUCCESS)
        {
            if(rv == SCARD_E_NO_SERVICE || rv == SCARD_E_UNKNOWN_READER)
            {
                Reset_Reader();
            }
            LOGE("[%d]::CloudPos_PowerOff: %s (0x%lX)\n",__LINE__ ,pcsc_stringify_error(rv),rv);
        }
        pStrInfo->hCard = 0;
    }
    return rv;
   }

   int PBOC_APDU_SendAPI(unsigned int using_reader_id,unsigned char * bSendBuffer,unsigned int send_length,unsigned char * bRecvBuffer,unsigned int *length)
   {
    LOGD("%s",__FUNCTION__);
    LONG rv;
    int ret = 0;
    unsigned int i;
    unsigned char temp[64] = {0};
    //char debug_buffer[512] = {0};

    pStrReaderListInfo pStrInfo;

    if(using_reader_id < 0 || using_reader_id >= posReaderList.count)
    {
        LOGE("[%d]reader id is wrong(%d)\n",__LINE__, using_reader_id);
        ret = FALSE;
        goto SEND_ERROR;
    }
   #ifdef SE_KL81
    if(send_length%64 == 54)
    {
        LOGI("fill 0xFFFF");
        bSendBuffer[send_length] = 0xFF;
        bSendBuffer[send_length+1] = 0xFF;
        send_length += 2;
    }
   #endif
    pStrInfo = &posReaderList.readerList[using_reader_id];

    /* APDU select applet */

    LOGD("[%d]::PBOC_APDU_SendAPI()::Dev send to card:\n",__LINE__);
    if(send_length >= 256)
    {
        LOGD("send_length is bigger then print buffer length");
    }
    else
    {
        MyPrintfBuff("APDU_send:",bSendBuffer, send_length);
    }
    *length = MAX_BUFFER_SIZE_SZ;
    MyMemset(bRecvBuffer,0x00,MAX_BUFFER_SIZE_SZ);
    if(memcmp(&pStrInfo->pioSendPci,SCARD_PCI_T0,sizeof(pStrInfo->pioSendPci)) == 0)
    {
        LOGD("using T0");
    }
    else if(memcmp(&pStrInfo->pioSendPci,SCARD_PCI_T1,sizeof(pStrInfo->pioSendPci)) == 0)
    {
        LOGD("using T1");
    }
    else
    {
        LOGD("using None");
    }
    rv = SCardTransmit(pStrInfo->hCard, &pStrInfo->pioSendPci, bSendBuffer, (DWORD)send_length,
                            &pStrInfo->pioRecvPci, bRecvBuffer, (DWORD *)length);
    if(rv != SCARD_S_SUCCESS)
    {
        if(rv == SCARD_E_NO_SERVICE || rv == SCARD_E_UNKNOWN_READER)
        {
            Reset_Reader();
        }
        MyMemset(bRecvBuffer,0x00,MAX_BUFFER_SIZE_SZ);
        LOGE("SCardTransmit error,try again!(0x%08x)(%d)",(unsigned int)rv,*length);
        snprintf(temp,sizeof(temp),"SCardTransmit error(0x%x)",(unsigned int)rv);
        sleep(1);
        *length = MAX_BUFFER_SIZE_SZ;
        rv = SCardTransmit(pStrInfo->hCard, &pStrInfo->pioSendPci, bSendBuffer, (DWORD )send_length,
                                &pStrInfo->pioRecvPci, bRecvBuffer,(DWORD *) length);

        if(rv != SCARD_S_SUCCESS)
        {
            if(rv == SCARD_E_NO_SERVICE || rv == SCARD_E_UNKNOWN_READER)
            {
                Reset_Reader();
            }
            LOGE("(rv = 0x%08x)",(unsigned int)rv);
            snprintf(temp,sizeof(temp),"SCardTransmit error again(0x%x)",(unsigned int)rv);
            ret = FALSE;
            goto SEND_ERROR;
        }
    }
    LOGD("[%d]::Card response to dev:\n",__LINE__);
    if(*length >= 5120)
    {
        LOGD("length is bigger then print buffer length.");
    }
    else
    {
        MyPrintfBuff("APDU_Recv:",bRecvBuffer, *length);
    }
    if(*length > 54 && (*length+10)%64 == 2 && (bRecvBuffer[*length-2] == 0xFF && bRecvBuffer[*length-1] == 0xFF))
    {
        LOGD("clean FFFF");
        (*length) -= 2;
    }

   PRASE_CARD_RETURN_STATUS:
    if(bRecvBuffer[*length-2] == 0x61)
    {
        LOGD("[%d]::Dev 0x61 to card:\n",__LINE__);
        memset(bSendBuffer,0x00,MAX_BUFFER_SIZE_SZ);
        usleep(100*1000);
        LOGD("[%d]::Card with the remaining returned to %02x%02x\n",__LINE__,bRecvBuffer[0],bRecvBuffer[1]);
        send_length = 5;//00 C0 00 00 2A
        memcpy(bSendBuffer, "\x00\xC0\x00\x00",send_length-1);

        bSendBuffer[send_length-1] = bRecvBuffer[*length-1];

        LOGD("[%d]::Dev send to card:\n",__LINE__);
        MyMemset(bRecvBuffer,0x00,MAX_BUFFER_SIZE_SZ);
        *length = MAX_BUFFER_SIZE_SZ;
        rv = SCardTransmit(pStrInfo->hCard, &pStrInfo->pioSendPci, bSendBuffer, (DWORD )send_length,
            &pStrInfo->pioRecvPci, bRecvBuffer, (DWORD *)length);
        MyPrintfBuff("APDU_Recv:",bRecvBuffer, *length);
        if(rv != SCARD_S_SUCCESS)
        {
            if(rv == SCARD_E_NO_SERVICE || rv == SCARD_E_UNKNOWN_READER)
            {
                Reset_Reader();
            }
            MyMemset(bRecvBuffer,0x00,MAX_BUFFER_SIZE_SZ);
            LOGE("SCardTransmit error,try again!");
            snprintf(temp,sizeof(temp),"SCardTransmit error(0x%x)",(unsigned int)rv);
            *length = MAX_BUFFER_SIZE_SZ;
            sleep(1);
            rv = SCardTransmit(pStrInfo->hCard, &pStrInfo->pioSendPci, bSendBuffer, (DWORD )send_length,
                &pStrInfo->pioRecvPci, bRecvBuffer, (DWORD *)length);
            if(rv != SCARD_S_SUCCESS)
            {
                if(rv == SCARD_E_NO_SERVICE || rv == SCARD_E_UNKNOWN_READER)
                {
                    Reset_Reader();
                }
                LOGE("SCardTransmit error!");
                snprintf(temp,sizeof(temp),"SCardTransmit error again(0x%x)",(unsigned int)rv);
                ret = FALSE;
                goto SEND_ERROR;
            }
        }
        LOGD("[%d]::Card response to dev:\n",__LINE__);
        //memset(debug_buffer,0,sizeof(debug_buffer));
        if(*length >= 5120)
        {
            LOGE("length is bigger then print buffer length");
        }
        else
        {
        //  myAsciiToHex(bRecvBuffer, debug_buffer, *length);
            MyPrintfBuff("APDU_Recv:",bRecvBuffer, *length);

        }
        if(*length > 54 && (*length+10)%64 == 2 && (bRecvBuffer[*length-2] == 0xFF && bRecvBuffer[*length-1] == 0xFF))
        {
            LOGD("clean FFFF");
            (*length) -= 2;
        }
    }
    else if(bRecvBuffer[*length-2] == 0x6C)
    {
        LOGD("[%d]::Dev 0x6C to card:\n",__LINE__);
        bSendBuffer[send_length-1] = bRecvBuffer[*length-1];
        MyMemset(bRecvBuffer,0x00,MAX_BUFFER_SIZE_SZ);
        rv = SCardTransmit(pStrInfo->hCard, &pStrInfo->pioSendPci, bSendBuffer,(DWORD ) send_length,
            &pStrInfo->pioRecvPci, bRecvBuffer, (DWORD *)length);
        if(rv != SCARD_S_SUCCESS)
        {
            if(rv == SCARD_E_NO_SERVICE || rv == SCARD_E_UNKNOWN_READER)
            {
                Reset_Reader();
            }
            MyMemset(bRecvBuffer,0x00,MAX_BUFFER_SIZE_SZ);
            LOGE("SCardTransmit error,try again!");
            *length = MAX_BUFFER_SIZE_SZ;
            sleep(1);
            rv = SCardTransmit(pStrInfo->hCard, &pStrInfo->pioSendPci, bSendBuffer, (DWORD )send_length,
                &pStrInfo->pioRecvPci, bRecvBuffer, (DWORD*)length);
            if(rv != SCARD_S_SUCCESS)
            {
                if(rv == SCARD_E_NO_SERVICE || rv == SCARD_E_UNKNOWN_READER)
                {
                    Reset_Reader();
                }
   //               Nok_Reason = ErrorReason_PCSCD_TRANSMIT;
                ret = FALSE;
                goto SEND_ERROR;
            }
        }
        if(*length > 54 && (*length+10)%64 == 2 && (bRecvBuffer[*length-2] == 0xFF && bRecvBuffer[*length-1] == 0xFF))
        {
            LOGD("clean FFFF");
            (*length) -= 2;
        }
        goto PRASE_CARD_RETURN_STATUS;
    }
    else if((bRecvBuffer[*length-2] == 0x6A )&&( bRecvBuffer[*length-1] == 0x83))
    {
        LOGD("[%d]::Card response 6A83",__LINE__);
    }
    else if((bRecvBuffer[*length-2] != 0x90)||(bRecvBuffer[*length-1] != 0x00))
    {
        LOGE("Cards respont isn't 0x9000(0x%02X%02X)!\n",bRecvBuffer[*length-2],bRecvBuffer[*length-1]);
    }
    else
    {
        //LOGD("[%d]::Card response to dev:\n",__LINE__);
        //memset(debug_buffer,0,sizeof(debug_buffer));
        if(*length >= 5120)
        {
            LOGE("length is bigger then print buffer length");
        }
        else
        {
            //myAsciiToHex(bRecvBuffer, debug_buffer, *length);
            //MyPrintfBuff("APDU_Recv:",bRecvBuffer, *length);
        }
    }
   SEND_ERROR:
    return ret;
   }

//main.c
//使用test程序做PSAM卡和ICC卡通道通讯
ret = Pcscd_Init();
if(ret != 0)//读卡器未找到
{
    return -1;
}
while(1)
{
    printf("Please input command:\n");
    printf("################################\n");
    printf("'0' is select PSAM CARD\n");
    printf("'1' is select ICC CARD\n");
    printf("'quit' is return\n");
    printf("'break' is go back\n");
    printf("################################\n");
    MyMemset(camd_buf,0,sizeof(camd_buf));
    scanf("%s",camd_buf);
    getchar();
    if(MyMemcmp("0",camd_buf,MyStrlen("0")) == 0)
    {
        while(1)
        {
            printf("Please input APDU:\n");
            MyMemset(camd_buf,0,sizeof(camd_buf));
            scanf("%s",camd_buf);
            getchar();
            if(MyMemcmp("break",camd_buf,MyStrlen("break")) == 0)
                break;
            MyStrToHex((camd_buf),send_buf);
            ret = CloudPos_SendToPcscd((unsigned char *)send_buf,0,recv_buf,&recv_len);

            MyPrintfBuff("recv_buf::",recv_buf,recv_len);
        }
    }
    else if(memcmp("1",camd_buf,MyStrlen("1")) == 0)
    {
        ret = PBOC_Connect(1);
        if(ret == FALSE)
        {
            printf("Connect(1) error Please try again!\n");
        }
        else
        {
            printf("Connect(1) sucess\n");
            while(1)
            {
                printf("Please input APDU:\n");
                MyMemset(camd_buf,0,sizeof(camd_buf));
                scanf("%s",camd_buf);
                getchar();
                if(MyMemcmp("break",camd_buf,MyStrlen("break")) == 0)
                    break;
                send_len = MyStrToHex((camd_buf),send_buf);

                ret = PBOC_APDU_SendAPI(1,send_buf,send_len,recv_buf,&recv_len);
                MyPrintfBuff("recv_buf::",recv_buf,recv_len);

            }
        }
        PBOC_DisConnect(1);
    }
    else if(MyMemcmp("quit",camd_buf,MyStrlen("quit")) == 0)
    {
        break;
    }
}
Pcscd_Release();
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值