summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorMichael Lauer <mickey@vanille-media.de>2006-02-06 17:58:07 +0000
committerOpenEmbedded Project <openembedded-devel@lists.openembedded.org>2006-02-06 17:58:07 +0000
commitacee7a5c93e24fecf629468492d4354c8797b13a (patch)
tree28049bc2e7ae85c9e4f9870c15b9ae3cc39c9fbb /packages
parent8bfbe7716a29b764f269a7a37f0b6a5d5012b329 (diff)
nylon kernel patches courtesy Martin Dietze <dietze@4g-systems.com>
Diffstat (limited to 'packages')
-rw-r--r--packages/linux/linux-mtx-1-2.4.27/23-mtx-1_watchdog_autotrigger.patch98
-rw-r--r--packages/linux/linux-mtx-1-2.4.27/24-mtx-1_sysbtn.patch257
-rw-r--r--packages/linux/linux-mtx-1-2.4.27/25-mtx-sio2.diff12
-rw-r--r--packages/linux/linux-mtx-1-2.4.27/26-usbd-amd-pb1x00-kit-23may2003-update.diff130
-rw-r--r--packages/linux/linux-mtx-1-2.4.27/27-usbd-amd-pb1x00-kit-23may2003-usbd.diff15816
-rw-r--r--packages/linux/linux-mtx-1-2.4.27/29-au1000-pci-config-clear-errors.diff15
-rw-r--r--packages/linux/linux-mtx-1-2.4.27/defconfig-mtx-194
-rw-r--r--packages/linux/linux-mtx-1_2.4.24.bb2
-rw-r--r--packages/linux/linux-mtx-1_2.4.27.bb19
9 files changed, 16434 insertions, 9 deletions
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
index 0000000000..c6bb0aae68
--- /dev/null
+++ b/packages/linux/linux-mtx-1-2.4.27/23-mtx-1_watchdog_autotrigger.patch
@@ -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
index 0000000000..9c0808942a
--- /dev/null
+++ b/packages/linux/linux-mtx-1-2.4.27/24-mtx-1_sysbtn.patch
@@ -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
index 0000000000..4f4775a960
--- /dev/null
+++ b/packages/linux/linux-mtx-1-2.4.27/25-mtx-sio2.diff
@@ -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
index 0000000000..feaf08c563
--- /dev/null
+++ b/packages/linux/linux-mtx-1-2.4.27/26-usbd-amd-pb1x00-kit-23may2003-update.diff
@@ -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
index 0000000000..7766f71846
--- /dev/null
+++ b/packages/linux/linux-mtx-1-2.4.27/27-usbd-amd-pb1x00-kit-23may2003-usbd.diff
@@ -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
index 0000000000..5da1cd61f8
--- /dev/null
+++ b/packages/linux/linux-mtx-1-2.4.27/29-au1000-pci-config-clear-errors.diff
@@ -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;
+ }
diff --git a/packages/linux/linux-mtx-1-2.4.27/defconfig-mtx-1 b/packages/linux/linux-mtx-1-2.4.27/defconfig-mtx-1
index 13234772ab..d31e979829 100644
--- a/packages/linux/linux-mtx-1-2.4.27/defconfig-mtx-1
+++ b/packages/linux/linux-mtx-1-2.4.27/defconfig-mtx-1
@@ -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
@@ -552,6 +557,11 @@ CONFIG_SR_EXTRA_DEVS=2
# CONFIG_SCSI_DEBUG is not set
#
+# PCMCIA SCSI adapter support
+#
+# CONFIG_SCSI_PCMCIA is not set
+
+#
# Fusion MPT device support
#
# CONFIG_FUSION is not set
@@ -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
#
@@ -671,6 +684,11 @@ CONFIG_SHAPER=m
# CONFIG_WAN is not set
#
+# PCMCIA network device support
+#
+# CONFIG_NET_PCMCIA is not set
+
+#
# Amateur Radio support
#
# CONFIG_HAMRADIO is not set
@@ -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
@@ -1187,6 +1210,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
#
CONFIG_BLUEZ=m
diff --git a/packages/linux/linux-mtx-1_2.4.24.bb b/packages/linux/linux-mtx-1_2.4.24.bb
index 0a9e9a487e..b98f9fb638 100644
--- a/packages/linux/linux-mtx-1_2.4.24.bb
+++ b/packages/linux/linux-mtx-1_2.4.24.bb
@@ -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"
diff --git a/packages/linux/linux-mtx-1_2.4.27.bb b/packages/linux/linux-mtx-1_2.4.27.bb
index a73bdf6c25..31015634cc 100644
--- a/packages/linux/linux-mtx-1_2.4.27.bb
+++ b/packages/linux/linux-mtx-1_2.4.27.bb
@@ -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() {