ehci-q.c

来自「是关于linux2.5.1的完全源码」· C语言 代码 · 共 1,014 行 · 第 1/2 页

C
1,014
字号
/* * Copyright (c) 2001-2002 by David Brownell *  * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *//* this file is part of ehci-hcd.c *//*-------------------------------------------------------------------------*//* * EHCI hardware queue manipulation * * Control, bulk, and interrupt traffic all use "qh" lists.  They list "qtd" * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned * buffers needed for the larger number).  We use one QH per endpoint, queue * multiple (bulk or control) urbs per endpoint.  URBs may need several qtds. * A scheduled interrupt qh always has one qtd, one urb. * * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with * interrupts) needs careful scheduling.  Performance improvements can be * an ongoing challenge. *  * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs, * or otherwise through transaction translators (TTs) in USB 2.0 hubs using * (b) special fields in qh entries or (c) split iso entries.  TTs will * buffer low/full speed data so the host collects it at high speed. *//*-------------------------------------------------------------------------*//* fill a qtd, returning how much of the buffer we were able to queue up */static intqtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len, int token){	int	i, count;	/* one buffer entry per 4K ... first might be short or unaligned */	qtd->hw_buf [0] = cpu_to_le32 (buf);	count = 0x1000 - (buf & 0x0fff);	/* rest of that page */	if (likely (len < count))		/* ... iff needed */		count = len;	else {		buf +=  0x1000;		buf &= ~0x0fff;		/* per-qtd limit: from 16K to 20K (best alignment) */		for (i = 1; count < len && i < 5; i++) {			u64	addr = buf;			qtd->hw_buf [i] = cpu_to_le32 ((u32)addr);			qtd->hw_buf_hi [i] = cpu_to_le32 ((u32)(addr >> 32));			buf += 0x1000;			if ((count + 0x1000) < len)				count += 0x1000;			else				count = len;		}	}	qtd->hw_token = cpu_to_le32 ((count << 16) | token);	qtd->length = count;#if 0	vdbg ("  qtd_fill %p, token %8x bytes %d dma %x",		qtd, le32_to_cpu (qtd->hw_token), count, qtd->hw_buf [0]);#endif	return count;}/*-------------------------------------------------------------------------*//* update halted (but potentially linked) qh */static inline void qh_update (struct ehci_qh *qh, struct ehci_qtd *qtd){	qh->hw_current = 0;	qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma);	qh->hw_alt_next = EHCI_LIST_END;	/* HC must see latest qtd and qh data before we clear ACTIVE+HALT */	qh->hw_token &= __constant_cpu_to_le32 (QTD_TOGGLE | QTD_STS_PING);}/*-------------------------------------------------------------------------*/static inline void qtd_copy_status (struct urb *urb, size_t length, u32 token){	/* count IN/OUT bytes, not SETUP (even short packets) */	if (likely (QTD_PID (token) != 2))		urb->actual_length += length - QTD_LENGTH (token);	/* don't modify error codes */	if (unlikely (urb->status == -EINPROGRESS && (token & QTD_STS_HALT))) {		if (token & QTD_STS_BABBLE) {			urb->status = -EOVERFLOW;		} else if (!QTD_CERR (token)) {			if (token & QTD_STS_DBE)				urb->status = (QTD_PID (token) == 1) /* IN ? */					? -ENOSR  /* hc couldn't read data */					: -ECOMM; /* hc couldn't write data */			else if (token & QTD_STS_MMF)	/* missed tt uframe */				urb->status = -EPROTO;			else if (token & QTD_STS_XACT) {				if (QTD_LENGTH (token))					urb->status = -EPIPE;				else {					dbg ("3strikes");					urb->status = -EPROTO;				}			} else	/* presumably a stall */				urb->status = -EPIPE;		/* CERR nonzero + data left + halt --> stall */		} else if (QTD_LENGTH (token))			urb->status = -EPIPE;		else	/* unknown */			urb->status = -EPROTO;		dbg ("ep %d-%s qtd token %08x --> status %d",			/* devpath */			usb_pipeendpoint (urb->pipe),			usb_pipein (urb->pipe) ? "in" : "out",			token, urb->status);		/* stall indicates some recovery action is needed */		if (urb->status == -EPIPE) {			int	pipe = urb->pipe;			if (!usb_pipecontrol (pipe))				usb_endpoint_halt (urb->dev,					usb_pipeendpoint (pipe),					usb_pipeout (pipe));			if (urb->dev->tt && !usb_pipeint (pipe)) {err ("must CLEAR_TT_BUFFER, hub port %d%s addr %d ep %d",    urb->dev->ttport, /* devpath */    urb->dev->tt->multi ? "" : " (all-ports TT)",    urb->dev->devnum, usb_pipeendpoint (urb->pipe));				// FIXME something (khubd?) should make the hub				// CLEAR_TT_BUFFER ASAP, it's blocking other				// fs/ls requests... hub_tt_clear_buffer() ?			}		}	}}static void ehci_urb_complete (	struct ehci_hcd		*ehci,	dma_addr_t		addr,	struct urb		*urb) {	if (urb->transfer_buffer_length && usb_pipein (urb->pipe))		pci_dma_sync_single (ehci->hcd.pdev, addr,			urb->transfer_buffer_length,			PCI_DMA_FROMDEVICE);	/* cleanse status if we saw no error */	if (likely (urb->status == -EINPROGRESS)) {		if (urb->actual_length != urb->transfer_buffer_length				&& (urb->transfer_flags & USB_DISABLE_SPD))			urb->status = -EREMOTEIO;		else			urb->status = 0;	}	/* only report unlinks once */	if (likely (urb->status != -ENOENT && urb->status != -ENOTCONN))		urb->complete (urb);}/* urb->lock ignored from here on (hcd is done with urb) */static void ehci_urb_done (	struct ehci_hcd		*ehci,	dma_addr_t		addr,	struct urb		*urb) {	if (urb->transfer_buffer_length)		pci_unmap_single (ehci->hcd.pdev,			addr,			urb->transfer_buffer_length,			usb_pipein (urb->pipe)			    ? PCI_DMA_FROMDEVICE			    : PCI_DMA_TODEVICE);	if (likely (urb->hcpriv != 0)) {		qh_put (ehci, (struct ehci_qh *) urb->hcpriv);		urb->hcpriv = 0;	}	if (likely (urb->status == -EINPROGRESS)) {		if (urb->actual_length != urb->transfer_buffer_length				&& (urb->transfer_flags & USB_DISABLE_SPD))			urb->status = -EREMOTEIO;		else			urb->status = 0;	}	/* hand off urb ownership */	usb_hcd_giveback_urb (&ehci->hcd, urb);}/* * Process completed qtds for a qh, issuing completions if needed. * When freeing:  frees qtds, unmaps buf, returns URB to driver. * When not freeing (queued periodic qh):  retain qtds, mapping, and urb. * Races up to qh->hw_current; returns number of urb completions. */static intqh_completions (	struct ehci_hcd		*ehci,	struct list_head	*qtd_list,	int			freeing) {	struct ehci_qtd		*qtd, *last;	struct list_head	*next;	struct ehci_qh		*qh = 0;	int			unlink = 0, halted = 0;	unsigned long		flags;	int			retval = 0;	spin_lock_irqsave (&ehci->lock, flags);	if (unlikely (list_empty (qtd_list))) {		spin_unlock_irqrestore (&ehci->lock, flags);		return retval;	}	/* scan QTDs till end of list, or we reach an active one */	for (qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list),			    	last = 0, next = 0;			next != qtd_list;			last = qtd, qtd = list_entry (next,						struct ehci_qtd, qtd_list)) {		struct urb	*urb = qtd->urb;		u32		token = 0;		/* qh is non-null iff these qtds were queued to the HC */		qh = (struct ehci_qh *) urb->hcpriv;		/* clean up any state from previous QTD ...*/		if (last) {			if (likely (last->urb != urb)) {				/* complete() can reenter this HCD */				spin_unlock_irqrestore (&ehci->lock, flags);				if (likely (freeing != 0))					ehci_urb_done (ehci, last->buf_dma,						last->urb);				else					ehci_urb_complete (ehci, last->buf_dma,						last->urb);				spin_lock_irqsave (&ehci->lock, flags);				retval++;			}			/* qh overlays can have HC's old cached copies of			 * next qtd ptrs, if an URB was queued afterwards.			 */			if (qh && cpu_to_le32 (last->qtd_dma) == qh->hw_current					&& last->hw_next != qh->hw_qtd_next) {				qh->hw_alt_next = last->hw_alt_next;				qh->hw_qtd_next = last->hw_next;			}			if (likely (freeing != 0))				ehci_qtd_free (ehci, last);			last = 0;		}		next = qtd->qtd_list.next;		/* if these qtds were queued to the HC, some may be active.		 * else we're cleaning up after a failed URB submission.		 *		 * FIXME can simplify: cleanup case is gone now.		 */		if (likely (qh != 0)) {			int		qh_halted;			qh_halted = __constant_cpu_to_le32 (QTD_STS_HALT)					& qh->hw_token;			token = le32_to_cpu (qtd->hw_token);			halted = halted				|| qh_halted				|| (ehci->hcd.state == USB_STATE_HALT)				|| (qh->qh_state == QH_STATE_IDLE);			/* QH halts only because of fault or unlink; in both			 * cases, queued URBs get unlinked.  But for unlink,			 * URBs at the head of the queue can stay linked.			 */			if (unlikely (halted != 0)) {				/* unlink everything because of HC shutdown? */				if (ehci->hcd.state == USB_STATE_HALT) {					freeing = unlink = 1;					urb->status = -ESHUTDOWN;				/* explicit unlink, starting here? */				} else if (qh->qh_state == QH_STATE_IDLE					&& (urb->status == -ECONNRESET						|| urb->status == -ENOENT)) {					freeing = unlink = 1;				/* unlink everything because of error? */				} else if (qh_halted						&& !(token & QTD_STS_HALT)) {					freeing = unlink = 1;					if (urb->status == -EINPROGRESS)						urb->status = -ECONNRESET;				/* unlink the rest? */				} else if (unlink) {					urb->status = -ECONNRESET;				/* QH halted to unlink urbs after this?  */				} else if ((token & QTD_STS_ACTIVE) != 0) {					qtd = 0;					continue;				}			/* Else QH is active, so we must not modify QTDs			 * that HC may be working on.  Break from loop.			 */			} else if (unlikely ((token & QTD_STS_ACTIVE) != 0)) {				next = qtd_list;				qtd = 0;				continue;			}			spin_lock (&urb->lock);			qtd_copy_status (urb, qtd->length, token);			spin_unlock (&urb->lock);		}		/*		 * NOTE:  this won't work right with interrupt urbs that		 * need multiple qtds ... only the first scan of qh->qtd_list		 * starts at the right qtd, yet multiple scans could happen		 * for transfers that are scheduled across multiple uframes. 		 * (Such schedules are not currently allowed!)		 */		if (likely (freeing != 0))			list_del (&qtd->qtd_list);		else {			/* restore everything the HC could change			 * from an interrupt QTD			 */			qtd->hw_token = (qtd->hw_token					& ~__constant_cpu_to_le32 (0x8300))				| cpu_to_le32 (qtd->length << 16)				| __constant_cpu_to_le32 (QTD_IOC					| (EHCI_TUNE_CERR << 10)					| QTD_STS_ACTIVE);			qtd->hw_buf [0] &= ~__constant_cpu_to_le32 (0x0fff);			/* this offset, and the length above,			 * are likely wrong on QTDs #2..N			 */			qtd->hw_buf [0] |= cpu_to_le32 (0x0fff & qtd->buf_dma);		}#if 0		if (urb->status == -EINPROGRESS)			vdbg ("  qtd %p ok, urb %p, token %8x, len %d",				qtd, urb, token, urb->actual_length);		else			vdbg ("urb %p status %d, qtd %p, token %8x, len %d",				urb, urb->status, qtd, token,				urb->actual_length);#endif		/* SETUP for control urb? */		if (unlikely (QTD_PID (token) == 2))			pci_unmap_single (ehci->hcd.pdev,				qtd->buf_dma, sizeof (struct usb_ctrlrequest),				PCI_DMA_TODEVICE);	}	/* patch up list head? */	if (unlikely (halted && qh && !list_empty (qtd_list))) {		qh_update (qh, list_entry (qtd_list->next,				struct ehci_qtd, qtd_list));	}	spin_unlock_irqrestore (&ehci->lock, flags);	/* last urb's completion might still need calling */	if (likely (last != 0)) {		if (likely (freeing != 0)) {			ehci_urb_done (ehci, last->buf_dma, last->urb);			ehci_qtd_free (ehci, last);		} else			ehci_urb_complete (ehci, last->buf_dma, last->urb);		retval++;	}	return retval;}/*-------------------------------------------------------------------------*//* * reverse of qh_urb_transaction:  free a list of TDs. * used for cleanup after errors, before HC sees an URB's TDs. */static void qtd_list_free (	struct ehci_hcd		*ehci,	struct urb		*urb,	struct list_head	*qtd_list) {	struct list_head	*entry, *temp;	int			unmapped = 0;	list_for_each_safe (entry, temp, qtd_list) {		struct ehci_qtd	*qtd;		qtd = list_entry (entry, struct ehci_qtd, qtd_list);		list_del (&qtd->qtd_list);		if (unmapped != 2) {			int	direction;			size_t	size;			/* for ctrl unmap twice: SETUP and DATA;			 * else (bulk, intr) just once: DATA			 */			if (!unmapped++ && usb_pipecontrol (urb->pipe)) {				direction = PCI_DMA_TODEVICE;				size = sizeof (struct usb_ctrlrequest);			} else {				direction = usb_pipein (urb->pipe)					? PCI_DMA_FROMDEVICE					: PCI_DMA_TODEVICE;				size = qtd->urb->transfer_buffer_length;				unmapped++;			}			if (qtd->buf_dma)				pci_unmap_single (ehci->hcd.pdev,					qtd->buf_dma,					size, direction);		}		ehci_qtd_free (ehci, qtd);	}}/* * create a list of filled qtds for this URB; won't link into qh. */static struct list_head *qh_urb_transaction (	struct ehci_hcd		*ehci,	struct urb		*urb,	struct list_head	*head,	int			flags) {	struct ehci_qtd		*qtd, *qtd_prev;	dma_addr_t		buf, map_buf;	int			len, maxpacket;	u32			token;	/*	 * URBs map to sequences of QTDs:  one logical transaction	 */	qtd = ehci_qtd_alloc (ehci, flags);	if (unlikely (!qtd))		return 0;	qtd_prev = 0;	list_add_tail (&qtd->qtd_list, head);	qtd->urb = urb;	token = QTD_STS_ACTIVE;	token |= (EHCI_TUNE_CERR << 10);	/* for split transactions, SplitXState initialized to zero */	if (usb_pipecontrol (urb->pipe)) {		/* control request data is passed in the "setup" pid */		qtd->buf_dma = pci_map_single (					ehci->hcd.pdev,					urb->setup_packet,					sizeof (struct usb_ctrlrequest),					PCI_DMA_TODEVICE);		if (unlikely (!qtd->buf_dma))			goto cleanup;		/* SETUP pid */		qtd_fill (qtd, qtd->buf_dma, sizeof (struct usb_ctrlrequest),			token | (2 /* "setup" */ << 8));		/* ... and always at least one more pid */		token ^= QTD_TOGGLE;		qtd_prev = qtd;		qtd = ehci_qtd_alloc (ehci, flags);		if (unlikely (!qtd))			goto cleanup;		qtd->urb = urb;		qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);		list_add_tail (&qtd->qtd_list, head);	} 	/*

⌨️ 快捷键说明

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