通过HIDDEV编程接口读取易方数码笔的坐标数据

来源:互联网 发布:淘宝 高6戴卡轮毂 编辑:程序博客网 时间:2024/05/16 07:31

易方数码笔缺省是一个标准的USB HID设备(Mouse),在OS为Android的平板电脑上接入时,基于USB Mouse形式输出的笔迹精度不够,为此,我们将其切换至hiddev设备类型,通过发送相关命令来获得笔输出的原始坐标信息,这样精度就大大提升了。

基于USB HID协议,除可实现为普通的输入设备外,也可用作为有自定义通讯功能的设备。事实上,当前Linux 内核中已提供了这两种HID事件的接口,即输入子系统和hiddev接口(具体请参见Linux内核根目录下的Documentation/hid/hiddev.txt文档)。

为使用hiddev设备,我们只需在配置内核时要打开hiddev选项,这样就可以编写应用级代码来读取笔的原始坐标信息了。hiddev驱动是一个字符型驱动,其访问节点一般为/dev/usb/hiddev[0~15],在应用程序中打开这个设备节点后,即可调用hiddev API来与hiddev设备进行通讯。

hiddev API有两个调用接口,read和ioctl调用。read只用于获取hiddev设备的状态变化,而主机与设备间进行数据交换是通过ioctl调用来实现的,写数据时传入ioctl的命令字为HDIOCSREPORT,读数据时则传入HDIOCGREPORT,传送的数据封装在report中,每个report分成多个filed,而每个filed又有多个usage。

访问数码笔时,我们要向设备发命令数据来通知设备切换输出模式(输出原始坐标信息),同时,我们也要实时地读取出数据笔输出的原始坐标信息。

1. 打开设备

[cpp] view plaincopyprint?
  1. int digitalpen_open(void)  
  2. {  
  3.   int  index;  
  4.   int  fd;  
  5.   char hid_dev_node[50];  
  6.   struct hiddev_devinfo dinfo;  
  7.    
  8.   for(index = 0; index < 15; index ++) {  
  9.    
  10.     sprintf(hid_dev_node, "/dev/usb/hiddev%d", index);  
  11.     fd = open(hid_dev_node, O_RDONLY);  
  12.    
  13.     if(fd > 0) {  
  14.    
  15.       memset(&dinfo, 0, sizeof(dinfo));  
  16.       ioctl(fd, HIDIOCGDEVINFO, &dinfo);  
  17.    
  18.       if( (dinfo.vendor == 0x0e20) && (dinfo.product == 0x0101))  
  19.         break;  
  20.    
  21.       close(fd);  
  22.       fd = -1;  
  23.    
  24.     }  
  25.   }  
  26.    
  27.   return fd;  
  28. }  

打开函数中通过对USB的VID和PID信息来确认所打开的设备是否为数码笔(可通过以下命令查到设备的VID和PID)。如打开成功,返回的是数码笔设备的文件描述符。

[cpp] view plaincopyprint?
  1. hfhe@hfhe:~$ lsusb  
  2. Bus 003 Device 003: ID 0e20:0101 Pegasus Technologies Ltd. NoteTaker  
  3. Bus 003 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub  
  4. Bus 002 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub  
  5. Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub  
  6. Bus 001 Device 008: ID 04b3:3107 IBM Corp. ThinkPad 800dpi Optical Travel Mouse  
  7. Bus 001 Device 006: ID 04f2:b217 Chicony Electronics Co., Ltd  
  8. Bus 001 Device 005: ID 0a5c:217f Broadcom Corp. Bluetooth Controller  
  9. Bus 001 Device 004: ID 147e:2016 Upek Biometric Touchchip/Touchstrip Fingerprint Sensor  
  10. Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub  
  11. Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub  

2. 获得数码笔的配置信息

