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

📄 pxa2xx_udc.c

📁 linux下面gadget设备驱动
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * linux/drivers/usb/gadget/pxa2xx_udc.c * Intel PXA25x and IXP4xx on-chip full speed USB device controllers * * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker) * Copyright (C) 2003 Robert Schwebel, Pengutronix * Copyright (C) 2003 Benedikt Spranger, Pengutronix * Copyright (C) 2003 David Brownell * Copyright (C) 2003 Joshua Wise * * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA * */// #define	VERBOSE	DBG_VERBOSE#include <linux/device.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/ioport.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/delay.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/timer.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/proc_fs.h>#include <linux/mm.h>#include <linux/platform_device.h>#include <linux/dma-mapping.h>#include <linux/irq.h>#include <linux/clk.h>#include <linux/err.h>#include <asm/byteorder.h>#include <asm/dma.h>#include <asm/gpio.h>#include <asm/io.h>#include <asm/system.h>#include <asm/mach-types.h>#include <asm/unaligned.h>#include <asm/hardware.h>#include <linux/usb/ch9.h>#include <linux/usb/gadget.h>#include <asm/mach/udc_pxa2xx.h>/* * This driver handles the USB Device Controller (UDC) in Intel's PXA 25x * series processors.  The UDC for the IXP 4xx series is very similar. * There are fifteen endpoints, in addition to ep0. * * Such controller drivers work with a gadget driver.  The gadget driver * returns descriptors, implements configuration and data protocols used * by the host to interact with this device, and allocates endpoints to * the different protocol interfaces.  The controller driver virtualizes * usb hardware so that the gadget drivers will be more portable. * * This UDC hardware wants to implement a bit too much USB protocol, so * it constrains the sorts of USB configuration change events that work. * The errata for these chips are misleading; some "fixed" bugs from * pxa250 a0/a1 b0/b1/b2 sure act like they're still there. * * Note that the UDC hardware supports DMA (except on IXP) but that's * not used here.  IN-DMA (to host) is simple enough, when the data is * suitably aligned (16 bytes) ... the network stack doesn't do that, * other software can.  OUT-DMA is buggy in most chip versions, as well * as poorly designed (data toggle not automatic).  So this driver won't * bother using DMA.  (Mostly-working IN-DMA support was available in * kernels before 2.6.23, but was never enabled or well tested.) */#define	DRIVER_VERSION	"30-June-2007"#define	DRIVER_DESC	"PXA 25x USB Device Controller driver"static const char driver_name [] = "pxa2xx_udc";static const char ep0name [] = "ep0";#ifdef CONFIG_ARCH_IXP4XX/* cpu-specific register addresses are compiled in to this code */#ifdef CONFIG_ARCH_PXA#error "Can't configure both IXP and PXA"#endif#endif#include "pxa2xx_udc.h"#ifdef	CONFIG_USB_PXA2XX_SMALL#define SIZE_STR	" (small)"#else#define SIZE_STR	""#endif/* --------------------------------------------------------------------------- *	endpoint related parts of the api to the usb controller hardware, *	used by gadget driver; and the inner talker-to-hardware core. * --------------------------------------------------------------------------- */static void pxa2xx_ep_fifo_flush (struct usb_ep *ep);static void nuke (struct pxa2xx_ep *, int status);/* one GPIO should be used to detect VBUS from the host */static int is_vbus_present(void){	struct pxa2xx_udc_mach_info		*mach = the_controller->mach;	if (mach->gpio_vbus)		return gpio_get_value(mach->gpio_vbus);	if (mach->udc_is_connected)		return mach->udc_is_connected();	return 1;}/* one GPIO should control a D+ pullup, so host sees this device (or not) */static void pullup_off(void){	struct pxa2xx_udc_mach_info		*mach = the_controller->mach;	if (mach->gpio_pullup)		gpio_set_value(mach->gpio_pullup, 0);	else if (mach->udc_command)		mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);}static void pullup_on(void){	struct pxa2xx_udc_mach_info		*mach = the_controller->mach;	if (mach->gpio_pullup)		gpio_set_value(mach->gpio_pullup, 1);	else if (mach->udc_command)		mach->udc_command(PXA2XX_UDC_CMD_CONNECT);}static void pio_irq_enable(int bEndpointAddress){        bEndpointAddress &= 0xf;        if (bEndpointAddress < 8)                UICR0 &= ~(1 << bEndpointAddress);        else {                bEndpointAddress -= 8;                UICR1 &= ~(1 << bEndpointAddress);	}}static void pio_irq_disable(int bEndpointAddress){        bEndpointAddress &= 0xf;        if (bEndpointAddress < 8)                UICR0 |= 1 << bEndpointAddress;        else {                bEndpointAddress -= 8;                UICR1 |= 1 << bEndpointAddress;        }}/* The UDCCR reg contains mask and interrupt status bits, * so using '|=' isn't safe as it may ack an interrupt. */#define UDCCR_MASK_BITS         (UDCCR_REM | UDCCR_SRM | UDCCR_UDE)static inline void udc_set_mask_UDCCR(int mask){	UDCCR = (UDCCR & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS);}static inline void udc_clear_mask_UDCCR(int mask){	UDCCR = (UDCCR & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS);}static inline void udc_ack_int_UDCCR(int mask){	/* udccr contains the bits we dont want to change */	__u32 udccr = UDCCR & UDCCR_MASK_BITS;	UDCCR = udccr | (mask & ~UDCCR_MASK_BITS);}/* * endpoint enable/disable * * we need to verify the descriptors used to enable endpoints.  since pxa2xx * endpoint configurations are fixed, and are pretty much always enabled, * there's not a lot to manage here. * * because pxa2xx can't selectively initialize bulk (or interrupt) endpoints, * (resetting endpoint halt and toggle), SET_INTERFACE is unusable except * for a single interface (with only the default altsetting) and for gadget * drivers that don't halt endpoints (not reset by set_interface).  that also * means that if you use ISO, you must violate the USB spec rule that all * iso endpoints must be in non-default altsettings. */static int pxa2xx_ep_enable (struct usb_ep *_ep,		const struct usb_endpoint_descriptor *desc){	struct pxa2xx_ep        *ep;	struct pxa2xx_udc       *dev;	ep = container_of (_ep, struct pxa2xx_ep, ep);	if (!_ep || !desc || ep->desc || _ep->name == ep0name			|| desc->bDescriptorType != USB_DT_ENDPOINT			|| ep->bEndpointAddress != desc->bEndpointAddress			|| ep->fifo_size < le16_to_cpu						(desc->wMaxPacketSize)) {		DMSG("%s, bad ep or descriptor\n", __FUNCTION__);		return -EINVAL;	}	/* xfer types must match, except that interrupt ~= bulk */	if (ep->bmAttributes != desc->bmAttributes			&& ep->bmAttributes != USB_ENDPOINT_XFER_BULK			&& desc->bmAttributes != USB_ENDPOINT_XFER_INT) {		DMSG("%s, %s type mismatch\n", __FUNCTION__, _ep->name);		return -EINVAL;	}	/* hardware _could_ do smaller, but driver doesn't */	if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK				&& le16_to_cpu (desc->wMaxPacketSize)						!= BULK_FIFO_SIZE)			|| !desc->wMaxPacketSize) {		DMSG("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name);		return -ERANGE;	}	dev = ep->dev;	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {		DMSG("%s, bogus device state\n", __FUNCTION__);		return -ESHUTDOWN;	}	ep->desc = desc;	ep->stopped = 0;	ep->pio_irqs = 0;	ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize);	/* flush fifo (mostly for OUT buffers) */	pxa2xx_ep_fifo_flush (_ep);	/* ... reset halt state too, if we could ... */	DBG(DBG_VERBOSE, "enabled %s\n", _ep->name);	return 0;}static int pxa2xx_ep_disable (struct usb_ep *_ep){	struct pxa2xx_ep	*ep;	unsigned long		flags;	ep = container_of (_ep, struct pxa2xx_ep, ep);	if (!_ep || !ep->desc) {		DMSG("%s, %s not enabled\n", __FUNCTION__,			_ep ? ep->ep.name : NULL);		return -EINVAL;	}	local_irq_save(flags);	nuke (ep, -ESHUTDOWN);	/* flush fifo (mostly for IN buffers) */	pxa2xx_ep_fifo_flush (_ep);	ep->desc = NULL;	ep->stopped = 1;	local_irq_restore(flags);	DBG(DBG_VERBOSE, "%s disabled\n", _ep->name);	return 0;}/*-------------------------------------------------------------------------*//* for the pxa2xx, these can just wrap kmalloc/kfree.  gadget drivers * must still pass correctly initialized endpoints, since other controller * drivers may care about how it's currently set up (dma issues etc). *//* *	pxa2xx_ep_alloc_request - allocate a request data structure */static struct usb_request *pxa2xx_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags){	struct pxa2xx_request *req;	req = kzalloc(sizeof(*req), gfp_flags);	if (!req)		return NULL;	INIT_LIST_HEAD (&req->queue);	return &req->req;}/* *	pxa2xx_ep_free_request - deallocate a request data structure */static voidpxa2xx_ep_free_request (struct usb_ep *_ep, struct usb_request *_req){	struct pxa2xx_request	*req;	req = container_of (_req, struct pxa2xx_request, req);	WARN_ON (!list_empty (&req->queue));	kfree(req);}/*-------------------------------------------------------------------------*//* *	done - retire a request; caller blocked irqs */static void done(struct pxa2xx_ep *ep, struct pxa2xx_request *req, int status){	unsigned		stopped = ep->stopped;	list_del_init(&req->queue);	if (likely (req->req.status == -EINPROGRESS))		req->req.status = status;	else		status = req->req.status;	if (status && status != -ESHUTDOWN)		DBG(DBG_VERBOSE, "complete %s req %p stat %d len %u/%u\n",			ep->ep.name, &req->req, status,			req->req.actual, req->req.length);	/* don't modify queue heads during completion callback */	ep->stopped = 1;	req->req.complete(&ep->ep, &req->req);	ep->stopped = stopped;}static inline void ep0_idle (struct pxa2xx_udc *dev){	dev->ep0state = EP0_IDLE;}static intwrite_packet(volatile u32 *uddr, struct pxa2xx_request *req, unsigned max){	u8		*buf;	unsigned	length, count;	buf = req->req.buf + req->req.actual;	prefetch(buf);	/* how big will this packet be? */	length = min(req->req.length - req->req.actual, max);	req->req.actual += length;	count = length;	while (likely(count--))		*uddr = *buf++;	return length;}/* * write to an IN endpoint fifo, as many packets as possible. * irqs will use this to write the rest later. * caller guarantees at least one packet buffer is ready (or a zlp). */static intwrite_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req){	unsigned		max;	max = le16_to_cpu(ep->desc->wMaxPacketSize);	do {		unsigned	count;		int		is_last, is_short;		count = write_packet(ep->reg_uddr, req, max);		/* last packet is usually short (or a zlp) */		if (unlikely (count != max))			is_last = is_short = 1;		else {			if (likely(req->req.length != req->req.actual)					|| req->req.zero)				is_last = 0;			else				is_last = 1;			/* interrupt/iso maxpacket may not fill the fifo */			is_short = unlikely (max < ep->fifo_size);		}		DBG(DBG_VERY_NOISY, "wrote %s %d bytes%s%s %d left %p\n",			ep->ep.name, count,			is_last ? "/L" : "", is_short ? "/S" : "",			req->req.length - req->req.actual, req);		/* let loose that packet. maybe try writing another one,		 * double buffering might work.  TSP, TPC, and TFS		 * bit values are the same for all normal IN endpoints.		 */		*ep->reg_udccs = UDCCS_BI_TPC;		if (is_short)			*ep->reg_udccs = UDCCS_BI_TSP;		/* requests complete when all IN data is in the FIFO */		if (is_last) {			done (ep, req, 0);			if (list_empty(&ep->queue))				pio_irq_disable (ep->bEndpointAddress);			return 1;		}		// TODO experiment: how robust can fifo mode tweaking be?		// double buffering is off in the default fifo mode, which		// prevents TFS from being set here.	} while (*ep->reg_udccs & UDCCS_BI_TFS);	return 0;}/* caller asserts req->pending (ep0 irq status nyet cleared); starts * ep0 data stage.  these chips want very simple state transitions. */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);#ifndef CONFIG_ARCH_IXP4XX#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#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 int

⌨️ 快捷键说明

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