linux EHCI DRIVER之中断处理函数ehci_irq()分析(二)

先上代码,

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中由qhqtd来完成的,从通信模型的抽象层面上来说,qhqtd分别代表了通信的一方,qh代表USB device方(准确的说是device上的endpoint),qtd代表CPU方(实际上是某块DMA内存区域)。分配给device endpointqh是伴随deviceHCD中一直存在的,只要device没有被拔出,为device endpoint分配的qh就是固定的;相对而言CPU方的用于数据传输的内存区域却是变化的,这也是显而易见的,所以在完成数据传输后分配的qtd会被回收。而scan_async()函数的工作就是去check传输的状况,并回收qtd

    首先了解一下EHCI asynchronous传输是怎样利用qhqtd来完成数据传输的。如图1所示,CPU通过由EHCI HC提供的一组register来与之交互,qhqtd是存在于内存中的数据段,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

    Linuxehci driverqh的组织方式是先分配一个不依附于任何endpointqh数据结构,作为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. 

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值