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 内核系列的早期为 Linux 添加了 USB 支持,并且从那时起一直在进一步开发它。 除了支持每个新一代 USB 之外,各种主机控制器也获得了支持,添加了新的外围设备驱动程序,并引入了用于延迟测量和改进电源管理的高级功能。

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

USB 主机端 API 模型

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

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

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

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

  • 从 USB 3.0 开始,配置有一个或多个“功能”,这些功能提供通用功能,并组合在一起用于电源管理。

  • 配置或功能具有一个或多个“接口”,每个接口可能具有“备用设置”。 接口可以由 USB “类”规范标准化,也可以特定于供应商或设备。

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

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

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

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

因此,暴露给设备驱动程序的 USB Core 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_otg_state_string(enum usb_otg_state state)

返回 OTG 状态的可读名称。

参数

enum usb_otg_state state

要返回可读名称的 OTG 状态。 如果它不是 usb_otg_state 枚举中定义的任何状态,则将返回“UNDEFINED”。

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”获取最大速度字符串,并返回相应的 enum 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”是通道数,则此函数返回相应的 enum 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”获取字符串,并返回相应的 enum usb_dr_mode。

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

将 bInterval 解码为以 1us 为单位表示的时间

参数

const struct usb_endpoint_descriptor *epd

端点的描述符

enum usb_device_speed speed

端点工作的速度

描述

函数返回以 1us 为单位表示的为端点提供数据传输服务的时间间隔。

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”获取字符串,并返回相应的 enum usb_dr_mode。

bool of_usb_host_tpl_support(struct device_node *np)

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

参数

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 将缓冲区传递给某些 HCD,这些 HCD 为设备驱动程序执行 I/O。

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 usb_eusb2_isoc_ep_comp_descriptor        eusb2_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 同步辅助描述符

eusb2_isoc_ep_comp

此端点的 eUSB2 同步辅助描述符

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

已推迟 altsetting 0 的设置接口请求时设置的标志。

needs_binding

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

resetting_device

USB 内核重置设备,因此使用 alt 设置 0 作为当前设置; 重置后需要带宽分配。

authorized

这允许(取消)授权单个接口,而不是与设备授权相反的整个设备。

wireless_status

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

wireless_status_work

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

dev

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

usb_dev

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

reset_ws

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

描述

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

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

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

USB 规范指出,备用设置编号必须从 0 运行到小于备用设置总数的一个数字。 但是某些设备设法弄乱了这一点,并且无论如何都不能保证结构以数字顺序存储。 使用 usb_altnum_to_altsetting() 在 altsetting 数组中查找基于其编号的备用设置。

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

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

参数

struct usb_interface *intf

USB 接口

void *data

驱动程序数据

描述

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

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

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

设备的配置描述符。

string

如果存在此配置的 iConfiguration 字符串的缓存版本,则指向该字符串的指针。

intf_assoc

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

interface

指向 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

正在使用的 rx 通道数,USB 3.2 增加了双通道支持

tx_lanes

正在使用的 tx 通道数,USB 3.2 增加了双通道支持

ssp_rate

SuperSpeed Plus phy 信令速率和通道数

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:由 HW 分配,其他:与 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

整个设备的 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

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

Notes

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 (零)。

Note

调用者必须拥有驱动程序模型的 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) 如果大小太小,则为负数。

Note

此标识符旨在“稳定”,反映硬件中的物理路径,例如主机控制器或 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

将 USB 接口驱动程序标识到 usbcore

定义:

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() 来指定适当的 altsetting。 如果不愿意管理该接口,则返回 -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,...) 导出此表。 必须设置此表,否则您的驱动程序的探测函数将永远不会被调用。

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 核心在调用驱动程序的断开连接方法之前不会终止 URB 和禁用端点。

描述

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

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

probe() 和 disconnect() 方法在可以休眠的上下文中调用,但应避免滥用该特权。 连接到设备的大多数工作应在打开设备时完成,并在最后一次关闭时完成撤消。 断开连接的代码需要解决有关 open() 和 close() 方法的并发问题,以及强制所有挂起的 I/O 请求完成(根据需要取消链接它们,并阻止直到取消链接完成)。

struct usb_device_driver

将 USB 设备驱动程序标识到 usbcore

定义:

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,则除了驱动程序自己的函数之外,还将调用通用 USB 驱动程序的 probe、disconnect、resume 和 suspend 函数,因此不需要复制此设置部分。

描述

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 结构

描述

用于在模块 init/exit 中不执行任何特殊操作的 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()。 端点号的范围为零到十五。 请注意,“in”端点 2 与“out”端点 2 是不同的端点(和管道)。 当前配置控制任何给定端点的存在、类型和最大数据包大小。

stream_id

批量流的端点流 ID

status

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

transfer_flags

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

transfer_buffer

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

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

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

setup_packet

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

setup_dma

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

start_frame

返回等时传输的初始帧。

number_of_packets

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

interval

指定中断或等时传输的轮询间隔。 对于全速和低速设备,单位为帧(毫秒),对于高速和 SuperSpeed 设备,单位为微帧(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() 取消挂起的请求。

Data Transfer Buffers

通常,驱动程序提供使用 kmalloc() 分配的 I/O 缓冲区,或者以其他方式从通用页面池中获取的 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。 如果必须在此类控制器上的高内存区域和设备之间传输,请创建一个缓冲区或通过错误退出。 如果无法设置 transfer_buffer(在高内存中)并且控制器具有 DMA 功能,则将其分配为 NULL,以便 usbmon 知道不要使用该值。 必须始终设置 setup_packet,因此它不能位于高内存中。

Initialization

所有提交的 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 必须提供一个 interval,说明轮询传输的频率(以毫秒为单位,或者对于高速设备,以 125 微秒为单位)。 提交 URB 后,interval 字段会反映实际安排传输的方式。 轮询间隔可能比请求的频率更高。 例如,某些控制器的最大间隔为 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() 中进行的,完成处理程序应该做的第一件事之一是检查 status 字段。status 字段是为所有 URB 提供的。它用于报告未链接的 URB 以及所有非 ISO 传输的状态。在 URB 返回到完成处理程序之前,不应检查它。

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

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

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

请注意,即使标记为“public”的字段,当 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()

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

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() 的请求句柄。请求对象的大多数成员都不供驱动程序访问。

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

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

USB Core 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 core 不再使用它。

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

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 数据包的数量传递。

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

返回

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

void usb_free_urb(struct urb *urb)

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

参数

struct urb *urb

要释放的 urb 的指针,可能为 NULL

描述

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

Note

除非设置了 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() 之类的函数可用于确保为特定类型的传输正确初始化大多数字段,尽管它们不会初始化任何传输标志。

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

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

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

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

请求排队

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

从 Linux 2.6 开始,所有 USB 端点传输队列都支持大于 1 的深度。这以前是 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. 您正在持有自旋锁或 rwlock(不适用于信号量),或者

  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. USB 探测和断开连接可以使用 GFP_KERNEL,除非应用 (b) 或 (c);并且

  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() 的其他情况下使用。

驱动程序的断开连接方法返回后,不应调用此例程。

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() 的其他情况下使用。

驱动程序的断开连接方法返回后,不应调用此例程。

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,并保证在此函数返回后,不会从锚发生任何完成回调。

驱动程序的断开连接方法返回后,不应调用此例程。

void usb_poison_anchored_urbs(struct usb_anchor *anchor)

停止来自锚的所有流量

参数

struct usb_anchor *anchor

请求绑定到的锚

描述

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

驱动程序的断开连接方法返回后,不应调用此例程。

void usb_unpoison_anchored_urbs(struct usb_anchor *anchor)

让锚再次成功使用

参数

struct usb_anchor *anchor

请求绑定到的锚

描述

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

异步地批量取消传输请求

参数

struct usb_anchor *anchor

请求绑定到的锚

描述

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

驱动程序的断开连接方法返回后,不应调用此例程。

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 没有与其关联的 urb,则为 NULL

void usb_scuttle_anchored_urbs(struct usb_anchor *anchor)

取消锚的所有 urb

参数

struct usb_anchor *anchor

您想要取消锚定的 urb 的锚

描述

使用此方法摆脱锚的所有 urb

int usb_anchor_empty(struct usb_anchor *anchor)

锚是否为空

参数

struct usb_anchor *anchor

您要查询的锚

返回

如果锚没有与其关联的 urb,则为 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

要将消息发送到的端点“管道”

__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

要将消息发送到的端点“管道”

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

要将消息发送到的端点“管道”

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 KBytes。

通过此 API 使用中断传输的原因很可能是为了保留高速带宽,在这种情况下,可以传输高达每毫秒 24 KBytes 的数据。对于低速或全速中断端点,此功能不太有用,这些端点最多允许每毫秒一个数据包,最大为 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_get_string() 或 usb_string() 用于 USB_DT_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'ed 缓冲区的指针,如果索引为 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

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

上下文

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

描述

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

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

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

成功时返回 0 和 *data 中的状态值(以主机字节顺序),否则返回底层 usb_control_msg() 调用的状态代码。

int usb_clear_halt(struct usb_device *dev, int pipe)

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

参数

struct usb_device *dev

端点已停止的设备

int pipe

正在清除的端点“管道”

上下文

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

描述

这用于清除批量和中断端点的停止状态,如 URB 完成状态报告的那样。停止的端点有时被称为“stalled”。在清除停止状态之前,此类端点无法发送或接收数据。任何为此类端点排队的 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 个字节,而每微帧最多 3KBytes 的中断传输是合法的。此外,同步端点可能永远不会成为接口默认设置的一部分。要访问此类带宽,必须使备用接口设置成为当前设置。

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

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

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

返回

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

int usb_reset_configuration(struct usb_device *dev)

轻量级设备重置

参数

struct usb_device *dev

正在重置其配置的设备

描述

这使用当前配置向设备发出标准 SET_CONFIGURATION 请求。效果是重置设备中的大多数 USB 相关状态,包括接口备用设置(重置为零)、端点停止(清除)和端点状态(仅适用于批量和中断端点)。其他 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

正在选择的配置。

上下文

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

描述

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

尽管如此,在某些特殊情况下,可能会出现这种需求。此例程通过使用工作线程提交 change-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 数组,并返回第一个与设备或接口匹配的 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 中记录的那样)。

