我要做一个 包含两个接口 (HID键盘 + 自定义接口)的设备
刚开始我用的是圈圈的代码.主要遇到了两个问题
1. HID键盘无法识别.这个调试了我好久,一度以为是芯片坏了/时序没弄正确
2.HID键盘识别出来了,但是把接口直接添加到配置描述符识别不了. 用busbound没装驱动当然识别不出来,windows驱动不会写,但是用linux驱动也识别不出来
第一个虽然调了久,但是解决也好简单..我用busbound抓包了手上的usb键盘的所有描述符,看来看去只有hid描述符有问题.替换到圈圈的HID键盘代码去就可以了.完整的描述符见最下面.至于为什么圈圈那个经过这么多人验证过的描述符在我的机子上用不了我也不明白..看了些HID描述符的文档,太复杂了大脑内存不够就不看了
第二个是在网上找到解决办法的:
下面节选 patch
+ /*On this combined interface, you cannot apply boot device.(bInterfaceClass, bInterfaceSubclass, bInterfaceProtocol)= 03,0,0*/
+
//bInterfaceClass字段。该接口所使用的类。USB键盘是HID类,
//HID类的编码为0x03。
0x03,
//bInterfaceSubClass字段。该接口所使用的子类。在HID1.1协议中,
//只规定了一种子类:支持BIOS引导启动的子类。
//USB键盘、鼠标属于该子类,子类代码为0x01。
- 0x01,
- //bInterfaceProtocol字段。如果子类为支持引导启动的子类,
- //则协议可选择鼠标和键盘。键盘代码为0x01,鼠标代码为0x02。
- 0x01,
+0,//@wei0x01,
+//bInterfaceProtocol字段。如果子类为支持引导启动的子类,
+//则协议可选择鼠标和键盘。键盘代码为0x01,鼠标代码为0x02。
+0,//@wei 0x01,
链接没记.主要是这句话.
On this combined interface, you cannot apply boot device.(bInterfaceClass, bInterfaceSubclass, bInterfaceProtocol)= 03,0,0
原话在国外的论坛上出现.但是我找了整个usb文档包都找不到相关说明..
T.T
上面简简单单几句话就是我差不多一个暑假才弄出来的..(当然别的事情也有耽搁,以后还是一段时间集中做某件事好了)
附上沾满泪水的描述符:
//USB设备描述符的定义
uint8 DeviceDescriptor[0x12] = //设备描述符为18字节
{
//bLength字段。设备描述符的长度为18(0x12)字节
0x12,
//bDescriptorType字段。设备描述符的编号为0x01
0x01,
//bcdUSB字段。这里设置版本为USB1.1,即0x0110。
//由于是小端结构,所以低字节在先,即0x10,0x01。
0x00,0x02,//0x10, 0x01,//
//bDeviceClass字段。用户自定义USB设备,代码为0xFF @wei 必须使用2.0?
0x00,//0xFF, @wei
//0xef,//each interface defien the device class IAD class
/*each interface within a configuration specifies its own class information and the various
interfaces operate independently. 每个interface自己指配class */
//bDeviceSubClass字段。为0,没有规定子类。
0x00,
//0x02,
//bDeviceProtocol字段。为0,没有规定协议。
0x00,
//0x01,
//bMaxPacketSize0字段。PDIUSBD12的端点0大小的16字节。 //@wei 端点0 大小为0x08
0x08,//0x40,//0x08,//@wei 原 orginal 0x10
//idVender字段。厂商ID号,我们这里取0x8888,仅供实验用。
0x88, 0x88,
//idProduct字段。产品ID号,由于是第11个实验,我们这里取0x000B。
//注意小端模式,低字节应该在前。
0x0b, 0x00, //@wei
//bcdDevice字段。设备版本号,取1.0版,即0x0100。
0x00,0x02,//0x00, 0x01,//0x10, 0x01, //@wei oringal 0x00 0x01
//iManufacturer字段。厂商字符串的索引值,为了方便记忆和管理,
//字符串索引就从1开始吧。
0x01,
//iProduct字段。产品字符串的索引值。刚刚用了1,这里就取2吧。
//注意字符串索引值不要使用相同的值。
0x02,
//iSerialNumber字段。设备的序列号字符串索引值。
//这里取3就可以了。 //@wei 填3 识别不到keyboard ;0 指向语言ID字符串
//成品 log里 这个值也是0
0x03,//0x00,
//bNumConfigurations字段。该设备所具有的配置数。
//我们只需要一种配置就行了,因此该值设置为1。
0x01 };
//设备描述符完毕//
//USB报告描述符的定义
//更详细的说明请参看USB HID协议,该协议可从Http://www.usb.org下载。
uint8 ReportDescriptor[] = {
0x05,0x01,0x09,0x06,0xa1,0x01 ,0x05 ,0x08,
0x19,0x01,0x29,0x03,0x15,0x00 ,0x25 ,0x01,
0x75,0x01,0x95,0x03,0x91,0x02 ,0x95 ,0x05,
0x91,0x01,0x05,0x07,0x19,0xe0 ,0x29 ,0xe7,
0x95,0x08,0x81,0x02,0x75,0x08 ,0x95 ,0x01,
0x81,0x01,0x19,0x00,0x29,0x91 ,0x26 ,0xff,
0x00,0x95,0x06,0x81,0x00,0xc0
};
//通过上面的报告描述符的定义,我们知道返回的输入报告具有8字节。
//第一字节的8个bit用来表示特殊键是否按下(例如Shift、Alt等键)。
//第二字节为保留值,值为常量0。第三到第八字节是一个普通键键值的
//数组,当没有键按下时,全部6个字节值都为0。当只有一个普通键按下时,
//这六个字节中的第一字节值即为该按键的键值(具体的键值请看HID的
//用途表文档),当有多个普通键同时按下时,则同时返回这些键的键值。
//如果按下的键太多,则这六个字节都为0xFF(不能返回0x00,这样会让
//操作系统认为所有键都已经释放)。至于键值在数组中的先后顺序是
//无所谓的,操作系统会负责检查是否有新键按下。我们应该在中断端点1 @wei 是HID协议约定通过端点1吗?
//中按照上面的格式返回实际的键盘数据。另外,报告中还定义了一个字节
//的输出报告,是用来控制LED情况的。只使用了低7位,高1位是保留值0。
//当某位的值为1时,则表示对应的LED要点亮。操作系统会负责同步各个
//键盘之间的LED,例如你有两块键盘,一块的数字键盘灯亮时,另一块
//也会跟着亮。键盘本身不需要判断各种LED应该何时亮,它只是等待主机
//发送报告给它,然后根据报告值来点亮相应的LED。我们在端点1输出中断
//中读出这1字节的输出报告,然后对它取反(因为学习板上的LED是低电平时
//亮),直接发送到LED上。这样main函数中按键点亮LED的代码就不需要了。
///报告描述符完毕
//USB配置描述符集合的定义
//配置描述符总长度为9+9+7+7+7+7字节//9 + /*9 + 7 + 7 + 7 + 7 +*/ (9 + 9 + 7 + 7)
#define IAD_DESCRIPTOR_LENGTH 8
uint8 ConfigurationDescriptor[9
//+IAD_DESCRIPTOR_LENGTH*2
+(9+9+7+7 )
+(9+7+7 +7
//+7+7
)] = {
//bLength字段。配置描述符的长度为9字节。
0x09,
//bDescriptorType字段。配置描述符编号为0x02。
0x02,
//wTotalLength字段。配置描述符集合的总长度,
//包括配置描述符本身、接口描述符、类描述符、端点描述符等。
(uint8)sizeof(ConfigurationDescriptor) & 0xFF, //低字节
(sizeof(ConfigurationDescriptor) >> 8) & 0xFF, //高字节
//bNumInterfaces字段。该配置包含的接口数,
0x02,
//bConfiguration字段。该配置的值为1。
0x01,
//iConfigurationz字段,该配置的字符串索引。这里没有,为0。
0x00,
//bmAttributes字段,该设备的属性。由于我们的板子是总线供电的,
//并且我们不想实现远程唤醒的功能,所以该字段的值为0x80。
0x80,
//bMaxPower字段,该设备需要的最大电流量。由于我们的板子
//需要的电流不到100mA,因此我们这里设置为100mA。由于每单位
//电流为2mA,所以这里设置为50(0x32)。
//@wei 这里待修改
0x32,//0xc8,//0x32,
/****************************接口描述符 EPD******************************/
//IAD-EPD:
/*
IAD_DESCRIPTOR_LENGTH, //.bLength =
0x0b, //.bDescriptorType =
0x00, //.bFirstInterface =
0x01, //.bInterfaceCount =
0x08,//0xff,//0x0e, //.bFunctionClass = vendor defined
0x06,//0,//0x03, //.bFunctionSubClass
0x50,//0, //.bFunctionProtocol
0,//0x04, //.iFunction 描述字符串索引 //在 usb_desc.c 中 value 0x02; why??
*/
//bLength字段。接口描述符的长度为9字节。
0x09,
//bDescriptorType字段。接口描述符的编号为0x04。
0x04,
//bInterfaceNumber字段。该接口的编号,第一个接口,编号为0。
INTERFACE_INDEX_EPD,
//bAlternateSetting字段。该接口的备用编号,为0。
0x00,
//bNumEndpoints字段。非0端点的数目。本实例需要四个端点,因此该值为4。@wei
0x03,
//@wei 暂时只使用两个
//bInterfaceClass字段。该接口所使用的类,未指定。
//@wei 复合设备在这里指定
0xff,//0x00,0x08,//
//bInterfaceSubClass字段。该接口所使用的子类,未指定
//@wei 子类
0x00,//0x06,//
//bInterfaceProtocol字段。该接口所使用的协议,未指定。
//@wei 子类
0x00,//0x50,//
//iConfiguration字段。该接口的字符串索引值。这里没有,为0。
0x00,
/**********************中断输出端点描述符***********************/
//bLength字段。端点描述符长度为7字节。
0x07,
//bDescriptorType字段。端点描述符编号为0x05。
0x05,
//bEndpointAddress字段。端点的地址。我们使用D12的输出端点1。
//D7位表示数据方向,输出端点D7为0。所以输出端点1的地址为0x01。
0x01,
//bmAttributes字段。D1~D0为端点传输类型选择。
//该端点为中断端点。中断端点的编号为3。其它位保留为0。
0x03,
//wMaxPacketSize字段。该端点的最大包长。端点1的最大包长为16字节。
//注意低字节在先。
0x10, 0x00,
//bInterval字段。端点查询的时间,我们设置为10个帧时间,即10ms。
0x0A,
/************** 批量输入端点2描述符 *****************/
//bLength字段。端点描述符长度为7字节。
0x07,
//bDescriptorType字段。端点描述符编号为0x05。
0x05,
//bEndpointAddress字段。端点的地址。我们使用D12的输入端点2。
//D7位表示数据方向,输入端点D7为1。所以输入端点2的地址为0x82。
0x82,
//bmAttributes字段。D1~D0为端点传输类型选择。
//该端点为批量端点,批量端点的编号为0x02。其它位保留为0。
0x02,
//wMaxPacketSize字段。该端点的最大包长。端点2的最大包长为64字节。
0x40,
0x00,
//bInterval字段。端点查询的时间,这里对批量端点无效。
0x00,
/************** 批量输出端点2描述符 *****************/
//bLength字段。端点描述符长度为7字节。
0x07,
//bDescriptorType字段。端点描述符编号为0x05。
0x05,
//bEndpointAddress字段。端点的地址。我们使用D12的输出端点2。
//D7位表示数据方向,输出端点D7为0。所以输出端点2的地址为0x02。
//@wei 就这里跟上一个描述符不同
0x02,
//bmAttributes字段。D1~D0为端点传输类型选择。
//该端点为批量端点,批量端点的编号为0x02。其它位保留为0。
0x02,
//wMaxPacketSize字段。该端点的最大包长。端点2的最大包长为64字节。
//注意低字节在先。
0x40,
0x00,
//bInterval字段。端点查询的时间,这里对批量端点无效。
0x00,
//@wei add
/***************************接口描述符 HID******************************/
//IAD-HID:
/*
0x08,// .bLength =
0x0b, // .bDescriptorType =
0x01, //.bFirstInterface =
0x01, //.bInterfaceCount =
0x03,//0x0e, //.bFunctionClass =
0x01,//0x03, //.bFunctionSubClass
0x01, //.bFunctionProtocol
0,//0x04, //.iFunction 描述字符串索引 //如何指示??
*/
//bLength字段。接口描述符的长度为9字节。
0x09,
//bDescriptorType字段。接口描述符的编号为0x04。
0x04,
//bInterfaceNumber字段。该接口的编号,第er个接口,编号为01。
INTERFACE_INDEX_HID,
//bAlternateSetting字段。该接口的备用编号,为0。
0x00,
//bNumEndpoints字段。非0端点的数目。该USB键盘需要二个
//中断端点(一个输入一个输出),因此该值为2。
//@wei 怎么知道我用哪个端点? 一种约定???
0x02,
/*On this combined interface, you cannot apply boot device.(bInterfaceClass, bInterfaceSubclass, bInterfaceProtocol)= 03,0,0*/
//bInterfaceClass字段。该接口所使用的类。USB键盘是HID类,
//HID类的编码为0x03。
0x03,
//bInterfaceSubClass字段。该接口所使用的子类。在HID1.1协议中,
//只规定了一种子类:支持BIOS引导启动的子类。
//USB键盘、鼠标属于该子类,子类代码为0x01。
0,//@wei0x01,
//bInterfaceProtocol字段。如果子类为支持引导启动的子类,
//则协议可选择鼠标和键盘。键盘代码为0x01,鼠标代码为0x02。
0,//@wei 0x01,
//iConfiguration字段。该接口的字符串索引值。这里没有,为0。
0x00,
/******************HID描述符************************/
//bLength字段。本HID描述符下只有一个下级描述符。所以长度为9字节。
0x09,
//bDescriptorType字段。HID描述符的编号为0x21。
0x21,
//bcdHID字段。本协议使用的HID1.1协议。注意低字节在先。
0x10, 0x01,
//bCountyCode字段。设备适用的国家代码,这里选择为美国,代码0x21。
0x21,
//bNumDescriptors字段。下级描述符的数目。我们只有一个报告描述符。
0x01,
//bDescriptorType字段。下级描述符的类型,为报告描述符,编号为0x22。
0x22,
//bDescriptorLength字段。下级描述符的长度。下级描述符为报告描述符。
sizeof(ReportDescriptor) & 0xFF,
(sizeof(ReportDescriptor) >> 8) & 0xFF,
/**********************输入端点描述符***********************/
//bLength字段。端点描述符长度为7字节。
0x07,
//bDescriptorType字段。端点描述符编号为0x05。
//@wei protocol P437 //
0x05,
//bEndpointAddress字段。端点的地址。我们使用D12的输入端点1。
//D7位表示数据方向,输入端点D7为1。所以输入端点1的地址为0x81。
0x81,
//bmAttributes字段。D1~D0为端点传输类型选择。
//该端点为中断端点。中断端点的编号为3。其它位保留为0。
0x03,
//wMaxPacketSize字段。该端点的最大包长。端点1的最大包长为16字节。
//注意低字节在先。
0x10, 0x00,
//bInterval字段。端点查询的时间,我们设置为10个帧时间,即10ms。
0x0A,
/**********************中断输出端点描述符***********************/
//bLength字段。端点描述符长度为7字节。
0x07,
//bDescriptorType字段。端点描述符编号为0x05。
0x05,
//bEndpointAddress字段。端点的地址。我们使用D12的输出端点1。
//D7位表示数据方向,输出端点D7为0。所以输出端点1的地址为0x01。
0x01,
//bmAttributes字段。D1~D0为端点传输类型选择。
//该端点为中断端点。中断端点的编号为3。其它位保留为0。
0x03,
//wMaxPacketSize字段。该端点的最大包长。端点1的最大包长为16字节。
//注意低字节在先。
0x10, 0x00,
//bInterval字段。端点查询的时间,我们设置为10个帧时间,即10ms。
0x0A,
//@wei end
};
配置描述符集合完毕//
/************************语言ID的定义********************/
uint8 LanguageId[4] = { 0x04, //本描述符的长度
0x03, //字符串描述符
//0x0409为美式英语的ID
0x55, 0x04 };//@wei 0x00 原本为 0x04
语言ID完毕//
/**************************************************/
/********* 本转换结果来自 **********/
/********* Http://computer00.21ic.org **********/
/********* 作者: 电脑圈圈 **********/
/********* 欢迎大家使用 **********/
/********* 版权所有,盗版请写明出处 **********/
/**************************************************/
//http://computer00.21ic.org/user1/2198/archives/2007/42769.html
//字符串“电脑圈圈的USB专区 Http://group.ednchina.com/93/”的Unicode编码
//8位小端格式
/*
uint8 ManufacturerStringDescriptor[82] = { 82, //该描述符的长度为82字节
0x03, //字符串描述符的类型编码为0x03
0x35, 0x75, //电
0x11, 0x81, //脑
0x08, 0x57, //圈
0x08, 0x57, //圈
0x84, 0x76, //的
0x55, 0x00, //U
0x53, 0x00, //S
0x42, 0x00, //B
0x13, 0x4e, //专
0x3a, 0x53, //区
0x20, 0x00, //
0x48, 0x00, //H
0x74, 0x00, //t
0x74, 0x00, //t
0x70, 0x00, //p
0x3a, 0x00, //:
0x2f, 0x00, ///
0x2f, 0x00, ///
0x67, 0x00, //g
0x72, 0x00, //r
0x6f, 0x00, //o
0x75, 0x00, //u
0x70, 0x00, //p
0x2e, 0x00, //.
0x65, 0x00, //e
0x64, 0x00, //d
0x6e, 0x00, //n
0x63, 0x00, //c
0x68, 0x00, //h
0x69, 0x00, //i
0x6e, 0x00, //n
0x61, 0x00, //a
0x2e, 0x00, //.
0x63, 0x00, //c
0x6f, 0x00, //o
0x6d, 0x00, //m
0x2f, 0x00, ///
0x39, 0x00, //9
0x33, 0x00, //3
0x2f, 0x00 ///
};
*/
uint8 ManufacturerStringDescriptor[16] = {
16, 0x03,
0x63, 0x00, //c
0x6f, 0x00, //o
0x6d, 0x00, //m
0x2f, 0x00, ///
0x39, 0x00, //9
0x33, 0x00, //3
0x2f, 0x00 ///
};
/厂商字符串结束/
//字符串“《圈圈教你玩USB》之用户自定义的USB设备”的Unicode编码
//8位小端格式
uint8 ProductStringDescriptor[] = {
0x1a, 0x03, 0x55, 0x00, 0x53, 0x00, 0x42, 0x00, //..U.S.B.
0x20, 0x00, 0x4b, 0x00, 0x65, 0x00, 0x79, 0x00, // .K.e.y.
0x6b, 0x00, 0x6f, 0x00, 0x61, 0x00, 0x72, 0x00, //k.o.a.r.
0x64, 0x00 //d.
};
产品字符串结束
//字符串“2008-08-22”的Unicode编码
//8位小端格式
uint8 SerialNumberStringDescriptor[22] = { 22, //该描述符的长度为22字节
0x03, //字符串描述符的类型编码为0x03
0x32, 0x00, //2
0x30, 0x00, //0
0x30, 0x00, //0
0x38, 0x00, //8
0x2d, 0x00, //-
0x30, 0x00, //0
0x38, 0x00, //8
0x2d, 0x00, //-
0x32, 0x00, //2
0x32, 0x00 //2
};
//产品序列号字符串结束/
uint8 HidReportComfire[] = {0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};