版权声明:本文为博主原创文章,未经博主允许不得转载。
https://blog.csdn.net/huangweiqing80/article/details/82998850
USB拓扑
对于每个嵌入式的CPU芯片中一般都会集成一个或多个Host 控制器,每个Host控制器其实就是一个PCI设备,挂载在PCI总线上,在Linux系统中,驱动开发人员应该给Host 控制器提供驱动程序,Host 控制器用usb_hcd结构来表示。该结构体在/include/linux/usb/hcd.h中定义。
每个USB Host控制器都会自带一个USB Hub,被称为跟(Root)Hub。这个根Hub又可以接子(Sub)Hub,每个Hub上又可以挂载USB 设备。从广义上来说,USB Hub也算是USB设备。每个根USB Hub下可以直接或间接地连接127个设备
几种USB控制器类型:OHCI,UHCI,EHCI,xHCI
OHCI、UHCI都是USB1.1的接口标准,而EHCI是对应USB2.0的接口标准,最新的xHCI是USB3.0的接口标准。
- OHCI(Open Host Controller Interface)是支持USB1.1的标准,但它不仅仅是针对USB,还支持其他的一些接口,比如它还支持Apple的火线(Firewire,IEEE
1394)接口。与UHCI相比,OHCI的硬件复杂,硬件做的事情更多,所以实现对应的软件驱动的任务,就相对较简单。主要用于非x86的USB,如扩展卡、嵌入式开发板的USB主控。 - UHCI(Universal Host Controller Interface),是Intel主导的对USB1.0、1.1的接口标准,与OHCI不兼容。UHCI的软件驱动的任务重,需要做得比较复杂,但可以使用较便宜、较简单的硬件的USB控制器。Intel和VIA使用UHCI,而其余的硬件提供商使用OHCI。
- EHCI(Enhanced Host Controller Interface),是Intel主导的USB2.0的接口标准。EHCI仅提供USB2.0的高速功能,而依靠UHCI或OHCI来提供对全速(full-speed)或低速(low-speed)设备的支持。
- xHCI(eXtensible Host Controller Interface),是最新最火的USB3.0的接口标准,它在速度、节能、虚拟化等方面都比前面3中有了较大的提高。xHCI支持所有种类速度的USB设备(USB 3.0 SuperSpeed, USB 2.0 Low-, Full-, and High-speed, USB 1.1 Low- and Full-speed)。xHCI的目的是为了替换前面3中(UHCI/OHCI/EHCI)。
USB主机控制器驱动
顾名思义,主机控制器就是用来控制USB设备与CPU之间通信的。CPU要对设备做什么操作,会先通知主机控制器,而不是直接发送指令给USB设备。主机控制器接收到CPU的命令后,会去指挥USB设备完成相应的任务。这样,CPU把命令传给主机控制器后,就不用管余下的工作了,CPU转向处理其他的事情。
USB主机控制器必须由USB主机控制器驱动程序驱动才能运行。USB主机控制器驱动用hc_driver表示,在计算机系统中的每一个主机控制器都有一个对应的hc_driver结构体,该结构体在/include/linux/usb/hcd.h中定义。
下面我们分析一下USB主机控制器驱动,以usb 3.0 xHCI为例来讲解
xHCI是通过platform平台设备驱动模型来驱动的,同样的他有device和driver。
先看device部分
/driver/usb/dwc3/host.c
int dwc3_host_init(struct dwc3 *dwc)
{
struct platform_device *xhci;
int ret;
xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
if (!xhci) {
dev_err(dwc->dev, "couldn't allocate xHCI device\n");
ret = -ENOMEM;
goto err0;
}
dma_set_coherent_mask(&xhci->dev, dwc->dev->coherent_dma_mask);
xhci->dev.parent = dwc->dev;
xhci->dev.dma_mask = dwc->dev->dma_mask;
xhci->dev.dma_parms = dwc->dev->dma_parms;
dwc->xhci = xhci;
ret = platform_device_add_resources(xhci, dwc->xhci_resources,
DWC3_XHCI_RESOURCES_NUM);
if (ret) {
dev_err(dwc->dev, "couldn't add resources to xHCI device\n");
goto err1;
}
ret = platform_device_add(xhci);
if (ret) {
dev_err(dwc->dev, "failed to register xHCI device\n");
goto err1;
}
return 0;
err1:
platform_device_put(xhci);
err0:
return ret;
}
在看driver部分
/drivers/usb/host/xhic.c
static int __init xhci_hcd_init(void)
{
int retval;
retval = xhci_register_pci(); //注册到PCI总线上
if (retval < 0) {
pr_debug("Problem registering PCI driver.\n");
return retval;
}
retval = xhci_register_plat(); //平台驱动
if (retval < 0) {
pr_debug("Problem registering platform driver.\n");
goto unreg_pci;
}
/*
* Check the compiler generated sizes of structures that must be laid
* out in specific ways for hardware access.
*/
BUILD_BUG_ON(sizeof(struct xhci_doorbell_array) != 256*32/8);
BUILD_BUG_ON(sizeof(struct xhci_slot_ctx) != 8*32/8);
BUILD_BUG_ON(sizeof(struct xhci_ep_ctx) != 8*32/8);
/* xhci_device_control has eight fields, and also
* embeds one xhci_slot_ctx and 31 xhci_ep_ctx
*/
BUILD_BUG_ON(sizeof(struct xhci_stream_ctx) != 4*32/8);
BUILD_BUG_ON(sizeof(union xhci_trb) != 4*32/8);
BUILD_BUG_ON(sizeof(struct xhci_erst_entry) != 4*32/8);
BUILD_BUG_ON(sizeof(struct xhci_cap_regs) != 7*32/8);
BUILD_BUG_ON(sizeof(struct xhci_intr_reg) != 8*32/8);
/* xhci_run_regs has eight fields and embeds 128 xhci_intr_regs */
BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8);
return 0;
unreg_pci:
xhci_unregister_pci();
return retval;
}
module_init(xhci_hcd_init);
在来看一下xhci_register_plat这个函数
static struct platform_driver usb_xhci_driver = {
.probe = xhci_plat_probe,
.remove = xhci_plat_remove,
.driver = {
.name = "xhci-hcd",
.pm = DEV_PM_OPS,
.of_match_table = of_match_ptr(usb_xhci_of_match),
},
};
MODULE_ALIAS("platform:xhci-hcd");
int xhci_register_plat(void)
{
return platform_driver_register(&usb_xhci_driver);
}