首页 > 系统相关 >linux usb 驱动 - hcd 驱动框架

linux usb 驱动 - hcd 驱动框架

时间:2025-01-17 14:58:42浏览次数:3  
标签:usb driver struct hcd device 驱动 设备

linux usb hcd 驱动框架

作者: baron
个人博客: baron-z.cn

基于网站原因csdn上面的图片有压缩, 如果不是很清楚, 可以到个人博客看原图.

本文基于君正平台(SoC)和其集成的 DWC2(DesignWare® USB 2.0 Controller),对 USB 子系统的核心流程进行了详细分析。文章主要聚焦于以下几个方面:

  • USB 设备描述符和 urb 通讯
  • USB 设备驱动管理
  • USB 主机控制器(HCD)的注册流程;
  • USB 设备的识别流程;

希望本文能为从事相关开发或研究的读者提供有价值的参考

一、USB 描述符

    USB 设备用 USB 描述符 来描述自己属性及用途. 所以设备端必须实现对应的描述符. 主机会在枚举此设备的时候根据设备实现的描述符去确定设备到底是一个什么样的设备、设备需要的总线资源、和设备的通讯方式等.

// ./include/uapi/linux/usb/ch9.h
#define USB_DT_DEVICE                              0x01   // 设备描述符
#define USB_DT_CONFIG                              0x02   // 配置描述符
#define USB_DT_STRING                              0x03   // 字符串描述符
#define USB_DT_INTERFACE                           0x04   // 接口描述符
#define USB_DT_ENDPOINT                            0x05   // 端点描述符
#define USB_INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE  0x0b   // 接口关联描述符
......

1. 数据结构

1.1 设备描述符

    每一个 USB 设备只有一个设备描述符, 主要向主机说明设备类型、端点0最大包长、设备版本、配置数量等等。

struct usb_device_descriptor {
    __u8  bLength;              // 设备描述符 usb_device_descriptor 结构体的大小, 是固定值为 sizeof(struct usb_device_descriptor)
    __u8  bDescriptorType;      // 描述符类型编号, 设备描述符固定为 0x01

    __le16 bcdUSB;              // usb 的版本号, USB 1.0: 0x0100、USB 2.0: 0x0200 
    __u8  bDeviceClass;         // 设备类, 0x08 表示 u 盘, 0xEF 表示复合设备
    __u8  bDeviceSubClass;      // 描述具体的设备类型, 如 0x01 表示音频设备
    __u8  bDeviceProtocol;      // bDeviceSubClass 的细分, 详细参考后面的表格
    __u8  bMaxPacketSize0;      // 端点0的最大包的大小
    __le16 idVendor;            // 厂商 ID
    __le16 idProduct;           // 设备 ID
    __le16 bcdDevice;           // 设备出厂编码
    __u8  iManufacturer;        // 厂商描述符字符串索引.索引到对应的字符串描述符. 为0则表示没有
    __u8  iProduct;             // 产品描述符字符串索引, 为0则表示没有
    __u8  iSerialNumber;        // 设备序列号字符串索引, 为0则表示没有
    __u8  bNumConfigurations;   // 可能的配置数量, 定义设备以当前速度支持的配置数量
} __attribute__ ((packed));

    其中 bDeviceClassbDeviceSubClassbDeviceProtocol组合起来描述具体的设备的类型.

bDeviceClassbDeviceSubClass 值子类别描述bDeviceProtocol 值协议描述
设备未定义 (0x00)0x00类别由接口指定0x00无协议
音频类 (0x01)
0x00不指定子类别0x00无协议
0x01音频控制设备0x00无协议
0x02音频流传输设备0x00无协议
0x03MIDI 流传输设备0x00无协议
通信设备类 (0x02)
0x00不指定子类别0x00无协议
0x01直接线路控制模型0x00无协议
0x02通用协议模型0x00无协议
0x06以太网网络设备0x00无协议
人机接口设备 (HID, 0x03)
0x00无子类0x00无协议
0x01键盘0x01键盘协议
0x02鼠标0x02鼠标协议
物理设备类 (0x05)
0x00物理设备0x00无协议
(无子类别和协议定义)
图像类 (0x06)
0x00图像控制设备0x00无协议
0x01图像传输设备0x00无协议
(无其他子类和协议定义)
打印机类 (0x07)
0x00打印机设备0x01双向协议
(0x02 - IEEE 1284.4 协议)
(0x03 - IEEE 1284.4 兼容协议)
大容量存储类 (0x08)
0x00未定义0x00无协议
0x01只读存储设备 (RBC)0x00无协议
0x02ATAPI 命令块设备0x00无协议
0x04UFI(通用软盘接口设备)0x00无协议
0x05SFF-8070i0x00无协议
0x06SCSI 传输协议0x50Bulk-Only Transport
集线器类 (0x09)
0x00集线器设备0x00全速集线器(FS)
(0x01 - 高速集线器(HS))
复合设备类 (0xEF)0x02常规复合设备0x01FCP (功能复合协议)
应用特定类 (0xFE)
0x01DFU(设备固件升级)0x01设备固件升级协议
0x02IrDA 桥接设备0x00无协议
0x03USB 测试和测量类设备0x00无协议
供应商自定义类 (0xFF)0x00供应商自定义设备0x00无协议

1.2 配置描述符

一个设备可以有多个配置描述符, 大部分 usb 只有一个配置描述符.

struct usb_config_descriptor {
    __u8  bLength;               // 配置描述符占用多少字节, 固定值
    __u8  bDescriptorType;       // 描述符的类型编号. 配置描述符固定为 USB_DT_CONFIG 0x02

    __le16 wTotalLength;         // 该配置中所有相关描述符的总长度,单位是字节。它不仅包括配置描述符本身的长度,还包括该配置下的所有接口描述符、端点描述符和其他可能的描述符(例如 HID 描述符)的长度总和
    __u8  bNumInterfaces;        // 该配置下的接口描述符的数量 
    __u8  bConfigurationValue;   // 对应配置的编号, 不同的配置拥有不同的编号, 第一个为1 , 第二个为2. 通过这个编号选择不同的配置
    __u8  iConfiguration;        // 对应的字符串描述符的编号, 通过这个编号拿到对应的字符串描述符.


    __u8  bmAttributes;         // 设备配置的电源特性和功能, 7位保留,始终为1. 6位是否支持自供电, 1自供电、5是否支持远程唤醒,1支持、4-0保留始终为0 
    __u8  bMaxPower;            // 设备从总线提取的最大电流
} __attribute__ ((packed));

1.3 接口描述符

struct usb_interface_descriptor {
    __u8  bLength;              // 接口描述符的长度
    __u8  bDescriptorType;      // 描述符类型编号, 固定为 USB_DT_INTERFACE 0x04

    __u8  bInterfaceNumber;     // 接口编号
    __u8  bAlternateSetting;    // 备用的接口编号
    __u8  bNumEndpoints;        // 该接口使用的端点数量
    __u8  bInterfaceClass;      // 接口类别, 即前面的设备描述中的设备类型的具体细分
    __u8  bInterfaceSubClass;   // 描述具体的设备类型, 如 0x01 表示音频设备
    __u8  bInterfaceProtocol;   // 进一步细分接口类型
    __u8  iInterface;           // 描述该接口的字符串描述符索引
} __attribute__ ((packed));

