文本旨在简单介绍一下Linux中USB协议栈的代码框架:
下图是USB协议栈相关数据结构的关系图:
下面结合上图看一下系统初始化的流程:
1.USB子系统初始化:\drivers\usb\core\usb.c
subsys_initcall(usb_init);
static int __init usb_init(void)中调用了很多初始化函数,目前关注下面两个:
retval = bus_register(&usb_bus_type);// 注册usb 总线 retval = usb_hub_init(); // 注册hub driver
1> bus_register()负责注册USB总线,当我们注册一个总线的时候,他会初始化两个链表,一个用来链接总线上所有的device,一个用来链接总线上所有的driver。当我们调用usb_register()来注册一个usb driver时,driver就会被连接到driver链表上。
// // 初始化总线上的两个链表: one for device; one for driver // klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put); klist_init(&bus->klist_drivers, NULL, NULL);
2> usb_hub_init()负责初始化hub,他会注册一个hub的驱动,并创建一个内核线程用来查询hub的状态(用来检查设备的插入)。
int usb_hub_init(void) { if (usb_register(&hub_driver) < 0) { // 注册hub驱动 printk(KERN_ERR "%s: can't register hub driver\n", usbcore_name); return -1; } khubd_task = kthread_run(hub_thread, NULL, "khubd"); // 创建内核线程 hub_thread if (!IS_ERR(khubd_task)) return 0; // 成功,返回0 /* Fall through if kernel_thread failed */ usb_deregister(&hub_driver); printk(KERN_ERR "%s: can't start khubd\n", usbcore_name); return -1; }
3> 当内核线程hub_thread()检测到新设备接入时,他会调用usb_new_device()来把新设备注册到总线的device链表上。
2.驱动与设备是如何绑定的:
不管是调用driver_register()来注册驱动,还是调用device_add()来注册设备;他们都会调用到总线的match函数,在调用驱动的probe函数:
int driver_probe_device(struct device_driver * drv, struct device * dev) { int ret = 0; if (!device_is_registered(dev)) return -ENODEV; // 调用bus的match() if (drv->bus->match && !drv->bus->match(dev, drv)) goto done; pr_debug("%s: Matched Device %s with Driver %s\n", drv->bus->name, dev->bus_id, drv->name); // 调用 probe() ret = really_probe(dev, drv); done: return ret; }
3.最后来看一下host controller是如何驱动的:
以EHCI为例,他的驱动入口函数为\drivers\usb\host\ehci-hcd.c::ehci_hcd_init():
// ehci主控制器驱动的入口函数 // static int __init ehci_hcd_init(void) { int retval = 0; #ifdef PLATFORM_DRIVER retval = platform_driver_register(&PLATFORM_DRIVER); // 注册平台驱动#endif #ifdef PCI_DRIVER // host controller 挂在PCI总线下 retval = pci_register_driver(&PCI_DRIVER); // 注册一个PCI driver ...... }
这里分不同的情况:主控制器有可能挂在PCI总线下面(那它就是一个PCI设备),也有可能直接挂在CPU下面(ARM系统)。
通过分析对应的probe()函数,我们发现,不管是那种情况,他们都会去执行下面的调用:调用usb_create_hcd()来创建usb_hcd结构体;调用usb_add_hcd()来把主控制器添加到usb总线上。
1> usb_create_hcd()主要值得注意的是hc_driver ,它是访问HC的接口(HCDI)(针对上面的不同情况hc_driver的具体实现也不一样,比如PCI平台上的static const struct hc_driver ehci_pci_hc_driver 和 Freescale平台上的static const struct hc_driver ehci_fsl_hc_driver 就不一样):
struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, struct device *dev, char *bus_name) { struct usb_hcd *hcd; // 分配资源 hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL); .... // 初始化 hc_driver,并获取其中的product_desc; // 比如pci的会显示:EHCI Host Controller // 比如fsl的会显示:Freescale On-Chip EHCI Host Controller hcd->driver = driver; hcd->product_desc = (driver->product_desc) ? driver->product_desc : "USB Host Controller"; return hcd; }
2> usb_add_hcd()调用register_root_hub()来把EHCI注册为USB总线的控制器:
int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags) { int retval; struct usb_device *rhdev; .... // 调用hc_driver->reset()来重启hc // if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) { dev_err(hcd->self.controller, "can't setup\n"); goto err_hcd_driver_setup; }// // //启动HC、root hub // if ((retval = hcd->driver->start(hcd)) < 0) { // 注册root hub if ((retval = register_root_hub(hcd)) != 0) .... }
register_root_hub()调用usb_new_device(),通过hcd->self.root_hub(图中的usb_hcd.usb_bus.usb_device)注册主控制器设备:
static int register_root_hub(struct usb_hcd *hcd) { struct device *parent_dev = hcd->self.controller; struct usb_device *usb_dev = hcd->self.root_hub; // 用来把usb_hcd注册到usb总线上 const int devnum = 1; int retval;// 会调用device_add,把device添加到usb总线上 retval = usb_new_device (usb_dev); .... return retval; }
通过最上面的图中的数据结构的关系可以看出来各个设备之间的关系,以及底层是通过bus_type、device、device_driver的模型来管理的。理解bus_type、device、device_driver的模型是理解Linux中整个USB子系统架构的关键。理解ehci_hcd和hc_driver是理解HCD的关键。上层驱动(USBD、USB device driver)最终都需要调用HCD来真正操作硬件(所以不同的硬件实现,HCD也不一样)。