diff -NurpP --minimal linux-2.6.8-rc2-h1940-0.03/drivers/usb/gadget/Kconfig linux-2.6.8-rc2-h1940-0.04/drivers/usb/gadget/Kconfig
--- linux-2.6.8-rc2-h1940-0.03/drivers/usb/gadget/Kconfig	2004-07-23 18:10:09.000000000 +0200
+++ linux-2.6.8-rc2-h1940-0.04/drivers/usb/gadget/Kconfig	2004-07-30 18:06:29.000000000 +0200
@@ -135,6 +135,21 @@ config USB_SA1100
 	depends on USB_GADGET_SA1100
 	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 -NurpP --minimal linux-2.6.8-rc2-h1940-0.03/drivers/usb/gadget/Makefile linux-2.6.8-rc2-h1940-0.04/drivers/usb/gadget/Makefile
--- linux-2.6.8-rc2-h1940-0.03/drivers/usb/gadget/Makefile	2004-07-19 03:33:39.000000000 +0200
+++ linux-2.6.8-rc2-h1940-0.04/drivers/usb/gadget/Makefile	2004-07-30 18:07:02.000000000 +0200
@@ -4,6 +4,7 @@
 obj-$(CONFIG_USB_DUMMY_HCD)	+= dummy_hcd.o
 obj-$(CONFIG_USB_NET2280)	+= net2280.o
 obj-$(CONFIG_USB_PXA2XX)	+= pxa2xx_udc.o
+obj-$(CONFIG_USB_S3C2410)	+= s3c2410_udc.o
 obj-$(CONFIG_USB_GOKU)		+= goku_udc.o
 
 #
