Linux USB Gadget API

作者:

David Brownell

日期:

2004 年 8 月 20 日

簡介

本文件介紹了 Linux-USB “Gadget” 核心模式 API,供嵌入 Linux 的外圍裝置和其他 USB 裝置使用。它概述了 API 結構,並展示了它如何融入系統開發專案中。這是 Linux 上釋出的第一個此類 API,旨在解決許多重要問題,包括

  • 支援 USB 2.0,適用於可以每秒流式傳輸數十兆位元組資料的高速裝置。

  • 可以處理具有數十個端點的裝置,就像處理只有兩個固定功能端點的裝置一樣。可以編寫 Gadget 驅動程式,以便輕鬆移植到新硬體。

  • 足夠靈活,可以公開更復雜的 USB 裝置功能,例如多個配置、多個介面、複合裝置和備用介面設定。

  • USB “On-The-Go”(OTG)支援,結合了對 Linux-USB 主機端的更新。

  • 與 Linux-USB 主機端 API 共享資料結構和 API 模型。這有助於 OTG 支援,並展望更對稱的框架(主機和裝置側驅動程式使用相同的 I/O 模型)。

  • 極簡主義,因此更容易支援新的裝置控制器硬體。I/O 處理並不意味著對記憶體或 CPU 資源的大量需求。

大多數 Linux 開發人員將無法使用此 API,因為他們在 PC、工作站或伺服器中擁有 USB host 硬體。具有嵌入式系統的 Linux 使用者更有可能擁有 USB 外圍硬體。為了區分在此類硬體內部執行的驅動程式與更熟悉的 Linux “USB 裝置驅動程式”(它是真實 USB 裝置的主機端代理),使用了一個不同的術語:外圍裝置內部的驅動程式是“USB gadget 驅動程式”。在 USB 協議互動中,裝置驅動程式是主裝置(或“客戶端驅動程式”),而 gadget 驅動程式是從裝置(或“功能驅動程式”)。

gadget API 類似於主機端 Linux-USB API,因為兩者都使用請求物件佇列來打包 I/O 緩衝區,並且可以提交或取消這些請求。它們共享標準 USB 第 9 章訊息、結構和常量的通用定義。此外,兩個 API 都將驅動程式繫結和解除繫結到裝置。API 在細節上有所不同,因為主機端當前的 URB 框架公開了許多不適用於 gadget API 的實現細節和假設。雖然控制傳輸和配置管理的模型必然不同(一側是硬體無關的主裝置,另一側是硬體感知的從裝置),但此處使用的端點 I/0 API 也應該可用於開銷降低的主機端 API。

Gadget 驅動程式的結構

在 USB 外圍裝置內部執行的系統通常在核心內部至少有三個層來處理 USB 協議處理,並且可能在使用者空間程式碼中具有額外的層。gadget API 由中間層用於與最低層(直接處理硬體)互動。

在 Linux 中,從下到上,這些層是

USB 控制器驅動程式

這是最低的軟體級別。它是唯一透過暫存器、fifo、dma、irq 等與硬體對話的層。<linux/usb/gadget.h> API 抽象了外圍控制器端點硬體。該硬體透過端點物件公開,這些端點物件接受 IN/OUT 緩衝區流,並透過與 gadget 驅動程式互動的回撥公開。由於正常的 USB 裝置只有一個上游埠,因此它們只有一個這樣的驅動程式。控制器驅動程式可以支援任意數量的不同 gadget 驅動程式,但一次只能使用其中一個。

此類控制器硬體的示例包括基於 PCI 的 NetChip 2280 USB 2.0 高速控制器、SA-11x0 或 PXA-25x UDC(在許多 PDA 中發現)以及各種其他產品。

Gadget 驅動程式

此驅動程式的下邊界使用對控制器驅動程式的呼叫實現硬體無關的 USB 功能。由於此類硬體在功能和限制方面差異很大,並且用於空間非常寶貴的嵌入式環境中,因此 gadget 驅動程式通常在編譯時配置為與特定控制器支援的端點一起使用。使用條件編譯,Gadget 驅動程式可以移植到幾種不同的控制器。(透過為許多面向批處理的驅動程式自動自動配置端點,最近的核心大大簡化了支援新硬體所涉及的工作。)Gadget 驅動程式的職責包括

  • 處理設定請求(ep0 協議響應),可能包括類特定的功能

  • 返回配置和字串描述符

  • (重新)設定配置和介面 altsetting,包括啟用和配置端點

  • 處理生命週期事件,例如管理與硬體的繫結、USB 掛起/恢復、遠端喚醒和與 USB 主機的斷開連線。

  • 管理所有當前已啟用端點上的 IN 和 OUT 傳輸

此類驅動程式可以是專有程式碼的模組,儘管 Linux 社群不鼓勵這種方法。

上層

大多數 gadget 驅動程式都有一個上邊界,該邊界連線到 Linux 中的某些 Linux 驅動程式或框架。資料透過該邊界流動,gadget 驅動程式透過 USB 上的協議傳輸來生成和/或消耗資料。示例包括

  • 使用者模式程式碼,在 /dev 中使用通用 (gadgetfs) 或特定於應用程式的檔案

  • 網路子系統(用於網路 gadget,例如 CDC 乙太網模型 gadget 驅動程式)

  • 資料捕獲驅動程式,可能是 video4Linux 或掃描器驅動程式;或測試和測量硬體。

  • 輸入子系統(用於 HID gadget)

  • 聲音子系統(用於音訊 gadget)

  • 檔案系統(用於 PTP gadget)

  • 塊 i/o 子系統(用於 usb-storage gadget)

  • ... 還有更多

其他層

可能存在其他層。這些層可能包括核心層,例如網路協議棧,以及構建在標準 POSIX 系統呼叫 API(例如 open()close()read()write())之上的使用者模式應用程式。在較新的系統上,POSIX 非同步 I/O 呼叫可能是一種選擇。此類使用者模式程式碼不一定受 GNU 通用公共許可證 (GPL) 的約束。

支援 OTG 的系統還需要包括一個標準的 Linux-USB 主機端堆疊,其中包含 usbcore、一個或多個主機控制器驅動程式 (HCD)、USB 裝置驅動程式以支援 OTG “目標外圍裝置列表”等等。還將有一個OTG 控制器驅動程式,只有 gadget 和裝置驅動程式開發人員才能間接看到該驅動程式。這有助於主機和裝置端 USB 控制器實現兩個新的 OTG 協議(HNP 和 SRP)。角色在 USB 掛起處理期間使用 HNP 切換(主機到外圍裝置,反之亦然),並且 SRP 可以被視為一種更省電的裝置喚醒協議。

隨著時間的推移,可重用的實用程式正在不斷發展,以幫助簡化某些 gadget 驅動程式任務。例如,現在可以自動從配置介面和端點的描述符向量構建配置描述符,並且許多驅動程式現在使用自動配置來選擇硬體端點並初始化其描述符。一個特別感興趣的潛在示例是為 HID、網路、儲存或音訊類實現標準 USB-IF 協議的程式碼。一些開發人員對 KDB 或 KGDB 掛鉤感興趣,以允許遠端除錯目標硬體。大多數此類 USB 協議程式碼不需要特定於硬體,就像 X11、HTTP 或 NFS 等網路協議一樣。此類 gadget 端介面驅動程式最終應該組合起來,以實現複合裝置。

核心模式 Gadget API

Gadget 驅動程式透過 struct usb_gadget_driver 宣告自己,它負責 struct usb_gadget 大部分的列舉。對 set_configuration 的響應通常涉及啟用 gadget 公開的一個或多個 struct usb_ep 物件,並提交一個或多個 struct usb_request 緩衝區來傳輸資料。瞭解這四種資料型別及其操作,您將瞭解此 API 的工作方式。

注意

除了“第 9 章”資料型別之外,大多數重要的資料型別和函式都在此處描述。

但是,您正在閱讀的內容可能遺漏了一些相關資訊。端點自動配置就是此類資訊的一個示例。您必須閱讀標頭檔案並使用示例原始碼(例如“Gadget Zero”的原始碼)才能完全理解 API。

API 中實現某些基本驅動程式功能的部分特定於正在使用的 Linux 核心版本。2.6 及更高版本的核心包含一個驅動程式模型框架,該框架在早期核心上沒有類似物;因此,gadget API 的這些部分並非完全可移植。(它們在 2.4 核心上實現,但方式不同。)驅動程式模型狀態是 kerneldoc 工具忽略的此 API 的另一部分。

核心 API 不公開所有可能的硬體功能,只公開最廣泛可用的功能。存在重要的硬體功能,例如裝置到裝置 DMA(無需在記憶體緩衝區中進行臨時儲存),將使用特定於硬體的 API 新增這些功能。

