ChangeSet 1.1074.1.15, 2003/07/09 22:17:36-07:00, [EMAIL PROTECTED]

[PATCH] USB: patch for sl811 usb host controller driver

Ok, I have get rid of the typedefs and make it build on 2.4.21.

I'm not try to merge this with the existing sl811 driver, I modifed most
of it for support isochronous transfer mode, especially the structrue
and the urb schedule part, so it's almost a new one.  Some one need this
feature can use this one, others can still use the existing.  So may be
it makes sense to have two different drivers.  Note: I wrote it on
2.4.18,  at our board which use SA1110, and I test it with usb mouse,
usb moving disk and a web camera which use ov511 chipset.


 drivers/usb/host/Config.in |    1 
 drivers/usb/host/Makefile  |    1 
 drivers/usb/host/sl811.c   | 2749 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/sl811.h   |  177 ++
 4 files changed, 2928 insertions(+)


diff -Nru a/drivers/usb/host/Config.in b/drivers/usb/host/Config.in
--- a/drivers/usb/host/Config.in        Mon Jul 14 10:04:12 2003
+++ b/drivers/usb/host/Config.in        Mon Jul 14 10:04:12 2003
@@ -12,4 +12,5 @@
    define_bool CONFIG_USB_UHCI_ALT n
 fi
 dep_tristate '  OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI 
$CONFIG_USB
+dep_tristate '  SL811HS Alternate (support isochornous mode)' CONFIG_USB_SL811HS_ALT 
$CONFIG_USB
 
diff -Nru a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
--- a/drivers/usb/host/Makefile Mon Jul 14 10:04:12 2003
+++ b/drivers/usb/host/Makefile Mon Jul 14 10:04:12 2003
@@ -9,6 +9,7 @@
 obj-$(CONFIG_USB_UHCI_ALT)                     += uhci.o
 obj-$(CONFIG_USB_UHCI)                         += usb-uhci.o
 obj-$(CONFIG_USB_OHCI)                         += usb-ohci.o
+obj-$(CONFIG_USB_SL811HS_ALT)                          += sl811.o
 
 # Extract lists of the multi-part drivers.
 # The 'int-*' lists are the intermediate files used to build the multi's.
