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

📄 ohci-q.c

📁 是关于linux2.5.1的完全源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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. * $Id: ohci-q.c,v 1.8 2002/03/27 20:57:01 dbrownell Exp $ */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 = urb_priv->td [0];#ifdef CONFIG_PCI		int		len = td->urb->transfer_buffer_length;		int		dir = usb_pipeout (td->urb->pipe)					? PCI_DMA_TODEVICE					: PCI_DMA_FROMDEVICE;		/* unmap CTRL URB setup buffer (always td 0) */		if (usb_pipecontrol (td->urb->pipe)) {			pci_unmap_single (hc->hcd.pdev, 					td->data_dma, 8, PCI_DMA_TODEVICE);						/* CTRL data buffer starts at td 1 if len > 0 */			if (len && last > 0)				td = urb_priv->td [1]; 				}		/* else:  ISOC, BULK, INTR data buffer starts at td 0 */		/* unmap data buffer */		if (len && td->data_dma)			pci_unmap_single (hc->hcd.pdev,					td->data_dma, len, dir);#else#	warning "assuming no buffer unmapping is needed"#endif		for (i = 0; i <= last; i++) {			td = urb_priv->td [i];			if (td)				td_free (hc, td);		}	}	kfree (urb_priv);}/*-------------------------------------------------------------------------*//* * URB goes back to driver, and isn't reissued. * It's completely gone from HC data structures. * PRECONDITION:  no locks held  (Giveback can call into HCD.) */static void finish_urb (struct ohci_hcd *ohci, struct urb *urb){	unsigned long	flags;#ifdef DEBUG	if (!urb->hcpriv) {		err ("already unlinked!");		BUG ();	}#endif	urb_free_priv (ohci, urb->hcpriv);	urb->hcpriv = NULL;	spin_lock_irqsave (&urb->lock, flags);	if (likely (urb->status == -EINPROGRESS))		urb->status = 0;	spin_unlock_irqrestore (&urb->lock, flags);#ifdef OHCI_VERBOSE_DEBUG	urb_print (urb, "RET", usb_pipeout (urb->pipe));#endif	usb_hcd_giveback_urb (&ohci->hcd, urb);}static void td_submit_urb (struct urb *urb);/* Report interrupt transfer completion, maybe reissue */static void intr_resub (struct ohci_hcd *hc, struct urb *urb){	urb_priv_t	*urb_priv = urb->hcpriv;	unsigned long	flags;#ifdef CONFIG_PCI// FIXME rewrite this resubmit path.  use pci_dma_sync_single()// and requeue more cheaply, and only if needed.// Better yet ... abolish the notion of automagic resubmission.	pci_unmap_single (hc->hcd.pdev,		urb_priv->td [0]->data_dma,		urb->transfer_buffer_length,		usb_pipeout (urb->pipe)			? PCI_DMA_TODEVICE			: PCI_DMA_FROMDEVICE);#endif	/* FIXME: MP race.  If another CPU partially unlinks	 * this URB (urb->status was updated, hasn't yet told	 * us to dequeue) before we call complete() here, an	 * extra "unlinked" completion will be reported...	 */	spin_lock_irqsave (&urb->lock, flags);	if (likely (urb->status == -EINPROGRESS))		urb->status = 0;	spin_unlock_irqrestore (&urb->lock, flags);#ifdef OHCI_VERBOSE_DEBUG	urb_print (urb, "INTR", usb_pipeout (urb->pipe));#endif	urb->complete (urb);	/* always requeued, but ED_SKIP if complete() unlinks.	 * EDs are removed from periodic table only at SOF intr.	 */	urb->actual_length = 0;	spin_lock_irqsave (&urb->lock, flags);	if (urb_priv->state != URB_DEL)		urb->status = -EINPROGRESS;	spin_unlock (&urb->lock);	spin_lock (&hc->lock);	td_submit_urb (urb);	spin_unlock_irqrestore (&hc->lock, flags);}/*-------------------------------------------------------------------------* * ED handling functions *-------------------------------------------------------------------------*/  /* search for the right branch to insert an interrupt ed into the int tree  * do some load balancing; * returns the branch and  * sets the interval to interval = 2^integer (ld (interval)) */static int ep_int_balance (struct ohci_hcd *ohci, int interval, int load){	int	i, branch = 0;	/* search for the least loaded interrupt endpoint branch */	for (i = 0; i < NUM_INTS ; i++) 		if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i])			branch = i; 	branch = branch % interval;	for (i = branch; i < NUM_INTS; i += interval)		ohci->ohci_int_load [i] += load;	return branch;}/*-------------------------------------------------------------------------*//*  2^int ( ld (inter)) */static int ep_2_n_interval (int inter){		int	i;	for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++)		continue;	return 1 << i;}/*-------------------------------------------------------------------------*//* the int tree is a binary tree  * in order to process it sequentially the indexes of the branches have * to be mapped the mapping reverses the bits of a word of num_bits length */static int ep_rev (int num_bits, int word){	int			i, wout = 0;	for (i = 0; i < num_bits; i++)		wout |= (( (word >> i) & 1) << (num_bits - i - 1));	return wout;}/*-------------------------------------------------------------------------*//* link an ed into one of the HC chains */static int ep_link (struct ohci_hcd *ohci, struct ed *edi){	 	int			int_branch, i;	int			inter, interval, load;	__u32			*ed_p;	volatile struct ed	*ed = edi;	ed->state = ED_OPER;	switch (ed->type) {	case PIPE_CONTROL:		ed->hwNextED = 0;		if (ohci->ed_controltail == NULL) {			writel (ed->dma, &ohci->regs->ed_controlhead);		} else {			ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma);		}		ed->ed_prev = ohci->ed_controltail;		if (!ohci->ed_controltail				&& !ohci->ed_rm_list [0]				&& !ohci->ed_rm_list [1]				&& !ohci->sleeping				) {			ohci->hc_control |= OHCI_CTRL_CLE;			writel (ohci->hc_control, &ohci->regs->control);		}		ohci->ed_controltail = edi;	  		break;	case PIPE_BULK:		ed->hwNextED = 0;		if (ohci->ed_bulktail == NULL) {			writel (ed->dma, &ohci->regs->ed_bulkhead);		} else {			ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma);		}		ed->ed_prev = ohci->ed_bulktail;		if (!ohci->ed_bulktail				&& !ohci->ed_rm_list [0]				&& !ohci->ed_rm_list [1]				&& !ohci->sleeping				) {			ohci->hc_control |= OHCI_CTRL_BLE;			writel (ohci->hc_control, &ohci->regs->control);		}		ohci->ed_bulktail = edi;	  		break;	case PIPE_INTERRUPT:		load = ed->int_load;		interval = ep_2_n_interval (ed->int_period);		ed->int_interval = interval;		int_branch = ep_int_balance (ohci, interval, load);		ed->int_branch = int_branch;		for (i = 0; i < ep_rev (6, interval); i += inter) {			inter = 1;			for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i) + int_branch]); 				 (*ed_p != 0) && ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval >= interval); 				ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) 					inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);			ed->hwNextED = *ed_p; 			*ed_p = cpu_to_le32 (ed->dma);		}#ifdef OHCI_VERBOSE_DEBUG		ohci_dump_periodic (ohci, "LINK_INT");#endif		break;	case PIPE_ISOCHRONOUS:		ed->hwNextED = 0;		ed->int_interval = 1;		if (ohci->ed_isotail != NULL) {			ohci->ed_isotail->hwNextED = cpu_to_le32 (ed->dma);			ed->ed_prev = ohci->ed_isotail;		} else {			for ( i = 0; i < NUM_INTS; i += inter) {				inter = 1;				for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i)]); 					*ed_p != 0; 					ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) 						inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);				*ed_p = cpu_to_le32 (ed->dma);				}				ed->ed_prev = NULL;		}			ohci->ed_isotail = edi;  #ifdef OHCI_VERBOSE_DEBUG		ohci_dump_periodic (ohci, "LINK_ISO");#endif		break;	}	 		return 0;}/*-------------------------------------------------------------------------*//* scan the periodic table to find and unlink this ED */static void periodic_unlink (	struct ohci_hcd	*ohci,	struct ed	*ed,	unsigned	index,	unsigned	period) {	for (; index < NUM_INTS; index += period) {		__u32	*ed_p = &ohci->hcca->int_table [index];		while (*ed_p != 0) {			if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {				*ed_p = ed->hwNextED;						break;			}			ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED);		}	}	}/* 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 */static int ep_unlink (struct ohci_hcd *ohci, struct ed *ed) {	int	i;	ed->hwINFO |= ED_SKIP;	switch (ed->type) {	case PIPE_CONTROL:		if (ed->ed_prev == NULL) {			if (!ed->hwNextED) {				ohci->hc_control &= ~OHCI_CTRL_CLE;				writel (ohci->hc_control, &ohci->regs->control);			}			writel (le32_to_cpup (&ed->hwNextED),				&ohci->regs->ed_controlhead);		} else {			ed->ed_prev->hwNextED = ed->hwNextED;		}		if (ohci->ed_controltail == ed) {			ohci->ed_controltail = ed->ed_prev;		} else {			 (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))			 	->ed_prev = ed->ed_prev;		}		break;	case PIPE_BULK:		if (ed->ed_prev == NULL) {			if (!ed->hwNextED) {				ohci->hc_control &= ~OHCI_CTRL_BLE;				writel (ohci->hc_control, &ohci->regs->control);			}			writel (le32_to_cpup (&ed->hwNextED),				&ohci->regs->ed_bulkhead);		} else {			ed->ed_prev->hwNextED = ed->hwNextED;		}		if (ohci->ed_bulktail == ed) {			ohci->ed_bulktail = ed->ed_prev;		} else {			 (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))			 	->ed_prev = ed->ed_prev;		}		break;	case PIPE_INTERRUPT:		periodic_unlink (ohci, ed, ed->int_branch, ed->int_interval);		for (i = ed->int_branch; i < NUM_INTS; i += ed->int_interval)			ohci->ohci_int_load [i] -= ed->int_load;#ifdef OHCI_VERBOSE_DEBUG		ohci_dump_periodic (ohci, "UNLINK_INT");#endif		break;	case PIPE_ISOCHRONOUS:		if (ohci->ed_isotail == ed)			ohci->ed_isotail = ed->ed_prev;		if (ed->hwNextED != 0) 			(dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))		    		->ed_prev = ed->ed_prev;		if (ed->ed_prev != NULL)			ed->ed_prev->hwNextED = ed->hwNextED;		else			periodic_unlink (ohci, ed, 0, 1);#ifdef OHCI_VERBOSE_DEBUG		ohci_dump_periodic (ohci, "UNLINK_ISO");#endif		break;	}	/* FIXME ED's "unlink" state is indeterminate;	 * the HC might still be caching it (till SOF).	 */	ed->state = ED_UNLINK;	return 0;}/*-------------------------------------------------------------------------*//* (re)init an endpoint; this _should_ be done once at the * usb_set_configuration command, but the USB stack is a bit stateless * so we do it at every transaction. * if the state of the ed is ED_NEW then a dummy td is added and the * state is changed to ED_UNLINK * in all other cases the state is left unchanged * the ed info fields are set even though most of them should * not change */static struct ed *ep_add_ed (	struct usb_device	*udev,	unsigned int		pipe,	int			interval,	int			load,	int			mem_flags) {   	struct ohci_hcd		*ohci = hcd_to_ohci (udev->bus->hcpriv);	struct hcd_dev		*dev = (struct hcd_dev *) udev->hcpriv;	struct td		*td;	struct ed		*ed; 	unsigned		ep;	unsigned long		flags;	spin_lock_irqsave (&ohci->lock, flags);	ep = usb_pipeendpoint (pipe) << 1;	if (!usb_pipecontrol (pipe) && usb_pipeout (pipe))		ep |= 1;	if (!(ed = dev->ep [ep])) {		ed = ed_alloc (ohci, SLAB_ATOMIC);		if (!ed) {			/* out of memory */			spin_unlock_irqrestore (&ohci->lock, flags);			return NULL;		}		dev->ep [ep] = ed;	}	if (ed->state & ED_URB_DEL) {		/* pending unlink request */		spin_unlock_irqrestore (&ohci->lock, flags);		return NULL;	}	if (ed->state == ED_NEW) {		ed->hwINFO = ED_SKIP;  		/* dummy td; end of td list for ed */		td = td_alloc (ohci, SLAB_ATOMIC); 		if (!td) {			/* out of memory */			spin_unlock_irqrestore (&ohci->lock, flags);			return NULL;		}		ed->hwTailP = cpu_to_le32 (td->td_dma);		ed->hwHeadP = ed->hwTailP;			ed->state = ED_UNLINK;		ed->type = usb_pipetype (pipe);	}// FIXME:  don't do this if it's linked to the HC,// we might clobber data toggle or other state ...	ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe)			| usb_pipeendpoint (pipe) << 7			| (usb_pipeisoc (pipe)? 0x8000: 0)			| (usb_pipecontrol (pipe)				? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) 			| (udev->speed == USB_SPEED_LOW) << 13			| usb_maxpacket (udev, pipe, usb_pipeout (pipe))				<< 16);  	if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) {  		ed->int_period = interval;  		ed->int_load = load;  	}	spin_unlock_irqrestore (&ohci->lock, flags);	return ed; }/*-------------------------------------------------------------------------*//* request unlinking of an endpoint from an operational HC. * put the ep on the rm_list and stop the bulk or ctrl list  * real work is done at the next start frame (SF) hardware interrupt */static void ed_unlink (struct usb_device *usb_dev, struct ed *ed){    	unsigned int		frame;   	struct ohci_hcd		*ohci = hcd_to_ohci (usb_dev->bus->hcpriv);	/* already pending? */	if (ed->state & ED_URB_DEL)		return;	ed->state |= ED_URB_DEL;

⌨️ 快捷键说明

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