Merge bk://oe-devel.bkbits.net/openembedded
authornslu2-linux.adm@bkbits.net <nslu2-linux.adm@bkbits.net>
Mon, 9 May 2005 21:39:51 +0000 (21:39 +0000)
committernslu2-linux.adm@bkbits.net <nslu2-linux.adm@bkbits.net>
Mon, 9 May 2005 21:39:51 +0000 (21:39 +0000)
into  bkbits.net:/repos/n/nslu2-linux/openembedded

2005/05/09 23:23:33+02:00 uni-frankfurt.de!mickeyl
merge cfu1-modules

2005/05/09 23:14:49+02:00 uni-frankfurt.de!mickeyl
Merge bk://oe-devel@oe-devel.bkbits.net/openembedded
into r2d2.tm.informatik.uni-frankfurt.de:/local/pkg/oe/packages

2005/05/09 23:12:50+02:00 uni-frankfurt.de!mickeyl
upgrade cfu1 driver courtesy Botond Botyanszki

2005/05/09 20:27:28+02:00 uni-frankfurt.de!mickeyl
add Earthmate userland library. groundwork by Simon Pickering

BKrev: 427fd8a7SbeNYa-AZ1i7hWo5_CV2iA

14 files changed:
packages/cfu1/cfu1-driver_2.6.bb [deleted file]
packages/cfu1/cfu1-modules_2.6.bb
packages/cfu1/files/Makefile [new file with mode: 0644]
packages/cfu1/files/cfu1-driver/hcd.h [deleted file]
packages/cfu1/files/cfu1-driver/hub.h [deleted file]
packages/cfu1/files/cfu1-driver/sl811.h [deleted file]
packages/cfu1/files/cfu1-driver/sl811_cs.c [deleted file]
packages/cfu1/files/cfu1-driver/sl811_hcd.c [deleted file]
packages/cfu1/files/hcd.h [new file with mode: 0644]
packages/cfu1/files/hub.h [new file with mode: 0644]
packages/cfu1/files/sl811.h [new file with mode: 0644]
packages/cfu1/files/sl811_cs.c [new file with mode: 0644]
packages/cfu1/files/sl811_hcd.c [new file with mode: 0644]
packages/emul/emul_1.0.5.bb [moved from packages/cfu1/files/cfu1-driver/Makefile with 100% similarity]

