Linux中USB协议栈框架详解

2023-02-20 00:00:00 调用 初始化 驱动 注册 总线

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也不一样)。

相关文章