PCSC那事儿(三十--PCSCD)

 

 

464         /*

465          * Allocate memory for reader structures

466          */

467         (void)RFAllocateReaderSpace();

468

RFAllocateReaderSpace 定义在 readerfactory.c

实现如下:

  55 LONG RFAllocateReaderSpace(void)

  56 {

  57         int i;  /* Counter */

  58

  59         /* Allocate each reader structure */

  60         for (i = 0; i < PCSCLITE_MAX_READERS_CONTEXTS; i++)

  61         {

  62                 sReadersContexts[i] = malloc(sizeof(READER_CONTEXT));

  63                 (sReadersContexts[i])->vHandle = NULL;

  64                 (sReadersContexts[i])->readerState = NULL;

  65          }

  66

  67         /* Create public event structures */

  68         return EHInitializeEventStructures();

  69 }

 

60~65 行,初始化读卡器上下文 ReadersContexts

EHInitializeEventStructures 则是创建共享内存。

  44 LONG EHInitializeEventStructures(void)

  45 {

  46         int fd, i, pageSize;

  47         int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;

  48

  49         fd = 0;

  50         i = 0;

  51         pageSize = 0;

  52

  53         (void)SYS_RemoveFile(PCSCLITE_PUBSHM_FILE);

  54

  55         fd = SYS_OpenFile(PCSCLITE_PUBSHM_FILE, O_RDWR | O_CREAT, mode);

  56         if (fd < 0)

  57         {

  58                 Log3(PCSC_LOG_CRITICAL, "Cannot create public shared file %s: %s",

  59                         PCSCLITE_PUBSHM_FILE, strerror(errno));

  60                 exit(1);

  61         }

  62

  63         /* set correct mode even is umask is too restictive */

  64         (void)SYS_Chmod(PCSCLITE_PUBSHM_FILE, mode);

  65

  66         pageSize = SYS_GetPageSize();

  67

  68         /*

  69          * Jump to end of file space and allocate zero's

  70          */

  71         (void)SYS_SeekFile(fd, pageSize * PCSCLITE_MAX_READERS_CONTEXTS);

  72         (void)SYS_WriteFile(fd, "", 1);

  73

  74         /*

  75          * Allocate each reader structure

  76          */

  77         for (i = 0; i < PCSCLITE_MAX_READERS_CONTEXTS; i++)

  78         {

  79                 readerStates[i] = (PREADER_STATE)

  80                         SYS_MemoryMap(sizeof(READER_STATE), fd, (i * pageSize));

  81                 if (readerStates[i] == MAP_FAILED)

  82                 {

  83                         Log3(PCSC_LOG_CRITICAL, "Cannot memory map public shared file %s: %s",

  84                                 PCSCLITE_PUBSHM_FILE, strerror(errno));

  85                         exit(1);

  86                 }

  87

  88                 /*

  89                  * Zero out each value in the struct

  90                   */

  91                 memset((readerStates[i])->readerName, 0, MAX_READERNAME);

  92                 memset((readerStates[i])->cardAtr, 0, MAX_ATR_SIZE);

  93                 (readerStates[i])->readerID = 0;

  94                 (readerStates[i])->readerState = 0;

  95                 (readerStates[i])->readerSharing = 0;

  96                 (readerStates[i])->cardAtrLength = 0;

  97                 (readerStates[i])->cardProtocol = SCARD_PROTOCOL_UNDEFINED;

  98         }

  99

100          return SCARD_S_SUCCESS;

101 }

LONG EHInitializeEventStructures(void)

45~101 行,

创建共享内存文件,映射文件到内存 readerStates 数组,并初始化数组各成员。

 

回到 pcscdaemon.c

469         /*

470          * Grab the information from the reader.conf

471          */

472         if (newReaderConfig)

473         {

474                 rv = RFStartSerialReaders(newReaderConfig);

475                 if (rv != 0)

476                 {

477                         Log3(PCSC_LOG_CRITICAL, "invalid file %s: %s", newReaderConfig,

478                                  strerror(errno));

479                         ExitValue = EXIT_FAILURE;

480                         at_exit();

481                 }

482         }