此 API 允許驅動程式使用條件編譯來處理不同硬體的端點功能,但不要求這樣做。硬體往往具有任意限制,涉及傳輸型別、定址、資料包大小、緩衝和可用性。通常,此類差異僅對處理裝置配置和管理的“端點零”邏輯有意義。API 透過端點的命名約定支援有限的執行時功能檢測。許多驅動程式將能夠至少部分地自動配置自己。特別是,驅動程式 init 部分通常具有端點自動配置邏輯,該邏輯掃描硬體的端點列表以查詢與驅動程式要求匹配的端點(依賴於這些約定),以消除條件編譯的一些最常見原因。

與 Linux-USB 主機端 API 類似,此 API 公開了 USB 訊息的“塊狀”性質:I/O 請求以一個或多個“資料包”為單位,並且資料包邊界對驅動程式可見。與 RS-232 序列協議相比,USB 更類似於同步協議(例如 HDLC)(每幀 N 個位元組、多點定址、主機作為主站,裝置作為從站),而不是非同步協議(tty 樣式:每幀 8 個數據位、無奇偶校驗、一個停止位)。因此,例如,控制器驅動程式不會將兩個單位元組寫入緩衝到單個雙位元組 USB IN 資料包中,儘管 gadget 驅動程式在實現資料包邊界(和“短資料包”)不重要的協議時可能會這樣做。

驅動程式生命週期

Gadget 驅動程式向硬體發出端點 I/O 請求,而無需瞭解硬體的許多細節,但驅動程式設定/配置程式碼需要處理一些差異。像這樣使用 API

  1. 為特定的裝置端 usb 控制器硬體註冊一個驅動程式,例如 PCI 上的 net2280 (USB 2.0)、Linux PDA 中發現的 sa11x0 或 pxa25x 等。此時,裝置在邏輯上處於 USB ch9 初始狀態 (attached),不耗電且不可用(因為它尚不支援列舉)。任何主機都不應看到該裝置,因為它尚未啟用主機用於檢測裝置的資料線拉高,即使 VBUS 電源可用。

  2. 註冊一個實現某些更高級別裝置功能的 gadget 驅動程式。然後,它將 bind() 到 usb_gadget,後者會在檢測到 VBUS 後的某個時間啟用資料線拉高。

  3. 硬體驅動程式現在可以開始列舉。它處理的步驟是接受 USB powerset_address 請求。其他步驟由 gadget 驅動程式處理。如果在主機開始列舉之前解除安裝 gadget 驅動程式模組,則會跳過步驟 7 之前的步驟。

  4. gadget 驅動程式的 setup() 呼叫返回 usb 描述符,該描述符既基於匯流排介面硬體提供的功能,又基於正在實現的功能。除非硬體阻止此類操作,否則這可能涉及備用設定或配置。對於 OTG 裝置,每個配置描述符都包含一個 OTG 描述符。

  5. gadget 驅動程式處理列舉的最後一步,當 USB 主機發出 set_configuration 呼叫時。它會啟用該配置中使用的所有端點,以及所有介面中的預設設定。這涉及使用硬體的端點列表,並根據其描述符啟用每個端點。它還可能涉及使用 usb_gadget_vbus_draw 以允許從 VBUS 汲取更多電力,如該配置所允許的那樣。對於 OTG 裝置,設定配置還可能涉及透過使用者介面報告 HNP 功能。

  6. 執行實際工作並執行資料傳輸,可能涉及更改介面設定或切換到新配置,直到裝置從主機斷開 disconnect() 連線。將任意數量的傳輸請求排隊到每個端點。在斷開連線之前,可能會掛起和恢復多次。斷開連線時,驅動程式將返回到步驟 3(如上所述)。

  7. 解除安裝 gadget 驅動程式模組時,會發出驅動程式 unbind() 回撥。這允許解除安裝控制器驅動程式。

驅動程式通常會進行安排,以便僅載入 gadget 驅動程式模組(或將其靜態連結到 Linux 核心中)就可以列舉外圍裝置,但某些驅動程式會將列舉推遲到某些更高級別的元件(例如使用者模式守護程式)啟用它為止。請注意,在此最低級別,沒有關於如何實現 ep0 配置邏輯的策略,除非它應遵守 USB 規範。此類問題屬於 gadget 驅動程式的範圍,包括瞭解某些 USB 控制器施加的實現約束或瞭解複合裝置可能透過整合可重用元件構建。

請注意,OTG 裝置的上述生命週期可能略有不同。除了在每個配置中提供一個額外的 OTG 描述符之外,只有與 HNP 相關的差異對驅動程式程式碼特別可見。它們涉及在 SET_CONFIGURATION 請求期間報告要求,以及在某些掛起回撥期間呼叫 HNP 的選項。此外,SRP 略微更改了 usb_gadget_wakeup 的語義。

USB 2.0 第 9 章型別和常量

Gadget 驅動程式依賴於 linux/usb/ch9.h 標頭檔案中定義的通用 USB 結構和常量,該檔案是 Linux 2.6+ 核心中的標準。這些是主機端驅動程式(和 usbcore)使用的相同型別和常量。

核心物件和方法

這些宣告在 <linux/usb/gadget.h> 中,gadget 驅動程式使用它們與 USB 外圍控制器驅動程式互動。

struct usb_request

描述一個 i/o 請求

定義:

struct usb_request {
    void *buf;
    unsigned length;
    dma_addr_t dma;
    struct scatterlist      *sg;
    unsigned num_sgs;
    unsigned num_mapped_sgs;
    unsigned stream_id:16;
    unsigned is_last:1;
    unsigned no_interrupt:1;
    unsigned zero:1;
    unsigned short_not_ok:1;
    unsigned dma_mapped:1;
    unsigned sg_was_mapped:1;
    void (*complete)(struct usb_ep *ep, struct usb_request *req);
    void *context;
    struct list_head        list;
    unsigned frame_number;
    int status;
    unsigned actual;
};

成員

buf

用於資料的緩衝區。始終提供此緩衝區;某些控制器僅使用 PIO,或者不將 DMA 用於某些端點。

length

該資料的長度

dma

與“buf”對應的 DMA 地址。如果您未設定此欄位,並且 usb 控制器需要一個地址,則它負責對映和取消對映緩衝區。

sg

用於支援 SG 的控制器的 scatterlist。

num_sgs

SG 條目的數量

num_mapped_sgs

對映到 DMA 的 SG 條目數(內部)

stream_id

流 ID,當使用 USB3.0 批次流時

is_last

指示這是否是在切換到其他流之前的 stream_id 的最後一個請求(DWC3 控制器需要)。

no_interrupt

如果為 true,則提示不需要完成 irq。有時在使用由 DMA 控制器直接處理的深度請求佇列時很有幫助。

zero

如果為 true,則在寫入資料時,透過根據需要新增零長度資料包來使最後一個數據包“短”;

short_not_ok

讀取資料時,使短資料包被視為錯誤(佇列停止前進,直到清理)。

dma_mapped

指示請求是否已對映到 DMA(內部)

sg_was_mapped

如果 scatterlist 在請求之前已對映,則設定

complete

請求完成時呼叫的函式,以便可以重新使用此請求及其緩衝區。該函式將始終在停用中斷的情況下呼叫,並且不得休眠。讀取以短資料包或緩衝區填滿時終止,以先發生者為準。當寫入終止時,一些資料位元組通常仍在傳輸中(通常在硬體 fifo 中)。錯誤(對於讀取或寫入)會阻止佇列前進,直到完成函式返回,以便首先取消佇列由錯誤失效的任何傳輸。

context

供完成回撥使用

list

供 gadget 驅動程式使用。

frame_number

報告以(微)幀為單位傳輸或接收同步傳輸的間隔號。

status

報告完成程式碼,零或負 errno。通常,故障會阻止傳輸佇列前進,直到完成回撥返回。程式碼“-ESHUTDOWN”指示由裝置斷開連線引起的完成,或者在驅動程式停用端點時引起的完成。

actual

報告從緩衝區傳輸到緩衝區或從緩衝區傳輸的位元組數。對於讀取(OUT 傳輸),這可能小於請求的長度。如果設定了 short_not_ok 標誌,即使狀態另有指示成功完成,短讀取也會被視為錯誤。請注意,對於寫入(IN 傳輸),當請求報告為完成時,某些資料位元組可能仍駐留在裝置端 FIFO 中。

描述

這些透過它們使用的端點分配/釋放。硬體的驅動程式可以向其返回的記憶體中新增額外的每個請求資料,這通常避免了單獨的記憶體分配(潛在的故障),稍後在請求排隊時。

請求標誌會影響請求處理,例如是否寫入零長度資料包(“zero”標誌)、是否應將短讀取視為錯誤(阻止請求佇列前進,“short_not_ok”標誌)或提示不需要中斷(“no_interrupt”標誌,用於深度請求佇列)。

批次端點可以使用任何大小的緩衝區,也可以用於中斷傳輸。僅中斷端點的功能要少得多。

注意

這類似於主機端的 'struct urb',只是它更薄並促進了更多的預分配。

