HID類

1. 簡述

        USB HID類是USB設備的一個標準設備類,包括的設備非常多。HID類設備定義它屬於人機交互操作的設備,用於控制計算機操作的一些方面,如USB鼠標、USB鍵盤、USB遊戲操縱桿等。但HID設備類不一定要有人機接口,只要符合HID類別規範的設備都是HID設備。

   USB HID設備的一個好處就是操作系統自帶了HID類的驅動程序,而用戶無需去開發驅動程序,只要使用API系統調用即可完成通信。

官方的資料下載詳見:

http://www.usb.org/developers/hidpage

其中包含最主要的兩個說明:

1. 《Device Class Definition for human interface device (HID)》

2. 《Universal Serial Bus HID Usage Tables》

其中的 1 文檔中描述了 HID 的基本組成和格式,2 文檔中是對文檔 1 的補充,將各種不同的 HID 設備的基本組成列舉出來。

 

2. HID 協議

在上一章介紹基本的 USB 協議的基礎之上,本章針對 HID 的人機交互的類進行着重介紹。(默認有 USB 通信基本協議基礎)

 

2.1 USB HID設備類的通信管道

所有的HID設備通過USB的控制管道(默認管道,即端點0)和中斷管道(端點1或端點2)與主機進行通信。

  管道        要求      說明

  控制(端點0)    必須      傳輸USB描述符、類請求代碼以及供查詢的消息數據

  中斷輸入      必須      傳輸從設備到主機的輸入數據

  中斷輸出      可選      傳輸從主機到設備的輸出數據

注:USB主機爲PC,USB設備如鼠標等。中斷端點的描述中,指定了主機輪詢的時間 Interval。即,主機會隔一段時間來「要」數據。

控制管道主要用於下面3個方面

  • 接收/響應USB主機的控制請求以及相關的類數據
  • 在USB主機查詢時傳輸數據(如響應Get_Report請求等)
  • 接收USB主機的數據

中斷管道主要用於下面兩個方面

  • USB主機接收USB設備的異步傳輸數據
  • USB主機發送有實時性要求的數據給USB設備

從USB主機到USB設備的中斷輸出數據傳輸是可選的,當不支持中斷輸出數據時,USB主機通過控制管道將數據傳輸給USB設備。

 

2.2 與 USB HID 設備有關的描述符

HID設備的描述符除了5個USB的標準描述符(設備描述符、配置描述符、接口描述符、端點描述符、字符串描述符)外,還包括三個HID設備類特定的描述符:HID描述符、報告描述符(Report)、實體描述符(Physical)。

他們之間的層次關係如圖:

可以看到,在 Interface desc 層,就對 HID desc 有指定。

除了HID的三個特定描述符組成對HID設備的解釋外,5個標準描述符中與HID設備有關的部分有:

  1. 設備描述符中:bDeviceClass, bDeviceSubClass, bDeviceProtocol三個值必須爲 0
  2. 接口描述符中:bInterfaceClass 的值必須是 0x03, bInterfaceSubClass 的值爲 0 或 1, 爲1表示HID設備是一個啓動設備(BootDevice, 一般對PC機有意義,意思是BIOS啓動時能識別您使用的HID設備,切只有標準鼠標或者鍵盤才能稱爲BootDevice),爲0表示HID設備是操作系統啓動厚才能識別使用的設備。bInterfaceProtocol 的取值含義如下:
設備描述符中
bDeviceClass 0
bDeviceSubClass 0
bDeviceProtocol 0
接口描述符中
bInterfaceClass  0x03
bInterfaceSubClass 

0 — No Sub Class

1 — Boot Interface SubClass

2~255  Reserved

bInterfaceProtocol

0 — NONE

1 — Keyboard(鍵盤)

2 — Mouse (鼠標)

3~255  Reserved

 

