Linux 的 USB Gadget API

作者:

David Brownell

日期:

2004 年 8 月 20 日

简介

本文档介绍了 Linux-USB “Gadget” 内核模式 API,用于嵌入 Linux 的外围设备和其他 USB 设备中。它概述了 API 结构,并展示了如何将其融入系统开发项目中。这是 Linux 上发布的第一个此类 API,旨在解决许多重要问题,包括

  • 支持 USB 2.0,用于可以每秒传输数十兆字节数据的高速设备。

  • 可以像处理只有两个固定功能端点的设备一样,很好地处理具有数十个端点的设备。Gadget 驱动程序可以编写成易于移植到新硬件。

  • 足够灵活,可以公开更复杂的 USB 设备功能,例如多个配置、多个接口、复合设备和备用接口设置。

  • 与 Linux-USB 主机端的更新结合使用,支持 USB “On-The-Go” (OTG)。

  • 与 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/O 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 协议响应),可能包括特定于类的功能

  • 返回配置和字符串描述符

  • (重新)设置配置和接口备用设置,包括启用和配置端点

  • 处理生命周期事件,例如管理与硬件的绑定、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)、支持 OTG “目标外围设备列表”的 USB 设备驱动程序 等。还将有一个 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 通过端点的命名约定支持有限的运行时功能检测。许多驱动程序至少能够部分地自动配置自身。特别是,驱动程序的初始化部分通常具有端点自动配置逻辑,该逻辑会扫描硬件的端点列表,以查找符合驱动程序要求的端点(依赖于这些约定),从而消除一些最常见的条件编译原因。

与 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,该 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 的控制器的散列表。

num_sgs

SG 条目的数量

num_mapped_sgs

映射到 DMA 的 SG 条目的数量(内部)

stream_id

使用 USB3.0 批量流时的流 ID

is_last

指示这是否是在切换到其他流之前 stream_id 的最后一个请求(DWC3 控制器需要)。

no_interrupt

如果为 true,则提示不需要完成 irq。对于由 DMA 控制器直接处理的深度请求队列有时很有帮助。

zero

如果为 true,则在写入数据时,通过根据需要添加零长度数据包,使最后一个数据包为“短”数据包;

short_not_ok

读取数据时,使短数据包被视为错误(队列停止前进直到清理)。

dma_mapped

指示请求是否已映射到 DMA(内部)

sg_was_mapped

如果散列表在请求之前已映射,则进行设置

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 同步 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

上次使用的 out ep 编号

in_epnum

上次使用的 in 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,USB 电缆的“A”端在 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 和勘误表。

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

使用其 maxpacketsize 对齐 len 的端点

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

要检查怪癖的控制器

struct usb_ep *ep

使用其 maxpacketsize 对齐 len 的端点

size_t len

要与 ep 的 maxpacketsize 对齐的缓冲区大小的长度

描述

如果需要出于任何原因检查并可能将缓冲区的大小与 ep 的 maxpacketsize 对齐,则使用此辅助函数。

int gadget_is_altset_supported(struct usb_gadget *g)

如果硬件支持 altsettings,则返回 true

参数

struct usb_gadget *g

要检查怪癖的控制器

int gadget_is_stall_supported(struct usb_gadget *g)

如果硬件支持停止,则返回 true

参数

struct usb_gadget *g

要检查怪癖的控制器

int gadget_is_zlp_supported(struct usb_gadget *g)

如果硬件支持 zlp,则返回 true

参数

struct usb_gadget *g

要检查怪癖的控制器

int gadget_avoids_skb_reserve(struct usb_gadget *g)

如果硬件希望避免 skb_reserve 以提高性能,则返回 true。

参数

struct usb_gadget *g

要检查怪癖的控制器

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 设备驱动程序

定义:

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;
};

成员

功能

描述设备功能的字符串

max_speed

驱动程序处理的最高速度。

绑定

驱动程序的绑定回调

解绑

当驱动程序从设备解绑时调用,通常来自 rmmod(在报告断开连接之后)。在允许休眠的上下文中调用。

设置

对于硬件级别驱动程序未处理的 ep0 控制请求调用。大多数调用必须由设备驱动程序处理,包括描述符和配置管理。设置数据的 16 位成员采用 USB 字节顺序。在中断中调用;这可能不会休眠。驱动程序将响应排队到 ep0,或返回负值以停止。

断开连接

在所有传输停止后,当主机断开连接时调用。可能在中断中调用;这可能不会休眠。某些设备无法检测到断开连接,因此可能不会调用它,除非作为控制器关闭的一部分。

