当前位置:   article > 正文

Linux驱动开发之USB驱动深入学习(三)——USB2.0ECHI驱动注册_usb_register_notify

usb_register_notify

一、前言

本篇博客仅对ECHI主机控制器驱动的注册部分进行简要叙述,后面再对一些重要的接口进行分析讲解。

二、USB

1、概述

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控制器上。

2、硬件拓扑结构

图 - USB硬件拓扑结构

USB主机控制器(USB Host Controller)通过跟集线器与其他USB设备相连。集线器也属于USB设备,通过它可以在一个USB接口上扩展出多个接口。除根集线器外。可以层叠5个集线器,每条USB电缆的最大长度是5m,所以USB总线的最大距离为30m。一条USB总线上可以外接127个设备,包括根集线器和其他集线器。整个结构图是一个星状结构,一条USB总线上所有设备共享一条通往主机的数据通道,同一时刻只能有一个设备与主机通信。

3、主机控制器

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。

虽然对于大型的驱动模块从框架切入能更好的理解代码,但我目前的基础知识还比较薄弱,所以还是依旧从驱动入口看起。

  1. #ifdef CONFIG_USB_EHCI_HCD_PLATFORM
  2. #include "ehci-platform.c"
  3. #define PLATFORM_DRIVER ehci_platform_driver
  4. #endif
  5. static int __init ehci_hcd_init(void)
  6. {
  7. int retval = 0;
  8. if (usb_disabled())
  9. return -ENODEV;
  10. printk(KERN_INFO "%s: " DRIVER_DESC "\n", hcd_name);
  11. //设置usb_hcds_loaded的第USB_EHCI_LOADED位为1
  12. //作用通过下面的打印就能知道:它能让ehci主机控制器驱动先于uhci和ohci
  13. set_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
  14. if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) || test_bit(USB_OHCI_LOADED, &usb_hcds_loaded))
  15. printk(KERN_WARNING "Warning! ehci_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n");
  16. #ifdef PLATFORM_DRIVER
  17. retval = platform_driver_register(&PLATFORM_DRIVER);
  18. if (retval < 0)
  19. goto clean0;
  20. #endif
  21. return retval;
  22. #ifdef PLATFORM_DRIVER
  23. platform_driver_unregister(&PLATFORM_DRIVER);
  24. clean0:
  25. #endif
  26. clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
  27. return retval;
  28. }
  29. static void __exit ehci_hcd_cleanup(void)
  30. {
  31. #ifdef PLATFORM_DRIVER
  32. platform_driver_unregister(&PLATFORM_DRIVER);
  33. #endif
  34. clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
  35. }
  36. module_init(ehci_hcd_init);
  37. module_exit(ehci_hcd_cleanup);
  38. MODULE_DESCRIPTION(DRIVER_DESC);
  39. MODULE_AUTHOR (DRIVER_AUTHOR);
  40. 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文件中。

  1. static const struct platform_device_id ehci_platform_table[] = {
  2. {"ehci-platform", 0},
  3. {}
  4. };
  5. static struct platform_driver ehci_platform_driver = {
  6. .id_table = ehci_platform_table,
  7. .probe = ehci_platform_probe,
  8. .remove = __devexit_p(ehci_platform_remove),
  9. #ifdef CONFIG_PM_WARP
  10. .resume = sp_ehci_fb_resume,
  11. .suspend = sp_ehci_fb_suspend,
  12. #endif
  13. .shutdown = usb_hcd_platform_shutdown,
  14. .driver = {
  15. .owner = THIS_MODULE,
  16. .name = "ehci-platform",
  17. #ifndef CONFIG_PM_WARP
  18. .pm = &ehci_platform_pm_ops,
  19. #endif
  20. }
  21. };

