USB Hub 检测设备
系列文章目录
xHCI 简单分析
USB Root Hub 分析
USB Hub 检测设备
文章目录
一、引言
USB Hub 检测设备 一文中讲到,当有 USB 插入时,它会激活 hub_events
函数。
static int hub_thread(void *__unused)
{
do {
hub_events();
wait_event_interruptible(khubd_wait,
!list_empty(&hub_event_list) ||
kthread_should_stop());
try_to_freeze();
} while (!kthread_should_stop() || !list_empty(&hub_event_list));
pr_debug("%s: khubd exiting\n", usbcore_name);
return 0;
}
二、hub_events
static void hub_events(void)
{
// ...
while (1) {
// ...
tmp = hub_event_list.next;
list_del_init(tmp);
hub = list_entry(tmp, struct usb_hub, event_list);
// 描述 usb 设备(Hub,整体,不是接口)
hdev = hub->hdev;
intf = to_usb_interface(hub->intfdev);
// hub 接口设备
hub_dev = &intf->dev;
// ...
/* Lock the device, then check to see if we were
* disconnected while waiting for the lock to succeed. */
if (locktree(hdev) < 0) {
// ...
/* Autoresume */
ret = usb_autopm_get_interface(intf);
// ...
/* deal with port status changes */
for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
// ...
ret = hub_port_status(hub, i,
&portstatus, &portchange);
// ...
if (connect_change)
hub_port_connect_change(hub, i,
portstatus, portchange);
} /* end for i */
// ...
if (!hdev->parent && !hub->busy_bits[0])
usb_enable_root_hub_irq(hdev->bus);
loop_autopm:
/* Allow autosuspend if we're not going to run again */
if (list_empty(&hub->event_list))
usb_autopm_enable(intf);
loop:
usb_unlock_device(hdev);
usb_put_intf(intf);
} /* end while (1) */
}
在这个循环中,检测 Hub 的每个端口是否有变化,有变化则调用 hub_port_connect_change
进行处理。
hub_port_connect_change
static void hub_port_connect_change(struct usb_hub *hub, int port1,
u16 portstatus, u16 portchange)
{
struct usb_device *hdev = hub->hdev;
struct device *hub_dev = hub->intfdev;
// ...
/* Disconnect any existing devices under this port */
if (hdev->children[port1-1])
usb_disconnect(&hdev->children[port1-1]);
clear_bit(port1, hub->change_bits);
// ...
for (i = 0; i < SET_CONFIG_TRIES; i++) {
struct usb_device *udev;
// ...
udev = usb_alloc_dev(hdev, hdev->bus, port1);
// ...
usb_set_device_state(udev, USB_STATE_POWERED);
udev->speed = USB_SPEED_UNKNOWN;
udev->bus_mA = hub->mA_per_port;
udev->level = hdev->level + 1;
// ...
choose_address(udev);
// ...
/* reset and get descriptor */
status = hub_port_init(hub, udev, port1, i);
// ...
if (!status) {
status = usb_new_device(udev);
// ...
}
// ...
status = hub_power_remaining(hub);
// ...
}
在这个循环中,主要涉及 8 个重量级函数,先点明它们的角色分工。
第一个函数,usb_alloc_dev(),一个 struct usb_device 结构体指针,申请内存,这个结构体指针可不是为 Hub 准备的,它正是为了 Hub 这个端口所接的设备而申请的,别忘了我们此时此刻的上下文,之所以进入这个循环,是因为我们的 Hub 检测到某个端口有设备连接,所以,Hub 驱动就义不容辞地要为该设备做点什么。
第二个函数,usb_set_device_state(),这个函数用来设置设备的状态,在 struct usb_device 结构体中,有一个成员 enum usb_device_state state,这一刻会把这个设备的状态设置为 USB_STATE_POWERED,即上电状态。
第三个函数,choose_address(),为设备选择一个地址。后面会用实例来查看效果。
第四个函数,hub_port_init(),端口初始化,主要就是前面所讲的获取设备的描述符。
第五个函数,usb_get_status(),这个函数是专门为 Hub 准备的,不是为当前的这个 Hub,而是说当前 Hub 的这个端口上连接的如果又是 Hub,那么和连接普通设备就不一样。
第六个函数,check_highspeed(),不同速度的设备,当然待遇不一样。
第七个函数,usb_new_device()。寻找驱动程序,调用驱动程序的 probe,跟踪这个函数就能一直到设备驱动程序的 probe() 函数的调用。
第八个函数,hub_power_remaining(),电源管理。
usb_alloc_dev
struct usb_device *
usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
{
struct usb_device *dev;
// ...
device_initialize(&dev->dev);
dev->dev.bus = &usb_bus_type;
dev->dev.type = &usb_device_type;
dev->dev.dma_mask = bus->controller->dma_mask;
dev->state = USB_STATE_ATTACHED;
INIT_LIST_HEAD(&dev->ep0.urb_list);
dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
/* ep0 maxpacket comes later, from device descriptor */
dev->ep_in[0] = dev->ep_out[0] = &dev->ep0;
// ...
dev->portnum = port1;
dev->bus = bus;
dev->parent = parent;
INIT_LIST_HEAD(&dev->filelist);
#ifdefCONFIG_PM
mutex_init(&dev->pm_mutex);
INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work);
dev->autosuspend_delay = usb_autosuspend_delay * HZ;
#endif
return dev;
}
usb_set_device_state
void usb_set_device_state(struct usb_device *udev,
enum usb_device_state new_state)
{
unsigned long flags;
spin_lock_irqsave(&device_state_lock, flags);
if (udev->state == USB_STATE_NOTATTACHED)
;/* do nothing */
else if (new_state != USB_STATE_NOTATTACHED) {
/* root hub wakeup capabilities are managed out-of-band
* and may involve silicon errata ... ignore them here.
*/
if (udev->parent) {
if (udev->state == USB_STATE_SUSPENDED
|| new_state == USB_STATE_SUSPENDED)
;/* No change to wakeup settings */
else if (new_state == USB_STATE_CONFIGURED)
device_init_wakeup(&udev->dev,
(udev->actconfig->desc.bmAttributes
& USB_CONFIG_ATT_WAKEUP));
else
device_init_wakeup(&udev->dev, 0);
}
udev->state = new_state;
} else
recursively_mark_NOTATTACHED(udev);
spin_unlock_irqrestore(&device_state_lock, flags);
}
hub_port_init
static int
hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
int retry_counter)
{
// ...
/* Reset the device; full speed may morph to high speed */
retval = hub_port_reset(hub, port1, udev, delay);
// ...
for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
if (USE_NEW_SCHEME(retry_counter)) {
struct usb_device_descriptor *buf;
int r = 0;
#define GET_DESCRIPTOR_BUFSIZE64
buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
// ...
for (j = 0; j < 3; ++j) {
buf->bMaxPacketSize0 = 0;
r = usb_control_msg(udev, usb_rcvaddr0pipe(),
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
USB_DT_DEVICE << 8, 0,
buf, GET_DESCRIPTOR_BUFSIZE,
USB_CTRL_GET_TIMEOUT);
// ...
}
udev->descriptor.bMaxPacketSize0 =
buf->bMaxPacketSize0;
kfree(buf);
retval = hub_port_reset(hub, port1, udev, delay);
// ...
}
for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
retval = hub_set_address(udev);
if (retval >= 0)
break;
msleep(200);
}
// ...
retval = usb_get_device_descriptor(udev, 8);
// ...
}
usb_new_device
int usb_new_device(struct usb_device *udev)
{
// ...
usb_detect_quirks(udev);
err = usb_get_configuration(udev);
// ...
/* read the standard strings and cache them if present */
udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
udev->manufacturer = usb_cache_string(udev,
udev->descriptor.iManufacturer);
udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
// ...
/* export the usbdev device-node for libusb */
udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
// ...
err = device_add(&udev->dev);
// ...
if (udev->parent)
usb_autoresume_device(udev->parent);
// ...
}
☆
原文地址:https://blog.csdn.net/Liuqz2009/article/details/144721194
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!