UHID - 用於 HID 子系統的使用者空間 I/O 驅動程式支援¶
UHID 允許使用者空間實現 HID 傳輸驅動程式。 請參閱 HID I/O 傳輸驅動程式 以獲得 HID 傳輸驅動程式的介紹。本文件在很大程度上依賴於此處宣告的定義。
使用 UHID,使用者空間傳輸驅動程式可以為連線到使用者空間控制匯流排的每個裝置建立核心 hid 裝置。 UHID API 定義了從核心到使用者空間以及從使用者空間到核心提供的 I/O 事件。
./samples/uhid/uhid-example.c 中有一個使用者空間應用程式示例。
UHID API¶
UHID 透過字元雜項裝置訪問。次要數字是動態分配的,因此您需要依靠 udev(或類似的東西)來建立裝置節點。 預設情況下這是 /dev/uhid。
如果您的 HID I/O 驅動程式檢測到新裝置,並且您想在 HID 子系統中註冊此裝置,則需要為您要註冊的每個裝置開啟一次 /dev/uhid。 所有進一步的通訊都是透過 read() 或 write() “struct uhid_event” 物件完成的。 設定 O_NONBLOCK 支援非阻塞操作
struct uhid_event {
__u32 type;
union {
struct uhid_create2_req create2;
struct uhid_output_req output;
struct uhid_input2_req input2;
...
} u;
};
“type” 欄位包含事件的 ID。 根據 ID 傳送不同的有效負載。 您不得將單個事件拆分到多個 read() 或多個 write() 中。 單個事件必須始終作為一個整體傳送。 此外,每個 read() 或 write() 只能傳送一個事件。 掛起的資料將被忽略。 如果您想在單個 syscall 中處理多個事件,請使用帶有 readv()/writev() 的向量化 I/O。 “type” 欄位定義有效負載。 對於每種型別,聯合“u”中都有一個有效的負載結構(除了空有效負載)。 此有效負載包含管理和/或裝置資料。
您應該做的第一件事是傳送一個 UHID_CREATE2 事件。 這將註冊裝置。 UHID 將以 UHID_START 事件響應。 您現在可以開始向 UHID 傳送資料和從 UHID 讀取資料。 但是,除非 UHID 傳送 UHID_OPEN 事件,否則內部連線的 HID 裝置驅動程式沒有附加使用者。 也就是說,除非您收到 UHID_OPEN 事件,否則您可能會使您的裝置進入睡眠狀態。 如果您收到 UHID_OPEN 事件,您應該啟動 I/O。 如果最後一個使用者關閉了 HID 裝置,您將收到一個 UHID_CLOSE 事件。 這之後可能會再次出現 UHID_OPEN 事件,等等。 不需要在使用者空間執行引用計數。 也就是說,您永遠不會在沒有 UHID_CLOSE 事件的情況下收到多個 UHID_OPEN 事件。 HID 子系統為您執行 ref 計數。 您也可以決定忽略 UHID_OPEN/UHID_CLOSE。 即使裝置可能沒有使用者,也允許 I/O。
如果您想在中斷通道上將資料傳送到 HID 子系統,您可以傳送帶有原始資料有效負載的 HID_INPUT2 事件。 如果核心想在中斷通道上將資料傳送到裝置,您將讀取一個 UHID_OUTPUT 事件。 控制通道上的資料請求目前僅限於 GET_REPORT 和 SET_REPORT(到目前為止尚未定義控制通道上的其他資料報告)。 這些請求始終是同步的。 這意味著,核心傳送 UHID_GET_REPORT 和 UHID_SET_REPORT 事件,並要求您在控制通道上將它們轉發到裝置。 裝置響應後,您必須透過 UHID_GET_REPORT_REPLY 和 UHID_SET_REPORT_REPLY 將響應轉發到核心。 核心在此類往返過程中阻止內部驅動程式執行(在硬編碼的週期後超時)。
如果您的裝置斷開連線,您應該傳送一個 UHID_DESTROY 事件。 這將登出裝置。 您現在可以再次傳送 UHID_CREATE2 以註冊新裝置。 如果您 close() fd,則裝置會自動登出並在內部銷燬。
write()¶
write() 允許您修改裝置的狀態並將輸入資料饋送到核心中。 核心將立即解析事件,如果不支援事件 ID,它將返回 -EOPNOTSUPP。 如果有效負載無效,則返回 -EINVAL,否則,返回讀取的資料量,並且請求已成功處理。 O_NONBLOCK 不影響 write(),因為寫入始終以非阻塞方式立即處理。 但是,未來的請求可能會使用 O_NONBLOCK。
- UHID_CREATE2
這將建立內部 HID 裝置。 在您將此事件傳送到核心之前,無法進行 I/O。 有效負載的型別為 struct uhid_create2_req,並且包含有關您的裝置的資訊。 您現在可以開始 I/O。
- UHID_DESTROY
這將銷燬內部 HID 裝置。 不會接受進一步的 I/O。 您仍然可以透過 read() 接收掛起的訊息,但不能將進一步的 UHID_INPUT 事件傳送到核心。 您可以透過再次傳送 UHID_CREATE2 來建立一個新裝置。 無需重新開啟字元裝置。
- UHID_INPUT2
您必須在將輸入傳送到核心之前傳送 UHID_CREATE2! 此事件包含資料有效負載。 這是您從裝置的中斷通道讀取的原始資料。 核心將解析 HID 報告。
- UHID_GET_REPORT_REPLY
如果您收到一個 UHID_GET_REPORT 請求,您必須使用此請求回覆。 您必須將請求中的“id”欄位複製到答案中。 如果未發生錯誤,請將“err”欄位設定為 0,如果發生 I/O 錯誤,請將其設定為 EIO。 如果“err”為 0,那麼您應該使用 GET_REPORT 請求的結果填充答案的緩衝區,並相應地設定“size”。
- UHID_SET_REPORT_REPLY
這是 UHID_GET_REPORT_REPLY 的 SET_REPORT 等效項。 與 GET_REPORT 不同,SET_REPORT 永遠不會返回資料緩衝區,因此,只需正確設定“id”和“err”欄位即可。
read()¶
read() 將返回一個排隊的輸出報告。 不需要對它們進行任何反應,但您應該根據自己的需要處理它們。
- UHID_START
這是在 HID 裝置啟動時傳送的。 將其視為 UHID_CREATE2 的答案。 這始終是傳送的第一個事件。 請注意,此事件可能不會在 write(UHID_CREATE2) 返回後立即提供。 裝置驅動程式可能需要延遲設定。 此事件包含型別為 uhid_start_req 的有效負載。“dev_flags”欄位描述裝置的特殊行為。 定義了以下標誌
UHID_DEV_NUMBERED_FEATURE_REPORTS
UHID_DEV_NUMBERED_OUTPUT_REPORTS
UHID_DEV_NUMBERED_INPUT_REPORTS
每個標誌定義給定的報告型別是否使用編號的報告。 如果某種型別使用編號的報告,則來自核心的所有訊息都已經具有報告編號作為字首。 否則,核心不會新增任何字首。 對於使用者空間傳送到核心的訊息,您必須根據這些標誌調整字首。
- UHID_STOP
這是在 HID 裝置停止時傳送的。 將其視為 UHID_DESTROY 的答案。
如果您沒有透過 UHID_DESTROY 銷燬您的裝置,但核心傳送了一個 UHID_STOP 事件,這通常應該被忽略。 這意味著核心重新載入/更改了載入在您的 HID 裝置上的裝置驅動程式(或者發生了一些其他維護操作)。
您通常可以安全地忽略任何 UHID_STOP 事件。
- UHID_OPEN
這是在 HID 裝置開啟時傳送的。 也就是說,HID 裝置提供的資料被其他程序讀取。 您可以忽略此事件,但它對電源管理很有用。 只要您沒有收到此事件,實際上就沒有其他程序讀取您的資料,因此無需向核心傳送 UHID_INPUT2 事件。
- UHID_CLOSE
當沒有更多程序讀取 HID 資料時,將傳送此訊息。 它是 UHID_OPEN 的對應物,您也可以忽略此事件。
- UHID_OUTPUT
如果 HID 裝置驅動程式想在中斷通道上將原始資料傳送到 I/O 裝置,則會發送此訊息。 您應該讀取有效負載並將其轉發到裝置。 有效負載的型別為“struct uhid_output_req”。 即使您尚未收到 UHID_OPEN,也可能會收到此訊息。
- UHID_GET_REPORT
如果核心驅動程式想在控制通道上執行 GET_REPORT 請求(如 HID 規範中所述),則會發送此事件。 報告型別和報告編號在有效負載中可用。 核心序列化 GET_REPORT 請求,因此永遠不會有兩個並行。 但是,如果您未能使用 UHID_GET_REPORT_REPLY 響應,則請求可能會靜默超時。 一旦您讀取了一個 GET_REPORT 請求,您應該將其轉發到 HID 裝置並記住有效負載中的“id”欄位。 一旦您的 HID 裝置響應 GET_REPORT(或者如果它失敗),您必須向核心傳送一個 UHID_GET_REPORT_REPLY,其“id”與請求中的完全相同。 如果請求已經超時,核心將靜默忽略響應。“id”欄位永遠不會重複使用,因此不會發生衝突。
- UHID_SET_REPORT
這是 UHID_GET_REPORT 的 SET_REPORT 等效項。 收到後,您應該向您的 HID 裝置傳送一個 SET_REPORT 請求。 一旦它回覆,您必須透過 UHID_SET_REPORT_REPLY 告訴核心。 適用於 UHID_GET_REPORT 的相同限制也適用。
撰寫於 2012 年,David Herrmann <dh.herrmann@gmail.com>