概述
本文对USB驱动中的HCD(主机控制器驱动,Host Controller Driver)初始化代码进行分析,梳理总结其中涉及到的重要数据结构和处理流程。HCD驱动代码根据USB标准分为OHCI、UHCI、EHCI和XHCI,本文以EHCI标准为主,进行分析,选取freescale驱动代码
初始化代码分析
HCD驱动是CPU上USB主机控制器的抽象,HCD驱动代码位于driver/usb/host路径下,入口为ehci_hcd.c
static int __init ehci_hcd_init(void)
{
int retval = 0;
set_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) ||
test_bit(USB_OHCI_LOADED, &usb_hcds_loaded))
printk(KERN_WARNING "Warning! ehci_hcd should always be loaded"
" before uhci_hcd and ohci_hcd, not after\n");
pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n",
hcd_name,
sizeof(struct ehci_qh), sizeof(struct ehci_qtd),
sizeof(struct ehci_itd), sizeof(struct ehci_sitd));
#ifdef DEBUG
ehci_debug_root = debugfs_create_dir("ehci", NULL);
if (!ehci_debug_root) {
retval = -ENOENT;
goto err_debug;
}
#endif
#ifdef PLATFORM_DRIVER
retval = platform_driver_register(&PLATFORM_DRIVER);
if (retval < 0)
goto clean0;
#endif
#ifdef PCI_DRIVER
retval = pci_register_driver(&PCI_DRIVER);
if (retval < 0)
goto clean1;
#endif
#ifdef PS3_SYSTEM_BUS_DRIVER
retval = ps3_ehci_driver_register(&PS3_SYSTEM_BUS_DRIVER);
if (retval < 0)
goto clean2;
#endif
#ifdef OF_PLATFORM_DRIVER
retval = of_register_platform_driver(&OF_PLATFORM_DRIVER);
if (retval < 0)
goto clean3;
#endif
return retval;
#ifdef OF_PLATFORM_DRIVER
/* of_unregister_platform_driver(&OF_PLATFORM_DRIVER); */
clean3:
#endif
#ifdef PS3_SYSTEM_BUS_DRIVER
ps3_ehci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
clean2:
#endif
#ifdef PCI_DRIVER
pci_unregister_driver(&PCI_DRIVER);
clean1:
#endif
#ifdef PLATFORM_DRIVER
platform_driver_unregister(&PLATFORM_DRIVER);
clean0:
#endif
#ifdef DEBUG
debugfs_remove(ehci_debug_root);
ehci_debug_root = NULL;
err_debug:
#endif
clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
return retval;
}
module_init(ehci_hcd_init);
该函数其实并没有做什么实质性的工作,根据宏定义进入具体设备的驱动程序中,以freescale芯片来分析的话,会调用()向系统注册ehci_fsl_driver驱动,驱动内容如下
static struct platform_driver ehci_fsl_driver = {
.probe = ehci_fsl_drv_probe,
.remove = ehci_fsl_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "fsl-ehci",
},
};
其probe函数为ehci_fsl_probe,驱动名称为"fsl-ehci"。那么驱动注册了,怎么才会调用probe函数呢?当然是要有设备注册。那么设备是什么时候注册的呢?主机控制器设备属于CPU片上资源,这些片上资源一般都是在内核启动的时候注册到系统的,在arch/powerpc/sysdev/fsl_soc.c文件中,对设备进行了注册操作
usb_dev_mph =
platform_device_register_simple("fsl-ehci", i, r, 2);
if (IS_ERR(usb_dev_mph)) {
ret = PTR_ERR(usb_dev_mph);
goto err;
}
代码中通过调用platform_device_register_simple()向系统中注册名为fsl-ehci的设备。现在就清楚了,不管是设备先注册还是驱动先注册,现在设备和驱动刚好对应起来了,所以ehci_fsl_drv_probe会马上调用起来。
在分析echi_fsl_drv_probe之前,我们先来分析一下设备结构以及内容。fsl_soc.c文件中通过调用fsl_usb_of_init()函数来注册USB主机控制器设备,其设备信息和资源从哪里来的呢?看代码
for_each_compatible_node(np, NULL, "fsl-usb2-mph") {
struct resource r[2];
struct fsl_usb2_platform_data usb_data;
const unsigned char *prop = NULL;
memset(&r, 0, sizeof(r));
memset(&usb_data, 0, sizeof(usb_data));
ret = of_address_to_resource(np, 0, &r[0]);
if (ret)
goto err;
of_irq_to_resource(np, 0, &r[1]);
usb_dev_mph =
platform_device_register_simple("fsl-ehci", i, r, 2);
if (IS_ERR(usb_dev_mph)) {
ret = PTR_ERR(usb_dev_mph);
goto err;
}
这里通过调用for_each_compatible_node(),其实要涉及到linux设备树的概念,这里不展开说明。简单说就是通过平台文件里的一个xxx.dts文件中读取名字为fsl-usb2-mph的设备树结构,从中拿到硬件资源和信息,所以我们主要分析清楚这些资源和信息就行。从arch/powerpc/boot/dts/mpc834x_mds.dts文件中找到这样一段描述
usb@22000 {
compatible = "fsl-usb2-mph";
reg = <0x22000 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
interrupt-parent = ;
interrupts = <39 0x8>;
phy_type = "ulpi";
port1;
};
可以看到寄存器的初始地址为0x22000,长度为1000,中断号为39,有一个port。
HCD probe
下面来分析ehci_fsl_drv_probe()函数,里面返回的是一个usb_hcd_fsl_probe()函数,传递了pdev和一个ehci_fsl_hc_driver
static int ehci_fsl_drv_probe(struct platform_device *pdev)
{
if (usb_disabled())
return -ENODEV;
/* FIXME we only want one one probe() not two */
return usb_hcd_fsl_probe(&ehci_fsl_hc_driver, pdev);
}
pdev就是前面分析的向系统注册的主机控制器设备“fsl-ehci”,但是ehci_fsl_hc_driver又是什么呢?
首先,它的类型是struct hc_driver,然后它是一个static类型的静态变量