在正式读写数据前,先获得数码笔设备的配置信息。我从Google上没有搜到较具体描述获知hiddev设备配置信息的文档,只能看Kernel中的代码和前面提及到的hiddev.txt文件,在此将与此相关的IOCTL命令整理如下:

  • HIDIOCGNAME – 获得设备名称
  • HIDIOCGDEVINFO – 获得由结构体hiddev_devinfo描述的设备信息
  • HIDIOCGCOLLECTIONINFO – 获得由结构体hiddev_collection_info描述的设备所拥有的集合(包括应用集合),调用时需传入index域,如index超过最后一 个集合,返回值为-1。
  • HIDIOCGREPORTINFO – 调用时需填写结构体hiddev_report_info中的type和id域。当ID为HID_REPORT_ID_FIRST传回第一个报告信息,(HID_REPORT_ID_NEXT |report_id)传回下一个报告信息;而类型值在HID_REPORT_TYPE_MIN和HID_REPORT_TYPE_MAX间。
  • HIDIOCGFIELDINFO – 返回报告中的域(filed)信息。调用时需传入报告的id和type, 也要填入field index,filed的数目从调用 HIDIOCGREPORTINFO的返回结构体中获得。
  • HIDIOCGUCODE – 返回用途码,调用时需传入报告的id和type、域的index和用途的索引(最大用途索引值在调用HIDIOCGFIELDINFO的返回结构体中获得)

可见,除HIDIOCGNAME和HIDIOCGDEVINFO调用外,其他的均是枚举过程。相关结构体的定义就不附上了,下面直接给出获得配置信息的代码。

[cpp] view plaincopyprint?
  1. int digitalpen_getconfiginfo(int fd)  
  2. {  
  3.   char name[1024];  
  4.   int  index;  
  5.   int  report_type,report_id,field_index,usage_index;  
  6.    
  7.   struct hiddev_devinfo dinfo;  
  8.   struct hiddev_collection_info cinfo;  
  9.   struct hiddev_report_info rinfo;  
  10.   struct hiddev_field_info finfo;  
  11.   struct hiddev_usage_ref uref;  
  12.    
  13.   const char *collection_type_name[] = {"Physical""Application""Logical"};  
  14.   const char *report_type_name[] = {"Input""Output""Feature"};  
  15.    
  16.   // get device name   
  17.   memset(name, 0, sizeof(name));  
  18.   if( ioctl(fd, HIDIOCGNAME(sizeof(name)), name) < 0) {;  
  19.     return -1;  
  20.   }  
  21.    
  22.   // get device info   
  23.   memset(&dinfo, 0, sizeof(dinfo));  
  24.   if( ioctl(fd, HIDIOCGDEVINFO, &dinfo) < 0) {  
  25.     return -1;  
  26.   }  
  27.    
  28.   printf("\n[Device Information]\n");  
  29.   printf("device name      = %s\n", name);  
  30.   printf("vendor id        = 0x%04x\n", dinfo.vendor & 0xffff);  
  31.   printf("product id       = 0x%04x\n", dinfo.product & 0xffff);  
  32.   printf("version          = 0x%04x\n", dinfo.version & 0xffff);  
  33.   printf("num_applications = 0x%x\n",   dinfo.num_applications);  
  34.    
  35.   // get collection info   
  36.   printf("\n[Collections Information]\n");  
  37.    
  38.   index = 0;  
  39.   while(1) {  
  40.     memset(&cinfo, 0, sizeof(cinfo));  
  41.     cinfo.index = index;  
  42.    
  43.     if(ioctl(fd, HIDIOCGCOLLECTIONINFO, &cinfo) < 0)  
  44.       break;  
  45.     index++;  
  46.    
  47.     printf("index = %d\n", cinfo.index);  
  48.     if(cinfo.type >= 0 && cinfo.type <= 2)  
  49.       printf("type  = %s\n", collection_type_name[cinfo.type]);  
  50.     else  
  51.       printf("type  = %d\n", cinfo.type);  
  52.     printf("usage = 0x%x\n", cinfo.usage);  
  53.     printf("level = %d\n\n", cinfo.level);  
  54.   }  
  55.    
  56.   // get reports info   
  57.   printf("[Report Information]\n");  
  58.   for(report_type = HID_REPORT_TYPE_MIN; report_type <= HID_REPORT_TYPE_MAX; report_type++) {  
  59.    
  60.     for(report_id = HID_REPORT_ID_FIRST; ;report_id++) {  
  61.       memset(&rinfo, 0, sizeof(rinfo));  
  62.       rinfo.report_type = report_type;  
  63.       rinfo.report_id = report_id;  
  64.       if(ioctl(fd, HIDIOCGREPORTINFO, &rinfo) < 0)  
  65.         break;  
  66.    
  67.       printf("report_type = %s\n", report_type_name[report_type - 1]);  
  68.       printf("report_id   = %d\n", report_id);  
  69.       printf("num_fields  = %d\n", rinfo.num_fields);  
  70.    
  71.       // get field info   
  72.       printf("\n    [field information]\n");  
  73.       for(field_index = 0; field_index < rinfo.num_fields; field_index++) {  
  74.         memset(&finfo, 0, sizeof(finfo));  
  75.         finfo.report_type = report_type;  
  76.         finfo.report_id   = report_id;  
  77.         finfo.field_index = field_index;  
  78.    
  79.         if(ioctl(fd, HIDIOCGFIELDINFO, &finfo) < 0)  
  80.           break;  
  81.    
  82.         printf("    field_index   = %d\n",   finfo.field_index);  
  83.         printf("    maxusage      = %d\n",   finfo.maxusage);  
  84.         printf("    flags         = %d\n",   finfo.flags);  
  85.         printf("    physical      = %d\n",   finfo.physical);  
  86.         printf("    logical       = %d\n",   finfo.logical);  
  87.         printf("    application   = 0x%x\n", finfo.application);  
  88.         printf("    unit_exponent = %d\n",   finfo.unit_exponent);  
  89.         printf("    unit          = %d\n",   finfo.unit);  
  90.         printf("    logical_minimum  = %d; logical_maximum  = %d\n", finfo.logical_minimum, finfo.logical_maximum);  
  91.         printf("    physical_minimum = %d; physical_maximum = %d\n", finfo.physical_minimum, finfo.physical_maximum);  
  92.    
  93.         // get usage info   
  94.         printf("\n        [Usage code information]\n");  
  95.         for(usage_index = 0; usage_index < finfo.maxusage; usage_index++) {  
  96.           memset(&uref, 0, sizeof(uref));  
  97.           uref.report_type = report_type;  
  98.           uref.report_id   = report_id;  
  99.           uref.field_index = field_index;  
  100.           uref.usage_index = usage_index;  
  101.           if(ioctl(fd, HIDIOCGUCODE, &uref) < 0)  
  102.             break;  
  103.    
  104.           printf("        usage_index = %d, usage_code = 0x%x, value = %d\n",  uref.usage_index, uref.usage_code, uref.value);  
  105.         }  
  106.       }  
  107.    
  108.       report_id = report_id | HID_REPORT_ID_NEXT;  
  109.       printf("\n");  
  110.    
  111.     }  
  112.   }  
  113. }  