struct usb_ep_caps

端點功能描述

定義:

struct usb_ep_caps {
    unsigned type_control:1;
    unsigned type_iso:1;
    unsigned type_bulk:1;
    unsigned type_int:1;
    unsigned dir_in:1;
    unsigned dir_out:1;
};

成員

type_control

端點支援控制型別(為 ep0 保留)。

type_iso

端點支援同步傳輸。

type_bulk

端點支援批次傳輸。

type_int

端點支援中斷傳輸。

dir_in

端點支援 IN 方向。

dir_out

端點支援 OUT 方向。

struct usb_ep

USB 端點的裝置端表示

定義:

struct usb_ep {
    void *driver_data;
    const char              *name;
    const struct usb_ep_ops *ops;
    const struct usb_endpoint_descriptor    *desc;
    const struct usb_ss_ep_comp_descriptor  *comp_desc;
    struct list_head        ep_list;
    struct usb_ep_caps      caps;
    bool claimed;
    bool enabled;
    unsigned mult:2;
    unsigned maxburst:5;
    u8 address;
    u16 maxpacket;
    u16 maxpacket_limit;
    u16 max_streams;
};

成員

driver_data

供 gadget 驅動程式使用。

name

端點的識別符號,例如“ep-a”或“ep9in-bulk”

ops

用於訪問特定於硬體的操作的函式指標。

desc

端點描述符。此指標在啟用端點之前設定,並且在停用端點之前保持有效。

comp_desc

如果支援 SuperSpeed,這是用於配置端點的端點配套描述符

ep_list

gadget 的 ep_list 儲存其所有端點

caps

描述端點支援的型別和方向的結構。

claimed

如果此端點已被函式宣告,則為 True。

enabled

當前端點啟用/停用狀態。

mult

乘數,SS Isoc EP 的“mult”值

maxburst

此 EP 支援的最大突發數(對於 usb3)

address

用於在查詢與連線速度匹配的描述符時標識端點

maxpacket

此端點上使用的最大資料包大小。根據用於配置端點的端點描述符,有時可以減小初始值(允許硬體)。

maxpacket_limit

此端點可以處理的最大資料包大小值。它由 UDC 驅動程式在初始化端點時設定一次,並且不應更改。不應與 maxpacket 混淆。

max_streams

此 EP 支援的最大流數(0 - 16,實際數字為 2^n)

描述

匯流排控制器驅動程式在 gadget->ep_list 中列出所有通用端點。控制端點 (gadget->ep0) 不在該列表中,並且僅響應驅動程式 setup() 回撥進行訪問。

struct usb_gadget

表示 USB 裝置

定義:

struct usb_gadget {
    struct work_struct              work;
    struct usb_udc                  *udc;
    const struct usb_gadget_ops     *ops;
    struct usb_ep                   *ep0;
    struct list_head                ep_list;
    enum usb_device_speed           speed;
    enum usb_device_speed           max_speed;
    enum usb_ssp_rate               ssp_rate;
    enum usb_ssp_rate               max_ssp_rate;
    enum usb_device_state           state;
    const char                      *name;
    struct device                   dev;
    unsigned isoch_delay;
    unsigned out_epnum;
    unsigned in_epnum;
    unsigned mA;
    struct usb_otg_caps             *otg_caps;
    unsigned sg_supported:1;
    unsigned is_otg:1;
    unsigned is_a_peripheral:1;
    unsigned b_hnp_enable:1;
    unsigned a_hnp_support:1;
    unsigned a_alt_hnp_support:1;
    unsigned hnp_polling_support:1;
    unsigned host_request_flag:1;
    unsigned quirk_ep_out_aligned_size:1;
    unsigned quirk_altset_not_supp:1;
    unsigned quirk_stall_not_supp:1;
    unsigned quirk_zlp_not_supp:1;
    unsigned quirk_avoids_skb_reserve:1;
    unsigned is_selfpowered:1;
    unsigned deactivated:1;
    unsigned connected:1;
    unsigned lpm_capable:1;
    unsigned wakeup_capable:1;
    unsigned wakeup_armed:1;
    int irq;
    int id_number;
};

成員

work

(內部使用)用於 sysfs_notify() 的工作佇列

udc

此 gadget 的 struct usb_udc 指標

ops

用於訪問特定於硬體的操作的函式指標。

ep0

端點零,用於讀取或寫入對驅動程式 setup() 請求的響應

ep_list

裝置支援的其他端點的列表。

speed

當前連線到 USB 主機的速度。

max_speed

UDC 可以處理的最大速度。UDC 必須支援此速度和所有較慢的速度。

ssp_rate

當前連線的 SuperSpeed Plus 訊號速率和通道數。

max_ssp_rate

UDC 可以處理的最大 SuperSpeed Plus 訊號速率和通道數。UDC 必須支援此速度和所有較慢的速度和較低的通道數。

state

我們現在的狀態(已連線、已掛起、已配置等)

name

標識控制器硬體型別。用於診斷,有時用於配置。

dev

此抽象裝置的驅動程式模型狀態。

isoch_delay

來自 Set Isoch Delay 請求的值。僅在 SS/SSP 上有效

out_epnum

上次使用的輸出 ep 號

in_epnum

上次使用的輸入 ep 號

mA

上次設定的 mA 值

otg_caps

此 gadget 的 OTG 功能。

sg_supported

如果我們可以處理分散-聚集,則為 true

is_otg

如果 USB 裝置埠使用 Mini-AB 插孔,則為 True,因此 gadget 驅動程式必須提供 USB OTG 描述符。

is_a_peripheral

除非 is_otg,否則為 False,“A”USB 電纜的一端位於 Mini-AB 插孔中,並且 HNP 已用於切換角色,因此“A”裝置當前充當 A-外圍裝置,而不是 A-主機。

b_hnp_enable

OTG 裝置功能標誌,指示 A-主機已啟用 HNP 支援。

a_hnp_support

OTG 裝置功能標誌,指示 A-主機在此埠上支援 HNP。

a_alt_hnp_support

OTG 裝置功能標誌,指示 A-主機僅在不同的根埠上支援 HNP。

hnp_polling_support

OTG 裝置功能標誌,指示外圍裝置模式下的 OTG 裝置是否可以支援 HNP 輪詢。

host_request_flag

OTG 裝置功能標誌,指示 A-外圍裝置或 B-外圍裝置是否要承擔主機角色。

quirk_ep_out_aligned_size

epout 需要將緩衝區大小與 MaxPacketSize 對齊。

quirk_altset_not_supp

UDC 控制器不支援 alt 設定。

quirk_stall_not_supp

UDC 控制器不支援停止。

quirk_zlp_not_supp

UDC 控制器不支援 ZLP。

quirk_avoids_skb_reserve

udc/平臺希望避免 u_ether.c 中的 skb_reserve(),以提高效能。

is_selfpowered

如果 gadget 是自供電的。

deactivated

如果 gadget 已停用,則為 True - 在停用狀態下,無法連線它。

connected

如果 gadget 已連線,則為 True。

lpm_capable

如果 gadget max_speed 為 FULL 或 HIGH,則此標誌指示它根據 LPM ECN 和勘誤表支援 LPM。

wakeup_capable

如果 gadget 能夠傳送遠端喚醒訊號,則為 True。

wakeup_armed

如果 gadget 由主機配置為遠端喚醒,則為 True。

irq

裝置控制器的中斷號。

id_number

用於確保 gadget 名稱不同的唯一 ID 號

描述

Gadget 具有一個主要可移植的“gadget 驅動程式”,該驅動程式實現了裝置功能,處理所有 USB 配置和介面。Gadget 驅動程式透過 ops 向量間接與特定於硬體的程式碼通訊。這使 gadget 驅動程式與硬體細節隔離,並透過通用 i/o 佇列打包硬體端點。“usb_gadget”和“usb_ep”介面提供與硬體的隔離。

除了驅動程式資料外,此結構中的所有欄位對於 gadget 驅動程式都是隻讀的。該驅動程式資料是 2.6(及更高版本)核心中的“驅動程式模型”基礎結構的一部分,對於早期系統,它被分組在一個類似的結構中,核心的其餘部分不知道該結構。

這三個 OTG 裝置功能標誌的值會在與 USB_REQ_SET_CONFIGURATION 對應的 setup() 呼叫之前以及驅動程式 suspend() 呼叫之前更新。它們僅在 is_otg 且裝置充當 B-外圍裝置時有效(因此 is_a_peripheral 為 false)。

size_t usb_ep_align(struct usb_ep *ep, size_t len)

返回與 ep 的 maxpacketsize 對齊的 len

引數

struct usb_ep *ep

用於對齊 len 的 maxpacketsize 的端點

size_t len

緩衝區大小的長度與 ep 的 maxpacketsize 對齊

描述

這個輔助函式用於將緩衝區大小與 ep 的 maxpacketsize 對齊。

size_t usb_ep_align_maybe(struct usb_gadget *g, struct usb_ep *ep, size_t len)

