Linux-USB 主机端 API

Linux 上 USB 简介

通用串行总线 (USB) 用于将主机(例如 PC 或工作站)连接到多个外围设备。USB 使用树形结构,主机作为根(系统的控制者),集线器作为内部节点,外围设备作为叶子(和从设备)。现代 PC 支持多个这样的 USB 设备树,通常有几个 USB 3.0 (5 GBit/s) 或 USB 3.1 (10 GBit/s) 以及一些旧的 USB 2.0 (480 MBit/s) 总线,以防万一。

这种主/从不对称性是出于多种原因设计的,其中之一是易于使用。物理上不可能混淆上游和下游,或者使用 C 型插头无关紧要(或者它们内置在外部设备中)。此外,主机软件不需要处理分布式自动配置,因为预先指定的主节点管理所有这些配置。

内核开发人员在 2.2 内核系列早期将 USB 支持添加到 Linux,此后一直在进一步开发它。除了支持每一代新的 USB 之外,各种主机控制器获得了支持,添加了新的外围设备驱动程序,并引入了用于延迟测量和改进的电源管理的高级功能。

Linux 可以在 USB 设备内部以及控制这些设备的主机上运行。但是,在这些外围设备内部运行的 USB 设备驱动程序与在主机内部运行的驱动程序的功能不同,因此它们被赋予了不同的名称:gadget 驱动程序。本文档不涵盖 gadget 驱动程序。

USB 主机端 API 模型

USB 设备的 主机端驱动程序与 “usbcore” API 通信。有两个。一个是为通用驱动程序(通过驱动程序框架公开)设计的,另一个是为核心部分驱动程序设计的。此类核心驱动程序包括集线器驱动程序(管理 USB 设备树)和几种不同类型的主机控制器驱动程序,它们控制各个总线。

USB 驱动程序看到的设备模型相对复杂。

  • USB 支持四种数据传输类型(控制、批量、中断和同步)。其中两种(控制和批量)使用可用带宽,而另外两种(中断和同步)则被安排以提供有保证的带宽。

  • 设备描述模型包括每个设备的一个或多个“配置”,一次只有一个配置处于活动状态。设备应能够在低于其最高速度的速度下运行,并且可以提供 BOS 描述符,以显示它们保持完全运行的最低速度。

  • 从 USB 3.0 开始,配置具有一个或多个“功能”,这些功能提供常见的功能,并且为了电源管理的目的而分组在一起。

  • 配置或功能具有一个或多个“接口”,每个接口可能具有“替代设置”。接口可以通过 USB “类”规范进行标准化,或者特定于供应商或设备。

    USB 设备驱动程序实际上绑定到接口,而不是设备。将它们视为“接口驱动程序”,尽管您可能看不到很多区分很重要的设备。大多数 USB 设备都很简单,只有一个功能、一个配置、一个接口和一个替代设置。

  • 接口具有一个或多个“端点”,每个端点都支持一种数据传输类型和方向,例如“批量输出”或“中断输入”。整个配置在每个方向上最多可以有 16 个端点,根据需要在所有接口之间分配。

  • USB 上的数据传输是分组的;每个端点都有最大数据包大小。驱动程序通常必须知道诸如使用“短”(包括零长度)数据包标记批量传输结束之类的约定。

  • Linux USB API 支持用于控制和批量消息的同步调用。它还支持使用称为“URB”(USB 请求块)的请求结构对所有类型的数据传输进行异步调用。

因此,公开给设备驱动程序的 USB 核心 API 涵盖了相当多的领域。您可能需要查阅 USB 3.0 规范,该规范可在 www.usb.org 上免费在线获取,以及类或设备规范。

唯一实际接触硬件(读取/写入寄存器、处理 IRQ 等)的主机端驱动程序是 HCD。从理论上讲,所有 HCD 都通过相同的 API 提供相同的功能。在实践中,这种情况越来越真实,但是仍然存在一些差异,尤其是在不太常见的控制器上处理故障时。不同的控制器不一定会报告相同的故障方面,并且从故障中恢复(包括软件引起的故障,例如取消链接 URB)尚未完全一致。设备驱动程序作者应重点进行断开连接测试(在设备处于活动状态时),使用每个不同的主机控制器驱动程序,以确保驱动程序本身没有错误,并确保它们不依赖于某些 HCD 特定的行为。

USB 标准类型

include/uapi/linux/usb/ch9.h 中,您将找到 USB 规范第 9 章中定义的 USB 数据类型。这些数据类型在整个 USB 中以及包括此主机端 API、gadget API、usb 字符设备和 debugfs 接口在内的 API 中使用。该文件本身包含在 include/linux/usb/ch9.h 中,后者还包含用于操作这些数据类型的几个实用程序的声明;实现位于 drivers/usb/common/common.c 中。

const char *usb_ep_type_string(int ep_type)

返回端点类型的人类可读名称。

参数

int ep_type

要返回人类可读名称的端点类型。如果它不是以下类型中的任何一种:USB_ENDPOINT_XFER_{CONTROL, ISOC, BULK, INT},通常由 usb_endpoint_type() 获取,则将返回字符串“unknown”。

const char *usb_speed_string(enum usb_device_speed speed)

返回速度的人类可读名称。

参数

enum usb_device_speed speed

要返回人类可读名称的速度。如果它不是 usb_device_speed 枚举中定义的任何速度,则将返回 USB_SPEED_UNKNOWN 的字符串。

enum usb_device_speed usb_get_maximum_speed(struct device *dev)

获取给定 USB 控制器的最大请求速度。

参数

struct device *dev

指向给定 USB 控制器设备的指针

描述

该函数从属性 “maximum-speed” 获取最大速度字符串,并返回相应的枚举 usb_device_speed。

enum usb_ssp_rate usb_get_maximum_ssp_rate(struct device *dev)

获取支持 SuperSpeed Plus 的设备的信号速率生成和通道数。

参数

struct device *dev

指向给定 USB 控制器设备的指针

描述

如果来自 “maximum-speed” 属性的字符串为 super-speed-plus-genXxY,其中 ‘X’ 是代数,‘Y’ 是通道数,则此函数返回相应的枚举 usb_ssp_rate。

const char *usb_state_string(enum usb_device_state state)

返回状态的人类可读名称。

参数

enum usb_device_state state

要返回人类可读名称的状态。如果它不是 usb_device_state_string 枚举中的任何设备状态,则将返回字符串 UNKNOWN。

enum usb_dr_mode usb_get_role_switch_default_mode(struct device *dev)

获取给定设备的默认模式

参数

struct device *dev

指向给定设备的指针

描述

该函数从属性 ‘role-switch-default-mode’ 获取字符串,并返回相应的枚举 usb_dr_mode。

unsigned int usb_decode_interval(const struct usb_endpoint_descriptor *epd, enum usb_device_speed speed)

将 bInterval 解码为以 1 微秒为单位的时间

参数

const struct usb_endpoint_descriptor *epd

端点的描述符

enum usb_device_speed speed

端点工作的速度

描述

函数返回以 1 微秒为单位的时间间隔,用于为数据传输提供端点服务。

enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0)

获取与给定 phy device_node 关联的控制器设备的双重角色模式

参数

struct device_node *np

指向给定 phy device_node 的指针

int arg0

对于 #phy-cells >= 1 的 phy,为 phandle args[0];对于没有 phy-cells 的 phy,为 -1

描述

在 dts 中,USB 控制器与 phy 设备关联。该函数从与给定 phy 设备节点关联的控制器的属性 ‘dr_mode’ 获取字符串,并返回相应的枚举 usb_dr_mode。

bool of_usb_host_tpl_support(struct device_node *np)

获取给定的目标主机(非 PC 主机)是否支持目标外围设备列表 (TPL)

参数

struct device_node *np

指向给定 device_node 的指针

描述

该函数获取目标主机是否支持 TPL

int of_usb_update_otg_caps(struct device_node *np, struct usb_otg_caps *otg_caps)

根据 DT 中传递的属性更新 USB OTG 功能。

参数

struct device_node *np

指向给定 device_node 的指针

struct usb_otg_caps *otg_caps

指向要设置的目标 usb_otg_caps 的指针

描述

该函数更新 OTG 功能

struct device *usb_of_get_companion_dev(struct device *dev)

查找伴随设备

参数

struct device *dev

用于查找伴随设备的设备指针

描述

从平台总线查找伴随设备。

获取对返回的 struct device 的引用,使用后需要将其释放。

返回值

成功时,返回指向伴随设备的指针,失败时返回 NULL

此外,一些用于创建调试输出的函数在 drivers/usb/common/debug.c 中定义。

主机端数据类型和宏

主机端 API 向驱动程序公开了多个层,其中一些层比其他层更必要。这些层支持主机端驱动程序和设备的生命周期模型,并支持通过 usbcore 将缓冲区传递给一些为设备驱动程序执行 I/O 的 HCD。

struct usb_host_endpoint

主机端端点描述符和队列

定义:

struct usb_host_endpoint {
    struct usb_endpoint_descriptor          desc;
    struct usb_ss_ep_comp_descriptor        ss_ep_comp;
    struct usb_ssp_isoc_ep_comp_descriptor  ssp_isoc_ep_comp;
    struct list_head                urb_list;
    void *hcpriv;
    struct ep_device                *ep_dev;
    unsigned char *extra;
    int extralen;
    int enabled;
    int streams;
};

成员

desc

此端点的描述符,wMaxPacketSize 为本机字节序

ss_ep_comp

此端点的 SuperSpeed 伴随描述符

ssp_isoc_ep_comp

此端点的 SuperSpeedPlus 等时伴随描述符

urb_list

排队到此端点的 urb;由 usbcore 维护

hcpriv

供 HCD 使用;通常保存硬件 DMA 队列头 (QH),每个 urb 有一个或多个传输描述符 (TD)

ep_dev

用于 sysfs 信息的 ep_device

extra

此端点在配置中的后续描述符

extralen

有多少字节的“extra”是有效的

enabled

URB 可以提交到此端点

streams

端点上分配的 USB-3 流的数量

描述

USB 请求始终排队到给定的端点,该端点由给定 USB 配置中活动接口内的描述符标识。

struct usb_interface

USB 设备驱动程序与之通信的对象

定义:

struct usb_interface {
    struct usb_host_interface *altsetting;
    struct usb_host_interface *cur_altsetting;
    unsigned num_altsetting;
    struct usb_interface_assoc_descriptor *intf_assoc;
    int minor;
    enum usb_interface_condition condition;
    unsigned sysfs_files_created:1;
    unsigned ep_devs_created:1;
    unsigned unregistering:1;
    unsigned needs_remote_wakeup:1;
    unsigned needs_altsetting0:1;
    unsigned needs_binding:1;
    unsigned resetting_device:1;
    unsigned authorized:1;
    enum usb_wireless_status wireless_status;
    struct work_struct wireless_status_work;
    struct device dev;
    struct device *usb_dev;
    struct work_struct reset_ws;
};

成员

altsetting

接口结构数组,每个可能被选择的备用设置都有一个。每个接口结构都包含一组端点配置。它们将没有特定的顺序。

cur_altsetting

当前的备用设置。

num_altsetting

定义的备用设置的数量。

intf_assoc

接口关联描述符

minor

分配给此接口的次要编号,如果此接口绑定到使用 USB 主要编号的驱动程序。如果此接口不使用 USB 主要编号,则应不使用此字段。驱动程序应在驱动程序的 probe() 函数中设置此值,在通过调用 usb_register_dev() 从 USB 核心分配次要编号之后。

condition

接口的绑定状态:未绑定、正在绑定 (probe() 中)、绑定到驱动程序或正在解除绑定 (disconnect() 中)

sysfs_files_created

sysfs 属性是否存在

ep_devs_created

端点子伪设备是否存在

unregistering

当正在注销接口时设置的标志

needs_remote_wakeup

当驱动程序在自动挂起期间需要远程唤醒功能时设置的标志。

needs_altsetting0

当对备用设置 0 的 set-interface 请求被延迟时设置的标志。

needs_binding

当驱动程序在不支持的重置或挂起操作后应重新探测或解除绑定时设置的标志。

resetting_device

USB 核心重置设备,因此将备用设置 0 用作当前设置;重置后需要带宽分配。

authorized

这允许(取消)授权单个接口,而不是像设备授权那样授权整个设备。

wireless_status

如果 USB 设备使用接收器/发射器组合,则发射器是否已连接。

wireless_status_work

用于从原子上下文调度无线状态更改。

dev

驱动程序模型对此设备的视图

usb_dev

如果接口绑定到 USB 主要编号,则这将指向该设备的 sysfs 表示。

reset_ws

用于从原子上下文调度重置。

描述

USB 设备驱动程序附加到物理设备上的接口。每个接口封装一个单独的高级功能,例如将音频流馈送到扬声器或报告音量控制的更改。许多 USB 设备只有一个接口。用于与接口的端点通信的协议可以在 USB“类”规范中定义,也可以由产品供应商定义。(默认)控制端点是每个接口的一部分,但永远不会在接口的描述符中列出。

绑定到接口的驱动程序可以在此结构的 dev 成员上使用标准的驱动程序模型调用,例如 dev_get_drvdata()。

每个接口都可以有备用设置。设备的初始配置设置备用设置 0,但设备驱动程序可以使用 usb_set_interface() 更改该设置。备用设置通常用于控制定期端点的使用,例如让不同的端点使用不同数量的保留 USB 带宽。所有使用等时端点的符合标准的 USB 设备都将在非默认设置中使用它们。

USB 规范规定备用设置编号必须从 0 到比备用设置总数少 1。但有些设备设法搞砸了,而且这些结构也不一定按数字顺序存储。使用 usb_altnum_to_altsetting() 以根据其编号在 altsetting 数组中查找备用设置。

void usb_set_intfdata(struct usb_interface *intf, void *data)

将特定于驱动程序的数据与接口关联

参数

struct usb_interface *intf

USB 接口

void *data

驱动程序数据

描述

驱动程序可以在它们的 probe() 回调函数中使用此函数,将驱动程序特定的数据与接口关联起来。

请注意,通常没有必要清除 driver-data 指针,即使某些驱动程序出于历史或特定实现的原因这样做。

struct usb_interface_cache

设备接口的长期表示

定义:

struct usb_interface_cache {
    unsigned num_altsetting;
    struct kref ref;
    struct usb_host_interface altsetting[];
};

成员

num_altsetting

定义的备用设置的数量。

ref

引用计数器。

altsetting

可变长度的接口结构数组,每个可选择的备用设置对应一个结构。每个结构都包含一组端点配置。它们的顺序不确定。

描述

这些结构在 usb_device 的生命周期内持续存在,不像 struct usb_interface(仅在其配置安装时持续存在)。可以随时通过这些结构访问 altsetting 数组,允许比较配置并为 /sys/kernel/debug/usb/devices 伪文件提供支持。

struct usb_host_config

设备配置的表示

定义:

struct usb_host_config {
    struct usb_config_descriptor    desc;
    char *string;
    struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS];
    struct usb_interface *interface[USB_MAXINTERFACES];
    struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];
    unsigned char *extra;
    int extralen;
};

成员

desc

设备的配置描述符。

字符串

指向此配置的 iConfiguration 字符串缓存版本的指针(如果存在)。

intf_assoc

此配置中任何接口关联描述符的列表

接口

指向 usb_interface 结构的指针数组,配置中的每个接口对应一个指针。接口的数量存储在 desc.bNumInterfaces 中。这些指针仅在配置处于活动状态时有效。

intf_cache

指向 usb_interface_cache 结构的指针数组,配置中的每个接口对应一个结构。这些结构在设备的整个生命周期内都存在。

extra

指向包含与此配置关联的所有额外描述符(在第一个接口描述符之前的描述符)的缓冲区的指针。

extralen

额外描述符缓冲区的长度。

描述

USB 设备可能具有多个配置,但任何时候只能有一个配置处于活动状态。每个配置封装了一个不同的操作环境;例如,双速设备将为全速和高速操作提供单独的配置。可用配置的数量存储在设备描述符中,为 bNumConfigurations。

一个配置可以包含多个接口。每个接口对应于 USB 设备的不同功能,并且只要配置处于活动状态,所有接口都可用。USB 标准规定接口应该从 0 到 desc.bNumInterfaces-1 编号,但是许多设备都搞错了。此外,不能保证接口数组按数字顺序排序。使用 usb_ifnum_to_if() 根据接口编号查找接口条目。

设备驱动程序不应尝试激活配置。选择安装哪个配置是一个策略决策,基于诸如可用电源、提供的功能和用户意愿(通过用户空间工具表达)等因素。但是,驱动程序可以调用 usb_reset_configuration() 来重新初始化当前配置及其所有接口。

struct usb_device

USB 设备在内核中的表示

定义:

struct usb_device {
    int devnum;
    char devpath[16];
    u32 route;
    enum usb_device_state   state;
    enum usb_device_speed   speed;
    unsigned int            rx_lanes;
    unsigned int            tx_lanes;
    enum usb_ssp_rate       ssp_rate;
    struct usb_tt   *tt;
    int ttport;
    unsigned int toggle[2];
    struct usb_device *parent;
    struct usb_bus *bus;
    struct usb_host_endpoint ep0;
    struct device dev;
    struct usb_device_descriptor descriptor;
    struct usb_host_bos *bos;
    struct usb_host_config *config;
    struct usb_host_config *actconfig;
    struct usb_host_endpoint *ep_in[16];
    struct usb_host_endpoint *ep_out[16];
    char **rawdescriptors;
    unsigned short bus_mA;
    u8 portnum;
    u8 level;
    u8 devaddr;
    unsigned can_submit:1;
    unsigned persist_enabled:1;
    unsigned reset_in_progress:1;
    unsigned have_langid:1;
    unsigned authorized:1;
    unsigned authenticated:1;
    unsigned lpm_capable:1;
    unsigned lpm_devinit_allow:1;
    unsigned usb2_hw_lpm_capable:1;
    unsigned usb2_hw_lpm_besl_capable:1;
    unsigned usb2_hw_lpm_enabled:1;
    unsigned usb2_hw_lpm_allowed:1;
    unsigned usb3_lpm_u1_enabled:1;
    unsigned usb3_lpm_u2_enabled:1;
    int string_langid;
    char *product;
    char *manufacturer;
    char *serial;
    struct list_head filelist;
    int maxchild;
    u32 quirks;
    atomic_t urbnum;
    unsigned long active_duration;
    unsigned long connect_time;
    unsigned do_remote_wakeup:1;
    unsigned reset_resume:1;
    unsigned port_is_suspended:1;
    enum usb_link_tunnel_mode tunnel_mode;
    int slot_id;
    struct usb2_lpm_parameters l1_params;
    struct usb3_lpm_parameters u1_params;
    struct usb3_lpm_parameters u2_params;
    unsigned lpm_disable_count;
    u16 hub_delay;
    unsigned use_generic_driver:1;
};

成员

devnum

设备编号;USB 总线上的地址

devpath

用于消息中的设备 ID 字符串(例如,/port/...)

route

用于 xHCI 的树拓扑十六进制字符串

state

设备状态:已配置、未连接等。

speed

设备速度:高/全/低速(或错误)

rx_lanes

正在使用的接收通道数,USB 3.2 添加了双通道支持

tx_lanes

正在使用的发送通道数,USB 3.2 添加了双通道支持

ssp_rate

SuperSpeed Plus 物理层信令速率和通道数

tt

事务转换器信息;用于低/全速设备,高速集线器

ttport

该 tt 集线器上的设备端口

toggle

每个端点一位,其中 ([0] = IN, [1] = OUT) 端点

parent

我们的集线器,除非我们是根

