5.1 使用流程
在 USB 协议中,永远是 Host 主动发起传输。作为一个 Gadget 驱动程序,它永远都是这样:
- 想接收数据:
- 先构造好 usb_request:分配 buffer、设置回调函数
- 把 usb_request 放入队列
- UDC 和 Host 完成 USB 传输,在 usb_request 中填充数据,并触发中断调用 usb_request 的回调函数
- 想发送数据:
- 先构造好 usb_request:分配 buffer、在 buffer 里填充数据、设置回调函数
- 把 usb_request 放入队列
- UDC 和 Host 完成 USB 传输,把 usb_request 的数据发给 Host,并触发中断调用 usb_request 的回调函数
5.2 endpoint 是核心
USB 传输的对象是 endpoint,使用流程如下:
- 功能驱动里,通过 endpoint 描述符表明需要怎样的 endpoint,比如(注意:bEndpointAddress 是表明方向,里面还没有地址,driversusbgadgetfunctionf_loopback.c):
- 功能驱动里,它的 bind 函数根据 endpoint 描述符向底层申请分配 endpoint,比如:
- 功能驱动里,使能 endpoint,比如:
- 功能驱动里,给 endpoint 分配 buffer、设置 usb_request、提交 usb_request,比如:
5.3 回调函数
功能驱动里构造的 usb_request,可以是接收 Host 发来的数据,也可以是向 Host 发送数据。当传输完成,usb_request 的回调函数被调用。
在回调函数里,可以再次提交 usb_request。
怎么调用到回调函数?源头是 UDC 的中断函数。
5.3.1 IMX6ULL
调用关系如下:
// Linux-4.9.88driversusbchipideacore.c
ci_irq
/* Handle device/host interrupt */
if (ci- >role != CI_ROLE_END)
ret = ci_role(ci)- >irq(ci); // udc_irq
udc_irq
if (USBi_UI & intr)
isr_tr_complete_handler(ci);
err = isr_tr_complete_low(hwep);
usb_gadget_giveback_request(&hweptemp- >ep, &hwreq- >req);
req- >complete(ep, req);
5.3.2 STM32MP157
调用关系如下:
// Linux-5.4driversusbdwc2gadget.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);
dwc2_hsotg_handle_outdone(hsotg, idx);
dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, result);
usb_gadget_giveback_request(&hs_ep- >ep, &hs_req- >req);
req- >complete(ep, req);
}
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_complete_in(hsotg, hs_ep);
dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
usb_gadget_giveback_request(&hs_ep- >ep, &hs_req- >req);
req- >complete(ep, req);
}
5.4 f_loopback分析
loopback 就是回环,Host 发数据给 Gadget,然后再读 Gadget 就可以得到原样的数据。
5.4.1 Gadget接收数据
Host 选择某个配置时,默认会选择这个配置下那些接口的第 0 个设置(altsetting);
当 Host 发来 USB_REQ_SET_INTERFACE 请求时,可以选择指定的设置。
所以,我们从 f_loopback.c 的函数loopback_set_alt
开始分析。
调用关系为:
loopback_set_alt
enable_loopback
result = enable_endpoint(cdev, loop, loop- >in_ep);
result = enable_endpoint(cdev, loop, loop- >out_ep);
result = alloc_requests(cdev, loop);
如上图所示,先提交的是 out_req,它在等待 Host 发来数据。
假设断点 loop->out_ep 的 out_req 获得了数据,它的回调函数loopback_complete
被调用,如下:
5.4.2 Gadget 回环数据
5.5 f_sourcesink 分析
前面的 f_loopback 也实现了两个方向的数据传输:Host 到 Gadget、Gadget 到 Host,但是它们之间是有依赖关系的,Host 必须先发送数据再读数据。
f_sourcesink.c 也实现了两个方向的数据传输:Host 到 Gadget、Gadget 到 Host,它们是独立的。
- Host 读 Gadget:驱动程序里构造好数据,Host 可以读到,Gadget 作为源(source)
- Host 写 Gadget:驱动程序里得到 Host 发来的数据,Gadget 作为目的(sink)
5.5.1 Host 写 Gadget
Host 选择某个配置时,默认会选择这个配置下那些接口的第 0 个设置(altsetting);
当 Host 发来 USB_REQ_SET_INTERFACE 请求时,可以选择指定的设置。
所为,我们从 f_sourcesink.c 的函数sourcesink_set_alt
开始分析。
sourcesink_set_alt
enable_source_sink(cdev, ss, alt);
作为"source",函数source_sink_start_ep
会构造数据、提交 usb_request:
当 Host 读取到数据后,usb_request 的回调函数被调用,它只是再次提交 USB 请求,给 Host 继续提供跟上次一样的数据:
5.5.2 Host 读 Gadget
仍然从 f_sourcesink.c 的函数sourcesink_set_alt
开始分析。
sourcesink_set_alt
enable_source_sink(cdev, ss, alt);
作为"sink",函数source_sink_start_ep
会故意把数据设置为 0x55(这是为了调试,当读到数据时可以看到 0x55 被覆盖)、提交 usb_request:
当 Host 发来数据,usb_request 的回调函数被调用,它检查收到的数据,再次提交 usb_request:
-
嵌入式
+关注
关注
5082文章
19104浏览量
304796 -
Linux
+关注
关注
87文章
11292浏览量
209322 -
框架
+关注
关注
0文章
403浏览量
17475
发布评论请先 登录
相关推荐
评论