/home/sl/USB/PB1500/work/usbd-gpl/kits/amd-pb1x00-kit/amd-pb1x00-linux-2.4.21
diff -Nru a/arch/mips/config-shared.in b/arch/mips/config-shared.in
--- a/arch/mips/config-shared.in	Fri Feb 27 14:22:51 2004
+++ b/arch/mips/config-shared.in	Fri Feb 27 14:22:51 2004
@@ -985,6 +985,7 @@
 endmenu
 
 source drivers/usb/Config.in
+source drivers/usbd/Config.in
 
 source net/bluetooth/Config.in
 
--- a/drivers/Makefile	2005-08-15 18:51:50.332030952 +0200
+++ b/drivers/Makefile	2005-08-15 18:53:08.938081016 +0200
@@ -41,6 +41,7 @@
 subdir-$(CONFIG_ISDN_BOOL)	+= isdn
 subdir-$(CONFIG_ATM)		+= atm
 subdir-$(CONFIG_FC4)		+= fc4
+subdir-$(CONFIG_USBD)		+= usbd
 
 # CONFIG_HAMRADIO can be set without CONFIG_NETDEVICE being set  -- ch
 subdir-$(CONFIG_HAMRADIO)	+= net/hamradio
diff -Nru a/drivers/usbd/Config.in b/drivers/usbd/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/Config.in	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,58 @@
+#
+# USB device configuration from the device viewpoint (e.g. Linux running inside a USB device, not as host)
+#
+# Copyright (c) 2002-2003 Belcarra
+#
+#
+
+mainmenu_option next_comment
+
+comment 'USB clients (devices, not hosts)'
+
+tristate 'Support for USB Clients (USB Device, not USB Host)' CONFIG_USBD
+
+if [ "$CONFIG_USBD" = "y" -o "$CONFIG_USBD" = "m" ]; then
+   comment ''
+   bool   '   Enable High Speed Descriptors' CONFIG_USBD_HIGH_SPEED
+
+   bool   '   Do Not Use Serial Number in Device Descriptor (Default is no' CONFIG_USBD_NO_SERIAL_NUMBER
+   if [ "$CONFIG_USBD_NO_SERIAL_NUMBER" != "y" ]; then
+      string '  Default Serial number (string)' CONFIG_USBD_SERIAL_NUMBER_STR ""
+   fi
+
+   int '   Max Power (mA) (Default is zero, whick is self powered)' CONFIG_USBD_MAXPOWER "0"
+
+   comment ''
+
+   bool   '  USBD Proc FS' CONFIG_USBD_PROCFS
+
+   tristate   '  USBD Proc FS Module' CONFIG_USBD_PROCFSM $CONFIG_USBD
+
+   comment 'Function Drivers'
+
+   source drivers/usbd/network_fd/Config.in
+   source drivers/usbd/acm_fd/Config.in
+
+   source drivers/usbd/mouse_fd/Config.in
+
+   comment 'Bus Interface'
+
+   source drivers/usbd/au1x00_bi/Config.in
+
+   bool '  USB Device Register Tracing' CONFIG_USBD_BI_REGISTER_TRACE
+   if [ "$CONFIG_USBD" = "y" ]; then
+      bool '  USB Device Manual Enable' CONFIG_USBD_BI_DELAY_ENABLE
+   fi
+
+
+   #source drivers/usbd/l7205_bi/Config.in
+   #source drivers/usbd/sl11_bi/Config.in
+
+   #source drivers/usbd/cdc_fd/Config.in
+   #source drivers/usbd/serialnumber/Config.in
+   #source drivers/usbd/audio_fd/Config.in
+   #source drivers/usbd/eg_fd/Config.in
+   #source drivers/usbd/keyboard_fd/Config.in
+fi
+
+endmenu
diff -Nru a/drivers/usbd/Makefile b/drivers/usbd/Makefile
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/Makefile	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,157 @@
+#
+# Makefile for the kernel USBD (device not host) drivers.
+#
+# Copyright (c) 2002 Belcarra
+# Copyright (C) 2001 Lineo, Inc.
+# Copyright (C) 2001 Hewlett-Packard Co.
+
+# Subdirs.
+# This is a bit complex, because some subdirs are for
+# proprietary code, and are simply not present in a
+# general distribution.
+
+TOPDIR ?= ../../..
+
+# The all-CAPS *_DIRS get nuked in the new versions
+# of Rules.make, so use only the subdir-* methods.
+subdir-y 	:= 
+subdir-m 	:=
+subdir-n 	:=
+subdir- 	:=
+
+# Function Drivers
+subdir-$(CONFIG_USBD_ACM) += acm_fd			
+subdir-$(CONFIG_USBD_MOUSE) += mouse_fd			
+subdir-$(CONFIG_USBD_NETWORK) += network_fd		
+
+#subdir-$(CONFIG_USBD_AUDIO) += audio_fd			
+#subdir-$(CONFIG_USBD_EG) += eg_fd			
+#subdir-$(CONFIG_USBD_KEYBOARD) += keyboard_fd			
+#subdir-$(CONFIG_USBD_CDC) += cdc_fd		
+
+# Bus Interface Drivers
+subdir-$(CONFIG_USBD_AU1X00_BUS) += au1x00_bi		
+
+#subdir-$(CONFIG_USBD_OMAP_BUS) += omap_bi			
+#subdir-$(CONFIG_USBD_TC86C001_BUS) += tc86c001_bi			
+
+#subdir-$(CONFIG_USBD_SL11_BUS) += sl11_bi		
+
+# The target object and module list name.
+
+O_TARGET	:= usbdev.o
+
+# Objects that export symbols.
+
+export-objs	:= usbd.o usbd-bops.o usbd-fops.o usbd-bi.o ep0.o
+
+# Multipart objects.
+
+list-multi		:= usbdcore.o 
+usbdcore-objs		:= usbd.o ep0.o usbd-fops.o usbd-bops.o 
+usbdprocfs-objs		:= usbd-procfs.o
+
+
+# Optional parts of multipart objects.
+
+# Object file lists.
+
+obj-y		:=
+obj-m		:=
+obj-n		:=
+obj-		:=
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_USBD)		+= usbdcore.o 
+obj-$(CONFIG_USBD_PROCFSM)	+= usbdprocfs.o 
+
+# Object files in subdirectories
+
+#
+obj-$(CONFIG_USBD_MONITOR) += monitor/monitor.o
+
+obj-$(CONFIG_USBD_ACM) += acm_fd/acm_fd.o
+obj-$(CONFIG_USBD_MOUSE) += mouse_fd/mouse_fd.o
+obj-$(CONFIG_USBD_NETWORK) += network_fd/network_fd.o
+
+#obj-$(CONFIG_USBD_AUDIO) += audio_fd/audio_fd.o
+#obj-$(CONFIG_USBD_EG) += eg_fd/eg_fd.o
+#obj-$(CONFIG_USBD_KEYBOARD) += keyboard_fd/keyboard_fd.o
+
+# Bus Interface Drivers
+obj-$(CONFIG_USBD_AU1X00_BUS) += au1x00_bi/au1x00_bi.o
+
+#obj-$(CONFIG_USBD_OMAP_BUS) += omap_bi/omap_bi.o
+#obj-$(CONFIG_USBD_TC86C001_BUS) += tc86c001_bi/tc86c001_bi.o
+
+#obj-$(CONFIG_USBD_SL11_BUS) += sl11_bi/sl11_bi.o
+
+
+# Yech.  This isn't the best way to do this, but there isn't a config flag
+# common to all the possible bus interfaces
+
+
+# Extract lists of the multi-part drivers.
+# The 'int-*' lists are the intermediate files used to build the multi's.
+
+multi-y		:= $(filter $(list-multi), $(obj-y))
+multi-m		:= $(filter $(list-multi), $(obj-m))
+int-y		:= $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
+int-m		:= $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
+
+# Files that are both resident and modular: remove from modular.
+
+obj-m		:= $(filter-out $(obj-y), $(obj-m))
+int-m		:= $(filter-out $(int-y), $(int-m))
+
+# Translate to Rules.make lists.
+
+O_OBJS		:= $(filter-out $(export-objs), $(obj-y))
+OX_OBJS		:= $(filter     $(export-objs), $(obj-y))
+M_OBJS		:= $(sort $(filter-out $(export-objs), $(obj-m)))
+MX_OBJS		:= $(sort $(filter     $(export-objs), $(obj-m)))
+MI_OBJS		:= $(sort $(filter-out $(export-objs), $(int-m)))
+MIX_OBJS	:= $(sort $(filter     $(export-objs), $(int-m)))
+
+# The global Rules.make.
+
+include $(TOPDIR)/Rules.make
+EXTRA_CFLAGS += -Wno-format -Wall
+
+# Link rules for multi-part drivers.
+
+usbdprocfs.o: $(usbdprocfs-objs)
+	$(LD) -r -o $@ $(usbdprocfs-objs)
+
+usbdcore.o: $(usbdcore-objs)
+	$(LD) -r -o $@ $(usbdcore-objs)
+
+# dependencies:
+
+usbd.o: usbd-build.h
+
+usbd-build.h:
+	echo "#define USBD_BUILD          \"000\"" > $@
+	
+# local
+
+%.h:%.p
+	release inc build < $< > $@
+	cp $@ $<
+
+release.h: release.p
+
+inc-build:
+	release inc build < release.p > release.h
+	cp release.h release.p
+
+
+menuconfig:
+	cd $(TOPDIR); make menuconfig
+
+xconfig:
+	cd $(TOPDIR); make xconfig
+
+
+
diff -Nru a/drivers/usbd/acm_fd/Config.in b/drivers/usbd/acm_fd/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/acm_fd/Config.in	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,27 @@
+#
+# CDC ACM Function Driver
+#
+# Copyright (C) 2003,2004 Belcarra
+#
+
+mainmenu_option next_comment
+comment "CDC ACM Function"
+
+dep_tristate '  CDC ACM Function' CONFIG_USBD_ACM $CONFIG_USBD
+if [ "$CONFIG_USBD_ACM" = "y" -o "$CONFIG_USBD_ACM" = "m" ]; then
+   hex     'VendorID (hex value)' CONFIG_USBD_ACM_VENDORID "12b9"
+   hex     'ProductID (hex value)' CONFIG_USBD_ACM_PRODUCTID "f002"
+   hex     'bcdDevice (binary-coded decimal)' CONFIG_USBD_ACM_BCDDEVICE "0100"
+
+   string 'iManufacturer (string)' CONFIG_USBD_ACM_MANUFACTURER "Belcarra"
+   string 'iProduct (string)' CONFIG_USBD_ACM_PRODUCT_NAME "Belcarra ACM Device"
+
+   string 'iConfiguration (string)' CONFIG_USBD_ACM_DESC "Acm Cfg"
+   string 'Comm Interface iInterface (string)' CONFIG_USBD_ACM_COMM_INTF "Comm Intf"
+   string 'Data Interface iInterface (string)' CONFIG_USBD_ACM_DATA_INTF "Data Intf"
+
+   bool    '  ACM Tracing' CONFIG_USBD_ACM_TRACE
+   comment ''
+fi
+
+endmenu
diff -Nru a/drivers/usbd/acm_fd/Makefile b/drivers/usbd/acm_fd/Makefile
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/acm_fd/Makefile	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,65 @@
+#
+# Function driver for a CDC ACM USB Device
+#
+# Copyright (c) 2003 Belcarra
+
+# Multipart objects.
+
+O_TARGET	:= acm_fd.o
+list-multi	:= acm_fd.o 
+
+acm_fd-objs	:= acm.o trace.o
+
+# Objects that export symbols.
+export-objs	:= acm.o
+
+# Object file lists.
+
+obj-y	:=
+obj-m	:=
+obj-n	:=
+obj-	:=
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_USBD_ACM)   += acm_fd.o
+
+# Extract lists of the multi-part drivers.
+# The 'int-*' lists are the intermediate files used to build the multi's.
+
+multi-y		:= $(filter $(list-multi), $(obj-y))
+multi-m		:= $(filter $(list-multi), $(obj-m))
+int-y		:= $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
+int-m		:= $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
+
+# Files that are both resident and modular: remove from modular.
+
+obj-m		:= $(filter-out $(obj-y), $(obj-m))
+int-m		:= $(filter-out $(int-y), $(int-m))
+
+# Translate to Rules.make lists.
+
+O_OBJS		:= $(filter-out $(export-objs), $(obj-y))
+OX_OBJS		:= $(filter     $(export-objs), $(obj-y))
+M_OBJS		:= $(sort $(filter-out $(export-objs), $(obj-m)))
+MX_OBJS		:= $(sort $(filter     $(export-objs), $(obj-m)))
+MI_OBJS		:= $(sort $(filter-out $(export-objs), $(int-m)))
+MIX_OBJS	:= $(sort $(filter     $(export-objs), $(int-m)))
+
+# The global Rules.make.
+
+USBD=$(TOPDIR)/drivers/usbd
+ACMD=$(USBD)/acm_fd
+include $(TOPDIR)/Rules.make
+EXTRA_CFLAGS += -I$(ACMD) -I$(USBD) -Wno-unused -Wno-format 
+EXTRA_CFLAGS_nostdinc += -I$(ACMD) -I$(USBD) -Wno-unused -Wno-format 
+
+# Link rules for multi-part drivers.
+
+acm_fd.o: $(acm_fd-objs)
+	$(LD) -r -o $@ $(acm_fd-objs)
+
+# dependencies:
+
+acm.o: $(USBD)/usbd.h $(USBD)/usbd-bus.h $(USBD)/usbd-func.h
+
diff -Nru a/drivers/usbd/acm_fd/acm.c b/drivers/usbd/acm_fd/acm.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/acm_fd/acm.c	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,1662 @@
+/*
+ * usbd/acm_fd/acm.c
+ *
+ *      Copyright (c) 2003, 2004 Belcarra
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * Note: this function driver requires the following endpoints:
+ *
+ *      BULK-IN
+ *      BULK-OUT
+ *      INTERRUPT-IN
+ *
+ * This function driver cannot be used on devices (such as the StrongArm
+ * SA1100) that do not have and interrupt endpoint.
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+
+#include <usbd-export.h>
+#include <usbd-build.h>
+
+MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com");
+
+MODULE_DESCRIPTION ("Belcarra CDC-ACM Function");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17)
+MODULE_LICENSE("GPL");
+#endif
+
+#define ACM_TRACE_NAME "acm_trace"
+
+#undef USE_TICKER
+#undef MCEL
+//#define MCEL 1
+//#define CONFIG_USBD_ACM_DATALOG 1
+#undef CONFIG_USBD_ACM_DATALOG
+#undef PST_FD_AVAILABLE
+
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <asm/atomic.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+
+#include <usbd-chap9.h>
+#include <usbd-mem.h>
+#include <usbd.h>
+#include <usbd-func.h>
+
+#include "trace.h"
+// Define the low order 16 bits of an urb's memory address as it's ID for tracing.
+#define urbID(urb) (0xffff & (u32) (void *) urb)
+int acm_interrupts;
+
+USBD_MODULE_INFO ("acm_fd 2.1-beta");
+
+#define MAX_QUEUED_BYTES        256
+#define MAX_QUEUED_URBS         10     // Max for write
+
+#define MAX_RECV_URBS           2      // Max for receiving data
+#define RECV_RING_SIZE          (MAX_RECV_URBS+1)
+
+// Endpoint indexes in acm_endpoint_requests[] and the endpoint map.
+#define BULK_OUT        0x00
+#define BULK_IN         0x01
+#define INT_IN          0x02
+#if !defined(CONFIG_USBD_ACM_DATALOG)
+#define ENDPOINTS       0x03
+#else
+#define DATALOG_BULK_IN 0x03
+#define ENDPOINTS       0x04
+#endif
+
+#define COMM_INTF       0x00
+#define DATA_INTF       0x01
+#if defined(CONFIG_USBD_ACM_DATALOG)
+#define DATALOG_INTF    0x02
+#define TESTCMD_INTF    0x03
+#endif
+
+#if defined(CONFIG_USBD_ACM_DATALOG) && defined(PST_FD_AVAILABLE)
+extern int pst_dev_create(struct usb_device_instance *device);
+extern void pst_dev_destroy(void);
+extern void ep0_process_vendor_request( struct urb *urb );
+extern int pst_urb_sent (struct urb *urb, int rc);
+#endif
+
+/* Module Parameters ************************************************************************* */
+
+static u32 vendor_id;
+static u32 product_id;
+static u32 max_queued_urbs = MAX_QUEUED_URBS;
+static u32 max_queued_bytes = MAX_QUEUED_BYTES;
+
+MODULE_PARM (vendor_id, "i");
+MODULE_PARM (product_id, "i");
+MODULE_PARM (max_queued_urbs, "i");
+MODULE_PARM (max_queued_bytes, "i");
+
+MODULE_PARM_DESC (vendor_id, "Device Vendor ID");
+MODULE_PARM_DESC (product_id, "Device Product ID");
+MODULE_PARM_DESC (max_queued_urbs, "Maximum TX Queued Urbs");
+MODULE_PARM_DESC (max_queued_bytes, "Maximum TX Queued Bytes");
+
+/*
+ * CDC ACM Configuration
+ *
+ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions
+ */
+
+/* Endpoints */
+static __u8 acm_alt_1[] = { 0x07, USB_DT_ENDPOINT, OUT, BULK,      0, 0x00, 0x00, };
+static __u8 acm_alt_2[] = { 0x07, USB_DT_ENDPOINT, IN,  BULK,     0,  0x00, 0x00, };
+static struct usb_endpoint_descriptor *acm_alt_endpoints[] = { 
+        (struct usb_endpoint_descriptor *) acm_alt_1, 
+        (struct usb_endpoint_descriptor *) acm_alt_2, };
+u8 acm_alt_indexes[] = { BULK_OUT, BULK_IN, };
+
+static __u8 acm_comm_1[] = { 0x07, USB_DT_ENDPOINT, IN,  INTERRUPT, 0, 0x00, 0x0a, };
+static struct usb_endpoint_descriptor *acm_comm_endpoints[] = { (struct usb_endpoint_descriptor *) acm_comm_1 };
+u8 acm_comm_indexes[] = { INT_IN, };
+
+static __u8 cdc_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x01, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_VERSION */ };
+static __u8 cdc_class_2[] = { 0x05, CS_INTERFACE, USB_ST_CMF, 0x03, 0x01, /* bMasterInterface: 0, bSlaveInterface: 1 */ };
+static __u8 cdc_class_3[] = { 0x05, CS_INTERFACE, USB_ST_UF, 0x00, 0x01, /* bMasterInterface: 0, bSlaveInterface: 1 */ };
+
+/* ACMF - c.f. Table 28
+ * currenty set to 0x2 - Support Set_Line_Coding etc, 
+ *
+ * XXX Should we also set 0x4 - Supports Network_Notification?
+ */
+static __u8 cdc_class_4[] = { 0x04, CS_INTERFACE, USB_ST_ACMF, 0x02, };
+
+static struct usb_generic_class_descriptor *cdc_comm_class_descriptors[] = 
+        { (struct usb_generic_class_descriptor *) cdc_class_1, 
+        (struct usb_generic_class_descriptor *) cdc_class_2, 
+        (struct usb_generic_class_descriptor *) cdc_class_3, 
+        (struct usb_generic_class_descriptor *) cdc_class_4, };
+
+
+#if defined(CONFIG_USBD_ACM_DATALOG)
+
+#if 0
+/* This is used to get a specific INTERFACE number, by padding the list of interfaces. */
+static struct usb_alternate_description dummy_alternate_descriptions[] = {
+    { iInterface:"Dummy", },
+};
+#endif
+
+/* There is no way to request a specific ENDPOINT address, since that depends on
+   the bus interface hardware. */
+//#define DATA_LOG_IN_ENDPOINT       11   
+
+static __u8 datalog_endpoint[] = {
+        0x07,                      // bLength
+        USB_DT_ENDPOINT,           // bDescriptorType   // 0x5
+        /*DATA_LOG_IN_ENDPOINT |*/ IN, // bEndpointAddress
+        BULK,                      // bmAttributes
+        0, 0x00,                       // wMaxPacketSize 
+        0x00,                      // bInterval
+};
+static struct usb_endpoint_descriptor *datalog_endpoints[] = { datalog_endpoint };
+u8 datalog_indexes[] = { DATALOG_BULK_IN, };
+
+
+// No endpoints needed for TestCmd interface.
+#endif
+
+/* Alternate Descriptors */
+// First two bytes are identical in all: bLength, bDescriptorType (0x09 0x04)
+static __u8 cdc_comm_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = {
+        0x09, USB_DT_INTERFACE, COMM_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints
+        COMMUNICATIONS_INTERFACE_CLASS, COMMUNICATIONS_ACM_SUBCLASS, 0x01, 0x00, };
+
+static __u8 cdc_data_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = {
+        0x09, USB_DT_INTERFACE, DATA_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints
+        DATA_INTERFACE_CLASS, COMMUNICATIONS_NO_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00, };
+
+
+#if defined(CONFIG_USBD_ACM_DATALOG)
+
+static __u8 datalog_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = {
+        0x09, USB_DT_INTERFACE, DATALOG_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints
+        // bInterfaceClass, bInterfaceSubClass, bInterfaceProtocol, iInterface
+        0xFF,               0x02,               0xFF,               0x00, };
+
+static __u8 testcmd_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = {
+        0x09, USB_DT_INTERFACE, TESTCMD_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints
+        // bInterfaceClass, bInterfaceSubClass, bInterfaceProtocol, iInterface
+        0xFF,               0x03,               0xFF,               0x00, };
+
+#endif
+
+/* Alternate Descriptions */
+static struct usb_alternate_description cdc_comm_alternate_descriptions[] = {
+      { iInterface: CONFIG_USBD_ACM_COMM_INTF,
+              interface_descriptor: (struct usb_interface_descriptor *)&cdc_comm_alternate_descriptor,
+	      classes:sizeof (cdc_comm_class_descriptors) / sizeof (struct usb_generic_class_descriptor *),
+              class_list: cdc_comm_class_descriptors,
+              endpoint_list: acm_comm_endpoints,
+	      endpoints:sizeof (acm_comm_endpoints) / sizeof(struct usb_endpoint_descriptor *),
+              endpoint_indexes: acm_comm_indexes,
+      }, };
+
+static struct usb_alternate_description cdc_data_alternate_descriptions[] = {
+      { iInterface: CONFIG_USBD_ACM_DATA_INTF,
+              interface_descriptor: (struct usb_interface_descriptor *)&cdc_data_alternate_descriptor,
+              endpoint_list: acm_alt_endpoints,
+	      endpoints:sizeof (acm_alt_endpoints) / sizeof(struct usb_endpoint_descriptor *),
+              endpoint_indexes: acm_alt_indexes,
+              }, };
+
+#if defined(CONFIG_USBD_ACM_DATALOG)
+
+static struct usb_alternate_description datalog_alternate_descriptions[] = {
+      { iInterface:"Motorola MCU Data Logger",
+             interface_descriptor: (struct usb_interface_descriptor *)&datalog_alternate_descriptor,
+             endpoint_list: datalog_endpoints,
+             endpoints:sizeof (datalog_endpoints) / sizeof (struct usb_endpoint_descriptor *),
+             endpoint_indexes: datalog_indexes,
+      },
+};
+
+static struct usb_alternate_description testcmd_alternate_descriptions[] = {
+      { iInterface:"Motorola Test Command",
+                interface_descriptor: (struct usb_interface_descriptor *)&testcmd_alternate_descriptor,
+      },
+};
+#endif
+
+/* Interface Descriptions */
+static struct usb_interface_description cdc_interfaces[] = {
+      { 
+                alternates:sizeof (cdc_comm_alternate_descriptions) / sizeof (struct usb_alternate_description),
+                alternate_list:cdc_comm_alternate_descriptions,
+      },
+
+      { 
+                alternates:sizeof (cdc_data_alternate_descriptions) / sizeof (struct usb_alternate_description),
+                alternate_list:cdc_data_alternate_descriptions,
+      },
+
+#if defined(CONFIG_USBD_ACM_DATALOG)
+#if 0
+      // Space the INTERFACEs out by adding as many dummies as required (for now, none).
+      { alternate_list: dummy_alternate_descriptions, }, /* 0xN */
+#endif
+
+      { alternates:sizeof (datalog_alternate_descriptions) / sizeof (struct usb_alternate_description),
+               alternate_list:datalog_alternate_descriptions,
+      },
+
+      { alternates:sizeof (testcmd_alternate_descriptions) / sizeof (struct usb_alternate_description),
+              alternate_list:testcmd_alternate_descriptions,
+     },
+
+#endif
+
+};
+
+
+/* Configuration Descriptor and Description */
+static __u8 cdc_configuration_descriptor[sizeof(struct usb_configuration_descriptor)] = {
+        0x09, USB_DT_CONFIG, 0x00, 0x00, sizeof (cdc_interfaces) / sizeof (struct usb_interface_description),
+        0x01, 0x00, BMATTRIBUTE, BMAXPOWER, };
+
+struct usb_configuration_description acm_description[] = {
+      { iConfiguration: CONFIG_USBD_ACM_DESC,
+              configuration_descriptor: (struct usb_configuration_descriptor *)cdc_configuration_descriptor,
+	      bNumInterfaces:sizeof (cdc_interfaces) / sizeof (struct usb_interface_description),
+              interface_list:cdc_interfaces,}, };
+
+static struct usb_device_descriptor acm_device_descriptor = {
+	bLength: sizeof(struct usb_device_descriptor),
+	bDescriptorType: USB_DT_DEVICE,
+	bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
+	bDeviceClass: COMMUNICATIONS_DEVICE_CLASS,
+	bDeviceSubClass: 0x02,
+	bDeviceProtocol: 0x00,
+	bMaxPacketSize0: 0x00,
+	idVendor: __constant_cpu_to_le16(CONFIG_USBD_ACM_VENDORID),
+	idProduct: __constant_cpu_to_le16(CONFIG_USBD_ACM_PRODUCTID),
+	bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_ACM_BCDDEVICE),
+};
+
+
+static struct usb_endpoint_request acm_endpoint_requests[ENDPOINTS+1] = {
+        { 1, 1, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, 64, 512, },
+        { 1, 1, 0, USB_DIR_IN | USB_ENDPOINT_BULK, 64 /* * 4 */, 512, },
+        { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, },
+#if defined(CONFIG_USBD_ACM_DATALOG)
+        { 1, 1, 0, USB_DIR_IN | USB_ENDPOINT_BULK, 64, 512, },
+#endif
+        { 0, },
+};
+
+
+struct usb_device_description acm_device_description = {
+        device_descriptor: &acm_device_descriptor,
+	iManufacturer: CONFIG_USBD_ACM_MANUFACTURER,
+	iProduct: CONFIG_USBD_ACM_PRODUCT_NAME,
+#if !defined(CONFIG_USBD_NO_SERIAL_NUMBER) && defined(CONFIG_USBD_SERIAL_NUMBER_STR)
+	iSerialNumber: CONFIG_USBD_SERIAL_NUMBER_STR, 
+#endif
+        endpointsRequested: ENDPOINTS,
+        requestedEndpoints: acm_endpoint_requests,
+};
+
+/* Missing atomic functions ******************************************************************** */
+
+static __inline__ int atomic_post_inc(volatile atomic_t *v)
+{
+	unsigned long flags;
+	int result;
+	local_irq_save(flags);
+	result = (v->counter)++;
+	local_irq_restore(flags);
+	return(result);
+}
+
+static __inline__ int atomic_pre_dec(volatile atomic_t *v)
+{
+	unsigned long flags;
+	int result;
+	local_irq_save(flags);
+	result = --(v->counter);
+	local_irq_restore(flags);
+	return(result);
+}
+
+/* ACM ***************************************************************************************** */
+/*
+ * Output and Input control lines and line errors.
+ */
+#define LINE_OUT_DTR            0x01
+#define LINE_OUT_RTS            0x02
+#define LINE_IN_DCD             0x01
+#define LINE_IN_DSR             0x02
+#define LINE_IN_BRK             0x04
+#define LINE_IN_RI              0x08
+#define LINE_IN_FRAMING         0x10
+#define LINE_IN_PARITY          0x20
+#define LINE_IN_OVERRUN         0x40
+
+/* ******************************************************************************************* */
+
+#define ACM_TTY_MAJOR   166
+#define ACM_TTY_MINOR   0
+#define ACM_TTY_MINORS  1
+
+struct acm_private {
+        struct usb_function_instance *function;
+        struct tty_driver *tty_driver;
+        int tty_driver_registered;              // non-zero if tty_driver registered
+        int usb_driver_registered;              // non-zero if usb function registered
+
+        struct tty_struct *tty;                 // non-null if tty open
+        struct tq_struct wqueue;                // task queue for writer wakeup 
+        struct tq_struct hqueue;                // task queue for hangup
+        wait_queue_head_t open_wait;            // wait queue for blocking open
+	int open_wait_count;			// count of (possible) blocked
+        int exiting;				// True if module exiting
+
+        unsigned char throttle;                 // non-zero if we are throttled
+        unsigned char clocal;                   // non-zero if clocal set
+        unsigned char connected;                // non-zero if connected to host (configured)
+
+        unsigned int writesize;                 // packetsize * 4
+        unsigned int ctrlin;                    // line state device sends to host
+        unsigned int ctrlout;                   // line state device received from host
+
+	int exclusive;
+        atomic_t used;
+        atomic_t queued_bytes;
+        atomic_t queued_urbs;
+
+	/*TBR debug receive flow control */
+	unsigned long bytes_received;
+	unsigned long bytes_forwarded;
+	/*TBR end debug */
+
+	struct urb *recv_ring[RECV_RING_SIZE];
+	unsigned int rr_in_ndx;
+	unsigned int rr_out_ndx;
+
+        struct urb *int_urb;                    // pending interrupt urb
+};
+
+static struct acm_private acm_private;
+
+static int acm_send_int_notification(struct usb_function_instance *, int , int );
+static int acm_urb_sent_bulk (struct urb *urb, int rc);
+static int acm_urb_sent_int (struct urb *urb, int rc);
+static int acm_recv_urb (struct urb *urb, int rc);
+
+/* Serial Functions **************************************************************************** */
+
+static void acm_schedule(struct tq_struct *queue)
+{
+	TRACE_MSG1("task %p",queue);
+        RETURN_IF(!queue->data || queue->sync);
+        MOD_INC_USE_COUNT;
+        queue_task(queue, &tq_immediate);
+        mark_bh(IMMEDIATE_BH);
+	TRACE_MSG1("task %p scheduled and marked",queue);
+}
+
+                                
+static int block_until_ready(
+	struct tty_struct *tty,
+	struct file *filp,
+	struct acm_private *acm)
+{                               
+        unsigned long flags;    
+        int rc = 0;             
+                        
+        // FUTURE: check tty for non-blocking open...
+        
+        local_irq_save(flags);
+	TRACE_MSG1("owc=%d --> at entry",acm->open_wait_count);
+        acm->open_wait_count += 1;
+        for (;;) {
+                if (tty_hung_up_p(filp)) {
+			TRACE_MSG("tty_hung_up_p()");
+                        rc = -ERESTARTSYS;
+                        break;
+                }
+                if (signal_pending(current)) {
+			TRACE_MSG("signal_pending()");
+                        rc = -ERESTARTSYS;
+                        break;  
+                }               
+                if (acm->exiting) {
+			TRACE_MSG("module exiting()");
+                        rc = -ENODEV;
+                        break;  
+                }               
+                if (acm->ctrlout & LINE_OUT_DTR) { 
+                        // OK, there's somebody on the other end, let's go...
+			TRACE_MSG("found DTR");
+                        break;
+                }
+		TRACE_MSG1("owc=%d sleeping...",acm->open_wait_count);
+                interruptible_sleep_on(&acm->open_wait);
+		TRACE_MSG1("owc=%d got WAKEUP",acm->open_wait_count);
+        }
+        acm->open_wait_count -= 1;
+	TRACE_MSG1("owc=%d <-- at exit",acm->open_wait_count);
+        local_irq_restore(flags);
+        return(rc);
+}
+
+static void acm_hangup(void *private)
+{
+        //struct acm_private *acm = &acm_private;
+        struct acm_private *acm = (struct acm_private *) private;
+        struct tty_struct *tty = acm->tty;
+	TRACE_MSG("entered");
+
+        if (tty && !acm->clocal) 
+                tty_hangup(tty);
+
+        wake_up_interruptible(&acm->open_wait);
+
+        MOD_DEC_USE_COUNT;
+        //printk (KERN_INFO"%s: MOD: %d connected: %d tty: %p clocal: %x\n", 
+        //                __FUNCTION__, MOD_IN_USE, acm->connected, tty, acm->clocal);
+	TRACE_MSG("exited");
+}
+
+static int acm_tty_open(struct tty_struct *tty, struct file *filp)
+{
+        struct acm_private *acm = &acm_private;
+        //struct usb_function_instance *function = acm->function;
+        int used;
+	int nonblocking;
+        int rc = 0;
+	unsigned long flags;
+
+	TRACE_MSG2("used=%d MOD=%d",atomic_read(&acm->used),MOD_IN_USE);
+        //printk (KERN_INFO"%s: bus: %p used: %d MOD: %d\n",
+	//	 __FUNCTION__, acm->function, atomic_read(&acm->used), MOD_IN_USE);
+        nonblocking = filp->f_flags & O_NONBLOCK;
+        /* Lock and increment used counter, save current value.
+           Check for exclusive open at the same time. */
+	local_irq_save(flags);
+	/* The value of acm->used controls MOD_{INC/DEC}_USE_COUNT, so
+           it has to be incremented unconditionally, and no early return
+           made until after USE_COUNT has been adjusted to match. */
+        used = atomic_post_inc(&acm->used);
+#ifdef MCEL
+        filp->f_flags |= O_EXCL;  // QQQ Can we persuade MCEL to add this to their app?
+        if (nonblocking && !(acm->connected)) {
+		// QQQ Is MCEL actually using this "feature"?  (See below for printk)
+                rc = -EINVAL;
+        } else
+#endif
+        if (filp->f_flags & O_EXCL) {
+		/* This is intended to be an exclusive open, so
+                   make sure no one has the device open already, and
+                   set the exclusive flag so no one can open it later. */
+		if (used > 0) {
+			// Someone already has it.
+			rc = -EBUSY;
+		} else {
+			acm->exclusive = 1;
+			set_bit(TTY_EXCLUSIVE, &tty->flags);
+		}
+	} else if (acm->exclusive != 0 && !suser()) {
+		// Only the superuser can do a normal open of an O_EXCL tty
+		rc = -EBUSY;
+	}
+	local_irq_restore(flags);
+	if (0 == used) {
+		// The value before incrementing was 0, this is the first open.
+	        MOD_INC_USE_COUNT;
+	}
+	// OK, now it's safe to make an early return.
+	if (0 != rc) {
+#ifdef MCEL
+		// This can dissappear when the "feature" above does.
+		if (-EINVAL == rc)
+                	printk(KERN_INFO "\nusb cable not connected!\n");
+#endif
+		return(rc);
+	}
+	/* To truly emulate the old dual-device approach of having a non-blocking
+           device (e.g cu0) and a blocking device (e.g. tty0) we would need to
+           track blocking and non-blocking opens separately.  We don't.  This
+           may lead to funny behavior in the multiple open case. */
+	if (0 == used) {
+		// First open.
+		TRACE_MSG2("FIRST OPEN nb=%x xo=%x",nonblocking,acm->exclusive);
+	       	//printk (KERN_INFO"%s: FIRST OPEN used_%sblock: s=%d, a=%d x=%d\n",
+		//	 __FUNCTION__,(nonblocking?"non":""), used, atomic_read(&acm->used),
+		//	 acm->exclusive);
+	        tty->driver_data = acm;
+	        acm->tty = tty;
+	        tty->low_latency = 1;
+		acm->bytes_received = 0;
+		acm->bytes_forwarded = 0;
+	        acm->ctrlin = LINE_IN_DCD | LINE_IN_DSR;
+	        if (NULL != acm->function) {
+			TRACE_MSG1("sending notification ctrlin=%x",acm->ctrlin);
+	       	        //printk (KERN_INFO"%s: sending ctrlin notification\n",__FUNCTION__);
+	                rc = acm_send_int_notification(acm->function, CDC_NOTIFICATION_SERIAL_STATE,
+			                               acm->ctrlin);
+		}
+	}
+
+        if (0 == rc && !nonblocking) {
+        	// Blocking open - all callers block until DTR shows up.
+                rc = block_until_ready(tty,filp,acm);
+	}
+                
+        /* The tty layer calls acm_tty_close() even if this open fails,
+           so any cleanup (rc != 0) will be done there. */
+	TRACE_MSG2("used=%d rc=%d",atomic_read(&acm->used),rc);
+        return(rc);
+}
+
+static void acm_tty_close(struct tty_struct *tty, struct file *filp)
+{
+        struct acm_private *acm = tty->driver_data;
+        struct usb_function_instance *function = acm->function;
+        int used;
+
+	TRACE_MSG2("used=%d MOD=%d",atomic_read(&acm->used),MOD_IN_USE);
+        //printk (KERN_INFO"%s: function: %p used: %d MOD: %d\n",
+	//	 __FUNCTION__, function, atomic_read(&acm->used), MOD_IN_USE);
+
+        // lock and decrement used counter, save result
+        used = atomic_pre_dec(&acm->used);
+
+        // finished unless this is the last close
+        if (used <= 0) {
+                // This is the last close, clean up
+
+                acm->tty = NULL;
+                acm->ctrlin = 0x0;
+
+                if (acm->function) {
+                        acm_send_int_notification(function, CDC_NOTIFICATION_SERIAL_STATE, acm->ctrlin);
+                }
+                /* This should never happen if this is the last close,
+                   but it can't hurt to check. */
+                if (acm->open_wait_count) {
+                        wake_up_interruptible(&acm->open_wait);
+                }
+		if (acm->exclusive) {
+			acm->exclusive = 0;
+			clear_bit(TTY_EXCLUSIVE, &tty->flags);
+		}
+                MOD_DEC_USE_COUNT;
+		TRACE_MSG1("LAST CLOSE r-f=%d",(acm->bytes_received-acm->bytes_forwarded));
+                //printk (KERN_INFO"%s: LAST CLOSE used: %d MOD: %d bytes_received: %lu forwarded: %lu\n",
+		//	 __FUNCTION__, atomic_read(&acm->used), MOD_IN_USE, acm->bytes_received,
+		//	acm->bytes_forwarded);
+        }
+	TRACE_MSG("exited");
+}
+
+/* Transmit Function - called by serproto ****************************************************** */
+
+static int acm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
+{
+        struct acm_private *acm = tty->driver_data;
+        struct usb_function_instance *function = acm->function;
+	struct urb *urb;
+
+	TRACE_MSG1("count=%d",count);
+        //printk (KERN_INFO"%s: used: %d count: %d\n", __FUNCTION__, atomic_read(&acm->used), count);
+
+        RETURN_EINVAL_IF(!function);
+	TRACE_MSG("function OK");
+
+        // sanity check and are we connect
+        RETURN_EINVAL_IF(!atomic_read(&acm->used)); 
+	TRACE_MSG("used OK");
+        RETURN_ZERO_IF (!count || !acm->connected);
+	TRACE_MSG("connected OK");
+        RETURN_ZERO_IF(max_queued_urbs <= atomic_read(&acm->queued_urbs));
+	TRACE_MSG("max_queued_urbs OK");
+
+        // allocate a write urb
+        count = MIN(count, acm->writesize);
+
+        // XXX verify that we don't attempt to send wMaxPacketSize packets...
+        if (!(count % usbd_endpoint_wMaxPacketSize(function, BULK_OUT, 0)))  //QQSV
+                count--;
+        
+        //printk(KERN_INFO"%s:\n", __FUNCTION__);
+	RETURN_ENOMEM_IF (!(urb = usbd_alloc_urb (function, BULK_IN, count, acm_urb_sent_bulk)));
+	TRACE_MSG("alloc_urb OK");
+
+        if (from_user) 
+                copy_from_user(urb->buffer, buf, count);
+        else 
+                memcpy(urb->buffer, buf, count);
+        urb->privdata = acm;
+        urb->actual_length = count;
+        atomic_add(count, &acm->queued_bytes);
+        atomic_inc(&acm->queued_urbs);
+        usbd_send_urb(urb);
+	TRACE_MSG2("urbID#%04x --> %d",urbID(urb),count);
+        return count;
+}
+
+static int acm_tty_write_room(struct tty_struct *tty)
+{       
+        struct acm_private *acm = &acm_private;
+	int rc;
+
+	TRACE_MSG("entered");
+        //printk (KERN_INFO"%s: used: %d queued: %d %d room: %d\n", __FUNCTION__, atomic_read(&acm->used),
+        //                atomic_read(&acm->queued_urbs), atomic_read(&acm->queued_bytes),
+        //                (max_queued_urbs > atomic_read(&acm->queued_urbs)) && 
+        //                (max_queued_bytes > atomic_read(&acm->queued_bytes)) ? acm->writesize : 0);
+
+        RETURN_EINVAL_IF(!atomic_read(&acm->used)); 
+	TRACE_MSG("used OK");
+        rc = (!acm->function || max_queued_urbs <= atomic_read(&acm->queued_urbs) ||
+              max_queued_bytes <= atomic_read(&acm->queued_bytes) ) ?  0 : acm->writesize ;
+	TRACE_MSG1("--> %d",rc);
+        return(rc);
+}       
+
+static int acm_tty_chars_in_buffer(struct tty_struct *tty)
+{       
+        struct acm_private *acm = &acm_private;
+	int rc;
+	TRACE_MSG("entered");
+        RETURN_EINVAL_IF(!atomic_read(&acm->used)); 
+	TRACE_MSG("used OK");
+        RETURN_ZERO_IF(!acm->function); 
+	TRACE_MSG("function OK");
+        rc = atomic_read(&acm->queued_bytes);
+	TRACE_MSG1("--> %d",rc);
+        return(rc);
+}
+
+static struct urb *acm_frwd_recv_urbs(struct tty_struct *tty, struct acm_private *acm)
+{
+	unsigned long flags;
+	struct urb *urb;
+
+	TRACE_MSG("entered");
+        //struct usb_function_instance *function = acm->function;
+
+	//if (acm->throttle) 
+        //        printk(KERN_INFO"%s: THROTTLED at 0\n", __FUNCTION__);
+	
+	while (acm->rr_in_ndx != acm->rr_out_ndx && !acm->throttle) {
+		// Forward one URB from the receive ring
+		local_irq_save(flags);
+		urb = acm->recv_ring[acm->rr_out_ndx];
+		acm->recv_ring[acm->rr_out_ndx] = NULL;
+		acm->rr_out_ndx = (acm->rr_out_ndx + 1) % RECV_RING_SIZE;
+		local_irq_restore(flags);
+
+		if (urb->status == RECV_OK && tty) {
+        		unsigned char *cp = urb->buffer;
+			int i;
+                        int f = 0;
+			// Future: think about throttle part way through...
+	                //printk(KERN_INFO"%s: ", __FUNCTION__);
+#define ACM_WORD_COPY
+#ifndef ACM_WORD_COPY
+	                for (i = 0; i < urb->actual_length; i++) {
+	                        if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+	                                tty_flip_buffer_push(tty);
+                                        acm->bytes_forwarded += f;
+                                        f = 0;
+                                }
+	                         
+	                        //printk("%02x ", *cp);
+	                        tty_insert_flip_char(tty, *cp++, 0);
+                                f += 1;
+
+				if (acm->throttle) {
+	                		//printk(KERN_INFO"%s: throttled at %d/%d\n", __FUNCTION__,i,urb->actual_length);
+					break;
+				}
+	                }
+	                //printk("\n");
+	                tty_flip_buffer_push(tty);
+                        acm->bytes_forwarded += f;
+#else
+                        for (i = urb->actual_length; i > 0; i -= f) {
+                                f = i;
+                                if (tty->flip.count + f > TTY_FLIPBUF_SIZE) {
+                                        tty_flip_buffer_push(tty);
+                                        f = MIN(f, TTY_FLIPBUF_SIZE - tty->flip.count);
+                                }
+                                if (f <= 0) {
+                                        // Shouldn't happen...
+                                        printk(KERN_ERR"%s: %d bytes dropped\n", __FUNCTION__, i);
+                                        break;
+                                }
+                                memcpy(tty->flip.char_buf_ptr, cp, f); // see: arch/arm/lib/memcpy.S
+                                cp += f;
+                                tty->flip.count += f;
+                                tty->flip.flag_buf_ptr += f;
+                                tty->flip.char_buf_ptr += f;        // w20146 - Jul 31
+                                tty_flip_buffer_push(tty);
+                                acm->bytes_forwarded += f;
+                        }
+
+#endif
+			if (acm->throttle) {
+                		printk(KERN_INFO"%s: THROTTLED at 1\n", __FUNCTION__);
+                        }
+		} 
+                else if (urb->status != RECV_OK) 
+                	printk(KERN_INFO"%s: !RECV_OK, dumping %d bytes\n", __FUNCTION__,urb->actual_length);
+		else 
+                	printk(KERN_INFO"%s: tty NULL\n", __FUNCTION__);
+		
+		// Put urb back in line for a refill.
+        	if (usbd_start_recv (urb)) {
+			// Bail if it can't be queued for refill.
+                	printk(KERN_INFO"%s: usbd_start_recv() failed\n", __FUNCTION__);
+			return(urb);
+		}
+	}
+	TRACE_MSG("exited");
+	return(NULL);
+}
+
+static void acm_tty_throttle(struct tty_struct *tty)
+{       
+        struct acm_private *acm = &acm_private;
+	TRACE_MSG("entered");
+        //uuu printk (KERN_INFO"%s:\n", __FUNCTION__);
+        if (acm && atomic_read(&acm->used)) 
+                acm->throttle = 1;
+	TRACE_MSG("exited");
+}               
+
+static void acm_tty_unthrottle(struct tty_struct *tty)
+{               
+        struct acm_private *acm = &acm_private;
+	struct urb *urb;
+	TRACE_MSG("entered");
+        //uuu printk (KERN_INFO"%s:\n", __FUNCTION__);
+        if (acm && atomic_read(&acm->used)) {
+                acm->throttle = 0;
+		// Forward any queued URBS.
+		if (NULL != (urb = acm_frwd_recv_urbs(tty,acm))) {
+			/* usbd_start_recv(urb) failed, so we need to dispose of this urb.
+                           Count on someone else to replace it. */
+                	usbd_dealloc_urb(urb);
+		}
+	}
+	TRACE_MSG("exited");
+}       
+
+static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
+{       
+        struct acm_private *acm = &acm_private;
+	struct usb_function_instance *function = acm->function;
+        unsigned int mask;
+        unsigned int newctrl;
+
+	TRACE_MSG("entered");
+        //printk (KERN_INFO"%s: used: %d\n", __FUNCTION__, atomic_read(&acm->used));
+        RETURN_EINVAL_IF(!atomic_read(&acm->used)); 
+
+#if 1	// temporary solution for IOCTL
+	if(cmd == TIOCMGET)
+		return put_user( acm->ctrlout & LINE_OUT_DTR ? TIOCM_DTR : 0 | TIOCM_CTS, (unsigned long *) arg);
+#endif
+
+	TRACE_MSG("--> -ENOIOCTLCMD");
+        return -ENOIOCTLCMD;
+
+#if 0
+        switch(cmd) {
+        case TCGETS:
+        case TCFLSH:
+        case TCSETS:
+                printk (KERN_INFO"%s: ignored cmd: %x\n", __FUNCTION__, cmd);
+                return 0;
+
+        case TCSETSW:
+
+                //user_termios_to_kernel_termios(&priv->termios, (struct termios *)arg);
+                printk (KERN_INFO"%s: ignored cmd: %x\n", __FUNCTION__, cmd);
+                return 0;
+
+        case TIOCMGET:
+                printk (KERN_INFO"%s: TIOCMGET: %04x\n", __FUNCTION__, acm->ctrlin & LINE_OUT_DTR ? TIOCM_DTR : 0 | TIOCM_CTS);
+                return put_user( acm->ctrlin & LINE_OUT_DTR ? TIOCM_DTR : 0 | TIOCM_CTS, (unsigned long *) arg);
+
+        case TIOCMSET:
+        case TIOCMBIS:
+        case TIOCMBIC:
+
+                printk (KERN_INFO"%s: TIOCM{SET,BIS,BIC}:\n", __FUNCTION__);
+                RETURN_EFAULT_IF (get_user(mask, (unsigned long *) arg));
+
+                printk (KERN_INFO"%s: TIOCM{SET,BIS,BIC}: %04x\n", __FUNCTION__, mask);
+
+                newctrl = acm->ctrlin;
+
+                mask = (mask & TIOCM_DTR ? LINE_IN_DCD|LINE_IN_DSR : 0)/* | (mask & TIOCM_RTS ? LINE_OUT_RTS : 0)*/;
+
+                switch(cmd) {
+                case TIOCMSET: newctrl = mask; break;
+                case TIOCMBIS: newctrl |= mask; break;
+                case TIOCMBIC: newctrl &= mask; break;
+                }
+                RETURN_ZERO_IF(acm->ctrlin == newctrl);
+
+                printk (KERN_INFO"%s: newctrl: %04x\n", __FUNCTION__, newctrl);
+                return acm_send_int_notification(function, CDC_NOTIFICATION_SERIAL_STATE, acm->ctrlin);
+
+        case 3:
+                return 0;
+        default:
+                printk (KERN_INFO"%s: unknown: %04x\n", __FUNCTION__, cmd);
+                break;
+        }
+        return -ENOIOCTLCMD;
+#endif
+}
+
+static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_old)
+{
+        struct acm_private *acm = &acm_private;
+        struct termios *termios;
+
+	TRACE_MSG("entered");
+        //printk (KERN_INFO"%s: tty: %p\n", __FUNCTION__, tty);
+
+        RETURN_IF(!atomic_read(&acm->used) || !tty || !tty->termios);
+
+        termios = tty->termios;
+        acm->clocal = termios->c_cflag & CLOCAL;
+
+        //printk (KERN_INFO"%s: clocal: %d\n", __FUNCTION__, acm->clocal);
+	TRACE_MSG("exited");
+}
+
+/* ********************************************************************************************* */
+
+static void acm_wakeup_writers(void *private)
+{
+        struct acm_private *acm = &acm_private;
+        struct tty_struct *tty = acm->tty;
+
+	TRACE_MSG("entered");
+        MOD_DEC_USE_COUNT;
+        
+        //printk (KERN_INFO"%s: MOD: %d connected: %d tty: %p\n", __FUNCTION__, MOD_IN_USE, acm->connected, tty);
+
+        RETURN_IF(!acm->connected || !atomic_read(&acm->used) || !tty);
+	TRACE_MSG("connected and used OK");
+
+        if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
+		TRACE_MSG("ldisc wakeup");
+                (tty->ldisc.write_wakeup)(tty);
+	}
+        
+        // printk (KERN_INFO"%s: write_wait wakeup\n", __FUNCTION__);
+        wake_up_interruptible(&tty->write_wait);
+	TRACE_MSG("exited");
+}
+
+
+/* ********************************************************************************************* */
+
+static int acm_tty_refcount;
+static struct tty_struct *acm_tty_table[ACM_TTY_MINORS];
+static struct termios *acm_tty_termios[ACM_TTY_MINORS];
+static struct termios *acm_tty_termios_locked[ACM_TTY_MINORS];
+
+static struct tty_driver acm_tty_driver = {
+        magic:                  TTY_DRIVER_MAGIC,
+        type:                   TTY_DRIVER_TYPE_SERIAL,
+        subtype:                SERIAL_TYPE_NORMAL,
+        flags:                  TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,
+        driver_name:            "acm-CDC",
+        name:                   "usb/acm/%d",
+        major:                  ACM_TTY_MAJOR,
+        num:                    ACM_TTY_MINORS,
+        minor_start:            0, 
+
+        open:                   acm_tty_open,
+        close:                  acm_tty_close,
+        write:                  acm_tty_write,
+        write_room:             acm_tty_write_room,
+        ioctl:                  acm_tty_ioctl,
+        throttle:               acm_tty_throttle,
+        unthrottle:             acm_tty_unthrottle,
+        chars_in_buffer:        acm_tty_chars_in_buffer,
+        set_termios:            acm_tty_set_termios,
+
+        refcount:               &acm_tty_refcount,
+        table:                  acm_tty_table,
+        termios:                acm_tty_termios,
+        termios_locked:         acm_tty_termios_locked,
+};      
+
+
+/* Transmit INTERRUPT ************************************************************************** */
+
+/**
+ * Generates a response urb on the notification (INTERRUPT) endpoint.
+ * Return a non-zero result code to STALL the transaction.
+ * CALLED from interrupt context.
+ */
+static int acm_send_int_notification(struct usb_function_instance *function, int bnotification, int data)
+{
+	struct acm_private *acm = &acm_private;
+        struct urb *urb = NULL;
+        struct cdc_notification_descriptor *notification;
+        unsigned long flags;
+        int rc;
+
+        //printk(KERN_INFO"%s: function: %p connected: %d DTR: %d\n", __FUNCTION__, 
+        //                function, acm->connected, acm->ctrlout & LINE_OUT_DTR);
+
+        local_irq_save(flags);
+	TRACE_MSG("entered");
+
+        do {
+                BREAK_IF(!function);
+                //BREAK_IF(!acm->connected || !acm->ctrlout & LINE_OUT_DTR);
+                BREAK_IF(!acm->connected);
+
+                //printk (KERN_INFO"%s: bnotification: %x data: %d int_urb: %p\n", 
+                //                __FUNCTION__, bnotification, data, acm->int_urb);
+
+                if (acm->int_urb) {
+                        //uuu printk(KERN_INFO"%s: int_urb: %p\n", __FUNCTION__, acm->int_urb);
+                        usbd_cancel_urb_irq(acm->int_urb);
+                        acm->int_urb = NULL;
+                }
+
+                //printk(KERN_INFO"%s: AAA\n", __FUNCTION__);
+
+                //uuu printk(KERN_INFO"%s:\n", __FUNCTION__);
+                BREAK_IF(!(urb = usbd_alloc_urb (function, INT_IN, 
+                                                sizeof(struct cdc_notification_descriptor), acm_urb_sent_int))); 
+
+                //printk(KERN_INFO"%s: BBB\n", __FUNCTION__);
+
+                memset(urb->buffer, 0, urb->buffer_length);
+                urb->actual_length = sizeof(struct cdc_notification_descriptor);
+                urb->privdata = &acm_private;
+
+                // fill in notification structure
+                notification = (struct cdc_notification_descriptor *) urb->buffer;
+
+                notification->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+                notification->bNotification = bnotification;
+
+                switch (bnotification) {
+                case CDC_NOTIFICATION_NETWORK_CONNECTION:
+                        notification->wValue = data;
+                        break;
+                case CDC_NOTIFICATION_SERIAL_STATE:
+                        notification->wLength = cpu_to_le16(2);
+                        *((unsigned short *)notification->data) = cpu_to_le16(data);
+                        break;
+                }
+
+                acm->int_urb = urb;
+                //printk(KERN_INFO"%s: CCC\n", __FUNCTION__);
+                BREAK_IF(!(rc = usbd_send_urb (urb)));
+
+                //uuu printk(KERN_ERR"%s: usbd_send_urb failed err: %x\n", __FUNCTION__, rc);
+                acm->int_urb = urb;
+                urb->privdata = NULL;
+                usbd_dealloc_urb (urb);
+
+        } while(0);
+
+	TRACE_MSG1("urbID#%04x --> 0",urbID(urb));
+        local_irq_restore(flags);
+        //printk(KERN_INFO"%s: DDD\n", __FUNCTION__);
+        return 0;
+}
+
+/* Callback functions for TX urb completion (function chosen when urb is allocated) ***********/
+
+/* acm_urb_sent_bulk - called to indicate URB transmit finished
+ * @urb: pointer to struct urb
+ * @rc: result
+ */
+static int acm_urb_sent_bulk (struct urb *urb, int rc)
+{
+	struct acm_private *acm = &acm_private;
+        struct usb_function_instance *function;
+
+	acm_interrupts++;
+	TRACE_MSG2("entered urbID#%04x rc=%d",urbID(urb),rc);
+
+	if (!urb || !(function = urb->function_instance)) {
+		TRACE_MSG1("urbID#%04x --> -EINVAL",urbID(urb));
+		return(-EINVAL);
+	}
+	TRACE_MSG1("IN length=%d",urb->actual_length);
+
+        atomic_sub(urb->actual_length, &acm->queued_bytes);
+        atomic_dec(&acm->queued_urbs);
+        urb->privdata = NULL;
+        usbd_dealloc_urb (urb);
+        acm_schedule(&acm->wqueue);
+	TRACE_MSG1("urbID#%04x --> 0",urbID(urb));
+        return 0;
+}
+
+/* acm_urb_sent_int - called to indicate URB transmit finished
+ * @urb: pointer to struct urb
+ * @rc: result
+ */
+static int acm_urb_sent_int (struct urb *urb, int rc)
+{
+        struct acm_private *acm = &acm_private;
+        struct usb_function_instance *function;
+
+	acm_interrupts++;
+	TRACE_MSG2("entered urbID#%04x rc=%d",urbID(urb),rc);
+        //printk(KERN_INFO"%s: urb: %p function: %p rc: %d\n", __FUNCTION__, urb, urb->function_instance, rc);
+	if (!urb || !(function = urb->function_instance)) {
+		TRACE_MSG1("urbID#%04x --> -EINVAL",urbID(urb));
+		return(-EINVAL);
+	}
+	TRACE_MSG1("INT length=%d",urb->actual_length);
+
+        acm->int_urb = NULL;
+        usbd_dealloc_urb(urb);
+	TRACE_MSG1("urbID#%04x --> 0",urbID(urb));
+        return 0;
+}
+
+#if defined(CONFIG_USBD_ACM_DATALOG)
+/* acm_urb_sent_pst - called to indicate URB transmit finished
+ * @urb: pointer to struct urb
+ * @rc: result
+ */
+int acm_urb_sent_pst (struct urb *urb, int rc)
+{
+        struct acm_private *acm = &acm_private;
+        struct usb_function_instance *function;
+
+	acm_interrupts++;
+	TRACE_MSG2("entered urbID#%04x rc=%d",urbID(urb),rc);
+        //uuu printk(KERN_INFO"urb: %p function: %p rc: %d\n", urb, urb->function_instance, rc);
+	if (!urb || !(function = urb->function_instance)) {
+		TRACE_MSG1("urbID#%04x --> -EINVAL",urbID(urb));
+		return(-EINVAL);
+	}
+	TRACE_MSG1("PST length=%d",urb->actual_length);
+
+#if defined(PST_FD_AVAILABLE)
+        pst_urb_sent(urb, rc);
+#endif
+        urb->privdata = NULL;
+        usbd_dealloc_urb(urb);
+	TRACE_MSG1("urbID#%04x --> 0",urbID(urb));
+        return 0;
+}
+#endif
+
+/* USB Device Functions ************************************************************************ */
+
+/* acm_event_irq - process a device event
+ *
+ */
+void acm_event_irq (struct usb_function_instance *function, usb_device_event_t event, int data)
+{
+        struct acm_private *acm = &acm_private;
+        int i;
+
+	acm_interrupts++;
+	TRACE_MSG1("entered ev=%d",event);
+	switch (event) {
+
+#if defined(CONFIG_USBD_ACM_DATALOG)
+	case DEVICE_CREATE:
+#if defined(PST_FD_AVAILABLE)
+		if (0 != (ret = pst_dev_create(device)))
+			return;
+#endif
+		break;
+
+	case DEVICE_DESTROY:
+#if defined(PST_FD_AVAILABLE)
+		pst_dev_destroy();
+#endif
+		break;
+#endif
+
+	case DEVICE_CONFIGURED:
+		TRACE_MSG("CONFIGURED");
+                acm->connected = 1;
+                acm_schedule(&acm->wqueue);
+                for (i = 0; i < MAX_RECV_URBS; i++) {
+                        struct urb *urb;
+                        // printk(KERN_INFO"%s:\n", __FUNCTION__);
+                        BREAK_IF(!(urb = usbd_alloc_urb(function, BULK_OUT, 
+                                                        usbd_endpoint_transferSize(function, BULK_OUT,usbd_high_speed(function)),
+			                                acm_recv_urb)));
+                        if (usbd_start_recv(urb)) 
+                                usbd_dealloc_urb(urb);
+                }
+                break;
+
+	case DEVICE_RESET:
+	case DEVICE_DE_CONFIGURED:
+		TRACE_MSG("RESET");
+                BREAK_IF(!acm->connected);
+                acm->connected = 0;
+                acm->int_urb = NULL;
+                acm_schedule(&acm->hqueue);
+                // XXX flush
+		// Release any queued urbs
+		break;
+
+        default: break;
+	}
+	TRACE_MSG("exited");
+}
+
+static int acm_recv_urb (struct urb *urb, int rc)
+{
+	/* Return 0 if urb has been accepted,
+	   return 1 and expect caller to deal with urb release otherwise. */
+	struct acm_private *acm = &acm_private;
+        struct tty_struct *tty = acm->tty;
+	struct urb *furb;
+
+	acm_interrupts++;
+	TRACE_MSG1("entered rc=%d",rc);
+        //printk(KERN_INFO"%s: urb: %p rc=%d\n", __FUNCTION__, urb, rc);
+	if (RECV_CANCELLED == rc) {
+		TRACE_MSG1("cancelled URB=%p",urb);
+                usbd_dealloc_urb(urb);
+		return(0);
+	}
+	if (RECV_OK != rc) {
+		// Shouldn't happen, but...
+		// Reject it.
+		TRACE_MSG1("rejected URB=%p",urb);
+        	//printk(KERN_INFO"%s: rejecting URB %p rc=%d\n", __FUNCTION__, urb, rc);
+		return(1);
+	}
+	// Queue it for forwarding, then kick off forwarding routine.
+	acm->bytes_received += urb->actual_length;
+	acm->recv_ring[acm->rr_in_ndx] = urb;
+        acm->rr_in_ndx = (acm->rr_in_ndx + 1) % RECV_RING_SIZE;
+	if (urb == (furb = acm_frwd_recv_urbs(tty,acm))) {
+		// Couldn't restart urb, tell the caller to dispose of it.
+		TRACE_MSG1("caller to release URB=%p",urb);
+        	//printk(KERN_INFO"%s: caller to release URB %p rc=%d\n", __FUNCTION__, urb, rc);
+		return(1);
+	}
+        if (NULL != furb) {
+		/* Couldn't restart some other urb, but we can't tell the caller
+		   about it, so we need to dispose of it ourselves.  This should
+                   only happen if the bi layer is shutting down, so we let someone
+                   else allocate the replacement if one is needed. */
+		TRACE_MSG1("releasing URB=%p",urb);
+                usbd_dealloc_urb(urb);
+	}
+	TRACE_MSG("--> 0");
+	return(0);
+}
+
+/* acm_line_coding_urb_received - callback for sent URB
+ *
+ * Handles notification that an urb has been sent (successfully or otherwise).
+ *
+ * Returns non-zero for failure.
+ */
+static int acm_line_coding_urb_received (struct urb *urb, int urb_rc)
+{
+	TRACE_MSG2("urbID#%04x rc=%d",urbID(urb),urb_rc);
+
+	RETURN_EINVAL_IF (RECV_OK != urb_rc);
+
+	return -EINVAL;         // caller will de-allocate
+}
+
+/* acm_recv_setup_irq - called to indicate urb has been received
+ */
+int acm_recv_setup_irq (struct usb_device_request *request)
+{
+        struct acm_private *acm = &acm_private;
+	struct usb_function_instance *function = acm->function;
+
+	acm_interrupts++;
+	TRACE_SETUP(request);
+
+        // verify that this is a usb class request per cdc-acm specification or a vendor request.
+        if (!(request->bmRequestType & (USB_REQ_TYPE_CLASS | USB_REQ_TYPE_VENDOR))) {
+		TRACE_MSG("--> 0");
+		return(0);
+	}
+       
+        // determine the request direction and process accordingly
+        switch (request->bmRequestType & (USB_REQ_DIRECTION_MASK | USB_REQ_TYPE_MASK)) {
+
+        case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_CLASS:
+                switch (request->bRequest) {
+                case CDC_CLASS_REQUEST_SEND_ENCAPSULATED: break;
+                case CDC_CLASS_REQUEST_SET_COMM_FEATURE: break;
+                case CDC_CLASS_REQUEST_CLEAR_COMM_FEATURE: break;
+                case CDC_CLASS_REQUEST_SET_LINE_CODING:
+			{
+				struct urb *urb;
+				int len = le16_to_cpu(request->wLength);
+				TRACE_MSG1("SET_LINE_CODING wLength=%d",len);
+				if (len <= 0) {
+					TRACE_MSG("(len<=0)--> 0");
+					return(0);
+				}
+				// Set up an ep0 recv urb for the rest of it.
+				urb = usbd_alloc_urb_ep0(function, len, acm_line_coding_urb_received);
+				if (NULL == urb) {
+					TRACE_MSG("no mem for ep0 recv urb");
+					return(-EINVAL);
+				}
+				if (usbd_start_recv(urb)) {
+					TRACE_MSG("usbd_start_recv() failed");
+					usbd_dealloc_urb(urb);          // de-alloc if error
+					TRACE_MSG("--> -EINVAL");
+					return(-EINVAL);
+				}
+                        }
+                        break;
+                case CDC_CLASS_REQUEST_SET_CONTROL_STATE:
+                        {
+                                struct acm_private *acm = &acm_private;
+				unsigned int prev_ctrlout = acm->ctrlout;
+                                acm->ctrlout = le16_to_cpu(request->wValue);
+                                
+				TRACE_MSG1("set control state, tty=%p",acm->tty);
+                                //uuu printk(KERN_INFO"%s: tty: %p clocal: %x ctrlout: %02x DTR: %x\n", 
+                                //uuu                 __FUNCTION__, acm->tty, acm->clocal, acm->ctrlout, acm->ctrlout & LINE_OUT_DTR);
+                                
+                                // schedule writers or hangup IFF open
+                                BREAK_IF(!acm->tty);
+				TRACE_MSG1("set control state, ctrlout#%04x",acm->ctrlout);
+				// make sure there really is a state change
+				if ((acm->ctrlout ^ prev_ctrlout) & LINE_OUT_DTR) {
+					TRACE_MSG1("DTR state changed -> %x",(acm->ctrlout&LINE_OUT_DTR));
+                                	//printk(KERN_INFO"%s: tty: %p DTR state changed -> %u\n", 
+                                        //        __FUNCTION__, acm->tty, (acm->ctrlout&LINE_OUT_DTR));
+                                	acm_schedule(((acm->ctrlout & LINE_OUT_DTR) ? &acm->wqueue : &acm->hqueue));
+					// wake up blocked opens
+					if (acm->open_wait_count > 0) {
+						wake_up_interruptible(&acm->open_wait);
+					}
+				} // end of state change operation
+
+                                // send notification if we have DCD
+				TRACE_MSG1("checking DCD ctrlin#%04x",acm->ctrlin);
+                                BREAK_IF(!(acm->ctrlin & (LINE_IN_DCD | LINE_IN_DSR)));
+				TRACE_MSG1("tty=%p sending (DCD|DSR) notification",acm->tty);
+                                acm_send_int_notification(function, CDC_NOTIFICATION_SERIAL_STATE, acm->ctrlin);
+                        }
+                        break;
+
+                case CDC_CLASS_REQUEST_SEND_BREAK: break;
+                default: break;
+                }
+		TRACE_MSG("--> 0");
+                return 0;
+
+        case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_CLASS:
+                switch (request->bRequest) {
+                case CDC_CLASS_REQUEST_GET_ENCAPSULATED: break;
+                case CDC_CLASS_REQUEST_GET_COMM_FEATURE: break;
+                case CDC_CLASS_REQUEST_GET_LINE_CODING: 
+                        {
+                                struct urb *urb;
+                                struct cdc_acm_line_coding *results;
+				int len = le16_to_cpu(request->wLength);
+				if (len != sizeof(struct cdc_acm_line_coding)) {
+					TRACE_MSG2("(len=%d!=sz=%d--> -EINVAL",len,sizeof(struct cdc_acm_line_coding));
+                                	return -EINVAL;
+				}
+
+                                if (!(urb = usbd_alloc_urb_ep0(function, len, NULL))) {
+					TRACE_MSG("(nomem)--> -EINVAL");
+                                	return -EINVAL;
+				}
+                                results = (struct cdc_acm_line_coding *)urb->buffer;
+                                results->dwDTERate = cpu_to_le16(0x1c200); // 115200
+                                results ->bDataBits = 0x08;
+                                urb->actual_length = len;
+				TRACE_MSG1("sending line coding urb=%p",(u32)(void*)urb);
+                                if (!usbd_send_urb(urb)) {
+					TRACE_MSG("--> 0");
+					return(0);
+				}
+                                usbd_dealloc_urb(urb);
+				TRACE_MSG("(send failed)--> -EINVAL");
+                                return -EINVAL;
+                        }
+                default: break;
+                }
+		TRACE_MSG("--> 0");
+                return 0;
+
+        case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_VENDOR: break;
+        case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_VENDOR: break;
+#if defined(PST_FD_AVAILABLE)
+		ep0_process_vendor_request( urb );
+		TRACE_MSG("--> 0");
+		return 0;
+#endif
+
+        default: break;
+        }
+	TRACE_MSG("--> 0");
+        return 0;
+}
+
+
+static int acm_function_enable (struct usb_function_instance *function)
+{
+        struct acm_private *acm = &acm_private;
+	acm_interrupts++;
+	TRACE_MSG("entered");
+	MOD_INC_USE_COUNT;
+        acm->function = function;
+        acm->writesize = usbd_endpoint_wMaxPacketSize(function, BULK_OUT, 0) * 4; // QQSV
+	TRACE_MSG("-> 0");
+        return 0;
+}
+
+static void acm_function_disable (struct usb_function_instance *function)
+{
+        struct acm_private *acm = &acm_private;
+	acm_interrupts++;
+	TRACE_MSG("entered");
+        acm->writesize = 0;
+        acm->function = NULL;
+        MOD_DEC_USE_COUNT;
+	TRACE_MSG("exited");
+}
+
+
+static struct usb_function_operations function_ops = {
+	event_irq: acm_event_irq,
+	recv_setup_irq: acm_recv_setup_irq,
+        function_enable: acm_function_enable,
+        function_disable: acm_function_disable,
+};
+
+static struct usb_function_driver function_driver = {
+	name: "acm-CDC",
+	fops:&function_ops,
+	device_description:&acm_device_description,
+	bNumConfigurations:sizeof (acm_description) / sizeof (struct usb_configuration_description),
+	configuration_description:acm_description,
+	idVendor: __constant_cpu_to_le16(CONFIG_USBD_ACM_VENDORID),
+	idProduct: __constant_cpu_to_le16(CONFIG_USBD_ACM_PRODUCTID),
+	bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_ACM_BCDDEVICE),
+};
+
+#if defined(USE_TICKER)
+/* usb ticker ********************************************************************************** */
+
+#define retrytime 10
+                
+int ticker_terminating;
+int ticker_timer_set;
+
+static DECLARE_MUTEX_LOCKED (ticker_sem_start);
+static DECLARE_MUTEX_LOCKED (ticker_sem_work);
+
+void ticker_tick (unsigned long data)
+{
+        ticker_timer_set = 0;
+        up (&ticker_sem_work);
+}
+
+void udc_ticker_poke (void)
+{
+        up (&ticker_sem_work);
+}
+
+int ticker_thread (void *data)
+{
+        struct timer_list ticker;
+        // detach
+        lock_kernel ();
+        exit_mm (current);
+        exit_files (current);
+        exit_fs (current);
+
+        // setsid equivalent, used at start of kernel thread, no error checks needed, or at least none made :). 
+        current->leader = 1;
+        current->session = current->pgrp = current->pid;
+        current->tty = NULL;
+        current->tty_old_pgrp = 0;
+        sprintf (current->comm, "acm_fd");
+
+        // setup signal handler
+        current->exit_signal = SIGCHLD;
+        spin_lock (&current->sigmask_lock);
+        flush_signals (current);
+        spin_unlock (&current->sigmask_lock);
+
+        // run at a high priority, ahead of sync and friends
+        current->policy = SCHED_OTHER;
+        unlock_kernel ();
+
+        // setup timer
+        init_timer (&ticker);
+        ticker.data = 0;
+        ticker.function = ticker_tick;
+
+        // let startup continue
+        up (&ticker_sem_start);
+        
+        // process loop
+        for (ticker_timer_set = ticker_terminating = 0; !ticker_terminating;) {
+
+                struct acm_private * acm = &acm_private;
+
+                if (!ticker_timer_set) {
+                        mod_timer (&ticker, jiffies + HZ * retrytime);
+                }
+
+                // wait for someone to tell us to do something
+                down (&ticker_sem_work);
+
+                // sanity checks before proceeding
+                BREAK_IF(ticker_terminating);
+                CONTINUE_IF(!(function = acm->function));
+                CONTINUE_IF(USBD_OK != bus->status);
+
+                // do what we need to do
+                acm_send_int_notification(function, CDC_NOTIFICATION_SERIAL_STATE, acm->ctrlin);
+        }
+
+        // remove timer, let the process stopping us know we are done and return
+        del_timer (&ticker);
+        up (&ticker_sem_start);
+        return 0;
+}
+#endif
+
+
+/* USB Module init/exit ************************************************************************ */
+/*
+ * acm_modinit - module init
+ *
+ */
+static int acm_modinit (void)
+{
+	int i;
+        printk (KERN_INFO "Copyright (c) 2003-2004 sl@belcarra.com\n");
+
+	if (0 != acm_trace_init(ACM_TRACE_NAME)) {
+                printk(KERN_ERR"%s: ERROR tracing configured, but init failed.\n", __FUNCTION__);
+                return -EINVAL;
+	}
+	TRACE_MSG("entered");
+
+        if (vendor_id) 
+                function_driver.idVendor = cpu_to_le16(vendor_id);
+        if (product_id) 
+                function_driver.idProduct = cpu_to_le16(product_id);
+	printk (KERN_INFO "%s: %s vendor_id: %04x product_id: %04x\n", __FUNCTION__,
+		 __usbd_module_info, function_driver.idVendor, function_driver.idProduct);
+
+        // initialize private structure
+        acm_private.tty_driver = &acm_tty_driver;
+        acm_private.wqueue.routine = acm_wakeup_writers;
+        acm_private.wqueue.data = &acm_private;
+        acm_private.hqueue.routine = acm_hangup;
+        acm_private.hqueue.data = &acm_private;
+
+        init_waitqueue_head(&acm_private.open_wait);
+
+	for (i = 0; i < RECV_RING_SIZE; i++) {
+		acm_private.recv_ring[i] = NULL;
+	}
+	acm_private.rr_in_ndx = acm_private.rr_out_ndx = 0;
+
+        // register as tty driver
+        acm_tty_driver.init_termios = tty_std_termios;
+        acm_tty_driver.init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;
+        acm_tty_driver.init_termios.c_lflag &= ~(ECHO | ICANON);
+        THROW_IF(tty_register_driver(&acm_tty_driver), error);
+
+        tty_register_devfs(&acm_tty_driver, 0, ACM_TTY_MINOR);
+        acm_private.tty_driver_registered++;
+
+	// register as usb function driver
+	THROW_IF (usbd_register_function (&function_driver), error);
+        acm_private.usb_driver_registered++;
+
+#if defined(USE_TICKER)
+        // kickoff_thread - start management thread
+        //ticker_terminating = 0;
+        //kernel_thread (&ticker_thread, NULL, 0);
+        //down (&ticker_sem_start);
+#endif
+
+        CATCH(error) {
+                printk(KERN_ERR"%s: ERROR\n", __FUNCTION__);
+
+                if (acm_private.tty_driver_registered) {
+                        tty_unregister_driver(&acm_tty_driver);
+                        acm_private.tty_driver_registered = 0;
+                }
+                if (acm_private.usb_driver_registered) {
+                        usbd_deregister_function (&function_driver);
+                        acm_private.usb_driver_registered = 0;
+                }
+		TRACE_MSG("--> -EINVAL");
+                return -EINVAL;
+        }
+	TRACE_MSG("--> 0");
+	return 0;
+}
+
+void acm_wait_task(struct tq_struct *queue)
+{
+	TRACE_MSG1("entered data=%p",queue->data);
+        RETURN_IF(!queue->data);
+        queue->data = NULL;
+        while (queue->sync) {
+                //uuu printk(KERN_INFO"%s: waiting for queue: %p\n", __FUNCTION__, queue);
+		TRACE_MSG1("waiting for queue: %p",queue);
+                schedule_timeout(HZ);
+        }
+	TRACE_MSG("exited");
+}
+
+/* acm_modexit - module cleanup
+ */
+static void acm_modexit (void)
+{
+	unsigned long flags;
+	struct urb *urb;
+	TRACE_MSG("entered");
+
+#if defined(USE_TICKER)
+        // killoff_thread - stop management thread
+        //if (!ticker_terminating) {
+        //        ticker_terminating = 1;
+        //        up (&ticker_sem_work);
+        //        down (&ticker_sem_start);
+        //}
+#endif
+
+	// Wake up any pending opens after setting the exiting flag.
+	local_irq_save(flags);
+	acm_private.exiting = 1;
+	if (acm_private.open_wait_count > 0) {
+		wake_up_interruptible(&acm_private.open_wait);
+	}
+	local_irq_restore(flags);
+
+        // verify no tasks are running
+        acm_wait_task(&acm_private.wqueue);
+        acm_wait_task(&acm_private.hqueue);
+
+        // de-register as tty  and usb drivers
+        if (acm_private.tty_driver_registered) {
+                tty_unregister_driver(&acm_tty_driver);
+        }
+        if (acm_private.usb_driver_registered) {
+                usbd_deregister_function (&function_driver);
+        }
+
+	// Flush any urbs in the recv_ring.
+	while (acm_private.rr_in_ndx != acm_private.rr_out_ndx) {
+		// Remove and dealloc one URB from the receive ring
+		local_irq_save(flags);
+		urb = acm_private.recv_ring[acm_private.rr_out_ndx];
+		acm_private.recv_ring[acm_private.rr_out_ndx] = NULL;
+		acm_private.rr_out_ndx = (acm_private.rr_out_ndx + 1) % RECV_RING_SIZE;
+		local_irq_restore(flags);
+        	// printk(KERN_INFO"%s: releasing URB %p\n", __FUNCTION__, urb);
+		usbd_dealloc_urb(urb);
+	}
+	acm_trace_exit(ACM_TRACE_NAME);
+}
+
+
+module_init (acm_modinit);
+module_exit (acm_modexit);
diff -Nru a/drivers/usbd/acm_fd/trace.c b/drivers/usbd/acm_fd/trace.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/acm_fd/trace.c	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,530 @@
+/*
+ * usbd/acm_fd/trace.c
+ *
+ *      Copyright (c) 2004 Belcarra
+ *
+ * Adapted from earlier work:
+ *      Copyright (c) 2002, 2003 Belcarra
+ *      Copyright (c) 2002 Lineo
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+
+#include <asm/atomic.h>
+#include <asm/io.h>
+
+#include <linux/proc_fs.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,6)
+#define USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK 1
+#include <linux/timer.h>
+#else
+#undef USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK
+#include <linux/tqueue.h>
+#endif
+
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/cache.h>
+
+
+#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA)
+#include <asm/dma.h>
+#include <asm/mach/dma.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/hardware.h>
+#include <asm/types.h>
+#endif
+
+#if defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100)
+#include <asm/au1000.h>
+#include <asm/au1000_dma.h>
+#include <asm/mipsregs.h>
+#endif
+
+#if defined(CONFIG_ARCH_SAMSUNG)
+#include <asm/arch/timers.h>
+#include <asm/arch/hardware.h>
+#endif
+
+#if defined(CONFIG_ARCH_MX1ADS)
+#include "dbmx1_bi/dbmx1.h"
+#endif
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+
+#include <usbd-chap9.h>
+#include <usbd-mem.h>
+#include <usbd.h>
+#include <trace.h>
+
+
+int acm_trace_first;
+int acm_trace_last_read;
+int acm_trace_next;
+int acm_trace_total;
+acm_trace_t *acm_traces;
+
+extern int acm_interrupts;
+
+
+#if defined(CONFIG_USBD_ACM_TRACE) && defined(CONFIG_PROC_FS)
+
+acm_trace_t *ACM_TRACE_NEXT(char *fn, acm_trace_types_t acm_trace_type)
+{
+        acm_trace_t *p;
+	unsigned long flags;
+
+	// Get the next trace slot - this needs to be atomic.
+	local_irq_save (flags);
+        p = acm_traces + acm_trace_next;
+#if defined(TRACE_MAX_IS_2N)
+	acm_trace_next = (acm_trace_next + 1) & TRACE_MASK;
+        if (acm_trace_next == acm_trace_first) {
+		// We have wraparound, bump acm_trace_first
+		if (acm_trace_first == acm_trace_last_read) {
+			// We have to bump last read too.
+			acm_trace_last_read = (acm_trace_last_read + 1) & TRACE_MASK;
+		}
+		acm_trace_first = (acm_trace_first + 1) & TRACE_MASK;
+	}
+#else
+        if (TRACE_MAX <= ++acm_trace_next)
+		acm_trace_next = 0;
+
+        if (acm_trace_next == acm_trace_first) {
+		// We have wrap around, bump acm_trace_first
+		if (acm_trace_first == acm_trace_last_read) {
+			// We have to bump last read too.
+        		if (TRACE_MAX <= ++acm_trace_last_read)
+				acm_trace_last_read = 0;
+		}
+        	if (TRACE_MAX <= ++acm_trace_first)
+			acm_trace_first = 0;
+        }
+#endif
+	acm_trace_total++;
+	// End of next trace slot.
+	local_irq_restore (flags);
+
+#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA)
+        p->oscr = OSCR;
+#elif defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19)
+        p->cp0_count = __read_32bit_c0_register(CP0_COUNT);
+#else
+        p->cp0_count = read_c0_count();
+#endif
+#elif defined(CONFIG_ARCH_SAMSUNG)
+        //p->jiffies = jiffies;
+        //p->tcnt0 = *(volatile u32 *)TCNT0;
+        p->tcnt1 = *(volatile u32 *)TCNT1;
+#else
+        p->jiffies = jiffies;
+#endif
+#if defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100)
+        //p->sofs = au_readl(USBD_FRAMENUM);
+#endif
+#if defined(CONFIG_ARCH_MX1ADS)
+        p->sofs = USBD_FRAME & 0x3ff;
+#endif
+
+        p->interrupts = acm_interrupts;
+        p->acm_trace_type = acm_trace_type;
+	p->function = fn;
+
+        //printk(KERN_INFO"first: %d next: %d interrupts: %d oscr: %d\n", acm_trace_first, acm_trace_next, acm_interrupts, p->oscr);
+
+        return p;
+}
+
+/* Proc Filesystem *************************************************************************** */
+        
+/* *    
+ * acm_trace_proc_read - implement proc file system read.
+ * @file        
+ * @buf         
+ * @count
+ * @pos 
+ *      
+ * Standard proc file system read function.
+ */         
+static ssize_t acm_trace_proc_read (struct file *file, char *buf, size_t count, loff_t * pos)
+{                                  
+        unsigned long page;
+        int len = 0;
+        int index;
+        int oindex;
+        int previous;
+	unsigned long flags;
+
+        acm_trace_t px;
+        acm_trace_t ox;
+        acm_trace_t *o, *p;
+	
+
+        MOD_INC_USE_COUNT;
+
+	/* Get the index of the entry to read and update last_read - this needs to be atomic. */
+        p = &px;
+	o = NULL;
+        oindex = index = (*pos)++;
+	local_irq_save (flags);
+                         
+#if defined(TRACE_MAX_IS_2N)
+	index = (acm_trace_first + index) & TRACE_MASK;
+#else
+        index += acm_trace_first;
+        if (index >= TRACE_MAX) {
+                index -= TRACE_MAX;
+        }
+#endif
+	// Are we at the end of the data?
+        if (((acm_trace_first < acm_trace_next) &&
+             (index >= acm_trace_first) && (index < acm_trace_next)) ||
+            ((acm_trace_first > acm_trace_next) &&
+             ((index < acm_trace_next) || (index >= acm_trace_first)))) {
+		// Nope, there's data to show.
+		memcpy(p,(acm_traces+index),sizeof(acm_trace_t));
+		// Is there a previous event?
+        	previous = (index) ? (index - 1) : (TRACE_MAX - 1);
+		if (previous != acm_trace_next && acm_trace_total > 1) {
+			// There is a valid previous event.
+			o = &ox;
+			memcpy(o,(acm_traces+previous),sizeof(acm_trace_t));
+		}
+		acm_trace_last_read = index;
+	} else {
+		index = -1;
+	}
+	local_irq_restore (flags);
+	if (index < 0) {
+		// End of data.
+		return(0);
+	}
+
+        // get a page, max 4095 bytes of data...
+        if (!(page = get_free_page (GFP_KERNEL))) {
+                MOD_DEC_USE_COUNT;
+                return -ENOMEM;
+        }
+
+        len = 0;
+
+        if (oindex == 0) {
+#if defined(CONFIG_ARCH_SAMSUNG)
+                len += sprintf ((char *) page + len, " Index     Ints     Ticks [%d]\n", CONFIG_USBD_SMDK2500_BCLOCK );
+#else
+                len += sprintf ((char *) page + len, " Index     Ints     Ticks\n");
+#endif
+        }       
+
+        //printk(KERN_INFO"first: %d next: %d index: %d %d prev: %d\n", acm_trace_first, acm_trace_next, oindex, index, previous);
+
+#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) || defined(CONFIG_MIPS_AU1000)  || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100)
+        u32 ticks = 0;
+#elif defined(CONFIG_ARCH_SAMSUNG)
+        u32 ticks = 0;
+#else
+        u64 jifs = 0;
+#endif
+        unsigned char *cp;
+        unsigned int *ip;
+        int skip = 0;
+
+	/* If there is a previous trace event, we want to calculate how many
+           ticks have elapsed siince it happened.  Unfortunately, determining
+           if there _is_ a previous event isn't obvious, since we have to watch
+           out for startup and wraparound. */
+        if (o != NULL) {
+
+#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA)
+                /*
+                 * oscr is 3.6864 Mhz free running counter, 
+                 *
+                 *      1/3.6864 = .2712
+                 *      60/221   = .2714
+                 *
+                 */
+                if (o->oscr) {
+                        ticks = (p->oscr > o->oscr) ? (p->oscr - o->oscr) : (o->oscr - p->oscr) ;
+                        ticks = (ticks * 60) / 221;
+                }
+
+#elif defined(CONFIG_MIPS_AU1000)  || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100)
+                /*
+                 * cp0_count is incrementing timer at system clock
+                 */
+                if (o->cp0_count) {
+                        ticks = (p->cp0_count > o->cp0_count) ? 
+                                (p->cp0_count - o->cp0_count) : (o->cp0_count - p->cp0_count) ;
+                        ticks = ticks / CONFIG_USBD_AU1X00_SCLOCK;
+                }
+
+#elif defined(CONFIG_ARCH_SAMSUNG)
+                /*
+                 * tcnt1 is a count-down timer running at the system bus clock
+                 * The divisor must be set as a configuration value, typically 66 or 133.
+                 */
+                if (o->tcnt1) {
+                        ticks = (p->tcnt1 < o->tcnt1) ?  (o->tcnt1 - p->tcnt1) : (p->tcnt1 - o->tcnt1) ;
+                        ticks /= CONFIG_USBD_SMDK2500_BCLOCK;
+                }
+#else
+                if (o->jiffies) {
+                        jifs = p->jiffies - acm_traces[previous].jiffies;
+                }
+#endif
+
+                if (o->interrupts != p->interrupts) {
+                        skip++;
+                }
+        }
+                
+        //printk(KERN_INFO"index: %d interrupts: %d\n", index, p->interrupts);
+        len += sprintf ((char *) page + len, "%s%6d %8d ", skip?"\n":"", index, p->interrupts);
+
+#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) || defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100)
+        if (ticks > 1024*1024) {
+                len += sprintf ((char *) page + len, "%8dM ", ticks>>20);
+        }
+        else {
+                len += sprintf ((char *) page + len, "%8d  ", ticks);
+        }
+#elif defined(CONFIG_ARCH_SAMSUNG)
+        //len += sprintf ((char *) page + len, "%8u ", p->jiffies);
+        //len += sprintf ((char *) page + len, "%8u ", p->tcnt0);
+        len += sprintf ((char *) page + len, "%8u ", p->tcnt1);
+        if (ticks > 1024*1024) {
+                len += sprintf ((char *) page + len, "%8dM ", ticks>>20);
+        }
+        else {
+                len += sprintf ((char *) page + len, "%8d  ", ticks);
+        }
+#else
+        if (jifs > 1024) {
+                len += sprintf ((char *) page + len, "%4dK ", (int)jifs>>10);
+        }
+        else {
+                len += sprintf ((char *) page + len, "%4d  ", (int)jifs);
+        }
+#endif
+#if defined(CONFIG_ARCH_MX1ADS)
+        len += sprintf ((char *) page + len, "%6d  ", (int)p->sofs);
+#endif
+
+        switch (p->acm_trace_type) {
+        case acm_trace_msg_n:
+                len += sprintf ((char *) page + len, " --                   %s: ",p->function);
+                len += sprintf ((char *) page + len, p->trace.msg.msg);
+                break;
+
+        case acm_trace_msg32_n:
+                len += sprintf ((char *) page + len, " --                   %s: ",p->function);
+                len += sprintf ((char *) page + len, p->trace.msg32.msg, p->trace.msg32.val);
+                break;
+
+        case acm_trace_msg16_n:
+                len += sprintf ((char *) page + len, " --                   %s: ",p->function);
+                len += sprintf ((char *) page + len, p->trace.msg16.msg, p->trace.msg16.val0, p->trace.msg16.val1);
+                break;
+
+        case acm_trace_msg8_n:
+                len += sprintf ((char *) page + len, " --                   %s: ",p->function);
+                len += sprintf ((char *) page + len, p->trace.msg8.msg, 
+                                p->trace.msg8.val0, p->trace.msg8.val1, p->trace.msg8.val2, p->trace.msg8.val3);
+                break;
+
+        case acm_trace_setup_n:
+                cp = (unsigned char *)&p->trace.setup;
+                len += sprintf ((char *) page + len, 
+                                " --           %s: request [%02x %02x %02x %02x %02x %02x %02x %02x]", 
+                                p->function, cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
+                break;
+        }
+        len += sprintf ((char *) page + len, "\n");
+
+	// len == 0 is valid, it just means we've reached the end of the data.
+        if (len > count) {
+                len = -EINVAL;
+        } 
+        else if (len > 0 && copy_to_user (buf, (char *) page, len)) {
+                len = -EFAULT;
+        }
+        free_page (page);
+        MOD_DEC_USE_COUNT;
+        return len;
+}
+
+/* *
+ * acm_trace_proc_write - implement proc file system write.
+ * @file
+ * @buf
+ * @count
+ * @pos
+ *
+ * Proc file system write function.
+ */
+static ssize_t acm_trace_proc_write (struct file *file, const char *buf, size_t count, loff_t * pos)
+{
+#define MAX_TRACE_CMD_LEN  64
+        char command[MAX_TRACE_CMD_LEN+1];
+        size_t n = count;
+	size_t l;
+	char c;
+
+        if (n > 0) {
+                l = MIN(n,MAX_TRACE_CMD_LEN);
+                if (copy_from_user (command, buf, l)) {
+                        count = -EFAULT;
+                } else {
+                        // flush remainder, if any
+                        n -= l;
+                        while (n > 0) {
+                                // Not too efficient, but it shouldn't matter
+                                if (copy_from_user (&c, buf + (count - n), 1)) {
+                                        count = -EFAULT;
+                                        break;
+                                }
+                                n -= 1;
+                        }
+			// Terminate command[]
+                        if (l > 0 && command[l-1] == '\n') {
+                                l -= 1;
+                        }
+                        command[l] = 0;
+                }
+        }
+
+        if (0 >= count) {
+                printk(KERN_INFO"%s: count <= 0 %d\n", __FUNCTION__, count);
+                return count;
+        }
+
+	if (!strncmp("flush", command, 5)) {
+		/* Move pointers so that next read continues from last read point,
+                   instead of all available messages - this needs to be atomic.  */
+		unsigned long flags;
+
+		local_irq_save (flags);
+#if defined(TRACE_MAX_IS_2N)
+		acm_trace_first = (acm_trace_last_read + 1) & TRACE_MASK;
+#else
+		if (TRACE_MAX <= (acm_trace_first = acm_trace_last_read + 1)) {
+			acm_trace_first = 0;
+		}
+#endif
+		local_irq_restore (flags);
+	}
+
+        return count;
+}
+
+static struct file_operations acm_trace_proc_operations_functions = {
+        read:acm_trace_proc_read,
+        write:acm_trace_proc_write,
+};
+
+#if defined(CONFIG_ARCH_SAMSUNG)
+#endif
+
+/**
+ * acm_trace_init
+ *
+ * Return non-zero if not successful.
+ */
+int acm_trace_init (char *name)
+{
+        printk(KERN_INFO"%s: creating /proc/%s with %u entries\n", __FUNCTION__,name,TRACE_MAX);
+        if (!(acm_traces = vmalloc(sizeof(acm_trace_t) * TRACE_MAX))) {
+                printk(KERN_ERR"%s: malloc failed %p %d\n", __FUNCTION__, acm_traces, sizeof(acm_trace_t) * TRACE_MAX);
+                return -EINVAL;
+        }
+        memset(acm_traces, 0, sizeof(acm_trace_t) * TRACE_MAX);
+	acm_trace_last_read = TRACE_MAX - 1;
+
+        {
+                struct proc_dir_entry *p;
+
+                // create proc filesystem entries
+                if ((p = create_proc_entry (name, 0, 0)) == NULL) {
+                        printk(KERN_INFO"%s PROC FS failed\n",name);
+                }
+                else {
+                        p->proc_fops = &acm_trace_proc_operations_functions;
+                }
+        }
+#if defined(CONFIG_ARCH_SAMSUNG)
+        *(volatile u32 *)TMOD |= 0x3 << 3;
+#endif
+        printk(KERN_INFO"%s: OK\n", __FUNCTION__);
+	return 0;
+}
+
+/**
+ * acm_trace_exit - remove procfs entry, free trace data space.
+ */
+void acm_trace_exit (char *name)
+{
+        {
+                unsigned long flags;
+                local_irq_save (flags);
+                remove_proc_entry (name, NULL);
+                if (acm_traces) {
+                        acm_trace_t *p = acm_traces;
+                        acm_traces = NULL;
+                        vfree(p);
+                }
+                local_irq_restore (flags);
+        }
+}
+
+
+#else
+int acm_trace_init (char *name)
+{
+        return 0;
+}
+
+void acm_trace_exit (char *name)
+{
+	return;
+}
+#endif
+
+/* End of FILE */
+
diff -Nru a/drivers/usbd/acm_fd/trace.h b/drivers/usbd/acm_fd/trace.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/acm_fd/trace.h	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,268 @@
+/*
+ * usbd/acm_fd/trace.h
+ *
+ *      Copyright (c) 2004 Belcarra
+ *
+ * Adapted from earlier work:
+ *      Copyright (c) 2002, 2003 Belcarra
+ *      Copyright (c) 2002 Lineo
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#if defined(CONFIG_ARCH_SAMSUNG)
+#ifndef CONFIG_USBD_SMDK2500_BCLOCK 
+#define CONFIG_USBD_SMDK2500_BCLOCK 66
+#endif
+#endif
+
+/* Fast Trace Utility
+   This set of definitions and code is meant to provide a _fast_ debugging facility
+   (much faster than printk) so that time critical code can be debugged by looking
+   at a trace of events provided by reading a file in the procfs without affecting
+   the timing of events in the critical code.
+
+   The mechanism used it to allocate a (large) ring buffer of relatively small structures
+   that include location and high-res timestamp info, and up to 8 bytes of optional
+   data.  Values are stored and timestamps are taken as the critical code runs, but
+   data formatting and display are done during the procfs read, when more time is
+   available :).
+
+   Note that there is usually some machine dependent code involved in getting the
+   high-res timestamp, and there may be other bits used just to keep the overall
+   time impact as low as possible.
+
+   varargs style macros were avoided because there seems to be no way to avoid
+   a run-time check on the number of arguments if they are used, and the time penalty
+   doesn't seem to be worth the gain in utility.
+ */
+
+typedef enum acm_trace_types {
+        acm_trace_setup_n, acm_trace_msg_n, acm_trace_msg32_n, acm_trace_msg16_n, acm_trace_msg8_n
+} acm_trace_types_t;
+
+typedef struct acm_trace_msg {
+        char    *msg;
+} acm_trace_msg_t;
+
+typedef struct acm_trace_msg32 {
+        u32      val;
+        char    *msg;
+} acm_trace_msg32_t;
+
+typedef struct acm_trace_msg16 {
+        u16     val0;
+        u16     val1;
+        char    *msg;
+} acm_trace_msg16_t;
+
+typedef struct acm_trace_msg8 {
+        u8      val0;
+        u8      val1;
+        u8      val2;
+        u8      val3;
+        char    *msg;
+} acm_trace_msg8_t;
+
+
+typedef struct trace {
+        acm_trace_types_t        acm_trace_type;
+        char    *function;
+        u32     interrupts;
+#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA)
+        u32     oscr;
+#elif defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100)
+        u32     cp0_count;
+#elif defined(CONFIG_ARCH_SAMSUNG)
+        //u32     tcnt0;
+        u32     tcnt1;
+        //u64     jiffies;
+#else
+        u64     jiffies;
+#endif
+#if defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100) || defined(CONFIG_ARCH_MX1ADS)
+        u64     sofs;
+#endif
+        union {
+                acm_trace_msg_t        msg;
+                acm_trace_msg8_t       msg8;
+                acm_trace_msg16_t      msg16;
+                acm_trace_msg32_t      msg32;
+
+                struct usb_device_request       setup;
+
+        } trace;
+
+} acm_trace_t;
+
+#define TRACE_MAX_IS_2N 1
+#if defined(TRACE_MAX_IS_2N)
+#define TRACE_MAX  0x00008000
+#define TRACE_MASK 0x00007FFF
+#else
+#define TRACE_MAX       30000
+#endif
+
+extern int acm_trace_first;
+extern int acm_trace_last_read;
+extern int acm_trace_next;
+
+extern acm_trace_t *acm_traces;
+
+#ifdef CONFIG_USBD_ACM_TRACE
+
+acm_trace_t *ACM_TRACE_NEXT(char *fn, acm_trace_types_t acm_trace_type);
+
+#if 0
+static __inline__ acm_trace_t *ACM_TRACE_NEXT(char *fn, acm_trace_types_t acm_trace_type)
+{
+        acm_trace_t *p;
+	unsigned long flags;
+
+	// Get the next trace slot - this needs to be atomic.
+	local_irq_save (flags);
+        p = acm_traces + acm_trace_next;
+#if defined(TRACE_MAX_IS_2N)
+	acm_trace_next = (acm_trace_next + 1) & TRACE_MASK;
+        if (acm_trace_next == acm_trace_first) {
+		// We have wraparound, bump acm_trace_first
+		if (acm_trace_first == acm_trace_last_read) {
+			// We have to bump last read too.
+			acm_trace_last_read = (acm_trace_last_read + 1) & TRACE_MASK;
+		}
+		acm_trace_first = (acm_trace_first + 1) & TRACE_MASK;
+	}
+#else
+        if (TRACE_MAX <= ++acm_trace_next)
+		acm_trace_next = 0;
+
+        if (acm_trace_next == acm_trace_first) {
+		// We have wrap around, bump acm_trace_first
+		if (acm_trace_first == acm_trace_last_read) {
+			// We have to bump last read too.
+        		if (TRACE_MAX <= ++acm_trace_last_read)
+				acm_trace_last_read = 0;
+		}
+        	if (TRACE_MAX <= ++acm_trace_first)
+			acm_trace_first = 0;
+        }
+#endif
+	// End of next trace slot.
+	local_irq_restore (flags);
+
+#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA)
+        p->oscr = OSCR;
+#elif defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100)
+        //p->cp0_count = __read_32bit_c0_register(CP0_COUNT);
+        p->cp0_count = read_c0_count();
+#elif defined(CONFIG_ARCH_SAMSUNG)
+        //p->jiffies = jiffies;
+        //p->tcnt0 = *(volatile u32 *)TCNT0;
+        p->tcnt1 = *(volatile u32 *)TCNT1;
+#else
+        p->jiffies = jiffies;
+#endif
+#if defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100)
+        //p->sofs = au_readl(USBD_FRAMENUM);
+#endif
+
+        p->interrupts = udc_interrupts;
+        p->acm_trace_type = acm_trace_type;
+	p->function = fn;
+
+        acm_trace_next++;
+        acm_trace_next = (acm_trace_next == TRACE_MAX) ? 0 : acm_trace_next;
+
+        if (acm_trace_next == acm_trace_first) {
+                acm_trace_first++;
+                acm_trace_first = (acm_trace_first == TRACE_MAX) ? 0 : acm_trace_first;
+        }
+        //printk(KERN_INFO"first: %d next: %d interrupts: %d\n", acm_trace_first, acm_trace_next, udc_interrupts);
+
+        return p;
+}
+#endif
+
+static __inline__ void acm_trace_setup(char *fn, struct usb_device_request *setup)
+{
+        if (acm_traces) {
+                acm_trace_t *p = ACM_TRACE_NEXT(fn,acm_trace_setup_n);
+                memcpy(&p->trace.setup, setup, sizeof(struct usb_device_request));
+        }
+}
+#define TRACE_SETUP(setup) acm_trace_setup(__FUNCTION__,setup)
+
+static __inline__ void acm_trace_msg(char *fn, char *msg)
+{
+        if (acm_traces) {
+                acm_trace_t *p = ACM_TRACE_NEXT(fn,acm_trace_msg_n);
+                p->trace.msg.msg = msg;
+        }
+}
+#define TRACE_MSG(msg) acm_trace_msg(__FUNCTION__,msg)
+
+
+static __inline__ void acm_trace_msg_1xU32(char *fn, char *fmt, u32 val)
+{
+        if (acm_traces) {
+                acm_trace_t *p = ACM_TRACE_NEXT(fn,acm_trace_msg32_n);
+                p->trace.msg32.val = val;
+                p->trace.msg32.msg = fmt;
+        }
+}
+#define TRACE_MSG1(fmt,val) acm_trace_msg_1xU32(__FUNCTION__,fmt,(u32)val)
+
+static __inline__ void acm_trace_msg_2xU16(char *fn, char *fmt, u16 val0, u16 val1)
+{
+        if (acm_traces) {
+                acm_trace_t *p = ACM_TRACE_NEXT(fn,acm_trace_msg16_n);
+                p->trace.msg16.val0 = val0;
+                p->trace.msg16.val1 = val1;
+                p->trace.msg16.msg = fmt;
+        }
+}
+#define TRACE_MSG2(fmt,val0,val1) acm_trace_msg_2xU16(__FUNCTION__,fmt,(u16)val0,(u16)val1)
+
+static __inline__ void acm_trace_msg_4xU8(char *fn, char *fmt, u8 val0, u8 val1, u8 val2, u8 val3)
+{
+        if (acm_traces) {
+                acm_trace_t *p = ACM_TRACE_NEXT(fn,acm_trace_msg8_n);
+                p->trace.msg8.val0 = val0;
+                p->trace.msg8.val1 = val1;
+                p->trace.msg8.val2 = val2;
+                p->trace.msg8.val3 = val3;
+                p->trace.msg8.msg = fmt;
+        }
+}
+#define TRACE_MSG4(fmt,val0,val1,val2,val3) acm_trace_msg_4xU8(__FUNCTION__,fmt,(u8)val0,(u8)val1,(u8)val2,(u8)val3)
+
+#else
+
+#define TRACE_SETUP(setup)
+#define TRACE_MSG(msg)
+#define TRACE_MSG1(fmt,val)
+#define TRACE_MSG2(fmt,val0,val1)
+#define TRACE_MSG4(fmt,val0,val1,val2,val3)
+
+#endif
+
+int acm_trace_init (char *str);
+void acm_trace_exit (char *str);
+
diff -Nru a/drivers/usbd/au1x00_bi/Config.in b/drivers/usbd/au1x00_bi/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/au1x00_bi/Config.in	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,27 @@
+
+# USB device configuration from the device viewpoint (e.g. Linux running inside a USB device, not as host)
+#
+# Copyright (c) 2002-2003 Belcarra
+#
+
+#
+# CONFIG_SOC_AU1X00 and/or CONFIG_MIPS_AU1X00 are the new and preferred tags (as of 2.4.20)
+#
+# CONFIG_MIPS_AU1000, CONFIG_MIPS_AU1100, CONFIG_MIPS_AU1500 are deprecated (prior to 2.4.20)
+#
+
+if [ "$CONFIG_SOC_AU1X00" = "y" -o "$CONFIG_MIPS_AU1X00" = "y" -o "$CONFIG_CPU_AU1X00" = "y" -o "$CONFIG_MIPS_AU1500" = "y" -o "$CONFIG_MIPS_AU1100" = "y" -o "$CONFIG_MIPS_AU1000" = "y" ]
+then	
+   mainmenu_option next_comment
+
+   comment 'AMD AU1X000 Bus Interface'
+
+   dep_tristate '  AU1X00 (AMD/Alchemy) support' CONFIG_USBD_AU1X00_BUS $CONFIG_USBD
+   if [ "$CONFIG_USBD_AU1X00_BUS" = "m" -o "$CONFIG_USBD_AU1X00_BUS" = "y"  ]; then
+      int  '  AU1X00  System Clock' CONFIG_USBD_AU1X00_SCLOCK 400 
+   fi
+   define_bool CONFIG_AU1000_USB_DEVICE y
+   define_bool CONFIG_AU1X00_USB_DEVICE y
+
+   endmenu
+fi
diff -Nru a/drivers/usbd/au1x00_bi/Makefile b/drivers/usbd/au1x00_bi/Makefile
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/au1x00_bi/Makefile	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,88 @@
+#
+# Makefile for the kernel USBD (device not host) drivers.
+#
+# Copyright (c) 2002 Belcarra
+
+# Subdirs.
+# This is a bit complex, because some subdirs are for
+# proprietary code, and are simply not present in a
+# general distribution.
+
+# The all-CAPS *_DIRS get nuked in the new versions
+# of Rules.make, so use only the subdir-* methods.
+subdir-y 	:=
+subdir-m 	:=
+subdir-n 	:=
+subdir- 	:=
+
+# The target object and module list name.
+
+O_TARGET	:= au1x00_bi.o
+
+# Objects that export symbols.
+
+export-objs	:= 
+
+# Multipart objects.
+
+au1x00_bi-objs	:= au1x00.o usbd-bi.o trace.o
+
+# Optional parts of multipart objects.
+
+# Object file lists.
+
+#obj-y		:= usbd-bi.o 
+obj-y		:=
+obj-m		:=
+obj-n		:=
+obj-		:=
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_USBD_AU1X00_BUS)		+= au1x00_bi.o
+
+# Object files in subdirectories
+
+
+# Extract lists of the multi-part drivers.
+# The 'int-*' lists are the intermediate files used to build the multi's.
+
+multi-y		:= $(filter $(list-multi), $(obj-y))
+multi-m		:= $(filter $(list-multi), $(obj-m))
+int-y		:= $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
+int-m		:= $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
+
+# Files that are both resident and modular: remove from modular.
+
+obj-m		:= $(filter-out $(obj-y), $(obj-m))
+int-m		:= $(filter-out $(int-y), $(int-m))
+
+# Translate to Rules.make lists.
+
+O_OBJS		:= $(filter-out $(export-objs), $(obj-y))
+OX_OBJS		:= $(filter     $(export-objs), $(obj-y))
+M_OBJS		:= $(sort $(filter-out $(export-objs), $(obj-m)))
+MX_OBJS		:= $(sort $(filter     $(export-objs), $(obj-m)))
+MI_OBJS		:= $(sort $(filter-out $(export-objs), $(int-m)))
+MIX_OBJS	:= $(sort $(filter     $(export-objs), $(int-m)))
+
+# The global Rules.make.
+
+include $(TOPDIR)/Rules.make
+USBD=$(TOPDIR)/drivers/usbd
+BI_DIR=$(USBD)/au1x00_bi
+EXTRA_CFLAGS += -Wno-missing-prototypes -Wno-unused -Wno-format -I$(USBD) -I$(BI_DIR)
+EXTRA_CFLAGS_nostdinc += -Wno-missing-prototypes -Wno-unused -Wno-format -I$(USBD) -I$(BI_DIR)
+
+vpath %.c $(USBD)
+
+# Link rules for multi-part drivers.
+
+au1x00_bi.o: $(au1x00_bi-objs)
+	$(LD) -r -o $@ $(au1x00_bi-objs)
+
+# dependencies:
+
+# local
+
+
diff -Nru a/drivers/usbd/au1x00_bi/au1x00.c b/drivers/usbd/au1x00_bi/au1x00.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/au1x00_bi/au1x00.c	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,1268 @@
+/*
+ * usbd/au1x00_bi/au1x00.c -- USB Device Controller driver. 
+ *
+ *      Copyright (c) 2004 Belcarra
+ *
+ * Adapted from earlier work:
+ *      Copyright (c) 2002, 2003 Belcarra
+ *      Copyright (c) 2002 Lineo
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Notes
+ *
+ * 1. EP0 - packetsize of 8 does not work, this means that PIO cannot be used for IN (transmit).
+ * DMA must be used. Only 16, 32 and 64 are usable.
+ *
+ * Update: even though we couldn't program the endpoint size to 8, we just set it to 8 in the
+ * device descriptor and things work ok. This works and we don't need or use DMA for either
+ * direction for EP0.
+ *
+ * 2. For each BULK transfer the first N packets must be sent with the full packetsize and the
+ * last must be sent as a short packet. The packetsize must be set before enabling the DMA.
+ *
+ * 3. For IN endpoints an interrupt will be generated for EVERY IN packet including ones that
+ * will be NAK'd. To reduce overhead the interrupt should only be enabled when there is data
+ * being sent and we need to know when it has been sent.
+ *
+ * Update: for new silicon this is not true, the UDC will interrupt less often and will NAK
+ * until the interrupt is cleared.
+ *
+ * 4. The AU1x00 auto-acks many setup commands and is very sensitive to latency of the irq
+ * handler processing the interrupt and clearing the ep0 fifo. Like the pxa we need to spool the
+ * requests and process later when we receive a request for data.  There is a special test in
+ * the bulk data receive handler to check if there are spooled requests to process.
+ *
+ * Update: this has been mostly mitigated by reducing all upper layer processing in recv_setup,
+ * event_irq etc so that they complete in less than a milli-second. This simplifies things and
+ * we can now pass UUT tests.
+ *
+ * 5. There is a time sensitive problem in au_in_epn(). Without a udelay(8) sending large
+ * packets will eventually stall interrupts.
+ *
+ * Update: this is not required for new silicon.
+ *
+ * 6. When receiving data (Bulk OUT) the UDC will not start NAKing until and unless the receive
+ * FIFO is full. This means that there is no reliable way to determine the end of one packet and
+ * the beginning of the next if there is any undue latency in handling the interrupt for the
+ * first packet.
+ *
+ * The Belcarra Windows and Macintosh network class drivers have an option that can be used
+ * to pad all outgoing packets to a multiple of 8 bytes. This helps eliminate this problem.
+ *
+ * Update: this is not a problem with new silicon, it NAK's until the interrupt is cleared.
+ *
+ * 8. The original AU1x00 UDC generated an interrupt for EVERY IN packet. If we do not have
+ * any data we need to disable the interrupt for the endpoint to eliminate the overhead
+ * for processing them. The interrupt must be re-enabled when DMA is finished and we
+ * actually want to know that the endpoint has finished.
+ *
+ * Leaving the interrupt enabled also interfers with the DMA process. It is not apparant
+ * why.
+ *
+ * Update: This has been fixed on the new silicon but it doesn't hurt to continue to do this.
+ *
+ * 9. The DMA functions replace the equivalent routines in au1100_dma.h except that they pass
+ * the actual struct dma_chan pointer instead of the channel number.  The bounds checking and
+ * table lookup to derive this information amounts to a substantial increase in code size and
+ * latency which can simply be avoided by using the structure address directly and/or doing the
+ * equivalent i/o directly.
+ */
+
+
+#include <udc.h>
+#include "au1x00.h"
+
+#include <asm/io.h>
+#include <asm/au1000.h>
+#include <asm/au1000_dma.h>
+#include <asm/mipsregs.h>
+
+
+#ifdef CONFIG_MIPS_FREEHAND
+#include <linux/i2c.h>
+#include <linux/sensors.h>      /* for reading serial number */
+#endif
+
+MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com");
+MODULE_DESCRIPTION ("USB Device AU1x00  Bus Interface");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17)
+MODULE_LICENSE("GPL");
+#endif
+USBD_MODULE_INFO ("au1x00_bi 2.0-beta");
+const char *usbd_bi_module_info(void)
+{ return __usbd_module_info; }
+
+#undef CHECK_LATENCY
+#undef RECORD_LATENCY
+#undef MAX_INTR_LOOP_STATS   10
+#if defined(MAX_INTR_LOOP_STATS)
+static u32 interrupt_loop_stats[MAX_INTR_LOOP_STATS+1];
+#endif
+#ifdef  RECORD_LATENCY
+#define CP0_COUNTS 50
+u32 cp0_counts[CP0_COUNTS];
+u32 cp0_record;
+#endif
+u32 cp0_count;
+unsigned int udc_interrupts;
+unsigned int udc_saw_bus_activity;
+int au_halt_dma_expired;
+u32 au1x00_new_silicon;
+u32 au1x00_inten;
+static int udc_connected_status;
+
+__u8 au1x00_config_bulk[25] = {
+        0x04, (USB_ENDPOINT_CONTROL << 4) | (EP0_PACKETSIZE & 0x380) >> 7,                            // 1
+        (EP0_PACKETSIZE & 0x7F) << 1, 0x00, 0x00,
+        0x24, (USB_ENDPOINT_BULK << 4) | USB_DIR_IN | (MAX_EPN_PACKET_SIZE & 0x380) >> 7,             // 6
+        (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x02, 
+        0x34, (USB_ENDPOINT_BULK << 4) | USB_DIR_IN | (MAX_EPN_PACKET_SIZE & 0x380) >> 7,             // 11
+        (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x03, 
+        0x44, (USB_ENDPOINT_BULK << 4) | USB_DIR_OUT | (MAX_EPN_PACKET_SIZE & 0x380) >> 7,            // 16
+        (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x04,
+        0x54, (USB_ENDPOINT_BULK << 4) | USB_DIR_OUT | (MAX_EPN_PACKET_SIZE & 0x380) >> 7,            // 21
+        (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x05,
+};
+
+/* ********************************************************************************************* */
+/* Note - The endpoints names are confusing. To simplify mapping of the interrupt request lines
+ * we use a numbering of the physical endpoints of 0-5, which also matches the fifo numbering
+ * (see the config block).
+ *
+ *   Physical  FIFO    Name    Direction   Logical
+ *      0       0       EP0     OUT         0
+ *      1       1       EP0     IN          0
+ *      2       2       EP1     IN          81
+ *      3       3       EP2     IN          82
+ *      4       4       EP3     OUT         3
+ *      5       5       EP4     OUT         4
+ *
+ * The ep_regs array maps a physical endpoint number to the registers required to access the udc
+ * for that endpoint. Note that ep0 is always accessed via 0.  The epl2p array maps the logical
+ * addresses back to the physical number.  The epp2l array maps the physical number to the
+ * logical endpoint address
+ */
+struct ep_regs ep_regs[6] = {
+        { rd: USBD_EP0RD, rds: USBD_EP0RDSTAT, /*rx_id: DMA_ID_USBDEV_EP0_RX,*/ cs: USBD_EP0CS, rx_str: "EP0 OUT RD", 
+          wr: USBD_EP0WR, wrs: USBD_EP0WRSTAT, tx_id: DMA_ID_USBDEV_EP0_TX,                 tx_str: "EP0 IN WR",},  
+        { rds: 0, wrs: 0, indma: -1, outdma: -1, },
+        { wr: NUSBD_EP1WR, wrs: NUSBD_EP1WRSTAT, tx_id: NDMA_ID_USBDEV_EP1_TX, cs: NUSBD_EP1CS, tx_str: "EP1 IN WR",}, 
+        { wr: NUSBD_EP2WR, wrs: NUSBD_EP2WRSTAT, tx_id: NDMA_ID_USBDEV_EP2_TX, cs: NUSBD_EP2CS, tx_str: "EP2 IN WR",}, 
+
+        { rd: NUSBD_EP3RD, rds: NUSBD_EP3RDSTAT, rx_id: NDMA_ID_USBDEV_EP3_RX, cs: NUSBD_EP3CS, rx_str: "EP3 OUT RD",},
+        { rd: NUSBD_EP4RD, rds: NUSBD_EP4RDSTAT, rx_id: NDMA_ID_USBDEV_EP4_RX, cs: NUSBD_EP4CS, rx_str: "EP4 OUT RD",},
+};
+__u8 epl2p[6] = { 0x00, 0x02, 0x03, 0x04, 0x05, 0x00, };        // map logical to physical
+__u8 epp2l[6] = { 0x00, 0x00, 0x81, 0x82, 0x03, 0x04, };        // map physical to logical
+
+static __inline__ void au_fifo_read(struct ep_regs *ep, unsigned char * cp, int bytes)
+{
+        u32 rd = ep->rd;
+        for (; bytes--; *cp++ = au_readl(rd));
+}
+
+static __inline__ void au_fifo_write(int ep, unsigned char * cp, int bytes)
+{
+        u32 wr = ep_regs[ep].wr;
+        for (; bytes--; au_writel(*cp++, wr));
+        ep_regs[ep].last = bytes;
+}
+
+void __inline__ au_inten(u32 inten)
+{
+        au1x00_inten = inten;
+        au_writel(au1x00_inten, USBD_INTEN);
+}
+
+void __inline__ udc_epn_interrupt_enable(int epn)
+{
+        au_inten(au1x00_inten | (1 << epn));
+}
+
+void __inline__ udc_epn_interrupt_disable(int epn)
+{
+        au_inten(au1x00_inten & ~(1 << epn));
+        au_writel((1 << epn) , USBD_INTSTAT);
+}
+
+static void __inline__ send_zlp(unsigned char epn)
+{
+        au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep_regs[epn].wrs);
+        au_writel(0 << 1, ep_regs[epn].cs);
+        au_writel(0, ep_regs[epn].wr);
+}
+/* ********************************************************************************************* */
+#define AU_DMA_HALT_POLL 0x1000
+static void __inline__ au_halt_dma(struct dma_chan *chan)
+{
+        int i;
+        au_writel(DMA_GO, chan->io + DMA_MODE_CLEAR);
+        for (i = 0; i < AU_DMA_HALT_POLL; i++) 
+                RETURN_IF (au_readl(chan->io + DMA_MODE_READ) & DMA_HALT);
+        au_halt_dma_expired++;
+}
+
+static int __inline__ au_get_dma_residue(struct dma_chan *chan)
+{
+        int curBufCntReg = (au_readl(chan->io + DMA_MODE_READ) & DMA_AB) ? DMA_BUFFER1_COUNT : DMA_BUFFER0_COUNT;
+        return au_readl(chan->io + curBufCntReg) & DMA_COUNT_MASK;
+}
+
+static void __inline__ au_start_dma(struct dma_chan *chan, __u8 *bp, int len)
+{
+        if (au_readl(chan->io + DMA_MODE_READ) & DMA_AB) {
+                au_writel(DMA_D1, chan->io + DMA_MODE_CLEAR);
+                au_writel(len & DMA_COUNT_MASK, chan->io + DMA_BUFFER1_COUNT);
+                au_writel(0, chan->io + DMA_BUFFER0_COUNT);
+                au_writel(virt_to_phys(bp), chan->io + DMA_BUFFER1_START);
+                au_writel(DMA_BE1, chan->io + DMA_MODE_SET);
+        }
+        else {
+                au_writel(DMA_D0, chan->io + DMA_MODE_CLEAR);
+                au_writel(len & DMA_COUNT_MASK, chan->io + DMA_BUFFER0_COUNT);
+                au_writel(0, chan->io + DMA_BUFFER1_COUNT);
+                au_writel(virt_to_phys(bp), chan->io + DMA_BUFFER0_START);
+                au_writel(DMA_BE0, chan->io + DMA_MODE_SET);
+        }
+        au_writel(DMA_GO, chan->io + DMA_MODE_SET);
+}
+
+static void __inline__ au_start_dma_in(int indma, __u8 *bp, int len)
+{
+        au_start_dma(get_dma_chan(indma), bp, len);
+}
+
+static void __inline__ au_start_dma_out(int outdma, __u8 *bp, int wMaxPacketSize)
+{
+        dma_cache_inv((unsigned long) bp, wMaxPacketSize);
+        au_start_dma(get_dma_chan(outdma), bp, wMaxPacketSize);
+}
+
+static struct urb *au_rcv_complete_irq(struct usb_endpoint_instance *endpoint, int len, int urb_bad)
+{
+        return bi_rcv_complete_irq(endpoint, len, urb_bad);
+}
+
+/* ********************************************************************************************* */
+/* au_start_in_ep0 - start transmit
+ */
+static void au_start_in_ep0 (struct usb_endpoint_instance *endpoint, struct ep_regs *ep)
+{
+        struct urb *urb = endpoint->tx_urb;
+        int last = ep->last = endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->wMaxPacketSize);
+        TRACE_MSG16("START IN EP0 SENT: %d SENDING: %d", endpoint->sent, last);
+        RETURN_IF ((urb->actual_length - endpoint->sent) <= 0);
+        au_writel(last << 1, ep->cs); // XXX
+        au_fifo_write(0, urb->buffer + endpoint->sent, last);
+        endpoint->last = last;
+}
+
+/* au_in_ep0 - called to service an endpoint zero IN interrupt, data sent
+ */
+static void au_in_ep0(struct usb_endpoint_instance *endpoint, struct ep_regs *ep)
+{
+        u32 cs;
+        struct urb *tx_urb;
+        int last;
+        TRACE_MSG32("EP0 IN: tx_urb: %p", (int)endpoint->tx_urb);
+        if ((cs = au_readl(ep->cs)) & USBDEV_CS_STALL) {                // clear stall if present
+                TRACE_MSG32("CLEAR STALL %d", 0);
+                cs &= ~USBDEV_CS_STALL;
+                au_writel(cs, ep->cs);
+                return;
+        }
+        if (!(tx_urb = bi_tx_complete_irq(endpoint, 0))) {               // wait for setup if no more data
+                endpoint->state = WAIT_FOR_SETUP;
+                return;
+        }
+        TRACE_MSG8("EP0 IN actual: %d last: %d sent: %d flags: %x", endpoint->tx_urb->actual_length, 
+                        endpoint->last, endpoint->sent, endpoint->tx_urb->flags);
+        if (bi_tx_sendzlp(endpoint)) {                          // check if tx_urb we have is finished
+                TRACE_MSG("EP0 IN BULK - sending ZLP");
+                tx_urb->flags &= ~USBD_URB_SENDZLP;
+                send_zlp(0);
+                bi_tx_complete_irq(endpoint, 0);
+                return;
+        }
+        if (tx_urb->actual_length > endpoint->sent) {
+                if ((tx_urb->actual_length - endpoint->sent) < endpoint->wMaxPacketSize) 
+                        TRACE_MSG32("EP0 IN starting short packet %d", tx_urb->actual_length - endpoint->sent);
+                else 
+                        TRACE_MSG32("EP0 IN LEFT TO SEND %d", tx_urb->actual_length - endpoint->sent);
+                au_start_in_ep0(endpoint, ep);
+        }
+}
+
+/* au_out_ep0 - called to service an endpoint zero OUT interrupt, data received
+ */
+static void au_out_ep0(struct usb_endpoint_instance *endpoint, struct ep_regs *ep)
+{
+        struct usb_device_request request;
+        int i;
+        u32 cs;
+        u32 bytes;
+        TRACE_MSG("EP0 OUT");
+        cs = au_readl(ep->cs);                                  // check if host aborted transfer and flush the write fifo
+        bytes = au_readl(ep->rds) & USBDEV_FSTAT_FCNT_MASK;
+        if (endpoint->state == DATA_STATE_RECV) {
+                struct urb *rcv_urb = bi_rcv_next_irq(endpoint);
+                TRACE_MSG32("EP0 OUT: RECV: rcv_urb: %x", (int) rcv_urb);
+                if (rcv_urb) {
+                        au_fifo_read(ep, rcv_urb->buffer + rcv_urb->actual_length, bytes);
+                        if (au_rcv_complete_irq(endpoint, bytes, 0)) 
+                                return;
+                        au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds);
+                        endpoint->state = WAIT_FOR_SETUP;
+                        send_zlp(0);
+                        return;
+                }
+                endpoint->state = WAIT_FOR_SETUP;
+        }
+        bi_tx_cancelled_irq(endpoint);
+        bi_rcv_cancelled_irq(endpoint);
+        au_fifo_read(ep, (u8 *)&request, bytes);
+        au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs);
+        if (bytes != 8) {
+                TRACE_MSG32("ERROR SETUP SET not eight bytes: %d", bytes);
+                au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs);
+                return;
+        }
+        TRACE_MSG8("SETUP bmRequestType: %02x bRequest %02x state: %d status: %d", request.bmRequestType, 
+                        request.bRequest, usbd_bus->device_state, usbd_bus->status);
+        switch (request.bRequest) {                     // we need to simply ignore any of these
+        case USB_REQ_SET_ADDRESS:                       // Fake a bus reset IFF not state default and then process normally
+                BREAK_IF (usbd_bus->device_state == STATE_DEFAULT);
+                udc_saw_bus_activity = 0;
+                usbd_bus_event_irq (usbd_bus, DEVICE_RESET, 0);
+                usbd_bus_event_irq (usbd_bus, DEVICE_ADDRESS_ASSIGNED, 0);
+                break;
+        case USB_REQ_GET_DESCRIPTOR:                    // Fake a bus reset IFF suspended and then process normally
+                BREAK_IF (STATE_SUSPENDED != usbd_bus->device_state);
+                udc_saw_bus_activity = 0;
+                usbd_bus_event_irq (usbd_bus, DEVICE_RESET, 0);
+                usbd_bus_event_irq (usbd_bus, DEVICE_ADDRESS_ASSIGNED, 0);
+                break;
+        }
+        if (bi_recv_setup_irq(&request)) {
+                TRACE_MSG32("ep0 STALL %d", cs);
+                au_writel(USBDEV_CS_STALL, USBD_EP0CS);
+                return;
+        }
+        if (((request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) && le16_to_cpu (request.wLength)) {
+                TRACE_MSG32("ep0 Class H2D request %04x", le16_to_cpu(request.wLength));
+                endpoint->state = DATA_STATE_RECV;
+                return;
+        }                               
+        if ((request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) {
+                TRACE_MSG32("ep0 Class H2D request %04x", le16_to_cpu(request.wLength));
+                if ((request.bmRequestType & ~USB_REQ_DIRECTION_MASK)) {
+                        TRACE_MSG32("ep0 Class or Vendor, send ZLP %d", cs);
+                        send_zlp(0);
+                        return;
+                }
+        }
+        TRACE_MSG32("ep0 Class D2H request %04x", le16_to_cpu(request.wLength));
+}
+/* ********************************************************************************************* */
+/* au_start_in_bulk - start transmit
+ * The au1x00 will start to send when the first byte is loaded into the FIFO, either by
+ * DMA or PIO. The packetsize must be set first.
+ */
+static void au_start_in_bulk (unsigned int epn, struct usb_endpoint_instance *endpoint, struct ep_regs *ep)
+{
+        struct urb *urb = endpoint->tx_urb;
+        unsigned char *bp = urb->buffer + endpoint->sent;
+        int indma = ep->indma;
+        int last;
+        TRACE_MSG32("START IN BULK %d", epn);
+        RETURN_IF (!urb || (( (urb->actual_length - endpoint->sent) == 0) && !(urb->flags & USBD_URB_SENDZLP)));
+        last = ep->last = endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->wMaxPacketSize);
+        TRACE_MSG16("START IN BULK sent: %d last:%d", endpoint->sent, last);
+        au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); // XXX
+        if (!last) {
+                if (endpoint->tx_urb->flags & USBD_URB_SENDZLP) {
+                        TRACE_MSG("START IN BULK - zending ZLP");
+                        send_zlp(epn);
+                        endpoint->tx_urb->flags &= ~USBD_URB_SENDZLP;
+                }
+                return;
+        }
+        else if (au1x00_new_silicon  && (8 >= last)) {
+                au_writel(last << 1, ep->cs);
+                au_fifo_write(epn, bp, last);
+                return;
+        }
+        if (!au1x00_new_silicon) 
+                udc_epn_interrupt_disable(epn);
+        dma_cache_wback_inv((unsigned long) bp, last); 
+        au_writel(last << 1, ep->cs);
+        au_start_dma_in(indma, bp, last);
+}
+
+static void au_in_bulk(unsigned int epn, struct ep_regs *ep, struct usb_endpoint_instance *endpoint)
+{
+        struct urb *tx_urb;
+        int rc = 0;
+        u32 cs = au_readl(ep->cs);
+        u32 wrs = au_readl(ep->wrs);
+        TRACE_MSG16("BULK IN EPN - cs: %x wrs: %x", cs, wrs);
+        if (!au1x00_new_silicon) 
+                if (epn && (ep->last > 8)) {
+                        TRACE_MSG16("BULK IN EPN - DMA ACTIVE epn %d last %d", epn, ep->last);
+                        udc_epn_interrupt_disable(epn);
+                        return;
+                }
+        if (wrs) {                                                              // check for underflow or overflow
+                rc = 1;
+                if (wrs & USBDEV_FSTAT_UF) {
+                        TRACE_MSG16("BULK IN EPN - UF epn %d wrs: %x", epn, wrs);
+                        rc = 1;                                                 // set rc to indicate an error
+                }
+                if (wrs & USBDEV_FSTAT_OF) 
+                        TRACE_MSG16("BULK IN EPN - OF epn %d wrs: %x", epn, wrs);
+                au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs);     // flush the fifo 
+        }
+        if (cs & USBDEV_CS_NAK) {
+                RETURN_IF (ep->last && ((wrs&0x1f) == ep->last));
+                rc = 1;
+        }
+        if (cs & USBDEV_CS_STALL) {                                             // clear stall if present
+                TRACE_MSG32("BULK IN EPN - CLEAR STALL %d", epn);
+                cs &= ~USBDEV_CS_STALL;
+                au_writel(cs, ep->cs);
+                return;
+        }
+        TRACE_MSG8("BULK IN EPN epn: %d rc: %d last: %d sent: %d", epn, rc, endpoint->last, endpoint->sent);
+        if ((tx_urb = bi_tx_complete_irq(endpoint, rc))) {
+                if ((tx_urb->actual_length > endpoint->sent) || (endpoint->tx_urb->flags & USBD_URB_SENDZLP)) {
+                        au_start_in_bulk(epl2p[endpoint->bEndpointAddress&0xf], endpoint, ep);
+                        /* XXX magic delay - without this large packets will eventually stall the transmit
+                         * XXX and all traffic in both directions will stop. 
+                         */
+                        if (!au1x00_new_silicon) 
+                                udelay(8);
+                        TRACE_MSG32("BULK IN EPN - LEFT TO SEND %d", tx_urb->actual_length - endpoint->sent);
+                        return;
+                }
+        }
+        udc_epn_interrupt_disable(epn);                                         // disable interrupts
+}
+
+/* au_start_in_iso - start transmit
+ * The au1x00 will start to send when the first byte is loaded into the FIFO, either by DMA or
+ * PIO. The packetsize must be set first.
+ */
+static void au_start_in_iso (unsigned int epn, struct usb_endpoint_instance *endpoint, struct ep_regs *ep)
+{
+        struct urb *urb = endpoint->tx_urb;
+        unsigned char *bp = urb->buffer + endpoint->sent;
+        int last;
+        TRACE_MSG16("START IN ISO actual: %d sent: %d", urb->actual_length, endpoint->sent);
+        RETURN_IF ((urb->actual_length - endpoint->sent) == 0);
+        last = ep->last = endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->wMaxPacketSize);
+        TRACE_MSG16("START IN ISO last: %d packetSize: %d", last, endpoint->wMaxPacketSize);
+        au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); // XXX
+        au1x00_new_silicon ? udc_epn_interrupt_enable : udc_epn_interrupt_disable (epn);
+        dma_cache_wback_inv((unsigned long) bp, last);
+        au_writel(last << 1, ep->cs);
+        au_start_dma_in(ep->indma, bp, last);
+}
+
+static void au_in_iso(unsigned int epn, struct ep_regs *ep, struct usb_endpoint_instance *endpoint)
+{
+        struct urb *tx_urb = bi_tx_complete_irq(endpoint, 0);
+        u32 cs = au_readl(ep->cs);
+        u32 wrs = au_readl(ep->wrs);
+        TRACE_MSG16("ISO IN EPN - cs: %x wrs: %x", cs, wrs);
+        if (!au1x00_new_silicon) 
+                if (epn && (ep->last > 8)) {
+                        udc_epn_interrupt_disable(epn);
+                        return;
+                }
+        TRACE_MSG8("ISO IN EPN epn: %d last: %d sent: %d", epn, endpoint->last, endpoint->sent, 0);
+        ep->last = 0;
+        if ((tx_urb = endpoint->tx_urb) && (tx_urb->actual_length > endpoint->sent)) {
+                au_start_in_iso(epl2p[endpoint->bEndpointAddress&0xf], endpoint, ep);
+                /* XXX magic delay - without this large packets will eventually stall the transmit
+                 * XXX and all traffic in both directions will stop. 
+                 */
+                if (!au1x00_new_silicon) 
+                        udelay(8);
+                TRACE_MSG32("ISO IN EPN - LEFT TO SEND %d", tx_urb->actual_length - endpoint->sent);
+                return;
+        }
+        else 
+                TRACE_MSG("ISO IN EPN - nothing to send");
+        udc_epn_interrupt_disable(epn);                                 // disable interrupts
+}
+/* ********************************************************************************************* */
+
+static void au_start_out_bulk(unsigned int epn, struct usb_endpoint_instance *endpoint, struct ep_regs *ep)
+{
+        int outdma = ep->outdma;
+        TRACE_MSG16("START OUT BULK %d %d", epn, endpoint->wMaxPacketSize);
+        if (!endpoint->rcv_urb) {
+                TRACE_MSG("START OUT BULK DISABLE");
+                udc_epn_interrupt_disable(epn);
+                return;
+        }
+        if (endpoint->rcv_error) {
+                TRACE_MSG("START OUT BULK reseting rcv_error");
+                endpoint->rcv_error = 0;
+        }
+        au_start_dma_out(outdma, endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length, endpoint->wMaxPacketSize);
+}
+
+static void au_out_bulk(unsigned int epn, struct ep_regs *ep, struct usb_endpoint_instance *endpoint)
+{
+        int bytes = 0;
+        int outdma = ep->outdma;
+        struct dma_chan *chan = get_dma_chan(outdma);
+        struct urb *urb;
+        struct urb *completed_urb = NULL;
+        u32 cs;
+        u32 rds;
+        u32 nrds;
+        au_halt_dma(chan);
+        cs = au_readl(ep->cs);
+        rds = au_readl(ep->rds);
+        TRACE_MSG16("BULK OUT CS: %04x RD: %04x", cs, rds);
+        bytes = endpoint->wMaxPacketSize - au_get_dma_residue(chan);
+        if (!(urb = bi_rcv_next_irq(endpoint))) {
+                TRACE_MSG("BULK OUT EPN - no rcv_urb");
+                udc_epn_interrupt_disable(epn);
+                return;
+        }
+        /* The original AU1X00 UDC design will continue to receive data as long as there is room
+         * in the FIFO. We cannot tell when we are at the end of a packet and/or have the start
+         * of a new one.
+         *
+         * There are only two scenarios that are guaranteed (almost) to be correct:
+         *
+         *      64 bytes of data from DMA, empty fifo, continue Bulk OUT < 60 bytes of data and
+         *      < 4 bytes in fifo, end Bulk OUT.
+         *
+         * There may be a third scenario that is ok:
+         *
+         *      0 bytes dma, 0 bytes in fifo, NAK
+         *
+         * Everything else is an error. In all cases we assume that it is safer to drop data
+         * than to accept it in error. This allows CRC or size protected encapsulations to
+         * notice bulk transfers received with errors.
+         *
+         * In general none of the policies or strategies are able to cope with all errors
+         * without missing errors and dropping good data.  The intent is to minimize the amount
+         * of potentially bad data getting to the function driver while minimizing the amount of
+         * good data that is dropped.
+         *
+         * The new silicon mitigates this problem for non control endpoints because it will NAK
+         * additional data until the interrupt service flag is reset.
+         * 
+         * Start with generic error tests, OF, UF or NAK indicate an error we cannot recover
+         * from, start flushing until end of current bulk transfer (wait for a short packet)
+         *
+         */
+        if (rds & (USBDEV_FSTAT_OF | USBDEV_FSTAT_UF)) {
+                TRACE_MSG16("BULK OUT FLUSHING %d length: %d", bytes, urb->actual_length);
+                THROW(start_flushing);
+        }
+        rds = rds & USBDEV_FSTAT_FCNT_MASK;
+        nrds = au_readl(ep->rds);
+        if (64 == bytes) {
+                TRACE_MSG16("BULK OUT 64 BYTES %d length: %d", bytes, urb->actual_length);
+                /* full size packet received, check that we are not flushing and that the FIFO
+                 * does not have any data. If there is data in the FIFO we may not be able to
+                 * restart DMA in time, so start flushing 
+                 */
+                if (endpoint->rcv_error) {
+                        TRACE_MSG8("FULL PACKET bytes: %d rds: %d nrds: %d cp0: %d CONTINUE FLUSHING", 
+                                        bytes, rds, nrds, cp0_count);
+                        THROW(start_flushing);
+                }
+                if ((nrds > 6)  && (nrds < 8) ) {
+                        TRACE_MSG8("FIFO not empty bytes: %d rds: %d nrds: %d cp0: %d START FLUSHING nrds > 6 < 8", 
+                                        bytes, rds, nrds, cp0_count);
+                        THROW(start_flushing);
+                }
+                if (!urb->actual_length) 
+                        TRACE_MSG8("PACKET ok bytes: %d rds: %d nrds: %d cp0: %d ACCEPTING 64 bytes", 
+                                        bytes, rds, nrds, cp0_count);
+                au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds);
+                au_rcv_complete_irq(endpoint, 64, 0);
+                if (cs & USBDEV_CS_NAK) 
+                        if (nrds && (nrds < 8) ) {
+                                TRACE_MSG8("NAK bytes: %d rds: %d nrds: %d cp0: %d START FLUSHING CS_NAK", 
+                                                bytes, rds, nrds, cp0_count);
+                                THROW(start_flushing);
+                        }
+        }
+        else if ((cs & USBDEV_CS_NAK) && (!nrds || (nrds == 8)) ) {
+                TRACE_MSG16("BULK OUT  NAK BYTES %d length: %d", bytes, urb->actual_length);
+                /* a nak'd packet may be ok to ignore IFF the FIFO is empty(?) or completely full.
+                 */
+                if (endpoint->rcv_error) {
+                        TRACE_MSG8("NAK bytes: %d rds: %d nrds: %d cp0: %d CONTINUE FLUSHING", 
+                                        bytes, rds, nrds, cp0_count);
+                        THROW(start_flushing);
+                }
+                au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds);
+        }
+        else {
+                TRACE_MSG16("BULK OUT < 64 BYTES %d length: %d", bytes, urb->actual_length);
+                /* short packet by DMA, additional data for this packet in FIFO, more than 3
+                 * bytes is probably an error.
+                 */
+                if ((cs & USBDEV_CS_NAK) && nrds && (nrds < 8) ) {
+                        TRACE_MSG8("BULK OUT NAK bytes: %d rds: %d nrds: %d cp0: %d START FLUSHING", 
+                                        bytes, rds, nrds, cp0_count);
+                        THROW(start_flushing);
+                }
+                if (nrds > 4) {
+                        TRACE_MSG8("BULK OUT SHORT PACKET by DMA full FIFO bytes: %d rds: %d nrds: %d cp0: %d START FLUSHING", 
+                                        bytes, rds, nrds, cp0_count);
+                        THROW(start_flushing);
+                }
+                TRACE_MSG8("BULK OUT SHORT bytes: %d rds: %d nrds: %d cp0: %d reading fifo", bytes, rds, nrds, cp0_count);
+                au_fifo_read(ep, urb->buffer + urb->actual_length + bytes, nrds);
+                bytes += nrds;
+                TRACE_MSG32("BULK OUT < 64 BYTES %d", bytes);
+                au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds);
+                if (!endpoint->rcv_error) {
+                        TRACE_MSG("BULK OUT COMPLETED URB");
+                        au_rcv_complete_irq(endpoint, bytes, 0);
+                }
+                else {
+                        TRACE_MSG("BULK OUT - FLUSHING URB - reseting rcv_error");
+                        endpoint->rcv_error = 0;
+                }
+        }
+        CATCH(start_flushing) {
+                TRACE_MSG("BULK OUT - START FLUSHING URB");
+                endpoint->rcv_error = 1;
+                endpoint->rcv_urb->actual_length = 0;
+                au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds);
+                bytes = 0;
+        }
+        TRACE_MSG("BULK OUT - RESTARTING");
+        au_start_out_bulk(epn, endpoint, ep);
+}
+
+static void au_start_out_iso(unsigned int epn, struct usb_endpoint_instance *endpoint, struct ep_regs *ep)
+{
+        int outdma = ep->outdma;
+        RETURN_IF(!endpoint->rcv_urb);
+        au_start_dma_out(outdma, endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length, endpoint->wMaxPacketSize);
+}
+
+static void au_out_iso(unsigned int epn, struct ep_regs *ep, struct usb_endpoint_instance *endpoint)
+{
+        int bytes = 0;
+        int outdma = ep->outdma;
+        struct dma_chan *chan = get_dma_chan(outdma);
+        struct urb *urb;
+        u32 cs;
+        u32 rds;
+        au_halt_dma(chan);
+        cs = au_readl(ep->cs);
+        rds = au_readl(ep->rds);
+        if (!endpoint) {
+                au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds);
+                return;
+        }
+        if (!(urb = endpoint->rcv_urb)) {
+                TRACE_MSG16("ISO OUT EPN - rcv_urb was NULL bytes: %d rds: %d", bytes, rds);
+                au_rcv_complete_irq(endpoint, bytes, 1);
+                TRACE_MSG("ISO OUT setting rcv_error");
+        }
+        bytes = endpoint->wMaxPacketSize - au_get_dma_residue(chan);
+        rds = rds & USBDEV_FSTAT_FCNT_MASK;
+        au_fifo_read(ep, urb->buffer + urb->actual_length + bytes, rds);
+        bytes += rds;
+        au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds);
+        au_rcv_complete_irq(endpoint, bytes, 0);
+        au_start_out_iso(epn, endpoint, ep);
+}
+/* ********************************************************************************************* */
+/* udc_tx_dma_done - TX DMA interrupt handler
+ */
+static void udc_tx_dma_done(int irq, void *dev_id, struct pt_regs *regs)
+{
+        int epn = (int) dev_id;
+        struct usb_endpoint_instance *endpoint = usbd_bus->endpoint_array + epn;
+        struct ep_regs *ep = &ep_regs[epn];
+        int residue;
+        int indma = irq - 6;
+        struct dma_chan *chan = get_dma_chan(indma);
+        u32 mode = au_readl(chan->io + DMA_MODE_READ);
+        udc_interrupts++;
+        TRACE_MSG32("TX DMA done: mode: %x", mode);
+        if (mode & DMA_D0) 
+                au_writel(DMA_D0, chan->io + DMA_MODE_CLEAR);
+        if (mode & DMA_D1) 
+                au_writel(DMA_D1, chan->io + DMA_MODE_CLEAR);
+        RETURN_IF (!epn);
+        au_halt_dma(chan);
+        residue = au_get_dma_residue(chan);
+        TRACE_MSG16("TX DMA IRQ - epn %d residue %d", epn, residue);
+        if (!au1x00_new_silicon) {
+                /* XXX magic delay - without this large packets will eventually stall the transmit
+                 * XXX and all traffic in both directions will stop. 
+                 */
+                udelay(8);
+                bi_tx_complete_irq (endpoint, residue?1:0);
+                udc_epn_interrupt_enable(epl2p[endpoint->bEndpointAddress&0xf]);
+        }
+        ep->last = 0;
+}
+
+/* udc_int_req - usb interrupt handler
+ */
+static void udc_int_req (int irq, void *dev_id, struct pt_regs *regs)
+{
+        u32 intstat;
+        struct ep_regs *ep;
+#if defined(MAX_INTR_LOOP_STATS)
+        u32 loopcount = 0;
+#endif
+#ifdef  RECORD_LATENCY
+        cp0_count = (read_c0_count(CP0_COUNT) - cp0_record) >> 9;
+        if (cp0_count < CP0_COUNTS) 
+                cp0_counts[cp0_count]++;
+#endif
+#ifdef  CHECK_LATENCY
+        u32 cp0_count = read_c0_count(CP0_COUNT);
+#endif
+        udc_interrupts++;
+#if 0
+        if (udc_interrupts > 2000) {
+                TRACE_MSG("UDC_INT call udc_disable_interrupts");
+                au_inten(0);
+                return;
+        }
+#endif
+        while (( intstat = au_readl(USBD_INTSTAT) & au1x00_inten)) {    // read and reset interrupt status register
+
+                int epn;
+#if 1                
+                for (epn = 2; epn < 6; epn++) {
+                        CONTINUE_IF (!(intstat & (1 << epn)));
+#else
+                // NOT TESTED
+                u32 local_intstat = intstat;
+                TRACE_MSG16("INTSTAT: %04x %04x", intstat, au_readl(USBD_INTEN));
+                while (local_intstat & 0x3f) {
+                        int epn = 31 - au_clz(local_intstat);
+                        local_intstat &= ~(1<<epn);
+#endif
+                        if (udc_saw_bus_activity) {
+                                udc_saw_bus_activity = 0;
+                                usbd_bus_event_irq (usbd_bus, DEVICE_BUS_ACTIVITY, 0);
+                        }
+                        ep = &ep_regs[epn];
+                        switch(ep->eptype) {
+                        case USB_DIR_IN | USB_ENDPOINT_BULK:
+                        case USB_DIR_IN | USB_ENDPOINT_INTERRUPT:
+                                //TRACE_MSG16("BULK IN %d %02x", epn, ep->eptype);
+                                au_in_bulk(epn, ep, usbd_bus->endpoint_array + epn);
+                                break;
+                        case USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS:
+                                //TRACE_MSG16("ISO IN %d %02x", epn, ep->eptype);
+                                au_in_iso(epn, ep, usbd_bus->endpoint_array + epn);
+                                break;
+                        case USB_DIR_OUT | USB_ENDPOINT_BULK:
+                        case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT:
+                                //TRACE_MSG16("BULK OUT %d %02x", epn, ep->eptype);
+                                au_out_bulk(epn, ep, usbd_bus->endpoint_array + epn);
+                                break;
+                        case USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS:
+                                //TRACE_MSG16("ISO OUT %d %04d", epn, au_readl(NUSBD_FRAMENUM));
+                                au_out_iso(epn, ep, usbd_bus->endpoint_array + epn);
+                                break;
+                        }
+                }
+                au_writel(intstat, USBD_INTSTAT);               // Only clear the interrupt(s) AFTER servicing OUT
+                /* even though we disable the bulk-in interrupt (endpoint 2) prior to enabling
+                 * DMA we always see one additional interrupt that is a NAK on that endpoint.
+                 */
+                CONTINUE_IF(!(intstat & au1x00_inten));
+                /* handle control endpoint and suspend interrupt
+                 */
+                if (intstat & ( ((1 << 0) | (1 << 1) | (1 << 3) | (1 << 5) | USBDEV_INT_SOF))) {
+                        if (intstat & (1 << 0)) 
+                                au_out_ep0(usbd_bus->endpoint_array + 0, &ep_regs[0]);
+                        if (intstat & (1 << 1)) 
+                                au_in_ep0(usbd_bus->endpoint_array + 0, &ep_regs[0]);
+                        if (intstat & USBDEV_INT_SOF) 
+                                if (USBD_SUSPENDED == usbd_bus->status) {
+                                        TRACE_MSG("SUS - ACTIVITY");
+                                        udc_saw_bus_activity++;
+                                }
+                }
+#if defined(MAX_INTR_LOOP_STATS)
+                loopcount += 1;                         // Gather stats on how many times this loop is performed. 
+#endif
+        }
+#if defined(MAX_INTR_LOOP_STATS)
+        interrupt_loop_stats[MIN(loopcount, MAX_INTR_LOOP_STATS)]++;
+#endif
+#if defined(CHECK_LATENCY)
+        TRACE_MSG32("USB IRQ - %d", read_c0_count(CP0_COUNT) - cp0_count);
+#endif
+#if defined(RECORD_LATENCY)
+        cp0_record = read_c0_count(CP0_COUNT);
+#endif
+}
+
+/* udc_int_sus -suspend interrupt handler
+ */
+static void udc_int_sus (int irq, void *dev_id, struct pt_regs *regs)
+{
+        udc_interrupts++;
+        TRACE_MSG16("SUS - INACTIVE device: %d status: %d", usbd_bus->device_state, usbd_bus->status);
+        switch(usbd_bus->status) {
+        case USBD_OPENING:
+        case USBD_OK:
+                usbd_bus_event_irq (usbd_bus, DEVICE_BUS_INACTIVE, 0);
+                break;
+        default:
+                break;
+        }
+}
+/* ********************************************************************************************* */
+/* udc_start_endpoint_in - start transmit
+ */
+void udc_start_endpoint_in(struct usb_endpoint_instance *endpoint)
+{
+        int epn = epl2p[endpoint->bEndpointAddress&0xf];
+        struct ep_regs *ep = &ep_regs[epn];
+        TRACE_MSG16("UDC START IN %02x %d", endpoint->bEndpointAddress, epn);
+        TRACE_MSG16("UDC START IN len: %d flags: %x", (unsigned int)endpoint->tx_urb->actual_length, endpoint->tx_urb->flags);
+        switch(endpoint->bmAttributes & USB_ENDPOINT_MASK) {
+        case USB_ENDPOINT_CONTROL:
+                TRACE_MSG32("UDC START IN EP0 %p", (u32)endpoint->rcv_urb);
+                au_in_ep0(endpoint, ep);
+                break;
+        case USB_ENDPOINT_BULK:
+        case USB_ENDPOINT_INTERRUPT:
+                TRACE_MSG32("UDC START IN BULK %p", (u32)endpoint->rcv_urb);
+                au_start_in_bulk(epn, endpoint, ep);
+                break;
+        case USB_ENDPOINT_ISOCHRONOUS:
+                TRACE_MSG32("UDC START IN ISO %p", (u32)endpoint->rcv_urb);
+                au_start_in_iso(epn, endpoint, ep);
+                break;
+        }
+        if (au1x00_new_silicon) 
+                udc_epn_interrupt_enable(epn);
+}
+
+/* udc_start_endpoint_out - start receive
+ */
+void udc_start_endpoint_out(struct usb_endpoint_instance *endpoint)
+{
+        int epn = epl2p[endpoint->bEndpointAddress&0xf];
+        struct ep_regs *ep = &ep_regs[epn];
+        TRACE_MSG16("UDC START OUT %02x %d", endpoint->bEndpointAddress, epn);
+        TRACE_MSG32("UDC START OUT len: %d", endpoint->rcv_urb->buffer_length);
+        switch(endpoint->bmAttributes & USB_ENDPOINT_MASK) {
+        case USB_ENDPOINT_CONTROL:
+                TRACE_MSG32("UDC START OUT EP0 %p", (u32)endpoint->rcv_urb);
+                break;
+        case USB_ENDPOINT_BULK:
+        case USB_ENDPOINT_INTERRUPT:
+                TRACE_MSG32("UDC START OUT BULK %p", (u32)endpoint->rcv_urb);
+                au_start_out_bulk(epn, endpoint, ep);
+                udc_epn_interrupt_enable(epn);
+                break;
+        case USB_ENDPOINT_ISOCHRONOUS:
+                TRACE_MSG32("UDC START OUT ISO %p", (u32)endpoint->rcv_urb);
+                au_start_out_iso(epn, endpoint, ep);
+                break;
+        }
+}
+
+void udc_cancel_in_irq(struct urb *urb)
+{
+        int epn = epl2p[urb->endpoint->bEndpointAddress&0xf];
+        struct ep_regs *ep = &ep_regs[epn];
+        TRACE_MSG("CANCEL IN URB");
+        au_in_bulk(epn, ep, urb->endpoint);
+}
+
+void udc_cancel_out_irq(struct urb *urb)
+{
+        int epn = epl2p[urb->endpoint->bEndpointAddress&0xf];
+        struct ep_regs *ep = &ep_regs[epn];
+        TRACE_MSG("CANCEL OUT URB");
+        if (epn)
+                au_out_bulk(epn, ep, urb->endpoint);
+}
+
+/* udc_init - initialize
+ */
+int udc_init (void)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19)
+#undef read_c0_prid
+#define read_c0_prid()  read_32bit_cp0_register(CP0_PRID)
+#endif
+        u32 cp0_prid = read_c0_prid();
+        switch (cp0_prid & CP0_PRID_SOC_MASK) {
+        case CP0_PRID_AU1000:
+        case CP0_PRID_AU1100:
+                au1x00_new_silicon = (cp0_prid & CP0_PRID_REV_MASK) >= 4;
+                printk(KERN_INFO"%s: AU1100 cp0_prid: new: %d\n", __FUNCTION__, au1x00_new_silicon);
+                break;
+        case CP0_PRID_AU1500:
+                au1x00_new_silicon = (cp0_prid & CP0_PRID_REV_MASK) >= 2;
+                printk(KERN_INFO"%s: AU1500 cp0_prid: new: %d\n", __FUNCTION__, au1x00_new_silicon);
+                break;
+        default:
+                printk(KERN_INFO"%s: UNKNOWN CPU cp0_prid: %08x UNKNOWN UDC\n", __FUNCTION__, cp0_prid);
+                return -EINVAL;
+        }
+        return 0;
+}
+
+/* udc_serial_init - set a serial number if available
+ */
+int udc_serial_init (void)
+{
+#if defined(CONFIG_MIPS_FREEHAND)
+        int length;
+        long data[16];          /* yeah a hack, but we KNOW it's 16 */
+        struct i2c_client *client;
+        char chData[16];
+        int i;
+
+        if (!(client = getFreeHandEepromClient())) {
+                printk(KERN_INFO"eeprom not ready when udc_serial_init called\n");
+                return -EINVAL;
+        }
+        eeprom_contents(client, SENSORS_PROC_REAL_READ, EEPROM_SYSCTL1, &length, (long *)data);
+
+        /* serial number is first 9 longs. But each long is just an ASCII char
+         * convert this to a string and then extract a 4 byte value from it
+         */
+        for (i = 0; i < 9; chData[i] = (char)data[i], i++);     /* trunc, is ok */
+        chData[9] = 0;                                          /* terminate string */
+        printk(KERN_INFO"%s: %s\n", __FUNCTION__, chData);
+        usbd_bus->serial_number_str = lstrdup(chData);
+        return 0;
+#else
+        return -EINVAL;
+#endif
+}
+
+/* udc_setup_ep - setup endpoint 
+ */
+void udc_setup_ep (unsigned int epn, struct usb_endpoint_instance *endpoint)
+{
+        RETURN_IF (epn); 
+        endpoint->state = WAIT_FOR_SETUP;
+}
+
+/* udc_attached - is the USB cable connected
+ * Return non-zero if cable is connected.
+ *
+ * udc_connect - enable pullup resistor
+ * Turn on the USB connection by enabling the pullup resistor.
+ *
+ * udc_disconnect - disable pullup resistor
+ * Turn off the USB connection by disabling the pullup resistor.
+ */
+
+#if defined(CONFIG_MIPS_PICOENGINE_MVCI)
+int udc_attached (void)
+{
+        return 1;
+}
+
+int udc_connected(void)
+{
+        return udc_connected_status;
+}
+
+void udc_connect (void)
+{
+        extern void pico_mvci_set_usb_pullup(int);
+        pico_mvci_set_usb_pullup(1);
+        udc_connected_status = 1;
+}
+
+void udc_disconnect (void)
+{               
+        extern void pico_mvci_set_usb_pullup(int);
+        pico_mvci_set_usb_pullup(0);
+        udc_connected_status = 0;
+}
+
+#elif defined(CONFIG_MIPS_FREEHAND_NATIVE)
+/* have to do this because version A boards don't have pullups 
+ * XXX checkme
+*/
+int udc_attached (void)
+{
+        return 1;
+}
+
+int udc_connected(void)
+{
+        return udc_connected_status;
+}
+
+void udc_connect (void)
+{
+        au1000gpio_set(GPIO01);
+        udc_connected_status = 1;
+}
+
+void udc_disconnect (void)
+{               
+        au1000gpio_clear(GPIO01);
+        udc_connected_status = 0;
+}
+
+#endif
+
+/* udc_framenum - get current framenum
+ */
+int udc_framenum (void)
+{
+        return au_readl(NUSBD_FRAMENUM);
+}
+
+/* udc_all_interrupts - enable interrupts
+ */
+void udc_all_interrupts (void)
+{
+        au_inten(0x0033|USBDEV_INT_SOF);                                // Only enable receive interrupts. 
+}
+
+/* udc_suspended_interrupts - enable suspended interrupts
+ */
+void udc_suspended_interrupts (void)
+{
+        au_inten(0x0033|USBDEV_INT_SOF);
+}
+
+/* udc_disable_interrupts - disable interrupts.
+ */
+void udc_disable_interrupts (void)
+{
+        au_inten(0);
+}
+
+/* udc_disable - disable the UDC
+ */
+void udc_disable (void)
+{
+        au_writel(0x0000, USBD_ENABLE);
+}
+
+/* udc_release_udc_irq - release UDC irq
+ */
+void udc_release_udc_irq (void)
+{
+        free_irq (AU1000_USB_DEV_REQ_INT, NULL);
+        free_irq (AU1000_USB_DEV_SUS_INT, NULL);
+#if defined(MAX_INTR_LOOP_STATS)
+        {
+                u32 lc;
+                for (lc = 0; lc <= MAX_INTR_LOOP_STATS; lc++) 
+                        if (interrupt_loop_stats[lc]) 
+                                printk(KERN_ERR "%s: interrupt loopcount[%02u] %9u\n", __FUNCTION__, lc,interrupt_loop_stats[lc]);
+                printk(KERN_INFO"%s: halt_dma_expired: %d\n", __FUNCTION__, au_halt_dma_expired);
+        }
+#endif
+#ifdef RECORD_LATENCY
+        {
+                int i;
+                for (i = 0; i < CP0_COUNTS; i++) 
+                        if (cp0_counts[i]) 
+                                printk(KERN_INFO"%s: cp0_counts[%d] %d\n", __FUNCTION__, i, cp0_counts[i]);
+        }
+#endif
+}
+
+/* udc_request_udc_irq - request UDC interrupt
+ */
+int udc_request_udc_irq (void)
+{
+	RETURN_EINVAL_IF (request_irq (AU1000_USB_DEV_REQ_INT, udc_int_req, SA_INTERRUPT, UDC_NAME "UDC Req", NULL));
+	if (request_irq (AU1000_USB_DEV_SUS_INT, udc_int_sus, SA_INTERRUPT, UDC_NAME "UDC Sus", NULL) != 0) {
+                udc_release_udc_irq();
+                free_irq (AU1000_USB_DEV_REQ_INT, NULL);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int request_dma(int ep, int id, char *str, void dma_done(int , void *, struct pt_regs *))
+{
+        int dma;
+        if ((dma = request_au1000_dma(id, str, dma_done, SA_INTERRUPT | SA_SAMPLE_RANDOM, (void *)ep)) < 0) {
+                printk(KERN_INFO"request_io[%d] dma: %d id: %x %s FAILED\n", ep, dma, id, str);
+                return -1;
+        }
+        return dma;
+}
+
+/* udc_request_io - request IO region
+ */
+int udc_request_io (void)
+{
+        int i;
+        for (i = 0; i < 6; i++) {
+                ep_regs[i].indma = ep_regs[i].tx_id ? request_dma(i, ep_regs[i].tx_id, ep_regs[i].tx_str, udc_tx_dma_done) : -1;
+                ep_regs[i].outdma = ep_regs[i].rx_id ? request_dma(i, ep_regs[i].rx_id, ep_regs[i].rx_str, NULL) : -1;
+        }
+        return 0;
+}
+
+/* udc_release_io - release IO region
+ */
+void udc_release_io (void)
+{
+        int j;
+        for (j = 0; j < 6; j++) {
+                struct ep_regs *ep = &ep_regs[j]; 
+                if (ep->indma != -1) {
+                        free_au1000_dma(ep->indma);
+                        ep->indma = -1;
+                }
+                if (ep->outdma != -1) {
+                        free_au1000_dma(ep->outdma);
+                        ep->outdma = -1;
+                }
+        }
+}
+
+int udc_assign_endpoint( __u8 physicalEndpoint, int used[6], struct usb_endpoint_map *endpoint_map, __u8 bmAttributes,
+                __u16 wMaxPacketSize, __u16 transferSize) 
+{
+        struct ep_regs *ep = &ep_regs[physicalEndpoint]; 
+        RETURN_EINVAL_IF(used[physicalEndpoint]);
+        endpoint_map->bEndpointAddress[0] = epp2l[physicalEndpoint];
+        endpoint_map->physicalEndpoint[0] = physicalEndpoint;
+        endpoint_map->wMaxPacketSize[0] = wMaxPacketSize;
+        endpoint_map->transferSize[0] = transferSize;
+        endpoint_map->bmAttributes[0] = bmAttributes;
+        used[physicalEndpoint]++;
+        ep->eptype = bmAttributes & 0x83;
+        return 0;
+}
+
+int udc_request_endpoints(struct usb_endpoint_map *endpoint_map_array, int endpointsRequested, 
+                struct usb_endpoint_request *requestedEndpoints)
+{
+        struct usb_device_description *device_description;
+        int i;
+        int used[6];
+        memset(used, 0, sizeof(used));
+        for (i = 0; i < endpointsRequested; i++) {
+                struct usb_endpoint_map *endpoint_map = endpoint_map_array + i;
+                u8 bmAttributes = requestedEndpoints[i].bmAttributes;
+                u16 transferSize = requestedEndpoints[i].fs_requestedTransferSize;
+                endpoint_map->bmAttributes[0] = bmAttributes;
+                endpoint_map->wMaxPacketSize[0] = 0x40;
+                switch(bmAttributes) {
+                case USB_DIR_OUT | USB_ENDPOINT_BULK:
+                case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT:
+                case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT:
+                        CONTINUE_IF(!udc_assign_endpoint(4, used, endpoint_map, bmAttributes, 0x40, transferSize));
+                        CONTINUE_IF(!udc_assign_endpoint(5, used, endpoint_map, bmAttributes, 0x40, transferSize));
+                        break;
+                case USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS:
+                        CONTINUE_IF(!udc_assign_endpoint(4, used, endpoint_map, bmAttributes, transferSize, transferSize));
+                        CONTINUE_IF(!udc_assign_endpoint(5, used, endpoint_map, bmAttributes, transferSize, transferSize));
+                        break;
+                case USB_DIR_IN | USB_ENDPOINT_BULK:
+                case USB_DIR_IN | USB_ENDPOINT_INTERRUPT:
+                case USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT:
+                        CONTINUE_IF(!udc_assign_endpoint(2, used, endpoint_map, bmAttributes, 0x40, transferSize));
+                        CONTINUE_IF(!udc_assign_endpoint(3, used, endpoint_map, bmAttributes, 0x40, transferSize));
+                        break;
+                case USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS:
+                        CONTINUE_IF(!udc_assign_endpoint(2, used, endpoint_map, bmAttributes, transferSize, transferSize));
+                        CONTINUE_IF(!udc_assign_endpoint(3, used, endpoint_map, bmAttributes, transferSize, transferSize));
+                        break;
+                }
+                CONTINUE_IF(bmAttributes & USB_ENDPOINT_OPT);
+                return -EINVAL;
+        }
+        return 0;
+}
+
+int udc_set_endpoints(int endpointsRequested, struct usb_endpoint_map *endpoint_map_array)
+{
+        int i, j;
+        __u8 config[25];
+        __u8 *cp;
+        memcpy(config, au1x00_config_bulk, sizeof(config));
+        for (i = 0; i < endpointsRequested; i++) {
+                struct usb_endpoint_map *endpoint_map = endpoint_map_array + i;
+                int epreq = endpoint_map->bmAttributes[0];
+                int eptype = epreq & USB_ENDPOINT_MASK;
+                int epdir = epreq & USB_ENDPOINT_DIR_MASK ? 0x8 : 0;
+                int epsize = endpoint_map->wMaxPacketSize[0];
+                int epaddr = epp2l[endpoint_map->physicalEndpoint[0]];
+                CONTINUE_IF(!endpoint_map->physicalEndpoint[0] || (endpoint_map->physicalEndpoint[0] > 5));
+                cp = config + ((endpoint_map->physicalEndpoint[0] - 1) * 5);
+                cp[0] = (epaddr & 0xf) << 4 | 0x4;
+                cp[1] = (eptype << 4) | epdir | (epsize & 0x380) >> 7;
+                cp[2] = (epsize & 0x7F) << 1;
+        } 
+        au_inten(0);                                                            // disable interrupts
+        au_writel(0x0002, USBD_ENABLE);                                         // reset controller
+        udelay(100);
+        au_writel(0x0003, USBD_ENABLE);                                         // enable controller
+        udelay(100);
+        for (cp = config, i = 0; i < 25; i++, au_writel(*cp++, USBD_CONFIG));   // feed the config into the UDC
+        return 0;
+}
+/* ********************************************************************************************* */
+struct udc_ops udc_ops = {
+        max_endpoints:  UDC_MAX_ENDPOINTS,
+        ep0_packetsize:  EP0_PACKETSIZE,
+        name:  UDC_NAME,
+        start_endpoint_in: udc_start_endpoint_in,
+	start_endpoint_out: udc_start_endpoint_out,
+        request_endpoints: udc_request_endpoints,
+        set_endpoints: udc_set_endpoints,
+        cancel_in_irq: udc_cancel_in_irq,
+        cancel_out_irq: udc_cancel_out_irq,
+        setup_ep: udc_setup_ep,
+#if defined(CONFIG_MIPS_PICOENGINE_MVCI) ||defined(CONFIG_MIPS_FREEHAND_NATIVE)
+        attached: udc_attached,
+        connected: udc_connected,
+        connect: udc_connect,
+        disconnect: udc_disconnect,
+#endif
+        framenum: udc_framenum,
+        all_interrupts: udc_all_interrupts,
+        suspended_interrupts: udc_suspended_interrupts,
+        disable_interrupts: udc_disable_interrupts,
+        disable: udc_disable,
+        init: udc_init,
+        request_udc_irq: udc_request_udc_irq,
+        release_udc_irq: udc_release_udc_irq,
+        release_io: udc_release_io,
+        serial_init: udc_serial_init,
+        request_io: udc_request_io,
+};
+
diff -Nru a/drivers/usbd/au1x00_bi/au1x00.h b/drivers/usbd/au1x00_bi/au1x00.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/au1x00_bi/au1x00.h	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,112 @@
+/*
+ * usbd/au1x00_bi/au1100.h
+ *
+ *      Copyright (c) 2004 Belcarra
+ *
+ * Adapted from earlier work:
+ *      Copyright (c) 2002, 2003 Belcarra
+ *      Copyright (c) 2000, 2001, 2002 Lineo
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+/*
+ * au1100 
+ *
+ * The au1100 does not seem to work properly with an 8 byte
+ * packetsize. So 16, 32 or 64 are the only valid values.
+ *
+ * Unfortunately this means that a DMA channel is required for
+ * EP0 transmit.
+ */
+
+#define EP0_PACKETSIZE  0x8
+
+#define UDC_MAX_ENDPOINTS       6
+
+#define UDC_NAME        "AU1100"
+
+#define NUSBD_EP0RD                0xB0200000
+#define NUSBD_EP0WR                0xB0200004
+#define NUSBD_EP1WR                0xB0200008
+#define NUSBD_EP2WR                0xB020000C
+#define NUSBD_EP3RD                0xB0200010
+#define NUSBD_EP4RD                0xB0200014
+
+#define NUSBD_EP0CS                0xB0200024
+#define NUSBD_EP1CS                0xB0200028
+#define NUSBD_EP2CS                0xB020002C
+#define NUSBD_EP3CS                0xB0200030
+#define NUSBD_EP4CS                0xB0200034
+
+#define NUSBD_EP0RDSTAT            0xB0200040
+#define NUSBD_EP0WRSTAT            0xB0200044
+#define NUSBD_EP1WRSTAT            0xB0200048
+#define NUSBD_EP2WRSTAT            0xB020004C
+#define NUSBD_EP3RDSTAT            0xB0200050
+#define NUSBD_EP4RDSTAT            0xB0200054
+
+#define NUSBD_FRAMENUM             0xB0200038
+
+enum {
+        NDMA_ID_UART0_TX = 0,
+        NDMA_ID_UART0_RX,
+        NDMA_ID_GP04,
+        NDMA_ID_GP05,
+        NDMA_ID_AC97C_TX,
+        NDMA_ID_AC97C_RX,
+        NDMA_ID_UART3_TX,
+        NDMA_ID_UART3_RX,
+        NDMA_ID_USBDEV_EP0_RX,
+        NDMA_ID_USBDEV_EP0_TX,
+        NDMA_ID_USBDEV_EP1_TX,
+        NDMA_ID_USBDEV_EP2_TX,
+        NDMA_ID_USBDEV_EP3_RX,
+        NDMA_ID_USBDEV_EP4_RX,
+        NDMA_ID_I2S_TX,
+        NDMA_ID_I2S_RX,
+        NDMA_NUM_DEV
+};
+
+typedef struct ep_regs {
+        int rd;
+        int wr;
+        int cs;
+        int rds;
+        int wrs;
+        int rx_id;
+        int tx_id;
+        char * rx_str;
+        char * tx_str;
+        int indma;    
+        int outdma;
+        int last;
+        int eptype;
+} ep_regs_t;
+
+#define MAX_EPN_PACKET_SIZE 64
+#define CP0_PRID_SOC_MASK       0xff000000
+#define CP0_PRID_AU1000         0x00000000
+#define CP0_PRID_AU1500         0x01000000
+#define CP0_PRID_AU1100         0x02000000
+#define CP0_PRID_REV_MASK       0x000000ff
+
diff -Nru a/drivers/usbd/ep0.c b/drivers/usbd/ep0.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/ep0.c	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,565 @@
+/*
+ * usbd/ep0.c
+ *
+ *      Copyright (c) 2004 Belcarra
+ *
+ * Adapted from earlier work:
+ *      Copyright (c) 2002, 2003, 2004 Belcarra
+ *      Copyright (c) 2000, 2001, 2002 Lineo
+ *      Copyright (c) 2001 Hewlett Packard
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *      Bruce Balden <balden@lineo.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * This function driver implements support for all of the USB 2.0 Chapter
+ * nine requests.
+ *
+ * Any request that is not required by Chapter nine is passed to the other
+ * function drivers recv_setup routine.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+//EXPORT_NO_SYMBOLS;
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <asm/uaccess.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+
+#include <linux/smp_lock.h>
+#include <linux/ctype.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+
+#include "usbd-chap9.h"
+#include "usbd-mem.h"
+#include "usbd.h"
+#include "usbd-func.h"
+#include "usbd-bus.h"           // for usbd_recv_setup_irq() definition
+#include "usbd-admin.h"
+
+struct usb_function_instance * ep0_function;
+
+/* C.f. 9.4 Standard Requests and table 9.3
+ *
+ * Encode valid requests into a bitmap for each recipient type for 
+ * both directions.
+ *
+ */
+
+#define STD(x) (1<<(x+1))
+
+/* h2d_standard_requests and d2h_standard_requests
+ *
+ * These tables list all of the valid Chapter Nine requests. Any request
+ * not listed in these tables will NOT be processed by the EP0 function and 
+ * will instead be passed to the appropriate function driver.
+ */
+u32 h2d_standard_requests[4] = {
+        // 0 - Device
+        STD(USB_REQ_CLEAR_FEATURE) |
+        STD(USB_REQ_SET_FEATURE) |
+        STD(USB_REQ_SET_ADDRESS) |
+        STD(USB_REQ_SET_DESCRIPTOR) |
+        STD(USB_REQ_GET_CONFIGURATION) |
+        STD(USB_REQ_SET_CONFIGURATION) ,
+        // 1 - Interface
+        STD(USB_REQ_CLEAR_FEATURE) |
+        STD(USB_REQ_SET_FEATURE) |
+        STD(USB_REQ_SET_INTERFACE) ,
+        // 2 - Endpoint
+        STD(USB_REQ_CLEAR_FEATURE) |
+        STD(USB_REQ_SET_FEATURE) ,
+        // 3 - Other
+        0,
+};
+
+u32 d2h_standard_requests[4] = {
+        // 0 - Device
+        STD(USB_REQ_GET_STATUS) |
+        STD(USB_REQ_GET_DESCRIPTOR) ,
+        // 1 - Interface
+        STD(USB_REQ_GET_STATUS) |
+        STD(USB_REQ_GET_INTERFACE) ,
+        // 2 - Endpoint
+        STD(USB_REQ_GET_STATUS) |
+        STD(USB_REQ_SYNCH_FRAME) ,
+        // 3 - Other
+        0,
+};
+
+
+/* Endpoint ZEro Configuration *************************************************************** */
+
+/* ep0_event_irq - respond to USB event
+ *
+ * Process USB events.
+ */
+static void ep0_event_irq (struct usb_function_instance *function, usb_device_event_t event, int dummy )
+{
+	switch (event) {
+	case DEVICE_CREATE:
+        default:
+                break;
+        }
+}
+
+/* copy_config - copy data into urb buffer
+ */
+static int copy_config (u8 *cp, void *data, int actual_length, int max_buf)
+{
+	int available = max_buf - actual_length;
+        int length = MIN(*(u8 *)data, available);
+
+	RETURN_ZERO_IF (!length);
+	memcpy (cp, data, length);
+        return length;
+}
+
+/* copy_config - copy data into urb buffer
+ */
+static int copy_endpoint (struct usb_function_instance *function, u8 *cp,
+                struct usb_endpoint_descriptor *endpoint, int endpoint_index, int actual_length, int max_buf, int hs)
+{
+	int available = max_buf - actual_length;
+	int length = MIN(endpoint->bLength, available);
+        struct usb_endpoint_descriptor endpoint_copy;
+
+        RETURN_ZERO_IF (!length);
+	memcpy (&endpoint_copy, endpoint, endpoint->bLength);
+        usbd_endpoint_update(function, endpoint_index, &endpoint_copy, hs);
+	memcpy (cp, &endpoint_copy, length);
+        return length;
+}
+
+/* usbd_get_descriptor - copy descriptor into urb buffer
+ *
+ * Return non-zero for error.
+ */
+int usbd_get_descriptor (struct usb_bus_instance *bus, u8 *buffer, int max, int descriptor_type, int index)
+{
+        struct usb_function_driver *function_driver = bus->function_instance->function_driver;
+        int actual_length = 0;
+
+	switch (descriptor_type) {
+	case USB_DESCRIPTOR_TYPE_DEVICE:
+		{
+			struct usb_device_descriptor *device_descriptor = function_driver->device_descriptor;
+
+			// copy descriptor for this device
+			actual_length += copy_config (buffer + actual_length, device_descriptor, actual_length, max);
+
+			// correct the correct control endpoint 0 max packet size into the descriptor
+			device_descriptor = (struct usb_device_descriptor *) buffer;
+			device_descriptor->bMaxPacketSize0 = bus->driver->maxpacketsize;
+		}
+		break;
+
+#ifdef CONFIG_USBD_HIGH_SPEED
+	case USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER:      // c.f. 9.6.2 Device Qualifier
+                {
+			struct usb_device_qualifier_descriptor *device_qualifier_descriptor = 
+                                function_driver->device_qualifier_descriptor;
+
+			// copy descriptor for this device
+			actual_length += copy_config (buffer + actual_length, device_qualifier_descriptor, actual_length, max);
+
+		}
+                break;
+
+	case USB_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION:
+	case USB_DESCRIPTOR_TYPE_CONFIGURATION:
+		{
+                        int hs = bus->HighSpeedFlag ?  descriptor_type == USB_DESCRIPTOR_TYPE_CONFIGURATION:
+                                descriptor_type == USB_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION;
+#else
+	case USB_DESCRIPTOR_TYPE_CONFIGURATION:
+		{
+                        int hs = 0;
+#endif
+			int interface;
+
+                        struct usb_configuration_instance *configuration_instance = 
+                                &function_driver->configuration_instance_array[index];
+
+			struct usb_configuration_descriptor *configuration_descriptor = 
+                                configuration_instance->configuration_descriptor;
+
+                        RETURN_EINVAL_IF (!configuration_descriptor);
+			RETURN_EINVAL_IF (index > function_driver->device_descriptor->bNumConfigurations);
+
+			actual_length += copy_config (buffer + actual_length, configuration_descriptor, actual_length, max);
+
+			// iterate across bNumInterfaces for specified configuration
+			for (interface = 0; interface < configuration_descriptor->bNumInterfaces; interface++) {
+
+				int alternate;
+				struct usb_interface_instance *interface_instance =
+                                        configuration_instance->interface_instance_array + interface;
+
+				// iterate across interface alternates
+				for (alternate = 0; alternate < interface_instance->alternates; alternate++) {
+
+					int class;
+					int endpoint;
+
+					struct usb_alternate_instance *alternate_instance =
+                                                interface_instance->alternates_instance_array + alternate;
+
+                                        //struct usb_interface_descriptor *usb_interface_descriptor;
+
+					// copy descriptor for this interface
+					actual_length += copy_config (buffer + actual_length, 
+                                                        alternate_instance->interface_descriptor, actual_length, max);
+
+					// iterate across classes for this alternate interface
+					for (class = 0; class < alternate_instance->classes; class++) 
+						actual_length += copy_config (buffer + actual_length, 
+                                                                *(alternate_instance->class_list + class), actual_length, max);
+
+					// iterate across endpoints for this alternate interface
+					//interface_descriptor = alternate_instance->interface_descriptor;
+
+                                        for (endpoint = 0; endpoint < alternate_instance->endpoints ; endpoint++) {
+
+                                                //printk(KERN_INFO"%s: endpoint: %d index: %d\n",
+                                                //                __FUNCTION__, endpoint, 
+                                                //                alternate_instance->endpoint_indexes[endpoint]
+                                                //                );
+                                                actual_length += copy_endpoint (bus->function_instance, 
+                                                                buffer + actual_length, 
+                                                                *(( alternate_instance->endpoint_list) + endpoint), 
+                                                                alternate_instance->endpoint_indexes[endpoint],
+                                                                actual_length, max, hs);
+                                        }
+                                }
+                        }
+                }
+                break;
+
+	case USB_DESCRIPTOR_TYPE_STRING:
+		{
+			struct usb_string_descriptor *string_descriptor;
+			RETURN_EINVAL_IF (!(string_descriptor = usbd_get_string (index)));
+			actual_length += copy_config (buffer + actual_length, string_descriptor, actual_length, max);
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	return actual_length;
+}
+
+/* ep0_recv_setup_irq - process a device request
+ *
+ * Process a received device request. If not a Chapter nine request pass it
+ * to the other loaded function driver recv_setup_irq() function.
+ *
+ * Return non-zero to indicate failure.
+ */
+static int ep0_recv_setup_irq (struct usb_device_request *request)
+{
+        struct usb_function_instance *function = ep0_function;
+        struct usb_bus_instance *bus = ep0_function->bus;
+
+#if 0
+	printk(KERN_INFO"%s: bus: %p bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x %d\n", __FUNCTION__,
+	        function->bus, request->bmRequestType, request->bRequest, 
+	        le16_to_cpu(request->wValue), le16_to_cpu(request->wIndex), le16_to_cpu(request->wLength),
+                request->bRequest);
+#endif
+
+	// handle USB Standard Request only (c.f. USB Spec table 9-2, D6..5 must be 0)
+
+        THROW_IF ((request->bmRequestType & USB_REQ_TYPE_MASK) != 0, non_standard);
+
+        THROW_IF(!( (request->bmRequestType & USB_DIR_IN ?  d2h_standard_requests : h2d_standard_requests) 
+                        [request->bmRequestType & 0x3] & STD(request->bRequest)), non_standard ); 
+        
+        CATCH(non_standard) {
+                return usbd_recv_setup_irq(bus->function_instance, request);
+        }
+
+        switch (bus->device_state) {
+        case STATE_CREATED:
+        case STATE_ATTACHED:
+        case STATE_POWERED:
+                return -EINVAL;
+
+        case STATE_INIT:
+        case STATE_DEFAULT:
+                switch (request->bRequest) {
+                case USB_REQ_GET_STATUS:
+                case USB_REQ_GET_INTERFACE:
+		case USB_REQ_SYNCH_FRAME:
+		case USB_REQ_CLEAR_FEATURE:
+		case USB_REQ_SET_FEATURE:
+		case USB_REQ_SET_DESCRIPTOR:
+		case USB_REQ_SET_INTERFACE:
+                        printk(KERN_INFO"%s: bad device_state\n", __FUNCTION__);
+			return -EINVAL;
+
+		case USB_REQ_SET_CONFIGURATION:
+		case USB_REQ_SET_ADDRESS:
+		case USB_REQ_GET_DESCRIPTOR:
+		case USB_REQ_GET_CONFIGURATION:
+			break;
+		}
+	case STATE_ADDRESSED:
+	case STATE_CONFIGURED:
+	case STATE_SUSPENDED:
+		break;
+	case STATE_UNKNOWN:
+                        printk(KERN_INFO"%s: suspended or unknown\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	// handle all requests that return data (direction bit set on bm RequestType)
+	if ((request->bmRequestType & USB_REQ_DIRECTION_MASK)) {
+
+                struct urb *urb;
+                int rc = 0;
+		switch (request->bRequest) {
+		case USB_REQ_SYNCH_FRAME:
+		case USB_REQ_CLEAR_FEATURE:
+		case USB_REQ_SET_FEATURE:
+		case USB_REQ_SET_ADDRESS:
+		case USB_REQ_SET_DESCRIPTOR:
+		case USB_REQ_SET_CONFIGURATION:
+		case USB_REQ_SET_INTERFACE:
+                        printk(KERN_INFO"%s: bad direction\n", __FUNCTION__);
+			return -EINVAL;
+		}
+
+                RETURN_EINVAL_IF(!le16_to_cpu(request->wLength));
+
+                // allocate urb, no callback, urb will be automatically de-allocated
+                RETURN_EINVAL_IF(!(urb = usbd_alloc_urb (function, 0, le16_to_cpu(request->wLength), NULL)));
+
+		switch (request->bRequest) {
+
+		case USB_REQ_GET_STATUS:
+                        urb->actual_length = 2;
+                        urb->buffer[0] = urb->buffer[1] = 0;
+                        switch (request->bmRequestType & USB_REQ_RECIPIENT_MASK) {
+                        case USB_REQ_RECIPIENT_DEVICE:
+                                urb->buffer[0] = USB_STATUS_SELFPOWERED;
+                                break;
+                        case USB_REQ_RECIPIENT_INTERFACE:
+                                break;
+                        case USB_REQ_RECIPIENT_ENDPOINT:
+                                urb->buffer[0] = usbd_endpoint_halted (function, le16_to_cpu(request->wIndex));
+                                break;
+                        case USB_REQ_RECIPIENT_OTHER:
+                                urb->actual_length = 0;
+                        default:
+                                break;
+                        }
+                        rc = 0;
+
+                        break;
+
+                case USB_REQ_GET_DESCRIPTOR:
+                        rc = usbd_get_descriptor (bus, urb->buffer, 
+                                        le16_to_cpu (request->wLength), 
+                                        le16_to_cpu (request->wValue >> 8), 
+                                        le16_to_cpu (request->wValue) & 0xff);
+                        if (rc != -EINVAL) {
+                                urb->actual_length = rc;
+                                rc = 0;
+                        }
+                        break;
+
+                case USB_REQ_GET_CONFIGURATION:
+                        urb->actual_length = 1;	
+                        urb->buffer[0] = bus->ConfigurationValue;
+                        break;
+
+                case USB_REQ_GET_INTERFACE:
+                        RETURN_EINVAL_IF(le16_to_cpu(request->wIndex) > bus->bNumInterfaces);
+                        urb->actual_length = 1;	
+                        urb->buffer[0] = bus->alternates[le16_to_cpu(request->wIndex)];
+                        break;
+                default:
+                        rc = 1;
+                }
+
+                //printk(KERN_INFO"%s: actual: %d packetsize: %d wIndex: %d rc: %d\n", __FUNCTION__,
+                //                urb->actual_length, urb->bus->driver->maxpacketsize, le16_to_cpu(request->wLength), rc);
+
+                if (!(urb->actual_length % urb->bus->driver->maxpacketsize) && 
+                                (urb->actual_length < le16_to_cpu(request->wLength))) 
+                {
+                        //printk(KERN_INFO"%s: NEED ZLP\n", __FUNCTION__);
+                        urb->flags |= USBD_URB_SENDZLP;
+                }
+                if (!rc)
+                        RETURN_ZERO_IF(!usbd_send_urb(urb));
+                //printk(KERN_INFO"%s: failed urb: %p\n", __FUNCTION__, urb);
+                usbd_dealloc_urb(urb);
+                return -EINVAL;
+        }
+        // handle the requests that do not return data
+        else {
+
+                switch (request->bRequest) {
+
+                case USB_REQ_CLEAR_FEATURE:
+                case USB_REQ_SET_FEATURE:
+                        switch (request->bmRequestType & USB_REQ_RECIPIENT_MASK) {
+                        case USB_REQ_RECIPIENT_DEVICE:
+                                // XXX DEVICE_REMOTE_WAKEUP or TEST_MODE would be added here
+                                // XXX fall through for now as we do not support either
+                        case USB_REQ_RECIPIENT_INTERFACE:
+                        case USB_REQ_RECIPIENT_OTHER:
+                        default:
+                                return -EINVAL;
+
+                        case USB_REQ_RECIPIENT_ENDPOINT:
+                                if (le16_to_cpu(request->wValue) == USB_ENDPOINT_HALT) 
+                                        return usbd_device_feature (function, le16_to_cpu (request->wIndex) & 0x7f,
+                                                        request->bRequest == USB_REQ_SET_FEATURE);
+
+                                else 
+                                        return -EINVAL ;
+                        }
+
+                case USB_REQ_SET_ADDRESS:
+                        // check if this is a re-address, reset first if it is (this shouldn't be possible)
+                        RETURN_EINVAL_IF (bus->device_state != STATE_DEFAULT);
+                        usbd_bus_event (bus, DEVICE_ADDRESS_ASSIGNED, le16_to_cpu(request->wValue));
+                        return 0;
+
+                case USB_REQ_SET_DESCRIPTOR:	
+                        // XXX should we support this?
+                        // This would require allocating a rcv urb and using usbd_start_recv()
+                        return -EINVAL;
+
+                case USB_REQ_SET_CONFIGURATION:
+                        {
+                                struct usb_function_driver *function_driver = bus->function_instance->function_driver;
+                                int bNumConfigurations = function_driver->bNumConfigurations;
+                                int bNumInterfaces;
+                                u8 ConfigurationValue;
+
+
+                                struct usb_configuration_instance *configuration_instance;
+                                struct usb_configuration_descriptor *configuration_descriptor;
+
+                                // get rid of previous interface and alternates 
+                                if (bus->bNumInterfaces && bus->alternates) {
+                                        bus->bNumInterfaces = 0;
+                                        lkfree(bus->alternates);
+                                        bus->alternates = NULL;
+                                }
+
+                                // c.f. 9.4.7 - the top half of wValue is reserved
+                                //
+                                // c.f. 9.4.7 - zero is the default or addressed state, in our case this
+                                // is the same is configuration zero, but will be fixed in usbd.c when used.
+                                
+                                ConfigurationValue = le16_to_cpu (request->wValue) & 0x7f;
+
+                                RETURN_EINVAL_IF(ConfigurationValue > bNumConfigurations);
+                                 
+                                ConfigurationValue = ConfigurationValue ? 0 : ConfigurationValue -1;
+                                
+                                configuration_instance = &function_driver->configuration_instance_array[ConfigurationValue];
+                                configuration_descriptor = configuration_instance->configuration_descriptor;
+
+                                RETURN_EINVAL_IF(!configuration_descriptor);
+
+                                bNumInterfaces = configuration_instance->bNumInterfaces;
+
+                                bus->ConfigurationValue = ConfigurationValue + 1;
+
+                                // reset interface and alternate settings
+
+                                RETURN_EINVAL_IF (!(bus->alternates = ckmalloc(bNumInterfaces, GFP_ATOMIC)));
+                                bus->bNumInterfaces = bNumInterfaces;
+
+                                //usbd_bus->device_event (bus, DEVICE_CONFIGURED, 0);
+                                usbd_bus_event (bus, DEVICE_CONFIGURED, 0);
+                                return 0;
+                        }
+
+		case USB_REQ_SET_INTERFACE:
+                        {
+                                int interface = le16_to_cpu(request->wIndex);
+
+                                RETURN_EINVAL_IF(interface > bus->bNumInterfaces);
+
+                                bus->alternates[interface] = le16_to_cpu(request->wValue);
+                                usbd_bus_event (bus, DEVICE_SET_INTERFACE, 0);
+                                return 0;
+                        }
+
+                case USB_REQ_GET_STATUS:
+		case USB_REQ_GET_DESCRIPTOR:
+		case USB_REQ_GET_CONFIGURATION:
+		case USB_REQ_GET_INTERFACE:
+		case USB_REQ_SYNCH_FRAME:	// XXX should never see this (?)
+                        printk(KERN_INFO"%s: unknown\n", __FUNCTION__);
+			return -EINVAL;
+		}
+	}
+	return -EINVAL;
+}
+
+
+/* ep0_function_enable - enable the endpoint zero function
+ *
+ * Return non-zero on failure.
+ */
+static int ep0_function_enable (struct usb_function_instance *function)
+{
+        ep0_function = function;
+        return 0;
+}
+
+/* ep0_function_disable - disable the endpoint zero function
+ */
+static void ep0_function_disable (struct usb_function_instance *function)
+{
+        ep0_function = NULL;
+}       
+
+
+static struct usb_function_operations ep0_ops = {
+	event_irq:	ep0_event_irq,
+	recv_setup_irq:	ep0_recv_setup_irq,
+        function_enable: ep0_function_enable,
+        function_disable: ep0_function_disable,
+};
+
+struct usb_function_driver ep0_driver = {
+	name:		"EP0",
+	fops:		&ep0_ops,
+};
+
+EXPORT_SYMBOL(usbd_get_descriptor);
diff -Nru a/drivers/usbd/mouse_fd/Config.in b/drivers/usbd/mouse_fd/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/mouse_fd/Config.in	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,26 @@
+#
+# Mouse Function Driver
+#
+# Copyright (C) 2003 Belcarra
+#
+
+
+mainmenu_option next_comment
+
+comment "Random Mouse Function"
+dep_tristate '  Mouse Function' CONFIG_USBD_MOUSE $CONFIG_USBD
+
+if [ "$CONFIG_USBD_MOUSE" = "y" -o "$CONFIG_USBD_MOUSE" = "m" ]; then
+   hex     'VendorID (hex value)' CONFIG_USBD_MOUSE_VENDORID "12b9"
+   hex     'ProductID (hex value)' CONFIG_USBD_MOUSE_PRODUCTID "f003"
+   hex     'bcdDevice (binary-coded decimal)' CONFIG_USBD_MOUSE_BCDDEVICE "0100"
+
+   string 'iManufacturer (string)' CONFIG_USBD_MOUSE_MANUFACTURER "Belcarra"
+   #string 'iProduct (string)' CONFIG_USBD_MOUSE_PRODUCT_NAME "Belcarra Mouse"
+
+   string 'iConfiguration (string)' CONFIG_USBD_MOUSE_DESC "Acm Cfg"
+   string 'Comm Interface iInterface (string)' CONFIG_USBD_MOUSE_COMM_INTF "Comm Intf"
+   bool   'Mouse BH Test'  CONFIG_USBD_MOUSE_BH
+
+fi
+endmenu
diff -Nru a/drivers/usbd/mouse_fd/Makefile b/drivers/usbd/mouse_fd/Makefile
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/mouse_fd/Makefile	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,66 @@
+#
+# Function driver for a Mouse USB Device
+#
+# Copyright (c) 2003 Belcarra
+
+# Multipart objects.
+
+O_TARGET	:= mouse_fd.o
+list-multi	:= mouse_fd.o 
+
+mouse_fd-objs	:= mouse.o 
+
+# Objects that export symbols.
+export-objs	:= mouse.o
+
+# Object file lists.
+
+obj-y	:=
+obj-m	:=
+obj-n	:=
+obj-	:=
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_USBD_MOUSE)   += mouse_fd.o
+
+# Extract lists of the multi-part drivers.
+# The 'int-*' lists are the intermediate files used to build the multi's.
+
+multi-y		:= $(filter $(list-multi), $(obj-y))
+multi-m		:= $(filter $(list-multi), $(obj-m))
+int-y		:= $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
+int-m		:= $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
+
+# Files that are both resident and modular: remove from modular.
+
+obj-m		:= $(filter-out $(obj-y), $(obj-m))
+int-m		:= $(filter-out $(int-y), $(int-m))
+
+# Translate to Rules.make lists.
+
+O_OBJS		:= $(filter-out $(export-objs), $(obj-y))
+OX_OBJS		:= $(filter     $(export-objs), $(obj-y))
+M_OBJS		:= $(sort $(filter-out $(export-objs), $(obj-m)))
+MX_OBJS		:= $(sort $(filter     $(export-objs), $(obj-m)))
+MI_OBJS		:= $(sort $(filter-out $(export-objs), $(int-m)))
+MIX_OBJS	:= $(sort $(filter     $(export-objs), $(int-m)))
+
+# The global Rules.make.
+
+USBD=$(TOPDIR)/drivers/usbd
+MOUSED=$(USBD)/mouse_fd
+include $(TOPDIR)/Rules.make
+EXTRA_CFLAGS += -I$(MOUSED) -I$(USBD) -Wno-unused -Wno-format 
+EXTRA_CFLAGS_nostdinc += -I$(MOUSED) -I$(USBD) -Wno-unused -Wno-format 
+
+# Link rules for multi-part drivers.
+
+mouse_fd.o: $(mouse_fd-objs)
+	$(LD) -r -o $@ $(mouse_fd-objs)
+
+# dependencies:
+
+mouse.o: $(USBD)/usbd.h $(USBD)/usbd-bus.h $(USBD)/usbd-func.h
+
+
diff -Nru a/drivers/usbd/mouse_fd/getmouse.c b/drivers/usbd/mouse_fd/getmouse.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/mouse_fd/getmouse.c	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * File:		FILE
+ * Module:		MODULE
+ * Author:		AUTHOR
+ * Date:		DATE
+ * 
+ * Notes:
+ *
+ * $Id$
+ *
+ * History:
+ * $Log$
+ *
+ ******************************************************************************/
+ 
+/*
+ *
+ *
+ * 3.2. Non-Canonical Input Processing
+ * 
+ * In non-canonical input processing mode, input is not assembled into lines and
+ * input processing (erase, kill, delete, etc.) does not occur. Two parameters
+ * control the behavior of this mode: c_cc[VTIME] sets the character timer, and
+ * c_cc[VMIN] sets the minimum number of characters to receive before satisfying
+ * the read.
+ * 
+ * If MIN > 0 and TIME = 0, MIN sets the number of characters to receive before
+ * the read is satisfied. As TIME is zero, the timer is not used.
+ * 
+ * If MIN = 0 and TIME > 0, TIME serves as a timeout value. The read will be
+ * satisfied if a single character is read, or TIME is exceeded (t = TIME *0.1
+ * s). If TIME is exceeded, no character will be returned.
+ * 
+ * If MIN > 0 and TIME > 0, TIME serves as an inter-character timer. The read
+ * will be satisfied if MIN characters are received, or the time between two
+ * characters exceeds TIME. The timer is restarted every time a character is
+ * received and only becomes active after the first character has been received.
+ * 
+ * If MIN = 0 and TIME = 0, read will be satisfied immediately. The number of
+ * characters currently available, or the number of characters requested will be
+ * returned. According to Antonino (see contributions), you could issue a fcntl
+ * (fd, F_SETFL, FNDELAY); before reading to get the same result.
+ * 
+ * By modifying newtio.c_cc[VTIME] and newtio.c_cc[VMIN] all modes described
+ * above can be tested.
+ * 
+ */
+
+#include <sys/types.h>                                                    
+#include <sys/stat.h>                                                     
+#include <fcntl.h>                                                        
+#include <termios.h>                                                      
+#include <stdio.h>                                                        
+
+#define BAUDRATE B1200                                                   
+#define MODEMDEVICE "/dev/ttyS0"                                          
+#define _POSIX_SOURCE 1 /* POSIX compliant source */                      
+#define FALSE 0                                                           
+#define TRUE 1                                                            
+
+volatile int STOP=FALSE;                                                  
+
+main()                                                                    
+{                                                                         
+        int fd,c, res;                                                          
+        struct termios oldtio,newtio;                                           
+        unsigned char buf[255];                                                          
+
+        int bytes;
+        unsigned char mouse[3];
+
+        fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );                             
+        if (fd <0) {perror(MODEMDEVICE); exit(-1); }                            
+
+        tcgetattr(fd,&oldtio); /* save current port settings */                 
+
+        bzero(&newtio, sizeof(newtio));                                         
+        newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;             
+        newtio.c_iflag = IGNPAR;                                                
+        newtio.c_oflag = 0;                                                     
+
+        /* set input mode (non-canonical, no echo,...) */                       
+        newtio.c_lflag = 0;                                                     
+
+        newtio.c_cc[VTIME]    = 0;   /* inter-character timer unused */         
+        newtio.c_cc[VMIN]     = 1;   /* blocking read until 5 chars received */ 
+
+        tcflush(fd, TCIFLUSH);                                                  
+        tcsetattr(fd,TCSANOW,&newtio);                                          
+
+
+        bytes = 0;
+        while (STOP==FALSE) {       /* loop for input */                        
+                unsigned char c;
+
+                res = read(fd,buf,1);   /* returns after 5 chars have been input */ 
+                buf[res]=0;               /* so we can printf... */                   
+                //fprintf(stderr, ":%02x:%d\n", buf[0], res);                                         
+                //if (buf[0]=='z') STOP=TRUE;                                           
+
+                c = buf[0];
+
+                if ( c & 0x40 ) {
+                        bytes = 1;
+                        mouse[0] = c;
+                }
+                else if (bytes == 1) {
+                        bytes = 2;
+                        mouse[1] = c;
+                }
+                else if (bytes == 2) {
+                        bytes = 0;
+                        mouse[2] = c;
+                        fprintf(stderr, "%02x %02x %02x\n", mouse[0], mouse[1], mouse[2]);
+                        //printf("%c%c%c", mouse[0], mouse[1], mouse[2]);
+                }
+        }                                                                       
+        tcsetattr(fd,TCSANOW,&oldtio);                                          
+}                                                                         
+
+
+/* End of FILE */
diff -Nru a/drivers/usbd/mouse_fd/mouse.c b/drivers/usbd/mouse_fd/mouse.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/mouse_fd/mouse.c	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,548 @@
+/*
+ * usbd/mouse_fd/mouse.c
+ *
+ *      Copyright (c) 2003, 2004 Belcarra
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * This is a test USB HID Function Driver designed to test and
+ * verify that INTERRUPT IN endpoints work properly.
+ *
+ * This emulates a simple USB mouse and generates a constant stream
+ * of small random mouse movements.
+ *
+ * To use simply load with something like:
+ *
+ *      insmod mouse_fd.o vendor_id=0xffff product_id=0xffff
+ *
+ * And attach to a Windows box. Windows should recognize as a mouse and
+ * immediately start receiving a stream of data. 
+ *
+ * To terminate simply unplug.
+ *
+ * The mouse driver has several other characteristics to allow testing of
+ * other features of the bus interface driver:
+ *
+ *      - ep0 ZLP handling
+ *
+ *      - ep0 delayed CONTROL READ
+ *
+ *
+ * Notes
+ *
+ * 1. The mouse driver is product name is hard coded to a string that will
+ *    generate a string descriptor that is 32 bytes long. This will test
+ *    most UDC's ep0 ZLP handling as it is a multiple of the most common
+ *    UDC endpoint zero size. (An option can be added later to allow for
+ *    64 byte ep0 packetsize.)
+ *
+ * 2. The CONFIG_USBD_MOUSE_BH option can be enabled to delay the HID report 
+ *    to being generated by a bottom half handler. This will verify that 
+ *    the bus interface driver properly handles the case of a delayed
+ *    CONTROL READ. I.e. when the usbd_recv_setup_irq() function returns
+ *    zero for successful completion but there is no tx_urb containing the
+ *    requested data. The bus interface driver must setup the conditions for
+ *    ACK'ing the SETUP packet but then NAK the IN data for endpoint zero
+ *    until the tx_urb is started later.
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+
+#include <usbd-export.h>
+#include <usbd-build.h>
+
+MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com");
+MODULE_LICENSE("PROPRIETARY");
+MODULE_DESCRIPTION ("Belcarra Random Walk MOUSE Function");
+
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <asm/atomic.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+
+#include "usbd-chap9.h"
+#include <usbd-mem.h>
+#include <usbd.h>
+#include <usbd-func.h>
+
+USBD_MODULE_INFO ("mouse_fd 2.0-beta");
+
+/* Module Parameters ************************************************************************* */
+
+static u32 vendor_id;
+static u32 product_id;
+
+MODULE_PARM (vendor_id, "i");
+MODULE_PARM (product_id, "i");
+
+MODULE_PARM_DESC (vendor_id, "Device Vendor ID");
+MODULE_PARM_DESC (product_id, "Device Product ID");
+
+/*
+ * ep0 testing.... ensure that this is exactly 16 bytes
+ */
+#undef CONFIG_USBD_MOUSE_PRODUCT_NAME
+#define CONFIG_USBD_MOUSE_PRODUCT_NAME "Belcarra  Mouse"
+
+/*
+ * MOUSE Configuration
+ *
+ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions
+ */
+
+#define BULK_INT        0x00
+#define ENDPOINTS       0x01
+
+char MouseHIDReport[52] = {
+    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
+    0x09, 0x02,                    // USAGE (Mouse)
+    0xa1, 0x01,                    // COLLECTION (Application)
+    0x09, 0x01,                    //   USAGE (Pointer)
+    0xa1, 0x00,                    //   COLLECTION (Physical)
+    0x05, 0x09,                    //     USAGE_PAGE (Button)
+    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
+    0x29, 0x03,                    //     USAGE_MAXIMUM (Button 3)
+    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
+    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
+    0x95, 0x03,                    //     REPORT_COUNT (3)
+    0x75, 0x01,                    //     REPORT_SIZE (1)
+    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
+    0x95, 0x01,                    //     REPORT_COUNT (1)
+    0x75, 0x05,                    //     REPORT_SIZE (5)
+    0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)
+    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
+    0x09, 0x30,                    //     USAGE (X)
+    0x09, 0x31,                    //     USAGE (Y)
+    0x09, 0x38,                    //     USAGE (WHEEL)
+    0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
+    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
+    0x75, 0x08,                    //     REPORT_SIZE (8)
+    0x95, 0x03,                    //     REPORT_COUNT (3)
+    0x81, 0x06,                    //     INPUT (Data,Var,Rel)
+    0xc0,                          //   END_COLLECTION
+    0xc0                           // END_COLLECTION
+};
+
+struct usb_endpoint_descriptor mouse_data = {
+        bLength: 0x07, 
+        bDescriptorType: USB_DT_ENDPOINT, 
+        bEndpointAddress: IN, 
+        bmAttributes: INTERRUPT, 
+        wMaxPacketSize: __constant_cpu_to_le16(0x10), 
+        bInterval: 0x01, 
+};
+
+struct hid_descriptor mouse_hid = {
+        bLength: 0x09, 
+        bDescriptorType: 0x21, 
+        bcdHID: __constant_cpu_to_le16(0x110), 
+        bCountryCode: 0x00, 
+        bNumDescriptors: 0x01, 
+        bReportType: 0x22, 
+        wItemLength: __constant_cpu_to_le16(0x34), 
+};
+
+static struct usb_endpoint_descriptor *mouse_default[] = { &mouse_data, };
+u8 mouse_indexes[] = { BULK_INT, };
+static struct usb_generic_class_descriptor *mouse_hid_descriptors[] = { (struct usb_generic_class_descriptor *)&mouse_hid, };
+
+/* Data Interface Alternate description(s)
+ */
+static __u8 mouse_data_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = {
+        0x09, USB_DT_INTERFACE, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting
+        sizeof (mouse_default) / sizeof(struct usb_endpoint_descriptor), // bNumEndpoints
+        0x03, 0x01, 0x02, 0x00,
+};
+
+static struct usb_alternate_description mouse_data_alternate_descriptions[] = {
+      { iInterface:"Simple Mouse Interface - Interrupt",
+              interface_descriptor: (struct usb_interface_descriptor *)&mouse_data_alternate_descriptor,
+              classes:sizeof (mouse_hid_descriptors) / sizeof (struct usb_generic_class_descriptor *),
+              class_list: mouse_hid_descriptors,
+	      endpoints:sizeof (mouse_default) / sizeof(struct usb_endpoint_descriptor *),
+              endpoint_list: mouse_default,
+              endpoint_indexes: mouse_indexes,
+      },
+};
+
+/* Interface description(s)
+ */
+static struct usb_interface_description mouse_interfaces[] = {
+      { alternates:sizeof (mouse_data_alternate_descriptions) / sizeof (struct usb_alternate_description),
+              alternate_list:mouse_data_alternate_descriptions,},
+};
+
+
+/* Configuration description(s)
+ */
+static __u8 mouse_configuration_descriptor[sizeof(struct usb_configuration_descriptor)] = {
+        0x09, USB_DT_CONFIG, 0x00, 0x00, // wLength
+        sizeof (mouse_interfaces) / sizeof (struct usb_interface_description),
+        0x01, 0x00, // bConfigurationValue, iConfiguration
+        BMATTRIBUTE, BMAXPOWER,
+};
+
+struct usb_configuration_description mouse_description[] = {
+      { configuration_descriptor: (struct usb_configuration_descriptor *)mouse_configuration_descriptor,
+              iConfiguration:"USB Simple Serial Configuration",
+	      bNumInterfaces:sizeof (mouse_interfaces) / sizeof (struct usb_interface_description),
+              interface_list:mouse_interfaces,},
+};
+
+/* Device Description
+ */
+static struct usb_device_descriptor mouse_device_descriptor = {
+	bLength: sizeof(struct usb_device_descriptor),
+	bDescriptorType: USB_DT_DEVICE,
+	bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
+	bDeviceClass: 0x00,
+	bDeviceSubClass: 0x00,
+	bDeviceProtocol: 0x00,
+	bMaxPacketSize0: 0x00,
+	idVendor: __constant_cpu_to_le16(CONFIG_USBD_MOUSE_VENDORID),
+	idProduct: __constant_cpu_to_le16(CONFIG_USBD_MOUSE_PRODUCTID),
+	bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_MOUSE_BCDDEVICE),
+};
+
+static struct usb_endpoint_request mouse_endpoint_requests[ENDPOINTS+1] = {
+        { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, },
+        { 0, },
+};
+
+
+struct usb_device_description mouse_device_description = {
+        device_descriptor: &mouse_device_descriptor,
+	iManufacturer: CONFIG_USBD_MOUSE_MANUFACTURER,
+	iProduct: CONFIG_USBD_MOUSE_PRODUCT_NAME,
+#if !defined(CONFIG_USBD_NO_SERIAL_NUMBER) && defined(CONFIG_USBD_SERIAL_NUMBER_STR)
+	iSerialNumber: CONFIG_USBD_SERIAL_NUMBER_STR, 
+#endif
+        endpointsRequested: ENDPOINTS,
+        requestedEndpoints: mouse_endpoint_requests,
+};
+
+
+/* MOUSE ***************************************************************************************** */
+
+struct mouse_private {
+        struct usb_function_instance *function;
+        struct tq_struct notification_bh;
+        int usb_driver_registered;              // non-zero if usb function registered
+        unsigned char connected;                // non-zero if connected to host (configured)
+        unsigned int writesize;                 // packetsize * 4
+        struct urb *tx_urb;
+        int wLength;
+        int x;
+        int y;
+        int last_x;
+        int last_y;
+        int n;
+};
+
+struct mouse_private mouse_private;
+
+
+/* Transmit Function *************************************************************************** */
+
+int get_xy[8] = {
+         0,  0,  1,  1, 
+         0,  0, -1, -1, 
+};
+
+void mouse_send(struct usb_function_instance *function)
+{
+        struct mouse_private *mouse = &mouse_private;
+        int new_x = 0;
+        int new_y = 0;
+        u8 random;
+
+        memset(mouse->tx_urb->buffer, 0, 4);
+        if (!mouse->n) {
+
+                get_random_bytes(&random, 1);
+
+                mouse->last_x = MAX(-4, MIN(4, mouse->last_x + get_xy[random & 0x7]));
+                mouse->last_y = MAX(-4, MIN(4, mouse->last_y + get_xy[(random >> 3) & 0x7]));
+                mouse->n = (random>>6) & 0x3;
+
+                new_x = mouse->x + mouse->last_x;
+                new_y = mouse->y + mouse->last_y;
+
+                mouse->tx_urb->buffer[1] = mouse->last_x;
+                mouse->x = new_x;
+                mouse->tx_urb->buffer[2] = mouse->last_y;
+                mouse->y = new_y;
+                mouse->tx_urb->actual_length = 4;
+#if 0
+                printk(KERN_INFO"%s: x: %4d y: %4d rand: %02x x: %2d y: %2d new_x: %4d new_y: %4d  urb: %02x %02x %02x %02x\n", 
+                                __FUNCTION__, 
+                                mouse->x, mouse->y, random, x, y, new_x, new_y, 
+                                mouse->tx_urb->buffer[0], mouse->tx_urb->buffer[1], 
+                                mouse->tx_urb->buffer[2], mouse->tx_urb->buffer[3] );
+#endif
+        }
+        else if ((mouse->n)&1) {
+                mouse->n--;
+        } 
+        else {
+                mouse->n--;
+                mouse->tx_urb->buffer[1] = mouse->last_x;
+                mouse->x = new_x;
+                mouse->tx_urb->buffer[2] = mouse->last_y;
+                mouse->y = new_y;
+        }
+        usbd_send_urb(mouse->tx_urb);
+}
+
+/* mouse_urb_sent - called to indicate URB transmit finished
+ * @urb: pointer to struct urb
+ * @rc: result
+ */
+int mouse_urb_sent (struct urb *urb, int rc)
+{
+        struct mouse_private *mouse = &mouse_private;
+        struct usb_function_instance *function = mouse->function;
+
+        RETURN_ZERO_IF(usbd_bus_status(function) == USBD_CLOSING);
+        RETURN_ZERO_IF(usbd_bus_status(function) != USBD_OK);
+        RETURN_ZERO_IF(usbd_device_state(function) != STATE_CONFIGURED);
+
+        mouse_send(function);                                                   // re-send
+        return 0;
+}
+
+/* USB Device Functions ************************************************************************ */
+
+/* mouse_event_irq - process a device event
+ *
+ */
+void mouse_event_irq (struct usb_function_instance *function, usb_device_event_t event, int data)
+{
+        struct mouse_private *mouse = &mouse_private;
+
+	switch (event) {
+	case DEVICE_CONFIGURED:
+                mouse->connected = 1;
+                if (!(mouse->tx_urb = usbd_alloc_urb (function, BULK_INT, 4, mouse_urb_sent))) 
+                        printk(KERN_INFO"%s: alloc failed\n", __FUNCTION__);
+                mouse_send(function);                                           // start sending
+		break;
+
+	case DEVICE_RESET:
+	case DEVICE_DE_CONFIGURED:
+                BREAK_IF(!mouse->connected);
+                mouse->connected = 0;
+                if (mouse->tx_urb) {
+                        usbd_dealloc_urb (mouse->tx_urb);
+                        mouse->tx_urb = NULL;
+                }
+		break;
+        default: 
+                break;
+	}
+}
+
+/* copy_config
+ * @urb: pointer to urb
+ * @data: pointer to configuration data
+ * @length: length of data
+ *
+ * Copy configuration data to urb transfer buffer if there is room for it.
+ */
+static int copy_report (struct urb *urb, void *data, int size, int max_buf)
+{
+	int available;
+	int length;
+
+	RETURN_EINVAL_IF (!urb);
+	RETURN_EINVAL_IF (!data);
+	RETURN_EINVAL_IF (!(length = size));
+	RETURN_EINVAL_IF ((available = max_buf - urb->actual_length) <= 0);
+	
+	length = (length < available) ? length : available;
+	memcpy (urb->buffer + urb->actual_length, data, length);
+	urb->actual_length += length;
+        return 0;
+}
+
+/* mouse_send_hid - send an EP0 urb containing HID report
+ */
+static int mouse_send_hid (void *data)
+{
+        struct mouse_private *mouse = &mouse_private;
+	struct usb_function_instance *function = mouse->function;
+        struct urb *urb = usbd_alloc_urb_ep0(function, mouse->wLength, NULL);
+        RETURN_EINVAL_IF (copy_report(urb, MouseHIDReport, sizeof(MouseHIDReport), mouse->wLength));
+        RETURN_ZERO_IF(!usbd_send_urb(urb));
+        usbd_dealloc_urb(urb);
+        return -EINVAL;
+}
+
+#ifdef CONFIG_USBD_MOUSE_BH
+/* mouse_hid_bh - Bottom half handler to send a HID report
+ */
+static void mouse_hid_bh (void *data)
+{
+	struct usb_function_instance *function = mouse_private.function;
+        mouse_send_hid(function);
+        MOD_DEC_USE_COUNT;
+}
+
+/* mouse_schedule_bh - schedule a call for mouse_hid_bh
+ */
+static int mouse_schedule_bh (void)
+{
+        MOD_INC_USE_COUNT;
+        if (!schedule_task (&mouse_private.notification_bh)) {
+                MOD_DEC_USE_COUNT;
+                return -EINVAL;
+        }
+        return 0;
+}
+#endif /* CONFIG_USBD_MOUSE_BH */
+
+
+
+/* mouse_recv_setup_irq - called to indicate urb has been received
+ */
+int mouse_recv_setup_irq (struct usb_device_request *request)
+{
+	struct usb_function_instance *function = mouse_private.function;
+
+        /* verify that this is a usb class request per cdc-mouse specification or a vendor request.
+         * determine the request direction and process accordingly
+         */
+        switch (request->bmRequestType & (USB_REQ_DIRECTION_MASK | USB_REQ_TYPE_MASK)) {
+
+        case USB_REQ_HOST2DEVICE:
+        case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_CLASS:
+        case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_VENDOR: 
+                return 0;
+
+        case USB_REQ_DEVICE2HOST :
+        case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_CLASS:
+        case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_VENDOR:
+
+                switch (request->bRequest) {
+                case USB_REQ_GET_DESCRIPTOR:
+                        switch (le16_to_cpu(request->wValue)>>8) {
+                        case HID_REPORT:
+                                mouse_private.wLength = request->wLength;
+#ifdef CONFIG_USBD_MOUSE_BH
+                                return mouse_schedule_bh();
+#else
+                                return mouse_send_hid(function);
+#endif
+                        }
+                default: break;
+                }
+                break;
+
+        default:
+                break;
+        }
+        return -EINVAL;
+}
+
+
+static int mouse_function_enable (struct usb_function_instance *function)
+{
+        struct mouse_private *mouse = &mouse_private;
+
+	MOD_INC_USE_COUNT;
+        mouse->function = function;
+        mouse->n = 0;
+        mouse->x = 0;
+        mouse->y = 0;
+        mouse->last_x = 0;
+        mouse->last_y = 0;
+
+        mouse->writesize = usbd_endpoint_wMaxPacketSize(function, BULK_INT, 0);
+
+        return 0;
+}
+
+static void mouse_function_disable (struct usb_function_instance *function)
+{               
+        struct mouse_private *mouse = &mouse_private;
+        mouse->function = NULL;
+        mouse->writesize = 0;
+        mouse->function = NULL;
+	MOD_DEC_USE_COUNT;
+}
+
+static struct usb_function_operations function_ops = {
+	event_irq: mouse_event_irq,
+	recv_setup_irq: mouse_recv_setup_irq,
+        function_enable: mouse_function_enable,
+        function_disable: mouse_function_disable,
+};
+
+static struct usb_function_driver function_driver = {
+	name:"mouse-random",
+	fops:&function_ops,
+	device_description:&mouse_device_description,
+	bNumConfigurations:sizeof (mouse_description) / sizeof (struct usb_configuration_description),
+	configuration_description:mouse_description,
+	idVendor: __constant_cpu_to_le16(CONFIG_USBD_MOUSE_VENDORID),
+	idProduct: __constant_cpu_to_le16(CONFIG_USBD_MOUSE_PRODUCTID),
+	bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_MOUSE_BCDDEVICE),
+};
+
+
+/* USB Module init/exit ************************************************************************ */
+/*
+ * mouse_modinit - module init
+ *
+ */
+static int mouse_modinit (void)
+{
+	printk (KERN_INFO "%s: %s vendor_id: %04x product_id: %04x\n", __FUNCTION__, __usbd_module_info, vendor_id, product_id);
+
+        if (vendor_id) 
+                function_driver.idVendor = cpu_to_le16(vendor_id);
+        if (product_id) 
+                function_driver.idProduct = cpu_to_le16(product_id);
+
+        mouse_hid.wItemLength = cpu_to_le16(0x34);      // XXX mips compiler bug.....
+
+        // register as usb function driver
+        THROW_IF (usbd_register_function (&function_driver), error);
+        mouse_private.usb_driver_registered++;
+#ifdef CONFIG_USBD_MOUSE_BH
+        mouse_private.notification_bh.routine = mouse_hid_bh;
+        mouse_private.notification_bh.data = NULL;
+#endif
+        CATCH(error) {
+                if (mouse_private.usb_driver_registered) {
+                        usbd_deregister_function (&function_driver);
+                        mouse_private.usb_driver_registered = 0;
+                }
+                return -EINVAL;
+        }
+	return 0;
+}
+
+/* mouse_modexit - module cleanup
+ */
+static void mouse_modexit (void)
+{
+        if (mouse_private.usb_driver_registered) {
+                usbd_deregister_function (&function_driver);
+        }
+}
+
+
+module_init (mouse_modinit);
+module_exit (mouse_modexit);
diff -Nru a/drivers/usbd/network_fd/Config.in b/drivers/usbd/network_fd/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/network_fd/Config.in	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,97 @@
+#
+# Network Function
+#
+# Copyright (C) 2002-2003 Belcarra
+#
+
+mainmenu_option next_comment
+comment "Network Function"
+
+dep_tristate '  Network Function Driver' CONFIG_USBD_NETWORK $CONFIG_USBD
+
+if [ "$CONFIG_USBD_NETWORK" = "y" -o "$CONFIG_USBD_NETWORK" = "m" ]; then
+
+   hex     'VendorID (hex value)'             CONFIG_USBD_NETWORK_VENDORID "12b9"
+   hex     'ProductID (hex value)'            CONFIG_USBD_NETWORK_PRODUCTID "f001"
+   hex     'bcdDevice (binary-coded decimal)' CONFIG_USBD_NETWORK_BCDDEVICE "0100"
+
+   string 'iManufacturer (string)' CONFIG_USBD_NETWORK_MANUFACTURER "Belcarra"
+   string 'iProduct (string)'      CONFIG_USBD_NETWORK_PRODUCT_NAME "Belcarra BLAN Device"
+
+
+   comment ''
+   bool " MDLM-BLAN Networking mode (Personal Devices)"  CONFIG_USBD_NETWORK_BLAN
+   if [ "$CONFIG_USBD_NETWORK_BLAN" = "y" ]; then
+      string '    iConfiguration (string)' CONFIG_USBD_NETWORK_BLAN_DESC "BLAN Net Cfg"
+      string '    iInterface (string)'     CONFIG_USBD_NETWORK_BLAN_INTF "Comm/Data Intf"
+   fi
+
+   if [ "$CONFIG_USBD_NETWORK_BLAN" = "y" ]; then
+      bool " CRC"  CONFIG_USBD_NETWORK_BLAN_CRC
+
+      if [ "$CONFIG_USBD_NETWORK_BLAN_CRC" = "y" ]; then
+
+         bool "     Pad Before CRC (to wMaxPacketSize-1)"  CONFIG_USBD_NETWORK_BLAN_PADBEFORE
+         bool "     Pad After CRC"  CONFIG_USBD_NETWORK_BLAN_PADAFTER
+         if [ "$CONFIG_USBD_NETWORK_BLAN" = "y" -a "$CONFIG_USBD_NETWORK_BLAN_PADAFTER" = "y" ]; then
+              int '        Pad multiple' CONFIG_USBD_NETWORK_BLAN_PADBYTES "8"
+         fi
+         bool "     Fermat Randomizer"  CONFIG_USBD_NETWORK_BLAN_FERMAT
+      fi
+      bool " Do not Set Time"  CONFIG_USBD_NETWORK_BLAN_DO_NOT_SETTIME
+      bool " Request Hostname"  CONFIG_USBD_NETWORK_BLAN_HOSTNAME
+      bool " Infrastructure Device"  CONFIG_USBD_NETWORK_BLAN_NOBRIDGE
+      comment ''
+   fi
+
+   bool " MDLM-SAFE Networking mode (Bridge/Routers)" CONFIG_USBD_NETWORK_SAFE
+   if [ "$CONFIG_USBD_NETWORK_SAFE" = "y" ]; then
+      string '    Data Interface iConfiguration (string)' CONFIG_USBD_NETWORK_SAFE_DESC "SAFE Net Cfg"
+      string '    Data Interface iInterface (string)'     CONFIG_USBD_NETWORK_SAFE_INTF "Data Intf"
+   fi
+
+
+   if [ "$CONFIG_USBD_NETWORK_SAFE" = "y" ]; then
+      bool " Do not Set Time"  CONFIG_USBD_NETWORK_SAFE_DO_NOT_SETTIME
+      bool " CRC"  CONFIG_USBD_NETWORK_SAFE_CRC
+      if [ "$CONFIG_USBD_NETWORK_SAFE_CRC" = "y" ]; then
+         bool "     Pad Before CRC (to wMaxPacketSize-1)"  CONFIG_USBD_NETWORK_SAFE_PADBEFORE
+      fi
+      bool " Infrastructure Device"  CONFIG_USBD_NETWORK_SAFE_NOBRIDGE
+      comment ''
+   fi
+
+   bool " CDC Networking mode (Bridge/Routers)" CONFIG_USBD_NETWORK_CDC
+   if [ "$CONFIG_USBD_NETWORK_CDC" = "y" ]; then
+      string '    iConfiguration (string)'            CONFIG_USBD_NETWORK_CDC_DESC "SAFE Net Cfg"
+      string '    Data Interface iInterface (string)' CONFIG_USBD_NETWORK_CDC_COMM_INTF "Comm Intf"
+      string '    Data (diabled) iInterface (string)' CONFIG_USBD_NETWORK_CDC_NODATA_INTF "Data (Disabled) Intf"
+      string '    Comm Interface iInterface (string)' CONFIG_USBD_NETWORK_CDC_DATA_INTF "Dat Intf"
+   fi
+
+   if [ "$CONFIG_USBD_NETWORK_SAFE" = "y" -a "$CONFIG_USBD_NETWORK_CDC" = "y" ]; then
+      comment 'Warning: CDC and MDLM-SAFE not allowed'
+   fi
+
+   
+   bool " Failsafe BASIC Networking mode" CONFIG_USBD_NETWORK_BASIC
+   if [ "$CONFIG_USBD_NETWORK_BASIC" = "y" ]; then
+      string '    Data Interface iConfiguration (string)' CONFIG_USBD_NETWORK_BASIC_DESC "BASIC Net Cfg"
+      string '    Data Interface iInterface (string)'     CONFIG_USBD_NETWORK_BASIC_INTF "Data Intf"
+   fi
+
+   bool " Failsafe BASIC2 Networking mode" CONFIG_USBD_NETWORK_BASIC2
+   if [ "$CONFIG_USBD_NETWORK_BASIC2" = "y" ]; then
+      string '    Data Interface iConfiguration (string)' CONFIG_USBD_NETWORK_BASIC2_DESC "BASIC Net Cfg"
+      string '    Comm Interface iInterface (string)'     CONFIG_USBD_NETWORK_BASIC2_COMM_INTF "Comm Intf"
+      string '    Data Interface iInterface (string)'     CONFIG_USBD_NETWORK_BASIC2_DATA_INTF "Data Intf"
+   fi
+
+
+   comment ''
+   bool    ' Start Single Urb Test' CONFIG_USBD_NETWORK_START_SINGLE
+   bool    ' EP0 Test' CONFIG_USBD_NETWORK_EP0TEST
+
+fi
+
+endmenu
diff -Nru a/drivers/usbd/network_fd/Makefile b/drivers/usbd/network_fd/Makefile
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/network_fd/Makefile	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,66 @@
+#
+# Network Function Driver
+#
+# Copyright (C) 2002-2003 Belcarra
+
+
+O_TARGET	:= network_fd.o
+list-multi	:= network_fd.o
+
+network_fd-objs	:= network.o basic.o basic2.o blan.o cdc.o safe.o fermat.o
+
+# Objects that export symbols.
+export-objs	:= network.o
+
+
+# Object file lists.
+
+obj-y		:=
+obj-m		:=
+obj-n		:=
+obj-		:=
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_USBD_NETWORK)   += network_fd.o
+
+# Extract lists of the multi-part drivers.
+# The 'int-*' lists are the intermediate files used to build the multi's.
+
+multi-y		:= $(filter $(list-multi), $(obj-y))
+multi-m		:= $(filter $(list-multi), $(obj-m))
+int-y		:= $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
+int-m		:= $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
+
+# Files that are both resident and modular: remove from modular.
+
+obj-m		:= $(filter-out $(obj-y), $(obj-m))
+int-m		:= $(filter-out $(int-y), $(int-m))
+
+# Translate to Rules.make lists.
+
+O_OBJS		:= $(filter-out $(export-objs), $(obj-y))
+OX_OBJS		:= $(filter     $(export-objs), $(obj-y))
+M_OBJS		:= $(sort $(filter-out $(export-objs), $(obj-m)))
+MX_OBJS		:= $(sort $(filter     $(export-objs), $(obj-m)))
+MI_OBJS		:= $(sort $(filter-out $(export-objs), $(int-m)))
+MIX_OBJS	:= $(sort $(filter     $(export-objs), $(int-m)))
+
+# The global Rules.make.
+
+USBD=$(TOPDIR)/drivers/usbd
+NETWORKD=$(USBD)/network_fd
+include $(TOPDIR)/Rules.make
+EXTRA_CFLAGS += -I$(NETWORKD) -I$(USBD) -Wno-unused -Wno-format 
+EXTRA_CFLAGS_nostdinc += -I$(NETWORKD) -I$(USBD) -Wno-unused -Wno-format 
+
+# Link rules for multi-part drivers.
+
+network_fd.o: $(network_fd-objs)
+	$(LD) -r -o $@ $(network_fd-objs)
+
+# dependencies:
+
+network.o: network.h $(USBD)/usbd.h $(USBD)/usbd-bus.h $(USBD)/usbd-func.h
+
+
diff -Nru a/drivers/usbd/network_fd/basic.c b/drivers/usbd/network_fd/basic.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/network_fd/basic.c	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,157 @@
+/*
+ * usbd/network_fd/basic.c - Network Function Driver
+ *
+ *      Copyright (c) 2002, 2003, 2004 Belcarra
+ *
+ * By: 
+ *      Chris Lynne <cl@belcarra.com>
+ *      Stuart Lynne <sl@belcarra.com>
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <usbd-export.h>
+#include <usbd-build.h>
+
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/utsname.h>
+#include <linux/netdevice.h>
+
+#include <usbd-chap9.h>
+#include <usbd-mem.h>
+#include <usbd.h>
+#include <usbd-func.h>
+
+#include "network.h"
+
+
+#ifdef CONFIG_USBD_NETWORK_BASIC
+/* USB BASIC Configuration ******************************************************************** */
+
+/* BASIC Communication Interface Class descriptors
+ */
+static __u8 basic_data_1[] = { 0x07, USB_DT_ENDPOINT, OUT, BULK,     0, 0x00, 0x00,  };
+static __u8 basic_data_2[] = { 0x07, USB_DT_ENDPOINT, IN,  BULK,     0, 0x00, 0x00, };
+static __u8 basic_comm_1[] = { 0x07, USB_DT_ENDPOINT, IN,  INTERRUPT,0, 0x00, 0x0a, };
+
+static  struct usb_endpoint_descriptor  *basic_default[] = { 
+        (struct usb_endpoint_descriptor *) &basic_data_1, 
+        (struct usb_endpoint_descriptor *) &basic_data_2, 
+        (struct usb_endpoint_descriptor *) &basic_comm_1, };
+u8 basic_indexes[] = { BULK_OUT, BULK_IN, INT_IN, };
+
+/* BASIC Data Interface Alternate endpoints
+ */
+static __u8 basic_data_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = {
+        0x09, USB_DT_INTERFACE, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting
+        sizeof (basic_default) / sizeof(struct usb_endpoint_descriptor), // bNumEndpoints
+        LINEO_CLASS, LINEO_SUBCLASS_BASIC_NET, LINEO_BASIC_NET_CRC, 0x00,
+};
+
+static struct usb_alternate_description basic_data_alternate_descriptions[] = {
+	{ iInterface: CONFIG_USBD_NETWORK_BASIC_INTF,
+                interface_descriptor: (struct usb_interface_descriptor *)&basic_data_alternate_descriptor,
+                endpoints:sizeof (basic_default) / sizeof(struct usb_endpoint_descriptor *),
+                endpoint_list: basic_default,
+                endpoint_indexes: basic_indexes,
+                },
+};
+
+
+/* BASIC Data Interface Alternate descriptions and descriptors
+ */
+
+/* BASIC Interface descriptions and descriptors
+ */
+struct usb_interface_description basic_interfaces[] = {
+	{ alternates:sizeof (basic_data_alternate_descriptions) / sizeof (struct usb_alternate_description),
+                alternate_list:basic_data_alternate_descriptions,},
+};
+
+/* BASIC Configuration descriptions and descriptors
+ */
+__u8 basic_configuration_descriptor[sizeof(struct usb_configuration_descriptor)] = {
+        0x09, USB_DT_CONFIG, 0x00, 0x00, // wLength
+        sizeof (basic_interfaces) / sizeof (struct usb_interface_description),
+        0x01, 0x00, // bConfigurationValue, iConfiguration
+        BMATTRIBUTE, BMAXPOWER,
+};
+
+struct usb_configuration_description basic_description[] = {
+	{ iConfiguration: CONFIG_USBD_NETWORK_BASIC_DESC,
+                configuration_descriptor: (struct usb_configuration_descriptor *)basic_configuration_descriptor,
+                bNumInterfaces:sizeof (basic_interfaces) / sizeof (struct usb_interface_description),
+                interface_list:basic_interfaces,},
+
+};
+
+/* BASIC Device Description
+ */
+static struct usb_device_descriptor basic_device_descriptor = {
+	bLength: sizeof(struct usb_device_descriptor),
+	bDescriptorType: USB_DT_DEVICE,
+	bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
+	bDeviceClass: COMMUNICATIONS_DEVICE_CLASS,
+	bDeviceSubClass: 0x02,
+	bDeviceProtocol: 0x00,
+	bMaxPacketSize0: 0x00,
+	idVendor: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_VENDORID),
+	idProduct: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_PRODUCTID),
+	bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_BCDDEVICE),
+};
+
+static struct usb_endpoint_request basic_endpoint_requests[ENDPOINTS+1] = {
+        { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, },
+        { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, },
+        { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, },
+        { 0, },
+};
+
+struct usb_device_description basic_device_description = {
+        device_descriptor: &basic_device_descriptor,
+	iManufacturer: CONFIG_USBD_NETWORK_MANUFACTURER,
+	iProduct: CONFIG_USBD_NETWORK_PRODUCT_NAME,
+#if !defined(CONFIG_USBD_NO_SERIAL_NUMBER) && defined(CONFIG_USBD_SERIAL_NUMBER_STR)
+	iSerialNumber:CONFIG_USBD_SERIAL_NUMBER_STR,
+#endif
+        endpointsRequested: ENDPOINTS,
+        requestedEndpoints: basic_endpoint_requests,
+};
+
+
+void basic_init (struct usb_function_instance *function)
+{
+        basic_data_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 3 : 2;
+}
+
+struct usb_function_driver basic_function_driver = {
+	name: "network-BASIC",
+	fops: &network_fd_function_ops,
+	device_description: &basic_device_description,
+	bNumConfigurations: sizeof (basic_description) / sizeof (struct usb_configuration_description),
+	configuration_description: basic_description,
+	idVendor: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_VENDORID),
+	idProduct: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_PRODUCTID),
+	bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_BCDDEVICE),
+};
+#endif				/* CONFIG_USBD_NETWORK_BASIC */
+
diff -Nru a/drivers/usbd/network_fd/basic2.c b/drivers/usbd/network_fd/basic2.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/network_fd/basic2.c	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,198 @@
+/*
+ * usbd/network_fd/basic22.c - Network Function Driver
+ *
+ *      Copyright (c) 2002, 2003, 2004 Belcarra
+ *
+ * By: 
+ *      Chris Lynne <cl@belcarra.com>
+ *      Stuart Lynne <sl@belcarra.com>
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <usbd-export.h>
+#include <usbd-build.h>
+
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/utsname.h>
+#include <linux/netdevice.h>
+
+#include <usbd-chap9.h>
+#include <usbd-mem.h>
+#include <usbd.h>
+#include <usbd-func.h>
+
+#include "network.h"
+
+
+#ifdef CONFIG_USBD_NETWORK_BASIC2
+/* USB BASIC Configuration ******************************************************************** */
+
+/*
+ * This provides a slight amplification of the basic configuration, it moves the
+ * interrupt endpoint (if available) to a separate interface, so that it is similiar
+ * to the cdc configuration
+ */
+
+/* BASIC Communication Interface Class descriptors
+ */
+static __u8 basic2_data_1[] = { 0x07, USB_DT_ENDPOINT, OUT, BULK,     0, 0x00, 0x00,  };
+static __u8 basic2_data_2[] = { 0x07, USB_DT_ENDPOINT, IN,  BULK,     0, 0x00, 0x00, };
+static __u8 basic2_comm_1[] = { 0x07, USB_DT_ENDPOINT, IN,  INTERRUPT,0, 0x00, 0x0a, };
+
+static __u8 *basic2_comm_endpoints[] = { 
+        (struct usb_endpoint_descriptor *) &basic2_comm_1, };
+
+static __u8 *basic2_data_endpoints[] = { 
+        (struct usb_endpoint_descriptor *) &basic2_data_1, 
+        (struct usb_endpoint_descriptor *) &basic2_data_2, };
+
+u8 basic2_comm_indexes[] = { INT_IN, };
+u8 basic2_data_indexes[] = { BULK_OUT, BULK_IN, };
+
+
+/* BASIC2 Data Interface Alternate endpoints
+ */
+
+static __u8 basic2_comm_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = {
+        0x09, USB_DT_INTERFACE, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting
+        sizeof (basic2_comm_endpoints) / sizeof(struct usb_endpoint_descriptor), // bNumEndpoints
+        COMMUNICATIONS_INTERFACE_CLASS, COMMUNICATIONS_NETWORK_SUBCLASS, VENDOR, 0x00,
+};
+static __u8 basic2_data_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = {
+        0x09, USB_DT_INTERFACE, 0x01, 0x00, // bInterfaceNumber, bAlternateSetting
+        sizeof (basic2_data_endpoints) / sizeof(struct usb_endpoint_descriptor), // bNumEndpoints
+        DATA_INTERFACE_CLASS, COMMUNICATIONS_NO_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00,
+};
+
+static struct usb_alternate_description basic2_comm_alternate_descriptions[] = {
+      { iInterface: CONFIG_USBD_NETWORK_BASIC2_COMM_INTF,
+                interface_descriptor: (struct usb_interface_descriptor *)&basic2_comm_alternate_descriptor,
+                endpoints:sizeof (basic2_comm_endpoints) / sizeof(struct usb_endpoint_descriptor *),
+                endpoint_list:basic2_comm_endpoints,
+                endpoint_indexes:basic2_comm_indexes,
+                },
+};
+
+
+static struct usb_alternate_description basic2_data_alternate_descriptions[] = {
+      { iInterface: CONFIG_USBD_NETWORK_BASIC2_DATA_INTF,
+                interface_descriptor: (struct usb_interface_descriptor *)&basic2_data_alternate_descriptor,
+                endpoints:sizeof (basic2_data_endpoints) / sizeof(struct usb_endpoint_descriptor *),
+                endpoint_list: basic2_data_endpoints,
+                endpoint_indexes: basic2_data_indexes,
+        },
+};
+
+
+/* BASIC Data Interface Alternate descriptions and descriptors
+ */
+
+/* BASIC Interface descriptions and descriptors
+ */
+static struct usb_interface_description basic2_interfaces[] = {
+        { alternates:sizeof (basic2_comm_alternate_descriptions) / sizeof (struct usb_alternate_description),
+                alternate_list:basic2_comm_alternate_descriptions,},
+
+        { alternates:sizeof (basic2_data_alternate_descriptions) / sizeof (struct usb_alternate_description),
+                alternate_list:basic2_data_alternate_descriptions,},
+};
+
+
+/* BASIC Configuration descriptions and descriptors
+ */
+__u8 basic2_configuration_descriptor[sizeof(struct usb_configuration_descriptor)] = {
+        0x09, USB_DT_CONFIG, 0x00, 0x00, // wLength
+        sizeof (basic2_interfaces) / sizeof (struct usb_interface_description),
+        0x01, 0x00, // bConfigurationValue, iConfiguration
+        BMATTRIBUTE, BMAXPOWER,
+};
+
+struct usb_configuration_description basic2_description[] = {
+	{ iConfiguration: CONFIG_USBD_NETWORK_BASIC2_DESC,
+                configuration_descriptor: (struct usb_configuration_descriptor *)basic2_configuration_descriptor,
+                bNumInterfaces:sizeof (basic2_interfaces) / sizeof (struct usb_interface_description),
+                interface_list:basic2_interfaces,},
+
+};
+
+/* BASIC Device Description
+ */
+static struct usb_device_descriptor basic2_device_descriptor = {
+	bLength: sizeof(struct usb_device_descriptor),
+	bDescriptorType: USB_DT_DEVICE,
+	bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
+	bDeviceClass: COMMUNICATIONS_DEVICE_CLASS,
+	bDeviceSubClass: 0x02,
+	bDeviceProtocol: 0x00,
+	bMaxPacketSize0: 0x00,
+	idVendor: __constant_cpu_to_le16(CONFIG_USBD_BASIC2_VENDORID),
+	idProduct: __constant_cpu_to_le16(CONFIG_USBD_BASIC2_PRODUCTID),
+	bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_BASIC2_BCDDEVICE),
+};
+
+static struct usb_endpoint_request basic2_endpoint_requests[ENDPOINTS+1] = {
+        { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512,  },
+        { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, },
+        { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, },
+        { 0, },
+};
+
+struct usb_device_description basic2_device_description = {
+        device_descriptor: &basic2_device_descriptor,
+	iManufacturer: CONFIG_USBD_NETWORK_MANUFACTURER,
+	iProduct: CONFIG_USBD_NETWORK_PRODUCT_NAME,
+#if !defined(CONFIG_USBD_NO_SERIAL_NUMBER) && defined(CONFIG_USBD_SERIAL_NUMBER_STR)
+	iSerialNumber:CONFIG_USBD_SERIAL_NUMBER_STR,
+#endif
+        endpointsRequested: ENDPOINTS,
+        requestedEndpoints: basic2_endpoint_requests,
+};
+
+
+void basic2_init (struct usb_function_instance *function)
+{
+        printk(KERN_INFO"%s:\n", __FUNCTION__);
+
+        basic2_comm_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 1 : 0;
+
+        printk(KERN_INFO"%s: alternate: %p endpoints: %d\n", __FUNCTION__,
+                        basic2_data_alternate_descriptions,
+                        basic2_data_alternate_descriptions->endpoints
+        );
+
+        printk(KERN_INFO"%s: finis\n", __FUNCTION__);
+}
+
+
+struct usb_function_driver basic2_function_driver = {
+	name: "network-BASIC2",
+	fops: &network_fd_function_ops,
+	device_description: &basic2_device_description,
+	bNumConfigurations: sizeof (basic2_description) / sizeof (struct usb_configuration_description),
+	configuration_description: basic2_description,
+	idVendor: __constant_cpu_to_le16(CONFIG_USBD_BASIC2_VENDORID),
+	idProduct: __constant_cpu_to_le16(CONFIG_USBD_BASIC2_PRODUCTID),
+	bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_BASIC2_BCDDEVICE),
+};
+#endif				/* CONFIG_USBD_NETWORK_BASIC2 */
+
diff -Nru a/drivers/usbd/network_fd/blan.c b/drivers/usbd/network_fd/blan.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/network_fd/blan.c	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,251 @@
+/*
+ * usbd/network_fd/blan.c - Network Function Driver
+ *
+ *      Copyright (c) 2002, 2003, 2004 Belcarra
+ *
+ * By: 
+ *      Chris Lynne <cl@belcarra.com>
+ *      Stuart Lynne <sl@belcarra.com>
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+
+#include <usbd-export.h>
+#include <usbd-build.h>
+
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/utsname.h>
+#include <linux/netdevice.h>
+
+#include <usbd-chap9.h>
+#include <usbd-mem.h>
+#include <usbd.h>
+#include <usbd-func.h>
+
+#include "network.h"
+
+
+#ifdef CONFIG_USBD_NETWORK_BLAN
+/* USB BLAN  Configuration ******************************************************************** */
+
+/*
+ * BLAN Ethernet Configuration
+ */
+
+/* Communication Interface Class descriptors
+ */
+
+static __u8 blan_data_1[] = { 0x07, USB_DT_ENDPOINT, OUT, BULK,     0, 0x00, 0x00, };
+static __u8 blan_data_2[] = { 0x07, USB_DT_ENDPOINT, IN,  BULK,     0, 0x00, 0x00, };
+static __u8 blan_comm_1[] = { 0x07, USB_DT_ENDPOINT, IN,  INTERRUPT,0, 0x00, 0x0a, };
+
+static __u8 blan_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x10, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_VERSION */ };
+static __u8 blan_class_2[] = { 0x15, CS_INTERFACE, USB_ST_MDLM, 0x00, 0x01, /* bcdVersion, bcdVersion */
+                0x74, 0xf0, 0x3d, 0xbd, 0x1e, 0xc1, 0x44, 0x70,  /* bGUID */
+                0xa3, 0x67, 0x71, 0x34, 0xc9, 0xf5, 0x54, 0x37,  /* bGUID */ };
+
+
+static __u8 blan_class_3[] = { 0x07, CS_INTERFACE, USB_ST_MDLMD, 0x01, 0x00, 0x00, 0x00, };
+
+static __u8 blan_class_4[] = { 0x0d, CS_INTERFACE, USB_ST_ENF, 
+        0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x05, /* 1514 maximum frame size */
+        0x00, 0x00, 0x00 , };
+
+static __u8 blan_class_5[] = { 0x07, CS_INTERFACE, USB_ST_NCT, 0x00, 0x00, 0x00, 0x00, };
+
+
+static struct usb_endpoint_descriptor *blan_alt_endpoints[] = { 
+        (struct usb_endpoint_descriptor *) blan_data_1, 
+        (struct usb_endpoint_descriptor *) blan_data_2, 
+        (struct usb_endpoint_descriptor *) blan_comm_1, };
+
+u8 blan_alt_indexes[] = { BULK_OUT, BULK_IN, INT_IN, };
+
+static struct usb_generic_class_descriptor *blan_comm_class_descriptors[] = {
+        (struct usb_generic_class_descriptor *) blan_class_1, 
+        (struct usb_generic_class_descriptor *) blan_class_2, 
+        (struct usb_generic_class_descriptor *) blan_class_3, 
+        (struct usb_generic_class_descriptor *) blan_class_4, 
+        (struct usb_generic_class_descriptor *) blan_class_5, };
+
+
+/* Data Interface Alternate descriptions and descriptors
+ */
+static __u8 blan_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = {
+        0x09, USB_DT_INTERFACE, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting
+        sizeof (blan_alt_endpoints) / sizeof(struct usb_endpoint_descriptor), // bNumEndpoints
+        COMMUNICATIONS_INTERFACE_CLASS, COMMUNICATIONS_MDLM_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00,
+};
+
+static struct usb_alternate_description blan_alternate_descriptions[] = {
+	{ iInterface: CONFIG_USBD_NETWORK_BLAN_INTF,
+                interface_descriptor: (struct usb_interface_descriptor *)&blan_alternate_descriptor,
+                classes:sizeof (blan_comm_class_descriptors) / sizeof (struct usb_generic_class_descriptor *),
+                class_list: blan_comm_class_descriptors,
+                endpoints:sizeof (blan_alt_endpoints) / sizeof(struct usb_endpoint_descriptor *),
+                endpoint_list: blan_alt_endpoints,
+                endpoint_indexes: blan_alt_indexes,
+        },
+};
+/* Interface descriptions and descriptors
+ */
+static struct usb_interface_description blan_interfaces[] = {
+	{ 
+                alternates: sizeof (blan_alternate_descriptions) / sizeof (struct usb_alternate_description),
+                alternate_list: blan_alternate_descriptions, 
+        },
+};
+
+
+/* Configuration descriptions and descriptors
+ */
+
+static __u8 blan_configuration_descriptor[sizeof(struct usb_configuration_descriptor)] = {
+        0x09, USB_DT_CONFIG, 0x00, 0x00, // wLength
+        sizeof (blan_interfaces) / sizeof (struct usb_interface_description),
+        0x01, 0x00, // bConfigurationValue, iConfiguration
+        BMATTRIBUTE, BMAXPOWER,
+};
+
+struct usb_configuration_description blan_description[] = {
+	{ iConfiguration: CONFIG_USBD_NETWORK_BLAN_DESC,
+                configuration_descriptor: (struct usb_configuration_descriptor *)blan_configuration_descriptor,
+	        bNumInterfaces:sizeof (blan_interfaces) / sizeof (struct usb_interface_description),
+                interface_list:blan_interfaces, },
+};
+
+/* Device Description
+ */
+
+static struct usb_device_descriptor blan_device_descriptor = {
+	bLength: sizeof(struct usb_device_descriptor),
+	bDescriptorType: USB_DT_DEVICE,
+	bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
+	bDeviceClass: COMMUNICATIONS_DEVICE_CLASS,
+	bDeviceSubClass: 0x02,
+	bDeviceProtocol: 0x00,
+	bMaxPacketSize0: 0x00,
+	idVendor: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_VENDORID),
+	idProduct: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_PRODUCTID),
+	bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_BCDDEVICE),
+};
+
+static struct usb_endpoint_request blan_endpoint_requests[ENDPOINTS+1] = {
+        { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512,  },
+        { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512,  },
+        { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, },
+        { 0, },
+};
+
+struct usb_device_description blan_device_description = {
+        device_descriptor: &blan_device_descriptor,
+	iManufacturer: CONFIG_USBD_NETWORK_MANUFACTURER,
+	iProduct: CONFIG_USBD_NETWORK_PRODUCT_NAME,
+#if !defined(CONFIG_USBD_NO_SERIAL_NUMBER) && defined(CONFIG_USBD_SERIAL_NUMBER_STR)
+	iSerialNumber:CONFIG_USBD_SERIAL_NUMBER_STR,
+#endif
+        endpointsRequested: ENDPOINTS,
+        requestedEndpoints: blan_endpoint_requests,
+};
+
+
+void blan_init (struct usb_function_instance *function)
+{
+        struct usb_class_ethernet_networking_descriptor *ethernet;
+        struct usb_class_network_channel_descriptor *channel;
+
+        int len = 0;
+        char buf[255];
+
+        buf[0] = 0;
+
+        blan_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 3 : 2;
+
+        // Update the iMACAddress field in the ethernet descriptor
+        {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17)
+                char address_str[14];
+                snprintf(address_str, 13, "%02x%02x%02x%02x%02x%02x",
+                                local_dev_addr[0], local_dev_addr[1], local_dev_addr[2], 
+                                local_dev_addr[3], local_dev_addr[4], local_dev_addr[5]);
+#else
+                char address_str[20];
+                sprintf(address_str, "%02x%02x%02x%02x%02x%02x",
+                                local_dev_addr[0], local_dev_addr[1], local_dev_addr[2], 
+                                local_dev_addr[3], local_dev_addr[4], local_dev_addr[5]);
+#endif
+                //printk(KERN_INFO"%s: alloc mac string\n", __FUNCTION__);
+                if ((ethernet = (struct usb_class_ethernet_networking_descriptor *)blan_class_4)) 
+                        ethernet->iMACAddress = usbd_alloc_string(address_str);
+                //printk(KERN_INFO"%s: alloc mac string finis\n", __FUNCTION__);
+        }
+        //printk(KERN_INFO"%s: alloc channel string\n", __FUNCTION__);
+        if ((channel = (struct usb_class_network_channel_descriptor *)blan_class_5)) 
+                channel->iName = usbd_alloc_string(system_utsname.nodename);
+        //printk(KERN_INFO"%s: alloc channel string finis\n", __FUNCTION__);
+
+#ifdef CONFIG_USBD_NETWORK_BLAN_PADBYTES
+        blan_class_3[6] = CONFIG_USBD_NETWORK_BLAN_PADBYTES;
+        len += sprintf(buf + len, "PADBYTES: %02x ", blan_class_3[6]);
+#endif
+#ifdef CONFIG_USBD_NETWORK_BLAN_PADBEFORE
+        blan_class_3[5] |= BMDATA_PADBEFORE;
+        len += sprintf(buf + len, "PADBEFORE: %02x ", blan_class_3[5]);
+#endif
+#ifdef CONFIG_USBD_NETWORK_BLAN_PADAFTER
+        blan_class_3[5] |= BMDATA_PADAFTER;
+        len += sprintf(buf + len, "PADAFTER: %02x ", blan_class_3[5]);
+#endif
+#ifdef CONFIG_USBD_NETWORK_BLAN_CRC
+        blan_class_3[5] |= BMDATA_CRC;
+        len += sprintf(buf + len, "CRC: %02x ", blan_class_3[5]);
+#endif
+#ifdef CONFIG_USBD_NETWORK_BLAN_FERMAT
+        blan_class_3[5] |= BMDATA_FERMAT;
+        len += sprintf(buf + len, "FERMAT: %02x ",blan_class_3[5]);
+#endif
+#ifdef CONFIG_USBD_NETWORK_BLAN_HOSTNAME
+        blan_class_3[5] |= BMDATA_HOSTNAME;
+        len += sprintf(buf + len, "HOSTNAME: %02x ",blan_class_3[5]);
+#endif
+#ifdef CONFIG_USBD_NETWORK_BLAN_NOBRIDGE
+        blan_class_3[4] |= BMNETWORK_NOBRIDGE;
+        len += sprintf(buf + len, "NOBRIDGE: %02x ",blan_class_3[4]);
+#endif
+        if (strlen(buf))
+                printk(KERN_INFO"%s: %s\n", __FUNCTION__, buf);
+}
+
+struct usb_function_driver blan_function_driver = {
+	name: "network-BLAN",
+	fops: &network_fd_function_ops,
+	device_description: &blan_device_description,
+	bNumConfigurations: sizeof (blan_description) / sizeof (struct usb_configuration_description),
+	configuration_description: blan_description,
+	idVendor: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_VENDORID),
+	idProduct: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_PRODUCTID),
+	bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_BCDDEVICE),
+};
+#endif				/* CONFIG_USBD_NETWORK_BLAN */
+
diff -Nru a/drivers/usbd/network_fd/cdc.c b/drivers/usbd/network_fd/cdc.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/network_fd/cdc.c	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,222 @@
+/*
+ * usbd/network_fd/cdc.c - Network Function Driver
+ *
+ *      Copyright (c) 2002, 2003, 2004 Belcarra
+ *
+ * By: 
+ *      Chris Lynne <cl@belcarra.com>
+ *      Stuart Lynne <sl@belcarra.com>
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <usbd-export.h>
+#include <usbd-build.h>
+
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/utsname.h>
+#include <linux/netdevice.h>
+
+#include <usbd-chap9.h>
+#include <usbd-mem.h>
+#include <usbd.h>
+#include <usbd-func.h>
+
+#include "network.h"
+
+
+
+#ifdef CONFIG_USBD_NETWORK_CDC
+/* USB CDC Configuration ********************************************************************* */
+
+/* CDC Communication Interface Class descriptors
+ */
+static __u8 cdc_data_1[] = { 0x07, USB_DT_ENDPOINT, 0 | OUT, BULK,     0, 0x00, 0x00, };
+static __u8 cdc_data_2[] = { 0x07, USB_DT_ENDPOINT, 0 | IN,  BULK,     0,  0x00, 0x00, };
+
+static __u8 cdc_comm_1[] = { 0x07, USB_DT_ENDPOINT, 0 | IN,  INTERRUPT,0, 0x00, 0x0a, };
+
+static __u8 cdc_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x10, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_VERSION */ };
+static __u8 cdc_class_2[] = { 
+        0x0d, CS_INTERFACE, USB_ST_ENF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x05, /* 1514 maximum frame size */
+        0x00, 0x00, 0x00 , };
+static __u8 cdc_class_3[] = { 0x05, CS_INTERFACE, USB_ST_UF, 0x00, 0x01, /* bMasterInterface, bSlaveInterface */};
+
+
+static __u8 *cdc_comm_class_descriptors[] = { 
+        (struct usb_generic_class_descriptor *) &cdc_class_1, 
+        (struct usb_generic_class_descriptor *) &cdc_class_2, 
+        (struct usb_generic_class_descriptor *) &cdc_class_3, };
+
+static __u8 *cdc_data_endpoints[] = { 
+        (struct usb_endpoint_descriptor *) &cdc_data_1, 
+        (struct usb_endpoint_descriptor *) &cdc_data_2, };
+static __u8 *cdc_comm_endpoints[] = { 
+        (struct usb_endpoint_descriptor *) &cdc_comm_1, };
+
+u8 cdc_comm_indexes[] = { INT_IN, };
+u8 cdc_data_indexes[] = { BULK_OUT, BULK_IN, };
+
+
+/* Data Interface Alternate descriptions and descriptors
+ */
+static __u8 cdc_comm_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = {
+        0x09, USB_DT_INTERFACE, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting
+        sizeof (cdc_comm_endpoints) / sizeof(struct usb_endpoint_descriptor), // bNumEndpoints
+        COMMUNICATIONS_INTERFACE_CLASS, COMMUNICATIONS_ENCM_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00,
+};
+static __u8 cdc_nodata_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = {
+        0x09, USB_DT_INTERFACE, 0x01, 0x00, // bInterfaceNumber, bAlternateSetting
+        0x00, // bNumEndpoints
+        DATA_INTERFACE_CLASS, COMMUNICATIONS_NO_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00,
+};
+static __u8 cdc_data_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = {
+        0x09, USB_DT_INTERFACE, 0x01, 0x01, // bInterfaceNumber, bAlternateSetting
+        sizeof (cdc_data_endpoints) / sizeof(struct usb_endpoint_descriptor), // bNumEndpoints
+        DATA_INTERFACE_CLASS, COMMUNICATIONS_NO_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00,
+};
+
+static struct usb_alternate_description cdc_comm_alternate_descriptions[] = {
+	{ iInterface: CONFIG_USBD_NETWORK_CDC_COMM_INTF,
+                interface_descriptor: (struct usb_interface_descriptor *)&cdc_comm_alternate_descriptor,
+                classes:sizeof (cdc_comm_class_descriptors) / sizeof (struct usb_generic_class_descriptor *),
+                class_list: cdc_comm_class_descriptors,
+                endpoints:sizeof (cdc_comm_endpoints) / sizeof(struct usb_endpoint_descriptor *),
+                endpoint_list: cdc_comm_endpoints,
+                endpoint_indexes: cdc_comm_indexes,
+        },
+};
+
+
+static struct usb_alternate_description cdc_data_alternate_descriptions[] = {
+	{ iInterface: CONFIG_USBD_NETWORK_CDC_NODATA_INTF,
+                interface_descriptor: (struct usb_interface_descriptor *)&cdc_nodata_alternate_descriptor, },
+	{ iInterface: CONFIG_USBD_NETWORK_CDC_DATA_INTF,
+                interface_descriptor: (struct usb_interface_descriptor *)&cdc_data_alternate_descriptor,
+                endpoints:sizeof (cdc_data_endpoints) / sizeof(struct usb_endpoint_descriptor *),
+                endpoint_list: cdc_data_endpoints,
+                endpoint_indexes: cdc_data_indexes,
+                },
+};
+
+/* Interface descriptions and descriptors
+ */
+struct usb_interface_description cdc_interfaces[] = {
+	{ alternates:sizeof (cdc_comm_alternate_descriptions) / sizeof (struct usb_alternate_description),
+                alternate_list:cdc_comm_alternate_descriptions,},
+
+	{ alternates:sizeof (cdc_data_alternate_descriptions) / sizeof (struct usb_alternate_description),
+                alternate_list:cdc_data_alternate_descriptions,},
+};
+
+/* Configuration descriptions and descriptors
+ */
+
+__u8 cdc_configuration_descriptor[sizeof(struct usb_configuration_descriptor)] = {
+        0x09, USB_DT_CONFIG, 0x00, 0x00, // wLength
+        sizeof (cdc_interfaces) / sizeof (struct usb_interface_description),
+        0x01, 0x00, // bConfigurationValue, iConfiguration
+        BMATTRIBUTE, BMAXPOWER,
+};
+
+struct usb_configuration_description cdc_description[] = {
+	{ iConfiguration: CONFIG_USBD_NETWORK_CDC_DESC,
+                configuration_descriptor: (struct usb_configuration_descriptor *)cdc_configuration_descriptor,
+                bNumInterfaces:sizeof (cdc_interfaces) / sizeof (struct usb_interface_description),
+                interface_list:cdc_interfaces,},
+};
+
+
+
+/* Device Description
+ */
+
+static struct usb_device_descriptor cdc_device_descriptor = {
+	bLength: sizeof(struct usb_device_descriptor),
+	bDescriptorType: USB_DT_DEVICE,
+	bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
+	bDeviceClass: COMMUNICATIONS_DEVICE_CLASS,
+	bDeviceSubClass: 0x02,
+	bDeviceProtocol: 0x00,
+	bMaxPacketSize0: 0x00,
+	idVendor: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_VENDORID),
+	idProduct: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_PRODUCTID),
+	bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_BCDDEVICE),
+};
+
+static struct usb_endpoint_request cdc_endpoint_requests[ENDPOINTS+1] = {
+        { 1, 1, 1, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512,  },
+        { 1, 1, 1, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512,  },
+        { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, },
+        { 0, },
+};
+
+struct usb_device_description cdc_device_description = {
+        device_descriptor: &cdc_device_descriptor,
+	iManufacturer: CONFIG_USBD_NETWORK_MANUFACTURER,
+	iProduct: CONFIG_USBD_NETWORK_PRODUCT_NAME,
+#if !defined(CONFIG_USBD_NO_SERIAL_NUMBER) && defined(CONFIG_USBD_SERIAL_NUMBER_STR)
+	iSerialNumber:CONFIG_USBD_SERIAL_NUMBER_STR,
+#endif
+        endpointsRequested: ENDPOINTS,
+        requestedEndpoints: cdc_endpoint_requests,
+};
+
+
+
+void cdc_init (struct usb_function_instance *function)
+{
+        struct usb_class_ethernet_networking_descriptor *ethernet;
+
+        cdc_comm_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 1 : 0;
+
+        // Update the iMACAddress field in the ethernet descriptor
+        {
+                char address_str[14];
+                snprintf(address_str, 13, "%02x%02x%02x%02x%02x%02x",
+                                remote_dev_addr[0], remote_dev_addr[1], remote_dev_addr[2], 
+                                remote_dev_addr[3], remote_dev_addr[4], remote_dev_addr[5]);
+
+                if ((ethernet = (struct usb_class_ethernet_networking_descriptor *)cdc_class_2)) {
+                        if (ethernet->iMACAddress) {
+                                usbd_dealloc_string(ethernet->iMACAddress);
+                        }
+                        ethernet->iMACAddress = usbd_alloc_string(address_str);
+                }
+        }
+}
+
+
+struct usb_function_driver cdc_function_driver = {
+	name: "network-CDC",
+	fops: &network_fd_function_ops,
+	device_description: &cdc_device_description,
+	bNumConfigurations: sizeof (cdc_description) / sizeof (struct usb_configuration_description),
+	configuration_description: cdc_description,
+	idVendor: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_VENDORID),
+	idProduct: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_PRODUCTID),
+	bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_BCDDEVICE),
+};
+#endif				/* CONFIG_USBD_NETWORK_CDC */
+
diff -Nru a/drivers/usbd/network_fd/fermat.c b/drivers/usbd/network_fd/fermat.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/network_fd/fermat.c	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,132 @@
+/*
+ * usbd/network_fd/fermat.c - Network Function Driver
+ *
+ *      Copyright (c) 2003, 2004 Belcarra
+ *
+ * By: 
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+
+#ifdef CONFIG_USBD_NETWORK_BLAN_FERMAT
+
+#include "fermat.h"
+
+#ifndef FERMAT_DEFINED
+typedef unsigned char BYTE;
+typedef struct fermat {
+	int length;
+	BYTE power[256];
+} FERMAT;
+#endif
+
+
+static int fermat_setup(FERMAT *p, int seed){
+	int i = 0;
+	unsigned long x,y;
+	y = 1;
+	do{
+		x = y;
+		p->power[i] = ( x == 256 ? 0 : x);
+		y = ( seed * x ) % 257;
+		i += 1;
+	}while( y != 1);
+	p->length = i;
+	return i;
+}
+
+static void fermat_xform(FERMAT *p, BYTE *data, int length){
+	BYTE *pw = p->power;
+	int   i, j;
+	BYTE * q ;
+	for(i = 0, j=0, q = data; i < length; i++, j++, q++){
+		if(j>=p->length){
+			j = 0;
+		}
+		*q ^= pw[j];
+	}
+}
+
+static FERMAT default_fermat;
+static const int primitive_root = 5;
+void fermat_init(){
+	(void) fermat_setup(&default_fermat, primitive_root); 
+}
+
+// Here are the public official versions.
+// Change the primitive_root above to another primitive root
+// if you need better scatter. Possible values are 3 and 7
+
+
+void fermat_encode(BYTE *data, int length){
+	fermat_xform(&default_fermat, data, length);
+}
+
+void fermat_decode(BYTE *data, int length){
+	fermat_xform(&default_fermat, data, length);
+}
+
+		
+// Note: the seed must be a "primitive root" of 257. This means that
+// the return value of the setup routine must be 256 (otherwise the
+// seed is not a primitive root.  The routine will still work fine
+// but will be less pseudo-random.
+
+#undef TEST 
+#if TEST
+#include <stdio.h>
+#include <memory.h>
+
+// Use FERMAT in two ways: to encode, and to generate test data.
+
+main(){
+	//Note 3, 5, and 7 are primitive roots of 257
+	// 11 is not a primitive root
+	FERMAT three, five, seven;
+	
+	FERMAT three2;
+	printf("Cycle lengths: 3,5,7 %d %d %d \n", 
+			fermat_setup(&three, 3), 
+			fermat_setup(&five, 5), 
+			fermat_setup(&seven, 7));
+	three2=three; // Copy data from three
+	fermat_xform(&three,three2.power,three2.length);
+	fermat_xform(&five,three2.power,three2.length);
+	fermat_xform(&seven,three2.power,three2.length);
+	fermat_xform(&seven,three2.power,three2.length);
+	fermat_xform(&five,three2.power,three2.length);
+	fermat_xform(&three,three2.power,three2.length);
+
+	//At this stage, three2 and three should be identical
+	if(memcpy(&three,&three2,sizeof(FERMAT))){
+		printf("Decoded intact\n");
+	}
+
+	fermat_init();
+	fermat_encode(three2.power,256);
+	
+}
+#endif
+
+#endif /* CONFIG_USBD_NETWORK_BLAN_FERMAT */
+
diff -Nru a/drivers/usbd/network_fd/fermat.h b/drivers/usbd/network_fd/fermat.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/network_fd/fermat.h	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,39 @@
+/*
+ * usbd/network_fd/fermat.h - Network Function Driver
+ *
+ *      Copyright (c) 2003, 2004 Belcarra
+ *
+ * By: 
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#ifndef FERMAT_DEFINED
+#define FERMAT_DEFINED 1
+typedef unsigned char BYTE;
+typedef struct fermat {
+	int length;
+	BYTE power[256];
+} FERMAT;
+
+void fermat_init(void);
+void fermat_encode(BYTE *data, int length);
+void fermat_decode(BYTE *data, int length);
+#endif
+
+		
diff -Nru a/drivers/usbd/network_fd/network.c b/drivers/usbd/network_fd/network.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/network_fd/network.c	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,2112 @@
+/*
+ * usbd/network_fd/network.c - Network Function Driver
+ *
+ *      Copyright (c) 2002, 2003, 2004 Belcarra
+ *
+ * By: 
+ *      Chris Lynne <cl@belcarra.com>
+ *      Stuart Lynne <sl@belcarra.com>
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * This network function driver intended to interoperate with 
+ * Belcarra's USBLAN Class drivers. 
+ *
+ * These are available for Windows, Linux and Mac OSX. For more 
+ * information and to download a copy for testing:
+ *
+ *      http://www.belcarra.com/usblan/
+ *
+ *
+ * Notes
+ *
+ * 1. If compiled into the kernel this driver can be used with NFSROOT to
+ * provide the ROOT filesystem. Please note that the kernel NFSROOT support
+ * (circa 2.4.20) can have problems if there are multiple interfaces. So 
+ * it is best to ensure that there are no other network interfaces compiled
+ * in.
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+
+#include <usbd-export.h>
+#include <usbd-build.h>
+
+MODULE_AUTHOR ("sl@belcarra.com, balden@belcarra.com");
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17)
+MODULE_LICENSE("GPL");
+#endif
+
+MODULE_DESCRIPTION ("USB Network Function");
+
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <net/arp.h>
+#include <linux/rtnetlink.h>
+#include <linux/smp_lock.h>
+#include <linux/ctype.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/atmdev.h>
+#include <linux/pkt_sched.h>
+#include <linux/random.h>
+#include <linux/utsname.h>
+
+#include <linux/ip.h>
+#include <linux/if_ether.h>
+#include <linux/in.h>
+#include <linux/inetdevice.h>
+
+#include <linux/kmod.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <usbd-chap9.h>
+#include <usbd-mem.h>
+#include <usbd.h>
+#include <usbd-func.h>
+
+USBD_MODULE_INFO ("network_fd 2.0-beta");
+
+#include "network.h"
+#ifdef CONFIG_USBD_NETWORK_BLAN_FERMAT
+#include "fermat.h"
+#endif
+
+
+#if defined(CONFIG_USBD_NETWORK_CDC)
+
+static u32 cdc = 0;
+MODULE_PARM (cdc, "i");
+MODULE_PARM_DESC (cdc, "Enable CDC mode");
+void cdc_init(struct usb_function_instance *);
+
+extern struct usb_function_driver cdc_function_driver;
+
+#if defined(CONFIG_USBD_NETWORK_NETWORK_CDC_MYDHCPD)
+extern u_int8_t * checkfordhcp(struct net_device *net_device, u_int8_t *, int, int* );
+#endif
+
+#endif
+
+#ifdef CONFIG_USBD_NETWORK_BASIC
+static u32 basic = 0;
+MODULE_PARM (basic, "i");
+MODULE_PARM_DESC (basic, "Enable BASIC mode");
+void basic_init(struct usb_function_instance *);
+extern struct usb_function_driver basic_function_driver;
+#endif
+
+#ifdef CONFIG_USBD_NETWORK_BASIC2
+static u32 basic2 = 0;
+MODULE_PARM (basic, "i");
+MODULE_PARM_DESC (basic2, "Enable BASIC2 mode");
+void basic2_init(struct usb_function_instance *);
+extern struct usb_function_driver basic2_function_driver;
+#endif
+
+
+#ifdef CONFIG_USBD_NETWORK_SAFE
+static u32 safe = 0;
+MODULE_PARM (safe, "i");
+MODULE_PARM_DESC (safe, "Enable SAFE mode");
+void safe_init(struct usb_function_instance *);
+extern struct usb_function_driver safe_function_driver;
+#endif
+
+#ifdef CONFIG_USBD_NETWORK_BLAN
+static u32 blan = 0;
+MODULE_PARM (blan, "i");
+MODULE_PARM_DESC (blan, "Enable BLAN mode");
+void blan_init(struct usb_function_instance *);
+extern struct usb_function_driver blan_function_driver;
+#endif
+
+static struct usb_function_driver *function_driver = NULL;
+
+
+/* Module Parameters ************************************************************************* */
+
+wait_queue_head_t usb_netif_wq;
+#ifdef CONFIG_USBD_NET_NFS_SUPPORT
+int usb_is_configured;
+#endif
+
+#define CONFIG_USBD_NETWORK_ALLOW_SETID           1
+
+
+
+#ifdef CONFIG_USBD_NETWORK_ALLOW_SETID
+// override vendor ID
+static u32 vendor_id;
+MODULE_PARM (vendor_id, "i");
+MODULE_PARM_DESC (vendor_id, "vendor id");
+
+// override product ID
+static u32 product_id;
+MODULE_PARM (product_id, "i");
+MODULE_PARM_DESC (product_id, "product id");
+#endif
+
+// override local mac address
+#ifdef CONFIG_USBD_NETWORK_LOCAL_MACADDR
+static char *local_mac_address_str = CONFIG_USBD_NETWORK_LOCAL_MACADDR;
+#else
+static char *local_mac_address_str;
+#endif
+MODULE_PARM (local_mac_address_str, "s");
+MODULE_PARM_DESC (local_mac_address_str, "Local MAC");
+
+// override remote mac address
+#ifdef CONFIG_USBD_NETWORK_REMOTE_MACADDR
+static char *remote_mac_address_str = CONFIG_USBD_NETWORK_REMOTE_MACADDR;
+#else
+static char *remote_mac_address_str;
+#endif
+MODULE_PARM (remote_mac_address_str, "s");
+MODULE_PARM_DESC (remote_mac_address_str, "Remote MAC");
+
+
+#ifdef CONFIG_USBD_NETWORK_EP0TEST
+static u32 ep0test;
+MODULE_PARM (ep0test, "i");
+MODULE_PARM_DESC (ep0test, "Test EP0 String Handling - set to ep0 packetsize [8,16,32,64]");
+#endif
+
+
+static u32 zeroconf = 0;
+MODULE_PARM (zeroconf, "i");
+MODULE_PARM_DESC (zeroconf, "Use usbz%d for network name");
+
+#define NETWORK_CREATED         0x01
+#define NETWORK_REGISTERED      0x02
+#define NETWORK_DESTROYING      0x04
+#define NETWORK_ENABLED         0x08
+#define NETWORK_ATTACHED        0x10
+#define NETWORK_OPEN            0x20
+
+
+#define MCCI_ENABLE_CRC         0x03
+#define BELCARRA_GETMAC         0x04
+
+#define BELCARRA_SETTIME        0x04
+#define BELCARRA_SETIP          0x05
+#define BELCARRA_SETMSK         0x06
+#define BELCARRA_SETROUTER      0x07
+#define BELCARRA_SETDNS         0x08
+#define BELCARRA_PING           0x09
+#define BELCARRA_SETFERMAT      0x0a
+#define BELCARRA_HOSTNAME       0x0b
+
+
+#define RFC868_OFFSET_TO_EPOCH  0x83AA7E80      // RFC868 - seconds from midnight on 1 Jan 1900 GMT to Midnight 1 Jan 1970 GMT
+
+
+__u8 local_dev_addr[ETH_ALEN];
+
+__u8 remote_dev_addr[ETH_ALEN];
+static __u8 zeros[ETH_ALEN];
+
+static __u32 ip_addr;
+static __u32 router_ip;
+static __u32 network_mask;
+static __u32 dns_server_ip;
+
+/* Prevent overlapping of bus administrative functions:
+ *
+ *      network_function_enable
+ *      network_function_disable
+ *      network_hard_start_xmit
+ */
+DECLARE_MUTEX(usbd_network_sem);
+
+
+struct net_device Network_net_device;
+struct usb_network_private Usb_network_private;
+
+static void network_send_int_blan(struct usb_function_instance *function, int connected );
+static void notification_schedule_bh (void);
+static int network_urb_sent_bulk (struct urb *urb, int urb_rc);
+static int network_urb_sent_int (struct urb *urb, int urb_rc);
+
+
+//_________________________________________________________________________________________________
+
+/*
+ * Synchronization
+ *
+ *
+ * Notification bottom half
+ *   
+ *  This is a scheduled task that will send an interrupt notification. Because it
+ *  is called from the task scheduler context it needs to verify that the device is
+ *  still usable.
+ *
+ *      static int network_send_int_blan(struct usb_device_instance *, int )
+ *      static void notification_bh (void *)
+ *      void notification_schedule_bh (void)
+ *
+ *
+ * Netdevice functions
+ *
+ *   These are called by the Linux network layer. They must be protected by irq locks
+ *   if necessary to prevent interruption by IRQ level events.
+ *
+ *      int network_init (struct net_device *net_device)
+ *      void network_uninit (struct net_device *net_device)
+ *      int network_open (struct net_device *net_device)
+ *      int network_stop (struct net_device *net_device)
+ *      struct net_device_stats *network_get_stats (struct net_device *net_device)
+ *      int network_set_mac_addr (struct net_device *net_device, void *p)
+ *      void network_tx_timeout (struct net_device *net_device)
+ *      int network_set_config (struct net_device *net_device, struct ifmap *map)
+ *      int network_stop (struct net_device *net_device)
+ *      int network_hard_start_xmit (struct sk_buff *skb, struct net_device *net_device)
+ *      int network_do_ioctl (struct net_device *net_device, struct ifreq *rp, int cmd)
+ *
+ *
+ * Data bottom half functions
+ *
+ *  These are called from the bus bottom half handler. 
+ *
+ *      static int network_recv (struct usb_network_private *, struct net_device *, struct sk_buff *)
+ *      int network_recv_urb (struct urb *)
+ *      int network_urb_sent (struct urb *, int )
+ *      
+ *
+ * Hotplug bottom half:
+ *
+ *  This is a scheduled task that will send do a hotplug call. Because it is
+ *  called from the task scheduler context it needs to verify that the
+ *  device is still usable.
+ *
+ *      static int hotplug_attach (__u32 ip, __u32 mask, __u32 router, int attach)
+ *      static void hotplug_bh (void *data)
+ *      void hotplug_schedule_bh (void)
+ *
+ *
+ * Irq level functions:
+ *
+ *   These are called at interrupt time do process or respond to USB setup
+ *   commands. 
+ *
+ *      int network_recv_setup_irq (struct usb_device_request *)
+ *      void network_event_irq (struct usb_function_instance *function, usb_device_event_t event, int data)
+ *
+ *      
+ * Enable and disable functions:
+ *
+ *      void network_function_enable (struct usb_function_instance *, struct usb_function_instance *)
+ *      void network_function_disable (struct usb_function_instance *function)
+ *
+ *
+ * Driver initialization and exit:
+ *
+ *      static  int network_create (void)
+ *      static  void network_destroy (void)
+ *
+ *      int network_modinit (void)
+ *      void network_modexit (void)
+ */
+
+//_________________________________________________________________________________________________
+
+/*
+ * If the following are defined we implement the crc32_copy routine using
+ * Duff's device. This will unroll the copy loop by either 4 or 8. Do not
+ * use these without profiling to test if it actually helps on any specific
+ * device.
+ */
+#undef CONFIG_USBD_NETWORK_CRC_DUFF4
+#undef CONFIG_USBD_NETWORK_CRC_DUFF8
+
+static __u32 *network_crc32_table;
+
+#define CRC32_INIT   0xffffffff      // Initial FCS value
+#define CRC32_GOOD   0xdebb20e3      // Good final FCS value
+
+#define CRC32_POLY   0xedb88320      // Polynomial for table generation
+        
+#define COMPUTE_FCS(val, c) (((val) >> 8) ^ network_crc32_table[((val) ^ (c)) & 0xff])
+
+//_________________________________________________________________________________________________
+//                                      crc32_copy
+
+/**
+ * Generate the crc32 table
+ *
+ * return non-zero if malloc fails
+ */
+static int make_crc_table(void)
+{
+        u32 n;
+        RETURN_ZERO_IF(network_crc32_table);
+        RETURN_ENOMEM_IF(!(network_crc32_table = (u32 *)ckmalloc(256*4, GFP_KERNEL)));
+        for (n = 0; n < 256; n++) {
+                int k;
+                u32 c = n;
+                for (k = 0; k < 8; k++) {
+                        c =  (c & 1) ? (CRC32_POLY ^ (c >> 1)) : (c >> 1);
+                }
+                network_crc32_table[n] = c;
+        }
+        return 0;
+}
+
+#if !defined(CONFIG_USBD_NETWORK_CRC_DUFF4) && !defined(CONFIG_USBD_NETWORK_CRC_DUFF8)
+/**
+ * Copies a specified number of bytes, computing the 32-bit CRC FCS as it does so.
+ *
+ * dst   Pointer to the destination memory area.
+ * src   Pointer to the source memory area.
+ * len   Number of bytes to copy.
+ * val   Starting value for the CRC FCS.
+ *
+ * Returns      Final value of the CRC FCS.
+ *
+ * @sa crc32_pad
+ */
+static __u32 __inline__ crc32_copy (__u8 *dst, __u8 *src, int len, __u32 val)
+{
+        for (; len-- > 0; val = COMPUTE_FCS (val, *dst++ = *src++));
+        return val;
+}
+
+#else /* DUFFn */
+
+/**
+ * Copies a specified number of bytes, computing the 32-bit CRC FCS as it does so.
+ *
+ * dst   Pointer to the destination memory area.
+ * src   Pointer to the source memory area.
+ * len   Number of bytes to copy.
+ * val   Starting value for the CRC FCS.
+ *
+ * Returns      Final value of the CRC FCS.
+ *
+ * @sa crc32_pad
+ */
+static __u32 crc32_copy (__u8 *dst, __u8 *src, int len, __u32 val)
+{
+#if defined(CONFIG_USBD_NETWORK_CRC_DUFF8)
+        int n = (len + 7) / 8;
+        switch (len % 8) 
+#elif defined(CONFIG_USBD_NETWORK_CRC_DUFF4)
+                int n = (len + 3) / 4;
+        switch (len % 4) 
+#endif
+        {
+        case 0: do {
+                        val = COMPUTE_FCS (val, *dst++ = *src++);
+#if defined(CONFIG_USBD_NETWORK_CRC_DUFF8)
+                case 7:
+                        val = COMPUTE_FCS (val, *dst++ = *src++);
+                case 6:
+                        val = COMPUTE_FCS (val, *dst++ = *src++);
+                case 5:
+                        val = COMPUTE_FCS (val, *dst++ = *src++);
+                case 4:
+                        val = COMPUTE_FCS (val, *dst++ = *src++);
+#endif
+                case 3:
+                        val = COMPUTE_FCS (val, *dst++ = *src++);
+                case 2:
+                        val = COMPUTE_FCS (val, *dst++ = *src++);
+                case 1:
+                        val = COMPUTE_FCS (val, *dst++ = *src++);
+                } while (--n > 0);
+        }
+        return val;
+}
+#endif /* DUFFn */
+
+
+//_________________________________________________________________________________________________
+//                                      crc32_pad
+
+/* crc32_pad - pad and calculate crc32
+ *
+ * Returns - CRC FCS
+ */
+static __u32 __inline__ crc32_pad (__u8 *dst, int len, __u32 val)
+{
+        for (; len-- > 0; val = COMPUTE_FCS (val, *dst++ = '\0'));
+        return val;
+}
+
+//_________________________________________________________________________________________________
+//                                      network_send_int
+//
+
+/* network_send_int_blan - send an interrupt notification response
+ *
+ * Generates a response urb on the notification (INTERRUPT) endpoint.
+ * Return a non-zero result code to STALL the transaction.
+ *
+ * This is called from either a scheduled task or from the process context
+ * that calls network_open() or network_close().
+ *
+ */
+static void network_send_int_blan(struct usb_function_instance *function, int connected )
+{
+        struct urb *urb;
+        struct cdc_notification_descriptor *cdc;
+        int rc;
+        struct usb_network_private *network_private = &Usb_network_private;
+
+        //printk(KERN_INFO"%s: device: %p\n", __FUNCTION__, device);
+
+        /*
+         * This needs to lock out interrupts as network_event_irq can
+         * change the NETWORK_ATTACHED status
+         */
+        unsigned long flags;
+        local_irq_save(flags);
+        do {
+                BREAK_IF(!function);
+
+
+                BREAK_IF(network_private->network_type != network_blan);
+                BREAK_IF(!network_private->have_interrupt);
+
+                BREAK_IF(!(network_private->flags & NETWORK_ATTACHED));
+
+                //printk(KERN_INFO"%s: connected: %d network: %d %d\n", __FUNCTION__, connected, 
+                //                network_private->network_type, network_blan);
+
+                BREAK_IF(usbd_bus_status(function) != USBD_OK);
+
+                if (network_private->int_urb) {
+                        printk(KERN_INFO"%s: int_urb: %p\n", __FUNCTION__, network_private->int_urb);
+                        usbd_cancel_urb_irq(network_private->int_urb);
+                        network_private->int_urb = NULL;
+                }
+
+                BREAK_IF(!(urb = usbd_alloc_urb (function, INT_IN, 
+                                                sizeof(struct cdc_notification_descriptor), network_urb_sent_int)));
+                
+                urb->actual_length = sizeof(struct cdc_notification_descriptor);
+                memset(urb->buffer, 0, sizeof(struct cdc_notification_descriptor));
+
+                cdc = (struct cdc_notification_descriptor *)urb->buffer;
+
+                cdc->bmRequestType = 0xa1;
+                cdc->bNotification = 0x00;
+                cdc->wValue = connected ? 0x01 : 0x00;
+                cdc->wIndex = 0x01; // XXX interface - check that this is correct
+
+
+                network_private->int_urb = urb;
+                //printk(KERN_INFO"%s: int_urb: %p\n", __FUNCTION__, urb);
+                BREAK_IF (!(rc = usbd_send_urb (urb)));
+
+                printk(KERN_ERR"%s: usbd_send_urb failed err: %x\n", __FUNCTION__, rc);
+                urb->privdata = NULL;
+                network_private->int_urb = NULL;
+                usbd_dealloc_urb (urb);
+
+        } while(0);
+        local_irq_restore(flags);
+}
+
+
+/* notification_bh - Bottom half handler to send a notification status 
+ *
+ * Send a notification with open/close status
+ *
+ * It should not be possible for this to be called more than once at a time
+ * as it is only called via schedule_task() which protects against a second
+ * invocation.
+ */
+static void notification_bh (void *data)
+{
+        network_send_int_blan(Usb_network_private.function, Usb_network_private.flags & NETWORK_OPEN);
+        MOD_DEC_USE_COUNT;
+}
+
+/* notification_schedule_bh - schedule a call for notification_bh
+ */
+static void notification_schedule_bh (void)
+{
+        MOD_INC_USE_COUNT;
+        if (!schedule_task (&Usb_network_private.notification_bh)) 
+                MOD_DEC_USE_COUNT;
+}
+
+
+//______________________________________Network Layer Functions____________________________________
+
+/*
+ * In general because the functions are called from an independant layer it is necessary
+ * to verify that the device is still ok and to lock interrupts out to prevent in-advertant
+ * closures while in progress.
+ */
+
+/* network_init -
+ *
+ * Initializes the specified network device.
+ *
+ * Returns non-zero for failure.
+ */
+static int network_init (struct net_device *net_device)
+{
+        //printk(KERN_INFO"%s:\n", __FUNCTION__);
+	return 0;
+}
+
+
+/* network_uninit -
+ *
+ * Uninitializes the specified network device.
+ */
+static void network_uninit (struct net_device *net_device)
+{
+        //printk(KERN_INFO"%s:\n", __FUNCTION__);
+	return;
+}
+
+
+/* network_open -
+ *
+ * Opens the specified network device.
+ *
+ * Returns non-zero for failure.
+ */
+static int network_open (struct net_device *net_device)
+{
+        struct usb_network_private *network_private = &Usb_network_private;
+
+        // XXX should this be before or after the preceeding netif_running() test?
+	MOD_INC_USE_COUNT;
+        network_private->flags |= NETWORK_OPEN;
+	netif_wake_queue (net_device);
+        network_send_int_blan(network_private->function, 1);
+
+#ifdef CONFIG_USBD_NET_NFS_SUPPORT
+        if (!usb_is_configured) {
+                if (!in_interrupt()) {
+                        printk(KERN_ERR"Please replug USB cable and then ifconfig host interface.\n");
+                        interruptible_sleep_on(&usb_netif_wq);
+                }
+                else {
+                        printk(KERN_ERR"Wanring! In interrupt\n");
+                }
+        }
+#endif
+	return 0;
+}
+
+
+/* network_stop -
+ *
+ * Stops the specified network device.
+ *
+ * Returns non-zero for failure.
+ */
+static int network_stop (struct net_device *net_device)
+{
+        struct usb_network_private *network_private = &Usb_network_private;
+
+        //printk(KERN_INFO"%s:\n", __FUNCTION__);
+
+	//netif_stop_queue (net_device);
+
+        network_private->flags &= ~NETWORK_OPEN;
+        network_send_int_blan(network_private->function, 0);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+
+/* network_get_stats -
+ *
+ * Gets statistics from the specified network device.
+ *
+ * Returns pointer to net_device_stats structure with the required information.
+ */
+static struct net_device_stats *network_get_stats (struct net_device *net_device)
+{
+        struct usb_network_private *network_private = &Usb_network_private;
+	return &network_private->stats;
+}
+
+
+/* network_set_mac_addr
+ *
+ * Sets the MAC address of the specified network device. Fails if the device is in use.
+ *
+ * Returns non-zero for failure.
+ */
+static int network_set_mac_addr (struct net_device *net_device, void *p)
+{
+	struct sockaddr *addr = p;
+        unsigned long flags;
+
+        //printk(KERN_INFO"%s:\n", __FUNCTION__);
+
+	RETURN_EBUSY_IF(netif_running (net_device));
+        local_irq_save(flags);
+        memcpy (net_device->dev_addr, addr->sa_data, net_device->addr_len);
+        local_irq_restore(flags);
+	return 0;
+}
+
+
+/* network_tx_timeout -
+ *
+ * Tells the specified network device that its current transmit attempt has timed out.
+ */
+static void network_tx_timeout (struct net_device *net_device)
+{
+        struct usb_network_private *network_private = &Usb_network_private;
+#if 0
+        //printk(KERN_ERR"%s:\n", __FUNCTION__);
+        network_private->stats.tx_errors++;
+        network_private->stats.tx_dropped++;
+        usbd_cancel_urb_irq (network_private->bus, NULL);
+#endif
+#if 0
+        // XXX attempt to wakeup the host...
+        if ((network_private->network_type == network_blan) && (network_private->flags & NETWORK_OPEN)) {
+                notification_schedule_bh();
+        }
+#endif
+}
+
+
+/** network_set_config -
+ *
+ * Sets the specified network device's configuration. Fails if the device is in use.
+ *
+ * map           An ifmap structure containing configuration values.
+ *                      Those values which are non-zero/non-null update the corresponding fields
+ *                      in net_device.
+ *
+ * Returns non-zero for failure.
+ */
+static int network_set_config (struct net_device *net_device, struct ifmap *map)
+{
+	RETURN_EBUSY_IF(netif_running (net_device));
+	if (map->base_addr) 
+		net_device->base_addr = map->base_addr;
+	if (map->mem_start) 
+		net_device->mem_start = map->mem_start;
+	if (map->irq) 
+		net_device->irq = map->irq;
+	return 0;
+}
+
+
+/* network_change_mtu -
+ *
+ * Sets the specified network device's MTU. Fails if the new value is larger and
+ * the device is in use.
+ *
+ * Returns non-zero for failure.
+ */
+static int network_change_mtu (struct net_device *net_device, int mtu)
+{
+        struct usb_network_private *network_private = &Usb_network_private;
+
+        //printk(KERN_INFO"%s:\n", __FUNCTION__);
+
+	RETURN_EBUSY_IF(netif_running (net_device));
+	RETURN_EBUSY_IF(mtu > network_private->mtu);
+
+	net_device->mtu = mtu;
+	return 0;
+}
+
+//_________________________________________________________________________________________________
+//                                      network_hard_start_xmit
+
+/* network_hard_start_xmit - start sending an skb
+ *
+ * Starts a network device's transmit function.
+ * Called by kernel network layer to transmit a frame on this device.
+ * Grab locks and pass to @c netproto_do_xmit().
+ * The network layer flow control is managed to prevent more than 
+ * @c device->max_queue_entries from being outstanding.
+ *
+ * skb           Pointer to the sk_buff structure to be sent.
+ * net_device    Specifies the device by pointing to its net_device struct.
+ *
+ * Return non-zero (1) if busy.
+ */
+static int network_hard_start_xmit (struct sk_buff *skb, struct net_device *net_device)
+{
+        struct usb_network_private *network_private = &Usb_network_private;
+        struct usb_function_instance *function = network_private->function;
+	struct urb *urb = NULL;
+	int len = skb->len;
+	int rc = 1;
+        int in_pkt_sz;
+
+        down(&usbd_network_sem);
+        do {
+
+
+#if 0
+                printk(KERN_INFO"%s: %s len: %d encap: %d\n", __FUNCTION__, net_device->name, skb->len, network_private->encapsulation);
+                printk(KERN_INFO"start_xmit: len: %x head: %p data: %p tail: %p\n", 
+                                skb->len, skb->head, skb->data, skb->tail);
+                {
+                        __u8 *cp = skb->data;
+                        int i;
+                        for (i = 0; i < skb->len; i++) {
+                                if ((i%32) == 0) {
+                                        printk("\ntx[%2x] ", i);
+                                }
+                                printk("%02x ", *cp++);
+                        }
+                        printk("\n");
+                }
+#endif
+
+                THROW_IF(!(network_private->flags & NETWORK_ATTACHED), not_ok);
+                THROW_IF(!netif_carrier_ok (net_device), not_ok);
+                THROW_IF(usbd_bus_status(function) != USBD_OK, not_ok);
+
+#if defined(CONFIG_USBD_NETWORK_CDC)
+                // verify interface is enabled - non-zero altsetting means data is enabled
+                THROW_IF(!usbd_interface_AltSetting(function, DATA_INTF), not_ok);
+#endif
+                in_pkt_sz = usbd_endpoint_wMaxPacketSize(function, BULK_IN, usbd_high_speed(function));
+
+                // stop queue, it will be restart only when we are ready for another skb
+                netif_stop_queue (net_device);
+
+                // lock and update some stats
+                network_private->stopped++;
+                network_private->queued_entries++;
+                network_private->queued_bytes += skb->len;
+
+
+                // Set the timestamp for tx timeout
+                net_device->trans_start = jiffies;
+
+                switch (network_private->encapsulation) {
+                case simple_crc:
+                        //printk(KERN_INFO"%s: BASIC_CRC\n", __FUNCTION__);
+                        {
+                                u32 crc;
+
+                                // allocate urb 5 bytes larger than required
+                                if (!(urb = usbd_alloc_urb (function, BULK_IN, 
+                                                                skb->len + 5 + in_pkt_sz, network_urb_sent_bulk ))) 
+                                {
+                                        printk(KERN_ERR"%s: urb alloc failed len: %d endpoint: %02x\n", 
+                                                        __FUNCTION__, skb->len, 
+                                                        usbd_endpoint_bEndpointAddress(function, BULK_IN, 
+                                                                usbd_high_speed(function)));
+                                        return -ENOMEM;
+                                }
+
+                                // copy and crc skb->len bytes
+                                crc = crc32_copy(urb->buffer, skb->data, skb->len, CRC32_INIT);
+                                urb->actual_length = skb->len;
+
+                                // add a pad byte if required to ensure a short packet, usbdnet driver
+                                // will correctly handle pad byte before or after CRC, but the MCCI driver
+                                // wants it before the CRC.
+                                if ((urb->actual_length % in_pkt_sz) == (in_pkt_sz - 4)) {
+                                        crc = crc32_pad(urb->buffer + urb->actual_length, 1, crc);
+                                        urb->actual_length++;
+                                }
+
+                                // munge and append crc
+                                crc = ~crc;
+                                urb->buffer[urb->actual_length++] = crc & 0xff;
+                                urb->buffer[urb->actual_length++] = (crc >> 8) & 0xff;
+                                urb->buffer[urb->actual_length++] = (crc >> 16) & 0xff;
+                                urb->buffer[urb->actual_length++] = (crc >> 24) & 0xff;
+
+                                break;
+                        }
+                default:
+                        break;
+
+                }
+                if (!urb) {
+                        printk(KERN_ERR"%s: unknown encapsulation\n", __FUNCTION__);
+                        rc = -EINVAL;
+                        break;
+                }
+
+                // save skb for netproto_done
+                urb->privdata = (void *) skb;
+#if 0
+                printk(KERN_INFO"start_xmit: len: %d : %d data: %p\n", skb->len, urb->actual_length, urb->buffer);
+                {
+                        __u8 *cp = urb->buffer;
+                        int i;
+                        for (i = 0; i < urb->actual_length; i++) {
+                                if ((i%32) == 0) {
+                                        printk("\ntx[%2x] ", i);
+                                }
+                                printk("%02x ", *cp++);
+                        }
+                        printk("\n");
+                }
+#endif
+#if defined(CONFIG_USBD_NETWORK_BLAN_FERMAT)
+                if (network_private->fermat) {
+                        fermat_encode(urb->buffer, urb->actual_length);
+                }
+#endif
+
+                //printk(KERN_INFO"%s: urb: %p\n", __FUNCTION__, urb);
+                if ((rc = usbd_send_urb (urb))) {
+
+                        printk(KERN_ERR"%s: FAILED: %d\n", __FUNCTION__, rc);
+                        urb->privdata = NULL;
+                        usbd_dealloc_urb (urb);
+
+                        switch (rc) {
+
+                        case -EINVAL:
+                        case -EUNATCH:
+                                printk(KERN_ERR"%s: not attached, send failed: %d\n", __FUNCTION__, rc);
+                                network_private->stats.tx_errors++;
+                                network_private->stats.tx_carrier_errors++;
+                                netif_wake_queue (net_device);
+                                break;
+
+                        case -ENOMEM:
+                                printk(KERN_ERR"%s: no mem, send failed: %d\n", __FUNCTION__, rc);
+                                network_private->stats.tx_errors++;
+                                network_private->stats.tx_fifo_errors++;
+                                netif_wake_queue (net_device);
+                                break;
+
+                        case -ECOMM:
+                                printk(KERN_ERR"%s: comm failure, send failed: %d %p\n", __FUNCTION__, rc, net_device);
+                                network_private->stats.tx_dropped++;
+                                break;
+
+                        }
+                        dev_kfree_skb_any (skb);
+                        //rc = NET_XMIT_DROP; XXX this is what we should do, blows up on some 2.4.20 kernels
+                        rc = 0;
+                        break;
+                }
+
+                // XXX should we restart network queue
+                //printk(KERN_INFO"%s: OK: %d\n", __FUNCTION__, rc);
+                network_private->stats.tx_packets++;
+                network_private->stats.tx_bytes += len;
+
+                if ((network_private->queued_entries < network_private->max_queue_entries) && 
+                                (network_private->queued_bytes < network_private->max_queue_bytes)) 
+                        netif_wake_queue (net_device);
+                
+                CATCH(not_ok) {
+                        dev_kfree_skb_any (skb);
+                        network_private->stats.tx_dropped++;
+                        //netif_stop_queue(net_device); // XXX
+                        //rc = NET_XMIT_DROP; XXX this is what we should do, blows up on some 2.4.20 kernels
+                        rc = 0;
+                        break;
+                }
+
+        } while (0);
+        up(&usbd_network_sem);
+        return rc;
+}
+
+
+/* network_do_ioctl - perform an ioctl call 
+ *
+ * Carries out IOCTL commands for the specified network device.
+ *
+ * rp            Points to an ifreq structure containing the IOCTL parameter(s).
+ * cmd           The IOCTL command.
+ *
+ * Returns non-zero for failure.
+ */
+static int network_do_ioctl (struct net_device *net_device, struct ifreq *rp, int cmd)
+{
+	return -ENOIOCTLCMD;
+}
+
+//_________________________________________________________________________________________________
+
+struct net_device Network_net_device = {
+        get_stats: network_get_stats,
+        tx_timeout: network_tx_timeout,
+        do_ioctl: network_do_ioctl,
+        set_config: network_set_config,
+        set_mac_address: network_set_mac_addr,
+        hard_start_xmit: network_hard_start_xmit,
+        change_mtu: network_change_mtu,
+        init: network_init,
+        uninit: network_uninit,
+        open: network_open,
+        stop: network_stop,
+        priv: &Usb_network_private,
+};
+
+//_________________________________________________________________________________________________
+
+
+/* network_recv - function to process an received data URB
+ *
+ * Passes received data to the network layer. Passes skb to network layer.
+ *
+ * Returns non-zero for failure.
+ */
+static __inline__ int network_recv (struct usb_network_private *network_private, 
+                struct net_device *net_device, struct sk_buff *skb)
+{
+	int rc;
+#if 0
+        printk(KERN_INFO"%s: len: %x head: %p data: %p tail: %p\n", __FUNCTION__, 
+                        skb->len, skb->head, skb->data, skb->tail);
+        {
+                __u8 *cp = skb->data;
+                int i;
+                for (i = 0; i < skb->len; i++) {
+                        if ((i%32) == 0) {
+                                printk("\nrx[%2x] ", i);
+                        }
+                        printk("%02x ", *cp++);
+                }
+                printk("\n");
+        }
+#endif
+
+        // refuse if no device present
+        if (!netif_device_present (net_device)) {
+                printk(KERN_INFO"%s: device not present\n", __FUNCTION__);
+                return -EINVAL;
+        }
+
+        // refuse if no carrier
+        if (!netif_carrier_ok (net_device)) {
+                printk(KERN_INFO"%s: no carrier\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	// refuse if the net device is down
+	if (!(net_device->flags & IFF_UP)) {
+		//printk(KERN_INFO"%s: not up net_dev->flags: %x\n", __FUNCTION__, net_device->flags);
+		network_private->stats.rx_dropped++;
+		return -EINVAL;
+	}
+
+	skb->dev = net_device;
+	skb->pkt_type = PACKET_HOST;
+	skb->protocol = eth_type_trans (skb, net_device);
+	skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+        //printk(KERN_INFO"%s: len: %x head: %p data: %p tail: %p\n", __FUNCTION__, 
+        //                skb->len, skb->head, skb->data, skb->tail);
+
+
+	// pass it up to kernel networking layer
+	if ((rc = netif_rx (skb))) {
+		//printk(KERN_INFO"%s: netif_rx rc: %d\n", __FUNCTION__, rc);
+	}
+        network_private->stats.rx_bytes += skb->len;
+        network_private->stats.rx_packets++;
+
+	return 0;
+}
+
+//_________________________________________________________________________________________________
+
+/* network_recv_urb - callback to process a received URB
+ *
+ * Returns non-zero for failure.
+ */
+static int network_recv_urb (struct urb *urb, int rc)
+{
+        struct net_device *net_device;
+        struct usb_network_private *network_private = &Usb_network_private;
+	struct usb_function_instance *function = network_private->function;
+
+	struct sk_buff *skb = NULL;
+        int out_pkt_sz;
+
+        RETURN_EINVAL_IF(!(network_private->flags & NETWORK_ATTACHED));
+        RETURN_EINVAL_IF(!(net_device = &Network_net_device));
+
+        out_pkt_sz = usbd_endpoint_wMaxPacketSize(function, BULK_OUT, usbd_high_speed(function));
+
+#if 0
+        printk(KERN_INFO"%s: urb: %p len: %d maxtransfer: %d encap: %d\n", __FUNCTION__, 
+                        urb, urb->actual_length, network_private->maxtransfer, network_private->encapsulation);
+
+        {
+                __u8 *cp = urb->buffer;
+                int i;
+                for (i = 0; i < urb->actual_length; i++) {
+                        if ((i%32) == 0) {
+                                printk("\n[%2x] ", i);
+                        }
+                        printk("%02x ", *cp++);
+                }
+                printk("\n");
+        }
+#endif
+
+        THROW_IF(urb->status != RECV_OK, error);
+
+        // Is CDC active (we have received CONTROL WRITE setup packets indicating real CDC host)
+        switch (network_private->encapsulation) {
+                int len;
+        case simple_crc:
+
+                len = urb->actual_length;
+
+                // allocate skb of appropriate length, reserve 2 to align ip
+                THROW_IF(!(skb = dev_alloc_skb (len + 2)), error);
+                skb_reserve(skb, 2);
+
+#if defined(CONFIG_USBD_NETWORK_BLAN_PADAFTER)
+                {
+                        /* This version simply checks for a correct CRC along the 
+                         * entire packet. Some UDC's have trouble with some packet
+                         * sizes, this allows us to add pad bytes after the CRC.
+                         */
+
+                        u8 *dst = skb_put(skb, len - 1);
+                        u8 *src = urb->buffer;
+                        int copied;
+                        u32 crc;
+
+                        // XXX this should work, but the MIPS optimizer seems to get it wrong....
+                        //copied = (len < out_pkt_sz) ? 0 : ((len / out_pkt_sz) - 1) * out_pkt_sz;
+                        
+                        if (len < out_pkt_sz*2) 
+                                copied = 0;
+                        else {
+                                int pkts = ((len - out_pkt_sz) / out_pkt_sz);
+                                copied = (pkts - 1) * out_pkt_sz;
+                        }
+
+                        len -= copied;
+                        crc = CRC32_INIT;
+                        for (; copied-- > 0 ; crc = COMPUTE_FCS (crc, *dst++ = *src++));
+
+                        for (; (len-- > 0) && (CRC32_GOOD != crc); crc = COMPUTE_FCS (crc, *dst++ = *src++));
+
+                        skb_trim(skb, skb->len - len - 4);
+
+                        if (CRC32_GOOD != crc) {
+                                //printk(KERN_INFO"%s: AAA frame: %03x\n", __FUNCTION__, urb->framenum);
+                                THROW_IF(network_private->crc, crc_error);
+                        }
+                        else 
+                                network_private->crc = 1;
+                }
+#else
+                /* 
+                 * The CRC can be sent in two ways when the size of the transfer 
+                 * ends up being a multiple of the packetsize:
+                 *
+                 *                                           |
+                 *                <data> <CRC><CRC><CRC><CRC>|<???>     case 1
+                 *                <data> <NUL><CRC><CRC><CRC>|<CRC>     case 2
+                 *           <data> <NUL><CRC><CRC><CRC><CRC>|          case 3
+                 *     <data> <NUL><CRC><CRC><CRC>|<CRC>     |          case 4
+                 *                                           |
+                 *        
+                 * This complicates CRC checking, there are four scenarios:
+                 *
+                 *      1. length is 1 more than multiple of packetsize with a trailing byte
+                 *      2. length is 1 more than multiple of packetsize 
+                 *      3. length is multiple of packetsize
+                 *      4. none of the above
+                 *
+                 * Finally, even though we always compute CRC, we do not actually throw
+                 * things away until and unless we have previously seen a good CRC.
+                 * This allows backwards compatibility with hosts that do not support
+                 * adding a CRC to the frame.
+                 *
+                 */
+
+                // test if 1 more than packetsize multiple
+                if (1 == (len % out_pkt_sz)) {
+
+                        // copy and CRC up to the packetsize boundary
+                        u32 crc = crc32_copy(skb_put(skb, len - 1), urb->buffer, len - 1, CRC32_INIT);
+
+                        // if the CRC is good then this is case 1
+                        if (CRC32_GOOD != crc) {
+
+                                crc = crc32_copy(skb_put(skb, 1), urb->buffer + len - 1, 1, crc);
+
+                                if (CRC32_GOOD != crc) {
+                                        //crc_errors[len%64]++;
+                                        printk(KERN_INFO"%s: A CRC error %08x %03x\n", __FUNCTION__, crc, urb->framenum);
+                                        THROW_IF(network_private->crc, crc_error);
+                                }
+                                else 
+                                        network_private->crc = 1;
+                        }
+                        else 
+                                network_private->crc = 1;
+                }
+                else {
+                        u32 crc = crc32_copy(skb_put(skb, len), urb->buffer, len, CRC32_INIT);
+
+                        if (CRC32_GOOD != crc) {
+                                //crc_errors[len%64]++;
+                                //printk(KERN_INFO"%s: CCC\n", __FUNCTION__);
+                                THROW_IF(network_private->crc, crc_error);
+                        }
+                        else 
+                                network_private->crc = 1;
+                }
+                // trim IFF we are paying attention to crc
+                if (network_private->crc) 
+                        skb_trim(skb, skb->len - 4);
+#endif
+
+                // pass it up, free skb if non zero
+                THROW_IF(network_recv (network_private, net_device, skb), skb_error);
+                
+                break;
+        default:
+                break;
+        }
+
+        // catch a simple error, just increment missed error and general error
+        CATCH(error) {
+
+                network_private->stats.rx_frame_errors++;
+                network_private->stats.rx_errors++;
+
+                // catch error where skb may need to be released
+                CATCH(skb_error) {
+
+                        // catch a CRC error
+                        
+                        // XXX We need to track whether we have seen a correct CRC, until then 
+                        // we ignore CRC errors.
+
+                        CATCH(crc_error) {
+#if 0
+                                printk(KERN_INFO"%s: urb: %p status: %d len: %d maxtransfer: %d encap: %d\n", __FUNCTION__, 
+                                                urb, urb->status, urb->actual_length, network_private->maxtransfer, 
+                                                network_private->encapsulation);
+
+                                {
+                                        __u8 *cp = urb->buffer;
+                                        int i;
+                                        for (i = 0; i < urb->actual_length; i++) {
+                                                if ((i%32) == 0) {
+                                                        printk("\n[%2x] ", i);
+                                                }
+                                                printk("%02x ", *cp++);
+                                        }
+                                        printk("\n");
+                                }
+#endif
+                                network_private->stats.rx_crc_errors++;
+                                network_private->stats.rx_errors++;
+                        }
+
+                        // catch an overrun error
+                        // (Only used if CONFIG_USBD_NETWORK_CDC is defined.)
+                        //CATCH(fifo_error) {
+                        //        network_private->stats.rx_fifo_errors++;
+                        //        network_private->stats.rx_errors++;
+                        //}
+
+                        // if skb defined free it
+                        if (skb) 
+                                dev_kfree_skb_any (skb);
+                }
+                network_private->stats.rx_dropped++;
+                //return -EINVAL;
+        }
+        //printk(KERN_INFO"%s: restart: %p\n", __FUNCTION__, urb);
+        return (usbd_start_recv (urb));
+}
+
+//_________________________________________________________________________________________________
+//                                      network_urb_sent
+
+/* network_urb_sent_bulk - callback function to process a sent URB
+ *
+ * Handles notification that an urb has been sent (successfully or otherwise).
+ *
+ * urb   Pointer to the urb that has been sent.
+ * rc    Result code from the send operation.
+ *
+ * Returns non-zero for failure.
+ */
+static int network_urb_sent_bulk (struct urb *urb, int urb_rc)
+{
+        struct sk_buff *skb;
+        struct net_device *net_device;
+        unsigned long flags;
+        struct usb_network_private *network_private = &Usb_network_private;
+        struct usb_function_instance *function = network_private->function;
+        int rc = -EINVAL;
+
+        //printk(KERN_INFO"%s: urb: %p device: %p address: %x urb_rc: %d\n", __FUNCTION__,
+        //                urb, urb->device, urb->endpoint->bEndpointAddress, urb_rc);
+
+        local_irq_save(flags);
+        do {
+
+                BREAK_IF(!urb);
+                BREAK_IF(!(function = urb->function_instance));
+
+                switch (urb_rc) {
+                case SEND_FINISHED_ERROR:
+                        network_private->stats.tx_errors++;
+                        network_private->stats.tx_dropped++;
+                        break;
+                case SEND_FINISHED_CANCELLED:
+                        network_private->stats.tx_errors++;
+                        network_private->stats.tx_carrier_errors++;
+                        break;
+                default:
+                        break;
+                }
+
+                // XXX should we zap skb first if error?
+                RETURN_EINVAL_IF(!(network_private->flags & NETWORK_CREATED));
+
+                // retrieve skb pointer and unlink from urb pointers
+                skb = (struct sk_buff *) urb->privdata;
+
+                urb->privdata = NULL;
+                usbd_dealloc_urb (urb);
+
+                // tell netproto we are done with the skb, it will test for NULL
+                // netproto_done (interface, skb, urb_rc != SEND_FINISHED_OK);
+
+                BREAK_IF(!skb);
+                BREAK_IF(!(net_device = &Network_net_device));
+
+
+                network_private->avg_queue_entries += network_private->queued_entries;
+                network_private->queued_entries--;
+                network_private->samples++;
+                network_private->jiffies += jiffies - *(time_t *) (&skb->cb);
+                network_private->queued_bytes -= skb->len;
+
+                dev_kfree_skb_any (skb);
+
+                if (netif_queue_stopped (net_device)) {
+                        netif_wake_queue (net_device);
+                        network_private->restarts++;
+                }
+                rc = 0;
+
+        } while (0);
+        local_irq_restore(flags);
+        return rc;
+}
+
+/* network_urb_sent_int - callback for sent URB
+ *
+ * Handles notification that an urb has been sent (successfully or otherwise).
+ *
+ * Returns non-zero for failure.
+ */
+static int network_urb_sent_int (struct urb *urb, int urb_rc)
+{
+        struct usb_function_instance *function;
+        struct sk_buff *skb;
+        struct net_device *net_device;
+        unsigned long flags;
+        int rc = -EINVAL;
+        struct usb_network_private *network_private = &Usb_network_private;
+
+        //printk(KERN_INFO"%s: urb: %p device: %p address: %x urb_rc: %d\n", __FUNCTION__,
+        //                urb, urb->device, urb->endpoint->bEndpointAddress, urb_rc);
+
+        local_irq_save(flags);
+        do {
+
+                BREAK_IF(!urb);
+                BREAK_IF(!(function = urb->function_instance));
+                //RETURN_EINVAL_IF(!(network_private->flags & NETWORK_ATTACHED));
+
+                //printk(KERN_INFO"%s: reseting int_urb: %p\n", __FUNCTION__, network_private->int_urb);
+                usbd_dealloc_urb (urb);
+                network_private->int_urb = NULL;
+                rc = 0;
+
+        } while (0);
+        local_irq_restore(flags);
+        return rc;
+}
+
+
+//_________________________________________________________________________________________________
+//                                      network_recv_setup_irq
+//
+/* network_urb_received_ep0 - callback for sent URB
+ *
+ * Handles notification that an urb has been sent (successfully or otherwise).
+ *
+ * Returns non-zero for failure.
+ */
+static int network_urb_received_ep0 (struct urb *urb, int urb_rc)
+{
+        printk(KERN_INFO"%s: urb: %p status: %d\n", __FUNCTION__, urb, urb->status);
+
+        RETURN_EINVAL_IF (RECV_OK != urb->status);
+
+        printk(KERN_INFO"%s: %s\n", __FUNCTION__, urb->buffer);
+
+        return -EINVAL;         // caller will de-allocate
+}
+
+/* network_recv_setup_irq - process a received SETUP URB
+ *
+ * Processes a received setup packet and CONTROL WRITE data.
+ * Results for a CONTROL READ are placed in urb->buffer.
+ *
+ * Returns non-zero for failure.
+ */
+static int network_recv_setup_irq (struct usb_device_request *request)
+{
+        struct usb_network_private *network_private = &Usb_network_private;
+        struct usb_function_instance *function = network_private->function;
+        struct urb *urb;
+
+
+        // Verify that this is a USB Class request per CDC specification or a vendor request.
+        RETURN_ZERO_IF (!(request->bmRequestType & (USB_REQ_TYPE_CLASS | USB_REQ_TYPE_VENDOR)));
+
+        // Determine the request direction and process accordingly
+        switch (request->bmRequestType & (USB_REQ_DIRECTION_MASK | USB_REQ_TYPE_MASK)) {
+
+        case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_VENDOR:
+
+                switch (request->bRequest) {
+                case MCCI_ENABLE_CRC:
+                        if (make_crc_table()) 
+                                return -EINVAL;
+                        network_private->encapsulation = simple_crc;
+                        return 0;
+
+                case BELCARRA_PING:
+                        //printk(KERN_INFO"%s: H2D VENDOR IP: %08x\n", __FUNCTION__, ip_addr);
+                        if ((network_private->network_type == network_blan)) 
+                                notification_schedule_bh();
+                        break;
+
+#if !defined(CONFIG_USBD_NETWORK_BLAN_DO_NOT_SETTIME) || !defined(CONFIG_USBD_NETWORK_SAFE_DO_NOT_SETTIME) 
+                case BELCARRA_SETTIME:
+                        {
+                                struct timeval tv;
+
+                                // wIndex and wLength contain RFC868 time - seconds since midnight 1 jan 1900
+
+                                tv.tv_sec = ntohl( request->wValue << 16 | request->wIndex);
+                                tv.tv_usec = 0;
+
+                                // convert to Unix time - seconds since midnight 1 jan 1970
+                                
+                                tv.tv_sec -= RFC868_OFFSET_TO_EPOCH; 
+
+                                //printk(KERN_INFO"%s: H2D VENDOR TIME: %08x\n", __FUNCTION__, tv.tv_sec);
+
+                                // set the time
+                                do_settimeofday(&tv);
+                        } break;
+#endif
+                case BELCARRA_SETIP:
+                        ip_addr = ntohl( request->wValue << 16 | request->wIndex);
+                        break;
+
+                case BELCARRA_SETMSK:
+                        network_mask = ntohl( request->wValue << 16 | request->wIndex);
+                        break;
+
+                case BELCARRA_SETROUTER:
+                        router_ip = ntohl( request->wValue << 16 | request->wIndex);
+                        break;
+
+                case BELCARRA_SETDNS:
+                        dns_server_ip = ntohl( request->wValue << 16 | request->wIndex);
+                        break;
+#ifdef CONFIG_USBD_NETWORK_BLAN_FERMAT
+                case BELCARRA_SETFERMAT:
+                        network_private->fermat = 1;
+                        break;
+#endif
+#ifdef CONFIG_USBD_NETWORK_BLAN_HOSTNAME
+                case BELCARRA_HOSTNAME:
+                        //printk(KERN_INFO"%s: HOSTNAME\n", __FUNCTION__);
+                        RETURN_EINVAL_IF(!(urb = usbd_alloc_urb_ep0(function, le16_to_cpu(request->wLength), 
+                                                        network_urb_received_ep0) ));
+                        RETURN_ZERO_IF(!usbd_start_recv(urb));                  // return if no error
+                        usbd_dealloc_urb(urb);                                  // de-alloc if error
+                        return -EINVAL;
+#endif
+                }
+                return 0;
+#if 0
+        case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_VENDOR:
+                urb->actual_length = 0;
+                switch (request->bRequest) {
+                case BELCARRA_GETMAC:
+                        {
+                                // copy and free the original buffer
+                                memcpy(urb->buffer, Network_net_device.dev_addr, ETH_ALEN);
+                                urb->actual_length = ETH_ALEN;
+                                return 0;
+                        }
+                }
+#endif
+                return 0;
+        default:
+                break;
+        }
+        return -EINVAL;
+}
+
+//______________________________________ Hotplug Functions ________________________________________
+
+#ifdef CONFIG_HOTPLUG
+
+#define AGENT "network_fd"
+
+/* hotplug_attach - call hotplug 
+ */
+static int hotplug_attach (__u32 ip, __u32 mask, __u32 router, int attach)
+{
+        static int count = 0;
+        char *argv[3];
+        char *envp[10];
+        char ifname[20+12 + IFNAMSIZ];
+        int i;
+        char count_str[20];
+
+        RETURN_EINVAL_IF(!hotplug_path[0]);
+
+        argv[0] = hotplug_path;
+        argv[1] = AGENT;
+        argv[2] = 0;
+
+        sprintf (ifname, "INTERFACE=%s", Network_net_device.name);
+        sprintf (count_str, "COUNT=%d", count++);
+
+        i = 0;
+        envp[i++] = "HOME=/";
+        envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+        envp[i++] = ifname;
+
+
+        if (attach) {
+                unsigned char *cp;
+                char ip_str[20+32];
+                char mask_str[20+32];
+                char router_str[20+32];
+                __u32 nh;
+
+                nh = htonl(ip);
+                cp = (unsigned char*) &nh;
+                sprintf (ip_str, "IP=%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]);
+
+                nh = htonl(mask);
+                cp = (unsigned char*) &nh;
+                sprintf (mask_str, "MASK=%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]);
+
+                nh = htonl(router);
+                cp = (unsigned char*) &nh;
+                sprintf (router_str, "ROUTER=%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]);
+
+                //printk (KERN_INFO "%s: attach %s %s %s\n", __FUNCTION__, ifname, ip_str, count_str);
+
+                envp[i++] = "ACTION=attach";
+                envp[i++] = ip_str;
+                envp[i++] = mask_str;
+                envp[i++] = router_str;
+        }
+        else {
+                //printk (KERN_INFO "%s: detach %s %s\n", __FUNCTION__, ifname, count_str);
+                envp[i++] = "ACTION=detach";
+        }
+
+        envp[i++] = count_str;
+        envp[i++] = 0;
+
+        return call_usermodehelper (argv[0], argv, envp);
+}
+
+
+/* hotplug_bh - bottom half handler to call hotplug script to signal ATTACH or DETACH
+ *
+ * Check connected status and load/unload as appropriate.
+ *
+ * It should not be possible for this to be called more than once at a time
+ * as it is only called via schedule_task() which protects against a second
+ * invocation.
+ */
+static void hotplug_bh (void *data)
+{
+        struct usb_network_private *network_private = &Usb_network_private;
+        struct usb_function_instance *function = network_private->function;
+
+        //printk(KERN_INFO"%s: BUS state: %d status: %d\n", __FUNCTION__, usbd_device_state(function), usbd_bus_status(function));
+
+        if (function && (USBD_OK == usbd_bus_status(function)) && (STATE_CONFIGURED == usbd_device_state(function))) {
+                if (hotplug_attached != network_private->hotplug_status) {
+                        //printk(KERN_INFO"%s: ATTACH\n", __FUNCTION__);
+                        network_private->hotplug_status = hotplug_attached;
+                        hotplug_attach (ip_addr, network_mask, router_ip, 1);
+                }
+        }
+        else {
+                if (hotplug_detached != network_private->hotplug_status) {
+                        //printk(KERN_INFO"%s: DETACH\n", __FUNCTION__);
+                        network_private->hotplug_status = hotplug_detached;
+                        hotplug_attach (ip_addr, network_mask, router_ip, 0);
+                }
+        }
+        MOD_DEC_USE_COUNT;
+}
+
+/* hotplug_schedule_bh - schedule a call to hotplug bottom half
+ */
+static void hotplug_schedule_bh (void)
+{
+        MOD_INC_USE_COUNT;
+        if (!schedule_task (&Usb_network_private.hotplug_bh)) 
+                MOD_DEC_USE_COUNT;
+}
+#endif				/* CONFIG_HOTPLUG */
+
+
+//_________________________________________________________________________________________________
+
+#ifdef CONFIG_USBD_NETWORK_START_SINGLE
+#define NETWORK_START_URBS 1
+#else
+#define NETWORK_START_URBS 2
+#endif
+
+/* network_start_recv - start recv urb(s)
+ */
+void network_start_recv(struct usb_function_instance *function)
+{
+        int i;
+        for (i = 0; i < NETWORK_START_URBS; i++) {
+                struct urb *urb;
+                BREAK_IF(!(urb = usbd_alloc_urb (function, BULK_OUT, 
+                                                usbd_endpoint_transferSize(function, BULK_OUT, usbd_high_speed(function)), 
+                                                network_recv_urb)));
+                //printk(KERN_INFO"%s: i: %d start: %p\n", __FUNCTION__, i, urb);
+                if (usbd_start_recv(urb)) 
+                        usbd_dealloc_urb(urb);
+        }
+}
+
+/* network_event_irg - Processes a USB event.
+ */
+static void network_event_irq (struct usb_function_instance *function, usb_device_event_t event, int data)
+{
+        struct usb_network_private *network_private = &Usb_network_private;
+
+        switch (event) {
+
+        case DEVICE_RESET:
+        case DEVICE_DESTROY:	
+        case DEVICE_BUS_INACTIVE:
+                //printk(KERN_INFO"%s: RST %x\n", __FUNCTION__, ip_addr);
+                {
+                        //printk(KERN_INFO"%s:\n", __FUNCTION__);
+                        // Return if argument is null.
+
+                        // XXX flush
+
+                        network_private->flags &= ~NETWORK_ATTACHED;
+                        network_private->int_urb = NULL;
+
+                        // Disable our net-device.
+                        // Apparently it doesn't matter if we should do this more than once.
+
+                        netif_stop_queue(&Network_net_device);
+                        netif_carrier_off(&Network_net_device);
+
+                        // If we aren't already tearing things down, do it now.
+                        if (!(network_private->flags & NETWORK_DESTROYING)) {
+                                network_private->flags |= NETWORK_DESTROYING;
+                                //network_private->device = NULL;
+                        }
+                }
+                break;
+
+        case DEVICE_CONFIGURED:
+        case DEVICE_BUS_ACTIVITY:
+                //printk(KERN_INFO"%s: CFG %x\n", __FUNCTION__, ip_addr);
+                network_private->flags |= NETWORK_ATTACHED;
+
+                if ((network_private->network_type == network_blan) && (network_private->flags & NETWORK_OPEN)) 
+                        notification_schedule_bh();
+                
+                netif_carrier_on (&Network_net_device);
+                netif_wake_queue (&Network_net_device);
+
+                network_start_recv(function);
+
+#ifdef CONFIG_USBD_NET_NFS_SUPPORT
+                if (!usb_is_configured) {
+                        wake_up(&usb_netif_wq);
+                        usb_is_configured = 1;
+                }
+#endif
+                break;
+
+	case DEVICE_SET_INTERFACE:
+                // XXX if CDC then we can check device->alternates[1] and see if we should
+                // enable/disable data flow.
+                // XXX verify ep0.c SET_CONFIGURATION and SET_INTERFACE implmentation are
+                // complete before using this
+		break;
+
+        default:
+                return;
+        }
+#ifdef CONFIG_HOTPLUG
+        hotplug_schedule_bh();
+#endif
+}
+
+//_________________________________________________________________________________________________
+
+/* network_function_enable - enable the function driver
+ *
+ * Called for usbd_function_enable() from usbd_register_device()
+ */
+
+static int network_function_enable (struct usb_function_instance *function)
+{
+        //printk(KERN_INFO"%s: DOWN\n", __FUNCTION__);
+        down(&usbd_network_sem);
+	MOD_INC_USE_COUNT;  // QQQ Should this be _before_ the down()?
+
+        // set the network device address from the local device address
+        memcpy(Network_net_device.dev_addr, local_dev_addr, ETH_ALEN);
+
+        //Usb_network_private.bus = function->bus;
+        Usb_network_private.function = function;
+        Usb_network_private.have_interrupt = usbd_endpoint_bEndpointAddress(function, INT_IN, usbd_high_speed(function)) ? 1 : 0;
+
+        Usb_network_private.flags |= NETWORK_ENABLED;
+
+#if defined(CONFIG_USBD_NETWORK_CDC)
+        cdc_init(function);
+#endif                          /* CONFIG_USBD_NETWORK_CDC */
+
+#ifdef CONFIG_USBD_NETWORK_BASIC
+        basic_init(function);
+#endif
+
+#ifdef CONFIG_USBD_NETWORK_BASIC2
+        basic2_init(function);
+#endif
+
+#ifdef CONFIG_USBD_NETWORK_SAFE
+        safe_init(function);
+#endif
+#ifdef CONFIG_USBD_NETWORK_BLAN
+        blan_init(function);
+#endif
+        up(&usbd_network_sem);
+        //printk(KERN_INFO"%s: UP\n", __FUNCTION__);
+        return 0;
+}
+
+/* network_functino_disable - disable the function driver
+ *
+ */
+static void network_function_disable (struct usb_function_instance *function)
+{
+        //printk(KERN_INFO"%s: DOWN\n", __FUNCTION__);
+        down(&usbd_network_sem);
+        Usb_network_private.flags &= ~NETWORK_ENABLED;
+        //Usb_network_private.bus = NULL;
+        Usb_network_private.function = NULL;
+	MOD_DEC_USE_COUNT;  // QQQ Should this be _after_ the up()?
+        up(&usbd_network_sem);
+        //printk(KERN_INFO"%s: UP\n", __FUNCTION__);
+}
+
+struct usb_function_operations network_fd_function_ops = {
+	recv_setup_irq:    network_recv_setup_irq,
+	event_irq:     network_event_irq,
+	function_enable: network_function_enable,
+	function_disable: network_function_disable,
+};
+
+//_________________________________________________________________________________________________
+
+/* network_create - create and initialize network private structure
+ *
+ * Returns non-zero for failure.
+ */
+static int network_create (void)
+{
+	struct usb_network_private *network_private = &Usb_network_private;
+
+        //printk(KERN_INFO"%s:\n", __FUNCTION__);
+
+	// Set some fields to generic defaults and register the network device with the kernel networking code
+
+	memset(&network_private->stats, 0, sizeof network_private->stats);
+
+        ether_setup (&Network_net_device);
+        RETURN_EINVAL_IF (register_netdev(&Network_net_device));
+
+        netif_stop_queue (&Network_net_device);
+        netif_carrier_off (&Network_net_device);
+        Network_net_device.flags &= ~IFF_UP;
+
+        network_private->flags |= NETWORK_CREATED;
+
+        network_private->maxtransfer = MAXFRAMESIZE + 4 + 64;
+
+        network_private->flags |= NETWORK_REGISTERED;
+
+        network_private->network_type = network_unknown;
+
+        //printk(KERN_INFO"%s: finis\n", __FUNCTION__);
+        return 0;
+}
+
+/* network_destroy - destroy network private struture
+ *
+ * Destroys the network interface referenced by the global variable @c network_private.
+ */
+static void network_destroy (void)
+{
+        if (Usb_network_private.flags & NETWORK_REGISTERED) {
+                netif_stop_queue (&Network_net_device);
+                netif_carrier_off (&Network_net_device);
+                unregister_netdev (&Network_net_device);
+        }
+        Usb_network_private.flags = 0;
+}
+
+
+//______________________________________module_init and module_exit________________________________
+
+/* hexdigit -
+ *
+ * Converts characters in [0-9A-F] to 0..15, characters in [a-f] to 42..47, and all others to 0.
+ */
+static __u8 hexdigit (char c)
+{
+	return isxdigit (c) ? (isdigit (c) ? (c - '0') : (c - 'A' + 10)) : 0;
+}
+
+/* set_address -
+ */
+void set_address(char *mac_address_str, __u8 *dev_addr)
+{
+        int i;
+        if (mac_address_str && strlen(mac_address_str)) {
+                for (i = 0; i < ETH_ALEN; i++) {
+                        dev_addr[i] = 
+                                hexdigit (mac_address_str[i * 2]) << 4 | 
+                                hexdigit (mac_address_str[i * 2 + 1]);
+                }
+        }
+        else {
+                get_random_bytes(dev_addr, ETH_ALEN);
+                dev_addr[0] = (dev_addr[0] & 0xfe) | 0x02;
+        }
+}
+
+/* macstrtest -
+ */
+int macstrtest(char *mac_address_str)
+{
+        int l = 0;
+
+        if (mac_address_str) {
+                l = strlen(mac_address_str);
+        }
+        return ((l != 0) && (l != 12));
+}
+
+/* network_modinit - driver intialization
+ *
+ * Returns non-zero for failure.
+ */
+static int network_modinit (void)
+{
+        network_type_t  network_type = network_unknown;
+
+        init_waitqueue_head(&usb_netif_wq);
+
+
+        printk(KERN_INFO "Copyright (c) 2002-2004 Belcarra Technologies; www.belcarra.com; sl@belcarra.com\n");
+        printk(KERN_INFO "%s: %s vendor_id: %04x product_id: %04x\n", __FUNCTION__, __usbd_module_info, vendor_id, product_id);
+
+#ifdef CONFIG_USBD_NETWORK_BLAN_FERMAT
+        //printk(KERN_INFO "%s: fermat\n", __FUNCTION__);
+        fermat_init();
+#endif
+
+#if defined(CONFIG_USBD_NETWORK_CDC) 
+        if (cdc) {
+                //printk(KERN_INFO "%s: cdc\n", __FUNCTION__);
+                network_type = network_cdc;
+        }
+#endif
+
+#ifdef CONFIG_USBD_NETWORK_BASIC
+        if (basic) {
+                THROW_IF (network_type != network_unknown, select_error);
+                //printk(KERN_INFO "%s: basic\n", __FUNCTION__);
+                network_type = network_basic;
+        }
+#endif
+
+#ifdef CONFIG_USBD_NETWORK_BASIC2
+        if (basic2) {
+                THROW_IF (network_type != network_unknown, select_error);
+                //printk(KERN_INFO "%s: basic2\n", __FUNCTION__);
+                network_type = network_basic2;
+        }
+#endif
+
+#ifdef CONFIG_USBD_NETWORK_BLAN
+        if (blan) {
+                //printk(KERN_INFO "%s: blan\n", __FUNCTION__);
+                THROW_IF (network_type != network_unknown, select_error);
+                network_type = network_blan;
+        }
+#endif
+
+#if defined(CONFIG_USBD_NETWORK_CDC)
+        if (network_type == network_unknown) {
+                //printk(KERN_INFO "%s: cdc\n", __FUNCTION__);
+                network_type = network_cdc;
+        }
+#endif
+
+        // still unknown - check for other bNumConfigurations
+
+        if (network_type == network_unknown) {
+#if defined(CONFIG_USBD_NETWORK_BASIC)
+                //printk(KERN_INFO "%s: basic\n", __FUNCTION__);
+                THROW_IF (network_type != network_unknown, select_error);
+                network_type = network_basic;
+#endif
+#if defined(CONFIG_USBD_NETWORK_BASIC2)
+                //printk(KERN_INFO "%s: basic2\n", __FUNCTION__);
+                THROW_IF (network_type != network_unknown, select_error);
+                network_type = network_basic2;
+#endif
+#if defined(CONFIG_USBD_NETWORK_SAFE)
+                //printk(KERN_INFO "%s: safe\n", __FUNCTION__);
+                THROW_IF (network_type != network_unknown, select_error);
+                network_type = network_safe;
+#endif
+#if defined(CONFIG_USBD_NETWORK_BLAN)
+                //printk(KERN_INFO "%s: blan\n", __FUNCTION__);
+                THROW_IF (network_type != network_unknown, select_error);
+                network_type = network_blan;
+#endif
+        }
+
+        // sanity check
+        THROW_IF (network_type == network_unknown, select_error);
+
+        // select the function driver descriptors based on network_type
+
+        switch (network_type) {
+
+#if defined(CONFIG_USBD_NETWORK_BASIC)
+        case network_basic:
+                //printk(KERN_INFO "%s: basic\n", __FUNCTION__);
+                function_driver = &basic_function_driver;
+                break;
+#endif
+
+#if defined(CONFIG_USBD_NETWORK_BASIC2)
+        case network_basic2:
+                //printk(KERN_INFO "%s: basic2\n", __FUNCTION__);
+                function_driver = &basic2_function_driver;
+                break;
+#endif
+
+#if defined(CONFIG_USBD_NETWORK_SAFE)
+        case network_safe:
+                //printk(KERN_INFO "%s: blan bNumConfigurations: %d\n", __FUNCTION__, blan_function_driver.bNumConfigurations);
+                function_driver = &safe_function_driver;
+                break;
+#endif
+#if defined(CONFIG_USBD_NETWORK_BLAN)
+        case network_blan:
+                //printk(KERN_INFO "%s: blan bNumConfigurations: %d\n", __FUNCTION__, blan_function_driver.bNumConfigurations);
+                function_driver = &blan_function_driver;
+                break;
+#endif
+
+#if defined(CONFIG_USBD_NETWORK_CDC)
+        case network_cdc:
+                function_driver = &cdc_function_driver;
+                break;
+#endif
+        default:
+                THROW(select_error);
+                break;
+        }
+
+        strncpy(Network_net_device.name, network_type == zeroconf ? "usbz0" : (network_blan ? "usbl0" : "usbb0"), 6);
+
+
+        THROW_IF (!function_driver, select_error);
+
+        CATCH(select_error) {
+                printk(KERN_INFO "%s: configuration selection error\n", __FUNCTION__);
+		return -EINVAL;
+        }
+
+
+#ifdef CONFIG_USBD_NETWORK_EP0TEST
+        /*
+         * ep0test - test that bus interface can do ZLP on endpoint zero
+         *
+         * This will artificially force iProduct string descriptor to be
+         * exactly the same as the endpoint zero packetsize.  When the host
+         * requests this string it will request it not knowing the strength
+         * and will use a max length of 0xff. The bus interface driver must
+         * send a ZLP to terminate the transaction.
+         *
+         * The iProduct descriptor is used because both the Linux and
+         * Windows usb implmentations fetch this in a default enumeration. 
+         *
+         */
+        if (ep0test) {
+                switch (ep0test) {
+                case 8:  function_driver->device_description->iProduct = "012"; break;
+                case 16: function_driver->device_description->iProduct = "0123456"; break;
+                case 32: function_driver->device_description->iProduct = "0123456789abcde"; break;
+                case 64: function_driver->device_description->iProduct = "0123456789abcdef0123456789abcde"; break;
+                default: printk(KERN_ERR"%s: ep0test: bad value: %d, must be one of 8, 16, 32 or 64\n", 
+                                         __FUNCTION__, ep0test); return -EINVAL;
+                         break;
+                }
+                printk(KERN_INFO"%s: ep0test: iProduct set to: %s\n", __FUNCTION__, 
+                                function_driver->device_description->iProduct);
+        }
+        else 
+                printk(KERN_INFO"%s: ep0test: not set\n", __FUNCTION__);
+#endif /* CONFIG_USBD_NETWORK_EP0TEST */
+
+
+#ifdef CONFIG_USBD_NETWORK_ALLOW_SETID
+        //printk(KERN_INFO"%s: checking idVendor: %04x idProduct: %04x\n", __FUNCTION__, vendor_id, product_id);
+
+        if (vendor_id) 
+                function_driver->idVendor = cpu_to_le16(vendor_id);
+        
+        if (product_id) 
+                function_driver->idProduct = cpu_to_le16(product_id);
+#endif
+
+        if ((macstrtest(local_mac_address_str) || macstrtest(remote_mac_address_str))) {
+                printk(KERN_INFO"%s: bad size %s %s\n", __FUNCTION__, local_mac_address_str, remote_mac_address_str);
+		return -EINVAL;
+        }
+
+        set_address(local_mac_address_str, local_dev_addr);
+        set_address(remote_mac_address_str, remote_dev_addr);
+
+        RETURN_EINVAL_IF(network_create());
+        
+        Usb_network_private.network_type = network_type;
+
+        Usb_network_private.notification_bh.routine = notification_bh;
+        Usb_network_private.notification_bh.data = NULL;
+
+#ifdef CONFIG_HOTPLUG
+        Usb_network_private.hotplug_bh.routine = hotplug_bh;
+        Usb_network_private.hotplug_bh.data = NULL;
+#endif
+
+        memcpy(Network_net_device.dev_addr, local_dev_addr, ETH_ALEN);
+
+        THROW_IF(make_crc_table(), error);
+        
+        Usb_network_private.encapsulation = simple_crc;
+
+	THROW_IF(usbd_register_function (function_driver), error);
+
+	return 0;
+
+        CATCH(error) {
+                network_destroy();
+		return -EINVAL;
+        }
+}
+
+//_________________________________________________________________________________________________
+
+/* network_modexit - driver exit
+ *
+ * Cleans up the module. Deregisters the function driver and destroys the network object.
+ */
+static void network_modexit (void)
+{
+	//printk(KERN_INFO"%s: exiting\n", __FUNCTION__);
+        
+        while (Usb_network_private.notification_bh.sync) {
+                printk(KERN_ERR"%s: waiting for notificationhotplug bh\n", __FUNCTION__);
+                schedule_timeout(10 * HZ);
+        }
+
+#ifdef CONFIG_HOTPLUG
+        while (Usb_network_private.hotplug_bh.sync) {
+                printk(KERN_ERR"%s: waiting for hotplug bh\n", __FUNCTION__);
+                schedule_timeout(10 * HZ);
+        }
+#endif
+
+	usbd_deregister_function (function_driver);
+        network_destroy();
+        if (network_crc32_table) {
+                lkfree(network_crc32_table);
+                network_crc32_table = NULL;
+        }
+}
+
+//_________________________________________________________________________________________________
+
+module_init (network_modinit);
+module_exit (network_modexit);
+
diff -Nru a/drivers/usbd/network_fd/network.h b/drivers/usbd/network_fd/network.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/network_fd/network.h	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,203 @@
+/*
+ * usbd/network_fd/network.h - Network Function Driver
+ *
+ *      Copyright (c) 2002, 2003, 2004 Belcarra
+ *
+ * By: 
+ *      Chris Lynne <cl@belcarra.com>
+ *      Stuart Lynne <sl@belcarra.com>
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef NETWORK_FD_H
+#define NETWORK_FD_H 1
+
+typedef enum network_encapsulation {
+        simple_net, simple_crc, 
+} network_encapsulation_t;
+
+typedef enum network_hotplug_status {
+	hotplug_unkown,
+	hotplug_attached,
+	hotplug_detached
+} network_hotplug_status_t;
+
+typedef enum network_type {
+        network_unknown,
+        network_blan,
+        network_safe,
+        network_cdc,
+        network_basic,
+        network_basic2,
+} network_type_t;
+
+struct usb_network_private {
+
+        struct net_device_stats stats;  /* network device statistics */
+
+	int flags;
+	struct usb_function_instance *function;
+	unsigned int maxtransfer;
+        rwlock_t rwlock;
+
+	network_hotplug_status_t hotplug_status;
+        network_type_t network_type;
+
+        int state;
+
+        int mtu; 
+	int crc;
+#if defined(CONFIG_USBD_NETWORK_BLAN_FERMAT)
+        int fermat;
+#endif
+
+        unsigned int stopped; 
+        unsigned int restarts;
+
+        unsigned int max_queue_entries;
+        unsigned int max_queue_bytes;
+
+        unsigned int queued_entries;
+        unsigned int queued_bytes;
+
+        time_t avg_queue_entries;
+
+        time_t jiffies;
+        unsigned long samples;
+
+        int have_interrupt;
+
+        struct urb *int_urb;
+
+        network_encapsulation_t encapsulation;
+
+	struct tq_struct notification_bh;
+
+#ifdef CONFIG_HOTPLUG
+	struct tq_struct hotplug_bh;
+#endif
+};
+
+// XXX this needs to be co-ordinated with rndis.c maximum's
+#define MAXFRAMESIZE 2000
+
+#if !defined(CONFIG_USBD_MAXPOWER)
+	#define CONFIG_USBD_MAXPOWER                    0
+#endif
+
+#if !defined(CONFIG_USBD_MANUFACTURER)
+	#define CONFIG_USBD_MANUFACTURER              	"Belcarra"
+#endif
+
+
+#if !defined(CONFIG_USBD_SERIAL_NUMBER_STR)
+	#define CONFIG_USBD_SERIAL_NUMBER_STR           ""
+#endif
+
+/*
+ * Lineo specific 
+ */
+
+#define VENDOR_SPECIFIC_CLASS           0xff
+#define VENDOR_SPECIFIC_SUBCLASS        0xff
+#define VENDOR_SPECIFIC_PROTOCOL        0xff
+
+/*
+ * Lineo Classes
+ */
+#define LINEO_CLASS                     0xff
+
+#define LINEO_SUBCLASS_BASIC_NET          0x01
+#define LINEO_SUBCLASS_BASIC_SERIAL       0x02
+
+/*
+ * Lineo Protocols
+ */
+#define LINEO_BASIC_NET_CRC             0x01
+#define LINEO_BASIC_NET_CRC_PADDED      0x02
+
+#define LINEO_BASIC_SERIAL_CRC          0x01
+#define LINEO_BASIC_SERIAL_CRC_PADDED   0x02
+
+
+/*
+ * endpoint and interface indexes
+ */
+#define BULK_OUT        0x00
+#define BULK_IN         0x01
+#define INT_IN          0x02
+#define ENDPOINTS       0x03
+
+#define COMM_INTF       0x00
+#define DATA_INTF       0x01
+
+
+/* bmDataCapabilities */
+#define BMDATA_CRC              	0x01
+#define BMDATA_PADBEFORE        	0x02
+#define BMDATA_PADAFTER         	0x04
+#define BMDATA_FERMAT           	0x08
+#define BMDATA_HOSTNAME         	0x10
+
+/* bmNetworkCapabilities */
+#define BMNETWORK_SET_PACKET_OK 	0x01
+#define BMNETWORK_NOBRIDGE 	0x02
+
+
+/*
+ * BLAN Data Plane
+ */
+//#define CONFIG_USBD_NETWORK_PADBYTES 	8
+//#define CONFIG_USBD_NETWORK_PADAFTER 	1
+//#undef CONFIG_USBD_NETWORK_PADBEFORE 	
+//#define CONFIG_USBD_NETWORK_CRC 	1
+
+
+extern __u8 network_requested_endpoints[ENDPOINTS+1];
+extern __u16 network_requested_transferSizes[ENDPOINTS+1];
+extern struct usb_network_private Usb_network_private;
+extern __u8 local_dev_addr[ETH_ALEN];
+extern __u8 remote_dev_addr[ETH_ALEN];
+
+extern struct usb_function_operations network_fd_function_ops;
+extern struct usb_network_private Usb_network_private;
+
+struct usb_class_safe_networking_mdlm_descriptor {
+        __u8 bFunctionLength;		// 0x06
+        __u8 bDescriptorType;		// 0x24
+        __u8 bDescriptorSubtype;        // 0x13
+        __u8 bGuidDescriptorType;	// 0x00
+        __u8 bmNetworkCapabilities;
+        __u8 bmDataCapabilities;
+} __attribute__ ((packed));
+
+struct usb_class_blan_networking_mdlm_descriptor {
+        __u8 bFunctionLength;		// 0x07
+        __u8 bDescriptorType;		// 0x24
+        __u8 bDescriptorSubtype;        // 0x13
+        __u8 bGuidDescriptorType;	// 0x01
+        __u8 bmNetworkCapabilities;
+        __u8 bmDataCapabilities;
+        __u8 bPad;
+} __attribute__ ((packed));
+
+
+
+
+
+#endif /* NETWORK_FD_H */
diff -Nru a/drivers/usbd/network_fd/safe.c b/drivers/usbd/network_fd/safe.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/network_fd/safe.c	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,224 @@
+/*
+ * usbd/network_fd/safe.c - Network Function Driver
+ *
+ *      Copyright (c) 2002, 2003, 2004 Belcarra
+ *
+ * By: 
+ *      Chris Lynne <cl@belcarra.com>
+ *      Stuart Lynne <sl@belcarra.com>
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <usbd-export.h>
+#include <usbd-build.h>
+
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/utsname.h>
+#include <linux/netdevice.h>
+
+#include <usbd-chap9.h>
+#include <usbd-mem.h>
+#include <usbd.h>
+#include <usbd-func.h>
+
+#include "network.h"
+
+
+#ifdef CONFIG_USBD_NETWORK_SAFE
+/* USB SAFE  Configuration ******************************************************************** */
+
+/*
+ * SAFE Ethernet Configuration
+ */
+
+/* Communication Interface Class descriptors
+ */
+
+static __u8 safe_data_1[] = { 0x07, USB_DT_ENDPOINT, OUT,  BULK,     0,  0x00, 0x00, };
+static __u8 safe_data_2[] = { 0x07, USB_DT_ENDPOINT, IN,   BULK,     0,  0x00, 0x00, };
+static __u8 safe_comm_1[] = { 0x07, USB_DT_ENDPOINT, IN,   INTERRUPT,0,  0x00, 0x0a, };
+
+static __u8 safe_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x10, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_VERSION */ };
+static __u8 safe_class_2[] = { 0x15, CS_INTERFACE, USB_ST_MDLM, 0x00, 0x01, /* bcdVersion, bcdVersion */
+                0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6,  /* bGUID */
+                0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f,  /* bGUID */ };
+
+static __u8 safe_class_3[] = { 0x06, CS_INTERFACE, USB_ST_MDLMD, 0x00, 0x00, 0x00,    /* bDetailData */ };
+
+static __u8 safe_class_4[] = { 0x0d, CS_INTERFACE, USB_ST_ENF, 
+        0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x05, /* 1514 maximum frame size */
+        0x00, 0x00, 0x00 , };
+
+
+static __u8 *safe_alt_endpoints[] = { 
+        (struct usb_endpoint_descriptor *) &safe_data_1, 
+        (struct usb_endpoint_descriptor *) &safe_data_2, 
+        (struct usb_endpoint_descriptor *) &safe_comm_1, };
+static __u8 *safe_comm_class_descriptors[] = { 
+        (struct usb_generic_class_descriptor *) &safe_class_1, 
+        (struct usb_generic_class_descriptor *) &safe_class_2, 
+        (struct usb_generic_class_descriptor *) &safe_class_3, 
+        (struct usb_generic_class_descriptor *) &safe_class_4, };
+
+u8 safe_alt_indexes[] = { BULK_OUT, BULK_IN, INT_IN, };
+
+/* Data Interface Alternate descriptions and descriptors
+ */
+static __u8 safe_alternate_descriptor[sizeof(struct usb_interface_descriptor)] = {
+        0x09, USB_DT_INTERFACE, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting
+        sizeof (safe_alt_endpoints) / sizeof(struct usb_endpoint_descriptor), // bNumEndpoints
+        COMMUNICATIONS_INTERFACE_CLASS, COMMUNICATIONS_MDLM_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00,
+};
+
+static struct usb_alternate_description safe_alternate_descriptions[] = {
+	{ iInterface: CONFIG_USBD_NETWORK_SAFE_INTF,
+                interface_descriptor: (struct usb_interface_descriptor *)&safe_alternate_descriptor,
+                classes:sizeof (safe_comm_class_descriptors) / sizeof (struct usb_generic_class_descriptor *),
+                class_list: safe_comm_class_descriptors,
+                endpoints:sizeof (safe_alt_endpoints) / sizeof(struct usb_endpoint_descriptor *),
+                endpoint_list: safe_alt_endpoints,
+                endpoint_indexes: safe_alt_indexes,
+                },
+};
+
+/* Interface descriptions and descriptors
+ */
+static struct usb_interface_description safe_interfaces[] = {
+	{ alternates:sizeof (safe_alternate_descriptions) / sizeof (struct usb_alternate_description),
+                alternate_list:safe_alternate_descriptions, },
+};
+
+
+/* Configuration descriptions and descriptors
+ */
+
+static __u8 safe_configuration_descriptor[sizeof(struct usb_configuration_descriptor)] = {
+        0x09, USB_DT_CONFIG, 0x00, 0x00, // wLength
+        sizeof (safe_interfaces) / sizeof (struct usb_interface_description),
+        0x01, 0x00, // bConfigurationValue, iConfiguration
+        BMATTRIBUTE, BMAXPOWER,
+};
+
+struct usb_configuration_description safe_description[] = {
+	{ iConfiguration: CONFIG_USBD_NETWORK_SAFE_DESC,
+                configuration_descriptor: (struct usb_configuration_descriptor *)safe_configuration_descriptor,
+	        bNumInterfaces:sizeof (safe_interfaces) / sizeof (struct usb_interface_description),
+                interface_list:safe_interfaces, },
+};
+
+/* Device Description
+ */
+
+//static __u8 safe_device_descriptor[sizeof(struct usb_device_descriptor)] = {
+//        0x12, USB_DT_DEVICE, 
+//        0x00, 0x02, // bcdUSB
+//        COMMUNICATIONS_DEVICE_CLASS, 
+//        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+//};
+static struct usb_device_descriptor safe_device_descriptor = {
+	bLength: sizeof(struct usb_device_descriptor),
+	bDescriptorType: USB_DT_DEVICE,
+	bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION),
+	bDeviceClass: COMMUNICATIONS_DEVICE_CLASS,
+	bDeviceSubClass: 0x02,
+	bDeviceProtocol: 0x00,
+	bMaxPacketSize0: 0x00,
+	idVendor: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_VENDORID),
+	idProduct: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_PRODUCTID),
+	bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_BCDDEVICE),
+};
+
+static struct usb_endpoint_request safe_endpoint_requests[ENDPOINTS+1] = {
+        { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512,  },
+        { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512,  },
+        { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, },
+        { 0, },
+};
+
+struct usb_device_description safe_device_description = {
+        device_descriptor: &safe_device_descriptor,
+	iManufacturer: CONFIG_USBD_NETWORK_MANUFACTURER,
+        iProduct: CONFIG_USBD_NETWORK_PRODUCT_NAME,
+#if !defined(CONFIG_USBD_NO_SERIAL_NUMBER) && defined(CONFIG_USBD_SERIAL_NUMBER_STR)
+	iSerialNumber:CONFIG_USBD_SERIAL_NUMBER_STR,
+#endif
+        endpointsRequested: ENDPOINTS,
+        requestedEndpoints: safe_endpoint_requests,
+};
+
+void safe_init (struct usb_function_instance *function)
+{
+        struct usb_class_ethernet_networking_descriptor *ethernet;
+        int len = 0;
+        char buf[255];
+
+        buf[0] = 0;
+
+        safe_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 3 : 2;
+
+        // Update the iMACAddress field in the ethernet descriptor
+        {
+                char address_str[14];
+                snprintf(address_str, 13, "%02x%02x%02x%02x%02x%02x",
+                                remote_dev_addr[0], remote_dev_addr[1], remote_dev_addr[2], 
+                                remote_dev_addr[3], remote_dev_addr[4], remote_dev_addr[5]);
+                if ((ethernet = (struct usb_class_ethernet_networking_descriptor *)safe_class_4)) {
+                        if (ethernet->iMACAddress) {
+                                usbd_dealloc_string(ethernet->iMACAddress);
+                        }
+                        ethernet->iMACAddress = usbd_alloc_string(address_str);
+                }
+        }
+
+
+#ifdef CONFIG_USBD_NETWORK_SAFE_PADBEFORE
+        safe_class_3[5] |= BMDATA_PADBEFORE;
+        len += sprintf(buf + len, "PADBEFORE: %02x ", safe_class_3[5]);
+#endif
+#ifdef CONFIG_USBD_NETWORK_SAFE_CRC
+        safe_class_3[5] |= BMDATA_CRC;
+        len += sprintf(buf + len, "CRC: %02x ", safe_class_3[5]);
+#endif
+#ifdef CONFIG_USBD_NETWORK_SAFE_NOBRIDGE
+        safe_class_3[4] |= BMNETWORK_NOBRIDGE;
+        len += sprintf(buf + len, "NOBRIDGE: %02x ",safe_class_3[4]);
+#endif
+        if (strlen(buf))
+                printk(KERN_INFO"%s: %s\n", __FUNCTION__, buf);
+
+}
+
+struct usb_function_driver safe_function_driver = {
+	name: "network-SAFE",
+	fops: &network_fd_function_ops,
+	device_description: &safe_device_description,
+	bNumConfigurations: sizeof (safe_description) / sizeof (struct usb_configuration_description),
+	configuration_description: safe_description,
+	idVendor: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_VENDORID),
+	idProduct: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_PRODUCTID),
+	bcdDevice: __constant_cpu_to_le16(CONFIG_USBD_NETWORK_BCDDEVICE),
+};
+#endif				/* CONFIG_USBD_NETWORK_SAFE */
+
diff -Nru a/drivers/usbd/trace.c b/drivers/usbd/trace.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/trace.c	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,509 @@
+/*
+ * usbd/trace.c
+ *
+ *      Copyright (c) 2004 Belcarra
+ *
+ * Adapted from earlier work:
+ *      Copyright (c) 2002, 2003 Belcarra
+ *      Copyright (c) 2002 Lineo
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+//EXPORT_NO_SYMBOLS;
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+
+#include <asm/atomic.h>
+#include <asm/io.h>
+
+#include <linux/proc_fs.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,6)
+#define USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK 1
+#include <linux/timer.h>
+#else
+#undef USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK
+#include <linux/tqueue.h>
+#endif
+
+#include <linux/pci.h>
+#include <linux/cache.h>
+
+
+#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA)
+#include <asm/dma.h>
+#include <asm/mach/dma.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/hardware.h>
+#include <asm/types.h>
+#endif
+
+#if defined(CONFIG_SOC_AU1X00) || \
+        defined(CONFIG_MIPS_AU1X00) || \
+        defined(CONFIG_CPU_AU1X00) || \
+        defined(CONFIG_MIPS_AU1000) || \
+        defined(CONFIG_MIPS_PB1500) || \
+        defined(CONFIG_MIPS_PB1100) 
+
+#include <asm/au1000.h>
+#include <asm/au1000_dma.h>
+#include <asm/mipsregs.h>
+
+#endif /* defined(..) */
+
+#if defined(CONFIG_ARCH_SAMSUNG)
+#include <asm/arch/timers.h>
+#include <asm/arch/hardware.h>
+#endif /* defined(CONFIG_ARCH_SAMSUNG) */
+
+#if defined(CONFIG_ARCH_MX1ADS)
+#include "dbmx1_bi/dbmx1.h"
+#endif /* defined(CONFIG_ARCH_MX1ADS) */
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+
+#include "usbd-chap9.h"
+#include "usbd-mem.h"
+#include "usbd.h"
+#include "usbd-func.h"
+#include "usbd-bus.h"
+
+#include "trace.h"
+#include "usbd-bi.h"
+
+
+int trace_first;
+int trace_next;
+trace_t *traces;
+
+#if defined(CONFIG_USBD_BI_REGISTER_TRACE) && defined(CONFIG_PROC_FS)
+
+trace_t *TRACE_NEXT(trace_types_t trace_type)
+{
+        trace_t *p;
+
+        p = traces + trace_next;
+
+        #if defined(CONFIG_ARCH_SA1100) || \
+                        defined (CONFIG_ARCH_PXA)
+
+                p->oscr = OSCR;
+
+        #elif defined(CONFIG_SOC_AU1X00) || \
+                defined(CONFIG_MIPS_AU1X00) || \
+                        defined(CONFIG_CPU_AU1X00) || \
+                        defined(CONFIG_MIPS_AU1000) || \
+                        defined(CONFIG_MIPS_PB1500) || \
+                        defined(CONFIG_MIPS_PB1100) 
+
+        #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19)
+                p->cp0_count = __read_32bit_c0_register(CP0_COUNT);
+        #else
+                p->cp0_count = read_c0_count();
+        #endif
+
+        #elif defined(CONFIG_ARCH_SAMSUNG)
+                p->tcnt1 = *(volatile u32 *)TCNT1;
+        #else
+                p->jiffies = jiffies;
+        #endif
+
+        #if defined(CONFIG_SOC_AU1X00) || \
+                defined(CONFIG_MIPS_AU1X00) || \
+                        defined(CONFIG_CPU_AU1X00) || \
+                        defined(CONFIG_MIPS_AU1000) || \
+                        defined(CONFIG_MIPS_PB1500) || \
+                        defined(CONFIG_MIPS_PB1100) 
+                //p->sofs = au_readl(USBD_FRAMENUM);
+        #endif
+
+        #if defined(CONFIG_ARCH_MX1ADS)
+                p->sofs = USBD_FRAME & 0x3ff;
+        #endif
+
+        p->interrupts = udc_interrupts;
+        p->trace_type = trace_type;
+
+        trace_next++;
+        trace_next = (trace_next == TRACE_MAX) ? 0 : trace_next;
+
+        if (trace_next == trace_first) {
+                trace_first++;
+                trace_first = (trace_first == TRACE_MAX) ? 0 : trace_first;
+        }
+
+        return p;
+}
+
+/* Proc Filesystem *************************************************************************** */
+        
+static __inline__ char *nullchk(char *s)
+{
+        return s ? s : "(NULL)";
+}
+/* *    
+ * trace_proc_read - implement proc file system read.
+ * @file        
+ * @buf         
+ * @count
+ * @pos 
+ *      
+ * Standard proc file system read function.
+ */         
+static ssize_t trace_proc_read (struct file *file, char *buf, size_t count, loff_t * pos)
+{                                  
+        unsigned long page;
+        int len = 0;
+        int index;
+        int oindex;
+        int previous;
+
+        #ifdef MODULE
+                MOD_INC_USE_COUNT;
+        #endif
+        // get a page, max 4095 bytes of data...
+        if (!(page = get_free_page (GFP_KERNEL))) {
+
+                #ifdef MODULE
+                        MOD_DEC_USE_COUNT;
+                #endif
+                return -ENOMEM;
+        }
+
+        len = 0;
+        oindex = index = (*pos)++;
+
+        if (index == 0) {
+                #if defined(CONFIG_ARCH_SAMSUNG)
+                        len += sprintf ((char *) page + len, " Index     Ints     Ticks [%d]\n", CONFIG_USBD_SMDK2500_BCLOCK );
+                #else
+                        len += sprintf ((char *) page + len, " Index     Ints     Ticks\n");
+                #endif
+        }       
+                         
+        index += trace_first;
+        if (index >= TRACE_MAX) {
+                index -= TRACE_MAX;
+        }
+        previous = (index) ? (index - 1) : (TRACE_MAX - 1);
+
+        //printk(KERN_INFO"first: %d next: %d index: %d %d prev: %d\n", trace_first, trace_next, oindex, index, previous);
+
+        if (
+                        ((trace_first < trace_next) && (index >= trace_first) && (index < trace_next)) ||
+                        ((trace_first > trace_next) && ((index < trace_next) || (index >= trace_first)))
+           )
+        {
+
+                #if defined(CONFIG_SOC_AU1X00) || \
+                                defined(CONFIG_MIPS_AU1X00) || \
+                                        defined(CONFIG_CPU_AU1X00) || \
+                                        defined(CONFIG_MIPS_AU1000) || \
+                                        defined(CONFIG_MIPS_PB1500) || \
+                                        defined(CONFIG_MIPS_PB1100) 
+
+                        u32 ticks = 0;
+
+                #elif defined(CONFIG_ARCH_SAMSUNG)
+                        u32 ticks = 0;
+                #elif defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA)
+                        u32 ticks = 0;
+                        u64 jifs = 0;
+                #else
+                        u64 jifs = 0;
+                #endif
+
+                trace_t *p = traces + index;
+                unsigned char *cp;
+                int skip = 0;
+
+                if (oindex > 0) {
+                        trace_t *o = traces + previous;
+
+                        #if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA)
+                                /*
+                                 * oscr is 3.6864 Mhz free running counter, 
+                                 *
+                                 *      1/3.6864 = .2712
+                                 *      60/221   = .2714
+                                 *
+                                 */
+                                if (o->oscr) {
+                                        ticks = (p->oscr > o->oscr) ? (p->oscr - o->oscr) : (o->oscr - p->oscr) ;
+                                        ticks = (ticks * 60) / 221;
+                                }
+
+                        #elif defined(CONFIG_SOC_AU1X00) || \
+                                        defined(CONFIG_MIPS_AU1X00) || \
+                                        defined(CONFIG_CPU_AU1X00) || \
+                                        defined(CONFIG_MIPS_AU1000) || \
+                                        defined(CONFIG_MIPS_PB1500) || \
+                                        defined(CONFIG_MIPS_PB1100) 
+                                /*
+                                 * cp0_count is incrementing timer at system clock
+                                 */
+                                if (o->cp0_count) {
+                                        ticks = (p->cp0_count > o->cp0_count) ? 
+                                                (p->cp0_count - o->cp0_count) : (o->cp0_count - p->cp0_count) ;
+                                        ticks = ticks / CONFIG_USBD_AU1X00_SCLOCK;
+                                }
+                        #elif defined(CONFIG_ARCH_SAMSUNG)
+                                /*
+                                 * tcnt1 is a count-down timer running at the system bus clock
+                                 * The divisor must be set as a configuration value, typically 66 or 133.
+                                 */
+                                if (o->tcnt1) {
+                                        ticks = (p->tcnt1 < o->tcnt1) ?  (o->tcnt1 - p->tcnt1) : (p->tcnt1 - o->tcnt1) ;
+                                        ticks /= CONFIG_USBD_SMDK2500_BCLOCK;
+                                }
+                        #else
+                                if (o->jiffies) {
+                                        jifs = p->jiffies - traces[previous].jiffies;
+                                }
+                        #endif
+
+                        if (o->interrupts != p->interrupts) {
+                                skip++;
+                        }
+                }
+                
+                //printk(KERN_INFO"index: %d interrupts: %d\n", index, p->interrupts);
+                len += sprintf ((char *) page + len, "%s%6d %8d ", skip?"\n":"", index, p->interrupts);
+
+                #if defined(CONFIG_SOC_AU1X00) || \
+                               defined(CONFIG_MIPS_AU1X00) || \
+                               defined(CONFIG_CPU_AU1X00) || \
+                               defined(CONFIG_MIPS_AU1000) || \
+                               defined(CONFIG_MIPS_PB1500) || \
+                               defined(CONFIG_MIPS_PB1100) || \
+                               defined(CONFIG_ARCH_SA1100) || \
+                               defined(CONFIG_ARCH_PXA)
+
+                        if (ticks > 1024*1024) {
+                                len += sprintf ((char *) page + len, "%8dM ", ticks>>20);
+                        }
+                        else {
+                                len += sprintf ((char *) page + len, "%8d  ", ticks);
+                        }
+
+                #elif defined(CONFIG_ARCH_SAMSUNG)
+                        //len += sprintf ((char *) page + len, "%8u ", p->jiffies);
+                        //len += sprintf ((char *) page + len, "%8u ", p->tcnt0);
+                        len += sprintf ((char *) page + len, "%8u ", p->tcnt1);
+                        if (ticks > 1024*1024) {
+                                len += sprintf ((char *) page + len, "%8dM ", ticks>>20);
+                        }
+                        else {
+                                len += sprintf ((char *) page + len, "%8d  ", ticks);
+                        }
+                #else
+                        if (jifs > 1024) {
+                                len += sprintf ((char *) page + len, "%4d ", (int)jifs>>20);
+                        }
+                        else {
+                                len += sprintf ((char *) page + len, "%4d  ", (int)jifs);
+                        }
+                #endif
+                #if defined(CONFIG_ARCH_MX1ADS)
+                        len += sprintf ((char *) page + len, "%6d  ", (int)p->sofs);
+                #endif
+
+                switch (p->trace_type) {
+                case trace_msg:
+                        len += sprintf ((char *) page + len, " --                   ");
+                        len += sprintf ((char *) page + len, p->trace.msg.msg);
+                        break;
+
+                case trace_w:
+                        len += sprintf ((char *) page + len, " -->                  ");
+                        len += sprintf ((char *) page + len, "[%8x] W %s", p->trace.msg32.val, nullchk(p->trace.msg32.msg));
+                        break;
+
+                case trace_r:
+                        len += sprintf ((char *) page + len, "<--                   ");
+                        len += sprintf ((char *) page + len, "[%8x] R %s", p->trace.msg32.val, nullchk(p->trace.msg32.msg));
+                        break;
+
+                case trace_msg32:
+                        len += sprintf ((char *) page + len, " --                   ");
+                        len += sprintf ((char *) page + len, p->trace.msg32.msg, p->trace.msg32.val);
+                        break;
+
+                case trace_msg16:
+                        len += sprintf ((char *) page + len, " --                   ");
+                        len += sprintf ((char *) page + len, p->trace.msg16.msg, p->trace.msg16.val0, p->trace.msg16.val1);
+                        break;
+
+                case trace_msg8:
+                        len += sprintf ((char *) page + len, " --                   ");
+                        len += sprintf ((char *) page + len, p->trace.msg8.msg, 
+                                        p->trace.msg8.val0, p->trace.msg8.val1, p->trace.msg8.val2, p->trace.msg8.val3);
+                        break;
+
+                case trace_setup:
+                        cp = (unsigned char *)&p->trace.setup;
+                        len += sprintf ((char *) page + len, 
+                                        " --           request [%02x %02x %02x %02x %02x %02x %02x %02x]", 
+                                        cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
+                        break;
+
+                case trace_recv:
+                case trace_sent:
+                        cp = (unsigned char *)&p->trace.sent;
+                        len += sprintf ((char *) page + len, 
+                                        "%s             %s [%02x %02x %02x %02x %02x %02x %02x %02x]", 
+                                        ( p->trace_type == trace_recv)?"<-- ":" -->",
+                                        ( p->trace_type == trace_recv)?"recv":"sent",
+                                        cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
+                        break;
+                }
+                len += sprintf ((char *) page + len, "\n");
+        }
+
+        if ((len > count) || (len == 0)) 
+                len = -EINVAL;
+        
+        else if (len > 0 && copy_to_user (buf, (char *) page, len)) 
+                len = -EFAULT;
+        
+        free_page (page);
+        MOD_DEC_USE_COUNT;
+        return len;
+}
+
+/* *
+ * trace_proc_write - implement proc file system write.
+ * @file
+ * @buf
+ * @count
+ * @pos
+ *
+ * Proc file system write function, used to signal monitor actions complete.
+ * (Hotplug script (or whatever) writes to the file to signal the completion
+ * of the script.)  An ugly hack.
+ */
+static ssize_t trace_proc_write (struct file *file, const char *buf, size_t count, loff_t * pos)
+{
+        return count;
+}
+
+static struct file_operations trace_proc_operations_functions = {
+        read:trace_proc_read,
+        write:trace_proc_write,
+};
+
+int trace_reinit (void)
+{
+        printk(KERN_INFO"%s:\n", __FUNCTION__);
+        //trace_first = trace_next = 0;
+        return 0;
+}
+
+/**
+ * udc_request_udc_io - request UDC io region
+ *
+ * Return non-zero if not successful.
+ */
+int trace_init (void)
+{
+        struct proc_dir_entry *p;
+        trace_first = trace_next = 0;
+        printk(KERN_INFO"%s:\n", __FUNCTION__);
+
+        if (!(traces = vmalloc(sizeof(trace_t) * TRACE_MAX))) {
+                printk(KERN_ERR"BITRACE malloc failed %p %d\n", traces, sizeof(trace_t) * TRACE_MAX);
+                return -EINVAL;
+        }
+
+        memset(traces, 0, sizeof(trace_t) * TRACE_MAX);
+
+        TRACE_MSG("init");
+        TRACE_MSG("test");
+
+        // create proc filesystem entries
+        if ((p = create_proc_entry ("bitrace", 0, 0)) == NULL) 
+                printk(KERN_INFO"BITRACE PROC FS failed\n");
+        else 
+                p->proc_fops = &trace_proc_operations_functions;
+
+        #if defined(CONFIG_ARCH_SAMSUNG)
+                *(volatile u32 *)TMOD |= 0x3 << 3;
+        #endif
+	return 0;
+}
+
+/**
+ * udc_release_io - release UDC io region
+ */
+void trace_exit (void)
+{
+        {
+                unsigned long flags;
+                local_irq_save (flags);
+                remove_proc_entry ("bitrace", NULL);
+                if (traces) {
+                        trace_t *p = traces;
+                        traces = 0;
+                        vfree(p);
+                }
+                local_irq_restore (flags);
+        }
+}
+
+#else
+int trace_reinit (void)
+{
+        return 0;
+}
+
+int trace_init (void)
+{
+        return 0;
+}
+
+void trace_exit (void) 
+{
+	return;
+}
+#endif
+
+/* End of FILE */
+
+
+//EXPORT_SYMBOL(trace_first);
+//EXPORT_SYMBOL(trace_next);
+//EXPORT_SYMBOL(traces);
+
diff -Nru a/drivers/usbd/trace.h b/drivers/usbd/trace.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/trace.h	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,270 @@
+/*
+ * usbd/trace.h
+ *
+ *      Copyright (c) 2004 Belcarra
+ *
+ * Adapted from earlier work:
+ *      Copyright (c) 2002, 2003 Belcarra
+ *      Copyright (c) 2002 Lineo
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#if defined(CONFIG_ARCH_SAMSUNG)
+#ifndef CONFIG_USBD_SMDK2500_BCLOCK 
+#define CONFIG_USBD_SMDK2500_BCLOCK 66
+#endif
+#endif
+
+
+typedef enum trace_int_types {
+        trace_int_udc, trace_int_ep0, trace_int_in, trace_int_out, trace_int_int
+} trace_int_types_t;
+
+typedef struct trace_int {
+        u32     last;
+        u32     total;
+        u32     samples;
+} trace_int_t;
+
+
+typedef enum trace_types {
+        trace_setup, trace_msg, trace_msg32, trace_msg16, trace_msg8, trace_recv, trace_sent, trace_w, trace_r
+} trace_types_t;
+
+
+typedef struct trace_regs32 {
+        u32     reg;
+        char *  msg;
+} trace_regs32_t;
+
+
+typedef struct trace_msg {
+        char    *msg;
+} trace_msg_t;
+
+typedef struct trace_msg32 {
+        u32      val;
+        char    *msg;
+} trace_msg32_t;
+
+typedef struct trace_msg16 {
+        u16     val0;
+        u16     val1;
+        char    *msg;
+} trace_msg16_t;
+
+typedef struct trace_msg8 {
+        u8      val0;
+        u8      val1;
+        u8      val2;
+        u8      val3;
+        char    *msg;
+} trace_msg8_t;
+
+
+typedef struct trace {
+        trace_types_t        trace_type;
+        u32     interrupts;
+#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA)
+        u32     oscr;
+
+#elif defined(CONFIG_SOC_AU1X00) || defined(CONFIG_MIPS_AU1X00) || defined(CONFIG_CPU_AU1X00) || defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) 
+
+        u32     cp0_count;
+#elif defined(CONFIG_ARCH_SAMSUNG)
+        //u32     tcnt0;
+        u32     tcnt1;
+        //u64     jiffies;
+#else
+        u64     jiffies;
+#endif
+#if defined(CONFIG_SOC_AU1X00) || defined(CONFIG_MIPS_AU1X00) || defined(CONFIG_CPU_AU1X00) || defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_ARCH_MX1ADS)
+        u64     sofs;
+#endif
+        union {
+                trace_msg_t        msg;
+                trace_msg8_t       msg8;
+                trace_msg16_t      msg16;
+                trace_msg32_t      msg32;
+
+                struct usb_device_request       setup;
+                unsigned char      recv[8];
+                unsigned char      sent[8];
+
+        } trace;
+
+} trace_t;
+
+
+#define TRACE_MAX       10000
+
+extern int trace_first;
+extern int trace_next;
+
+extern trace_int_t *trace_ints;
+extern trace_t *traces;
+
+#ifdef CONFIG_USBD_BI_REGISTER_TRACE
+
+trace_t *TRACE_NEXT(trace_types_t trace_type);
+
+static __inline__ void TRACE_SETUP(struct usb_device_request *setup)
+{
+        if (traces) {
+                trace_t *p = TRACE_NEXT(trace_setup);
+                p->trace_type = trace_setup;
+                memcpy(&p->trace.setup, setup, sizeof(struct usb_device_request));
+        }
+}
+
+static __inline__ void TRACE_MSG(char *msg)
+{
+        if (traces) {
+                trace_t *p = TRACE_NEXT(trace_msg);
+                p->trace.msg.msg = msg;
+        }
+}
+
+static __inline__ void TRACE_W(char *msg, u32 val)
+{
+        if (traces) {
+                trace_t *p = TRACE_NEXT(trace_w);
+                p->trace.msg32.val = val;
+                p->trace.msg32.msg = msg;
+        }
+}
+
+static __inline__ void TRACE_R(char *msg, u32 val)
+{
+        if (traces) {
+                trace_t *p = TRACE_NEXT(trace_r);
+                p->trace.msg32.val = val;
+                p->trace.msg32.msg = msg;
+        }
+}
+
+static __inline__ void TRACE_MSG32(char *msg, u32 val)
+{
+        if (traces) {
+                trace_t *p = TRACE_NEXT(trace_msg32);
+                p->trace.msg32.val = val;
+                p->trace.msg32.msg = msg;
+        }
+}
+
+static __inline__ void TRACE_MSG16(char *msg, u16 val0, u16 val1)
+{
+        if (traces) {
+                trace_t *p = TRACE_NEXT(trace_msg16);
+                p->trace.msg16.val0 = val0;
+                p->trace.msg16.val1 = val1;
+                p->trace.msg16.msg = msg;
+        }
+}
+
+static __inline__ void TRACE_MSG8(char *msg, u8 val0, u8 val1, u8 val2, u8 val3)
+{
+        if (traces) {
+                trace_t *p = TRACE_NEXT(trace_msg8);
+                p->trace.msg8.val0 = val0;
+                p->trace.msg8.val1 = val1;
+                p->trace.msg8.val2 = val2;
+                p->trace.msg8.val3 = val3;
+                p->trace.msg8.msg = msg;
+        }
+}
+
+static __inline__ void TRACE_RECV(unsigned char *cp)
+{
+        if (traces) {
+                trace_t *p = TRACE_NEXT(trace_recv);
+                memcpy(&p->trace.recv, cp, 8);
+        }
+}
+
+static __inline__ void TRACE_RECVN(unsigned char *cp, int bytes)
+{
+        if (traces) {
+                trace_t *p = TRACE_NEXT(trace_recv);
+                memset(&p->trace.recv, 0, 8);
+                memcpy(&p->trace.recv, cp, bytes);
+        }
+}
+
+static __inline__ void TRACE_SENT(unsigned char *cp)
+{
+        if (traces) {
+                trace_t *p = TRACE_NEXT(trace_sent);
+                memcpy(&p->trace.sent, cp, 8);
+        }
+}
+
+#else
+
+static __inline__ void TRACE_SETUP(struct usb_device_request *setup)
+{
+}
+
+static __inline__ void TRACE_IRQS(u32 cr, u32 sr)
+{
+}
+
+static __inline__ void TRACE_RECV(unsigned char *cp)
+{
+}
+
+static __inline__ void TRACE_SENT(unsigned char *cp)
+{
+}
+
+static __inline__ void TRACE_W(char *msg, u32 val)
+{
+}
+
+static __inline__ void TRACE_R(char *msg, u32 val)
+{
+}
+
+static __inline__ void TRACE_MSG(char *msg)
+{
+}
+
+static __inline__ void TRACE_MSG32(char *msg, u32 val)
+{
+}
+
+static __inline__ void TRACE_MSG16(char *msg, u16 val0, u16 val1)
+{
+}
+
+static __inline__ void TRACE_MSG8(char *msg, u8 val0, u8 val1, u8 val2, u8 vale)
+{
+}
+static __inline__ void TRACE_RECVN(unsigned char *cp, int bytes)
+{
+}
+
+#endif
+
+int trace_init (void);
+int trace_reinit (void);
+void trace_exit (void);
+
diff -Nru a/drivers/usbd/udc.h b/drivers/usbd/udc.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/udc.h	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,48 @@
+/*
+ * usbd/udc.h
+ *
+ *      Copyright (c) 2004 Belcarra
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <asm/atomic.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include <usbd-export.h>
+#include <usbd-build.h>
+#include <usbd-chap9.h>
+#include <usbd-mem.h>
+#include <usbd.h>
+#include <usbd-bus.h>
+#include <trace.h>
+#include <usbd-bi.h>
+
diff -Nru a/drivers/usbd/usbd-admin.h b/drivers/usbd/usbd-admin.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/usbd-admin.h	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,61 @@
+/*
+ * usbd/usbd-admin.h 
+ *
+ * Copyright (c) 2004 Belcarra
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+/*
+ * This defines the public interface to administer the USB Device Software.
+ *
+ * Specifically it allows the USB Device software to be:
+ *
+ * 	enabled		enable a specific function driver for use
+ * 	disabled	disable the USB Device Software
+ *
+ * 	disconnected	disable the USB Pullup Resistor (also known as soft-connect)
+ * 	connected	enable the USB Pullup Resistor
+ *
+ * 	pm_off		perform required functions for Power Management off
+ * 	pm_on		perform required functions for Power Management on
+ *
+ * 
+ */
+
+int usbd_enable(char *);
+int usbd_enable_irq(char *);
+int usbd_disable(char *);
+int usbd_disable_irq(char *);
+
+int usbd_disconnect(char *);
+int usbd_connect(char *);
+int usbd_pm_on(char *);
+int usbd_pm_off(char *);
+
+#ifdef CONFIG_USBD_DEPRECATED
+int usbd_load(char *);
+int usbd_unload(char *);
+int usbd_replug(char *);
+int usbd_unplug(char *);
+#endif
+
diff -Nru a/drivers/usbd/usbd-bi.c b/drivers/usbd/usbd-bi.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/usbd-bi.c	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,753 @@
+/*
+ * usbd/usbd-bi.c - USB Bus Interface Driver
+ *
+ *      Copyright (c) 2004 Belcarra
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcara.com>, 
+ *      Tom Rushworth <tbr@belcara.com>, 
+ *      Bruce Balden <balden@belcara.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Notes
+ *
+ * 1. The usbd-bi layer has be re-implemented to re-factor the UDC layer in a way to
+ * simplify implementation of UDC drivers. As much of the complexity of dealing with the middle
+ * layers and buffer (urb) handling is provided for in the common bi layer. 
+ *
+ * 2. TODO The udc interface will be further modified to allow the UDC to export a block of
+ * function pointers for the common bi layer to use. This will allow the common layer to
+ * implement default operations where the UDC does not provide an function. For example many UDC
+ * drivers do not provide full support for cable detection and usb pullup control. If these
+ * routines are not provided the common layer will supply defaults. This eliminates a reasonably
+ * larger amount of effectively unused code from many of the udc drivers.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <asm/uaccess.h>
+
+#include "usbd-chap9.h"
+#include "usbd-mem.h"
+#include "usbd.h"
+#include "usbd-bus.h"
+#include "usbd-func.h"
+#include "trace.h"
+#include "usbd-bi.h"
+#include "usbd-admin.h"
+
+#ifdef MODULE
+static char *serial_number_str;
+MODULE_PARM (serial_number_str, "s");
+MODULE_PARM_DESC (serial_number_str, "Serial Number");
+#else 
+static char *serial_number_str;
+#endif
+
+struct usb_bus_instance *usbd_bus;
+int have_cable_irq;
+
+/* ******************************************************************************************* */
+
+int bi_attached (struct usb_bus_instance *bus)
+{
+        return udc_ops.attached ? udc_ops.attached () : 1;
+}
+
+int bi_connected (struct usb_bus_instance *bus)
+{
+        return udc_ops.connected ?  udc_ops.connected () : 1;
+}
+
+int bi_disconnect (struct usb_bus_instance *bus, char *arg)
+{
+        RETURN_EINVAL_IF (in_interrupt ());
+        if (udc_ops.disconnect) udc_ops.disconnect ();
+        return 0;
+}
+
+int bi_connect (struct usb_bus_instance *bus, char *arg)
+{
+        RETURN_EINVAL_IF (in_interrupt ());
+        if (udc_ops.connect) udc_ops.connect ();
+        return 0;
+}
+
+int bi_request_endpoints(struct usb_endpoint_map *endpoint_map_array, int endpointsRequested, 
+                struct usb_endpoint_request *requestedEndpoints)
+{
+        int i;
+        RETURN_EINVAL_IF (udc_ops.request_endpoints(endpoint_map_array, endpointsRequested, requestedEndpoints));
+        for (i = 0; i < endpointsRequested; i++) {
+                struct usb_endpoint_map *endpoint_map = endpoint_map_array + i;
+                TRACE_MSG8 ("address: %02x physical: %02x request: %02x size: %04x", 
+                                endpoint_map->bEndpointAddress[0], endpoint_map->physicalEndpoint[0], 
+                                endpoint_map->bmAttributes[0], endpoint_map->wMaxPacketSize[0]);
+        }
+        return 0;
+}
+int bi_set_endpoints(int endpointsRequested, struct usb_endpoint_map *endpoint_map_array)
+{
+        return udc_ops.set_endpoints ? udc_ops.set_endpoints(endpointsRequested, endpoint_map_array) : 0;
+}
+
+/* ******************************************************************************************* */
+/* bi_endpoint_halted - check if endpoint halted
+ * Used by the USB Device Core to check endpoint halt status.
+ */
+int bi_endpoint_halted (struct usb_bus_instance *bus, int endpoint_index)
+{
+        return udc_ops.endpoint_halted ? udc_ops.endpoint_halted(endpoint_index) : 0;
+}
+
+/* bi_device_feature - handle set/clear feature requests
+ * Used by the USB Device Core to check endpoint halt status.
+ */
+int bi_device_feature (struct usb_bus_instance *bus, int endpoint_index, int flag)
+{
+        return 0;
+}
+
+/* bi_disable_endpoints - disable udc and all endpoints
+ */
+static void bi_disable_endpoints (struct usb_bus_instance *bus)
+{
+        int i;
+        RETURN_IF (!bus || !bus->endpoint_array);
+        for (i = 1; i < udc_ops.max_endpoints; i++) {
+                struct usb_endpoint_instance *endpoint = (bus->endpoint_array + i);
+                CONTINUE_IF (!endpoint);
+                usbd_flush_endpoint (endpoint);
+        }
+}
+
+
+/* bi_device_event_irq - handle generic bus event
+ * Called by usb core layer to inform bus of an event.
+ */
+int bi_device_event_irq (struct usb_bus_instance *bus, usb_device_event_t event, int data)
+{
+        int epn;
+        int endpointsRequested = bus->function_instance->endpointsRequested;
+        struct usb_endpoint_map *endpoint_map_array = bus->function_instance->endpoint_map_array;
+
+        TRACE_MSG32 ("EVENT %x", event);
+        switch (event) {
+        case DEVICE_UNKNOWN:
+                break;
+
+        case DEVICE_INIT:
+                TRACE_MSG ("EVENT INIT");
+                break;
+
+        case DEVICE_CREATE:	
+                TRACE_MSG ("EVENT CREATE");
+                bi_disable_endpoints (bus);
+                if (udc_ops.enable) udc_ops.enable ();
+                if (udc_ops.suspended_interrupts) udc_ops.suspended_interrupts ();
+                break;
+
+        case DEVICE_HUB_CONFIGURED:
+                TRACE_MSG ("EVENT HUB_CONFIGURED");
+                bi_connect (usbd_bus, NULL);
+                break;
+
+        case DEVICE_RESET:
+                TRACE_MSG ("EVENT RESET");
+                if (udc_ops.set_address) udc_ops.set_address (0);
+                if (udc_ops.reset_ep) udc_ops.reset_ep (0);
+                if (udc_ops.suspended_interrupts) udc_ops.suspended_interrupts ();
+                bi_disable_endpoints (bus);
+                break;
+
+        case DEVICE_ADDRESS_ASSIGNED:
+                TRACE_MSG ("EVENT ADDRESSED");
+                if (udc_ops.set_address) udc_ops.set_address (data);
+                if (udc_ops.all_interrupts) udc_ops.all_interrupts ();
+                break;
+
+        case DEVICE_CONFIGURED:
+                TRACE_MSG ("EVENT CONFIGURED");
+                // iterate across the physical endpoint instance array to enable the endpoints
+                if (udc_ops.setup_ep)
+                        for (epn = 1; epn < bus->endpoints; epn++) 
+                                udc_ops.setup_ep (epn, bus->endpoint_array + epn);
+                return 0;
+
+        case DEVICE_DE_CONFIGURED:
+                TRACE_MSG ("EVENT DE-CONFIGURED");
+                break;
+
+        case DEVICE_SET_INTERFACE:
+                TRACE_MSG ("EVENT SET INTERFACE");
+                break;
+
+        case DEVICE_SET_FEATURE:
+                TRACE_MSG ("EVENT SET FEATURE");
+                break;
+
+        case DEVICE_CLEAR_FEATURE:
+                TRACE_MSG ("EVENT CLEAR FEATURE");
+                break;
+
+        case DEVICE_BUS_INACTIVE:
+                TRACE_MSG ("EVENT INACTIVE");
+                if (udc_ops.suspended_interrupts) udc_ops.suspended_interrupts (); // disable suspend interrupt
+                if (bi_attached (usbd_bus)) 
+                        usbd_bus_event_irq (bus, DEVICE_RESET, 0);
+                break;
+
+        case DEVICE_BUS_ACTIVITY:
+                TRACE_MSG ("EVENT ACTIVITY");
+                if (udc_ops.all_interrupts) udc_ops.all_interrupts (); // enable suspend interrupt
+                break;
+
+        case DEVICE_POWER_INTERRUPTION:
+                TRACE_MSG ("POWER INTERRUPTION");
+                break;
+
+        case DEVICE_HUB_RESET:
+                TRACE_MSG ("HUB RESET");
+                break;
+
+        case DEVICE_DESTROY:
+                TRACE_MSG ("DEVICE DESTROY");
+                bi_disconnect (usbd_bus, NULL);
+                bi_disable_endpoints (bus);
+                if (udc_ops.disable_interrupts) udc_ops.disable_interrupts ();
+                if (udc_ops.disable) udc_ops.disable ();
+                break;
+
+        case DEVICE_CLOSE:
+                break;
+        }
+        return 0;
+}
+
+
+/* bi_start_endpoint_in
+ */
+int bi_start_endpoint_in (struct usb_bus_instance *bus, struct usb_endpoint_instance *endpoint)
+{
+        unsigned long flags;
+
+        //printk (KERN_INFO"%s:  bus: %p status: %d\n", __FUNCTION__, bus, bus->status);
+        
+        RETURN_ZERO_IF (!endpoint);
+        udc_interrupts++;
+
+        TRACE_MSG32 ("BI START ENDPOINT IN:  tx_urb: %x", (int)endpoint->tx_urb);
+        local_irq_save (flags);
+        // call udc_start_endpoint_in IFF we didn't previously have a tx urb 
+        if (!endpoint->tx_urb && bi_tx_next_irq (endpoint)) {
+                TRACE_MSG16 ("BI START ENDPOINT IN:  bEndpointAddress: %x actual_length: %d", 
+                                (int)endpoint->bEndpointAddress, endpoint->tx_urb->actual_length);
+                udc_ops.start_endpoint_in (endpoint);
+        }
+        local_irq_restore (flags);
+        //printk (KERN_INFO"%s: finis\n", __FUNCTION__);
+        return 0;
+}
+
+/* bi_start_endpoint_out
+ */
+int bi_start_endpoint_out (struct usb_bus_instance *bus, struct usb_endpoint_instance *endpoint)
+{
+        unsigned long flags;
+
+        //printk (KERN_INFO"%s:  bus: %p status: %d\n", __FUNCTION__, bus, bus->status);
+        
+        RETURN_ZERO_IF (!endpoint);
+        udc_interrupts++;
+        TRACE_MSG32 ("BI START ENDPOINT OUT:  rcv_urb: %x", (int)endpoint->rcv_urb);
+        local_irq_save (flags);
+        // call udc_start_endpoint_OUT IFF we didn't previously have a rcv urb 
+        if (!endpoint->rcv_urb && bi_rcv_next_irq (endpoint)) {
+                TRACE_MSG16 ("BI START ENDPOINT OUT:  bEndpointAddress: %x request_length: %d", 
+                                (int)endpoint->bEndpointAddress, endpoint->rcv_urb->request_length);
+                udc_ops.start_endpoint_out (endpoint);
+        }
+        local_irq_restore (flags);
+        //printk (KERN_INFO"%s: finis\n", __FUNCTION__);
+        return 0;
+}
+
+
+/* bi_cancel_urb_irq - cancel sending an urb
+ * Used by the USB Device Core to cancel an urb.
+ */
+int bi_cancel_urb_irq (struct urb *urb)
+{
+        RETURN_EINVAL_IF (!urb);
+        switch (urb->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+        case USB_DIR_IN:
+                //printk (KERN_INFO"%s: IN urb: %p\n", __FUNCTION__, urb);
+                // is this the active urb?
+                if (urb->endpoint->tx_urb == urb) {
+                        urb->endpoint->tx_urb = NULL;
+                        if (udc_ops.cancel_in_irq) udc_ops.cancel_in_irq (urb);
+                }
+                usbd_urb_sent_finished_irq (urb, SEND_FINISHED_CANCELLED);
+                break;
+
+        case USB_DIR_OUT:
+                //printk (KERN_INFO"%s: OUT urb: %p\n", __FUNCTION__, urb);
+                // is this the active urb?
+                if (urb->endpoint->rcv_urb == urb) {
+                        urb->endpoint->rcv_urb = NULL;
+                        if (udc_ops.cancel_out_irq) udc_ops.cancel_out_irq (urb);
+                }
+                TRACE_MSG ("CANCEL RECV URB");
+                usbd_urb_recv_finished_irq (urb, RECV_CANCELLED);
+                break;
+        }
+        return 0;
+}
+
+struct urb * bi_rcv_complete_irq (struct usb_endpoint_instance *endpoint, int len, int urb_bad)
+{
+        struct urb *rcv_urb;
+        
+        // if we had an urb then update actual_length, dispatch if neccessary
+        if (likely ( (int) (rcv_urb = endpoint->rcv_urb))) {
+        
+                //printk (KERN_ERR"%s: actual: %d buffer: %d\n", 
+                //              __FUNCTION__, rcv_urb->actual_length, rcv_urb->buffer_length);
+
+                TRACE_MSG8 ("BI RCV COMPLETE: actual: %d len: %d bad: %d: status: %d",
+                                rcv_urb->actual_length, len, urb_bad, rcv_urb->status);
+
+                TRACE_MSG8 ("BI RCV COMPLETE: request: %d buffer: %d packet: %d transfer: %d",
+                                rcv_urb->request_length, rcv_urb->buffer_length,
+                                endpoint->wMaxPacketSize, endpoint->rcv_transferSize);
+
+                // check the urb is ok, are we adding data less than the packetsize
+                if (!urb_bad && !endpoint->rcv_error && (rcv_urb->bus->status == USBD_OK) && (len <= endpoint->wMaxPacketSize)) {
+
+                        // increment the received data size
+                        rcv_urb->actual_length += len;
+
+                        // if the current received data is short (less than full packetsize) which
+                        // indicates the end of the bulk transfer, we have received the maximum
+                        // transfersize, or if we do not have enough room to receive another packet 
+                        // then pass this data up to the function driver
+
+                        // XXX this needs to be fixed, for example the MSC driver 
+                        // has varying maximum sizes
+
+
+                        if (
+                                         ( (len < endpoint->wMaxPacketSize) ||
+                                         (rcv_urb->actual_length >= endpoint->rcv_transferSize) ||
+                                         (rcv_urb->actual_length >= rcv_urb->request_length) ||
+                                         (rcv_urb->actual_length + endpoint->wMaxPacketSize > rcv_urb->buffer_length)))
+                        {
+#if 0
+                                int i;
+                                for (i = 0; i < rcv_urb->actual_length; TRACE_RECV (rcv_urb->buffer + i), i+= 8);
+#endif
+                                endpoint->rcv_urb = NULL;
+                                rcv_urb->jiffies = jiffies;
+                                rcv_urb->framenum = udc_ops.framenum ? udc_ops.framenum () : 0;
+                                TRACE_MSG32 ("BI RCV COMPLETE: finished length: %d", rcv_urb->actual_length);
+                                TRACE_MSG32 ("BI RCV COMPLETE: framenum: %x", (int) rcv_urb->framenum);
+                                usbd_urb_recv_finished_irq (rcv_urb, RECV_OK);
+                                rcv_urb = NULL;
+                        }
+                }
+                else {
+                        rcv_urb->actual_length = 0;
+                        //endpoint->rcv_error = 1;
+                }
+        }
+
+        // if we don't have an urb see if we can get one
+        return bi_rcv_next_irq (endpoint);
+}
+
+struct urb * bi_tx_complete_irq (struct usb_endpoint_instance *endpoint, int restart)
+{
+        struct urb *tx_urb;
+
+        // if we have a tx_urb advance or reset, finish if complete
+        if ( (likely ( (int)tx_urb = endpoint->tx_urb))) {
+
+                TRACE_MSG32 ("BI TX CURRENT TX_URB: %p", (int)endpoint->tx_urb);
+                TRACE_MSG8 ("BI TX COMPLETE: actual: %d sent: %d last: %d: status: %d",
+                                tx_urb->actual_length, endpoint->sent, endpoint->last, tx_urb->status);
+
+                if (likely (!restart)) {
+                        int sent = endpoint->last;
+                        endpoint->sent += sent;
+                        endpoint->last -= sent;
+                }
+                else
+                        endpoint->last = 0;
+
+                //printk (KERN_INFO"%s: act: %d last: %d sent: %d status: %d flags: %x\n", __FUNCTION__,
+                //              endpoint->tx_urb->actual_length, endpoint->last, endpoint->sent, 
+                //              tx_urb->status, tx_urb->flags);
+
+                // XXX is the test for bEndpointAddress still appropriate for CONTROL WRITES
+                if ( ( (tx_urb->actual_length - endpoint->sent) <= 0) && ! (tx_urb->flags & USBD_URB_SENDZLP) ) {
+                        //if (endpoint->bEndpointAddress) 
+                        tx_urb->jiffies = jiffies;
+                        tx_urb->framenum = udc_ops.framenum ? udc_ops.framenum () : 0;
+                        TRACE_MSG32 ("BI TX COMPLETE: finished tx_urb: %p", (int)tx_urb);
+                        TRACE_MSG32 ("BI TX COMPLETE: framenum: %x", (int)tx_urb->framenum);
+                        usbd_urb_sent_finished_irq (tx_urb, SEND_FINISHED_OK);
+                        endpoint->tx_urb = NULL;
+                        endpoint->last = endpoint->sent = 0;
+                }
+        }
+        return bi_tx_next_irq (endpoint);
+}
+
+/* ******************************************************************************************* */
+/* bi_udc_init_irq - initialize USB Device Controller
+ * Get ready to use the USB Device Controller.
+ * Register the interrupt handlers and optional IO region.
+ * Return non-zero for error.
+ */
+int bi_udc_init_irq (void)
+{
+	THROW_IF (udc_ops.request_udc_irq ? udc_ops.request_udc_irq () : 0, irq_err);
+	THROW_IF (udc_ops.request_io ? udc_ops.request_io () : 0, io_err);
+	THROW_IF (udc_ops.init ? udc_ops.init () : 0, init_err);
+        CATCH (irq_err) {
+                printk (KERN_ERR"%s: could not request USB IRQ\n", __FUNCTION__);
+                CATCH (io_err) {
+                        CATCH (init_err) {
+                                printk (KERN_ERR"%s: could not request USB IO space\n", __FUNCTION__);
+                                if (udc_ops.release_io) udc_ops.release_io ();
+                        }
+                        if (udc_ops.release_udc_irq) udc_ops.release_udc_irq ();
+                }
+                printk (KERN_ERR"%s: error\n", __FUNCTION__);
+                return -EINVAL;
+        }
+	return 0;
+}
+
+
+/* bi_udc_exit - Stop using the USB Device Controller
+ * Stop using the USB Device Controller.
+ * Shutdown and free dma channels, de-register the interrupt handler.
+ */
+void bi_udc_exit (struct usb_bus_instance *bus)
+{
+        if (udc_ops.disable_ep) udc_ops.disable_ep (0);
+        bi_disable_endpoints (bus);
+	bi_disconnect (usbd_bus, NULL);
+	if (udc_ops.disable) udc_ops.disable ();
+	if (udc_ops.release_io) udc_ops.release_io ();
+	if (udc_ops.release_udc_irq) udc_ops.release_udc_irq ();
+}
+
+
+/* ************************************************************************************* */
+
+int bus_disable (struct usb_bus_instance *bus, char *arg);
+int bus_enable (struct usb_bus_instance *bus, char *arg);
+
+int bi_pm_off (struct usb_bus_instance *bus, char *arg)
+{
+        //printk (KERN_INFO"%s:\n", __FUNCTION__);
+        RETURN_EINVAL_IF (in_interrupt ());
+        return bus_disable (bus, arg);
+}
+
+int bi_pm_on (struct usb_bus_instance *bus, char *arg)
+{
+        //printk (KERN_INFO"%s:\n", __FUNCTION__);
+        RETURN_EINVAL_IF (in_interrupt ());
+        return bi_attached (usbd_bus) ? bus_enable (bus, arg) : 0;
+}
+
+int bi_fix_serial_number_str (struct usb_bus_instance *bus)
+{
+        char *sp, *dp;
+        for (sp = dp = usbd_bus->serial_number_str; sp && sp[0]; sp++) {
+                CONTINUE_IF (!isxdigit (sp[0]));
+                *dp++ = toupper (*sp);
+        }
+        if (dp) 
+                *dp = '\0';
+        return 0;
+}
+
+int bi_serial_number (struct usb_bus_instance *bus, char *arg)
+{
+        char *sp, *dp;
+        RETURN_EINVAL_IF (in_interrupt ());
+        if (usbd_bus->serial_number_str) 
+                lkfree (usbd_bus->serial_number_str);
+        usbd_bus->serial_number_str = lstrdup (arg);
+        bi_fix_serial_number_str (bus);
+        return 0;
+}
+
+
+/* ******************************************************************************************* */
+
+struct usb_bus_operations bi_ops = {
+        bus_enable: bus_enable,
+        bus_disable: bus_disable,
+        bus_pm_off: bi_pm_off,
+        bus_pm_on: bi_pm_on,
+        bus_serial_number: bi_serial_number,
+        start_endpoint_in: bi_start_endpoint_in,
+        start_endpoint_out: bi_start_endpoint_out,
+        cancel_urb_irq: bi_cancel_urb_irq,
+        endpoint_halted: bi_endpoint_halted,
+        device_feature: bi_device_feature,
+        device_event: bi_device_event_irq,
+        bus_disconnect: bi_disconnect,
+        bus_connect: bi_connect,
+        bus_attached: bi_attached,
+        bus_connected: bi_connected,
+        request_endpoints: bi_request_endpoints,
+        set_endpoints: bi_set_endpoints,
+};
+
+
+struct usb_bus_driver bi_driver = {
+	bops: &bi_ops,
+};
+
+void bi_cable_event_irq (void)
+{
+        (bi_attached (usbd_bus) ? usbd_enable_irq : usbd_disable_irq) (NULL);
+}
+
+void bi_cable_event (void)
+{
+        unsigned long flags;
+        local_irq_save (flags);
+        bi_cable_event_irq ();
+        local_irq_restore (flags);
+}
+
+
+/* Prevent overlapp of bi administrative functions mainly:
+ *      bus_enable
+ *      bus_disable
+ *      bi_modinit
+ *      bi_modexit
+ */ 
+DECLARE_MUTEX (usbd_bi_sem);     
+
+int bus_disable_sem (struct usb_bus_instance *bus, char *arg)
+{
+	struct bi_data *data;
+        unsigned long flags;
+
+        RETURN_ZERO_IF (usbd_bus_state_enabled != bus->bus_state);
+        MOD_DEC_USE_COUNT;
+
+        bi_disconnect (usbd_bus, NULL);
+        if (udc_ops.disable) udc_ops.disable ();
+
+        local_irq_save (flags);
+        if (bus->device_state != STATE_ATTACHED) {
+                usbd_bus_event_irq (bus, DEVICE_RESET, 0);
+                usbd_bus_event_irq (bus, DEVICE_POWER_INTERRUPTION, 0);
+                usbd_bus_event_irq (bus, DEVICE_HUB_RESET, 0);
+        }
+        usbd_bus_event_irq (bus, DEVICE_DESTROY, 0);
+        bi_disable_endpoints (bus);
+        bi_udc_exit (bus);
+        local_irq_restore (flags);
+
+        usbd_disable_function (bus);
+        bus->bus_state = usbd_bus_state_disabled;
+        return 0;
+}
+
+int bus_disable (struct usb_bus_instance *bus, char *arg)
+{
+        RETURN_EINVAL_IF (in_interrupt ());
+        down (&usbd_bi_sem);
+        bus_disable_sem (bus, arg);
+        up (&usbd_bi_sem);
+        return 0;
+}
+
+void bi_startup_events(void)
+{
+        if (udc_ops.startup_events)
+                udc_ops.startup_events();
+        else {
+                usbd_bus_event_irq (usbd_bus, DEVICE_INIT, 0);
+                usbd_bus_event_irq (usbd_bus, DEVICE_CREATE, 0);
+                usbd_bus_event_irq (usbd_bus, DEVICE_HUB_CONFIGURED, 0);
+                usbd_bus_event_irq (usbd_bus, DEVICE_RESET, 0);
+        }
+}
+
+int bus_enable (struct usb_bus_instance *bus, char *arg)
+{
+        struct usb_endpoint_instance *endpoint;
+        int rc = -EINVAL;
+        unsigned long flags;
+
+        //printk (KERN_INFO "%s: %s bus_state: %d\n", __FUNCTION__, arg ? arg : " (NULL)", bus->bus_state);
+        RETURN_EINVAL_IF (in_interrupt ());
+
+        down (&usbd_bi_sem);
+        bus_disable_sem (bus, arg);
+        MOD_INC_USE_COUNT;
+
+        local_irq_save (flags);
+        do {
+                // check if we can see the UDC and register, then enable the function
+                BREAK_IF (bi_udc_init_irq ());
+                BREAK_IF (usbd_enable_function_irq (bus, arg));
+                rc = 0;
+
+                // setup endpoint zero
+                endpoint = bus->endpoint_array + 0;
+                endpoint->bEndpointAddress = 0;
+
+                //endpoint->tx_attributes = 0;
+                endpoint->wMaxPacketSize = udc_ops.ep0_packetsize;
+                endpoint->rcv_transferSize = 255;       // XXX should this be higher
+                endpoint->wMaxPacketSize = udc_ops.ep0_packetsize;
+                if (udc_ops.setup_ep) udc_ops.setup_ep (0, endpoint);
+
+                // hopefully device enumeration will finish this process
+                bi_startup_events ();
+                trace_reinit ();
+        } while (0);
+        local_irq_restore (flags);
+        up (&usbd_bi_sem);
+        
+        //printk (KERN_INFO"%s: finis\n", __FUNCTION__);
+        if (rc) {
+                printk (KERN_INFO "%s: failed\n", __FUNCTION__);
+                bi_udc_exit (NULL);
+        }
+        return rc;
+}
+
+/* ************************************************************************************* */
+
+static int bi_modinit (void)
+{
+        extern const char *usbd_bi_module_info (void);
+	//struct bi_data *data = NULL;
+        static int first = 0;
+
+        printk (KERN_INFO "%s: %s serial: \"%s\"\n", __FUNCTION__, usbd_bi_module_info (), 
+                        serial_number_str && strlen (serial_number_str) ? serial_number_str : "");
+
+        down (&usbd_bi_sem);
+
+        THROW_IF (trace_init (), error);
+        THROW_IF (usbd_bus, error);
+
+        // Set the UDC defaults
+        udc_ops.disable_interrupts ();
+        bi_disconnect (usbd_bus, NULL);
+
+	bi_driver.name = udc_ops.name;
+	bi_driver.max_endpoints = udc_ops.max_endpoints;
+	bi_driver.maxpacketsize = udc_ops.ep0_packetsize;
+
+	// register this bus interface driver and create the device driver instance
+	THROW_IF (! (usbd_bus = usbd_register_bus (&bi_driver)), error);
+
+	//THROW_IF (! (data = ckmalloc (sizeof (struct bi_data), GFP_KERNEL)), error);
+	//memset (data, 0, sizeof (struct bi_data));
+	//usbd_bus->privdata = data;
+
+	// see if we can scrounge up something to set a sort of unique device address
+	if (udc_ops.serial_init ? udc_ops.serial_init () : -EINVAL) {
+                if (serial_number_str && strlen (serial_number_str)) 
+                        bi_serial_number (usbd_bus, serial_number_str);
+        }
+        else 
+                bi_fix_serial_number_str (usbd_bus);
+        
+        have_cable_irq = udc_ops.request_cable_irq ? !udc_ops.request_cable_irq () : 0;
+        up (&usbd_bi_sem);
+        
+#if defined (MODULE) || !defined (CONFIG_USBD_BI_DELAY_ENABLE)
+        // if we are connected OR if we don't have a cable irq fake an attach event
+        
+        // XXX this is not quite correct, if not attached then an enable
+        // will be required later if attached on a system that does not have
+        // cable event.
+        //
+        //if (!have_cable_irq || udc_attached ()) {
+	// Even if we have a cable irq, we need to check the current status,
+	// because the cable may have been attached before we installed the handler.
+
+        if (bi_attached (usbd_bus)) {
+                printk (KERN_INFO"%s: cable attached\n", __FUNCTION__);
+                bi_cable_event ();
+	}
+#endif
+	return 0;
+
+        CATCH (error) {
+                printk (KERN_ERR"%s: error loading module\n", __FUNCTION__);
+                //if (data)
+                //        lkfree (data);
+                if (usbd_bus) 
+                        usbd_deregister_bus (usbd_bus);
+                usbd_bus = NULL;
+                trace_exit ();
+                up (&usbd_bi_sem);
+                printk (KERN_INFO"%s: UP\n", __FUNCTION__);
+                return -EINVAL;
+        }
+}
+
+#ifdef MODULE
+/* bi_modexit - This is *only* used for drivers compiled and used as a module.
+ */
+static void bi_modexit (void)
+{
+        down (&usbd_bi_sem);
+        RETURN_IF (!usbd_bus);
+
+	if (have_cable_irq) 
+                udc_ops.release_cable_irq ();
+
+        bus_disable_sem (usbd_bus, NULL);
+        
+        //if ( (usbd_bus->privdata)) 
+        //        lkfree (usbd_bus->privdata);
+        //usbd_bus->privdata = NULL;
+
+        if (usbd_bus->serial_number_str) 
+                lkfree (usbd_bus->serial_number_str);
+        
+        usbd_deregister_bus (usbd_bus);
+        usbd_bus = NULL;
+        trace_exit ();
+}
+
+module_exit (bi_modexit);
+#endif
+
+module_init (bi_modinit);
+
diff -Nru a/drivers/usbd/usbd-bi.h b/drivers/usbd/usbd-bi.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/usbd-bi.h	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,196 @@
+/*
+ * usbd/usbd-bi.h
+ *
+ *      Copyright (c) 2004 Belcarra
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+struct udc_ops {
+
+	/* mandatory */
+	int max_endpoints;
+	int ep0_packetsize;
+	char *name;
+
+	void (*start_endpoint_in) 
+		(struct usb_endpoint_instance *); 	/* start an IN urb */
+	void (*start_endpoint_out) 
+		(struct usb_endpoint_instance *);	/* start an OUT urb */
+
+	int (*request_endpoints) 
+		(struct usb_endpoint_map *, int, struct usb_endpoint_request *);
+
+	/* optional */
+
+	int (*set_endpoints) 
+		(int , struct usb_endpoint_map *);
+
+	void (*cancel_in_irq) (struct urb *urb);	/* cancel active urb for IN endpoint */
+	void (*cancel_out_irq) (struct urb *urb);	/* cancel active urb for OUT endpoint */
+
+	void (*reset_ep) (unsigned int ep); 		/* reset requested endpoint */
+	int (*endpoint_halted) (unsigned int ep); 	/* Return non-zero if requested endpoint is halted */
+
+	void (*set_address) (unsigned char address);/* set the device USB address */
+	int (* serial_init) (void); 			/* get device serial number if available */
+	void (*setup_ep) (unsigned int ep, 
+		 struct usb_endpoint_instance *endpoint); /* setup specified endpoint for use */
+	void (*disable_ep) (unsigned int ep);		/* disable specified endpoint */
+	void (*stall_ep) (u32 );			/* stall endpoint */
+
+	int (*attached) (void); 			/* return non-zero if the USB cable connected */
+	int (*connected) (void); 			/* return non-zero if the USB pullup resistor is enabled */
+	void (*connect) (void); 			/* enable USB pullup resistor to enable connection to host */
+	void (*disconnect) (void); 			/* disable pullup resistor to disconnect from host */
+
+	int (*framenum) (void); 			/* fetch SOF framenum */
+
+	void (*all_interrupts) (void); 			/* enable all interrupts for normal operation */
+	void (*suspended_interrupts) (void); 		/* enable interrupts for suspended operation */
+	void (*disable_interrupts) (void); 		/* disable all interrupts */
+
+	void (*enable) (void); 				/* enable the UDC */
+	void (*disable) (void); 			/* disable the UDC */
+
+	void (*startup_events) (void); 			/* perform UDC specific USB events */
+	int (*init) (void); 				/* initialize USB Device Controller */
+
+	int (*request_udc_irq) (void); 			/* return non-zero if request for UDC interrupt fails */
+	int (*request_cable_irq) (void); 		/* return non-zero if request for Cable interrupt fails */
+	int (*request_io) (void); 			/* return non-zero if request UDC IO region fails */
+	void (*release_udc_irq) (void); 		/* release USB Device Controller interrupt */
+	void (*release_cable_irq) (void); 		/* release Cable interrupt */
+	void (*release_io) (void); 			/* release UDC IO region */
+};
+
+extern struct udc_ops udc_ops;
+extern unsigned int udc_interrupts;
+extern struct usb_bus_instance *usbd_bus;
+
+void bi_cable_event_irq (void); 			/* called from cradle interrupt handler */
+void bi_cable_event (void);
+
+/* bi_rcv_next_irq - complete a receive
+ * Called from rcv interrupt to complete.
+ */
+static __inline__ struct urb * bi_rcv_next_irq (struct usb_endpoint_instance *endpoint)
+{
+	if (!endpoint->rcv_urb)
+		if ( (endpoint->rcv_urb = usbd_first_urb_detached_irq (&endpoint->rdy)))
+			endpoint->rcv_urb->status = RECV_IN_PROGRESS;
+        TRACE_MSG32 ("BI RCV_URB: %p", (int)endpoint->rcv_urb);
+	return endpoint->rcv_urb;
+}
+
+
+/* bi_rcv_complete_irq - complete a receive
+ * Called from rcv interrupt to complete.
+ */
+struct urb * bi_rcv_complete_irq (struct usb_endpoint_instance *endpoint, int len, int urb_bad);
+
+static __inline__ void bi_rcv_cancelled_irq (struct usb_endpoint_instance *endpoint)
+{
+        struct urb *rcv_urb;
+
+	TRACE_MSG32 ("BI RCV CANCELLED: %p", (int) endpoint->rcv_urb);
+        RETURN_IF (! (rcv_urb = endpoint->rcv_urb));
+	printk (KERN_INFO"%s: rcv_urb: %p\n", __FUNCTION__, endpoint->rcv_urb);
+        usbd_urb_recv_finished_irq (rcv_urb, RECV_CANCELLED);
+	endpoint->sent = endpoint->last = 0;
+        endpoint->rcv_urb = NULL;
+	printk (KERN_INFO"%s: rcv_urb: %p\n", __FUNCTION__, endpoint->rcv_urb);
+}
+
+
+/* bi_tx_next_irq - complete a receive
+ * Called from tx interrupt to complete.
+ */
+static __inline__ struct urb * bi_tx_next_irq (struct usb_endpoint_instance *endpoint)
+{
+	if (!endpoint->tx_urb)
+		if ( (endpoint->tx_urb = usbd_first_urb_detached_irq (&endpoint->tx))) {
+#if 0
+			int i;
+			TRACE_MSG16 ("NEXT TX: length: %d flags: %x", endpoint->tx_urb->actual_length, endpoint->tx_urb->flags);
+			for (i = 0; i < endpoint->tx_urb->actual_length; TRACE_SENT (endpoint->tx_urb->buffer + i), i+= 8);
+#endif
+			endpoint->tx_urb->status = SEND_IN_PROGRESS;
+		}
+	TRACE_MSG32 ("BI TX NEXT TX_URB: %p", (int)endpoint->tx_urb);
+	return endpoint->tx_urb;
+}
+
+
+/* bi_tx_complete_irq - complete a transmit
+ * Called from tx interrupt to complete.
+ */
+struct urb * bi_tx_complete_irq (struct usb_endpoint_instance *endpoint, int restart);
+
+static __inline__ void bi_tx_cancelled_irq (struct usb_endpoint_instance *endpoint)
+{
+        struct urb *tx_urb;
+
+	TRACE_MSG32 ("BI TX CANCELLED: %p", (int) endpoint->tx_urb);
+        RETURN_IF (! (tx_urb = endpoint->tx_urb));
+        usbd_urb_sent_finished_irq (tx_urb, SEND_FINISHED_CANCELLED);
+	endpoint->sent = endpoint->last = 0;
+        endpoint->tx_urb = NULL;
+}
+
+static __inline__ int bi_tx_sendzlp (struct usb_endpoint_instance *endpoint)
+{
+	struct urb *tx_urb = endpoint->tx_urb;
+	RETURN_ZERO_IF (!tx_urb || (tx_urb->actual_length != endpoint->sent) || ! (tx_urb->flags & USBD_URB_SENDZLP)); 
+        tx_urb->flags &= ~USBD_URB_SENDZLP;
+        return 1;
+}
+
+/* bi_rcv_complete_irq - complete a receive
+ * Called from rcv interrupt to complete.
+ */
+static __inline__ void bi_rcv_fast_complete_irq (struct usb_endpoint_instance *endpoint, struct urb *rcv_urb)
+{
+	TRACE_MSG32 ("BI RCV FAST COMPLETE: %d", rcv_urb->actual_length);
+        usbd_urb_recv_finished_irq (rcv_urb, RECV_OK);
+} 
+
+/* bi_recv_setup - process a device request
+ * Note that we verify if a receive urb has been queued for H2D with non-zero wLength
+ * and return -EINVAL to stall if the upper layers have not properly tested for and 
+ * setup a receive urb in this case.
+ */
+static __inline__ int bi_recv_setup_irq (struct usb_device_request *request)
+{
+        struct usb_endpoint_instance *endpoint = usbd_bus->endpoint_array + 0;
+	TRACE_SETUP (request);
+	RETURN_EINVAL_IF (usbd_recv_setup_irq (usbd_bus->ep0, request));         // fail if already failed
+        RETURN_ZERO_IF ( (request->bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_DEVICE2HOST);      
+        RETURN_ZERO_IF (!le16_to_cpu (request->wLength));
+        RETURN_EINVAL_IF (!endpoint->rcv_urb);
+        return 0;
+}
+
+/* bi_ep0_reset_irq - reset ep0 endpoint 
+ */
+static void __inline__ bi_ep0_reset_endpoint_irq (struct usb_endpoint_instance *endpoint)
+{
+        bi_tx_cancelled_irq (endpoint);
+        bi_rcv_cancelled_irq (endpoint);
+        endpoint->sent = endpoint->last = 0;
+}
diff -Nru a/drivers/usbd/usbd-bops.c b/drivers/usbd/usbd-bops.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/usbd-bops.c	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,889 @@
+/*
+ * usbd/usbd-bops.c - USB Device Prototype
+ *
+ *      Copyright (c) 2004 Belcarra
+ *
+ * Adapted from earlier work:
+ *      Copyright (c) 2002, 2003 Belcarra
+ *      Copyright (c) 2000, 2001, 2002 Lineo
+ *      Copyright (c) 2001 Hewlett Packard
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <asm/uaccess.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/smp_lock.h>
+#include <linux/ctype.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+
+#include "usbd-chap9.h"
+#include "usbd-mem.h"
+#include "usbd.h"
+#include "usbd-func.h"
+#include "usbd-bus.h"
+
+/* Private defs from usbd-func.c *************************************************************** */
+/* Function driver enable/disable
+ *
+ * Called by usbd_enable_function/usbd_disable_function to call the selected
+ * function drivers function_enable or function_disable function.
+ */
+int usbd_function_enable (struct usb_bus_instance *, struct usb_function_instance *);
+void usbd_function_disable(struct usb_function_instance *);
+void usbd_func_event_irq(struct usb_bus_instance *, struct usb_function_instance *, usb_device_event_t , int );
+int usbd_strings_init (void);
+void usbd_strings_exit(void);
+void usbd_urb_callback (struct urb *urb, int rc);
+
+/* List support functions ******************************************************************** */
+
+/*
+ * Structure member address manipulation macros.
+ * These are used by client code (code using the urb_link routines), since
+ * the urb_link structure is embedded in the client data structures.
+ *
+ * Note: a macro offsetof equivalent to member_offset is defined in stddef.h
+ *       but this is kept here for the sake of portability.
+ *
+ * p2surround returns a pointer to the surrounding structure given
+ * type of the surrounding structure, the name memb of the structure
+ * member pointed at by ptr.  For example, if you have:
+ * 
+ *      struct foo {
+ *          int x;
+ *          float y;
+ *          char z;
+ *      } thingy;
+ *  
+ *      char *cp = &thingy.z;
+ *
+ * then
+ *      &thingy == p2surround(struct foo, z, cp)
+ */
+
+#define _cv_(ptr)                 ((char*)(void*)(ptr))
+#define member_offset(type,memb)  (_cv_(&(((type*)0)->memb))-(char*)0)
+#define p2surround(type,memb,ptr) ((type*)(void*)(_cv_(ptr)-member_offset(type,memb)))
+
+static inline struct usb_function_driver *list_entry_function (const struct list_head *le)
+{
+	return list_entry (le, struct usb_function_driver, drivers);
+}
+
+
+/*
+ * Append an urb_link (or a whole list of
+ * urb_links) to the tail of another list
+ * of urb_links.
+ */
+static __inline__ void urb_append_irq (urb_link * hd, struct urb *urb)
+{
+	if (hd && urb) {
+		urb_link *new = &urb->link;
+
+#ifdef C2L_SINGLETON_ONLY
+		// This _assumes_ the new urb is a singleton,
+		// but allows it to have an uninitialized link.
+		//printk(KERN_DEBUG"urb_append: hd: %p n:%p p:%p new: %p n:%p p:%p\n", hd, hd->next, hd->prev, new, new->next, new->prev);
+
+		new->prev = hd->prev;
+		new->next = hd;
+
+		hd->prev->next = new;
+		hd->prev = new;
+#else
+		// This allows the new urb to be a list of urbs,
+		// with new pointing at the first, but the link
+		// must be initialized.
+		// Order is important here...
+		urb_link *pul = hd->prev;
+		new->prev->next = hd;
+		hd->prev = new->prev;
+		new->prev = pul;
+		pul->next = new;
+#endif
+	}
+}
+
+/*
+ * Return the first urb_link in a list with a distinguished
+ * head "hd", or NULL if the list is empty.  This will also
+ * work as a predicate, returning NULL if empty, and non-NULL
+ * otherwise.
+ *
+ * Called from interrupt.
+ */
+static __inline__ urb_link *first_urb_link_irq (urb_link * hd)
+{
+	urb_link *nx;
+        return (!hd || !(nx = hd->next) || (nx == hd)) ? NULL : nx;
+}
+
+/*
+ * Return the first urb in a list with a distinguished
+ * head "hd", or NULL if the list is empty.
+ *
+ * Called from interrupt.
+ */
+static __inline__ struct urb *first_urb_irq (urb_link * hd)
+{
+	urb_link *nx;
+	return (!(nx = first_urb_link_irq (hd))) ? NULL : p2surround (struct urb, link, nx);
+}
+
+/*
+ * Detach and return the first urb in a list with a distinguished
+ * head "hd", or NULL if the list is empty.
+ */
+struct urb *usbd_first_urb_detached_irq (urb_link * hd)
+{
+	struct urb *urb;
+	urb_link *ul;
+	RETURN_NULL_IF (!(urb = first_urb_irq (hd)));
+	ul = &urb->link;
+	ul->next->prev = ul->prev;
+	ul->prev->next = ul->next;
+	ul->prev = ul->next = ul;
+	return urb;
+}
+
+static __inline__ void urb_append(urb_link *hd, struct urb *urb)
+{
+	unsigned long flags;
+	local_irq_save (flags);
+	urb_append_irq(hd, urb);
+	local_irq_restore (flags);
+}
+
+/* usbd_urb_sent_finished_irq - tell function that an urb has been transmitted.
+ *
+ * Must be called from an interrupt or with interrupts disabled.
+ *
+ * Used by a USB Bus driver to pass a sent urb back to the function 
+ * driver via the endpoints done queue.
+ */
+void usbd_urb_sent_finished_irq (struct urb *urb, int rc)
+{
+        //printk(KERN_INFO"%s: urb: %p\n", __FUNCTION__, urb);
+        urb->status = rc;
+        urb->jiffies = jiffies;
+        if (USBD_OK != urb->bus->status) 
+                usbd_urb_callback(urb, urb->status);
+        else {
+                urb_append_irq (&(urb->endpoint->done), urb);
+                RETURN_IF ( urb->bus->device_bh.sync);
+                queue_task (&urb->bus->device_bh, &tq_immediate);
+                mark_bh (IMMEDIATE_BH);
+        }
+}
+
+
+/* usbd_urb_recv_finished_irq - tell function that an urb has been received.
+ *
+ * Must be called from an interrupt or with interrupts disabled.
+ *
+ * Used by a USB Bus driver to pass a sent urb back to the function 
+ * driver via the endpoints done queue.
+ */
+void usbd_urb_recv_finished_irq (struct urb *urb, int rc)
+{
+        urb->status = rc;
+        urb->jiffies = jiffies;
+        if (USBD_OK != urb->bus->status) 
+                usbd_urb_callback (urb, rc);
+        else {
+                urb_append_irq (&(urb->endpoint->rcv), urb);
+                RETURN_IF (urb->bus->device_bh.sync);
+                queue_task (&urb->bus->device_bh, &tq_immediate);
+                mark_bh (IMMEDIATE_BH);
+        }
+}
+
+
+/* first_urb_detached -
+ *
+ * Detach and return the first urb in a list with a distinguished
+ * head "hd", or NULL if the list is empty.
+ *
+ */
+static __inline__ struct urb *first_urb_detached (urb_link * hd)
+{
+	struct urb *urb;
+	unsigned long flags;
+	local_irq_save (flags);
+	urb = usbd_first_urb_detached_irq (hd);
+	local_irq_restore (flags);
+	return urb;
+}
+
+
+/* Private defs from usbd-fops.c *************************************************************** */
+
+
+extern struct usb_function_driver ep0_driver;
+extern struct list_head usbd_function_drivers;
+extern struct usb_bus_instance *usbd_bus_instance;
+
+#define LANGID_ENGLISH          "\011"
+#define LANGID_US_ENGLISH       "\004"
+#define LANGIDs  LANGID_US_ENGLISH LANGID_ENGLISH
+
+/* usbd_flush_endpoint_irq - flush urbs from endpoint
+ *
+ * Iterate across the approrpiate tx or rcv list and cancel any outstanding urbs.
+ */
+void usbd_flush_endpoint_irq (struct usb_endpoint_instance *endpoint)
+{
+	struct urb *urb;
+        //printk(KERN_INFO"%s: bEndpointAddress: %d\n", __FUNCTION__, endpoint->bEndpointAddress);
+        if ((urb = endpoint->tx_urb)) 
+                usbd_cancel_urb_irq(urb);
+
+        for (; (urb = usbd_first_urb_detached_irq (&endpoint->tx)); usbd_cancel_urb_irq(urb));
+
+        if ((urb = endpoint->rcv_urb)) 
+                usbd_cancel_urb_irq(urb);
+
+        for (; (urb = usbd_first_urb_detached_irq (&endpoint->rdy)); usbd_cancel_urb_irq(urb));
+}
+
+/* usbd_flush_endpoint - flush urbs from endpoint
+ *
+ * Iterate across the approrpiate tx or rcv list and cancel any outstanding urbs.
+ */
+void usbd_flush_endpoint (struct usb_endpoint_instance *endpoint)
+{
+        unsigned long flags;
+        local_irq_save (flags);
+        usbd_flush_endpoint_irq(endpoint);
+        local_irq_restore (flags);
+}
+
+/* usbd_endpoint_halted
+ *
+ * Return non-zero if endpoint is halted.
+ */
+int usbd_endpoint_halted (struct usb_function_instance *function, int endpoint)
+{
+        //printk(KERN_INFO"%s:\n", __FUNCTION__);
+        return function->bus->driver->bops->endpoint_halted (function->bus, endpoint);
+}
+
+
+/* usbd_device_feature - set usb device feature
+ *
+ * Return non-zero if error
+ */
+int usbd_device_feature (struct usb_function_instance *function, int endpoint, int feature)
+{
+        //printk(KERN_INFO"%s:\n", __FUNCTION__);
+        return function->bus->driver->bops->device_feature (function->bus, endpoint, feature);
+}
+
+/* usbd_cancel_urb_irq - cancel an urb being sent
+ *
+ * Return non-zero if error
+ */
+int usbd_cancel_urb_irq (struct urb *urb)
+{
+        //printk(KERN_INFO"%s: urb: %p\n", __FUNCTION__, urb);
+        // XXX should we do usbd_dealloc_urb(urb);
+        return urb->bus->driver->bops->cancel_urb_irq (urb);
+}
+
+/* usbd_send_urb - submit a urb to send
+ *
+ * Used by a USB Function driver to submit data to be sent in an urb to the
+ * appropriate USB Bus driver via the endpoints transmit queue.
+ *
+ * Return non-zero if error
+ */
+int usbd_send_urb (struct urb *urb)
+{
+        //printk(KERN_INFO"%s: urb: %p length: %d\n", __FUNCTION__, urb, urb->actual_length);
+	//RETURN_EINVAL_IF (USBD_OK != urb->bus->status);
+	RETURN_EINVAL_IF (urb->endpoint->bEndpointAddress && (USBD_OK != urb->bus->status));
+	urb->status = SEND_IN_QUEUE;
+	urb->jiffies = jiffies;
+	urb_append (&(urb->endpoint->tx), urb);
+        return urb->bus->driver->bops->start_endpoint_in(urb->bus, urb->endpoint);
+}
+
+/* usbd_start_recv - recycle a received urb
+ *
+ * Used by a USB Function interface driver to recycle an urb.
+ *
+ * Return non-zero if error
+ */
+int usbd_start_recv (struct urb *urb)
+{
+        //printk(KERN_INFO"%s: urb: %p\n", __FUNCTION__, urb);
+	RETURN_EINVAL_IF (urb->endpoint->bEndpointAddress && (USBD_OK != urb->bus->status));
+	urb->actual_length = 0;
+	urb->status = RECV_IN_QUEUE;
+	urb->jiffies = jiffies;
+	urb_append (&(urb->endpoint->rdy), urb);
+        urb->bus->driver->bops->start_endpoint_out(urb->bus, urb->endpoint);
+        //printk(KERN_INFO"%s: finis\n", __FUNCTION__, urb);
+        return 0;
+}
+
+/* usbd_alloc_string_zero - allocate a string descriptor and return index number
+ *
+ * Find an empty slot in index string array, create a corresponding descriptor
+ * and return the slot number.
+ */
+__u8 usbd_alloc_string_zero (char *str)
+{
+	__u8 bLength;
+	__u16 *wData;
+	struct usb_string_descriptor *string;
+
+        RETURN_ZERO_IF(usb_strings[0] != NULL);
+
+        bLength = sizeof (struct usb_string_descriptor) + strlen (str);
+
+        RETURN_ZERO_IF(!(string = ckmalloc (bLength, GFP_KERNEL)));
+
+        string->bLength = bLength;
+        string->bDescriptorType = USB_DT_STRING;
+
+        for (wData = string->wData; *str; str += 2) {
+                *wData = (__u16) ((str[0] << 8 | str[1]));
+                wData++;
+        }
+        usb_strings[0] = string; // store in string index array
+        return 0;
+}
+
+
+int usbd_strings_init (void)
+{
+        //printk(KERN_INFO"%s: usb_strings: %p\n", __FUNCTION__, usb_strings);
+	RETURN_ZERO_IF(usb_strings);
+        usbd_maxstrings = MIN(usbd_maxstrings, 254);
+	
+	RETURN_EINVAL_IF (!(usb_strings = ckmalloc (sizeof (struct usb_string_descriptor *) * usbd_maxstrings, GFP_KERNEL)));
+	if (usbd_alloc_string_zero (LANGIDs) != 0) {
+		lkfree (usb_strings);
+		return -1;
+	}
+	return 0;
+}
+
+void usbd_strings_exit(void)
+{
+        int i;
+        //printk(KERN_INFO"%s: usb_strings: %p\n", __FUNCTION__, usb_strings);
+        RETURN_IF (!usb_strings);
+        for (i = 0; i < usbd_maxstrings; i++) 
+                usbd_dealloc_string(i);
+        lkfree (usb_strings);
+        usb_strings = NULL;
+}
+
+static usb_device_state_t event_states[DEVICE_CLOSE] = {
+        STATE_UNKNOWN, STATE_INIT, STATE_ATTACHED, STATE_POWERED,
+        STATE_DEFAULT, STATE_ADDRESSED, STATE_CONFIGURED, STATE_UNKNOWN,
+        STATE_UNKNOWN, STATE_UNKNOWN, STATE_ADDRESSED, STATE_SUSPENDED,
+        STATE_UNKNOWN, STATE_POWERED, STATE_ATTACHED, 
+};
+
+static usb_device_status_t event_status[DEVICE_CLOSE+1] = {
+        USBD_UNKNOWN,   // DEVICE_UNKNOWN
+        USBD_OPENING,   // DEVICE_INIT
+        USBD_OPENING,   // DEVICE_CREATE
+        USBD_OPENING,   // DEVICE_HUB_CONFIGURED
+        USBD_RESETING,  // DEVICE_RESET
+        USBD_OK,        // DEVICE_ADDRESS_ASSIGNED
+        USBD_OK,        // DEVICE_CONFIGURED
+        USBD_OK,        // DEVICE_SET_INTERFACE
+        USBD_OK,        // DEVICE_SET_FEATURE
+        USBD_OK,        // DEVICE_CLEAR_FEATURE
+        USBD_OK,        // DEVICE_DE_CONFIGURED
+        USBD_SUSPENDED, // DEVICE_BUS_INACTIVE
+        USBD_OK,        // DEVICE_BUS_ACTIVITY
+        USBD_RESETING,  // DEVICE_POWER_INTERRUPTION
+        USBD_RESETING,  // DEVICE_HUB_RESET
+        USBD_CLOSING,   // DEVICE_DESTROY
+        USBD_CLOSED,    // DEVICE_CLOSE
+};
+
+/* usbd_device_event_irq - called to respond to various usb events
+ *
+ * Used by a Bus driver to indicate an event.
+ */
+void usbd_bus_event_irq (struct usb_bus_instance *bus, usb_device_event_t event, int data)
+{
+        RETURN_IF(!bus);
+
+        //printk(KERN_INFO"%s:\n", __FUNCTION__);
+        //printk(KERN_INFO"%s: --> event: %d status: %d state: %d\n", __FUNCTION__, event, bus->status, bus->device_state);
+	switch (event) {
+	case DEVICE_BUS_INACTIVE:
+                bus->suspended_state = bus->device_state;
+                //printk(KERN_INFO"%s: INACTIVE\n", __FUNCTION__);
+                /* FALL THROUGH */
+        default:
+                bus->device_state = event_states[event];
+                //printk(KERN_INFO"%s: DEFAUL\n", __FUNCTION__);
+                break;
+	case DEVICE_UNKNOWN:
+                //printk(KERN_INFO"%s: UNKNOWN\n", __FUNCTION__);
+		break;
+	case DEVICE_BUS_ACTIVITY:
+                bus->device_state = bus->suspended_state;
+                //printk(KERN_INFO"%s: ACTIVITY\n", __FUNCTION__);
+		break;
+
+	case DEVICE_SET_INTERFACE:
+	case DEVICE_SET_FEATURE:
+	case DEVICE_CLEAR_FEATURE:
+                //printk(KERN_INFO"%s: SET\n", __FUNCTION__);
+		break;
+	}
+
+        switch (event) {
+        case DEVICE_BUS_ACTIVITY:
+        case DEVICE_BUS_INACTIVE:
+                BREAK_IF(USBD_CLOSING != bus->status);
+                /* FALL THROUGH */
+        default:
+                bus->status = event_status[event];
+                //printk(KERN_INFO"%s: DEFAULT\n", __FUNCTION__);
+                break;
+        }
+        //printk(KERN_INFO"%s: <-- event: %d status: %d state: %d\n", __FUNCTION__, event, bus->status, bus->device_state);
+
+        // if we lost configuration then get rid of alternate settings
+        if ((bus->device_state != STATE_CONFIGURED) && bus->bNumInterfaces && bus->alternates) {
+                bus->bNumInterfaces = 0;
+                lkfree(bus->alternates);
+                bus->alternates = NULL;
+        }
+        bus->driver->bops->device_event (bus, event, data);
+        usbd_func_event_irq (bus, bus->ep0, event, data);
+        usbd_func_event_irq (bus, bus->function_instance, event, data);
+}
+
+
+/* usbd_bus_event
+ */
+void usbd_bus_event (struct usb_bus_instance *bus, usb_device_event_t event, int data)
+{
+	unsigned long flags;
+	local_irq_save (flags);
+	usbd_bus_event_irq (bus, event, data);
+	local_irq_restore (flags);
+}
+
+
+/* usbd_attached - return cable status
+ *
+ * Return non-zero if cable attached
+ */
+int usbd_attached (struct usb_bus_instance *bus)
+{
+	return bus->driver->bops->bus_attached (bus);
+}
+
+/* usbd_connected - return pullup resistor control status
+ *  *
+ *   * Return non-zero if pullup enabled
+ *    */
+int usbd_connected (struct usb_bus_instance *bus)
+{
+        return bus->driver->bops->bus_connected (bus);
+}
+
+
+/* usbb bus bottom half ********************************************************************** */
+
+/*
+ * Note that all of the functions from here to the end of the file protect against 
+ * overlapped operation using the usbd_bus_sem
+ *
+ *      usbd_device_bh
+ *
+ *      usbd_register_bus
+ *      usbd_deregister_bus
+ *      usbd_enable_function_irq
+ *      usbd_disable_function
+ */
+DECLARE_MUTEX(usbd_bus_sem);
+
+
+/* usbd_device_bh - 
+ *
+ * Bottom half handler to process sent or received urbs.
+ */
+void usbd_device_bh (void *data)
+{
+        int i;
+        struct usb_bus_instance *bus = data;
+        struct usb_endpoint_instance *endpoint;
+
+        RETURN_IF (!bus || !(endpoint = bus->endpoint_array));
+        down(&usbd_bus_sem);
+        // process received and sent urbs
+        for (i = 0; i < bus->endpoints; i++, endpoint++) {
+                struct urb *urb;
+                for (; (urb = first_urb_detached (&endpoint->rcv )); usbd_urb_callback (urb, urb->status));
+                for (; (urb = first_urb_detached (&endpoint->done)); usbd_urb_callback (urb, urb->status));
+        }
+        if (USBD_CLOSING == bus->status) 
+                bus->device_bh.data = NULL;
+        up(&usbd_bus_sem);
+}
+
+
+/* usb-device USB BUS INTERFACE generic functions ******************************************** */
+
+/*
+ * Initialize an urb_link to be a single element list.
+ * If the urb_link is being used as a distinguished list head
+ * the list is empty when the head is the only link in the list.
+ */
+static __inline__ void urb_link_init (urb_link * ul)
+{
+        ul->prev = ul->next = ul;
+}
+
+/* usbd_register_bus - called by a USB BUS INTERFACE driver to register a bus driver
+ *
+ * Used by a USB Bus interface driver to register itself with the usb device layer.
+ *
+ * Return non-zero if error
+ */
+struct usb_bus_instance *usbd_register_bus (struct usb_bus_driver *driver)
+{
+        int i;
+        struct usb_bus_instance *bus = NULL;
+
+        //printk(KERN_INFO"%s: DOWN USBD_BUS_SEM\n", __FUNCTION__);
+        down(&usbd_bus_sem);
+	MOD_INC_USE_COUNT;  // QQQ should this be before the down()?
+
+        THROW_IF(usbd_bus_instance, error);
+        THROW_IF((bus = ckmalloc (sizeof (struct usb_bus_instance), GFP_ATOMIC)) == NULL, error);
+
+        bus->driver = driver;
+        bus->endpoints = bus->driver->max_endpoints;
+
+        THROW_IF(!(bus->endpoint_array = ckmalloc(sizeof (struct usb_endpoint_instance) * bus->endpoints, GFP_ATOMIC)), error); 
+
+        for (i = 0; i < bus->endpoints; i++) {
+                struct usb_endpoint_instance *endpoint = bus->endpoint_array + i;
+                endpoint->physical_endpoint = i;
+                urb_link_init (&endpoint->rcv);
+                urb_link_init (&endpoint->rdy);
+                urb_link_init (&endpoint->tx);
+                urb_link_init (&endpoint->done);
+        }
+
+        bus->admin[usbd_admin_enable] = driver->bops->bus_enable;
+        bus->admin[usbd_admin_disable] = driver->bops->bus_disable;
+        bus->admin[usbd_admin_disconnect] = driver->bops->bus_disconnect;
+        bus->admin[usbd_admin_connect] = driver->bops->bus_connect;
+        bus->admin[usbd_admin_pm_off] = driver->bops->bus_pm_off;
+        bus->admin[usbd_admin_pm_on] = driver->bops->bus_pm_on;
+        bus->admin[usbd_admin_serial_number] = driver->bops->bus_serial_number;
+
+	THROW_IF((bus->ep0 = ckmalloc (sizeof (struct usb_function_instance), GFP_ATOMIC)) == NULL, error)
+	bus->ep0->function_driver = &ep0_driver;
+        bus->ep0->endpointsRequested = 1;
+        THROW_IF(!(bus->ep0->endpoint_map_array = ckmalloc(sizeof(struct usb_endpoint_map) * 1, GFP_KERNEL)), error);
+
+	bus->device_state = STATE_CREATED;
+	bus->status = USBD_OPENING;
+
+        usbd_bus_instance = bus;
+
+        CATCH(error) {
+                if (bus) {
+                        if (bus->endpoint_array) 
+                                lkfree(bus->endpoint_array);
+                        lkfree(bus);
+                }
+                bus->endpoints = 0;
+                printk(KERN_INFO"%s: FAILED\n", __FUNCTION__);
+                bus =  NULL;
+		MOD_DEC_USE_COUNT;
+        }
+
+        up(&usbd_bus_sem);
+        //printk(KERN_INFO"%s: UP USBD_BUS_SEM\n", __FUNCTION__);
+	return bus;
+}
+
+/* usbd_deregister_bus - called by a USB BUS INTERFACE driver to deregister a bus driver
+ *
+ * Used by a USB Bus interface driver to de-register itself with the usb device
+ * layer.
+ */
+void usbd_deregister_bus (struct usb_bus_instance *bus)
+{
+        //printk(KERN_INFO"%s: DOWN USBD_BUS_SEM\n", __FUNCTION__);
+        down(&usbd_bus_sem);
+
+        usbd_bus_instance = NULL;
+
+        if (bus->ep0) {
+                if (bus->ep0->endpoint_map_array)
+                        lkfree(bus->ep0->endpoint_map_array);
+                lkfree(bus->ep0);
+        }
+
+        lkfree (bus->arg);
+	lkfree (bus->endpoint_array);
+	lkfree (bus);
+        bus->endpoints = 0;
+	MOD_DEC_USE_COUNT;  // QQQ should this be after the up()?
+        up(&usbd_bus_sem);
+        //printk(KERN_INFO"%s: UP USBD_BUS_SEM\n", __FUNCTION__);
+}
+
+
+/* usb-device USB Device generic functions *************************************************** */
+
+/* usbd_enable_function_irq - called to enable the desired function
+ *
+ * Used by a USB Bus interface driver to create a virtual device.
+ *
+ * Return non-zero if error
+ */
+int usbd_enable_function_irq (struct usb_bus_instance *bus, char *arg)
+{
+	struct usb_function_instance *function = NULL;
+	struct list_head *lhd;
+        int len = 0;
+        int i;
+        int rc = -EINVAL;
+        int epn;
+
+        //printk(KERN_INFO"%s: DOWN USBD_BUS_SEM\n", __FUNCTION__);
+        down(&usbd_bus_sem);
+
+        if (arg && bus->arg) 
+                lkfree(bus->arg);
+
+        if (arg) {
+                bus->arg = lstrdup(arg);
+                len = strlen(arg);
+        }
+
+	// initialize the strings pool
+	THROW_IF(usbd_strings_init (), error);
+
+        for (i = 1; i < bus->endpoints; i++) {
+                struct usb_endpoint_instance *endpoint = bus->endpoint_array + i;
+                endpoint->rcv_urb = endpoint->tx_urb = NULL;
+        }               
+
+	list_for_each (lhd, &usbd_function_drivers) {
+		struct usb_function_driver *function_driver;
+		function_driver = list_entry_function (lhd);
+
+                printk(KERN_INFO"%s: check: [%s] %s\n", __FUNCTION__,
+		       (arg?arg:""), function_driver->name);
+
+                // mode - single, either the first or a named function
+                CONTINUE_IF(arg && len && strncmp(function_driver->name, arg, len));
+
+                //printk(KERN_INFO"%s: found: %s\n", __FUNCTION__, function_driver->name);
+
+                THROW_IF (!(function = ckmalloc (sizeof (struct usb_function_instance), GFP_ATOMIC)), error);
+                function->function_driver = function_driver;  // XXX
+
+                THROW_IF (!(function->endpoint_map_array = ckmalloc (sizeof(struct usb_endpoint_map) * 
+                                                function->function_driver->device_description->endpointsRequested, 
+                                                GFP_KERNEL)), error);
+
+                THROW_IF(bus->driver->bops->request_endpoints( function->endpoint_map_array,
+                                        function->function_driver->device_description->endpointsRequested,
+                                        function->function_driver->device_description->requestedEndpoints), error);
+
+                function->endpointsRequested = function->function_driver->device_description->endpointsRequested;
+                usbd_function_enable (bus, function);
+                break;
+	}
+
+        if (NULL == function && NULL != arg && 0 != len) {
+                printk(KERN_INFO"Unknown function driver (len=%d) [%s], known drivers:\n",len,arg);
+                list_for_each (lhd, &usbd_function_drivers) {
+                        struct usb_function_driver *function_driver = list_entry_function (lhd);
+                        printk(KERN_INFO"   [%s]\n", function_driver->name);
+                }
+        }
+
+        THROW_IF (!(bus->function_instance = function), error);
+
+        THROW_IF(bus->driver->bops->set_endpoints( function->function_driver->device_description->endpointsRequested,
+                                        function->endpoint_map_array), error);
+	// device bottom half
+	bus->device_bh.routine = usbd_device_bh;
+	bus->device_bh.data = bus;
+	bus->status = USBD_OK;
+	bus->bus_state = usbd_bus_state_enabled;
+        bus->ep0->endpoint_map_array->endpoint = bus->endpoint_array;
+        THROW_IF (usbd_function_enable (bus, bus->ep0), error);
+
+        //printk(KERN_INFO"%s: %d endpoint: %p map: %p\n", __FUNCTION__, 0,
+        //                bus->ep0->endpoint_map_array->endpoint, 
+        //                bus->ep0->endpoint_map_array);
+
+        // iterate across the logical endpoint map to copy appropriate information 
+        // into the physical endpoint instance array
+
+        for (epn = 0; epn < bus->function_instance->endpointsRequested; epn++) {
+
+                struct usb_endpoint_map *endpoint_map = bus->function_instance->endpoint_map_array + epn;
+                int physicalEndpoint = endpoint_map->physicalEndpoint[0];
+                struct usb_endpoint_instance *endpoint = bus->endpoint_array + physicalEndpoint;
+
+                //printk(KERN_INFO"%s: %d endpoint: %p map: %p bEndpointAddress: %02x\n", __FUNCTION__, 
+                //                epn, endpoint, endpoint_map, endpoint_map->bEndpointAddress[0]);
+
+                endpoint_map->endpoint = endpoint;
+                endpoint->bEndpointAddress = endpoint_map->bEndpointAddress[0];
+                endpoint->bmAttributes = endpoint_map->bmAttributes[0];
+
+                switch(endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+                case USB_DIR_IN:
+                        endpoint->wMaxPacketSize = endpoint_map->wMaxPacketSize[0];
+                        endpoint->last = 0;
+                        endpoint->tx_urb = NULL;
+                        break;
+
+                case USB_DIR_OUT:
+                        endpoint->rcv_transferSize = endpoint_map->transferSize[0];
+                        endpoint->wMaxPacketSize = endpoint_map->wMaxPacketSize[0];
+                        endpoint->rcv_urb = NULL;
+                        break;
+                }
+        }
+
+        rc = 0;
+
+        CATCH(error) {
+                printk(KERN_INFO"%s: FAILED\n", __FUNCTION__);
+                usbd_strings_exit();
+        }
+        up(&usbd_bus_sem);
+        //printk(KERN_INFO"%s: UP USBD_BUS_SEM\n", __FUNCTION__);
+        return rc;
+}
+
+/* usbd_disable_function - called to disable the current function
+ *
+ * Used by a USB Bus interface driver to destroy a virtual device.
+ */
+void usbd_disable_function (struct usb_bus_instance *bus)
+{
+        printk(KERN_INFO"%s: DOWN USBD_BUS_SEM\n", __FUNCTION__);
+        down(&usbd_bus_sem);
+	// prevent any more bottom half scheduling
+        bus->status = USBD_CLOSING;
+        up(&usbd_bus_sem);
+        //printk(KERN_INFO"%s: UP USBD_BUS_SEM\n", __FUNCTION__);
+
+	// wait for pending device bottom half to finish
+	while (bus->device_bh.data /*|| device->function_bh.data */) {
+
+                //printk(KERN_INFO"%s: waiting for usbd_device_bh %ld %p interrupt: %d\n", __FUNCTION__, 
+                //                bus->device_bh.sync, bus->device_bh.data, in_interrupt());
+
+                // This can probably be either, but for consistency's sake...
+                queue_task(&bus->device_bh, &tq_immediate);
+                mark_bh (IMMEDIATE_BH);
+                // schedule_task(&device->device_bh);
+
+		schedule_timeout (2000 * HZ);
+	}
+
+        //printk(KERN_INFO"%s: DOWN USBD_BUS_SEM\n", __FUNCTION__);
+        down(&usbd_bus_sem);
+
+	// tell the function driver to close
+	usbd_function_disable (bus->ep0);
+	usbd_function_disable (bus->function_instance);
+
+        // free alternates memory
+        if (/*bus->bNumInterfaces &&*/ bus->alternates) {
+                bus->bNumInterfaces = 0;
+                lkfree(bus->alternates);
+                bus->alternates = NULL;
+        }
+        if (bus->function_instance) {
+                if (bus->function_instance->endpoint_map_array) 
+                        lkfree(bus->function_instance->endpoint_map_array);
+                lkfree(bus->function_instance);
+                bus->function_instance = NULL;
+        }
+        usbd_strings_exit();
+        bus->status = USBD_CLOSED;
+        up(&usbd_bus_sem);
+        printk(KERN_INFO"%s: UP USBD_BUS_SEM\n", __FUNCTION__);
+}
+
+usb_device_state_t usbd_device_state(struct usb_function_instance *function)
+{
+        //printk(KERN_INFO"%s: %d\n", __FUNCTION__, function->bus->device_state);
+        return (function && function->bus) ? function->bus->device_state : STATE_UNKNOWN;
+}
+usb_device_state_t usbd_bus_state(struct usb_function_instance *function)
+{
+        //printk(KERN_INFO"%s: %d\n", __FUNCTION__, function->bus->bus_state);
+        return (function && function->bus) ? function->bus->bus_state : usbd_bus_state_unknown;
+}
+usb_device_status_t usbd_bus_status(struct usb_function_instance *function)
+{
+        //printk(KERN_INFO"%s: %d\n", __FUNCTION__, function->bus->status);
+        return (function && function->bus) ?  function->bus->status : USBD_UNKNOWN;
+}
+
+EXPORT_SYMBOL(usbd_register_bus);
+EXPORT_SYMBOL(usbd_deregister_bus);
+EXPORT_SYMBOL(usbd_enable_function_irq);
+EXPORT_SYMBOL(usbd_disable_function);
+EXPORT_SYMBOL(usbd_attached);
+EXPORT_SYMBOL(usbd_send_urb);
+EXPORT_SYMBOL(usbd_start_recv);
+EXPORT_SYMBOL(usbd_flush_endpoint);
+EXPORT_SYMBOL(usbd_cancel_urb_irq);
+EXPORT_SYMBOL(usbd_bus_event_irq);
+EXPORT_SYMBOL(usbd_bus_event);
+EXPORT_SYMBOL(usbd_urb_sent_finished_irq);
+EXPORT_SYMBOL(usbd_urb_recv_finished_irq);
+EXPORT_SYMBOL(usbd_device_state);
+EXPORT_SYMBOL(usbd_bus_state);
+EXPORT_SYMBOL(usbd_bus_status);
+EXPORT_SYMBOL(usbd_first_urb_detached_irq);
+
diff -Nru a/drivers/usbd/usbd-build.h b/drivers/usbd/usbd-build.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/usbd-build.h	Fri Feb 27 14:22:51 2004
@@ -0,0 +1 @@
+#define USBD_BUILD "000"
diff -Nru a/drivers/usbd/usbd-bus.h b/drivers/usbd/usbd-bus.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/usbd-bus.h	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,316 @@
+/*
+ * usbd/usbd-bus.c - USB Device Bus Interface Driver Interface
+ *
+ *      Copyright (c) 2004 Belcarra
+ *
+ * Adapted from earlier work:
+ *      Copyright (c) 2002, 2003 Belcarra
+ *      Copyright (c) 2000, 2001, 2002 Lineo
+ *      Copyright (c) 2001 Hewlett Packard
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * This file contains the USB Bus Interface Driver Interface definitions.
+ *
+ * This is the interface between the bottom of the USB Core and the top of
+ * the Bus Interace Drivers and is comprised of:
+ *
+ * 	o public functions exported by the USB Core layer 
+ *
+ * 	o structures and functions passed by the Function Driver to the USB
+ * 	Core layer
+ *
+ * USB Bus Interface Drivers are structured such that the upper edge
+ * implements interfaces to the USB Core layer to provide low level USB
+ * services and the lower edge interfaces to the actual USB Hardware
+ * (typically called the USB Device Controller or UDC.)
+ *
+ */
+
+struct usb_endpoint_map; 
+
+/* Operations that the slave layer or function driver can use to interact
+ * with the bus interface driver. 
+ *
+ * The send_urb() function is used by the usb-device endpoint 0 driver and
+ * function drivers to submit data to be sent.
+ *
+ * The cancel_urb() function is used by the usb-device endpoint 0 driver and
+ * function drivers to remove previously queued data to be sent.
+ *
+ * The endpoint_halted() function is used by the ep0 control function to
+ * check if an endpoint is halted.
+ *
+ * The device_feature() function is used by the ep0 control function to
+ * set/reset device features on an endpoint.
+ *
+ * The device_event() function is used by the usb device core to tell the
+ * bus interface driver about various events.
+ */
+struct usb_bus_operations {
+	int (*bus_enable) (struct usb_bus_instance *, char *);
+	int (*bus_disable) (struct usb_bus_instance *, char *);
+	int (*bus_disconnect) (struct usb_bus_instance *, char *);
+	int (*bus_connect) (struct usb_bus_instance *, char *);
+	int (*bus_pm_off) (struct usb_bus_instance *, char *);
+	int (*bus_pm_on) (struct usb_bus_instance *, char *);
+	int (*bus_serial_number) (struct usb_bus_instance *, char *);
+
+	int (*bus_attached) (struct usb_bus_instance *);
+        int (*bus_connected) (struct usb_bus_instance *);
+	int (*start_endpoint_in) (struct usb_bus_instance *, struct usb_endpoint_instance *);
+	int (*start_endpoint_out) (struct usb_bus_instance *, struct usb_endpoint_instance *);
+	int (*cancel_urb_irq) (struct urb *);
+	int (*endpoint_halted) (struct usb_bus_instance *, int);
+	int (*device_feature) (struct usb_bus_instance *, int, int);
+	int (*device_event) (struct usb_bus_instance *, usb_device_event_t, int);
+	int (*request_endpoints) (struct usb_endpoint_map *, int, struct usb_endpoint_request *);
+        int (*set_endpoints) (int , struct usb_endpoint_map *);
+};
+
+
+/* Endpoint Map
+ *
+ * An array of these structures is created by the bus interface driver to
+ * show what endpoints have been configured for the function driver.
+ */
+struct usb_endpoint_map {
+        u8 configuration;
+        u8 interface;
+        u8 alternate;
+	u8 bEndpointAddress[2];		// logical endpoint address
+	u16 wMaxPacketSize[2];		// packetsSize for requested endpoint
+	u8 bmAttributes[2];		// requested endpoint type
+	u16 transferSize[2];		// transferSize for bulk transfers
+	u8 physicalEndpoint[2];		// physical endpoint number
+	struct usb_endpoint_instance 	*endpoint;
+};
+
+
+/* Endpoint configuration
+ *
+ * Per endpoint configuration data. Used to track which function driver owns
+ * an endpoint.
+ *
+ */
+struct usb_endpoint_instance {
+	int 				bEndpointAddress;	// logical endpoint address 
+	int 				physical_endpoint;	// physical endpoint address - bus interface specific
+        int 				bmAttributes;       	// endpoint type
+	u16 				wMaxPacketSize;		// packet size for requested endpoint
+
+	// control
+        int 				status;             	// halted
+	int 				state;			// available for use by bus interface driver
+
+	// receive side
+	struct urb_link 		rcv;			// received urbs
+	struct urb_link 		rdy;			// empty urbs ready to receive
+	struct urb 			*rcv_urb;		// active urb
+	__u32 				rcv_transferSize;	// maximum transfer size from function driver
+	int 				rcv_error;		// current bulk-in has an error
+
+	// transmit side
+	struct urb_link 		tx;			// urbs ready to transmit
+	struct urb_link 		done;			// transmitted urbs
+	struct urb 			*tx_urb;		// active urb
+
+	__u32 				sent;			// data already sent
+	__u32 				last;			// data sent in last packet XXX do we need this
+};
+
+/* endpoint zero states
+ */
+#define WAIT_FOR_SETUP          0
+#define DATA_STATE_XMIT         1
+#define DATA_STATE_NEED_ZLP     2
+#define WAIT_FOR_OUT_STATUS     3
+#define DATA_STATE_RECV         4
+#define DATA_STATE_PENDING_XMIT 5
+
+
+/* Bus Interface data structure
+ *
+ * Keep track of specific bus interface. 
+ *
+ * This is passed to the usb-device layer when registering. It contains all
+ * required information about each real bus interface found such that the
+ * usb-device layer can create and maintain a usb-device structure.
+ *
+ * Note that bus interface registration is incumbent on finding specific
+ * actual real bus interfaces. There will be a registration for each such
+ * device found.
+ *
+ * The max_tx_endpoints and max_rx_endpoints are the maximum number of
+ * possible endpoints that this bus interface can support. The default
+ * endpoint 0 is not included in these counts.
+ *
+ */
+struct usb_bus_driver {
+	char				*name;
+	u8				max_endpoints;	// maximimum number of rx enpoints
+	u8				maxpacketsize;
+	u8				HighSpeedCapable;
+	struct usb_device_description	*device_description;
+	struct usb_bus_operations	*bops;
+};
+
+
+typedef enum usbd_admin {
+        usbd_admin_enable = 0,
+        usbd_admin_disable = 1,
+        usbd_admin_connect = 2,
+        usbd_admin_disconnect = 3,
+        usbd_admin_pm_off = 4,
+        usbd_admin_pm_on = 5,
+        usbd_admin_serial_number = 6
+} usbd_admin_t;
+
+typedef int (*usbd_admin_proc_t) (struct usb_bus_instance *, char *);
+
+
+/* Function configuration structure
+ *
+ * This is allocated for each configured instance of a function driver.
+ *
+ * It stores pointers to the usb_function_driver for the appropriate function,
+ * and pointers to the USB HOST requested usb_configuration_description and
+ * usb_interface_description.
+ *
+ * The privdata pointer may be used by the function driver to store private
+ * per instance state information.
+ *
+ */
+struct usb_function_instance {
+	struct usb_bus_instance 	*bus;
+	struct usb_function_driver 	*function_driver;
+	void 				*privdata;		// private data for the function
+	__u8 				endpointsRequested;	// number of requested endpoints
+	struct usb_endpoint_map 	*endpoint_map_array;	// map of endpoints requested by function driver
+};
+
+
+/* Bus Interface configuration structure
+ *
+ * This is allocated for each configured instance of a bus interface driver.
+ *
+ * It contains a pointer to the appropriate bus interface driver.
+ *
+ * The privdata pointer may be used by the bus interface driver to store private
+ * per instance state information.
+ */
+struct usb_bus_instance {
+
+	struct usb_bus_driver           *driver;
+
+        usbd_admin_proc_t               admin [8];
+
+        int                             endpoints;
+	struct usb_endpoint_instance    *endpoint_array;	// array of available configured endpoints
+
+	char                            *serial_number_str;
+
+	usb_device_status_t             status;	                // device status
+        usbd_bus_state_t                bus_state;
+	usb_device_state_t              device_state;		// current USB Device state
+	usb_device_state_t              suspended_state;	// previous USB Device state
+
+	struct usb_function_instance    *ep0;	                // ep0 configuration
+	struct usb_function_instance    *function_instance;
+
+
+        u8                              HighSpeedFlag;
+	u8                              ConfigurationValue;	// current set configuration (zero is default)
+        u8                              bNumInterfaces;        	// number of interfaces in the current configuration
+        u8                              *alternates;         	// array[0..interfaces-1] of alternate settings for each interface
+
+	struct tq_struct                device_bh;		// runs as bottom half, equivalent to interrupt time
+
+	void                            *privdata;		// private data for the bus interface
+	char				*arg;
+};
+
+/* bus driver registration
+ *
+ * Called by bus interface drivers to register themselves when loaded
+ * or de-register when unloading.
+ */
+struct usb_bus_instance *usbd_register_bus (struct usb_bus_driver *);
+void usbd_deregister_bus (struct usb_bus_instance *);
+
+
+
+/* Enable/Disable Function
+ *
+ * Called by a bus interface driver to select and enable a specific function 
+ * driver.
+ */
+int usbd_enable_function_irq (struct usb_bus_instance *, char *);
+void usbd_disable_function (struct usb_bus_instance *);
+
+
+void usbd_flush_endpoint_irq (struct usb_endpoint_instance *);
+void usbd_flush_endpoint (struct usb_endpoint_instance *);
+
+/* 
+ * usbd_configure_device is used by function drivers (usually the control endpoint)
+ * to change the device configuration.
+ *
+ * usbd_device_event is used by bus interface drivers to tell the higher layers that
+ * certain events have taken place.
+ */
+void usbd_bus_event_irq (struct usb_bus_instance *, usb_device_event_t, int);
+void usbd_bus_event (struct usb_bus_instance *, usb_device_event_t, int);
+
+
+/**
+ * usbd_recv_setup_irq - process a received urb
+ * @urb: pointer to an urb structure
+ *
+ * Used by a USB Bus interface driver to pass received data in a URB to the
+ * appropriate USB Function driver.
+ *
+ * This function must return 0 for success and -EINVAL if the request
+ * is to be stalled.
+ *
+ * Not that if the SETUP is Host to Device with a non-zero wLength then there
+ * *MUST* be a valid receive urb queued OR the request must be stalled.
+ */
+int usbd_recv_setup_irq (struct usb_function_instance*, struct usb_device_request *);
+
+
+void usbd_urb_sent_finished_irq (struct urb *, int );
+void usbd_urb_recv_finished_irq (struct urb *, int );
+
+
+/*
+ * Detach and return the first urb in a list with a distinguished
+ * head "hd", or NULL if the list is empty.
+ */
+struct urb *usbd_first_urb_detached_irq (urb_link * hd);
+
+/*
+ * Get a descriptor
+ */
+int usbd_get_descriptor (struct usb_bus_instance *bus, u8 *buffer, int max, int descriptor_type, int index);
+
diff -Nru a/drivers/usbd/usbd-chap9.h b/drivers/usbd/usbd-chap9.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/usbd-chap9.h	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,1054 @@
+/*
+ * usbd/usbd-chap9.h 
+ *
+ *      Copyright (c) 2004 Belcarra
+ *
+ * Adapted from earlier work:
+ *      Copyright (c) 2002, 2003 Belcarra
+ *      Copyright (c) 2000, 2001, 2002 Lineo
+ *      Copyright (c) 2001 Hewlett Packard
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+/*
+ * USB Descriptors are used to build a configuration database for each USB
+ * Function driver.
+ *
+ */
+
+
+/*
+ * Device and/or Interface Class codes
+ */
+#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_PRINTER               7
+#define USB_CLASS_MASS_STORAGE          8
+#define USB_CLASS_HUB                   9
+#define USB_CLASS_DATA                  10
+#define USB_CLASS_APP_SPEC              0xfe
+#define USB_CLASS_VENDOR_SPEC           0xff
+
+/*
+ * USB Device Request Types (bmRequestType)
+ * C.f. USB 2.0 Table 9-2
+ */
+#define USB_TYPE_STANDARD               (0x00 << 5)
+#define USB_TYPE_CLASS                  (0x01 << 5)
+#define USB_TYPE_VENDOR                 (0x02 << 5)
+#define USB_TYPE_RESERVED               (0x03 << 5)
+
+#define USB_RECIP_DEVICE                0x00
+#define USB_RECIP_INTERFACE             0x01
+#define USB_RECIP_ENDPOINT              0x02
+#define USB_RECIP_OTHER                 0x03
+
+#define USB_DIR_OUT                     0
+#define USB_DIR_IN                      0x80
+#define	USB_ENDPOINT_OPT		0x40
+
+
+
+/*
+ * Descriptor types
+ * C.f. USB 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_HID                      (USB_TYPE_CLASS | 0x01)
+#define USB_DT_REPORT                   (USB_TYPE_CLASS | 0x02)
+#define USB_DT_PHYSICAL                 (USB_TYPE_CLASS | 0x03)
+#define USB_DT_HUB                      (USB_TYPE_CLASS | 0x09)
+
+/*
+ * Descriptor sizes per descriptor type
+ */
+#define USB_DT_DEVICE_SIZE              18
+#define USB_DT_CONFIG_SIZE              9
+#define USB_DT_INTERFACE_SIZE           9
+#define USB_DT_ENDPOINT_SIZE            7
+#define USB_DT_ENDPOINT_AUDIO_SIZE      9	/* Audio extension */
+#define USB_DT_HUB_NONVAR_SIZE          7
+#define USB_DT_HID_SIZE                 9
+
+/*
+ * Endpoints
+ */
+#define USB_ENDPOINT_NUMBER_MASK        0x0f	/* in bEndpointAddress */
+#define USB_ENDPOINT_DIR_MASK           0x80
+
+#define USB_ENDPOINT_MASK               0x03	/* in bmAttributes */
+#define USB_ENDPOINT_CONTROL            0x00
+#define USB_ENDPOINT_ISOCHRONOUS        0x01
+#define USB_ENDPOINT_BULK               0x02
+#define USB_ENDPOINT_INTERRUPT          0x03
+
+/*
+ * USB Packet IDs (PIDs)
+ */
+#define USB_PID_UNDEF_0                        0xf0
+#define USB_PID_OUT                            0xe1
+#define USB_PID_ACK                            0xd2
+#define USB_PID_DATA0                          0xc3
+#define USB_PID_PING                           0xb4	/* USB 2.0 */
+#define USB_PID_SOF                            0xa5
+#define USB_PID_NYET                           0x96	/* USB 2.0 */
+#define USB_PID_DATA2                          0x87	/* USB 2.0 */
+#define USB_PID_SPLIT                          0x78	/* USB 2.0 */
+#define USB_PID_IN                             0x69
+#define USB_PID_NAK                            0x5a
+#define USB_PID_DATA1                          0x4b
+#define USB_PID_PREAMBLE                       0x3c	/* Token mode */
+#define USB_PID_ERR                            0x3c	/* USB 2.0: handshake mode */
+#define USB_PID_SETUP                          0x2d
+#define USB_PID_STALL                          0x1e
+#define USB_PID_MDATA                          0x0f	/* USB 2.0 */
+
+/*
+ * Standard requests
+ */
+#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
+
+/*
+ * HID requests
+ */
+#define USB_REQ_GET_REPORT              0x01
+#define USB_REQ_GET_IDLE                0x02
+#define USB_REQ_GET_PROTOCOL            0x03
+#define USB_REQ_SET_REPORT              0x09
+#define USB_REQ_SET_IDLE                0x0A
+#define USB_REQ_SET_PROTOCOL            0x0B
+
+
+/*
+ * USB Spec Release number
+ */
+
+#define USB_BCD_VERSION                 0x0200
+
+/*
+ * Audio
+ */
+#define CS_AUDIO_UNDEFINED              0x20
+#define CS_AUDIO_DEVICE                 0x21
+#define CS_AUDIO_CONFIGURATION          0x22
+#define CS_AUDIO_STRING                 0x23
+#define CS_AUDIO_INTERFACE              0x24
+#define CS_AUDIO_ENDPOINT               0x25
+
+#define AUDIO_HEADER                    0x01
+#define AUDIO_INPUT_TERMINAL            0x02
+#define AUDIO_OUTPUT_TERMINAL           0x03
+#define AUDIO_MIXER_UNIT                0x04
+#define AUDIO_SELECTOR_UNIT             0x05
+#define AUDIO_FEATURE_UNIT              0x06
+#define AUDIO_PROCESSING_UNIT           0x07
+#define AUDIO_EXTENSION_UNIT            0x08
+
+
+/*
+ * Device Requests      (c.f Table 9-2)
+ */
+
+#define USB_REQ_DIRECTION_MASK          0x80
+#define USB_REQ_TYPE_MASK               0x60
+#define USB_REQ_RECIPIENT_MASK          0x1f
+
+#define USB_REQ_DEVICE2HOST             0x80
+#define USB_REQ_HOST2DEVICE             0x00
+
+#define USB_REQ_TYPE_STANDARD           0x00
+#define USB_REQ_TYPE_CLASS              0x20
+#define USB_REQ_TYPE_VENDOR             0x40
+
+#define USB_REQ_RECIPIENT_DEVICE        0x00
+#define USB_REQ_RECIPIENT_INTERFACE     0x01
+#define USB_REQ_RECIPIENT_ENDPOINT      0x02
+#define USB_REQ_RECIPIENT_OTHER         0x03
+
+/*
+ * get status bits
+ */
+
+#define USB_STATUS_SELFPOWERED          0x01
+#define USB_STATUS_REMOTEWAKEUP         0x02
+
+#define USB_STATUS_HALT                 0x01
+
+/* 
+ * descriptor types
+ */
+
+#define USB_DESCRIPTOR_TYPE_DEVICE                      0x01
+#define USB_DESCRIPTOR_TYPE_CONFIGURATION               0x02
+#define USB_DESCRIPTOR_TYPE_STRING                      0x03
+#define USB_DESCRIPTOR_TYPE_INTERFACE                   0x04
+#define USB_DESCRIPTOR_TYPE_ENDPOINT                    0x05
+#define USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER            0x06
+#define USB_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION   0x07
+#define USB_DESCRIPTOR_TYPE_INTERFACE_POWER             0x08
+
+/*
+ * standard feature selectors
+ */
+#define	USB_ENDPOINT_HALT		0x00
+#define USB_DEVICE_REMOTE_WAKEUP	0x01
+#define	USB_TEST_MODE			0x02
+
+
+/* USB Requests
+ *
+ */
+
+struct usb_device_request {
+	__u8 bmRequestType;
+	__u8 bRequest;
+	__u16 wValue;
+	__u16 wIndex;
+	__u16 wLength;
+} __attribute__ ((packed));
+
+
+/*
+ * Class-Specific Request Codes
+ * C.f. CDC Table 46
+ */
+#define CDC_CLASS_REQUEST_SEND_ENCAPSULATED	0x00
+#define CDC_CLASS_REQUEST_GET_ENCAPSULATED	0x01
+
+#define CDC_CLASS_REQUEST_SET_COMM_FEATURE	0x02
+#define CDC_CLASS_REQUEST_GET_COMM_FEATURE	0x03
+#define CDC_CLASS_REQUEST_CLEAR_COMM_FEATURE	0x04
+
+#define CDC_CLASS_REQUEST_SET_LINE_CODING 	0x20
+#define CDC_CLASS_REQUEST_GET_LINE_CODING 	0x21
+
+#define CDC_CLASS_REQUEST_SET_CONTROL_STATE	0x22
+#define CDC_CLASS_REQUEST_SEND_BREAK        	0x23
+
+/*
+ * Notification codes
+ * c.f. CDC Table 68
+ */
+
+#define CDC_NOTIFICATION_NETWORK_CONNECTION         0x00
+#define CDC_NOTIFICATION_RESPONSE_AVAILABLE         0x01
+#define CDC_NOTIFICATION_AUX_JACK_HOOK_STATE        0x08
+#define CDC_NOTIFICATION_RING_DETECT                0x09
+#define CDC_NOTIFICATION_SERIAL_STATE               0x20
+#define CDC_NOTIFICATION_CALL_STATE_CHANGE          0x28
+#define CDC_NOTIFICATION_LINE_STATE_CHANGE          0x29
+#define CDC_NOTIFICATION_CONNECTION_SPEED_CHANGE    0x2a
+
+/*
+ * HID - Class Descriptors
+ * C.f. 7.1.1
+ */
+#define HID		0x21
+#define	HID_REPORT	0x22
+#define	HID_PHYSICAL	0x23
+
+/*
+ * HID Descriptor
+ * C.f. E.8
+ */
+struct hid_descriptor {
+	u8 bLength;
+	u8 bDescriptorType;
+	u16 bcdHID;
+	u8 bCountryCode;
+	u8 bNumDescriptors;
+	u8 bReportType;
+	u16 wItemLength;
+} __attribute__((packed));
+
+/*
+ * ACM - Line Coding structure
+ * C.f CDC Table 50
+ */
+struct cdc_acm_line_coding {
+	__u32 dwDTERate;
+	__u8 bCharFormat;
+	__u8 bParityType;
+	__u8 bDataBits;
+} __attribute__((packed));
+
+/*
+ * ACM
+ * C.f CDC Table 50
+ */
+
+
+/*
+ * ACM
+ * C.f CDC Table 51
+ */
+
+
+/* USB Notification
+ *
+ */
+
+struct cdc_notification_descriptor {
+        __u8 bmRequestType;
+        __u8 bNotification;
+	__u16 wValue;
+	__u16 wIndex;
+        __u16 wLength;
+	__u8 data[2];
+} __attribute__ ((packed));
+
+
+
+
+/*
+ * communications class types
+ *
+ * c.f. CDC  USB Class Definitions for Communications Devices 
+ * c.f. WMCD USB CDC Subclass Specification for Wireless Mobile Communications Devices
+ *
+ */
+
+#define CLASS_BCD_VERSION		0x0110
+
+// c.f. CDC 4.1 Table 14
+#define AUDIO_CLASS	                0x01
+#define COMMUNICATIONS_DEVICE_CLASS	0x02
+
+// c.f. CDC 4.2 Table 15
+#define COMMUNICATIONS_INTERFACE_CLASS	0x02
+
+// c.f. CDC 4.3 Table 16
+#define COMMUNICATIONS_NO_SUBCLASS	0x00
+#define COMMUNICATIONS_DLCM_SUBCLASS	0x01
+#define COMMUNICATIONS_ACM_SUBCLASS	0x02
+#define COMMUNICATIONS_TCM_SUBCLASS	0x03
+#define COMMUNICATIONS_MCCM_SUBCLASS	0x04
+#define COMMUNICATIONS_CCM_SUBCLASS	0x05
+#define COMMUNICATIONS_ENCM_SUBCLASS	0x06
+#define COMMUNICATIONS_ANCM_SUBCLASS	0x07
+
+#define AUDIO_CONTROL_SUBCLASS          0x01
+#define AUDIO_STREAMING_SUBCLASS        0x02
+
+// c.f. WMCD 5.1
+#define COMMUNICATIONS_WHCM_SUBCLASS	0x08
+#define COMMUNICATIONS_DMM_SUBCLASS	0x09
+#define COMMUNICATIONS_MDLM_SUBCLASS	0x0a
+#define COMMUNICATIONS_OBEX_SUBCLASS	0x0b
+
+// c.f. CDC 4.6 Table 18
+#define DATA_INTERFACE_CLASS		0x0a
+
+// c.f. CDC 4.7 Table 19
+#define COMMUNICATIONS_NO_PROTOCOL	0x00
+
+
+// c.f. CDC 5.2.3 Table 24
+// c.f. Audio Appendix A.4
+#define CS_UNDEFINED             	0x20
+#define CS_DEVICE                	0x21
+#define CS_CONFIG                	0x22
+#define CS_STRING                	0x23
+#define CS_INTERFACE			0x24
+#define CS_ENDPOINT			0x25
+
+// Audio Interface Class - c.f Appendix A.1
+#define	AUDIO_INTERFACE_CLASS		0x01
+
+// Audio Interface Subclass - c.f Appendix A.2
+#define	AUDIO_SUBCLASS_UNDEFINED	0x00
+#define	AUDIO_AUDIOCONTROL		0x01
+#define	AUDIO_AUDIOSTREAMING		0x02
+#define	AUDIO_MIDISTREAMING		0x03
+
+// Audio Interface Proctol - c.f. Appendix A.3
+#define	AUDIO_PR_PROTOCOL_UNDEFINED	0x00
+
+// Audio Class-Specific AC Interface Descriptor Subtypes - c.f. A.5
+#define	AUDIO_AC_DESCRIPTOR_UNDEFINED 	0x00
+#define	AUDIO_AC_HEADER			0x01
+#define	AUDIO_AC_INPUT_TERMINAL		0x02
+#define	AUDIO_AC_OUTPUT_TERMINAL	0x03
+#define	AUDIO_AC_MIXER_UNIT		0x04
+#define	AUDIO_AC_SELECTOR_UNIT		0x05
+#define	AUDIO_AC_FEATURE_UNIT		0x06
+#define	AUDIO_AC_PROCESSING_UNIT	0x07
+#define	AUDIO_AC_EXTENSION_UNIT		0x08
+
+// Audio Class-Specific AS Interface Descriptor Subtypes - c.f. A.6
+#define	AUDIO_AS_DESCRIPTOR_UNDEFINED 	0x00
+#define	AUDIO_AS_GENERAL		0x01
+#define	AUDIO_AS_FORMAT_TYPE		0x02
+#define	AUDIO_AS_FORMAT_UNSPECFIC	0x03
+
+// Audio Class Processing Unit Processing Types - c.f. A.7
+#define	AUDIO_PROCESS_UNDEFINED		0x00
+#define	AUDIO_UP_DOWN_MUIX_PROCESS	0x01
+#define	AUDIO_DOLBY_PROLOGIC_PROCESS	0x02
+#define	AUDIO_3D_STEREO_EXTENDER_PROCESS 0x03
+#define	AUDIO_REVERBERATION_PROCESS	0x04
+#define	AUDIO_CHORUS_PROCESS		0x05
+#define	AUDIO_DYN_RANGE_COMP_PROCESS	0x06
+
+// Audio Class-Specific Endpoint Descriptor Subtypes - c.f. A.8
+#define	AUDIO_DESCRIPTOR_UNDEFINED	0x00
+#define	AUDIO_EP_GENERAL		0x01
+
+// Audio Class-Specific Request Codes - c.f. A.9
+#define	AUDIO_REQUEST_CODE_UNDEFINED	0x00
+#define	AUDIO_SET_CUR			0x01
+#define	AUDIO_GET_CUR			0x81
+#define	AUDIO_SET_MIN			0x02
+#define	AUDIO_GET_MIN			0x82
+#define	AUDIO_SET_MAX			0x03
+#define	AUDIO_GET_MAX			0x83
+#define	AUDIO_SET_RES			0x04
+#define	AUDIO_GET_RES			0x84
+#define	AUDIO_SET_MEM			0x05
+#define	AUDIO_GET_MEM			0x85
+#define	AUDIO_GET_STAT			0xff
+
+
+
+
+
+
+/*
+ * bDescriptorSubtypes
+ *
+ * c.f. CDC 5.2.3 Table 25
+ * c.f. WMCD 5.3 Table 5.3
+ */
+
+#define USB_ST_HEADER			0x00
+#define USB_ST_CMF			0x01
+#define USB_ST_ACMF			0x02
+#define USB_ST_DLMF			0x03
+#define USB_ST_TRF			0x04
+#define USB_ST_TCLF			0x05
+#define USB_ST_UF			0x06
+#define USB_ST_CSF			0x07
+#define USB_ST_TOMF			0x08
+#define USB_ST_USBTF			0x09
+#define USB_ST_NCT			0x0a
+#define USB_ST_PUF			0x0b
+#define USB_ST_EUF			0x0c
+#define USB_ST_MCMF			0x0d
+#define USB_ST_CCMF			0x0e
+#define USB_ST_ENF			0x0f
+#define USB_ST_ATMNF			0x10
+
+#define USB_ST_WHCM			0x11
+#define USB_ST_MDLM			0x12
+#define USB_ST_MDLMD			0x13
+#define USB_ST_DMM			0x14
+#define USB_ST_OBEX			0x15
+#define USB_ST_CS			0x16
+#define USB_ST_CSD			0x17
+#define USB_ST_TCM			0x18
+
+
+
+/*
+ * commuications class description structures
+ *
+ * c.f. CDC 5.1
+ * c.f. WCMC 6.7.2
+ *
+ * XXX add the other dozen class descriptor description structures....
+ */
+
+struct usb_header_description {
+	__u8 bDescriptorSubtype;
+	__u16 bcdCDC;
+};
+
+struct usb_call_management_description {
+	__u8 bmCapabilities;
+	__u8 bDataInterface;
+};
+
+struct usb_abstract_control_description {
+	__u8 bmCapabilities;
+};
+
+struct usb_union_function_description {
+	__u8 bMasterInterface;
+	__u8 bSlaveInterface[1];
+	//__u8        bSlaveInterface[0];                    // XXX FIXME
+};
+
+struct usb_ethernet_networking_description {
+	char *iMACAddress;
+	__u8 bmEthernetStatistics;
+	__u16 wMaxSegmentSize;
+	__u16 wNumberMCFilters;
+	__u8 bNumberPowerFilters;
+};
+
+struct usb_mobile_direct_line_model_description {
+	__u16 bcdVersion;
+	__u8 bGUID[16];
+};
+
+struct usb_mobile_direct_line_model_detail_description {
+	__u8 bGuidDescriptorType;
+	__u8 bDetailData[2];
+	//__u8      bDetailData[0];                         // XXX FIXME
+};
+
+struct usb_class_description {
+	__u8 bDescriptorSubtype;
+	__u8 elements;
+	union {
+		struct usb_header_description header;
+		struct usb_call_management_description call_management;
+		struct usb_abstract_control_description abstract_control;
+		struct usb_union_function_description union_function;
+		struct usb_ethernet_networking_description ethernet_networking;
+		struct usb_mobile_direct_line_model_description mobile_direct;
+		struct usb_mobile_direct_line_model_detail_description mobile_direct_detail;
+	} description;
+};
+
+/* endpoint modifiers
+ * static struct usb_endpoint_description function_default_A_1[] = {
+ *
+ *     {this_endpoint: 0, attributes: CONTROL,   max_size: 8,  polling_interval: 0 },
+ *     {this_endpoint: 1, attributes: BULK,      max_size: 64, polling_interval: 0, direction: IN},
+ *     {this_endpoint: 2, attributes: BULK,      max_size: 64, polling_interval: 0, direction: OUT},
+ *     {this_endpoint: 3, attributes: INTERRUPT, max_size: 8,  polling_interval: 0},
+ *
+ *
+ */
+#define OUT		0x00
+#define IN		0x80
+
+#define CONTROL		0x00
+#define ISOCHRONOUS	0x01
+#define BULK		0x02
+#define INTERRUPT	0x03
+
+
+/* configuration modifiers
+ */
+#define BMATTRIBUTE_RESERVED		0x80
+#define BMATTRIBUTE_SELF_POWERED	0x40
+
+/*
+ * The UUT tester specifically tests for MaxPower to be non-zero (> 0).
+ */
+#if !defined(CONFIG_USBD_MAXPOWER) || (CONFIG_USBD_MAXPOWER == 0)
+	#define BMATTRIBUTE BMATTRIBUTE_RESERVED | BMATTRIBUTE_SELF_POWERED
+	#define BMAXPOWER 1
+#else
+	#define BMATTRIBUTE BMATTRIBUTE_RESERVED
+	#define BMAXPOWER CONFIG_USBD_MAXPOWER
+#endif
+
+
+
+
+/* 
+ * standard usb descriptor structures
+ */
+
+struct usb_endpoint_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;	// 0x5
+	__u8 bEndpointAddress;
+	__u8 bmAttributes;
+	__u16 wMaxPacketSize;
+	__u8 bInterval;
+} __attribute__ ((packed));
+
+struct usb_interface_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;	// 0x04
+	__u8 bInterfaceNumber;
+	__u8 bAlternateSetting;
+	__u8 bNumEndpoints;
+	__u8 bInterfaceClass;
+	__u8 bInterfaceSubClass;
+	__u8 bInterfaceProtocol;
+	__u8 iInterface;
+} __attribute__ ((packed));
+
+struct usb_configuration_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;	// 0x2
+	__u16 wTotalLength;
+	__u8 bNumInterfaces;
+	__u8 bConfigurationValue;
+	__u8 iConfiguration;
+	__u8 bmAttributes;
+	__u8 bMaxPower;
+} __attribute__ ((packed));
+
+struct usb_device_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;	// 0x01
+	__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));
+
+struct usb_string_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;	// 0x03
+	__u16 wData[0];
+} __attribute__ ((packed));
+
+struct usb_device_qualifier_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u16 bcdUSB;
+	__u8 bDeviceClass;
+	__u8 bDeviceSubClass;
+	__u8 bDeviceProtocol;
+	__u8 bMaxPacketSize0;
+	__u8 bNumConfigurations;
+	__u8 bReserved;
+} __attribute__ ((packed));
+
+struct usb_generic_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+} __attribute__ ((packed));
+
+struct usb_generic_class_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+} __attribute__ ((packed));
+
+
+/* 
+ * communications class descriptor structures
+ *
+ * c.f. CDC 5.2 Table 25c
+ */
+
+struct usb_class_function_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+} __attribute__ ((packed));
+
+struct usb_class_function_descriptor_generic {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bmCapabilities;
+} __attribute__ ((packed));
+
+struct usb_class_header_function_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;	// 0x00
+	__u16 bcdCDC;
+} __attribute__ ((packed));
+
+struct usb_class_call_management_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;	// 0x01
+	__u8 bmCapabilities;
+	__u8 bDataInterface;
+} __attribute__ ((packed));
+
+struct usb_class_abstract_control_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;	// 0x02
+	__u8 bmCapabilities;
+} __attribute__ ((packed));
+
+struct usb_class_direct_line_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;	// 0x03
+} __attribute__ ((packed));
+
+struct usb_class_telephone_ringer_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;	// 0x04
+	__u8 bRingerVolSeps;
+	__u8 bNumRingerPatterns;
+} __attribute__ ((packed));
+
+struct usb_class_telephone_call_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;	// 0x05
+	__u8 bmCapabilities;
+} __attribute__ ((packed));
+
+struct usb_class_union_function_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;	// 0x06
+	__u8 bMasterInterface;
+	__u8 bSlaveInterface0[0];
+} __attribute__ ((packed));
+
+struct usb_class_country_selection_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;	// 0x07
+	__u8 iCountryCodeRelDate;
+	__u16 wCountryCode0[0];
+} __attribute__ ((packed));
+
+
+struct usb_class_telephone_operational_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;	// 0x08
+	__u8 bmCapabilities;
+} __attribute__ ((packed));
+
+
+struct usb_class_usb_terminal_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;	// 0x09
+	__u8 bEntityId;
+	__u8 bInterfaceNo;
+	__u8 bOutInterfaceNo;
+	__u8 bmOptions;
+	__u8 bChild0[0];
+} __attribute__ ((packed));
+
+struct usb_class_network_channel_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;	// 0x0a
+	__u8 bEntityId;
+	__u8 iName;
+	__u8 bChannelIndex;
+	__u8 bPhysicalInterface;
+} __attribute__ ((packed));
+
+struct usb_class_protocol_unit_function_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;	// 0x0b
+	__u8 bEntityId;
+	__u8 bProtocol;
+	__u8 bChild0[0];
+} __attribute__ ((packed));
+
+struct usb_class_extension_unit_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;	// 0x0c
+	__u8 bEntityId;
+	__u8 bExtensionCode;
+	__u8 iName;
+	__u8 bChild0[0];
+} __attribute__ ((packed));
+
+struct usb_class_multi_channel_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;	// 0x0d
+	__u8 bmCapabilities;
+} __attribute__ ((packed));
+
+struct usb_class_capi_control_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;	// 0x0e
+	__u8 bmCapabilities;
+} __attribute__ ((packed));
+
+struct usb_class_ethernet_networking_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;	// 0x0f
+	__u8 iMACAddress;
+	__u32 bmEthernetStatistics;
+	__u16 wMaxSegmentSize;
+	__u16 wNumberMCFilters;
+	__u8 bNumberPowerFilters;
+} __attribute__ ((packed));
+
+struct usb_class_atm_networking_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;	// 0x10
+	__u8 iEndSystermIdentifier;
+	__u8 bmDataCapabilities;
+	__u8 bmATMDeviceStatistics;
+	__u16 wType2MaxSegmentSize;
+	__u16 wType3MaxSegmentSize;
+	__u16 wMaxVC;
+} __attribute__ ((packed));
+
+
+struct usb_class_mdlm_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;	// 0x12
+	__u16 bcdVersion;
+	__u8 bGUID[16];
+} __attribute__ ((packed));
+
+struct usb_class_mdlmd_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;	// 0x13
+	__u8 bGuidDescriptorType;
+	__u8 bDetailData[0];
+
+} __attribute__ ((packed));
+
+
+/* 
+ * descriptor union structures
+ */
+
+struct usb_descriptor {
+	union {
+		struct usb_generic_descriptor generic;
+		struct usb_generic_class_descriptor generic_class;
+		struct usb_endpoint_descriptor endpoint;
+		struct usb_interface_descriptor interface;
+		struct usb_configuration_descriptor configuration;
+		struct usb_device_descriptor device;
+		struct usb_string_descriptor string;
+	} descriptor;
+
+} __attribute__ ((packed));
+
+struct usb_class_descriptor {
+	union {
+		struct usb_class_function_descriptor function;
+		struct usb_class_function_descriptor_generic generic;
+		struct usb_class_header_function_descriptor header_function;
+		struct usb_class_call_management_descriptor call_management;
+		struct usb_class_abstract_control_descriptor abstract_control;
+		struct usb_class_direct_line_descriptor direct_line;
+		struct usb_class_telephone_ringer_descriptor telephone_ringer;
+		struct usb_class_telephone_operational_descriptor telephone_operational;
+		struct usb_class_telephone_call_descriptor telephone_call;
+		struct usb_class_union_function_descriptor union_function;
+		struct usb_class_country_selection_descriptor country_selection;
+		struct usb_class_usb_terminal_descriptor usb_terminal;
+		struct usb_class_network_channel_descriptor network_channel;
+		struct usb_class_extension_unit_descriptor extension_unit;
+		struct usb_class_multi_channel_descriptor multi_channel;
+		struct usb_class_capi_control_descriptor capi_control;
+		struct usb_class_ethernet_networking_descriptor ethernet_networking;
+		struct usb_class_atm_networking_descriptor atm_networking;
+		struct usb_class_mdlm_descriptor mobile_direct;
+		struct usb_class_mdlmd_descriptor mobile_direct_detail;
+	} descriptor;
+
+} __attribute__ ((packed));
+
+
+/*
+ * Audio Status Word Format - c.f. Table 3.1
+ */
+#define AUDIO_STATUS_INTERRUPT_PENDING		1<<7
+#define AUDIO_STATUS_MEMORY_CONTENT_CHANGED	1<<6
+#define AUDIO_STATUS_ORIGINATOR_AUDIO_CONTROL_INTERFACE			0
+#define AUDIO_STATUS_ORIGINATOR_AUDIO_STREAMING_INTERFACE		1
+#define AUDIO_STATUS_ORIGINATOR_AUDIO_STREAMING_ENDPOINT		2
+#define AUDIO_STATUS_ORIGINATOR_AUDIOCONTROL_INTERFACE			0
+
+struct usb_audio_status_word {
+        __u8 bStatusType;
+        __u8 bOriginator;
+} __attribute__ ((packed));
+
+struct usb_audio_ac_interface_header_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+        __u16 bcdADC;
+        __u16 wTotalLength;
+        __u8 binCollection;
+        __u8 bainterfaceNr[0];
+} __attribute__ ((packed));
+
+struct usb_audio_input_terminal_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+        __u8 bTerminalID;
+        __u16 wTerminalType;
+        __u8 bAssocTerminal;
+        __u8 bNrChannels;
+        __u16 wChannelConfig;
+        __u8 iChannelNames;
+        __u8 iTerminal;
+} __attribute__ ((packed));
+
+struct usb_audio_input_terminal_description {
+        struct usb_audio_input_terminal_descriptor *audio_input_terminal_descriptor;
+        __u16 wTerminalType;
+        __u16 wChannelConfig;
+        char * iChannelNames;
+        char * iTerminal;
+}; 
+
+struct usb_audio_output_terminal_descriptor {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+        __u8 bTerminalID;
+        __u16 wTerminalType;
+        __u8 bAssocTerminal;
+        __u8 bSourceID;
+        __u8 iTerminal;
+} __attribute__ ((packed));
+
+struct usb_audio_output_terminal_description {
+        struct usb_audio_output_terminal_descriptor *audio_output_terminal_descriptor;
+        __u16 wTerminalType;
+        char * iTerminal;
+}; 
+
+struct usb_audio_mixer_unit_descriptor_a {
+	__u8 bFunctionLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+        __u8 bUniteID;
+        __u8 bNrinPins;
+} __attribute__ ((packed));
+
+struct usb_audio_mixer_unit_descriptor_b {
+        __u8 baSourceID;
+        __u8 bNrChannels;
+        __u16 wChannelConfig;
+        __u8 iChannelNames;
+        __u8 bmControls;
+        __u8 iMixer;
+} __attribute__ ((packed));
+
+struct usb_audio_mixer_unit_description {
+        struct usb_audio_mixer_unit_descriptor *audio_mixer_unit_descriptor;
+        __u16 wChannelConfig;
+        char * iMixer;
+}; 
+
+
+struct usb_audio_as_general_interface_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;	
+	__u8 bDescriptorSubtype;	
+        __u8 bTerminalLink;
+        __u8 bDelay;
+        __u16 wFormatTag;
+} __attribute__ ((packed));
+
+struct usb_audio_as_general_interface_description {
+        struct usb_audio_as_general_interface_descriptor *audio_as_general_interface_descriptor;
+        __u16 wFormatTag;
+}; 
+
+struct usb_audio_as_format_type_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;	
+	__u8 bDescriptorSubtype;	
+        __u8 bFormatType;
+        __u8 bNrChannels;
+        __u8 bSubFrameSize;
+        __u8 bBitResolution;
+        __u8 bSamFreqType;
+        __u8 iSamFreq[3];
+} __attribute__ ((packed));
+
+struct usb_audio_as_format_type_description {
+        struct usb_audio_as_format_type_descriptor *audio_as_format_type_descriptor;
+        __u16 wFormatTag;
+}; 
+
+
+
+
+struct usb_audio_descriptor {
+	union {
+                struct usb_audio_ac_interface_header_descriptor header;
+                struct usb_audio_input_terminal_descriptor input;
+                struct usb_audio_output_terminal_descriptor output;
+	} descriptor;
+} __attribute__ ((packed));
+
+
+
+struct usb_ac_endpoint_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bEndpointAddress;
+	__u8 bmAttributes;
+	__u16 wMaxPacketSize;
+	__u8 bInterval;
+        __u8 bRefresh;
+        __u8 bSynchAddress;
+} __attribute__ ((packed));
+
+
+struct usb_as_iso_endpoint_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bmAttributes;
+        __u8 bLockDelayUnits;
+        __u16 wLockDelay;
+} __attribute__ ((packed));
+
+
diff -Nru a/drivers/usbd/usbd-export.h b/drivers/usbd/usbd-export.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/usbd-export.h	Fri Feb 27 14:22:51 2004
@@ -0,0 +1 @@
+#define USBD_EXPORT_TAG "usbd-gpl-20040227-1420-usbd-amd-pb1x00-kit"
diff -Nru a/drivers/usbd/usbd-fops.c b/drivers/usbd/usbd-fops.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/usbd-fops.c	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,589 @@
+/*
+ * usbd/usbd-fops.c - USB Function support
+ *
+ *      Copyright (c) 2004 Belcarra
+ *
+ * Adapted from earlier work:
+ *      Copyright (c) 2002, 2003 Belcarra
+ *      Copyright (c) 2000, 2001, 2002 Lineo
+ *      Copyright (c) 2001 Hewlett Packard
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <asm/uaccess.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+
+#include <linux/smp_lock.h>
+#include <linux/ctype.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+
+#include "usbd-chap9.h"
+#include "usbd-mem.h"
+#include "usbd.h"
+#include "usbd-func.h"
+#include "usbd-bus.h"
+
+
+struct usb_string_descriptor **usb_strings;
+
+extern struct list_head usbd_function_drivers;
+extern struct usb_bus_instance *usbd_bus_instance;
+
+
+/* usbd_get_string - find and return a string descriptor
+ *
+ * Find an indexed string and return a pointer to a it.
+ */
+struct usb_string_descriptor *usbd_get_string (__u8 index)
+{
+	RETURN_NULL_IF (index >= usbd_maxstrings);
+	return usb_strings[index];
+}
+
+/* usbd_alloc_string - allocate a string descriptor and return index number
+ *
+ * Find an empty slot in index string array, create a corresponding descriptor
+ * and return the slot number.
+ */
+__u8 usbd_alloc_string (char *str)
+{
+	int i;
+	struct usb_string_descriptor *string;
+	__u8 bLength;
+	__u16 *wData;
+
+	RETURN_ZERO_IF(!str || !strlen (str));
+
+        //printk(KERN_INFO"%s: %s\n", __FUNCTION__, str);
+
+	// find an empty string descriptor slot
+	for (i = 1; i < usbd_maxstrings; i++) {
+
+		CONTINUE_IF (usb_strings[i] != NULL);
+
+                bLength = sizeof (struct usb_string_descriptor) + 2 * strlen (str);
+
+                RETURN_ZERO_IF(!(string = ckmalloc (bLength, GFP_KERNEL)));
+
+                string->bLength = bLength;
+                string->bDescriptorType = USB_DT_STRING;
+
+                for (wData = string->wData; *str;) 
+                        *wData++ = (__u16) (*str++);
+
+                // store in string index array
+                usb_strings[i] = string;
+                return i;
+        }
+	return 0;
+}
+
+
+/* usbd_dealloc_string - deallocate a string descriptor
+ *
+ * Find and remove an allocated string.
+ */
+void usbd_dealloc_string (__u8 index)
+{
+	struct usb_string_descriptor *string;
+
+	if ((index < usbd_maxstrings) && (string = usb_strings[index])) {
+		usb_strings[index] = NULL;
+		lkfree (string);
+	}
+}
+
+/* usbd_alloc_urb - allocate an URB appropriate for specified endpoint
+ *
+ * Allocate an urb structure. The usb device urb structure is used to
+ * contain all data associated with a transfer, including a setup packet for
+ * control transfers.
+ */
+struct urb *usbd_alloc_urb (struct usb_function_instance *function, int endpoint_index,
+                int length, int (*callback) (struct urb *, int))
+{
+        struct urb *urb = NULL;
+        struct usb_endpoint_map *endpoint_map;
+        struct usb_endpoint_instance *endpoint;
+        unsigned long flags;
+
+        //printk(KERN_INFO"%s: %s\n", __FUNCTION__, function->function_driver->name);
+        RETURN_NULL_IF(!function);
+        RETURN_NULL_IF(!(endpoint_map = function->endpoint_map_array));
+        RETURN_NULL_IF(!(endpoint = endpoint_map[endpoint_index].endpoint));
+
+        local_irq_save(flags);
+        THROW_IF (!(urb = ckmalloc (sizeof (struct urb), GFP_ATOMIC)), error); 
+        urb->endpoint = endpoint;
+        urb->bus = function->bus;
+        urb->function_instance = function;
+        urb->link.prev = urb->link.next = &urb->link;
+        urb->callback = callback;
+        urb->buffer_length = urb->actual_length = 0;
+
+        if (length) {
+                urb->request_length = length;
+
+                /* For receive we always overallocate to ensure that receiving another
+                 * full sized packet when we are only expecting a short packet will
+                 * not overflow the buffer
+                 */
+                if (!endpoint->bEndpointAddress || endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+                        length = ((length / endpoint->wMaxPacketSize) + 1) * endpoint->wMaxPacketSize;
+                }
+                urb->buffer_length = length;
+
+                if (urb->endpoint && urb->endpoint->bEndpointAddress && urb->function_instance && 
+                                urb->function_instance->function_driver->fops->alloc_urb_data) 
+                {
+                        THROW_IF(urb->function_instance->function_driver->fops->alloc_urb_data (urb, length), error); 
+                }
+                else {
+                        THROW_IF(!(urb->buffer = ckmalloc (length, GFP_ATOMIC)), error);
+                }
+        }
+
+        CATCH(error) {
+                printk(KERN_ERR"%s: dealloc %p\n", __FUNCTION__, urb); 
+                usbd_dealloc_urb(urb);
+                urb = NULL;
+        }
+        local_irq_restore(flags);
+        //printk(KERN_INFO"%s: finis %p\n", __FUNCTION__, urb);
+        return urb;
+}
+
+struct urb *usbd_alloc_urb_ep0 (struct usb_function_instance *function, int length, int (*callback) (struct urb *, int))
+{
+        return usbd_alloc_urb(function->bus->ep0, 0, length, callback);
+}
+
+/* usbd_dealloc_urb - deallocate an URB and associated buffer
+ *
+ * Deallocate an urb structure and associated data.
+ */
+void usbd_dealloc_urb (struct urb *urb)
+{
+        RETURN_IF (!urb);
+        if (urb->buffer) 
+                (urb->function_instance && urb->function_instance->function_driver->fops->dealloc_urb_data) ?
+                        urb->function_instance->function_driver->fops->dealloc_urb_data : lkfree (urb->buffer);
+        lkfree (urb);
+}
+
+/* usbd_urb_callback - tell function that an urb has been transmitted.
+ *
+ * Must be called from an interrupt or with interrupts disabled.
+ *
+ * Used by a USB Bus driver to pass a sent urb back to the function 
+ * driver via the endpoints done queue.
+ */
+void usbd_urb_callback (struct urb *urb, int rc)
+{
+        RETURN_IF (!urb);
+        if (!urb->callback || urb->callback(urb,rc)) 
+                usbd_dealloc_urb(urb);
+}
+
+
+/* usbd_recv_setup_irq - process a received urb
+ *
+ * Used by a USB Bus interface driver to pass received data in a URB to the
+ * appropriate USB Function driver.
+ */
+int usbd_recv_setup_irq (struct usb_function_instance *function, struct usb_device_request *request)
+{
+        return function->function_driver->fops->recv_setup_irq(request);
+}
+
+void *usbd_function_get_privdata(struct usb_function_instance *function)
+{
+	return(function->privdata);
+}
+
+void usbd_function_set_privdata(struct usb_function_instance *function, void *privdata)
+{
+	function->privdata = privdata;
+}
+
+int usbd_endpoint_wMaxPacketSize(struct usb_function_instance *function, int endpoint_index, int hs)
+{
+        struct usb_endpoint_map *endpoint_map;
+        RETURN_ZERO_IF(!(endpoint_map = function->endpoint_map_array));
+        return le16_to_cpu(endpoint_map[endpoint_index].wMaxPacketSize[hs]);
+}
+
+int usbd_endpoint_bEndpointAddress(struct usb_function_instance *function, int endpoint_index, int hs)
+{
+        struct usb_endpoint_map *endpoint_map;
+        RETURN_ZERO_IF(!(endpoint_map = function->endpoint_map_array));
+        return endpoint_map[endpoint_index].bEndpointAddress[hs];
+}
+
+int usbd_endpoint_transferSize(struct usb_function_instance *function, int endpoint_index, int hs)
+{
+        struct usb_endpoint_map *endpoint_map;
+        RETURN_ZERO_IF(!(endpoint_map = function->endpoint_map_array));
+        return endpoint_map[endpoint_index].transferSize[hs];
+}
+
+void usbd_endpoint_update(struct usb_function_instance *function, int endpoint_index, 
+                struct usb_endpoint_descriptor *endpoint, int hs)
+{
+        endpoint->bEndpointAddress = usbd_endpoint_bEndpointAddress(function, endpoint_index, hs);
+        endpoint->wMaxPacketSize = usbd_endpoint_wMaxPacketSize(function, endpoint_index, hs);
+}
+
+int usbd_endpoint_interface(struct usb_function_instance *function, int endpoint_index)
+{
+        struct usb_endpoint_map *endpoint_map;
+        RETURN_ZERO_IF(!(endpoint_map = function->endpoint_map_array));
+        return endpoint_map[endpoint_index].interface;
+}
+
+int usbd_interface_AltSetting(struct usb_function_instance *function, int interface_index)
+{
+        // XXX TODO - per function modifications for composite devices
+        return function->bus->alternates[interface_index];
+}
+
+int usbd_ConfigurationValue(struct usb_function_instance *function)
+{
+        // XXX TODO - per function modifications for composite devices
+        return function->bus->ConfigurationValue;
+}
+
+int usbd_high_speed(struct usb_function_instance *function)
+{
+        // XXX TODO - per function modifications for composite devices
+        return function->bus->HighSpeedFlag;
+}
+
+/* usb-device USB FUNCTION generic functions ************************************************* */
+
+/* alloc_function_alternates - allocate alternate instance array
+ *
+ * Return a pointer to an array of alternate instances. Each instance contains a pointer
+ * to a filled in alternate descriptor and a pointer to the endpoint descriptor array.
+ *
+ * Returning NULL will cause the caller to cleanup all previously allocated memory.
+ */
+static struct usb_alternate_instance *alloc_function_alternates (int bInterfaceNumber,
+                struct usb_interface_description *interface_description,
+                int alternates, struct usb_alternate_description *alternate_description_array)
+{
+	int i;
+	struct usb_alternate_instance *alternate_instance_array;
+
+	// allocate array of alternate instances
+	RETURN_NULL_IF(!(alternate_instance_array = ckmalloc (sizeof (struct usb_alternate_instance) * alternates, GFP_KERNEL)));
+	
+	// iterate across the alternate descriptions
+	for (i = 0; i < alternates; i++) {
+
+		struct usb_alternate_description *alternate_description = alternate_description_array + i;
+		struct usb_alternate_instance *alternate_instance = alternate_instance_array + i;
+		struct usb_interface_descriptor *interface_descriptor;
+
+                interface_descriptor = alternate_description->interface_descriptor;
+                if (interface_descriptor->bInterfaceNumber != bInterfaceNumber)	{
+                        printk(KERN_INFO"%s: bInterfaceNumber mis-match: %d should be %d\n", __FUNCTION__,
+                                        interface_descriptor->bInterfaceNumber, bInterfaceNumber);
+                        lkfree(alternate_instance_array);
+                        return NULL;
+                }
+                
+		interface_descriptor->bNumEndpoints = alternate_description->endpoints;
+		interface_descriptor->iInterface = usbd_alloc_string (alternate_description->iInterface );
+
+                alternate_instance->class_list = alternate_description->class_list;
+		alternate_instance->classes = alternate_description->classes;
+		alternate_instance->endpoint_list = alternate_description->endpoint_list; 
+		alternate_instance->endpoint_indexes = alternate_description->endpoint_indexes; 
+
+		// save number of alternates, classes and endpoints for this alternate
+		alternate_instance->endpoints = alternate_description->endpoints;
+		alternate_instance->interface_descriptor = interface_descriptor;
+	}
+	return alternate_instance_array;
+}
+
+/* alloc_function_interfaces - allocate interface instance array
+ *
+ * Return a pointer to an array of interface instances. Each instance contains a pointer
+ * to a filled in interface descriptor and a pointer to the endpoint descriptor array.
+ *
+ * Returning NULL will cause the caller to cleanup all previously allocated memory.
+ */
+static struct usb_interface_instance *alloc_function_interfaces (int bNumInterfaces, 
+                struct usb_interface_description *interface_description_array)
+{
+	int interface;
+	struct usb_interface_instance *interface_instance_array;
+
+	// allocate array of interface instances
+	RETURN_NULL_IF(!(interface_instance_array = 
+                                ckmalloc (sizeof (struct usb_interface_instance) * bNumInterfaces, GFP_KERNEL)));
+	
+	// iterate across the interface descriptions
+	for (interface = 0; interface < bNumInterfaces; interface++) {
+
+		struct usb_interface_description *interface_description = interface_description_array + interface;
+		struct usb_interface_instance *interface_instance = interface_instance_array + interface;
+
+		interface_instance->alternates_instance_array = 
+                        alloc_function_alternates (interface, interface_description, 
+                                        interface_description->alternates, interface_description->alternate_list);
+		interface_instance->alternates = interface_description->alternates;
+	}
+	return interface_instance_array;
+}
+
+
+/* alloc_function_configurations - allocate configuration instance array
+ *
+ * Return a pointer to an array of configuration instances. Each instance contains a pointer
+ * to a filled in configuration descriptor and a pointer to the interface instances array.
+ *
+ * Returning NULL will cause the caller to cleanup all previously allocated memory.
+ */
+static struct usb_configuration_instance *alloc_function_configurations (int bNumConfigurations, 
+                struct usb_configuration_description *configuration_description_array)
+{
+	int i;
+	struct usb_configuration_instance *configuration_instance_array;
+
+	// allocate array
+	RETURN_NULL_IF(!(configuration_instance_array = 
+                                ckmalloc (sizeof (struct usb_configuration_instance) * bNumConfigurations, GFP_KERNEL)));
+	// fill in array
+	for (i = 0; i < bNumConfigurations; i++) {
+		int j;
+		int length;
+
+		struct usb_configuration_description *configuration_description = configuration_description_array + i;
+		struct usb_configuration_descriptor *configuration_descriptor;
+
+                configuration_descriptor = configuration_description->configuration_descriptor;
+
+		// setup fields in configuration descriptor
+		// XXX c.f. 9.4.7 zero is default, so config MUST BE 1 to n, not 0 to n-1
+		// XXX N.B. the configuration itself is fetched 0 to n-1.
+
+		configuration_descriptor->bConfigurationValue = i + 1;
+		configuration_descriptor->wTotalLength = 0;
+		configuration_descriptor->bNumInterfaces = configuration_description->bNumInterfaces;
+		configuration_descriptor->iConfiguration = usbd_alloc_string (configuration_description->iConfiguration);
+
+		// save the configuration descriptor in the configuration instance array
+		configuration_instance_array[i].configuration_descriptor = configuration_descriptor;
+
+		RETURN_NULL_IF (!(configuration_instance_array[i].interface_instance_array = 
+                                        alloc_function_interfaces (configuration_description->bNumInterfaces, 
+                                                configuration_description->interface_list)));
+
+		length = sizeof(struct usb_configuration_descriptor);
+
+		for (j = 0; j < configuration_descriptor->bNumInterfaces; j++) {
+
+			int alternate;
+			struct usb_interface_instance *interface_instance = 
+                                configuration_instance_array[i].interface_instance_array + j;
+
+                        //printk(KERN_INFO"%s: len: %02x:%02d configuration\n", __FUNCTION__, length, length);
+			for (alternate = 0; alternate < interface_instance->alternates; alternate++) {
+				int class;
+                                int endpoint;
+				struct usb_alternate_instance *alternate_instance = 
+                                        interface_instance->alternates_instance_array + alternate;
+
+				length += sizeof (struct usb_interface_descriptor);
+
+                                //printk(KERN_INFO"%s: len: %02x:%02d interface\n", __FUNCTION__, length, length);
+				for (class = 0; class < alternate_instance->classes; class++) {
+                                        struct usb_generic_class_descriptor * class_descriptor = 
+                                                *(alternate_instance->class_list + class);
+					length += class_descriptor->bLength;
+                                        //printk(KERN_INFO"%s: len: %02x:%02d class\n", __FUNCTION__, length, length);
+				}
+
+				for (endpoint = 0; endpoint < alternate_instance->endpoints; endpoint++) {
+                                        struct usb_endpoint_descriptor * endpoint_descriptor = 
+                                                *(alternate_instance->endpoint_list + endpoint);
+					length += endpoint_descriptor->bLength;
+                                        //printk(KERN_INFO"%s: len: %02x:%02d endpoint\n", __FUNCTION__, length, length);
+				}
+			}
+		}
+		configuration_descriptor->wTotalLength = cpu_to_le16 (length);
+		configuration_instance_array[i].bNumInterfaces = configuration_description->bNumInterfaces;
+                //printk(KERN_INFO"%s: configuration_instance_array[%d]: %p\n", __FUNCTION__, i, 
+                //                &configuration_instance_array[i]);
+	}
+	return configuration_instance_array;
+}
+
+
+void usbd_func_event_irq(struct usb_bus_instance *bus, struct usb_function_instance *function, 
+                usb_device_event_t event , int data)
+{
+        function->function_driver->fops->event_irq(function, event, data);
+}
+
+
+/* usbd_register_function - register a usb function driver
+ *
+ * Used by a USB Function driver to register itself with the usb device layer.
+ *
+ * It will create a usb_function_instance structure.
+ *
+ * The user friendly configuration/interface/endpoint descriptions are compiled into
+ * the equivalent ready to use descriptor records.
+ *
+ * All function drivers must register before any bus interface drivers.
+ *
+ */
+int usbd_register_function (struct usb_function_driver *function_driver)
+{
+	MOD_INC_USE_COUNT;
+	list_add_tail (&function_driver->drivers, &usbd_function_drivers);
+	return 0;
+}
+
+
+/* usbd_deregister_function - called by a USB FUNCTION driver to deregister itself
+ *
+ * Called by a USB Function driver De-register a usb function driver.
+ */
+void usbd_deregister_function (struct usb_function_driver *function_driver)
+{
+	list_del (&function_driver->drivers);
+	MOD_DEC_USE_COUNT;
+}
+
+
+int usbd_function_enable (struct usb_bus_instance *bus, struct usb_function_instance *function)
+{
+        struct usb_function_driver *function_driver = function->function_driver;
+	struct usb_device_description *device_description;
+	struct usb_device_descriptor *device_descriptor;
+        int rc = 0;
+
+        function->bus = bus;
+
+        RETURN_EINVAL_IF(function_driver->fops->function_enable (function));
+	RETURN_ZERO_IF(!(device_description = function_driver->device_description));
+	RETURN_ZERO_IF(!(device_descriptor = device_description->device_descriptor));
+
+	device_descriptor->bMaxPacketSize0 = bus->driver->maxpacketsize;
+	device_descriptor->iManufacturer = usbd_alloc_string (device_description->iManufacturer);
+	device_descriptor->iProduct = usbd_alloc_string (device_description->iProduct);
+
+	if (bus->serial_number_str && strlen (bus->serial_number_str)) 
+		device_descriptor->iSerialNumber = usbd_alloc_string (bus->serial_number_str);
+        else  
+		device_descriptor->iSerialNumber = usbd_alloc_string (device_description->iSerialNumber);
+
+	device_descriptor->bNumConfigurations = function_driver->bNumConfigurations;
+	device_descriptor->idVendor = function_driver->idVendor;
+	device_descriptor->idProduct = function_driver->idProduct;
+	device_descriptor->bcdDevice = function_driver->bcdDevice;
+
+	/* call alloc_function_configurations() to allocate the configuration descriptor array
+         */
+	RETURN_EINVAL_IF (!(function_driver->configuration_instance_array = 
+                                alloc_function_configurations (function_driver->bNumConfigurations, 
+                                        function_driver->configuration_description)));
+	function_driver->device_descriptor = device_descriptor;
+	function_driver->device_qualifier_descriptor = device_description->device_qualifier_descriptor;
+        return rc;
+}
+
+
+void usbd_function_disable (struct usb_function_instance *function)
+{
+	int configuration;
+        struct usb_function_driver *function_driver = function->function_driver;
+	struct usb_configuration_instance *configuration_instance_array = function_driver->configuration_instance_array;
+
+        function->function_driver->fops->function_disable (function);
+
+        RETURN_IF(!function_driver->configuration_instance_array);
+
+	// iterate across the descriptors list and de-allocate all structures
+	if (function_driver->configuration_instance_array) {
+		for (configuration = 0; configuration < function_driver->bNumConfigurations; configuration++) {
+			int interface;
+			struct usb_configuration_instance *configuration_instance = configuration_instance_array + configuration;
+
+			for (interface = 0; interface < configuration_instance->bNumInterfaces; interface++) {
+
+				//int alternate;
+				struct usb_interface_instance *interface_instance = 
+                                        configuration_instance->interface_instance_array + interface;
+
+				lkfree (interface_instance->alternates_instance_array);
+			}
+			lkfree (configuration_instance->interface_instance_array);
+			//usbd_dealloc_descriptor_strings( (struct usb_descriptor *)
+                        //                configuration_instance_array[configuration].configuration_descriptor);
+		}
+	}
+	
+        //usbd_dealloc_descriptor_strings ((struct usb_descriptor *) function_driver->device_descriptor);
+	lkfree (configuration_instance_array);
+
+        function->bus = NULL;
+}
+
+EXPORT_SYMBOL(usbd_register_function );
+EXPORT_SYMBOL(usbd_deregister_function);
+EXPORT_SYMBOL(usbd_alloc_string);
+EXPORT_SYMBOL(usbd_dealloc_string);
+EXPORT_SYMBOL(usbd_get_string);
+EXPORT_SYMBOL(usbd_alloc_urb);
+EXPORT_SYMBOL(usbd_alloc_urb_ep0);
+EXPORT_SYMBOL(usbd_dealloc_urb);
+EXPORT_SYMBOL(usbd_recv_setup_irq);
+EXPORT_SYMBOL(usbd_urb_callback);
+EXPORT_SYMBOL(usbd_endpoint_halted);
+EXPORT_SYMBOL(usbd_device_feature);
+EXPORT_SYMBOL(usbd_function_get_privdata);
+EXPORT_SYMBOL(usbd_function_set_privdata);
+
+EXPORT_SYMBOL(usbd_endpoint_wMaxPacketSize);
+EXPORT_SYMBOL(usbd_endpoint_bEndpointAddress);
+EXPORT_SYMBOL(usbd_endpoint_interface);
+EXPORT_SYMBOL(usbd_endpoint_transferSize);
+EXPORT_SYMBOL(usbd_interface_AltSetting);
+EXPORT_SYMBOL(usbd_ConfigurationValue);
+EXPORT_SYMBOL(usbd_high_speed);
+EXPORT_SYMBOL(usbd_endpoint_update);
diff -Nru a/drivers/usbd/usbd-func.h b/drivers/usbd/usbd-func.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/usbd-func.h	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,256 @@
+/*
+ * usbd/usbd-func.h - USB Device Function Driver Interface
+ *
+ *      Copyright (c) 2004 Belcarra
+ *
+ * Adapted from earlier work:
+ *      Copyright (c) 2002, 2003 Belcarra
+ *      Copyright (c) 2000, 2001, 2002 Lineo
+ *      Copyright (c) 2001 Hewlett Packard
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * This file contains the USB Function Driver Interface definitions.
+ *
+ * This is the interface between the bottom of the USB Function drivers and
+ * the top of the USB Core and is comprised of:
+ *
+ * 	o public functions exported by the USB Core layer 
+ *
+ * 	o structures and functions passed by the Bus Interface Driver to the
+ * 	USB Core layer
+ *
+ *
+ * USB Function Drivers are structured such that the upper edge implements
+ * some specific function (network, serial, mass storage etc) and the lower
+ * edge interfaces to the USB Device Core for USB services.
+ *
+ */
+
+/*
+ * USB Function Driver structures
+ *
+ * Descriptors:
+ *   struct usb_endpoint_description
+ *   struct usb_interface_description 
+ *   struct usb_configuration_description
+ *
+ * Driver description:
+ *   struct usb_function_driver
+ *   struct usb_function_operations
+ *
+ */
+
+struct usb_function_operations {
+	int (*function_enable) (struct usb_function_instance *);
+	void (*function_disable) (struct usb_function_instance *);
+	void (*event_irq) (struct usb_function_instance *, usb_device_event_t, int);
+	int (*recv_setup_irq) (struct usb_device_request *);
+	int (*alloc_urb_data) (struct urb *, int);
+	void (*dealloc_urb_data) (struct urb *);
+};
+
+
+/*
+ * function driver definitions
+ */
+struct usb_alternate_instance {
+	struct usb_interface_descriptor *interface_descriptor;
+	int 				classes;
+	struct usb_generic_class_descriptor 	**class_list;
+	int 				endpoints;
+        struct usb_endpoint_descriptor  **endpoint_list;
+	u8				*endpoint_indexes;
+};
+
+
+/*
+ * usb device description structures
+ */
+
+struct usb_alternate_description {
+	struct usb_interface_descriptor *interface_descriptor;
+	char 				*iInterface;
+
+	// list of CDC class descriptions for this alternate interface
+	u8 				classes;
+	struct usb_generic_class_descriptor 	**class_list;
+
+	// list of endpoint descriptions for this alternate interface
+	u8 				endpoints;
+        struct usb_endpoint_descriptor  **endpoint_list;
+
+	// list of indexes into endpoint request map for each endpoint descriptor
+	u8				*endpoint_indexes;
+};
+
+struct usb_interface_description {
+	// list of alternate interface descriptions for this interface
+	u8 				alternates;
+	struct usb_alternate_description *alternate_list;
+};
+
+struct usb_configuration_description {
+        struct usb_configuration_descriptor *configuration_descriptor;
+	char 				*iConfiguration;
+	// list of interface descriptons for this configuration
+	u8 				bNumInterfaces;
+	struct usb_interface_description *interface_list;
+	int 				configuration_type;
+};
+
+struct usb_device_description {
+        struct usb_device_descriptor 	*device_descriptor;
+	struct usb_device_qualifier_descriptor *device_qualifier_descriptor;
+	char 				*iManufacturer;
+	char 				*iProduct;
+	char 				*iSerialNumber;
+        u8 				endpointsRequested;
+	struct usb_endpoint_request 	*requestedEndpoints;
+	struct usb_function_endpoint_config *endpointConfig;
+};
+
+
+struct usb_interface_instance {
+	u8 				alternates;
+	struct usb_alternate_instance 	*alternates_instance_array;
+};
+
+struct usb_configuration_instance {
+	int 				bNumInterfaces;
+	struct usb_configuration_descriptor *configuration_descriptor;
+	struct usb_interface_instance 	*interface_instance_array;
+	struct usb_function_driver 	*function_driver;
+};
+
+
+extern struct usb_string_descriptor **usb_strings;
+__u8 usbd_alloc_string (char *);
+void usbd_dealloc_string (__u8 );
+void usbd_dealloc_descriptor_strings (struct usb_descriptor *);
+struct usb_string_descriptor *usbd_get_string (__u8);
+
+
+
+
+/* Function Driver data structure
+ *
+ * Function driver and its configuration descriptors. 
+ *
+ * This is passed to the usb-device layer when registering. It contains all
+ * required information about the function driver for the usb-device layer
+ * to use the function drivers configuration data and to configure this
+ * function driver an active configuration.
+ *
+ * Note that each function driver registers itself on a speculative basis.
+ * Whether a function driver is actually configured will depend on the USB
+ * HOST selecting one of the function drivers configurations. 
+ *
+ * This may be done multiple times WRT to either a single bus interface
+ * instance or WRT to multiple bus interface instances. In other words a
+ * multiple configurations may be selected for a specific bus interface. Or
+ * the same configuration may be selected for multiple bus interfaces. 
+ *
+ */
+struct usb_function_driver {
+	const char 			*name;
+	struct usb_function_operations 	*fops;	// functions 
+
+	// device & configuration descriptions 
+	struct usb_device_description 	*device_description;
+	struct usb_configuration_description *configuration_description;
+	int 				bNumConfigurations;
+
+	u16				idVendor;
+	u16				idProduct;
+	u16				bcdDevice;
+
+	// constructed descriptors
+	struct usb_device_descriptor 	*device_descriptor;
+	struct usb_device_qualifier_descriptor *device_qualifier_descriptor;
+	struct usb_configuration_instance *configuration_instance_array;
+
+	struct list_head 		drivers;	// linked list 
+};
+
+
+struct usb_function_instance;
+
+/* function driver registration
+ *
+ * Called by function drivers to register themselves when loaded
+ * or de-register when unloading.
+ */
+//int usbd_strings_init(void);
+int usbd_register_function (struct usb_function_driver *);
+void usbd_deregister_function (struct usb_function_driver *);
+
+/* Access to function privdata
+ */
+void *usbd_function_get_privdata(struct usb_function_instance *function);
+void usbd_function_set_privdata(struct usb_function_instance *function, void *privdata);
+/*
+ * Called to queue urb's for send or recv
+ */
+int usbd_send_urb (struct urb *urb);
+int usbd_start_recv (struct urb *);
+
+/*
+ * queury endpoint astatus
+ */
+int usbd_endpoint_halted (struct usb_function_instance *, int);
+int usbd_device_feature (struct usb_function_instance *, int, int);
+
+
+/*
+ * query bus state and status
+ */
+usb_device_state_t usbd_device_state(struct usb_function_instance *);
+usbd_bus_state_t usbd_bus_state(struct usb_function_instance *);
+usb_device_status_t usbd_bus_status(struct usb_function_instance *);
+
+
+/* urb allocation
+ *
+ * Used to allocate and de-allocate urb structures.
+ * usb_cancel_urb is used to cancel a previously submitted data urb for sending.
+ */
+struct urb *usbd_alloc_urb (struct usb_function_instance *, int, int length, int (*callback) (struct urb *, int));
+struct urb *usbd_alloc_urb_ep0 (struct usb_function_instance *, int length, int (*callback) (struct urb *, int));
+void usbd_dealloc_urb (struct urb *urb);
+int usbd_alloc_urb_data (struct urb *, int);
+int usbd_cancel_urb_irq (struct urb *);
+
+/* endpoint information
+ *
+ * Used by function drivers to get specific endpoint information
+ */
+int usbd_endpoint_wMaxPacketSize(struct usb_function_instance *, int, int);
+int usbd_endpoint_bEndpointAddress(struct usb_function_instance *, int, int);
+int usbd_endpoint_transferSize(struct usb_function_instance *, int, int);
+int usbd_endpoint_interface(struct usb_function_instance *, int);
+int usbd_interface_AltSetting(struct usb_function_instance *, int);
+int usbd_ConfigurationValue(struct usb_function_instance *);
+void usbd_endpoint_update(struct usb_function_instance *, int , struct usb_endpoint_descriptor *, int);
+int usbd_high_speed(struct usb_function_instance *);
+
diff -Nru a/drivers/usbd/usbd-mem.h b/drivers/usbd/usbd-mem.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/usbd-mem.h	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,146 @@
+/*
+ * usbd/usbd-mem.h 
+ *
+ *      Copyright (c) 2004 Belcarra
+ *
+ * Adapted from earlier work:
+ *      Copyright (c) 2002, 2003 Belcarra
+ *      Copyright (c) 2000, 2001, 2002 Lineo
+ *      Copyright (c) 2001 Hewlett Packard
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#ifndef MODULE
+#undef __init
+#define __init
+#undef __exit
+#define __exit
+#undef THIS_MODULE
+#define THIS_MODULE 0
+#endif
+
+#define USBD_MODULE_INFO(info) static const char __usbd_module_info[] = info " " USBD_BUILD " %D% %@%";
+
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+#define CATCH(x) while(0) x:
+#define THROW(x) goto x
+#define THROW_IF(e, x) if (e)  goto x; 
+#define BREAK_IF(x) if (x)  break; 
+#define CONTINUE_IF(x) if (x)  continue; 
+#define RETURN_IF(x) if (x)  return; 
+#define RETURN_ZERO_IF(x) if (x)  return 0; 
+#define RETURN_EINVAL_IF(x) if (x)  return -EINVAL; 
+#define RETURN_EFAULT_IF(x) if (x)  return -EFAULT; 
+#define RETURN_ENOMEM_IF(x) if (x)  return -ENOMEM; 
+#define RETURN_EBUSY_IF(x) if (x)  return -EBUSY; 
+#define RETURN_NULL_IF(x) if (x)  return NULL; 
+#define unless(x) if(!(x))
+#define UNLESS(x) if(!(x))
+#define RETURN_UNLESS(x) UNLESS (x)  return; 
+#define RETURN_ZERO_UNLESS(x) UNLESS (x)  return 0; 
+#define CONTINUE_UNLESS(x) UNLESS (x)  continue; 
+
+
+#ifndef likely
+#define likely(x) x
+#endif
+
+#ifndef unlikely 
+#define unlikely(x) x
+#endif
+
+#define ckmalloc(n,f) _ckmalloc(__FUNCTION__, __LINE__, n, f)
+#define lstrdup(str) _lstrdup(__FUNCTION__, __LINE__, str)
+#define lkfree(p) _lkfree(__FUNCTION__, __LINE__, p)
+
+#define MALLOC_TEST
+#undef MALLOC_DEBUG
+
+#ifdef MALLOC_TEST
+extern int usbd_mallocs;
+#endif
+
+
+static __inline__ void *_ckmalloc (char *func, int line, int n, int f)
+{
+	void *p;
+	if ((p = kmalloc (n, f)) == NULL) {
+		return NULL;
+	}
+	memset (p, 0, n);
+#ifdef MALLOC_TEST
+        ++usbd_mallocs;
+#endif
+#ifdef MALLOC_DEBUG
+        printk(KERN_INFO"%s: %p %s %d %d\n", __FUNCTION__, p, func, line, usbd_mallocs);
+#endif
+	return p;
+}
+
+static __inline__ char *_lstrdup (char *func, int line, char *str)
+{
+	int n;
+	char *s;
+	if (str && (n = strlen (str) + 1) && (s = kmalloc (n, GFP_ATOMIC))) {
+#ifdef MALLOC_TEST
+                ++usbd_mallocs;
+#endif
+#ifdef MALLOC_DEBUG
+                printk(KERN_INFO"%s: %p %s %d %d\n", __FUNCTION__, s, func, line, usbd_mallocs);
+#endif
+		return strcpy (s, str);
+	}
+	return NULL;
+}
+
+static __inline__ void _lkfree (char *func, int line, void *p)
+{
+	if (p) {
+#ifdef MALLOC_TEST
+                --usbd_mallocs;
+#endif
+#ifdef MALLOC_DEBUG
+                printk(KERN_INFO"%s: %p %s %d %d\n", __FUNCTION__, p, func, line, usbd_mallocs);
+#endif
+		kfree (p);
+#ifdef MALLOC_TEST
+                if (usbd_mallocs < 0) {
+                        printk(KERN_INFO"%s: %p %s %d %d usbd_mallocs less zero!\n", __FUNCTION__, p, func, line, usbd_mallocs);
+                }
+#endif
+	}
+#ifdef MALLOC_DEBUG
+        else {
+                printk(KERN_INFO"%s: %s %d NULL\n", __FUNCTION__, func, line);
+        }
+#endif
+}
+
+
+
diff -Nru a/drivers/usbd/usbd-procfs.c b/drivers/usbd/usbd-procfs.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/usbd-procfs.c	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,337 @@
+/*
+ * usbd/usbd-procfs.c - USB Device Core Layer
+ *
+ *      Copyright (c) 2004 Belcarra
+ *
+ * Adapted from earlier work:
+ *      Copyright (c) 2002, 2003 Belcarra
+ *      Copyright (c) 2000, 2001, 2002 Lineo
+ *      Copyright (c) 2001 Hewlett Packard
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+
+EXPORT_NO_SYMBOLS;
+
+#include "usbd-export.h"
+#include "usbd-build.h"
+
+#ifdef MODULE
+MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com");
+MODULE_DESCRIPTION ("USB Device Core Support Procfs");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17)
+MODULE_LICENSE("GPL");
+#endif
+
+#endif
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+
+#include <linux/smp_lock.h>
+#include <linux/ctype.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+
+#include "usbd-chap9.h"
+#include "usbd-mem.h"
+#include "usbd.h"
+#include "usbd-func.h"
+#include "usbd-bus.h"
+
+USBD_MODULE_INFO ("usbdprocfs 2.0-beta");
+
+#define MAX_INTERFACES 2
+
+extern struct list_head usbd_function_drivers;
+extern struct usb_bus_instance *usbd_bus_instance;
+
+
+#ifdef CONFIG_USBD_PROCFS
+/* Proc Filesystem *************************************************************************** */
+
+
+/* *
+ * dohex
+ *
+ */
+static void dohexdigit (char *cp, unsigned char val)
+{
+	if (val < 0xa) {
+		*cp = val + '0';
+	} else if ((val >= 0x0a) && (val <= 0x0f)) {
+		*cp = val - 0x0a + 'a';
+	}
+}
+
+/* *
+ * dohex
+ *
+ */
+static void dohexval (char *cp, unsigned char val)
+{
+	dohexdigit (cp++, val >> 4);
+	dohexdigit (cp++, val & 0xf);
+}
+
+/* *
+ * dump_descriptor
+ */
+static int dump_descriptor (char *buf, char *sp)
+{
+        int num = *sp;
+	int len = 0;
+
+        //printk(KERN_INFO"%s: %p %d %d %d\n", __FUNCTION__, buf, *buf, buf[0], num);
+
+	while (sp && num--) {
+		dohexval (buf, *sp++);
+		buf += 2;
+		*buf++ = ' ';
+		len += 3;
+	}
+	len++;
+	*buf = '\n';
+	return len;
+}
+
+/* dump_descriptors
+ */
+static int dump_config_descriptor(char *buf, char *sp)
+{
+        struct usb_configuration_descriptor *config = (struct usb_configuration_descriptor *) sp;
+
+        int wTotalLength = le16_to_cpu(config->wTotalLength);
+        int bConfigurationValue = config->bConfigurationValue;
+        int interface;
+        int class;
+        int endpoint;
+        int total;
+
+        interface = class = endpoint = 0;
+
+        for (total = 0; wTotalLength; ) {
+                //printk(KERN_INFO"%s: wTotalLength: %d total: %d bLength: %d\n", 
+                //                __FUNCTION__, wTotalLength, total, sp[0]);
+                switch (sp[1]) {
+                case USB_DESCRIPTOR_TYPE_CONFIGURATION:
+                        interface = class = endpoint = 0;
+                        total += sprintf(buf + total, "\nConfiguration descriptor [%d      ] ", 
+                                        bConfigurationValue); 
+                        break;
+                case USB_DESCRIPTOR_TYPE_INTERFACE:
+                        class = 0;
+                        total += sprintf(buf + total, "\nInterface descriptor     [%d:%d:%d  ] ", 
+                                        bConfigurationValue, ++interface, class); 
+                        break;
+                case USB_DESCRIPTOR_TYPE_ENDPOINT:
+                        class = endpoint = 0;
+                        total += sprintf(buf + total, "Endpint descriptor       [%d:%d:%d:%d] ", 
+                                        bConfigurationValue, interface, class, ++endpoint); 
+                        break;
+                default:
+                        endpoint = 0;
+                        total += sprintf(buf + total, "Class descriptor         [%d:%d:%d  ] ", 
+                                        bConfigurationValue, interface, ++class); 
+                        break;
+                }
+                total += dump_descriptor(buf + total, sp);
+                wTotalLength -= sp[0];
+                sp += sp[0];
+        }
+        total += sprintf(buf + total, "\n"); 
+        return total;
+}
+
+/* *
+ * usbd_device_proc_read - implement proc file system read.
+ * @file
+ * @buf
+ * @count
+ * @pos
+ *
+ * Standard proc file system read function.
+ *
+ * We let upper layers iterate for us, *pos will indicate which device to return
+ * statistics for.
+ */
+static ssize_t usbd_device_proc_read_functions (struct file *file, char *buf, size_t count, loff_t * pos)
+{
+	unsigned long page;
+	int len = 0;
+	int index;
+
+        u8 config_descriptor[512];
+        int config_size;
+
+	//struct list_head *lhd;
+
+	// get a page, max 4095 bytes of data...
+	if (!(page = get_free_page (GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	len = 0;
+	index = (*pos)++;
+
+	if (index == 0) {
+		len += sprintf ((char *) page + len, "usb-device list\n");
+        }
+
+        //printk(KERN_INFO"%s: index: %d len: %d\n", __FUNCTION__, index, len);
+
+        if (usbd_bus_instance && usbd_bus_instance->function_instance) {
+                int configuration = index;
+                struct usb_function_instance *function_instance = usbd_bus_instance->function_instance;
+                struct usb_function_driver *function_driver = function_instance->function_driver;
+                struct usb_configuration_instance *configuration_instance_array = function_driver->configuration_instance_array;
+                if (configuration_instance_array) {
+
+                        if (index == 0) {
+                                len += sprintf ((char *) page + len, "\nDevice descriptor                  ");
+                                len += dump_descriptor ((char *) page + len, (char *) function_driver->device_descriptor);
+#ifdef CONFIG_USBD_HIGH_SPEED
+                                len += sprintf ((char *) page + len, "\nDevice Qualifier descriptor        ");
+                                len += dump_descriptor ((char *) page + len, 
+                                                (char *) function_driver->device_qualifier_descriptor);
+#endif
+                        }
+                        if (configuration < function_driver->bNumConfigurations) {
+
+                                if ((config_size = usbd_get_descriptor(usbd_bus_instance, config_descriptor, 
+                                                                sizeof(config_descriptor),
+                                                                USB_DESCRIPTOR_TYPE_CONFIGURATION, 0)) > index) {
+                                        len += dump_config_descriptor((char *)page + len, config_descriptor );
+                                }
+#ifdef CONFIG_USBD_HIGH_SPEED
+                                if ((config_size = usbd_get_descriptor(usbd_bus_instance, config_descriptor, 
+                                                                sizeof(config_descriptor),
+                                                                USB_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION, index)) > 0) {
+                                        len += dump_config_descriptor((char *)page + len, config_descriptor );
+                                }
+
+#endif
+                        }
+                        else if (configuration == function_driver->bNumConfigurations) {
+                                int i;
+                                int k;
+                                struct usb_string_descriptor *string_descriptor;
+
+                                //len += sprintf ((char *) page + len, "\n\n");
+
+                                if ((string_descriptor = usbd_get_string (0)) != NULL) {
+                                        len += sprintf ((char *) page + len, "String                   [%2d]      ", 0);
+
+                                        for (k = 0; k < (string_descriptor->bLength / 2) - 1; k++) {
+                                                len += sprintf ((char *) page + len, "%02x %02x ", 
+                                                                string_descriptor->wData[k] >> 8, 
+                                                                string_descriptor->wData[k] & 0xff);
+                                                len++;
+                                        }
+                                        len += sprintf ((char *) page + len, "\n");
+                                }
+
+                                for (i = 1; i < usbd_maxstrings; i++) {
+
+                                        if ((string_descriptor = usbd_get_string (i)) != NULL) {
+
+                                                len += sprintf((char *)page+len, "String                   [%2d:%2d]   ", 
+                                                                i, string_descriptor->bLength);
+
+                                                // bLength = sizeof(struct usb_string_descriptor) + 2*strlen(str)-2;
+
+                                                for (k = 0; k < (string_descriptor->bLength / 2) - 1; k++) {
+                                                        *(char *) (page + len) = (char) string_descriptor->wData[k];
+                                                        len++;
+                                                }
+                                                len += sprintf ((char *) page + len, "\n");
+                                        }
+                                }
+                                len += sprintf((char *)page + len, "\n--\n"); 
+                        }
+                }
+        }
+
+        //printk(KERN_INFO"%s: len: %d count: %d\n", __FUNCTION__, len, count);
+
+        if (len > count) {
+                //printk(KERN_INFO"%s: len > count\n", __FUNCTION__);
+                //printk(KERN_INFO"%s", page);
+                len = -EINVAL;
+        } 
+        else if ((len > 0) && copy_to_user (buf, (char *) page, len)) {
+                //printk(KERN_INFO"%s: EFAULT\n", __FUNCTION__);
+                len = -EFAULT;
+        }
+        else {
+                //printk(KERN_INFO"%s: OK\n", __FUNCTION__);
+        }
+        free_page (page);
+        return len;
+}
+
+static struct file_operations usbd_device_proc_operations_functions = {
+read:usbd_device_proc_read_functions,
+};
+
+#endif
+
+
+
+/* Module init ******************************************************************************* */
+
+
+static int usbd_procfs_init (void)
+{
+#ifdef CONFIG_USBD_PROCFS
+        {
+                struct proc_dir_entry *p;
+
+                // create proc filesystem entries
+                if ((p = create_proc_entry ("usb-functions", 0, 0)) == NULL)
+                        return -ENOMEM;
+                p->proc_fops = &usbd_device_proc_operations_functions;
+        }
+#endif
+        return 0;
+}
+
+static void usbd_procfs_exit (void)
+{
+#ifdef CONFIG_USBD_PROCFS
+        // remove proc filesystem entry
+        remove_proc_entry ("usb-functions", NULL);
+#endif
+}
+
+module_init (usbd_procfs_init);
+module_exit (usbd_procfs_exit);
+
diff -Nru a/drivers/usbd/usbd.c b/drivers/usbd/usbd.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/usbd.c	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,663 @@
+/*
+ * usbd/usbd.c - USB Device Core Layer
+ *
+ *      Copyright (c) 2004 Belcarra
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#if defined(CONFIG_PM) 
+#include <linux/pm.h>
+#endif
+
+#include "usbd-export.h"
+#include "usbd-build.h"
+#include "usbd-chap9.h"
+#include "usbd-mem.h"
+#include "usbd.h"
+#include "usbd-func.h"
+#include "usbd-bus.h"
+#include "usbd-admin.h"
+
+MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com");
+MODULE_DESCRIPTION ("USB Device Core Support");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17)
+MODULE_LICENSE("GPL");
+#endif
+USBD_MODULE_INFO ("usbdcore 2.0-beta");
+
+int usbd_maxstrings = 20;
+extern int usbd_switch_init(void);
+LIST_HEAD (usbd_function_drivers);	// list of all registered function modules
+struct usb_bus_instance *usbd_bus_instance;
+#ifdef MALLOC_TEST
+int usbd_mallocs;
+#endif
+
+/* ******************************************************************************************* */
+/*
+ * USBD Switch will make direct calls to the appropriate modinit/modexit and
+ * bus interface bi_modinit/bi_modexit functions to load and unload as appropriate.
+ *
+ *
+ * The following entry points are exported:
+ *
+ *      int usbd_enable(void)
+ *      int usbd_disable(char *name)
+ *      int usbd_disconnect(char *name)
+ *      int usbd_connect(char *name)
+ *      int usbd_pm_on(char *name)
+ *      int usbd_pm_off(char *name)
+ *
+ *      int usbd_unload(void)
+ *      int usbd_load(char *name)
+ *      int usbd_unplug(char *name)
+ *      int usbd_replug(char *name)
+ *
+ *
+ * If CONFIG_PROCFS is defined then the following commands can be given to /proc/usbd-switch:
+ *
+ *      enable
+ *      enable name
+ *      disable
+ *      disconnect
+ *      connect
+ *
+ *      serial_number
+ *      debug
+ *      nodebug
+ *
+ *
+ * There are four different styles of operations that can be used:
+ *
+ *      1. switch - change or configure the device function by sending any
+ *      one of a number of commands to the switch interface [1] of
+ *      usbd-switch:
+ *
+ *              - echo "serial_number xxxxx" > /proc/usbd-switch
+ *              - echo "enable network_fd" > /proc/usbd-switch
+ *              - echo "disconnect" > /proc/usbd-switch
+ *              - echo "connect" > /proc/usbd-switch
+ *              - echo "disable" > /proc/usbd-switch
+ *      
+ *      serial_number - set the serial number string
+ *      enable function - set current function and enable function and bus interface
+ *	enable          - enable current function and bus interface
+ *      disable         - disable bus interface and current function
+ *      connect         - enable current function and bus interface and connect pullup resistor
+ *      disconnect      - unload bus interface, current function and reset current function,
+ *	                  and disconnect pullup resistor
+ *
+ *      2. simple power management
+ *
+ *              - EXPORT_SYMBOL(usbd_enable);
+ *              - EXPORT_SYMBOL(usbd_disable);
+ *              - EXPORT_SYMBOL(usbd_disconnect);
+ *              - EXPORT_SYMBOL(usbd_connect);
+ *              - EXPORT_SYMBOL(usbd_pm_on);
+ *              - EXPORT_SYMBOL(usbd_pm_off);
+ *
+ *         Deprecated:
+ *
+ *              - EXPORT_SYMBOL(usbd_load);
+ *              - EXPORT_SYMBOL(usbd_unload);
+ *              - EXPORT_SYMBOL(usbd_unplug);
+ *              - EXPORT_SYMBOL(usbd_replug);
+ *
+ *      usbd_enable() - load function and bus interface
+ *      usbd_disable() - unload bus interface and function
+ *
+ *      usbd_connect() - connect
+ *      usbd_disconnect() - disconnect
+ *
+ *      usbd_pm_on() - connect
+ *      usbd_pm_off() - disconnect
+ *
+ *      usbd_load() - load function and bus interface
+ *      usbd_unload() - unload bus interface and function
+ *      usbd_unplug() - disconnect
+ *      usbd_replug() - connect
+ *
+ *      3. official power management CONFIG_PM [not tested yet]
+ *
+ *      Perform unplug and plug functions appropriate for CONFIG_PM actions.
+ *
+ *
+ * N.B.
+ *
+ *      [1] The switch interface is only available when the USB device stack
+ *      and usbd-switch are compiled into the kernel.  It is not available
+ *      when the USB device stack consists of loadable modules.
+ *
+ *      [2] Cable detection requires implementation of architecture specific
+ *      code that knows about the appropriate interrupt, GPIO pins etc.
+ *
+ */
+
+
+extern struct list_head usbd_function_drivers;
+
+
+/*
+ * This is a table of function drivers that are compiled into the kernel.
+ */
+
+#define MAX_FUNC_NAME_LEN  64
+char current_function[MAX_FUNC_NAME_LEN+1];
+int switch_debug = 0;
+
+char *usbd_load_arg;
+
+int switch_exiting;
+struct tq_struct enable_bh;
+struct tq_struct disable_bh;
+
+
+
+/* ******************************************************************************************* */
+
+static DECLARE_MUTEX(usbd_sem);         // protect against multiple administrative actions overlapping
+
+extern struct usb_bus_instance *usbd_bus_instance;
+
+char *usbd_admin_names[] = {
+        "enable", "disable", "connect", "disconnect", "pm_off", "pm_on", "serial_number", 
+};
+
+char *usbd_admin_aliases[] = {
+        "load", "unload", "replug", "unplug", "pm_off", "pm_on", "serial_number_str", 
+};
+
+
+/* usbd_admin - 
+ */
+static int usbd_admin(usbd_admin_t usbd_admin_ndx, char *arg)
+{
+
+        printk(KERN_INFO"%s: %s current: %s arg: %s\n", __FUNCTION__, 
+                        usbd_admin_names[usbd_admin_ndx], 
+                        current_function[0] ? current_function : "(EMPTY)",
+                        (arg && arg[0]) ? arg : "(EMPTY)");
+
+        RETURN_EINVAL_IF(in_interrupt() || down_interruptible(&usbd_sem));
+
+        /*
+         * 
+         */
+	if (usbd_admin_ndx == usbd_admin_enable && (NULL == arg || 0 == *arg)) {
+		// s/enable/enable current_function/
+		arg = current_function;
+	}
+
+        THROW_IF(!usbd_bus_instance, error);
+
+        THROW_IF (!usbd_bus_instance->admin[usbd_admin_ndx] || 
+                        usbd_bus_instance->admin[usbd_admin_ndx](usbd_bus_instance, arg), error);
+
+	if (usbd_admin_ndx == usbd_admin_enable && NULL != arg && arg != current_function) {
+		// update current_function
+		strncpy(current_function,arg,MAX_FUNC_NAME_LEN);
+	}
+
+        up(&usbd_sem);
+        return 0;
+
+        CATCH(error) {
+                printk(KERN_INFO"%s: %d error\n", __FUNCTION__, usbd_admin_ndx);
+
+                up(&usbd_sem);
+                return -ENODEV;
+        }
+}
+
+
+/* usbd_enable - 
+ */
+int usbd_enable(char *arg)
+{
+        printk(KERN_INFO"%s:\n", __FUNCTION__);
+        RETURN_ZERO_IF(switch_exiting);
+        return usbd_admin(usbd_admin_enable, arg);
+}
+
+void usbd_enable_bh(void *arg)
+{
+        usbd_admin(usbd_admin_enable, (char *)arg);
+}
+
+int usbd_enable_irq(char *arg)
+{
+        RETURN_ZERO_IF(enable_bh.sync || switch_exiting);
+        schedule_task(&enable_bh);
+        return 0;
+}
+
+
+/* usbd_disable - 
+ */
+int usbd_disable(char *arg)
+{
+        RETURN_ZERO_IF(switch_exiting);
+        return usbd_admin(usbd_admin_disable, arg);
+}
+
+void usbd_disable_bh(void *arg)
+{
+        usbd_admin(usbd_admin_disable, (char *)arg);
+}
+
+int usbd_disable_irq(char *arg)
+{
+        RETURN_ZERO_IF(disable_bh.sync || switch_exiting);
+        schedule_task(&disable_bh);
+        return 0;
+}
+
+/* usbd_load - 
+ */
+int usbd_load(char *arg)
+{
+        if (arg && strlen(arg)) {
+                if ( usbd_load_arg)
+                        lkfree(usbd_load_arg);
+                usbd_load_arg = lstrdup(arg);
+        }
+        return usbd_admin(usbd_admin_enable, arg);
+}
+
+/* usbd_unload - 
+ */
+int usbd_unload(char *arg)
+{
+        return usbd_admin(usbd_admin_disable, arg);
+}
+
+
+/* usbd_connect - 
+ */
+int usbd_connect(char *arg)
+{
+        return usbd_admin(usbd_admin_connect, arg);
+}
+
+/* usbd_disconnect - 
+ */
+int usbd_disconnect(char *arg)
+{
+        return usbd_admin(usbd_admin_disconnect, arg);
+}
+
+
+/* usbd_replug - 
+ */
+int usbd_replug(char *arg)
+{
+        return usbd_admin(usbd_admin_connect, arg);
+}
+
+/* usbd_unplug - 
+ */
+int usbd_unplug(char *arg)
+{
+        return usbd_admin(usbd_admin_disconnect, arg);
+}
+
+
+
+/* usbd_pm_off - 
+ */
+int usbd_pm_off(char *arg)
+{
+        return usbd_admin(usbd_admin_pm_off, arg);
+}
+
+/* usbd_pm_on - 
+ */
+int usbd_pm_on(char *arg)
+{
+        return usbd_admin(usbd_admin_pm_on, arg);
+}
+
+
+
+
+/* ******************************************************************************************* */
+#ifdef CONFIG_USBD_PROCFS
+
+static int usbd_admin_procfs(char *arg)
+{
+        usbd_admin_t admin;
+        RETURN_EINVAL_IF(!arg || !strlen(arg));
+        
+        for (admin = usbd_admin_enable; admin <= usbd_admin_serial_number; admin++) {
+                char *cp = usbd_admin_names[admin];
+                int len = strlen(cp);
+                if (!strncmp(arg, cp, len)) {
+                        cp = arg + len;
+                        while (cp && ((*cp == ' ') || (*cp == '\t'))) cp++;
+                        return usbd_admin(admin, cp);
+                }
+        }
+
+        for (admin = usbd_admin_enable; admin <= usbd_admin_serial_number; admin++) {
+                char *cp = usbd_admin_aliases[admin];
+                int len = strlen(cp);
+                if (!strncmp(arg, cp, len)) {
+                        cp = arg + len;
+                        while (cp && ((*cp == ' ') || (*cp == '\t'))) cp++;
+                        return usbd_admin(admin, cp);
+                }
+        }
+        
+        return -EINVAL;
+}
+
+/* usbd_proc_switch_write - handle a user switch request
+ */
+static ssize_t usbd_proc_switch_write (struct file *file, const char *buf, size_t count, loff_t * pos)
+{
+
+        char command[MAX_FUNC_NAME_LEN+1]; 
+        size_t n = count;
+        size_t l;
+        char c;
+
+        command[0] = 0;
+        if (n > 0) {
+                l = MIN(n,MAX_FUNC_NAME_LEN);
+                if (copy_from_user (command, buf, l)) {
+                        count = -EFAULT;
+                } 
+                else {
+                        if (l > 0 && command[l-1] == '\n') {
+                                l -= 1;
+                        }
+                        command[l] = 0;
+                        n -= l;
+                        // flush remainder, if any
+                        while (n > 0) {
+                                // Not too efficient, but it shouldn't matter
+                                if (copy_from_user (&c, buf + (count - n), 1)) {
+                                        count = -EFAULT;
+                                        break;
+                                }
+                                n -= 1;
+                        }
+                }
+        }
+
+        if (0 >= count) {
+                printk(KERN_INFO"%s: count <= 0 %d\n", __FUNCTION__, count);
+                return count;
+        }
+
+        //printk(KERN_INFO"%s: %s\n", __FUNCTION__, command);
+        if (!usbd_admin_procfs(command)) {
+                // do nothing
+                //printk(KERN_INFO"%s: back\n", __FUNCTION__);
+        }
+
+
+        /*
+         * debug - increment switch_debug
+         */
+        else if (!strncmp("debug", command, 5)) {
+                switch_debug++;
+                if (switch_debug)
+                        printk(KERN_INFO"%s: debug: %d\n", __FUNCTION__, switch_debug);
+        }
+        /*
+         * nodebug - reset switch_debug
+         */
+        else if (!strncmp("nodebug", command, 7)) {
+                switch_debug = 0;
+                if (switch_debug)
+                        printk(KERN_INFO"%s: no debug\n", __FUNCTION__);
+        }
+
+        return count;
+}
+
+/* usbd_proc_switch_read - implement proc file system read.
+ * Standard proc file system read function.
+ */
+static ssize_t usbd_proc_switch_read (struct file *file, char *buf, size_t count, loff_t * pos)
+{
+	unsigned long page;
+	int len;
+	//int index;
+	char *bp,*ep;
+	struct list_head *lhd;
+
+	if ((*pos)++ > 0) {
+		/* This is the second time, we finished on the first time,
+		   indicate we have no more data by returning 0. */
+		return(0);
+	}
+
+	// get a page, max 4095 bytes of data...
+	if (!(page = get_free_page (GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	bp = (char *) page;
+	ep = bp + 4095;
+
+	bp += sprintf(bp, "USB cable ");
+	if (NULL != usbd_bus_instance &&
+	    NULL != usbd_bus_instance->driver &&
+	    NULL != usbd_bus_instance->driver->bops &&
+	    NULL != usbd_bus_instance->driver->bops->bus_attached &&
+	    usbd_bus_instance->driver->bops->bus_attached(usbd_bus_instance)) {
+		bp += sprintf(bp, "at");
+	} else {
+		bp += sprintf(bp, "de");
+	}
+	bp += sprintf(bp, "tached");
+	bp += sprintf(bp, ", USB pullup resistor ");
+	if (NULL != usbd_bus_instance &&
+	    NULL != usbd_bus_instance->driver &&
+	    NULL != usbd_bus_instance->driver->bops &&
+	    NULL != usbd_bus_instance->driver->bops->bus_connected &&
+	    !usbd_bus_instance->driver->bops->bus_connected(usbd_bus_instance)) {
+		bp += sprintf(bp, "dis");
+	}
+	bp += sprintf(bp, "connected");
+	bp += sprintf(bp, "\nUSB Device function drivers:\n");
+	list_for_each (lhd, &usbd_function_drivers) {
+		struct usb_function_driver *fd = list_entry(lhd, struct usb_function_driver, drivers);
+		char enabled = '-';
+		if (usbd_bus_instance && usbd_bus_instance->function_instance &&
+		    usbd_bus_instance->function_instance->function_driver == fd) {
+			enabled = '+';
+		}
+		if ((ep - bp) < 64) {
+       			printk(KERN_INFO"%s: Too many usbd functions!\n", __FUNCTION__);
+			break;
+		}
+		bp += sprintf(bp," %c [%s]\n",enabled,fd->name);
+	}
+	len = bp - (char *) page;
+
+        if (len > 0 && copy_to_user (buf, (char *) page, len)) {
+                //printk(KERN_INFO"%s: EFAULT\n", __FUNCTION__);
+                len = -EFAULT;
+        } else {
+                //printk(KERN_INFO"%s: OK\n", __FUNCTION__);
+        }
+        free_page (page);
+        return len;
+}
+
+static struct file_operations usbd_proc_switch_functions = {
+                write:usbd_proc_switch_write,
+                read:usbd_proc_switch_read,
+};
+#endif
+
+
+/* ******************************************************************************************* */
+
+#if defined(CONFIG_PM) 
+/* switch_pm_event
+ *
+ * Handle power management event
+ */
+static int switch_pm_event (struct pm_dev *dev, pm_request_t rqst, void *unused)
+{
+	switch (rqst) {
+	case PM_SUSPEND:
+		usbd_pm_off(usbd_load_arg);
+		break;
+	case PM_RESUME:
+                usbd_pm_on(usbd_load_arg);
+                break;
+	}
+	return 0;
+}
+#endif
+
+/* ******************************************************************************************* */
+
+
+
+/* ******************************************************************************************* */
+
+#if defined(CONFIG_PM) 
+struct pm_dev *switch_pm_info;
+#endif
+
+/* usbd_switch_init 
+ *
+ */
+int usbd_switch_init(void)
+{
+
+#ifdef CONFIG_USBD_PROCFS
+        struct proc_dir_entry *p;
+
+        // create proc filesystem entries
+        //printk(KERN_INFO"%s: creating /proc/usbd-switch\n", __FUNCTION__);
+        if ((p = create_proc_entry ("usbd-switch", 0666, 0)) == NULL) {
+                printk(KERN_ERR"%s: creating /proc/usbd-switch failed\n", __FUNCTION__);
+                return -ENOMEM;
+	}
+        p->proc_fops = &usbd_proc_switch_functions;
+#endif
+#ifdef CONFIG_PM
+	switch_pm_info = NULL;
+
+	if (!(switch_pm_info = pm_register (PM_USB_DEV, PM_SYS_UNKNOWN, switch_pm_event))) 
+                return -ENOMEM;
+        
+        switch_pm_info->state = 0;
+#endif
+        enable_bh.routine = usbd_enable_bh;
+        enable_bh.data = NULL;
+        disable_bh.routine = usbd_disable_bh;
+        disable_bh.data = NULL;
+	return 0;
+}
+
+void usbd_switch_exit (void)
+{
+        // This is *only* used for drivers compiled and used as a module.
+#ifdef CONFIG_PM
+        if (switch_pm_info)
+                pm_unregister_all(switch_pm_event);
+	switch_pm_info = NULL;
+#endif
+#ifdef CONFIG_USBD_PROCFS
+        remove_proc_entry("usbd-switch", NULL);
+#endif
+        switch_exiting = 1;
+        while (enable_bh.sync || disable_bh.sync) {
+                printk(KERN_INFO"%s: waiting for bh\n", __FUNCTION__);
+                schedule_timeout(10 * HZ);
+        }
+}
+
+
+
+/* Module init ******************************************************************************* */
+
+int usbd_switch_init(void);
+void usbd_switch_exit (void);
+
+static int usbd_device_init (void)
+{
+        printk (KERN_INFO "%s: %s %s\n", __FUNCTION__, __usbd_module_info, USBD_EXPORT_TAG);
+
+        usbd_switch_init();
+
+        return 0;
+}
+
+static void usbd_device_exit (void)
+{
+        //printk (KERN_INFO "%s: %s exiting\n", __FUNCTION__, __usbd_module_info);
+
+        usbd_switch_exit();
+
+        if ( usbd_load_arg)
+                lkfree(usbd_load_arg);
+
+#ifdef MALLOC_TEST
+        if (usbd_mallocs) printk(KERN_INFO"%s: usbd_mallocs non-zero! %d\n", __FUNCTION__, usbd_mallocs);
+#endif
+}
+
+module_init (usbd_device_init);
+module_exit (usbd_device_exit);
+
+EXPORT_SYMBOL(usbd_function_drivers);
+EXPORT_SYMBOL(usbd_maxstrings);
+
+EXPORT_SYMBOL(usbd_enable);
+EXPORT_SYMBOL(usbd_disable);
+EXPORT_SYMBOL(usbd_enable_irq);
+EXPORT_SYMBOL(usbd_disable_irq);
+
+EXPORT_SYMBOL(usbd_connect);
+EXPORT_SYMBOL(usbd_disconnect);
+EXPORT_SYMBOL(usbd_pm_on);
+EXPORT_SYMBOL(usbd_pm_off);
+
+EXPORT_SYMBOL(usbd_load);
+EXPORT_SYMBOL(usbd_unload);
+EXPORT_SYMBOL(usbd_unplug);
+EXPORT_SYMBOL(usbd_replug);
+
+
+EXPORT_SYMBOL(usbd_bus_instance);
+#ifdef MALLOC_TEST
+EXPORT_SYMBOL(usbd_mallocs);
+#endif
+
+
diff -Nru a/drivers/usbd/usbd.h b/drivers/usbd/usbd.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usbd/usbd.h	Fri Feb 27 14:22:51 2004
@@ -0,0 +1,239 @@
+/*
+ * usbd/usbd.h 
+ *
+ *      Copyright (c) 2004 Belcarra
+ *
+ * Adapted from earlier work:
+ *      Copyright (c) 2002, 2003 Belcarra
+ *      Copyright (c) 2000, 2001, 2002 Lineo
+ *      Copyright (c) 2001 Hewlett Packard
+ *
+ * By: 
+ *      Stuart Lynne <sl@belcarra.com>, 
+ *      Tom Rushworth <tbr@belcarra.com>, 
+ *      Bruce Balden <balden@belcarra.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * This file contains the USB Device Core Common definitions. It also contains definitions for
+ * functions that are commonly used by both Function Drivers and Bus Interface Drivers.
+ *
+ * Specifically:
+ *
+ * 	o public functions exported by the USB Core layer 
+ *
+ * 	o common structures and definitions 
+ *
+ */
+
+
+/* 
+ * Device Events 
+ *
+ * These are defined in the USB Spec (c.f USB Spec 2.0 Figure 9-1).
+ *
+ * There are additional events defined to handle some extra actions we need to have handled.
+ *
+ */
+typedef enum usb_device_event {
+
+	DEVICE_UNKNOWN,		// bi - unknown event
+	DEVICE_INIT,		// bi  - initialize
+	DEVICE_CREATE,		// bi  - 
+	DEVICE_HUB_CONFIGURED,	// bi  - bus has been plugged int
+	DEVICE_RESET,		// bi  - hub has powered our port
+
+	DEVICE_ADDRESS_ASSIGNED,// ep0 - set address setup received
+	DEVICE_CONFIGURED,	// ep0 - set configure setup received
+	DEVICE_SET_INTERFACE,	// ep0 - set interface setup received
+
+	DEVICE_SET_FEATURE,	// ep0 - set feature setup received
+	DEVICE_CLEAR_FEATURE,	// ep0 - clear feature setup received
+
+	DEVICE_DE_CONFIGURED,	// ep0 - set configure setup received for ??
+
+	DEVICE_BUS_INACTIVE,	// bi  - bus in inactive (no SOF packets)
+	DEVICE_BUS_ACTIVITY,	// bi  - bus is active again
+
+	DEVICE_POWER_INTERRUPTION,// bi  - hub has depowered our port
+	DEVICE_HUB_RESET,	// bi  - bus has been unplugged
+	DEVICE_DESTROY,		// bi  - device instance should be destroyed
+	DEVICE_CLOSE,		// bi  - device instance should be destroyed
+
+} usb_device_event_t;
+
+
+
+struct usb_bus_instance;
+struct usb_function_instance;
+
+typedef struct urb_link {
+	struct urb_link *next;
+	struct urb_link *prev;
+} urb_link;
+
+/* URB Status
+ *
+ */
+typedef enum usbd_urb_status {
+        SEND_IN_QUEUE,
+	SEND_IN_PROGRESS,
+	SEND_FINISHED_OK,
+	SEND_FINISHED_ERROR,
+	SEND_FINISHED_CANCELLED,
+	RECV_IN_QUEUE,
+	RECV_IN_PROGRESS,
+	RECV_OK,
+	RECV_ERROR,
+	RECV_CANCELLED
+} usbd_urb_status_t;
+
+
+
+/* USB Data structure 
+ *
+ * This is used for both sending and receiving data. 
+ *
+ * The callback function is used to let the function driver know when transmitted data has been
+ * sent.
+ *
+ * The callback function is set by the alloc_recv function when an urb is allocated for
+ * receiving data for an endpoint and used to call the function driver to inform it that data
+ * has arrived.
+ *
+ * Note that for OUT urbs the buffer is always allocated to a multiple of the packetsize that is
+ * 1 larger than the requested size. This prevents overflow if the host unexpectedly sends a
+ * full sized packet when we are expecting a short one (the host is always right..)
+ */
+
+struct urb {
+
+	struct usb_bus_instance 	*bus;
+	struct usb_function_instance 	*function_instance;
+	struct usb_endpoint_instance 	*endpoint;
+
+	int 				(*callback) (struct urb *, int);
+
+	u8 			        *buffer;	// data received (OUT) or being sent (IN)
+	u32 				request_length; // maximum data expected for OUT
+	u32 				buffer_length;	// allocated size of buffer
+	u32 				actual_length;	// actual data received (OUT or being sent (IN)
+        u32 				flags;
+        
+	void 				*privdata;
+
+	struct urb_link 		link;	
+	usbd_urb_status_t 		status;		// what is the current status of the urb
+	time_t 				jiffies;	// timestamp for when urb was started or finished
+	u16 				framenum;	// SOF framenum when urb was finished
+};
+
+#define USBD_URB_SENDZLP        0x01
+
+
+/* 
+ * Device State (c.f USB Spec 2.0 Figure 9-1)
+ *
+ * What state the usb device is in. 
+ *
+ * Note the state does not change if the device is suspended, we simply set a
+ * flag to show that it is suspended.
+ *
+ */
+typedef enum usb_device_state {
+	STATE_INIT,		// just initialized
+	STATE_CREATED,		// just created
+	STATE_ATTACHED,		// we are attached 
+	STATE_POWERED,		// we have seen power indication (electrical bus signal)
+	STATE_DEFAULT,		// we been reset
+	STATE_ADDRESSED,	// we have been addressed (in default configuration)
+	STATE_CONFIGURED,	// we have seen a set configuration device command
+	STATE_SUSPENDED,
+	STATE_UNKNOWN,		// destroyed
+} usb_device_state_t;
+
+/*
+ * Device status
+ *
+ * Overall state
+ */
+typedef enum usb_device_status {
+	USBD_OPENING,		// we are currently opening
+	USBD_RESETING,		// we are currently opening
+	USBD_OK,		// ok to use
+	USBD_SUSPENDED,		// we are currently suspended
+	USBD_CLOSING,		// we are currently closing
+	USBD_CLOSED,		// we are currently closing
+	USBD_UNKNOWN,
+} usb_device_status_t;
+
+/*
+ * Bus state
+ *
+ * enabled or disabled
+ */
+typedef enum usbd_bus_state {
+        usbd_bus_state_unknown,
+        usbd_bus_state_disabled,
+        usbd_bus_state_enabled
+} usbd_bus_state_t;
+
+
+
+/* Endpoint Request
+ *
+ * An array of these structures is initialized by each function driver to specify the endpoints
+ * it requires. 
+ *
+ * The bus interface driver will attempt to fulfill these requests with the actual endpoints it
+ * has available.
+ *
+ * Note that in most cases the bEndpointAddress should be left as zero except for specialized
+ * function drivers that both require a specific value and know ahead of time that the specified
+ * value is legal for the bus interface driver being used.
+ */
+struct usb_endpoint_request {
+	u8 configuration;		// configuration endpoint will be in
+	u8 interface;			// interface endpoint will be in
+	u8 alternate;			// altsetting for this request
+	u8 bmAttributes;		// endpoint type AND direction
+	u16 fs_requestedTransferSize;	// max full speed transfer size for this endpoint
+	u16 hs_requestedTransferSize;	// max high speed transfer size for this endpoint
+        u8 bEndpointAddress;            // specific bEndpointAddress function driver requires
+	u8 physical;			// physical endpoint used
+};
+
+
+
+struct usb_function_operations;
+struct usb_function_driver;
+
+
+
+/*
+ * USB Bus Interface Driver structures
+ *
+ * Driver description:
+ * 
+ *  struct usb_bus_operations
+ *  struct usb_bus_driver
+ *
+ */
+
+struct usb_bus_operations;
+struct usb_bus_driver;
+extern int usbd_maxstrings;
+
+