## Automatically generated incremental diff
## From:   linux-2.4.22-bk28
## To:     linux-2.4.22-bk29
## Robot:  $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $

diff -urN linux-2.4.22-bk28/CREDITS linux-2.4.22-bk29/CREDITS
--- linux-2.4.22-bk28/CREDITS	2003-10-06 02:50:22.000000000 -0700
+++ linux-2.4.22-bk29/CREDITS	2003-10-06 02:50:30.000000000 -0700
@@ -2719,7 +2719,7 @@
 D: Dosemu
 
 N: Duncan Sands
-E: duncan.sands@wanadoo.fr
+E: duncan.sands@free.fr
 W: http://topo.math.u-psud.fr/~sands
 D: Alcatel SpeedTouch USB driver
 S: 69 rue Dunois
diff -urN linux-2.4.22-bk28/Documentation/Configure.help linux-2.4.22-bk29/Documentation/Configure.help
--- linux-2.4.22-bk28/Documentation/Configure.help	2003-10-06 02:50:22.000000000 -0700
+++ linux-2.4.22-bk29/Documentation/Configure.help	2003-10-06 02:50:30.000000000 -0700
@@ -14676,8 +14676,8 @@
 USB HIDBP Mouse (basic) support
 CONFIG_USB_MOUSE
   Say Y here only if you are absolutely sure that you don't want
-  to use the generic HID driver for your USB keyboard and prefer
-  to use the keyboard in its limited Boot Protocol mode instead.
+  to use the generic HID driver for your USB mouse and prefer
+  to use the mouse in its limited Boot Protocol mode instead.
 
   This is almost certainly not what you want.
 
@@ -15037,6 +15037,9 @@
 CONFIG_USB_SERIAL_KEYSPAN_USA49W
   Say Y here to include firmware for the USA-49W converter.
 
+CONFIG_USB_SERIAL_KEYSPAN_USA49WLC
+  Say Y here to include firmware for the USA-49WLC converter.
+
 USB ZyXEL omni.net LCD Plus Driver
 CONFIG_USB_SERIAL_OMNINET
   Say Y here if you want to use a ZyXEL omni.net LCD ISDN TA.
diff -urN linux-2.4.22-bk28/MAINTAINERS linux-2.4.22-bk29/MAINTAINERS
--- linux-2.4.22-bk28/MAINTAINERS	2003-10-06 02:50:22.000000000 -0700
+++ linux-2.4.22-bk29/MAINTAINERS	2003-10-06 02:50:30.000000000 -0700
@@ -202,7 +202,7 @@
 
 ALCATEL SPEEDTOUCH USB DRIVER
 P:	Duncan Sands
-M:	duncan.sands@wanadoo.fr
+M:	duncan.sands@free.fr
 L:	linux-usb-users@lists.sourceforge.net
 L:	linux-usb-devel@lists.sourceforge.net
 W:	http://www.linux-usb.org/SpeedTouch/
diff -urN linux-2.4.22-bk28/Makefile linux-2.4.22-bk29/Makefile
--- linux-2.4.22-bk28/Makefile	2003-10-06 02:50:22.000000000 -0700
+++ linux-2.4.22-bk29/Makefile	2003-10-06 02:50:30.000000000 -0700
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 22
-EXTRAVERSION = -bk28
+EXTRAVERSION = -bk29
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
@@ -181,6 +181,7 @@
 DRIVERS-$(CONFIG_HAMRADIO) += drivers/net/hamradio/hamradio.o
 DRIVERS-$(CONFIG_TC) += drivers/tc/tc.a
 DRIVERS-$(CONFIG_USB) += drivers/usb/usbdrv.o
+DRIVERS-$(CONFIG_USB_GADGET) += drivers/usb/gadget/built-in.o
 DRIVERS-y +=drivers/media/media.o
 DRIVERS-$(CONFIG_INPUT) += drivers/input/inputdrv.o
 DRIVERS-$(CONFIG_HIL) += drivers/hil/hil.o
diff -urN linux-2.4.22-bk28/arch/i386/lib/checksum.S linux-2.4.22-bk29/arch/i386/lib/checksum.S
--- linux-2.4.22-bk28/arch/i386/lib/checksum.S	2002-11-28 15:53:09.000000000 -0800
+++ linux-2.4.22-bk29/arch/i386/lib/checksum.S	2003-10-06 02:50:30.000000000 -0700
@@ -48,6 +48,9 @@
 	   * least a twofold speedup on 486 and Pentium if it is 4-byte aligned.
 	   * Fortunately, it is easy to convert 2-byte alignment to 4-byte
 	   * alignment for the unrolled loop.
+	   *
+	   * Danger, Will Robinson: with sendfile 2 byte alignment is not guaranteed.
+	   *
 	   */		
 csum_partial:	
 	pushl %esi
@@ -237,18 +240,37 @@
 	movl $0xffffff,%ebx	# by the shll and shrl instructions
 	shll $3,%ecx
 	shrl %cl,%ebx
-	andl -128(%esi),%ebx	# esi is 4-aligned so should be ok
+tail:	
+	andl -128(%esi),%ebx
+tail_finished:
 	addl %ebx,%eax
 	adcl $0,%eax
 80: 
 	testl $1, 12(%esp)
 	jz 90f
 	roll $8, %eax
-90: 
+90:  
 	popl %ebx
 	popl %esi
 	ret
-				
+	
+	.section __ex_table,"a"
+	.long tail,tail_recover
+	.long tail_byte3,tail_byte1
+	.long tail_byte2,tail_finished
+	.previous
+		
+tail_recover:	
+	xorl %ebx,%ebx
+tail_byte3:	
+	movb -126(%esi),%bl
+	shl  $16,%ebx
+tail_byte1:			
+	movb -128(%esi),%bl
+tail_byte2:	
+	movb -127(%esi),%bh
+	jmp tail_finished
+					
 #endif
 
 /*
diff -urN linux-2.4.22-bk28/arch/ia64/Makefile linux-2.4.22-bk29/arch/ia64/Makefile
--- linux-2.4.22-bk28/arch/ia64/Makefile	2003-10-06 02:50:23.000000000 -0700
+++ linux-2.4.22-bk29/arch/ia64/Makefile	2003-10-06 02:50:30.000000000 -0700
@@ -62,9 +62,11 @@
 
 ifdef CONFIG_IA64_HP_SIM
         SUBDIRS         :=      arch/$(ARCH)/hp		\
+                                arch/$(ARCH)/drivers	\
                                 $(SUBDIRS)
         CORE_FILES      :=      arch/$(ARCH)/hp/hp.o	\
                                 $(CORE_FILES)
+        DRIVERS         +=      arch/$(ARCH)/drivers/drivers.o
 endif
 
 ifdef CONFIG_IA64_HP_ZX1
diff -urN linux-2.4.22-bk28/arch/ia64/drivers/Makefile linux-2.4.22-bk29/arch/ia64/drivers/Makefile
--- linux-2.4.22-bk28/arch/ia64/drivers/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-bk29/arch/ia64/drivers/Makefile	2003-10-06 02:50:30.000000000 -0700
@@ -0,0 +1,13 @@
+#
+# ia64/drivers/Makefile
+#
+# Copyright (C) 2003 Hewlett-Packard Co.
+#	Bjorn Helgaas <bjorn.helgaas@hp.com>
+
+O_TARGET := drivers.o
+
+obj-$(CONFIG_HP_SIMETH) += simeth.o
+obj-$(CONFIG_HP_SIMSERIAL) += simserial.o
+obj-$(CONFIG_HP_SIMSCSI) += simscsi.o
+
+include $(TOPDIR)/Rules.make
diff -urN linux-2.4.22-bk28/arch/ia64/drivers/simeth.c linux-2.4.22-bk29/arch/ia64/drivers/simeth.c
--- linux-2.4.22-bk28/arch/ia64/drivers/simeth.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-bk29/arch/ia64/drivers/simeth.c	2003-10-06 02:50:30.000000000 -0700
@@ -0,0 +1,537 @@
+/*
+ * Simulated Ethernet Driver
+ *
+ * Copyright (C) 1999-2001, 2003 Hewlett-Packard Co
+ *	Stephane Eranian <eranian@hpl.hp.com>
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/notifier.h>
+#include <asm/bitops.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+
+#define SIMETH_RECV_MAX	10
+
+/*
+ * Maximum possible received frame for Ethernet.
+ * We preallocate an sk_buff of that size to avoid costly
+ * memcpy for temporary buffer into sk_buff. We do basically
+ * what's done in other drivers, like eepro with a ring.
+ * The difference is, of course, that we don't have real DMA !!!
+ */
+#define SIMETH_FRAME_SIZE	ETH_FRAME_LEN
+
+
+#define SSC_NETDEV_PROBE		100
+#define SSC_NETDEV_SEND			101
+#define SSC_NETDEV_RECV			102
+#define SSC_NETDEV_ATTACH		103
+#define SSC_NETDEV_DETACH		104
+
+#define NETWORK_INTR			8
+
+struct simeth_local {
+	struct net_device_stats stats;
+	int 			simfd;	 /* descriptor in the simulator */
+};
+
+static int simeth_probe1(void);
+static int simeth_open(struct net_device *dev);
+static int simeth_close(struct net_device *dev);
+static int simeth_tx(struct sk_buff *skb, struct net_device *dev);
+static int simeth_rx(struct net_device *dev);
+static struct net_device_stats *simeth_get_stats(struct net_device *dev);
+static void simeth_interrupt(int irq, void *dev_id, struct pt_regs * regs);
+static void set_multicast_list(struct net_device *dev);
+static int simeth_device_event(struct notifier_block *this,unsigned long event, void *ptr);
+
+static char *simeth_version="0.3";
+
+/*
+ * This variable is used to establish a mapping between the Linux/ia64 kernel
+ * and the host linux kernel.
+ *
+ * As of today, we support only one card, even though most of the code
+ * is ready for many more. The mapping is then:
+ *	linux/ia64 -> linux/x86
+ * 	   eth0    -> eth1
+ *
+ * In the future, we some string operations, we could easily support up
+ * to 10 cards (0-9).
+ *
+ * The default mapping can be changed on the kernel command line by
+ * specifying simeth=ethX (or whatever string you want).
+ */
+static char *simeth_device="eth0";	 /* default host interface to use */
+
+
+
+static volatile unsigned int card_count; /* how many cards "found" so far */
+static int simeth_debug;		/* set to 1 to get debug information */
+
+/*
+ * Used to catch IFF_UP & IFF_DOWN events
+ */
+static struct notifier_block simeth_dev_notifier = {
+	simeth_device_event,
+	0
+};
+
+
+/*
+ * Function used when using a kernel command line option.
+ *
+ * Format: simeth=interface_name (like eth0)
+ */
+static int __init
+simeth_setup(char *str)
+{
+	simeth_device = str;
+	return 1;
+}
+
+__setup("simeth=", simeth_setup);
+
+/*
+ * Function used to probe for simeth devices when not installed
+ * as a loadable module
+ */
+
+int __init
+simeth_probe (void)
+{
+	int r;
+
+	printk(KERN_INFO "simeth: v%s\n", simeth_version);
+
+	r = simeth_probe1();
+
+	if (r == 0) register_netdevice_notifier(&simeth_dev_notifier);
+
+	return r;
+}
+
+extern long ia64_ssc (long, long, long, long, int);
+extern void ia64_ssc_connect_irq (long intr, long irq);
+
+static inline int
+netdev_probe(char *name, unsigned char *ether)
+{
+	return ia64_ssc(__pa(name), __pa(ether), 0,0, SSC_NETDEV_PROBE);
+}
+
+
+static inline int
+netdev_connect(int irq)
+{
+	/* XXX Fix me
+	 * this does not support multiple cards
+	 * also no return value
+	 */
+	ia64_ssc_connect_irq(NETWORK_INTR, irq);
+	return 0;
+}
+
+static inline int
+netdev_attach(int fd, int irq, unsigned int ipaddr)
+{
+	/* this puts the host interface in the right mode (start interrupting) */
+	return ia64_ssc(fd, ipaddr, 0,0, SSC_NETDEV_ATTACH);
+}
+
+
+static inline int
+netdev_detach(int fd)
+{
+	/*
+	 * inactivate the host interface (don't interrupt anymore) */
+	return ia64_ssc(fd, 0,0,0, SSC_NETDEV_DETACH);
+}
+
+static inline int
+netdev_send(int fd, unsigned char *buf, unsigned int len)
+{
+	return ia64_ssc(fd, __pa(buf), len, 0, SSC_NETDEV_SEND);
+}
+
+static inline int
+netdev_read(int fd, unsigned char *buf, unsigned int len)
+{
+	return ia64_ssc(fd, __pa(buf), len, 0, SSC_NETDEV_RECV);
+}
+
+/*
+ * Function shared with module code, so cannot be in init section
+ *
+ * So far this function "detects" only one card (test_&_set) but could
+ * be extended easily.
+ *
+ * Return:
+ * 	- -ENODEV is no device found
+ *	- -ENOMEM is no more memory
+ *	- 0 otherwise
+ */
+static int
+simeth_probe1(void)
+{
+	unsigned char mac_addr[ETH_ALEN];
+	struct simeth_local *local;
+	struct net_device *dev;
+	int fd, i;
+
+	/*
+	 * XXX Fix me
+	 * let's support just one card for now
+	 */
+	if (test_and_set_bit(0, &card_count))
+		return -ENODEV;
+
+	/*
+	 * check with the simulator for the device
+	 */
+	fd = netdev_probe(simeth_device, mac_addr);
+	if (fd == -1)
+		return -ENODEV;
+
+	dev = init_etherdev(NULL, sizeof(struct simeth_local));
+	if (!dev)
+		return -ENOMEM;
+
+	memcpy(dev->dev_addr, mac_addr, sizeof(mac_addr));
+
+	dev->irq = ia64_alloc_vector();
+
+	/*
+	 * attach the interrupt in the simulator, this does enable interrupts
+	 * until a netdev_attach() is called
+	 */
+	netdev_connect(dev->irq);
+
+	memset(dev->priv, 0, sizeof(struct simeth_local));
+
+	local = dev->priv;
+	local->simfd = fd; /* keep track of underlying file descriptor */
+
+	dev->open		= simeth_open;
+	dev->stop		= simeth_close;
+	dev->hard_start_xmit	= simeth_tx;
+	dev->get_stats		= simeth_get_stats;
+	dev->set_multicast_list = set_multicast_list; /* no yet used */
+
+	/* Fill in the fields of the device structure with ethernet-generic values. */
+	ether_setup(dev);
+
+	printk(KERN_INFO "%s: hosteth=%s simfd=%d, HwAddr",
+	       dev->name, simeth_device, local->simfd);
+	for(i = 0; i < ETH_ALEN; i++) {
+		printk(" %2.2x", dev->dev_addr[i]);
+	}
+	printk(", IRQ %d\n", dev->irq);
+
+	return 0;
+}
+
+/*
+ * actually binds the device to an interrupt vector
+ */
+static int
+simeth_open(struct net_device *dev)
+{
+	if (request_irq(dev->irq, simeth_interrupt, 0, "simeth", dev)) {
+		printk(KERN_WARNING "simeth: unable to get IRQ %d.\n", dev->irq);
+		return -EAGAIN;
+	}
+
+	netif_start_queue(dev);
+
+	return 0;
+}
+
+/* copied from lapbether.c */
+static __inline__ int dev_is_ethdev(struct net_device *dev)
+{
+       return ( dev->type == ARPHRD_ETHER && strncmp(dev->name, "dummy", 5));
+}
+
+
+/*
+ * Handler for IFF_UP or IFF_DOWN
+ *
+ * The reason for that is that we don't want to be interrupted when the
+ * interface is down. There is no way to unconnect in the simualtor. Instead
+ * we use this function to shutdown packet processing in the frame filter
+ * in the simulator. Thus no interrupts are generated
+ *
+ *
+ * That's also the place where we pass the IP address of this device to the
+ * simulator so that that we can start filtering packets for it
+ *
+ * There may be a better way of doing this, but I don't know which yet.
+ */
+static int
+simeth_device_event(struct notifier_block *this,unsigned long event, void *ptr)
+{
+	struct net_device *dev = (struct net_device *)ptr;
+	struct simeth_local *local;
+	struct in_device *in_dev;
+	struct in_ifaddr **ifap = NULL;
+	struct in_ifaddr *ifa = NULL;
+	int r;
+
+
+	if ( ! dev ) {
+		printk(KERN_WARNING "simeth_device_event dev=0\n");
+		return NOTIFY_DONE;
+	}
+
+	if ( event != NETDEV_UP && event != NETDEV_DOWN ) return NOTIFY_DONE;
+
+	/*
+	 * Check whether or not it's for an ethernet device
+	 *
+	 * XXX Fixme: This works only as long as we support one
+	 * type of ethernet device.
+	 */
+	if ( !dev_is_ethdev(dev) ) return NOTIFY_DONE;
+
+	if ((in_dev=dev->ip_ptr) != NULL) {
+		for (ifap=&in_dev->ifa_list; (ifa=*ifap) != NULL; ifap=&ifa->ifa_next)
+			if (strcmp(dev->name, ifa->ifa_label) == 0) break;
+	}
+	if ( ifa == NULL ) {
+		printk(KERN_ERR "simeth_open: can't find device %s's ifa\n", dev->name);
+		return NOTIFY_DONE;
+	}
+
+	printk(KERN_INFO "simeth_device_event: %s ipaddr=0x%x\n",
+	       dev->name, htonl(ifa->ifa_local));
+
+	/*
+	 * XXX Fix me
+	 * if the device was up, and we're simply reconfiguring it, not sure
+	 * we get DOWN then UP.
+	 */
+
+	local = dev->priv;
+	/* now do it for real */
+	r = event == NETDEV_UP ?
+		netdev_attach(local->simfd, dev->irq, htonl(ifa->ifa_local)):
+		netdev_detach(local->simfd);
+
+	printk(KERN_INFO "simeth: netdev_attach/detach: event=%s ->%d\n",
+	       event == NETDEV_UP ? "attach":"detach", r);
+
+	return NOTIFY_DONE;
+}
+
+static int
+simeth_close(struct net_device *dev)
+{
+	netif_stop_queue(dev);
+
+	free_irq(dev->irq, dev);
+
+	return 0;
+}
+
+/*
+ * Only used for debug
+ */
+static void
+frame_print(unsigned char *from, unsigned char *frame, int len)
+{
+	int i;
+
+	printk("%s: (%d) %02x", from, len, frame[0] & 0xff);
+	for(i=1; i < 6; i++ ) {
+		printk(":%02x", frame[i] &0xff);
+	}
+	printk(" %2x", frame[6] &0xff);
+	for(i=7; i < 12; i++ ) {
+		printk(":%02x", frame[i] &0xff);
+	}
+	printk(" [%02x%02x]\n", frame[12], frame[13]);
+
+	for(i=14; i < len; i++ ) {
+		printk("%02x ", frame[i] &0xff);
+		if ( (i%10)==0) printk("\n");
+	}
+	printk("\n");
+}
+
+
+/*
+ * Function used to transmit of frame, very last one on the path before
+ * going to the simulator.
+ */
+static int
+simeth_tx(struct sk_buff *skb, struct net_device *dev)
+{
+	struct simeth_local *local = (struct simeth_local *)dev->priv;
+
+#if 0
+	/* ensure we have at least ETH_ZLEN bytes (min frame size) */
+	unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+	/* Where do the extra padding bytes comes from inthe skbuff ? */
+#else
+	/* the real driver in the host system is going to take care of that
+	 * or maybe it's the NIC itself.
+	 */
+	unsigned int length = skb->len;
+#endif
+
+	local->stats.tx_bytes += skb->len;
+	local->stats.tx_packets++;
+
+
+	if (simeth_debug > 5) frame_print("simeth_tx", skb->data, length);
+
+	netdev_send(local->simfd, skb->data, length);
+
+	/*
+	 * we are synchronous on write, so we don't simulate a
+	 * trasnmit complete interrupt, thus we don't need to arm a tx
+	 */
+
+	dev_kfree_skb(skb);
+	return 0;
+}
+
+static inline struct sk_buff *
+make_new_skb(struct net_device *dev)
+{
+	struct sk_buff *nskb;
+
+	/*
+	 * The +2 is used to make sure that the IP header is nicely
+	 * aligned (on 4byte boundary I assume 14+2=16)
+	 */
+	nskb = dev_alloc_skb(SIMETH_FRAME_SIZE + 2);
+	if ( nskb == NULL ) {
+		printk(KERN_NOTICE "%s: memory squeeze. dropping packet.\n", dev->name);
+		return NULL;
+	}
+	nskb->dev = dev;
+
+	skb_reserve(nskb, 2);	/* Align IP on 16 byte boundaries */
+
+	skb_put(nskb,SIMETH_FRAME_SIZE);
+
+	return nskb;
+}
+
+/*
+ * called from interrupt handler to process a received frame
+ */
+static int
+simeth_rx(struct net_device *dev)
+{
+	struct simeth_local	*local;
+	struct sk_buff		*skb;
+	int			len;
+	int			rcv_count = SIMETH_RECV_MAX;
+
+	local = (struct simeth_local *)dev->priv;
+	/*
+	 * the loop concept has been borrowed from other drivers
+	 * looks to me like it's a throttling thing to avoid pushing to many
+	 * packets at one time into the stack. Making sure we can process them
+	 * upstream and make forward progress overall
+	 */
+	do {
+		if ( (skb=make_new_skb(dev)) == NULL ) {
+			printk(KERN_NOTICE "%s: memory squeeze. dropping packet.\n", dev->name);
+			local->stats.rx_dropped++;
+			return 0;
+		}
+		/*
+		 * Read only one frame at a time
+		 */
+		len = netdev_read(local->simfd, skb->data, SIMETH_FRAME_SIZE);
+		if ( len == 0 ) {
+			if ( simeth_debug > 0 ) printk(KERN_WARNING "%s: count=%d netdev_read=0\n",
+						       dev->name, SIMETH_RECV_MAX-rcv_count);
+			break;
+		}
+#if 0
+		/*
+		 * XXX Fix me
+		 * Should really do a csum+copy here
+		 */
+		memcpy(skb->data, frame, len);
+#endif
+		skb->protocol = eth_type_trans(skb, dev);
+
+		if ( simeth_debug > 6 ) frame_print("simeth_rx", skb->data, len);
+
+		/*
+		 * push the packet up & trigger software interrupt
+		 */
+		netif_rx(skb);
+
+		local->stats.rx_packets++;
+		local->stats.rx_bytes += len;
+
+	} while ( --rcv_count );
+
+	return len; /* 0 = nothing left to read, otherwise, we can try again */
+}
+
+/*
+ * Interrupt handler (Yes, we can do it too !!!)
+ */
+static void
+simeth_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct net_device *dev = dev_id;
+
+	if ( dev == NULL ) {
+		printk(KERN_WARNING "simeth: irq %d for unknown device\n", irq);
+		return;
+	}
+
+	/*
+	 * very simple loop because we get interrupts only when receiving
+	 */
+	while (simeth_rx(dev));
+}
+
+static struct net_device_stats *
+simeth_get_stats(struct net_device *dev)
+{
+	struct simeth_local  *local = (struct simeth_local *) dev->priv;
+
+	return &local->stats;
+}
+
+/* fake multicast ability */
+static void
+set_multicast_list(struct net_device *dev)
+{
+	printk(KERN_WARNING "%s: set_multicast_list called\n", dev->name);
+}
+
+#ifdef CONFIG_NET_FASTROUTE
+static int
+simeth_accept_fastpath(struct net_device *dev, struct dst_entry *dst)
+{
+	printk(KERN_WARNING "%s: simeth_accept_fastpath called\n", dev->name);
+	return -1;
+}
+#endif
+
+__initcall(simeth_probe);
diff -urN linux-2.4.22-bk28/arch/ia64/drivers/simscsi.c linux-2.4.22-bk29/arch/ia64/drivers/simscsi.c
--- linux-2.4.22-bk28/arch/ia64/drivers/simscsi.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-bk29/arch/ia64/drivers/simscsi.c	2003-10-06 02:50:30.000000000 -0700
@@ -0,0 +1,396 @@
+/*
+ * Simulated SCSI driver.
+ *
+ * Copyright (C) 1999, 2001-2003 Hewlett-Packard Co
+ *	David Mosberger-Tang <davidm@hpl.hp.com>
+ *	Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * 02/01/15 David Mosberger	Updated for v2.5.1
+ * 99/12/18 David Mosberger	Added support for READ10/WRITE10 needed by linux v2.3.33
+ */
+#include <linux/config.h>
+#include <linux/blk.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+
+#include <scsi/scsi.h>
+
+#include <asm/irq.h>
+
+#include "../drivers/scsi/scsi.h"
+#include "../drivers/scsi/sd.h"
+#include "../drivers/scsi/hosts.h"
+#include "simscsi.h"
+
+#define DEBUG_SIMSCSI	1
+
+/* Simulator system calls: */
+
+#define SSC_OPEN			50
+#define SSC_CLOSE			51
+#define SSC_READ			52
+#define SSC_WRITE			53
+#define SSC_GET_COMPLETION		54
+#define SSC_WAIT_COMPLETION		55
+
+#define SSC_WRITE_ACCESS		2
+#define SSC_READ_ACCESS			1
+
+#if DEBUG_SIMSCSI
+  int simscsi_debug;
+# define DBG	simscsi_debug
+#else
+# define DBG	0
+#endif
+
+static void simscsi_interrupt (unsigned long val);
+DECLARE_TASKLET(simscsi_tasklet, simscsi_interrupt, 0);
+
+struct disk_req {
+	unsigned long addr;
+	unsigned len;
+};
+
+struct disk_stat {
+	int fd;
+	unsigned count;
+};
+
+extern long ia64_ssc (long arg0, long arg1, long arg2, long arg3, int nr);
+
+static int desc[16] = {
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static struct queue_entry {
+	Scsi_Cmnd *sc;
+} queue[SIMSCSI_REQ_QUEUE_LEN];
+
+static int rd, wr;
+static atomic_t num_reqs = ATOMIC_INIT(0);
+
+/* base name for default disks */
+static char *simscsi_root = DEFAULT_SIMSCSI_ROOT;
+
+#define MAX_ROOT_LEN	128
+
+/*
+ * used to setup a new base for disk images
+ * to use /foo/bar/disk[a-z] as disk images
+ * you have to specify simscsi=/foo/bar/disk on the command line
+ */
+static int __init
+simscsi_setup (char *s)
+{
+	/* XXX Fix me we may need to strcpy() ? */
+	if (strlen(s) > MAX_ROOT_LEN) {
+		printk(KERN_ERR "simscsi_setup: prefix too long---using default %s\n",
+		       simscsi_root);
+	}
+	simscsi_root = s;
+	return 1;
+}
+
+__setup("simscsi=", simscsi_setup);
+
+static void
+simscsi_interrupt (unsigned long val)
+{
+	unsigned long flags;
+	Scsi_Cmnd *sc;
+
+	spin_lock_irqsave(&io_request_lock, flags);
+	{
+		while ((sc = queue[rd].sc) != 0) {
+			atomic_dec(&num_reqs);
+			queue[rd].sc = 0;
+			if (DBG)
+				printk("simscsi_interrupt: done with %ld\n", sc->serial_number);
+			(*sc->scsi_done)(sc);
+			rd = (rd + 1) % SIMSCSI_REQ_QUEUE_LEN;
+		}
+	}
+	spin_unlock_irqrestore(&io_request_lock, flags);
+}
+
+int
+simscsi_detect (Scsi_Host_Template *templ)
+{
+	templ->proc_name = "simscsi";
+	return 1;	/* fake one SCSI host adapter */
+}
+
+int
+simscsi_release (struct Scsi_Host *host)
+{
+	return 0;	/* this is easy...  */
+}
+
+const char *
+simscsi_info (struct Scsi_Host *host)
+{
+	return "simulated SCSI host adapter";
+}
+
+int
+simscsi_biosparam (Disk *disk, kdev_t n, int ip[])
+{
+	unsigned capacity = disk->capacity;
+
+	ip[0] = 64;		/* heads */
+	ip[1] = 32;		/* sectors */
+	ip[2] = capacity >> 11;	/* cylinders */
+	return 0;
+}
+
+static void
+simscsi_readwrite (Scsi_Cmnd *sc, int mode, unsigned long offset, unsigned long len)
+{
+	struct disk_stat stat;
+	struct disk_req req;
+
+	req.addr = __pa(sc->request_buffer);
+	req.len  = len;			/* # of bytes to transfer */
+
+	if (sc->request_bufflen < req.len)
+		return;
+
+	stat.fd = desc[sc->target];
+	if (DBG)
+		printk("simscsi_%s @ %lx (off %lx)\n",
+		       mode == SSC_READ ? "read":"write", req.addr, offset);
+	ia64_ssc(stat.fd, 1, __pa(&req), offset, mode);
+	ia64_ssc(__pa(&stat), 0, 0, 0, SSC_WAIT_COMPLETION);
+
+	if (stat.count == req.len) {
+		sc->result = GOOD;
+	} else {
+		sc->result = DID_ERROR << 16;
+	}
+}
+
+static void
+simscsi_sg_readwrite (Scsi_Cmnd *sc, int mode, unsigned long offset)
+{
+	int list_len = sc->use_sg;
+	struct scatterlist *sl = (struct scatterlist *)sc->buffer;
+	struct disk_stat stat;
+	struct disk_req req;
+
+	stat.fd = desc[sc->target];
+
+	while (list_len) {
+		req.addr = __pa(sl->address);
+		req.len  = sl->length;
+		if (DBG)
+			printk("simscsi_sg_%s @ %lx (off %lx) use_sg=%d len=%d\n",
+			       mode == SSC_READ ? "read":"write", req.addr, offset,
+			       list_len, sl->length);
+		ia64_ssc(stat.fd, 1, __pa(&req), offset, mode);
+		ia64_ssc(__pa(&stat), 0, 0, 0, SSC_WAIT_COMPLETION);
+
+		/* should not happen in our case */
+		if (stat.count != req.len) {
+			sc->result = DID_ERROR << 16;
+			return;
+		}
+		offset +=  sl->length;
+		sl++;
+		list_len--;
+	}
+	sc->result = GOOD;
+}
+
+/*
+ * function handling both READ_6/WRITE_6 (non-scatter/gather mode)
+ * commands.
+ * Added 02/26/99 S.Eranian
+ */
+static void
+simscsi_readwrite6 (Scsi_Cmnd *sc, int mode)
+{
+	unsigned long offset;
+
+	offset = (((sc->cmnd[1] & 0x1f) << 16) | (sc->cmnd[2] << 8) | sc->cmnd[3])*512;
+	if (sc->use_sg > 0)
+		simscsi_sg_readwrite(sc, mode, offset);
+	else
+		simscsi_readwrite(sc, mode, offset, sc->cmnd[4]*512);
+}
+
+static size_t
+simscsi_get_disk_size (int fd)
+{
+	struct disk_stat stat;
+	size_t bit, sectors = 0;
+	struct disk_req req;
+	char buf[512];
+
+	/*
+	 * This is a bit kludgey: the simulator doesn't provide a direct way of determining
+	 * the disk size, so we do a binary search, assuming a maximum disk size of 4GB.
+	 */
+	for (bit = (4UL << 30)/512; bit != 0; bit >>= 1) {
+		req.addr = __pa(&buf);
+		req.len = sizeof(buf);
+		ia64_ssc(fd, 1, __pa(&req), ((sectors | bit) - 1)*512, SSC_READ);
+		stat.fd = fd;
+		ia64_ssc(__pa(&stat), 0, 0, 0, SSC_WAIT_COMPLETION);
+		if (stat.count == sizeof(buf))
+			sectors |= bit;
+	}
+	return sectors - 1;	/* return last valid sector number */
+}
+
+static void
+simscsi_readwrite10 (Scsi_Cmnd *sc, int mode)
+{
+	unsigned long offset;
+
+	offset = (  (sc->cmnd[2] << 24) | (sc->cmnd[3] << 16)
+		  | (sc->cmnd[4] <<  8) | (sc->cmnd[5] <<  0))*512;
+	if (sc->use_sg > 0)
+		simscsi_sg_readwrite(sc, mode, offset);
+	else
+		simscsi_readwrite(sc, mode, offset, ((sc->cmnd[7] << 8) | sc->cmnd[8])*512);
+}
+
+int
+simscsi_queuecommand (Scsi_Cmnd *sc, void (*done)(Scsi_Cmnd *))
+{
+	char fname[MAX_ROOT_LEN+16];
+	size_t disk_size;
+	char *buf;
+#if DEBUG_SIMSCSI
+	register long sp asm ("sp");
+
+	if (DBG)
+		printk("simscsi_queuecommand: target=%d,cmnd=%u,sc=%lu,sp=%lx,done=%p\n",
+		       sc->target, sc->cmnd[0], sc->serial_number, sp, done);
+#endif
+
+	sc->result = DID_BAD_TARGET << 16;
+	sc->scsi_done = done;
+	if (sc->target <= 15 && sc->lun == 0) {
+		switch (sc->cmnd[0]) {
+		      case INQUIRY:
+			if (sc->request_bufflen < 35) {
+				break;
+			}
+			sprintf (fname, "%s%c", simscsi_root, 'a' + sc->target);
+			desc[sc->target] = ia64_ssc(__pa(fname), SSC_READ_ACCESS|SSC_WRITE_ACCESS,
+						    0, 0, SSC_OPEN);
+			if (desc[sc->target] < 0) {
+				/* disk doesn't exist... */
+				break;
+			}
+			buf = sc->request_buffer;
+			buf[0] = 0;	/* magnetic disk */
+			buf[1] = 0;	/* not a removable medium */
+			buf[2] = 2;	/* SCSI-2 compliant device */
+			buf[3] = 2;	/* SCSI-2 response data format */
+			buf[4] = 31;	/* additional length (bytes) */
+			buf[5] = 0;	/* reserved */
+			buf[6] = 0;	/* reserved */
+			buf[7] = 0;	/* various flags */
+			memcpy(buf + 8, "HP      SIMULATED DISK  0.00",  28);
+			sc->result = GOOD;
+			break;
+
+		      case TEST_UNIT_READY:
+			sc->result = GOOD;
+			break;
+
+		      case READ_6:
+			if (desc[sc->target] < 0 )
+				break;
+			simscsi_readwrite6(sc, SSC_READ);
+			break;
+
+		      case READ_10:
+			if (desc[sc->target] < 0 )
+				break;
+			simscsi_readwrite10(sc, SSC_READ);
+			break;
+
+		      case WRITE_6:
+			if (desc[sc->target] < 0)
+				break;
+			simscsi_readwrite6(sc, SSC_WRITE);
+			break;
+
+		      case WRITE_10:
+			if (desc[sc->target] < 0)
+				break;
+			simscsi_readwrite10(sc, SSC_WRITE);
+			break;
+
+
+		      case READ_CAPACITY:
+			if (desc[sc->target] < 0 || sc->request_bufflen < 8) {
+				break;
+			}
+			buf = sc->request_buffer;
+
+			disk_size = simscsi_get_disk_size(desc[sc->target]);
+			buf[0] = (disk_size >> 24) & 0xff;
+			buf[1] = (disk_size >> 16) & 0xff;
+			buf[2] = (disk_size >>  8) & 0xff;
+			buf[3] = (disk_size >>  0) & 0xff;
+			/* set block size of 512 bytes: */
+			buf[4] = 0;
+			buf[5] = 0;
+			buf[6] = 2;
+			buf[7] = 0;
+			sc->result = GOOD;
+			break;
+
+		      case MODE_SENSE:
+			/* sd.c uses this to determine whether disk does write-caching. */
+			memset(sc->request_buffer, 0, 128);
+			sc->result = GOOD;
+			break;
+
+		      case START_STOP:
+			printk(KERN_ERR "START_STOP\n");
+			break;
+
+		      default:
+			panic("simscsi: unknown SCSI command %u\n", sc->cmnd[0]);
+		}
+	}
+	if (sc->result == DID_BAD_TARGET) {
+		sc->result |= DRIVER_SENSE << 24;
+		sc->sense_buffer[0] = 0x70;
+		sc->sense_buffer[2] = 0x00;
+	}
+	if (atomic_read(&num_reqs) >= SIMSCSI_REQ_QUEUE_LEN) {
+		panic("Attempt to queue command while command is pending!!");
+	}
+	atomic_inc(&num_reqs);
+	queue[wr].sc = sc;
+	wr = (wr + 1) % SIMSCSI_REQ_QUEUE_LEN;
+
+	tasklet_schedule(&simscsi_tasklet);
+	return 0;
+}
+
+int
+simscsi_reset (Scsi_Cmnd *cmd, unsigned int reset_flags)
+{
+	printk(KERN_ERR "simscsi_reset: unimplemented\n");
+	return SCSI_RESET_SUCCESS;
+}
+
+int
+simscsi_abort (Scsi_Cmnd *cmd)
+{
+	printk(KERN_ERR "simscsi_abort: unimplemented\n");
+	return SCSI_ABORT_SUCCESS;
+}
+
+static Scsi_Host_Template driver_template = SIMSCSI;
+
+#include "../drivers/scsi/scsi_module.c"
diff -urN linux-2.4.22-bk28/arch/ia64/drivers/simscsi.h linux-2.4.22-bk29/arch/ia64/drivers/simscsi.h
--- linux-2.4.22-bk28/arch/ia64/drivers/simscsi.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-bk29/arch/ia64/drivers/simscsi.h	2003-10-06 02:50:30.000000000 -0700
@@ -0,0 +1,39 @@
+/*
+ * Simulated SCSI driver.
+ *
+ * Copyright (C) 1999, 2002 Hewlett-Packard Co
+ *	David Mosberger-Tang <davidm@hpl.hp.com>
+ */
+#ifndef SIMSCSI_H
+#define SIMSCSI_H
+
+#define SIMSCSI_REQ_QUEUE_LEN	64
+
+#define DEFAULT_SIMSCSI_ROOT	"/var/ski-disks/sd"
+
+extern int simscsi_detect (Scsi_Host_Template *);
+extern int simscsi_release (struct Scsi_Host *);
+extern const char *simscsi_info (struct Scsi_Host *);
+extern int simscsi_queuecommand (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+extern int simscsi_abort (Scsi_Cmnd *);
+extern int simscsi_reset (Scsi_Cmnd *, unsigned int);
+extern int simscsi_biosparam (Disk *, kdev_t, int[]);
+
+#define SIMSCSI {						\
+	.detect			= simscsi_detect,		\
+	.release		= simscsi_release,		\
+	.info			= simscsi_info,			\
+	.queuecommand		= simscsi_queuecommand,		\
+	.abort			= simscsi_abort,		\
+	.reset			= simscsi_reset,		\
+	.bios_param		= simscsi_biosparam,		\
+	.can_queue		= SIMSCSI_REQ_QUEUE_LEN,	\
+	.this_id		= -1,				\
+	.sg_tablesize		= SG_ALL,			\
+	.cmd_per_lun		= SIMSCSI_REQ_QUEUE_LEN,	\
+	.present		= 0,				\
+	.unchecked_isa_dma	= 0,				\
+	.use_clustering		= DISABLE_CLUSTERING		\
+}
+
+#endif /* SIMSCSI_H */
diff -urN linux-2.4.22-bk28/arch/ia64/drivers/simserial.c linux-2.4.22-bk29/arch/ia64/drivers/simserial.c
--- linux-2.4.22-bk28/arch/ia64/drivers/simserial.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-bk29/arch/ia64/drivers/simserial.c	2003-10-06 02:50:30.000000000 -0700
@@ -0,0 +1,1094 @@
+/*
+ * Simulated Serial Driver (fake serial)
+ *
+ * This driver is mostly used for bringup purposes and will go away.
+ * It has a strong dependency on the system console. All outputs
+ * are rerouted to the same facility as the one used by printk which, in our
+ * case means sys_sim.c console (goes via the simulator). The code hereafter
+ * is completely leveraged from the serial.c driver.
+ *
+ * Copyright (C) 1999-2000, 2002-2003 Hewlett-Packard Co
+ *	Stephane Eranian <eranian@hpl.hp.com>
+ *	David Mosberger-Tang <davidm@hpl.hp.com>
+ *
+ * 02/04/00 D. Mosberger	Merged in serial.c bug fixes in rs_close().
+ * 02/25/00 D. Mosberger	Synced up with 2.3.99pre-5 version of serial.c.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/console.h>
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/serialP.h>
+
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#undef SIMSERIAL_DEBUG	/* define this to get some debug information */
+
+#define KEYBOARD_INTR	3	/* must match with simulator! */
+
+#define NR_PORTS	1	/* only one port for now */
+#define SERIAL_INLINE	1
+
+#ifdef SERIAL_INLINE
+#define _INLINE_ inline
+#endif
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)
+
+#define SSC_GETCHAR	21
+
+extern long ia64_ssc (long, long, long, long, int);
+extern void ia64_ssc_connect_irq (long intr, long irq);
+
+static char *serial_name = "SimSerial driver";
+static char *serial_version = "0.6";
+
+/*
+ * This has been extracted from asm/serial.h. We need one eventually but
+ * I don't know exactly what we're going to put in it so just fake one
+ * for now.
+ */
+#define BASE_BAUD ( 1843200 / 16 )
+
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
+
+/*
+ * Most of the values here are meaningless to this particular driver.
+ * However some values must be preserved for the code (leveraged from serial.c
+ * to work correctly).
+ * port must not be 0
+ * type must not be UNKNOWN
+ * So I picked arbitrary (guess from where?) values instead
+ */
+static struct serial_state rs_table[NR_PORTS]={
+  /* UART CLK   PORT IRQ     FLAGS        */
+  { 0, BASE_BAUD, 0x3F8, 0, STD_COM_FLAGS,0,PORT_16550 }  /* ttyS0 */
+};
+
+/*
+ * Just for the fun of it !
+ */
+static struct serial_uart_config uart_config[] = {
+	{ "unknown", 1, 0 },
+	{ "8250", 1, 0 },
+	{ "16450", 1, 0 },
+	{ "16550", 1, 0 },
+	{ "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO },
+	{ "cirrus", 1, 0 },
+	{ "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH },
+	{ "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO |
+		  UART_STARTECH },
+	{ "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO},
+	{ 0, 0}
+};
+
+static struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+static struct async_struct *IRQ_ports[NR_IRQS];
+static struct tty_struct *serial_table[NR_PORTS];
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+
+static struct console *console;
+
+static unsigned char *tmp_buf;
+static DECLARE_MUTEX(tmp_buf_sem);
+
+extern struct console *console_drivers; /* from kernel/printk.c */
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void rs_stop(struct tty_struct *tty)
+{
+#ifdef SIMSERIAL_DEBUG
+	printk("rs_stop: tty->stopped=%d tty->hw_stopped=%d tty->flow_stopped=%d\n",
+		tty->stopped, tty->hw_stopped, tty->flow_stopped);
+#endif
+
+}
+
+static void rs_start(struct tty_struct *tty)
+{
+#if SIMSERIAL_DEBUG
+	printk("rs_start: tty->stopped=%d tty->hw_stopped=%d tty->flow_stopped=%d\n",
+		tty->stopped, tty->hw_stopped, tty->flow_stopped);
+#endif
+}
+
+static  void receive_chars(struct tty_struct *tty, struct pt_regs *regs)
+{
+	unsigned char ch;
+	static unsigned char seen_esc = 0;
+
+	while ( (ch = ia64_ssc(0, 0, 0, 0, SSC_GETCHAR)) ) {
+		if ( ch == 27 && seen_esc == 0 ) {
+			seen_esc = 1;
+			continue;
+		} else {
+			if ( seen_esc==1 && ch == 'O' ) {
+				seen_esc = 2;
+				continue;
+			} else if ( seen_esc == 2 ) {
+				if ( ch == 'P' ) show_state();		/* F1 key */
+				if ( ch == 'Q' ) show_buffers();	/* F2 key */
+				seen_esc = 0;
+				continue;
+			}
+		}
+		seen_esc = 0;
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE) break;
+
+		*tty->flip.char_buf_ptr = ch;
+
+		*tty->flip.flag_buf_ptr = 0;
+
+		tty->flip.flag_buf_ptr++;
+		tty->flip.char_buf_ptr++;
+		tty->flip.count++;
+	}
+	tty_flip_buffer_push(tty);
+}
+
+/*
+ * This is the serial driver's interrupt routine for a single port
+ */
+static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct async_struct * info;
+
+	/*
+	 * I don't know exactly why they don't use the dev_id opaque data
+	 * pointer instead of this extra lookup table
+	 */
+	info = IRQ_ports[irq];
+	if (!info || !info->tty) {
+		printk(KERN_INFO "simrs_interrupt_single: info|tty=0 info=%p problem\n", info);
+		return;
+	}
+	/*
+	 * pretty simple in our case, because we only get interrupts
+	 * on inbound traffic
+	 */
+	receive_chars(info->tty, regs);
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+#if 0
+/*
+ * not really used in our situation so keep them commented out for now
+ */
+static DECLARE_TASK_QUEUE(tq_serial); /* used to be at the top of the file */
+static void do_serial_bh(void)
+{
+	run_task_queue(&tq_serial);
+	printk(KERN_ERR "do_serial_bh: called\n");
+}
+#endif
+
+static void do_softint(void *private_)
+{
+	printk(KERN_ERR "simserial: do_softint called\n");
+}
+
+static void rs_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	unsigned long flags;
+
+	if (!tty || !info->xmit.buf) return;
+
+	save_flags(flags); cli();
+	if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) == 0) {
+		restore_flags(flags);
+		return;
+	}
+	info->xmit.buf[info->xmit.head] = ch;
+	info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1);
+	restore_flags(flags);
+}
+
+static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
+{
+	int count;
+	unsigned long flags;
+
+	save_flags(flags); cli();
+
+	if (info->x_char) {
+		char c = info->x_char;
+
+		console->write(console, &c, 1);
+
+		info->state->icount.tx++;
+		info->x_char = 0;
+
+		goto out;
+	}
+
+	if (info->xmit.head == info->xmit.tail || info->tty->stopped || info->tty->hw_stopped) {
+#ifdef SIMSERIAL_DEBUG
+		printk("transmit_chars: head=%d, tail=%d, stopped=%d\n",
+		       info->xmit.head, info->xmit.tail, info->tty->stopped);
+#endif
+		goto out;
+	}
+	/*
+	 * We removed the loop and try to do it in to chunks. We need
+	 * 2 operations maximum because it's a ring buffer.
+	 *
+	 * First from current to tail if possible.
+	 * Then from the beginning of the buffer until necessary
+	 */
+
+	count = MIN(CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE),
+		    SERIAL_XMIT_SIZE - info->xmit.tail);
+	console->write(console, info->xmit.buf+info->xmit.tail, count);
+
+	info->xmit.tail = (info->xmit.tail+count) & (SERIAL_XMIT_SIZE-1);
+
+	/*
+	 * We have more at the beginning of the buffer
+	 */
+	count = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+	if (count) {
+		console->write(console, info->xmit.buf, count);
+		info->xmit.tail += count;
+	}
+out:
+	restore_flags(flags);
+}
+
+static void rs_flush_chars(struct tty_struct *tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+	if (info->xmit.head == info->xmit.tail || tty->stopped || tty->hw_stopped ||
+	    !info->xmit.buf)
+		return;
+
+	transmit_chars(info, NULL);
+}
+
+
+static int rs_write(struct tty_struct * tty, int from_user,
+		    const unsigned char *buf, int count)
+{
+	int	c, ret = 0;
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	unsigned long flags;
+
+	if (!tty || !info->xmit.buf || !tmp_buf) return 0;
+
+	save_flags(flags);
+	if (from_user) {
+		down(&tmp_buf_sem);
+		while (1) {
+			int c1;
+			c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+			if (count < c)
+				c = count;
+			if (c <= 0)
+				break;
+
+			c -= copy_from_user(tmp_buf, buf, c);
+			if (!c) {
+				if (!ret)
+					ret = -EFAULT;
+				break;
+			}
+			cli();
+			c1 = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+			if (c1 < c)
+				c = c1;
+			memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);
+			info->xmit.head = ((info->xmit.head + c) &
+					   (SERIAL_XMIT_SIZE-1));
+			restore_flags(flags);
+			buf += c;
+			count -= c;
+			ret += c;
+		}
+		up(&tmp_buf_sem);
+	} else {
+		cli();
+		while (1) {
+			c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+			if (count < c)
+				c = count;
+			if (c <= 0) {
+				break;
+			}
+			memcpy(info->xmit.buf + info->xmit.head, buf, c);
+			info->xmit.head = ((info->xmit.head + c) &
+					   (SERIAL_XMIT_SIZE-1));
+			buf += c;
+			count -= c;
+			ret += c;
+		}
+		restore_flags(flags);
+	}
+	/*
+	 * Hey, we transmit directly from here in our case
+	 */
+	if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE)
+	    && !tty->stopped && !tty->hw_stopped) {
+		transmit_chars(info, NULL);
+	}
+	return ret;
+}
+
+static int rs_write_room(struct tty_struct *tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+	return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+}
+
+static int rs_chars_in_buffer(struct tty_struct *tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+	return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+}
+
+static void rs_flush_buffer(struct tty_struct *tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	unsigned long flags;
+
+	save_flags(flags); cli();
+	info->xmit.head = info->xmit.tail = 0;
+	restore_flags(flags);
+
+	wake_up_interruptible(&tty->write_wait);
+
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+static void rs_send_xchar(struct tty_struct *tty, char ch)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+	info->x_char = ch;
+	if (ch) {
+		/*
+		 * I guess we could call console->write() directly but
+		 * let's do that for now.
+		 */
+		transmit_chars(info, NULL);
+	}
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void rs_throttle(struct tty_struct * tty)
+{
+	if (I_IXOFF(tty)) rs_send_xchar(tty, STOP_CHAR(tty));
+
+	printk(KERN_INFO "simrs_throttle called\n");
+}
+
+static void rs_unthrottle(struct tty_struct * tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+	if (I_IXOFF(tty)) {
+		if (info->x_char)
+			info->x_char = 0;
+		else
+			rs_send_xchar(tty, START_CHAR(tty));
+	}
+	printk(KERN_INFO "simrs_unthrottle called\n");
+}
+
+/*
+ * rs_break() --- routine which turns the break handling on or off
+ */
+static void rs_break(struct tty_struct *tty, int break_state)
+{
+}
+
+static int rs_ioctl(struct tty_struct *tty, struct file * file,
+		    unsigned int cmd, unsigned long arg)
+{
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
+	    (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+		    return -EIO;
+	}
+
+	switch (cmd) {
+		case TIOCMGET:
+			printk(KERN_INFO "rs_ioctl: TIOCMGET called\n");
+			return -EINVAL;
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+			printk(KERN_INFO "rs_ioctl: TIOCMBIS/BIC/SET called\n");
+			return -EINVAL;
+		case TIOCGSERIAL:
+			printk(KERN_INFO "simrs_ioctl TIOCGSERIAL called\n");
+			return 0;
+		case TIOCSSERIAL:
+			printk(KERN_INFO "simrs_ioctl TIOCSSERIAL called\n");
+			return 0;
+		case TIOCSERCONFIG:
+			printk(KERN_INFO "rs_ioctl: TIOCSERCONFIG called\n");
+			return -EINVAL;
+
+		case TIOCSERGETLSR: /* Get line status register */
+			printk(KERN_INFO "rs_ioctl: TIOCSERGETLSR called\n");
+			return  -EINVAL;
+
+		case TIOCSERGSTRUCT:
+			printk(KERN_INFO "rs_ioctl: TIOCSERGSTRUCT called\n");
+#if 0
+			if (copy_to_user((struct async_struct *) arg,
+					 info, sizeof(struct async_struct)))
+				return -EFAULT;
+#endif
+			return 0;
+
+		/*
+		 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+		 * - mask passed in arg for lines of interest
+		 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+		 * Caller should use TIOCGICOUNT to see which one it was
+		 */
+		case TIOCMIWAIT:
+			printk(KERN_INFO "rs_ioctl: TIOCMIWAIT: called\n");
+			return 0;
+		/*
+		 * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+		 * Return: write counters to the user passed counter struct
+		 * NB: both 1->0 and 0->1 transitions are counted except for
+		 *     RI where only 0->1 is counted.
+		 */
+		case TIOCGICOUNT:
+			printk(KERN_INFO "rs_ioctl: TIOCGICOUNT called\n");
+			return 0;
+
+		case TIOCSERGWILD:
+		case TIOCSERSWILD:
+			/* "setserial -W" is called in Debian boot */
+			printk (KERN_INFO "TIOCSER?WILD ioctl obsolete, ignored.\n");
+			return 0;
+
+		default:
+			return -ENOIOCTLCMD;
+		}
+	return 0;
+}
+
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	unsigned int cflag = tty->termios->c_cflag;
+
+	if (   (cflag == old_termios->c_cflag)
+	    && (   RELEVANT_IFLAG(tty->termios->c_iflag)
+		== RELEVANT_IFLAG(old_termios->c_iflag)))
+	  return;
+
+
+	/* Handle turning off CRTSCTS */
+	if ((old_termios->c_cflag & CRTSCTS) &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+		rs_start(tty);
+	}
+}
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void shutdown(struct async_struct * info)
+{
+	unsigned long	flags;
+	struct serial_state *state;
+	int		retval;
+
+	if (!(info->flags & ASYNC_INITIALIZED)) return;
+
+	state = info->state;
+
+#ifdef SIMSERIAL_DEBUG
+	printk("Shutting down serial port %d (irq %d)....", info->line,
+	       state->irq);
+#endif
+
+	save_flags(flags); cli(); /* Disable interrupts */
+
+	/*
+	 * First unlink the serial port from the IRQ chain...
+	 */
+	if (info->next_port)
+		info->next_port->prev_port = info->prev_port;
+	if (info->prev_port)
+		info->prev_port->next_port = info->next_port;
+	else
+		IRQ_ports[state->irq] = info->next_port;
+
+	/*
+	 * Free the IRQ, if necessary
+	 */
+	if (state->irq && (!IRQ_ports[state->irq] ||
+			  !IRQ_ports[state->irq]->next_port)) {
+		if (IRQ_ports[state->irq]) {
+			free_irq(state->irq, NULL);
+			retval = request_irq(state->irq, rs_interrupt_single,
+					     IRQ_T(info), "serial", NULL);
+
+			if (retval)
+				printk(KERN_ERR "serial shutdown: request_irq: error %d"
+				       "  Couldn't reacquire IRQ.\n", retval);
+		} else
+			free_irq(state->irq, NULL);
+	}
+
+	if (info->xmit.buf) {
+		free_page((unsigned long) info->xmit.buf);
+		info->xmit.buf = 0;
+	}
+
+	if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->flags &= ~ASYNC_INITIALIZED;
+	restore_flags(flags);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ *
+ * This routine is called when the serial port gets closed.  First, we
+ * wait for the last remaining data to be sent.  Then, we unlink its
+ * async structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void rs_close(struct tty_struct *tty, struct file * filp)
+{
+	struct async_struct * info = (struct async_struct *)tty->driver_data;
+	struct serial_state *state;
+	unsigned long flags;
+
+	if (!info ) return;
+
+	state = info->state;
+
+	save_flags(flags); cli();
+
+	if (tty_hung_up_p(filp)) {
+#ifdef SIMSERIAL_DEBUG
+		printk("rs_close: hung_up\n");
+#endif
+		MOD_DEC_USE_COUNT;
+		restore_flags(flags);
+		return;
+	}
+#ifdef SIMSERIAL_DEBUG
+	printk("rs_close ttys%d, count = %d\n", info->line, state->count);
+#endif
+	if ((tty->count == 1) && (state->count != 1)) {
+		/*
+		 * Uh, oh.  tty->count is 1, which means that the tty
+		 * structure will be freed.  state->count should always
+		 * be one in these conditions.  If it's greater than
+		 * one, we've got real problems, since it means the
+		 * serial port won't be shutdown.
+		 */
+		printk(KERN_ERR "rs_close: bad serial port count; tty->count is 1, "
+		       "state->count is %d\n", state->count);
+		state->count = 1;
+	}
+	if (--state->count < 0) {
+		printk(KERN_ERR "rs_close: bad serial port count for ttys%d: %d\n",
+		       info->line, state->count);
+		state->count = 0;
+	}
+	if (state->count) {
+		MOD_DEC_USE_COUNT;
+		restore_flags(flags);
+		return;
+	}
+	info->flags |= ASYNC_CLOSING;
+	restore_flags(flags);
+
+	/*
+	 * Now we wait for the transmit buffer to clear; and we notify
+	 * the line discipline to only process XON/XOFF characters.
+	 */
+	shutdown(info);
+	if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty);
+	info->event = 0;
+	info->tty = 0;
+	if (info->blocked_open) {
+		if (info->close_delay) {
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(info->close_delay);
+		}
+		wake_up_interruptible(&info->open_wait);
+	}
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|ASYNC_CLOSING);
+	wake_up_interruptible(&info->close_wait);
+	MOD_DEC_USE_COUNT;
+}
+
+/*
+ * rs_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+}
+
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void rs_hangup(struct tty_struct *tty)
+{
+	struct async_struct * info = (struct async_struct *)tty->driver_data;
+	struct serial_state *state = info->state;
+
+#ifdef SIMSERIAL_DEBUG
+	printk("rs_hangup: called\n");
+#endif
+
+	state = info->state;
+
+	rs_flush_buffer(tty);
+	if (info->flags & ASYNC_CLOSING)
+		return;
+	shutdown(info);
+
+	info->event = 0;
+	state->count = 0;
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+	info->tty = 0;
+	wake_up_interruptible(&info->open_wait);
+}
+
+
+static int get_async_struct(int line, struct async_struct **ret_info)
+{
+	struct async_struct *info;
+	struct serial_state *sstate;
+
+	sstate = rs_table + line;
+	sstate->count++;
+	if (sstate->info) {
+		*ret_info = sstate->info;
+		return 0;
+	}
+	info = kmalloc(sizeof(struct async_struct), GFP_KERNEL);
+	if (!info) {
+		sstate->count--;
+		return -ENOMEM;
+	}
+	memset(info, 0, sizeof(struct async_struct));
+	init_waitqueue_head(&info->open_wait);
+	init_waitqueue_head(&info->close_wait);
+	init_waitqueue_head(&info->delta_msr_wait);
+	info->magic = SERIAL_MAGIC;
+	info->port = sstate->port;
+	info->flags = sstate->flags;
+	info->xmit_fifo_size = sstate->xmit_fifo_size;
+	info->line = line;
+	info->tqueue.routine = do_softint;
+	info->tqueue.data = info;
+	info->state = sstate;
+	if (sstate->info) {
+		kfree(info);
+		*ret_info = sstate->info;
+		return 0;
+	}
+	*ret_info = sstate->info = info;
+	return 0;
+}
+
+static int
+startup(struct async_struct *info)
+{
+	unsigned long flags;
+	int	retval=0;
+	void (*handler)(int, void *, struct pt_regs *);
+	struct serial_state *state= info->state;
+	unsigned long page;
+
+	page = get_free_page(GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+
+	save_flags(flags); cli();
+
+	if (info->flags & ASYNC_INITIALIZED) {
+		free_page(page);
+		goto errout;
+	}
+
+	if (!state->port || !state->type) {
+		if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags);
+		free_page(page);
+		goto errout;
+	}
+	if (info->xmit.buf)
+		free_page(page);
+	else
+		info->xmit.buf = (unsigned char *) page;
+
+#ifdef SIMSERIAL_DEBUG
+	printk("startup: ttys%d (irq %d)...", info->line, state->irq);
+#endif
+
+	/*
+	 * Allocate the IRQ if necessary
+	 */
+	if (state->irq && (!IRQ_ports[state->irq] ||
+			  !IRQ_ports[state->irq]->next_port)) {
+		if (IRQ_ports[state->irq]) {
+			retval = -EBUSY;
+			goto errout;
+		} else
+			handler = rs_interrupt_single;
+
+		retval = request_irq(state->irq, handler, IRQ_T(info), "simserial", NULL);
+		if (retval) {
+			if (capable(CAP_SYS_ADMIN)) {
+				if (info->tty)
+					set_bit(TTY_IO_ERROR,
+						&info->tty->flags);
+				retval = 0;
+			}
+			goto errout;
+		}
+	}
+
+	/*
+	 * Insert serial port into IRQ chain.
+	 */
+	info->prev_port = 0;
+	info->next_port = IRQ_ports[state->irq];
+	if (info->next_port)
+		info->next_port->prev_port = info;
+	IRQ_ports[state->irq] = info;
+
+	if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->xmit.head = info->xmit.tail = 0;
+
+#if 0
+	/*
+	 * Set up serial timers...
+	 */
+	timer_table[RS_TIMER].expires = jiffies + 2*HZ/100;
+	timer_active |= 1 << RS_TIMER;
+#endif
+
+	/*
+	 * Set up the tty->alt_speed kludge
+	 */
+	if (info->tty) {
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+			info->tty->alt_speed = 57600;
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+			info->tty->alt_speed = 115200;
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+			info->tty->alt_speed = 230400;
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+			info->tty->alt_speed = 460800;
+	}
+
+	info->flags |= ASYNC_INITIALIZED;
+	restore_flags(flags);
+	return 0;
+
+errout:
+	restore_flags(flags);
+	return retval;
+}
+
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+static int rs_open(struct tty_struct *tty, struct file * filp)
+{
+	struct async_struct	*info;
+	int			retval, line;
+	unsigned long		page;
+
+	MOD_INC_USE_COUNT;
+	line = MINOR(tty->device) - tty->driver.minor_start;
+	if ((line < 0) || (line >= NR_PORTS)) {
+		MOD_DEC_USE_COUNT;
+		return -ENODEV;
+	}
+	retval = get_async_struct(line, &info);
+	if (retval) {
+		MOD_DEC_USE_COUNT;
+		return retval;
+	}
+	tty->driver_data = info;
+	info->tty = tty;
+
+#ifdef SIMSERIAL_DEBUG
+	printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
+	       info->state->count);
+#endif
+	info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+	if (!tmp_buf) {
+		page = get_free_page(GFP_KERNEL);
+		if (!page) {
+			/* MOD_DEC_USE_COUNT; "info->tty" will cause this? */
+			return -ENOMEM;
+		}
+		if (tmp_buf)
+			free_page(page);
+		else
+			tmp_buf = (unsigned char *) page;
+	}
+
+	/*
+	 * If the port is the middle of closing, bail out now
+	 */
+	if (tty_hung_up_p(filp) ||
+	    (info->flags & ASYNC_CLOSING)) {
+		if (info->flags & ASYNC_CLOSING)
+			interruptible_sleep_on(&info->close_wait);
+		/* MOD_DEC_USE_COUNT; "info->tty" will cause this? */
+#ifdef SERIAL_DO_RESTART
+		return ((info->flags & ASYNC_HUP_NOTIFY) ?
+			-EAGAIN : -ERESTARTSYS);
+#else
+		return -EAGAIN;
+#endif
+	}
+
+	/*
+	 * Start up serial port
+	 */
+	retval = startup(info);
+	if (retval) {
+		/* MOD_DEC_USE_COUNT; "info->tty" will cause this? */
+		return retval;
+	}
+
+	if ((info->state->count == 1) &&
+	    (info->flags & ASYNC_SPLIT_TERMIOS)) {
+		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+			*tty->termios = info->state->normal_termios;
+		else
+			*tty->termios = info->state->callout_termios;
+	}
+
+	/*
+	 * figure out which console to use (should be one already)
+	 */
+	console = console_drivers;
+	while (console) {
+		if ((console->flags & CON_ENABLED) && console->write) break;
+		console = console->next;
+	}
+
+	info->session = current->session;
+	info->pgrp = current->pgrp;
+
+#ifdef SIMSERIAL_DEBUG
+	printk("rs_open ttys%d successful\n", info->line);
+#endif
+	return 0;
+}
+
+/*
+ * /proc fs routines....
+ */
+
+static inline int line_info(char *buf, struct serial_state *state)
+{
+	return sprintf(buf, "%d: uart:%s port:%lX irq:%d\n",
+		       state->line, uart_config[state->type].name,
+		       state->port, state->irq);
+}
+
+static int rs_read_proc(char *page, char **start, off_t off, int count,
+		 int *eof, void *data)
+{
+	int i, len = 0, l;
+	off_t	begin = 0;
+
+	len += sprintf(page, "simserinfo:1.0 driver:%s\n", serial_version);
+	for (i = 0; i < NR_PORTS && len < 4000; i++) {
+		l = line_info(page + len, &rs_table[i]);
+		len += l;
+		if (len+begin > off+count)
+			goto done;
+		if (len+begin < off) {
+			begin += len;
+			len = 0;
+		}
+	}
+	*eof = 1;
+done:
+	if (off >= len+begin)
+		return 0;
+	*start = page + (begin-off);
+	return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * rs_init() and friends
+ *
+ * rs_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static inline void show_serial_version(void)
+{
+	printk(KERN_INFO "%s version %s with", serial_name, serial_version);
+	printk(KERN_INFO " no serial options enabled\n");
+}
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+static int __init
+simrs_init (void)
+{
+	int			i;
+	struct serial_state	*state;
+
+	show_serial_version();
+
+	/* Initialize the tty_driver structure */
+
+	memset(&serial_driver, 0, sizeof(struct tty_driver));
+	serial_driver.magic = TTY_DRIVER_MAGIC;
+	serial_driver.driver_name = "simserial";
+	serial_driver.name = "ttyS";
+	serial_driver.major = TTY_MAJOR;
+	serial_driver.minor_start = 64;
+	serial_driver.num = 1;
+	serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+	serial_driver.subtype = SERIAL_TYPE_NORMAL;
+	serial_driver.init_termios = tty_std_termios;
+	serial_driver.init_termios.c_cflag =
+		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	serial_driver.flags = TTY_DRIVER_REAL_RAW;
+	serial_driver.refcount = &serial_refcount;
+	serial_driver.table = serial_table;
+	serial_driver.termios = serial_termios;
+	serial_driver.termios_locked = serial_termios_locked;
+
+	serial_driver.open = rs_open;
+	serial_driver.close = rs_close;
+	serial_driver.write = rs_write;
+	serial_driver.put_char = rs_put_char;
+	serial_driver.flush_chars = rs_flush_chars;
+	serial_driver.write_room = rs_write_room;
+	serial_driver.chars_in_buffer = rs_chars_in_buffer;
+	serial_driver.flush_buffer = rs_flush_buffer;
+	serial_driver.ioctl = rs_ioctl;
+	serial_driver.throttle = rs_throttle;
+	serial_driver.unthrottle = rs_unthrottle;
+	serial_driver.send_xchar = rs_send_xchar;
+	serial_driver.set_termios = rs_set_termios;
+	serial_driver.stop = rs_stop;
+	serial_driver.start = rs_start;
+	serial_driver.hangup = rs_hangup;
+	serial_driver.break_ctl = rs_break;
+	serial_driver.wait_until_sent = rs_wait_until_sent;
+	serial_driver.read_proc = rs_read_proc;
+
+	/*
+	 * Let's have a little bit of fun !
+	 */
+	for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
+
+		if (state->type == PORT_UNKNOWN) continue;
+
+		if (!state->irq) {
+			state->irq = ia64_alloc_vector();
+			ia64_ssc_connect_irq(KEYBOARD_INTR, state->irq);
+		}
+
+		printk(KERN_INFO "ttyS%02d at 0x%04lx (irq = %d) is a %s\n",
+		       state->line,
+		       state->port, state->irq,
+		       uart_config[state->type].name);
+	}
+	/*
+	 * The callout device is just like normal device except for
+	 * major number and the subtype code.
+	 */
+	callout_driver = serial_driver;
+	callout_driver.name = "cua";
+	callout_driver.major = TTYAUX_MAJOR;
+	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+	callout_driver.read_proc = 0;
+	callout_driver.proc_entry = 0;
+
+	if (tty_register_driver(&serial_driver))
+		panic("Couldn't register simserial driver\n");
+
+	if (tty_register_driver(&callout_driver))
+		panic("Couldn't register callout driver\n");
+
+	return 0;
+}
+
+#ifndef MODULE
+__initcall(simrs_init);
+#endif
diff -urN linux-2.4.22-bk28/arch/x86_64/kernel/e820.c linux-2.4.22-bk29/arch/x86_64/kernel/e820.c
--- linux-2.4.22-bk28/arch/x86_64/kernel/e820.c	2003-10-06 02:50:23.000000000 -0700
+++ linux-2.4.22-bk29/arch/x86_64/kernel/e820.c	2003-10-06 02:50:30.000000000 -0700
@@ -13,6 +13,7 @@
 #include <asm/e820.h>
 #include <asm/proto.h>
 #include <asm/acpi.h>
+#include <asm/apic.h>
 #include <asm/bootsetup.h>
 
 extern unsigned long table_start, table_end;
diff -urN linux-2.4.22-bk28/drivers/Makefile linux-2.4.22-bk29/drivers/Makefile
--- linux-2.4.22-bk28/drivers/Makefile	2003-08-25 04:44:40.000000000 -0700
+++ linux-2.4.22-bk29/drivers/Makefile	2003-10-06 02:50:30.000000000 -0700
@@ -8,7 +8,7 @@
 
 mod-subdirs :=	dio hil mtd sbus video macintosh usb input telephony ide \
 		message/i2o message/fusion scsi md ieee1394 pnp isdn atm \
-		fc4 net/hamradio i2c acpi bluetooth
+		fc4 net/hamradio i2c acpi bluetooth usb/gadget
 
 subdir-y :=	parport char block net sound misc media cdrom hotplug
 subdir-m :=	$(subdir-y)
@@ -28,6 +28,7 @@
 subdir-$(CONFIG_MAC)		+= macintosh
 subdir-$(CONFIG_PPC32)		+= macintosh
 subdir-$(CONFIG_USB)		+= usb
+subdir-$(CONFIG_USB_GADGET)	+= usb/gadget
 subdir-$(CONFIG_INPUT)		+= input
 subdir-$(CONFIG_PHONE)		+= telephony
 subdir-$(CONFIG_IDE)		+= ide
diff -urN linux-2.4.22-bk28/drivers/char/mem.c linux-2.4.22-bk29/drivers/char/mem.c
--- linux-2.4.22-bk28/drivers/char/mem.c	2003-06-13 07:51:33.000000000 -0700
+++ linux-2.4.22-bk29/drivers/char/mem.c	2003-10-06 02:50:31.000000000 -0700
@@ -21,6 +21,7 @@
 #include <linux/raw.h>
 #include <linux/tty.h>
 #include <linux/capability.h>
+#include <linux/ptrace.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -503,16 +504,23 @@
  */
 static loff_t memory_lseek(struct file * file, loff_t offset, int orig)
 {
+	loff_t ret;
+
 	switch (orig) {
 		case 0:
 			file->f_pos = offset;
-			return file->f_pos;
+			ret = file->f_pos;
+			force_successful_syscall_return();
+			break;
 		case 1:
 			file->f_pos += offset;
-			return file->f_pos;
+			ret = file->f_pos;
+			force_successful_syscall_return();
+			break;
 		default:
-			return -EINVAL;
+			ret = -EINVAL;
 	}
+	return ret;
 }
 
 static int open_port(struct inode * inode, struct file * filp)
diff -urN linux-2.4.22-bk28/drivers/md/lvm.c linux-2.4.22-bk29/drivers/md/lvm.c
--- linux-2.4.22-bk28/drivers/md/lvm.c	2003-10-06 02:50:24.000000000 -0700
+++ linux-2.4.22-bk29/drivers/md/lvm.c	2003-10-06 02:50:31.000000000 -0700
@@ -1584,8 +1584,10 @@
 		minor = vg_ptr->vg_number;
 
 	/* check limits */
-	if (minor >= ABS_MAX_VG)
+	if (minor >= ABS_MAX_VG) {
+		kfree(vg_ptr);
 		return -EFAULT;
+	}
 
 	/* Validate it */
 	if (vg[VG_CHR(minor)] != NULL) {
@@ -1653,8 +1655,7 @@
 				P_IOCTL
 				    ("ERROR: copying LV ptr %p (%d bytes)\n",
 				     lvp, sizeof(lv_t));
-				lvm_do_vg_remove(minor);
-				return -EFAULT;
+				goto copy_fault;
 			}
 			if (lv.lv_access & LV_SNAPSHOT) {
 				snap_lv_ptr[ls] = lvp;
@@ -1665,8 +1666,7 @@
 			vg_ptr->lv[l] = NULL;
 			/* only create original logical volumes for now */
 			if (lvm_do_lv_create(minor, lv.lv_name, &lv) != 0) {
-				lvm_do_vg_remove(minor);
-				return -EFAULT;
+				goto copy_fault;
 			}
 		}
 	}
@@ -1676,12 +1676,10 @@
 	for (l = 0; l < ls; l++) {
 		lv_t *lvp = snap_lv_ptr[l];
 		if (copy_from_user(&lv, lvp, sizeof(lv_t)) != 0) {
-			lvm_do_vg_remove(minor);
-			return -EFAULT;
+			goto copy_fault;
 		}
 		if (lvm_do_lv_create(minor, lv.lv_name, &lv) != 0) {
-			lvm_do_vg_remove(minor);
-			return -EFAULT;
+			goto copy_fault;
 		}
 	}
 
@@ -1696,6 +1694,10 @@
 	vg_ptr->vg_status |= VG_ACTIVE;
 
 	return 0;
+copy_fault:
+	lvm_do_vg_remove(minor);
+	vfree(snap_lv_ptr);
+	return -EFAULT;
 }				/* lvm_do_vg_create() */
 
 
diff -urN linux-2.4.22-bk28/drivers/net/wan/dscc4.c linux-2.4.22-bk29/drivers/net/wan/dscc4.c
--- linux-2.4.22-bk28/drivers/net/wan/dscc4.c	2003-10-06 02:50:25.000000000 -0700
+++ linux-2.4.22-bk29/drivers/net/wan/dscc4.c	2003-10-06 02:50:31.000000000 -0700
@@ -47,7 +47,7 @@
  * Tx direction
  * When the tx ring is full, the xmit routine issues a call to netdev_stop.
  * The device is supposed to be enabled again during an ALLS irq (we could
- * use HI but as it's easy to loose events, it's fscked).
+ * use HI but as it's easy to lose events, it's fscked).
  *
  * Rx direction
  * The received frames aren't supposed to span over multiple receiving areas.
@@ -80,7 +80,6 @@
  * - misc crapectomy.
  */
 
-#include <linux/version.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/errno.h>
@@ -108,7 +107,7 @@
 #include <linux/hdlc.h>
 
 /* Version */
-static const char version[] = "$Id: dscc4.c,v 1.159 2002/04/10 22:05:17 romieu Exp $ for Linux\n";
+static const char version[] = "$Id: dscc4.c,v 1.173 2003/09/20 23:55:34 romieu Exp $ for Linux\n";
 static int debug;
 static int quartz;
 
@@ -165,7 +164,7 @@
 #define SOURCE_ID(flags)	(((flags) >> 28) & 0x03)
 #define TO_SIZE(state)		(((state) >> 16) & 0x1fff)
 #define TO_STATE(len)		cpu_to_le32(((len) & TxSizeMax) << 16)
-#define RX_MAX(len)		((((len) >> 5) + 1) << 5)
+#define RX_MAX(len)		((((len) >> 5) + 1) << 5)	/* Cf RLCR */
 #define SCC_REG_START(dpriv)	(SCC_START+(dpriv->dev_id)*SCC_OFFSET)
 
 struct dscc4_pci_priv {
@@ -283,6 +282,7 @@
 #define Hold		0x40000000
 #define SccBusy		0x10000000
 #define PowerUp		0x80000000
+#define Vis		0x00001000
 #define FrameOk		(FrameVfr | FrameCrc)
 #define FrameVfr	0x80
 #define FrameRdo	0x40
@@ -323,6 +323,12 @@
 #define NeedIDR		0x00000001
 #define NeedIDT		0x00000002
 #define RdoSet		0x00000004
+/* Don't mask RDO. Ever. */
+#ifdef DSCC4_POLLING
+#define EventsMask	0xfffeef7f
+#else
+#define EventsMask	0xfffa8f7a
+#endif
 
 /* Functions prototypes */
 static inline void dscc4_rx_irq(struct dscc4_pci_priv *, struct dscc4_dev_priv *);
@@ -487,9 +493,9 @@
 	skb = dev_alloc_skb(len);
 	dpriv->rx_skbuff[dirty] = skb;
 	if (skb) {
-	skb->dev = dev;
+		skb->dev = dev;
 		skb->protocol = hdlc_type_trans(skb, dev);
-	skb->mac.raw = skb->data;
+		skb->mac.raw = skb->data;
 		rx_fd->data = pci_map_single(dpriv->pci_priv->pdev, skb->data,
 					     len, PCI_DMA_FROMDEVICE);
 	} else {
@@ -571,7 +577,7 @@
 {
 	/* Cf errata DS5 p.6 */
 	writel(0x00000000, dev->base_addr + CH0LRDA + dpriv->dev_id*4);
-	scc_writel(~PowerUp & scc_readl(dpriv, CCR0), dpriv, dev, CCR0);
+	scc_patchl(PowerUp, 0, dpriv, dev, CCR0);
 	readl(dev->base_addr + CH0LRDA + dpriv->dev_id*4);
 	writel(MTFi|Rdr, dev->base_addr + dpriv->dev_id*0x0c + CH0CFG);
 	writel(Action, dev->base_addr + GCMDR);
@@ -582,7 +588,7 @@
 	u16 i = 0;
 
 	/* Cf errata DS5 p.7 */
-	scc_writel(~PowerUp & scc_readl(dpriv, CCR0), dpriv, dev, CCR0);
+	scc_patchl(PowerUp, 0, dpriv, dev, CCR0);
 	scc_writel(0x00050000, dpriv, dev, CCR2);
 	/*
 	 * Must be longer than the time required to fill the fifo.
@@ -619,6 +625,8 @@
 		stats->rx_packets++;
 		stats->rx_bytes += pkt_len;
 		skb_put(skb, pkt_len);
+		if (netif_running(dev))
+			skb->protocol = hdlc_type_trans(skb, dev);
 		skb->dev->last_rx = jiffies;
 		netif_rx(skb);
 	} else {
@@ -661,8 +669,8 @@
 	kfree(ppriv);
 }
 
-static int __init dscc4_init_one(struct pci_dev *pdev,
-				  const struct pci_device_id *ent)
+static int __devinit dscc4_init_one(struct pci_dev *pdev,
+				    const struct pci_device_id *ent)
 {
 	struct dscc4_pci_priv *priv;
 	struct dscc4_dev_priv *dpriv;
@@ -805,7 +813,8 @@
 static void dscc4_init_registers(struct dscc4_dev_priv *dpriv,
 				 struct net_device *dev)
 {
-	scc_writel(0x80001000, dpriv, dev, CCR0);
+	/* No interrupts, SCC core disabled. Let's relax */
+	scc_writel(0x00000000, dpriv, dev, CCR0);
 
 	scc_writel(LengthCheck | (HDLC_MAX_MRU >> 5), dpriv, dev, RLCR);
 
@@ -820,15 +829,6 @@
 	scc_writel(0x00050008 & ~RxActivate, dpriv, dev, CCR2);
 	// crc forwarded
 	//scc_writel(0x00250008 & ~RxActivate, dpriv, dev, CCR2);
-
-	/* Don't mask RDO. Ever. */
-#ifdef DSCC4_POLLING
-	scc_writel(0xfffeef7f, dpriv, dev, IMR); /* Interrupt mask */
-#else
-	//scc_writel(0xfffaef7f, dpriv, dev, IMR); /* Interrupt mask */
-	//scc_writel(0xfffaef7e, dpriv, dev, IMR); /* Interrupt mask */
-	scc_writel(0xfffa8f7a, dpriv, dev, IMR); /* Interrupt mask */
-#endif
 }
 
 static int dscc4_found1(struct pci_dev *pdev, unsigned long ioaddr)
@@ -955,6 +955,8 @@
 	/* IDT+IDR during XPR */
 	dpriv->flags = NeedIDR | NeedIDT;
 
+	scc_patchl(0, PowerUp | Vis, dpriv, dev, CCR0);
+
 	/*
 	 * The following is a bit paranoid...
 	 *
@@ -969,11 +971,13 @@
 	} else
 		printk(KERN_INFO "%s: available. Good\n", dev->name);
 
+	scc_writel(EventsMask, dpriv, dev, IMR);
+
 	/* Posted write is flushed in the wait_ack loop */
 	scc_writel(TxSccRes | RxSccRes, dpriv, dev, CMDR);
 
 	if ((ret = dscc4_wait_ack_cec(dpriv, dev, "Cec")) < 0)
-		goto err_free_ring;
+		goto err_disable_scc_events;
 
 	/*
 	 * I would expect XPR near CE completion (before ? after ?).
@@ -984,7 +988,7 @@
 	 */
 	if ((ret = dscc4_xpr_ack(dpriv)) < 0) {
 		printk(KERN_ERR "%s: %s timeout\n", DRV_NAME, "XPR");
-		goto err_free_ring;
+		goto err_disable_scc_events;
 	}
 	
 	if (debug > 2)
@@ -1001,7 +1005,10 @@
 
 	return 0;
 
+err_disable_scc_events:
+	scc_writel(0xffffffff, dpriv, dev, IMR);
 err_free_ring:
+	scc_patchl(PowerUp | Vis, 0, dpriv, dev, CCR0);
 	dscc4_release_ring(dpriv);
 err_out:
 	hdlc_close(hdlc);
@@ -1069,6 +1076,9 @@
 
 	dscc4_tx_reset(dpriv, dev);
 
+	scc_patchl(PowerUp | Vis, 0, dpriv, dev, CCR0);
+	scc_writel(0xffffffff, dpriv, dev, IMR);
+
 	hdlc_close(hdlc);
 	dscc4_release_ring(dpriv);
 
@@ -1076,6 +1086,10 @@
 	return 0;
 }
 
+/*
+ * Et*nc's PCISYNC only allows clock generation on first two ports.
+ * DSCC4 can do more. It depends on the manufacturer of the card.
+ */
 static inline int dscc4_check_clock_ability(int port)
 {
 	int ret = 0;
@@ -1087,6 +1101,49 @@
 	return ret;
 }
 
+/*
+ * DS1 p.137: "There are a total of 13 different clocking modes..."
+ *                                  ^^
+ * Design choices:
+ * - by default, assume a clock is provided on pin RxClk/TxClk (clock mode 0a).
+ *   Clock mode 3b _should_ work but the testing seems to make this point
+ *   dubious (DIY testing requires setting CCR0 at 0x00000033).
+ *   This is supposed to provide least surprise "DTE like" behavior.
+ * - if line rate is specified, clocks are assumed to be locally generated.
+ *   A quartz must be available (on pin XTAL1). Modes 6b/7b are used. Choosing
+ *   between these it automagically done according on the required frequency
+ *   scaling. Of course some rounding may take place.
+ * - no high speed mode (40Mb/s). May be trivial to do but I don't have an
+ *   appropriate external clocking device for testing.
+ * - no time-slot/clock mode 5: shameless lazyness.
+ *
+ * The clock signals wiring can be (is ?) manufacturer dependant. Good luck.
+ *
+ * BIG FAT WARNING: if the device isn't provided enough clocking signal, it
+ * won't pass the init sequence. For example, straight back-to-back DTE without
+ * external clock will fail when dscc4_open() (<- 'ifconfig hdlcx xxx') is
+ * called.
+ *
+ * Typos lurk in datasheet (missing divier in clock mode 7a figure 51 p.153
+ * DS0 for example)
+ * 
+ * Clock mode related bits of CCR0:
+ *     +------------ TOE: output TxClk (0b/2b/3a/3b/6b/7a/7b only)
+ *     | +---------- SSEL: sub-mode select 0 -> a, 1 -> b
+ *     | | +-------- High Speed: say 0
+ *     | | | +-+-+-- Clock Mode: 0..7
+ *     | | | | | |
+ * -+-+-+-+-+-+-+-+
+ * x|x|5|4|3|2|1|0| lower bits
+ *
+ * Division factor of BRR: k = (N+1)x2^M (total divider = 16xk in mode 6b)
+ *            +-+-+-+------------------ M (0..15)
+ *            | | | |     +-+-+-+-+-+-- N (0..63)
+ *    0 0 0 0 | | | | 0 0 | | | | | |
+ * ...-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *    f|e|d|c|b|a|9|8|7|6|5|4|3|2|1|0| lower bits
+ *
+ */
 static int dscc4_set_clock(struct net_device *dev, u32 *bps, u32 *state)
 {
 	struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
@@ -1123,13 +1180,13 @@
 		}
 		brr = (m << 8) | n;
 		divider = n << m;
-		if (!(*state & 0x00000001)) /* Clock mode 6b */
+		if (!(*state & 0x00000001)) /* ?b mode mask => clock mode 6b */
 			divider <<= 4;
 		*bps = xtal / divider;
 	} else {
 		/*
 		 * External clock - DTE
-		 * "state" already reflects Clock mode 0a.
+		 * "state" already reflects Clock mode 0a (CCR0 = 0xzzzzzz00).
 		 * Nothing more to be done
 		 */
 		brr = 0;
@@ -1226,7 +1283,7 @@
 				dev->name, dpriv->settings.clock_rate, bps);
 		}
 	} else { /* DTE */
-		state = 0x80001000;
+		state |= PowerUp | Vis;
 		printk(KERN_DEBUG "%s: external RxClk (DTE)\n", dev->name);
 	}
 	scc_writel(state, dpriv, dev, CCR0);
@@ -1463,9 +1520,19 @@
 		}
 		if (state & Xpr) {
 			u32 scc_addr, ring;
+			int i;
+
+			/*
+			 * - the busy condition happens (sometimes);
+			 * - it doesn't seem to make the handler unreliable.
+			 */
+			for (i = 1; i; i <<= 1) {
+				if (!(scc_readl_star(dpriv, dev) & SccBusy))
+					break;
+			}
+			if (!i)
+				printk(KERN_INFO "%s busy in irq\n", dev->name);
 
-			if (scc_readl_star(dpriv, dev) & SccBusy)
-				printk(KERN_ERR "%s busy. Fatal\n", dev->name);
 			scc_addr = dev->base_addr + 0x0c*dpriv->dev_id;
 			/* Keep this order: IDT before IDR */
 			if (dpriv->flags & NeedIDT) {
@@ -1757,7 +1824,7 @@
 					(++i%TX_RING_SIZE)*sizeof(*tx_fd));
 	} while (i < TX_RING_SIZE);
 
-	if (dscc4_init_dummy_skb(dpriv) < 0)
+	if (dscc4_init_dummy_skb(dpriv) == NULL)
 		goto err_free_dma_tx;
 
 	memset(dpriv->rx_skbuff, 0, sizeof(struct sk_buff *)*RX_RING_SIZE);
@@ -1785,7 +1852,7 @@
 	return -ENOMEM;
 }
 
-static void __exit dscc4_remove_one(struct pci_dev *pdev)
+static void __devexit dscc4_remove_one(struct pci_dev *pdev)
 {
 	struct dscc4_pci_priv *ppriv;
 	struct dscc4_dev_priv *root;
@@ -1843,6 +1910,7 @@
 	return 0;
 }
 
+#ifndef MODULE
 static int __init dscc4_setup(char *str)
 {
 	int *args[] = { &debug, &quartz, NULL }, **p = args;
@@ -1853,8 +1921,9 @@
 }
 
 __setup("dscc4.setup=", dscc4_setup);
+#endif /* MODULE */
 
-static struct pci_device_id dscc4_pci_tbl[] __devinitdata = {
+static struct pci_device_id dscc4_pci_tbl[] = {
 	{ PCI_VENDOR_ID_SIEMENS, PCI_DEVICE_ID_SIEMENS_DSCC4,
 	        PCI_ANY_ID, PCI_ANY_ID, },
 	{ 0,}
@@ -1865,7 +1934,7 @@
 	.name		= DRV_NAME,
 	.id_table	= dscc4_pci_tbl,
 	.probe		= dscc4_init_one,
-	.remove		= dscc4_remove_one,
+	.remove		= __devexit_p(dscc4_remove_one),
 };
 
 static int __init dscc4_init_module(void)
diff -urN linux-2.4.22-bk28/drivers/pci/pci.c linux-2.4.22-bk29/drivers/pci/pci.c
--- linux-2.4.22-bk28/drivers/pci/pci.c	2003-10-06 02:50:25.000000000 -0700
+++ linux-2.4.22-bk29/drivers/pci/pci.c	2003-10-06 02:50:31.000000000 -0700
@@ -921,7 +921,7 @@
  *
  * RETURNS: An appriopriate -ERRNO error value on eror, or zero for success.
  */
-int
+static int
 pci_generic_prep_mwi(struct pci_dev *dev)
 {
 	u8 cacheline_size;
@@ -2164,7 +2164,6 @@
 EXPORT_SYMBOL(pci_set_master);
 EXPORT_SYMBOL(pci_set_mwi);
 EXPORT_SYMBOL(pci_clear_mwi);
-EXPORT_SYMBOL(pci_generic_prep_mwi);
 EXPORT_SYMBOL(pci_set_dma_mask);
 EXPORT_SYMBOL(pci_dac_set_dma_mask);
 EXPORT_SYMBOL(pci_assign_resource);
diff -urN linux-2.4.22-bk28/drivers/usb/Config.in linux-2.4.22-bk29/drivers/usb/Config.in
--- linux-2.4.22-bk28/drivers/usb/Config.in	2003-08-25 04:44:42.000000000 -0700
+++ linux-2.4.22-bk29/drivers/usb/Config.in	2003-10-06 02:50:31.000000000 -0700
@@ -110,4 +110,7 @@
       dep_tristate '  Alcatel Speedtouch USB support' CONFIG_USB_SPEEDTOUCH $CONFIG_ATM $CONFIG_USB
    fi
 fi
+
+source drivers/usb/gadget/Config.in
+
 endmenu
diff -urN linux-2.4.22-bk28/drivers/usb/devio.c linux-2.4.22-bk29/drivers/usb/devio.c
--- linux-2.4.22-bk28/drivers/usb/devio.c	2003-10-06 02:50:26.000000000 -0700
+++ linux-2.4.22-bk29/drivers/usb/devio.c	2003-10-06 02:50:31.000000000 -0700
@@ -1114,7 +1114,7 @@
                        usb_driver_release_interface (driver, ifp);
                        up (&driver->serialize);
                } else
-                       return -ENODATA;
+                       retval = -ENODATA;
                break;
 
        /* let kernel drivers try to (re)bind to the interface */
diff -urN linux-2.4.22-bk28/drivers/usb/gadget/Config.in linux-2.4.22-bk29/drivers/usb/gadget/Config.in
--- linux-2.4.22-bk28/drivers/usb/gadget/Config.in	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-bk29/drivers/usb/gadget/Config.in	2003-10-06 02:50:31.000000000 -0700
@@ -0,0 +1,69 @@
+#
+# USB device-side configuration
+# for 2.4 kbuild, drivers/usb/gadget/Config.in
+#
+# Long term, this likely doesn't all belong in one directory
+# Plan to split it up eventually.
+#
+mainmenu_option next_comment
+comment 'Support for USB gadgets'
+
+bool 'Support for USB Gadgets' CONFIG_USB_GADGET
+if [ "$CONFIG_USB_GADGET" = "y" ]; then
+  
+  #
+  # really want _exactly one_ device controller driver at a time,
+  # since they control compile options for gadget drivers.
+  #
+  comment 'USB Peripheral Controller Drivers'
+  
+  # assume all the dependencies may be undefined ("== true", yeech)
+  if [ "$CONFIG_PCI" = "y" ] ; then
+    tristate '  NetChip 2280 support' CONFIG_USB_NET2280
+    define_tristate CONFIG_USB_GADGET_CONTROLLER $CONFIG_USB_NET2280
+  fi
+
+  if [ "$CONFIG_USB_GADGET_CONTROLLER" = "y" -o "$CONFIG_USB_GADGET_CONTROLLER" = "m" ] ; then
+
+  #
+  # no reason not to enable more than one gadget driver module, but
+  # for static linking that would make no sense since the usb model
+  # has exactly one of these upstream connections and only one
+  # lowest-level driver can control it.
+  #
+  # gadget drivers are compiled to work on specific hardware, since
+  #
+  # (a) gadget driver need hardware-specific configuration, like what
+  #     endpoint names and numbers to use, maxpacket sizes, etc
+  #
+  # (b) specific hardware features like iso endpoints may be required
+  #
+  comment 'USB Gadget Drivers'
+
+  dep_tristate '  Gadget Zero (DEVELOPMENT)' CONFIG_USB_ZERO $CONFIG_USB_GADGET_CONTROLLER
+  if [ "$CONFIG_USB_ZERO" = "y" -o "$CONFIG_USB_ZERO" = "m" ]; then
+      if [ "$CONFIG_USB_NET2280" = "y" -o "$CONFIG_USB_NET2280" = "m" ]; then
+  	define_bool CONFIG_USB_ZERO_NET2280 y
+      fi
+      if [ "$CONFIG_USB_PXA2XX" = "y" -o "$CONFIG_USB_PXA2XX" = "m" ]; then
+  	define_bool CONFIG_USB_ZERO_PXA2XX y
+      fi
+      # ...
+  fi
+  
+  dep_tristate '  Ethernet Gadget (EXPERIMENTAL)' CONFIG_USB_ETH $CONFIG_USB_GADGET_CONTROLLER
+  if [ "$CONFIG_USB_ETH" = "y" -o "$CONFIG_USB_ETH" = "m" ]; then
+      if [ "$CONFIG_USB_NET2280" = "y" -o "$CONFIG_USB_NET2280" = "m" ]; then
+  	define_bool CONFIG_USB_ETH_NET2280 y
+      fi
+      if [ "$CONFIG_USB_PXA2XX" = "y" -o "$CONFIG_USB_PXA2XX" = "m" ]; then
+  	define_bool CONFIG_USB_ETH_PXA2XX y
+      fi
+      # ...
+  fi
+  
+  # ... or other gadget drivers:  printer class, storage, hid, etc ...
+
+  fi
+fi
+endmenu
diff -urN linux-2.4.22-bk28/drivers/usb/gadget/Makefile linux-2.4.22-bk29/drivers/usb/gadget/Makefile
--- linux-2.4.22-bk28/drivers/usb/gadget/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-bk29/drivers/usb/gadget/Makefile	2003-10-06 02:50:31.000000000 -0700
@@ -0,0 +1,31 @@
+#
+# Makefile for USB peripheral controller and gadget drivers
+# for kbuild 2.4
+#
+
+# for static linking
+O_TARGET	:= built-in.o
+
+list-multi			:= g_zero.o g_ether.o
+
+obj-$(CONFIG_USB_NET2280)	+= net2280.o
+
+# only one of these may be statically linked ...
+controller-$(CONFIG_USB_NET2280)	+= net2280.o
+
+# ... and only one of these, too; kbuild/kconfig don't help though.
+g_zero-objs			:= zero.o usbstring.o
+obj-$(CONFIG_USB_ZERO)		+= g_zero.o
+
+g_ether-objs			:= ether.o usbstring.o
+obj-$(CONFIG_USB_ETH)		+= g_ether.o
+
+
+export-objs :=			$(controller-y) $(controller-m)
+
+include $(TOPDIR)/Rules.make
+
+g_zero.o: $(g_zero-objs)
+	$(LD) -r -o $@ $(g_zero-objs)
+g_ether.o: $(g_ether-objs)
+	$(LD) -r -o $@ $(g_ether-objs)
diff -urN linux-2.4.22-bk28/drivers/usb/gadget/ether.c linux-2.4.22-bk29/drivers/usb/gadget/ether.c
--- linux-2.4.22-bk28/drivers/usb/gadget/ether.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-bk29/drivers/usb/gadget/ether.c	2003-10-06 02:50:31.000000000 -0700
@@ -0,0 +1,1879 @@
+/*
+ * ether.c -- Ethernet gadget driver, with CDC and non-CDC options
+ *
+ * Copyright (C) 2003 David Brownell
+ *
+ * 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
+ */
+
+
+#define DEBUG 1
+// #define VERBOSE
+
+#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/uts.h>
+#include <linux/version.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+
+#include <linux/random.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Ethernet gadget driver -- with CDC and non-CDC options
+ *
+ * CDC Ethernet is the standard USB solution for sending Ethernet frames
+ * using USB.  Real hardware tends to use the same framing protocol but look
+ * different for control features.  And Microsoft pushes their own approach
+ * (RNDIS) instead of the standard.
+ *
+ * There's some hardware that can't talk CDC.  We make that hardware
+ * implement a "minimalist" vendor-agnostic CDC core:  same framing, but
+ * link-level setup only requires activating the configuration.
+ */
+
+#define DRIVER_DESC		"Ethernet Gadget"
+#define DRIVER_VERSION		"Bastille Day 2003"
+
+static const char shortname [] = "ether";
+static const char driver_desc [] = DRIVER_DESC;
+
+#define MIN_PACKET	sizeof(struct ethhdr)
+#define	MAX_PACKET	ETH_DATA_LEN	/* biggest packet we'll rx/tx */
+#define RX_EXTRA	20		/* guard against rx overflows */
+
+/* FIXME allow high speed jumbograms */
+
+/*-------------------------------------------------------------------------*/
+
+#ifndef container_of
+#define container_of		list_entry
+#endif
+
+/* 2.5 modified and renamed these */
+
+#define work_struct		tq_struct
+#define INIT_WORK		INIT_TQUEUE
+#define schedule_work		schedule_task
+#define	flush_scheduled_work	flush_scheduled_tasks
+
+/*-------------------------------------------------------------------------*/
+
+struct eth_dev {
+	spinlock_t		lock;
+	struct usb_gadget	*gadget;
+	struct usb_request	*req;		/* for control responses */
+
+	u8			config;
+	struct usb_ep		*in_ep, *out_ep, *status_ep;
+	const struct usb_endpoint_descriptor
+				*in, *out, *status;
+	struct list_head	tx_reqs, rx_reqs;
+
+	struct net_device	*net;
+	struct net_device_stats	stats;
+	atomic_t		tx_qlen;
+
+	struct work_struct	work;
+	unsigned long		todo;
+#define	WORK_RX_MEMORY		0
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* This driver keeps a variable number of requests queued, more at
+ * high speeds.  (Numbers are just educated guesses, untuned.)
+ * Shrink the queue if memory is tight, or make it bigger to
+ * handle bigger traffic bursts between IRQs (assuming hw dma queues)
+ */
+
+static unsigned qmult = 4;
+
+#define HS_FACTOR	5
+
+#define qlen(gadget) \
+	(qmult*((gadget->speed == USB_SPEED_HIGH) ? HS_FACTOR : 1))
+
+/* defer IRQs on highspeed TX */
+#define TX_DELAY	8
+
+
+MODULE_PARM (qmult, "i");
+MODULE_PARM_DESC (qmult, "rx/tx buffering factor");
+
+/*-------------------------------------------------------------------------*/
+
+/* Thanks to NetChip Technologies for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
+ */
+#define DRIVER_VENDOR_NUM	0x0525		/* NetChip */
+#define DRIVER_PRODUCT_NUM	0xa4a1		/* Linux-USB Ethernet Gadget */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * hardware-specific configuration, controlled by which device
+ * controller driver was configured.
+ *
+ * CHIP ... hardware identifier
+ * DRIVER_VERSION_NUM ... alerts the host side driver to differences
+ * EP0_MAXPACKET ... controls packetization of control requests
+ * EP_*_NAME ... which endpoints do we use for which purpose?
+ * EP_*_NUM ... numbers for them (often limited by hardware)
+ * HIGHSPEED ... define if ep0 and descriptors need high speed support
+ * MAX_USB_POWER ... define if we use other than 100 mA bus current
+ * SELFPOWER ... unless we can run on bus power, USB_CONFIG_ATT_SELFPOWER
+ * WAKEUP ... if hardware supports remote wakeup AND we will issue the
+ * 	usb_gadget_wakeup() call to initiate it, USB_CONFIG_ATT_WAKEUP
+ *
+ * hw_optimize(gadget) ... for any hardware tweaks we want to kick in
+ * 	before we enable our endpoints
+ *
+ * add other defines for other portability issues, like hardware that
+ * for some reason doesn't handle full speed bulk maxpacket of 64.
+ */
+
+#define DEV_CONFIG_VALUE	3	/* some hardware cares */
+
+/* #undef on hardware that can't implement CDC */
+#define	DEV_CONFIG_CDC
+
+/*
+ * NetChip 2280, PCI based.
+ *
+ * use DMA with fat fifos for all data traffic, PIO for the status channel
+ * where its 64 byte maxpacket ceiling is no issue.
+ *
+ * performance note:  only PIO needs per-usb-packet IRQs (ep0, ep-e, ep-f)
+ * otherwise IRQs are per-Ethernet-packet unless TX_DELAY and chaining help.
+ */
+#ifdef	CONFIG_USB_ETH_NET2280
+#define CHIP			"net2280"
+#define DRIVER_VERSION_NUM	0x0111
+#define EP0_MAXPACKET		64
+static const char EP_OUT_NAME [] = "ep-a";
+#define EP_OUT_NUM	1
+static const char EP_IN_NAME [] = "ep-b";
+#define EP_IN_NUM	2
+static const char EP_STATUS_NAME [] = "ep-f";
+#define EP_STATUS_NUM	3
+#define HIGHSPEED
+/* specific hardware configs could be bus-powered */
+#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
+/* supports remote wakeup, but this driver doesn't */
+
+extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode);
+
+static inline void hw_optimize (struct usb_gadget *gadget)
+{
+	/* we can have bigger ep-a/ep-b fifos (2KB each, 4 USB packets
+	 * for highspeed bulk) because we're not using ep-c/ep-d.
+	 */
+	net2280_set_fifo_mode (gadget, 1);
+}
+#endif
+
+/*
+ * PXA-2xx UDC:  widely used in second gen Linux-capable ARM PDAs
+ * and other products.
+ *
+ * multiple interfaces (or altsettings) aren't usable.  so this hardware
+ * can't implement CDC, which needs both capabilities.
+ */
+#ifdef	CONFIG_USB_ETH_PXA2XX
+#undef	DEV_CONFIG_CDC
+#define CHIP			"pxa2xx"
+#define DRIVER_VERSION_NUM	0x0113
+#define EP0_MAXPACKET		16
+static const char EP_OUT_NAME [] = "ep2out-bulk";
+#define EP_OUT_NUM	2
+static const char EP_IN_NAME [] = "ep1in-bulk";
+#define EP_IN_NUM	1
+/* doesn't support bus-powered operation */
+#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
+/* supports remote wakeup, but this driver doesn't */
+
+/* no hw optimizations to apply */
+#define hw_optimize(g) do {} while (0);
+#endif
+
+/*
+ * SA-1100 UDC:  widely used in first gen Linux-capable PDAs.
+ *
+ * can't have a notification endpoint, since there are only the two
+ * bulk-capable ones.  the CDC spec allows that.
+ */
+#ifdef	CONFIG_USB_ETH_SA1100
+#define CHIP			"sa1100"
+#define DRIVER_VERSION_NUM	0x0115
+#define EP0_MAXPACKET		8
+static const char EP_OUT_NAME [] = "ep1out-bulk";
+#define EP_OUT_NUM	1
+static const char EP_IN_NAME [] = "ep2in-bulk";
+#define EP_IN_NUM	2
+// EP_STATUS_NUM is undefined
+/* doesn't support bus-powered operation */
+#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
+/* doesn't support remote wakeup? */
+
+/* no hw optimizations to apply */
+#define hw_optimize(g) do {} while (0);
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#ifndef EP0_MAXPACKET
+#	error Configure some USB peripheral controller driver!
+#endif
+
+/* We normally expect hardware that can talk CDC.  That involves
+ * using multiple interfaces and altsettings, and maybe a status
+ * interrupt.  Driver binding to be done according to USB-IF class,
+ * though you can use different VENDOR and PRODUCT numbers if you
+ * want (and they're officially assigned).
+ * 
+ * For hardware that can't talk CDC, we use the same vendor ID that
+ * ARM Linux has used for ethernet-over-usb, both with sa1100 and
+ * with pxa250.  We're protocol-compatible, if the host-side drivers
+ * use the endpoint descriptors.  DRIVER_VERSION_NUM is nonzero, so
+ * drivers that need to hard-wire endpoint numbers have a hook.
+ */
+#ifdef	DEV_CONFIG_CDC
+#define	DEV_CONFIG_CLASS	USB_CLASS_COMM
+#else	
+#define	DEV_CONFIG_CLASS	USB_CLASS_VENDOR_SPEC
+#undef	EP_STATUS_NUM
+#undef	DRIVER_VENDOR_NUM
+#undef	DRIVER_PRODUCT_NUM
+#define	DRIVER_VENDOR_NUM	0x049f
+#define	DRIVER_PRODUCT_NUM	0x505a
+#endif /* CONFIG_CDC_ETHER */
+
+/* power usage is config specific.
+ * hardware that supports remote wakeup defaults to disabling it.
+ */
+
+#ifndef	SELFPOWER
+/* default: say we rely on bus power */
+#define SELFPOWER	0
+/* else:
+ * - SELFPOWER value must be USB_CONFIG_ATT_SELFPOWER
+ * - MAX_USB_POWER may be nonzero.
+ */
+#endif
+
+#ifndef	MAX_USB_POWER
+/* any hub supports this steady state bus power consumption */
+#define MAX_USB_POWER	100	/* mA */
+#endif
+
+#ifndef	WAKEUP
+/* default: this driver won't do remote wakeup */
+#define WAKEUP		0
+/* else value must be USB_CONFIG_ATT_WAKEUP */
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#define xprintk(d,level,fmt,args...) \
+	printk(level "%s %s: " fmt , shortname , (d)->gadget->dev.bus_id , \
+		## args)
+
+#ifdef DEBUG
+#undef DEBUG
+#define DEBUG(dev,fmt,args...) \
+	xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DEBUG(dev,fmt,args...) \
+	do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE
+#define VDEBUG	DEBUG
+#else
+#define VDEBUG(dev,fmt,args...) \
+	do { } while (0)
+#endif /* DEBUG */
+
+#define ERROR(dev,fmt,args...) \
+	xprintk(dev , KERN_ERR , fmt , ## args)
+#define WARN(dev,fmt,args...) \
+	xprintk(dev , KERN_WARNING , fmt , ## args)
+#define INFO(dev,fmt,args...) \
+	xprintk(dev , KERN_INFO , fmt , ## args)
+
+/*-------------------------------------------------------------------------*/
+
+/* USB DRIVER HOOKUP (to the hardware driver, below us), mostly
+ * ep0 implementation:  descriptors, config management, setup().
+ * also optional class-specific notification interrupt transfer.
+ */
+
+/*
+ * DESCRIPTORS ... most are static, but strings and (full) configuration
+ * descriptors are built on demand.  Notice how most of the cdc descriptors
+ * aren't needed in the "minimalist" mode.
+ */
+
+#define STRING_MANUFACTURER		1
+#define STRING_PRODUCT			2
+#define STRING_ETHADDR			3
+#define STRING_DATA			4
+#define STRING_CONTROL			5
+
+#define USB_BUFSIZ	256		/* holds our biggest descriptor */
+
+/*
+ * This device advertises one configuration.
+ */
+static const struct usb_device_descriptor
+device_desc = {
+	.bLength =		sizeof device_desc,
+	.bDescriptorType =	USB_DT_DEVICE,
+
+	.bcdUSB =		__constant_cpu_to_le16 (0x0200),
+
+	.bDeviceClass =		DEV_CONFIG_CLASS,
+	.bDeviceSubClass =	0,
+	.bDeviceProtocol =	0,
+	.bMaxPacketSize0 =	EP0_MAXPACKET,
+
+	.idVendor =		__constant_cpu_to_le16 (DRIVER_VENDOR_NUM),
+	.idProduct =		__constant_cpu_to_le16 (DRIVER_PRODUCT_NUM),
+	.bcdDevice =		__constant_cpu_to_le16 (DRIVER_VERSION_NUM),
+	.iManufacturer =	STRING_MANUFACTURER,
+	.iProduct =		STRING_PRODUCT,
+	.bNumConfigurations =	1,
+};
+
+static const struct usb_config_descriptor
+eth_config = {
+	.bLength =		sizeof eth_config,
+	.bDescriptorType =	USB_DT_CONFIG,
+
+	/* compute wTotalLength on the fly */
+#ifdef	DEV_CONFIG_CDC
+	.bNumInterfaces =	2,
+#else
+	.bNumInterfaces =	1,
+#endif
+	.bConfigurationValue =	DEV_CONFIG_VALUE,
+	.iConfiguration =	STRING_PRODUCT,
+	.bmAttributes =		USB_CONFIG_ATT_ONE | SELFPOWER | WAKEUP,
+	.bMaxPower =		(MAX_USB_POWER + 1) / 2,
+};
+
+#ifdef	DEV_CONFIG_CDC
+
+/*
+ * Compared to the "minimalist" non-CDC model, the CDC model adds
+ * three class descriptors, two interface descrioptors, and a status
+ * endpoint.  Both have a "data" interface and two bulk endpoints.
+ * There are also differences in how control requests are handled.
+ */
+
+/* master comm interface optionally has a status notification endpoint */
+
+static const struct usb_interface_descriptor
+control_intf = {
+	.bLength =		sizeof control_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bInterfaceNumber =	0,
+#ifdef	EP_STATUS_NUM
+	.bNumEndpoints =	1,
+#else
+	.bNumEndpoints =	0,
+#endif
+	.bInterfaceClass =	USB_CLASS_COMM,
+	.bInterfaceSubClass =	6,	/* ethernet control model */
+	.bInterfaceProtocol =	0,
+	.iInterface =		STRING_CONTROL,
+};
+
+/* "Header Functional Descriptor" from CDC spec  5.2.3.1 */
+struct header_desc {
+	u8	bLength;
+	u8	bDescriptorType;
+	u8	bDescriptorSubType;
+
+	u16	bcdCDC;
+} __attribute__ ((packed));
+
+static const struct header_desc header_desc = {
+	.bLength =		sizeof header_desc,
+	.bDescriptorType =	0x24,
+	.bDescriptorSubType =	0,
+
+	.bcdCDC =		__constant_cpu_to_le16 (0x0110),
+};
+
+/* "Union Functional Descriptor" from CDC spec 5.2.3.X */
+struct union_desc {
+	u8	bLength;
+	u8	bDescriptorType;
+	u8	bDescriptorSubType;
+
+	u8	bMasterInterface0;
+	u8	bSlaveInterface0;
+	/* ... and there could be other slave interfaces */
+} __attribute__ ((packed));
+
+static const struct union_desc union_desc = {
+	.bLength =		sizeof union_desc,
+	.bDescriptorType =	0x24,
+	.bDescriptorSubType =	6,
+
+	.bMasterInterface0 =	0,	/* index of control interface */
+	.bSlaveInterface0 =	1,	/* index of DATA interface */
+};
+
+/* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */
+struct ether_desc {
+	u8	bLength;
+	u8	bDescriptorType;
+	u8	bDescriptorSubType;
+
+	u8	iMACAddress;
+	u32	bmEthernetStatistics;
+	u16	wMaxSegmentSize;
+	u16	wNumberMCFilters;
+	u8	bNumberPowerFilters;
+} __attribute__ ((packed));
+
+static const struct ether_desc ether_desc = {
+	.bLength =		sizeof ether_desc,
+	.bDescriptorType =	0x24,
+	.bDescriptorSubType =	0x0f,
+
+	/* this descriptor actually adds value, surprise! */
+	.iMACAddress =		STRING_ETHADDR,
+	.bmEthernetStatistics =	__constant_cpu_to_le32 (0), /* no statistics */
+	.wMaxSegmentSize =	__constant_cpu_to_le16 (MAX_PACKET + ETH_HLEN),
+	.wNumberMCFilters =	__constant_cpu_to_le16 (0),
+	.bNumberPowerFilters =	0,
+};
+
+#ifdef	EP_STATUS_NUM
+
+/* include the status endpoint if we can, even though it's optional.
+ *
+ * some drivers (like current Linux cdc-ether!) "need" it to exist even
+ * if they ignore the connect/disconnect notifications that real aether
+ * can provide.  more advanced cdc configurations might want to support
+ * encapsulated commands (vendor-specific, using control-OUT).
+ */
+ 
+#define LOG2_STATUS_INTERVAL_MSEC	6
+#define STATUS_BYTECOUNT		16	/* 8 byte header + data */
+static const struct usb_endpoint_descriptor
+fs_status_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_STATUS_NUM | USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16 (STATUS_BYTECOUNT),
+	.bInterval =		1 << LOG2_STATUS_INTERVAL_MSEC,
+};
+#endif
+
+/* the default data interface has no endpoints ... */
+
+static const struct usb_interface_descriptor
+data_nop_intf = {
+	.bLength =		sizeof data_nop_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bInterfaceNumber =	1,
+	.bAlternateSetting =	0,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	USB_CLASS_CDC_DATA,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0,
+	.iInterface =		STRING_DATA,
+};
+
+/* ... but the "real" data interface has two full speed bulk endpoints */
+
+static const struct usb_interface_descriptor
+data_intf = {
+	.bLength =		sizeof data_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bInterfaceNumber =	1,
+	.bAlternateSetting =	1,
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_CDC_DATA,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0,
+	.iInterface =		STRING_DATA,
+};
+#else
+
+/*
+ * "Minimalist" non-CDC option is a simple vendor-neutral model that most
+ * full speed controllers can handle:  one interface, two bulk endpoints.
+ */
+
+static const struct usb_interface_descriptor
+data_intf = {
+	.bLength =		sizeof data_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bInterfaceNumber =	0,
+	.bAlternateSetting =	0,
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0,
+	.iInterface =		STRING_DATA,
+};
+
+#endif	/* DEV_CONFIG_CDC */
+
+
+static const struct usb_endpoint_descriptor
+fs_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_IN_NUM | USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16 (64),
+};
+
+static const struct usb_endpoint_descriptor
+fs_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_OUT_NUM,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16 (64),
+};
+
+#ifdef	HIGHSPEED
+
+/*
+ * usb 2.0 devices need to expose both high speed and full speed
+ * descriptors, unless they only run at full speed.
+ */
+
+#ifdef	EP_STATUS_NUM
+static const struct usb_endpoint_descriptor
+hs_status_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_STATUS_NUM | USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16 (STATUS_BYTECOUNT),
+	.bInterval =		LOG2_STATUS_INTERVAL_MSEC + 3,
+};
+#endif
+
+static const struct usb_endpoint_descriptor
+hs_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_IN_NUM | USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16 (512),
+	.bInterval =		1,
+};
+
+static const struct usb_endpoint_descriptor
+hs_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_OUT_NUM,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16 (512),
+	.bInterval =		1,
+};
+
+static const struct usb_qualifier_descriptor
+dev_qualifier = {
+	.bLength =		sizeof dev_qualifier,
+	.bDescriptorType =	USB_DT_DEVICE_QUALIFIER,
+
+	.bcdUSB =		__constant_cpu_to_le16 (0x0200),
+	.bDeviceClass =		DEV_CONFIG_CLASS,
+
+	/* assumes ep0 uses the same value for both speeds ... */
+	.bMaxPacketSize0 =	EP0_MAXPACKET,
+
+	.bNumConfigurations =	1,
+};
+
+/* maxpacket and other transfer characteristics vary by speed. */
+#define ep_desc(g,hs,fs) (((g)->speed==USB_SPEED_HIGH)?(hs):(fs))
+
+#else
+
+/* if there's no high speed support, maxpacket doesn't change. */
+#define ep_desc(g,hs,fs) fs
+
+#endif	/* !HIGHSPEED */
+
+/*-------------------------------------------------------------------------*/
+
+/* descriptors that are built on-demand */
+
+#ifdef	DEV_CONFIG_CDC
+/* address that the host will use ... usually assigned at random */
+static char				ethaddr [2 * ETH_ALEN + 1];
+#endif
+
+/* static strings, in iso 8859/1 */
+static struct usb_string		strings [] = {
+	{ STRING_MANUFACTURER,	UTS_SYSNAME " " UTS_RELEASE "/" CHIP, },
+	{ STRING_PRODUCT,	driver_desc, },
+#ifdef	DEV_CONFIG_CDC
+	{ STRING_ETHADDR,	ethaddr, },
+	{ STRING_CONTROL,	"CDC Communications Control", },
+#endif
+	{ STRING_DATA,		"Ethernet Data", },
+	{  }		/* end of list */
+};
+
+static struct usb_gadget_strings	stringtab = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings,
+};
+
+/*
+ * one config, two interfaces:  control, data.
+ * complications: class descriptors, and an altsetting.
+ */
+static int
+config_buf (enum usb_device_speed speed, u8 *buf, u8 type, unsigned index)
+{
+	const unsigned	config_len = USB_DT_CONFIG_SIZE
+#ifdef DEV_CONFIG_CDC
+				+ 2 * USB_DT_INTERFACE_SIZE
+				+ sizeof header_desc
+				+ sizeof union_desc
+				+ sizeof ether_desc
+#ifdef	EP_STATUS_NUM
+				+ USB_DT_ENDPOINT_SIZE
+#endif
+#endif /* DEV_CONFIG_CDC */
+				+ USB_DT_INTERFACE_SIZE
+				+ 2 * USB_DT_ENDPOINT_SIZE;
+
+#ifdef HIGHSPEED
+	int		hs;
+#endif
+	/* a single configuration must always be index 0 */
+	if (index > 0)
+		return -EINVAL;
+	if (config_len > USB_BUFSIZ)
+		return -EDOM;
+
+	/* config (or other speed config) */
+	memcpy (buf, &eth_config, USB_DT_CONFIG_SIZE);
+	buf [1] = type;
+	((struct usb_config_descriptor *) buf)->wTotalLength
+		= __constant_cpu_to_le16 (config_len);
+	buf += USB_DT_CONFIG_SIZE;
+#ifdef	HIGHSPEED
+	hs = (speed == USB_SPEED_HIGH);
+	if (type == USB_DT_OTHER_SPEED_CONFIG)
+		hs = !hs;
+#endif
+
+#ifdef DEV_CONFIG_CDC
+	/* control interface, class descriptors, optional status endpoint */
+	memcpy (buf, &control_intf, USB_DT_INTERFACE_SIZE);
+	buf += USB_DT_INTERFACE_SIZE;
+
+	memcpy (buf, &header_desc, sizeof header_desc);
+	buf += sizeof header_desc;
+	memcpy (buf, &union_desc, sizeof union_desc);
+	buf += sizeof union_desc;
+	memcpy (buf, &ether_desc, sizeof ether_desc);
+	buf += sizeof ether_desc;
+
+#ifdef	EP_STATUS_NUM
+#ifdef	HIGHSPEED
+	if (hs)
+		memcpy (buf, &hs_status_desc, USB_DT_ENDPOINT_SIZE);
+	else
+#endif	/* HIGHSPEED */
+		memcpy (buf, &fs_status_desc, USB_DT_ENDPOINT_SIZE);
+	buf += USB_DT_ENDPOINT_SIZE;
+#endif	/* EP_STATUS_NUM */
+
+	/* default data altsetting has no endpoints */
+	memcpy (buf, &data_nop_intf, USB_DT_INTERFACE_SIZE);
+	buf += USB_DT_INTERFACE_SIZE;
+#endif /* DEV_CONFIG_CDC */
+
+	/* the "real" data interface has two endpoints */
+	memcpy (buf, &data_intf, USB_DT_INTERFACE_SIZE);
+	buf += USB_DT_INTERFACE_SIZE;
+#ifdef HIGHSPEED
+	if (hs) {
+		memcpy (buf, &hs_source_desc, USB_DT_ENDPOINT_SIZE);
+		buf += USB_DT_ENDPOINT_SIZE;
+		memcpy (buf, &hs_sink_desc, USB_DT_ENDPOINT_SIZE);
+		buf += USB_DT_ENDPOINT_SIZE;
+	} else
+#endif
+	{
+		memcpy (buf, &fs_source_desc, USB_DT_ENDPOINT_SIZE);
+		buf += USB_DT_ENDPOINT_SIZE;
+		memcpy (buf, &fs_sink_desc, USB_DT_ENDPOINT_SIZE);
+		buf += USB_DT_ENDPOINT_SIZE;
+	}
+
+	return config_len;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void eth_start (struct eth_dev *dev, int gfp_flags);
+static int alloc_requests (struct eth_dev *dev, unsigned n, int gfp_flags);
+
+static int
+set_ether_config (struct eth_dev *dev, int gfp_flags)
+{
+	int			result = 0;
+	struct usb_ep		*ep;
+	struct usb_gadget	*gadget = dev->gadget;
+
+	gadget_for_each_ep (ep, gadget) {
+		const struct usb_endpoint_descriptor	*d;
+
+#ifdef	DEV_CONFIG_CDC
+		/* With CDC,  the host isn't allowed to use these two data
+		 * endpoints in the default altsetting for the interface.
+		 * so we don't activate them yet.
+		 */
+
+		/* one endpoint writes data back IN to the host */
+		if (strcmp (ep->name, EP_IN_NAME) == 0) {
+			d = ep_desc (gadget, &hs_source_desc, &fs_source_desc);
+			ep->driver_data = dev;
+			dev->in_ep = ep;
+			dev->in = d;
+			continue;
+
+		/* one endpoint just reads OUT packets */
+		} else if (strcmp (ep->name, EP_OUT_NAME) == 0) {
+			d = ep_desc (gadget, &hs_sink_desc, &fs_sink_desc);
+			ep->driver_data = dev;
+			dev->out_ep = ep;
+			dev->out = d;
+			continue;
+		}
+
+#ifdef	EP_STATUS_NUM
+		/* optional status/notification endpoint */
+		else if (strcmp (ep->name, EP_STATUS_NAME) == 0) {
+			d = ep_desc (gadget, &hs_status_desc, &fs_status_desc);
+			result = usb_ep_enable (ep, d);
+			if (result == 0) {
+				ep->driver_data = dev;
+				dev->status_ep = ep;
+				dev->status = d;
+				continue;
+			}
+		}
+#endif
+
+#else	/* !CONFIG_CDC_ETHER */
+
+		/* non-CDC is simpler:  if the device is there,
+		 * it's live with rx and tx endpoints.
+		 */
+		/* one endpoint writes data back IN to the host */
+		if (strcmp (ep->name, EP_IN_NAME) == 0) {
+			d = ep_desc (gadget, &hs_source_desc, &fs_source_desc);
+			result = usb_ep_enable (ep, d);
+			if (result == 0) {
+				ep->driver_data = dev;
+				dev->in_ep = ep;
+				dev->in = d;
+				continue;
+			}
+
+		/* one endpoint just reads OUT packets */
+		} else if (strcmp (ep->name, EP_OUT_NAME) == 0) {
+			d = ep_desc (gadget, &hs_sink_desc, &fs_sink_desc);
+			result = usb_ep_enable (ep, d);
+			if (result == 0) {
+				ep->driver_data = dev;
+				dev->out_ep = ep;
+				dev->out = d;
+				continue;
+			}
+		}
+
+#endif /* !CONFIG_CDC_ETHER */
+
+		/* ignore any other endpoints */
+		else
+			continue;
+
+		/* stop on error */
+		ERROR (dev, "can't enable %s, result %d\n", ep->name, result);
+		break;
+	}
+	if (!result && (!dev->in_ep || !dev->out_ep))
+		result = -ENODEV;
+
+	if (result == 0)
+		result = alloc_requests (dev, qlen (gadget), gfp_flags);
+
+#ifndef	DEV_CONFIG_CDC
+	if (result == 0) {
+		netif_carrier_on (dev->net);
+		if (netif_running (dev->net))
+			eth_start (dev, GFP_ATOMIC);
+	} else {
+		(void) usb_ep_disable (dev->in_ep);
+		dev->in_ep = 0;
+		dev->in = 0;
+		(void) usb_ep_disable (dev->out_ep);
+		dev->out_ep = 0;
+		dev->out = 0;
+	}
+#endif /* !CONFIG_CDC_ETHER */
+
+	if (result == 0)
+		DEBUG (dev, "qlen %d\n", qlen (gadget));
+
+	/* caller is responsible for cleanup on error */
+	return result;
+}
+
+static void eth_reset_config (struct eth_dev *dev)
+{
+	if (dev->config == 0)
+		return;
+
+	DEBUG (dev, "%s\n", __FUNCTION__);
+
+	netif_stop_queue (dev->net);
+	netif_carrier_off (dev->net);
+
+	/* just disable endpoints, forcing completion of pending i/o.
+	 * all our completion handlers free their requests in this case.
+	 */
+	if (dev->in_ep) {
+		usb_ep_disable (dev->in_ep);
+		dev->in_ep = 0;
+	}
+	if (dev->out_ep) {
+		usb_ep_disable (dev->out_ep);
+		dev->out_ep = 0;
+	}
+#ifdef	EP_STATUS_NUM
+	if (dev->status_ep) {
+		usb_ep_disable (dev->status_ep);
+		dev->status_ep = 0;
+	}
+#endif
+	dev->config = 0;
+}
+
+/* change our operational config.  must agree with the code
+ * that returns config descriptors, and altsetting code.
+ */
+static int
+eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags)
+{
+	int			result = 0;
+	struct usb_gadget	*gadget = dev->gadget;
+
+	if (number == dev->config)
+		return 0;
+
+#ifdef CONFIG_USB_ETH_SA1100
+	if (dev->config && atomic_read (&dev->tx_qlen) != 0) {
+		/* tx fifo is full, but we can't clear it...*/
+		INFO (dev, "can't change configurations\n");
+		return -ESPIPE;
+	}
+#endif
+	eth_reset_config (dev);
+	hw_optimize (gadget);
+
+	switch (number) {
+	case DEV_CONFIG_VALUE:
+		result = set_ether_config (dev, gfp_flags);
+		break;
+	default:
+		result = -EINVAL;
+		/* FALL THROUGH */
+	case 0:
+		return result;
+	}
+
+	if (result)
+		eth_reset_config (dev);
+	else {
+		char *speed;
+
+		switch (gadget->speed) {
+		case USB_SPEED_FULL:	speed = "full"; break;
+#ifdef HIGHSPEED
+		case USB_SPEED_HIGH:	speed = "high"; break;
+#endif
+		default: 		speed = "?"; break;
+		}
+
+		dev->config = number;
+		INFO (dev, "%s speed config #%d: %s\n", speed, number,
+				driver_desc);
+	}
+	return result;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef	EP_STATUS_NUM
+
+/* section 3.8.2 table 11 of the CDC spec lists Ethernet notifications */
+#define CDC_NOTIFY_NETWORK_CONNECTION	0x00	/* required; 6.3.1 */
+#define CDC_NOTIFY_SPEED_CHANGE		0x2a	/* required; 6.3.8 */
+
+struct cdc_notification {
+	u8	bmRequestType;
+	u8	bNotificationType;
+	u16	wValue;
+	u16	wIndex;
+	u16	wLength;
+
+	/* SPEED_CHANGE data looks like this */
+	u32	data [2];
+};
+
+static void eth_status_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	struct cdc_notification	*event = req->buf;
+	int			value = req->status;
+	struct eth_dev		*dev = ep->driver_data;
+
+	/* issue the second notification if host reads the first */
+	if (event->bNotificationType == CDC_NOTIFY_NETWORK_CONNECTION
+			&& value == 0) {
+		event->bmRequestType = 0xA1;
+		event->bNotificationType = CDC_NOTIFY_SPEED_CHANGE;
+		event->wValue = __constant_cpu_to_le16 (0);
+		event->wIndex = __constant_cpu_to_le16 (1);
+		event->wLength = __constant_cpu_to_le16 (8);
+
+		/* SPEED_CHANGE data is up/down speeds in bits/sec */
+		event->data [0] = event->data [1] =
+			(dev->gadget->speed == USB_SPEED_HIGH)
+				? (13 * 512 * 8 * 1000 * 8)
+				: (19 *  64 * 1 * 1000 * 8);
+
+		req->length = 16;
+		value = usb_ep_queue (ep, req, GFP_ATOMIC);
+		DEBUG (dev, "send SPEED_CHANGE --> %d\n", value);
+		if (value == 0)
+			return;
+	} else
+		DEBUG (dev, "event %02x --> %d\n",
+			event->bNotificationType, value);
+
+	/* free when done */
+	usb_ep_free_buffer (ep, req->buf, req->dma, 16);
+	usb_ep_free_request (ep, req);
+}
+
+static void issue_start_status (struct eth_dev *dev)
+{
+	struct usb_request	*req;
+	struct cdc_notification	*event;
+	int			value;
+ 
+	DEBUG (dev, "%s, flush old status first\n", __FUNCTION__);
+
+	/* flush old status
+	 *
+	 * FIXME ugly idiom, maybe we'd be better with just
+	 * a "cancel the whole queue" primitive since any
+	 * unlink-one primitive has way too many error modes.
+	 * here, we "know" toggle is already clear...
+	 */
+	usb_ep_disable (dev->status_ep);
+	usb_ep_enable (dev->status_ep, dev->status);
+
+	/* FIXME make these allocations static like dev->req */
+	req = usb_ep_alloc_request (dev->status_ep, GFP_ATOMIC);
+	if (req == 0) {
+		DEBUG (dev, "status ENOMEM\n");
+		return;
+	}
+	req->buf = usb_ep_alloc_buffer (dev->status_ep, 16,
+				&dev->req->dma, GFP_ATOMIC);
+	if (req->buf == 0) {
+		DEBUG (dev, "status buf ENOMEM\n");
+free_req:
+		usb_ep_free_request (dev->status_ep, req);
+		return;
+	}
+
+	/* 3.8.1 says to issue first NETWORK_CONNECTION, then
+	 * a SPEED_CHANGE.  could be useful in some configs.
+	 */
+	event = req->buf;
+	event->bmRequestType = 0xA1;
+	event->bNotificationType = CDC_NOTIFY_NETWORK_CONNECTION;
+	event->wValue = __constant_cpu_to_le16 (1);	/* connected */
+	event->wIndex = __constant_cpu_to_le16 (1);
+	event->wLength = 0;
+
+	req->length = 8;
+	req->complete = eth_status_complete;
+	value = usb_ep_queue (dev->status_ep, req, GFP_ATOMIC);
+	if (value < 0) {
+		DEBUG (dev, "status buf queue --> %d\n", value);
+		usb_ep_free_buffer (dev->status_ep,
+				req->buf, dev->req->dma, 16);
+		goto free_req;
+	}
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static void eth_setup_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	if (req->status || req->actual != req->length)
+		DEBUG ((struct eth_dev *) ep->driver_data,
+				"setup complete --> %d, %d/%d\n",
+				req->status, req->actual, req->length);
+}
+
+/* see section 3.8.2 table 10 of the CDC spec for more ethernet
+ * requests, mostly for filters (multicast, pm) and statistics
+ */
+#define CDC_SET_ETHERNET_PACKET_FILTER	0x43	/* required */
+
+/*
+ * The setup() callback implements all the ep0 functionality that's not
+ * handled lower down.  CDC has a number of less-common features:
+ *
+ *  - two interfaces:  control, and ethernet data
+ *  - data interface has two altsettings:  default, and active
+ *  - class-specific descriptors for the control interface
+ *  - a mandatory class-specific control request
+ */
+static int
+eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+	struct eth_dev		*dev = get_gadget_data (gadget);
+	struct usb_request	*req = dev->req;
+	int			value = -EOPNOTSUPP;
+
+	/* descriptors just go into the pre-allocated ep0 buffer,
+	 * while config change events may enable network traffic.
+	 */
+	switch (ctrl->bRequest) {
+
+	case USB_REQ_GET_DESCRIPTOR:
+		if (ctrl->bRequestType != USB_DIR_IN)
+			break;
+		switch (ctrl->wValue >> 8) {
+
+		case USB_DT_DEVICE:
+			value = min (ctrl->wLength, (u16) sizeof device_desc);
+			memcpy (req->buf, &device_desc, value);
+			break;
+#ifdef HIGHSPEED
+		case USB_DT_DEVICE_QUALIFIER:
+			value = min (ctrl->wLength, (u16) sizeof dev_qualifier);
+			memcpy (req->buf, &dev_qualifier, value);
+			break;
+
+		case USB_DT_OTHER_SPEED_CONFIG:
+			// FALLTHROUGH
+#endif /* HIGHSPEED */
+		case USB_DT_CONFIG:
+			value = config_buf (gadget->speed, req->buf,
+					ctrl->wValue >> 8,
+					ctrl->wValue & 0xff);
+			if (value >= 0)
+				value = min (ctrl->wLength, (u16) value);
+			break;
+
+		case USB_DT_STRING:
+			value = usb_gadget_get_string (&stringtab,
+					ctrl->wValue & 0xff, req->buf);
+			if (value >= 0)
+				value = min (ctrl->wLength, (u16) value);
+			break;
+		}
+		break;
+
+	case USB_REQ_SET_CONFIGURATION:
+		if (ctrl->bRequestType != 0)
+			break;
+		spin_lock (&dev->lock);
+		value = eth_set_config (dev, ctrl->wValue, GFP_ATOMIC);
+		spin_unlock (&dev->lock);
+		break;
+#ifdef	CONFIG_USB_ETH_PXA2XX
+	/* PXA UDC prevents us from using SET_INTERFACE in normal ways.
+	 * And it hides GET_CONFIGURATION and GET_INTERFACE too.
+	 */
+	case USB_REQ_SET_INTERFACE:
+		spin_lock (&dev->lock);
+		value = eth_set_config (dev, DEV_CONFIG_VALUE, GFP_ATOMIC);
+		spin_unlock (&dev->lock);
+		break;
+
+#else	/* hardware that that stays out of our way */
+	case USB_REQ_GET_CONFIGURATION:
+		if (ctrl->bRequestType != USB_DIR_IN)
+			break;
+		*(u8 *)req->buf = dev->config;
+		value = min (ctrl->wLength, (u16) 1);
+		break;
+
+	case USB_REQ_SET_INTERFACE:
+		if (ctrl->bRequestType != USB_RECIP_INTERFACE
+				|| !dev->config
+				|| ctrl->wIndex > 1)
+			break;
+		spin_lock (&dev->lock);
+		switch (ctrl->wIndex) {
+		case 0:		/* control/master intf */
+			if (ctrl->wValue != 0)
+				break;
+#ifdef	EP_STATUS_NUM
+			if (dev->status_ep) {
+				usb_ep_disable (dev->status_ep);
+				usb_ep_enable (dev->status_ep, dev->status);
+			}
+#endif
+			value = 0;
+			break;
+		case 1:		/* data intf */
+			if (ctrl->wValue > 1)
+				break;
+			usb_ep_disable (dev->in_ep);
+			usb_ep_disable (dev->out_ep);
+
+			/* CDC requires the data transfers not be done from
+			 * the default interface setting ... also, setting
+			 * the non-default interface clears filters etc.
+			 */
+			if (ctrl->wValue == 1) {
+				usb_ep_enable (dev->in_ep, dev->in);
+				usb_ep_enable (dev->out_ep, dev->out);
+				netif_carrier_on (dev->net);
+#ifdef	EP_STATUS_NUM
+				issue_start_status (dev);
+#endif
+				if (netif_running (dev->net))
+					eth_start (dev, GFP_ATOMIC);
+			} else {
+				netif_stop_queue (dev->net);
+				netif_carrier_off (dev->net);
+			}
+			value = 0;
+			break;
+		}
+		spin_unlock (&dev->lock);
+		break;
+	case USB_REQ_GET_INTERFACE:
+		if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)
+				|| !dev->config
+				|| ctrl->wIndex > 1)
+			break;
+
+		/* if carrier is on, data interface is active. */
+		*(u8 *)req->buf =
+			((ctrl->wIndex == 1) && netif_carrier_ok (dev->net))
+				? 1
+				: 0,
+		value = min (ctrl->wLength, (u16) 1);
+		break;
+#endif
+
+#ifdef DEV_CONFIG_CDC
+	case CDC_SET_ETHERNET_PACKET_FILTER:
+		/* see 6.2.30: no data, wIndex = interface,
+		 * wValue = packet filter bitmap
+		 */
+		if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE)
+				|| ctrl->wLength != 0
+				|| ctrl->wIndex > 1)
+		DEBUG (dev, "NOP packet filter %04x\n", ctrl->wValue);
+		/* NOTE: table 62 has 5 filter bits to reduce traffic,
+		 * and we "must" support multicast and promiscuous.
+		 * this NOP implements a bad filter...
+		 */
+		value = 0;
+		break;
+#endif /* DEV_CONFIG_CDC */
+
+	default:
+		VDEBUG (dev,
+			"unknown control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			ctrl->wValue, ctrl->wIndex, ctrl->wLength);
+	}
+
+	/* respond with data transfer before status phase? */
+	if (value >= 0) {
+		req->length = value;
+		value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0) {
+			DEBUG (dev, "ep_queue --> %d\n", value);
+			req->status = 0;
+			eth_setup_complete (gadget->ep0, req);
+		}
+	}
+
+	/* host either stalls (value < 0) or reports success */
+	return value;
+}
+
+static void
+eth_disconnect (struct usb_gadget *gadget)
+{
+	struct eth_dev		*dev = get_gadget_data (gadget);
+	unsigned long		flags;
+
+	spin_lock_irqsave (&dev->lock, flags);
+	netif_stop_queue (dev->net);
+	netif_carrier_off (dev->net);
+	eth_reset_config (dev);
+	spin_unlock_irqrestore (&dev->lock, flags);
+
+	/* next we may get setup() calls to enumerate new connections;
+	 * or an unbind() during shutdown (including removing module).
+	 */
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* NETWORK DRIVER HOOKUP (to the layer above this driver) */
+
+static int eth_change_mtu (struct net_device *net, int new_mtu)
+{
+	struct eth_dev	*dev = (struct eth_dev *) net->priv;
+
+	if (new_mtu <= MIN_PACKET || new_mtu > MAX_PACKET)
+		return -ERANGE;
+	/* no zero-length packet read wanted after mtu-sized packets */
+	if (((new_mtu + sizeof (struct ethhdr)) % dev->in_ep->maxpacket) == 0)
+		return -EDOM;
+	net->mtu = new_mtu;
+	return 0;
+}
+
+static struct net_device_stats *eth_get_stats (struct net_device *net)
+{
+	return &((struct eth_dev *) net->priv)->stats;
+}
+
+static int eth_ethtool_ioctl (struct net_device *net, void *useraddr)
+{
+	struct eth_dev	*dev = (struct eth_dev *) net->priv;
+	u32		cmd;
+
+	if (get_user (cmd, (u32 *)useraddr))
+		return -EFAULT;
+	switch (cmd) {
+
+	case ETHTOOL_GDRVINFO: {	/* get driver info */
+		struct ethtool_drvinfo		info;
+
+		memset (&info, 0, sizeof info);
+		info.cmd = ETHTOOL_GDRVINFO;
+		strncpy (info.driver, shortname, sizeof info.driver);
+		strncpy (info.version, DRIVER_VERSION, sizeof info.version);
+		strncpy (info.fw_version, CHIP, sizeof info.fw_version);
+		strncpy (info.bus_info, dev->gadget->dev.bus_id,
+			sizeof info.bus_info);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+		}
+
+	case ETHTOOL_GLINK: {		/* get link status */
+		struct ethtool_value	edata = { ETHTOOL_GLINK };
+
+		edata.data = (dev->gadget->speed != USB_SPEED_UNKNOWN);
+		if (copy_to_user (useraddr, &edata, sizeof (edata)))
+			return -EFAULT;
+		return 0;
+		}
+
+	}
+	/* Note that the ethtool user space code requires EOPNOTSUPP */
+	return -EOPNOTSUPP;
+}
+
+static int eth_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
+{
+	switch (cmd) {
+	case SIOCETHTOOL:
+		return eth_ethtool_ioctl (net, (void *)rq->ifr_data);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static void defer_kevent (struct eth_dev *dev, int flag)
+{
+	if (test_and_set_bit (flag, &dev->todo))
+		return;
+	if (!schedule_work (&dev->work))
+		ERROR (dev, "kevent %d may have been dropped\n", flag);
+	else
+		DEBUG (dev, "kevent %d scheduled\n", flag);
+}
+
+static void rx_complete (struct usb_ep *ep, struct usb_request *req);
+
+static int
+rx_submit (struct eth_dev *dev, struct usb_request *req, int gfp_flags)
+{
+	struct sk_buff		*skb;
+	int			retval = 0;
+	size_t			size;
+
+	size = (sizeof (struct ethhdr) + dev->net->mtu + RX_EXTRA);
+
+	if ((skb = alloc_skb (size, gfp_flags)) == 0) {
+		DEBUG (dev, "no rx skb\n");
+		defer_kevent (dev, WORK_RX_MEMORY);
+		list_add (&req->list, &dev->rx_reqs);
+		return -ENOMEM;
+	}
+
+	req->buf = skb->data;
+	req->length = size;
+	req->complete = rx_complete;
+	req->context = skb;
+
+	retval = usb_ep_queue (dev->out_ep, req, gfp_flags);
+	if (retval == -ENOMEM)
+		defer_kevent (dev, WORK_RX_MEMORY);
+	if (retval) {
+		DEBUG (dev, "rx submit --> %d\n", retval);
+		dev_kfree_skb_any (skb);
+		list_add (&req->list, &dev->rx_reqs);
+	}
+	return retval;
+}
+
+static void rx_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	struct sk_buff	*skb = req->context;
+	struct eth_dev	*dev = ep->driver_data;
+	int		status = req->status;
+
+	switch (status) {
+
+	/* normal completion */
+	case 0:
+		skb_put (skb, req->actual);
+		if (MIN_PACKET > skb->len
+				|| skb->len > (MAX_PACKET + ETH_HLEN)) {
+			dev->stats.rx_errors++;
+			dev->stats.rx_length_errors++;
+			DEBUG (dev, "rx length %d\n", skb->len);
+			break;
+		}
+
+		skb->dev = dev->net;
+		skb->protocol = eth_type_trans (skb, dev->net);
+		dev->stats.rx_packets++;
+		dev->stats.rx_bytes += skb->len;
+
+		/* no buffer copies needed, unless hardware can't
+		 * use skb buffers.
+		 */
+		status = netif_rx (skb);
+		skb = 0;
+		break;
+
+	/* software-driven interface shutdown */
+	case -ECONNRESET:		// unlink
+	case -ESHUTDOWN:		// disconnect etc
+		VDEBUG (dev, "rx shutdown, code %d\n", status);
+		goto quiesce;
+
+	/* for hardware automagic (such as pxa) */
+	case -ECONNABORTED:		// endpoint reset
+		DEBUG (dev, "rx %s reset\n", ep->name);
+		defer_kevent (dev, WORK_RX_MEMORY);
+quiesce:
+		dev_kfree_skb_any (skb);
+		goto clean;
+
+	/* data overrun */
+	case -EOVERFLOW:
+		dev->stats.rx_over_errors++;
+		// FALLTHROUGH
+	    
+	default:
+		dev->stats.rx_errors++;
+		DEBUG (dev, "rx status %d\n", status);
+		break;
+	}
+
+	if (skb)
+		dev_kfree_skb_any (skb);
+	if (!netif_running (dev->net)) {
+clean:
+		list_add (&req->list, &dev->rx_reqs);
+		req = 0;
+	}
+	if (req)
+		rx_submit (dev, req, GFP_ATOMIC);
+}
+
+static int prealloc (struct list_head *list, struct usb_ep *ep,
+			unsigned n, int gfp_flags)
+{
+	unsigned		i;
+	struct usb_request	*req;
+
+	if (!n)
+		return -ENOMEM;
+
+	/* queue/recycle up to N requests */
+	i = n;
+	list_for_each_entry (req, list, list) {
+		if (i-- == 0)
+			goto extra;
+	}
+	while (i--) {
+		req = usb_ep_alloc_request (ep, gfp_flags);
+		if (!req)
+			return list_empty (list) ? -ENOMEM : 0;
+		list_add (&req->list, list);
+	}
+	return 0;
+
+extra:
+	/* free extras */
+	for (;;) {
+		struct list_head	*next;
+
+		next = req->list.next;
+		list_del (&req->list);
+		usb_ep_free_request (ep, req);
+
+		if (next == list)
+			break;
+
+		req = container_of (next, struct usb_request, list);
+	}
+	return 0;
+}
+
+static int alloc_requests (struct eth_dev *dev, unsigned n, int gfp_flags)
+{
+	int status;
+
+	status = prealloc (&dev->tx_reqs, dev->in_ep, n, gfp_flags);
+	if (status < 0)
+		goto fail;
+	status = prealloc (&dev->rx_reqs, dev->out_ep, n, gfp_flags);
+	if (status < 0)
+		goto fail;
+	return 0;
+fail:
+	DEBUG (dev, "can't alloc requests\n");
+	return status;
+}
+
+static void rx_fill (struct eth_dev *dev, int gfp_flags)
+{
+	struct usb_request	*req;
+
+	clear_bit (WORK_RX_MEMORY, &dev->todo);
+
+	/* fill unused rxq slots with some skb */
+	while (!list_empty (&dev->rx_reqs)) {
+		req = container_of (dev->rx_reqs.next,
+				struct usb_request, list);
+		list_del_init (&req->list);
+		if (rx_submit (dev, req, gfp_flags) < 0) {
+			defer_kevent (dev, WORK_RX_MEMORY);
+			return;
+		}
+	}
+}
+
+static void eth_work (void *_dev)
+{
+	struct eth_dev		*dev = _dev;
+
+	if (test_bit (WORK_RX_MEMORY, &dev->todo)) {
+		if (netif_running (dev->net))
+			rx_fill (dev, GFP_KERNEL);
+		else
+			clear_bit (WORK_RX_MEMORY, &dev->todo);
+	}
+
+	if (dev->todo)
+		DEBUG (dev, "work done, flags = 0x%lx\n", dev->todo);
+}
+
+static void tx_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	struct sk_buff	*skb = req->context;
+	struct eth_dev	*dev = ep->driver_data;
+
+	switch (req->status) {
+	default:
+		dev->stats.tx_errors++;
+		VDEBUG (dev, "tx err %d\n", req->status);
+		/* FALLTHROUGH */
+	case -ECONNRESET:		// unlink
+	case -ESHUTDOWN:		// disconnect etc
+		break;
+	case 0:
+		dev->stats.tx_bytes += skb->len;
+	}
+	dev->stats.tx_packets++;
+
+	list_add (&req->list, &dev->tx_reqs);
+	dev_kfree_skb_any (skb);
+
+	atomic_dec (&dev->tx_qlen);
+	if (netif_carrier_ok (dev->net))
+		netif_wake_queue (dev->net);
+}
+
+static int eth_start_xmit (struct sk_buff *skb, struct net_device *net)
+{
+	struct eth_dev		*dev = (struct eth_dev *) net->priv;
+	int			length = skb->len;
+	int			retval;
+	struct usb_request	*req = 0;
+
+	req = container_of (dev->tx_reqs.next, struct usb_request, list);
+	list_del (&req->list);
+	if (list_empty (&dev->tx_reqs))
+		netif_stop_queue (net);
+
+	/* no buffer copies needed, unless the network stack did it
+	 * or the hardware can't use skb buffers.
+	 */
+	req->buf = skb->data;
+	req->context = skb;
+	req->complete = tx_complete;
+
+#ifdef	CONFIG_USB_ETH_SA1100
+	/* don't demand zlp (req->zero) support from all hardware */
+	if ((length % dev->in_ep->maxpacket) == 0)
+		length++;
+#else
+	/* use zlp framing on tx for strict CDC-Ether conformance,
+	 * though any robust network rx path ignores extra padding.
+	 */
+	req->zero = 1;
+#endif
+	req->length = length;
+
+#ifdef	HIGHSPEED
+	/* throttle highspeed IRQ rate back slightly */
+	req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH)
+		? ((atomic_read (&dev->tx_qlen) % TX_DELAY) != 0)
+		: 0;
+#endif
+
+	retval = usb_ep_queue (dev->in_ep, req, GFP_ATOMIC);
+	switch (retval) {
+	default:
+		DEBUG (dev, "tx queue err %d\n", retval);
+		break;
+	case 0:
+		net->trans_start = jiffies;
+		atomic_inc (&dev->tx_qlen);
+	}
+
+	if (retval) {
+		dev->stats.tx_dropped++;
+		dev_kfree_skb_any (skb);
+		if (list_empty (&dev->tx_reqs))
+			netif_start_queue (net);
+		list_add (&req->list, &dev->tx_reqs);
+	}
+	return 0;
+}
+
+static void eth_start (struct eth_dev *dev, int gfp_flags)
+{
+	DEBUG (dev, "%s\n", __FUNCTION__);
+
+	/* fill the rx queue */
+	rx_fill (dev, gfp_flags);
+
+	/* and open the tx floodgates */ 
+	atomic_set (&dev->tx_qlen, 0);
+	netif_wake_queue (dev->net);
+}
+
+static int eth_open (struct net_device *net)
+{
+	struct eth_dev		*dev = (struct eth_dev *) net->priv;
+
+	DEBUG (dev, "%s\n", __FUNCTION__);
+	if (netif_carrier_ok (dev->net))
+		eth_start (dev, GFP_KERNEL);
+	return 0;
+}
+
+static int eth_stop (struct net_device *net)
+{
+	struct eth_dev		*dev = (struct eth_dev *) net->priv;
+
+	DEBUG (dev, "%s\n", __FUNCTION__);
+	netif_stop_queue (net);
+
+	DEBUG (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
+		dev->stats.rx_packets, dev->stats.tx_packets, 
+		dev->stats.rx_errors, dev->stats.tx_errors
+		);
+
+	/* ensure there are no more active requests */
+	if (dev->gadget->speed != USB_SPEED_UNKNOWN) {
+		usb_ep_disable (dev->in_ep);
+		usb_ep_disable (dev->out_ep);
+		if (netif_carrier_ok (dev->net)) {
+			DEBUG (dev, "host still using in/out endpoints\n");
+			usb_ep_enable (dev->in_ep, dev->in);
+			usb_ep_enable (dev->out_ep, dev->out);
+		}
+#ifdef	EP_STATUS_NUM
+		usb_ep_disable (dev->status_ep);
+		usb_ep_enable (dev->status_ep, dev->status);
+#endif
+	}
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+eth_unbind (struct usb_gadget *gadget)
+{
+	struct eth_dev		*dev = get_gadget_data (gadget);
+	struct usb_request	*req;
+
+	DEBUG (dev, "unbind\n");
+
+	/* we've already been disconnected ... no i/o is active */
+	if (dev->req) {
+		usb_ep_free_buffer (gadget->ep0,
+				dev->req->buf, dev->req->dma,
+				USB_BUFSIZ);
+		usb_ep_free_request (gadget->ep0, dev->req);
+		dev->req = 0;
+	}
+
+	while (!list_empty (&dev->tx_reqs)) {
+		req = container_of (dev->tx_reqs.next,
+					struct usb_request, list);
+		list_del (&req->list);
+		usb_ep_free_request (dev->in_ep, req);
+	}
+	while (!list_empty (&dev->rx_reqs)) {
+		req = container_of (dev->rx_reqs.next,
+					struct usb_request, list);
+		list_del (&req->list);
+		usb_ep_free_request (dev->out_ep, req);
+	}
+
+	unregister_netdev (dev->net);
+	dev_put (dev->net);
+
+	/* assuming we used keventd, it must quiesce too */
+	flush_scheduled_work ();
+	set_gadget_data (gadget, 0);
+}
+
+static int
+eth_bind (struct usb_gadget *gadget)
+{
+	struct eth_dev		*dev;
+	struct net_device	*net;
+	int			status = -ENOMEM;
+#ifdef	DEV_CONFIG_CDC
+	u8			node_id [ETH_ALEN];
+
+	/* just one upstream link at a time */
+	if (ethaddr [0] != 0)
+		return -ENODEV;
+#endif
+
+ 	net = alloc_etherdev (sizeof *dev);
+ 	if (!net)
+		return status;
+	dev = net->priv;
+	spin_lock_init (&dev->lock);
+	INIT_WORK (&dev->work, eth_work, dev);
+	INIT_LIST_HEAD (&dev->tx_reqs);
+	INIT_LIST_HEAD (&dev->rx_reqs);
+
+	/* network device setup */
+	dev->net = net;
+	SET_MODULE_OWNER (net);
+	net->priv = dev;
+	strcpy (net->name, "usb%d");
+	ether_setup (net);
+
+	/* one random address for the gadget device ... both of these could
+	 * reasonably come from an id prom or a module parameter.
+	 */
+	get_random_bytes (net->dev_addr, ETH_ALEN);
+	net->dev_addr [0] &= 0xfe;	// clear multicast bit
+	net->dev_addr [0] |= 0x02;	// set local assignment bit (IEEE802)
+
+#ifdef	DEV_CONFIG_CDC
+	/* ... another address for the host, on the other end of the
+	 * link, gets exported through CDC (see CDC spec table 41)
+	 */
+	get_random_bytes (node_id, sizeof node_id);
+	node_id [0] &= 0xfe;	// clear multicast bit
+	node_id [0] |= 0x02;    // set local assignment bit (IEEE802)
+	snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X",
+		node_id [0], node_id [1], node_id [2],
+		node_id [3], node_id [4], node_id [5]);
+#endif
+
+	net->change_mtu = eth_change_mtu;
+	net->get_stats = eth_get_stats;
+	net->hard_start_xmit = eth_start_xmit;
+	net->open = eth_open;
+	net->stop = eth_stop;
+	// watchdog_timeo, tx_timeout ...
+	// set_multicast_list
+	net->do_ioctl = eth_ioctl;
+
+	/* preallocate control response and buffer */
+	dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
+	if (!dev->req)
+		goto fail;
+	dev->req->complete = eth_setup_complete;
+	dev->req->buf = usb_ep_alloc_buffer (gadget->ep0, USB_BUFSIZ,
+				&dev->req->dma, GFP_KERNEL);
+	if (!dev->req->buf) {
+		usb_ep_free_request (gadget->ep0, dev->req);
+		goto fail;
+	}
+
+	/* finish hookup to lower layer ... */
+	dev->gadget = gadget;
+	set_gadget_data (gadget, dev);
+	gadget->ep0->driver_data = dev;
+	INFO (dev, "%s, " CHIP ", version: " DRIVER_VERSION "\n", driver_desc);
+#ifdef	DEV_CONFIG_CDC
+	INFO (dev, "CDC host enet %s\n", ethaddr);
+#endif
+	
+	/* two kinds of host-initiated state changes:
+	 *  - iff DATA transfer is active, carrier is "on"
+	 *  - tx queueing enabled if open *and* carrier is "on"
+	 */
+	netif_stop_queue (dev->net);
+	netif_carrier_off (dev->net);
+
+ 	// SET_NETDEV_DEV (dev->net, &gadget->dev);
+ 	status = register_netdev (dev->net);
+ 	if (status == 0)
+ 		return status;
+fail:
+	eth_unbind (gadget);
+	return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_gadget_driver eth_driver = {
+#ifdef HIGHSPEED
+	.speed		= USB_SPEED_HIGH,
+#else
+	.speed		= USB_SPEED_FULL,
+#endif
+	.function	= (char *) driver_desc,
+	.bind		= eth_bind,
+	.unbind		= eth_unbind,
+
+	.setup		= eth_setup,
+	.disconnect	= eth_disconnect,
+
+	.driver 	= {
+		.name		= (char *) shortname,
+		// .shutdown = ...
+		// .suspend = ...
+		// .resume = ...
+	},
+};
+
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_AUTHOR ("David Brownell");
+MODULE_LICENSE ("GPL");
+
+
+static int __init init (void)
+{
+	return usb_gadget_register_driver (&eth_driver);
+}
+module_init (init);
+
+static void __exit cleanup (void)
+{
+	usb_gadget_unregister_driver (&eth_driver);
+}
+module_exit (cleanup);
+
diff -urN linux-2.4.22-bk28/drivers/usb/gadget/net2280.c linux-2.4.22-bk29/drivers/usb/gadget/net2280.c
--- linux-2.4.22-bk28/drivers/usb/gadget/net2280.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-bk29/drivers/usb/gadget/net2280.c	2003-10-06 02:50:31.000000000 -0700
@@ -0,0 +1,2731 @@
+/*
+ * Driver for the NetChip 2280 USB device controller.
+ * Specs and errata are available from <http://www.netchip.com>.
+ *
+ * NetChip Technology Inc. supported the development of this driver.
+ *
+ *
+ * CODE STATUS HIGHLIGHTS
+ *
+ * Used with a gadget driver like "zero.c" this enumerates fine to Windows
+ * or Linux hosts; handles disconnect, reconnect, and reset, for full or
+ * high speed operation; and passes USB-IF "chapter 9" tests.
+ *
+ * Handles standard stress loads from the Linux "usbtest" driver, with
+ * either DMA (default) or PIO (use_dma=n) used for ep-{a,b,c,d}.  Testing
+ * with "ttcp" (and the "ether.c" driver) behaves nicely too.
+ *
+ * DMA is enabled by default.  Drivers using transfer queues might use
+ * DMA chaining to remove IRQ latencies between transfers.  (Except when
+ * short OUT transfers happen.)  Drivers can use the req->no_interrupt
+ * hint to completely eliminate some IRQs, if a later IRQ is guaranteed
+ * and DMA chaining is enabled.
+ *
+ * Note that almost all the errata workarounds here are only needed for
+ * rev1 chips.  Rev1a silicon (0110) fixes almost all of them.
+ */
+
+#define USE_DMA_CHAINING
+
+
+/*
+ * Copyright (C) 2003 David Brownell
+ * Copyright (C) 2003 NetChip Technologies
+ *
+ * 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
+ */
+
+#define DEBUG	1
+// #define	VERBOSE		/* extra debug messages (success too) */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/version.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>
+
+
+#define	DRIVER_DESC		"NetChip 2280 USB Peripheral Controller"
+#define	DRIVER_VERSION		"Bastille Day 2003"
+
+#define	DMA_ADDR_INVALID	(~(dma_addr_t)0)
+#define	EP_DONTUSE		13	/* nonzero */
+
+#define USE_RDK_LEDS		/* GPIO pins control three LEDs */
+
+
+static const char driver_name [] = "net2280";
+static const char driver_desc [] = DRIVER_DESC;
+
+static const char ep0name [] = "ep0";
+static const char *ep_name [] = {
+	ep0name,
+	"ep-a", "ep-b", "ep-c", "ep-d",
+	"ep-e", "ep-f",
+};
+
+static int use_dma = 1;
+
+/* mode 0 == ep-{a,b,c,d} 1K fifo each
+ * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable
+ * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable
+ */
+static ushort fifo_mode = 0;
+
+MODULE_PARM (use_dma, "i");
+MODULE_PARM_DESC (use_dma, "true to use dma controllers");
+
+MODULE_PARM (fifo_mode, "h");
+MODULE_PARM_DESC (fifo_mode, "net2280 fifo mode");
+
+
+#define	DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
+
+#if defined(USE_SYSFS_DEBUG_FILES) || defined (DEBUG)
+static char *type_string (u8 bmAttributes)
+{
+	switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) {
+	case USB_ENDPOINT_XFER_BULK:	return "bulk";
+	case USB_ENDPOINT_XFER_ISOC:	return "iso";
+	case USB_ENDPOINT_XFER_INT:	return "intr";
+	};
+	return "control";
+}
+#endif
+
+#include "net2280.h"
+
+#define valid_bit	__constant_cpu_to_le32 (1 << VALID_BIT)
+#define dma_done_ie	__constant_cpu_to_le32 (1 << DMA_DONE_INTERRUPT_ENABLE)
+
+/*-------------------------------------------------------------------------*/
+
+static int
+net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+	struct net2280		*dev;
+	struct net2280_ep	*ep;
+	u32			max, tmp;
+	unsigned long		flags;
+
+	ep = container_of (_ep, struct net2280_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;
+
+	/* erratum 0119 workaround ties up an endpoint number */
+	if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE)
+		return -EDOM;
+
+	/* sanity check ep-e/ep-f since their fifos are small */
+	max = le16_to_cpu (desc->wMaxPacketSize) & 0x1fff;
+	if (ep->num > 4 && max > 64)
+		return -ERANGE;
+
+	spin_lock_irqsave (&dev->lock, flags);
+	_ep->maxpacket = max & 0x7ff;
+	ep->desc = desc;
+
+	/* ep_reset() has already been called */
+	ep->stopped = 0;
+
+	/* set speed-dependent max packet; may kick in high bandwidth */
+	set_idx_reg (dev->regs, REG_EP_MAXPKT (dev, ep->num), max);
+
+	/* FIFO lines can't go to different packets.  PIO is ok, so
+	 * use it instead of troublesome (non-bulk) multi-packet DMA.
+	 */
+	if (ep->is_in && ep->dma && (max % 4) != 0) {
+		DEBUG (ep->dev, "%s, no IN dma for maxpacket %d\n",
+			ep->ep.name, ep->ep.maxpacket);
+		ep->dma = 0;
+	}
+
+	/* set type, direction, address; reset fifo counters */
+	writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat);
+	tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
+	if (tmp == USB_ENDPOINT_XFER_INT) {
+		/* not just because of erratum 0105; avoid ever
+		 * kicking in the "toggle-irrelevant" mode.
+		 */
+		tmp = USB_ENDPOINT_XFER_BULK;
+	} else if (tmp == USB_ENDPOINT_XFER_BULK) {
+		/* catch some particularly blatant driver bugs */
+		if ((dev->gadget.speed == USB_SPEED_HIGH
+					&& max != 512)
+				|| (dev->gadget.speed == USB_SPEED_FULL
+					&& max > 64))
+			return -ERANGE;
+	}
+	ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC) ? 1 : 0;
+	tmp <<= ENDPOINT_TYPE;
+	tmp |= desc->bEndpointAddress;
+	tmp |= (4 << ENDPOINT_BYTE_COUNT);	/* default full fifo lines */
+	tmp |= 1 << ENDPOINT_ENABLE;
+	wmb ();
+
+	/* for OUT transfers, block the rx fifo until a read is posted */
+	ep->is_in = (tmp & USB_DIR_IN) != 0;
+	if (!ep->is_in)
+		writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+
+	writel (tmp, &ep->regs->ep_cfg);
+
+#ifdef NET2280_DMA_OUT_WORKAROUND
+	if (!ep->is_in)
+		ep->dma = 0;
+#endif
+
+	/* enable irqs */
+	if (!ep->dma) {				/* pio, per-packet */
+		tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0);
+		writel (tmp, &dev->regs->pciirqenb0);
+
+		tmp = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE)
+			| (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE)
+			| readl (&ep->regs->ep_irqenb);
+		writel (tmp, &ep->regs->ep_irqenb);
+	} else {				/* dma, per-request */
+		tmp = (1 << (8 + ep->num));	/* completion */
+		tmp |= readl (&dev->regs->pciirqenb1);
+		writel (tmp, &dev->regs->pciirqenb1);
+
+		/* for short OUT transfers, dma completions can't
+		 * advance the queue; do it pio-style, by hand.
+		 * NOTE erratum 0112 workaround #2
+		 */
+		if ((desc->bEndpointAddress & USB_DIR_IN) == 0) {
+			tmp = (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE);
+			writel (tmp, &ep->regs->ep_irqenb);
+
+			tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0);
+			writel (tmp, &dev->regs->pciirqenb0);
+		}
+	}
+
+	tmp = desc->bEndpointAddress;
+	DEBUG (dev, "enabled %s (ep%d%s-%s) %s max %04x\n",
+		_ep->name, tmp & 0x0f, DIR_STRING (tmp),
+		type_string (desc->bmAttributes),
+		ep->dma ? "dma" : "pio", max);
+
+	/* pci writes may still be posted */
+	spin_unlock_irqrestore (&dev->lock, flags);
+	return 0;
+}
+
+static int handshake (u32 *ptr, u32 mask, u32 done, int usec)
+{
+	u32	result;
+
+	do {
+		result = readl (ptr);
+		if (result == ~(u32)0)		/* "device unplugged" */
+			return -ENODEV;
+		result &= mask;
+		if (result == done)
+			return 0;
+		udelay (1);
+		usec--;
+	} while (usec > 0);
+	return -ETIMEDOUT;
+}
+
+static struct usb_ep_ops net2280_ep_ops;
+
+static void ep_reset (struct net2280_regs *regs, struct net2280_ep *ep)
+{
+	u32		tmp;
+
+	ep->desc = 0;
+	INIT_LIST_HEAD (&ep->queue);
+
+	ep->ep.maxpacket = ~0;
+	ep->ep.ops = &net2280_ep_ops;
+
+	/* disable the dma, irqs, endpoint... */
+	if (ep->dma) {
+		writel (0, &ep->dma->dmactl);
+		writel (  (1 << DMA_SCATTER_GATHER_DONE_INTERRUPT)
+			| (1 << DMA_TRANSACTION_DONE_INTERRUPT)
+			| (1 << DMA_ABORT)
+			, &ep->dma->dmastat);
+
+		tmp = readl (&regs->pciirqenb0);
+		tmp &= ~(1 << ep->num);
+		writel (tmp, &regs->pciirqenb0);
+	} else {
+		tmp = readl (&regs->pciirqenb1);
+		tmp &= ~(1 << (8 + ep->num));	/* completion */
+		writel (tmp, &regs->pciirqenb1);
+	}
+	writel (0, &ep->regs->ep_irqenb);
+
+	/* init to our chosen defaults, notably so that we NAK OUT
+	 * packets until the driver queues a read (+note erratum 0112)
+	 */
+	writel (  (1 << SET_NAK_OUT_PACKETS_MODE)
+		| (1 << SET_NAK_OUT_PACKETS)
+		| (1 << CLEAR_EP_HIDE_STATUS_PHASE)
+		| (1 << CLEAR_INTERRUPT_MODE)
+		| (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)
+		| (1 << CLEAR_ENDPOINT_TOGGLE)
+		| (1 << CLEAR_ENDPOINT_HALT)
+		, &ep->regs->ep_rsp);
+
+	/* scrub most status bits, and flush any fifo state */
+	writel (  (1 << TIMEOUT)
+		| (1 << USB_STALL_SENT)
+		| (1 << USB_IN_NAK_SENT)
+		| (1 << USB_IN_ACK_RCVD)
+		| (1 << USB_OUT_PING_NAK_SENT)
+		| (1 << USB_OUT_ACK_SENT)
+		| (1 << FIFO_OVERFLOW)
+		| (1 << FIFO_UNDERFLOW)
+		| (1 << FIFO_FLUSH)
+		| (1 << SHORT_PACKET_OUT_DONE_INTERRUPT)
+		| (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)
+		| (1 << DATA_PACKET_RECEIVED_INTERRUPT)
+		| (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
+		| (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
+		| (1 << DATA_IN_TOKEN_INTERRUPT)
+		, &ep->regs->ep_stat);
+
+	/* fifo size is handled separately */
+}
+
+static void nuke (struct net2280_ep *);
+
+static int net2280_disable (struct usb_ep *_ep)
+{
+	struct net2280_ep	*ep;
+	unsigned long		flags;
+
+	ep = container_of (_ep, struct net2280_ep, ep);
+	if (!_ep || !ep->desc || _ep->name == ep0name)
+		return -EINVAL;
+
+	spin_lock_irqsave (&ep->dev->lock, flags);
+	nuke (ep);
+	ep_reset (ep->dev->regs, ep);
+
+	VDEBUG (ep->dev, "disabled %s %s\n",
+			ep->dma ? "dma" : "pio", _ep->name);
+
+	/* synch memory views with the device */
+	(void) readl (&ep->regs->ep_cfg);
+
+	if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4)
+		ep->dma = &ep->dev->dma [ep->num - 1];
+
+	spin_unlock_irqrestore (&ep->dev->lock, flags);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_request *
+net2280_alloc_request (struct usb_ep *_ep, int gfp_flags)
+{
+	struct net2280_ep	*ep;
+	struct net2280_request	*req;
+
+	if (!_ep)
+		return 0;
+	ep = container_of (_ep, struct net2280_ep, ep);
+
+	req = kmalloc (sizeof *req, gfp_flags);
+	if (!req)
+		return 0;
+
+	memset (req, 0, sizeof *req);
+	req->req.dma = DMA_ADDR_INVALID;
+	INIT_LIST_HEAD (&req->queue);
+
+	/* this dma descriptor may be swapped with the previous dummy */
+	if (ep->dma) {
+		struct net2280_dma	*td;
+
+		td = pci_pool_alloc (ep->dev->requests, gfp_flags,
+				&req->td_dma);
+		if (!td) {
+			kfree (req);
+			return 0;
+		}
+		td->dmacount = 0;	/* not VALID */
+		td->dmaaddr = __constant_cpu_to_le32 (DMA_ADDR_INVALID);
+		req->td = td;
+	}
+	return &req->req;
+}
+
+static void
+net2280_free_request (struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct net2280_ep	*ep;
+	struct net2280_request	*req;
+
+	ep = container_of (_ep, struct net2280_ep, ep);
+	if (!_ep || !_req)
+		return;
+
+	req = container_of (_req, struct net2280_request, req);
+	WARN_ON (!list_empty (&req->queue));
+	if (req->td)
+		pci_pool_free (ep->dev->requests, req->td, req->td_dma);
+	kfree (req);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#undef USE_KMALLOC
+
+/* many common platforms have dma-coherent caches, which means that it's
+ * safe to use kmalloc() memory for all i/o buffers without using any
+ * cache flushing calls.  (unless you're trying to share cache lines
+ * between dma and non-dma activities, which is a slow idea in any case.)
+ *
+ * other platforms need more care, with 2.5 having a moderately general
+ * solution (which falls down for allocations smaller than one page)
+ * that improves significantly on the 2.4 PCI allocators by removing
+ * the restriction that memory never be freed in_interrupt().
+ */
+#if	defined(CONFIG_X86)
+#define USE_KMALLOC
+
+#elif	defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE)
+#define USE_KMALLOC
+
+/* FIXME there are other cases, including an x86-64 one ...  */
+#endif
+
+/* allocating buffers this way eliminates dma mapping overhead, which
+ * on some platforms will mean eliminating a per-io buffer copy.  with
+ * some kinds of system caches, further tweaks may still be needed.
+ */
+static void *
+net2280_alloc_buffer (
+	struct usb_ep		*_ep,
+	unsigned		bytes,
+	dma_addr_t		*dma,
+	int			gfp_flags
+)
+{
+	void			*retval;
+	struct net2280_ep	*ep;
+
+	ep = container_of (_ep, struct net2280_ep, ep);
+	if (!_ep)
+		return 0;
+
+	*dma = DMA_ADDR_INVALID;
+	if (ep->dma) {
+#if	defined(USE_KMALLOC)
+		retval = kmalloc (bytes, gfp_flags);
+		if (retval)
+			*dma = virt_to_phys (retval);
+
+#elif	LINUX_VERSION_CODE > KERNEL_VERSION(2,5,58)
+#warning Using dma_alloc_consistent even with sub-page allocations
+		/* the main problem with this call is that it wastes memory
+		 * on typical 1/N page allocations: it allocates 1-N pages.
+		 */
+		retval = dma_alloc_coherent (&ep->dev->pdev->dev,
+				bytes, dma, gfp_flags);
+#else
+#error No dma-coherent memory allocator is available
+		/* pci_alloc_consistent works, but pci_free_consistent()
+		 * isn't safe in_interrupt().  plus, in addition to the
+		 * 1/Nth page weakness, it doesn't understand gfp_flags.
+		 */
+#endif
+	} else
+		retval = kmalloc (bytes, gfp_flags);
+
+	return retval;
+}
+
+static void
+net2280_free_buffer (
+	struct usb_ep *_ep,
+	void *buf,
+	dma_addr_t dma,
+	unsigned bytes
+) {
+	/* free memory into the right allocator */
+#ifndef	USE_KMALLOC
+	if (dma != DMA_ADDR_INVALID)
+		dma_free_coherent (ep->dev->pdev, bytes, dma);
+	else
+#endif
+		kfree (buf);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* load a packet into the fifo we use for usb IN transfers.
+ * works for all endpoints.
+ *
+ * NOTE: pio with ep-a..ep-d could stuff multiple packets into the fifo
+ * at a time, but this code is simpler because it knows it only writes
+ * one packet.  ep-a..ep-d should use dma instead.
+ */
+static void
+write_fifo (struct net2280_ep *ep, struct usb_request *req)
+{
+	struct net2280_ep_regs	*regs = ep->regs;
+	u8			*buf;
+	u32			tmp;
+	unsigned		count, total;
+
+	/* INVARIANT:  fifo is currently empty. (testable) */
+
+	if (req) {
+		buf = req->buf + req->actual;
+		prefetch (buf);
+		total = req->length - req->actual;
+	} else {
+		total = 0;
+		buf = 0;
+	}
+
+	/* write just one packet at a time */
+	count = min (ep->ep.maxpacket, total);
+	VDEBUG (ep->dev, "write %s fifo (IN) %d bytes%s req %p\n",
+			ep->ep.name, count,
+			(count != ep->ep.maxpacket) ? " (short)" : "",
+			req);
+	while (count >= 4) {
+		/* NOTE be careful if you try to align these. fifo lines
+		 * should normally be full (4 bytes) and successive partial
+		 * lines are ok only in certain cases.
+		 */
+		tmp = get_unaligned ((u32 *)buf);
+		cpu_to_le32s (&tmp);
+		writel (tmp, &regs->ep_data);
+		buf += 4;
+		count -= 4;
+	}
+
+	/* last fifo entry is "short" unless we wrote a full packet */
+	if (total < ep->ep.maxpacket) {
+		tmp = count ? get_unaligned ((u32 *)buf) : count;
+		cpu_to_le32s (&tmp);
+		set_fifo_bytecount (ep, count & 0x03);
+		writel (tmp, &regs->ep_data);
+	}
+
+	/* pci writes may still be posted */
+}
+
+/* work around erratum 0106: PCI and USB race over the OUT fifo.
+ * caller guarantees chiprev 0100, out endpoint is NAKing, and
+ * there's no real data in the fifo.
+ */
+static void out_flush (struct net2280_ep *ep)
+{
+	u32	*statp, tmp;
+
+	ASSERT_OUT_NAKING (ep);
+
+	statp = &ep->regs->ep_stat;
+	writel (  (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
+		| (1 << DATA_PACKET_RECEIVED_INTERRUPT)
+		, statp);
+	writel ((1 << FIFO_FLUSH), statp);
+	mb ();
+	tmp = readl (statp);
+	if (tmp & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
+			/* high speed did bulk NYET; fifo isn't filling */
+			&& ep->dev->gadget.speed == USB_SPEED_FULL) {
+		unsigned	usec;
+
+		usec = 50;		/* 64 byte bulk/interrupt */
+		handshake (statp, (1 << USB_OUT_PING_NAK_SENT),
+				(1 << USB_OUT_PING_NAK_SENT), usec);
+		/* NAK done; now CLEAR_NAK_OUT_PACKETS is safe */
+	}
+}
+
+/* unload packet(s) from the fifo we use for usb OUT transfers.
+ * returns true iff the request completed, because of short packet
+ * or the request buffer having filled with full packets.
+ *
+ * for ep-a..ep-d this will read multiple packets out when they
+ * have been accepted.
+ */
+static int
+read_fifo (struct net2280_ep *ep, struct net2280_request *req)
+{
+	struct net2280_ep_regs	*regs = ep->regs;
+	u8			*buf = req->req.buf + req->req.actual;
+	unsigned		count, tmp, is_short;
+	unsigned		cleanup = 0, prevent = 0;
+
+	/* erratum 0106 ... packets coming in during fifo reads might
+	 * be incompletely rejected.  not all cases have workarounds.
+	 */
+	if (ep->dev->chiprev == 0x0100) {
+		tmp = readl (&ep->regs->ep_stat);
+		if ((tmp & (1 << NAK_OUT_PACKETS)))
+			/* cleanup = 1 */;
+		else if ((tmp & (1 << FIFO_FULL))
+				/* don't break hs PING protocol ... */
+				|| ep->dev->gadget.speed == USB_SPEED_FULL) {
+			start_out_naking (ep);
+			prevent = 1;
+		}
+		/* else: hope we don't see the problem */
+	}
+
+	/* never overflow the rx buffer. the fifo reads packets until
+	 * it sees a short one; we might not be ready for them all.
+	 */
+	prefetchw (buf);
+	count = readl (&regs->ep_avail);
+	tmp = req->req.length - req->req.actual;
+	if (count > tmp) {
+		/* as with DMA, data overflow gets flushed */
+		if ((tmp % ep->ep.maxpacket) != 0) {
+			ERROR (ep->dev,
+				"%s out fifo %d bytes, expected %d\n",
+				ep->ep.name, count, tmp);
+			req->req.status = -EOVERFLOW;
+			cleanup = 1;
+		}
+		count = tmp;
+	}
+	req->req.actual += count;
+
+	is_short = (count == 0) || ((count % ep->ep.maxpacket) != 0);
+
+	VDEBUG (ep->dev, "read %s fifo (OUT) %d bytes%s%s%s req %p %d/%d\n",
+			ep->ep.name, count, is_short ? " (short)" : "",
+			cleanup ? " flush" : "", prevent ? " nak" : "",
+			req, req->req.actual, req->req.length);
+
+	while (count >= 4) {
+		tmp = readl (&regs->ep_data);
+		cpu_to_le32s (&tmp);
+		put_unaligned (tmp, (u32 *)buf);
+		buf += 4;
+		count -= 4;
+	}
+	if (count) {
+		tmp = readl (&regs->ep_data);
+		cpu_to_le32s (&tmp);
+		do {
+			*buf++ = (u8) tmp;
+			tmp >>= 8;
+		} while (--count);
+	}
+	if (cleanup)
+		out_flush (ep);
+	if (prevent) {
+		writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+		(void) readl (&ep->regs->ep_rsp);
+	}
+
+	return is_short || ((req->req.actual == req->req.length)
+				&& !req->req.zero);
+}
+
+/* fill out dma descriptor to match a given request */
+static inline void
+fill_dma_desc (struct net2280_ep *ep, struct net2280_request *req, int valid)
+{
+	struct net2280_dma	*td = req->td;
+	u32			dmacount = req->req.length;
+
+	/* don't let DMA continue after a short OUT packet,
+	 * so overruns can't affect the next transfer.
+	 * in case of overruns on max-size packets, we can't
+	 * stop the fifo from filling but we can flush it.
+	 */
+	if (ep->is_in)
+		dmacount |= (1 << DMA_DIRECTION);
+	else
+		dmacount |= (1 << END_OF_CHAIN);
+
+	req->valid = valid;
+	if (valid)
+		dmacount |= (1 << VALID_BIT);
+#ifdef USE_DMA_CHAINING
+	if (!req->req.no_interrupt)
+#endif
+		dmacount |= (1 << DMA_DONE_INTERRUPT_ENABLE);
+
+	/* td->dmadesc = previously set by caller */
+	td->dmaaddr = cpu_to_le32p (&req->req.dma);
+
+	/* 2280 may be polling VALID_BIT through ep->dma->dmadesc */
+	wmb ();
+	td->dmacount = cpu_to_le32p (&dmacount);
+}
+
+static const u32 dmactl_default =
+		  (1 << DMA_CLEAR_COUNT_ENABLE)
+		/* erratum 0116 workaround part 1 (use POLLING) */
+		| (POLL_100_USEC << DESCRIPTOR_POLLING_RATE)
+		| (1 << DMA_VALID_BIT_POLLING_ENABLE)
+		| (1 << DMA_VALID_BIT_ENABLE)
+		| (1 << DMA_SCATTER_GATHER_ENABLE)
+		/* erratum 0116 workaround part 2 (no AUTOSTART) */
+		| (1 << DMA_ENABLE);
+
+static inline void spin_stop_dma (struct net2280_dma_regs *dma)
+{
+	handshake (&dma->dmactl, (1 << DMA_ENABLE), 0, 50);
+}
+
+static inline void stop_dma (struct net2280_dma_regs *dma)
+{
+	writel (dmactl_default & ~(1 << DMA_ENABLE), &dma->dmactl);
+	spin_stop_dma (dma);
+}
+
+static void start_dma (struct net2280_ep *ep, struct net2280_request *req)
+{
+	u32			tmp;
+	int			clear_nak = 0;
+	struct net2280_dma_regs	*dma = ep->dma;
+
+	/* FIXME can't use DMA for ZLPs */
+
+	/* previous OUT packet might have been short */
+	if (!ep->is_in && ((tmp = readl (&ep->regs->ep_stat))
+		 		& (1 << NAK_OUT_PACKETS)) != 0) {
+		writel ((1 << SHORT_PACKET_TRANSFERRED_INTERRUPT),
+			&ep->regs->ep_stat);
+
+		tmp = readl (&ep->regs->ep_avail);
+		if (tmp == 0)
+			clear_nak = 1;
+		else {
+			/* transfer all/some fifo data */
+			writel (req->req.dma, &dma->dmaaddr);
+			tmp = min (tmp, req->req.length);
+
+			/* dma irq, faking scatterlist status */
+			req->td->dmacount = cpu_to_le32 (req->req.length - tmp);
+			writel ((1 << DMA_DONE_INTERRUPT_ENABLE)
+				| tmp, &dma->dmacount);
+
+			writel ((1 << DMA_ENABLE), &dma->dmactl);
+			writel ((1 << DMA_START), &dma->dmastat);
+			return;
+		}
+	}
+
+	/* on this path we know there's no dma queue (yet) */
+	WARN_ON (readl (&dma->dmactl) & (1 << DMA_ENABLE));
+	tmp = dmactl_default;
+
+	/* force packet boundaries between dma requests, but prevent the
+	 * controller from automagically writing a last "short" packet
+	 * (zero length) unless the driver explicitly said to do that.
+	 */
+	if (ep->is_in) {
+		if (likely ((req->req.length % ep->ep.maxpacket) != 0
+				|| req->req.zero)) {
+			tmp |= (1 << DMA_FIFO_VALIDATE);
+			ep->in_fifo_validate = 1;
+		} else
+			ep->in_fifo_validate = 0;
+	}
+
+	/* init req->td, pointing to the current dummy */
+	req->td->dmadesc = cpu_to_le32 (ep->td_dma);
+	fill_dma_desc (ep, req, 1);
+
+#ifdef USE_DMA_CHAINING
+	writel (  (1 << VALID_BIT)
+		| (ep->is_in << DMA_DIRECTION)
+		| 0, &dma->dmacount);
+#else
+	req->td->dmacount |= __constant_cpu_to_le32 (1 << END_OF_CHAIN);
+#endif
+
+	writel (req->td_dma, &dma->dmadesc);
+	writel (tmp, &dma->dmactl);
+
+	/* erratum 0116 workaround part 3:  pci arbiter away from net2280 */
+	(void) readl (&ep->dev->pci->pcimstctl);
+
+	writel ((1 << DMA_START), &dma->dmastat);
+
+	/* recover from previous short read; erratum 0112 workaround #1 */
+	if (clear_nak)
+		writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+}
+
+static inline void
+queue_dma (struct net2280_ep *ep, struct net2280_request *req, int valid)
+{
+	struct net2280_dma	*end;
+	dma_addr_t		tmp;
+
+	/* swap new dummy for old, link; fill and maybe activate */
+	end = ep->dummy;
+	ep->dummy = req->td;
+	req->td = end;
+
+	tmp = ep->td_dma;
+	ep->td_dma = req->td_dma;
+	req->td_dma = tmp;
+
+	end->dmadesc = cpu_to_le32 (ep->td_dma);
+
+	fill_dma_desc (ep, req, valid);
+}
+
+static void
+done (struct net2280_ep *ep, struct net2280_request *req, int status)
+{
+	struct net2280		*dev;
+	unsigned		stopped = ep->stopped;
+
+	list_del_init (&req->queue);
+
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	dev = ep->dev;
+	if (req->mapped) {
+		pci_unmap_single (dev->pdev, req->req.dma, req->req.length,
+			ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+		req->req.dma = DMA_ADDR_INVALID;
+		req->mapped = 0;
+	}
+
+	if (status && status != -ESHUTDOWN)
+		VDEBUG (dev, "complete %s req %p stat %d len %u/%u\n",
+			ep->ep.name, &req->req, status,
+			req->req.actual, req->req.length);
+
+	/* don't modify queue heads during completion callback */
+	ep->stopped = 1;
+	spin_unlock (&dev->lock);
+	req->req.complete (&ep->ep, &req->req);
+	spin_lock (&dev->lock);
+	ep->stopped = stopped;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+net2280_queue (struct usb_ep *_ep, struct usb_request *_req, int gfp_flags)
+{
+	struct net2280_request	*req;
+	struct net2280_ep	*ep;
+	struct net2280		*dev;
+	unsigned long		flags;
+
+	/* we always require a cpu-view buffer, so that we can
+	 * always use pio (as fallback or whatever).
+	 */
+	req = container_of (_req, struct net2280_request, req);
+	if (!_req || !_req->complete || !_req->buf
+			|| !list_empty (&req->queue))
+		return -EINVAL;
+	if (_req->length > (~0 & DMA_BYTE_COUNT_MASK))
+		return -EDOM;
+	ep = container_of (_ep, struct net2280_ep, ep);
+	if (!_ep || (!ep->desc && ep->num != 0))
+		return -EINVAL;
+	dev = ep->dev;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	/* FIXME implement PIO fallback for ZLPs with DMA */
+	if (ep->dma && _req->length == 0)
+		return -EOPNOTSUPP;
+
+	/* set up dma mapping in case the caller didn't */
+	if (ep->dma && _req->dma == DMA_ADDR_INVALID) {
+		_req->dma = pci_map_single (dev->pdev, _req->buf, _req->length,
+			ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+		req->mapped = 1;
+	}
+
+#if 0
+	VDEBUG (dev, "%s queue req %p, len %d buf %p\n",
+			_ep->name, _req, _req->length, _req->buf);
+#endif
+
+	spin_lock_irqsave (&dev->lock, flags);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+	req->dma_done = 0;
+
+	/* kickstart this i/o queue? */
+	if (list_empty (&ep->queue) && !ep->stopped) {
+		/* use DMA if the endpoint supports it, else pio */
+		if (ep->dma)
+			start_dma (ep, req);
+		else {
+			/* maybe there's no control data, just status ack */
+			if (ep->num == 0 && _req->length == 0) {
+				allow_status (ep);
+				done (ep, req, 0);
+				VDEBUG (dev, "%s status ack\n", ep->ep.name);
+				goto done;
+			}
+
+			/* PIO ... stuff the fifo, or unblock it.  */
+			if (ep->is_in)
+				write_fifo (ep, _req);
+			else if (list_empty (&ep->queue)) {
+				u32	s;
+
+				/* OUT FIFO might have packet(s) buffered */
+				s = readl (&ep->regs->ep_stat);
+				if ((s & (1 << FIFO_EMPTY)) == 0) {
+					/* note:  _req->short_not_ok is
+					 * ignored here since PIO _always_
+					 * stops queue advance here, and
+					 * _req->status doesn't change for
+					 * short reads (only _req->actual)
+					 */
+					if (read_fifo (ep, req)) {
+						done (ep, req, 0);
+						if (ep->num == 0)
+							allow_status (ep);
+						/* don't queue it */
+						req = 0;
+					} else
+						s = readl (&ep->regs->ep_stat);
+				}
+
+				/* don't NAK, let the fifo fill */
+				if (req && (s & (1 << NAK_OUT_PACKETS)))
+					writel ((1 << CLEAR_NAK_OUT_PACKETS),
+							&ep->regs->ep_rsp);
+			}
+		}
+
+	} else if (ep->dma) {
+		int	valid = 1;
+
+		if (ep->is_in) {
+			int	expect;
+
+			/* preventing magic zlps is per-engine state, not
+			 * per-transfer; irq logic must recover hiccups.
+			 */
+			expect = likely (req->req.zero
+				|| (req->req.length % ep->ep.maxpacket) != 0);
+			if (expect != ep->in_fifo_validate)
+				valid = 0;
+		}
+		queue_dma (ep, req, valid);
+
+	} /* else the irq handler advances the queue. */
+
+	if (req)
+		list_add_tail (&req->queue, &ep->queue);
+done:
+	spin_unlock_irqrestore (&dev->lock, flags);
+
+	/* pci writes may still be posted */
+	return 0;
+}
+
+static inline void
+dma_done (
+	struct net2280_ep *ep,
+	struct net2280_request *req,
+	u32 dmacount,
+	int status
+)
+{
+	req->req.actual = req->req.length - (DMA_BYTE_COUNT_MASK & dmacount);
+	rmb ();
+	done (ep, req, status);
+}
+
+static void scan_dma_completions (struct net2280_ep *ep)
+{
+	/* only look at descriptors that were "naturally" retired,
+	 * so fifo and list head state won't matter
+	 */
+	while (!list_empty (&ep->queue)) {
+		struct net2280_request	*req;
+		u32			tmp;
+
+		req = list_entry (ep->queue.next,
+				struct net2280_request, queue);
+		if (!req->valid)
+			break;
+		rmb ();
+		tmp = le32_to_cpup (&req->td->dmacount);
+		if ((tmp & (1 << VALID_BIT)) != 0)
+			break;
+
+		/* SHORT_PACKET_TRANSFERRED_INTERRUPT handles "usb-short"
+		 * packets, including overruns, even when the transfer was
+		 * exactly the length requested (dmacount now zero).
+		 * FIXME there's an overrun case here too, where we expect
+		 * a short packet but receive a max length one (won't NAK).
+		 */
+		if (!ep->is_in && (req->req.length % ep->ep.maxpacket) != 0) {
+			req->dma_done = 1;
+			break;
+		}
+		dma_done (ep, req, tmp, 0);
+	}
+}
+
+static void restart_dma (struct net2280_ep *ep)
+{
+	struct net2280_request	*req;
+
+	if (ep->stopped)
+		return;
+	req = list_entry (ep->queue.next, struct net2280_request, queue);
+
+#ifdef USE_DMA_CHAINING
+	/* the 2280 will be processing the queue unless queue hiccups after
+	 * the previous transfer:
+	 *  IN:   wanted automagic zlp, head doesn't (or vice versa)
+	 *  OUT:  was "usb-short", we must restart.
+	 */
+	if (!req->valid) {
+		struct net2280_request	*entry, *prev = 0;
+		int			qmode, reqmode, done = 0;
+
+		DEBUG (ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td);
+		qmode = likely (req->req.zero
+			|| (req->req.length % ep->ep.maxpacket) != 0);
+		list_for_each_entry (entry, &ep->queue, queue) {
+			u32		dmacount;
+
+			if (entry != req)
+				continue;
+			dmacount = entry->td->dmacount;
+			if (!done) {
+				reqmode = likely (entry->req.zero
+					|| (entry->req.length
+						% ep->ep.maxpacket) != 0);
+				if (reqmode == qmode) {
+					entry->valid = 1;
+					dmacount |= valid_bit;
+					entry->td->dmacount = dmacount;
+					prev = entry;
+					continue;
+				} else {
+					prev->td->dmacount |= dma_done_ie;
+					done = 1;
+				}
+			}
+
+			/* walk the rest of the queue so unlinks behave */
+			entry->valid = 0;
+			dmacount &= ~valid_bit;
+			entry->td->dmacount = dmacount;
+			prev = entry;
+		}
+		start_dma (ep, req);
+	} else if (!ep->is_in
+			&& (readl (&ep->regs->ep_stat)
+				& (1 << NAK_OUT_PACKETS)) != 0)
+		start_dma (ep, req);
+#else
+	start_dma (ep, req);
+#endif
+}
+
+static inline void abort_dma (struct net2280_ep *ep)
+{
+	/* abort the current transfer */
+	writel ((1 << DMA_ABORT), &ep->dma->dmastat);
+
+	/* collect completed transfers (except the current one) */
+	scan_dma_completions (ep);
+}
+
+/* dequeue ALL requests */
+static void nuke (struct net2280_ep *ep)
+{
+	struct net2280_request	*req;
+
+	/* called with spinlock held */
+	ep->stopped = 1;
+	if (ep->dma)
+		abort_dma (ep);
+	while (!list_empty (&ep->queue)) {
+		req = list_entry (ep->queue.next,
+				struct net2280_request,
+				queue);
+		done (ep, req, -ESHUTDOWN);
+	}
+}
+
+/* dequeue JUST ONE request */
+static int net2280_dequeue (struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct net2280_ep	*ep;
+	struct net2280_request	*req;
+	unsigned long		flags;
+	u32			dmactl;
+	int			stopped;
+
+	ep = container_of (_ep, struct net2280_ep, ep);
+	req = container_of (_req, struct net2280_request, req);
+	if (!_ep || (!ep->desc && ep->num != 0) || !_req)
+		return -EINVAL;
+
+	spin_lock_irqsave (&ep->dev->lock, flags);
+	stopped = ep->stopped;
+
+	/* pause dma while we scan the queue */
+	dmactl = 0;
+	ep->stopped = 1;
+	if (ep->dma) {
+		dmactl = readl (&ep->dma->dmactl);
+		writel (dmactl & ~(1 << DMA_ENABLE), &ep->dma->dmactl);
+		/* force synch, clean any completed requests */
+		spin_stop_dma (ep->dma);
+		scan_dma_completions (ep);
+	}
+
+	/* queue head may be partially complete. */
+	if (ep->queue.next == &req->queue) {
+		if (ep->dma) {
+			DEBUG (ep->dev, "unlink (%s) dma\n", _ep->name);
+			_req->status = -ECONNRESET;
+			abort_dma (ep);
+			if (likely (ep->queue.next == &req->queue))
+				dma_done (ep, req,
+					le32_to_cpup (&req->td->dmacount),
+					-ECONNRESET);
+		} else {
+			DEBUG (ep->dev, "unlink (%s) pio\n", _ep->name);
+			done (ep, req, -ECONNRESET);
+		}
+		req = 0;
+
+#ifdef USE_DMA_CHAINING
+	/* patch up hardware chaining data */
+	} else if (ep->dma) {
+		if (req->queue.prev == ep->queue.next) {
+			writel (le32_to_cpu (req->td->dmadesc),
+				&ep->dma->dmadesc);
+			if (req->td->dmacount & dma_done_ie)
+				writel (readl (&ep->dma->dmacount)
+						| dma_done_ie,
+					&ep->dma->dmacount);
+		} else {
+			struct net2280_request	*prev;
+
+			prev = list_entry (req->queue.prev,
+				struct net2280_request, queue);
+			prev->td->dmadesc = req->td->dmadesc;
+			if (req->td->dmacount & dma_done_ie)
+				prev->td->dmacount |= dma_done_ie;
+		}
+#endif
+	}
+
+	if (req)
+		done (ep, req, -ECONNRESET);
+	ep->stopped = stopped;
+
+	if (ep->dma) {
+		/* turn off dma on inactive queues */
+		if (list_empty (&ep->queue))
+			stop_dma (ep->dma);
+		else if (!ep->stopped) {
+			/* resume current request, or start new one */
+			if (req)
+				writel (dmactl, &ep->dma->dmactl);
+			else
+				start_dma (ep, list_entry (ep->queue.next,
+					struct net2280_request, queue));
+		}
+	}
+
+	spin_unlock_irqrestore (&ep->dev->lock, flags);
+	return req ? 0 : -EOPNOTSUPP;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+net2280_set_halt (struct usb_ep *_ep, int value)
+{
+	struct net2280_ep	*ep;
+
+	ep = container_of (_ep, struct net2280_ep, ep);
+	if (!_ep || (!ep->desc && ep->num != 0))
+		return -EINVAL;
+	if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+	if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03)
+						== USB_ENDPOINT_XFER_ISOC)
+		return -EINVAL;
+
+	VDEBUG (ep->dev, "%s %s halt\n", _ep->name, value ? "set" : "clear");
+
+	/* set/clear, then synch memory views with the device */
+	if (value) {
+		if (ep->num == 0)
+			ep->dev->protocol_stall = 1;
+		else
+			set_halt (ep);
+	} else
+		clear_halt (ep);
+	(void) readl (&ep->regs->ep_rsp);
+
+	return 0;
+}
+
+static int
+net2280_fifo_status (struct usb_ep *_ep)
+{
+	struct net2280_ep	*ep;
+	u32			avail;
+
+	ep = container_of (_ep, struct net2280_ep, ep);
+	if (!_ep || (!ep->desc && ep->num != 0))
+		return -ENODEV;
+	if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	avail = readl (&ep->regs->ep_avail) & ((1 << 12) - 1);
+	if (avail > ep->fifo_size)
+		return -EOVERFLOW;
+	if (ep->is_in)
+		avail = ep->fifo_size - avail;
+	return avail;
+}
+
+static void
+net2280_fifo_flush (struct usb_ep *_ep)
+{
+	struct net2280_ep	*ep;
+
+	ep = container_of (_ep, struct net2280_ep, ep);
+	if (!_ep || (!ep->desc && ep->num != 0))
+		return;
+	if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return;
+
+	writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat);
+	(void) readl (&ep->regs->ep_rsp);
+}
+
+static struct usb_ep_ops net2280_ep_ops = {
+	.enable		= net2280_enable,
+	.disable	= net2280_disable,
+
+	.alloc_request	= net2280_alloc_request,
+	.free_request	= net2280_free_request,
+
+	.alloc_buffer	= net2280_alloc_buffer,
+	.free_buffer	= net2280_free_buffer,
+
+	.queue		= net2280_queue,
+	.dequeue	= net2280_dequeue,
+
+	.set_halt	= net2280_set_halt,
+	.fifo_status	= net2280_fifo_status,
+	.fifo_flush	= net2280_fifo_flush,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int net2280_get_frame (struct usb_gadget *_gadget)
+{
+	struct net2280		*dev;
+	unsigned long		flags;
+	u16			retval;
+
+	if (!_gadget)
+		return -ENODEV;
+	dev = container_of (_gadget, struct net2280, gadget);
+	spin_lock_irqsave (&dev->lock, flags);
+	retval = get_idx_reg (dev->regs, REG_FRAME) & 0x03ff;
+	spin_unlock_irqrestore (&dev->lock, flags);
+	return retval;
+}
+
+static int net2280_wakeup (struct usb_gadget *_gadget)
+{
+	struct net2280		*dev;
+
+	if (!_gadget)
+		return 0;
+	dev = container_of (_gadget, struct net2280, gadget);
+	writel (1 << GENERATE_RESUME, &dev->usb->usbstat);
+
+	/* pci writes may still be posted */
+	return 0;
+}
+
+static const struct usb_gadget_ops net2280_ops = {
+	.get_frame	= net2280_get_frame,
+	.wakeup		= net2280_wakeup,
+
+	// .set_selfpowered = net2280_set_selfpowered,
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef	USE_SYSFS_DEBUG_FILES
+
+/* "function" sysfs attribute */
+static ssize_t
+show_function (struct device *_dev, char *buf)
+{
+	struct net2280	*dev = dev_get_drvdata (_dev);
+
+	if (!dev->driver
+			|| !dev->driver->function
+			|| strlen (dev->driver->function) > PAGE_SIZE)
+		return 0;
+	return snprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function);
+}
+static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
+
+static ssize_t
+show_registers (struct device *_dev, char *buf)
+{
+	struct net2280		*dev;
+	char			*next;
+	unsigned		size, t;
+	unsigned long		flags;
+	int			i;
+	u32			t1, t2;
+	char			*s;
+
+	dev = dev_get_drvdata (_dev);
+	next = buf;
+	size = PAGE_SIZE;
+	spin_lock_irqsave (&dev->lock, flags);
+
+	if (dev->driver)
+		s = dev->driver->driver.name;
+	else
+		s = "(none)";
+
+	/* Main Control Registers */
+	t = snprintf (next, size, "%s version " DRIVER_VERSION
+			", chiprev %04x\n"
+			"devinit %03x fifoctl %08x gadget '%s'\n"
+			"pci irqenb0 %02x irqenb1 %08x "
+			"irqstat0 %04x irqstat1 %08x\n",
+			driver_name, dev->chiprev,
+			readl (&dev->regs->devinit),
+			readl (&dev->regs->fifoctl),
+			s,
+			readl (&dev->regs->pciirqenb0),
+			readl (&dev->regs->pciirqenb1),
+			readl (&dev->regs->irqstat0),
+			readl (&dev->regs->irqstat1));
+	size -= t;
+	next += t;
+
+	/* USB Control Registers */
+	t1 = readl (&dev->usb->usbctl);
+	t2 = readl (&dev->usb->usbstat);
+	if (t1 & (1 << VBUS_PIN)) {
+		if (t2 & (1 << HIGH_SPEED))
+			s = "high speed";
+		else if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+			s = "powered";
+		else
+			s = "full speed";
+		/* full speed bit (6) not working?? */
+	} else
+			s = "not attached";
+	t = snprintf (next, size,
+			"stdrsp %08x usbctl %08x usbstat %08x "
+				"addr 0x%02x (%s)\n",
+			readl (&dev->usb->stdrsp), t1, t2,
+			readl (&dev->usb->ouraddr), s);
+	size -= t;
+	next += t;
+
+	/* PCI Master Control Registers */
+
+	/* DMA Control Registers */
+
+	/* Configurable EP Control Registers */
+	for (i = 0; i < 7; i++) {
+		struct net2280_ep	*ep;
+
+		ep = &dev->ep [i];
+		if (i && !ep->desc)
+			continue;
+
+		t1 = readl (&ep->regs->ep_cfg);
+		t2 = readl (&ep->regs->ep_rsp) & 0xff;
+		t = snprintf (next, size,
+				"%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s"
+					"irqenb %02x\n",
+				ep->ep.name, t1, t2,
+				(t2 & (1 << CLEAR_NAK_OUT_PACKETS))
+					? "NAK " : "",
+				(t2 & (1 << CLEAR_EP_HIDE_STATUS_PHASE))
+					? "hide " : "",
+				(t2 & (1 << CLEAR_EP_FORCE_CRC_ERROR))
+					? "CRC " : "",
+				(t2 & (1 << CLEAR_INTERRUPT_MODE))
+					? "interrupt " : "",
+				(t2 & (1<<CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE))
+					? "status " : "",
+				(t2 & (1 << CLEAR_NAK_OUT_PACKETS_MODE))
+					? "NAKmode " : "",
+				(t2 & (1 << CLEAR_ENDPOINT_TOGGLE))
+					? "DATA1 " : "DATA0 ",
+				(t2 & (1 << CLEAR_ENDPOINT_HALT))
+					? "HALT " : "",
+				readl (&ep->regs->ep_irqenb));
+		size -= t;
+		next += t;
+
+		t = snprintf (next, size,
+				"\tstat %08x avail %04x "
+				"(ep%d%s-%s)%s\n",
+				readl (&ep->regs->ep_stat),
+				readl (&ep->regs->ep_avail),
+				t1 & 0x0f, DIR_STRING (t1),
+				type_string (t1 >> 8),
+				ep->stopped ? "*" : "");
+		size -= t;
+		next += t;
+
+		if (!ep->dma)
+			continue;
+
+		t = snprintf (next, size,
+				"  dma\tctl %08x stat %08x count %08x\n"
+				"\taddr %08x desc %08x\n",
+				readl (&ep->dma->dmactl),
+				readl (&ep->dma->dmastat),
+				readl (&ep->dma->dmacount),
+				readl (&ep->dma->dmaaddr),
+				readl (&ep->dma->dmadesc));
+		size -= t;
+		next += t;
+
+	}
+
+	/* Indexed Registers */
+		// none yet 
+
+	/* Statistics */
+	t = snprintf (next, size, "irqs:  ");
+	size -= t;
+	next += t;
+	for (i = 0; i < 7; i++) {
+		struct net2280_ep	*ep;
+
+		ep = &dev->ep [i];
+		if (i && !ep->irqs)
+			continue;
+		t = snprintf (next, size, " %s/%ld", ep->ep.name, ep->irqs);
+		size -= t;
+		next += t;
+
+	}
+	t = snprintf (next, size, "\n");
+	size -= t;
+	next += t;
+
+	spin_unlock_irqrestore (&dev->lock, flags);
+
+	return PAGE_SIZE - size;
+}
+static DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
+
+static ssize_t
+show_queues (struct device *_dev, char *buf)
+{
+	struct net2280		*dev;
+	char			*next;
+	unsigned		size;
+	unsigned long		flags;
+	int			i;
+
+	dev = dev_get_drvdata (_dev);
+	next = buf;
+	size = PAGE_SIZE;
+	spin_lock_irqsave (&dev->lock, flags);
+
+	for (i = 0; i < 7; i++) {
+		struct net2280_ep		*ep = &dev->ep [i];
+		struct net2280_request		*req;
+		int				t;
+
+		if (i != 0) {
+			const struct usb_endpoint_descriptor	*d;
+
+			d = ep->desc;
+			if (!d)
+				continue;
+			t = d->bEndpointAddress;
+			t = snprintf (next, size,
+				"%s (ep%d%s-%s) max %04x %s\n",
+				ep->ep.name, t & USB_ENDPOINT_NUMBER_MASK,
+				(t & USB_DIR_IN) ? "in" : "out",
+				({ char *val;
+				 switch (d->bmAttributes & 0x03) {
+				 case USB_ENDPOINT_XFER_BULK:
+				 	val = "bulk"; break;
+				 case USB_ENDPOINT_XFER_INT:
+				 	val = "intr"; break;
+				 default:
+				 	val = "iso"; break;
+				 }; val; }),
+				le16_to_cpu (d->wMaxPacketSize) & 0x1fff,
+				ep->dma ? "dma" : "pio"
+				);
+		} else /* ep0 should only have one transfer queued */
+			t = snprintf (next, size, "ep0 max 64 pio %s\n",
+					ep->is_in ? "in" : "out");
+		if (t <= 0 || t > size)
+			goto done;
+		size -= t;
+		next += t;
+
+		if (list_empty (&ep->queue)) {
+			t = snprintf (next, size, "\t(nothing queued)\n");
+			if (t <= 0 || t > size)
+				goto done;
+			size -= t;
+			next += t;
+			continue;
+		}
+		list_for_each_entry (req, &ep->queue, queue) {
+			if (ep->dma && req->td_dma == readl (&ep->dma->dmadesc))
+				t = snprintf (next, size,
+					"\treq %p len %d/%d "
+					"buf %p (dmacount %08x)\n",
+					&req->req, req->req.actual,
+					req->req.length, req->req.buf,
+					readl (&ep->dma->dmacount));
+			else
+				t = snprintf (next, size,
+					"\treq %p len %d/%d buf %p\n",
+					&req->req, req->req.actual,
+					req->req.length, req->req.buf);
+			if (t <= 0 || t > size)
+				goto done;
+			size -= t;
+			next += t;
+		}
+	}
+
+done:
+	spin_unlock_irqrestore (&dev->lock, flags);
+	return PAGE_SIZE - size;
+}
+static DEVICE_ATTR (queues, S_IRUGO, show_queues, NULL);
+
+
+#else
+
+#define device_create_file(a,b)	do {} while (0)
+#define device_remove_file	device_create_file
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* another driver-specific mode might be a request type doing dma
+ * to/from another device fifo instead of to/from memory.
+ */
+
+static void set_fifo_mode (struct net2280 *dev, int mode)
+{
+	/* keeping high bits preserves BAR2 */
+	writel ((0xffff << PCI_BASE2_RANGE) | mode, &dev->regs->fifoctl);
+
+	/* always ep-{a,b,e,f} ... maybe not ep-c or ep-d */
+	INIT_LIST_HEAD (&dev->gadget.ep_list);
+	list_add_tail (&dev->ep [1].ep.ep_list, &dev->gadget.ep_list);
+	list_add_tail (&dev->ep [2].ep.ep_list, &dev->gadget.ep_list);
+	switch (mode) {
+	case 0:
+		list_add_tail (&dev->ep [3].ep.ep_list, &dev->gadget.ep_list);
+		list_add_tail (&dev->ep [4].ep.ep_list, &dev->gadget.ep_list);
+		dev->ep [1].fifo_size = dev->ep [2].fifo_size = 1024;
+		break;
+	case 1:
+		dev->ep [1].fifo_size = dev->ep [2].fifo_size = 2048;
+		break;
+	case 2:
+		list_add_tail (&dev->ep [3].ep.ep_list, &dev->gadget.ep_list);
+		dev->ep [1].fifo_size = 2048;
+		dev->ep [2].fifo_size = 1024;
+		break;
+	}
+	/* fifo sizes for ep0, ep-c, ep-d, ep-e, and ep-f never change */
+	list_add_tail (&dev->ep [5].ep.ep_list, &dev->gadget.ep_list);
+	list_add_tail (&dev->ep [6].ep.ep_list, &dev->gadget.ep_list);
+}
+
+/**
+ * net2280_set_fifo_mode - change allocation of fifo buffers
+ * @gadget: access to the net2280 device that will be updated
+ * @mode: 0 for default, four 1kB buffers (ep-a through ep-d);
+ * 	1 for two 2kB buffers (ep-a and ep-b only);
+ * 	2 for one 2kB buffer (ep-a) and two 1kB ones (ep-b, ep-c).
+ *
+ * returns zero on success, else negative errno.  when this succeeds,
+ * the contents of gadget->ep_list may have changed.
+ *
+ * you may only call this function when endpoints a-d are all disabled.
+ * use it whenever extra hardware buffering can help performance, such
+ * as before enabling "high bandwidth" interrupt endpoints that use
+ * maxpacket bigger than 512 (when double buffering would otherwise
+ * be unavailable).
+ */
+int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode)
+{
+	int			i;
+	struct net2280		*dev;
+	int			status = 0;
+	unsigned long		flags;
+
+	if (!gadget)
+		return -ENODEV;
+	dev = container_of (gadget, struct net2280, gadget);
+
+	spin_lock_irqsave (&dev->lock, flags);
+
+	for (i = 1; i <= 4; i++)
+		if (dev->ep [i].desc) {
+			status = -EINVAL;
+			break;
+		}
+	if (mode < 0 || mode > 2)
+		status = -EINVAL;
+	if (status == 0)
+		set_fifo_mode (dev, mode);
+	spin_unlock_irqrestore (&dev->lock, flags);
+
+	if (status == 0) {
+		if (mode == 1)
+			DEBUG (dev, "fifo:  ep-a 2K, ep-b 2K\n");
+		else if (mode == 2)
+			DEBUG (dev, "fifo:  ep-a 2K, ep-b 1K, ep-c 1K\n");
+		/* else all are 1K */
+	}
+	return status;
+}
+EXPORT_SYMBOL (net2280_set_fifo_mode);
+
+/*-------------------------------------------------------------------------*/
+
+/* keeping it simple:
+ * - one bus driver, initted first;
+ * - one function driver, initted second
+ *
+ * most of the work to support multiple net2280 controllers would
+ * be to associate this gadget driver (yes?) with all of them, or
+ * perhaps to bind specific drivers to specific devices.
+ */
+
+static struct net2280	*the_controller;
+
+static void usb_reset (struct net2280 *dev)
+{
+	u32	tmp;
+
+	/* force immediate bus disconnect, and synch through pci */
+	writel (0, &dev->usb->usbctl);
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+	(void) readl (&dev->usb->usbctl);
+
+	net2280_led_init (dev);
+
+	/* disable automatic responses, and irqs */
+	writel (0, &dev->usb->stdrsp);
+	writel (0, &dev->regs->pciirqenb0);
+	writel (0, &dev->regs->pciirqenb1);
+
+	/* clear old dma and irq state */
+	for (tmp = 0; tmp < 4; tmp++) {
+		writel ((1 << DMA_ABORT), &dev->dma [tmp].dmastat);
+		stop_dma (&dev->dma [tmp]);
+	}
+	writel (~0, &dev->regs->irqstat0),
+	writel (~(1 << SUSPEND_REQUEST_INTERRUPT), &dev->regs->irqstat1),
+
+	/* reset, and enable pci */
+	tmp = readl (&dev->regs->devinit)
+		| (1 << PCI_ENABLE)
+		| (1 << FIFO_SOFT_RESET)
+		| (1 << USB_SOFT_RESET)
+		| (1 << M8051_RESET);
+	writel (tmp, &dev->regs->devinit);
+
+	/* standard fifo and endpoint allocations */
+	set_fifo_mode (dev, (fifo_mode <= 2) ? fifo_mode : 0);
+}
+
+static void usb_reinit (struct net2280 *dev)
+{
+	u32	tmp;
+	int	init_dma;
+
+	/* use_dma changes are ignored till next device re-init */
+	init_dma = use_dma;
+
+	/* basic endpoint init */
+	for (tmp = 0; tmp < 7; tmp++) {
+		struct net2280_ep	*ep = &dev->ep [tmp];
+
+		ep->ep.name = ep_name [tmp];
+		ep->dev = dev;
+		ep->num = tmp;
+
+		if (tmp > 0 && tmp <= 4) {
+			ep->fifo_size = 1024;
+			if (init_dma)
+				ep->dma = &dev->dma [tmp - 1];
+		} else
+			ep->fifo_size = 64;
+		ep->regs = &dev->epregs [tmp];
+		ep_reset (dev->regs, ep);
+	}
+	dev->ep [0].ep.maxpacket = 64;
+	dev->ep [5].ep.maxpacket = 64;
+	dev->ep [6].ep.maxpacket = 64;
+
+	dev->gadget.ep0 = &dev->ep [0].ep;
+	dev->ep [0].stopped = 0;
+	INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
+
+	/* we want to prevent lowlevel/insecure access from the USB host,
+	 * but erratum 0119 means this enable bit is ignored
+	 */
+	for (tmp = 0; tmp < 5; tmp++)
+		writel (EP_DONTUSE, &dev->dep [tmp].dep_cfg);
+}
+
+static void ep0_start (struct net2280 *dev)
+{
+	writel (  (1 << CLEAR_EP_HIDE_STATUS_PHASE)
+		| (1 << CLEAR_NAK_OUT_PACKETS)
+		| (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)
+		, &dev->epregs [0].ep_rsp);
+
+	/*
+	 * hardware optionally handles a bunch of standard requests
+	 * that the API hides from drivers anyway.  have it do so.
+	 * endpoint status/features are handled in software, to
+	 * help pass tests for some dubious behavior.
+	 */
+	writel (  (1 << SET_TEST_MODE)
+		| (1 << SET_ADDRESS)
+		| (1 << DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP)
+		| (1 << GET_DEVICE_STATUS)
+		| (1 << GET_INTERFACE_STATUS)
+		, &dev->usb->stdrsp);
+	writel (  (1 << USB_ROOT_PORT_WAKEUP_ENABLE)
+		| (1 << SELF_POWERED_USB_DEVICE)
+		| (1 << REMOTE_WAKEUP_SUPPORT)
+		| (1 << USB_DETECT_ENABLE)
+		| (1 << DEVICE_REMOTE_WAKEUP_ENABLE)
+		, &dev->usb->usbctl);
+
+	/* enable irqs so we can see ep0 and general operation  */
+	writel (  (1 << SETUP_PACKET_INTERRUPT_ENABLE)
+		| (1 << ENDPOINT_0_INTERRUPT_ENABLE)
+		, &dev->regs->pciirqenb0);
+	writel (  (1 << PCI_INTERRUPT_ENABLE)
+		| (1 << PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE)
+		| (1 << PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE)
+		| (1 << PCI_RETRY_ABORT_INTERRUPT_ENABLE)
+		| (1 << VBUS_INTERRUPT_ENABLE)
+		| (1 << ROOT_PORT_RESET_INTERRUPT_ENABLE)
+		, &dev->regs->pciirqenb1);
+
+	/* don't leave any writes posted */
+	(void) readl (&dev->usb->usbctl);
+}
+
+/* when a driver is successfully registered, it will receive
+ * control requests including set_configuration(), which enables
+ * non-control requests.  then usb traffic follows until a
+ * disconnect is reported.  then a host may connect again, or
+ * the driver might get unbound.
+ */
+int usb_gadget_register_driver (struct usb_gadget_driver *driver)
+{
+	struct net2280		*dev = the_controller;
+	int			retval;
+	unsigned		i;
+
+	/* insist on high speed support from the driver, since
+	 * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE)
+	 * "must not be used in normal operation"
+	 */
+	if (!driver
+			|| driver->speed != USB_SPEED_HIGH
+			|| !driver->bind
+			|| !driver->unbind
+			|| !driver->setup)
+		return -EINVAL;
+	if (!dev)
+		return -ENODEV;
+	if (dev->driver)
+		return -EBUSY;
+
+	for (i = 0; i < 7; i++)
+		dev->ep [i].irqs = 0;
+
+	/* hook up the driver ... */
+	dev->driver = driver;
+	retval = driver->bind (&dev->gadget);
+	if (retval) {
+		DEBUG (dev, "bind to driver %s --> %d\n",
+				driver->driver.name, retval);
+		dev->driver = 0;
+		return retval;
+	}
+
+	/* ... then enable host detection and ep0; and we're ready
+	 * for set_configuration as well as eventual disconnect.
+	 */
+	net2280_led_active (dev, 1);
+	ep0_start (dev);
+
+	DEBUG (dev, "%s ready, usbctl %08x stdrsp %08x\n",
+			driver->driver.name,
+			readl (&dev->usb->usbctl),
+			readl (&dev->usb->stdrsp));
+
+	/* pci writes may still be posted */
+	return 0;
+}
+EXPORT_SYMBOL (usb_gadget_register_driver);
+
+static void
+stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver)
+{
+	int			i;
+
+	/* don't disconnect if it's not connected */
+	if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+		driver = 0;
+
+	/* stop hardware; prevent new request submissions;
+	 * and kill any outstanding requests.
+	 */
+	usb_reset (dev);
+	for (i = 0; i < 7; i++)
+		nuke (&dev->ep [i]);
+
+	/* report disconnect; the driver is already quiesced */
+	if (driver) {
+		spin_unlock (&dev->lock);
+		driver->disconnect (&dev->gadget);
+		spin_lock (&dev->lock);
+	}
+
+	usb_reinit (dev);
+}
+
+int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
+{
+	struct net2280	*dev = the_controller;
+	unsigned long	flags;
+
+	if (!dev)
+		return -ENODEV;
+	if (!driver || driver != dev->driver)
+		return -EINVAL;
+
+	spin_lock_irqsave (&dev->lock, flags);
+	stop_activity (dev, driver);
+	spin_unlock_irqrestore (&dev->lock, flags);
+
+	driver->unbind (&dev->gadget);
+	dev->driver = 0;
+
+	net2280_led_active (dev, 0);
+
+	DEBUG (dev, "unregistered driver '%s'\n", driver->driver.name);
+	return 0;
+}
+EXPORT_SYMBOL (usb_gadget_unregister_driver);
+
+
+/*-------------------------------------------------------------------------*/
+
+/* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq.
+ * also works for dma-capable endpoints, in pio mode or just
+ * to manually advance the queue after short OUT transfers.
+ */
+static void handle_ep_small (struct net2280_ep *ep)
+{
+	struct net2280_request	*req;
+	u32			t;
+	/* 0 error, 1 mid-data, 2 done */
+	int			mode = 1;
+
+	if (!list_empty (&ep->queue))
+		req = list_entry (ep->queue.next,
+			struct net2280_request, queue);
+	else
+		req = 0;
+
+	/* ack all, and handle what we care about */
+	t = readl (&ep->regs->ep_stat);
+	ep->irqs++;
+#if 0
+	VDEBUG (ep->dev, "%s ack ep_stat %08x, req %p\n",
+			ep->ep.name, t, req ? &req->req : 0);
+#endif
+	writel (t & ~(1 << NAK_OUT_PACKETS), &ep->regs->ep_stat);
+
+	/* for ep0, monitor token irqs to catch data stage length errors
+	 * and to synchronize on status.
+	 *
+	 * also, to defer reporting of protocol stalls ... here's where
+	 * data or status first appears, handling stalls here should never
+	 * cause trouble on the host side..
+	 *
+	 * control requests could be slightly faster without token synch for
+	 * status, but status can jam up that way.
+	 */
+	if (unlikely (ep->num == 0)) {
+		if (ep->is_in) {
+			/* status; stop NAKing */
+			if (t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)) {
+				if (ep->dev->protocol_stall) {
+					ep->stopped = 1;
+					set_halt (ep);
+				}
+				mode = 2;
+			/* reply to extra IN data tokens with a zlp */
+			} else if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) {
+				if (ep->dev->protocol_stall) {
+					ep->stopped = 1;
+					set_halt (ep);
+					mode = 2;
+				} else if (!req && ep->stopped)
+					write_fifo (ep, 0);
+			}
+		} else {
+			/* status; stop NAKing */
+			if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) {
+				if (ep->dev->protocol_stall) {
+					ep->stopped = 1;
+					set_halt (ep);
+				}
+				mode = 2;
+			/* an extra OUT token is an error */
+			} else if (((t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT))
+					&& req
+					&& req->req.actual == req->req.length)
+					|| !req) {
+				ep->dev->protocol_stall = 1;
+				set_halt (ep);
+				ep->stopped = 1;
+				if (req)
+					done (ep, req, -EOVERFLOW);
+				req = 0;
+			}
+		}
+	}
+
+	if (unlikely (!req))
+		return;
+
+	/* manual DMA queue advance after short OUT */
+	if (likely (ep->dma != 0)) {
+		if (t & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) {
+			u32	count;
+
+			/* TRANSFERRED works around OUT_DONE erratum 0112.
+			 * we expect (N <= maxpacket) bytes; host wrote M.
+			 * iff (M < N) we won't ever see a DMA interrupt.
+			 */
+			count = readl (&ep->dma->dmacount);
+			count &= DMA_BYTE_COUNT_MASK;
+			if (!req->dma_done) {
+				/* dma can finish with the FIFO non-empty,
+				 * on (M > N) errors.
+				 */
+				while (count && (t & (1 << FIFO_EMPTY)) == 0) {
+					cpu_relax ();
+					t = readl (&ep->regs->ep_stat);
+					count = readl (&ep->dma->dmacount);
+					count &= DMA_BYTE_COUNT_MASK;
+				}
+			}
+
+			/* stop DMA, leave ep NAKing */
+			writel ((1 << DMA_ABORT), &ep->dma->dmastat);
+			spin_stop_dma (ep->dma);
+
+			/* buffer might have been too small */
+			t = readl (&ep->regs->ep_avail);
+			if (t != 0)
+				DEBUG (ep->dev, "%s dma, discard %d len %d\n",
+						ep->ep.name, t, count);
+			dma_done (ep, req, count, t ? -EOVERFLOW : 0);
+
+			/* also flush to prevent erratum 0106 trouble */
+			if (t || ep->dev->chiprev == 0x0100)
+				out_flush (ep);
+
+			/* restart dma (still NAKing OUT!) if needed */
+			if (!list_empty (&ep->queue))
+				restart_dma (ep);
+		} else
+			DEBUG (ep->dev, "%s dma ep_stat %08x ??\n",
+					ep->ep.name, t);
+		return;
+
+	/* data packet(s) received (in the fifo, OUT) */
+	} else if (t & (1 << DATA_PACKET_RECEIVED_INTERRUPT)) {
+		if (read_fifo (ep, req) && ep->num != 0)
+			mode = 2;
+
+	/* data packet(s) transmitted (IN) */
+	} else if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)) {
+		unsigned	len;
+
+		len = req->req.length - req->req.actual;
+		len = min (ep->ep.maxpacket, len);
+		req->req.actual += len;
+
+		/* if we wrote it all, we're usually done */
+		if (req->req.actual == req->req.length) {
+			if (ep->num == 0) {
+				/* wait for control status */
+				if (mode != 2)
+					req = 0;
+			} else if (!req->req.zero || len != ep->ep.maxpacket)
+				mode = 2;
+		}
+
+	/* there was nothing to do ...  */
+	} else if (mode == 1)
+		return;
+
+	/* done */
+	if (mode == 2) {
+		/* stream endpoints often resubmit/unlink in completion */
+		done (ep, req, 0);
+
+		/* maybe advance queue to next request */
+		if (ep->num == 0) {
+			/* NOTE:  net2280 could let gadget driver start the
+			 * status stage later. since not all controllers let
+			 * them control that, the api doesn't (yet) allow it.
+			 */
+			if (!ep->stopped)
+				allow_status (ep);
+			req = 0;
+		} else {
+			if (!list_empty (&ep->queue) && !ep->stopped)
+				req = list_entry (ep->queue.next,
+					struct net2280_request, queue);
+			else
+				req = 0;
+			if (req && !ep->is_in)
+				stop_out_naking (ep);
+		}
+	}
+
+	/* is there a buffer for the next packet?
+	 * for best streaming performance, make sure there is one.
+	 */
+	if (req && !ep->stopped) {
+
+		/* load IN fifo with next packet (may be zlp) */
+		if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT))
+			write_fifo (ep, &req->req);
+	}
+}
+
+static struct net2280_ep *
+get_ep_by_addr (struct net2280 *dev, u16 wIndex)
+{
+	struct net2280_ep	*ep;
+
+	if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0)
+		return &dev->ep [0];
+	list_for_each_entry (ep, &dev->gadget.ep_list, ep.ep_list) {
+		u8	bEndpointAddress;
+
+		if (!ep->desc)
+			continue;
+		bEndpointAddress = ep->desc->bEndpointAddress;
+		if ((wIndex ^ bEndpointAddress) & USB_DIR_IN)
+			continue;
+		if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f))
+			return ep;
+	}
+	return 0;
+}
+
+static void handle_stat0_irqs (struct net2280 *dev, u32 stat)
+{
+	struct net2280_ep	*ep;
+	u32			num, scratch;
+
+	/* most of these don't need individual acks */
+	stat &= ~(1 << INTA_ASSERTED);
+	if (!stat)
+		return;
+	// DEBUG (dev, "irqstat0 %04x\n", stat);
+
+	/* starting a control request? */
+	if (unlikely (stat & (1 << SETUP_PACKET_INTERRUPT))) {
+		union {
+			u32			raw [2];
+			struct usb_ctrlrequest	r;
+		} u;
+		int				tmp = 0;
+		struct net2280_request		*req;
+
+		if (dev->gadget.speed == USB_SPEED_UNKNOWN) {
+			if (readl (&dev->usb->usbstat) & (1 << HIGH_SPEED))
+				dev->gadget.speed = USB_SPEED_HIGH;
+			else
+				dev->gadget.speed = USB_SPEED_FULL;
+			net2280_led_speed (dev, dev->gadget.speed);
+			DEBUG (dev, "%s speed\n",
+				(dev->gadget.speed == USB_SPEED_HIGH)
+					? "high" : "full");
+		}
+
+		ep = &dev->ep [0];
+		ep->irqs++;
+
+		/* make sure any leftover request state is cleared */
+		stat &= ~(1 << ENDPOINT_0_INTERRUPT);
+		while (!list_empty (&ep->queue)) {
+			req = list_entry (ep->queue.next,
+					struct net2280_request, queue);
+			done (ep, req, (req->req.actual == req->req.length)
+						? 0 : -EPROTO);
+		}
+		ep->stopped = 0;
+		dev->protocol_stall = 0;
+		writel (  (1 << TIMEOUT)
+			| (1 << USB_STALL_SENT)
+			| (1 << USB_IN_NAK_SENT)
+			| (1 << USB_IN_ACK_RCVD)
+			| (1 << USB_OUT_PING_NAK_SENT)
+			| (1 << USB_OUT_ACK_SENT)
+			| (1 << FIFO_OVERFLOW)
+			| (1 << FIFO_UNDERFLOW)
+			| (1 << SHORT_PACKET_OUT_DONE_INTERRUPT)
+			| (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)
+			| (1 << DATA_PACKET_RECEIVED_INTERRUPT)
+			| (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
+			| (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
+			| (1 << DATA_IN_TOKEN_INTERRUPT)
+			, &ep->regs->ep_stat);
+		u.raw [0] = readl (&dev->usb->setup0123);
+		u.raw [1] = readl (&dev->usb->setup4567);
+		
+		cpu_to_le32s (&u.raw [0]);
+		cpu_to_le32s (&u.raw [1]);
+
+		le16_to_cpus (&u.r.wValue);
+		le16_to_cpus (&u.r.wIndex);
+		le16_to_cpus (&u.r.wLength);
+
+		/* ack the irq */
+		writel (1 << SETUP_PACKET_INTERRUPT, &dev->regs->irqstat0);
+		stat ^= (1 << SETUP_PACKET_INTERRUPT);
+
+		/* watch control traffic at the token level, and force
+		 * synchronization before letting the status stage happen.
+		 * FIXME ignore tokens we'll NAK, until driver responds.
+		 * that'll mean a lot less irqs for some drivers.
+		 */
+		ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0;
+		if (ep->is_in)
+			scratch = (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
+				| (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
+				| (1 << DATA_IN_TOKEN_INTERRUPT);
+		else
+			scratch = (1 << DATA_PACKET_RECEIVED_INTERRUPT)
+				| (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
+				| (1 << DATA_IN_TOKEN_INTERRUPT);
+		writel (scratch, &dev->epregs [0].ep_irqenb);
+
+		/* we made the hardware handle most lowlevel requests;
+		 * everything else goes uplevel to the gadget code.
+		 */
+		switch (u.r.bRequest) {
+		case USB_REQ_GET_STATUS: {
+			struct net2280_ep	*e;
+			u16			status;
+
+			/* hw handles device and interface status */
+			if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT))
+				goto delegate;
+			if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0
+					|| u.r.wLength > 2)
+				goto do_stall;
+
+			if (readl (&e->regs->ep_rsp)
+					& (1 << SET_ENDPOINT_HALT))
+				status = __constant_cpu_to_le16 (1);
+			else
+				status = __constant_cpu_to_le16 (0);
+
+			/* don't bother with a request object! */
+			writel (0, &dev->epregs [0].ep_irqenb);
+			set_fifo_bytecount (ep, u.r.wLength);
+			writel (status, &dev->epregs [0].ep_data);
+			allow_status (ep);
+			VDEBUG (dev, "%s stat %02x\n", ep->ep.name, status);
+			goto next_endpoints;
+			}
+			break;
+		case USB_REQ_CLEAR_FEATURE: {
+			struct net2280_ep	*e;
+
+			/* hw handles device features */
+			if (u.r.bRequestType != USB_RECIP_ENDPOINT)
+				goto delegate;
+			if (u.r.wValue != 0 /* HALT feature */
+					|| u.r.wLength != 0)
+				goto do_stall;
+			if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0)
+				goto do_stall;
+			clear_halt (e);
+			allow_status (ep);
+			VDEBUG (dev, "%s clear halt\n", ep->ep.name);
+			goto next_endpoints;
+			}
+			break;
+		case USB_REQ_SET_FEATURE: {
+			struct net2280_ep	*e;
+
+			/* hw handles device features */
+			if (u.r.bRequestType != USB_RECIP_ENDPOINT)
+				goto delegate;
+			if (u.r.wValue != 0 /* HALT feature */
+					|| u.r.wLength != 0)
+				goto do_stall;
+			if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0)
+				goto do_stall;
+			set_halt (e);
+			allow_status (ep);
+			VDEBUG (dev, "%s set halt\n", ep->ep.name);
+			goto next_endpoints;
+			}
+			break;
+		default:
+delegate:
+			VDEBUG (dev, "setup %02x.%02x v%04x i%04x "
+				"ep_cfg %08x\n",
+				u.r.bRequestType, u.r.bRequest,
+				u.r.wValue, u.r.wIndex,
+				readl (&ep->regs->ep_cfg));
+			spin_unlock (&dev->lock);
+			tmp = dev->driver->setup (&dev->gadget, &u.r);
+			spin_lock (&dev->lock);
+		}
+
+		/* stall ep0 on error */
+		if (tmp < 0) {
+do_stall:
+			VDEBUG (dev, "req %02x.%02x protocol STALL; stat %d\n",
+					u.r.bRequestType, u.r.bRequest, tmp);
+			dev->protocol_stall = 1;
+		}
+
+		/* some in/out token irq should follow; maybe stall then.
+		 * driver must queue a request (even zlp) or halt ep0
+		 * before the host times out.
+		 */
+	}
+
+next_endpoints:
+	/* endpoint data irq ? */
+	scratch = stat & 0x7f;
+	stat &= ~0x7f;
+	for (num = 0; scratch; num++) {
+		u32		t;
+
+		/* do this endpoint's FIFO and queue need tending? */
+		t = 1 << num;
+		if ((scratch & t) == 0)
+			continue;
+		scratch ^= t;
+
+		ep = &dev->ep [num];
+		handle_ep_small (ep);
+	}
+
+	if (stat)
+		DEBUG (dev, "unhandled irqstat0 %08x\n", stat);
+}
+
+#define DMA_INTERRUPTS ( \
+		  (1 << DMA_D_INTERRUPT) \
+		| (1 << DMA_C_INTERRUPT) \
+		| (1 << DMA_B_INTERRUPT) \
+		| (1 << DMA_A_INTERRUPT))
+#define	PCI_ERROR_INTERRUPTS ( \
+		  (1 << PCI_MASTER_ABORT_RECEIVED_INTERRUPT) \
+		| (1 << PCI_TARGET_ABORT_RECEIVED_INTERRUPT) \
+		| (1 << PCI_RETRY_ABORT_INTERRUPT))
+
+static void handle_stat1_irqs (struct net2280 *dev, u32 stat)
+{
+	struct net2280_ep	*ep;
+	u32			tmp, num, scratch;
+
+	/* after disconnect there's nothing else to do! */
+	tmp = (1 << VBUS_INTERRUPT) | (1 << ROOT_PORT_RESET_INTERRUPT);
+	if (stat & tmp) {
+		writel (tmp, &dev->regs->irqstat1);
+		if (((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) != 0
+			|| (readl (&dev->usb->usbctl) & (1 << VBUS_PIN)) == 0
+			) && dev->gadget.speed != USB_SPEED_UNKNOWN) {
+			DEBUG (dev, "disconnect %s\n",
+					dev->driver->driver.name);
+			stop_activity (dev, dev->driver);
+			ep0_start (dev);
+			return;
+		}
+		stat &= ~tmp;
+
+		/* vBUS can bounce ... one of many reasons to ignore the
+		 * notion of hotplug events on bus connect/disconnect!
+		 */
+		if (!stat)
+			return;
+	}
+
+	/* NOTE: we don't actually suspend the hardware; that starts to
+	 * interact with PCI power management, and needs something like a
+	 * controller->suspend() call to clear SUSPEND_REQUEST_INTERRUPT.
+	 * we shouldn't see resume interrupts.
+	 * for rev 0100, this also avoids erratum 0102.
+	 */
+	tmp = (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT);
+	if (stat & tmp) {
+		if (dev->driver->suspend)
+			dev->driver->suspend (&dev->gadget);
+		stat &= ~tmp;
+	}
+	stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT);
+
+	/* clear any other status/irqs */
+	if (stat)
+		writel (stat, &dev->regs->irqstat1);
+
+	/* some status we can just ignore */
+	stat &= ~((1 << CONTROL_STATUS_INTERRUPT)
+			| (1 << RESUME_INTERRUPT)
+			| (1 << SOF_INTERRUPT));
+	if (!stat)
+		return;
+	// DEBUG (dev, "irqstat1 %08x\n", stat);
+
+	/* DMA status, for ep-{a,b,c,d} */
+	scratch = stat & DMA_INTERRUPTS;
+	stat &= ~DMA_INTERRUPTS;
+	scratch >>= 9;
+	for (num = 0; scratch; num++) {
+		struct net2280_dma_regs	*dma;
+
+		tmp = 1 << num;
+		if ((tmp & scratch) == 0)
+			continue;
+		scratch ^= tmp;
+
+		ep = &dev->ep [num + 1];
+		dma = ep->dma;
+
+		if (!dma)
+			continue;
+
+		/* clear ep's dma status */
+		tmp = readl (&dma->dmastat);
+		writel (tmp, &dma->dmastat);
+
+#ifdef USE_DMA_CHAINING
+		/* chaining should stop only on error (which?)
+		 * or (stat0 codepath) short OUT transfer.
+		 */
+#else
+		if ((tmp & (1 << DMA_TRANSACTION_DONE_INTERRUPT)) == 0) {
+			DEBUG (ep->dev, "%s no xact done? %08x\n",
+				ep->ep.name, tmp);
+			continue;
+		}
+		stop_dma (ep->dma);
+#endif
+
+		/* OUT transfers terminate when the data from the
+		 * host is in our memory.  Process whatever's done.
+		 * On this path, we know transfer's last packet wasn't
+		 * less than req->length. NAK_OUT_PACKETS may be set,
+		 * or the FIFO may already be holding new packets.
+		 *
+		 * IN transfers can linger in the FIFO for a very
+		 * long time ... we ignore that for now, accounting
+		 * precisely (like PIO does) needs per-packet irqs
+		 */
+		scan_dma_completions (ep);
+
+		/* disable dma on inactive queues; else maybe restart */
+		if (list_empty (&ep->queue)) {
+#ifdef USE_DMA_CHAINING
+			stop_dma (ep->dma);
+#endif
+		} else {
+			tmp = readl (&dma->dmactl);
+			if ((tmp & (1 << DMA_SCATTER_GATHER_ENABLE)) == 0
+					|| (tmp & (1 << DMA_ENABLE)) == 0)
+				restart_dma (ep);
+#ifdef USE_DMA_CHAINING
+			else if (ep->desc->bEndpointAddress & USB_DIR_IN) {
+				struct net2280_request	*req;
+				u32			dmacount;
+
+				/* the descriptor at the head of the chain
+				 * may still have VALID_BIT clear; that's
+				 * used to trigger changing DMA_FIFO_VALIDATE
+				 * (affects automagic zlp writes).
+				 */
+				req = list_entry (ep->queue.next,
+						struct net2280_request, queue);
+				dmacount = req->td->dmacount;
+				dmacount &= __constant_cpu_to_le32 (
+						(1 << VALID_BIT)
+						| DMA_BYTE_COUNT_MASK);
+				if (dmacount && (dmacount & valid_bit) == 0) {
+					stop_dma (ep->dma);
+					restart_dma (ep);
+				}
+			}
+#endif
+		}
+		ep->irqs++;
+	}
+
+	/* NOTE:  there are other PCI errors we might usefully notice.
+	 * if they appear very often, here's where to try recovering.
+	 */
+	if (stat & PCI_ERROR_INTERRUPTS) {
+		ERROR (dev, "pci dma error; stat %08x\n", stat);
+		stat &= ~PCI_ERROR_INTERRUPTS;
+		/* these are fatal errors, but "maybe" they won't
+		 * happen again ...
+		 */
+		stop_activity (dev, dev->driver);
+		ep0_start (dev);
+		stat = 0;
+	}
+
+	if (stat)
+		DEBUG (dev, "unhandled irqstat1 %08x\n", stat);
+}
+
+static irqreturn_t net2280_irq (int irq, void *_dev, struct pt_regs * r)
+{
+	struct net2280		*dev = _dev;
+
+	spin_lock (&dev->lock);
+
+	/* handle disconnect, dma, and more */
+	handle_stat1_irqs (dev, readl (&dev->regs->irqstat1));
+
+	/* control requests and PIO */
+	handle_stat0_irqs (dev, readl (&dev->regs->irqstat0));
+
+	spin_unlock (&dev->lock);
+
+	return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* tear down the binding between this driver and the pci device */
+
+static void net2280_remove (struct pci_dev *pdev)
+{
+	struct net2280		*dev = pci_get_drvdata (pdev);
+
+	/* start with the driver above us */
+	if (dev->driver) {
+		/* should have been done already by driver model core */
+		WARN (dev, "pci remove, driver '%s' is still registered\n",
+				dev->driver->driver.name);
+		usb_gadget_unregister_driver (dev->driver);
+	}
+
+	/* then clean up the resources we allocated during probe() */
+	net2280_led_shutdown (dev);
+	if (dev->requests) {
+		int		i;
+		for (i = 1; i < 5; i++) {
+			if (!dev->ep [i].dummy)
+				continue;
+			pci_pool_free (dev->requests, dev->ep [i].dummy,
+					dev->ep [i].td_dma);
+		}
+		pci_pool_destroy (dev->requests);
+	}
+	if (dev->got_irq)
+		free_irq (pdev->irq, dev);
+	if (dev->regs)
+		iounmap (dev->regs);
+	if (dev->region)
+		release_mem_region (pci_resource_start (pdev, 0),
+				pci_resource_len (pdev, 0));
+	if (dev->enabled)
+		pci_disable_device (pdev);
+	pci_set_drvdata (pdev, 0);
+
+	INFO (dev, "unbind from pci %s\n", pdev->slot_name);
+
+	kfree (dev);
+	the_controller = 0;
+}
+
+/* wrap this driver around the specified device, but
+ * don't respond over USB until a gadget driver binds to us.
+ */
+
+static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct net2280		*dev;
+	unsigned long		resource, len;
+	void			*base = 0;
+	int			retval, i;
+	char			buf [8], *bufp;
+
+	/* if you want to support more than one controller in a system,
+	 * usb_gadget_driver_{register,unregister}() must change.
+	 */
+	if (the_controller) {
+		WARN (the_controller, "ignoring %s\n", pdev->slot_name);
+		return -EBUSY;
+	}
+
+	/* alloc, and start init */
+	dev = kmalloc (sizeof *dev, SLAB_KERNEL);
+	if (dev == NULL){
+		retval = -ENOMEM;
+		goto done;
+	}
+
+	memset (dev, 0, sizeof *dev);
+	spin_lock_init (&dev->lock);
+	dev->pdev = pdev;
+	dev->gadget.ops = &net2280_ops;
+
+	dev->gadget.dev.bus_id = pdev->slot_name;
+	dev->gadget.name = driver_name;
+
+	/* now all the pci goodies ... */
+	if (pci_enable_device (pdev) < 0) {
+   	        retval = -ENODEV;
+		goto done;
+	}
+	dev->enabled = 1;
+
+	/* BAR 0 holds all the registers
+	 * BAR 1 is 8051 memory; unused here (note erratum 0103)
+	 * BAR 2 is fifo memory; unused here
+	 */
+	resource = pci_resource_start (pdev, 0);
+	len = pci_resource_len (pdev, 0);
+	if (!request_mem_region (resource, len, driver_name)) {
+		DEBUG (dev, "controller already in use\n");
+		retval = -EBUSY;
+		goto done;
+	}
+	dev->region = 1;
+
+	base = ioremap_nocache (resource, len);
+	if (base == NULL) {
+		DEBUG (dev, "can't map memory\n");
+		retval = -EFAULT;
+		goto done;
+	}
+	dev->regs = (struct net2280_regs *) base;
+	dev->usb = (struct net2280_usb_regs *) (base + 0x0080);
+	dev->pci = (struct net2280_pci_regs *) (base + 0x0100);
+	dev->dma = (struct net2280_dma_regs *) (base + 0x0180);
+	dev->dep = (struct net2280_dep_regs *) (base + 0x0200);
+	dev->epregs = (struct net2280_ep_regs *) (base + 0x0300);
+
+	/* put into initial config, link up all endpoints */
+	usb_reset (dev);
+	usb_reinit (dev);
+
+	/* irq setup after old hardware is cleaned up */
+	if (!pdev->irq) {
+		ERROR (dev, "No IRQ.  Check PCI setup!\n");
+		retval = -ENODEV;
+		goto done;
+	}
+#ifndef __sparc__
+	snprintf (buf, sizeof buf, "%d", pdev->irq);
+	bufp = buf;
+#else
+	bufp = __irq_itoa(pdev->irq);
+#endif
+	if (request_irq (pdev->irq, net2280_irq, SA_SHIRQ, driver_name, dev)
+			!= 0) {
+		ERROR (dev, "request interrupt %s failed\n", bufp);
+		retval = -EBUSY;
+		goto done;
+	}
+	dev->got_irq = 1;
+
+	/* DMA setup */
+	dev->requests = pci_pool_create ("requests", pdev,
+		sizeof (struct net2280_dma),
+		0 /* no alignment requirements */,
+		0 /* or page-crossing issues */,
+		SLAB_KERNEL /* 2.4 only */ );
+	if (!dev->requests) {
+		DEBUG (dev, "can't get request pool\n");
+		retval = -ENOMEM;
+		goto done;
+	}
+	for (i = 1; i < 5; i++) {
+		struct net2280_dma	*td;
+
+		td = pci_pool_alloc (dev->requests, GFP_KERNEL,
+				&dev->ep [i].td_dma);
+		if (!td) {
+			DEBUG (dev, "can't get dummy %d\n", i);
+			retval = -ENOMEM;
+			goto done;
+		}
+		td->dmacount = 0;	/* not VALID */
+		td->dmaaddr = __constant_cpu_to_le32 (DMA_ADDR_INVALID);
+		dev->ep [i].dummy = td;
+	}
+
+	/* enable lower-overhead pci memory bursts during DMA */
+	writel ((1 << PCI_RETRY_ABORT_ENABLE)
+			| (1 << DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE)
+			| (1 << DMA_READ_MULTIPLE_ENABLE)
+			| (1 << DMA_READ_LINE_ENABLE)
+			, &dev->pci->pcimstctl);
+	/* erratum 0115 shouldn't appear: Linux inits PCI_LATENCY_TIMER */
+	pci_set_master (pdev);
+#ifdef	HAVE_PCI_SET_MWI
+	pci_set_mwi (pdev);
+#endif
+
+	/* ... also flushes any posted pci writes */
+	dev->chiprev = get_idx_reg (dev->regs, REG_CHIPREV) & 0xffff;
+
+	/* done */
+	pci_set_drvdata (pdev, dev);
+	INFO (dev, "%s\n", driver_desc);
+	INFO (dev, "irq %s, pci mem %p, chip rev %04x\n",
+			bufp, base, dev->chiprev);
+	bufp = DRIVER_VERSION
+#ifndef USE_DMA_CHAINING
+		" (no dma chain)"
+#endif
+#ifdef NET2280_DMA_OUT_WORKAROUND
+		" (no dma out)"
+#endif
+		;
+	INFO (dev, "version: %s\n", bufp);
+	the_controller = dev;
+
+	return 0;
+
+done:
+	if (dev)
+		net2280_remove (pdev);
+	return retval;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static struct pci_device_id pci_ids [] = { {
+	.class = 	((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+	.class_mask = 	~0,
+	.vendor =	0x17cc,
+	.device =	0x2280,
+	.subvendor =	PCI_ANY_ID,
+	.subdevice =	PCI_ANY_ID,
+
+}, { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE (pci, pci_ids);
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct pci_driver net2280_pci_driver = {
+	.name =		(char *) driver_name,
+	.id_table =	pci_ids,
+
+	.probe =	net2280_probe,
+	.remove =	net2280_remove,
+
+	/* FIXME add power management support */
+};
+
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_AUTHOR ("David Brownell");
+MODULE_LICENSE ("GPL");
+
+static int __init init (void)
+{
+	return pci_module_init (&net2280_pci_driver);
+}
+module_init (init);
+
+static void __exit cleanup (void)
+{
+	pci_unregister_driver (&net2280_pci_driver);
+}
+module_exit (cleanup);
diff -urN linux-2.4.22-bk28/drivers/usb/gadget/net2280.h linux-2.4.22-bk29/drivers/usb/gadget/net2280.h
--- linux-2.4.22-bk28/drivers/usb/gadget/net2280.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-bk29/drivers/usb/gadget/net2280.h	2003-10-06 02:50:31.000000000 -0700
@@ -0,0 +1,755 @@
+/*
+ * NetChip 2280 high/full speed USB device controller.
+ * Unlike many such controllers, this one talks PCI.
+ */
+
+/*
+ * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com)
+ * Copyright (C) 2003 David Brownell
+ *
+ * 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
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* NET2280 MEMORY MAPPED REGISTERS
+ *
+ * The register layout came from the chip documentation, and the bit
+ * number definitions were extracted from chip specification.
+ *
+ * Use the shift operator ('<<') to build bit masks, with readl/writel
+ * to access the registers through PCI.
+ */
+
+/* main registers, BAR0 + 0x0000 */
+struct net2280_regs {
+	// offset 0x0000
+	u32		devinit;
+#define     LOCAL_CLOCK_FREQUENCY                               8
+#define     FORCE_PCI_RESET                                     7
+#define     PCI_ID                                              6
+#define     PCI_ENABLE                                          5
+#define     FIFO_SOFT_RESET                                     4
+#define     CFG_SOFT_RESET                                      3
+#define     PCI_SOFT_RESET                                      2
+#define     USB_SOFT_RESET                                      1
+#define     M8051_RESET                                         0
+	u32		eectl;
+#define     EEPROM_ADDRESS_WIDTH                                23
+#define     EEPROM_CHIP_SELECT_ACTIVE                           22
+#define     EEPROM_PRESENT                                      21
+#define     EEPROM_VALID                                        20
+#define     EEPROM_BUSY                                         19
+#define     EEPROM_CHIP_SELECT_ENABLE                           18
+#define     EEPROM_BYTE_READ_START                              17
+#define     EEPROM_BYTE_WRITE_START                             16
+#define     EEPROM_READ_DATA                                    8
+#define     EEPROM_WRITE_DATA                                   0
+	u32		eeclkfreq;
+	u32		_unused0;
+	// offset 0x0010
+
+	u32		pciirqenb0;		/* interrupt PCI master ... */
+#define     SETUP_PACKET_INTERRUPT_ENABLE                       7
+#define     ENDPOINT_F_INTERRUPT_ENABLE                         6
+#define     ENDPOINT_E_INTERRUPT_ENABLE                         5
+#define     ENDPOINT_D_INTERRUPT_ENABLE                         4
+#define     ENDPOINT_C_INTERRUPT_ENABLE                         3
+#define     ENDPOINT_B_INTERRUPT_ENABLE                         2
+#define     ENDPOINT_A_INTERRUPT_ENABLE                         1
+#define     ENDPOINT_0_INTERRUPT_ENABLE                         0
+	u32		pciirqenb1;
+#define     PCI_INTERRUPT_ENABLE                                31
+#define     POWER_STATE_CHANGE_INTERRUPT_ENABLE                 27
+#define     PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE                26
+#define     PCI_PARITY_ERROR_INTERRUPT_ENABLE                   25
+#define     PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE          20
+#define     PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE          19
+#define     PCI_TARGET_ABORT_ASSERTED_INTERRUPT_ENABLE          18
+#define     PCI_RETRY_ABORT_INTERRUPT_ENABLE                    17
+#define     PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE              16
+#define     GPIO_INTERRUPT_ENABLE                               13
+#define     DMA_D_INTERRUPT_ENABLE                              12
+#define     DMA_C_INTERRUPT_ENABLE                              11
+#define     DMA_B_INTERRUPT_ENABLE                              10
+#define     DMA_A_INTERRUPT_ENABLE                              9
+#define     EEPROM_DONE_INTERRUPT_ENABLE                        8
+#define     VBUS_INTERRUPT_ENABLE                               7
+#define     CONTROL_STATUS_INTERRUPT_ENABLE                     6
+#define     ROOT_PORT_RESET_INTERRUPT_ENABLE                    4
+#define     SUSPEND_REQUEST_INTERRUPT_ENABLE                    3
+#define     SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE             2
+#define     RESUME_INTERRUPT_ENABLE                             1
+#define     SOF_INTERRUPT_ENABLE                                0
+	u32		cpu_irqenb0;		/* ... or onboard 8051 */
+#define     SETUP_PACKET_INTERRUPT_ENABLE                       7
+#define     ENDPOINT_F_INTERRUPT_ENABLE                         6
+#define     ENDPOINT_E_INTERRUPT_ENABLE                         5
+#define     ENDPOINT_D_INTERRUPT_ENABLE                         4
+#define     ENDPOINT_C_INTERRUPT_ENABLE                         3
+#define     ENDPOINT_B_INTERRUPT_ENABLE                         2
+#define     ENDPOINT_A_INTERRUPT_ENABLE                         1
+#define     ENDPOINT_0_INTERRUPT_ENABLE                         0
+	u32		cpu_irqenb1;
+#define     CPU_INTERRUPT_ENABLE                                31
+#define     POWER_STATE_CHANGE_INTERRUPT_ENABLE                 27
+#define     PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE                26
+#define     PCI_PARITY_ERROR_INTERRUPT_ENABLE                   25
+#define     PCI_INTA_INTERRUPT_ENABLE                           24
+#define     PCI_PME_INTERRUPT_ENABLE                            23
+#define     PCI_SERR_INTERRUPT_ENABLE                           22
+#define     PCI_PERR_INTERRUPT_ENABLE                           21
+#define     PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE          20
+#define     PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE          19
+#define     PCI_RETRY_ABORT_INTERRUPT_ENABLE                    17
+#define     PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE              16
+#define     GPIO_INTERRUPT_ENABLE                               13
+#define     DMA_D_INTERRUPT_ENABLE                              12
+#define     DMA_C_INTERRUPT_ENABLE                              11
+#define     DMA_B_INTERRUPT_ENABLE                              10
+#define     DMA_A_INTERRUPT_ENABLE                              9
+#define     EEPROM_DONE_INTERRUPT_ENABLE                        8
+#define     VBUS_INTERRUPT_ENABLE                               7
+#define     CONTROL_STATUS_INTERRUPT_ENABLE                     6
+#define     ROOT_PORT_RESET_INTERRUPT_ENABLE                    4
+#define     SUSPEND_REQUEST_INTERRUPT_ENABLE                    3
+#define     SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE             2
+#define     RESUME_INTERRUPT_ENABLE                             1
+#define     SOF_INTERRUPT_ENABLE                                0
+
+	// offset 0x0020
+	u32		_unused1;
+	u32		usbirqenb1;
+#define     USB_INTERRUPT_ENABLE                                31
+#define     POWER_STATE_CHANGE_INTERRUPT_ENABLE                 27
+#define     PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE                26
+#define     PCI_PARITY_ERROR_INTERRUPT_ENABLE                   25
+#define     PCI_INTA_INTERRUPT_ENABLE                           24
+#define     PCI_PME_INTERRUPT_ENABLE                            23
+#define     PCI_SERR_INTERRUPT_ENABLE                           22
+#define     PCI_PERR_INTERRUPT_ENABLE                           21
+#define     PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE          20
+#define     PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE          19
+#define     PCI_RETRY_ABORT_INTERRUPT_ENABLE                    17
+#define     PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE              16
+#define     GPIO_INTERRUPT_ENABLE                               13
+#define     DMA_D_INTERRUPT_ENABLE                              12
+#define     DMA_C_INTERRUPT_ENABLE                              11
+#define     DMA_B_INTERRUPT_ENABLE                              10
+#define     DMA_A_INTERRUPT_ENABLE                              9
+#define     EEPROM_DONE_INTERRUPT_ENABLE                        8
+#define     VBUS_INTERRUPT_ENABLE                               7
+#define     CONTROL_STATUS_INTERRUPT_ENABLE                     6
+#define     ROOT_PORT_RESET_INTERRUPT_ENABLE                    4
+#define     SUSPEND_REQUEST_INTERRUPT_ENABLE                    3
+#define     SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE             2
+#define     RESUME_INTERRUPT_ENABLE                             1
+#define     SOF_INTERRUPT_ENABLE                                0
+	u32		irqstat0;
+#define     INTA_ASSERTED                                       12
+#define     SETUP_PACKET_INTERRUPT                              7
+#define     ENDPOINT_F_INTERRUPT                                6
+#define     ENDPOINT_E_INTERRUPT                                5
+#define     ENDPOINT_D_INTERRUPT                                4
+#define     ENDPOINT_C_INTERRUPT                                3
+#define     ENDPOINT_B_INTERRUPT                                2
+#define     ENDPOINT_A_INTERRUPT                                1
+#define     ENDPOINT_0_INTERRUPT                                0
+	u32		irqstat1;
+#define     POWER_STATE_CHANGE_INTERRUPT                        27
+#define     PCI_ARBITER_TIMEOUT_INTERRUPT                       26
+#define     PCI_PARITY_ERROR_INTERRUPT                          25
+#define     PCI_INTA_INTERRUPT                                  24
+#define     PCI_PME_INTERRUPT                                   23
+#define     PCI_SERR_INTERRUPT                                  22
+#define     PCI_PERR_INTERRUPT                                  21
+#define     PCI_MASTER_ABORT_RECEIVED_INTERRUPT                 20
+#define     PCI_TARGET_ABORT_RECEIVED_INTERRUPT                 19
+#define     PCI_RETRY_ABORT_INTERRUPT                           17
+#define     PCI_MASTER_CYCLE_DONE_INTERRUPT                     16
+#define     GPIO_INTERRUPT                                      13
+#define     DMA_D_INTERRUPT                                     12
+#define     DMA_C_INTERRUPT                                     11
+#define     DMA_B_INTERRUPT                                     10
+#define     DMA_A_INTERRUPT                                     9
+#define     EEPROM_DONE_INTERRUPT                               8
+#define     VBUS_INTERRUPT                                      7
+#define     CONTROL_STATUS_INTERRUPT                            6
+#define     ROOT_PORT_RESET_INTERRUPT                           4
+#define     SUSPEND_REQUEST_INTERRUPT                           3
+#define     SUSPEND_REQUEST_CHANGE_INTERRUPT                    2
+#define     RESUME_INTERRUPT                                    1
+#define     SOF_INTERRUPT                                       0
+	// offset 0x0030
+	u32		idxaddr;
+	u32		idxdata;
+	u32		fifoctl;
+#define     PCI_BASE2_RANGE                                     16
+#define     IGNORE_FIFO_AVAILABILITY                            3
+#define     PCI_BASE2_SELECT                                    2
+#define     FIFO_CONFIGURATION_SELECT                           0
+	u32		_unused2;
+	// offset 0x0040
+	u32		memaddr;
+#define     START                                               28
+#define     DIRECTION                                           27
+#define     FIFO_DIAGNOSTIC_SELECT                              24
+#define     MEMORY_ADDRESS                                      0
+	u32		memdata0;
+	u32		memdata1;
+	u32		_unused3;
+	// offset 0x0050
+	u32		gpioctl;
+#define     GPIO3_LED_SELECT                                    12
+#define     GPIO3_INTERRUPT_ENABLE                              11
+#define     GPIO2_INTERRUPT_ENABLE                              10
+#define     GPIO1_INTERRUPT_ENABLE                              9
+#define     GPIO0_INTERRUPT_ENABLE                              8
+#define     GPIO3_OUTPUT_ENABLE                                 7
+#define     GPIO2_OUTPUT_ENABLE                                 6
+#define     GPIO1_OUTPUT_ENABLE                                 5
+#define     GPIO0_OUTPUT_ENABLE                                 4
+#define     GPIO3_DATA                                          3
+#define     GPIO2_DATA                                          2
+#define     GPIO1_DATA                                          1
+#define     GPIO0_DATA                                          0
+	u32		gpiostat;
+#define     GPIO3_INTERRUPT                                     3
+#define     GPIO2_INTERRUPT                                     2
+#define     GPIO1_INTERRUPT                                     1
+#define     GPIO0_INTERRUPT                                     0
+} __attribute__ ((packed));
+
+/* usb control, BAR0 + 0x0080 */
+struct net2280_usb_regs {
+	// offset 0x0080
+	u32		stdrsp;
+#define     STALL_UNSUPPORTED_REQUESTS                          31
+#define     SET_TEST_MODE                                       16
+#define     GET_OTHER_SPEED_CONFIGURATION                       15
+#define     GET_DEVICE_QUALIFIER                                14
+#define     SET_ADDRESS                                         13
+#define     ENDPOINT_SET_CLEAR_HALT                             12
+#define     DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP               11
+#define     GET_STRING_DESCRIPTOR_2                             10
+#define     GET_STRING_DESCRIPTOR_1                             9
+#define     GET_STRING_DESCRIPTOR_0                             8
+#define     GET_SET_INTERFACE                                   6
+#define     GET_SET_CONFIGURATION                               5
+#define     GET_CONFIGURATION_DESCRIPTOR                        4
+#define     GET_DEVICE_DESCRIPTOR                               3
+#define     GET_ENDPOINT_STATUS                                 2
+#define     GET_INTERFACE_STATUS                                1
+#define     GET_DEVICE_STATUS                                   0
+	u32		prodvendid;
+#define     PRODUCT_ID                                          16
+#define     VENDOR_ID                                           0
+	u32		relnum;
+	u32		usbctl;
+#define     SERIAL_NUMBER_INDEX                                 16
+#define     PRODUCT_ID_STRING_ENABLE                            13
+#define     VENDOR_ID_STRING_ENABLE                             12
+#define     USB_ROOT_PORT_WAKEUP_ENABLE                         11
+#define     VBUS_PIN                                            10
+#define     TIMED_DISCONNECT                                    9
+#define     SUSPEND_IMMEDIATELY                                 7
+#define     SELF_POWERED_USB_DEVICE                             6
+#define     REMOTE_WAKEUP_SUPPORT                               5
+#define     PME_POLARITY                                        4
+#define     USB_DETECT_ENABLE                                   3
+#define     PME_WAKEUP_ENABLE                                   2
+#define     DEVICE_REMOTE_WAKEUP_ENABLE                         1
+#define     SELF_POWERED_STATUS                                 0
+	// offset 0x0090
+	u32		usbstat;
+#define     HIGH_SPEED                                          7
+#define     FULL_SPEED                                          6
+#define     GENERATE_RESUME                                     5
+#define     GENERATE_DEVICE_REMOTE_WAKEUP                       4
+	u32		xcvrdiag;
+#define     FORCE_HIGH_SPEED_MODE                               31
+#define     FORCE_FULL_SPEED_MODE                               30
+#define     USB_TEST_MODE                                       24
+#define     LINE_STATE                                          16
+#define     TRANSCEIVER_OPERATION_MODE                          2
+#define     TRANSCEIVER_SELECT                                  1
+#define     TERMINATION_SELECT                                  0
+	u32		setup0123;
+	u32		setup4567;
+	// offset 0x0090
+	u32		_unused0;
+	u32		ouraddr;
+#define     FORCE_IMMEDIATE                                     7
+#define     OUR_USB_ADDRESS                                     0
+	u32		ourconfig;
+} __attribute__ ((packed));
+
+/* pci control, BAR0 + 0x0100 */
+struct net2280_pci_regs {
+	// offset 0x0100
+	u32		 pcimstctl;
+#define     PCI_ARBITER_PARK_SELECT                             13
+#define     PCI_MULTI LEVEL_ARBITER                             12
+#define     PCI_RETRY_ABORT_ENABLE                              11
+#define     DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE              10
+#define     DMA_READ_MULTIPLE_ENABLE                            9
+#define     DMA_READ_LINE_ENABLE                                8
+#define     PCI_MASTER_COMMAND_SELECT                           6
+#define         MEM_READ_OR_WRITE                                   0
+#define         IO_READ_OR_WRITE                                    1
+#define         CFG_READ_OR_WRITE                                   2
+#define     PCI_MASTER_START                                    5
+#define     PCI_MASTER_READ_WRITE                               4
+#define         PCI_MASTER_WRITE                                    0
+#define         PCI_MASTER_READ                                     1
+#define     PCI_MASTER_BYTE_WRITE_ENABLES                       0
+	u32		 pcimstaddr;
+	u32		 pcimstdata;
+	u32		 pcimststat;
+#define     PCI_ARBITER_CLEAR                                   2
+#define     PCI_EXTERNAL_ARBITER                                1
+#define     PCI_HOST_MODE                                       0
+} __attribute__ ((packed));
+
+/* dma control, BAR0 + 0x0180 ... array of four structs like this,
+ * for channels 0..3.  see also struct net2280_dma:  descriptor
+ * that can be loaded into some of these registers.
+ */
+struct net2280_dma_regs {	/* [11.7] */
+	// offset 0x0180, 0x01a0, 0x01c0, 0x01e0, 
+	u32		dmactl;
+#define     DMA_SCATTER_GATHER_DONE_INTERRUPT_ENABLE            25
+#define     DMA_CLEAR_COUNT_ENABLE                              21
+#define     DESCRIPTOR_POLLING_RATE                             19
+#define         POLL_CONTINUOUS                                     0
+#define         POLL_1_USEC                                         1
+#define         POLL_100_USEC                                       2
+#define         POLL_1_MSEC                                         3
+#define     DMA_VALID_BIT_POLLING_ENABLE                        18
+#define     DMA_VALID_BIT_ENABLE                                17
+#define     DMA_SCATTER_GATHER_ENABLE                           16
+#define     DMA_OUT_AUTO_START_ENABLE                           4
+#define     DMA_PREEMPT_ENABLE                                  3
+#define     DMA_FIFO_VALIDATE                                   2
+#define     DMA_ENABLE                                          1
+#define     DMA_ADDRESS_HOLD                                    0
+	u32		dmastat;
+#define     DMA_SCATTER_GATHER_DONE_INTERRUPT                   25
+#define     DMA_TRANSACTION_DONE_INTERRUPT                      24
+#define     DMA_ABORT                                           1
+#define     DMA_START                                           0
+	u32		_unused0 [2];
+	// offset 0x0190, 0x01b0, 0x01d0, 0x01f0, 
+	u32		dmacount;
+#define     VALID_BIT                                           31
+#define     DMA_DIRECTION                                       30
+#define     DMA_DONE_INTERRUPT_ENABLE                           29
+#define     END_OF_CHAIN                                        28
+#define         DMA_BYTE_COUNT_MASK                                 ((1<<24)-1)
+#define     DMA_BYTE_COUNT                                      0
+	u32		dmaaddr;
+	u32		dmadesc;
+	u32		_unused1;
+} __attribute__ ((packed));
+
+/* dedicated endpoint registers, BAR0 + 0x0200 */
+
+struct net2280_dep_regs {	/* [11.8] */
+	// offset 0x0200, 0x0210, 0x220, 0x230, 0x240
+	u32		dep_cfg;
+	// offset 0x0204, 0x0214, 0x224, 0x234, 0x244
+	u32		dep_rsp;
+	u32		_unused [2];
+} __attribute__ ((packed));
+
+/* configurable endpoint registers, BAR0 + 0x0300 ... array of seven structs
+ * like this, for ep0 then the configurable endpoints A..F
+ * ep0 reserved for control; E and F have only 64 bytes of fifo
+ */
+struct net2280_ep_regs {	/* [11.9] */
+	// offset 0x0300, 0x0320, 0x0340, 0x0360, 0x0380, 0x03a0, 0x03c0
+	u32		ep_cfg;
+#define     ENDPOINT_BYTE_COUNT                                 16
+#define     ENDPOINT_ENABLE                                     10
+#define     ENDPOINT_TYPE                                       8
+#define     ENDPOINT_DIRECTION                                  7
+#define     ENDPOINT_NUMBER                                     0
+	u32		ep_rsp;
+#define     SET_NAK_OUT_PACKETS                                 15
+#define     SET_EP_HIDE_STATUS_PHASE                            14
+#define     SET_EP_FORCE_CRC_ERROR                              13
+#define     SET_INTERRUPT_MODE                                  12
+#define     SET_CONTROL_STATUS_PHASE_HANDSHAKE                  11
+#define     SET_NAK_OUT_PACKETS_MODE                            10
+#define     SET_ENDPOINT_TOGGLE                                 9
+#define     SET_ENDPOINT_HALT                                   8
+#define     CLEAR_NAK_OUT_PACKETS                               7
+#define     CLEAR_EP_HIDE_STATUS_PHASE                          6
+#define     CLEAR_EP_FORCE_CRC_ERROR                            5
+#define     CLEAR_INTERRUPT_MODE                                4
+#define     CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE                3
+#define     CLEAR_NAK_OUT_PACKETS_MODE                          2
+#define     CLEAR_ENDPOINT_TOGGLE                               1
+#define     CLEAR_ENDPOINT_HALT                                 0
+	u32		ep_irqenb;
+#define     SHORT_PACKET_OUT_DONE_INTERRUPT_ENABLE              6
+#define     SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE           5
+#define     DATA_PACKET_RECEIVED_INTERRUPT_ENABLE               3
+#define     DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE            2
+#define     DATA_OUT_PING_TOKEN_INTERRUPT_ENABLE                1
+#define     DATA_IN_TOKEN_INTERRUPT_ENABLE                      0
+	u32		ep_stat;
+#define     FIFO_VALID_COUNT                                    24
+#define     HIGH_BANDWIDTH_OUT_TRANSACTION_PID                  22
+#define     TIMEOUT                                             21
+#define     USB_STALL_SENT                                      20
+#define     USB_IN_NAK_SENT                                     19
+#define     USB_IN_ACK_RCVD                                     18
+#define     USB_OUT_PING_NAK_SENT                               17
+#define     USB_OUT_ACK_SENT                                    16
+#define     FIFO_OVERFLOW                                       13
+#define     FIFO_UNDERFLOW                                      12
+#define     FIFO_FULL                                           11
+#define     FIFO_EMPTY                                          10
+#define     FIFO_FLUSH                                          9
+#define     SHORT_PACKET_OUT_DONE_INTERRUPT                     6
+#define     SHORT_PACKET_TRANSFERRED_INTERRUPT                  5
+#define     NAK_OUT_PACKETS                                     4
+#define     DATA_PACKET_RECEIVED_INTERRUPT                      3
+#define     DATA_PACKET_TRANSMITTED_INTERRUPT                   2
+#define     DATA_OUT_PING_TOKEN_INTERRUPT                       1
+#define     DATA_IN_TOKEN_INTERRUPT                             0
+	// offset 0x0310, 0x0330, 0x0350, 0x0370, 0x0390, 0x03b0, 0x03d0
+	u32		ep_avail;
+	u32		ep_data;
+	u32		_unused0 [2];
+} __attribute__ ((packed));
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef	__KERNEL__
+
+/* indexed registers [11.10] are accessed indirectly
+ * caller must own the device lock.
+ */
+
+static inline u32
+get_idx_reg (struct net2280_regs *regs, u32 index)
+{
+	writel (index, &regs->idxaddr);
+	/* NOTE:  synchs device/cpu memory views */
+	return readl (&regs->idxdata);
+}
+
+static inline void
+set_idx_reg (struct net2280_regs *regs, u32 index, u32 value)
+{
+	writel (index, &regs->idxaddr);
+	writel (value, &regs->idxdata);
+	/* posted, may not be visible yet */
+}
+
+#endif	/* __KERNEL__ */
+
+
+#define REG_DIAG		0x0
+#define     RETRY_COUNTER                                       16
+#define     FORCE_PCI_SERR                                      11
+#define     FORCE_PCI_INTERRUPT                                 10
+#define     FORCE_USB_INTERRUPT                                 9
+#define     FORCE_CPU_INTERRUPT                                 8
+#define     ILLEGAL_BYTE_ENABLES                                5
+#define     FAST_TIMES                                          4
+#define     FORCE_RECEIVE_ERROR                                 2
+#define     FORCE_TRANSMIT_CRC_ERROR                            0
+#define REG_FRAME		0x02	/* from last sof */
+#define REG_CHIPREV		0x03	/* in bcd */
+#define	REG_HS_NAK_RATE		0x0a	/* NAK per N uframes */
+
+#define	CHIPREV_1	0x0100
+#define	CHIPREV_1A	0x0110
+
+#ifdef	__KERNEL__
+
+/* ep a-f highspeed and fullspeed maxpacket, addresses
+ * computed from ep->num
+ */
+#define REG_EP_MAXPKT(dev,num) (((num) + 1) * 0x10 + \
+		(((dev)->gadget.speed == USB_SPEED_HIGH) ? 0 : 1))
+
+/*-------------------------------------------------------------------------*/
+
+/* [8.3] for scatter/gather i/o
+ * use struct net2280_dma_regs bitfields
+ */
+struct net2280_dma {
+	u32		dmacount;
+	u32		dmaaddr;		/* the buffer */
+	u32		dmadesc;		/* next dma descriptor */
+	u32		_reserved;
+} __attribute__ ((aligned (16)));
+
+/*-------------------------------------------------------------------------*/
+
+/* DRIVER DATA STRUCTURES and UTILITIES */
+
+struct net2280_ep {
+	struct usb_ep				ep;
+	struct net2280_ep_regs			*regs;
+	struct net2280_dma_regs			*dma;
+	struct net2280_dma			*dummy;
+	dma_addr_t				td_dma;	/* of dummy */
+	struct net2280				*dev;
+	unsigned long				irqs;
+
+	/* analogous to a host-side qh */
+	struct list_head			queue;
+	const struct usb_endpoint_descriptor	*desc;
+	unsigned				num : 8,
+						fifo_size : 12,
+						in_fifo_validate : 1,
+						stopped : 1,
+						is_in : 1,
+						is_iso : 1;
+};
+
+static inline void allow_status (struct net2280_ep *ep)
+{
+	/* ep0 only */
+	writel (  (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)
+		| (1 << CLEAR_NAK_OUT_PACKETS_MODE)
+		, &ep->regs->ep_rsp);
+	ep->stopped = 1;
+}
+
+/* count (<= 4) bytes in the next fifo write will be valid */
+static inline void set_fifo_bytecount (struct net2280_ep *ep, unsigned count)
+{
+	writeb (count, 2 + (u8 *) &ep->regs->ep_cfg);
+}
+
+struct net2280_request {
+	struct usb_request		req;
+	struct net2280_dma		*td;
+	dma_addr_t			td_dma;
+	struct list_head		queue;
+	unsigned			mapped : 1,
+					dma_done : 1,
+					valid : 1;
+};
+
+struct net2280 {
+	/* each pci device provides one gadget, several endpoints */
+	struct usb_gadget		gadget;
+	spinlock_t			lock;
+	struct net2280_ep		ep [7];
+	struct usb_gadget_driver 	*driver;
+	unsigned			enabled : 1,
+					protocol_stall : 1,
+					got_irq : 1,
+					region : 1,
+					selfpowered : 1;
+	u16				chiprev;
+
+	/* pci state used to access those endpoints */
+	struct pci_dev			*pdev;
+	struct net2280_regs		*regs;
+	struct net2280_usb_regs		*usb;
+	struct net2280_pci_regs		*pci;
+	struct net2280_dma_regs		*dma;
+	struct net2280_dep_regs		*dep;
+	struct net2280_ep_regs		*epregs;
+
+	struct pci_pool			*requests;
+	// statistics...
+};
+
+static inline void set_halt (struct net2280_ep *ep)
+{
+	/* ep0 and bulk/intr endpoints */
+	writel (  (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)
+		    /* set NAK_OUT for erratum 0114 */
+		| ((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS)
+		| (1 << SET_ENDPOINT_HALT)
+		, &ep->regs->ep_rsp);
+}
+
+static inline void clear_halt (struct net2280_ep *ep)
+{
+	/* ep0 and bulk/intr endpoints */
+	writel (  (1 << CLEAR_ENDPOINT_HALT)
+		| (1 << CLEAR_ENDPOINT_TOGGLE)
+		    /* unless the gadget driver left a short packet in the
+		     * fifo, this reverses the erratum 0114 workaround.
+		     */
+		| ((ep->dev->chiprev == CHIPREV_1) << CLEAR_NAK_OUT_PACKETS)
+		, &ep->regs->ep_rsp);
+}
+
+#ifdef USE_RDK_LEDS
+
+static inline void net2280_led_init (struct net2280 *dev)
+{
+	/* LED3 (green) is on during USB activity. note erratum 0113. */
+	writel ((1 << GPIO3_LED_SELECT)
+		| (1 << GPIO3_OUTPUT_ENABLE)
+		| (1 << GPIO2_OUTPUT_ENABLE)
+		| (1 << GPIO1_OUTPUT_ENABLE)
+		| (1 << GPIO0_OUTPUT_ENABLE)
+		, &dev->regs->gpioctl);
+}
+
+/* indicate speed with bi-color LED 0/1 */
+static inline
+void net2280_led_speed (struct net2280 *dev, enum usb_device_speed speed)
+{
+	u32	val = readl (&dev->regs->gpioctl);
+	switch (speed) {
+	case USB_SPEED_HIGH:		/* green */
+		val &= ~(1 << GPIO0_DATA);
+		val |= (1 << GPIO1_DATA);
+		break;
+	case USB_SPEED_FULL:		/* red */
+		val &= ~(1 << GPIO1_DATA);
+		val |= (1 << GPIO0_DATA);
+		break;
+	default:			/* (off/black) */
+		val &= ~((1 << GPIO1_DATA) | (1 << GPIO0_DATA));
+		break;
+	}
+	writel (val, &dev->regs->gpioctl);
+}
+
+/* indicate power with LED 2 */
+static inline void net2280_led_active (struct net2280 *dev, int is_active)
+{
+	u32	val = readl (&dev->regs->gpioctl);
+
+	// FIXME this LED never seems to turn on.
+	if (is_active)
+		val |= GPIO2_DATA;
+	else
+		val &= ~GPIO2_DATA;
+	writel (val, &dev->regs->gpioctl);
+}
+static inline void net2280_led_shutdown (struct net2280 *dev)
+{
+	/* turn off all four GPIO*_DATA bits */
+	writel (readl (&dev->regs->gpioctl) & ~0x0f,
+			&dev->regs->gpioctl);
+}
+
+#else
+
+#define net2280_led_init(dev)		do { } while (0)
+#define net2280_led_speed(dev, speed)	do { } while (0)
+#define net2280_led_shutdown(dev)	do { } while (0)
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#define xprintk(dev,level,fmt,args...) \
+	printk(level "%s %s: " fmt , driver_name , \
+			dev->pdev->slot_name , ## args)
+
+#ifdef DEBUG
+#undef DEBUG
+#define DEBUG(dev,fmt,args...) \
+	xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DEBUG(dev,fmt,args...) \
+	do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE
+#define VDEBUG DEBUG
+#else
+#define VDEBUG(dev,fmt,args...) \
+	do { } while (0)
+#endif	/* VERBOSE */
+
+#define ERROR(dev,fmt,args...) \
+	xprintk(dev , KERN_ERR , fmt , ## args)
+#define WARN(dev,fmt,args...) \
+	xprintk(dev , KERN_WARNING , fmt , ## args)
+#define INFO(dev,fmt,args...) \
+	xprintk(dev , KERN_INFO , fmt , ## args)
+
+/*-------------------------------------------------------------------------*/
+
+static inline void start_out_naking (struct net2280_ep *ep)
+{
+	/* NOTE:  hardware races lurk here, and PING protocol issues */
+	writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+	/* synch with device */
+	readl (&ep->regs->ep_rsp);
+}
+
+#ifdef DEBUG
+static inline void assert_out_naking (struct net2280_ep *ep, const char *where)
+{
+	u32	tmp = readl (&ep->regs->ep_stat);
+
+	if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) {
+		DEBUG (ep->dev, "%s %s %08x !NAK\n",
+				ep->ep.name, where, tmp);
+		writel ((1 << SET_NAK_OUT_PACKETS),
+			&ep->regs->ep_rsp);
+	}
+}
+#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep,__FUNCTION__)
+#else
+#define ASSERT_OUT_NAKING(ep) do {} while (0)
+#endif
+
+static inline void stop_out_naking (struct net2280_ep *ep)
+{
+	u32	tmp;
+
+	tmp = readl (&ep->regs->ep_stat);
+	if ((tmp & (1 << NAK_OUT_PACKETS)) != 0)
+		writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* 2.5 and 2.4.older portability changes ... */
+
+#ifndef container_of
+#define	container_of	list_entry
+#endif
+
+#ifndef likely
+#define likely(x) (x)
+#define unlikely(x) (x)
+#endif
+
+#ifndef BUG_ON
+#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0)
+#endif
+
+#ifndef WARN_ON
+#define	WARN_ON(x)	do { } while (0)
+#endif
+
+#ifndef	IRQ_NONE
+typedef void irqreturn_t;
+#define IRQ_NONE
+#define IRQ_HANDLED
+#define IRQ_RETVAL(x)
+#endif
+
+#endif	/* __KERNEL__ */
diff -urN linux-2.4.22-bk28/drivers/usb/gadget/usbstring.c linux-2.4.22-bk29/drivers/usb/gadget/usbstring.c
--- linux-2.4.22-bk28/drivers/usb/gadget/usbstring.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-bk29/drivers/usb/gadget/usbstring.c	2003-10-06 02:50:31.000000000 -0700
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2003 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/version.h>
+
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+
+
+/**
+ * usb_gadget_get_string - fill out a string descriptor 
+ * @table: of c strings using iso latin/1 characters
+ * @id: string id, from low byte of wValue in get string descriptor
+ * @buf: at least 256 bytes
+ *
+ * Finds the iso latin/1 string matching the ID, and converts it into a
+ * string descriptor in utf16-le.
+ * Returns length of descriptor (always even) or negative errno
+ *
+ * If your driver needs stings in multiple languages, you'll need to
+ * to use some alternate solution for languages where the ISO 8859/1
+ * (latin/1) character set can't be used.  For example, they can't be
+ * used with Chinese (Big5, GB2312, etc), Japanese, Korean, or many other
+ * languages.  You'd likely "switch (wIndex) { ... }"  in your ep0
+ * string descriptor logic, using this routine in cases where "western
+ * european" characters suffice for the strings being returned.
+ */
+int
+usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf)
+{
+	struct usb_string	*s;
+	int			len;
+
+	/* descriptor 0 has the language id */
+	if (id == 0) {
+		buf [0] = 4;
+		buf [1] = USB_DT_STRING;
+		buf [2] = (u8) table->language;
+		buf [3] = (u8) (table->language >> 8);
+		return 4;
+	}
+	for (s = table->strings; s && s->s; s++)
+		if (s->id == id)
+			break;
+
+	/* unrecognized: stall. */
+	if (!s || !s->s)
+		return -EINVAL;
+
+	/* string descriptors have length, tag, then UTF16-LE text */
+	len = min ((size_t) 126, strlen (s->s));
+	buf [0] = (len + 1) * 2;
+	buf [1] = USB_DT_STRING;
+	memset (buf + 2, 0, 2 * len);	/* zero all the high bytes */
+	while (len) {
+		buf [2 * len] = s->s [len - 1];
+		len--;
+	}
+	return buf [0];
+}
+
diff -urN linux-2.4.22-bk28/drivers/usb/gadget/zero.c linux-2.4.22-bk29/drivers/usb/gadget/zero.c
--- linux-2.4.22-bk28/drivers/usb/gadget/zero.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-bk29/drivers/usb/gadget/zero.c	2003-10-06 02:50:31.000000000 -0700
@@ -0,0 +1,1261 @@
+/*
+ * zero.c -- Gadget Zero, for USB development
+ *
+ * Copyright (C) 2003 David Brownell
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Gadget Zero only needs two bulk endpoints, and is an example of how you
+ * can write a hardware-agnostic gadget driver running inside a USB device.
+ *
+ * Hardware details are visible (see CONFIG_USB_ZERO_* below) but don't
+ * affect most of the driver.
+ *
+ * Use it with the Linux host/master side "usbtest" driver to get a basic
+ * functional test of your device-side usb stack, or with "usb-skeleton".
+ *
+ * It supports two similar configurations.  One sinks whatever the usb host
+ * writes, and in return sources zeroes.  The other loops whatever the host
+ * writes back, so the host can read it.  Module options include:
+ *
+ *   buflen=N		default N=4096, buffer size used
+ *   qlen=N		default N=32, how many buffers in the loopback queue
+ *   loopdefault	default false, list loopback config first
+ *
+ * Many drivers will only have one configuration, letting them be much
+ * simpler if they also don't support high speed operation (like this
+ * driver does).
+ */
+
+#define DEBUG 1
+// #define VERBOSE
+
+#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/uts.h>
+#include <linux/version.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+
+
+/*-------------------------------------------------------------------------*/
+
+#define DRIVER_VERSION		"Bastille Day 2003"
+
+static const char shortname [] = "zero";
+static const char longname [] = "Gadget Zero";
+
+static const char source_sink [] = "source and sink data";
+static const char loopback [] = "loop input to output";
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * hardware-specific configuration, controlled by which device
+ * controller driver was configured.
+ *
+ * CHIP ... hardware identifier
+ * DRIVER_VERSION_NUM ... alerts the host side driver to differences
+ * EP0_MAXPACKET ... controls packetization of control requests
+ * EP_*_NAME ... which endpoints do we use for which purpose?
+ * EP_*_NUM ... numbers for them (often limited by hardware)
+ * HIGHSPEED ... define if ep0 and descriptors need high speed support
+ * MAX_USB_POWER ... define if we use other than 100 mA bus current
+ * SELFPOWER ... unless we can run on bus power, USB_CONFIG_ATT_SELFPOWER
+ * WAKEUP ... if hardware supports remote wakeup AND we will issue the
+ * 	usb_gadget_wakeup() call to initiate it, USB_CONFIG_ATT_WAKEUP
+ *
+ * hw_optimize(gadget) ... for any hardware tweaks we want to kick in
+ * 	before we enable our endpoints
+ *
+ * add other defines for other portability issues, like hardware that
+ * for some reason doesn't handle full speed bulk maxpacket of 64.
+ */
+
+/*
+ * DRIVER_VERSION_NUM 0x0000 (?):  Martin Diehl's ezusb an21/fx code
+ */
+
+/*
+ * NetChip 2280, PCI based.
+ *
+ * This has half a dozen configurable endpoints, four with dedicated
+ * DMA channels to manage their FIFOs.  It supports high speed.
+ * Those endpoints can be arranged in any desired configuration.
+ */
+#ifdef	CONFIG_USB_ZERO_NET2280
+#define CHIP			"net2280"
+#define DRIVER_VERSION_NUM	0x0111
+#define EP0_MAXPACKET		64
+static const char EP_OUT_NAME [] = "ep-a";
+#define EP_OUT_NUM	2
+static const char EP_IN_NAME [] = "ep-b";
+#define EP_IN_NUM	2
+#define HIGHSPEED
+/* specific hardware configs could be bus-powered */
+#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
+/* supports remote wakeup, but this driver doesn't */
+
+extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode);
+
+static inline void hw_optimize (struct usb_gadget *gadget)
+{
+	/* we can have bigger ep-a/ep-b fifos (2KB each, 4 packets
+	 * for highspeed bulk) because we're not using ep-c/ep-d.
+	 */
+	net2280_set_fifo_mode (gadget, 1);
+}
+#endif
+
+/*
+ * PXA-2xx UDC:  widely used in second gen Linux-capable PDAs.
+ *
+ * This has fifteen fixed-function full speed endpoints, and it
+ * can support all USB transfer types.
+ *
+ * These supports three or four configurations, with fixed numbers.
+ * The hardware interprets SET_INTERFACE, net effect is that you
+ * can't use altsettings or reset the interfaces independently.
+ * So stick to a single interface.
+ */
+#ifdef	CONFIG_USB_ZERO_PXA2XX
+#define CHIP			"pxa2xx"
+#define DRIVER_VERSION_NUM	0x0113
+#define EP0_MAXPACKET		16
+static const char EP_OUT_NAME [] = "ep12out-bulk";
+#define EP_OUT_NUM	12
+static const char EP_IN_NAME [] = "ep11in-bulk";
+#define EP_IN_NUM	11
+/* doesn't support bus-powered operation */
+#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
+/* supports remote wakeup, but this driver doesn't */
+
+/* no hw optimizations to apply */
+#define hw_optimize(g) do {} while (0);
+#endif
+
+/*
+ * SA-1100 UDC:  widely used in first gen Linux-capable PDAs.
+ *
+ * This has only two fixed function endpoints, which can only
+ * be used for bulk (or interrupt) transfers.  (Plus control.)
+ *
+ * Since it can't flush its TX fifos without disabling the UDC,
+ * the current configuration or altsettings can't change except
+ * in special situations.  So this is a case of "choose it right
+ * during enumeration" ...
+ */
+#ifdef	CONFIG_USB_ZERO_SA1100
+#define CHIP			"sa1100"
+#define DRIVER_VERSION_NUM	0x0115
+#define EP0_MAXPACKET		8
+static const char EP_OUT_NAME [] = "ep1out-bulk";
+#define EP_OUT_NUM	1
+static const char EP_IN_NAME [] = "ep2in-bulk";
+#define EP_IN_NUM	2
+/* doesn't support bus-powered operation */
+#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
+/* doesn't support remote wakeup? */
+
+/* no hw optimizations to apply */
+#define hw_optimize(g) do {} while (0);
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#ifndef EP0_MAXPACKET
+#	error Configure some USB peripheral controller driver!
+#endif
+
+/* power usage is config specific.
+ * hardware that supports remote wakeup defaults to disabling it.
+ */
+
+#ifndef	SELFPOWER
+/* default: say we rely on bus power */
+#define SELFPOWER	0
+/* else:
+ * - SELFPOWER value must be USB_CONFIG_ATT_SELFPOWER
+ * - MAX_USB_POWER may be nonzero.
+ */
+#endif
+
+#ifndef	MAX_USB_POWER
+/* any hub supports this steady state bus power consumption */
+#define MAX_USB_POWER	100	/* mA */
+#endif
+
+#ifndef	WAKEUP
+/* default: this driver won't do remote wakeup */
+#define WAKEUP		0
+/* else value must be USB_CONFIG_ATT_WAKEUP */
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* big enough to hold our biggest descriptor */
+#define USB_BUFSIZ	256
+
+struct zero_dev {
+	spinlock_t		lock;
+	struct usb_gadget	*gadget;
+	struct usb_request	*req;		/* for control responses */
+
+	/* when configured, we have one of two configs:
+	 * - source data (in to host) and sink it (out from host)
+	 * - or loop it back (out from host back in to host)
+	 */
+	u8			config;
+	struct usb_ep		*in_ep, *out_ep;
+};
+
+#define xprintk(d,level,fmt,args...) \
+	printk(level "%s %s: " fmt , shortname , (d)->gadget->dev.bus_id , \
+		## args)
+
+#ifdef DEBUG
+#undef DEBUG
+#define DEBUG(dev,fmt,args...) \
+	xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DEBUG(dev,fmt,args...) \
+	do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE
+#define VDEBUG	DEBUG
+#else
+#define VDEBUG(dev,fmt,args...) \
+	do { } while (0)
+#endif /* DEBUG */
+
+#define ERROR(dev,fmt,args...) \
+	xprintk(dev , KERN_ERR , fmt , ## args)
+#define WARN(dev,fmt,args...) \
+	xprintk(dev , KERN_WARNING , fmt , ## args)
+#define INFO(dev,fmt,args...) \
+	xprintk(dev , KERN_INFO , fmt , ## args)
+
+/*-------------------------------------------------------------------------*/
+
+static unsigned buflen = 4096;
+static unsigned qlen = 32;
+static unsigned pattern = 0;
+
+/*
+ * Normally the "loopback" configuration is second (index 1) so
+ * it's not the default.  Here's where to change that order, to
+ * work better with hosts (like Linux ... for now!) where config
+ * changes are problematic.
+ */
+static int loopdefault = 0;
+
+
+MODULE_PARM (buflen, "i");
+MODULE_PARM_DESC (buflen, "size of i/o buffers");
+
+MODULE_PARM (qlen, "i");
+MODULE_PARM_DESC (qlen, "depth of loopback buffering");
+
+MODULE_PARM (pattern, "i");
+MODULE_PARM_DESC (pattern, "0 for default all-zeroes, 1 for mod63");
+
+MODULE_PARM (loopdefault, "b");
+MODULE_PARM_DESC (loopdefault, "true to have default config be loopback");
+
+/*-------------------------------------------------------------------------*/
+
+/* Thanks to NetChip Technologies for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with any other driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
+ */
+#define DRIVER_VENDOR_NUM	0x0525		/* NetChip */
+#define DRIVER_PRODUCT_NUM	0xa4a0		/* Linux-USB "Gadget Zero" */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * DESCRIPTORS ... most are static, but strings and (full)
+ * configuration descriptors are built on demand.
+ */
+
+#define STRING_MANUFACTURER		25
+#define STRING_PRODUCT			42
+#define STRING_SERIAL			101
+#define STRING_SOURCE_SINK		250
+#define STRING_LOOPBACK			251
+
+/*
+ * This device advertises two configurations; these numbers work
+ * on a pxa250 as well as more flexible hardware.
+ */
+#define	CONFIG_SOURCE_SINK	3
+#define	CONFIG_LOOPBACK		2
+
+static const struct usb_device_descriptor
+device_desc = {
+	.bLength =		sizeof device_desc,
+	.bDescriptorType =	USB_DT_DEVICE,
+
+	.bcdUSB =		__constant_cpu_to_le16 (0x0200),
+	.bDeviceClass =		USB_CLASS_VENDOR_SPEC,
+	.bMaxPacketSize0 =	EP0_MAXPACKET,
+
+	.idVendor =		__constant_cpu_to_le16 (DRIVER_VENDOR_NUM),
+	.idProduct =		__constant_cpu_to_le16 (DRIVER_PRODUCT_NUM),
+	.bcdDevice =		__constant_cpu_to_le16 (DRIVER_VERSION_NUM),
+	.iManufacturer =	STRING_MANUFACTURER,
+	.iProduct =		STRING_PRODUCT,
+	.iSerialNumber =	STRING_SERIAL,
+	.bNumConfigurations =	2,
+};
+
+static const struct usb_config_descriptor
+source_sink_config = {
+	.bLength =		sizeof source_sink_config,
+	.bDescriptorType =	USB_DT_CONFIG,
+
+	/* compute wTotalLength on the fly */
+	.bNumInterfaces =	1,
+	.bConfigurationValue =	CONFIG_SOURCE_SINK,
+	.iConfiguration =	STRING_SOURCE_SINK,
+	.bmAttributes =		USB_CONFIG_ATT_ONE | SELFPOWER | WAKEUP,
+	.bMaxPower =		(MAX_USB_POWER + 1) / 2,
+};
+
+static const struct usb_config_descriptor
+loopback_config = {
+	.bLength =		sizeof loopback_config,
+	.bDescriptorType =	USB_DT_CONFIG,
+
+	/* compute wTotalLength on the fly */
+	.bNumInterfaces =	1,
+	.bConfigurationValue =	CONFIG_LOOPBACK,
+	.iConfiguration =	STRING_LOOPBACK,
+	.bmAttributes =		USB_CONFIG_ATT_ONE | SELFPOWER | WAKEUP,
+	.bMaxPower =		(MAX_USB_POWER + 1) / 2,
+};
+
+/* one interface in each configuration */
+
+static const struct usb_interface_descriptor
+source_sink_intf = {
+	.bLength =		sizeof source_sink_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
+	.iInterface =		STRING_SOURCE_SINK,
+};
+
+static const struct usb_interface_descriptor
+loopback_intf = {
+	.bLength =		sizeof loopback_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
+	.iInterface =		STRING_LOOPBACK,
+};
+
+/* two full speed bulk endpoints; their use is config-dependent */
+
+static const struct usb_endpoint_descriptor
+fs_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_IN_NUM | USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16 (64),
+};
+
+static const struct usb_endpoint_descriptor
+fs_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_OUT_NUM,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16 (64),
+};
+
+#ifdef	HIGHSPEED
+
+/*
+ * usb 2.0 devices need to expose both high speed and full speed
+ * descriptors, unless they only run at full speed.
+ *
+ * that means alternate endpoint descriptors (bigger packets)
+ * and a "device qualifier" ... plus more construction options
+ * for the config descriptor.
+ */
+
+static const struct usb_endpoint_descriptor
+hs_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_IN_NUM | USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16 (512),
+};
+
+static const struct usb_endpoint_descriptor
+hs_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	EP_OUT_NUM,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16 (512),
+};
+
+static const struct usb_qualifier_descriptor
+dev_qualifier = {
+	.bLength =		sizeof dev_qualifier,
+	.bDescriptorType =	USB_DT_DEVICE_QUALIFIER,
+
+	.bcdUSB =		__constant_cpu_to_le16 (0x0200),
+	.bDeviceClass =		USB_CLASS_VENDOR_SPEC,
+
+	/* assumes ep0 uses the same value for both speeds ... */
+	.bMaxPacketSize0 =	EP0_MAXPACKET,
+
+	.bNumConfigurations =	2,
+};
+
+/* maxpacket and other transfer characteristics vary by speed. */
+#define ep_desc(g,hs,fs) (((g)->speed==USB_SPEED_HIGH)?(hs):(fs))
+
+#else
+
+/* if there's no high speed support, maxpacket doesn't change. */
+#define ep_desc(g,hs,fs) fs
+
+#endif	/* !HIGHSPEED */
+
+static char				serial [40];
+
+/* static strings, in iso 8859/1 */
+static struct usb_string		strings [] = {
+	{ STRING_MANUFACTURER, UTS_SYSNAME " " UTS_RELEASE " with " CHIP, },
+	{ STRING_PRODUCT, longname, },
+	{ STRING_SERIAL, serial, },
+	{ STRING_LOOPBACK, loopback, },
+	{ STRING_SOURCE_SINK, source_sink, },
+	{  }			/* end of list */
+};
+
+static struct usb_gadget_strings	stringtab = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings,
+};
+
+/*
+ * config descriptors are also handcrafted.  these must agree with code
+ * that sets configurations, and with code managing interfaces and their
+ * altsettings.  other complexity may come from:
+ *
+ *  - high speed support, including "other speed config" rules
+ *  - multiple configurations
+ *  - interfaces with alternate settings
+ *  - embedded class or vendor-specific descriptors
+ *
+ * this handles high speed, and has a second config that could as easily
+ * have been an alternate interface setting (on most hardware).
+ *
+ * NOTE:  to demonstrate (and test) more USB capabilities, this driver
+ * should include an altsetting to test interrupt transfers, including
+ * high bandwidth modes at high speed.  (Maybe work like Intel's test
+ * device?)
+ */
+static int
+config_buf (enum usb_device_speed speed,
+		u8 *buf, u8 type, unsigned index)
+{
+	int		is_source_sink;
+	const unsigned	config_len = USB_DT_CONFIG_SIZE
+				+ USB_DT_INTERFACE_SIZE
+				+ 2 * USB_DT_ENDPOINT_SIZE;
+#ifdef HIGHSPEED
+	int		hs;
+#endif
+	/* two configurations will always be index 0 and index 1 */
+	if (index > 1)
+		return -EINVAL;
+	if (config_len > USB_BUFSIZ)
+		return -EDOM;
+	is_source_sink = loopdefault ? (index == 1) : (index == 0);
+
+	/* config (or other speed config) */
+	if (is_source_sink)
+		memcpy (buf, &source_sink_config, USB_DT_CONFIG_SIZE);
+	else
+		memcpy (buf, &loopback_config, USB_DT_CONFIG_SIZE);
+	buf [1] = type;
+	((struct usb_config_descriptor *) buf)->wTotalLength
+		= __constant_cpu_to_le16 (config_len);
+	buf += USB_DT_CONFIG_SIZE;
+
+	/* one interface */
+	if (is_source_sink)
+		memcpy (buf, &source_sink_intf, USB_DT_INTERFACE_SIZE);
+	else
+		memcpy (buf, &loopback_intf, USB_DT_INTERFACE_SIZE);
+	buf += USB_DT_INTERFACE_SIZE;
+
+	/* the endpoints in that interface (at that speed) */
+#ifdef HIGHSPEED
+	hs = (speed == USB_SPEED_HIGH);
+	if (type == USB_DT_OTHER_SPEED_CONFIG)
+		hs = !hs;
+	if (hs) {
+		memcpy (buf, &hs_source_desc, USB_DT_ENDPOINT_SIZE);
+		buf += USB_DT_ENDPOINT_SIZE;
+		memcpy (buf, &hs_sink_desc, USB_DT_ENDPOINT_SIZE);
+		buf += USB_DT_ENDPOINT_SIZE;
+	} else
+#endif
+	{
+		memcpy (buf, &fs_source_desc, USB_DT_ENDPOINT_SIZE);
+		buf += USB_DT_ENDPOINT_SIZE;
+		memcpy (buf, &fs_sink_desc, USB_DT_ENDPOINT_SIZE);
+		buf += USB_DT_ENDPOINT_SIZE;
+	}
+
+	return config_len;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_request *
+alloc_ep_req (struct usb_ep *ep, unsigned length)
+{
+	struct usb_request	*req;
+
+	req = usb_ep_alloc_request (ep, GFP_ATOMIC);
+	if (req) {
+		req->length = length;
+		req->buf = usb_ep_alloc_buffer (ep, length,
+				&req->dma, GFP_ATOMIC);
+		if (!req->buf) {
+			usb_ep_free_request (ep, req);
+			req = 0;
+		}
+	}
+	return req;
+}
+
+static void free_ep_req (struct usb_ep *ep, struct usb_request *req)
+{
+	if (req->buf)
+		usb_ep_free_buffer (ep, req->buf, req->dma, req->length);
+	usb_ep_free_request (ep, req);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* optionally require specific source/sink data patterns  */
+
+static inline int
+check_read_data (
+	struct zero_dev		*dev,
+	struct usb_ep		*ep,
+	struct usb_request	*req
+)
+{
+	unsigned	i;
+	u8		*buf = req->buf;
+
+	for (i = 0; i < req->actual; i++, buf++) {
+		switch (pattern) {
+		/* all-zeroes has no synchronization issues */
+		case 0:
+			if (*buf == 0)
+				continue;
+			break;
+		/* mod63 stays in sync with short-terminated transfers,
+		 * or otherwise when host and gadget agree on how large
+		 * each usb transfer request should be.  resync is done
+		 * with set_interface or set_config.
+		 */
+		case 1:
+			if (*buf == (u8)(i % 63))
+				continue;
+			break;
+		}
+		ERROR (dev, "bad OUT byte, buf [%d] = %d\n", i, *buf);
+		usb_ep_set_halt (ep);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static inline void
+reinit_write_data (
+	struct zero_dev		*dev,
+	struct usb_ep		*ep,
+	struct usb_request	*req
+)
+{
+	unsigned	i;
+	u8		*buf = req->buf;
+
+	switch (pattern) {
+	case 0:
+		memset (req->buf, 0, req->length);
+		break;
+	case 1:
+		for  (i = 0; i < req->length; i++)
+			*buf++ = (u8) (i % 63);
+		break;
+	}
+}
+
+/* if there is only one request in the queue, there'll always be an
+ * irq delay between end of one request and start of the next.
+ * that prevents using hardware dma queues.
+ */
+static void source_sink_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	struct zero_dev	*dev = ep->driver_data;
+	int		status = req->status;
+
+	switch (status) {
+
+	case 0: 			/* normal completion? */
+		if (ep == dev->out_ep)
+			check_read_data (dev, ep, req);
+		else
+			reinit_write_data (dev, ep, req);
+		break;
+
+	/* this endpoint is normally active while we're configured */
+	case -ECONNABORTED: 		/* hardware forced ep reset */
+	case -ECONNRESET:		/* request dequeued */
+	case -ESHUTDOWN:		/* disconnect from host */
+		VDEBUG (dev, "%s gone (%d), %d/%d\n", ep->name, status,
+				req->actual, req->length);
+		if (ep == dev->out_ep)
+			check_read_data (dev, ep, req);
+		free_ep_req (ep, req);
+		return;
+
+	case -EOVERFLOW:		/* buffer overrun on read means that
+					 * we didn't provide a big enough
+					 * buffer.
+					 */
+	default:
+#if 1
+		DEBUG (dev, "%s complete --> %d, %d/%d\n", ep->name,
+				status, req->actual, req->length);
+#endif
+	case -EREMOTEIO:		/* short read */
+		break;
+	}
+
+	status = usb_ep_queue (ep, req, GFP_ATOMIC);
+	if (status) {
+		ERROR (dev, "kill %s:  resubmit %d bytes --> %d\n",
+				ep->name, req->length, status);
+		usb_ep_set_halt (ep);
+		/* FIXME recover later ... somehow */
+	}
+}
+
+static struct usb_request *
+source_sink_start_ep (struct usb_ep *ep, int gfp_flags)
+{
+	struct usb_request	*req;
+	int			status;
+
+	req = alloc_ep_req (ep, buflen);
+	if (!req)
+		return 0;
+
+	memset (req->buf, 0, req->length);
+	req->complete = source_sink_complete;
+
+	if (strcmp (ep->name, EP_IN_NAME) == 0)
+		reinit_write_data (ep->driver_data, ep, req);
+
+	status = usb_ep_queue (ep, req, gfp_flags);
+	if (status) {
+		struct zero_dev	*dev = ep->driver_data;
+
+		ERROR (dev, "start %s --> %d\n", ep->name, status);
+		free_ep_req (ep, req);
+		req = 0;
+	}
+
+	return req;
+}
+
+static int
+set_source_sink_config (struct zero_dev *dev, int gfp_flags)
+{
+	int			result = 0;
+	struct usb_ep		*ep;
+	struct usb_gadget	*gadget = dev->gadget;
+
+	gadget_for_each_ep (ep, gadget) {
+		const struct usb_endpoint_descriptor	*d;
+
+		/* one endpoint writes (sources) zeroes in (to the host) */
+		if (strcmp (ep->name, EP_IN_NAME) == 0) {
+			d = ep_desc (gadget, &hs_source_desc, &fs_source_desc);
+			result = usb_ep_enable (ep, d);
+			if (result == 0) {
+				ep->driver_data = dev;
+				if (source_sink_start_ep (ep, gfp_flags) != 0) {
+					dev->in_ep = ep;
+					continue;
+				}
+				usb_ep_disable (ep);
+				result = -EIO;
+			}
+
+		/* one endpoint reads (sinks) anything out (from the host) */
+		} else if (strcmp (ep->name, EP_OUT_NAME) == 0) {
+			d = ep_desc (gadget, &hs_sink_desc, &fs_sink_desc);
+			result = usb_ep_enable (ep, d);
+			if (result == 0) {
+				ep->driver_data = dev;
+				if (source_sink_start_ep (ep, gfp_flags) != 0) {
+					dev->out_ep = ep;
+					continue;
+				}
+				usb_ep_disable (ep);
+				result = -EIO;
+			}
+
+		/* ignore any other endpoints */
+		} else
+			continue;
+
+		/* stop on error */
+		ERROR (dev, "can't start %s, result %d\n", ep->name, result);
+		break;
+	}
+	if (result == 0)
+		DEBUG (dev, "buflen %d\n", buflen);
+
+	/* caller is responsible for cleanup on error */
+	return result;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void loopback_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	struct zero_dev	*dev = ep->driver_data;
+	int		status = req->status;
+
+	switch (status) {
+
+	case 0: 			/* normal completion? */
+		if (ep == dev->out_ep) {
+			/* loop this OUT packet back IN to the host */
+			req->zero = (req->actual < req->length);
+			req->length = req->actual;
+			status = usb_ep_queue (dev->in_ep, req, GFP_ATOMIC);
+			if (status == 0)
+				return;
+
+			/* "should never get here" */
+			ERROR (dev, "can't loop %s to %s: %d\n",
+				ep->name, dev->in_ep->name,
+				status);
+		}
+
+		/* queue the buffer for some later OUT packet */
+		req->length = buflen;
+		status = usb_ep_queue (dev->out_ep, req, GFP_ATOMIC);
+		if (status == 0)
+			return;
+
+		/* "should never get here" */
+		/* FALLTHROUGH */
+
+	default:
+		ERROR (dev, "%s loop complete --> %d, %d/%d\n", ep->name,
+				status, req->actual, req->length);
+		/* FALLTHROUGH */
+
+	/* NOTE:  since this driver doesn't maintain an explicit record
+	 * of requests it submitted (just maintains qlen count), we
+	 * rely on the hardware driver to clean up on disconnect or
+	 * endpoint disable.
+	 */
+	case -ECONNABORTED: 		/* hardware forced ep reset */
+	case -ECONNRESET:		/* request dequeued */
+	case -ESHUTDOWN:		/* disconnect from host */
+		free_ep_req (ep, req);
+		return;
+	}
+}
+
+static int
+set_loopback_config (struct zero_dev *dev, int gfp_flags)
+{
+	int			result = 0;
+	struct usb_ep		*ep;
+	struct usb_gadget	*gadget = dev->gadget;
+
+	gadget_for_each_ep (ep, gadget) {
+		const struct usb_endpoint_descriptor	*d;
+
+		/* one endpoint writes data back IN to the host */
+		if (strcmp (ep->name, EP_IN_NAME) == 0) {
+			d = ep_desc (gadget, &hs_source_desc, &fs_source_desc);
+			result = usb_ep_enable (ep, d);
+			if (result == 0) {
+				ep->driver_data = dev;
+				dev->in_ep = ep;
+				continue;
+			}
+
+		/* one endpoint just reads OUT packets */
+		} else if (strcmp (ep->name, EP_OUT_NAME) == 0) {
+			d = ep_desc (gadget, &hs_sink_desc, &fs_sink_desc);
+			result = usb_ep_enable (ep, d);
+			if (result == 0) {
+				ep->driver_data = dev;
+				dev->out_ep = ep;
+				continue;
+			}
+
+		/* ignore any other endpoints */
+		} else
+			continue;
+
+		/* stop on error */
+		ERROR (dev, "can't enable %s, result %d\n", ep->name, result);
+		break;
+	}
+
+	/* allocate a bunch of read buffers and queue them all at once.
+	 * we buffer at most 'qlen' transfers; fewer if any need more
+	 * than 'buflen' bytes each.
+	 */
+	if (result == 0) {
+		struct usb_request	*req;
+		unsigned		i;
+
+		ep = dev->out_ep;
+		for (i = 0; i < qlen && result == 0; i++) {
+			req = alloc_ep_req (ep, buflen);
+			if (req) {
+				req->complete = loopback_complete;
+				result = usb_ep_queue (ep, req, GFP_ATOMIC);
+				if (result)
+					DEBUG (dev, "%s queue req --> %d\n",
+							ep->name, result);
+			} else
+				result = -ENOMEM;
+		}
+	}
+	if (result == 0)
+		DEBUG (dev, "qlen %d, buflen %d\n", qlen, buflen);
+
+	/* caller is responsible for cleanup on error */
+	return result;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void zero_reset_config (struct zero_dev *dev)
+{
+	if (dev->config == 0)
+		return;
+
+	DEBUG (dev, "reset config\n");
+
+	/* just disable endpoints, forcing completion of pending i/o.
+	 * all our completion handlers free their requests in this case.
+	 */
+	if (dev->in_ep) {
+		usb_ep_disable (dev->in_ep);
+		dev->in_ep = 0;
+	}
+	if (dev->out_ep) {
+		usb_ep_disable (dev->out_ep);
+		dev->out_ep = 0;
+	}
+	dev->config = 0;
+}
+
+/* change our operational config.  this code must agree with the code
+ * that returns config descriptors, and altsetting code.
+ *
+ * it's also responsible for power management interactions. some
+ * configurations might not work with our current power sources.
+ *
+ * note that some device controller hardware will constrain what this
+ * code can do, perhaps by disallowing more than one configuration or
+ * by limiting configuration choices (like the pxa2xx).
+ */
+static int
+zero_set_config (struct zero_dev *dev, unsigned number, int gfp_flags)
+{
+	int			result = 0;
+	struct usb_gadget	*gadget = dev->gadget;
+
+	if (number == dev->config)
+		return 0;
+
+#ifdef CONFIG_USB_ZERO_SA1100
+	if (dev->config) {
+		/* tx fifo is full, but we can't clear it...*/
+		INFO (dev, "can't change configurations\n");
+		return -ESPIPE;
+	}
+#endif
+	zero_reset_config (dev);
+	hw_optimize (gadget);
+
+	switch (number) {
+	case CONFIG_SOURCE_SINK:
+		result = set_source_sink_config (dev, gfp_flags);
+		break;
+	case CONFIG_LOOPBACK:
+		result = set_loopback_config (dev, gfp_flags);
+		break;
+	default:
+		result = -EINVAL;
+		/* FALL THROUGH */
+	case 0:
+		return result;
+	}
+
+	if (!result && (!dev->in_ep || !dev->out_ep))
+		result = -ENODEV;
+	if (result)
+		zero_reset_config (dev);
+	else {
+		char *speed;
+
+		switch (gadget->speed) {
+		case USB_SPEED_LOW:	speed = "low"; break;
+		case USB_SPEED_FULL:	speed = "full"; break;
+		case USB_SPEED_HIGH:	speed = "high"; break;
+		default: 		speed = "?"; break;
+		}
+
+		dev->config = number;
+		INFO (dev, "%s speed config #%d: %s\n", speed, number,
+				(number == CONFIG_SOURCE_SINK)
+					? source_sink : loopback);
+	}
+	return result;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void zero_setup_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	if (req->status || req->actual != req->length)
+		DEBUG ((struct zero_dev *) ep->driver_data,
+				"setup complete --> %d, %d/%d\n",
+				req->status, req->actual, req->length);
+}
+
+/*
+ * The setup() callback implements all the ep0 functionality that's
+ * not handled lower down, in hardware or the hardware driver (like
+ * device and endpoint feature flags, and their status).  It's all
+ * housekeeping for the gadget function we're implementing.  Most of
+ * the work is in config-specific setup.
+ */
+static int
+zero_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+	struct zero_dev		*dev = get_gadget_data (gadget);
+	struct usb_request	*req = dev->req;
+	int			value = -EOPNOTSUPP;
+
+	/* usually this stores reply data in the pre-allocated ep0 buffer,
+	 * but config change events will reconfigure hardware.
+	 */
+	switch (ctrl->bRequest) {
+
+	case USB_REQ_GET_DESCRIPTOR:
+		if (ctrl->bRequestType != USB_DIR_IN)
+			break;
+		switch (ctrl->wValue >> 8) {
+
+		case USB_DT_DEVICE:
+			value = min (ctrl->wLength, (u16) sizeof device_desc);
+			memcpy (req->buf, &device_desc, value);
+			break;
+#ifdef HIGHSPEED
+		case USB_DT_DEVICE_QUALIFIER:
+			value = min (ctrl->wLength, (u16) sizeof dev_qualifier);
+			memcpy (req->buf, &dev_qualifier, value);
+			break;
+
+		case USB_DT_OTHER_SPEED_CONFIG:
+			// FALLTHROUGH
+#endif /* HIGHSPEED */
+		case USB_DT_CONFIG:
+			value = config_buf (gadget->speed, req->buf,
+					ctrl->wValue >> 8,
+					ctrl->wValue & 0xff);
+			if (value >= 0)
+				value = min (ctrl->wLength, (u16) value);
+			break;
+
+		case USB_DT_STRING:
+			/* wIndex == language code.
+			 * this driver only handles one language, you can
+			 * add others even if they don't use iso8859/1
+			 */
+			value = usb_gadget_get_string (&stringtab,
+					ctrl->wValue & 0xff, req->buf);
+			if (value >= 0)
+				value = min (ctrl->wLength, (u16) value);
+			break;
+		}
+		break;
+
+	/* currently two configs, two speeds */
+	case USB_REQ_SET_CONFIGURATION:
+		if (ctrl->bRequestType != 0)
+			break;
+		spin_lock (&dev->lock);
+		value = zero_set_config (dev, ctrl->wValue, GFP_ATOMIC);
+		spin_unlock (&dev->lock);
+		break;
+	case USB_REQ_GET_CONFIGURATION:
+		if (ctrl->bRequestType != USB_DIR_IN)
+			break;
+		*(u8 *)req->buf = dev->config;
+		value = min (ctrl->wLength, (u16) 1);
+		break;
+
+	/* until we add altsetting support, or other interfaces,
+	 * only 0/0 are possible.  pxa2xx only supports 0/0 (poorly)
+	 * and already killed pending endpoint I/O.
+	 */
+	case USB_REQ_SET_INTERFACE:
+		if (ctrl->bRequestType != USB_RECIP_INTERFACE)
+			break;
+		spin_lock (&dev->lock);
+		if (dev->config && ctrl->wIndex == 0 && ctrl->wValue == 0) {
+			u8		config = dev->config;
+
+			/* resets interface configuration, forgets about
+			 * previous transaction state (queued bufs, etc)
+			 * and re-inits endpoint state (toggle etc)
+			 * no response queued, just zero status == success.
+			 * if we had more than one interface we couldn't
+			 * use this "reset the config" shortcut.
+			 */
+			zero_reset_config (dev);
+			zero_set_config (dev, config, GFP_ATOMIC);
+			value = 0;
+		}
+		spin_unlock (&dev->lock);
+		break;
+	case USB_REQ_GET_INTERFACE:
+		if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
+			break;
+		if (!dev->config)
+			break;
+		if (ctrl->wIndex != 0) {
+			value = -EDOM;
+			break;
+		}
+		*(u8 *)req->buf = 0;
+		value = min (ctrl->wLength, (u16) 1);
+		break;
+
+	default:
+		VDEBUG (dev,
+			"unknown control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			ctrl->wValue, ctrl->wIndex, ctrl->wLength);
+	}
+
+	/* respond with data transfer before status phase? */
+	if (value >= 0) {
+		req->length = value;
+		value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0) {
+			DEBUG (dev, "ep_queue --> %d\n", value);
+			req->status = 0;
+			zero_setup_complete (gadget->ep0, req);
+		}
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+static void
+zero_disconnect (struct usb_gadget *gadget)
+{
+	struct zero_dev		*dev = get_gadget_data (gadget);
+	unsigned long		flags;
+
+	spin_lock_irqsave (&dev->lock, flags);
+	zero_reset_config (dev);
+
+	/* a more significant application might have some non-usb
+	 * activities to quiesce here, saving resources like power
+	 * or pushing the notification up a network stack.
+	 */
+	spin_unlock_irqrestore (&dev->lock, flags);
+
+	/* next we may get setup() calls to enumerate new connections;
+	 * or an unbind() during shutdown (including removing module).
+	 */
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+zero_unbind (struct usb_gadget *gadget)
+{
+	struct zero_dev		*dev = get_gadget_data (gadget);
+
+	DEBUG (dev, "unbind\n");
+
+	/* we've already been disconnected ... no i/o is active */
+	if (dev->req)
+		free_ep_req (gadget->ep0, dev->req);
+	kfree (dev);
+	set_gadget_data (gadget, 0);
+}
+
+static int
+zero_bind (struct usb_gadget *gadget)
+{
+	struct zero_dev		*dev;
+
+	dev = kmalloc (sizeof *dev, SLAB_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+	memset (dev, 0, sizeof *dev);
+	spin_lock_init (&dev->lock);
+	dev->gadget = gadget;
+	set_gadget_data (gadget, dev);
+
+	/* preallocate control response and buffer */
+	dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
+	if (!dev->req)
+		goto enomem;
+	dev->req->buf = usb_ep_alloc_buffer (gadget->ep0, USB_BUFSIZ,
+				&dev->req->dma, GFP_KERNEL);
+	if (!dev->req->buf)
+		goto enomem;
+
+	dev->req->complete = zero_setup_complete;
+
+	gadget->ep0->driver_data = dev;
+
+	INFO (dev, "%s, version: " DRIVER_VERSION "\n", longname);
+	return 0;
+
+enomem:
+	zero_unbind (gadget);
+	return -ENOMEM;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_gadget_driver zero_driver = {
+#ifdef HIGHSPEED
+	.speed		= USB_SPEED_HIGH,
+#else
+	.speed		= USB_SPEED_FULL,
+#endif
+	.function	= (char *) longname,
+	.bind		= zero_bind,
+	.unbind		= zero_unbind,
+
+	.setup		= zero_setup,
+	.disconnect	= zero_disconnect,
+
+	.driver 	= {
+		.name		= (char *) shortname,
+		// .shutdown = ...
+		// .suspend = ...
+		// .resume = ...
+	},
+};
+
+MODULE_AUTHOR ("David Brownell");
+MODULE_LICENSE ("Dual BSD/GPL");
+
+
+static int __init init (void)
+{
+	/* a real value would likely come through some id prom
+	 * or module option.  this one takes at least two packets.
+	 */
+	strncpy (serial, "0123456789.0123456789.0123456789", sizeof serial);
+	serial [sizeof serial - 1] = 0;
+
+	return usb_gadget_register_driver (&zero_driver);
+}
+module_init (init);
+
+static void __exit cleanup (void)
+{
+	usb_gadget_unregister_driver (&zero_driver);
+}
+module_exit (cleanup);
+
diff -urN linux-2.4.22-bk28/drivers/usb/mdc800.c linux-2.4.22-bk29/drivers/usb/mdc800.c
--- linux-2.4.22-bk28/drivers/usb/mdc800.c	2003-10-06 02:50:26.000000000 -0700
+++ linux-2.4.22-bk29/drivers/usb/mdc800.c	2003-10-06 02:50:31.000000000 -0700
@@ -726,8 +726,10 @@
 		else
 		{
 			/* memcpy Bytes */
-			if (copy_to_user(ptr, &mdc800->out [mdc800->out_ptr], sts))
+			if (copy_to_user(ptr, &mdc800->out [mdc800->out_ptr], sts)) {
+				up (&mdc800->io_lock);
 				return -EFAULT;
+			}
 			ptr+=sts;
 			left-=sts;
 			mdc800->out_ptr+=sts;
diff -urN linux-2.4.22-bk28/drivers/usb/serial/ftdi_sio.c linux-2.4.22-bk29/drivers/usb/serial/ftdi_sio.c
--- linux-2.4.22-bk28/drivers/usb/serial/ftdi_sio.c	2003-10-06 02:50:26.000000000 -0700
+++ linux-2.4.22-bk29/drivers/usb/serial/ftdi_sio.c	2003-10-06 02:50:31.000000000 -0700
@@ -17,6 +17,11 @@
  * See http://ftdi-usb-sio.sourceforge.net for upto date testing info
  *	and extra documentation
  *
+ * (21/Sep/2003) Ian Abbott
+ *      Added VID/PID for Omnidirectional Control Technology US101 USB to
+ *      RS-232 adapter (also rebadged as Dick Smith Electronics XH6381).
+ *      VID/PID supplied by Donald Gordon.
+ *
  * (19/Aug/2003) Ian Abbott
  *      Omitted some paranoid checks in write bulk callback that don't matter.
  *
@@ -325,6 +330,7 @@
 	{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_7_PID, 0, 0x3ff) },
 	{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_8_PID, 0, 0x3ff) },
 	{ USB_DEVICE_VER(IDTECH_VID, IDTECH_IDT1221U_PID, 0, 0x3ff) },
+	{ USB_DEVICE_VER(OCT_VID, OCT_US101_PID, 0, 0x3ff) },
 	{ }						/* Terminating entry */
 };
 
@@ -397,6 +403,7 @@
 	{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_7_PID, 0x400, 0xffff) },
 	{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_8_PID, 0x400, 0xffff) },
 	{ USB_DEVICE_VER(IDTECH_VID, IDTECH_IDT1221U_PID, 0x400, 0xffff) },
+	{ USB_DEVICE_VER(OCT_VID, OCT_US101_PID, 0x400, 0xffff) },
 	{ }						/* Terminating entry */
 };
 
@@ -482,6 +489,7 @@
 	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_7_PID) },
 	{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_8_PID) },
 	{ USB_DEVICE(IDTECH_VID, IDTECH_IDT1221U_PID) },
+	{ USB_DEVICE(OCT_VID, OCT_US101_PID) },
 	{ USB_DEVICE_VER(FTDI_VID, FTDI_HE_TIRA1_PID, 0x400, 0xffff) },
 	{ USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID) },
 	{ }						/* Terminating entry */
diff -urN linux-2.4.22-bk28/drivers/usb/serial/ftdi_sio.h linux-2.4.22-bk29/drivers/usb/serial/ftdi_sio.h
--- linux-2.4.22-bk28/drivers/usb/serial/ftdi_sio.h	2003-10-06 02:50:26.000000000 -0700
+++ linux-2.4.22-bk29/drivers/usb/serial/ftdi_sio.h	2003-10-06 02:50:31.000000000 -0700
@@ -133,6 +133,13 @@
 #define IDTECH_VID		0x0ACD	/* ID TECH Vendor ID */
 #define IDTECH_IDT1221U_PID	0x0300	/* IDT1221U USB to RS-232 adapter */
 
+/*
+ * Definitions for Omnidirectional Control Technology, Inc. devices
+ */
+#define OCT_VID			0x0B39	/* OCT vendor ID */
+/* Note: OCT US101 is also rebadged as Dick Smith Electronics (NZ) XH6381 */
+#define OCT_US101_PID		0x0421	/* OCT US101 USB to RS-232 */
+
 /* Commands */
 #define FTDI_SIO_RESET 		0 /* Reset the port */
 #define FTDI_SIO_MODEM_CTRL 	1 /* Set the modem control register */
diff -urN linux-2.4.22-bk28/drivers/usb/serial/ipaq.c linux-2.4.22-bk29/drivers/usb/serial/ipaq.c
--- linux-2.4.22-bk28/drivers/usb/serial/ipaq.c	2003-08-25 04:44:42.000000000 -0700
+++ linux-2.4.22-bk29/drivers/usb/serial/ipaq.c	2003-10-06 02:50:31.000000000 -0700
@@ -332,7 +332,7 @@
 	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
 
 	tty = port->tty;
-	if (urb->actual_length) {
+	if (tty && urb->actual_length) {
 		for (i = 0; i < urb->actual_length ; ++i) {
 			/* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */
 			if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
diff -urN linux-2.4.22-bk28/drivers/usb/serial/keyspan.c linux-2.4.22-bk29/drivers/usb/serial/keyspan.c
--- linux-2.4.22-bk28/drivers/usb/serial/keyspan.c	2003-06-13 07:51:37.000000000 -0700
+++ linux-2.4.22-bk29/drivers/usb/serial/keyspan.c	2003-10-06 02:50:31.000000000 -0700
@@ -28,6 +28,9 @@
 
   Change History
 
+  	2003sep04	LPM (Keyspan) add support for new single port product USA19HS.
+				Improve setup message handling for all devices.
+
 	2003Apr16	LPM (Keyspan) fix delayed control message resend for multi-port
 				'Open' case. Fix 'mpr' entries in keyspan.h (previously broken)
 
@@ -174,6 +177,7 @@
 	int		baud;
 	int		old_baud;
 	unsigned int	cflag;
+	unsigned int	old_cflag;
 	enum		{flow_none, flow_cts, flow_xon} flow_control;
 	int		rts_state;	/* Handshaking pins (outputs) */
 	int		dtr_state;
@@ -189,11 +193,12 @@
 
 	
 /* Include Keyspan message headers.  All current Keyspan Adapters
-   make use of one of three message formats which are referred
-   to as USA-26, USA-28 and USA-49 by Keyspan and within this driver. */
+   make use of one of four message formats which are referred
+   to as USA-26, USA-28 and USA-49, USA-90 by Keyspan and within this driver. */
 #include "keyspan_usa26msg.h"
 #include "keyspan_usa28msg.h"
 #include "keyspan_usa49msg.h"
+#include "keyspan_usa90msg.h"
 	
 
 /* Functions used by new usb-serial code. */
@@ -327,8 +332,8 @@
 	return -ENOIOCTLCMD;
 }
 
-	/* Write function is generic for the three protocols used
-	   with only a minor change for usa49 required */
+	/* Write function is similar for the four protocols used
+	   with only a minor change for usa90 (usa19hs) required */
 static int keyspan_write(struct usb_serial_port *port, int from_user, 
 			 const unsigned char *buf, int count)
 {
@@ -337,18 +342,27 @@
 	int				flip;
 	int 				left, todo;
 	struct urb			*this_urb;
-	int 				err;
+	int 				err, maxDataLen, dataOffset;
 
 	p_priv = (struct keyspan_port_private *)(port->private);
 	d_details = p_priv->device_details;
 
+	if (d_details->msg_format == msg_usa90) {
+   		maxDataLen = 64;
+		dataOffset = 0;
+	}
+	else {
+			maxDataLen = 63;
+			dataOffset = 1;
+	}
+	
 	dbg("%s - for port %d (%d chars [%x]), flip=%d",
 	    __FUNCTION__, port->number, count, buf[0], p_priv->out_flip);
 
 	for (left = count; left > 0; left -= todo) {
 		todo = left;
-		if (todo > 63)
-			todo = 63;
+		if (todo > maxDataLen)
+			todo = maxDataLen;
 
 		flip = p_priv->out_flip;
 	
@@ -371,20 +385,20 @@
 			break;
 		}
 
-		/* First byte in buffer is "last flag" - unused so
+		/* First byte in buffer is "last flag" (except for usa19hx) - unused so
 		   for now so set to zero */
 		((char *)this_urb->transfer_buffer)[0] = 0;
 
 		if (from_user) {
-			if (copy_from_user(this_urb->transfer_buffer + 1, buf, todo))
+			if (copy_from_user(this_urb->transfer_buffer + dataOffset, buf, todo))
 				return -EFAULT;
 		} else {
-			memcpy (this_urb->transfer_buffer + 1, buf, todo);
+			memcpy (this_urb->transfer_buffer + dataOffset, buf, todo);
 		}
 		buf += todo;
 
 		/* send the data out the bulk port */
-		this_urb->transfer_buffer_length = todo + 1;
+		this_urb->transfer_buffer_length = todo + dataOffset;
 
 		this_urb->transfer_flags &= ~USB_ASYNC_UNLINK;
 		this_urb->dev = port->serial->dev;
@@ -424,9 +438,12 @@
 	if (urb->actual_length) {
 		/* 0x80 bit is error flag */
 		if ((data[0] & 0x80) == 0) {
-			/* no error on any byte */
+			/* no errors on individual bytes, only possible overrun err*/
+			if (data[0] & RXERROR_OVERRUN)
+					err = TTY_OVERRUN;
+			else err = 0;
 			for (i = 1; i < urb->actual_length ; ++i) {
-				tty_insert_flip_char(tty, data[i], 0);
+				tty_insert_flip_char(tty, data[i], err);
 			}
 		} else {
 			/* some bytes had errors, every byte has status */
@@ -455,7 +472,7 @@
 	return;
 }
 
-	/* Outdat handling is common for usa26, usa28 and usa49 messages */
+	/* Outdat handling is common for all devices */
 static void	usa2x_outdat_callback(struct urb *urb)
 {
 	struct usb_serial_port *port;
@@ -561,7 +578,7 @@
 }
 
 
-static void     usa28_indat_callback(struct urb *urb)
+static void usa28_indat_callback(struct urb *urb)
 {
 	int                     i, err;
 	struct usb_serial_port  *port;
@@ -848,28 +865,171 @@
 }
 
 
+static void	usa90_indat_callback(struct urb *urb)
+{
+	int			i, err;
+	int			endpoint;
+	struct usb_serial_port	*port;
+	struct keyspan_port_private	 	*p_priv;
+	struct tty_struct	*tty;
+	unsigned char 		*data = urb->transfer_buffer;
+
+	dbg ("%s", __FUNCTION__); 
+
+	endpoint = usb_pipeendpoint(urb->pipe);
+
+
+	if (urb->status) {
+		dbg("%s - nonzero status: %x on endpoint %d.",
+		    __FUNCTION__, urb->status, endpoint);
+		return;
+	}
+
+	port = (struct usb_serial_port *) urb->context;
+	p_priv = (struct keyspan_port_private *)(port->private);
+
+	tty = port->tty;
+	if (urb->actual_length) {
+	
+		/* if current mode is DMA, looks like usa28 format
+	   		otherwise looks like usa26 data format */
+
+		if (p_priv->baud > 57600) {
+			for (i = 0; i < urb->actual_length ; ++i) 
+				tty_insert_flip_char(tty, data[i], 0);
+		}
+		else {
+			
+			/* 0x80 bit is error flag */
+			if ((data[0] & 0x80) == 0) {
+				/* no errors on individual bytes, only possible overrun err*/
+				if (data[0] & RXERROR_OVERRUN)
+						err = TTY_OVERRUN;
+				else err = 0;
+				for (i = 1; i < urb->actual_length ; ++i) 
+					tty_insert_flip_char(tty, data[i], err);
+			
+			} 
+			else {
+			/* some bytes had errors, every byte has status */
+				dbg("%s - RX error!!!!", __FUNCTION__);
+				for (i = 0; i + 1 < urb->actual_length; i += 2) {
+					int stat = data[i], flag = 0;
+					if (stat & RXERROR_OVERRUN)
+						flag |= TTY_OVERRUN;
+					if (stat & RXERROR_FRAMING)
+						flag |= TTY_FRAME;
+					if (stat & RXERROR_PARITY)
+						flag |= TTY_PARITY;
+					/* XXX should handle break (0x10) */
+					tty_insert_flip_char(tty, data[i+1], flag);
+				}
+			}
+		}
+		tty_flip_buffer_push(tty);
+	}
+				
+	/* Resubmit urb so we continue receiving */
+	urb->dev = port->serial->dev;
+	if (port->open_count)
+		if ((err = usb_submit_urb(urb)) != 0) {
+			dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
+		}
+	return;
+}
+
+
+static void	usa90_instat_callback(struct urb *urb)
+{
+	unsigned char 				*data = urb->transfer_buffer;
+	struct keyspan_usa90_portStatusMessage	*msg;
+	struct usb_serial			*serial;
+	struct usb_serial_port			*port;
+	struct keyspan_port_private	 	*p_priv;
+	int old_dcd_state, err;
+
+	serial = (struct usb_serial *) urb->context;
+
+	if (urb->status) {
+		dbg("%s - nonzero status: %x", __FUNCTION__, urb->status);
+		return;
+	}
+	if (urb->actual_length < 14) {
+		dbg("%s - %d byte report??", __FUNCTION__, urb->actual_length);
+		goto exit;
+	}
+
+	msg = (struct keyspan_usa90_portStatusMessage *)data;
+
+	/* Now do something useful with the data */
+
+	port = &serial->port[0];
+	p_priv = (struct keyspan_port_private *)(port->private);
+	
+	/* Update handshaking pin state information */
+	old_dcd_state = p_priv->dcd_state;
+	p_priv->cts_state = ((msg->cts) ? 1 : 0);
+	p_priv->dsr_state = ((msg->dsr) ? 1 : 0);
+	p_priv->dcd_state = ((msg->dcd) ? 1 : 0);
+	p_priv->ri_state = ((msg->ri) ? 1 : 0);
+
+	if (port->tty && !C_CLOCAL(port->tty)
+	    && old_dcd_state != p_priv->dcd_state) {
+		if (old_dcd_state)
+			tty_hangup(port->tty);
+		/*  else */
+		/*	wake_up_interruptible(&p_priv->open_wait); */
+	}
+	
+	/* Resubmit urb so we continue receiving */
+	urb->dev = serial->dev;
+	if ((err = usb_submit_urb(urb)) != 0) {
+		dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
+	}
+exit:
+	;
+}
+
+static void	usa90_outcont_callback(struct urb *urb)
+{
+	struct usb_serial_port *port;
+	struct keyspan_port_private *p_priv;
+
+	port = (struct usb_serial_port *) urb->context;
+	p_priv = (struct keyspan_port_private *)(port->private);
+
+	if (p_priv->resend_cont) {
+		dbg ("%s - sending setup", __FUNCTION__); 
+		keyspan_usa90_send_setup(port->serial, port, p_priv->resend_cont - 1);
+	}
+}
+
 
 static int keyspan_write_room (struct usb_serial_port *port)
 {
 	struct keyspan_port_private	*p_priv;
 	const struct keyspan_device_details	*d_details;
-	int				flip;
+	int				flip,dataLen;
 	struct urb			*this_urb;
 
 	dbg("%s", __FUNCTION__);
 	p_priv = (struct keyspan_port_private *)(port->private);
 	d_details = p_priv->device_details;
 
+	if (d_details->msg_format == msg_usa90)
+   		dataLen = 64;
+	else dataLen = 63;
+
 	flip = p_priv->out_flip;
 
 	/* Check both endpoints to see if any are available. */
 	if ((this_urb = p_priv->out_urbs[flip]) != 0) {
 		if (this_urb->status != -EINPROGRESS)
-			return (63);
+			return (dataLen);
 		flip = (flip + 1) & d_details->outdat_endp_flip;        
 		if ((this_urb = p_priv->out_urbs[flip]) != 0) 
 			if (this_urb->status != -EINPROGRESS)
-				return (63);
+				return (dataLen);
 	}
 	return (0);
 }
@@ -888,20 +1048,25 @@
 	struct usb_serial 		*serial = port->serial;
 	const struct keyspan_device_details	*d_details;
 	int				i, err;
+	int				baud_rate, device_port;
 	struct urb			*urb;
+	unsigned int	cflag;
 
 	s_priv = (struct keyspan_serial_private *)(serial->private);
 	p_priv = (struct keyspan_port_private *)(port->private);
-	d_details = s_priv->device_details;
+	d_details = p_priv->device_details;
 	
 	dbg("%s - port%d.", __FUNCTION__, port->number); 
 
-	p_priv = (struct keyspan_port_private *)(port->private);
-	
 	/* Set some sane defaults */
 	p_priv->rts_state = 1;
 	p_priv->dtr_state = 1;
+	p_priv->baud = 9600;
 
+	/* force baud and lcr to be set on open */
+	p_priv->old_baud = 0;
+	p_priv->old_cflag = 0;
+	
 	p_priv->out_flip = 0;
 	p_priv->in_flip = 0;
 
@@ -910,7 +1075,10 @@
 		if ((urb = p_priv->in_urbs[i]) == NULL)
 			continue;
 		urb->dev = serial->dev;
-		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 0);
+
+		/* make sure endpoint data toggle is synchronized with the device */
+		
+		usb_clear_halt(urb->dev, urb->pipe);
 
 		if ((err = usb_submit_urb(urb)) != 0) {
 			dbg("%s - submit urb %d failed (%d)", __FUNCTION__, i, err);
@@ -925,12 +1093,29 @@
 		/* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 0); */
 	}
 
-	// if the device is a USA49x, determine whether it is an W or WLC model
-	// and set the baud clock accordingly
+	/* get the terminal config for the setup message now so we don't 
+	 * need to send 2 of them */
+
+	cflag = port->tty->termios->c_cflag;
+	device_port = port->number - port->serial->minor;
+
+	/* Baud rate calculation takes baud rate as an integer
+	   so other rates can be generated if desired. */
+	baud_rate = tty_get_baud_rate(port->tty);
+	/* If no match or invalid, leave as default */		
+	if (baud_rate >= 0
+	    && d_details->calculate_baud_rate(baud_rate, d_details->baudclk,
+				NULL, NULL, NULL, device_port) == KEYSPAN_BAUD_RATE_OK) {
+		p_priv->baud = baud_rate;
+	}
+
+	/* set CTS/RTS handshake etc. */
+	p_priv->cflag = cflag;
+	p_priv->flow_control = (cflag & CRTSCTS)? flow_cts: flow_none;
 
 	keyspan_send_setup(port, 1);
 	//mdelay(100);
-	keyspan_set_termios(port, NULL);
+	//keyspan_set_termios(port, NULL);
 
 	return (0);
 }
@@ -965,7 +1150,7 @@
 		keyspan_send_setup(port, 2);
 		/* pilot-xfer seems to work best with this delay */
 		mdelay(100);
-		keyspan_set_termios(port, NULL);
+		// keyspan_set_termios(port, NULL);
 	}
 
 	/*while (p_priv->outcont_urb->status == -EINPROGRESS) {
@@ -1160,6 +1345,14 @@
 		.outdat_callback =	usa2x_outdat_callback,
 		.inack_callback =	usa49_inack_callback,
 		.outcont_callback =	usa49_outcont_callback,
+	}, {
+		/* msg_usa90 callbacks */
+		.instat_callback =	usa90_instat_callback,
+		.glocont_callback =	usa28_glocont_callback,		
+		.indat_callback =	usa90_indat_callback,
+		.outdat_callback =	usa2x_outdat_callback,
+		.inack_callback =	usa28_inack_callback,
+		.outcont_callback =	usa90_outcont_callback,
 	}
 };
 
@@ -1283,6 +1476,41 @@
 	return (KEYSPAN_BAUD_RATE_OK);
 }
 
+/* usa19hs function doesn't require prescaler */
+static int keyspan_usa19hs_calc_baud(u32 baud_rate, u32 baudclk, u8 *rate_hi,
+				   u8 *rate_low, u8 *prescaler, int portnum)
+{
+	u32 	b16,	/* baud rate times 16 (actual rate used internally) */
+			div;	/* divisor */	
+		
+	dbg ("%s - %d.", __FUNCTION__, baud_rate);
+
+		/* prevent divide by zero...  */
+	if( (b16 = (baud_rate * 16L)) == 0) 
+		return (KEYSPAN_INVALID_BAUD_RATE);
+	
+
+
+		/* calculate the divisor */
+	if( (div = (baudclk / b16)) == 0) 
+		return (KEYSPAN_INVALID_BAUD_RATE);
+
+	if(div > 0xffff) 
+		return (KEYSPAN_INVALID_BAUD_RATE);
+
+		/* return the counter values if non-null */
+	if (rate_low) 
+		*rate_low = (u8) (div & 0xff);
+	
+	if (rate_hi) 
+		*rate_hi = (u8) ((div >> 8) & 0xff);
+	
+	if (rate_low && rate_hi) 
+		dbg ("%s - %d %02x %02x.", __FUNCTION__, baud_rate, *rate_hi, *rate_low);
+	
+	return (KEYSPAN_BAUD_RATE_OK);
+}
+
 static int keyspan_usa19w_calc_baud(u32 baud_rate, u32 baudclk, u8 *rate_hi,
 				    u8 *rate_low, u8 *prescaler, int portnum)
 {
@@ -1435,6 +1663,7 @@
 		p_priv->resend_cont = reset_port + 1;
 	if (this_urb->status == -EINPROGRESS) {
 		/*  dbg ("%s - already writing", __FUNCTION__); */
+		mdelay(5);
 		return(-1);
 	}
 
@@ -1585,6 +1814,7 @@
 		p_priv->resend_cont = reset_port + 1;
 	if (this_urb->status == -EINPROGRESS) {
 		dbg ("%s already writing", __FUNCTION__);
+		mdelay(5);
 		return(-1);
 	}
 
@@ -1717,6 +1947,7 @@
 		p_priv->resend_cont = reset_port + 1;
 	if (this_urb->status == -EINPROGRESS) {
 		/*  dbg ("%s - already writing", __FUNCTION__); */
+		mdelay(5);
 		return(-1);
 	}
 
@@ -1845,6 +2076,144 @@
 	return (0);
 }
 
+static int keyspan_usa90_send_setup(struct usb_serial *serial,
+				    struct usb_serial_port *port,
+				    int reset_port)
+{
+	struct keyspan_usa90_portControlMessage	msg;		
+	struct keyspan_serial_private 		*s_priv;
+	struct keyspan_port_private 		*p_priv;
+	const struct keyspan_device_details	*d_details;
+	struct urb				*this_urb;
+	int 					err;
+	u8						prescaler;
+
+	dbg ("%s", __FUNCTION__);
+
+	s_priv = (struct keyspan_serial_private *)(serial->private);
+	p_priv = (struct keyspan_port_private *)(port->private);
+	d_details = s_priv->device_details;
+
+	/* only do something if we have a bulk out endpoint */
+	if ((this_urb = p_priv->outcont_urb) == NULL) {
+		dbg("%s - oops no urb.", __FUNCTION__);
+		return -1;
+	}
+
+	/* Save reset port val for resend.
+	   Don't overwrite resend for open/close condition. */
+	if ((reset_port + 1) > p_priv->resend_cont)
+		p_priv->resend_cont = reset_port + 1;
+	if (this_urb->status == -EINPROGRESS) {
+		dbg ("%s already writing", __FUNCTION__);
+		mdelay(5);
+		return(-1);
+	}
+
+	memset(&msg, 0, sizeof (struct keyspan_usa90_portControlMessage));
+
+	/* Only set baud rate if it's changed */	
+	if (p_priv->old_baud != p_priv->baud) {
+		p_priv->old_baud = p_priv->baud;
+		msg.setClocking = 0x01;
+		if (d_details->calculate_baud_rate
+		    (p_priv->baud, d_details->baudclk, &msg.baudHi,
+		     &msg.baudLo, &prescaler, 0) == KEYSPAN_INVALID_BAUD_RATE ) {
+			dbg("%s - Invalid baud rate %d requested, using 9600.", __FUNCTION__,
+			    p_priv->baud);
+			p_priv->baud = 9600;
+			d_details->calculate_baud_rate (p_priv->baud, d_details->baudclk, 
+				&msg.baudHi, &msg.baudLo, &prescaler, 0);
+		}
+		msg.setRxMode = 1;
+		msg.setTxMode = 1;
+	}
+
+	/* modes must always be correctly specified */
+	if (p_priv->baud > 57600)
+	{
+		msg.rxMode = RXMODE_DMA;
+		msg.txMode = TXMODE_DMA;
+	}
+	else
+	{
+		msg.rxMode = RXMODE_BYHAND;
+		msg.txMode = TXMODE_BYHAND;
+	}
+
+	msg.lcr = (p_priv->cflag & CSTOPB)? STOPBITS_678_2: STOPBITS_5678_1;
+	switch (p_priv->cflag & CSIZE) {
+	case CS5:
+		msg.lcr |= USA_DATABITS_5;
+		break;
+	case CS6:
+		msg.lcr |= USA_DATABITS_6;
+		break;
+	case CS7:
+		msg.lcr |= USA_DATABITS_7;
+		break;
+	case CS8:
+		msg.lcr |= USA_DATABITS_8;
+		break;
+	}
+	if (p_priv->cflag & PARENB) {
+		/* note USA_PARITY_NONE == 0 */
+		msg.lcr |= (p_priv->cflag & PARODD)?
+			USA_PARITY_ODD: USA_PARITY_EVEN;
+	}
+	if (p_priv->old_cflag != p_priv->cflag) {
+		p_priv->old_cflag = p_priv->cflag;
+		msg.setLcr = 0x01;
+	}
+
+	if (p_priv->flow_control == flow_cts)
+		msg.txFlowControl = TXFLOW_CTS;
+	msg.setTxFlowControl = 0x01;
+	msg.setRxFlowControl = 0x01;
+	
+	msg.rxForwardingLength = 16;
+	msg.rxForwardingTimeout = 16;	
+	msg.txAckSetting = 0;
+	msg.xonChar = 17;
+	msg.xoffChar = 19;
+
+	/* Opening port */ 
+	if (reset_port == 1) {
+		msg.portEnabled = 1;
+		msg.rxFlush = 1;
+		msg.txBreak = (p_priv->break_on);
+	}
+	/* Closing port */
+	else if (reset_port == 2) {
+		msg.portEnabled = 0;
+	}
+	/* Sending intermediate configs */
+	else {
+		if (port->open_count)
+			msg.portEnabled = 1;
+		msg.txBreak = (p_priv->break_on);
+	}
+
+	/* Do handshaking outputs */	
+	msg.setRts = 0x01;
+	msg.rts = p_priv->rts_state;
+
+	msg.setDtr = 0x01;
+	msg.dtr = p_priv->dtr_state;
+		
+	p_priv->resend_cont = 0;
+	memcpy (this_urb->transfer_buffer, &msg, sizeof(msg));
+	
+	/* send the data out the device on control endpoint */
+	this_urb->transfer_buffer_length = sizeof(msg);
+
+	this_urb->dev = serial->dev;
+	if ((err = usb_submit_urb(this_urb)) != 0) {
+		dbg("%s - usb_submit_urb(setup) failed (%d)", __FUNCTION__, err);
+	}
+	return (0);
+}
+
 static void keyspan_send_setup(struct usb_serial_port *port, int reset_port)
 {
 	struct usb_serial *serial = port->serial;
@@ -1866,9 +2235,13 @@
 	case msg_usa49:
 		keyspan_usa49_send_setup(serial, port, reset_port);
 		break;
+	case msg_usa90:
+		keyspan_usa90_send_setup(serial, port, reset_port);
+		break;
 	}
 }
 
+
 /* Gets called by the "real" driver (ie once firmware is loaded
    and renumeration has taken place. */
 static int keyspan_startup (struct usb_serial *serial)
diff -urN linux-2.4.22-bk28/drivers/usb/serial/keyspan.h linux-2.4.22-bk29/drivers/usb/serial/keyspan.h
--- linux-2.4.22-bk28/drivers/usb/serial/keyspan.h	2003-06-13 07:51:37.000000000 -0700
+++ linux-2.4.22-bk29/drivers/usb/serial/keyspan.h	2003-10-06 02:50:31.000000000 -0700
@@ -77,6 +77,10 @@
 					 u8 *rate_hi, u8 *rate_low,
 					 u8 *prescaler, int portnum);
 
+static int  keyspan_usa19hs_calc_baud	(u32 baud_rate, u32 baudclk,
+					 u8 *rate_hi, u8 *rate_low,
+					 u8 *prescaler, int portnum);
+
 static int  keyspan_usa28_send_setup	(struct usb_serial *serial,
 					 struct usb_serial_port *port,
 					 int reset_port);
@@ -87,6 +91,9 @@
 					 struct usb_serial_port *port,
 					 int reset_port);
 
+static int  keyspan_usa90_send_setup	(struct usb_serial *serial,
+					 struct usb_serial_port *port,
+					 int reset_port);
 
 /* Struct used for firmware - increased size of data section
    to allow Keyspan's 'C' firmware struct to be used unmodified */
@@ -178,6 +185,7 @@
 #define	KEYSPAN_USA18X_BAUDCLK			(12000000L)	/* a guess */
 #define	KEYSPAN_USA19_BAUDCLK			(12000000L)
 #define	KEYSPAN_USA19W_BAUDCLK			(24000000L)
+#define	KEYSPAN_USA19HS_BAUDCLK			(14769231L)
 #define	KEYSPAN_USA28_BAUDCLK			(1843200L)
 #define	KEYSPAN_USA28X_BAUDCLK			(12000000L)
 #define	KEYSPAN_USA49W_BAUDCLK			(48000000L)
@@ -210,6 +218,7 @@
 #define	keyspan_usa18x_product_id		0x0112
 #define	keyspan_usa19_product_id		0x0107
 #define	keyspan_usa19qi_product_id		0x010c
+#define	keyspan_usa19hs_product_id		0x0121
 #define	keyspan_mpr_product_id			0x011c
 #define	keyspan_usa19qw_product_id		0x0119
 #define	keyspan_usa19w_product_id		0x0108
@@ -225,7 +234,7 @@
 	/* product ID value */
 	int	product_id;
 
-	enum	{msg_usa26, msg_usa28, msg_usa49} msg_format;
+	enum	{msg_usa26, msg_usa28, msg_usa49, msg_usa90} msg_format;
 
 		/* Number of physical ports */
 	int	num_ports;
@@ -361,6 +370,22 @@
 	baudclk:		KEYSPAN_USA19W_BAUDCLK,
 };
 
+static const struct keyspan_device_details usa19hs_device_details = {
+	product_id:		keyspan_usa19hs_product_id,
+	msg_format:		msg_usa90,
+	num_ports:		1,
+	indat_endp_flip:	0,
+	outdat_endp_flip:	0,
+	indat_endpoints:	{0x81},
+	outdat_endpoints:	{0x01},
+	inack_endpoints:	{-1},
+	outcont_endpoints:	{0x02},
+	instat_endpoint:	0x82,
+	glocont_endpoint:	-1,
+	calculate_baud_rate:	keyspan_usa19hs_calc_baud,
+	baudclk:		KEYSPAN_USA19HS_BAUDCLK,
+};
+
 static const struct keyspan_device_details usa28_device_details = {
 	product_id:		keyspan_usa28_product_id,
 	msg_format:		msg_usa28,
@@ -450,6 +475,7 @@
 	&mpr_device_details,
 	&usa19qw_device_details,
 	&usa19w_device_details,
+	&usa19hs_device_details,
 	&usa28_device_details,
 	&usa28x_device_details,
 	&usa28xa_device_details,
@@ -477,6 +503,7 @@
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19hs_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_product_id) },
@@ -512,6 +539,7 @@
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19hs_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_product_id) },
 	{ } /* Terminating entry */
 };
@@ -547,8 +575,8 @@
 	name:			"Keyspan 1 port adapter",
 	id_table:		keyspan_1port_ids,
 	num_interrupt_in:	NUM_DONT_CARE,
-	num_bulk_in:		3,
-	num_bulk_out:		4,
+	num_bulk_in:		NUM_DONT_CARE,
+	num_bulk_out:		NUM_DONT_CARE,
 	num_ports:		1,
 	open:			keyspan_open,
 	close:			keyspan_close,
diff -urN linux-2.4.22-bk28/drivers/usb/serial/keyspan_usa26msg.h linux-2.4.22-bk29/drivers/usb/serial/keyspan_usa26msg.h
--- linux-2.4.22-bk28/drivers/usb/serial/keyspan_usa26msg.h	2003-10-06 02:50:26.000000000 -0700
+++ linux-2.4.22-bk29/drivers/usb/serial/keyspan_usa26msg.h	2003-10-06 02:50:31.000000000 -0700
@@ -4,7 +4,7 @@
 	Copyright (C) 1998-2000 InnoSys Incorporated.  All Rights Reserved
 	This file is available under a BSD-style copyright
 
-	Keyspan USB Async Firmware to run on Anchor EZ-USB
+	Keyspan USB Async Message Formats for the USA28X
 
 	Redistribution and use in source and binary forms, with or without
 	modification, are permitted provided that the following conditions are
@@ -19,11 +19,7 @@
 
         	This file is available under a BSD-style copyright
 
-	2. Redistributions in binary form must reproduce the above copyright
-   	notice, this list of conditions and the following disclaimer in the
-   	documentation and/or other materials provided with the distribution.
-
-	3. The name of InnoSys Incorprated may not be used to endorse or promote
+	2. The name of InnoSys Incorporated may not be used to endorse or promote
    	products derived from this software without specific prior written
    	permission.
 
diff -urN linux-2.4.22-bk28/drivers/usb/serial/keyspan_usa28msg.h linux-2.4.22-bk29/drivers/usb/serial/keyspan_usa28msg.h
--- linux-2.4.22-bk28/drivers/usb/serial/keyspan_usa28msg.h	2003-10-06 02:50:26.000000000 -0700
+++ linux-2.4.22-bk29/drivers/usb/serial/keyspan_usa28msg.h	2003-10-06 02:50:31.000000000 -0700
@@ -4,7 +4,7 @@
 	Copyright (C) 1998-2000 InnoSys Incorporated.  All Rights Reserved
 	This file is available under a BSD-style copyright
 
-	Keyspan USB Async Firmware to run on Anchor EZ-USB
+	Keyspan USB Async Message Formats for the USA26X
 
 	Redistribution and use in source and binary forms, with or without
 	modification, are permitted provided that the following conditions are
@@ -19,11 +19,7 @@
 
         	This file is available under a BSD-style copyright
 
-	2. Redistributions in binary form must reproduce the above copyright
-   	notice, this list of conditions and the following disclaimer in the
-   	documentation and/or other materials provided with the distribution.
-
-	3. The name of InnoSys Incorprated may not be used to endorse or promote
+	2. The name of InnoSys Incorporated may not be used to endorse or promote
    	products derived from this software without specific prior written
    	permission.
 
diff -urN linux-2.4.22-bk28/drivers/usb/serial/keyspan_usa49msg.h linux-2.4.22-bk29/drivers/usb/serial/keyspan_usa49msg.h
--- linux-2.4.22-bk28/drivers/usb/serial/keyspan_usa49msg.h	2003-10-06 02:50:26.000000000 -0700
+++ linux-2.4.22-bk29/drivers/usb/serial/keyspan_usa49msg.h	2003-10-06 02:50:31.000000000 -0700
@@ -4,7 +4,7 @@
 	Copyright (C) 1998-2000 InnoSys Incorporated.  All Rights Reserved
 	This file is available under a BSD-style copyright
 
-	Keyspan USB Async Firmware to run on Anchor EZ-USB
+	Keyspan USB Async Message Formats for the USA49W
 
 	Redistribution and use in source and binary forms, with or without
 	modification, are permitted provided that the following conditions are
@@ -19,11 +19,7 @@
 
         	This file is available under a BSD-style copyright
 
-	2. Redistributions in binary form must reproduce the above copyright
-   	notice, this list of conditions and the following disclaimer in the
-   	documentation and/or other materials provided with the distribution.
-
-	3. The name of InnoSys Incorprated may not be used to endorse or promote
+	2. The name of InnoSys Incorporated may not be used to endorse or promote
    	products derived from this software without specific prior written
    	permission.
 
diff -urN linux-2.4.22-bk28/drivers/usb/serial/keyspan_usa90msg.h linux-2.4.22-bk29/drivers/usb/serial/keyspan_usa90msg.h
--- linux-2.4.22-bk28/drivers/usb/serial/keyspan_usa90msg.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-bk29/drivers/usb/serial/keyspan_usa90msg.h	2003-10-06 02:50:31.000000000 -0700
@@ -0,0 +1,198 @@
+/*
+	usa90msg.h
+
+	Copyright (c) 1998-2003 InnoSys Incorporated.  All Rights Reserved
+	This file is available under a BSD-style copyright
+
+	Keyspan USB Async Message Formats for the USA19HS
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions are
+	met:
+
+	1. Redistributions of source code must retain this licence text
+   	without modification, this list of conditions, and the following
+   	disclaimer.  The following copyright notice must appear immediately at
+   	the beginning of all source files:
+
+        	Copyright (c) 1998-2003 InnoSys Incorporated.  All Rights Reserved
+
+        	This file is available under a BSD-style copyright
+
+	2. The name of InnoSys Incorprated may not be used to endorse or promote
+   	products derived from this software without specific prior written
+   	permission.
+
+	THIS SOFTWARE IS PROVIDED BY INNOSYS CORP. ``AS IS'' AND ANY EXPRESS OR
+	IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+	OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+	NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+	INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+	SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+	CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+	LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+	OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+	SUCH DAMAGE.    
+
+	Revisions:
+
+	2003feb14		add setTxMode/txMode  and cancelRxXoff to portControl
+	2003mar21		change name of PARITY_0/1 to add MARK/SPACE
+*/
+
+#ifndef	__USA90MSG__
+#define	__USA90MSG__
+
+struct keyspan_usa90_portControlMessage
+{
+	/*
+		there are three types of "commands" sent in the control message:
+
+		1.	configuration changes which must be requested by setting
+			the corresponding "set" flag (and should only be requested
+			when necessary, to reduce overhead on the device):
+	*/
+
+	u8	setClocking,	// host requests baud rate be set
+		baudLo,			// host does baud divisor calculation
+		baudHi,			// host does baud divisor calculation 
+		
+		setLcr,			// host requests lcr be set
+		lcr,			// use PARITY, STOPBITS, DATABITS below
+		
+		setRxMode,		// set receive mode
+		rxMode,			// RXMODE_DMA or RXMODE_BYHAND
+
+		setTxMode,		// set transmit mode
+		txMode,			// TXMODE_DMA or TXMODE_BYHAND
+
+		setTxFlowControl,	// host requests tx flow control be set
+		txFlowControl	,	// use TX_FLOW... bits below
+		setRxFlowControl,	// host requests rx flow control be set
+		rxFlowControl,	// use RX_FLOW... bits below
+		sendXoff,		// host requests XOFF transmitted immediately
+		sendXon,		// host requests XON char transmitted
+		xonChar,		// specified in current character format
+		xoffChar,		// specified in current character format
+
+		sendChar,		// host requests char transmitted immediately
+		txChar,			// character to send
+
+		setRts,			// host requests RTS output be set
+		rts,			// 1=on, 0=off
+		setDtr, 		// host requests DTR output be set
+		dtr;			// 1=on, 0=off
+
+	
+	/*
+		2.	configuration data which is simply used as is 
+			and must be specified correctly in every host message.
+	*/
+
+	u8	rxForwardingLength,  // forward when this number of chars available
+		rxForwardingTimeout, // (1-31 in ms)
+		txAckSetting;	   // 0=don't ack, 1=normal, 2-255 TBD...
+	/*
+		3.	Firmware states which cause actions if they change					
+		and must be specified correctly in every host message.
+	*/
+
+	u8	portEnabled,	// 0=disabled, 1=enabled
+		txFlush,		// 0=normal, 1=toss outbound data
+		txBreak,		// 0=break off, 1=break on
+		loopbackMode;	// 0=no loopback, 1=loopback enabled
+
+	/*
+		4.	commands which are flags only; these are processed in order
+			(so that, e.g., if rxFlush and rxForward flags are set, the
+			port will have no data to forward); any non-zero value 
+			is respected
+	*/
+
+	u8	rxFlush,		// toss inbound data
+		rxForward,		// forward all inbound data, NOW (as if fwdLen==1)
+		cancelRxXoff,	// cancel any receive XOFF state (_txXoff)
+		returnStatus;	// return current status NOW
+};
+
+// defines for bits in lcr
+#define		USA_DATABITS_5		0x00
+#define		USA_DATABITS_6		0x01
+#define		USA_DATABITS_7		0x02
+#define		USA_DATABITS_8		0x03
+#define		STOPBITS_5678_1		0x00	// 1 stop bit for all byte sizes
+#define		STOPBITS_5_1p5		0x04	// 1.5 stop bits for 5-bit byte
+#define		STOPBITS_678_2		0x04	// 2 stop bits for 6-8 bit byte
+#define		USA_PARITY_NONE		0x00
+#define		USA_PARITY_ODD		0x08
+#define		USA_PARITY_EVEN		0x18
+#define		PARITY_MARK_1  		0x28   	// force parity MARK
+#define		PARITY_SPACE_0 		0x38	// force parity SPACE
+
+#define		TXFLOW_CTS			0x04	
+#define		TXFLOW_DSR			0x08
+#define		TXFLOW_XOFF			0x01	
+#define		TXFLOW_XOFF_ANY		0x02	
+#define		TXFLOW_XOFF_BITS	(TXFLOW_XOFF | TXFLOW_XOFF_ANY)
+
+#define		RXFLOW_XOFF			0x10	
+#define		RXFLOW_RTS			0x20	
+#define		RXFLOW_DTR			0x40
+#define		RXFLOW_DSR_SENSITIVITY	0x80
+
+#define		RXMODE_BYHAND		0x00	
+#define		RXMODE_DMA			0x02	
+
+#define		TXMODE_BYHAND		0x00	
+#define		TXMODE_DMA			0x02	
+
+
+// all things called "StatusMessage" are sent on the status endpoint
+
+struct keyspan_usa90_portStatusMessage	
+{
+	u8	msr,			// reports the actual MSR register
+		cts,			// reports CTS pin
+		dcd,			// reports DCD pin
+		dsr,			// reports DSR pin
+		ri,				// reports RI pin
+		_txXoff,		// port is in XOFF state (we received XOFF)
+		rxBreak,		// reports break state
+		rxOverrun,		// count of overrun errors (since last reported)
+		rxParity,		// count of parity errors (since last reported)
+		rxFrame,		// count of frame errors (since last reported)
+		portState,		// PORTSTATE_xxx bits (useful for debugging)
+		messageAck,		// message acknowledgement
+		charAck,		// character acknowledgement
+		controlResponse;	// (value = returnStatus) a control message has been processed 
+};
+
+// bits in RX data message when STAT byte is included
+
+#define	RXERROR_OVERRUN		0x02
+#define	RXERROR_PARITY		0x04
+#define	RXERROR_FRAMING		0x08
+#define	RXERROR_BREAK		0x10
+
+#define	PORTSTATE_ENABLED	0x80
+#define	PORTSTATE_TXFLUSH	0x01
+#define	PORTSTATE_TXBREAK	0x02
+#define	PORTSTATE_LOOPBACK 	0x04
+
+// MSR bits
+
+#define MSR_dCTS	  		0x01		// CTS has changed since last report	
+#define MSR_dDSR	  		0x02
+#define MSR_dRI				0x04
+#define MSR_dDCD	  		0x08
+
+#define MSR_CTS				0x10	  	// current state of CTS
+#define MSR_DSR				0x20
+#define MSR_RI				0x40
+#define MSR_DCD				0x80
+
+// ie: the maximum length of an endpoint buffer
+#define		MAX_DATA_LEN			64
+
+#endif
diff -urN linux-2.4.22-bk28/drivers/usb/serial/pl2303.c linux-2.4.22-bk29/drivers/usb/serial/pl2303.c
--- linux-2.4.22-bk28/drivers/usb/serial/pl2303.c	2003-10-06 02:50:26.000000000 -0700
+++ linux-2.4.22-bk29/drivers/usb/serial/pl2303.c	2003-10-06 02:50:31.000000000 -0700
@@ -76,6 +76,7 @@
 	{ USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) },
 	{ USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) },
 	{ USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) },
+	{ USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) },
 	{ }					/* Terminating entry */
 };
 
diff -urN linux-2.4.22-bk28/drivers/usb/serial/pl2303.h linux-2.4.22-bk29/drivers/usb/serial/pl2303.h
--- linux-2.4.22-bk28/drivers/usb/serial/pl2303.h	2003-06-13 07:51:37.000000000 -0700
+++ linux-2.4.22-bk29/drivers/usb/serial/pl2303.h	2003-10-06 02:50:31.000000000 -0700
@@ -37,3 +37,6 @@
 
 #define DCU10_VENDOR_ID		0x0731
 #define DCU10_PRODUCT_ID	0x0528
+
+#define SITECOM_VENDOR_ID	0x6189
+#define SITECOM_PRODUCT_ID	0x2068
diff -urN linux-2.4.22-bk28/drivers/usb/speedtch.c linux-2.4.22-bk29/drivers/usb/speedtch.c
--- linux-2.4.22-bk28/drivers/usb/speedtch.c	2003-08-25 04:44:42.000000000 -0700
+++ linux-2.4.22-bk29/drivers/usb/speedtch.c	2003-10-06 02:50:31.000000000 -0700
@@ -21,7 +21,9 @@
  ******************************************************************************/
 
 /*
- *  Written by Johan Verrept, maintained by Duncan Sands (duncan.sands@wanadoo.fr)
+ *  Written by Johan Verrept, maintained by Duncan Sands (duncan.sands@free.fr)
+ *
+ *  1.7+:	- See the check-in logs
  *
  *  1.6:	- No longer opens a connection if the firmware is not loaded
  *  		- Added support for the speedtouch 330
@@ -83,6 +85,11 @@
 
 #include <linux/usb.h>
 
+#ifdef DEBUG
+#define DEBUG_ON(x)	BUG_ON(x)
+#else
+#define DEBUG_ON(x)	do { if (x); } while (0)
+#endif
 
 #ifdef VERBOSE_DEBUG
 static int udsl_print_packet (const unsigned char *data, int len);
@@ -93,9 +100,9 @@
 #define vdbg(arg...)
 #endif
 
-#define DRIVER_AUTHOR	"Johan Verrept, Duncan Sands <duncan.sands@wanadoo.fr>"
+#define DRIVER_AUTHOR	"Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
 #define DRIVER_DESC	"Alcatel SpeedTouch USB driver"
-#define DRIVER_VERSION	"1.6"
+#define DRIVER_VERSION	"1.7"
 
 static const char udsl_driver_name [] = "speedtch";
 
@@ -181,8 +188,7 @@
 	struct atm_vcc *vcc;
 
 	/* raw cell reassembly */
-	struct sk_buff *skb;
-	unsigned int max_pdu;
+	struct sk_buff *sarb;
 };
 
 /* send */
@@ -306,12 +312,10 @@
 {
 	struct udsl_vcc_data *cached_vcc = NULL;
 	struct atm_vcc *vcc;
-	struct sk_buff *skb;
+	struct sk_buff *sarb;
 	struct udsl_vcc_data *vcc_data;
 	int cached_vci = 0;
 	unsigned int i;
-	unsigned int length;
-	unsigned int pdu_length;
 	int pti;
 	int vci;
 	short cached_vpi = 0;
@@ -336,74 +340,73 @@
 		}
 
 		vcc = vcc_data->vcc;
+		sarb = vcc_data->sarb;
 
-		if (!vcc_data->skb && !(vcc_data->skb = dev_alloc_skb (vcc_data->max_pdu))) {
-			dbg ("udsl_extract_cells: no memory for skb (vcc: 0x%p)!", vcc);
-			if (pti)
-				atomic_inc (&vcc->stats->rx_err);
-			continue;
-		}
-
-		skb = vcc_data->skb;
-
-		if (skb->len + ATM_CELL_PAYLOAD > vcc_data->max_pdu) {
-			dbg ("udsl_extract_cells: buffer overrun (max_pdu: %u, skb->len %u, vcc: 0x%p)", vcc_data->max_pdu, skb->len, vcc);
+		if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) {
+			dbg ("udsl_extract_cells: buffer overrun (sarb->len %u, vcc: 0x%p)!", sarb->len, vcc);
 			/* discard cells already received */
-			skb_trim (skb, 0);
-			BUG_ON (vcc_data->max_pdu < ATM_CELL_PAYLOAD);
+			skb_trim (sarb, 0);
 		}
 
-		memcpy (skb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
-		__skb_put (skb, ATM_CELL_PAYLOAD);
+		memcpy (sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
+		__skb_put (sarb, ATM_CELL_PAYLOAD);
 
 		if (pti) {
+			struct sk_buff *skb;
+			unsigned int length;
+			unsigned int pdu_length;
+
 			length = (source [ATM_CELL_SIZE - 6] << 8) + source [ATM_CELL_SIZE - 5];
 
 			/* guard against overflow */
 			if (length > ATM_MAX_AAL5_PDU) {
-				dbg ("udsl_extract_cells: bogus length %u (vcc: 0x%p)", length, vcc);
-				goto drop;
+				dbg ("udsl_extract_cells: bogus length %u (vcc: 0x%p)!", length, vcc);
+				atomic_inc (&vcc->stats->rx_err);
+				goto out;
 			}
 
 			pdu_length = UDSL_NUM_CELLS (length) * ATM_CELL_PAYLOAD;
 
-			if (skb->len < pdu_length) {
-				dbg ("udsl_extract_cells: bogus pdu_length %u (skb->len: %u, vcc: 0x%p)", pdu_length, skb->len, vcc);
-				goto drop;
+			if (sarb->len < pdu_length) {
+				dbg ("udsl_extract_cells: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!", pdu_length, sarb->len, vcc);
+				atomic_inc (&vcc->stats->rx_err);
+				goto out;
 			}
 
-			if (crc32_be (~0, skb->tail - pdu_length, pdu_length) != 0xc704dd7b) {
-				dbg ("udsl_extract_cells: packet failed crc check (vcc: 0x%p)", vcc);
-				goto drop;
+			if (crc32_be (~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) {
+				dbg ("udsl_extract_cells: packet failed crc check (vcc: 0x%p)!", vcc);
+				atomic_inc (&vcc->stats->rx_err);
+				goto out;
 			}
 
-			if (!atm_charge (vcc, skb->truesize)) {
-				dbg ("udsl_extract_cells: failed atm_charge (skb->truesize: %u)", skb->truesize);
-				goto drop_no_stats; /* atm_charge increments rx_drop */
-			}
+			vdbg ("udsl_extract_cells: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", length, pdu_length, vcc);
 
-			/* now that we are sure to send the skb, it is ok to change skb->data */
-			if (skb->len > pdu_length)
-				skb_pull (skb, skb->len - pdu_length); /* discard initial junk */
+			if (!(skb = dev_alloc_skb (length))) {
+				dbg ("udsl_extract_cells: no memory for skb (length: %u)!", length);
+				atomic_inc (&vcc->stats->rx_drop);
+				goto out;
+			}
 
-			skb_trim (skb, length); /* drop zero padding and trailer */
+			vdbg ("udsl_extract_cells: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", skb, skb->truesize);
 
-			atomic_inc (&vcc->stats->rx);
+			if (!atm_charge (vcc, skb->truesize)) {
+				dbg ("udsl_extract_cells: failed atm_charge (skb->truesize: %u)!", skb->truesize);
+				dev_kfree_skb (skb);
+				goto out; /* atm_charge increments rx_drop */
+			}
 
-			PACKETDEBUG (skb->data, skb->len);
+			memcpy (skb->data, sarb->tail - pdu_length, length);
+			__skb_put (skb, length);
 
 			vdbg ("udsl_extract_cells: sending skb 0x%p, skb->len %u, skb->truesize %u", skb, skb->len, skb->truesize);
 
-			vcc->push (vcc, skb);
-
-			vcc_data->skb = NULL;
+			PACKETDEBUG (skb->data, skb->len);
 
-			continue;
+			vcc->push (vcc, skb);
 
-drop:
-			atomic_inc (&vcc->stats->rx_err);
-drop_no_stats:
-			skb_trim (skb, 0);
+			atomic_inc (&vcc->stats->rx);
+out:
+			skb_trim (sarb, 0);
 		}
 	}
 }
@@ -497,7 +500,7 @@
 		memset (target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER);
 		target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER;
 
-		BUG_ON (--ctrl->num_cells);
+		DEBUG_ON (--ctrl->num_cells);
 	}
 
 	memcpy (target, ctrl->aal5_trailer, ATM_AAL5_TRAILER);
@@ -534,7 +537,7 @@
 
 	vdbg ("udsl_complete_receive: urb 0x%p, status %d, actual_length %d, filled_cells %u, rcv 0x%p, buf 0x%p", urb, urb->status, urb->actual_length, buf->filled_cells, rcv, buf);
 
-	BUG_ON (buf->filled_cells > rcv_buf_size);
+	DEBUG_ON (buf->filled_cells > rcv_buf_size);
 
 	/* may not be in_interrupt() */
 	spin_lock_irqsave (&instance->receive_lock, flags);
@@ -865,6 +868,7 @@
 {
 	struct udsl_instance_data *instance = vcc->dev->dev_data;
 	struct udsl_vcc_data *new;
+	unsigned int max_pdu;
 
 	dbg ("udsl_atm_open: vpi %hd, vci %d", vpi, vci);
 
@@ -877,8 +881,10 @@
 		return -EINVAL;
 
 	/* only support AAL5 */
-	if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0) || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU))
+	if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0) || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) {
+		dbg ("udsl_atm_open: unsupported ATM type %d!", vcc->qos.aal);
 		return -EINVAL;
+	}
 
 	if (!instance->firmware_loaded) {
 		dbg ("udsl_atm_open: firmware not loaded!");
@@ -888,11 +894,13 @@
 	down (&instance->serialize); /* vs self, udsl_atm_close */
 
 	if (udsl_find_vcc (instance, vpi, vci)) {
+		dbg ("udsl_atm_open: %hd/%d already in use!", vpi, vci);
 		up (&instance->serialize);
 		return -EADDRINUSE;
 	}
 
 	if (!(new = kmalloc (sizeof (struct udsl_vcc_data), GFP_KERNEL))) {
+		dbg ("udsl_atm_open: no memory for vcc_data!");
 		up (&instance->serialize);
 		return -ENOMEM;
 	}
@@ -901,7 +909,15 @@
 	new->vcc = vcc;
 	new->vpi = vpi;
 	new->vci = vci;
-	new->max_pdu = max (1, UDSL_NUM_CELLS (vcc->qos.rxtp.max_sdu)) * ATM_CELL_PAYLOAD;
+
+	/* udsl_extract_cells requires at least one cell */
+	max_pdu = max (1, UDSL_NUM_CELLS (vcc->qos.rxtp.max_sdu)) * ATM_CELL_PAYLOAD;
+	if (!(new->sarb = alloc_skb (max_pdu, GFP_KERNEL))) {
+		dbg ("udsl_atm_open: no memory for SAR buffer!");
+	        kfree (new);
+		up (&instance->serialize);
+		return -ENOMEM;
+	}
 
 	vcc->dev_data = new;
 	vcc->vpi = vpi;
@@ -919,7 +935,7 @@
 
 	tasklet_schedule (&instance->receive_tasklet);
 
-	dbg ("udsl_atm_open: allocated vcc data 0x%p (max_pdu: %u)", new, new->max_pdu);
+	dbg ("udsl_atm_open: allocated vcc data 0x%p (max_pdu: %u)", new, max_pdu);
 
 	return 0;
 }
@@ -946,9 +962,8 @@
 	list_del (&vcc_data->list);
 	tasklet_enable (&instance->receive_tasklet);
 
-	if (vcc_data->skb)
-		dev_kfree_skb (vcc_data->skb);
-	vcc_data->skb = NULL;
+	kfree_skb (vcc_data->sarb);
+	vcc_data->sarb = NULL;
 
 	kfree (vcc_data);
 	vcc->dev_data = NULL;
@@ -1205,15 +1220,14 @@
 
 	for (i = 0; i < num_rcv_urbs; i++)
 		if ((result = usb_unlink_urb (instance->receivers [i].urb)) < 0)
-			dbg ("udsl_usb_disconnect: usb_unlink_urb on receive urb %d returned %d", i, result);
+			dbg ("udsl_usb_disconnect: usb_unlink_urb on receive urb %d returned %d!", i, result);
 
 	/* wait for completion handlers to finish */
 	do {
 		count = 0;
 		spin_lock_irq (&instance->receive_lock);
 		list_for_each (pos, &instance->spare_receivers)
-			if (++count > num_rcv_urbs)
-				panic (__FILE__ ": memory corruption detected at line %d!\n", __LINE__);
+			DEBUG_ON (++count > num_rcv_urbs);
 		spin_unlock_irq (&instance->receive_lock);
 
 		dbg ("udsl_usb_disconnect: found %u spare receivers", count);
@@ -1242,15 +1256,14 @@
 
 	for (i = 0; i < num_snd_urbs; i++)
 		if ((result = usb_unlink_urb (instance->senders [i].urb)) < 0)
-			dbg ("udsl_usb_disconnect: usb_unlink_urb on send urb %d returned %d", i, result);
+			dbg ("udsl_usb_disconnect: usb_unlink_urb on send urb %d returned %d!", i, result);
 
 	/* wait for completion handlers to finish */
 	do {
 		count = 0;
 		spin_lock_irq (&instance->send_lock);
 		list_for_each (pos, &instance->spare_senders)
-			if (++count > num_snd_urbs)
-				panic (__FILE__ ": memory corruption detected at line %d!\n", __LINE__);
+			DEBUG_ON (++count > num_snd_urbs);
 		spin_unlock_irq (&instance->send_lock);
 
 		dbg ("udsl_usb_disconnect: found %u spare senders", count);
@@ -1289,11 +1302,9 @@
 
 static int __init udsl_usb_init (void)
 {
-	struct sk_buff *skb; /* dummy for sizeof */
-
 	dbg ("udsl_usb_init: driver version " DRIVER_VERSION);
 
-	if (sizeof (struct udsl_control) > sizeof (skb->cb)) {
+	if (sizeof (struct udsl_control) > sizeof (((struct sk_buff *)0)->cb)) {
 		printk (KERN_ERR __FILE__ ": unusable with this kernel!\n");
 		return -EIO;
 	}
diff -urN linux-2.4.22-bk28/drivers/usb/storage/protocol.c linux-2.4.22-bk29/drivers/usb/storage/protocol.c
--- linux-2.4.22-bk28/drivers/usb/storage/protocol.c	2003-08-25 04:44:42.000000000 -0700
+++ linux-2.4.22-bk29/drivers/usb/storage/protocol.c	2003-10-06 02:50:31.000000000 -0700
@@ -237,6 +237,10 @@
 	 * a unsigned char cmnd[12], so we know we have storage available
 	 */
 
+	/* Pad the ATAPI command with zeros */
+	for (; srb->cmd_len<12; srb->cmd_len++)
+		srb->cmnd[srb->cmd_len] = 0;
+
 	/* set command length to 12 bytes (this affects the transport layer) */
 	srb->cmd_len = 12;
 
diff -urN linux-2.4.22-bk28/drivers/usb/storage/unusual_devs.h linux-2.4.22-bk29/drivers/usb/storage/unusual_devs.h
--- linux-2.4.22-bk28/drivers/usb/storage/unusual_devs.h	2003-10-06 02:50:26.000000000 -0700
+++ linux-2.4.22-bk29/drivers/usb/storage/unusual_devs.h	2003-10-06 02:50:31.000000000 -0700
@@ -289,7 +289,7 @@
 UNUSUAL_DEV(  0x057b, 0x0000, 0x0000, 0x0299, 
 		"Y-E Data",
 		"Flashbuster-U",
-		US_SC_UFI,  US_PR_CB, NULL,
+		US_SC_DEVICE,  US_PR_CB, NULL,
 		US_FL_SINGLE_LUN),
 
 UNUSUAL_DEV(  0x057b, 0x0000, 0x0300, 0x9999, 
@@ -394,6 +394,12 @@
 		"Dimage F300",
 		US_SC_SCSI, US_PR_BULK, NULL, 0 ),
 
+/* Reported by Miguel A. Fosas <amn3s1a@ono.com> */
+UNUSUAL_DEV(  0x0686, 0x4017, 0x0001, 0x0001,
+                "Minolta",
+                "DIMAGE E223",
+                US_SC_SCSI, US_PR_DEVICE, NULL, 0 ),
+
 UNUSUAL_DEV(  0x0693, 0x0002, 0x0100, 0x0100, 
 		"Hagiwara",
 		"FlashGate SmartMedia",
@@ -542,7 +548,7 @@
  * - They don't like the INQUIRY command. So we must handle this command
  *   of the SCSI layer ourselves.
  */
-UNUSUAL_DEV( 0x07cf, 0x1001, 0x1000, 0x9009,
+UNUSUAL_DEV( 0x07cf, 0x1001, 0x1000, 0x5009,
 		"Casio",
 		"QV DigitalCamera",
 		US_SC_8070, US_PR_CB, NULL,
diff -urN linux-2.4.22-bk28/drivers/usb/usbnet.c linux-2.4.22-bk29/drivers/usb/usbnet.c
--- linux-2.4.22-bk28/drivers/usb/usbnet.c	2003-10-06 02:50:26.000000000 -0700
+++ linux-2.4.22-bk29/drivers/usb/usbnet.c	2003-10-06 02:50:31.000000000 -0700
@@ -133,6 +133,7 @@
 #include <linux/etherdevice.h>
 #include <linux/random.h>
 #include <linux/ethtool.h>
+#include <linux/mii.h>
 #include <linux/tqueue.h>
 #include <asm/uaccess.h>
 #include <asm/unaligned.h>
@@ -157,6 +158,7 @@
 
 /* minidrivers _could_ be individually configured */
 #define	CONFIG_USB_AN2720
+#define	CONFIG_USB_AX8817X
 #define	CONFIG_USB_BELKIN
 #define	CONFIG_USB_EPSON2888
 #define	CONFIG_USB_GENESYS
@@ -227,6 +229,7 @@
 	struct net_device	net;
 	struct net_device_stats	stats;
 	int			msg_level;
+	struct mii_if_info	mii;
 
 #ifdef CONFIG_USB_NET1080
 	u16			packet_id;
@@ -254,6 +257,10 @@
 #define FLAG_FRAMING_GL	0x0002		/* genelink batches packets */
 #define FLAG_FRAMING_Z	0x0004		/* zaurus adds a trailer */
 #define FLAG_NO_SETINT	0x0010		/* device can't set_interface() */
+#define FLAG_ETHER	0x0020		/* maybe use "eth%d" names */
+
+	/* init device ... can sleep, or cause probe() failure */
+	int	(*bind)(struct usbnet *, struct usb_device *);
 
 	/* reset device ... can sleep */
 	int	(*reset)(struct usbnet *);
@@ -275,6 +282,8 @@
 	int		in;		/* rx endpoint */
 	int		out;		/* tx endpoint */
 	int		epsize;
+
+	unsigned long	data;		/* Misc driver specific data */
 };
 
 // we record the state for each of our queued skbs
@@ -305,6 +314,8 @@
 #define	RUN_CONTEXT (in_irq () ? "in_irq" \
 			: (in_interrupt () ? "in_interrupt" : "can sleep"))
 
+static struct ethtool_ops usbnet_ethtool_ops;
+
 /* mostly for PDA style devices, which are always present */
 static int always_connected (struct usbnet *dev)
 {
@@ -405,6 +416,306 @@
 
 #endif	/* CONFIG_USB_AN2720 */
 
+
+#ifdef CONFIG_USB_AX8817X
+/* ASIX AX8817X based USB 2.0 Ethernet Devices */
+
+#define HAVE_HARDWARE
+#define NEED_MII
+
+#include <linux/crc32.h>
+
+#define AX_CMD_SET_SW_MII		0x06
+#define AX_CMD_READ_MII_REG		0x07
+#define AX_CMD_WRITE_MII_REG		0x08
+#define AX_CMD_SET_HW_MII		0x0a
+#define AX_CMD_WRITE_RX_CTL		0x10
+#define AX_CMD_READ_IPG012		0x11
+#define AX_CMD_WRITE_IPG0		0x12
+#define AX_CMD_WRITE_IPG1		0x13
+#define AX_CMD_WRITE_IPG2		0x14
+#define AX_CMD_WRITE_MULTI_FILTER	0x16
+#define AX_CMD_READ_NODE_ID		0x17
+#define AX_CMD_READ_PHY_ID		0x19
+#define AX_CMD_WRITE_MEDIUM_MODE	0x1b
+#define AX_CMD_WRITE_GPIOS		0x1f
+
+#define AX_MCAST_FILTER_SIZE		8
+#define AX_MAX_MCAST			64
+
+static int ax8817x_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			    u16 size, void *data)
+{
+	return usb_control_msg(
+		dev->udev,
+		usb_rcvctrlpipe(dev->udev, 0),
+		cmd,
+		USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+		value,
+		index,
+		data,
+		size,
+		CONTROL_TIMEOUT_JIFFIES);
+}
+
+static int ax8817x_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			     u16 size, void *data)
+{
+	return usb_control_msg(
+		dev->udev,
+		usb_sndctrlpipe(dev->udev, 0),
+		cmd,
+		USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+		value,
+		index,
+		data,
+		size,
+		CONTROL_TIMEOUT_JIFFIES);
+}
+
+static void ax8817x_async_cmd_callback(struct urb *urb)
+{
+	struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
+
+	if (urb->status < 0)
+		printk(KERN_DEBUG "ax8817x_async_cmd_callback() failed with %d",
+			urb->status);
+
+	kfree(req);
+	usb_free_urb(urb);
+}
+
+static void ax8817x_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+				    u16 size, void *data)
+{
+	struct usb_ctrlrequest *req;
+	int status;
+	struct urb *urb;
+
+	if ((urb = ALLOC_URB(0, GFP_ATOMIC)) == NULL) {
+		devdbg(dev, "Error allocating URB in write_cmd_async!");
+		return;
+	}
+
+	if ((req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC)) == NULL) {
+		devdbg(dev, "Failed to allocate memory for control request");
+		usb_free_urb(urb);
+		return;
+	}
+
+	req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+	req->bRequest = cmd;
+	req->wValue = cpu_to_le16(value);
+	req->wIndex = cpu_to_le16(index); 
+	req->wLength = cpu_to_le16(size);
+
+	usb_fill_control_urb(urb, dev->udev,
+			     usb_sndctrlpipe(dev->udev, 0),
+			     (void *)req, data, size,
+			     ax8817x_async_cmd_callback, req);
+
+	if((status = SUBMIT_URB(urb, GFP_ATOMIC)) < 0)
+		devdbg(dev, "Error submitting the control message: status=%d", status);
+}
+
+static void ax8817x_set_multicast(struct net_device *net)
+{
+	struct usbnet *dev = (struct usbnet *) net->priv;
+	u8 rx_ctl = 0x8c;
+
+	if (net->flags & IFF_PROMISC) {
+		rx_ctl |= 0x01;
+	} else if (net->flags & IFF_ALLMULTI
+		   || net->mc_count > AX_MAX_MCAST) {
+		rx_ctl |= 0x02;
+	} else if (net->mc_count == 0) {
+		/* just broadcast and directed */
+	} else {
+		struct dev_mc_list *mc_list = net->mc_list;
+		u8 *multi_filter;
+		u32 crc_bits;
+		int i;
+
+		multi_filter = kmalloc(AX_MCAST_FILTER_SIZE, GFP_ATOMIC);
+		if (multi_filter == NULL) {
+			/* Oops, couldn't allocate a buffer for setting the multicast
+			   filter. Try all multi mode. */
+			rx_ctl |= 0x02;
+		} else {
+			memset(multi_filter, 0, AX_MCAST_FILTER_SIZE);
+
+			/* Build the multicast hash filter. */
+			for (i = 0; i < net->mc_count; i++) {
+				crc_bits =
+				    ether_crc(ETH_ALEN,
+					      mc_list->dmi_addr) >> 26;
+				multi_filter[crc_bits >> 3] |=
+				    1 << (crc_bits & 7);
+				mc_list = mc_list->next;
+			}
+
+			ax8817x_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
+					   AX_MCAST_FILTER_SIZE, multi_filter);
+
+			rx_ctl |= 0x10;
+		}
+	}
+
+	ax8817x_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL);
+}
+
+static int ax8817x_mdio_read(struct net_device *netdev, int phy_id, int loc)
+{
+	struct usbnet *dev = netdev->priv;
+	u16 res;
+	u8 buf[4];
+
+	ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, &buf);
+	ax8817x_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, (__u16)loc, 2, (u16 *)&res);
+	ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf);
+
+	return res & 0xffff;
+}
+
+static void ax8817x_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
+{
+	struct usbnet *dev = netdev->priv;
+	u16 res = val;
+	u8 buf[4];
+
+	ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, &buf);
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, (u16 *)&res);
+	ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf);
+}
+
+static int ax8817x_bind(struct usbnet *dev, struct usb_device *intf)
+{
+	int ret;
+	u8 buf[6];
+	u16 *buf16 = (u16 *) buf;
+	int i;
+	unsigned long gpio_bits = dev->driver_info->data;
+
+	dev->in = usb_rcvbulkpipe(dev->udev, 3);
+	dev->out = usb_sndbulkpipe(dev->udev, 2);
+
+	/* Toggle the GPIOs in a manufacturer/model specific way */
+	for (i = 2; i >= 0; i--) {
+		if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+				       (gpio_bits >> (i * 8)) & 0xff, 0, 0,
+				       buf)) < 0)
+			return ret;
+		wait_ms(5);
+        }
+                                                                                
+	if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x80, 0, 0, buf)) < 0) {
+		dbg("send AX_CMD_WRITE_RX_CTL failed: %d", ret);
+		return ret;
+	}
+
+	/* Get the MAC address */
+	memset(buf, 0, ETH_ALEN);
+	if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, 6, buf)) < 0) {
+		dbg("read AX_CMD_READ_NODE_ID failed: %d", ret);
+		return ret;
+	}
+	memcpy(dev->net.dev_addr, buf, ETH_ALEN);
+
+	/* Get IPG values */
+	if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_IPG012, 0, 0, 3, buf)) < 0) {
+		dbg("Error reading IPG values: %d", ret);
+		return ret;
+	}
+
+	for(i = 0;i < 3;i++) {
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_IPG0 + i, 0, 0, 1, &buf[i]);
+	}
+
+	/* Get the PHY id */
+	if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf)) < 0) {
+		dbg("error on read AX_CMD_READ_PHY_ID: %02x", ret);
+		return ret;
+	} else if (ret < 2) {
+		/* this should always return 2 bytes */
+		dbg("AX_CMD_READ_PHY_ID returned less than 2 bytes: ret=%02x", ret);
+		return -EIO;
+	}
+
+	/* Initialize MII structure */
+	dev->mii.dev = &dev->net;
+	dev->mii.mdio_read = ax8817x_mdio_read;
+	dev->mii.mdio_write = ax8817x_mdio_write;
+	dev->mii.phy_id_mask = 0x3f;
+	dev->mii.reg_num_mask = 0x1f;
+	dev->mii.phy_id = buf[1];
+
+	if ((ret = ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, &buf)) < 0) {
+		dbg("Failed to go to software MII mode: %02x", ret);
+		return ret;
+	}
+
+	*buf16 = cpu_to_le16(BMCR_RESET);
+	if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG,
+				     dev->mii.phy_id, MII_BMCR, 2, buf16)) < 0) {
+		dbg("Failed to write MII reg - MII_BMCR: %02x", ret);
+		return ret;
+	}
+
+	/* Advertise that we can do full-duplex pause */
+	*buf16 = cpu_to_le16(ADVERTISE_ALL | ADVERTISE_CSMA | 0x0400);
+	if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG,
+			   	     dev->mii.phy_id, MII_ADVERTISE, 
+				     2, buf16)) < 0) {
+		dbg("Failed to write MII_REG advertisement: %02x", ret);
+		return ret;
+	}
+
+	*buf16 = cpu_to_le16(BMCR_ANENABLE | BMCR_ANRESTART);
+	if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG,
+			  	     dev->mii.phy_id, MII_BMCR, 
+				     2, buf16)) < 0) {
+		dbg("Failed to write MII reg autonegotiate: %02x", ret);
+		return ret;
+	}
+
+	if ((ret = ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf)) < 0) {
+		dbg("Failed to set hardware MII: %02x", ret);
+		return ret;
+	}
+
+	dev->net.set_multicast_list = ax8817x_set_multicast;
+
+	return 0;
+}
+
+static const struct driver_info ax8817x_info = {
+	.description = "ASIX AX8817x USB 2.0 Ethernet",
+	.bind = ax8817x_bind,
+	.flags =  FLAG_ETHER,
+	.data = 0x00130103,
+};
+
+static const struct driver_info dlink_dub_e100_info = {
+	.description = "DLink DUB-E100 USB Ethernet",
+	.bind = ax8817x_bind,
+	.flags =  FLAG_ETHER,
+	.data = 0x009f9d9f,
+};
+
+static const struct driver_info netgear_fa120_info = {
+	.description = "Netgear FA-120 USB Ethernet",
+	.bind = ax8817x_bind,
+	.flags =  FLAG_ETHER,
+	.data = 0x00130103,
+};
+
+static const struct driver_info hawking_uf200_info = {
+	.description = "Hawking UF200 USB Ethernet",
+	.bind = ax8817x_bind,
+	.flags =  FLAG_ETHER,
+	.data = 0x001f1d1f,
+};
+#endif /* CONFIG_USB_AX8817X */
 
 
 #ifdef	CONFIG_USB_BELKIN
@@ -1779,74 +2090,56 @@
 
 /*-------------------------------------------------------------------------*/
 
-static int usbnet_ethtool_ioctl (struct net_device *net, void *useraddr)
+static void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info)
 {
-	struct usbnet	*dev = (struct usbnet *) net->priv;
-	u32		cmd;
+	struct usbnet *dev = net->priv;
 
-	if (get_user (cmd, (u32 *)useraddr))
-		return -EFAULT;
-	switch (cmd) {
-
-	case ETHTOOL_GDRVINFO: {	/* get driver info */
-		struct ethtool_drvinfo		info;
-
-		memset (&info, 0, sizeof info);
-		info.cmd = ETHTOOL_GDRVINFO;
-		strncpy (info.driver, driver_name, sizeof info.driver);
-		strncpy (info.version, DRIVER_VERSION, sizeof info.version);
-		strncpy (info.fw_version, dev->driver_info->description,
-			sizeof info.fw_version);
-		usb_make_path (dev->udev, info.bus_info, sizeof info.bus_info);
-		if (copy_to_user (useraddr, &info, sizeof (info)))
-			return -EFAULT;
-		return 0;
-		}
+	strncpy (info->driver, driver_name, sizeof info->driver);
+	strncpy (info->version, DRIVER_VERSION, sizeof info->version);
+	strncpy (info->fw_version, dev->driver_info->description,
+		sizeof info->fw_version);
+	usb_make_path (dev->udev, info->bus_info, sizeof info->bus_info);
+}
 
-	case ETHTOOL_GLINK: 		/* get link status */
-		if (dev->driver_info->check_connect) {
-			struct ethtool_value	edata = { ETHTOOL_GLINK };
-
-			edata.data = dev->driver_info->check_connect (dev) == 0;
-			if (copy_to_user (useraddr, &edata, sizeof (edata)))
-				return -EFAULT;
-			return 0;
-		}
-		break;
+static u32 usbnet_get_link (struct net_device *net)
+{
+	struct usbnet *dev = net->priv;
 
-	case ETHTOOL_GMSGLVL: {		/* get message-level */
-		struct ethtool_value	edata = {ETHTOOL_GMSGLVL};
+	/* If a check_connect is defined, return it's results */
+	if (dev->driver_info->check_connect)
+		return dev->driver_info->check_connect (dev) == 0;
 
-		edata.data = dev->msg_level;
-		if (copy_to_user (useraddr, &edata, sizeof (edata)))
-			return -EFAULT;
-		return 0;
-		}
+	/* Otherwise, we're up to avoid breaking scripts */
+	return 1;
+}
 
-	case ETHTOOL_SMSGLVL: {		/* set message-level */
-		struct ethtool_value	edata;
+static u32 usbnet_get_msglevel (struct net_device *net)
+{
+	struct usbnet *dev = net->priv;
 
-		if (copy_from_user (&edata, useraddr, sizeof (edata)))
-			return -EFAULT;
-		dev->msg_level = edata.data;
-		return 0;
-		}
-	
-	/* could also map RINGPARAM to RX/TX QLEN */
+	return dev->msg_level;
+}
 
-	}
-	/* Note that the ethtool user space code requires EOPNOTSUPP */
-	return -EOPNOTSUPP;
+static void usbnet_set_msglevel (struct net_device *net, u32 level)
+{
+	struct usbnet *dev = net->priv;
+
+	dev->msg_level = level;
 }
 
 static int usbnet_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
 {
-	switch (cmd) {
-	case SIOCETHTOOL:
-		return usbnet_ethtool_ioctl (net, (void *)rq->ifr_data);
-	default:
-		return -EOPNOTSUPP;
+#ifdef NEED_MII
+	{
+	struct usbnet *dev = (struct usbnet *)net->priv;
+
+	if (dev->mii.mdio_read != NULL && dev->mii.mdio_write != NULL)
+		return generic_mii_ioctl(&dev->mii,
+				(struct mii_ioctl_data *) &rq->ifr_data,
+				cmd, NULL);
 	}
+#endif
+	return -EOPNOTSUPP;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -2155,6 +2448,7 @@
 	struct net_device 		*net;
 	struct driver_info		*info;
 	int				altnum = 0;
+	int				status;
 
 	info = (struct driver_info *) prod->driver_info;
 
@@ -2221,22 +2515,27 @@
 	net->watchdog_timeo = TX_TIMEOUT_JIFFIES;
 	net->tx_timeout = usbnet_tx_timeout;
 	net->do_ioctl = usbnet_ioctl;
+	net->ethtool_ops = &usbnet_ethtool_ops;
 
-	// get rx/tx params from descriptors; avoid compiled-in details
-	if (!info->in || !info->out) {
-		int status = get_endpoints (dev,
-					udev->actconfig->interface + ifnum);
-		if (status < 0) {
-			err ("get_endpoints failed, %d", status);
-			kfree (dev);
-			return 0;
-		}
-	} else {
+	// allow device-specific bind/init procedures
+	// NOTE net->name still not usable ...
+	if (info->bind) {
+		status = info->bind (dev, udev);
+		// heuristic:  "usb%d" for links we know are two-host,
+		// else "eth%d" when there's reasonable doubt.  userspace
+		// can rename the link if it knows better.
+		if ((dev->driver_info->flags & FLAG_ETHER) != 0
+				&& (net->dev_addr [0] & 0x02) == 0)
+			strcpy (net->name, "eth%d");
+	} else if (!info->in || info->out)
+		status = get_endpoints (dev, udev->actconfig->interface + ifnum);
+	else {
 		dev->in = usb_rcvbulkpipe (udev, info->in);
 		dev->out = usb_sndbulkpipe (udev, info->out);
-		dev->maxpacket = info->epsize;
 	}
 
+	dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1);
+
 	register_netdev (&dev->net);
 	devinfo (dev, "register usbnet usb-%s-%s, %s",
 		udev->bus->bus_name, udev->devpath,
@@ -2274,6 +2573,30 @@
 },
 #endif
 
+#ifdef CONFIG_USB_AX8817X
+{
+	// Linksys USB200M
+	USB_DEVICE (0x077b, 0x2226),
+	.driver_info =	(unsigned long) &ax8817x_info,
+}, {
+	// Netgear FA120
+	USB_DEVICE (0x0846, 0x1040),
+	.driver_info =	(unsigned long) &netgear_fa120_info,
+}, {
+	// DLink DUB-E100
+	USB_DEVICE (0x2001, 0x1a00),
+	.driver_info =	(unsigned long) &dlink_dub_e100_info,
+}, {
+	// Intellinet, ST Lab USB Ethernet
+	USB_DEVICE (0x0b95, 0x1720),
+	.driver_info =	(unsigned long) &ax8817x_info,
+}, {
+	// Hawking UF200, TrendNet TU2-ET100
+	USB_DEVICE (0x07b8, 0x420a),
+	.driver_info =	(unsigned long) &hawking_uf200_info,
+}, 
+#endif
+
 #ifdef	CONFIG_USB_BELKIN
 {
 	USB_DEVICE (0x050d, 0x0004),	// Belkin
@@ -2415,6 +2738,13 @@
 	.disconnect =	usbnet_disconnect,
 };
 
+/* Default ethtool_ops assigned.  Devices can override in their bind() routine */
+static struct ethtool_ops usbnet_ethtool_ops = {
+	.get_drvinfo		= usbnet_get_drvinfo,
+	.get_link		= usbnet_get_link,
+	.get_msglevel		= usbnet_get_msglevel,
+	.set_msglevel		= usbnet_set_msglevel,
+};
 /*-------------------------------------------------------------------------*/
 
 static int __init usbnet_init (void)
diff -urN linux-2.4.22-bk28/drivers/usb/vicam.c linux-2.4.22-bk29/drivers/usb/vicam.c
--- linux-2.4.22-bk28/drivers/usb/vicam.c	2003-10-06 02:50:26.000000000 -0700
+++ linux-2.4.22-bk29/drivers/usb/vicam.c	2003-10-06 02:50:31.000000000 -0700
@@ -1004,8 +1004,10 @@
 	if (count > cam->framebuf_size)
 		count = cam->framebuf_size;
 
-	if (copy_to_user(buf, cam->framebuf, count))
+	if (copy_to_user(buf, cam->framebuf, count)) {
+		up(&cam->busy_lock);
 		return -EFAULT;
+	}
 
 	if (count != cam->framebuf_size)
 		cam->framebuf_read_start = count;
diff -urN linux-2.4.22-bk28/fs/fcntl.c linux-2.4.22-bk29/fs/fcntl.c
--- linux-2.4.22-bk28/fs/fcntl.c	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.22-bk29/fs/fcntl.c	2003-10-06 02:50:31.000000000 -0700
@@ -11,6 +11,7 @@
 #include <linux/smp_lock.h>
 #include <linux/slab.h>
 #include <linux/iobuf.h>
+#include <linux/ptrace.h>
 
 #include <asm/poll.h>
 #include <asm/siginfo.h>
@@ -293,6 +294,7 @@
 			 * to fix this will be in libc.
 			 */
 			err = filp->f_owner.pid;
+			force_successful_syscall_return();
 			break;
 		case F_SETOWN:
 			lock_kernel();
diff -urN linux-2.4.22-bk28/fs/proc/base.c linux-2.4.22-bk29/fs/proc/base.c
--- linux-2.4.22-bk28/fs/proc/base.c	2003-10-06 02:50:26.000000000 -0700
+++ linux-2.4.22-bk29/fs/proc/base.c	2003-10-06 02:50:31.000000000 -0700
@@ -476,7 +476,24 @@
 }
 #endif
 
+static loff_t mem_lseek(struct file * file, loff_t offset, int orig)
+{
+	switch (orig) {
+	case 0:
+		file->f_pos = offset;
+		break;
+	case 1:
+		file->f_pos += offset;
+		break;
+	default:
+		return -EINVAL;
+	}
+	force_successful_syscall_return();
+	return file->f_pos;
+}
+
 static struct file_operations proc_mem_operations = {
+	llseek:		mem_lseek,
 	read:		mem_read,
 	write:		mem_write,
 	open:		mem_open,
diff -urN linux-2.4.22-bk28/include/asm-ia64/ptrace.h linux-2.4.22-bk29/include/asm-ia64/ptrace.h
--- linux-2.4.22-bk28/include/asm-ia64/ptrace.h	2003-10-06 02:50:26.000000000 -0700
+++ linux-2.4.22-bk29/include/asm-ia64/ptrace.h	2003-10-06 02:50:31.000000000 -0700
@@ -248,11 +248,10 @@
   extern void ia64_increment_ip (struct pt_regs *pt);
   extern void ia64_decrement_ip (struct pt_regs *pt);
 
-static inline void
-force_successful_syscall_return (void)
-{
-	ia64_task_regs(current)->r8 = 0;
-}
+#define force_successful_syscall_return()		\
+	do {						\
+		ia64_task_regs(current)->r8 = 0;	\
+	} while (0)
 
 #endif /* !__KERNEL__ */
 
diff -urN linux-2.4.22-bk28/include/asm-x86_64/acpi.h linux-2.4.22-bk29/include/asm-x86_64/acpi.h
--- linux-2.4.22-bk28/include/asm-x86_64/acpi.h	2003-10-06 02:50:27.000000000 -0700
+++ linux-2.4.22-bk29/include/asm-x86_64/acpi.h	2003-10-06 02:50:33.000000000 -0700
@@ -115,18 +115,6 @@
 #endif
 #ifdef CONFIG_X86_IO_APIC
 extern int acpi_ioapic;
-extern int skip_ioapic_setup;
-
-static inline void disable_ioapic_setup(void)
-{
-	
-	skip_ioapic_setup = 1;
-}
-
-static inline int ioapic_setup_disabled(void)
-{
-	return skip_ioapic_setup;
-}
 #else
 static inline void disable_ioapic_setup(void)
 { }
diff -urN linux-2.4.22-bk28/include/asm-x86_64/apic.h linux-2.4.22-bk29/include/asm-x86_64/apic.h
--- linux-2.4.22-bk28/include/asm-x86_64/apic.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.22-bk29/include/asm-x86_64/apic.h	2003-10-06 02:50:33.000000000 -0700
@@ -102,4 +102,17 @@
 #define esr_disable 0
 extern unsigned boot_cpu_id;
 
+extern int skip_ioapic_setup;
+
+static inline void disable_ioapic_setup(void)
+{
+	
+	skip_ioapic_setup = 1;
+}
+
+static inline int ioapic_setup_disabled(void)
+{
+	return skip_ioapic_setup;
+}
+
 #endif /* __ASM_APIC_H */
diff -urN linux-2.4.22-bk28/include/linux/pci.h linux-2.4.22-bk29/include/linux/pci.h
--- linux-2.4.22-bk28/include/linux/pci.h	2003-10-06 02:50:27.000000000 -0700
+++ linux-2.4.22-bk29/include/linux/pci.h	2003-10-06 02:50:33.000000000 -0700
@@ -628,7 +628,6 @@
 #define HAVE_PCI_SET_MWI
 int pci_set_mwi(struct pci_dev *dev);
 void pci_clear_mwi(struct pci_dev *dev);
-int pci_generic_prep_mwi(struct pci_dev *dev);
 int pci_set_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask);
 int pci_assign_resource(struct pci_dev *dev, int i);
diff -urN linux-2.4.22-bk28/include/linux/ptrace.h linux-2.4.22-bk29/include/linux/ptrace.h
--- linux-2.4.22-bk28/include/linux/ptrace.h	2001-09-14 15:55:17.000000000 -0700
+++ linux-2.4.22-bk29/include/linux/ptrace.h	2003-10-06 02:50:33.000000000 -0700
@@ -23,4 +23,22 @@
 
 #include <asm/ptrace.h>
 
+#ifdef __KERNEL__
+
+#ifndef force_successful_syscall_return
+/*
+ * System call handlers that, upon successful completion, need to return a
+ * negative value should call force_successful_syscall_return() right before
+ * returning.  On architectures where the syscall convention provides for a
+ * separate error flag (e.g., alpha, ia64, ppc{,64}, sparc{,64}, possibly
+ * others), this macro can be used to ensure that the error flag will not get
+ * set.  On architectures which do not support a separate error flag, the macro
+ * is a no-op and the spurious error condition needs to be filtered out by some
+ * other means (e.g., in user-level, by passing an extra argument to the
+ * syscall handler, or something along those lines).
+ */
+#define force_successful_syscall_return() do { } while (0)
+#endif
+#endif
+
 #endif
diff -urN linux-2.4.22-bk28/include/linux/usb_ch9.h linux-2.4.22-bk29/include/linux/usb_ch9.h
--- linux-2.4.22-bk28/include/linux/usb_ch9.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-bk29/include/linux/usb_ch9.h	2003-10-06 02:50:33.000000000 -0700
@@ -0,0 +1,315 @@
+/*
+ * This file holds USB constants and structures that are needed for USB
+ * device APIs.  These are used by the USB device model, which is defined
+ * in chapter 9 of the USB 2.0 specification.  Linux has several APIs in C
+ * that need these:
+ *
+ * - the master/host side Linux-USB kernel driver API;
+ * - the "usbfs" user space API; and
+ * - (eventually) a Linux "gadget" slave/device side driver API.
+ *
+ * USB 2.0 adds an additional "On The Go" (OTG) mode, which lets systems
+ * act either as a USB master/host or as a USB slave/device.  That means
+ * the master and slave side APIs will benefit from working well together.
+ */
+
+#ifndef __LINUX_USB_CH9_H
+#define __LINUX_USB_CH9_H
+
+#include <asm/types.h>		/* __u8 etc */
+
+/*-------------------------------------------------------------------------*/
+
+/* CONTROL REQUEST SUPPORT */
+
+/*
+ * USB directions
+ *
+ * This bit flag is used in endpoint descriptors' bEndpointAddress field.
+ * It's also one of three fields in control requests bRequestType.
+ */
+#define USB_DIR_OUT			0		/* to device */
+#define USB_DIR_IN			0x80		/* to host */
+
+/*
+ * USB types, the second of three bRequestType fields
+ */
+#define USB_TYPE_MASK			(0x03 << 5)
+#define USB_TYPE_STANDARD		(0x00 << 5)
+#define USB_TYPE_CLASS			(0x01 << 5)
+#define USB_TYPE_VENDOR			(0x02 << 5)
+#define USB_TYPE_RESERVED		(0x03 << 5)
+
+/*
+ * USB recipients, the third of three bRequestType fields
+ */
+#define USB_RECIP_MASK			0x1f
+#define USB_RECIP_DEVICE		0x00
+#define USB_RECIP_INTERFACE		0x01
+#define USB_RECIP_ENDPOINT		0x02
+#define USB_RECIP_OTHER			0x03
+
+/*
+ * Standard requests, for the bRequest field of a SETUP packet.
+ *
+ * These are qualified by the bRequestType field, so that for example
+ * TYPE_CLASS or TYPE_VENDOR specific feature flags could be retrieved
+ * by a GET_STATUS request.
+ */
+#define USB_REQ_GET_STATUS		0x00
+#define USB_REQ_CLEAR_FEATURE		0x01
+#define USB_REQ_SET_FEATURE		0x03
+#define USB_REQ_SET_ADDRESS		0x05
+#define USB_REQ_GET_DESCRIPTOR		0x06
+#define USB_REQ_SET_DESCRIPTOR		0x07
+#define USB_REQ_GET_CONFIGURATION	0x08
+#define USB_REQ_SET_CONFIGURATION	0x09
+#define USB_REQ_GET_INTERFACE		0x0A
+#define USB_REQ_SET_INTERFACE		0x0B
+#define USB_REQ_SYNCH_FRAME		0x0C
+
+
+/**
+ * struct usb_ctrlrequest - SETUP data for a USB device control request
+ * @bRequestType: matches the USB bmRequestType field
+ * @bRequest: matches the USB bRequest field
+ * @wValue: matches the USB wValue field (le16 byte order)
+ * @wIndex: matches the USB wIndex field (le16 byte order)
+ * @wLength: matches the USB wLength field (le16 byte order)
+ *
+ * This structure is used to send control requests to a USB device.  It matches
+ * the different fields of the USB 2.0 Spec section 9.3, table 9-2.  See the
+ * USB spec for a fuller description of the different fields, and what they are
+ * used for.
+ *
+ * Note that the driver for any interface can issue control requests.
+ * For most devices, interfaces don't coordinate with each other, so
+ * such requests may be made at any time.
+ */
+struct usb_ctrlrequest {
+	__u8 bRequestType;
+	__u8 bRequest;
+	__u16 wValue;
+	__u16 wIndex;
+	__u16 wLength;
+} __attribute__ ((packed));
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * STANDARD DESCRIPTORS ... as returned by GET_DESCRIPTOR, or
+ * (rarely) accepted by SET_DESCRIPTOR.
+ *
+ * Note that all multi-byte values here are encoded in little endian
+ * byte order "on the wire".  But when exposed through Linux-USB APIs,
+ * they've been converted to cpu byte order.
+ */
+
+/*
+ * Descriptor types ... USB 2.0 spec table 9.5
+ */
+#define USB_DT_DEVICE			0x01
+#define USB_DT_CONFIG			0x02
+#define USB_DT_STRING			0x03
+#define USB_DT_INTERFACE		0x04
+#define USB_DT_ENDPOINT			0x05
+#define USB_DT_DEVICE_QUALIFIER		0x06
+#define USB_DT_OTHER_SPEED_CONFIG	0x07
+#define USB_DT_INTERFACE_POWER		0x08
+
+/* All standard descriptors have these 2 fields at the beginning */
+struct usb_descriptor_header {
+	__u8  bLength;
+	__u8  bDescriptorType;
+} __attribute__ ((packed));
+
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_DEVICE: Device descriptor */
+struct usb_device_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+
+	__u16 bcdUSB;
+	__u8  bDeviceClass;
+	__u8  bDeviceSubClass;
+	__u8  bDeviceProtocol;
+	__u8  bMaxPacketSize0;
+	__u16 idVendor;
+	__u16 idProduct;
+	__u16 bcdDevice;
+	__u8  iManufacturer;
+	__u8  iProduct;
+	__u8  iSerialNumber;
+	__u8  bNumConfigurations;
+} __attribute__ ((packed));
+
+#define USB_DT_DEVICE_SIZE		18
+
+
+/*
+ * Device and/or Interface Class codes
+ * as found in bDeviceClass or bInterfaceClass
+ * and defined by www.usb.org documents
+ */
+#define USB_CLASS_PER_INTERFACE		0	/* for DeviceClass */
+#define USB_CLASS_AUDIO			1
+#define USB_CLASS_COMM			2
+#define USB_CLASS_HID			3
+#define USB_CLASS_PHYSICAL		5
+#define USB_CLASS_STILL_IMAGE		6
+#define USB_CLASS_PRINTER		7
+#define USB_CLASS_MASS_STORAGE		8
+#define USB_CLASS_HUB			9
+#define USB_CLASS_CDC_DATA		0x0a
+#define USB_CLASS_CSCID			0x0b	/* chip+ smart card */
+#define USB_CLASS_CONTENT_SEC		0x0d	/* content security */
+#define USB_CLASS_APP_SPEC		0xfe
+#define USB_CLASS_VENDOR_SPEC		0xff
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_CONFIG: Configuration descriptor information.
+ *
+ * USB_DT_OTHER_SPEED_CONFIG is the same descriptor, except that the
+ * descriptor type is different.  Highspeed-capable devices can look
+ * different depending on what speed they're currently running.  Only
+ * devices with a USB_DT_DEVICE_QUALIFIER have any OTHER_SPEED_CONFIG
+ * descriptors.
+ */
+struct usb_config_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+
+	__u16 wTotalLength;
+	__u8  bNumInterfaces;
+	__u8  bConfigurationValue;
+	__u8  iConfiguration;
+	__u8  bmAttributes;
+	__u8  bMaxPower;
+} __attribute__ ((packed));
+
+#define USB_DT_CONFIG_SIZE		9
+
+/* from config descriptor bmAttributes */
+#define USB_CONFIG_ATT_ONE		(1 << 7)	/* must be set */
+#define USB_CONFIG_ATT_SELFPOWER	(1 << 6)	/* self powered */
+#define USB_CONFIG_ATT_WAKEUP		(1 << 5)	/* can wakeup */
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_STRING: String descriptor */
+struct usb_string_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+
+	__u16 wData[1];		/* UTF-16LE encoded */
+} __attribute__ ((packed));
+
+/* note that "string" zero is special, it holds language codes that
+ * the device supports, not Unicode characters.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_INTERFACE: Interface descriptor */
+struct usb_interface_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+
+	__u8  bInterfaceNumber;
+	__u8  bAlternateSetting;
+	__u8  bNumEndpoints;
+	__u8  bInterfaceClass;
+	__u8  bInterfaceSubClass;
+	__u8  bInterfaceProtocol;
+	__u8  iInterface;
+} __attribute__ ((packed));
+
+#define USB_DT_INTERFACE_SIZE		9
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_ENDPOINT: Endpoint descriptor */
+struct usb_endpoint_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+
+	__u8  bEndpointAddress;
+	__u8  bmAttributes;
+	__u16 wMaxPacketSize;
+	__u8  bInterval;
+
+	// NOTE:  these two are _only_ in audio endpoints.
+	// use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof.
+	__u8  bRefresh;
+	__u8  bSynchAddress;
+} __attribute__ ((packed));
+
+#define USB_DT_ENDPOINT_SIZE		7
+#define USB_DT_ENDPOINT_AUDIO_SIZE	9	/* Audio extension */
+
+
+/*
+ * Endpoints
+ */
+#define USB_ENDPOINT_NUMBER_MASK	0x0f	/* in bEndpointAddress */
+#define USB_ENDPOINT_DIR_MASK		0x80
+
+#define USB_ENDPOINT_XFERTYPE_MASK	0x03	/* in bmAttributes */
+#define USB_ENDPOINT_XFER_CONTROL	0
+#define USB_ENDPOINT_XFER_ISOC		1
+#define USB_ENDPOINT_XFER_BULK		2
+#define USB_ENDPOINT_XFER_INT		3
+
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_DEVICE_QUALIFIER: Device Qualifier descriptor */
+struct usb_qualifier_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+
+	__u16 bcdUSB;
+	__u8  bDeviceClass;
+	__u8  bDeviceSubClass;
+	__u8  bDeviceProtocol;
+	__u8  bMaxPacketSize0;
+	__u8  bNumConfigurations;
+	__u8  bRESERVED;
+} __attribute__ ((packed));
+
+
+/*-------------------------------------------------------------------------*/
+
+/* USB 2.0 defines three speeds, here's how Linux identifies them */
+
+enum usb_device_speed {
+	USB_SPEED_UNKNOWN = 0,			/* enumerating */
+	USB_SPEED_LOW, USB_SPEED_FULL,		/* usb 1.1 */
+	USB_SPEED_HIGH				/* usb 2.0 */
+};
+
+enum usb_device_state {
+	/* NOTATTACHED isn't in the USB spec, and this state acts
+	 * the same as ATTACHED ... but it's clearer this way.
+	 */
+	USB_STATE_NOTATTACHED = 0,
+
+	/* the chapter 9 device states */
+	USB_STATE_ATTACHED,
+	USB_STATE_POWERED,
+	USB_STATE_DEFAULT,			/* limited function */
+	USB_STATE_ADDRESS,
+	USB_STATE_CONFIGURED,			/* most functions */
+
+	USB_STATE_SUSPENDED
+
+	/* NOTE:  there are actually four different SUSPENDED
+	 * states, returning to POWERED, DEFAULT, ADDRESS, or
+	 * CONFIGURED respectively when SOF tokens flow again.
+	 */
+};
+
+#endif	/* __LINUX_USB_CH9_H */
diff -urN linux-2.4.22-bk28/include/linux/usb_gadget.h linux-2.4.22-bk29/include/linux/usb_gadget.h
--- linux-2.4.22-bk28/include/linux/usb_gadget.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-bk29/include/linux/usb_gadget.h	2003-10-06 02:50:33.000000000 -0700
@@ -0,0 +1,719 @@
+/*
+ * <linux/usb_gadget.h>
+ *
+ * We call the USB code inside a Linux-based peripheral device a "gadget"
+ * driver, except for the hardware-specific bus glue.  One USB host can
+ * master many USB gadgets, but the gadgets are only slaved to one host.
+ *
+ *
+ * (c) Copyright 2002-2003 by David Brownell
+ * All Rights Reserved.
+ *
+ * This software is licensed under the GNU GPL version 2.
+ */
+
+#ifndef __LINUX_USB_GADGET_H
+#define __LINUX_USB_GADGET_H
+
+#ifdef __KERNEL__
+
+struct usb_ep;
+
+/**
+ * struct usb_request - describes one i/o request
+ * @buf: Buffer used for data.  Always provide this; some controllers
+ * 	only use PIO, or don't use DMA for some endpoints.
+ * @dma: DMA address corresponding to 'buf'.  If you don't set this
+ * 	field, and the usb controller needs one, it is responsible
+ * 	for mapping and unmapping the buffer.
+ * @length: Length of that data
+ * @no_interrupt: If true, hints that no completion irq is needed.
+ *	Helpful sometimes with deep request queues.
+ * @zero: If true, when writing data, makes the last packet be "short"
+ *     by adding a zero length packet as needed;
+ * @short_not_ok: When reading data, makes short packets be
+ *     treated as errors (queue stops advancing till cleanup).
+ * @complete: Function called when request completes
+ * @context: For use by the completion callback
+ * @list: For use by the gadget driver.
+ * @status: Reports completion code, zero or a negative errno.
+ * 	Normally, faults block the transfer queue from advancing until
+ * 	the completion callback returns.
+ * 	Code "-ESHUTDOWN" indicates completion caused by device disconnect,
+ * 	or when the driver disabled the endpoint.
+ * @actual: Reports actual bytes transferred.  For reads (OUT
+ * 	transfers) this may be less than the requested length.  If the
+ * 	short_not_ok flag is set, short reads are treated as errors
+ * 	even when status otherwise indicates successful completion.
+ * 	Note that for writes (IN transfers) the data bytes may still
+ * 	reside in a device-side FIFO.
+ *
+ * These are allocated/freed through the endpoint they're used with.  The
+ * hardware's driver can add extra per-request data to the memory it returns,
+ * which often avoids separate memory allocations (potential failures),
+ * later when the request is queued.
+ *
+ * Request flags affect request handling, such as whether a zero length
+ * packet is written (the "zero" flag), whether a short read should be
+ * treated as an error (blocking request queue advance, the "short_not_ok"
+ * flag), or hinting that an interrupt is not required (the "no_interrupt"
+ * flag, for use with deep request queues).
+ *
+ * Bulk endpoints can use any size buffers, and can also be used for interrupt
+ * transfers. interrupt-only endpoints can be much less functional.
+ */
+	// NOTE this is analagous to 'struct urb' on the host side,
+	// except that it's thinner and promotes more pre-allocation.
+	//
+	// ISSUE should this be allocated through the device?
+
+struct usb_request {
+	void			*buf;
+	unsigned		length;
+	dma_addr_t		dma;
+
+	unsigned		no_interrupt : 1,
+				zero : 1,
+				short_not_ok : 1;
+
+	void			(*complete)(struct usb_ep *ep,
+					struct usb_request *req);
+	void			*context;
+	struct list_head	list;
+
+	int			status;
+	unsigned		actual;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* endpoint-specific parts of the api to the usb controller hardware.
+ * unlike the urb model, (de)multiplexing layers are not required.
+ * (so this api could slash overhead if used on the host side...)
+ *
+ * note that device side usb controllers commonly differ in how many
+ * endpoints they support, as well as their capabilities.
+ */
+struct usb_ep_ops {
+	int (*enable) (struct usb_ep *ep,
+		const struct usb_endpoint_descriptor *desc);
+	int (*disable) (struct usb_ep *ep);
+
+	struct usb_request *(*alloc_request) (struct usb_ep *ep,
+		int gfp_flags);
+	void (*free_request) (struct usb_ep *ep, struct usb_request *req);
+
+	void *(*alloc_buffer) (struct usb_ep *ep, unsigned bytes,
+		dma_addr_t *dma, int gfp_flags);
+	void (*free_buffer) (struct usb_ep *ep, void *buf, dma_addr_t dma,
+		unsigned bytes);
+	// NOTE:  on 2.5, drivers may also use dma_map() and
+	// dma_sync_single() to manage dma overhead. 
+
+	int (*queue) (struct usb_ep *ep, struct usb_request *req,
+		int gfp_flags);
+	int (*dequeue) (struct usb_ep *ep, struct usb_request *req);
+
+	int (*set_halt) (struct usb_ep *ep, int value);
+	int (*fifo_status) (struct usb_ep *ep);
+	void (*fifo_flush) (struct usb_ep *ep);
+};
+
+/**
+ * struct usb_ep - device side representation of USB endpoint
+ * @name:identifier for the endpoint, such as "ep-a" or "ep9in-bulk"
+ * @ep_list:the gadget's ep_list holds all of its endpoints
+ * @maxpacket:the maximum packet size used on this endpoint, as
+ * 	configured when the endpoint was enabled.
+ * @driver_data:for use by the gadget driver.  all other fields are
+ * 	read-only to gadget drivers.
+ *
+ * the bus controller driver lists all the general purpose endpoints in
+ * gadget->ep_list.  the control endpoint (gadget->ep0) is not in that list,
+ * and is accessed only in response to a driver setup() callback.
+ */
+struct usb_ep {
+	void			*driver_data;
+
+	const char		*name;
+	const struct usb_ep_ops	*ops;
+	struct list_head	ep_list;
+	unsigned		maxpacket : 16;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * usb_ep_enable - configure endpoint, making it usable
+ * @ep:the endpoint being configured.  may not be the endpoint named "ep0".
+ * 	drivers discover endpoints through the ep_list of a usb_gadget.
+ * @desc:descriptor for desired behavior.  caller guarantees this pointer
+ * 	remains valid until the endpoint is disabled; the data byte order
+ * 	is little-endian (usb-standard).
+ *
+ * when configurations are set, or when interface settings change, the driver
+ * will enable or disable the relevant endpoints.  while it is enabled, an
+ * endpoint may be used for i/o until the driver receives a disconnect() from
+ * the host or until the endpoint is disabled.
+ *
+ * the ep0 implementation (which calls this routine) must ensure that the
+ * hardware capabilities of each endpoint match the descriptor provided
+ * for it.  for example, an endpoint named "ep2in-bulk" would be usable
+ * for interrupt transfers as well as bulk, but it likely couldn't be used
+ * for iso transfers or for endpoint 14.  some endpoints are fully
+ * configurable, with more generic names like "ep-a".  (remember that for
+ * USB, "in" means "towards the USB master".)
+ *
+ * returns zero, or a negative error code.
+ */
+static inline int
+usb_ep_enable (struct usb_ep *ep, const struct usb_endpoint_descriptor *desc)
+{
+	return ep->ops->enable (ep, desc);
+}
+
+/**
+ * usb_ep_disable - endpoint is no longer usable
+ * @ep:the endpoint being unconfigured.  may not be the endpoint named "ep0".
+ *
+ * no other task may be using this endpoint when this is called.
+ * any pending and uncompleted requests will complete with status
+ * indicating disconnect (-ESHUTDOWN) before this call returns.
+ * gadget drivers must call usb_ep_enable() again before queueing
+ * requests to the endpoint.
+ *
+ * returns zero, or a negative error code.
+ */
+static inline int
+usb_ep_disable (struct usb_ep *ep)
+{
+	return ep->ops->disable (ep);
+}
+
+/**
+ * usb_ep_alloc_request - allocate a request object to use with this endpoint
+ * @ep:the endpoint to be used with with the request
+ * @gfp_flags:GFP_* flags to use
+ *
+ * Request objects must be allocated with this call, since they normally
+ * need controller-specific setup and may even need endpoint-specific
+ * resources such as allocation of DMA descriptors.
+ * Requests may be submitted with usb_ep_queue(), and receive a single
+ * completion callback.  Free requests with usb_ep_free_request(), when
+ * they are no longer needed.
+ *
+ * Returns the request, or null if one could not be allocated.
+ */
+static inline struct usb_request *
+usb_ep_alloc_request (struct usb_ep *ep, int gfp_flags)
+{
+	return ep->ops->alloc_request (ep, gfp_flags);
+}
+
+/**
+ * usb_ep_free_request - frees a request object
+ * @ep:the endpoint associated with the request
+ * @req:the request being freed
+ *
+ * Reverses the effect of usb_ep_alloc_request().
+ * Caller guarantees the request is not queued, and that it will
+ * no longer be requeued (or otherwise used).
+ */
+static inline void
+usb_ep_free_request (struct usb_ep *ep, struct usb_request *req)
+{
+	ep->ops->free_request (ep, req);
+}
+
+/**
+ * usb_ep_alloc_buffer - allocate an I/O buffer
+ * @ep:the endpoint associated with the buffer
+ * @len:length of the desired buffer
+ * @dma:pointer to the buffer's DMA address; must be valid
+ * @gfp_flags:GFP_* flags to use
+ *
+ * Returns a new buffer, or null if one could not be allocated.
+ * The buffer is suitably aligned for dma, if that endpoint uses DMA,
+ * and the caller won't have to care about dma-inconsistency
+ * or any hidden "bounce buffer" mechanism.  No additional per-request
+ * DMA mapping will be required for such buffers.
+ * Free it later with usb_ep_free_buffer().
+ *
+ * You don't need to use this call to allocate I/O buffers unless you
+ * want to make sure drivers don't incur costs for such "bounce buffer"
+ * copies or per-request DMA mappings.
+ */
+static inline void *
+usb_ep_alloc_buffer (struct usb_ep *ep, unsigned len, dma_addr_t *dma,
+	int gfp_flags)
+{
+	return ep->ops->alloc_buffer (ep, len, dma, gfp_flags);
+}
+
+/**
+ * usb_ep_free_buffer - frees an i/o buffer
+ * @ep:the endpoint associated with the buffer
+ * @buf:CPU view address of the buffer
+ * @dma:the buffer's DMA address
+ * @len:length of the buffer
+ *
+ * reverses the effect of usb_ep_alloc_buffer().
+ * caller guarantees the buffer will no longer be accessed
+ */
+static inline void
+usb_ep_free_buffer (struct usb_ep *ep, void *buf, dma_addr_t dma, unsigned len)
+{
+	ep->ops->free_buffer (ep, buf, dma, len);
+}
+
+/**
+ * usb_ep_queue - queues (submits) an I/O request to an endpoint.
+ * @ep:the endpoint associated with the request
+ * @req:the request being submitted
+ * @gfp_flags: GFP_* flags to use in case the lower level driver couldn't
+ * 	pre-allocate all necessary memory with the request.
+ *
+ * This tells the device controller to perform the specified request through
+ * that endpoint (reading or writing a buffer).  When the request completes,
+ * including being canceled by usb_ep_dequeue(), the request's completion
+ * routine is called to return the request to the driver.  Any endpoint
+ * (except control endpoints like ep0) may have more than one transfer
+ * request queued; they complete in FIFO order.  Once a gadget driver
+ * submits a request, that request may not be examined or modified until it
+ * is given back to that driver through the completion callback.
+ *
+ * Each request is turned into one or more packets.  The controller driver
+ * never merges adjacent requests into the same packet.  OUT transfers
+ * will sometimes use data that's already buffered in the hardware.
+ *
+ * Bulk endpoints can queue any amount of data; the transfer is packetized
+ * automatically.  The last packet will be short if the request doesn't fill it
+ * out completely.  Zero length packets (ZLPs) should be avoided in portable
+ * protocols since not all usb hardware can successfully handle zero length
+ * packets.  (ZLPs may be explicitly written, and may be implicitly written if
+ * the request 'zero' flag is set.)  Bulk endpoints may also be used
+ * for interrupt transfers; but the reverse is not true, and some endpoints
+ * won't support every interrupt transfer.  (Such as 768 byte packets.)
+ *
+ * Interrupt-only endpoints are less functional than bulk endpoints, for
+ * example by not supporting queueing or not handling buffers that are
+ * larger than the endpoint's maxpacket size.  They may also treat data
+ * toggle differently.
+ *
+ * Control endpoints ... after getting a setup() callback, the driver queues
+ * one response (even if it would be zero length).  That enables the
+ * status ack, after transfering data as specified in the response.  Setup
+ * functions may return negative error codes to generate protocol stalls.
+ * (Note that some USB device controllers disallow protocol stall responses
+ * in some cases.)  When control responses are deferred (the response is
+ * written after the setup callback returns), then usb_ep_set_halt() may be
+ * used on ep0 to trigger protocol stalls.
+ *
+ * For periodic endpoints, like interrupt or isochronous ones, the usb host
+ * arranges to poll once per interval, and the gadget driver usually will
+ * have queued some data to transfer at that time.
+ *
+ * Returns zero, or a negative error code.  Endpoints that are not enabled
+ * report errors; errors will also be
+ * reported when the usb peripheral is disconnected.
+ */
+static inline int
+usb_ep_queue (struct usb_ep *ep, struct usb_request *req, int gfp_flags)
+{
+	return ep->ops->queue (ep, req, gfp_flags);
+}
+
+/**
+ * usb_ep_dequeue - dequeues (cancels, unlinks) an I/O request from an endpoint
+ * @ep:the endpoint associated with the request
+ * @req:the request being canceled
+ *
+ * if the request is still active on the endpoint, it is dequeued and its
+ * completion routine is called (with status -ECONNRESET); else a negative
+ * error code is returned.
+ *
+ * note that some hardware can't clear out write fifos (to unlink the request
+ * at the head of the queue) except as part of disconnecting from usb.  such
+ * restrictions prevent drivers from supporting configuration changes,
+ * even to configuration zero (a "chapter 9" requirement).
+ */
+static inline int usb_ep_dequeue (struct usb_ep *ep, struct usb_request *req)
+{
+	return ep->ops->dequeue (ep, req);
+}
+
+/**
+ * usb_ep_set_halt - sets the endpoint halt feature.
+ * @ep: the non-isochronous endpoint being stalled
+ *
+ * Use this to stall an endpoint, perhaps as an error report.
+ * Except for control endpoints,
+ * the endpoint stays halted (will not stream any data) until the host
+ * clears this feature; drivers may need to empty the endpoint's request
+ * queue first, to make sure no inappropriate transfers happen.
+ *
+ * Note that while an endpoint CLEAR_FEATURE will be invisible to the
+ * gadget driver, a SET_INTERFACE will not be.  To reset endpoints for the
+ * current altsetting, see usb_ep_clear_halt().  When switching altsettings,
+ * it's simplest to use usb_ep_enable() or usb_ep_disable() for the endpoints.
+ *
+ * Returns zero, or a negative error code.  On success, this call sets
+ * underlying hardware state that blocks data transfers.
+ */
+static inline int
+usb_ep_set_halt (struct usb_ep *ep)
+{
+	return ep->ops->set_halt (ep, 1);
+}
+
+/**
+ * usb_ep_clear_halt - clears endpoint halt, and resets toggle
+ * @ep:the bulk or interrupt endpoint being reset
+ *
+ * Use this when responding to the standard usb "set interface" request,
+ * for endpoints that aren't reconfigured, after clearing any other state
+ * in the endpoint's i/o queue.
+ *
+ * Returns zero, or a negative error code.  On success, this call clears
+ * the underlying hardware state reflecting endpoint halt and data toggle.
+ * Note that some hardware can't support this request (like pxa2xx_udc),
+ * and accordingly can't correctly implement interface altsettings.
+ */
+static inline int
+usb_ep_clear_halt (struct usb_ep *ep)
+{
+	return ep->ops->set_halt (ep, 0);
+}
+
+/**
+ * usb_ep_fifo_status - returns number of bytes in fifo, or error
+ * @ep: the endpoint whose fifo status is being checked.
+ *
+ * FIFO endpoints may have "unclaimed data" in them in certain cases,
+ * such as after aborted transfers.  Hosts may not have collected all
+ * the IN data written by the gadget driver, as reported by a request
+ * completion.  The gadget driver may not have collected all the data
+ * written OUT to it by the host.  Drivers that need precise handling for
+ * fault reporting or recovery may need to use this call.
+ *
+ * This returns the number of such bytes in the fifo, or a negative
+ * errno if the endpoint doesn't use a FIFO or doesn't support such
+ * precise handling.
+ */
+static inline int
+usb_ep_fifo_status (struct usb_ep *ep)
+{
+	if (ep->ops->fifo_status)
+		return ep->ops->fifo_status (ep);
+	else
+		return -EOPNOTSUPP;
+}
+
+/**
+ * usb_ep_fifo_flush - flushes contents of a fifo
+ * @ep: the endpoint whose fifo is being flushed.
+ *
+ * This call may be used to flush the "unclaimed data" that may exist in
+ * an endpoint fifo after abnormal transaction terminations.  The call
+ * must never be used except when endpoint is not being used for any
+ * protocol translation.
+ */
+static inline void
+usb_ep_fifo_flush (struct usb_ep *ep)
+{
+	if (ep->ops->fifo_flush)
+		ep->ops->fifo_flush (ep);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+struct usb_gadget;
+
+/* the rest of the api to the controller hardware: device operations,
+ * which don't involve endpoints (or i/o).
+ */
+struct usb_gadget_ops {
+	int	(*get_frame)(struct usb_gadget *);
+	int	(*wakeup)(struct usb_gadget *);
+	int	(*set_selfpowered) (struct usb_gadget *, int value);
+	int	(*ioctl)(struct usb_gadget *,
+				unsigned code, unsigned long param);
+};
+
+/**
+ * struct usb_gadget - represents a usb slave device
+ * @ep0: Endpoint zero, used when reading or writing responses to
+ * 	driver setup() requests
+ * @ep_list: List of other endpoints supported by the device.
+ * @speed: Speed of current connection to USB host.
+ * @name: Identifies the controller hardware type.  Used in diagnostics
+ * 	and sometimes configuration.
+ *
+ * Gadgets have a mostly-portable "gadget driver" implementing device
+ * functions, handling all usb configurations and interfaces.  They
+ * also have a hardware-specific driver (accessed through ops vectors),
+ * which insulates the gadget driver from hardware details and packages
+ * the hardware endpoints through generic i/o queues.
+ *
+ * Except for the driver data, all fields in this structure are
+ * read-only to the gadget driver.  That driver data is part of the
+ * "driver model" infrastructure in 2.5 (and later) kernels, and for
+ * earlier systems is grouped in a similar structure that's not known
+ * to the rest of the kernel.
+ */
+struct usb_gadget {
+	/* readonly to gadget driver */
+	const struct usb_gadget_ops	*ops;
+	struct usb_ep			*ep0;
+	struct list_head		ep_list;	/* of usb_ep */
+	enum usb_device_speed		speed;
+	const char			*name;
+
+	struct __gadget_device {
+		const char		*bus_id;
+		void			*driver_data;
+	} dev;
+};
+
+static inline void set_gadget_data (struct usb_gadget *gadget, void *data)
+	{ gadget->dev.driver_data = data; }
+static inline void *get_gadget_data (struct usb_gadget *gadget)
+	{ return gadget->dev.driver_data; }
+
+
+/* iterates the non-control endpoints; 'tmp' is a struct usb_ep pointer */
+#define gadget_for_each_ep(tmp,gadget) \
+	list_for_each_entry(tmp, &(gadget)->ep_list, ep_list)
+
+#ifndef list_for_each_entry
+/* not available in 2.4.18 */
+#define list_for_each_entry(pos, head, member)				\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		     prefetch(pos->member.next);			\
+	     &pos->member != (head); 					\
+	     pos = list_entry(pos->member.next, typeof(*pos), member),	\
+		     prefetch(pos->member.next))
+#endif
+
+
+/**
+ * usb_gadget_frame_number - returns the current frame number
+ * @gadget: controller that reports the frame number
+ *
+ * Returns the usb frame number, normally eleven bits from a SOF packet,
+ * or negative errno if this device doesn't support this capability.
+ */
+static inline int usb_gadget_frame_number (struct usb_gadget *gadget)
+{
+	return gadget->ops->get_frame (gadget);
+}
+
+/**
+ * usb_gadget_wakeup - tries to wake up the host connected to this gadget
+ * @gadget: controller used to wake up the host
+ *
+ * Returns zero on success, else negative error code if the hardware
+ * doesn't support such attempts, or its support has not been enabled
+ * by the usb host.  Drivers must return device descriptors that report
+ * their ability to support this, or hosts won't enable it.
+ */
+static inline int usb_gadget_wakeup (struct usb_gadget *gadget)
+{
+	if (!gadget->ops->wakeup)
+		return -EOPNOTSUPP;
+	return gadget->ops->wakeup (gadget);
+}
+
+/**
+ * usb_gadget_set_selfpowered - sets the device selfpowered feature.
+ * @gadget:the device being declared as self-powered
+ *
+ * this affects the device status reported by the hardware driver
+ * to reflect that it now has a local power supply.
+ *
+ * returns zero on success, else negative errno.
+ */
+static inline int
+usb_gadget_set_selfpowered (struct usb_gadget *gadget)
+{
+	if (!gadget->ops->set_selfpowered)
+		return -EOPNOTSUPP;
+	return gadget->ops->set_selfpowered (gadget, 1);
+}
+
+/**
+ * usb_gadget_clear_selfpowered - clear the device selfpowered feature.
+ * @gadget:the device being declared as bus-powered
+ *
+ * this affects the device status reported by the hardware driver.
+ * some hardware may not support bus-powered operation, in which
+ * case this feature's value can never change.
+ *
+ * returns zero on success, else negative errno.
+ */
+static inline int
+usb_gadget_clear_selfpowered (struct usb_gadget *gadget)
+{
+	if (!gadget->ops->set_selfpowered)
+		return -EOPNOTSUPP;
+	return gadget->ops->set_selfpowered (gadget, 0);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * struct usb_gadget_driver - driver for usb 'slave' devices
+ * @function: String describing the gadget's function
+ * @speed: Highest speed the driver handles.
+ * @bind: Invoked when the driver is bound to a gadget, usually
+ * 	after registering the driver.
+ * 	At that point, ep0 is fully initialized, and ep_list holds
+ * 	the currently-available endpoints.
+ * 	Called in a context that permits sleeping.
+ * @setup: Invoked for ep0 control requests that aren't handled by
+ * 	the hardware level driver. Most calls must be handled by
+ * 	the gadget driver, including descriptor and configuration
+ * 	management.  The 16 bit members of the setup data are in
+ * 	cpu order. Called in_interrupt; this may not sleep.  Driver
+ *	queues a response to ep0, or returns negative to stall.
+ * @disconnect: Invoked after all transfers have been stopped,
+ * 	when the host is disconnected.  May be called in_interrupt; this
+ * 	may not sleep.  Some devices can't detect disconnect, so this might
+ *	not be called except as part of controller shutdown.
+ * @unbind: Invoked when the driver is unbound from a gadget,
+ * 	usually from rmmod (after a disconnect is reported).
+ * 	Called in a context that permits sleeping.
+ * @suspend: Invoked on USB suspend.  May be called in_interrupt.
+ * @resume: Invoked on USB resume.  May be called in_interrupt.
+ *
+ * Devices are disabled till a gadget driver successfully bind()s, which
+ * means the driver will handle setup() requests needed to enumerate (and
+ * meet "chapter 9" requirements) then do some useful work.
+ *
+ * Drivers use hardware-specific knowledge to configure the usb hardware.
+ * endpoint addressing is only one of several hardware characteristics that
+ * are in descriptors the ep0 implementation returns from setup() calls.
+ *
+ * Except for ep0 implementation, most driver code shouldn't need change to
+ * run on top of different usb controllers.  It'll use endpoints set up by
+ * that ep0 implementation.
+ *
+ * The usb controller driver handles a few standard usb requests.  Those
+ * include set_address, and feature flags for devices, interfaces, and
+ * endpoints (the get_status, set_feature, and clear_feature requests).
+ *
+ * Accordingly, the driver's setup() callback must always implement all
+ * get_descriptor requests, returning at least a device descriptor and
+ * a configuration descriptor.  Drivers must make sure the endpoint
+ * descriptors match any hardware constraints. Some hardware also constrains
+ * other descriptors. (The pxa250 allows only configurations 1, 2, or 3).
+ *
+ * The driver's setup() callback must also implement set_configuration,
+ * and should also implement set_interface, get_configuration, and
+ * get_interface.  Setting a configuration (or interface) is where
+ * endpoints should be activated or (config 0) shut down.
+ *
+ * (Note that only the default control endpoint is supported.  Neither
+ * hosts nor devices generally support control traffic except to ep0.)
+ *
+ * Most devices will ignore USB suspend/resume operations, and so will
+ * not provide those callbacks.  However, some may need to change modes
+ * when the host is not longer directing those activities.  For example,
+ * local controls (buttons, dials, etc) may need to be re-enabled since
+ * the (remote) host can't do that any longer; or an error state might
+ * be cleared, to make the device behave identically whether or not
+ * power is maintained.
+ */
+struct usb_gadget_driver {
+	char			*function;
+	enum usb_device_speed	speed;
+	int			(*bind)(struct usb_gadget *);
+	void			(*unbind)(struct usb_gadget *);
+	int			(*setup)(struct usb_gadget *,
+					const struct usb_ctrlrequest *);
+	void			(*disconnect)(struct usb_gadget *);
+	void			(*suspend)(struct usb_gadget *);
+	void			(*resume)(struct usb_gadget *);
+
+	// FIXME support safe rmmod
+	struct __gadget_driver {
+		const char	*name;
+		void		*driver_data;
+	} driver;
+};
+
+
+
+/*-------------------------------------------------------------------------*/
+
+/* driver modules register and unregister, as usual.
+ * these calls must be made in a context that can sleep.
+ *
+ * these will usually be implemented directly by the hardware-dependent
+ * usb bus interface driver, which will only support a single driver.
+ */
+
+/**
+ * usb_gadget_register_driver - register a gadget driver
+ * @driver:the driver being registered
+ *
+ * Call this in your gadget driver's module initialization function,
+ * to tell the underlying usb controller driver about your driver.
+ * The driver's bind() function will be called to bind it to a
+ * gadget.  This function must be called in a context that can sleep.
+ */
+int usb_gadget_register_driver (struct usb_gadget_driver *driver);
+
+/**
+ * usb_gadget_unregister_driver - unregister a gadget driver
+ * @driver:the driver being unregistered
+ *
+ * Call this in your gadget driver's module cleanup function,
+ * to tell the underlying usb controller that your driver is
+ * going away.  If the controller is connected to a USB host,
+ * it will first disconnect().  The driver is also requested
+ * to unbind() and clean up any device state, before this procedure
+ * finally returns.
+ * This function must be called in a context that can sleep.
+ */
+int usb_gadget_unregister_driver (struct usb_gadget_driver *driver);
+
+/*-------------------------------------------------------------------------*/
+
+/* utility to simplify dealing with string descriptors */
+
+/**
+ * struct usb_string - wraps a C string and its USB id
+ * @id:the (nonzero) ID for this string
+ * @s:the string, in ISO-8859/1 characters
+ *
+ * If you're using usb_gadget_get_string(), use this to wrap a string
+ * together with its ID.
+ */
+struct usb_string {
+	u8			id;
+	const char		*s;
+};
+
+/**
+ * struct usb_gadget_strings - a set of USB strings in a given language
+ * @language:identifies the strings' language (0x0409 for en-us)
+ * @strings:array of strings with their ids
+ *
+ * If you're using usb_gadget_get_string(), use this to wrap all the
+ * strings for a given language.
+ */
+struct usb_gadget_strings {
+	u16			language;	/* 0x0409 for en-us */
+	struct usb_string	*strings;
+};
+
+/* put descriptor for string with that id into buf (buflen >= 256) */
+int usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf);
+
+
+#endif  /* __KERNEL__ */
+
+#endif	/* __LINUX_USB_GADGET_H */
