1.概述

USB Hub提供了连接USB主机和USB设备的电气接口。USB Hub拥有一个上行口,至少一个下行口,上行口连接上一级的Hub的下行口或者USB主机,连接主机的为Root Hub,下行口连接下一级Hub的上行口或者USB设备。经过Hub的扩展,一个USB主机可以和多个USB设备通信。USB Hub有如下特性:

  1. 良好的扩展性
  2. 电源管理
  3. 探测设备连接和断开连接
  4. 总线错误探测和修复
  5. 支持USB1.0、USB1.1、USB2.0、USB3.2设备。

下图是USB Hub的架构图,拥有一个上行口,4个下行口,内部包含了USB3.2 Hub,USB2.0 Hub。USB2.0 Hub支持USB1.0、USB1.1、USB2.0设备,USB3.2 Hub支持USB3.2设备。

2.数据结构及接口

2.1.数据结构

Linux内核中使用struct usb_hub结构体描述USB Hub,同时USB Hub也是一个USB设备,因此struct usb_hub中的hdev指向了描述USB Hub的struct usb_device数据结构。

[drivers/usb/core/hub.h]
struct usb_hub {
struct device *intfdev; /* the "interface" device */
struct usb_device *hdev;
struct kref kref;
struct urb *urb; /* for interrupt polling pipe */ /* buffer for urb ... with extra space in case of babble */
u8 (*buffer)[8];
union {
struct usb_hub_status hub;
struct usb_port_status port;
} *status; /* buffer for status reports */
struct mutex status_mutex; /* for the status buffer */ int error; /* last reported error */
int nerrors; /* track consecutive errors */ unsigned long event_bits[1]; /* status change bitmask */
unsigned long change_bits[1]; /* ports with logical connect
status change */
unsigned long removed_bits[1]; /* ports with a "removed"
device present */
unsigned long wakeup_bits[1]; /* ports that have signaled
remote wakeup */
unsigned long power_bits[1]; /* ports that are powered */
unsigned long child_usage_bits[1]; /* ports powered on for
children */
unsigned long warm_reset_bits[1]; /* ports requesting warm
reset recovery */
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
#error event_bits[] is too short!
#endif struct usb_hub_descriptor *descriptor; /* class descriptor */
struct usb_tt tt; /* Transaction Translator */
......
}; Linux内核中使用`struct usb_port`结构体描述Hub上的port。 ```c
[drivers/usb/core/hub.h]
struct usb_port {
struct usb_device *child; // usb device attached to the port
struct device dev; // generic device interface
struct usb_dev_state *port_owner;
struct usb_port *peer; // related usb2 and usb3 ports (share the same connector)
struct dev_pm_qos_request *req;
enum usb_port_connect_type connect_type;
usb_port_location_t location;
struct mutex status_lock;
u32 over_current_count;
u8 portnum;
u32 quirks;
unsigned int is_superspeed:1;
unsigned int usb3_lpm_u1_permit:1;
unsigned int usb3_lpm_u2_permit:1;
};

Hub有专门的描述符,使用struct usb_hub_descriptor描述。

[include/uapi/linux/usb/ch11.h]
/*
* Hub descriptor
* See USB 2.0 spec Table 11-13
*/
#define USB_DT_HUB (USB_TYPE_CLASS | 0x09)
#define USB_DT_SS_HUB (USB_TYPE_CLASS | 0x0a)
#define USB_DT_HUB_NONVAR_SIZE 7
#define USB_DT_SS_HUB_SIZE 12 /*
* Hub Device descriptor
* USB Hub class device protocols
* usb_device_descriptor中的bDeviceProtocol描述hub支持的最高速度
*/
#define USB_HUB_PR_FS 0 /* Full speed hub */
#define USB_HUB_PR_HS_NO_TT 0 /* Hi-speed hub without TT */
#define USB_HUB_PR_HS_SINGLE_TT 1 /* Hi-speed hub with single TT */
#define USB_HUB_PR_HS_MULTI_TT 2 /* Hi-speed hub with multiple TT */
#define USB_HUB_PR_SS 3 /* Super speed hub */
struct usb_hub_descriptor {
__u8 bDescLength;
__u8 bDescriptorType;
__u8 bNbrPorts; // number of ports on this hub
__le16 wHubCharacteristics; // Hub Charateristics
__u8 bPwrOn2PwrGood; // 设备完全打开所花费的时间(以 2 毫秒间隔)
__u8 bHubContrCurrent; // 集线器的控制器组件的最大电流要求 /* 2.0 and 3.0 hubs differ here */
union {
struct {
/* add 1 bit for hub status change; round to bytes */
__u8 DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];
__u8 PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];
} __attribute__ ((packed)) hs; struct {
__u8 bHubHdrDecLat;
__le16 wHubDelay;
__le16 DeviceRemovable;
} __attribute__ ((packed)) ss;
} u;
} __attribute__ ((packed));

Hub有专门的描述符,使用struct usb_hub_descriptor描述。

[include/uapi/linux/usb/ch11.h]
/*
* Hub descriptor
* See USB 2.0 spec Table 11-13
*/
#define USB_DT_HUB (USB_TYPE_CLASS | 0x09)
#define USB_DT_SS_HUB (USB_TYPE_CLASS | 0x0a)
#define USB_DT_HUB_NONVAR_SIZE 7
#define USB_DT_SS_HUB_SIZE 12 /*
* Hub Device descriptor
* USB Hub class device protocols
* usb_device_descriptor中的bDeviceProtocol描述hub支持的最高速度
*/
#define USB_HUB_PR_FS 0 /* Full speed hub */
#define USB_HUB_PR_HS_NO_TT 0 /* Hi-speed hub without TT */
#define USB_HUB_PR_HS_SINGLE_TT 1 /* Hi-speed hub with single TT */
#define USB_HUB_PR_HS_MULTI_TT 2 /* Hi-speed hub with multiple TT */
#define USB_HUB_PR_SS 3 /* Super speed hub */
struct usb_hub_descriptor {
__u8 bDescLength;
__u8 bDescriptorType;
__u8 bNbrPorts; // number of ports on this hub
__le16 wHubCharacteristics; // Hub Charateristics
__u8 bPwrOn2PwrGood; // 设备完全打开所花费的时间(以 2 毫秒间隔)
__u8 bHubContrCurrent; // 集线器的控制器组件的最大电流要求 /* 2.0 and 3.0 hubs differ here */
union {
struct {
/* add 1 bit for hub status change; round to bytes */
__u8 DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];
__u8 PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];
} __attribute__ ((packed)) hs; struct {
__u8 bHubHdrDecLat;
__le16 wHubDelay;
__le16 DeviceRemovable;
} __attribute__ ((packed)) ss;
} u;
} __attribute__ ((packed));

2.2.接口

[drivers/usb/core/hub.h]
/* 创建port数据结构usb_port */
extern int usb_hub_create_port_device(struct usb_hub *hub,
int port1);
/* 释放port数据结构usb_port */
extern void usb_hub_remove_port_device(struct usb_hub *hub,
int port1);
/**
* call this function to control port's power via setting or
* clearing the port's PORT_POWER feature.
*/
extern int usb_hub_set_port_power(struct usb_device *hdev,
struct usb_hub *hub, int port1, bool set);
/* 清除port的特性 */
extern int usb_clear_port_feature(struct usb_device *hdev,
int port1, int feature);
/* 判断hub是否是superspeed hub */
static inline int hub_is_superspeed(struct usb_device *hdev)
{
return hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS;
}
/* 判断hub是否是superspeedplus hub */
static inline int hub_is_superspeedplus(struct usb_device *hdev)
{
return (hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS &&
le16_to_cpu(hdev->descriptor.bcdUSB) >= 0x0310 &&
hdev->bos->ssp_cap);
}

3.初始化

hub的驱动定义为hub_driver,也是一个usb_driver结构体。hub驱动的匹配方式由hub_id_table定义,可以根据PID、VID、接口类型匹配。

[drivers/usb/core/hub.c]
static const struct usb_device_id hub_id_table[] = {
{ .match_flags = USB_DEVICE_ID_MATCH_VENDOR
| USB_DEVICE_ID_MATCH_PRODUCT
| USB_DEVICE_ID_MATCH_INT_CLASS,
.idVendor = USB_VENDOR_SMSC,
.idProduct = USB_PRODUCT_USB5534B,
.bInterfaceClass = USB_CLASS_HUB,
.driver_info = HUB_QUIRK_DISABLE_AUTOSUSPEND},
{ .match_flags = USB_DEVICE_ID_MATCH_VENDOR
| USB_DEVICE_ID_MATCH_PRODUCT,
.idVendor = USB_VENDOR_CYPRESS,
.idProduct = USB_PRODUCT_CY7C65632,
.driver_info = HUB_QUIRK_DISABLE_AUTOSUSPEND},
{ .match_flags = USB_DEVICE_ID_MATCH_VENDOR
| USB_DEVICE_ID_MATCH_INT_CLASS,
.idVendor = USB_VENDOR_GENESYS_LOGIC,
.bInterfaceClass = USB_CLASS_HUB,
.driver_info = HUB_QUIRK_CHECK_PORT_AUTOSUSPEND},
{ .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
.bDeviceClass = USB_CLASS_HUB},
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
.bInterfaceClass = USB_CLASS_HUB},
{ } /* Terminating entry */
}; MODULE_DEVICE_TABLE(usb, hub_id_table);
static struct usb_driver hub_driver = {
.name = "hub",
.probe = hub_probe,
.disconnect = hub_disconnect,
.suspend = hub_suspend,
.resume = hub_resume,
.reset_resume = hub_reset_resume,
.pre_reset = hub_pre_reset,
.post_reset = hub_post_reset,
.unlocked_ioctl = hub_ioctl,
.id_table = hub_id_table,
.supports_autosuspend = 1,
};

hub驱动匹配和初始化过程如下图所示,主要的工作流程有:

  1. 当注册root hub时,会调用device_add注册设备。内核调用usb_hub_init函数初始化hub,此时会调用usb_register_driver注册hub驱动hub_driver。hub设备或驱动注册时都会调用usb_device_match匹配到对方,若匹配成功,则hub_probe函数被调用,进入hub的初始化流程。
  2. hub_probe函数中做了两件事,第一件是初始化轮询hub所用的工作队列和定时器,hub_event比较重要,用于处理USB设备连接、断开、低功耗模式等,后续会介绍,第二件是配置hub,具体的工作如下:
    1. 获取hub描述符,hub有专用的描述。
    2. 初始化用于事务分割的工作队列。主要是将USB2.0事物转换成USB1.0或USB1.1。
    3. 通过请求获取hub的信息和状态。
    4. 分配hub中断传输的urb,用以查询hub状态。
    5. 填充hub中断传输的urb,回调函数为hub_irq
    6. 给hub的每个port创建usb_port数据结构。
    7. 使能hub,主要是给port上电,检查port状态、提交之前设置的中断传输urb,用于查询hub的状态;最后调度处理hub事件的工作队列,当有事件发生时会调用hub_event处理。

4.枚举设备

USB主机通过hub感知USB设备的连接、断开、状态变化等事件。当事件发生会产生xHCI中断,在中断处理函数中处理USB设备事件,具体的流程如下:

  1. 产生中断后会最终调用到handle_port_status函数处理hub port口事件,没事件发生则直接退出,有事件发生,且status_urb空闲,则走下面的流程。

    1. 设置HCD_FLAG_POLL_RH标志,调用usb_hcd_poll_rh_status函数开始轮询hub状态。
    2. 调用xHCI驱动的xhci_hub_status_data函数查询root hub每个port的状态,若有变化,则会反汇事件数据的长度。port的状态由port_开头的宏定义定义。
    3. 清除轮询hub的标志,将查询hub状态的status_urb从对应的端点队中移除,然后调用usb_hcd_giveback_urb,最终通过调度tasklet处理hub事件。详细的处理过程后续介绍。
  2. 当status_urb被占用时,即hcd->status_urb为NULL时,说明上一个hub事件还没处理完(tasklet没处理完),因此会设置HCD_FLAG_POLL_RH标志,调用mod_timer开启定时器,定时器到期后调用rh_timer_func处理hub事件,处理流程和上面一样。

xHCI驱动轮询的hub事件定义如下所示。root hub的port状态由一个32位的寄存器表示,地址保存在struct xhci_port中的addr变量中,事件的位域定义在xhci.h头文件中。

[drivers/usb/host/xhci.h]
/* true: port has an over-current condition */
#define PORT_OC (1 << 3)
/* true: connect status change */
#define PORT_CSC (1 << 17)
/* true: port enable change */
#define PORT_PEC (1 << 18)
/* true: warm reset for a USB 3.0 device is done. A "hot" reset puts the port
* into an enabled state, and the device into the default state. A "warm" reset
* also resets the link, forcing the device through the link training sequence.
* SW can also look at the Port Reset register to see when warm reset is done.
*/
#define PORT_WRC (1 << 19)
/* true: over-current change */
#define PORT_OCC (1 << 20)
/* true: reset change - 1 to 0 transition of PORT_RESET */
#define PORT_RC (1 << 21)
/* port link status change - set on some port link state transitions:
* Transition Reason
* ------------------------------------------------------------------------------
* - U3 to Resume Wakeup signaling from a device
* - Resume to Recovery to U0 USB 3.0 device resume
* - Resume to U0 USB 2.0 device resume
* - U3 to Recovery to U0 Software resume of USB 3.0 device complete
* - U3 to U0 Software resume of USB 2.0 device complete
* - U2 to U0 L1 resume of USB 2.1 device complete
* - U0 to U0 (???) L1 entry rejection by USB 2.1 device
* - U0 to disabled L1 entry error with USB 2.1 device
* - Any state to inactive Error on USB 3.0 port
*/
#define PORT_PLC (1 << 22)
/* port configure error change - port failed to configure its link partner */
#define PORT_CEC (1 << 23)

在众多事件中,枚举USB设备是最重要的,下图描述了USB主机枚举USB设备的过程。主要的工作有:

  1. 通过usb_hcd_poll_rh_status函数轮询到了hub的port上有事件发生,最终通过调用hub_irq函数处理这些事件。
  2. hub_irq函数做了两件事,第一件是重新提交查询hub状态的urb,即设置hcd->status_urb = urb,当下一次处理hub事件时,又会调用到hub_irq;第二件事是调度工作队列处理hub事件。
  3. hub事件通过hub_event函数处理,该函数会遍历每个port,当port上有USB设备连接时,调用hub_port_connect处理。
  4. 首先为设备分配usb_device数据结构,接着设置USB设备号,然后初始化设备,包括复位设备并获取设备速度、设置设备地址、获取设备描述符。
  5. 枚举USB设备和匹配USB设备驱动在usb_new_device函数中完成,主要的工作如下:
    1. 获取USB设备的配置、接口、端点等描述符,若开启了相关选项,则内核会打印USB设备的详细信息。
    2. 通过usb_device_match匹配设备驱动,即usb_device_type类型,最终会调用到内核提供的通用的USB设备驱动usb_generic_driver_probe
    3. 最后遍历USB设备的所有接口,调用usb_device_match为接口匹配驱动(USB驱动和USB接口对应,此时的匹配类型为usb_if_device_type),匹配成功后接口驱动的probe函数被调用。

USB总线-Linux内核USB3.0 Hub驱动分析(十四)的更多相关文章

  1. 基于tiny4412的Linux内核移植 -- DM9621NP网卡驱动移植(四)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  2. 如何在Ubuntu/CentOS上安装Linux内核4.0

    大家好,今天我们学习一下如何从Elrepo或者源代码来安装最新的Linux内核4.0.代号为‘Hurr durr I'm a sheep’的Linux内核4.0是目前为止最新的主干内核.它是稳定版3. ...

  3. 安装 Linux 内核 4.0

    大家好,今天我们学习一下如何从Elrepo或者源代码来安装最新的Linux内核4.0.代号为‘Hurr durr I'm a sheep’的Linux内核4.0是目前为止最新的主干内核.它是稳定版3. ...

  4. Win7原装ISO镜像封装USB3.0&网卡驱动

    Win7原装ISO镜像封装USB3.0&网卡驱动   最新购买的电脑是Windows10系统,想装回Windows7,但是装Windows7发现网络适配器没出现,如果没有USB2.0接口,US ...

  5. 《Linux内核设计与实现》课本第四章自学笔记——20135203齐岳

    <Linux内核设计与实现>课本第四章自学笔记 进程调度 By20135203齐岳 4.1 多任务 多任务操作系统就是能同时并发的交互执行多个进程的操作系统.多任务操作系统使多个进程处于堵 ...

  6. Linux内核OOM机制的详细分析(转)

    Linux 内核 有个机制叫OOM killer(Out-Of-Memory killer),该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了 防止内存耗尽而内核会把该进程杀掉.典 ...

  7. 2017-2018-1 《Linux内核原理与设计》第十二周作业

    <linux内核原理与设计>第十二周作业 Sql注入基础原理介绍 分组: 和20179215袁琳完成实验 一.实验说明   SQL注入攻击通过构建特殊的输入作为参数传入Web应用程序,而这 ...

  8. 2017-2018-1 20179205《Linux内核原理与设计》第十周作业

    <Linux内核原理与设计>第十周作业 教材17.19.20章学习及收获 1.在Linux以及所有unix系统中,设备被分为以下三种:块设备(blkdev)以块为单位寻址,通过块设备节点来 ...

  9. Linux 内核调度器源码分析 - 初始化

    导语 上篇系列文 混部之殇-论云原生资源隔离技术之CPU隔离(一) 介绍了云原生混部场景中CPU资源隔离核心技术:内核调度器,本系列文章<Linux内核调度器源码分析>将从源码的角度剖析内 ...

  10. Linux内核[CVE-2016-5195] (dirty COW)原理分析

    [原创]Linux内核[CVE-2016-5195] (dirty COW)原理分析-二进制漏洞-看雪论坛-安全社区|安全招聘|bbs.pediy.com https://bbs.pediy.com/ ...

随机推荐

  1. 使用nvm管理node

    安装包在阿里云盘 安装时,记录安装位置 安装后,在安装目录的setting添加镜像地址 node_mirror:npm.taobao.org/mirrors/node/ npm_mirror:npm. ...

  2. Intent 显示与隐式了解认识

    显示Intent 用于精确匹配,指定跳转目标 1.在intent构造函数中调用 Intent intent = new Intent(this,XX.class); 2.调用意图对象的setClass ...

  3. 嘿,我使用了mp的自连接+分页查询之后,再使用条件查询居然失效了。

    原因:我想通过自连接查询将一个表的两条数据放在一起,为此我重写了mp的分页查询 IPage<Indi> selectIndiShow(IPage<Indi> page, @Pa ...

  4. 【Java】【常用类】SimpleDateFormat 简单日期格式化类

    Date类的API不易于国际化,大部分基本摈弃了 java.text.SimpleDateFormate 不和语言环境有关的方式来格式化和解析日期的具体类 支持 文本转格式,格式转文本 public ...

  5. 【Spring】05 注解开发

    环境搭建 配置ApplicationContext.xml容器文件[半注解实现] <?xml version="1.0" encoding="UTF-8" ...

  6. MIT6.1810の学习笔记

    webliuのmit.6.828学习笔记 写在前面 本文基于mit/6.828课程,附官方网址. 本文采用的实验环境为2020年版的xv6系统,需要wsl,vscode,docker工具.附环境配置教 ...

  7. 某宝上搞来的电子书,经典的量化投资书籍,《Advances in Financial Machine Learning》—— 《金融机器学习的进展》、《量化投资与机器学习》、《金融机器学习研究进展》

    英文书名: <Advances in Financial Machine Learning> 经典的量化投资书籍,某宝上6元搞来的电子版:

  8. Java项目生产启动、关闭脚本

    1.直接启动 #!/bin/bash #这里可替换为你自己的执行程序,其他代码无需更改 APP_NAME=XXXX-api-1.0.jar #使用说明,用来提示输入参数 usage() { echo ...

  9. 使用触发器来审计表的DML、DDL操作

    最近帮客户排查某问题时,因为怀疑应用对某张配置表有变更,所以需要对这张表的所有操作进行审计. 原本Oracle对某张表的审计是非常方便的,一条命令就可以实现,也不需要费心自定义审计表. -- 启用对表 ...

  10. Python 潮流周刊#64:Python 的函数调用还很慢么?(摘要)

    本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...