2.2.1  HID 描述符

  HID描述符關聯於接口描述符,因而如果一個設備只有一個接口描述符,則無論它有幾個端點描述符,HID設備只有一個HID描述符。HID設備描述符主要描述HID規範的版本號, HID通信所使用的額外描述符, 報表描述符的長度等。下表爲HID描述符的結構。

HID 描述符
偏移量 大小(Byte) 描述
0 bLength 1 數字 此描述符的長度,以字節爲單位
1 bDescriptorType 1 常量 描述符種類(此處 0x21爲HID類)
2 bcdHID 2 數字 HID規範版本號(BCD碼),採用4個16進制的BCD格式編碼
4 bCountryCode 1 數字 硬件目的國家的識別碼
5 bNumDescriptors 1 數字 支持的附屬描述符數目
6 bDescriptorType 1 常量 HID相關描述符的類型,見下表
7 wDescriptorLength 2 數字 報告描述符的總長度
9 bDescriptorType 1 常量 用於識別描述符類型的常量,使用有一個以上描述符的設備
10 wDescriptorLength 2 數字 描述符總長度,使用在有一個以上描述符的設備 
HID相關描述符類型定義
描述符類型值

HID相關描述符類型

0x21

HID描述符

0x22 報表描述符
0x23 實體描述符

在 USB 標準請求裏,當使用了獲取配置描述符的時候,會按照下面的順序進行返回,即 HID 描述符也會被返回。

HID描述符裏又包含了其附屬的描述符的類型和長度(如報告描述符),然後主機再根據HID描述符的信息請求其相關的描述符。也就是說,主機通過獲取描述符,就知道該設備是一個 HID 設備。

典型的,在一個請求配置描述符,並獲取全部描述符的流程中,選擇 「Class 0x01」:

其中 Request 的 value 爲上述內容。

 

2.2.2  報告(Report)描述符

報告描述符是 USB 的所有描述裏面最複雜的一個,因爲他和其他的不一樣,沒有固定的長度和表。是一個可變而且變化比較多樣的一類描述符。

報告描述符,其實就是告訴主機,通過中斷端點傳輸的數據,哪些位或者哪些 Bytes ,代表着什麼含義。這樣講,可能會比較抽象,但是看完後面的描述後,在回頭看來,會覺得比較清晰。

要了解報告描述符,需要兩份 USB 官方的 HID 資料:

《Device Class Definition for human interface device (HID)》

《Universal Serial Bus HID Usage Tables》

一份是說明 HID 的描述符的協議,另一個說明 HID 的報表描述符的內容定義。

 

報表描述符由一個一個的 Item 構成,Item 分爲了兩種,格式分別如下:

1. Short Item

 

2. Long Item

上述中,這裏主要講述 Short Item,用得最多的是 Short Item

在 Short Item 中,起始的 1 Byte 代表了這個 Item 的用途:

bSize  :代表後面的數據,最大 4 bytes。

bType  :代表了這個 Item 是什麼類型的 Item,這裏有三種大的類型:Main(0x00)、Global(0x01)、Local(0x02)。

bTag    :代表在對應的 Item 下的更加細的分類。

    | —— Main      分爲:Input、Output、Feature、Collection、End Collection。

    | —— Global    分爲:Usage Page、 Logical Minimum、Logical Maximum、等等。

    | —— Local     分爲 :Usage 、Usage Minimum、Usage Maximum、String 等等。

 

對於 bSize + bType + bTag 的 1 Byte 的組合詳見下表:

表中列出了所有的配合,其中的 「?」 代表了低 4 bits 的值,打個比方,一個 Short Item:

0x05(Prefix), 0x01(Data), // USAGE_PAGE (Generic Desktop)

其中,0x05表示前綴,0x01爲數據部分。

0x05 =》00000101 可以分解出:bSize = 1;bType = 1(Global);bTag = 0(Usage Page)

上述解讀,來自 《Device Class Definition for human interface device (HID)》 文件,後面跟的那個 0x01 是帶的 1 Byte 的數據,這個數據的具體含義,需要查找 :《Universal Serial Bus HID Usage Tables》 中,可以找到,含義是:Generic Desktop。

即,根據【bSize + bType + bTag】的信息知道 Tag 的具體含義,在結合 Table 表和具體的 Tag,查找後面跟的數據對應的含義,這樣,達到解讀報告描述符的目的。

下面來分析一下幾個重要的 Tag 的作用和含義。

 

Main

    |—— Input :表示設備操作輸入到主機的數據模式。這個數據格式就形成一個輸入報告,雖然輸入報告可以用控制型管線以get report(input)來傳輸,但是通常用中斷型輸入管線來傳輸以確保在每一固定週期內都能將更新的輸入報告傳給主機。

    |—— Output : 表示由主機輸出到裝置操作的數據格式。這個數據格式就形成一個輸出報告。輸出報告通常不適用輪詢的方式來傳送給設備,而是由應用軟件依實際需求以傳令方式要求送出輸出報告,所以大多用控制型管線以set report(output)指令來將報告送到設備。當然也可以選擇用中斷型輸出管線來傳送,只是通常不建議這樣用。

    |—— Feature : 表示由主機送到設備的組態所需數據的數據格式。這個數據模式就形成一個特徵報告。特徵報告只能用控制型管線以get report(feature)和set report(feature)指令分別來取得和設定設備的特徵值。

Main (後面帶的數據格式以及含義)中產生報告數據格式的三個卷標(Input,Output,和Feature)具有共通的數據定義,這些數據:

Bit

8

7

6

5

4

3

2

1

0

0

Bit Field

Non Volatile

No Null Position

Preferred State

Linear

No Wrap

Absolute

Array

Data

1

Buffered Bytes

Volatile

Null State

No Preferred

Non Linear

Wrap

Relative

Variable

Constant

Data/Constant:主項目之數據爲可變值(設爲Data),或爲固定不可變值(設爲Constant)。Constant 都用於Feature 的報告,或是用於填充位(padding),使報告長度以字節爲單位。

Array/Variable:主項目之數據的每個字段可以表示幾個不同的操作的其中一個被觸發(設爲Array),或是每個字段僅表示一個操作(設爲Variable)。如果是Variable,則Report Count 的數據值等於報告數據的字段數。若是Array,則Report Count 的數據值表示可以同時被觸發的最多操作數目。

Absolute/Relative:主項目的數據是以相對於固定的基準點方式提供絕對數值(設爲Absolute),或是提供相對於前次報告的相對值(設爲Relative)。

No Wrap/Wrap:主項目的數據值達到極值後會轉爲極低值,反之亦同,稱作卷繞(設爲Wrap)。例如一個轉鈕可以做360°旋轉,輸出值從0 至10,若設定爲Wrap,則值達10 後,在同方向旋轉則值變爲0,反之若達到0,再轉就得到10。

Linear/Nonlinear:主項目的數據與操作刻度爲線性關係(設爲Linear),或爲非線性(設爲Nonlinear)。

Preferred State/No Preferred:主項目對應的操作再不被觸發時會自動恢復到初始狀態(設爲Preferred State),或是不會恢復原狀(設爲No Preferred)。例如鍵盤的按鍵和會自動置中(self-centering)的遊戲杆,皆爲Preferred State。

No Null Position/Null State:主項目對應的操和有一個狀態,其不會送出有意義的數據,即數據將不在Logical Minimum 和Logical Maximum 之間,這種操控要標註Null State,否則爲No Null Position。例如幾個按鍵,而無鍵被按下的用途沒有聲明在Usage 之列,則可以在主項目的數據中設Null State,將無鍵被按下的狀態排除在Logical Minimum 和Logical Maximum區間之外,進一步請參看Universal Serial Bus HID Usage Tables 文件的Appendix A.3 節中範例。