暂停

在 USB 暂停时调用。可能在中断中调用。

恢复

在 USB 恢复时调用。可能在中断中调用。

复位

在 USB 总线复位时调用。对于所有设备驱动程序都是强制性的,应在中断中调用。

驱动程序

此驱动程序的驱动程序模型状态。

udc_name

此驱动程序应绑定到的 UDC 的名称。如果 udc_name 为 NULL,则此驱动程序将绑定到任何可用的 UDC。

match_existing_only

如果未找到 udc,则返回错误并使驱动程序注册失败

is_bound

允许一个驱动程序仅绑定到一个设备

描述

设备在设备驱动程序成功 bind() 之前处于禁用状态,这意味着驱动程序将处理枚举所需的 setup() 请求(并满足“第 9 章”要求),然后执行一些有用的工作。

如果 gadget->is_otg 为 true,则设备驱动程序必须在枚举期间提供 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)关闭端点的地方。

设备驱动程序的 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)

注册一个设备驱动程序

参数

struct usb_gadget_driver *driver

正在注册的驱动程序

struct module *owner

驱动程序模块

const char *mod_name

驱动程序模块的构建名称

上下文

可以休眠

描述

在设备驱动程序的模块初始化函数中调用此函数,以告知基础 UDC 控制器驱动程序有关您的驱动程序的信息。在注册调用返回之前,将调用 bind() 函数将其绑定到设备。预计 bind() 函数将在 init 部分中。

请使用下面定义的宏,而不是直接调用此函数。

int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)

注销设备驱动程序

参数

struct usb_gadget_driver *driver

正在注销的驱动程序

上下文

可以休眠

描述

在设备驱动程序的模块清理函数中调用此函数,以告知基础 USB 控制器您的驱动程序即将消失。如果控制器连接到 USB 主机,它将首先断开连接()。还需要驱动程序在返回此过程之前解绑()并清理任何设备状态。预计 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;
};

成员

语言

标识字符串的语言(en-us 为 0x0409)

字符串

具有 ID 的字符串数组

描述

如果您使用 usb_gadget_get_string(),请使用此函数包装给定语言的所有字符串。

void usb_free_descriptors(struct usb_descriptor_header **v)

释放 usb_copy_descriptors() 返回的描述符

参数

struct usb_descriptor_header **v

描述符向量

可选实用程序

核心 API 足以编写 USB 设备驱动程序,但提供了一些可选实用程序来简化常见任务。这些实用程序包括端点自动配置。

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,来自获取字符串描述符时 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

要复制的空终止向量

上下文

初始化代码,可能会休眠

描述

这将复制 USB 描述符向量。它的主要用途是支持可以有多个副本的 usb_function 对象,每个副本都需要不同的描述符。函数可能具有静态描述符表,这些表用作模板,并使用给定函数实例所需的标识符(用于接口、字符串、端点等)进行自定义。

复合设备框架

核心 API 足以编写用于复合 USB 设备(在给定配置中具有多个功能)的驱动程序,以及多配置设备(也具有多个功能,但不一定共享给定配置)。但是,有一个可选框架可以更容易地重用和组合功能。

使用此框架的设备提供 struct usb_composite_driver,它反过来提供一个或多个 struct usb_configuration 实例。每个这样的配置至少包含一个 struct 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;
};

成员

条目

用于保留扩展属性列表

类型

扩展属性类型

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

用于诊断,标识功能。

字符串

字符串表,由 bind() 期间分配的标识符以及控制请求中提供的语言 ID 键控

fs_descriptors

全速(或低速)描述符表,使用 bind() 期间分配的接口和字符串标识符。如果此指针为空,则该功能在全速(或低速)下不可用。

hs_descriptors

高速描述符表,使用 bind() 期间分配的接口和字符串标识符。如果此指针为空,则该功能在高速下不可用。

ss_descriptors

超速描述符表,使用 bind() 期间分配的接口和字符串标识符。如果在初始化后此指针为空,则该功能在超速下不可用。

ssp_descriptors

超速加描述符表,使用 bind() 期间分配的接口和字符串标识符。如果在初始化后此指针为空,则该功能在超速加下不可用。

config

在调用 usb_add_function() 时分配;这是此功能与之关联的配置。

os_desc_table

(接口 ID、OS 描述符)对的表。该功能可以公开多个接口。如果一个接口是 IAD 的成员,则只有 IAD 的第一个接口在表中具有条目。

os_desc_n

os_desc_table 中的条目数

绑定

在 gadget 可以注册之前,其所有功能都绑定 () 到可用资源,包括接口或类描述符中使用的字符串和接口标识符;端点;I/O 缓冲区等。

解绑

反转 bind; 作为取消注册添加此功能的驱动程序的副作用而调用。

free_func

释放 struct usb_function

mod

(内部)指向创建此结构的模块。

set_alt

(必需)重新配置备用设置;功能驱动程序可以在此时初始化 usb_ep.driver 数据(当使用时)。请注意,将接口设置为其当前备用设置会重置接口状态,并且所有接口都具有禁用状态。

get_alt

返回活动的备用设置。如果未提供此项,则仅支持备用设置零。

disable

(必需)指示应禁用该功能。原因包括主机重置或重新配置 gadget 以及断开连接。

设置

用于特定于接口的控制请求。

req_match

测试此功能是否可以处理给定的类请求。

暂停

当主机停止发送 USB 流量时通知功能。

恢复

当主机重新启动 USB 流量时通知功能。

get_status

当接收者为接口时,返回功能状态作为对 GetStatus() 请求的回复。

func_suspend

当收到 SetFeature(FUNCTION_SUSPEND) 时要调用的回调

func_suspended

指示函数是否处于函数挂起状态。

func_wakeup_armed

指示该函数是否已由主机启用以进行唤醒信号。

描述

一个 USB 功能使用一个或多个接口,并且在大多数情况下应支持全速和高速操作。每个功能都通过 usb_add_function() 与一个配置关联;该功能会导致调用 bind(),以便在设置 gadget 驱动程序时分配资源。这些资源包括端点,应使用 usb_ep_autoconfig() 分配。

为了支持双速操作,功能驱动程序会为高速和全速操作提供描述符。除了不涉及批量端点的极少数情况外,每种速度都需要不同的端点描述符。

功能驱动程序选择自己的策略来管理实例数据。最简单的策略只是将其声明为“static”,这意味着该功能只能激活一次。如果该功能需要在给定速度下的多个配置中公开,则它需要支持多个 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

用于诊断,描述配置。

字符串

字符串表,由在 bind() 期间分配的标识符和控制请求中提供的语言 ID 键控。

descriptors

所有功能描述符之前的描述符表。示例包括 OTG 和供应商特定的描述符。

解绑

反转 bind;作为取消注册添加此配置的驱动程序的副作用调用。

设置

用于委托不由标准设备基础设施处理或针对特定接口的控制请求。

bConfigurationValue

复制到配置描述符中。

iConfiguration

复制到配置描述符中。

bmAttributes

复制到配置描述符中。

MaxPower

功耗(以 mA 为单位)。用于在考虑总线速度后计算配置描述符中的 bMaxPower。

cdev

在调用 bind() 之前由 usb_add_config() 分配;这是与此配置关联的设备。

描述

配置是围绕功能驱动程序构建的 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

设备的模板描述符,包括默认设备标识符。

字符串

字符串表,由在 bind 期间分配的标识符和控制请求中提供的语言 ID 键控。注意:第一个条目是预定义的。可以使用的第一个条目是 USB_GADGET_FIRST_AVAIL_IDX

max_speed

驱动程序支持的最高速度。

needs_serial

如果 gadget 需要用户空间提供序列号,则设置为 1。如果未提供,则会打印警告。

绑定

(必需)用于分配整个设备共享的资源,例如字符串 ID,并使用 usb_add_config() 添加其配置。这可能会因返回负 errno 值而失败;它应该在成功初始化时返回零。

解绑

反转 bind;作为取消注册此驱动程序的副作用调用。

断开连接

可选的驱动程序断开连接方法

暂停

在功能通知之后,当主机停止发送 USB 流量时发出通知

恢复

在功能通知之前,当主机重新启动 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 结构

描述

用于在模块 init/exit 中不执行任何特殊操作的 USB gadget 复合驱动程序的辅助宏。这消除了很多样板。每个模块只能使用此宏一次,调用它会替换 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

正在添加的功能

上下文

在设备设置期间单线程执行

描述

初始化后,每个配置必须添加一个或多个功能。添加功能包括调用其 bind() 方法来分配资源,如接口和字符串标识符以及端点。

此函数返回功能的 bind() 的值,成功则为零,否则为负的 errno 值。

int usb_function_deactivate(struct usb_function *function)

阻止功能和设备枚举

参数

struct usb_function *function

尚未准备好响应的功能

描述

通过阻止激活数据线上的上拉电阻,来阻止设备驱动程序对主机枚举的响应。这通常在 bind() 处理期间调用,以从初始的“准备好响应”状态进行更改,或者当所需的资源可用时调用。