主要用于描述复合设备中的接口关联.

struct usb_interface_assoc_descriptor {
    __u8  bLength;            // 接口关联描述符的长度
    __u8  bDescriptorType;    // 描述符型固定为 USB_INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE 0x0b

    __u8  bFirstInterface;    // 第一个接口编号
    __u8  bInterfaceCount;    // 接口总数
    __u8  bFunctionClass;     // 定义该接口关联描述符的功能类别,帮助主机识别设备的功能类别。对于复合设备,bFunctionClass 描述了整个关联接口的类别,让主机能够加载相应的驱动程序
    __u8  bFunctionSubClass;  // 功能的子类
    __u8  bFunctionProtocol;  // 定义该功能的协议
    __u8  iFunction;          // 接口关联描述符的字符串描述符索引
} __attribute__ ((packed));

1.4 端点描述符

struct usb_endpoint_descriptor {
    __u8  bLength;            // 端点描述符的长度
    __u8  bDescriptorType;    // 描述符的类型, 固定值 USB_DT_ENDPOIN 0x05

    __u8  bEndpointAddress;   // 端点地址, bit0-3 表示端点号, bit7表示方向, 1 输入, 0 输出
    __u8  bmAttributes;       // 端点的传输类型 bit0-1: 00, 控制, 01 同步, 02 批量, 03终端.
    __le16 wMaxPacketSize;    // 端点, 收发的最大包大小
    __u8  bInterval;          // 表示主机查询端点的时间间隔,以帧为单位

    __u8  bRefresh;           // 刷新频率,用于音频同步端点, 仅音频使用
    __u8  bSynchAddress;      // 指定同步端点的关联端点地址, 仅音频使用
} __attribute__ ((packed));

其中 bmAttributes的详细说明如下

位位置描述含义
位 0-1传输类型00控制传输(Control)
01同步传输(Isochronous)
10批量传输(Bulk)
11中断传输(Interrupt)
位 2-3同步类型(同步传输时有效)00无同步
01异步
10自适应
11同步
位 4-5使用类型(同步传输时有效)00数据端点
01反馈端点
10隐式反馈数据端点
11保留
位 6-7保留位

    Linux 内核中的 USB Core 是 USB 驱动子系统的核心部分,负责管理整个 USB 子系统的初始化、配置、驱动程序的加载, 它包括 usb 设备驱动管理, USB 主机控制器驱动(Host Controller Driver, HCD) 、usb hub、USB Gadget、 urb数据传输、USB 设备驱动程序等服务.

1.5 usb_hub_descriptor

    hub 描述符用来描述 usb hub

struct usb_hub_descriptor {
    __u8  bDescLength;            // 描述符长度
    __u8  bDescriptorType;        // 描述符类型
    __u8  bNbrPorts;              // 端口数量
    __le16 wHubCharacteristics;   // hub 特性
    __u8  bPwrOn2PwrGood;         // 上电到正常供电的时间
    __u8  bHubContrCurrent;       // hub 控制电流

    /* 2.0和3.0 hub 在这里有所不同 */
    union {
        struct {
            /* 增加1位用于 hub 状态变化;按字节对齐 */
            __u8  DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];  // 可移除设备标志
            __u8  PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];  // 端口电源控制掩码
        }  __attribute__ ((packed)) hs;  // 高速 hub 描述

        struct {
            __u8 bHubHdrDecLat;            // hub 头解码延迟
            __le16 wHubDelay;              // hub 延迟
            __le16 DeviceRemovable;        // 可移除设备的位掩码
        }  __attribute__ ((packed)) ss;  // 超级速度 hub 描述
    } u;
} __attribute__ ((packed));  // 按字节对齐

1.5 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]; // 接口描述符缓冲区, 该配置下的接口描述符保存在 intf_cache 中

    unsigned char *extra;
    int extralen;
};

1.6 usb_interface_cache

    用来管理当前配置描述符下所有的接口信息, 使用了柔性数组的技巧, 在 altsetting 数组中保存管理接口描述符的结构体 usb_host_interface

struct usb_interface_cache {
    unsigned num_altsetting; // altsetting 的数量, 等于接口的数量
    struct kref ref;

    struct usb_host_interface altsetting[0]; // 存放管理接口描述符的数据结构

1.7 usb_host_interface

    用来管理单独的接口信息.

struct usb_host_interface {
    struct usb_interface_descriptor desc; // 接口描述符

    int extralen;
    unsigned char *extra;
 
    struct usb_host_endpoint *endpoint;  // 保存管理端点描述符的数组

    char *string;  
};

1.8 usb_host_endpoint

    用来管理单独的接口描述符.

struct usb_host_endpoint {
    struct usb_endpoint_descriptor      desc; // 接口描述符
    struct usb_ss_ep_comp_descriptor    ss_ep_comp;
    struct list_head        urb_list;
    void                *hcpriv;
    struct ep_device        *ep_dev;

    unsigned char *extra;
    int extralen;
    int enabled;
    int streams;
};

2. 描述符的构建

usb core 中通过函数 usb_get_configuration来构建描述符信息.

  1. 检查配置描述符 dev->descriptor.bNumConfigurations 的数量是否合理, 配置描述符只能 1-7 最多 8 个最少 1 个.
  2. 首先通过函数 usb_get_descriptor 获取配置描述符的信息, 以及该配置中所有相关描述符的总长度 desc->wTotalLength.
  3. 第二次通过第一次获取到的 desc->wTotalLength 长度, 再次通过 usb_get_descriptor 获取到所有的配置描述符, 保存到 dev->rawdescriptors[cfgno] 中, 这里面包含所有的描述符, 被称为原始描述符.
  4. 调用 usb_parse_configuration 解析原始描述符, 解析完之后就会构建出完整的设备描述符信息, 注意这里只构建描述符信息, 并不会创建对应的接口设备. 代码太长就不贴了, 解析完之后的数据如下图所示
    请添加图片描述
  • 补充说明: 内核中设备描述符的构建是自动的, 第一种是 root hub, root hub 本身也是一个 usb 设备, 他是在 root hub 创建的时候自动调用时调用 usb_get_configuration 构建的描述符. 第二种是连接在 hub 上的设备是在设备插入的时候由 hub 调用 usb_get_configuration 构建描述符. 后文叙述这个过程.

二、usb 设备驱动管理

    usb core中的设备和驱动都有两种. 分别是usb 设备 usb_device、usb 设备驱动 usb_device_driver, usb 接口设备 usb_interface、usb 驱动 usb_driver.

  • Linux 内核中 USB 驱动设计采用了 usb_device 和 usb_interface 的分离设计,以便更好地支持复合设备和多功能设备的管理
  • usb_device 表示物理上的整个 USB 设备,负责设备的基本信息和底层通信
  • usb_interface 则代表设备中的具体功能单元,便于系统为每个接口加载独立的驱动
  • usb_device 对应的驱动为 usb_device_driver, 对应的 probe 接口为 usb_probe_device, 对应的设备类型为 usb_device_type
  • usb_interface 对应的驱动为 usb_driver, 对应的 probe 接口为 usb_probe_interface, 对应的设备类型为 usb_if_device_type
  • 他们在内核中通过设备驱动模型由 usb 总线管理, 如下图所示.

在这里插入图片描述

1. 设备和驱动类型

1.1 usb_device

