1. 程式人生 > >mini2440 usb host device controller驅動分析(二) -----資料(urb)的收發流程

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。

這樣,我們就看到了回撥過程的實現。