赞
踩
USB Hub提供了连接USB主机和USB设备的电气接口。USB Hub拥有一个上行口,至少一个下行口,上行口连接上一级的Hub的下行口或者USB主机,连接主机的为Root Hub,下行口连接下一级Hub的上行口或者USB设备。经过Hub的扩展,一个USB主机可以和多个USB设备通信。USB Hub有如下特性:
下图是USB Hub的架构图,拥有一个上行口,4个下行口,内部包含了USB3.2 Hub,USB2.0 Hub。USB2.0 Hub支持USB1.0、USB1.1、USB2.0设备,USB3.2 Hub支持USB3.2设备。
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));
[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); }
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驱动匹配和初始化过程如下图所示,主要的工作流程有:
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_irq
。hub_event
处理。USB主机通过hub感知USB设备的连接、断开、状态变化等事件。当事件发生会产生xHCI中断,在中断处理函数中处理USB设备事件,具体的流程如下:
handle_port_status
函数处理hub port口事件,没事件发生则直接退出,有事件发生,且status_urb
空闲,则走下面的流程。
HCD_FLAG_POLL_RH
标志,调用usb_hcd_poll_rh_status
函数开始轮询hub状态。xhci_hub_status_data
函数查询root hub每个port的状态,若有变化,则会反汇事件数据的长度。port的状态由port_开头的宏定义定义。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_event
函数处理,该函数会遍历每个port,当port上有USB设备连接时,调用hub_port_connect
处理。usb_device
数据结构,接着设置USB设备号,然后初始化设备,包括复位设备并获取设备速度、设置设备地址、获取设备描述符。usb_new_device
函数中完成,主要的工作如下:
usb_device_match
匹配设备驱动,即usb_device_type
类型,最终会调用到内核提供的通用的USB设备驱动usb_generic_driver_probe
。
usb_device_match
为接口匹配驱动(USB驱动和USB接口对应,此时的匹配类型为usb_if_device_type
),匹配成功后接口驱动的probe函数被调用。Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。