diff -Nru a/drivers/usb/host/sl811.c b/drivers/usb/host/sl811.c
--- /dev/null   Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/host/sl811.c  Mon Jul 14 10:04:12 2003
@@ -0,0 +1,2749 @@
+/*
+ * SL811 Host Controller Interface driver for USB.
+ *
+ * Copyright (c) 2003/06, Courage Co., Ltd.
+ *
+ * Based on: 
+ *     1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap,
+ *       Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber,
+ *       Adam Richter, Gregory P. Smith;
+       2.Original SL811 driver (hc_sl811.o) by Pei Liu <[EMAIL PROTECTED]>
+ *
+ * It's now support isochronous mode and more effective than hc_sl811.o
+ *
+ * To do:
+ *     1.Modify the timeout part, it's some messy
+ *     2.Use usb-a and usb-b set in Ping-Pong mode
+       
+ * 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.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/list.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/arch/irq.h>
+#include <linux/usb.h>
+
+#include "hcd.h"
+#include "hub.h"
+#include "sl811.h"
+
+#define DRIVER_VERSION "v0.28"
+#define DRIVER_AUTHOR "Yin Aihua <[EMAIL PROTECTED]>"
+#define DRIVER_DESC "Sl811 USB Host Controller Driver"
+
+static int sl811_addr_io = 0xf100000e;
+static int sl811_data_io = 0xf100000f;
+static int sl811_irq = 44;
+
+static LIST_HEAD(sl811_hcd_list);
+
+/*
+ * 0, normal prompt and information
+ * 1, error should not occur in normal
+ * 2, error maybe occur in normal
+ * 3, useful and detail debug information
+ * 4, function level enter and level inforamtion
+ * 5, endless information will output because of timer function or interrupt 
+ */
+static int debug = 0;
+
+MODULE_PARM(sl811_addr_io,"i");
+MODULE_PARM_DESC(sl811_addr_io,"sl811 address io port 0xf100000e");
+MODULE_PARM(sl811_data_io,"i");
+MODULE_PARM_DESC(sl811_data_io,"sl811 data io port 0xf100000f");
+MODULE_PARM(sl811_irq,"i");
+MODULE_PARM_DESC(sl811_irq,"sl811 irq 44(default)");
+MODULE_PARM(debug,"i");
+MODULE_PARM_DESC(debug,"debug level");
+
+static void sl811_rh_int_timer_do(unsigned long ptr);
+static void sl811_transfer_done(struct sl811_hc *hc, int sof); 
+
+/*
+ * Read        a byte of data from the SL811H/SL11H
+ */
+static __u8 sl811_read(struct sl811_hc *hc,    __u8 offset)
+{
+       __u8 data;
+
+       writeb(offset, hc->addr_io);
+       wmb();
+       data = readb(hc->data_io);
+       rmb();
+
+       return data;
+}
+
+/*
+ * Write a byte        of data to the SL811H/SL11H
+ */
+static void sl811_write(struct sl811_hc *hc, __u8 offset, __u8 data)
+{
+       writeb(offset, hc->addr_io);
+       writeb(data, hc->data_io);
+       wmb();
+}
+
+/*
+ * Read        consecutive bytes of data from the SL811H/SL11H buffer
+ */
+static void sl811_read_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size)
+{
+       writeb(offset, hc->addr_io);
+       wmb();
+       while (size--) {
+               *buf++ = readb(hc->data_io);
+               rmb();
+       }
+}
+
+/*
+ * Write consecutive bytes of data to the SL811H/SL11H buffer
+ */
+static void sl811_write_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size)
+{
+       writeb(offset, hc->addr_io);
+       wmb();
+       while (size--) {
+               writeb(*buf, hc->data_io);
+               wmb();
+               buf++;
+       }
+}
+
+/*
+ * This        routine test the Read/Write functionality of SL811HS registers
+ */
+static int sl811_reg_test(struct sl811_hc *hc)
+{
+       int i, data, result = 0;
+       __u8 buf[256];
+
+       for (i = 0x10; i < 256; i++) {
+               /* save the original buffer */
+               buf[i] = sl811_read(hc, i);
+
+               /* Write the new data to the buffer */
+               sl811_write(hc, i, i);
+       }
+
+       /* compare the written data */
+       for (i = 0x10; i < 256; i++) {
+               data = sl811_read(hc, i);
+               if (data != i) {
+                       PDEBUG(1, "Pattern test failed!! value = 0x%x, s/b 0x%x", 
data, i);
+                       result = -1;
+               }
+       }
+
+       /* restore the data */
+       for (i = 0x10; i < 256; i++)
+               sl811_write(hc, i, buf[i]);
+
+       return result;
+}
+
+/*
+ * Display all SL811HS register        values
+ */
+static void sl811_reg_show(struct sl811_hc *hc)
+{
+       int i;
+
+       for (i = 0; i < 256; i++)
+               PDEBUG(4, "offset %d: 0x%x", i, sl811_read(hc, i));
+}
+
+/*
+ * This        function enables SL811HS interrupts
+ */
+static void sl811_enable_interrupt(struct sl811_hc *hc)
+{
+       PDEBUG(4, "enter");
+       sl811_write(hc, SL811_INTR, SL811_INTR_DONE_A | SL811_INTR_SOF | 
SL811_INTR_INSRMV);
+}
+
+/*
+ * This        function disables SL811HS interrupts
+ */
+static void sl811_disable_interrupt(struct sl811_hc *hc)
+{
+       PDEBUG(4, "enter");
+       // Disable all other interrupt except for insert/remove.
+       sl811_write(hc, SL811_INTR, SL811_INTR_INSRMV);
+}
+
+/*
+ * SL811 Virtual Root Hub
+ */
+
+/* Device descriptor */
+static __u8 sl811_rh_dev_des[] =
+{
+       0x12,       /*  __u8  bLength; */
+       0x01,       /*  __u8  bDescriptorType; Device */
+       0x10,       /*  __u16 bcdUSB; v1.1 */
+       0x01,
+       0x09,       /*  __u8  bDeviceClass; HUB_CLASSCODE */
+       0x00,       /*  __u8  bDeviceSubClass; */
+       0x00,       /*  __u8  bDeviceProtocol; */
+       0x08,       /*  __u8  bMaxPacketSize0; 8 Bytes */
+       0x00,       /*  __u16 idVendor; */
+       0x00,
+       0x00,       /*  __u16 idProduct; */
+       0x00,
+       0x00,       /*  __u16 bcdDevice; */
+       0x00,
+       0x00,       /*  __u8  iManufacturer; */
+       0x02,       /*  __u8  iProduct; */
+       0x01,       /*  __u8  iSerialNumber; */
+       0x01        /*  __u8  bNumConfigurations; */
+};
+
+/* Configuration descriptor */
+static __u8 sl811_rh_config_des[] =
+{
+       0x09,       /*  __u8  bLength; */
+       0x02,       /*  __u8  bDescriptorType; Configuration */
+       0x19,       /*  __u16 wTotalLength; */
+       0x00,
+       0x01,       /*  __u8  bNumInterfaces; */
+       0x01,       /*  __u8  bConfigurationValue; */
+       0x00,       /*  __u8  iConfiguration; */
+       0x40,       /*  __u8  bmAttributes;
+                    Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup,
+                    4..0: resvd */
+       0x00,       /*  __u8  MaxPower; */
+
+       /* interface */
+       0x09,       /*  __u8  if_bLength; */
+       0x04,       /*  __u8  if_bDescriptorType; Interface */
+       0x00,       /*  __u8  if_bInterfaceNumber; */
+       0x00,       /*  __u8  if_bAlternateSetting; */
+       0x01,       /*  __u8  if_bNumEndpoints; */
+       0x09,       /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
+       0x00,       /*  __u8  if_bInterfaceSubClass; */
+       0x00,       /*  __u8  if_bInterfaceProtocol; */
+       0x00,       /*  __u8  if_iInterface; */
+
+       /* endpoint */
+       0x07,       /*  __u8  ep_bLength; */
+       0x05,       /*  __u8  ep_bDescriptorType; Endpoint */
+       0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
+       0x03,       /*  __u8  ep_bmAttributes; Interrupt */
+       0x08,       /*  __u16 ep_wMaxPacketSize; */
+       0x00,
+       0xff        /*  __u8  ep_bInterval; 255 ms */
+};
+
+/* root hub class descriptor*/
+static __u8 sl811_rh_hub_des[] =
+{
+       0x09,                   /*  __u8  bLength; */
+       0x29,                   /*  __u8  bDescriptorType; Hub-descriptor */
+       0x01,                   /*  __u8  bNbrPorts; */
+       0x00,                   /* __u16  wHubCharacteristics; */
+       0x00,
+       0x50,                   /*  __u8  bPwrOn2pwrGood; 2ms */
+       0x00,                   /*  __u8  bHubContrCurrent; 0 mA */
+       0xfc,                   /*  __u8  DeviceRemovable; *** 7 Ports max *** */
+       0xff                    /*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
+};
+
+/*
+ * This function examine the port change in the virtual root hub. HUB INTERRUPT 
ENDPOINT.
+ */
+static int sl811_rh_send_irq(struct sl811_hc *hc, __u8 *rh_change, int rh_len)
+{
+       __u8 data = 0;
+               
+       PDEBUG(5, "enter");
+
+       /*
+        * Right now, It is assume the power is good and no changes and only one port.
+        */
+       if (hc->rh_status.wPortChange & (USB_PORT_STAT_CONNECTION | 
USB_PORT_STAT_ENABLE)) {  
+               data = 1<<1;
+               *(__u8 *)rh_change = data;
+               return 1;
+       } else
+               return 0;
+}
+
+/*
+ * This function creates a timer that act as interrupt pipe in the virtual hub.
+ *
+ * Note:  The virtual root hub's interrupt pipe are polled by the timer
+ *        every "interval" ms
+ */
+static void sl811_rh_init_int_timer(struct urb * urb)
+{
+        struct sl811_hc *hc = urb->dev->bus->hcpriv;
+        hc->rh.interval = urb->interval;
+
+        init_timer(&hc->rh.rh_int_timer);
+        hc->rh.rh_int_timer.function = sl811_rh_int_timer_do;
+        hc->rh.rh_int_timer.data = (unsigned long)urb;
+        hc->rh.rh_int_timer.expires = jiffies +
+               (HZ * (urb->interval < 30? 30: urb->interval)) / 1000;
+        add_timer (&hc->rh.rh_int_timer);
+}
+
+/*
+ * This function is called when the timer expires.  It gets the the port
+ * change data and pass along to the upper protocol.
+ */
+static void sl811_rh_int_timer_do(unsigned long ptr)
+{
+       int len;
+       struct urb *urb = (struct urb *)ptr;
+       struct sl811_hc *hc = urb->dev->bus->hcpriv;
+       PDEBUG (5, "enter");
+
+       if(hc->rh.send) {
+               len = sl811_rh_send_irq(hc, urb->transfer_buffer,
+                       urb->transfer_buffer_length);
+               if (len > 0) {
+                       urb->actual_length = len;
+                       if (urb->complete)
+                               urb->complete(urb);
+               }
+       }
+
+#ifdef SL811_TIMEOUT
+       
+{
+       struct list_head *head, *tmp;
+       struct sl811_urb_priv *urbp;
+       struct urb *u;
+       int i;
+       static int timeout_count = 0;
+
+// check time out every second
+       if (++timeout_count > 4) {
+               int max_scan = hc->active_urbs;
+               timeout_count = 0;
+               for (i = 0; i < 6; ++i) {
+                       head = &hc->urb_list[i];
+                       tmp = head->next;
+                       while (tmp != head && max_scan--) {
+                               u = list_entry(tmp, struct urb, urb_list);
+                               urbp = (struct sl811_urb_priv *)u->hcpriv;
+                               tmp = tmp->next;
+                               // Check if the URB timed out
+                               if (u->timeout && time_after_eq(jiffies, 
urbp->inserttime + u->timeout)) {
+                                       PDEBUG(3, "urb = %p time out, we kill it", 
urb);
+                                       u->transfer_flags |= USB_TIMEOUT_KILLED;
+                               }
+                       }
+               }
+       }
+}
+
+#endif
+       // re-activate the timer
+       sl811_rh_init_int_timer(urb);
+}
+
+/* helper macro */
+#define OK(x)  len = (x); break
+
+/*
+ * This function handles all USB request to the the virtual root hub
+ */
+static int sl811_rh_submit_urb(struct urb *urb)
+{
+       struct usb_device *usb_dev = urb->dev;
+       struct sl811_hc *hc = usb_dev->bus->hcpriv;
+       struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *)urb->setup_packet;
+       void *data = urb->transfer_buffer;
+       int buf_len = urb->transfer_buffer_length;
+       unsigned int pipe = urb->pipe;
+       __u8 data_buf[16];
+       __u8 *bufp = data_buf;
+       int len = 0;
+       int status = 0;
+       
+       __u16 bmRType_bReq;
+       __u16 wValue;
+       __u16 wIndex;
+       __u16 wLength;
+
+       if (usb_pipeint(pipe)) {
+               hc->rh.urb =  urb;
+               hc->rh.send = 1;
+               hc->rh.interval = urb->interval;
+               sl811_rh_init_int_timer(urb);
+               urb->status = 0;
+
+               return 0;
+       }
+
+       bmRType_bReq  = cmd->bRequestType | (cmd->bRequest << 8);
+       wValue        = le16_to_cpu (cmd->wValue);
+       wIndex        = le16_to_cpu (cmd->wIndex);
+       wLength       = le16_to_cpu (cmd->wLength);
+
+       PDEBUG(5, "submit rh urb, req = %d(%x) len=%d", bmRType_bReq, bmRType_bReq, 
wLength);
+
+       /* Request Destination:
+                  without flags: Device,
+                  USB_RECIP_INTERFACE: interface,
+                  USB_RECIP_ENDPOINT: endpoint,
+                  USB_TYPE_CLASS means HUB here,
+                  USB_RECIP_OTHER | USB_TYPE_CLASS  almost ever means HUB_PORT here
+       */
+       switch (bmRType_bReq) {
+       case RH_GET_STATUS:
+               *(__u16 *)bufp = cpu_to_le16(1);
+               OK(2);
+
+       case RH_GET_STATUS | USB_RECIP_INTERFACE:
+               *(__u16 *)bufp = cpu_to_le16(0);
+               OK(2);
+
+       case RH_GET_STATUS | USB_RECIP_ENDPOINT:
+               *(__u16 *)bufp = cpu_to_le16(0);
+               OK(2);
+
+       case RH_GET_STATUS | USB_TYPE_CLASS:
+               *(__u32 *)bufp = cpu_to_le32(0);
+               OK(4);
+
+       case RH_GET_STATUS | USB_RECIP_OTHER | USB_TYPE_CLASS:
+               *(__u32 *)bufp = cpu_to_le32(hc->rh_status.wPortChange<<16 | 
hc->rh_status.wPortStatus);
+               OK(4);
+
+       case RH_CLEAR_FEATURE | USB_RECIP_ENDPOINT:
+               switch (wValue) {
+               case 1: 
+                       OK(0);
+               }
+               break;
+
+       case RH_CLEAR_FEATURE | USB_TYPE_CLASS:
+               switch (wValue) {
+               case C_HUB_LOCAL_POWER:
+                       OK(0);
+
+               case C_HUB_OVER_CURRENT:
+                       OK(0);
+               }
+               break;
+
+       case RH_CLEAR_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:
+               switch (wValue) {
+               case USB_PORT_FEAT_ENABLE:
+                       hc->rh_status.wPortStatus &= ~USB_PORT_STAT_ENABLE;
+                       OK(0);
+
+               case USB_PORT_FEAT_SUSPEND:
+                       hc->rh_status.wPortStatus &= ~USB_PORT_STAT_SUSPEND;
+                       OK(0);
+
+               case USB_PORT_FEAT_POWER:
+                       hc->rh_status.wPortStatus &= ~USB_PORT_STAT_POWER;
+                       OK(0);
+
+               case USB_PORT_FEAT_C_CONNECTION:
+                       hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_CONNECTION;
+                       OK(0);
+
+               case USB_PORT_FEAT_C_ENABLE:
+                       hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_ENABLE;
+                       OK(0);
+
+               case USB_PORT_FEAT_C_SUSPEND:
+                       hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_SUSPEND;
+                       OK(0);
+
+               case USB_PORT_FEAT_C_OVER_CURRENT:
+                       hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_OVERCURRENT;
+                       OK(0);
+
+               case USB_PORT_FEAT_C_RESET:
+                       hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_RESET;
+                       OK(0);
+               }
+               break;
+
+       case RH_SET_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:
+               switch (wValue) {
+               case USB_PORT_FEAT_SUSPEND:
+                       hc->rh_status.wPortStatus |= USB_PORT_STAT_SUSPEND;
+                       OK(0);
+
+               case USB_PORT_FEAT_RESET:
+                       hc->rh_status.wPortStatus |= USB_PORT_STAT_RESET;
+                       hc->rh_status.wPortChange = 0;
+                       hc->rh_status.wPortChange |= USB_PORT_STAT_C_RESET;
+                       hc->rh_status.wPortStatus &= ~USB_PORT_STAT_RESET;
+                       hc->rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;
+                       OK(0);
+
+               case USB_PORT_FEAT_POWER:
+                       hc->rh_status.wPortStatus |= USB_PORT_STAT_POWER;
+                       OK(0);
+
+               case USB_PORT_FEAT_ENABLE:
+                       hc->rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;
+                       OK(0);
+               }
+               break;
+
+       case RH_SET_ADDRESS:
+               hc->rh.devnum = wValue;
+               OK(0);
+
+       case RH_GET_DESCRIPTOR:
+               switch ((wValue & 0xff00) >> 8) {
+               case USB_DT_DEVICE:
+                       len = sizeof(sl811_rh_dev_des);
+                       bufp = sl811_rh_dev_des;
+                       OK(len);
+
+               case USB_DT_CONFIG: 
+                       len = sizeof(sl811_rh_config_des);
+                       bufp = sl811_rh_config_des;
+                       OK(len);
+
+               case USB_DT_STRING:
+                       len = usb_root_hub_string(wValue & 0xff, (int)(long)0,  
"SL811HS", data, wLength);
+                       if (len > 0) {
+                               bufp = data;
+                               OK(len);
+                       }
+               
+               default:
+                       status = -EPIPE;
+               }
+               break;
+
+       case RH_GET_DESCRIPTOR | USB_TYPE_CLASS:
+               len = sizeof(sl811_rh_hub_des);
+               bufp = sl811_rh_hub_des;
+               OK(len);
+
+       case RH_GET_CONFIGURATION:
+               bufp[0] = 0x01;
+               OK(1);
+
+       case RH_SET_CONFIGURATION:
+               OK(0);
+
+       default:
+               PDEBUG(1, "unsupported root hub command");
+               status = -EPIPE;
+       }
+
+       len = min(len, buf_len);
+       if (data != bufp)
+               memcpy(data, bufp, len);
+       urb->actual_length = len;
+       urb->status = status;
+
+       PDEBUG(5, "len = %d, status = %d", len, status);
+       
+       urb->hcpriv = NULL;
+       urb->dev = NULL;
+       if (urb->complete)
+               urb->complete(urb);
+
+       return 0;
+}
+
+/*
+ * This function unlinks the URB
+ */
+static int sl811_rh_unlink_urb(struct urb *urb)
+{
+       struct sl811_hc *hc = urb->dev->bus->hcpriv;
+
+       PDEBUG(5, "enter");
+       
+       if (hc->rh.urb == urb) {
+               hc->rh.send = 0;
+               del_timer(&hc->rh.rh_int_timer);
+               hc->rh.urb = NULL;
+               urb->hcpriv = NULL;
+               usb_dec_dev_use(urb->dev);
+               urb->dev = NULL;
+               if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+                       urb->status = -ECONNRESET;
+                       if (urb->complete)
+                               urb->complete(urb);
+               } else
+                       urb->status = -ENOENT;
+       }
+
+       return 0;
+}
+
+/*
+ * This function connect the virtual root hub to the USB stack
+ */
+static int sl811_connect_rh(struct sl811_hc * hc)
+{
+       struct usb_device *usb_dev;
+
+       hc->rh.devnum = 0;
+       usb_dev = usb_alloc_dev(NULL, hc->bus);
+       if (!usb_dev)
+               return -ENOMEM;
+
+       hc->bus->root_hub = usb_dev;
+       usb_connect(usb_dev);
+
+       if (usb_new_device(usb_dev)) {
+               usb_free_dev(usb_dev);
+               return -ENODEV;
+       }
+       
+       PDEBUG(5, "leave success");
+       
+       return 0;
+}
+
+/*
+ * This function allocates private data space for the usb device
+ */
+static int sl811_alloc_dev_priv(struct usb_device *usb_dev)
+{
+       return 0;
+}
+
+/*
+ * This function de-allocates private data space for the usb devic
+ */
+static int sl811_free_dev_priv (struct usb_device *usb_dev)
+{
+       return 0;
+}
+
+/*
+ * This function allocates private data space for the urb
+ */
+static struct sl811_urb_priv* sl811_alloc_urb_priv(struct urb *urb)
+{
+       struct sl811_urb_priv *urbp;
+       
+       urbp = kmalloc(sizeof(*urbp), GFP_KERNEL);
+       if (!urbp)
+               return NULL;
+       
+       memset(urbp, 0, sizeof(*urbp));
+       
+       INIT_LIST_HEAD(&urbp->td_list);
+       
+       urbp->urb = urb;
+       urb->hcpriv = urbp;
+       
+       return urbp;
+}
+
+/*
+ * This function free private data space for the urb
+ */
+static void sl811_free_urb_priv(struct urb *urb)
+{
+       struct sl811_urb_priv *urbp = urb->hcpriv;
+       struct sl811_td *td;
+       struct list_head *head, *tmp;
+       
+       if (!urbp)
+               return ;
+       
+       head = &urbp->td_list;
+       tmp = head->next;
+       
+       while (tmp != head) {
+               td = list_entry(tmp, struct sl811_td, td_list);
+               tmp = tmp->next;
+               kfree(td);
+       }
+       
+       kfree(urbp);
+       urb->hcpriv = NULL;
+       
+       return ;
+}
+
+/*
+ * This        function calculate the bus time need by this td.
+ * Fix me! Can this use usb_calc_bus_time()?
+ */
+static void sl811_calc_td_time(struct sl811_td *td)
+{
+#if 1
+       int time;
+       int len = td->len;
+       struct sl811_hc *hc = td->urb->dev->bus->hcpriv; 
+
+       if (hc->rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED)
+               time = 8*8*len + 1024;
+       else {
+               if (td->ctrl & SL811_USB_CTRL_PREAMBLE)
+                       time = 8*8*len + 2048;
+               else
+                       time = 8*len + 256;
+       }
+
+       time += 2*10 * len;
+
+       td->bustime = time;
+       
+#else
+
+       unsigned long tmp;
+       int time;
+       int low_speed = usb_pipeslow(td->urb->pipe);
+       int input_dir = usb_pipein(td->urb->pipe);
+       int bytecount = td->len;
+       int isoc = usb_pipeisoc(td->urb->pipe); 
+
+       if (low_speed) {        /* no isoc. here */
+               if (input_dir) {
+                       tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;
+                       time =  (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+               } else {
+                       tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L;
+                       time =  (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+               }
+       } else if (!isoc){      /* for full-speed: */
+               tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
+               time = (9107L + BW_HOST_DELAY + tmp);
+       } else {                /* for isoc: */
+               tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
+               time =  (((input_dir) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
+       }
+       
+       td->bustime = time / 84;
+
+#endif          
+}
+
+/*
+ * This        function calculate the remainder bus time in current frame.
+ */
+static inline int sl811_calc_bus_remainder(struct sl811_hc *hc)
+{
+       return (sl811_read(hc, SL811_SOFCNTDIV) * 64);
+}
+
+/*
+ * This function allocates td for the urb
+ */
+static struct sl811_td* sl811_alloc_td(struct urb *urb)
+{
+       struct sl811_urb_priv *urbp = urb->hcpriv;
+       struct sl811_td *td;
+       
+       td = kmalloc(sizeof (*td), GFP_KERNEL);
+       if (!td)
+               return NULL;
+       
+       memset(td, 0, sizeof(*td));
+       
+       INIT_LIST_HEAD(&td->td_list);
+       
+       td->urb = urb;
+       list_add_tail(&td->td_list, &urbp->td_list);
+       
+       return td;
+}
+
+/*
+ * Fill the td.
+ */
+static inline void sl811_fill_td(struct sl811_td *td, __u8 ctrl, __u8 addr, __u8 len, 
__u8 pidep, __u8 dev, __u8 *buf)
+{
+       td->ctrl = ctrl;
+       td->addr = addr;
+       td->len = len;
+       td->pidep = pidep;
+       td->dev = dev;
+       td->buf = buf;
+       td->left = len;
+       td->errcnt = 3;
+}
+
+/*
+ * Fill the td.
+ */
+static inline void sl811_reset_td(struct sl811_td *td)
+{
+       td->status = 0;
+       td->left = td->len;
+       td->done = 0;
+       td->errcnt = 3;
+       td->nakcnt = 0;
+       td->td_status = 0;
+}
+
+static void sl811_print_td(int level, struct sl811_td *td)
+{
+        PDEBUG(level, "td = %p, ctrl = %x, addr = %x, len = %x, pidep = %x\n 
+               dev = %x, status = %x, left = %x, errcnt = %x, done = %x\n 
+               buf = %p, bustime = %d, td_status = %d\n", 
+               td, td->ctrl, td->addr, td->len, td->pidep,
+               td->dev, td->status, td->left, td->errcnt, td->done,
+               td->buf, td->bustime, td->td_status);
+}
+
+/*
+ * Isochronous transfers
+ */
+static int sl811_submit_isochronous(struct urb *urb)
+{
+       __u8 dev = usb_pipedevice(urb->pipe);
+       __u8 pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe));
+       __u8 ctrl = 0;
+       struct sl811_urb_priv *urbp = urb->hcpriv;
+       struct sl811_td *td = NULL;
+       int i;
+       
+       PDEBUG(4, "enter, urb = %p, urbp = %p", urb, urbp);
+       
+       /* Can't have low speed bulk transfers */
+       if (usb_pipeslow(urb->pipe)) {
+               PDEBUG(1, "error, urb = %p, low speed device", urb);
+               return -EINVAL;
+       }
+       
+       if (usb_pipeout(urb->pipe))
+               ctrl |= SL811_USB_CTRL_DIR_OUT;
+               
+       ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE | SL811_USB_CTRL_ISO;
+       
+       for (i = 0; i < urb->number_of_packets; i++) {
+               urb->iso_frame_desc[i].actual_length = 0;
+               urb->iso_frame_desc[i].status = -EXDEV;
+                       
+               td = sl811_alloc_td(urb);
+               if (!td)
+                       return -ENOMEM;
+
+               sl811_fill_td(td, ctrl, SL811_DATA_START, 
+                       urb->iso_frame_desc[i].length,
+                       pidep, dev,
+                       urb->transfer_buffer + urb->iso_frame_desc[i].offset);
+               sl811_calc_td_time(td);
+               if (urbp->cur_td == NULL)
+                       urbp->cur_td = urbp->first_td = td;     
+       }
+       
+       urbp->last_td = td;     
+       
+       PDEBUG(4, "leave success");
+
+/*     
+// for debug
+       {
+               struct list_head *head, *tmp;
+               struct sl811_td *td;
+               int i = 0;
+               head = &urbp->td_list;
+               tmp = head->next;
+       
+               if (list_empty(&urbp->td_list)) {
+                       PDEBUG(1, "bug!!! td list is empty!");
+                       return -ENODEV;
+               }
+               
+               while (tmp != head) {
+                       ++i;
+                       td = list_entry(tmp, struct sl811_td, td_list);
+                       PDEBUG(2, "td = %p, i = %d", td, i);
+                       tmp = tmp->next;
+               }
+       }
+*/     
+       return 0;
+}
+
+/*
+ * Reset isochronous transfers
+ */
+static void sl811_reset_isochronous(struct urb *urb)
+{
+       struct sl811_urb_priv *urbp = urb->hcpriv;
+       struct sl811_td *td = NULL;
+       struct list_head *head, *tmp;
+       int i;
+
+       PDEBUG(4, "enter, urb = %p", urb);
+       
+       for (i = 0; i < urb->number_of_packets; i++) {
+               urb->iso_frame_desc[i].actual_length = 0;
+               urb->iso_frame_desc[i].status = -EXDEV;
+       }
+
+       head = &urbp->td_list;
+       tmp = head->next;
+       while (tmp != head) {
+               td = list_entry(tmp, struct sl811_td, td_list);
+               tmp = tmp->next;
+               sl811_reset_td(td);
+       }
+       
+       urbp->cur_td = urbp->first_td;
+       
+       urb->status = -EINPROGRESS;
+       urb->actual_length = 0;
+       urb->error_count = 0;
+}
+
+/*
+ * Result the iso urb.
+ */
+static void sl811_result_isochronous(struct urb *urb)
+{
+       struct list_head *tmp, *head;
+       struct sl811_urb_priv *urbp = urb->hcpriv;
+       int status = 0;
+       struct sl811_td *td;
+       int i;
+
+       PDEBUG(4, "enter, urb = %p", urb);
+               
+       urb->actual_length = 0;
+
+       i = 0;
+       head = &urbp->td_list;
+       tmp = head->next;
+       while (tmp != head) {
+               td = list_entry(tmp, struct sl811_td, td_list);
+               tmp = tmp->next;
+               
+               if (!td->done) {
+                       if (urbp->unlink)
+                               urb->status = -ENOENT;
+                       else {
+                               PDEBUG(1, "we should not get here!");
+                               urb->status = -EXDEV;
+                       }
+                       return ;        
+               }
+               if (td->td_status) {
+                       status = td->td_status;
+                       urb->error_count++;
+                       PDEBUG(1, "error: td = %p, td status = %d", td, td->td_status);
+               }
+
+               urb->iso_frame_desc[i].actual_length = td->len - td->left;
+               urb->actual_length += td->len - td->left;
+               urb->iso_frame_desc[i].status = td->td_status;
+               ++i;
+               if (td->left)
+                       PDEBUG(3, "short packet, td = %p, len = %d, left = %d", td, 
td->len, td->left);
+       }
+
+       urb->status = status;
+/*
+// for debug
+       PDEBUG(2, "iso urb complete, len = %d, status =%d ", urb->actual_length, 
urb->status);          
+*/
+       PDEBUG(4, "leave success");
+}
+
+/*
+ * Interrupt transfers
+ */
+static int sl811_submit_interrupt(struct urb *urb)
+{
+       int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+       int len = urb->transfer_buffer_length;
+       __u8 *data = urb->transfer_buffer;
+       __u8 dev = usb_pipedevice(urb->pipe);
+       __u8 pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe));
+       __u8 ctrl = 0;
+       struct sl811_hc *hc = urb->dev->bus->hcpriv;
+       struct sl811_urb_priv *urbp = urb->hcpriv;
+       struct sl811_td *td = NULL;
+       
+       PDEBUG(4, "enter, urb = %p", urb);
+       
+       if (len > maxsze) {
+               PDEBUG(1, "length is big than max packet size, len = %d, max packet = 
%d", len, maxsze);
+               return -EINVAL;
+       }
+       if (usb_pipeslow(urb->pipe) && !(hc->rh_status.wPortStatus & 
USB_PORT_STAT_LOW_SPEED))
+               ctrl |= SL811_USB_CTRL_PREAMBLE;
+       
+       ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE;
+       if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), 
usb_pipeout(urb->pipe)))
+               ctrl |= SL811_USB_CTRL_TOGGLE_1;
+       usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));   
 