diff --git a/packages/cfu1/cfu1-driver_2.6.bb b/packages/cfu1/cfu1-driver_2.6.bb
deleted file mode 100644 (file)
index 72d67ed..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-DESCRIPTION = "PCMCIA driver for the RATOC REX-CFU1 USB host controller CF card."
-MAINTAINER = "Botond Botyanszki <openembedded@siliconium.net>"
-SECTION = "kernel/modules"
-PRIORITY = "optional"
-DEPENDS = "virtual/kernel"
-
-RDEPENDS = "kernel-module-usbcore"
-
-SRC_URI = "file://rex-cfu1.conf \
- file://cfu1-driver/Makefile \
- file://cfu1-driver/sl811_cs.c \
- file://cfu1-driver/sl811_hcd.c \
- file://cfu1-driver/sl811.h \
- file://cfu1-driver/hcd.h \
- file://cfu1-driver/hub.h \
- "
-
-S = "${WORKDIR}/${PN}"
-
-inherit module
-
-#EXTRA_OEMAKE = "-C ${STAGING_KERNEL_DIR} SUBDIRS=${WORKDIR}/cfu1-driver"
-EXTRA_OEMAKE = 'EXTRA_CFLAGS="-DCONFIG_USB_SL811_CS -DCONFIG_USB_DEBUG" -C ${STAGING_KERNEL_DIR} SUBDIRS=${WORKDIR}/cfu1-driver'
-
-do_configure() {
-        if grep CONFIG_PCMCIA=[ym] ${STAGING_KERNEL_DIR}/.config && 
-           grep CONFIG_USB_ARCH_HAS_HCD=[ym] ${STAGING_KERNEL_DIR}/.config; then
-#                echo "CONFIG_USB_SL811_CS=m"          >> config.mk
-                echo ".config OK"
-       else
-               echo "CONFIG_USB_ARCH_HAS_HCD or CONFIG_PCMCIA not defined in kernel config!"
-               exit 1
-        fi
-}
-
-do_compile() {
-       unset CFLAGS CPPFLAGS CXXFLAGS LDFLAGS
-       oe_runmake modules
-}
-               
-
-do_install() {
-       install -d ${D}/lib/modules/${KERNEL_VERSION}/pcmcia/
-       install -m 0644 sl811_cs.ko ${D}/lib/modules/${KERNEL_VERSION}/pcmcia/
-       install -m 0644 sl811_hcd.ko ${D}/lib/modules/${KERNEL_VERSION}/pcmcia/
-}
-
-do_install_append () {
-       install -d ${D}/${sysconfdir}/pcmcia/
-       install -m 0644 ${WORKDIR}/rex-cfu1.conf ${D}/${sysconfdir}/pcmcia/
-}
index e69de29..07aa7b5 100644 (file)
@@ -0,0 +1,31 @@
+DESCRIPTION = "PCMCIA driver for the RATOC REX-CFU1 USB host controller CF card."
+MAINTAINER = "Botond Botyanszki <openembedded@siliconium.net>"
+SECTION = "kernel/modules"
+PRIORITY = "optional"
+DEPENDS = "virtual/kernel"
+LICENSE = "GPL"
+RDEPENDS = "kernel-module-usbcore"
+PR = "r2"
+
+SRC_URI = "file://rex-cfu1.conf \
+           file://Makefile \
+           file://sl811_cs.c \
+           file://sl811_hcd.c \
+           file://sl811.h \
+           file://hcd.h \
+           file://hub.h"
+
+S = "${WORKDIR}"
+
+inherit module
+
+EXTRA_OEMAKE = 'EXTRA_CFLAGS="-DCONFIG_USB_SL811_CS -DCONFIG_USB_DEBUG" -C ${STAGING_KERNEL_DIR} SUBDIRS=${WORKDIR}'
+
+do_install() {
+       install -d ${D}/lib/modules/${KERNEL_VERSION}/pcmcia/
+       install -m 0644 sl811_cs.ko ${D}/lib/modules/${KERNEL_VERSION}/pcmcia/
+       install -m 0644 sl811_hcd.ko ${D}/lib/modules/${KERNEL_VERSION}/pcmcia/
+
+       install -d ${D}/${sysconfdir}/pcmcia/
+       install -m 0644 ${WORKDIR}/rex-cfu1.conf ${D}/${sysconfdir}/pcmcia/
+}
diff --git a/packages/cfu1/files/Makefile b/packages/cfu1/files/Makefile
new file mode 100644 (file)
index 0000000..dbfb40d
--- /dev/null
@@ -0,0 +1,2 @@
+obj-m += sl811_cs.o
+obj-m += sl811_hcd.o
diff --git a/packages/cfu1/files/cfu1-driver/hcd.h b/packages/cfu1/files/cfu1-driver/hcd.h
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/packages/cfu1/files/cfu1-driver/hub.h b/packages/cfu1/files/cfu1-driver/hub.h
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/packages/cfu1/files/cfu1-driver/sl811.h b/packages/cfu1/files/cfu1-driver/sl811.h
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/packages/cfu1/files/cfu1-driver/sl811_cs.c b/packages/cfu1/files/cfu1-driver/sl811_cs.c
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/packages/cfu1/files/cfu1-driver/sl811_hcd.c b/packages/cfu1/files/cfu1-driver/sl811_hcd.c
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/packages/cfu1/files/hcd.h b/packages/cfu1/files/hcd.h
new file mode 100644 (file)
index 0000000..a9bb990
--- /dev/null
@@ -0,0 +1,487 @@
+/*
+ * Copyright (c) 2001-2002 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#ifdef __KERNEL__
+
+/* This file contains declarations of usbcore internals that are mostly
+ * used or exposed by Host Controller Drivers.
+ */
+
+/*
+ * USB Packet IDs (PIDs)
+ */
+#define USB_PID_UNDEF_0                        0xf0
+#define USB_PID_OUT                    0xe1
+#define USB_PID_ACK                    0xd2
+#define USB_PID_DATA0                  0xc3
+#define USB_PID_PING                   0xb4    /* USB 2.0 */
+#define USB_PID_SOF                    0xa5
+#define USB_PID_NYET                   0x96    /* USB 2.0 */
+#define USB_PID_DATA2                  0x87    /* USB 2.0 */
+#define USB_PID_SPLIT                  0x78    /* USB 2.0 */
+#define USB_PID_IN                     0x69
+#define USB_PID_NAK                    0x5a
+#define USB_PID_DATA1                  0x4b
+#define USB_PID_PREAMBLE               0x3c    /* Token mode */
+#define USB_PID_ERR                    0x3c    /* USB 2.0: handshake mode */
+#define USB_PID_SETUP                  0x2d
+#define USB_PID_STALL                  0x1e
+#define USB_PID_MDATA                  0x0f    /* USB 2.0 */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * USB Host Controller Driver (usb_hcd) framework
+ *
+ * Since "struct usb_bus" is so thin, you can't share much code in it.
+ * This framework is a layer over that, and should be more sharable.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+struct usb_hcd {       /* usb_bus.hcpriv points to this */
+
+       /*
+        * housekeeping
+        */
+       struct usb_bus          self;           /* hcd is-a bus */
+
+       const char              *product_desc;  /* product/vendor string */
+       char                    irq_descr[24];  /* driver + bus # */
+
+       struct timer_list       rh_timer;       /* drives root-hub polling */
+       struct urb              *status_urb;    /* the current status urb */
+
+       /*
+        * hardware info/state
+        */
+       const struct hc_driver  *driver;        /* hw-specific hooks */
+       unsigned                saw_irq : 1;
+       unsigned                can_wakeup:1;   /* hw supports wakeup? */
+       unsigned                remote_wakeup:1;/* sw should use wakeup? */
+       unsigned                rh_registered:1;/* is root hub registered? */
+
+       /* The next flag is a stopgap, to be removed when all the HCDs
+        * support the new root-hub polling mechanism. */
+       unsigned                uses_new_polling:1;
+       unsigned                poll_rh:1;      /* poll for rh status? */
+       unsigned                poll_pending:1; /* status has changed? */
+
+       int                     irq;            /* irq allocated */
+       void __iomem            *regs;          /* device memory/io */
+       u64                     rsrc_start;     /* memory/io resource start */
+       u64                     rsrc_len;       /* memory/io resource length */
+       unsigned                power_budget;   /* in mA, 0 = no limit */
+
+#define HCD_BUFFER_POOLS       4
+       struct dma_pool         *pool [HCD_BUFFER_POOLS];
+
+       int                     state;
+#      define  __ACTIVE                0x01
+#      define  __SUSPEND               0x04
+#      define  __TRANSIENT             0x80
+
+#      define  HC_STATE_HALT           0
+#      define  HC_STATE_RUNNING        (__ACTIVE)
+#      define  HC_STATE_QUIESCING      (__SUSPEND|__TRANSIENT|__ACTIVE)
+#      define  HC_STATE_RESUMING       (__SUSPEND|__TRANSIENT)
+#      define  HC_STATE_SUSPENDED      (__SUSPEND)
+
+#define        HC_IS_RUNNING(state) ((state) & __ACTIVE)
+#define        HC_IS_SUSPENDED(state) ((state) & __SUSPEND)
+
+       /* more shared queuing code would be good; it should support
+        * smarter scheduling, handle transaction translators, etc;
+        * input size of periodic table to an interrupt scheduler. 
+        * (ohci 32, uhci 1024, ehci 256/512/1024).
+        */
+
+       /* The HC driver's private data is stored at the end of
+        * this structure.
+        */
+       unsigned long hcd_priv[0]
+                       __attribute__ ((aligned (sizeof(unsigned long))));
+};
+
+/* 2.4 does this a bit differently ... */
+static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd)
+{
+       return &hcd->self;
+}
+
+
+// urb.hcpriv is really hardware-specific
+
+struct hcd_timeout {   /* timeouts we allocate */
+       struct list_head        timeout_list;
+       struct timer_list       timer;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * FIXME usb_operations should vanish or become hc_driver,
+ * when usb_bus and usb_hcd become the same thing.
+ */
+
+struct usb_operations {
+       int (*get_frame_number) (struct usb_device *usb_dev);
+       int (*submit_urb) (struct urb *urb, int mem_flags);
+       int (*unlink_urb) (struct urb *urb, int status);
+
+       /* allocate dma-consistent buffer for URB_DMA_NOMAPPING */
+       void *(*buffer_alloc)(struct usb_bus *bus, size_t size,
+                       int mem_flags,
+                       dma_addr_t *dma);
+       void (*buffer_free)(struct usb_bus *bus, size_t size,
+                       void *addr, dma_addr_t dma);
+
+       void (*disable)(struct usb_device *udev,
+                       struct usb_host_endpoint *ep);
+
+       /* global suspend/resume of bus */
+       int (*hub_suspend)(struct usb_bus *);
+       int (*hub_resume)(struct usb_bus *);
+};
+
+/* each driver provides one of these, and hardware init support */
+
+struct pt_regs;
+
+struct hc_driver {
+       const char      *description;   /* "ehci-hcd" etc */
+       const char      *product_desc;  /* product/vendor string */
+       size_t          hcd_priv_size;  /* size of private data */
+
+       /* irq handler */
+       irqreturn_t     (*irq) (struct usb_hcd *hcd, struct pt_regs *regs);
+
+       int     flags;
+#define        HCD_MEMORY      0x0001          /* HC regs use memory (else I/O) */
+#define        HCD_USB11       0x0010          /* USB 1.1 */
+#define        HCD_USB2        0x0020          /* USB 2.0 */
+
+       /* called to init HCD and root hub */
+       int     (*reset) (struct usb_hcd *hcd);
+       int     (*start) (struct usb_hcd *hcd);
+
+       /* NOTE:  these suspend/resume calls relate to the HC as
+        * a whole, not just the root hub; they're for bus glue.
+        */
+       /* called after all devices were suspended */
+       int     (*suspend) (struct usb_hcd *hcd, pm_message_t message);
+
+       /* called before any devices get resumed */
+       int     (*resume) (struct usb_hcd *hcd);
+
+       /* cleanly make HCD stop writing memory and doing I/O */
+       void    (*stop) (struct usb_hcd *hcd);
+
+       /* return current frame number */
+       int     (*get_frame_number) (struct usb_hcd *hcd);
+
+       /* manage i/o requests, device state */
+       int     (*urb_enqueue) (struct usb_hcd *hcd,
+                                       struct usb_host_endpoint *ep,
+                                       struct urb *urb,
+                                       int mem_flags);
+       int     (*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb);
+
+       /* hw synch, freeing endpoint resources that urb_dequeue can't */
+       void    (*endpoint_disable)(struct usb_hcd *hcd,
+                       struct usb_host_endpoint *ep);
+
+       /* root hub support */
+       int             (*hub_status_data) (struct usb_hcd *hcd, char *buf);
+       int             (*hub_control) (struct usb_hcd *hcd,
+                               u16 typeReq, u16 wValue, u16 wIndex,
+                               char *buf, u16 wLength);
+       int             (*hub_suspend)(struct usb_hcd *);
+       int             (*hub_resume)(struct usb_hcd *);
+       int             (*start_port_reset)(struct usb_hcd *, unsigned port_num);
+       void            (*hub_irq_enable)(struct usb_hcd *);
+               /* Needed only if port-change IRQs are level-triggered */
+};
+
+extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs);
+
+extern struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
+               struct device *dev, char *bus_name);
+extern void usb_put_hcd (struct usb_hcd *hcd);
+extern int usb_add_hcd(struct usb_hcd *hcd,
+               unsigned int irqnum, unsigned long irqflags);
+extern void usb_remove_hcd(struct usb_hcd *hcd);
+
+#ifdef CONFIG_PCI
+struct pci_dev;
+struct pci_device_id;
+extern int usb_hcd_pci_probe (struct pci_dev *dev,
+                               const struct pci_device_id *id);
+extern void usb_hcd_pci_remove (struct pci_dev *dev);
+
+#ifdef CONFIG_PM
+extern int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t state);
+extern int usb_hcd_pci_resume (struct pci_dev *dev);
+#endif /* CONFIG_PM */
+
+#endif /* CONFIG_PCI */
+
+/* pci-ish (pdev null is ok) buffer alloc/mapping support */
+int hcd_buffer_create (struct usb_hcd *hcd);
+void hcd_buffer_destroy (struct usb_hcd *hcd);
+
+void *hcd_buffer_alloc (struct usb_bus *bus, size_t size,
+       int mem_flags, dma_addr_t *dma);
+void hcd_buffer_free (struct usb_bus *bus, size_t size,
+       void *addr, dma_addr_t dma);
+
+/* generic bus glue, needed for host controllers that don't use PCI */
+extern irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs *r);
+
+extern void usb_hc_died (struct usb_hcd *hcd);
+extern void usb_hcd_poll_rh_status(struct usb_hcd *hcd);
+
+/* -------------------------------------------------------------------------- */
+
+/* Enumeration is only for the hub driver, or HCD virtual root hubs */
+extern struct usb_device *usb_alloc_dev(struct usb_device *parent,
+                                       struct usb_bus *, unsigned port);
+extern int usb_new_device(struct usb_device *dev);
+extern void usb_disconnect(struct usb_device **);
+
+extern int usb_get_configuration(struct usb_device *dev);
+extern void usb_destroy_configuration(struct usb_device *dev);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * HCD Root Hub support
+ */
+
+#include "hub.h"
+
+/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */
+#define DeviceRequest \
+       ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
+#define DeviceOutRequest \
+       ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
+
+#define InterfaceRequest \
+       ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+
+#define EndpointRequest \
+       ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+#define EndpointOutRequest \
+       ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+
+/* class requests from the USB 2.0 hub spec, table 11-15 */
+/* GetBusState and SetHubDescriptor are optional, omitted */
+#define ClearHubFeature                (0x2000 | USB_REQ_CLEAR_FEATURE)
+#define ClearPortFeature       (0x2300 | USB_REQ_CLEAR_FEATURE)
+#define GetHubDescriptor       (0xa000 | USB_REQ_GET_DESCRIPTOR)
+#define GetHubStatus           (0xa000 | USB_REQ_GET_STATUS)
+#define GetPortStatus          (0xa300 | USB_REQ_GET_STATUS)
+#define SetHubFeature          (0x2000 | USB_REQ_SET_FEATURE)
+#define SetPortFeature         (0x2300 | USB_REQ_SET_FEATURE)
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Generic bandwidth allocation constants/support
+ */
+#define FRAME_TIME_USECS       1000L
+#define BitTime(bytecount)  (7 * 8 * bytecount / 6)  /* with integer truncation */
+               /* Trying not to use worst-case bit-stuffing
+                   of (7/6 * 8 * bytecount) = 9.33 * bytecount */
+               /* bytecount = data payload byte count */
+
+#define NS_TO_US(ns)   ((ns + 500L) / 1000L)
+                       /* convert & round nanoseconds to microseconds */
+
+extern void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb,
+               int bustime, int isoc);
+extern void usb_release_bandwidth (struct usb_device *dev, struct urb *urb,
+               int isoc);
+
+/*
+ * Full/low speed bandwidth allocation constants/support.
+ */
+#define BW_HOST_DELAY  1000L           /* nanoseconds */
+#define BW_HUB_LS_SETUP        333L            /* nanoseconds */
+                        /* 4 full-speed bit times (est.) */
+
+#define FRAME_TIME_BITS         12000L         /* frame = 1 millisecond */
+#define FRAME_TIME_MAX_BITS_ALLOC      (90L * FRAME_TIME_BITS / 100L)
+#define FRAME_TIME_MAX_USECS_ALLOC     (90L * FRAME_TIME_USECS / 100L)
+
+extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb);
+
+/*
+ * Ceiling microseconds (typical) for that many bytes at high speed
+ * ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed
+ * to preallocate bandwidth)
+ */
+#define USB2_HOST_DELAY        5       /* nsec, guess */
+#define HS_USECS(bytes) NS_TO_US ( ((55 * 8 * 2083)/1000) \
+       + ((2083UL * (3167 + BitTime (bytes)))/1000) \
+       + USB2_HOST_DELAY)
+#define HS_USECS_ISO(bytes) NS_TO_US ( ((38 * 8 * 2083)/1000) \
+       + ((2083UL * (3167 + BitTime (bytes)))/1000) \
+       + USB2_HOST_DELAY)
+
+extern long usb_calc_bus_time (int speed, int is_input,
+                       int isoc, int bytecount);
+
+/*-------------------------------------------------------------------------*/
+
+extern struct usb_bus *usb_alloc_bus (struct usb_operations *);
+
+extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd);
+
+extern void usb_set_device_state(struct usb_device *udev,
+               enum usb_device_state new_state);
+
+/*-------------------------------------------------------------------------*/
+
+/* exported only within usbcore */
+
+extern struct list_head usb_bus_list;
+extern struct semaphore usb_bus_list_lock;
+extern wait_queue_head_t usb_kill_urb_queue;
+
+extern struct usb_bus *usb_bus_get (struct usb_bus *bus);
+extern void usb_bus_put (struct usb_bus *bus);
+
+extern void usb_enable_root_hub_irq (struct usb_bus *bus);
+
+extern int usb_find_interface_driver (struct usb_device *dev,
+       struct usb_interface *interface);
+
+#define usb_endpoint_out(ep_dir)       (!((ep_dir) & USB_DIR_IN))
+
+/*
+ * USB device fs stuff
+ */
+
+#ifdef CONFIG_USB_DEVICEFS
+
+/*
+ * these are expected to be called from the USB core/hub thread
+ * with the kernel lock held
+ */
+extern void usbfs_add_bus(struct usb_bus *bus);
+extern void usbfs_remove_bus(struct usb_bus *bus);
+extern void usbfs_add_device(struct usb_device *dev);
+extern void usbfs_remove_device(struct usb_device *dev);
+extern void usbfs_update_special (void);
+
+extern int usbfs_init(void);
+extern void usbfs_cleanup(void);
+
+#else /* CONFIG_USB_DEVICEFS */
+
+static inline void usbfs_add_bus(struct usb_bus *bus) {}
+static inline void usbfs_remove_bus(struct usb_bus *bus) {}
+static inline void usbfs_add_device(struct usb_device *dev) {}
+static inline void usbfs_remove_device(struct usb_device *dev) {}
+static inline void usbfs_update_special (void) {}
+
+static inline int usbfs_init(void) { return 0; }
+static inline void usbfs_cleanup(void) { }
+
+#endif /* CONFIG_USB_DEVICEFS */
+
+/*-------------------------------------------------------------------------*/
+
+#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
+
+struct usb_mon_operations {
+       void (*urb_submit)(struct usb_bus *bus, struct urb *urb);
+       void (*urb_submit_error)(struct usb_bus *bus, struct urb *urb, int err);
+       void (*urb_complete)(struct usb_bus *bus, struct urb *urb);
+       /* void (*urb_unlink)(struct usb_bus *bus, struct urb *urb); */
+       void (*bus_add)(struct usb_bus *bus);
+       void (*bus_remove)(struct usb_bus *bus);
+};
+
+extern struct usb_mon_operations *mon_ops;
+
+static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb)
+{
+       if (bus->monitored)
+               (*mon_ops->urb_submit)(bus, urb);
+}
+
+static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
+    int error)
+{
+       if (bus->monitored)
+               (*mon_ops->urb_submit_error)(bus, urb, error);
+}
+
+static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb)
+{
+       if (bus->monitored)
+               (*mon_ops->urb_complete)(bus, urb);
+}
+static inline void usbmon_notify_bus_add(struct usb_bus *bus)
+{
+       if (mon_ops)
+               (*mon_ops->bus_add)(bus);
+}
+
+static inline void usbmon_notify_bus_remove(struct usb_bus *bus)
+{
+       if (mon_ops)
+               (*mon_ops->bus_remove)(bus);
+}
+
+int usb_mon_register(struct usb_mon_operations *ops);
+void usb_mon_deregister(void);
+
+#else
+
+static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb) {}
+static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
+    int error) {}
+static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb) {}
+static inline void usbmon_notify_bus_add(struct usb_bus *bus) {}
+static inline void usbmon_notify_bus_remove(struct usb_bus *bus) {}
+
+#endif /* CONFIG_USB_MON */
+
+/*-------------------------------------------------------------------------*/
+
+/* hub.h ... DeviceRemovable in 2.4.2-ac11, gone in 2.4.10 */
+// bleech -- resurfaced in 2.4.11 or 2.4.12
+#define bitmap         DeviceRemovable
+
+
+/*-------------------------------------------------------------------------*/
+
+/* random stuff */
+
+#define        RUN_CONTEXT (in_irq () ? "in_irq" \
+               : (in_interrupt () ? "in_interrupt" : "can sleep"))
+
+
+#endif /* __KERNEL__ */
+
diff --git a/packages/cfu1/files/hub.h b/packages/cfu1/files/hub.h
new file mode 100644 (file)
index 0000000..53bf564
--- /dev/null
@@ -0,0 +1,227 @@
+#ifndef __LINUX_HUB_H
+#define __LINUX_HUB_H
+
+/*
+ * Hub protocol and driver data structures.
+ *
+ * Some of these are known to the "virtual root hub" code
+ * in host controller drivers.
+ */
+
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <linux/compiler.h>    /* likely()/unlikely() */
+
+/*
+ * Hub request types
+ */
+
+#define USB_RT_HUB     (USB_TYPE_CLASS | USB_RECIP_DEVICE)
+#define USB_RT_PORT    (USB_TYPE_CLASS | USB_RECIP_OTHER)
+
+/*
+ * Hub class requests
+ * See USB 2.0 spec Table 11-16
+ */
+#define HUB_CLEAR_TT_BUFFER    8
+#define HUB_RESET_TT           9
+#define HUB_GET_TT_STATE       10
+#define HUB_STOP_TT            11
+
+/*
+ * Hub Class feature numbers
+ * See USB 2.0 spec Table 11-17
+ */
+#define C_HUB_LOCAL_POWER      0
+#define C_HUB_OVER_CURRENT     1
+
+/*
+ * Port feature numbers
+ * See USB 2.0 spec Table 11-17
+ */
+#define USB_PORT_FEAT_CONNECTION       0
+#define USB_PORT_FEAT_ENABLE           1
+#define USB_PORT_FEAT_SUSPEND          2
+#define USB_PORT_FEAT_OVER_CURRENT     3
+#define USB_PORT_FEAT_RESET            4
+#define USB_PORT_FEAT_POWER            8
+#define USB_PORT_FEAT_LOWSPEED         9
+#define USB_PORT_FEAT_HIGHSPEED                10
+#define USB_PORT_FEAT_C_CONNECTION     16
+#define USB_PORT_FEAT_C_ENABLE         17
+#define USB_PORT_FEAT_C_SUSPEND                18
+#define USB_PORT_FEAT_C_OVER_CURRENT   19
+#define USB_PORT_FEAT_C_RESET          20
+#define USB_PORT_FEAT_TEST              21
+#define USB_PORT_FEAT_INDICATOR         22
+
+/* 
+ * Hub Status and Hub Change results
+ * See USB 2.0 spec Table 11-19 and Table 11-20
+ */
+struct usb_port_status {
+       __le16 wPortStatus;
+       __le16 wPortChange;     
+} __attribute__ ((packed));
+
+/* 
+ * wPortStatus bit field
+ * See USB 2.0 spec Table 11-21
+ */
+#define USB_PORT_STAT_CONNECTION       0x0001
+#define USB_PORT_STAT_ENABLE           0x0002
+#define USB_PORT_STAT_SUSPEND          0x0004
+#define USB_PORT_STAT_OVERCURRENT      0x0008
+#define USB_PORT_STAT_RESET            0x0010
+/* bits 5 to 7 are reserved */
+#define USB_PORT_STAT_POWER            0x0100
+#define USB_PORT_STAT_LOW_SPEED                0x0200
+#define USB_PORT_STAT_HIGH_SPEED        0x0400
+#define USB_PORT_STAT_TEST              0x0800
+#define USB_PORT_STAT_INDICATOR         0x1000
+/* bits 13 to 15 are reserved */
+
+/* 
+ * wPortChange bit field
+ * See USB 2.0 spec Table 11-22
+ * Bits 0 to 4 shown, bits 5 to 15 are reserved
+ */
+#define USB_PORT_STAT_C_CONNECTION     0x0001
+#define USB_PORT_STAT_C_ENABLE         0x0002
+#define USB_PORT_STAT_C_SUSPEND                0x0004
+#define USB_PORT_STAT_C_OVERCURRENT    0x0008
+#define USB_PORT_STAT_C_RESET          0x0010
+
+/*
+ * wHubCharacteristics (masks) 
+ * See USB 2.0 spec Table 11-13, offset 3
+ */
+#define HUB_CHAR_LPSM          0x0003 /* D1 .. D0 */
+#define HUB_CHAR_COMPOUND      0x0004 /* D2       */
+#define HUB_CHAR_OCPM          0x0018 /* D4 .. D3 */
+#define HUB_CHAR_TTTT           0x0060 /* D6 .. D5 */
+#define HUB_CHAR_PORTIND        0x0080 /* D7       */
+
+struct usb_hub_status {
+       __le16 wHubStatus;
+       __le16 wHubChange;
+} __attribute__ ((packed));
+
+/*
+ * Hub Status & Hub Change bit masks
+ * See USB 2.0 spec Table 11-19 and Table 11-20
+ * Bits 0 and 1 for wHubStatus and wHubChange
+ * Bits 2 to 15 are reserved for both
+ */
+#define HUB_STATUS_LOCAL_POWER 0x0001
+#define HUB_STATUS_OVERCURRENT 0x0002
+#define HUB_CHANGE_LOCAL_POWER 0x0001
+#define HUB_CHANGE_OVERCURRENT 0x0002
+
+
+/* 
+ * Hub descriptor 
+ * See USB 2.0 spec Table 11-13
+ */
+
+#define USB_DT_HUB                     (USB_TYPE_CLASS | 0x09)
+#define USB_DT_HUB_NONVAR_SIZE         7
+
+struct usb_hub_descriptor {
+       __u8  bDescLength;
+       __u8  bDescriptorType;
+       __u8  bNbrPorts;
+       __u16 wHubCharacteristics;
+       __u8  bPwrOn2PwrGood;
+       __u8  bHubContrCurrent;
+               /* add 1 bit for hub status change; round to bytes */
+       __u8  DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];
+       __u8  PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];
+} __attribute__ ((packed));
+
+
+/* port indicator status selectors, tables 11-7 and 11-25 */
+#define HUB_LED_AUTO   0
+#define HUB_LED_AMBER  1
+#define HUB_LED_GREEN  2
+#define HUB_LED_OFF    3
+
+enum hub_led_mode {
+       INDICATOR_AUTO = 0,
+       INDICATOR_CYCLE,
+       /* software blinks for attention:  software, hardware, reserved */
+       INDICATOR_GREEN_BLINK, INDICATOR_GREEN_BLINK_OFF,
+       INDICATOR_AMBER_BLINK, INDICATOR_AMBER_BLINK_OFF,
+       INDICATOR_ALT_BLINK, INDICATOR_ALT_BLINK_OFF
+} __attribute__ ((packed));
+
+struct usb_device;
+
+/*
+ * As of USB 2.0, full/low speed devices are segregated into trees.
+ * One type grows from USB 1.1 host controllers (OHCI, UHCI etc).
+ * The other type grows from high speed hubs when they connect to
+ * full/low speed devices using "Transaction Translators" (TTs).
+ *
+ * TTs should only be known to the hub driver, and high speed bus
+ * drivers (only EHCI for now).  They affect periodic scheduling and
+ * sometimes control/bulk error recovery.
+ */
+struct usb_tt {
+       struct usb_device       *hub;   /* upstream highspeed hub */
+       int                     multi;  /* true means one TT per port */
+
+       /* for control/bulk error recovery (CLEAR_TT_BUFFER) */
+       spinlock_t              lock;
+       struct list_head        clear_list;     /* of usb_tt_clear */
+       struct work_struct                      kevent;
+};
+
+struct usb_tt_clear {
+       struct list_head        clear_list;
+       unsigned                tt;
+       u16                     devinfo;
+};
+
+extern void usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe);
+
+struct usb_hub {
+       struct device           *intfdev;       /* the "interface" device */
+       struct usb_device       *hdev;
+       struct urb              *urb;           /* for interrupt polling pipe */
+
+       /* buffer for urb ... with extra space in case of babble */
+       char                    (*buffer)[8];
+       dma_addr_t              buffer_dma;     /* DMA address for buffer */
+       union {
+               struct usb_hub_status   hub;
+               struct usb_port_status  port;
+       }                       *status;        /* buffer for status reports */
+
+       int                     error;          /* last reported error */
+       int                     nerrors;        /* track consecutive errors */
+
+       struct list_head        event_list;     /* hubs w/data or errs ready */
+       unsigned long           event_bits[1];  /* status change bitmask */
+       unsigned long           change_bits[1]; /* ports with logical connect
+                                                       status change */
+       unsigned long           busy_bits[1];   /* ports being reset */
+#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
+#error event_bits[] is too short!
+#endif
+
+       struct usb_hub_descriptor *descriptor;  /* class descriptor */
+       struct usb_tt           tt;             /* Transaction Translator */
+
+       u8                      power_budget;   /* in 2mA units; or zero */
+
+       unsigned                quiescing:1;
+       unsigned                activating:1;
+       unsigned                resume_root_hub:1;
+
+       unsigned                has_indicators:1;
+       enum hub_led_mode       indicator[USB_MAXCHILDREN];
+       struct work_struct      leds;
+};
+
+#endif /* __LINUX_HUB_H */
diff --git a/packages/cfu1/files/sl811.h b/packages/cfu1/files/sl811.h
new file mode 100644 (file)
index 0000000..7690d98
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * SL811HS register declarations and HCD data structures
+ *
+ * Copyright (C) 2004 Psion Teklogix
+ * Copyright (C) 2004 David Brownell
+ * Copyright (C) 2001 Cypress Semiconductor Inc. 
+ */
+
+/*
+ * SL811HS has transfer registers, and control registers.  In host/master
+ * mode one set of registers is used; in peripheral/slave mode, another.
+ *  - SL11H only has some "A" transfer registers from 0x00-0x04
+ *  - SL811HS also has "B" registers from 0x08-0x0c
+ *  - SL811S (or HS in slave mode) has four A+B sets, at 00, 10, 20, 30
+ */
+
+#define SL811_EP_A(base)       ((base) + 0)
+#define SL811_EP_B(base)       ((base) + 8)
+
+#define SL811_HOST_BUF         0x00
+#define SL811_PERIPH_EP0       0x00
+#define SL811_PERIPH_EP1       0x10
+#define SL811_PERIPH_EP2       0x20
+#define SL811_PERIPH_EP3       0x30
+
+
+/* TRANSFER REGISTERS:  host and peripheral sides are similar
+ * except for the control models (master vs slave).
+ */
+#define SL11H_HOSTCTLREG       0
+#      define SL11H_HCTLMASK_ARM       0x01
+#      define SL11H_HCTLMASK_ENABLE    0x02
+#      define SL11H_HCTLMASK_IN        0x00
+#      define SL11H_HCTLMASK_OUT       0x04
+#      define SL11H_HCTLMASK_ISOCH     0x10
+#      define SL11H_HCTLMASK_AFTERSOF  0x20
+#      define SL11H_HCTLMASK_TOGGLE    0x40
+#      define SL11H_HCTLMASK_PREAMBLE  0x80
+#define SL11H_BUFADDRREG       1
+#define SL11H_BUFLNTHREG       2
+#define SL11H_PKTSTATREG       3       /* read */
+#      define SL11H_STATMASK_ACK       0x01
+#      define SL11H_STATMASK_ERROR     0x02
+#      define SL11H_STATMASK_TMOUT     0x04
+#      define SL11H_STATMASK_SEQ       0x08
+#      define SL11H_STATMASK_SETUP     0x10
+#      define SL11H_STATMASK_OVF       0x20
+#      define SL11H_STATMASK_NAK       0x40
+#      define SL11H_STATMASK_STALL     0x80
+#define SL11H_PIDEPREG         3       /* write */
+#      define  SL_SETUP        0xd0
+#      define  SL_IN           0x90
+#      define  SL_OUT          0x10
+#      define  SL_SOF          0x50
+#      define  SL_PREAMBLE     0xc0
+#      define  SL_NAK          0xa0
+#      define  SL_STALL        0xe0
+#      define  SL_DATA0        0x30
+#      define  SL_DATA1        0xb0
+#define SL11H_XFERCNTREG       4       /* read */
+#define SL11H_DEVADDRREG       4       /* write */
+
+
+/* CONTROL REGISTERS:  host and peripheral are very different.
+ */
+#define SL11H_CTLREG1          5
+#      define SL11H_CTL1MASK_SOF_ENA   0x01
+#      define SL11H_CTL1MASK_FORCE     0x18
+#              define SL11H_CTL1MASK_NORMAL    0x00
+#              define SL11H_CTL1MASK_SE0       0x08    /* reset */
+#              define SL11H_CTL1MASK_J         0x10
+#              define SL11H_CTL1MASK_K         0x18    /* resume */
+#      define SL11H_CTL1MASK_LSPD      0x20
+#      define SL11H_CTL1MASK_SUSPEND   0x40
+#define SL11H_IRQ_ENABLE       6
+#      define SL11H_INTMASK_DONE_A     0x01
+#      define SL11H_INTMASK_DONE_B     0x02
+#      define SL11H_INTMASK_SOFINTR    0x10
+#      define SL11H_INTMASK_INSRMV     0x20    /* to/from SE0 */
+#      define SL11H_INTMASK_RD         0x40
+#      define SL11H_INTMASK_DP         0x80    /* only in INTSTATREG */
+#define SL11S_ADDRESS          7
+
+/* 0x08-0x0c are for the B buffer (not in SL11) */
+
+#define SL11H_IRQ_STATUS       0x0D    /* write to ack */
+#define SL11H_HWREVREG         0x0E    /* read */
+#      define SL11H_HWRMASK_HWREV      0xF0
+#define SL11H_SOFLOWREG                0x0E    /* write */
+#define SL11H_SOFTMRREG                0x0F    /* read */
+
+/* a write to this register enables SL811HS features.
+ * HOST flag presumably overrides the chip input signal?
+ */
+#define SL811HS_CTLREG2                0x0F
+#      define SL811HS_CTL2MASK_SOF_MASK        0x3F
+#      define SL811HS_CTL2MASK_DSWAP           0x40
+#      define SL811HS_CTL2MASK_HOST            0x80
+
+#define SL811HS_CTL2_INIT      (SL811HS_CTL2MASK_HOST | 0x2e)
+
+
+/* DATA BUFFERS: registers from 0x10..0xff are for data buffers;
+ * that's 240 bytes, which we'll split evenly between A and B sides.
+ * Only ISO can use more than 64 bytes per packet.
+ * (The SL11S has 0x40..0xff for buffers.)
+ */
+#define H_MAXPACKET    120             /* bytes in A or B fifos */
+
+#define SL11H_DATA_START       0x10
+#define        SL811HS_PACKET_BUF(is_a)        ((is_a) \
+               ? SL11H_DATA_START \
+               : (SL11H_DATA_START + H_MAXPACKET))
+
+/*-------------------------------------------------------------------------*/
+
+#define        LOG2_PERIODIC_SIZE      5       /* arbitrary; this matches OHCI */
+#define        PERIODIC_SIZE           (1 << LOG2_PERIODIC_SIZE)
+
+struct sl811 {
+       spinlock_t              lock;
+       void __iomem            *addr_reg;
+       void __iomem            *data_reg;
+       struct sl811_platform_data      *board;
+       struct proc_dir_entry   *pde;
+
+       unsigned long           stat_insrmv;
+       unsigned long           stat_wake;
+       unsigned long           stat_sof;
+       unsigned long           stat_a;
+       unsigned long           stat_b;
+       unsigned long           stat_lost;
+       unsigned long           stat_overrun;
+
+       /* sw model */
+       struct timer_list       timer;
+       struct sl811h_ep        *next_periodic;
+       struct sl811h_ep        *next_async;
+
+       struct sl811h_ep        *active_a;
+       unsigned long           jiffies_a;
+       struct sl811h_ep        *active_b;
+       unsigned long           jiffies_b;
+
+       u32                     port1;
+       u8                      ctrl1, ctrl2, irq_enable;
+       u16                     frame;
+
+       /* async schedule: control, bulk */
+       struct list_head        async;
+
+       /* periodic schedule: interrupt, iso */
+       u16                     load[PERIODIC_SIZE];
+       struct sl811h_ep        *periodic[PERIODIC_SIZE];
+       unsigned                periodic_count;
+};
+
+static inline struct sl811 *hcd_to_sl811(struct usb_hcd *hcd)
+{
+       return (struct sl811 *) (hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *sl811_to_hcd(struct sl811 *sl811)
+{
+       return container_of((void *) sl811, struct usb_hcd, hcd_priv);
+}
+
+struct sl811h_ep {
+       struct usb_host_endpoint *hep;
+       struct usb_device       *udev;
+
+       u8                      defctrl;
+       u8                      maxpacket;
+       u8                      epnum;
+       u8                      nextpid;
+
+       u16                     error_count;
+       u16                     nak_count;
+       u16                     length;         /* of current packet */
+
+       /* periodic schedule */
+       u16                     period;
+       u16                     branch;
+       u16                     load;
+       struct sl811h_ep        *next;
+
+       /* async schedule */
+       struct list_head        schedule;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* These register utilities should work for the SL811S register API too
+ * NOTE:  caller must hold sl811->lock.
+ */
+
+static inline u8 sl811_read(struct sl811 *sl811, int reg)
+{
+       writeb(reg, sl811->addr_reg);
+       return readb(sl811->data_reg);
+}
+
+static inline void sl811_write(struct sl811 *sl811, int reg, u8 val)
+{
+       writeb(reg, sl811->addr_reg);
+       writeb(val, sl811->data_reg);
+}
+
+static inline void
+sl811_write_buf(struct sl811 *sl811, int addr, const void *buf, size_t count)
+{
+       const u8        *data;
+       void __iomem    *data_reg;
+
+       if (!count)
+               return;
+       writeb(addr, sl811->addr_reg);
+
+       data = buf;
+       data_reg = sl811->data_reg;
+       do {
+               writeb(*data++, data_reg);
+       } while (--count);
+}
+
+static inline void
+sl811_read_buf(struct sl811 *sl811, int addr, void *buf, size_t count)
+{
+       u8              *data;
+       void __iomem    *data_reg;
+
+       if (!count)
+               return;
+       writeb(addr, sl811->addr_reg);
+
+       data = buf;
+       data_reg = sl811->data_reg;
+       do {
+               *data++ = readb(data_reg);
+       } while (--count);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+#define DBG(stuff...)          printk(KERN_DEBUG "sl811: " stuff)
+#else
+#define DBG(stuff...)          do{}while(0)
+#endif
+
+#ifdef VERBOSE
+#    define VDBG               DBG
+#else
+#    define VDBG(stuff...)     do{}while(0)
+#endif
+
+#ifdef PACKET_TRACE
+#    define PACKET             VDBG
+#else
+#    define PACKET(stuff...)   do{}while(0)
+#endif
+
+#define ERR(stuff...)          printk(KERN_ERR "sl811: " stuff)
+#define WARN(stuff...)         printk(KERN_WARNING "sl811: " stuff)
+#define INFO(stuff...)         printk(KERN_INFO "sl811: " stuff)
+
diff --git a/packages/cfu1/files/sl811_cs.c b/packages/cfu1/files/sl811_cs.c
new file mode 100644 (file)
index 0000000..e223b64
--- /dev/null
@@ -0,0 +1,518 @@
+/*
+  A SL811 CLIENT DRIVER for linux 2.4.x
+  Filename: sl811_cs.c
+  Version:  0.0.2
+  Author:   Yukio Yamamoto
+
+  Port to sl811-hcd and 2.6.x by
+    Botond Botyanszki <boti()rocketmail.com>
+    Simon Pickering
+
+  Last update: 2005-05-05
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/version.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+#include <linux/usb_sl811.h>
+
+MODULE_AUTHOR("Botond Botyanszki");
+MODULE_DESCRIPTION("REX-CFU1 PCMCIA driver for 2.6");
+MODULE_LICENSE("GPL");
+
+
+/*====================================================================*/
+/* MACROS                                                             */
+/*====================================================================*/
+
+#if defined(DEBUG) || defined(CONFIG_USB_DEBUG)
+#define DBG(n, args...) printk(KERN_DEBUG "sl811_cs: " args)
+#else
+#define DBG(n, args...) do{}while(0)
+#endif
+
+#define INFO(args...) printk(KERN_INFO "sl811_cs: " args)
+
+/*static char *version = "sl811_cs.c 0.04 2005/04/27 00:00:00 (OpenZaurus Team)";*/
+
+#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
+
+#define CS_CHECK(fn, ret) \
+       do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+/*====================================================================*/
+/* VARIABLES                                                          */
+/*====================================================================*/
+
+static dev_info_t dev_info  = "sl811_cs";
+static dev_link_t *dev_list = NULL;
+
+static ioaddr_t base_addr   = 0x00000000;
+static int irq              = -1;
+
+static int irq_list[4] = { -1 };
+MODULE_PARM(irq_list, "1-4i");
+INT_MODULE_PARM(free_ports, 0);
+INT_MODULE_PARM(irq_mask, 0xdeb8);
+
+/*====================================================================*/
+/* PROTO TYPES                                                        */
+/*====================================================================*/
+
+static dev_link_t* sl811_cs_attach(void);
+static void        sl811_cs_detach(dev_link_t *);
+static void        sl811_cs_config(dev_link_t *link);
+static void        sl811_cs_release(dev_link_t *arg);
+static int         sl811_cs_event(event_t event, int priority,
+                                 event_callback_args_t *args);
+
+/*====================================================================*/
+/* PROTO TYPES                                                        */
+/*====================================================================*/
+
+typedef struct local_info_t {
+  dev_link_t           link;
+  dev_node_t           node;
+} local_info_t;
+
+static struct pcmcia_driver sl811_driver = {
+       .owner          = THIS_MODULE,
+       .drv            = {
+               .name   = "sl811_cs",
+       },
+       .attach         = sl811_cs_attach,
+       .detach         = sl811_cs_detach,
+};
+extern struct device_driver sl811h_driver;
+
+static struct sl811_platform_data platform_data;
+static struct res {
+       struct resource irq_res;
+       struct resource addr_res;
+       struct resource data_res;
+} resources;
+static struct platform_device platform_dev = {
+       .name              = "sl811_cs",
+       .id                = 0,
+       .num_resources     = 0,
+       .dev.dma_mask      = 0,
+       .dev.platform_data = &platform_data,
+       .dev.bus_id        = "sl811-hcd",
+       .dev.driver        = &sl811h_driver
+};
+
+/*====================================================================*/
+/* EXTERNAL FUNCTIONS                                                 */
+/*====================================================================*/
+int sl811h_probe(void *dev);
+
+/*====================================================================*/
+
+
+/*====================================================================*/
+static void release_platform_dev(struct device * dev) {
+       DBG(0, "sl811_cs platform_dev release\n");
+}
+
+/*====================================================================*/
+static dev_link_t *sl811_cs_attach(void)
+{
+  local_info_t *local;
+  dev_link_t *link;
+  client_reg_t client_reg;
+  int ret, i;
+  
+  local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
+  if (!local) return NULL;
+  memset(local, 0, sizeof(local_info_t));
+  link = &local->link; link->priv = local;
+
+  /* Initialize */
+  link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+  link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
+  if (irq_list[0] == -1)
+    link->irq.IRQInfo2 = irq_mask;
+  else
+    for (i = 0; i < 4; i++)
+      link->irq.IRQInfo2 |= 1 << irq_list[i];
+  link->irq.Handler = NULL;
+    
+  link->conf.Attributes = 0;
+  link->conf.Vcc = 33;
+  link->conf.IntType = INT_MEMORY_AND_IO;
+  
+  /* Register with Card Services */
+  link->next = dev_list;
+  dev_list = link;
+  client_reg.dev_info = &dev_info;
+  client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+  client_reg.EventMask =
+    CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+    CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+    CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+  client_reg.event_handler = &sl811_cs_event;
+  client_reg.Version = 0x0210;
+  client_reg.event_callback_args.client_data = link;
+  ret = pcmcia_register_client(&link->handle, &client_reg);
+  if (ret != CS_SUCCESS) {
+         cs_error(link->handle, RegisterClient, ret);
+         sl811_cs_detach(link);
+         return NULL;
+  }
+  
+  return link;
+} /* sl811_cs_attach */
+
+/*====================================================================*/
+static void sl811_cs_detach(dev_link_t *link)
+{
+  dev_link_t **linkp;
+
+  DBG(0, "sl811_cs_detach(0x%p)\n", link);
+    
+  /* Locate device structure */
+  for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+    if (*linkp == link) break;
+  if (*linkp == NULL)
+    return;
+  
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) 
+  if (link->state & DEV_CONFIG) {
+#ifdef PCMCIA_DEBUG
+    printk(KERN_DEBUG "sl811_cs: detach postponed, '%s' "
+          "still locked\n", link->dev->dev_name);
+#endif
+    link->state |= DEV_STALE_LINK;
+    return;
+  }
+#endif
+
+  /* Break the link with Card Services */
+  if (link->handle)
+    pcmcia_deregister_client(link->handle);
+  
+  /* Unlink device structure, and free it */
+  *linkp = link->next;
+  /* This points to the parent local_info_t struct */
+  kfree(link->priv);
+  
+} /* sl811_cs_detach */
+
+
+/*====================================================================*/
+static int sl811_hc_init(void)
+{
+       /* set up the device structure */
+       resources.irq_res.flags = IORESOURCE_IRQ;
+       resources.irq_res.start = irq;
+
+       resources.addr_res.flags = IORESOURCE_MEM;
+       resources.addr_res.start = base_addr;
+       resources.addr_res.end = base_addr + 1;
+
+       resources.data_res.flags = IORESOURCE_MEM;
+       resources.data_res.start = base_addr + 1;
+       resources.data_res.end   = base_addr + 4;
+
+       platform_dev.dev.release = release_platform_dev;
+       platform_device_register(&platform_dev);
+       /* FIXME: we register the platform device with 0 resources 
+        otherwise the unregister call won't work*/
+
+       platform_dev.num_resources = 3;
+       platform_dev.resource      = (struct resource *) &resources;
+
+       /* try to initialize the host controller */
+       if (sl811h_probe(&platform_dev.dev) != 0) {
+               DBG(0, "sl811h_probe() didn't return 0\n");
+               return 0;
+       }
+       return 1;
+}
+
+
+/*====================================================================*/
+static void sl811_cs_config(dev_link_t *link)
+{
+  client_handle_t handle = link->handle;
+  local_info_t *dev = link->priv;
+  tuple_t tuple;
+  cisparse_t parse;
+  int last_fn, last_ret;
+  u_char buf[64];
+  config_info_t conf;
+  cistpl_cftable_entry_t dflt = { 0 };
+
+  DBG(0, "sl811_cs_config(0x%p)\n", link);
+  
+  tuple.DesiredTuple = CISTPL_CONFIG;
+  tuple.Attributes = 0;
+  tuple.TupleData = buf;
+  tuple.TupleDataMax = sizeof(buf);
+  tuple.TupleOffset = 0;
+  CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+  CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+  CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+  link->conf.ConfigBase = parse.config.base;
+  link->conf.Present = parse.config.rmask[0];
+    
+  /* Configure card */
+  link->state |= DEV_CONFIG;
+
+  /* Look up the current Vcc */
+  CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
+  link->conf.Vcc = conf.Vcc;
+  
+  tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+  CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+  while (1) {
+    cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
+       if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+               pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+               goto next_entry;
+
+    if (cfg->flags & CISTPL_CFTABLE_DEFAULT) {
+      dflt = *cfg;
+    }
+
+    if (cfg->index == 0) goto next_entry;    
+
+    link->conf.ConfigIndex = cfg->index;
+    
+    /* Does this card need audio output? */
+    if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+      link->conf.Attributes |= CONF_ENABLE_SPKR;
+      link->conf.Status = CCSR_AUDIO_ENA;
+    }
+    
+    /* Use power settings for Vcc and Vpp if present */
+    /*  Note that the CIS values need to be rescaled */
+    if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) {
+      if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM]/10000) {
+       goto next_entry;
+      }
+    } else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) {
+      if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM]/10000) {
+       goto next_entry;
+      }
+    }
+    
+    if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
+      link->conf.Vpp1 = link->conf.Vpp2 =
+       cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
+    else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
+      link->conf.Vpp1 = link->conf.Vpp2 =
+       dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
+    
+    /* Do we need to allocate an interrupt? */
+    if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
+      link->conf.Attributes |= CONF_ENABLE_IRQ;
+    
+    /* IO window settings */
+    link->io.NumPorts1 = link->io.NumPorts2 = 0;
+    if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
+      cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
+
+      link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+      link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+      link->io.BasePort1 = io->win[0].base;
+      link->io.NumPorts1 = io->win[0].len;
+
+      if (pcmcia_request_io(link->handle, &link->io) != 0)
+             goto next_entry;
+    }
+    break;
+    
+  next_entry:
+    if (link->io.NumPorts1)
+      pcmcia_release_io(link->handle, &link->io);
+         last_ret = pcmcia_get_next_tuple(handle, &tuple);
+  }
+  
+  if (link->conf.Attributes & CONF_ENABLE_IRQ)
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+  
+  CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+  
+  if (free_ports) {
+    if (link->io.BasePort1)
+      release_region(link->io.BasePort1, link->io.NumPorts1);
+  }
+
+  sprintf(dev->node.dev_name, "cf_usb0");
+  dev->node.major = dev->node.minor = 0;
+  link->dev = &dev->node;
+  
+  printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
+        dev->node.dev_name, link->conf.ConfigIndex,
+        link->conf.Vcc/10, link->conf.Vcc%10);
+  if (link->conf.Vpp1)
+    printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
+  if (link->conf.Attributes & CONF_ENABLE_IRQ) {
+    printk(", irq %d", link->irq.AssignedIRQ);
+    irq = link->irq.AssignedIRQ;
+  }
+
+  if (link->io.NumPorts1) {
+    printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+          link->io.BasePort1+link->io.NumPorts1-1);
+    base_addr = link->io.BasePort1;
+  }
+
+  printk("\n");
+  
+  link->state &= ~DEV_CONFIG_PENDING;
+
+  /* Release resources claimed by PCMCIA for the sl811h driver */
+  release_region(link->io.BasePort1, link->io.NumPorts1);
+  
+  if (sl811_hc_init() == 0) goto cs_failed;
+
+  return;
+  
+ cs_failed:
+  printk("sl811_cs_config failed\n");
+  cs_error(link->handle, last_fn, last_ret);
+  sl811_cs_release(link);
+  link->state &= ~DEV_CONFIG_PENDING;
+
+} /* sl811_cs_config */
+
+/*====================================================================*/
+static void sl811_cs_release(dev_link_t * link)
+{
+
+  DBG(0, "sl811_cs_release(0x%p)\n", link);
+  
+  if (link->open) {
+    DBG(1, "sl811_cs: release postponed, '%s' still open\n",
+         link->dev->dev_name);
+    link->state |= DEV_STALE_CONFIG;
+    return;
+  }
+
+  /* request IO, because PCMCIA thinks it has claimed it */
+  request_region(link->io.BasePort1, link->io.NumPorts1, "sl811_cs");
+
+  /* Unlink the device chain */
+  link->dev = NULL;
+  
+  pcmcia_release_configuration(link->handle);
+  if (link->io.NumPorts1)
+    pcmcia_release_io(link->handle, &link->io);
+  if (link->irq.AssignedIRQ)
+    pcmcia_release_irq(link->handle, &link->irq);
+  link->state &= ~DEV_CONFIG;
+  
+  if (link->state & DEV_STALE_LINK)
+    sl811_cs_detach(link);
+
+  /* FIXME: if the unregister call frees up the resources, it oopses
+   so we pretend to have 0 resources */
+  platform_dev.num_resources = 0;
+  platform_dev.resource      = NULL;
+
+  platform_device_unregister(&platform_dev);
+
+} /* sl811_cs_release */
+
+/*====================================================================*/
+static int sl811_cs_event(event_t event, int priority, event_callback_args_t *args)
+{
+  dev_link_t *link = args->client_data;
+    
+  DBG(1, "sl811_cs_event(0x%06x)\n", event);
+    
+  switch (event) {
+  case CS_EVENT_CARD_REMOVAL:
+    link->state &= ~DEV_PRESENT;
+    if (link->state & DEV_CONFIG) {
+      sl811_cs_release(link);
+    }
+    break;
+
+  case CS_EVENT_CARD_INSERTION:
+    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+    sl811_cs_config(link);
+    break;
+
+  case CS_EVENT_PM_SUSPEND:
+    link->state |= DEV_SUSPEND;
+    /* Fall through... */
+  case CS_EVENT_RESET_PHYSICAL:
+    if (link->state & DEV_CONFIG)
+      pcmcia_release_configuration(link->handle);
+    break;
+  case CS_EVENT_PM_RESUME:
+    link->state &= ~DEV_SUSPEND;
+    /* Fall through... */
+  case CS_EVENT_CARD_RESET:
+    if (link->state & DEV_CONFIG)
+      pcmcia_request_configuration(link->handle, &link->conf);
+
+    INFO("FIXME: card reset\n");
+
+    break;
+  }
+  return 0;
+} /* sl811_cs_event */
+
+/*====================================================================*/
+static int __init init_sl811_cs(void)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+  servinfo_t serv;
+  DBG(0, "%s\n", version);
+  CardServices(GetCardServicesInfo, &serv);
+  if (serv.Revision != CS_RELEASE_CODE) {
+    printk(KERN_NOTICE "sl811_cs: Card Services release "
+          "does not match!\n");
+    return -EINVAL;
+  }
+  register_pccard_driver(&dev_info, &sl811_cs_attach, &sl811_cs_detach);
+  return 0;
+#else
+  return pcmcia_register_driver(&sl811_driver);
+#endif
+
+}
+
+/*====================================================================*/
+static void __exit exit_sl811_cs(void)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) 
+  DBG(0, "sl811_cs: unloading\n");
+  unregister_pccard_driver(&dev_info);
+  while (dev_list != NULL) {
+    del_timer(&dev_list->release);
+    if (dev_list->state & DEV_CONFIG)
+      sl811_cs_release(dev_list);
+    sl811_cs_detach(dev_list);
+  }
+#else
+  pcmcia_unregister_driver(&sl811_driver);
+#endif
+
+}
+
+/*====================================================================*/
+
+module_init(init_sl811_cs);
+module_exit(exit_sl811_cs);
diff --git a/packages/cfu1/files/sl811_hcd.c b/packages/cfu1/files/sl811_hcd.c
new file mode 100644 (file)
index 0000000..90e5f86
--- /dev/null
@@ -0,0 +1,1932 @@
+/*
+ * SL811HS HCD (Host Controller Driver) for USB.
+ *
+ * Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
+ * Copyright (C) 2004 David Brownell
+ * 
+ * Periodic scheduling is based on Roman's OHCI code
+ *     Copyright (C) 1999 Roman Weissgaerber
+ *
+ * The SL811HS controller handles host side USB (like the SL11H, but with
+ * another register set and SOF generation) as well as peripheral side USB
+ * (like the SL811S).  This driver version doesn't implement the Gadget API
+ * for the peripheral role; or OTG (that'd need much external circuitry).
+ *
+ * For documentation, see the SL811HS spec and the "SL811HS Embedded Host"
+ * document (providing significant pieces missing from that spec); plus
+ * the SL811S spec if you want peripheral side info.
+ */ 
+
+/*
+ * Status:  Passed basic stress testing, works with hubs, mice, keyboards,
+ * and usb-storage.
+ *
+ * TODO:
+ * - usb suspend/resume triggered by sl811 (with USB_SUSPEND)
+ * - various issues noted in the code
+ * - performance work; use both register banks; ...
+ * - use urb->iso_frame_desc[] with ISO transfers
+ */
+
+#undef VERBOSE
+#undef PACKET_TRACE
+
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_DEBUG
+#      define DEBUG
+#else
+#      undef DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/usb_sl811.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+
+#include "hcd.h"
+#include "sl811.h"
+
+
+MODULE_DESCRIPTION("SL811HS USB Host Controller Driver");
+MODULE_LICENSE("GPL");
+
+#define DRIVER_VERSION "15 Dec 2004"
+
+
+#ifndef DEBUG
+#      define  STUB_DEBUG_FILE
+#endif
+
+/* for now, use only one transfer register bank */
+#undef USE_B
+
+/* this doesn't understand urb->iso_frame_desc[], but if you had a driver
+ * that just queued one ISO frame per URB then iso transfers "should" work
+ * using the normal urb status fields.
+ */
+#define        DISABLE_ISO
+
+#define        QUIRK2
+//#define      QUIRK3
+
+static const char hcd_name[] = "sl811-hcd";
+
+/*-------------------------------------------------------------------------*/
+
+
+static void port_power(struct sl811 *sl811, int is_on)
+{
+       struct usb_hcd  *hcd = sl811_to_hcd(sl811);
+
+       /* hub is inactive unless the port is powered */
+       if (is_on) {
+               if (sl811->port1 & (1 << USB_PORT_FEAT_POWER))
+                       return;
+
+               sl811->port1 = (1 << USB_PORT_FEAT_POWER);
+               sl811->irq_enable = SL11H_INTMASK_INSRMV;
+               hcd->self.controller->power.power_state = PMSG_ON;
+       } else {
+               sl811->port1 = 0;
+               sl811->irq_enable = 0;
+               hcd->state = HC_STATE_HALT;
+               hcd->self.controller->power.power_state = PMSG_SUSPEND;
+       }
+       sl811->ctrl1 = 0;
+       sl811_write(sl811, SL11H_IRQ_ENABLE, 0);
+       sl811_write(sl811, SL11H_IRQ_STATUS, ~0);
+
+       if (sl811->board && sl811->board->port_power) {
+               /* switch VBUS, at 500mA unless hub power budget gets set */
+               DBG("power %s\n", is_on ? "on" : "off");
+               sl811->board->port_power(hcd->self.controller, is_on);
+       }
+
+       /* reset as thoroughly as we can */
+       if (sl811->board && sl811->board->reset)
+               sl811->board->reset(hcd->self.controller);
+
+       sl811_write(sl811, SL11H_IRQ_ENABLE, 0);
+       sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+       sl811_write(sl811, SL811HS_CTLREG2, SL811HS_CTL2_INIT);
+       sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+
+       // if !is_on, put into lowpower mode now
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* This is a PIO-only HCD.  Queueing appends URBs to the endpoint's queue,
+ * and may start I/O.  Endpoint queues are scanned during completion irq
+ * handlers (one per packet: ACK, NAK, faults, etc) and urb cancelation.
+ *
+ * Using an external DMA engine to copy a packet at a time could work,
+ * though setup/teardown costs may be too big to make it worthwhile.
+ */
+
+/* SETUP starts a new control request.  Devices are not allowed to
+ * STALL or NAK these; they must cancel any pending control requests.
+ */
+static void setup_packet(
+       struct sl811            *sl811,
+       struct sl811h_ep        *ep,
+       struct urb              *urb,
+       u8                      bank,
+       u8                      control
+)
+{
+       u8                      addr;
+       u8                      len;
+       void __iomem            *data_reg;
+
+       addr = SL811HS_PACKET_BUF(bank == 0);
+       len = sizeof(struct usb_ctrlrequest);
+       data_reg = sl811->data_reg;
+       sl811_write_buf(sl811, addr, urb->setup_packet, len);
+
+       /* autoincrementing */
+       sl811_write(sl811, bank + SL11H_BUFADDRREG, addr);
+       writeb(len, data_reg);
+       writeb(SL_SETUP /* | ep->epnum */, data_reg);
+       writeb(usb_pipedevice(urb->pipe), data_reg);
+
+       /* always OUT/data0 */ ;
+       sl811_write(sl811, bank + SL11H_HOSTCTLREG,
+                       control | SL11H_HCTLMASK_OUT);
+       ep->length = 0;
+       PACKET("SETUP qh%p\n", ep);
+}
+
+/* STATUS finishes control requests, often after IN or OUT data packets */
+static void status_packet(
+       struct sl811            *sl811,
+       struct sl811h_ep        *ep,
+       struct urb              *urb,
+       u8                      bank,
+       u8                      control
+)
+{
+       int                     do_out;
+       void __iomem            *data_reg;
+
+       do_out = urb->transfer_buffer_length && usb_pipein(urb->pipe);
+       data_reg = sl811->data_reg;
+
+       /* autoincrementing */
+       sl811_write(sl811, bank + SL11H_BUFADDRREG, 0);
+       writeb(0, data_reg);
+       writeb((do_out ? SL_OUT : SL_IN) /* | ep->epnum */, data_reg);
+       writeb(usb_pipedevice(urb->pipe), data_reg);
+
+       /* always data1; sometimes IN */
+       control |= SL11H_HCTLMASK_TOGGLE;
+       if (do_out)
+               control |= SL11H_HCTLMASK_OUT;
+       sl811_write(sl811, bank + SL11H_HOSTCTLREG, control);
+       ep->length = 0;
+       PACKET("STATUS%s/%s qh%p\n", ep->nak_count ? "/retry" : "",
+                       do_out ? "out" : "in", ep);
+}
+
+/* IN packets can be used with any type of endpoint. here we just
+ * start the transfer, data from the peripheral may arrive later.
+ * urb->iso_frame_desc is currently ignored here...
+ */
+static void in_packet(
+       struct sl811            *sl811,
+       struct sl811h_ep        *ep,
+       struct urb              *urb,
+       u8                      bank,
+       u8                      control
+)
+{
+       u8                      addr;
+       u8                      len;
+       void __iomem            *data_reg;
+
+       /* avoid losing data on overflow */
+       len = ep->maxpacket;
+       addr = SL811HS_PACKET_BUF(bank == 0);
+       if (!(control & SL11H_HCTLMASK_ISOCH)
+                       && usb_gettoggle(urb->dev, ep->epnum, 0))
+               control |= SL11H_HCTLMASK_TOGGLE;
+       data_reg = sl811->data_reg;
+
+       /* autoincrementing */
+       sl811_write(sl811, bank + SL11H_BUFADDRREG, addr);
+       writeb(len, data_reg);
+       writeb(SL_IN | ep->epnum, data_reg);
+       writeb(usb_pipedevice(urb->pipe), data_reg);
+
+       sl811_write(sl811, bank + SL11H_HOSTCTLREG, control);
+       ep->length = min((int)len,
+                       urb->transfer_buffer_length - urb->actual_length);
+       PACKET("IN%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "",
+                       !!usb_gettoggle(urb->dev, ep->epnum, 0), ep, len);
+}
+
+/* OUT packets can be used with any type of endpoint.
+ * urb->iso_frame_desc is currently ignored here...
+ */
+static void out_packet(
+       struct sl811            *sl811,
+       struct sl811h_ep        *ep,
+       struct urb              *urb,
+       u8                      bank,
+       u8                      control
+)
+{
+       void                    *buf;
+       u8                      addr;
+       u8                      len;
+       void __iomem            *data_reg;
+
+       buf = urb->transfer_buffer + urb->actual_length;
+       prefetch(buf);
+
+       len = min((int)ep->maxpacket,
+                       urb->transfer_buffer_length - urb->actual_length);
+
+       if (!(control & SL11H_HCTLMASK_ISOCH)
+                       && usb_gettoggle(urb->dev, ep->epnum, 1))
+               control |= SL11H_HCTLMASK_TOGGLE;
+       addr = SL811HS_PACKET_BUF(bank == 0);
+       data_reg = sl811->data_reg;
+
+       sl811_write_buf(sl811, addr, buf, len);
+
+       /* autoincrementing */
+       sl811_write(sl811, bank + SL11H_BUFADDRREG, addr);
+       writeb(len, data_reg);
+       writeb(SL_OUT | ep->epnum, data_reg);
+       writeb(usb_pipedevice(urb->pipe), data_reg);
+
+       sl811_write(sl811, bank + SL11H_HOSTCTLREG,
+                       control | SL11H_HCTLMASK_OUT);
+       ep->length = len;
+       PACKET("OUT%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "",
+                       !!usb_gettoggle(urb->dev, ep->epnum, 1), ep, len);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* caller updates on-chip enables later */
+
+static inline void sofirq_on(struct sl811 *sl811)
+{
+       if (sl811->irq_enable & SL11H_INTMASK_SOFINTR)
+               return;
+       VDBG("sof irq on\n");
+       sl811->irq_enable |= SL11H_INTMASK_SOFINTR;
+}
+
+static inline void sofirq_off(struct sl811 *sl811)
+{
+       if (!(sl811->irq_enable & SL11H_INTMASK_SOFINTR))
+               return;
+       VDBG("sof irq off\n");
+       sl811->irq_enable &= ~SL11H_INTMASK_SOFINTR;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* pick the next endpoint for a transaction, and issue it.
+ * frames start with periodic transfers (after whatever is pending
+ * from the previous frame), and the rest of the time is async
+ * transfers, scheduled round-robin.
+ */
+static struct sl811h_ep        *start(struct sl811 *sl811, u8 bank)
+{
+       struct sl811h_ep        *ep;
+       struct urb              *urb;
+       int                     fclock;
+       u8                      control;
+
+       /* use endpoint at schedule head */
+       if (sl811->next_periodic) {
+               ep = sl811->next_periodic;
+               sl811->next_periodic = ep->next;
+       } else {
+               if (sl811->next_async)
+                       ep = sl811->next_async;
+               else if (!list_empty(&sl811->async))
+                       ep = container_of(sl811->async.next,
+                                       struct sl811h_ep, schedule);
+               else {
+                       /* could set up the first fullspeed periodic
+                        * transfer for the next frame ...
+                        */
+                       return NULL;
+               }
+
+#ifdef USE_B
+               if ((bank && sl811->active_b == ep) || sl811->active_a == ep)
+                       return NULL;
+#endif
+
+               if (ep->schedule.next == &sl811->async)
+                       sl811->next_async = NULL;
+               else
+                       sl811->next_async = container_of(ep->schedule.next,
+                                       struct sl811h_ep, schedule);
+       }
+
+       if (unlikely(list_empty(&ep->hep->urb_list))) {
+               DBG("empty %p queue?\n", ep);
+               return NULL;
+       }
+
+       urb = container_of(ep->hep->urb_list.next, struct urb, urb_list);
+       control = ep->defctrl;
+
+       /* if this frame doesn't have enough time left to transfer this
+        * packet, wait till the next frame.  too-simple algorithm...
+        */
+       fclock = sl811_read(sl811, SL11H_SOFTMRREG) << 6;
+       fclock -= 100;          /* setup takes not much time */
+       if (urb->dev->speed == USB_SPEED_LOW) {
+               if (control & SL11H_HCTLMASK_PREAMBLE) {
+                       /* also note erratum 1: some hubs won't work */
+                       fclock -= 800;
+               }
+               fclock -= ep->maxpacket << 8;
+
+               /* erratum 2: AFTERSOF only works for fullspeed */
+               if (fclock < 0) {
+                       if (ep->period)
+                               sl811->stat_overrun++;
+                       sofirq_on(sl811);
+                       return NULL;
+               }
+       } else {
+               fclock -= 12000 / 19;   /* 19 64byte packets/msec */
+               if (fclock < 0) {
+                       if (ep->period)
+                               sl811->stat_overrun++;
+                       control |= SL11H_HCTLMASK_AFTERSOF;
+
+               /* throttle bulk/control irq noise */
+               } else if (ep->nak_count)
+                       control |= SL11H_HCTLMASK_AFTERSOF;
+       }
+
+
+       switch (ep->nextpid) {
+       case USB_PID_IN:
+               in_packet(sl811, ep, urb, bank, control);
+               break;
+       case USB_PID_OUT:
+               out_packet(sl811, ep, urb, bank, control);
+               break;
+       case USB_PID_SETUP:
+               setup_packet(sl811, ep, urb, bank, control);
+               break;
+       case USB_PID_ACK:               /* for control status */
+               status_packet(sl811, ep, urb, bank, control);
+               break;
+       default:
+               DBG("bad ep%p pid %02x\n", ep, ep->nextpid);
+               ep = NULL;
+       }
+       return ep;
+}
+
+#define MIN_JIFFIES    ((msecs_to_jiffies(2) > 1) ? msecs_to_jiffies(2) : 2)
+
+static inline void start_transfer(struct sl811 *sl811)
+{
+       if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND))
+               return;
+       if (sl811->active_a == NULL) {
+               sl811->active_a = start(sl811, SL811_EP_A(SL811_HOST_BUF));
+               if (sl811->active_a != NULL)
+                       sl811->jiffies_a = jiffies + MIN_JIFFIES;
+       }
+#ifdef USE_B
+       if (sl811->active_b == NULL) {
+               sl811->active_b = start(sl811, SL811_EP_B(SL811_HOST_BUF));
+               if (sl811->active_b != NULL)
+                       sl811->jiffies_b = jiffies + MIN_JIFFIES;
+       }
+#endif
+}
+
+static void finish_request(
+       struct sl811            *sl811,
+       struct sl811h_ep        *ep,
+       struct urb              *urb,
+       struct pt_regs          *regs,
+       int                     status
+) __releases(sl811->lock) __acquires(sl811->lock)
+{
+       unsigned                i;
+
+       if (usb_pipecontrol(urb->pipe))
+               ep->nextpid = USB_PID_SETUP;
+
+       spin_lock(&urb->lock);
+       if (urb->status == -EINPROGRESS)
+               urb->status = status;
+       spin_unlock(&urb->lock);
+
+       spin_unlock(&sl811->lock);
+       usb_hcd_giveback_urb(sl811_to_hcd(sl811), urb, regs);
+       spin_lock(&sl811->lock);
+
+       /* leave active endpoints in the schedule */
+       if (!list_empty(&ep->hep->urb_list))
+               return;
+
+       /* async deschedule? */
+       if (!list_empty(&ep->schedule)) {
+               list_del_init(&ep->schedule);
+               if (ep == sl811->next_async)
+                       sl811->next_async = NULL;
+               return;
+       }
+
+       /* periodic deschedule */
+       DBG("deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch);
+       for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) {
+               struct sl811h_ep        *temp;
+               struct sl811h_ep        **prev = &sl811->periodic[i];
+
+               while (*prev && ((temp = *prev) != ep))
+                       prev = &temp->next;
+               if (*prev)
+                       *prev = ep->next;
+               sl811->load[i] -= ep->load;
+       }       
+       ep->branch = PERIODIC_SIZE;
+       sl811->periodic_count--;
+       sl811_to_hcd(sl811)->self.bandwidth_allocated
+               -= ep->load / ep->period;
+       if (ep == sl811->next_periodic)
+               sl811->next_periodic = ep->next;
+
+       /* we might turn SOFs back on again for the async schedule */
+       if (sl811->periodic_count == 0)
+               sofirq_off(sl811);
+}
+
+static void
+done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank, struct pt_regs *regs)
+{
+       u8                      status;
+       struct urb              *urb;
+       int                     urbstat = -EINPROGRESS;
+
+       if (unlikely(!ep))
+               return;
+
+       status = sl811_read(sl811, bank + SL11H_PKTSTATREG);
+
+       urb = container_of(ep->hep->urb_list.next, struct urb, urb_list);
+
+       /* we can safely ignore NAKs */
+       if (status & SL11H_STATMASK_NAK) {
+               // PACKET("...NAK_%02x qh%p\n", bank, ep);
+               if (!ep->period)
+                       ep->nak_count++;
+               ep->error_count = 0;
+
+       /* ACK advances transfer, toggle, and maybe queue */
+       } else if (status & SL11H_STATMASK_ACK) {
+               struct usb_device       *udev = urb->dev;
+               int                     len;
+               unsigned char           *buf;
+
+               /* urb->iso_frame_desc is currently ignored here... */
+
+               ep->nak_count = ep->error_count = 0;
+               switch (ep->nextpid) {
+               case USB_PID_OUT:
+                       // PACKET("...ACK/out_%02x qh%p\n", bank, ep);
+                       urb->actual_length += ep->length;
+                       usb_dotoggle(udev, ep->epnum, 1);
+                       if (urb->actual_length
+                                       == urb->transfer_buffer_length) {
+                               if (usb_pipecontrol(urb->pipe))
+                                       ep->nextpid = USB_PID_ACK;
+
+                               /* some bulk protocols terminate OUT transfers
+                                * by a short packet, using ZLPs not padding.
+                                */
+                               else if (ep->length < ep->maxpacket
+                                               || !(urb->transfer_flags
+                                                       & URB_ZERO_PACKET))
+                                       urbstat = 0;
+                       }
+                       break;
+               case USB_PID_IN:
+                       // PACKET("...ACK/in_%02x qh%p\n", bank, ep);
+                       buf = urb->transfer_buffer + urb->actual_length;
+                       prefetchw(buf);
+                       len = ep->maxpacket - sl811_read(sl811,
+                                               bank + SL11H_XFERCNTREG);
+                       if (len > ep->length) {
+                               len = ep->length;
+                               urb->status = -EOVERFLOW;
+                       }
+                       urb->actual_length += len;
+                       sl811_read_buf(sl811, SL811HS_PACKET_BUF(bank == 0),
+                                       buf, len);
+                       usb_dotoggle(udev, ep->epnum, 0);
+                       if (urb->actual_length == urb->transfer_buffer_length)
+                               urbstat = 0;
+                       else if (len < ep->maxpacket) {
+                               if (urb->transfer_flags & URB_SHORT_NOT_OK)
+                                       urbstat = -EREMOTEIO;
+                               else
+                                       urbstat = 0;
+                       }
+                       if (usb_pipecontrol(urb->pipe)
+                                       && (urbstat == -EREMOTEIO
+                                               || urbstat == 0)) {
+
+                               /* NOTE if the status stage STALLs (why?),
+                                * this reports the wrong urb status.
+                                */
+                               spin_lock(&urb->lock);
+                               if (urb->status == -EINPROGRESS)
+                                       urb->status = urbstat;
+                               spin_unlock(&urb->lock);
+
+                               urb = NULL;
+                               ep->nextpid = USB_PID_ACK;
+                       }
+                       break;
+               case USB_PID_SETUP:
+                       // PACKET("...ACK/setup_%02x qh%p\n", bank, ep);
+                       if (urb->transfer_buffer_length == urb->actual_length)
+                               ep->nextpid = USB_PID_ACK;
+                       else if (usb_pipeout(urb->pipe)) {
+                               usb_settoggle(udev, 0, 1, 1);
+                               ep->nextpid = USB_PID_OUT;
+                       } else {
+                               usb_settoggle(udev, 0, 0, 1);
+                               ep->nextpid = USB_PID_IN;
+                       }
+                       break;
+               case USB_PID_ACK:
+                       // PACKET("...ACK/status_%02x qh%p\n", bank, ep);
+                       urbstat = 0;
+                       break;
+               }
+
+       /* STALL stops all transfers */
+       } else if (status & SL11H_STATMASK_STALL) {
+               PACKET("...STALL_%02x qh%p\n", bank, ep);
+               ep->nak_count = ep->error_count = 0;
+               urbstat = -EPIPE;
+
+       /* error? retry, until "3 strikes" */
+       } else if (++ep->error_count >= 3) {
+               if (status & SL11H_STATMASK_TMOUT)
+                       urbstat = -ETIMEDOUT;
+               else if (status & SL11H_STATMASK_OVF)
+                       urbstat = -EOVERFLOW;
+               else
+                       urbstat = -EPROTO;
+               ep->error_count = 0;
+               PACKET("...3STRIKES_%02x %02x qh%p stat %d\n",
+                               bank, status, ep, urbstat);
+       }
+
+       if (urb && (urbstat != -EINPROGRESS || urb->status != -EINPROGRESS))
+               finish_request(sl811, ep, urb, regs, urbstat);
+}
+
+static inline u8 checkdone(struct sl811 *sl811)
+{
+       u8      ctl;
+       u8      irqstat = 0;
+
+       if (sl811->active_a && time_before_eq(sl811->jiffies_a, jiffies)) {
+               ctl = sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG));
+               if (ctl & SL11H_HCTLMASK_ARM)
+                       sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0);
+               DBG("%s DONE_A: ctrl %02x sts %02x\n",
+                       (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost",
+                       ctl,
+                       sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG)));
+               irqstat |= SL11H_INTMASK_DONE_A;
+       }
+#ifdef USE_B
+       if (sl811->active_b && time_before_eq(sl811->jiffies_b, jiffies)) {
+               ctl = sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG));
+               if (ctl & SL11H_HCTLMASK_ARM)
+                       sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0);
+               DBG("%s DONE_B: ctrl %02x sts %02x\n",
+                       (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost",
+                       ctl,
+                       sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG)));
+               irqstat |= SL11H_INTMASK_DONE_A;
+       }
+#endif
+       return irqstat;
+}
+
+static irqreturn_t sl811h_irq(struct usb_hcd *hcd, struct pt_regs *regs)
+{
+       struct sl811    *sl811 = hcd_to_sl811(hcd);
+       u8              irqstat;
+       irqreturn_t     ret = IRQ_NONE;
+       unsigned        retries = 5;
+
+       spin_lock(&sl811->lock);
+
+retry:
+       irqstat = sl811_read(sl811, SL11H_IRQ_STATUS) & ~SL11H_INTMASK_DP;
+       if (irqstat) {
+               sl811_write(sl811, SL11H_IRQ_STATUS, irqstat);
+               irqstat &= sl811->irq_enable;
+       }
+
+#ifdef QUIRK2
+       /* this may no longer be necessary ... */
+       if (irqstat == 0 && ret == IRQ_NONE) {
+               irqstat = checkdone(sl811);
+               if (irqstat /* && irq != ~0 */ )
+                       sl811->stat_lost++;
+       }
+#endif
+
+       /* USB packets, not necessarily handled in the order they're
+        * issued ... that's fine if they're different endpoints.
+        */
+       if (irqstat & SL11H_INTMASK_DONE_A) {
+               done(sl811, sl811->active_a, SL811_EP_A(SL811_HOST_BUF), regs);
+               sl811->active_a = NULL;
+               sl811->stat_a++;
+       }
+#ifdef USE_B
+       if (irqstat & SL11H_INTMASK_DONE_B) {
+               done(sl811, sl811->active_b, SL811_EP_B(SL811_HOST_BUF), regs);
+               sl811->active_b = NULL;
+               sl811->stat_b++;
+       }
+#endif
+       if (irqstat & SL11H_INTMASK_SOFINTR) {
+               unsigned index;
+
+               index = sl811->frame++ % (PERIODIC_SIZE - 1);
+               sl811->stat_sof++;
+
+               /* be graceful about almost-inevitable periodic schedule
+                * overruns:  continue the previous frame's transfers iff
+                * this one has nothing scheduled.
+                */
+               if (sl811->next_periodic) {
+                       // ERR("overrun to slot %d\n", index);
+                       sl811->stat_overrun++;
+               }
+               if (sl811->periodic[index])
+                       sl811->next_periodic = sl811->periodic[index];
+       }
+
+       /* khubd manages debouncing and wakeup */
+       if (irqstat & SL11H_INTMASK_INSRMV) {
+               sl811->stat_insrmv++;
+
+               /* most stats are reset for each VBUS session */
+               sl811->stat_wake = 0;
+               sl811->stat_sof = 0;
+               sl811->stat_a = 0;
+               sl811->stat_b = 0;
+               sl811->stat_lost = 0;
+
+               sl811->ctrl1 = 0;
+               sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+
+               sl811->irq_enable = SL11H_INTMASK_INSRMV;
+               sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+
+               /* usbcore nukes other pending transactions on disconnect */
+               if (sl811->active_a) {
+                       sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0);
+                       finish_request(sl811, sl811->active_a,
+                               container_of(sl811->active_a->hep->urb_list.next,
+                                       struct urb, urb_list),
+                               NULL, -ESHUTDOWN);
+                       sl811->active_a = NULL;
+               }
+#ifdef USE_B
+               if (sl811->active_b) {
+                       sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0);
+                       finish_request(sl811, sl811->active_b,
+                               container_of(sl811->active_b->hep->urb_list.next,
+                                       struct urb, urb_list),
+                               NULL, -ESHUTDOWN);
+                       sl811->active_b = NULL;
+               }
+#endif
+
+               /* port status seems wierd until after reset, so
+                * force the reset and make khubd clean up later.
+                */
+               sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION)
+                               | (1 << USB_PORT_FEAT_CONNECTION);
+
+       } else if (irqstat & SL11H_INTMASK_RD) {
+               if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)) {
+                       DBG("wakeup\n");
+                       sl811->port1 |= 1 << USB_PORT_FEAT_C_SUSPEND;
+                       sl811->stat_wake++;
+               } else
+                       irqstat &= ~SL11H_INTMASK_RD;
+       }
+
+       if (irqstat) {
+               if (sl811->port1 & (1 << USB_PORT_FEAT_ENABLE))
+                       start_transfer(sl811);
+               ret = IRQ_HANDLED;
+               if (retries--)
+                       goto retry;
+       }
+
+       if (sl811->periodic_count == 0 && list_empty(&sl811->async)) 
+               sofirq_off(sl811);
+       sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+
+       spin_unlock(&sl811->lock);
+
+       return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* usb 1.1 says max 90% of a frame is available for periodic transfers.
+ * this driver doesn't promise that much since it's got to handle an
+ * IRQ per packet; irq handling latencies also use up that time.
+ */
+#define        MAX_PERIODIC_LOAD       500     /* out of 1000 usec */
+
+static int balance(struct sl811 *sl811, u16 period, u16 load)
+{
+       int     i, branch = -ENOSPC;
+
+       /* search for the least loaded schedule branch of that period
+        * which has enough bandwidth left unreserved.
+        */
+       for (i = 0; i < period ; i++) {
+               if (branch < 0 || sl811->load[branch] > sl811->load[i]) {
+                       int     j;
+
+                       for (j = i; j < PERIODIC_SIZE; j += period) {
+                               if ((sl811->load[j] + load)
+                                               > MAX_PERIODIC_LOAD)
+                                       break;
+                       }
+                       if (j < PERIODIC_SIZE)
+                               continue;
+                       branch = i; 
+               }
+       }
+       return branch;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int sl811h_urb_enqueue(
+       struct usb_hcd          *hcd,
+       struct usb_host_endpoint *hep,
+       struct urb              *urb,
+       int                     mem_flags
+) {
+       struct sl811            *sl811 = hcd_to_sl811(hcd);
+       struct usb_device       *udev = urb->dev;
+       unsigned int            pipe = urb->pipe;
+       int                     is_out = !usb_pipein(pipe);
+       int                     type = usb_pipetype(pipe);
+       int                     epnum = usb_pipeendpoint(pipe);
+       struct sl811h_ep        *ep = NULL;
+       unsigned long           flags;
+       int                     i;
+       int                     retval = 0;
+
+#ifdef DISABLE_ISO
+       if (type == PIPE_ISOCHRONOUS)
+               return -ENOSPC;
+#endif
+
+       /* avoid all allocations within spinlocks */
+       if (!hep->hcpriv) {
+               ep = kcalloc(1, sizeof *ep, mem_flags);
+               /* set hep, otherwise sl811h_ep *start(struct sl811 *sl811, u8 bank) crashes */
+               ep->hep = hep;
+       }
+
+       spin_lock_irqsave(&sl811->lock, flags);
+
+       /* don't submit to a dead or disabled port */
+       if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE))
+                       || !HC_IS_RUNNING(hcd->state)) {
+               retval = -ENODEV;
+               goto fail;
+       }
+
+       if (hep->hcpriv) {
+               kfree(ep);
+               ep = hep->hcpriv;
+       } else if (!ep) {
+               retval = -ENOMEM;
+               goto fail;
+
+       } else {
+               INIT_LIST_HEAD(&ep->schedule);
+               ep->udev = usb_get_dev(udev);
+               ep->epnum = epnum;
+               ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out);
+               ep->defctrl = SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENABLE;
+               usb_settoggle(udev, epnum, is_out, 0);
+
+               if (type == PIPE_CONTROL)
+                       ep->nextpid = USB_PID_SETUP;
+               else if (is_out)
+                       ep->nextpid = USB_PID_OUT;
+               else
+                       ep->nextpid = USB_PID_IN;
+
+               if (ep->maxpacket > H_MAXPACKET) {
+                       /* iso packets up to 240 bytes could work... */
+                       DBG("dev %d ep%d maxpacket %d\n",
+                               udev->devnum, epnum, ep->maxpacket);
+                       retval = -EINVAL;
+                       goto fail;
+               }
+
+               if (udev->speed == USB_SPEED_LOW) {
+                       /* send preamble for external hub? */
+                       if (!(sl811->ctrl1 & SL11H_CTL1MASK_LSPD))
+                               ep->defctrl |= SL11H_HCTLMASK_PREAMBLE;
+               }
+               switch (type) {
+               case PIPE_ISOCHRONOUS:
+               case PIPE_INTERRUPT:
+                       if (urb->interval > PERIODIC_SIZE)
+                               urb->interval = PERIODIC_SIZE;
+                       ep->period = urb->interval;
+                       ep->branch = PERIODIC_SIZE;
+                       if (type == PIPE_ISOCHRONOUS)
+                               ep->defctrl |= SL11H_HCTLMASK_ISOCH;
+                       ep->load = usb_calc_bus_time(udev->speed, !is_out,
+                               (type == PIPE_ISOCHRONOUS),
+                               usb_maxpacket(udev, pipe, is_out))
+                                       / 1000;
+                       break;
+               }
+
+               hep->hcpriv = ep;
+       }
+
+       /* maybe put endpoint into schedule */
+       switch (type) {
+       case PIPE_CONTROL:
+       case PIPE_BULK:
+               if (list_empty(&ep->schedule))
+                       list_add_tail(&ep->schedule, &sl811->async);
+               break;
+       case PIPE_ISOCHRONOUS:
+       case PIPE_INTERRUPT:
+               urb->interval = ep->period;
+               if (ep->branch < PERIODIC_SIZE)
+                       break;
+
+               retval = balance(sl811, ep->period, ep->load);
+               if (retval < 0)
+                       goto fail;
+               ep->branch = retval;
+               retval = 0;
+               urb->start_frame = (sl811->frame & (PERIODIC_SIZE - 1))
+                                       + ep->branch;
+
+               /* sort each schedule branch by period (slow before fast)
+                * to share the faster parts of the tree without needing
+                * dummy/placeholder nodes
+                */
+               DBG("schedule qh%d/%p branch %d\n", ep->period, ep, ep->branch);
+               for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) {
+                       struct sl811h_ep        **prev = &sl811->periodic[i];
+                       struct sl811h_ep        *here = *prev;
+
+                       while (here && ep != here) {
+                               if (ep->period > here->period)
+                                       break;
+                               prev = &here->next;
+                               here = *prev;
+                       }
+                       if (ep != here) {
+                               ep->next = here;
+                               *prev = ep;
+                       }
+                       sl811->load[i] += ep->load;
+               }
+               sl811->periodic_count++;
+               hcd->self.bandwidth_allocated += ep->load / ep->period;
+               sofirq_on(sl811);
+       }
+
+       /* in case of unlink-during-submit */
+       spin_lock(&urb->lock);
+       if (urb->status != -EINPROGRESS) {
+               spin_unlock(&urb->lock);
+               finish_request(sl811, ep, urb, NULL, 0);
+               retval = 0;
+               goto fail;
+       }
+       
+       /* when uncommented, causes this error on mouse open: 
+               drivers/usb/input/usbmouse.c: can't resubmit intr, sl811-hcd-1/input0, status -22
+               urb->hcpriv = hep;  */
+       
+       spin_unlock(&urb->lock);
+
+       start_transfer(sl811);
+       sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+fail:
+       spin_unlock_irqrestore(&sl811->lock, flags);
+       return retval;
+}
+
+static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+{
+       struct sl811            *sl811 = hcd_to_sl811(hcd);
+       struct usb_host_endpoint *hep = urb->hcpriv;
+       unsigned long           flags;
+       struct sl811h_ep        *ep;
+       int                     retval = 0;
+
+       if (!hep)
+               return -EINVAL;
+
+       spin_lock_irqsave(&sl811->lock, flags);
+       ep = hep->hcpriv;
+       if (ep) {
+               /* finish right away if this urb can't be active ...
+                * note that some drivers wrongly expect delays
+                */
+               if (ep->hep->urb_list.next != &urb->urb_list) {
+                       /* not front of queue?  never active */
+
+               /* for active transfers, we expect an IRQ */
+               } else if (sl811->active_a == ep) {
+                       if (time_before_eq(sl811->jiffies_a, jiffies)) {
+                               /* happens a lot with lowspeed?? */
+                               DBG("giveup on DONE_A: ctrl %02x sts %02x\n",
+                                       sl811_read(sl811,
+                                               SL811_EP_A(SL11H_HOSTCTLREG)),
+                                       sl811_read(sl811,
+                                               SL811_EP_A(SL11H_PKTSTATREG)));
+                               sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG),
+                                               0);
+                               sl811->active_a = NULL;
+                       } else
+                               urb = NULL;
+#ifdef USE_B
+               } else if (sl811->active_b == ep) {
+                       if (time_before_eq(sl811->jiffies_a, jiffies)) {
+                               /* happens a lot with lowspeed?? */
+                               DBG("giveup on DONE_B: ctrl %02x sts %02x\n",
+                                       sl811_read(sl811,
+                                               SL811_EP_B(SL11H_HOSTCTLREG)),
+                                       sl811_read(sl811,
+                                               SL811_EP_B(SL11H_PKTSTATREG)));
+                               sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG),
+                                               0);
+                               sl811->active_b = NULL;
+                       } else
+                               urb = NULL;
+#endif
+               } else {
+                       /* front of queue for inactive endpoint */
+               }
+
+               if (urb)
+                       finish_request(sl811, ep, urb, NULL, 0);
+               else
+                       VDBG("dequeue, urb %p active %s; wait4irq\n", urb,
+                               (sl811->active_a == ep) ? "A" : "B");
+       } else
+               retval = -EINVAL;
+       spin_unlock_irqrestore(&sl811->lock, flags);
+       return retval;
+}
+
+static void
+sl811h_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
+{
+       struct sl811h_ep        *ep = hep->hcpriv;
+
+       if (!ep)
+               return;
+
+       /* assume we'd just wait for the irq */
+       if (!list_empty(&hep->urb_list))
+               msleep(3);
+       if (!list_empty(&hep->urb_list))
+               WARN("ep %p not empty?\n", ep);
+
+       usb_put_dev(ep->udev);
+       kfree(ep);
+       hep->hcpriv = NULL;
+}
+
+static int
+sl811h_get_frame(struct usb_hcd *hcd)
+{
+       struct sl811 *sl811 = hcd_to_sl811(hcd);
+
+       /* wrong except while periodic transfers are scheduled;
+        * never matches the on-the-wire frame;
+        * subject to overruns.
+        */
+       return sl811->frame;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* the virtual root hub timer IRQ checks for hub status */
+static int
+sl811h_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+       struct sl811 *sl811 = hcd_to_sl811(hcd);
+#ifdef QUIRK3
+       unsigned long flags;
+
+       /* non-SMP HACK: use root hub timer as i/o watchdog
+        * this seems essential when SOF IRQs aren't in use...
+        */
+       local_irq_save(flags);
+       if (!timer_pending(&sl811->timer)) {
+               if (sl811h_irq( /* ~0, */ hcd, NULL) != IRQ_NONE)
+                       sl811->stat_lost++;
+       }
+       local_irq_restore(flags);
+#endif
+
+       if (!(sl811->port1 & (0xffff << 16)))
+               return 0;
+
+       /* tell khubd port 1 changed */
+       *buf = (1 << 1);
+       return 1;
+}
+
+static void
+sl811h_hub_descriptor (
+       struct sl811                    *sl811,
+       struct usb_hub_descriptor       *desc
+) {
+       u16             temp = 0;
+
+       desc->bDescriptorType = 0x29;
+       desc->bHubContrCurrent = 0;
+
+       desc->bNbrPorts = 1;
+       desc->bDescLength = 9;
+
+       /* per-port power switching (gang of one!), or none */
+       desc->bPwrOn2PwrGood = 0;
+       if (sl811->board && sl811->board->port_power) {
+               desc->bPwrOn2PwrGood = sl811->board->potpg;
+               if (!desc->bPwrOn2PwrGood)
+                       desc->bPwrOn2PwrGood = 10;
+               temp = 0x0001;
+       } else
+               temp = 0x0002;
+
+       /* no overcurrent errors detection/handling */
+       temp |= 0x0010;
+
+       desc->wHubCharacteristics = (__force __u16)cpu_to_le16(temp);
+
+       /* two bitmaps:  ports removable, and legacy PortPwrCtrlMask */
+       desc->bitmap[0] = 1 << 1;
+       desc->bitmap[1] = ~0;
+}
+
+static void
+sl811h_timer(unsigned long _sl811)
+{
+       struct sl811    *sl811 = (void *) _sl811;
+       unsigned long   flags;
+       u8              irqstat;
+       u8              signaling = sl811->ctrl1 & SL11H_CTL1MASK_FORCE;
+       const u32       mask = (1 << USB_PORT_FEAT_CONNECTION)
+                               | (1 << USB_PORT_FEAT_ENABLE)
+                               | (1 << USB_PORT_FEAT_LOWSPEED);
+
+       spin_lock_irqsave(&sl811->lock, flags);
+
+       /* stop special signaling */
+       sl811->ctrl1 &= ~SL11H_CTL1MASK_FORCE;
+       sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+       udelay(3);
+
+       irqstat = sl811_read(sl811, SL11H_IRQ_STATUS);
+
+       switch (signaling) {
+       case SL11H_CTL1MASK_SE0:
+               DBG("end reset\n");
+               sl811->port1 = (1 << USB_PORT_FEAT_C_RESET)
+                               | (1 << USB_PORT_FEAT_POWER);
+               sl811->ctrl1 = 0;
+               /* don't wrongly ack RD */
+               if (irqstat & SL11H_INTMASK_INSRMV)
+                       irqstat &= ~SL11H_INTMASK_RD;
+               break;
+       case SL11H_CTL1MASK_K:
+               DBG("end resume\n");
+               sl811->port1 &= ~(1 << USB_PORT_FEAT_SUSPEND);
+               break;
+       default:
+               DBG("odd timer signaling: %02x\n", signaling);
+               break;
+       }
+       sl811_write(sl811, SL11H_IRQ_STATUS, irqstat);
+
+       if (irqstat & SL11H_INTMASK_RD) {
+               /* usbcore nukes all pending transactions on disconnect */
+               if (sl811->port1 & (1 << USB_PORT_FEAT_CONNECTION))
+                       sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION)
+                                       | (1 << USB_PORT_FEAT_C_ENABLE);
+               sl811->port1 &= ~mask;
+               sl811->irq_enable = SL11H_INTMASK_INSRMV;
+       } else {
+               sl811->port1 |= mask;
+               if (irqstat & SL11H_INTMASK_DP)
+                       sl811->port1 &= ~(1 << USB_PORT_FEAT_LOWSPEED);
+               sl811->irq_enable = SL11H_INTMASK_INSRMV | SL11H_INTMASK_RD;
+       }
+
+       if (sl811->port1 & (1 << USB_PORT_FEAT_CONNECTION)) {
+               u8      ctrl2 = SL811HS_CTL2_INIT;
+
+               sl811->irq_enable |= SL11H_INTMASK_DONE_A;
+#ifdef USE_B
+               sl811->irq_enable |= SL11H_INTMASK_DONE_B;
+#endif
+               if (sl811->port1 & (1 << USB_PORT_FEAT_LOWSPEED)) {
+                       sl811->ctrl1 |= SL11H_CTL1MASK_LSPD;
+                       ctrl2 |= SL811HS_CTL2MASK_DSWAP;
+               }
+
+               /* start SOFs flowing, kickstarting with A registers */
+               sl811->ctrl1 |= SL11H_CTL1MASK_SOF_ENA;
+               sl811_write(sl811, SL11H_SOFLOWREG, 0xe0);
+               sl811_write(sl811, SL811HS_CTLREG2, ctrl2);
+
+               /* autoincrementing */
+               sl811_write(sl811, SL811_EP_A(SL11H_BUFLNTHREG), 0);
+               writeb(SL_SOF, sl811->data_reg);
+               writeb(0, sl811->data_reg);
+               sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG),
+                               SL11H_HCTLMASK_ARM);
+
+               /* khubd provides debounce delay */
+       } else {
+               sl811->ctrl1 = 0;
+       }
+       sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+
+       /* reenable irqs */
+       sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+       spin_unlock_irqrestore(&sl811->lock, flags);
+}
+
+static int
+sl811h_hub_control(
+       struct usb_hcd  *hcd,
+       u16             typeReq,
+       u16             wValue,
+       u16             wIndex,
+       char            *buf,
+       u16             wLength
+) {
+       struct sl811    *sl811 = hcd_to_sl811(hcd);
+       int             retval = 0;
+       unsigned long   flags;
+
+       spin_lock_irqsave(&sl811->lock, flags);
+
+       switch (typeReq) {
+       case ClearHubFeature:
+       case SetHubFeature:
+               switch (wValue) {
+               case C_HUB_OVER_CURRENT:
+               case C_HUB_LOCAL_POWER:
+                       break;
+               default:
+                       goto error;
+               }
+               break;
+       case ClearPortFeature:
+               if (wIndex != 1 || wLength != 0)
+                       goto error;
+
+               switch (wValue) {
+               case USB_PORT_FEAT_ENABLE:
+                       sl811->port1 &= (1 << USB_PORT_FEAT_POWER);
+                       sl811->ctrl1 = 0;
+                       sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+                       sl811->irq_enable = SL11H_INTMASK_INSRMV;
+                       sl811_write(sl811, SL11H_IRQ_ENABLE,
+                                               sl811->irq_enable);
+                       break;
+               case USB_PORT_FEAT_SUSPEND:
+                       if (!(sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)))
+                               break;
+
+                       /* 20 msec of resume/K signaling, other irqs blocked */
+                       DBG("start resume...\n");
+                       sl811->irq_enable = 0;
+                       sl811_write(sl811, SL11H_IRQ_ENABLE,
+                                               sl811->irq_enable);
+                       sl811->ctrl1 |= SL11H_CTL1MASK_K;
+                       sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+
+                       mod_timer(&sl811->timer, jiffies
+                                       + msecs_to_jiffies(20));
+                       break;
+               case USB_PORT_FEAT_POWER:
+                       port_power(sl811, 0);
+                       break;
+               case USB_PORT_FEAT_C_ENABLE:
+               case USB_PORT_FEAT_C_SUSPEND:
+               case USB_PORT_FEAT_C_CONNECTION:
+               case USB_PORT_FEAT_C_OVER_CURRENT:
+               case USB_PORT_FEAT_C_RESET:
+                       break;
+               default:
+                       goto error;
+               }
+               sl811->port1 &= ~(1 << wValue);
+               break;
+       case GetHubDescriptor:
+               sl811h_hub_descriptor(sl811, (struct usb_hub_descriptor *) buf);
+               break;
+       case GetHubStatus:
+               *(__le32 *) buf = cpu_to_le32(0);
+               break;
+       case GetPortStatus:
+               if (wIndex != 1)
+                       goto error;
+               *(__le32 *) buf = cpu_to_le32(sl811->port1);
+
+#ifndef        VERBOSE
+       if (*(u16*)(buf+2))     /* only if wPortChange is interesting */
+#endif
+               DBG("GetPortStatus %08x\n", sl811->port1);
+               break;
+       case SetPortFeature:
+               if (wIndex != 1 || wLength != 0)
+                       goto error;
+               switch (wValue) {
+               case USB_PORT_FEAT_SUSPEND:
+                       if (sl811->port1 & (1 << USB_PORT_FEAT_RESET))
+                               goto error;
+                       if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE)))
+                               goto error;
+
+                       DBG("suspend...\n");
+                       sl811->ctrl1 &= ~SL11H_CTL1MASK_SOF_ENA;
+                       sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+                       break;
+               case USB_PORT_FEAT_POWER:
+                       port_power(sl811, 1);
+                       break;
+               case USB_PORT_FEAT_RESET:
+                       if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND))
+                               goto error;
+                       if (!(sl811->port1 & (1 << USB_PORT_FEAT_POWER)))
+                               break;
+
+                       /* 50 msec of reset/SE0 signaling, irqs blocked */
+                       sl811->irq_enable = 0;
+                       sl811_write(sl811, SL11H_IRQ_ENABLE,
+                                               sl811->irq_enable);
+                       sl811->ctrl1 = SL11H_CTL1MASK_SE0;
+                       sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+                       sl811->port1 |= (1 << USB_PORT_FEAT_RESET);
+                       mod_timer(&sl811->timer, jiffies
+                                       + msecs_to_jiffies(50));
+                       break;
+               default:
+                       goto error;
+               }
+               sl811->port1 |= 1 << wValue;
+               break;
+
+       default:
+error:
+               /* "protocol stall" on error */
+               retval = -EPIPE;
+       }
+
+       spin_unlock_irqrestore(&sl811->lock, flags);
+       return retval;
+}
+
+#ifdef CONFIG_PM
+
+static int
+sl811h_hub_suspend(struct usb_hcd *hcd)
+{
+       // SOFs off
+       DBG("%s\n", __FUNCTION__);
+       return 0;
+}
+
+static int
+sl811h_hub_resume(struct usb_hcd *hcd)
+{
+       // SOFs on
+       DBG("%s\n", __FUNCTION__);
+       return 0;
+}
+
+#else
+
+#define        sl811h_hub_suspend      NULL
+#define        sl811h_hub_resume       NULL
+
+#endif
+
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef STUB_DEBUG_FILE
+
+static inline void create_debug_file(struct sl811 *sl811) { }
+static inline void remove_debug_file(struct sl811 *sl811) { }
+
+#else
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+static void dump_irq(struct seq_file *s, char *label, u8 mask)
+{
+       seq_printf(s, "%s %02x%s%s%s%s%s%s\n", label, mask,
+               (mask & SL11H_INTMASK_DONE_A) ? " done_a" : "",
+               (mask & SL11H_INTMASK_DONE_B) ? " done_b" : "",
+               (mask & SL11H_INTMASK_SOFINTR) ? " sof" : "",
+               (mask & SL11H_INTMASK_INSRMV) ? " ins/rmv" : "",
+               (mask & SL11H_INTMASK_RD) ? " rd" : "",
+               (mask & SL11H_INTMASK_DP) ? " dp" : "");
+}
+
+static int proc_sl811h_show(struct seq_file *s, void *unused)
+{
+       struct sl811            *sl811 = s->private;
+       struct sl811h_ep        *ep;
+       unsigned                i;
+
+       seq_printf(s, "%s\n%s version %s\nportstatus[1] = %08x\n",
+               sl811_to_hcd(sl811)->product_desc,
+               hcd_name, DRIVER_VERSION,
+               sl811->port1);
+
+       seq_printf(s, "insert/remove: %ld\n", sl811->stat_insrmv);
+       seq_printf(s, "current session:  done_a %ld done_b %ld "
+                       "wake %ld sof %ld overrun %ld lost %ld\n\n",
+               sl811->stat_a, sl811->stat_b,
+               sl811->stat_wake, sl811->stat_sof,
+               sl811->stat_overrun, sl811->stat_lost);
+
+       spin_lock_irq(&sl811->lock);
+
+       if (sl811->ctrl1 & SL11H_CTL1MASK_SUSPEND)
+               seq_printf(s, "(suspended)\n\n");
+       else {
+               u8      t = sl811_read(sl811, SL11H_CTLREG1);
+
+               seq_printf(s, "ctrl1 %02x%s%s%s%s\n", t,
+                       (t & SL11H_CTL1MASK_SOF_ENA) ? " sofgen" : "",
+                       ({char *s; switch (t & SL11H_CTL1MASK_FORCE) {
+                       case SL11H_CTL1MASK_NORMAL: s = ""; break;
+                       case SL11H_CTL1MASK_SE0: s = " se0/reset"; break;
+                       case SL11H_CTL1MASK_K: s = " k/resume"; break;
+                       default: s = "j"; break;
+                       }; s; }),
+                       (t & SL11H_CTL1MASK_LSPD) ? " lowspeed" : "",
+                       (t & SL11H_CTL1MASK_SUSPEND) ? " suspend" : "");
+
+               dump_irq(s, "irq_enable",
+                               sl811_read(sl811, SL11H_IRQ_ENABLE));
+               dump_irq(s, "irq_status",
+                               sl811_read(sl811, SL11H_IRQ_STATUS));
+               seq_printf(s, "frame clocks remaining:  %d\n",
+                               sl811_read(sl811, SL11H_SOFTMRREG) << 6);
+       }
+
+       seq_printf(s, "A: qh%p ctl %02x sts %02x\n", sl811->active_a,
+               sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG)),
+               sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG)));
+       seq_printf(s, "B: qh%p ctl %02x sts %02x\n", sl811->active_b,
+               sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG)),
+               sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG)));
+       seq_printf(s, "\n");
+       list_for_each_entry (ep, &sl811->async, schedule) {
+               struct urb              *urb;
+
+               seq_printf(s, "%s%sqh%p, ep%d%s, maxpacket %d"
+                                       " nak %d err %d\n",
+                       (ep == sl811->active_a) ? "(A) " : "",
+                       (ep == sl811->active_b) ? "(B) " : "",
+                       ep, ep->epnum,
+                       ({ char *s; switch (ep->nextpid) {
+                       case USB_PID_IN: s = "in"; break;
+                       case USB_PID_OUT: s = "out"; break;
+                       case USB_PID_SETUP: s = "setup"; break;
+                       case USB_PID_ACK: s = "status"; break;
+                       default: s = "?"; break;
+                       }; s;}),
+                       ep->maxpacket,
+                       ep->nak_count, ep->error_count);
+               list_for_each_entry (urb, &ep->hep->urb_list, urb_list) {
+                       seq_printf(s, "  urb%p, %d/%d\n", urb,
+                               urb->actual_length,
+                               urb->transfer_buffer_length);
+               }
+       }
+       if (!list_empty(&sl811->async))
+               seq_printf(s, "\n");
+
+       seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE);
+
+       for (i = 0; i < PERIODIC_SIZE; i++) {
+               ep = sl811->periodic[i];
+               if (!ep)
+                       continue;
+               seq_printf(s, "%2d [%3d]:\n", i, sl811->load[i]);
+
+               /* DUMB: prints shared entries multiple times */
+               do {
+                       seq_printf(s,
+                               "   %s%sqh%d/%p (%sdev%d ep%d%s max %d) "
+                                       "err %d\n",
+                               (ep == sl811->active_a) ? "(A) " : "",
+                               (ep == sl811->active_b) ? "(B) " : "",
+                               ep->period, ep,
+                               (ep->udev->speed == USB_SPEED_FULL)
+                                       ? "" : "ls ",
+                               ep->udev->devnum, ep->epnum,
+                               (ep->epnum == 0) ? ""
+                                       : ((ep->nextpid == USB_PID_IN)
+                                               ? "in"
+                                               : "out"),
+                               ep->maxpacket, ep->error_count);
+                       ep = ep->next;
+               } while (ep);
+       }
+
+       spin_unlock_irq(&sl811->lock);
+       seq_printf(s, "\n");
+
+       return 0;
+}
+
+static int proc_sl811h_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, proc_sl811h_show, PDE(inode)->data);
+}
+
+static struct file_operations proc_ops = {
+       .open           = proc_sl811h_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+/* expect just one sl811 per system */
+static const char proc_filename[] = "driver/sl811h";
+
+static void create_debug_file(struct sl811 *sl811)
+{
+       struct proc_dir_entry *pde;
+
+       pde = create_proc_entry(proc_filename, 0, NULL);
+       if (pde == NULL)
+               return;
+
+       pde->proc_fops = &proc_ops;
+       pde->data = sl811;
+       sl811->pde = pde;
+}
+
+static void remove_debug_file(struct sl811 *sl811)
+{
+       if (sl811->pde)
+               remove_proc_entry(proc_filename, NULL);
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static void
+sl811h_stop(struct usb_hcd *hcd)
+{
+       struct sl811    *sl811 = hcd_to_sl811(hcd);
+       unsigned long   flags;
+
+       del_timer_sync(&hcd->rh_timer);
+
+       spin_lock_irqsave(&sl811->lock, flags);
+       port_power(sl811, 0);
+       spin_unlock_irqrestore(&sl811->lock, flags);
+}
+
+static int
+sl811h_start(struct usb_hcd *hcd)
+{
+       struct sl811            *sl811 = hcd_to_sl811(hcd);
+
+       /* chip has been reset, VBUS power is off */
+       hcd->state = HC_STATE_RUNNING;
+
+       if (sl811->board) {
+               hcd->can_wakeup = sl811->board->can_wakeup;
+               hcd->power_budget = sl811->board->power * 2;
+       }
+
+       // enable power and interupts   
+       port_power(sl811, 1);
+
+       return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct hc_driver sl811h_hc_driver = {
+       .description =          hcd_name,
+       .hcd_priv_size =        sizeof(struct sl811),
+
+       /*
+        * generic hardware linkage
+        */
+       .irq =                  sl811h_irq,
+       .flags =                HCD_USB11 | HCD_MEMORY,
+
+       /* Basic lifecycle operations */
+       .start =                sl811h_start,
+       .stop =                 sl811h_stop,
+
+       /*
+        * managing i/o requests and associated device resources
+        */
+       .urb_enqueue =          sl811h_urb_enqueue,
+       .urb_dequeue =          sl811h_urb_dequeue,
+       .endpoint_disable =     sl811h_endpoint_disable,
+
+       /*
+        * periodic schedule support
+        */
+       .get_frame_number =     sl811h_get_frame,
+
+       /*
+        * root hub support
+        */
+       .hub_status_data =      sl811h_hub_status_data,
+       .hub_control =          sl811h_hub_control,
+       .hub_suspend =          sl811h_hub_suspend,
+       .hub_resume =           sl811h_hub_resume,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init_or_module
+sl811h_remove(struct device *dev)
+{
+       struct usb_hcd          *hcd = dev_get_drvdata(dev);
+       struct sl811            *sl811 = hcd_to_sl811(hcd);
+       struct platform_device  *pdev;
+       struct resource         *res;
+
+       DBG("sl811h_remove\n");
+       pdev = container_of(dev, struct platform_device, dev);
+
+       remove_debug_file(sl811);
+       usb_remove_hcd(hcd);
+
+#ifndef CONFIG_USB_SL811_CS
+       iounmap(sl811->data_reg);
+#endif
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (res != NULL) release_mem_region(res->start, 1);
+       else DBG("res: %d\n", res);
+#ifndef CONFIG_USB_SL811_CS
+       iounmap(sl811->addr_reg);
+#endif
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res != NULL) release_mem_region(res->start, 1);
+       else DBG("res: %d\n", res);
+
+       usb_put_hcd(hcd);
+       return 0;
+}
+
+#define resource_len(r) (((r)->end - (r)->start) + 1)
+
+/***************************************************************************
+ * This routine tests the Read/Write functionality of SL811HS registers
+ *
+ * 1) Store original register value into a buffer
+ * 2) Write to registers with a RAMP pattern. (10, 11, 12, ..., 255)
+ * 3) Read from register
+ * 4) Compare the written value with the read value and make sure they are
+ *    equivalent
+ * 5) Restore the original register value
+ *
+ * Input:  hci = data structure for the host controller
+ *
+ * Return: 1 = passed; 0 = failed
+ **************************************************************************/
+static int test_registers(struct sl811 * sl811)
+{
+        int i, result = 1;
+        u8 buf[256], data;
+
+        for (i = 0x10; i < 256; i++) {
+                /* save the original buffer */
+                buf[i] = sl811_read(sl811, i);
+
+                /* Write the new data to the buffer */
+                sl811_write(sl811, i, i); 
+        }
+
+        /* compare the written data */
+        for (i = 0x10; i < 256; i++) {
+                data = sl811_read(sl811, i);
+                if (data != i) {
+                        DBG("Pattern test failed!! value = 0x%x, s/b 0x%x\n",
+                           data, i);
+                        result = 0;
+                       break;
+                }
+        }
+
+        /* restore the data */
+        for (i = 0x10; i < 256; i++) {
+                sl811_write(sl811, i, buf[i]);
+        }
+
+       return result;
+}
+
+
+int sl811h_probe(struct device *dev)
+{
+       struct usb_hcd          *hcd;
+       struct sl811            *sl811;
+       struct platform_device  *pdev;
+       struct resource         *addr, *data;
+       int                     irq;
+       void __iomem            *addr_reg;
+       void __iomem            *data_reg;
+       int                     retval;
+       u8                      tmp;
+
+       /* basic sanity checks first.  board-specific init logic should
+        * have initialized these three resources and probably board
+        * specific platform_data.  we don't probe for IRQs, and do only
+        * minimal sanity checking.
+        */
+       pdev = container_of(dev, struct platform_device, dev);
+       if (pdev->num_resources < 3)
+               return -ENODEV;
+
+       addr = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       data = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       irq = platform_get_irq(pdev, 0);
+       if (!addr || !data || irq < 0)
+               return -ENODEV;
+
+       /* refuse to confuse usbcore */
+       if (dev->dma_mask) {
+               DBG("no we won't dma\n");
+               return -EINVAL;
+       }
+
+       if (!request_mem_region(addr->start, 1, hcd_name)) {
+               retval = -EBUSY;
+               goto err1;
+       }
+#ifdef CONFIG_USB_SL811_CS
+       addr_reg = (void *) addr->start;
+#else
+       addr_reg = ioremap(addr->start, resource_len(addr));
+#endif
+       if (addr_reg == NULL) {
+               retval = -ENOMEM;
+               goto err2;
+       }
+
+       if (!request_mem_region(data->start, 1, hcd_name)) {
+               retval = -EBUSY;
+               goto err3;
+       }
+
+#ifdef CONFIG_USB_SL811_CS
+       data_reg = (void *) data->start;
+#else
+       data_reg = ioremap(data->start, resource_len(data));
+#endif
+       if (data_reg == NULL) {
+               retval = -ENOMEM;
+               goto err4;
+       }
+
+       DBG("SL811_HCD: addr_reg: 0x%04x, data_reg: 0x%04x\n",
+           (int) addr_reg, (int) data_reg);
+
+       /* allocate and initialize hcd */
+       hcd = usb_create_hcd(&sl811h_hc_driver, dev, dev->bus_id);
+       if (!hcd) {
+               retval = -ENOMEM;
+               goto err5;
+       }
+       hcd->rsrc_start = addr->start;
+
+       sl811 = hcd_to_sl811(hcd);
+
+       spin_lock_init(&sl811->lock);
+       INIT_LIST_HEAD(&sl811->async);
+       sl811->board = dev->platform_data;
+       init_timer(&sl811->timer);
+       sl811->timer.function = sl811h_timer;
+       sl811->timer.data = (unsigned long) sl811;
+       sl811->addr_reg = addr_reg;
+       sl811->data_reg = data_reg;
+
+       if (test_registers(sl811) == 0) {
+               DBG("ERROR: register test failed!\n");
+               goto err5;
+       }
+       else DBG("sl811 register test passed.\n");
+
+
+       spin_lock_irq(&sl811->lock);
+       port_power(sl811, 0);
+       spin_unlock_irq(&sl811->lock);
+       msleep(200);
+
+       tmp = sl811_read(sl811, SL11H_HWREVREG);
+       switch (tmp >> 4) {
+       case 1:
+               hcd->product_desc = "SL811HS v1.2";
+               break;
+       case 2:
+               hcd->product_desc = "SL811HS v1.5";
+               break;
+       default:
+               /* reject case 0, SL11S is less functional */
+               DBG("chiprev %02x\n", tmp);
+               retval = -ENXIO;
+               goto err6;
+       }
+       /* sl811s would need a different handler for this irq */
+
+#if !defined(CONFIG_USB_SL811_CS) && defined(CONFIG_ARM)
+       /* Cypress docs say the IRQ is IRQT_HIGH ... */
+       set_irq_type(irq, IRQT_RISING);
+       
+#endif
+       retval = usb_add_hcd(hcd, irq, SA_INTERRUPT);
+       if (retval != 0)
+               goto err6;
+
+       INFO("%s, irq %d\n", hcd->product_desc, irq);
+
+       create_debug_file(sl811);
+       DBG("sl811_hcd initialized\n");
+       return retval;
+
+ err6:
+       usb_put_hcd(hcd);
+ err5:
+
+#ifndef CONFIG_USB_SL811_CS
+       iounmap(data_reg);
+#endif
+ err4:
+       release_mem_region(data->start, 1);
+ err3:
+#ifndef CONFIG_USB_SL811_CS
+       iounmap(addr_reg);
+#endif
+ err2:
+       release_mem_region(addr->start, 1);
+ err1:
+
+       DBG("init error, %d\n", retval);
+       return retval;
+}
+EXPORT_SYMBOL (sl811h_probe);
+
+#ifdef CONFIG_PM
+
+/* for this device there's no useful distinction between the controller
+ * and its root hub, except that the root hub only gets direct PM calls 
+ * when CONFIG_USB_SUSPEND is enabled.
+ */
+
+static int
+sl811h_suspend(struct device *dev, pm_message_t state, u32 phase)
+{
+       struct usb_hcd  *hcd = dev_get_drvdata(dev);
+       struct sl811    *sl811 = hcd_to_sl811(hcd);
+       int             retval = 0;
+
+       if (phase != SUSPEND_POWER_DOWN)
+               return retval;
+
+       if (state <= PM_SUSPEND_MEM)
+               retval = sl811h_hub_suspend(hcd);
+       else
+               port_power(sl811, 0);
+       if (retval == 0)
+               dev->power.power_state = state;
+       return retval;
+}
+
+static int
+sl811h_resume(struct device *dev, u32 phase)
+{
+       struct usb_hcd  *hcd = dev_get_drvdata(dev);
+       struct sl811    *sl811 = hcd_to_sl811(hcd);
+
+       if (phase != RESUME_POWER_ON)
+               return 0;
+
+       /* with no "check to see if VBUS is still powered" board hook,
+        * let's assume it'd only be powered to enable remote wakeup.
+        */
+       if (dev->power.power_state > PM_SUSPEND_MEM
+                       || !hcd->can_wakeup) {
+               sl811->port1 = 0;
+               port_power(sl811, 1);
+               return 0;
+       }
+
+       dev->power.power_state = PMSG_ON;
+       return sl811h_hub_resume(hcd);
+}
+
+#else
+
+#define        sl811h_suspend  NULL
+#define        sl811h_resume   NULL
+
+#endif
+
+
+struct device_driver sl811h_driver = {
+       .name =         (char *) hcd_name,
+       .bus =          &platform_bus_type,
+
+       .probe =        sl811h_probe,
+       .remove =       sl811h_remove,
+
+       .suspend =      sl811h_suspend,
+       .resume =       sl811h_resume,
+};
+EXPORT_SYMBOL(sl811h_driver);
+
+/*-------------------------------------------------------------------------*/
+static int __init sl811h_init(void) 
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       INFO("driver %s, %s\n", hcd_name, DRIVER_VERSION);
+       return driver_register(&sl811h_driver);
+}
+module_init(sl811h_init);
+
+static void __exit sl811h_cleanup(void) 
+{      
+       driver_unregister(&sl811h_driver);
+}
+module_exit(sl811h_cleanup);