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

📄 pxa2xx_udc.c

📁 linux下面gadget设备驱动
💻 C
📖 第 1 页 / 共 4 页
字号:
read_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;}/*-------------------------------------------------------------------------*/static intpxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t 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;	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 = NULL;				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							|UDCCFR_MB1;					done(ep, req, 0);					dev->ep0state = EP0_END_XFER;					local_irq_restore (flags);					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 = NULL;				}				break;			default:				DMSG("ep0 i/o, odd state %d\n", dev->ep0state);				local_irq_restore (flags);				return -EL2HLT;			}		/* can the FIFO can satisfy the request immediately? */		} else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) {			if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0					&& write_fifo(ep, req))				req = NULL;		} else if ((*ep->reg_udccs & UDCCS_BO_RFS) != 0				&& read_fifo(ep, req)) {			req = NULL;		}		if (likely (req && ep->desc))			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 */	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;	local_irq_save(flags);	/* make sure it's actually queued on this endpoint */	list_for_each_entry (req, &ep->queue, queue) {		if (&req->req == _req)			break;	}	if (&req->req != _req) {		local_irq_restore(flags);		return -EINVAL;	}	done(ep, req, -ECONNRESET);	local_irq_restore(flags);	return 0;}/*-------------------------------------------------------------------------*/static int pxa2xx_ep_set_halt(struct usb_ep *_ep, int value){	struct pxa2xx_ep	*ep;	unsigned long		flags;	ep = container_of(_ep, struct pxa2xx_ep, ep);	if (unlikely (!_ep			|| (!ep->desc && ep->ep.name != ep0name))			|| ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) {		DMSG("%s, bad ep\n", __FUNCTION__);		return -EINVAL;	}	if (value == 0) {		/* this path (reset toggle+halt) is needed to implement		 * SET_INTERFACE on normal hardware.  but it can't be		 * done from software on the PXA UDC, and the hardware		 * forgets to do it as part of SET_INTERFACE automagic.		 */		DMSG("only host can clear %s halt\n", _ep->name);		return -EROFS;	}	local_irq_save(flags);	if ((ep->bEndpointAddress & USB_DIR_IN) != 0			&& ((*ep->reg_udccs & UDCCS_BI_TFS) == 0			   || !list_empty(&ep->queue))) {		local_irq_restore(flags);		return -EAGAIN;	}	/* FST bit is the same for control, bulk in, bulk out, interrupt in */	*ep->reg_udccs = UDCCS_BI_FST|UDCCS_BI_FTF;	/* ep0 needs special care */	if (!ep->desc) {		start_watchdog(ep->dev);		ep->dev->req_pending = 0;		ep->dev->ep0state = EP0_STALL;	/* and bulk/intr endpoints like dropping stalls too */	} else {		unsigned i;		for (i = 0; i < 1000; i += 20) {			if (*ep->reg_udccs & UDCCS_BI_SST)				break;			udelay(20);		}	}	local_irq_restore(flags);	DBG(DBG_VERBOSE, "%s halt\n", _ep->name);	return 0;}static int pxa2xx_ep_fifo_status(struct usb_ep *_ep){	struct pxa2xx_ep        *ep;	ep = container_of(_ep, struct pxa2xx_ep, ep);	if (!_ep) {		DMSG("%s, bad ep\n", __FUNCTION__);		return -ENODEV;	}	/* pxa can't report unclaimed bytes from IN fifos */	if ((ep->bEndpointAddress & USB_DIR_IN) != 0)		return -EOPNOTSUPP;	if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN			|| (*ep->reg_udccs & UDCCS_BO_RFS) == 0)		return 0;	else		return (*ep->reg_ubcr & 0xfff) + 1;}static void pxa2xx_ep_fifo_flush(struct usb_ep *_ep){	struct pxa2xx_ep        *ep;	ep = container_of(_ep, struct pxa2xx_ep, ep);	if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) {		DMSG("%s, bad ep\n", __FUNCTION__);		return;	}	/* toggle and halt bits stay unchanged */	/* for OUT, just read and discard the FIFO contents. */	if ((ep->bEndpointAddress & USB_DIR_IN) == 0) {		while (((*ep->reg_udccs) & UDCCS_BO_RNE) != 0)			(void) *ep->reg_uddr;		return;	}	/* most IN status is the same, but ISO can't stall */	*ep->reg_udccs = UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR		| (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC)			? 0 : UDCCS_BI_SST;}static struct usb_ep_ops pxa2xx_ep_ops = {	.enable		= pxa2xx_ep_enable,	.disable	= pxa2xx_ep_disable,	.alloc_request	= pxa2xx_ep_alloc_request,	.free_request	= pxa2xx_ep_free_request,	.queue		= pxa2xx_ep_queue,	.dequeue	= pxa2xx_ep_dequeue,	.set_halt	= pxa2xx_ep_set_halt,	.fifo_status	= pxa2xx_ep_fifo_status,	.fifo_flush	= pxa2xx_ep_fifo_flush,};/* --------------------------------------------------------------------------- *	device-scoped parts of the api to the usb controller hardware * --------------------------------------------------------------------------- */static int pxa2xx_udc_get_frame(struct usb_gadget *_gadget){	return ((UFNRH & 0x07) << 8) | (UFNRL & 0xff);}static int pxa2xx_udc_wakeup(struct usb_gadget *_gadget){	/* host may not have enabled remote wakeup */	if ((UDCCS0 & UDCCS0_DRWF) == 0)		return -EHOSTUNREACH;	udc_set_mask_UDCCR(UDCCR_RSM);	return 0;}static void stop_activity(struct pxa2xx_udc *, struct usb_gadget_driver *);static void udc_enable (struct pxa2xx_udc *);static void udc_disable(struct pxa2xx_udc *);/* We disable the UDC -- and its 48 MHz clock -- whenever it's not * in active use. */static int pullup(struct pxa2xx_udc *udc, int is_active){	is_active = is_active && udc->vbus && udc->pullup;	DMSG("%s\n", is_active ? "active" : "inactive");	if (is_active)		udc_enable(udc);	else {		if (udc->gadget.speed != USB_SPEED_UNKNOWN) {			DMSG("disconnect %s\n", udc->driver				? udc->driver->driver.name				: "(no driver)");			stop_activity(udc, udc->driver);		}		udc_disable(udc);	}	return 0;}/* VBUS reporting logically comes from a transceiver */static int pxa2xx_udc_vbus_session(struct usb_gadget *_gadget, int is_active){	struct pxa2xx_udc	*udc;	udc = container_of(_gadget, struct pxa2xx_udc, gadget);	udc->vbus = is_active = (is_active != 0);	DMSG("vbus %s\n", is_active ? "supplied" : "inactive");	pullup(udc, is_active);	return 0;}/* drivers may have software control over D+ pullup */static int pxa2xx_udc_pullup(struct usb_gadget *_gadget, int is_active){	struct pxa2xx_udc	*udc;	udc = container_of(_gadget, struct pxa2xx_udc, gadget);	/* not all boards support pullup control */	if (!udc->mach->gpio_pullup && !udc->mach->udc_command)		return -EOPNOTSUPP;	is_active = (is_active != 0);	udc->pullup = is_active;	pullup(udc, is_active);	return 0;}static const struct usb_gadget_ops pxa2xx_udc_ops = {	.get_frame	= pxa2xx_udc_get_frame,	.wakeup		= pxa2xx_udc_wakeup,	.vbus_session	= pxa2xx_udc_vbus_session,	.pullup		= pxa2xx_udc_pullup,	// .vbus_draw ... boards may consume current from VBUS, up to	// 100-500mA based on config.  the 500uA suspend ceiling means	// that exclusively vbus-powered PXA designs violate USB specs.};/*-------------------------------------------------------------------------*/#ifdef CONFIG_USB_GADGET_DEBUG_FILESstatic const char proc_node_name [] = "driver/udc";static intudc_proc_read(char *page, char **start, off_t off, int count,		int *eof, void *_dev){	char			*buf = page;	struct pxa2xx_udc	*dev = _dev;	char			*next = buf;	unsigned		size = count;	unsigned long		flags;	int			i, t;	u32			tmp;	if (off != 0)		return 0;	local_irq_save(flags);	/* basic device status */	t = scnprintf(next, size, DRIVER_DESC "\n"		"%s version: %s\nGadget driver: %s\nHost %s\n\n",		driver_name, DRIVER_VERSION SIZE_STR "(pio)",		dev->driver ? dev->driver->driver.name : "(none)",		is_vbus_present() ? "full speed" : "disconnected");	size -= t;	next += t;	/* registers for device and ep0 */	t = scnprintf(next, size,		"uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n",		UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL);	size -= t;	next += t;	tmp = UDCCR;	t = scnprintf(next, size,		"udccr %02X =%s%s%s%s%s%s%s%s\n", tmp,		(tmp & UDCCR_REM) ? " rem" : "",		(tmp & UDCCR_RSTIR) ? " rstir" : "",		(tmp & UDCCR_SRM) ? " srm" : "",		(tmp & UDCCR_SUSIR) ? " susir" : "",		(tmp & UDCCR_RESIR) ? " resir" : "",		(tmp & UDCCR_RSM) ? " rsm" : "",		(tmp & UDCCR_UDA) ? " uda" : "",		(tmp & UDCCR_UDE) ? " ude" : "");	size -= t;	next += t;	tmp = UDCCS0;	t = scnprintf(next, size,		"udccs0 %02X =%s%s%s%s%s%s%s%s\n", tmp,		(tmp & UDCCS0_SA) ? " sa" : "",		(tmp & UDCCS0_RNE) ? " rne" : "",		(tmp & UDCCS0_FST) ? " fst" : "",		(tmp & UDCCS0_SST) ? " sst" : "",		(tmp & UDCCS0_DRWF) ? " dwrf" : "",		(tmp & UDCCS0_FTF) ? " ftf" : "",		(tmp & UDCCS0_IPR) ? " ipr" : "",		(tmp & UDCCS0_OPR) ? " opr" : "");	size -= t;	next += t;	if (dev->has_cfr) {		tmp = UDCCFR;		t = scnprintf(next, size,			"udccfr %02X =%s%s\n", tmp,			(tmp & UDCCFR_AREN) ? " aren" : "",			(tmp & UDCCFR_ACM) ? " acm" : "");		size -= t;		next += t;	}	if (!is_vbus_present() || !dev->driver)		goto done;	t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n",		dev->stats.write.bytes, dev->stats.write.ops,		dev->stats.read.bytes, dev->stats.read.ops,		dev->stats.irqs);	size -= t;	next += t;	/* dump endpoint queues */	for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {		struct pxa2xx_ep	*ep = &dev->ep [i];		struct pxa2xx_request	*req;		if (i != 0) {			const struct usb_endpoint_descriptor	*d;			d = ep->desc;			if (!d)				continue;			tmp = *dev->ep [i].reg_udccs;			t = scnprintf(next, size,				"%s max %d %s udccs %02x irqs %lu\n",				ep->ep.name, le16_to_cpu (d->wMaxPacketSize),				"pio", tmp, ep->pio_irqs);			/* TODO translate all five groups of udccs bits! */		} else /* ep0 should only have one transfer queued */			t = scnprintf(next, size, "ep0 max 16 pio irqs %lu\n",				ep->pio_irqs);		if (t <= 0 || t > size)			goto done;		size -= t;		next += t;		if (list_empty(&ep->queue)) {			t = scnprintf(next, size, "\t(nothing queued)\n");			if (t <= 0 || t > size)				goto done;			size -= t;			next += t;			continue;		}		list_for_each_entry(req, &ep->queue, queue) {			t = scnprintf(next, size,					"\treq %p len %d/%d buf %p\n",					&req->req, req->req.actual,					req->req.length, req->req.buf);			if (t <= 0 || t > size)				goto done;			size -= t;			next += t;		}	}done:	local_irq_restore(flags);	*eof = 1;	return count - size;}#define create_proc_files() \	create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev)#define remove_proc_files() \	remove_proc_entry(proc_node_name, NULL)#else	/* !CONFIG_USB_GADGET_DEBUG_FILES */#define create_proc_files() do {} while (0)#define remove_proc_files() do {} while (0)#endif	/* CONFIG_USB_GADGET_DEBUG_FILES *//*-------------------------------------------------------------------------*//* *	udc_disable - disable USB device controller */static void udc_disable(struct pxa2xx_udc *dev){	/* block all irqs */	udc_set_mask_UDCCR(UDCCR_SRM|UDCCR_REM);	UICR0 = UICR1 = 0xff;	UFNRH = UFNRH_SIM;	/* if hardware supports it, disconnect from usb */	pullup_off();	udc_clear_mask_UDCCR(UDCCR_UDE);#ifdef	CONFIG_ARCH_PXA        /* Disable clock for USB device */	clk_disable(dev->clk);#endif	ep0_idle (dev);	dev->gadget.speed = USB_SPEED_UNKNOWN;}/* *	udc_reinit - initialize software state */static void udc_reinit(struct pxa2xx_udc *dev){	u32	i;	/* device/ep0 records init */	INIT_LIST_HEAD (&dev->gadget.ep_list);	INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);	dev->ep0state = EP0_IDLE;	/* basic endpoint records init */	for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {		struct pxa2xx_ep *ep = &dev->ep[i];		if (i != 0)			list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);		ep->desc = NULL;		ep->stopped = 0;

⌨️ 快捷键说明

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