    usb 设备它由 struct usb_device描述,代表的是整个物理 USB 设备.它包含该 USB 设备的所有信息,包括设备描述符、配置描述符、地址等。

struct usb_device {
    int devnum;                                  // 设备编号
    char devpath[16];                            // 设备路径
    u32 route;                                   // 设备路由
    enum usb_device_state state;                 // 设备状态
    enum usb_device_speed speed;                 // 设备速度
    ......

    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;                    // 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;                       // 用于保存配置描述符的原始数据
    ......
};
#define to_usb_device(d) container_of(d, struct usb_device, dev)

1.2 usb 设备分配

    接口 usb_alloc_dev 用于分配 usb_device. 主要的内容如下

  1. 分配 usb_device 结构体的内存空间,并为相关字段做初始化
  2. 设置总线类型 (dev->dev.bus = &usb_bus_type) 和设备类型 (dev->dev.type = &usb_device_type),表示这是一个 USB 设备。
  3. 为设备分配默认的属性节点、DMA 掩码和设备节点信 初始化端点 0 的描述符,并将其添加到端点输出 dev->ep_out[0] 数组和输入 dev->ep_in[0] 数组中.
  4. 根据是否有父设备(parent)来处理不同的设备路径和路由, 若设备无父设备,则设置为 root hub,若设备有父设备,则基于父设备的路径生成子设备的路径
  5. 如果启用了电源管理配置(CONFIG_PM),则设置设备的自动挂起延迟时间, 如果设备为root hub,则直接设置为授权;否则,授权状态由控制器的默认授权配置决定

1.3 usb_device_driver

    内核为 usb_device 提供专用的驱动 usb 设备驱动 usb_device_driver, 其中 usb_device_driver->drvwrap->for_devices标志位用来区分当前的 driver 是 usb_device_driver还是 usb_driver.为 1 表示 usb_device_driver,为 0 表示 usb_driver.

struct usb_device_driver {
    const char *name;

    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);
    struct usbdrv_wrap drvwrap; // 设备模型相关接口
    unsigned int supports_autosuspend:1;
};

struct usbdrv_wrap {
    struct device_driver driver;  // 注册进入 usb 总线
    int for_devices;              // match 的时候用来区分是 usb_device_driver 还是 usb_driver. 为 1 表示 usb_device_driver
};

    驱动的注册接口为 usb_register_device_driver

int usb_register_device_driver(struct usb_device_driver *new_udriver, struct module *owner)
{
    int retval = 0;

    if (usb_disabled())
        return -ENODEV;

    new_udriver->drvwrap.for_devices = 1; // 设置为 1 表示当前驱动是 usb_device_driver
    new_udriver->drvwrap.driver.name = new_udriver->name;
    new_udriver->drvwrap.driver.bus = &usb_bus_type;  // 设置总线
    new_udriver->drvwrap.driver.probe = usb_probe_device; // 设置 probe
    new_udriver->drvwrap.driver.remove = usb_unbind_device;
    new_udriver->drvwrap.driver.owner = owner;

    // 注册驱动到总线
    retval = driver_register(&new_udriver->drvwrap.driver);

    if (!retval)
        pr_info("%s: registered new device driver %s\n",
            usbcore_name, new_udriver->name);
    else
        printk(KERN_ERR "%s: error %d registering device "
            "   driver %s\n",
            usbcore_name, retval, new_udriver->name);

    return retval;
}
EXPORT_SYMBOL_GPL(usb_register_device_driver);

1.4 USB 接口设备

    USB 接口设备 usb_interface 是 usb_device 中的一个功能单元,代表设备中的某个具体功能接口. 一个 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;              /* sysfs 属性是否已创建 */
    unsigned ep_devs_created:1;                  /* 端点“设备”是否已创建 */
    unsigned unregistering:1;                    /* 是否正在注销 */
    unsigned needs_remote_wakeup:1;              /* 驱动程序是否需要远程唤醒 */
    unsigned needs_altsetting0:1;                /* 是否需要切换到备用设置 0 */
    unsigned needs_binding:1;                    /* 是否需要延迟解绑/重绑 */
    unsigned resetting_device:1;                 /* 设备重置后是否需要带宽分配 */
    unsigned authorized:1;                       /* 用于接口授权 */

    struct device dev;
    struct device *usb_dev;                      /* 指向所属 USB 设备的指针 */
    atomic_t pm_usage_cnt;                       /* 用于自动挂起的使用计数器 */
    struct work_struct reset_ws;                 /* 用于原子上下文中的重置操作 */
};

#define to_usb_interface(d) container_of(d, struct usb_interface, dev)

    同样 usb core也提供了对应的驱动 usb_driver.

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

    const struct usb_device_id *id_table; // 匹配规则

    struct usb_dynids dynids;
    struct usbdrv_wrap drvwrap; // 设备模型接口
    unsigned int no_dynamic_id:1;
    unsigned int supports_autosuspend:1;
    unsigned int disable_hub_initiated_lpm:1;
    unsigned int soft_unbind:1;
};
#define to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver)

struct usbdrv_wrap {
    struct device_driver driver;  // 注册进入 usb 总线
    int for_devices;              // match 的时候用来区分是 usb_device_driver 还是 usb_driver. 为 1 表示 usb_device_driver
};

    对应的注册接口

int usb_register_driver(struct usb_driver *new_driver, struct module *owner, const char *mod_name)
{
    int retval = 0;

    if (usb_disabled())
        return -ENODEV;

    new_driver->drvwrap.for_devices = 0; // 0 表示当前驱动为 usb_driver
    new_driver->drvwrap.driver.name = new_driver->name;
    new_driver->drvwrap.driver.bus = &usb_bus_type; // 设置总线
    new_driver->drvwrap.driver.probe = usb_probe_interface; // 设置 probe 接口
    new_driver->drvwrap.driver.remove = usb_unbind_interface;
    new_driver->drvwrap.driver.owner = owner;
    new_driver->drvwrap.driver.mod_name = mod_name;
    spin_lock_init(&new_driver->dynids.lock);
    INIT_LIST_HEAD(&new_driver->dynids.list);

    // 注册驱动
    retval = driver_register(&new_driver->drvwrap.driver);
    if (retval)
        goto out;

    retval = usb_create_newid_files(new_driver);
    if (retval)
        goto out_newid;

    pr_info("%s: registered new interface driver %s\n",
            usbcore_name, new_driver->name);

out:
    return retval;

out_newid:
    driver_unregister(&new_driver->drvwrap.driver);

    printk(KERN_ERR "%s: error %d registering interface "
            "   driver %s\n",
            usbcore_name, retval, new_driver->name);
    goto out;
}
EXPORT_SYMBOL_GPL(usb_register_driver);

2. 总线