Non Volatile/Volatile:主項目Feature 的數據不允許被主機改變(設爲Non Volatile),或是允許被主機改變(設爲Volatile)。注意主項目Input 和Output,此標註設定無意義,所以bit 7 的代碼必須爲0。

Bit Field/Buffered Bytes:主項目的數據格式要以字節爲單位,不足構成字節時自動填充成字節則設Buffered Bytes。

 

Collection 和 End Collection。以鼠標而言,在實體上是一個指針(pointer),只是應用爲計算機鼠標﹔而這個指針含有三個按鍵和二個平移軸X 和Y。所以指針的報告是由不同格式的數據所構成,因而需要用到Collection 和End Collection 將幾個Input 項目集結成一組,其用途爲指針,再用Collection 和End Collection 將指針括起來說明其應用爲鼠標。卷標End Collection 沒有跟隨任何資料。但是卷標Collection 跟隨一個字節的數據,例如指針的數據名爲Physical,而鼠標的爲Application。所有Collection的數據名稱與代碼如表:

 

Physical

Application

Logical

Report

Named Arrary

Usage Modifier

Usage Switch

Reserved

Vendor-defined

代碼

0x00

0x01

0x02

0x03

0x04

0x05

0x06

0x07-0x7f

0x80-0xff

用途

CP

CA

CL

 

Nary

US

UM

 

 

Collection 的數據名稱很難有一個準則來給定,Universal Serial Bus HID Usage Tables文檔中將各種用途的用途種類(usage type)列出,使用者必須依據用途種類來指定Collection 的數據名稱,例如鼠標,鍵盤和遊戲杆的用途種類爲CA,所以要用Collection (Application),而指針爲CP,所以用Collection (Physical)。

 

 

Local

Designator是要搭配實體描述符使用的,這裏不對實體描述符進行介紹,所以略過這些designator標籤。

標籤 Usage 實際上應該稱作Usage ID,它搭配 Global 的 Usage Page 卷標才形成前文所定義的用途 {usage}。

但是報告描述符允許在區域項目的 Usage 卷標直接用32位的方式來指定用途,這種方式稱作擴充式用途指定法(extended usage)以示區別。例如:Usage(Generic Desktop:Mouse),Usage Minimum(Keyboard:0),和Usage Maximum(Keyboard:101)。很明顯的,擴充式用途指定法會取代『項目狀態表』中的Usage Page。還有,使用擴充式用途指定法時,數據的高16 個位爲用途類頁Usage Page,低16 個位則爲用途識別名Usage ID。往往一個報告數據會對應到幾個操作,因而會有幾個用途,例如101 按鍵的鍵盤利用不同代碼代表不同的鍵,每一個鍵是一個操作,有自己的用途,要將所有 Usage ID 列出不太現實,所以就需要Usage Minimum 和Usage Maximum 二個標籤。以鍵盤爲例,主項目之上只要二個區域項目:Usage Minimum (0), Usage Maximum (101)。如此一來,則無鍵按下(Usage ID 爲0)和101 鍵中任一鍵被按下(Usage ID 爲1 至101)的用途都被賦於到一個報告數據上。

卷標String Index 類似卷標Usage,而卷標String Minimum 和String Maximum 則類似標籤Usage Minimum 和Usage Maximum。如果希望某個操作對應到一個字串,則用String Index 來描述該操控的報告數據,這個字符串在字符串描述符中,StringIndex (data)項目中的data 是這個字符串在字符串描述符中的位置索引。如果需要用到幾個字符串,則可以使用String Minimum 來指向字符串描述符中被用到字符串的最先位置索引,和String Maximum 來指向最後位置索引。

標籤Delimiter 很少用到,請參考Universal Serial Bus HID Usage Tables 文件中Appendix B 的範例詳細說明。

 

Global

全局項目的卷標事實上只要Usage Page,Logical Minimum,Logical Maximum,Report Size,Report ID,Report Count 就足夠了。表2 列了二個音量操作的例子(音量增減鍵和音量旋鈕)將用來輔助說明這些卷標,不過主項目括號內的數據會在後文中再做說明。