bus

我们所属的总线

ep0

端点 0 数据(默认控制管道)

dev

通用设备接口

descriptor

USB 设备描述符

bos

USB 设备 BOS 描述符集

config

设备的所有配置

actconfig

活动配置

ep_in

IN 端点数组

ep_out

OUT 端点数组

rawdescriptors

每个配置的原始描述符

bus_mA

总线提供的电流

portnum

父端口号(从 1 开始)

level

USB 集线器祖先的数量

devaddr

设备地址,XHCI:由硬件分配,其他:与 devnum 相同

can_submit

可以提交 URB

persist_enabled

此设备已启用 USB_PERSIST

reset_in_progress

设备正在重置

have_langid

string_langid 是否有效

authorized

策略已表示我们可以使用它;(用户空间)策略决定我们是否授权使用此设备。默认情况下,有线 USB 设备是授权的。WUSB 设备则不是,直到我们从用户空间授权它们。FIXME -- 完整的文档

authenticated

加密身份验证已通过

lpm_capable

设备支持 LPM

lpm_devinit_allow

允许 USB3 设备发起 LPM,退出延迟在范围内

usb2_hw_lpm_capable

设备可以执行 USB2 硬件 LPM

usb2_hw_lpm_besl_capable

设备可以执行 USB2 硬件 BESL LPM

usb2_hw_lpm_enabled

USB2 硬件 LPM 已启用

usb2_hw_lpm_allowed

用户空间允许启用 USB 2.0 LPM

usb3_lpm_u1_enabled

已启用 USB3 硬件 U1 LPM

usb3_lpm_u2_enabled

已启用 USB3 硬件 U2 LPM

string_langid

字符串的语言 ID

product

iProduct 字符串(如果存在)(静态)

manufacturer

iManufacturer 字符串(如果存在)(静态)

serial

iSerialNumber 字符串(如果存在)(静态)

filelist

打开到此设备的 usbfs 文件

maxchild

如果是集线器,则为端口数

quirks

整个设备的怪癖

urbnum

为整个设备提交的 URB 数

active_duration

设备未挂起的总时间

connect_time

设备首次连接的时间

do_remote_wakeup

应启用远程唤醒

reset_resume

需要重置而不是恢复

port_is_suspended

上游端口已挂起(L2 或 U3)

tunnel_mode

通过 USB4 的连接是原生连接还是隧道连接

slot_id

由 xHCI 分配的插槽 ID

l1_params

USB2 L1 LPM 状态的最佳努力服务延迟和 L1 超时。

u1_params

USB3 U1 LPM 状态的退出延迟和集线器发起的超时。

u2_params

USB3 U2 LPM 状态的退出延迟和集线器发起的超时。

lpm_disable_count

usb_disable_lpm() 和 usb_enable_lpm() 使用的引用计数,用于跟踪需要为此 usb_device 禁用 USB 3.0 链路电源管理的函数数量。此计数应仅由这些函数操作,并持有 bandwidth_mutex。

hub_delay

缓存值,由以下组成:parent->hub_delay + wHubDelay + tTPTransmissionDelay (40ns) 将用作 SetIsochDelay 请求的 wValue。

use_generic_driver

要求驱动程序核心使用通用驱动程序重新探测。

注意

Usbcore 驱动程序不应直接设置 usbdev->state。而是使用 usb_set_device_state()

usb_hub_for_each_child

usb_hub_for_each_child (hdev, port1, child)

迭代集线器上的所有子设备

参数

hdev

属于 USB 集线器的 USB 设备

port1

与子设备关联的端口号

child

子设备指针

int usb_interface_claimed(struct usb_interface *iface)

如果接口被声明,则返回 true

参数

struct usb_interface *iface

正在检查的接口

返回值

如果接口被声明,则返回 true (非零) ,否则返回 false (零)。

注意

调用者必须拥有驱动程序模型的 usb 总线读取锁。因此驱动程序 probe() 条目不需要额外的锁定,但其他调用上下文可能需要显式声明该锁。

int usb_make_path(struct usb_device *dev, char *buf, size_t size)

返回 USB 树中稳定的设备路径

参数

struct usb_device *dev

正在构建路径的设备

char *buf

存放字符串的位置

size_t size

“buf” 的大小?

返回值

字符串的长度 (> 0),如果大小太小,则为负数。

注意

此标识符旨在“稳定”,反映硬件中的物理路径,例如主机控制器的物理总线地址或 USB 集线器上的端口。这使得它保持不变,直到系统通过重新连接 USB 设备树或移动 USB 主机控制器来进行物理重新配置。添加和删除设备(包括主机控制器驱动程序模块中的虚拟根集线器)不会更改这些路径标识符;重启或重新枚举也不会。这些标识符比可更改的(“不稳定”)标识符(如总线号或设备地址)更有用。

描述

除了连接到 USB 2.0 根集线器的设备之外,这些标识符也是可预测的。只要设备树没有更改,将任何 USB 设备插入给定的集线器端口始终会为其提供相同的路径。由于使用了“伴侣”控制器,因此连接到 USB 2.0 根集线器(EHCI 主机控制器)上的端口的设备,如果它们是高速设备,将获得一个路径 ID,如果它们是全速或低速设备,则会获得不同的路径 ID。

USB_DEVICE

USB_DEVICE (vend, prod)

用于描述特定 USB 设备的宏

参数

vend

16 位 USB 供应商 ID

prod

16 位 USB 产品 ID

描述

此宏用于创建与特定设备匹配的 struct usb_device_id

USB_DEVICE_VER

USB_DEVICE_VER (vend, prod, lo, hi)

描述具有版本范围的特定 USB 设备

参数

vend

16 位 USB 供应商 ID

prod

16 位 USB 产品 ID

lo

bcdDevice_lo 值

hi

bcdDevice_hi 值

描述

此宏用于创建与具有版本范围的特定设备匹配的 struct usb_device_id

USB_DEVICE_INTERFACE_CLASS

USB_DEVICE_INTERFACE_CLASS (vend, prod, cl)

描述具有特定接口类别的 USB 设备

参数

vend

16 位 USB 供应商 ID

prod

16 位 USB 产品 ID

cl

bInterfaceClass 值

描述

此宏用于创建与设备的特定接口类别匹配的 struct usb_device_id

USB_DEVICE_INTERFACE_PROTOCOL

USB_DEVICE_INTERFACE_PROTOCOL (vend, prod, pr)

描述具有特定接口协议的 USB 设备

参数

vend

16 位 USB 供应商 ID

prod

16 位 USB 产品 ID

pr

bInterfaceProtocol 值

描述

此宏用于创建与设备的特定接口协议匹配的 struct usb_device_id

USB_DEVICE_INTERFACE_NUMBER

USB_DEVICE_INTERFACE_NUMBER (vend, prod, num)

描述具有特定接口编号的 USB 设备

参数

vend

16 位 USB 供应商 ID

prod

16 位 USB 产品 ID

num

bInterfaceNumber 值

描述

此宏用于创建与设备的特定接口编号匹配的 struct usb_device_id

USB_DEVICE_INFO

USB_DEVICE_INFO (cl, sc, pr)

用于描述一类 USB 设备的宏

参数

cl

bDeviceClass 值

sc

bDeviceSubClass 值

pr

bDeviceProtocol 值

描述

此宏用于创建与特定类别的设备匹配的 struct usb_device_id

USB_INTERFACE_INFO

USB_INTERFACE_INFO (cl, sc, pr)

用于描述一类 USB 接口的宏

参数

cl

bInterfaceClass 值

sc

bInterfaceSubClass 值

pr

bInterfaceProtocol 值

描述

此宏用于创建与特定类别的接口匹配的 struct usb_device_id

USB_DEVICE_AND_INTERFACE_INFO

USB_DEVICE_AND_INTERFACE_INFO (vend, prod, cl, sc, pr)

描述具有一类 USB 接口的特定 USB 设备

参数

vend

16 位 USB 供应商 ID

prod

16 位 USB 产品 ID

cl

bInterfaceClass 值

sc

bInterfaceSubClass 值

pr

bInterfaceProtocol 值

描述

此宏用于创建与具有特定类别的接口的特定设备匹配的 struct usb_device_id

当显式匹配具有供应商特定 bDeviceClass 值但符合标准的接口的设备时,此方法尤其有用。

USB_VENDOR_AND_INTERFACE_INFO

USB_VENDOR_AND_INTERFACE_INFO (vend, cl, sc, pr)

描述具有一类 USB 接口的特定 USB 供应商

参数

vend

16 位 USB 供应商 ID

cl

bInterfaceClass 值

sc

bInterfaceSubClass 值

pr

bInterfaceProtocol 值

描述

此宏用于创建与具有特定类别的接口的特定供应商匹配的 struct usb_device_id

当显式匹配具有供应商特定 bDeviceClass 值但符合标准的接口的设备时,此方法尤其有用。

struct usb_driver

向 usbcore 标识 USB 接口驱动程序

定义:

struct usb_driver {
    const char *name;
    int (*probe) (struct usb_interface *intf, const struct usb_device_id *id);
    void (*disconnect) (struct usb_interface *intf);
    int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code, void *buf);
    int (*suspend) (struct usb_interface *intf, pm_message_t message);
    int (*resume) (struct usb_interface *intf);
    int (*reset_resume)(struct usb_interface *intf);
    int (*pre_reset)(struct usb_interface *intf);
    int (*post_reset)(struct usb_interface *intf);
    void (*shutdown)(struct usb_interface *intf);
    const struct usb_device_id *id_table;
    const struct attribute_group **dev_groups;
    struct usb_dynids dynids;
    struct device_driver driver;
    unsigned int no_dynamic_id:1;
    unsigned int supports_autosuspend:1;
    unsigned int disable_hub_initiated_lpm:1;
    unsigned int soft_unbind:1;
};

成员

name

驱动程序名称在 USB 驱动程序中应该是唯一的,并且通常应该与模块名称相同。

probe

调用以查看驱动程序是否愿意管理设备上的特定接口。如果愿意,则 probe 返回零,并使用 usb_set_intfdata() 将特定于驱动程序的数据与接口关联。它还可以使用 usb_set_interface() 来指定适当的备用设置。如果不愿意管理接口,则返回 -ENODEV,如果发生真正的 IO 错误,则返回适当的负 errno 值。

disconnect

当接口不再可访问时调用,通常是因为其设备已断开连接(或正在断开连接)或正在卸载驱动程序模块。

unlocked_ioctl

用于希望通过“usbfs”文件系统与用户空间通信的驱动程序。这允许设备提供向用户空间公开信息的方式,无论它们在文件系统中以何种方式(或不)显示。

suspend

当系统要通过系统睡眠或运行时挂起上下文挂起设备时调用。在系统睡眠上下文中,返回值将被忽略,因此如果挂起失败,请勿尝试继续使用该设备。相反,让恢复或重置恢复例程从故障中恢复。

resume

当系统正在恢复设备时调用。

reset_resume

当挂起的设备已重置而不是恢复时调用。

pre_reset

当设备即将重置时,由 usb_reset_device() 调用。此例程必须在驱动程序没有该设备的活动 URB 之前返回,并且在调用 post_reset 方法之前不得再提交 URB。

post_reset

设备重置后由 usb_reset_device() 调用

shutdown

在关闭时调用以使设备静止。

id_table

USB 驱动程序使用 ID 表来支持热插拔。使用 MODULE_DEVICE_TABLE(usb,...) 导出此项。必须设置此项,否则永远不会调用驱动程序的 probe 函数。

dev_groups

绑定到驱动程序后将创建的附加到设备的属性。

dynids

在内部用于保存此驱动程序的动态添加设备 ID 列表。

driver

驱动程序模型的内核驱动程序结构。

no_dynamic_id

如果设置为 1,则 USB 内核将通过阻止创建 sysfs 文件,来阻止将动态 ID 添加到此驱动程序。

supports_autosuspend

如果设置为 0,则 USB 内核将不允许绑定到此驱动程序的接口进行自动挂起。

disable_hub_initiated_lpm

如果设置为 1,则当发生空闲超时时,USB 内核将不允许集线器启动较低功耗链路状态转换。仍然允许设备启动的 USB 3.0 链路 PM。

soft_unbind

如果设置为 1,则 USB 内核在调用驱动程序的 disconnect 方法之前,将不会终止 URB 和禁用端点。

描述

USB接口驱动程序必须提供名称、probe() 和 disconnect() 方法,以及 id_table。其他驱动程序字段是可选的。

id_table 用于热插拔。它包含一组描述符,并且每个条目都可以关联专门的数据。该表被用户和内核模式的热插拔支持使用。

probe() 和 disconnect() 方法在可以休眠的上下文中调用,但应避免滥用此特权。连接设备的大部分工作应在设备打开时完成,并在最后关闭时撤销。disconnect 代码需要解决与 open() 和 close() 方法的并发问题,以及强制所有待处理的 I/O 请求完成(通过根据需要取消链接它们,并阻塞直到取消链接完成)。

struct usb_device_driver

向 usbcore 标识 USB 设备驱动程序

定义:

struct usb_device_driver {
    const char *name;
    bool (*match) (struct usb_device *udev);
    int (*probe) (struct usb_device *udev);
    void (*disconnect) (struct usb_device *udev);
    int (*suspend) (struct usb_device *udev, pm_message_t message);
    int (*resume) (struct usb_device *udev, pm_message_t message);
    int (*choose_configuration) (struct usb_device *udev);
    const struct attribute_group **dev_groups;
    struct device_driver driver;
    const struct usb_device_id *id_table;
    unsigned int supports_autosuspend:1;
    unsigned int generic_subclass:1;
};

成员

name

驱动程序名称在 USB 驱动程序中应该是唯一的,并且通常应该与模块名称相同。

match

如果设置,则用于更好地匹配设备/驱动程序。

probe

调用以查看驱动程序是否愿意管理特定的设备。如果是,则 probe 返回零,并使用 dev_set_drvdata() 将驱动程序特定的数据与设备关联。如果不愿意管理设备,则返回负的 errno 值。

disconnect

当设备不再可访问时调用,通常是因为它已(或正在)断开连接,或者正在卸载驱动程序的模块。

suspend

当系统即将挂起设备时调用。

resume

当系统正在恢复设备时调用。

choose_configuration

如果非 NULL,则调用此函数而不是默认的 usb_choose_configuration()。如果此函数返回错误,我们将继续调用正常的 usb_choose_configuration()。

dev_groups

绑定到驱动程序后将创建的附加到设备的属性。

driver

驱动程序模型的内核驱动程序结构。

id_table

match() 一起使用,以在 probe() 时选择更好的匹配驱动程序。

supports_autosuspend

如果设置为 0,则 USB 核心将不允许绑定到此驱动程序的设备自动挂起。

generic_subclass

如果设置为 1,则除了驱动程序自己的 probe、disconnect、resume 和 suspend 函数之外,还会调用通用 USB 驱动程序的这些函数,因此不需要复制此部分设置。

描述

USB 驱动程序必须提供上面列出的所有字段,除了 driver、match 和 id_table。

struct usb_class_driver

标识想要使用 USB 主设备号的 USB 驱动程序

定义:

struct usb_class_driver {
    char *name;
    char *(*devnode)(const struct device *dev, umode_t *mode);
    const struct file_operations *fops;
    int minor_base;
};

成员

name

此驱动程序的 USB 类设备名称。将显示在 sysfs 中。

devnode

回调以提供可能创建的设备节点的命名提示。

fops

指向此驱动程序的 struct file_operations 的指针。

minor_base

此驱动程序的次设备号范围的起始位置。

描述

此结构用于 usb_register_dev()usb_deregister_dev() 函数,以整合用于它们的许多参数。

module_usb_driver

module_usb_driver (__usb_driver)

用于注册 USB 驱动程序的助手宏

参数

__usb_driver

usb_driver 结构

描述

用于在模块初始化/退出时不执行任何特殊操作的 USB 驱动程序的助手宏。这消除了大量的样板代码。每个模块只能使用此宏一次,并且调用它会替换 module_init()module_exit()

struct urb

USB 请求块

定义:

struct urb {
    struct list_head urb_list;
    struct list_head anchor_list;
    struct usb_anchor *anchor;
    struct usb_device *dev;
    struct usb_host_endpoint *ep;
    unsigned int pipe;
    unsigned int stream_id;
    int status;
    unsigned int transfer_flags;
    void *transfer_buffer;
    dma_addr_t transfer_dma;
    struct scatterlist *sg;
    int num_mapped_sgs;
    int num_sgs;
    u32 transfer_buffer_length;
    u32 actual_length;
    unsigned char *setup_packet;
    dma_addr_t setup_dma;
    int start_frame;
    int number_of_packets;
    int interval;
    int error_count;
    void *context;
    usb_complete_t complete;
    struct usb_iso_packet_descriptor iso_frame_desc[];
};

成员

urb_list

供 URB 的当前所有者使用。

anchor_list

锚点列表中成员身份

anchor

将 URB 锚定到公共系泊点

dev

标识要执行请求的 USB 设备。

ep

指向端点的数据结构。最终将取代 pipe

pipe

保存端点号、方向、类型等。使用八个可用的宏创建这些值:usb_{snd,rcv}TYPEpipe(dev,endpoint),其中 TYPE 为“ctrl”(控制)、“bulk”(批量)、“int”(中断)或“iso”(等时)。例如 usb_sndbulkpipe() 或 usb_rcvintpipe()。端点号的范围从零到十五。请注意,“输入”端点 2 与“输出”端点 2 是不同的端点(和管道)。当前配置控制任何给定端点的存在、类型和最大数据包大小。

stream_id

批量流的端点流 ID

status

这在非等时完成函数中读取,以获取特定请求的状态。ISO 请求仅使用它来判断 URB 是否已取消链接;每个帧的详细状态在 iso_frame-desc 的字段中。

transfer_flags

可以使用各种标志来影响 URB 提交、取消链接或操作的处理方式。不同类型的 URB 可以使用不同的标志。

transfer_buffer

这标识了将执行 I/O 请求的缓冲区(或者从其中执行 I/O 请求的缓冲区),除非设置了 URB_NO_TRANSFER_DMA_MAP(但是,即使在设置了 URB_NO_TRANSFER_DMA_MAP 的情况下,也不要在 transfer_buffer 中留下垃圾)。此缓冲区必须适合 DMA;使用 kmalloc() 或等效方法分配它。对于到“输入”端点的传输,将修改此缓冲区的内容。此缓冲区用于控制传输的数据阶段。

transfer_dma

当 transfer_flags 包括 URB_NO_TRANSFER_DMA_MAP 时,设备驱动程序表示它提供了此 DMA 地址,主机控制器驱动程序应优先使用该地址而不是 transfer_buffer。

sg

分散收集缓冲区列表,如果 ‘struct usb_bus’ 中未设置 no_sg_constraint,则列表中每个元素(最后一个元素除外)的缓冲区大小必须可被端点的最大数据包大小整除

num_mapped_sgs

(内部)映射的 sg 条目的数量

num_sgs

sg 列表中的条目数

transfer_buffer_length

transfer_buffer 有多大。根据端点当前的最大数据包大小,可以将传输分成若干块,该大小是配置的一个函数,并且编码在管道中。当长度为零时,不使用 transfer_buffer 和 transfer_dma。

actual_length

这在非等时完成函数中读取,它告诉传输了多少字节(超出 transfer_buffer_length)。除非报告了错误或执行了短读取,否则它通常与请求的字节数相同。可以使用 URB_SHORT_NOT_OK 传输标志将此类短读取报告为错误。

setup_packet