如果 gadget 需要 quirk_ep_out_aligned_size,則返回與 ep 的 maxpacketsize 對齊的 len,否則返回 len。

引數

struct usb_gadget *g

要檢查 quirk 的控制器

struct usb_ep *ep

用於對齊 len 的 maxpacketsize 的端點

size_t len

緩衝區大小的長度與 ep 的 maxpacketsize 對齊

描述

如果需要檢查並可能將緩衝區大小與 ep 的 maxpacketsize 對齊,則使用此輔助函式。

int gadget_is_altset_supported(struct usb_gadget *g)

如果硬體支援 altsetting,則返回 true

引數

struct usb_gadget *g

要檢查 quirk 的控制器

int gadget_is_stall_supported(struct usb_gadget *g)

如果硬體支援 stalling,則返回 true

引數

struct usb_gadget *g

要檢查 quirk 的控制器

int gadget_is_zlp_supported(struct usb_gadget *g)

如果硬體支援 zlp,則返回 true

引數

struct usb_gadget *g

要檢查 quirk 的控制器

int gadget_avoids_skb_reserve(struct usb_gadget *g)

如果硬體想要避免 skb_reserve 以提高效能,則返回 true。

引數

struct usb_gadget *g

要檢查 quirk 的控制器

int gadget_is_dualspeed(struct usb_gadget *g)

如果硬體處理高速,則返回 true

引數

struct usb_gadget *g

可能支援高速和全速的控制器

int gadget_is_superspeed(struct usb_gadget *g)

如果硬體處理超高速,則返回 true

引數

struct usb_gadget *g

可能支援超高速的控制器

int gadget_is_superspeed_plus(struct usb_gadget *g)

如果硬體處理超高速+,則返回 true

引數

struct usb_gadget *g

可能支援超高速+的控制器

int gadget_is_otg(struct usb_gadget *g)

如果硬體已準備好 OTG,則返回 true

引數

struct usb_gadget *g

可能具有 Mini-AB 聯結器的控制器

描述

這是一個執行時測試,因為具有 USB-OTG 堆疊的核心有時在只有 Mini-B(或 Mini-A)聯結器的板上執行。

struct usb_gadget_driver

usb gadget 裝置的驅動程式

定義:

struct usb_gadget_driver {
    char *function;
    enum usb_device_speed   max_speed;
    int (*bind)(struct usb_gadget *gadget, struct usb_gadget_driver *driver);
    void (*unbind)(struct usb_gadget *);
    int (*setup)(struct usb_gadget *, const struct usb_ctrlrequest *);
    void (*disconnect)(struct usb_gadget *);
    void (*suspend)(struct usb_gadget *);
    void (*resume)(struct usb_gadget *);
    void (*reset)(struct usb_gadget *);
    struct device_driver    driver;
    char *udc_name;
    unsigned match_existing_only:1;
    bool is_bound:1;
};

成員

function

描述 gadget 功能的字串

max_speed

驅動程式處理的最高速度。

bind

驅動程式的 bind 回撥

unbind

當驅動程式從 gadget 解綁時呼叫,通常來自 rmmod(在報告斷開連線之後)。在允許休眠的上下文中呼叫。

setup

針對硬體級別驅動程式未處理的 ep0 控制請求呼叫。 大多數呼叫必須由 gadget 驅動程式處理,包括描述符和配置管理。設定資料的 16 位成員採用 USB 位元組順序。 在中斷中呼叫; 這可能不會休眠。 驅動程式將響應排隊到 ep0,或返回負值以停止。

disconnect

在所有傳輸停止後,當主機斷開連線時呼叫。 可以在中斷中呼叫; 這可能不會休眠。 有些裝置無法檢測到斷開連線,因此可能不會呼叫此方法,除非作為控制器關閉的一部分。

suspend

在 USB 掛起時呼叫。 可以在中斷中呼叫。

resume

在 USB 恢復時呼叫。 可以在中斷中呼叫。

reset

在 USB 匯流排重置時呼叫。 對於所有 gadget 驅動程式都是強制性的,應在中斷中呼叫。

driver

此驅動程式的驅動程式模型狀態。

udc_name

此驅動程式應繫結到的 UDC 的名稱。 如果 udc_name 為 NULL,則此驅動程式將繫結到任何可用的 UDC。

match_existing_only

如果未找到 udc,則返回錯誤並使驅動程式註冊失敗

is_bound

允許驅動程式僅繫結到一個 gadget

描述

裝置將被停用,直到 gadget 驅動程式成功 bind(),這意味著驅動程式將處理列舉所需的 setup() 請求(並滿足“第 9 章”的要求),然後執行一些有用的工作。

如果 gadget->is_otg 為 true,則 gadget 驅動程式必須在列舉期間提供 OTG 描述符,否則 bind() 呼叫將失敗。 在這種情況下,在 bind() 返回之前沒有呼叫 usb_gadget_disconnect() 並且 USB 主機堆疊已初始化之前,可能不會有 USB 流量流動。

驅動程式使用特定於硬體的知識來配置 usb 硬體。 端點定址只是 ep0 實現從 setup() 呼叫返回的描述符中的幾個硬體特性之一。

除了 ep0 實現之外,大多數驅動程式程式碼不需要更改即可在不同的 usb 控制器之上執行。 它將使用由 ep0 實現設定的端點。

usb 控制器驅動程式處理一些標準的 usb 請求。 其中包括 set_address 和裝置、介面和端點的特徵標誌(get_status、set_feature 和 clear_feature 請求)。

因此,驅動程式的 setup() 回撥必須始終實現所有 get_descriptor 請求,至少返回裝置描述符和配置描述符。 驅動程式必須確保端點描述符與任何硬體約束匹配。 某些硬體還會約束其他描述符。(pxa250 僅允許配置 1、2 或 3)。

驅動程式的 setup() 回撥還必須實現 set_configuration,並且還應實現 set_interface、get_configuration 和 get_interface。 設定配置(或介面)是應該啟用端點或(配置 0)關閉端點的地方。

gadget 驅動程式的 setup() 回撥不必在 setup() 呼叫中將響應排隊到 ep0,驅動程式可以在 setup() 返回後執行此操作。 UDC 驅動程式必須等到此類響應排隊後才能繼續控制傳輸的資料/狀態階段。

(請注意,僅支援預設控制端點。主機和裝置通常都不支援除 ep0 以外的控制流量。)

大多數裝置將忽略 USB 掛起/恢復操作,因此不會提供這些回撥。 但是,當主機不再指導這些活動時,某些裝置可能需要更改模式。 例如,可能需要重新啟用本地控制元件(按鈕、撥盤等),因為(遠端)主機無法再執行此操作; 或者可以清除錯誤狀態,以使裝置的行為與是否保持電源相同。

注意

目前,許多 UDC 驅動程式依賴於從 setup() 回撥返回的 USB_GADGET_DELAYED_STATUS,這是一個錯誤。 有關詳細資訊,請參閱 USB_GADGET_DELAYED_STATUS 旁邊的註釋。

int usb_gadget_register_driver_owner(struct usb_gadget_driver *driver, struct module *owner, const char *mod_name)

註冊 gadget 驅動程式

引數

struct usb_gadget_driver *driver

正在註冊的驅動程式

struct module *owner

驅動程式模組

const char *mod_name

驅動程式模組的構建名稱

Context

可以休眠

描述

在 gadget 驅動程式的模組初始化函式中呼叫此函式,以告知基礎 UDC 控制器驅動程式有關您的驅動程式的資訊。 在此註冊呼叫返回之前,將呼叫 bind() 函式將其繫結到 gadget。 預計 bind() 函式將在 init 部分中。

使用下面定義的宏而不是直接呼叫此函式。

int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)

登出 gadget 驅動程式

引數

struct usb_gadget_driver *driver

正在登出的驅動程式

Context

可以休眠

描述

在 gadget 驅動程式的模組清理函式中呼叫此函式,以告知基礎 usb 控制器您的驅動程式即將消失。 如果控制器已連線到 USB 主機,它將首先 disconnect()。 在此過程最終返回之前,還請求驅動程式 unbind() 並清理任何裝置狀態。 預計 unbind() 函式將在退出部分中,因此可能未連結到某些核心中。

struct usb_string

包裝 C 字串及其 USB ID

定義:

struct usb_string {
    u8 id;
    const char              *s;
};

成員

id

此字串的(非零)ID

s

字串,採用 UTF-8 編碼

描述

如果您使用 usb_gadget_get_string(),請使用此方法將字串與其 ID 一起包裝。

struct usb_gadget_strings

給定語言的一組 USB 字串

定義:

struct usb_gadget_strings {
    u16 language;
    struct usb_string       *strings;
};

成員

language

標識字串的語言(en-us 為 0x0409)

strings

帶有 ID 的字串陣列

描述

如果您使用 usb_gadget_get_string(),請使用此方法包裝給定語言的所有字串。

