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

📄 pxa2xx_udc.c

📁 优龙2410linux2.6.8内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
 */static inlinevoid ep0start(struct pxa2xx_udc *dev, u32 flags, const char *tag){	UDCCS0 = flags|UDCCS0_SA|UDCCS0_OPR;	USIR0 = USIR0_IR0;	dev->req_pending = 0;	DBG(DBG_VERY_NOISY, "%s %s, %02x/%02x\n",		__FUNCTION__, tag, UDCCS0, flags);}static intwrite_ep0_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req){	unsigned	count;	int		is_short;	count = write_packet(&UDDR0, req, EP0_FIFO_SIZE);	ep->dev->stats.write.bytes += count;	/* last packet "must be" short (or a zlp) */	is_short = (count != EP0_FIFO_SIZE);	DBG(DBG_VERY_NOISY, "ep0in %d bytes %d left %p\n", count,		req->req.length - req->req.actual, req);	if (unlikely (is_short)) {		if (ep->dev->req_pending)			ep0start(ep->dev, UDCCS0_IPR, "short IN");		else			UDCCS0 = UDCCS0_IPR;		count = req->req.length;		done (ep, req, 0);		ep0_idle(ep->dev);#if 1		/* This seems to get rid of lost status irqs in some cases:		 * host responds quickly, or next request involves config		 * change automagic, or should have been hidden, or ...		 *		 * FIXME get rid of all udelays possible...		 */		if (count >= EP0_FIFO_SIZE) {			count = 100;			do {				if ((UDCCS0 & UDCCS0_OPR) != 0) {					/* clear OPR, generate ack */					UDCCS0 = UDCCS0_OPR;					break;				}				count--;				udelay(1);			} while (count);		}#endif	} else if (ep->dev->req_pending)		ep0start(ep->dev, 0, "IN");	return is_short;}/* * read_fifo -  unload packet(s) from the fifo we use for usb OUT * transfers and put them into the request.  caller should have made * sure there's at least one packet ready. * * returns true if the request completed because of short packet or the * request buffer having filled (and maybe overran till end-of-packet). */static intread_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req){	for (;;) {		u32		udccs;		u8		*buf;		unsigned	bufferspace, count, is_short;		/* make sure there's a packet in the FIFO.		 * UDCCS_{BO,IO}_RPC are all the same bit value.		 * UDCCS_{BO,IO}_RNE are all the same bit value.		 */		udccs = *ep->reg_udccs;		if (unlikely ((udccs & UDCCS_BO_RPC) == 0))			break;		buf = req->req.buf + req->req.actual;		prefetchw(buf);		bufferspace = req->req.length - req->req.actual;		/* read all bytes from this packet */		if (likely (udccs & UDCCS_BO_RNE)) {			count = 1 + (0x0ff & *ep->reg_ubcr);			req->req.actual += min (count, bufferspace);		} else /* zlp */			count = 0;		is_short = (count < ep->ep.maxpacket);		DBG(DBG_VERY_NOISY, "read %s %02x, %d bytes%s req %p %d/%d\n",			ep->ep.name, udccs, count,			is_short ? "/S" : "",			req, req->req.actual, req->req.length);		while (likely (count-- != 0)) {			u8	byte = (u8) *ep->reg_uddr;			if (unlikely (bufferspace == 0)) {				/* this happens when the driver's buffer				 * is smaller than what the host sent.				 * discard the extra data.				 */				if (req->req.status != -EOVERFLOW)					DMSG("%s overflow %d\n",						ep->ep.name, count);				req->req.status = -EOVERFLOW;			} else {				*buf++ = byte;				bufferspace--;			}		}		*ep->reg_udccs =  UDCCS_BO_RPC;		/* RPC/RSP/RNE could now reflect the other packet buffer */		/* iso is one request per packet */		if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) {			if (udccs & UDCCS_IO_ROF)				req->req.status = -EHOSTUNREACH;			/* more like "is_done" */			is_short = 1;		}		/* completion */		if (is_short || req->req.actual == req->req.length) {			done (ep, req, 0);			if (list_empty(&ep->queue))				pio_irq_disable (ep->bEndpointAddress);			return 1;		}		/* finished that packet.  the next one may be waiting... */	}	return 0;}/* * special ep0 version of the above.  no UBCR0 or double buffering; status * handshaking is magic.  most device protocols don't need control-OUT. * CDC vendor commands (and RNDIS), mass storage CB/CBI, and some other * protocols do use them. */static intread_ep0_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req){	u8		*buf, byte;	unsigned	bufferspace;	buf = req->req.buf + req->req.actual;	bufferspace = req->req.length - req->req.actual;	while (UDCCS0 & UDCCS0_RNE) {		byte = (u8) UDDR0;		if (unlikely (bufferspace == 0)) {			/* this happens when the driver's buffer			 * is smaller than what the host sent.			 * discard the extra data.			 */			if (req->req.status != -EOVERFLOW)				DMSG("%s overflow\n", ep->ep.name);			req->req.status = -EOVERFLOW;		} else {			*buf++ = byte;			req->req.actual++;			bufferspace--;		}	}	UDCCS0 = UDCCS0_OPR | UDCCS0_IPR;	/* completion */	if (req->req.actual >= req->req.length)		return 1;	/* finished that packet.  the next one may be waiting... */	return 0;}#ifdef	USE_DMA#define	MAX_IN_DMA	((DCMD_LENGTH + 1) - BULK_FIFO_SIZE)static voidstart_dma_nodesc(struct pxa2xx_ep *ep, struct pxa2xx_request *req, int is_in){	u32	dcmd = req->req.length;	u32	buf = req->req.dma;	u32	fifo = io_v2p ((u32)ep->reg_uddr);	/* caller guarantees there's a packet or more remaining	 *  - IN may end with a short packet (TSP set separately),	 *  - OUT is always full length	 */	buf += req->req.actual;	dcmd -= req->req.actual;	ep->dma_fixup = 0;	/* no-descriptor mode can be simple for bulk-in, iso-in, iso-out */	DCSR(ep->dma) = DCSR_NODESC;	if (is_in) {		DSADR(ep->dma) = buf;		DTADR(ep->dma) = fifo;		if (dcmd > MAX_IN_DMA)			dcmd = MAX_IN_DMA;		else			ep->dma_fixup = (dcmd % ep->ep.maxpacket) != 0;		dcmd |= DCMD_BURST32 | DCMD_WIDTH1			| DCMD_FLOWTRG | DCMD_INCSRCADDR;	} else {#ifdef USE_OUT_DMA		DSADR(ep->dma) = fifo;		DTADR(ep->dma) = buf;		if (ep->bmAttributes != USB_ENDPOINT_XFER_ISOC)			dcmd = ep->ep.maxpacket;		dcmd |= DCMD_BURST32 | DCMD_WIDTH1			| DCMD_FLOWSRC | DCMD_INCTRGADDR;#endif	}	DCMD(ep->dma) = dcmd;	DCSR(ep->dma) = DCSR_RUN | DCSR_NODESC		| (unlikely(is_in)			? DCSR_STOPIRQEN	/* use dma_nodesc_handler() */			: 0);			/* use handle_ep() */}static void kick_dma(struct pxa2xx_ep *ep, struct pxa2xx_request *req){	int	is_in = ep->bEndpointAddress & USB_DIR_IN;	if (is_in) {		/* unaligned tx buffers and zlps only work with PIO */		if ((req->req.dma & 0x0f) != 0				|| unlikely((req->req.length - req->req.actual)						== 0)) {			pio_irq_enable(ep->bEndpointAddress);			if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0)				(void) write_fifo(ep, req);		} else {			start_dma_nodesc(ep, req, USB_DIR_IN);		}	} else {		if ((req->req.length - req->req.actual) < ep->ep.maxpacket) {			DMSG("%s short dma read...\n", ep->ep.name);			/* we're always set up for pio out */			read_fifo (ep, req);		} else {			*ep->reg_udccs = UDCCS_BO_DME				| (*ep->reg_udccs & UDCCS_BO_FST);			start_dma_nodesc(ep, req, USB_DIR_OUT);		}	}}static void cancel_dma(struct pxa2xx_ep *ep){	struct pxa2xx_request	*req;	u32			tmp;	if (DCSR(ep->dma) == 0 || list_empty(&ep->queue))		return;	DCSR(ep->dma) = 0;	while ((DCSR(ep->dma) & DCSR_STOPSTATE) == 0)		cpu_relax();	req = list_entry(ep->queue.next, struct pxa2xx_request, queue);	tmp = DCMD(ep->dma) & DCMD_LENGTH;	req->req.actual = req->req.length - (tmp & DCMD_LENGTH);	/* the last tx packet may be incomplete, so flush the fifo.	 * FIXME correct req.actual if we can	 */	if (ep->bEndpointAddress & USB_DIR_IN)		*ep->reg_udccs = UDCCS_BI_FTF;}/* dma channel stopped ... normal tx end (IN), or on error (IN/OUT) */static void dma_nodesc_handler(int dmach, void *_ep, struct pt_regs *r){	struct pxa2xx_ep	*ep = _ep;	struct pxa2xx_request	*req;	u32			tmp, completed;	local_irq_disable();	req = list_entry(ep->queue.next, struct pxa2xx_request, queue);	ep->dma_irqs++;	ep->dev->stats.irqs++;	HEX_DISPLAY(ep->dev->stats.irqs);	/* ack/clear */	tmp = DCSR(ep->dma);	DCSR(ep->dma) = tmp;	if ((tmp & DCSR_STOPSTATE) == 0			|| (DDADR(ep->dma) & DDADR_STOP) != 0) {		DBG(DBG_VERBOSE, "%s, dcsr %08x ddadr %08x\n",			ep->ep.name, DCSR(ep->dma), DDADR(ep->dma));		goto done;	}	DCSR(ep->dma) = 0;	/* clear DCSR_STOPSTATE */	/* update transfer status */	completed = tmp & DCSR_BUSERR;	if (ep->bEndpointAddress & USB_DIR_IN)		tmp = DSADR(ep->dma);	else		tmp = DTADR(ep->dma);	req->req.actual = tmp - req->req.dma;	/* FIXME seems we sometimes see partial transfers... */	if (unlikely(completed != 0))		req->req.status = -EIO;	else if (req->req.actual) {		/* these registers have zeroes in low bits; they miscount		 * some (end-of-transfer) short packets:  tx 14 as tx 12		 */		if (ep->dma_fixup)			req->req.actual = min(req->req.actual + 3,						req->req.length);		tmp = (req->req.length - req->req.actual);		completed = (tmp == 0);		if (completed && (ep->bEndpointAddress & USB_DIR_IN)) {			/* maybe validate final short packet ... */			if ((req->req.actual % ep->ep.maxpacket) != 0)				*ep->reg_udccs = UDCCS_BI_TSP/*|UDCCS_BI_TPC*/;			/* ... or zlp, using pio fallback */			else if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK					&& req->req.zero) {				DMSG("%s zlp terminate ...\n", ep->ep.name);				completed = 0;			}		}	}	if (likely(completed)) {		done(ep, req, 0);		/* maybe re-activate after completion */		if (ep->stopped || list_empty(&ep->queue))			goto done;		req = list_entry(ep->queue.next, struct pxa2xx_request, queue);	}	kick_dma(ep, req);done:	local_irq_enable();}#endif/*-------------------------------------------------------------------------*/static intpxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags){	struct pxa2xx_request	*req;	struct pxa2xx_ep	*ep;	struct pxa2xx_udc	*dev;	unsigned long		flags;	req = container_of(_req, struct pxa2xx_request, req);	if (unlikely (!_req || !_req->complete || !_req->buf			|| !list_empty(&req->queue))) {		DMSG("%s, bad params\n", __FUNCTION__);		return -EINVAL;	}	ep = container_of(_ep, struct pxa2xx_ep, ep);	if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {		DMSG("%s, bad ep\n", __FUNCTION__);		return -EINVAL;	}	dev = ep->dev;	if (unlikely (!dev->driver			|| dev->gadget.speed == USB_SPEED_UNKNOWN)) {		DMSG("%s, bogus device state\n", __FUNCTION__);		return -ESHUTDOWN;	}	/* iso is always one packet per request, that's the only way	 * we can report per-packet status.  that also helps with dma.	 */	if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC			&& req->req.length > le16_to_cpu						(ep->desc->wMaxPacketSize)))		return -EMSGSIZE;#ifdef	USE_DMA	// FIXME caller may already have done the dma mapping	if (ep->dma >= 0) {		_req->dma = dma_map_single(dev->dev,			_req->buf, _req->length,			((ep->bEndpointAddress & USB_DIR_IN) != 0)				? DMA_TO_DEVICE				: DMA_FROM_DEVICE);	}#endif	DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n",	     _ep->name, _req, _req->length, _req->buf);	local_irq_save(flags);	_req->status = -EINPROGRESS;	_req->actual = 0;	/* kickstart this i/o queue? */	if (list_empty(&ep->queue) && !ep->stopped) {		if (ep->desc == 0 /* ep0 */) {			unsigned	length = _req->length;			switch (dev->ep0state) {			case EP0_IN_DATA_PHASE:				dev->stats.write.ops++;				if (write_ep0_fifo(ep, req))					req = 0;				break;			case EP0_OUT_DATA_PHASE:				dev->stats.read.ops++;				/* messy ... */				if (dev->req_config) {					DBG(DBG_VERBOSE, "ep0 config ack%s\n",						dev->has_cfr ?  "" : " raced");					if (dev->has_cfr)						UDCCFR = UDCCFR_AREN|UDCCFR_ACM;					done(ep, req, 0);					dev->ep0state = EP0_END_XFER;					return 0;				}				if (dev->req_pending)					ep0start(dev, UDCCS0_IPR, "OUT");				if (length == 0 || ((UDCCS0 & UDCCS0_RNE) != 0						&& read_ep0_fifo(ep, req))) {					ep0_idle(dev);					done(ep, req, 0);					req = 0;				}				break;			default:				DMSG("ep0 i/o, odd state %d\n", dev->ep0state);				local_irq_restore (flags);				return -EL2HLT;			}#ifdef	USE_DMA		/* either start dma or prime pio pump */		} else if (ep->dma >= 0) {			kick_dma(ep, req);#endif		/* can the FIFO can satisfy the request immediately? */		} else if ((ep->bEndpointAddress & USB_DIR_IN) != 0				&& (*ep->reg_udccs & UDCCS_BI_TFS) != 0				&& write_fifo(ep, req)) {			req = 0;		} else if ((*ep->reg_udccs & UDCCS_BO_RFS) != 0				&& read_fifo(ep, req)) {			req = 0;		}		if (likely (req && ep->desc) && ep->dma < 0)			pio_irq_enable(ep->bEndpointAddress);	}	/* pio or dma irq handler advances the queue. */	if (likely (req != 0))		list_add_tail(&req->queue, &ep->queue);	local_irq_restore(flags);	return 0;}/* * 	nuke - dequeue ALL requests */static void nuke(struct pxa2xx_ep *ep, int status){	struct pxa2xx_request *req;	/* called with irqs blocked */#ifdef	USE_DMA	if (ep->dma >= 0 && !ep->stopped)		cancel_dma(ep);#endif	while (!list_empty(&ep->queue)) {		req = list_entry(ep->queue.next,				struct pxa2xx_request,				queue);		done(ep, req, status);	}	if (ep->desc)		pio_irq_disable (ep->bEndpointAddress);}/* dequeue JUST ONE request */static int pxa2xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req){	struct pxa2xx_ep	*ep;	struct pxa2xx_request	*req;	unsigned long		flags;	ep = container_of(_ep, struct pxa2xx_ep, ep);	if (!_ep || ep->ep.name == ep0name)		return -EINVAL;

⌨️ 快捷键说明

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