仅用于控制传输,此指针指向八个字节的设置数据。控制传输始终通过将此数据发送到设备来开始。然后,如果需要,将读取或写入 transfer_buffer。

setup_dma

设置数据包的 DMA 指针。调用者不得使用此字段;setup_packet 必须指向有效缓冲区。

start_frame

返回等时传输的初始帧。

number_of_packets

列出 ISO 传输缓冲区的数量。

interval

指定中断或等时传输的轮询间隔。对于全速和低速设备,单位是帧(毫秒),对于高速和超高速设备,单位是微帧(1/8 毫秒)。

error_count

返回报告错误的 ISO 传输数量。

context

用于完成函数中。这通常指向特定于请求的驱动程序上下文。

complete

完成处理程序。此 URB 作为参数传递给完成函数。然后,完成函数可以对 URB 执行任何操作,包括重新提交或释放它。

iso_frame_desc

用于提供 ISO 传输缓冲区数组,并收集每个缓冲区的传输状态。

描述

此结构标识 USB 传输请求。URB 必须通过调用 usb_alloc_urb() 来分配,并通过调用 usb_free_urb() 来释放。可以使用各种 usb_fill_*_urb() 函数完成初始化。使用 usb_submit_urb() 提交 URB,并且可以使用 usb_unlink_urb()usb_kill_urb() 取消挂起的请求。

数据传输缓冲区

通常,驱动程序会提供使用 kmalloc() 分配的 I/O 缓冲区,或者从通用页面池中获取的其他缓冲区。这是由 transfer_buffer 提供(控制请求还使用 setup_packet),并且主机控制器驱动程序会为每个传输的缓冲区执行 DMA 映射(和取消映射)。在某些平台上,这些映射操作可能会很昂贵(可能使用 DMA 反弹缓冲区或与 IOMMU 通信),尽管它们在商品 x86 和 ppc 硬件上很便宜。

或者,驱动程序可以传递 URB_NO_TRANSFER_DMA_MAP 传输标志,该标志告诉主机控制器驱动程序,由于设备驱动程序是 DMA 感知的,因此不需要对 transfer_buffer 进行此类映射。例如,设备驱动程序可以使用 usb_alloc_coherent() 分配 DMA 缓冲区,或者调用 usb_buffer_map()。当提供此传输标志时,主机控制器驱动程序将尝试使用在 transfer_dma 字段中找到的 DMA 地址,而不是自己确定 DMA 地址。

请注意,如果控制器不支持 DMA(如 hcd_uses_dma() 所示)并且与根集线器通信时,仍必须设置 transfer_buffer。如果必须在此类控制器上的 highmem 区域和设备之间传输,请创建反弹缓冲区或因错误而中止。如果无法设置 transfer_buffer(在 highmem 中)并且控制器具有 DMA 功能,请为其分配 NULL,以便 usbmon 知道不使用该值。必须始终设置 setup_packet,因此它不能位于 highmem 中。

初始化

所有提交的 URB 都必须初始化 dev、pipe、transfer_flags(可能为零)和 complete 字段。所有 URB 还必须初始化 transfer_buffer 和 transfer_buffer_length。它们可以提供 URB_SHORT_NOT_OK 传输标志,指示将短读取视为错误;该标志对于写请求无效。

批量 URB 可以使用 URB_ZERO_PACKET 传输标志,表示批量 OUT 传输应始终以短数据包终止,即使这意味着添加一个额外的零长度数据包。

控制 URB 必须在 setup_packet 字段中提供有效的指针。与 transfer_buffer 不同,setup_packet 不能预先映射为 DMA。

中断 URB 必须提供一个间隔,说明轮询传输的频率(以毫秒为单位,或者对于高速设备,以 125 微秒为单位)。提交 URB 后,间隔字段会反映实际调度的传输方式。轮询间隔可能比请求的更频繁。例如,某些控制器最大间隔为 32 毫秒,而另一些控制器则支持最大 1024 毫秒的间隔。同步 URB 也有传输间隔。(请注意,对于同步端点以及高速中断端点,端点描述符中传输间隔的编码是对数的。设备驱动程序必须自己将该值转换为线性单位。)

如果同步端点队列尚未运行,则主机控制器将在带宽利用率允许的情况下尽快安排新的 URB 开始。如果队列正在运行,则将安排新的 URB 在前一个 URB 结束后第一个传输时隙开始,前提是该时隙尚未过期。如果时隙已过期(当 IRQ 传递延迟很长时间时可能会发生这种情况),则调度行为取决于 URB_ISO_ASAP 标志。如果该标志被清除,则 URB 将被安排在过期的时隙开始,这意味着其某些数据包将不会被传输;如果设置了该标志,则 URB 将被安排在第一个未过期的时隙中,从而破坏队列的同步。URB 完成后,start_frame 字段将被设置为安排传输的(微)帧号。帧计数器值的范围是特定于 HC 的,可以从低至 256 到高达 65536 帧不等。

同步 URB 具有不同的数据传输模型,部分原因是服务质量仅为“尽力而为”。调用者提供特殊分配的 URB,末尾有 number_of_packets 个 iso_frame_desc 结构。每个这样的数据包都是一个单独的 ISO 传输。通常将同步 URB 排队,由驱动程序提交以安排传输至少进行双缓冲,然后在完成处理程序中显式重新提交,以便数据(例如音频或视频)以主机控制器调度程序可以支持的尽可能恒定的速率流式传输。

完成回调

完成回调是在 in_interrupt() 中进行的,完成处理程序应做的第一件事之一是检查状态字段。状态字段为所有 URB 提供。它用于报告未链接的 URB 以及所有非 ISO 传输的状态。在 URB 返回到完成处理程序之前,不应检查它。

上下文字段通常用于将 URB 链接回相关的驱动程序或请求状态。

当为非同步 URB 调用完成回调时,actual_length 字段会告知传输了多少字节。即使 URB 以错误终止或未链接,也会更新此字段。

ISO 传输状态在 iso_frame_desc 数组的状态和 actual_length 字段中报告,错误数量在 error_count 中报告。ISO 传输的完成回调通常会(重新)提交 URB,以确保恒定的传输速率。

请注意,即使标记为“公共”的字段,当 urb 由 hcd 拥有时,驱动程序也不应触碰这些字段,也就是说,从调用 usb_submit_urb() 到进入完成例程之间。

void usb_fill_control_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, unsigned char *setup_packet, void *transfer_buffer, int buffer_length, usb_complete_t complete_fn, void *context)

初始化控制 urb

参数

struct urb *urb

指向要初始化的 urb 的指针。

struct usb_device *dev

指向此 urb 的 struct usb_device 的指针。

unsigned int pipe

端点管道

unsigned char *setup_packet

指向 setup_packet 缓冲区的指针。该缓冲区必须适合 DMA。

void *transfer_buffer

指向传输缓冲区的指针。该缓冲区必须适合 DMA。

int buffer_length

传输缓冲区的长度

usb_complete_t complete_fn

指向 usb_complete_t 函数的指针

void *context

要将 urb 上下文设置为的内容。

描述

使用提交给设备所需的适当信息初始化控制 urb。

传输缓冲区和 setup_packet 缓冲区很可能会通过 DMA 填充或读取。获取可以 DMA 的缓冲区的最简单方法是通过 kmalloc() 或等效方法分配它,即使对于很小的缓冲区也是如此。如果缓冲区嵌入在较大的结构中,则由于缓存不一致,缓冲区本身、之前的字段和/或后续字段可能会损坏;或者如果它们从缓存中逐出,则会减慢速度。有关更多信息,请查看 struct urb

void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, void *transfer_buffer, int buffer_length, usb_complete_t complete_fn, void *context)

帮助初始化批量 urb 的宏

参数

struct urb *urb

指向要初始化的 urb 的指针。

struct usb_device *dev

指向此 urb 的 struct usb_device 的指针。

unsigned int pipe

端点管道

void *transfer_buffer

指向传输缓冲区的指针。该缓冲区必须适合 DMA。

int buffer_length

传输缓冲区的长度

usb_complete_t complete_fn

指向 usb_complete_t 函数的指针

void *context

要将 urb 上下文设置为的内容。

描述

使用提交给设备所需的适当信息初始化批量 urb。

有关 transfer_buffer 的要求的描述,请参阅 usb_fill_control_urb()

void usb_fill_int_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, void *transfer_buffer, int buffer_length, usb_complete_t complete_fn, void *context, int interval)

帮助初始化中断 urb 的宏

参数

struct urb *urb

指向要初始化的 urb 的指针。

struct usb_device *dev

指向此 urb 的 struct usb_device 的指针。

unsigned int pipe

端点管道

void *transfer_buffer

指向传输缓冲区的指针。该缓冲区必须适合 DMA。

int buffer_length

传输缓冲区的长度

usb_complete_t complete_fn

指向 usb_complete_t 函数的指针

void *context

要将 urb 上下文设置为的内容。

int interval

要设置 urb 间隔的值,编码方式类似于端点描述符的 bInterval 值。

描述

使用提交给设备所需的适当信息初始化中断 urb。

有关 transfer_buffer 的要求的描述,请参阅 usb_fill_control_urb()

请注意,高速和超高速 (+) 中断端点使用端点间隔的对数编码,并以微帧(每毫秒 8 个)而不是帧(每毫秒 1 个)表示轮询间隔。

int usb_urb_dir_in(struct urb *urb)

检查 URB 是否描述 IN 传输

参数

struct urb *urb

要检查的 URB

返回值

如果 urb 描述 IN 传输(设备到主机),则为 1,否则为 0。

int usb_urb_dir_out(struct urb *urb)

检查 URB 是否描述 OUT 传输

参数

struct urb *urb

要检查的 URB

返回值

如果 urb 描述 OUT 传输(主机到设备),则为 1,否则为 0。

struct usb_sg_request

支持散布/收集 I/O

定义:

struct usb_sg_request {
    int status;
    size_t bytes;
};

成员

status

零表示成功,否则为负 errno

字节

计数传输的字节数。

描述

这些请求使用 usb_sg_init() 初始化,然后用作传递给 usb_sg_wait()usb_sg_cancel() 的请求句柄。请求对象的大多数成员不供驱动程序访问。

只有在 usb_sg_wait() 返回后,状态和字节计数的值才有效。如果状态为零,则字节计数与请求的总数匹配。

在错误完成后,驱动程序可能需要清除端点上的暂停条件。

USB 核心 API

USB API 中有两种基本的 I/O 模型。最基本的一种是异步的:驱动程序以 URB 的形式提交请求,URB 的完成回调处理下一步。所有 USB 传输类型都支持该模型,尽管控制 URB(始终具有设置和状态阶段,但可能没有数据阶段)和等时 URB(允许大数据包并包含每个数据包的故障报告)存在特殊情况。在此基础上构建的是同步 API 支持,其中驱动程序调用一个例程,该例程分配一个或多个 URB,提交它们,并等待它们完成。存在用于单缓冲区控制和批量传输(在某些驱动程序断开连接的情况下使用起来很尴尬)以及基于散列表的流式 I/O(批量或中断)的同步包装器。

USB 驱动程序需要提供可用于 DMA 的缓冲区,尽管它们不一定需要自己提供 DMA 映射。有一些 API 可用于分配 DMA 缓冲区,这可以防止在某些系统上使用反弹缓冲区。在某些情况下,驱动程序可能能够依靠 64 位 DMA 来消除另一种反弹缓冲区。

void usb_init_urb(struct urb *urb)

初始化 urb,以便 USB 驱动程序可以使用它

参数

struct urb *urb

指向要初始化的 urb 的指针

描述

初始化 urb,以便 USB 子系统可以正确使用它。

如果使用调用 usb_alloc_urb() 创建了 urb,则无需调用此函数。仅当您自己分配了 struct urb 的空间时才使用此函数。如果调用此函数,请在释放您的 urb 的内存时小心,确保它不再被 USB 核心使用。

仅当您_真正_了解自己在做什么时才使用此函数。

struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)

创建一个新的 urb 供 USB 驱动程序使用

参数

int iso_packets

此 urb 的 iso 数据包数

gfp_t mem_flags

要分配的内存类型,有关此类型的有效选项列表,请参阅 kmalloc()

描述

创建供 USB 驱动程序使用的 urb,初始化一些内部结构,增加使用计数器,并返回指向它的指针。

如果驱动程序希望将此 urb 用于中断、控制或批量端点,则将 ‘0’ 作为 iso 数据包的数量传递。

当驱动程序完成 urb 后,必须调用 usb_free_urb()

返回值

指向新 urb 的指针,如果没有可用内存,则为 NULL

void usb_free_urb(struct urb *urb)

当所有用户都完成使用 urb 时,释放该 urb 使用的内存

参数

struct urb *urb

指向要释放的 urb 的指针,可以为 NULL

描述

当 urb 的用户完成使用时,必须调用此函数。当 urb 的最后一个用户调用此函数时,urb 的内存将被释放。

注意

除非设置了 URB_FREE_BUFFER 传输标志,否则不会释放与 urb 关联的传输缓冲区。

struct urb *usb_get_urb(struct urb *urb)

增加 urb 的引用计数

参数

struct urb *urb

指向要修改的 urb 的指针,可以为 NULL

描述

每当 urb 从设备驱动程序传输到主机控制器驱动程序时,都必须调用此函数。这允许对 urb 进行正确的引用计数。

返回值

指向具有递增的引用计数器的 urb 的指针。

void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)

在处理 URB 时将其锚定

参数

struct urb *urb

指向要锚定的 urb 的指针

struct usb_anchor *anchor

指向锚点的指针

描述

可以调用此函数来访问要执行的 URB,而无需费心跟踪它们

void usb_unanchor_urb(struct urb *urb)

取消锚定 URB

参数

struct urb *urb

指向要锚定的 urb 的指针

描述

调用此函数以停止系统跟踪此 URB

int usb_pipe_type_check(struct usb_device *dev, unsigned int pipe)

对 usb 设备的特定管道进行健全性检查

参数

struct usb_device *dev

要检查的 struct usb_device

unsigned int pipe

要检查的管道

描述

这将对给定 usb 设备中的端点执行轻量级健全性检查。如果管道对于特定 usb 设备有效,则返回 0,否则返回负错误代码。

int usb_urb_ep_type_check(const struct urb *urb)

对给定 urb 中的端点进行健全性检查

参数

const struct urb *urb

要检查的 urb

描述

这将对给定 urb 中的端点执行轻量级健全性检查。如果 urb 包含有效的端点,则返回 0,否则返回负错误代码。

int usb_submit_urb(struct urb *urb, gfp_t mem_flags)

为端点发出异步传输请求

参数

struct urb *urb

指向描述请求的 urb 的指针

gfp_t mem_flags

要分配的内存类型,有关此类型的有效选项列表,请参阅 kmalloc()

描述

这将提交传输请求,并将描述该请求的 URB 的控制权转移到 USB 子系统。请求完成将稍后通过调用完成处理程序异步指示。三种类型的完成是成功、错误和取消链接(软件引起的故障,也称为“请求取消”)。

URB 可以在中断上下文中提交。

调用者必须在提交 URB 之前正确初始化它。诸如 usb_fill_bulk_urb()usb_fill_control_urb() 等函数可用于确保大多数字段针对特定类型的传输得到正确初始化,尽管它们不会初始化任何传输标志。

如果提交成功,则在 USB 核心和主机控制器驱动程序 (HCD) 完成 URB 后,将准确调用一次 URB 的 complete() 回调。当调用完成函数时,URB 的控制权将返回给发出请求的设备驱动程序。然后,完成处理程序可以立即释放或重用该 URB。

除了少数例外情况,在调用 complete() 之前,USB 设备驱动程序绝不应访问 usbcore 或 HCD 提供的 URB 字段。这些例外情况与定期传输调度相关。对于中断和同步 URB,作为成功 URB 提交的一部分,urb->interval 将被修改以反映所使用的实际传输周期(通常是 2 的某个幂)。对于同步 URB,urb->start_frame 将被修改以反映 URB 的传输计划开始时间。

并非所有同步传输调度策略都有效,但大多数主机控制器驱动程序应能轻松处理从现在到未来 10-200 毫秒的 ISO 队列。驱动程序应尽量在队列中保留至少 1 到 2 毫秒的数据;许多控制器要求新传输在添加时至少在未来 1 毫秒开始。如果驱动程序无法跟上并且队列为空,则新提交的行为由 URB_ISO_ASAP 标志控制。如果设置了该标志,或者队列处于空闲状态,则 URB 始终被分配到端点调度中第一个可用(且尚未过期)的槽中。如果未设置该标志且队列处于活动状态,则 URB 始终被分配到端点先前 URB 结束后的下一个调度槽中,即使该槽已过时。当以这种方式将数据包分配到已过期的槽中时,数据包将不会被传输,并且相应的 usb_iso_packet_descriptor 的状态字段将返回 -EXDEV。如果 URB 中的所有数据包都发生这种情况,则提交将失败并显示 -EXDEV 错误代码。

对于控制端点,通常使用同步的 usb_control_msg() 调用(在非中断上下文中)来代替此调用。这通常通过便捷包装器使用,用于 USB 2.0 规范中标准化的请求。对于批量端点,可以使用同步的 usb_bulk_msg() 调用。

请求排队

可以在先前的 URB 完成之前将 URB 提交到端点,以最大限度地减少中断延迟和系统开销对数据吞吐量的影响。使用该排队策略,端点的队列永远不会为空。这对于连续的同步数据流是必需的,并且对于某些类型的中断传输也可能是必需的。这种排队还可以让 USB 控制器在驱动程序软件完成先前(成功)请求的完成处理之前开始处理稍后的请求,从而最大限度地提高带宽利用率。

从 Linux 2.6 开始,所有 USB 端点传输队列都支持深度大于 1。以前,除了 ISO 传输之外,这是一种特定于 HCD 的行为。在故障(传输错误或取消)后的清理过程中,非同步端点队列处于非活动状态。

保留带宽传输

定期传输(中断或同步)使用 URB 中指定的间隔重复执行。将第一个 URB 提交到端点会保留执行这些传输所需的带宽。如果 USB 子系统无法分配足够的带宽来执行定期请求,则提交此类定期请求应该会失败。

对于 xHCI 下的设备,带宽在配置时或选择备用设置时保留。如果总线带宽不足,则配置/备用设置请求将失败。因此,在 xHCI 下的设备上向定期端点提交请求绝不应因带宽限制而失败。

设备驱动程序必须通过确保某些 URB 始终位于端点的队列中(可能在完成回调期间的短时间内除外)来显式请求重复。当不再有 URB 排队时,将取消端点的带宽预留。这意味着驱动程序可以使用其完成处理程序来确保他们保留所需的带宽,方法是重新初始化并重新提交刚刚完成的 URB,直到驱动程序不再需要该定期带宽为止。

内存标志

关于如何决定使用哪个 mem_flags 的一般规则与 kmalloc 的规则相同。有四个不同的可能值:GFP_KERNEL、GFP_NOFS、GFP_NOIO 和 GFP_ATOMIC。

GFP_NOFS 从未使用过,因为它尚未实现。

以下情况使用 GFP_ATOMIC
  1. 你位于完成处理程序、中断、下半部、任务或计时器内,或

  2. 你持有自旋锁或读写锁(不适用于信号量),或

  3. current->state != TASK_RUNNING,这仅在你更改它之后才会发生。

GFP_NOIO 在存储设备的块 IO 路径和错误处理中使用。

所有其他情况都使用 GFP_KERNEL。

