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

📄 ehci-q.c

📁 linux2.4.20下的针对三星公司的s3c2410的usb模块驱动代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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 ... the core.  QH/QTD 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 (for now) 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.  That's in "ehci-sched.c". *  * 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 */	wmb ();	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) {			/* FIXME "must" disable babbling device's port too */			urb->status = -EOVERFLOW;		} else if (token & QTD_STS_MMF) {			/* fs/ls interrupt xfer missed the complete-split */			urb->status = -EPROTO;		} else 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_XACT) {			/* timeout, bad crc, wrong PID, etc; retried */			if (QTD_CERR (token))				urb->status = -EPIPE;			else {				dbg ("3strikes");				urb->status = -EPROTO;			}		/* CERR nonzero + no errors + halt --> stall */		} else if (QTD_CERR (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 ehci_qh		*qh,	int			freeing) {	struct ehci_qtd		*qtd, *last;	struct list_head	*next, *qtd_list = &qh->qtd_list;	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;		/* 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 (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;		/* QTDs at tail may be active if QH+HC are running,		 * or when unlinking some urbs queued to this QH		 */		token = le32_to_cpu (qtd->hw_token);		halted = halted			|| (__constant_cpu_to_le32 (QTD_STS_HALT)				& qh->hw_token) != 0			|| (ehci->hcd.state == USB_STATE_HALT)			|| (qh->qh_state == QH_STATE_IDLE);		/* fault: unlink the rest, since this qtd saw an error? */		if (unlikely ((token & QTD_STS_HALT) != 0)) {			freeing = unlink = 1;			/* status copied below */		/* QH halts only because of fault (above) or unlink (here). */		} else if (unlikely (halted != 0)) {			/* unlinking everything because of HC shutdown? */			if (ehci->hcd.state == USB_STATE_HALT) {				freeing = unlink = 1;			/* explicit unlink, maybe starting here? */			} else if (qh->qh_state == QH_STATE_IDLE					&& (urb->status == -ECONNRESET						|| urb->status == -ENOENT)) {				freeing = unlink = 1;			/* QH halted to unlink urbs _after_ this?  */			} else if (!unlink && (token & QTD_STS_ACTIVE) != 0) {				qtd = 0;				continue;			}			/* unlink the rest?  once we start unlinking, after			 * a fault or explicit unlink, we unlink all later			 * urbs.  usb spec requires that.			 */			if (unlink && urb->status == -EINPROGRESS)				urb->status = -ECONNRESET;		/* Else QH is active, so we must not modify QTDs		 * that HC may be working on.  No more qtds to check.		 */		} 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_STS_ACTIVE					| (EHCI_TUNE_CERR << 10));			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 && !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);	} 	/*	 * data transfer stage:  buffer setup	 */	len = urb->transfer_buffer_length;	if (likely (len > 0)) {		buf = map_buf = pci_map_single (ehci->hcd.pdev,			urb->transfer_buffer, len,			usb_pipein (urb->pipe)			    ? PCI_DMA_FROMDEVICE			    : PCI_DMA_TODEVICE);		if (unlikely (!buf))

⌨️ 快捷键说明

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