音量減鍵

音量旋鈕

Usage Page(consumer)

Usage Page(Consumer)

Usage(Volume)

Usage(Volume)

Logical Minimum(-1)

Logical Minimum(0)

Logical Maximum(-1)

Logical Maximum(100)

Report Size(2)

Report Size(7)

Report Count(1)

Report Count(1)

Input(Data,Variable,Relative)

Input(Data,Variable,Absolute,No Wrap,Linear,No Relative)

Logical Minimum 和 Logical Maximum 代表了該特報告字段的取值範圍。

Report Size :代表了用多少個 bit 來表徵特性。

Report Count : 代表有多少個這樣的結構。

比如:音量增減鍵的例子中ReportCount (1)表示主項目Input 只產生一個字段,所以可知只有一個音量增減鍵﹔而Report Size (2)表示這個字段爲2 位。另一個音量旋鈕例子也是隻有一個旋鈕,所以用Report Count (1)﹔但是因爲Report Size (7),所以該旋鈕的數據字段爲7位,可以表示0到127之數值。再舉一例,如果是鼠標的三個按鍵,每個按鍵佔用一個一位的字段,則Report Size (1), Report Count (3)﹔那麼這個報告長度爲三個位,可以同時呈現出三個按鍵的狀態(原狀或被按下)。

注意:當要將同一種報告分成數個部分,則每一個部分要給予一個識別值,這時就需用到卷標Report ID,其數據值必須從1起算,不可使用0。沒有賦予Report ID 標籤的報告,主機有可能會將其Report ID 視爲0,所以Report ID (0)被要求不能使用。這個標籤對控制型管線纔有意義,因爲它可以在請求報告時指定Report ID的值。對於中斷型管線,其爲週期性傳輸報告,所以每次都會將所屬報告傳完,沒有僅傳輸部分之必要,所以 Report ID 標籤就無意義。

 

2.3 報告(Report)描述符的組成以及解析

2.3.1 報告(Report)描述符的組成

根據官方的給出的資料,報告描述符的組成結構如下:

由一個 App Collection 開始,如果有多個 Report 結構,則增加 Collection 進行組合。

首先需要描述他的 Usage 然後依次描述 Logical Minimum 、Logical Maximum、Report Size 、Report Count 等等。

 

2.3.2 報告(Report)描述符的解析,解讀,構建

首先,要解讀,解析,構建報告描述符,則需要了解並清楚他們之間的順序:

Main 用來描述一個主項目,其他的都是用來修飾主項目的。Main 的作用域,一直到下一個 Main爲止。

Global 既然是全局的意思,可以把它理解成爲一個全局變量,遇到下一個同樣的全局變量的定義時刻,此定義失效,並啓用下一個定義的值。

Local 理解成爲本地變量,用來修飾這一次的 Main 項目的。

比如:官方的例子:

從上到下,可以看到,首先,定義了一個 Input,Size 是 3,Count 是 2,即 2 個 3 bits 的 Input;

接着 Size 修改爲 8,即 數據成爲了 8bits,但是數據個數還是之前的沒變,定義了 含有 2 個 8 bits 的一個 Input;

接着定義了 Output,沿用了之前的含有 2 個 8 bits 的屬性;

 

NOTE: 在閱讀 Report 的時候,都是上面的內容,來修飾下面的一個 Main。

 

2.3.3 報告(Report)描述符的例子

下面是一個鍵盤和鼠標的報告描述符:

鍵     盤

鼠      標

項  目

編 碼

項   目

編 碼

Usage Page (Generic Desktop),

0x0105

Usage Page (Generic Desktop),

0x0105

Usage (Keyboard),

0x0609

Usage (Mouse),

0x0209

Collection (Application),

0x01A1

Collection (Application)

0x01A1

Usage Page (Keyboard),