void usb_free_descriptors(struct usb_descriptor_header **v)

釋放由 usb_copy_descriptors() 返回的描述符

引數

struct usb_descriptor_header **v

描述符向量

可選實用程式

核心 API 足以編寫 USB Gadget 驅動程式,但提供了一些可選實用程式來簡化常見任務。 這些實用程式包括端點自動配置。

int usb_gadget_get_string(const struct usb_gadget_strings *table, int id, u8 *buf)

填寫字串描述符

引數

const struct usb_gadget_strings *table

使用 UTF-8 編碼的 C 字串

int id

字串 ID,來自 get string 描述符中 wValue 的低位元組

u8 *buf

至少 256 位元組,必須 16 位對齊

描述

查詢與 ID 匹配的 UTF-8 字串,並將其轉換為 utf16-le 中的字串描述符。 返回描述符的長度(始終為偶數)或負 errno

如果您的驅動程式需要多種語言的刺痛,您可能需要在 ep0 字串描述符邏輯中使用“switch (wIndex) { ... }”,在使用哪一組 UTF-8 字串後使用此例程。 請注意,US-ASCII 是 UTF-8 的嚴格子集; 第八位設定的任何字串位元組都將是多位元組 UTF-8 字元,而不是 ISO-8859/1 字元(也廣泛用於 C 字串中)。

bool usb_validate_langid(u16 langid)

驗證 usb 語言識別符號

引數

u16 langid

usb 語言識別符號

描述

對於有效的語言識別符號返回 true,否則返回 false。

int usb_descriptor_fillbuf(void *buf, unsigned buflen, const struct usb_descriptor_header **src)

用描述符填充緩衝區

引數

void *buf

要填充的緩衝區

unsigned buflen

buf 的大小

const struct usb_descriptor_header **src

描述符指標陣列,以空指標結尾。

描述

將描述符複製到緩衝區中,如果無法全部複製,則返回長度或負錯誤程式碼。 在為用作配置複合裝置一部分的關聯介面集組裝描述符時;或者在需要整理描述符集的情況。

int usb_gadget_config_buf(const struct usb_config_descriptor *config, void *buf, unsigned length, const struct usb_descriptor_header **desc)

構建完整的配置描述符

引數

const struct usb_config_descriptor *config

描述符的標頭,包括電源要求和介面數量等特徵。

void *buf

用於生成配置描述符的緩衝區。

unsigned length

緩衝區長度。 如果這不夠大以容納整個配置描述符,將返回錯誤程式碼。

const struct usb_descriptor_header **desc

指向定義此裝置配置中所有功能的描述符(介面、端點等)的空終止指標向量。

描述

這會將描述符複製到響應緩衝區中,從而為該配置構建描述符。 它返回緩衝區長度或負狀態程式碼。 config.wTotalLength 欄位設定為與結果的長度匹配,但其他描述符欄位(包括功耗和介面計數)必須由呼叫者設定。

Gadget 驅動程式可以在構造配置描述符以響應 USB_REQ_GET_DESCRIPTOR 時使用它。 如果需要 USB_DT_OTHER_SPEED_CONFIG,則需要修補生成的 bDescriptorType 值。

struct usb_descriptor_header **usb_copy_descriptors(struct usb_descriptor_header **src)

複製 USB 描述符的向量

引數

struct usb_descriptor_header **src

要複製的空終止向量

Context

初始化程式碼,可能會休眠

描述

這會複製 USB 描述符的向量。 它的主要用途是支援可以有多個副本的 usb_function 物件,每個副本都需要不同的描述符。 函式可能有描述符的靜態表,這些表用作模板,並根據給定的函式例項的需要使用識別符號(用於介面、字串、端點等)進行自定義。

複合裝置框架

核心 API 足以編寫複合 USB 裝置的驅動程式(在給定的配置中具有多個功能),以及多配置裝置(也具有多個功能,但不一定共享給定的配置)。 但是,有一個可選框架使其更容易重用和組合功能。

使用此框架的裝置提供一個 struct usb_composite_driver,它反過來提供一個或多個 struct usb_configuration 例項。 每個此類配置至少包括一個結構 usb_function,它打包了使用者可見的角色,例如“網路連結”或“大容量儲存裝置”。 管理功能也可能存在,例如“裝置韌體升級”。

struct usb_os_desc_ext_prop

描述一個“擴充套件屬性”

定義:

struct usb_os_desc_ext_prop {
    struct list_head        entry;
    u8 type;
    int name_len;
    char *name;
    int data_len;
    char *data;
    struct config_item      item;
};

成員

entry

用於儲存擴充套件屬性列表

type

擴充套件屬性型別

name_len

擴充套件屬性 unicode 名稱長度,包括終止符“0”

name

擴充套件屬性名稱

data_len

擴充套件屬性 Blob 的長度(對於 unicode 儲存,長度加倍)

data

擴充套件屬性 Blob

item

在 configfs 中表示此擴充套件屬性

struct usb_os_desc

描述與一個介面關聯的 OS 描述符

定義:

struct usb_os_desc {
    char *ext_compat_id;
    struct list_head        ext_prop;
    int ext_prop_len;
    int ext_prop_count;
    struct mutex            *opts_mutex;
    struct config_group     group;
    struct module           *owner;
};

成員

ext_compat_id

16 個位元組的“相容 ID”和“子相容 ID”

ext_prop

擴充套件屬性列表

ext_prop_len

擴充套件屬性 Blob 的總長度

ext_prop_count

擴充套件屬性的數量

opts_mutex

可選的互斥鎖,用於保護 usb_function_instance 的配置資料

group

表示與 configfs 中介面關聯的 OS 描述符

owner

與此 OS 描述符關聯的模組

struct usb_os_desc_table

描述與 usb_function 的一個介面關聯的 OS 描述符

定義:

struct usb_os_desc_table {
    int if_id;
    struct usb_os_desc      *os_desc;
};

成員

if_id

介面 ID

os_desc

介面的“擴充套件相容性 ID”和“擴充套件屬性”

描述

每個介面最多可以有一個“擴充套件相容性 ID”和多個“擴充套件屬性”。

struct usb_function

描述配置的一個功能

定義:

struct usb_function {
    const char                      *name;
    struct usb_gadget_strings       **strings;
    struct usb_descriptor_header    **fs_descriptors;
    struct usb_descriptor_header    **hs_descriptors;
    struct usb_descriptor_header    **ss_descriptors;
    struct usb_descriptor_header    **ssp_descriptors;
    struct usb_configuration        *config;
    struct usb_os_desc_table        *os_desc_table;
    unsigned os_desc_n;
    int (*bind)(struct usb_configuration *, struct usb_function *);
    void (*unbind)(struct usb_configuration *, struct usb_function *);
    void (*free_func)(struct usb_function *f);
    struct module           *mod;
    int (*set_alt)(struct usb_function *, unsigned interface, unsigned alt);
    int (*get_alt)(struct usb_function *, unsigned interface);
    void (*disable)(struct usb_function *);
    int (*setup)(struct usb_function *, const struct usb_ctrlrequest *);
    bool (*req_match)(struct usb_function *,const struct usb_ctrlrequest *, bool config0);
    void (*suspend)(struct usb_function *);
    void (*resume)(struct usb_function *);
    int (*get_status)(struct usb_function *);
    int (*func_suspend)(struct usb_function *, u8 suspend_opt);
    bool func_suspended;
    bool func_wakeup_armed;
};

成員

name

用於診斷,標識功能。

strings

字串表,按 bind() 期間分配的識別符號和控制請求中提供的語言 ID 鍵入

fs_descriptors

全速(或低速)描述符表,使用在 bind() 期間分配的介面和字串識別符號。 如果此指標為 null,則該功能在全速(或低速)下將不可用。

hs_descriptors

高速描述符表,使用在 bind() 期間分配的介面和字串識別符號。 如果此指標為 null,則該功能在高速下將不可用。

ss_descriptors

超高速描述符表,使用在 bind() 期間分配的介面和字串識別符號。 如果初始化後此指標為 null,則該功能在超高速下將不可用。

ssp_descriptors

超高速+描述符表,使用在 bind() 期間分配的介面和字串識別符號。 如果初始化後此指標為 null,則該功能在超高速+下將不可用。

config

在呼叫 usb_add_function() 時分配;這是與此功能關聯的配置。

os_desc_table

(介面 ID,OS 描述符)對的表。 該函式可以公開多個介面。 如果介面是 IAD 的成員,則只有 IAD 的第一個介面在表中具有其條目。

os_desc_n

os_desc_table 中的條目數

bind

在 gadget 可以註冊之前,其所有功能都 bind() 到可用資源,包括介面或類描述符中使用的字串和介面識別符號; 端點; I/O 緩衝區; 等等。

unbind

反轉 bind;作為登出新增此功能的驅動程式的副作用呼叫。

free_func

釋放 struct usb_function

mod

