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

📄 n9604.c

📁 LINUX2.4.18内核下的usb GADGET驱动程序
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * National 9603/4 USB Device Controller driver * Copyright (C) 2004 Technical Solutions Inc. (support@techsol.ca) * ported from : The Goku-S driver * Copyright (C) 2003 MontaVista Software (source@mvista.com) * * 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 semi-configurable bulk/interrupt endpoints. * *  - Endpoint numbering is fixed: *  Endpoint 0: ep0 *  Endpoint 1: ep1in  (tx) *  Endpoint 2: ep2out (rx) *  Endpoint 3: ep3in  (tx) *  Endpoint 4: ep4out (rx) *  Endpoint 5: ep5in  (tx) *  Endpoint 6: ep6out (rx) *//* * The ep->stage information refers to the state of a setup transaction * * state 0: no setup packet has been received * state 1: setup packet has been received * state 2: data has been sent/received * state 3: ZLP has been received/sent */#include <linux/config.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/delay.h>#include <linux/ioport.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/timer.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/usb_ch9.h>#include <linux/usb_gadget.h>#include <asm/byteorder.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/unaligned.h>#include "n9604.h"#include "n9604regs.h"inline void Flush_and_enable(u8 control_reg) {	write_9604(RXC_FLUSH, control_reg);	while (read_9604(control_reg) & RXC_FLUSH);	write_9604(RXC_RX_EN, control_reg);}inline void Flush(u8 control_reg) {	write_9604(RXC_FLUSH, control_reg);	while (read_9604(control_reg) & RXC_FLUSH);}#define	DRIVER_DESC		"N9604 USB Device Controller"#define	DRIVER_VERSION		"29-Oct 2004"static const char driver_name [] = "n9604_udc";static const char driver_desc [] = DRIVER_DESC;MODULE_AUTHOR("support@techsol.ca");MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE("GPL");static void nuke(struct n9604_ep *ep, int status);inline void send_zero_length(int endpoint, struct n9604_udc *dev);u8 * USBN9604_Offset; //device virtual address/* FIXME all the IRQ stuff is board-specific */#define h7201_readl	readl#define h7201_writel	writel#define ETHER_IRQ_IP_OFFSET	0#define ETHER_IRQ_BIT_POS	0#define ETHER_IRQ_IM_OFFSET	0#define IRQ_GPIOC		-1#define USBD_ENABLE_IRQ {h7201_writel( h7201_readl(ETHER_IRQ_IP_OFFSET) | (1 << ETHER_IRQ_BIT_POS), ETHER_IRQ_IP_OFFSET);  h7201_writel( h7201_readl(ETHER_IRQ_IM_OFFSET) |  (1 << ETHER_IRQ_BIT_POS), ETHER_IRQ_IM_OFFSET);}#define USBD_DISABLE_IRQ h7201_writel( h7201_readl(ETHER_IRQ_IM_OFFSET) & ~(1 << ETHER_IRQ_BIT_POS), ETHER_IRQ_IM_OFFSET);/*-------------------------------------------------------------------------*///enable an end point, of description descstatic int n9604_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) {	struct n9604_udc *dev;	struct n9604_ep  *ep;	u16              max;	ep = container_of(_ep, struct n9604_ep, ep);	if (!_ep || !desc || ep->desc || desc->bDescriptorType != USB_DT_ENDPOINT)		return -EINVAL;	dev = ep->dev;	if (!ep->num) 		return -EINVAL;	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)		return -ESHUTDOWN;	if (ep->num && !(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;	}	write_9604((ep->numActual & EPC_EP_MASK) | EPC_EP_EN | (EPC_ISO * ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC)), ep->control);	if (ep->is_in)		Flush(ep->command);	else		Flush_and_enable(ep->command);		max = le16_to_cpu(get_unaligned(&desc->wMaxPacketSize));	ep->ep.maxpacket = min_t(u16, max, MAX_FIFO_SIZE);	ep->desc = desc;		return 0;}static int n9604_ep_disable(struct usb_ep *_ep)//ep > 0{	struct n9604_ep		*ep;	struct n9604_udc	*dev;	unsigned long		flags;	ep = container_of(_ep, struct n9604_ep, ep);	if (!_ep || !ep->desc)		return -ENODEV;	dev = ep->dev;	spin_lock_irqsave(&dev->lock, flags);	nuke(ep, -ESHUTDOWN);	write_9604(0, ep->command);	ep->desc = NULL;	spin_unlock_irqrestore(&dev->lock, flags);	return 0;}/*-------------------------------------------------------------------------*/static struct usb_request *n9604_alloc_request(struct usb_ep *_ep, int gfp_flags){	struct n9604_request	*req;	if (!_ep)		return 0;	req = kmalloc(sizeof *req, gfp_flags);	if (!req)		return 0;	memset(req, 0, sizeof *req);	INIT_LIST_HEAD(&req->queue);	return &req->req;}static voidn9604_free_request(struct usb_ep *_ep, struct usb_request *_req){	struct n9604_request	*req;	if (!_ep || !_req)		return;	req = container_of(_req, struct n9604_request, req);	WARN_ON(!list_empty(&req->queue));	kfree(req);}/*-------------------------------------------------------------------------*/static void done(struct n9604_ep *ep, struct n9604_request *req, int status);static inline intwrite_packet(struct n9604_ep *ep, u8 *buf, struct n9604_request *req){	unsigned        written_length, desired_length, available_length, maximum_length, flags, loop_length;	u8 fifo = ep->fifo;	u8 command = ep->command;	u8 status = ep->status;	if (!ep->num) {		fifo = TXD0;		command = TXC0;		status = TXS0;	}		if (read_9604(command) & TXC_TX_EN)		return -EBUSY;	ep->packets++;		desired_length = req->req.length - req->req.actual;	available_length = read_9604(status) & TXS_TCOUNT_MASK;//might be greater	written_length = 0;	if (ep->num)		maximum_length = MAX_FIFO_SIZE;	else		maximum_length = MAX_EP0_SIZE;	while ((loop_length = min(min(available_length, desired_length), maximum_length))) {		int i = loop_length;		while (i) { write_9604(*buf++,fifo); i--; }		written_length += loop_length;		desired_length -= loop_length;		maximum_length -= loop_length;		if (desired_length && maximum_length)//check if really need to read the chip again			available_length = (read_9604(status) & TXS_TCOUNT_MASK);	}		req->req.actual += written_length;	flags = TXC_TX_EN;	if (ep->num)		flags |= TXC_LAST;	if (ep->toggle)		flags |= TXC_TOGGLE;	write_9604(flags, command);	ep->toggle = !(ep->toggle);	if (!written_length) req->req.zero = 0;//just wrote zero bytes, there is no more need for req.zero 	return written_length;}       // return:  0 = still running, 1 = completed, negative = errnostatic int write_fifo(struct n9604_ep *ep, struct n9604_request *req){	struct n9604_udc *dev = ep->dev;	u8              *buf;	unsigned        count;	int             is_last;	buf = req->req.buf + req->req.actual;	prefetch(buf);		dev = ep->dev;	count = write_packet(ep, buf, req);	if (count < 0)		return count;		/* last packet often short (sometimes a zlp, especially on ep0) */	if ((req->req.length != req->req.actual) || req->req.zero)		is_last = 0;	else		is_last = 1;		/* 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;}static inline void pio_irq_enable(struct n9604_ep *ep);static int read_fifo(struct n9604_ep *ep, struct n9604_request *req){	u32                     size;	u8                      *buf;	int			bufferspace_available, fifospace_left, num_bytes_read;	int 			fifo, status;	ep->packets++;	if (!ep->num) { 		fifo = RXD0;		status = RXS0;	} else {		fifo = ep->fifo;		status = ep->status;	}	num_bytes_read = 0;	buf = req->req.buf + req->req.actual;	bufferspace_available = req->req.length - req->req.actual;	size = read_9604(status) & (RXS_RCOUNTMASK | RXS_RX_ERR);//number of bytes ready to be read (15 if greater than 15)	if (ep->num && (size & RXS_RX_ERR)) {		ERROR(ep->dev, "DATA ERROR!!!! on ep%d\nFlushing Fifo", ep->num);		Flush_and_enable(ep->command);		goto leave;	}	size = size & ~RXS_RX_ERR;//clear the bit	if (ep->num)	fifospace_left = MAX_FIFO_SIZE;	else		fifospace_left = MAX_EP0_SIZE;loop:	/* read all bytes from this packet */	while (size-- != 0) {		u8 byte = read_9604(fifo);		if (unlikely(bufferspace_available == 0)) {			/* this happens when the driver's buffer			 * is smaller than what the host sent.			 * discard the extra data in this packet.			 */			done(ep, req, -EOVERFLOW);			return 1;		} else {			*buf++ = byte;			bufferspace_available--;			fifospace_left--;			num_bytes_read++;		}	}	if ((size = (read_9604(status) & RXS_RCOUNTMASK))) {		goto loop;//since there is more data	}	/* completion */	req->req.actual = req->req.actual + num_bytes_read;	if (fifospace_left || req->req.actual == req->req.length) {		done(ep, req, 0);		return 1;	}leave:	pio_irq_enable(ep);//turn the interrupt back on	return 0;}/*-------------------------------------------------------------------------*/static inline voidpio_irq_enable(struct n9604_ep *ep){	if (ep->is_in) 		write_9604(read_9604(TXMSK) | 1 << ep->fifoNum | 0x10 << ep->fifoNum, TXMSK);	else {		u8 command = ep->command;		if (!ep->num) command = RXC0;		write_9604(read_9604(RXMSK) | 1 << ep->fifoNum | 0x10 << ep->fifoNum, RXMSK);		write_9604(RXC_RX_EN | RXC_RFWL0 | RXC_RFWL1, command);	}}static inline voidpio_irq_disable(struct n9604_ep *ep)//epnum != 0{	if (ep->is_in)		write_9604(read_9604(TXMSK) & ~(1 << ep->fifoNum) & ~(0x10 << ep->fifoNum), TXMSK);	else		write_9604(read_9604(RXMSK) & ~(1 << ep->fifoNum) & ~(0x10 << ep->fifoNum), RXMSK);}static int request_voodoo = 0;//number of bytes the host requestedstatic inline voidpio_advance(struct n9604_ep *ep){	struct n9604_request     *req;	if (list_empty (&ep->queue)) {		if (!ep->num) {			if (ep->is_in && (ep->stage == 2)) {				ep->is_in = 0;//switch modes				Flush_and_enable(RXC0);//needed to receive a ZLP after tx				ep->stage++;//and bump the stage number 			} else if (ep->stage == 3) {				ep->stage = 0;			}		}		return;	}	req = list_entry(ep->queue.next, struct n9604_request, queue);	(ep->is_in ? write_fifo : read_fifo)(ep, req);}/*-------------------------------------------------------------------------*/static void * n9604_alloc_buffer(struct usb_ep *_ep, unsigned bytes, dma_addr_t *dma, int  gfp_flags){	return kmalloc(bytes, gfp_flags);}static void n9604_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes){	kfree (buf);}/*-------------------------------------------------------------------------*/static voiddone(struct n9604_ep *ep, struct n9604_request *req, int status){	struct n9604_udc         *dev;	list_del_init(&req->queue);	ep->queue_active--;		if (req->req.status == -EINPROGRESS)		req->req.status = status;	else		status = req->req.status;		dev = ep->dev;		/* don't modify queue heads during completion callback */	if (ep->num)		pio_irq_disable(ep);	else if (!ep->nuking) {		ep->stage++;		ep->toggle = 1;//other endpoints stay in their flipping mode between transactions		if (ep->stage == 2) {//we are in stage 2 now			if (!ep->is_in) {				ep->is_in = 1;//switch modes				request_voodoo = 1;//prevents n9604_queue from calling us again before doing anything				send_zero_length(0, dev);			} else {//we have to receive a ZLP				//this will happen when the tx is complete, the pio_advance fcn will activate it for us			}		}	}			req->req.complete(&ep->ep, &req->req);}/*-------------------------------------------------------------------------*/static intn9604_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags){	struct n9604_request	*req;	struct n9604_ep		*ep;	struct n9604_udc	*dev;	unsigned long		flags;	int			status;	req = container_of(_req, struct n9604_request, req);	if (unlikely(!_req || !_req->complete			|| !_req->buf || !list_empty(&req->queue)))		return -EINVAL;	ep = container_of(_ep, struct n9604_ep, ep);	if (unlikely(!_ep || (!ep->desc && ep->num != 0)))		return -EINVAL;	dev = ep->dev;	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {		return -ESHUTDOWN;	}	if (ep->nuking)		return -ESHUTDOWN;	spin_lock_irqsave(&dev->lock, flags);	ep->queue_reqs++;	ep->queue_active++;	_req->status = -EINPROGRESS;	_req->actual = 0;	/* for ep0 IN without premature status, zlp is required and	 * writing EOP starts the status stage (OUT).	 */	if (ep->num == 0) {		if ((request_voodoo > _req->length) && !(_req->length % MAX_EP0_SIZE) && (_req->length != 0)) {			_req->zero = 1;		}		if (!request_voodoo && !ep->is_in) {//this is a zero length request			spin_unlock_irqrestore(&dev->lock, flags);//David			done(ep, req, 0);//this doesn't check if the list is empty (probably not an issue)			return 0;	//shouldn't this be handled by the rx irq fcn, and passed to pio_advance		}//that may conflict with the voodoo stuff, maybe best to leave it	}		/* kickstart this i/o queue? */	status = 0;	if (list_empty(&ep->queue) && ep->is_in) {		status = write_fifo(ep, req);		if (status == -EBUSY)			;//we should queue up the request then		else {			if (status != 0) {				if (status > 0)					status = 0;				req = 0;			}		}	} /* else pio or dma irq handler advances the queue. */	if (req != 0) {		list_add_tail(&req->queue, &ep->queue);		pio_irq_enable(ep);	}	spin_unlock_irqrestore(&dev->lock, flags);	return status;}/* dequeue ALL requests */static void nuke(struct n9604_ep *ep, int status){	struct n9604_request	*req;		if (list_empty(&ep->queue))		return;	ep->nuking = 1;	while (!list_empty(&ep->queue)) {		req = list_entry(ep->queue.next, struct n9604_request, queue);		done(ep, req, status);	}	ep->nuking = 0;}/* dequeue JUST ONE request */static int n9604_dequeue(struct usb_ep *_ep, struct usb_request *_req){	struct n9604_request	*req;	struct n9604_ep		*ep;	struct n9604_udc	*dev;	unsigned long		flags;	ep = container_of(_ep, struct n9604_ep, ep);	if (!_ep || !_req || (!ep->desc && ep->num != 0))		return -EINVAL;	dev = ep->dev;	if (!dev->driver)		return -ESHUTDOWN;	spin_lock_irqsave(&dev->lock, flags);	/* make sure it's actually queued on this endpoint */	list_for_each_entry (req, &ep->queue, queue) {

⌨️ 快捷键说明

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