我要做一個 包含兩個接口 (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};