(內部)指向建立此結構的模組。

set_alt

(必需)重新配置 altsetting; 功能驅動程式可以在此時初始化 usb_ep.driver 資料(當使用時)。 請注意,將介面設定為其當前 altsetting 會重置介面狀態,並且所有介面都處於停用狀態。

get_alt

返回活動的 altsetting。 如果未提供此選項,則僅支援 altsetting 零。

disable

(必需)指示應停用該功能。 原因包括主機重置或重新配置 gadget 以及斷開連線。

setup

用於特定於介面的控制請求。

req_match

測試此功能是否可以處理給定的類請求。

suspend

當主機停止傳送 USB 流量時通知功能。

resume

當主機重新啟動 USB 流量時通知功能。

get_status

當接收者是介面時,將功能狀態作為 GetStatus() 請求的回覆返回。

func_suspend

在收到 SetFeature(FUNCTION_SUSPEND) 時要呼叫的回撥

func_suspended

指示該功能是否處於功能掛起狀態。

func_wakeup_armed

指示該功能是否已由主機武裝以進行喚醒訊號。

描述

單個 USB 功能使用一個或多個介面,並且在大多數情況下應支援在全速和高速下執行。 每個功能都由 usb_add_function() 與一個配置相關聯;該功能會導致呼叫 bind(),以便可以分配資源作為設定 gadget 驅動程式的一部分。 這些資源包括端點,應使用 usb_ep_autoconfig() 進行分配。

為了支援雙速操作,功能驅動程式為高速和全速操作提供描述符。 除非在不涉及批次端點的極少數情況下,否則每種速度都需要不同的端點描述符。

功能驅動程式選擇自己的策略來管理例項資料。 最簡單的策略只是宣告它是“靜態”的,這意味著該功能只能啟用一次。 如果需要在給定速度下的多個配置中公開該功能,則它需要支援多個 usb_function 結構(每個配置一個)。

更復雜的策略可能會將 usb_function 結構封裝在特定於驅動程式的例項結構中,以允許多次啟用。 多次啟用的一個示例可能是 CDC ACM 功能,該功能在同一配置中支援兩個或多個不同的例項,從而向 USB 主機提供多個獨立的邏輯資料鏈接。

struct usb_configuration

表示一個 gadget 配置

定義:

struct usb_configuration {
    const char                      *label;
    struct usb_gadget_strings       **strings;
    const struct usb_descriptor_header **descriptors;
    void (*unbind)(struct usb_configuration *);
    int (*setup)(struct usb_configuration *, const struct usb_ctrlrequest *);
    u8 bConfigurationValue;
    u8 iConfiguration;
    u8 bmAttributes;
    u16 MaxPower;
    struct usb_composite_dev        *cdev;
};

成員

label

用於診斷,描述配置。

strings

字串表,按在 bind() 期間分配的識別符號和控制請求中提供的語言 ID 鍵入。

descriptors

先於所有功能描述符的描述符表。 示例包括 OTG 和特定於供應商的描述符。

unbind

反轉 bind;作為登出新增此配置的驅動程式的副作用呼叫。

setup

用於委派不由標準裝置基礎結構處理或定向到特定介面的控制請求。

bConfigurationValue

複製到配置描述符中。

iConfiguration

複製到配置描述符中。

bmAttributes

複製到配置描述符中。

MaxPower

功耗,以 mA 為單位。 用於在考慮匯流排速度後計算配置描述符中的 bMaxPower。

cdev

usb_add_config() 在呼叫 bind() 之前分配;這是與此配置關聯的裝置。

描述

配置是圍繞功能驅動程式構建的 gadget 驅動程式的構建塊。 簡單的 USB gadget 只需要一個功能和一個配置,並透過始終提供相同的功能來處理雙速硬體。 稍微複雜的 gadget 可能在給定速度下具有多個單功能配置; 或者具有僅在一種速度下工作的配置。

根據定義,複合裝置是具有包含多個功能的配置的裝置。

usb_configuration 的生命週期包括分配、初始化上述欄位以及呼叫 usb_add_config() 以設定內部資料並將其繫結到特定裝置。 然後使用配置的 bind() 方法初始化所有功能,然後為它們呼叫 usb_add_function()

這些函式通常彼此獨立,但這不是強制性的。CDC WMC 裝置就是一個例子,其中函式通常依賴於其他函式,一些函式是其他函式的附屬。這種相互依賴性可以透過任何方式進行管理,只要所有的描述符在複合驅動程式從其 bind() 例程返回之前完成即可。

struct usb_composite_driver

將配置分組到一個 gadget 中

定義:

struct usb_composite_driver {
    const char                              *name;
    const struct usb_device_descriptor      *dev;
    struct usb_gadget_strings               **strings;
    enum usb_device_speed                   max_speed;
    unsigned needs_serial:1;
    int (*bind)(struct usb_composite_dev *cdev);
    int (*unbind)(struct usb_composite_dev *);
    void (*disconnect)(struct usb_composite_dev *);
    void (*suspend)(struct usb_composite_dev *);
    void (*resume)(struct usb_composite_dev *);
    struct usb_gadget_driver                gadget_driver;
};

成員

name

用於診斷,標識驅動程式。

dev

裝置的模板描述符,包括預設裝置識別符號。

strings

字串表,透過在 bind 期間分配的識別符號和控制請求中提供的語言 ID 進行鍵控。注意:第一個條目是預定義的。可能使用的第一個條目是 USB_GADGET_FIRST_AVAIL_IDX

max_speed

驅動程式支援的最高速度。

needs_serial

如果 gadget 需要使用者空間提供序列號,則設定為 1。如果未提供,將列印警告。

bind

(必需)用於分配跨整個裝置共享的資源,例如字串 ID,並使用 usb_add_config() 新增其配置。這可能會因返回負的 errno 值而失敗;成功初始化時應返回零。

unbind

反轉 bind;作為登出此驅動程式的副作用呼叫。

disconnect

可選的驅動程式斷開連線方法

suspend

在函式通知後,當主機停止傳送 USB 流量時通知

resume

在函式通知之前,當主機重新啟動 USB 流量時通知配置

gadget_driver

控制此驅動程式的 Gadget 驅動程式

描述

裝置預設為報告自供電操作。依賴於匯流排供電操作的裝置應在其 bind 方法中報告這一點。

在從 bind 返回之前,可以覆蓋模板描述符中的各種欄位。這些欄位包括通常繫結相應主機端驅動程式的 idVendor/idProduct/bcdDevice 值,以及通常用於提供使用者有意義的裝置識別符號的三個字串(iManufacturer、iProduct、iSerialNumber)。(除非在 devstrings 中定義了字串,否則不會定義字串。)還會報告正確的 ep0 maxpacket 大小,如底層控制器驅動程式所定義。

module_usb_composite_driver

module_usb_composite_driver (__usb_composite_driver)

用於註冊 USB gadget 複合驅動程式的輔助宏

引數

__usb_composite_driver

usb_composite_driver 結構

描述

用於 USB gadget 複合驅動程式的輔助宏,這些驅動程式在模組 init/exit 中不做任何特殊處理。這消除了大量的樣板程式碼。每個模組只能使用此宏一次,呼叫它會替換 module_init()module_exit()

struct usb_composite_dev

表示一個複合 usb gadget

定義:

struct usb_composite_dev {
    struct usb_gadget               *gadget;
    struct usb_request              *req;
    struct usb_request              *os_desc_req;
    struct usb_configuration        *config;
    u8 qw_sign[OS_STRING_QW_SIGN_LEN];
    u8 b_vendor_code;
    struct usb_configuration        *os_desc_config;
    unsigned int                    use_os_string:1;
    u16 bcd_webusb_version;
    u8 b_webusb_vendor_code;
    char landing_page[WEBUSB_URL_RAW_MAX_LENGTH];
    unsigned int                    use_webusb:1;
    unsigned int                    setup_pending:1;
    unsigned int                    os_desc_pending:1;
};

成員

gadget

只讀,抽象了 gadget 的 usb 外圍控制器

req

用於控制響應;緩衝區是預先分配的

os_desc_req

用於 OS 描述符響應;緩衝區是預先分配的

config

當前活動的配置

qw_sign

OS 字串的 qwSignature 部分

b_vendor_code

OS 字串的 bMS_VendorCode 部分

os_desc_config

要與 OS 描述符一起使用的配置

use_os_string

預設為 false,感興趣的 gadget 會設定它

bcd_webusb_version

預設為 0x0100,WebUSB 規範版本

b_webusb_vendor_code

預設為 0x0,WebUSB 的供應商程式碼

landing_page

預設為空,WebUSB 中要公佈的著陸頁

use_webusb

預設為 false,感興趣的 gadget 會設定它

setup_pending

當設定請求排隊但未完成時為 true

os_desc_pending

當 os_desc 請求排隊但未完成時為 true

描述

在呼叫關聯裝置驅動程式的 bind() 之前,會分配和初始化其中一個裝置。