2.1 usb_bus_type

    它在 usb core 中用于管理维护usb 设备 usb_device、usb 设备驱动 usb_device_driver 、usb 接口设备 usb_interface 、usb 驱动 usb_driver 以及他们的匹配.

struct bus_type usb_bus_type = {
    .name =     "usb",
    .match =    usb_device_match,
    .uevent =   usb_uevent,
};

    它由系统在开机的时候默认默认创建.

// drivers/usb/core/usb.c
static int __init usb_init(void)
{
    retval = bus_register(&usb_bus_type);
}

subsys_initcall(usb_init);

    设备驱动的匹配规则由 usb_bus_type提供的 usb_device_match函数决定.

static int usb_device_match(struct device *dev, struct device_driver *drv)
{
    
    if (is_usb_device(dev)) { // 判断设备是不是 usb_device

        // 判断驱动是不是 usb_device_driver, 不是直接返回
        if (!is_usb_device_driver(drv))
            return 0;

        return 1; // 如果设备是 usb_device 且驱动是 usb_device_driver 则直接匹配成功

    } else if (is_usb_interface(dev)) { // 判断设备是不是 usb_interface
        struct usb_interface *intf;
        struct usb_driver *usb_drv;
        const struct usb_device_id *id;

        // 判断驱动是不是 usb_device_driver, 如果是直接返回
        if (is_usb_device_driver(drv))
            return 0;

        // 获取 usb_interface 设备结构
        intf = to_usb_interface(dev);
        // 获取 usb_driver 驱动结构
        usb_drv = to_usb_driver(drv);

        // 和 usb_drv->id_table 进行匹配
        // intf->usb_dev 和 usb_drv->id_table 中设置的 match_flags 匹配条件进行匹配
        // intf->cur_altsetting 和 usb_drv->id_table 中设置的 match_flags 匹配条件
        id = usb_match_id(intf, usb_drv->id_table);
        if (id)
            return 1;
        
		// intf->usb_dev 和 usb_drv->dynids 中链接的 dynid->id 设置的 match_flags 匹配条件进行匹配
        // intf->cur_altsetting 和 usb_drv->dynids 中链接的 dynid->id 设置的 match_flags 匹配条件进行匹配
        id = usb_match_dynamic_id(intf, usb_drv);
        if (id)
            return 1;
    }

    return 0;
}

2.2 匹配规则

  • 设备 usb_device 且驱动是 usb_device_driver 则直接匹配成功

  • 接口设备 usb_interface 的匹配则有两次匹配机会, 首先是和 usb_drv->id_table 进行匹配, 然后是和 usb_drv->dynids 中链接的 dynid->id 进行匹配, 匹配规则如下.

    1. 匹配 usb_interface 所属的 usb_device 和 usb_drv->id_table 中设置的 match_flags 匹配条件进行匹配, 匹配成功直接返回
    2. 第一种匹配失败之后, 匹配 usb_interface 当前使用的接口描述符 cur_altsetting 和 usb_drv->id_table 中设置的 match_flags 匹配条件进行匹配
  • 匹配规则如下所

struct usb_device_id {
    
    __u16       match_flags;         // 定义匹配规则的标志位,用于标识哪些字段需要匹配
    
// 匹配标志位的宏定义
// 通过这些宏定义来设置 match_flags,指定需要匹配的条件。
#define USB_DEVICE_ID_MATCH_VENDOR          0x0001  // 匹配厂商 ID(idVendor)
#define USB_DEVICE_ID_MATCH_PRODUCT         0x0002  // 匹配产品 ID(idProduct)
#define USB_DEVICE_ID_MATCH_DEV_LO          0x0004  // 匹配最低设备版本(bcdDevice_lo)
#define USB_DEVICE_ID_MATCH_DEV_HI          0x0008  // 匹配最高设备版本(bcdDevice_hi)
#define USB_DEVICE_ID_MATCH_DEV_CLASS       0x0010  // 匹配设备类(bDeviceClass)
#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS    0x0020  // 匹配设备子类(bDeviceSubClass)
#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL    0x0040  // 匹配设备协议(bDeviceProtocol)
#define USB_DEVICE_ID_MATCH_INT_CLASS       0x0080  // 匹配接口类(bInterfaceClass)
#define USB_DEVICE_ID_MATCH_INT_SUBCLASS    0x0100  // 匹配接口子类(bInterfaceSubClass)
#define USB_DEVICE_ID_MATCH_INT_PROTOCOL    0x0200  // 匹配接口协议(bInterfaceProtocol)
#define USB_DEVICE_ID_MATCH_INT_NUMBER      0x0400  // 匹配接口编号(bInterfaceNumber)
    
    __u16       idVendor;            // USB 设备的厂商 ID,用于匹配特定厂商的设备
    __u16       idProduct;           // USB 设备的产品 ID,用于匹配厂商的特定产品
    __u16       bcdDevice_lo;        // USB 设备的最低版本号,用于匹配特定的设备版本范围
    __u16       bcdDevice_hi;        // USB 设备的最高版本号,用于匹配特定的设备版本范围

    __u8        bDeviceClass;        // 设备类,用于匹配特定类型的设备(例如:存储设备、通信设备等)
    __u8        bDeviceSubClass;     // 设备子类,用于更细粒度地匹配设备类型
    __u8        bDeviceProtocol;     // 设备协议,用于匹配特定的设备协议类型

    __u8        bInterfaceClass;     // 接口类,用于匹配特定的接口类型
    __u8        bInterfaceSubClass;  // 接口子类,用于更细粒度地匹配接口类型
    __u8        bInterfaceProtocol;  // 接口协议,用于匹配特定的接口协议

    __u8        bInterfaceNumber;    // 接口编号,用于匹配特定的接口号

    kernel_ulong_t  driver_info      // 不参与匹配,用于存储驱动的私有信息
        __attribute__((aligned(sizeof(kernel_ulong_t))));
};

3. 创建接口设备

    usb 接口设备(usb_interface) 描述的是具体的接口, 依赖于 usb 设备(usb_device). 从前面的匹配规则可以知道, 设备 usb_device 且驱动是 usb_device_driver 则直接匹配成功. 利用这个规则内核提供了一个 usb_generic_driver专门用来创建 usb 设备(usb_device) 下的 usb 接口设备(usb_interface).

struct usb_device_driver usb_generic_driver = {
    .name = "usb",
    .probe = generic_probe, // 内核中只要注册 usb 设备(usb_device) 都会调用这个接口.
    .disconnect = generic_disconnect,
#ifdef  CONFIG_PM
    .suspend = generic_suspend,
    .resume = generic_resume,
#endif
    .supports_autosuspend = 1,
};

    内核中只要注册 usb 设备(usb_device) 都会调用 generic_probe如下图所示.

在这里插入图片描述

    然后在 generic_probe中调用 usb_set_configuration为该设备创建其所属的 usb 接口设备(usb_interface) .然后注册 usb_interface 到 usb_bus_type 同时设置设备类型为 usb_if_device_type.

