diff -Nur linux-2.6.10-rc1-bk10.orig/arch/arm/mach-s3c2410/mach-h1940.c linux-2.6.10-rc1-bk10/arch/arm/mach-s3c2410/mach-h1940.c
--- linux-2.6.10-rc1-bk10.orig/arch/arm/mach-s3c2410/mach-h1940.c	2004-10-31 20:07:42.000000000 +0100
+++ linux-2.6.10-rc1-bk10/arch/arm/mach-s3c2410/mach-h1940.c	2004-10-31 18:11:45.000000000 +0100
@@ -216,6 +216,7 @@
 	&s3c_device_wdt,
 	&s3c_device_i2c,
 	&s3c_device_iis,
+	&s3c_device_usbgadget,
 };
 
 static struct s3c24xx_board h1940_board __initdata = {
diff -Nur linux-2.6.10-rc1-bk10.orig/arch/arm/mach-s3c2410/s3c2410.c linux-2.6.10-rc1-bk10/arch/arm/mach-s3c2410/s3c2410.c
--- linux-2.6.10-rc1-bk10.orig/arch/arm/mach-s3c2410/s3c2410.c	2004-10-31 17:28:09.000000000 +0100
+++ linux-2.6.10-rc1-bk10/arch/arm/mach-s3c2410/s3c2410.c	2004-10-31 20:01:07.000000000 +0100
@@ -51,6 +51,7 @@
 
 static struct map_desc s3c2410_iodesc[] __initdata = {
 	IODESC_ENT(USBHOST),
+	IODESC_ENT(USBDEV),
 	IODESC_ENT(CLKPWR),
 	IODESC_ENT(LCD),
 	IODESC_ENT(UART),
diff -Nur linux-2.6.10-rc1-bk10.orig/drivers/usb/gadget/ether.c linux-2.6.10-rc1-bk10/drivers/usb/gadget/ether.c
--- linux-2.6.10-rc1-bk10.orig/drivers/usb/gadget/ether.c	2004-10-31 17:28:08.000000000 +0100
+++ linux-2.6.10-rc1-bk10/drivers/usb/gadget/ether.c	2004-10-31 20:04:09.000000000 +0100
@@ -256,6 +256,9 @@
 #define	DEV_CONFIG_SUBSET
 #endif
 
+#ifdef CONFIG_USB_GADGET_S3C2410
+#define DEV_CONFIG_CDC
+#endif
 
 /*-------------------------------------------------------------------------*/
 
@@ -360,7 +363,7 @@
 	.bLength =		sizeof device_desc,
 	.bDescriptorType =	USB_DT_DEVICE,
 
-	.bcdUSB =		__constant_cpu_to_le16 (0x0200),
+	.bcdUSB =		__constant_cpu_to_le16 (0x0110),
 
 	.bDeviceClass =		USB_CLASS_COMM,
 	.bDeviceSubClass =	0,
@@ -2321,6 +2324,8 @@
 		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209);
 	} else if (gadget_is_n9604(gadget)) {
 		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0210);
