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。

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

你可能感兴趣的:(USB驱动分析,usb,host,mini2440)