运行后输出结果为:

[cpp] view plaincopyprint?
  1. [Device Information]  
  2. device name      = Pegasus Technologies Ltd. EN202 Ver 3.02  
  3. vendor id        = 0x0e20  
  4. product id       = 0x0101  
  5. version          = 0x0303  
  6. num_applications = 0x1  
  7.    
  8. [Collections Information]  
  9. index = 0  
  10. type  = Application  
  11. usage = 0xffa00001  
  12. level = 0  
  13.    
  14. [Report Information]  
  15. report_type = Input  
  16. report_id   = 256  
  17. num_fields  = 1  
  18.    
  19.     [field information]  
  20.     field_index   = 0  
  21.     maxusage      = 64  
  22.     flags         = 2  
  23.     physical      = 0  
  24.     logical       = 0  
  25.     application   = 0xffa00001  
  26.     unit_exponent = 0  
  27.     unit          = 0  
  28.     logical_minimum  = -128; logical_maximum  = 127  
  29.     physical_minimum = 0; physical_maximum = 0  
  30.    
  31.         [Usage code information]  
  32.         usage_index = 0, usage_code = 0xffa00002, value = 0  
  33.         usage_index = 1, usage_code = 0xffa00002, value = 0  
  34.         usage_index = 2, usage_code = 0xffa00002, value = 0  
  35.         usage_index = 3, usage_code = 0xffa00002, value = 0  
  36.         usage_index = 4, usage_code = 0xffa00002, value = 0  
  37.         usage_index = 5, usage_code = 0xffa00002, value = 0  
  38.         usage_index = 6, usage_code = 0xffa00002, value = 0  
  39.         usage_index = 7, usage_code = 0xffa00002, value = 0  
  40.         usage_index = 8, usage_code = 0xffa00002, value = 0  
  41.         usage_index = 9, usage_code = 0xffa00002, value = 0  
  42.         usage_index = 10, usage_code = 0xffa00002, value = 0  
  43.         usage_index = 11, usage_code = 0xffa00002, value = 0  
  44.         usage_index = 12, usage_code = 0xffa00002, value = 0  
  45.         usage_index = 13, usage_code = 0xffa00002, value = 0  
  46.         usage_index = 14, usage_code = 0xffa00002, value = 0  
  47.         usage_index = 15, usage_code = 0xffa00002, value = 0  
  48.         usage_index = 16, usage_code = 0xffa00002, value = 0  
  49.         usage_index = 17, usage_code = 0xffa00002, value = 0  
  50.         usage_index = 18, usage_code = 0xffa00002, value = 0  
  51.         usage_index = 19, usage_code = 0xffa00002, value = 0  
  52.         usage_index = 20, usage_code = 0xffa00002, value = 0  
  53.         usage_index = 21, usage_code = 0xffa00002, value = 0  
  54.         usage_index = 22, usage_code = 0xffa00002, value = 0  
  55.         usage_index = 23, usage_code = 0xffa00002, value = 0  
  56.         usage_index = 24, usage_code = 0xffa00002, value = 0  
  57.         usage_index = 25, usage_code = 0xffa00002, value = 0  
  58.         usage_index = 26, usage_code = 0xffa00002, value = 0  
  59.         usage_index = 27, usage_code = 0xffa00002, value = 0  
  60.         usage_index = 28, usage_code = 0xffa00002, value = 0  
  61.         usage_index = 29, usage_code = 0xffa00002, value = 0  
  62.         usage_index = 30, usage_code = 0xffa00002, value = 0  
  63.         usage_index = 31, usage_code = 0xffa00002, value = 0  
  64.         usage_index = 32, usage_code = 0xffa00002, value = 0  
  65.         usage_index = 33, usage_code = 0xffa00002, value = 0  
  66.         usage_index = 34, usage_code = 0xffa00002, value = 0  
  67.         usage_index = 35, usage_code = 0xffa00002, value = 0  
  68.         usage_index = 36, usage_code = 0xffa00002, value = 0  
  69.         usage_index = 37, usage_code = 0xffa00002, value = 0  
  70.         usage_index = 38, usage_code = 0xffa00002, value = 0  
  71.         usage_index = 39, usage_code = 0xffa00002, value = 0  
  72.         usage_index = 40, usage_code = 0xffa00002, value = 0  
  73.         usage_index = 41, usage_code = 0xffa00002, value = 0  
  74.         usage_index = 42, usage_code = 0xffa00002, value = 0  
  75.         usage_index = 43, usage_code = 0xffa00002, value = 0  
  76.         usage_index = 44, usage_code = 0xffa00002, value = 0  
  77.         usage_index = 45, usage_code = 0xffa00002, value = 0  
  78.         usage_index = 46, usage_code = 0xffa00002, value = 0  
  79.         usage_index = 47, usage_code = 0xffa00002, value = 0  
  80.         usage_index = 48, usage_code = 0xffa00002, value = 0  
  81.         usage_index = 49, usage_code = 0xffa00002, value = 0  
  82.         usage_index = 50, usage_code = 0xffa00002, value = 0  
  83.         usage_index = 51, usage_code = 0xffa00002, value = 0  
  84.         usage_index = 52, usage_code = 0xffa00002, value = 0  
  85.         usage_index = 53, usage_code = 0xffa00002, value = 0  
  86.         usage_index = 54, usage_code = 0xffa00002, value = 0  
  87.         usage_index = 55, usage_code = 0xffa00002, value = 0  
  88.         usage_index = 56, usage_code = 0xffa00002, value = 0  
  89.         usage_index = 57, usage_code = 0xffa00002, value = 0  
  90.         usage_index = 58, usage_code = 0xffa00002, value = 0  
  91.         usage_index = 59, usage_code = 0xffa00002, value = 0  
  92.         usage_index = 60, usage_code = 0xffa00002, value = 0  
  93.         usage_index = 61, usage_code = 0xffa00002, value = 0  
  94.         usage_index = 62, usage_code = 0xffa00002, value = 0  
  95.         usage_index = 63, usage_code = 0xffa00002, value = 0  
  96.    
  97. report_type = Output  
  98. report_id   = 256  
  99. num_fields  = 1  
  100.    
  101.     [field information]  
  102.     field_index   = 0  
  103.     maxusage      = 8  
  104.     flags         = 8  
  105.     physical      = 0  
  106.     logical       = 0  
  107.     application   = 0xffa00001  
  108.     unit_exponent = 0  
  109.     unit          = 0  
  110.     logical_minimum  = -128; logical_maximum  = 127  
  111.     physical_minimum = 0; physical_maximum = 255  
  112.    
  113.         [Usage code information]  
  114.         usage_index = 0, usage_code = 0xffa00003, value = 0  
  115.         usage_index = 1, usage_code = 0xffa00003, value = 0  
  116.         usage_index = 2, usage_code = 0xffa00003, value = 0  
  117.         usage_index = 3, usage_code = 0xffa00003, value = 0  
  118.         usage_index = 4, usage_code = 0xffa00003, value = 0  
  119.         usage_index = 5, usage_code = 0xffa00003, value = 0  
  120.         usage_index = 6, usage_code = 0xffa00003, value = 0  
  121.         usage_index = 7, usage_code = 0xffa00003, value = 0  