static int generic_probe(struct usb_device *udev)
{
    int err, c;

    if (udev->authorized == 0)
        dev_err(&udev->dev, "Device is not authorized for usage\n");
    else {
        // 返回对应的配置描述符索引, 一般情况一个设备一个配置描述符.
        c = usb_choose_configuration(udev);
        if (c >= 0) {
            // 1. 检测设备是否被授权了, 如果授权了则获取 configuration 对应的 usb_host_config
            // 2. 根据接口描述符的数量为每一个接口创建 usb 接口设备 usb_interface 并初始化接口设备
            // 3. 增加电源的引用计数, 唤醒设备, 如果设备未分配地址则关闭设备, 更新设备的带宽
            // 4. 向设备发送 SET_CONFIGURATION 设置设备处于默认的配置状态
            // 5. 检查设备是否可以被挂起, 如果可以就进入休眠.
            // 6. 注册 usb_interface 到 usb_bus_type 并设置设备类型为 usb_if_device_type
            err = usb_set_configuration(udev, c);
            if (err && err != -ENODEV) {
                dev_err(&udev->dev, "can't set config #%d, error %d\n",
                    c, err);
            }
        }
    }

    // 调用内核通知链通知设备的增加
    usb_notify_add_device(udev);

    return 0;
}

    接口设备创建流程如下所示.

在这里插入图片描述

此时我们已经可以大致总结出 usb 设备的枚举流程,

  1. 识别到设备之后, 调用 usb_new_deivce 解析设备的描述符信息, 创建设备对应的 usb_device
  2. 然后会调用 usb core 提供的 generic_probe 函数, 在这里为设备的每一个接口创建接口设备 usb_interface

但是还是有以下疑问, 设备在哪里被检测? 如何触发检测等? 等这些问题, 这里可以先给出结论, 在hub中被检查, 详细参考后文 hub 章节相关内容.

三、usb 通讯

    usb core 中为 usb 通讯提供了统一的接口 urb. URB 用于描述一个 USB 请求,它封装了与 USB 设备通信的所有必要信息,包括数据传输、控制命令等。通过 URB, 驱动程序可以发送和接收数据以及执行各种操作。每一次传输请求都需要通过 urb 进行, 每个 urb 都是独立的. 并且可以多次重复使用. urb 的数据结构如下所示.

struct urb {
    struct kref kref;                  /* URB 的引用计数 */
    void *hcpriv;                      /* 主机控制器的私有数据 */
    atomic_t use_count;                /* 并发提交计数器 */
    atomic_t reject;                   /* 提交是否会失败的标志 */
    int unlinked;

    /* 驱动程序可用的公共字段 */
    struct list_head urb_list;         /* URB 当前所有者的链表头 */
    struct list_head anchor_list;
    struct usb_anchor *anchor;
    struct usb_device *dev;            /* 关联的 USB 设备指针 */
    struct usb_host_endpoint *ep;      /* 关联的端点指针*/
    unsigned int pipe;                 /* 管道信息 */
    unsigned int stream_id;            /* 流 ID */
    int status;                        /* 返回状态(非等时传输使用) */
    unsigned int transfer_flags;       /* 传输标志,例如 URB_SHORT_NOT_OK 等 */
    void *transfer_buffer;             /* 数据缓冲区指针 */
    dma_addr_t transfer_dma;           /* 数据缓冲区的 DMA 地址 */
    struct scatterlist *sg;            
    int num_mapped_sgs;                
    int num_sgs;                       
    u32 transfer_buffer_length;        /* 数据缓冲区长度 */
    u32 actual_length;                 /* 实际传输的数据长度 */
    unsigned char *setup_packet;       /* 控制传输的 SETUP 包 */
    dma_addr_t setup_dma;              /* 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[0]; /* 等时传输的包描述符数组 */
};

urb 的使用分为三步, 创建 urb, 填充 urb, 提交 urb.

1. 创建 urb

urb 的创建比较简单调用 usb_alloc_urb 即可创建一个 urb.

struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);

2. 填充 urb

根据传输类型的不同内核提供了不同的填充接口

2.1 控制传输

    主要用于获取设备描述符(如 USB_REQ_GET_DESCRIPTOR 请求), 设置设备配置(如 USB_REQ_SET_CONFIGURATION 请求), 与 USB 设备的控制端点通信等场景.

static inline void usb_fill_control_urb(
    struct urb *urb,               /* 指向分配的 URB 结构,用于封装控制传输的信息 */
    struct usb_device *dev,        /* 发起请求的 USB 设备 */
    unsigned int pipe,             /* 指定传输管道,使用 usb_sndctrlpipe 或 usb_rcvctrlpipe 生成 */
    unsigned char *setup_packet,   /* 指向控制传输的 SETUP 包,定义请求的类型和参数 */
    void *transfer_buffer,         /* 数据缓冲区,用于存放传输数据 */
    int buffer_length,             /* 数据缓冲区的长度 */
    usb_complete_t complete_fn,    /* 传输完成后的回调函数 */
    void *context                  /* 传递给回调函数的上下文数据 */
);

2.2 批量传输

    用于大容量数据传输,如从 USB 存储设备读取数据或向其写入数据. 打印机通信(如文件传输到 USB 打印机). 适合对数据速率要求较高、可靠性优先的设备

static inline void usb_fill_bulk_urb(
    struct urb *urb,               /* 指向分配的 URB 结构,用于封装批量传输的信息 */
    struct usb_device *dev,        /* 发起请求的 USB 设备 */
    unsigned int pipe,             /* 指定传输管道,使用 usb_sndbulkpipe 或 usb_rcvbulkpipe 生成 */
    void *transfer_buffer,         /* 数据缓冲区,用于存放传输数据 */
    int buffer_length,             /* 数据缓冲区的长度 */
    usb_complete_t complete_fn,    /* 传输完成后的回调函数 */
    void *context                  /* 传递给回调函数的上下文数据 */
);

2.3 中断传输

    用于键盘、鼠标等低延迟输入设备的数据传输. 游戏控制器的按键输入与状态反馈. 需要周期性短数据通信的场景.

static inline void usb_fill_int_urb(
    struct urb *urb,               /* 指向分配的 URB 结构,用于封装中断传输的信息 */
    struct usb_device *dev,        /* 发起请求的 USB 设备 */
    unsigned int pipe,             /* 指定传输管道,使用 usb_sndintpipe 或 usb_rcvintpipe 生成 */
    void *transfer_buffer,         /* 数据缓冲区,用于存放传输数据 */
    int buffer_length,             /* 数据缓冲区的长度 */
    usb_complete_t complete_fn,    /* 传输完成后的回调函数 */
    void *context,                 /* 传递给回调函数的上下文数据 */
    int interval                   /* 中断传输的时间间隔(单位为帧,1 ms 或 125 μs) */
);

2.4 同步传输

    同步传输并没有专门的接口需要手动填充, 用于实时性高的音频设备, 视屏流设备的数据传输.

