7.12.4. 设计说明¶

整个 USB 系统的软件栈如上图所示,本文仅描述其中的 HCD (Host Controller Driver) 和 DCD (Device Controller Driver)。
7.12.4.1. USB Host Controller Driver¶
下面以 EHCI 为例,说明 HCD 软件设计思想。
7.12.4.1.1. 源码说明¶
| 相关模块 | 源码路径 | 
|---|---|
| EHCI | source\linux-5.10\drivers\usb\host\ehci-aic.c  | 
7.12.4.1.2. 模块架构¶

从 HCD (Host Controller Driver) 的框架图中可以看到,HCD 主要提供了两大功能:
- 普通 URB 数据收发功能。 
将 USB Class Driver 下发的 URB,按照硬件控制器要求的格式,按分类发送到硬件 List 当中。
- RootHub URB 的处理功能。 
对于 RootHub Driver 下发的 ep0 控制命令 URB,系统不会发送到硬件控制器之上,而是转发给 HCD 使用软件来模拟执行。
对于 RootHub Driver 下发的端口状态查询 URB,通过响应中断进行上报。
7.12.4.1.3. 关键流程¶
7.12.4.1.3.1. 初始化流程¶
HCD 驱动的入口是 platform 驱动,初始化流程先获取 irq、reg、clk、reset 等资源并进行初始化,最后调用 usb_add_hcd() 向系统中注册。
大致的流程如下:
|-->ehci_platform_init()
    |-->ehci_init_driver()
    |-->platform_driver_register()
        |-->aic_ehci_platform_probe()
            |-->hcd = usb_create_hcd()
            |-->irq = platform_get_irq(dev, 0);
            |-->priv->clks[i] = of_clk_get(dev->dev.of_node, i);
            |-->priv->rst[i] = devm_reset_control_get_shared_by_index(&dev->dev, i);
            |-->hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
            |-->aic_ehci_platform_power_on()
                |-->reset_control_deassert(priv->rst[i]);
                |-->clk_prepare_enable(priv->clks[i]);
            |-->usb_add_hcd(hcd, irq, IRQF_SHARED);
7.12.4.1.3.2. 普通 URB 处理流程¶

如上图所示,一个普通 urb 的处理分为两步:
- urb enqueue。首先调用 hcd 的 - .urb_enqueue()函数,将需要传输的数据插入到硬件控制器的链表当中。
- urb complete。在链表中的一帧数据传输完成后硬件会产生 - complete中断,在中断服务程序中对相应 urb 发送- complete信号,让- usb_start_wait_urb()的流程继续执行。
7.12.4.1.3.3. Roothub URB 处理流程¶

如上图所示,roothub urb 的处理分为两种类型:
- ep0 control urb。对于 roothub control urb,HCD 需要使用软件来模拟,实际上 urb 没有发送到硬件控制器中,因为是软件模拟所以无需等待 - complete可以立即释放。
- 获取端口状态 urb。这类 urb 会阻塞等待端口状态改变,一旦端口状态改变会触发硬件中断,在中断处理中唤醒对应 urb 的 - complete信号,让- usb_start_wait_urb()的流程继续执行。
7.12.4.1.4. 数据结构¶
7.12.4.1.4.1. ehci_hc_driver¶
HCD 核心的数据结构为 hc_driver,EHCI 实现了以下的核心函数:
static const struct hc_driver ehci_hc_driver = {
    .description =          hcd_name,
    .product_desc =         "EHCI Host Controller",
    .hcd_priv_size =        sizeof(struct ehci_hcd),
    /*
    * generic hardware linkage
    */
    .irq =                  ehci_irq,
    .flags =                HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,
    /*
    * basic lifecycle operations
    */
    .reset =                ehci_setup,
    .start =                ehci_run,
    .stop =                 ehci_stop,
    .shutdown =             ehci_shutdown,
    /*
    * managing i/o requests and associated device resources
    */
    .urb_enqueue =          ehci_urb_enqueue,
    .urb_dequeue =          ehci_urb_dequeue,
    .endpoint_disable =     ehci_endpoint_disable,
    .endpoint_reset =       ehci_endpoint_reset,
    .clear_tt_buffer_complete =     ehci_clear_tt_buffer_complete,
    /*
    * scheduling support
    */
    .get_frame_number =     ehci_get_frame,
    /*
    * root hub support
    */
    .hub_status_data =      ehci_hub_status_data,
    .hub_control =          ehci_hub_control,
    .bus_suspend =          ehci_bus_suspend,
    .bus_resume =           ehci_bus_resume,
    .relinquish_port =      ehci_relinquish_port,
    .port_handed_over =     ehci_port_handed_over,
    .get_resuming_ports =   ehci_get_resuming_ports,
    /*
    * device support
    */
    .free_dev =             ehci_remove_device,
};
7.12.4.1.5. 接口设计¶
7.12.4.1.5.1. ehci_urb_enqueue¶
| 函数原型 | int ehci_urb_enqueue (struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) | 
|---|---|
| 功能说明 | 接收上层传入的 urb,并将其压入 EHCI 的硬件队列。 | 
| 参数定义 | hcd:当前 hcd 控制结构  | 
| 返回值 | 0,成功; < 0,失败 | 
| 注意事项 | 
7.12.4.1.5.2. ehci_hub_control¶
| 函数原型 | int ehci_hub_control (struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) | 
|---|---|
| 功能说明 | 处理 roothub 相关的 control 命令。 | 
| 参数定义 | hcd:当前 hcd 控制结构  | 
| 返回值 | 0,成功; < 0,失败 | 
| 注意事项 | 
7.12.4.1.5.3. ehci_hub_status_data¶
| 函数原型 | int ehci_hub_status_data (struct usb_hcd *hcd, char *buf) | 
|---|---|
| 功能说明 | 查询 hub 端口状态。 | 
| 参数定义 | hcd:当前 hcd 控制结构  | 
| 返回值 | >0,成功获取端口状态的长度; = 0,失败 | 
| 注意事项 | 
7.12.4.2. USB Device Controller Driver¶
Linux 利用 Device Controller Driver 把整个单板模拟成一个 USB Device 设备。
7.12.4.2.1. 源码说明¶
| 相关模块 | 源码路径 | 
|---|---|
| AIC UDC | source\linux-5.10\drivers\usb\gadget\udc\aic_udc.c  | 
7.12.4.2.2. 模块架构¶

从上述 DCD (Device Controller Driver) 的框架图中可以看到,DCD 主要提供了两大功能:
- 普通 ep 的 usb request 处理。 
DCD 提供了一个 endpoint 资源池,Gadget Function Driver 可以在这个资源池中分配需要的 endpoint,这部分的 endpoint 就称为 普通 ep。
- ep0 的 usb request 处理。 
因为 DCD 对外呈现为一个 USB Device,USB Device 的 ep0 是管理通道是需要特殊处理的。对 ep0 传达过来的 control 数据需要在 DCD 层开始解析。
7.12.4.2.3. 关键流程¶
7.12.4.2.3.1. 初始化流程¶
DCD 驱动的入口是 platform 驱动,初始化流程先获取 irq、reg、clk、reset 等资源并进行初始化,最后调用 usb_add_gadget_udc() 向系统中注册。
大致的流程如下:
|-->aic_udc_probe()
    |-->gg->regs = devm_ioremap_resource(&dev->dev, res);
    |-->gg->reset = devm_reset_control_get_optional(gg->dev, "aicudc");
    |-->gg->reset_ecc = devm_reset_control_get_optional_shared(gg->dev,"aicudc-ecc");
    |-->gg->clks[i] = of_clk_get(gg->dev->of_node, i);
    |-->aic_gadget_init(gg);
        |-->aic_low_hw_enable()
            |-->clk_prepare_enable(gg->clks[i]);
            |-->reset_control_deassert(gg->reset);
            |-->reset_control_deassert(gg->reset_ecc);
    |-->res = platform_get_resource(dev, IORESOURCE_IRQ, 0);
    |-->gg->irq = res->start;
    |-->usb_add_gadget_udc(gg->dev, &gg->gadget);
7.12.4.2.3.2. ep 分配流程¶

如上图所示,ep 资源的操作分为两部分:
- ep 资源池初始化。在 udc 驱动初始化的时候同时初始化了 ep 资源池,这样就决定了当前有多少个 ep 资源可用。 
- ep 资源的分配。gadget composite device 可以配置多个 interface 即 gadget function driver,当 function driver 启用时,会从资源池中分配需要的 ep。如果配置的 function driver 过多,就可能会分配失败。 
7.12.4.2.3.3. 普通 ep 的 request 处理¶

如上图所示,对于普通 ep 的 reuqest 数据收发分为两步:
- request enqueue。首先调用 udc 的 - .queue()函数,将需要传输的数据插入到硬件控制器对应的 ep 寄存器当中。
- complete callback。ep 数据收发完成会产生 - transfer complete中断,在中断服务程序中调用- complete回调函数,结束整个 request 传输。
7.12.4.2.4. 数据结构¶
7.12.4.2.4.1. aic_usb_ep_ops¶
AIC UDC 驱动核心的数据结构为 usb_ep_ops, 实现了 op 操作的相关函数:
static const struct usb_ep_ops aic_usb_ep_ops = {
    .enable                 = aic_ep_enable,
    .disable                = aic_ep_disable,
    .alloc_request          = aic_ep_alloc_request,
    .free_request           = aic_ep_free_request,
    .queue                  = aic_ep_queue_request,
    .dequeue                = aic_ep_dequeue_request,
    .set_halt               = aic_ep_sethalt,
};
7.12.4.2.5. 接口设计¶
7.12.4.2.5.1. aic_ep_queue_request¶
| 函数原型 | int aic_ep_queue_request(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags) | 
|---|---|
| 功能说明 | 接收上层传入的 request,并将其配置到 ep 寄存器中。 | 
| 参数定义 | ep:当前 ep 控制结构  | 
| 返回值 | 0,成功; < 0,失败 | 
| 注意事项 | 
7.12.4.2.5.2. aic_ep0_process_control¶
| 函数原型 | void aic_ep0_process_control(struct aic_usb_gadget *gg, struct usb_ctrlrequest *ctrl) | 
|---|---|
| 功能说明 | 处理 ep0 接收到的 control 数据包。 | 
| 参数定义 | gg:当前 gadget 控制结构  | 
| 返回值 | 0,成功; < 0,失败 | 
| 注意事项 | 