可以推断出一些更具体的 mem_flags 规则,例如
  1. 网络驱动程序的 start_xmit、timeout 和 receive 方法必须使用 GFP_ATOMIC(它们是在持有自旋锁的情况下调用的);

  2. scsi 驱动程序的 queuecommand 方法必须使用 GFP_ATOMIC(也是在持有自旋锁的情况下调用的);

  3. 如果将内核线程与网络驱动程序一起使用,则必须使用 GFP_NOIO,除非 (b) 或 (c) 适用;

  4. 在完成 down() 后,可以使用 GFP_KERNEL,除非 (b) 或 (c) 适用,或者你位于存储驱动程序的块 IO 路径中;

  5. 除非 (b) 或 (c) 适用,否则 USB 探测和断开连接可以使用 GFP_KERNEL;并且

  6. 在运行的存储或网络设备上更改固件使用 GFP_NOIO,除非 b) 或 c) 适用

返回值

成功提交时为 0。否则为负错误编号。

中止/取消端点的传输请求

参数

struct urb *urb

指向描述先前提交的请求的 URB 的指针,可能为 NULL

描述

此例程将取消正在进行的请求。URB 每次提交仅完成一次,并且每次提交仅可取消一次。成功取消意味着将加速终止 urb,并且将使用指示请求已取消(而不是任何其他代码)的状态代码调用完成处理程序。

驱动程序在其断开连接方法返回后,不应调用此例程或相关例程,例如 usb_kill_urb()usb_unlink_anchored_urbs()。断开连接函数应与驱动程序的 I/O 例程同步,以确保在返回之前所有与 URB 相关的活动都已完成。

此请求是异步的,但是 HCD 可能会在取消链接期间调用 ->complete() 回调。因此,当驱动程序调用 usb_unlink_urb() 时,它们不得持有任何可能被完成函数获取的锁。成功通过返回 -EINPROGRESS 表示,此时 URB 可能尚未返回给设备驱动程序。最终调用时,完成函数将看到 urb->status == -ECONNRESET。失败通过 usb_unlink_urb() 返回任何其他值表示。当 urb 当前未“链接”(即,从未提交,或之前已取消链接,或硬件已完成处理)时,取消链接将失败,即使完成处理程序尚未运行。

当此例程运行时,不得取消分配 URB。特别是,当驱动程序调用此例程时,它必须确保完成处理程序无法取消分配 URB。

取消链接和端点队列

[下面描述的行为和保证不适用于虚拟根集线器,而仅适用于物理 USB 设备的端点队列。]

主机控制器驱动程序 (HCD) 将特定端点的所有 URB 放入队列中。通常,当控制器硬件处理每个请求时,队列会前进。但是,当 URB 因错误而终止时,其队列通常会停止(请参见下文),至少在 URB 的完成例程返回之前是这样。保证停止的队列不会重新启动,直到其所有取消链接的 URB 都已完全停用,并且其完成例程已运行,即使那是在原始完成处理程序返回后的一段时间之后。当 URB 因取消链接而终止时,应用相同的行为和保证。

只要 URB 因任何类型的错误而终止,包括 -ECONNRESET、-ENOENT 和 -EREMOTEIO,批量和中断端点队列就会停止。控制端点队列的行为方式相同,只是它们不保证因 -EREMOTEIO 错误而停止。同步端点的队列的处理方式不同,因为它们必须以固定速率前进。当 URB 遇到错误或取消链接时,此类队列不会停止。取消链接的同步 URB 可能会在数据包流中留下间隙;是否可以填充这些间隙是未定义的。

请注意,如果设置了 URB_SHORT_NOT_OK 标志,则仅当收到短数据包时 URB 的提前终止才会生成 -EREMOTEIO 错误。通过设置此标志,USB 设备驱动程序可以为大型或复杂的批量传输构建深层队列,并通过在第一次故障时取消链接所有挂起的 URB,在任何类型的中止传输后可靠地清理它们。

当控制 URB 因 -EREMOTEIO 以外的错误而终止时,很可能不会发生传输的状态阶段。

返回值

成功时为 -EINPROGRESS。有关失败的其他值,请参见说明。

void usb_kill_urb(struct urb *urb)

取消传输请求并等待其完成

参数

struct urb *urb

指向描述先前提交的请求的 URB 的指针,可能为 NULL

描述

此例程会取消正在进行中的请求。保证在返回时,所有完成处理程序都已完成,并且 URB 将完全空闲且可供重用。这些特性使其成为在 disconnect() 回调或 close() 函数中停止 I/O 的理想方法。如果请求尚未完成或取消链接,完成处理程序将看到 urb->status == -ENOENT。

在例程运行时,尝试重新提交 URB 将失败,并出现错误 -EPERM。因此,即使 URB 的完成处理程序总是尝试重新提交,它也不会成功,并且 URB 将变为闲置状态。

当此例程运行时,不得取消分配 URB。特别是,当驱动程序调用此例程时,它必须确保完成处理程序无法取消分配 URB。

此例程不得在中断上下文(例如下半部或完成处理程序)中或在持有自旋锁时或在调用者无法 schedule() 的其他情况下使用。

驱动程序的 disconnect 方法返回后,不应调用此例程。

void usb_poison_urb(struct urb *urb)

可靠地终止传输并防止进一步使用 URB

参数

struct urb *urb

指向描述先前提交的请求的 URB 的指针,可能为 NULL

描述

此例程会取消正在进行中的请求。保证在返回时,所有完成处理程序都已完成,并且 URB 将完全空闲且不可重用。这些特性使其成为在 disconnect() 回调中停止 I/O 的理想方法。如果请求尚未完成或取消链接,完成处理程序将看到 urb->status == -ENOENT。

在例程运行期间和之后,尝试重新提交 URB 将失败,并出现错误 -EPERM。因此,即使 URB 的完成处理程序总是尝试重新提交,它也不会成功,并且 URB 将变为闲置状态。

当此例程运行时,不得取消分配 URB。特别是,当驱动程序调用此例程时,它必须确保完成处理程序无法取消分配 URB。

此例程不得在中断上下文(例如下半部或完成处理程序)中或在持有自旋锁时或在调用者无法 schedule() 的其他情况下使用。

驱动程序的 disconnect 方法返回后,不应调用此例程。

void usb_block_urb(struct urb *urb)

可靠地防止进一步使用 URB

参数

struct urb *urb

要阻止的 URB 指针,可以为 NULL

描述

在例程运行后,尝试重新提交 URB 将失败,并出现错误 -EPERM。因此,即使 URB 的完成处理程序总是尝试重新提交,它也不会成功,并且 URB 将变为闲置状态。

当此例程运行时,不得取消分配 URB。特别是,当驱动程序调用此例程时,它必须确保完成处理程序无法取消分配 URB。

void usb_kill_anchored_urbs(struct usb_anchor *anchor)

终止与锚点关联的所有 URB

参数

struct usb_anchor *anchor

请求绑定到的锚点

描述

这将终止从队列尾部开始的所有未完成 URB,并保证在此函数返回后,不会从锚点发生任何完成回调。

驱动程序的 disconnect 方法返回后,不应调用此例程。

void usb_poison_anchored_urbs(struct usb_anchor *anchor)

停止来自锚点的所有流量

参数

struct usb_anchor *anchor

请求绑定到的锚点

描述

这允许从队列尾部开始中毒所有未完成的 URB。新添加的 URB 也将被中毒

驱动程序的 disconnect 方法返回后,不应调用此例程。

void usb_unpoison_anchored_urbs(struct usb_anchor *anchor)

允许再次成功使用锚点

参数

struct usb_anchor *anchor

请求绑定到的锚点

描述

反转 usb_poison_anchored_urbs 的效果,锚点在返回后可以正常使用

异步批量取消传输请求

参数

struct usb_anchor *anchor

请求绑定到的锚点

描述

这允许从队列尾部开始取消链接所有未完成的 URB。此函数是异步的。取消链接只是被触发。它可能会在此函数返回后发生。

驱动程序的 disconnect 方法返回后,不应调用此例程。

void usb_anchor_suspend_wakeups(struct usb_anchor *anchor)

参数

struct usb_anchor *anchor

您要在其上暂停唤醒的锚点

描述

调用此函数以停止最后一个从取消锚定中唤醒任何 usb_wait_anchor_empty_timeout 等待者的 urb。这在 hcd urb give-back 路径中使用,以延迟唤醒,直到完成处理程序运行之后。

void usb_anchor_resume_wakeups(struct usb_anchor *anchor)

参数

struct usb_anchor *anchor

您要在其上恢复唤醒的锚点

描述

允许再次唤醒 usb_wait_anchor_empty_timeout 等待者,如果锚点为空,则唤醒任何当前的等待者。

int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor, unsigned int timeout)

等待锚点未使用

参数

struct usb_anchor *anchor

您希望变为空闲的锚点

unsigned int timeout

您愿意等待多长时间,以毫秒为单位

描述

如果您想确保锚点的所有 URB 都已完成,请调用此函数

返回值

如果锚点变为空闲,则为非零值。超时时为零。

struct urb *usb_get_from_anchor(struct usb_anchor *anchor)

获取锚点最旧的 urb

参数

struct usb_anchor *anchor

您想要其 urb 的锚点

描述

这将从锚点获取最旧的 urb,取消锚定并返回它

返回值

来自 anchor 的最旧的 urb,如果 anchor 没有与之关联的 urbs,则为 NULL

void usb_scuttle_anchored_urbs(struct usb_anchor *anchor)

取消锚点所有 urbs 的锚定

参数

struct usb_anchor *anchor

您要取消锚定的锚点的 urbs

描述

使用此方法来摆脱锚点的所有 urbs

int usb_anchor_empty(struct usb_anchor *anchor)

锚点是否为空

参数

struct usb_anchor *anchor

您要查询的锚点

返回值

如果锚点没有与之关联的 urbs,则为 1。

int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout)

构建控制 urb,发送并等待完成

参数

struct usb_device *dev

指向要向其发送消息的 usb 设备的指针

unsigned int pipe

要向其发送消息的端点“pipe”

__u8 request

USB 消息请求值

__u8 requesttype

USB 消息请求类型值

__u16 value

USB 消息值

__u16 index

USB 消息索引值

void *data

指向要发送的数据的指针

__u16 size

要发送的数据的字节长度

int timeout

等待消息完成的超时时间(毫秒),如果为 0 则表示永远等待

上下文

任务上下文,可能会睡眠。

描述

此函数向指定的端点发送一个简单的控制消息,并等待消息完成或超时。

请勿在中断上下文中使用此函数。如果需要异步消息,或者需要在中断上下文中发送消息,请使用 usb_submit_urb()。如果驱动程序中的线程使用此调用,请确保您的 disconnect() 方法可以等待它完成。由于您没有使用 URB 的句柄,因此无法取消请求。

返回值

如果成功,则返回传输的字节数。否则,返回一个负的错误号。

int usb_control_msg_send(struct usb_device *dev, __u8 endpoint, __u8 request, __u8 requesttype, __u16 value, __u16 index, const void *driver_data, __u16 size, int timeout, gfp_t memflags)

构建一个控制“发送”消息,发送它并等待完成

参数

struct usb_device *dev

指向要向其发送消息的 usb 设备的指针

__u8 endpoint

要向其发送消息的端点

__u8 request

USB 消息请求值

__u8 requesttype

USB 消息请求类型值

__u16 value

USB 消息值

__u16 index

USB 消息索引值

const void *driver_data

指向要发送的数据的指针

__u16 size

要发送的数据的字节长度

int timeout

等待消息完成的超时时间(毫秒),如果为 0 则表示永远等待

gfp_t memflags

用于缓冲区内存分配的标志

上下文

!in_interrupt ()

描述

此函数向指定的端点发送一个控制消息,该端点不期望填充响应(即“发送消息”),并等待消息完成或超时。

请勿在中断上下文中使用此函数。如果需要异步消息,或者需要在中断上下文中发送消息,请使用 usb_submit_urb()。如果驱动程序中的线程使用此调用,请确保您的 disconnect() 方法可以等待它完成。由于您没有使用 URB 的句柄,因此无法取消请求。

数据指针可以指向堆栈上的引用或其他任何位置,因为它根本不会被修改。这没有 usb_control_msg() 的限制,即数据指针必须指向动态分配的内存(即可以成功 DMA 到设备的内存)。

返回值

如果成功,则返回 0。否则,返回一个负的错误号。

int usb_control_msg_recv(struct usb_device *dev, __u8 endpoint, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *driver_data, __u16 size, int timeout, gfp_t memflags)

构建一个控制“接收”消息,发送它并等待完成

参数

struct usb_device *dev

指向要向其发送消息的 usb 设备的指针

__u8 endpoint

要向其发送消息的端点

__u8 request

USB 消息请求值

__u8 requesttype

USB 消息请求类型值

__u16 value

USB 消息值

__u16 index

USB 消息索引值

void *driver_data

指向消息要填充的数据的指针

__u16 size

要接收的数据的字节长度

int timeout

等待消息完成的超时时间(毫秒),如果为 0 则表示永远等待

gfp_t memflags

用于缓冲区内存分配的标志

上下文

!in_interrupt ()

描述

此函数向指定的端点发送一个控制消息,该端点期望填充响应(即“接收消息”),并等待消息完成或超时。

请勿在中断上下文中使用此函数。如果需要异步消息,或者需要在中断上下文中发送消息,请使用 usb_submit_urb()。如果驱动程序中的线程使用此调用,请确保您的 disconnect() 方法可以等待它完成。由于您没有使用 URB 的句柄,因此无法取消请求。

数据指针可以指向堆栈上的引用或其他任何可以成功写入的位置。此函数没有 usb_control_msg() 的限制,即数据指针必须指向动态分配的内存(即可以成功 DMA 到设备的内存)。

必须从设备正确接收“整个”消息,此函数才能成功。如果设备返回的数据少于预期数量,则该函数将失败。不要将此函数用于可能返回可变数量的数据的消息。

返回值

如果成功,则返回 0。否则,返回一个负的错误号。

int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)

构建一个中断 urb,发送它并等待完成

参数

struct usb_device *usb_dev

指向要向其发送消息的 usb 设备的指针

unsigned int pipe

要向其发送消息的端点“pipe”

void *data

指向要发送的数据的指针

int len

要发送的数据的字节长度

int *actual_length

指向存储实际传输的字节数的位置的指针

int timeout

等待消息完成的超时时间(毫秒),如果为 0 则表示永远等待

上下文

任务上下文,可能会睡眠。

描述

此函数向指定的端点发送一个简单的中断消息,并等待消息完成或超时。

请勿在中断上下文中使用此函数。如果需要异步消息,或者需要在中断上下文中发送消息,请使用 usb_submit_urb()。如果驱动程序中的线程使用此调用,请确保您的 disconnect() 方法可以等待它完成。由于您没有使用 URB 的句柄,因此无法取消请求。

返回值

如果成功,则返回 0。否则,返回一个负的错误号。实际传输的字节数将存储在 actual_length 参数中。

int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)

构建一个批量 urb,发送它并等待完成

参数

struct usb_device *usb_dev

指向要向其发送消息的 usb 设备的指针

unsigned int pipe

要向其发送消息的端点“pipe”

void *data

指向要发送的数据的指针

int len

要发送的数据的字节长度

int *actual_length

指向存储实际传输的字节数的位置的指针

int timeout

等待消息完成的超时时间(毫秒),如果为 0 则表示永远等待

上下文

任务上下文,可能会睡眠。

描述

此函数向指定的端点发送一个简单的批量消息,并等待消息完成或超时。

请勿在中断上下文中使用此函数。如果需要异步消息,或者需要在中断上下文中发送消息,请使用 usb_submit_urb()。如果驱动程序中的线程使用此调用,请确保您的 disconnect() 方法可以等待它完成。由于您没有使用 URB 的句柄,因此无法取消请求。

由于没有 usb_interrupt_msg() 也没有 USBDEVFS_INTERRUPT ioctl,因此用户被迫滥用此例程,通过它为中断端点提交 URB。如果目标是中断端点,我们将采取创建中断 URB(使用默认间隔)的自由。

返回值

如果成功,则返回 0。否则,返回一个负的错误号。实际传输的字节数将存储在 actual_length 参数中。

int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, unsigned pipe, unsigned period, struct scatterlist *sg, int nents, size_t length, gfp_t mem_flags)

初始化基于散列表的批量/中断 I/O 请求

参数

struct usb_sg_request *io

正在初始化请求块。在 usb_sg_wait() 返回之前,将其视为指向不透明内存块的指针,

struct usb_device *dev

将发送或接收数据的 USB 设备

unsigned pipe

用于传输数据的端点“管道”

unsigned period

中断端点的轮询速率,以帧为单位或(对于高速端点)以微帧为单位;对于批量传输,此项将被忽略

struct scatterlist *sg

散列表条目

int nents

散列表中有多少条目

size_t length

要从散列表中发送多少字节,或者为零以发送列表中标识的每个字节。

gfp_t mem_flags

影响此调用中内存分配的 SLAB_* 标志

描述

这将初始化一个散布/收集请求,分配诸如 I/O 映射和 urb 内存之类的资源(可能不包括 USB 控制器驱动程序使用的内存)。

必须使用 usb_sg_wait() 发出请求,该请求会等待 I/O 完成(或被取消),然后清理 usb_sg_init() 分配的所有资源。

可以使用 usb_sg_cancel() 取消请求,可以在调用 usb_sg_wait() 之前或之后进行取消。

返回值

成功时为零,否则为负的 errno 值。

void usb_sg_wait(struct usb_sg_request *io)

同步执行散布/收集请求

参数

struct usb_sg_request *io

请求块句柄,由 usb_sg_init() 初始化。当此调用返回时,某些字段变为可访问。

上下文

任务上下文,可能会睡眠。

描述

此函数会阻塞,直到指定的 I/O 操作完成。它利用相关 I/O 请求的分组,通过将请求排队来获得良好的传输速率。在更高的速度下,这种排队可以显著提高 USB 吞吐量。

此函数有三种完成类型。

  1. 成功,其中 io->status 为零。传输的 io->bytes 的数量与请求的数量相同。

  2. 错误,其中 io->status 为负的 errno 值。错误之前传输的 io->bytes 的数量通常少于请求的数量,并且可以为非零值。

  3. 取消,一种错误类型,状态为 -ECONNRESET,由 usb_sg_cancel() 启动。

当此函数返回时,通过 usb_sg_init() 或此调用分配的所有内存都将被释放。请求块参数仍然可以传递给 usb_sg_cancel(),或者可以释放它。它也可以重新初始化然后重复使用。

数据传输速率

批量传输对于全速或高速端点有效。最佳全速数据速率是每帧 19 个数据包,每个数据包 64 字节,即每毫秒 1216 字节。最佳高速数据速率是每个微帧 13 个数据包,每个数据包 512 字节,即每毫秒 52 KB。

通过此 API 使用中断传输的原因很可能是为了保留高速带宽,在这种情况下,每毫秒最多可以传输 24 KB。对于低速或全速中断端点,此功能不太有用,因为它们每毫秒最多允许一个数据包,分别为最多 8 或 64 字节。

对于 xHCI 主控制器下的设备,无需调用此函数来保留带宽,因为在选择配置或接口备用设置时会保留带宽。

void usb_sg_cancel(struct usb_sg_request *io)

停止 usb_sg_wait() 发出的散布/收集 I/O

参数

struct usb_sg_request *io

请求块,由 usb_sg_init() 初始化

描述

这会停止 usb_sg_wait() 启动的请求。它还可以防止 usb_sg_init() 初始化的请求启动,因此该调用只是释放为请求分配的资源。

int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size)

发出通用的 GET_DESCRIPTOR 请求

参数

struct usb_device *dev

正在检索其描述符的设备

unsigned char type

描述符类型 (USB_DT_*)