struct urb *urb = usb_alloc_urb(num_packets, GFP_KERNEL);
urb->dev = dev;                                   /* 发起请求的 USB 设备 */
urb->pipe = usb_sndisocpipe(dev, endpoint);       /* 设置管道 */
urb->transfer_buffer = transfer_buffer;           /* 数据缓冲区 */
urb->transfer_buffer_length = buffer_length;      /* 数据缓冲区长度 */
urb->number_of_packets = num_packets;             /* 要传输的包数量 */
urb->interval = interval;                         /* 时间间隔 */
urb->complete = complete_fn;                      /* 完成回调函数 */
urb->context = context;                           /* 自定义上下文 */
for (int i = 0; i < num_packets; i++) {
    urb->iso_frame_desc[i].offset = i * packet_size; /* 每个包的偏移量 */
    urb->iso_frame_desc[i].length = packet_size;     /* 每个包的长度 */
}

3. 提交 urb

接口 usb_submit_urb 用于提交 urb . 函数原型如下.

int usb_submit_urb(struct urb *urb, gfp_t mem_flags);

提交流程如下图所示.

在这里插入图片描述

4. pipe

    urb 传输过程中的参数, 它包含传输需要的, 传输类型, 端点号, 设备号, 方向标志. 也就是 pipe 就决定了传输的类型方向以及端点. 如下所示

位范围位表示内容描述示例值
31–30PIPE_*(传输类型)指定传输类型:控制、同步、批量、中断。00(控制)、01(同步)、10(批量)、11(中断)。
29–16endpoint(端点号)USB 端点号,用于标识设备上的端点。0x01(端点 1)
15–8dev->devnum(设备号)USB 设备号,用于标识具体的连接设备。0x02(设备号 2) ,不能超过 128 个
7USB_DIR_IN(方向标志)指定传输方向:OUT 或 IN。1(IN,设备到主机)、0(OUT,主机到设备)
6–0保留通常未使用,可能用于协议扩展。

    内核也提供了创建的接口如下

static inline unsigned int __create_pipe(struct usb_device *dev,
        unsigned int endpoint)
{
    return (dev->devnum << 8) | (endpoint << 15); /* 生成基础 pipe:设备号和端点号的位拼接 */
}

/* 发送控制类型的 pipe(Control OUT) */
#define usb_sndctrlpipe(dev, endpoint)  \
    ((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint))

/* 接收控制类型的 pipe(Control IN) */
#define usb_rcvctrlpipe(dev, endpoint)  \
    ((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)

/* 发送同步类型的 pipe(Isochronous OUT) */
#define usb_sndisocpipe(dev, endpoint)  \
    ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint))

/* 接收同步类型的 pipe(Isochronous IN) */
#define usb_rcvisocpipe(dev, endpoint)  \
    ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)

/* 发送批量传输类型的 pipe(Bulk OUT) */
#define usb_sndbulkpipe(dev, endpoint)  \
    ((PIPE_BULK << 30) | __create_pipe(dev, endpoint))

/* 接收批量传输类型的 pipe(Bulk IN) */
#define usb_rcvbulkpipe(dev, endpoint)  \
    ((PIPE_BULK << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)

/* 发送中断传输类型的 pipe(Interrupt OUT) */
#define usb_sndintpipe(dev, endpoint)   \
    ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint))

/* 接收中断传输类型的 pipe(Interrupt IN) */
#define usb_rcvintpipe(dev, endpoint)   \
    ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)

四、 hcd

    HCD (Host Controller Driver) 是在 USB 主机控制器驱动, 它主要包括 usb_hcd、root_hub 两个部分.它们分别负责不同的任务.

    usb_hcd 主要用于管理和维护 USB 控制器的硬件资源,包括 PHY (物理层接口)、IRQ (中断) 等硬件相关的部分。这一部分主要与底层硬件打交道,确保 USB 控制器的正常工作。它通过控制器的寄存器操作和物理层接口来实现 USB 通信,并管理中断处理流程

    root_hub 是 USB 控制器的一部分, 它由 usb 控制器模拟出来,作为位于拓扑结构顶部的 hub, 与普通的物理 hub 在功能上类似. 在内核中 root hub 和 hub 均用 usb_device 来描述,它们的主要职责如下:

  • hub 的功能: 负责 usb 设备的连接和枚举,管理端口的电源状态和数据传输,并支持多层级 usb 拓扑结构的扩展。
  • root_hub 的特殊性: 作为控制器的逻辑扩展, root hub 不是真正的物理设备, 但功能上完全模拟了普通 hub 的行为。它位于 usb 层次结构的顶端, 与主机控制器直接通信, 所有 usb 设备连接的起点.

在这里插入图片描述

    一个 usb host 最多可以同时支持 128 个地址, 地址 0 作为默认地址,只在设备枚举期间临时使用,而不能被分配给任何一个设备,因此一个 usb host 最多可以同时支持 127 个地址.

1. 数据结构

1.1 usb_hcd

    用来描述 usb 控制器的信息

struct usb_hcd {
    struct usb_bus self;                   /* hcd 是一个总线 */
    struct kref kref;                      /* 引用计数器 */
    const char *product_desc;              
    int speed;                             /* roothu 的速度,可能与 hcd->driver->flags & HCD_MASK 不同 */
    char irq_descr[24];                    /* 驱动 + 总线编号 */
    struct timer_list rh_timer;            /* root hub 轮询 */
    struct urb *status_urb;                /* 当前状态 urb */
#ifdef CONFIG_PM
    struct work_struct wakeup_work;        /* 用于远程唤醒 */
#endif

    /* 硬件信息/状态 */
    const struct hc_driver *driver;        /* 硬件相关的回调函数 */

    struct usb_phy *usb_phy;              /* 需要设置 CONFIG_USB_PHY , 在内核中调用 usb_add_phy_dev 添加的 phy */ 
    struct phy *phy;                      /* 需要设置 CONFIG_GENERIC_PHY, 通过设备树解析的 phy */
	
    ......
        
    unsigned long hcd_priv[0]              /* 驱动私有数据 */
            __attribute__ ((aligned(sizeof(s64))));
};

1.2 usb_bus

    usb_bus 表示 usb 控制其的一部分, 用来描述 usb 控制器的物理总线信息.

struct usb_bus {
    struct device *controller;                     // USB控制器设备指针
    struct device *sysdev;                         // 系统设备指针
    int busnum;                                    // USB总线编号,注册HCD时分配,通常为64
    const char *bus_name;                          // USB总线名称
    u8 uses_dma;                                   // 是否支持 DMA 传输
    u8 uses_pio_for_control;                       // 是否使用 PIO 模式进行控制传输
    
    u8 otg_port;                                   // OTG 端口号
    unsigned is_b_host:1;                          // 是否为 B 型主机
    unsigned b_hnp_enable:1;                       // 是否启用 B 型主机协商协议
    unsigned no_stop_on_short:1;                   // 短包时是否不停止传输
    
    unsigned no_sg_constraint:1;                   
    unsigned sg_tablesize;                         
    int devnum_next;                               // 下一个可用的设备号
    struct mutex devnum_next_mutex;                // 设备号分配互斥锁
    struct usb_devmap devmap;                      // USB设备地址映射表
    struct usb_device *root_hub;                   // roo_hub
	......
};

1.3 usb_hub

    usb_hub 用来描述 hub 和 hub 的接口状态.