int config_ep_by_speed_and_alt(struct usb_gadget *g, struct usb_function *f, struct usb_ep *_ep, u8 alt)

根據 gadget 速度配置給定的端點。

引數

struct usb_gadget *g

指向 gadget 的指標

struct usb_function *f

usb 函式

struct usb_ep *_ep

要配置的端點

u8 alt

備用設定編號

返回

錯誤程式碼,成功時為 0

描述

此函式根據 gadget 速度為給定的端點選擇正確的描述符,並將其儲存在端點 desc 欄位中。如果端點已經分配了一個描述符,則使用當前相應的描述符覆蓋它。端點 maxpacket 欄位根據所選描述符進行更新。

注意

提供的函式應儲存支援速度的所有描述符

int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f, struct usb_ep *_ep)

根據 gadget 速度配置給定的端點。

引數

struct usb_gadget *g

指向 gadget 的指標

struct usb_function *f

usb 函式

struct usb_ep *_ep

要配置的端點

返回

錯誤程式碼,成功時為 0

描述

此函式根據 gadget 速度為給定的端點選擇正確的描述符,並將其儲存在端點 desc 欄位中。如果端點已經分配了一個描述符,則使用當前相應的描述符覆蓋它。端點 maxpacket 欄位根據所選描述符進行更新。

注意

提供的函式應儲存支援速度的所有描述符

int usb_add_function(struct usb_configuration *config, struct usb_function *function)

將函式新增到配置

引數

struct usb_configuration *config

配置

struct usb_function *function

要新增的函式

Context

在 gadget 設定期間單執行緒

描述

初始化後,每個配置必須新增一個或多個函式。新增函式涉及呼叫其 bind() 方法來分配資源,例如介面和字串識別符號以及端點。

此函式返回函式的 bind() 值,成功時為零,否則為負的 errno 值。

int usb_function_deactivate(struct usb_function *function)

阻止函式和 gadget 列舉

引數

struct usb_function *function

尚未準備好響應的函式

描述

透過阻止啟用資料線上拉來阻止 gadget 驅動程式對主機列舉的響應。這通常在 bind() 處理期間呼叫,以從初始的“準備好響應”狀態更改,或者在所需的資源可用時呼叫。

例如,作為使用者空間守護程式的傳遞的驅動程式可以阻止列舉,除非該守護程式(例如 OBEX、MTP 或列印伺服器)已準備好處理主機請求。

並非所有系統都支援對其 USB 外圍資料上拉的軟體控制。

成功時返回零,否則返回負的 errno。

int usb_function_activate(struct usb_function *function)

允許函式和 gadget 列舉

引數

struct usb_function *function

呼叫 usb_function_activate() 的函式

描述

反轉 usb_function_deactivate() 的效果。如果沒有更多的函式延遲其啟用,gadget 驅動程式將響應主機列舉過程。

成功時返回零,否則返回負的 errno。

int usb_interface_id(struct usb_configuration *config, struct usb_function *function)

分配一個未使用的介面 ID

引數

struct usb_configuration *config

與介面關聯的配置

struct usb_function *function

處理介面的函式

Context

在 gadget 設定期間單執行緒

描述

usb_interface_id() 從 usb_function.bind() 回撥中呼叫,以分配新的介面 ID。然後,函式驅動程式會將該 ID 儲存在介面、關聯、CDC 聯合和其他描述符中。它還會處理以該介面為目標的任何控制請求,特別是透過 set_alt() 更改其備用設定。還可能存在要處理的特定於類或特定於供應商的請求。

應使用此例程分配所有介面識別符號,以確保例如不同的函式不會錯誤地將不同的含義分配給同一個識別符號。請注意,由於介面識別符號是特定於配置的,因此在多個配置中使用(或在給定配置中使用多次)的函式需要相關描述符的多個版本。

返回已分配的介面 ID;如果無法分配更多介面 ID,則返回 -ENODEV。

int usb_func_wakeup(struct usb_function *func)

向主機發送函式喚醒通知。

引數

struct usb_function *func

傳送遠端喚醒通知的函式。

描述

適用於以增強的超速執行的裝置,當 usb 函式處於函式掛起狀態併為函式遠端喚醒做好準備時。完成後,將傳送函式喚醒通知。如果裝置處於低功耗狀態,它會嘗試在傳送喚醒通知之前將裝置置於活動狀態。由於這是一個同步呼叫,因此呼叫者必須注意不要在中斷上下文中呼叫它。對於以較低速度執行的裝置,返回負的 errno。

成功時返回零,否則返回負的 errno。

int usb_add_config(struct usb_composite_dev *cdev, struct usb_configuration *config, int (*bind)(struct usb_configuration*))

將配置新增到裝置。

引數

struct usb_composite_dev *cdev

包裝 USB gadget

struct usb_configuration *config

配置,已分配 bConfigurationValue

int (*bind)(struct usb_configuration *)

配置的 bind 函式

Context

在 gadget 設定期間單執行緒

描述

複合 bind() 例程的主要任務之一是使用此例程新增它支援的每個配置。

此函式返回配置的 bind() 的值,成功時為零,否則為負的 errno 值。繫結配置分配全域性資源,包括字串 ID,以及每個配置的資源,例如介面 ID 和端點。

int usb_string_id(struct usb_composite_dev *cdev)

分配一個未使用的字串 ID

引數

struct usb_composite_dev *cdev

要分配其字串描述符 ID 的裝置

Context

在 gadget 設定期間單執行緒

描述

usb_string_id() 從 bind() 回撥中呼叫,以分配字串 ID。然後,函式、配置或 gadget 的驅動程式會將該 ID 儲存在相應的描述符和字串表中。

應使用此例程、usb_string_ids_tab()usb_string_ids_n() 例程分配所有字串識別符號,以確保例如不同的函式不會錯誤地將不同的含義分配給同一個識別符號。

int usb_string_ids_tab(struct usb_composite_dev *cdev, struct usb_string *str)

批次分配未使用的字串 ID

引數

struct usb_composite_dev *cdev

要分配其字串描述符 ID 的裝置

struct usb_string *str

要分配數字的 usb_string 物件陣列

Context

在 gadget 設定期間單執行緒

描述

usb_string_ids() 從 bind() 回撥中呼叫,以分配字串 ID。然後,函式、配置或 gadget 的驅動程式會將 ID 從字串表複製到其他語言的相應描述符和字串表。

應使用此例程、usb_string_id()usb_string_ids_n() 例程分配所有字串識別符號,以確保例如不同的函式不會錯誤地將不同的含義分配給同一個識別符號。

struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev, struct usb_gadget_strings **sp, unsigned n_strings)

將 gadget 字串附加到 cdev 並分配 ID

引數

struct usb_composite_dev *cdev

要分配和附加其字串描述符 ID 的裝置。

struct usb_gadget_strings **sp

要附加的 usb_gadget_strings 陣列。

unsigned n_strings

每個 usb_strings 陣列 (sp[]->strings) 中的條目數

描述

此函式將建立 usb_gadget_strings 和 usb_string 的深層副本,並將其附加到 cdev。實際字串 (usb_string.s) 不會被複制,只會建立一個引用。 struct usb_gadget_strings 陣列可能包含多種語言,應以 NULL 結尾。每個 struct usb_gadget_strings 的 ->language 指標必須包含相同數量的條目。例如:sp[0] 是 en-US,sp[1] 是 es-ES。預計 es-ES 的第一個 usb_string 條目包含 en-US 的第一個 usb_string 條目的翻譯。因此,兩個條目都成為相同的 ID 分配。

int usb_string_ids_n(struct usb_composite_dev *c, unsigned n)

批次分配未使用的字串 ID

引數

struct usb_composite_dev *c

要分配其字串描述符 ID 的裝置

unsigned n

要分配的字串 ID 數

Context

在 gadget 設定期間單執行緒

描述

返回第一個請求的 ID。此 ID 和下一個 n-1 個 ID 現在是有效的 ID。至少在 n 為非零的情況下提供,因為如果是,則返回最後一個請求的 ID,這是現在非常有用的資訊。

usb_string_ids_n() 從 bind() 回撥中呼叫,以分配字串 ID。然後,函式、配置或 gadget 的驅動程式會將該 ID 儲存在相應的描述符和字串表中。

應使用此例程、usb_string_id()usb_string_ids_n() 例程分配所有字串識別符號,以確保例如不同的函式不會錯誤地將不同的含義分配給同一個識別符號。

int usb_composite_probe(struct usb_composite_driver *driver)

註冊一個複合驅動程式

引數

struct usb_composite_driver *driver

要註冊的驅動程式

Context

在 gadget 設定期間單執行緒

描述

此函式用於使用複合驅動程式框架註冊驅動程式。返回值是零,或者是一個負的 errno 值。這些值通常來自驅動程式的 bind 方法,該方法完成了設定驅動程式以匹配硬體的所有工作。