unsigned char index

描述符的编号

void *buf

放置描述符的位置

int size

“buf” 的大小?

上下文

任务上下文,可能会睡眠。

描述

获取 USB 描述符。存在方便函数以简化获取某些类型的描述符。对于 USB_DT_STRING,请使用 usb_get_string() 或 usb_string()。设备 (USB_DT_DEVICE) 和配置描述符 (USB_DT_CONFIG) 是设备结构的一部分。除了许多 USB 标准描述符外,某些设备还使用特定于类或特定于供应商的描述符。

此调用是同步的,并且不能在中断上下文中使用。

返回值

成功接收的字节数,否则为底层 usb_control_msg() 调用返回的状态代码。

int usb_string(struct usb_device *dev, int index, char *buf, size_t size)

返回字符串描述符的 UTF-8 版本

参数

struct usb_device *dev

正在检索其字符串描述符的设备

int index

描述符的编号

char *buf

存放字符串的位置

size_t size

“buf” 的大小?

上下文

任务上下文,可能会睡眠。

描述

这会将设备返回的 UTF-16LE 编码的字符串(来自 usb_get_string_descriptor())转换为以 null 结尾的 UTF-8 编码的字符串,这些字符串在大多数内核上下文中更易于使用。请注意,此函数选择设备支持的第一种语言的字符串。

此调用是同步的,并且不能在中断上下文中使用。

返回值

字符串的长度(>= 0)或 usb_control_msg 状态(< 0)。

char *usb_cache_string(struct usb_device *udev, int index)

读取字符串描述符并缓存它以供以后使用

参数

struct usb_device *udev

正在读取其字符串描述符的设备

int index

描述符索引

返回值

指向包含描述符字符串的 kmalloc 缓冲区的指针,如果索引为 0 或无法读取字符串,则为 NULL

int usb_get_status(struct usb_device *dev, int recip, int type, int target, void *data)

发出 GET_STATUS 调用

参数

struct usb_device *dev

正在检查其状态的设备

int recip

USB_RECIP_*;对于设备、接口或端点

int type

USB_STATUS_TYPE_*; 用于标准或 PTM 状态类型

int target

零(对于设备),否则为接口或端点编号

void *data

指向两个字节位图数据的指针

上下文

任务上下文,可能会睡眠。

描述

返回设备、接口或端点状态。通常仅用于查看设备是否为自供电,或是否启用了远程唤醒功能;或者批量或中断端点是否已停止(“停滞”)。

这些状态位图中的位是使用 SET_FEATURE 请求设置的,并使用 CLEAR_FEATURE 请求清除的。应该使用 usb_clear_halt() 函数来清除停止(“停滞”)状态。

此调用是同步的,并且不能在中断上下文中使用。

成功时返回 0,并将状态值置于 *data 中(以主机字节序);否则返回底层 usb_control_msg() 调用返回的状态码。

int usb_clear_halt(struct usb_device *dev, int pipe)

告诉设备清除端点停止/停滞状态

参数

struct usb_device *dev

端点已停止的设备

int pipe

正在清除的端点“管道”

上下文

任务上下文,可能会睡眠。

描述

这用于清除批量和中断端点的停止状态,正如 URB 完成状态所报告的那样。停止的端点有时被称为“停滞”。在清除停止状态之前,此类端点无法发送或接收数据。对于此类端点排队的任何 URB 通常应由驱动程序取消链接,然后再清除停止状态,如 USB 2.0 规范的 5.7.5 和 5.8.5 节所述。

请注意,控制和同步端点不会停止,尽管控制端点使用与报告真正停止相同的状态码报告“协议停滞”(对于不支持的请求)。

此调用是同步的,不能在中断上下文中使用。如果驱动程序中的线程使用此调用,请确保您的 disconnect() 方法可以等待其完成。

返回值

成功时返回零,否则返回底层 usb_control_msg() 调用返回的状态码。

void usb_reset_endpoint(struct usb_device *dev, unsigned int epaddr)

重置端点的状态。

参数

struct usb_device *dev

要重置其端点的设备

unsigned int epaddr

端点的地址。输出的端点编号,输入的端点编号 + USB_DIR_IN

描述

重置任何主机端端点状态,例如切换位、序列号或当前窗口。

int usb_set_interface(struct usb_device *dev, int interface, int alternate)

使特定的备用设置生效

参数

struct usb_device *dev

正在更新其接口的设备

int interface

正在更新的接口

int alternate

正在选择的设置。

上下文

任务上下文,可能会睡眠。

描述

这用于在默认情况下可能未启用的接口上启用数据传输。并非所有设备都支持这种可配置性。只有绑定到接口的驱动程序才可以更改其设置。

在任何给定的配置中,每个接口可能有多个备用设置。这些通常用于控制带宽消耗的级别。例如,高速中断端点的默认设置可能每个微帧发送不超过 64 个字节,而每个微帧最多 3K 字节的中断传输是合法的。此外,同步端点可能永远不会成为接口默认设置的一部分。要访问此类带宽,必须使备用接口设置生效。

请注意,在 Linux USB 子系统中,与给定备用设置中的端点关联的带宽直到提交需要该带宽的 URB 时才会被保留。某些其他操作系统会在选择配置时尽早分配带宽。

xHCI 会在 usb_hcd_alloc_bandwidth() 中保留带宽并配置备用设置。如果失败,则可能会禁用原始接口的 altsetting。驱动程序不能依赖于失败后任何特定的备用设置生效。

此调用是同步的,不能在中断上下文中使用。此外,在为该接口中的端点计划 urbs 时,驱动程序不得更改 altsettings;必须先完成所有此类 urbs(可能会通过取消链接强制完成)。如果驱动程序中的线程使用此调用,请确保您的 disconnect() 方法可以等待其完成。

返回值

成功时返回零,否则返回底层 usb_control_msg() 调用返回的状态码。

int usb_reset_configuration(struct usb_device *dev)

轻量级设备重置

参数

struct usb_device *dev

正在重置其配置的设备

描述

这将使用当前配置向设备发出标准 SET_CONFIGURATION 请求。效果是重置设备中与 USB 相关的大部分状态,包括接口 altsettings(重置为零)、端点停止(清除)和端点状态(仅适用于批量和中断端点)。其他 usbcore 状态保持不变,包括 USB 设备驱动程序与接口的绑定。

由于这会影响多个接口,因此请避免将其用于复合(多接口)设备。相反,每个接口的驱动程序可以在其声明的接口上使用 usb_set_interface()。但要小心;某些设备不支持 SET_INTERFACE 请求,而其他设备不会重置所有接口状态(尤其是端点状态)。重置整个配置会影响其他驱动程序的接口。

调用者必须拥有设备锁。

如果此例程失败,则设备可能会处于无法使用的状态,端点被禁用,接口仅部分启用。

返回值

成功时返回零,否则返回负错误代码。

int usb_set_wireless_status(struct usb_interface *iface, enum usb_wireless_status status)

设置 wireless_status 结构成员

参数

struct usb_interface *iface

要修改的接口

enum usb_wireless_status status

新的无线状态

描述

将 wireless_status 结构成员设置为新值,并根据需要发出 sysfs 更改。

返回值

成功时返回 0,如果已设置则返回 -EALREADY。

int usb_driver_set_configuration(struct usb_device *udev, int config)

为驱动程序提供更改设备配置的方法

参数

struct usb_device *udev

正在更新其配置的设备

int config

正在选择的配置。

上下文

在进程上下文中,必须能够休眠

描述

不允许设备接口驱动程序更改设备配置。这是因为更改配置会破坏驱动程序绑定的接口并创建新接口;这就像软盘驱动程序告诉计算机用磁带驱动器替换软盘驱动器一样!

尽管如此,在某些特殊情况下,可能会出现这种需要。此例程通过使用工作线程提交更改配置请求来绕过正常限制。

返回值

如果请求已成功排队,则返回 0,否则返回错误代码。调用者无法知道排队的请求最终是否会成功。

int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr, struct usb_interface *intf, u8 *buffer, int buflen)

解析 CDC 设备中存在的额外标头

参数

struct usb_cdc_parsed_header *hdr

放置解析结果的位置

struct usb_interface *intf

请求解析的接口

u8 *buffer

指向要解析的额外标头的指针

int buflen

额外标头的长度

描述

此函数评估 CDC 设备中存在的额外头部信息,这些头部信息绑定数据和控制接口,并提供有关设备功能的详细信息。

返回值

解析的描述符数量,如果头部信息存在无法修复的矛盾,则返回 -EINVAL。

int usb_register_dev(struct usb_interface *intf, struct usb_class_driver *class_driver)

注册一个 USB 设备,并请求一个次设备号。

参数

struct usb_interface *intf

指向正在注册的 usb_interface 的指针。

struct usb_class_driver *class_driver

指向此设备的 usb_class_driver 的指针。

描述

所有使用 USB 主设备号的 USB 驱动程序都应调用此函数。如果启用了 CONFIG_USB_DYNAMIC_MINORS,则将从可用次设备号列表中动态分配次设备号。如果未启用,则次设备号将基于下一个可用的空闲次设备号,从 class_driver->minor_base 开始。

此函数还在 sysfs 树中创建一个 usb 类设备。

当驱动程序不再需要此函数给出的次设备号时,必须调用 usb_deregister_dev()

返回值

如果尝试注册设备时出现问题,则返回 -EINVAL,成功时返回 0。

void usb_deregister_dev(struct usb_interface *intf, struct usb_class_driver *class_driver)

注销 USB 设备的动态次设备号。

参数

struct usb_interface *intf

指向正在注销的 usb_interface 的指针。

struct usb_class_driver *class_driver

指向此设备的 usb_class_driver 的指针。

描述

usb_register_dev() 结合使用。当 USB 驱动程序不再需要通过调用 usb_register_dev() 获取的次设备号时(通常在设备与系统断开连接时),将调用此函数。

此函数还会从 sysfs 树中删除 usb 类设备。

所有使用 USB 主设备号的驱动程序都应调用此函数。

int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void *data)

将驱动程序绑定到接口。

参数

struct usb_driver *driver

要绑定的驱动程序。

struct usb_interface *iface

要绑定到的接口;必须位于 USB 设备的活动配置中。

void *data

与该接口关联的驱动程序数据。

描述

当探测时,需要声明设备上的多个接口的 USB 设备驱动程序使用此函数(音频和 acm 是当前的示例)。任何设备驱动程序都不应直接修改内部 usb_interface 或 usb_device 结构成员。

调用者必须拥有设备锁,因此驱动程序的 probe() 条目不需要额外的锁定,但其他调用上下文可能需要显式声明该锁。

返回值

成功时返回 0。

void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface)

从接口解除绑定驱动程序。

参数

struct usb_driver *driver

要解除绑定的驱动程序。

struct usb_interface *iface

要解除绑定的接口。

描述

驱动程序可以使用此函数来释放接口,而无需等待调用其 disconnect() 方法。在典型情况下,这也将导致调用驱动程序的 disconnect() 方法。

此调用是同步的,不能在中断上下文中使用。调用者必须拥有设备锁,因此驱动程序的 disconnect() 条目不需要额外的锁定,但其他调用上下文可能需要显式声明该锁。

const struct usb_device_id *usb_match_id(struct usb_interface *interface, const struct usb_device_id *id)

查找第一个与设备或接口匹配的 usb_device_id。

参数

struct usb_interface *interface

感兴趣的接口。

const struct usb_device_id *id

usb_device_id 结构数组,以零条目终止。

描述

usb_match_id 搜索 usb_device_id 数组,并返回第一个与设备或接口匹配的项,如果未找到则返回 null。这在将驱动程序绑定(或重新绑定)到接口时使用。大多数 USB 设备驱动程序会通过 USB 内核间接使用此函数,但一些分层驱动程序框架会直接使用它。这些设备表通过 modutils 与 MODULE_DEVICE_TABLE 一起导出,以支持 USB 热插拔的驱动程序加载功能。

匹配的内容

usb_device_id 中的 “match_flags” 元素控制使用哪些成员。如果设置了对应的位,则 device_id 中的值必须与其对应的设备或接口描述符中的成员匹配,否则 device_id 不匹配。

“driver_info” 通常仅由设备驱动程序使用,但如果仅提供具有非零 “driver_info” 字段的 id,则可以创建一个通配符 “匹配任何内容” 的 usb_device_id 作为驱动程序的 “modules.usbmap” 条目。如果执行此操作,则 USB 设备驱动程序的 probe() 例程应使用额外的智能来决定是否绑定到指定的接口。

如何创建良好的 usb_device_id 表

匹配算法非常简单,因此驱动程序选择中的智能必须来自智能驱动程序 id 记录。除非有充分的理由使用其他选择策略,否则只在相关的组中提供匹配元素,并按照从具体到一般的顺序排列匹配说明符。如果可以,请使用为此目的提供的宏。

最具体的匹配说明符使用设备描述符数据。这些通常用于特定产品的匹配;USB_DEVICE 宏允许您提供供应商和产品 ID,还可以匹配产品修订版本范围。这些广泛用于具有应用程序或供应商特定的 bDeviceClass 值的设备。

基于设备类/子类/协议规范的匹配稍微更通用;使用 USB_DEVICE_INFO 宏或其同类宏。这些用于 bDeviceClass 未指定每个接口都有自己类的单功能设备。

基于接口类/子类/协议的匹配是最通用的;它们允许驱动程序绑定到多功能设备上的任何接口。使用 USB_INTERFACE_INFO 宏或其同类宏来匹配每个接口样式的类设备(如 bInterfaceClass 中记录的那样)。

请注意,如果设备类设置为“供应商特定”,则由 USB_INTERFACE_INFO 创建的条目将不匹配任何接口。这是有意为之的;根据 USB 规范,这些设备的接口类/子类/协议的含义也是供应商特定的,因此针对标准产品类进行匹配将不起作用。如果真的想为此类设备使用基于接口的匹配,请创建一个还指定供应商 ID 的匹配记录。(不幸的是,没有用于创建此类记录的标准宏。)

在这些组中,请记住并非所有组合都有意义。例如,不要在没有供应商和产品 ID 的情况下给出产品版本范围;或者在没有其关联的类和子类的情况下指定协议。

返回值

第一个匹配的 usb_device_id,如果未找到则返回 NULL

int usb_register_device_driver(struct usb_device_driver *new_udriver, struct module *owner)

注册一个 USB 设备(而非接口)驱动程序

参数

struct usb_device_driver *new_udriver

设备驱动程序的 USB 操作

struct module *owner

此驱动程序的模块所有者。

描述

向 USB 核心注册一个 USB 设备驱动程序。每当添加新驱动程序时,都会重新扫描未连接设备的列表,从而允许新驱动程序连接到任何已识别的设备。

返回值

失败时返回负错误代码,成功时返回 0。

void usb_deregister_device_driver(struct usb_device_driver *udriver)

注销一个 USB 设备(而非接口)驱动程序

参数

struct usb_device_driver *udriver

要注销的设备驱动程序的 USB 操作

上下文

必须能够休眠

描述

从内部 USB 驱动程序列表中取消链接指定的驱动程序。

int usb_register_driver(struct usb_driver *new_driver, struct module *owner, const char *mod_name)

注册一个 USB 接口驱动程序

参数

struct usb_driver *new_driver

接口驱动程序的 USB 操作

struct module *owner

此驱动程序的模块所有者。

const char *mod_name

模块名称字符串

描述

向 USB 核心注册一个 USB 接口驱动程序。每当添加新驱动程序时,都会重新扫描未连接接口的列表,从而允许新驱动程序连接到任何已识别的接口。

返回值

失败时返回负错误代码,成功时返回 0。

注意

如果希望驱动程序使用 USB 主编号,则必须调用 usb_register_dev() 来启用该功能。此函数不再处理该问题。

void usb_deregister(struct usb_driver *driver)

注销一个 USB 接口驱动程序

参数

struct usb_driver *driver

要注销的接口驱动程序的 USB 操作

上下文

必须能够休眠

描述

从内部 USB 驱动程序列表中取消链接指定的驱动程序。

注意

如果调用了 usb_register_dev(),仍然需要调用 usb_deregister_dev() 来清理驱动程序分配的次编号,此调用 * 将不再为你执行此操作。

void usb_enable_autosuspend(struct usb_device *udev)

允许 USB 设备自动挂起

参数

struct usb_device *udev

可能被自动挂起的 USB 设备

描述

此例程允许 udev 被自动挂起。在 autosuspend_delay 过去且满足所有其他必要条件之前,不会发生自动挂起。

调用者必须持有 udev 的设备锁。

void usb_disable_autosuspend(struct usb_device *udev)

阻止 USB 设备自动挂起

参数

struct usb_device *udev

可能不会被自动挂起的 USB 设备

描述

此例程阻止 udev 被自动挂起,并且如果它已经自动挂起,则会将其唤醒。

调用者必须持有 udev 的设备锁。

void usb_autopm_put_interface(struct usb_interface *intf)

减少 USB 接口的 PM 使用计数器

参数

struct usb_interface *intf

应该减少其计数器的 usb_interface

描述

当接口驱动程序完成使用 intf 并希望允许其自动挂起时,应调用此例程。一个典型的例子是字符设备驱动程序在其设备文件关闭时。

该例程会减少 intf 的使用计数器。当计数器达到 0 时,会尝试对 intf 的设备进行延迟自动挂起请求。尝试可能会失败(请参阅 autosuspend_check())。

此例程只能在进程上下文中运行。

void usb_autopm_put_interface_async(struct usb_interface *intf)

减少 USB 接口的 PM 使用计数器

参数

struct usb_interface *intf

应该减少其计数器的 usb_interface

描述

此例程与 usb_autopm_put_interface() 的作用大致相同:它会减少 intf 的使用计数器,如果计数器 <= 0,则会计划一个延迟的自动挂起请求。不同之处在于它不执行任何同步;调用者应持有私有锁并自行处理所有同步问题。

通常,如果没有任何 URB 挂起,驱动程序会在 URB 的完成处理程序中调用此例程。

此例程可以在原子上下文中运行。

void usb_autopm_put_interface_no_suspend(struct usb_interface *intf)

减少 USB 接口的 PM 使用计数器

参数

struct usb_interface *intf

应该减少其计数器的 usb_interface

描述

此例程会减少 intf 的使用计数器,但不会执行自动挂起。

此例程可以在原子上下文中运行。

int usb_autopm_get_interface(struct usb_interface *intf)

增加 USB 接口的 PM 使用计数器

参数

struct usb_interface *intf

应该增加其计数器的 usb_interface

描述

当接口驱动程序想要使用 intf 并需要保证它不被挂起时,应调用此例程。此外,该例程可防止 intf 随后被自动挂起。(请注意,这不会阻止源自 PM 核心的挂起事件。)这种预防措施将持续到调用 usb_autopm_put_interface() 或取消绑定 intf。一个典型的例子是字符设备驱动程序在其设备文件打开时。

intf 的使用计数器会递增,以防止后续的自动挂起。但是,如果自动恢复失败,则计数器会重新递减。

此例程只能在进程上下文中运行。

返回值

成功时返回 0。

int usb_autopm_get_interface_async(struct usb_interface *intf)

增加 USB 接口的 PM 使用计数器

参数

struct usb_interface *intf

应该增加其计数器的 usb_interface

描述

此例程的功能与 usb_autopm_get_interface() 非常相似:它会递增 intf 的使用计数器,如果设备处于挂起状态,则将自动恢复请求加入队列。区别在于,它不执行任何同步(调用者应持有私有锁并自行处理所有同步问题),并且它不会直接自动恢复设备(它只会将请求加入队列)。成功调用后,设备可能尚未恢复。