struct usb_hub {
    struct device       *intfdev;    // 该 hub 对应的接口设备
    struct usb_device   *hdev;       // usb 设备结构, 用来描述这个 hub
    struct kref     kref;            // 
    struct urb      *urb;            // 通讯的 urb

    u8          (*buffer)[8];
    union {
        struct usb_hub_status   hub;  // hub 的状态
        struct usb_port_status  port; // hub 的接口状态
    }           *status;
    struct mutex        status_mutex;

    int         error; 
    int         nerrors; 

    ......

    struct work_struct      events;
    struct usb_port     **ports;
};

2. 注册 hcd

    注册 hcd 主要分为两步, 首先调用 usb_create_hcd 创建 usb_hcd 结构, 然后调用 usb_add_hcd 注册.

// drivers/usb/dwc2_new/hcd.c
int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
{
    struct platform_device *pdev = to_platform_device(hsotg->dev);
    struct resource *res;
    struct usb_hcd *hcd;
    struct dwc2_host_chan *channel;
    u32 hcfg;
    int i, num_channels;
    int retval;

    ......

    // 创建 usb_hcd
    hcd = usb_create_hcd(&dwc2_hc_driver, hsotg->dev, dev_name(hsotg->dev));
 
    ......

    // 注册 usb_hcd
    retval = usb_add_hcd(hcd, hsotg->irq, IRQF_SHARED);

    ......
}

2.1 usb_create_hcd

    usb_create_hcd 函数的主要功能是创建并初始化一个 usb_hcd, 该函数的功能分为几个主要步骤:

  1. 分配和初始化 usb_hcd 结构体, 分配内存空间,如果失败则返回 NULL
  2. 为 address0_mutex 和 bandwidth_mutex 锁分配和初始化
  3. 初始化 usb 逻辑总线 usb_bus, 设置总线名称、控制器和 DMA 支持等
  4. 设置定时器, 初始化定时器并配置回调函数 rh_timer_func. 这个定时器是hub用来监听是否有新的 usb 插入用的, 老式的 ehci , xhci 等usb 控制器使用这个方式.而文章中的 dwc2 以及后面的 dwc3 都采用中断的方式检查是否有新的设备插入.
  5. 电源管理支持, 如果启用了电源管理,初始化恢复工作队列
  6. 设置驱动和描述信息, 配置驱动、速度标志和产品描述信息
// drivers/usb/core/hcd.c
struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
        struct device *dev, const char *bus_name)
{
    return usb_create_shared_hcd(driver, dev, bus_name, NULL);
}

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

    // 分配一个 hcd
    hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);
    if (!hcd) {
        dev_dbg (dev, "hcd alloc failed\n");
        return NULL;
    }
    
    // 如果 primary_hcd 为空则分配相关锁, 否则继承 primary_hcd 的锁.
    if (primary_hcd == NULL) {
        hcd->address0_mutex = kmalloc(sizeof(*hcd->address0_mutex),
                GFP_KERNEL);
        if (!hcd->address0_mutex) {
            kfree(hcd);
            dev_dbg(dev, "hcd address0 mutex alloc failed\n");
            return NULL;
        }
        mutex_init(hcd->address0_mutex);
        hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex),
                GFP_KERNEL);
        if (!hcd->bandwidth_mutex) {
            kfree(hcd->address0_mutex);
            kfree(hcd);
            dev_dbg(dev, "hcd bandwidth mutex alloc failed\n");
            return NULL;
        }
        mutex_init(hcd->bandwidth_mutex);
        dev_set_drvdata(dev, hcd);
    } else {
        mutex_lock(&usb_port_peer_mutex);
        hcd->address0_mutex = primary_hcd->address0_mutex;
        hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex;
        hcd->primary_hcd = primary_hcd;
        primary_hcd->primary_hcd = primary_hcd;
        hcd->shared_hcd = primary_hcd;
        primary_hcd->shared_hcd = hcd;
        mutex_unlock(&usb_port_peer_mutex);
    }

    kref_init(&hcd->kref);

    // 简单初始化 usb_bus
    usb_bus_init(&hcd->self);
    hcd->self.controller = dev;     // 设置 controller 对应的 dev
    hcd->self.bus_name = bus_name;  // 设置 name 
    hcd->self.uses_dma = (dev->dma_mask != NULL); // 是否使用 dma

    init_timer(&hcd->rh_timer);
    hcd->rh_timer.function = rh_timer_func;   // 回调函数
    hcd->rh_timer.data = (unsigned long) hcd; // 设置传入的参数
#ifdef CONFIG_PM
    INIT_WORK(&hcd->wakeup_work, hcd_resume_work); // 支持电源管理则初始化工作队列
#endif

    hcd->driver = driver;                          // 设置回调 driver
    hcd->speed = driver->flags & HCD_MASK;         // 设置速度标志位
    hcd->product_desc = (driver->product_desc) ? driver->product_desc : // 设置描述 hcd 字符串
            "USB Host Controller";
    return hcd;
}
EXPORT_SYMBOL_GPL(usb_create_shared_hcd);

2.2 usb_add_hcd

    注册 hcd 主要包括两个内容, 一个是 usb controler 相关的 phy 等硬件的初始化, 另一个则是 root hub 的注册.如下图所示

请添加图片描述

root hub 是 usb 控制器虚拟出来的 usb_device:

  1. 在 usb_new_device 中调用 usb_get_configuration 从 usb 控制器获取并解析 root hub 设备的描述符信息, 构建描述符拓扑结构. 并将该 udev 注册进内核.
  2. root hub 对应的 udev 注册进内核之后, 会和通用的 usb_generic_driver 匹配, 并调用 generic_probe 函数, 为 root hub 创建对应接口设备 usb_interface 并将其注册进内核.
  3. 然后该接口设备会匹配应的驱动的 prob 函数, 君正用的匹配规则如下所示, 上图也标注出来了.
static const struct usb_device_id hub_id_table[] = {
    { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
      .bDeviceClass = USB_CLASS_HUB},
    { }
}

匹配成功之后会调用 hub_probe 函数.然后做进一步的处理, 如下图所示

请添加图片描述

如图所示整个过程分为3个阶段

2.2.1 hub_probe
  1. 设置 hub 自动休眠时间, 使能自动休眠
  2. 检测 hdev->level 设备的拓扑层级最大不能超过 6 层
  3. 检测 hub 的端点数, hub 有且只能有一个中断输入端点
  4. 初始化 hub_event 工作队列, 分配并初始化 usb_hub 结构, 同时通过 usb_set_intfdata* (intf, hub) 设置接口的私有数据为 usb_hub
2.2.2 hub_configure
  1. 分配更新 hub 描述符, 更新端口数量, root hub 只有一个端口
  2. 更新 hub 的电源切换模式, hub 过流保护相关标志位, 根据 hub 标志位设置 hub 的时延.
  3. 获取 hub 的状态, 更新 hub 的电流设置, 如果电流太小打印异常, 如果是自供电设备也打印日志提示. 如果设置了过流保护也打印日志提示.
  4. 创建一个中断传输的 pipe, 使用这个 pipe 和 hub_irq 函数创建 hub->urb. 使用中断传输调用这个 urb 时最后会调用到 hub_irq.
  5. 根据端口的数量为 hub 接口设备创建并注册端口 usb_port, 存放在 hub->ports[i]中.
  6. 回调 hcd->driver->update_hub_device()
