- 了解usb host driver.
1.USB Subsystem Framework
The following chart shows the framework of the USB subsystem in Linux. Like i2c, the USB subsystem can be divided into three layers: ** Device Driver Layer - USB Core - Controller Driver Layer*
The usb protocol is a complex protocol,The currently involved versions are usb1.0, usb2.0, usb3.0. If you open the kernel usb host directory, you will find that the following contains various forms of controller drivers such as ohci, uhci, ehci, xhci, and whci.
2.USB core 初始化
usb_int 初始化整个usb系统的基础部分。(drivers/usb/core/usb.c)
865 static int __init usb_init(void)
866 {
867 int retval;
868 if (nousb) {
869 pr_info("%s: USB support disabled/n", usbcore_name);
870 return 0;
871 }
872
873 retval = ksuspend_usb_init();
874 if (retval)
875 goto out;
876 retval = bus_register(&usb_bus_type);
877 if (retval)
878 goto bus_register_failed;
879 retval = usb_host_init();
880 if (retval)
881 goto host_init_failed;
882 retval = usb_major_init();
883 if (retval)
884 goto major_init_failed;
885 retval = usb_register(&usbfs_driver);
886 if (retval)
887 goto driver_register_failed;
888 retval = usb_devio_init();
889 if (retval)
890 goto usb_devio_init_failed;
891 retval = usbfs_init();
892 if (retval)
893 goto fs_init_failed;
894 retval = usb_hub_init();
895 if (retval)
896 goto hub_init_failed;
897 retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
898 if (!retval)
899 goto out;
900 ...
916 out:
917 return retval;
918 }
- 注册USB总线
bus_register(&usb_bus_type); - 注册usbfs驱动
usb_register(&usbfs_driver); - 注册usb hub驱动
usb_hub_init -> usb_register(&hub_driver) - 注册通用设备驱动
usb_register_device_driver(&usb_generic_driver, THIS_MODULE)
2.1.usb_hub_init
usb子系统中所有和hub相关的都交由一个内核线程表征, 通过 static struct workqueue_struct *hub_wq 来管理。
5469 static struct usb_driver hub_driver = {
5470 .name = "hub",
5471 .probe = hub_probe,
5472 .disconnect = hub_disconnect,
5473 .suspend = hub_suspend,
5474 .resume = hub_resume,
5475 .reset_resume = hub_reset_resume,
5476 .pre_reset = hub_pre_reset,
5477 .post_reset = hub_post_reset,
5478 .unlocked_ioctl = hub_ioctl,
5479 .id_table = hub_id_table,
5480 .supports_autosuspend = 1,
5481 };
5483 int usb_hub_init(void)
5484 {
5485 if (usb_register(&hub_driver) < 0) {
5486 printk(KERN_ERR "%s: can't register hub driver\n",
5487 usbcore_name);
5488 return -1;
5489 }
5490
5491 /*
5492 * The workqueue needs to be freezable to avoid interfering with
5493 * USB-PERSIST port handover. Otherwise it might see that a full-speed
5494 * device was gone before the EHCI controller had handed its port
5495 * over to the companion full-speed controller.
5496 */
5497 hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0);
5498 if (hub_wq)
5499 return 0;
5500
5501 /* Fall through if kernel_thread failed */
5502 usb_deregister(&hub_driver);
5503 pr_err("%s: can't allocate workqueue for usb hub\n", usbcore_name);
5504
5505 return -1;
5506 }
usb_hub_init()就做两件事:
- 注册驱动–针对hub接口设备的驱动
- 创建工作队列“usb_hub_wq”
记住hub本省也是个usb设备, 跟普通的U盘使用的都是同样的结构体, 当有hub设备被创建时,hub驱动的probe()将会match调用, 那问题来了,一个普通设备是被hub创建的, 那hub设备是谁创建的呢?很显然最初的root hub设备必须是静态创建的, 且这部分代码没放在hub.c, 而是放到了hcd.c, 可以看出一个Host必然有一个root hub, 是绑定的!
int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags)
{
int retval;
struct usb_device *rhdev;
/* 1. 创建一个root hub设备 */
if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) {
dev_err(hcd->self.controller, "unable to allocate root hub\n");
retval = -ENOMEM;
goto err_allocate_root_hub;
}
/* 2. 让hcd与root hub 紧紧地绑在一起! */
hcd->self.root_hub = rhdev;
/* 3. 注册usb设备 */
if ((retval = register_root_hub(hcd)) != 0)
goto err_register_root_hub;
return retval;
}
EXPORT_SYMBOL_GPL(usb_add_hcd);
=========================================
/* root hub 设备默认就接在Host, 不是热拔插 */
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;
int retval;
/* 4. 有效设备地址1~127, root hub默认使用地址1 */
usb_dev->devnum = 1;
/* 5. 直接进入地址阶段 */
usb_set_device_state(usb_dev, USB_STATE_ADDRESS);
/* 6. 直接设置ep0 size=64, 看来是协议规定的了 */
usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
/* 7. root hub 也是设备, 也要获取各种描述符 */
retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
retval = usb_get_bos_descriptor(usb_dev);
/* 8. 注册设备(是注册usb_device, 不是usb_interface) */
retval = usb_new_device (usb_dev);
return retval;
}
3.Ehci host
流程图如下所示:
4.s3c2410 host driver
4.1.Makefile
obj-$(config_usb_ohci_hcd_s3c2410) +=ohci-s3c2410.o
obj-$(config_usb_ohci_hcd) +=ohci-hcd.o
4.2.Kconfig
config usb_ohci_hcd_s3c2410
tristate "ohci support for samsung s3c24xx/s3c64xx soc series"
depends on usb_ohci_hcd&&(arch_s3c24xx || arch_s3c64xx)
default y
--- help ---
enables support for the on-chip ohci controller on
s3c24xx/s3c64xx chips.
config usb_ohci_hcd
tristate "ohci hcd (usb 1.1) support"
depends on has_dma && has_iomem
4.3.Files
- drivers/usb/host/ohci-hcd.c
- drivers/usb/host/ohci-s3c2410.c
代码分析:
drivers/usb/host/ohci-s3c2410.c :
468 static int __init ohci_s3c2410_init(void)
469 {
470 if (usb_disabled())
471 return -ENODEV;
472
473 pr_info("%s: " DRIVER_DESC "\n", hcd_name);
474 ohci_init_driver(&ohci_s3c2410_hc_driver, NULL);
475
476 /*
477 * The Samsung HW has some unusual quirks, which require
478 * Sumsung-specific workarounds. We override certain hc_driver
479 * functions here to achieve that. We explicitly do not enhance
480 * ohci_driver_overrides to allow this more easily, since this
481 * is an unusual case, and we don't want to encourage others to
482 * override these functions by making it too easy.
483 */
484
485 ohci_s3c2410_hc_driver.hub_status_data = ohci_s3c2410_hub_status_data;
486 ohci_s3c2410_hc_driver.hub_control = ohci_s3c2410_hub_control;
487
488 return platform_driver_register(&ohci_hcd_s3c2410_driver);
489 }
490 module_init(ohci_s3c2410_init);
ohci_hcd_s3c2410_driver作为platform_driver实例,指定当与bus上相应的platform_device匹配后需要执行的probe函数。ohci的platform_device在arch/arm/plat-samsumg/devs.c中定义:
939 struct platform_device s3c_device_ohci = {
940 .name = "s3c2410-ohci",
941 .id = -1,
942 .num_resources = ARRAY_SIZE(s3c_usb_resource),
943 .resource = s3c_usb_resource,
944 .dev = {
945 .dma_mask = &samsung_device_dma_mask,
946 .coherent_dma_mask = DMA_BIT_MASK(32),
947 }
948 };
bus_match成功后,执行ohci_hcd_s3c2410_probe函数进行驱动的加载。
static int usb_hcd_s3c2410_probe(const struct hc_driver *driver,
struct platform_device *dev)
{
struct usb_hcd *hcd = NULL;
struct s3c2410_hcd_info *info = dev_get_platdata(&dev->dev);
int retval;
s3c2410_usb_set_power(info, 1, 1);
s3c2410_usb_set_power(info, 2, 1);
hcd = usb_create_hcd(driver, &dev->dev, "s3c24xx");
if (hcd == NULL)
return -ENOMEM;
hcd->rsrc_start = dev->resource[0].start; // 指向hc 的寄存器首地址0x49000000
hcd->rsrc_len = resource_size(&dev->resource[0]);
hcd->regs = devm_ioremap_resource(&dev->dev, &dev->resource[0]); // 转换为虚拟地址
clk = devm_clk_get(&dev->dev, "usb-host");
usb_clk = devm_clk_get(&dev->dev, "usb-bus-host");
s3c2410_start_hc(dev, hcd);
retval = usb_add_hcd(hcd, dev->resource[1].start, 0); //中断号26
device_wakeup_enable(hcd->self.controller);
return 0;
err_ioremap:
s3c2410_stop_hc(dev);
err_put:
usb_put_hcd(hcd);
return retval;
}
4.1.usb_create_hcd
struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
struct device *dev, char *bus_name)
{
struct usb_hcd *hcd;
//申请空间,hcd_priv_size为动态的附加结构长度, 附加在usb_hcd的尾部
// 这段空间用于主机控制器的私有结构,uhci的私有结构为uhci_hcd,用于描述uhci的属性
hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);
if (!hcd)
{
dev_dbg (dev, "hcd alloc failed\n");
return NULL;
}
//连接usb_hcd到pci_device
dev_set_drvdata(dev, hcd);
//初始化引用计数器
kref_init(&hcd->kref);
//初始化usb总线
usb_bus_init(&hcd->self);
//连接pci_device到usb_hcd的usb_bus上
hcd->self.controller = dev;
//设置usb_bus名称
hcd->self.bus_name = bus_name;
//设置是否使用dma
hcd->self.uses_dma = (dev->dma_mask != NULL);
//hcd->rh_timer的注释为drives root-hub polling
//字面意思是一个用于记时执行某函数的结构,例如5ms后执行指定函数
init_timer(&hcd->rh_timer);
hcd->rh_timer.function = rh_timer_func;
hcd->rh_timer.data = (unsigned long) hcd;
#ifdef CONFIG_PM
INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
#endif
//连接hc_driver到usb_hcd上
hcd->driver = driver;
//设置设备名称
hcd->product_desc = (driver->product_desc) ? driver->product_desc : "USB Host Controller";
return hcd;
}
- hcd->rh_timer.function = rh_timer_func;
rh_timer_func这个是root_hub轮询计时器 ,控制器以轮询的方式查找端口变化状态。 rh_timer_func调用usb_hcd_poll_rh_status:
void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
{
struct urb *urb;
int length;
unsigned long flags;
char buffer[4]; /* Any root hubs with > 31 ports? */
//检测主机控制器驱动是否已经注册
if (unlikely(!hcd->rh_registered))
return;
if (!hcd->uses_new_polling && !hcd->status_urb)
return;
//负责检测端口和td队列的状态
length = hcd->driver->hub_status_data(hcd, buffer);
//端口有设备
if (length > 0)
{
/* try to complete the status urb */
spin_lock_irqsave(&hcd_root_hub_lock, flags);
urb = hcd->status_urb;
//检测urb是否存在
if (urb)
{
hcd->poll_pending = 0;
//清除hcd的状态urb
hcd->status_urb = NULL;
//置实际传输长度为1
urb->actual_length = length;
//拷贝端口状态描述组到urb中
memcpy(urb->transfer_buffer, buffer, length);
//卸载urb与节点的连接
usb_hcd_unlink_urb_from_ep(hcd, urb);
spin_unlock(&hcd_root_hub_lock);
//返回urb给驱动程序
usb_hcd_giveback_urb(hcd, urb, 0);
spin_lock(&hcd_root_hub_lock);
}
else
{
length = 0;
hcd->poll_pending = 1;
}
spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
}
/* The USB 2.0 spec says 256 ms. This is close enough and won't
* exceed that limit if HZ is 100. The math is more clunky than
* maybe expected, this is to make sure that all timers for USB devices
* fire at the same time to give the CPU a break inbetween */
//每间隔HZ/4就执行一次hcd->rh_timer中指定的函数
if (hcd->uses_new_polling ? hcd->poll_rh :(length == 0 && hcd->status_urb != NULL))
mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
}
最终调用流程:
->usb_hcd_poll_rh_status //hcd.c
->hcd->driver->hub_status_data(hcd, buffer)
->usb_hcd_unlink_urb_from_ep(hcd, urb);
->usb_hcd_giveback_urb(hcd, urb, 0)
->usb_giveback_urb_bh(); //tasklet_hi_schedule(&bh->bh);
->__usb_hcd_giveback_urb(urb);
->urb->complete(urb); //hub_irq
->hub_irq //hub.c usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
->kick_hub_wq(hub);
->hub_event //INIT_WORK(&hub->events, hub_event);
->port_event(hub, i);
->hub_port_connect_change
->hub_port_connect
->hub_port_init
->usb_new_device(udev);
->usb_enumerate_device(udev);//开始枚举
->device_add(&udev->dev);//枚举完毕后加载设备驱动
device_add函数会出发总线的通知链发送通知,最终会调用总线的match方法。usb设备和驱动一旦match,则会调用驱动的drvwrap.driver.probe方法:
- 若是设备则通过driver.c的usb_register_device_driver函数调用usb_probe_device方法
- 若是接口则通过driver.c的usb_register_driver函数调用usb_probe_interface方法
- 假设是U盘接入,则调用mass_storage驱动的probe,并在probe中使用usb_alloc_urb分配urb,最后usb_submit_urb提交urb。
Note: usb_hcd_poll_rh_status 其他调用流程:
usb_hcd_irq->ehci_irq->usb_hcd_poll_rh_status
4.2.usb_add_hcd 添加hcd到系统中
-
向usb系统中注册一条总线
usb_register_bus(&hcd->self)) -
创建一个USB设备,作为根hub
rhdev = usb_alloc_dev(NULL, &hcd->self, 0)
hcd->self.root_hub = rhdev -
usb_hcd_request_irqs:申请中断并注册中断函数usb_hub_irq
例如触发中断流程:
usb_hcd_irq->ehci_irq->usb_hcd_resume_root_hub->queue_work(pm_wq, &hcd->wakeup_work);->hcd_resume_work->usb_runtime_resume->usb_resume_both->usb_resume_device->hub_resume->hub_activate->kick_hub_wq->queue_work(hub_wq, &hub->events)->hub_events
- 为该总线注册根hub
register_root_hub(hcd)- 获取根hub的描述信息
usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE); - 向usb系统中添加了一个usb设备
usb_new_device (usb_dev);
- 获取根hub的描述信息
5.usb suspend/resume
drivers/usb/core/usb.c:
495 static const struct dev_pm_ops usb_device_pm_ops = {
496 .prepare = usb_dev_prepare,
497 .complete = usb_dev_complete,
498 .suspend = usb_dev_suspend,
499 .resume = usb_dev_resume,
500 .freeze = usb_dev_freeze,
501 .thaw = usb_dev_thaw,
502 .poweroff = usb_dev_poweroff,
503 .restore = usb_dev_restore,
504 .runtime_suspend = usb_runtime_suspend,
505 .runtime_resume = usb_runtime_resume,
506 .runtime_idle = usb_runtime_idle,
507 };
522 struct device_type usb_device_type = {
523 .name = "usb_device",
524 .release = usb_release_dev,
525 .uevent = usb_dev_uevent,
526 .devnode = usb_devnode,
527 #ifdef CONFIG_PM
528 .pm = &usb_device_pm_ops,
529 #endif
530 };
- suspend 流程:
usb_dev_suspend->usb_suspend->usb_suspend_both->usb_suspend_interface->hub_suspend
refer to
- https://www.shuzhiduo.com/A/xl56nWQo5r/
- https://blog.csdn.net/chenliang0224/article/details/79692374
- https://blog.csdn.net/chenliang0224/article/details/79692057