此例程可以在原子上下文中运行。

返回值

成功时返回 0。否则返回负的错误代码。

void usb_autopm_get_interface_no_resume(struct usb_interface *intf)

增加 USB 接口的 PM 使用计数器

参数

struct usb_interface *intf

应该增加其计数器的 usb_interface

描述

此例程会递增 intf 的使用计数器,但不会执行自动恢复。

此例程可以在原子上下文中运行。

int usb_find_common_endpoints(struct usb_host_interface *alt, struct usb_endpoint_descriptor **bulk_in, struct usb_endpoint_descriptor **bulk_out, struct usb_endpoint_descriptor **int_in, struct usb_endpoint_descriptor **int_out)
  • 查找通用端点描述符

参数

struct usb_host_interface *alt

要搜索的备用设置

struct usb_endpoint_descriptor **bulk_in

指向描述符指针的指针,或 NULL

struct usb_endpoint_descriptor **bulk_out

指向描述符指针的指针,或 NULL

struct usb_endpoint_descriptor **int_in

指向描述符指针的指针,或 NULL

struct usb_endpoint_descriptor **int_out

指向描述符指针的指针,或 NULL

描述

搜索备用设置的端点描述符,找到第一个批量输入、批量输出、中断输入和中断输出端点,并在提供的指针中返回它们(除非它们为 NULL)。

如果找不到请求的端点,则将相应的指针设置为 NULL。

返回值

如果找到所有请求的描述符,则返回 0,否则返回 -ENXIO。

int usb_find_common_endpoints_reverse(struct usb_host_interface *alt, struct usb_endpoint_descriptor **bulk_in, struct usb_endpoint_descriptor **bulk_out, struct usb_endpoint_descriptor **int_in, struct usb_endpoint_descriptor **int_out)
  • 查找通用端点描述符

参数

struct usb_host_interface *alt

要搜索的备用设置

struct usb_endpoint_descriptor **bulk_in

指向描述符指针的指针,或 NULL

struct usb_endpoint_descriptor **bulk_out

指向描述符指针的指针,或 NULL

struct usb_endpoint_descriptor **int_in

指向描述符指针的指针,或 NULL

struct usb_endpoint_descriptor **int_out

指向描述符指针的指针,或 NULL

描述

搜索备用设置的端点描述符,找到最后一个批量输入、批量输出、中断输入和中断输出端点,并在提供的指针中返回它们(除非它们为 NULL)。

如果找不到请求的端点,则将相应的指针设置为 NULL。

返回值

如果找到所有请求的描述符,则返回 0,否则返回 -ENXIO。

bool usb_check_bulk_endpoints(const struct usb_interface *intf, const u8 *ep_addrs)

检查接口的当前备用设置是否包含一组具有给定地址的批量端点。

参数

const struct usb_interface *intf

要搜索其当前备用设置的接口

const u8 *ep_addrs

要查找的端点地址(编号和方向)的 0 结尾数组

描述

搜索具有指定地址的端点并检查其类型。

返回值

如果找到所有端点且均为批量端点,则返回 true,否则返回 false

bool usb_check_int_endpoints(const struct usb_interface *intf, const u8 *ep_addrs)

检查接口的当前备用设置是否包含一组具有给定地址的中断端点。

参数

const struct usb_interface *intf

要搜索其当前备用设置的接口

const u8 *ep_addrs

要查找的端点地址(编号和方向)的 0 结尾数组

描述

搜索具有指定地址的端点并检查其类型。

返回值

如果找到所有端点且均为中断端点,则返回 true,否则返回 false

struct usb_host_interface *usb_find_alt_setting(struct usb_host_config *config, unsigned int iface_num, unsigned int alt_num)

给定配置,查找给定接口的备用设置。

参数

struct usb_host_config *config

要搜索的配置(不一定是当前配置)。

unsigned int iface_num

要搜索的接口号

unsigned int alt_num

要搜索的备用接口设置编号。

描述

在配置的接口缓存中搜索给定的备用设置。

返回值

如果找到,则返回备用设置。否则返回 NULL

struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev, unsigned ifnum)

获取具有给定接口号的接口对象

参数

const struct usb_device *dev

考虑其当前配置的设备

unsigned ifnum

所需的接口

描述

此函数会遍历当前活动的配置的设备描述符,以查找具有特定接口号的接口对象。

请注意,配置描述符不需要按顺序分配接口号,因此假设该描述符中的第一个接口对应于接口零是不正确的。此例程有助于设备驱动程序避免此类错误。但是,您应该确保对该接口的任何可用备用设置执行正确的操作。

除非您绑定到此设备上的某个接口或已锁定该设备,否则请勿调用此函数!

返回值

如果找到接口号为 ifnum 的接口,则返回指向该接口的指针。否则返回 NULL

struct usb_host_interface *usb_altnum_to_altsetting(const struct usb_interface *intf, unsigned int altnum)

获取具有给定备用设置号的备用设置结构。

参数

const struct usb_interface *intf

包含所查询的备用设置的接口

unsigned int altnum

所需的备用设置号

描述

此函数在指定接口的备用设置数组中搜索具有正确 bAlternateSetting 值的条目。

请注意,备用设置不一定按数字顺序存储,因此假设数组中的第一个备用设置条目对应于备用设置零是不正确的。此例程有助于设备驱动程序避免此类错误。

除非您绑定到 intf 接口或已锁定设备,否则请勿调用此函数!

返回值

指向 intf 的备用设置数组中,备用设置号为 altnum 的条目的指针。如果未找到,则为 NULL

struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)

查找驱动程序和设备的 usb_interface 指针

参数

struct usb_driver *drv

考虑其当前配置的驱动程序

int minor

所需设备的次要设备号

描述

此函数遍历总线设备列表,并返回指向具有匹配的次要设备号和驱动程序的接口的指针。请注意,这仅适用于共享 USB 主要设备号的设备。

返回值

指向具有匹配的主要设备号和 minor 的接口的指针。

int usb_for_each_dev(void *data, int (*fn)(struct usb_device*, void*))

遍历系统中的所有 USB 设备

参数

void *data

将传递给回调函数的数据指针

int (*fn)(struct usb_device *, void *)

为每个 USB 设备调用的回调函数

描述

遍历所有 USB 设备,并为每个设备调用 fn,并传递 data。如果它返回除 0 之外的任何值,我们将提前中断迭代并返回该值。

struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)

usb 设备构造函数 (usbcore-内部)

参数

struct usb_device *parent

设备连接到的集线器;对于分配根集线器,则为 null

struct usb_bus *bus

用于访问设备的总线

unsigned port1

基于 1 的端口索引;对于根集线器,则忽略

上下文

任务上下文,可能会睡眠。

描述

只有集线器驱动程序(包括主机控制器的虚拟根集线器驱动程序)才应调用此函数。

此调用不得在非睡眠上下文中使用。

返回值

成功时,返回指向分配的 usb 设备的指针。失败时返回 NULL

struct usb_device *usb_get_dev(struct usb_device *dev)

增加 usb 设备结构的引用计数

参数

struct usb_device *dev

正在引用的设备

描述

应为对设备的每个活动引用进行引用计数。

USB 接口的驱动程序通常应在其 probe() 方法(当它们绑定到接口时)中记录此类引用,并通过在其 disconnect() 方法中调用 usb_put_dev() 来释放这些引用。但是,如果驱动程序在其 disconnect() 方法返回后不访问 usb_device 结构,则无需进行引用计数,因为 USB 核心保证在所有接口驱动程序都已解除绑定之后,才取消分配 usb_device。

返回值

指向引用计数已增加的设备的指针。

void usb_put_dev(struct usb_device *dev)

释放对 usb 设备结构的使用

参数

struct usb_device *dev

已断开连接的设备

描述

当设备的用户完成使用后,必须调用此函数。当设备的最后一个用户调用此函数时,设备的内存将被释放。

struct usb_interface *usb_get_intf(struct usb_interface *intf)

增加 usb 接口结构的引用计数

参数

struct usb_interface *intf

正在引用的接口

描述

必须为对接口的每个活动引用进行引用计数。

USB 接口的驱动程序通常应在其 probe() 方法(当它们绑定到接口时)中记录此类引用,并通过在其 disconnect() 方法中调用 usb_put_intf() 来释放这些引用。但是,如果驱动程序在其 disconnect() 方法返回后不访问 usb_interface 结构,则无需进行引用计数,因为 USB 核心保证在驱动程序解除绑定之后,才取消分配 usb_interface。

返回值

指向引用计数已增加的接口的指针。

void usb_put_intf(struct usb_interface *intf)

释放对 usb 接口结构的使用

参数

struct usb_interface *intf

引用计数已减少的接口

描述

当接口的用户完成使用后,必须调用此函数。当接口的最后一个用户调用此函数时,接口的内存将被释放。

struct device *usb_intf_get_dma_device(struct usb_interface *intf)

获取 USB 接口的 DMA 端点的引用。

参数

struct usb_interface *intf

USB 接口。

描述

虽然 USB 设备本身不能执行 DMA 操作,但许多 USB 控制器可以。调用 usb_intf_get_dma_device() 会返回给定 USB 接口的 DMA 端点(如果有)。返回的设备结构必须使用 put_device() 释放。

另请参见 usb_get_dma_device()。

返回值

USB 接口的 DMA 端点的引用;如果不存在,则为 NULL。

存在。

int usb_lock_device_for_reset(struct usb_device *udev, const struct usb_interface *iface)

谨慎地获取 USB 设备结构的锁。

参数

struct usb_device *udev

正在锁定的设备。

const struct usb_interface *iface

绑定到发出请求的驱动程序的接口(可选)。

描述

尝试获取设备锁,但如果设备处于 NOTATTACHED 或 SUSPENDED 状态,或者如果指定了 iface 且该接口既不是 BINDING 也不是 BOUND,则会失败。该例程会重复轮询,而不是休眠等待锁。这是为了防止与断开连接死锁;在某些驱动程序(例如 usb-storage)中,disconnect() 或 suspend() 方法会阻塞,等待设备复位完成。

返回值

失败时返回负的错误代码,否则返回 0。

int usb_get_current_frame_number(struct usb_device *dev)

返回当前总线帧号。

参数

struct usb_device *dev

正在查询其总线的设备。

返回值

给定 USB 设备所使用的 USB 主机控制器的当前帧号。这可以在调度同步请求时使用。

注意

不同类型的主机控制器具有不同的“调度范围”。虽然一种类型可能只支持调度未来 32 帧,但其他类型可能支持调度未来多达 1024 帧。

void *usb_alloc_coherent(struct usb_device *dev, size_t size, gfp_t mem_flags, dma_addr_t *dma)

为 URB_NO_xxx_DMA_MAP 分配 DMA 一致性缓冲区。

参数

struct usb_device *dev

将使用缓冲区的设备。

size_t size

请求的缓冲区大小。

gfp_t mem_flags

影响分配是否可能阻塞。

dma_addr_t *dma

用于返回缓冲区的 DMA 地址。

返回值

可以是 null(表示无法分配缓冲区),或者是指向缓冲区的 CPU 空间指针,该缓冲区可用于执行到指定设备的 DMA。此类 CPU 空间缓冲区会与 DMA 地址(通过提供的指针)一起返回。

注意

这些缓冲区与 urb->transfer_flags 中设置的 URB_NO_xxx_DMA_MAP 一起使用,以避免诸如使用“DMA 反弹缓冲区”或在 URB 完成/重新提交期间扰乱 IOMMU 硬件等行为。实现方式因平台而异,具体取决于 DMA 将如何与此设备一起工作。使用这些缓冲区还可以消除 CPU 缓存不是 DMA 一致性的体系结构上的缓存行共享问题。在没有总线监听缓存的系统上,这些缓冲区是未缓存的。

描述

不再使用缓冲区时,请使用 usb_free_coherent() 释放它。

void usb_free_coherent(struct usb_device *dev, size_t size, void *addr, dma_addr_t dma)

释放使用 usb_alloc_coherent() 分配的内存。

参数

struct usb_device *dev

使用缓冲区的设备。

size_t size

请求的缓冲区大小。

void *addr

缓冲区的 CPU 地址。

dma_addr_t dma

缓冲区的 DMA 地址。

描述

这会回收 I/O 缓冲区,使其可以重复使用。内存必须已使用 usb_alloc_coherent() 分配,并且参数必须与该分配请求中提供的参数匹配。

int usb_hub_clear_tt_buffer(struct urb *urb)

清除高速集线器中的控制/批量 TT 状态。

参数

struct urb *urb

与失败或不完整的拆分事务关联的 URB。

描述

高速 HCD 使用此方法来告知集线器驱动程序,某些拆分控制或批量事务失败,需要清除事务转换器的内部状态。这通常是从中断上下文中检测(和报告)的。

在完全清除该状态之前,集线器可能无法处理其他全速(或低速)事务。

返回值

如果成功,则为 0。否则,为负的错误代码。

void usb_set_device_state(struct usb_device *udev, enum usb_device_state new_state)

更改设备的当前状态(usbcore、hcds)。

参数

struct usb_device *udev

指向应更改其状态的设备的指针。

enum usb_device_state new_state

要存储的新状态值。

描述

udev->state _并非_完全受设备锁保护。虽然大多数转换仅在持有锁时进行,但状态几乎可以在任何时候更改为 USB_STATE_NOTATTACHED。这是为了使设备可以尽快标记为断开连接,而无需等待释放任何信号量。因此,对任何设备状态的所有更改都必须受到 device_state_lock 自旋锁的保护。

将设备添加到设备树后,应使用此例程对设备状态进行所有更改。状态_不应_直接设置。

如果 udev->state 已经为 USB_STATE_NOTATTACHED,则不会进行任何更改。否则,udev->state 将设置为 new_state,并且如果 new_state 为 USB_STATE_NOTATTACHED,则 udev 的所有后代的状态也将设置为 USB_STATE_NOTATTACHED。

void usb_root_hub_lost_power(struct usb_device *rhdev)

如果根集线器丢失 Vbus 电源,则由 HCD 调用。

参数

struct usb_device *rhdev

根集线器的 struct usb_device

描述

当根集线器恢复并且 Vbus 电源中断或控制器已复位时,USB 主机控制器驱动程序会调用此函数。该例程会将 rhdev 标记为已丢失电源。当集线器驱动程序恢复时,它将注意到并对所有启用了“USB-PERSIST”的子设备执行电源会话恢复;其他设备将断开连接。

int usb_reset_device(struct usb_device *udev)

警告接口驱动程序并执行 USB 端口复位。

参数

struct usb_device *udev

要复位的设备(未处于 NOTATTACHED 状态)。

描述

警告绑定到已注册接口的所有驱动程序(使用其 pre_reset 方法),执行端口复位,然后通知驱动程序复位已结束(使用其 post_reset 方法)。

如果当前正在探测或断开连接接口,我们假设其驱动程序知道如何处理复位。对于所有其他接口,如果驱动程序没有 pre_reset 和 post_reset 方法,则我们尝试解除绑定并在此之后重新绑定。

返回值

与 usb_reset_and_verify_device() 相同。但是,如果复位已经在进行中(例如,如果驱动程序没有 pre_reset() 或 post_reset() 回调,并且在正在进行的复位期间正在解除绑定或重新绑定时,其 disconnect() 或 probe() 例程尝试执行第二个嵌套复位),则该例程将返回 -EINPROGRESS。

注意

调用者必须拥有设备锁。例如,在下载新的固件后,从驱动程序的 probe() 例程中使用它是安全的。对于可能不会在 probe() 期间发生的调用,驱动程序应使用 usb_lock_device_for_reset() 锁定设备。

void usb_queue_reset_device(struct usb_interface *iface)

从原子上下文中重置 USB 设备

参数

struct usb_interface *iface

属于要重置的设备的 USB 接口

描述

此函数可用于从原子上下文中重置 USB 设备,而 usb_reset_device() 将不起作用(因为它会阻塞)。

通过此方法执行重置在功能上等同于调用 usb_reset_device(),只是它被延迟到一个工作队列中。这意味着绑定到其他接口的任何驱动程序都可能会被解除绑定,以及来自用户空间的 usbfs 用户。

特殊情况

  • 有可能从附加到同一设备的不同接口的两个不同驱动程序同时调度两个重置;具体取决于附加到每个接口的驱动程序如何处理 ->pre_reset(),第二次重置可能会发生也可能不会发生。

  • 如果重置延迟的时间太长,以至于接口与其驱动程序解除绑定,则重置将被跳过。

  • 可以在 .probe() 期间调用此函数。也可以在 .disconnect() 期间调用它,但这样做毫无意义,因为不会发生重置。如果真的想在 .disconnect() 期间重置设备,请直接调用 usb_reset_device() -- 但要注意嵌套的解除绑定问题!

struct usb_device *usb_hub_find_child(struct usb_device *hdev, int port1)

获取附加到由 port1 指定的端口的子设备的指针。

参数

struct usb_device *hdev

属于 USB 集线器的 USB 设备

int port1

端口号,指示子设备连接到哪个端口。

描述

USB 驱动程序调用此函数以获取集线器的子设备指针。

返回值

如果输入参数无效,则为 NULL;如果非 NULL,则为子设备的 usb_device 指针。

主机控制器 API

这些 API 仅供主机控制器驱动程序使用,其中大多数实现标准的寄存器接口,例如 XHCI、EHCI、OHCI 或 UHCI。UHCI 是最早的接口之一,由英特尔设计,也被 VIA 使用;它在硬件方面没有做太多事情。OHCI 是后来设计的,目的是让硬件做更多的工作(更大的传输,跟踪协议状态等等)。EHCI 是为 USB 2.0 设计的;它的设计具有类似于 OHCI 的功能(硬件做更多的工作)以及 UHCI 的功能(ISO 支持的某些部分、TD 列表处理)。XHCI 是为 USB 3.0 设计的。它继续将对功能的支持转移到硬件中。

除了“三大”主机控制器之外,还有其他主机控制器,尽管大多数基于 PCI 的控制器(以及一些非基于 PCI 的控制器)都使用其中一个接口。并非所有主机控制器都使用 DMA;有些使用 PIO,还有一个模拟器和一个虚拟主机控制器,可以通过网络传输 USB。

所有这些控制器的驱动程序都可以使用相同的基本 API。由于历史原因,它们分为两层:struct usb_bus 是一个相当薄的层,在 2.2 内核中可用,而 struct usb_hcd 是一个功能更强大的层,允许 HCD 共享通用代码,以缩小驱动程序大小并显着减少 hcd 特定行为。

long usb_calc_bus_time(int speed, int is_input, int isoc, int bytecount)

以纳秒为单位的近似周期事务时间

参数

int speed

来自 dev->speed;USB_SPEED_{LOW,FULL,HIGH}

int is_input

如果事务将数据发送到主机,则为 true

int isoc

对于同步事务为 true,对于中断事务为 false

int bytecount

事务中的字节数。

返回值

周期性事务的近似总线时间(以纳秒为单位)。

注意

请参阅 USB 2.0 规范第 5.11.3 节;只有周期性传输才需要在软件中进行调度,此函数仅用于此类调度。

将 URB 添加到其端点队列

参数

struct usb_hcd *hcd

提交 urb 的主机控制器

struct urb *urb

正在提交的 URB

描述

主机控制器驱动程序应在其 enqueue() 方法中调用此例程。必须持有 HCD 的私有自旋锁,并且必须禁用中断。此处执行的操作是 URB 提交、端点关闭以及 usb_kill_urb 所必需的。

返回值