3. 向数码笔设备写入数据

与hiddev设备进行数据交换不是通过read和write这两个函数来完成的,而是通过调用IOCTL(命令字为HIDIOCGREPORT和HIDIOCSREPORT)并将传入传出的数据封装在report中来实现的。

写数据比较容易实现,根据hiddev.txt中的描述,就是在发出报告之间先通过调用HIDIOCSUSAGE(传给IOCTL)来填写将要传送的数据,其后调用HIDIOCSREPORT 让Linux内核发报告给设备。

[cpp] view plaincopyprint?
  1. int hiddev_send_report(int fd, unsigned int report_id, unsigned int num_fields, unsigned int field_index, unsigned int usage_index, unsigned int usage_code, char *buf, unsigned int size)  
  2. {  
  3.   unsigned int index;  
  4.   struct hiddev_usage_ref_multi urefm;  
  5.   struct hiddev_report_info rinfo;  
  6.    
  7.   if(size > HID_MAX_MULTI_USAGES)  
  8.     return -1;  
  9.    
  10.   memset(&urefm, 0, sizeof(urefm));  
  11.   urefm.uref.report_type = HID_REPORT_TYPE_OUTPUT;  
  12.   urefm.uref.report_id = report_id;  
  13.   urefm.uref.field_index = field_index;  
  14.   urefm.uref.usage_index = usage_index;  
  15.   urefm.uref.usage_code = usage_code;  
  16.    
  17.   urefm.num_values = size;  
  18.   for(index = 0; index < size; index++)  
  19.     urefm.values[index] = (unsigned int)buf[index];  
  20.    
  21.   if(ioctl(fd, HIDIOCSUSAGES, &urefm) < 0)  
  22.     return -2;  
  23.    
  24.   memset(&rinfo, 0, sizeof(rinfo));  
  25.   rinfo.report_type = HID_REPORT_TYPE_OUTPUT;  
  26.   rinfo.report_id = report_id;  
  27.   rinfo.num_fields = num_fields;  
  28.   if(ioctl(fd, HIDIOCSREPORT, &rinfo) < 0)  
  29.     return -3;  
  30.    
  31.   return 0;  
  32. }  

调用时,要用到2小节中获取的一些参数,比较好的作法是将这些参数放进全司结构体中。当然,为图方便也可直接填入获取到的参数,例如要将数码笔切换至原始坐标输出模式,通过下面的调用即实现。

[cpp] view plaincopyprint?
  1. const char cmd_opmode_pen[]   = { 0x02, 0x04, 0x80, 0xB5, 0x01, 0x01, 0x00, 0x00};  
  2.    
  3. if(hiddev_send_report(fd, HID_REPORT_ID_FIRST, 1, 0, 0, 0xffa00003, cmd_opmode_pen, sizeof(cmd_opmode_pen)) < 0)  
  4.     printf("Fail to send report\n");  

通过所获得的配置信息可知,报告类型为输出模式时,report_id的值为256,即为宏HID_REPORT_ID_FIRST所定义的值。