对报告描述符里主项目input和output的理解(原)
1 code char MouseReportDescriptor[29] = { 2 0x06,0x00,0xFF, //USAGE_PAGE (Vendor Defined Page 1) 3 0x09,0x01, //USAGE (Vendor Usage 1) 4 0xA1,0x01, //COLLECTION (Application) 5 6 0x19,0x01, //(Vendor Usage 1) 7 0x29,0x08, //(Vendor Usage 1) 8 0x15,0x00, //LOGICAL_MINIMUM (0) 9 0x26,0xFF,0x00, //LOGICAL_MAXIMUM (255) 10 0x75,0x08, //REPORT_SIZE (8) 11 0x95,0x40, //REPORT_COUNT (64) 12 0x81,0x02, //INPUT (Data,Var,Abs) 13 14 0x19,0x01, //(Vendor Usage 1) 15 0x29,0x08, //(Vendor Usage 1) 16 0x91,0x02, //OUTPUT (Data,Var,Abs) 17 18 0xC0 // END_COLLECTION 19 };
此报告描述符描述的是厂商自定义设备,定义了64个字节的输入输出数据。
首先是input(Data,Var,Abs),然后是output(Data,Var,Abs)。之前一直很困惑,这里定义了输入输出报告,那到底什么时候发送数据呢?白皮书上也没有解释,我就自己理解为这段报告描述符是告诉系统如果收到input报告的数据,就立即向设备output数据。这一点还在键盘的报告描述符里得到验证。
1 code char MouseReportDescriptor[63] = { 2 //表示用途页为通用桌面设备 3 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 4 5 //表示用途为键盘 6 0x09, 0x06, // USAGE (Keyboard) 7 8 //表示应用集合,必须要以END_COLLECTION来结束它,见最后的END_COLLECTION 9 0xa1, 0x01, // COLLECTION (Application) 10 11 //表示用途页为按键 12 0x05, 0x07, // USAGE_PAGE (Keyboard) 13 14 //用途最小值,这里为左ctrl键 15 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl) 16 //用途最大值,这里为右GUI键,即window键 17 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) 18 //逻辑最小值为0 19 0x15, 0x00, // LOGICAL_MINIMUM (0) 20 //逻辑最大值为1 21 0x25, 0x01, // LOGICAL_MAXIMUM (1) 22 //报告大小(即这个字段的宽度)为1bit,所以前面的逻辑最小值为0,逻辑最大值为1 23 0x75, 0x01, // REPORT_SIZE (1) 24 //报告的个数为8,即总共有8个bits 25 0x95, 0x08, // REPORT_COUNT (8) 26 //输入用,变量,值,绝对值。像键盘这类一般报告绝对值, 27 //而鼠标移动这样的则报告相对值,表示鼠标移动多少 28 0x81, 0x02, // INPUT (Data,Var,Abs) 29 //上面这这几项描述了一个输入用的字段,总共为8个bits,每个bit表示一个按键 30 //分别从左ctrl键到右GUI键。这8个bits刚好构成一个字节,它位于报告的第一个字节。 31 //它的最低位,即bit-0对应着左ctrl键,如果返回的数据该位为1,则表示左ctrl键被按下, 32 //否则,左ctrl键没有按下。最高位,即bit-7表示右GUI键的按下情况。中间的几个位, 33 //需要根据HID协议中规定的用途页表(HID Usage Tables)来确定。这里通常用来表示 34 //特殊键,例如ctrl,shift,del键等 35 36 37 //这样的数据段个数为1 38 0x95, 0x01, // REPORT_COUNT (1) 39 //每个段长度为8bits 40 0x75, 0x08, // REPORT_SIZE (8) 41 //输入用,常量,值,绝对值 42 0x81, 0x03, // INPUT (Cnst,Var,Abs) 43 44 //上面这8个bit是常量,设备必须返回0 45 46 47 //这样的数据段个数为5 48 0x95, 0x05, // REPORT_COUNT (5) 49 //每个段大小为1bit 50 0x75, 0x01, // REPORT_SIZE (1) 51 //用途是LED,即用来控制键盘上的LED用的,因此下面会说明它是输出用 52 0x05, 0x08, // USAGE_PAGE (LEDs) 53 //用途最小值是Num Lock,即数字键锁定灯 54 0x19, 0x01, // USAGE_MINIMUM (Num Lock) 55 //用途最大值是Kana,这个是什么灯我也不清楚^_^ 56 0x29, 0x05, // USAGE_MAXIMUM (Kana) 57 //如前面所说,这个字段是输出用的,用来控制LED。变量,值,绝对值。 58 //1表示灯亮,0表示灯灭 59 0x91, 0x02, // OUTPUT (Data,Var,Abs) 60 61 //这样的数据段个数为1 62 0x95, 0x01, // REPORT_COUNT (1) 63 //每个段大小为3bits 64 0x75, 0x03, // REPORT_SIZE (3) 65 //输出用,常量,值,绝对 66 0x91, 0x03, // OUTPUT (Cnst,Var,Abs) 67 //由于要按字节对齐,而前面控制LED的只用了5个bit, 68 //所以后面需要附加3个不用bit,设置为常量。 69 70 //报告个数为6 71 0x95, 0x06, // REPORT_COUNT (6) 72 //每个段大小为8bits 73 0x75, 0x08, // REPORT_SIZE (8) 74 //逻辑最小值0 75 0x15, 0x00, // LOGICAL_MINIMUM (0) 76 //逻辑最大值255 77 0x25, 0xFF, // LOGICAL_MAXIMUM (255) 78 //用途页为按键 79 0x05, 0x07, // USAGE_PAGE (Keyboard) 80 //使用最小值为0 81 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) 82 //使用最大值为0x65 83 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application) 84 //输入用,变量,数组,绝对值 85 0x81, 0x00, // INPUT (Data,Ary,Abs) 86 //以上定义了6个8bit宽的数组,每个8bit(即一个字节)用来表示一个按键,所以可以同时 87 //有6个按键按下。没有按键按下时,全部返回0。如果按下的键太多,导致键盘扫描系统 88 //无法区分按键时,则全部返回0x01,即6个0x01。如果有一个键按下,则这6个字节中的第一 89 //个字节为相应的键值(具体的值参看HID Usage Tables),如果两个键按下,则第1、2两个 90 //字节分别为相应的键值,以次类推。 91 92 //关集合,跟上面的对应 93 0xc0 // END_COLLECTION 94 };
当按下Num Lock键时,PC会发送输出报告,从报告描述符中我们知道,Num Lock的LED对应着输出报告的最低位,
当数字小键盘打开时,输出xxxxxxx1(二进制,打x的由其它的LED状态决定);
当数字小键盘关闭时,输出xxxxxxx0(同前)。取出最低位就可以控制数字键锁定LED了。
用Bus Hound软件可捕捉到输入输出数据。
后来才发现自己的理解是错的。报告描述符里只规定了报告的格式,即每个字节,每个位是表示什么意思,没有说明输入输出的时间,这中间不存在顺序关系。键盘的报告描述符里,output报告规定了哪一个位对应哪一个LED灯的状态,并没有说设备产生一个Input报告后,系统一定会有一个Output报告出来。Output报告的产生是要看按键有没有改变LED灯的状态了,如果改变了,则有output报告输出,如果按键不影响LED灯的状态,系统也没有必要输出output报告。
当然了,这只是我的理解,有不同意见,欢迎交流。