diff -NurpP --minimal linux-2.6.8-rc2-h1940-0.03/drivers/usb/gadget/s3c2410_udc.c linux-2.6.8-rc2-h1940-0.04/drivers/usb/gadget/s3c2410_udc.c
--- linux-2.6.8-rc2-h1940-0.03/drivers/usb/gadget/s3c2410_udc.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.8-rc2-h1940-0.04/drivers/usb/gadget/s3c2410_udc.c	2004-07-30 20:26:44.000000000 +0200
@@ -0,0 +1,1057 @@
+/*
+ * linux/drivers/usb/gadget/s3c2410_udc.c
+ * Samsung on-chip full speed USB device controllers
+ *
+ * Copyright (C) 2004 Herbert Pötzl
+ *
+ * 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>
+
+/* remove this ASAP!!! */
+#include "../core/hub.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-usb.h>
+
+
+#define dprintk(x...)	printk("USB:" x)
+
+
+#define DRIVER_DESC     "S3C2410 USB Device Controller Gadget"
+#define DRIVER_VERSION  "14 Mar 2004"
+
+static const char	gadget_name [] = "s3c2410_udc";
+
+
+/*-------------------------------------------------------------------------*/
+
+
+struct s3c2410_udc;
+
+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;
+
+	unsigned long			pio_irqs;
+	unsigned long			dma_irqs;
+	short				dma;
+
+	unsigned short			fifo_size;
+	u8				bEndpointAddress;
+	u8				bmAttributes;
+
+	unsigned			halted : 1;
+	unsigned			already_seen : 1;
+	unsigned			setup_stage : 1;
+//	unsigned			stopped : 1;	    /* replaced by halted? */
+	unsigned			dma_fixup : 1;
+
+
+};
+
+struct s3c2410_request {
+	struct list_head		queue;		/* ep's requests */
+	struct usb_request		req;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Every device has ep0 for control requests, plus up to 30 more endpoints.
+ *
+ * Gadget drivers are responsible for not setting up conflicting endpoint
+ * configurations, illegal or unsupported packet lengths, and so on.
+ */
+
+static const char ep0name [] = "ep0";
+
+static const char *const ep_name[] = {
+	ep0name,				/* everyone has ep0 */
+
+	/* s3c2410 four bidirectional bulk endpoints */
+	"ep1", "ep2", "ep3", "ep4",
+};
+
+#define S3C2410_ENDPOINTS	ARRAY_SIZE(ep_name)
+
+#define FIFO_SIZE		64
+
+
+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[FIFO_SIZE];
+	u16				devstatus;
+	
+	u32				port_status;
+
+	unsigned			got_irq : 1;
+};
+
+static struct s3c2410_udc *the_controller;
+
+static inline
+struct s3c2410_udc *ep_to_udc (struct s3c2410_ep *ep)
+{
+	return container_of (ep->gadget, struct s3c2410_udc, gadget);
+}
+
+static inline
+struct s3c2410_udc *gadget_dev_to_udc (struct device *dev)
+{
+	return container_of (dev, struct s3c2410_udc, gadget.dev);
+}
+
+
+
+#define is_enabled() \
+	(the_controller->port_status & USB_PORT_STAT_ENABLE)
+
+static int
+s3c2410_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+	struct s3c2410_udc	*udc;
+	struct s3c2410_ep	*ep;
+	unsigned		max;
+	int			retval;
+
+	ep = container_of (_ep, struct s3c2410_ep, ep);
+	if (!_ep || !desc || ep->desc || _ep->name == ep0name
+		|| desc->bDescriptorType != USB_DT_ENDPOINT)
+		
+	if (!the_controller->driver || !is_enabled ())
+		return -ESHUTDOWN;
+	max = desc->wMaxPacketSize & 0x3ff;
+
+	/* drivers must not request bad settings, since lower levels
+	 * (hardware or its drivers) may not check.  some endpoints
+	 * can't do iso, many have maxpacket limitations, etc.
+	 */
+	udc = container_of (ep->gadget, struct s3c2410_udc, gadget);
+	retval = -EINVAL;
+	switch (desc->bmAttributes & 0x03) {
+	case USB_ENDPOINT_XFER_BULK:
+		switch (udc->gadget.speed) {
+		case USB_SPEED_HIGH:
+			if (max == 512)
+				break;
+			/* conserve return statements */
+		default:
+			switch (max) {
+			case 8: case 16: case 32: case 64:
+				/* we'll fake any legal size */
+				break;
+			default:
+		case USB_SPEED_LOW:
+				goto done;
+			}
+		}
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		switch (udc->gadget.speed) {
+		case USB_SPEED_HIGH:
+			if (max <= 1024)
+				break;
+			/* save a return statement */
+		case USB_SPEED_FULL:
+			if (max <= 64)
+				break;
+			/* save a return statement */
+		default:
+			if (max <= 8)
+				break;
+			goto done;
+		}
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		/* real hardware might not handle all packet sizes */
+		switch (udc->gadget.speed) {
+		case USB_SPEED_HIGH:
+			if (max <= 1024)
+				break;
+			/* save a return statement */
+		case USB_SPEED_FULL:
+			if (max <= 1023)
+				break;
+			/* save a return statement */
+		default:
+			goto done;
+		}
+		break;
+	default:
+		/* few chips support control except on ep0 */
+		goto done;
+	}
+
+	_ep->maxpacket = max;
+	ep->desc = desc;
+
+	dprintk( "enabled %s (ep%d%s-%s) maxpacket %d\n",
+		_ep->name,
+		desc->bEndpointAddress & 0x0f,
+		(desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
+		({ char *val;
+		 switch (desc->bmAttributes & 0x03) {
+		 case USB_ENDPOINT_XFER_BULK: val = "bulk"; break;
+		 case USB_ENDPOINT_XFER_ISOC: val = "iso"; break;
+		 case USB_ENDPOINT_XFER_INT: val = "intr"; break;
+		 default: val = "ctrl"; break;
+		 }; val; }),
+		max);
+
+	/* at this point real hardware should be NAKing transfers
+	 * to that endpoint, until a buffer is queued to it.
+	 */
+	retval = 0;
+done:
+	return retval;
+}
+
+/* called with spinlock held */
+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);
+	}
+}
+
+static int s3c2410_disable (struct usb_ep *_ep)
+{
+	struct s3c2410_ep	*ep;
+	struct s3c2410_udc	*udc;
+	unsigned long		flags;
+	int			retval;
+
+	ep = container_of (_ep, struct s3c2410_ep, ep);
+	if (!_ep || !ep->desc || _ep->name == ep0name)
+		return -EINVAL;
+	udc = ep_to_udc (ep);
+
+	spin_lock_irqsave (&udc->lock, flags);
+	ep->desc = 0;
+	retval = 0;
+	nuke (udc, ep);
+	spin_unlock_irqrestore (&udc->lock, flags);
+
+	dprintk( "disabled %s\n", _ep->name);
+	return retval;
+}
+
+static struct usb_request *
+s3c2410_alloc_request (struct usb_ep *_ep, int mem_flags)
+{
+	struct s3c2410_ep	*ep;
+	struct s3c2410_request	*req;
+
+	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;
+}
+
+static void
+s3c2410_free_request (struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct s3c2410_ep	*ep;
+	struct s3c2410_request	*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);
+}
+
+static void *
+s3c2410_alloc_buffer (
+	struct usb_ep *_ep,
+	unsigned bytes,
+	dma_addr_t *dma,
+	int mem_flags)
+{
+	char *retval;
+
+	if (!the_controller->driver)
+		return 0;
+	retval = kmalloc (bytes, mem_flags);
+	*dma = (dma_addr_t) retval;
+	return retval;
+}
+
+static void
+s3c2410_free_buffer (
+	struct usb_ep *_ep,
+	void *buf,
+	dma_addr_t dma,
+	unsigned bytes)
+{
+	if (bytes)
+		kfree (buf);
+}
+
+static void
+fifo_complete (struct usb_ep *ep, struct usb_request *req)
+{
+#if 0
+	dprintk( "fifo_complete: %d\n", req->status);
+#endif
+}
+
+static int
+s3c2410_queue (struct usb_ep *_ep, struct usb_request *_req, int mem_flags)
+{
+	struct s3c2410_ep	*ep;
+	struct s3c2410_request	*req;
+	struct s3c2410_udc	*udc;
+	unsigned long		flags;
+
+	req = container_of (_req, struct s3c2410_request, req);
+	if (!_req || !list_empty (&req->queue) || !_req->complete)
+		return -EINVAL;
+
+	ep = container_of (_ep, struct s3c2410_ep, ep);
+	if (!_ep || (!ep->desc && _ep->name != ep0name))
+		return -EINVAL;
+
+	if (!the_controller->driver || !is_enabled ())
+		return -ESHUTDOWN;
+
+	udc = container_of (ep->gadget, struct s3c2410_udc, gadget);
+
+#if 0
+	dprintk( "ep %p queue req %p to %s, len %d buf %p\n",
+			ep, _req, _ep->name, _req->length, _req->buf);
+#endif
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+	spin_lock_irqsave (&udc->lock, flags);
+
+	/* implement an emulated single-request FIFO */
+	if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
+			list_empty (&udc->fifo_req.queue) &&
+			list_empty (&ep->queue) &&
+			_req->length <= FIFO_SIZE) {
+		req = &udc->fifo_req;
+		req->req = *_req;
+		req->req.buf = udc->fifo_buf;
+		memcpy (udc->fifo_buf, _req->buf, _req->length);
+		req->req.context = udc;
+		req->req.complete = fifo_complete;
+
+		spin_unlock (&udc->lock);
+		_req->actual = _req->length;
+		_req->status = 0;
+		_req->complete (_ep, _req);
+		spin_lock (&udc->lock);
+	}
+	list_add_tail (&req->queue, &ep->queue);
+	spin_unlock_irqrestore (&udc->lock, flags);
+
+	/* real hardware would likely enable transfers here, in case
+	 * it'd been left NAKing.
+	 */
+	return 0;
+}
+
+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;
+
+	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);
+	}
+	return retval;
+}
+
+static int
+s3c2410_set_halt (struct usb_ep *_ep, int value)
+{
+	struct s3c2410_ep	*ep;
+
+	if (!_ep)
+		return -EINVAL;
+	if (!the_controller->driver)
+		return -ESHUTDOWN;
+	ep = container_of (_ep, struct s3c2410_ep, ep);
+	if (!value)
+		ep->halted = 0;
+	else if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
+			!list_empty (&ep->queue))
+		return -EAGAIN;
+	else
+		ep->halted = 1;
+	/* FIXME clear emulated data toggle too */
+	return 0;
+}
+
+
+
+static const struct usb_ep_ops s3c2410_ep_ops = {
+	.enable		= s3c2410_enable,
+	.disable	= s3c2410_disable,
+
+	.alloc_request	= s3c2410_alloc_request,
+	.free_request	= s3c2410_free_request,
+
+	.alloc_buffer	= s3c2410_alloc_buffer,
+	.free_buffer	= s3c2410_free_buffer,
+	/* map, unmap, ... eventually hook the "generic" dma calls */
+
+	.queue		= s3c2410_queue,
+	.dequeue	= s3c2410_dequeue,
+
+	.set_halt	= s3c2410_set_halt,
+};
+
+
+
+/*-------------------------------------------------------------------------*/
+
+/* there are both host and device side versions of this call ... */
+static int s3c2410_g_get_frame (struct usb_gadget *_gadget)
+{
+	struct timeval	tv;
+
+	do_gettimeofday (&tv);
+	return tv.tv_usec / 1000;
+}
+
+static int s3c2410_wakeup (struct usb_gadget *_gadget)
+{
+	struct s3c2410_udc  *udc;
+
+	udc = container_of (_gadget, struct s3c2410_udc, gadget);
+	if ((udc->devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) == 0
+		|| !(udc->port_status & (1 << USB_PORT_FEAT_SUSPEND)))
+		return -EINVAL;
+
+	udc->port_status |= (1 << USB_PORT_FEAT_C_SUSPEND);
+	return 0;
+}
+
+static int s3c2410_set_selfpowered (struct usb_gadget *_gadget, int value)
+{
+	struct s3c2410_udc  *udc;
+
+	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,
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+/* "function" sysfs attribute */
+static ssize_t
+show_function (struct device *_dev, char *buf)
+{
+	struct s3c2410_udc *udc = the_controller;
+
+	if (!udc->driver->function
+		|| strlen (udc->driver->function) > PAGE_SIZE)
+		return 0;
+	return snprintf (buf, PAGE_SIZE, "%s\n", udc->driver->function);
+}
+
+DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Driver registration/unregistration.
+ *
+ * This is basically hardware-specific; there's usually only one real USB
+ * device (not host) controller since that's how USB devices are intended
+ * to work.  So most implementations of these api calls will rely on the
+ * fact that only one driver will ever bind to the hardware.  But curious
+ * hardware can be built with discrete components, so the gadget API doesn't
+ * require that assumption.
+ */
+
+
+#if 0
+
+static void
+s3c2410_udc_release (struct device *dev)
+{
+	struct s3c2410_udc *udc = gadget_dev_to_udc (dev);
+
+	complete (&udc->released);
+}
+
+
+static int
+s3c2410_register_udc (struct s3c2410_udc *udc)
+{
+	int		rc;
+
+	strcpy (udc->gadget.dev.bus_id, "udc");
+	udc->gadget.dev.parent = &udc->pdev.dev;
+	udc->gadget.dev.release = s3c2410_udc_release;
+
+	rc = device_register (&udc->gadget.dev);
+	if (rc == 0)
+		device_create_file (&udc->gadget.dev, &dev_attr_function);
+	return rc;
+}
+
+static void
+s3c2410_unregister_udc (struct s3c2410_udc *udc)
+{
+	device_remove_file (&udc->gadget.dev, &dev_attr_function);
+	init_completion (&udc->released);
+	device_unregister (&udc->gadget.dev);
+	wait_for_completion (&udc->released);
+}
+
+#endif
+
+int
+usb_gadget_register_driver (struct usb_gadget_driver *driver)
+{
+	struct s3c2410_udc *udc = the_controller;
+	int		retval, i;
+
+	if (!udc)
+		return -EINVAL;
+	if (udc->driver)
+		return -EBUSY;
+	if (!driver->bind || !driver->unbind || !driver->setup
+			|| driver->speed == USB_SPEED_UNKNOWN)
+		return -EINVAL;
+
+	/*
+	 * SLAVE side init ... the layer above hardware, which
+	 * can't enumerate without help from the driver we're binding.
+	 */
+	udc->gadget.name = gadget_name;
+	udc->gadget.ops = &s3c2410_ops;
+	udc->gadget.is_dualspeed = 1;
+
+	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->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;
+		ep->ep.maxpacket = ~0;
+		ep->last_io = jiffies;
+		ep->gadget = &udc->gadget;
+		ep->desc = 0;
+		INIT_LIST_HEAD (&ep->queue);
+	}
+
+	udc->gadget.ep0 = &udc->ep[0].ep;
+	udc->ep[0].ep.maxpacket = 64;
+	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;
+	}
+
+	// FIXME: Check these calls for errors and re-order
+//	driver->driver.bus = udc->pdev.dev.bus;
+	driver_register (&driver->driver);
+
+	device_bind_driver (&udc->gadget.dev);
+
+	/* khubd will enumerate this in a while */
+	udc->port_status |= USB_PORT_STAT_CONNECTION
+		| (1 << USB_PORT_FEAT_C_CONNECTION);
+	return 0;
+}
+
+EXPORT_SYMBOL (usb_gadget_register_driver);
+
+
+/* caller must hold lock */
+static void
+stop_activity (struct s3c2410_udc *udc, struct usb_gadget_driver *driver)
+{
+	struct s3c2410_ep *ep;
+
+	/* prevent any more requests */
+	udc->address = 0;
+
+	/* The timer is left running so that outstanding URBs can fail */
+
+	/* nuke any pending requests first, so driver i/o is quiesced */
+	list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list)
+		nuke (udc, ep);
+
+	/* driver now does any non-usb quiescing necessary */
+	if (driver) {
+		spin_unlock (&udc->lock);
+		driver->disconnect (&udc->gadget);
+		spin_lock (&udc->lock);
+	}
+}
+
+int
+usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
+{
+	struct s3c2410_udc *udc = the_controller;
+	unsigned long	flags;
+
+	if (!udc)
+		return -ENODEV;
+	if (!driver || driver != udc->driver)
+		return -EINVAL;
+
+	dprintk( "unregister gadget driver '%s'\n",
+			driver->driver.name);
+
+	spin_lock_irqsave (&udc->lock, flags);
+	stop_activity (udc, driver);
+	udc->port_status &= ~USB_PORT_STAT_CONNECTION;
+	udc->port_status |= (1 << USB_PORT_FEAT_C_CONNECTION);
+	spin_unlock_irqrestore (&udc->lock, flags);
+
+	driver->unbind (&udc->gadget);
+	udc->driver = 0;
+
+	device_release_driver (&udc->gadget.dev);
+
+	driver_unregister (&driver->driver);
+
+	return 0;
+}
+
+EXPORT_SYMBOL (usb_gadget_unregister_driver);
+
+#undef is_enabled
+
+
+
+int s3c2410_set_fifo_mode (struct usb_gadget *gadget, int mode)
+{
+	return -ENOSYS;
+}
+
+EXPORT_SYMBOL (s3c2410_set_fifo_mode);
+
+
+
+
+/*-------------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+
+static void nop_release (struct device *dev)
+{
+	dprintk("%s %s\n", __FUNCTION__, dev->bus_id);
+}
+
+
+#define EP0_FIFO_SIZE	16
+#define BULK_FIFO_SIZE	64
+
+
+/* this uses load-time allocation and initialization (instead of
+ * doing it at run-time) to save code, eliminate fault paths, and
+ * be more obviously correct.
+ */
+static struct s3c2410_udc memory = {
+	.gadget = {
+		.ops		= &s3c2410_ops,
+		.ep0		= &memory.ep[0].ep,
+		.name		= gadget_name,
+		.dev = {
+			.bus_id		= "gadget",
+			.release	= nop_release,
+		},
+	},
+
+	/* control endpoint */
+	.ep[0] = {
+		.ep = {
+			.name		= ep0name,
+			.ops		= &s3c2410_ep_ops,
+			.maxpacket	= EP0_FIFO_SIZE,
+		},
+		.dev		= &memory,
+//		.reg_udccs	= &UDCCS0,
+//		.reg_uddr	= &UDDR0,
+	},
+
+	/* group of endpoints */
+	.ep[1] = {
+		.ep = {
+			.name		= "ep1-bulk",
+			.ops		= &s3c2410_ep_ops,
+			.maxpacket	= BULK_FIFO_SIZE,
+		},
+		.dev		= &memory,
+		.fifo_size	= BULK_FIFO_SIZE,
+		.bEndpointAddress = 1,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+//		.reg_udccs	= &UDCCS1,
+//		.reg_uddr	= &UDDR1,
+//		drcmr (25)
+	},
+	.ep[2] = {
+		.ep = {
+			.name		= "ep2-bulk",
+			.ops		= &s3c2410_ep_ops,
+			.maxpacket	= BULK_FIFO_SIZE,
+		},
+		.dev		= &memory,
+		.fifo_size	= BULK_FIFO_SIZE,
+		.bEndpointAddress = 2,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+//		.reg_udccs	= &UDCCS2,
+//		.reg_ubcr	= &UBCR2,
+//		.reg_uddr	= &UDDR2,
+//		drcmr (26)
+	},
+	.ep[3] = {
+		.ep = {
+			.name		= "ep3-bulk",
+			.ops		= &s3c2410_ep_ops,
+			.maxpacket	= BULK_FIFO_SIZE,
+		},
+		.dev		= &memory,
+		.fifo_size	= BULK_FIFO_SIZE,
+		.bEndpointAddress = 3,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+//		.reg_udccs	= &UDCCS3,
+//		.reg_uddr	= &UDDR3,
+//		drcmr (27)
+	},
+};
+
+
+
+
+/*
+ *	s3c2410_udc_irq - interrupt handler
+ *
+ * avoid delays in ep0 processing. the control handshaking isn't always
+ * under software control, and delays could cause usb protocol errors.
+ */
+static irqreturn_t
+s3c2410_udc_irq(int irq, void *_dev, struct pt_regs *r)
+{
+	struct s3c2410_udc	*dev = _dev;
+	static int sb_debug_cnt = 1;	
+	int handled;
+
+	dprintk("s3c2410_udc_irq\n");
+
+	do {
+		int saveIdx = __raw_readl(S3C2410_UDC_INDEX_REG);
+		int usb_status = __raw_readl(S3C2410_UDC_USB_INT_REG);
+		int usbd_status = __raw_readl(S3C2410_UDC_EP_INT_REG);
+	
+		handled = 0;
+	
+		dprintk("usb_status = 0x%02x, usbd_status = 0x%02x\n",
+				usb_status, usbd_status);
+				
+		/* ReSeT Interrupt Request - USB reset */
+		if (usb_status & S3C2410_UDC_USBINT_RESET) {
+
+			dprintk("USB reset\n");
+			
+			/* clear interrupt */
+			__raw_writel(S3C2410_UDC_USBINT_RESET,
+				S3C2410_UDC_USB_INT_REG);
+
+			dev->gadget.speed = USB_SPEED_FULL;
+			// memset(&dev->stats, 0, sizeof dev->stats);
+			/* driver and endpoints are still reset */
+			// enable_disconnect_irq();
+			handled = 1;
+		}
+
+		/* RESume Interrupt Request */
+		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
+				/* && is_usb_connected() */)
+				dev->driver->resume(&dev->gadget);
+			handled = 1;
+		}
+
+
+		/* SUSpend Interrupt Request */
+		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 (!is_usb_connected())
+				stop_activity(dev, dev->driver);
+			/* else */ if (dev->gadget.speed != USB_SPEED_UNKNOWN
+					&& dev->driver
+					&& dev->driver->suspend)
+				dev->driver->suspend(&dev->gadget);
+			// ep0_idle (dev);
+			handled = 1;
+		}
+
+		// UD_INDEX= saveIdx; /* restore idx */
+
+	} while (handled);
+	return IRQ_HANDLED;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+ *	probe - binds to the platform device
+ */
+static int __init s3c2410_udc_probe(struct device *_dev)
+{
+	struct s3c2410_udc *udc = &memory;
+	int retval, out_dma = 0;
+
+	dprintk("s3c2410_udc_probe\n");
+
+	/* other non-static parts of init */
+//	udc->dev = _dev;
+//	udc->mach = _dev->platform_data;
+
+/*
+	init_timer(&dev->timer);
+	dev->timer.function = udc_watchdog;
+	dev->timer.data = (unsigned long) dev;
+*/
+	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);
+
+	// udc_disable(udc);
+	// udc_reinit(udc);
+
+	/* 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;
+
+//	create_proc_files();
+
+	return 0;
+}
+
+static int __exit s3c2410_udc_remove(struct device *_dev)
+{
+	struct s3c2410_udc *udc = _dev->driver_data;
+
+	dprintk("s3c2410_udc_remove\n");
+
+	udc_disable(udc);
+//	remove_proc_files();
+	usb_gadget_unregister_driver(udc->driver);
+
+	if (udc->got_irq) {
+		free_irq(IRQ_USBD, udc);
+		udc->got_irq = 0;
+	}
+
+	dev_set_drvdata(_dev, 0);
+	the_controller = 0;
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct device_driver udc_driver = {
+	.name		= "s3c2410-udc",
+	.bus		= &platform_bus_type,
+	.probe		= s3c2410_udc_probe,
+	.remove		= __exit_p(s3c2410_udc_remove),
+
+	// FIXME power management support
+	// .suspend = ... disable UDC
+	// .resume = ... re-enable UDC
+};
+
+static int __init udc_init(void)
+{
+	int tmp;
+	
+	dprintk("%s: version %s\n", gadget_name, DRIVER_VERSION);
+	
+	tmp = __raw_readl(S3C2410_CLKCON);
+	tmp &= ~S3C2410_CLKCON_USBD;
+	__raw_writel(tmp, S3C2410_CLKCON);
+
+	tmp = __raw_readl(S3C2410_MISCCR);
+	tmp &= ~S3C2410_MISCCR_USBHOST;
+	__raw_writel(tmp, S3C2410_MISCCR);
+	
+	/* UPLLCON */
+	tmp = (0x78 << S3C2410_PLLCON_MDIVSHIFT)
+		| (0x02 << S3C2410_PLLCON_PDIVSHIFT)
+		| (0x03 << S3C2410_PLLCON_SDIVSHIFT);
+	__raw_writel(tmp, S3C2410_UPLLCON);
+	
+	tmp = __raw_readl(S3C2410_CLKCON);
+	tmp |= S3C2410_CLKCON_USBD;
+	__raw_writel(tmp, S3C2410_CLKCON);
+
+	mdelay(10);
+	
+	return driver_register(&udc_driver);
+}
+module_init(udc_init);
+
+static void __exit udc_exit(void)
+{
+	driver_unregister(&udc_driver);
+}
+module_exit(udc_exit);
+
+
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_AUTHOR ("Herbert Pötzl");
+MODULE_LICENSE ("GPL");
+
diff -NurpP --minimal linux-2.6.8-rc2-h1940-0.03/drivers/usb/gadget/serial.c linux-2.6.8-rc2-h1940-0.04/drivers/usb/gadget/serial.c
--- linux-2.6.8-rc2-h1940-0.03/drivers/usb/gadget/serial.c	2004-07-23 18:10:09.000000000 +0200
+++ linux-2.6.8-rc2-h1940-0.04/drivers/usb/gadget/serial.c	2004-07-30 19:57:22.000000000 +0200
@@ -249,6 +249,23 @@ static const char EP_IN_NAME[] =	"ep1in-
 #define hw_optimize(g)			do {} while (0)
 #endif
 