+       td = sl811_alloc_td(urb);
+       if (!td)
+               return -ENOMEM;
+               
+       sl811_fill_td(td, ctrl, SL811_DATA_START, len, pidep, dev, data);
+       sl811_calc_td_time(td);
+       urbp->cur_td = urbp->first_td = urbp->last_td = td;
+       urbp->interval = 0;
+       
+       PDEBUG(4, "leave success");
+       
+       return 0;
+}
+
+/*
+ * Reset interrupt transfers
+ */
+static void sl811_reset_interrupt(struct urb *urb)
+{
+       struct sl811_urb_priv *urbp = urb->hcpriv;
+       struct sl811_td *td = urbp->cur_td;
+       
+       PDEBUG(4, "enter, interval = %d", urb->interval);
+       
+       td->ctrl &= ~SL811_USB_CTRL_TOGGLE_1;
+       if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), 
usb_pipeout(urb->pipe)))
+               td->ctrl |= SL811_USB_CTRL_TOGGLE_1;
+       usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));   
 
+       
+       sl811_reset_td(td);
+
+       urbp->interval = urb->interval;
+       
+       urb->status = -EINPROGRESS;
+       urb->actual_length = 0;
+}
+
+/*
+ * Result the interrupt urb.
+ */
+static void sl811_result_interrupt(struct urb *urb)
+{
+       struct list_head *tmp;
+       struct sl811_urb_priv *urbp = urb->hcpriv;
+       struct sl811_td *td;
+       int toggle;
+       
+       PDEBUG(4, "enter, urb = %p", urb);
+       
+       urb->actual_length = 0;
+
+       tmp = &urbp->td_list;
+       tmp = tmp->next;
+       td = list_entry(tmp, struct sl811_td, td_list);
+
+       // success.
+       if (td->done && td->td_status == 0) {
+               urb->actual_length += td->len - td->left;
+               urb->status = 0;
+               return ;
+       }
+       // tranfer is done but fail, reset the toggle.
+       else if (td->done && td->td_status) {
+               urb->status = td->td_status;
+reset_toggle:
+               toggle = (td->ctrl & SL811_USB_CTRL_TOGGLE_1) ? 1 : 0;
+               usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), 
usb_pipeout(urb->pipe), toggle);
+               PDEBUG(3, "error: td = %p, td status = %d", td, td->td_status);
+               return ;
+       }
+       // unlink, and not do transfer yet
+       else if (td->done == 0 && urbp->unlink && td->td_status == 0) {
+               urb->status = -ENOENT;
+               PDEBUG(3, "unlink and not transfer!");
+               return ;
+       }
+       // unlink, and transfer not complete yet.
+       else if (td->done == 0 && urbp->unlink && td->td_status) {
+               urb->status = -ENOENT;
+               PDEBUG(3, "unlink and not complete!");
+               goto reset_toggle;
+       }
+       // must be bug!!!
+       else {// (td->done == 0 && urbp->unlink == 0)
+               PDEBUG(1, "we should not get here!");
+               urb->status = -EPIPE;
+               return ;
+       }
+}
+
+/*
+ * Control transfers
+ */
+static int sl811_submit_control(struct urb *urb)
+{
+       int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+       int len = urb->transfer_buffer_length;
+       __u8 *data = urb->transfer_buffer;
+       __u8 dev = usb_pipedevice(urb->pipe);
+       __u8 pidep = 0;
+       __u8 ctrl = 0;
+       struct sl811_hc *hc = urb->dev->bus->hcpriv;
+       struct sl811_urb_priv *urbp = urb->hcpriv;
+       struct sl811_td *td = NULL;
+       
+       PDEBUG(4, "enter, urb = %p", urb);
+       
+       if (usb_pipeslow(urb->pipe) && !(hc->rh_status.wPortStatus & 
USB_PORT_STAT_LOW_SPEED))
+               ctrl |= SL811_USB_CTRL_PREAMBLE;
+       
+       /* Build SETUP TD */
+       pidep = PIDEP(USB_PID_SETUP, usb_pipeendpoint(urb->pipe));
+       ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE | SL811_USB_CTRL_DIR_OUT;
+       td = sl811_alloc_td(urb);
+       if (!td)
+               return -ENOMEM;
+               
+       sl811_fill_td(td, ctrl, SL811_DATA_START, 8, pidep, dev, urb->setup_packet);
+       sl811_calc_td_time(td);
+       
+       urbp->cur_td = urbp->first_td = td;
+       
+       /*
+        * If direction is "send", change the frame from SETUP (0x2D)
+        * to OUT (0xE1). Else change it from SETUP to IN (0x69).
+        */
+       pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe));
+       if (usb_pipeout(urb->pipe))
+               ctrl |= SL811_USB_CTRL_DIR_OUT;
+       else
+               ctrl &= ~SL811_USB_CTRL_DIR_OUT;
+
+       /* Build the DATA TD's */
+       while (len > 0) {
+               int pktsze = len;
+
+               if (pktsze > maxsze)
+                       pktsze = maxsze;
+
+               /* Alternate Data0/1 (start with Data1) */
+               ctrl ^= SL811_USB_CTRL_TOGGLE_1;
+       
+               td = sl811_alloc_td(urb);
+               if (!td)
+                       return -ENOMEM;
+
+               sl811_fill_td(td, ctrl, SL811_DATA_START, pktsze, pidep, dev, data);   
 
+               sl811_calc_td_time(td);
+               
+               data += pktsze;
+               len -= pktsze;
+       }
+
+       /* Build the final TD for control status */
+       td = sl811_alloc_td(urb);
+       if (!td)
+               return -ENOMEM;
+
+       /* It's IN if the pipe is an output pipe or we're not expecting data back */
+       if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length) {
+               pidep = PIDEP(USB_PID_IN, usb_pipeendpoint(urb->pipe));
+               ctrl &= ~SL811_USB_CTRL_DIR_OUT;        
+       } else {
+               pidep = PIDEP(USB_PID_OUT, usb_pipeendpoint(urb->pipe));
+               ctrl |= SL811_USB_CTRL_DIR_OUT;
+       }
+               
+       /* End in Data1 */
+       ctrl |= SL811_USB_CTRL_TOGGLE_1;
+
+       sl811_fill_td(td, ctrl, SL811_DATA_START, 0, pidep, dev, 0);
+       sl811_calc_td_time(td);
+       urbp->last_td = td;
+/*     
+// for debug
+       {
+               struct list_head *head, *tmp;
+               struct sl811_td *td;
+               int i = 0;
+               head = &urbp->td_list;
+               tmp = head->next;
+       
+               if (list_empty(&urbp->td_list)) {
+                       PDEBUG(1, "bug!!! td list is empty!");
+                       return -ENODEV;
+               }
+               
+               while (tmp != head) {
+                       ++i;
+                       td = list_entry(tmp, struct sl811_td, td_list);
+                       PDEBUG(3, "td = %p, i = %d", td, i);
+                       tmp = tmp->next;
+               }
+       }
+*/     
+       PDEBUG(4, "leave success");
+       
+       return 0;
+}
+
+/*
+ * Result the control urb.
+ */
+static void sl811_result_control(struct urb *urb)
+{
+       struct list_head *tmp, *head;
+       struct sl811_urb_priv *urbp = urb->hcpriv;
+       struct sl811_td *td;
+
+       PDEBUG(4, "enter, urb = %p", urb);
+       
+       if (list_empty(&urbp->td_list)) {
+               PDEBUG(1, "td list is empty");
+               return ;
+       }
+
+       head = &urbp->td_list;
+
+       tmp = head->next;
+       td = list_entry(tmp, struct sl811_td, td_list);
+
+       /* The first TD is the SETUP phase, check the status, but skip the count */
+       if (!td->done) {
+               PDEBUG(3, "setup phase error, td = %p, done = %d", td, td->done);
+               goto err_done;
+       }
+       if (td->td_status)  {
+               PDEBUG(3, "setup phase error, td = %p, td status = %d", td, 
td->td_status);
+               goto err_status;
+       }
+
+       urb->actual_length = 0;
+
+       /* The rest of the TD's (but the last) are data */
+       tmp = tmp->next;
+       while (tmp != head && tmp->next != head) {
+               td = list_entry(tmp, struct sl811_td, td_list);
+               tmp = tmp->next;
+               if (!td->done) {
+                       PDEBUG(3, "data phase error, td = %p, done = %d", td, 
td->done);
+                       goto err_done;
+               }
+               if (td->td_status)  {
+                       PDEBUG(3, "data phase error, td = %p, td status = %d", td, 
td->td_status);
+                       goto err_status;
+               }
+
+               urb->actual_length += td->len - td->left;
+               // short packet.
+               if (td->left) {
+                       PDEBUG(3, "data phase short packet, td = %p, count = %d", td, 
td->len - td->left);
+                       break;
+               }
+       }
+
+       /* The last td is status phase */
+       td = urbp->last_td;
+       if (!td->done) {
+               PDEBUG(3, "status phase error, td = %p, done = %d", td, td->done);
+               goto err_done;
+       }
+       if (td->td_status)  {
+               PDEBUG(3, "status phase error, td = %p, td status = %d", td, 
td->td_status);
+               goto err_status;
+       }
+       
+       PDEBUG(4, "leave success");
+       
+       urb->status = 0;
+       return ;
+
+err_done:
+       if (urbp->unlink)
+               urb->status = -ENOENT;
+       else {
+               PDEBUG(1, "we should not get here! td = %p", td);
+               urb->status = -EPIPE;
+       }
+       return ;        
+
+err_status:
+       urb->status = td->td_status;            
+       return ;
+}
+
+/*
+ * Bulk transfers
+ */
+static int sl811_submit_bulk(struct urb *urb)
+{
+       int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+       int len = urb->transfer_buffer_length;
+       __u8 *data = urb->transfer_buffer;
+       __u8 dev = usb_pipedevice(urb->pipe);
+       __u8 pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe));
+       __u8 ctrl = 0;
+       struct sl811_urb_priv *urbp = urb->hcpriv;
+       struct sl811_td *td = NULL;
+
+       PDEBUG(4, "enter, urb = %p", urb);
+               
+       if (len < 0) {
+               PDEBUG(1, "error, urb = %p, len = %d", urb, len);
+               return -EINVAL;
+       }
+
+       /* Can't have low speed bulk transfers */
+       if (usb_pipeslow(urb->pipe)) {
+               PDEBUG(1, "error, urb = %p, low speed device", urb);
+               return -EINVAL;
+       }
+
+       if (usb_pipeout(urb->pipe))
+               ctrl |= SL811_USB_CTRL_DIR_OUT;
+               
+       ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE;
+                       
+       /* Build the DATA TD's */
+       do {    /* Allow zero length packets */
+               int pktsze = len;
+
+               if (pktsze > maxsze)
+                       pktsze = maxsze;
+
+               td = sl811_alloc_td(urb);
+               if (!td)
+                       return -ENOMEM;
+
+               /* Alternate Data0/1 (start with Data1) */
+               ctrl &= ~SL811_USB_CTRL_TOGGLE_1;
+               if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), 
usb_pipeout(urb->pipe)))
+                       ctrl |= SL811_USB_CTRL_TOGGLE_1;
+               usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), 
usb_pipeout(urb->pipe));            
+               
+               sl811_fill_td(td, ctrl, SL811_DATA_START, pktsze, pidep, dev, data);
+               sl811_calc_td_time(td);
+               
+               if (urbp->cur_td == NULL)
+                       urbp->cur_td = urbp->first_td = td;
+                       
+               data += pktsze;
+               len -= maxsze;
+       } while (len > 0);
+
+       /*
+        * USB_ZERO_PACKET means adding a 0-length packet, if
+        * direction is OUT and the transfer_length was an
+        * exact multiple of maxsze, hence
+        * (len = transfer_length - N * maxsze) == 0
+        * however, if transfer_length == 0, the zero packet
+        * was already prepared above.
+        */
+       if (usb_pipeout(urb->pipe) && (urb->transfer_flags & USB_ZERO_PACKET) &&
+          !len && urb->transfer_buffer_length) {
+               
+               td = sl811_alloc_td(urb);
+               if (!td)
+                       return -ENOMEM;
+
+               /* Alternate Data0/1 (start with Data1) */
+               ctrl &= ~SL811_USB_CTRL_TOGGLE_1;
+               if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), 
usb_pipeout(urb->pipe)))
+                       ctrl |= SL811_USB_CTRL_TOGGLE_1;
+               usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), 
usb_pipeout(urb->pipe));
+                       
+               sl811_fill_td(td, ctrl, SL811_DATA_START, 0, pidep, dev, 0);
+               sl811_calc_td_time(td);
+       }
+       
+       urbp->last_td = td;
+       
+       PDEBUG(4, "leave success");
+       
+       return 0;
+}
+
+/*
+ * Reset bulk transfers
+ */
+static int sl811_reset_bulk(struct urb *urb)
+{
+       struct sl811_urb_priv *urbp = urb->hcpriv;
+       struct sl811_td *td;
+       struct list_head *head, *tmp;
+
+       PDEBUG(4, "enter, urb = %p", urb);
+       
+       
+       head = &urbp->td_list;  
+       tmp = head->next;
+       
+       while (tmp != head) {
+               td = list_entry(tmp, struct sl811_td, td_list);
+
+               /* Alternate Data0/1 (start with Data1) */
+               td->ctrl &= ~SL811_USB_CTRL_TOGGLE_1;
+               if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), 
usb_pipeout(urb->pipe)))
+                       td->ctrl |= SL811_USB_CTRL_TOGGLE_1;
+               usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), 
usb_pipeout(urb->pipe));
+               
+               sl811_reset_td(td);
+       } 
+
+       urb->status = -EINPROGRESS;
+       urb->actual_length = 0;
+       urbp->cur_td = urbp->first_td;
+
+       PDEBUG(4, "leave success");
+       
+       return 0;
+}
+
+/*
+ * Result the bulk urb.
+ */
+static void sl811_result_bulk(struct urb *urb)
+{
+       struct list_head *tmp, *head;
+       struct sl811_urb_priv *urbp = urb->hcpriv;
+       struct sl811_td *td = NULL;
+       int toggle;
+
+       PDEBUG(4, "enter, urb = %p", urb);
+       
+       urb->actual_length = 0;
+
+       head = &urbp->td_list;
+       tmp = head->next;
+       while (tmp != head) {
+               td = list_entry(tmp, struct sl811_td, td_list);
+               tmp = tmp->next;
+
+               // success.
+               if (td->done && td->td_status == 0) {
+                       urb->actual_length += td->len - td->left;
+                       
+                       // short packet
+                       if (td->left) {
+                               urb->status = 0;
+                               PDEBUG(3, "short packet, td = %p, count = %d", td, 
td->len - td->left);
+                               goto reset_toggle;
+                       }
+               }
+               // tranfer is done but fail, reset the toggle.
+               else if (td->done && td->td_status) {
+                       urb->status = td->td_status;
+                       PDEBUG(3, "error: td = %p, td status = %d", td, td->td_status);
+                       goto reset_toggle;
+               }
+               // unlink, and not do transfer yet
+               else if (td->done == 0 && urbp->unlink && td->td_status == 0) {
+                       urb->status = -ENOENT;
+                       PDEBUG(3, "unlink and not transfer!");
+                       return ;
+               }
+               // unlink, and transfer not complete yet.
+               else if (td->done == 0 && urbp->unlink && td->td_status) {
+                       PDEBUG(3, "unlink and not complete!");
+                       urb->status = -ENOENT;
+                       goto reset_toggle;
+               }
+               // must be bug!!!
+               else {// (td->done == 0 && urbp->unlink == 0)
+                       urb->status = -EPIPE;
+                       PDEBUG(1, "we should not get here!");
+                       return ;
+               }
+       }
+       
+       PDEBUG(4, "leave success");             
+       urb->status = 0;
+       return ;
+
+reset_toggle:
+       toggle = (td->ctrl & SL811_USB_CTRL_TOGGLE_1) ? 1 : 0;
+       usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 
toggle);
+}
+
+/*
+ * Find the first urb have the same dev and endpoint.
+ */
+static inline int sl811_find_same_urb(struct list_head *head, struct urb *urb)
+{
+       struct list_head *tmp;
+       struct urb *u;
+       
+       if (!head || !urb)
+               return 0;
+               
+       tmp = head->next;
+       
+       while (tmp != head) {
+               u = list_entry(tmp, struct urb, urb_list);
+               if (u == urb)
+                       return 1;
+               tmp = tmp->next;        
+       }
+       
+       return 0;
+}
+
+/*
+ * Find the first urb have the same dev and endpoint.
+ */
+static inline struct urb* sl811_find_same_devep(struct list_head *head, struct urb 
*urb)
+{
+       struct list_head *tmp;
+       struct urb *u;
+       
+       if (!head || !urb)
+               return NULL;
+               
+       tmp = head->next;
+       
+       while (tmp != head) {
+               u = list_entry(tmp, struct urb, urb_list);
+               if ((usb_pipe_endpdev(u->pipe)) == (usb_pipe_endpdev(urb->pipe)))
+                       return u;
+               tmp = tmp->next;        
+       }
+       
+       return NULL;
+}
+
+/*
+ * This function is called by the USB core API when an URB is available to
+ * process. 
+ */
+static int sl811_submit_urb(struct urb *urb)
+{
+       struct sl811_hc *hc = urb->dev->bus->hcpriv;
+       unsigned int pipe = urb->pipe;
+       struct list_head *head = NULL;
+       unsigned long flags;
+       int bustime;
+       int ret = 0;
+       
+       if (!urb) {
+               PDEBUG(1, "urb is null");
+               return -EINVAL;
+       }
+       
+       if (urb->hcpriv) {
+               PDEBUG(1, "urbp is not null, urb = %p, urbp = %p", urb, urb->hcpriv);
+               return -EINVAL;
+       }
+       
+       if (!urb->dev || !urb->dev->bus || !hc)  {
+               PDEBUG(1, "dev or bus or hc is null");
+               return -ENODEV;
+       }
+       
+       if (usb_endpoint_halted(urb->dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) {
+               PDEBUG(2, "sl811_submit_urb: endpoint_halted");
+               return -EPIPE;
+       }
+       
+       if (usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)) > 
SL811_DATA_LIMIT) {
+               printk(KERN_ERR "Packet size is big for SL811, should < %d!\n", 
SL811_DATA_LIMIT);
+               return -EINVAL;
+       }
+       
+       /* a request to the virtual root hub */ 
+       if (usb_pipedevice(pipe) == hc->rh.devnum)
+               return sl811_rh_submit_urb(urb);
+       
+       spin_lock_irqsave(&hc->hc_lock, flags);
+       spin_lock(&urb->lock);
+       
+       switch (usb_pipetype(urb->pipe)) {
+       case PIPE_ISOCHRONOUS:
+               head = &hc->iso_list;
+               break;
+       case PIPE_INTERRUPT:
+               head = &hc->intr_list;
+               break;
+       case PIPE_CONTROL:
+               head = &hc->ctrl_list;
+               break;
+       case PIPE_BULK:
+               head = &hc->bulk_list;
+               break;
+       }
+               
+       if (sl811_find_same_devep(head, urb)) {
+               list_add(&urb->urb_list, &hc->wait_list);
+               PDEBUG(4, "add to wait list");
+               goto out_unlock;
+       }
+       
+       if (!sl811_alloc_urb_priv(urb)) {
+               ret = -ENOMEM;
+               goto out_unlock;
+       }
+       
+       switch (usb_pipetype(urb->pipe)) {
+       case PIPE_ISOCHRONOUS:
+               if (urb->number_of_packets <= 0) {
+                       ret = -EINVAL;
+                       break;
+               }
+               bustime = usb_check_bandwidth(urb->dev, urb);
+               if (bustime < 0) {
+                       ret = bustime;
+                       break;
+               }
+               if (!(ret = sl811_submit_isochronous(urb)))
+                       usb_claim_bandwidth(urb->dev, urb, bustime, 1);
+               break;
+       case PIPE_INTERRUPT:
+               bustime = usb_check_bandwidth(urb->dev, urb);
+               if (bustime < 0)
+                       ret = bustime;
+               else if (!(ret = sl811_submit_interrupt(urb)))
+                       usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+               break;
+       case PIPE_CONTROL:
+               ret = sl811_submit_control(urb);
+               break;
+       case PIPE_BULK:
+               ret = sl811_submit_bulk(urb);
+               break;
+       }
+       
+       if (!ret) {
+               ((struct sl811_urb_priv *)urb->hcpriv)->inserttime = jiffies;
+               list_add(&urb->urb_list, head);
+               PDEBUG(4, "add to type list");
+               urb->status = -EINPROGRESS;
+               if (++hc->active_urbs == 1)
+                       sl811_enable_interrupt(hc);
+               goto out_unlock;        
+       } else {
+               PDEBUG(2, "submit urb fail! error = %d", ret);
+               sl811_free_urb_priv(urb);
+       }
+       
+out_unlock:    
+       spin_unlock(&urb->lock);
+       spin_unlock_irqrestore(&hc->hc_lock, flags);
+
+       return ret;
+}
+
+/*
+ * Submit the urb the wait list.
+ */
+static int sl811_submit_urb_with_lock(struct urb *urb)
+{
+       struct sl811_hc *hc = urb->dev->bus->hcpriv;
+       struct list_head *head = NULL;
+       int bustime;
+       int ret = 0;
+       
+       spin_lock(&urb->lock);
+       
+       switch (usb_pipetype(urb->pipe)) {
+       case PIPE_ISOCHRONOUS:
+               head = &hc->iso_list;
+               break;
+       case PIPE_INTERRUPT:
+               head = &hc->intr_list;
+               break;
+       case PIPE_CONTROL:
+               head = &hc->ctrl_list;
+               break;
+       case PIPE_BULK:
+               head = &hc->bulk_list;
+               break;
+       }
+               
+       if (!sl811_alloc_urb_priv(urb)) {
+               ret = -ENOMEM;
+               goto out_unlock;
+       }
+       
+       switch (usb_pipetype(urb->pipe)) {
+       case PIPE_ISOCHRONOUS:
+               if (urb->number_of_packets <= 0) {
+                       ret = -EINVAL;
+                       break;
+               }
+               bustime = usb_check_bandwidth(urb->dev, urb);
+               if (bustime < 0) {
+                       ret = bustime;
+                       break;
+               }
+               if (!(ret = sl811_submit_isochronous(urb)))
+                       usb_claim_bandwidth(urb->dev, urb, bustime, 1);
+               break;
+       case PIPE_INTERRUPT:
+               bustime = usb_check_bandwidth(urb->dev, urb);
+               if (bustime < 0)
+                       ret = bustime;
+               else if (!(ret = sl811_submit_interrupt(urb)))
+                       usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+               break;
+       case PIPE_CONTROL:
+               ret = sl811_submit_control(urb);
+               break;
+       case PIPE_BULK:
+               ret = sl811_submit_bulk(urb);
+               break;
+       }
+       
+       if (ret == 0) {
+               ((struct sl811_urb_priv *)urb->hcpriv)->inserttime = jiffies;
+               list_add(&urb->urb_list, head);
+               PDEBUG(4, "add to type list");
+               urb->status = -EINPROGRESS;
+               if (++hc->active_urbs == 1)
+                       sl811_enable_interrupt(hc);
+               goto out_unlock;        
+       } else {
+               PDEBUG(2, "submit urb fail! error = %d", ret);
+               sl811_free_urb_priv(urb);
+       }
+       
+out_unlock:    
+       spin_unlock(&urb->lock);
+
+       return ret;
+}
+
+/*
+ * Reset the urb
+ */
+static void sl811_reset_urb(struct urb *urb)
+{
+       struct sl811_urb_priv *urbp = urb->hcpriv;
+
+       switch (usb_pipetype(urb->pipe)) {
+       case PIPE_ISOCHRONOUS:
+               sl811_reset_isochronous(urb);
+               break;
+       case PIPE_INTERRUPT:
+               sl811_reset_interrupt(urb);
+               break;
+       case PIPE_CONTROL:
+               return;
+       case PIPE_BULK:
+               sl811_reset_bulk(urb);
+               break;
+       }
+       urbp->inserttime = jiffies;
+}
+
+/*
+ * Return the result of a transfer
+ */
+static void sl811_result_urb(struct urb *urb)
+{
+       struct sl811_urb_priv *urbp = urb->hcpriv;
+       struct sl811_hc *hc = urb->dev->bus->hcpriv;
+       struct list_head *head = NULL;
+       struct urb *u = NULL;
+       int reset = 0;
+       int ring = 0;
+
+       if (urb->status != -EINPROGRESS) {
+               PDEBUG(1, "urb status is not EINPROGRESS!");
+               return ;
+       }
+       
+       spin_lock(&urb->lock);
+       
+       switch (usb_pipetype(urb->pipe)) {
+       case PIPE_ISOCHRONOUS:
+               head = &hc->iso_list;
+               sl811_result_isochronous(urb);
+               
+               // if the urb is not unlink and is in a urb "ring", we reset it
+               if (!urbp->unlink && urb->next) 
+                       ring = 1;
+               break;
+       case PIPE_INTERRUPT:
+               head = &hc->intr_list;
+               sl811_result_interrupt(urb);
+               
+               // if the urb is not unlink and not "once" query, we reset.
+               if (!urbp->unlink && urb->interval)
+                       reset = 1;
+               break;
+       case PIPE_CONTROL:
+               head = &hc->ctrl_list;
+               sl811_result_control(urb);
+               break;
+       case PIPE_BULK:
+               head = &hc->bulk_list;
+               sl811_result_bulk(urb);
+               
+               // if the urb is not unlink and is in a urb "ring", we reset it
+               if (!urbp->unlink && urb->next)
+                       ring = 1;
+               break;
+       }
+       
+       PDEBUG(4, "result urb status = %d", urb->status);
+       
+       if (ring && urb->next == urb)
+               reset = 1;
+       
+       if (!reset) {
+               switch (usb_pipetype(urb->pipe)) {
+               case PIPE_ISOCHRONOUS:
+                       usb_release_bandwidth(urb->dev, urb, 1);
+                       break;
+               case PIPE_INTERRUPT:
+                       usb_release_bandwidth(urb->dev, urb, 0);
+                       break;
+               }
+               sl811_free_urb_priv(urb);
+       }
+       
+       spin_unlock(&urb->lock);
+       
+       if (urb->complete)
+               urb->complete(urb);
+       
+       if (reset) {
+               spin_lock(&urb->lock);
+               sl811_reset_urb(urb);
+               if (usb_pipeint(urb->pipe))
+                       list_add(&urb->urb_list, &hc->idle_intr_list);
+               else
+                       list_add(&urb->urb_list, head);
+               spin_unlock(&urb->lock);
+       } else {
+               if (--hc->active_urbs <= 0) {
+                       hc->active_urbs = 0;
+                       sl811_disable_interrupt(hc);
+               }
+               
+               if (ring) 
+                       u = urb->next;
+               else
+                       u = sl811_find_same_devep(&hc->wait_list, urb);
+                       
+               if (u) {
+                       if (!list_empty(&u->urb_list))
+                               list_del(&u->urb_list);
+                       if (sl811_submit_urb_with_lock(u))
+                               list_add(&u->urb_list, &hc->wait_list);
+               }
+       }
+}
+
+
+#ifdef SL811_TIMEOUT
+
+/*
+ * Unlink the urb from the urb list
+ */
+static int sl811_unlink_urb(struct urb *urb)
+{
+       unsigned long flags;
+       struct sl811_hc *hc;
+       struct sl811_urb_priv *urbp;
+       int call = 0;
+       int schedule = 0;
+       int count = 0;
+       
+       if (!urb) {
+               PDEBUG(1, "urb is null");
+               return -EINVAL;
+       }
+       
+       if (!urb->dev || !urb->dev->bus) {
+               PDEBUG(1, "dev or bus is null");
+               return -ENODEV;
+       }
+       
+       hc = urb->dev->bus->hcpriv; 
+       urbp = urb->hcpriv;
+       
+       /* a request to the virtual root hub */
+       if (usb_pipedevice(urb->pipe) == hc->rh.devnum)
+               return sl811_rh_unlink_urb(urb); 
+       
+       spin_lock_irqsave(&hc->hc_lock, flags);
+       spin_lock(&urb->lock);
+
+       // in wait list
+       if (sl811_find_same_urb(&hc->wait_list, urb)) { 
+               PDEBUG(4, "unlink urb in wait list");
+               list_del_init(&urb->urb_list);
+               urb->status = -ENOENT;
+               call = 1;
+               goto out;
+       }
+       
+       // in intr idle list.
+       if (sl811_find_same_urb(&hc->idle_intr_list, urb)) {    
+               PDEBUG(4, "unlink urb in idle intr list");
+               list_del_init(&urb->urb_list);
+               urb->status = -ENOENT;
+               sl811_free_urb_priv(urb);
+               usb_release_bandwidth(urb->dev, urb, 0);
+               if (--hc->active_urbs <= 0) {
+                       hc->active_urbs = 0;
+                       sl811_disable_interrupt(hc);
+               }
+               call = 1;
+               goto out;
+       }
+
+       if (urb->status == -EINPROGRESS) {  
+               PDEBUG(3, "urb is still in progress");
+               urbp->unlink = 1;
+
+re_unlink:
+               // Is it in progress?
+               urbp = urb->hcpriv;
+               if (urbp && hc->cur_td == urbp->cur_td) {
+                       ++count;
+                       if (sl811_read(hc, 0) & SL811_USB_CTRL_ARM) {
+                               PDEBUG(3, "unlink: cur td is still in progress! count 
= %d", count);
+re_schedule:                           
+                               schedule = 1;
+                               spin_unlock(&urb->lock);
+                               spin_unlock_irqrestore(&hc->hc_lock, flags);
+                               schedule_timeout(HZ/50);
+                               spin_lock_irqsave(&hc->hc_lock, flags);
+                               spin_lock(&urb->lock);
+                       } else {
+                               PDEBUG(3, "unlink: lost of interrupt? do parse! count 
= %d", count);
+                               spin_unlock(&urb->lock);
+                               sl811_transfer_done(hc, 0);
+                               spin_lock(&urb->lock);
+                       }
+                       goto re_unlink;
+               }
+               
+               if (list_empty(&urb->urb_list)) {
+                       PDEBUG(3, "unlink: list empty!");
+                       goto out;
+               }
+                       
+               if (urb->transfer_flags & USB_TIMEOUT_KILLED) { 
+                       PDEBUG(3, "unlink: time out killed");
+                       // it is timeout killed by us 
+                       goto result;
+               } else if (urb->transfer_flags & USB_ASYNC_UNLINK) { 
+                       // we do nothing, just let it be processing later
+                       PDEBUG(3, "unlink async, do nothing");
+                       goto out;
+               } else {
+                       // synchron without callback
+                       PDEBUG(3, "unlink synchron, we wait the urb complete or 
timeout");
+                       if (schedule == 0) {
+                               PDEBUG(3, "goto re_schedule");
+                               goto re_schedule;
+                       } else {
+                               PDEBUG(3, "already scheduled");
+                               goto result;
+                       }
+               }
+       } else if (!list_empty(&urb->urb_list)) {
+               PDEBUG(1, "urb = %p, status = %d is in a list, why?", urb, 
urb->status);
+               //list_del_init(&urb->urb_list);
+               //call = 1;
+       }
+
+out:
+       spin_unlock(&urb->lock);
+       spin_unlock_irqrestore(&hc->hc_lock, flags);
+       
+       if (call && urb->complete)
+               urb->complete(urb);
+       
+       return 0;
+       
+result:
+       spin_unlock(&urb->lock);
+       
+       list_del_init(&urb->urb_list);
+       sl811_result_urb(urb);  
+       
+       spin_unlock_irqrestore(&hc->hc_lock, flags);
+       
+       return 0;
+}
+
+#else
+
+/*
+ * Unlink the urb from the urb list
+ */
+static int sl811_unlink_urb(struct urb *urb)
+{
+       unsigned long flags;
+       struct sl811_hc *hc;
+       struct sl811_urb_priv *urbp;
+       int call = 0;
+       
+       if (!urb) {
+               PDEBUG(1, "urb is null");
+               return -EINVAL;
+       }
+       
+       if (!urb->dev || !urb->dev->bus) {
+               PDEBUG(1, "dev or bus is null");
+               return -ENODEV;
+       }
+       
+       hc = urb->dev->bus->hcpriv; 
+       urbp = urb->hcpriv;
+       
+       /* a request to the virtual root hub */
+       if (usb_pipedevice(urb->pipe) == hc->rh.devnum)
+               return sl811_rh_unlink_urb(urb); 
+       
+       spin_lock_irqsave(&hc->hc_lock, flags);
+       spin_lock(&urb->lock);
+
+       // in wait list
+       if (sl811_find_same_urb(&hc->wait_list, urb)) { 
+               PDEBUG(2, "unlink urb in wait list");
+               list_del_init(&urb->urb_list);
+               urb->status = -ENOENT;
+               call = 1;
+               goto out;
+       }
+       
+       if (urb->status == -EINPROGRESS) {  
+               PDEBUG(2, "urb is still in progress");
+               urbp->unlink = 1;
+
+               // Is it in progress?
+               urbp = urb->hcpriv;
+               if (urbp && hc->cur_td == urbp->cur_td) {
+                       // simple, let it out
+                       PDEBUG(2, "unlink: cur td is still in progress!");
+                       hc->cur_td = NULL;
+               }
+               
+               goto result;
+       } else if (!list_empty(&urb->urb_list)) {
+               PDEBUG(1, "urb = %p, status = %d is in a list, why?", urb, 
urb->status);
+               list_del_init(&urb->urb_list);
+               if (urbp)
+                       goto result;
+               else
+                       call = 1;
+       }
+
+out:
+       spin_unlock(&urb->lock);
+       spin_unlock_irqrestore(&hc->hc_lock, flags);
+       
+       if (call && urb->complete)
+               urb->complete(urb);
+                       
+       return 0;
+       
+result:
+       spin_unlock(&urb->lock);
+       
+       list_del_init(&urb->urb_list);
+       sl811_result_urb(urb);  
+       
+       spin_unlock_irqrestore(&hc->hc_lock, flags);
+       
+       return 0;
+}
+
+#endif
+
+static int sl811_get_current_frame_number(struct usb_device *usb_dev)
+{
+       return ((struct sl811_hc *)(usb_dev->bus->hcpriv))->frame_number;
+}
+
+static struct usb_operations sl811_device_operations = 
+{
+       sl811_alloc_dev_priv,
+       sl811_free_dev_priv,
+       sl811_get_current_frame_number,
+       sl811_submit_urb,
+       sl811_unlink_urb
+};
+
+/*
+ * This        functions transmit a td.
+ */
+static inline void sl811_trans_cur_td(struct sl811_hc *hc, struct sl811_td *td)
+{
+       sl811_print_td(4, td);
+       sl811_write_buf(hc, SL811_ADDR_A,  &td->addr, 4);
+       if (td->len && (td->ctrl & SL811_USB_CTRL_DIR_OUT))
+               sl811_write_buf(hc, td->addr,  td->buf, td->len);
+
+       sl811_write(hc, SL811_CTRL_A, td->ctrl);
+}
+
+               
+/*
+ * This        function checks the status of the transmitted or received packet
+ * and copy the        data from the SL811HS register into a buffer.
+ */
+static void sl811_parse_cur_td(struct sl811_hc *hc, struct sl811_td *td)
+{
+       struct urb *urb = td->urb;
+#ifdef SL811_DEBUG
+       int dev = usb_pipedevice(td->urb->pipe);
+       int ep = usb_pipeendpoint(td->urb->pipe);
+#endif
+
+       sl811_read_buf(hc, SL811_STS_A, &td->status, 2);
+       
+       if (td->status & SL811_USB_STS_ACK) {
+               td->done = 1;
+               
+/*             if ((td->ctrl & SL811_USB_CTRL_TOGGLE_1) != (td->status & 
SL811_USB_STS_TOGGLE_1)) {
+                       PDEBUG(2, "dev %d endpoint %d unexpect data toggle!", dev, ep);
+                       td->td_status = -EILSEQ;
+               }
+*/             
+               if (!(td->ctrl & SL811_USB_CTRL_DIR_OUT) && td->len > 0)
+                       sl811_read_buf(hc, td->addr, td->buf, td->len - td->left);
+                       
+               if (td->left && (urb->transfer_flags & USB_DISABLE_SPD)) {
+                       PDEBUG(2, "dev %d endpoint %d unexpect short packet! td = %p", 
dev, ep, td);
+                       td->td_status = -EREMOTEIO;
+               } else
+                       td->td_status = 0;
+       } else if (td->status & SL811_USB_STS_STALL) {
+               PDEBUG(2, "dev %d endpoint %d halt, td = %p", dev, ep, td);
+               td->td_status = -EPIPE;
+               if (urb->dev)
+                       usb_endpoint_halt(td->urb->dev, 
usb_pipeendpoint(td->urb->pipe), usb_pipeout(td->urb->pipe));
+               td->done = 1;
+       } else if (td->status & SL811_USB_STS_OVERFLOW) {
+               PDEBUG(1, "dev %d endpoint %d overflow, sl811 only support packet less 
than %d", dev, ep, SL811_DATA_LIMIT);    
+               td->td_status = -EOVERFLOW;
+               td->done = 1;
+       } else if (td->status & SL811_USB_STS_TIMEOUT ) {
+               PDEBUG(2, "dev %d endpoint %d timeout, td = %p", dev, ep, td);  
+               td->td_status = -ETIMEDOUT;
+               if (--td->errcnt == 0)
+                       td->done = 1;
+       } else if (td->status & SL811_USB_STS_ERROR) {
+               PDEBUG(2, "dev %d endpoint %d error, td = %p", dev, ep, td);
+               td->td_status = -EILSEQ;
+               if (--td->errcnt == 0)
+                       td->done = 1;
+       } else if (td->status & SL811_USB_STS_NAK) {
+               ++td->nakcnt;
+               PDEBUG(3, "dev %d endpoint %d nak, td = %p, count = %d", dev, ep, td, 
td->nakcnt);
+               td->td_status = -EINPROGRESS;
+               if (!usb_pipeslow(td->urb->pipe) && td->nakcnt > 1024) {
+                       PDEBUG(2, "too many naks, td = %p, count = %d", td, 
td->nakcnt);
+                       td->td_status = -ETIMEDOUT;
+                       td->done = 1;
+               } 
+       } 
+       
+       sl811_print_td(4, td);
+}
+
+/*
+ * This        function checks the status of current urb.
+ */
+static int sl811_parse_cur_urb(struct urb *urb)
+{
+       struct sl811_urb_priv *urbp = urb->hcpriv;
+       struct sl811_td *td = urbp->cur_td;
+       struct list_head *tmp;
+       
+       sl811_print_td(5, td);
+       
+       // this td not done yet.
+       if (!td->done)
+               return 0;
+       
+       // the last ld, so the urb is done.
+       if (td == urbp->last_td) {
+               PDEBUG(4, "urb = %p is done success", td->urb);
+               if (usb_pipeisoc(td->urb->pipe))
+                       PDEBUG(4, "ISO URB DONE, td = %p", td);
+               return 1;
+       }
+       
+       // iso transfer, we always advance to next td 
+       if (usb_pipeisoc(td->urb->pipe)) {
+               tmp = &td->td_list;
+               tmp = tmp->next;
+               urbp->cur_td = list_entry(tmp, struct sl811_td, td_list);
+               PDEBUG(4, "ISO NEXT, td = %p", urbp->cur_td);   
+               return 0;
+       }
+               
+       // some error occur, so the urb is done.
+       if (td->td_status) {
+               PDEBUG(3, "urb = %p is done error, td status is = %d", td->urb, 
td->td_status);
+               return 1;
+       }
+               
+       // short packet.
+       if (td->left) {
+               if (usb_pipecontrol(td->urb->pipe)) {
+                       // control packet, we advance to the last td
+                       PDEBUG(3, "ctrl short packet, advance to last td");
+                       urbp->cur_td = urbp->last_td;
+                       return 0;
+               } else {
+                       // interrut and bulk packet, urb is over.
+                       PDEBUG(3, "bulk or intr short packet, urb is over");
+                       return 1;
+               }
+       }
+
+       // we advance to next td.       
+       tmp = &td->td_list;
+       tmp = tmp->next;
+       urbp->cur_td = list_entry(tmp, struct sl811_td, td_list);
+#ifdef SL811_DEBUG
+       PDEBUG(4, "advance to the next td, urb = %p, td = %p", urb, urbp->cur_td);
+       sl811_print_td(5, urbp->cur_td);
+       if (td == urbp->cur_td)
+               PDEBUG(1, "bug!!!");
+#endif         
+       return 0;
+}
+
+/*
+ * Find the next td to transfer.
+ */
+static inline struct sl811_td* sl811_schedule_next_td(struct urb *urb, struct 
sl811_td *cur_td)
+{
+       struct sl811_urb_priv *urbp = urb->hcpriv;
+       
+       PDEBUG(4, "urb at %p, cur td at %p", urb, cur_td);
+       
+       // iso don't schedule the td in the same frame.
+       if (usb_pipeisoc(cur_td->urb->pipe))
+               return NULL;
+       
+       // cur td is not complete
+       if (!cur_td->done)
+               return NULL;    
+       
+       // here, urbp->cur_td is already the next td;
+       return urbp->cur_td;
+}
+
+/*
+ * Scan the list to find a active urb
+ */
+static inline struct urb* sl811_get_list_next_urb(struct sl811_hc *hc, struct 
list_head *next)
+{
+       struct urb *urb;
+       int i;
+       
+       if (list_empty(next))
+               return NULL;
+       
+       if (next == hc->cur_list)
+               return NULL;
+                       
+       for (i = 0; i < 4; ++i) 
+               if (next == &hc->urb_list[i])
+                       return NULL;
+                       
+       urb = list_entry(next, struct urb, urb_list);
+       PDEBUG(4, "next urb in list is at %p", urb);
+               
+       return urb;
+}
+
+/*
+ * Find the next td to transfer.
+ */
+static struct sl811_td* sl811_schedule_next_urb(struct sl811_hc *hc, struct list_head 
*next)
+{
+       struct urb *urb = NULL;
+       int back_loop = 1;
+       struct list_head *old_list = hc->cur_list;
+               
+       // try to get next urb in the same list.
+       if (next) {
+               urb = sl811_get_list_next_urb(hc, next);
+               if (!urb)
+                       ++hc->cur_list;
+       }
+
+       // try other list.
+       if (!urb) {                     
+re_loop:
+               // try all the list.
+               while (hc->cur_list < &hc->urb_list[4]) { 
+                       if ((urb = sl811_get_list_next_urb(hc, hc->cur_list->next)))
+                               return ((struct sl811_urb_priv *)urb->hcpriv)->cur_td;
+                       ++hc->cur_list;
+               }
+               // the last list is try 
+               if (back_loop && (old_list >= &hc->ctrl_list)) {
+                       hc->cur_list = &hc->ctrl_list;
+                       back_loop = 0;
+                       goto re_loop;
+               }
+       }
+       
+       if (hc->cur_list > &hc->urb_list[3])
+               hc->cur_list = &hc->ctrl_list;
+                       
+       return NULL;
+}
+
+/*
+ * This function process the transfer rusult.
+ */
+static void sl811_transfer_done(struct sl811_hc *hc, int sof) 
+{
+       struct sl811_td *cur_td = hc->cur_td, *next_td = NULL;
+       struct urb *cur_urb = NULL;
+               struct list_head *next = NULL;
+               int done;
+       
+       PDEBUG(5, "enter");
+       
+       if (cur_td == NULL) {
+               PDEBUG(1, "in done interrupt, but td is null, be already parsed?");
+               return ;
+       }
+
+       cur_urb = cur_td->urb;
+       hc->cur_td = NULL;
+       next = &cur_urb->urb_list;
+       next = next->next;
+       
+       spin_lock(&cur_urb->lock);      
+       sl811_parse_cur_td(hc, cur_td);
+       done = sl811_parse_cur_urb(cur_urb);
+       spin_unlock(&cur_urb->lock);
+       
+       if (done) {
+               list_del_init(&cur_urb->urb_list);
+               cur_td = NULL;
+               sl811_result_urb(cur_urb);      
+       }
+
+       if (sof)
+               return ;
+       
+       if (!done) {
+               next_td = sl811_schedule_next_td(cur_urb, cur_td);
+               if (next_td && next_td != cur_td && (sl811_calc_bus_remainder(hc) > 
next_td->bustime)) {
+                       hc->cur_td = next_td;
+                       PDEBUG(5, "ADD TD");
+                       sl811_trans_cur_td(hc, next_td);
+                       return ;
+               }
+       }
+       
+       while (1) {
+               next_td = sl811_schedule_next_urb(hc, next);
+               if (!next_td)
+                       return;
+               if (next_td == cur_td)
+                       return;
+               next = &next_td->urb->urb_list;
+               next = next->next;
+               if (sl811_calc_bus_remainder(hc) > next_td->bustime) {
+                       hc->cur_td = next_td;
+                       PDEBUG(5, "ADD TD");
+                       sl811_trans_cur_td(hc, next_td);
+                       return ;
+               }
+       }
+}
+
+/*
+ *
+ */
+static void inline sl811_dec_intr_interval(struct sl811_hc *hc)
+{
+       struct list_head *head, *tmp;
+       struct urb *urb;
+       struct sl811_urb_priv *urbp;
+       
+       if (list_empty(&hc->idle_intr_list))
+               return ;
+       
+       head = &hc->idle_intr_list;
+       tmp = head->next;
+       
+       while (tmp != head) {
+               urb = list_entry(tmp, struct urb, urb_list);
+               tmp = tmp->next;
+               spin_lock(&urb->lock);
+               urbp = urb->hcpriv;
+               if (--urbp->interval == 0) {
+                       list_del(&urb->urb_list);
+                       list_add(&urb->urb_list, &hc->intr_list);
+                       PDEBUG(4, "intr urb active");
+               }
+               spin_unlock(&urb->lock);
+       }
+}
+
+/*
+ * The sof interrupt is happen.        
+ */
+static void sl811_start_sof(struct sl811_hc *hc)
+{
+       struct sl811_td *next_td;
+#ifdef SL811_DEBUG
+       static struct sl811_td *repeat_td = NULL;
+       static repeat_cnt = 1;
+#endif 
+       if (++hc->frame_number > 1024)
+               hc->frame_number = 0;
+       
+       if (hc->active_urbs == 0)
+               return ;
+       
+       sl811_dec_intr_interval(hc);
+       
+       if (hc->cur_td) {
+               if (sl811_read(hc, 0) & SL811_USB_CTRL_ARM) {
+#ifdef SL811_DEBUG
+                       if (repeat_td == hc->cur_td) 
+                               ++repeat_cnt;
+                       else {
+                               if (repeat_cnt >= 2)
+                                       PDEBUG(2, "cur td = %p repeat %d", hc->cur_td, 
repeat_cnt);
+                               repeat_cnt = 1;
+                               repeat_td = hc->cur_td;
+                       }
+#endif
+                       return ;
+               } else {
+                       PDEBUG(2, "lost of interrupt in sof? do parse!");
+                       sl811_transfer_done(hc, 1);
+                       
+                       // let this frame idle  
+                       return;
+               }
+       }
+       
+       hc->cur_list = &hc->iso_list;
+       
+       if (hc->active_urbs == 0)
+               return ;
+       
+       next_td = sl811_schedule_next_urb(hc, NULL);
+       if (!next_td) {
+#ifdef SL811_DEBUG
+               if (list_empty(&hc->idle_intr_list))
+                       PDEBUG(2, "not schedule a td, why? urbs = %d", 
hc->active_urbs);
+#endif
+               return; 
+       }
+       if (sl811_calc_bus_remainder(hc) > next_td->bustime) {
+               hc->cur_td = next_td;
+               sl811_trans_cur_td(hc, next_td);
+       } else
+               PDEBUG(2, "bus time if not enough, why?");
+}
+
+/*
+ * This        function resets SL811HS controller and detects the speed of
+ * the connecting device
+ *
+ * Return: 0 = no device attached; 1 = USB device attached
+ */
+static int sl811_hc_reset(struct sl811_hc *hc)
+{
+       int status ;
+
+       sl811_write(hc, SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
+       sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET);
+
+       mdelay(20);
+       
+       // Disable hardware SOF generation.
+       sl811_write(hc, SL811_CTRL1, 0);
+       mdelay(2);
+       sl811_write(hc, SL811_INTRSTS, 0xff); 
+       status = sl811_read(hc, SL811_INTRSTS);
+
+       if (status & SL811_INTR_NOTPRESENT) {
+               // Device is not present
+               PDEBUG(0, "Device not present");
+               hc->rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | 
USB_PORT_STAT_ENABLE);
+               hc->rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
+               sl811_write(hc, SL811_INTR, SL811_INTR_INSRMV);
+               return 0;
+       }
+
+       // Send SOF to address 0, endpoint 0.
+       sl811_write(hc, SL811_LEN_B, 0);
+       sl811_write(hc, SL811_PIDEP_B, PIDEP(USB_PID_SOF, 0));
+       sl811_write(hc, SL811_DEV_B, 0x00);
+       sl811_write (hc, SL811_SOFLOW, SL811_12M_HI);
+
+       if (status & SL811_INTR_SPEED_FULL) {
+               /* full speed device connect directly to root hub */
+               PDEBUG (0, "Full speed Device attached");
+               
+               sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET);
+               mdelay(20);
+               sl811_write(hc, SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
+               sl811_write(hc, SL811_CTRL1, SL811_CTRL1_SOF);
+
+               /* start the SOF or EOP */
+               sl811_write(hc, SL811_CTRL_B, SL811_USB_CTRL_ARM);
+               hc->rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION;
+               hc->rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED;
+               mdelay(2);
+               sl811_write (hc, SL811_INTRSTS, 0xff);
+       } else {
+               /* slow speed device connect directly to root-hub */
+               PDEBUG(0, "Low speed Device attached");
+               
+               sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET);
+               mdelay(20);
+               sl811_write(hc, SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | 
SL811_12M_HI);
+               sl811_write(hc, SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF);
+
+               /* start the SOF or EOP */
+               sl811_write(hc, SL811_CTRL_B, SL811_USB_CTRL_ARM);
+               hc->rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | 
USB_PORT_STAT_LOW_SPEED;
+               mdelay(2);
+               sl811_write(hc, SL811_INTRSTS, 0xff);
+       }
+
+       hc->rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
+       sl811_write(hc, SL811_INTR, SL811_INTR_INSRMV);
+       
+       return 1;
+}
+
+/*
+ * Interrupt service routine.
+ */
+static void sl811_interrupt(int irq, void *__hc, struct pt_regs * r)
+{
+       __u8 status;
+       struct sl811_hc *hc = __hc;
+
+       status = sl811_read(hc, SL811_INTRSTS);
+       if (status == 0)
+               return ;
+
+       sl811_write(hc, SL811_INTRSTS, 0xff);
+
+       if (status & SL811_INTR_INSRMV) {
+               sl811_write(hc, SL811_INTR, 0);
+               sl811_write(hc, SL811_CTRL1, 0);
+               // wait for device stable
+               mdelay(100);                    
+               sl811_hc_reset(hc);
+               return ;
+       }
+
+       spin_lock(&hc->hc_lock);
+       
+       if (status & SL811_INTR_DONE_A) {
+               if (status & SL811_INTR_SOF) {
+                       sl811_transfer_done(hc, 1);
+                       PDEBUG(4, "sof in done!");
+                       sl811_start_sof(hc);
+               } else
+                       sl811_transfer_done(hc, 0);
+       } else if (status & SL811_INTR_SOF)
+               sl811_start_sof(hc);    
+
+       spin_unlock(&hc->hc_lock);      
+
+       return ;
+}
+
+/*
+ * This        function allocates all data structure and store in the
+ * private data        structure.
+ *
+ * Return value         : data structure for the host controller
+ */
+static struct sl811_hc* __devinit sl811_alloc_hc(void)
+{
+       struct sl811_hc *hc;
+       struct usb_bus *bus;
+       int i;
+
+       PDEBUG(5, "enter");
+
+       hc = (struct sl811_hc *)kmalloc(sizeof(struct sl811_hc), GFP_KERNEL);
+       if (!hc)
+               return NULL;
+
+       memset(hc, 0, sizeof(struct sl811_hc));
+
+       hc->rh_status.wPortStatus = USB_PORT_STAT_POWER;
+       hc->rh_status.wPortChange = 0;
+
+       hc->active_urbs = 0;
+       INIT_LIST_HEAD(&hc->hc_hcd_list);
+       list_add(&hc->hc_hcd_list, &sl811_hcd_list);
+       
+       init_waitqueue_head(&hc->waitq);
+       
+       for (i = 0; i < 6; ++i)
+               INIT_LIST_HEAD(&hc->urb_list[i]);
+       
+       hc->cur_list = &hc->iso_list;
+
+       bus = usb_alloc_bus(&sl811_device_operations);
+       if (!bus) {
+               kfree (hc);
+               return NULL;
+       }
+
+       hc->bus = bus;
+       bus->hcpriv = hc;
+
+       return hc;
+}
+
+/*
+ * This        function De-allocate all resources
+ */
+static void sl811_release_hc(struct sl811_hc *hc)
+{
+       PDEBUG(5, "enter");
+
+       /* disconnect all devices */
+       if (hc->bus->root_hub)
+               usb_disconnect(&hc->bus->root_hub);
+
+       if (hc->addr_io)
+               release_region(hc->addr_io, 1);
+
+       if (hc->data_io)
+               release_region(hc->data_io, 1);
+
+       if (hc->irq)
+               free_irq(hc->irq, hc);
+
+       usb_deregister_bus(hc->bus);
+       usb_free_bus(hc->bus);
+
+       list_del(&hc->hc_hcd_list);
+       INIT_LIST_HEAD(&hc->hc_hcd_list);
+
+       kfree (hc);
+}
+
+/*
+ * This        function is board specific.  It sets up the interrupt to
+ * be an edge trigger and trigger on the rising        edge
+ */
+static void sl811_init_irq(void)
+{
+       GPDR &= ~(1<<23);
+       set_GPIO_IRQ_edge(1<<23, GPIO_RISING_EDGE);
+}
+
+/*
+ * This        function request IO memory regions, request IRQ, and
+ * allocate all        other resources.
+ *
+ * Input: addr_io = first IO address
+ *       data_io = second IO address
+ *       irq = interrupt number
+ *
+ * Return: 0 = success or error        condition
+ */
+static int __devinit sl811_found_hc(int addr_io, int data_io, int irq)
+{
+       struct sl811_hc *hc;
+
+       PDEBUG(5, "enter");
+
+       hc = sl811_alloc_hc();
+       if (!hc)
+               return -ENOMEM;
+
+       sl811_init_irq();
+
+       if (!request_region(addr_io, 1, "SL811 USB HOST")) {
+               PDEBUG(1, "request address %d failed", addr_io);
+               sl811_release_hc(hc);
+               return -EBUSY;
+       }
+       hc->addr_io =   addr_io;
+
+       if (!request_region(data_io, 1, "SL811 USB HOST")) {
+               PDEBUG(1, "request address %d failed", data_io);
+               sl811_release_hc (hc);
+               return -EBUSY;
+       }
+       hc->data_io =   data_io;
+
+       usb_register_bus(hc->bus);
+
+       if (request_irq(irq, sl811_interrupt, 0, "SL811", hc)) {
+               PDEBUG(1, "request interrupt %d failed", irq);
+               sl811_release_hc(hc);
+               return -EBUSY;
+       }
+       hc->irq = irq;
+
+       printk(KERN_INFO __FILE__ ": USB SL811 at %x, addr2 = %x, IRQ %d\n",
+               addr_io, data_io, irq);
+
+       if (sl811_reg_test(hc)) {
+               PDEBUG(1, "SL811 register test failed!");
+               sl811_release_hc(hc);
+               return -ENODEV;
+       }
+       
+       sl811_hc_reset(hc);
+       sl811_connect_rh(hc);
+       
+       return 0;
+}
+
+/*
+ * This        is an init function, and it is the first function being called
+ *
+ * Return: 0 = success or error        condition
+ */
+static int __init sl811_hcd_init(void)
+{
+       int ret;
+
+       PDEBUG(5, "etner");
+
+       info(DRIVER_VERSION " : " DRIVER_DESC);
+       
+       ret = sl811_found_hc(sl811_addr_io, sl811_data_io, sl811_irq);
+
+       return ret;
+}
+
+/*
+ * This        is a cleanup function, and it is called when module is unloaded.
+ */
+static void __exit sl811_hcd_cleanup(void)
+{
+       struct list_head *list = sl811_hcd_list.next;
+       struct sl811_hc *hc;
+
+       PDEBUG(5, "enter");
+
+       for (; list != &sl811_hcd_list; ) {
+               hc = list_entry(list, struct sl811_hc, hc_hcd_list);
+               list = list->next;
+               sl811_release_hc(hc);
+       }
+}
+
+module_init(sl811_hcd_init);
+module_exit(sl811_hcd_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
diff -Nru a/drivers/usb/host/sl811.h b/drivers/usb/host/sl811.h
--- /dev/null   Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/host/sl811.h  Mon Jul 14 10:04:12 2003
@@ -0,0 +1,177 @@
+#ifndef __LINUX_SL811_H
+#define __LINUX_SL811_H
+
+//#define SL811_DEBUG
+
+#ifdef SL811_DEBUG
+       #define PDEBUG(level, fmt, args...) \
+               if (debug >= (level)) info("[%s:%d] " fmt, \
+               __PRETTY_FUNCTION__, __LINE__ , ## args)
+#else
+       #define PDEBUG(level, fmt, args...) do {} while(0)
+#endif
+
+//#define SL811_TIMEOUT
+               
+/* Sl811 host control register */
+#define        SL811_CTRL_A            0x00
+#define        SL811_ADDR_A            0x01
+#define        SL811_LEN_A             0x02
+#define        SL811_STS_A             0x03    /* read */
+#define        SL811_PIDEP_A           0x03    /* write */
+#define        SL811_CNT_A             0x04    /* read */
+#define        SL811_DEV_A             0x04    /* write */
+#define        SL811_CTRL1             0x05
+#define        SL811_INTR              0x06
+#define        SL811_CTRL_B            0x08
+#define        SL811_ADDR_B            0x09
+#define        SL811_LEN_B             0x0A
+#define        SL811_STS_B             0x0B    /* read */
+#define        SL811_PIDEP_B           0x0B    /* write */
+#define        SL811_CNT_B             0x0C    /* read */
+#define        SL811_DEV_B             0x0C    /* write */
+#define        SL811_INTRSTS           0x0D    /* write clears bitwise */
+#define        SL811_HWREV             0x0E    /* read */
+#define        SL811_SOFLOW            0x0E    /* write */
+#define        SL811_SOFCNTDIV         0x0F    /* read */
+#define        SL811_CTRL2             0x0F    /* write */
+
+/* USB control register bits (addr 0x00 and addr 0x08) */
+#define        SL811_USB_CTRL_ARM      0x01
+#define        SL811_USB_CTRL_ENABLE   0x02
+#define        SL811_USB_CTRL_DIR_OUT  0x04
+#define        SL811_USB_CTRL_ISO      0x10
+#define        SL811_USB_CTRL_SOF      0x20
+#define        SL811_USB_CTRL_TOGGLE_1 0x40
+#define        SL811_USB_CTRL_PREAMBLE 0x80
+
+/* USB status register bits (addr 0x03 and addr 0x0B) */
+#define        SL811_USB_STS_ACK       0x01
+#define        SL811_USB_STS_ERROR     0x02
+#define        SL811_USB_STS_TIMEOUT   0x04
+#define        SL811_USB_STS_TOGGLE_1  0x08
+#define        SL811_USB_STS_SETUP     0x10
+#define        SL811_USB_STS_OVERFLOW  0x20
+#define        SL811_USB_STS_NAK       0x40
+#define        SL811_USB_STS_STALL     0x80
+
+/* Control register 1 bits (addr 0x05) */
+#define        SL811_CTRL1_SOF         0x01
+#define        SL811_CTRL1_RESET       0x08
+#define        SL811_CTRL1_JKSTATE     0x10
+#define        SL811_CTRL1_SPEED_LOW   0x20
+#define        SL811_CTRL1_SUSPEND     0x40
+
+/* Interrut enable (addr 0x06) and interrupt status register bits (addr 0x0D) */
+#define        SL811_INTR_DONE_A       0x01
+#define        SL811_INTR_DONE_B       0x02
+#define        SL811_INTR_SOF          0x10
+#define        SL811_INTR_INSRMV       0x20
+#define        SL811_INTR_DETECT       0x40
+#define        SL811_INTR_NOTPRESENT   0x40
+#define        SL811_INTR_SPEED_FULL   0x80    /* only in status reg */
+
+/* HW rev and SOF lo register bits (addr 0x0E) */
+#define        SL811_HWR_HWREV         0xF0
+
+/* SOF counter and control reg 2 (addr 0x0F) */
+#define        SL811_CTL2_SOFHI        0x3F
+#define        SL811_CTL2_DSWAP        0x40
+#define        SL811_CTL2_HOST         0x80
+
+/* Set up for 1-ms SOF time. */
+#define SL811_12M_LOW          0xE0
+#define SL811_12M_HI           0x2E
+
+#define SL811_DATA_START       0x10
+#define SL811_DATA_LIMIT       240
+
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS           0x0080
+#define RH_CLEAR_FEATURE        0x0100
+#define RH_SET_FEATURE          0x0300
+#define RH_SET_ADDRESS         0x0500
+#define RH_GET_DESCRIPTOR      0x0680
+#define RH_SET_DESCRIPTOR       0x0700
+#define RH_GET_CONFIGURATION   0x0880
+#define RH_SET_CONFIGURATION   0x0900
+#define RH_GET_STATE            0x0280
+#define RH_GET_INTERFACE        0x0A80
+#define RH_SET_INTERFACE        0x0B00
+#define RH_SYNC_FRAME           0x0C80
+
+
+#define PIDEP(pid, ep) (((pid) & 0x0f) << 4 | (ep))
+
+/* Virtual Root HUB */
+struct virt_root_hub {
+       int devnum;                     /* Address of Root Hub endpoint */ 
+       void *urb;                      /* interrupt URB of root hub */
+       int send;                       /* active flag */
+       int interval;                   /* intervall of roothub interrupt transfers */
+       struct timer_list rh_int_timer; /* intervall timer for rh interrupt EP */
+};
+
+struct sl811_td {
+       /* hardware */
+       __u8 ctrl;                      /* control register */
+       
+       /* write */                     
+       __u8 addr;                      /* base adrress register */
+       __u8 len;                       /* base length register */
+       __u8 pidep;                     /* PId and endpoint register */
+       __u8 dev;                       /* device address register */
+       
+       /* read */
+       __u8 status;                    /* status register */
+       __u8 left;                      /* transfer count register */
+       
+       /* software */
+       __u8 errcnt;                    /* error count, begin with 3 */
+       __u8 done;                      /* is this td tranfer done */
+       __u8 *buf;                      /* point to data buffer for tranfer */
+       int bustime;                    /* the bus time need by this td */
+       int td_status;                  /* the status of this td */
+       int nakcnt;                     /* number of naks */
+       struct urb *urb;                        /* the urb this td belongs to */
+       struct list_head td_list;       /* link to a list of the urb */
+};
+
+struct sl811_urb_priv {
+       struct urb *urb;                        /* the urb this priv beloings to */
+       struct list_head td_list;       /* list of all the td of this urb */
+       struct sl811_td *cur_td;                /* current td is in processing or it 
will be */
+       struct sl811_td *first_td;              /* the first td of this urb */
+       struct sl811_td *last_td;               /* the last td of this urb */
+       int interval;                   /* the query time value for intr urb */
+       int unlink;                     /* is the this urb unlinked */
+       unsigned long inserttime;       /* the time when insert to list */
+};
+
+struct sl811_hc {
+       spinlock_t hc_lock;             /* Lock for this structure */
+       
+       int irq;                        /* IRQ number this hc use */
+       int addr_io;                    /* I/O address line address */
+       int data_io;                    /* I/O data line address */
+       struct virt_root_hub rh;                /* root hub */
+       struct usb_port_status rh_status;/* root hub port status */
+       struct list_head urb_list[6];   /* set of urbs, the order is 
iso,intr,ctrl,bulk,inactive intr, wait */
+       struct list_head *cur_list;     /* the current list is in process */
+       wait_queue_head_t waitq;        /* deletion of URBs and devices needs a 
waitqueue */
+       struct sl811_td *cur_td;                /* point to the td is in process */
+       struct list_head hc_hcd_list;   /* list of all hci_hcd */
+       struct usb_bus *bus;            /* our bus */
+       int active_urbs;                /* total number of active usbs */
+       int frame_number;               /* the current frame number, we do't use it, 
any one need it? */
+};
+
+#define iso_list       urb_list[0]     /* set of isoc urbs */
+#define intr_list      urb_list[1]     /* ordered (tree) set of int urbs */
+#define ctrl_list      urb_list[2]     /* set of ctrl urbs */
+#define bulk_list      urb_list[3]     /* set of bulk urbs */
+#define idle_intr_list urb_list[4]     /* set of intr urbs in its idle time*/
+#define wait_list      urb_list[5]     /* set of wait urbs */
+
+#endif



-------------------------------------------------------
This SF.Net email sponsored by: Parasoft
Error proof Web apps, automate testing & more.
Download & eval WebKing and get a free book.
www.parasoft.com/bulletproofapps1
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to