483         else

484         {

485                 rv = RFStartSerialReaders(PCSCLITE_READER_CONFIG);

486

487 #if 0

488                 if (rv == 1)

489                 {

490                         Log1(PCSC_LOG_INFO,

491                                 "warning: no " PCSCLITE_READER_CONFIG " found");

492                          /*

493                          * Token error in file

494                          */

495                 }

496                 else

497 #endif

498                         if (rv == -1)

499                         {

500                                  ExitValue = EXIT_FAILURE;

501                                 at_exit();

502                         }

503         }

504

如果 main 的命令行参数有指定 serial PCMCIA 类型的读卡器配置文件路径,则读取该配置文件

RFStartSerialReaders 定义在 readerfactory.c

实现如下:

1299 int RFStartSerialReaders(const char *readerconf)

1300 {

1301         SerialReader *reader_list;

1302         int i, rv;

1303

1304         /* remember the configuration filename for RFReCheckReaderConf() */

1305         ConfigFile = strdup(readerconf);

1306

1307         rv = DBGetReaderList(readerconf, &reader_list);

1308

1309         /* the list is empty */

1310         if (NULL == reader_list)

1311                 return rv;

1312

1313         for (i=0; reader_list[i].pcFriendlyname; i++)

1314         {

1315                  int j;

1316

1317                 (void)RFAddReader(reader_list[i].pcFriendlyname,

1318                         reader_list[i].dwChannelId,

1319                         reader_list[i].pcLibpath, reader_list[i].pcDevicename);

1320

1321                  /* update the ConfigFileCRC (this false "CRC" is very weak) */

1322                 for (j=0; j<reader_list[i].pcFriendlyname[j]; j++)

1323                         ConfigFileCRC += reader_list[i].pcFriendlyname[j];

1324                 for (j=0; j<reader_list[i].pcLibpath[j]; j++)

1325                         ConfigFileCRC += reader_list[i].pcLibpath[j];

1326                 for (j=0; j<reader_list[i].pcDevicename[j]; j++)

1327                         ConfigFileCRC += reader_list[i].pcDevicename[j];

1328

1329                 /* free strings allocated by DBGetReaderList() */

1330                 free(reader_list[i].pcFriendlyname);

1331                 free(reader_list[i].pcLibpath);

1332                 free(reader_list[i].pcDevicename);

1333         }

1334         free(reader_list);

1335

1336         return rv;

1337 }

1307 行, DBGetReaderList 定义在 configfile.c

实现如下:

1932 int DBGetReaderList(const char *readerconf, SerialReader **caller_reader_list)

1933 {

1934         FILE *configFile = NULL;

1935

1936         *caller_reader_list = NULL;     /* no list by default */

1937

1938         configFile = fopen(readerconf, "r");

1939

1940         if (configFile == NULL)

1941                 return 1;

1942

1943         yyin = configFile;

1944

1945         /* (re)start with a clean state */

1946         iLinenumber = 1;

1947         iOldLinenumber = -1;

1948         reader_list = NULL;

1949         reader_list_size = 0;

1950         pcFriendlyname = NULL;

1951         pcDevicename = NULL;

1952         pcLibpath = NULL;

1953         pcChannelid = NULL;

1954         pcPrevious = NULL;

1955         pcCurrent = NULL;

1956         badError = 0;

1957

1958         do

1959         {

1960                 (void)yylex();

1961          }

1962         while (!feof(configFile));

1963

1964         (void)fclose(configFile);

1965

1966         *caller_reader_list = reader_list;

1967

1968         if (badError == 1)

1969                 return -1;

1970         else

1971                  return 0;

1972 }                                                               /* End of configfile.c */

1932~1972 行,解析 reader.conf 文件,构造 reader_list.

解析过程采用 flex. flex 又是另外的故事,请参考相关书籍。不是服务端重点,不做深入解说。

 

实际上只要记住 caller_reader_list

根据配置文件 reader.conf 解析出读卡器列表。

1313~1320 行,遍历读卡器列表,

 

RFAddReader 定义在 readerfacotry.c

