mirror of
https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git
synced 2025-11-30 12:01:02 +08:00
发布: 11.4_从获取描述符的角度理解Gadget框架
This commit is contained in:
@@ -151,7 +151,7 @@ Host和设备都会维护自己的数据包切换机制,当数据包成功发
|
||||
|
||||
比如:
|
||||
|
||||
* Host发送DATA0给设备,设备返回ACK表示成功接收,设备期待下一个数据时DATA1
|
||||
* Host发送DATA0给设备,设备返回ACK表示成功接收,设备期待下一个数据是DATA1
|
||||
* 但是Host没有接收到ACK,Host认为数据没有发送成功,Host继续使用DATA0发送上一次的数据
|
||||
* 设备再次接收到DATA0数据包,它就知道:哦,这是重传的数据包
|
||||
|
||||
|
||||
BIN
IMX6ULL/doc_pic/12_USB/11.4_从获取描述符的角度理解Gadget框架.tif
Normal file
BIN
IMX6ULL/doc_pic/12_USB/11.4_从获取描述符的角度理解Gadget框架.tif
Normal file
Binary file not shown.
@@ -229,7 +229,165 @@ USB传输的核心是endpoint,使用endpoint可以收发数据。在endpoint
|
||||
|
||||
|
||||
|
||||
## 4. 从获得描述符的角度理解Gadget框架
|
||||
## 4. 从获取描述符的角度理解Gadget框架
|
||||
|
||||
安装好gadget驱动程序后(比如modprobe g_zero), 它只是构造好了各类描述符。在设备的枚举过程会读取描述符,枚举过程要做的事情可以参考《05_USB描述符.md》的《4. 设备枚举过程示例》。
|
||||
|
||||
使用OTG线连接电脑和开发板时,电脑软件会执行如下操作:
|
||||
|
||||
* 使用控制传输,读取设备信息(设备描述符):第一次读取时,它只需要得到8字节数据,因为第8个数据表示端点0能传输的最大数据长度。
|
||||
* Host分配地址给设备,然后把新地址发给设备
|
||||
* 使用新地址,重新读取设备描述符,设备描述符长度是18
|
||||
* 读取配置描述符:它传入的长度是255,想一次性把当前配置描述符、它下面的接口描述符、端点描述符全部读出来
|
||||
* 读取字符描述符
|
||||
|
||||
上述过程里,设备方都是接收到Host发给endpoint 0的数据,然后做出回应。不同的Gadget设备,在返回描述符给主机时,这些操作都是一样的,只是回应的数据不同而已。源码分析的起点都是某个中断函数:
|
||||
|
||||
* IMX6ULL:ci_irq(drivers/usb/chipidea/core.c)
|
||||
* STM32MP157: dwc2_hsotg_irq(drivers/usb/dwc2/gadget.c)
|
||||
|
||||
### 4.1 IMX6ULL的核心函数
|
||||
|
||||
IMX6ULL芯片中USB控制器型号是chipidea,在`Linux-4.9.88\drivers\usb\chipidea\core.c`中注册了中断函数:
|
||||
|
||||
```c
|
||||
ci_hdrc_probe
|
||||
ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED,
|
||||
ci->platdata->name, ci);
|
||||
```
|
||||
|
||||
发生中断后,对于endpoint 0的数据处理流程如下:
|
||||
|
||||
```shell
|
||||
// Linux-4.9.88\drivers\usb\chipidea\core.c
|
||||
ci_irq
|
||||
/* Handle device/host interrupt */
|
||||
if (ci->role != CI_ROLE_END)
|
||||
ret = ci_role(ci)->irq(ci); // udc_irq
|
||||
|
||||
// Linux-4.9.88\drivers\usb\chipidea\udc.c
|
||||
udc_irq
|
||||
if (USBi_UI & intr)
|
||||
// Linux-4.9.88\drivers\usb\chipidea\udc.c
|
||||
isr_tr_complete_handler(ci);
|
||||
/* Only handle setup packet below */
|
||||
if (i == 0 &&
|
||||
hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(0)))
|
||||
// Linux-4.9.88\drivers\usb\chipidea\udc.c
|
||||
isr_setup_packet_handler(ci);
|
||||
```
|
||||
|
||||
|
||||
|
||||
函数`isr_setup_packet_handler`就是处理endpoint 0接收到的控制传输的关键。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 4.2 STM32MP157的核心函数
|
||||
|
||||
STM32MP157芯片中USB控制器型号是dwc2,在`Linux-5.4\drivers\usb\dwc2\gadget.c`中注册了中断函数:
|
||||
|
||||
```shell
|
||||
dwc2_gadget_init
|
||||
ret = devm_request_irq(hsotg->dev, hsotg->irq, dwc2_hsotg_irq,
|
||||
IRQF_SHARED, dev_name(hsotg->dev), hsotg);
|
||||
```
|
||||
|
||||
发生中断后,函数`dwc2_hsotg_irq`被调用,它处理endpoint中断有两种方法:
|
||||
|
||||
* 使用DMA时:调用`dwc2_hsotg_epint`来处理
|
||||
* 不使用DMA时:调用`dwc2_hsotg_handle_rx`来处理
|
||||
|
||||
以`dwc2_hsotg_epint`为例进行分析,对于endpoint 0的数据处理流程如下:
|
||||
|
||||
```shell
|
||||
// Linux-5.4\drivers\usb\dwc2\gadget.c
|
||||
dwc2_hsotg_irq
|
||||
// 处理endpoint中断
|
||||
for (ep = 0; ep < hsotg->num_of_eps && daint_out;
|
||||
ep++, daint_out >>= 1) {
|
||||
if (daint_out & 1)
|
||||
dwc2_hsotg_epint(hsotg, ep, 0);
|
||||
}
|
||||
|
||||
for (ep = 0; ep < hsotg->num_of_eps && daint_in;
|
||||
ep++, daint_in >>= 1) {
|
||||
if (daint_in & 1)
|
||||
dwc2_hsotg_epint(hsotg, ep, 1);
|
||||
}
|
||||
```
|
||||
|
||||
函数`dwc2_hsotg_epint`中,对于endpoint 0的处理如下:
|
||||
|
||||
```shell
|
||||
// Linux-5.4\drivers\usb\dwc2\gadget.c
|
||||
dwc2_hsotg_epint
|
||||
if (idx == 0 && !hs_ep->req)
|
||||
dwc2_hsotg_enqueue_setup(hsotg);
|
||||
```
|
||||
|
||||
|
||||
|
||||
函数`dwc2_hsotg_enqueue_setup`被调用时,Gadget设备已经收到了SETUP令牌包,但是还没收到DATA0令牌包。`dwc2_hsotg_enqueue_setup`的作用是,设置、启动一个request,核心在于设置了request的complete函数(当SETTUP事务完成后这个函数被调用):
|
||||
|
||||