0x0705

Usage (Pointer),

0x0109

Usage Minimum (224),

0xE019

Collection (Physical),

0x00A1

Usage Maximum (231),

0xE729

Usage Page (Buttons),

0x0905

Logical Minimum (0),

0x0015

Usage Minimum (1),

0x0119

Logical Maximum (1),

0x0125

Usage Maximum (3),

0x0329

Report Size (1),

0x0175

Logical Minimum (0),

0x0015

Report Count (8),

0x0895

Logical Maximum (1),

0x0125

Input (Data, Variable, Absolute),

0x0281

Report Size (1),

0x0175

Report Size (8),

0x0875

Report Count (3),

0x0395

Report Count (1),

0x0195

Input (Data, Variable, bsolute),

0x0281

Input (Constant),

0x0181

Report Size (5),

0x0575

Usage Minimum (0),

0x0019

Report Count (1),

0x0195

Usage Maximum (101),

0x6529

Input (Constant),

0x0181

Logical Minimum (0),

0x0015

Usage Page (Generic Desktop),

0x0105

Logical Maximum (101),

0x6525

Usage (X),

0x3009

Report Size (8),

0x0875

Usage (Y),

0x3109

Report Count (6),

0x0695

Logical Minimum (-127),

0x8115

Input (Data, Array),

0x0081

Logical Maximum (127),

0x7F25

Usage Page (LEDs),

0x0805

Report Size (8),

0x0875

Usage Minimum (1),

0x0119

Report Count (2),

0x0295

Usage Maximum (5),

0x0529

Input (Data, Variable, Relative),

0x0681

Logical Minimum (0),

0x0015

End Collection,

0xC0

Logical Maximum (1),

0x0125

End Collection

0xC0

Report Size (1),

0x0175

 

 

Report Count (5),

0x0595

 

 

Output (Data, Variable,Absolute),

0x0291

 

 

Report Size (3),

0x0375

 

 

Report Count (1),

0x0195

 

 

Output (Constant),

0x0191

 

 

End Collection

0xC0

 

 

這裏先簡述一下鼠標的情況:

鼠標的報告描述符的輸入數據格式中最低的一個字節只有最低3個位有意義,其分別對應到鼠標上的三個按鈕,用途類頁爲Buttons。其它二個字節的用途爲(Generic Desktop: X)和(Generic Desktop: X),分別對應到鼠標X 軸和Y 軸的位移操控。這二個位移值得邏輯範圍爲-127 到127,即一個字節可以表示最大範圍。位移的數值是相對值,所以主項目爲Input (Data, Variable, Relative)。

下面是官方給出的一個鼠標的報告描述符:

 

2.3.4 報告(Report)和報告描述符

報告描述等於是告訴了 Host 端,將以什麼樣的形式進行數據傳送,報告描述符賦予了數據的意義,讓 HOST 能夠在收到裸的數據的時候,根據報告描述符進行分解到正確的數據,並根據 Usage 來進行正確的操作。

比如:上述例子中,以 3 個 Bytes 進行上報:(沒有報告ID)

如果鼠標左鍵按下,則返回 01 00 00(十六進制值)

如果鼠標右鍵按下,則返回 02 00 00

如果鼠標中鍵按下,則返回 04 00 00

如果三個鍵同時按下,則返回07 00 00。

如果鼠標往右移動則,第二字節返回正值,值越大移動速度越快。其它的類推。

 

2.4 USB Device 支持 Class Request

支持 HID ,設備除了支持基本的請求以外,還需要支持 HID 的幾個特定的請求:

通過 get report 返回報告描述符。等等。具體參考 HID 文檔。

 

工具:

USB 協會提供了一個HID 描述符編輯工具稱作HID Descriptor Tool,其執行程序爲DT.exe。這個工具軟件可以在USB 網站上取得。雖然稱作HID 描述符工具,事實上,僅提供編撰報告描述符之用。