USB总线-Linux内核USB3.0 Hub驱动分析(十四)
1.概述
USB Hub提供了连接USB主机和USB设备的电气接口。USB Hub拥有一个上行口,至少一个下行口,上行口连接上一级的Hub的下行口或者USB主机,连接主机的为Root Hub,下行口连接下一级Hub的上行口或者USB设备。经过Hub的扩展,一个USB主机可以和多个USB设备通信。USB Hub有如下特性:
- 良好的扩展性
- 电源管理
- 探测设备连接和断开连接
- 总线错误探测和修复
- 支持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驱动匹配和初始化过程如下图所示,主要的工作流程有:
- 当注册root hub时,会调用device_add注册设备。内核调用usb_hub_init函数初始化hub,此时会调用usb_register_driver注册hub驱动hub_driver。hub设备或驱动注册时都会调用usb_device_match匹配到对方,若匹配成功,则hub_probe函数被调用,进入hub的初始化流程。
hub_probe
函数中做了两件事,第一件是初始化轮询hub所用的工作队列和定时器,hub_event
比较重要,用于处理USB设备连接、断开、低功耗模式等,后续会介绍,第二件是配置hub,具体的工作如下:- 获取hub描述符,hub有专用的描述。
- 初始化用于事务分割的工作队列。主要是将USB2.0事物转换成USB1.0或USB1.1。
- 通过请求获取hub的信息和状态。
- 分配hub中断传输的urb,用以查询hub状态。
- 填充hub中断传输的urb,回调函数为
hub_irq
。 - 给hub的每个port创建usb_port数据结构。
- 使能hub,主要是给port上电,检查port状态、提交之前设置的中断传输urb,用于查询hub的状态;最后调度处理hub事件的工作队列,当有事件发生时会调用
hub_event
处理。
4.枚举设备
USB主机通过hub感知USB设备的连接、断开、状态变化等事件。当事件发生会产生xHCI中断,在中断处理函数中处理USB设备事件,具体的流程如下:
产生中断后会最终调用到
handle_port_status
函数处理hub port口事件,没事件发生则直接退出,有事件发生,且status_urb
空闲,则走下面的流程。- 设置
HCD_FLAG_POLL_RH
标志,调用usb_hcd_poll_rh_status
函数开始轮询hub状态。 - 调用xHCI驱动的
xhci_hub_status_data
函数查询root hub每个port的状态,若有变化,则会反汇事件数据的长度。port的状态由port_开头的宏定义定义。 - 清除轮询hub的标志,将查询hub状态的
status_urb
从对应的端点队中移除,然后调用usb_hcd_giveback_urb
,最终通过调度tasklet处理hub事件。详细的处理过程后续介绍。
- 设置
当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设备的过程。主要的工作有:
- 通过
usb_hcd_poll_rh_status
函数轮询到了hub的port上有事件发生,最终通过调用hub_irq
函数处理这些事件。 hub_irq
函数做了两件事,第一件是重新提交查询hub状态的urb,即设置hcd->status_urb = urb
,当下一次处理hub事件时,又会调用到hub_irq
;第二件事是调度工作队列处理hub事件。- hub事件通过
hub_event
函数处理,该函数会遍历每个port,当port上有USB设备连接时,调用hub_port_connect
处理。 - 首先为设备分配
usb_device
数据结构,接着设置USB设备号,然后初始化设备,包括复位设备并获取设备速度、设置设备地址、获取设备描述符。 - 枚举USB设备和匹配USB设备驱动在
usb_new_device
函数中完成,主要的工作如下:- 获取USB设备的配置、接口、端点等描述符,若开启了相关选项,则内核会打印USB设备的详细信息。
- 通过
usb_device_match
匹配设备驱动,即usb_device_type
类型,最终会调用到内核提供的通用的USB设备驱动usb_generic_driver_probe
。 - 最后遍历USB设备的所有接口,调用
usb_device_match
为接口匹配驱动(USB驱动和USB接口对应,此时的匹配类型为usb_if_device_type
),匹配成功后接口驱动的probe函数被调用。
USB总线-Linux内核USB3.0 Hub驱动分析(十四)的更多相关文章
- 基于tiny4412的Linux内核移植 -- DM9621NP网卡驱动移植(四)
作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...
- 如何在Ubuntu/CentOS上安装Linux内核4.0
大家好,今天我们学习一下如何从Elrepo或者源代码来安装最新的Linux内核4.0.代号为‘Hurr durr I'm a sheep’的Linux内核4.0是目前为止最新的主干内核.它是稳定版3. ...
- 安装 Linux 内核 4.0
大家好,今天我们学习一下如何从Elrepo或者源代码来安装最新的Linux内核4.0.代号为‘Hurr durr I'm a sheep’的Linux内核4.0是目前为止最新的主干内核.它是稳定版3. ...
- Win7原装ISO镜像封装USB3.0&网卡驱动
Win7原装ISO镜像封装USB3.0&网卡驱动 最新购买的电脑是Windows10系统,想装回Windows7,但是装Windows7发现网络适配器没出现,如果没有USB2.0接口,US ...
- 《Linux内核设计与实现》课本第四章自学笔记——20135203齐岳
<Linux内核设计与实现>课本第四章自学笔记 进程调度 By20135203齐岳 4.1 多任务 多任务操作系统就是能同时并发的交互执行多个进程的操作系统.多任务操作系统使多个进程处于堵 ...
- Linux内核OOM机制的详细分析(转)
Linux 内核 有个机制叫OOM killer(Out-Of-Memory killer),该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了 防止内存耗尽而内核会把该进程杀掉.典 ...
- 2017-2018-1 《Linux内核原理与设计》第十二周作业
<linux内核原理与设计>第十二周作业 Sql注入基础原理介绍 分组: 和20179215袁琳完成实验 一.实验说明 SQL注入攻击通过构建特殊的输入作为参数传入Web应用程序,而这 ...
- 2017-2018-1 20179205《Linux内核原理与设计》第十周作业
<Linux内核原理与设计>第十周作业 教材17.19.20章学习及收获 1.在Linux以及所有unix系统中,设备被分为以下三种:块设备(blkdev)以块为单位寻址,通过块设备节点来 ...
- Linux 内核调度器源码分析 - 初始化
导语 上篇系列文 混部之殇-论云原生资源隔离技术之CPU隔离(一) 介绍了云原生混部场景中CPU资源隔离核心技术:内核调度器,本系列文章<Linux内核调度器源码分析>将从源码的角度剖析内 ...
- Linux内核[CVE-2016-5195] (dirty COW)原理分析
[原创]Linux内核[CVE-2016-5195] (dirty COW)原理分析-二进制漏洞-看雪论坛-安全社区|安全招聘|bbs.pediy.com https://bbs.pediy.com/ ...
随机推荐
- 使用nvm管理node
安装包在阿里云盘 安装时,记录安装位置 安装后,在安装目录的setting添加镜像地址 node_mirror:npm.taobao.org/mirrors/node/ npm_mirror:npm. ...
- Intent 显示与隐式了解认识
显示Intent 用于精确匹配,指定跳转目标 1.在intent构造函数中调用 Intent intent = new Intent(this,XX.class); 2.调用意图对象的setClass ...
- 嘿,我使用了mp的自连接+分页查询之后,再使用条件查询居然失效了。
原因:我想通过自连接查询将一个表的两条数据放在一起,为此我重写了mp的分页查询 IPage<Indi> selectIndiShow(IPage<Indi> page, @Pa ...
- 【Java】【常用类】SimpleDateFormat 简单日期格式化类
Date类的API不易于国际化,大部分基本摈弃了 java.text.SimpleDateFormate 不和语言环境有关的方式来格式化和解析日期的具体类 支持 文本转格式,格式转文本 public ...
- 【Spring】05 注解开发
环境搭建 配置ApplicationContext.xml容器文件[半注解实现] <?xml version="1.0" encoding="UTF-8" ...
- MIT6.1810の学习笔记
webliuのmit.6.828学习笔记 写在前面 本文基于mit/6.828课程,附官方网址. 本文采用的实验环境为2020年版的xv6系统,需要wsl,vscode,docker工具.附环境配置教 ...
- 某宝上搞来的电子书,经典的量化投资书籍,《Advances in Financial Machine Learning》—— 《金融机器学习的进展》、《量化投资与机器学习》、《金融机器学习研究进展》
英文书名: <Advances in Financial Machine Learning> 经典的量化投资书籍,某宝上6元搞来的电子版:
- Java项目生产启动、关闭脚本
1.直接启动 #!/bin/bash #这里可替换为你自己的执行程序,其他代码无需更改 APP_NAME=XXXX-api-1.0.jar #使用说明,用来提示输入参数 usage() { echo ...
- 使用触发器来审计表的DML、DDL操作
最近帮客户排查某问题时,因为怀疑应用对某张配置表有变更,所以需要对这张表的所有操作进行审计. 原本Oracle对某张表的审计是非常方便的,一条命令就可以实现,也不需要费心自定义审计表. -- 启用对表 ...
- Python 潮流周刊#64:Python 的函数调用还很慢么?(摘要)
本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...