例如,充当用户空间守护进程的通道的驱动程序可以阻止枚举,除非该守护进程(例如 OBEX、MTP 或打印服务器)已准备好处理主机请求。

并非所有系统都支持对其 USB 外围数据上拉电阻的软件控制。

成功返回零,否则返回负的 errno。

int usb_function_activate(struct usb_function *function)

允许功能和设备枚举

参数

struct usb_function *function

调用 usb_function_activate() 的功能

描述

反转 usb_function_deactivate() 的效果。如果没有更多功能延迟其激活,则设备驱动程序将响应主机枚举过程。

成功返回零,否则返回负的 errno。

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

分配一个未使用的接口 ID

参数

struct usb_configuration *config

与接口关联的配置

struct usb_function *function

处理接口的功能

上下文

在设备设置期间单线程执行

描述

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 设备

struct usb_configuration *config

配置,已分配 bConfigurationValue

int (*bind)(struct usb_configuration *)

配置的 bind 函数

上下文

在设备设置期间单线程执行

描述

复合 bind() 例程的主要任务之一是使用此例程添加其支持的每个配置。

此函数返回配置的 bind() 的值,成功则为零,否则为负的 errno 值。绑定配置会分配全局资源,包括字符串 ID,以及每个配置的资源,例如接口 ID 和端点。

int usb_string_id(struct usb_composite_dev *cdev)

分配一个未使用的字符串 ID

参数

struct usb_composite_dev *cdev

正在分配其字符串描述符 ID 的设备

上下文

在设备设置期间单线程执行

描述

usb_string_id() 从 bind() 回调中调用,以分配字符串 ID。然后,功能、配置或设备的驱动程序会将该 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 对象数组

上下文

在设备设置期间单线程执行

描述

usb_string_ids() 从 bind() 回调中调用,以分配字符串 ID。然后,功能、配置或设备的驱动程序会将 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 的数量

上下文

在设备设置期间单线程执行

描述

返回第一个请求的 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

要注册的驱动程序

上下文

在设备设置期间单线程执行

描述

此函数用于使用复合驱动程序框架注册驱动程序。返回值是零,或负 errno 值。这些值通常来自驱动程序的 bind 方法,该方法执行所有设置驱动程序以匹配硬件的工作。

成功返回后,除非其组件之一在绑定时调用 usb_gadget_disconnect(),否则 gadget 已准备好响应主机的请求。通常会这样做是为了等待某些用户空间参与。

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 驱动程序的某些部分,而无需任何特殊的硬件,并且可能在 User Mode Linux 上运行的 GDB 等工具的帮助下进行。至少有一个人表示有兴趣采用这种方法,将其连接到微控制器的模拟器。这种模拟器可以帮助调试运行时硬件对软件开发不友好或尚不可用的子系统。

预计随着此驱动程序框架的发展,将随着时间的推移开发和贡献对其他控制器的支持。

Gadget 驱动程序

除了Gadget Zero(主要用于使用 USB 控制器硬件的驱动程序进行测试和开发)之外,还存在其他 gadget 驱动程序。

有一个 ethernet gadget 驱动程序,它实现了最有用的通信设备类 (CDC) 模型之一。电缆调制解调器互操作性的标准之一甚至指定使用此以太网模型作为两个强制性选项之一。使用此代码的 gadget 在 USB 主机看来就像是以太网适配器。它提供对网络的访问,其中 gadget 的 CPU 是一个主机,可以很容易地桥接、路由或防火墙访问其他网络。由于某些硬件无法完全实现 CDC 以太网要求,因此该驱动程序还实现了 CDC 以太网的“仅限好部分”子集。(该子集不会将自身宣传为 CDC 以太网,以避免产生问题。)

Pengutronix 和 Auerswald GmbH 贡献了对 Microsoft 的 RNDIS 协议的支持。这类似于 CDC 以太网,但它在更多略微的 USB 硬件上运行(但少于 CDC 子集)。但是,它最出名的是能够使用 Microsoft 捆绑和支持的驱动程序直接连接到最新版本的 Windows,从而更轻松地与 Windows 联网。

还支持使用 gadgetfs 的用户模式 gadget 驱动程序。这提供了一个用户模式 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)中,有时可以在没有实际串行线的非常小的系统中使用该模型。

随着此驱动程序框架的发展,预计会随着时间的推移开发和贡献对其他类型设备的支持。

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 设备驱动程序在适当的时候使用 usb_suspend_device() 触发 HNP。这也可以节省电池电量,即使对于非 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 协议。