linux usb core
2013-11-28 16:49
281 查看
linux usb core是针对usb host,对于做手机的我而言,更加关注的是usb gadget,毕竟手机更多的作为usb外设而存在,不过随着越来越多的otg需求,这块就难以避免了
花点时间研究了下,usb gadget下次再谈
从init开始
usb_init是整个usb core的入口,略过前面debugfs之类的函数,注意到bus_register(&usb_bus_type),用来注册usb bus
接下来一个重要的调用就是usb_hub_init
这个函数很简短,主要功能有两块,注册interface driver:hub,启动守护进程khubd。
注册interface driver, 调用usb_register(&hub_driver),usb_register用来注册usb interface driver, 对应的还有一个函数用来注册usb device driver: usb_register_device_driver,这个接下来会提到,需要搞清的一个概念是,USB是一个层次结构,每一个设备都有一个或多个config,每一个config又有一个或多个interface(class),每一个interface都有多个endpoint。
启动守护进程khubd,这个守护进程后面再谈
接下来调用 usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
这里注册了一个通用的usb device driver: usb_generic_driver
这里需要搞清的一个概念是,无论hub还是外设,凡是插入usb的设备都是device,都会用一个device来描述,host相对特殊一些,会建立一个roothub的device,所有的这些device加入进来的时候都会调用usb_generic_driver的probe函数。
usb_init至此也就完成了,不过看到这里对usb core还是云里雾里,怎么和host controller结合起来呢?怎么枚举外设呢?
针对这两个问题,下面我们来看看加载host的关键函数
int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags)
这个函数很长,这里不全部列出,核心调用如下:
usb_register_bus(&hcd->self)),注册一个usb bus,一个usb controller对应一个usb bus
rhdev = usb_alloc_dev(NULL, &hcd->self, 0)),分配一个usb device, 这里是root hub
retval = hcd->driver->start(hcd); 调用host controller的start函数启动host
register_root_hub(hcd),注册root hub,这是个关键函数,从这里开始也就和上面的usb core产生了关系,这个函数主要调用的是usb_new_device (usb_dev), usb_new_device主要做两件事:枚举设备,主要是get descriptor;调用device_add注册第二步中分配的root hub,熟悉linux device driver的话,应该能够看出这一步会产生什么效果,还记得前面提到的usb_generic_driver么?没错,添加设备之后,会调用usb_generic_driver的probe函数
usb_generic_driver generic_probe
可以看出,这个函数是在处理usb的configuration,熟悉usb枚举过程的应该明白,这个是usb枚举的最后一步了
从上面这段截取的code中可以看出usb_set_configuration会取出设备的每一个interface然后调用device_add创建interface device,由于host只有一个interface:hub,所以这里只加了hub。还得的前面在usb_hub_init里面注册了的interface driver:hub 么?没错,这里hub加进来会触发hub interface driver的hub_probe
hub_probe --> hub_configure --> hub_activate --> kick_khubd
kick_khubd是一个关键函数,在这之前,hub的枚举已经全部完成,这意味着hub可以开始服务,检测是否有外设插入进来,kick_khubd给了hub这个机会,
这个函数将hub->event_list加入到了hub_event_list,代表有一个hub可以开始工作,剩下的任务就由usb core层来负责了。
到这里我们看到了如何将host加入到usb core,下面我们来回答之前提出的第二个问题:怎么枚举外设?
这部分工作由前面usb_init创建的守护进程khubd完成
可以看出hub_thread里主要调用的是hub_events
这一部分是从hub_event_list中取出需要操作的hub,没错,这个正是前面在kick_khubd 中加进来的那个hub
接下来去取每一个port的status,如果状态有改变,会设置connect_change标志
然后调用hub_port_connect_change,做的操作和host刚加进来会注册root hub类似,也是加一个usb device(例如:U盘),触发generic_probe完成枚举,在set_configuration中加入每一个interface device,对于u盘而言,只有一个interface: mass storage,于是会调用interface: mass storage的probe函数:storage_probe。
至此外设连接完成。
花点时间研究了下,usb gadget下次再谈
从init开始
1006 static int __init usb_init(void) 1007 { 1008 int retval; 1009 if (nousb) { 1010 pr_info("%s: USB support disabled\n", usbcore_name); 1011 return 0; 1012 } 1013 1014 retval = usb_debugfs_init(); 1015 if (retval) 1016 goto out; 1017 1018 retval = bus_register(&usb_bus_type); 1019 if (retval) 1020 goto bus_register_failed; 1021 retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb); 1022 if (retval) 1023 goto bus_notifier_failed; 1024 retval = usb_major_init(); 1025 if (retval) 1026 goto major_init_failed; 1027 retval = usb_register(&usbfs_driver); 1028 if (retval) 1029 goto driver_register_failed; 1030 retval = usb_devio_init(); 1031 if (retval) 1032 goto usb_devio_init_failed; 1033 retval = usbfs_init(); 1034 if (retval) 1035 goto fs_init_failed; 1036 retval = usb_hub_init(); 1037 if (retval) 1038 goto hub_init_failed; 1039 retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE); 1040 if (!retval) 1041 goto out;
usb_init是整个usb core的入口,略过前面debugfs之类的函数,注意到bus_register(&usb_bus_type),用来注册usb bus
接下来一个重要的调用就是usb_hub_init
3864 int usb_hub_init(void) 3865 { 3866 if (usb_register(&hub_driver) < 0) { 3867 printk(KERN_ERR "%s: can't register hub driver\n", 3868 usbcore_name); 3869 return -1; 3870 } 3871 3872 khubd_task = kthread_run(hub_thread, NULL, "khubd"); 3873 if (!IS_ERR(khubd_task)) 3874 return 0; 3875 3876 /* Fall through if kernel_thread failed */ 3877 usb_deregister(&hub_driver); 3878 printk(KERN_ERR "%s: can't start khubd\n", usbcore_name); 3879 3880 return -1; 3881 }
这个函数很简短,主要功能有两块,注册interface driver:hub,启动守护进程khubd。
注册interface driver, 调用usb_register(&hub_driver),usb_register用来注册usb interface driver, 对应的还有一个函数用来注册usb device driver: usb_register_device_driver,这个接下来会提到,需要搞清的一个概念是,USB是一个层次结构,每一个设备都有一个或多个config,每一个config又有一个或多个interface(class),每一个interface都有多个endpoint。
启动守护进程khubd,这个守护进程后面再谈
接下来调用 usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
这里注册了一个通用的usb device driver: usb_generic_driver
这里需要搞清的一个概念是,无论hub还是外设,凡是插入usb的设备都是device,都会用一个device来描述,host相对特殊一些,会建立一个roothub的device,所有的这些device加入进来的时候都会调用usb_generic_driver的probe函数。
usb_init至此也就完成了,不过看到这里对usb core还是云里雾里,怎么和host controller结合起来呢?怎么枚举外设呢?
针对这两个问题,下面我们来看看加载host的关键函数
int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags)
这个函数很长,这里不全部列出,核心调用如下:
usb_register_bus(&hcd->self)),注册一个usb bus,一个usb controller对应一个usb bus
rhdev = usb_alloc_dev(NULL, &hcd->self, 0)),分配一个usb device, 这里是root hub
retval = hcd->driver->start(hcd); 调用host controller的start函数启动host
register_root_hub(hcd),注册root hub,这是个关键函数,从这里开始也就和上面的usb core产生了关系,这个函数主要调用的是usb_new_device (usb_dev), usb_new_device主要做两件事:枚举设备,主要是get descriptor;调用device_add注册第二步中分配的root hub,熟悉linux device driver的话,应该能够看出这一步会产生什么效果,还记得前面提到的usb_generic_driver么?没错,添加设备之后,会调用usb_generic_driver的probe函数
usb_generic_driver generic_probe
156 static int generic_probe(struct usb_device *udev) 157 { 158 int err, c; 159 160 /* Choose and set the configuration. This registers the interfaces 161 * with the driver core and lets interface drivers bind to them. 162 */ 163 if (usb_device_is_owned(udev)) 164 ; /* Don't configure if the device is owned */ 165 else if (udev->authorized == 0) 166 dev_err(&udev->dev, "Device is not authorized for usage\n"); 167 else { 168 c = usb_choose_configuration(udev); 169 if (c >= 0) { 170 err = usb_set_configuration(udev, c); 171 if (err) { 172 dev_err(&udev->dev, "can't set config #%d, error %d\n", 173 c, err); 174 /* This need not be fatal. The user can try to 175 * set other configurations. */ 176 } 177 } 178 } 179 /* USB device state == configured ... usable */ 180 usb_notify_add_device(udev); 181 182 return 0; 183 }
可以看出,这个函数是在处理usb的configuration,熟悉usb枚举过程的应该明白,这个是usb枚举的最后一步了
1681 int usb_set_configuration(struct usb_device *dev, int configuration) { ... 1848 for (i = 0; i < nintf; ++i) { 1849 struct usb_interface *intf = cp->interface[i]; 1850 1851 dev_dbg(&dev->dev, 1852 "adding %s (config #%d, interface %d)\n", 1853 dev_name(&intf->dev), configuration, 1854 intf->cur_altsetting->desc.bInterfaceNumber); 1855 device_enable_async_suspend(&intf->dev); 1856 ret = device_add(&intf->dev); 1857 if (ret != 0) { 1858 dev_err(&dev->dev, "device_add(%s) --> %d\n", 1859 dev_name(&intf->dev), ret); 1860 continue; 1861 } 1862 create_intf_ep_devs(intf); 1863 } 1864 1865 usb_autosuspend_device(dev); 1866 return 0; 1867 }
从上面这段截取的code中可以看出usb_set_configuration会取出设备的每一个interface然后调用device_add创建interface device,由于host只有一个interface:hub,所以这里只加了hub。还得的前面在usb_hub_init里面注册了的interface driver:hub 么?没错,这里hub加进来会触发hub interface driver的hub_probe
hub_probe --> hub_configure --> hub_activate --> kick_khubd
kick_khubd是一个关键函数,在这之前,hub的枚举已经全部完成,这意味着hub可以开始服务,检测是否有外设插入进来,kick_khubd给了hub这个机会,
392 static void kick_khubd(struct usb_hub *hub) 393 { 394 unsigned long flags; 395 396 spin_lock_irqsave(&hub_event_lock, flags); 397 if (!hub->disconnected && list_empty(&hub->event_list)) { 398 list_add_tail(&hub->event_list, &hub_event_list); 399 400 /* Suppress autosuspend until khubd runs */ 401 usb_autopm_get_interface_no_resume( 402 to_usb_interface(hub->intfdev)); 403 wake_up(&khubd_wait); 404 } 405 spin_unlock_irqrestore(&hub_event_lock, flags); 406 }
这个函数将hub->event_list加入到了hub_event_list,代表有一个hub可以开始工作,剩下的任务就由usb core层来负责了。
到这里我们看到了如何将host加入到usb core,下面我们来回答之前提出的第二个问题:怎么枚举外设?
这部分工作由前面usb_init创建的守护进程khubd完成
3820 static int hub_thread(void *__unused) 3821 { 3822 /* khubd needs to be freezable to avoid intefering with USB-PERSIST 3823 * port handover. Otherwise it might see that a full-speed device 3824 * was gone before the EHCI controller had handed its port over to 3825 * the companion full-speed controller. 3826 */ 3827 set_freezable(); 3828 3829 do { 3830 hub_events(); 3831 wait_event_freezable(khubd_wait, 3832 !list_empty(&hub_event_list) || 3833 kthread_should_stop()); 3834 } while (!kthread_should_stop() || !list_empty(&hub_event_list)); 3835 3836 pr_debug("%s: khubd exiting\n", usbcore_name); 3837 return 0; 3838 }
可以看出hub_thread里主要调用的是hub_events
3580 static void hub_events(void) 3581 { ... 3600 while (1) { 3601 3602 /* Grab the first entry at the beginning of the list */ 3603 spin_lock_irq(&hub_event_lock); 3604 if (list_empty(&hub_event_list)) { 3605 spin_unlock_irq(&hub_event_lock); 3606 break; 3607 } 3608 3609 tmp = hub_event_list.next; 3610 list_del_init(tmp); 3611 3612 hub = list_entry(tmp, struct usb_hub, event_list); 3613 kref_get(&hub->kref); 3614 spin_unlock_irq(&hub_event_lock); 3615
这一部分是从hub_event_list中取出需要操作的hub,没错,这个正是前面在kick_khubd 中加进来的那个hub
3667 for (i = 1; i <= hub->descriptor->bNbrPorts; i++) { 3668 if (test_bit(i, hub->busy_bits)) 3669 continue; 3670 connect_change = test_bit(i, hub->change_bits); 3671 wakeup_change = test_and_clear_bit(i, hub->wakeup_bits); 3672 if (!test_and_clear_bit(i, hub->event_bits) && 3673 !connect_change && !wakeup_change) 3674 continue; 3675 3676 ret = hub_port_status(hub, i, 3677 &portstatus, &portchange);
接下来去取每一个port的status,如果状态有改变,会设置connect_change标志
3770 if (connect_change) 3771 hub_port_connect_change(hub, i, 3772 portstatus, portchange);
然后调用hub_port_connect_change,做的操作和host刚加进来会注册root hub类似,也是加一个usb device(例如:U盘),触发generic_probe完成枚举,在set_configuration中加入每一个interface device,对于u盘而言,只有一个interface: mass storage,于是会调用interface: mass storage的probe函数:storage_probe。
至此外设连接完成。
相关文章推荐
- Linux 与 Windows 对UNICODE 的处理方式
- Ubuntu12.04下QQ完美走起啊!走起啊!有木有啊!
- 解決Linux下Android开发真机调试设备不被识别问题
- Ubuntu Linux使用体验
- c语言实现hashmap(转载)
- Linux 信号signal处理机制
- linux下mysql添加用户
- Scientific Linux 5.5 图形安装教程
- 基于 Linux 集群环境上 GPFS 的问题诊断
- 谁是桌面王者?Win PK Linux三大镇山之宝
- vivi下重新调整分区
- Linux VS Unix:Linux欲一统天下 Unix不死
- linux下设定环境变量
- Linux下修改MySQL编码的方法
- Linux串口通信
- 从Windows系统下访问Linux分区相关软件
- 看看我的Ubuntu Linux截图
- ARM Linux系统启动
- Linux及ARM Linux程序开发笔记(零基础入门篇)