+/*
+ * S3C2410 UDC:  used in iPAQ H1940 PDAs.
+ */
+#ifdef	CONFIG_USB_GADGET_S3C2410
+#define CHIP				"s3c2410"
+#define EP0_MAXPACKET			16
+static const char EP_OUT_NAME[] =	"ep2-bulk";
+#define EP_OUT_NUM			2
+static const char EP_IN_NAME[] =	"ep1-bulk";
+#define EP_IN_NUM			1
+#define SELFPOWER			USB_CONFIG_ATT_SELFPOWER
+
+/* no hw optimizations to apply */
+#define hw_optimize(g)			do {} while (0)
+#endif
+
+
 #ifdef	CONFIG_USB_GADGET_OMAP
 #define CHIP			"omap"
 #define EP0_MAXPACKET			64
diff -NurpP --minimal linux-2.6.8-rc2-h1940-0.03/include/asm-arm/arch/regs-usb.h linux-2.6.8-rc2-h1940-0.04/include/asm-arm/arch/regs-usb.h
--- linux-2.6.8-rc2-h1940-0.03/include/asm-arm/arch/regs-usb.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.8-rc2-h1940-0.04/include/asm-arm/arch/regs-usb.h	2004-07-30 19:42:48.000000000 +0200
@@ -0,0 +1,150 @@
+#ifndef __ASM_ARCH_REGS_USB_H
+#define __ASM_ARCH_REGS_USB_H
+
+#define S3C2410_USBHREG(x) ((x) + S3C2410_VA_USBHOST)
+
+#define S3C2410_UHC_REVISION		S3C2410_USBHREG(0x00)
+#define S3C2410_UHC_CONTROL		S3C2410_USBHREG(0x04)
+#define S3C2410_UHC_COMMON_STATUS	S3C2410_USBHREG(0x08)
+#define S3C2410_UHC_INTERRUPT_STATUS	S3C2410_USBHREG(0x0c)
+#define S3C2410_UHC_INTERRUPT_ENABLE	S3C2410_USBHREG(0x10)
+#define S3C2410_UHC_INTERRUPT_DISABLE	S3C2410_USBHREG(0x14)
+
+#define S3C2410_UHC_HCCA		S3C2410_USBHREG(0x18)
+
+
+
+
+#define S3C2410_USBDREG(x) ((x) + S3C2410_VA_USBDEV)
+
+#define S3C2410_UDC_FUNC_ADDR_REG	S3C2410_USBDREG(0x0140)
+#define S3C2410_UDC_PWR_REG		S3C2410_USBDREG(0x0144)
+#define S3C2410_UDC_EP_INT_REG		S3C2410_USBDREG(0x0148)
+
+#define S3C2410_UDC_USB_INT_REG	S3C2410_USBDREG(0x0158)
+#define S3C2410_UDC_EP_INT_EN_REG	S3C2410_USBDREG(0x015c)
+
+#define S3C2410_UDC_USB_INT_EN_REG	S3C2410_USBDREG(0x016c)
+
+#define S3C2410_UDC_FRAME_NUM1_REG	S3C2410_USBDREG(0x0170)
+#define S3C2410_UDC_FRAME_NUM2_REG	S3C2410_USBDREG(0x0174)
+
+#define S3C2410_UDC_EP0_FIFO_REG	S3C2410_USBDREG(0x01c0)
+#define S3C2410_UDC_EP1_FIFO_REG	S3C2410_USBDREG(0x01c4)
+#define S3C2410_UDC_EP2_FIFO_REG	S3C2410_USBDREG(0x01c8)
+#define S3C2410_UDC_EP3_FIFO_REG	S3C2410_USBDREG(0x01cc)
+#define S3C2410_UDC_EP4_FIFO_REG	S3C2410_USBDREG(0x01d0)
+
+#define S3C2410_UDC_EP1_DMA_CON	S3C2410_USBDREG(0x0200)
+#define S3C2410_UDC_EP1_DMA_UNIT	S3C2410_USBDREG(0x0204)
+#define S3C2410_UDC_EP1_DMA_FIFO	S3C2410_USBDREG(0x0208)
+#define S3C2410_UDC_EP1_DMA_TTC_L	S3C2410_USBDREG(0x020c)
+#define S3C2410_UDC_EP1_DMA_TTC_M	S3C2410_USBDREG(0x0210)
+#define S3C2410_UDC_EP1_DMA_TTC_H	S3C2410_USBDREG(0x0214)
+
+#define S3C2410_UDC_EP2_DMA_CON	S3C2410_USBDREG(0x0218)
+#define S3C2410_UDC_EP2_DMA_UNIT	S3C2410_USBDREG(0x021c)
+#define S3C2410_UDC_EP2_DMA_FIFO	S3C2410_USBDREG(0x0220)
+#define S3C2410_UDC_EP2_DMA_TTC_L	S3C2410_USBDREG(0x0224)
+#define S3C2410_UDC_EP2_DMA_TTC_M	S3C2410_USBDREG(0x0228)
+#define S3C2410_UDC_EP2_DMA_TTC_H	S3C2410_USBDREG(0x022c)
+
+#define S3C2410_UDC_EP3_DMA_CON	S3C2410_USBDREG(0x0240)
+#define S3C2410_UDC_EP3_DMA_UNIT	S3C2410_USBDREG(0x0244)
+#define S3C2410_UDC_EP3_DMA_FIFO	S3C2410_USBDREG(0x0248)
+#define S3C2410_UDC_EP3_DMA_TTC_L	S3C2410_USBDREG(0x024c)
+#define S3C2410_UDC_EP3_DMA_TTC_M	S3C2410_USBDREG(0x0250)
+#define S3C2410_UDC_EP3_DMA_TTC_H	S3C2410_USBDREG(0x0254)
+
+#define S3C2410_UDC_EP4_DMA_CON	S3C2410_USBDREG(0x0258)
+#define S3C2410_UDC_EP4_DMA_UNIT	S3C2410_USBDREG(0x025c)
+#define S3C2410_UDC_EP4_DMA_FIFO	S3C2410_USBDREG(0x0260)
+#define S3C2410_UDC_EP4_DMA_TTC_L	S3C2410_USBDREG(0x0264)
+#define S3C2410_UDC_EP4_DMA_TTC_M	S3C2410_USBDREG(0x0268)
+#define S3C2410_UDC_EP4_DMA_TTC_H	S3C2410_USBDREG(0x026c)
+
+#define S3C2410_UDC_INDEX_REG		S3C2410_USBDREG(0x0178)
+
+/* indexed registers */
+
+#define S3C2410_UDC_MAXP_REG		S3C2410_USBDREG(0x018c)
+
+#define S3C2410_UDC_IN_CSR1_REG		S3C2410_USBDREG(0x0184)
+#define S3C2410_UDC_IN_CSR2_REG		S3C2410_USBDREG(0x0188)
+
+#define S3C2410_UDC_OUT_CSR1_REG	S3C2410_USBDREG(0x0190)
+#define S3C2410_UDC_OUT_CSR2_REG	S3C2410_USBDREG(0x0194)
+#define S3C2410_UDC_OUT_FIFO_CNT1_REG	S3C2410_USBDREG(0x0198)
+#define S3C2410_UDC_OUT_FIFO_CNT2_REG	S3C2410_USBDREG(0x019c)
+
+
+
+#define S3C2410_UDC_PWR_ISOUP		(1<<7) // R/W
+#define S3C2410_UDC_PWR_RESET		(1<<3) // R
+#define S3C2410_UDC_PWR_RESUME		(1<<2) // R/W
+#define S3C2410_UDC_PWR_SUSPEND		(1<<1) // R
+#define S3C2410_UDC_PWR_ENSUSPEND	(1<<0) // R/W
+
+#define S3C2410_UDC_PWR_DEFAULT		0x00
+
+#define S3C2410_UDC_INT_EP4		(1<<4) // R/W (clear only)
+#define S3C2410_UDC_INT_EP3		(1<<3) // R/W (clear only)
+#define S3C2410_UDC_INT_EP2		(1<<2) // R/W (clear only)
+#define S3C2410_UDC_INT_EP1		(1<<1) // R/W (clear only)
+#define S3C2410_UDC_INT_EP0		(1<<0) // R/W (clear only)
+
+#define S3C2410_UDC_USBINT_RESET	(1<<2) // R/W (clear only)
+#define S3C2410_UDC_USBINT_RESUME	(1<<1) // R/W (clear only)
+#define S3C2410_UDC_USBINT_SUSPEND	(1<<0) // R/W (clear only)
+
+#define S3C2410_UDC_INTE_EP4		(1<<4) // R/W
+#define S3C2410_UDC_INTE_EP3		(1<<3) // R/W
+#define S3C2410_UDC_INTE_EP2		(1<<2) // R/W
+#define S3C2410_UDC_INTE_EP1		(1<<1) // R/W
+#define S3C2410_UDC_INTE_EP0		(1<<0) // R/W
+
+#define S3C2410_UDC_USBINTE_RESET	(1<<2) // R/W
+#define S3C2410_UDC_USBINTE_SUSPEND	(1<<0) // R/W
+
+/*
+#define fUD_FRAMEL_NUM	Fld(8,0) // R
+#define UD_FRAMEL_NUM	FMsk(fUD_FRAMEL_NUM)
+
+#define fUD_FRAMEH_NUM	Fld(8,0) // R
+#define UD_FRAMEH_NUM	FMsk(fUD_FRAMEH_NUM)
+*/
+
+#define S3C2410_UDC_INDEX_EP0		(0x00)
+#define S3C2410_UDC_INDEX_EP1		(0x01) // ??
+#define S3C2410_UDC_INDEX_EP2		(0x02) // ??
+#define S3C2410_UDC_INDEX_EP3		(0x03) // ??
+#define S3C2410_UDC_INDEX_EP4		(0x04) // ??
+
+#define S3C2410_UDC_ICSR1_CLRDT	(1<<6) // R/W
+#define S3C2410_UDC_ICSR1_SENTSTL	(1<<5) // R/W (clear only)
+#define S3C2410_UDC_ICSR1_SENDSTL	(1<<4) // R/W
+#define S3C2410_UDC_ICSR1_FFLUSH	(1<<3) // W	    (set only)
+#define S3C2410_UDC_ICSR1_UNDRUN	(1<<2) // R/W (clear only)
+#define S3C2410_UDC_ICSR1_PKTRDY	(1<<0) // R/W (set only)
+
+#define S3C2410_UDC_ICSR2_AUTOSET	(1<<7) // R/W
+#define S3C2410_UDC_ICSR2_ISO		(1<<6) // R/W
+#define S3C2410_UDC_ICSR2_MODEIN	(1<<5) // R/W
+#define S3C2410_UDC_ICSR2_DMAIEN	(1<<4) // R/W
+
+#define S3C2410_UDC_OCSR1_CLRDT		(1<<7) // R/W
+#define S3C2410_UDC_OCSR1_SENTSTL	(1<<6) // R/W (clear only)
+#define S3C2410_UDC_OCSR1_SENDSTL	(1<<5) // R/W
+#define S3C2410_UDC_OCSR1_FFLUSH	(1<<4) // R/W
+#define S3C2410_UDC_OCSR1_DERROR	(1<<3) // R
+#define S3C2410_UDC_OCSR1_OVRRUN	(1<<2) // R/W (clear only)
+#define S3C2410_UDC_OCSR1_PKTRDY	(1<<0) // R/W (clear only)
+
+#define S3C2410_UDC_OCSR2_AUTOCLR	(1<<7) // R/W
+#define S3C2410_UDC_OCSR2_ISO		(1<<6) // R/W
+#define S3C2410_UDC_OCSR2_DMAIEN	(1<<5) // R/W
+
+
+#endif
+
+
