赞
踩
本篇博客仅对ECHI主机控制器驱动的注册部分进行简要叙述,后面再对一些重要的接口进行分析讲解。
USB(Universal Serial Bus)即“通用外部总线”,在各种场所已经大量使用。它的接口简单(只有5v电源和地、两根数据线D+和D-),可以外接硬盘、键盘、鼠标、打印机等多种设备。
USB总线规范有1.1版和2.0版。USB1.1支持两种传输速率:低速1.5Mbit/s,全速12Mbit/s,对于鼠标、键盘、CD-ROM等设备,这样的速率够了。但是在访问硬盘、摄像机时,还是稍显不足、为此,USB2.0提供了一种更高的传输速率:高速,他可以达到480Mbit/s。USB2.0向下兼容USB1.1,可以将遵循USB1.1规范的设备连接到USB2.0控制器上,也可以把USB2.0的设备连接到USB1.1控制器上。
USB主机控制器(USB Host Controller)通过跟集线器与其他USB设备相连。集线器也属于USB设备,通过它可以在一个USB接口上扩展出多个接口。除根集线器外。可以层叠5个集线器,每条USB电缆的最大长度是5m,所以USB总线的最大距离为30m。一条USB总线上可以外接127个设备,包括根集线器和其他集线器。整个结构图是一个星状结构,一条USB总线上所有设备共享一条通往主机的数据通道,同一时刻只能有一个设备与主机通信。
USB主机控制器分为3种:UHCI、OHCI和EHCI。其中HCI表示“Host Controller Interface”。UHCI、OHCI属于USB1.1的主机控制器规范,而EHCI属于USB2.0的主机控制器规范。UHCI(即Universal HCI),它是由Inrel公司制定的标准,它的硬件所做的事情比较少,这使得软件比较复杂。与之相对的是OHCI(即Open HCI),塔友Compaq、Microsoft和National Semiconductor联合制定,在硬件方面它具备更多的智能,使得软件相对简单,因此,我们公司用的就是OHCI。
USB驱动路径:kernel/driver/usb/。
ECHI驱动文件路径:kernel/driver/usb/host/echi-hcd.c。
虽然对于大型的驱动模块从框架切入能更好的理解代码,但我目前的基础知识还比较薄弱,所以还是依旧从驱动入口看起。
- #ifdef CONFIG_USB_EHCI_HCD_PLATFORM
- #include "ehci-platform.c"
- #define PLATFORM_DRIVER ehci_platform_driver
- #endif
-
- static int __init ehci_hcd_init(void)
- {
- int retval = 0;
-
- if (usb_disabled())
- return -ENODEV;
-
- printk(KERN_INFO "%s: " DRIVER_DESC "\n", hcd_name);
-
- //设置usb_hcds_loaded的第USB_EHCI_LOADED位为1
- //作用通过下面的打印就能知道:它能让ehci主机控制器驱动先于uhci和ohci
- 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");
-
- #ifdef PLATFORM_DRIVER
- retval = platform_driver_register(&PLATFORM_DRIVER);
- if (retval < 0)
- goto clean0;
- #endif
-
- return retval;
-
- #ifdef PLATFORM_DRIVER
- platform_driver_unregister(&PLATFORM_DRIVER);
- clean0:
- #endif
-
- clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
- return retval;
- }
-
- static void __exit ehci_hcd_cleanup(void)
- {
- #ifdef PLATFORM_DRIVER
- platform_driver_unregister(&PLATFORM_DRIVER);
- #endif
-
- clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
- }
-
- module_init(ehci_hcd_init);
- module_exit(ehci_hcd_cleanup);
-
- MODULE_DESCRIPTION(DRIVER_DESC);
- MODULE_AUTHOR (DRIVER_AUTHOR);
- MODULE_LICENSE ("GPL");
以上代码是我经过删减剩下的。
在echi-hcd的模块入口和出口函数有许多的宏控,而我留下了我目前所学习的平台(sunplus 8368u)相关的代码。
在init函数中做的事情相对较少,就是进行位设置、位检测和平台驱动注册。
位设置和位检测中的USB_*_LOADED被定义在kernel/include/linux/usb/hcd.h,值为0、1、2。
平台驱动注册所用的平台驱动结构体PLATFORM_DRIVER,可以看到上述代码最上方的define,显然,这个结构体在同级目录下的ehci-platform.c文件中。
- static const struct platform_device_id ehci_platform_table[] = {
- {"ehci-platform", 0},
- {}
- };
-
- static struct platform_driver ehci_platform_driver = {
- .id_table = ehci_platform_table,
- .probe = ehci_platform_probe,
- .remove = __devexit_p(ehci_platform_remove),
- #ifdef CONFIG_PM_WARP
- .resume = sp_ehci_fb_resume,
- .suspend = sp_ehci_fb_suspend,
- #endif
- .shutdown = usb_hcd_platform_shutdown,
- .driver = {
- .owner = THIS_MODULE,
- .name = "ehci-platform",
- #ifndef CONFIG_PM_WARP
- .pm = &ehci_platform_pm_ops,
- #endif
- }
- };
平台设备一般通过.driver.name进行匹配,匹配成功之后则进入probe函数。
- static int __devinit ehci_platform_probe(struct platform_device *dev)
- {
- struct usb_hcd *hcd;
- struct resource *res_mem;
- int irq;
- int err = -ENOMEM;
-
- struct ehci_hcd_sp *ehci_sp;
-
- BUG_ON(!dev->dev.platform_data);
-
- if (usb_disabled())
- return -ENODEV;
-
- /* 获取中断号 */
- irq = platform_get_irq(dev, 0);
- if (irq < 0) {
- pr_err("no irq provieded");
- return irq;
- }
- /* 获取平台设备资源 */
- res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
- if (!res_mem) {
- pr_err("no memory recourse provieded");
- return -ENXIO;
- }
-
- /* 创建和初始化一个hcd结构体 */
- //usb_hcd主要描述主机控制器相关信息
- //hc_driver结构体类型内主要是控制主机的钩子函数
- hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, dev_name(&dev->dev));
- if (!hcd)
- return -ENOMEM;
-
- hcd->rsrc_start = res_mem->start;
- hcd->rsrc_len = resource_size(res_mem);
-
- //从rsrc_start地址开始申请一个rsrc_len长度的内存,用于后面做io映射
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- pr_err("controller already in use");
- err = -EBUSY;
- goto err_put_hcd;
- }
-
- //io映射:映射实际io物理地址到虚拟内存
- hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
- if (!hcd->regs)
- goto err_release_region;
-
- tasklet_init(&hcd->host_irq_tasklet, ehci_irq_tasklet, (unsigned long)hcd);
-
- err = usb_add_hcd(hcd, irq, IRQ_TYPE_LEVEL_HIGH);
- if (err)
- goto err_iounmap;
-
- platform_set_drvdata(dev, hcd);
- /****************************************************/
-
- ehci_sp = (struct ehci_hcd_sp *)hcd_to_ehci(hcd);
-
- //创建sys调试节点
- if (dev->id < 3) {
- device_create_file(&dev->dev, &dev_attr_usb_role_switch);
- device_create_file(&dev->dev, &dev_attr_usb_ctrl_reset);
- }
- device_create_file(&dev->dev, &dev_attr_usb_speed_switch);
- device_create_file(&dev->dev, &dev_attr_usb_uphy_swing);
- device_create_file(&dev->dev, &dev_attr_usb_disconnect_level);
- device_create_file(&dev->dev, &dev_attr_usb_rx_sqct_level);
- device_create_file(&dev->dev, &dev_attr_usb_rx_clk_invert);
-
- ehci_sp->flag = 0;
-
- printk("flag ***%p %p %d %d %p\n", hcd, hcd->hcd_priv, sizeof(struct ehci_hcd_sp), hcd->driver->hcd_priv_size, &ehci_sp->flag);
-
- /* ohci reset uphy notifier */
- ehci_sp->ehci_notifier.notifier_call = ehci_notifier_call;
- usb_register_notify(&ehci_sp->ehci_notifier);
-
- init_waitqueue_head(&hcd->reset_queue);
- hcd->ptr_flag = &ehci_sp->flag;
-
- ehci_sp->reset_thread = kthread_create(ehci_reset_thread,
- hcd_to_ehci(hcd),
- "ehci_reset_wait_event");
-
- if (IS_ERR(ehci_sp->reset_thread)) {
- pr_err("Create EHCI(%d) reset thread fail!\n", dev->id);
- return err;
- }
-
- /* Tell the thread to start working */
- wake_up_process(ehci_sp->reset_thread);
-
- return err;
-
- err_iounmap:
- iounmap(hcd->regs);
- err_release_region:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
- err_put_hcd:
- usb_put_hcd(hcd);
- return err;
- }
probe函数我们先关注两个结构体,第三行的hcd和31行的ehci_platform_hc_driver。
hcd的类型为“struct usb_hcd *hcd”,这个结构体主要用来描述主机控制器的相关信息,定义位置在kernel/include/linux/usb/hcd.h。
ehci_platform_hc_driver的类型为“struct hc_driver”,这个结构体则是一些控制usb主机控制器的回调函数。
以下是echi_hcd驱动的hc_driver。
- static const struct hc_driver ehci_platform_hc_driver = {
- .description = hcd_name,
- .product_desc = "Generic Platform EHCI Controller",
-
- //私有数据大小
- .hcd_priv_size = sizeof(struct ehci_hcd_sp),
-
- //中断处理函数
- .irq = ehci_irq,
- .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
-
- //复位、开始、停止、关闭函数
- .reset = ehci_platform_reset,
- .start = ehci_run,
- .stop = ehci_stop,
- .shutdown = ehci_shutdown,
-
- //usb logo test support
- .usb_logo_test = ehci_usb_logo_test,
-
- .urb_enqueue = ehci_urb_enqueue,
- .urb_dequeue = ehci_urb_dequeue,
- .endpoint_disable = ehci_endpoint_disable,
- .endpoint_reset = ehci_endpoint_reset,
-
- .get_frame_number = ehci_get_frame,
-
- .hub_status_data = ehci_hub_status_data,
- .hub_control = ehci_hub_control,
- #if defined(CONFIG_PM)
- .bus_suspend = ehci_bus_suspend,
- .bus_resume = ehci_bus_resume,
- #endif
- .relinquish_port = ehci_relinquish_port,
- .port_handed_over = ehci_port_handed_over,
-
- .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
- .get_port_status_from_register = ehci_get_port_status_from_register,
- };
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。