请注意,如果设备类设置为 Vendor-Specific,则由 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。

返回

如果找到所有请求的描述符,则为零,否则为 -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。

返回

如果找到所有请求的描述符,则为零,否则为 -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

端口的从一开始的索引;对于根集线器将被忽略

上下文

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

描述

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

此调用可能无法在非睡眠上下文中使用。

返回

成功时,指向分配的 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 控制器可以执行 DMA 操作。调用 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 主机控制器的当前帧编号。这可以在计划等时请求时使用。

Note

不同类型的主机控制器具有不同的“计划范围”。虽然一种类型可能仅支持在未来 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 地址一起返回(通过提供的指针)。

Note

这些缓冲区与 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

描述

当 USB 主机控制器驱动程序恢复其根集线器并且 Vbus 电源已中断或控制器已重置时,它会调用此函数。该例程将 **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。

Note

调用者必须拥有设备锁。例如,在下载新固件后,从此驱动程序 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,如果 child 的 usb_device 指针不为 NULL,则为 child 的 usb_device 指针。

主机控制器 API

这些 API 仅供主机控制器驱动程序使用,其中大多数实现了标准寄存器接口,例如 XHCI、EHCI、OHCI 或 UHCI。UHCI 是最早的接口之一,由 Intel 设计,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

事务中有多少字节。

返回

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

Note

请参阅 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

正在返回到 USB 设备驱动程序的 URB。

int status

URB 的完成状态代码。

上下文

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

描述

这使用 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 主机控制器挂起其根集线器(启用了远程唤醒功能)并且收到远程唤醒请求时,它会调用此函数。该例程提交一个工作队列请求以恢复根集线器(即,再次管理其下游端口)。

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 的指针

描述

如果控制器未 HALTed,则调用驱动程序的 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驱动程序编写新的内核代码。用户模式设备驱动程序通常打包为应用程序或库,并且可以使用一些封装它的编程库来使用字符设备。这样的库包括

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

Note

  • 它们曾经通过 *usbfs* 实现,但这不是sysfs调试接口的一部分。

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

“devtmpfs”中有哪些文件?

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

  • /dev/bus/usb/BBB/DDD ... 魔术文件暴露每个设备的配置描述符,并支持一系列的ioctl来发出设备请求,包括到设备的I/O。(纯粹是为了程序访问。)

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

/dev/bus/usb/BBB/DDD

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

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

  • 执行USB操作 使用 ioctl() 请求来发出端点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 选择性地授予其他用户读/写权限。此外,usbfs挂载选项(如 devmode=0666)可能很有用。

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

这样的驱动程序首先需要找到它可以处理的设备的设备文件。也许是因为 /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设备模型请求,来自USB 2.0规范的“第9章”,自动包含在 <linux/usb/ch9.h> 标头中。

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

这些文件中的每一个都多路复用对多个I/O流的访问,每个端点一个。每个设备都有一个控制端点(端点零),它支持有限的RPC风格RPC访问。设备由 hub_wq(在内核中)设置影响功耗和基本功能等设备的设备范围 配置。端点是USB 接口 的一部分,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

返回设备的速度。速度以数值形式返回,与enum 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 KBytes的请求。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 KBytes的数据传输到设备或从设备传输数据;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

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

USBDEVFS_DISCSIGNAL

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

USBDEVFS_REAPURB

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

USBDEVFS_REAPURBNDELAY

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

USBDEVFS_SUBMITURB

TBS

USB设备

USB设备现在通过debugfs导出

  • /sys/kernel/debug/usb/devices ... 一个文本文件,显示内核已知的每个USB设备及其配置描述符。您也可以轮询()此文件以了解新设备。

/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表示SuperSpeed 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设备驱动程序。一个常见的例子是带有用于播放的音频接口和用于软件音量控制的HID接口的USB扬声器。

接口描述符信息(每个Config可以有多个)
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 KBytes)。

使用Linux-USB堆栈,定期带宽预留使用URB提供的传输间隔和大小,这些间隔和大小可能小于端点描述符中的间隔和大小。

用法示例

如果用户或脚本仅对拓扑信息感兴趣,例如,使用类似 grep ^T: /sys/kernel/debug/usb/devices 仅获取拓扑行。诸如 grep -i ^[tdp]: /sys/kernel/debug/usb/devices 之类的命令可用于仅列出以方括号中的字符开头的行,其中有效字符为 TDPCIE。通过稍有能力的脚本,它可以显示任何选定的行(例如,仅T,D和P行)并更改其输出格式。(procusb Perl脚本是这个想法的开始。它将仅列出来自 /sys/kernel/debug/usb/devices 的选定行[从TBDPSCIE中选择]或“全部”行。)

拓扑行可用于生成系统根集线器上的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: