nylon kernel patches courtesy Martin Dietze <dietze@4g-systems.com>
authorMichael Lauer <mickey@vanille-media.de>
Mon, 6 Feb 2006 17:58:07 +0000 (17:58 +0000)
committerOpenEmbedded Project <openembedded-devel@lists.openembedded.org>
Mon, 6 Feb 2006 17:58:07 +0000 (17:58 +0000)
packages/linux/linux-mtx-1-2.4.27/23-mtx-1_watchdog_autotrigger.patch [new file with mode: 0644]
packages/linux/linux-mtx-1-2.4.27/24-mtx-1_sysbtn.patch [new file with mode: 0644]
packages/linux/linux-mtx-1-2.4.27/25-mtx-sio2.diff [new file with mode: 0644]
packages/linux/linux-mtx-1-2.4.27/26-usbd-amd-pb1x00-kit-23may2003-update.diff [new file with mode: 0644]
packages/linux/linux-mtx-1-2.4.27/27-usbd-amd-pb1x00-kit-23may2003-usbd.diff [new file with mode: 0644]
packages/linux/linux-mtx-1-2.4.27/29-au1000-pci-config-clear-errors.diff [new file with mode: 0644]
packages/linux/linux-mtx-1-2.4.27/defconfig-mtx-1
packages/linux/linux-mtx-1_2.4.24.bb
packages/linux/linux-mtx-1_2.4.27.bb

diff --git a/packages/linux/linux-mtx-1-2.4.27/23-mtx-1_watchdog_autotrigger.patch b/packages/linux/linux-mtx-1-2.4.27/23-mtx-1_watchdog_autotrigger.patch
new file mode 100644 (file)
index 0000000..c6bb0aa
--- /dev/null
@@ -0,0 +1,98 @@
+--- linux/arch/mips/au1000/mtx-1/mtx-1_watchdog.c.orig 2005-03-22 17:18:01.000000000 +0100
++++ linux/arch/mips/au1000/mtx-1/mtx-1_watchdog.c      2005-07-06 12:10:06.000000000 +0200
+@@ -39,6 +39,7 @@
+ #include <linux/watchdog.h>
+ #include <linux/slab.h>
+ #include <linux/init.h>
++#include <asm/uaccess.h>
+ #include <asm/au1000.h>
+@@ -115,6 +116,8 @@
+ //---------[ File Functions ]-----------------
++static char restart_after_close;
++
+ static int mtx1wd_open (struct inode *inode, struct file *file)
+ {
+       if (MINOR(inode->i_rdev)!=WATCHDOG_MINOR) return -ENODEV;
+@@ -134,6 +137,10 @@
+ static int mtx1wd_release (struct inode *inode, struct file *file) {
+       if (MINOR(inode->i_rdev)==WATCHDOG_MINOR) {
++
++        if (restart_after_close)
++          start_wd_timer ();
++
+       }
+       MOD_DEC_USE_COUNT;
+       return 0;
+@@ -141,22 +148,53 @@
+ static ssize_t mtx1wd_write (struct file *file, const char *buf, size_t count, loff_t *ppos) {
+-      if (ppos!=&file->f_pos)
+-              return -ESPIPE;
+-      if (count) {
+               mtx1_trigger_wd ();
+-              return 1;
++
++      if (count > 0) {
++              char buffer[10];
++              int n = (count>9)?9:count;
++
++              if (copy_from_user (&buffer, buf, n))
++                      return -EFAULT;
++              buffer[n]=0;
++              
++              if (count >= 4 && strncmp("auto", buffer, 4)==0)
++                      restart_after_close = 1;
++              
++              else if (count >= 6 && strncmp("manual", buffer, 6)==0)
++                      restart_after_close = 0;
++              
++              return n;
+       }
+       return 0;
+ }
++static ssize_t mtx1wd_read (struct file *file, char *buf, size_t count, loff_t *ppos)
++{
++      char * state = restart_after_close ? "auto\n" : "manual\n";
++      int n = strlen(state)+1;
++
++      if (file->f_pos >= n)
++              return 0;
++
++      if (count < n)
++              return -EINVAL;
++
++      if(copy_to_user(buf, state, n))
++              return -EFAULT;
++
++      file->f_pos += n;
++
++      return n;
++}
++
+ static struct file_operations mtx1wd_fops = {
+       .owner = THIS_MODULE,
+       .llseek = NULL,
+-      .read = NULL,
++      .read = mtx1wd_read,
+       .write = mtx1wd_write,
+       .readdir = NULL,
+       .poll = NULL,
+@@ -194,6 +232,8 @@
+ {
+       printk("MTX-1 watchdog driver\n");
++      restart_after_close = 0;
++
+       mtx1_enable_wd ();
+       //-- trigger it for the first time.
diff --git a/packages/linux/linux-mtx-1-2.4.27/24-mtx-1_sysbtn.patch b/packages/linux/linux-mtx-1-2.4.27/24-mtx-1_sysbtn.patch
new file mode 100644 (file)
index 0000000..9c08089
--- /dev/null
@@ -0,0 +1,257 @@
+diff -Nru linux-old/arch/mips/au1000/mtx-1/Makefile linux/arch/mips/au1000/mtx-1/Makefile
+--- linux-old/arch/mips/au1000/mtx-1/Makefile  2005-07-19 18:59:27.000000000 +0200
++++ linux/arch/mips/au1000/mtx-1/Makefile      2005-07-07 10:15:01.000000000 +0200
+@@ -15,6 +15,6 @@
+ O_TARGET := mtx-1.o
+-obj-y := init.o board_setup.o irqmap.o mtx-1_watchdog.o
++obj-y := init.o board_setup.o irqmap.o mtx-1_watchdog.o mtx-1_sysbtn.o
+ include $(TOPDIR)/Rules.make
+diff -Nru linux-old/arch/mips/au1000/mtx-1/irqmap.c linux/arch/mips/au1000/mtx-1/irqmap.c
+--- linux-old/arch/mips/au1000/mtx-1/irqmap.c  2005-07-19 17:42:05.000000000 +0200
++++ linux/arch/mips/au1000/mtx-1/irqmap.c      2005-07-06 13:37:25.000000000 +0200
+@@ -50,10 +50,11 @@
+ /* Need to define this.
+ */
+ au1xxx_irq_map_t au1xxx_irq_map[] = {
++      { AU1500_GPIO_207, INTC_INT_RISE_AND_FALL_EDGE, 0 },
+       { 0, 0, 0}
+ };
+-int au1xxx_nr_irqs = 0;
++int au1xxx_nr_irqs = 1;
+ #ifdef CONFIG_PCI
+diff -Nru linux-old/arch/mips/au1000/mtx-1/mtx-1_sysbtn.c linux/arch/mips/au1000/mtx-1/mtx-1_sysbtn.c
+--- linux-old/arch/mips/au1000/mtx-1/mtx-1_sysbtn.c    1970-01-01 01:00:00.000000000 +0100
++++ linux/arch/mips/au1000/mtx-1/mtx-1_sysbtn.c        2005-07-07 10:29:24.000000000 +0200
+@@ -0,0 +1,226 @@
++/*
++ *      Driver for the MTX-1 System Button.
++ *
++ *      (c) Copyright 2005 4G Systems <info@4g-systems.biz>, All Rights Reserved.
++ *                              http://www.4g-systems.biz
++ *
++ *      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.
++ *
++ *      Neither Michael Stickel nor 4G Systeme GmbH admit liability nor provide
++ *      warranty for any of this software. This material is provided
++ *      "AS-IS" and at no charge.
++ *
++ *      (c) Copyright 2005    4G Systems <info@4g-systems.biz>
++ *
++ *      Release 0.01.
++ *
++ *      Author: Michael Stickel  michael.stickel@4g-systems.biz
++ *
++ *
++ *      After the module is loaded there is a device /dev/misc/btn
++ *      that can be read. It returns one char '1' if the button
++ *      has been pressed an '0' if it has been released.
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/version.h>
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/poll.h>
++#include <linux/sched.h>
++#include <linux/miscdevice.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/irq.h>
++
++#include <asm/uaccess.h>
++
++#include <asm/au1000.h>
++
++
++#ifndef FALSE
++# define FALSE (0)
++#endif
++
++#ifndef TRUE
++# define TRUE (!FALSE)
++#endif
++
++
++//---------[ declarations ]-----------------
++
++
++static DECLARE_WAIT_QUEUE_HEAD(mtx1btn_wait_queue);
++static char state_changed;
++static char last_value;
++static char is_inuse;
++
++
++//---------[ Hardware Functions ]-----------------
++
++// The MTX-1 Button is attached to GPIO207.
++#define MTX1_GPIO2_SYSBTN (7)
++#define MTX1_SYSBTN_IRQ   (AU1500_GPIO_207)
++
++
++static char mtx1_getbtn (int btnno)
++{
++  if (btnno==0) {
++    return (au_readl(GPIO2_PINSTATE) & (1<<MTX1_GPIO2_SYSBTN)) ? 0 : 1;
++  }
++  return 0;
++}
++
++static void mtx1_initbuttons (void)
++{
++  au_writel (au_readl(GPIO2_DIR) & ~(1<<MTX1_GPIO2_SYSBTN), GPIO2_DIR);
++}
++
++
++//---------[ Interrupt handling ]-----------------
++
++
++static void mtx1_btn_interrupt (int irq, void *private, struct pt_regs *regs)
++{
++      char value = mtx1_getbtn(0);
++      if (last_value != value)
++      {
++              last_value = value;
++              state_changed = 1;
++              wake_up (&mtx1btn_wait_queue);
++      }
++//    kill_fasync(&async_queue, SIGIO, POLL_OUT);
++}
++
++
++static int mtx1_btn_startirq (void)
++{
++      if (!request_irq (MTX1_SYSBTN_IRQ, mtx1_btn_interrupt, 0 /* | SA_INTERRUPT */, "mtx1btn", (void *)&state_changed)) {
++              return 0;
++    }
++      return -1;
++}
++
++static int mtx1_btn_stopirq  (void)
++{
++      free_irq(MTX1_SYSBTN_IRQ, (void *)&state_changed);
++      return 0;
++}
++
++
++
++//---------[ File Functions ]-----------------
++
++
++static int mtx1sysbtn_minor = -1;
++
++
++static int mtx1sysbtn_open (struct inode *inode, struct file *file)
++{
++      if (MINOR(inode->i_rdev)!=mtx1sysbtn_minor) return -ENODEV;
++      if (is_inuse) return -EBUSY;
++      is_inuse=1;
++      last_value = mtx1_getbtn(0);
++      state_changed = 0;
++      MOD_INC_USE_COUNT;
++      return 0;
++}
++
++
++static int mtx1sysbtn_release (struct inode *inode, struct file *file) {
++      if (MINOR(inode->i_rdev)==mtx1sysbtn_minor) {
++              is_inuse=0;
++      }
++      MOD_DEC_USE_COUNT;
++      return 0;
++}
++
++
++static ssize_t mtx1sysbtn_read (struct file *file, char *buf, size_t count, loff_t *ppos)
++{
++      if (count < 1)
++              return -EINVAL;
++      if (!state_changed)
++              interruptible_sleep_on (&mtx1btn_wait_queue);
++      state_changed = 0;
++      char c = last_value ? '1' : '0'; /* mtx1_getbtn(0) */
++      if(copy_to_user(buf, &c, 1))
++              return -EFAULT;
++      return 1;
++}
++
++
++static unsigned int mtx1sysbtn_poll (struct file *file, poll_table * wait)
++{
++  unsigned int mask = 0;
++
++  poll_wait (file, &mtx1btn_wait_queue, wait);
++
++  if (state_changed) // state changed since last time.
++    mask |= POLLIN | POLLRDNORM;
++
++  return mask;
++}
++
++
++static struct file_operations mtx1sysbtn_fops = {
++      .owner = THIS_MODULE,
++      .llseek = NULL,
++      .read = mtx1sysbtn_read,
++      .write = NULL,
++      .readdir = NULL,
++      .poll = mtx1sysbtn_poll,
++      .ioctl = NULL,
++      .mmap = NULL,
++      .open = mtx1sysbtn_open,
++      .flush = NULL,
++      .release = mtx1sysbtn_release
++};
++
++
++static struct miscdevice mtx1sysbtn_miscdev = {
++      MISC_DYNAMIC_MINOR /* SYSBTN_MINOR */ ,
++      "btn",
++      &mtx1sysbtn_fops
++};
++
++
++
++//---------[ Module Functions ]-----------------
++
++
++void __exit cleanup_mtx1_sysbtn (void)
++{
++      is_inuse = 1;
++      mtx1_btn_stopirq ();
++      misc_deregister(&mtx1sysbtn_miscdev);
++}
++
++
++int __init init_mtx1_sysbtn (void)
++{
++      printk("MTX-1 System Button driver\n");
++      is_inuse = 1;
++      mtx1_initbuttons ();
++      if (misc_register (&mtx1sysbtn_miscdev) >= 0) {
++              mtx1sysbtn_minor = mtx1sysbtn_miscdev.minor;
++              if (mtx1_btn_startirq () == 0) {
++                      is_inuse=0;
++                      return 0;
++              }
++              misc_deregister(&mtx1sysbtn_miscdev);
++      }
++      return 1;
++}
++
++__initcall(init_mtx1_sysbtn);
++__exitcall(cleanup_mtx1_sysbtn);
++
++MODULE_AUTHOR("Michael Stickel");
++MODULE_DESCRIPTION("Driver for the MTX-1 system button");
++MODULE_LICENSE("GPL");
++EXPORT_NO_SYMBOLS;
diff --git a/packages/linux/linux-mtx-1-2.4.27/25-mtx-sio2.diff b/packages/linux/linux-mtx-1-2.4.27/25-mtx-sio2.diff
new file mode 100644 (file)
index 0000000..4f4775a
--- /dev/null
@@ -0,0 +1,12 @@
+--- linux.old/arch/mips/au1000/mtx-1/board_setup.c     2005-08-10 15:22:14.014147000 +0200
++++ linux/arch/mips/au1000/mtx-1/board_setup.c 2005-08-10 15:26:34.346571264 +0200
+@@ -78,8 +78,7 @@
+       // initialize sys_pinfunc:
+       // disable second ethernet port (SYS_PF_NI2)
+-      // set U3/GPIO23 to GPIO23 (SYS_PF_U3)
+-      au_writel( SYS_PF_NI2 | SYS_PF_U3, SYS_PINFUNC );
++      au_writel( SYS_PF_NI2, SYS_PINFUNC );
+       // initialize GPIO
+       au_writel( 0xFFFFFFFF, SYS_TRIOUTCLR );
diff --git a/packages/linux/linux-mtx-1-2.4.27/26-usbd-amd-pb1x00-kit-23may2003-update.diff b/packages/linux/linux-mtx-1-2.4.27/26-usbd-amd-pb1x00-kit-23may2003-update.diff
new file mode 100644 (file)
index 0000000..feaf08c
--- /dev/null
@@ -0,0 +1,130 @@
+diff -Nru a/arch/mips/au1000/common/irq.c b/arch/mips/au1000/common/irq.c
+--- a/arch/mips/au1000/common/irq.c    Fri Feb 27 14:22:51 2004
++++ b/arch/mips/au1000/common/irq.c    Fri Feb 27 14:22:51 2004
+@@ -458,7 +458,14 @@
+       intc0_req0 |= au_readl(IC0_REQ0INT);
+       if (!intc0_req0) return;
+-
++#if 0
++        /* 
++         * This is no longer required. The find first bit operation
++         * is almost as fast at finding the correct interrupt, whereas
++         * leaving this in adds a small amount of overhead to all other
++         * interrupts and therefore induces more latency which may
++         * actually cause USB problems.
++         */
+       /*
+        * Because of the tight timing of SETUP token to reply
+        * transactions, the USB devices-side packet complete
+@@ -469,7 +476,7 @@
+               do_IRQ(AU1000_USB_DEV_REQ_INT, regs);
+               return;
+       }
+-
++#endif
+       irq = au_ffs(intc0_req0) - 1;
+       intc0_req0 &= ~(1<<irq);
+       do_IRQ(irq, regs);
+diff -Nru a/arch/mips/defconfig-bosporus b/arch/mips/defconfig-bosporus
+--- a/arch/mips/defconfig-bosporus     Fri Feb 27 14:22:51 2004
++++ b/arch/mips/defconfig-bosporus     Fri Feb 27 14:22:51 2004
+@@ -606,8 +606,6 @@
+ # CONFIG_SERIAL_TXX9_CONSOLE is not set
+ CONFIG_AU1X00_UART=y
+ CONFIG_AU1X00_SERIAL_CONSOLE=y
+-# CONFIG_AU1X00_USB_TTY is not set
+-# CONFIG_AU1X00_USB_RAW is not set
+ # CONFIG_TXX927_SERIAL is not set
+ CONFIG_UNIX98_PTYS=y
+ CONFIG_UNIX98_PTY_COUNT=256
+diff -Nru a/arch/mips/defconfig-db1000 b/arch/mips/defconfig-db1000
+--- a/arch/mips/defconfig-db1000       Fri Feb 27 14:22:51 2004
++++ b/arch/mips/defconfig-db1000       Fri Feb 27 14:22:51 2004
+@@ -509,8 +509,6 @@
+ # CONFIG_SERIAL_TXX9_CONSOLE is not set
+ CONFIG_AU1X00_UART=y
+ CONFIG_AU1X00_SERIAL_CONSOLE=y
+-# CONFIG_AU1X00_USB_TTY is not set
+-# CONFIG_AU1X00_USB_RAW is not set
+ # CONFIG_TXX927_SERIAL is not set
+ CONFIG_UNIX98_PTYS=y
+ CONFIG_UNIX98_PTY_COUNT=256
+diff -Nru a/arch/mips/defconfig-db1100 b/arch/mips/defconfig-db1100
+--- a/arch/mips/defconfig-db1100       Fri Feb 27 14:22:51 2004
++++ b/arch/mips/defconfig-db1100       Fri Feb 27 14:22:51 2004
+@@ -516,8 +516,6 @@
+ # CONFIG_SERIAL_TXX9_CONSOLE is not set
+ CONFIG_AU1X00_UART=y
+ CONFIG_AU1X00_SERIAL_CONSOLE=y
+-# CONFIG_AU1X00_USB_TTY is not set
+-# CONFIG_AU1X00_USB_RAW is not set
+ # CONFIG_TXX927_SERIAL is not set
+ CONFIG_UNIX98_PTYS=y
+ CONFIG_UNIX98_PTY_COUNT=256
+diff -Nru a/arch/mips/defconfig-db1500 b/arch/mips/defconfig-db1500
+--- a/arch/mips/defconfig-db1500       Fri Feb 27 14:22:51 2004
++++ b/arch/mips/defconfig-db1500       Fri Feb 27 14:22:51 2004
+@@ -506,8 +506,6 @@
+ # CONFIG_SERIAL_TXX9_CONSOLE is not set
+ CONFIG_AU1X00_UART=y
+ CONFIG_AU1X00_SERIAL_CONSOLE=y
+-# CONFIG_AU1X00_USB_TTY is not set
+-# CONFIG_AU1X00_USB_RAW is not set
+ # CONFIG_TXX927_SERIAL is not set
+ CONFIG_UNIX98_PTYS=y
+ CONFIG_UNIX98_PTY_COUNT=256
+diff -Nru a/arch/mips/defconfig-pb1000 b/arch/mips/defconfig-pb1000
+--- a/arch/mips/defconfig-pb1000       Fri Feb 27 14:22:51 2004
++++ b/arch/mips/defconfig-pb1000       Fri Feb 27 14:22:51 2004
+@@ -569,8 +569,6 @@
+ # CONFIG_SERIAL_TXX9_CONSOLE is not set
+ CONFIG_AU1X00_UART=y
+ CONFIG_AU1X00_SERIAL_CONSOLE=y
+-# CONFIG_AU1X00_USB_TTY is not set
+-# CONFIG_AU1X00_USB_RAW is not set
+ # CONFIG_TXX927_SERIAL is not set
+ CONFIG_UNIX98_PTYS=y
+ CONFIG_UNIX98_PTY_COUNT=256
+diff -Nru a/arch/mips/defconfig-pb1100 b/arch/mips/defconfig-pb1100
+--- a/arch/mips/defconfig-pb1100       Fri Feb 27 14:22:51 2004
++++ b/arch/mips/defconfig-pb1100       Fri Feb 27 14:22:51 2004
+@@ -515,8 +515,6 @@
+ # CONFIG_SERIAL_TXX9_CONSOLE is not set
+ CONFIG_AU1X00_UART=y
+ CONFIG_AU1X00_SERIAL_CONSOLE=y
+-# CONFIG_AU1X00_USB_TTY is not set
+-# CONFIG_AU1X00_USB_RAW is not set
+ # CONFIG_TXX927_SERIAL is not set
+ CONFIG_UNIX98_PTYS=y
+ CONFIG_UNIX98_PTY_COUNT=256
+diff -Nru a/arch/mips/defconfig-pb1500 b/arch/mips/defconfig-pb1500
+--- a/arch/mips/defconfig-pb1500       Fri Feb 27 14:22:51 2004
++++ b/arch/mips/defconfig-pb1500       Fri Feb 27 14:22:51 2004
+@@ -619,8 +619,6 @@
+ # CONFIG_SERIAL_TXX9_CONSOLE is not set
+ CONFIG_AU1X00_UART=y
+ CONFIG_AU1X00_SERIAL_CONSOLE=y
+-# CONFIG_AU1X00_USB_TTY is not set
+-# CONFIG_AU1X00_USB_RAW is not set
+ # CONFIG_TXX927_SERIAL is not set
+ CONFIG_UNIX98_PTYS=y
+ CONFIG_UNIX98_PTY_COUNT=256
+diff -Nru a/drivers/char/Config.in b/drivers/char/Config.in
+--- a/drivers/char/Config.in   Fri Feb 27 14:22:51 2004
++++ b/drivers/char/Config.in   Fri Feb 27 14:22:51 2004
+@@ -91,14 +91,6 @@
+        if [ "$CONFIG_AU1X00_UART" = "y" ]; then
+           bool '        Enable Au1x00 serial console' CONFIG_AU1X00_SERIAL_CONSOLE
+          fi
+-         dep_tristate '  Au1x00 USB TTY Device support' CONFIG_AU1X00_USB_TTY $CONFIG_SOC_AU1X00
+-          if [ "$CONFIG_AU1000_USB_TTY" != "y" ]; then
+-             dep_tristate '  Au1x00 USB Raw Device support' CONFIG_AU1X00_USB_RAW $CONFIG_SOC_AU1X00
+-          fi
+-          if [ "$CONFIG_AU1X00_USB_TTY" != "n" -o \
+-               "$CONFIG_AU1X00_USB_RAW" != "n" ]; then
+-               define_bool CONFIG_AU1X00_USB_DEVICE y
+-          fi
+       fi
+       bool '  TXx927 SIO support' CONFIG_TXX927_SERIAL 
+       if [ "$CONFIG_TXX927_SERIAL" = "y" ]; then
diff --git a/packages/linux/linux-mtx-1-2.4.27/27-usbd-amd-pb1x00-kit-23may2003-usbd.diff b/packages/linux/linux-mtx-1-2.4.27/27-usbd-amd-pb1x00-kit-23may2003-usbd.diff
new file mode 100644 (file)
index 0000000..7766f71
--- /dev/null
@@ -0,0 +1,15816 @@
+/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;
++
++
diff --git a/packages/linux/linux-mtx-1-2.4.27/29-au1000-pci-config-clear-errors.diff b/packages/linux/linux-mtx-1-2.4.27/29-au1000-pci-config-clear-errors.diff
new file mode 100644 (file)
index 0000000..5da1cd6
--- /dev/null
@@ -0,0 +1,15 @@
+--- linux/arch/mips/au1000/common/pci_ops.c.orig       2005-08-24 17:36:25.000000000 +0200
++++ linux/arch/mips/au1000/common/pci_ops.c    2005-08-24 17:37:38.000000000 +0200
+@@ -259,7 +259,11 @@
+               *data = 0xffffffff;
+               error = -1;
+       } else if ((status >> 28) & 0xf) {
+-              DBG("PCI ERR detected: status %x\n", status);
++              DBG("PCI ERR detected: device %d, status %x\n", device, ((status >> 28) & 0xf));
++
++              /* clear errors */
++              au_writel(status & 0xf000ffff, Au1500_PCI_STATCMD);
++
+               *data = 0xffffffff;
+               error = -1;
+       } 
index 1323477..d31e979 100644 (file)
@@ -133,7 +133,12 @@ CONFIG_HOTPLUG=y
 #
 # PCMCIA/CardBus support
 #
-# CONFIG_PCMCIA is not set
+CONFIG_PCMCIA=m
+CONFIG_CARDBUS=y
+# CONFIG_TCIC is not set
+# CONFIG_I82092 is not set
+# CONFIG_I82365 is not set
+# CONFIG_PCMCIA_AU1X00 is not set
 
 #
 # PCI Hotplug Support
@@ -156,7 +161,7 @@ CONFIG_BINFMT_ELF=y
 # CONFIG_MIPS32_O32 is not set
 # CONFIG_MIPS32_N32 is not set
 # CONFIG_BINFMT_ELF32 is not set
-# CONFIG_BINFMT_MISC is not set
+CONFIG_BINFMT_MISC=m
 # CONFIG_OOM_KILLER is not set
 # CONFIG_CMDLINE_BOOL is not set
 # CONFIG_PM is not set
@@ -183,7 +188,7 @@ CONFIG_MTD_CFI=y
 # CONFIG_MTD_JEDECPROBE is not set
 CONFIG_MTD_GEN_PROBE=y
 # CONFIG_MTD_CFI_ADV_OPTIONS is not set
-# CONFIG_MTD_CFI_INTELEXT is not set
+CONFIG_MTD_CFI_INTELEXT=y
 CONFIG_MTD_CFI_AMDSTD=y
 CONFIG_MTD_CFI_AMDSTD_RETRY=y
 CONFIG_MTD_CFI_AMDSTD_RETRY_MAX=5
@@ -551,6 +556,11 @@ CONFIG_SR_EXTRA_DEVS=2
 # CONFIG_SCSI_NSP32 is not set
 # CONFIG_SCSI_DEBUG is not set
 
+#
+# PCMCIA SCSI adapter support
+#
+# CONFIG_SCSI_PCMCIA is not set
+
 #
 # Fusion MPT device support
 #
@@ -655,6 +665,9 @@ CONFIG_AIRO=m
 # CONFIG_PLX_HERMES is not set
 # CONFIG_TMD_HERMES is not set
 # CONFIG_PCI_HERMES is not set
+# CONFIG_PCMCIA_HERMES is not set
+# CONFIG_AIRO_CS is not set
+# CONFIG_PCMCIA_ATMEL is not set
 CONFIG_NET_WIRELESS=y
 
 #
@@ -670,6 +683,11 @@ CONFIG_SHAPER=m
 #
 # CONFIG_WAN is not set
 
+#
+# PCMCIA network device support
+#
+# CONFIG_NET_PCMCIA is not set
+
 #
 # Amateur Radio support
 #
@@ -836,6 +854,12 @@ CONFIG_SENSORS_PCF8591=m
 # Direct Rendering Manager (XFree86 DRI support)
 #
 # CONFIG_DRM is not set
+
+#
+# PCMCIA character devices
+#
+# CONFIG_PCMCIA_SERIAL_CS is not set
+# CONFIG_SYNCLINK_CS is not set
 CONFIG_AU1X00_GPIO=m
 # CONFIG_TS_AU1X00_ADS7846 is not set
 
@@ -1090,7 +1114,6 @@ CONFIG_USB_DEVICEFS=y
 # CONFIG_USB_UHCI is not set
 # CONFIG_USB_UHCI_ALT is not set
 CONFIG_USB_OHCI=y
-CONFIG_USB_NON_PCI_OHCI=y
 CONFIG_USB_AUDIO=m
 CONFIG_USB_EMI26=m
 CONFIG_USB_MIDI=m
@@ -1186,6 +1209,69 @@ CONFIG_USB_LCD=m
 #
 # CONFIG_USB_GADGET is not set
 
+#
+# USB clients (devices, not hosts)
+#
+CONFIG_USBD=m
+# CONFIG_USBD_HIGH_SPEED is not set
+# CONFIG_USBD_NO_SERIAL_NUMBER is not set
+CONFIG_USBD_SERIAL_NUMBER_STR=""
+CONFIG_USBD_MAXPOWER=0
+CONFIG_USBD_PROCFS=y
+CONFIG_USBD_PROCFSM=m
+
+#
+# Network Function
+#
+CONFIG_USBD_NETWORK=m
+CONFIG_USBD_NETWORK_VENDORID=12b9
+CONFIG_USBD_NETWORK_PRODUCTID=f001
+CONFIG_USBD_NETWORK_BCDDEVICE=0100
+CONFIG_USBD_NETWORK_MANUFACTURER="Belcarra"
+## CONFIG_USBD_NETWORK_PRODUCT_NAME="Belcarra BLAN Device"
+## CONFIG_USBD_NETWORK_BLAN=y
+## CONFIG_USBD_NETWORK_BLAN_DESC="BLAN Net Cfg"
+## CONFIG_USBD_NETWORK_BLAN_INTF="Comm/Data Intf"
+# CONFIG_USBD_NETWORK_BLAN_CRC is not set
+# CONFIG_USBD_NETWORK_BLAN_DO_NOT_SETTIME is not set
+# CONFIG_USBD_NETWORK_BLAN_HOSTNAME is not set
+# CONFIG_USBD_NETWORK_BLAN_NOBRIDGE is not set
+CONFIG_USBD_NETWORK_SAFE=y
+CONFIG_USBD_NETWORK_SAFE_NOBRIDGE=y
+# CONFIG_USBD_NETWORK_CDC is not set
+# CONFIG_USBD_NETWORK_BASIC is not set
+# CONFIG_USBD_NETWORK_BASIC2 is not set
+# CONFIG_USBD_NETWORK_START_SINGLE is not set
+# CONFIG_USBD_NETWORK_EP0TEST is not set
+
+#
+# CDC ACM Function
+#                 
+CONFIG_USBD_ACM=m
+CONFIG_USBD_ACM_VENDORID=12b9
+CONFIG_USBD_ACM_PRODUCTID=f002
+CONFIG_USBD_ACM_BCDDEVICE=0100
+CONFIG_USBD_ACM_MANUFACTURER="Belcarra"
+CONFIG_USBD_ACM_PRODUCT_NAME="Belcarra ACM Device"
+CONFIG_USBD_ACM_DESC="Acm Cfg"
+CONFIG_USBD_ACM_COMM_INTF="Comm Intf"
+CONFIG_USBD_ACM_DATA_INTF="Data Intf"
+# CONFIG_USBD_ACM_TRACE is not set
+
+#
+# Random Mouse Function
+#
+# CONFIG_USBD_MOUSE is not set
+
+#
+# AMD AU1X000 Bus Interface
+#
+CONFIG_USBD_AU1X00_BUS=m
+CONFIG_USBD_AU1X00_SCLOCK=400
+CONFIG_AU1000_USB_DEVICE=y
+CONFIG_AU1X00_USB_DEVICE=y
+# CONFIG_USBD_BI_REGISTER_TRACE is not set
+
 #
 # Bluetooth support
 #
index 0a9e9a4..b98f9fb 100644 (file)
@@ -26,7 +26,7 @@ SRC_URI = "cvs://cvs:cvs@ftp.linux-mips.org/home/cvs;module=linux;tag=linux_2_4_
 
 S = "${WORKDIR}/linux"
 
-inherit kernel
+inherit module-base kernel
 
 PACKAGE_ARCH = "mtx-1"
 ARCH = "mips"
index a73bdf6..3101563 100644 (file)
@@ -3,7 +3,10 @@ MAINTAINER = "Bruno Randolf <bruno.randolf@4g-systems.biz>"
 HOMEPAGE = "http://meshcube.org/meshwiki/"
 LICENSE = "GPL"
 KV = "${PV}"
-PR = "r3"
+PR = "r11"
+inherit module-base kernel
+
+PROVIDES = "virtual/kernel"
 RDEPENDS = "mtd-utils"
 
 SRC_URI = "cvs://cvs@ftp.linux-mips.org/home/cvs;module=linux;tag=linux_2_4_27 \
@@ -27,21 +30,29 @@ SRC_URI = "cvs://cvs@ftp.linux-mips.org/home/cvs;module=linux;tag=linux_2_4_27 \
        file://19-kernel-make-depend.diff;patch=1 \
        file://20-au1x00_ethernet_tx_stats.diff;patch=1 \
        file://21-mtx-1-watchdog.diff;patch=1 \
+       file://23-mtx-1_watchdog_autotrigger.patch;patch=1 \
+       file://24-mtx-1_sysbtn.patch;patch=1 \
+       file://25-mtx-sio2.diff;patch=1 \
+       file://26-usbd-amd-pb1x00-kit-23may2003-update.diff;patch=1 \
+       file://27-usbd-amd-pb1x00-kit-23may2003-usbd.diff;patch=1 \
+       file://29-au1000-pci-config-clear-errors.diff;patch=1 \
        file://defconfig-mtx-1"
 
 S = "${WORKDIR}/linux"
 
-inherit kernel
-
 COMPATIBLE_HOST = "mipsel.*-linux"
-PACKAGE_ARCH = "mtx-1"
 ARCH = "mips"
 KERNEL_OUTPUT = "arch/mips/zboot/images/mtx-1.flash.bin"
 KERNEL_IMAGETYPE = "zImage.flash"
 KERNEL_IMAGEDEST = "tmp"
 
+MTX_KERNEL_NON_PCI_OHCI = "yes"
+
 do_configure_prepend() {
         install -m 0644 ${WORKDIR}/defconfig-mtx-1 ${S}/.config
+       if [ "x${MTX_KERNEL_NON_PCI_OHCI}" == "xyes" ]; then
+               echo "CONFIG_USB_NON_PCI_OHCI=y" >> ${S}/.config
+       fi
 }
 
 pkg_postinst_kernel() {