成功返回後,gadget 已準備好響應來自主機的請求,除非其元件之一在繫結時呼叫 usb_gadget_disconnect()。這通常是為了等待某些使用者空間的參與而完成的。

void usb_composite_unregister(struct usb_composite_driver *driver)

登出一個複合驅動程式

引數

struct usb_composite_driver *driver

要登出的驅動程式

描述

此函式用於使用複合驅動程式框架登出驅動程式。

void usb_composite_setup_continue(struct usb_composite_dev *cdev)

繼續控制傳輸

引數

struct usb_composite_dev *cdev

被保持等待的複合裝置

描述

如果 USB 函式驅動程式已請求延遲資料/狀態階段,則必須呼叫此函式以繼續控制傳輸的資料/狀態階段。USB 函式的設定處理程式(例如 set_alt())可以透過返回 USB_GADGET_DELAYED_STATUS 來請求複合框架延遲設定請求的資料/狀態階段。

複合裝置函式

在撰寫本文時,一些當前的 gadget 驅動程式已轉換為此框架。近期計劃包括轉換所有驅動程式,除了 gadgetfs 之外。

外圍控制器驅動程式

第一個支援此 API 的硬體是 NetChip 2280 控制器,它支援 USB 2.0 高速,並且基於 PCI。這是 net2280 驅動程式模組。該驅動程式支援 Linux 核心版本 2.4 和 2.6;請聯絡 NetChip Technologies 獲取開發板和產品資訊。

gadget 框架中工作的其他硬體包括:Intel 的 PXA 25x 和 IXP42x 系列處理器 (pxa2xx_udc)、Toshiba TC86c001 “Goku-S” (goku_udc)、Renesas SH7705/7727 (sh_udc)、MediaQ 11xx (mq11xx_udc)、Hynix HMS30C7202 (h7202_udc)、National 9303/4 (n9604_udc)、Texas Instruments OMAP (omap_udc)、Sharp LH7A40x (lh7a40x_udc) 等。其中大多數是全速控制器。

在撰寫本文時,有人正在此框架中開發其他幾種 USB 裝置控制器的驅動程式,並計劃使其中許多驅動程式廣泛可用。

一個部分 USB 模擬器,dummy_hcd 驅動程式,可用。它可以像 net2280、pxa25x 或 sa11x0 一樣,在可用端點和裝置速度方面;並且它模擬控制、批次,並在一定程度上模擬中斷傳輸。這使您可以在沒有特殊硬體的普通 PC 上開發 gadget 驅動程式的某些部分,並且可能借助 GDB 等工具在使用者模式 Linux 中執行。至少有一個人表示有興趣調整該方法,將其連線到微控制器的模擬器。這種模擬器可以幫助除錯執行時硬體對軟體開發不友好的子系統,或者尚未可用的子系統。

預計隨著此驅動程式框架的發展,將開發和貢獻對其他控制器的支援。

Gadget 驅動程式

除了 Gadget Zero(主要用於與 usb 控制器硬體的驅動程式進行測試和開發)之外,還存在其他 gadget 驅動程式。

有一個 ethernet gadget 驅動程式,它實現了最有用的 通訊裝置類 (CDC) 模型之一。有線調變解調器互操作性的標準之一甚至指定使用此乙太網模型作為兩個強制選項之一。使用此程式碼的 Gadget 對於 USB 主機來說看起來像是乙太網介面卡。它提供對網路的訪問,其中 gadget 的 CPU 是一個主機,可以輕鬆地橋接、路由或防火牆訪問其他網路。由於某些硬體無法完全實現 CDC 乙太網要求,因此該驅動程式還實現了 CDC 乙太網的“僅良好部分”子集。(該子集不會宣傳自己是 CDC 乙太網,以避免產生問題。)

Pengutronix 和 Auerswald GmbH 貢獻了對 Microsoft 的 RNDIS 協議的支援。這類似於 CDC 乙太網,但它在更多稍微的 USB 硬體上執行(但少於 CDC 子集)。但是,它的主要優點是能夠直接連線到最新版本的 Windows,使用 Microsoft 捆綁和支援的驅動程式,從而使與 Windows 的聯網變得更加簡單。

還支援使用者模式 gadget 驅動程式,使用 gadgetfs。這提供了一個 使用者模式 API,該 API 將每個端點表示為單個檔案描述符。I/O 使用正常的 read()read() 呼叫完成。熟悉的工具(如 GDB 和 pthreads)可用於開發和除錯使用者模式驅動程式,因此一旦強大的控制器驅動程式可用,它的許多應用程式將不需要新的核心模式軟體。Linux 2.6 非同步 I/O (AIO) 支援可用,因此使用者模式軟體可以流式傳輸資料,其開銷僅比核心驅動程式略高。

有一個 USB 大容量儲存類驅動程式,它為與 MS-Windows 和 MacOS 等系統的互操作性提供了不同的解決方案。該 大容量儲存 驅動程式使用檔案或塊裝置作為驅動器的後備儲存,如 loop 驅動程式。USB 主機使用大容量儲存類規範的 BBB、CB 或 CBI 版本,使用透明 SCSI 命令從後備儲存訪問資料。

有一個“序列線路”驅動程式,可用於透過 USB 進行 TTY 樣式操作。該驅動程式的最新版本支援 CDC ACM 樣式操作,如 USB 調變解調器等,因此在大多數硬體上,它可以輕鬆地與 MS-Windows 互操作。該驅動程式的一個有趣用途是在啟動韌體(如 BIOS)中,有時可以在沒有真實序列線路的小型系統中使用該模型。

預計隨著此驅動程式框架的發展,將開發和貢獻對其他型別 gadget 的支援。

USB On-The-GO (OTG)

Linux 2.6 上的 USB OTG 支援最初由德州儀器為 OMAP 16xx 和 17xx 系列處理器開發。其他 OTG 系統應該以類似的方式工作,但硬體級別的細節可能非常不同。

系統需要專門的硬體支援才能實現 OTG,特別是包括一個特殊的 Mini-AB 插孔和相關的收發器,以支援雙重角色操作:它們可以作為主機,使用標準的 Linux-USB 主機端驅動程式堆疊,或者作為外圍裝置,使用這個 gadget 框架。為此,系統軟體依賴於對這些程式設計介面的小修改,以及影響哪個驅動程式堆疊連線到 OTG 埠的新內部元件(這裡稱為“OTG 控制器”)。在每個角色中,系統都可以重用現有的硬體中性驅動程式池,這些驅動程式分層在控制器驅動程式介面(usb_bususb_gadget)之上。這些驅動程式最多隻需要進行少量更改,並且新增的用於支援 OTG 的大多數呼叫也可以使非 OTG 產品受益。

  • Gadget 驅動程式測試 is_otg 標誌,並使用它來確定是否在它們的每個配置中包含 OTG 描述符。

  • Gadget 驅動程式可能需要更改以支援兩種新的 OTG 協議,這些協議在新 gadget 屬性中公開,例如 b_hnp_enable 標誌。HNP 支援應該透過使用者介面報告(兩個 LED 即可),並且在某些情況下,當主機掛起外圍裝置時會觸發。SRP 支援可以像遠端喚醒一樣由使用者發起,可能透過按下相同的按鈕。

  • 在主機端,需要教導 USB 裝置驅動程式在適當的時候觸發 HNP,使用 usb_suspend_device()。這也可以節省電池電量,即使對於非 OTG 配置也很有用。

  • 同樣在主機端,驅動程式必須支援 OTG“目標外圍裝置列表”。這只是一個白名單,用於拒絕給定 Linux OTG 主機不支援的外圍裝置。此白名單是特定於產品的;每個產品必須修改 otg_whitelist.h以匹配其互操作性規範。

    非 OTG Linux 主機(如 PC 和工作站)通常有一些新增驅動程式的解決方案,以便最終可以支援無法識別的外圍裝置。對於可能永遠不會升級其韌體的消費類產品來說,這種方法是不合理的,並且通常不現實地期望傳統的 PC/工作站/伺服器支援模型能夠工作。例如,一旦產品已分發,更改裝置韌體通常是不切實際的,因此如果在發貨後發現驅動程式錯誤,通常無法修復。

在那些硬體中性的 usb_bususb_gadget 驅動程式介面之下,還需要進行其他更改;這裡沒有詳細討論這些更改。這些更改影響每個 USB 主機或外圍裝置控制器的硬體特定程式碼,以及 HCD 如何初始化(因為 OTG 只能在單個埠上啟用)。它們還涉及可能被稱為OTG 控制器驅動程式的東西,該驅動程式管理 OTG 收發器和 OTG 狀態機邏輯以及 OTG 埠的大部分根集線器行為。OTG 控制器驅動程式需要根據相關的裝置角色啟用和停用 USB 控制器。usbcore 內部需要進行一些相關的更改,以便它可以識別支援 OTG 的裝置並對 HNP 或 SRP 協議做出適當的響應。