一.测试中断或者Bulk传输:
首先要使用Libusb打印出HID设备的Endpoint查看是否支持中断或者Bulk传输模式;如果支持的话才可以进一步测试;
因为HID设备在插入的时候无需安装,并且一般会被OS直接占用,所以如果直接使用Interrupt传输(通常只有一个断点)会发生超时
所以建议使用zadig给对应的HID设备安装WinUSB来防止直接被占用,然后就可以收到数据了:
对于收到的数据该如何解析(以及如果想要修改Device来发送不同的字母)可以查看:HID键盘对照表 和 字母的ASCII码表;
这是我的一部分解析数据的代码(传入参数就是接收缓冲区的首地址指针)
1 void parse_mouse_data(char* data) 2 { 3 char c1 = *data; 4 char c2 = *(data + 1); 5 char c3 = *(data + 2); 6 char c4 = *(data + 3); 7 if (c1 & LEFT_PRESS) 8 printf("Left key press\n"); 9 if (c1 & RIGHT_PRESS) 10 printf("Right Key Press\n"); 11 if (c1 & MIDLE_PRESS) 12 printf("Middle Key Press\n"); 13 if (c2 & LEFT_SIDE) 14 printf("Go Left : %d\n", (c2 & 0x7f)); 15 else 16 printf("Go Right : %d\n", (c2 & 0x7f)); 17 if (c3 & DOWN_SIDE) 18 printf("Go Down : %d\n", (c3 & 0x7f)); 19 else 20 printf("Go Up : %d\n", (c3 & 0x7f)); 21 } 22 void parse_keyboard_data(char* data) 23 { 24 char temp1 = *data; 25 if (temp1 & (0x1 << 0)) 26 printf("Left Control Presed"); 27 if (temp1 & (0x1 << 1)) 28 printf("Left Shift Pressed\n"); 29 if (temp1 & (0x1 << 2)) 30 printf("Left Alt Pressed\n"); 31 if (temp1 & (0x1 << 4)) 32 printf("Right Control Pressed\n"); 33 if (temp1 & (0x1 << 5)) 34 printf("Right Shift Pressed\n"); 35 if (temp1 & (0x1 << 6)) 36 printf("Right Alt Pressed\n"); 37 char alpha = data + 3; 38 printf("input: %c\n", (char)((int)alpha + ASCII_LOWERCASE_SUB)); 39 }
二.如果失败了之后之后测试就会一直失败的问题:
查找资料:如果不是EP0端点传输时,Device收到了不支持或者无效的请求,对应的EP将会在data或者status阶段返回STALL故障 —— 需要Host的EP0发送clear Halt指令才能重新启用这个EP( 也就是需要使用Libusb提供的clear_halt函数)
三. HID report
这是标志一个设备是HID的重要方式:report数组:
注意要区分:报告和报告描述符:前者是用来传输数据(在get report和set report命令,控制EP中传输)后者是对数据用途的说明,是一个数组;
可以根据 HID设备描述符的官方文档 来写自己的HID report array;当然更方便的是结合使用官方提供的 Dt工具 生成更保险;
是我对device,interface,endpoint的修改:
1 /* 2 * for maxPakcetSize0 for device descriptor 3 * it means the mas packet size for endpoint 0 4 * for low: 8; for full: 8 or 16, 32, 65; for high : only can be 64 5 */ 6 device_descriptor{ 7 .bcdUSB = 0x0210, //表示这个是HID设备 8 .bDeviceClass = 0x00, 9 .bDeviceSubClass = 0x00, 10 .bDeviceProtocol = 0x00, 11 } 12 13 /* 14 * bConfiguration Value must >= 0x01 15 * if == 0x00, after set configuration, enter not configured state 16 * bmAttributes in Config desc 17 * for usb2.0 bit7 reserved must 1 18 * bit6 = 1:means self-powered 19 * bit5 = 1:means Remote Wakeup 20 * rest must be 0 21 */ 22 config_descriptor{ 23 .bmAttributes = 0xA0, 24 .bMaxPower = 0x32, 25 } 26 27 /* 28 * interfaceClass: class code : delivered by USB-IF; == bDeviceClass 29 * 0x03 means HID 30 * iInterface == 0 means there is no string descriptor 31 */ 32 interface_descriptor{ 33 .bInterfaceClass = 0x03, 34 .bInterfaceSubClass = 0x00, 35 .bInterfaceProtocol = 0x00, 36 .iInterface = 0x00, 37 } 38 39 /* 40 * for hid descriptor type == 0x21 41 * bDesccriptorType: 0x21 means HID; 42 * bDescriptorType0 : Lower level desciptor : 0x22 means report 43 */ 44 hid_descriptor{ 45 .bDescriptorType = 0x21, 46 .bDescriptorType0 = 0x22, 47 .wDescriptorLength0 = REPORT_DESC_SIZE, 48 } 49 50 // 下面是使用的HID report结构: 51 0x05, 0x0c, // USAGE PAGE 使用 Consumer Devices 这样就不会被OS直接占用,可以进行bulk等传输 52 0x09, 0x01, // USAGE 使用 Consumer Control 53 0xa1, 0x01, // Collection 选择 Application; 54 0x09, 0x00, // USAGE 使用 Unassigned定义自己的report; 55 0xc0 // End of Collection
四. Get. Set report 指令的使用:
这一部分才是使用的精髓:之前使用多个都是中断传输;但是HID设备更常用或者说更应该使用控制节点传输的report命令:
因为前者中断传输算是异步:需要另外开辟一个线程来等待监听;但是控制节点的类似于同步,对于Host来说更方便:
参考:
所以两者分别是 0xa1 0x01 和 0x21 0x09
然后通过控制节点传输的方法就可以传输了
并且注意:如果是对于HID设备传输时候(在Windows中)一定要将buffer的第一个Byte写为在report 数组中龟腚的ReportID(默认是0x00)才能被传输;
使用libusb中的interrupt_transfer如果判断device是hid设备将会转给hid_transfer函数,所以将ReportID设置为第一个字符非常重要。
五. hidapi踩坑:
相比起Libusb:Hidapi并没有caim interface;以及判断是否被占用和clear_halt等命令;对于mouse之类的无能为力;
并且提供的set report函数功能也很有限;不建议使用;但是比较简单。
原文地址:http://www.cnblogs.com/celesial-dancers/p/16876206.html