mini2440 usb host device controller驱动分析(二) -----数据(urb)的收发流程

来源:互联网 发布:上海电信网络套餐介绍 编辑:程序博客网 时间:2024/05/23 01:57

这节分析urb的收发流程。

我们首先知道对于usb device 来讲,读写数据用到的是usb_request。而对于usb host来讲,读写数据用到的是urb,有些类似于网络中skbuff。

无论是进行 读还是写 用到的函数都是 usb_submit_urb。在urb结构体中有一个回调函数指针complete。(usb_request中也有一个回调函数指针)这样,对于一个写请求,complete函数表示写请求结束。对于读请求,complete函数表示要读的数据已经读到,通常在complete函数中进行处理。 因此,无论读写都可以通过usb_submit_urb来实现。既然有回调函数,那么usb_submit_urb就是异步的。

下面,我们来看usb_submit_urb的实现。usb_submit_urb 最终会调用usb_hcd_submit_urb。

int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags){intstatus;struct usb_hcd*hcd = bus_to_hcd(urb->dev->bus);usb_get_urb(urb);atomic_inc(&urb->use_count);atomic_inc(&urb->dev->urbnum);usbmon_urb_submit(&hcd->self, urb);status = map_urb_for_dma(hcd, urb, mem_flags);if (unlikely(status)) {usbmon_urb_submit_error(&hcd->self, urb, status);goto error;}if (is_root_hub(urb->dev))status = rh_urb_enqueue(hcd, urb);elsestatus = hcd->driver->urb_enqueue(hcd, urb, mem_flags);return status;}

对于发送目标是普通device(非root hub,root hub的情况我们在后面分析),会调用到hcd->driver->urb_enqueue。这个函数对应着ohci_urb_enqueue。并最终调用到td_submit_urb完成传输。

 

完成传输之后,我们看看complete回调函数时怎么被调用的。按照常理,不论是发完或者是接收到 新的数据,它的入口都应该在中断函数中,也就是我们之前看到的usb_hcd_irq.在这里又调用了ohci_irq. 为此,我们看这个函数。

static irqreturn_t ohci_irq (struct usb_hcd *hcd){struct ohci_hcd*ohci = hcd_to_ohci (hcd);struct ohci_regs __iomem *regs = ohci->regs;intints;/* Read interrupt status (and flush pending writes).  We ignore the * optimization of checking the LSB of hcca->done_head; it doesn't * work on all systems (edge triggering for OHCI can be a factor). */ints = ohci_readl(ohci, *regs->intrstatus);/* Check for an all 1's result which is a typical consequence * of dead, unclocked, or unplugged (CardBus...) devices */if (ints == ~(u32)0) {disable (ohci);ohci_dbg (ohci, "device removed!\n");return IRQ_HANDLED;}/* We only care about interrupts that are enabled */ints &= ohci_readl(ohci, ®s->intrenable);/* interrupt for some other device? */if (ints == 0)return IRQ_NOTMINE;if (ints & OHCI_INTR_RHSC) {ohci_vdbg(ohci, "rhsc\n");ohci->next_statechange = jiffies + STATECHANGE_DELAY;ohci_writel(ohci, OHCI_INTR_RD | OHCI_INTR_RHSC,®s->intrstatus);ohci_writel(ohci, OHCI_INTR_RHSC, regs->intrdisable);usb_hcd_poll_rh_status(hcd);}/* For connect and disconnect events, we expect the controller * to turn on RHSC along with RD.  But for remote wakeup events * this might not happen. */else if (ints & OHCI_INTR_RD) {ohci_vdbg(ohci, "resume detect\n");ohci_writel(ohci, OHCI_INTR_RD, regs->intrstatus);hcd->poll_rh = 1;if (ohci->autostop) {spin_lock (&ohci->lock);ohci_rh_resume (ohci);spin_unlock (&ohci->lock);} elseusb_hcd_resume_root_hub(hcd);}if (ints & OHCI_INTR_WDH) {spin_lock (&ohci->lock);dl_done_list (ohci);spin_unlock (&ohci->lock);}if (quirk_zfmicro(ohci) && (ints & OHCI_INTR_SF)) {spin_lock(&ohci->lock);if (ohci->ed_to_check) {struct ed *ed = ohci->ed_to_check;if (check_ed(ohci, ed)) {/* HC thinks the TD list is empty; HCD knows * at least one TD is outstanding */if (--ohci->zf_delay == 0) {struct td *td = list_entry(ed->td_list.next,struct td, td_list);ohci_warn(ohci,  "Reclaiming orphan TD %p\n",  td);takeback_td(ohci, td);ohci->ed_to_check = NULL;}} elseohci->ed_to_check = NULL;}spin_unlock(&ohci->lock);}/* could track INTR_SO to reduce available PCI/... bandwidth *//* handle any pending URB/ED unlinks, leaving INTR_SF enabled * when there's still unlinking to be done (next frame). */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);// flush those writes(void) ohci_readl (ohci, &ohci->regs->control);}return IRQ_HANDLED;}


有三类中断需要我们关注。

1. 其中RHSC代表 root hub status change, 这里应该会给root hub发送消息。也就是说root hub感应有设备插拔的入口 也是这个中断函数,root hub并没有单独的中断函数来处理这样的事件。 后面我们在讲述root hub的工作原理。

2. OHCI_INTR_SF  SF 是 start frame。 这个是什么呢??

3.. OHCI_INTR_WDH 代表write back of done head。就是在这里调用了complete函数。我们看这个函数。

static voiddl_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;}}

后面的调用过程是这样的:takeback_td -----  finish_urb ----- usb_hcd_giveback_urb------ urb->complete。

这样,我们就看到了回调过程的实现。