实现如下:

    71 LONG RFAddReader(LPSTR lpcReader, DWORD dwPort, LPSTR lpcLibrary, LPSTR lpcDevice)

  72 {

  73         DWORD dwContext = 0, dwGetSize;

  74         UCHAR ucGetData[1], ucThread[1];

  75          LONG rv, parentNode;

  76         int i, j;

  77

  78         if ((lpcReader == NULL) || (lpcLibrary == NULL) || (lpcDevice == NULL))

  79                 return SCARD_E_INVALID_VALUE;

  80

  81         /* Reader name too long? also count " 00 00"*/

  82         if (strlen(lpcReader) > MAX_READERNAME - sizeof(" 00 00"))

  83         {

  84                 Log3(PCSC_LOG_ERROR, "Reader name too long: %d chars instead of max %d",

  85                         strlen(lpcReader), MAX_READERNAME - sizeof(" 00 00"));

  86                 return SCARD_E_INVALID_VALUE;

  87         }

  88

  89         /* Library name too long? */

  90         if (strlen(lpcLibrary) >= MAX_LIBNAME)

  91         {

  92                 Log3(PCSC_LOG_ERROR, "Library name too long: %d chars instead of max %d",

  93                         strlen(lpcLibrary), MAX_LIBNAME);

  94                 printf("%s/n %d/n",lpcLibrary,MAX_LIBNAME);

  95                 return SCARD_E_INVALID_VALUE;

  96         }

  97

  98         /* Device name too long? */

  99         if (strlen(lpcDevice) >= MAX_DEVICENAME)

  100         {

  101                 Log3(PCSC_LOG_ERROR, "Device name too long: %d chars instead of max %d",

  102                         strlen(lpcDevice), MAX_DEVICENAME);

  103                 return SCARD_E_INVALID_VALUE;

  104         }

  105

 

72~105 行,在函数入口处进行检查。分别对读卡器,库文件名字,设备名。

参数 dwPort(DWORD, 前面提过了,也就是 unsigned long, 具体的类型长度,根据体系结构而定 ) 分两种情况,一种是表示串行读卡器的编号 COM1 等,另一种用于 usb 读卡器,

实际是一种上下文编号 (hotplug_libusb.c) ,这种情况下,这个编号的高 16 (31~16bit) PCSCLITE_HP_BASE_PORT

 

  106         /* Same name, same port - duplicate reader cannot be used */

  107         if (dwNumReadersContexts != 0)

  108         {

  109                 for (i = 0; i < PCSCLITE_MAX_READERS_CONTEXTS; i++)

  110                 {

  111                         if ((sReadersContexts[i])->vHandle != 0)

  112                          {

  113                                 char lpcStripReader[MAX_READERNAME];

  114                                 int tmplen;

  115

  116                                 /* get the reader name without the reader and slot numbers */

  117                                 strncpy(lpcStripReader, (sReadersContexts[i])->lpcReader,

  118                                         sizeof(lpcStripReader));

  119                                 tmplen = strlen(lpcStripReader);

  120                                  lpcStripReader[tmplen - 6] = 0;

111 行, vhandle ,从后面的分析可以知道,这个表示所打开动态库的句柄。

如果 vhandle==0, 则表示对应的读卡器上下文未被使用。

如果被使用,则比较要加入的读卡器的读卡器名字和上下文中已经存在的读卡器名字。

NEWS for OpenSC -- History of user visible changes Complete change history is available online: http://www.opensc-project.org/opensc/timeline New in 0.12.0; 2010-12-22 * OpenSC uses a single reader driver, specified at compile time. * New card driver: Italian eID (CNS) by Emanuele Pucciarelli. * New card driver: Portuguese eID by Jo茫o Poupino. * New card driver: westcos by Fran莽ois Leblanc. * pkcs11-tool can use a slot based on ID, label or index in the slot list. * PIN flags are updated from supported cards when C_GetTokenInfo is called. * Support for CardOS 4.4 cards added. * Fature to exclude readers from OpenSC PKCS#11 via "ignored_readers" configuration file entry. * #229: Support semi-automatic fixes to cards personalized with older and broken OpenSC versions. * Software keys removed from pkcs15-init and the PKCS#11 module. OpenSC can either generate keys on card or import plaintext keys to the card, but will never generate plaintext key material in software by itself. All traces of a software token (PKCS#15 Section 7) shall be removed. * Updates to PC/SC driver to build with pcsc-lite >= 1.6.2 * Build script for a binary Mac OS X installer for 10.5 and 10.6 systems. Binary installer includes OpenSC.tokend for platform integration. 10.6 installer includes engine_pkcs11. * Modify Rutoken S binary interfaces by Aktiv Co. * Support GOST R 34.10-2001 and GOST R 34.11-94 by Aktiv Co. * CardOS driver now emulates sign on rsa keys with sign+decrypt usage with padding and decrypt(). This is compatible with old cards and card initialized by Siemens software. Removed "--split-key" option, as it is no longer needed. * Improved debugging support: debug level 3 will show everything except of ASN1 and card matching debugging (usualy not needed). * Massive changes to libopensc. This library is now internal, only used by opensc-pkcs11.so and command line tools. Header files are no longer installed, library should not be used by other applications.
<think> 我们正在解决用户的问题:在Linux系统下使用新中新身份证阅读器DKQ-A16D时,执行`pcsc_scan`无输出结果的问题。根据用户上次的问题和引用的内容,我们可以按照以下步骤进行排查和解决。 ### 步骤1: 确认USB设备被识别 首先,我们需要确认系统是否识别了USB设备。执行以下命令: ```bash lsusb ``` 在输出中查找类似`1c34:0800 XinZhongXin Electronics`的设备。如果看不到设备,可能是USB连接问题或者虚拟机没有正确连接设备(如果使用虚拟机的话)。在虚拟机环境下,需要手动将USB设备连接到虚拟机(如VMware的“可移动设备”菜单中选中该设备)[^1]。 ### 步骤2: 检查pcscd服务状态 确保`pcscd`服务已经启动并正常运行: ```bash systemctl status pcscd ``` 如果服务没有运行,启动它: ```bash sudo systemctl start pcscd ``` 如果服务正在运行,尝试重启它: ```bash sudo systemctl restart pcscd ``` ### 步骤3: 检查pcscd守护进程的调试输出 运行`pcscd`在前台并启用调试输出,以查看详细日志: ```bash sudo pcscd -f -d ``` 在另一个终端中运行`pcsc_scan`,观察`pcscd`终端的输出。注意是否有错误信息,特别是关于设备无法打开或者通信失败等信息。 ### 步骤4: 检查设备权限 确保当前用户有权限访问设备。检查设备的权限和所属组: ```bash ls -l /dev/bus/usb/$(lsusb | grep '1c34:0800' | awk '{print $2,$4}' | sed 's/://') ``` 通常,设备文件(如`/dev/bus/usb/001/002`)的权限需要为`crw-rw-r--`,并且所属组可能是`root`或者`plugdev`。如果当前用户不在设备所属组中,可以将用户添加到`plugdev`组(如果设备属于该组): ```bash sudo usermod -aG plugdev $USER ``` 然后重新登录。 ### 步骤5: 检查内核消息 查看内核日志,了解设备插入时的信息: ```bash dmesg | grep -i usb ``` 查找是否有关于该设备的错误信息。 ### 步骤6: 检查libccid配置 确认`libccid`是否正确支持该设备的USB ID。编辑`/etc/libccid_Info.plist`文件,确保设备的USB ID在配置文件中被允许。查找类似以下的行: ```xml <key>ifdProductID</key> <array> <string>0x0800</string> </array> <key>ifdVendorID</key> <array> <string>0x1C34</string> </array> ``` 如果没有,可以手动添加(以root权限编辑)。然后重启`pcscd`服务。 ### 步骤7: 手动加载usb驱动 有时候,需要手动加载USB驱动模块: ```bash sudo modprobe usbserial sudo modprobe pl2303 # 如果设备使用PL2303芯片,但新中新通常不是,这一步可能不需要 ``` ### 步骤8: 使用其他工具测试 安装`libusb`相关的工具来测试设备: ```bash sudo apt install usbutils libusb-1.0-0-dev ``` 然后使用`lsusb -v`查看设备详细信息,或者使用`usb-devices`命令。 ### 步骤9: 虚拟机注意事项 如果是在虚拟机中运行,确保虚拟机设置中已经将USB控制器设置为3.0(或2.0)并启用。同时,在虚拟机软件中,可能需要设置USB设备的筛选规则,确保设备可以自动连接到虚拟机。 ### 步骤10: 固件问题 如果以上步骤都失败,可能是设备固件与Linux不兼容。尝试联系厂商获取更新的固件或专用的Linux驱动。 ## 总结流程图 ```mermaid graph TD A[pcsc_scan无输出] --> B[运行lsusb确认设备] B -->|设备不存在| C[检查USB连接/虚拟机设置] B -->|设备存在| D[检查pcscd服务状态] D -->|未运行| E[启动pcscd服务] D -->|已运行| F[检查pcscd调试输出] F --> G[检查设备权限] G --> H[检查内核日志] H --> I[检查libccid配置] I --> J[手动加载USB驱动] J --> K[使用其他工具测试] K -->|仍失败| L[虚拟机设置/固件问题] ``` §§ 相关问题 §§ 1. 如何解决Linux下PC/SC服务报`Error 8010001e`错误? 2. 虚拟机中USB设备无法传递给Linux系统怎么办? 3. 如何将用户添加到`plugdev`组? 4. 如何修改`libccid`的配置文件以支持新的USB读卡器? </think> ### 解决DKQ-A16D身份证阅读器`pcsc_scan`无输出的排查指南 #### 1. **验证USB设备识别** ```bash lsusb | grep "XinZhongXin" # 检查设备是否被系统识别 dmesg | tail -20 # 查看内核日志 ``` - ✅ 预期结果:应显示类似 `ID 1c34:0800` 的设备标识 - ❌ 若无输出: - 物理层:检查USB接口/线缆接触 - 虚拟机:需手动连接USB设备到虚拟机(VMware/VirtualBox菜单)[^1] - 权限问题:尝试`sudo pcsc_scan` #### 2. **检查PC/SC服务状态** ```bash systemctl status pcscd # 查看服务状态 journalctl -u pcscd -n 50 # 检查服务日志 ``` - 常见问题处理: ```bash sudo systemctl restart pcscd # 重启服务 sudo sed -i 's/^#FFFE/FFFE/g' /etc/libccid_Info.plist # 解除厂商限制[^4] ``` #### 3. **启用调试模式** ```bash sudo pcscd -f -d & # 前台运行并输出调试日志[^1] pcsc_scan -v # 显示详细通信过程 ``` 观察关键信息: - `CANNOT RESET CARD` → 供电不足(尝试USB直连主板接口) - `IFD not supported` → 驱动不兼容 #### 4. **驱动兼容性配置** 针对ARM架构(如树莓派): ```bash ./configure --host=arm-linux \ LIBUSB_LIBS="-L/usr/ccid/lib -lusb" \ --disable-libhal # 交叉编译配置[^4] ``` 验证驱动加载: ```bash lsmod | grep usbserial # 检查内核模块 ``` #### 5. **内核级排查** ```mermaid graph TD A[pcsc_scan无输出] --> B{lsusb显示设备?} B -->|是| C[检查pcscd服务状态] B -->|否| D[检查USB控制器供电] C -->|服务异常| E[分析journalctl日志] C -->|服务正常| F[执行pcscd -f -d] F --> G[观察ATR码交互] G -->|无ATR| H[更换USB主机控制器] ``` #### 6. **特殊环境处理** - **老旧系统(如CentOS 6)**: ```bash chkconfig --level 3 pcscd on # 设置运行级别[^2] ``` - **延迟响应问题**: ```bash echo 200 | sudo tee /sys/module/usbcore/parameters/delayed_autosuspend ``` > **注意**:DKQ-A16D的预期ATR码应为`3B 9F 96 81...`,若实际获取不符需检查硬件兼容性[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值