如果无错误,则返回 0,否则返回负错误代码(在这种情况下,enqueue() 方法必须失败)。如果未发生错误,但 enqueue() 仍然失败,则它必须在释放私有自旋锁并返回之前调用 usb_hcd_unlink_urb_from_ep()

检查是否可以取消链接 URB

参数

struct usb_hcd *hcd

提交 urb 的主机控制器

struct urb *urb

正在检查是否可取消链接的 URB

int status

如果取消链接成功,则存储在 urb 中的错误代码

描述

主机控制器驱动程序应在其 dequeue() 方法中调用此例程。必须持有 HCD 的私有自旋锁,并且必须禁用中断。此处执行的操作是为确保取消链接有效所必需的。

返回值

如果无错误,则返回 0,否则返回负错误代码(在这种情况下,dequeue() 方法必须失败)。可能的错误代码是

-EIDRM:urb 未提交或已完成。

可能尚未调用完成函数。

-EBUSY:urb 已取消链接。

从其端点队列中删除 URB

参数

struct usb_hcd *hcd

提交 urb 的主机控制器

struct urb *urb

正在取消链接的 URB

描述

主机控制器驱动程序应在调用 usb_hcd_giveback_urb() 之前调用此例程。必须持有 HCD 的私有自旋锁,并且必须禁用中断。此处执行的操作是 URB 完成所必需的。

void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)

将 URB 从 HCD 返回给设备驱动程序

参数

struct usb_hcd *hcd

主机控制器返回 URB。

struct urb *urb

URB 被返回给 USB 设备驱动程序。

int status

URB 的完成状态码。

上下文

原子操作。完成回调函数在调用者的上下文中被调用。对于设置了 HCD_BH 标志的 HCD,完成回调函数在 BH 上下文中被调用(提交给根集线器的 URB 除外,它们始终在调用者的上下文中完成)。

描述

此函数使用其完成函数将 URB 从 HCD 传递给其 USB 设备驱动程序。HCD 已释放所有每个 URB 的资源(并且已完成使用 urb->hcpriv)。它还释放了所有 HCD 锁;如果设备驱动程序释放、修改或重新提交此 URB,则不会导致问题。

如果 urb 被取消链接,则 status 的值将被 urb->unlinked 覆盖。如果 HCD 没有检查到,则会检测到错误的短传输。

int usb_alloc_streams(struct usb_interface *interface, struct usb_host_endpoint **eps, unsigned int num_eps, unsigned int num_streams, gfp_t mem_flags)

分配批量端点流 ID。

参数

struct usb_interface *interface

包含所有端点的备用设置。

struct usb_host_endpoint **eps

需要流的端点数组。

unsigned int num_eps

数组中的端点数量。

unsigned int num_streams

要分配的流的数量。

gfp_t mem_flags

HCD 应用于分配内存的标志。

描述

设置一组批量端点以使其具有 num_streams 个可用的流 ID。驱动程序可以将多个传输排队到不同的流 ID,这些传输可能以与排队顺序不同的顺序完成。

返回值

成功时,返回分配的流的数量。失败时,返回负错误代码。

int usb_free_streams(struct usb_interface *interface, struct usb_host_endpoint **eps, unsigned int num_eps, gfp_t mem_flags)

释放批量端点流 ID。

参数

struct usb_interface *interface

包含所有端点的备用设置。

struct usb_host_endpoint **eps

要从中删除流的端点数组。

unsigned int num_eps

数组中的端点数量。

gfp_t mem_flags

HCD 应用于分配内存的标志。

描述

将一组批量端点恢复为不使用流 ID。如果提供的参数错误,或者 HCD 损坏,则可能会失败。

返回值

成功时返回 0。失败时,返回负错误代码。

void usb_hcd_resume_root_hub(struct usb_hcd *hcd)

由 HCD 调用以恢复其根集线器

参数

struct usb_hcd *hcd

此根集线器的主机控制器

描述

当 USB 主机控制器的根集线器被挂起(启用远程唤醒功能)并且收到远程唤醒请求时,USB 主机控制器会调用此函数。该例程会提交一个工作队列请求,以恢复根集线器(即,再次管理其下游端口)。

int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num)

启动立即枚举(用于 OTG)

参数

struct usb_bus *bus

总线(必须使用 hcd 框架)

unsigned port_num

基于 1 的端口号;通常为 bus->otg_port

上下文

原子操作

描述

启动枚举,先立即重置,然后由 hub_wq 识别并可能配置设备。OTG 控制器驱动程序需要此功能,它可以帮助满足 HNP 协议对启动端口重置的时序要求。

返回值

如果成功,则返回 0。

irqreturn_t usb_hcd_irq(int irq, void *__hcd)

将 IRQ 挂钩到 HCD 框架(总线胶水)

参数

int irq

正在引发的 IRQ

void *__hcd

指向发出 IRQ 信号的 HCD 的指针

描述

如果控制器未暂停,则调用驱动程序的 irq 处理程序。检查控制器是否已失效。

返回值

如果 IRQ 已处理,则返回 IRQ_HANDLED。否则返回 IRQ_NONE

void usb_hc_died(struct usb_hcd *hcd)

报告主机控制器异常关闭(总线胶水)

参数

struct usb_hcd *hcd

指向表示控制器的 HCD 的指针

描述

当 USB 主机控制器在操作可能仍在挂起时失效时,总线胶水会调用此函数以进行报告。PCI 胶水会自动调用此函数,因此只有非 PCI 总线的胶水才需要调用它。

仅使用主 HCD 调用此函数。

struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name, struct usb_hcd *primary_hcd)

创建并初始化 HCD 结构

参数

const struct hc_driver *driver

将使用此 hcd 的 HC 驱动程序

struct device *dev

此 HC 的设备,存储在 hcd->self.controller 中

const char *bus_name

要存储在 hcd->self.bus_name 中的值

struct usb_hcd *primary_hcd

指向共享 PCI 设备的 usb_hcd 结构的指针。仅为主 HCD 分配某些资源

上下文

任务上下文,可能会睡眠。

描述

分配一个 struct usb_hcd,并在末尾留出额外的空间用于 HC 驱动程序的私有数据。初始化 hcd 结构的通用成员。

返回值

成功时,返回指向已创建并初始化的 HCD 结构的指针。失败时(例如,如果内存不可用),返回 NULL

struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name)

创建并初始化 HCD 结构

参数

const struct hc_driver *driver

将使用此 hcd 的 HC 驱动程序

struct device *dev

此 HC 的设备,存储在 hcd->self.controller 中

const char *bus_name

要存储在 hcd->self.bus_name 中的值

上下文

任务上下文,可能会睡眠。

描述

分配一个 struct usb_hcd,并在末尾留出额外的空间用于 HC 驱动程序的私有数据。初始化 hcd 结构的通用成员。

返回值

成功时,返回指向已创建并初始化的 HCD 结构的指针。失败时(例如,如果内存不可用),返回 NULL

int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags)

完成通用 HCD 结构初始化并注册

参数

struct usb_hcd *hcd

要初始化的 usb_hcd 结构体

unsigned int irqnum

要分配的中断线

unsigned long irqflags

中断类型标志

描述

完成通用 HCD 初始化剩余部分:分配一致性内存缓冲区,注册总线,请求 IRQ 线,并调用驱动程序的 reset() 和 start() 例程。

void usb_remove_hcd(struct usb_hcd *hcd)

通用 HCD 的关闭处理

参数

struct usb_hcd *hcd

要移除的 usb_hcd 结构体

上下文

任务上下文,可能会睡眠。

描述

断开根集线器,然后撤销 usb_add_hcd() 的影响,调用 HCD 的 stop() 方法。

int usb_hcd_pci_probe(struct pci_dev *dev, const struct hc_driver *driver)

初始化基于 PCI 的 HCD

参数

struct pci_dev *dev

正在探测的 USB 主机控制器

const struct hc_driver *driver

USB HC 驱动程序句柄

上下文

任务上下文,可能会休眠

描述

为此 USB 主机控制器分配基本的 PCI 资源,然后通过热插拔条目的 driver_data 调用与其关联的 HCD 的 start() 方法。

将此函数存储在 HCD 的 struct pci_driver 中作为 probe()。

返回值

如果成功,则返回 0。

void usb_hcd_pci_remove(struct pci_dev *dev)

基于 PCI 的 HCD 的关闭处理

参数

struct pci_dev *dev

正在移除的 USB 主机控制器

上下文

任务上下文,可能会休眠

描述

撤销 usb_hcd_pci_probe() 的影响,首先调用 HCD 的 stop() 方法。它总是从线程上下文调用,通常是 “rmmod”、“apmd” 或类似的线程。

将此函数存储在 HCD 的 struct pci_driver 中作为 remove()。

void usb_hcd_pci_shutdown(struct pci_dev *dev)

关闭主机控制器

参数

struct pci_dev *dev

正在关闭的 USB 主机控制器

int hcd_buffer_create(struct usb_hcd *hcd)

初始化缓冲区池

参数

struct usb_hcd *hcd

要初始化其缓冲区池的总线

上下文

任务上下文,可能会休眠

描述

在初始化使用 dma 内存分配器的主机控制器时调用此函数。它初始化一些 dma 一致性内存池,这些内存池将由使用该控制器的所有驱动程序共享。

使用这些池后,调用 hcd_buffer_destroy() 进行清理。

返回值

成功返回 0。否则返回负的 errno 值。

void hcd_buffer_destroy(struct usb_hcd *hcd)

释放缓冲区池

参数

struct usb_hcd *hcd

要销毁其缓冲区池的总线

上下文

任务上下文,可能会休眠

描述

这将释放由 hcd_buffer_create() 创建的缓冲区池。

USB 字符设备节点

本章介绍 Linux 字符设备节点。您可能希望避免为您的 USB 驱动程序编写新的内核代码。用户模式设备驱动程序通常打包为应用程序或库,并且可能通过一些封装它的编程库使用字符设备。此类库包括

  • libusb,用于 C/C++,以及

  • jUSB,用于 Java。

有关它的旧信息可以在 USB 指南的 “USB 设备文件系统” 部分中看到。USB 指南的最新副本可以在 http://www.linux-usb.org/ 找到

注意

  • 它们曾经通过 *usbfs* 实现,但这不属于 sysfs 调试接口。

  • 此特定文档不完整,尤其是在异步模式方面。截至内核 2.5.66,代码和此(新)文档需要交叉审查。

“devtmpfs” 中有哪些文件?

通常挂载在 /dev/bus/usb/,usbfs 的功能包括

  • /dev/bus/usb/BBB/DDD ... 公开每个设备的配置描述符的魔术文件,并支持一系列用于发出设备请求(包括设备 I/O)的 ioctl。(纯粹供程序访问。)

每个总线都会根据其枚举时间获得一个编号 (BBB);在每个总线内,每个设备都会获得一个类似的编号 (DDD)。这些 BBB/DDD 路径不是 “稳定” 的标识符;即使您始终将设备插入到同一个集线器端口,也可能会发生变化。*甚至不要考虑将它们保存在应用程序配置文件中。* 稳定标识符可用于想要使用它们的用户模式应用程序。HID 和网络设备公开这些稳定 ID,例如,您可以确保您告诉正确的 UPS 关闭其第二台服务器。请注意,它(尚未)公开这些 ID。

/dev/bus/usb/BBB/DDD

以下列基本方式之一使用这些文件

  • *它们可以被读取,* 首先生成设备描述符(18 字节),然后生成当前配置的描述符。有关这些二进制数据格式的详细信息,请参阅 USB 2.0 规范。您需要将大多数多字节值从小端格式转换为您的本机主机字节顺序,尽管设备描述符中的一些字段(两个 BCD 编码的字段以及供应商和产品 ID)将为您进行字节交换。请注意,配置描述符包括接口、altsetting、端点以及可能附加的类描述符的描述符。

  • *使用 *ioctl()* 请求执行 USB 操作,以发出端点 I/O 请求(同步或异步)或管理设备。这些请求需要 CAP_SYS_RAWIO 功能,以及文件系统访问权限。一次只能在其中一个设备文件上发出一个 ioctl 请求。这意味着如果您从一个线程同步读取一个端点,则在读取完成之前,您将无法从另一个线程写入不同的端点。这适用于*半双工*协议,否则您将使用异步 i/o 请求。

每个连接的 USB 设备都有一个文件。BBB 表示总线编号。DDD 表示该总线上的设备地址。这两个数字都是按顺序分配的,并且可以重复使用,因此您不能依赖它们来稳定访问设备。例如,设备在仍然连接时重新枚举是相对常见的(也许有人碰了它们的电源、集线器或 USB 电缆),因此当您第一次连接设备时,它可能是 002/027,而稍后可能是 002/048

这些文件可以作为二进制数据读取。二进制数据首先由设备描述符组成,然后是设备的每个配置的描述符。设备描述符中的多字节字段由内核转换为主机字节序。配置描述符采用总线字节序格式!配置描述符彼此间隔 wTotalLength 字节。如果设备返回的配置描述符数据少于 wTotalLength 指示的数据,则该文件中将出现缺少字节的空洞。此信息也以文本形式显示在稍后描述的 /sys/kernel/debug/usb/devices 文件中。

这些文件也可以用于为 USB 设备编写用户级驱动程序。您将以读/写方式打开 /dev/bus/usb/BBB/DDD 文件,读取其描述符以确保它是您期望的设备,然后使用 ioctl 调用绑定到接口(或多个接口)。您将向设备发出更多 ioctl,以使用控制、批量或其他类型的 USB 传输与其通信。IOCTL 列在 <linux/usbdevice_fs.h> 文件中,并且在编写本文时,源代码 (linux/drivers/usb/core/devio.c) 是通过这些文件访问设备的主要参考。

请注意,默认情况下,这些 BBB/DDD 文件只有 root 用户才能写入,因此只有 root 用户才能编写此类用户模式驱动程序。您可以使用 chmod 有选择地向其他用户授予读/写权限。此外,诸如 devmode=0666 之类的 usbfs 挂载选项可能会有所帮助。

用户模式驱动程序的生命周期

此类驱动程序首先需要找到它知道如何处理的设备的设备文件。也许是因为 /sbin/hotplug 事件处理代理选择了该驱动程序来处理新设备,从而告知了它。或者,它可能是一个扫描所有 /dev/bus/usb 设备文件并忽略大多数设备的应用程序。在任何一种情况下,它都应该 read() 从设备文件中读取所有描述符,并根据它知道如何处理的内容进行检查。它可能只拒绝除特定供应商和产品 ID 之外的所有内容,或者需要更复杂的策略。

永远不要假设系统上一次只有一个此类设备!如果您的代码一次无法处理多个设备,至少要检测到何时存在多个设备,并让您的用户选择要使用哪个设备。

一旦您的用户模式驱动程序知道要使用哪个设备,它将以两种样式之一与设备交互。简单样式是仅发出控制请求;某些设备不需要比这些更复杂的交互。(一个示例可能是软件使用供应商特定的控制请求来进行某些初始化或配置任务,而内核驱动程序用于其余部分。)

更可能的是,您需要一个更复杂的样式驱动程序:一个使用非控制端点、读取或写入数据并声明对接口的独占使用权的驱动程序。批量传输最容易使用,但只有它们的同级中断传输才能与低速设备一起使用。中断传输和同步传输都提供服务保证,因为它们的带宽已预留。除非您使用的是异步调用,否则通过 usbfs 使用此类“定期”传输会很麻烦。但是,中断传输也可以以同步的“一次性”样式使用。

当设备断开连接时,您的用户模式驱动程序永远不需要担心清理请求状态,尽管一旦开始看到 ENODEV 错误,它就应该关闭其打开的文件描述符。

ioctl() 请求

要使用这些 ioctl,您需要在您的用户空间程序中包含以下头文件

#include <linux/usb.h>
#include <linux/usbdevice_fs.h>
#include <asm/byteorder.h>

来自 USB 2.0 规范“第 9 章”的标准 USB 设备模型请求会自动从 <linux/usb/ch9.h> 头文件中包含。

除非另有说明,否则此处描述的 ioctl 请求将更新应用它们的 usbfs 文件的修改时间(除非它们失败)。返回零表示成功;否则,将返回标准 USB 错误代码(这些代码记录在USB 错误代码中)。

这些文件中的每一个都会复用对多个 I/O 流的访问,每个端点一个。每个设备都有一个控制端点(端点零),该端点支持有限的 RPC 样式 RPC 访问。设备由 hub_wq(在内核中)配置,设置一个影响功耗和基本功能等方面的设备范围的配置。端点是 USB 接口的一部分,这些接口可能具有影响哪些端点可用的备用设置。许多设备只有一个配置和接口,因此它们的驱动程序将忽略配置和备用设置。

管理/状态请求

许多 usbfs 请求并非直接处理设备 I/O。它们主要与设备管理和状态有关。这些都是同步请求。

USBDEVFS_CLAIMINTERFACE

用于强制 usbfs 声明一个特定接口,该接口之前未被 usbfs 或任何其他内核驱动程序声明。ioctl 参数是一个整数,其中包含接口的编号(来自描述符的 bInterfaceNumber)。

请注意,如果您的驱动程序在尝试使用接口的端点之前未声明接口,并且没有其他驱动程序绑定到该接口,则该接口会自动被 usbfs 声明。

此声明将通过 RELEASEINTERFACE ioctl 或关闭文件描述符来释放。此请求不会更新文件修改时间。

USBDEVFS_CONNECTINFO

指示设备是否为低速。ioctl 参数指向一个类似于这样的结构

struct usbdevfs_connectinfo {
        unsigned int   devnum;
        unsigned char  slow;
};

此请求不会更新文件修改时间。

您无法判断“非慢速”设备是以高速(480 MBit/秒)连接还是仅以全速(12 MBit/秒)连接。您应该已经知道 devnum 值,它是设备文件名的 DDD 值。

USBDEVFS_GET_SPEED

返回设备的速度。速度以数字值形式返回,符合枚举 usb_device_speed

此请求不会更新文件修改时间。

USBDEVFS_GETDRIVER

返回绑定到给定接口的内核驱动程序的名称(字符串)。参数是指向此结构的指针,该结构已修改

struct usbdevfs_getdriver {
        unsigned int  interface;
        char          driver[USBDEVFS_MAXDRIVERNAME + 1];
};

此请求不会更新文件修改时间。

USBDEVFS_IOCTL

将来自用户空间的请求传递给在它注册的struct usb_driver中具有 ioctl 条目的内核驱动程序

struct usbdevfs_ioctl {
        int     ifno;
        int     ioctl_code;
        void    *data;
};

/* user mode call looks like this.
 * 'request' becomes the driver->ioctl() 'code' parameter.
 * the size of 'param' is encoded in 'request', and that data
 * is copied to or from the driver->ioctl() 'buf' parameter.
 */
static int
usbdev_ioctl (int fd, int ifno, unsigned request, void *param)
{
        struct usbdevfs_ioctl   wrapper;

        wrapper.ifno = ifno;
        wrapper.ioctl_code = request;
        wrapper.data = param;

        return ioctl (fd, USBDEVFS_IOCTL, &wrapper);
}

此请求不会更新文件修改时间。

此请求允许内核驱动程序通过文件系统操作与用户模式代码进行通信,即使它们没有创建字符或块特殊设备。它也被用来执行诸如询问设备应该使用哪个设备特殊文件之类的操作。两个预定义的 ioctl 用于断开和重新连接内核驱动程序,以便用户模式代码可以完全管理设备的绑定和配置。

USBDEVFS_RELEASEINTERFACE

用于释放 usbfs 在接口上进行的声明(隐式声明或由于 USBDEVFS_CLAIMINTERFACE 调用),在关闭文件描述符之前。ioctl 参数是一个整数,其中包含接口的编号(来自描述符的 bInterfaceNumber);此请求不会更新文件修改时间。