2.2.3 hub_activate
  1. 非唤醒的状态, 才能触发 init1 阶段, 更新电源状态为上电状态.
  2. init2 阶段检查并更新端口状态
  3. init3 阶段调用 usb_submit_urb 提交 hub->urb 最终通过定时器回调 hub_irq 接口, 调用 kick_hub_wq 启动 hub_event

五、usb 设备的识别

    usb 设备的识别根据同的 usb 控制器有不同的实现方式, 但总体上分为两种.

  • 通过定时器的方式, 轮训监听是否有设备的插入., 早期的设备采用这种方式 UHCI 等
  • 通过中断的方式监听, 如果有设备插入触发中断, 启动检测流程. 新的中断控制器 OHCI , EHCI, xHCI, dwc2 等都采用中断的方式.

请添加图片描述

  1. usb 控制器触发中断, 调用 usb_hcd_resume_root_hub 唤醒 usb 系统, 之后调用 usb_resume_both
  2. usb_resume_both 先唤醒 usb 设备, 给 usb 设备上电等相关初始化. 然后唤醒 usb 接口设备触发 usb 的检测, 在 hub_event 中检测到 usb change
  3. 在 usb_new_device 中尝试枚举 usb 设备, 如果枚举成功则注册设备到 usb_bus_type 总线中, 匹配通用驱动 usb_generic_driver 为 usb 设备创建 usb 接口设备
  4. 在 generic_probe 解析设备描述中的接口描述符, 为设备创建接口设备, 并也将其注册进入 usb_bus_type 总线中, 在总线中匹配对应接口的驱动, 这个驱动就是我们所说的 usb 驱动了

注意: 参考前面 usb设备 和 usb接口设备的概念

  • usb 设备: 由 struct usb_device描述,代表的是整个物理 USB 设备.
  • usb 接口设备: 由 usb_interface是 usb_device 中的一个功能单元

    不同厂家的控制器的识别可能略有差别, 但也都是大同小异, rk 使用 extcon 监听usb id 脚的变化, 如果由usb插入则动态调用 usb_add_hcd 注册 usb 控制器, 然后控制器中调用 usb_hcd_resume_root_hub 触发唤醒流程.

标签:usb,driver,struct,hcd,device,驱动,设备
From: https://blog.csdn.net/sty01z/article/details/145033086

相关文章

  • 计算机毕业设计Springboot在线作业管理系统 基于Springboot的在线作业管理平台 Spring
    计算机毕业设计Springboot在线作业管理系统6u09wm4d(配套有源码程序mysql数据库论文)本套源码可以先看具体功能演示视频领取,文末有联xi可分享随着信息技术的迅猛发展,传统的教学方式正逐步向现代化、数字化转型。作业作为教学过程中的重要环节,其管理方式的改革尤为关键。传......
  • Linux驱动开发:一文掌握 块设备VS字符设备开发流程全解!
    Linux驱动开发是嵌入式系统开发中的一个重要组成部分,它直接关系到硬件设备的功能实现和性能优化。在Linux系统中,驱动开发主要分为字符设备驱动、块设备驱动和网络设备驱动三大类。本文将重点介绍字符设备和块设备的基础知识,以及它们在驱动开发中的差异和开发流程。一、字符设......
  • Docker Shrink实战:AI驱动的Docker镜像优化工具
    引言在容器化应用主导的现代开发范式中,有效处理Docker容器是开发者和组织的主要关注点之一。随着项目规模的增长,我们经常会遇到Docker镜像体积过大的问题,这不仅增加了存储成本和构建时间,还可能在生产环境中引发其他问题。DockerShrink应运而生,它是一款利用AI技术来......
  • STC12单片机设置50Hz的PWM波驱动舵机
    STC12单片机设置50Hz的PWM波驱动舵机一、引言在机器人控制、航模制作以及各种自动化设备领域,舵机作为一种关键的执行元件,能够精准地控制角度,实现诸如机械臂关节运动、模型转向等功能。而使用STC12单片机来产生50Hz的PWM波驱动舵机,是一种经济高效且灵活的方案。STC12系列单......
  • 计算机毕业设计Springboot基于vue的购物商城 Vue.js驱动的Springboot购物平台开发 基
    计算机毕业设计Springboot基于vue的购物商城z6fe4u53(配套有源码程序mysql数据库论文)本套源码可以先看具体功能演示视频领取,文末有联xi可分享随着互联网技术的飞速发展,电子商务已经成为人们生活中不可或缺的一部分。一个高效、用户友好的购物商城系统,不仅能够为消费者提......
  • Linux驱动开发:处理空指针错误,ERR_PTR、IS_ERR、PTR_ERR用法
    免责声明:本文内容摘自《Linux设备驱动开发》一书,作者为JohnMadieu,译者为袁鹏飞、刘寿永,由人民邮电出版社出版。本文仅为分享知识和讨论之用,非商业用途。书籍版权归原作者及出版社所有。本人及本博客不对因使用或误用本文内容而产生的任何后果负责。请读者尊重版权,合理使用内容。......
  • Kernel Memory: 强大的AI驱动记忆系统
    KernelMemory简介KernelMemory(简称KM)是由微软开发的一个强大的多模态AI服务,专门用于高效索引和处理大规模数据集。它支持检索增强生成(RAG)、合成记忆、提示工程和自定义语义记忆处理等先进功能,为构建智能应用提供了强大的基础设施。 KM可以作为Web服务、Docker容器、......
  • Windows 蓝牙驱动开发-BLE低功耗
    蓝牙的版本为1.0~5.2版本,蓝牙1.0~3.0都是经典蓝牙(rfcomm)。而蓝牙4.0开始就是包括蓝牙BLE了。蓝牙4.0的蓝牙芯片都是双模的,既包括经典蓝牙也包括低能耗蓝牙。经典蓝牙和蓝牙BLE完全是两个东西,只是它们在底层上有一些相似的地方。蓝牙BLE相比于经典蓝牙的优点是搜索、连接的速......
  • Windows 蓝牙驱动开发-安装蓝牙设备
    蓝牙配置文件驱动程序有两种安装类型:客户端安装,在此类安装中,远程设备播发其服务,并且计算机与之连接。示例包括:鼠标、键盘和打印机;服务器端安装,在此类安装中,计算机播发服务,并且远程设备可以连接到计算机以使用这些服务;例如,供应商可以创作服务器端安装,使移动设备能够打印到附......
  • 深入解析 ipoib_vlan.c:IPoIB 驱动中的 VLAN 管理
    引言在InfiniBand网络中,IPoIB(IPoverInfiniBand)是一种允许传统IP应用程序在InfiniBand网络上运行的协议。ipoib_vlan.c 是Linux内核中IPoIB驱动的一部分,主要负责处理VLAN(虚拟局域网)相关的功能。本文将详细解析该文件的功能、关键函数及其实现逻辑。文件概述ipo......