Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6
authorLinus Torvalds <torvalds@ppc970.osdl.org>
Mon, 27 Jun 2005 22:13:26 +0000 (15:13 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Mon, 27 Jun 2005 22:13:26 +0000 (15:13 -0700)
80 files changed:
arch/arm/mach-omap/usb.c
drivers/usb/Makefile
drivers/usb/atm/Kconfig
drivers/usb/atm/Makefile
drivers/usb/atm/cxacru.c [new file with mode: 0644]
drivers/usb/atm/speedtch.c
drivers/usb/atm/usb_atm.c [deleted file]
drivers/usb/atm/usb_atm.h [deleted file]
drivers/usb/atm/usbatm.c [new file with mode: 0644]
drivers/usb/atm/usbatm.h [new file with mode: 0644]
drivers/usb/atm/xusbatm.c [new file with mode: 0644]
drivers/usb/class/cdc-acm.c
drivers/usb/class/cdc-acm.h
drivers/usb/class/usblp.c
drivers/usb/core/devio.c
drivers/usb/core/hcd.c
drivers/usb/core/hcd.h
drivers/usb/core/hub.c
drivers/usb/core/hub.h
drivers/usb/gadget/Kconfig
drivers/usb/gadget/dummy_hcd.c
drivers/usb/gadget/ether.c
drivers/usb/gadget/file_storage.c
drivers/usb/gadget/goku_udc.c
drivers/usb/gadget/inode.c
drivers/usb/gadget/ndis.h
drivers/usb/gadget/net2280.c
drivers/usb/gadget/omap_udc.c
drivers/usb/gadget/omap_udc.h
drivers/usb/gadget/pxa2xx_udc.c
drivers/usb/gadget/pxa2xx_udc.h
drivers/usb/gadget/rndis.c
drivers/usb/gadget/rndis.h
drivers/usb/gadget/serial.c
drivers/usb/gadget/zero.c
drivers/usb/host/Kconfig
drivers/usb/host/Makefile
drivers/usb/host/ehci-dbg.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci-q.c
drivers/usb/host/ehci-sched.c
drivers/usb/host/isp116x-hcd.c [new file with mode: 0644]
drivers/usb/host/isp116x.h [new file with mode: 0644]
drivers/usb/host/ohci-hcd.c
drivers/usb/host/ohci-mem.c
drivers/usb/host/ohci-omap.c
drivers/usb/host/ohci.h
drivers/usb/host/sl811-hcd.c
drivers/usb/host/uhci-debug.c
drivers/usb/host/uhci-hcd.c
drivers/usb/host/uhci-hcd.h
drivers/usb/host/uhci-hub.c
drivers/usb/host/uhci-q.c
drivers/usb/input/ati_remote.c
drivers/usb/media/stv680.c
drivers/usb/media/stv680.h
drivers/usb/misc/idmouse.c
drivers/usb/misc/usbtest.c
drivers/usb/net/usbnet.c
drivers/usb/net/zd1201.c
drivers/usb/net/zd1201.h
drivers/usb/serial/cyberjack.c
drivers/usb/serial/generic.c
drivers/usb/serial/ipaq.c
drivers/usb/serial/ipw.c
drivers/usb/serial/ir-usb.c
drivers/usb/serial/keyspan_pda.c
drivers/usb/serial/omninet.c
drivers/usb/serial/safe_serial.c
drivers/usb/serial/usb-serial.c
drivers/usb/serial/usb-serial.h
drivers/usb/storage/scsiglue.c
drivers/usb/storage/scsiglue.h
drivers/usb/storage/transport.c
drivers/usb/storage/transport.h
include/asm-arm/arch-omap/usb.h
include/linux/usb_ch9.h
include/linux/usb_gadget.h
include/linux/usb_isp116x.h [new file with mode: 0644]

index 6e805d4..7f37857 100644 (file)
@@ -288,8 +288,8 @@ static void usb_release(struct device *dev)
 static struct resource udc_resources[] = {
        /* order is significant! */
        {               /* registers */
-               .start          = IO_ADDRESS(UDC_BASE),
-               .end            = IO_ADDRESS(UDC_BASE + 0xff),
+               .start          = UDC_BASE,
+               .end            = UDC_BASE + 0xff,
                .flags          = IORESOURCE_MEM,
        }, {            /* general IRQ */
                .start          = IH2_BASE + 20,
@@ -355,8 +355,8 @@ static struct platform_device ohci_device = {
 static struct resource otg_resources[] = {
        /* order is significant! */
        {
-               .start          = IO_ADDRESS(OTG_BASE),
-               .end            = IO_ADDRESS(OTG_BASE + 0xff),
+               .start          = OTG_BASE,
+               .end            = OTG_BASE + 0xff,
                .flags          = IORESOURCE_MEM,
        }, {
                .start          = IH2_BASE + 8,
index a708a1d..d79cd21 100644 (file)
@@ -9,6 +9,7 @@ obj-$(CONFIG_USB)               += core/
 obj-$(CONFIG_USB_MON)          += mon/
 
 obj-$(CONFIG_USB_EHCI_HCD)     += host/
+obj-$(CONFIG_USB_ISP116X_HCD)  += host/
 obj-$(CONFIG_USB_OHCI_HCD)     += host/
 obj-$(CONFIG_USB_UHCI_HCD)     += host/
 obj-$(CONFIG_USB_SL811_HCD)    += host/
index 0d9f537..f429862 100644 (file)
@@ -1,30 +1,60 @@
 #
-# USB ATM driver configuration
+# USB/ATM DSL configuration
 #
-comment "USB ATM/DSL drivers"
+
+menu "USB DSL modem support"
        depends on USB
 
 config USB_ATM
-       tristate "Generic USB ATM/DSL core I/O support"
+       tristate "USB DSL modem support"
        depends on USB && ATM
        select CRC32
        default n
        help
-         This provides a library which is used for packet I/O by USB DSL
-         modems, such as the SpeedTouch driver below. 
+         Say Y here if you want to connect a USB Digital Subscriber Line (DSL)
+         modem to your computer's USB port.  You will then need to choose your
+         modem from the list below.
 
          To compile this driver as a module, choose M here: the
-         module will be called usb_atm.
+         module will be called usbatm.
 
 config USB_SPEEDTOUCH
-       tristate "Alcatel Speedtouch USB support"
-       depends on USB && ATM
-       select USB_ATM
+       tristate "Speedtouch USB support"
+       depends on USB_ATM
+       select FW_LOADER
        help
-         Say Y here if you have an Alcatel SpeedTouch USB or SpeedTouch 330
+         Say Y here if you have an SpeedTouch USB or SpeedTouch 330
          modem.  In order to use your modem you will need to install the 
          two parts of the firmware, extracted by the user space tools; see
          <http://www.linux-usb.org/SpeedTouch/> for details.
 
          To compile this driver as a module, choose M here: the
          module will be called speedtch.
+
+config USB_CXACRU
+       tristate "Conexant AccessRunner USB support"
+       depends on USB_ATM
+       select FW_LOADER
+       help
+         Say Y here if you have an ADSL USB modem based on the Conexant
+         AccessRunner chipset.  In order to use your modem you will need to
+         install the firmware, extracted by the user space tools; see
+         <http://accessrunner.sourceforge.net/> for details.
+
+         To compile this driver as a module, choose M here: the
+         module will be called cxacru.
+
+config USB_XUSBATM
+       tristate "Other USB DSL modem support"
+       depends on USB_ATM
+       help
+         Say Y here if you have a DSL USB modem not explicitly supported by
+         another USB DSL drivers.  In order to use your modem you will need to
+         pass the vendor ID, product ID, and endpoint numbers for transmission
+         and reception as module parameters.  You may need to initialize the
+         the modem using a user space utility (a firmware loader for example).
+
+         To compile this driver as a module, choose M here: the
+         module will be called xusbatm.
+
+endmenu
index 9213b8b..751f297 100644 (file)
@@ -1,7 +1,8 @@
 #
-# Makefile for the rest of the USB drivers
-# (the ones that don't fit into any other categories)
+# Makefile for USB ATM/xDSL drivers
 #
 
-obj-$(CONFIG_USB_ATM)          += usb_atm.o
+obj-$(CONFIG_USB_CXACRU)       += cxacru.o
 obj-$(CONFIG_USB_SPEEDTOUCH)   += speedtch.o
+obj-$(CONFIG_USB_ATM)          += usbatm.o
+obj-$(CONFIG_USB_XUSBATM)      += xusbatm.o
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
new file mode 100644 (file)
index 0000000..cbd4a7d
--- /dev/null
@@ -0,0 +1,878 @@
+/******************************************************************************
+ *  cxacru.c  -  driver for USB ADSL modems based on
+ *               Conexant AccessRunner chipset
+ *
+ *  Copyright (C) 2004 David Woodhouse, Duncan Sands, Roman Kagan
+ *  Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru)
+ *
+ *  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., 59
+ *  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ ******************************************************************************/
+
+/*
+ *  Credit is due for Josep Comas, who created the original patch to speedtch.c
+ *  to support the different padding used by the AccessRunner (now generalized
+ *  into usbatm), and the userspace firmware loading utility.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/device.h>      /* FIXME: linux/firmware.h should include it itself */
+#include <linux/firmware.h>
+
+#include "usbatm.h"
+
+#define DRIVER_AUTHOR  "Roman Kagan, David Woodhouse, Duncan Sands"
+#define DRIVER_VERSION "0.2"
+#define DRIVER_DESC    "Conexant AccessRunner ADSL USB modem driver"
+
+static const char cxacru_driver_name[] = "cxacru";
+
+#define CXACRU_EP_CMD          0x01    /* Bulk/interrupt in/out */
+#define CXACRU_EP_DATA         0x02    /* Bulk in/out */
+
+#define CMD_PACKET_SIZE                64      /* Should be maxpacket(ep)? */
+
+/* Addresses */
+#define PLLFCLK_ADDR   0x00350068
+#define PLLBCLK_ADDR   0x0035006c
+#define SDRAMEN_ADDR   0x00350010
+#define FW_ADDR                0x00801000
+#define BR_ADDR                0x00180600
+#define SIG_ADDR       0x00180500
+#define BR_STACK_ADDR  0x00187f10
+
+/* Values */
+#define SDRAM_ENA      0x1
+
+#define CMD_TIMEOUT    2000    /* msecs */
+#define POLL_INTERVAL  5000    /* msecs */
+
+/* commands for interaction with the modem through the control channel before
+ * firmware is loaded  */
+enum cxacru_fw_request {
+       FW_CMD_ERR,
+       FW_GET_VER,
+       FW_READ_MEM,
+       FW_WRITE_MEM,
+       FW_RMW_MEM,
+       FW_CHECKSUM_MEM,
+       FW_GOTO_MEM,
+};
+
+/* commands for interaction with the modem through the control channel once
+ * firmware is loaded  */
+enum cxacru_cm_request {
+       CM_REQUEST_UNDEFINED = 0x80,
+       CM_REQUEST_TEST,
+       CM_REQUEST_CHIP_GET_MAC_ADDRESS,
+       CM_REQUEST_CHIP_GET_DP_VERSIONS,
+       CM_REQUEST_CHIP_ADSL_LINE_START,
+       CM_REQUEST_CHIP_ADSL_LINE_STOP,
+       CM_REQUEST_CHIP_ADSL_LINE_GET_STATUS,
+       CM_REQUEST_CHIP_ADSL_LINE_GET_SPEED,
+       CM_REQUEST_CARD_INFO_GET,
+       CM_REQUEST_CARD_DATA_GET,
+       CM_REQUEST_CARD_DATA_SET,
+       CM_REQUEST_COMMAND_HW_IO,
+       CM_REQUEST_INTERFACE_HW_IO,
+       CM_REQUEST_CARD_SERIAL_DATA_PATH_GET,
+       CM_REQUEST_CARD_SERIAL_DATA_PATH_SET,
+       CM_REQUEST_CARD_CONTROLLER_VERSION_GET,
+       CM_REQUEST_CARD_GET_STATUS,
+       CM_REQUEST_CARD_GET_MAC_ADDRESS,
+       CM_REQUEST_CARD_GET_DATA_LINK_STATUS,
+       CM_REQUEST_MAX,
+};
+
+/* reply codes to the commands above */
+enum cxacru_cm_status {
+       CM_STATUS_UNDEFINED,
+       CM_STATUS_SUCCESS,
+       CM_STATUS_ERROR,
+       CM_STATUS_UNSUPPORTED,
+       CM_STATUS_UNIMPLEMENTED,
+       CM_STATUS_PARAMETER_ERROR,
+       CM_STATUS_DBG_LOOPBACK,
+       CM_STATUS_MAX,
+};
+
+/* indices into CARD_INFO_GET return array */
+enum cxacru_info_idx {
+       CXINF_DOWNSTREAM_RATE,
+       CXINF_UPSTREAM_RATE,
+       CXINF_LINK_STATUS,
+       CXINF_LINE_STATUS,
+       CXINF_MAC_ADDRESS_HIGH,
+       CXINF_MAC_ADDRESS_LOW,
+       CXINF_UPSTREAM_SNR_MARGIN,
+       CXINF_DOWNSTREAM_SNR_MARGIN,
+       CXINF_UPSTREAM_ATTENUATION,
+       CXINF_DOWNSTREAM_ATTENUATION,
+       CXINF_TRANSMITTER_POWER,
+       CXINF_UPSTREAM_BITS_PER_FRAME,
+       CXINF_DOWNSTREAM_BITS_PER_FRAME,
+       CXINF_STARTUP_ATTEMPTS,
+       CXINF_UPSTREAM_CRC_ERRORS,
+       CXINF_DOWNSTREAM_CRC_ERRORS,
+       CXINF_UPSTREAM_FEC_ERRORS,
+       CXINF_DOWNSTREAM_FEC_ERRORS,
+       CXINF_UPSTREAM_HEC_ERRORS,
+       CXINF_DOWNSTREAM_HEC_ERRORS,
+       CXINF_LINE_STARTABLE,
+       CXINF_MODULATION,
+       CXINF_ADSL_HEADEND,
+       CXINF_ADSL_HEADEND_ENVIRONMENT,
+       CXINF_CONTROLLER_VERSION,
+       /* dunno what the missing two mean */
+       CXINF_MAX = 0x1c,
+};
+
+struct cxacru_modem_type {
+       u32 pll_f_clk;
+       u32 pll_b_clk;
+       int boot_rom_patch;
+};
+
+struct cxacru_data {
+       struct usbatm_data *usbatm;
+
+       const struct cxacru_modem_type *modem_type;
+
+       int line_status;
+       struct work_struct poll_work;
+
+       /* contol handles */
+       struct semaphore cm_serialize;
+       u8 *rcv_buf;
+       u8 *snd_buf;
+       struct urb *rcv_urb;
+       struct urb *snd_urb;
+       struct completion rcv_done;
+       struct completion snd_done;
+};
+
+/* the following three functions are stolen from drivers/usb/core/message.c */
+static void cxacru_blocking_completion(struct urb *urb, struct pt_regs *regs)
+{
+       complete((struct completion *)urb->context);
+}
+
+static void cxacru_timeout_kill(unsigned long data)
+{
+       usb_unlink_urb((struct urb *) data);
+}
+
+static int cxacru_start_wait_urb(struct urb *urb, struct completion *done,
+                                int* actual_length)
+{
+       struct timer_list timer;
+       int status;
+
+       init_timer(&timer);
+       timer.expires = jiffies + msecs_to_jiffies(CMD_TIMEOUT);
+       timer.data = (unsigned long) urb;
+       timer.function = cxacru_timeout_kill;
+       add_timer(&timer);
+       wait_for_completion(done);
+       status = urb->status;
+       if (status == -ECONNRESET)
+               status = -ETIMEDOUT;
+       del_timer_sync(&timer);
+
+       if (actual_length)
+               *actual_length = urb->actual_length;
+       return status;
+}
+
+static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
+                    u8 *wdata, int wsize, u8 *rdata, int rsize)
+{
+       int ret, actlen;
+       int offb, offd;
+       const int stride = CMD_PACKET_SIZE - 4;
+       u8 *wbuf = instance->snd_buf;
+       u8 *rbuf = instance->rcv_buf;
+       int wbuflen = ((wsize - 1) / stride + 1) * CMD_PACKET_SIZE;
+       int rbuflen = ((rsize - 1) / stride + 1) * CMD_PACKET_SIZE;
+
+       if (wbuflen > PAGE_SIZE || rbuflen > PAGE_SIZE) {
+               dbg("too big transfer requested");
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       down(&instance->cm_serialize);
+
+       /* submit reading urb before the writing one */
+       init_completion(&instance->rcv_done);
+       ret = usb_submit_urb(instance->rcv_urb, GFP_KERNEL);
+       if (ret < 0) {
+               dbg("submitting read urb for cm %#x failed", cm);
+               ret = ret;
+               goto fail;
+       }
+
+       memset(wbuf, 0, wbuflen);
+       /* handle wsize == 0 */
+       wbuf[0] = cm;
+       for (offb = offd = 0; offd < wsize; offd += stride, offb += CMD_PACKET_SIZE) {
+               wbuf[offb] = cm;
+               memcpy(wbuf + offb + 4, wdata + offd, min_t(int, stride, wsize - offd));
+       }
+
+       instance->snd_urb->transfer_buffer_length = wbuflen;
+       init_completion(&instance->snd_done);
+       ret = usb_submit_urb(instance->snd_urb, GFP_KERNEL);
+       if (ret < 0) {
+               dbg("submitting write urb for cm %#x failed", cm);
+               ret = ret;
+               goto fail;
+       }
+
+       ret = cxacru_start_wait_urb(instance->snd_urb, &instance->snd_done, NULL);
+       if (ret < 0) {
+               dbg("sending cm %#x failed", cm);
+               ret = ret;
+               goto fail;
+       }
+
+       ret = cxacru_start_wait_urb(instance->rcv_urb, &instance->rcv_done, &actlen);
+       if (ret < 0) {
+               dbg("receiving cm %#x failed", cm);
+               ret = ret;
+               goto fail;
+       }
+       if (actlen % CMD_PACKET_SIZE || !actlen) {
+               dbg("response is not a positive multiple of %d: %#x",
+                               CMD_PACKET_SIZE, actlen);
+               ret = -EIO;
+               goto fail;
+       }
+
+       /* check the return status and copy the data to the output buffer, if needed */
+       for (offb = offd = 0; offd < rsize && offb < actlen; offb += CMD_PACKET_SIZE) {
+               if (rbuf[offb] != cm) {
+                       dbg("wrong cm %#x in response", rbuf[offb]);
+                       ret = -EIO;
+                       goto fail;
+               }
+               if (rbuf[offb + 1] != CM_STATUS_SUCCESS) {
+                       dbg("response failed: %#x", rbuf[offb + 1]);
+                       ret = -EIO;
+                       goto fail;
+               }
+               if (offd >= rsize)
+                       break;
+               memcpy(rdata + offd, rbuf + offb + 4, min_t(int, stride, rsize - offd));
+               offd += stride;
+       }
+
+       ret = offd;
+       dbg("cm %#x", cm);
+fail:
+       up(&instance->cm_serialize);
+       return ret;
+}
+
+static int cxacru_cm_get_array(struct cxacru_data *instance, enum cxacru_cm_request cm,
+                              u32 *data, int size)
+{
+       int ret, len;
+       u32 *buf;
+       int offb, offd;
+       const int stride = CMD_PACKET_SIZE / (4 * 2) - 1;
+       int buflen =  ((size - 1) / stride + 1 + size * 2) * 4;
+
+       buf = kmalloc(buflen, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       ret = cxacru_cm(instance, cm, NULL, 0, (u8 *) buf, buflen);
+       if (ret < 0)
+               goto cleanup;
+
+       /* len > 0 && len % 4 == 0 guaranteed by cxacru_cm() */
+       len = ret / 4;
+       for (offb = 0; offb < len; ) {
+               int l = le32_to_cpu(buf[offb++]);
+               if (l > stride || l > (len - offb) / 2) {
+                       dbg("wrong data length %#x in response", l);
+                       ret = -EIO;
+                       goto cleanup;
+               }
+               while (l--) {
+                       offd = le32_to_cpu(buf[offb++]);
+                       if (offd >= size) {
+                               dbg("wrong index %#x in response", offd);
+                               ret = -EIO;
+                               goto cleanup;
+                       }
+                       data[offd] = le32_to_cpu(buf[offb++]);
+               }
+       }
+
+       ret = 0;
+
+cleanup:
+       kfree(buf);
+       return ret;
+}
+
+static int cxacru_card_status(struct cxacru_data *instance)
+{
+       int ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_STATUS, NULL, 0, NULL, 0);
+       if (ret < 0) {          /* firmware not loaded */
+               dbg("cxacru_adsl_start: CARD_GET_STATUS returned %d", ret);
+               return ret;
+       }
+       return 0;
+}
+
+static void cxacru_poll_status(struct cxacru_data *instance);
+
+static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
+               struct atm_dev *atm_dev)
+{
+       struct cxacru_data *instance = usbatm_instance->driver_data;
+       struct device *dev = &usbatm_instance->usb_intf->dev;
+       /*
+       struct atm_dev *atm_dev = usbatm_instance->atm_dev;
+       */
+       int ret;
+
+       dbg("cxacru_atm_start");
+
+       /* Read MAC address */
+       ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_MAC_ADDRESS, NULL, 0,
+                       atm_dev->esi, sizeof(atm_dev->esi));
+       if (ret < 0) {
+               dev_err(dev, "cxacru_atm_start: CARD_GET_MAC_ADDRESS returned %d\n", ret);
+               return ret;
+       }
+
+       /* start ADSL */
+       ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);
+       if (ret < 0) {
+               dev_err(dev, "cxacru_atm_start: CHIP_ADSL_LINE_START returned %d\n", ret);
+               return ret;
+       }
+
+       /* Start status polling */
+       cxacru_poll_status(instance);
+       return 0;
+}
+
+static void cxacru_poll_status(struct cxacru_data *instance)
+{
+       u32 buf[CXINF_MAX] = {};
+       struct device *dev = &instance->usbatm->usb_intf->dev;
+       struct atm_dev *atm_dev = instance->usbatm->atm_dev;
+       int ret;
+
+       ret = cxacru_cm_get_array(instance, CM_REQUEST_CARD_INFO_GET, buf, CXINF_MAX);
+       if (ret < 0) {
+               dev_warn(dev, "poll status: error %d\n", ret);
+               goto reschedule;
+       }
+
+       if (instance->line_status == buf[CXINF_LINE_STATUS])
+               goto reschedule;
+
+       instance->line_status = buf[CXINF_LINE_STATUS];
+       switch (instance->line_status) {
+       case 0:
+               atm_dev->signal = ATM_PHY_SIG_LOST;
+               dev_info(dev, "ADSL line: down\n");
+               break;
+
+       case 1:
+               atm_dev->signal = ATM_PHY_SIG_LOST;
+               dev_info(dev, "ADSL line: attemtping to activate\n");
+               break;
+
+       case 2:
+               atm_dev->signal = ATM_PHY_SIG_LOST;
+               dev_info(dev, "ADSL line: training\n");
+               break;
+
+       case 3:
+               atm_dev->signal = ATM_PHY_SIG_LOST;
+               dev_info(dev, "ADSL line: channel analysis\n");
+               break;
+
+       case 4:
+               atm_dev->signal = ATM_PHY_SIG_LOST;
+               dev_info(dev, "ADSL line: exchange\n");
+               break;
+
+       case 5:
+               atm_dev->link_rate = buf[CXINF_DOWNSTREAM_RATE] * 1000 / 424;
+               atm_dev->signal = ATM_PHY_SIG_FOUND;
+
+               dev_info(dev, "ADSL line: up (%d Kib/s down | %d Kib/s up)\n",
+                    buf[CXINF_DOWNSTREAM_RATE], buf[CXINF_UPSTREAM_RATE]);
+               break;
+
+       case 6:
+               atm_dev->signal = ATM_PHY_SIG_LOST;
+               dev_info(dev, "ADSL line: waiting\n");
+               break;
+
+       case 7:
+               atm_dev->signal = ATM_PHY_SIG_LOST;
+               dev_info(dev, "ADSL line: initializing\n");
+               break;
+
+       default:
+               atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
+               dev_info(dev, "Unknown line state %02x\n", instance->line_status);
+               break;
+       }
+reschedule:
+       schedule_delayed_work(&instance->poll_work, msecs_to_jiffies(POLL_INTERVAL));
+}
+
+static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw,
+                    u8 code1, u8 code2, u32 addr, u8 *data, int size)
+{
+       int ret;
+       u8 *buf;
+       int offd, offb;
+       const int stride = CMD_PACKET_SIZE - 8;
+
+       buf = (u8 *) __get_free_page(GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       offb = offd = 0;
+       do {
+               int l = min_t(int, stride, size - offd);
+               buf[offb++] = fw;
+               buf[offb++] = l;
+               buf[offb++] = code1;
+               buf[offb++] = code2;
+               *((u32 *) (buf + offb)) = cpu_to_le32(addr);
+               offb += 4;
+               addr += l;
+               if(l)
+                       memcpy(buf + offb, data + offd, l);
+               if (l < stride)
+                       memset(buf + offb + l, 0, stride - l);
+               offb += stride;
+               offd += stride;
+               if ((offb >= PAGE_SIZE) || (offd >= size)) {
+                       ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, CXACRU_EP_CMD),
+                                          buf, offb, NULL, CMD_TIMEOUT);
+                       if (ret < 0) {
+                               dbg("sending fw %#x failed", fw);
+                               goto cleanup;
+                       }
+                       offb = 0;
+               }
+       } while(offd < size);
+       dbg("sent fw %#x", fw);
+
+       ret = 0;
+
+cleanup:
+       free_page((unsigned long) buf);
+       return ret;
+}
+
+static void cxacru_upload_firmware(struct cxacru_data *instance,
+                                  const struct firmware *fw,
+                                  const struct firmware *bp,
+                                  const struct firmware *cf)
+{
+       int ret;
+       int off;
+       struct usb_device *usb_dev = instance->usbatm->usb_dev;
+       struct device *dev = &instance->usbatm->usb_intf->dev;
+       u16 signature[] = { usb_dev->descriptor.idVendor, usb_dev->descriptor.idProduct };
+       u32 val;
+
+       dbg("cxacru_upload_firmware");
+
+       /* FirmwarePllFClkValue */
+       val = cpu_to_le32(instance->modem_type->pll_f_clk);
+       ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, PLLFCLK_ADDR, (u8 *) &val, 4);
+       if (ret) {
+               dev_err(dev, "FirmwarePllFClkValue failed: %d\n", ret);
+               return;
+       }
+
+       /* FirmwarePllBClkValue */
+       val = cpu_to_le32(instance->modem_type->pll_b_clk);
+       ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, PLLBCLK_ADDR, (u8 *) &val, 4);
+       if (ret) {
+               dev_err(dev, "FirmwarePllBClkValue failed: %d\n", ret);
+               return;
+       }
+
+       /* Enable SDRAM */
+       val = cpu_to_le32(SDRAM_ENA);
+       ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, SDRAMEN_ADDR, (u8 *) &val, 4);
+       if (ret) {
+               dev_err(dev, "Enable SDRAM failed: %d\n", ret);
+               return;
+       }
+
+       /* Firmware */
+       ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, FW_ADDR, fw->data, fw->size);
+       if (ret) {
+               dev_err(dev, "Firmware upload failed: %d\n", ret);
+               return;
+       }
+
+       /* Boot ROM patch */
+       if (instance->modem_type->boot_rom_patch) {
+               ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_ADDR, bp->data, bp->size);
+               if (ret) {
+                       dev_err(dev, "Boot ROM patching failed: %d\n", ret);
+                       return;
+               }
+       }
+
+       /* Signature */
+       ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, SIG_ADDR, (u8 *) signature, 4);
+       if (ret) {
+               dev_err(dev, "Signature storing failed: %d\n", ret);
+               return;
+       }
+
+       if (instance->modem_type->boot_rom_patch) {
+               val = cpu_to_le32(BR_ADDR);
+               ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_STACK_ADDR, (u8 *) &val, 4);
+       }
+       else {
+               ret = cxacru_fw(usb_dev, FW_GOTO_MEM, 0x0, 0x0, FW_ADDR, NULL, 0);
+       }
+       if (ret) {
+               dev_err(dev, "Passing control to firmware failed: %d\n", ret);
+               return;
+       }
+
+       /* Delay to allow firmware to start up. */
+       msleep_interruptible(1000);
+
+       usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev, CXACRU_EP_CMD));
+       usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, CXACRU_EP_CMD));
+       usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev, CXACRU_EP_DATA));
+       usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, CXACRU_EP_DATA));
+
+       ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_STATUS, NULL, 0, NULL, 0);
+       if (ret < 0) {
+               dev_err(dev, "modem failed to initialize: %d\n", ret);
+               return;
+       }
+
+       /* Load config data (le32), doing one packet at a time */
+       if (cf)
+               for (off = 0; off < cf->size / 4; ) {
+                       u32 buf[CMD_PACKET_SIZE / 4 - 1];
+                       int i, len = min_t(int, cf->size / 4 - off, CMD_PACKET_SIZE / 4 / 2 - 1);
+                       buf[0] = cpu_to_le32(len);
+                       for (i = 0; i < len; i++, off++) {
+                               buf[i * 2 + 1] = cpu_to_le32(off);
+                               memcpy(buf + i * 2 + 2, cf->data + off * 4, 4);
+                       }
+                       ret = cxacru_cm(instance, CM_REQUEST_CARD_DATA_SET,
+                                       (u8 *) buf, len, NULL, 0);
+                       if (ret < 0) {
+                               dev_err(dev, "load config data failed: %d\n", ret);
+                               return;
+                       }
+               }
+
+       msleep_interruptible(4000);
+}
+
+static int cxacru_find_firmware(struct cxacru_data *instance,
+                               char* phase, const struct firmware **fw_p)
+{
+       struct device *dev = &instance->usbatm->usb_intf->dev;
+       char buf[16];
+
+       sprintf(buf, "cxacru-%s.bin", phase);
+       dbg("cxacru_find_firmware: looking for %s", buf);
+
+       if (request_firmware(fw_p, buf, dev)) {
+               dev_dbg(dev, "no stage %s firmware found\n", phase);
+               return -ENOENT;
+       }
+
+       dev_info(dev, "found firmware %s\n", buf);
+
+       return 0;
+}
+
+static int cxacru_heavy_init(struct usbatm_data *usbatm_instance,
+                            struct usb_interface *usb_intf)
+{
+       struct device *dev = &usbatm_instance->usb_intf->dev;
+       const struct firmware *fw, *bp, *cf;
+       struct cxacru_data *instance = usbatm_instance->driver_data;
+
+       int ret = cxacru_find_firmware(instance, "fw", &fw);
+       if (ret) {
+               dev_warn(dev, "firmware (cxacru-fw.bin) unavailable (hotplug misconfiguration?)\n");
+               return ret;
+       }
+
+       if (instance->modem_type->boot_rom_patch) {
+               ret = cxacru_find_firmware(instance, "bp", &bp);
+               if (ret) {
+                       dev_warn(dev, "boot ROM patch (cxacru-bp.bin) unavailable (hotplug misconfiguration?)\n");
+                       release_firmware(fw);
+                       return ret;
+               }
+       }
+
+       if (cxacru_find_firmware(instance, "cf", &cf))          /* optional */
+               cf = NULL;
+
+       cxacru_upload_firmware(instance, fw, bp, cf);
+
+       if (cf)
+               release_firmware(cf);
+       if (instance->modem_type->boot_rom_patch)
+               release_firmware(bp);
+       release_firmware(fw);
+
+       ret = cxacru_card_status(instance);
+       if (ret)
+               dbg("modem initialisation failed");
+       else
+               dbg("done setting up the modem");
+
+       return ret;
+}
+
+static int cxacru_bind(struct usbatm_data *usbatm_instance,
+                      struct usb_interface *intf, const struct usb_device_id *id,
+                      int *need_heavy_init)
+{
+       struct cxacru_data *instance;
+       struct usb_device *usb_dev = interface_to_usbdev(intf);
+       int ret;
+
+       /* instance init */
+       instance = kmalloc(sizeof(*instance), GFP_KERNEL);
+       if (!instance) {
+               dbg("cxacru_bind: no memory for instance data");
+               return -ENOMEM;
+       }
+
+       memset(instance, 0, sizeof(*instance));
+
+       instance->usbatm = usbatm_instance;
+       instance->modem_type = (struct cxacru_modem_type *) id->driver_info;
+
+       instance->rcv_buf = (u8 *) __get_free_page(GFP_KERNEL);
+       if (!instance->rcv_buf) {
+               dbg("cxacru_bind: no memory for rcv_buf");
+               ret = -ENOMEM;
+               goto fail;
+       }
+       instance->snd_buf = (u8 *) __get_free_page(GFP_KERNEL);
+       if (!instance->snd_buf) {
+               dbg("cxacru_bind: no memory for snd_buf");
+               ret = -ENOMEM;
+               goto fail;
+       }
+       instance->rcv_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!instance->rcv_urb) {
+               dbg("cxacru_bind: no memory for rcv_urb");
+               ret = -ENOMEM;
+               goto fail;
+       }
+       instance->snd_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!instance->snd_urb) {
+               dbg("cxacru_bind: no memory for snd_urb");
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       usb_fill_int_urb(instance->rcv_urb,
+                       usb_dev, usb_rcvintpipe(usb_dev, CXACRU_EP_CMD),
+                       instance->rcv_buf, PAGE_SIZE,
+                       cxacru_blocking_completion, &instance->rcv_done, 1);
+       instance->rcv_urb->transfer_flags |= URB_ASYNC_UNLINK;
+
+       usb_fill_int_urb(instance->snd_urb,
+                       usb_dev, usb_sndintpipe(usb_dev, CXACRU_EP_CMD),
+                       instance->snd_buf, PAGE_SIZE,
+                       cxacru_blocking_completion, &instance->snd_done, 4);
+       instance->snd_urb->transfer_flags |= URB_ASYNC_UNLINK;
+
+       init_MUTEX(&instance->cm_serialize);
+
+       INIT_WORK(&instance->poll_work, (void *)cxacru_poll_status, instance);
+
+       usbatm_instance->driver_data = instance;
+
+       *need_heavy_init = cxacru_card_status(instance);
+
+       return 0;
+
+ fail:
+       free_page((unsigned long) instance->snd_buf);
+       free_page((unsigned long) instance->rcv_buf);
+       usb_free_urb(instance->snd_urb);
+       usb_free_urb(instance->rcv_urb);
+       kfree(instance);
+
+       return ret;
+}
+
+static void cxacru_unbind(struct usbatm_data *usbatm_instance,
+               struct usb_interface *intf)
+{
+       struct cxacru_data *instance = usbatm_instance->driver_data;
+
+       dbg("cxacru_unbind entered");
+
+       if (!instance) {
+               dbg("cxacru_unbind: NULL instance!");
+               return;
+       }
+
+       while (!cancel_delayed_work(&instance->poll_work))
+              flush_scheduled_work();
+
+       usb_kill_urb(instance->snd_urb);
+       usb_kill_urb(instance->rcv_urb);
+       usb_free_urb(instance->snd_urb);
+       usb_free_urb(instance->rcv_urb);
+
+       free_page((unsigned long) instance->snd_buf);
+       free_page((unsigned long) instance->rcv_buf);
+       kfree(instance);
+
+       usbatm_instance->driver_data = NULL;
+}
+
+static const struct cxacru_modem_type cxacru_cafe = {
+       .pll_f_clk = 0x02d874df,
+       .pll_b_clk = 0x0196a51a,
+       .boot_rom_patch = 1,
+};
+
+static const struct cxacru_modem_type cxacru_cb00 = {
+       .pll_f_clk = 0x5,
+       .pll_b_clk = 0x3,
+       .boot_rom_patch = 0,
+};
+
+static const struct usb_device_id cxacru_usb_ids[] = {
+       { /* V = Conexant                       P = ADSL modem (Euphrates project)      */
+               USB_DEVICE(0x0572, 0xcafe),     .driver_info = (unsigned long) &cxacru_cafe
+       },
+       { /* V = Conexant                       P = ADSL modem (Hasbani project)        */
+               USB_DEVICE(0x0572, 0xcb00),     .driver_info = (unsigned long) &cxacru_cb00
+       },
+       { /* V = Conexant                       P = ADSL modem                          */
+               USB_DEVICE(0x0572, 0xcb01),     .driver_info = (unsigned long) &cxacru_cb00
+       },
+       { /* V = Conexant                       P = ADSL modem                          */
+               USB_DEVICE(0x0572, 0xcb06),     .driver_info = (unsigned long) &cxacru_cb00
+       },
+       { /* V = Olitec                         P = ADSL modem version 2                */
+               USB_DEVICE(0x08e3, 0x0100),     .driver_info = (unsigned long) &cxacru_cafe
+       },
+       { /* V = Olitec                         P = ADSL modem version 3                */
+               USB_DEVICE(0x08e3, 0x0102),     .driver_info = (unsigned long) &cxacru_cb00
+       },
+       { /* V = Trust/Amigo Technology Co.     P = AMX-CA86U                           */
+               USB_DEVICE(0x0eb0, 0x3457),     .driver_info = (unsigned long) &cxacru_cafe
+       },
+       { /* V = Zoom                           P = 5510                                */
+               USB_DEVICE(0x1803, 0x5510),     .driver_info = (unsigned long) &cxacru_cb00
+       },
+       { /* V = Draytek                        P = Vigor 318                           */
+               USB_DEVICE(0x0675, 0x0200),     .driver_info = (unsigned long) &cxacru_cb00
+       },
+       { /* V = Zyxel                          P = 630-C1 aka OMNI ADSL USB (Annex A)  */
+               USB_DEVICE(0x0586, 0x330a),     .driver_info = (unsigned long) &cxacru_cb00
+       },
+       { /* V = Zyxel                          P = 630-C3 aka OMNI ADSL USB (Annex B)  */
+               USB_DEVICE(0x0586, 0x330b),     .driver_info = (unsigned long) &cxacru_cb00
+       },
+       { /* V = Aethra                         P = Starmodem UM1020                    */
+               USB_DEVICE(0x0659, 0x0020),     .driver_info = (unsigned long) &cxacru_cb00
+       },
+       { /* V = Aztech Systems                 P = ? AKA Pirelli AUA-010               */
+               USB_DEVICE(0x0509, 0x0812),     .driver_info = (unsigned long) &cxacru_cb00
+       },
+       { /* V = Netopia                        P = Cayman 3341(Annex A)/3351(Annex B)  */
+               USB_DEVICE(0x100d, 0xcb01),     .driver_info = (unsigned long) &cxacru_cb00
+       },
+       { /* V = Netopia                        P = Cayman 3342(Annex A)/3352(Annex B)  */
+               USB_DEVICE(0x100d, 0x3342),     .driver_info = (unsigned long) &cxacru_cb00
+       },
+       {}
+};
+
+MODULE_DEVICE_TABLE(usb, cxacru_usb_ids);
+
+static struct usbatm_driver cxacru_driver = {
+       .owner          = THIS_MODULE,
+       .driver_name    = cxacru_driver_name,
+       .bind           = cxacru_bind,
+       .heavy_init     = cxacru_heavy_init,
+       .unbind         = cxacru_unbind,
+       .atm_start      = cxacru_atm_start,
+       .in             = CXACRU_EP_DATA,
+       .out            = CXACRU_EP_DATA,
+       .rx_padding     = 3,
+       .tx_padding     = 11,
+};
+
+static int cxacru_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+       return usbatm_usb_probe(intf, id, &cxacru_driver);
+}
+
+static struct usb_driver cxacru_usb_driver = {
+       .owner          = THIS_MODULE,
+       .name           = cxacru_driver_name,
+       .probe          = cxacru_usb_probe,
+       .disconnect     = usbatm_usb_disconnect,
+       .id_table       = cxacru_usb_ids
+};
+
+static int __init cxacru_init(void)
+{
+       return usb_register(&cxacru_usb_driver);
+}
+
+static void __exit cxacru_cleanup(void)
+{
+       usb_deregister(&cxacru_usb_driver);
+}
+
+module_init(cxacru_init);
+module_exit(cxacru_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
index 2a1697b..6a6eaa2 100644 (file)
@@ -5,6 +5,8 @@
  *  Copyright (C) 2003, Duncan Sands
  *  Copyright (C) 2004, David Woodhouse
  *
+ *  Based on "modem_run.c", copyright (C) 2001, Benoit Papillault
+ *
  *  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)
  *
  ******************************************************************************/
 
-#include <linux/module.h>
-#include <linux/moduleparam.h>
+#include <asm/page.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/firmware.h>
 #include <linux/gfp.h>
+#include <linux/init.h>
 #include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/errno.h>
-#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/slab.h>
-#include <linux/wait.h>
-#include <linux/list.h>
-#include <asm/processor.h>
-#include <asm/uaccess.h>
-#include <linux/smp_lock.h>
-#include <linux/interrupt.h>
-#include <linux/atm.h>
-#include <linux/atmdev.h>
-#include <linux/crc32.h>
-#include <linux/init.h>
-#include <linux/firmware.h>
-
-#include "usb_atm.h"
+#include <linux/stat.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
 
-#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
-#      define USE_FW_LOADER
-#endif
+#include "usbatm.h"
 
 #define DRIVER_AUTHOR  "Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
-#define DRIVER_VERSION "1.8"
+#define DRIVER_VERSION "1.9"
 #define DRIVER_DESC    "Alcatel SpeedTouch USB driver version " DRIVER_VERSION
 
 static const char speedtch_driver_name[] = "speedtch";
 
-#define SPEEDTOUCH_VENDORID            0x06b9
-#define SPEEDTOUCH_PRODUCTID           0x4061
+#define CTRL_TIMEOUT 2000      /* milliseconds */
+#define DATA_TIMEOUT 2000      /* milliseconds */
 
-/* Timeout in jiffies */
-#define CTRL_TIMEOUT 2000
-#define DATA_TIMEOUT 2000
+#define OFFSET_7       0               /* size 1 */
+#define OFFSET_b       1               /* size 8 */
+#define OFFSET_d       9               /* size 4 */
+#define OFFSET_e       13              /* size 1 */
+#define OFFSET_f       14              /* size 1 */
+#define TOTAL          15
 
-#define OFFSET_7  0            /* size 1 */
-#define OFFSET_b  1            /* size 8 */
-#define OFFSET_d  9            /* size 4 */
-#define OFFSET_e 13            /* size 1 */
-#define OFFSET_f 14            /* size 1 */
-#define TOTAL    15
+#define SIZE_7         1
+#define SIZE_b         8
+#define SIZE_d         4
+#define SIZE_e         1
+#define SIZE_f         1
 
-#define SIZE_7 1
-#define SIZE_b 8
-#define SIZE_d 4
-#define SIZE_e 1
-#define SIZE_f 1
+#define MIN_POLL_DELAY         5000    /* milliseconds */
+#define MAX_POLL_DELAY         60000   /* milliseconds */
 
-static int dl_512_first = 0;
-static int sw_buffering = 0;
+#define RESUBMIT_DELAY         1000    /* milliseconds */
 
-module_param(dl_512_first, bool, 0444);
-MODULE_PARM_DESC(dl_512_first, "Read 512 bytes before sending firmware");
+#define DEFAULT_ALTSETTING     1
+#define DEFAULT_DL_512_FIRST   0
+#define DEFAULT_SW_BUFFERING   0
 
-module_param(sw_buffering, uint, 0444);
-MODULE_PARM_DESC(sw_buffering, "Enable software buffering");
+static int altsetting = DEFAULT_ALTSETTING;
+static int dl_512_first = DEFAULT_DL_512_FIRST;
+static int sw_buffering = DEFAULT_SW_BUFFERING;
 
-#define UDSL_IOCTL_LINE_UP             1
-#define UDSL_IOCTL_LINE_DOWN           2
+module_param(altsetting, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(altsetting,
+                "Alternative setting for data interface (default: "
+                __MODULE_STRING(DEFAULT_ALTSETTING) ")");
 
-#define SPEEDTCH_ENDPOINT_INT          0x81
-#define SPEEDTCH_ENDPOINT_DATA         0x07
-#define SPEEDTCH_ENDPOINT_FIRMWARE     0x05
+module_param(dl_512_first, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dl_512_first,
+                "Read 512 bytes before sending firmware (default: "
+                __MODULE_STRING(DEFAULT_DL_512_FIRST) ")");
 
-#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) )
+module_param(sw_buffering, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(sw_buffering,
+                "Enable software buffering (default: "
+                __MODULE_STRING(DEFAULT_SW_BUFFERING) ")");
 
-static struct usb_device_id speedtch_usb_ids[] = {
-       {USB_DEVICE(SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID)},
-       {}
-};
+#define ENDPOINT_INT           0x81
+#define ENDPOINT_DATA          0x07
+#define ENDPOINT_FIRMWARE      0x05
 
-MODULE_DEVICE_TABLE(usb, speedtch_usb_ids);
+#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) )
 
 struct speedtch_instance_data {
-       struct udsl_instance_data u;
+       struct usbatm_data *usbatm;
+
+       struct work_struct status_checker;
 
-       /* Status */
+       int poll_delay; /* milliseconds */
+
+       struct timer_list resubmit_timer;
        struct urb *int_urb;
        unsigned char int_data[16];
-       struct work_struct poll_work;
-       struct timer_list poll_timer;
-};
-/* USB */
-
-static int speedtch_usb_probe(struct usb_interface *intf,
-                             const struct usb_device_id *id);
-static void speedtch_usb_disconnect(struct usb_interface *intf);
-static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code,
-                             void *user_data);
-static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs);
-static void speedtch_poll_status(struct speedtch_instance_data *instance);
 
-static struct usb_driver speedtch_usb_driver = {
-       .owner          = THIS_MODULE,
-       .name           = speedtch_driver_name,
-       .probe          = speedtch_usb_probe,
-       .disconnect     = speedtch_usb_disconnect,
-       .ioctl          = speedtch_usb_ioctl,
-       .id_table       = speedtch_usb_ids,
+       unsigned char scratch_buffer[TOTAL];
 };
 
 /***************
 **  firmware  **
 ***************/
 
-static void speedtch_got_firmware(struct speedtch_instance_data *instance,
-                                 int got_it)
+static void speedtch_set_swbuff(struct speedtch_instance_data *instance, int state)
 {
-       int err;
-       struct usb_interface *intf;
-
-       down(&instance->u.serialize);   /* vs self, speedtch_firmware_start */
-       if (instance->u.status == UDSL_LOADED_FIRMWARE)
-               goto out;
-       if (!got_it) {
-               instance->u.status = UDSL_NO_FIRMWARE;
-               goto out;
-       }
-       if ((err = usb_set_interface(instance->u.usb_dev, 1, 1)) < 0) {
-               dbg("speedtch_got_firmware: usb_set_interface returned %d!", err);
-               instance->u.status = UDSL_NO_FIRMWARE;
-               goto out;
-       }
-
-       /* Set up interrupt endpoint */
-       intf = usb_ifnum_to_if(instance->u.usb_dev, 0);
-       if (intf && !usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) {
-
-               instance->int_urb = usb_alloc_urb(0, GFP_KERNEL);
-               if (instance->int_urb) {
-
-                       usb_fill_int_urb(instance->int_urb, instance->u.usb_dev,
-                                        usb_rcvintpipe(instance->u.usb_dev, SPEEDTCH_ENDPOINT_INT),
-                                        instance->int_data,
-                                        sizeof(instance->int_data),
-                                        speedtch_handle_int, instance, 50);
-                       err = usb_submit_urb(instance->int_urb, GFP_KERNEL);
-                       if (err) {
-                               /* Doesn't matter; we'll poll anyway */
-                               dbg("speedtch_got_firmware: Submission of interrupt URB failed %d", err);
-                               usb_free_urb(instance->int_urb);
-                               instance->int_urb = NULL;
-                               usb_driver_release_interface(&speedtch_usb_driver, intf);
-                       }
-               }
-       }
-       /* Start status polling */
-       mod_timer(&instance->poll_timer, jiffies + (1 * HZ));
-
-       instance->u.status = UDSL_LOADED_FIRMWARE;
-       tasklet_schedule(&instance->u.receive_tasklet);
- out:
-       up(&instance->u.serialize);
-       wake_up_interruptible(&instance->u.firmware_waiters);
-}
-
-static int speedtch_set_swbuff(struct speedtch_instance_data *instance,
-                              int state)
-{
-       struct usb_device *dev = instance->u.usb_dev;
+       struct usbatm_data *usbatm = instance->usbatm;
+       struct usb_device *usb_dev = usbatm->usb_dev;
        int ret;
 
-       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
-                             0x32, 0x40, state ? 0x01 : 0x00,
-                             0x00, NULL, 0, 100);
-       if (ret < 0) {
-               printk("Warning: %sabling SW buffering: usb_control_msg returned %d\n",
-                    state ? "En" : "Dis", ret);
-               return ret;
-       }
-
-       dbg("speedtch_set_swbuff: %sbled SW buffering", state ? "En" : "Dis");
-       return 0;
+       ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
+                             0x32, 0x40, state ? 0x01 : 0x00, 0x00, NULL, 0, CTRL_TIMEOUT);
+       if (ret < 0)
+               usb_warn(usbatm,
+                        "%sabling SW buffering: usb_control_msg returned %d\n",
+                        state ? "En" : "Dis", ret);
+       else
+               dbg("speedtch_set_swbuff: %sbled SW buffering", state ? "En" : "Dis");
 }
 
 static void speedtch_test_sequence(struct speedtch_instance_data *instance)
 {
-       struct usb_device *dev = instance->u.usb_dev;
-       unsigned char buf[10];
+       struct usbatm_data *usbatm = instance->usbatm;
+       struct usb_device *usb_dev = usbatm->usb_dev;
+       unsigned char *buf = instance->scratch_buffer;
        int ret;
 
        /* URB 147 */
        buf[0] = 0x1c;
        buf[1] = 0x50;
-       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
-                             0x01, 0x40, 0x0b, 0x00, buf, 2, 100);
+       ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
+                             0x01, 0x40, 0x0b, 0x00, buf, 2, CTRL_TIMEOUT);
        if (ret < 0)
-               printk(KERN_WARNING "%s failed on URB147: %d\n", __func__, ret);
+               usb_warn(usbatm, "%s failed on URB147: %d\n", __func__, ret);
 
        /* URB 148 */
        buf[0] = 0x32;
        buf[1] = 0x00;
-       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
-                             0x01, 0x40, 0x02, 0x00, buf, 2, 100);
+       ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
+                             0x01, 0x40, 0x02, 0x00, buf, 2, CTRL_TIMEOUT);
        if (ret < 0)
-               printk(KERN_WARNING "%s failed on URB148: %d\n", __func__, ret);
+               usb_warn(usbatm, "%s failed on URB148: %d\n", __func__, ret);
 
        /* URB 149 */
        buf[0] = 0x01;
        buf[1] = 0x00;
        buf[2] = 0x01;
-       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
-                             0x01, 0x40, 0x03, 0x00, buf, 3, 100);
+       ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
+                             0x01, 0x40, 0x03, 0x00, buf, 3, CTRL_TIMEOUT);
        if (ret < 0)
-               printk(KERN_WARNING "%s failed on URB149: %d\n", __func__, ret);
+               usb_warn(usbatm, "%s failed on URB149: %d\n", __func__, ret);
 
        /* URB 150 */
        buf[0] = 0x01;
        buf[1] = 0x00;
        buf[2] = 0x01;
-       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
-                             0x01, 0x40, 0x04, 0x00, buf, 3, 100);
+       ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
+                             0x01, 0x40, 0x04, 0x00, buf, 3, CTRL_TIMEOUT);
        if (ret < 0)
-               printk(KERN_WARNING "%s failed on URB150: %d\n", __func__, ret);
+               usb_warn(usbatm, "%s failed on URB150: %d\n", __func__, ret);
 }
 
-static int speedtch_start_synchro(struct speedtch_instance_data *instance)
+static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
+                                    const struct firmware *fw1,
+                                    const struct firmware *fw2)
 {
-       struct usb_device *dev = instance->u.usb_dev;
-       unsigned char buf[2];
-       int ret;
+       unsigned char *buffer;
+       struct usbatm_data *usbatm = instance->usbatm;
+       struct usb_interface *intf;
+       struct usb_device *usb_dev = usbatm->usb_dev;
+       int actual_length;
+       int ret = 0;
+       int offset;
+
+       usb_dbg(usbatm, "%s entered\n", __func__);
+
+       if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) {
+               ret = -ENOMEM;
+               usb_dbg(usbatm, "%s: no memory for buffer!\n", __func__);
+               goto out;
+       }
+
+       if (!(intf = usb_ifnum_to_if(usb_dev, 2))) {
+               ret = -ENODEV;
+               usb_dbg(usbatm, "%s: interface not found!\n", __func__);
+               goto out_free;
+       }
+
+       /* URB 7 */
+       if (dl_512_first) {     /* some modems need a read before writing the firmware */
+               ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
+                                  buffer, 0x200, &actual_length, 2000);
+
+               if (ret < 0 && ret != -ETIMEDOUT)
+                       usb_dbg(usbatm, "%s: read BLOCK0 from modem failed (%d)!\n", __func__, ret);
+               else
+                       usb_dbg(usbatm, "%s: BLOCK0 downloaded (%d bytes)\n", __func__, ret);
+       }
+
+       /* URB 8 : both leds are static green */
+       for (offset = 0; offset < fw1->size; offset += PAGE_SIZE) {
+               int thislen = min_t(int, PAGE_SIZE, fw1->size - offset);
+               memcpy(buffer, fw1->data + offset, thislen);
+
+               ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
+                                  buffer, thislen, &actual_length, DATA_TIMEOUT);
+
+               if (ret < 0) {
+                       usb_dbg(usbatm, "%s: write BLOCK1 to modem failed (%d)!\n", __func__, ret);
+                       goto out_free;
+               }
+               usb_dbg(usbatm, "%s: BLOCK1 uploaded (%zu bytes)\n", __func__, fw1->size);
+       }
+
+       /* USB led blinking green, ADSL led off */
+
+       /* URB 11 */
+       ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
+                          buffer, 0x200, &actual_length, DATA_TIMEOUT);
 
-       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
-                             0x12, 0xc0, 0x04, 0x00,
-                             buf, sizeof(buf), CTRL_TIMEOUT);
        if (ret < 0) {
-               printk(KERN_WARNING "SpeedTouch: Failed to start ADSL synchronisation: %d\n", ret);
-               return ret;
+               usb_dbg(usbatm, "%s: read BLOCK2 from modem failed (%d)!\n", __func__, ret);
+               goto out_free;
        }
+       usb_dbg(usbatm, "%s: BLOCK2 downloaded (%d bytes)\n", __func__, actual_length);
 
-       dbg("speedtch_start_synchro: modem prodded. %d Bytes returned: %02x %02x", ret, buf[0], buf[1]);
-       return 0;
+       /* URBs 12 to 139 - USB led blinking green, ADSL led off */
+       for (offset = 0; offset < fw2->size; offset += PAGE_SIZE) {
+               int thislen = min_t(int, PAGE_SIZE, fw2->size - offset);
+               memcpy(buffer, fw2->data + offset, thislen);
+
+               ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
+                                  buffer, thislen, &actual_length, DATA_TIMEOUT);
+
+               if (ret < 0) {
+                       usb_dbg(usbatm, "%s: write BLOCK3 to modem failed (%d)!\n", __func__, ret);
+                       goto out_free;
+               }
+       }
+       usb_dbg(usbatm, "%s: BLOCK3 uploaded (%zu bytes)\n", __func__, fw2->size);
+
+       /* USB led static green, ADSL led static red */
+
+       /* URB 142 */
+       ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
+                          buffer, 0x200, &actual_length, DATA_TIMEOUT);
+
+       if (ret < 0) {
+               usb_dbg(usbatm, "%s: read BLOCK4 from modem failed (%d)!\n", __func__, ret);
+               goto out_free;
+       }
+
+       /* success */
+       usb_dbg(usbatm, "%s: BLOCK4 downloaded (%d bytes)\n", __func__, actual_length);
+
+       /* Delay to allow firmware to start up. We can do this here
+          because we're in our own kernel thread anyway. */
+       msleep_interruptible(1000);
+
+       /* Enable software buffering, if requested */
+       if (sw_buffering)
+               speedtch_set_swbuff(instance, 1);
+
+       /* Magic spell; don't ask us what this does */
+       speedtch_test_sequence(instance);
+
+       ret = 0;
+
+out_free:
+       free_page((unsigned long)buffer);
+out:
+       return ret;
 }
 
-static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs)
+static int speedtch_find_firmware(struct usb_interface *intf, int phase,
+                                 const struct firmware **fw_p)
 {
-       struct speedtch_instance_data *instance = urb->context;
-       unsigned int count = urb->actual_length;
-       int ret;
+       struct device *dev = &intf->dev;
+       const u16 bcdDevice = le16_to_cpu(interface_to_usbdev(intf)->descriptor.bcdDevice);
+       const u8 major_revision = bcdDevice >> 8;
+       const u8 minor_revision = bcdDevice & 0xff;
+       char buf[24];
 
-       /* The magic interrupt for "up state" */
-       const static unsigned char up_int[6]   = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 };
-       /* The magic interrupt for "down state" */
-       const static unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00 };
+       sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision);
+       dev_dbg(dev, "%s: looking for %s\n", __func__, buf);
 
-       switch (urb->status) {
-       case 0:
-               /* success */
-               break;
-       case -ECONNRESET:
-       case -ENOENT:
-       case -ESHUTDOWN:
-               /* this urb is terminated; clean up */
-               dbg("%s - urb shutting down with status: %d", __func__, urb->status);
-               return;
-       default:
-               dbg("%s - nonzero urb status received: %d", __func__, urb->status);
-               goto exit;
-       }
+       if (request_firmware(fw_p, buf, dev)) {
+               sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision);
+               dev_dbg(dev, "%s: looking for %s\n", __func__, buf);
 
-       if (count < 6) {
-               dbg("%s - int packet too short", __func__);
-               goto exit;
+               if (request_firmware(fw_p, buf, dev)) {
+                       sprintf(buf, "speedtch-%d.bin", phase);
+                       dev_dbg(dev, "%s: looking for %s\n", __func__, buf);
+
+                       if (request_firmware(fw_p, buf, dev)) {
+                               dev_warn(dev, "no stage %d firmware found!\n", phase);
+                               return -ENOENT;
+                       }
+               }
        }
 
-       if (!memcmp(up_int, instance->int_data, 6)) {
-               del_timer(&instance->poll_timer);
-               printk(KERN_NOTICE "DSL line goes up\n");
-       } else if (!memcmp(down_int, instance->int_data, 6)) {
-               printk(KERN_NOTICE "DSL line goes down\n");
-       } else {
-               int i;
+       dev_info(dev, "found stage %d firmware %s\n", phase, buf);
 
-               printk(KERN_DEBUG "Unknown interrupt packet of %d bytes:", count);
-               for (i = 0; i < count; i++)
-                       printk(" %02x", instance->int_data[i]);
-               printk("\n");
+       return 0;
+}
+
+static int speedtch_heavy_init(struct usbatm_data *usbatm, struct usb_interface *intf)
+{
+       const struct firmware *fw1, *fw2;
+       struct speedtch_instance_data *instance = usbatm->driver_data;
+       int ret;
+
+       if ((ret = speedtch_find_firmware(intf, 1, &fw1)) < 0)
+                       return ret;
+
+       if ((ret = speedtch_find_firmware(intf, 2, &fw2)) < 0) {
+               release_firmware(fw1);
+               return ret;
        }
-       schedule_work(&instance->poll_work);
 
- exit:
-       rmb();
-       if (!instance->int_urb)
-               return;
+       ret = speedtch_upload_firmware(instance, fw1, fw2);
+
+       release_firmware(fw2);
+       release_firmware(fw1);
 
-       ret = usb_submit_urb(urb, GFP_ATOMIC);
-       if (ret)
-               err("%s - usb_submit_urb failed with result %d", __func__, ret);
+       return ret;
 }
 
-static int speedtch_get_status(struct speedtch_instance_data *instance,
-                              unsigned char *buf)
+
+/**********
+**  ATM  **
+**********/
+
+static int speedtch_read_status(struct speedtch_instance_data *instance)
 {
-       struct usb_device *dev = instance->u.usb_dev;
+       struct usbatm_data *usbatm = instance->usbatm;
+       struct usb_device *usb_dev = usbatm->usb_dev;
+       unsigned char *buf = instance->scratch_buffer;
        int ret;
 
        memset(buf, 0, TOTAL);
 
-       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+       ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
                              0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7,
                              CTRL_TIMEOUT);
        if (ret < 0) {
-               dbg("MSG 7 failed");
+               atm_dbg(usbatm, "%s: MSG 7 failed\n", __func__);
                return ret;
        }
 
-       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+       ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
                              0x12, 0xc0, 0x0b, 0x00, buf + OFFSET_b, SIZE_b,
                              CTRL_TIMEOUT);
        if (ret < 0) {
-               dbg("MSG B failed");
+               atm_dbg(usbatm, "%s: MSG B failed\n", __func__);
                return ret;
        }
 
-       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+       ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
                              0x12, 0xc0, 0x0d, 0x00, buf + OFFSET_d, SIZE_d,
                              CTRL_TIMEOUT);
        if (ret < 0) {
-               dbg("MSG D failed");
+               atm_dbg(usbatm, "%s: MSG D failed\n", __func__);
                return ret;
        }
 
-       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+       ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
                              0x01, 0xc0, 0x0e, 0x00, buf + OFFSET_e, SIZE_e,
                              CTRL_TIMEOUT);
        if (ret < 0) {
-               dbg("MSG E failed");
+               atm_dbg(usbatm, "%s: MSG E failed\n", __func__);
                return ret;
        }
 
-       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+       ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
                              0x01, 0xc0, 0x0f, 0x00, buf + OFFSET_f, SIZE_f,
                              CTRL_TIMEOUT);
        if (ret < 0) {
-               dbg("MSG F failed");
+               atm_dbg(usbatm, "%s: MSG F failed\n", __func__);
                return ret;
        }
 
        return 0;
 }
 
-static void speedtch_poll_status(struct speedtch_instance_data *instance)
+static int speedtch_start_synchro(struct speedtch_instance_data *instance)
 {
-       unsigned char buf[TOTAL];
+       struct usbatm_data *usbatm = instance->usbatm;
+       struct usb_device *usb_dev = usbatm->usb_dev;
+       unsigned char *buf = instance->scratch_buffer;
        int ret;
 
-       ret = speedtch_get_status(instance, buf);
-       if (ret) {
-               printk(KERN_WARNING
-                      "SpeedTouch: Error %d fetching device status\n", ret);
+       atm_dbg(usbatm, "%s entered\n", __func__);
+
+       memset(buf, 0, 2);
+
+       ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
+                             0x12, 0xc0, 0x04, 0x00,
+                             buf, 2, CTRL_TIMEOUT);
+
+       if (ret < 0)
+               atm_warn(usbatm, "failed to start ADSL synchronisation: %d\n", ret);
+       else
+               atm_dbg(usbatm, "%s: modem prodded. %d bytes returned: %02x %02x\n",
+                       __func__, ret, buf[0], buf[1]);
+
+       return ret;
+}
+
+static void speedtch_check_status(struct speedtch_instance_data *instance)
+{
+       struct usbatm_data *usbatm = instance->usbatm;
+       struct atm_dev *atm_dev = usbatm->atm_dev;
+       unsigned char *buf = instance->scratch_buffer;
+       int ret;
+
+       atm_dbg(usbatm, "%s entered\n", __func__);
+
+       ret = speedtch_read_status(instance);
+       if (ret < 0) {
+               atm_warn(usbatm, "error %d fetching device status\n", ret);
+               if (instance->poll_delay < MAX_POLL_DELAY)
+                       instance->poll_delay *= 2;
                return;
        }
 
-       dbg("Line state %02x", buf[OFFSET_7]);
+       if (instance->poll_delay > MIN_POLL_DELAY)
+               instance->poll_delay /= 2;
+
+       atm_dbg(usbatm, "%s: line state %02x\n", __func__, buf[OFFSET_7]);
 
        switch (buf[OFFSET_7]) {
        case 0:
-               if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) {
-                       instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
-                       printk(KERN_NOTICE "ADSL line is down\n");
+               if (atm_dev->signal != ATM_PHY_SIG_LOST) {
+                       atm_dev->signal = ATM_PHY_SIG_LOST;
+                       atm_info(usbatm, "ADSL line is down\n");
                        /* It'll never resync again unless we ask it to... */
-                       speedtch_start_synchro(instance);
+                       ret = speedtch_start_synchro(instance);
                }
                break;
 
        case 0x08:
-               if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) {
-                       instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
-                       printk(KERN_NOTICE "ADSL line is blocked?\n");
+               if (atm_dev->signal != ATM_PHY_SIG_UNKNOWN) {
+                       atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
+                       atm_info(usbatm, "ADSL line is blocked?\n");
                }
                break;
 
        case 0x10:
-               if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) {
-                       instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
-                       printk(KERN_NOTICE "ADSL line is synchronising\n");
+               if (atm_dev->signal != ATM_PHY_SIG_LOST) {
+                       atm_dev->signal = ATM_PHY_SIG_LOST;
+                       atm_info(usbatm, "ADSL line is synchronising\n");
                }
                break;
 
        case 0x20:
-               if (instance->u.atm_dev->signal != ATM_PHY_SIG_FOUND) {
+               if (atm_dev->signal != ATM_PHY_SIG_FOUND) {
                        int down_speed = buf[OFFSET_b] | (buf[OFFSET_b + 1] << 8)
                                | (buf[OFFSET_b + 2] << 16) | (buf[OFFSET_b + 3] << 24);
                        int up_speed = buf[OFFSET_b + 4] | (buf[OFFSET_b + 5] << 8)
                                | (buf[OFFSET_b + 6] << 16) | (buf[OFFSET_b + 7] << 24);
 
-                       if (!(down_speed & 0x0000ffff) &&
-                           !(up_speed & 0x0000ffff)) {
+                       if (!(down_speed & 0x0000ffff) && !(up_speed & 0x0000ffff)) {
                                down_speed >>= 16;
                                up_speed >>= 16;
                        }
-                       instance->u.atm_dev->link_rate = down_speed * 1000 / 424;
-                       instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND;
 
-                       printk(KERN_NOTICE
-                              "ADSL line is up (%d Kib/s down | %d Kib/s up)\n",
-                              down_speed, up_speed);
+                       atm_dev->link_rate = down_speed * 1000 / 424;
+                       atm_dev->signal = ATM_PHY_SIG_FOUND;
+
+                       atm_info(usbatm,
+                                "ADSL line is up (%d Kib/s down | %d Kib/s up)\n",
+                                down_speed, up_speed);
                }
                break;
 
        default:
-               if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) {
-                       instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
-                       printk(KERN_NOTICE "Unknown line state %02x\n", buf[OFFSET_7]);
+               if (atm_dev->signal != ATM_PHY_SIG_UNKNOWN) {
+                       atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
+                       atm_info(usbatm, "Unknown line state %02x\n", buf[OFFSET_7]);
                }
                break;
        }
 }
 
-static void speedtch_timer_poll(unsigned long data)
+static void speedtch_status_poll(unsigned long data)
 {
        struct speedtch_instance_data *instance = (void *)data;
 
-       schedule_work(&instance->poll_work);
-       mod_timer(&instance->poll_timer, jiffies + (5 * HZ));
+       schedule_work(&instance->status_checker);
+
+       /* The following check is racy, but the race is harmless */
+       if (instance->poll_delay < MAX_POLL_DELAY)
+               mod_timer(&instance->status_checker.timer, jiffies + msecs_to_jiffies(instance->poll_delay));
+       else
+               atm_warn(instance->usbatm, "Too many failures - disabling line status polling\n");
 }
 
-#ifdef USE_FW_LOADER
-static void speedtch_upload_firmware(struct speedtch_instance_data *instance,
-                                    const struct firmware *fw1,
-                                    const struct firmware *fw2)
+static void speedtch_resubmit_int(unsigned long data)
 {
-       unsigned char *buffer;
-       struct usb_device *usb_dev = instance->u.usb_dev;
-       struct usb_interface *intf;
-       int actual_length, ret;
-       int offset;
-
-       dbg("speedtch_upload_firmware");
-
-       if (!(intf = usb_ifnum_to_if(usb_dev, 2))) {
-               dbg("speedtch_upload_firmware: interface not found!");
-               goto fail;
-       }
-
-       if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) {
-               dbg("speedtch_upload_firmware: no memory for buffer!");
-               goto fail;
-       }
-
-       /* A user-space firmware loader may already have claimed interface #2 */
-       if ((ret =
-            usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) < 0) {
-               dbg("speedtch_upload_firmware: interface in use (%d)!", ret);
-               goto fail_free;
-       }
-
-       /* URB 7 */
-       if (dl_512_first) {     /* some modems need a read before writing the firmware */
-               ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
-                                  buffer, 0x200, &actual_length, 2000);
-
-               if (ret < 0 && ret != -ETIMEDOUT)
-                       dbg("speedtch_upload_firmware: read BLOCK0 from modem failed (%d)!", ret);
-               else
-                       dbg("speedtch_upload_firmware: BLOCK0 downloaded (%d bytes)", ret);
-       }
-
-       /* URB 8 : both leds are static green */
-       for (offset = 0; offset < fw1->size; offset += PAGE_SIZE) {
-               int thislen = min_t(int, PAGE_SIZE, fw1->size - offset);
-               memcpy(buffer, fw1->data + offset, thislen);
+       struct speedtch_instance_data *instance = (void *)data;
+       struct urb *int_urb = instance->int_urb;
+       int ret;
 
-               ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
-                                  buffer, thislen, &actual_length, DATA_TIMEOUT);
+       atm_dbg(instance->usbatm, "%s entered\n", __func__);
 
-               if (ret < 0) {
-                       dbg("speedtch_upload_firmware: write BLOCK1 to modem failed (%d)!", ret);
-                       goto fail_release;
+       if (int_urb) {
+               ret = usb_submit_urb(int_urb, GFP_ATOMIC);
+               if (!ret)
+                       schedule_work(&instance->status_checker);
+               else {
+                       atm_dbg(instance->usbatm, "%s: usb_submit_urb failed with result %d\n", __func__, ret);
+                       mod_timer(&instance->resubmit_timer, jiffies + msecs_to_jiffies(RESUBMIT_DELAY));
                }
-               dbg("speedtch_upload_firmware: BLOCK1 uploaded (%zu bytes)", fw1->size);
        }
+}
 
-       /* USB led blinking green, ADSL led off */
+static void speedtch_handle_int(struct urb *int_urb, struct pt_regs *regs)
+{
+       struct speedtch_instance_data *instance = int_urb->context;
+       struct usbatm_data *usbatm = instance->usbatm;
+       unsigned int count = int_urb->actual_length;
+       int ret = int_urb->status;
 
-       /* URB 11 */
-       ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
-                          buffer, 0x200, &actual_length, DATA_TIMEOUT);
+       /* The magic interrupt for "up state" */
+       const static unsigned char up_int[6]   = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 };
+       /* The magic interrupt for "down state" */
+       const static unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+       atm_dbg(usbatm, "%s entered\n", __func__);
 
        if (ret < 0) {
-               dbg("speedtch_upload_firmware: read BLOCK2 from modem failed (%d)!", ret);
-               goto fail_release;
+               atm_dbg(usbatm, "%s: nonzero urb status %d!\n", __func__, ret);
+               goto fail;
        }
-       dbg("speedtch_upload_firmware: BLOCK2 downloaded (%d bytes)", actual_length);
 
-       /* URBs 12 to 139 - USB led blinking green, ADSL led off */
-       for (offset = 0; offset < fw2->size; offset += PAGE_SIZE) {
-               int thislen = min_t(int, PAGE_SIZE, fw2->size - offset);
-               memcpy(buffer, fw2->data + offset, thislen);
+       if ((count == 6) && !memcmp(up_int, instance->int_data, 6)) {
+               del_timer(&instance->status_checker.timer);
+               atm_info(usbatm, "DSL line goes up\n");
+       } else if ((count == 6) && !memcmp(down_int, instance->int_data, 6)) {
+               atm_info(usbatm, "DSL line goes down\n");
+       } else {
+               int i;
 
-               ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
-                                  buffer, thislen, &actual_length, DATA_TIMEOUT);
+               atm_dbg(usbatm, "%s: unknown interrupt packet of length %d:", __func__, count);
+               for (i = 0; i < count; i++)
+                       printk(" %02x", instance->int_data[i]);
+               printk("\n");
+               goto fail;
+       }
 
+       if ((int_urb = instance->int_urb)) {
+               ret = usb_submit_urb(int_urb, GFP_ATOMIC);
+               schedule_work(&instance->status_checker);
                if (ret < 0) {
-                       dbg("speedtch_upload_firmware: write BLOCK3 to modem failed (%d)!", ret);
-                       goto fail_release;
+                       atm_dbg(usbatm, "%s: usb_submit_urb failed with result %d\n", __func__, ret);
+                       goto fail;
                }
        }
-       dbg("speedtch_upload_firmware: BLOCK3 uploaded (%zu bytes)", fw2->size);
-
-       /* USB led static green, ADSL led static red */
-
-       /* URB 142 */
-       ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
-                          buffer, 0x200, &actual_length, DATA_TIMEOUT);
-
-       if (ret < 0) {
-               dbg("speedtch_upload_firmware: read BLOCK4 from modem failed (%d)!", ret);
-               goto fail_release;
-       }
-
-       /* success */
-       dbg("speedtch_upload_firmware: BLOCK4 downloaded (%d bytes)", actual_length);
-
-       /* Delay to allow firmware to start up. We can do this here
-          because we're in our own kernel thread anyway. */
-       msleep(1000);
-
-       /* Enable software buffering, if requested */
-       if (sw_buffering)
-               speedtch_set_swbuff(instance, 1);
-
-       /* Magic spell; don't ask us what this does */
-       speedtch_test_sequence(instance);
-
-       /* Start modem synchronisation */
-       if (speedtch_start_synchro(instance))
-               dbg("speedtch_start_synchro: failed");
-
-       speedtch_got_firmware(instance, 1);
 
-       free_page((unsigned long)buffer);
        return;
 
- fail_release:
-       /* Only release interface #2 if uploading failed; we don't release it
-          we succeeded.  This prevents the userspace tools from trying to load
-          the firmware themselves */
-       usb_driver_release_interface(&speedtch_usb_driver, intf);
- fail_free:
-       free_page((unsigned long)buffer);
- fail:
-       speedtch_got_firmware(instance, 0);
+fail:
+       if ((int_urb = instance->int_urb))
+               mod_timer(&instance->resubmit_timer, jiffies + msecs_to_jiffies(RESUBMIT_DELAY));
 }
 
-static int speedtch_find_firmware(struct speedtch_instance_data
-                                 *instance, int phase,
-                                 const struct firmware **fw_p)
+static int speedtch_atm_start(struct usbatm_data *usbatm, struct atm_dev *atm_dev)
 {
-       char buf[24];
-       const u16 bcdDevice = le16_to_cpu(instance->u.usb_dev->descriptor.bcdDevice);
-       const u8 major_revision = bcdDevice >> 8;
-       const u8 minor_revision = bcdDevice & 0xff;
-
-       sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision);
-       dbg("speedtch_find_firmware: looking for %s", buf);
-
-       if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) {
-               sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision);
-               dbg("speedtch_find_firmware: looking for %s", buf);
+       struct usb_device *usb_dev = usbatm->usb_dev;
+       struct speedtch_instance_data *instance = usbatm->driver_data;
+       int i, ret;
+       unsigned char mac_str[13];
 
-               if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) {
-                       sprintf(buf, "speedtch-%d.bin", phase);
-                       dbg("speedtch_find_firmware: looking for %s", buf);
+       atm_dbg(usbatm, "%s entered\n", __func__);
 
-                       if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) {
-                               dev_warn(&instance->u.usb_dev->dev, "no stage %d firmware found!", phase);
-                               return -ENOENT;
-                       }
-               }
+       if ((ret = usb_set_interface(usb_dev, 1, altsetting)) < 0) {
+               atm_dbg(usbatm, "%s: usb_set_interface returned %d!\n", __func__, ret);
+               return ret;
        }
 
-       dev_info(&instance->u.usb_dev->dev, "found stage %d firmware %s\n", phase, buf);
-
-       return 0;
-}
-
-static int speedtch_load_firmware(void *arg)
-{
-       const struct firmware *fw1, *fw2;
-       struct speedtch_instance_data *instance = arg;
-
-       BUG_ON(!instance);
+       /* Set MAC address, it is stored in the serial number */
+       memset(atm_dev->esi, 0, sizeof(atm_dev->esi));
+       if (usb_string(usb_dev, usb_dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) {
+               for (i = 0; i < 6; i++)
+                       atm_dev->esi[i] = (hex2int(mac_str[i * 2]) * 16) + (hex2int(mac_str[i * 2 + 1]));
+       }
 
-       daemonize("firmware/speedtch");
+       /* Start modem synchronisation */
+       ret = speedtch_start_synchro(instance);
 
-       if (!speedtch_find_firmware(instance, 1, &fw1)) {
-               if (!speedtch_find_firmware(instance, 2, &fw2)) {
-                       speedtch_upload_firmware(instance, fw1, fw2);
-                       release_firmware(fw2);
+       /* Set up interrupt endpoint */
+       if (instance->int_urb) {
+               ret = usb_submit_urb(instance->int_urb, GFP_KERNEL);
+               if (ret < 0) {
+                       /* Doesn't matter; we'll poll anyway */
+                       atm_dbg(usbatm, "%s: submission of interrupt URB failed (%d)!\n", __func__, ret);
+                       usb_free_urb(instance->int_urb);
+                       instance->int_urb = NULL;
                }
-               release_firmware(fw1);
        }
 
-       /* In case we failed, set state back to NO_FIRMWARE so that
-          another later attempt may work. Otherwise, we never actually
-          manage to recover if, for example, the firmware is on /usr and
-          we look for it too early. */
-       speedtch_got_firmware(instance, 0);
+       /* Start status polling */
+       mod_timer(&instance->status_checker.timer, jiffies + msecs_to_jiffies(1000));
 
-       module_put(THIS_MODULE);
-       udsl_put_instance(&instance->u);
        return 0;
 }
-#endif /* USE_FW_LOADER */
 
-static void speedtch_firmware_start(struct speedtch_instance_data *instance)
+static void speedtch_atm_stop(struct usbatm_data *usbatm, struct atm_dev *atm_dev)
 {
-#ifdef USE_FW_LOADER
-       int ret;
-#endif
-
-       dbg("speedtch_firmware_start");
-
-       down(&instance->u.serialize);   /* vs self, speedtch_got_firmware */
-
-       if (instance->u.status >= UDSL_LOADING_FIRMWARE) {
-               up(&instance->u.serialize);
-               return;
-       }
+       struct speedtch_instance_data *instance = usbatm->driver_data;
+       struct urb *int_urb = instance->int_urb;
+
+       atm_dbg(usbatm, "%s entered\n", __func__);
+
+       del_timer_sync(&instance->status_checker.timer);
+
+       /*
+        * Since resubmit_timer and int_urb can schedule themselves and
+        * each other, shutting them down correctly takes some care
+        */
+       instance->int_urb = NULL; /* signal shutdown */
+       mb();
+       usb_kill_urb(int_urb);
+       del_timer_sync(&instance->resubmit_timer);
+       /*
+        * At this point, speedtch_handle_int and speedtch_resubmit_int
+        * can run or be running, but instance->int_urb == NULL means that
+        * they will not reschedule
+        */
+       usb_kill_urb(int_urb);
+       del_timer_sync(&instance->resubmit_timer);
+       usb_free_urb(int_urb);
 
-       instance->u.status = UDSL_LOADING_FIRMWARE;
-       up(&instance->u.serialize);
+       flush_scheduled_work();
+}
 
-#ifdef USE_FW_LOADER
-       udsl_get_instance(&instance->u);
-       try_module_get(THIS_MODULE);
 
-       ret = kernel_thread(speedtch_load_firmware, instance,
-                           CLONE_FS | CLONE_FILES);
+/**********
+**  USB  **
+**********/
 
-       if (ret >= 0)
-               return;         /* OK */
+static struct usb_device_id speedtch_usb_ids[] = {
+       {USB_DEVICE(0x06b9, 0x4061)},
+       {}
+};
 
-       dbg("speedtch_firmware_start: kernel_thread failed (%d)!", ret);
+MODULE_DEVICE_TABLE(usb, speedtch_usb_ids);
 
-       module_put(THIS_MODULE);
-       udsl_put_instance(&instance->u);
-       /* Just pretend it never happened... hope modem_run happens */
-#endif                         /* USE_FW_LOADER */
+static int speedtch_usb_probe(struct usb_interface *, const struct usb_device_id *);
 
-       speedtch_got_firmware(instance, 0);
-}
-
-static int speedtch_firmware_wait(struct udsl_instance_data *instance)
-{
-       speedtch_firmware_start((void *)instance);
+static struct usb_driver speedtch_usb_driver = {
+       .owner          = THIS_MODULE,
+       .name           = speedtch_driver_name,
+       .probe          = speedtch_usb_probe,
+       .disconnect     = usbatm_usb_disconnect,
+       .id_table       = speedtch_usb_ids
+};
 
-       if (wait_event_interruptible(instance->firmware_waiters, instance->status != UDSL_LOADING_FIRMWARE) < 0)
-               return -ERESTARTSYS;
+static void speedtch_release_interfaces(struct usb_device *usb_dev, int num_interfaces) {
+       struct usb_interface *cur_intf;
+       int i;
 
-       return (instance->status == UDSL_LOADED_FIRMWARE) ? 0 : -EAGAIN;
+       for(i = 0; i < num_interfaces; i++)
+               if ((cur_intf = usb_ifnum_to_if(usb_dev, i))) {
+                       usb_set_intfdata(cur_intf, NULL);
+                       usb_driver_release_interface(&speedtch_usb_driver, cur_intf);
+               }
 }
 
-/**********
-**  USB  **
-**********/
-
-static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code,
-                             void *user_data)
+static int speedtch_bind(struct usbatm_data *usbatm,
+                        struct usb_interface *intf,
+                        const struct usb_device_id *id,
+                        int *need_heavy_init)
 {
-       struct speedtch_instance_data *instance = usb_get_intfdata(intf);
+       struct usb_device *usb_dev = interface_to_usbdev(intf);
+       struct usb_interface *cur_intf;
+       struct speedtch_instance_data *instance;
+       int ifnum = intf->altsetting->desc.bInterfaceNumber;
+       int num_interfaces = usb_dev->actconfig->desc.bNumInterfaces;
+       int i, ret;
 
-       dbg("speedtch_usb_ioctl entered");
+       usb_dbg(usbatm, "%s entered\n", __func__);
 
-       if (!instance) {
-               dbg("speedtch_usb_ioctl: NULL instance!");
+       if (usb_dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) {
+               usb_dbg(usbatm, "%s: wrong device class %d\n", __func__, usb_dev->descriptor.bDeviceClass);
                return -ENODEV;
        }
 
-       switch (code) {
-       case UDSL_IOCTL_LINE_UP:
-               instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND;
-               speedtch_got_firmware(instance, 1);
-               return (instance->u.status == UDSL_LOADED_FIRMWARE) ? 0 : -EIO;
-       case UDSL_IOCTL_LINE_DOWN:
-               instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
-               return 0;
-       default:
-               return -ENOTTY;
-       }
-}
+       /* claim all interfaces */
 
-static int speedtch_usb_probe(struct usb_interface *intf,
-                             const struct usb_device_id *id)
-{
-       struct usb_device *dev = interface_to_usbdev(intf);
-       int ifnum = intf->altsetting->desc.bInterfaceNumber;
-       struct speedtch_instance_data *instance;
-       unsigned char mac_str[13];
-       int ret, i;
-       char buf7[SIZE_7];
+       for (i=0; i < num_interfaces; i++) {
+               cur_intf = usb_ifnum_to_if(usb_dev, i);
 
-       dbg("speedtch_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d",
-           le16_to_cpu(dev->descriptor.idVendor),
-           le16_to_cpu(dev->descriptor.idProduct), ifnum);
+               if ((i != ifnum) && cur_intf) {
+                       ret = usb_driver_claim_interface(&speedtch_usb_driver, cur_intf, usbatm);
 
-       if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) || 
-           (ifnum != 1))
-               return -ENODEV;
-
-       dbg("speedtch_usb_probe: device accepted");
+                       if (ret < 0) {
+                               usb_dbg(usbatm, "%s: failed to claim interface %d (%d)\n", __func__, i, ret);
+                               speedtch_release_interfaces(usb_dev, i);
+                               return ret;
+                       }
+               }
+       }
 
-       /* instance init */
        instance = kmalloc(sizeof(*instance), GFP_KERNEL);
+
        if (!instance) {
-               dbg("speedtch_usb_probe: no memory for instance data!");
-               return -ENOMEM;
+               usb_dbg(usbatm, "%s: no memory for instance data!\n", __func__);
+               ret = -ENOMEM;
+               goto fail_release;
        }
 
        memset(instance, 0, sizeof(struct speedtch_instance_data));
 
-       if ((ret = usb_set_interface(dev, 0, 0)) < 0)
-               goto fail;
+       instance->usbatm = usbatm;
 
-       if ((ret = usb_set_interface(dev, 2, 0)) < 0)
-               goto fail;
+       INIT_WORK(&instance->status_checker, (void *)speedtch_check_status, instance);
 
-       instance->u.data_endpoint = SPEEDTCH_ENDPOINT_DATA;
-       instance->u.firmware_wait = speedtch_firmware_wait;
-       instance->u.driver_name = speedtch_driver_name;
+       instance->status_checker.timer.function = speedtch_status_poll;
+       instance->status_checker.timer.data = (unsigned long)instance;
+       instance->poll_delay = MIN_POLL_DELAY;
 
-       ret = udsl_instance_setup(dev, &instance->u);
-       if (ret)
-               goto fail;
+       init_timer(&instance->resubmit_timer);
+       instance->resubmit_timer.function = speedtch_resubmit_int;
+       instance->resubmit_timer.data = (unsigned long)instance;
 
-       init_timer(&instance->poll_timer);
-       instance->poll_timer.function = speedtch_timer_poll;
-       instance->poll_timer.data = (unsigned long)instance;
+       instance->int_urb = usb_alloc_urb(0, GFP_KERNEL);
 
-       INIT_WORK(&instance->poll_work, (void *)speedtch_poll_status, instance);
+       if (instance->int_urb)
+               usb_fill_int_urb(instance->int_urb, usb_dev,
+                                usb_rcvintpipe(usb_dev, ENDPOINT_INT),
+                                instance->int_data, sizeof(instance->int_data),
+                                speedtch_handle_int, instance, 50);
+       else
+               usb_dbg(usbatm, "%s: no memory for interrupt urb!\n", __func__);
 
-       /* set MAC address, it is stored in the serial number */
-       memset(instance->u.atm_dev->esi, 0, sizeof(instance->u.atm_dev->esi));
-       if (usb_string(dev, dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) {
-               for (i = 0; i < 6; i++)
-                       instance->u.atm_dev->esi[i] =
-                               (hex2int(mac_str[i * 2]) * 16) + (hex2int(mac_str[i * 2 + 1]));
-       }
+       /* check whether the modem already seems to be alive */
+       ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
+                             0x12, 0xc0, 0x07, 0x00,
+                             instance->scratch_buffer + OFFSET_7, SIZE_7, 500);
 
-       /* First check whether the modem already seems to be alive */
-       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
-                             0x12, 0xc0, 0x07, 0x00, buf7, SIZE_7, 500);
+       *need_heavy_init = (ret != SIZE_7);
 
-       if (ret == SIZE_7) {
-               dbg("firmware appears to be already loaded");
-               speedtch_got_firmware(instance, 1);
-               speedtch_poll_status(instance);
-       } else {
-               speedtch_firmware_start(instance);
-       }
+       usb_dbg(usbatm, "%s: firmware %s loaded\n", __func__, need_heavy_init ? "not" : "already");
+
+       if (*need_heavy_init)
+               if ((ret = usb_reset_device(usb_dev)) < 0)
+                       goto fail_free;
 
-       usb_set_intfdata(intf, instance);
+        usbatm->driver_data = instance;
 
        return 0;
 
- fail:
+fail_free:
+       usb_free_urb(instance->int_urb);
        kfree(instance);
-
-       return -ENOMEM;
+fail_release:
+       speedtch_release_interfaces(usb_dev, num_interfaces);
+       return ret;
 }
 
-static void speedtch_usb_disconnect(struct usb_interface *intf)
+static void speedtch_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
 {
-       struct speedtch_instance_data *instance = usb_get_intfdata(intf);
-
-       dbg("speedtch_usb_disconnect entered");
-
-       if (!instance) {
-               dbg("speedtch_usb_disconnect: NULL instance!");
-               return;
-       }
+       struct usb_device *usb_dev = interface_to_usbdev(intf);
+       struct speedtch_instance_data *instance = usbatm->driver_data;
 
-/*QQ need to handle disconnects on interface #2 while uploading firmware */
-/*QQ and what about interface #1? */
-
-       if (instance->int_urb) {
-               struct urb *int_urb = instance->int_urb;
-               instance->int_urb = NULL;
-               wmb();
-               usb_unlink_urb(int_urb);
-               usb_free_urb(int_urb);
-       }
+       usb_dbg(usbatm, "%s entered\n", __func__);
 
-       instance->int_data[0] = 1;
-       del_timer_sync(&instance->poll_timer);
-       wmb();
-       flush_scheduled_work();
-
-       udsl_instance_disconnect(&instance->u);
-
-       /* clean up */
-       usb_set_intfdata(intf, NULL);
-       udsl_put_instance(&instance->u);
+       speedtch_release_interfaces(usb_dev, usb_dev->actconfig->desc.bNumInterfaces);
+       usb_free_urb(instance->int_urb);
+       kfree(instance);
 }
 
+
 /***********
 **  init  **
 ***********/
 
+static struct usbatm_driver speedtch_usbatm_driver = {
+       .owner          = THIS_MODULE,
+       .driver_name    = speedtch_driver_name,
+       .bind           = speedtch_bind,
+       .heavy_init     = speedtch_heavy_init,
+       .unbind         = speedtch_unbind,
+       .atm_start      = speedtch_atm_start,
+       .atm_stop       = speedtch_atm_stop,
+       .in             = ENDPOINT_DATA,
+       .out            = ENDPOINT_DATA
+};
+
+static int speedtch_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+       return usbatm_usb_probe(intf, id, &speedtch_usbatm_driver);
+}
+
 static int __init speedtch_usb_init(void)
 {
-       dbg("speedtch_usb_init: driver version " DRIVER_VERSION);
+       dbg("%s: driver version %s", __func__, DRIVER_VERSION);
 
        return usb_register(&speedtch_usb_driver);
 }
 
 static void __exit speedtch_usb_cleanup(void)
 {
-       dbg("speedtch_usb_cleanup entered");
+       dbg("%s", __func__);
 
        usb_deregister(&speedtch_usb_driver);
 }
diff --git a/drivers/usb/atm/usb_atm.c b/drivers/usb/atm/usb_atm.c
deleted file mode 100644 (file)
index a4cd447..0000000
+++ /dev/null
@@ -1,1188 +0,0 @@
-/******************************************************************************
- *  usb_atm.c - Generic USB xDSL driver core
- *
- *  Copyright (C) 2001, Alcatel
- *  Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas
- *  Copyright (C) 2004, David Woodhouse
- *
- *  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., 59
- *  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- *
- ******************************************************************************/
-
-/*
- *  Written by Johan Verrept, maintained by Duncan Sands (duncan.sands@free.fr)
- *
- *  1.7+:      - See the check-in logs
- *
- *  1.6:       - No longer opens a connection if the firmware is not loaded
- *             - Added support for the speedtouch 330
- *             - Removed the limit on the number of devices
- *             - Module now autoloads on device plugin
- *             - Merged relevant parts of sarlib
- *             - Replaced the kernel thread with a tasklet
- *             - New packet transmission code
- *             - Changed proc file contents
- *             - Fixed all known SMP races
- *             - Many fixes and cleanups
- *             - Various fixes by Oliver Neukum (oliver@neukum.name)
- *
- *  1.5A:      - Version for inclusion in 2.5 series kernel
- *             - Modifications by Richard Purdie (rpurdie@rpsys.net)
- *             - made compatible with kernel 2.5.6 onwards by changing
- *             udsl_usb_send_data_context->urb to a pointer and adding code
- *             to alloc and free it
- *             - remove_wait_queue() added to udsl_atm_processqueue_thread()
- *
- *  1.5:       - fixed memory leak when atmsar_decode_aal5 returned NULL.
- *             (reported by stephen.robinson@zen.co.uk)
- *
- *  1.4:       - changed the spin_lock() under interrupt to spin_lock_irqsave()
- *             - unlink all active send urbs of a vcc that is being closed.
- *
- *  1.3.1:     - added the version number
- *
- *  1.3:       - Added multiple send urb support
- *             - fixed memory leak and vcc->tx_inuse starvation bug
- *               when not enough memory left in vcc.
- *
- *  1.2:       - Fixed race condition in udsl_usb_send_data()
- *  1.1:       - Turned off packet debugging
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/errno.h>
-#include <linux/proc_fs.h>
-#include <linux/slab.h>
-#include <linux/wait.h>
-#include <linux/list.h>
-#include <asm/uaccess.h>
-#include <linux/smp_lock.h>
-#include <linux/interrupt.h>
-#include <linux/atm.h>
-#include <linux/atmdev.h>
-#include <linux/crc32.h>
-#include <linux/init.h>
-#include <linux/firmware.h>
-
-#include "usb_atm.h"
-
-#ifdef VERBOSE_DEBUG
-static int udsl_print_packet(const unsigned char *data, int len);
-#define PACKETDEBUG(arg...)    udsl_print_packet (arg)
-#define vdbg(arg...)           dbg (arg)
-#else
-#define PACKETDEBUG(arg...)
-#define vdbg(arg...)
-#endif
-
-#define DRIVER_AUTHOR  "Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
-#define DRIVER_VERSION "1.8"
-#define DRIVER_DESC    "Generic USB ATM/DSL I/O, version " DRIVER_VERSION
-
-static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS;
-static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS;
-static unsigned int num_rcv_bufs = UDSL_DEFAULT_RCV_BUFS;
-static unsigned int num_snd_bufs = UDSL_DEFAULT_SND_BUFS;
-static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE;
-static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE;
-
-module_param(num_rcv_urbs, uint, 0444);
-MODULE_PARM_DESC(num_rcv_urbs,
-                "Number of urbs used for reception (range: 0-"
-                __MODULE_STRING(UDSL_MAX_RCV_URBS) ", default: "
-                __MODULE_STRING(UDSL_DEFAULT_RCV_URBS) ")");
-
-module_param(num_snd_urbs, uint, 0444);
-MODULE_PARM_DESC(num_snd_urbs,
-                "Number of urbs used for transmission (range: 0-"
-                __MODULE_STRING(UDSL_MAX_SND_URBS) ", default: "
-                __MODULE_STRING(UDSL_DEFAULT_SND_URBS) ")");
-
-module_param(num_rcv_bufs, uint, 0444);
-MODULE_PARM_DESC(num_rcv_bufs,
-                "Number of buffers used for reception (range: 0-"
-                __MODULE_STRING(UDSL_MAX_RCV_BUFS) ", default: "
-                __MODULE_STRING(UDSL_DEFAULT_RCV_BUFS) ")");
-
-module_param(num_snd_bufs, uint, 0444);
-MODULE_PARM_DESC(num_snd_bufs,
-                "Number of buffers used for transmission (range: 0-"
-                __MODULE_STRING(UDSL_MAX_SND_BUFS) ", default: "
-                __MODULE_STRING(UDSL_DEFAULT_SND_BUFS) ")");
-
-module_param(rcv_buf_size, uint, 0444);
-MODULE_PARM_DESC(rcv_buf_size,
-                "Size of the buffers used for reception (range: 0-"
-                __MODULE_STRING(UDSL_MAX_RCV_BUF_SIZE) ", default: "
-                __MODULE_STRING(UDSL_DEFAULT_RCV_BUF_SIZE) ")");
-
-module_param(snd_buf_size, uint, 0444);
-MODULE_PARM_DESC(snd_buf_size,
-                "Size of the buffers used for transmission (range: 0-"
-                __MODULE_STRING(UDSL_MAX_SND_BUF_SIZE) ", default: "
-                __MODULE_STRING(UDSL_DEFAULT_SND_BUF_SIZE) ")");
-
-/* ATM */
-
-static void udsl_atm_dev_close(struct atm_dev *dev);
-static int udsl_atm_open(struct atm_vcc *vcc);
-static void udsl_atm_close(struct atm_vcc *vcc);
-static int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg);
-static int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb);
-static int udsl_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page);
-
-static struct atmdev_ops udsl_atm_devops = {
-       .dev_close      = udsl_atm_dev_close,
-       .open           = udsl_atm_open,
-       .close          = udsl_atm_close,
-       .ioctl          = udsl_atm_ioctl,
-       .send           = udsl_atm_send,
-       .proc_read      = udsl_atm_proc_read,
-       .owner          = THIS_MODULE,
-};
-
-/***********
-**  misc  **
-***********/
-
-static inline void udsl_pop(struct atm_vcc *vcc, struct sk_buff *skb)
-{
-       if (vcc->pop)
-               vcc->pop(vcc, skb);
-       else
-               dev_kfree_skb(skb);
-}
-
-/*************
-**  decode  **
-*************/
-
-static inline struct udsl_vcc_data *udsl_find_vcc(struct udsl_instance_data *instance,
-                                                 short vpi, int vci)
-{
-       struct udsl_vcc_data *vcc;
-
-       list_for_each_entry(vcc, &instance->vcc_list, list)
-               if ((vcc->vci == vci) && (vcc->vpi == vpi))
-                       return vcc;
-       return NULL;
-}
-
-static void udsl_extract_cells(struct udsl_instance_data *instance,
-                              unsigned char *source, unsigned int howmany)
-{
-       struct udsl_vcc_data *cached_vcc = NULL;
-       struct atm_vcc *vcc;
-       struct sk_buff *sarb;
-       struct udsl_vcc_data *vcc_data;
-       int cached_vci = 0;
-       unsigned int i;
-       int pti;
-       int vci;
-       short cached_vpi = 0;
-       short vpi;
-
-       for (i = 0; i < howmany;
-            i++, source += ATM_CELL_SIZE + instance->rcv_padding) {
-               vpi = ((source[0] & 0x0f) << 4) | (source[1] >> 4);
-               vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4);
-               pti = (source[3] & 0x2) != 0;
-
-               vdbg("udsl_extract_cells: vpi %hd, vci %d, pti %d", vpi, vci, pti);
-
-               if (cached_vcc && (vci == cached_vci) && (vpi == cached_vpi))
-                       vcc_data = cached_vcc;
-               else if ((vcc_data = udsl_find_vcc(instance, vpi, vci))) {
-                       cached_vcc = vcc_data;
-                       cached_vpi = vpi;
-                       cached_vci = vci;
-               } else {
-                       dbg("udsl_extract_cells: unknown vpi/vci (%hd/%d)!", vpi, vci);
-                       continue;
-               }
-
-               vcc = vcc_data->vcc;
-               sarb = vcc_data->sarb;
-
-               if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) {
-                       dbg("udsl_extract_cells: buffer overrun (sarb->len %u, vcc: 0x%p)!", sarb->len, vcc);
-                       /* discard cells already received */
-                       skb_trim(sarb, 0);
-               }
-
-               memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
-               __skb_put(sarb, ATM_CELL_PAYLOAD);
-
-               if (pti) {
-                       struct sk_buff *skb;
-                       unsigned int length;
-                       unsigned int pdu_length;
-
-                       length = (source[ATM_CELL_SIZE - 6] << 8) + source[ATM_CELL_SIZE - 5];
-
-                       /* guard against overflow */
-                       if (length > ATM_MAX_AAL5_PDU) {
-                               dbg("udsl_extract_cells: bogus length %u (vcc: 0x%p)!", length, vcc);
-                               atomic_inc(&vcc->stats->rx_err);
-                               goto out;
-                       }
-
-                       pdu_length = UDSL_NUM_CELLS(length) * ATM_CELL_PAYLOAD;
-
-                       if (sarb->len < pdu_length) {
-                               dbg("udsl_extract_cells: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!", pdu_length, sarb->len, vcc);
-                               atomic_inc(&vcc->stats->rx_err);
-                               goto out;
-                       }
-
-                       if (crc32_be(~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) {
-                               dbg("udsl_extract_cells: packet failed crc check (vcc: 0x%p)!", vcc);
-                               atomic_inc(&vcc->stats->rx_err);
-                               goto out;
-                       }
-
-                       vdbg("udsl_extract_cells: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", length, pdu_length, vcc);
-
-                       if (!(skb = dev_alloc_skb(length))) {
-                               dbg("udsl_extract_cells: no memory for skb (length: %u)!", length);
-                               atomic_inc(&vcc->stats->rx_drop);
-                               goto out;
-                       }
-
-                       vdbg("udsl_extract_cells: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", skb, skb->truesize);
-
-                       if (!atm_charge(vcc, skb->truesize)) {
-                               dbg("udsl_extract_cells: failed atm_charge (skb->truesize: %u)!", skb->truesize);
-                               dev_kfree_skb(skb);
-                               goto out;       /* atm_charge increments rx_drop */
-                       }
-
-                       memcpy(skb->data, sarb->tail - pdu_length, length);
-                       __skb_put(skb, length);
-
-                       vdbg("udsl_extract_cells: sending skb 0x%p, skb->len %u, skb->truesize %u", skb, skb->len, skb->truesize);
-
-                       PACKETDEBUG(skb->data, skb->len);
-
-                       vcc->push(vcc, skb);
-
-                       atomic_inc(&vcc->stats->rx);
-               out:
-                       skb_trim(sarb, 0);
-               }
-       }
-}
-
-/*************
-**  encode  **
-*************/
-
-static inline void udsl_fill_cell_header(unsigned char *target, struct atm_vcc *vcc)
-{
-       target[0] = vcc->vpi >> 4;
-       target[1] = (vcc->vpi << 4) | (vcc->vci >> 12);
-       target[2] = vcc->vci >> 4;
-       target[3] = vcc->vci << 4;
-       target[4] = 0xec;
-}
-
-static const unsigned char zeros[ATM_CELL_PAYLOAD];
-
-static void udsl_groom_skb(struct atm_vcc *vcc, struct sk_buff *skb)
-{
-       struct udsl_control *ctrl = UDSL_SKB(skb);
-       unsigned int zero_padding;
-       u32 crc;
-
-       ctrl->atm_data.vcc = vcc;
-
-       ctrl->num_cells = UDSL_NUM_CELLS(skb->len);
-       ctrl->num_entire = skb->len / ATM_CELL_PAYLOAD;
-
-       zero_padding = ctrl->num_cells * ATM_CELL_PAYLOAD - skb->len - ATM_AAL5_TRAILER;
-
-       if (ctrl->num_entire + 1 < ctrl->num_cells)
-               ctrl->pdu_padding = zero_padding - (ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER);
-       else
-               ctrl->pdu_padding = zero_padding;
-
-       ctrl->aal5_trailer[0] = 0;      /* UU = 0 */
-       ctrl->aal5_trailer[1] = 0;      /* CPI = 0 */
-       ctrl->aal5_trailer[2] = skb->len >> 8;
-       ctrl->aal5_trailer[3] = skb->len;
-
-       crc = crc32_be(~0, skb->data, skb->len);
-       crc = crc32_be(crc, zeros, zero_padding);
-       crc = crc32_be(crc, ctrl->aal5_trailer, 4);
-       crc = ~crc;
-
-       ctrl->aal5_trailer[4] = crc >> 24;
-       ctrl->aal5_trailer[5] = crc >> 16;
-       ctrl->aal5_trailer[6] = crc >> 8;
-       ctrl->aal5_trailer[7] = crc;
-}
-
-static unsigned int udsl_write_cells(struct udsl_instance_data *instance,
-                                    unsigned int howmany, struct sk_buff *skb,
-                                    unsigned char **target_p)
-{
-       struct udsl_control *ctrl = UDSL_SKB(skb);
-       unsigned char *target = *target_p;
-       unsigned int nc, ne, i;
-
-       vdbg("udsl_write_cells: howmany=%u, skb->len=%d, num_cells=%u, num_entire=%u, pdu_padding=%u", howmany, skb->len, ctrl->num_cells, ctrl->num_entire, ctrl->pdu_padding);
-
-       nc = ctrl->num_cells;
-       ne = min(howmany, ctrl->num_entire);
-
-       for (i = 0; i < ne; i++) {
-               udsl_fill_cell_header(target, ctrl->atm_data.vcc);
-               target += ATM_CELL_HEADER;
-               memcpy(target, skb->data, ATM_CELL_PAYLOAD);
-               target += ATM_CELL_PAYLOAD;
-               if (instance->snd_padding) {
-                       memset(target, 0, instance->snd_padding);
-                       target += instance->snd_padding;
-               }
-               __skb_pull(skb, ATM_CELL_PAYLOAD);
-       }
-
-       ctrl->num_entire -= ne;
-
-       if (!(ctrl->num_cells -= ne) || !(howmany -= ne))
-               goto out;
-
-       udsl_fill_cell_header(target, ctrl->atm_data.vcc);
-       target += ATM_CELL_HEADER;
-       memcpy(target, skb->data, skb->len);
-       target += skb->len;
-       __skb_pull(skb, skb->len);
-       memset(target, 0, ctrl->pdu_padding);
-       target += ctrl->pdu_padding;
-
-       if (--ctrl->num_cells) {
-               if (!--howmany) {
-                       ctrl->pdu_padding = ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER;
-                       goto out;
-               }
-
-               if (instance->snd_padding) {
-                       memset(target, 0, instance->snd_padding);
-                       target += instance->snd_padding;
-               }
-               udsl_fill_cell_header(target, ctrl->atm_data.vcc);
-               target += ATM_CELL_HEADER;
-               memset(target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER);
-               target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER;
-
-               --ctrl->num_cells;
-               UDSL_ASSERT(!ctrl->num_cells);
-       }
-
-       memcpy(target, ctrl->aal5_trailer, ATM_AAL5_TRAILER);
-       target += ATM_AAL5_TRAILER;
-       /* set pti bit in last cell */
-       *(target + 3 - ATM_CELL_SIZE) |= 0x2;
-       if (instance->snd_padding) {
-               memset(target, 0, instance->snd_padding);
-               target += instance->snd_padding;
-       }
- out:
-       *target_p = target;
-       return nc - ctrl->num_cells;
-}
-
-/**************
-**  receive  **
-**************/
-
-static void udsl_complete_receive(struct urb *urb, struct pt_regs *regs)
-{
-       struct udsl_receive_buffer *buf;
-       struct udsl_instance_data *instance;
-       struct udsl_receiver *rcv;
-       unsigned long flags;
-
-       if (!urb || !(rcv = urb->context)) {
-               dbg("udsl_complete_receive: bad urb!");
-               return;
-       }
-
-       instance = rcv->instance;
-       buf = rcv->buffer;
-
-       buf->filled_cells = urb->actual_length / (ATM_CELL_SIZE + instance->rcv_padding);
-
-       vdbg("udsl_complete_receive: urb 0x%p, status %d, actual_length %d, filled_cells %u, rcv 0x%p, buf 0x%p", urb, urb->status, urb->actual_length, buf->filled_cells, rcv, buf);
-
-       UDSL_ASSERT(buf->filled_cells <= rcv_buf_size);
-
-       /* may not be in_interrupt() */
-       spin_lock_irqsave(&instance->receive_lock, flags);
-       list_add(&rcv->list, &instance->spare_receivers);
-       list_add_tail(&buf->list, &instance->filled_receive_buffers);
-       if (likely(!urb->status))
-               tasklet_schedule(&instance->receive_tasklet);
-       spin_unlock_irqrestore(&instance->receive_lock, flags);
-}
-
-static void udsl_process_receive(unsigned long data)
-{
-       struct udsl_receive_buffer *buf;
-       struct udsl_instance_data *instance = (struct udsl_instance_data *)data;
-       struct udsl_receiver *rcv;
-       int err;
-
- made_progress:
-       while (!list_empty(&instance->spare_receive_buffers)) {
-               spin_lock_irq(&instance->receive_lock);
-               if (list_empty(&instance->spare_receivers)) {
-                       spin_unlock_irq(&instance->receive_lock);
-                       break;
-               }
-               rcv = list_entry(instance->spare_receivers.next,
-                                struct udsl_receiver, list);
-               list_del(&rcv->list);
-               spin_unlock_irq(&instance->receive_lock);
-
-               buf = list_entry(instance->spare_receive_buffers.next,
-                                struct udsl_receive_buffer, list);
-               list_del(&buf->list);
-
-               rcv->buffer = buf;
-
-               usb_fill_bulk_urb(rcv->urb, instance->usb_dev,
-                                 usb_rcvbulkpipe(instance->usb_dev, instance->data_endpoint),
-                                 buf->base,
-                                 rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding),
-                                 udsl_complete_receive, rcv);
-
-               vdbg("udsl_process_receive: sending urb 0x%p, rcv 0x%p, buf 0x%p",
-                    rcv->urb, rcv, buf);
-
-               if ((err = usb_submit_urb(rcv->urb, GFP_ATOMIC)) < 0) {
-                       dbg("udsl_process_receive: urb submission failed (%d)!", err);
-                       list_add(&buf->list, &instance->spare_receive_buffers);
-                       spin_lock_irq(&instance->receive_lock);
-                       list_add(&rcv->list, &instance->spare_receivers);
-                       spin_unlock_irq(&instance->receive_lock);
-                       break;
-               }
-       }
-
-       spin_lock_irq(&instance->receive_lock);
-       if (list_empty(&instance->filled_receive_buffers)) {
-               spin_unlock_irq(&instance->receive_lock);
-               return;         /* done - no more buffers */
-       }
-       buf = list_entry(instance->filled_receive_buffers.next,
-                        struct udsl_receive_buffer, list);
-       list_del(&buf->list);
-       spin_unlock_irq(&instance->receive_lock);
-
-       vdbg("udsl_process_receive: processing buf 0x%p", buf);
-       udsl_extract_cells(instance, buf->base, buf->filled_cells);
-       list_add(&buf->list, &instance->spare_receive_buffers);
-       goto made_progress;
-}
-
-/***********
-**  send  **
-***********/
-
-static void udsl_complete_send(struct urb *urb, struct pt_regs *regs)
-{
-       struct udsl_instance_data *instance;
-       struct udsl_sender *snd;
-       unsigned long flags;
-
-       if (!urb || !(snd = urb->context) || !(instance = snd->instance)) {
-               dbg("udsl_complete_send: bad urb!");
-               return;
-       }
-
-       vdbg("udsl_complete_send: urb 0x%p, status %d, snd 0x%p, buf 0x%p", urb,
-            urb->status, snd, snd->buffer);
-
-       /* may not be in_interrupt() */
-       spin_lock_irqsave(&instance->send_lock, flags);
-       list_add(&snd->list, &instance->spare_senders);
-       list_add(&snd->buffer->list, &instance->spare_send_buffers);
-       tasklet_schedule(&instance->send_tasklet);
-       spin_unlock_irqrestore(&instance->send_lock, flags);
-}
-
-static void udsl_process_send(unsigned long data)
-{
-       struct udsl_send_buffer *buf;
-       struct udsl_instance_data *instance = (struct udsl_instance_data *)data;
-       struct sk_buff *skb;
-       struct udsl_sender *snd;
-       int err;
-       unsigned int num_written;
-
- made_progress:
-       spin_lock_irq(&instance->send_lock);
-       while (!list_empty(&instance->spare_senders)) {
-               if (!list_empty(&instance->filled_send_buffers)) {
-                       buf = list_entry(instance->filled_send_buffers.next,
-                                        struct udsl_send_buffer, list);
-                       list_del(&buf->list);
-               } else if ((buf = instance->current_buffer)) {
-                       instance->current_buffer = NULL;
-               } else          /* all buffers empty */
-                       break;
-
-               snd = list_entry(instance->spare_senders.next,
-                                struct udsl_sender, list);
-               list_del(&snd->list);
-               spin_unlock_irq(&instance->send_lock);
-
-               snd->buffer = buf;
-               usb_fill_bulk_urb(snd->urb, instance->usb_dev,
-                                 usb_sndbulkpipe(instance->usb_dev, instance->data_endpoint),
-                                 buf->base,
-                                 (snd_buf_size - buf->free_cells) * (ATM_CELL_SIZE + instance->snd_padding),
-                                 udsl_complete_send, snd);
-
-               vdbg("udsl_process_send: submitting urb 0x%p (%d cells), snd 0x%p, buf 0x%p",
-                    snd->urb, snd_buf_size - buf->free_cells, snd, buf);
-
-               if ((err = usb_submit_urb(snd->urb, GFP_ATOMIC)) < 0) {
-                       dbg("udsl_process_send: urb submission failed (%d)!", err);
-                       spin_lock_irq(&instance->send_lock);
-                       list_add(&snd->list, &instance->spare_senders);
-                       spin_unlock_irq(&instance->send_lock);
-                       list_add(&buf->list, &instance->filled_send_buffers);
-                       return; /* bail out */
-               }
-
-               spin_lock_irq(&instance->send_lock);
-       }                       /* while */
-       spin_unlock_irq(&instance->send_lock);
-
-       if (!instance->current_skb)
-               instance->current_skb = skb_dequeue(&instance->sndqueue);
-       if (!instance->current_skb)
-               return;         /* done - no more skbs */
-
-       skb = instance->current_skb;
-
-       if (!(buf = instance->current_buffer)) {
-               spin_lock_irq(&instance->send_lock);
-               if (list_empty(&instance->spare_send_buffers)) {
-                       instance->current_buffer = NULL;
-                       spin_unlock_irq(&instance->send_lock);
-                       return; /* done - no more buffers */
-               }
-               buf = list_entry(instance->spare_send_buffers.next,
-                              struct udsl_send_buffer, list);
-               list_del(&buf->list);
-               spin_unlock_irq(&instance->send_lock);
-
-               buf->free_start = buf->base;
-               buf->free_cells = snd_buf_size;
-
-               instance->current_buffer = buf;
-       }
-
-       num_written = udsl_write_cells(instance, buf->free_cells, skb, &buf->free_start);
-
-       vdbg("udsl_process_send: wrote %u cells from skb 0x%p to buffer 0x%p",
-            num_written, skb, buf);
-
-       if (!(buf->free_cells -= num_written)) {
-               list_add_tail(&buf->list, &instance->filled_send_buffers);
-               instance->current_buffer = NULL;
-       }
-
-       vdbg("udsl_process_send: buffer contains %d cells, %d left",
-            snd_buf_size - buf->free_cells, buf->free_cells);
-
-       if (!UDSL_SKB(skb)->num_cells) {
-               struct atm_vcc *vcc = UDSL_SKB(skb)->atm_data.vcc;
-
-               udsl_pop(vcc, skb);
-               instance->current_skb = NULL;
-
-               atomic_inc(&vcc->stats->tx);
-       }
-
-       goto made_progress;
-}
-
-static void udsl_cancel_send(struct udsl_instance_data *instance,
-                            struct atm_vcc *vcc)
-{
-       struct sk_buff *skb, *n;
-
-       dbg("udsl_cancel_send entered");
-       spin_lock_irq(&instance->sndqueue.lock);
-       for (skb = instance->sndqueue.next, n = skb->next;
-            skb != (struct sk_buff *)&instance->sndqueue;
-            skb = n, n = skb->next)
-               if (UDSL_SKB(skb)->atm_data.vcc == vcc) {
-                       dbg("udsl_cancel_send: popping skb 0x%p", skb);
-                       __skb_unlink(skb, &instance->sndqueue);
-                       udsl_pop(vcc, skb);
-               }
-       spin_unlock_irq(&instance->sndqueue.lock);
-
-       tasklet_disable(&instance->send_tasklet);
-       if ((skb = instance->current_skb) && (UDSL_SKB(skb)->atm_data.vcc == vcc)) {
-               dbg("udsl_cancel_send: popping current skb (0x%p)", skb);
-               instance->current_skb = NULL;
-               udsl_pop(vcc, skb);
-       }
-       tasklet_enable(&instance->send_tasklet);
-       dbg("udsl_cancel_send done");
-}
-
-static int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
-{
-       struct udsl_instance_data *instance = vcc->dev->dev_data;
-       int err;
-
-       vdbg("udsl_atm_send called (skb 0x%p, len %u)", skb, skb->len);
-
-       if (!instance) {
-               dbg("udsl_atm_send: NULL data!");
-               err = -ENODEV;
-               goto fail;
-       }
-
-       if (vcc->qos.aal != ATM_AAL5) {
-               dbg("udsl_atm_send: unsupported ATM type %d!", vcc->qos.aal);
-               err = -EINVAL;
-               goto fail;
-       }
-
-       if (skb->len > ATM_MAX_AAL5_PDU) {
-               dbg("udsl_atm_send: packet too long (%d vs %d)!", skb->len,
-                   ATM_MAX_AAL5_PDU);
-               err = -EINVAL;
-               goto fail;
-       }
-
-       PACKETDEBUG(skb->data, skb->len);
-
-       udsl_groom_skb(vcc, skb);
-       skb_queue_tail(&instance->sndqueue, skb);
-       tasklet_schedule(&instance->send_tasklet);
-
-       return 0;
-
- fail:
-       udsl_pop(vcc, skb);
-       return err;
-}
-
-/********************
-**  bean counting  **
-********************/
-
-static void udsl_destroy_instance(struct kref *kref)
-{
-       struct udsl_instance_data *instance =
-           container_of(kref, struct udsl_instance_data, refcount);
-
-       tasklet_kill(&instance->receive_tasklet);
-       tasklet_kill(&instance->send_tasklet);
-       usb_put_dev(instance->usb_dev);
-       kfree(instance);
-}
-
-void udsl_get_instance(struct udsl_instance_data *instance)
-{
-       kref_get(&instance->refcount);
-}
-
-void udsl_put_instance(struct udsl_instance_data *instance)
-{
-       kref_put(&instance->refcount, udsl_destroy_instance);
-}
-
-/**********
-**  ATM  **
-**********/
-
-static void udsl_atm_dev_close(struct atm_dev *dev)
-{
-       struct udsl_instance_data *instance = dev->dev_data;
-
-       dev->dev_data = NULL;
-       udsl_put_instance(instance);
-}
-
-static int udsl_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page)
-{
-       struct udsl_instance_data *instance = atm_dev->dev_data;
-       int left = *pos;
-
-       if (!instance) {
-               dbg("udsl_atm_proc_read: NULL instance!");
-               return -ENODEV;
-       }
-
-       if (!left--)
-               return sprintf(page, "%s\n", instance->description);
-
-       if (!left--)
-               return sprintf(page, "MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
-                              atm_dev->esi[0], atm_dev->esi[1],
-                              atm_dev->esi[2], atm_dev->esi[3],
-                              atm_dev->esi[4], atm_dev->esi[5]);
-
-       if (!left--)
-               return sprintf(page,
-                              "AAL5: tx %d ( %d err ), rx %d ( %d err, %d drop )\n",
-                              atomic_read(&atm_dev->stats.aal5.tx),
-                              atomic_read(&atm_dev->stats.aal5.tx_err),
-                              atomic_read(&atm_dev->stats.aal5.rx),
-                              atomic_read(&atm_dev->stats.aal5.rx_err),
-                              atomic_read(&atm_dev->stats.aal5.rx_drop));
-
-       if (!left--) {
-               switch (atm_dev->signal) {
-               case ATM_PHY_SIG_FOUND:
-                       sprintf(page, "Line up");
-                       break;
-               case ATM_PHY_SIG_LOST:
-                       sprintf(page, "Line down");
-                       break;
-               default:
-                       sprintf(page, "Line state unknown");
-                       break;
-               }
-
-               if (instance->usb_dev->state == USB_STATE_NOTATTACHED)
-                       strcat(page, ", disconnected\n");
-               else {
-                       if (instance->status == UDSL_LOADED_FIRMWARE)
-                               strcat(page, ", firmware loaded\n");
-                       else if (instance->status == UDSL_LOADING_FIRMWARE)
-                               strcat(page, ", firmware loading\n");
-                       else
-                               strcat(page, ", no firmware\n");
-               }
-
-               return strlen(page);
-       }
-
-       return 0;
-}
-
-static int udsl_atm_open(struct atm_vcc *vcc)
-{
-       struct udsl_instance_data *instance = vcc->dev->dev_data;
-       struct udsl_vcc_data *new;
-       unsigned int max_pdu;
-       int vci = vcc->vci;
-       short vpi = vcc->vpi;
-       int err;
-
-       dbg("udsl_atm_open: vpi %hd, vci %d", vpi, vci);
-
-       if (!instance) {
-               dbg("udsl_atm_open: NULL data!");
-               return -ENODEV;
-       }
-
-       /* only support AAL5 */
-       if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0)
-           || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) {
-               dbg("udsl_atm_open: unsupported ATM type %d!", vcc->qos.aal);
-               return -EINVAL;
-       }
-
-       if (instance->firmware_wait &&
-           (err = instance->firmware_wait(instance)) < 0) {
-               dbg("udsl_atm_open: firmware not loaded (%d)!", err);
-               return err;
-       }
-
-       down(&instance->serialize);     /* vs self, udsl_atm_close */
-
-       if (udsl_find_vcc(instance, vpi, vci)) {
-               dbg("udsl_atm_open: %hd/%d already in use!", vpi, vci);
-               up(&instance->serialize);
-               return -EADDRINUSE;
-       }
-
-       if (!(new = kmalloc(sizeof(struct udsl_vcc_data), GFP_KERNEL))) {
-               dbg("udsl_atm_open: no memory for vcc_data!");
-               up(&instance->serialize);
-               return -ENOMEM;
-       }
-
-       memset(new, 0, sizeof(struct udsl_vcc_data));
-       new->vcc = vcc;
-       new->vpi = vpi;
-       new->vci = vci;
-
-       /* udsl_extract_cells requires at least one cell */
-       max_pdu = max(1, UDSL_NUM_CELLS(vcc->qos.rxtp.max_sdu)) * ATM_CELL_PAYLOAD;
-       if (!(new->sarb = alloc_skb(max_pdu, GFP_KERNEL))) {
-               dbg("udsl_atm_open: no memory for SAR buffer!");
-               kfree(new);
-               up(&instance->serialize);
-               return -ENOMEM;
-       }
-
-       vcc->dev_data = new;
-
-       tasklet_disable(&instance->receive_tasklet);
-       list_add(&new->list, &instance->vcc_list);
-       tasklet_enable(&instance->receive_tasklet);
-
-       set_bit(ATM_VF_ADDR, &vcc->flags);
-       set_bit(ATM_VF_PARTIAL, &vcc->flags);
-       set_bit(ATM_VF_READY, &vcc->flags);
-
-       up(&instance->serialize);
-
-       tasklet_schedule(&instance->receive_tasklet);
-
-       dbg("udsl_atm_open: allocated vcc data 0x%p (max_pdu: %u)", new, max_pdu);
-
-       return 0;
-}
-
-static void udsl_atm_close(struct atm_vcc *vcc)
-{
-       struct udsl_instance_data *instance = vcc->dev->dev_data;
-       struct udsl_vcc_data *vcc_data = vcc->dev_data;
-
-       dbg("udsl_atm_close called");
-
-       if (!instance || !vcc_data) {
-               dbg("udsl_atm_close: NULL data!");
-               return;
-       }
-
-       dbg("udsl_atm_close: deallocating vcc 0x%p with vpi %d vci %d",
-           vcc_data, vcc_data->vpi, vcc_data->vci);
-
-       udsl_cancel_send(instance, vcc);
-
-       down(&instance->serialize);     /* vs self, udsl_atm_open */
-
-       tasklet_disable(&instance->receive_tasklet);
-       list_del(&vcc_data->list);
-       tasklet_enable(&instance->receive_tasklet);
-
-       kfree_skb(vcc_data->sarb);
-       vcc_data->sarb = NULL;
-
-       kfree(vcc_data);
-       vcc->dev_data = NULL;
-
-       vcc->vpi = ATM_VPI_UNSPEC;
-       vcc->vci = ATM_VCI_UNSPEC;
-       clear_bit(ATM_VF_READY, &vcc->flags);
-       clear_bit(ATM_VF_PARTIAL, &vcc->flags);
-       clear_bit(ATM_VF_ADDR, &vcc->flags);
-
-       up(&instance->serialize);
-
-       dbg("udsl_atm_close successful");
-}
-
-static int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd,
-                         void __user * arg)
-{
-       switch (cmd) {
-       case ATM_QUERYLOOP:
-               return put_user(ATM_LM_NONE, (int __user *)arg) ? -EFAULT : 0;
-       default:
-               return -ENOIOCTLCMD;
-       }
-}
-
-/**********
-**  USB  **
-**********/
-
-int udsl_instance_setup(struct usb_device *dev,
-                       struct udsl_instance_data *instance)
-{
-       char *buf;
-       int i, length;
-
-       kref_init(&instance->refcount); /* one for USB */
-       udsl_get_instance(instance);    /* one for ATM */
-
-       init_MUTEX(&instance->serialize);
-
-       instance->usb_dev = dev;
-
-       INIT_LIST_HEAD(&instance->vcc_list);
-
-       instance->status = UDSL_NO_FIRMWARE;
-       init_waitqueue_head(&instance->firmware_waiters);
-
-       spin_lock_init(&instance->receive_lock);
-       INIT_LIST_HEAD(&instance->spare_receivers);
-       INIT_LIST_HEAD(&instance->filled_receive_buffers);
-
-       tasklet_init(&instance->receive_tasklet, udsl_process_receive, (unsigned long)instance);
-       INIT_LIST_HEAD(&instance->spare_receive_buffers);
-
-       skb_queue_head_init(&instance->sndqueue);
-
-       spin_lock_init(&instance->send_lock);
-       INIT_LIST_HEAD(&instance->spare_senders);
-       INIT_LIST_HEAD(&instance->spare_send_buffers);
-
-       tasklet_init(&instance->send_tasklet, udsl_process_send,
-                    (unsigned long)instance);
-       INIT_LIST_HEAD(&instance->filled_send_buffers);
-
-       /* receive init */
-       for (i = 0; i < num_rcv_urbs; i++) {
-               struct udsl_receiver *rcv = &(instance->receivers[i]);
-
-               if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
-                       dbg("udsl_usb_probe: no memory for receive urb %d!", i);
-                       goto fail;
-               }
-
-               rcv->instance = instance;
-
-               list_add(&rcv->list, &instance->spare_receivers);
-       }
-
-       for (i = 0; i < num_rcv_bufs; i++) {
-               struct udsl_receive_buffer *buf =
-                   &(instance->receive_buffers[i]);
-
-               buf->base = kmalloc(rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding),
-                                   GFP_KERNEL);
-               if (!buf->base) {
-                       dbg("udsl_usb_probe: no memory for receive buffer %d!", i);
-                       goto fail;
-               }
-
-               list_add(&buf->list, &instance->spare_receive_buffers);
-       }
-
-       /* send init */
-       for (i = 0; i < num_snd_urbs; i++) {
-               struct udsl_sender *snd = &(instance->senders[i]);
-
-               if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) {
-                       dbg("udsl_usb_probe: no memory for send urb %d!", i);
-                       goto fail;
-               }
-
-               snd->instance = instance;
-
-               list_add(&snd->list, &instance->spare_senders);
-       }
-
-       for (i = 0; i < num_snd_bufs; i++) {
-               struct udsl_send_buffer *buf = &(instance->send_buffers[i]);
-
-               buf->base = kmalloc(snd_buf_size * (ATM_CELL_SIZE + instance->snd_padding),
-                                   GFP_KERNEL);
-               if (!buf->base) {
-                       dbg("udsl_usb_probe: no memory for send buffer %d!", i);
-                       goto fail;
-               }
-
-               list_add(&buf->list, &instance->spare_send_buffers);
-       }
-
-       /* ATM init */
-       instance->atm_dev = atm_dev_register(instance->driver_name,
-                                            &udsl_atm_devops, -1, NULL);
-       if (!instance->atm_dev) {
-               dbg("udsl_usb_probe: failed to register ATM device!");
-               goto fail;
-       }
-
-       instance->atm_dev->ci_range.vpi_bits = ATM_CI_MAX;
-       instance->atm_dev->ci_range.vci_bits = ATM_CI_MAX;
-       instance->atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
-
-       /* temp init ATM device, set to 128kbit */
-       instance->atm_dev->link_rate = 128 * 1000 / 424;
-
-       /* device description */
-       buf = instance->description;
-       length = sizeof(instance->description);
-
-       if ((i = usb_string(dev, dev->descriptor.iProduct, buf, length)) < 0)
-               goto finish;
-
-       buf += i;
-       length -= i;
-
-       i = scnprintf(buf, length, " (");
-       buf += i;
-       length -= i;
-
-       if (length <= 0 || (i = usb_make_path(dev, buf, length)) < 0)
-               goto finish;
-
-       buf += i;
-       length -= i;
-
-       snprintf(buf, length, ")");
-
- finish:
-       /* ready for ATM callbacks */
-       wmb();
-       instance->atm_dev->dev_data = instance;
-
-       usb_get_dev(dev);
-
-       return 0;
-
- fail:
-       for (i = 0; i < num_snd_bufs; i++)
-               kfree(instance->send_buffers[i].base);
-
-       for (i = 0; i < num_snd_urbs; i++)
-               usb_free_urb(instance->senders[i].urb);
-
-       for (i = 0; i < num_rcv_bufs; i++)
-               kfree(instance->receive_buffers[i].base);
-
-       for (i = 0; i < num_rcv_urbs; i++)
-               usb_free_urb(instance->receivers[i].urb);
-
-       return -ENOMEM;
-}
-
-void udsl_instance_disconnect(struct udsl_instance_data *instance)
-{
-       int i;
-
-       dbg("udsl_instance_disconnect entered");
-
-       if (!instance) {
-               dbg("udsl_instance_disconnect: NULL instance!");
-               return;
-       }
-
-       /* receive finalize */
-       tasklet_disable(&instance->receive_tasklet);
-
-       for (i = 0; i < num_rcv_urbs; i++)
-               usb_kill_urb(instance->receivers[i].urb);
-
-       /* no need to take the spinlock */
-       INIT_LIST_HEAD(&instance->filled_receive_buffers);
-       INIT_LIST_HEAD(&instance->spare_receive_buffers);
-
-       tasklet_enable(&instance->receive_tasklet);
-
-       for (i = 0; i < num_rcv_urbs; i++)
-               usb_free_urb(instance->receivers[i].urb);
-
-       for (i = 0; i < num_rcv_bufs; i++)
-               kfree(instance->receive_buffers[i].base);
-
-       /* send finalize */
-       tasklet_disable(&instance->send_tasklet);
-
-       for (i = 0; i < num_snd_urbs; i++)
-               usb_kill_urb(instance->senders[i].urb);
-
-       /* no need to take the spinlock */
-       INIT_LIST_HEAD(&instance->spare_senders);
-       INIT_LIST_HEAD(&instance->spare_send_buffers);
-       instance->current_buffer = NULL;
-
-       tasklet_enable(&instance->send_tasklet);
-
-       for (i = 0; i < num_snd_urbs; i++)
-               usb_free_urb(instance->senders[i].urb);
-
-       for (i = 0; i < num_snd_bufs; i++)
-               kfree(instance->send_buffers[i].base);
-
-       /* ATM finalize */
-       shutdown_atm_dev(instance->atm_dev);
-}
-
-EXPORT_SYMBOL_GPL(udsl_get_instance);
-EXPORT_SYMBOL_GPL(udsl_put_instance);
-EXPORT_SYMBOL_GPL(udsl_instance_setup);
-EXPORT_SYMBOL_GPL(udsl_instance_disconnect);
-
-/***********
-**  init  **
-***********/
-
-static int __init udsl_usb_init(void)
-{
-       dbg("udsl_usb_init: driver version " DRIVER_VERSION);
-
-       if (sizeof(struct udsl_control) > sizeof(((struct sk_buff *) 0)->cb)) {
-               printk(KERN_ERR __FILE__ ": unusable with this kernel!\n");
-               return -EIO;
-       }
-
-       if ((num_rcv_urbs > UDSL_MAX_RCV_URBS)
-           || (num_snd_urbs > UDSL_MAX_SND_URBS)
-           || (num_rcv_bufs > UDSL_MAX_RCV_BUFS)
-           || (num_snd_bufs > UDSL_MAX_SND_BUFS)
-           || (rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE)
-           || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE))
-               return -EINVAL;
-
-       return 0;
-}
-
-static void __exit udsl_usb_exit(void)
-{
-}
-
-module_init(udsl_usb_init);
-module_exit(udsl_usb_exit);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-MODULE_VERSION(DRIVER_VERSION);
-
-/************
-**  debug  **
-************/
-
-#ifdef VERBOSE_DEBUG
-static int udsl_print_packet(const unsigned char *data, int len)
-{
-       unsigned char buffer[256];
-       int i = 0, j = 0;
-
-       for (i = 0; i < len;) {
-               buffer[0] = '\0';
-               sprintf(buffer, "%.3d :", i);
-               for (j = 0; (j < 16) && (i < len); j++, i++) {
-                       sprintf(buffer, "%s %2.2x", buffer, data[i]);
-               }
-               dbg("%s", buffer);
-       }
-       return i;
-}
-#endif
diff --git a/drivers/usb/atm/usb_atm.h b/drivers/usb/atm/usb_atm.h
deleted file mode 100644 (file)
index cf8c532..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-/******************************************************************************
- *  usb_atm.h - Generic USB xDSL driver core
- *
- *  Copyright (C) 2001, Alcatel
- *  Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas
- *  Copyright (C) 2004, David Woodhouse
- *
- *  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., 59
- *  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- *
- ******************************************************************************/
-
-#include <linux/config.h>
-#include <linux/list.h>
-#include <linux/kref.h>
-#include <linux/atm.h>
-#include <linux/atmdev.h>
-#include <asm/semaphore.h>
-
-/*
-#define DEBUG
-#define VERBOSE_DEBUG
-*/
-
-#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)
-#      define DEBUG
-#endif
-
-#include <linux/usb.h>
-
-#ifdef DEBUG
-#define UDSL_ASSERT(x) BUG_ON(!(x))
-#else
-#define UDSL_ASSERT(x) do { if (!(x)) warn("failed assertion '" #x "' at line %d", __LINE__); } while(0)
-#endif
-
-#define UDSL_MAX_RCV_URBS              4
-#define UDSL_MAX_SND_URBS              4
-#define UDSL_MAX_RCV_BUFS              8
-#define UDSL_MAX_SND_BUFS              8
-#define UDSL_MAX_RCV_BUF_SIZE          1024    /* ATM cells */
-#define UDSL_MAX_SND_BUF_SIZE          1024    /* ATM cells */
-#define UDSL_DEFAULT_RCV_URBS          2
-#define UDSL_DEFAULT_SND_URBS          2
-#define UDSL_DEFAULT_RCV_BUFS          4
-#define UDSL_DEFAULT_SND_BUFS          4
-#define UDSL_DEFAULT_RCV_BUF_SIZE      64      /* ATM cells */
-#define UDSL_DEFAULT_SND_BUF_SIZE      64      /* ATM cells */
-
-#define ATM_CELL_HEADER                        (ATM_CELL_SIZE - ATM_CELL_PAYLOAD)
-#define UDSL_NUM_CELLS(x)              (((x) + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD)
-
-/* receive */
-
-struct udsl_receive_buffer {
-       struct list_head list;
-       unsigned char *base;
-       unsigned int filled_cells;
-};
-
-struct udsl_receiver {
-       struct list_head list;
-       struct udsl_receive_buffer *buffer;
-       struct urb *urb;
-       struct udsl_instance_data *instance;
-};
-
-struct udsl_vcc_data {
-       /* vpi/vci lookup */
-       struct list_head list;
-       short vpi;
-       int vci;
-       struct atm_vcc *vcc;
-
-       /* raw cell reassembly */
-       struct sk_buff *sarb;
-};
-
-/* send */
-
-struct udsl_send_buffer {
-       struct list_head list;
-       unsigned char *base;
-       unsigned char *free_start;
-       unsigned int free_cells;
-};
-
-struct udsl_sender {
-       struct list_head list;
-       struct udsl_send_buffer *buffer;
-       struct urb *urb;
-       struct udsl_instance_data *instance;
-};
-
-struct udsl_control {
-       struct atm_skb_data atm_data;
-       unsigned int num_cells;
-       unsigned int num_entire;
-       unsigned int pdu_padding;
-       unsigned char aal5_trailer[ATM_AAL5_TRAILER];
-};
-
-#define UDSL_SKB(x)            ((struct udsl_control *)(x)->cb)
-
-/* main driver data */
-
-enum udsl_status {
-       UDSL_NO_FIRMWARE,
-       UDSL_LOADING_FIRMWARE,
-       UDSL_LOADED_FIRMWARE
-};
-
-struct udsl_instance_data {
-       struct kref refcount;
-       struct semaphore serialize;
-
-       /* USB device part */
-       struct usb_device *usb_dev;
-       char description[64];
-       int data_endpoint;
-       int snd_padding;
-       int rcv_padding;
-       const char *driver_name;
-
-       /* ATM device part */
-       struct atm_dev *atm_dev;
-       struct list_head vcc_list;
-
-       /* firmware */
-       int (*firmware_wait) (struct udsl_instance_data *);
-       enum udsl_status status;
-       wait_queue_head_t firmware_waiters;
-
-       /* receive */
-       struct udsl_receiver receivers[UDSL_MAX_RCV_URBS];
-       struct udsl_receive_buffer receive_buffers[UDSL_MAX_RCV_BUFS];
-
-       spinlock_t receive_lock;
-       struct list_head spare_receivers;
-       struct list_head filled_receive_buffers;
-
-       struct tasklet_struct receive_tasklet;
-       struct list_head spare_receive_buffers;
-
-       /* send */
-       struct udsl_sender senders[UDSL_MAX_SND_URBS];
-       struct udsl_send_buffer send_buffers[UDSL_MAX_SND_BUFS];
-
-       struct sk_buff_head sndqueue;
-
-       spinlock_t send_lock;
-       struct list_head spare_senders;
-       struct list_head spare_send_buffers;
-
-       struct tasklet_struct send_tasklet;
-       struct sk_buff *current_skb;                    /* being emptied */
-       struct udsl_send_buffer *current_buffer;        /* being filled */
-       struct list_head filled_send_buffers;
-};
-
-extern int udsl_instance_setup(struct usb_device *dev,
-                              struct udsl_instance_data *instance);
-extern void udsl_instance_disconnect(struct udsl_instance_data *instance);
-extern void udsl_get_instance(struct udsl_instance_data *instance);
-extern void udsl_put_instance(struct udsl_instance_data *instance);
diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c
new file mode 100644 (file)
index 0000000..bb1db19
--- /dev/null
@@ -0,0 +1,1230 @@
+/******************************************************************************
+ *  usbatm.c - Generic USB xDSL driver core
+ *
+ *  Copyright (C) 2001, Alcatel
+ *  Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas
+ *  Copyright (C) 2004, David Woodhouse, Roman Kagan
+ *
+ *  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., 59
+ *  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ ******************************************************************************/
+
+/*
+ *  Written by Johan Verrept, Duncan Sands (duncan.sands@free.fr) and David Woodhouse
+ *
+ *  1.7+:      - See the check-in logs
+ *
+ *  1.6:       - No longer opens a connection if the firmware is not loaded
+ *             - Added support for the speedtouch 330
+ *             - Removed the limit on the number of devices
+ *             - Module now autoloads on device plugin
+ *             - Merged relevant parts of sarlib
+ *             - Replaced the kernel thread with a tasklet
+ *             - New packet transmission code
+ *             - Changed proc file contents
+ *             - Fixed all known SMP races
+ *             - Many fixes and cleanups
+ *             - Various fixes by Oliver Neukum (oliver@neukum.name)
+ *
+ *  1.5A:      - Version for inclusion in 2.5 series kernel
+ *             - Modifications by Richard Purdie (rpurdie@rpsys.net)
+ *             - made compatible with kernel 2.5.6 onwards by changing
+ *             usbatm_usb_send_data_context->urb to a pointer and adding code
+ *             to alloc and free it
+ *             - remove_wait_queue() added to usbatm_atm_processqueue_thread()
+ *
+ *  1.5:       - fixed memory leak when atmsar_decode_aal5 returned NULL.
+ *             (reported by stephen.robinson@zen.co.uk)
+ *
+ *  1.4:       - changed the spin_lock() under interrupt to spin_lock_irqsave()
+ *             - unlink all active send urbs of a vcc that is being closed.
+ *
+ *  1.3.1:     - added the version number
+ *
+ *  1.3:       - Added multiple send urb support
+ *             - fixed memory leak and vcc->tx_inuse starvation bug
+ *               when not enough memory left in vcc.
+ *
+ *  1.2:       - Fixed race condition in usbatm_usb_send_data()
+ *  1.1:       - Turned off packet debugging
+ *
+ */
+
+#include "usbatm.h"
+
+#include <asm/uaccess.h>
+#include <linux/crc32.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/stat.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+
+#ifdef VERBOSE_DEBUG
+static int usbatm_print_packet(const unsigned char *data, int len);
+#define PACKETDEBUG(arg...)    usbatm_print_packet (arg)
+#define vdbg(arg...)           dbg (arg)
+#else
+#define PACKETDEBUG(arg...)
+#define vdbg(arg...)
+#endif
+
+#define DRIVER_AUTHOR  "Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
+#define DRIVER_VERSION "1.9"
+#define DRIVER_DESC    "Generic USB ATM/DSL I/O, version " DRIVER_VERSION
+
+static const char usbatm_driver_name[] = "usbatm";
+
+#define UDSL_MAX_RCV_URBS              16
+#define UDSL_MAX_SND_URBS              16
+#define UDSL_MAX_RCV_BUF_SIZE          1024    /* ATM cells */
+#define UDSL_MAX_SND_BUF_SIZE          1024    /* ATM cells */
+#define UDSL_DEFAULT_RCV_URBS          4
+#define UDSL_DEFAULT_SND_URBS          4
+#define UDSL_DEFAULT_RCV_BUF_SIZE      64      /* ATM cells */
+#define UDSL_DEFAULT_SND_BUF_SIZE      64      /* ATM cells */
+
+#define ATM_CELL_HEADER                        (ATM_CELL_SIZE - ATM_CELL_PAYLOAD)
+
+#define THROTTLE_MSECS                 100     /* delay to recover processing after urb submission fails */
+
+static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS;
+static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS;
+static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE;
+static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE;
+
+module_param(num_rcv_urbs, uint, S_IRUGO);
+MODULE_PARM_DESC(num_rcv_urbs,
+                "Number of urbs used for reception (range: 0-"
+                __MODULE_STRING(UDSL_MAX_RCV_URBS) ", default: "
+                __MODULE_STRING(UDSL_DEFAULT_RCV_URBS) ")");
+
+module_param(num_snd_urbs, uint, S_IRUGO);
+MODULE_PARM_DESC(num_snd_urbs,
+                "Number of urbs used for transmission (range: 0-"
+                __MODULE_STRING(UDSL_MAX_SND_URBS) ", default: "
+                __MODULE_STRING(UDSL_DEFAULT_SND_URBS) ")");
+
+module_param(rcv_buf_size, uint, S_IRUGO);
+MODULE_PARM_DESC(rcv_buf_size,
+                "Size of the buffers used for reception in ATM cells (range: 1-"
+                __MODULE_STRING(UDSL_MAX_RCV_BUF_SIZE) ", default: "
+                __MODULE_STRING(UDSL_DEFAULT_RCV_BUF_SIZE) ")");
+
+module_param(snd_buf_size, uint, S_IRUGO);
+MODULE_PARM_DESC(snd_buf_size,
+                "Size of the buffers used for transmission in ATM cells (range: 1-"
+                __MODULE_STRING(UDSL_MAX_SND_BUF_SIZE) ", default: "
+                __MODULE_STRING(UDSL_DEFAULT_SND_BUF_SIZE) ")");
+
+
+/* receive */
+
+struct usbatm_vcc_data {
+       /* vpi/vci lookup */
+       struct list_head list;
+       short vpi;
+       int vci;
+       struct atm_vcc *vcc;
+
+       /* raw cell reassembly */
+       struct sk_buff *sarb;
+};
+
+
+/* send */
+
+struct usbatm_control {
+       struct atm_skb_data atm;
+       u32 len;
+       u32 crc;
+};
+
+#define UDSL_SKB(x)            ((struct usbatm_control *)(x)->cb)
+
+
+/* ATM */
+
+static void usbatm_atm_dev_close(struct atm_dev *dev);
+static int usbatm_atm_open(struct atm_vcc *vcc);
+static void usbatm_atm_close(struct atm_vcc *vcc);
+static int usbatm_atm_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg);
+static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb);
+static int usbatm_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page);
+
+static struct atmdev_ops usbatm_atm_devops = {
+       .dev_close      = usbatm_atm_dev_close,
+       .open           = usbatm_atm_open,
+       .close          = usbatm_atm_close,
+       .ioctl          = usbatm_atm_ioctl,
+       .send           = usbatm_atm_send,
+       .proc_read      = usbatm_atm_proc_read,
+       .owner          = THIS_MODULE,
+};
+
+
+/***********
+**  misc  **
+***********/
+
+static inline unsigned int usbatm_pdu_length(unsigned int length)
+{
+       length += ATM_CELL_PAYLOAD - 1 + ATM_AAL5_TRAILER;
+       return length - length % ATM_CELL_PAYLOAD;
+}
+
+static inline void usbatm_pop(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+       if (vcc->pop)
+               vcc->pop(vcc, skb);
+       else
+               dev_kfree_skb(skb);
+}
+
+
+/***********
+**  urbs  **
+************/
+
+static inline struct urb *usbatm_pop_urb(struct usbatm_channel *channel)
+{
+       struct urb *urb;
+
+       spin_lock_irq(&channel->lock);
+       if (list_empty(&channel->list)) {
+               spin_unlock_irq(&channel->lock);
+               return NULL;
+       }
+
+       urb = list_entry(channel->list.next, struct urb, urb_list);
+       list_del(&urb->urb_list);
+       spin_unlock_irq(&channel->lock);
+
+       return urb;
+}
+
+static inline int usbatm_submit_urb(struct urb *urb)
+{
+       struct usbatm_channel *channel = urb->context;
+       int ret;
+
+       vdbg("%s: submitting urb 0x%p, size %u",
+            __func__, urb, urb->transfer_buffer_length);
+
+       ret = usb_submit_urb(urb, GFP_ATOMIC);
+       if (ret) {
+               atm_dbg(channel->usbatm, "%s: urb 0x%p submission failed (%d)!\n",
+                       __func__, urb, ret);
+
+               /* consider all errors transient and return the buffer back to the queue */
+               urb->status = -EAGAIN;
+               spin_lock_irq(&channel->lock);
+
+               /* must add to the front when sending; doesn't matter when receiving */
+               list_add(&urb->urb_list, &channel->list);
+
+               spin_unlock_irq(&channel->lock);
+
+               /* make sure the channel doesn't stall */
+               mod_timer(&channel->delay, jiffies + msecs_to_jiffies(THROTTLE_MSECS));
+       }
+
+       return ret;
+}
+
+static void usbatm_complete(struct urb *urb, struct pt_regs *regs)
+{
+       struct usbatm_channel *channel = urb->context;
+       unsigned long flags;
+
+       vdbg("%s: urb 0x%p, status %d, actual_length %d",
+            __func__, urb, urb->status, urb->actual_length);
+
+       /* usually in_interrupt(), but not always */
+       spin_lock_irqsave(&channel->lock, flags);
+
+       /* must add to the back when receiving; doesn't matter when sending */
+       list_add_tail(&urb->urb_list, &channel->list);
+
+       spin_unlock_irqrestore(&channel->lock, flags);
+
+       if (unlikely(urb->status))
+               /* throttle processing in case of an error */
+               mod_timer(&channel->delay, jiffies + msecs_to_jiffies(THROTTLE_MSECS));
+       else
+               tasklet_schedule(&channel->tasklet);
+}
+
+
+/*************
+**  decode  **
+*************/
+
+static inline struct usbatm_vcc_data *usbatm_find_vcc(struct usbatm_data *instance,
+                                                 short vpi, int vci)
+{
+       struct usbatm_vcc_data *vcc;
+
+       list_for_each_entry(vcc, &instance->vcc_list, list)
+               if ((vcc->vci == vci) && (vcc->vpi == vpi))
+                       return vcc;
+       return NULL;
+}
+
+static void usbatm_extract_cells(struct usbatm_data *instance,
+                              unsigned char *source, unsigned int avail_data)
+{
+       struct usbatm_vcc_data *cached_vcc = NULL;
+       struct atm_vcc *vcc;
+       struct sk_buff *sarb;
+       unsigned int stride = instance->rx_channel.stride;
+       int vci, cached_vci = 0;
+       short vpi, cached_vpi = 0;
+       u8 pti;
+
+       for (; avail_data >= stride; avail_data -= stride, source += stride) {
+               vpi = ((source[0] & 0x0f) << 4)  | (source[1] >> 4);
+               vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4);
+               pti = ((source[3] & 0xe) >> 1);
+
+               vdbg("%s: vpi %hd, vci %d, pti %d", __func__, vpi, vci, pti);
+
+               if ((vci != cached_vci) || (vpi != cached_vpi)) {
+                       cached_vpi = vpi;
+                       cached_vci = vci;
+
+                       cached_vcc = usbatm_find_vcc(instance, vpi, vci);
+
+                       if (!cached_vcc)
+                               atm_dbg(instance, "%s: unknown vpi/vci (%hd/%d)!\n", __func__, vpi, vci);
+               }
+
+               if (!cached_vcc)
+                       continue;
+
+               vcc = cached_vcc->vcc;
+
+               /* OAM F5 end-to-end */
+               if (pti == ATM_PTI_E2EF5) {
+                       atm_warn(instance, "%s: OAM not supported (vpi %d, vci %d)!\n", __func__, vpi, vci);
+                       atomic_inc(&vcc->stats->rx_err);
+                       continue;
+               }
+
+               sarb = cached_vcc->sarb;
+
+               if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) {
+                       atm_dbg(instance, "%s: buffer overrun (sarb->len %u, vcc: 0x%p)!\n",
+                                       __func__, sarb->len, vcc);
+                       /* discard cells already received */
+                       skb_trim(sarb, 0);
+                       UDSL_ASSERT(sarb->tail + ATM_CELL_PAYLOAD <= sarb->end);
+               }
+
+               memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
+               __skb_put(sarb, ATM_CELL_PAYLOAD);
+
+               if (pti & 1) {
+                       struct sk_buff *skb;
+                       unsigned int length;
+                       unsigned int pdu_length;
+
+                       length = (source[ATM_CELL_SIZE - 6] << 8) + source[ATM_CELL_SIZE - 5];
+
+                       /* guard against overflow */
+                       if (length > ATM_MAX_AAL5_PDU) {
+                               atm_dbg(instance, "%s: bogus length %u (vcc: 0x%p)!\n",
+                                               __func__, length, vcc);
+                               atomic_inc(&vcc->stats->rx_err);
+                               goto out;
+                       }
+
+                       pdu_length = usbatm_pdu_length(length);
+
+                       if (sarb->len < pdu_length) {
+                               atm_dbg(instance, "%s: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!\n",
+                                               __func__, pdu_length, sarb->len, vcc);
+                               atomic_inc(&vcc->stats->rx_err);
+                               goto out;
+                       }
+
+                       if (crc32_be(~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) {
+                               atm_dbg(instance, "%s: packet failed crc check (vcc: 0x%p)!\n",
+                                               __func__, vcc);
+                               atomic_inc(&vcc->stats->rx_err);
+                               goto out;
+                       }
+
+                       vdbg("%s: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", __func__, length, pdu_length, vcc);
+
+                       if (!(skb = dev_alloc_skb(length))) {
+                               atm_dbg(instance, "%s: no memory for skb (length: %u)!\n", __func__, length);
+                               atomic_inc(&vcc->stats->rx_drop);
+                               goto out;
+                       }
+
+                       vdbg("%s: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", __func__, skb, skb->truesize);
+
+                       if (!atm_charge(vcc, skb->truesize)) {
+                               atm_dbg(instance, "%s: failed atm_charge (skb->truesize: %u)!\n", __func__, skb->truesize);
+                               dev_kfree_skb(skb);
+                               goto out;       /* atm_charge increments rx_drop */
+                       }
+
+                       memcpy(skb->data, sarb->tail - pdu_length, length);
+                       __skb_put(skb, length);
+
+                       vdbg("%s: sending skb 0x%p, skb->len %u, skb->truesize %u",
+                            __func__, skb, skb->len, skb->truesize);
+
+                       PACKETDEBUG(skb->data, skb->len);
+
+                       vcc->push(vcc, skb);
+
+                       atomic_inc(&vcc->stats->rx);
+               out:
+                       skb_trim(sarb, 0);
+               }
+       }
+}
+
+
+/*************
+**  encode  **
+*************/
+
+static unsigned int usbatm_write_cells(struct usbatm_data *instance,
+                                      struct sk_buff *skb,
+                                      u8 *target, unsigned int avail_space)
+{
+       struct usbatm_control *ctrl = UDSL_SKB(skb);
+       struct atm_vcc *vcc = ctrl->atm.vcc;
+       unsigned int num_written;
+       unsigned int stride = instance->tx_channel.stride;
+
+       vdbg("%s: skb->len=%d, avail_space=%u", __func__, skb->len, avail_space);
+       UDSL_ASSERT(!(avail_space % stride));
+
+       for (num_written = 0; num_written < avail_space && ctrl->len;
+            num_written += stride, target += stride) {
+               unsigned int data_len = min_t(unsigned int, skb->len, ATM_CELL_PAYLOAD);
+               unsigned int left = ATM_CELL_PAYLOAD - data_len;
+               u8 *ptr = target;
+
+               ptr[0] = vcc->vpi >> 4;
+               ptr[1] = (vcc->vpi << 4) | (vcc->vci >> 12);
+               ptr[2] = vcc->vci >> 4;
+               ptr[3] = vcc->vci << 4;
+               ptr[4] = 0xec;
+               ptr += ATM_CELL_HEADER;
+
+               memcpy(ptr, skb->data, data_len);
+               ptr += data_len;
+               __skb_pull(skb, data_len);
+
+               if(!left)
+                       continue;
+
+               memset(ptr, 0, left);
+
+               if (left >= ATM_AAL5_TRAILER) { /* trailer will go in this cell */
+                       u8 *trailer = target + ATM_CELL_SIZE - ATM_AAL5_TRAILER;
+                       /* trailer[0] = 0;              UU = 0 */
+                       /* trailer[1] = 0;              CPI = 0 */
+                       trailer[2] = ctrl->len >> 8;
+                       trailer[3] = ctrl->len;
+
+                       ctrl->crc = ~ crc32_be(ctrl->crc, ptr, left - 4);
+
+                       trailer[4] = ctrl->crc >> 24;
+                       trailer[5] = ctrl->crc >> 16;
+                       trailer[6] = ctrl->crc >> 8;
+                       trailer[7] = ctrl->crc;
+
+                       target[3] |= 0x2;       /* adjust PTI */
+
+                       ctrl->len = 0;          /* tag this skb finished */
+               }
+               else
+                       ctrl->crc = crc32_be(ctrl->crc, ptr, left);
+       }
+
+       return num_written;
+}
+
+
+/**************
+**  receive  **
+**************/
+
+static void usbatm_rx_process(unsigned long data)
+{
+       struct usbatm_data *instance = (struct usbatm_data *)data;
+       struct urb *urb;
+
+       while ((urb = usbatm_pop_urb(&instance->rx_channel))) {
+               vdbg("%s: processing urb 0x%p", __func__, urb);
+
+               if (usb_pipeisoc(urb->pipe)) {
+                       int i;
+                       for (i = 0; i < urb->number_of_packets; i++)
+                               if (!urb->iso_frame_desc[i].status)
+                                       usbatm_extract_cells(instance,
+                                                            (u8 *)urb->transfer_buffer + urb->iso_frame_desc[i].offset,
+                                                            urb->iso_frame_desc[i].actual_length);
+               }
+               else
+                       if (!urb->status)
+                               usbatm_extract_cells(instance, urb->transfer_buffer, urb->actual_length);
+
+               if (usbatm_submit_urb(urb))
+                       return;
+       }
+}
+
+
+/***********
+**  send  **
+***********/
+
+static void usbatm_tx_process(unsigned long data)
+{
+       struct usbatm_data *instance = (struct usbatm_data *)data;
+       struct sk_buff *skb = instance->current_skb;
+       struct urb *urb = NULL;
+       const unsigned int buf_size = instance->tx_channel.buf_size;
+       unsigned int num_written = 0;
+       u8 *buffer = NULL;
+
+       if (!skb)
+               skb = skb_dequeue(&instance->sndqueue);
+
+       while (skb) {
+               if (!urb) {
+                       urb = usbatm_pop_urb(&instance->tx_channel);
+                       if (!urb)
+                               break;          /* no more senders */
+                       buffer = urb->transfer_buffer;
+                       num_written = (urb->status == -EAGAIN) ?
+                               urb->transfer_buffer_length : 0;
+               }
+
+               num_written += usbatm_write_cells(instance, skb,
+                                                 buffer + num_written,
+                                                 buf_size - num_written);
+
+               vdbg("%s: wrote %u bytes from skb 0x%p to urb 0x%p",
+                    __func__, num_written, skb, urb);
+
+               if (!UDSL_SKB(skb)->len) {
+                       struct atm_vcc *vcc = UDSL_SKB(skb)->atm.vcc;
+
+                       usbatm_pop(vcc, skb);
+                       atomic_inc(&vcc->stats->tx);
+
+                       skb = skb_dequeue(&instance->sndqueue);
+               }
+
+               if (num_written == buf_size || (!skb && num_written)) {
+                       urb->transfer_buffer_length = num_written;
+
+                       if (usbatm_submit_urb(urb))
+                               break;
+                       urb = NULL;
+               }
+       }
+
+       instance->current_skb = skb;
+}
+
+static void usbatm_cancel_send(struct usbatm_data *instance,
+                              struct atm_vcc *vcc)
+{
+       struct sk_buff *skb, *n;
+
+       atm_dbg(instance, "%s entered\n", __func__);
+       spin_lock_irq(&instance->sndqueue.lock);
+       for (skb = instance->sndqueue.next, n = skb->next;
+            skb != (struct sk_buff *)&instance->sndqueue;
+            skb = n, n = skb->next)
+               if (UDSL_SKB(skb)->atm.vcc == vcc) {
+                       atm_dbg(instance, "%s: popping skb 0x%p\n", __func__, skb);
+                       __skb_unlink(skb, &instance->sndqueue);
+                       usbatm_pop(vcc, skb);
+               }
+       spin_unlock_irq(&instance->sndqueue.lock);
+
+       tasklet_disable(&instance->tx_channel.tasklet);
+       if ((skb = instance->current_skb) && (UDSL_SKB(skb)->atm.vcc == vcc)) {
+               atm_dbg(instance, "%s: popping current skb (0x%p)\n", __func__, skb);
+               instance->current_skb = NULL;
+               usbatm_pop(vcc, skb);
+       }
+       tasklet_enable(&instance->tx_channel.tasklet);
+       atm_dbg(instance, "%s done\n", __func__);
+}
+
+static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+       struct usbatm_data *instance = vcc->dev->dev_data;
+       struct usbatm_control *ctrl = UDSL_SKB(skb);
+       int err;
+
+       vdbg("%s called (skb 0x%p, len %u)", __func__, skb, skb->len);
+
+       if (!instance) {
+               dbg("%s: NULL data!", __func__);
+               err = -ENODEV;
+               goto fail;
+       }
+
+       if (vcc->qos.aal != ATM_AAL5) {
+               atm_dbg(instance, "%s: unsupported ATM type %d!\n", __func__, vcc->qos.aal);
+               err = -EINVAL;
+               goto fail;
+       }
+
+       if (skb->len > ATM_MAX_AAL5_PDU) {
+               atm_dbg(instance, "%s: packet too long (%d vs %d)!\n",
+                               __func__, skb->len, ATM_MAX_AAL5_PDU);
+               err = -EINVAL;
+               goto fail;
+       }
+
+       PACKETDEBUG(skb->data, skb->len);
+
+       /* initialize the control block */
+       ctrl->atm.vcc = vcc;
+       ctrl->len = skb->len;
+       ctrl->crc = crc32_be(~0, skb->data, skb->len);
+
+       skb_queue_tail(&instance->sndqueue, skb);
+       tasklet_schedule(&instance->tx_channel.tasklet);
+
+       return 0;
+
+ fail:
+       usbatm_pop(vcc, skb);
+       return err;
+}
+
+
+/********************
+**  bean counting  **
+********************/
+
+static void usbatm_destroy_instance(struct kref *kref)
+{
+       struct usbatm_data *instance = container_of(kref, struct usbatm_data, refcount);
+
+       dbg("%s", __func__);
+
+       tasklet_kill(&instance->rx_channel.tasklet);
+       tasklet_kill(&instance->tx_channel.tasklet);
+       usb_put_dev(instance->usb_dev);
+       kfree(instance);
+}
+
+void usbatm_get_instance(struct usbatm_data *instance)
+{
+       dbg("%s", __func__);
+
+       kref_get(&instance->refcount);
+}
+
+void usbatm_put_instance(struct usbatm_data *instance)
+{
+       dbg("%s", __func__);
+
+       kref_put(&instance->refcount, usbatm_destroy_instance);
+}
+
+
+/**********
+**  ATM  **
+**********/
+
+static void usbatm_atm_dev_close(struct atm_dev *dev)
+{
+       struct usbatm_data *instance = dev->dev_data;
+
+       dbg("%s", __func__);
+
+       if (!instance)
+               return;
+
+       dev->dev_data = NULL;
+       usbatm_put_instance(instance);  /* taken in usbatm_atm_init */
+}
+
+static int usbatm_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page)
+{
+       struct usbatm_data *instance = atm_dev->dev_data;
+       int left = *pos;
+
+       if (!instance) {
+               dbg("%s: NULL instance!", __func__);
+               return -ENODEV;
+       }
+
+       if (!left--)
+               return sprintf(page, "%s\n", instance->description);
+
+       if (!left--)
+               return sprintf(page, "MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+                              atm_dev->esi[0], atm_dev->esi[1],
+                              atm_dev->esi[2], atm_dev->esi[3],
+                              atm_dev->esi[4], atm_dev->esi[5]);
+
+       if (!left--)
+               return sprintf(page,
+                              "AAL5: tx %d ( %d err ), rx %d ( %d err, %d drop )\n",
+                              atomic_read(&atm_dev->stats.aal5.tx),
+                              atomic_read(&atm_dev->stats.aal5.tx_err),
+                              atomic_read(&atm_dev->stats.aal5.rx),
+                              atomic_read(&atm_dev->stats.aal5.rx_err),
+                              atomic_read(&atm_dev->stats.aal5.rx_drop));
+
+       if (!left--)
+               switch (atm_dev->signal) {
+               case ATM_PHY_SIG_FOUND:
+                       return sprintf(page, "Line up\n");
+               case ATM_PHY_SIG_LOST:
+                       return sprintf(page, "Line down\n");
+               default:
+                       return sprintf(page, "Line state unknown\n");
+               }
+
+       return 0;
+}
+
+static int usbatm_atm_open(struct atm_vcc *vcc)
+{
+       struct usbatm_data *instance = vcc->dev->dev_data;
+       struct usbatm_vcc_data *new = NULL;
+       int ret;
+       int vci = vcc->vci;
+       short vpi = vcc->vpi;
+
+       if (!instance) {
+               dbg("%s: NULL data!", __func__);
+               return -ENODEV;
+       }
+
+       atm_dbg(instance, "%s: vpi %hd, vci %d\n", __func__, vpi, vci);
+
+       /* only support AAL5 */
+       if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0)
+           || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) {
+               atm_dbg(instance, "%s: unsupported ATM type %d!\n", __func__, vcc->qos.aal);
+               return -EINVAL;
+       }
+
+       down(&instance->serialize);     /* vs self, usbatm_atm_close */
+
+       if (usbatm_find_vcc(instance, vpi, vci)) {
+               atm_dbg(instance, "%s: %hd/%d already in use!\n", __func__, vpi, vci);
+               ret = -EADDRINUSE;
+               goto fail;
+       }
+
+       if (!(new = kmalloc(sizeof(struct usbatm_vcc_data), GFP_KERNEL))) {
+               atm_dbg(instance, "%s: no memory for vcc_data!\n", __func__);
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       memset(new, 0, sizeof(struct usbatm_vcc_data));
+       new->vcc = vcc;
+       new->vpi = vpi;
+       new->vci = vci;
+
+       new->sarb = alloc_skb(usbatm_pdu_length(vcc->qos.rxtp.max_sdu), GFP_KERNEL);
+       if (!new->sarb) {
+               atm_dbg(instance, "%s: no memory for SAR buffer!\n", __func__);
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       vcc->dev_data = new;
+
+       tasklet_disable(&instance->rx_channel.tasklet);
+       list_add(&new->list, &instance->vcc_list);
+       tasklet_enable(&instance->rx_channel.tasklet);
+
+       set_bit(ATM_VF_ADDR, &vcc->flags);
+       set_bit(ATM_VF_PARTIAL, &vcc->flags);
+       set_bit(ATM_VF_READY, &vcc->flags);
+
+       up(&instance->serialize);
+
+       atm_dbg(instance, "%s: allocated vcc data 0x%p\n", __func__, new);
+
+       return 0;
+
+fail:
+       kfree(new);
+       up(&instance->serialize);
+       return ret;
+}
+
+static void usbatm_atm_close(struct atm_vcc *vcc)
+{
+       struct usbatm_data *instance = vcc->dev->dev_data;
+       struct usbatm_vcc_data *vcc_data = vcc->dev_data;
+
+       if (!instance || !vcc_data) {
+               dbg("%s: NULL data!", __func__);
+               return;
+       }
+
+       atm_dbg(instance, "%s entered\n", __func__);
+
+       atm_dbg(instance, "%s: deallocating vcc 0x%p with vpi %d vci %d\n",
+               __func__, vcc_data, vcc_data->vpi, vcc_data->vci);
+
+       usbatm_cancel_send(instance, vcc);
+
+       down(&instance->serialize);     /* vs self, usbatm_atm_open */
+
+       tasklet_disable(&instance->rx_channel.tasklet);
+       list_del(&vcc_data->list);
+       tasklet_enable(&instance->rx_channel.tasklet);
+
+       kfree_skb(vcc_data->sarb);
+       vcc_data->sarb = NULL;
+
+       kfree(vcc_data);
+       vcc->dev_data = NULL;
+
+       vcc->vpi = ATM_VPI_UNSPEC;
+       vcc->vci = ATM_VCI_UNSPEC;
+       clear_bit(ATM_VF_READY, &vcc->flags);
+       clear_bit(ATM_VF_PARTIAL, &vcc->flags);
+       clear_bit(ATM_VF_ADDR, &vcc->flags);
+
+       up(&instance->serialize);
+
+       atm_dbg(instance, "%s successful\n", __func__);
+}
+
+static int usbatm_atm_ioctl(struct atm_dev *dev, unsigned int cmd,
+                         void __user * arg)
+{
+       switch (cmd) {
+       case ATM_QUERYLOOP:
+               return put_user(ATM_LM_NONE, (int __user *)arg) ? -EFAULT : 0;
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
+static int usbatm_atm_init(struct usbatm_data *instance)
+{
+       struct atm_dev *atm_dev;
+       int ret, i;
+
+       /* ATM init */
+       atm_dev = atm_dev_register(instance->driver_name, &usbatm_atm_devops, -1, NULL);
+       if (!atm_dev) {
+               usb_dbg(instance, "%s: failed to register ATM device!\n", __func__);
+               return -1;
+       }
+
+       instance->atm_dev = atm_dev;
+
+       atm_dev->ci_range.vpi_bits = ATM_CI_MAX;
+       atm_dev->ci_range.vci_bits = ATM_CI_MAX;
+       atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
+
+       /* temp init ATM device, set to 128kbit */
+       atm_dev->link_rate = 128 * 1000 / 424;
+
+       if (instance->driver->atm_start && ((ret = instance->driver->atm_start(instance, atm_dev)) < 0)) {
+               atm_dbg(instance, "%s: atm_start failed: %d!\n", __func__, ret);
+               goto fail;
+       }
+
+       /* ready for ATM callbacks */
+       usbatm_get_instance(instance);  /* dropped in usbatm_atm_dev_close */
+       mb();
+       atm_dev->dev_data = instance;
+
+       /* submit all rx URBs */
+       for (i = 0; i < num_rcv_urbs; i++)
+               usbatm_submit_urb(instance->urbs[i]);
+
+       return 0;
+
+ fail:
+       instance->atm_dev = NULL;
+       shutdown_atm_dev(atm_dev); /* usbatm_atm_dev_close will eventually be called */
+       return ret;
+}
+
+
+/**********
+**  USB  **
+**********/
+
+static int usbatm_do_heavy_init(void *arg)
+{
+       struct usbatm_data *instance = arg;
+       int ret;
+
+       daemonize(instance->driver->driver_name);
+       allow_signal(SIGTERM);
+
+       complete(&instance->thread_started);
+
+       ret = instance->driver->heavy_init(instance, instance->usb_intf);
+
+       if (!ret)
+               ret = usbatm_atm_init(instance);
+
+       down(&instance->serialize);
+       instance->thread_pid = -1;
+       up(&instance->serialize);
+
+       complete_and_exit(&instance->thread_exited, ret);
+}
+
+static int usbatm_heavy_init(struct usbatm_data *instance)
+{
+       int ret = kernel_thread(usbatm_do_heavy_init, instance, CLONE_KERNEL);
+
+       if (ret < 0) {
+               usb_dbg(instance, "%s: failed to create kernel_thread (%d)!\n", __func__, ret);
+               return ret;
+       }
+
+       down(&instance->serialize);
+       instance->thread_pid = ret;
+       up(&instance->serialize);
+
+       wait_for_completion(&instance->thread_started);
+
+       return 0;
+}
+
+static void usbatm_tasklet_schedule(unsigned long data)
+{
+       tasklet_schedule((struct tasklet_struct *) data);
+}
+
+static inline void usbatm_init_channel(struct usbatm_channel *channel)
+{
+       spin_lock_init(&channel->lock);
+       INIT_LIST_HEAD(&channel->list);
+       channel->delay.function = usbatm_tasklet_schedule;
+       channel->delay.data = (unsigned long) &channel->tasklet;
+       init_timer(&channel->delay);
+}
+
+int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
+                    struct usbatm_driver *driver)
+{
+       struct device *dev = &intf->dev;
+       struct usb_device *usb_dev = interface_to_usbdev(intf);
+       struct usbatm_data *instance;
+       char *buf;
+       int error = -ENOMEM;
+       int i, length;
+       int need_heavy;
+
+       dev_dbg(dev, "%s: trying driver %s with vendor=0x%x, product=0x%x, ifnum %d\n",
+                       __func__, driver->driver_name,
+                       le16_to_cpu(usb_dev->descriptor.idVendor),
+                       le16_to_cpu(usb_dev->descriptor.idProduct),
+                       intf->altsetting->desc.bInterfaceNumber);
+
+       /* instance init */
+       instance = kcalloc(1, sizeof(*instance) + sizeof(struct urb *) * (num_rcv_urbs + num_snd_urbs), GFP_KERNEL);
+       if (!instance) {
+               dev_dbg(dev, "%s: no memory for instance data!\n", __func__);
+               return -ENOMEM;
+       }
+
+       /* public fields */
+
+       instance->driver = driver;
+       snprintf(instance->driver_name, sizeof(instance->driver_name), driver->driver_name);
+
+       instance->usb_dev = usb_dev;
+       instance->usb_intf = intf;
+
+       buf = instance->description;
+       length = sizeof(instance->description);
+
+       if ((i = usb_string(usb_dev, usb_dev->descriptor.iProduct, buf, length)) < 0)
+               goto bind;
+
+       buf += i;
+       length -= i;
+
+       i = scnprintf(buf, length, " (");
+       buf += i;
+       length -= i;
+
+       if (length <= 0 || (i = usb_make_path(usb_dev, buf, length)) < 0)
+               goto bind;
+
+       buf += i;
+       length -= i;
+
+       snprintf(buf, length, ")");
+
+ bind:
+       need_heavy = 1;
+       if (driver->bind && (error = driver->bind(instance, intf, id, &need_heavy)) < 0) {
+                       dev_dbg(dev, "%s: bind failed: %d!\n", __func__, error);
+                       goto fail_free;
+       }
+
+       /* private fields */
+
+       kref_init(&instance->refcount);         /* dropped in usbatm_usb_disconnect */
+       init_MUTEX(&instance->serialize);
+
+       instance->thread_pid = -1;
+       init_completion(&instance->thread_started);
+       init_completion(&instance->thread_exited);
+
+       INIT_LIST_HEAD(&instance->vcc_list);
+
+       usbatm_init_channel(&instance->rx_channel);
+       usbatm_init_channel(&instance->tx_channel);
+       tasklet_init(&instance->rx_channel.tasklet, usbatm_rx_process, (unsigned long)instance);
+       tasklet_init(&instance->tx_channel.tasklet, usbatm_tx_process, (unsigned long)instance);
+       instance->rx_channel.endpoint = usb_rcvbulkpipe(usb_dev, driver->in);
+       instance->tx_channel.endpoint = usb_sndbulkpipe(usb_dev, driver->out);
+       instance->rx_channel.stride = ATM_CELL_SIZE + driver->rx_padding;
+       instance->tx_channel.stride = ATM_CELL_SIZE + driver->tx_padding;
+       instance->rx_channel.buf_size = rcv_buf_size * instance->rx_channel.stride;
+       instance->tx_channel.buf_size = snd_buf_size * instance->tx_channel.stride;
+       instance->rx_channel.usbatm = instance->tx_channel.usbatm = instance;
+
+       skb_queue_head_init(&instance->sndqueue);
+
+       for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++) {
+               struct urb *urb;
+               u8 *buffer;
+               unsigned int iso_packets = 0, iso_size = 0;
+               struct usbatm_channel *channel = i < num_rcv_urbs ?
+                       &instance->rx_channel : &instance->tx_channel;
+
+               if (usb_pipeisoc(channel->endpoint)) {
+                       /* don't expect iso out endpoints */
+                       iso_size = usb_maxpacket(instance->usb_dev, channel->endpoint, 0);
+                       iso_size -= iso_size % channel->stride; /* alignment */
+                       BUG_ON(!iso_size);
+                       iso_packets = (channel->buf_size - 1) / iso_size + 1;
+               }
+
+               urb = usb_alloc_urb(iso_packets, GFP_KERNEL);
+               if (!urb) {
+                       dev_dbg(dev, "%s: no memory for urb %d!\n", __func__, i);
+                       goto fail_unbind;
+               }
+
+               instance->urbs[i] = urb;
+
+               buffer = kmalloc(channel->buf_size, GFP_KERNEL);
+               if (!buffer) {
+                       dev_dbg(dev, "%s: no memory for buffer %d!\n", __func__, i);
+                       goto fail_unbind;
+               }
+               memset(buffer, 0, channel->buf_size);
+
+               usb_fill_bulk_urb(urb, instance->usb_dev, channel->endpoint,
+                                 buffer, channel->buf_size, usbatm_complete, channel);
+               if (iso_packets) {
+                       int j;
+                       urb->interval = 1;
+                       urb->transfer_flags = URB_ISO_ASAP;
+                       urb->number_of_packets = iso_packets;
+                       for (j = 0; j < iso_packets; j++) {
+                               urb->iso_frame_desc[j].offset = iso_size * j;
+                               urb->iso_frame_desc[j].length = min_t(int, iso_size,
+                                                                     channel->buf_size - urb->iso_frame_desc[j].offset);
+                       }
+               }
+
+               /* put all tx URBs on the list of spares */
+               if (i >= num_rcv_urbs)
+                       list_add_tail(&urb->urb_list, &channel->list);
+
+               vdbg("%s: alloced buffer 0x%p buf size %u urb 0x%p",
+                    __func__, urb->transfer_buffer, urb->transfer_buffer_length, urb);
+       }
+
+       if (need_heavy && driver->heavy_init) {
+               error = usbatm_heavy_init(instance);
+       } else {
+               complete(&instance->thread_exited);     /* pretend that heavy_init was run */
+               error = usbatm_atm_init(instance);
+       }
+
+       if (error < 0)
+               goto fail_unbind;
+
+       usb_get_dev(usb_dev);
+       usb_set_intfdata(intf, instance);
+
+       return 0;
+
+ fail_unbind:
+       if (instance->driver->unbind)
+               instance->driver->unbind(instance, intf);
+ fail_free:
+       for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++) {
+               if (instance->urbs[i])
+                       kfree(instance->urbs[i]->transfer_buffer);
+               usb_free_urb(instance->urbs[i]);
+       }
+
+       kfree (instance);
+
+       return error;
+}
+EXPORT_SYMBOL_GPL(usbatm_usb_probe);
+
+void usbatm_usb_disconnect(struct usb_interface *intf)
+{
+       struct device *dev = &intf->dev;
+       struct usbatm_data *instance = usb_get_intfdata(intf);
+       int i;
+
+       dev_dbg(dev, "%s entered\n", __func__);
+
+       if (!instance) {
+               dev_dbg(dev, "%s: NULL instance!\n", __func__);
+               return;
+       }
+
+       usb_set_intfdata(intf, NULL);
+
+       down(&instance->serialize);
+       if (instance->thread_pid >= 0)
+               kill_proc(instance->thread_pid, SIGTERM, 1);
+       up(&instance->serialize);
+
+       wait_for_completion(&instance->thread_exited);
+
+       tasklet_disable(&instance->rx_channel.tasklet);
+       tasklet_disable(&instance->tx_channel.tasklet);
+
+       for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++)
+               usb_kill_urb(instance->urbs[i]);
+
+       del_timer_sync(&instance->rx_channel.delay);
+       del_timer_sync(&instance->tx_channel.delay);
+
+       if (instance->atm_dev && instance->driver->atm_stop)
+               instance->driver->atm_stop(instance, instance->atm_dev);
+
+       if (instance->driver->unbind)
+               instance->driver->unbind(instance, intf);
+
+       instance->driver_data = NULL;
+
+       /* turn usbatm_[rt]x_process into noop */
+       /* no need to take the spinlock */
+       INIT_LIST_HEAD(&instance->rx_channel.list);
+       INIT_LIST_HEAD(&instance->tx_channel.list);
+
+       tasklet_enable(&instance->rx_channel.tasklet);
+       tasklet_enable(&instance->tx_channel.tasklet);
+
+       for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++) {
+               kfree(instance->urbs[i]->transfer_buffer);
+               usb_free_urb(instance->urbs[i]);
+       }
+
+       /* ATM finalize */
+       if (instance->atm_dev)
+               shutdown_atm_dev(instance->atm_dev);
+
+       usbatm_put_instance(instance);  /* taken in usbatm_usb_probe */
+}
+EXPORT_SYMBOL_GPL(usbatm_usb_disconnect);
+
+
+/***********
+**  init  **
+***********/
+
+static int __init usbatm_usb_init(void)
+{
+       dbg("%s: driver version %s", __func__, DRIVER_VERSION);
+
+       if (sizeof(struct usbatm_control) > sizeof(((struct sk_buff *) 0)->cb)) {
+               printk(KERN_ERR "%s unusable with this kernel!\n", usbatm_driver_name);
+               return -EIO;
+       }
+
+       if ((num_rcv_urbs > UDSL_MAX_RCV_URBS)
+           || (num_snd_urbs > UDSL_MAX_SND_URBS)
+           || (rcv_buf_size < 1)
+           || (rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE)
+           || (snd_buf_size < 1)
+           || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE))
+               return -EINVAL;
+
+       return 0;
+}
+module_init(usbatm_usb_init);
+
+static void __exit usbatm_usb_exit(void)
+{
+       dbg("%s", __func__);
+}
+module_exit(usbatm_usb_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
+
+/************
+**  debug  **
+************/
+
+#ifdef VERBOSE_DEBUG
+static int usbatm_print_packet(const unsigned char *data, int len)
+{
+       unsigned char buffer[256];
+       int i = 0, j = 0;
+
+       for (i = 0; i < len;) {
+               buffer[0] = '\0';
+               sprintf(buffer, "%.3d :", i);
+               for (j = 0; (j < 16) && (i < len); j++, i++) {
+                       sprintf(buffer, "%s %2.2x", buffer, data[i]);
+               }
+               dbg("%s", buffer);
+       }
+       return i;
+}
+#endif
diff --git a/drivers/usb/atm/usbatm.h b/drivers/usb/atm/usbatm.h
new file mode 100644 (file)
index 0000000..9366464
--- /dev/null
@@ -0,0 +1,184 @@
+/******************************************************************************
+ *  usbatm.h - Generic USB xDSL driver core
+ *
+ *  Copyright (C) 2001, Alcatel
+ *  Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas
+ *  Copyright (C) 2004, David Woodhouse
+ *
+ *  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., 59
+ *  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ ******************************************************************************/
+
+#ifndef        _USBATM_H_
+#define        _USBATM_H_
+
+#include <linux/config.h>
+
+/*
+#define DEBUG
+#define VERBOSE_DEBUG
+*/
+
+#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)
+#      define DEBUG
+#endif
+
+#include <asm/semaphore.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/stringify.h>
+#include <linux/usb.h>
+
+#ifdef DEBUG
+#define UDSL_ASSERT(x) BUG_ON(!(x))
+#else
+#define UDSL_ASSERT(x) do { if (!(x)) warn("failed assertion '%s' at line %d", __stringify(x), __LINE__); } while(0)
+#endif
+
+#define usb_err(instance, format, arg...)      \
+       dev_err(&(instance)->usb_intf->dev , format , ## arg)
+#define usb_info(instance, format, arg...)     \
+       dev_info(&(instance)->usb_intf->dev , format , ## arg)
+#define usb_warn(instance, format, arg...)     \
+       dev_warn(&(instance)->usb_intf->dev , format , ## arg)
+#define usb_dbg(instance, format, arg...)      \
+       dev_dbg(&(instance)->usb_intf->dev , format , ## arg)
+
+/* FIXME: move to dev_* once ATM is driver model aware */
+#define atm_printk(level, instance, format, arg...)    \
+       printk(level "ATM dev %d: " format ,            \
+       (instance)->atm_dev->number , ## arg)
+
+#define atm_err(instance, format, arg...)      \
+       atm_printk(KERN_ERR, instance , format , ## arg)
+#define atm_info(instance, format, arg...)     \
+       atm_printk(KERN_INFO, instance , format , ## arg)
+#define atm_warn(instance, format, arg...)     \
+       atm_printk(KERN_WARNING, instance , format , ## arg)
+#ifdef DEBUG
+#define atm_dbg(instance, format, arg...)      \
+       atm_printk(KERN_DEBUG, instance , format , ## arg)
+#else
+#define atm_dbg(instance, format, arg...)      \
+       do {} while (0)
+#endif
+
+
+/* mini driver */
+
+struct usbatm_data;
+
+/*
+*  Assuming all methods exist and succeed, they are called in this order:
+*
+*      bind, heavy_init, atm_start, ..., atm_stop, unbind
+*/
+
+struct usbatm_driver {
+       struct module *owner;
+
+       const char *driver_name;
+
+       /*
+       *  init device ... can sleep, or cause probe() failure.  Drivers with a heavy_init
+       *  method can avoid having it called by setting need_heavy_init to zero.
+       */
+        int (*bind) (struct usbatm_data *, struct usb_interface *,
+                    const struct usb_device_id *id, int *need_heavy_init);
+
+       /* additional device initialization that is too slow to be done in probe() */
+        int (*heavy_init) (struct usbatm_data *, struct usb_interface *);
+
+       /* cleanup device ... can sleep, but can't fail */
+        void (*unbind) (struct usbatm_data *, struct usb_interface *);
+
+       /* init ATM device ... can sleep, or cause ATM initialization failure */
+       int (*atm_start) (struct usbatm_data *, struct atm_dev *);
+
+       /* cleanup ATM device ... can sleep, but can't fail */
+       void (*atm_stop) (struct usbatm_data *, struct atm_dev *);
+
+        int in;                /* rx endpoint */
+        int out;       /* tx endpoint */
+
+       unsigned rx_padding;
+       unsigned tx_padding;
+};
+
+extern int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
+               struct usbatm_driver *driver);
+extern void usbatm_usb_disconnect(struct usb_interface *intf);
+
+
+struct usbatm_channel {
+       int endpoint;                   /* usb pipe */
+       unsigned int stride;            /* ATM cell size + padding */
+       unsigned int buf_size;          /* urb buffer size */
+       spinlock_t lock;
+       struct list_head list;
+       struct tasklet_struct tasklet;
+       struct timer_list delay;
+       struct usbatm_data *usbatm;
+};
+
+/* main driver data */
+
+struct usbatm_data {
+       /******************
+       *  public fields  *
+        ******************/
+
+       /* mini driver */
+       struct usbatm_driver *driver;
+       void *driver_data;
+       char driver_name[16];
+
+       /* USB device */
+       struct usb_device *usb_dev;
+       struct usb_interface *usb_intf;
+       char description[64];
+
+       /* ATM device */
+       struct atm_dev *atm_dev;
+
+       /********************************
+       *  private fields - do not use  *
+        ********************************/
+
+       struct kref refcount;
+       struct semaphore serialize;
+
+       /* heavy init */
+       int thread_pid;
+       struct completion thread_started;
+       struct completion thread_exited;
+
+       /* ATM device */
+       struct list_head vcc_list;
+
+       struct usbatm_channel rx_channel;
+       struct usbatm_channel tx_channel;
+
+       struct sk_buff_head sndqueue;
+       struct sk_buff *current_skb;                    /* being emptied */
+
+       struct urb *urbs[0];
+};
+
+#endif /* _USBATM_H_ */
diff --git a/drivers/usb/atm/xusbatm.c b/drivers/usb/atm/xusbatm.c
new file mode 100644 (file)
index 0000000..7fe7fb4
--- /dev/null
@@ -0,0 +1,196 @@
+/******************************************************************************
+ *  xusbatm.c -        dumb usbatm-based driver for modems initialized in userspace
+ *
+ *  Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru)
+ *
+ *  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., 59
+ *  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ ******************************************************************************/
+
+#include <linux/module.h>
+#include <linux/netdevice.h>           /* FIXME: required by linux/etherdevice.h */
+#include <linux/etherdevice.h>         /* for random_ether_addr() */
+
+#include "usbatm.h"
+
+
+#define XUSBATM_DRIVERS_MAX    8
+
+#define XUSBATM_PARM(name, type, parmtype, desc) \
+       static type name[XUSBATM_DRIVERS_MAX]; \
+       static int num_##name; \
+       module_param_array(name, parmtype, &num_##name, 0444); \
+       MODULE_PARM_DESC(name, desc)
+
+XUSBATM_PARM(vendor, unsigned short, ushort, "USB device vendor");
+XUSBATM_PARM(product, unsigned short, ushort, "USB device product");
+
+XUSBATM_PARM(rx_endpoint, unsigned char, byte, "rx endpoint number");
+XUSBATM_PARM(tx_endpoint, unsigned char, byte, "tx endpoint number");
+XUSBATM_PARM(rx_padding, unsigned char, byte, "rx padding (default 0)");
+XUSBATM_PARM(tx_padding, unsigned char, byte, "tx padding (default 0)");
+
+static const char xusbatm_driver_name[] = "xusbatm";
+
+static struct usbatm_driver xusbatm_drivers[XUSBATM_DRIVERS_MAX];
+static struct usb_device_id xusbatm_usb_ids[XUSBATM_DRIVERS_MAX + 1];
+static struct usb_driver xusbatm_usb_driver;
+
+static int usb_intf_has_ep(const struct usb_interface *intf, u8 ep)
+{
+       int i, j;
+
+       for (i = 0; i < intf->num_altsetting; i++) {
+               struct usb_host_interface *alt = intf->altsetting;
+               for (j = 0; j < alt->desc.bNumEndpoints; j++)
+                       if ((alt->endpoint[i].desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) == ep)
+                               return 1;
+       }
+       return 0;
+}
+
+static int xusbatm_bind(struct usbatm_data *usbatm_instance,
+                       struct usb_interface *intf, const struct usb_device_id *id,
+                       int *need_heavy_init)
+{
+       struct usb_device *usb_dev = interface_to_usbdev(intf);
+       int drv_ix = id - xusbatm_usb_ids;
+       int rx_ep_present = usb_intf_has_ep(intf, rx_endpoint[drv_ix]);
+       int tx_ep_present = usb_intf_has_ep(intf, tx_endpoint[drv_ix]);
+       u8 searched_ep = rx_ep_present ? tx_endpoint[drv_ix] : rx_endpoint[drv_ix];
+       int i, ret;
+
+       usb_dbg(usbatm_instance, "%s: binding driver %d: vendor %#x product %#x"
+               " rx: ep %#x padd %d tx: ep %#x padd %d\n",
+               __func__, drv_ix, vendor[drv_ix], product[drv_ix],
+               rx_endpoint[drv_ix], rx_padding[drv_ix],
+               tx_endpoint[drv_ix], tx_padding[drv_ix]);
+
+       if (!rx_ep_present && !tx_ep_present) {
+               usb_dbg(usbatm_instance, "%s: intf #%d has neither rx (%#x) nor tx (%#x) endpoint\n",
+                       __func__, intf->altsetting->desc.bInterfaceNumber,
+                       rx_endpoint[drv_ix], tx_endpoint[drv_ix]);
+               return -ENODEV;
+       }
+
+       if (rx_ep_present && tx_ep_present)
+               return 0;
+
+       for(i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) {
+               struct usb_interface *cur_if = usb_dev->actconfig->interface[i];
+
+               if (cur_if != intf && usb_intf_has_ep(cur_if, searched_ep)) {
+                       ret = usb_driver_claim_interface(&xusbatm_usb_driver,
+                                                        cur_if, usbatm_instance);
+                       if (!ret)
+                               usb_err(usbatm_instance, "%s: failed to claim interface #%d (%d)\n",
+                                       __func__, cur_if->altsetting->desc.bInterfaceNumber, ret);
+                       return ret;
+               }
+       }
+
+       usb_err(usbatm_instance, "%s: no interface has endpoint %#x\n",
+               __func__, searched_ep);
+       return -ENODEV;
+}
+
+static void xusbatm_unbind(struct usbatm_data *usbatm_instance,
+                          struct usb_interface *intf)
+{
+       struct usb_device *usb_dev = interface_to_usbdev(intf);
+       int i;
+       usb_dbg(usbatm_instance, "%s entered\n", __func__);
+
+       for(i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) {
+               struct usb_interface *cur_if = usb_dev->actconfig->interface[i];
+               usb_set_intfdata(cur_if, NULL);
+               usb_driver_release_interface(&xusbatm_usb_driver, cur_if);
+       }
+}
+
+static int xusbatm_atm_start(struct usbatm_data *usbatm_instance,
+                            struct atm_dev *atm_dev)
+{
+       atm_dbg(usbatm_instance, "%s entered\n", __func__);
+
+       /* use random MAC as we've no way to get it from the device */
+       random_ether_addr(atm_dev->esi);
+
+       return 0;
+}
+
+
+static int xusbatm_usb_probe(struct usb_interface *intf,
+                            const struct usb_device_id *id)
+{
+       return usbatm_usb_probe(intf, id,
+                               xusbatm_drivers + (id - xusbatm_usb_ids));
+}
+
+static struct usb_driver xusbatm_usb_driver = {
+       .owner          = THIS_MODULE,
+       .name           = xusbatm_driver_name,
+       .probe          = xusbatm_usb_probe,
+       .disconnect     = usbatm_usb_disconnect,
+       .id_table       = xusbatm_usb_ids
+};
+
+static int __init xusbatm_init(void)
+{
+       int i;
+
+       dbg("xusbatm_init");
+
+       if (!num_vendor ||
+           num_vendor != num_product ||
+           num_vendor != num_rx_endpoint ||
+           num_vendor != num_tx_endpoint) {
+               warn("malformed module parameters");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < num_vendor; i++) {
+               xusbatm_usb_ids[i].match_flags  = USB_DEVICE_ID_MATCH_DEVICE;
+               xusbatm_usb_ids[i].idVendor     = vendor[i];
+               xusbatm_usb_ids[i].idProduct    = product[i];
+
+
+               xusbatm_drivers[i].owner        = THIS_MODULE;
+               xusbatm_drivers[i].driver_name  = xusbatm_driver_name;
+               xusbatm_drivers[i].bind         = xusbatm_bind;
+               xusbatm_drivers[i].unbind       = xusbatm_unbind;
+               xusbatm_drivers[i].atm_start    = xusbatm_atm_start;
+               xusbatm_drivers[i].in           = rx_endpoint[i];
+               xusbatm_drivers[i].out          = tx_endpoint[i];
+               xusbatm_drivers[i].rx_padding   = rx_padding[i];
+               xusbatm_drivers[i].tx_padding   = tx_padding[i];
+       }
+
+       return usb_register(&xusbatm_usb_driver);
+}
+module_init(xusbatm_init);
+
+static void __exit xusbatm_exit(void)
+{
+       dbg("xusbatm_exit entered");
+
+       usb_deregister(&xusbatm_usb_driver);
+}
+module_exit(xusbatm_exit);
+
+MODULE_AUTHOR("Roman Kagan, Duncan Sands");
+MODULE_DESCRIPTION("Driver for USB ADSL modems initialized in userspace");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.1");
index 6d1f9b6..69e859e 100644 (file)
@@ -105,6 +105,111 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int
 #define acm_send_break(acm, ms) \
        acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
 
+/*
+ * Write buffer management.
+ * All of these assume proper locks taken by the caller.
+ */
+
+static int acm_wb_alloc(struct acm *acm)
+{
+       int i, wbn;
+       struct acm_wb *wb;
+
+       wbn = acm->write_current;
+       i = 0;
+       for (;;) {
+               wb = &acm->wb[wbn];
+               if (!wb->use) {
+                       wb->use = 1;
+                       return wbn;
+               }
+               wbn = (wbn + 1) % ACM_NWB;
+               if (++i >= ACM_NWB)
+                       return -1;
+       }
+}
+
+static void acm_wb_free(struct acm *acm, int wbn)
+{
+       acm->wb[wbn].use = 0;
+}
+
+static int acm_wb_is_avail(struct acm *acm)
+{
+       int i, n;
+
+       n = 0;
+       for (i = 0; i < ACM_NWB; i++) {
+               if (!acm->wb[i].use)
+                       n++;
+       }
+       return n;
+}
+
+static inline int acm_wb_is_used(struct acm *acm, int wbn)
+{
+       return acm->wb[wbn].use;
+}
+
+/*
+ * Finish write.
+ */
+static void acm_write_done(struct acm *acm)
+{
+       unsigned long flags;
+       int wbn;
+
+       spin_lock_irqsave(&acm->write_lock, flags);
+       acm->write_ready = 1;
+       wbn = acm->write_current;
+       acm_wb_free(acm, wbn);
+       acm->write_current = (wbn + 1) % ACM_NWB;
+       spin_unlock_irqrestore(&acm->write_lock, flags);
+}
+
+/*
+ * Poke write.
+ */
+static int acm_write_start(struct acm *acm)
+{
+       unsigned long flags;
+       int wbn;
+       struct acm_wb *wb;
+       int rc;
+
+       spin_lock_irqsave(&acm->write_lock, flags);
+       if (!acm->dev) {
+               spin_unlock_irqrestore(&acm->write_lock, flags);
+               return -ENODEV;
+       }
+
+       if (!acm->write_ready) {
+               spin_unlock_irqrestore(&acm->write_lock, flags);
+               return 0;       /* A white lie */
+       }
+
+       wbn = acm->write_current;
+       if (!acm_wb_is_used(acm, wbn)) {
+               spin_unlock_irqrestore(&acm->write_lock, flags);
+               return 0;
+       }
+       wb = &acm->wb[wbn];
+
+       acm->write_ready = 0;
+       spin_unlock_irqrestore(&acm->write_lock, flags);
+
+       acm->writeurb->transfer_buffer = wb->buf;
+       acm->writeurb->transfer_dma = wb->dmah;
+       acm->writeurb->transfer_buffer_length = wb->len;
+       acm->writeurb->dev = acm->dev;
+
+       if ((rc = usb_submit_urb(acm->writeurb, GFP_ATOMIC)) < 0) {
+               dbg("usb_submit_urb(write bulk) failed: %d", rc);
+               acm_write_done(acm);
+       }
+       return rc;
+}
+
 /*
  * Interrupt handlers for various ACM device responses
  */
@@ -237,17 +342,13 @@ static void acm_rx_tasklet(unsigned long _acm)
 static void acm_write_bulk(struct urb *urb, struct pt_regs *regs)
 {
        struct acm *acm = (struct acm *)urb->context;
-       dbg("Entering acm_write_bulk with status %d\n", urb->status);
-
-       if (!ACM_READY(acm))
-               goto out;
 
-       if (urb->status)
-               dbg("nonzero write bulk status received: %d", urb->status);
+       dbg("Entering acm_write_bulk with status %d\n", urb->status);
 
-       schedule_work(&acm->work);
-out:
-       acm->ready_for_write = 1;
+       acm_write_done(acm);
+       acm_write_start(acm);
+       if (ACM_READY(acm))
+               schedule_work(&acm->work);
 }
 
 static void acm_softint(void *private)
@@ -351,32 +452,33 @@ static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int c
 {
        struct acm *acm = tty->driver_data;
        int stat;
+       unsigned long flags;
+       int wbn;
+       struct acm_wb *wb;
+
        dbg("Entering acm_tty_write to write %d bytes,\n", count);
 
        if (!ACM_READY(acm))
                return -EINVAL;
-       if (!acm->ready_for_write)
-               return 0;
        if (!count)
                return 0;
 
-       count = (count > acm->writesize) ? acm->writesize : count;
+       spin_lock_irqsave(&acm->write_lock, flags);
+       if ((wbn = acm_wb_alloc(acm)) < 0) {
+               spin_unlock_irqrestore(&acm->write_lock, flags);
+               acm_write_start(acm);
+               return 0;
+       }
+       wb = &acm->wb[wbn];
 
+       count = (count > acm->writesize) ? acm->writesize : count;
        dbg("Get %d bytes...", count);
-       memcpy(acm->write_buffer, buf, count);
-       dbg("  Successfully copied.\n");
+       memcpy(wb->buf, buf, count);
+       wb->len = count;
+       spin_unlock_irqrestore(&acm->write_lock, flags);
 
-       acm->writeurb->transfer_buffer_length = count;
-       acm->writeurb->dev = acm->dev;
-
-       acm->ready_for_write = 0;
-       stat = usb_submit_urb(acm->writeurb, GFP_ATOMIC);
-       if (stat < 0) {
-               dbg("usb_submit_urb(write bulk) failed");
-               acm->ready_for_write = 1;
+       if ((stat = acm_write_start(acm)) < 0)
                return stat;
-       }
-
        return count;
 }
 
@@ -385,7 +487,11 @@ static int acm_tty_write_room(struct tty_struct *tty)
        struct acm *acm = tty->driver_data;
        if (!ACM_READY(acm))
                return -EINVAL;
-       return !acm->ready_for_write ? 0 : acm->writesize;
+       /*
+        * Do not let the line discipline to know that we have a reserve,
+        * or it might get too enthusiastic.
+        */
+       return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0;
 }
 
 static int acm_tty_chars_in_buffer(struct tty_struct *tty)
@@ -393,7 +499,10 @@ static int acm_tty_chars_in_buffer(struct tty_struct *tty)
        struct acm *acm = tty->driver_data;
        if (!ACM_READY(acm))
                return -EINVAL;
-       return !acm->ready_for_write ? acm->writeurb->transfer_buffer_length : 0;
+       /*
+        * This is inaccurate (overcounts), but it works.
+        */
+       return (ACM_NWB - acm_wb_is_avail(acm)) * acm->writesize;
 }
 
 static void acm_tty_throttle(struct tty_struct *tty)
@@ -526,6 +635,39 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_
  * USB probe and disconnect routines.
  */
 
+/* Little helper: write buffers free */
+static void acm_write_buffers_free(struct acm *acm)
+{
+       int i;
+       struct acm_wb *wb;
+
+       for (wb = &acm->wb[0], i = 0; i < ACM_NWB; i++, wb++) {
+               usb_buffer_free(acm->dev, acm->writesize, wb->buf, wb->dmah);
+       }
+}
+
+/* Little helper: write buffers allocate */
+static int acm_write_buffers_alloc(struct acm *acm)
+{
+       int i;
+       struct acm_wb *wb;
+
+       for (wb = &acm->wb[0], i = 0; i < ACM_NWB; i++, wb++) {
+               wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
+                   &wb->dmah);
+               if (!wb->buf) {
+                       while (i != 0) {
+                               --i;
+                               --wb;
+                               usb_buffer_free(acm->dev, acm->writesize,
+                                   wb->buf, wb->dmah);
+                       }
+                       return -ENOMEM;
+               }
+       }
+       return 0;
+}
+
 static int acm_probe (struct usb_interface *intf,
                      const struct usb_device_id *id)
 {
@@ -700,7 +842,8 @@ skip_normal_probe:
        acm->bh.data = (unsigned long) acm;
        INIT_WORK(&acm->work, acm_softint, acm);
        spin_lock_init(&acm->throttle_lock);
-       acm->ready_for_write = 1;
+       spin_lock_init(&acm->write_lock);
+       acm->write_ready = 1;
 
        buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
        if (!buf) {
@@ -716,12 +859,10 @@ skip_normal_probe:
        }
        acm->read_buffer = buf;
 
-       buf = usb_buffer_alloc(usb_dev, acm->writesize, GFP_KERNEL, &acm->write_dma);
-       if (!buf) {
+       if (acm_write_buffers_alloc(acm) < 0) {
                dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
                goto alloc_fail4;
        }
-       acm->write_buffer = buf;        
 
        acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
        if (!acm->ctrlurb) {
@@ -750,9 +891,9 @@ skip_normal_probe:
        acm->readurb->transfer_dma = acm->read_dma;
 
        usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
-                         acm->write_buffer, acm->writesize, acm_write_bulk, acm);
+                         NULL, acm->writesize, acm_write_bulk, acm);
        acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP;
-       acm->writeurb->transfer_dma = acm->write_dma;
+       /* acm->writeurb->transfer_dma = 0; */
 
        dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
 
@@ -775,7 +916,7 @@ alloc_fail7:
 alloc_fail6:
        usb_free_urb(acm->ctrlurb);
 alloc_fail5:
-       usb_buffer_free(usb_dev, acm->writesize, acm->write_buffer, acm->write_dma);
+       acm_write_buffers_free(acm);
 alloc_fail4:
        usb_buffer_free(usb_dev, readsize, acm->read_buffer, acm->read_dma);
 alloc_fail3:
@@ -806,7 +947,7 @@ static void acm_disconnect(struct usb_interface *intf)
 
        flush_scheduled_work(); /* wait for acm_softint */
 
-       usb_buffer_free(usb_dev, acm->writesize, acm->write_buffer, acm->write_dma);
+       acm_write_buffers_free(acm);
        usb_buffer_free(usb_dev, acm->readsize, acm->read_buffer, acm->read_dma);
        usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
 
index 9009114..963a5df 100644 (file)
  * Internal driver structures.
  */
 
+/*
+ * The only reason to have several buffers is to accomodate assumptions
+ * in line disciplines. They ask for empty space amount, receive our URB size,
+ * and proceed to issue several 1-character writes, assuming they will fit.
+ * The very first write takes a complete URB. Fortunately, this only happens
+ * when processing onlcr, so we only need 2 buffers.
+ */
+#define ACM_NWB  2
+struct acm_wb {
+       unsigned char *buf;
+       dma_addr_t dmah;
+       int len;
+       int use;
+};
+
 struct acm {
        struct usb_device *dev;                         /* the corresponding usb device */
        struct usb_interface *control;                  /* control interface */
        struct usb_interface *data;                     /* data interface */
        struct tty_struct *tty;                         /* the corresponding tty */
        struct urb *ctrlurb, *readurb, *writeurb;       /* urbs */
-       u8 *ctrl_buffer, *read_buffer, *write_buffer;   /* buffers of urbs */
-       dma_addr_t ctrl_dma, read_dma, write_dma;       /* dma handles of buffers */
+       u8 *ctrl_buffer, *read_buffer;                  /* buffers of urbs */
+       dma_addr_t ctrl_dma, read_dma;                  /* dma handles of buffers */
+       struct acm_wb wb[ACM_NWB];
+       int write_current;                              /* current write buffer */
+       int write_used;                                 /* number of non-empty write buffers */
+       int write_ready;                                /* write urb is not running */
+       spinlock_t write_lock;
        struct usb_cdc_line_coding line;                /* bits, stop, parity */
        struct work_struct work;                        /* work queue entry for line discipline waking up */
        struct tasklet_struct bh;                       /* rx processing */
@@ -71,7 +91,6 @@ struct acm {
        unsigned int minor;                             /* acm minor number */
        unsigned char throttle;                         /* throttled by tty layer */
        unsigned char clocal;                           /* termios CLOCAL */
-       unsigned char ready_for_write;                  /* write urb can be used */
        unsigned char resubmit_to_unthrottle;           /* throtteling has disabled the read urb */
        unsigned int ctrl_caps;                         /* control capabilities from the class specific header */
 };
index bba22e9..7ce43fb 100644 (file)
@@ -379,6 +379,8 @@ static int usblp_open(struct inode *inode, struct file *file)
        usblp->writeurb->transfer_buffer_length = 0;
        usblp->wcomplete = 1; /* we begin writeable */
        usblp->rcomplete = 0;
+       usblp->writeurb->status = 0;
+       usblp->readurb->status = 0;
 
        if (usblp->bidir) {
                usblp->readcount = 0;
@@ -751,6 +753,7 @@ static ssize_t usblp_read(struct file *file, char __user *buffer, size_t count,
                                schedule();
                        } else {
                                set_current_state(TASK_RUNNING);
+                               down(&usblp->sem);
                                break;
                        }
                        down (&usblp->sem);
index 6bfab4b..787c27a 100644 (file)
@@ -784,16 +784,16 @@ static int proc_setconfig(struct dev_state *ps, void __user *arg)
                for (i = 0; i < actconfig->desc.bNumInterfaces; ++i) {
                        if (usb_interface_claimed(actconfig->interface[i])) {
                                dev_warn (&ps->dev->dev,
-                                       "usbfs: interface %d claimed "
+                                       "usbfs: interface %d claimed by %s "
                                        "while '%s' sets config #%d\n",
                                        actconfig->interface[i]
                                                ->cur_altsetting
                                                ->desc.bInterfaceNumber,
+                                       actconfig->interface[i]
+                                               ->dev.driver->name,
                                        current->comm, u);
-#if 0  /* FIXME:  enable in 2.6.10 or so */
                                status = -EBUSY;
                                break;
-#endif
                        }
                }
        }
index 0da2373..83e732a 100644 (file)
@@ -519,119 +519,120 @@ error:
 /*-------------------------------------------------------------------------*/
 
 /*
- * Root Hub interrupt transfers are synthesized with a timer.
- * Completions are called in_interrupt() but not in_irq().
+ * Root Hub interrupt transfers are polled using a timer if the
+ * driver requests it; otherwise the driver is responsible for
+ * calling usb_hcd_poll_rh_status() when an event occurs.
  *
- * Note: some root hubs (including common UHCI based designs) can't
- * correctly issue port change IRQs.  They're the ones that _need_ a
- * timer; most other root hubs don't.  Some systems could save a
- * lot of battery power by eliminating these root hub timer IRQs.
+ * Completions are called in_interrupt(), but they may or may not
+ * be in_irq().
  */
+void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
+{
+       struct urb      *urb;
+       int             length;
+       unsigned long   flags;
+       char            buffer[4];      /* Any root hubs with > 31 ports? */
 
-static void rh_report_status (unsigned long ptr);
+       if (!hcd->uses_new_polling && !hcd->status_urb)
+               return;
 
-static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb) 
-{
-       int     len = 1 + (urb->dev->maxchild / 8);
+       length = hcd->driver->hub_status_data(hcd, buffer);
+       if (length > 0) {
 
-       /* rh_timer protected by hcd_data_lock */
-       if (hcd->rh_timer.data || urb->transfer_buffer_length < len) {
-               dev_dbg (hcd->self.controller,
-                               "not queuing rh status urb, stat %d\n",
-                               urb->status);
-               return -EINVAL;
+               /* try to complete the status urb */
+               local_irq_save (flags);
+               spin_lock(&hcd_root_hub_lock);
+               urb = hcd->status_urb;
+               if (urb) {
+                       spin_lock(&urb->lock);
+                       if (urb->status == -EINPROGRESS) {
+                               hcd->poll_pending = 0;
+                               hcd->status_urb = NULL;
+                               urb->status = 0;
+                               urb->hcpriv = NULL;
+                               urb->actual_length = length;
+                               memcpy(urb->transfer_buffer, buffer, length);
+                       } else          /* urb has been unlinked */
+                               length = 0;
+                       spin_unlock(&urb->lock);
+               } else
+                       length = 0;
+               spin_unlock(&hcd_root_hub_lock);
+
+               /* local irqs are always blocked in completions */
+               if (length > 0)
+                       usb_hcd_giveback_urb (hcd, urb, NULL);
+               else
+                       hcd->poll_pending = 1;
+               local_irq_restore (flags);
        }
 
-       init_timer (&hcd->rh_timer);
-       hcd->rh_timer.function = rh_report_status;
-       hcd->rh_timer.data = (unsigned long) urb;
-       /* USB 2.0 spec says 256msec; this is close enough */
-       hcd->rh_timer.expires = jiffies + HZ/4;
-       add_timer (&hcd->rh_timer);
-       urb->hcpriv = hcd;      /* nonzero to indicate it's queued */
-       return 0;
+       /* The USB 2.0 spec says 256 ms.  This is close enough and won't
+        * exceed that limit if HZ is 100. */
+       if (hcd->uses_new_polling ? hcd->poll_rh :
+                       (length == 0 && hcd->status_urb != NULL))
+               mod_timer (&hcd->rh_timer, jiffies + msecs_to_jiffies(250));
 }
+EXPORT_SYMBOL_GPL(usb_hcd_poll_rh_status);
 
 /* timer callback */
+static void rh_timer_func (unsigned long _hcd)
+{
+       usb_hcd_poll_rh_status((struct usb_hcd *) _hcd);
+}
 
-static void rh_report_status (unsigned long ptr)
+/*-------------------------------------------------------------------------*/
+
+static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb)
 {
-       struct urb      *urb;
-       struct usb_hcd  *hcd;
-       int             length = 0;
+       int             retval;
        unsigned long   flags;
+       int             len = 1 + (urb->dev->maxchild / 8);
 
-       urb = (struct urb *) ptr;
-       local_irq_save (flags);
-       spin_lock (&urb->lock);
+       spin_lock_irqsave (&hcd_root_hub_lock, flags);
+       if (urb->status != -EINPROGRESS)        /* already unlinked */
+               retval = urb->status;
+       else if (hcd->status_urb || urb->transfer_buffer_length < len) {
+               dev_dbg (hcd->self.controller, "not queuing rh status urb\n");
+               retval = -EINVAL;
+       } else {
+               hcd->status_urb = urb;
+               urb->hcpriv = hcd;      /* indicate it's queued */
 
-       /* do nothing if the urb's been unlinked */
-       if (!urb->dev
-                       || urb->status != -EINPROGRESS
-                       || (hcd = urb->dev->bus->hcpriv) == NULL) {
-               spin_unlock (&urb->lock);
-               local_irq_restore (flags);
-               return;
-       }
+               if (!hcd->uses_new_polling)
+                       mod_timer (&hcd->rh_timer, jiffies +
+                                       msecs_to_jiffies(250));
 
-       /* complete the status urb, or retrigger the timer */
-       spin_lock (&hcd_data_lock);
-       if (urb->dev->state == USB_STATE_CONFIGURED) {
-               length = hcd->driver->hub_status_data (
-                                       hcd, urb->transfer_buffer);
-               if (length > 0) {
-                       hcd->rh_timer.data = 0;
-                       urb->actual_length = length;
-                       urb->status = 0;
-                       urb->hcpriv = NULL;
-               } else
-                       mod_timer (&hcd->rh_timer, jiffies + HZ/4);
+               /* If a status change has already occurred, report it ASAP */
+               else if (hcd->poll_pending)
+                       mod_timer (&hcd->rh_timer, jiffies);
+               retval = 0;
        }
-       spin_unlock (&hcd_data_lock);
-       spin_unlock (&urb->lock);
-
-       /* local irqs are always blocked in completions */
-       if (length > 0)
-               usb_hcd_giveback_urb (hcd, urb, NULL);
-       local_irq_restore (flags);
+       spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
+       return retval;
 }
 
-/*-------------------------------------------------------------------------*/
-
 static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
 {
-       if (usb_pipeint (urb->pipe)) {
-               int             retval;
-               unsigned long   flags;
-
-               spin_lock_irqsave (&hcd_data_lock, flags);
-               retval = rh_status_urb (hcd, urb);
-               spin_unlock_irqrestore (&hcd_data_lock, flags);
-               return retval;
-       }
+       if (usb_pipeint (urb->pipe))
+               return rh_queue_status (hcd, urb);
        if (usb_pipecontrol (urb->pipe))
                return rh_call_control (hcd, urb);
-       else
-               return -EINVAL;
+       return -EINVAL;
 }
 
 /*-------------------------------------------------------------------------*/
 
+/* Asynchronous unlinks of root-hub control URBs are legal, but they
+ * don't do anything.  Status URB unlinks must be made in process context
+ * with interrupts enabled.
+ */
 static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
 {
-       unsigned long   flags;
+       if (usb_pipeendpoint(urb->pipe) == 0) { /* Control URB */
+               if (in_interrupt())
+                       return 0;               /* nothing to do */
 
-       /* note:  always a synchronous unlink */
-       if ((unsigned long) urb == hcd->rh_timer.data) {
-               del_timer_sync (&hcd->rh_timer);
-               hcd->rh_timer.data = 0;
-
-               local_irq_save (flags);
-               urb->hcpriv = NULL;
-               usb_hcd_giveback_urb (hcd, urb, NULL);
-               local_irq_restore (flags);
-
-       } else if (usb_pipeendpoint(urb->pipe) == 0) {
                spin_lock_irq(&urb->lock);      /* from usb_kill_urb */
                ++urb->reject;
                spin_unlock_irq(&urb->lock);
@@ -642,8 +643,22 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
                spin_lock_irq(&urb->lock);
                --urb->reject;
                spin_unlock_irq(&urb->lock);
-       } else
-               return -EINVAL;
+
+       } else {                                /* Status URB */
+               if (!hcd->uses_new_polling)
+                       del_timer_sync (&hcd->rh_timer);
+               local_irq_disable ();
+               spin_lock (&hcd_root_hub_lock);
+               if (urb == hcd->status_urb) {
+                       hcd->status_urb = NULL;
+                       urb->hcpriv = NULL;
+               } else
+                       urb = NULL;             /* wasn't fully queued */
+               spin_unlock (&hcd_root_hub_lock);
+               if (urb)
+                       usb_hcd_giveback_urb (hcd, urb, NULL);
+               local_irq_enable ();
+       }
 
        return 0;
 }
@@ -817,30 +832,22 @@ static void usb_deregister_bus (struct usb_bus *bus)
 }
 
 /**
- * usb_hcd_register_root_hub - called by HCD to register its root hub 
+ * register_root_hub - called by usb_add_hcd() to register a root hub
  * @usb_dev: the usb root hub device to be registered.
  * @hcd: host controller for this root hub
  *
- * The USB host controller calls this function to register the root hub
- * properly with the USB subsystem.  It sets up the device properly in
- * the device tree and stores the root_hub pointer in the bus structure,
- * then calls usb_new_device() to register the usb device.  It also
- * assigns the root hub's USB address (always 1).
+ * This function registers the root hub with the USB subsystem.  It sets up
+ * the device properly in the device tree and stores the root_hub pointer
+ * in the bus structure, then calls usb_new_device() to register the usb
+ * device.  It also assigns the root hub's USB address (always 1).
  */
-int usb_hcd_register_root_hub (struct usb_device *usb_dev, struct usb_hcd *hcd)
+static int register_root_hub (struct usb_device *usb_dev,
+               struct usb_hcd *hcd)
 {
        struct device *parent_dev = hcd->self.controller;
        const int devnum = 1;
        int retval;
 
-       /* hcd->driver->start() reported can_wakeup, probably with
-        * assistance from board's boot firmware.
-        * NOTE:  normal devices won't enable wakeup by default.
-        */
-       if (hcd->can_wakeup)
-               dev_dbg (parent_dev, "supports USB remote wakeup\n");
-       hcd->remote_wakeup = hcd->can_wakeup;
-
        usb_dev->devnum = devnum;
        usb_dev->bus->devnum_next = devnum + 1;
        memset (&usb_dev->bus->devmap.devicemap, 0,
@@ -883,7 +890,16 @@ int usb_hcd_register_root_hub (struct usb_device *usb_dev, struct usb_hcd *hcd)
 
        return retval;
 }
-EXPORT_SYMBOL_GPL(usb_hcd_register_root_hub);
+
+void usb_enable_root_hub_irq (struct usb_bus *bus)
+{
+       struct usb_hcd *hcd;
+
+       hcd = container_of (bus, struct usb_hcd, self);
+       if (hcd->driver->hub_irq_enable && !hcd->poll_rh &&
+                       hcd->state != HC_STATE_HALT)
+               hcd->driver->hub_irq_enable (hcd);
+}
 
 
 /*-------------------------------------------------------------------------*/
@@ -1348,7 +1364,8 @@ hcd_endpoint_disable (struct usb_device *udev, struct usb_host_endpoint *ep)
 
        hcd = udev->bus->hcpriv;
 
-       WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT);
+       WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT &&
+                       udev->state != USB_STATE_NOTATTACHED);
 
        local_irq_disable ();
 
@@ -1612,6 +1629,8 @@ void usb_hc_died (struct usb_hcd *hcd)
 
        spin_lock_irqsave (&hcd_root_hub_lock, flags);
        if (hcd->rh_registered) {
+               hcd->poll_rh = 0;
+               del_timer(&hcd->rh_timer);
 
                /* make khubd clean up old urbs and devices */
                usb_set_device_state (hcd->self.root_hub,
@@ -1665,6 +1684,8 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
        hcd->self.bus_name = bus_name;
 
        init_timer(&hcd->rh_timer);
+       hcd->rh_timer.function = rh_timer_func;
+       hcd->rh_timer.data = (unsigned long) hcd;
 
        hcd->driver = driver;
        hcd->product_desc = (driver->product_desc) ? driver->product_desc :
@@ -1694,7 +1715,8 @@ EXPORT_SYMBOL (usb_put_hcd);
 int usb_add_hcd(struct usb_hcd *hcd,
                unsigned int irqnum, unsigned long irqflags)
 {
-       int     retval;
+       int retval;
+       struct usb_device *rhdev;
 
        dev_info(hcd->self.controller, "%s\n", hcd->product_desc);
 
@@ -1710,7 +1732,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
        }
 
        if ((retval = usb_register_bus(&hcd->self)) < 0)
-               goto err1;
+               goto err_register_bus;
 
        if (hcd->driver->irq) {
                char    buf[8], *bufp = buf;
@@ -1727,7 +1749,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
                                hcd->irq_descr, hcd)) != 0) {
                        dev_err(hcd->self.controller,
                                        "request interrupt %s failed\n", bufp);
-                       goto err2;
+                       goto err_request_irq;
                }
                hcd->irq = irqnum;
                dev_info(hcd->self.controller, "irq %s, %s 0x%08llx\n", bufp,
@@ -1743,19 +1765,55 @@ int usb_add_hcd(struct usb_hcd *hcd,
                                        (unsigned long long)hcd->rsrc_start);
        }
 
+       /* Allocate the root hub before calling hcd->driver->start(),
+        * but don't register it until afterward so that the hardware
+        * is running.
+        */
+       if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) {
+               dev_err(hcd->self.controller, "unable to allocate root hub\n");
+               retval = -ENOMEM;
+               goto err_allocate_root_hub;
+       }
+       rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH :
+                       USB_SPEED_FULL;
+
+       /* Although in principle hcd->driver->start() might need to use rhdev,
+        * none of the current drivers do.
+        */
        if ((retval = hcd->driver->start(hcd)) < 0) {
                dev_err(hcd->self.controller, "startup error %d\n", retval);
-               goto err3;
+               goto err_hcd_driver_start;
        }
 
+       /* hcd->driver->start() reported can_wakeup, probably with
+        * assistance from board's boot firmware.
+        * NOTE:  normal devices won't enable wakeup by default.
+        */
+       if (hcd->can_wakeup)
+               dev_dbg(hcd->self.controller, "supports USB remote wakeup\n");
+       hcd->remote_wakeup = hcd->can_wakeup;
+
+       if ((retval = register_root_hub(rhdev, hcd)) != 0)
+               goto err_register_root_hub;
+
+       if (hcd->uses_new_polling && hcd->poll_rh)
+               usb_hcd_poll_rh_status(hcd);
        return retval;
 
- err3:
+ err_register_root_hub:
+       hcd->driver->stop(hcd);
+
+ err_hcd_driver_start:
+       usb_put_dev(rhdev);
+
+ err_allocate_root_hub:
        if (hcd->irq >= 0)
                free_irq(irqnum, hcd);
- err2:
+
+ err_request_irq:
        usb_deregister_bus(&hcd->self);
- err1:
+
+ err_register_bus:
        hcd_buffer_destroy(hcd);
        return retval;
 } 
@@ -1782,6 +1840,9 @@ void usb_remove_hcd(struct usb_hcd *hcd)
        spin_unlock_irq (&hcd_root_hub_lock);
        usb_disconnect(&hcd->self.root_hub);
 
+       hcd->poll_rh = 0;
+       del_timer_sync(&hcd->rh_timer);
+
        hcd->driver->stop(hcd);
        hcd->state = HC_STATE_HALT;
 
index 325a516..8dc13cd 100644 (file)
@@ -65,7 +65,8 @@ struct usb_hcd {      /* usb_bus.hcpriv points to this */
        const char              *product_desc;  /* product/vendor string */
        char                    irq_descr[24];  /* driver + bus # */
 
-       struct timer_list       rh_timer;       /* drives root hub */
+       struct timer_list       rh_timer;       /* drives root-hub polling */
+       struct urb              *status_urb;    /* the current status urb */
 
        /*
         * hardware info/state
@@ -76,10 +77,17 @@ struct usb_hcd {    /* usb_bus.hcpriv points to this */
        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];
@@ -207,6 +215,8 @@ struct hc_driver {
        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);
@@ -243,7 +253,9 @@ void hcd_buffer_free (struct usb_bus *bus, size_t size,
 
 /* 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);
 
 /* -------------------------------------------------------------------------- */
 
@@ -341,9 +353,6 @@ extern long usb_calc_bus_time (int speed, int is_input,
 
 extern struct usb_bus *usb_alloc_bus (struct usb_operations *);
 
-extern int usb_hcd_register_root_hub (struct usb_device *usb_dev,
-               struct usb_hcd *hcd);
-
 extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd);
 
 extern void usb_set_device_state(struct usb_device *udev,
@@ -360,6 +369,8 @@ 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);
 
index a8d879a..32ff321 100644 (file)
@@ -643,15 +643,21 @@ static int hub_configure(struct usb_hub *hub,
                message = "can't get hub status";
                goto fail;
        }
-       cpu_to_le16s(&hubstatus);
-       if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
+       le16_to_cpus(&hubstatus);
+       if (hdev == hdev->bus->root_hub) {
+               struct usb_hcd *hcd =
+                               container_of(hdev->bus, struct usb_hcd, self);
+
+               hub->power_budget = min(500u, hcd->power_budget) / 2;
+       } else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
                dev_dbg(hub_dev, "hub controller current requirement: %dmA\n",
                        hub->descriptor->bHubContrCurrent);
                hub->power_budget = (501 - hub->descriptor->bHubContrCurrent)
                                        / 2;
+       }
+       if (hub->power_budget)
                dev_dbg(hub_dev, "%dmA bus power budget for children\n",
                        hub->power_budget * 2);
-       }
 
 
        ret = hub_hub_status(hub, &hubstatus, &hubchange);
@@ -1727,7 +1733,7 @@ static int finish_port_resume(struct usb_device *udev)
                        struct usb_driver       *driver;
 
                        intf = udev->actconfig->interface[i];
-                       if (intf->dev.power.power_state == PMSG_SUSPEND)
+                       if (intf->dev.power.power_state == PMSG_ON)
                                continue;
                        if (!intf->dev.driver) {
                                /* FIXME maybe force to alt 0 */
@@ -2787,6 +2793,11 @@ static void hub_events(void)
 
                hub->activating = 0;
 
+               /* If this is a root hub, tell the HCD it's okay to
+                * re-enable port-change interrupts now. */
+               if (!hdev->parent)
+                       usb_enable_root_hub_irq(hdev->bus);
+
 loop:
                usb_unlock_device(hdev);
                usb_put_intf(intf);
index d114b84..53bf564 100644 (file)
@@ -224,15 +224,4 @@ struct usb_hub {
        struct work_struct      leds;
 };
 
-/* use this for low-powered root hubs */
-static inline void
-hub_set_power_budget (struct usb_device *hubdev, unsigned mA)
-{
-       struct usb_hub  *hub;
-
-       hub = (struct usb_hub *)
-               usb_get_intfdata (hubdev->actconfig->interface[0]);
-       hub->power_budget = min(mA,(unsigned)500)/2;
-}
-
 #endif /* __LINUX_HUB_H */
index 3b24f9f..ff075a5 100644 (file)
@@ -53,6 +53,9 @@ config USB_GADGET_DEBUG_FILES
           driver on a new board.   Enable these files by choosing "Y"
           here.  If in doubt, or to conserve kernel memory, say "N".
 
+config USB_GADGET_SELECTED
+       boolean
+
 #
 # USB Peripheral Controller Support
 #
@@ -85,6 +88,7 @@ config USB_NET2280
        tristate
        depends on USB_GADGET_NET2280
        default USB_GADGET
+       select USB_GADGET_SELECTED
 
 config USB_GADGET_PXA2XX
        boolean "PXA 25x or IXP 4xx"
@@ -105,6 +109,7 @@ config USB_PXA2XX
        tristate
        depends on USB_GADGET_PXA2XX
        default USB_GADGET
+       select USB_GADGET_SELECTED
 
 # if there's only one gadget driver, using only two bulk endpoints,
 # don't waste memory for the other endpoints
@@ -134,6 +139,7 @@ config USB_GOKU
        tristate
        depends on USB_GADGET_GOKU
        default USB_GADGET
+       select USB_GADGET_SELECTED
 
 
 config USB_GADGET_LH7A40X
@@ -146,6 +152,7 @@ config USB_LH7A40X
        tristate
        depends on USB_GADGET_LH7A40X
        default USB_GADGET
+       select USB_GADGET_SELECTED
 
 
 config USB_GADGET_OMAP
@@ -167,6 +174,7 @@ config USB_OMAP
        tristate
        depends on USB_GADGET_OMAP
        default USB_GADGET
+       select USB_GADGET_SELECTED
 
 config USB_OTG
        boolean "OTG Support"
@@ -207,6 +215,7 @@ config USB_DUMMY_HCD
        tristate
        depends on USB_GADGET_DUMMY_HCD
        default USB_GADGET
+       select USB_GADGET_SELECTED
 
 # NOTE:  Please keep dummy_hcd LAST so that "real hardware" appears
 # first and will be selected by default.
@@ -226,7 +235,7 @@ config USB_GADGET_DUALSPEED
 #
 choice
        tristate "USB Gadget Drivers"
-       depends on USB_GADGET
+       depends on USB_GADGET && USB_GADGET_SELECTED
        default USB_ETH
        help
          A Linux "Gadget Driver" talks to the USB Peripheral Controller
index c039d2f..4d69267 100644 (file)
@@ -65,7 +65,7 @@
 
 
 #define DRIVER_DESC    "USB Host+Gadget Emulator"
-#define DRIVER_VERSION "17 Dec 2004"
+#define DRIVER_VERSION "02 May 2005"
 
 static const char      driver_name [] = "dummy_hcd";
 static const char      driver_desc [] = "USB Host+Gadget Emulator";
@@ -141,6 +141,8 @@ static const char *const ep_name [] = {
 };
 #define DUMMY_ENDPOINTS        (sizeof(ep_name)/sizeof(char *))
 
+/*-------------------------------------------------------------------------*/
+
 #define FIFO_SIZE              64
 
 struct urbp {
@@ -148,6 +150,13 @@ struct urbp {
        struct list_head        urbp_list;
 };
 
+
+enum dummy_rh_state {
+       DUMMY_RH_RESET,
+       DUMMY_RH_SUSPENDED,
+       DUMMY_RH_RUNNING
+};
+
 struct dummy {
        spinlock_t                      lock;
 
@@ -161,12 +170,18 @@ struct dummy {
        struct dummy_request            fifo_req;
        u8                              fifo_buf [FIFO_SIZE];
        u16                             devstatus;
+       unsigned                        udc_suspended:1;
+       unsigned                        pullup:1;
+       unsigned                        active:1;
+       unsigned                        old_active:1;
 
        /*
         * MASTER/HOST side support
         */
+       enum dummy_rh_state             rh_state;
        struct timer_list               timer;
        u32                             port_status;
+       u32                             old_status;
        unsigned                        resuming:1;
        unsigned long                   re_timeout;
 
@@ -189,6 +204,11 @@ static inline struct device *dummy_dev (struct dummy *dum)
        return dummy_to_hcd(dum)->self.controller;
 }
 
+static inline struct device *udc_dev (struct dummy *dum)
+{
+       return dum->gadget.dev.parent;
+}
+
 static inline struct dummy *ep_to_dummy (struct dummy_ep *ep)
 {
        return container_of (ep->gadget, struct dummy, gadget);
@@ -208,16 +228,98 @@ static struct dummy                       *the_controller;
 
 /*-------------------------------------------------------------------------*/
 
-/*
- * This "hardware" may look a bit odd in diagnostics since it's got both
- * host and device sides; and it binds different drivers to each side.
- */
-static struct platform_device          the_pdev;
+/* SLAVE/GADGET SIDE UTILITY ROUTINES */
 
-static struct device_driver dummy_driver = {
-       .name           = (char *) driver_name,
-       .bus            = &platform_bus_type,
-};
+/* called with spinlock held */
+static void nuke (struct dummy *dum, struct dummy_ep *ep)
+{
+       while (!list_empty (&ep->queue)) {
+               struct dummy_request    *req;
+
+               req = list_entry (ep->queue.next, struct dummy_request, queue);
+               list_del_init (&req->queue);
+               req->req.status = -ESHUTDOWN;
+
+               spin_unlock (&dum->lock);
+               req->req.complete (&ep->ep, &req->req);
+               spin_lock (&dum->lock);
+       }
+}
+
+/* caller must hold lock */
+static void
+stop_activity (struct dummy *dum)
+{
+       struct dummy_ep *ep;
+
+       /* prevent any more requests */
+       dum->address = 0;
+
+       /* The timer is left running so that outstanding URBs can fail */
+
+       /* nuke any pending requests first, so driver i/o is quiesced */
+       list_for_each_entry (ep, &dum->gadget.ep_list, ep.ep_list)
+               nuke (dum, ep);
+
+       /* driver now does any non-usb quiescing necessary */
+}
+
+/* caller must hold lock */
+static void
+set_link_state (struct dummy *dum)
+{
+       dum->active = 0;
+       if ((dum->port_status & USB_PORT_STAT_POWER) == 0)
+               dum->port_status = 0;
+
+       /* UDC suspend must cause a disconnect */
+       else if (!dum->pullup || dum->udc_suspended) {
+               dum->port_status &= ~(USB_PORT_STAT_CONNECTION |
+                                       USB_PORT_STAT_ENABLE |
+                                       USB_PORT_STAT_LOW_SPEED |
+                                       USB_PORT_STAT_HIGH_SPEED |
+                                       USB_PORT_STAT_SUSPEND);
+               if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0)
+                       dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16);
+       } else {
+               dum->port_status |= USB_PORT_STAT_CONNECTION;
+               if ((dum->old_status & USB_PORT_STAT_CONNECTION) == 0)
+                       dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16);
+               if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0)
+                       dum->port_status &= ~USB_PORT_STAT_SUSPEND;
+               else if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0 &&
+                               dum->rh_state != DUMMY_RH_SUSPENDED)
+                       dum->active = 1;
+       }
+
+       if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0 || dum->active)
+               dum->resuming = 0;
+
+       if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0 ||
+                       (dum->port_status & USB_PORT_STAT_RESET) != 0) {
+               if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0 &&
+                               (dum->old_status & USB_PORT_STAT_RESET) == 0 &&
+                               dum->driver) {
+                       stop_activity (dum);
+                       spin_unlock (&dum->lock);
+                       dum->driver->disconnect (&dum->gadget);
+                       spin_lock (&dum->lock);
+               }
+       } else if (dum->active != dum->old_active) {
+               if (dum->old_active && dum->driver->suspend) {
+                       spin_unlock (&dum->lock);
+                       dum->driver->suspend (&dum->gadget);
+                       spin_lock (&dum->lock);
+               } else if (!dum->old_active && dum->driver->resume) {
+                       spin_unlock (&dum->lock);
+                       dum->driver->resume (&dum->gadget);
+                       spin_lock (&dum->lock);
+               }
+       }
+
+       dum->old_status = dum->port_status;
+       dum->old_active = dum->active;
+}
 
 /*-------------------------------------------------------------------------*/
 
@@ -324,7 +426,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
        _ep->maxpacket = max;
        ep->desc = desc;
 
-       dev_dbg (dummy_dev(dum), "enabled %s (ep%d%s-%s) maxpacket %d\n",
+       dev_dbg (udc_dev(dum), "enabled %s (ep%d%s-%s) maxpacket %d\n",
                _ep->name,
                desc->bEndpointAddress & 0x0f,
                (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
@@ -345,22 +447,6 @@ done:
        return retval;
 }
 
-/* called with spinlock held */
-static void nuke (struct dummy *dum, struct dummy_ep *ep)
-{
-       while (!list_empty (&ep->queue)) {
-               struct dummy_request    *req;
-
-               req = list_entry (ep->queue.next, struct dummy_request, queue);
-               list_del_init (&req->queue);
-               req->req.status = -ESHUTDOWN;
-
-               spin_unlock (&dum->lock);
-               req->req.complete (&ep->ep, &req->req);
-               spin_lock (&dum->lock);
-       }
-}
-
 static int dummy_disable (struct usb_ep *_ep)
 {
        struct dummy_ep         *ep;
@@ -379,7 +465,7 @@ static int dummy_disable (struct usb_ep *_ep)
        nuke (dum, ep);
        spin_unlock_irqrestore (&dum->lock, flags);
 
-       dev_dbg (dummy_dev(dum), "disabled %s\n", _ep->name);
+       dev_dbg (udc_dev(dum), "disabled %s\n", _ep->name);
        return retval;
 }
 
@@ -474,7 +560,7 @@ dummy_queue (struct usb_ep *_ep, struct usb_request *_req, int mem_flags)
                return -ESHUTDOWN;
 
 #if 0
-       dev_dbg (dummy_dev(dum), "ep %p queue req %p to %s, len %d buf %p\n",
+       dev_dbg (udc_dev(dum), "ep %p queue req %p to %s, len %d buf %p\n",
                        ep, _req, _ep->name, _req->length, _req->buf);
 #endif
 
@@ -537,7 +623,7 @@ static int dummy_dequeue (struct usb_ep *_ep, struct usb_request *_req)
        spin_unlock_irqrestore (&dum->lock, flags);
 
        if (retval == 0) {
-               dev_dbg (dummy_dev(dum),
+               dev_dbg (udc_dev(dum),
                                "dequeued req %p from %s, len %d buf %p\n",
                                req, _ep->name, _req->length, _req->buf);
                _req->complete (_ep, _req);
@@ -601,13 +687,21 @@ static int dummy_wakeup (struct usb_gadget *_gadget)
        struct dummy    *dum;
 
        dum = gadget_to_dummy (_gadget);
-       if ((dum->devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) == 0
-                       || !(dum->port_status & (1 << USB_PORT_FEAT_SUSPEND)))
+       if (!(dum->devstatus &  ( (1 << USB_DEVICE_B_HNP_ENABLE)
+                               | (1 << USB_DEVICE_REMOTE_WAKEUP))))
                return -EINVAL;
+       if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0)
+               return -ENOLINK;
+       if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0 &&
+                        dum->rh_state != DUMMY_RH_SUSPENDED)
+               return -EIO;
+
+       /* FIXME: What if the root hub is suspended but the port isn't? */
 
        /* hub notices our request, issues downstream resume, etc */
        dum->resuming = 1;
-       dum->port_status |= (1 << USB_PORT_FEAT_C_SUSPEND);
+       dum->re_timeout = jiffies + msecs_to_jiffies(20);
+       mod_timer (&dummy_to_hcd (dum)->rh_timer, dum->re_timeout);
        return 0;
 }
 
@@ -623,10 +717,26 @@ static int dummy_set_selfpowered (struct usb_gadget *_gadget, int value)
        return 0;
 }
 
+static int dummy_pullup (struct usb_gadget *_gadget, int value)
+{
+       struct dummy    *dum;
+       unsigned long   flags;
+
+       dum = gadget_to_dummy (_gadget);
+       spin_lock_irqsave (&dum->lock, flags);
+       dum->pullup = (value != 0);
+       set_link_state (dum);
+       spin_unlock_irqrestore (&dum->lock, flags);
+
+       usb_hcd_poll_rh_status (dummy_to_hcd (dum));
+       return 0;
+}
+
 static const struct usb_gadget_ops dummy_ops = {
        .get_frame      = dummy_g_get_frame,
        .wakeup         = dummy_wakeup,
        .set_selfpowered = dummy_set_selfpowered,
+       .pullup         = dummy_pullup,
 };
 
 /*-------------------------------------------------------------------------*/
@@ -641,7 +751,7 @@ show_function (struct device *dev, struct device_attribute *attr, char *buf)
                return 0;
        return scnprintf (buf, PAGE_SIZE, "%s\n", dum->driver->function);
 }
-DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
+static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
 
 /*-------------------------------------------------------------------------*/
 
@@ -659,38 +769,6 @@ DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
  * for each driver that registers:  just add to a big root hub.
  */
 
-static void
-dummy_udc_release (struct device *dev)
-{
-}
-
-static void
-dummy_pdev_release (struct device *dev)
-{
-}
-
-static int
-dummy_register_udc (struct dummy *dum)
-{
-       int             rc;
-
-       strcpy (dum->gadget.dev.bus_id, "udc");
-       dum->gadget.dev.parent = dummy_dev(dum);
-       dum->gadget.dev.release = dummy_udc_release;
-
-       rc = device_register (&dum->gadget.dev);
-       if (rc == 0)
-               device_create_file (&dum->gadget.dev, &dev_attr_function);
-       return rc;
-}
-
-static void
-dummy_unregister_udc (struct dummy *dum)
-{
-       device_remove_file (&dum->gadget.dev, &dev_attr_function);
-       device_unregister (&dum->gadget.dev);
-}
-
 int
 usb_gadget_register_driver (struct usb_gadget_driver *driver)
 {
@@ -709,12 +787,8 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver)
         * SLAVE side init ... the layer above hardware, which
         * can't enumerate without help from the driver we're binding.
         */
-       dum->gadget.name = gadget_name;
-       dum->gadget.ops = &dummy_ops;
-       dum->gadget.is_dualspeed = 1;
 
        dum->devstatus = 0;
-       dum->resuming = 0;
 
        INIT_LIST_HEAD (&dum->gadget.ep_list);
        for (i = 0; i < DUMMY_ENDPOINTS; i++) {
@@ -740,7 +814,7 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver)
 
        dum->driver = driver;
        dum->gadget.dev.driver = &driver->driver;
-       dev_dbg (dummy_dev(dum), "binding gadget driver '%s'\n",
+       dev_dbg (udc_dev(dum), "binding gadget driver '%s'\n",
                        driver->driver.name);
        if ((retval = driver->bind (&dum->gadget)) != 0) {
                dum->driver = NULL;
@@ -748,42 +822,21 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver)
                return retval;
        }
 
-       // FIXME: Check these calls for errors and re-order
        driver->driver.bus = dum->gadget.dev.parent->bus;
        driver_register (&driver->driver);
-
        device_bind_driver (&dum->gadget.dev);
 
        /* khubd will enumerate this in a while */
-       dum->port_status |= USB_PORT_STAT_CONNECTION
-               | (1 << USB_PORT_FEAT_C_CONNECTION);
+       spin_lock_irq (&dum->lock);
+       dum->pullup = 1;
+       set_link_state (dum);
+       spin_unlock_irq (&dum->lock);
+
+       usb_hcd_poll_rh_status (dummy_to_hcd (dum));
        return 0;
 }
 EXPORT_SYMBOL (usb_gadget_register_driver);
 
-/* caller must hold lock */
-static void
-stop_activity (struct dummy *dum, struct usb_gadget_driver *driver)
-{
-       struct dummy_ep *ep;
-
-       /* prevent any more requests */
-       dum->address = 0;
-
-       /* The timer is left running so that outstanding URBs can fail */
-
-       /* nuke any pending requests first, so driver i/o is quiesced */
-       list_for_each_entry (ep, &dum->gadget.ep_list, ep.ep_list)
-               nuke (dum, ep);
-
-       /* driver now does any non-usb quiescing necessary */
-       if (driver) {
-               spin_unlock (&dum->lock);
-               driver->disconnect (&dum->gadget);
-               spin_lock (&dum->lock);
-       }
-}
-
 int
 usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
 {
@@ -795,35 +848,138 @@ usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
        if (!driver || driver != dum->driver)
                return -EINVAL;
 
-       dev_dbg (dummy_dev(dum), "unregister gadget driver '%s'\n",
+       dev_dbg (udc_dev(dum), "unregister gadget driver '%s'\n",
                        driver->driver.name);
 
        spin_lock_irqsave (&dum->lock, flags);
-       stop_activity (dum, driver);
-       dum->port_status &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE |
-                       USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED);
-       dum->port_status |= (1 << USB_PORT_FEAT_C_CONNECTION);
+       dum->pullup = 0;
+       set_link_state (dum);
        spin_unlock_irqrestore (&dum->lock, flags);
 
        driver->unbind (&dum->gadget);
        dum->driver = NULL;
 
        device_release_driver (&dum->gadget.dev);
-
        driver_unregister (&driver->driver);
 
+       spin_lock_irqsave (&dum->lock, flags);
+       dum->pullup = 0;
+       set_link_state (dum);
+       spin_unlock_irqrestore (&dum->lock, flags);
+
+       usb_hcd_poll_rh_status (dummy_to_hcd (dum));
        return 0;
 }
 EXPORT_SYMBOL (usb_gadget_unregister_driver);
 
 #undef is_enabled
 
+/* just declare this in any driver that really need it */
+extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode);
+
 int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode)
 {
        return -ENOSYS;
 }
 EXPORT_SYMBOL (net2280_set_fifo_mode);
 
+
+/* The gadget structure is stored inside the hcd structure and will be
+ * released along with it. */
+static void
+dummy_gadget_release (struct device *dev)
+{
+#if 0          /* usb_bus_put isn't EXPORTed! */
+       struct dummy    *dum = gadget_dev_to_dummy (dev);
+
+       usb_bus_put (&dummy_to_hcd (dum)->self);
+#endif
+}
+
+static int dummy_udc_probe (struct device *dev)
+{
+       struct dummy    *dum = the_controller;
+       int             rc;
+
+       dum->gadget.name = gadget_name;
+       dum->gadget.ops = &dummy_ops;
+       dum->gadget.is_dualspeed = 1;
+
+       /* maybe claim OTG support, though we won't complete HNP */
+       dum->gadget.is_otg = (dummy_to_hcd(dum)->self.otg_port != 0);
+
+       strcpy (dum->gadget.dev.bus_id, "gadget");
+       dum->gadget.dev.parent = dev;
+       dum->gadget.dev.release = dummy_gadget_release;
+       rc = device_register (&dum->gadget.dev);
+       if (rc < 0)
+               return rc;
+
+#if 0          /* usb_bus_get isn't EXPORTed! */
+       usb_bus_get (&dummy_to_hcd (dum)->self);
+#endif
+
+       dev_set_drvdata (dev, dum);
+       device_create_file (&dum->gadget.dev, &dev_attr_function);
+       return rc;
+}
+
+static int dummy_udc_remove (struct device *dev)
+{
+       struct dummy    *dum = dev_get_drvdata (dev);
+
+       dev_set_drvdata (dev, NULL);
+       device_remove_file (&dum->gadget.dev, &dev_attr_function);
+       device_unregister (&dum->gadget.dev);
+       return 0;
+}
+
+static int dummy_udc_suspend (struct device *dev, pm_message_t state,
+               u32 level)
+{
+       struct dummy    *dum = dev_get_drvdata(dev);
+
+       if (level != SUSPEND_DISABLE)
+               return 0;
+
+       dev_dbg (dev, "%s\n", __FUNCTION__);
+       spin_lock_irq (&dum->lock);
+       dum->udc_suspended = 1;
+       set_link_state (dum);
+       spin_unlock_irq (&dum->lock);
+
+       dev->power.power_state = state;
+       usb_hcd_poll_rh_status (dummy_to_hcd (dum));
+       return 0;
+}
+
+static int dummy_udc_resume (struct device *dev, u32 level)
+{
+       struct dummy    *dum = dev_get_drvdata(dev);
+
+       if (level != RESUME_ENABLE)
+               return 0;
+
+       dev_dbg (dev, "%s\n", __FUNCTION__);
+       spin_lock_irq (&dum->lock);
+       dum->udc_suspended = 0;
+       set_link_state (dum);
+       spin_unlock_irq (&dum->lock);
+
+       dev->power.power_state = PMSG_ON;
+       usb_hcd_poll_rh_status (dummy_to_hcd (dum));
+       return 0;
+}
+
+static struct device_driver dummy_udc_driver = {
+       .name           = (char *) gadget_name,
+       .bus            = &platform_bus_type,
+       .probe          = dummy_udc_probe,
+       .remove         = dummy_udc_remove,
+       .suspend        = dummy_udc_suspend,
+       .resume         = dummy_udc_resume,
+};
+
 /*-------------------------------------------------------------------------*/
 
 /* MASTER/HOST SIDE DRIVER
@@ -880,7 +1036,16 @@ static int dummy_urb_enqueue (
 
 static int dummy_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
 {
-       /* giveback happens automatically in timer callback */
+       struct dummy    *dum;
+       unsigned long   flags;
+
+       /* giveback happens automatically in timer callback,
+        * so make sure the callback happens */
+       dum = hcd_to_dummy (hcd);
+       spin_lock_irqsave (&dum->lock, flags);
+       if (dum->rh_state != DUMMY_RH_RUNNING && !list_empty(&dum->urbp_list))
+               mod_timer (&dum->timer, jiffies);
+       spin_unlock_irqrestore (&dum->lock, flags);
        return 0;
 }
 
@@ -1025,7 +1190,6 @@ static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep)
 
                /* high bandwidth mode */
                tmp = le16_to_cpu(ep->desc->wMaxPacketSize);
-               tmp = le16_to_cpu (tmp);
                tmp = (tmp >> 11) & 0x03;
                tmp *= 8 /* applies to entire frame */;
                limit += limit * tmp;
@@ -1123,7 +1287,8 @@ restart:
                if (urb->status != -EINPROGRESS) {
                        /* likely it was just unlinked */
                        goto return_urb;
-               }
+               } else if (dum->rh_state != DUMMY_RH_RUNNING)
+                       continue;
                type = usb_pipetype (urb->pipe);
 
                /* used up this frame's non-periodic bandwidth?
@@ -1168,12 +1333,14 @@ restart:
                        struct usb_ctrlrequest          setup;
                        int                             value = 1;
                        struct dummy_ep                 *ep2;
+                       unsigned                        w_index;
+                       unsigned                        w_value;
 
                        setup = *(struct usb_ctrlrequest*) urb->setup_packet;
-                       le16_to_cpus (&setup.wIndex);
-                       le16_to_cpus (&setup.wValue);
-                       le16_to_cpus (&setup.wLength);
-                       if (setup.wLength != urb->transfer_buffer_length) {
+                       w_index = le16_to_cpu(setup.wIndex);
+                       w_value = le16_to_cpu(setup.wValue);
+                       if (le16_to_cpu(setup.wLength) !=
+                                       urb->transfer_buffer_length) {
                                maybe_set_status (urb, -EOVERFLOW);
                                goto return_urb;
                        }
@@ -1182,7 +1349,7 @@ restart:
                        list_for_each_entry (req, &ep->queue, queue) {
                                list_del_init (&req->queue);
                                req->req.status = -EOVERFLOW;
-                               dev_dbg (dummy_dev(dum), "stale req = %p\n",
+                               dev_dbg (udc_dev(dum), "stale req = %p\n",
                                                req);
 
                                spin_unlock (&dum->lock);
@@ -1203,31 +1370,40 @@ restart:
                        case USB_REQ_SET_ADDRESS:
                                if (setup.bRequestType != Dev_Request)
                                        break;
-                               dum->address = setup.wValue;
+                               dum->address = w_value;
                                maybe_set_status (urb, 0);
-                               dev_dbg (dummy_dev(dum), "set_address = %d\n",
-                                               setup.wValue);
+                               dev_dbg (udc_dev(dum), "set_address = %d\n",
+                                               w_value);
                                value = 0;
                                break;
                        case USB_REQ_SET_FEATURE:
                                if (setup.bRequestType == Dev_Request) {
                                        value = 0;
-                                       switch (setup.wValue) {
+                                       switch (w_value) {
                                        case USB_DEVICE_REMOTE_WAKEUP:
                                                break;
+                                       case USB_DEVICE_B_HNP_ENABLE:
+                                               dum->gadget.b_hnp_enable = 1;
+                                               break;
+                                       case USB_DEVICE_A_HNP_SUPPORT:
+                                               dum->gadget.a_hnp_support = 1;
+                                               break;
+                                       case USB_DEVICE_A_ALT_HNP_SUPPORT:
+                                               dum->gadget.a_alt_hnp_support
+                                                       = 1;
+                                               break;
                                        default:
                                                value = -EOPNOTSUPP;
                                        }
                                        if (value == 0) {
                                                dum->devstatus |=
-                                                       (1 << setup.wValue);
+                                                       (1 << w_value);
                                                maybe_set_status (urb, 0);
                                        }
 
                                } else if (setup.bRequestType == Ep_Request) {
                                        // endpoint halt
-                                       ep2 = find_endpoint (dum,
-                                                       setup.wIndex);
+                                       ep2 = find_endpoint (dum, w_index);
                                        if (!ep2) {
                                                value = -EOPNOTSUPP;
                                                break;
@@ -1239,7 +1415,7 @@ restart:
                                break;
                        case USB_REQ_CLEAR_FEATURE:
                                if (setup.bRequestType == Dev_Request) {
-                                       switch (setup.wValue) {
+                                       switch (w_value) {
                                        case USB_DEVICE_REMOTE_WAKEUP:
                                                dum->devstatus &= ~(1 <<
                                                        USB_DEVICE_REMOTE_WAKEUP);
@@ -1252,8 +1428,7 @@ restart:
                                        }
                                } else if (setup.bRequestType == Ep_Request) {
                                        // endpoint halt
-                                       ep2 = find_endpoint (dum,
-                                                       setup.wIndex);
+                                       ep2 = find_endpoint (dum, w_index);
                                        if (!ep2) {
                                                value = -EOPNOTSUPP;
                                                break;
@@ -1279,7 +1454,7 @@ restart:
                                        if (urb->transfer_buffer_length > 0) {
                                                if (setup.bRequestType ==
                                                                Ep_InRequest) {
-       ep2 = find_endpoint (dum, setup.wIndex);
+       ep2 = find_endpoint (dum, w_index);
        if (!ep2) {
                value = -EOPNOTSUPP;
                break;
@@ -1321,7 +1496,7 @@ restart:
 
                        if (value < 0) {
                                if (value != -EOPNOTSUPP)
-                                       dev_dbg (dummy_dev(dum),
+                                       dev_dbg (udc_dev(dum),
                                                "setup --> %d\n",
                                                value);
                                maybe_set_status (urb, -EPIPE);
@@ -1377,12 +1552,12 @@ return_urb:
                goto restart;
        }
 
-       /* want a 1 msec delay here */
-       if (!list_empty (&dum->urbp_list))
-               mod_timer (&dum->timer, jiffies + msecs_to_jiffies(1));
-       else {
+       if (list_empty (&dum->urbp_list)) {
                usb_put_dev (dum->udev);
                dum->udev = NULL;
+       } else if (dum->rh_state == DUMMY_RH_RUNNING) {
+               /* want a 1 msec delay here */
+               mod_timer (&dum->timer, jiffies + msecs_to_jiffies(1));
        }
 
        spin_unlock_irqrestore (&dum->lock, flags);
@@ -1391,29 +1566,39 @@ return_urb:
 /*-------------------------------------------------------------------------*/
 
 #define PORT_C_MASK \
-        ((1 << USB_PORT_FEAT_C_CONNECTION) \
-       | (1 << USB_PORT_FEAT_C_ENABLE) \
-       | (1 << USB_PORT_FEAT_C_SUSPEND) \
-       | (1 << USB_PORT_FEAT_C_OVER_CURRENT) \
-       | (1 << USB_PORT_FEAT_C_RESET))
+       ((USB_PORT_STAT_C_CONNECTION \
+       | USB_PORT_STAT_C_ENABLE \
+       | USB_PORT_STAT_C_SUSPEND \
+       | USB_PORT_STAT_C_OVERCURRENT \
+       | USB_PORT_STAT_C_RESET) << 16)
 
 static int dummy_hub_status (struct usb_hcd *hcd, char *buf)
 {
        struct dummy            *dum;
        unsigned long           flags;
-       int                     retval;
+       int                     retval = 0;
 
        dum = hcd_to_dummy (hcd);
 
        spin_lock_irqsave (&dum->lock, flags);
-       if (!(dum->port_status & PORT_C_MASK))
-               retval = 0;
-       else {
+       if (hcd->state != HC_STATE_RUNNING)
+               goto done;
+
+       if (dum->resuming && time_after_eq (jiffies, dum->re_timeout)) {
+               dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16);
+               dum->port_status &= ~USB_PORT_STAT_SUSPEND;
+               set_link_state (dum);
+       }
+
+       if ((dum->port_status & PORT_C_MASK) != 0) {
                *buf = (1 << 1);
                dev_dbg (dummy_dev(dum), "port status 0x%08x has changes\n",
-                       dum->port_status);
+                               dum->port_status);
                retval = 1;
+               if (dum->rh_state == DUMMY_RH_SUSPENDED)
+                       usb_hcd_resume_root_hub (hcd);
        }
+done:
        spin_unlock_irqrestore (&dum->lock, flags);
        return retval;
 }
@@ -1424,7 +1609,8 @@ hub_descriptor (struct usb_hub_descriptor *desc)
        memset (desc, 0, sizeof *desc);
        desc->bDescriptorType = 0x29;
        desc->bDescLength = 9;
-       desc->wHubCharacteristics = __constant_cpu_to_le16 (0x0001);
+       desc->wHubCharacteristics = (__force __u16)
+                       (__constant_cpu_to_le16 (0x0001));
        desc->bNbrPorts = 1;
        desc->bitmap [0] = 0xff;
        desc->bitmap [1] = 0xff;
@@ -1442,6 +1628,9 @@ static int dummy_hub_control (
        int             retval = 0;
        unsigned long   flags;
 
+       if (hcd->state != HC_STATE_RUNNING)
+               return -ETIMEDOUT;
+
        dum = hcd_to_dummy (hcd);
        spin_lock_irqsave (&dum->lock, flags);
        switch (typeReq) {
@@ -1450,27 +1639,27 @@ static int dummy_hub_control (
        case ClearPortFeature:
                switch (wValue) {
                case USB_PORT_FEAT_SUSPEND:
-                       if (dum->port_status & (1 << USB_PORT_FEAT_SUSPEND)) {
+                       if (dum->port_status & USB_PORT_STAT_SUSPEND) {
                                /* 20msec resume signaling */
                                dum->resuming = 1;
                                dum->re_timeout = jiffies +
-                                                       msecs_to_jiffies(20);
+                                               msecs_to_jiffies(20);
                        }
                        break;
                case USB_PORT_FEAT_POWER:
-                       dum->port_status = 0;
-                       dum->resuming = 0;
-                       stop_activity(dum, dum->driver);
-                       break;
+                       if (dum->port_status & USB_PORT_STAT_POWER)
+                               dev_dbg (dummy_dev(dum), "power-off\n");
+                       /* FALLS THROUGH */
                default:
                        dum->port_status &= ~(1 << wValue);
+                       set_link_state (dum);
                }
                break;
        case GetHubDescriptor:
                hub_descriptor ((struct usb_hub_descriptor *) buf);
                break;
        case GetHubStatus:
-               *(u32 *) buf = __constant_cpu_to_le32 (0);
+               *(__le32 *) buf = __constant_cpu_to_le32 (0);
                break;
        case GetPortStatus:
                if (wIndex != 1)
@@ -1479,23 +1668,16 @@ static int dummy_hub_control (
                /* whoever resets or resumes must GetPortStatus to
                 * complete it!!
                 */
-               if (dum->resuming && time_after (jiffies, dum->re_timeout)) {
-                       dum->port_status |= (1 << USB_PORT_FEAT_C_SUSPEND);
-                       dum->port_status &= ~(1 << USB_PORT_FEAT_SUSPEND);
-                       dum->resuming = 0;
-                       dum->re_timeout = 0;
-                       if (dum->driver && dum->driver->resume) {
-                               spin_unlock (&dum->lock);
-                               dum->driver->resume (&dum->gadget);
-                               spin_lock (&dum->lock);
-                       }
+               if (dum->resuming &&
+                               time_after_eq (jiffies, dum->re_timeout)) {
+                       dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16);
+                       dum->port_status &= ~USB_PORT_STAT_SUSPEND;
                }
-               if ((dum->port_status & (1 << USB_PORT_FEAT_RESET)) != 0
-                               && time_after (jiffies, dum->re_timeout)) {
-                       dum->port_status |= (1 << USB_PORT_FEAT_C_RESET);
-                       dum->port_status &= ~(1 << USB_PORT_FEAT_RESET);
-                       dum->re_timeout = 0;
-                       if (dum->driver) {
+               if ((dum->port_status & USB_PORT_STAT_RESET) != 0 &&
+                               time_after_eq (jiffies, dum->re_timeout)) {
+                       dum->port_status |= (USB_PORT_STAT_C_RESET << 16);
+                       dum->port_status &= ~USB_PORT_STAT_RESET;
+                       if (dum->pullup) {
                                dum->port_status |= USB_PORT_STAT_ENABLE;
                                /* give it the best speed we agree on */
                                dum->gadget.speed = dum->driver->speed;
@@ -1516,8 +1698,9 @@ static int dummy_hub_control (
                                }
                        }
                }
-               ((u16 *) buf)[0] = cpu_to_le16 (dum->port_status);
-               ((u16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16);
+               set_link_state (dum);
+               ((__le16 *) buf)[0] = cpu_to_le16 (dum->port_status);
+               ((__le16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16);
                break;
        case SetHubFeature:
                retval = -EPIPE;
@@ -1525,36 +1708,37 @@ static int dummy_hub_control (
        case SetPortFeature:
                switch (wValue) {
                case USB_PORT_FEAT_SUSPEND:
-                       if ((dum->port_status & (1 << USB_PORT_FEAT_SUSPEND))
-                                       == 0) {
-                               dum->port_status |=
-                                               (1 << USB_PORT_FEAT_SUSPEND);
-                               if (dum->driver && dum->driver->suspend) {
-                                       spin_unlock (&dum->lock);
-                                       dum->driver->suspend (&dum->gadget);
-                                       spin_lock (&dum->lock);
-                               }
+                       if (dum->active) {
+                               dum->port_status |= USB_PORT_STAT_SUSPEND;
+
+                               /* HNP would happen here; for now we
+                                * assume b_bus_req is always true.
+                                */
+                               set_link_state (dum);
+                               if (((1 << USB_DEVICE_B_HNP_ENABLE)
+                                               & dum->devstatus) != 0)
+                                       dev_dbg (dummy_dev(dum),
+                                                       "no HNP yet!\n");
                        }
                        break;
+               case USB_PORT_FEAT_POWER:
+                       dum->port_status |= USB_PORT_STAT_POWER;
+                       set_link_state (dum);
+                       break;
                case USB_PORT_FEAT_RESET:
-                       /* if it's already running, disconnect first */
-                       if (dum->port_status & USB_PORT_STAT_ENABLE) {
-                               dum->port_status &= ~(USB_PORT_STAT_ENABLE
-                                               | USB_PORT_STAT_LOW_SPEED
-                                               | USB_PORT_STAT_HIGH_SPEED);
-                               if (dum->driver) {
-                                       dev_dbg (dummy_dev(dum),
-                                                       "disconnect\n");
-                                       stop_activity (dum, dum->driver);
-                               }
-
-                               /* FIXME test that code path! */
-                       }
+                       /* if it's already enabled, disable */
+                       dum->port_status &= ~(USB_PORT_STAT_ENABLE
+                                       | USB_PORT_STAT_LOW_SPEED
+                                       | USB_PORT_STAT_HIGH_SPEED);
+                       dum->devstatus = 0;
                        /* 50msec reset signaling */
                        dum->re_timeout = jiffies + msecs_to_jiffies(50);
-                       /* FALLTHROUGH */
+                       /* FALLTHROUGH */
                default:
-                       dum->port_status |= (1 << wValue);
+                       if ((dum->port_status & USB_PORT_STAT_POWER) != 0) {
+                               dum->port_status |= (1 << wValue);
+                               set_link_state (dum);
+                       }
                }
                break;
 
@@ -1567,9 +1751,35 @@ static int dummy_hub_control (
                retval = -EPIPE;
        }
        spin_unlock_irqrestore (&dum->lock, flags);
+
+       if ((dum->port_status & PORT_C_MASK) != 0)
+               usb_hcd_poll_rh_status (hcd);
        return retval;
 }
 
+static int dummy_hub_suspend (struct usb_hcd *hcd)
+{
+       struct dummy *dum = hcd_to_dummy (hcd);
+
+       spin_lock_irq (&dum->lock);
+       dum->rh_state = DUMMY_RH_SUSPENDED;
+       set_link_state (dum);
+       spin_unlock_irq (&dum->lock);
+       return 0;
+}
+
+static int dummy_hub_resume (struct usb_hcd *hcd)
+{
+       struct dummy *dum = hcd_to_dummy (hcd);
+
+       spin_lock_irq (&dum->lock);
+       dum->rh_state = DUMMY_RH_RUNNING;
+       set_link_state (dum);
+       if (!list_empty(&dum->urbp_list))
+               mod_timer (&dum->timer, jiffies);
+       spin_unlock_irq (&dum->lock);
+       return 0;
+}
 
 /*-------------------------------------------------------------------------*/
 
@@ -1625,8 +1835,6 @@ static DEVICE_ATTR (urbs, S_IRUGO, show_urbs, NULL);
 static int dummy_start (struct usb_hcd *hcd)
 {
        struct dummy            *dum;
-       struct usb_device       *root;
-       int                     retval;
 
        dum = hcd_to_dummy (hcd);
 
@@ -1639,38 +1847,22 @@ static int dummy_start (struct usb_hcd *hcd)
        init_timer (&dum->timer);
        dum->timer.function = dummy_timer;
        dum->timer.data = (unsigned long) dum;
+       dum->rh_state = DUMMY_RH_RUNNING;
 
        INIT_LIST_HEAD (&dum->urbp_list);
 
-       root = usb_alloc_dev (NULL, &hcd->self, 0);
-       if (!root)
-               return -ENOMEM;
-
-       /* root hub enters addressed state... */
-       hcd->state = HC_STATE_RUNNING;
-       root->speed = USB_SPEED_HIGH;
-
-       /* ...then configured, so khubd sees us. */
-       if ((retval = usb_hcd_register_root_hub (root, hcd)) != 0) {
-               goto err1;
-       }
-
        /* only show a low-power port: just 8mA */
-       hub_set_power_budget (root, 8);
+       hcd->power_budget = 8;
+       hcd->state = HC_STATE_RUNNING;
+       hcd->uses_new_polling = 1;
 
-       if ((retval = dummy_register_udc (dum)) != 0)
-               goto err2;
+#ifdef CONFIG_USB_OTG
+       hcd->self.otg_port = 1;
+#endif
 
        /* FIXME 'urbs' should be a per-device thing, maybe in usbcore */
        device_create_file (dummy_dev(dum), &dev_attr_urbs);
        return 0;
-
- err2:
-       usb_disconnect (&hcd->self.root_hub);
- err1:
-       usb_put_dev (root);
-       hcd->state = HC_STATE_QUIESCING;
-       return retval;
 }
 
 static void dummy_stop (struct usb_hcd *hcd)
@@ -1680,10 +1872,7 @@ static void dummy_stop (struct usb_hcd *hcd)
        dum = hcd_to_dummy (hcd);
 
        device_remove_file (dummy_dev(dum), &dev_attr_urbs);
-
        usb_gadget_unregister_driver (dum->driver);
-       dummy_unregister_udc (dum);
-
        dev_info (dummy_dev(dum), "stopped\n");
 }
 
@@ -1711,9 +1900,11 @@ static const struct hc_driver dummy_hcd = {
 
        .hub_status_data =      dummy_hub_status,
        .hub_control =          dummy_hub_control,
+       .hub_suspend =          dummy_hub_suspend,
+       .hub_resume =           dummy_hub_resume,
 };
 
-static int dummy_probe (struct device *dev)
+static int dummy_hcd_probe (struct device *dev)
 {
        struct usb_hcd          *hcd;
        int                     retval;
@@ -1733,7 +1924,7 @@ static int dummy_probe (struct device *dev)
        return retval;
 }
 
-static void dummy_remove (struct device *dev)
+static int dummy_hcd_remove (struct device *dev)
 {
        struct usb_hcd          *hcd;
 
@@ -1741,53 +1932,127 @@ static void dummy_remove (struct device *dev)
        usb_remove_hcd (hcd);
        usb_put_hcd (hcd);
        the_controller = NULL;
+       return 0;
 }
 
-/*-------------------------------------------------------------------------*/
-
-static int dummy_pdev_detect (void)
+static int dummy_hcd_suspend (struct device *dev, pm_message_t state,
+               u32 level)
 {
-       int                     retval;
+       struct usb_hcd          *hcd;
 
-       retval = driver_register (&dummy_driver);
-       if (retval < 0)
-               return retval;
+       if (level != SUSPEND_DISABLE)
+               return 0;
+
+       dev_dbg (dev, "%s\n", __FUNCTION__);
+       hcd = dev_get_drvdata (dev);
 
-       the_pdev.name = "hc";
-       the_pdev.dev.driver = &dummy_driver;
-       the_pdev.dev.release = dummy_pdev_release;
+#ifndef CONFIG_USB_SUSPEND
+       /* Otherwise this would never happen */
+       usb_lock_device (hcd->self.root_hub);
+       dummy_hub_suspend (hcd);
+       usb_unlock_device (hcd->self.root_hub);
+#endif
 
-       retval = platform_device_register (&the_pdev);
-       if (retval < 0)
-               driver_unregister (&dummy_driver);
-       return retval;
+       hcd->state = HC_STATE_SUSPENDED;
+       return 0;
 }
 
-static void dummy_pdev_remove (void)
+static int dummy_hcd_resume (struct device *dev, u32 level)
 {
-       platform_device_unregister (&the_pdev);
-       driver_unregister (&dummy_driver);
+       struct usb_hcd          *hcd;
+
+       if (level != RESUME_ENABLE)
+               return 0;
+
+       dev_dbg (dev, "%s\n", __FUNCTION__);
+       hcd = dev_get_drvdata (dev);
+       hcd->state = HC_STATE_RUNNING;
+
+#ifndef CONFIG_USB_SUSPEND
+       /* Otherwise this would never happen */
+       usb_lock_device (hcd->self.root_hub);
+       dummy_hub_resume (hcd);
+       usb_unlock_device (hcd->self.root_hub);
+#endif
+
+       usb_hcd_poll_rh_status (hcd);
+       return 0;
 }
 
+static struct device_driver dummy_hcd_driver = {
+       .name           = (char *) driver_name,
+       .bus            = &platform_bus_type,
+       .probe          = dummy_hcd_probe,
+       .remove         = dummy_hcd_remove,
+       .suspend        = dummy_hcd_suspend,
+       .resume         = dummy_hcd_resume,
+};
+
 /*-------------------------------------------------------------------------*/
 
+/* These don't need to do anything because the pdev structures are
+ * statically allocated. */
+static void
+dummy_udc_release (struct device *dev) {}
+
+static void
+dummy_hcd_release (struct device *dev) {}
+
+static struct platform_device          the_udc_pdev = {
+       .name           = (char *) gadget_name,
+       .id             = -1,
+       .dev            = {
+               .release        = dummy_udc_release,
+       },
+};
+
+static struct platform_device          the_hcd_pdev = {
+       .name           = (char *) driver_name,
+       .id             = -1,
+       .dev            = {
+               .release        = dummy_hcd_release,
+       },
+};
+
 static int __init init (void)
 {
        int     retval;
 
        if (usb_disabled ())
                return -ENODEV;
-       if ((retval = dummy_pdev_detect ()) != 0)
+
+       retval = driver_register (&dummy_hcd_driver);
+       if (retval < 0)
                return retval;
-       if ((retval = dummy_probe (&the_pdev.dev)) != 0)
-               dummy_pdev_remove ();
+
+       retval = driver_register (&dummy_udc_driver);
+       if (retval < 0)
+               goto err_register_udc_driver;
+
+       retval = platform_device_register (&the_hcd_pdev);
+       if (retval < 0)
+               goto err_register_hcd;
+
+       retval = platform_device_register (&the_udc_pdev);
+       if (retval < 0)
+               goto err_register_udc;
+       return retval;
+
+err_register_udc:
+       platform_device_unregister (&the_hcd_pdev);
+err_register_hcd:
+       driver_unregister (&dummy_udc_driver);
+err_register_udc_driver:
+       driver_unregister (&dummy_hcd_driver);
        return retval;
 }
 module_init (init);
 
 static void __exit cleanup (void)
 {
-       dummy_remove (&the_pdev.dev);
-       dummy_pdev_remove ();
+       platform_device_unregister (&the_udc_pdev);
+       platform_device_unregister (&the_hcd_pdev);
+       driver_unregister (&dummy_udc_driver);
+       driver_unregister (&dummy_hcd_driver);
 }
 module_exit (cleanup);
index 3f783cb..5bb53ae 100644 (file)
  */
 
 #define DRIVER_DESC            "Ethernet Gadget"
-#define DRIVER_VERSION         "Equinox 2004"
+#define DRIVER_VERSION         "May Day 2005"
 
 static const char shortname [] = "ether";
 static const char driver_desc [] = DRIVER_DESC;
 
 #define RX_EXTRA       20              /* guard against rx overflows */
 
-#ifdef CONFIG_USB_ETH_RNDIS
 #include "rndis.h"
-#else
-#define rndis_init() 0
-#define rndis_exit() do{}while(0)
+
+#ifndef        CONFIG_USB_ETH_RNDIS
+#define rndis_uninit(x)                do{}while(0)
+#define rndis_deregister(c)    do{}while(0)
+#define rndis_exit()           do{}while(0)
 #endif
 
 /* CDC and RNDIS support the same host-chosen outgoing packet filters. */
@@ -140,9 +141,6 @@ struct eth_dev {
  * It also ASSUMES a self-powered device, without remote wakeup,
  * although remote wakeup support would make sense.
  */
-static const char *EP_IN_NAME;
-static const char *EP_OUT_NAME;
-static const char *EP_STATUS_NAME;
 
 /*-------------------------------------------------------------------------*/
 
@@ -312,6 +310,7 @@ static inline int rndis_active(struct eth_dev *dev)
 #define        FS_BPS          (19 *  64 * 1 * 1000 * 8)
 
 #ifdef CONFIG_USB_GADGET_DUALSPEED
+#define        DEVSPEED        USB_SPEED_HIGH
 
 static unsigned qmult = 5;
 module_param (qmult, uint, S_IRUGO|S_IWUSR);
@@ -330,6 +329,8 @@ static inline int BITRATE(struct usb_gadget *g)
 }
 
 #else  /* full speed (low speed doesn't do bulk) */
+#define        DEVSPEED        USB_SPEED_FULL
+
 #define qlen(gadget) DEFAULT_QLEN
 
 static inline int BITRATE(struct usb_gadget *g)
@@ -395,7 +396,8 @@ static inline int BITRATE(struct usb_gadget *g)
 #define STRING_SUBSET                  8
 #define STRING_RNDIS                   9
 
-#define USB_BUFSIZ     256             /* holds our biggest descriptor */
+/* holds our biggest descriptor (or RNDIS response) */
+#define USB_BUFSIZ     256
 
 /*
  * This device advertises one configuration, eth_config, unless RNDIS
@@ -538,7 +540,7 @@ static const struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = {
        .bDataInterface =       0x01,
 };
 
-static struct usb_cdc_acm_descriptor acm_descriptor = {
+static const struct usb_cdc_acm_descriptor acm_descriptor = {
        .bLength =              sizeof acm_descriptor,
        .bDescriptorType =      USB_DT_CS_INTERFACE,
        .bDescriptorSubType =   USB_CDC_ACM_TYPE,
@@ -846,7 +848,7 @@ static const struct usb_descriptor_header *hs_rndis_function [] = {
 #else
 
 /* if there's no high speed support, maxpacket doesn't change. */
-#define ep_desc(g,hs,fs) fs
+#define ep_desc(g,hs,fs) (((void)(g)), (fs))
 
 static inline void __init hs_subset_descriptors(void)
 {
@@ -946,10 +948,31 @@ config_buf (enum usb_device_speed speed,
 static void eth_start (struct eth_dev *dev, int gfp_flags);
 static int alloc_requests (struct eth_dev *dev, unsigned n, int gfp_flags);
 
-#ifdef DEV_CONFIG_CDC
-static inline int ether_alt_ep_setup (struct eth_dev *dev, struct usb_ep *ep)
+static int
+set_ether_config (struct eth_dev *dev, int gfp_flags)
 {
-       const struct usb_endpoint_descriptor    *d;
+       int                                     result = 0;
+       struct usb_gadget                       *gadget = dev->gadget;
+
+       /* status endpoint used for RNDIS and (optionally) CDC */
+       if (!subset_active(dev) && dev->status_ep) {
+               dev->status = ep_desc (gadget, &hs_status_desc,
+                                               &fs_status_desc);
+               dev->status_ep->driver_data = dev;
+
+               result = usb_ep_enable (dev->status_ep, dev->status);
+               if (result != 0) {
+                       DEBUG (dev, "enable %s --> %d\n", 
+                               dev->status_ep->name, result);
+                       goto done;
+               }
+       }
+
+       dev->in = ep_desc (dev->gadget, &hs_source_desc, &fs_source_desc);
+       dev->in_ep->driver_data = dev;
+
+       dev->out = ep_desc (dev->gadget, &hs_sink_desc, &fs_sink_desc);
+       dev->out_ep->driver_data = dev;
 
        /* With CDC,  the host isn't allowed to use these two data
         * endpoints in the default altsetting for the interface.
@@ -959,135 +982,33 @@ static inline int ether_alt_ep_setup (struct eth_dev *dev, struct usb_ep *ep)
         * a side effect of setting a packet filter.  Deactivation is
         * from REMOTE_NDIS_HALT_MSG, reset from REMOTE_NDIS_RESET_MSG.
         */
-
-       /* one endpoint writes data back IN to the host */
-       if (strcmp (ep->name, EP_IN_NAME) == 0) {
-               d = ep_desc (dev->gadget, &hs_source_desc, &fs_source_desc);
-               ep->driver_data = dev;
-               dev->in = d;
-
-       /* one endpoint just reads OUT packets */
-       } else if (strcmp (ep->name, EP_OUT_NAME) == 0) {
-               d = ep_desc (dev->gadget, &hs_sink_desc, &fs_sink_desc);
-               ep->driver_data = dev;
-               dev->out = d;
-
-       /* optional status/notification endpoint */
-       } else if (EP_STATUS_NAME &&
-                       strcmp (ep->name, EP_STATUS_NAME) == 0) {
-               int                     result;
-
-               d = ep_desc (dev->gadget, &hs_status_desc, &fs_status_desc);
-               result = usb_ep_enable (ep, d);
-               if (result < 0)
-                       return result;
-
-               ep->driver_data = dev;
-               dev->status = d;
-       }
-       return 0;
-}
-#endif
-
-#if    defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS)
-static inline int ether_ep_setup (struct eth_dev *dev, struct usb_ep *ep)
-{
-       int                                     result;
-       const struct usb_endpoint_descriptor    *d;
-
-       /* CDC subset is simpler:  if the device is there,
-        * it's live with rx and tx endpoints.
-        *
-        * Do this as a shortcut for RNDIS too.
-        */
-
-       /* one endpoint writes data back IN to the host */
-       if (strcmp (ep->name, EP_IN_NAME) == 0) {
-               d = ep_desc (dev->gadget, &hs_source_desc, &fs_source_desc);
-               result = usb_ep_enable (ep, d);
-               if (result < 0)
-                       return result;
-
-               ep->driver_data = dev;
-               dev->in = d;
-
-       /* one endpoint just reads OUT packets */
-       } else if (strcmp (ep->name, EP_OUT_NAME) == 0) {
-               d = ep_desc (dev->gadget, &hs_sink_desc, &fs_sink_desc);
-               result = usb_ep_enable (ep, d);
-               if (result < 0)
-                       return result;
-
-               ep->driver_data = dev;
-               dev->out = d;
-       }
-
-       return 0;
-}
-#endif
-
-static int
-set_ether_config (struct eth_dev *dev, int gfp_flags)
-{
-       int                     result = 0;
-       struct usb_ep           *ep;
-       struct usb_gadget       *gadget = dev->gadget;
-
-       gadget_for_each_ep (ep, gadget) {
-#ifdef DEV_CONFIG_CDC
-               if (!dev->rndis && dev->cdc) {
-                       result = ether_alt_ep_setup (dev, ep);
-                       if (result == 0)
-                               continue;
+       if (!cdc_active(dev)) {
+               result = usb_ep_enable (dev->in_ep, dev->in);
+               if (result != 0) {
+                       DEBUG(dev, "enable %s --> %d\n", 
+                               dev->in_ep->name, result);
+                       goto done;
                }
-#endif
-
-#ifdef CONFIG_USB_ETH_RNDIS
-               if (dev->rndis && strcmp (ep->name, EP_STATUS_NAME) == 0) {
-                       const struct usb_endpoint_descriptor    *d;
-                       d = ep_desc (gadget, &hs_status_desc, &fs_status_desc);
-                       result = usb_ep_enable (ep, d);
-                       if (result == 0) {
-                               ep->driver_data = dev;
-                               dev->status = d;
-                               continue;
-                       }
-               } else
-#endif
 
-               {
-#if    defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS)
-                       result = ether_ep_setup (dev, ep);
-                       if (result == 0)
-                               continue;
-#endif
+               result = usb_ep_enable (dev->out_ep, dev->out);
+               if (result != 0) {
+                       DEBUG (dev, "enable %s --> %d\n", 
+                               dev->in_ep->name, result);
+                       goto done;
                }
-
-               /* stop on error */
-               ERROR (dev, "can't enable %s, result %d\n", ep->name, result);
-               break;
        }
-       if (!result && (!dev->in_ep || !dev->out_ep))
-               result = -ENODEV;
 
+done:
        if (result == 0)
                result = alloc_requests (dev, qlen (gadget), gfp_flags);
 
        /* on error, disable any endpoints  */
        if (result < 0) {
-#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
-               if (dev->status)
+               if (!subset_active(dev))
                        (void) usb_ep_disable (dev->status_ep);
-#endif
                dev->status = NULL;
-#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS)
-               if (dev->rndis || !dev->cdc) {
-                       if (dev->in)
-                               (void) usb_ep_disable (dev->in_ep);
-                       if (dev->out)
-                               (void) usb_ep_disable (dev->out_ep);
-               }
-#endif
+               (void) usb_ep_disable (dev->in_ep);
+               (void) usb_ep_disable (dev->out_ep);
                dev->in = NULL;
                dev->out = NULL;
        } else
@@ -1095,8 +1016,7 @@ set_ether_config (struct eth_dev *dev, int gfp_flags)
        /* activate non-CDC configs right away
         * this isn't strictly according to the RNDIS spec
         */
-#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS)
-       if (dev->rndis || !dev->cdc) {
+       if (!cdc_active (dev)) {
                netif_carrier_on (dev->net);
                if (netif_running (dev->net)) {
                        spin_unlock (&dev->lock);
@@ -1104,7 +1024,6 @@ set_ether_config (struct eth_dev *dev, int gfp_flags)
                        spin_lock (&dev->lock);
                }
        }
-#endif
 
        if (result == 0)
                DEBUG (dev, "qlen %d\n", qlen (gadget));
@@ -1124,6 +1043,7 @@ static void eth_reset_config (struct eth_dev *dev)
 
        netif_stop_queue (dev->net);
        netif_carrier_off (dev->net);
+       rndis_uninit(dev->rndis_config);
 
        /* disable endpoints, forcing (synchronous) completion of
         * pending i/o.  then free the requests.
@@ -1150,6 +1070,8 @@ static void eth_reset_config (struct eth_dev *dev)
        if (dev->status) {
                usb_ep_disable (dev->status_ep);
        }
+       dev->rndis = 0;
+       dev->cdc_filter = 0;
        dev->config = 0;
 }
 
@@ -1162,9 +1084,6 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags)
        int                     result = 0;
        struct usb_gadget       *gadget = dev->gadget;
 
-       if (number == dev->config)
-               return 0;
-
        if (gadget_is_sa1100 (gadget)
                        && dev->config
                        && atomic_read (&dev->tx_qlen) != 0) {
@@ -1174,12 +1093,8 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags)
        }
        eth_reset_config (dev);
 
-       /* default:  pass all packets, no multicast filtering */
-       dev->cdc_filter = DEFAULT_FILTER;
-
        switch (number) {
        case DEV_CONFIG_VALUE:
-               dev->rndis = 0;
                result = set_ether_config (dev, gfp_flags);
                break;
 #ifdef CONFIG_USB_ETH_RNDIS
@@ -1218,9 +1133,9 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags)
                dev->config = number;
                INFO (dev, "%s speed config #%d: %d mA, %s, using %s\n",
                                speed, number, power, driver_desc,
-                               dev->rndis
+                               rndis_active(dev)
                                        ? "RNDIS"
-                                       : (dev->cdc
+                                       : (cdc_active(dev)
                                                ? "CDC Ethernet"
                                                : "CDC Ethernet Subset"));
        }
@@ -1231,6 +1146,13 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags)
 
 #ifdef DEV_CONFIG_CDC
 
+/* The interrupt endpoint is used in CDC networking models (Ethernet, ATM)
+ * only to notify the host about link status changes (which we support) or
+ * report completion of some encapsulated command (as used in RNDIS).  Since
+ * we want this CDC Ethernet code to be vendor-neutral, we don't use that
+ * command mechanism; and only one status request is ever queued.
+ */
+
 static void eth_status_complete (struct usb_ep *ep, struct usb_request *req)
 {
        struct usb_cdc_notification     *event = req->buf;
@@ -1259,7 +1181,7 @@ static void eth_status_complete (struct usb_ep *ep, struct usb_request *req)
        } else if (value != -ECONNRESET)
                DEBUG (dev, "event %02x --> %d\n",
                        event->bNotificationType, value);
-       event->bmRequestType = 0xff;
+       req->context = NULL;
 }
 
 static void issue_start_status (struct eth_dev *dev)
@@ -1276,6 +1198,8 @@ static void issue_start_status (struct eth_dev *dev)
         * a "cancel the whole queue" primitive since any
         * unlink-one primitive has way too many error modes.
         * here, we "know" toggle is already clear...
+        *
+        * FIXME iff req->context != null just dequeue it
         */
        usb_ep_disable (dev->status_ep);
        usb_ep_enable (dev->status_ep, dev->status);
@@ -1292,6 +1216,8 @@ static void issue_start_status (struct eth_dev *dev)
 
        req->length = sizeof *event;
        req->complete = eth_status_complete;
+       req->context = dev;
+
        value = usb_ep_queue (dev->status_ep, req, GFP_ATOMIC);
        if (value < 0)
                DEBUG (dev, "status buf queue --> %d\n", value);
@@ -1351,9 +1277,9 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
        struct eth_dev          *dev = get_gadget_data (gadget);
        struct usb_request      *req = dev->req;
        int                     value = -EOPNOTSUPP;
-       u16                     wIndex = (__force u16) ctrl->wIndex;
-       u16                     wValue = (__force u16) ctrl->wValue;
-       u16                     wLength = (__force u16) ctrl->wLength;
+       u16                     wIndex = le16_to_cpu(ctrl->wIndex);
+       u16                     wValue = le16_to_cpu(ctrl->wValue);
+       u16                     wLength = le16_to_cpu(ctrl->wLength);
 
        /* descriptors just go into the pre-allocated ep0 buffer,
         * while config change events may enable network traffic.
@@ -1424,7 +1350,7 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
                                || !dev->config
                                || wIndex > 1)
                        break;
-               if (!dev->cdc && wIndex != 0)
+               if (!cdc_active(dev) && wIndex != 0)
                        break;
                spin_lock (&dev->lock);
 
@@ -1456,9 +1382,11 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 
                        /* CDC requires the data transfers not be done from
                         * the default interface setting ... also, setting
-                        * the non-default interface clears filters etc.
+                        * the non-default interface resets filters etc.
                         */
                        if (wValue == 1) {
+                               if (!cdc_active (dev))
+                                       break;
                                usb_ep_enable (dev->in_ep, dev->in);
                                usb_ep_enable (dev->out_ep, dev->out);
                                dev->cdc_filter = DEFAULT_FILTER;
@@ -1492,11 +1420,11 @@ done_set_intf:
                                || !dev->config
                                || wIndex > 1)
                        break;
-               if (!(dev->cdc || dev->rndis) && wIndex != 0)
+               if (!(cdc_active(dev) || rndis_active(dev)) && wIndex != 0)
                        break;
 
                /* for CDC, iff carrier is on, data interface is active. */
-               if (dev->rndis || wIndex != 1)
+               if (rndis_active(dev) || wIndex != 1)
                        *(u8 *)req->buf = 0;
                else
                        *(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0;
@@ -1509,8 +1437,7 @@ done_set_intf:
                 * wValue = packet filter bitmap
                 */
                if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE)
-                               || !dev->cdc
-                               || dev->rndis
+                               || !cdc_active(dev)
                                || wLength != 0
                                || wIndex > 1)
                        break;
@@ -1534,7 +1461,7 @@ done_set_intf:
         */
        case USB_CDC_SEND_ENCAPSULATED_COMMAND:
                if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE)
-                               || !dev->rndis
+                               || !rndis_active(dev)
                                || wLength > USB_BUFSIZ
                                || wValue
                                || rndis_control_intf.bInterfaceNumber
@@ -1549,7 +1476,7 @@ done_set_intf:
        case USB_CDC_GET_ENCAPSULATED_RESPONSE:
                if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)
                                        == ctrl->bRequestType
-                               && dev->rndis
+                               && rndis_active(dev)
                                // && wLength >= 0x0400
                                && !wValue
                                && rndis_control_intf.bInterfaceNumber
@@ -1688,10 +1615,8 @@ rx_submit (struct eth_dev *dev, struct usb_request *req, int gfp_flags)
         */
        size = (sizeof (struct ethhdr) + dev->net->mtu + RX_EXTRA);
        size += dev->out_ep->maxpacket - 1;
-#ifdef CONFIG_USB_ETH_RNDIS
-       if (dev->rndis)
+       if (rndis_active(dev))
                size += sizeof (struct rndis_packet_msg_type);
-#endif 
        size -= size % dev->out_ep->maxpacket;
 
        if ((skb = alloc_skb (size + NET_IP_ALIGN, gfp_flags)) == 0) {
@@ -1735,11 +1660,9 @@ static void rx_complete (struct usb_ep *ep, struct usb_request *req)
        /* normal completion */
        case 0:
                skb_put (skb, req->actual);
-#ifdef CONFIG_USB_ETH_RNDIS
                /* we know MaxPacketsPerTransfer == 1 here */
-               if (dev->rndis)
+               if (rndis_active(dev))
                        status = rndis_rm_hdr (skb);
-#endif
                if (status < 0
                                || ETH_HLEN > skb->len
                                || skb->len > ETH_FRAME_LEN) {
@@ -1859,8 +1782,6 @@ static void rx_fill (struct eth_dev *dev, int gfp_flags)
        struct usb_request      *req;
        unsigned long           flags;
 
-       clear_bit (WORK_RX_MEMORY, &dev->todo);
-
        /* fill unused rxq slots with some skb */
        spin_lock_irqsave (&dev->lock, flags);
        while (!list_empty (&dev->rx_reqs)) {
@@ -1883,11 +1804,9 @@ static void eth_work (void *_dev)
 {
        struct eth_dev          *dev = _dev;
 
-       if (test_bit (WORK_RX_MEMORY, &dev->todo)) {
+       if (test_and_clear_bit (WORK_RX_MEMORY, &dev->todo)) {
                if (netif_running (dev->net))
                        rx_fill (dev, GFP_KERNEL);
-               else
-                       clear_bit (WORK_RX_MEMORY, &dev->todo);
        }
 
        if (dev->todo)
@@ -1971,8 +1890,7 @@ static int eth_start_xmit (struct sk_buff *skb, struct net_device *net)
         * or the hardware can't use skb buffers.
         * or there's not enough space for any RNDIS headers we need
         */
-#ifdef CONFIG_USB_ETH_RNDIS
-       if (dev->rndis) {
+       if (rndis_active(dev)) {
                struct sk_buff  *skb_rndis;
 
                skb_rndis = skb_realloc_headroom (skb,
@@ -1985,7 +1903,6 @@ static int eth_start_xmit (struct sk_buff *skb, struct net_device *net)
                rndis_add_hdr (skb);
                length = skb->len;
        }
-#endif
        req->buf = skb->data;
        req->context = skb;
        req->complete = tx_complete;
@@ -2018,9 +1935,7 @@ static int eth_start_xmit (struct sk_buff *skb, struct net_device *net)
        }
 
        if (retval) {
-#ifdef CONFIG_USB_ETH_RNDIS
 drop:
-#endif
                dev->stats.tx_dropped++;
                dev_kfree_skb_any (skb);
                spin_lock_irqsave (&dev->lock, flags);
@@ -2036,27 +1951,31 @@ drop:
 
 #ifdef CONFIG_USB_ETH_RNDIS
 
-static void rndis_send_media_state (struct eth_dev *dev, int connect)
-{
-       if (!dev)
-               return;
-       
-       if (connect) {
-               if (rndis_signal_connect (dev->rndis_config))
-                       return;
-       } else {
-               if (rndis_signal_disconnect (dev->rndis_config))
-                       return;
-       }
-}
+/* The interrupt endpoint is used in RNDIS to notify the host when messages
+ * other than data packets are available ... notably the REMOTE_NDIS_*_CMPLT
+ * messages, but also REMOTE_NDIS_INDICATE_STATUS_MSG and potentially even
+ * REMOTE_NDIS_KEEPALIVE_MSG.
+ *
+ * The RNDIS control queue is processed by GET_ENCAPSULATED_RESPONSE, and
+ * normally just one notification will be queued.
+ */
+
+static struct usb_request *eth_req_alloc (struct usb_ep *, unsigned, unsigned);
+static void eth_req_free (struct usb_ep *ep, struct usb_request *req);
 
 static void
 rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req)
 {
+       struct eth_dev          *dev = ep->driver_data;
+
        if (req->status || req->actual != req->length)
-               DEBUG ((struct eth_dev *) ep->driver_data,
+               DEBUG (dev,
                        "rndis control ack complete --> %d, %d/%d\n",
                        req->status, req->actual, req->length);
+       req->context = NULL;
+
+       if (req != dev->stat_req)
+               eth_req_free(ep, req);
 }
 
 static int rndis_control_ack (struct net_device *net)
@@ -2071,11 +1990,19 @@ static int rndis_control_ack (struct net_device *net)
                return -ENODEV;
        }
 
+       /* in case queue length > 1 */
+       if (resp->context) {
+               resp = eth_req_alloc (dev->status_ep, 8, GFP_ATOMIC);
+               if (!resp)
+                       return -ENOMEM;
+       }
+
        /* Send RNDIS RESPONSE_AVAILABLE notification;
         * USB_CDC_NOTIFY_RESPONSE_AVAILABLE should work too
         */
        resp->length = 8;
        resp->complete = rndis_control_ack_complete;
+       resp->context = dev;
        
        *((__le32 *) resp->buf) = __constant_cpu_to_le32 (1);
        *((__le32 *) resp->buf + 1) = __constant_cpu_to_le32 (0);
@@ -2089,6 +2016,10 @@ static int rndis_control_ack (struct net_device *net)
        return 0;
 }
 
+#else
+
+#define        rndis_control_ack       NULL
+
 #endif /* RNDIS */
 
 static void eth_start (struct eth_dev *dev, int gfp_flags)
@@ -2101,14 +2032,12 @@ static void eth_start (struct eth_dev *dev, int gfp_flags)
        /* and open the tx floodgates */ 
        atomic_set (&dev->tx_qlen, 0);
        netif_wake_queue (dev->net);
-#ifdef CONFIG_USB_ETH_RNDIS
-       if (dev->rndis) {
+       if (rndis_active(dev)) {
                rndis_set_param_medium (dev->rndis_config,
                                        NDIS_MEDIUM_802_3,
                                        BITRATE(dev->gadget)/100);
-               rndis_send_media_state (dev, 1);
+               (void) rndis_signal_connect (dev->rndis_config);
        }
-#endif 
 }
 
 static int eth_open (struct net_device *net)
@@ -2149,28 +2078,27 @@ static int eth_stop (struct net_device *net)
                }
        }
        
-#ifdef CONFIG_USB_ETH_RNDIS
-       if (dev->rndis) {
+       if (rndis_active(dev)) {
                rndis_set_param_medium (dev->rndis_config,
                                        NDIS_MEDIUM_802_3, 0);
-               rndis_send_media_state (dev, 0);
+               (void) rndis_signal_disconnect (dev->rndis_config);
        }
-#endif
 
        return 0;
 }
 
 /*-------------------------------------------------------------------------*/
 
-static struct usb_request *eth_req_alloc (struct usb_ep *ep, unsigned size)
+static struct usb_request *
+eth_req_alloc (struct usb_ep *ep, unsigned size, unsigned gfp_flags)
 {
        struct usb_request      *req;
 
-       req = usb_ep_alloc_request (ep, GFP_KERNEL);
+       req = usb_ep_alloc_request (ep, gfp_flags);
        if (!req)
                return NULL;
 
-       req->buf = kmalloc (size, GFP_KERNEL);
+       req->buf = kmalloc (size, gfp_flags);
        if (!req->buf) {
                usb_ep_free_request (ep, req);
                req = NULL;
@@ -2192,10 +2120,8 @@ eth_unbind (struct usb_gadget *gadget)
        struct eth_dev          *dev = get_gadget_data (gadget);
 
        DEBUG (dev, "unbind\n");
-#ifdef CONFIG_USB_ETH_RNDIS
        rndis_deregister (dev->rndis_config);
        rndis_exit ();
-#endif
 
        /* we've already been disconnected ... no i/o is active */
        if (dev->req) {
@@ -2368,13 +2294,11 @@ autoconf_fail:
                        gadget->name);
                return -ENODEV;
        }
-       EP_IN_NAME = in_ep->name;
        in_ep->driver_data = in_ep;     /* claim */
        
        out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc);
        if (!out_ep)
                goto autoconf_fail;
-       EP_OUT_NAME = out_ep->name;
        out_ep->driver_data = out_ep;   /* claim */
 
 #if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
@@ -2384,7 +2308,6 @@ autoconf_fail:
        if (cdc || rndis) {
                status_ep = usb_ep_autoconfig (gadget, &fs_status_desc);
                if (status_ep) {
-                       EP_STATUS_NAME = status_ep->name;
                        status_ep->driver_data = status_ep;     /* claim */
                } else if (rndis) {
                        dev_err (&gadget->dev,
@@ -2426,7 +2349,7 @@ autoconf_fail:
        hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
        hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
 #if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
-       if (EP_STATUS_NAME)
+       if (status_ep)
                hs_status_desc.bEndpointAddress =
                                fs_status_desc.bEndpointAddress;
 #endif
@@ -2499,20 +2422,23 @@ autoconf_fail:
        SET_ETHTOOL_OPS(net, &ops);
 
        /* preallocate control message data and buffer */
-       dev->req = eth_req_alloc (gadget->ep0, USB_BUFSIZ);
+       dev->req = eth_req_alloc (gadget->ep0, USB_BUFSIZ, GFP_KERNEL);
        if (!dev->req)
                goto fail;
        dev->req->complete = eth_setup_complete;
 
        /* ... and maybe likewise for status transfer */
+#ifdef DEV_CONFIG_CDC
        if (dev->status_ep) {
                dev->stat_req = eth_req_alloc (dev->status_ep,
-                                       STATUS_BYTECOUNT);
+                                       STATUS_BYTECOUNT, GFP_KERNEL);
                if (!dev->stat_req) {
                        eth_req_free (gadget->ep0, dev->req);
                        goto fail;
                }
+               dev->stat_req->context = NULL;
        }
+#endif
 
        /* finish hookup to lower layer ... */
        dev->gadget = gadget;
@@ -2526,16 +2452,16 @@ autoconf_fail:
        netif_stop_queue (dev->net);
        netif_carrier_off (dev->net);
 
-       // SET_NETDEV_DEV (dev->net, &gadget->dev);
+       SET_NETDEV_DEV (dev->net, &gadget->dev);
        status = register_netdev (dev->net);
        if (status < 0)
                goto fail1;
 
        INFO (dev, "%s, version: " DRIVER_VERSION "\n", driver_desc);
        INFO (dev, "using %s, OUT %s IN %s%s%s\n", gadget->name,
-               EP_OUT_NAME, EP_IN_NAME,
-               EP_STATUS_NAME ? " STATUS " : "",
-               EP_STATUS_NAME ? EP_STATUS_NAME : ""
+               out_ep->name, in_ep->name,
+               status_ep ? " STATUS " : "",
+               status_ep ? status_ep->name : ""
                );
        INFO (dev, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
                net->dev_addr [0], net->dev_addr [1],
@@ -2548,7 +2474,6 @@ autoconf_fail:
                        dev->host_mac [2], dev->host_mac [3],
                        dev->host_mac [4], dev->host_mac [5]);
 
-#ifdef CONFIG_USB_ETH_RNDIS
        if (rndis) {
                u32     vendorID = 0;
 
@@ -2565,7 +2490,7 @@ fail0:
                /* these set up a lot of the OIDs that RNDIS needs */
                rndis_set_host_mac (dev->rndis_config, dev->host_mac);
                if (rndis_set_param_dev (dev->rndis_config, dev->net,
-                                        &dev->stats))
+                                        &dev->stats, &dev->cdc_filter))
                        goto fail0;
                if (rndis_set_param_vendor (dev->rndis_config, vendorID,
                                            manufacturer))
@@ -2576,7 +2501,6 @@ fail0:
                        goto fail0;
                INFO (dev, "RNDIS ready\n");
        }
-#endif 
 
        return status;
 
@@ -2610,11 +2534,8 @@ eth_resume (struct usb_gadget *gadget)
 /*-------------------------------------------------------------------------*/
 
 static struct usb_gadget_driver eth_driver = {
-#ifdef CONFIG_USB_GADGET_DUALSPEED
-       .speed          = USB_SPEED_HIGH,
-#else
-       .speed          = USB_SPEED_FULL,
-#endif
+       .speed          = DEVSPEED,
+
        .function       = (char *) driver_desc,
        .bind           = eth_bind,
        .unbind         = eth_unbind,
index a9be851..4f57085 100644 (file)
  *     removable               Default false, boolean for removable media
  *     luns=N                  Default N = number of filenames, number of
  *                                     LUNs to support
+ *     stall                   Default determined according to the type of
+ *                                     USB device controller (usually true),
+ *                                     boolean to permit the driver to halt
+ *                                     bulk endpoints
  *     transport=XXX           Default BBB, transport name (CB, CBI, or BBB)
  *     protocol=YYY            Default SCSI, protocol name (RBC, 8020 or
  *                                     ATAPI, QIC, UFI, 8070, or SCSI;
  *     buflen=N                Default N=16384, buffer size used (will be
  *                                     rounded down to a multiple of
  *                                     PAGE_CACHE_SIZE)
- *     stall                   Default determined according to the type of
- *                                     USB device controller (usually true),
- *                                     boolean to permit the driver to halt
- *                                     bulk endpoints
  *
  * If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "ro",
- * "removable", and "luns" options are available; default values are used
- * for everything else.
+ * "removable", "luns", and "stall" options are available; default values
+ * are used for everything else.
  *
  * The pathnames of the backing files and the ro settings are available in
  * the attribute files "file" and "ro" in the lun<n> subdirectory of the
@@ -342,14 +342,15 @@ static struct {
        int             num_ros;
        unsigned int    nluns;
 
+       int             removable;
+       int             can_stall;
+
        char            *transport_parm;
        char            *protocol_parm;
-       int             removable;
        unsigned short  vendor;
        unsigned short  product;
        unsigned short  release;
        unsigned int    buflen;
-       int             can_stall;
 
        int             transport_type;
        char            *transport_name;
@@ -360,11 +361,11 @@ static struct {
        .transport_parm         = "BBB",
        .protocol_parm          = "SCSI",
        .removable              = 0,
+       .can_stall              = 1,
        .vendor                 = DRIVER_VENDOR_ID,
        .product                = DRIVER_PRODUCT_ID,
        .release                = 0xffff,       // Use controller chip type
        .buflen                 = 16384,
-       .can_stall              = 1,
        };
 
 
@@ -380,6 +381,9 @@ MODULE_PARM_DESC(luns, "number of LUNs");
 module_param_named(removable, mod_data.removable, bool, S_IRUGO);
 MODULE_PARM_DESC(removable, "true to simulate removable media");
 
+module_param_named(stall, mod_data.can_stall, bool, S_IRUGO);
+MODULE_PARM_DESC(stall, "false to prevent bulk stalls");
+
 
 /* In the non-TEST version, only the module parameters listed above
  * are available. */
@@ -404,9 +408,6 @@ MODULE_PARM_DESC(release, "USB release number");
 module_param_named(buflen, mod_data.buflen, uint, S_IRUGO);
 MODULE_PARM_DESC(buflen, "I/O buffer size");
 
-module_param_named(stall, mod_data.can_stall, bool, S_IRUGO);
-MODULE_PARM_DESC(stall, "false to prevent bulk stalls");
-
 #endif /* CONFIG_USB_FILE_STORAGE_TEST */
 
 
@@ -818,7 +819,7 @@ static void inline put_be32(u8 *buf, u32 val)
        buf[0] = val >> 24;
        buf[1] = val >> 16;
        buf[2] = val >> 8;
-       buf[3] = val;
+       buf[3] = val & 0xff;
 }
 
 
@@ -1276,8 +1277,8 @@ static int class_setup_req(struct fsg_dev *fsg,
 {
        struct usb_request      *req = fsg->ep0req;
        int                     value = -EOPNOTSUPP;
-       u16                     w_index = ctrl->wIndex;
-       u16                     w_length = ctrl->wLength;
+       u16                     w_index = le16_to_cpu(ctrl->wIndex);
+       u16                     w_length = le16_to_cpu(ctrl->wLength);
 
        if (!fsg->config)
                return value;
@@ -1312,7 +1313,7 @@ static int class_setup_req(struct fsg_dev *fsg,
                        }
                        VDBG(fsg, "get max LUN\n");
                        *(u8 *) req->buf = fsg->nluns - 1;
-                       value = min(w_length, (u16) 1);
+                       value = 1;
                        break;
                }
        }
@@ -1344,7 +1345,7 @@ static int class_setup_req(struct fsg_dev *fsg,
                        "unknown class-specific control req "
                        "%02x.%02x v%04x i%04x l%u\n",
                        ctrl->bRequestType, ctrl->bRequest,
-                       ctrl->wValue, w_index, w_length);
+                       le16_to_cpu(ctrl->wValue), w_index, w_length);
        return value;
 }
 
@@ -1358,9 +1359,8 @@ static int standard_setup_req(struct fsg_dev *fsg,
 {
        struct usb_request      *req = fsg->ep0req;
        int                     value = -EOPNOTSUPP;
-       u16                     w_index = ctrl->wIndex;
-       u16                     w_value = ctrl->wValue;
-       u16                     w_length = ctrl->wLength;
+       u16                     w_index = le16_to_cpu(ctrl->wIndex);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
 
        /* Usually this just stores reply data in the pre-allocated ep0 buffer,
         * but config change events will also reconfigure hardware. */
@@ -1374,7 +1374,7 @@ static int standard_setup_req(struct fsg_dev *fsg,
 
                case USB_DT_DEVICE:
                        VDBG(fsg, "get device descriptor\n");
-                       value = min(w_length, (u16) sizeof device_desc);
+                       value = sizeof device_desc;
                        memcpy(req->buf, &device_desc, value);
                        break;
 #ifdef CONFIG_USB_GADGET_DUALSPEED
@@ -1382,7 +1382,7 @@ static int standard_setup_req(struct fsg_dev *fsg,
                        VDBG(fsg, "get device qualifier\n");
                        if (!fsg->gadget->is_dualspeed)
                                break;
-                       value = min(w_length, (u16) sizeof dev_qualifier);
+                       value = sizeof dev_qualifier;
                        memcpy(req->buf, &dev_qualifier, value);
                        break;
 
@@ -1401,8 +1401,6 @@ static int standard_setup_req(struct fsg_dev *fsg,
                                        req->buf,
                                        w_value >> 8,
                                        w_value & 0xff);
-                       if (value >= 0)
-                               value = min(w_length, (u16) value);
                        break;
 
                case USB_DT_STRING:
@@ -1411,8 +1409,6 @@ static int standard_setup_req(struct fsg_dev *fsg,
                        /* wIndex == language code */
                        value = usb_gadget_get_string(&stringtab,
                                        w_value & 0xff, req->buf);
-                       if (value >= 0)
-                               value = min(w_length, (u16) value);
                        break;
                }
                break;
@@ -1438,7 +1434,7 @@ static int standard_setup_req(struct fsg_dev *fsg,
                        break;
                VDBG(fsg, "get configuration\n");
                *(u8 *) req->buf = fsg->config;
-               value = min(w_length, (u16) 1);
+               value = 1;
                break;
 
        case USB_REQ_SET_INTERFACE:
@@ -1466,14 +1462,14 @@ static int standard_setup_req(struct fsg_dev *fsg,
                }
                VDBG(fsg, "get interface\n");
                *(u8 *) req->buf = 0;
-               value = min(w_length, (u16) 1);
+               value = 1;
                break;
 
        default:
                VDBG(fsg,
                        "unknown control req %02x.%02x v%04x i%04x l%u\n",
                        ctrl->bRequestType, ctrl->bRequest,
-                       w_value, w_index, w_length);
+                       w_value, w_index, le16_to_cpu(ctrl->wLength));
        }
 
        return value;
@@ -1485,6 +1481,7 @@ static int fsg_setup(struct usb_gadget *gadget,
 {
        struct fsg_dev          *fsg = get_gadget_data(gadget);
        int                     rc;
+       int                     w_length = le16_to_cpu(ctrl->wLength);
 
        ++fsg->ep0_req_tag;             // Record arrival of a new request
        fsg->ep0req->context = NULL;
@@ -1498,9 +1495,9 @@ static int fsg_setup(struct usb_gadget *gadget,
 
        /* Respond with data/status or defer until later? */
        if (rc >= 0 && rc != DELAYED_STATUS) {
+               rc = min(rc, w_length);
                fsg->ep0req->length = rc;
-               fsg->ep0req->zero = (rc < ctrl->wLength &&
-                               (rc % gadget->ep0->maxpacket) == 0);
+               fsg->ep0req->zero = rc < w_length;
                fsg->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ?
                                "ep0-in" : "ep0-out");
                rc = ep0_queue(fsg);
@@ -2660,7 +2657,7 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size,
                }
        }
 
-       /* Check that the LUN values are oonsistent */
+       /* Check that the LUN values are consistent */
        if (transport_is_bbb()) {
                if (fsg->lun != lun)
                        DBG(fsg, "using LUN %d from CBW, "
index 005db7c..ed773a9 100644 (file)
@@ -70,7 +70,7 @@ MODULE_LICENSE("GPL");
  * seem to behave quite as expected.  Used by default.
  *
  * OUT dma documents design problems handling the common "short packet"
- * transfer termination policy; it couldn't enabled by default, even
+ * transfer termination policy; it couldn't be enabled by default, even
  * if the OUT-dma abort problems had a resolution.
  */
 static unsigned use_dma = 1;
@@ -313,7 +313,7 @@ goku_free_request(struct usb_ep *_ep, struct usb_request *_req)
 #if    defined(CONFIG_X86)
 #define USE_KMALLOC
 
-#elif  defined(CONFIG_MIPS) && !defined(CONFIG_NONCOHERENT_IO)
+#elif  defined(CONFIG_MIPS) && !defined(CONFIG_DMA_NONCOHERENT)
 #define USE_KMALLOC
 
 #elif  defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE)
@@ -1524,9 +1524,12 @@ static void ep0_setup(struct goku_udc *dev)
        /* read SETUP packet and enter DATA stage */
        ctrl.bRequestType = readl(&regs->bRequestType);
        ctrl.bRequest = readl(&regs->bRequest);
-       ctrl.wValue  = (readl(&regs->wValueH)  << 8) | readl(&regs->wValueL);
-       ctrl.wIndex  = (readl(&regs->wIndexH)  << 8) | readl(&regs->wIndexL);
-       ctrl.wLength = (readl(&regs->wLengthH) << 8) | readl(&regs->wLengthL);
+       ctrl.wValue  = cpu_to_le16((readl(&regs->wValueH)  << 8)
+                                       | readl(&regs->wValueL));
+       ctrl.wIndex  = cpu_to_le16((readl(&regs->wIndexH)  << 8)
+                                       | readl(&regs->wIndexL));
+       ctrl.wLength = cpu_to_le16((readl(&regs->wLengthH) << 8)
+                                       | readl(&regs->wLengthL));
        writel(0, &regs->SetupRecv);
 
        nuke(&dev->ep[0], 0);
@@ -1548,18 +1551,20 @@ static void ep0_setup(struct goku_udc *dev)
                case USB_REQ_CLEAR_FEATURE:
                        switch (ctrl.bRequestType) {
                        case USB_RECIP_ENDPOINT:
-                               tmp = ctrl.wIndex & 0x0f;
+                               tmp = le16_to_cpu(ctrl.wIndex) & 0x0f;
                                /* active endpoint */
                                if (tmp > 3 || (!dev->ep[tmp].desc && tmp != 0))
                                        goto stall;
-                               if (ctrl.wIndex & USB_DIR_IN) {
+                               if (ctrl.wIndex & __constant_cpu_to_le16(
+                                               USB_DIR_IN)) {
                                        if (!dev->ep[tmp].is_in)
                                                goto stall;
                                } else {
                                        if (dev->ep[tmp].is_in)
                                                goto stall;
                                }
-                               if (ctrl.wValue != USB_ENDPOINT_HALT)
+                               if (ctrl.wValue != __constant_cpu_to_le16(
+                                               USB_ENDPOINT_HALT))
                                        goto stall;
                                if (tmp)
                                        goku_clear_halt(&dev->ep[tmp]);
@@ -1571,7 +1576,7 @@ succeed:
                                return;
                        case USB_RECIP_DEVICE:
                                /* device remote wakeup: always clear */
-                               if (ctrl.wValue != 1)
+                               if (ctrl.wValue != __constant_cpu_to_le16(1))
                                        goto stall;
                                VDBG(dev, "clear dev remote wakeup\n");
                                goto succeed;
@@ -1589,14 +1594,15 @@ succeed:
 #ifdef USB_TRACE
        VDBG(dev, "SETUP %02x.%02x v%04x i%04x l%04x\n",
                ctrl.bRequestType, ctrl.bRequest,
-               ctrl.wValue, ctrl.wIndex, ctrl.wLength);
+               le16_to_cpu(ctrl.wValue), le16_to_cpu(ctrl.wIndex),
+               le16_to_cpu(ctrl.wLength));
 #endif
 
        /* hw wants to know when we're configured (or not) */
        dev->req_config = (ctrl.bRequest == USB_REQ_SET_CONFIGURATION
                                && ctrl.bRequestType == USB_RECIP_DEVICE);
        if (unlikely(dev->req_config))
-               dev->configured = (ctrl.wValue != 0);
+               dev->configured = (ctrl.wValue != __constant_cpu_to_le16(0));
 
        /* delegate everything to the gadget driver.
         * it may respond after this irq handler returns.
index 1e5e6dd..0208153 100644 (file)
@@ -417,8 +417,8 @@ ep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
                goto free1;
 
        value = ep_io (data, kbuf, len);
-       VDEBUG (data->dev, "%s read %d OUT, status %d\n",
-               data->name, len, value);
+       VDEBUG (data->dev, "%s read %zu OUT, status %d\n",
+               data->name, len, (int) value);
        if (value >= 0 && copy_to_user (buf, kbuf, value))
                value = -EFAULT;
 
@@ -465,8 +465,8 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
        }
 
        value = ep_io (data, kbuf, len);
-       VDEBUG (data->dev, "%s write %d IN, status %d\n",
-               data->name, len, value);
+       VDEBUG (data->dev, "%s write %zu IN, status %d\n",
+               data->name, len, (int) value);
 free1:
        up (&data->lock);
        kfree (kbuf);
@@ -1318,8 +1318,8 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
        struct usb_request              *req = dev->req;
        int                             value = -EOPNOTSUPP;
        struct usb_gadgetfs_event       *event;
-       u16                             w_value = ctrl->wValue;
-       u16                             w_length = ctrl->wLength;
+       u16                             w_value = le16_to_cpu(ctrl->wValue);
+       u16                             w_length = le16_to_cpu(ctrl->wLength);
 
        spin_lock (&dev->lock);
        dev->setup_abort = 0;
index c553bbf..09e3ee4 100644 (file)
@@ -47,17 +47,17 @@ struct NDIS_PM_WAKE_UP_CAPABILITIES {
 #define NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE   0x00000004
 
 struct NDIS_PNP_CAPABILITIES {
-       u32                                     Flags;
+       __le32                                  Flags;
        struct NDIS_PM_WAKE_UP_CAPABILITIES     WakeUpCapabilities;
 };
 
 struct NDIS_PM_PACKET_PATTERN {
-       u32     Priority;
-       u32     Reserved;
-       u32     MaskSize;
-       u32     PatternOffset;
-       u32     PatternSize;
-       u32     PatternFlags;
+       __le32  Priority;
+       __le32  Reserved;
+       __le32  MaskSize;
+       __le32  PatternOffset;
+       __le32  PatternSize;
+       __le32  PatternFlags;
 };
 
 
index e47e398..13a3dbc 100644 (file)
@@ -448,7 +448,7 @@ net2280_free_request (struct usb_ep *_ep, struct usb_request *_req)
 #elif  defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE)
 #define USE_KMALLOC
 
-#elif  defined(CONFIG_MIPS) && !defined(CONFIG_NONCOHERENT_IO)
+#elif  defined(CONFIG_MIPS) && !defined(CONFIG_DMA_NONCOHERENT)
 #define USE_KMALLOC
 
 /* FIXME there are other cases, including an x86-64 one ...  */
@@ -1113,7 +1113,7 @@ static void restart_dma (struct net2280_ep *ep)
                if (ep->in_fifo_validate)
                        dmactl |= (1 << DMA_FIFO_VALIDATE);
                list_for_each_entry (entry, &ep->queue, queue) {
-                       u32             dmacount;
+                       __le32          dmacount;
 
                        if (entry == req)
                                continue;
@@ -1238,7 +1238,7 @@ static int net2280_dequeue (struct usb_ep *_ep, struct usb_request *_req)
                                &ep->dma->dmadesc);
                        if (req->td->dmacount & dma_done_ie)
                                writel (readl (&ep->dma->dmacount)
-                                               | dma_done_ie,
+                                               | le32_to_cpu(dma_done_ie),
                                        &ep->dma->dmacount);
                } else {
                        struct net2280_request  *prev;
@@ -1779,6 +1779,9 @@ static void set_fifo_mode (struct net2280 *dev, int mode)
        list_add_tail (&dev->ep [6].ep.ep_list, &dev->gadget.ep_list);
 }
 
+/* just declare this in any driver that really need it */
+extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode);
+
 /**
  * net2280_set_fifo_mode - change allocation of fifo buffers
  * @gadget: access to the net2280 device that will be updated
@@ -2382,9 +2385,9 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat)
                cpu_to_le32s (&u.raw [0]);
                cpu_to_le32s (&u.raw [1]);
 
-               le16_to_cpus (&u.r.wValue);
-               le16_to_cpus (&u.r.wIndex);
-               le16_to_cpus (&u.r.wLength);
+#define        w_value         le16_to_cpup (&u.r.wValue)
+#define        w_index         le16_to_cpup (&u.r.wIndex)
+#define        w_length        le16_to_cpup (&u.r.wLength)
 
                /* ack the irq */
                writel (1 << SETUP_PACKET_INTERRUPT, &dev->regs->irqstat0);
@@ -2413,25 +2416,25 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat)
                switch (u.r.bRequest) {
                case USB_REQ_GET_STATUS: {
                        struct net2280_ep       *e;
-                       u16                     status;
+                       __le32                  status;
 
                        /* hw handles device and interface status */
                        if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT))
                                goto delegate;
-                       if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0
-                                       || u.r.wLength > 2)
+                       if ((e = get_ep_by_addr (dev, w_index)) == 0
+                                       || w_length > 2)
                                goto do_stall;
 
                        if (readl (&e->regs->ep_rsp)
                                        & (1 << SET_ENDPOINT_HALT))
-                               status = __constant_cpu_to_le16 (1);
+                               status = __constant_cpu_to_le32 (1);
                        else
-                               status = __constant_cpu_to_le16 (0);
+                               status = __constant_cpu_to_le32 (0);
 
                        /* don't bother with a request object! */
                        writel (0, &dev->epregs [0].ep_irqenb);
-                       set_fifo_bytecount (ep, u.r.wLength);
-                       writel (status, &dev->epregs [0].ep_data);
+                       set_fifo_bytecount (ep, w_length);
+                       writel ((__force u32)status, &dev->epregs [0].ep_data);
                        allow_status (ep);
                        VDEBUG (dev, "%s stat %02x\n", ep->ep.name, status);
                        goto next_endpoints;
@@ -2443,10 +2446,10 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat)
                        /* hw handles device features */
                        if (u.r.bRequestType != USB_RECIP_ENDPOINT)
                                goto delegate;
-                       if (u.r.wValue != USB_ENDPOINT_HALT
-                                       || u.r.wLength != 0)
+                       if (w_value != USB_ENDPOINT_HALT
+                                       || w_length != 0)
                                goto do_stall;
-                       if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0)
+                       if ((e = get_ep_by_addr (dev, w_index)) == 0)
                                goto do_stall;
                        clear_halt (e);
                        allow_status (ep);
@@ -2460,10 +2463,10 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat)
                        /* hw handles device features */
                        if (u.r.bRequestType != USB_RECIP_ENDPOINT)
                                goto delegate;
-                       if (u.r.wValue != USB_ENDPOINT_HALT
-                                       || u.r.wLength != 0)
+                       if (w_value != USB_ENDPOINT_HALT
+                                       || w_length != 0)
                                goto do_stall;
-                       if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0)
+                       if ((e = get_ep_by_addr (dev, w_index)) == 0)
                                goto do_stall;
                        set_halt (e);
                        allow_status (ep);
@@ -2473,10 +2476,10 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat)
                        break;
                default:
 delegate:
-                       VDEBUG (dev, "setup %02x.%02x v%04x i%04x "
+                       VDEBUG (dev, "setup %02x.%02x v%04x i%04x l%04x"
                                "ep_cfg %08x\n",
                                u.r.bRequestType, u.r.bRequest,
-                               u.r.wValue, u.r.wIndex,
+                               w_value, w_index, w_length,
                                readl (&ep->regs->ep_cfg));
                        spin_unlock (&dev->lock);
                        tmp = dev->driver->setup (&dev->gadget, &u.r);
@@ -2497,6 +2500,10 @@ do_stall:
                 */
        }
 
+#undef w_value
+#undef w_index
+#undef w_length
+
 next_endpoints:
        /* endpoint data irq ? */
        scratch = stat & 0x7f;
@@ -2653,7 +2660,7 @@ static void handle_stat1_irqs (struct net2280 *dev, u32 stat)
                                restart_dma (ep);
                        else if (ep->is_in && use_dma_chaining) {
                                struct net2280_request  *req;
-                               u32                     dmacount;
+                               __le32                  dmacount;
 
                                /* the descriptor at the head of the chain
                                 * may still have VALID_BIT clear; that's
index 98cbcbc..a2b812a 100644 (file)
@@ -52,7 +52,6 @@
 #include <asm/mach-types.h>
 
 #include <asm/arch/dma.h>
-#include <asm/arch/mux.h>
 #include <asm/arch/usb.h>
 
 #include "omap_udc.h"
@@ -167,7 +166,7 @@ static int omap_ep_enable(struct usb_ep *_ep,
        maxp = le16_to_cpu (desc->wMaxPacketSize);
        if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
                                && maxp != ep->maxpacket)
-                       || desc->wMaxPacketSize > ep->maxpacket
+                       || le16_to_cpu(desc->wMaxPacketSize) > ep->maxpacket
                        || !desc->wMaxPacketSize) {
                DBG("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name);
                return -ERANGE;
@@ -214,7 +213,7 @@ static int omap_ep_enable(struct usb_ep *_ep,
        ep->has_dma = 0;
        ep->lch = -1;
        use_ep(ep, UDC_EP_SEL);
-       UDC_CTRL_REG = UDC_RESET_EP;
+       UDC_CTRL_REG = udc->clr_halt;
        ep->ackwait = 0;
        deselect_ep();
 
@@ -253,7 +252,7 @@ static int omap_ep_disable(struct usb_ep *_ep)
        }
 
        spin_lock_irqsave(&ep->udc->lock, flags);
-       ep->desc = 0;
+       ep->desc = NULL;
        nuke (ep, -ESHUTDOWN);
        ep->ep.maxpacket = ep->maxpacket;
        ep->has_dma = 0;
@@ -388,8 +387,8 @@ done(struct omap_ep *ep, struct omap_req *req, int status)
 
 /*-------------------------------------------------------------------------*/
 
-#define        FIFO_FULL       (UDC_NON_ISO_FIFO_FULL | UDC_ISO_FIFO_FULL)
-#define        FIFO_UNWRITABLE (UDC_EP_HALTED | FIFO_FULL)
+#define UDC_FIFO_FULL          (UDC_NON_ISO_FIFO_FULL | UDC_ISO_FIFO_FULL)
+#define UDC_FIFO_UNWRITABLE    (UDC_EP_HALTED | UDC_FIFO_FULL)
 
 #define FIFO_EMPTY     (UDC_NON_ISO_FIFO_EMPTY | UDC_ISO_FIFO_EMPTY)
 #define FIFO_UNREADABLE (UDC_EP_HALTED | FIFO_EMPTY)
@@ -433,7 +432,7 @@ static int write_fifo(struct omap_ep *ep, struct omap_req *req)
 
        /* PIO-IN isn't double buffered except for iso */
        ep_stat = UDC_STAT_FLG_REG;
-       if (ep_stat & FIFO_UNWRITABLE)
+       if (ep_stat & UDC_FIFO_UNWRITABLE)
                return 0;
 
        count = ep->ep.maxpacket;
@@ -504,7 +503,7 @@ static int read_fifo(struct omap_ep *ep, struct omap_req *req)
                if (ep_stat & UDC_EP_HALTED)
                        break;
 
-               if (ep_stat & FIFO_FULL)
+               if (ep_stat & UDC_FIFO_FULL)
                        avail = ep->ep.maxpacket;
                else  {
                        avail = UDC_RXFSTAT_REG;
@@ -538,6 +537,32 @@ static int read_fifo(struct omap_ep *ep, struct omap_req *req)
 
 /*-------------------------------------------------------------------------*/
 
+static inline dma_addr_t dma_csac(unsigned lch)
+{
+       dma_addr_t      csac;
+
+       /* omap 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is
+        * read before the DMA controller finished disabling the channel.
+        */
+       csac = omap_readw(OMAP_DMA_CSAC(lch));
+       if (csac == 0)
+               csac = omap_readw(OMAP_DMA_CSAC(lch));
+       return csac;
+}
+
+static inline dma_addr_t dma_cdac(unsigned lch)
+{
+       dma_addr_t      cdac;
+
+       /* omap 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is
+        * read before the DMA controller finished disabling the channel.
+        */
+       cdac = omap_readw(OMAP_DMA_CDAC(lch));
+       if (cdac == 0)
+               cdac = omap_readw(OMAP_DMA_CDAC(lch));
+       return cdac;
+}
+
 static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start)
 {
        dma_addr_t      end;
@@ -548,7 +573,7 @@ static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start)
        if (cpu_is_omap15xx())
                return 0;
 
-       end = omap_readw(OMAP_DMA_CSAC(ep->lch));
+       end = dma_csac(ep->lch);
        if (end == ep->dma_counter)
                return 0;
 
@@ -559,14 +584,14 @@ static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start)
 }
 
 #define DMA_DEST_LAST(x) (cpu_is_omap15xx() \
-               ? OMAP_DMA_CSAC(x) /* really: CPC */ \
-               : OMAP_DMA_CDAC(x))
+               ? omap_readw(OMAP_DMA_CSAC(x)) /* really: CPC */ \
+               : dma_cdac(x))
 
 static u16 dma_dest_len(struct omap_ep *ep, dma_addr_t start)
 {
        dma_addr_t      end;
 
-       end = omap_readw(DMA_DEST_LAST(ep->lch));
+       end = DMA_DEST_LAST(ep->lch);
        if (end == ep->dma_counter)
                return 0;
 
@@ -593,7 +618,7 @@ static void next_in_dma(struct omap_ep *ep, struct omap_req *req)
                                : OMAP_DMA_SYNC_ELEMENT;
 
        /* measure length in either bytes or packets */
-       if ((cpu_is_omap16xx() && length <= (UDC_TXN_TSC + 1))
+       if ((cpu_is_omap16xx() && length <= UDC_TXN_TSC)
                        || (cpu_is_omap15xx() && length < ep->maxpacket)) {
                txdma_ctrl = UDC_TXN_EOT | length;
                omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8,
@@ -602,15 +627,15 @@ static void next_in_dma(struct omap_ep *ep, struct omap_req *req)
                length = min(length / ep->maxpacket,
                                (unsigned) UDC_TXN_TSC + 1);
                txdma_ctrl = length;
-               omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8,
-                               ep->ep.maxpacket, length, sync_mode);
+               omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16,
+                               ep->ep.maxpacket >> 1, length, sync_mode);
                length *= ep->maxpacket;
        }
        omap_set_dma_src_params(ep->lch, OMAP_DMA_PORT_EMIFF,
                OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual);
 
        omap_start_dma(ep->lch);
-       ep->dma_counter = omap_readw(OMAP_DMA_CSAC(ep->lch));
+       ep->dma_counter = dma_csac(ep->lch);
        UDC_DMA_IRQ_EN_REG |= UDC_TX_DONE_IE(ep->dma_channel);
        UDC_TXDMA_REG(ep->dma_channel) = UDC_TXN_START | txdma_ctrl;
        req->dma_bytes = length;
@@ -650,12 +675,12 @@ static void next_out_dma(struct omap_ep *ep, struct omap_req *req)
        packets = (req->req.length - req->req.actual) / ep->ep.maxpacket;
        packets = min(packets, (unsigned)UDC_RXN_TC + 1);
        req->dma_bytes = packets * ep->ep.maxpacket;
-       omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8,
-                       ep->ep.maxpacket, packets,
+       omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16,
+                       ep->ep.maxpacket >> 1, packets,
                        OMAP_DMA_SYNC_ELEMENT);
        omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF,
                OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual);
-       ep->dma_counter = omap_readw(DMA_DEST_LAST(ep->lch));
+       ep->dma_counter = DMA_DEST_LAST(ep->lch);
 
        UDC_RXDMA_REG(ep->dma_channel) = UDC_RXN_STOP | (packets - 1);
        UDC_DMA_IRQ_EN_REG |= UDC_RX_EOT_IE(ep->dma_channel);
@@ -763,7 +788,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
                reg = UDC_TXDMA_CFG_REG;
        else
                reg = UDC_RXDMA_CFG_REG;
-       reg |= 1 << 12;         /* "pulse" activated */
+       reg |= UDC_DMA_REQ;             /* "pulse" activated */
 
        ep->dma_channel = 0;
        ep->lch = -1;
@@ -787,6 +812,11 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
                        ep->ep.name, dma_error, ep, &ep->lch);
                if (status == 0) {
                        UDC_TXDMA_CFG_REG = reg;
+                       /* EMIFF */
+                       omap_set_dma_src_burst_mode(ep->lch,
+                                               OMAP_DMA_DATA_BURST_4);
+                       omap_set_dma_src_data_pack(ep->lch, 1);
+                       /* TIPB */
                        omap_set_dma_dest_params(ep->lch,
                                OMAP_DMA_PORT_TIPB,
                                OMAP_DMA_AMODE_CONSTANT,
@@ -797,10 +827,15 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
                        ep->ep.name, dma_error, ep, &ep->lch);
                if (status == 0) {
                        UDC_RXDMA_CFG_REG = reg;
+                       /* TIPB */
                        omap_set_dma_src_params(ep->lch,
                                OMAP_DMA_PORT_TIPB,
                                OMAP_DMA_AMODE_CONSTANT,
                                (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG));
+                       /* EMIFF */
+                       omap_set_dma_dest_burst_mode(ep->lch,
+                                               OMAP_DMA_DATA_BURST_4);
+                       omap_set_dma_dest_data_pack(ep->lch, 1);
                }
        }
        if (status)
@@ -856,7 +891,7 @@ static void dma_channel_release(struct omap_ep *ep)
        if (!list_empty(&ep->queue))
                req = container_of(ep->queue.next, struct omap_req, queue);
        else
-               req = 0;
+               req = NULL;
 
        active = ((1 << 7) & omap_readl(OMAP_DMA_CCR(ep->lch))) != 0;
 
@@ -865,9 +900,13 @@ static void dma_channel_release(struct omap_ep *ep)
                        (ep->bEndpointAddress & USB_DIR_IN) ? 't' : 'r',
                        ep->dma_channel - 1, req);
 
+       /* NOTE: re-setting RX_REQ/TX_REQ because of a chip bug (before
+        * OMAP 1710 ES2.0) where reading the DMA_CFG can clear them.
+        */
+
        /* wait till current packet DMA finishes, and fifo empties */
        if (ep->bEndpointAddress & USB_DIR_IN) {
-               UDC_TXDMA_CFG_REG &= ~mask;
+               UDC_TXDMA_CFG_REG = (UDC_TXDMA_CFG_REG & ~mask) | UDC_DMA_REQ;
 
                if (req) {
                        finish_in_dma(ep, req, -ECONNRESET);
@@ -880,7 +919,7 @@ static void dma_channel_release(struct omap_ep *ep)
                while (UDC_TXDMA_CFG_REG & mask)
                        udelay(10);
        } else {
-               UDC_RXDMA_CFG_REG &= ~mask;
+               UDC_RXDMA_CFG_REG = (UDC_RXDMA_CFG_REG & ~mask) | UDC_DMA_REQ;
 
                /* dma empties the fifo */
                while (UDC_RXDMA_CFG_REG & mask)
@@ -997,18 +1036,19 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags)
                                        UDC_IRQ_EN_REG = irq_en;
                                }
 
-                               /* STATUS is reverse direction */
-                               UDC_EP_NUM_REG = is_in
-                                               ? UDC_EP_SEL
-                                               : (UDC_EP_SEL|UDC_EP_DIR);
+                               /* STATUS for zero length DATA stages is
+                                * always an IN ... even for IN transfers,
+                                * a wierd case which seem to stall OMAP.
+                                */
+                               UDC_EP_NUM_REG = (UDC_EP_SEL|UDC_EP_DIR);
                                UDC_CTRL_REG = UDC_CLR_EP;
                                UDC_CTRL_REG = UDC_SET_FIFO_EN;
-                               UDC_EP_NUM_REG = udc->ep0_in ? 0 : UDC_EP_DIR;
+                               UDC_EP_NUM_REG = UDC_EP_DIR;
 
                                /* cleanup */
                                udc->ep0_pending = 0;
                                done(ep, req, 0);
-                               req = 0;
+                               req = NULL;
 
                        /* non-empty DATA stage */
                        } else if (is_in) {
@@ -1029,7 +1069,7 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags)
                        (is_in ? next_in_dma : next_out_dma)(ep, req);
                else if (req) {
                        if ((is_in ? write_fifo : read_fifo)(ep, req) == 1)
-                               req = 0;
+                               req = NULL;
                        deselect_ep();
                        if (!is_in) {
                                UDC_CTRL_REG = UDC_SET_FIFO_EN;
@@ -1041,7 +1081,7 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags)
 
 irq_wait:
        /* irq handler advances the queue */
-       if (req != 0)
+       if (req != NULL)
                list_add_tail(&req->queue, &ep->queue);
        spin_unlock_irqrestore(&udc->lock, flags);
 
@@ -1140,7 +1180,7 @@ static int omap_ep_set_halt(struct usb_ep *_ep, int value)
                                dma_channel_claim(ep, channel);
                } else {
                        use_ep(ep, 0);
-                       UDC_CTRL_REG = UDC_RESET_EP;
+                       UDC_CTRL_REG = ep->udc->clr_halt;
                        ep->ackwait = 0;
                        if (!(ep->bEndpointAddress & USB_DIR_IN)) {
                                UDC_CTRL_REG = UDC_SET_FIFO_EN;
@@ -1238,6 +1278,8 @@ static int can_pullup(struct omap_udc *udc)
 
 static void pullup_enable(struct omap_udc *udc)
 {
+       udc->gadget.dev.parent->power.power_state = PMSG_ON;
+       udc->gadget.dev.power.power_state = PMSG_ON;
        UDC_SYSCON1_REG |= UDC_PULLUP_EN;
 #ifndef CONFIG_USB_OTG
        if (!cpu_is_omap15xx())
@@ -1382,7 +1424,7 @@ static void update_otg(struct omap_udc *udc)
 static void ep0_irq(struct omap_udc *udc, u16 irq_src)
 {
        struct omap_ep  *ep0 = &udc->ep[0];
-       struct omap_req *req = 0;
+       struct omap_req *req = NULL;
 
        ep0->irqs++;
 
@@ -1438,7 +1480,7 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
                                if (req)
                                        done(ep0, req, 0);
                        }
-                       req = 0;
+                       req = NULL;
                } else if (stat & UDC_STALL) {
                        UDC_CTRL_REG = UDC_CLR_HALT;
                        UDC_EP_NUM_REG = UDC_EP_DIR;
@@ -1511,9 +1553,10 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
                        u.word[3] = UDC_DATA_REG;
                        UDC_EP_NUM_REG = 0;
                } while (UDC_IRQ_SRC_REG & UDC_SETUP);
-               le16_to_cpus (&u.r.wValue);
-               le16_to_cpus (&u.r.wIndex);
-               le16_to_cpus (&u.r.wLength);
+
+#define        w_value         le16_to_cpup (&u.r.wValue)
+#define        w_index         le16_to_cpup (&u.r.wIndex)
+#define        w_length        le16_to_cpup (&u.r.wLength)
 
                /* Delegate almost all control requests to the gadget driver,
                 * except for a handful of ch9 status/feature requests that
@@ -1529,11 +1572,11 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
                        /* udc needs to know when ep != 0 is valid */
                        if (u.r.bRequestType != USB_RECIP_DEVICE)
                                goto delegate;
-                       if (u.r.wLength != 0)
+                       if (w_length != 0)
                                goto do_stall;
                        udc->ep0_set_config = 1;
-                       udc->ep0_reset_config = (u.r.wValue == 0);
-                       VDBG("set config %d\n", u.r.wValue);
+                       udc->ep0_reset_config = (w_value == 0);
+                       VDBG("set config %d\n", w_value);
 
                        /* update udc NOW since gadget driver may start
                         * queueing requests immediately; clear config
@@ -1549,23 +1592,28 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
                        /* clear endpoint halt */
                        if (u.r.bRequestType != USB_RECIP_ENDPOINT)
                                goto delegate;
-                       if (u.r.wValue != USB_ENDPOINT_HALT
-                                       || u.r.wLength != 0)
+                       if (w_value != USB_ENDPOINT_HALT
+                                       || w_length != 0)
                                goto do_stall;
-                       ep = &udc->ep[u.r.wIndex & 0xf];
+                       ep = &udc->ep[w_index & 0xf];
                        if (ep != ep0) {
-                               if (u.r.wIndex & USB_DIR_IN)
+                               if (w_index & USB_DIR_IN)
                                        ep += 16;
                                if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
                                                || !ep->desc)
                                        goto do_stall;
                                use_ep(ep, 0);
-                               UDC_CTRL_REG = UDC_RESET_EP;
+                               UDC_CTRL_REG = udc->clr_halt;
                                ep->ackwait = 0;
                                if (!(ep->bEndpointAddress & USB_DIR_IN)) {
                                        UDC_CTRL_REG = UDC_SET_FIFO_EN;
                                        ep->ackwait = 1 + ep->double_buf;
                                }
+                               /* NOTE:  assumes the host behaves sanely,
+                                * only clearing real halts.  Else we may
+                                * need to kill pending transfers and then
+                                * restart the queue... very messy for DMA!
+                                */
                        }
                        VDBG("%s halt cleared by host\n", ep->name);
                        goto ep0out_status_stage;
@@ -1573,11 +1621,11 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
                        /* set endpoint halt */
                        if (u.r.bRequestType != USB_RECIP_ENDPOINT)
                                goto delegate;
-                       if (u.r.wValue != USB_ENDPOINT_HALT
-                                       || u.r.wLength != 0)
+                       if (w_value != USB_ENDPOINT_HALT
+                                       || w_length != 0)
                                goto do_stall;
-                       ep = &udc->ep[u.r.wIndex & 0xf];
-                       if (u.r.wIndex & USB_DIR_IN)
+                       ep = &udc->ep[w_index & 0xf];
+                       if (w_index & USB_DIR_IN)
                                ep += 16;
                        if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
                                        || ep == ep0 || !ep->desc)
@@ -1615,13 +1663,13 @@ ep0out_status_stage:
                        UDC_CTRL_REG = UDC_SET_FIFO_EN;
                        UDC_EP_NUM_REG = UDC_EP_DIR;
                        status = 0;
-                       VDBG("GET_STATUS, interface %d\n", u.r.wIndex);
+                       VDBG("GET_STATUS, interface %d\n", w_index);
                        /* next, status stage */
                        break;
                default:
 delegate:
                        /* activate the ep0out fifo right away */
-                       if (!udc->ep0_in && u.r.wLength) {
+                       if (!udc->ep0_in && w_length) {
                                UDC_EP_NUM_REG = 0;
                                UDC_CTRL_REG = UDC_SET_FIFO_EN;
                        }
@@ -1632,7 +1680,11 @@ delegate:
                         */
                        VDBG("SETUP %02x.%02x v%04x i%04x l%04x\n",
                                u.r.bRequestType, u.r.bRequest,
-                               u.r.wValue, u.r.wIndex, u.r.wLength);
+                               w_value, w_index, w_length);
+
+#undef w_value
+#undef w_index
+#undef w_length
 
                        /* The gadget driver may return an error here,
                         * causing an immediate protocol stall.
@@ -2013,7 +2065,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver)
        udc->softconnect = 1;
 
        /* hook up the driver */
-       driver->driver.bus = 0;
+       driver->driver.bus = NULL;
        udc->driver = driver;
        udc->gadget.dev.driver = &driver->driver;
        spin_unlock_irqrestore(&udc->lock, flags);
@@ -2021,8 +2073,8 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver)
        status = driver->bind (&udc->gadget);
        if (status) {
                DBG("bind to %s --> %d\n", driver->driver.name, status);
-               udc->gadget.dev.driver = 0;
-               udc->driver = 0;
+               udc->gadget.dev.driver = NULL;
+               udc->driver = NULL;
                goto done;
        }
        DBG("bound to driver %s\n", driver->driver.name);
@@ -2035,8 +2087,8 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver)
                if (status < 0) {
                        ERR("can't bind to transceiver\n");
                        driver->unbind (&udc->gadget);
-                       udc->gadget.dev.driver = 0;
-                       udc->driver = 0;
+                       udc->gadget.dev.driver = NULL;
+                       udc->driver = NULL;
                        goto done;
                }
        } else {
@@ -2071,7 +2123,7 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
                omap_vbus_session(&udc->gadget, 0);
 
        if (udc->transceiver)
-               (void) otg_set_peripheral(udc->transceiver, 0);
+               (void) otg_set_peripheral(udc->transceiver, NULL);
        else
                pullup_disable(udc);
 
@@ -2080,9 +2132,8 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
        spin_unlock_irqrestore(&udc->lock, flags);
 
        driver->unbind(&udc->gadget);
-       udc->gadget.dev.driver = 0;
-       udc->driver = 0;
-
+       udc->gadget.dev.driver = NULL;
+       udc->driver = NULL;
 
        DBG("unregistered driver '%s'\n", driver->driver.name);
        return status;
@@ -2178,14 +2229,14 @@ static int proc_otg_show(struct seq_file *s)
 
        tmp = OTG_REV_REG;
        trans = USB_TRANSCEIVER_CTRL_REG;
-       seq_printf(s, "OTG rev %d.%d, transceiver_ctrl %03x\n",
+       seq_printf(s, "\nOTG rev %d.%d, transceiver_ctrl %05x\n",
                tmp >> 4, tmp & 0xf, trans);
        tmp = OTG_SYSCON_1_REG;
        seq_printf(s, "otg_syscon1 %08x usb2 %s, usb1 %s, usb0 %s,"
                        FOURBITS "\n", tmp,
                trx_mode(USB2_TRX_MODE(tmp), trans & CONF_USB2_UNI_R),
                trx_mode(USB1_TRX_MODE(tmp), trans & CONF_USB1_UNI_R),
-               (USB0_TRX_MODE(tmp) == 0)
+               (USB0_TRX_MODE(tmp) == 0 && !cpu_is_omap1710())
                        ? "internal"
                        : trx_mode(USB0_TRX_MODE(tmp), 1),
                (tmp & OTG_IDLE_EN) ? " !otg" : "",
@@ -2235,6 +2286,7 @@ static int proc_otg_show(struct seq_file *s)
        seq_printf(s, "otg_outctrl %04x" "\n", tmp);
        tmp = OTG_TEST_REG;
        seq_printf(s, "otg_test    %04x" "\n", tmp);
+       return 0;
 }
 
 static int proc_udc_show(struct seq_file *s, void *_)
@@ -2378,7 +2430,7 @@ static int proc_udc_show(struct seq_file *s, void *_)
 
 static int proc_udc_open(struct inode *inode, struct file *file)
 {
-       return single_open(file, proc_udc_show, 0);
+       return single_open(file, proc_udc_show, NULL);
 }
 
 static struct file_operations proc_ops = {
@@ -2399,7 +2451,7 @@ static void create_proc_file(void)
 
 static void remove_proc_file(void)
 {
-       remove_proc_entry(proc_filename, 0);
+       remove_proc_entry(proc_filename, NULL);
 }
 
 #else
@@ -2414,6 +2466,10 @@ static inline void remove_proc_file(void) {}
 /* Before this controller can enumerate, we need to pick an endpoint
  * configuration, or "fifo_mode"  That involves allocating 2KB of packet
  * buffer space among the endpoints we'll be operating.
+ *
+ * NOTE: as of OMAP 1710 ES2.0, writing a new endpoint config when
+ * UDC_SYSCON_1_REG.CFG_LOCK is set can now work.  We won't use that
+ * capability yet though.
  */
 static unsigned __init
 omap_ep_setup(char *name, u8 addr, u8 type,
@@ -2505,7 +2561,7 @@ static void omap_udc_release(struct device *dev)
 {
        complete(udc->done);
        kfree (udc);
-       udc = 0;
+       udc = NULL;
 }
 
 static int __init
@@ -2577,23 +2633,33 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv)
        case 1:
                OMAP_BULK_EP("ep1in",  USB_DIR_IN  | 1);
                OMAP_BULK_EP("ep2out", USB_DIR_OUT | 2);
+               OMAP_INT_EP("ep9in",   USB_DIR_IN  | 9, 16);
+
                OMAP_BULK_EP("ep3in",  USB_DIR_IN  | 3);
                OMAP_BULK_EP("ep4out", USB_DIR_OUT | 4);
+               OMAP_INT_EP("ep10in",  USB_DIR_IN  | 10, 16);
 
                OMAP_BULK_EP("ep5in",  USB_DIR_IN  | 5);
                OMAP_BULK_EP("ep5out", USB_DIR_OUT | 5);
+               OMAP_INT_EP("ep11in",  USB_DIR_IN  | 11, 16);
+
                OMAP_BULK_EP("ep6in",  USB_DIR_IN  | 6);
                OMAP_BULK_EP("ep6out", USB_DIR_OUT | 6);
+               OMAP_INT_EP("ep12in",  USB_DIR_IN  | 12, 16);
 
                OMAP_BULK_EP("ep7in",  USB_DIR_IN  | 7);
                OMAP_BULK_EP("ep7out", USB_DIR_OUT | 7);
+               OMAP_INT_EP("ep13in",  USB_DIR_IN  | 13, 16);
+               OMAP_INT_EP("ep13out", USB_DIR_OUT | 13, 16);
+
                OMAP_BULK_EP("ep8in",  USB_DIR_IN  | 8);
                OMAP_BULK_EP("ep8out", USB_DIR_OUT | 8);
+               OMAP_INT_EP("ep14in",  USB_DIR_IN  | 14, 16);
+               OMAP_INT_EP("ep14out", USB_DIR_OUT | 14, 16);
+
+               OMAP_BULK_EP("ep15in",  USB_DIR_IN  | 15);
+               OMAP_BULK_EP("ep15out", USB_DIR_OUT | 15);
 
-               OMAP_INT_EP("ep9in",   USB_DIR_IN  | 9, 16);
-               OMAP_INT_EP("ep10out", USB_DIR_IN  | 10, 16);
-               OMAP_INT_EP("ep11in",  USB_DIR_IN  | 9, 16);
-               OMAP_INT_EP("ep12out", USB_DIR_IN  | 10, 16);
                break;
 
 #ifdef USE_ISO
@@ -2640,8 +2706,8 @@ static int __init omap_udc_probe(struct device *dev)
        struct platform_device  *odev = to_platform_device(dev);
        int                     status = -ENODEV;
        int                     hmc;
-       struct otg_transceiver  *xceiv = 0;
-       const char              *type = 0;
+       struct otg_transceiver  *xceiv = NULL;
+       const char              *type = NULL;
        struct omap_usb_config  *config = dev->platform_data;
 
        /* NOTE:  "knows" the order of the resources! */
@@ -2676,54 +2742,78 @@ static int __init omap_udc_probe(struct device *dev)
                        FUNC_MUX_CTRL_0_REG = tmp;
                }
        } else {
+               /* The transceiver may package some GPIO logic or handle
+                * loopback and/or transceiverless setup; if we find one,
+                * use it.  Except for OTG, we don't _need_ to talk to one;
+                * but not having one probably means no VBUS detection.
+                */
+               xceiv = otg_get_transceiver();
+               if (xceiv)
+                       type = xceiv->label;
+               else if (config->otg) {
+                       DBG("OTG requires external transceiver!\n");
+                       goto cleanup0;
+               }
+
                hmc = HMC_1610;
                switch (hmc) {
+               case 0:                 /* POWERUP DEFAULT == 0 */
+               case 4:
+               case 12:
+               case 20:
+                       if (!cpu_is_omap1710()) {
+                               type = "integrated";
+                               break;
+                       }
+                       /* FALL THROUGH */
                case 3:
                case 11:
                case 16:
                case 19:
                case 25:
-                       xceiv = otg_get_transceiver();
                        if (!xceiv) {
                                DBG("external transceiver not registered!\n");
-                               if (config->otg)
-                                       goto cleanup0;
-                               type = "(unknown external)";
-                       } else
-                               type = xceiv->label;
-                       break;
-               case 0:                 /* POWERUP DEFAULT == 0 */
-               case 4:
-               case 12:
-               case 20:
-                       type = "INTEGRATED";
+                               type = "unknown";
+                       }
                        break;
                case 21:                        /* internal loopback */
-                       type = "(loopback)";
+                       type = "loopback";
                        break;
                case 14:                        /* transceiverless */
-                       type = "(none)";
+                       if (cpu_is_omap1710())
+                               goto bad_on_1710;
+                       /* FALL THROUGH */
+               case 13:
+               case 15:
+                       type = "no";
                        break;
 
                default:
+bad_on_1710:
                        ERR("unrecognized UDC HMC mode %d\n", hmc);
-                       return -ENODEV;
+                       goto cleanup0;
                }
        }
-       INFO("hmc mode %d, transceiver %s\n", hmc, type);
+       INFO("hmc mode %d, %s transceiver\n", hmc, type);
 
        /* a "gadget" abstracts/virtualizes the controller */
        status = omap_udc_setup(odev, xceiv);
        if (status) {
                goto cleanup0;
        }
-       xceiv = 0;
+       xceiv = NULL;
        // "udc" is now valid
        pullup_disable(udc);
 #if    defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
        udc->gadget.is_otg = (config->otg != 0);
 #endif
 
+       /* starting with omap1710 es2.0, clear toggle is a separate bit */
+       if (UDC_REV_REG >= 0x61)
+               udc->clr_halt = UDC_RESET_EP | UDC_CLRDATA_TOGGLE;
+       else
+               udc->clr_halt = UDC_RESET_EP;
+
        /* USB general purpose IRQ:  ep0, state changes, dma, etc */
        status = request_irq(odev->resource[1].start, omap_udc_irq,
                        SA_SAMPLE_RANDOM, driver_name, udc);
@@ -2765,7 +2855,7 @@ cleanup2:
 
 cleanup1:
        kfree (udc);
-       udc = 0;
+       udc = NULL;
 
 cleanup0:
        if (xceiv)
@@ -2788,7 +2878,7 @@ static int __exit omap_udc_remove(struct device *dev)
        pullup_disable(udc);
        if (udc->transceiver) {
                put_device(udc->transceiver->dev);
-               udc->transceiver = 0;
+               udc->transceiver = NULL;
        }
        UDC_SYSCON1_REG = 0;
 
@@ -2809,13 +2899,32 @@ static int __exit omap_udc_remove(struct device *dev)
        return 0;
 }
 
-static int omap_udc_suspend(struct device *dev, pm_message_t state, u32 level)
+/* suspend/resume/wakeup from sysfs (echo > power/state) or when the
+ * system is forced into deep sleep
+ *
+ * REVISIT we should probably reject suspend requests when there's a host
+ * session active, rather than disconnecting, at least on boards that can
+ * report VBUS irqs (UDC_DEVSTAT_REG.UDC_ATT).  And in any case, we need to
+ * make host resumes and VBUS detection trigger OMAP wakeup events; that
+ * may involve talking to an external transceiver (e.g. isp1301).
+ */
+static int omap_udc_suspend(struct device *dev, pm_message_t message, u32 level)
 {
-       if (level != 0)
+       u32     devstat;
+
+       if (level != SUSPEND_POWER_DOWN)
                return 0;
+       devstat = UDC_DEVSTAT_REG;
+
+       /* we're requesting 48 MHz clock if the pullup is enabled
+        * (== we're attached to the host) and we're not suspended,
+        * which would prevent entry to deep sleep...
+        */
+       if ((devstat & UDC_ATT) != 0 && (devstat & UDC_SUS) == 0) {
+               WARN("session active; suspend requires disconnect\n");
+               omap_pullup(&udc->gadget, 0);
+       }
 
-       DBG("suspend, state %d\n", state);
-       omap_pullup(&udc->gadget, 0);
        udc->gadget.dev.power.power_state = PMSG_SUSPEND;
        udc->gadget.dev.parent->power.power_state = PMSG_SUSPEND;
        return 0;
@@ -2823,7 +2932,7 @@ static int omap_udc_suspend(struct device *dev, pm_message_t state, u32 level)
 
 static int omap_udc_resume(struct device *dev, u32 level)
 {
-       if (level != 0)
+       if (level != RESUME_POWER_ON)
                return 0;
 
        DBG("resume + wakeup/SRP\n");
index c9e6854..652ee46 100644 (file)
@@ -20,6 +20,7 @@
 #define        UDC_CTRL_REG                    UDC_REG(0x0C)   /* Endpoint control */
 #      define  UDC_CLR_HALT            (1 << 7)
 #      define  UDC_SET_HALT            (1 << 6)
+#      define  UDC_CLRDATA_TOGGLE      (1 << 3)
 #      define  UDC_SET_FIFO_EN         (1 << 2)
 #      define  UDC_CLR_EP              (1 << 1)
 #      define  UDC_RESET_EP            (1 << 0)
 
 /* DMA configuration registers:  up to three channels in each direction.  */
 #define        UDC_RXDMA_CFG_REG               UDC_REG(0x40)   /* 3 eps for RX DMA */
+#      define  UDC_DMA_REQ             (1 << 12)
 #define        UDC_TXDMA_CFG_REG               UDC_REG(0x44)   /* 3 eps for TX DMA */
 #define        UDC_DATA_DMA_REG                UDC_REG(0x48)   /* rx/tx fifo addr */
 
@@ -162,6 +164,7 @@ struct omap_udc {
        spinlock_t                      lock;
        struct omap_ep                  ep[32];
        u16                             devstat;
+       u16                             clr_halt;
        struct otg_transceiver          *transceiver;
        struct list_head                iso;
        unsigned                        softconnect:1;
@@ -171,7 +174,6 @@ struct omap_udc {
        unsigned                        ep0_set_config:1;
        unsigned                        ep0_reset_config:1;
        unsigned                        ep0_setup:1;
-
        struct completion               *done;
 };
 
index b8b4524..6a0b957 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * linux/drivers/usb/gadget/pxa2xx_udc.c
- * Intel PXA2xx and IXP4xx on-chip full speed USB device controllers
+ * Intel PXA25x and IXP4xx on-chip full speed USB device controllers
  *
  * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker)
  * Copyright (C) 2003 Robert Schwebel, Pengutronix
@@ -63,7 +63,7 @@
 
 
 /*
- * This driver handles the USB Device Controller (UDC) in Intel's PXA 2xx
+ * This driver handles the USB Device Controller (UDC) in Intel's PXA 25x
  * series processors.  The UDC for the IXP 4xx series is very similar.
  * There are fifteen endpoints, in addition to ep0.
  *
@@ -79,8 +79,8 @@
  * pxa250 a0/a1 b0/b1/b2 sure act like they're still there.
  */
 
-#define        DRIVER_VERSION  "14-Dec-2003"
-#define        DRIVER_DESC     "PXA 2xx USB Device Controller driver"
+#define        DRIVER_VERSION  "4-May-2005"
+#define        DRIVER_DESC     "PXA 25x USB Device Controller driver"
 
 
 static const char driver_name [] = "pxa2xx_udc";
@@ -290,6 +290,7 @@ static int pxa2xx_ep_enable (struct usb_ep *_ep,
 static int pxa2xx_ep_disable (struct usb_ep *_ep)
 {
        struct pxa2xx_ep        *ep;
+       unsigned long           flags;
 
        ep = container_of (_ep, struct pxa2xx_ep, ep);
        if (!_ep || !ep->desc) {
@@ -297,6 +298,8 @@ static int pxa2xx_ep_disable (struct usb_ep *_ep)
                        _ep ? ep->ep.name : NULL);
                return -EINVAL;
        }
+       local_irq_save(flags);
+
        nuke (ep, -ESHUTDOWN);
 
 #ifdef USE_DMA
@@ -313,6 +316,7 @@ static int pxa2xx_ep_disable (struct usb_ep *_ep)
        ep->desc = NULL;
        ep->stopped = 1;
 
+       local_irq_restore(flags);
        DBG(DBG_VERBOSE, "%s disabled\n", _ep->name);
        return 0;
 }
@@ -971,10 +975,10 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags)
                        kick_dma(ep, req);
 #endif
                /* can the FIFO can satisfy the request immediately? */
-               } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0
-                               && (*ep->reg_udccs & UDCCS_BI_TFS) != 0
-                               && write_fifo(ep, req)) {
-                       req = NULL;
+               } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) {
+                       if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0
+                                       && write_fifo(ep, req))
+                               req = NULL;
                } else if ((*ep->reg_udccs & UDCCS_BO_RFS) != 0
                                && read_fifo(ep, req)) {
                        req = NULL;
@@ -1290,7 +1294,7 @@ udc_proc_read(char *page, char **start, off_t off, int count,
                "%s version: %s\nGadget driver: %s\nHost %s\n\n",
                driver_name, DRIVER_VERSION SIZE_STR DMASTR,
                dev->driver ? dev->driver->driver.name : "(none)",
-               is_usb_connected() ? "full speed" : "disconnected");
+               is_vbus_present() ? "full speed" : "disconnected");
        size -= t;
        next += t;
 
@@ -1339,7 +1343,7 @@ udc_proc_read(char *page, char **start, off_t off, int count,
                next += t;
        }
 
-       if (!is_usb_connected() || !dev->driver)
+       if (!is_vbus_present() || !dev->driver)
                goto done;
 
        t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n",
@@ -1454,7 +1458,7 @@ static void udc_disable(struct pxa2xx_udc *dev)
        UFNRH = UFNRH_SIM;
 
        /* if hardware supports it, disconnect from usb */
-       make_usb_disappear();
+       pullup_off();
 
        udc_clear_mask_UDCCR(UDCCR_UDE);
 
@@ -1567,7 +1571,7 @@ static void udc_enable (struct pxa2xx_udc *dev)
        UICR0 &= ~UICR0_IM0;
 
        /* if hardware supports it, pullup D+ and wait for reset */
-       let_usb_appear();
+       pullup_on();
 }
 
 
@@ -2052,10 +2056,10 @@ pxa2xx_udc_irq(int irq, void *_dev, struct pt_regs *r)
                if (unlikely(udccr & UDCCR_SUSIR)) {
                        udc_ack_int_UDCCR(UDCCR_SUSIR);
                        handled = 1;
-                       DBG(DBG_VERBOSE, "USB suspend%s\n", is_usb_connected()
+                       DBG(DBG_VERBOSE, "USB suspend%s\n", is_vbus_present()
                                ? "" : "+disconnect");
 
-                       if (!is_usb_connected())
+                       if (!is_vbus_present())
                                stop_activity(dev, dev->driver);
                        else if (dev->gadget.speed != USB_SPEED_UNKNOWN
                                        && dev->driver
@@ -2073,7 +2077,7 @@ pxa2xx_udc_irq(int irq, void *_dev, struct pt_regs *r)
                        if (dev->gadget.speed != USB_SPEED_UNKNOWN
                                        && dev->driver
                                        && dev->driver->resume
-                                       && is_usb_connected())
+                                       && is_vbus_present())
                                dev->driver->resume(&dev->gadget);
                }
 
@@ -2509,7 +2513,7 @@ static int __init pxa2xx_udc_probe(struct device *_dev)
        udc_disable(dev);
        udc_reinit(dev);
 
-       dev->vbus = is_usb_connected();
+       dev->vbus = is_vbus_present();
 
        /* irq setup after old hardware state is cleaned up */
        retval = request_irq(IRQ_USB, pxa2xx_udc_irq,
@@ -2555,6 +2559,12 @@ lubbock_fail0:
 
        return 0;
 }
+
+static void pxa2xx_udc_shutdown(struct device *_dev)
+{
+       pullup_off();
+}
+
 static int __exit pxa2xx_udc_remove(struct device *_dev)
 {
        struct pxa2xx_udc *dev = dev_get_drvdata(_dev);
@@ -2624,6 +2634,7 @@ static struct device_driver udc_driver = {
        .name           = "pxa2xx-udc",
        .bus            = &platform_bus_type,
        .probe          = pxa2xx_udc_probe,
+       .shutdown       = pxa2xx_udc_shutdown,
        .remove         = __exit_p(pxa2xx_udc_remove),
        .suspend        = pxa2xx_udc_suspend,
        .resume         = pxa2xx_udc_resume,
index 1f3a7d9..d0bc396 100644 (file)
@@ -177,23 +177,23 @@ struct pxa2xx_udc {
 
 static struct pxa2xx_udc *the_controller;
 
-/* one GPIO should be used to detect host disconnect */
-static inline int is_usb_connected(void)
+/* one GPIO should be used to detect VBUS from the host */
+static inline int is_vbus_present(void)
 {
        if (!the_controller->mach->udc_is_connected)
                return 1;
        return the_controller->mach->udc_is_connected();
 }
 
-/* one GPIO should force the host to see this device (or not) */
-static inline void make_usb_disappear(void)
+/* one GPIO should control a D+ pullup, so host sees this device (or not) */
+static inline void pullup_off(void)
 {
        if (!the_controller->mach->udc_command)
                return;
        the_controller->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
 }
 
-static inline void let_usb_appear(void)
+static inline void pullup_on(void)
 {
        if (!the_controller->mach->udc_command)
                return;
index 7457268..06b6eba 100644 (file)
@@ -41,6 +41,7 @@
 
 
 #undef RNDIS_PM
+#undef RNDIS_WAKEUP
 #undef VERBOSE
 
 #include "rndis.h"
@@ -60,7 +61,7 @@
        } while (0)
 static int rndis_debug = 0;
 
-module_param (rndis_debug, bool, 0);
+module_param (rndis_debug, int, 0);
 MODULE_PARM_DESC (rndis_debug, "enable debugging");
 
 #else
@@ -78,22 +79,103 @@ static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS];
 static const __le32 rndis_driver_version = __constant_cpu_to_le32 (1);
 
 /* Function Prototypes */
-static int rndis_init_response (int configNr, rndis_init_msg_type *buf);
-static int rndis_query_response (int configNr, rndis_query_msg_type *buf);
-static int rndis_set_response (int configNr, rndis_set_msg_type *buf);
-static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf);
-static int rndis_keepalive_response (int configNr, 
-                                    rndis_keepalive_msg_type *buf);
-
 static rndis_resp_t *rndis_add_response (int configNr, u32 length);
 
 
+/* supported OIDs */
+static const u32 oid_supported_list [] = 
+{
+       /* the general stuff */
+       OID_GEN_SUPPORTED_LIST,
+       OID_GEN_HARDWARE_STATUS,
+       OID_GEN_MEDIA_SUPPORTED,
+       OID_GEN_MEDIA_IN_USE,
+       OID_GEN_MAXIMUM_FRAME_SIZE,
+       OID_GEN_LINK_SPEED,
+       OID_GEN_TRANSMIT_BLOCK_SIZE,
+       OID_GEN_RECEIVE_BLOCK_SIZE,
+       OID_GEN_VENDOR_ID,
+       OID_GEN_VENDOR_DESCRIPTION,
+       OID_GEN_VENDOR_DRIVER_VERSION,
+       OID_GEN_CURRENT_PACKET_FILTER,
+       OID_GEN_MAXIMUM_TOTAL_SIZE,
+       OID_GEN_MEDIA_CONNECT_STATUS,
+       OID_GEN_PHYSICAL_MEDIUM,
+#if 0
+       OID_GEN_RNDIS_CONFIG_PARAMETER,
+#endif
+       
+       /* the statistical stuff */
+       OID_GEN_XMIT_OK,
+       OID_GEN_RCV_OK,
+       OID_GEN_XMIT_ERROR,
+       OID_GEN_RCV_ERROR,
+       OID_GEN_RCV_NO_BUFFER,
+#ifdef RNDIS_OPTIONAL_STATS
+       OID_GEN_DIRECTED_BYTES_XMIT,
+       OID_GEN_DIRECTED_FRAMES_XMIT,
+       OID_GEN_MULTICAST_BYTES_XMIT,
+       OID_GEN_MULTICAST_FRAMES_XMIT,
+       OID_GEN_BROADCAST_BYTES_XMIT,
+       OID_GEN_BROADCAST_FRAMES_XMIT,
+       OID_GEN_DIRECTED_BYTES_RCV,
+       OID_GEN_DIRECTED_FRAMES_RCV,
+       OID_GEN_MULTICAST_BYTES_RCV,
+       OID_GEN_MULTICAST_FRAMES_RCV,
+       OID_GEN_BROADCAST_BYTES_RCV,
+       OID_GEN_BROADCAST_FRAMES_RCV,
+       OID_GEN_RCV_CRC_ERROR,
+       OID_GEN_TRANSMIT_QUEUE_LENGTH,
+#endif /* RNDIS_OPTIONAL_STATS */
+
+       /* mandatory 802.3 */
+       /* the general stuff */
+       OID_802_3_PERMANENT_ADDRESS,
+       OID_802_3_CURRENT_ADDRESS,
+       OID_802_3_MULTICAST_LIST,
+       OID_802_3_MAC_OPTIONS,
+       OID_802_3_MAXIMUM_LIST_SIZE,
+       
+       /* the statistical stuff */
+       OID_802_3_RCV_ERROR_ALIGNMENT,
+       OID_802_3_XMIT_ONE_COLLISION,
+       OID_802_3_XMIT_MORE_COLLISIONS,
+#ifdef RNDIS_OPTIONAL_STATS
+       OID_802_3_XMIT_DEFERRED,
+       OID_802_3_XMIT_MAX_COLLISIONS,
+       OID_802_3_RCV_OVERRUN,
+       OID_802_3_XMIT_UNDERRUN,
+       OID_802_3_XMIT_HEARTBEAT_FAILURE,
+       OID_802_3_XMIT_TIMES_CRS_LOST,
+       OID_802_3_XMIT_LATE_COLLISIONS,
+#endif /* RNDIS_OPTIONAL_STATS */
+
+#ifdef RNDIS_PM
+       /* PM and wakeup are mandatory for USB: */
+
+       /* power management */
+       OID_PNP_CAPABILITIES,
+       OID_PNP_QUERY_POWER,
+       OID_PNP_SET_POWER,
+
+#ifdef RNDIS_WAKEUP
+       /* wake up host */
+       OID_PNP_ENABLE_WAKE_UP,
+       OID_PNP_ADD_WAKE_UP_PATTERN,
+       OID_PNP_REMOVE_WAKE_UP_PATTERN,
+#endif /* RNDIS_WAKEUP */
+#endif /* RNDIS_PM */
+};
+
+
 /* NDIS Functions */
-static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
+static int
+gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
+               rndis_resp_t *r)
 {
        int                     retval = -ENOTSUPP;
-       u32                     length = 0;
-       __le32                  *tmp;
+       u32                     length = 4;     /* usually */
+       __le32                  *outbuf;
        int                     i, count;
        rndis_query_cmplt_type  *resp;
 
@@ -101,7 +183,22 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        resp = (rndis_query_cmplt_type *) r->buf;
 
        if (!resp) return -ENOMEM;
-       
+
+       if (buf_len && rndis_debug > 1) {
+               DEBUG("query OID %08x value, len %d:\n", OID, buf_len);
+               for (i = 0; i < buf_len; i += 16) {
+                       DEBUG ("%03d: %08x %08x %08x %08x\n", i,
+                               le32_to_cpup((__le32 *)&buf[i]),
+                               le32_to_cpup((__le32 *)&buf[i + 4]),
+                               le32_to_cpup((__le32 *)&buf[i + 8]),
+                               le32_to_cpup((__le32 *)&buf[i + 12]));
+               }
+       }
+
+       /* response goes here, right after the header */
+       outbuf = (__le32 *) &resp[1];
+       resp->InformationBufferOffset = __constant_cpu_to_le32 (16);
+
        switch (OID) {
 
        /* general oids (table 4-1) */
@@ -111,42 +208,36 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
                DEBUG ("%s: OID_GEN_SUPPORTED_LIST\n", __FUNCTION__);
                length = sizeof (oid_supported_list);
                count  = length / sizeof (u32);
-               tmp = (__le32 *) ((u8 *)resp + 24);
                for (i = 0; i < count; i++)
-                       tmp[i] = cpu_to_le32 (oid_supported_list[i]);
+                       outbuf[i] = cpu_to_le32 (oid_supported_list[i]);
                retval = 0;
                break;
                
        /* mandatory */
        case OID_GEN_HARDWARE_STATUS:
                DEBUG("%s: OID_GEN_HARDWARE_STATUS\n", __FUNCTION__);
-               length = 4;
                /* Bogus question! 
                 * Hardware must be ready to receive high level protocols.
                 * BTW: 
                 * reddite ergo quae sunt Caesaris Caesari
                 * et quae sunt Dei Deo!
                 */
-               *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
+               *outbuf = __constant_cpu_to_le32 (0);
                retval = 0;
                break;
                
        /* mandatory */
        case OID_GEN_MEDIA_SUPPORTED:
                DEBUG("%s: OID_GEN_MEDIA_SUPPORTED\n", __FUNCTION__);
-               length = 4;
-               *((__le32 *) resp + 6) = cpu_to_le32 (
-                                       rndis_per_dev_params [configNr].medium);
+               *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium);
                retval = 0;
                break;
                
        /* mandatory */
        case OID_GEN_MEDIA_IN_USE:
                DEBUG("%s: OID_GEN_MEDIA_IN_USE\n", __FUNCTION__);
-               length = 4;
                /* one medium, one transport... (maybe you do it better) */
-               *((__le32 *) resp + 6) = cpu_to_le32 (
-                                       rndis_per_dev_params [configNr].medium);
+               *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium);
                retval = 0;
                break;
                
@@ -154,25 +245,21 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        case OID_GEN_MAXIMUM_FRAME_SIZE:
                DEBUG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].dev) {
-                       length = 4;
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
+                       *outbuf = cpu_to_le32 (
                                rndis_per_dev_params [configNr].dev->mtu);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
                
        /* mandatory */
        case OID_GEN_LINK_SPEED:
-//             DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__);
-               length = 4;
+               if (rndis_debug > 1)
+                       DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].media_state
-                       == NDIS_MEDIA_STATE_DISCONNECTED)
-                   *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
+                               == NDIS_MEDIA_STATE_DISCONNECTED)
+                       *outbuf = __constant_cpu_to_le32 (0);
                else
-                   *((__le32 *) resp + 6) = cpu_to_le32 (
+                       *outbuf = cpu_to_le32 (
                                rndis_per_dev_params [configNr].speed);
                retval = 0;
                break;
@@ -181,8 +268,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        case OID_GEN_TRANSMIT_BLOCK_SIZE:
                DEBUG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].dev) {
-                       length = 4;
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
+                       *outbuf = cpu_to_le32 (
                                rndis_per_dev_params [configNr].dev->mtu);
                        retval = 0;
                }
@@ -192,8 +278,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        case OID_GEN_RECEIVE_BLOCK_SIZE:
                DEBUG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].dev) {
-                       length = 4;
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
+                       *outbuf = cpu_to_le32 (
                                rndis_per_dev_params [configNr].dev->mtu);
                        retval = 0;
                }
@@ -202,8 +287,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        /* mandatory */
        case OID_GEN_VENDOR_ID:
                DEBUG("%s: OID_GEN_VENDOR_ID\n", __FUNCTION__);
-               length = 4;
-               *((__le32 *) resp + 6) = cpu_to_le32 (
+               *outbuf = cpu_to_le32 (
                        rndis_per_dev_params [configNr].vendorID);
                retval = 0;
                break;
@@ -212,51 +296,44 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        case OID_GEN_VENDOR_DESCRIPTION:
                DEBUG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __FUNCTION__);
                length = strlen (rndis_per_dev_params [configNr].vendorDescr);
-               memcpy ((u8 *) resp + 24, 
+               memcpy (outbuf,
                        rndis_per_dev_params [configNr].vendorDescr, length);
                retval = 0;
                break;
 
        case OID_GEN_VENDOR_DRIVER_VERSION:
                DEBUG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __FUNCTION__);
-               length = 4;
                /* Created as LE */
-               *((__le32 *) resp + 6) = rndis_driver_version;
+               *outbuf = rndis_driver_version;
                retval = 0;
                break;
 
        /* mandatory */
        case OID_GEN_CURRENT_PACKET_FILTER:
                DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__);
-               length = 4;
-               *((__le32 *) resp + 6) = cpu_to_le32 (
-                                       rndis_per_dev_params[configNr].filter);
+               *outbuf = cpu_to_le32 (*rndis_per_dev_params[configNr].filter);
                retval = 0;
                break;
 
        /* mandatory */
        case OID_GEN_MAXIMUM_TOTAL_SIZE:
                DEBUG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __FUNCTION__);
-               length = 4;
-               *((__le32 *) resp + 6) = __constant_cpu_to_le32(
-                                       RNDIS_MAX_TOTAL_SIZE);
+               *outbuf = __constant_cpu_to_le32(RNDIS_MAX_TOTAL_SIZE);
                retval = 0;
                break;
 
        /* mandatory */
        case OID_GEN_MEDIA_CONNECT_STATUS:
-               DEBUG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__);
-               length = 4;
-               *((__le32 *) resp + 6) = cpu_to_le32 (
-                                       rndis_per_dev_params [configNr]
+               if (rndis_debug > 1)
+                       DEBUG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__);
+               *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
                                                .media_state);
                retval = 0;
                break;
 
        case OID_GEN_PHYSICAL_MEDIUM:
                DEBUG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __FUNCTION__);
-               length = 4;
-               *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
+               *outbuf = __constant_cpu_to_le32 (0);
                retval = 0;
                break;
 
@@ -266,8 +343,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
         */
        case OID_GEN_MAC_OPTIONS:               /* from WinME */
                DEBUG("%s: OID_GEN_MAC_OPTIONS\n", __FUNCTION__);
-               length = 4;
-               *((__le32 *) resp + 6) = __constant_cpu_to_le32(
+               *outbuf = __constant_cpu_to_le32(
                          NDIS_MAC_OPTION_RECEIVE_SERIALIZED
                        | NDIS_MAC_OPTION_FULL_DUPLEX);
                retval = 0;
@@ -277,62 +353,49 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
 
        /* mandatory */
        case OID_GEN_XMIT_OK:
-               DEBUG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__);
+               if (rndis_debug > 1)
+                       DEBUG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].stats) {
-                       length = 4;
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
+                       *outbuf = cpu_to_le32 (
                            rndis_per_dev_params [configNr].stats->tx_packets - 
                            rndis_per_dev_params [configNr].stats->tx_errors -
                            rndis_per_dev_params [configNr].stats->tx_dropped);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
 
        /* mandatory */
        case OID_GEN_RCV_OK:
-               DEBUG("%s: OID_GEN_RCV_OK\n", __FUNCTION__);
+               if (rndis_debug > 1)
+                       DEBUG("%s: OID_GEN_RCV_OK\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].stats) {
-                       length = 4;
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
+                       *outbuf = cpu_to_le32 (
                            rndis_per_dev_params [configNr].stats->rx_packets - 
                            rndis_per_dev_params [configNr].stats->rx_errors -
                            rndis_per_dev_params [configNr].stats->rx_dropped);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
                
        /* mandatory */
        case OID_GEN_XMIT_ERROR:
-               DEBUG("%s: OID_GEN_XMIT_ERROR\n", __FUNCTION__);
+               if (rndis_debug > 1)
+                       DEBUG("%s: OID_GEN_XMIT_ERROR\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].stats) {
-                       length = 4;
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
-                               rndis_per_dev_params [configNr]
+                       *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
                                        .stats->tx_errors);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
                
        /* mandatory */
        case OID_GEN_RCV_ERROR:
-               DEBUG("%s: OID_GEN_RCV_ERROR\n", __FUNCTION__);
+               if (rndis_debug > 1)
+                       DEBUG("%s: OID_GEN_RCV_ERROR\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].stats) {
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
-                               rndis_per_dev_params [configNr]
+                       *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
                                        .stats->rx_errors);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
                
@@ -340,13 +403,9 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        case OID_GEN_RCV_NO_BUFFER:
                DEBUG("%s: OID_GEN_RCV_NO_BUFFER\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].stats) {
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
-                               rndis_per_dev_params [configNr]
+                       *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
                                        .stats->rx_dropped);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
 
@@ -359,8 +418,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
                 * divided by weight of Alpha Centauri
                 */
                if (rndis_per_dev_params [configNr].stats) {
-                       length = 4;
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
+                       *outbuf = cpu_to_le32 (
                                (rndis_per_dev_params [configNr]
                                        .stats->tx_packets - 
                                 rndis_per_dev_params [configNr]
@@ -369,9 +427,6 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
                                         .stats->tx_dropped)
                                * 123);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
                
@@ -379,8 +434,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
                DEBUG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __FUNCTION__);
                /* dito */
                if (rndis_per_dev_params [configNr].stats) {
-                       length = 4;
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
+                       *outbuf = cpu_to_le32 (
                                (rndis_per_dev_params [configNr]
                                        .stats->tx_packets - 
                                 rndis_per_dev_params [configNr]
@@ -389,144 +443,105 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
                                         .stats->tx_dropped)
                                / 123);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
                
        case OID_GEN_MULTICAST_BYTES_XMIT:
                DEBUG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].stats) {
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
-                               rndis_per_dev_params [configNr]
+                       *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
                                        .stats->multicast*1234);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
                
        case OID_GEN_MULTICAST_FRAMES_XMIT:
                DEBUG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].stats) {
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
-                               rndis_per_dev_params [configNr]
+                       *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
                                        .stats->multicast);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
                
        case OID_GEN_BROADCAST_BYTES_XMIT:
                DEBUG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].stats) {
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
-                               rndis_per_dev_params [configNr]
+                       *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
                                        .stats->tx_packets/42*255);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
                
        case OID_GEN_BROADCAST_FRAMES_XMIT:
                DEBUG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].stats) {
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
-                               rndis_per_dev_params [configNr]
+                       *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
                                        .stats->tx_packets/42);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
                
        case OID_GEN_DIRECTED_BYTES_RCV:
                DEBUG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __FUNCTION__);
-               *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
+               *outbuf = __constant_cpu_to_le32 (0);
                retval = 0;
                break;
                
        case OID_GEN_DIRECTED_FRAMES_RCV:
                DEBUG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __FUNCTION__);
-               *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
+               *outbuf = __constant_cpu_to_le32 (0);
                retval = 0;
                break;
                
        case OID_GEN_MULTICAST_BYTES_RCV:
                DEBUG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].stats) {
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
-                               rndis_per_dev_params [configNr]
+                       *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
                                        .stats->multicast * 1111);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
                
        case OID_GEN_MULTICAST_FRAMES_RCV:
                DEBUG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].stats) {
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
-                               rndis_per_dev_params [configNr]
+                       *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
                                        .stats->multicast);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
                
        case OID_GEN_BROADCAST_BYTES_RCV:
                DEBUG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].stats) {
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
-                               rndis_per_dev_params [configNr]
+                       *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
                                        .stats->rx_packets/42*255);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
                
        case OID_GEN_BROADCAST_FRAMES_RCV:
                DEBUG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].stats) {
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
-                               rndis_per_dev_params [configNr]
+                       *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
                                        .stats->rx_packets/42);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
                
        case OID_GEN_RCV_CRC_ERROR:
                DEBUG("%s: OID_GEN_RCV_CRC_ERROR\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].stats) {
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
-                               rndis_per_dev_params [configNr]
+                       *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
                                        .stats->rx_crc_errors);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
                
        case OID_GEN_TRANSMIT_QUEUE_LENGTH:
                DEBUG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __FUNCTION__);
-               *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
+               *outbuf = __constant_cpu_to_le32 (0);
                retval = 0;
                break;
 #endif /* RNDIS_OPTIONAL_STATS */
@@ -538,13 +553,10 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
                DEBUG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].dev) {
                        length = ETH_ALEN;
-                       memcpy ((u8 *) resp + 24,
+                       memcpy (outbuf,
                                rndis_per_dev_params [configNr].host_mac,
                                length);
                        retval = 0;
-               } else {
-                       *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
-                       retval = 0;
                }
                break;
                
@@ -553,7 +565,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
                DEBUG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__);
                if (rndis_per_dev_params [configNr].dev) {
                        length = ETH_ALEN;
-                       memcpy ((u8 *) resp + 24,
+                       memcpy (outbuf,
                                rndis_per_dev_params [configNr].host_mac,
                                length);
                        retval = 0;
@@ -563,18 +575,16 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        /* mandatory */
        case OID_802_3_MULTICAST_LIST:
                DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__);
-               length = 4;
                /* Multicast base address only */
-               *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0xE0000000);
+               *outbuf = __constant_cpu_to_le32 (0xE0000000);
                retval = 0;
                break;
                
        /* mandatory */
        case OID_802_3_MAXIMUM_LIST_SIZE:
                DEBUG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __FUNCTION__);
-                length = 4;
                /* Multicast base address only */
-               *((__le32 *) resp + 6) = __constant_cpu_to_le32 (1);
+               *outbuf = __constant_cpu_to_le32 (1);
                retval = 0;
                break;
                
@@ -587,11 +597,8 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        /* mandatory */
        case OID_802_3_RCV_ERROR_ALIGNMENT:
                DEBUG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __FUNCTION__);
-               if (rndis_per_dev_params [configNr].stats)
-               {
-                       length = 4;
-                       *((__le32 *) resp + 6) = cpu_to_le32 (
-                               rndis_per_dev_params [configNr]
+               if (rndis_per_dev_params [configNr].stats) {
+                       *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
                                        .stats->rx_frame_errors);
                        retval = 0;
                }
@@ -600,16 +607,14 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        /* mandatory */
        case OID_802_3_XMIT_ONE_COLLISION:
                DEBUG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __FUNCTION__);
-               length = 4;
-               *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
+               *outbuf = __constant_cpu_to_le32 (0);
                retval = 0;
                break;
                
        /* mandatory */
        case OID_802_3_XMIT_MORE_COLLISIONS:
                DEBUG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __FUNCTION__);
-               length = 4;
-               *((__le32 *) resp + 6) = __constant_cpu_to_le32 (0);
+               *outbuf = __constant_cpu_to_le32 (0);
                retval = 0;
                break;
                
@@ -655,27 +660,18 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
        case OID_PNP_CAPABILITIES:
                DEBUG("%s: OID_PNP_CAPABILITIES\n", __FUNCTION__);
 
-               /* just PM, and remote wakeup on link status change
-                * (not magic packet or pattern match)
-                */
+               /* for now, no wakeup capabilities */
                length = sizeof (struct NDIS_PNP_CAPABILITIES);
-               memset (resp, 0, length);
-               {
-                       struct NDIS_PNP_CAPABILITIES *caps = (void *) resp;
-
-                       caps->Flags = NDIS_DEVICE_WAKE_UP_ENABLE;
-                       caps->WakeUpCapabilities.MinLinkChangeWakeUp 
-                                = NdisDeviceStateD3;
-
-                       /* FIXME then use usb_gadget_wakeup(), and
-                        * set USB_CONFIG_ATT_WAKEUP in config desc
-                        */
-               }
+               memset(outbuf, 0, length);
                retval = 0;
                break;
        case OID_PNP_QUERY_POWER:
-               DEBUG("%s: OID_PNP_QUERY_POWER\n", __FUNCTION__);
-               /* sure, handle any power state that maps to USB suspend */
+               DEBUG("%s: OID_PNP_QUERY_POWER D%d\n", __FUNCTION__,
+                               le32_to_cpup((__le32 *) buf) - 1);
+               /* only suspend is a real power state, and
+                * it can't be entered by OID_PNP_SET_POWER...
+                */
+               length = 0;
                retval = 0;
                break;
 #endif
@@ -684,11 +680,12 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
                printk (KERN_WARNING "%s: query unknown OID 0x%08X\n", 
                         __FUNCTION__, OID);
        }
+       if (retval < 0)
+               length = 0;
        
-       resp->InformationBufferOffset = __constant_cpu_to_le32 (16);
        resp->InformationBufferLength = cpu_to_le32 (length);
-       resp->MessageLength = cpu_to_le32 (24 + length);
-       r->length = 24 + length;
+       r->length = length + sizeof *resp;
+       resp->MessageLength = cpu_to_le32 (r->length);
        return retval;
 }
 
@@ -705,45 +702,40 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len,
        if (!resp)
                return -ENOMEM;
 
-       DEBUG("set OID %08x value, len %d:\n", OID, buf_len);
-       for (i = 0; i < buf_len; i += 16) {
-               DEBUG ("%03d: "
-                       " %02x %02x %02x %02x"
-                       " %02x %02x %02x %02x"
-                       " %02x %02x %02x %02x"
-                       " %02x %02x %02x %02x"
-                       "\n",
-                       i,
-                       buf[i], buf [i+1],
-                               buf[i+2], buf[i+3],
-                       buf[i+4], buf [i+5],
-                               buf[i+6], buf[i+7],
-                       buf[i+8], buf [i+9],
-                               buf[i+10], buf[i+11],
-                       buf[i+12], buf [i+13],
-                               buf[i+14], buf[i+15]);
+       if (buf_len && rndis_debug > 1) {
+               DEBUG("set OID %08x value, len %d:\n", OID, buf_len);
+               for (i = 0; i < buf_len; i += 16) {
+                       DEBUG ("%03d: %08x %08x %08x %08x\n", i,
+                               le32_to_cpup((__le32 *)&buf[i]),
+                               le32_to_cpup((__le32 *)&buf[i + 4]),
+                               le32_to_cpup((__le32 *)&buf[i + 8]),
+                               le32_to_cpup((__le32 *)&buf[i + 12]));
+               }
        }
 
+       params = &rndis_per_dev_params [configNr];
        switch (OID) {
        case OID_GEN_CURRENT_PACKET_FILTER:
-               params = &rndis_per_dev_params [configNr];
-               retval = 0;
 
-               /* FIXME use these NDIS_PACKET_TYPE_* bitflags to
-                * set the cdc_filter; it's not RNDIS-specific
+               /* these NDIS_PACKET_TYPE_* bitflags are shared with
+                * cdc_filter; it's not RNDIS-specific
                 * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in:
                 *      PROMISCUOUS, DIRECTED,
                 *      MULTICAST, ALL_MULTICAST, BROADCAST
                 */
-               params->filter = le32_to_cpup((__le32 *)buf);
+               *params->filter = (u16) le32_to_cpup((__le32 *)buf);
                DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n",
-                       __FUNCTION__, params->filter);
+                       __FUNCTION__, *params->filter);
 
                /* this call has a significant side effect:  it's
                 * what makes the packet flow start and stop, like
                 * activating the CDC Ethernet altsetting.
                 */
-               if (params->filter) {
+#ifdef RNDIS_PM
+update_linkstate:
+#endif
+               retval = 0;
+               if (*params->filter) {
                        params->state = RNDIS_DATA_INITIALIZED;
                        netif_carrier_on(params->dev);
                        if (netif_running(params->dev))
@@ -776,21 +768,34 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len,
 
 #ifdef RNDIS_PM
        case OID_PNP_SET_POWER:
-               DEBUG ("OID_PNP_SET_POWER\n");
-               /* sure, handle any power state that maps to USB suspend */
-               retval = 0;
-               break;
-
-       case OID_PNP_ENABLE_WAKE_UP:
-               /* always-connected ... */
-               DEBUG ("OID_PNP_ENABLE_WAKE_UP\n");
-               retval = 0;
+               /* The only real power state is USB suspend, and RNDIS requests
+                * can't enter it; this one isn't really about power.  After
+                * resuming, Windows forces a reset, and then SET_POWER D0.
+                * FIXME ... then things go batty; Windows wedges itself.
+                */
+               i = le32_to_cpup((__force __le32 *)buf);
+               DEBUG("%s: OID_PNP_SET_POWER D%d\n", __FUNCTION__, i - 1);
+               switch (i) {
+               case NdisDeviceStateD0:
+                       *params->filter = params->saved_filter;
+                       goto update_linkstate;
+               case NdisDeviceStateD3:
+               case NdisDeviceStateD2:
+               case NdisDeviceStateD1:
+                       params->saved_filter = *params->filter;
+                       retval = 0;
+                       break;
+               }
                break;
 
-       // no PM resume patterns supported (specified where?)
-       // so OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN always fails
+#ifdef RNDIS_WAKEUP
+       // no wakeup support advertised, so wakeup OIDs always fail:
+       //  - OID_PNP_ENABLE_WAKE_UP
+       //  - OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN
 #endif
 
+#endif /* RNDIS_PM */
+
        default:
                printk (KERN_WARNING "%s: set unknown OID 0x%08X, size %d\n", 
                         __FUNCTION__, OID, buf_len);
@@ -811,13 +816,10 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf)
        if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP;
        
        r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type));
-       
-       if (!r) return -ENOMEM;
-       
+       if (!r)
+               return -ENOMEM;
        resp = (rndis_init_cmplt_type *) r->buf;
        
-       if (!resp) return -ENOMEM;
-       
        resp->MessageType = __constant_cpu_to_le32 (
                        REMOTE_NDIS_INITIALIZE_CMPLT);
        resp->MessageLength = __constant_cpu_to_le32 (52);
@@ -857,20 +859,22 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf)
         * oid_supported_list is the largest answer 
         */
        r = rndis_add_response (configNr, sizeof (oid_supported_list));
-       
-       if (!r) return -ENOMEM;
+       if (!r)
+               return -ENOMEM;
        resp = (rndis_query_cmplt_type *) r->buf;
        
-       if (!resp) return -ENOMEM;
-       
        resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_QUERY_CMPLT);
-       resp->MessageLength = __constant_cpu_to_le32 (24);
        resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
-       
-       if (gen_ndis_query_resp (configNr, le32_to_cpu (buf->OID), r)) {
+       
+       if (gen_ndis_query_resp (configNr, le32_to_cpu (buf->OID),
+                       le32_to_cpu(buf->InformationBufferOffset)
+                                       + 8 + (u8 *) buf,
+                       le32_to_cpu(buf->InformationBufferLength),
+                       r)) {
                /* OID not supported */
                resp->Status = __constant_cpu_to_le32 (
                                RNDIS_STATUS_NOT_SUPPORTED);
+               resp->MessageLength = __constant_cpu_to_le32 (sizeof *resp);
                resp->InformationBufferLength = __constant_cpu_to_le32 (0);
                resp->InformationBufferOffset = __constant_cpu_to_le32 (0);
        } else
@@ -889,10 +893,9 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf)
        rndis_resp_t            *r;
        
        r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type));
-       
-       if (!r) return -ENOMEM;
+       if (!r)
+               return -ENOMEM;
        resp = (rndis_set_cmplt_type *) r->buf;
-       if (!resp) return -ENOMEM;
 
        BufLength = le32_to_cpu (buf->InformationBufferLength);
        BufOffset = le32_to_cpu (buf->InformationBufferOffset);
@@ -930,10 +933,9 @@ static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf)
        rndis_resp_t            *r;
        
        r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type));
-       
-       if (!r) return -ENOMEM;
+       if (!r)
+               return -ENOMEM;
        resp = (rndis_reset_cmplt_type *) r->buf;
-       if (!resp) return -ENOMEM;
        
        resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_RESET_CMPLT);
        resp->MessageLength = __constant_cpu_to_le32 (16);
@@ -957,8 +959,9 @@ static int rndis_keepalive_response (int configNr,
        /* host "should" check only in RNDIS_DATA_INITIALIZED state */
 
        r = rndis_add_response (configNr, sizeof (rndis_keepalive_cmplt_type));
+       if (!r)
+               return -ENOMEM;
        resp = (rndis_keepalive_cmplt_type *) r->buf;
-       if (!resp) return -ENOMEM;
                
        resp->MessageType = __constant_cpu_to_le32 (
                        REMOTE_NDIS_KEEPALIVE_CMPLT);
@@ -987,10 +990,9 @@ static int rndis_indicate_status_msg (int configNr, u32 status)
        
        r = rndis_add_response (configNr, 
                                sizeof (rndis_indicate_status_msg_type));
-       if (!r) return -ENOMEM;
-       
+       if (!r)
+               return -ENOMEM;
        resp = (rndis_indicate_status_msg_type *) r->buf;
-       if (!resp) return -ENOMEM;
        
        resp->MessageType = __constant_cpu_to_le32 (
                        REMOTE_NDIS_INDICATE_STATUS_MSG);
@@ -1021,6 +1023,21 @@ int rndis_signal_disconnect (int configNr)
                                          RNDIS_STATUS_MEDIA_DISCONNECT);
 }
 
+void rndis_uninit (int configNr)
+{
+       u8 *buf;
+       u32 length;
+
+       if (configNr >= RNDIS_MAX_CONFIGS)
+               return;
+       rndis_per_dev_params [configNr].used = 0;
+       rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED;
+
+       /* drain the response queue */
+       while ((buf = rndis_get_next_response(configNr, &length)))
+               rndis_free_response(configNr, buf);
+}
+
 void rndis_set_host_mac (int configNr, const u8 *addr)
 {
        rndis_per_dev_params [configNr].host_mac = addr;
@@ -1046,9 +1063,13 @@ int rndis_msg_parser (u8 configNr, u8 *buf)
                return -ENOTSUPP;
        params = &rndis_per_dev_params [configNr];
        
+       /* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for
+        * rx/tx statistics and link status, in addition to KEEPALIVE traffic
+        * and normal HC level polling to see if there's any IN traffic.
+        */
+
        /* For USB: responses may take up to 10 seconds */
-       switch (MsgType)
-       {
+       switch (MsgType) {
        case REMOTE_NDIS_INITIALIZE_MSG:
                DEBUG("%s: REMOTE_NDIS_INITIALIZE_MSG\n", 
                        __FUNCTION__ );
@@ -1082,10 +1103,9 @@ int rndis_msg_parser (u8 configNr, u8 *buf)
 
        case REMOTE_NDIS_KEEPALIVE_MSG:
                /* For USB: host does this every 5 seconds */
-#ifdef VERBOSE
-               DEBUG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", 
-                       __FUNCTION__ );
-#endif
+               if (rndis_debug > 1)
+                       DEBUG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", 
+                               __FUNCTION__ );
                return rndis_keepalive_response (configNr,
                                                 (rndis_keepalive_msg_type *) 
                                                 buf);
@@ -1152,7 +1172,8 @@ void rndis_deregister (int configNr)
 }
 
 int rndis_set_param_dev (u8 configNr, struct net_device *dev, 
-                        struct net_device_stats *stats)
+                        struct net_device_stats *stats,
+                        u16 *cdc_filter)
 {
        DEBUG("%s:\n", __FUNCTION__ );
        if (!dev || !stats) return -1;
@@ -1160,6 +1181,7 @@ int rndis_set_param_dev (u8 configNr, struct net_device *dev,
        
        rndis_per_dev_params [configNr].dev = dev;
        rndis_per_dev_params [configNr].stats = stats;
+       rndis_per_dev_params [configNr].filter = cdc_filter;
        
        return 0;
 }
@@ -1178,7 +1200,7 @@ int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr)
 
 int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed)
 {
-       DEBUG("%s:\n", __FUNCTION__ );
+       DEBUG("%s: %u %u\n", __FUNCTION__, medium, speed);
        if (configNr >= RNDIS_MAX_CONFIGS) return -1;
        
        rndis_per_dev_params [configNr].medium = medium;
@@ -1242,6 +1264,7 @@ static rndis_resp_t *rndis_add_response (int configNr, u32 length)
 {
        rndis_resp_t    *r;
        
+       /* NOTE:  this gets copied into ether.c USB_BUFSIZ bytes ... */
        r = kmalloc (sizeof (rndis_resp_t) + length, GFP_ATOMIC);
        if (!r) return NULL;
        
index 2b5b55d..95b4c63 100644 (file)
 #define OID_PNP_ENABLE_WAKE_UP                 0xFD010106
 
 
-/* supported OIDs */
-static const u32 oid_supported_list [] = 
-{
-       /* the general stuff */
-       OID_GEN_SUPPORTED_LIST,
-       OID_GEN_HARDWARE_STATUS,
-       OID_GEN_MEDIA_SUPPORTED,
-       OID_GEN_MEDIA_IN_USE,
-       OID_GEN_MAXIMUM_FRAME_SIZE,
-       OID_GEN_LINK_SPEED,
-       OID_GEN_TRANSMIT_BLOCK_SIZE,
-       OID_GEN_RECEIVE_BLOCK_SIZE,
-       OID_GEN_VENDOR_ID,
-       OID_GEN_VENDOR_DESCRIPTION,
-       OID_GEN_VENDOR_DRIVER_VERSION,
-       OID_GEN_CURRENT_PACKET_FILTER,
-       OID_GEN_MAXIMUM_TOTAL_SIZE,
-       OID_GEN_MEDIA_CONNECT_STATUS,
-       OID_GEN_PHYSICAL_MEDIUM,
-#if 0
-       OID_GEN_RNDIS_CONFIG_PARAMETER,
-#endif
-       
-       /* the statistical stuff */
-       OID_GEN_XMIT_OK,
-       OID_GEN_RCV_OK,
-       OID_GEN_XMIT_ERROR,
-       OID_GEN_RCV_ERROR,
-       OID_GEN_RCV_NO_BUFFER,
-#ifdef RNDIS_OPTIONAL_STATS
-       OID_GEN_DIRECTED_BYTES_XMIT,
-       OID_GEN_DIRECTED_FRAMES_XMIT,
-       OID_GEN_MULTICAST_BYTES_XMIT,
-       OID_GEN_MULTICAST_FRAMES_XMIT,
-       OID_GEN_BROADCAST_BYTES_XMIT,
-       OID_GEN_BROADCAST_FRAMES_XMIT,
-       OID_GEN_DIRECTED_BYTES_RCV,
-       OID_GEN_DIRECTED_FRAMES_RCV,
-       OID_GEN_MULTICAST_BYTES_RCV,
-       OID_GEN_MULTICAST_FRAMES_RCV,
-       OID_GEN_BROADCAST_BYTES_RCV,
-       OID_GEN_BROADCAST_FRAMES_RCV,
-       OID_GEN_RCV_CRC_ERROR,
-       OID_GEN_TRANSMIT_QUEUE_LENGTH,
-#endif /* RNDIS_OPTIONAL_STATS */
-
-       /* mandatory 802.3 */
-       /* the general stuff */
-       OID_802_3_PERMANENT_ADDRESS,
-       OID_802_3_CURRENT_ADDRESS,
-       OID_802_3_MULTICAST_LIST,
-       OID_802_3_MAC_OPTIONS,
-       OID_802_3_MAXIMUM_LIST_SIZE,
-       
-       /* the statistical stuff */
-       OID_802_3_RCV_ERROR_ALIGNMENT,
-       OID_802_3_XMIT_ONE_COLLISION,
-       OID_802_3_XMIT_MORE_COLLISIONS,
-#ifdef RNDIS_OPTIONAL_STATS
-       OID_802_3_XMIT_DEFERRED,
-       OID_802_3_XMIT_MAX_COLLISIONS,
-       OID_802_3_RCV_OVERRUN,
-       OID_802_3_XMIT_UNDERRUN,
-       OID_802_3_XMIT_HEARTBEAT_FAILURE,
-       OID_802_3_XMIT_TIMES_CRS_LOST,
-       OID_802_3_XMIT_LATE_COLLISIONS,
-#endif /* RNDIS_OPTIONAL_STATS */
-
-#ifdef RNDIS_PM
-       /* PM and wakeup are mandatory for USB: */
-
-       /* power management */
-       OID_PNP_CAPABILITIES,
-       OID_PNP_QUERY_POWER,
-       OID_PNP_SET_POWER,
-
-       /* wake up host */
-       OID_PNP_ENABLE_WAKE_UP,
-       OID_PNP_ADD_WAKE_UP_PATTERN,
-       OID_PNP_REMOVE_WAKE_UP_PATTERN,
-#endif
-};
-
-
 typedef struct rndis_init_msg_type 
 {
        __le32  MessageType;
@@ -309,15 +225,18 @@ typedef struct rndis_resp_t
 typedef struct rndis_params
 {
        u8                      confignr;
-       int                     used;
+       u8                      used;
+       u16                     saved_filter;
        enum rndis_state        state;
-       u32                     filter;
        u32                     medium;
        u32                     speed;
        u32                     media_state;
+
        const u8                *host_mac;
+       u16                     *filter;
        struct net_device       *dev;
        struct net_device_stats *stats;
+
        u32                     vendorID;
        const char              *vendorDescr;
        int                     (*ack) (struct net_device *);
@@ -329,7 +248,8 @@ int  rndis_msg_parser (u8 configNr, u8 *buf);
 int  rndis_register (int (*rndis_control_ack) (struct net_device *));
 void rndis_deregister (int configNr);
 int  rndis_set_param_dev (u8 configNr, struct net_device *dev,
-                        struct net_device_stats *stats);
+                        struct net_device_stats *stats,
+                        u16 *cdc_filter);
 int  rndis_set_param_vendor (u8 configNr, u32 vendorID, 
                            const char *vendorDescr);
 int  rndis_set_param_medium (u8 configNr, u32 medium, u32 speed);
@@ -338,6 +258,7 @@ int rndis_rm_hdr (struct sk_buff *skb);
 u8   *rndis_get_next_response (int configNr, u32 *length);
 void rndis_free_response (int configNr, u8 *buf);
 
+void rndis_uninit (int configNr);
 int  rndis_signal_connect (int configNr);
 int  rndis_signal_disconnect (int configNr);
 int  rndis_state (int configNr);
index 4d591c7..9e4f1c6 100644 (file)
@@ -300,18 +300,18 @@ static int gs_build_config_buf(u8 *buf, enum usb_device_speed speed,
                u8 type, unsigned int index, int is_otg);
 
 static struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned int len,
-       int kmalloc_flags);
+       unsigned kmalloc_flags);
 static void gs_free_req(struct usb_ep *ep, struct usb_request *req);
 
 static struct gs_req_entry *gs_alloc_req_entry(struct usb_ep *ep, unsigned len,
-       int kmalloc_flags);
+       unsigned kmalloc_flags);
 static void gs_free_req_entry(struct usb_ep *ep, struct gs_req_entry *req);
 
-static int gs_alloc_ports(struct gs_dev *dev, int kmalloc_flags);
+static int gs_alloc_ports(struct gs_dev *dev, unsigned kmalloc_flags);
 static void gs_free_ports(struct gs_dev *dev);
 
 /* circular buffer */
-static struct gs_buf *gs_buf_alloc(unsigned int size, int kmalloc_flags);
+static struct gs_buf *gs_buf_alloc(unsigned int size, unsigned kmalloc_flags);
 static void gs_buf_free(struct gs_buf *gb);
 static void gs_buf_clear(struct gs_buf *gb);
 static unsigned int gs_buf_data_avail(struct gs_buf *gb);
@@ -1607,9 +1607,9 @@ static int gs_setup(struct usb_gadget *gadget,
        int ret = -EOPNOTSUPP;
        struct gs_dev *dev = get_gadget_data(gadget);
        struct usb_request *req = dev->dev_ctrl_req;
-       u16 wIndex = ctrl->wIndex;
-       u16 wValue = ctrl->wValue;
-       u16 wLength = ctrl->wLength;
+       u16 wIndex = le16_to_cpu(ctrl->wIndex);
+       u16 wValue = le16_to_cpu(ctrl->wValue);
+       u16 wLength = le16_to_cpu(ctrl->wLength);
 
        switch (ctrl->bRequestType & USB_TYPE_MASK) {
        case USB_TYPE_STANDARD:
@@ -1651,9 +1651,9 @@ static int gs_setup_standard(struct usb_gadget *gadget,
        int ret = -EOPNOTSUPP;
        struct gs_dev *dev = get_gadget_data(gadget);
        struct usb_request *req = dev->dev_ctrl_req;
-       u16 wIndex = ctrl->wIndex;
-       u16 wValue = ctrl->wValue;
-       u16 wLength = ctrl->wLength;
+       u16 wIndex = le16_to_cpu(ctrl->wIndex);
+       u16 wValue = le16_to_cpu(ctrl->wValue);
+       u16 wLength = le16_to_cpu(ctrl->wLength);
 
        switch (ctrl->bRequest) {
        case USB_REQ_GET_DESCRIPTOR:
@@ -1782,9 +1782,9 @@ static int gs_setup_class(struct usb_gadget *gadget,
        struct gs_dev *dev = get_gadget_data(gadget);
        struct gs_port *port = dev->dev_port[0];        /* ACM only has one port */
        struct usb_request *req = dev->dev_ctrl_req;
-       u16 wIndex = ctrl->wIndex;
-       u16 wValue = ctrl->wValue;
-       u16 wLength = ctrl->wLength;
+       u16 wIndex = le16_to_cpu(ctrl->wIndex);
+       u16 wValue = le16_to_cpu(ctrl->wValue);
+       u16 wLength = le16_to_cpu(ctrl->wLength);
 
        switch (ctrl->bRequest) {
        case USB_CDC_REQ_SET_LINE_CODING:
@@ -2119,7 +2119,8 @@ static int gs_build_config_buf(u8 *buf, enum usb_device_speed speed,
  * Allocate a usb_request and its buffer.  Returns a pointer to the
  * usb_request or NULL if there is an error.
  */
-static struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned int len, int kmalloc_flags)
+static struct usb_request *
+gs_alloc_req(struct usb_ep *ep, unsigned int len, unsigned kmalloc_flags)
 {
        struct usb_request *req;
 
@@ -2159,7 +2160,8 @@ static void gs_free_req(struct usb_ep *ep, struct usb_request *req)
  * Allocates a request and its buffer, using the given
  * endpoint, buffer len, and kmalloc flags.
  */
-static struct gs_req_entry *gs_alloc_req_entry(struct usb_ep *ep, unsigned len, int kmalloc_flags)
+static struct gs_req_entry *
+gs_alloc_req_entry(struct usb_ep *ep, unsigned len, unsigned kmalloc_flags)
 {
        struct gs_req_entry     *req;
 
@@ -2200,7 +2202,7 @@ static void gs_free_req_entry(struct usb_ep *ep, struct gs_req_entry *req)
  *
  * The device lock is normally held when calling this function.
  */
-static int gs_alloc_ports(struct gs_dev *dev, int kmalloc_flags)
+static int gs_alloc_ports(struct gs_dev *dev, unsigned kmalloc_flags)
 {
        int i;
        struct gs_port *port;
@@ -2282,7 +2284,7 @@ static void gs_free_ports(struct gs_dev *dev)
  *
  * Allocate a circular buffer and all associated memory.
  */
-static struct gs_buf *gs_buf_alloc(unsigned int size, int kmalloc_flags)
+static struct gs_buf *gs_buf_alloc(unsigned int size, unsigned kmalloc_flags)
 {
        struct gs_buf *gb;
 
index 6e49432..a6e035e 100644 (file)
@@ -919,9 +919,9 @@ zero_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
        struct zero_dev         *dev = get_gadget_data (gadget);
        struct usb_request      *req = dev->req;
        int                     value = -EOPNOTSUPP;
-       u16                     w_index = ctrl->wIndex;
-       u16                     w_value = ctrl->wValue;
-       u16                     w_length = ctrl->wLength;
+       u16                     w_index = le16_to_cpu(ctrl->wIndex);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+       u16                     w_length = le16_to_cpu(ctrl->wLength);
 
        /* usually this stores reply data in the pre-allocated ep0 buffer,
         * but config change events will reconfigure hardware.
index 19e598c..ed1899d 100644 (file)
@@ -49,6 +49,19 @@ config USB_EHCI_ROOT_HUB_TT
 
          This supports the EHCI implementation from TransDimension Inc.
 
+config USB_ISP116X_HCD
+       tristate "ISP116X HCD support"
+       depends on USB
+       default N
+       ---help---
+         The ISP1160 and ISP1161 chips are USB host controllers. Enable this
+         option if your board has this chip. If unsure, say N.
+
+         This driver does not support isochronous transfers.
+
+         To compile this driver as a module, choose M here: the
+         module will be called isp116x-hcd.
+
 config USB_OHCI_HCD
        tristate "OHCI HCD support"
        depends on USB && USB_ARCH_HAS_OHCI
index 5dbd3e7..350d14f 100644 (file)
@@ -4,6 +4,7 @@
 #
 
 obj-$(CONFIG_USB_EHCI_HCD)     += ehci-hcd.o
+obj-$(CONFIG_USB_ISP116X_HCD)  += isp116x-hcd.o
 obj-$(CONFIG_USB_OHCI_HCD)     += ohci-hcd.o
 obj-$(CONFIG_USB_UHCI_HCD)     += uhci-hcd.o
 obj-$(CONFIG_USB_SL811_HCD)    += sl811-hcd.o
index 2ff11d5..50cb018 100644 (file)
@@ -254,7 +254,7 @@ dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
        }
 
        return scnprintf (buf, len,
-               "%s%sport %d status %06x%s%s sig=%s %s%s%s%s%s%s%s%s%s",
+               "%s%sport %d status %06x%s%s sig=%s%s%s%s%s%s%s%s%s%s",
                label, label [0] ? " " : "", port, status,
                (status & PORT_POWER) ? " POWER" : "",
                (status & PORT_OWNER) ? " OWNER" : "",
@@ -644,9 +644,11 @@ show_registers (struct class_device *class_dev, char *buf)
        if (bus->controller->power.power_state) {
                size = scnprintf (next, size,
                        "bus %s, device %s (driver " DRIVER_VERSION ")\n"
+                       "%s\n"
                        "SUSPENDED (no register access)\n",
                        hcd->self.controller->bus->name,
-                       hcd->self.controller->bus_id);
+                       hcd->self.controller->bus_id,
+                       hcd->product_desc);
                goto done;
        }
 
@@ -654,13 +656,53 @@ show_registers (struct class_device *class_dev, char *buf)
        i = HC_VERSION(readl (&ehci->caps->hc_capbase));
        temp = scnprintf (next, size,
                "bus %s, device %s (driver " DRIVER_VERSION ")\n"
+               "%s\n"
                "EHCI %x.%02x, hcd state %d\n",
                hcd->self.controller->bus->name,
                hcd->self.controller->bus_id,
+               hcd->product_desc,
                i >> 8, i & 0x0ff, hcd->state);
        size -= temp;
        next += temp;
 
+#ifdef CONFIG_PCI
+       /* EHCI 0.96 and later may have "extended capabilities" */
+       if (hcd->self.controller->bus == &pci_bus_type) {
+               struct pci_dev  *pdev;
+               u32             offset, cap, cap2;
+               unsigned        count = 256/4;
+
+               pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller);
+               offset = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params));
+               while (offset && count--) {
+                       pci_read_config_dword (pdev, offset, &cap);
+                       switch (cap & 0xff) {
+                       case 1:
+                               temp = scnprintf (next, size,
+                                       "ownership %08x%s%s\n", cap,
+                                       (cap & (1 << 24)) ? " linux" : "",
+                                       (cap & (1 << 16)) ? " firmware" : "");
+                               size -= temp;
+                               next += temp;
+
+                               offset += 4;
+                               pci_read_config_dword (pdev, offset, &cap2);
+                               temp = scnprintf (next, size,
+                                       "SMI sts/enable 0x%08x\n", cap2);
+                               size -= temp;
+                               next += temp;
+                               break;
+                       case 0:         /* illegal reserved capability */
+                               cap = 0;
+                               /* FALLTHROUGH */
+                       default:                /* unknown */
+                               break;
+                       }
+                       temp = (cap >> 8) & 0xff;
+               }
+       }
+#endif
+
        // FIXME interpret both types of params
        i = readl (&ehci->caps->hcs_params);
        temp = scnprintf (next, size, "structural params 0x%08x\n", i);
@@ -696,12 +738,19 @@ show_registers (struct class_device *class_dev, char *buf)
        size -= temp;
        next += temp;
 
-       for (i = 0; i < HCS_N_PORTS (ehci->hcs_params); i++) {
-               temp = dbg_port_buf (scratch, sizeof scratch, label, i + 1,
-                               readl (&ehci->regs->port_status [i]));
+       for (i = 1; i <= HCS_N_PORTS (ehci->hcs_params); i++) {
+               temp = dbg_port_buf (scratch, sizeof scratch, label, i,
+                               readl (&ehci->regs->port_status [i - 1]));
                temp = scnprintf (next, size, fmt, temp, scratch);
                size -= temp;
                next += temp;
+               if (i == HCS_DEBUG_PORT(ehci->hcs_params) && ehci->debug) {
+                       temp = scnprintf (next, size,
+                                       "    debug control %08x\n",
+                                       readl (&ehci->debug->control));
+                       size -= temp;
+                       next += temp;
+               }
        }
 
        if (ehci->reclaim) {
index bc69bd7..35248a3 100644 (file)
@@ -304,30 +304,31 @@ static void ehci_watchdog (unsigned long param)
  */
 static int bios_handoff (struct ehci_hcd *ehci, int where, u32 cap)
 {
+       struct pci_dev *pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller);
+
+       /* always say Linux will own the hardware */
+       pci_write_config_byte(pdev, where + 3, 1);
+
+       /* maybe wait a while for BIOS to respond */
        if (cap & (1 << 16)) {
                int msec = 5000;
-               struct pci_dev *pdev =
-                               to_pci_dev(ehci_to_hcd(ehci)->self.controller);
 
-               /* request handoff to OS */
-               cap |= 1 << 24;
-               pci_write_config_dword(pdev, where, cap);
-
-               /* and wait a while for it to happen */
                do {
                        msleep(10);
                        msec -= 10;
                        pci_read_config_dword(pdev, where, &cap);
                } while ((cap & (1 << 16)) && msec);
                if (cap & (1 << 16)) {
-                       ehci_err (ehci, "BIOS handoff failed (%d, %04x)\n",
+                       ehci_err(ehci, "BIOS handoff failed (%d, %08x)\n",
                                where, cap);
                        // some BIOS versions seem buggy...
                        // return 1;
                        ehci_warn (ehci, "continuing after BIOS bug...\n");
-                       return 0;
-               } 
-               ehci_dbg (ehci, "BIOS handoff succeeded\n");
+                       /* disable all SMIs, and clear "BIOS owns" flag */
+                       pci_write_config_dword(pdev, where + 4, 0);
+                       pci_write_config_byte(pdev, where + 2, 0);
+               } else
+                       ehci_dbg(ehci, "BIOS handoff succeeded\n");
        }
        return 0;
 }
@@ -492,8 +493,6 @@ static int ehci_start (struct usb_hcd *hcd)
 {
        struct ehci_hcd         *ehci = hcd_to_ehci (hcd);
        u32                     temp;
-       struct usb_device       *udev;
-       struct usb_bus          *bus;
        int                     retval;
        u32                     hcc_params;
        u8                      sbrn = 0;
@@ -588,8 +587,8 @@ static int ehci_start (struct usb_hcd *hcd)
                writel (0, &ehci->regs->segment);
 #if 0
 // this is deeply broken on almost all architectures
-               if (!pci_set_dma_mask (to_pci_dev(hcd->self.controller), 0xffffffffffffffffULL))
-                       ehci_info (ehci, "enabled 64bit PCI DMA\n");
+               if (!dma_set_mask (hcd->self.controller, DMA_64BIT_MASK))
+                       ehci_info (ehci, "enabled 64bit DMA\n");
 #endif
        }
 
@@ -631,17 +630,6 @@ static int ehci_start (struct usb_hcd *hcd)
 
        /* set async sleep time = 10 us ... ? */
 
-       /* wire up the root hub */
-       bus = hcd_to_bus (hcd);
-       udev = first ? usb_alloc_dev (NULL, bus, 0) : bus->root_hub;
-       if (!udev) {
-done2:
-               ehci_mem_cleanup (ehci);
-               return -ENOMEM;
-       }
-       udev->speed = USB_SPEED_HIGH;
-       udev->state = first ? USB_STATE_ATTACHED : USB_STATE_CONFIGURED;
-
        /*
         * Start, enabling full USB 2.0 functionality ... usb 1.1 devices
         * are explicitly handed to companion controller(s), so no TT is
@@ -664,24 +652,6 @@ done2:
                first ? "initialized" : "restarted",
                temp >> 8, temp & 0xff, DRIVER_VERSION);
 
-       /*
-        * From here on, khubd concurrently accesses the root
-        * hub; drivers will be talking to enumerated devices.
-        * (On restart paths, khubd already knows about the root
-        * hub and could find work as soon as we wrote FLAG_CF.)
-        *
-        * Before this point the HC was idle/ready.  After, khubd
-        * and device drivers may start it running.
-        */
-       if (first && usb_hcd_register_root_hub (udev, hcd) != 0) {
-               if (hcd->state == HC_STATE_RUNNING)
-                       ehci_quiesce (ehci);
-               ehci_reset (ehci);
-               usb_put_dev (udev); 
-               retval = -ENODEV;
-               goto done2;
-       }
-
        writel (INTR_MASK, &ehci->regs->intr_enable); /* Turn On Interrupts */
 
        if (first)
index d7b4f79..36cc1f2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001-2002 by David Brownell
+ * Copyright (C) 2001-2004 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
index 7df9b9a..45d89a7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001-2002 by David Brownell
+ * Copyright (C) 2001-2004 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
index 2fa1ffe..c2104ca 100644 (file)
@@ -637,9 +637,8 @@ iso_stream_alloc (int mem_flags)
 {
        struct ehci_iso_stream *stream;
 
-       stream = kmalloc(sizeof *stream, mem_flags);
+       stream = kcalloc(1, sizeof *stream, mem_flags);
        if (likely (stream != NULL)) {
-               memset (stream, 0, sizeof(*stream));
                INIT_LIST_HEAD(&stream->td_list);
                INIT_LIST_HEAD(&stream->free_list);
                stream->next_uframe = -1;
@@ -894,7 +893,7 @@ itd_sched_init (
                trans |= length << 16;
                uframe->transaction = cpu_to_le32 (trans);
 
-               /* might need to cross a buffer page within a td */
+               /* might need to cross a buffer page within a uframe */
                uframe->bufp = (buf & ~(u64)0x0fff);
                buf += length;
                if (unlikely ((uframe->bufp != (buf & ~(u64)0x0fff))))
@@ -1194,6 +1193,7 @@ itd_init (struct ehci_iso_stream *stream, struct ehci_itd *itd)
 {
        int i;
 
+       /* it's been recently zeroed */
        itd->hw_next = EHCI_LIST_END;
        itd->hw_bufp [0] = stream->buf0;
        itd->hw_bufp [1] = stream->buf1;
@@ -1210,8 +1210,7 @@ itd_patch (
        struct ehci_itd         *itd,
        struct ehci_iso_sched   *iso_sched,
        unsigned                index,
-       u16                     uframe,
-       int                     first
+       u16                     uframe
 )
 {
        struct ehci_iso_packet  *uf = &iso_sched->packet [index];
@@ -1228,7 +1227,7 @@ itd_patch (
        itd->hw_bufp_hi [pg] |= cpu_to_le32 ((u32)(uf->bufp >> 32));
 
        /* iso_frame_desc[].offset must be strictly increasing */
-       if (unlikely (!first && uf->cross)) {
+       if (unlikely (uf->cross)) {
                u64     bufp = uf->bufp + 4096;
                itd->pg = ++pg;
                itd->hw_bufp [pg] |= cpu_to_le32 (bufp & ~(u32)0);
@@ -1257,7 +1256,7 @@ itd_link_urb (
        struct ehci_iso_stream  *stream
 )
 {
-       int                     packet, first = 1;
+       int                     packet;
        unsigned                next_uframe, uframe, frame;
        struct ehci_iso_sched   *iso_sched = urb->hcpriv;
        struct ehci_itd         *itd;
@@ -1290,7 +1289,6 @@ itd_link_urb (
                        list_move_tail (&itd->itd_list, &stream->td_list);
                        itd->stream = iso_stream_get (stream);
                        itd->urb = usb_get_urb (urb);
-                       first = 1;
                        itd_init (stream, itd);
                }
 
@@ -1298,8 +1296,7 @@ itd_link_urb (
                frame = next_uframe >> 3;
 
                itd->usecs [uframe] = stream->usecs;
-               itd_patch (itd, iso_sched, packet, uframe, first);
-               first = 0;
+               itd_patch (itd, iso_sched, packet, uframe);
 
                next_uframe += stream->interval;
                stream->depth += stream->interval;
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
new file mode 100644 (file)
index 0000000..ff0a168
--- /dev/null
@@ -0,0 +1,1875 @@
+/*
+ * ISP116x HCD (Host Controller Driver) for USB.
+ *
+ * Derived from the SL811 HCD, rewritten for ISP116x.
+ * Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee>
+ *
+ * Portions:
+ * 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 driver basically works. A number of people have used it with a range
+ * of devices.
+ *
+ *The driver passes all usbtests 1-14.
+ *
+ * Suspending/resuming of root hub via sysfs works. Remote wakeup works too.
+ * And suspending/resuming of platform device works too. Suspend/resume
+ * via HCD operations vector is not implemented.
+ *
+ * Iso transfer support is not implemented. Adding this would include
+ * implementing recovery from the failure to service the processed ITL
+ * fifo ram in time, which will involve chip reset.
+ *
+ * TODO:
+ + More testing of suspend/resume.
+*/
+
+/*
+  ISP116x chips require certain delays between accesses to its
+  registers. The following timing options exist.
+
+  1. Configure your memory controller (the best)
+  2. Implement platform-specific delay function possibly
+  combined with configuring the memory controller; see
+  include/linux/usb-isp116x.h for more info. Some broken
+  memory controllers line LH7A400 SMC need this. Also,
+  uncomment for that to work the following
+  USE_PLATFORM_DELAY macro.
+  3. Use ndelay (easiest, poorest). For that, uncomment
+  the following USE_NDELAY macro.
+*/
+#define USE_PLATFORM_DELAY
+//#define USE_NDELAY
+
+//#define DEBUG
+//#define VERBOSE
+/* Transfer descriptors. See dump_ptd() for printout format  */
+//#define PTD_TRACE
+/* enqueuing/finishing log of urbs */
+//#define URB_TRACE
+
+#include <linux/config.h>
+#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/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/usb_isp116x.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+
+#ifndef DEBUG
+#      define  STUB_DEBUG_FILE
+#endif
+
+#include "../core/hcd.h"
+#include "isp116x.h"
+
+#define DRIVER_VERSION "08 Apr 2005"
+#define DRIVER_DESC    "ISP116x USB Host Controller Driver"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static const char hcd_name[] = "isp116x-hcd";
+
+/*-----------------------------------------------------------------*/
+
+/*
+  Write len bytes to fifo, pad till 32-bit boundary
+ */
+static void write_ptddata_to_fifo(struct isp116x *isp116x, void *buf, int len)
+{
+       u8 *dp = (u8 *) buf;
+       u16 *dp2 = (u16 *) buf;
+       u16 w;
+       int quot = len % 4;
+
+       if ((unsigned long)dp2 & 1) {
+               /* not aligned */
+               for (; len > 1; len -= 2) {
+                       w = *dp++;
+                       w |= *dp++ << 8;
+                       isp116x_raw_write_data16(isp116x, w);
+               }
+               if (len)
+                       isp116x_write_data16(isp116x, (u16) * dp);
+       } else {
+               /* aligned */
+               for (; len > 1; len -= 2)
+                       isp116x_raw_write_data16(isp116x, *dp2++);
+               if (len)
+                       isp116x_write_data16(isp116x, 0xff & *((u8 *) dp2));
+       }
+       if (quot == 1 || quot == 2)
+               isp116x_raw_write_data16(isp116x, 0);
+}
+
+/*
+  Read len bytes from fifo and then read till 32-bit boundary.
+ */
+static void read_ptddata_from_fifo(struct isp116x *isp116x, void *buf, int len)
+{
+       u8 *dp = (u8 *) buf;
+       u16 *dp2 = (u16 *) buf;
+       u16 w;
+       int quot = len % 4;
+
+       if ((unsigned long)dp2 & 1) {
+               /* not aligned */
+               for (; len > 1; len -= 2) {
+                       w = isp116x_raw_read_data16(isp116x);
+                       *dp++ = w & 0xff;
+                       *dp++ = (w >> 8) & 0xff;
+               }
+               if (len)
+                       *dp = 0xff & isp116x_read_data16(isp116x);
+       } else {
+               /* aligned */
+               for (; len > 1; len -= 2)
+                       *dp2++ = isp116x_raw_read_data16(isp116x);
+               if (len)
+                       *(u8 *) dp2 = 0xff & isp116x_read_data16(isp116x);
+       }
+       if (quot == 1 || quot == 2)
+               isp116x_raw_read_data16(isp116x);
+}
+
+/*
+  Write ptd's and data for scheduled transfers into
+  the fifo ram. Fifo must be empty and ready.
+*/
+static void pack_fifo(struct isp116x *isp116x)
+{
+       struct isp116x_ep *ep;
+       struct ptd *ptd;
+       int buflen = isp116x->atl_last_dir == PTD_DIR_IN
+           ? isp116x->atl_bufshrt : isp116x->atl_buflen;
+       int ptd_count = 0;
+
+       isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT);
+       isp116x_write_reg16(isp116x, HCXFERCTR, buflen);
+       isp116x_write_addr(isp116x, HCATLPORT | ISP116x_WRITE_OFFSET);
+       for (ep = isp116x->atl_active; ep; ep = ep->active) {
+               ++ptd_count;
+               ptd = &ep->ptd;
+               dump_ptd(ptd);
+               dump_ptd_out_data(ptd, ep->data);
+               isp116x_write_data16(isp116x, ptd->count);
+               isp116x_write_data16(isp116x, ptd->mps);
+               isp116x_write_data16(isp116x, ptd->len);
+               isp116x_write_data16(isp116x, ptd->faddr);
+               buflen -= sizeof(struct ptd);
+               /* Skip writing data for last IN PTD */
+               if (ep->active || (isp116x->atl_last_dir != PTD_DIR_IN)) {
+                       write_ptddata_to_fifo(isp116x, ep->data, ep->length);
+                       buflen -= ALIGN(ep->length, 4);
+               }
+       }
+       BUG_ON(buflen);
+}
+
+/*
+  Read the processed ptd's and data from fifo ram back to
+  URBs' buffers. Fifo must be full and done
+*/
+static void unpack_fifo(struct isp116x *isp116x)
+{
+       struct isp116x_ep *ep;
+       struct ptd *ptd;
+       int buflen = isp116x->atl_last_dir == PTD_DIR_IN
+           ? isp116x->atl_buflen : isp116x->atl_bufshrt;
+
+       isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT);
+       isp116x_write_reg16(isp116x, HCXFERCTR, buflen);
+       isp116x_write_addr(isp116x, HCATLPORT);
+       for (ep = isp116x->atl_active; ep; ep = ep->active) {
+               ptd = &ep->ptd;
+               ptd->count = isp116x_read_data16(isp116x);
+               ptd->mps = isp116x_read_data16(isp116x);
+               ptd->len = isp116x_read_data16(isp116x);
+               ptd->faddr = isp116x_read_data16(isp116x);
+               buflen -= sizeof(struct ptd);
+               /* Skip reading data for last Setup or Out PTD */
+               if (ep->active || (isp116x->atl_last_dir == PTD_DIR_IN)) {
+                       read_ptddata_from_fifo(isp116x, ep->data, ep->length);
+                       buflen -= ALIGN(ep->length, 4);
+               }
+               dump_ptd(ptd);
+               dump_ptd_in_data(ptd, ep->data);
+       }
+       BUG_ON(buflen);
+}
+
+/*---------------------------------------------------------------*/
+
+/*
+  Set up PTD's.
+*/
+static void preproc_atl_queue(struct isp116x *isp116x)
+{
+       struct isp116x_ep *ep;
+       struct urb *urb;
+       struct ptd *ptd;
+       u16 toggle, dir, len;
+
+       for (ep = isp116x->atl_active; ep; ep = ep->active) {
+               BUG_ON(list_empty(&ep->hep->urb_list));
+               urb = container_of(ep->hep->urb_list.next,
+                                  struct urb, urb_list);
+               ptd = &ep->ptd;
+               len = ep->length;
+               spin_lock(&urb->lock);
+               ep->data = (unsigned char *)urb->transfer_buffer
+                   + urb->actual_length;
+
+               switch (ep->nextpid) {
+               case USB_PID_IN:
+                       toggle = usb_gettoggle(urb->dev, ep->epnum, 0);
+                       dir = PTD_DIR_IN;
+                       break;
+               case USB_PID_OUT:
+                       toggle = usb_gettoggle(urb->dev, ep->epnum, 1);
+                       dir = PTD_DIR_OUT;
+                       break;
+               case USB_PID_SETUP:
+                       toggle = 0;
+                       dir = PTD_DIR_SETUP;
+                       len = sizeof(struct usb_ctrlrequest);
+                       ep->data = urb->setup_packet;
+                       break;
+               case USB_PID_ACK:
+                       toggle = 1;
+                       len = 0;
+                       dir = (urb->transfer_buffer_length
+                              && usb_pipein(urb->pipe))
+                           ? PTD_DIR_OUT : PTD_DIR_IN;
+                       break;
+               default:
+                       /* To please gcc */
+                       toggle = dir = 0;
+                       ERR("%s %d: ep->nextpid %d\n", __func__, __LINE__,
+                           ep->nextpid);
+                       BUG_ON(1);
+               }
+
+               ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | PTD_TOGGLE(toggle);
+               ptd->mps = PTD_MPS(ep->maxpacket)
+                   | PTD_SPD(urb->dev->speed == USB_SPEED_LOW)
+                   | PTD_EP(ep->epnum);
+               ptd->len = PTD_LEN(len) | PTD_DIR(dir);
+               ptd->faddr = PTD_FA(usb_pipedevice(urb->pipe));
+               spin_unlock(&urb->lock);
+               if (!ep->active) {
+                       ptd->mps |= PTD_LAST_MSK;
+                       isp116x->atl_last_dir = dir;
+               }
+               isp116x->atl_bufshrt = sizeof(struct ptd) + isp116x->atl_buflen;
+               isp116x->atl_buflen = isp116x->atl_bufshrt + ALIGN(len, 4);
+       }
+}
+
+/*
+  Analyze transfer results, handle partial transfers and errors
+*/
+static void postproc_atl_queue(struct isp116x *isp116x)
+{
+       struct isp116x_ep *ep;
+       struct urb *urb;
+       struct usb_device *udev;
+       struct ptd *ptd;
+       int short_not_ok;
+       u8 cc;
+
+       for (ep = isp116x->atl_active; ep; ep = ep->active) {
+               BUG_ON(list_empty(&ep->hep->urb_list));
+               urb =
+                   container_of(ep->hep->urb_list.next, struct urb, urb_list);
+               udev = urb->dev;
+               ptd = &ep->ptd;
+               cc = PTD_GET_CC(ptd);
+
+               spin_lock(&urb->lock);
+               short_not_ok = 1;
+
+               /* Data underrun is special. For allowed underrun
+                  we clear the error and continue as normal. For
+                  forbidden underrun we finish the DATA stage
+                  immediately while for control transfer,
+                  we do a STATUS stage. */
+               if (cc == TD_DATAUNDERRUN) {
+                       if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) {
+                               DBG("Allowed data underrun\n");
+                               cc = TD_CC_NOERROR;
+                               short_not_ok = 0;
+                       } else {
+                               ep->error_count = 1;
+                               if (usb_pipecontrol(urb->pipe))
+                                       ep->nextpid = USB_PID_ACK;
+                               else
+                                       usb_settoggle(udev, ep->epnum,
+                                                     ep->nextpid ==
+                                                     USB_PID_OUT,
+                                                     PTD_GET_TOGGLE(ptd) ^ 1);
+                               urb->status = cc_to_error[TD_DATAUNDERRUN];
+                               spin_unlock(&urb->lock);
+                               continue;
+                       }
+               }
+               /* Keep underrun error through the STATUS stage */
+               if (urb->status == cc_to_error[TD_DATAUNDERRUN])
+                       cc = TD_DATAUNDERRUN;
+
+               if (cc != TD_CC_NOERROR && cc != TD_NOTACCESSED
+                   && (++ep->error_count >= 3 || cc == TD_CC_STALL
+                       || cc == TD_DATAOVERRUN)) {
+                       if (urb->status == -EINPROGRESS)
+                               urb->status = cc_to_error[cc];
+                       if (ep->nextpid == USB_PID_ACK)
+                               ep->nextpid = 0;
+                       spin_unlock(&urb->lock);
+                       continue;
+               }
+               /* According to usb spec, zero-length Int transfer signals
+                  finishing of the urb. Hey, does this apply only
+                  for IN endpoints? */
+               if (usb_pipeint(urb->pipe) && !PTD_GET_LEN(ptd)) {
+                       if (urb->status == -EINPROGRESS)
+                               urb->status = 0;
+                       spin_unlock(&urb->lock);
+                       continue;
+               }
+
+               /* Relax after previously failed, but later succeeded
+                  or correctly NAK'ed retransmission attempt */
+               if (ep->error_count
+                   && (cc == TD_CC_NOERROR || cc == TD_NOTACCESSED))
+                       ep->error_count = 0;
+
+               /* Take into account idiosyncracies of the isp116x chip
+                  regarding toggle bit for failed transfers */
+               if (ep->nextpid == USB_PID_OUT)
+                       usb_settoggle(udev, ep->epnum, 1, PTD_GET_TOGGLE(ptd)
+                                     ^ (ep->error_count > 0));
+               else if (ep->nextpid == USB_PID_IN)
+                       usb_settoggle(udev, ep->epnum, 0, PTD_GET_TOGGLE(ptd)
+                                     ^ (ep->error_count > 0));
+
+               switch (ep->nextpid) {
+               case USB_PID_IN:
+               case USB_PID_OUT:
+                       urb->actual_length += PTD_GET_COUNT(ptd);
+                       if (PTD_GET_ACTIVE(ptd)
+                           || (cc != TD_CC_NOERROR && cc < 0x0E))
+                               break;
+                       if (urb->transfer_buffer_length != urb->actual_length) {
+                               if (short_not_ok)
+                                       break;
+                       } else {
+                               if (urb->transfer_flags & URB_ZERO_PACKET
+                                   && ep->nextpid == USB_PID_OUT
+                                   && !(PTD_GET_COUNT(ptd) % ep->maxpacket)) {
+                                       DBG("Zero packet requested\n");
+                                       break;
+                               }
+                       }
+                       /* All data for this URB is transferred, let's finish */
+                       if (usb_pipecontrol(urb->pipe))
+                               ep->nextpid = USB_PID_ACK;
+                       else if (urb->status == -EINPROGRESS)
+                               urb->status = 0;
+                       break;
+               case USB_PID_SETUP:
+                       if (PTD_GET_ACTIVE(ptd)
+                           || (cc != TD_CC_NOERROR && cc < 0x0E))
+                               break;
+                       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:
+                       if (PTD_GET_ACTIVE(ptd)
+                           || (cc != TD_CC_NOERROR && cc < 0x0E))
+                               break;
+                       if (urb->status == -EINPROGRESS)
+                               urb->status = 0;
+                       ep->nextpid = 0;
+                       break;
+               default:
+                       BUG_ON(1);
+               }
+               spin_unlock(&urb->lock);
+       }
+}
+
+/*
+  Take done or failed requests out of schedule. Give back
+  processed urbs.
+*/
+static void finish_request(struct isp116x *isp116x, struct isp116x_ep *ep,
+                          struct urb *urb, struct pt_regs *regs)
+__releases(isp116x->lock) __acquires(isp116x->lock)
+{
+       unsigned i;
+
+       urb->hcpriv = NULL;
+       ep->error_count = 0;
+
+       if (usb_pipecontrol(urb->pipe))
+               ep->nextpid = USB_PID_SETUP;
+
+       urb_dbg(urb, "Finish");
+
+       spin_unlock(&isp116x->lock);
+       usb_hcd_giveback_urb(isp116x_to_hcd(isp116x), urb, regs);
+       spin_lock(&isp116x->lock);
+
+       /* take idle endpoints out of the schedule */
+       if (!list_empty(&ep->hep->urb_list))
+               return;
+
+       /* async deschedule */
+       if (!list_empty(&ep->schedule)) {
+               list_del_init(&ep->schedule);
+               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 isp116x_ep *temp;
+               struct isp116x_ep **prev = &isp116x->periodic[i];
+
+               while (*prev && ((temp = *prev) != ep))
+                       prev = &temp->next;
+               if (*prev)
+                       *prev = ep->next;
+               isp116x->load[i] -= ep->load;
+       }
+       ep->branch = PERIODIC_SIZE;
+       isp116x_to_hcd(isp116x)->self.bandwidth_allocated -=
+           ep->load / ep->period;
+
+       /* switch irq type? */
+       if (!--isp116x->periodic_count) {
+               isp116x->irqenb &= ~HCuPINT_SOF;
+               isp116x->irqenb |= HCuPINT_ATL;
+       }
+}
+
+/*
+  Scan transfer lists, schedule transfers, send data off
+  to chip.
+ */
+static void start_atl_transfers(struct isp116x *isp116x)
+{
+       struct isp116x_ep *last_ep = NULL, *ep;
+       struct urb *urb;
+       u16 load = 0;
+       int len, index, speed, byte_time;
+
+       if (atomic_read(&isp116x->atl_finishing))
+               return;
+
+       if (!HC_IS_RUNNING(isp116x_to_hcd(isp116x)->state))
+               return;
+
+       /* FIFO not empty? */
+       if (isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_FULL)
+               return;
+
+       isp116x->atl_active = NULL;
+       isp116x->atl_buflen = isp116x->atl_bufshrt = 0;
+
+       /* Schedule int transfers */
+       if (isp116x->periodic_count) {
+               isp116x->fmindex = index =
+                   (isp116x->fmindex + 1) & (PERIODIC_SIZE - 1);
+               if ((load = isp116x->load[index])) {
+                       /* Bring all int transfers for this frame
+                          into the active queue */
+                       isp116x->atl_active = last_ep =
+                           isp116x->periodic[index];
+                       while (last_ep->next)
+                               last_ep = (last_ep->active = last_ep->next);
+                       last_ep->active = NULL;
+               }
+       }
+
+       /* Schedule control/bulk transfers */
+       list_for_each_entry(ep, &isp116x->async, schedule) {
+               urb = container_of(ep->hep->urb_list.next,
+                                  struct urb, urb_list);
+               speed = urb->dev->speed;
+               byte_time = speed == USB_SPEED_LOW
+                   ? BYTE_TIME_LOWSPEED : BYTE_TIME_FULLSPEED;
+
+               if (ep->nextpid == USB_PID_SETUP) {
+                       len = sizeof(struct usb_ctrlrequest);
+               } else if (ep->nextpid == USB_PID_ACK) {
+                       len = 0;
+               } else {
+                       /* Find current free length ... */
+                       len = (MAX_LOAD_LIMIT - load) / byte_time;
+
+                       /* ... then limit it to configured max size ... */
+                       len = min(len, speed == USB_SPEED_LOW ?
+                                 MAX_TRANSFER_SIZE_LOWSPEED :
+                                 MAX_TRANSFER_SIZE_FULLSPEED);
+
+                       /* ... and finally cut to the multiple of MaxPacketSize,
+                          or to the real length if there's enough room. */
+                       if (len <
+                           (urb->transfer_buffer_length -
+                            urb->actual_length)) {
+                               len -= len % ep->maxpacket;
+                               if (!len)
+                                       continue;
+                       } else
+                               len = urb->transfer_buffer_length -
+                                   urb->actual_length;
+                       BUG_ON(len < 0);
+               }
+
+               load += len * byte_time;
+               if (load > MAX_LOAD_LIMIT)
+                       break;
+
+               ep->active = NULL;
+               ep->length = len;
+               if (last_ep)
+                       last_ep->active = ep;
+               else
+                       isp116x->atl_active = ep;
+               last_ep = ep;
+       }
+
+       /* Avoid starving of endpoints */
+       if ((&isp116x->async)->next != (&isp116x->async)->prev)
+               list_move(&isp116x->async, (&isp116x->async)->next);
+
+       if (isp116x->atl_active) {
+               preproc_atl_queue(isp116x);
+               pack_fifo(isp116x);
+       }
+}
+
+/*
+  Finish the processed transfers
+*/
+static void finish_atl_transfers(struct isp116x *isp116x, struct pt_regs *regs)
+{
+       struct isp116x_ep *ep;
+       struct urb *urb;
+
+       if (!isp116x->atl_active)
+               return;
+       /* Fifo not ready? */
+       if (!(isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_DONE))
+               return;
+
+       atomic_inc(&isp116x->atl_finishing);
+       unpack_fifo(isp116x);
+       postproc_atl_queue(isp116x);
+       for (ep = isp116x->atl_active; ep; ep = ep->active) {
+               urb =
+                   container_of(ep->hep->urb_list.next, struct urb, urb_list);
+               /* USB_PID_ACK check here avoids finishing of
+                  control transfers, for which TD_DATAUNDERRUN
+                  occured, while URB_SHORT_NOT_OK was set */
+               if (urb && urb->status != -EINPROGRESS
+                   && ep->nextpid != USB_PID_ACK)
+                       finish_request(isp116x, ep, urb, regs);
+       }
+       atomic_dec(&isp116x->atl_finishing);
+}
+
+static irqreturn_t isp116x_irq(struct usb_hcd *hcd, struct pt_regs *regs)
+{
+       struct isp116x *isp116x = hcd_to_isp116x(hcd);
+       u16 irqstat;
+       irqreturn_t ret = IRQ_NONE;
+
+       spin_lock(&isp116x->lock);
+       isp116x_write_reg16(isp116x, HCuPINTENB, 0);
+       irqstat = isp116x_read_reg16(isp116x, HCuPINT);
+       isp116x_write_reg16(isp116x, HCuPINT, irqstat);
+
+       if (irqstat & (HCuPINT_ATL | HCuPINT_SOF)) {
+               ret = IRQ_HANDLED;
+               finish_atl_transfers(isp116x, regs);
+       }
+
+       if (irqstat & HCuPINT_OPR) {
+               u32 intstat = isp116x_read_reg32(isp116x, HCINTSTAT);
+               isp116x_write_reg32(isp116x, HCINTSTAT, intstat);
+               if (intstat & HCINT_UE) {
+                       ERR("Unrecoverable error\n");
+                       /* What should we do here? Reset?  */
+               }
+               if (intstat & HCINT_RHSC) {
+                       isp116x->rhstatus =
+                           isp116x_read_reg32(isp116x, HCRHSTATUS);
+                       isp116x->rhport[0] =
+                           isp116x_read_reg32(isp116x, HCRHPORT1);
+                       isp116x->rhport[1] =
+                           isp116x_read_reg32(isp116x, HCRHPORT2);
+               }
+               if (intstat & HCINT_RD) {
+                       DBG("---- remote wakeup\n");
+                       schedule_work(&isp116x->rh_resume);
+                       ret = IRQ_HANDLED;
+               }
+               irqstat &= ~HCuPINT_OPR;
+               ret = IRQ_HANDLED;
+       }
+
+       if (irqstat & (HCuPINT_ATL | HCuPINT_SOF)) {
+               start_atl_transfers(isp116x);
+       }
+
+       isp116x_write_reg16(isp116x, HCuPINTENB, isp116x->irqenb);
+       spin_unlock(&isp116x->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.
+ */
+
+/* out of 1000 us */
+#define        MAX_PERIODIC_LOAD       600
+static int balance(struct isp116x *isp116x, 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 || isp116x->load[branch] > isp116x->load[i]) {
+                       int j;
+
+                       for (j = i; j < PERIODIC_SIZE; j += period) {
+                               if ((isp116x->load[j] + load)
+                                   > MAX_PERIODIC_LOAD)
+                                       break;
+                       }
+                       if (j < PERIODIC_SIZE)
+                               continue;
+                       branch = i;
+               }
+       }
+       return branch;
+}
+
+/* NB! ALL the code above this point runs with isp116x->lock
+   held, irqs off
+*/
+
+/*-----------------------------------------------------------------*/
+
+static int isp116x_urb_enqueue(struct usb_hcd *hcd,
+                              struct usb_host_endpoint *hep, struct urb *urb,
+                              int mem_flags)
+{
+       struct isp116x *isp116x = hcd_to_isp116x(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 isp116x_ep *ep = NULL;
+       unsigned long flags;
+       int i;
+       int ret = 0;
+
+       urb_dbg(urb, "Enqueue");
+
+       if (type == PIPE_ISOCHRONOUS) {
+               ERR("Isochronous transfers not supported\n");
+               urb_dbg(urb, "Refused to enqueue");
+               return -ENXIO;
+       }
+       /* avoid all allocations within spinlocks: request or endpoint */
+       if (!hep->hcpriv) {
+               ep = kcalloc(1, sizeof *ep, (__force unsigned)mem_flags);
+               if (!ep)
+                       return -ENOMEM;
+       }
+
+       spin_lock_irqsave(&isp116x->lock, flags);
+       if (!HC_IS_RUNNING(hcd->state)) {
+               ret = -ENODEV;
+               goto fail;
+       }
+
+       if (hep->hcpriv)
+               ep = hep->hcpriv;
+       else {
+               INIT_LIST_HEAD(&ep->schedule);
+               ep->udev = usb_get_dev(udev);
+               ep->epnum = epnum;
+               ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out);
+               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 (urb->interval) {
+                       /*
+                          With INT URBs submitted, the driver works with SOF
+                          interrupt enabled and ATL interrupt disabled. After
+                          the PTDs are written to fifo ram, the chip starts
+                          fifo processing and usb transfers after the next
+                          SOF and continues until the transfers are finished
+                          (succeeded or failed) or the frame ends. Therefore,
+                          the transfers occur only in every second frame,
+                          while fifo reading/writing and data processing
+                          occur in every other second frame. */
+                       if (urb->interval < 2)
+                               urb->interval = 2;
+                       if (urb->interval > 2 * PERIODIC_SIZE)
+                               urb->interval = 2 * PERIODIC_SIZE;
+                       ep->period = urb->interval >> 1;
+                       ep->branch = PERIODIC_SIZE;
+                       ep->load = usb_calc_bus_time(udev->speed,
+                                                    !is_out,
+                                                    (type == PIPE_ISOCHRONOUS),
+                                                    usb_maxpacket(udev, pipe,
+                                                                  is_out)) /
+                           1000;
+               }
+               hep->hcpriv = ep;
+               ep->hep = hep;
+       }
+
+       /* maybe put endpoint into schedule */
+       switch (type) {
+       case PIPE_CONTROL:
+       case PIPE_BULK:
+               if (list_empty(&ep->schedule))
+                       list_add_tail(&ep->schedule, &isp116x->async);
+               break;
+       case PIPE_INTERRUPT:
+               urb->interval = ep->period;
+               ep->length = min((int)ep->maxpacket,
+                                urb->transfer_buffer_length);
+
+               /* urb submitted for already existing endpoint */
+               if (ep->branch < PERIODIC_SIZE)
+                       break;
+
+               ret = ep->branch = balance(isp116x, ep->period, ep->load);
+               if (ret < 0)
+                       goto fail;
+               ret = 0;
+
+               urb->start_frame = (isp116x->fmindex & (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 isp116x_ep **prev = &isp116x->periodic[i];
+                       struct isp116x_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;
+                       }
+                       isp116x->load[i] += ep->load;
+               }
+               hcd->self.bandwidth_allocated += ep->load / ep->period;
+
+               /* switch over to SOFint */
+               if (!isp116x->periodic_count++) {
+                       isp116x->irqenb &= ~HCuPINT_ATL;
+                       isp116x->irqenb |= HCuPINT_SOF;
+                       isp116x_write_reg16(isp116x, HCuPINTENB,
+                                           isp116x->irqenb);
+               }
+       }
+
+       /* in case of unlink-during-submit */
+       spin_lock(&urb->lock);
+       if (urb->status != -EINPROGRESS) {
+               spin_unlock(&urb->lock);
+               finish_request(isp116x, ep, urb, NULL);
+               ret = 0;
+               goto fail;
+       }
+       urb->hcpriv = hep;
+       spin_unlock(&urb->lock);
+       start_atl_transfers(isp116x);
+
+      fail:
+       spin_unlock_irqrestore(&isp116x->lock, flags);
+       return ret;
+}
+
+/*
+   Dequeue URBs.
+*/
+static int isp116x_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+{
+       struct isp116x *isp116x = hcd_to_isp116x(hcd);
+       struct usb_host_endpoint *hep;
+       struct isp116x_ep *ep, *ep_act;
+       unsigned long flags;
+
+       spin_lock_irqsave(&isp116x->lock, flags);
+       hep = urb->hcpriv;
+       /* URB already unlinked (or never linked)? */
+       if (!hep) {
+               spin_unlock_irqrestore(&isp116x->lock, flags);
+               return 0;
+       }
+       ep = hep->hcpriv;
+       WARN_ON(hep != ep->hep);
+
+       /* In front of queue? */
+       if (ep->hep->urb_list.next == &urb->urb_list)
+               /* active? */
+               for (ep_act = isp116x->atl_active; ep_act;
+                    ep_act = ep_act->active)
+                       if (ep_act == ep) {
+                               VDBG("dequeue, urb %p active; wait for irq\n",
+                                    urb);
+                               urb = NULL;
+                               break;
+                       }
+
+       if (urb)
+               finish_request(isp116x, ep, urb, NULL);
+
+       spin_unlock_irqrestore(&isp116x->lock, flags);
+       return 0;
+}
+
+static void isp116x_endpoint_disable(struct usb_hcd *hcd,
+                                    struct usb_host_endpoint *hep)
+{
+       int i;
+       struct isp116x_ep *ep = hep->hcpriv;;
+
+       if (!ep)
+               return;
+
+       /* assume we'd just wait for the irq */
+       for (i = 0; i < 100 && !list_empty(&hep->urb_list); i++)
+               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 isp116x_get_frame(struct usb_hcd *hcd)
+{
+       struct isp116x *isp116x = hcd_to_isp116x(hcd);
+       u32 fmnum;
+       unsigned long flags;
+
+       spin_lock_irqsave(&isp116x->lock, flags);
+       fmnum = isp116x_read_reg32(isp116x, HCFMNUM);
+       spin_unlock_irqrestore(&isp116x->lock, flags);
+       return (int)fmnum;
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+  Adapted from ohci-hub.c. Currently we don't support autosuspend.
+*/
+static int isp116x_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+       struct isp116x *isp116x = hcd_to_isp116x(hcd);
+       int ports, i, changed = 0;
+
+       if (!HC_IS_RUNNING(hcd->state))
+               return -ESHUTDOWN;
+
+       ports = isp116x->rhdesca & RH_A_NDP;
+
+       /* init status */
+       if (isp116x->rhstatus & (RH_HS_LPSC | RH_HS_OCIC))
+               buf[0] = changed = 1;
+       else
+               buf[0] = 0;
+
+       for (i = 0; i < ports; i++) {
+               u32 status = isp116x->rhport[i];
+
+               if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
+                             | RH_PS_OCIC | RH_PS_PRSC)) {
+                       changed = 1;
+                       buf[0] |= 1 << (i + 1);
+                       continue;
+               }
+       }
+       return changed;
+}
+
+static void isp116x_hub_descriptor(struct isp116x *isp116x,
+                                  struct usb_hub_descriptor *desc)
+{
+       u32 reg = isp116x->rhdesca;
+
+       desc->bDescriptorType = 0x29;
+       desc->bDescLength = 9;
+       desc->bHubContrCurrent = 0;
+       desc->bNbrPorts = (u8) (reg & 0x3);
+       /* Power switching, device type, overcurrent. */
+       desc->wHubCharacteristics =
+           (__force __u16) cpu_to_le16((u16) ((reg >> 8) & 0x1f));
+       desc->bPwrOn2PwrGood = (u8) ((reg >> 24) & 0xff);
+       /* two bitmaps:  ports removable, and legacy PortPwrCtrlMask */
+       desc->bitmap[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1;
+       desc->bitmap[1] = ~0;
+}
+
+/* Perform reset of a given port.
+   It would be great to just start the reset and let the
+   USB core to clear the reset in due time. However,
+   root hub ports should be reset for at least 50 ms, while
+   our chip stays in reset for about 10 ms. I.e., we must
+   repeatedly reset it ourself here.
+*/
+static inline void root_port_reset(struct isp116x *isp116x, unsigned port)
+{
+       u32 tmp;
+       unsigned long flags, t;
+
+       /* Root hub reset should be 50 ms, but some devices
+          want it even longer. */
+       t = jiffies + msecs_to_jiffies(100);
+
+       while (time_before(jiffies, t)) {
+               spin_lock_irqsave(&isp116x->lock, flags);
+               /* spin until any current reset finishes */
+               for (;;) {
+                       tmp = isp116x_read_reg32(isp116x, port ?
+                                                HCRHPORT2 : HCRHPORT1);
+                       if (!(tmp & RH_PS_PRS))
+                               break;
+                       udelay(500);
+               }
+               /* Don't reset a disconnected port */
+               if (!(tmp & RH_PS_CCS)) {
+                       spin_unlock_irqrestore(&isp116x->lock, flags);
+                       break;
+               }
+               /* Reset lasts 10ms (claims datasheet) */
+               isp116x_write_reg32(isp116x, port ? HCRHPORT2 :
+                                   HCRHPORT1, (RH_PS_PRS));
+               spin_unlock_irqrestore(&isp116x->lock, flags);
+               msleep(10);
+       }
+}
+
+/* Adapted from ohci-hub.c */
+static int isp116x_hub_control(struct usb_hcd *hcd,
+                              u16 typeReq,
+                              u16 wValue, u16 wIndex, char *buf, u16 wLength)
+{
+       struct isp116x *isp116x = hcd_to_isp116x(hcd);
+       int ret = 0;
+       unsigned long flags;
+       int ports = isp116x->rhdesca & RH_A_NDP;
+       u32 tmp = 0;
+
+       switch (typeReq) {
+       case ClearHubFeature:
+               DBG("ClearHubFeature: ");
+               switch (wValue) {
+               case C_HUB_OVER_CURRENT:
+                       DBG("C_HUB_OVER_CURRENT\n");
+                       spin_lock_irqsave(&isp116x->lock, flags);
+                       isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_OCIC);
+                       spin_unlock_irqrestore(&isp116x->lock, flags);
+               case C_HUB_LOCAL_POWER:
+                       DBG("C_HUB_LOCAL_POWER\n");
+                       break;
+               default:
+                       goto error;
+               }
+               break;
+       case SetHubFeature:
+               DBG("SetHubFeature: ");
+               switch (wValue) {
+               case C_HUB_OVER_CURRENT:
+               case C_HUB_LOCAL_POWER:
+                       DBG("C_HUB_OVER_CURRENT or C_HUB_LOCAL_POWER\n");
+                       break;
+               default:
+                       goto error;
+               }
+               break;
+       case GetHubDescriptor:
+               DBG("GetHubDescriptor\n");
+               isp116x_hub_descriptor(isp116x,
+                                      (struct usb_hub_descriptor *)buf);
+               break;
+       case GetHubStatus:
+               DBG("GetHubStatus\n");
+               *(__le32 *) buf = cpu_to_le32(0);
+               break;
+       case GetPortStatus:
+               DBG("GetPortStatus\n");
+               if (!wIndex || wIndex > ports)
+                       goto error;
+               tmp = isp116x->rhport[--wIndex];
+               *(__le32 *) buf = cpu_to_le32(tmp);
+               DBG("GetPortStatus: port[%d]  %08x\n", wIndex + 1, tmp);
+               break;
+       case ClearPortFeature:
+               DBG("ClearPortFeature: ");
+               if (!wIndex || wIndex > ports)
+                       goto error;
+               wIndex--;
+
+               switch (wValue) {
+               case USB_PORT_FEAT_ENABLE:
+                       DBG("USB_PORT_FEAT_ENABLE\n");
+                       tmp = RH_PS_CCS;
+                       break;
+               case USB_PORT_FEAT_C_ENABLE:
+                       DBG("USB_PORT_FEAT_C_ENABLE\n");
+                       tmp = RH_PS_PESC;
+                       break;
+               case USB_PORT_FEAT_SUSPEND:
+                       DBG("USB_PORT_FEAT_SUSPEND\n");
+                       tmp = RH_PS_POCI;
+                       break;
+               case USB_PORT_FEAT_C_SUSPEND:
+                       DBG("USB_PORT_FEAT_C_SUSPEND\n");
+                       tmp = RH_PS_PSSC;
+                       break;
+               case USB_PORT_FEAT_POWER:
+                       DBG("USB_PORT_FEAT_POWER\n");
+                       tmp = RH_PS_LSDA;
+                       break;
+               case USB_PORT_FEAT_C_CONNECTION:
+                       DBG("USB_PORT_FEAT_C_CONNECTION\n");
+                       tmp = RH_PS_CSC;
+                       break;
+               case USB_PORT_FEAT_C_OVER_CURRENT:
+                       DBG("USB_PORT_FEAT_C_OVER_CURRENT\n");
+                       tmp = RH_PS_OCIC;
+                       break;
+               case USB_PORT_FEAT_C_RESET:
+                       DBG("USB_PORT_FEAT_C_RESET\n");
+                       tmp = RH_PS_PRSC;
+                       break;
+               default:
+                       goto error;
+               }
+               spin_lock_irqsave(&isp116x->lock, flags);
+               isp116x_write_reg32(isp116x, wIndex
+                                   ? HCRHPORT2 : HCRHPORT1, tmp);
+               isp116x->rhport[wIndex] =
+                   isp116x_read_reg32(isp116x, wIndex ? HCRHPORT2 : HCRHPORT1);
+               spin_unlock_irqrestore(&isp116x->lock, flags);
+               break;
+       case SetPortFeature:
+               DBG("SetPortFeature: ");
+               if (!wIndex || wIndex > ports)
+                       goto error;
+               wIndex--;
+               switch (wValue) {
+               case USB_PORT_FEAT_SUSPEND:
+                       DBG("USB_PORT_FEAT_SUSPEND\n");
+                       spin_lock_irqsave(&isp116x->lock, flags);
+                       isp116x_write_reg32(isp116x, wIndex
+                                           ? HCRHPORT2 : HCRHPORT1, RH_PS_PSS);
+                       break;
+               case USB_PORT_FEAT_POWER:
+                       DBG("USB_PORT_FEAT_POWER\n");
+                       spin_lock_irqsave(&isp116x->lock, flags);
+                       isp116x_write_reg32(isp116x, wIndex
+                                           ? HCRHPORT2 : HCRHPORT1, RH_PS_PPS);
+                       break;
+               case USB_PORT_FEAT_RESET:
+                       DBG("USB_PORT_FEAT_RESET\n");
+                       root_port_reset(isp116x, wIndex);
+                       spin_lock_irqsave(&isp116x->lock, flags);
+                       break;
+               default:
+                       goto error;
+               }
+               isp116x->rhport[wIndex] =
+                   isp116x_read_reg32(isp116x, wIndex ? HCRHPORT2 : HCRHPORT1);
+               spin_unlock_irqrestore(&isp116x->lock, flags);
+               break;
+
+       default:
+             error:
+               /* "protocol stall" on error */
+               DBG("PROTOCOL STALL\n");
+               ret = -EPIPE;
+       }
+       return ret;
+}
+
+#ifdef CONFIG_PM
+
+static int isp116x_hub_suspend(struct usb_hcd *hcd)
+{
+       struct isp116x *isp116x = hcd_to_isp116x(hcd);
+       unsigned long flags;
+       u32 val;
+       int ret = 0;
+
+       spin_lock_irqsave(&isp116x->lock, flags);
+
+       val = isp116x_read_reg32(isp116x, HCCONTROL);
+       switch (val & HCCONTROL_HCFS) {
+       case HCCONTROL_USB_OPER:
+               hcd->state = HC_STATE_QUIESCING;
+               val &= (~HCCONTROL_HCFS & ~HCCONTROL_RWE);
+               val |= HCCONTROL_USB_SUSPEND;
+               if (hcd->remote_wakeup)
+                       val |= HCCONTROL_RWE;
+               /* Wait for usb transfers to finish */
+               mdelay(2);
+               isp116x_write_reg32(isp116x, HCCONTROL, val);
+               hcd->state = HC_STATE_SUSPENDED;
+               /* Wait for devices to suspend */
+               mdelay(5);
+       case HCCONTROL_USB_SUSPEND:
+               break;
+       case HCCONTROL_USB_RESUME:
+               isp116x_write_reg32(isp116x, HCCONTROL,
+                                   (val & ~HCCONTROL_HCFS) |
+                                   HCCONTROL_USB_RESET);
+       case HCCONTROL_USB_RESET:
+               ret = -EBUSY;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       spin_unlock_irqrestore(&isp116x->lock, flags);
+       return ret;
+}
+
+static int isp116x_hub_resume(struct usb_hcd *hcd)
+{
+       struct isp116x *isp116x = hcd_to_isp116x(hcd);
+       u32 val;
+       int ret = -EINPROGRESS;
+
+       msleep(5);
+       spin_lock_irq(&isp116x->lock);
+
+       val = isp116x_read_reg32(isp116x, HCCONTROL);
+       switch (val & HCCONTROL_HCFS) {
+       case HCCONTROL_USB_SUSPEND:
+               val &= ~HCCONTROL_HCFS;
+               val |= HCCONTROL_USB_RESUME;
+               isp116x_write_reg32(isp116x, HCCONTROL, val);
+       case HCCONTROL_USB_RESUME:
+               break;
+       case HCCONTROL_USB_OPER:
+               /* Without setting power_state here the
+                  SUSPENDED state won't be removed from
+                  sysfs/usbN/power.state as a response to remote
+                  wakeup. Maybe in the future. */
+               hcd->self.root_hub->dev.power.power_state = PMSG_ON;
+               ret = 0;
+               break;
+       default:
+               ret = -EBUSY;
+       }
+
+       if (ret != -EINPROGRESS) {
+               spin_unlock_irq(&isp116x->lock);
+               return ret;
+       }
+
+       val = isp116x->rhdesca & RH_A_NDP;
+       while (val--) {
+               u32 stat =
+                   isp116x_read_reg32(isp116x, val ? HCRHPORT2 : HCRHPORT1);
+               /* force global, not selective, resume */
+               if (!(stat & RH_PS_PSS))
+                       continue;
+               DBG("%s: Resuming port %d\n", __func__, val);
+               isp116x_write_reg32(isp116x, RH_PS_POCI, val
+                                   ? HCRHPORT2 : HCRHPORT1);
+       }
+       spin_unlock_irq(&isp116x->lock);
+
+       hcd->state = HC_STATE_RESUMING;
+       mdelay(20);
+
+       /* Go operational */
+       spin_lock_irq(&isp116x->lock);
+       val = isp116x_read_reg32(isp116x, HCCONTROL);
+       isp116x_write_reg32(isp116x, HCCONTROL,
+                           (val & ~HCCONTROL_HCFS) | HCCONTROL_USB_OPER);
+       spin_unlock_irq(&isp116x->lock);
+       /* see analogous comment above */
+       hcd->self.root_hub->dev.power.power_state = PMSG_ON;
+       hcd->state = HC_STATE_RUNNING;
+
+       return 0;
+}
+
+static void isp116x_rh_resume(void *_hcd)
+{
+       struct usb_hcd *hcd = _hcd;
+
+       usb_resume_device(hcd->self.root_hub);
+}
+
+#else
+
+#define        isp116x_hub_suspend     NULL
+#define        isp116x_hub_resume      NULL
+
+static void isp116x_rh_resume(void *_hcd)
+{
+}
+
+#endif
+
+/*-----------------------------------------------------------------*/
+
+#ifdef STUB_DEBUG_FILE
+
+static inline void create_debug_file(struct isp116x *isp116x)
+{
+}
+
+static inline void remove_debug_file(struct isp116x *isp116x)
+{
+}
+
+#else
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+static void dump_irq(struct seq_file *s, char *label, u16 mask)
+{
+       seq_printf(s, "%s %04x%s%s%s%s%s%s\n", label, mask,
+                  mask & HCuPINT_CLKRDY ? " clkrdy" : "",
+                  mask & HCuPINT_SUSP ? " susp" : "",
+                  mask & HCuPINT_OPR ? " opr" : "",
+                  mask & HCuPINT_AIIEOT ? " eot" : "",
+                  mask & HCuPINT_ATL ? " atl" : "",
+                  mask & HCuPINT_SOF ? " sof" : "");
+}
+
+static void dump_int(struct seq_file *s, char *label, u32 mask)
+{
+       seq_printf(s, "%s %08x%s%s%s%s%s%s%s\n", label, mask,
+                  mask & HCINT_MIE ? " MIE" : "",
+                  mask & HCINT_RHSC ? " rhsc" : "",
+                  mask & HCINT_FNO ? " fno" : "",
+                  mask & HCINT_UE ? " ue" : "",
+                  mask & HCINT_RD ? " rd" : "",
+                  mask & HCINT_SF ? " sof" : "", mask & HCINT_SO ? " so" : "");
+}
+
+static int proc_isp116x_show(struct seq_file *s, void *unused)
+{
+       struct isp116x *isp116x = s->private;
+       struct isp116x_ep *ep;
+       struct urb *urb;
+       unsigned i;
+       char *str;
+
+       seq_printf(s, "%s\n%s version %s\n",
+                  isp116x_to_hcd(isp116x)->product_desc, hcd_name,
+                  DRIVER_VERSION);
+
+       if (HC_IS_SUSPENDED(isp116x_to_hcd(isp116x)->state)) {
+               seq_printf(s, "HCD is suspended\n");
+               return 0;
+       }
+       if (!HC_IS_RUNNING(isp116x_to_hcd(isp116x)->state)) {
+               seq_printf(s, "HCD not running\n");
+               return 0;
+       }
+
+       spin_lock_irq(&isp116x->lock);
+
+       dump_irq(s, "hc_irq_enable", isp116x_read_reg16(isp116x, HCuPINTENB));
+       dump_irq(s, "hc_irq_status", isp116x_read_reg16(isp116x, HCuPINT));
+       dump_int(s, "hc_int_enable", isp116x_read_reg32(isp116x, HCINTENB));
+       dump_int(s, "hc_int_status", isp116x_read_reg32(isp116x, HCINTSTAT));
+
+       list_for_each_entry(ep, &isp116x->async, schedule) {
+
+               switch (ep->nextpid) {
+               case USB_PID_IN:
+                       str = "in";
+                       break;
+               case USB_PID_OUT:
+                       str = "out";
+                       break;
+               case USB_PID_SETUP:
+                       str = "setup";
+                       break;
+               case USB_PID_ACK:
+                       str = "status";
+                       break;
+               default:
+                       str = "?";
+                       break;
+               };
+               seq_printf(s, "%p, ep%d%s, maxpacket %d:\n", ep,
+                          ep->epnum, str, ep->maxpacket);
+               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(&isp116x->async))
+               seq_printf(s, "\n");
+
+       seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE);
+
+       for (i = 0; i < PERIODIC_SIZE; i++) {
+               ep = isp116x->periodic[i];
+               if (!ep)
+                       continue;
+               seq_printf(s, "%2d [%3d]:\n", i, isp116x->load[i]);
+
+               /* DUMB: prints shared entries multiple times */
+               do {
+                       seq_printf(s, "   %d/%p (%sdev%d ep%d%s max %d)\n",
+                                  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 = ep->next;
+               } while (ep);
+       }
+       spin_unlock_irq(&isp116x->lock);
+       seq_printf(s, "\n");
+
+       return 0;
+}
+
+static int proc_isp116x_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, proc_isp116x_show, PDE(inode)->data);
+}
+
+static struct file_operations proc_ops = {
+       .open = proc_isp116x_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+/* expect just one isp116x per system */
+static const char proc_filename[] = "driver/isp116x";
+
+static void create_debug_file(struct isp116x *isp116x)
+{
+       struct proc_dir_entry *pde;
+
+       pde = create_proc_entry(proc_filename, 0, NULL);
+       if (pde == NULL)
+               return;
+
+       pde->proc_fops = &proc_ops;
+       pde->data = isp116x;
+       isp116x->pde = pde;
+}
+
+static void remove_debug_file(struct isp116x *isp116x)
+{
+       if (isp116x->pde)
+               remove_proc_entry(proc_filename, NULL);
+}
+
+#endif
+
+/*-----------------------------------------------------------------*/
+
+/*
+  Software reset - can be called from any contect.
+*/
+static int isp116x_sw_reset(struct isp116x *isp116x)
+{
+       int retries = 15;
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&isp116x->lock, flags);
+       isp116x_write_reg16(isp116x, HCSWRES, HCSWRES_MAGIC);
+       isp116x_write_reg32(isp116x, HCCMDSTAT, HCCMDSTAT_HCR);
+       while (--retries) {
+               /* It usually resets within 1 ms */
+               mdelay(1);
+               if (!(isp116x_read_reg32(isp116x, HCCMDSTAT) & HCCMDSTAT_HCR))
+                       break;
+       }
+       if (!retries) {
+               ERR("Software reset timeout\n");
+               ret = -ETIME;
+       }
+       spin_unlock_irqrestore(&isp116x->lock, flags);
+       return ret;
+}
+
+/*
+  Reset. Tries to perform platform-specific hardware
+  reset first; falls back to software reset.
+*/
+static int isp116x_reset(struct usb_hcd *hcd)
+{
+       struct isp116x *isp116x = hcd_to_isp116x(hcd);
+       unsigned long t;
+       u16 clkrdy = 0;
+       int ret = 0, timeout = 15 /* ms */ ;
+
+       if (isp116x->board && isp116x->board->reset) {
+               /* Hardware reset */
+               isp116x->board->reset(hcd->self.controller, 1);
+               msleep(10);
+               if (isp116x->board->clock)
+                       isp116x->board->clock(hcd->self.controller, 1);
+               msleep(1);
+               isp116x->board->reset(hcd->self.controller, 0);
+       } else
+               ret = isp116x_sw_reset(isp116x);
+
+       if (ret)
+               return ret;
+
+       t = jiffies + msecs_to_jiffies(timeout);
+       while (time_before_eq(jiffies, t)) {
+               msleep(4);
+               spin_lock_irq(&isp116x->lock);
+               clkrdy = isp116x_read_reg16(isp116x, HCuPINT) & HCuPINT_CLKRDY;
+               spin_unlock_irq(&isp116x->lock);
+               if (clkrdy)
+                       break;
+       }
+       if (!clkrdy) {
+               ERR("Clock not ready after 20ms\n");
+               /* After sw_reset the clock won't report to be ready, if
+                  H_WAKEUP pin is high. */
+               if (!isp116x->board || !isp116x->board->reset)
+                       ERR("The driver does not support hardware wakeup.\n");
+                       ERR("Please make sure that the H_WAKEUP pin "
+                               "is pulled low!\n");
+               ret = -ENODEV;
+       }
+       return ret;
+}
+
+static void isp116x_stop(struct usb_hcd *hcd)
+{
+       struct isp116x *isp116x = hcd_to_isp116x(hcd);
+       unsigned long flags;
+       u32 val;
+
+       spin_lock_irqsave(&isp116x->lock, flags);
+       isp116x_write_reg16(isp116x, HCuPINTENB, 0);
+
+       /* Switch off ports' power, some devices don't come up
+          after next 'insmod' without this */
+       val = isp116x_read_reg32(isp116x, HCRHDESCA);
+       val &= ~(RH_A_NPS | RH_A_PSM);
+       isp116x_write_reg32(isp116x, HCRHDESCA, val);
+       isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPS);
+       spin_unlock_irqrestore(&isp116x->lock, flags);
+
+       /* Put the chip into reset state */
+       if (isp116x->board && isp116x->board->reset)
+               isp116x->board->reset(hcd->self.controller, 0);
+       else
+               isp116x_sw_reset(isp116x);
+
+       /* Stop the clock */
+       if (isp116x->board && isp116x->board->clock)
+               isp116x->board->clock(hcd->self.controller, 0);
+}
+
+/*
+  Configure the chip. The chip must be successfully reset by now.
+*/
+static int isp116x_start(struct usb_hcd *hcd)
+{
+       struct isp116x *isp116x = hcd_to_isp116x(hcd);
+       struct isp116x_platform_data *board = isp116x->board;
+       u32 val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&isp116x->lock, flags);
+
+       /* clear interrupt status and disable all interrupt sources */
+       isp116x_write_reg16(isp116x, HCuPINT, 0xff);
+       isp116x_write_reg16(isp116x, HCuPINTENB, 0);
+
+       val = isp116x_read_reg16(isp116x, HCCHIPID);
+       if ((val & HCCHIPID_MASK) != HCCHIPID_MAGIC) {
+               ERR("Invalid chip ID %04x\n", val);
+               spin_unlock_irqrestore(&isp116x->lock, flags);
+               return -ENODEV;
+       }
+
+       isp116x_write_reg16(isp116x, HCITLBUFLEN, ISP116x_ITL_BUFSIZE);
+       isp116x_write_reg16(isp116x, HCATLBUFLEN, ISP116x_ATL_BUFSIZE);
+
+       /* ----- HW conf */
+       val = HCHWCFG_INT_ENABLE | HCHWCFG_DBWIDTH(1);
+       if (board->sel15Kres)
+               val |= HCHWCFG_15KRSEL;
+       /* Remote wakeup won't work without working clock */
+       if (board->clknotstop || board->remote_wakeup_enable)
+               val |= HCHWCFG_CLKNOTSTOP;
+       if (board->oc_enable)
+               val |= HCHWCFG_ANALOG_OC;
+       if (board->int_act_high)
+               val |= HCHWCFG_INT_POL;
+       if (board->int_edge_triggered)
+               val |= HCHWCFG_INT_TRIGGER;
+       isp116x_write_reg16(isp116x, HCHWCFG, val);
+
+       /* ----- Root hub conf */
+       val = 0;
+       /* AN10003_1.pdf recommends NPS to be always 1 */
+       if (board->no_power_switching)
+               val |= RH_A_NPS;
+       if (board->power_switching_mode)
+               val |= RH_A_PSM;
+       if (board->potpg)
+               val |= (board->potpg << 24) & RH_A_POTPGT;
+       else
+               val |= (25 << 24) & RH_A_POTPGT;
+       isp116x_write_reg32(isp116x, HCRHDESCA, val);
+       isp116x->rhdesca = isp116x_read_reg32(isp116x, HCRHDESCA);
+
+       val = RH_B_PPCM;
+       isp116x_write_reg32(isp116x, HCRHDESCB, val);
+       isp116x->rhdescb = isp116x_read_reg32(isp116x, HCRHDESCB);
+
+       val = 0;
+       if (board->remote_wakeup_enable) {
+               hcd->can_wakeup = 1;
+               val |= RH_HS_DRWE;
+       }
+       isp116x_write_reg32(isp116x, HCRHSTATUS, val);
+       isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS);
+
+       isp116x_write_reg32(isp116x, HCFMINTVL, 0x27782edf);
+
+       hcd->state = HC_STATE_RUNNING;
+
+       /* Set up interrupts */
+       isp116x->intenb = HCINT_MIE | HCINT_RHSC | HCINT_UE;
+       if (board->remote_wakeup_enable)
+               isp116x->intenb |= HCINT_RD;
+       isp116x->irqenb = HCuPINT_ATL | HCuPINT_OPR;    /* | HCuPINT_SUSP; */
+       isp116x_write_reg32(isp116x, HCINTENB, isp116x->intenb);
+       isp116x_write_reg16(isp116x, HCuPINTENB, isp116x->irqenb);
+
+       /* Go operational */
+       val = HCCONTROL_USB_OPER;
+       /* Remote wakeup connected - NOT SUPPORTED */
+       /*  if (board->remote_wakeup_connected)
+          val |= HCCONTROL_RWC;  */
+       if (board->remote_wakeup_enable)
+               val |= HCCONTROL_RWE;
+       isp116x_write_reg32(isp116x, HCCONTROL, val);
+
+       /* Disable ports to avoid race in device enumeration */
+       isp116x_write_reg32(isp116x, HCRHPORT1, RH_PS_CCS);
+       isp116x_write_reg32(isp116x, HCRHPORT2, RH_PS_CCS);
+
+       isp116x_show_regs(isp116x);
+       spin_unlock_irqrestore(&isp116x->lock, flags);
+       return 0;
+}
+
+/*-----------------------------------------------------------------*/
+
+static struct hc_driver isp116x_hc_driver = {
+       .description = hcd_name,
+       .product_desc = "ISP116x Host Controller",
+       .hcd_priv_size = sizeof(struct isp116x),
+
+       .irq = isp116x_irq,
+       .flags = HCD_USB11,
+
+       .reset = isp116x_reset,
+       .start = isp116x_start,
+       .stop = isp116x_stop,
+
+       .urb_enqueue = isp116x_urb_enqueue,
+       .urb_dequeue = isp116x_urb_dequeue,
+       .endpoint_disable = isp116x_endpoint_disable,
+
+       .get_frame_number = isp116x_get_frame,
+
+       .hub_status_data = isp116x_hub_status_data,
+       .hub_control = isp116x_hub_control,
+       .hub_suspend = isp116x_hub_suspend,
+       .hub_resume = isp116x_hub_resume,
+};
+
+/*----------------------------------------------------------------*/
+
+static int __init_or_module isp116x_remove(struct device *dev)
+{
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+       struct isp116x *isp116x;
+       struct platform_device *pdev;
+       struct resource *res;
+
+       if(!hcd)
+               return 0;
+       isp116x = hcd_to_isp116x(hcd);
+       pdev = container_of(dev, struct platform_device, dev);
+       remove_debug_file(isp116x);
+       usb_remove_hcd(hcd);
+
+       iounmap(isp116x->data_reg);
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       release_mem_region(res->start, 2);
+       iounmap(isp116x->addr_reg);
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(res->start, 2);
+
+       usb_put_hcd(hcd);
+       return 0;
+}
+
+#define resource_len(r) (((r)->end - (r)->start) + 1)
+
+static int __init isp116x_probe(struct device *dev)
+{
+       struct usb_hcd *hcd;
+       struct isp116x *isp116x;
+       struct platform_device *pdev;
+       struct resource *addr, *data;
+       void __iomem *addr_reg;
+       void __iomem *data_reg;
+       int irq;
+       int ret = 0;
+
+       pdev = container_of(dev, struct platform_device, dev);
+       if (pdev->num_resources < 3) {
+               ret = -ENODEV;
+               goto err1;
+       }
+
+       data = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       addr = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       irq = platform_get_irq(pdev, 0);
+       if (!addr || !data || irq < 0) {
+               ret = -ENODEV;
+               goto err1;
+       }
+
+       if (dev->dma_mask) {
+               DBG("DMA not supported\n");
+               ret = -EINVAL;
+               goto err1;
+       }
+
+       if (!request_mem_region(addr->start, 2, hcd_name)) {
+               ret = -EBUSY;
+               goto err1;
+       }
+       addr_reg = ioremap(addr->start, resource_len(addr));
+       if (addr_reg == NULL) {
+               ret = -ENOMEM;
+               goto err2;
+       }
+       if (!request_mem_region(data->start, 2, hcd_name)) {
+               ret = -EBUSY;
+               goto err3;
+       }
+       data_reg = ioremap(data->start, resource_len(data));
+       if (data_reg == NULL) {
+               ret = -ENOMEM;
+               goto err4;
+       }
+
+       /* allocate and initialize hcd */
+       hcd = usb_create_hcd(&isp116x_hc_driver, dev, dev->bus_id);
+       if (!hcd) {
+               ret = -ENOMEM;
+               goto err5;
+       }
+       /* this rsrc_start is bogus */
+       hcd->rsrc_start = addr->start;
+       isp116x = hcd_to_isp116x(hcd);
+       isp116x->data_reg = data_reg;
+       isp116x->addr_reg = addr_reg;
+       spin_lock_init(&isp116x->lock);
+       INIT_LIST_HEAD(&isp116x->async);
+       INIT_WORK(&isp116x->rh_resume, isp116x_rh_resume, hcd);
+       isp116x->board = dev->platform_data;
+
+       if (!isp116x->board) {
+               ERR("Platform data structure not initialized\n");
+               ret = -ENODEV;
+               goto err6;
+       }
+       if (isp116x_check_platform_delay(isp116x)) {
+               ERR("USE_PLATFORM_DELAY defined, but delay function not "
+                   "implemented.\n");
+               ERR("See comments in drivers/usb/host/isp116x-hcd.c\n");
+               ret = -ENODEV;
+               goto err6;
+       }
+
+       ret = usb_add_hcd(hcd, irq, SA_INTERRUPT);
+       if (ret != 0)
+               goto err6;
+
+       create_debug_file(isp116x);
+       return 0;
+
+      err6:
+       usb_put_hcd(hcd);
+      err5:
+       iounmap(data_reg);
+      err4:
+       release_mem_region(data->start, 2);
+      err3:
+       iounmap(addr_reg);
+      err2:
+       release_mem_region(addr->start, 2);
+      err1:
+       ERR("init error, %d\n", ret);
+       return ret;
+}
+
+#ifdef CONFIG_PM
+/*
+  Suspend of platform device
+*/
+static int isp116x_suspend(struct device *dev, pm_message_t state, u32 phase)
+{
+       int ret = 0;
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+       VDBG("%s: state %x, phase %x\n", __func__, state, phase);
+
+       if (phase != SUSPEND_DISABLE && phase != SUSPEND_POWER_DOWN)
+               return 0;
+
+       ret = usb_suspend_device(hcd->self.root_hub, state);
+       if (!ret) {
+               dev->power.power_state = state;
+               INFO("%s suspended\n", (char *)hcd_name);
+       } else
+               ERR("%s suspend failed\n", (char *)hcd_name);
+
+       return ret;
+}
+
+/*
+  Resume platform device
+*/
+static int isp116x_resume(struct device *dev, u32 phase)
+{
+       int ret = 0;
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+       VDBG("%s:  state %x, phase %x\n", __func__, dev->power.power_state,
+            phase);
+       if (phase != RESUME_POWER_ON)
+               return 0;
+
+       ret = usb_resume_device(hcd->self.root_hub);
+       if (!ret) {
+               dev->power.power_state = PMSG_ON;
+               VDBG("%s resumed\n", (char *)hcd_name);
+       }
+       return ret;
+}
+
+#else
+
+#define        isp116x_suspend    NULL
+#define        isp116x_resume     NULL
+
+#endif
+
+static struct device_driver isp116x_driver = {
+       .name = (char *)hcd_name,
+       .bus = &platform_bus_type,
+       .probe = isp116x_probe,
+       .remove = isp116x_remove,
+       .suspend = isp116x_suspend,
+       .resume = isp116x_resume,
+};
+
+/*-----------------------------------------------------------------*/
+
+static int __init isp116x_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       INFO("driver %s, %s\n", hcd_name, DRIVER_VERSION);
+       return driver_register(&isp116x_driver);
+}
+
+module_init(isp116x_init);
+
+static void __exit isp116x_cleanup(void)
+{
+       driver_unregister(&isp116x_driver);
+}
+
+module_exit(isp116x_cleanup);
diff --git a/drivers/usb/host/isp116x.h b/drivers/usb/host/isp116x.h
new file mode 100644 (file)
index 0000000..5887347
--- /dev/null
@@ -0,0 +1,583 @@
+/*
+ * ISP116x register declarations and HCD data structures
+ *
+ * Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee>
+ * Portions:
+ * Copyright (C) 2004 Lothar Wassmann
+ * Copyright (C) 2004 Psion Teklogix
+ * Copyright (C) 2004 David Brownell
+ */
+
+/* us of 1ms frame */
+#define  MAX_LOAD_LIMIT                850
+
+/* Full speed: max # of bytes to transfer for a single urb
+   at a time must be < 1024 && must be multiple of 64.
+   832 allows transfering 4kiB within 5 frames. */
+#define MAX_TRANSFER_SIZE_FULLSPEED    832
+
+/* Low speed: there is no reason to schedule in very big
+   chunks; often the requested long transfers are for
+   string descriptors containing short strings. */
+#define MAX_TRANSFER_SIZE_LOWSPEED     64
+
+/* Bytetime (us), a rough indication of how much time it
+   would take to transfer a byte of useful data over USB */
+#define BYTE_TIME_FULLSPEED    1
+#define BYTE_TIME_LOWSPEED     20
+
+/* Buffer sizes */
+#define ISP116x_BUF_SIZE       4096
+#define ISP116x_ITL_BUFSIZE    0
+#define ISP116x_ATL_BUFSIZE    ((ISP116x_BUF_SIZE) - 2*(ISP116x_ITL_BUFSIZE))
+
+#define ISP116x_WRITE_OFFSET   0x80
+
+/*------------ ISP116x registers/bits ------------*/
+#define        HCREVISION      0x00
+#define        HCCONTROL       0x01
+#define                HCCONTROL_HCFS  (3 << 6)        /* host controller
+                                                  functional state */
+#define                HCCONTROL_USB_RESET     (0 << 6)
+#define                HCCONTROL_USB_RESUME    (1 << 6)
+#define                HCCONTROL_USB_OPER      (2 << 6)
+#define                HCCONTROL_USB_SUSPEND   (3 << 6)
+#define                HCCONTROL_RWC   (1 << 9)        /* remote wakeup connected */
+#define                HCCONTROL_RWE   (1 << 10)       /* remote wakeup enable */
+#define        HCCMDSTAT       0x02
+#define                HCCMDSTAT_HCR   (1 << 0)        /* host controller reset */
+#define                HCCMDSTAT_SOC   (3 << 16)       /* scheduling overrun count */
+#define        HCINTSTAT       0x03
+#define                HCINT_SO        (1 << 0)        /* scheduling overrun */
+#define                HCINT_WDH       (1 << 1)        /* writeback of done_head */
+#define                HCINT_SF        (1 << 2)        /* start frame */
+#define                HCINT_RD        (1 << 3)        /* resume detect */
+#define                HCINT_UE        (1 << 4)        /* unrecoverable error */
+#define                HCINT_FNO       (1 << 5)        /* frame number overflow */
+#define                HCINT_RHSC      (1 << 6)        /* root hub status change */
+#define                HCINT_OC        (1 << 30)       /* ownership change */
+#define                HCINT_MIE       (1 << 31)       /* master interrupt enable */
+#define        HCINTENB        0x04
+#define        HCINTDIS        0x05
+#define        HCFMINTVL       0x0d
+#define        HCFMREM         0x0e
+#define        HCFMNUM         0x0f
+#define        HCLSTHRESH      0x11
+#define        HCRHDESCA       0x12
+#define                RH_A_NDP        (0x3 << 0)      /* # downstream ports */
+#define                RH_A_PSM        (1 << 8)        /* power switching mode */
+#define                RH_A_NPS        (1 << 9)        /* no power switching */
+#define                RH_A_DT         (1 << 10)       /* device type (mbz) */
+#define                RH_A_OCPM       (1 << 11)       /* overcurrent protection
+                                                  mode */
+#define                RH_A_NOCP       (1 << 12)       /* no overcurrent protection */
+#define                RH_A_POTPGT     (0xff << 24)    /* power on -> power good
+                                                  time */
+#define        HCRHDESCB       0x13
+#define                RH_B_DR         (0xffff << 0)   /* device removable flags */
+#define                RH_B_PPCM       (0xffff << 16)  /* port power control mask */
+#define        HCRHSTATUS      0x14
+#define                RH_HS_LPS       (1 << 0)        /* local power status */
+#define                RH_HS_OCI       (1 << 1)        /* over current indicator */
+#define                RH_HS_DRWE      (1 << 15)       /* device remote wakeup
+                                                  enable */
+#define                RH_HS_LPSC      (1 << 16)       /* local power status change */
+#define                RH_HS_OCIC      (1 << 17)       /* over current indicator
+                                                  change */
+#define                RH_HS_CRWE      (1 << 31)       /* clear remote wakeup
+                                                  enable */
+#define        HCRHPORT1       0x15
+#define                RH_PS_CCS       (1 << 0)        /* current connect status */
+#define                RH_PS_PES       (1 << 1)        /* port enable status */
+#define                RH_PS_PSS       (1 << 2)        /* port suspend status */
+#define                RH_PS_POCI      (1 << 3)        /* port over current
+                                                  indicator */
+#define                RH_PS_PRS       (1 << 4)        /* port reset status */
+#define                RH_PS_PPS       (1 << 8)        /* port power status */
+#define                RH_PS_LSDA      (1 << 9)        /* low speed device attached */
+#define                RH_PS_CSC       (1 << 16)       /* connect status change */
+#define                RH_PS_PESC      (1 << 17)       /* port enable status change */
+#define                RH_PS_PSSC      (1 << 18)       /* port suspend status
+                                                  change */
+#define                RH_PS_OCIC      (1 << 19)       /* over current indicator
+                                                  change */
+#define                RH_PS_PRSC      (1 << 20)       /* port reset status change */
+#define                HCRHPORT_CLRMASK        (0x1f << 16)
+#define        HCRHPORT2       0x16
+#define        HCHWCFG         0x20
+#define                HCHWCFG_15KRSEL         (1 << 12)
+#define                HCHWCFG_CLKNOTSTOP      (1 << 11)
+#define                HCHWCFG_ANALOG_OC       (1 << 10)
+#define                HCHWCFG_DACK_MODE       (1 << 8)
+#define                HCHWCFG_EOT_POL         (1 << 7)
+#define                HCHWCFG_DACK_POL        (1 << 6)
+#define                HCHWCFG_DREQ_POL        (1 << 5)
+#define                HCHWCFG_DBWIDTH_MASK    (0x03 << 3)
+#define                HCHWCFG_DBWIDTH(n)      (((n) << 3) & HCHWCFG_DBWIDTH_MASK)
+#define                HCHWCFG_INT_POL         (1 << 2)
+#define                HCHWCFG_INT_TRIGGER     (1 << 1)
+#define                HCHWCFG_INT_ENABLE      (1 << 0)
+#define        HCDMACFG        0x21
+#define                HCDMACFG_BURST_LEN_MASK (0x03 << 5)
+#define                HCDMACFG_BURST_LEN(n)   (((n) << 5) & HCDMACFG_BURST_LEN_MASK)
+#define                HCDMACFG_BURST_LEN_1    HCDMACFG_BURST_LEN(0)
+#define                HCDMACFG_BURST_LEN_4    HCDMACFG_BURST_LEN(1)
+#define                HCDMACFG_BURST_LEN_8    HCDMACFG_BURST_LEN(2)
+#define                HCDMACFG_DMA_ENABLE     (1 << 4)
+#define                HCDMACFG_BUF_TYPE_MASK  (0x07 << 1)
+#define                HCDMACFG_CTR_SEL        (1 << 2)
+#define                HCDMACFG_ITLATL_SEL     (1 << 1)
+#define                HCDMACFG_DMA_RW_SELECT  (1 << 0)
+#define        HCXFERCTR       0x22
+#define        HCuPINT         0x24
+#define                HCuPINT_SOF             (1 << 0)
+#define                HCuPINT_ATL             (1 << 1)
+#define                HCuPINT_AIIEOT          (1 << 2)
+#define                HCuPINT_OPR             (1 << 4)
+#define                HCuPINT_SUSP            (1 << 5)
+#define                HCuPINT_CLKRDY          (1 << 6)
+#define        HCuPINTENB      0x25
+#define        HCCHIPID        0x27
+#define                HCCHIPID_MASK           0xff00
+#define                HCCHIPID_MAGIC          0x6100
+#define        HCSCRATCH       0x28
+#define        HCSWRES         0x29
+#define                HCSWRES_MAGIC           0x00f6
+#define        HCITLBUFLEN     0x2a
+#define        HCATLBUFLEN     0x2b
+#define        HCBUFSTAT       0x2c
+#define                HCBUFSTAT_ITL0_FULL     (1 << 0)
+#define                HCBUFSTAT_ITL1_FULL     (1 << 1)
+#define                HCBUFSTAT_ATL_FULL      (1 << 2)
+#define                HCBUFSTAT_ITL0_DONE     (1 << 3)
+#define                HCBUFSTAT_ITL1_DONE     (1 << 4)
+#define                HCBUFSTAT_ATL_DONE      (1 << 5)
+#define        HCRDITL0LEN     0x2d
+#define        HCRDITL1LEN     0x2e
+#define        HCITLPORT       0x40
+#define        HCATLPORT       0x41
+
+/* Philips transfer descriptor */
+struct ptd {
+       u16 count;
+#define        PTD_COUNT_MSK   (0x3ff << 0)
+#define        PTD_TOGGLE_MSK  (1 << 10)
+#define        PTD_ACTIVE_MSK  (1 << 11)
+#define        PTD_CC_MSK      (0xf << 12)
+       u16 mps;
+#define        PTD_MPS_MSK     (0x3ff << 0)
+#define        PTD_SPD_MSK     (1 << 10)
+#define        PTD_LAST_MSK    (1 << 11)
+#define        PTD_EP_MSK      (0xf << 12)
+       u16 len;
+#define        PTD_LEN_MSK     (0x3ff << 0)
+#define        PTD_DIR_MSK     (3 << 10)
+#define        PTD_DIR_SETUP   (0)
+#define        PTD_DIR_OUT     (1)
+#define        PTD_DIR_IN      (2)
+#define        PTD_B5_5_MSK    (1 << 13)
+       u16 faddr;
+#define        PTD_FA_MSK      (0x7f << 0)
+#define        PTD_FMT_MSK     (1 << 7)
+} __attribute__ ((packed, aligned(2)));
+
+/* PTD accessor macros. */
+#define PTD_GET_COUNT(p)       (((p)->count & PTD_COUNT_MSK) >> 0)
+#define PTD_COUNT(v)           (((v) << 0) & PTD_COUNT_MSK)
+#define PTD_GET_TOGGLE(p)      (((p)->count & PTD_TOGGLE_MSK) >> 10)
+#define PTD_TOGGLE(v)          (((v) << 10) & PTD_TOGGLE_MSK)
+#define PTD_GET_ACTIVE(p)      (((p)->count & PTD_ACTIVE_MSK) >> 11)
+#define PTD_ACTIVE(v)          (((v) << 11) & PTD_ACTIVE_MSK)
+#define PTD_GET_CC(p)          (((p)->count & PTD_CC_MSK) >> 12)
+#define PTD_CC(v)              (((v) << 12) & PTD_CC_MSK)
+#define PTD_GET_MPS(p)         (((p)->mps & PTD_MPS_MSK) >> 0)
+#define PTD_MPS(v)             (((v) << 0) & PTD_MPS_MSK)
+#define PTD_GET_SPD(p)         (((p)->mps & PTD_SPD_MSK) >> 10)
+#define PTD_SPD(v)             (((v) << 10) & PTD_SPD_MSK)
+#define PTD_GET_LAST(p)                (((p)->mps & PTD_LAST_MSK) >> 11)
+#define PTD_LAST(v)            (((v) << 11) & PTD_LAST_MSK)
+#define PTD_GET_EP(p)          (((p)->mps & PTD_EP_MSK) >> 12)
+#define PTD_EP(v)              (((v) << 12) & PTD_EP_MSK)
+#define PTD_GET_LEN(p)         (((p)->len & PTD_LEN_MSK) >> 0)
+#define PTD_LEN(v)             (((v) << 0) & PTD_LEN_MSK)
+#define PTD_GET_DIR(p)         (((p)->len & PTD_DIR_MSK) >> 10)
+#define PTD_DIR(v)             (((v) << 10) & PTD_DIR_MSK)
+#define PTD_GET_B5_5(p)                (((p)->len & PTD_B5_5_MSK) >> 13)
+#define PTD_B5_5(v)            (((v) << 13) & PTD_B5_5_MSK)
+#define PTD_GET_FA(p)          (((p)->faddr & PTD_FA_MSK) >> 0)
+#define PTD_FA(v)              (((v) << 0) & PTD_FA_MSK)
+#define PTD_GET_FMT(p)         (((p)->faddr & PTD_FMT_MSK) >> 7)
+#define PTD_FMT(v)             (((v) << 7) & PTD_FMT_MSK)
+
+/*  Hardware transfer status codes -- CC from ptd->count */
+#define TD_CC_NOERROR      0x00
+#define TD_CC_CRC          0x01
+#define TD_CC_BITSTUFFING  0x02
+#define TD_CC_DATATOGGLEM  0x03
+#define TD_CC_STALL        0x04
+#define TD_DEVNOTRESP      0x05
+#define TD_PIDCHECKFAIL    0x06
+#define TD_UNEXPECTEDPID   0x07
+#define TD_DATAOVERRUN     0x08
+#define TD_DATAUNDERRUN    0x09
+    /* 0x0A, 0x0B reserved for hardware */
+#define TD_BUFFEROVERRUN   0x0C
+#define TD_BUFFERUNDERRUN  0x0D
+    /* 0x0E, 0x0F reserved for HCD */
+#define TD_NOTACCESSED     0x0F
+
+/* map PTD status codes (CC) to errno values */
+static const int cc_to_error[16] = {
+       /* No  Error  */ 0,
+       /* CRC Error  */ -EILSEQ,
+       /* Bit Stuff  */ -EPROTO,
+       /* Data Togg  */ -EILSEQ,
+       /* Stall      */ -EPIPE,
+       /* DevNotResp */ -ETIMEDOUT,
+       /* PIDCheck   */ -EPROTO,
+       /* UnExpPID   */ -EPROTO,
+       /* DataOver   */ -EOVERFLOW,
+       /* DataUnder  */ -EREMOTEIO,
+       /* (for hw)   */ -EIO,
+       /* (for hw)   */ -EIO,
+       /* BufferOver */ -ECOMM,
+       /* BuffUnder  */ -ENOSR,
+       /* (for HCD)  */ -EALREADY,
+       /* (for HCD)  */ -EALREADY
+};
+
+/*--------------------------------------------------------------*/
+
+#define        LOG2_PERIODIC_SIZE      5       /* arbitrary; this matches OHCI */
+#define        PERIODIC_SIZE           (1 << LOG2_PERIODIC_SIZE)
+
+struct isp116x {
+       spinlock_t lock;
+       struct work_struct rh_resume;
+
+       void __iomem *addr_reg;
+       void __iomem *data_reg;
+
+       struct isp116x_platform_data *board;
+
+       struct proc_dir_entry *pde;
+       unsigned long stat1, stat2, stat4, stat8, stat16;
+
+       /* HC registers */
+       u32 intenb;             /* "OHCI" interrupts */
+       u16 irqenb;             /* uP interrupts */
+
+       /* Root hub registers */
+       u32 rhdesca;
+       u32 rhdescb;
+       u32 rhstatus;
+       u32 rhport[2];
+
+       /* async schedule: control, bulk */
+       struct list_head async;
+
+       /* periodic schedule: int */
+       u16 load[PERIODIC_SIZE];
+       struct isp116x_ep *periodic[PERIODIC_SIZE];
+       unsigned periodic_count;
+       u16 fmindex;
+
+       /* Schedule for the current frame */
+       struct isp116x_ep *atl_active;
+       int atl_buflen;
+       int atl_bufshrt;
+       int atl_last_dir;
+       atomic_t atl_finishing;
+};
+
+static inline struct isp116x *hcd_to_isp116x(struct usb_hcd *hcd)
+{
+       return (struct isp116x *)(hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *isp116x_to_hcd(struct isp116x *isp116x)
+{
+       return container_of((void *)isp116x, struct usb_hcd, hcd_priv);
+}
+
+struct isp116x_ep {
+       struct usb_host_endpoint *hep;
+       struct usb_device *udev;
+       struct ptd ptd;
+
+       u8 maxpacket;
+       u8 epnum;
+       u8 nextpid;
+       u16 error_count;
+       u16 length;             /* of current packet */
+       unsigned char *data;    /* to databuf */
+       /* queue of active EP's (the ones scheduled for the
+          current frame) */
+       struct isp116x_ep *active;
+
+       /* periodic schedule */
+       u16 period;
+       u16 branch;
+       u16 load;
+       struct isp116x_ep *next;
+
+       /* async schedule */
+       struct list_head schedule;
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+#define DBG(stuff...)          printk(KERN_DEBUG "116x: " stuff)
+#else
+#define DBG(stuff...)          do{}while(0)
+#endif
+
+#ifdef VERBOSE
+#    define VDBG               DBG
+#else
+#    define VDBG(stuff...)     do{}while(0)
+#endif
+
+#define ERR(stuff...)          printk(KERN_ERR "116x: " stuff)
+#define WARN(stuff...)         printk(KERN_WARNING "116x: " stuff)
+#define INFO(stuff...)         printk(KERN_INFO "116x: " stuff)
+
+/* ------------------------------------------------- */
+
+#if defined(USE_PLATFORM_DELAY)
+#if defined(USE_NDELAY)
+#error USE_PLATFORM_DELAY and USE_NDELAY simultaneously defined.
+#endif
+#define        isp116x_delay(h,d)      (h)->board->delay(      \
+                               isp116x_to_hcd(h)->self.controller,d)
+#define isp116x_check_platform_delay(h)        ((h)->board->delay == NULL)
+#elif defined(USE_NDELAY)
+#define        isp116x_delay(h,d)      ndelay(d)
+#define isp116x_check_platform_delay(h)        0
+#else
+#define        isp116x_delay(h,d)      do{}while(0)
+#define isp116x_check_platform_delay(h)        0
+#endif
+
+#if defined(DEBUG)
+#define        IRQ_TEST()      BUG_ON(!irqs_disabled())
+#else
+#define        IRQ_TEST()      do{}while(0)
+#endif
+
+static inline void isp116x_write_addr(struct isp116x *isp116x, unsigned reg)
+{
+       IRQ_TEST();
+       writew(reg & 0xff, isp116x->addr_reg);
+       isp116x_delay(isp116x, 300);
+}
+
+static inline void isp116x_write_data16(struct isp116x *isp116x, u16 val)
+{
+       writew(val, isp116x->data_reg);
+       isp116x_delay(isp116x, 150);
+}
+
+static inline void isp116x_raw_write_data16(struct isp116x *isp116x, u16 val)
+{
+       __raw_writew(val, isp116x->data_reg);
+       isp116x_delay(isp116x, 150);
+}
+
+static inline u16 isp116x_read_data16(struct isp116x *isp116x)
+{
+       u16 val;
+
+       val = readw(isp116x->data_reg);
+       isp116x_delay(isp116x, 150);
+       return val;
+}
+
+static inline u16 isp116x_raw_read_data16(struct isp116x *isp116x)
+{
+       u16 val;
+
+       val = __raw_readw(isp116x->data_reg);
+       isp116x_delay(isp116x, 150);
+       return val;
+}
+
+static inline void isp116x_write_data32(struct isp116x *isp116x, u32 val)
+{
+       writew(val & 0xffff, isp116x->data_reg);
+       isp116x_delay(isp116x, 150);
+       writew(val >> 16, isp116x->data_reg);
+       isp116x_delay(isp116x, 150);
+}
+
+static inline u32 isp116x_read_data32(struct isp116x *isp116x)
+{
+       u32 val;
+
+       val = (u32) readw(isp116x->data_reg);
+       isp116x_delay(isp116x, 150);
+       val |= ((u32) readw(isp116x->data_reg)) << 16;
+       isp116x_delay(isp116x, 150);
+       return val;
+}
+
+/* Let's keep register access functions out of line. Hint:
+   we wait at least 150 ns at every access.
+*/
+static u16 isp116x_read_reg16(struct isp116x *isp116x, unsigned reg)
+{
+       isp116x_write_addr(isp116x, reg);
+       return isp116x_read_data16(isp116x);
+}
+
+static u32 isp116x_read_reg32(struct isp116x *isp116x, unsigned reg)
+{
+       isp116x_write_addr(isp116x, reg);
+       return isp116x_read_data32(isp116x);
+}
+
+static void isp116x_write_reg16(struct isp116x *isp116x, unsigned reg,
+                               unsigned val)
+{
+       isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET);
+       isp116x_write_data16(isp116x, (u16) (val & 0xffff));
+}
+
+static void isp116x_write_reg32(struct isp116x *isp116x, unsigned reg,
+                               unsigned val)
+{
+       isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET);
+       isp116x_write_data32(isp116x, (u32) val);
+}
+
+#define isp116x_show_reg(d,r) {                                        \
+       if ((r) < 0x20) {                                       \
+               DBG("%-12s[%02x]: %08x\n", #r,                  \
+                       r, isp116x_read_reg32(d, r));           \
+       } else {                                                \
+               DBG("%-12s[%02x]:     %04x\n", #r,              \
+                       r, isp116x_read_reg16(d, r));           \
+       }                                                       \
+}
+
+static inline void isp116x_show_regs(struct isp116x *isp116x)
+{
+       isp116x_show_reg(isp116x, HCREVISION);
+       isp116x_show_reg(isp116x, HCCONTROL);
+       isp116x_show_reg(isp116x, HCCMDSTAT);
+       isp116x_show_reg(isp116x, HCINTSTAT);
+       isp116x_show_reg(isp116x, HCINTENB);
+       isp116x_show_reg(isp116x, HCFMINTVL);
+       isp116x_show_reg(isp116x, HCFMREM);
+       isp116x_show_reg(isp116x, HCFMNUM);
+       isp116x_show_reg(isp116x, HCLSTHRESH);
+       isp116x_show_reg(isp116x, HCRHDESCA);
+       isp116x_show_reg(isp116x, HCRHDESCB);
+       isp116x_show_reg(isp116x, HCRHSTATUS);
+       isp116x_show_reg(isp116x, HCRHPORT1);
+       isp116x_show_reg(isp116x, HCRHPORT2);
+       isp116x_show_reg(isp116x, HCHWCFG);
+       isp116x_show_reg(isp116x, HCDMACFG);
+       isp116x_show_reg(isp116x, HCXFERCTR);
+       isp116x_show_reg(isp116x, HCuPINT);
+       isp116x_show_reg(isp116x, HCuPINTENB);
+       isp116x_show_reg(isp116x, HCCHIPID);
+       isp116x_show_reg(isp116x, HCSCRATCH);
+       isp116x_show_reg(isp116x, HCITLBUFLEN);
+       isp116x_show_reg(isp116x, HCATLBUFLEN);
+       isp116x_show_reg(isp116x, HCBUFSTAT);
+       isp116x_show_reg(isp116x, HCRDITL0LEN);
+       isp116x_show_reg(isp116x, HCRDITL1LEN);
+}
+
+#if defined(URB_TRACE)
+
+#define PIPETYPE(pipe)  ({ char *__s;                  \
+       if (usb_pipecontrol(pipe))      __s = "ctrl";   \
+       else if (usb_pipeint(pipe))     __s = "int";    \
+       else if (usb_pipebulk(pipe))    __s = "bulk";   \
+       else                            __s = "iso";    \
+       __s;})
+#define PIPEDIR(pipe)   ({ usb_pipein(pipe) ? "in" : "out"; })
+#define URB_NOTSHORT(urb) ({ (urb)->transfer_flags & URB_SHORT_NOT_OK ? \
+       "short_not_ok" : ""; })
+
+/* print debug info about the URB */
+static void urb_dbg(struct urb *urb, char *msg)
+{
+       unsigned int pipe;
+
+       if (!urb) {
+               DBG("%s: zero urb\n", msg);
+               return;
+       }
+       pipe = urb->pipe;
+       DBG("%s: FA %d ep%d%s %s: len %d/%d %s\n", msg,
+           usb_pipedevice(pipe), usb_pipeendpoint(pipe),
+           PIPEDIR(pipe), PIPETYPE(pipe),
+           urb->transfer_buffer_length, urb->actual_length, URB_NOTSHORT(urb));
+}
+
+#else
+
+#define  urb_dbg(urb,msg)   do{}while(0)
+
+#endif                         /* ! defined(URB_TRACE) */
+
+#if defined(PTD_TRACE)
+
+#define PTD_DIR_STR(ptd)  ({char __c;          \
+       switch(PTD_GET_DIR(ptd)){               \
+       case 0:  __c = 's'; break;              \
+       case 1:  __c = 'o'; break;              \
+       default: __c = 'i'; break;              \
+       }; __c;})
+
+/*
+  Dump PTD info. The code documents the format
+  perfectly, right :)
+*/
+static inline void dump_ptd(struct ptd *ptd)
+{
+       printk("td: %x %d%c%d %d,%d,%d  %x %x%x%x\n",
+              PTD_GET_CC(ptd), PTD_GET_FA(ptd),
+              PTD_DIR_STR(ptd), PTD_GET_EP(ptd),
+              PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd),
+              PTD_GET_TOGGLE(ptd), PTD_GET_ACTIVE(ptd),
+              PTD_GET_SPD(ptd), PTD_GET_LAST(ptd));
+}
+
+static inline void dump_ptd_out_data(struct ptd *ptd, u8 * buf)
+{
+       int k;
+
+       if (PTD_GET_DIR(ptd) != PTD_DIR_IN && PTD_GET_LEN(ptd)) {
+               printk("-> ");
+               for (k = 0; k < PTD_GET_LEN(ptd); ++k)
+                       printk("%02x ", ((u8 *) buf)[k]);
+               printk("\n");
+       }
+}
+
+static inline void dump_ptd_in_data(struct ptd *ptd, u8 * buf)
+{
+       int k;
+
+       if (PTD_GET_DIR(ptd) == PTD_DIR_IN && PTD_GET_COUNT(ptd)) {
+               printk("<- ");
+               for (k = 0; k < PTD_GET_COUNT(ptd); ++k)
+                       printk("%02x ", ((u8 *) buf)[k]);
+               printk("\n");
+       }
+       if (PTD_GET_LAST(ptd))
+               printk("-\n");
+}
+
+#else
+
+#define dump_ptd(ptd)               do{}while(0)
+#define dump_ptd_in_data(ptd,buf)   do{}while(0)
+#define dump_ptd_out_data(ptd,buf)  do{}while(0)
+
+#endif                         /* ! defined(PTD_TRACE) */
index 1e27f10..13cd217 100644 (file)
 #include <linux/init.h>
 #include <linux/timer.h>
 #include <linux/list.h>
-#include <linux/interrupt.h>  /* for in_interrupt () */
 #include <linux/usb.h>
 #include <linux/usb_otg.h>
-#include "../core/hcd.h"
 #include <linux/dma-mapping.h> 
-#include <linux/dmapool.h>    /* needed by ohci-mem.c when no PCI */
+#include <linux/dmapool.h>
+#include <linux/reboot.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/unaligned.h>
 #include <asm/byteorder.h>
 
+#include "../core/hcd.h"
 
-#define DRIVER_VERSION "2004 Nov 08"
+#define DRIVER_VERSION "2005 April 22"
 #define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
 #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
 
@@ -141,6 +141,7 @@ static const char   hcd_name [] = "ohci_hcd";
 static void ohci_dump (struct ohci_hcd *ohci, int verbose);
 static int ohci_init (struct ohci_hcd *ohci);
 static void ohci_stop (struct usb_hcd *hcd);
+static int ohci_reboot (struct notifier_block *, unsigned long , void *);
 
 #include "ohci-hub.c"
 #include "ohci-dbg.c"
@@ -420,6 +421,23 @@ static void ohci_usb_reset (struct ohci_hcd *ohci)
        ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
 }
 
+/* reboot notifier forcibly disables IRQs and DMA, helping kexec and
+ * other cases where the next software may expect clean state from the
+ * "firmware".  this is bus-neutral, unlike shutdown() methods.
+ */
+static int
+ohci_reboot (struct notifier_block *block, unsigned long code, void *null)
+{
+       struct ohci_hcd *ohci;
+
+       ohci = container_of (block, struct ohci_hcd, reboot_notifier);
+       ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
+       ohci_usb_reset (ohci);
+       /* flush the writes */
+       (void) ohci_readl (ohci, &ohci->regs->control);
+       return 0;
+}
+
 /*-------------------------------------------------------------------------*
  * HC functions
  *-------------------------------------------------------------------------*/
@@ -487,13 +505,10 @@ static int ohci_init (struct ohci_hcd *ohci)
 /* Start an OHCI controller, set the BUS operational
  * resets USB and controller
  * enable interrupts 
- * connect the virtual root hub
  */
 static int ohci_run (struct ohci_hcd *ohci)
 {
        u32                     mask, temp;
-       struct usb_device       *udev;
-       struct usb_bus          *bus;
        int                     first = ohci->fminterval == 0;
 
        disable (ohci);
@@ -654,37 +669,13 @@ retry:
 
        // POTPGT delay is bits 24-31, in 2 ms units.
        mdelay ((temp >> 23) & 0x1fe);
-       bus = &ohci_to_hcd(ohci)->self;
        ohci_to_hcd(ohci)->state = HC_STATE_RUNNING;
 
        ohci_dump (ohci, 1);
 
-       udev = bus->root_hub;
-       if (udev) {
-               return 0;
-       }
-       /* connect the virtual root hub */
-       udev = usb_alloc_dev (NULL, bus, 0);
-       if (!udev) {
-               disable (ohci);
-               ohci->hc_control &= ~OHCI_CTRL_HCFS;
-               ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
-               return -ENOMEM;
-       }
-
-       udev->speed = USB_SPEED_FULL;
-       if (usb_hcd_register_root_hub (udev, ohci_to_hcd(ohci)) != 0) {
-               usb_put_dev (udev);
-               disable (ohci);
-               ohci->hc_control &= ~OHCI_CTRL_HCFS;
-               ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
-               return -ENODEV;
-       }
-       if (ohci->power_budget)
-               hub_set_power_budget(udev, ohci->power_budget);
+       if (ohci_to_hcd(ohci)->self.root_hub == NULL)
+               create_debug_files (ohci);
 
-       create_debug_files (ohci);
        return 0;
 }
 
@@ -781,6 +772,7 @@ static void ohci_stop (struct usb_hcd *hcd)
        ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
        
        remove_debug_files (ohci);
+       unregister_reboot_notifier (&ohci->reboot_notifier);
        ohci_mem_cleanup (ohci);
        if (ohci->hcca) {
                dma_free_coherent (hcd->self.controller, 
index e55682b..23735a3 100644 (file)
@@ -29,6 +29,7 @@ static void ohci_hcd_init (struct ohci_hcd *ohci)
        spin_lock_init (&ohci->lock);
        INIT_LIST_HEAD (&ohci->pending);
        INIT_WORK (&ohci->rh_resume, ohci_rh_resume, ohci_to_hcd(ohci));
+       ohci->reboot_notifier.notifier_call = ohci_reboot;
 }
 
 /*-------------------------------------------------------------------------*/
index 8aab590..b62d699 100644 (file)
@@ -181,7 +181,7 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
        if (config->otg) {
                ohci_to_hcd(ohci)->self.otg_port = config->otg;
                /* default/minimum OTG power budget:  8 mA */
-               ohci->power_budget = 8;
+               ohci_to_hcd(ohci)->power_budget = 8;
        }
 
        /* boards can use OTG transceivers in non-OTG modes */
@@ -230,7 +230,7 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
 
                /* TPS2045 switch for internal transceiver (port 1) */
                if (machine_is_omap_osk()) {
-                       ohci->power_budget = 250;
+                       ohci_to_hcd(ohci)->power_budget = 250;
 
                        rh &= ~RH_A_NOCP;
 
index 22e1ac1..71cdd22 100644 (file)
@@ -371,7 +371,6 @@ struct ohci_hcd {
         * other external transceivers should be software-transparent 
         */
        struct otg_transceiver  *transceiver;
-       unsigned                power_budget;
 
        /*
         * memory management for queue data structures
@@ -390,6 +389,7 @@ struct ohci_hcd {
        u32                     fminterval;             /* saved register */
 
        struct work_struct      rh_resume;
+       struct notifier_block   reboot_notifier;
 
        unsigned long           flags;          /* for HC bugs */
 #define        OHCI_QUIRK_AMD756       0x01                    /* erratum #4 */
index 99d43f7..6c3f910 100644 (file)
@@ -1563,29 +1563,15 @@ static int
 sl811h_start(struct usb_hcd *hcd)
 {
        struct sl811            *sl811 = hcd_to_sl811(hcd);
-       struct usb_device       *udev;
 
        /* chip has been reset, VBUS power is off */
-
-       udev = usb_alloc_dev(NULL, &hcd->self, 0);
-       if (!udev)
-               return -ENOMEM;
-
-       udev->speed = USB_SPEED_FULL;
        hcd->state = HC_STATE_RUNNING;
 
-       if (sl811->board)
+       if (sl811->board) {
                hcd->can_wakeup = sl811->board->can_wakeup;
-
-       if (usb_hcd_register_root_hub(udev, hcd) != 0) {
-               usb_put_dev(udev);
-               sl811h_stop(hcd);
-               return -ENODEV;
+               hcd->power_budget = sl811->board->power * 2;
        }
 
-       if (sl811->board && sl811->board->power)
-               hub_set_power_budget(udev, sl811->board->power * 2);
-
        /* enable power and interupts */
        port_power(sl811, 1);
 
index 24c73c5..4538a98 100644 (file)
@@ -237,6 +237,37 @@ static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
        return out - buf;
 }
 
+static int uhci_show_root_hub_state(struct uhci_hcd *uhci, char *buf, int len)
+{
+       char *out = buf;
+       char *rh_state;
+
+       /* Try to make sure there's enough memory */
+       if (len < 60)
+               return 0;
+
+       switch (uhci->rh_state) {
+           case UHCI_RH_RESET:
+               rh_state = "reset";             break;
+           case UHCI_RH_SUSPENDED:
+               rh_state = "suspended";         break;
+           case UHCI_RH_AUTO_STOPPED:
+               rh_state = "auto-stopped";      break;
+           case UHCI_RH_RESUMING:
+               rh_state = "resuming";          break;
+           case UHCI_RH_SUSPENDING:
+               rh_state = "suspending";        break;
+           case UHCI_RH_RUNNING:
+               rh_state = "running";           break;
+           case UHCI_RH_RUNNING_NODEVS:
+               rh_state = "running, no devs";  break;
+           default:
+               rh_state = "?";                 break;
+       }
+       out += sprintf(out, "Root-hub state: %s\n", rh_state);
+       return out - buf;
+}
+
 static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len)
 {
        char *out = buf;
@@ -408,6 +439,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
 
        spin_lock_irqsave(&uhci->lock, flags);
 
+       out += uhci_show_root_hub_state(uhci, out, len - (out - buf));
        out += sprintf(out, "HC status\n");
        out += uhci_show_status(uhci, out, len - (out - buf));
 
index 49bd83e..0d5d254 100644 (file)
  * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
  *               support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
  * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
- * (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu
+ * (C) Copyright 2004-2005 Alan Stern, stern@rowland.harvard.edu
  *
  * Intel documents this fairly well, and as far as I know there
  * are no royalties or anything like that, but even so there are
  * people who decided that they want to do the same thing in a
  * completely different way.
  *
- * WARNING! The USB documentation is downright evil. Most of it
- * is just crap, written by a committee. You're better off ignoring
- * most of it, the important stuff is:
- *  - the low-level protocol (fairly simple but lots of small details)
- *  - working around the horridness of the rest
  */
 
 #include <linux/config.h>
@@ -64,7 +59,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v2.2"
+#define DRIVER_VERSION "v2.3"
 #define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, \
 Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, \
 Alan Stern"
@@ -89,8 +84,9 @@ static char *errbuf;
 
 static kmem_cache_t *uhci_up_cachep;   /* urb_priv */
 
+static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state);
+static void wakeup_rh(struct uhci_hcd *uhci);
 static void uhci_get_current_frame_number(struct uhci_hcd *uhci);
-static void hc_state_transitions(struct uhci_hcd *uhci);
 
 /* If a transfer is still active after this much time, turn off FSBR */
 #define IDLE_TIMEOUT   msecs_to_jiffies(50)
@@ -101,308 +97,352 @@ static void hc_state_transitions(struct uhci_hcd *uhci);
 /* to make sure it doesn't hog all of the bandwidth */
 #define DEPTH_INTERVAL 5
 
+static inline void restart_timer(struct uhci_hcd *uhci)
+{
+       mod_timer(&uhci->stall_timer, jiffies + msecs_to_jiffies(100));
+}
+
 #include "uhci-hub.c"
 #include "uhci-debug.c"
 #include "uhci-q.c"
 
-static int init_stall_timer(struct usb_hcd *hcd);
-
-static void stall_callback(unsigned long ptr)
+/*
+ * Make sure the controller is completely inactive, unable to
+ * generate interrupts or do DMA.
+ */
+static void reset_hc(struct uhci_hcd *uhci)
 {
-       struct usb_hcd *hcd = (struct usb_hcd *)ptr;
-       struct uhci_hcd *uhci = hcd_to_uhci(hcd);
-       struct urb_priv *up;
-       unsigned long flags;
+       int port;
 
-       spin_lock_irqsave(&uhci->lock, flags);
-       uhci_scan_schedule(uhci, NULL);
-
-       list_for_each_entry(up, &uhci->urb_list, urb_list) {
-               struct urb *u = up->urb;
-
-               spin_lock(&u->lock);
-
-               /* Check if the FSBR timed out */
-               if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT))
-                       uhci_fsbr_timeout(uhci, u);
+       /* Turn off PIRQ enable and SMI enable.  (This also turns off the
+        * BIOS's USB Legacy Support.)  Turn off all the R/WC bits too.
+        */
+       pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
+                       USBLEGSUP_RWC);
 
-               spin_unlock(&u->lock);
-       }
+       /* Reset the HC - this will force us to get a
+        * new notification of any already connected
+        * ports due to the virtual disconnect that it
+        * implies.
+        */
+       outw(USBCMD_HCRESET, uhci->io_addr + USBCMD);
+       mb();
+       udelay(5);
+       if (inw(uhci->io_addr + USBCMD) & USBCMD_HCRESET)
+               dev_warn(uhci_dev(uhci), "HCRESET not completed yet!\n");
 
-       /* Really disable FSBR */
-       if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) {
-               uhci->fsbrtimeout = 0;
-               uhci->skel_term_qh->link = UHCI_PTR_TERM;
-       }
+       /* Just to be safe, disable interrupt requests and
+        * make sure the controller is stopped.
+        */
+       outw(0, uhci->io_addr + USBINTR);
+       outw(0, uhci->io_addr + USBCMD);
 
-       /* Poll for and perform state transitions */
-       hc_state_transitions(uhci);
-       if (unlikely(uhci->suspended_ports && uhci->state != UHCI_SUSPENDED))
-               uhci_check_ports(uhci);
+       /* HCRESET doesn't affect the Suspend, Reset, and Resume Detect
+        * bits in the port status and control registers.
+        * We have to clear them by hand.
+        */
+       for (port = 0; port < uhci->rh_numports; ++port)
+               outw(0, uhci->io_addr + USBPORTSC1 + (port * 2));
 
-       init_stall_timer(hcd);
-       spin_unlock_irqrestore(&uhci->lock, flags);
+       uhci->port_c_suspend = uhci->suspended_ports =
+                       uhci->resuming_ports = 0;
+       uhci->rh_state = UHCI_RH_RESET;
+       uhci->is_stopped = UHCI_IS_STOPPED;
+       uhci_to_hcd(uhci)->state = HC_STATE_HALT;
+       uhci_to_hcd(uhci)->poll_rh = 0;
 }
 
-static int init_stall_timer(struct usb_hcd *hcd)
+/*
+ * Last rites for a defunct/nonfunctional controller
+ * or one we don't want to use any more.
+ */
+static void hc_died(struct uhci_hcd *uhci)
 {
-       struct uhci_hcd *uhci = hcd_to_uhci(hcd);
-
-       init_timer(&uhci->stall_timer);
-       uhci->stall_timer.function = stall_callback;
-       uhci->stall_timer.data = (unsigned long)hcd;
-       uhci->stall_timer.expires = jiffies + msecs_to_jiffies(100);
-       add_timer(&uhci->stall_timer);
-
-       return 0;
+       reset_hc(uhci);
+       uhci->hc_inaccessible = 1;
+       del_timer(&uhci->stall_timer);
 }
 
-static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
+/*
+ * Initialize a controller that was newly discovered or has just been
+ * resumed.  In either case we can't be sure of its previous state.
+ */
+static void check_and_reset_hc(struct uhci_hcd *uhci)
 {
-       struct uhci_hcd *uhci = hcd_to_uhci(hcd);
-       unsigned long io_addr = uhci->io_addr;
-       unsigned short status;
+       u16 legsup;
+       unsigned int cmd, intr;
 
        /*
-        * Read the interrupt status, and write it back to clear the
-        * interrupt cause.  Contrary to the UHCI specification, the
-        * "HC Halted" status bit is persistent: it is RO, not R/WC.
+        * When restarting a suspended controller, we expect all the
+        * settings to be the same as we left them:
+        *
+        *      PIRQ and SMI disabled, no R/W bits set in USBLEGSUP;
+        *      Controller is stopped and configured with EGSM set;
+        *      No interrupts enabled except possibly Resume Detect.
+        *
+        * If any of these conditions are violated we do a complete reset.
         */
-       status = inw(io_addr + USBSTS);
-       if (!(status & ~USBSTS_HCH))    /* shared interrupt, not mine */
-               return IRQ_NONE;
-       outw(status, io_addr + USBSTS);         /* Clear it */
-
-       if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {
-               if (status & USBSTS_HSE)
-                       dev_err(uhci_dev(uhci), "host system error, "
-                                       "PCI problems?\n");
-               if (status & USBSTS_HCPE)
-                       dev_err(uhci_dev(uhci), "host controller process "
-                                       "error, something bad happened!\n");
-               if ((status & USBSTS_HCH) && uhci->state > 0) {
-                       dev_err(uhci_dev(uhci), "host controller halted, "
-                                       "very bad!\n");
-                       /* FIXME: Reset the controller, fix the offending TD */
-               }
+       pci_read_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, &legsup);
+       if (legsup & ~(USBLEGSUP_RO | USBLEGSUP_RWC)) {
+               dev_dbg(uhci_dev(uhci), "%s: legsup = 0x%04x\n",
+                               __FUNCTION__, legsup);
+               goto reset_needed;
        }
 
-       if (status & USBSTS_RD)
-               uhci->resume_detect = 1;
+       cmd = inw(uhci->io_addr + USBCMD);
+       if ((cmd & USBCMD_RS) || !(cmd & USBCMD_CF) || !(cmd & USBCMD_EGSM)) {
+               dev_dbg(uhci_dev(uhci), "%s: cmd = 0x%04x\n",
+                               __FUNCTION__, cmd);
+               goto reset_needed;
+       }
 
-       spin_lock(&uhci->lock);
-       uhci_scan_schedule(uhci, regs);
-       spin_unlock(&uhci->lock);
+       intr = inw(uhci->io_addr + USBINTR);
+       if (intr & (~USBINTR_RESUME)) {
+               dev_dbg(uhci_dev(uhci), "%s: intr = 0x%04x\n",
+                               __FUNCTION__, intr);
+               goto reset_needed;
+       }
+       return;
 
-       return IRQ_HANDLED;
+reset_needed:
+       dev_dbg(uhci_dev(uhci), "Performing full reset\n");
+       reset_hc(uhci);
 }
 
-static void reset_hc(struct uhci_hcd *uhci)
+/*
+ * Store the basic register settings needed by the controller.
+ */
+static void configure_hc(struct uhci_hcd *uhci)
 {
-       unsigned long io_addr = uhci->io_addr;
+       /* Set the frame length to the default: 1 ms exactly */
+       outb(USBSOF_DEFAULT, uhci->io_addr + USBSOF);
 
-       /* Turn off PIRQ, SMI, and all interrupts.  This also turns off
-        * the BIOS's USB Legacy Support.
-        */
-       pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0);
-       outw(0, uhci->io_addr + USBINTR);
+       /* Store the frame list base address */
+       outl(uhci->fl->dma_handle, uhci->io_addr + USBFLBASEADD);
 
-       /* Global reset for 50ms */
-       uhci->state = UHCI_RESET;
-       outw(USBCMD_GRESET, io_addr + USBCMD);
-       msleep(50);
-       outw(0, io_addr + USBCMD);
+       /* Set the current frame number */
+       outw(uhci->frame_number, uhci->io_addr + USBFRNUM);
 
-       /* Another 10ms delay */
-       msleep(10);
-       uhci->resume_detect = 0;
-       uhci->is_stopped = UHCI_IS_STOPPED;
+       /* Mark controller as running before we enable interrupts */
+       uhci_to_hcd(uhci)->state = HC_STATE_RUNNING;
+       mb();
+
+       /* Enable PIRQ */
+       pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
+                       USBLEGSUP_DEFAULT);
 }
 
-static void suspend_hc(struct uhci_hcd *uhci)
+
+static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci)
 {
-       unsigned long io_addr = uhci->io_addr;
+       int port;
 
-       dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);
-       uhci->state = UHCI_SUSPENDED;
-       uhci->resume_detect = 0;
-       outw(USBCMD_EGSM, io_addr + USBCMD);
+       switch (to_pci_dev(uhci_dev(uhci))->vendor) {
+           default:
+               break;
 
-       /* FIXME: Wait for the controller to actually stop */
-       uhci_get_current_frame_number(uhci);
-       uhci->is_stopped = UHCI_IS_STOPPED;
+           case PCI_VENDOR_ID_GENESYS:
+               /* Genesys Logic's GL880S controllers don't generate
+                * resume-detect interrupts.
+                */
+               return 1;
 
-       uhci_scan_schedule(uhci, NULL);
+           case PCI_VENDOR_ID_INTEL:
+               /* Some of Intel's USB controllers have a bug that causes
+                * resume-detect interrupts if any port has an over-current
+                * condition.  To make matters worse, some motherboards
+                * hardwire unused USB ports' over-current inputs active!
+                * To prevent problems, we will not enable resume-detect
+                * interrupts if any ports are OC.
+                */
+               for (port = 0; port < uhci->rh_numports; ++port) {
+                       if (inw(uhci->io_addr + USBPORTSC1 + port * 2) &
+                                       USBPORTSC_OC)
+                               return 1;
+               }
+               break;
+       }
+       return 0;
 }
 
-static void wakeup_hc(struct uhci_hcd *uhci)
+static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state)
+__releases(uhci->lock)
+__acquires(uhci->lock)
 {
-       unsigned long io_addr = uhci->io_addr;
+       int auto_stop;
+       int int_enable;
 
-       switch (uhci->state) {
-               case UHCI_SUSPENDED:            /* Start the resume */
-                       dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);
-
-                       /* Global resume for >= 20ms */
-                       outw(USBCMD_FGR | USBCMD_EGSM, io_addr + USBCMD);
-                       uhci->state = UHCI_RESUMING_1;
-                       uhci->state_end = jiffies + msecs_to_jiffies(20);
-                       uhci->is_stopped = 0;
-                       break;
+       auto_stop = (new_state == UHCI_RH_AUTO_STOPPED);
+       dev_dbg(uhci_dev(uhci), "%s%s\n", __FUNCTION__,
+                       (auto_stop ? " (auto-stop)" : ""));
 
-               case UHCI_RESUMING_1:           /* End global resume */
-                       uhci->state = UHCI_RESUMING_2;
-                       outw(0, io_addr + USBCMD);
-                       /* Falls through */
-
-               case UHCI_RESUMING_2:           /* Wait for EOP to be sent */
-                       if (inw(io_addr + USBCMD) & USBCMD_FGR)
-                               break;
-
-                       /* Run for at least 1 second, and
-                        * mark it configured with a 64-byte max packet */
-                       uhci->state = UHCI_RUNNING_GRACE;
-                       uhci->state_end = jiffies + HZ;
-                       outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP,
-                                       io_addr + USBCMD);
-                       break;
+       /* If we get a suspend request when we're already auto-stopped
+        * then there's nothing to do.
+        */
+       if (uhci->rh_state == UHCI_RH_AUTO_STOPPED) {
+               uhci->rh_state = new_state;
+               return;
+       }
 
-               case UHCI_RUNNING_GRACE:        /* Now allowed to suspend */
-                       uhci->state = UHCI_RUNNING;
-                       break;
+       /* Enable resume-detect interrupts if they work.
+        * Then enter Global Suspend mode, still configured.
+        */
+       int_enable = (resume_detect_interrupts_are_broken(uhci) ?
+                       0 : USBINTR_RESUME);
+       outw(int_enable, uhci->io_addr + USBINTR);
+       outw(USBCMD_EGSM | USBCMD_CF, uhci->io_addr + USBCMD);
+       mb();
+       udelay(5);
 
-               default:
-                       break;
+       /* If we're auto-stopping then no devices have been attached
+        * for a while, so there shouldn't be any active URBs and the
+        * controller should stop after a few microseconds.  Otherwise
+        * we will give the controller one frame to stop.
+        */
+       if (!auto_stop && !(inw(uhci->io_addr + USBSTS) & USBSTS_HCH)) {
+               uhci->rh_state = UHCI_RH_SUSPENDING;
+               spin_unlock_irq(&uhci->lock);
+               msleep(1);
+               spin_lock_irq(&uhci->lock);
+               if (uhci->hc_inaccessible)      /* Died */
+                       return;
        }
-}
+       if (!(inw(uhci->io_addr + USBSTS) & USBSTS_HCH))
+               dev_warn(uhci_dev(uhci), "Controller not stopped yet!\n");
 
-static int ports_active(struct uhci_hcd *uhci)
-{
-       unsigned long io_addr = uhci->io_addr;
-       int connection = 0;
-       int i;
+       uhci_get_current_frame_number(uhci);
+       smp_wmb();
 
-       for (i = 0; i < uhci->rh_numports; i++)
-               connection |= (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_CCS);
+       uhci->rh_state = new_state;
+       uhci->is_stopped = UHCI_IS_STOPPED;
+       del_timer(&uhci->stall_timer);
+       uhci_to_hcd(uhci)->poll_rh = !int_enable;
 
-       return connection;
+       uhci_scan_schedule(uhci, NULL);
 }
 
-static int suspend_allowed(struct uhci_hcd *uhci)
+static void start_rh(struct uhci_hcd *uhci)
 {
-       unsigned long io_addr = uhci->io_addr;
-       int i;
-
-       if (to_pci_dev(uhci_dev(uhci))->vendor != PCI_VENDOR_ID_INTEL)
-               return 1;
+       uhci->is_stopped = 0;
+       smp_wmb();
 
-       /* Some of Intel's USB controllers have a bug that causes false
-        * resume indications if any port has an over current condition.
-        * To prevent problems, we will not allow a global suspend if
-        * any ports are OC.
-        *
-        * Some motherboards using Intel's chipsets (but not using all
-        * the USB ports) appear to hardwire the over current inputs active
-        * to disable the USB ports.
+       /* Mark it configured and running with a 64-byte max packet.
+        * All interrupts are enabled, even though RESUME won't do anything.
         */
-
-       /* check for over current condition on any port */
-       for (i = 0; i < uhci->rh_numports; i++) {
-               if (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_OC)
-                       return 0;
-       }
-
-       return 1;
+       outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, uhci->io_addr + USBCMD);
+       outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
+                       uhci->io_addr + USBINTR);
+       mb();
+       uhci->rh_state = UHCI_RH_RUNNING;
+       uhci_to_hcd(uhci)->poll_rh = 1;
+       restart_timer(uhci);
 }
 
-static void hc_state_transitions(struct uhci_hcd *uhci)
+static void wakeup_rh(struct uhci_hcd *uhci)
+__releases(uhci->lock)
+__acquires(uhci->lock)
 {
-       switch (uhci->state) {
-               case UHCI_RUNNING:
+       dev_dbg(uhci_dev(uhci), "%s%s\n", __FUNCTION__,
+                       uhci->rh_state == UHCI_RH_AUTO_STOPPED ?
+                               " (auto-start)" : "");
 
-                       /* global suspend if nothing connected for 1 second */
-                       if (!ports_active(uhci) && suspend_allowed(uhci)) {
-                               uhci->state = UHCI_SUSPENDING_GRACE;
-                               uhci->state_end = jiffies + HZ;
-                       }
-                       break;
-
-               case UHCI_SUSPENDING_GRACE:
-                       if (ports_active(uhci))
-                               uhci->state = UHCI_RUNNING;
-                       else if (time_after_eq(jiffies, uhci->state_end))
-                               suspend_hc(uhci);
-                       break;
-
-               case UHCI_SUSPENDED:
-
-                       /* wakeup if requested by a device */
-                       if (uhci->resume_detect)
-                               wakeup_hc(uhci);
-                       break;
+       /* If we are auto-stopped then no devices are attached so there's
+        * no need for wakeup signals.  Otherwise we send Global Resume
+        * for 20 ms.
+        */
+       if (uhci->rh_state == UHCI_RH_SUSPENDED) {
+               uhci->rh_state = UHCI_RH_RESUMING;
+               outw(USBCMD_FGR | USBCMD_EGSM | USBCMD_CF,
+                               uhci->io_addr + USBCMD);
+               spin_unlock_irq(&uhci->lock);
+               msleep(20);
+               spin_lock_irq(&uhci->lock);
+               if (uhci->hc_inaccessible)      /* Died */
+                       return;
+
+               /* End Global Resume and wait for EOP to be sent */
+               outw(USBCMD_CF, uhci->io_addr + USBCMD);
+               mb();
+               udelay(4);
+               if (inw(uhci->io_addr + USBCMD) & USBCMD_FGR)
+                       dev_warn(uhci_dev(uhci), "FGR not stopped yet!\n");
+       }
 
-               case UHCI_RESUMING_1:
-               case UHCI_RESUMING_2:
-               case UHCI_RUNNING_GRACE:
-                       if (time_after_eq(jiffies, uhci->state_end))
-                               wakeup_hc(uhci);
-                       break;
+       start_rh(uhci);
 
-               default:
-                       break;
-       }
+       /* Restart root hub polling */
+       mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies);
 }
 
-/*
- * Store the current frame number in uhci->frame_number if the controller
- * is runnning
- */
-static void uhci_get_current_frame_number(struct uhci_hcd *uhci)
+static void stall_callback(unsigned long _uhci)
 {
+       struct uhci_hcd *uhci = (struct uhci_hcd *) _uhci;
+       unsigned long flags;
+
+       spin_lock_irqsave(&uhci->lock, flags);
+       uhci_scan_schedule(uhci, NULL);
+       check_fsbr(uhci);
+
        if (!uhci->is_stopped)
-               uhci->frame_number = inw(uhci->io_addr + USBFRNUM);
+               restart_timer(uhci);
+       spin_unlock_irqrestore(&uhci->lock, flags);
 }
 
-static int start_hc(struct uhci_hcd *uhci)
+static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
 {
-       unsigned long io_addr = uhci->io_addr;
-       int timeout = 10;
+       struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+       unsigned short status;
+       unsigned long flags;
 
        /*
-        * Reset the HC - this will force us to get a
-        * new notification of any already connected
-        * ports due to the virtual disconnect that it
-        * implies.
+        * Read the interrupt status, and write it back to clear the
+        * interrupt cause.  Contrary to the UHCI specification, the
+        * "HC Halted" status bit is persistent: it is RO, not R/WC.
         */
-       outw(USBCMD_HCRESET, io_addr + USBCMD);
-       while (inw(io_addr + USBCMD) & USBCMD_HCRESET) {
-               if (--timeout < 0) {
-                       dev_err(uhci_dev(uhci), "USBCMD_HCRESET timed out!\n");
-                       return -ETIMEDOUT;
+       status = inw(uhci->io_addr + USBSTS);
+       if (!(status & ~USBSTS_HCH))    /* shared interrupt, not mine */
+               return IRQ_NONE;
+       outw(status, uhci->io_addr + USBSTS);           /* Clear it */
+
+       if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {
+               if (status & USBSTS_HSE)
+                       dev_err(uhci_dev(uhci), "host system error, "
+                                       "PCI problems?\n");
+               if (status & USBSTS_HCPE)
+                       dev_err(uhci_dev(uhci), "host controller process "
+                                       "error, something bad happened!\n");
+               if (status & USBSTS_HCH) {
+                       spin_lock_irqsave(&uhci->lock, flags);
+                       if (uhci->rh_state >= UHCI_RH_RUNNING) {
+                               dev_err(uhci_dev(uhci),
+                                       "host controller halted, "
+                                       "very bad!\n");
+                               hc_died(uhci);
+                               spin_unlock_irqrestore(&uhci->lock, flags);
+                               return IRQ_HANDLED;
+                       }
+                       spin_unlock_irqrestore(&uhci->lock, flags);
                }
-               msleep(1);
        }
 
-       /* Mark controller as running before we enable interrupts */
-       uhci_to_hcd(uhci)->state = HC_STATE_RUNNING;
-
-       /* Turn on PIRQ and all interrupts */
-       pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
-                       USBLEGSUP_DEFAULT);
-       outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
-               io_addr + USBINTR);
+       if (status & USBSTS_RD)
+               usb_hcd_poll_rh_status(hcd);
 
-       /* Start at frame 0 */
-       outw(0, io_addr + USBFRNUM);
-       outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD);
+       spin_lock_irqsave(&uhci->lock, flags);
+       uhci_scan_schedule(uhci, regs);
+       spin_unlock_irqrestore(&uhci->lock, flags);
 
-       /* Run and mark it configured with a 64-byte max packet */
-       uhci->state = UHCI_RUNNING_GRACE;
-       uhci->state_end = jiffies + HZ;
-       outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
-       uhci->is_stopped = 0;
+       return IRQ_HANDLED;
+}
 
-       return 0;
+/*
+ * Store the current frame number in uhci->frame_number if the controller
+ * is runnning
+ */
+static void uhci_get_current_frame_number(struct uhci_hcd *uhci)
+{
+       if (!uhci->is_stopped)
+               uhci->frame_number = inw(uhci->io_addr + USBFRNUM);
 }
 
 /*
@@ -448,16 +488,58 @@ static void release_uhci(struct uhci_hcd *uhci)
 static int uhci_reset(struct usb_hcd *hcd)
 {
        struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+       unsigned io_size = (unsigned) hcd->rsrc_len;
+       int port;
 
        uhci->io_addr = (unsigned long) hcd->rsrc_start;
 
-       /* Kick BIOS off this hardware and reset, so we won't get
-        * interrupts from any previous setup.
+       /* The UHCI spec says devices must have 2 ports, and goes on to say
+        * they may have more but gives no way to determine how many there
+        * are.  However according to the UHCI spec, Bit 7 of the port
+        * status and control register is always set to 1.  So we try to
+        * use this to our advantage.  Another common failure mode when
+        * a nonexistent register is addressed is to return all ones, so
+        * we test for that also.
         */
-       reset_hc(uhci);
+       for (port = 0; port < (io_size - USBPORTSC1) / 2; port++) {
+               unsigned int portstatus;
+
+               portstatus = inw(uhci->io_addr + USBPORTSC1 + (port * 2));
+               if (!(portstatus & 0x0080) || portstatus == 0xffff)
+                       break;
+       }
+       if (debug)
+               dev_info(uhci_dev(uhci), "detected %d ports\n", port);
+
+       /* Anything greater than 7 is weird so we'll ignore it. */
+       if (port > UHCI_RH_MAXCHILD) {
+               dev_info(uhci_dev(uhci), "port count misdetected? "
+                               "forcing to 2 ports\n");
+               port = 2;
+       }
+       uhci->rh_numports = port;
+
+       /* Kick BIOS off this hardware and reset if the controller
+        * isn't already safely quiescent.
+        */
+       check_and_reset_hc(uhci);
        return 0;
 }
 
+/* Make sure the controller is quiescent and that we're not using it
+ * any more.  This is mainly for the benefit of programs which, like kexec,
+ * expect the hardware to be idle: not doing DMA or generating IRQs.
+ *
+ * This routine may be called in a damaged or failing kernel.  Hence we
+ * do not acquire the spinlock before shutting down the controller.
+ */
+static void uhci_shutdown(struct pci_dev *pdev)
+{
+       struct usb_hcd *hcd = (struct usb_hcd *) pci_get_drvdata(pdev);
+
+       hc_died(hcd_to_uhci(hcd));
+}
+
 /*
  * Allocate a frame list, and then setup the skeleton
  *
@@ -478,17 +560,20 @@ static int uhci_start(struct usb_hcd *hcd)
 {
        struct uhci_hcd *uhci = hcd_to_uhci(hcd);
        int retval = -EBUSY;
-       int i, port;
-       unsigned io_size;
+       int i;
        dma_addr_t dma_handle;
-       struct usb_device *udev;
        struct dentry *dentry;
 
-       io_size = (unsigned) hcd->rsrc_len;
+       hcd->uses_new_polling = 1;
+       if (pci_find_capability(to_pci_dev(uhci_dev(uhci)), PCI_CAP_ID_PM))
+               hcd->can_wakeup = 1;            /* Assume it supports PME# */
 
-       dentry = debugfs_create_file(hcd->self.bus_name, S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root, uhci, &uhci_debug_operations);
+       dentry = debugfs_create_file(hcd->self.bus_name,
+                       S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root, uhci,
+                       &uhci_debug_operations);
        if (!dentry) {
-               dev_err(uhci_dev(uhci), "couldn't create uhci debugfs entry\n");
+               dev_err(uhci_dev(uhci),
+                               "couldn't create uhci debugfs entry\n");
                retval = -ENOMEM;
                goto err_create_debug_entry;
        }
@@ -510,6 +595,10 @@ static int uhci_start(struct usb_hcd *hcd)
 
        init_waitqueue_head(&uhci->waitqh);
 
+       init_timer(&uhci->stall_timer);
+       uhci->stall_timer.function = stall_callback;
+       uhci->stall_timer.data = (unsigned long) uhci;
+
        uhci->fl = dma_alloc_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
                        &dma_handle, 0);
        if (!uhci->fl) {
@@ -536,46 +625,14 @@ static int uhci_start(struct usb_hcd *hcd)
                goto err_create_qh_pool;
        }
 
-       /* Initialize the root hub */
-
-       /* UHCI specs says devices must have 2 ports, but goes on to say */
-       /*  they may have more but give no way to determine how many they */
-       /*  have. However, according to the UHCI spec, Bit 7 is always set */
-       /*  to 1. So we try to use this to our advantage */
-       for (port = 0; port < (io_size - 0x10) / 2; port++) {
-               unsigned int portstatus;
-
-               portstatus = inw(uhci->io_addr + 0x10 + (port * 2));
-               if (!(portstatus & 0x0080))
-                       break;
-       }
-       if (debug)
-               dev_info(uhci_dev(uhci), "detected %d ports\n", port);
-
-       /* This is experimental so anything less than 2 or greater than 8 is */
-       /*  something weird and we'll ignore it */
-       if (port < 2 || port > UHCI_RH_MAXCHILD) {
-               dev_info(uhci_dev(uhci), "port count misdetected? "
-                               "forcing to 2 ports\n");
-               port = 2;
-       }
-
-       uhci->rh_numports = port;
-
-       udev = usb_alloc_dev(NULL, &hcd->self, 0);
-       if (!udev) {
-               dev_err(uhci_dev(uhci), "unable to allocate root hub\n");
-               goto err_alloc_root_hub;
-       }
-
-       uhci->term_td = uhci_alloc_td(uhci, udev);
+       uhci->term_td = uhci_alloc_td(uhci);
        if (!uhci->term_td) {
                dev_err(uhci_dev(uhci), "unable to allocate terminating TD\n");
                goto err_alloc_term_td;
        }
 
        for (i = 0; i < UHCI_NUM_SKELQH; i++) {
-               uhci->skelqh[i] = uhci_alloc_qh(uhci, udev);
+               uhci->skelqh[i] = uhci_alloc_qh(uhci);
                if (!uhci->skelqh[i]) {
                        dev_err(uhci_dev(uhci), "unable to allocate QH\n");
                        goto err_alloc_skelqh;
@@ -641,32 +698,17 @@ static int uhci_start(struct usb_hcd *hcd)
 
        /*
         * Some architectures require a full mb() to enforce completion of
-        * the memory writes above before the I/O transfers in start_hc().
+        * the memory writes above before the I/O transfers in configure_hc().
         */
        mb();
-       if ((retval = start_hc(uhci)) != 0)
-               goto err_alloc_skelqh;
-
-       init_stall_timer(hcd);
-
-       udev->speed = USB_SPEED_FULL;
-
-       if (usb_hcd_register_root_hub(udev, hcd) != 0) {
-               dev_err(uhci_dev(uhci), "unable to start root hub\n");
-               retval = -ENOMEM;
-               goto err_start_root_hub;
-       }
 
+       configure_hc(uhci);
+       start_rh(uhci);
        return 0;
 
 /*
  * error exits:
  */
-err_start_root_hub:
-       reset_hc(uhci);
-
-       del_timer_sync(&uhci->stall_timer);
-
 err_alloc_skelqh:
        for (i = 0; i < UHCI_NUM_SKELQH; i++)
                if (uhci->skelqh[i]) {
@@ -678,9 +720,6 @@ err_alloc_skelqh:
        uhci->term_td = NULL;
 
 err_alloc_term_td:
-       usb_put_dev(udev);
-
-err_alloc_root_hub:
        dma_pool_destroy(uhci->qh_pool);
        uhci->qh_pool = NULL;
 
@@ -705,73 +744,114 @@ static void uhci_stop(struct usb_hcd *hcd)
 {
        struct uhci_hcd *uhci = hcd_to_uhci(hcd);
 
-       del_timer_sync(&uhci->stall_timer);
-       reset_hc(uhci);
-
        spin_lock_irq(&uhci->lock);
+       reset_hc(uhci);
        uhci_scan_schedule(uhci, NULL);
        spin_unlock_irq(&uhci->lock);
-       
+
+       del_timer_sync(&uhci->stall_timer);
        release_uhci(uhci);
 }
 
 #ifdef CONFIG_PM
+static int uhci_rh_suspend(struct usb_hcd *hcd)
+{
+       struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+       spin_lock_irq(&uhci->lock);
+       if (!uhci->hc_inaccessible)             /* Not dead */
+               suspend_rh(uhci, UHCI_RH_SUSPENDED);
+       spin_unlock_irq(&uhci->lock);
+       return 0;
+}
+
+static int uhci_rh_resume(struct usb_hcd *hcd)
+{
+       struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+       int rc = 0;
+
+       spin_lock_irq(&uhci->lock);
+       if (uhci->hc_inaccessible) {
+               if (uhci->rh_state == UHCI_RH_SUSPENDED) {
+                       dev_warn(uhci_dev(uhci), "HC isn't running!\n");
+                       rc = -ENODEV;
+               }
+               /* Otherwise the HC is dead */
+       } else
+               wakeup_rh(uhci);
+       spin_unlock_irq(&uhci->lock);
+       return rc;
+}
+
 static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message)
 {
        struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+       int rc = 0;
+
+       dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);
 
        spin_lock_irq(&uhci->lock);
+       if (uhci->hc_inaccessible)      /* Dead or already suspended */
+               goto done;
 
-       /* Don't try to suspend broken motherboards, reset instead */
-       if (suspend_allowed(uhci))
-               suspend_hc(uhci);
-       else {
-               spin_unlock_irq(&uhci->lock);
-               reset_hc(uhci);
-               spin_lock_irq(&uhci->lock);
-               uhci_scan_schedule(uhci, NULL);
-       }
+#ifndef CONFIG_USB_SUSPEND
+       /* Otherwise this would never happen */
+       suspend_rh(uhci, UHCI_RH_SUSPENDED);
+#endif
+
+       if (uhci->rh_state > UHCI_RH_SUSPENDED) {
+               dev_warn(uhci_dev(uhci), "Root hub isn't suspended!\n");
+               hcd->state = HC_STATE_RUNNING;
+               rc = -EBUSY;
+               goto done;
+       };
 
+       /* All PCI host controllers are required to disable IRQ generation
+        * at the source, so we must turn off PIRQ.
+        */
+       pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0);
+       uhci->hc_inaccessible = 1;
+
+       /* FIXME: Enable non-PME# remote wakeup? */
+
+done:
        spin_unlock_irq(&uhci->lock);
-       return 0;
+       if (rc == 0)
+               del_timer_sync(&hcd->rh_timer);
+       return rc;
 }
 
 static int uhci_resume(struct usb_hcd *hcd)
 {
        struct uhci_hcd *uhci = hcd_to_uhci(hcd);
-       int rc;
 
-       pci_set_master(to_pci_dev(uhci_dev(uhci)));
+       dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);
 
+       if (uhci->rh_state == UHCI_RH_RESET)    /* Dead */
+               return 0;
        spin_lock_irq(&uhci->lock);
 
-       if (uhci->state == UHCI_SUSPENDED) {
+       /* FIXME: Disable non-PME# remote wakeup? */
 
-               /*
-                * Some systems don't maintain the UHCI register values
-                * during a PM suspend/resume cycle, so reinitialize
-                * the Frame Number, Framelist Base Address, Interrupt
-                * Enable, and Legacy Support registers.
-                */
-               pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
-                               0);
-               outw(uhci->frame_number, uhci->io_addr + USBFRNUM);
-               outl(uhci->fl->dma_handle, uhci->io_addr + USBFLBASEADD);
-               outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC |
-                               USBINTR_SP, uhci->io_addr + USBINTR);
-               uhci->resume_detect = 1;
-               pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
-                               USBLEGSUP_DEFAULT);
-       } else {
-               spin_unlock_irq(&uhci->lock);
-               reset_hc(uhci);
-               if ((rc = start_hc(uhci)) != 0)
-                       return rc;
-               spin_lock_irq(&uhci->lock);
-       }
-       hcd->state = HC_STATE_RUNNING;
+       uhci->hc_inaccessible = 0;
+
+       /* The BIOS may have changed the controller settings during a
+        * system wakeup.  Check it and reconfigure to avoid problems.
+        */
+       check_and_reset_hc(uhci);
+       configure_hc(uhci);
+
+#ifndef CONFIG_USB_SUSPEND
+       /* Otherwise this would never happen */
+       wakeup_rh(uhci);
+#endif
+       if (uhci->rh_state == UHCI_RH_RESET)
+               suspend_rh(uhci, UHCI_RH_SUSPENDED);
 
        spin_unlock_irq(&uhci->lock);
+
+       if (hcd->poll_rh)
+               usb_hcd_poll_rh_status(hcd);
        return 0;
 }
 #endif
@@ -788,13 +868,15 @@ static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd,
 static int uhci_hcd_get_frame_number(struct usb_hcd *hcd)
 {
        struct uhci_hcd *uhci = hcd_to_uhci(hcd);
-       int frame_number;
        unsigned long flags;
+       int is_stopped;
+       int frame_number;
 
        /* Minimize latency by avoiding the spinlock */
        local_irq_save(flags);
-       rmb();
-       frame_number = (uhci->is_stopped ? uhci->frame_number :
+       is_stopped = uhci->is_stopped;
+       smp_rmb();
+       frame_number = (is_stopped ? uhci->frame_number :
                        inw(uhci->io_addr + USBFRNUM));
        local_irq_restore(flags);
        return frame_number;
@@ -817,6 +899,8 @@ static const struct hc_driver uhci_driver = {
 #ifdef CONFIG_PM
        .suspend =              uhci_suspend,
        .resume =               uhci_resume,
+       .hub_suspend =          uhci_rh_suspend,
+       .hub_resume =           uhci_rh_resume,
 #endif
        .stop =                 uhci_stop,
 
@@ -845,6 +929,7 @@ static struct pci_driver uhci_pci_driver = {
 
        .probe =        usb_hcd_pci_probe,
        .remove =       usb_hcd_pci_remove,
+       .shutdown =     uhci_shutdown,
 
 #ifdef CONFIG_PM
        .suspend =      usb_hcd_pci_suspend,
index 02255d6..bf9c5f9 100644 (file)
@@ -41,6 +41,7 @@
 #define USBFRNUM       6
 #define USBFLBASEADD   8
 #define USBSOF         12
+#define   USBSOF_DEFAULT       64      /* Frame length is exactly 1 ms */
 
 /* USB port status and control registers */
 #define USBPORTSC1     16
@@ -66,6 +67,8 @@
 /* Legacy support register */
 #define USBLEGSUP              0xc0
 #define   USBLEGSUP_DEFAULT    0x2000  /* only PIRQ enable set */
+#define   USBLEGSUP_RWC                0x8f00  /* the R/WC bits */
+#define   USBLEGSUP_RO         0x5040  /* R/O and reserved bits */
 
 #define UHCI_NULL_DATA_SIZE    0x7FF   /* for UHCI controller TD */
 
@@ -111,7 +114,6 @@ struct uhci_qh {
        /* Software fields */
        dma_addr_t dma_handle;
 
-       struct usb_device *dev;
        struct urb_priv *urbp;
 
        struct list_head list;          /* P: uhci->frame_list_lock */
@@ -203,7 +205,6 @@ struct uhci_td {
        /* Software fields */
        dma_addr_t dma_handle;
 
-       struct usb_device *dev;
        struct urb *urb;
 
        struct list_head list;          /* P: urb->lock */
@@ -314,26 +315,32 @@ static inline int __interval_to_skel(int interval)
 }
 
 /*
- * Device states for the host controller.
+ * States for the root hub.
  *
  * To prevent "bouncing" in the presence of electrical noise,
- * we insist on a 1-second "grace" period, before switching to
- * the RUNNING or SUSPENDED states, during which the state is
- * not allowed to change.
- *
- * The resume process is divided into substates in order to avoid
- * potentially length delays during the timer handler.
- *
- * States in which the host controller is halted must have values <= 0.
+ * when there are no devices attached we delay for 1 second in the
+ * RUNNING_NODEVS state before switching to the AUTO_STOPPED state.
+ * 
+ * (Note that the AUTO_STOPPED state won't be necessary once the hub
+ * driver learns to autosuspend.)
  */
-enum uhci_state {
-       UHCI_RESET,
-       UHCI_RUNNING_GRACE,             /* Before RUNNING */
-       UHCI_RUNNING,                   /* The normal state */
-       UHCI_SUSPENDING_GRACE,          /* Before SUSPENDED */
-       UHCI_SUSPENDED = -10,           /* When no devices are attached */
-       UHCI_RESUMING_1,
-       UHCI_RESUMING_2
+enum uhci_rh_state {
+       /* In the following states the HC must be halted.
+        * These two must come first */
+       UHCI_RH_RESET,
+       UHCI_RH_SUSPENDED,
+
+       UHCI_RH_AUTO_STOPPED,
+       UHCI_RH_RESUMING,
+
+       /* In this state the HC changes from running to halted,
+        * so it can legally appear either way. */
+       UHCI_RH_SUSPENDING,
+
+       /* In the following states it's an error if the HC is halted.
+        * These two must come last */
+       UHCI_RH_RUNNING,                /* The normal state */
+       UHCI_RH_RUNNING_NODEVS,         /* Running with no devices attached */
 };
 
 /*
@@ -363,15 +370,16 @@ struct uhci_hcd {
        int fsbr;                               /* Full-speed bandwidth reclamation */
        unsigned long fsbrtimeout;              /* FSBR delay */
 
-       enum uhci_state state;                  /* FIXME: needs a spinlock */
-       unsigned long state_end;                /* Time of next transition */
+       enum uhci_rh_state rh_state;
+       unsigned long auto_stop_time;           /* When to AUTO_STOP */
+
        unsigned int frame_number;              /* As of last check */
        unsigned int is_stopped;
 #define UHCI_IS_STOPPED                9999            /* Larger than a frame # */
 
        unsigned int scan_in_progress:1;        /* Schedule scan is running */
        unsigned int need_rescan:1;             /* Redo the schedule scan */
-       unsigned int resume_detect:1;           /* Need a Global Resume */
+       unsigned int hc_inaccessible:1;         /* HC is suspended or dead */
 
        /* Support for port suspend/resume/reset */
        unsigned long port_c_suspend;           /* Bit-arrays of ports */
@@ -451,4 +459,11 @@ struct urb_priv {
  * #2 urb->lock
  */
 
+
+/* Some special IDs */
+
+#define PCI_VENDOR_ID_GENESYS          0x17a0
+#define PCI_DEVICE_ID_GL880S_UHCI      0x8083
+#define PCI_DEVICE_ID_GL880S_EHCI      0x8084
+
 #endif
index 4c45ba8..4eace2b 100644 (file)
@@ -33,9 +33,24 @@ static __u8 root_hub_hub_des[] =
 /* status change bits:  nonzero writes will clear */
 #define RWC_BITS       (USBPORTSC_OCC | USBPORTSC_PEC | USBPORTSC_CSC)
 
-static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
+/* A port that either is connected or has a changed-bit set will prevent
+ * us from AUTO_STOPPING.
+ */
+static int any_ports_active(struct uhci_hcd *uhci)
+{
+       int port;
+
+       for (port = 0; port < uhci->rh_numports; ++port) {
+               if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) &
+                               (USBPORTSC_CCS | RWC_BITS)) ||
+                               test_bit(port, &uhci->port_c_suspend))
+                       return 1;
+       }
+       return 0;
+}
+
+static inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf)
 {
-       struct uhci_hcd *uhci = hcd_to_uhci(hcd);
        int port;
 
        *buf = 0;
@@ -44,8 +59,6 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
                                test_bit(port, &uhci->port_c_suspend))
                        *buf |= (1 << (port + 1));
        }
-       if (*buf && uhci->state == UHCI_SUSPENDED)
-               uhci->resume_detect = 1;
        return !!*buf;
 }
 
@@ -115,6 +128,11 @@ static void uhci_check_ports(struct uhci_hcd *uhci)
                                set_bit(port, &uhci->resuming_ports);
                                uhci->ports_timeout = jiffies +
                                                msecs_to_jiffies(20);
+
+                               /* Make sure we see the port again
+                                * after the resuming period is over. */
+                               mod_timer(&uhci_to_hcd(uhci)->rh_timer,
+                                               uhci->ports_timeout);
                        } else if (time_after_eq(jiffies,
                                                uhci->ports_timeout)) {
                                uhci_finish_suspend(uhci, port, port_addr);
@@ -123,6 +141,60 @@ static void uhci_check_ports(struct uhci_hcd *uhci)
        }
 }
 
+static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+       struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+       unsigned long flags;
+       int status;
+
+       spin_lock_irqsave(&uhci->lock, flags);
+       if (uhci->hc_inaccessible) {
+               status = 0;
+               goto done;
+       }
+
+       uhci_check_ports(uhci);
+       status = get_hub_status_data(uhci, buf);
+
+       switch (uhci->rh_state) {
+           case UHCI_RH_SUSPENDING:
+           case UHCI_RH_SUSPENDED:
+               /* if port change, ask to be resumed */
+               if (status)
+                       usb_hcd_resume_root_hub(hcd);
+               break;
+
+           case UHCI_RH_AUTO_STOPPED:
+               /* if port change, auto start */
+               if (status)
+                       wakeup_rh(uhci);
+               break;
+
+           case UHCI_RH_RUNNING:
+               /* are any devices attached? */
+               if (!any_ports_active(uhci)) {
+                       uhci->rh_state = UHCI_RH_RUNNING_NODEVS;
+                       uhci->auto_stop_time = jiffies + HZ;
+               }
+               break;
+
+           case UHCI_RH_RUNNING_NODEVS:
+               /* auto-stop if nothing connected for 1 second */
+               if (any_ports_active(uhci))
+                       uhci->rh_state = UHCI_RH_RUNNING;
+               else if (time_after_eq(jiffies, uhci->auto_stop_time))
+                       suspend_rh(uhci, UHCI_RH_AUTO_STOPPED);
+               break;
+
+           default:
+               break;
+       }
+
+done:
+       spin_unlock_irqrestore(&uhci->lock, flags);
+       return status;
+}
+
 /* size of returned buffer is part of USB spec */
 static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                        u16 wIndex, char *buf, u16 wLength)
@@ -134,6 +206,9 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
        u16 wPortChange, wPortStatus;
        unsigned long flags;
 
+       if (uhci->hc_inaccessible)
+               return -ETIMEDOUT;
+
        spin_lock_irqsave(&uhci->lock, flags);
        switch (typeReq) {
 
index 2a7c195..5f18084 100644 (file)
@@ -32,6 +32,8 @@ static void uhci_free_pending_tds(struct uhci_hcd *uhci);
  */
 static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci)
 {
+       if (uhci->is_stopped)
+               mod_timer(&uhci->stall_timer, jiffies);
        uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC); 
 }
 
@@ -46,7 +48,7 @@ static inline void uhci_moveto_complete(struct uhci_hcd *uhci,
        list_move_tail(&urbp->urb_list, &uhci->complete_list);
 }
 
-static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci, struct usb_device *dev)
+static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci)
 {
        dma_addr_t dma_handle;
        struct uhci_td *td;
@@ -61,14 +63,11 @@ static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci, struct usb_device *d
        td->buffer = 0;
 
        td->frame = -1;
-       td->dev = dev;
 
        INIT_LIST_HEAD(&td->list);
        INIT_LIST_HEAD(&td->remove_list);
        INIT_LIST_HEAD(&td->fl_list);
 
-       usb_get_dev(dev);
-
        return td;
 }
 
@@ -168,13 +167,10 @@ static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)
        if (!list_empty(&td->fl_list))
                dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td);
 
-       if (td->dev)
-               usb_put_dev(td->dev);
-
        dma_pool_free(uhci->td_pool, td, td->dma_handle);
 }
 
-static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, struct usb_device *dev)
+static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci)
 {
        dma_addr_t dma_handle;
        struct uhci_qh *qh;
@@ -188,14 +184,11 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, struct usb_device *d
        qh->element = UHCI_PTR_TERM;
        qh->link = UHCI_PTR_TERM;
 
-       qh->dev = dev;
        qh->urbp = NULL;
 
        INIT_LIST_HEAD(&qh->list);
        INIT_LIST_HEAD(&qh->remove_list);
 
-       usb_get_dev(dev);
-
        return qh;
 }
 
@@ -206,9 +199,6 @@ static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
        if (!list_empty(&qh->remove_list))
                dev_warn(uhci_dev(uhci), "qh %p still in remove_list!\n", qh);
 
-       if (qh->dev)
-               usb_put_dev(qh->dev);
-
        dma_pool_free(uhci->qh_pool, qh, qh->dma_handle);
 }
 
@@ -597,7 +587,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur
        /*
         * Build the TD for the control request setup packet
         */
-       td = uhci_alloc_td(uhci, urb->dev);
+       td = uhci_alloc_td(uhci);
        if (!td)
                return -ENOMEM;
 
@@ -626,7 +616,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur
                if (pktsze > maxsze)
                        pktsze = maxsze;
 
-               td = uhci_alloc_td(uhci, urb->dev);
+               td = uhci_alloc_td(uhci);
                if (!td)
                        return -ENOMEM;
 
@@ -644,7 +634,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur
        /*
         * Build the final TD for control status 
         */
-       td = uhci_alloc_td(uhci, urb->dev);
+       td = uhci_alloc_td(uhci);
        if (!td)
                return -ENOMEM;
 
@@ -666,7 +656,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur
        uhci_fill_td(td, status | TD_CTRL_IOC,
                destination | uhci_explen(UHCI_NULL_DATA_SIZE), 0);
 
-       qh = uhci_alloc_qh(uhci, urb->dev);
+       qh = uhci_alloc_qh(uhci);
        if (!qh)
                return -ENOMEM;
 
@@ -865,7 +855,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb
                                status &= ~TD_CTRL_SPD;
                }
 
-               td = uhci_alloc_td(uhci, urb->dev);
+               td = uhci_alloc_td(uhci);
                if (!td)
                        return -ENOMEM;
 
@@ -891,7 +881,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb
         */
        if (usb_pipeout(urb->pipe) && (urb->transfer_flags & URB_ZERO_PACKET) &&
            !len && urb->transfer_buffer_length) {
-               td = uhci_alloc_td(uhci, urb->dev);
+               td = uhci_alloc_td(uhci);
                if (!td)
                        return -ENOMEM;
 
@@ -913,7 +903,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb
         * flag setting. */
        td->status |= cpu_to_le32(TD_CTRL_IOC);
 
-       qh = uhci_alloc_qh(uhci, urb->dev);
+       qh = uhci_alloc_qh(uhci);
        if (!qh)
                return -ENOMEM;
 
@@ -1096,7 +1086,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb)
                if (!urb->iso_frame_desc[i].length)
                        continue;
 
-               td = uhci_alloc_td(uhci, urb->dev);
+               td = uhci_alloc_td(uhci);
                if (!td)
                        return -ENOMEM;
 
@@ -1497,6 +1487,7 @@ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs)
  rescan:
        uhci->need_rescan = 0;
 
+       uhci_clear_next_interrupt(uhci);
        uhci_get_current_frame_number(uhci);
 
        if (uhci->frame_number + uhci->is_stopped != uhci->qh_remove_age)
@@ -1537,3 +1528,26 @@ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs)
        /* Wake up anyone waiting for an URB to complete */
        wake_up_all(&uhci->waitqh);
 }
+
+static void check_fsbr(struct uhci_hcd *uhci)
+{
+       struct urb_priv *up;
+
+       list_for_each_entry(up, &uhci->urb_list, urb_list) {
+               struct urb *u = up->urb;
+
+               spin_lock(&u->lock);
+
+               /* Check if the FSBR timed out */
+               if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT))
+                       uhci_fsbr_timeout(uhci, u);
+
+               spin_unlock(&u->lock);
+       }
+
+       /* Really disable FSBR */
+       if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) {
+               uhci->fsbrtimeout = 0;
+               uhci->skel_term_qh->link = UHCI_PTR_TERM;
+       }
+}
index db95c97..654ac45 100644 (file)
@@ -639,6 +639,7 @@ static void ati_remote_input_init(struct ati_remote *ati_remote)
        idev->id.vendor = le16_to_cpu(ati_remote->udev->descriptor.idVendor);
        idev->id.product = le16_to_cpu(ati_remote->udev->descriptor.idProduct);
        idev->id.version = le16_to_cpu(ati_remote->udev->descriptor.bcdDevice);
+       idev->dev = &(ati_remote->udev->dev);
 }
 
 static int ati_remote_initialize(struct ati_remote *ati_remote)
index ae455c8..7398a7f 100644 (file)
@@ -1375,9 +1375,13 @@ static int stv680_probe (struct usb_interface *intf, const struct usb_device_id
            (le16_to_cpu(dev->descriptor.idProduct) == USB_PENCAM_PRODUCT_ID)) {
                camera_name = "STV0680";
                PDEBUG (0, "STV(i): STV0680 camera found.");
+       } else if ((le16_to_cpu(dev->descriptor.idVendor) == USB_CREATIVEGOMINI_VENDOR_ID) &&
+                  (le16_to_cpu(dev->descriptor.idProduct) == USB_CREATIVEGOMINI_PRODUCT_ID)) {
+               camera_name = "Creative WebCam Go Mini";
+               PDEBUG (0, "STV(i): Creative WebCam Go Mini found.");
        } else {
-               PDEBUG (0, "STV(e): Vendor/Product ID do not match STV0680 values.");
-               PDEBUG (0, "STV(e): Check that the STV0680 camera is connected to the computer.");
+               PDEBUG (0, "STV(e): Vendor/Product ID do not match STV0680 or Creative WebCam Go Mini values.");
+               PDEBUG (0, "STV(e): Check that the STV0680 or Creative WebCam Go Mini camera is connected to the computer.");
                retval = -ENODEV;
                goto error;
        }
index 7e0e314..4459406 100644 (file)
 
 #define USB_PENCAM_VENDOR_ID   0x0553
 #define USB_PENCAM_PRODUCT_ID  0x0202
+
+#define USB_CREATIVEGOMINI_VENDOR_ID   0x041e
+#define USB_CREATIVEGOMINI_PRODUCT_ID  0x4007
+
 #define PENCAM_TIMEOUT          1000
 /* fmt 4 */
 #define STV_VIDEO_PALETTE       VIDEO_PALETTE_RGB24
 
 static struct usb_device_id device_table[] = {
        {USB_DEVICE (USB_PENCAM_VENDOR_ID, USB_PENCAM_PRODUCT_ID)},
+       {USB_DEVICE (USB_CREATIVEGOMINI_VENDOR_ID, USB_CREATIVEGOMINI_PRODUCT_ID)},
        {}
 };
 MODULE_DEVICE_TABLE (usb, device_table);
index ce030d1..733acc2 100644 (file)
@@ -1,4 +1,4 @@
-/* Siemens ID Mouse driver v0.5
+/* Siemens ID Mouse driver v0.6
 
   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
@@ -11,6 +11,9 @@
   Derived from the USB Skeleton driver 1.1,
   Copyright (C) 2003 Greg Kroah-Hartman (greg@kroah.com)
 
+  Additional information provided by Martin Reising
+  <Martin.Reising@natural-computing.de>
+
 */
 
 #include <linux/config.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
 
+/* image constants */
 #define WIDTH 225
-#define HEIGHT 288
-#define HEADER "P5 225 288 255 "
+#define HEIGHT 289
+#define HEADER "P5 225 289 255 "
 #define IMGSIZE ((WIDTH * HEIGHT) + sizeof(HEADER)-1)
 
-/* Version Information */
-#define DRIVER_VERSION "0.5"
+/* version information */
+#define DRIVER_VERSION "0.6"
 #define DRIVER_SHORT   "idmouse"
 #define DRIVER_AUTHOR  "Florian 'Floe' Echtler <echtler@fs.tum.de>"
 #define DRIVER_DESC    "Siemens ID Mouse FingerTIP Sensor Driver"
 
-/* Siemens ID Mouse */
-#define USB_IDMOUSE_VENDOR_ID  0x0681
-#define USB_IDMOUSE_PRODUCT_ID 0x0005
-
-/* we still need a minor number */
+/* minor number for misc USB devices */
 #define USB_IDMOUSE_MINOR_BASE 132
 
+/* vendor and device IDs */
+#define ID_SIEMENS 0x0681
+#define ID_IDMOUSE 0x0005
+#define ID_CHERRY  0x0010
+
+/* device ID table */
 static struct usb_device_id idmouse_table[] = {
-       {USB_DEVICE(USB_IDMOUSE_VENDOR_ID, USB_IDMOUSE_PRODUCT_ID)},
-       {} /* null entry at the end */
+       {USB_DEVICE(ID_SIEMENS, ID_IDMOUSE)}, /* Siemens ID Mouse (Professional) */
+       {USB_DEVICE(ID_SIEMENS, ID_CHERRY )}, /* Cherry FingerTIP ID Board       */
+       {}                                    /* terminating null entry          */
 };
 
+/* sensor commands */
+#define FTIP_RESET   0x20
+#define FTIP_ACQUIRE 0x21
+#define FTIP_RELEASE 0x22
+#define FTIP_BLINK   0x23  /* LSB of value = blink pulse width */
+#define FTIP_SCROLL  0x24
+
+#define ftip_command(dev, command, value, index) \
+       usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), command, \
+       USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, value, index, NULL, 0, 1000)
+
 MODULE_DEVICE_TABLE(usb, idmouse_table);
 
 /* structure to hold all of our device specific stuff */
@@ -57,7 +75,8 @@ struct usb_idmouse {
        struct usb_interface *interface; /* the interface for this device */
 
        unsigned char *bulk_in_buffer; /* the buffer to receive data */
-       size_t bulk_in_size; /* the size of the receive buffer */
+       size_t bulk_in_size; /* the maximum bulk packet size */
+       size_t orig_bi_size; /* same as above, but reported by the device */
        __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
 
        int open; /* if the port is open or not */
@@ -103,7 +122,7 @@ static struct usb_driver idmouse_driver = {
        .id_table = idmouse_table,
 };
 
-// prevent races between open() and disconnect()
+/* prevent races between open() and disconnect() */
 static DECLARE_MUTEX(disconnect_sem);
 
 static int idmouse_create_image(struct usb_idmouse *dev)
@@ -112,42 +131,34 @@ static int idmouse_create_image(struct usb_idmouse *dev)
        int bulk_read = 0;
        int result = 0;
 
-       if (dev->bulk_in_size < sizeof(HEADER))
-               return -ENOMEM;
-
-       memcpy(dev->bulk_in_buffer,HEADER,sizeof(HEADER)-1);
+       memcpy(dev->bulk_in_buffer, HEADER, sizeof(HEADER)-1);
        bytes_read += sizeof(HEADER)-1;
 
-       /* Dump the setup packets. Yes, they are uncommented, simply 
-          because they were sniffed under Windows using SnoopyPro.
-          I _guess_ that 0x22 is a kind of reset command and 0x21 
-          means init..
-       */
-       result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
-                               0x21, 0x42, 0x0001, 0x0002, NULL, 0, 1000);
-       if (result < 0)
-               return result;
-       result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
-                               0x20, 0x42, 0x0001, 0x0002, NULL, 0, 1000);
+       /* reset the device and set a fast blink rate */
+       result = ftip_command(dev, FTIP_RELEASE, 0, 0);
        if (result < 0)
-               return result;
-       result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
-                               0x22, 0x42, 0x0000, 0x0002, NULL, 0, 1000);
+               goto reset;
+       result = ftip_command(dev, FTIP_BLINK,   1, 0);
        if (result < 0)
-               return result;
+               goto reset;
 
-       result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
-                               0x21, 0x42, 0x0001, 0x0002, NULL, 0, 1000);
+       /* initialize the sensor - sending this command twice */
+       /* significantly reduces the rate of failed reads     */
+       result = ftip_command(dev, FTIP_ACQUIRE, 0, 0);
        if (result < 0)
-               return result;
-       result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
-                               0x20, 0x42, 0x0001, 0x0002, NULL, 0, 1000);
+               goto reset;
+       result = ftip_command(dev, FTIP_ACQUIRE, 0, 0);
        if (result < 0)
-               return result;
-       result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
-                               0x20, 0x42, 0x0000, 0x0002, NULL, 0, 1000);
+               goto reset;
+
+       /* start the readout - sending this command twice */
+       /* presumably enables the high dynamic range mode */
+       result = ftip_command(dev, FTIP_RESET,   0, 0);
        if (result < 0)
-               return result;
+               goto reset;
+       result = ftip_command(dev, FTIP_RESET,   0, 0);
+       if (result < 0)
+               goto reset;
 
        /* loop over a blocking bulk read to get data from the device */
        while (bytes_read < IMGSIZE) {
@@ -155,22 +166,40 @@ static int idmouse_create_image(struct usb_idmouse *dev)
                                usb_rcvbulkpipe (dev->udev, dev->bulk_in_endpointAddr),
                                dev->bulk_in_buffer + bytes_read,
                                dev->bulk_in_size, &bulk_read, 5000);
-               if (result < 0)
-                       return result;
-               if (signal_pending(current))
-                       return -EINTR;
+               if (result < 0) {
+                       /* Maybe this error was caused by the increased packet size? */
+                       /* Reset to the original value and tell userspace to retry.  */
+                       if (dev->bulk_in_size != dev->orig_bi_size) {
+                               dev->bulk_in_size = dev->orig_bi_size;
+                               result = -EAGAIN;
+                       }
+                       break;
+               }
+               if (signal_pending(current)) {
+                       result = -EINTR;
+                       break;
+               }
                bytes_read += bulk_read;
        }
 
        /* reset the device */
-       result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
-                               0x22, 0x42, 0x0000, 0x0002, NULL, 0, 1000);
-       if (result < 0)
-               return result;
+reset:
+       ftip_command(dev, FTIP_RELEASE, 0, 0);
+
+       /* check for valid image */
+       /* right border should be black (0x00) */
+       for (bytes_read = sizeof(HEADER)-1 + WIDTH-1; bytes_read < IMGSIZE; bytes_read += WIDTH)
+               if (dev->bulk_in_buffer[bytes_read] != 0x00)
+                       return -EAGAIN;
 
-       /* should be IMGSIZE == 64815 */
+       /* lower border should be white (0xFF) */
+       for (bytes_read = IMGSIZE-WIDTH; bytes_read < IMGSIZE-1; bytes_read++)
+               if (dev->bulk_in_buffer[bytes_read] != 0xFF)
+                       return -EAGAIN;
+
+       /* should be IMGSIZE == 65040 */
        dbg("read %d bytes fingerprint data", bytes_read);
-       return 0;
+       return result;
 }
 
 static inline void idmouse_delete(struct usb_idmouse *dev)
@@ -282,10 +311,10 @@ static ssize_t idmouse_read(struct file *file, char __user *buffer, size_t count
 
        dev = (struct usb_idmouse *) file->private_data;
 
-       // lock this object
+       /* lock this object */
        down (&dev->sem);
 
-       // verify that the device wasn't unplugged
+       /* verify that the device wasn't unplugged */
        if (!dev->present) {
                up (&dev->sem);
                return -ENODEV;
@@ -296,8 +325,7 @@ static ssize_t idmouse_read(struct file *file, char __user *buffer, size_t count
                return 0;
        }
 
-       if (count > IMGSIZE - *ppos)
-               count = IMGSIZE - *ppos;
+       count = min ((loff_t)count, IMGSIZE - (*ppos));
 
        if (copy_to_user (buffer, dev->bulk_in_buffer + *ppos, count)) {
                result = -EFAULT;
@@ -306,7 +334,7 @@ static ssize_t idmouse_read(struct file *file, char __user *buffer, size_t count
                *ppos += count;
        }
 
-       // unlock the device 
+       /* unlock the device */
        up(&dev->sem);
        return result;
 }
@@ -318,7 +346,6 @@ static int idmouse_probe(struct usb_interface *interface,
        struct usb_idmouse *dev = NULL;
        struct usb_host_interface *iface_desc;
        struct usb_endpoint_descriptor *endpoint;
-       size_t buffer_size;
        int result;
 
        /* check if we have gotten the data or the hid interface */
@@ -344,11 +371,11 @@ static int idmouse_probe(struct usb_interface *interface,
                USB_ENDPOINT_XFER_BULK)) {
 
                /* we found a bulk in endpoint */
-               buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
-               dev->bulk_in_size = buffer_size;
+               dev->orig_bi_size = le16_to_cpu(endpoint->wMaxPacketSize);
+               dev->bulk_in_size = 0x200; /* works _much_ faster */
                dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
                dev->bulk_in_buffer =
-                       kmalloc(IMGSIZE + buffer_size, GFP_KERNEL);
+                       kmalloc(IMGSIZE + dev->bulk_in_size, GFP_KERNEL);
 
                if (!dev->bulk_in_buffer) {
                        err("Unable to allocate input buffer.");
index 3104f28..cda7249 100644 (file)
@@ -461,7 +461,7 @@ static int perform_sglist (
 
 static unsigned realworld = 1;
 module_param (realworld, uint, 0);
-MODULE_PARM_DESC (realworld, "clear to demand stricter ch9 compliance");
+MODULE_PARM_DESC (realworld, "clear to demand stricter spec compliance");
 
 static int get_altsetting (struct usbtest_dev *dev)
 {
@@ -604,9 +604,8 @@ static int ch9_postconfig (struct usbtest_dev *dev)
                                USB_DIR_IN | USB_RECIP_DEVICE,
                                0, 0, dev->buf, 1, USB_CTRL_GET_TIMEOUT);
                if (retval != 1 || dev->buf [0] != expected) {
-                       dev_dbg (&iface->dev,
-                               "get config --> %d (%d)\n", retval,
-                               expected);
+                       dev_dbg (&iface->dev, "get config --> %d %d (1 %d)\n",
+                               retval, dev->buf[0], expected);
                        return (retval < 0) ? retval : -EDOM;
                }
        }
@@ -1243,7 +1242,7 @@ static int ctrl_out (struct usbtest_dev *dev,
        char                    *what = "?";
        struct usb_device       *udev;
        
-       if (length > 0xffff || vary >= length)
+       if (length < 1 || length > 0xffff || vary >= length)
                return -EINVAL;
 
        buf = kmalloc(length, SLAB_KERNEL);
@@ -1266,6 +1265,11 @@ static int ctrl_out (struct usbtest_dev *dev,
                                0, 0, buf, len, USB_CTRL_SET_TIMEOUT);
                if (retval != len) {
                        what = "write";
+                       if (retval >= 0) {
+                               INFO(dev, "ctrl_out, wlen %d (expected %d)\n",
+                                               retval, len);
+                               retval = -EBADMSG;
+                       }
                        break;
                }
 
@@ -1275,6 +1279,11 @@ static int ctrl_out (struct usbtest_dev *dev,
                                0, 0, buf, len, USB_CTRL_GET_TIMEOUT);
                if (retval != len) {
                        what = "read";
+                       if (retval >= 0) {
+                               INFO(dev, "ctrl_out, rlen %d (expected %d)\n",
+                                               retval, len);
+                               retval = -EBADMSG;
+                       }
                        break;
                }
 
@@ -1293,8 +1302,13 @@ static int ctrl_out (struct usbtest_dev *dev,
                }
 
                len += vary;
+
+               /* [real world] the "zero bytes IN" case isn't really used.
+                * hardware can easily trip up in this wierd case, since its
+                * status stage is IN, not OUT like other ep0in transfers.
+                */
                if (len > length)
-                       len = 0;
+                       len = realworld ? 1 : 0;
        }
 
        if (retval < 0)
@@ -1519,6 +1533,11 @@ usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf)
        if (down_interruptible (&dev->sem))
                return -ERESTARTSYS;
 
+       if (intf->dev.power.power_state != PMSG_ON) {
+               up (&dev->sem);
+               return -EHOSTUNREACH;
+       }
+
        /* some devices, like ez-usb default devices, need a non-default
         * altsetting to have any active endpoints.  some tests change
         * altsettings; force a default so most tests don't need to check.
@@ -1762,8 +1781,10 @@ usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf)
        case 14:
                if (!dev->info->ctrl_out)
                        break;
-               dev_dbg (&intf->dev, "TEST 14:  %d ep0out, 0..%d vary %d\n",
-                               param->iterations, param->length, param->vary);
+               dev_dbg (&intf->dev, "TEST 14:  %d ep0out, %d..%d vary %d\n",
+                               param->iterations,
+                               realworld ? 1 : 0, param->length,
+                               param->vary);
                retval = ctrl_out (dev, param->iterations, 
                                param->length, param->vary);
                break;
@@ -1927,6 +1948,27 @@ usbtest_probe (struct usb_interface *intf, const struct usb_device_id *id)
        return 0;
 }
 
+static int usbtest_suspend (struct usb_interface *intf, pm_message_t message)
+{
+       struct usbtest_dev      *dev = usb_get_intfdata (intf);
+
+       down (&dev->sem);
+       intf->dev.power.power_state = PMSG_SUSPEND;
+       up (&dev->sem);
+       return 0;
+}
+
+static int usbtest_resume (struct usb_interface *intf)
+{
+       struct usbtest_dev      *dev = usb_get_intfdata (intf);
+
+       down (&dev->sem);
+       intf->dev.power.power_state = PMSG_ON;
+       up (&dev->sem);
+       return 0;
+}
+
+
 static void usbtest_disconnect (struct usb_interface *intf)
 {
        struct usbtest_dev      *dev = usb_get_intfdata (intf);
@@ -2115,6 +2157,8 @@ static struct usb_driver usbtest_driver = {
        .probe =        usbtest_probe,
        .ioctl =        usbtest_ioctl,
        .disconnect =   usbtest_disconnect,
+       .suspend =      usbtest_suspend,
+       .resume =       usbtest_resume,
 };
 
 /*-------------------------------------------------------------------------*/
index 4cbb408..8a945f4 100644 (file)
@@ -1429,7 +1429,7 @@ static int generic_cdc_bind (struct usbnet *dev, struct usb_interface *intf)
                        info->ether = (void *) buf;
                        if (info->ether->bLength != sizeof *info->ether) {
                                dev_dbg (&intf->dev, "CDC ether len %u\n",
-                                       info->u->bLength);
+                                       info->ether->bLength);
                                goto bad_desc;
                        }
                        dev->net->mtu = le16_to_cpup (
index 341ae5f..3b387b0 100644 (file)
@@ -1884,12 +1884,53 @@ static void zd1201_disconnect(struct usb_interface *interface)
        kfree(zd);
 }
 
+#ifdef CONFIG_PM
+
+static int zd1201_suspend(struct usb_interface *interface,
+                          pm_message_t message)
+{
+       struct zd1201 *zd = usb_get_intfdata(interface);
+
+       netif_device_detach(zd->dev);
+
+       zd->was_enabled = zd->mac_enabled;
+
+       if (zd->was_enabled)
+               return zd1201_disable(zd);
+       else
+               return 0;
+}
+
+static int zd1201_resume(struct usb_interface *interface)
+{
+       struct zd1201 *zd = usb_get_intfdata(interface);
+
+       if (!zd || !zd->dev)
+               return -ENODEV;
+
+       netif_device_attach(zd->dev);
+
+       if (zd->was_enabled)
+               return zd1201_enable(zd);
+       else
+               return 0;
+}
+
+#else
+
+#define zd1201_suspend NULL
+#define zd1201_resume  NULL
+
+#endif
+
 static struct usb_driver zd1201_usb = {
        .owner = THIS_MODULE,
        .name = "zd1201",
        .probe = zd1201_probe,
        .disconnect = zd1201_disconnect,
        .id_table = zd1201_table,
+       .suspend = zd1201_suspend,
+       .resume = zd1201_resume,
 };
 
 static int __init zd1201_init(void)
index 1627c71..235f0ee 100644 (file)
@@ -46,6 +46,7 @@ struct zd1201 {
        char                    essid[IW_ESSID_MAX_SIZE+1];
        int                     essidlen;
        int                     mac_enabled;
+       int                     was_enabled;
        int                     monitor;
        int                     encode_enabled;
        int                     encode_restricted;
index 46a204c..b5b4310 100644 (file)
@@ -213,10 +213,14 @@ static int cyberjack_write (struct usb_serial_port *port, const unsigned char *b
                return (0);
        }
 
-       if (port->write_urb->status == -EINPROGRESS) {
+       spin_lock(&port->lock);
+       if (port->write_urb_busy) {
+               spin_unlock(&port->lock);
                dbg("%s - already writing", __FUNCTION__);
-               return (0);
+               return 0;
        }
+       port->write_urb_busy = 1;
+       spin_unlock(&port->lock);
 
        spin_lock_irqsave(&priv->lock, flags);
 
@@ -224,6 +228,7 @@ static int cyberjack_write (struct usb_serial_port *port, const unsigned char *b
                /* To much data for buffer. Reset buffer. */
                priv->wrfilled=0;
                spin_unlock_irqrestore(&priv->lock, flags);
+               port->write_urb_busy = 0;
                return (0);
        }
 
@@ -268,6 +273,7 @@ static int cyberjack_write (struct usb_serial_port *port, const unsigned char *b
                        priv->wrfilled=0;
                        priv->wrsent=0;
                        spin_unlock_irqrestore(&priv->lock, flags);
+                       port->write_urb_busy = 0;
                        return 0;
                }
 
@@ -412,7 +418,8 @@ static void cyberjack_write_bulk_callback (struct urb *urb, struct pt_regs *regs
        struct cyberjack_private *priv = usb_get_serial_port_data(port);
 
        dbg("%s - port %d", __FUNCTION__, port->number);
-       
+
+       port->write_urb_busy = 0;
        if (urb->status) {
                dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
                return;
@@ -424,12 +431,6 @@ static void cyberjack_write_bulk_callback (struct urb *urb, struct pt_regs *regs
        if( priv->wrfilled ) {
                int length, blksize, result;
 
-               if (port->write_urb->status == -EINPROGRESS) {
-                       dbg("%s - already writing", __FUNCTION__);
-                       spin_unlock(&priv->lock);
-                       return;
-               }
-
                dbg("%s - transmitting data (frame n)", __FUNCTION__);
 
                length = ((priv->wrfilled - priv->wrsent) > port->bulk_out_size) ?
index 99214aa..ddde5fb 100644 (file)
@@ -174,10 +174,14 @@ int usb_serial_generic_write(struct usb_serial_port *port, const unsigned char *
 
        /* only do something if we have a bulk out endpoint */
        if (serial->num_bulk_out) {
-               if (port->write_urb->status == -EINPROGRESS) {
+               spin_lock(&port->lock);
+               if (port->write_urb_busy) {
+                       spin_unlock(&port->lock);
                        dbg("%s - already writing", __FUNCTION__);
-                       return (0);
+                       return 0;
                }
+               port->write_urb_busy = 1;
+               spin_unlock(&port->lock);
 
                count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
 
@@ -195,17 +199,20 @@ int usb_serial_generic_write(struct usb_serial_port *port, const unsigned char *
                                     usb_serial_generic_write_bulk_callback), port);
 
                /* send the data out the bulk port */
+               port->write_urb_busy = 1;
                result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
-               if (result)
+               if (result) {
                        dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result);
-               else
+                       /* don't have to grab the lock here, as we will retry if != 0 */
+                       port->write_urb_busy = 0;
+               } else
                        result = count;
 
                return result;
        }
 
        /* no bulk out, so return 0 bytes written */
-       return (0);
+       return 0;
 }
 
 int usb_serial_generic_write_room (struct usb_serial_port *port)
@@ -214,9 +221,9 @@ int usb_serial_generic_write_room (struct usb_serial_port *port)
        int room = 0;
 
        dbg("%s - port %d", __FUNCTION__, port->number);
-       
+
        if (serial->num_bulk_out) {
-               if (port->write_urb->status != -EINPROGRESS)
+               if (port->write_urb_busy)
                        room = port->bulk_out_size;
        }
 
@@ -232,7 +239,7 @@ int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port)
        dbg("%s - port %d", __FUNCTION__, port->number);
 
        if (serial->num_bulk_out) {
-               if (port->write_urb->status == -EINPROGRESS)
+               if (port->write_urb_busy)
                        chars = port->write_urb->transfer_buffer_length;
        }
 
@@ -291,6 +298,7 @@ void usb_serial_generic_write_bulk_callback (struct urb *urb, struct pt_regs *re
 
        dbg("%s - port %d", __FUNCTION__, port->number);
 
+       port->write_urb_busy = 0;
        if (urb->status) {
                dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
                return;
index 3bd69c4..c05c2a2 100644 (file)
@@ -818,11 +818,6 @@ static void ipaq_write_gather(struct usb_serial_port *port)
        struct ipaq_packet      *pkt, *tmp;
        struct urb              *urb = port->write_urb;
 
-       if (urb->status == -EINPROGRESS) {
-               /* Should never happen */
-               err("%s - flushing while urb is active !", __FUNCTION__);
-               return;
-       }
        room = URBDATA_SIZE;
        list_for_each_entry_safe(pkt, tmp, &priv->queue, list) {
                count = min(room, (int)(pkt->len - pkt->written));
index 11105d7..85e2424 100644 (file)
@@ -399,16 +399,21 @@ static int ipw_write(struct usb_serial_port *port, const unsigned char *buf, int
                dbg("%s - write request of 0 bytes", __FUNCTION__);
                return 0;
        }
-       
-       /* Racy and broken, FIXME properly! */
-       if (port->write_urb->status == -EINPROGRESS)
+
+       spin_lock(&port->lock);
+       if (port->write_urb_busy) {
+               spin_unlock(&port->lock);
+               dbg("%s - already writing", __FUNCTION__);
                return 0;
+       }
+       port->write_urb_busy = 1;
+       spin_unlock(&port->lock);
 
        count = min(count, port->bulk_out_size);
        memcpy(port->bulk_out_buffer, buf, count);
 
        dbg("%s count now:%d", __FUNCTION__, count);
-       
+
        usb_fill_bulk_urb(port->write_urb, dev,
                          usb_sndbulkpipe(dev, port->bulk_out_endpointAddress),
                          port->write_urb->transfer_buffer,
@@ -418,6 +423,7 @@ static int ipw_write(struct usb_serial_port *port, const unsigned char *buf, int
 
        ret = usb_submit_urb(port->write_urb, GFP_ATOMIC);
        if (ret != 0) {
+               port->write_urb_busy = 0;
                dbg("%s - usb_submit_urb(write bulk) failed with error = %d", __FUNCTION__, ret);
                return ret;
        }
index 59f234d..937b2fd 100644 (file)
@@ -341,10 +341,14 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int
        if (count == 0)
                return 0;
 
-       if (port->write_urb->status == -EINPROGRESS) {
-               dbg ("%s - already writing", __FUNCTION__);
+       spin_lock(&port->lock);
+       if (port->write_urb_busy) {
+               spin_unlock(&port->lock);
+               dbg("%s - already writing", __FUNCTION__);
                return 0;
        }
+       port->write_urb_busy = 1;
+       spin_unlock(&port->lock);
 
        transfer_buffer = port->write_urb->transfer_buffer;
        transfer_size = min(count, port->bulk_out_size - 1);
@@ -374,9 +378,10 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int
        port->write_urb->transfer_flags = URB_ZERO_PACKET;
 
        result = usb_submit_urb (port->write_urb, GFP_ATOMIC);
-       if (result)
+       if (result) {
+               port->write_urb_busy = 0;
                dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result);
-       else
+       else
                result = transfer_size;
 
        return result;
@@ -387,7 +392,8 @@ static void ir_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
        struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
 
        dbg("%s - port %d", __FUNCTION__, port->number);
-       
+
+       port->write_urb_busy = 0;
        if (urb->status) {
                dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
                return;
index 7fd0aa9..635c384 100644 (file)
@@ -520,9 +520,13 @@ static int keyspan_pda_write(struct usb_serial_port *port,
           the TX urb is in-flight (wait until it completes)
           the device is full (wait until it says there is room)
        */
-       if (port->write_urb->status == -EINPROGRESS || priv->tx_throttled ) {
-               return( 0 );
+       spin_lock(&port->lock);
+       if (port->write_urb_busy || priv->tx_throttled) {
+               spin_unlock(&port->lock);
+               return 0;
        }
+       port->write_urb_busy = 1;
+       spin_unlock(&port->lock);
 
        /* At this point the URB is in our control, nobody else can submit it
           again (the only sudden transition was the one from EINPROGRESS to
@@ -570,7 +574,7 @@ static int keyspan_pda_write(struct usb_serial_port *port,
                memcpy (port->write_urb->transfer_buffer, buf, count);
                /* send the data out the bulk port */
                port->write_urb->transfer_buffer_length = count;
-               
+
                priv->tx_room -= count;
 
                port->write_urb->dev = port->serial->dev;
@@ -593,6 +597,8 @@ static int keyspan_pda_write(struct usb_serial_port *port,
 
        rc = count;
 exit:
+       if (rc < 0)
+               port->write_urb_busy = 0;
        return rc;
 }
 
@@ -602,6 +608,7 @@ static void keyspan_pda_write_bulk_callback (struct urb *urb, struct pt_regs *re
        struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
        struct keyspan_pda_private *priv;
 
+       port->write_urb_busy = 0;
        priv = usb_get_serial_port_data(port);
 
        /* queue up a wakeup at scheduler time */
@@ -626,12 +633,12 @@ static int keyspan_pda_write_room (struct usb_serial_port *port)
 static int keyspan_pda_chars_in_buffer (struct usb_serial_port *port)
 {
        struct keyspan_pda_private *priv;
-       
+
        priv = usb_get_serial_port_data(port);
-       
+
        /* when throttled, return at least WAKEUP_CHARS to tell select() (via
           n_tty.c:normal_poll() ) that we're not writeable. */
-       if( port->write_urb->status == -EINPROGRESS || priv->tx_throttled )
+       if (port->write_urb_busy || priv->tx_throttled)
                return 256;
        return 0;
 }
index b5f2c06..6a99ae1 100644 (file)
@@ -254,10 +254,15 @@ static int omninet_write (struct usb_serial_port *port, const unsigned char *buf
                dbg("%s - write request of 0 bytes", __FUNCTION__);
                return (0);
        }
-       if (wport->write_urb->status == -EINPROGRESS) {
+
+       spin_lock(&port->lock);
+       if (port->write_urb_busy) {
+               spin_unlock(&port->lock);
                dbg("%s - already writing", __FUNCTION__);
-               return (0);
+               return 0;
        }
+       port->write_urb_busy = 1;
+       spin_unlock(&port->lock);
 
        count = (count > OMNINET_BULKOUTSIZE) ? OMNINET_BULKOUTSIZE : count;
 
@@ -275,9 +280,10 @@ static int omninet_write (struct usb_serial_port *port, const unsigned char *buf
 
        wport->write_urb->dev = serial->dev;
        result = usb_submit_urb(wport->write_urb, GFP_ATOMIC);
-       if (result)
+       if (result) {
+               port->write_urb_busy = 0;
                err("%s - failed submitting write urb, error %d", __FUNCTION__, result);
-       else
+       else
                result = count;
 
        return result;
@@ -291,7 +297,7 @@ static int omninet_write_room (struct usb_serial_port *port)
 
        int room = 0; // Default: no room
 
-       if (wport->write_urb->status != -EINPROGRESS)
+       if (wport->write_urb_busy)
                room = wport->bulk_out_size - OMNINET_HEADERLEN;
 
 //     dbg("omninet_write_room returns %d", room);
@@ -306,6 +312,7 @@ static void omninet_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
 
 //     dbg("omninet_write_bulk_callback, port %0x\n", port);
 
+       port->write_urb_busy = 0;
        if (urb->status) {
                dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
                return;
index 0e85ed6..96a1756 100644 (file)
@@ -299,10 +299,14 @@ static int safe_write (struct usb_serial_port *port, const unsigned char *buf, i
                dbg ("%s - write request of 0 bytes", __FUNCTION__);
                return (0);
        }
-       if (port->write_urb->status == -EINPROGRESS) {
-               dbg ("%s - already writing", __FUNCTION__);
-               return (0);
+       spin_lock(&port->lock);
+       if (port->write_urb_busy) {
+               spin_unlock(&port->lock);
+               dbg("%s - already writing", __FUNCTION__);
+               return 0;
        }
+       port->write_urb_busy = 1;
+       spin_unlock(&port->lock);
 
        packet_length = port->bulk_out_size;    // get max packetsize
 
@@ -354,6 +358,7 @@ static int safe_write (struct usb_serial_port *port, const unsigned char *buf, i
 #endif
        port->write_urb->dev = port->serial->dev;
        if ((result = usb_submit_urb (port->write_urb, GFP_KERNEL))) {
+               port->write_urb_busy = 0;
                err ("%s - failed submitting write urb, error %d", __FUNCTION__, result);
                return 0;
        }
@@ -368,7 +373,7 @@ static int safe_write_room (struct usb_serial_port *port)
 
        dbg ("%s", __FUNCTION__);
 
-       if (port->write_urb->status != -EINPROGRESS)
+       if (port->write_urb_busy)
                room = port->bulk_out_size - (safe ? 2 : 0);
 
        if (room) {
index 5da76dd..0267b26 100644 (file)
@@ -1047,6 +1047,7 @@ int usb_serial_probe(struct usb_interface *interface,
                memset(port, 0x00, sizeof(struct usb_serial_port));
                port->number = i + serial->minor;
                port->serial = serial;
+               spin_lock_init(&port->lock);
                INIT_WORK(&port->work, usb_serial_port_softint, port);
                serial->port[i] = port;
        }
index d1f0c40..57f92f0 100644 (file)
@@ -69,6 +69,7 @@
  * usb_serial_port: structure for the specific ports of a device.
  * @serial: pointer back to the struct usb_serial owner of this port.
  * @tty: pointer to the corresponding tty for this port.
+ * @lock: spinlock to grab when updating portions of this structure.
  * @number: the number of the port (the minor number).
  * @interrupt_in_buffer: pointer to the interrupt in buffer for this port.
  * @interrupt_in_urb: pointer to the interrupt in struct urb for this port.
@@ -98,6 +99,7 @@
 struct usb_serial_port {
        struct usb_serial *     serial;
        struct tty_struct *     tty;
+       spinlock_t              lock;
        unsigned char           number;
 
        unsigned char *         interrupt_in_buffer;
@@ -117,6 +119,7 @@ struct usb_serial_port {
        unsigned char *         bulk_out_buffer;
        int                     bulk_out_size;
        struct urb *            write_urb;
+       int                     write_urb_busy;
        __u8                    bulk_out_endpointAddress;
 
        wait_queue_head_t       write_wait;
index e43eddc..af294bb 100644 (file)
@@ -155,6 +155,15 @@ static int slave_configure(struct scsi_device *sdev)
                 * If this device makes that mistake, tell the sd driver. */
                if (us->flags & US_FL_FIX_CAPACITY)
                        sdev->fix_capacity = 1;
+
+               /* USB-IDE bridges tend to report SK = 0x04 (Non-recoverable
+                * Hardware Error) when any low-level error occurs,
+                * recoverable or not.  Setting this flag tells the SCSI
+                * midlayer to retry such commands, which frequently will
+                * succeed and fix the error.  The worst this can lead to
+                * is an occasional series of retries that will all fail. */
+               sdev->retry_hwerror = 1;
+
        } else {
 
                /* Non-disk-type devices don't need to blacklist any pages
@@ -255,50 +264,23 @@ static int device_reset(struct scsi_cmnd *srb)
 
        /* lock the device pointers and do the reset */
        down(&(us->dev_semaphore));
-       if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
-               result = FAILED;
-               US_DEBUGP("No reset during disconnect\n");
-       } else
-               result = us->transport_reset(us);
+       result = us->transport_reset(us);
        up(&(us->dev_semaphore));
 
-       return result;
+       return result < 0 ? FAILED : SUCCESS;
 }
 
-/* This resets the device's USB port. */
-/* It refuses to work if there's more than one interface in
- * the device, so that other users are not affected. */
+/* Simulate a SCSI bus reset by resetting the device's USB port. */
 /* This is always called with scsi_lock(host) held */
 static int bus_reset(struct scsi_cmnd *srb)
 {
        struct us_data *us = host_to_us(srb->device->host);
-       int result, rc;
+       int result;
 
        US_DEBUGP("%s called\n", __FUNCTION__);
 
-       /* The USB subsystem doesn't handle synchronisation between
-        * a device's several drivers. Therefore we reset only devices
-        * with just one interface, which we of course own. */
-
        down(&(us->dev_semaphore));
-       if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
-               result = -EIO;
-               US_DEBUGP("No reset during disconnect\n");
-       } else if (us->pusb_dev->actconfig->desc.bNumInterfaces != 1) {
-               result = -EBUSY;
-               US_DEBUGP("Refusing to reset a multi-interface device\n");
-       } else {
-               rc = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
-               if (rc < 0) {
-                       US_DEBUGP("unable to lock device for reset: %d\n", rc);
-                       result = rc;
-               } else {
-                       result = usb_reset_device(us->pusb_dev);
-                       if (rc)
-                               usb_unlock_device(us->pusb_dev);
-                       US_DEBUGP("usb_reset_device returns %d\n", result);
-               }
-       }
+       result = usb_stor_port_reset(us);
        up(&(us->dev_semaphore));
 
        /* lock the host for the return */
@@ -320,6 +302,14 @@ void usb_stor_report_device_reset(struct us_data *us)
        }
 }
 
+/* Report a driver-initiated bus reset to the SCSI layer.
+ * Calling this for a SCSI-initiated reset is unnecessary but harmless.
+ * The caller must own the SCSI host lock. */
+void usb_stor_report_bus_reset(struct us_data *us)
+{
+       scsi_report_bus_reset(us_to_host(us), 0);
+}
+
 /***********************************************************************
  * /proc/scsi/ functions
  ***********************************************************************/
index d0a49af..737e4fa 100644 (file)
@@ -42,6 +42,7 @@
 #define _SCSIGLUE_H_
 
 extern void usb_stor_report_device_reset(struct us_data *us);
+extern void usb_stor_report_bus_reset(struct us_data *us);
 
 extern unsigned char usb_stor_sense_invalidCDB[18];
 extern struct scsi_host_template usb_stor_host_template;
index 9743e28..e6b1c6c 100644 (file)
@@ -266,8 +266,9 @@ int usb_stor_clear_halt(struct us_data *us, unsigned int pipe)
                NULL, 0, 3*HZ);
 
        /* reset the endpoint toggle */
-       usb_settoggle(us->pusb_dev, usb_pipeendpoint(pipe),
-               usb_pipeout(pipe), 0);
+       if (result >= 0)
+               usb_settoggle(us->pusb_dev, usb_pipeendpoint(pipe),
+                               usb_pipeout(pipe), 0);
 
        US_DEBUGP("%s: result = %d\n", __FUNCTION__, result);
        return result;
@@ -540,15 +541,15 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
         */
        if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) {
                US_DEBUGP("-- command was aborted\n");
-               goto Handle_Abort;
+               srb->result = DID_ABORT << 16;
+               goto Handle_Errors;
        }
 
        /* if there is a transport error, reset and don't auto-sense */
        if (result == USB_STOR_TRANSPORT_ERROR) {
                US_DEBUGP("-- transport indicates error, resetting\n");
-               us->transport_reset(us);
                srb->result = DID_ERROR << 16;
-               return;
+               goto Handle_Errors;
        }
 
        /* if the transport provided its own sense data, don't auto-sense */
@@ -668,7 +669,8 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
 
                if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) {
                        US_DEBUGP("-- auto-sense aborted\n");
-                       goto Handle_Abort;
+                       srb->result = DID_ABORT << 16;
+                       goto Handle_Errors;
                }
                if (temp_result != USB_STOR_TRANSPORT_GOOD) {
                        US_DEBUGP("-- auto-sense failure\n");
@@ -677,9 +679,9 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
                         * multi-target device, since failure of an
                         * auto-sense is perfectly valid
                         */
-                       if (!(us->flags & US_FL_SCM_MULT_TARG))
-                               us->transport_reset(us);
                        srb->result = DID_ERROR << 16;
+                       if (!(us->flags & US_FL_SCM_MULT_TARG))
+                               goto Handle_Errors;
                        return;
                }
 
@@ -720,12 +722,28 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
 
        return;
 
-       /* abort processing: the bulk-only transport requires a reset
-        * following an abort */
-  Handle_Abort:
-       srb->result = DID_ABORT << 16;
-       if (us->protocol == US_PR_BULK)
+       /* Error and abort processing: try to resynchronize with the device
+        * by issuing a port reset.  If that fails, try a class-specific
+        * device reset. */
+  Handle_Errors:
+
+       /* Let the SCSI layer know we are doing a reset, set the
+        * RESETTING bit, and clear the ABORTING bit so that the reset
+        * may proceed. */
+       scsi_lock(us_to_host(us));
+       usb_stor_report_bus_reset(us);
+       set_bit(US_FLIDX_RESETTING, &us->flags);
+       clear_bit(US_FLIDX_ABORTING, &us->flags);
+       scsi_unlock(us_to_host(us));
+
+       result = usb_stor_port_reset(us);
+       if (result < 0) {
+               scsi_lock(us_to_host(us));
+               usb_stor_report_device_reset(us);
+               scsi_unlock(us_to_host(us));
                us->transport_reset(us);
+       }
+       clear_bit(US_FLIDX_RESETTING, &us->flags);
 }
 
 /* Stop the current URB transfer */
@@ -1124,7 +1142,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
  * It's handy that every transport mechanism uses the control endpoint for
  * resets.
  *
- * Basically, we send a reset with a 20-second timeout, so we don't get
+ * Basically, we send a reset with a 5-second timeout, so we don't get
  * jammed attempting to do the reset.
  */
 static int usb_stor_reset_common(struct us_data *us,
@@ -1133,28 +1151,18 @@ static int usb_stor_reset_common(struct us_data *us,
 {
        int result;
        int result2;
-       int rc = FAILED;
 
-       /* Let the SCSI layer know we are doing a reset, set the
-        * RESETTING bit, and clear the ABORTING bit so that the reset
-        * may proceed.
-        */
-       scsi_lock(us_to_host(us));
-       usb_stor_report_device_reset(us);
-       set_bit(US_FLIDX_RESETTING, &us->flags);
-       clear_bit(US_FLIDX_ABORTING, &us->flags);
-       scsi_unlock(us_to_host(us));
+       if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+               US_DEBUGP("No reset during disconnect\n");
+               return -EIO;
+       }
 
-       /* A 20-second timeout may seem rather long, but a LaCie
-        * StudioDrive USB2 device takes 16+ seconds to get going
-        * following a powerup or USB attach event.
-        */
        result = usb_stor_control_msg(us, us->send_ctrl_pipe,
                        request, requesttype, value, index, data, size,
-                       20*HZ);
+                       5*HZ);
        if (result < 0) {
                US_DEBUGP("Soft reset failed: %d\n", result);
-               goto Done;
+               return result;
        }
 
        /* Give the device some time to recover from the reset,
@@ -1164,7 +1172,7 @@ static int usb_stor_reset_common(struct us_data *us,
                        HZ*6);
        if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
                US_DEBUGP("Reset interrupted by disconnect\n");
-               goto Done;
+               return -EIO;
        }
 
        US_DEBUGP("Soft reset: clearing bulk-in endpoint halt\n");
@@ -1173,17 +1181,14 @@ static int usb_stor_reset_common(struct us_data *us,
        US_DEBUGP("Soft reset: clearing bulk-out endpoint halt\n");
        result2 = usb_stor_clear_halt(us, us->send_bulk_pipe);
 
-       /* return a result code based on the result of the control message */
-       if (result < 0 || result2 < 0) {
+       /* return a result code based on the result of the clear-halts */
+       if (result >= 0)
+               result = result2;
+       if (result < 0)
                US_DEBUGP("Soft reset failed\n");
-               goto Done;
-       }
-       US_DEBUGP("Soft reset done\n");
-       rc = SUCCESS;
-
-  Done:
-       clear_bit(US_FLIDX_RESETTING, &us->flags);
-       return rc;
+       else
+               US_DEBUGP("Soft reset done\n");
+       return result;
 }
 
 /* This issues a CB[I] Reset to the device in question
@@ -1213,3 +1218,32 @@ int usb_stor_Bulk_reset(struct us_data *us)
                                 USB_TYPE_CLASS | USB_RECIP_INTERFACE,
                                 0, us->ifnum, NULL, 0);
 }
+
+/* Issue a USB port reset to the device.  But don't do anything if
+ * there's more than one interface in the device, so that other users
+ * are not affected. */
+int usb_stor_port_reset(struct us_data *us)
+{
+       int result, rc;
+
+       if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+               result = -EIO;
+               US_DEBUGP("No reset during disconnect\n");
+       } else if (us->pusb_dev->actconfig->desc.bNumInterfaces != 1) {
+               result = -EBUSY;
+               US_DEBUGP("Refusing to reset a multi-interface device\n");
+       } else {
+               result = rc =
+                       usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
+               if (result < 0) {
+                       US_DEBUGP("unable to lock device for reset: %d\n",
+                                       result);
+               } else {
+                       result = usb_reset_device(us->pusb_dev);
+                       if (rc)
+                               usb_unlock_device(us->pusb_dev);
+                       US_DEBUGP("usb_reset_device returns %d\n", result);
+               }
+       }
+       return result;
+}
index e25f8d8..8d9e066 100644 (file)
@@ -171,4 +171,5 @@ extern int usb_stor_bulk_transfer_buf(struct us_data *us, unsigned int pipe,
 extern int usb_stor_bulk_transfer_sg(struct us_data *us, unsigned int pipe,
                void *buf, unsigned int length, int use_sg, int *residual);
 
+extern int usb_stor_port_reset(struct us_data *us);
 #endif
index 1438c6c..054fb9a 100644 (file)
 #      define   HMC_TLLATTACH          (1 << 6)
 #      define   OTG_HMC(w)             (((w)>>0)&0x3f)
 #define OTG_CTRL_REG                   OTG_REG32(0x0c)
+#      define   OTG_USB2_EN            (1 << 29)
+#      define   OTG_USB2_DP            (1 << 28)
+#      define   OTG_USB2_DM            (1 << 27)
+#      define   OTG_USB1_EN            (1 << 26)
+#      define   OTG_USB1_DP            (1 << 25)
+#      define   OTG_USB1_DM            (1 << 24)
+#      define   OTG_USB0_EN            (1 << 23)
+#      define   OTG_USB0_DP            (1 << 22)
+#      define   OTG_USB0_DM            (1 << 21)
 #      define   OTG_ASESSVLD           (1 << 20)
 #      define   OTG_BSESSEND           (1 << 19)
 #      define   OTG_BSESSVLD           (1 << 18)
index f5fe94e..39e7ff4 100644 (file)
@@ -6,11 +6,14 @@
  *
  * - the master/host side Linux-USB kernel driver API;
  * - the "usbfs" user space API; and
- * - (eventually) a Linux "gadget" slave/device side driver API.
+ * - the Linux "gadget" slave/device/peripheral side driver API.
  *
  * USB 2.0 adds an additional "On The Go" (OTG) mode, which lets systems
  * act either as a USB master/host or as a USB slave/device.  That means
- * the master and slave side APIs will benefit from working well together.
+ * the master and slave side APIs benefit from working well together.
+ *
+ * There's also "Wireless USB", using low power short range radios for
+ * peripheral interconnection but otherwise building on the USB framework.
  */
 
 #ifndef __LINUX_USB_CH9_H
 #define USB_REQ_SET_INTERFACE          0x0B
 #define USB_REQ_SYNCH_FRAME            0x0C
 
+#define USB_REQ_SET_ENCRYPTION         0x0D    /* Wireless USB */
+#define USB_REQ_GET_ENCRYPTION         0x0E
+#define USB_REQ_SET_HANDSHAKE          0x0F
+#define USB_REQ_GET_HANDSHAKE          0x10
+#define USB_REQ_SET_CONNECTION         0x11
+#define USB_REQ_SET_SECURITY_DATA      0x12
+#define USB_REQ_GET_SECURITY_DATA      0x13
+#define USB_REQ_SET_WUSB_DATA          0x14
+#define USB_REQ_LOOPBACK_DATA_WRITE    0x15
+#define USB_REQ_LOOPBACK_DATA_READ     0x16
+#define USB_REQ_SET_INTERFACE_DS       0x17
+
 /*
  * USB feature flags are written using USB_REQ_{CLEAR,SET}_FEATURE, and
  * are read as a bit array returned by USB_REQ_GET_STATUS.  (So there
  */
 #define USB_DEVICE_SELF_POWERED                0       /* (read only) */
 #define USB_DEVICE_REMOTE_WAKEUP       1       /* dev may initiate wakeup */
-#define USB_DEVICE_TEST_MODE           2       /* (high speed only) */
-#define USB_DEVICE_B_HNP_ENABLE                3       /* dev may initiate HNP */
-#define USB_DEVICE_A_HNP_SUPPORT       4       /* RH port supports HNP */
-#define USB_DEVICE_A_ALT_HNP_SUPPORT   5       /* other RH port does */
+#define USB_DEVICE_TEST_MODE           2       /* (wired high speed only) */
+#define USB_DEVICE_BATTERY             2       /* (wireless) */
+#define USB_DEVICE_B_HNP_ENABLE                3       /* (otg) dev may initiate HNP */
+#define USB_DEVICE_WUSB_DEVICE         3       /* (wireless)*/
+#define USB_DEVICE_A_HNP_SUPPORT       4       /* (otg) RH port supports HNP */
+#define USB_DEVICE_A_ALT_HNP_SUPPORT   5       /* (otg) other RH port does */
 #define USB_DEVICE_DEBUG_MODE          6       /* (special devices only) */
 
 #define USB_ENDPOINT_HALT              0       /* IN/OUT will STALL */
@@ -135,6 +152,13 @@ struct usb_ctrlrequest {
 #define USB_DT_OTG                     0x09
 #define USB_DT_DEBUG                   0x0a
 #define USB_DT_INTERFACE_ASSOCIATION   0x0b
+/* these are from the Wireless USB spec */
+#define USB_DT_SECURITY                        0x0c
+#define USB_DT_KEY                     0x0d
+#define USB_DT_ENCRYPTION_TYPE         0x0e
+#define USB_DT_BOS                     0x0f
+#define USB_DT_DEVICE_CAPABILITY       0x10
+#define USB_DT_WIRELESS_ENDPOINT_COMP  0x11
 
 /* conventional codes for class-specific descriptors */
 #define USB_DT_CS_DEVICE               0x21
@@ -192,6 +216,7 @@ struct usb_device_descriptor {
 #define USB_CLASS_CSCID                        0x0b    /* chip+ smart card */
 #define USB_CLASS_CONTENT_SEC          0x0d    /* content security */
 #define USB_CLASS_VIDEO                        0x0e
+#define USB_CLASS_WIRELESS_CONTROLLER  0xe0
 #define USB_CLASS_APP_SPEC             0xfe
 #define USB_CLASS_VENDOR_SPEC          0xff
 
@@ -223,6 +248,7 @@ struct usb_config_descriptor {
 #define USB_CONFIG_ATT_ONE             (1 << 7)        /* must be set */
 #define USB_CONFIG_ATT_SELFPOWER       (1 << 6)        /* self powered */
 #define USB_CONFIG_ATT_WAKEUP          (1 << 5)        /* can wakeup */
+#define USB_CONFIG_ATT_BATTERY         (1 << 4)        /* battery powered */
 
 /*-------------------------------------------------------------------------*/
 
@@ -289,6 +315,7 @@ struct usb_endpoint_descriptor {
 #define USB_ENDPOINT_XFER_ISOC         1
 #define USB_ENDPOINT_XFER_BULK         2
 #define USB_ENDPOINT_XFER_INT          3
+#define USB_ENDPOINT_MAX_ADJUSTABLE    0x80
 
 
 /*-------------------------------------------------------------------------*/
@@ -350,6 +377,147 @@ struct usb_interface_assoc_descriptor {
 } __attribute__ ((packed));
 
 
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_SECURITY:  group of wireless security descriptors, including
+ * encryption types available for setting up a CC/association.
+ */
+struct usb_security_descriptor {
+       __u8  bLength;
+       __u8  bDescriptorType;
+
+       __le16 wTotalLength;
+       __u8  bNumEncryptionTypes;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_KEY:  used with {GET,SET}_SECURITY_DATA; only public keys
+ * may be retrieved.
+ */
+struct usb_key_descriptor {
+       __u8  bLength;
+       __u8  bDescriptorType;
+
+       __u8  tTKID[3];
+       __u8  bReserved;
+       __u8  bKeyData[0];
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_ENCRYPTION_TYPE:  bundled in DT_SECURITY groups */
+struct usb_encryption_descriptor {
+       __u8  bLength;
+       __u8  bDescriptorType;
+
+       __u8  bEncryptionType;
+#define        USB_ENC_TYPE_UNSECURE           0
+#define        USB_ENC_TYPE_WIRED              1       /* non-wireless mode */
+#define        USB_ENC_TYPE_CCM_1              2       /* aes128/cbc session */
+#define        USB_ENC_TYPE_RSA_1              3       /* rsa3072/sha1 auth */
+       __u8  bEncryptionValue;         /* use in SET_ENCRYPTION */
+       __u8  bAuthKeyIndex;
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_BOS:  group of wireless capabilities */
+struct usb_bos_descriptor {
+       __u8  bLength;
+       __u8  bDescriptorType;
+
+       __le16 wTotalLength;
+       __u8  bNumDeviceCaps;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_DEVICE_CAPABILITY:  grouped with BOS */
+struct usb_dev_cap_header {
+       __u8  bLength;
+       __u8  bDescriptorType;
+       __u8  bDevCapabilityType;
+};
+
+#define        USB_CAP_TYPE_WIRELESS_USB       1
+
+struct usb_wireless_cap_descriptor {   /* Ultra Wide Band */
+       __u8  bLength;
+       __u8  bDescriptorType;
+       __u8  bDevCapabilityType;
+
+       __u8  bmAttributes;
+#define        USB_WIRELESS_P2P_DRD            (1 << 1)
+#define        USB_WIRELESS_BEACON_MASK        (3 << 2)
+#define        USB_WIRELESS_BEACON_SELF        (1 << 2)
+#define        USB_WIRELESS_BEACON_DIRECTED    (2 << 2)
+#define        USB_WIRELESS_BEACON_NONE        (3 << 2)
+       __le16 wPHYRates;       /* bit rates, Mbps */
+#define        USB_WIRELESS_PHY_53             (1 << 0)        /* always set */
+#define        USB_WIRELESS_PHY_80             (1 << 1)
+#define        USB_WIRELESS_PHY_107            (1 << 2)        /* always set */
+#define        USB_WIRELESS_PHY_160            (1 << 3)
+#define        USB_WIRELESS_PHY_200            (1 << 4)        /* always set */
+#define        USB_WIRELESS_PHY_320            (1 << 5)
+#define        USB_WIRELESS_PHY_400            (1 << 6)
+#define        USB_WIRELESS_PHY_480            (1 << 7)
+       __u8  bmTFITXPowerInfo; /* TFI power levels */
+       __u8  bmFFITXPowerInfo; /* FFI power levels */
+       __le16 bmBandGroup;
+       __u8  bReserved;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_WIRELESS_ENDPOINT_COMP:  companion descriptor associated with
+ * each endpoint descriptor for a wireless device
+ */
+struct usb_wireless_ep_comp_descriptor {
+       __u8  bLength;
+       __u8  bDescriptorType;
+
+       __u8  bMaxBurst;
+       __u8  bMaxSequence;
+       __le16 wMaxStreamDelay;
+       __le16 wOverTheAirPacketSize;
+       __u8  bOverTheAirInterval;
+       __u8  bmCompAttributes;
+#define USB_ENDPOINT_SWITCH_MASK       0x03    /* in bmCompAttributes */
+#define USB_ENDPOINT_SWITCH_NO         0
+#define USB_ENDPOINT_SWITCH_SWITCH     1
+#define USB_ENDPOINT_SWITCH_SCALE      2
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_REQ_SET_HANDSHAKE is a four-way handshake used between a wireless
+ * host and a device for connection set up, mutual authentication, and
+ * exchanging short lived session keys.  The handshake depends on a CC.
+ */
+struct usb_handshake {
+       __u8 bMessageNumber;
+       __u8 bStatus;
+       __u8 tTKID[3];
+       __u8 bReserved;
+       __u8 CDID[16];
+       __u8 nonce[16];
+       __u8 MIC[8];
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_REQ_SET_CONNECTION modifies or revokes a connection context (CC).
+ * A CC may also be set up using non-wireless secure channels (including
+ * wired USB!), and some devices may support CCs with multiple hosts.
+ */
+struct usb_connection_context {
+       __u8 CHID[16];          /* persistent host id */
+       __u8 CDID[16];          /* device id (unique w/in host context) */
+       __u8 CK[16];            /* connection key */
+};
+
 /*-------------------------------------------------------------------------*/
 
 /* USB 2.0 defines three speeds, here's how Linux identifies them */
@@ -357,7 +525,8 @@ struct usb_interface_assoc_descriptor {
 enum usb_device_speed {
        USB_SPEED_UNKNOWN = 0,                  /* enumerating */
        USB_SPEED_LOW, USB_SPEED_FULL,          /* usb 1.1 */
-       USB_SPEED_HIGH                          /* usb 2.0 */
+       USB_SPEED_HIGH,                         /* usb 2.0 */
+       USB_SPEED_VARIABLE,                     /* wireless (usb 2.5) */
 };
 
 enum usb_device_state {
index 9bba999..b00f127 100644 (file)
@@ -711,7 +711,7 @@ usb_gadget_disconnect (struct usb_gadget *gadget)
  *     the hardware level driver. Most calls must be handled by
  *     the gadget driver, including descriptor and configuration
  *     management.  The 16 bit members of the setup data are in
- *     cpu order. Called in_interrupt; this may not sleep.  Driver
+ *     USB byte order. Called in_interrupt; this may not sleep.  Driver
  *     queues a response to ep0, or returns negative to stall.
  * @disconnect: Invoked after all transfers have been stopped,
  *     when the host is disconnected.  May be called in_interrupt; this
diff --git a/include/linux/usb_isp116x.h b/include/linux/usb_isp116x.h
new file mode 100644 (file)
index 0000000..5f5a9d9
--- /dev/null
@@ -0,0 +1,47 @@
+
+/*
+ * Board initialization code should put one of these into dev->platform_data
+ * and place the isp116x onto platform_bus.
+ */
+
+struct isp116x_platform_data {
+       /* Enable internal resistors on downstream ports */
+       unsigned sel15Kres:1;
+       /* Chip's internal clock won't be stopped in suspended state.
+          Setting/unsetting this bit takes effect only if
+          'remote_wakeup_enable' below is not set. */
+       unsigned clknotstop:1;
+       /* On-chip overcurrent protection */
+       unsigned oc_enable:1;
+       /* INT output polarity */
+       unsigned int_act_high:1;
+       /* INT edge or level triggered */
+       unsigned int_edge_triggered:1;
+       /* WAKEUP pin connected - NOT SUPPORTED  */
+       /* unsigned remote_wakeup_connected:1; */
+       /* Wakeup by devices on usb bus enabled */
+       unsigned remote_wakeup_enable:1;
+       /* Switch or not to switch (keep always powered) */
+       unsigned no_power_switching:1;
+       /* Ganged port power switching (0) or individual port
+          power switching (1) */
+       unsigned power_switching_mode:1;
+       /* Given port_power, msec/2 after power on till power good */
+       u8 potpg;
+       /* Hardware reset set/clear. If implemented, this function must:
+          if set == 0,   deassert chip's HW reset pin
+          otherwise,     assert chip's HW reset pin       */
+       void (*reset) (struct device * dev, int set);
+       /* Hardware clock start/stop. If implemented, this function must:
+          if start == 0,    stop the external clock
+          otherwise,        start the external clock
+        */
+       void (*clock) (struct device * dev, int start);
+       /* Inter-io delay (ns). The chip is picky about access timings; it
+          expects at least:
+          150ns delay between consecutive accesses to DATA_REG,
+          300ns delay between access to ADDR_REG and DATA_REG
+          OE, WE MUST NOT be changed during these intervals
+        */
+       void (*delay) (struct device * dev, int delay);
+};