文本旨在簡單介紹一下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也不一樣)。