mini2440 usb host device controller驅動分析(二) -----資料(urb)的收發流程
這節分析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)
{
int status;
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);
else
status = 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; int ints; /* 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); } else usb_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; } } else ohci->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 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;
}
}
後面的呼叫過程是這樣的:takeback_td ----- finish_urb ----- usb_hcd_giveback_urb------ urb->complete。
這樣,我們就看到了回撥過程的實現。