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

📄 jz4730_udc.c

📁 linux下面gadget设备驱动
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * JZ4730 USB Device Controller driver * * This file is licensed under the terms of the GNU General Public * License version 2.  This program is licensed "as is" without any * warranty of any kind, whether express or implied. *//* * This device has ep0 and six bulk/interrupt/iso endpoints. * *  - Endpoint numbering is fixed: ep0, ep1in-int, ep2in-bulk, ep3in-bulk, *	  ep4in-iso, ep5out-bulk, ep6out-bulk, ep7out-iso. *  - Gadget drivers can choose ep maxpacket (8/16/32/64). *  - Just PIO mode currently. */#include <linux/kernel.h>#include <linux/module.h>#include <linux/platform_device.h>#include <linux/delay.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/proc_fs.h>#include <linux/usb.h>#include <linux/usb/gadget.h>#include <asm/byteorder.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm-mips/mach-jz4730/regs.h>#include <asm-mips/mach-jz4730/ops.h>#include "jz4730_udc.h"//#define DEBUG(fmt,args...) printk(KERN_DEBUG fmt , ## args)//#define DEBUG_EP0(fmt,args...) printk(KERN_DEBUG fmt , ## args)#ifndef DEBUG# define DEBUG(fmt,args...) do {} while(0)#endif#ifndef DEBUG_EP0# define DEBUG_EP0(fmt,args...) do {} while(0)#endif#define	DRIVER_DESC		"JZ4730 USB Device Controller"#define	DRIVER_VERSION		"20 Sep 2007"static const char driver_name [] = "jz4730_udc";static const char driver_desc [] = DRIVER_DESC;static unsigned int udc_debug = 0; /* 0: normal mode, 1: test udc cable type mode */module_param(udc_debug, int, 0);MODULE_PARM_DESC(udc_debug, "test udc cable type");#ifdef CONFIG_JZ_UDC_HOTPLUGextern int jz_udc_active; /* 0: No actions; 1: Have actions */#endif/* * Local declarations. */static void nuke(struct jz4730_ep *, int status);static inline void pio_irq_enable(struct jz4730_ep *ep);static inline void pio_irq_disable(struct jz4730_ep *ep);static void jz4730_udc_release (struct device *dev) {}/*-------------------------------------------------------------------------*/static int jz4730_ep_enable(struct usb_ep *_ep, 			    const struct usb_endpoint_descriptor *desc){	struct jz4730_udc *dev;	struct jz4730_ep *ep;	unsigned long flags;	u32 max;	ep = container_of(_ep, struct jz4730_ep, ep);	if (!_ep || !desc || ep->desc	    || desc->bDescriptorType != USB_DT_ENDPOINT)		return -EINVAL;	dev = ep->dev;	if (ep == &dev->ep[0])		return -EINVAL;	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)		return -ESHUTDOWN;	if (ep->index != (desc->bEndpointAddress & 0x0f))		return -EINVAL;	switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {	case USB_ENDPOINT_XFER_BULK:	case USB_ENDPOINT_XFER_INT:		break;	default:		return -EINVAL;	}//	max = le16_to_cpu(get_unaligned(&desc->wMaxPacketSize));	max = 64;	ep->is_in = (USB_DIR_IN & desc->bEndpointAddress) != 0;	spin_lock_irqsave(&ep->dev->lock, flags);	ep->stopped = 0;	ep->desc = desc;	ep->ep.maxpacket = max;	spin_unlock_irqrestore(&ep->dev->lock, flags);	DEBUG("enable %s %s maxpacket %u\n", ep->ep.name,	      ep->is_in ? "IN" : "OUT", max);	return 0;}static int jz4730_ep_disable(struct usb_ep *_ep){	struct jz4730_ep *ep;	struct jz4730_udc *dev;	unsigned long flags;	ep = container_of(_ep, struct jz4730_ep, ep);	if (!_ep || !ep->desc)		return -ENODEV;	dev = ep->dev;	if (dev->ep0state == EP0_SUSPEND)		return -EBUSY;	DEBUG("disable %s\n", _ep->name);	spin_lock_irqsave(&dev->lock, flags);	/* Nuke all pending requests */	nuke(ep, -ESHUTDOWN);	/* Disable ep IRQ */	pio_irq_disable(ep);	ep->desc = 0;	ep->stopped = 1;	spin_unlock_irqrestore(&dev->lock, flags);	return 0;}static struct usb_request *jz4730_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags){	struct jz4730_request *req;	req = kzalloc(sizeof *req, gfp_flags);	if (!req)		return 0;	INIT_LIST_HEAD(&req->queue);	return &req->req;}static void jz4730_free_request(struct usb_ep *_ep, struct usb_request *_req){	struct jz4730_request *req;	req = container_of(_req, struct jz4730_request, req);	WARN_ON(!list_empty(&req->queue));	kfree(req);}static void *jz4730_alloc_buffer(struct usb_ep *_ep, unsigned bytes,				 dma_addr_t *dma, gfp_t gfp_flags){	void *retval;	retval = kmalloc(bytes, gfp_flags & ~(__GFP_DMA | __GFP_HIGHMEM));	if (retval)		*dma = virt_to_phys(retval);	return retval;}static void jz4730_free_buffer(struct usb_ep *_ep, void *buf,			       dma_addr_t dma, unsigned bytes){	kfree(buf);}/* *	done - retire a request; caller blocked irqs */static void done(struct jz4730_ep *ep, struct jz4730_request *req, int status){	struct jz4730_udc *dev;	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)		DEBUG("complete %s req %p stat %d len %u/%u\n",		      ep->ep.name, &req->req, status,		      req->req.actual, req->req.length);	dev = ep->dev;	/* don't modify queue heads during completion callback */	ep->stopped = 1;	spin_unlock(&dev->lock);	req->req.complete(&ep->ep, &req->req);	spin_lock(&dev->lock);	ep->stopped = stopped;}/*-------------------------------------------------------------------------*/static __inline__ int write_packet(struct jz4730_ep *ep,				   struct jz4730_request *req, int max){	u8 *buf;		int length, nlong, nbyte;	volatile u32 *fifo = (volatile u32 *)ep->fifo;	buf = req->req.buf + req->req.actual;	prefetch(buf);	length = req->req.length - req->req.actual;	length = min(length, max);	req->req.actual += length;	DEBUG("Write %d (max %d), fifo %p\n", length, max, fifo);	if (!length) {		/* Send ZLP */		writel(0, (unsigned int *)UDC_TXZLP);		writel(0x12345678, (unsigned int *)fifo);	}	else {		nlong = length >> 2;		nbyte = length & 0x3;		while (nlong--) {			*fifo = *((u32 *)buf);			buf += 4;		}		while (nbyte--) {			*((volatile u8 *)fifo) = *buf++;		}	}			writel(0, (unsigned int *)UDC_TXCONFIRM);	return length;}static __inline__ int read_packet(struct jz4730_ep *ep, 				  struct jz4730_request *req, int count){	u8 *buf;	int length, nlong, nbyte;	volatile u32 *fifo = (volatile u32 *)ep->fifo;	buf = req->req.buf + req->req.actual;	prefetchw(buf);	length = req->req.length - req->req.actual;	length = min(length, count);	req->req.actual += length;	DEBUG("Read %d, fifo %p\n", length, fifo);	nlong = length >> 2;	nbyte = length & 0x3;	while (nlong--) {		*((u32 *)buf) = *fifo;		buf += 4;	}	if (nbyte) {		u32 data = *fifo;		while (nbyte--) {			*buf++ = data & 0x0ff;			data >>= 8;		}	}	REG32(UDC_RXCONFIRM);	return length;}/** Write request to FIFO (max write == maxp size) *  Return:  0 = still running, 1 = completed, negative = errno */static int write_fifo(struct jz4730_ep *ep, struct jz4730_request *req){	u32 max, count;	int is_last;	max = ep->ep.maxpacket;	count = write_packet(ep, req, max);	/* last packet often short (sometimes a zlp, especially on ep0) */	if (unlikely(count != max)) {		is_last = 1;	} else {		if (likely(req->req.length != req->req.actual)		    || req->req.zero)			is_last = 0;		else			is_last = 1;	}	DEBUG("write %s (%d)(IN) %d bytes%s req %p %d/%d is_last %d\n",	      ep->ep.name, ep->index, count,	      (count != ep->ep.maxpacket) ? " (short)" : "",	      req, req->req.actual, req->req.length, is_last);	/* requests complete when all IN data is in the FIFO,	 * or sometimes later, if a zlp was needed.	 */	if (is_last) {		done(ep, req, 0);		return 1;	}	return 0;}/** Read to request from FIFO (max read == bytes in fifo) *  Return:  0 = still running, 1 = completed, negative = errno */static int read_fifo(struct jz4730_ep *ep, struct jz4730_request *req, u32 count){	int is_short;	is_short = (count < ep->ep.maxpacket);	count = read_packet(ep, req, count);	DEBUG("read %s %u bytes%s OUT req %p %u/%u is_short %d\n",	      ep->ep.name, count, (count < ep->ep.maxpacket) ? "(short)" : "",	      req, req->req.actual, req->req.length, is_short);	/* completion */	if (is_short || req->req.actual == req->req.length) {		done(ep, req, 0);		return 1;	}	/* finished that packet.  the next one may be waiting... */	return 0;}static inline void pio_irq_enable(struct jz4730_ep *ep){	switch (ep->index) {	case 0:		REG_UDC_EPIntMR &= ~0x1;		break;	case 1:	case 2:	case 3:	case 4:		REG_UDC_EPIntMR &= ~(1 << ep->index);		break;	case 5:	case 6:	case 7:		REG_UDC_EPIntMR &= ~(1 << (ep->index + 16));		break;	}}static inline void pio_irq_disable(struct jz4730_ep *ep){	switch (ep->index) {	case 0:		REG_UDC_EPIntMR |= 0x1;		break;	case 1:	case 2:	case 3:	case 4:		REG_UDC_EPIntMR |= (1 << ep->index);		break;	case 5:	case 6:	case 7:		REG_UDC_EPIntMR |= (1 << (ep->index + 16));		break;	}}/*-------------------------------------------------------------------------*/static intjz4730_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags){	struct jz4730_request *req;	struct jz4730_ep *ep;	struct jz4730_udc *dev;	unsigned long flags;	int status;	/* always require a cpu-view buffer so pio works */	req = container_of(_req, struct jz4730_request, req);	if (unlikely(!_req || !_req->complete 			|| !_req->buf || !list_empty(&req->queue)))		return -EINVAL;	ep = container_of(_ep, struct jz4730_ep, ep);	if (unlikely(!_ep || (!ep->desc && ep->index != 0)))		return -EINVAL;	dev = ep->dev;	if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN))		return -ESHUTDOWN;	DEBUG("%s queue req %p, len %u buf %p\n",	      _ep->name, _req, _req->length, _req->buf);	spin_lock_irqsave(&dev->lock, flags);	_req->status = -EINPROGRESS;	_req->actual = 0;	/* for ep0 IN without premature status, zlp is required and	 * writing EOP starts the status stage (OUT).	 */	if (unlikely(ep->index == 0 && ep->is_in))		_req->zero = 1;	/* kickstart this i/o queue? */	status = 0;	if (list_empty(&ep->queue) && likely(!ep->stopped)) {		if (unlikely(ep->index == 0)) {			pio_irq_enable(ep);  			if (ep->irq_pending ||   			    (REG_UDC_EPIntR & UDC_EPIntR_OUTEP0)) {  				u32 stats, count;    				stats = REG_UDC_EP0OutSR;  				if (stats & UDC_EPSR_OUT_RCVDATA) {  					ep->irq_pending = 0;  					REG_UDC_EP0OutSR &= ~UDC_EPSR_OUT_MASK;  					if (REG_UDC_EPIntR & UDC_EPIntR_OUTEP0)  						REG_UDC_EPIntR = UDC_EPIntR_OUTEP0;    					count = OUT_COUNT(stats);  					if (read_fifo(ep, req, count) == 1)  						req = 0;  				}  			}		} else if (ep->is_in) {			/* EP1 ~ EP4 */			if (ep->irq_pending || 			    (REG_UDC_EPIntR & UDC_EPIntR_INEP2)) {				if (REG_UDC_EP2InSR & UDC_EPSR_IN) {					ep->irq_pending = 0;					REG_UDC_EP2InSR &= ~UDC_EPSR_IN;					if (REG_UDC_EPIntR & UDC_EPIntR_INEP2)						REG_UDC_EPIntR = UDC_EPIntR_INEP2;

⌨️ 快捷键说明

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