Chinaunix首页 | 论坛 | 博客
  • 博客访问: 424497
  • 博文数量: 177
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 20
  • 用 户 组: 普通用户
  • 注册时间: 2014-05-22 19:16
文章分类

全部博文(177)

文章存档

2017年(1)

2016年(12)

2015年(112)

2014年(52)

我的朋友

分类: LINUX

2015-03-25 20:05:32

原文地址:linux urb结束处理 作者:steven_miao

结束处理

谨以此文纪念过往的岁月

一.  前言

在前文中我们看过了urb的入队以及tded的处理,那在该文中来看我们td处理完成后,HCD的处理。神说:有始必须要有终。咱的urb的入队了,OHCI处理了,咱也不能不管啊,咱还是得处理他,要不然要它传输数据干什么啊!闲话不说,咱们开始干活。

二.  OHCI中断

OHCI中我们有很多程序咱们并没有看,其中一个最主要的就是OHCI的中断处理函数。那在本文中,我们来其具体的实现。我们将其中关于比较特殊的usb处理删除。其实说到中断,我们不得不来看OHCI的中断类型。

#define OHCI_INTR_SO    (1 << 0)    --调度溢出

usb在调度当前帧和更新溢出,该bit位会被设置同时导致HcCommandStatusSchedulingOverrunCount域增加

#define OHCI_INTR_WDH       (1 << 1)  --回写done_head

hcHcDoneHead写到HccaDoneHead时被设置,直到该bit位被清零才会真正的更新HccaDoneHeadHcd在保存了HccaDoneHead的内容后可以清除该位

#define OHCI_INTR_SF     (1 << 2)  --帧传输开始

在每一帧开始和更新HccaFrameNumber后设置该位,一般HC同时产生SOF

#define OHCI_INTR_RD    (1 << 3)  --监测恢复

HC监测到一个usb设备重新恢复

#define OHCI_INTR_UE    (1 << 4)

HC监测到usb系统错误

#define OHCI_INTR_FNO  (1 << 5)  --帧计数溢出

HcFmNumber的最高位值改变,从0->11->0,或者在HccaFrameNumber更新后,设置该位。

#define OHCI_INTR_RHSC       (1 << 6)  --hub状态改变

#define OHCI_INTR_OC    (1 << 30)       --所有者改变

#define OHCI_INTR_MIE  (1 << 31)

static irqreturn_t ohci_irq (struct usb_hcd *hcd)

{

       struct ohci_hcd          *ohci = hcd_to_ohci (hcd);

       struct ohci_regs __iomem *regs = ohci->regs;

       int                 ints;

 

       ints = ohci_readl(ohci, ®s->intrstatus);  --读取中断状态值

       if (ints == ~(u32)0) {

              disable (ohci);

              return IRQ_HANDLED;

       }

       ints &= ohci_readl(ohci, ®s->intrenable); --读取中断使能寄存器

       if (ints == 0)

return IRQ_NOTMINE;

       if (ints & OHCI_INTR_UE) { --usb系统错误,当然重新启动OHCI

              disable (ohci);

              ohci_dump (ohci, 1);

              ohci_usb_reset (ohci);

       }

       if (ints & OHCI_INTR_RHSC) {

              ohci->next_statechange = jiffies + STATECHANGE_DELAY;

              ohci_writel(ohci, OHCI_INTR_RD | OHCI_INTR_RHSC,®s->intrstatus);

              ohci_writel(ohci, OHCI_INTR_RHSC, ®s->intrdisable);

              (hcd);  --重新轮询root hub的状态

       }

       else if (ints & OHCI_INTR_RD) {

              ohci_writel(ohci, OHCI_INTR_RD, ®s->intrstatus);

              hcd->poll_rh = 1;

              if (ohci->autostop) {

                     spin_lock (&ohci->lock);

                     ohci_rh_resume (ohci);

                     spin_unlock (&ohci->lock);

              } else

                     usb_hcd_resume_root_hub(hcd);

       }

       if (ints & OHCI_INTR_WDH) { --我们真正关心的是这个分支。

              spin_lock (&ohci->lock);

              dl_done_list (ohci);

              spin_unlock (&ohci->lock);

       }

       spin_lock (&ohci->lock);

       if (ohci->ed_rm_list)

              finish_unlinks (ohci, ohci_frame_no(ohci));

       if ((ints & OHCI_INTR_SF) != 0&& !ohci->ed_rm_list&& !ohci->ed_to_check

              && HC_IS_RUNNING(hcd->state))

              ohci_writel (ohci, OHCI_INTR_SF, ®s->intrdisable);

       spin_unlock (&ohci->lock);

 

       if (HC_IS_RUNNING(hcd->state)) {

              ohci_writel (ohci, ints, ®s->intrstatus);

              ohci_writel (ohci, OHCI_INTR_MIE, ®s->intrenable);

              (void) ohci_readl (ohci, &ohci->regs->control);

       }

       return IRQ_HANDLED;

}

OHCI处理td时是不管这个td有没有真正的处理成功,就把它放到done_head中。

static void dl_done_list (struct ohci_hcd *ohci)

{

       struct td *td = dl_reverse_done_list (ohci);

       while (td) {

              struct td *td_next = td->next_dl_td;

              takeback_td(ohci, td);

              td = td_next;

       }

}

static struct td *dl_reverse_done_list (struct ohci_hcd *ohci)

{

       u32        td_dma;

       struct td *td_rev = NULL;

       struct td *td = NULL;

       td_dma = hc32_to_cpup (ohci, &ohci->hcca->done_head);

       ohci->hcca->done_head = 0;

       wmb();

       while (td_dma) {

              int          cc;

              td = (ohci, td_dma);

              td->hwINFO |= cpu_to_hc32 (ohci, TD_DONE);

              cc = TD_CC_GET (hc32_to_cpup (ohci, &td->hwINFO));

       if (cc != TD_CC_NOERROR&& (td->ed->hwHeadP&cpu_to_hc32 (ohci, ED_H)))

                     ed_halted(ohci, td, cc);  --如果td传输有错误则停止ed

              td->next_dl_td = td_rev;

              td_rev = td;

              td_dma = hc32_to_cpup (ohci, &td->hwNextTD);

       }

       return td_rev;

}

dma_to_td根据td物理地址来查找td,在上文中我们也没有将是如何将td如何保存在ohci->hash中的。从字面意思就可以理解其实是将td保存到hash表中,其保存的规则是:

#define TD_HASH_FUNC(td_dma)  ((td_dma ^ (td_dma >> 6)) % TD_HASH_SIZE)

关于hash表是什么东西是怎么用的,这里就不说了,以前看cdev的设备号时看过。在这里还是采用了链表法实现的.

 

阅读(1244) | 评论(0) | 转发(0) |
0

上一篇:linux urb处理

下一篇:usb message 处理

给主人留下些什么吧!~~