USB HUB 之热插拔
来源:互联网 发布:网络教育留学 编辑:程序博客网 时间:2024/04/29 06:15
1. usb_hub_init
1.1 注册 hub driver.
1.1.1 建立interupt in URB。侦测port status变化。
1.1.2 注册usb_port_device_type 设备。
1.2 建立内核线程 “khubd”.
1.2.1 通过hub_events 来处理热插拔事件。
===============================================================
对于USB 来说,hub是一类很特殊的设备。USB 的即插即用功能就是通过这个实现的。那么code里面是怎么做的呢。
首先看看hub的初始化函数
int usb_hub_init(void){if (usb_register(&hub_driver) < 0) {printk(KERN_ERR "%s: can't register hub driver\n",usbcore_name);return -1;}khubd_task = kthread_run(hub_thread, NULL, "khubd");if (!IS_ERR(khubd_task))return 0;/* Fall through if kernel_thread failed */usb_deregister(&hub_driver);printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);return -1;}
这个函数做了两件事情:
1. 注册了 hub的驱动。
2. 建立了一个内核线程。
首先来看看hub driver,直接进去probe函数。
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id){struct usb_host_interface *desc;struct usb_endpoint_descriptor *endpoint;struct usb_device *hdev;struct usb_hub *hub;desc = intf->cur_altsetting;hdev = interface_to_usbdev(intf);/* * Set default autosuspend delay as 0 to speedup bus suspend, * based on the below considerations: * * - Unlike other drivers, the hub driver does not rely on the * autosuspend delay to provide enough time to handle a wakeup * event, and the submitted status URB is just to check future * change on hub downstream ports, so it is safe to do it. * * - The patch might cause one or more auto supend/resume for * below very rare devices when they are plugged into hub * first time: * * devices having trouble initializing, and disconnect * themselves from the bus and then reconnect a second * or so later * * devices just for downloading firmware, and disconnects * themselves after completing it * * For these quite rare devices, their drivers may change the * autosuspend delay of their parent hub in the probe() to one * appropriate value to avoid the subtle problem if someone * does care it. * * - The patch may cause one or more auto suspend/resume on * hub during running 'lsusb', but it is probably too * infrequent to worry about. * * - Change autosuspend delay of hub can avoid unnecessary auto * suspend timer for hub, also may decrease power consumption * of USB bus. * * - If user has indicated to prevent autosuspend by passing * usbcore.autosuspend = -1 then keep autosuspend disabled. */#ifdef CONFIG_PM_RUNTIMEif (hdev->dev.power.autosuspend_delay >= 0)pm_runtime_set_autosuspend_delay(&hdev->dev, 0);#endif/* * Hubs have proper suspend/resume support, except for root hubs * where the controller driver doesn't have bus_suspend and * bus_resume methods. */if (hdev->parent) {/* normal device */usb_enable_autosuspend(hdev);} else {/* root hub */const struct hc_driver *drv = bus_to_hcd(hdev->bus)->driver;if (drv->bus_suspend && drv->bus_resume)usb_enable_autosuspend(hdev);}if (hdev->level == MAX_TOPO_LEVEL) {dev_err(&intf->dev,"Unsupported bus topology: hub nested too deep\n");return -E2BIG;}#ifdefCONFIG_USB_OTG_BLACKLIST_HUBif (hdev->parent) {dev_warn(&intf->dev, "ignoring external hub\n");return -ENODEV;}#endif/* Some hubs have a subclass of 1, which AFAICT according to the *//* specs is not defined, but it works */if ((desc->desc.bInterfaceSubClass != 0) && (desc->desc.bInterfaceSubClass != 1)) {descriptor_error:dev_err (&intf->dev, "bad descriptor, ignoring hub\n");return -EIO;}/* Multiple endpoints? What kind of mutant ninja-hub is this? */if (desc->desc.bNumEndpoints != 1)goto descriptor_error;endpoint = &desc->endpoint[0].desc;/* If it's not an interrupt in endpoint, we'd better punt! */if (!usb_endpoint_is_int_in(endpoint))goto descriptor_error;/* We found a hub */dev_info (&intf->dev, "USB hub found\n");hub = kzalloc(sizeof(*hub), GFP_KERNEL);if (!hub) {dev_dbg (&intf->dev, "couldn't kmalloc hub struct\n");return -ENOMEM;}kref_init(&hub->kref);INIT_LIST_HEAD(&hub->event_list);hub->intfdev = &intf->dev;hub->hdev = hdev;INIT_DELAYED_WORK(&hub->leds, led_work);INIT_DELAYED_WORK(&hub->init_work, NULL);usb_get_intf(intf);usb_set_intfdata (intf, hub);intf->needs_remote_wakeup = 1;pm_suspend_ignore_children(&intf->dev, true);if (hdev->speed == USB_SPEED_HIGH)highspeed_hubs++;if (id->driver_info & HUB_QUIRK_CHECK_PORT_AUTOSUSPEND)hub->quirk_check_port_auto_suspend = 1;if (hub_configure(hub, endpoint) >= 0)return 0;hub_disconnect (intf);return -ENODEV;}
函数很长,但其实。大部分的工作都是做一些验证和初始化。最终真正的动作发生在这个函数里面:hub_configure
函数太长,就不贴出来了。
这个函数里面主要有两个地方和热插拔有关系。
首先它初始化了一个URB,这个URB 给一个interrupt IN的pipe建立的。
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,hub, endpoint->bInterval);对于一个URB来说,最关键的是其回调函数:
static void hub_irq(struct urb *urb){struct usb_hub *hub = urb->context;int status = urb->status;unsigned i;unsigned long bits;switch (status) {case -ENOENT:/* synchronous unlink */case -ECONNRESET:/* async unlink */case -ESHUTDOWN:/* hardware going away */return;default:/* presumably an error *//* Cause a hub reset after 10 consecutive errors */dev_dbg (hub->intfdev, "transfer --> %d\n", status);if ((++hub->nerrors < 10) || hub->error)goto resubmit;hub->error = status;/* FALL THROUGH *//* let khubd handle things */case 0:/* we got data: port status changed */bits = 0;for (i = 0; i < urb->actual_length; ++i)bits |= ((unsigned long) ((*hub->buffer)[i]))<< (i*8);hub->event_bits[0] = bits;break;}hub->nerrors = 0;/* Something happened, let khubd figure it out */kick_khubd(hub);resubmit:if (hub->quiescing)return;if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0&& status != -ENODEV && status != -EPERM)dev_err (hub->intfdev, "resubmit --> %d\n", status);}
这个回调函数就是把hub返回的status存入 event_bits ,然后调用kick_khubd.
static void kick_khubd(struct usb_hub *hub){unsigned longflags;spin_lock_irqsave(&hub_event_lock, flags);if (!hub->disconnected && list_empty(&hub->event_list)) {list_add_tail(&hub->event_list, &hub_event_list);/* Suppress autosuspend until khubd runs */usb_autopm_get_interface_no_resume(to_usb_interface(hub->intfdev));wake_up(&khubd_wait);}spin_unlock_irqrestore(&hub_event_lock, flags);}这个kick_khubd 会把这个设备的event_list 加入到 hub_event_list 里面去。这个hub_event_list 会在后面用到。
hub_configure 做的第二件事情就是建立usb_port 设备,这种设备的类型是usb_port_device_type。
for (i = 0; i < hdev->maxchild; i++) {ret = usb_hub_create_port_device(hub, i + 1);if (ret < 0) {dev_err(hub->intfdev,"couldn't create port%d device.\n", i + 1);hdev->maxchild = i;goto fail_keep_maxchild;}}这种设备对应的驱动是:
static bool usb_acpi_bus_match(struct device *dev){return is_usb_device(dev) || is_usb_port(dev);}static struct acpi_bus_type usb_acpi_bus = {.name = "USB",.match = usb_acpi_bus_match,.find_device = usb_acpi_find_device,};
下面该看那个内核线程了:
khubd_task = kthread_run(hub_thread, NULL, "khubd");
它的执行函数:
static int hub_thread(void *__unused){/* khubd needs to be freezable to avoid intefering with USB-PERSIST * port handover. Otherwise it might see that a full-speed device * was gone before the EHCI controller had handed its port over to * the companion full-speed controller. */set_freezable();do {hub_events();wait_event_freezable(khubd_wait,!list_empty(&hub_event_list) ||kthread_should_stop());} while (!kthread_should_stop() || !list_empty(&hub_event_list));pr_debug("%s: khubd exiting\n", usbcore_name);return 0;}
它通过hub_events 来处理热插拔事件。hub_events 太长就不贴出来了。
它做的事情就处理hub_event_list 里面的event。而这个list 是在前面的kick_khubd 里面去填充的。
如果有这个event是代表一个设备的插入而且这个设备也能正常工作。那么就会调用hub_port_connect_change。这个函数就会把插入的这个设备注册到驱动模型中去。然后usb device 驱动就接手了。
0 0
- USB HUB 之热插拔
- usb hub
- USB HUB
- usb hub
- Linux设备驱动之USB hub驱动
- Linux设备驱动之USB hub驱动
- Linux设备驱动之USB hub驱动
- Linux设备驱动之USB hub驱动
- Linux设备驱动之USB hub驱动
- Linux设备驱动之USB hub驱动(续)
- Linux3.10.x的USB学习笔记之HUB
- USB hub的苦恼
- USB Hub Kernel Debug
- Usb Hub代码分析
- usb hub 分析(转)
- USB hub 分析
- USB hub驱动分析
- USB hub 分线器设计
- TCP/IP, WebSocket 和 MQTT
- C#设计模式学习笔记-单例模式
- Android自动化提高篇
- MarkDown语法概述
- Tomcat-----web.xml的加载顺序
- USB HUB 之热插拔
- Haskell的list
- 集合的子集
- ironic workflow---debug and changing (3)
- JSP九大内置对象及四个作用域
- (2.1.4)Java Socket编程
- 这是我的第一篇CSDN博客
- rails 消息队列应用
- webview