⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ohci-q.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * OHCI HCD (Host Controller Driver) for USB. *  * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> *  * This file is licenced under the GPL. */static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv){	int		last = urb_priv->length - 1;	if (last >= 0) {		int		i;		struct td	*td;		for (i = 0; i <= last; i++) {			td = urb_priv->td [i];			if (td)				td_free (hc, td);		}	}	list_del (&urb_priv->pending);	kfree (urb_priv);}/*-------------------------------------------------------------------------*//* * URB goes back to driver, and isn't reissued. * It's completely gone from HC data structures. * PRECONDITION:  ohci lock held, irqs blocked. */static voidfinish_urb (struct ohci_hcd *ohci, struct urb *urb, struct pt_regs *regs)__releases(ohci->lock)__acquires(ohci->lock){	// ASSERT (urb->hcpriv != 0);	urb_free_priv (ohci, urb->hcpriv);	urb->hcpriv = NULL;	spin_lock (&urb->lock);	if (likely (urb->status == -EINPROGRESS))		urb->status = 0;	/* report short control reads right even though the data TD always	 * has TD_R set.  (much simpler, but creates the 1-td limit.)	 */	if (unlikely (urb->transfer_flags & URB_SHORT_NOT_OK)			&& unlikely (usb_pipecontrol (urb->pipe))			&& urb->actual_length < urb->transfer_buffer_length			&& usb_pipein (urb->pipe)			&& urb->status == 0) {		urb->status = -EREMOTEIO;	}	spin_unlock (&urb->lock);	switch (usb_pipetype (urb->pipe)) {	case PIPE_ISOCHRONOUS:		ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs--;		break;	case PIPE_INTERRUPT:		ohci_to_hcd(ohci)->self.bandwidth_int_reqs--;		break;	}#ifdef OHCI_VERBOSE_DEBUG	urb_print (urb, "RET", usb_pipeout (urb->pipe));#endif	/* urb->complete() can reenter this HCD */	spin_unlock (&ohci->lock);	usb_hcd_giveback_urb (ohci_to_hcd(ohci), urb, regs);	spin_lock (&ohci->lock);	/* stop periodic dma if it's not needed */	if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0			&& ohci_to_hcd(ohci)->self.bandwidth_int_reqs == 0) {		ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_IE);		ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);	}}/*-------------------------------------------------------------------------* * ED handling functions *-------------------------------------------------------------------------*/  /* search for the right schedule branch to use for a periodic ed. * does some load balancing; returns the branch, or negative errno. */static int balance (struct ohci_hcd *ohci, int interval, int load){	int	i, branch = -ENOSPC;	/* iso periods can be huge; iso tds specify frame numbers */	if (interval > NUM_INTS)		interval = NUM_INTS;	/* search for the least loaded schedule branch of that period	 * that has enough bandwidth left unreserved.	 */	for (i = 0; i < interval ; i++) {		if (branch < 0 || ohci->load [branch] > ohci->load [i]) {#if 1	/* CONFIG_USB_BANDWIDTH */			int	j;			/* usb 1.1 says 90% of one frame */			for (j = i; j < NUM_INTS; j += interval) {				if ((ohci->load [j] + load) > 900)					break;			}			if (j < NUM_INTS)				continue;#endif			branch = i; 		}	}	return branch;}/*-------------------------------------------------------------------------*//* both iso and interrupt requests have periods; this routine puts them * into the schedule tree in the apppropriate place.  most iso devices use * 1msec periods, but that's not required. */static void periodic_link (struct ohci_hcd *ohci, struct ed *ed){	unsigned	i;	ohci_vdbg (ohci, "link %sed %p branch %d [%dus.], interval %d\n",		(ed->hwINFO & cpu_to_hc32 (ohci, ED_ISO)) ? "iso " : "",		ed, ed->branch, ed->load, ed->interval);	for (i = ed->branch; i < NUM_INTS; i += ed->interval) {		struct ed	**prev = &ohci->periodic [i];		__hc32		*prev_p = &ohci->hcca->int_table [i];		struct ed	*here = *prev;		/* sorting each branch by period (slow before fast)		 * lets us share the faster parts of the tree.		 * (plus maybe: put interrupt eds before iso)		 */		while (here && ed != here) {			if (ed->interval > here->interval)				break;			prev = &here->ed_next;			prev_p = &here->hwNextED;			here = *prev;		}		if (ed != here) {			ed->ed_next = here;			if (here)				ed->hwNextED = *prev_p;			wmb ();			*prev = ed;			*prev_p = cpu_to_hc32(ohci, ed->dma);			wmb();		}		ohci->load [i] += ed->load;	}	ohci_to_hcd(ohci)->self.bandwidth_allocated += ed->load / ed->interval;}/* link an ed into one of the HC chains */static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed){	 	int	branch;	if (ohci_to_hcd(ohci)->state == HC_STATE_QUIESCING)		return -EAGAIN;	ed->state = ED_OPER;	ed->ed_prev = NULL;	ed->ed_next = NULL;	ed->hwNextED = 0;	wmb ();	/* we care about rm_list when setting CLE/BLE in case the HC was at	 * work on some TD when CLE/BLE was turned off, and isn't quiesced	 * yet.  finish_unlinks() restarts as needed, some upcoming INTR_SF.	 *	 * control and bulk EDs are doubly linked (ed_next, ed_prev), but	 * periodic ones are singly linked (ed_next). that's because the	 * periodic schedule encodes a tree like figure 3-5 in the ohci	 * spec:  each qh can have several "previous" nodes, and the tree	 * doesn't have unused/idle descriptors.	 */	switch (ed->type) {	case PIPE_CONTROL:		if (ohci->ed_controltail == NULL) {			WARN_ON (ohci->hc_control & OHCI_CTRL_CLE);			ohci_writel (ohci, ed->dma,					&ohci->regs->ed_controlhead);		} else {			ohci->ed_controltail->ed_next = ed;			ohci->ed_controltail->hwNextED = cpu_to_hc32 (ohci,								ed->dma);		}		ed->ed_prev = ohci->ed_controltail;		if (!ohci->ed_controltail && !ohci->ed_rm_list) {			wmb();			ohci->hc_control |= OHCI_CTRL_CLE;			ohci_writel (ohci, 0, &ohci->regs->ed_controlcurrent);			ohci_writel (ohci, ohci->hc_control,					&ohci->regs->control);		}		ohci->ed_controltail = ed;		break;	case PIPE_BULK:		if (ohci->ed_bulktail == NULL) {			WARN_ON (ohci->hc_control & OHCI_CTRL_BLE);			ohci_writel (ohci, ed->dma, &ohci->regs->ed_bulkhead);		} else {			ohci->ed_bulktail->ed_next = ed;			ohci->ed_bulktail->hwNextED = cpu_to_hc32 (ohci,								ed->dma);		}		ed->ed_prev = ohci->ed_bulktail;		if (!ohci->ed_bulktail && !ohci->ed_rm_list) {			wmb();			ohci->hc_control |= OHCI_CTRL_BLE;			ohci_writel (ohci, 0, &ohci->regs->ed_bulkcurrent);			ohci_writel (ohci, ohci->hc_control,					&ohci->regs->control);		}		ohci->ed_bulktail = ed;		break;	// case PIPE_INTERRUPT:	// case PIPE_ISOCHRONOUS:	default:		branch = balance (ohci, ed->interval, ed->load);		if (branch < 0) {			ohci_dbg (ohci,				"ERR %d, interval %d msecs, load %d\n",				branch, ed->interval, ed->load);			// FIXME if there are TDs queued, fail them!			return branch;		}		ed->branch = branch;		periodic_link (ohci, ed);	}	 		/* the HC may not see the schedule updates yet, but if it does	 * then they'll be properly ordered.	 */	return 0;}/*-------------------------------------------------------------------------*//* scan the periodic table to find and unlink this ED */static void periodic_unlink (struct ohci_hcd *ohci, struct ed *ed){	int	i;	for (i = ed->branch; i < NUM_INTS; i += ed->interval) {		struct ed	*temp;		struct ed	**prev = &ohci->periodic [i];		__hc32		*prev_p = &ohci->hcca->int_table [i];		while (*prev && (temp = *prev) != ed) {			prev_p = &temp->hwNextED;			prev = &temp->ed_next;		}		if (*prev) {			*prev_p = ed->hwNextED;			*prev = ed->ed_next;		}		ohci->load [i] -= ed->load;	}		ohci_to_hcd(ohci)->self.bandwidth_allocated -= ed->load / ed->interval;	ohci_vdbg (ohci, "unlink %sed %p branch %d [%dus.], interval %d\n",		(ed->hwINFO & cpu_to_hc32 (ohci, ED_ISO)) ? "iso " : "",		ed, ed->branch, ed->load, ed->interval);}/* unlink an ed from one of the HC chains.  * just the link to the ed is unlinked. * the link from the ed still points to another operational ed or 0 * so the HC can eventually finish the processing of the unlinked ed * (assuming it already started that, which needn't be true). * * ED_UNLINK is a transient state: the HC may still see this ED, but soon * it won't.  ED_SKIP means the HC will finish its current transaction, * but won't start anything new.  The TD queue may still grow; device * drivers don't know about this HCD-internal state. * * When the HC can't see the ED, something changes ED_UNLINK to one of: * *  - ED_OPER: when there's any request queued, the ED gets rescheduled *    immediately.  HC should be working on them. * *  - ED_IDLE:  when there's no TD queue. there's no reason for the HC *    to care about this ED; safe to disable the endpoint. * * When finish_unlinks() runs later, after SOF interrupt, it will often * complete one or more URB unlinks before making that state change. */static void ed_deschedule (struct ohci_hcd *ohci, struct ed *ed) {	ed->hwINFO |= cpu_to_hc32 (ohci, ED_SKIP);	wmb ();	ed->state = ED_UNLINK;	/* To deschedule something from the control or bulk list, just	 * clear CLE/BLE and wait.  There's no safe way to scrub out list	 * head/current registers until later, and "later" isn't very	 * tightly specified.  Figure 6-5 and Section 6.4.2.2 show how	 * the HC is reading the ED queues (while we modify them).	 *	 * For now, ed_schedule() is "later".  It might be good paranoia	 * to scrub those registers in finish_unlinks(), in case of bugs	 * that make the HC try to use them.	 */	switch (ed->type) {	case PIPE_CONTROL:		/* remove ED from the HC's list: */		if (ed->ed_prev == NULL) {			if (!ed->hwNextED) {				ohci->hc_control &= ~OHCI_CTRL_CLE;				ohci_writel (ohci, ohci->hc_control,						&ohci->regs->control);				// a ohci_readl() later syncs CLE with the HC			} else				ohci_writel (ohci,					hc32_to_cpup (ohci, &ed->hwNextED),					&ohci->regs->ed_controlhead);		} else {			ed->ed_prev->ed_next = ed->ed_next;			ed->ed_prev->hwNextED = ed->hwNextED;		}		/* remove ED from the HCD's list: */		if (ohci->ed_controltail == ed) {			ohci->ed_controltail = ed->ed_prev;			if (ohci->ed_controltail)				ohci->ed_controltail->ed_next = NULL;		} else if (ed->ed_next) {			ed->ed_next->ed_prev = ed->ed_prev;		}		break;	case PIPE_BULK:		/* remove ED from the HC's list: */		if (ed->ed_prev == NULL) {			if (!ed->hwNextED) {				ohci->hc_control &= ~OHCI_CTRL_BLE;				ohci_writel (ohci, ohci->hc_control,						&ohci->regs->control);				// a ohci_readl() later syncs BLE with the HC			} else				ohci_writel (ohci,					hc32_to_cpup (ohci, &ed->hwNextED),					&ohci->regs->ed_bulkhead);		} else {			ed->ed_prev->ed_next = ed->ed_next;			ed->ed_prev->hwNextED = ed->hwNextED;		}		/* remove ED from the HCD's list: */		if (ohci->ed_bulktail == ed) {			ohci->ed_bulktail = ed->ed_prev;			if (ohci->ed_bulktail)				ohci->ed_bulktail->ed_next = NULL;		} else if (ed->ed_next) {			ed->ed_next->ed_prev = ed->ed_prev;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -