USB裝置驅動程式在現代作業系統中扮演著關鍵角色,負責橋接作業系統與USB裝置,確保裝置正常運作。理解USB裝置驅動程式的核心概念,包含裝置描述符、資料傳輸型別和裝置類別,對於開發和維護相關軟體至關重要。本文將探討這些核心概念,並提供實務上的程式碼範例,幫助讀者更深入地理解USB裝置驅動程式的運作機制。特別是針對不同資料傳輸型別的特性和應用場景進行詳細分析,並深入說明人機介面裝置的規範和要求,以及USB裝置、組態、介面描述符的結構和重要欄位。
USB裝置驅動程式解析
USB裝置驅動程式是作業系統與USB裝置之間的橋樑,負責管理和控制USB裝置的運作。在本章中,我們將探討USB裝置驅動程式的工作原理、USB資料傳輸型別以及USB裝置類別等主題。
USB設定與介面
USB設定(Configuration)定義了裝置的功能和特性,主要包括電源能力和介面。每個裝置可以有多個設定,但同一時間只能啟用一個設定。一個設定可以包含多個USB介面(Interface),這些介面定義了裝置的功能。通常,一個功能對應一個介面,但某些裝置可能會公開多個與一個功能相關的介面。例如,一個具有內建喇叭的鍵盤可能會提供一個用於播放音訊的介面和一個用於按鍵輸入的介面。此外,介面還包含可選的替代設定(Alternate Settings),用於定義與介面相關功能的頻寬需求。每個介面包含一個或多個端點(Endpoint),用於在裝置和主機之間傳輸資料。
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333
title USB設定與介面
rectangle "包含" as node1
rectangle "傳輸資料" as node2
node1 --> node2
@enduml
此圖示展示了USB裝置的層級結構,從裝置到端點的組織方式。
USB資料傳輸
USB資料傳輸是主機和裝置之間進行通訊的基礎。資料傳輸有四種型別:
- 控制傳輸(Control Transfers):用於在裝置連線時進行設定,也可以用於其他與裝置相關的操作,例如讀寫裝置特定暫存器或控制裝置上的其他管道。控制傳輸包含最多三個不同的階段:設定階段、資料階段和狀態階段。
- 批次資料傳輸(Bulk Data Transfers):能夠傳輸相對大量的資料或突發性資料。批次傳輸沒有保證的時序,但如果USB匯流排未被其他活動佔用,可以提供最快的資料傳輸速率。
- 中斷資料傳輸(Interrupt Data Transfers):用於及時但可靠地傳輸資料,例如具有人類可感知回饋特性的字元或座標。中斷傳輸具有保證的最大延遲。USB滑鼠和鍵盤通常使用中斷資料傳輸。
- 等時資料傳輸(Isochronous Data Transfers):佔用預先協商的USB頻寬,具有預先協商的傳輸延遲。等時傳輸具有保證的時序,但沒有錯誤修正能力。等時資料必須以接收時的速率傳輸,以保持其時序,並且可能對傳輸延遲敏感。典型的等時傳輸應用包括串流音訊或視訊。
內容解密:
- 控制傳輸:主要用於裝置的初始化和組態,同時也支援特定的裝置操作,如暫存器的讀寫。
- 批次資料傳輸:適合大量或非同步資料的傳輸,雖然沒有嚴格的時間保證,但在匯流排空閒時能達到較高的傳輸速率。
- 中斷資料傳輸:保證了及時性和可靠性,適合需要即時回饋的裝置,如滑鼠和鍵盤。
- 等時資料傳輸:為需要固定頻寬和低延遲的應用提供支援,如音訊和視訊串流。
USB裝置類別
USB規範定義了多種裝置類別,用於根據功能和介面需求對USB裝置進行分類別。當主機檢索裝置資訊時,類別分類別有助於主機確定如何與USB裝置進行通訊。人機介面裝置(HID)是其中一種類別,包括滑鼠、鍵盤等直接與人互動的裝置。HID裝置必須滿足一些基本要求,例如必須具有控制端點(Endpoint 0)和中斷IN端點,並且所有資料傳輸都必須格式化為報告(Report)結構。
人機介面裝置類別
HID類別裝置通常以某種方式與人互動,包括滑鼠、鍵盤、印表機等。HID規範定義了裝置和資料傳輸協定的基本要求,裝置不一定依賴於直接的人機互動。HID裝置必須滿足一些基本要求:
- 所有HID裝置都必須具有控制端點(Endpoint 0)和中斷IN端點。許多裝置還使用中斷OUT端點。在大多數情況下,HID裝置不允許具有多於一個OUT和一個IN端點。
- 所有傳輸的資料都必須格式化為報告,其結構在報告描述符(Report Descriptor)中定義。
- HID裝置除了回應所有標準USB請求外,還必須回應標準HID請求。
內容解密:
- 基本端點需求:HID裝置至少需要一個控制端點和一個中斷IN端點,部分裝置還會使用中斷OUT端點進行雙向通訊。
- 報告結構:所有HID資料都必須按照報告描述符定義的結構進行格式化,以確保主機能夠正確解析接收到的資料。
- 標準請求支援:除了支援標準USB請求外,HID裝置還需要回應特定的HID請求,以滿足其功能需求。
USB裝置驅動程式 Chapter 13
USB描述符
當USB裝置連線到主機時,主機軟體透過傳送各種標準控制請求到預設端點來取得裝置的描述符。這些請求指定要檢索的描述符型別。裝置會回應這些請求,傳送包含裝置資訊、其組態、介面和相關端點的描述符。裝置描述符包含有關整個裝置的資訊。
每個USB裝置都會暴露一個裝置描述符,指示裝置的類別資訊、廠商和產品識別碼,以及組態數量。每個組態都會暴露其組態描述符,指示介面數量和電源特性。每個介面都會為其每個替代設定暴露一個介面描述符,其中包含有關類別和端點數量的資訊。每個介面中的每個端點都會暴露端點描述符,指示端點型別和最大封包大小。
描述符以一個位元組開始,描述描述符的長度(以位元組為單位)。這個長度等於描述符中的總位元組數,包括儲存長度的那個位元組。下一個位元組指示描述符型別,這使得主機能夠正確解釋描述符中包含的其餘位元組。其餘位元組的內容和值特定於正在傳輸的描述符型別。描述符結構必須完全遵循規範;主機將忽略包含大小或值錯誤的已接收描述符,可能導致列舉失敗並禁止裝置與主機之間進一步通訊。
USB裝置描述符
每個通用序列匯流排(USB)裝置都必須能夠提供一個單一的裝置描述符,其中包含有關裝置的相關資訊。例如,idVendor和idProduct欄位分別指定廠商和產品識別碼。bcdUSB欄位指示裝置符合的USB規範版本。例如,0x0200表示裝置是根據USB 2.0規範設計的。bcdDevice值指示裝置定義的修訂號。裝置描述符還指示裝置支援的組態總數。
以下是一個包含所有裝置描述符欄位的結構範例:
typedef struct __attribute__ ((packed))
{
uint8_t bLength; // 此描述符的長度。
uint8_t bDescriptorType; // 裝置描述符型別(USB_DESCRIPTOR_DEVICE)。
uint16_t bcdUSB; // USB規範發行編號(BCD)。
uint8_t bDeviceClass; // 類別程式碼(由USB-IF分配)。0xFF-廠商專用。
uint8_t bDeviceSubClass; // 子類別程式碼(由USB-IF分配)。
uint8_t bDeviceProtocol; // 協定程式碼(由USB-IF分配)。0xFF-廠商專用。
uint8_t bMaxPacketSize0; // 端點0的最大封包大小。
uint16_t idVendor; // 廠商ID(由USB-IF分配)。
uint16_t idProduct; // 產品ID(由製造商分配)。
uint16_t bcdDevice; // 裝置發行編號(BCD)。
uint8_t iManufacturer; // 描述製造商的字串描述符索引。
uint8_t iProduct; // 描述產品的字串描述符索引。
uint8_t iSerialNumber; // 具有裝置序號的字串描述符索引。
uint8_t bNumConfigurations; // 可能的組態數量。
} USB_DEVICE_DESCRIPTOR;
內容解密:
bLength:描述符長度,應當是所有USB裝置描述符共有的。bDescriptorType:裝置描述符型別的常數一位元組設計者,應當是所有裝置描述符共有的。bcdUSB:BCD編碼的兩位元組專案,告訴系統該裝置遵循的USB規範發行。bDeviceClass:如果要在裝置描述符中定義USB裝置類別,則此專案將包含USB規範中定義的常數。bDeviceSubClass和bDeviceProtocol:如果bDeviceClass設為0x00,則這兩個專案也應設為0x00,提供有關裝置子類別設定的資訊。bMaxPacketSize0:告訴主機單個控制端點傳輸中可包含的最大位元組數。低速裝置必須設為8,全速裝置可以有8、16、32或64的最大端點0封包大小。idVendor和idProduct:分別標識裝置的廠商ID和產品ID,可以透過USB.org網站取得。主機應用程式將搜尋連線的USB裝置的廠商ID,以找到應用程式所需的特定裝置。
重點分析與技術洞察
在設計USB裝置驅動程式時,正確理解和實作USB描述符至關重要。裝置描述符作為裝置身份識別的重要組成部分,不僅提供了裝置的基本資訊,也直接影響到主機對裝置的識別和驅動程式的載入。因此,在開發過程中,需要嚴格遵循USB規範,確保描述符的正確性和完整性。
此外,隨著USB規範的不斷更新,開發者需要關注新版本規範帶來的變化和新特性,以確保設計出的USB裝置具有良好的相容性和可擴充套件性。
USB 裝置驅動程式
USB 裝置描述符
在 USB 裝置中,bcdDevice 專案與供應商 ID 和產品 ID 一起用於唯一識別每個 USB 裝置。接下來的三個單位元組專案告訴主機在檢索由系統螢幕上顯示的附加裝置的 UNICODE 字串時使用哪個字串陣列索引。這個字串描述了附加裝置的製造商。iManufacturer 字串索引值為 0x00 表示主機該裝置沒有儲存在記憶體中的此字串的值。
iProduct 索引將在主機想要檢索描述附加產品的字串時使用。例如,該字串可以讀取 “USB 鍵盤”。iSerialNumber 索引指向的字串可以包含產品序列號的 UNICODE 文字。
bNumConfigurations 專案告訴主機該裝置支援多少種組態。組態是裝置功能能力的定義,包括端點組態。所有裝置必須至少包含一個組態,但可以支援多個組態。
USB 組態描述符
USB 裝置可以有多種不同的組態,儘管大多數裝置只有一個。組態描述符指定了裝置如何供電、其最大功耗和其介面數量。有兩種可能的組態,一種用於裝置由匯流排供電時,另一種用於裝置由電源供電時。下面是一個包含所有組態描述符欄位的結構範例:
typedef struct __attribute__ ((packed))
{
uint8_t bLength; // 描述符的長度(以位元組為單位)
uint8_t bDescriptorType; // 組態描述符(0x02)
uint16_t wTotalLength; // 傳回的資料總長度(以位元組為單位)
uint8_t bNumInterfaces; // 介面數量
uint8_t bConfigurationValue; // 用作選擇此組態的引數值
uint8_t iConfiguration; // 描述此組態的字串描述符索引
uint8_t bmAttributes; // 組態的電源引數
uint8_t bMaxPower; // 最大功耗(以 2mA 為單位)
} USB_CONFIGURATION_DESCRIPTOR;
內容解密:
bLength:定義組態描述符的長度。這是一個標準長度。bDescriptorType:組態描述符的一位元組常數 0x02 識別符號。wTotalLength:定義此描述符和與此組態相關的所有其他描述符的總長度。例如,可以透過將組態描述符、介面描述符、HID 類別描述符和與此介面相關的兩個端點描述符的長度相加以計算該長度。此雙位元組專案遵循 “小端” 資料格式。bNumInterfaces:定義此組態中包含的介面設定數量。bConfigurationValue:由SetConfiguration請求用於選擇此組態。iConfiguration:描述組態的可讀形式字串描述符的索引。bmAttributes:告訴主機裝置是否支援 USB 功能,如遠端喚醒。專案位被設定或清除以描述這些條件。請參閱 USB 規範以瞭解對此專案的詳細討論。bMaxPower:告訴主機裝置在此組態下正常運作所需的電流量。
USB 介面描述符
USB 介面描述符可能包含有關 USB 介面替代設定的資訊。介面描述符具有一個 bInterfaceNumber 欄位,用於指定介面號碼;以及一個 bAlternateSetting 欄位,允許對該介面進行替代設定。例如,您可以有一個具有兩個介面的裝置。第一個介面的 bInterfaceNumber 設定為零,表示它是第一個介面描述符,而 bAlternateSetting 設定為零。第二個介面的 bInterfaceNumber 設定為一,而 bAlternateSetting 設定為零(預設)。第二個介面也可以有一個 bAlternateSetting 設定為一,作為第二個介面的替代設定。
bNumEndpoints 專案提供了介面使用的端點數量。bInterfaceClass、bInterfaceSubClass 和 bInterfaceProtocol 專案指定了支援的類別(HID、大量儲存等)。這允許許多裝置使用類別驅動程式,從而避免了為您的裝置編寫特定驅動程式的需要。iInterface 專案允許對介面進行字串描述。
下面是一個包含介面描述符欄位的結構範例:
typedef struct __attribute__ ((packed))
{
uint8_t bLength; // 描述符的長度(以位元組為單位)(9 位元組)
uint8_t bDescriptorType; // 介面描述符(0x04)
uint8_t bInterfaceNumber; // 介面編號
uint8_t bAlternateSetting; // 用於選擇替代設定的值
uint8_t bNumEndPoints; // 此介面使用的端點數量
uint8_t bInterfaceClass; // 類別程式碼(由 USB 組織分配)
uint8_t bInterfaceSubClass; // 子類別程式碼(由 USB 組織分配)
uint8_t bInterfaceProtocol; // 協定程式碼(由 USB 組織分配)
} USB_INTERFACE_DESCRIPTOR;
內容解密:
bLength:定義介面描述符的長度。bDescriptorType:介面描述符的一位元組常數 0x04 識別符號。bInterfaceNumber:指定介面號碼。bAlternateSetting:允許對該介面進行替代設定。bNumEndPoints:提供介面使用的端點數量。bInterfaceClass、bInterfaceSubClass和bInterfaceProtocol:指定支援的類別。iInterface:允許對介面進行字串描述。
此圖示表示了 USB 裝置描述符、組態描述符和介面描述符之間的關係:
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333
title 內容解密:
rectangle "包含" as node1
rectangle "有多個" as node2
node1 --> node2
@enduml
此圖示顯示了 USB 裝置描述符如何包含多個組態描述符,而每個組態描述符又可以包含多個介面描述符。這種結構允許 USB 裝置具有複雜的組態和多個介面,以支援不同的功能和應用。