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

📄 s3c2410_udc.c

📁 ARM S3C2410 USB SLAVE LINUX驱动
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * linux/drivers/usb/gadget/s3c2410_udc.c * Samsung on-chip full speed USB device controllers * * Copyright (C) 2004 Herbert Pötzl - Arnaud Patard * * 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 * */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.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/version.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/unaligned.h>#include <asm/arch/irqs.h>#include <asm/arch/hardware.h>#include <asm/arch/regs-clock.h>#include <asm/arch/regs-gpio.h>#include <asm/arch/regs-udc.h>#include <asm/hardware/clock.h>#include "s3c2410_udc.h"#define DRIVER_DESC     "S3C2410 USB Device Controller Gadget"#define DRIVER_VERSION  "29 Oct 2004"#define DRIVER_AUTHOR	"Herbert Pötzl <herbert@13thfloor.at>, Arnaud Patard <arnaud.patard@rtp-net.org>"static const char       gadget_name [] = "s3c2410_udc";static const char	driver_desc [] = DRIVER_DESC;static struct s3c2410_udc	*the_controller;static struct s3c2410_udc 	memory;static struct clk      		*udc_clock;/*------------------------- I/O ----------------------------------*/static void nuke (struct s3c2410_udc *udc, struct s3c2410_ep *ep){	while (!list_empty (&ep->queue)) {		struct s3c2410_request  *req;		req = list_entry (ep->queue.next, struct s3c2410_request, queue);		list_del_init (&req->queue);		req->req.status = -ESHUTDOWN;		spin_unlock (&udc->lock);		req->req.complete (&ep->ep, &req->req);		spin_lock (&udc->lock);	}}/* * 	done */static void done(struct s3c2410_ep *ep, struct s3c2410_request *req, int status){	list_del_init(&req->queue);	if (likely (req->req.status == -EINPROGRESS))		req->req.status = status;	else		status = req->req.status;	req->req.complete(&ep->ep, &req->req);}static inline void clear_ep_state (struct s3c2410_udc *dev){	        unsigned i;				/* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint		 * fifos, and pending transactions mustn't be continued in any case.		 */		 for (i = 1; i < S3C2410_ENDPOINTS; i++)			 nuke(dev, &dev->ep[i]);}static inline int fifo_count_out(void){	int tmp;		tmp = __raw_readl(S3C2410_UDC_OUT_FIFO_CNT2_REG) << 8;	tmp |= __raw_readl(S3C2410_UDC_OUT_FIFO_CNT1_REG);		return tmp & 0xffff;}/* * 	write_packet */static inline int write_packet(u32 fifo, struct s3c2410_request *req, unsigned max){	unsigned	len;	u8		*buf;	buf = req->req.buf + req->req.actual;	len = min(req->req.length - req->req.actual, max);	dprintk("write_packet %d %d %d ",req->req.actual,req->req.length,len);	req->req.actual += len;	dprintk("%d\n",req->req.actual);	max = len;	while (max--)		 __raw_writel(*buf++,fifo);	return len;}/* * 	write_fifo */// return:  0 = still running, 1 = completed, negative = errnostatic int write_fifo(struct s3c2410_ep *ep, struct s3c2410_request *req){	u8		*buf;	unsigned	count;	int		is_last;	u32		idx,fifo_reg;	u32		ep_csr;	switch(ep->bEndpointAddress&0x7F)	{		default:		case 0: idx = 0;			fifo_reg = S3C2410_UDC_EP0_FIFO_REG;			break;		case 1:			idx = 1;			fifo_reg = S3C2410_UDC_EP1_FIFO_REG;			break;		case 2:			idx = 2;			fifo_reg = S3C2410_UDC_EP2_FIFO_REG;			break;		case 3:			idx = 3;			fifo_reg = S3C2410_UDC_EP3_FIFO_REG;			break;		case 4:			idx = 4;			fifo_reg = S3C2410_UDC_EP4_FIFO_REG;			break;	}		buf = req->req.buf + req->req.actual;	prefetch(buf);	count = ep->ep.maxpacket;	count = write_packet(fifo_reg, req, count);	/* last packet is often short (sometimes a zlp) */	if (count < ep->ep.maxpacket)		is_last = 1;	else if (req->req.length == req->req.actual			&& !req->req.zero)		is_last = 2;	else		is_last = 0;	dprintk("Written ep%d %d. %d of %d b [last %d]\n",idx,count,req->req.actual,req->req.length,is_last);	if (is_last)	{		/* The order is important. It prevents to send 2 packet at the same time		 **/		if (!idx)		{			set_ep0_de_in();			ep->dev->ep0state=EP0_IDLE;		}		else		{			 __raw_writel(idx, S3C2410_UDC_INDEX_REG);			 ep_csr=__raw_readl(S3C2410_UDC_IN_CSR1_REG);			 __raw_writel(idx, S3C2410_UDC_INDEX_REG);			 __raw_writel(ep_csr|S3C2410_UDC_ICSR1_PKTRDY,S3C2410_UDC_IN_CSR1_REG);		}		done(ep, req, 0);	}	else	{		if (!idx)		{			set_ep0_ipr();		}		else		{			__raw_writel(idx, S3C2410_UDC_INDEX_REG);			ep_csr=__raw_readl(S3C2410_UDC_IN_CSR1_REG);			__raw_writel(idx, S3C2410_UDC_INDEX_REG);			__raw_writel(ep_csr|S3C2410_UDC_ICSR1_PKTRDY,S3C2410_UDC_IN_CSR1_REG);		}	}	return is_last;}static inline int read_packet(u32 fifo, u8 *buf, struct s3c2410_request *req, unsigned avail){	unsigned	len;	len = min(req->req.length - req->req.actual, avail);	req->req.actual += len;	avail = len;	while (avail--)		*buf++ = (unsigned char)__raw_readl(fifo);	return len;}// return:  0 = still running, 1 = queue empty, negative = errnostatic int read_fifo(struct s3c2410_ep *ep, struct s3c2410_request *req){	u8		*buf;	u32		ep_csr;	unsigned	bufferspace;	int is_last=1;	unsigned avail;	int fifo_count = 0;	u32		idx,fifo_reg;	switch(ep->bEndpointAddress&0x7F)	{		default:		case 0: idx = 0;			fifo_reg = S3C2410_UDC_EP0_FIFO_REG;			break;		case 1:			idx = 1;			fifo_reg = S3C2410_UDC_EP1_FIFO_REG;			break;		case 2:			idx = 2;			fifo_reg = S3C2410_UDC_EP2_FIFO_REG;			break;		case 3:			idx = 3;			fifo_reg = S3C2410_UDC_EP3_FIFO_REG;			break;		case 4:			idx = 4;			fifo_reg = S3C2410_UDC_EP4_FIFO_REG;			break;	}	buf = req->req.buf + req->req.actual;	bufferspace = req->req.length - req->req.actual;	if (!bufferspace)	{		dprintk("read_fifo: Buffer full !!\n");		return -1;	}	__raw_writel(idx, S3C2410_UDC_INDEX_REG);		        fifo_count = fifo_count_out();	dprintk("fifo_read fifo count : %d\n",fifo_count);	if (fifo_count > ep->ep.maxpacket)		avail = ep->ep.maxpacket;	else		avail = fifo_count;	fifo_count=read_packet(fifo_reg,buf,req,avail);	if (fifo_count < ep->ep.maxpacket) {		is_last = 1;		/* overflowed this request?  flush extra data */		if (fifo_count != avail) {			req->req.status = -EOVERFLOW;		}	} else {		if (req->req.length == req->req.actual) 			is_last = 1;		else			is_last = 0;	}	__raw_writel(idx, S3C2410_UDC_INDEX_REG);	fifo_count = fifo_count_out();			dprintk("fifo_read fifo count : %d [last %d]\n",fifo_count,is_last);/*	if (idx)		is_last=0;*/		if (is_last) {		if (!idx)		{			set_ep0_de_out();			ep->dev->ep0state=EP0_IDLE;		}		else		{			__raw_writel(idx, S3C2410_UDC_INDEX_REG);			ep_csr=__raw_readl(S3C2410_UDC_OUT_CSR1_REG);			__raw_writel(idx, S3C2410_UDC_INDEX_REG);			__raw_writel(ep_csr&~S3C2410_UDC_OCSR1_PKTRDY,S3C2410_UDC_OUT_CSR1_REG);		}		done(ep, req, 0);		if (!list_empty(&ep->queue))		{			is_last=0;			req = container_of(ep->queue.next,				struct s3c2410_request, queue);		}		else			is_last=1;//		is_last = 0;	}	else	{		if (!idx)		{			clear_ep0_opr();		}		else		{				__raw_writel(idx, S3C2410_UDC_INDEX_REG);			ep_csr=__raw_readl(S3C2410_UDC_OUT_CSR1_REG);			__raw_writel(idx, S3C2410_UDC_INDEX_REG);			__raw_writel(ep_csr&~S3C2410_UDC_OCSR1_PKTRDY,S3C2410_UDC_OUT_CSR1_REG);		}	}		return is_last;}static intread_fifo_crq(struct usb_ctrlrequest *crq){	int bytes_read = 0;	int fifo_count = 0;	int i;			unsigned char *pOut = (unsigned char*)crq;		__raw_writel(0, S3C2410_UDC_INDEX_REG);		fifo_count = fifo_count_out();		BUG_ON( fifo_count > 8 );		dprintk("read_fifo_crq(): fifo_count=%d\n", fifo_count );	while( fifo_count-- ) {		i = 0;				do {			*pOut = (unsigned char)__raw_readl(S3C2410_UDC_EP0_FIFO_REG);			udelay( 10 );			i++;		} while((fifo_count_out() != fifo_count) && (i < 10));				if ( i == 10 ) {			printk("read_fifo(): read failure\n");		}				pOut++;		bytes_read++;	}		dprintk("read_fifo_crq: len=%d %02x:%02x {%x,%x,%x}\n",			bytes_read, crq->bRequest, crq->bRequestType,			crq->wValue, crq->wIndex, crq->wLength);		return bytes_read;}/*------------------------- usb state machine -------------------------------*/static void handle_ep0(struct s3c2410_udc *dev){	u32			ep0csr;	struct s3c2410_ep	*ep = &dev->ep [0];	struct s3c2410_request	*req;	struct usb_ctrlrequest	crq;	if (list_empty(&ep->queue))    		req = 0;	else		req = list_entry(ep->queue.next, struct s3c2410_request, queue);  		__raw_writel(0, S3C2410_UDC_INDEX_REG);	ep0csr = __raw_readl(S3C2410_UDC_IN_CSR1_REG);	/* clear stall status */	if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) {		/* FIXME */		nuke(dev, ep);	    	dprintk("... clear SENT_STALL ...\n");	    	clear_ep0_sst();	    	ep0csr &= ~(S3C2410_UDC_EP0_CSR_SENTSTL|S3C2410_UDC_EP0_CSR_SENDSTL);		__raw_writel(0, S3C2410_UDC_INDEX_REG);		__raw_writel(ep0csr, S3C2410_UDC_IN_CSR1_REG);		dev->ep0state = EP0_IDLE;		return;	}	/* clear setup end */	if (ep0csr & S3C2410_UDC_EP0_CSR_SE	    	/* && dev->ep0state != EP0_IDLE */) {	    	dprintk("... serviced SETUP_END ...\n");		nuke(dev, ep);	    	clear_ep0_se();		dev->ep0state = EP0_IDLE;		return;	}	switch (dev->ep0state) {	case EP0_IDLE:		/* start control request? */		if (ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) {			int len, ret, tmp;

⌨️ 快捷键说明

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