平台设备一般通过.driver.name进行匹配,匹配成功之后则进入probe函数。

  1. static int __devinit ehci_platform_probe(struct platform_device *dev)
  2. {
  3. struct usb_hcd *hcd;
  4. struct resource *res_mem;
  5. int irq;
  6. int err = -ENOMEM;
  7. struct ehci_hcd_sp *ehci_sp;
  8. BUG_ON(!dev->dev.platform_data);
  9. if (usb_disabled())
  10. return -ENODEV;
  11. /* 获取中断号 */
  12. irq = platform_get_irq(dev, 0);
  13. if (irq < 0) {
  14. pr_err("no irq provieded");
  15. return irq;
  16. }
  17. /* 获取平台设备资源 */
  18. res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
  19. if (!res_mem) {
  20. pr_err("no memory recourse provieded");
  21. return -ENXIO;
  22. }
  23. /* 创建和初始化一个hcd结构体 */
  24. //usb_hcd主要描述主机控制器相关信息
  25. //hc_driver结构体类型内主要是控制主机的钩子函数
  26. hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, dev_name(&dev->dev));
  27. if (!hcd)
  28. return -ENOMEM;
  29. hcd->rsrc_start = res_mem->start;
  30. hcd->rsrc_len = resource_size(res_mem);
  31. //从rsrc_start地址开始申请一个rsrc_len长度的内存,用于后面做io映射
  32. if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
  33. pr_err("controller already in use");
  34. err = -EBUSY;
  35. goto err_put_hcd;
  36. }
  37. //io映射:映射实际io物理地址到虚拟内存
  38. hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
  39. if (!hcd->regs)
  40. goto err_release_region;
  41. tasklet_init(&hcd->host_irq_tasklet, ehci_irq_tasklet, (unsigned long)hcd);
  42. err = usb_add_hcd(hcd, irq, IRQ_TYPE_LEVEL_HIGH);
  43. if (err)
  44. goto err_iounmap;
  45. platform_set_drvdata(dev, hcd);
  46. /****************************************************/
  47. ehci_sp = (struct ehci_hcd_sp *)hcd_to_ehci(hcd);
  48. //创建sys调试节点
  49. if (dev->id < 3) {
  50. device_create_file(&dev->dev, &dev_attr_usb_role_switch);
  51. device_create_file(&dev->dev, &dev_attr_usb_ctrl_reset);
  52. }
  53. device_create_file(&dev->dev, &dev_attr_usb_speed_switch);
  54. device_create_file(&dev->dev, &dev_attr_usb_uphy_swing);
  55. device_create_file(&dev->dev, &dev_attr_usb_disconnect_level);
  56. device_create_file(&dev->dev, &dev_attr_usb_rx_sqct_level);
  57. device_create_file(&dev->dev, &dev_attr_usb_rx_clk_invert);
  58. ehci_sp->flag = 0;
  59. 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);
  60. /* ohci reset uphy notifier */
  61. ehci_sp->ehci_notifier.notifier_call = ehci_notifier_call;
  62. usb_register_notify(&ehci_sp->ehci_notifier);
  63. init_waitqueue_head(&hcd->reset_queue);
  64. hcd->ptr_flag = &ehci_sp->flag;
  65. ehci_sp->reset_thread = kthread_create(ehci_reset_thread,
  66. hcd_to_ehci(hcd),
  67. "ehci_reset_wait_event");
  68. if (IS_ERR(ehci_sp->reset_thread)) {
  69. pr_err("Create EHCI(%d) reset thread fail!\n", dev->id);
  70. return err;
  71. }
  72. /* Tell the thread to start working */
  73. wake_up_process(ehci_sp->reset_thread);
  74. return err;
  75. err_iounmap:
  76. iounmap(hcd->regs);
  77. err_release_region:
  78. release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
  79. err_put_hcd:
  80. usb_put_hcd(hcd);
  81. return err;
  82. }

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。

  1. static const struct hc_driver ehci_platform_hc_driver = {
  2. .description = hcd_name,
  3. .product_desc = "Generic Platform EHCI Controller",
  4. //私有数据大小
  5. .hcd_priv_size = sizeof(struct ehci_hcd_sp),
  6. //中断处理函数
  7. .irq = ehci_irq,
  8. .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
  9. //复位、开始、停止、关闭函数
  10. .reset = ehci_platform_reset,
  11. .start = ehci_run,
  12. .stop = ehci_stop,
  13. .shutdown = ehci_shutdown,
  14. //usb logo test support
  15. .usb_logo_test = ehci_usb_logo_test,
  16. .urb_enqueue = ehci_urb_enqueue,
  17. .urb_dequeue = ehci_urb_dequeue,
  18. .endpoint_disable = ehci_endpoint_disable,
  19. .endpoint_reset = ehci_endpoint_reset,
  20. .get_frame_number = ehci_get_frame,
  21. .hub_status_data = ehci_hub_status_data,
  22. .hub_control = ehci_hub_control,
  23. #if defined(CONFIG_PM)
  24. .bus_suspend = ehci_bus_suspend,
  25. .bus_resume = ehci_bus_resume,
  26. #endif
  27. .relinquish_port = ehci_relinquish_port,
  28. .port_handed_over = ehci_port_handed_over,
  29. .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
  30. .get_port_status_from_register = ehci_get_port_status_from_register,
  31. };

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/246975
推荐阅读
  

闽ICP备14008679号