警告

不进行安全检查以确保进行声明的任务是释放声明的任务。这意味着用户模式驱动程序可能会干扰其他驱动程序。

USBDEVFS_RESETEP

将端点(批量或中断)的数据切换值重置为 DATA0。ioctl 参数是一个整数端点编号(1 到 15,如端点描述符中标识),如果设备的端点将数据发送到主机,则添加 USB_DIR_IN。

警告

避免使用此请求。应该将其删除。 使用它通常意味着设备和驱动程序将失去切换同步。如果您真的失去了同步,您可能需要使用 CLEAR_HALT 或 SET_INTERFACE 之类的请求与设备完全握手。

USBDEVFS_DROP_PRIVILEGES

用于放弃对 usbfs 文件描述符执行某些被视为特权操作的能力。这包括声明任意接口、重置当前存在来自其他用户的声明接口的设备以及发出 USBDEVFS_IOCTL 调用。ioctl 参数是一个 32 位掩码,指示允许用户在此文件描述符上声明的接口。您可以多次发出此 ioctl 以缩小所述掩码。

同步 I/O 支持

同步请求涉及内核阻塞,直到用户模式请求完成(通过成功完成或报告错误)。在大多数情况下,这是使用 usbfs 最简单的方法,尽管如上所述,它确实会阻止一次对多个端点执行 I/O。

USBDEVFS_BULK

向设备发出批量读取或写入请求。ioctl 参数是指向此结构的指针

struct usbdevfs_bulktransfer {
        unsigned int  ep;
        unsigned int  len;
        unsigned int  timeout; /* in milliseconds */
        void          *data;
};

ep 值标识批量端点号(1 到 15,如端点描述符中标识),当引用将数据从设备发送到主机的端点时,用 USB_DIR_IN 掩码。数据缓冲区的长度由 len 标识;最近的内核支持高达约 128 KB 的请求。FIXME 说明如何返回读取长度以及如何处理短读取。

USBDEVFS_CLEAR_HALT

清除端点暂停(停止)并重置端点切换。这仅对批量或中断端点有意义。ioctl 参数是一个整数端点编号(1 到 15,如端点描述符中标识),当引用将数据从设备发送到主机的端点时,用 USB_DIR_IN 掩码。

在已停止的批量或中断端点上使用此选项,从而向数据传输请求返回 -EPIPE 状态。不要直接发出控制请求,因为这可能会使主机的数据切换记录无效。

USBDEVFS_CONTROL

向设备发出控制请求。ioctl 参数指向一个类似于这样的结构

struct usbdevfs_ctrltransfer {
        __u8   bRequestType;
        __u8   bRequest;
        __u16  wValue;
        __u16  wIndex;
        __u16  wLength;
        __u32  timeout;  /* in milliseconds */
        void   *data;
};

此结构的前八个字节是要发送到设备的 SETUP 数据包的内容;有关详细信息,请参阅 USB 2.0 规范。bRequestType 值是通过组合 USB_TYPE_* 值、USB_DIR_* 值和 USB_RECIP_* 值(来自 linux/usb.h)组成的。如果 wLength 不为零,则描述数据缓冲区的长度,该缓冲区要么写入设备 (USB_DIR_OUT),要么从设备读取 (USB_DIR_IN)。

在撰写本文时,您无法向设备传输超过 4 KB 的数据或从设备传输超过 4 KB 的数据;usbfs 有一个限制,并且某些主机控制器驱动程序也有一个限制。(这通常不是问题。)此外,无法表示从设备接收短读取是正常的。

USBDEVFS_RESET

执行 USB 级设备重置。ioctl 参数将被忽略。重置后,这会重新绑定所有设备接口。此请求不会更新文件修改时间。

警告

在修复一些 usbcore 错误之前,请避免使用此调用,因为它不能完全同步设备、接口和驱动程序(不仅仅是 usbfs)状态。

USBDEVFS_SETINTERFACE

设置接口的备用设置。ioctl 参数是指向类似于这样的结构的指针

struct usbdevfs_setinterface {
        unsigned int  interface;
        unsigned int  altsetting;
};

此请求不会更新文件修改时间。

这些结构体成员来自应用于当前配置的某个接口描述符。接口号是 bInterfaceNumber 的值,备选设置号是 bAlternateSetting 的值。(这会重置接口中的每个端点。)

USBDEVFS_SETCONFIGURATION

对设备发出 usb_set_configuration() 调用。参数是一个整数,表示配置的编号(来自描述符的 bConfigurationValue)。此请求不会更新文件修改时间。

警告

在修复一些 usbcore 错误之前,请避免使用此调用,因为它不能完全同步设备、接口和驱动程序(不仅仅是 usbfs)状态。

异步 I/O 支持

如上所述,在某些情况下,从用户模式代码发起并发操作可能很重要。这对于周期性传输(中断和同步)尤其重要,但也可用于其他类型的 USB 请求。在这种情况下,此处描述的异步请求是必不可少的。与其提交一个请求并让内核阻塞直到完成,不如将阻塞分开处理。

这些请求被打包到一个类似于内核设备驱动程序使用的 URB 的结构中。(抱歉,这里没有 POSIX 异步 I/O 支持。)它标识端点类型(USBDEVFS_URB_TYPE_*)、端点(编号,使用 USB_DIR_IN 进行适当的掩码)、缓冲区和长度,以及一个用于唯一标识每个请求的用户“上下文”值。(它通常是指向每个请求数据的指针。)标志可以修改请求(不如内核驱动程序支持的多)。

每个请求都可以指定一个实时信号编号(介于 SIGRTMIN 和 SIGRTMAX 之间,包括两者)以请求在请求完成时发送信号。

当 usbfs 返回这些 urbs 时,状态值会被更新,并且缓冲区可能已被修改。除了同步传输外,actual_length 会被更新以说明传输了多少字节;如果设置了 USBDEVFS_URB_DISABLE_SPD 标志(“不允许短包”),如果读取的字节数少于请求的字节数,则会收到错误报告

struct usbdevfs_iso_packet_desc {
        unsigned int                     length;
        unsigned int                     actual_length;
        unsigned int                     status;
};

struct usbdevfs_urb {
        unsigned char                    type;
        unsigned char                    endpoint;
        int                              status;
        unsigned int                     flags;
        void                             *buffer;
        int                              buffer_length;
        int                              actual_length;
        int                              start_frame;
        int                              number_of_packets;
        int                              error_count;
        unsigned int                     signr;
        void                             *usercontext;
        struct usbdevfs_iso_packet_desc  iso_frame_desc[];
};

对于这些异步请求,文件修改时间反映的是请求的发起时间。这与它们在同步请求中的使用形成对比,在同步请求中,它反映的是请求完成的时间。

USBDEVFS_DISCARDURB

待补充 此请求不会更新文件修改时间。

USBDEVFS_DISCSIGNAL

待补充 此请求不会更新文件修改时间。

USBDEVFS_REAPURB

待补充 此请求不会更新文件修改时间。

USBDEVFS_REAPURBNDELAY

待补充 此请求不会更新文件修改时间。

USBDEVFS_SUBMITURB

待补充

USB 设备

USB 设备现在通过 debugfs 导出

  • /sys/kernel/debug/usb/devices ... 一个文本文件,显示内核已知的所有 USB 设备及其配置描述符。你也可以轮询 (poll()) 它来了解新设备。

/sys/kernel/debug/usb/devices

此文件对于用户模式下的状态查看工具非常方便,它可以扫描文本格式并忽略大部分内容。更详细的设备状态(包括类和供应商状态)可以从特定于设备的文件中获得。有关此文件当前格式的信息,请参见下文。

此文件与 poll() 系统调用结合使用,还可以检测到何时添加或删除设备

int fd;
struct pollfd pfd;

fd = open("/sys/kernel/debug/usb/devices", O_RDONLY);
pfd = { fd, POLLIN, 0 };
for (;;) {
    /* The first time through, this call will return immediately. */
    poll(&pfd, 1, -1);

    /* To see what's changed, compare the file's previous and current
       contents or scan the filesystem.  (Scanning is more precise.) */
}

请注意,此行为旨在用于信息和调试目的。更适合使用诸如 udev 或 HAL 之类的程序来初始化设备或启动用户模式辅助程序,例如。

在此文件中,每个设备的输出都有多行 ASCII 输出。

我故意将其设为 ASCII 而不是二进制,这样有人就可以在不使用辅助程序的情况下从中获取一些有用的数据。但是,使用辅助程序,每行 T: 的前 4 列中的数字(拓扑信息:Lev、Prnt、Port、Cnt)可用于构建 USB 拓扑图。

每行都用该行的单字符 ID 标记

T = Topology (etc.)
B = Bandwidth (applies only to USB host controllers, which are
virtualized as root hubs)
D = Device descriptor info.
P = Product ID info. (from Device descriptor, but they won't fit
together on one line)
S = String descriptors.
C = Configuration descriptor info. (* = active configuration)
I = Interface descriptor info.
E = Endpoint descriptor info.

/sys/kernel/debug/usb/devices 输出格式

图例:

d = 十进制数(可能带有前导空格或 0) x = 十六进制数(可能带有前导空格或 0) s = 字符串

拓扑信息
T:  Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=dddd MxCh=dd
|   |      |      |       |       |      |        |        |__MaxChildren
|   |      |      |       |       |      |        |__Device Speed in Mbps
|   |      |      |       |       |      |__DeviceNumber
|   |      |      |       |       |__Count of devices at this level
|   |      |      |       |__Connector/Port on Parent for this device
|   |      |      |__Parent DeviceNumber
|   |      |__Level in topology for this bus
|   |__Bus number
|__Topology info tag

速度可能是

1.5

Mbit/s 用于低速 USB

12

Mbit/s 用于全速 USB

480

Mbit/s 用于高速 USB(为 USB 2.0 添加)

5000

Mbit/s 用于超高速 USB(为 USB 3.0 添加)

由于时间久远,端口号总是偏低 1。例如,插入端口 4 的设备将显示为 Port=03

带宽信息
B:  Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd
|   |                       |         |__Number of isochronous requests
|   |                       |__Number of interrupt requests
|   |__Total Bandwidth allocated to this bus
|__Bandwidth info tag

带宽分配是对一个帧(毫秒)内使用量的近似值。它仅反映周期性传输,这是唯一保留带宽的传输。控制和批量传输使用所有其他带宽,包括未用于传输的保留带宽(例如用于短数据包)。

百分比表示这些传输调度的“保留”带宽的多少。对于低速或全速总线(粗略地说,“USB 1.1”),保留 90% 的总线带宽。对于高速总线(粗略地说,“USB 2.0”),保留 80%。

设备描述符信息和产品 ID 信息
D:  Ver=x.xx Cls=xx(s) Sub=xx Prot=xx MxPS=dd #Cfgs=dd
P:  Vendor=xxxx ProdID=xxxx Rev=xx.xx

其中

D:  Ver=x.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd
|   |        |             |      |       |       |__NumberConfigurations
|   |        |             |      |       |__MaxPacketSize of Default Endpoint
|   |        |             |      |__DeviceProtocol
|   |        |             |__DeviceSubClass
|   |        |__DeviceClass
|   |__Device USB version
|__Device info tag #1

其中

P:  Vendor=xxxx ProdID=xxxx Rev=xx.xx
|   |           |           |__Product revision number
|   |           |__Product ID code
|   |__Vendor ID code
|__Device info tag #2
字符串描述符信息
S:  Manufacturer=ssss
|   |__Manufacturer of this device as read from the device.
|      For USB host controller drivers (virtual root hubs) this may
|      be omitted, or (for newer drivers) will identify the kernel
|      version and the driver which provides this hub emulation.
|__String info tag

S:  Product=ssss
|   |__Product description of this device as read from the device.
|      For older USB host controller drivers (virtual root hubs) this
|      indicates the driver; for newer ones, it's a product (and vendor)
|      description that often comes from the kernel's PCI ID database.
|__String info tag

S:  SerialNumber=ssss
|   |__Serial Number of this device as read from the device.
|      For USB host controller drivers (virtual root hubs) this is
|      some unique ID, normally a bus ID (address or slot name) that
|      can't be shared with any other device.
|__String info tag
配置描述符信息
C:* #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA
| | |       |       |      |__MaxPower in mA
| | |       |       |__Attributes
| | |       |__ConfiguratioNumber
| | |__NumberOfInterfaces
| |__ "*" indicates the active configuration (others are " ")
|__Config info tag

USB 设备可能具有多个配置,每个配置的行为都大相径庭。例如,总线供电的配置可能远不如自供电的配置强大。一次只能激活一个设备配置;大多数设备只有一个配置。

每个配置由一个或多个接口组成。每个接口提供一个不同的“功能”,通常绑定到不同的 USB 设备驱动程序。一个常见的示例是具有用于回放的音频接口的 USB 扬声器,以及用于软件音量控制的 HID 接口。

接口描述符信息(每个配置可以有多个)
I:* If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=ssss
| | |      |      |       |             |      |       |__Driver name
| | |      |      |       |             |      |          or "(none)"
| | |      |      |       |             |      |__InterfaceProtocol
| | |      |      |       |             |__InterfaceSubClass
| | |      |      |       |__InterfaceClass
| | |      |      |__NumberOfEndpoints
| | |      |__AlternateSettingNumber
| | |__InterfaceNumber
| |__ "*" indicates the active altsetting (others are " ")
|__Interface info tag

给定的接口可能有一个或多个“备选”设置。例如,默认设置可能不会使用超过少量周期性带宽。要使用总线带宽的相当大一部分,驱动程序必须选择一个非默认的备选设置。

一个接口一次只能激活一个设置,并且一次只能有一个驱动程序绑定到一个接口。大多数设备每个接口只有一个备选设置。

端点描述符信息(每个接口可以有多个)
E:  Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddss
|   |        |            |         |__Interval (max) between transfers
|   |        |            |__EndpointMaxPacketSize
|   |        |__Attributes(EndpointType)
|   |__EndpointAddress(I=In,O=Out)
|__Endpoint info tag

对于所有周期性(中断或同步)端点,间隔都为非零值。对于高速端点,传输间隔可以以微秒而不是毫秒为单位进行度量。

对于高速周期性端点,EndpointMaxPacketSize 反映了每个微帧的数据传输大小。对于“高带宽”端点,它可以反映每个端点的两个或三个数据包(每 125 微秒最多 3 千字节)。

对于 Linux-USB 堆栈,周期性带宽预留使用 URB 提供的传输间隔和大小,这些间隔和大小可能小于端点描述符中的值。

使用示例

例如,如果用户或脚本只对拓扑信息感兴趣,则使用类似 grep ^T: /sys/kernel/debug/usb/devices 的命令只获取拓扑行。可以使用类似 grep -i ^[tdp]: /sys/kernel/debug/usb/devices 的命令仅列出以方括号中的字符开头的行,其中有效的字符为 TDPCIE。使用稍微更强大的脚本,它可以显示任何选定的行(例如,仅 T、D 和 P 行)并更改其输出格式。(procusb Perl 脚本是这个想法的开始。它将仅列出选定的行(从 TBDPSCIE 中选择)或来自 /sys/kernel/debug/usb/devices 的“所有”行。)

拓扑行可用于生成系统根集线器上 USB 设备的图形/图示。(请参阅下文,了解如何执行此操作。)

接口行可用于确定每个设备正在使用哪个驱动程序以及它激活了哪个备选设置。

配置行可用于列出系统 USB 设备正在使用的最大功率(以毫安为单位)。例如,grep ^C: /sys/kernel/debug/usb/devices

以下是一个示例,来自一个具有 UHCI 根集线器、连接到根集线器的外部集线器以及连接到外部集线器的鼠标和串行转换器的系统。

T:  Bus=00 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#=  1 Spd=12   MxCh= 2
B:  Alloc= 28/900 us ( 3%), #Int=  2, #Iso=  0
D:  Ver= 1.00 Cls=09(hub  ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
P:  Vendor=0000 ProdID=0000 Rev= 0.00
S:  Product=USB UHCI Root Hub
S:  SerialNumber=dce0
C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr=  0mA
I:  If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
E:  Ad=81(I) Atr=03(Int.) MxPS=   8 Ivl=255ms

T:  Bus=00 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=12   MxCh= 4
D:  Ver= 1.00 Cls=09(hub  ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
P:  Vendor=0451 ProdID=1446 Rev= 1.00
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=100mA
I:  If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
E:  Ad=81(I) Atr=03(Int.) MxPS=   1 Ivl=255ms

T:  Bus=00 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#=  3 Spd=1.5  MxCh= 0
D:  Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
P:  Vendor=04b4 ProdID=0001 Rev= 0.00
C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=100mA
I:  If#= 0 Alt= 0 #EPs= 1 Cls=03(HID  ) Sub=01 Prot=02 Driver=mouse
E:  Ad=81(I) Atr=03(Int.) MxPS=   3 Ivl= 10ms

T:  Bus=00 Lev=02 Prnt=02 Port=02 Cnt=02 Dev#=  4 Spd=12   MxCh= 0
D:  Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
P:  Vendor=0565 ProdID=0001 Rev= 1.08
S:  Manufacturer=Peracom Networks, Inc.
S:  Product=Peracom USB to Serial Converter
C:* #Ifs= 1 Cfg#= 1 Atr=a0 MxPwr=100mA
I:  If#= 0 Alt= 0 #EPs= 3 Cls=00(>ifc ) Sub=00 Prot=00 Driver=serial
E:  Ad=81(I) Atr=02(Bulk) MxPS=  64 Ivl= 16ms
E:  Ad=01(O) Atr=02(Bulk) MxPS=  16 Ivl= 16ms
E:  Ad=82(I) Atr=03(Int.) MxPS=   8 Ivl=  8ms

从此中仅选择 T:I: 行(例如,使用 procusb ti),我们得到

T:  Bus=00 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#=  1 Spd=12   MxCh= 2
T:  Bus=00 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=12   MxCh= 4
I:  If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
T:  Bus=00 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#=  3 Spd=1.5  MxCh= 0
I:  If#= 0 Alt= 0 #EPs= 1 Cls=03(HID  ) Sub=01 Prot=02 Driver=mouse
T:  Bus=00 Lev=02 Prnt=02 Port=02 Cnt=02 Dev#=  4 Spd=12   MxCh= 0
I:  If#= 0 Alt= 0 #EPs= 3 Cls=00(>ifc ) Sub=00 Prot=00 Driver=serial

物理上,它看起来像这样(或者可以转换为):

                    +------------------+
                    |  PC/root_hub (12)|   Dev# = 1
                    +------------------+   (nn) is Mbps.
  Level 0           |  CN.0   |  CN.1  |   [CN = connector/port #]
                    +------------------+
                        /
                       /
          +-----------------------+
Level 1   | Dev#2: 4-port hub (12)|
          +-----------------------+
          |CN.0 |CN.1 |CN.2 |CN.3 |
          +-----------------------+
              \           \____________________
               \_____                          \
                     \                          \
             +--------------------+      +--------------------+
Level 2      | Dev# 3: mouse (1.5)|      | Dev# 4: serial (12)|
             +--------------------+      +--------------------+

或者,在更像树的结构中(可以省略没有连接的端口 [连接器])

PC:  Dev# 1, root hub, 2 ports, 12 Mbps
|_ CN.0:  Dev# 2, hub, 4 ports, 12 Mbps
     |_ CN.0:  Dev #3, mouse, 1.5 Mbps
     |_ CN.1:
     |_ CN.2:  Dev #4, serial, 12 Mbps
     |_ CN.3:
|_ CN.1: