先上代码,
1. static void scan_async (struct ehci_hcd *ehci)
2. {
3. struct ehci_qh *qh;
4. bool check_unlinks_later = false;
5.
6. ehci->qh_scan_next = ehci->async->qh_next.qh;
7. while (ehci->qh_scan_next) {
8. qh = ehci->qh_scan_next;
9. ehci->qh_scan_next = qh->qh_next.qh;
10.
11. /* clean any finished work for this qh */
12. if (!list_empty(&qh->qtd_list)) {
13. int temp;
14.
15. /*
16. * Unlinks could happen here; completion reporting
17. * drops the lock. That's why ehci->qh_scan_next
18. * always holds the next qh to scan; if the next qh
19. * gets unlinked then ehci->qh_scan_next is adjusted
20. * in single_unlink_async().
21. */
22. temp = qh_completions(ehci, qh);
23. if (unlikely(temp)) {
24. start_unlink_async(ehci, qh);
25. } else if (list_empty(&qh->qtd_list)
26. && qh->qh_state == QH_STATE_LINKED) {
27. qh->unlink_cycle = ehci->async_unlink_cycle;
28. check_unlinks_later = true;
29. }
30. }
31. }
32.
33. /*
34. * Unlink empty entries, reducing DMA usage as well
35. * as HCD schedule-scanning costs. Delay for any qh
36. * we just scanned, there's a not-unusual case that it
37. * doesn't stay idle for long.
38. */
39. if (check_unlinks_later && ehci->rh_state == EHCI_RH_RUNNING &&
40. !(ehci->enabled_hrtimer_events &
41. BIT(EHCI_HRTIMER_ASYNC_UNLINKS))) {
42. ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true);
43. ++ehci->async_unlink_cycle;
44. }
45. }
46.
Asynchronous传输在EHCI中由qh和qtd来完成的,从通信模型的抽象层面上来说,qh和qtd分别代表了通信的一方,qh代表USB device方(准确的说是device上的endpoint),qtd代表CPU方(实际上是某块DMA内存区域)。分配给device endpoint的qh是伴随device在HCD中一直存在的,只要device没有被拔出,为device endpoint分配的qh就是固定的;相对而言CPU方的用于数据传输的内存区域却是变化的,这也是显而易见的,所以在完成数据传输后分配的qtd会被回收。而scan_async()函数的工作就是去check传输的状况,并回收qtd。
首先了解一下EHCI asynchronous传输是怎样利用qh和qtd来完成数据传输的。如图1所示,CPU通过由EHCI HC提供的一组register来与之交互,qh和qtd是存在于内存中的数据段,HC需要从内存中将之读取到它的缓存中,HC正是通过EHCI提供的这组register中的AsyncListAddr获取到内存中qh的地址的。具体的过程是,CPU先把多个qh组成的一个循环队列头qh的物理地址写入AsyncListAddr中,HC再根据AsyncListAddr的值去内存寻址qh的所在位置。构成一个qh循环队列是通过qh结构体中的头四个字节,去指向下一个qh的物理地址实现的,并且最后一个qh将指回首个qh,而qtd则使依附在qh中的,即HC通过AsyncListAddr找到qh,在通过qh找到qtd。更详细的细节可参考EHCI spec。
图1
Linux中ehci driver对qh的组织方式是先分配一个不依附于任何endpoint的qh数据结构,作为qh循环队列的头,这个qh不用于实际的数据传输,只用于寻址下一个qh,这个qh的物理地址写入AsyncListAddr,如图1所示,并把qh的虚拟地址保存在ehci->async中。
回到qtd回收的讨论中来,scan_async()函数中第25行的qh_completions()函数完成了真正的qtd扫尾工作,那就先从这个qh_completions()说起,代码如下。
1. /*
2. * Process and free completed qtds for a qh, returning URBs to drivers.
3. * Chases up to qh->hw_current. Returns nonzero if the caller should
4. * unlink qh.
5. */
6. static unsigned
7. qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
8. {
9. struct ehci_qtd *last, *end = qh->dummy;
10. struct list_head *entry, *tmp;
11.