+	} else if (gadget_is_s3c2410(gadget)) {
+		device_desc.bcdDevice = __constant_cpu_to_le16 (0x020A);
 	} else {
 		/* can't assume CDC works.  don't want to default to
 		 * anything less functional on CDC-capable hardware,
diff -Nur linux-2.6.10-rc1-bk10.orig/drivers/usb/gadget/gadget_chips.h linux-2.6.10-rc1-bk10/drivers/usb/gadget/gadget_chips.h
--- linux-2.6.10-rc1-bk10.orig/drivers/usb/gadget/gadget_chips.h	2004-10-31 17:28:08.000000000 +0100
+++ linux-2.6.10-rc1-bk10/drivers/usb/gadget/gadget_chips.h	2004-10-31 19:35:06.000000000 +0100
@@ -68,6 +68,12 @@
 #define	gadget_is_n9604(g)	0
 #endif
 
+#ifdef CONFIG_USB_GADGET_S3C2410
+#define gadget_is_s3c2410(g)    !strcmp("s3c2410_udc", (g)->name)
+#else
+#define gadget_is_s3c2410(g)    0
+#endif
+
 // CONFIG_USB_GADGET_AT91RM9200
 // CONFIG_USB_GADGET_SX2
 // CONFIG_USB_GADGET_AU1X00
diff -Nur linux-2.6.10-rc1-bk10.orig/drivers/usb/gadget/Kconfig linux-2.6.10-rc1-bk10/drivers/usb/gadget/Kconfig
--- linux-2.6.10-rc1-bk10.orig/drivers/usb/gadget/Kconfig	2004-10-31 17:28:08.000000000 +0100
+++ linux-2.6.10-rc1-bk10/drivers/usb/gadget/Kconfig	2004-10-31 19:35:06.000000000 +0100
@@ -157,7 +157,19 @@
 	depends on USB_GADGET_LH7A40X
 	default USB_GADGET
 
-
+config USB_GADGET_S3C2410
+	boolean "S3C2410"
+	depends on ARCH_S3C2410
+	help
+	  Samsung's S3C2410 is an ARM-4 processor with an integrated
+	  full speed USB 1.1 device controller.
+	  It has 4 configurable endpoints, as well as endpoint
+	  zero (for control transfers).
+config USB_S3C2410
+	tristate
+	depends on USB_GADGET_S3C2410
+	default USB_GADGET
+											    
 config USB_GADGET_DUMMY_HCD
 	boolean "Dummy HCD (DEVELOPMENT)"
 	depends on USB && EXPERIMENTAL
diff -Nur linux-2.6.10-rc1-bk10.orig/drivers/usb/gadget/Makefile linux-2.6.10-rc1-bk10/drivers/usb/gadget/Makefile
--- linux-2.6.10-rc1-bk10.orig/drivers/usb/gadget/Makefile	2004-10-31 17:28:08.000000000 +0100
+++ linux-2.6.10-rc1-bk10/drivers/usb/gadget/Makefile	2004-10-31 19:35:06.000000000 +0100
@@ -7,7 +7,7 @@
 obj-$(CONFIG_USB_GOKU)		+= goku_udc.o
 obj-$(CONFIG_USB_OMAP)		+= omap_udc.o
 obj-$(CONFIG_USB_LH7A40X)	+= lh7a40x_udc.o
-
+obj-$(CONFIG_USB_S3C2410)	+= s3c2410_udc.o
 #
 # USB gadget drivers
 #
diff -Nur linux-2.6.10-rc1-bk10.orig/drivers/usb/gadget/s3c2410_udc.c linux-2.6.10-rc1-bk10/drivers/usb/gadget/s3c2410_udc.c
--- linux-2.6.10-rc1-bk10.orig/drivers/usb/gadget/s3c2410_udc.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.10-rc1-bk10/drivers/usb/gadget/s3c2410_udc.c	2004-10-31 19:29:49.000000000 +0100
@@ -0,0 +1,1365 @@
+/*
+ * 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 = errno
+static 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 = errno
+static 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 int
+read_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;
+			
+			nuke (dev, ep);
+
+			len = read_fifo_crq(&crq);
+			if (len != sizeof(crq)) {
+			  	dprintk("setup begin: fifo READ ERROR" 
+				    	" wanted %d bytes got %d. Stalling out...\n", 
+					sizeof(crq), len);
+ 				set_ep0_ss();
+				return;
+			}
+
+
+			/* cope with automagic for some standard requests. */
+			dev->req_std = (crq.bRequestType & USB_TYPE_MASK)
+						== USB_TYPE_STANDARD;
+			dev->req_config = 0;
+			dev->req_pending = 1;
+			switch (crq.bRequest) {
+				/* hardware restricts gadget drivers here! */
+				case USB_REQ_SET_CONFIGURATION:
+				    	dprintk("USB_REQ_SET_CONFIGURATION ... \n");
+					if (crq.bRequestType == USB_RECIP_DEVICE) {
+config_change:
+						dev->req_config = 1;
+						clear_ep_state(dev);
+						set_ep0_de_out();
+					}
+					break;
+				/* ... and here, even more ... */
+				case USB_REQ_SET_INTERFACE:
+				    	dprintk("USB_REQ_SET_INTERFACE ... \n");
+					if (crq.bRequestType == USB_RECIP_INTERFACE) {
+						goto config_change;
+					}
+					break;
+
+				/* hardware was supposed to hide this */
+				case USB_REQ_SET_ADDRESS:
+				    	dprintk("USB_REQ_SET_ADDRESS ... \n");
+					if (crq.bRequestType == USB_RECIP_DEVICE) {
+						tmp = crq.wValue & 0x7F;
+						dev->address = tmp;
+ 						__raw_writel((tmp | 0x80), S3C2410_UDC_FUNC_ADDR_REG);
+						set_ep0_de_out();
+						return;
+					}
+					break;
+				default:
+					clear_ep0_opr();
+					break;
+			}
+
+			if (crq.bRequestType & USB_DIR_IN)
+				dev->ep0state = EP0_IN_DATA_PHASE;
+			else
+				dev->ep0state = EP0_OUT_DATA_PHASE;
+			ret = dev->driver->setup(&dev->gadget, &crq);
+			if (ret < 0) {
+				if (dev->req_config) {
+					dprintk("config change %02x fail %d?\n",
+						crq.bRequest, ret);
+					return;
+				}
+				if (ret == -EOPNOTSUPP)
+					dprintk("Operation not supported\n");
+				else
+					dprintk("dev->driver->setup failed. (%d)\n",ret);
+				dev->ep0state = EP0_STALL;
+			/* deferred i/o == no response yet */
+			} else if (dev->req_pending) {
+			    	dprintk("dev->req_pending... what now?\n");
+				dev->req_pending=0;
+			}
+			dprintk("ep0state %s\n",ep0states[dev->ep0state]);
+		}
+		break;
+	case EP0_IN_DATA_PHASE:			/* GET_DESCRIPTOR etc */
+	    	dprintk("EP0_IN_DATA_PHASE ... what now?\n");
+		if (!(ep0csr & 2) && req)
+		{
+			write_fifo(ep, req);
+		}
+		break;
+	case EP0_OUT_DATA_PHASE:		/* SET_DESCRIPTOR etc */
+	    	dprintk("EP0_OUT_DATA_PHASE ... what now?\n");
+		if ((ep0csr & 1) && req ) {
+			read_fifo(ep,req);
+		}
+		break;
+	case EP0_END_XFER:
+	    	dprintk("EP0_END_XFER ... what now?\n");
+		dev->ep0state=EP0_IDLE;
+		break;
+	case EP0_STALL:
+	    	set_ep0_ss();
+		break;
+	}
+}
+/*
+ * 	handle_ep - Manage I/O endpoints
+ */
+static void handle_ep(struct s3c2410_ep *ep)
+{
+	struct s3c2410_request	*req;
+	int			is_in = ep->bEndpointAddress & USB_DIR_IN;
+	u32			ep_csr1;
+	u32			idx;
+
+	if (likely (!list_empty(&ep->queue)))
+		req = list_entry(ep->queue.next,
+				struct s3c2410_request, queue);
+	else
+		req = 0;
+		
+	idx = (u32)(ep->bEndpointAddress&0x7F);
+
+	if (is_in) {
+		__raw_writel(idx, S3C2410_UDC_INDEX_REG);
+		ep_csr1 = __raw_readl(S3C2410_UDC_IN_CSR1_REG);
+		dprintk("ep%01d write csr:%02x ",idx,ep_csr1);
+		
+		if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL)
+		{
+			dprintk("st\n");
+			__raw_writel(idx, S3C2410_UDC_INDEX_REG);
+			__raw_writel(0x00,S3C2410_UDC_IN_CSR1_REG);
+			return;
+		}
+
+		if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req)
+		{
+			write_fifo(ep,req);
+		}
+	} 
+	else {
+		__raw_writel(idx, S3C2410_UDC_INDEX_REG);
+		ep_csr1 = __raw_readl(S3C2410_UDC_OUT_CSR1_REG);
+		dprintk("ep%01d read csr:%02x\n",idx,ep_csr1);
+		
+		if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL)
+		{
+			__raw_writel(idx, S3C2410_UDC_INDEX_REG);
+			__raw_writel(0x00,S3C2410_UDC_OUT_CSR1_REG);
+			return;
+		}
+		if( (ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req)
+		{
+			read_fifo(ep,req);
+		}
+	}
+}
+
+/*
+ *      s3c2410_udc_irq - interrupt handler
+ */
+static irqreturn_t
+s3c2410_udc_irq(int irq, void *_dev, struct pt_regs *r)
+{
+	struct s3c2410_udc      *dev = _dev;
+	int usb_status;
+	int usbd_status;
+	int pwr_reg;
+	int ep0csr;
+	int     i;
+	u32	idx = __raw_readl(S3C2410_UDC_INDEX_REG);
+
+	usb_status = __raw_readl(S3C2410_UDC_USB_INT_REG);
+	usbd_status = __raw_readl(S3C2410_UDC_EP_INT_REG);
+	pwr_reg = __raw_readl(S3C2410_UDC_PWR_REG);
+
+	S3C2410_UDC_SETIX(EP0);
+	ep0csr = __raw_readl(S3C2410_UDC_IN_CSR1_REG);
+
+	dprintk("usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n", usb_status, usbd_status, pwr_reg,ep0csr);
+
+
+	/*
+	 * Now, handle interrupts. There's two types : 
+	 * - Reset, Resume, Suspend coming -> usb_int_reg
+	 * - EP -> ep_int_reg
+	 */
+
+	/* RESET */
+	if (usb_status & S3C2410_UDC_USBINT_RESET)
+	{
+		dprintk("USB reset\n");
+
+	    	__raw_writel(0x00, S3C2410_UDC_PWR_REG);
+		S3C2410_UDC_SETIX(EP0);
+		__raw_writel(S3C2410_UDC_MAXP_8, S3C2410_UDC_MAXP_REG);
+		__raw_writel(0x80, S3C2410_UDC_FUNC_ADDR_REG);
+//		clear_ep0_opr();
+		   
+		/* clear interrupt */
+		__raw_writel(S3C2410_UDC_USBINT_RESET,
+			S3C2410_UDC_USB_INT_REG);
+
+		dev->gadget.speed = USB_SPEED_FULL;
+		dev->ep0state = EP0_IDLE;
+	}
+
+	/* RESUME */
+	if (usb_status & S3C2410_UDC_USBINT_RESUME)
+	{
+		dprintk("USB resume\n");
+		
+		/* clear interrupt */
+		__raw_writel(S3C2410_UDC_USBINT_RESUME,
+			S3C2410_UDC_USB_INT_REG);
+
+		if (dev->gadget.speed != USB_SPEED_UNKNOWN
+			&& dev->driver
+			&& dev->driver->resume)
+			dev->driver->resume(&dev->gadget);
+	}
+
+	/* SUSPEND */
+	if (usb_status & S3C2410_UDC_USBINT_SUSPEND)
+	{
+		dprintk("USB suspend\n");
+			
+		/* clear interrupt */
+		__raw_writel(S3C2410_UDC_USBINT_SUSPEND,
+			S3C2410_UDC_USB_INT_REG);
+				
+		if (dev->gadget.speed != USB_SPEED_UNKNOWN
+				&& dev->driver
+				&& dev->driver->suspend)
+			dev->driver->suspend(&dev->gadget);
+
+		dev->ep0state = EP0_IDLE;
+	}
+
+	/* EP */
+	/* control traffic */
+	/* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready
+	 * generate an interrupt 
+	 */
+	if (usbd_status & S3C2410_UDC_INT_EP0)
+	{
+		dprintk("USB ep0 irq\n");
+		/* Clear the interrupt bit by setting it to 1 */
+		__raw_writel(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG);
+		handle_ep0(dev);
+	}
+	/* endpoint data transfers */
+	for (i = 1; i < S3C2410_ENDPOINTS; i++) {
+		u32 tmp = 1 << i;
+		if (usbd_status & tmp) {
+			dprintk("USB ep%d irq\n", i);
+
+			/* Clear the interrupt bit by setting it to 1 */
+			__raw_writel(tmp, S3C2410_UDC_EP_INT_REG);
+			handle_ep(&dev->ep[i]);
+		}
+	}
+	
+
+	dprintk("irq: %d done.\n", irq);
+	__raw_writel(idx,S3C2410_UDC_INDEX_REG);
+	return IRQ_HANDLED;		
+}
+
+/*
+ * udc_reinit
+ */
+static void udc_reinit(struct s3c2410_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 < S3C2410_ENDPOINTS; i++) {
+		struct s3c2410_ep *ep = &dev->ep[i];
+		if (i != 0)
+			list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
+
+		ep->desc = 0;
+		INIT_LIST_HEAD (&ep->queue);
+	}
+}
+
+/*------------------------- s3c2410_ep_ops ----------------------------------*/
+
+/*
+ * 	s3c2410_ep_enable
+ */
+static int
+s3c2410_ep_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+	struct s3c2410_udc	*dev;
+	struct s3c2410_ep	*ep;
+	u32			max, tmp;
+	unsigned long		flags;
+	u32			csr1,csr2;
+	u32			int_en_reg;
+
+
+	ep = container_of (_ep, struct s3c2410_ep, ep);
+	if (!_ep || !desc || ep->desc || _ep->name == ep0name
+			|| desc->bDescriptorType != USB_DT_ENDPOINT)
+		return -EINVAL;
+	dev = ep->dev;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	max = le16_to_cpu (desc->wMaxPacketSize) & 0x1fff;
+
+	spin_lock_irqsave (&dev->lock, flags);
+	_ep->maxpacket = max & 0x7ff;
+	ep->desc = desc;
+	ep->bEndpointAddress = desc->bEndpointAddress;
+
+	/* set max packet */
+	__raw_writel(ep->num, S3C2410_UDC_INDEX_REG);
+	__raw_writel(max>>3,S3C2410_UDC_MAXP_REG);
+
+
+	/* set type, direction, address; reset fifo counters */
+	if (desc->bEndpointAddress & USB_DIR_IN)
+	{
+		csr1 = S3C2410_UDC_ICSR1_FFLUSH|S3C2410_UDC_ICSR1_CLRDT;
+		csr2 = S3C2410_UDC_ICSR2_MODEIN|S3C2410_UDC_ICSR2_DMAIEN;
+		
+		__raw_writel(ep->num, S3C2410_UDC_INDEX_REG);
+		__raw_writel(csr1,S3C2410_UDC_IN_CSR1_REG);
+		__raw_writel(ep->num, S3C2410_UDC_INDEX_REG);
+		__raw_writel(csr2,S3C2410_UDC_IN_CSR2_REG);
+	}
+	else
+	{
+		/* don't flush he in fifo or there will be an interrupt for that
+		 * endpoint */ 
+		csr1 = S3C2410_UDC_ICSR1_CLRDT;
+		csr2 = S3C2410_UDC_ICSR2_DMAIEN;
+
+		__raw_writel(ep->num, S3C2410_UDC_INDEX_REG);
+		__raw_writel(csr1,S3C2410_UDC_IN_CSR1_REG);
+		__raw_writel(ep->num, S3C2410_UDC_INDEX_REG);
+		__raw_writel(csr2,S3C2410_UDC_IN_CSR2_REG);
+		
+		csr1 = S3C2410_UDC_OCSR1_FFLUSH | S3C2410_UDC_OCSR1_CLRDT;
+		csr2 = S3C2410_UDC_OCSR2_DMAIEN;
+		
+		__raw_writel(ep->num, S3C2410_UDC_INDEX_REG);
+		__raw_writel(csr1,S3C2410_UDC_OUT_CSR1_REG);
+		__raw_writel(ep->num, S3C2410_UDC_INDEX_REG);
+		__raw_writel(csr2,S3C2410_UDC_OUT_CSR2_REG);
+	}
+
+	
+	/* enable irqs */
+	int_en_reg = __raw_readl(S3C2410_UDC_EP_INT_EN_REG);
+	__raw_writel(int_en_reg | (1<<ep->num),S3C2410_UDC_EP_INT_EN_REG);
+
+
+	/* print some debug message */
+	tmp = desc->bEndpointAddress;
+	dprintk ("enable %s(%d) ep%x%s-blk max %02x\n",
+		_ep->name,ep->num, tmp, desc->bEndpointAddress & USB_DIR_IN ? "in" : "out", max);
+
+	spin_unlock_irqrestore (&dev->lock, flags);
+	
+	return 0;
+}
+
+/*
+ * s3c2410_ep_disable
+ */
+static int s3c2410_ep_disable (struct usb_ep *_ep)
+{
+	struct s3c2410_ep	*ep = container_of(_ep, struct s3c2410_ep, ep);
+	unsigned long	flags;
+
+	if (!_ep || !ep->desc) {
+		dprintk("%s not enabled\n",
+			_ep ? ep->ep.name : NULL);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&ep->dev->lock, flags);
+	ep->desc = 0;
+	
+	nuke (ep->dev, ep);
+	ep->ep.maxpacket = EP_FIFO_SIZE; 
+
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+	dprintk("%s disabled\n", _ep->name);
+
+	return 0;
+}
+
+/*
+ * s3c2410_alloc_request
+ */
+static struct usb_request *
+s3c2410_alloc_request (struct usb_ep *_ep, int mem_flags)
+{
+	struct s3c2410_ep	*ep;
+	struct s3c2410_request	*req;
+
+    	printk("s3c2410_alloc_request(ep=%p,flags=%d)\n", _ep, mem_flags);
+
+	ep = container_of (_ep, struct s3c2410_ep, ep);
+	if (!_ep)
+		return 0;
+
+	req = kmalloc (sizeof *req, mem_flags);
+	if (!req)
+		return 0;
+	memset (req, 0, sizeof *req);
+	INIT_LIST_HEAD (&req->queue);
+	return &req->req;
+}
+
+/*
+ * s3c2410_free_request
+ */
+static void
+s3c2410_free_request (struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct s3c2410_ep	*ep;
+	struct s3c2410_request	*req;
+
+    	printk("s3c2410_free_request(ep=%p,req=%p)\n", _ep, _req);
+
+	ep = container_of (_ep, struct s3c2410_ep, ep);
+	if (!ep || !_req || (!ep->desc && _ep->name != ep0name))
+		return;
+
+	req = container_of (_req, struct s3c2410_request, req);
+	WARN_ON (!list_empty (&req->queue));
+	kfree (req);
+}
+
+/*
+ * 	s3c2410_alloc_buffer
+ */
+static void *
+s3c2410_alloc_buffer (
+	struct usb_ep *_ep,
+	unsigned bytes,
+	dma_addr_t *dma,
+	int mem_flags)
+{
+	char *retval;
+
+    	printk("s3c2410_alloc_buffer()\n");
+
+	if (!the_controller->driver)
+		return 0;
+	retval = kmalloc (bytes, mem_flags);
+	*dma = (dma_addr_t) retval;
+	return retval;
+}
+
+/*
+ * s3c2410_free_buffer
+ */
+static void
+s3c2410_free_buffer (
+	struct usb_ep *_ep,
+	void *buf,
+	dma_addr_t dma,
+	unsigned bytes)
+{
+    	printk("s3c2410_free_buffer()\n");
+
+	if (bytes)
+		kfree (buf);
+}
+
+/*
+ * 	s3c2410_queue
+ */
+static int
+s3c2410_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags)
+{
+	struct s3c2410_request	*req;
+	struct s3c2410_ep	*ep;
+	struct s3c2410_udc	*dev;
+	u32			ep_csr=0;
+	int 			fifo_count=0;
+
+	req = container_of(_req, struct s3c2410_request, req);
+	if (unlikely (!_req || !_req->complete || !_req->buf
+	|| !list_empty(&req->queue))) {
+		if (!_req)
+			dprintk("s3c2410_queue: 1 X X X\n");
+		else
+		{
+			dprintk("s3c2410_queue: 0 %01d %01d %01d\n",!_req->complete,!_req->buf, !list_empty(&req->queue));
+		}
+		return -EINVAL;
+	}
+
+	ep = container_of(_ep, struct s3c2410_ep, ep);
+	if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
+		dprintk("s3c2410_queue: inval 2\n");
+		return -EINVAL;
+	}
+
+	dev = ep->dev;
+	if (unlikely (!dev->driver
+			|| dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+		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;
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	if (ep->bEndpointAddress)
+	{
+		__raw_writel(ep->bEndpointAddress&0x7F,S3C2410_UDC_INDEX_REG);
+		ep_csr = __raw_readl(ep->bEndpointAddress&USB_DIR_IN ? S3C2410_UDC_IN_CSR1_REG : S3C2410_UDC_OUT_CSR1_REG);
+		fifo_count=fifo_count_out();
+	}
+	/* kickstart this i/o queue? */
+	if (list_empty(&ep->queue)) {
+		if (ep->bEndpointAddress == 0 /* ep0 */) {
+
+			switch (dev->ep0state) {
+			case EP0_IN_DATA_PHASE:
+				if (write_fifo(ep, req))
+				{
+					req = 0;
+				}
+				break;
+
+			case EP0_OUT_DATA_PHASE:
+				/* nothing to do here */
+				dev->ep0state = EP0_IDLE;
+				break;
+
+			default:
+				return -EL2HLT;
+			}
+		}
+		else if ((ep->bEndpointAddress & USB_DIR_IN) != 0
+				&& (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY)) && write_fifo(ep, req)) {
+			req = 0;
+		} else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY) && fifo_count && read_fifo(ep, req)) {
+			req = 0;
+		}
+
+	}
+
+	/* pio or dma irq handler advances the queue. */
+	if (likely (req != 0))
+		list_add_tail(&req->queue, &ep->queue);
+	dprintk("s3c2410_queue normal end\n");
+	return 0;
+}
+
+/* 
+ * 	s3c2410_dequeue
+ */
+static int s3c2410_dequeue (struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct s3c2410_ep	*ep;
+	struct s3c2410_udc	*udc;
+	int			retval = -EINVAL;
+	unsigned long		flags;
+	struct s3c2410_request	*req = 0;
+
+    	printk("s3c2410_dequeue(ep=%p,req=%p)\n", _ep, _req);
+
+	if (!the_controller->driver)
+		return -ESHUTDOWN;
+
+	if (!_ep || !_req)
+		return retval;
+	ep = container_of (_ep, struct s3c2410_ep, ep);
+	udc = container_of (ep->gadget, struct s3c2410_udc, gadget);
+
+	spin_lock_irqsave (&udc->lock, flags);
+	list_for_each_entry (req, &ep->queue, queue) {
+		if (&req->req == _req) {
+			list_del_init (&req->queue);
+			_req->status = -ECONNRESET;
+			retval = 0;
+			break;
+		}
+	}
+	spin_unlock_irqrestore (&udc->lock, flags);
+
+	if (retval == 0) {
+		dprintk( "dequeued req %p from %s, len %d buf %p\n",
+				req, _ep->name, _req->length, _req->buf);
+
+		_req->complete (_ep, _req);
+		done(ep, req, -ECONNRESET);
+	}
+	return retval;
+
+	return 0;
+}
+
+
+/* 
+ * s3c2410_set_halt
+ */
+static int
+s3c2410_set_halt (struct usb_ep *_ep, int value)
+{
+	return 0;
+}
+
+
+static const struct usb_ep_ops s3c2410_ep_ops = {
+	.enable         = s3c2410_ep_enable,
+	.disable        = s3c2410_ep_disable,
+	
+	.alloc_request  = s3c2410_alloc_request,
+	.free_request   = s3c2410_free_request,
+	
+	.alloc_buffer   = s3c2410_alloc_buffer,
+	.free_buffer    = s3c2410_free_buffer,
+	
+	.queue          = s3c2410_queue,
+	.dequeue        = s3c2410_dequeue,
+	
+	.set_halt       = s3c2410_set_halt,
+};
+
+/*------------------------- usb_gadget_ops ----------------------------------*/
+
+/*
+ * 	s3c2410_g_get_frame
+ */
+static int s3c2410_g_get_frame (struct usb_gadget *_gadget)
+{
+	int tmp;
+	
+	printk("s3c2410_g_get_frame()\n");
+
+	tmp = __raw_readl(S3C2410_UDC_FRAME_NUM2_REG) << 8;
+	tmp |= __raw_readl(S3C2410_UDC_FRAME_NUM1_REG);
+	
+	return tmp & 0xffff;
+}
+
+/*
+ * 	s3c2410_wakeup
+ */
+static int s3c2410_wakeup (struct usb_gadget *_gadget)
+{
+	
+	printk("s3c2410_wakeup()\n");
+	
+	return 0;
+}
+
+/*
+ * 	s3c2410_set_selfpowered
+ */
+static int s3c2410_set_selfpowered (struct usb_gadget *_gadget, int value)
+{
+	struct s3c2410_udc  *udc;
+	
+	printk("s3c2410_set_selfpowered()\n");
+	
+	udc = container_of (_gadget, struct s3c2410_udc, gadget);
+	
+	if (value)
+		udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
+	else
+		udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
+	
+	return 0;
+}
+                                                                                   
+
+
+static const struct usb_gadget_ops s3c2410_ops = {
+	.get_frame          = s3c2410_g_get_frame,
+	.wakeup             = s3c2410_wakeup,
+	.set_selfpowered    = s3c2410_set_selfpowered,
+};
+
+
+/*------------------------- gadget driver handling---------------------------*/
+
+/*
+ * 	nop_release
+ */
+static void nop_release (struct device *dev)
+{
+	        dprintk("%s %s\n", __FUNCTION__, dev->bus_id);
+}
+/*
+ *	usb_gadget_register_driver
+ */
+int
+usb_gadget_register_driver (struct usb_gadget_driver *driver)
+{
+	struct s3c2410_udc *udc = the_controller;
+	int		retval, i;
+
+    	printk("usb_gadget_register_driver() '%s'\n",
+		driver->driver.name);
+
+	if (!udc)
+		return -ENODEV;
+	if (udc->driver)
+		return -EBUSY;
+	if (!driver->bind || !driver->unbind || !driver->setup
+			|| driver->speed == USB_SPEED_UNKNOWN)
+		return -EINVAL;
+
+	udc->gadget.name = gadget_name;
+	udc->gadget.ops = &s3c2410_ops;
+	udc->gadget.is_dualspeed = 1;
+
+	udc->gadget.dev.release = nop_release;
+	udc->devstatus = 0;
+
+	INIT_LIST_HEAD (&udc->gadget.ep_list);
+	for (i = 0; i < S3C2410_ENDPOINTS; i++) {
+		struct s3c2410_ep *ep = &udc->ep[i];
+
+		if (!ep_name[i])
+			break;
+		ep->num = i;
+		ep->ep.name = ep_name[i];
+		ep->ep.ops = &s3c2410_ep_ops;
+		list_add_tail (&ep->ep.ep_list, &udc->gadget.ep_list);
+		ep->halted = ep->already_seen = ep->setup_stage = 0;
+
+		/* maxpacket differs between ep0 and others ep */
+		if (!i) 
+			ep->ep.maxpacket = EP0_FIFO_SIZE;
+		else
+			ep->ep.maxpacket = EP_FIFO_SIZE;
+		ep->last_io = jiffies;
+		ep->gadget = &udc->gadget;
+		ep->dev = udc;
+		ep->desc = 0;
+		INIT_LIST_HEAD (&ep->queue);
+	}
+
+	udc->gadget.ep0 = &udc->ep[0].ep;
+	udc->ep[0].ep.maxpacket = 8;
+	list_del_init (&udc->ep[0].ep.ep_list);
+	INIT_LIST_HEAD(&udc->fifo_req.queue);
+
+	udc->driver = driver;
+	udc->gadget.dev.driver = &driver->driver;
+	dprintk( "binding gadget driver '%s'\n", driver->driver.name);
+	if ((retval = driver->bind (&udc->gadget)) != 0) {
+		udc->driver = 0;
+		udc->gadget.dev.driver = 0;
+		return retval;
+	}
+
+        driver->driver.bus = 0;
+	udc_reinit(udc);
+	
+	return 0;
+}
+
+
+/*
+ * 	usb_gadget_unregister_driver
+ */
+int
+usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
+{
+	struct s3c2410_udc *udc = the_controller;
+
+	if (!udc)
+		return -ENODEV;
+	if (!driver || driver != udc->driver)
+		return -EINVAL;
+
+    	printk("usb_gadget_register_driver() '%s'\n",
+		driver->driver.name);
+
+	driver->unbind (&udc->gadget);
+	udc->driver = 0;
+
+	device_release_driver (&udc->gadget.dev);
+	driver_unregister (&driver->driver);
+
+	return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+ *	probe - binds to the platform device
+ */
+static int __init s3c2410_udc_probe(struct device *_dev)
+{
+	struct s3c2410_udc *udc = &memory;
+	u32 int_en_reg;
+	int retval;
+
+
+	dprintk("s3c2410_udc_probe\n");
+
+
+	device_initialize(&udc->gadget.dev);
+	udc->gadget.dev.parent = _dev;
+	udc->gadget.dev.dma_mask = _dev->dma_mask;
+
+	the_controller = udc;
+	dev_set_drvdata(_dev, udc);
+	spin_lock_init (&udc->lock);
+
+	/* irq setup after old hardware state is cleaned up */
+	retval = request_irq(IRQ_USBD, s3c2410_udc_irq,
+		SA_INTERRUPT, gadget_name, udc);
+	if (retval != 0) {
+		printk(KERN_ERR "%s: can't get irq %i, err %d\n",
+			gadget_name, IRQ_USBD, retval);
+		return -EBUSY;
+	}
+	dprintk("%s: got irq %i\n", gadget_name, IRQ_USBD);
+	udc->got_irq = 1;
+
+	/* Enable ep0 interrupts */
+	int_en_reg = __raw_readl(S3C2410_UDC_EP_INT_EN_REG);
+	__raw_writel(int_en_reg | 1,S3C2410_UDC_EP_INT_EN_REG);
+
+	return 0;
+}
+
+/*
+ * 	s3c2410_udc_remove
+ */
+static int __exit s3c2410_udc_remove(struct device *_dev)
+{
+	struct s3c2410_udc *udc = _dev->driver_data;
+	
+	dprintk("s3c2410_udc_remove\n");
+	usb_gadget_unregister_driver(udc->driver);
+	
+	if (udc->got_irq) {
+		free_irq(IRQ_USBD, udc);
+		udc->got_irq = 0;
+	}
+	
+	dev_set_drvdata(_dev, 0);
+	kfree(udc);
+	
+	return 0;
+}
+                                                                               
+static struct device_driver udc_driver = {
+	.name		= "s3c2410-usbgadget",
+	.bus            = &platform_bus_type,
+	.probe          = s3c2410_udc_probe,
+	.remove         = __exit_p(s3c2410_udc_remove),			
+};
+
+static int __init udc_init(void)
+{
+	dprintk("%s: version %s\n", gadget_name, DRIVER_VERSION);
+	
+	udc_clock = clk_get(NULL, "usb-device");
+	if (!udc_clock) {
+		printk(KERN_INFO "failed to get udc clock source\n");
+		return -ENOENT;
+	}
+	clk_use(udc_clock);
+	clk_enable(udc_clock);
+	dprintk("got and enabled clock\n");
+						
+	
+	return driver_register(&udc_driver);
+}
+
+static void __exit udc_exit(void)
+{
+	if (udc_clock) {
+		clk_disable(udc_clock);
+		clk_unuse(udc_clock);
+		clk_put(udc_clock);
+		udc_clock = NULL;
+	}
+		
+	driver_unregister(&udc_driver);
+}
+
+
+EXPORT_SYMBOL (usb_gadget_unregister_driver);
+EXPORT_SYMBOL (usb_gadget_register_driver);
+
+module_init(udc_init);
+module_exit(udc_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff -Nur linux-2.6.10-rc1-bk10.orig/drivers/usb/gadget/s3c2410_udc.h linux-2.6.10-rc1-bk10/drivers/usb/gadget/s3c2410_udc.h
--- linux-2.6.10-rc1-bk10.orig/drivers/usb/gadget/s3c2410_udc.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.10-rc1-bk10/drivers/usb/gadget/s3c2410_udc.h	2004-10-31 19:29:54.000000000 +0100
@@ -0,0 +1,215 @@
+#ifndef _S3C2410_UDC_H
+#define _S3C2410_UDC_H
+
+struct s3c2410_ep {
+	struct list_head		queue;
+	unsigned long			last_io;	/* jiffies timestamp */
+	struct usb_gadget		*gadget;
+	struct s3c2410_udc		*dev;
+	const struct usb_endpoint_descriptor *desc;
+	struct usb_ep			ep;
+	u8				num;
+
+	unsigned short			fifo_size;
+	u8				bEndpointAddress;
+	u8				bmAttributes;
+
+	unsigned			halted : 1;
+	unsigned			already_seen : 1;
+	unsigned			setup_stage : 1;
+};
+
+static const char ep0name [] = "ep0";
+
+static const char *const ep_name[] = {
+	ep0name,                                /* everyone has ep0 */
+	/* s3c2410 four bidirectional bulk endpoints */
+	"ep1-bulk", "ep2-bulk", "ep3-bulk", "ep4-bulk",
+};
+
+#define S3C2410_ENDPOINTS       ARRAY_SIZE(ep_name)
+
+#define EP0_FIFO_SIZE		8
+#define EP_FIFO_SIZE		64
+
+
+struct s3c2410_request {
+	struct list_head		queue;		/* ep's requests */
+	struct usb_request		req;
+};
+
+enum ep0_state { 
+        EP0_IDLE,
+        EP0_IN_DATA_PHASE,
+        EP0_OUT_DATA_PHASE,
+        EP0_END_XFER,
+        EP0_STALL,
+};
+
+static const char *ep0states[]= {
+        "EP0_IDLE",
+        "EP0_IN_DATA_PHASE",
+        "EP0_OUT_DATA_PHASE",
+        "EP0_END_XFER",
+        "EP0_STALL",
+};
+
+struct s3c2410_udc {
+	spinlock_t			lock;
+
+	struct s3c2410_ep		ep[S3C2410_ENDPOINTS];
+	int				address;
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+	struct s3c2410_request		fifo_req;
+	u8				fifo_buf[EP_FIFO_SIZE];
+	u16				devstatus;
+	
+	u32				port_status;
+    	int 	    	    	    	ep0state;
+
+	unsigned			got_irq : 1;
+	
+	unsigned			req_std : 1;
+	unsigned			req_config : 1;
+	unsigned			req_pending : 1;
+};
+
+/****************** MACROS ******************/
+/* #define BIT_MASK	BIT_MASK*/
+#define BIT_MASK	0xFF
+
+#define __raw_maskb(v,m,a)      \
+	        __raw_writeb((__raw_readb(a) & ~(m))|((v)&(m)), (a))
+
+#define __raw_maskw(v,m,a)      \
+	        __raw_writew((__raw_readw(a) & ~(m))|((v)&(m)), (a))
+
+#define __raw_maskl(v,m,a)      \
+	        __raw_writel((__raw_readl(a) & ~(m))|((v)&(m)), (a))
+
+#define clear_ep0_sst() do { 				\
+    	S3C2410_UDC_SETIX(EP0); 			\
+	__raw_writel(0x00, S3C2410_UDC_EP0_CSR_REG); 	\
+} while(0)
+
+#define clear_ep0_se() do {				\
+    	S3C2410_UDC_SETIX(EP0); 			\
+	__raw_maskl(S3C2410_UDC_EP0_CSR_SSE,		\
+	    	BIT_MASK, S3C2410_UDC_EP0_CSR_REG); 	\
+} while(0)
+
+#define clear_ep0_opr() do {				\
+   	S3C2410_UDC_SETIX(EP0);				\
+	__raw_maskl(S3C2410_UDC_EP0_CSR_SOPKTRDY,	\
+		BIT_MASK, S3C2410_UDC_EP0_CSR_REG); 	\
+} while(0)
+
+#define set_ep0_ipr() do {				\
+   	S3C2410_UDC_SETIX(EP0);				\
+	__raw_maskl(S3C2410_UDC_EP0_CSR_IPKRDY,		\
+		BIT_MASK, S3C2410_UDC_EP0_CSR_REG); 	\
+} while(0)
+
+#define set_ep0_de() do {				\
+   	S3C2410_UDC_SETIX(EP0);				\
+	__raw_maskl(S3C2410_UDC_EP0_CSR_DE,		\
+		BIT_MASK, S3C2410_UDC_EP0_CSR_REG);		\
+} while(0)
+
+#define set_ep0_ss() do {				\
+   	S3C2410_UDC_SETIX(EP0);				\
+	__raw_maskl(S3C2410_UDC_EP0_CSR_SENDSTL,	\
+		BIT_MASK, S3C2410_UDC_EP0_CSR_REG);	\
+} while(0)
+
+#define set_ep0_de_out() do {				\
+   	S3C2410_UDC_SETIX(EP0);				\
+	__raw_maskl((S3C2410_UDC_EP0_CSR_SOPKTRDY 	\
+		| S3C2410_UDC_EP0_CSR_DE),		\
+		BIT_MASK, S3C2410_UDC_EP0_CSR_REG);		\
+} while(0)
+
+#define set_ep0_sse_out() do {				\
+   	S3C2410_UDC_SETIX(EP0);				\
+	__raw_maskl((S3C2410_UDC_EP0_CSR_SOPKTRDY 	\
+		| S3C2410_UDC_EP0_CSR_SSE),		\
+		BIT_MASK, S3C2410_UDC_EP0_CSR_REG);		\
+} while(0)
+
+#define set_ep0_de_in() do {				\
+   	S3C2410_UDC_SETIX(EP0);				\
+	__raw_maskl((S3C2410_UDC_EP0_CSR_IPKRDY		\
+		| S3C2410_UDC_EP0_CSR_DE),		\
+		BIT_MASK, S3C2410_UDC_EP0_CSR_REG);		\
+} while(0)
+
+#define set_ep0_caramba() do {				\
+   	S3C2410_UDC_SETIX(EP0);				\
+	__raw_maskl((S3C2410_UDC_EP0_CSR_IPKRDY		\
+		| S3C2410_UDC_EP0_CSR_SOPKTRDY		\
+		| S3C2410_UDC_EP0_CSR_DE),		\
+		BIT_MASK, S3C2410_UDC_EP0_CSR_REG);		\
+} while(0)
+
+
+
+#define clear_stall_ep1_out() do {			\
+   	S3C2410_UDC_SETIX(EP1);				\
+	__raw_orl(0, S3C2410_UDC_OUT_CSR1_REG);		\
+} while(0)
+
+
+#define clear_stall_ep2_out() do {			\
+   	S3C2410_UDC_SETIX(EP2);				\
+	__raw_orl(0, S3C2410_UDC_OUT_CSR1_REG);		\
+} while(0)
+
+
+#define clear_stall_ep3_out() do {			\
+   	S3C2410_UDC_SETIX(EP3);				\
+	__raw_orl(0, S3C2410_UDC_OUT_CSR1_REG);		\
+} while(0)
+
+
+#define clear_stall_ep4_out() do {			\
+   	S3C2410_UDC_SETIX(EP4);				\
+	__raw_orl(0, S3C2410_UDC_OUT_CSR1_REG);		\
+} while(0)
+
+/*************************** DEBUG FUNCTION ***************************/
+// #define DEBUG
+
+#ifdef DEBUG
+uint32_t s3c2410_ticks=0;
+static int dprintk(const char *fmt, ...)
+{
+	static char printk_buf[1024];
+	static long prevticks;
+	static int invocation;
+	va_list args;
+	int len;
+	
+	if (s3c2410_ticks != prevticks) {
+		prevticks = s3c2410_ticks;
+		invocation = 0;
+	}
+	
+	len = scnprintf(printk_buf, \
+			sizeof(printk_buf), "%1lu.%02d USB: ", \
+			prevticks, invocation++);
+
+	va_start(args, fmt);
+	len = vscnprintf(printk_buf+len, \
+			sizeof(printk_buf)-len, fmt, args);
+	va_end(args);
+	
+	return printk("%s", printk_buf);
+}
+#else
+static int dprintk(const char *fmt, ...)  { return 0; }
+#endif
+
+#endif
+
+
diff -Nur linux-2.6.10-rc1-bk10.orig/drivers/usb/gadget/serial.c linux-2.6.10-rc1-bk10/drivers/usb/gadget/serial.c
--- linux-2.6.10-rc1-bk10.orig/drivers/usb/gadget/serial.c	2004-10-31 17:28:08.000000000 +0100
+++ linux-2.6.10-rc1-bk10/drivers/usb/gadget/serial.c	2004-10-31 19:35:06.000000000 +0100
@@ -373,7 +373,11 @@
 static struct usb_device_descriptor gs_device_desc = {
 	.bLength =		USB_DT_DEVICE_SIZE,
 	.bDescriptorType =	USB_DT_DEVICE,
+#ifdef CONFIG_USB_GADGET_DUALSPEED
 	.bcdUSB =		__constant_cpu_to_le16(0x0200),
+#else
+	.bcdUSB =		__constant_cpu_to_le16(0x0110),
+#endif
 	.bDeviceClass =		USB_CLASS_VENDOR_SPEC,
 	.idVendor =		__constant_cpu_to_le16(GS_VENDOR_ID),
 	.idProduct =		__constant_cpu_to_le16(GS_PRODUCT_ID),
@@ -1274,6 +1278,9 @@
 	} else if (gadget_is_lh7a40x(gadget)) {
 		gs_device_desc.bcdDevice =
 			__constant_cpu_to_le16(GS_VERSION_NUM|0x0008);
+	} else if (gadget_is_s3c2410(gadget)) {
+		gs_device_desc.bcdDevice =
+			__constant_cpu_to_le16(GS_VERSION_NUM|0x0009);
 	} else {
 		printk(KERN_WARNING "gs_bind: controller '%s' not recognized\n",
 			gadget->name);
diff -Nur linux-2.6.10-rc1-bk10.orig/drivers/usb/gadget/zero.c linux-2.6.10-rc1-bk10/drivers/usb/gadget/zero.c
--- linux-2.6.10-rc1-bk10.orig/drivers/usb/gadget/zero.c	2004-10-31 17:28:08.000000000 +0100
+++ linux-2.6.10-rc1-bk10/drivers/usb/gadget/zero.c	2004-10-31 19:36:03.000000000 +0100
@@ -227,7 +227,11 @@
 	.bLength =		sizeof device_desc,
 	.bDescriptorType =	USB_DT_DEVICE,
 
+#ifdef	CONFIG_USB_GADGET_DUALSPEED
 	.bcdUSB =		__constant_cpu_to_le16 (0x0200),
+#else
+	.bcdUSB =               __constant_cpu_to_le16 (0x0110),
+#endif
 	.bDeviceClass =		USB_CLASS_VENDOR_SPEC,
 
 	.idVendor =		__constant_cpu_to_le16 (DRIVER_VENDOR_NUM),
@@ -1190,6 +1194,8 @@
 		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209);
 	} else if (gadget_is_n9604(gadget)) {
 		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0210);
+	} else if (gadget_is_s3c2410(gadget)) {
+ 		device_desc.bcdDevice = __constant_cpu_to_le16 (0x020A);
 	} else {
 		/* gadget zero is so simple (for now, no altsettings) that
 		 * it SHOULD NOT have problems with bulk-capable hardware.