|
||||
|
||||
当控制传输的"setup事务"完成时,函数`dwc2_hsotg_complete_setup`被调用。
|
||||
|
||||
|
||||
|
||||
### 4.3 如何处理控制传输
|
||||
|
||||
无论是IMX6ULL的函数`isr_setup_packet_handler`,还是STM32M157的函数`dwc2_hsotg_complete_setup`,它们都是在Gadget设备收到"SETUP事务"后才被调用。接收完"SETUP事务"后,就可以从里面知道这个控制传输想做什么(req.bRequest是什么),然后就可以处理它了。
|
||||
|
||||
怎么处理呢?可以分为3层:
|
||||
|
||||

|
||||
|
||||
* UDC驱动程序:类似"设置地址"的控制传输,在底层的UDC驱动程序里就可以处理,
|
||||
|
||||
* 这类请求有:
|
||||
|
||||
```shell
|
||||
USB_REQ_SET_ADDRESS
|
||||
USB_REQ_SET_FEATURE // 有一些请求可能需要上报改gadget driver
|
||||
USB_REQ_CLEAR_FEATURE // 有一些请求可能需要上报改gadget driver
|
||||
USB_REQ_GET_STATUS // 有一些请求可能需要上报改gadget driver
|
||||
```
|
||||
* 驱动程序位置
|
||||
|
||||
```shell
|
||||
IMX6ULL: Linux-4.9.88\drivers\usb\chipidea\udc.c, 函数isr_setup_packet_handler
|
||||
STM32MP157: Linux-5.4\drivers\usb\dwc2\gadget.c, 函数dwc2_hsotg_complete_setup
|
||||
```
|
||||
|
||||
* gadget driver:涉及描述符的操作
|
||||
|
||||
* 这类请求有:
|
||||
|
||||
```shell
|
||||
USB_REQ_GET_DESCRIPTOR
|
||||
USB_REQ_SET_CONFIGURATION
|
||||
USB_REQ_GET_CONFIGURATION
|
||||
USB_REQ_SET_INTERFACE
|
||||
USB_REQ_GET_INTERFACE
|
||||
USB_REQ_GET_STATUS // 底层UDC驱动无法处理的话, gadget driver来处理
|
||||
USB_REQ_CLEAR_FEATURE // 底层UDC驱动无法处理的话, gadget driver来处理
|
||||
USB_REQ_SET_FEATURE // 底层UDC驱动无法处理的话, gadget driver来处理
|
||||
```
|
||||
|
||||
* 驱动程序位置
|
||||
|
||||
```shell
|
||||
文件:drivers\usb\gadget\composite.c
|
||||
函数:composite_setup
|
||||
```
|
||||
|
||||
* usb_configuration或usb_function的处理:这是二选一的。大部分设备使用控制传输实现标准的USB请求,但是也可以用控制传输来进行实现相关的请求,对于这些非标准的请求,就需要上层驱动来处理。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
IMX6ULL/doc_pic/12_USB/pic/92_dwc2_enqueue_setup.png
Normal file
BIN
IMX6ULL/doc_pic/12_USB/pic/92_dwc2_enqueue_setup.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 73 KiB |
BIN
IMX6ULL/doc_pic/12_USB/pic/93_xxx_setup.png
Normal file
BIN
IMX6ULL/doc_pic/12_USB/pic/93_xxx_setup.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
Reference in New Issue
Block a user