Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6
authorLinus Torvalds <torvalds@g5.osdl.org>
Wed, 27 Sep 2006 21:40:30 +0000 (14:40 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Wed, 27 Sep 2006 21:40:30 +0000 (14:40 -0700)
* master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6: (129 commits)
  [PATCH] USB Storage: fix Rio Karma eject support build error
  USB: Airprime driver improvements to allow full speed EvDO transfers
  USB: remove OTG build warning
  USB: EHCI update VIA workaround
  USB: force root hub resume after power loss
  USB: ohci_usb can oops on shutdown
  USB: Dealias -110 code (more complete)
  USB: Remove unneeded void * casts in core files
  USB: u132-hcd: host controller driver for ELAN U132 adapter
  USB: ftdi-elan: client driver for ELAN Uxxx adapters
  usb serial: support Alcor Micro Corp. USB 2.0 TO RS-232 through pl2303 driver
  USB: Moschip 7840 USB-Serial Driver
  USB: add PlayStation 2 Trance Vibrator driver
  USB: Add ADU support for Ontrak ADU devices
  aircable: fix printk format warnings
  Add AIRcable USB Bluetooth Dongle Driver
  cypress_m8: implement graceful failure handling
  cypress_m8: improve control endpoint error handling
  cypress_m8: use usb_fill_int_urb where appropriate
  cypress_m8: use appropriate URB polling interval
  ...

173 files changed:
Documentation/DocBook/usb.tmpl
Documentation/devices.txt
Documentation/usb/error-codes.txt
Documentation/usb/usb-serial.txt
arch/arm/mach-pxa/corgi.c
arch/arm/plat-omap/usb.c
drivers/block/ub.c
drivers/i2c/chips/isp1301_omap.c
drivers/isdn/gigaset/bas-gigaset.c
drivers/isdn/hisax/hfc_usb.h
drivers/media/dvb/dvb-usb/dvb-usb-urb.c
drivers/media/dvb/ttusb-dec/ttusb_dec.c
drivers/media/video/ov511.c
drivers/media/video/pwc/pwc-if.c
drivers/media/video/w9968cf.c
drivers/net/irda/irda-usb.c
drivers/net/wireless/zd1201.c
drivers/usb/Kconfig
drivers/usb/Makefile
drivers/usb/atm/ueagle-atm.c
drivers/usb/class/usblp.c
drivers/usb/core/Makefile
drivers/usb/core/buffer.c
drivers/usb/core/config.c
drivers/usb/core/devices.c
drivers/usb/core/devio.c
drivers/usb/core/driver.c
drivers/usb/core/endpoint.c
drivers/usb/core/file.c
drivers/usb/core/generic.c [new file with mode: 0644]
drivers/usb/core/hcd-pci.c
drivers/usb/core/hcd.c
drivers/usb/core/hcd.h
drivers/usb/core/hub.c
drivers/usb/core/hub.h
drivers/usb/core/inode.c
drivers/usb/core/message.c
drivers/usb/core/notify.c
drivers/usb/core/sysfs.c
drivers/usb/core/urb.c
drivers/usb/core/usb.c
drivers/usb/core/usb.h
drivers/usb/gadget/Kconfig
drivers/usb/gadget/Makefile
drivers/usb/gadget/at91_udc.c
drivers/usb/gadget/dummy_hcd.c
drivers/usb/gadget/ether.c
drivers/usb/gadget/gmidi.c [new file with mode: 0644]
drivers/usb/gadget/inode.c
drivers/usb/gadget/net2280.c
drivers/usb/gadget/omap_udc.c
drivers/usb/gadget/pxa2xx_udc.c
drivers/usb/gadget/pxa2xx_udc.h
drivers/usb/gadget/serial.c
drivers/usb/host/Kconfig
drivers/usb/host/Makefile
drivers/usb/host/ehci-au1xxx.c
drivers/usb/host/ehci-dbg.c
drivers/usb/host/ehci-fsl.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci-mem.c
drivers/usb/host/ehci-pci.c
drivers/usb/host/ehci-q.c
drivers/usb/host/ehci-sched.c
drivers/usb/host/ehci.h
drivers/usb/host/isp116x-hcd.c
drivers/usb/host/isp116x.h
drivers/usb/host/ohci-at91.c
drivers/usb/host/ohci-au1xxx.c
drivers/usb/host/ohci-dbg.c
drivers/usb/host/ohci-ep93xx.c
drivers/usb/host/ohci-hcd.c
drivers/usb/host/ohci-hub.c
drivers/usb/host/ohci-lh7a404.c
drivers/usb/host/ohci-mem.c
drivers/usb/host/ohci-omap.c
drivers/usb/host/ohci-pci.c
drivers/usb/host/ohci-pnx4008.c [new file with mode: 0644]
drivers/usb/host/ohci-ppc-soc.c
drivers/usb/host/ohci-pxa27x.c
drivers/usb/host/ohci-s3c2410.c
drivers/usb/host/ohci-sa1111.c
drivers/usb/host/ohci.h
drivers/usb/host/sl811-hcd.c
drivers/usb/host/u132-hcd.c [new file with mode: 0644]
drivers/usb/host/uhci-debug.c
drivers/usb/host/uhci-hub.c
drivers/usb/image/mdc800.c
drivers/usb/input/Kconfig
drivers/usb/input/Makefile
drivers/usb/input/acecad.c
drivers/usb/input/appletouch.c
drivers/usb/input/ati_remote.c
drivers/usb/input/hid-core.c
drivers/usb/input/hiddev.c
drivers/usb/input/itmtouch.c
drivers/usb/input/keyspan_remote.c
drivers/usb/input/mtouchusb.c
drivers/usb/input/powermate.c
drivers/usb/input/touchkitusb.c
drivers/usb/input/trancevibrator.c [new file with mode: 0644]
drivers/usb/input/usbmouse.c
drivers/usb/input/usbtouchscreen.c
drivers/usb/input/wacom.c [deleted file]
drivers/usb/input/wacom.h [new file with mode: 0644]
drivers/usb/input/wacom_sys.c [new file with mode: 0644]
drivers/usb/input/wacom_wac.c [new file with mode: 0644]
drivers/usb/input/wacom_wac.h [new file with mode: 0644]
drivers/usb/input/yealink.c
drivers/usb/misc/Kconfig
drivers/usb/misc/Makefile
drivers/usb/misc/adutux.c [new file with mode: 0644]
drivers/usb/misc/auerswald.c
drivers/usb/misc/cypress_cy7c63.c
drivers/usb/misc/cytherm.c
drivers/usb/misc/ftdi-elan.c [new file with mode: 0644]
drivers/usb/misc/idmouse.c
drivers/usb/misc/ldusb.c
drivers/usb/misc/legousbtower.c
drivers/usb/misc/phidget.c [new file with mode: 0644]
drivers/usb/misc/phidget.h [new file with mode: 0644]
drivers/usb/misc/phidgetkit.c
drivers/usb/misc/phidgetmotorcontrol.c [new file with mode: 0644]
drivers/usb/misc/phidgetservo.c
drivers/usb/misc/sisusbvga/sisusb.c
drivers/usb/misc/usb_u132.h [new file with mode: 0644]
drivers/usb/misc/usblcd.c
drivers/usb/misc/usbled.c
drivers/usb/mon/mon_main.c
drivers/usb/mon/mon_stat.c
drivers/usb/mon/mon_text.c
drivers/usb/mon/usb_mon.h
drivers/usb/net/asix.c
drivers/usb/net/net1080.c
drivers/usb/net/pegasus.c
drivers/usb/net/rtl8150.c
drivers/usb/net/usbnet.c
drivers/usb/net/usbnet.h
drivers/usb/serial/Kconfig
drivers/usb/serial/Makefile
drivers/usb/serial/aircable.c [new file with mode: 0644]
drivers/usb/serial/airprime.c
drivers/usb/serial/ark3116.c
drivers/usb/serial/cypress_m8.c
drivers/usb/serial/ftdi_sio.c
drivers/usb/serial/garmin_gps.c
drivers/usb/serial/ipaq.c
drivers/usb/serial/mos7840.c [new file with mode: 0644]
drivers/usb/serial/pl2303.c
drivers/usb/serial/pl2303.h
drivers/usb/serial/usb-serial.c
drivers/usb/storage/Kconfig
drivers/usb/storage/Makefile
drivers/usb/storage/initializers.c
drivers/usb/storage/initializers.h
drivers/usb/storage/karma.c [new file with mode: 0644]
drivers/usb/storage/karma.h [new file with mode: 0644]
drivers/usb/storage/libusual.c
drivers/usb/storage/onetouch.c
drivers/usb/storage/scsiglue.c
drivers/usb/storage/transport.c
drivers/usb/storage/unusual_devs.h
drivers/usb/storage/usb.c
drivers/usb/usb-skeleton.c
include/asm-arm/arch-pxa/udc.h
include/linux/usb.h
include/linux/usb/audio.h [new file with mode: 0644]
include/linux/usb/midi.h [new file with mode: 0644]
include/linux/usb/otg.h [new file with mode: 0644]
include/linux/usb_otg.h [deleted file]
include/linux/usb_usual.h
sound/usb/usbmidi.c

index 320af25..3608472 100644 (file)
 
     <para>A Universal Serial Bus (USB) is used to connect a host,
     such as a PC or workstation, to a number of peripheral
-    devices.  USB uses a tree structure, with the host at the
+    devices.  USB uses a tree structure, with the host as the
     root (the system's master), hubs as interior nodes, and
-    peripheral devices as leaves (and slaves).
+    peripherals as leaves (and slaves).
     Modern PCs support several such trees of USB devices, usually
     one USB 2.0 tree (480 Mbit/sec each) with
     a few USB 1.1 trees (12 Mbit/sec each) that are used when you
     connect a USB 1.1 device directly to the machine's "root hub".
     </para>
 
-    <para>That master/slave asymmetry was designed in part for
-    ease of use.  It is not physically possible to assemble
-    (legal) USB cables incorrectly:  all upstream "to-the-host"
-    connectors are the rectangular typematching the sockets on
-    root hubs, and the downstream type are the squarish type
-    (or they are built in to the peripheral).
-    Software doesn't need to deal with distributed autoconfiguration
-    since the pre-designated master node manages all that.
-    At the electrical level, bus protocol overhead is reduced by
-    eliminating arbitration and moving scheduling into host software.
+    <para>That master/slave asymmetry was designed-in for a number of
+    reasons, one being ease of use.  It is not physically possible to
+    assemble (legal) USB cables incorrectly:  all upstream "to the host"
+    connectors are the rectangular type (matching the sockets on
+    root hubs), and all downstream connectors are the squarish type
+    (or they are built into the peripheral).
+    Also, the host software doesn't need to deal with distributed
+    auto-configuration since the pre-designated master node manages all that.
+    And finally, at the electrical level, bus protocol overhead is reduced by
+    eliminating arbitration and moving scheduling into the host software.
     </para>
 
-    <para>USB 1.0 was announced in January 1996, and was revised
+    <para>USB 1.0 was announced in January 1996 and was revised
     as USB 1.1 (with improvements in hub specification and
     support for interrupt-out transfers) in September 1998.
-    USB 2.0 was released in April 2000, including high speed
-    transfers and transaction translating hubs (used for USB 1.1
+    USB 2.0 was released in April 2000, adding high-speed
+    transfers and transaction-translating hubs (used for USB 1.1
     and 1.0 backward compatibility).
     </para>
 
-    <para>USB support was added to Linux early in the 2.2 kernel series
-    shortly before the 2.3 development forked off.  Updates
-    from 2.3 were regularly folded back into 2.2 releases, bringing
-    new features such as <filename>/sbin/hotplug</filename> support,
-    more drivers, and more robustness.
-    The 2.5 kernel series continued such improvements, and also
-    worked on USB 2.0 support,
-    higher performance,
-    better consistency between host controller drivers,
-    API simplification (to make bugs less likely),
-    and providing internal "kerneldoc" documentation.
+    <para>Kernel developers added USB support to Linux early in the 2.2 kernel
+    series, shortly before 2.3 development forked.  Updates from 2.3 were
+    regularly folded back into 2.2 releases, which improved reliability and
+    brought <filename>/sbin/hotplug</filename> support as well more drivers.
+    Such improvements were continued in the 2.5 kernel series, where they added
+    USB 2.0 support, improved performance, and made the host controller drivers
+    (HCDs) more consistent.  They also simplified the API (to make bugs less
+    likely) and added internal "kerneldoc" documentation.
     </para>
 
     <para>Linux can run inside USB devices as well as on
     the hosts that control the devices.
-    Because the Linux 2.x USB support evolved to support mass market
-    platforms such as Apple Macintosh or PC-compatible systems,
-    it didn't address design concerns for those types of USB systems.
-    So it can't be used inside mass-market PDAs, or other peripherals.
-    USB device drivers running inside those Linux peripherals
+    But USB device drivers running inside those peripherals
     don't do the same things as the ones running inside hosts,
-    and so they've been given a different name:
-    they're called <emphasis>gadget drivers</emphasis>.
-    This document does not present gadget drivers.
+    so they've been given a different name:
+    <emphasis>gadget drivers</emphasis>.
+    This document does not cover gadget drivers.
     </para>
 
     </chapter>
 <chapter id="host">
     <title>USB Host-Side API Model</title>
 
-    <para>Within the kernel,
-    host-side drivers for USB devices talk to the "usbcore" APIs.
-    There are two types of public "usbcore" APIs, targetted at two different
-    layers of USB driver.  Those are
-    <emphasis>general purpose</emphasis> drivers, exposed through
-    driver frameworks such as block, character, or network devices;
-    and drivers that are <emphasis>part of the core</emphasis>,
-    which are involved in managing a USB bus.
-    Such core drivers include the <emphasis>hub</emphasis> driver,
-    which manages trees of USB devices, and several different kinds
-    of <emphasis>host controller driver (HCD)</emphasis>,
+    <para>Host-side drivers for USB devices talk to the "usbcore" APIs.
+    There are two.  One is intended for
+    <emphasis>general-purpose</emphasis> drivers (exposed through
+    driver frameworks), and the other is for drivers that are
+    <emphasis>part of the core</emphasis>.
+    Such core drivers include the <emphasis>hub</emphasis> driver
+    (which manages trees of USB devices) and several different kinds
+    of <emphasis>host controller drivers</emphasis>,
     which control individual busses.
     </para>
 
      
     <itemizedlist>
 
-       <listitem><para>USB supports four kinds of data transfer
-       (control, bulk, interrupt, and isochronous).  Two transfer
-       types use bandwidth as it's available (control and bulk),
-       while the other two types of transfer (interrupt and isochronous)
+       <listitem><para>USB supports four kinds of data transfers
+       (control, bulk, interrupt, and isochronous).  Two of them (control
+       and bulk) use bandwidth as it's available,
+       while the other two (interrupt and isochronous)
        are scheduled to provide guaranteed bandwidth.
        </para></listitem>
 
        <listitem><para>The device description model includes one or more
        "configurations" per device, only one of which is active at a time.
-       Devices that are capable of high speed operation must also support
-       full speed configurations, along with a way to ask about the
-       "other speed" configurations that might be used.
+       Devices that are capable of high-speed operation must also support
+       full-speed configurations, along with a way to ask about the
+       "other speed" configurations which might be used.
        </para></listitem>
 
-       <listitem><para>Configurations have one or more "interface", each
+       <listitem><para>Configurations have one or more "interfaces", each
        of which may have "alternate settings".  Interfaces may be
        standardized by USB "Class" specifications, or may be specific to
        a vendor or device.</para>
        </para></listitem>
 
        <listitem><para>The Linux USB API supports synchronous calls for
-       control and bulk messaging.
+       control and bulk messages.
        It also supports asynchnous calls for all kinds of data transfer,
        using request structures called "URBs" (USB Request Blocks).
        </para></listitem>
            file in your Linux kernel sources.
            </para>
 
-           <para>Otherwise the main use for this file from programs
-           is to poll() it to get notifications of usb devices
-           as they're plugged or unplugged.
-           To see what changed, you'd need to read the file and
-           compare "before" and "after" contents, scan the filesystem,
-           or see its hotplug event.
+           <para>This file, in combination with the poll() system call, can
+           also be used to detect when devices are added or removed:
+<programlisting>int fd;
+struct pollfd pfd;
+
+fd = open("/proc/bus/usb/devices", O_RDONLY);
+pfd = { fd, POLLIN, 0 };
+for (;;) {
+       /* The first time through, this call will return immediately. */
+       poll(&amp;pfd, 1, -1);
+
+       /* To see what's changed, compare the file's previous and current
+          contents or scan the filesystem.  (Scanning is more precise.) */
+}</programlisting>
+           Note that this behavior is intended to be used for informational
+           and debug purposes.  It would be more appropriate to use programs
+           such as udev or HAL to initialize a device or start a user-mode
+           helper program, for instance.
            </para>
-
        </sect1>
 
        <sect1>
index 66c725f..addc67b 100644 (file)
@@ -2543,6 +2543,9 @@ Your cooperation is appreciated.
                 64 = /dev/usb/rio500   Diamond Rio 500
                 65 = /dev/usb/usblcd   USBLCD Interface (info@usblcd.de)
                 66 = /dev/usb/cpad0    Synaptics cPad (mouse/LCD)
+                67 = /dev/usb/adutux0  1st Ontrak ADU device
+                   ...
+                76 = /dev/usb/adutux10 10th Ontrak ADU device
                 96 = /dev/usb/hiddev0  1st USB HID device
                    ...
                111 = /dev/usb/hiddev15 16th USB HID device
index 867f4c3..39c68f8 100644 (file)
@@ -98,13 +98,13 @@ one or more packets could finish before an error stops further endpoint I/O.
                        error, a failure to respond (often caused by
                        device disconnect), or some other fault.
 
--ETIMEDOUT (**)                No response packet received within the prescribed
+-ETIME (**)            No response packet received within the prescribed
                        bus turn-around time.  This error may instead be
                        reported as -EPROTO or -EILSEQ.
 
-                       Note that the synchronous USB message functions
-                       also use this code to indicate timeout expired
-                       before the transfer completed.
+-ETIMEDOUT             Synchronous USB message functions use this code
+                       to indicate timeout expired before the transfer
+                       completed, and no other error was reported by HC.
 
 -EPIPE (**)            Endpoint stalled.  For non-control endpoints,
                        reset this status with usb_clear_halt().
@@ -163,6 +163,3 @@ usb_get_*/usb_set_*():
 usb_control_msg():
 usb_bulk_msg():
 -ETIMEDOUT             Timeout expired before the transfer completed.
-                       In the future this code may change to -ETIME,
-                       whose definition is a closer match to this sort
-                       of error.
index 02b0f7b..a2dee6e 100644 (file)
@@ -433,6 +433,11 @@ Options supported:
   See http://www.uuhaus.de/linux/palmconnect.html for up-to-date
   information on this driver.
 
+AIRcable USB Dongle Bluetooth driver
+  If there is the cdc_acm driver loaded in the system, you will find that the
+  cdc_acm claims the device before AIRcable can. This is simply corrected
+  by unloading both modules and then loading the aircable module before
+  cdc_acm module
 
 Generic Serial driver
 
index cce2657..337c01c 100644 (file)
@@ -284,21 +284,9 @@ static struct pxaficp_platform_data corgi_ficp_platform_data = {
 /*
  * USB Device Controller
  */
-static void corgi_udc_command(int cmd)
-{
-       switch(cmd)     {
-       case PXA2XX_UDC_CMD_CONNECT:
-               GPSR(CORGI_GPIO_USB_PULLUP) = GPIO_bit(CORGI_GPIO_USB_PULLUP);
-               break;
-       case PXA2XX_UDC_CMD_DISCONNECT:
-               GPCR(CORGI_GPIO_USB_PULLUP) = GPIO_bit(CORGI_GPIO_USB_PULLUP);
-               break;
-       }
-}
-
 static struct pxa2xx_udc_mach_info udc_info __initdata = {
        /* no connect GPIO; corgi can't tell connection status */
-       .udc_command            = corgi_udc_command,
+       .gpio_pullup            = CORGI_GPIO_USB_PULLUP,
 };
 
 
@@ -350,7 +338,6 @@ static void __init corgi_init(void)
        corgi_ssp_set_machinfo(&corgi_ssp_machinfo);
 
        pxa_gpio_mode(CORGI_GPIO_IR_ON | GPIO_OUT);
-       pxa_gpio_mode(CORGI_GPIO_USB_PULLUP | GPIO_OUT);
        pxa_gpio_mode(CORGI_GPIO_HSYNC | GPIO_IN);
 
        pxa_set_udc_info(&udc_info);
index 9b81532..7e80968 100644 (file)
@@ -26,7 +26,7 @@
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/platform_device.h>
-#include <linux/usb_otg.h>
+#include <linux/usb/otg.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
index d62b49f..45a8f40 100644 (file)
@@ -358,7 +358,7 @@ static void ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
 static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
     struct ub_scsi_cmd *cmd, struct ub_request *urq);
 static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static void ub_end_rq(struct request *rq, int uptodate);
+static void ub_end_rq(struct request *rq, unsigned int status);
 static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun,
     struct ub_request *urq, struct ub_scsi_cmd *cmd);
 static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
@@ -639,9 +639,15 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
        struct ub_request *urq;
        int n_elem;
 
-       if (atomic_read(&sc->poison) || lun->changed) {
+       if (atomic_read(&sc->poison)) {
+               blkdev_dequeue_request(rq);
+               ub_end_rq(rq, DID_NO_CONNECT << 16);
+               return 0;
+       }
+
+       if (lun->changed && !blk_pc_request(rq)) {
                blkdev_dequeue_request(rq);
-               ub_end_rq(rq, 0);
+               ub_end_rq(rq, SAM_STAT_CHECK_CONDITION);
                return 0;
        }
 
@@ -693,7 +699,7 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
 
 drop:
        ub_put_cmd(lun, cmd);
-       ub_end_rq(rq, 0);
+       ub_end_rq(rq, DID_ERROR << 16);
        return 0;
 }
 
@@ -761,47 +767,53 @@ static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
        struct ub_lun *lun = cmd->lun;
        struct ub_request *urq = cmd->back;
        struct request *rq;
-       int uptodate;
+       unsigned int scsi_status;
 
        rq = urq->rq;
 
        if (cmd->error == 0) {
-               uptodate = 1;
-
                if (blk_pc_request(rq)) {
                        if (cmd->act_len >= rq->data_len)
                                rq->data_len = 0;
                        else
                                rq->data_len -= cmd->act_len;
                }
+               scsi_status = 0;
        } else {
-               uptodate = 0;
-
                if (blk_pc_request(rq)) {
                        /* UB_SENSE_SIZE is smaller than SCSI_SENSE_BUFFERSIZE */
                        memcpy(rq->sense, sc->top_sense, UB_SENSE_SIZE);
                        rq->sense_len = UB_SENSE_SIZE;
                        if (sc->top_sense[0] != 0)
-                               rq->errors = SAM_STAT_CHECK_CONDITION;
+                               scsi_status = SAM_STAT_CHECK_CONDITION;
                        else
-                               rq->errors = DID_ERROR << 16;
+                               scsi_status = DID_ERROR << 16;
                } else {
                        if (cmd->error == -EIO) {
                                if (ub_rw_cmd_retry(sc, lun, urq, cmd) == 0)
                                        return;
                        }
+                       scsi_status = SAM_STAT_CHECK_CONDITION;
                }
        }
 
        urq->rq = NULL;
 
        ub_put_cmd(lun, cmd);
-       ub_end_rq(rq, uptodate);
+       ub_end_rq(rq, scsi_status);
        blk_start_queue(lun->disk->queue);
 }
 
-static void ub_end_rq(struct request *rq, int uptodate)
+static void ub_end_rq(struct request *rq, unsigned int scsi_status)
 {
+       int uptodate;
+
+       if (scsi_status == 0) {
+               uptodate = 1;
+       } else {
+               uptodate = 0;
+               rq->errors = scsi_status;
+       }
        end_that_request_first(rq, uptodate, rq->hard_nr_sectors);
        end_that_request_last(rq, uptodate);
 }
index f92505b..182f049 100644 (file)
@@ -30,7 +30,7 @@
 #include <linux/usb_ch9.h>
 #include <linux/usb_gadget.h>
 #include <linux/usb.h>
-#include <linux/usb_otg.h>
+#include <linux/usb/otg.h>
 #include <linux/i2c.h>
 #include <linux/workqueue.h>
 
index 3845def..5cfbe6a 100644 (file)
@@ -192,7 +192,7 @@ static char *get_usb_statmsg(int status)
                return "bit stuffing error, timeout, or unknown USB error";
        case -EILSEQ:
                return "CRC mismatch, timeout, or unknown USB error";
-       case -ETIMEDOUT:
+       case -ETIME:
                return "timed out";
        case -EPIPE:
                return "endpoint stalled";
index ec52c1a..6349367 100644 (file)
@@ -137,11 +137,11 @@ static struct hfcusb_symbolic_list urb_errlist[] = {
        {-ENXIO, "URB already queued"},
        {-EFBIG, "Too much ISO frames requested"},
        {-ENOSR, "Buffer error (overrun)"},
-       {-EPIPE, "Specified endpoint is stalled (device not responding)"},
+       {-EPIPE, "Specified endpoint is stalled"},
        {-EOVERFLOW, "Babble (bad cable?)"},
        {-EPROTO, "Bit-stuff error (bad cable?)"},
-       {-EILSEQ, "CRC/Timeout"},
-       {-ETIMEDOUT, "NAK (device does not respond)"},
+       {-EILSEQ, "CRC or missing token"},
+       {-ETIME, "Device did not respond"},
        {-ESHUTDOWN, "Device unplugged"},
        {-1, NULL}
 };
index 9002f35..88b2837 100644 (file)
@@ -80,7 +80,6 @@ static void dvb_usb_urb_complete(struct urb *urb, struct pt_regs *ptregs)
 
        switch (urb->status) {
                case 0:         /* success */
-               case -ETIMEDOUT:    /* NAK */
                        break;
                case -ECONNRESET:   /* kill */
                case -ENOENT:
index 6c1cb77..c9d6635 100644 (file)
@@ -215,7 +215,7 @@ static void ttusb_dec_handle_irq( struct urb *urb, struct pt_regs *regs)
                case -ECONNRESET:
                case -ENOENT:
                case -ESHUTDOWN:
-               case -ETIMEDOUT:
+               case -ETIME:
                        /* this urb is dead, cleanup */
                        dprintk("%s:urb shutting down with status: %d\n",
                                        __FUNCTION__, urb->status);
index 1b07a61..5d8cd28 100644 (file)
@@ -301,10 +301,11 @@ static struct symbolic_list senlist[] = {
 static struct symbolic_list urb_errlist[] = {
        { -ENOSR,       "Buffer error (overrun)" },
        { -EPIPE,       "Stalled (device not responding)" },
-       { -EOVERFLOW,   "Babble (bad cable?)" },
+       { -EOVERFLOW,   "Babble (device sends too much data)" },
        { -EPROTO,      "Bit-stuff error (bad cable?)" },
-       { -EILSEQ,      "CRC/Timeout" },
-       { -ETIMEDOUT,   "NAK (device does not respond)" },
+       { -EILSEQ,      "CRC/Timeout (bad cable?)" },
+       { -ETIME,       "Device does not respond to token" },
+       { -ETIMEDOUT,   "Device does not respond to command" },
        { -1, NULL }
 };
 
index d470394..53c4b57 100644 (file)
@@ -711,7 +711,7 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
                        case -EOVERFLOW:        errmsg = "Babble (bad cable?)"; break;
                        case -EPROTO:           errmsg = "Bit-stuff error (bad cable?)"; break;
                        case -EILSEQ:           errmsg = "CRC/Timeout (could be anything)"; break;
-                       case -ETIMEDOUT:        errmsg = "NAK (device does not respond)"; break;
+                       case -ETIME:            errmsg = "Device does not respond"; break;
                }
                PWC_DEBUG_FLOW("pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg);
                /* Give up after a number of contiguous errors on the USB bus.
index 20f211b..2912326 100644 (file)
@@ -586,15 +586,14 @@ static struct w9968cf_symbolic_list urb_errlist[] = {
        { -EFBIG,     "Too much ISO frames requested" },
        { -ENOSR,     "Buffer error (overrun)" },
        { -EPIPE,     "Specified endpoint is stalled (device not responding)"},
-       { -EOVERFLOW, "Babble (bad cable?)" },
+       { -EOVERFLOW, "Babble (too much data)" },
        { -EPROTO,    "Bit-stuff error (bad cable?)" },
        { -EILSEQ,    "CRC/Timeout" },
-       { -ETIMEDOUT, "NAK (device does not respond)" },
+       { -ETIME,     "Device does not respond to token" },
+       { -ETIMEDOUT, "Device does not respond to command" },
        { -1, NULL }
 };
 
-
-
 /****************************************************************************
  * Memory management functions                                              *
  ****************************************************************************/
index 2a0d538..383cef1 100644 (file)
@@ -671,10 +671,8 @@ static void irda_usb_net_timeout(struct net_device *netdev)
                         * Jean II */
                        done = 1;
                        break;
-               case -ECONNABORTED:             /* -103 */
-               case -ECONNRESET:               /* -104 */
-               case -ETIMEDOUT:                /* -110 */
-               case -ENOENT:                   /* -2 (urb unlinked by us)  */
+               case -ECONNRESET:
+               case -ENOENT:                   /* urb unlinked by us */
                default:                        /* ??? - Play safe */
                        urb->status = 0;
                        netif_wake_queue(self->netdev);
@@ -712,10 +710,8 @@ static void irda_usb_net_timeout(struct net_device *netdev)
                         * Jean II */
                        done = 1;
                        break;
-               case -ECONNABORTED:             /* -103 */
-               case -ECONNRESET:               /* -104 */
-               case -ETIMEDOUT:                /* -110 */
-               case -ENOENT:                   /* -2 (urb unlinked by us)  */
+               case -ECONNRESET:
+               case -ENOENT:                   /* urb unlinked by us */
                default:                        /* ??? - Play safe */
                        if(skb != NULL) {
                                dev_kfree_skb_any(skb);
@@ -845,14 +841,14 @@ static void irda_usb_receive(struct urb *urb, struct pt_regs *regs)
                        self->stats.rx_crc_errors++;    
                        /* Also precursor to a hot-unplug on UHCI. */
                        /* Fallthrough... */
-               case -ECONNRESET:               /* -104 */
+               case -ECONNRESET:
                        /* Random error, if I remember correctly */
                        /* uhci_cleanup_unlink() is going to kill the Rx
                         * URB just after we return. No problem, at this
                         * point the URB will be idle ;-) - Jean II */
-               case -ESHUTDOWN:                /* -108 */
+               case -ESHUTDOWN:
                        /* That's usually a hot-unplug. Submit will fail... */
-               case -ETIMEDOUT:                /* -110 */
+               case -ETIME:
                        /* Usually precursor to a hot-unplug on OHCI. */
                default:
                        self->stats.rx_errors++;
index c52e9bc..f50ec10 100644 (file)
@@ -119,7 +119,7 @@ static void zd1201_usbfree(struct urb *urb, struct pt_regs *regs)
        switch(urb->status) {
                case -EILSEQ:
                case -ENODEV:
-               case -ETIMEDOUT:
+               case -ETIME:
                case -ENOENT:
                case -EPIPE:
                case -EOVERFLOW:
@@ -201,7 +201,7 @@ static void zd1201_usbrx(struct urb *urb, struct pt_regs *regs)
        switch(urb->status) {
                case -EILSEQ:
                case -ENODEV:
-               case -ETIMEDOUT:
+               case -ETIME:
                case -ENOENT:
                case -EPIPE:
                case -EOVERFLOW:
index 0050431..f9b1719 100644 (file)
@@ -25,6 +25,7 @@ config USB_ARCH_HAS_OHCI
        default y if PXA27x
        default y if ARCH_EP93XX
        default y if (ARCH_AT91RM9200 || ARCH_AT91SAM9261)
+       default y if ARCH_PNX4008
        # PPC:
        default y if STB03xxx
        default y if PPC_MPC52xx
index 4710eb0..97d57cf 100644 (file)
@@ -14,6 +14,7 @@ obj-$(CONFIG_USB_ISP116X_HCD) += host/
 obj-$(CONFIG_USB_OHCI_HCD)     += host/
 obj-$(CONFIG_USB_UHCI_HCD)     += host/
 obj-$(CONFIG_USB_SL811_HCD)    += host/
+obj-$(CONFIG_USB_U132_HCD)     += host/
 obj-$(CONFIG_ETRAX_USB_HOST)   += host/
 obj-$(CONFIG_USB_OHCI_AT91)    += host/
 
@@ -23,6 +24,7 @@ obj-$(CONFIG_USB_PRINTER)     += class/
 obj-$(CONFIG_USB_STORAGE)      += storage/
 obj-$(CONFIG_USB)              += storage/
 
+obj-$(CONFIG_USB_ACECAD)       += input/
 obj-$(CONFIG_USB_AIPTEK)       += input/
 obj-$(CONFIG_USB_ATI_REMOTE)   += input/
 obj-$(CONFIG_USB_HID)          += input/
@@ -31,8 +33,8 @@ obj-$(CONFIG_USB_KBTAB)               += input/
 obj-$(CONFIG_USB_MOUSE)                += input/
 obj-$(CONFIG_USB_MTOUCH)       += input/
 obj-$(CONFIG_USB_POWERMATE)    += input/
+obj-$(CONFIG_USB_TRANCEVIBRATOR)+= input/
 obj-$(CONFIG_USB_WACOM)                += input/
-obj-$(CONFIG_USB_ACECAD)       += input/
 obj-$(CONFIG_USB_XPAD)         += input/
 
 obj-$(CONFIG_USB_CATC)         += net/
@@ -47,22 +49,24 @@ obj-$(CONFIG_USB_MICROTEK)  += image/
 
 obj-$(CONFIG_USB_SERIAL)       += serial/
 
+obj-$(CONFIG_USB_ADUTUX)       += misc/
+obj-$(CONFIG_USB_APPLEDISPLAY) += misc/
 obj-$(CONFIG_USB_AUERSWALD)    += misc/
 obj-$(CONFIG_USB_CYPRESS_CY7C63)+= misc/
 obj-$(CONFIG_USB_CYTHERM)      += misc/
 obj-$(CONFIG_USB_EMI26)                += misc/
 obj-$(CONFIG_USB_EMI62)                += misc/
+obj-$(CONFIG_USB_FTDI_ELAN)    += misc/
 obj-$(CONFIG_USB_IDMOUSE)      += misc/
 obj-$(CONFIG_USB_LCD)          += misc/
 obj-$(CONFIG_USB_LD)           += misc/
 obj-$(CONFIG_USB_LED)          += misc/
 obj-$(CONFIG_USB_LEGOTOWER)    += misc/
+obj-$(CONFIG_USB_PHIDGETSERVO) += misc/
 obj-$(CONFIG_USB_RIO500)       += misc/
+obj-$(CONFIG_USB_SISUSBVGA)    += misc/
 obj-$(CONFIG_USB_TEST)         += misc/
 obj-$(CONFIG_USB_USS720)       += misc/
-obj-$(CONFIG_USB_PHIDGETSERVO) += misc/
-obj-$(CONFIG_USB_SISUSBVGA)    += misc/
-obj-$(CONFIG_USB_APPLEDISPLAY) += misc/
 
 obj-$(CONFIG_USB_ATM)          += atm/
 obj-$(CONFIG_USB_SPEEDTOUCH)   += atm/
index b38990a..465961a 100644 (file)
@@ -1621,26 +1621,32 @@ static int claim_interface(struct usb_device *usb_dev,
        return ret;
 }
 
-static void create_fs_entries(struct uea_softc *sc, struct usb_interface *intf)
+static struct attribute *attrs[] = {
+       &dev_attr_stat_status.attr,
+       &dev_attr_stat_mflags.attr,
+       &dev_attr_stat_human_status.attr,
+       &dev_attr_stat_delin.attr,
+       &dev_attr_stat_vidcpe.attr,
+       &dev_attr_stat_usrate.attr,
+       &dev_attr_stat_dsrate.attr,
+       &dev_attr_stat_usattenuation.attr,
+       &dev_attr_stat_dsattenuation.attr,
+       &dev_attr_stat_usmargin.attr,
+       &dev_attr_stat_dsmargin.attr,
+       &dev_attr_stat_txflow.attr,
+       &dev_attr_stat_rxflow.attr,
+       &dev_attr_stat_uscorr.attr,
+       &dev_attr_stat_dscorr.attr,
+       &dev_attr_stat_usunc.attr,
+       &dev_attr_stat_dsunc.attr,
+};
+static struct attribute_group attr_grp = {
+       .attrs = attrs,
+};
+
+static int create_fs_entries(struct usb_interface *intf)
 {
-       /* sysfs interface */
-       device_create_file(&intf->dev, &dev_attr_stat_status);
-       device_create_file(&intf->dev, &dev_attr_stat_mflags);
-       device_create_file(&intf->dev, &dev_attr_stat_human_status);
-       device_create_file(&intf->dev, &dev_attr_stat_delin);
-       device_create_file(&intf->dev, &dev_attr_stat_vidcpe);
-       device_create_file(&intf->dev, &dev_attr_stat_usrate);
-       device_create_file(&intf->dev, &dev_attr_stat_dsrate);
-       device_create_file(&intf->dev, &dev_attr_stat_usattenuation);
-       device_create_file(&intf->dev, &dev_attr_stat_dsattenuation);
-       device_create_file(&intf->dev, &dev_attr_stat_usmargin);
-       device_create_file(&intf->dev, &dev_attr_stat_dsmargin);
-       device_create_file(&intf->dev, &dev_attr_stat_txflow);
-       device_create_file(&intf->dev, &dev_attr_stat_rxflow);
-       device_create_file(&intf->dev, &dev_attr_stat_uscorr);
-       device_create_file(&intf->dev, &dev_attr_stat_dscorr);
-       device_create_file(&intf->dev, &dev_attr_stat_usunc);
-       device_create_file(&intf->dev, &dev_attr_stat_dsunc);
+       return sysfs_create_group(&intf->dev.kobj, &attr_grp);
 }
 
 static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
@@ -1708,37 +1714,25 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
                return ret;
        }
 
-       create_fs_entries(sc, intf);
+       ret = create_fs_entries(intf);
+       if (ret) {
+               uea_stop(sc);
+               kfree(sc);
+               return ret;
+       }
        return 0;
 }
 
-static void destroy_fs_entries(struct uea_softc *sc, struct usb_interface *intf)
+static void destroy_fs_entries(struct usb_interface *intf)
 {
-       /* sysfs interface */
-       device_remove_file(&intf->dev, &dev_attr_stat_status);
-       device_remove_file(&intf->dev, &dev_attr_stat_mflags);
-       device_remove_file(&intf->dev, &dev_attr_stat_human_status);
-       device_remove_file(&intf->dev, &dev_attr_stat_delin);
-       device_remove_file(&intf->dev, &dev_attr_stat_vidcpe);
-       device_remove_file(&intf->dev, &dev_attr_stat_usrate);
-       device_remove_file(&intf->dev, &dev_attr_stat_dsrate);
-       device_remove_file(&intf->dev, &dev_attr_stat_usattenuation);
-       device_remove_file(&intf->dev, &dev_attr_stat_dsattenuation);
-       device_remove_file(&intf->dev, &dev_attr_stat_usmargin);
-       device_remove_file(&intf->dev, &dev_attr_stat_dsmargin);
-       device_remove_file(&intf->dev, &dev_attr_stat_txflow);
-       device_remove_file(&intf->dev, &dev_attr_stat_rxflow);
-       device_remove_file(&intf->dev, &dev_attr_stat_uscorr);
-       device_remove_file(&intf->dev, &dev_attr_stat_dscorr);
-       device_remove_file(&intf->dev, &dev_attr_stat_usunc);
-       device_remove_file(&intf->dev, &dev_attr_stat_dsunc);
+       sysfs_remove_group(&intf->dev.kobj, &attr_grp);
 }
 
 static void uea_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
 {
        struct uea_softc *sc = usbatm->driver_data;
 
-       destroy_fs_entries(sc, intf);
+       destroy_fs_entries(intf);
        uea_stop(sc);
        kfree(sc);
 }
index 48dee4b..9cac11c 100644 (file)
@@ -813,7 +813,7 @@ static unsigned int usblp_quirks (__u16 vendor, __u16 product)
        return 0;
 }
 
-static struct file_operations usblp_fops = {
+static const struct file_operations usblp_fops = {
        .owner =        THIS_MODULE,
        .read =         usblp_read,
        .write =        usblp_write,
@@ -927,7 +927,9 @@ static int usblp_probe(struct usb_interface *intf,
 
        /* Retrieve and store the device ID string. */
        usblp_cache_device_id_string(usblp);
-       device_create_file(&intf->dev, &dev_attr_ieee1284_id);
+       retval = device_create_file(&intf->dev, &dev_attr_ieee1284_id);
+       if (retval)
+               goto abort_intfdata;
 
 #ifdef DEBUG
        usblp_check_status(usblp, 0);
@@ -1021,18 +1023,13 @@ static int usblp_select_alts(struct usblp *usblp)
                for (e = 0; e < ifd->desc.bNumEndpoints; e++) {
                        epd = &ifd->endpoint[e].desc;
 
-                       if ((epd->bmAttributes&USB_ENDPOINT_XFERTYPE_MASK)!=
-                           USB_ENDPOINT_XFER_BULK)
-                               continue;
-
-                       if (!(epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK)) {
+                       if (usb_endpoint_is_bulk_out(epd))
                                if (!epwrite)
                                        epwrite = epd;
 
-                       } else {
+                       if (usb_endpoint_is_bulk_in(epd))
                                if (!epread)
                                        epread = epd;
-                       }
                }
 
                /* Ignore buggy hardware without the right endpoints. */
index ec51092..34e9bac 100644 (file)
@@ -4,7 +4,7 @@
 
 usbcore-objs   := usb.o hub.o hcd.o urb.o message.o driver.o \
                        config.o file.o buffer.o sysfs.o endpoint.o \
-                       devio.o notify.o
+                       devio.o notify.o generic.o
 
 ifeq ($(CONFIG_PCI),y)
        usbcore-objs    += hcd-pci.o
index f4f4ef0..840442a 100644 (file)
@@ -104,7 +104,7 @@ void *hcd_buffer_alloc (
        dma_addr_t              *dma
 )
 {
-       struct usb_hcd          *hcd = bus->hcpriv;
+       struct usb_hcd          *hcd = bus_to_hcd(bus);
        int                     i;
 
        /* some USB hosts just use PIO */
@@ -127,7 +127,7 @@ void hcd_buffer_free (
        dma_addr_t              dma
 )
 {
-       struct usb_hcd          *hcd = bus->hcpriv;
+       struct usb_hcd          *hcd = bus_to_hcd(bus);
        int                     i;
 
        if (!addr)
index 4c9e63e..bfb3731 100644 (file)
@@ -475,7 +475,9 @@ int usb_get_configuration(struct usb_device *dev)
                if (result < 0) {
                        dev_err(ddev, "unable to read config index %d "
                            "descriptor/%s\n", cfgno, "start");
-                       goto err;
+                       dev_err(ddev, "chopping to %d config(s)\n", cfgno);
+                       dev->descriptor.bNumConfigurations = cfgno;
+                       break;
                } else if (result < 4) {
                        dev_err(ddev, "config index %d descriptor too short "
                            "(expected %i, got %i)\n", cfgno,
index c0f3734..3538c2f 100644 (file)
@@ -593,7 +593,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbyte
 /* Kernel lock for "lastev" protection */
 static unsigned int usb_device_poll(struct file *file, struct poll_table_struct *wait)
 {
-       struct usb_device_status *st = (struct usb_device_status *)file->private_data;
+       struct usb_device_status *st = file->private_data;
        unsigned int mask = 0;
 
        lock_kernel();
@@ -603,7 +603,7 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct
                        unlock_kernel();
                        return POLLIN;
                }
-               
+
                /* we may have dropped BKL - need to check for having lost the race */
                if (file->private_data) {
                        kfree(st);
@@ -667,7 +667,7 @@ static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig)
        return ret;
 }
 
-struct file_operations usbfs_devices_fops = {
+const struct file_operations usbfs_devices_fops = {
        .llseek =       usb_device_lseek,
        .read =         usb_device_read,
        .poll =         usb_device_poll,
index 32e0300..a94c63b 100644 (file)
@@ -59,6 +59,9 @@
 #define USB_DEVICE_MAX                 USB_MAXBUS * 128
 static struct class *usb_device_class;
 
+/* Mutual exclusion for removal, open, and release */
+DEFINE_MUTEX(usbfs_mutex);
+
 struct async {
        struct list_head asynclist;
        struct dev_state *ps;
@@ -87,9 +90,10 @@ MODULE_PARM_DESC (usbfs_snoop, "true to log all usbfs traffic");
 
 #define        MAX_USBFS_BUFFER_SIZE   16384
 
-static inline int connected (struct usb_device *dev)
+static inline int connected (struct dev_state *ps)
 {
-       return dev->state != USB_STATE_NOTATTACHED;
+       return (!list_empty(&ps->list) &&
+                       ps->dev->state != USB_STATE_NOTATTACHED);
 }
 
 static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
@@ -118,7 +122,7 @@ static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
 
 static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
 {
-       struct dev_state *ps = (struct dev_state *)file->private_data;
+       struct dev_state *ps = file->private_data;
        struct usb_device *dev = ps->dev;
        ssize_t ret = 0;
        unsigned len;
@@ -127,7 +131,7 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l
 
        pos = *ppos;
        usb_lock_device(dev);
-       if (!connected(dev)) {
+       if (!connected(ps)) {
                ret = -ENODEV;
                goto err;
        } else if (pos < 0) {
@@ -301,7 +305,7 @@ static void snoop_urb(struct urb *urb, void __user *userurb)
 
 static void async_completed(struct urb *urb, struct pt_regs *regs)
 {
-        struct async *as = (struct async *)urb->context;
+        struct async *as = urb->context;
         struct dev_state *ps = as->ps;
        struct siginfo sinfo;
 
@@ -541,25 +545,25 @@ static int usbdev_open(struct inode *inode, struct file *file)
        struct dev_state *ps;
        int ret;
 
-       /* 
-        * no locking necessary here, as chrdev_open has the kernel lock
-        * (still acquire the kernel lock for safety)
-        */
+       /* Protect against simultaneous removal or release */
+       mutex_lock(&usbfs_mutex);
+
        ret = -ENOMEM;
        if (!(ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL)))
-               goto out_nolock;
+               goto out;
 
-       lock_kernel();
        ret = -ENOENT;
        /* check if we are called from a real node or usbfs */
        if (imajor(inode) == USB_DEVICE_MAJOR)
                dev = usbdev_lookup_minor(iminor(inode));
        if (!dev)
                dev = inode->i_private;
-       if (!dev) {
-               kfree(ps);
+       if (!dev)
                goto out;
-       }
+       ret = usb_autoresume_device(dev, 1);
+       if (ret)
+               goto out;
+
        usb_get_dev(dev);
        ret = 0;
        ps->dev = dev;
@@ -579,30 +583,36 @@ static int usbdev_open(struct inode *inode, struct file *file)
        list_add_tail(&ps->list, &dev->filelist);
        file->private_data = ps;
  out:
-       unlock_kernel();
- out_nolock:
-        return ret;
+       if (ret)
+               kfree(ps);
+       mutex_unlock(&usbfs_mutex);
+       return ret;
 }
 
 static int usbdev_release(struct inode *inode, struct file *file)
 {
-       struct dev_state *ps = (struct dev_state *)file->private_data;
+       struct dev_state *ps = file->private_data;
        struct usb_device *dev = ps->dev;
        unsigned int ifnum;
 
        usb_lock_device(dev);
+
+       /* Protect against simultaneous open */
+       mutex_lock(&usbfs_mutex);
        list_del_init(&ps->list);
+       mutex_unlock(&usbfs_mutex);
+
        for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed);
                        ifnum++) {
                if (test_bit(ifnum, &ps->ifclaimed))
                        releaseintf(ps, ifnum);
        }
        destroy_all_async(ps);
+       usb_autosuspend_device(dev, 1);
        usb_unlock_device(dev);
        usb_put_dev(dev);
-       ps->dev = NULL;
        kfree(ps);
-        return 0;
+       return 0;
 }
 
 static int proc_control(struct dev_state *ps, void __user *arg)
@@ -1322,7 +1332,7 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
                }
        }
 
-       if (!connected(ps->dev)) {
+       if (!connected(ps)) {
                kfree(buf);
                return -ENODEV;
        }
@@ -1349,7 +1359,7 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
        /* let kernel drivers try to (re)bind to the interface */
        case USBDEVFS_CONNECT:
                usb_unlock_device(ps->dev);
-               bus_rescan_devices(intf->dev.bus);
+               retval = bus_rescan_devices(intf->dev.bus);
                usb_lock_device(ps->dev);
                break;
 
@@ -1413,7 +1423,7 @@ static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg)
  */
 static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
-       struct dev_state *ps = (struct dev_state *)file->private_data;
+       struct dev_state *ps = file->private_data;
        struct usb_device *dev = ps->dev;
        void __user *p = (void __user *)arg;
        int ret = -ENOTTY;
@@ -1421,7 +1431,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
        if (!(file->f_mode & FMODE_WRITE))
                return -EPERM;
        usb_lock_device(dev);
-       if (!connected(dev)) {
+       if (!connected(ps)) {
                usb_unlock_device(dev);
                return -ENODEV;
        }
@@ -1556,18 +1566,18 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
 /* No kernel lock - fine */
 static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wait)
 {
-       struct dev_state *ps = (struct dev_state *)file->private_data;
-        unsigned int mask = 0;
+       struct dev_state *ps = file->private_data;
+       unsigned int mask = 0;
 
        poll_wait(file, &ps->wait, wait);
        if (file->f_mode & FMODE_WRITE && !list_empty(&ps->async_completed))
                mask |= POLLOUT | POLLWRNORM;
-       if (!connected(ps->dev))
+       if (!connected(ps))
                mask |= POLLERR | POLLHUP;
        return mask;
 }
 
-struct file_operations usbfs_device_file_operations = {
+const struct file_operations usbfs_device_file_operations = {
        .llseek =       usbdev_lseek,
        .read =         usbdev_read,
        .poll =         usbdev_poll,
index ec89065..b104632 100644 (file)
@@ -17,7 +17,8 @@
  *
  * NOTE! This is not actually a driver at all, rather this is
  * just a collection of helper routines that implement the
- * generic USB things that the real drivers can use..
+ * matching, probing, releasing, suspending and resuming for
+ * real drivers.
  *
  */
 
@@ -34,38 +35,6 @@ struct usb_dynid {
        struct usb_device_id id;
 };
 
-
-static int generic_probe(struct device *dev)
-{
-       return 0;
-}
-static int generic_remove(struct device *dev)
-{
-       struct usb_device *udev = to_usb_device(dev);
-
-       /* if this is only an unbind, not a physical disconnect, then
-        * unconfigure the device */
-       if (udev->state == USB_STATE_CONFIGURED)
-               usb_set_configuration(udev, 0);
-
-       /* in case the call failed or the device was suspended */
-       if (udev->state >= USB_STATE_CONFIGURED)
-               usb_disable_device(udev, 0);
-       return 0;
-}
-
-struct device_driver usb_generic_driver = {
-       .owner = THIS_MODULE,
-       .name = "usb",
-       .bus = &usb_bus_type,
-       .probe = generic_probe,
-       .remove = generic_remove,
-};
-
-/* Fun hack to determine if the struct device is a
- * usb device or a usb interface. */
-int usb_generic_driver_data;
-
 #ifdef CONFIG_HOTPLUG
 
 /*
@@ -80,6 +49,7 @@ static ssize_t store_new_id(struct device_driver *driver,
        u32 idVendor = 0;
        u32 idProduct = 0;
        int fields = 0;
+       int retval = 0;
 
        fields = sscanf(buf, "%x %x", &idVendor, &idProduct);
        if (fields < 2)
@@ -99,10 +69,12 @@ static ssize_t store_new_id(struct device_driver *driver,
        spin_unlock(&usb_drv->dynids.lock);
 
        if (get_driver(driver)) {
-               driver_attach(driver);
+               retval = driver_attach(driver);
                put_driver(driver);
        }
 
+       if (retval)
+               return retval;
        return count;
 }
 static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
@@ -115,7 +87,7 @@ static int usb_create_newid_file(struct usb_driver *usb_drv)
                goto exit;
 
        if (usb_drv->probe != NULL)
-               error = sysfs_create_file(&usb_drv->driver.kobj,
+               error = sysfs_create_file(&usb_drv->drvwrap.driver.kobj,
                                          &driver_attr_new_id.attr);
 exit:
        return error;
@@ -127,7 +99,7 @@ static void usb_remove_newid_file(struct usb_driver *usb_drv)
                return;
 
        if (usb_drv->probe != NULL)
-               sysfs_remove_file(&usb_drv->driver.kobj,
+               sysfs_remove_file(&usb_drv->drvwrap.driver.kobj,
                                  &driver_attr_new_id.attr);
 }
 
@@ -174,21 +146,57 @@ static const struct usb_device_id *usb_match_dynamic_id(struct usb_interface *in
 }
 
 
-/* called from driver core with usb_bus_type.subsys writelock */
+/* called from driver core with dev locked */
+static int usb_probe_device(struct device *dev)
+{
+       struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
+       struct usb_device *udev;
+       int error = -ENODEV;
+
+       dev_dbg(dev, "%s\n", __FUNCTION__);
+
+       if (!is_usb_device(dev))        /* Sanity check */
+               return error;
+
+       udev = to_usb_device(dev);
+
+       /* TODO: Add real matching code */
+
+       /* The device should always appear to be in use
+        * unless the driver suports autosuspend.
+        */
+       udev->pm_usage_cnt = !(udriver->supports_autosuspend);
+
+       error = udriver->probe(udev);
+       return error;
+}
+
+/* called from driver core with dev locked */
+static int usb_unbind_device(struct device *dev)
+{
+       struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
+
+       udriver->disconnect(to_usb_device(dev));
+       return 0;
+}
+
+
+/* called from driver core with dev locked */
 static int usb_probe_interface(struct device *dev)
 {
-       struct usb_interface * intf = to_usb_interface(dev);
-       struct usb_driver * driver = to_usb_driver(dev->driver);
+       struct usb_driver *driver = to_usb_driver(dev->driver);
+       struct usb_interface *intf;
+       struct usb_device *udev;
        const struct usb_device_id *id;
        int error = -ENODEV;
 
        dev_dbg(dev, "%s\n", __FUNCTION__);
 
-       if (!driver->probe)
+       if (is_usb_device(dev))         /* Sanity check */
                return error;
-       /* FIXME we'd much prefer to just resume it ... */
-       if (interface_to_usbdev(intf)->state == USB_STATE_SUSPENDED)
-               return -EHOSTUNREACH;
+
+       intf = to_usb_interface(dev);
+       udev = interface_to_usbdev(intf);
 
        id = usb_match_id(intf, driver->id_table);
        if (!id)
@@ -196,48 +204,165 @@ static int usb_probe_interface(struct device *dev)
        if (id) {
                dev_dbg(dev, "%s - got id\n", __FUNCTION__);
 
+               error = usb_autoresume_device(udev, 1);
+               if (error)
+                       return error;
+
                /* Interface "power state" doesn't correspond to any hardware
                 * state whatsoever.  We use it to record when it's bound to
                 * a driver that may start I/0:  it's not frozen/quiesced.
                 */
                mark_active(intf);
                intf->condition = USB_INTERFACE_BINDING;
+
+               /* The interface should always appear to be in use
+                * unless the driver suports autosuspend.
+                */
+               intf->pm_usage_cnt = !(driver->supports_autosuspend);
+
                error = driver->probe(intf, id);
                if (error) {
                        mark_quiesced(intf);
+                       intf->needs_remote_wakeup = 0;
                        intf->condition = USB_INTERFACE_UNBOUND;
                } else
                        intf->condition = USB_INTERFACE_BOUND;
+
+               usb_autosuspend_device(udev, 1);
        }
 
        return error;
 }
 
-/* called from driver core with usb_bus_type.subsys writelock */
+/* called from driver core with dev locked */
 static int usb_unbind_interface(struct device *dev)
 {
+       struct usb_driver *driver = to_usb_driver(dev->driver);
        struct usb_interface *intf = to_usb_interface(dev);
-       struct usb_driver *driver = to_usb_driver(intf->dev.driver);
+       struct usb_device *udev;
+       int error;
 
        intf->condition = USB_INTERFACE_UNBINDING;
 
+       /* Autoresume for set_interface call below */
+       udev = interface_to_usbdev(intf);
+       error = usb_autoresume_device(udev, 1);
+
        /* release all urbs for this interface */
        usb_disable_interface(interface_to_usbdev(intf), intf);
 
-       if (driver && driver->disconnect)
-               driver->disconnect(intf);
+       driver->disconnect(intf);
 
        /* reset other interface state */
        usb_set_interface(interface_to_usbdev(intf),
                        intf->altsetting[0].desc.bInterfaceNumber,
                        0);
        usb_set_intfdata(intf, NULL);
+
        intf->condition = USB_INTERFACE_UNBOUND;
        mark_quiesced(intf);
+       intf->needs_remote_wakeup = 0;
+
+       if (!error)
+               usb_autosuspend_device(udev, 1);
 
        return 0;
 }
 
+/**
+ * usb_driver_claim_interface - bind a driver to an interface
+ * @driver: the driver to be bound
+ * @iface: the interface to which it will be bound; must be in the
+ *     usb device's active configuration
+ * @priv: driver data associated with that interface
+ *
+ * This is used by usb device drivers that need to claim more than one
+ * interface on a device when probing (audio and acm are current examples).
+ * No device driver should directly modify internal usb_interface or
+ * usb_device structure members.
+ *
+ * Few drivers should need to use this routine, since the most natural
+ * way to bind to an interface is to return the private data from
+ * the driver's probe() method.
+ *
+ * Callers must own the device lock and the driver model's usb_bus_type.subsys
+ * writelock.  So driver probe() entries don't need extra locking,
+ * but other call contexts may need to explicitly claim those locks.
+ */
+int usb_driver_claim_interface(struct usb_driver *driver,
+                               struct usb_interface *iface, void* priv)
+{
+       struct device *dev = &iface->dev;
+       struct usb_device *udev = interface_to_usbdev(iface);
+       int retval = 0;
+
+       if (dev->driver)
+               return -EBUSY;
+
+       dev->driver = &driver->drvwrap.driver;
+       usb_set_intfdata(iface, priv);
+
+       mutex_lock_nested(&udev->pm_mutex, udev->level);
+       iface->condition = USB_INTERFACE_BOUND;
+       mark_active(iface);
+       iface->pm_usage_cnt = !(driver->supports_autosuspend);
+       mutex_unlock(&udev->pm_mutex);
+
+       /* if interface was already added, bind now; else let
+        * the future device_add() bind it, bypassing probe()
+        */
+       if (device_is_registered(dev))
+               retval = device_bind_driver(dev);
+
+       return retval;
+}
+EXPORT_SYMBOL(usb_driver_claim_interface);
+
+/**
+ * usb_driver_release_interface - unbind a driver from an interface
+ * @driver: the driver to be unbound
+ * @iface: the interface from which it will be unbound
+ *
+ * This can be used by drivers to release an interface without waiting
+ * for their disconnect() methods to be called.  In typical cases this
+ * also causes the driver disconnect() method to be called.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ * Callers must own the device lock and the driver model's usb_bus_type.subsys
+ * writelock.  So driver disconnect() entries don't need extra locking,
+ * but other call contexts may need to explicitly claim those locks.
+ */
+void usb_driver_release_interface(struct usb_driver *driver,
+                                       struct usb_interface *iface)
+{
+       struct device *dev = &iface->dev;
+       struct usb_device *udev = interface_to_usbdev(iface);
+
+       /* this should never happen, don't release something that's not ours */
+       if (!dev->driver || dev->driver != &driver->drvwrap.driver)
+               return;
+
+       /* don't release from within disconnect() */
+       if (iface->condition != USB_INTERFACE_BOUND)
+               return;
+
+       /* don't release if the interface hasn't been added yet */
+       if (device_is_registered(dev)) {
+               iface->condition = USB_INTERFACE_UNBINDING;
+               device_release_driver(dev);
+       }
+
+       dev->driver = NULL;
+       usb_set_intfdata(iface, NULL);
+
+       mutex_lock_nested(&udev->pm_mutex, udev->level);
+       iface->condition = USB_INTERFACE_UNBOUND;
+       mark_quiesced(iface);
+       iface->needs_remote_wakeup = 0;
+       mutex_unlock(&udev->pm_mutex);
+}
+EXPORT_SYMBOL(usb_driver_release_interface);
+
 /* returns 0 if no match, 1 if match */
 static int usb_match_one_id(struct usb_interface *interface,
                            const struct usb_device_id *id)
@@ -380,36 +505,224 @@ const struct usb_device_id *usb_match_id(struct usb_interface *interface,
 EXPORT_SYMBOL_GPL_FUTURE(usb_match_id);
 
 int usb_device_match(struct device *dev, struct device_driver *drv)
+{
+       /* devices and interfaces are handled separately */
+       if (is_usb_device(dev)) {
+
+               /* interface drivers never match devices */
+               if (!is_usb_device_driver(drv))
+                       return 0;
+
+               /* TODO: Add real matching code */
+               return 1;
+
+       } else {
+               struct usb_interface *intf;
+               struct usb_driver *usb_drv;
+               const struct usb_device_id *id;
+
+               /* device drivers never match interfaces */
+               if (is_usb_device_driver(drv))
+                       return 0;
+
+               intf = to_usb_interface(dev);
+               usb_drv = to_usb_driver(drv);
+
+               id = usb_match_id(intf, usb_drv->id_table);
+               if (id)
+                       return 1;
+
+               id = usb_match_dynamic_id(intf, usb_drv);
+               if (id)
+                       return 1;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_HOTPLUG
+
+/*
+ * This sends an uevent to userspace, typically helping to load driver
+ * or other modules, configure the device, and more.  Drivers can provide
+ * a MODULE_DEVICE_TABLE to help with module loading subtasks.
+ *
+ * We're called either from khubd (the typical case) or from root hub
+ * (init, kapmd, modprobe, rmmod, etc), but the agents need to handle
+ * delays in event delivery.  Use sysfs (and DEVPATH) to make sure the
+ * device (and this configuration!) are still present.
+ */
+static int usb_uevent(struct device *dev, char **envp, int num_envp,
+                     char *buffer, int buffer_size)
 {
        struct usb_interface *intf;
-       struct usb_driver *usb_drv;
-       const struct usb_device_id *id;
+       struct usb_device *usb_dev;
+       struct usb_host_interface *alt;
+       int i = 0;
+       int length = 0;
 
-       /* check for generic driver, which we don't match any device with */
-       if (drv == &usb_generic_driver)
-               return 0;
+       if (!dev)
+               return -ENODEV;
 
-       intf = to_usb_interface(dev);
-       usb_drv = to_usb_driver(drv);
+       /* driver is often null here; dev_dbg() would oops */
+       pr_debug ("usb %s: uevent\n", dev->bus_id);
 
-       id = usb_match_id(intf, usb_drv->id_table);
-       if (id)
-               return 1;
+       if (is_usb_device(dev)) {
+               usb_dev = to_usb_device(dev);
+               alt = NULL;
+       } else {
+               intf = to_usb_interface(dev);
+               usb_dev = interface_to_usbdev(intf);
+               alt = intf->cur_altsetting;
+       }
+
+       if (usb_dev->devnum < 0) {
+               pr_debug ("usb %s: already deleted?\n", dev->bus_id);
+               return -ENODEV;
+       }
+       if (!usb_dev->bus) {
+               pr_debug ("usb %s: bus removed?\n", dev->bus_id);
+               return -ENODEV;
+       }
+
+#ifdef CONFIG_USB_DEVICEFS
+       /* If this is available, userspace programs can directly read
+        * all the device descriptors we don't tell them about.  Or
+        * even act as usermode drivers.
+        *
+        * FIXME reduce hardwired intelligence here
+        */
+       if (add_uevent_var(envp, num_envp, &i,
+                          buffer, buffer_size, &length,
+                          "DEVICE=/proc/bus/usb/%03d/%03d",
+                          usb_dev->bus->busnum, usb_dev->devnum))
+               return -ENOMEM;
+#endif
+
+       /* per-device configurations are common */
+       if (add_uevent_var(envp, num_envp, &i,
+                          buffer, buffer_size, &length,
+                          "PRODUCT=%x/%x/%x",
+                          le16_to_cpu(usb_dev->descriptor.idVendor),
+                          le16_to_cpu(usb_dev->descriptor.idProduct),
+                          le16_to_cpu(usb_dev->descriptor.bcdDevice)))
+               return -ENOMEM;
+
+       /* class-based driver binding models */
+       if (add_uevent_var(envp, num_envp, &i,
+                          buffer, buffer_size, &length,
+                          "TYPE=%d/%d/%d",
+                          usb_dev->descriptor.bDeviceClass,
+                          usb_dev->descriptor.bDeviceSubClass,
+                          usb_dev->descriptor.bDeviceProtocol))
+               return -ENOMEM;
+
+       if (!is_usb_device(dev)) {
+
+               if (add_uevent_var(envp, num_envp, &i,
+                          buffer, buffer_size, &length,
+                          "INTERFACE=%d/%d/%d",
+                          alt->desc.bInterfaceClass,
+                          alt->desc.bInterfaceSubClass,
+                          alt->desc.bInterfaceProtocol))
+                       return -ENOMEM;
+
+               if (add_uevent_var(envp, num_envp, &i,
+                          buffer, buffer_size, &length,
+                          "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
+                          le16_to_cpu(usb_dev->descriptor.idVendor),
+                          le16_to_cpu(usb_dev->descriptor.idProduct),
+                          le16_to_cpu(usb_dev->descriptor.bcdDevice),
+                          usb_dev->descriptor.bDeviceClass,
+                          usb_dev->descriptor.bDeviceSubClass,
+                          usb_dev->descriptor.bDeviceProtocol,
+                          alt->desc.bInterfaceClass,
+                          alt->desc.bInterfaceSubClass,
+                          alt->desc.bInterfaceProtocol))
+                       return -ENOMEM;
+       }
+
+       envp[i] = NULL;
 
-       id = usb_match_dynamic_id(intf, usb_drv);
-       if (id)
-               return 1;
        return 0;
 }
 
+#else
+
+static int usb_uevent(struct device *dev, char **envp,
+                       int num_envp, char *buffer, int buffer_size)
+{
+       return -ENODEV;
+}
+
+#endif /* CONFIG_HOTPLUG */
+
+/**
+ * usb_register_device_driver - register a USB device (not interface) driver
+ * @new_udriver: USB operations for the device driver
+ * @owner: module owner of this driver.
+ *
+ * Registers a USB device driver with the USB core.  The list of
+ * unattached devices will be rescanned whenever a new driver is
+ * added, allowing the new driver to attach to any recognized devices.
+ * Returns a negative error code on failure and 0 on success.
+ */
+int usb_register_device_driver(struct usb_device_driver *new_udriver,
+               struct module *owner)
+{
+       int retval = 0;
+
+       if (usb_disabled())
+               return -ENODEV;
+
+       new_udriver->drvwrap.for_devices = 1;
+       new_udriver->drvwrap.driver.name = (char *) new_udriver->name;
+       new_udriver->drvwrap.driver.bus = &usb_bus_type;
+       new_udriver->drvwrap.driver.probe = usb_probe_device;
+       new_udriver->drvwrap.driver.remove = usb_unbind_device;
+       new_udriver->drvwrap.driver.owner = owner;
+
+       retval = driver_register(&new_udriver->drvwrap.driver);
+
+       if (!retval) {
+               pr_info("%s: registered new device driver %s\n",
+                       usbcore_name, new_udriver->name);
+               usbfs_update_special();
+       } else {
+               printk(KERN_ERR "%s: error %d registering device "
+                       "       driver %s\n",
+                       usbcore_name, retval, new_udriver->name);
+       }
+
+       return retval;
+}
+EXPORT_SYMBOL_GPL(usb_register_device_driver);
+
+/**
+ * usb_deregister_device_driver - unregister a USB device (not interface) driver
+ * @udriver: USB operations of the device driver to unregister
+ * Context: must be able to sleep
+ *
+ * Unlinks the specified driver from the internal USB driver list.
+ */
+void usb_deregister_device_driver(struct usb_device_driver *udriver)
+{
+       pr_info("%s: deregistering device driver %s\n",
+                       usbcore_name, udriver->name);
+
+       driver_unregister(&udriver->drvwrap.driver);
+       usbfs_update_special();
+}
+EXPORT_SYMBOL_GPL(usb_deregister_device_driver);
+
 /**
- * usb_register_driver - register a USB driver
- * @new_driver: USB operations for the driver
+ * usb_register_driver - register a USB interface driver
+ * @new_driver: USB operations for the interface driver
  * @owner: module owner of this driver.
  *
- * Registers a USB driver with the USB core.  The list of unattached
- * interfaces will be rescanned whenever a new driver is added, allowing
- * the new driver to attach to any recognized devices.
+ * Registers a USB interface driver with the USB core.  The list of
+ * unattached interfaces will be rescanned whenever a new driver is
+ * added, allowing the new driver to attach to any recognized interfaces.
  * Returns a negative error code on failure and 0 on success.
  *
  * NOTE: if you want your driver to use the USB major number, you must call
@@ -423,23 +736,25 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner)
        if (usb_disabled())
                return -ENODEV;
 
-       new_driver->driver.name = (char *)new_driver->name;
-       new_driver->driver.bus = &usb_bus_type;
-       new_driver->driver.probe = usb_probe_interface;
-       new_driver->driver.remove = usb_unbind_interface;
-       new_driver->driver.owner = owner;
+       new_driver->drvwrap.for_devices = 0;
+       new_driver->drvwrap.driver.name = (char *) new_driver->name;
+       new_driver->drvwrap.driver.bus = &usb_bus_type;
+       new_driver->drvwrap.driver.probe = usb_probe_interface;
+       new_driver->drvwrap.driver.remove = usb_unbind_interface;
+       new_driver->drvwrap.driver.owner = owner;
        spin_lock_init(&new_driver->dynids.lock);
        INIT_LIST_HEAD(&new_driver->dynids.list);
 
-       retval = driver_register(&new_driver->driver);
+       retval = driver_register(&new_driver->drvwrap.driver);
 
        if (!retval) {
-               pr_info("%s: registered new driver %s\n",
+               pr_info("%s: registered new interface driver %s\n",
                        usbcore_name, new_driver->name);
                usbfs_update_special();
                usb_create_newid_file(new_driver);
        } else {
-               printk(KERN_ERR "%s: error %d registering driver %s\n",
+               printk(KERN_ERR "%s: error %d registering interface "
+                       "       driver %s\n",
                        usbcore_name, retval, new_driver->name);
        }
 
@@ -448,8 +763,8 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner)
 EXPORT_SYMBOL_GPL_FUTURE(usb_register_driver);
 
 /**
- * usb_deregister - unregister a USB driver
- * @driver: USB operations of the driver to unregister
+ * usb_deregister - unregister a USB interface driver
+ * @driver: USB operations of the interface driver to unregister
  * Context: must be able to sleep
  *
  * Unlinks the specified driver from the internal USB driver list.
@@ -460,12 +775,554 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_register_driver);
  */
 void usb_deregister(struct usb_driver *driver)
 {
-       pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name);
+       pr_info("%s: deregistering interface driver %s\n",
+                       usbcore_name, driver->name);
 
        usb_remove_newid_file(driver);
        usb_free_dynids(driver);
-       driver_unregister(&driver->driver);
+       driver_unregister(&driver->drvwrap.driver);
 
        usbfs_update_special();
 }
 EXPORT_SYMBOL_GPL_FUTURE(usb_deregister);
+
+#ifdef CONFIG_PM
+
+/* Caller has locked udev->pm_mutex */
+static int suspend_device(struct usb_device *udev, pm_message_t msg)
+{
+       struct usb_device_driver        *udriver;
+       int                             status = 0;
+
+       if (udev->state == USB_STATE_NOTATTACHED ||
+                       udev->state == USB_STATE_SUSPENDED)
+               goto done;
+
+       /* For devices that don't have a driver, we do a standard suspend. */
+       if (udev->dev.driver == NULL) {
+               udev->do_remote_wakeup = 0;
+               status = usb_port_suspend(udev);
+               goto done;
+       }
+
+       udriver = to_usb_device_driver(udev->dev.driver);
+       status = udriver->suspend(udev, msg);
+
+done:
+       // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+       if (status == 0)
+               udev->dev.power.power_state.event = msg.event;
+       return status;
+}
+
+/* Caller has locked udev->pm_mutex */
+static int resume_device(struct usb_device *udev)
+{
+       struct usb_device_driver        *udriver;
+       int                             status = 0;
+
+       if (udev->state == USB_STATE_NOTATTACHED ||
+                       udev->state != USB_STATE_SUSPENDED)
+               goto done;
+
+       /* Can't resume it if it doesn't have a driver. */
+       if (udev->dev.driver == NULL) {
+               status = -ENOTCONN;
+               goto done;
+       }
+
+       udriver = to_usb_device_driver(udev->dev.driver);
+       status = udriver->resume(udev);
+
+done:
+       // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+       if (status == 0)
+               udev->dev.power.power_state.event = PM_EVENT_ON;
+       return status;
+}
+
+/* Caller has locked intf's usb_device's pm_mutex */
+static int suspend_interface(struct usb_interface *intf, pm_message_t msg)
+{
+       struct usb_driver       *driver;
+       int                     status = 0;
+
+       /* with no hardware, USB interfaces only use FREEZE and ON states */
+       if (interface_to_usbdev(intf)->state == USB_STATE_NOTATTACHED ||
+                       !is_active(intf))
+               goto done;
+
+       if (intf->condition == USB_INTERFACE_UNBOUND)   /* This can't happen */
+               goto done;
+       driver = to_usb_driver(intf->dev.driver);
+
+       if (driver->suspend && driver->resume) {
+               status = driver->suspend(intf, msg);
+               if (status == 0)
+                       mark_quiesced(intf);
+               else if (!interface_to_usbdev(intf)->auto_pm)
+                       dev_err(&intf->dev, "%s error %d\n",
+                                       "suspend", status);
+       } else {
+               // FIXME else if there's no suspend method, disconnect...
+               // Not possible if auto_pm is set...
+               dev_warn(&intf->dev, "no suspend for driver %s?\n",
+                               driver->name);
+               mark_quiesced(intf);
+       }
+
+done:
+       // dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status);
+       if (status == 0)
+               intf->dev.power.power_state.event = msg.event;
+       return status;
+}
+
+/* Caller has locked intf's usb_device's pm_mutex */
+static int resume_interface(struct usb_interface *intf)
+{
+       struct usb_driver       *driver;
+       int                     status = 0;
+
+       if (interface_to_usbdev(intf)->state == USB_STATE_NOTATTACHED ||
+                       is_active(intf))
+               goto done;
+
+       /* Don't let autoresume interfere with unbinding */
+       if (intf->condition == USB_INTERFACE_UNBINDING)
+               goto done;
+
+       /* Can't resume it if it doesn't have a driver. */
+       if (intf->condition == USB_INTERFACE_UNBOUND) {
+               status = -ENOTCONN;
+               goto done;
+       }
+       driver = to_usb_driver(intf->dev.driver);
+
+       if (driver->resume) {
+               status = driver->resume(intf);
+               if (status)
+                       dev_err(&intf->dev, "%s error %d\n",
+                                       "resume", status);
+               else
+                       mark_active(intf);
+       } else {
+               dev_warn(&intf->dev, "no resume for driver %s?\n",
+                               driver->name);
+               mark_active(intf);
+       }
+
+done:
+       // dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status);
+       if (status == 0)
+               intf->dev.power.power_state.event = PM_EVENT_ON;
+       return status;
+}
+
+/**
+ * usb_suspend_both - suspend a USB device and its interfaces
+ * @udev: the usb_device to suspend
+ * @msg: Power Management message describing this state transition
+ *
+ * This is the central routine for suspending USB devices.  It calls the
+ * suspend methods for all the interface drivers in @udev and then calls
+ * the suspend method for @udev itself.  If an error occurs at any stage,
+ * all the interfaces which were suspended are resumed so that they remain
+ * in the same state as the device.
+ *
+ * If an autosuspend is in progress (@udev->auto_pm is set), the routine
+ * checks first to make sure that neither the device itself or any of its
+ * active interfaces is in use (pm_usage_cnt is greater than 0).  If they
+ * are, the autosuspend fails.
+ *
+ * If the suspend succeeds, the routine recursively queues an autosuspend
+ * request for @udev's parent device, thereby propagating the change up
+ * the device tree.  If all of the parent's children are now suspended,
+ * the parent will autosuspend in turn.
+ *
+ * The suspend method calls are subject to mutual exclusion under control
+ * of @udev's pm_mutex.  Many of these calls are also under the protection
+ * of @udev's device lock (including all requests originating outside the
+ * USB subsystem), but autosuspend requests generated by a child device or
+ * interface driver may not be.  Usbcore will insure that the method calls
+ * do not arrive during bind, unbind, or reset operations.  However, drivers
+ * must be prepared to handle suspend calls arriving at unpredictable times.
+ * The only way to block such calls is to do an autoresume (preventing
+ * autosuspends) while holding @udev's device lock (preventing outside
+ * suspends).
+ *
+ * The caller must hold @udev->pm_mutex.
+ *
+ * This routine can run only in process context.
+ */
+int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
+{
+       int                     status = 0;
+       int                     i = 0;
+       struct usb_interface    *intf;
+       struct usb_device       *parent = udev->parent;
+
+       cancel_delayed_work(&udev->autosuspend);
+       if (udev->state == USB_STATE_NOTATTACHED)
+               return 0;
+       if (udev->state == USB_STATE_SUSPENDED)
+               return 0;
+
+       udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
+
+       /* For autosuspend, fail fast if anything is in use.
+        * Also fail if any interfaces require remote wakeup but it
+        * isn't available. */
+       if (udev->auto_pm) {
+               if (udev->pm_usage_cnt > 0)
+                       return -EBUSY;
+               if (udev->actconfig) {
+                       for (; i < udev->actconfig->desc.bNumInterfaces; i++) {
+                               intf = udev->actconfig->interface[i];
+                               if (!is_active(intf))
+                                       continue;
+                               if (intf->pm_usage_cnt > 0)
+                                       return -EBUSY;
+                               if (intf->needs_remote_wakeup &&
+                                               !udev->do_remote_wakeup) {
+                                       dev_dbg(&udev->dev,
+       "remote wakeup needed for autosuspend\n");
+                                       return -EOPNOTSUPP;
+                               }
+                       }
+                       i = 0;
+               }
+       }
+
+       /* Suspend all the interfaces and then udev itself */
+       if (udev->actconfig) {
+               for (; i < udev->actconfig->desc.bNumInterfaces; i++) {
+                       intf = udev->actconfig->interface[i];
+                       status = suspend_interface(intf, msg);
+                       if (status != 0)
+                               break;
+               }
+       }
+       if (status == 0)
+               status = suspend_device(udev, msg);
+
+       /* If the suspend failed, resume interfaces that did get suspended */
+       if (status != 0) {
+               while (--i >= 0) {
+                       intf = udev->actconfig->interface[i];
+                       resume_interface(intf);
+               }
+
+       /* If the suspend succeeded, propagate it up the tree */
+       } else if (parent)
+               usb_autosuspend_device(parent, 0);
+
+       // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+       return status;
+}
+
+/**
+ * usb_resume_both - resume a USB device and its interfaces
+ * @udev: the usb_device to resume
+ *
+ * This is the central routine for resuming USB devices.  It calls the
+ * the resume method for @udev and then calls the resume methods for all
+ * the interface drivers in @udev.
+ *
+ * Before starting the resume, the routine calls itself recursively for
+ * the parent device of @udev, thereby propagating the change up the device
+ * tree and assuring that @udev will be able to resume.  If the parent is
+ * unable to resume successfully, the routine fails.
+ *
+ * The resume method calls are subject to mutual exclusion under control
+ * of @udev's pm_mutex.  Many of these calls are also under the protection
+ * of @udev's device lock (including all requests originating outside the
+ * USB subsystem), but autoresume requests generated by a child device or
+ * interface driver may not be.  Usbcore will insure that the method calls
+ * do not arrive during bind, unbind, or reset operations.  However, drivers
+ * must be prepared to handle resume calls arriving at unpredictable times.
+ * The only way to block such calls is to do an autoresume (preventing
+ * other autoresumes) while holding @udev's device lock (preventing outside
+ * resumes).
+ *
+ * The caller must hold @udev->pm_mutex.
+ *
+ * This routine can run only in process context.
+ */
+int usb_resume_both(struct usb_device *udev)
+{
+       int                     status = 0;
+       int                     i;
+       struct usb_interface    *intf;
+       struct usb_device       *parent = udev->parent;
+
+       cancel_delayed_work(&udev->autosuspend);
+       if (udev->state == USB_STATE_NOTATTACHED)
+               return -ENODEV;
+
+       /* Propagate the resume up the tree, if necessary */
+       if (udev->state == USB_STATE_SUSPENDED) {
+               if (parent) {
+                       mutex_lock_nested(&parent->pm_mutex, parent->level);
+                       parent->auto_pm = 1;
+                       status = usb_resume_both(parent);
+               } else {
+
+                       /* We can't progagate beyond the USB subsystem,
+                        * so if a root hub's controller is suspended
+                        * then we're stuck. */
+                       if (udev->dev.parent->power.power_state.event !=
+                                       PM_EVENT_ON)
+                               status = -EHOSTUNREACH;
+               }
+               if (status == 0)
+                       status = resume_device(udev);
+               if (parent)
+                       mutex_unlock(&parent->pm_mutex);
+       } else {
+
+               /* Needed only for setting udev->dev.power.power_state.event
+                * and for possible debugging message. */
+               status = resume_device(udev);
+       }
+
+       /* Now the parent won't suspend until we are finished */
+
+       if (status == 0 && udev->actconfig) {
+               for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
+                       intf = udev->actconfig->interface[i];
+                       resume_interface(intf);
+               }
+       }
+
+       // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+       return status;
+}
+
+#ifdef CONFIG_USB_SUSPEND
+
+/**
+ * usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces
+ * @udev - the usb_device to autosuspend
+ * @dec_usage_cnt - flag to decrement @udev's PM-usage counter
+ *
+ * This routine should be called when a core subsystem is finished using
+ * @udev and wants to allow it to autosuspend.  Examples would be when
+ * @udev's device file in usbfs is closed or after a configuration change.
+ *
+ * @dec_usage_cnt should be 1 if the subsystem previously incremented
+ * @udev's usage counter (such as by passing 1 to usb_autoresume_device);
+ * otherwise it should be 0.
+ *
+ * If the usage counter for @udev or any of its active interfaces is greater
+ * than 0, the autosuspend request will not be queued.  (If an interface
+ * driver does not support autosuspend then its usage counter is permanently
+ * positive.)  Likewise, if an interface driver requires remote-wakeup
+ * capability during autosuspend but remote wakeup is disabled, the
+ * autosuspend will fail.
+ *
+ * Often the caller will hold @udev's device lock, but this is not
+ * necessary.
+ *
+ * This routine can run only in process context.
+ */
+void usb_autosuspend_device(struct usb_device *udev, int dec_usage_cnt)
+{
+       mutex_lock_nested(&udev->pm_mutex, udev->level);
+       udev->pm_usage_cnt -= dec_usage_cnt;
+       if (udev->pm_usage_cnt <= 0)
+               schedule_delayed_work(&udev->autosuspend,
+                               USB_AUTOSUSPEND_DELAY);
+       mutex_unlock(&udev->pm_mutex);
+       // dev_dbg(&udev->dev, "%s: cnt %d\n",
+       //              __FUNCTION__, udev->pm_usage_cnt);
+}
+
+/**
+ * usb_autoresume_device - immediately autoresume a USB device and its interfaces
+ * @udev - the usb_device to autoresume
+ * @inc_usage_cnt - flag to increment @udev's PM-usage counter
+ *
+ * This routine should be called when a core subsystem wants to use @udev
+ * and needs to guarantee that it is not suspended.  In addition, the
+ * caller can prevent @udev from being autosuspended subsequently.  (Note
+ * that this will not prevent suspend events originating in the PM core.)
+ * Examples would be when @udev's device file in usbfs is opened (autosuspend
+ * should be prevented until the file is closed) or when a remote-wakeup
+ * request is received (later autosuspends should not be prevented).
+ *
+ * @inc_usage_cnt should be 1 to increment @udev's usage counter and prevent
+ * autosuspends.  This prevention will persist until the usage counter is
+ * decremented again (such as by passing 1 to usb_autosuspend_device).
+ * Otherwise @inc_usage_cnt should be 0 to leave the usage counter unchanged.
+ * Regardless, if the autoresume fails then the usage counter is not
+ * incremented.
+ *
+ * Often the caller will hold @udev's device lock, but this is not
+ * necessary (and attempting it might cause deadlock).
+ *
+ * This routine can run only in process context.
+ */
+int usb_autoresume_device(struct usb_device *udev, int inc_usage_cnt)
+{
+       int     status;
+
+       mutex_lock_nested(&udev->pm_mutex, udev->level);
+       udev->pm_usage_cnt += inc_usage_cnt;
+       udev->auto_pm = 1;
+       status = usb_resume_both(udev);
+       if (status != 0)
+               udev->pm_usage_cnt -= inc_usage_cnt;
+       mutex_unlock(&udev->pm_mutex);
+       // dev_dbg(&udev->dev, "%s: status %d cnt %d\n",
+       //              __FUNCTION__, status, udev->pm_usage_cnt);
+       return status;
+}
+
+/**
+ * usb_autopm_put_interface - decrement a USB interface's PM-usage counter
+ * @intf - the usb_interface whose counter should be decremented
+ *
+ * This routine should be called by an interface driver when it is
+ * finished using @intf and wants to allow it to autosuspend.  A typical
+ * example would be a character-device driver when its device file is
+ * closed.
+ *
+ * The routine decrements @intf's usage counter.  When the counter reaches
+ * 0, a delayed autosuspend request for @intf's device is queued.  When
+ * the delay expires, if @intf->pm_usage_cnt is still <= 0 along with all
+ * the other usage counters for the sibling interfaces and @intf's
+ * usb_device, the device and all its interfaces will be autosuspended.
+ *
+ * Note that @intf->pm_usage_cnt is owned by the interface driver.  The
+ * core will not change its value other than the increment and decrement
+ * in usb_autopm_get_interface and usb_autopm_put_interface.  The driver
+ * may use this simple counter-oriented discipline or may set the value
+ * any way it likes.
+ *
+ * If the driver has set @intf->needs_remote_wakeup then autosuspend will
+ * take place only if the device's remote-wakeup facility is enabled.
+ *
+ * Suspend method calls queued by this routine can arrive at any time
+ * while @intf is resumed and its usage counter is equal to 0.  They are
+ * not protected by the usb_device's lock but only by its pm_mutex.
+ * Drivers must provide their own synchronization.
+ *
+ * This routine can run only in process context.
+ */
+void usb_autopm_put_interface(struct usb_interface *intf)
+{
+       struct usb_device       *udev = interface_to_usbdev(intf);
+
+       mutex_lock_nested(&udev->pm_mutex, udev->level);
+       if (intf->condition != USB_INTERFACE_UNBOUND) {
+               if (--intf->pm_usage_cnt <= 0)
+                       schedule_delayed_work(&udev->autosuspend,
+                                       USB_AUTOSUSPEND_DELAY);
+       }
+       mutex_unlock(&udev->pm_mutex);
+       // dev_dbg(&intf->dev, "%s: cnt %d\n",
+       //              __FUNCTION__, intf->pm_usage_cnt);
+}
+EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
+
+/**
+ * usb_autopm_get_interface - increment a USB interface's PM-usage counter
+ * @intf - the usb_interface whose counter should be incremented
+ *
+ * This routine should be called by an interface driver when it wants to
+ * use @intf and needs to guarantee that it is not suspended.  In addition,
+ * the routine prevents @intf from being autosuspended subsequently.  (Note
+ * that this will not prevent suspend events originating in the PM core.)
+ * This prevention will persist until usb_autopm_put_interface() is called
+ * or @intf is unbound.  A typical example would be a character-device
+ * driver when its device file is opened.
+ *
+ * The routine increments @intf's usage counter.  So long as the counter
+ * is greater than 0, autosuspend will not be allowed for @intf or its
+ * usb_device.  When the driver is finished using @intf it should call
+ * usb_autopm_put_interface() to decrement the usage counter and queue
+ * a delayed autosuspend request (if the counter is <= 0).
+ *
+ * Note that @intf->pm_usage_cnt is owned by the interface driver.  The
+ * core will not change its value other than the increment and decrement
+ * in usb_autopm_get_interface and usb_autopm_put_interface.  The driver
+ * may use this simple counter-oriented discipline or may set the value
+ * any way it likes.
+ *
+ * Resume method calls generated by this routine can arrive at any time
+ * while @intf is suspended.  They are not protected by the usb_device's
+ * lock but only by its pm_mutex.  Drivers must provide their own
+ * synchronization.
+ *
+ * This routine can run only in process context.
+ */
+int usb_autopm_get_interface(struct usb_interface *intf)
+{
+       struct usb_device       *udev = interface_to_usbdev(intf);
+       int                     status;
+
+       mutex_lock_nested(&udev->pm_mutex, udev->level);
+       if (intf->condition == USB_INTERFACE_UNBOUND)
+               status = -ENODEV;
+       else {
+               ++intf->pm_usage_cnt;
+               udev->auto_pm = 1;
+               status = usb_resume_both(udev);
+               if (status != 0)
+                       --intf->pm_usage_cnt;
+       }
+       mutex_unlock(&udev->pm_mutex);
+       // dev_dbg(&intf->dev, "%s: status %d cnt %d\n",
+       //              __FUNCTION__, status, intf->pm_usage_cnt);
+       return status;
+}
+EXPORT_SYMBOL_GPL(usb_autopm_get_interface);
+
+#endif /* CONFIG_USB_SUSPEND */
+
+static int usb_suspend(struct device *dev, pm_message_t message)
+{
+       int     status;
+
+       if (is_usb_device(dev)) {
+               struct usb_device *udev = to_usb_device(dev);
+
+               mutex_lock_nested(&udev->pm_mutex, udev->level);
+               udev->auto_pm = 0;
+               status = usb_suspend_both(udev, message);
+               mutex_unlock(&udev->pm_mutex);
+       } else
+               status = 0;
+       return status;
+}
+
+static int usb_resume(struct device *dev)
+{
+       int     status;
+
+       if (is_usb_device(dev)) {
+               struct usb_device *udev = to_usb_device(dev);
+
+               mutex_lock_nested(&udev->pm_mutex, udev->level);
+               udev->auto_pm = 0;
+               status = usb_resume_both(udev);
+               mutex_unlock(&udev->pm_mutex);
+
+               /* Rebind drivers that had no suspend method? */
+       } else
+               status = 0;
+       return status;
+}
+
+#endif /* CONFIG_PM */
+
+struct bus_type usb_bus_type = {
+       .name =         "usb",
+       .match =        usb_device_match,
+       .uevent =       usb_uevent,
+#ifdef CONFIG_PM
+       .suspend =      usb_suspend,
+       .resume =       usb_resume,
+#endif
+};
index 247b5a4..3ebb901 100644 (file)
@@ -207,9 +207,9 @@ static void ep_device_release(struct device *dev)
        kfree(ep_dev);
 }
 
-void usb_create_ep_files(struct device *parent,
-                        struct usb_host_endpoint *endpoint,
-                        struct usb_device *udev)
+int usb_create_ep_files(struct device *parent,
+                       struct usb_host_endpoint *endpoint,
+                       struct usb_device *udev)
 {
        char name[8];
        struct ep_device *ep_dev;
@@ -242,19 +242,33 @@ void usb_create_ep_files(struct device *parent,
        retval = device_register(&ep_dev->dev);
        if (retval)
                goto error;
-       sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
+       retval = sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
+       if (retval)
+               goto error_group;
 
        endpoint->ep_dev = ep_dev;
 
        /* create the symlink to the old-style "ep_XX" directory */
        sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
-       sysfs_create_link(&parent->kobj, &endpoint->ep_dev->dev.kobj, name);
-
+       retval = sysfs_create_link(&parent->kobj,
+                                  &endpoint->ep_dev->dev.kobj, name);
+       if (retval)
+               goto error_link;
 exit:
-       return;
+       return retval;
+
+error_link:
+       sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
+
+error_group:
+       device_unregister(&ep_dev->dev);
+       endpoint->ep_dev = NULL;
+       destroy_endpoint_class();
+       return retval;
 error:
        kfree(ep_dev);
-       return;
+       destroy_endpoint_class();
+       return retval;
 }
 
 void usb_remove_ep_files(struct usb_host_endpoint *endpoint)
index 8de4f8c..c376c65 100644 (file)
@@ -55,7 +55,7 @@ static int usb_open(struct inode * inode, struct file * file)
        return err;
 }
 
-static struct file_operations usb_fops = {
+static const struct file_operations usb_fops = {
        .owner =        THIS_MODULE,
        .open =         usb_open,
 };
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
new file mode 100644 (file)
index 0000000..16332cc
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * drivers/usb/generic.c - generic driver for USB devices (not interfaces)
+ *
+ * (C) Copyright 2005 Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * based on drivers/usb/usb.c which had the following copyrights:
+ *     (C) Copyright Linus Torvalds 1999
+ *     (C) Copyright Johannes Erdfelt 1999-2001
+ *     (C) Copyright Andreas Gal 1999
+ *     (C) Copyright Gregory P. Smith 1999
+ *     (C) Copyright Deti Fliegl 1999 (new USB architecture)
+ *     (C) Copyright Randy Dunlap 2000
+ *     (C) Copyright David Brownell 2000-2004
+ *     (C) Copyright Yggdrasil Computing, Inc. 2000
+ *             (usb_device_id matching changes by Adam J. Richter)
+ *     (C) Copyright Greg Kroah-Hartman 2002-2003
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/usb.h>
+#include "usb.h"
+
+static inline const char *plural(int n)
+{
+       return (n == 1 ? "" : "s");
+}
+
+static int choose_configuration(struct usb_device *udev)
+{
+       int i;
+       int num_configs;
+       int insufficient_power = 0;
+       struct usb_host_config *c, *best;
+
+       best = NULL;
+       c = udev->config;
+       num_configs = udev->descriptor.bNumConfigurations;
+       for (i = 0; i < num_configs; (i++, c++)) {
+               struct usb_interface_descriptor *desc = NULL;
+
+               /* It's possible that a config has no interfaces! */
+               if (c->desc.bNumInterfaces > 0)
+                       desc = &c->intf_cache[0]->altsetting->desc;
+
+               /*
+                * HP's USB bus-powered keyboard has only one configuration
+                * and it claims to be self-powered; other devices may have
+                * similar errors in their descriptors.  If the next test
+                * were allowed to execute, such configurations would always
+                * be rejected and the devices would not work as expected.
+                * In the meantime, we run the risk of selecting a config
+                * that requires external power at a time when that power
+                * isn't available.  It seems to be the lesser of two evils.
+                *
+                * Bugzilla #6448 reports a device that appears to crash
+                * when it receives a GET_DEVICE_STATUS request!  We don't
+                * have any other way to tell whether a device is self-powered,
+                * but since we don't use that information anywhere but here,
+                * the call has been removed.
+                *
+                * Maybe the GET_DEVICE_STATUS call and the test below can
+                * be reinstated when device firmwares become more reliable.
+                * Don't hold your breath.
+                */
+#if 0
+               /* Rule out self-powered configs for a bus-powered device */
+               if (bus_powered && (c->desc.bmAttributes &
+                                       USB_CONFIG_ATT_SELFPOWER))
+                       continue;
+#endif
+
+               /*
+                * The next test may not be as effective as it should be.
+                * Some hubs have errors in their descriptor, claiming
+                * to be self-powered when they are really bus-powered.
+                * We will overestimate the amount of current such hubs
+                * make available for each port.
+                *
+                * This is a fairly benign sort of failure.  It won't
+                * cause us to reject configurations that we should have
+                * accepted.
+                */
+
+               /* Rule out configs that draw too much bus current */
+               if (c->desc.bMaxPower * 2 > udev->bus_mA) {
+                       insufficient_power++;
+                       continue;
+               }
+
+               /* If the first config's first interface is COMM/2/0xff
+                * (MSFT RNDIS), rule it out unless Linux has host-side
+                * RNDIS support. */
+               if (i == 0 && desc
+                               && desc->bInterfaceClass == USB_CLASS_COMM
+                               && desc->bInterfaceSubClass == 2
+                               && desc->bInterfaceProtocol == 0xff) {
+#ifndef CONFIG_USB_NET_RNDIS_HOST
+                       continue;
+#else
+                       best = c;
+#endif
+               }
+
+               /* From the remaining configs, choose the first one whose
+                * first interface is for a non-vendor-specific class.
+                * Reason: Linux is more likely to have a class driver
+                * than a vendor-specific driver. */
+               else if (udev->descriptor.bDeviceClass !=
+                                               USB_CLASS_VENDOR_SPEC &&
+                               (!desc || desc->bInterfaceClass !=
+                                               USB_CLASS_VENDOR_SPEC)) {
+                       best = c;
+                       break;
+               }
+
+               /* If all the remaining configs are vendor-specific,
+                * choose the first one. */
+               else if (!best)
+                       best = c;
+       }
+
+       if (insufficient_power > 0)
+               dev_info(&udev->dev, "rejected %d configuration%s "
+                       "due to insufficient available bus power\n",
+                       insufficient_power, plural(insufficient_power));
+
+       if (best) {
+               i = best->desc.bConfigurationValue;
+               dev_info(&udev->dev,
+                       "configuration #%d chosen from %d choice%s\n",
+                       i, num_configs, plural(num_configs));
+       } else {
+               i = -1;
+               dev_warn(&udev->dev,
+                       "no configuration chosen from %d choice%s\n",
+                       num_configs, plural(num_configs));
+       }
+       return i;
+}
+
+static int generic_probe(struct usb_device *udev)
+{
+       int err, c;
+
+       /* put device-specific files into sysfs */
+       usb_create_sysfs_dev_files(udev);
+
+       /* Choose and set the configuration.  This registers the interfaces
+        * with the driver core and lets interface drivers bind to them.
+        */
+       c = choose_configuration(udev);
+       if (c >= 0) {
+               err = usb_set_configuration(udev, c);
+               if (err) {
+                       dev_err(&udev->dev, "can't set config #%d, error %d\n",
+                                       c, err);
+                       /* This need not be fatal.  The user can try to
+                        * set other configurations. */
+               }
+       }
+
+       /* USB device state == configured ... usable */
+       usb_notify_add_device(udev);
+
+       return 0;
+}
+
+static void generic_disconnect(struct usb_device *udev)
+{
+       usb_notify_remove_device(udev);
+
+       /* if this is only an unbind, not a physical disconnect, then
+        * unconfigure the device */
+       if (udev->actconfig)
+               usb_set_configuration(udev, 0);
+
+       usb_remove_sysfs_dev_files(udev);
+}
+
+#ifdef CONFIG_PM
+
+static int generic_suspend(struct usb_device *udev, pm_message_t msg)
+{
+       /* USB devices enter SUSPEND state through their hubs, but can be
+        * marked for FREEZE as soon as their children are already idled.
+        * But those semantics are useless, so we equate the two (sigh).
+        */
+       return usb_port_suspend(udev);
+}
+
+static int generic_resume(struct usb_device *udev)
+{
+       return usb_port_resume(udev);
+}
+
+#endif /* CONFIG_PM */
+
+struct usb_device_driver usb_generic_driver = {
+       .name = "usb",
+       .probe = generic_probe,
+       .disconnect = generic_disconnect,
+#ifdef CONFIG_PM
+       .suspend = generic_suspend,
+       .resume = generic_resume,
+#endif
+       .supports_autosuspend = 1,
+};
index fa36391..edf4300 100644 (file)
@@ -413,4 +413,20 @@ EXPORT_SYMBOL (usb_hcd_pci_resume);
 
 #endif /* CONFIG_PM */
 
+/**
+ * usb_hcd_pci_shutdown - shutdown host controller
+ * @dev: USB Host Controller being shutdown
+ */
+void usb_hcd_pci_shutdown (struct pci_dev *dev)
+{
+       struct usb_hcd          *hcd;
+
+       hcd = pci_get_drvdata(dev);
+       if (!hcd)
+               return;
+
+       if (hcd->driver->shutdown)
+               hcd->driver->shutdown(hcd);
+}
+EXPORT_SYMBOL (usb_hcd_pci_shutdown);
 
index fb4d058..e86f629 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/mutex.h>
 #include <asm/irq.h>
 #include <asm/byteorder.h>
+#include <linux/platform_device.h>
 
 #include <linux/usb.h>
 
@@ -632,31 +633,20 @@ static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
 
 /*-------------------------------------------------------------------------*/
 
-/* 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.
+/* Unlinks of root-hub control URBs are legal, but they don't do anything
+ * since these URBs always execute synchronously.
  */
 static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
 {
-       if (usb_pipeendpoint(urb->pipe) == 0) { /* Control URB */
-               if (in_interrupt())
-                       return 0;               /* nothing to do */
-
-               spin_lock_irq(&urb->lock);      /* from usb_kill_urb */
-               ++urb->reject;
-               spin_unlock_irq(&urb->lock);
-
-               wait_event(usb_kill_urb_queue,
-                               atomic_read(&urb->use_count) == 0);
+       unsigned long   flags;
 
-               spin_lock_irq(&urb->lock);
-               --urb->reject;
-               spin_unlock_irq(&urb->lock);
+       if (usb_pipeendpoint(urb->pipe) == 0) { /* Control URB */
+               ;       /* Do nothing */
 
        } else {                                /* Status URB */
                if (!hcd->uses_new_polling)
-                       del_timer_sync (&hcd->rh_timer);
-               local_irq_disable ();
+                       del_timer (&hcd->rh_timer);
+               local_irq_save (flags);
                spin_lock (&hcd_root_hub_lock);
                if (urb == hcd->status_urb) {
                        hcd->status_urb = NULL;
@@ -666,7 +656,7 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
                spin_unlock (&hcd_root_hub_lock);
                if (urb)
                        usb_hcd_giveback_urb (hcd, urb, NULL);
-               local_irq_enable ();
+               local_irq_restore (flags);
        }
 
        return 0;
@@ -674,31 +664,6 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
 
 /*-------------------------------------------------------------------------*/
 
-/* exported only within usbcore */
-struct usb_bus *usb_bus_get(struct usb_bus *bus)
-{
-       if (bus)
-               kref_get(&bus->kref);
-       return bus;
-}
-
-static void usb_host_release(struct kref *kref)
-{
-       struct usb_bus *bus = container_of(kref, struct usb_bus, kref);
-
-       if (bus->release)
-               bus->release(bus);
-}
-
-/* exported only within usbcore */
-void usb_bus_put(struct usb_bus *bus)
-{
-       if (bus)
-               kref_put(&bus->kref, usb_host_release);
-}
-
-/*-------------------------------------------------------------------------*/
-
 static struct class *usb_host_class;
 
 int usb_host_init(void)
@@ -730,39 +695,12 @@ static void usb_bus_init (struct usb_bus *bus)
        bus->devnum_next = 1;
 
        bus->root_hub = NULL;
-       bus->hcpriv = NULL;
        bus->busnum = -1;
        bus->bandwidth_allocated = 0;
        bus->bandwidth_int_reqs  = 0;
        bus->bandwidth_isoc_reqs = 0;
 
        INIT_LIST_HEAD (&bus->bus_list);
-
-       kref_init(&bus->kref);
-}
-
-/**
- * usb_alloc_bus - creates a new USB host controller structure
- * @op: pointer to a struct usb_operations that this bus structure should use
- * Context: !in_interrupt()
- *
- * Creates a USB host controller bus structure with the specified 
- * usb_operations and initializes all the necessary internal objects.
- *
- * If no memory is available, NULL is returned.
- *
- * The caller should call usb_put_bus() when it is finished with the structure.
- */
-struct usb_bus *usb_alloc_bus (struct usb_operations *op)
-{
-       struct usb_bus *bus;
-
-       bus = kzalloc (sizeof *bus, GFP_KERNEL);
-       if (!bus)
-               return NULL;
-       usb_bus_init (bus);
-       bus->op = op;
-       return bus;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1112,10 +1050,10 @@ static void urb_unlink (struct urb *urb)
  * expects usb_submit_urb() to have sanity checked and conditioned all
  * inputs in the urb
  */
-static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
+int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
 {
        int                     status;
-       struct usb_hcd          *hcd = urb->dev->bus->hcpriv;
+       struct usb_hcd          *hcd = bus_to_hcd(urb->dev->bus);
        struct usb_host_endpoint *ep;
        unsigned long           flags;
 
@@ -1186,7 +1124,7 @@ doit:
        /* lower level hcd code should use *_dma exclusively,
         * unless it uses pio or talks to another transport.
         */
-       if (hcd->self.controller->dma_mask) {
+       if (hcd->self.uses_dma) {
                if (usb_pipecontrol (urb->pipe)
                        && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
                        urb->setup_dma = dma_map_single (
@@ -1221,9 +1159,10 @@ done:
 /*-------------------------------------------------------------------------*/
 
 /* called in any context */
-static int hcd_get_frame_number (struct usb_device *udev)
+int usb_hcd_get_frame_number (struct usb_device *udev)
 {
-       struct usb_hcd  *hcd = (struct usb_hcd *)udev->bus->hcpriv;
+       struct usb_hcd  *hcd = bus_to_hcd(udev->bus);
+
        if (!HC_IS_RUNNING (hcd->state))
                return -ESHUTDOWN;
        return hcd->driver->get_frame_number (hcd);
@@ -1263,7 +1202,7 @@ unlink1 (struct usb_hcd *hcd, struct urb *urb)
  * caller guarantees urb won't be recycled till both unlink()
  * and the urb's completion function return
  */
-static int hcd_unlink_urb (struct urb *urb, int status)
+int usb_hcd_unlink_urb (struct urb *urb, int status)
 {
        struct usb_host_endpoint        *ep;
        struct usb_hcd                  *hcd = NULL;
@@ -1296,7 +1235,7 @@ static int hcd_unlink_urb (struct urb *urb, int status)
        spin_lock (&hcd_data_lock);
 
        sys = &urb->dev->dev;
-       hcd = urb->dev->bus->hcpriv;
+       hcd = bus_to_hcd(urb->dev->bus);
        if (hcd == NULL) {
                retval = -ENODEV;
                goto done;
@@ -1354,41 +1293,33 @@ done:
 /*-------------------------------------------------------------------------*/
 
 /* disables the endpoint: cancels any pending urbs, then synchronizes with
- * the hcd to make sure all endpoint state is gone from hardware. use for
+ * the hcd to make sure all endpoint state is gone from hardware, and then
+ * waits until the endpoint's queue is completely drained. use for
  * set_configuration, set_interface, driver removal, physical disconnect.
  *
  * example:  a qh stored in ep->hcpriv, holding state related to endpoint
  * type, maxpacket size, toggle, halt status, and scheduling.
  */
-static void
-hcd_endpoint_disable (struct usb_device *udev, struct usb_host_endpoint *ep)
+void usb_hcd_endpoint_disable (struct usb_device *udev,
+               struct usb_host_endpoint *ep)
 {
        struct usb_hcd          *hcd;
        struct urb              *urb;
 
-       hcd = udev->bus->hcpriv;
+       hcd = bus_to_hcd(udev->bus);
 
        WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT &&
                        udev->state != USB_STATE_NOTATTACHED);
 
        local_irq_disable ();
 
-       /* FIXME move most of this into message.c as part of its
-        * endpoint disable logic
-        */
-
        /* ep is already gone from udev->ep_{in,out}[]; no more submits */
 rescan:
        spin_lock (&hcd_data_lock);
        list_for_each_entry (urb, &ep->urb_list, urb_list) {
                int     tmp;
 
-               /* another cpu may be in hcd, spinning on hcd_data_lock
-                * to giveback() this urb.  the races here should be
-                * small, but a full fix needs a new "can't submit"
-                * urb state.
-                * FIXME urb->reject should allow that...
-                */
+               /* the urb may already have been unlinked */
                if (urb->status != -EINPROGRESS)
                        continue;
                usb_get_urb (urb);
@@ -1430,6 +1361,30 @@ rescan:
        might_sleep ();
        if (hcd->driver->endpoint_disable)
                hcd->driver->endpoint_disable (hcd, ep);
+
+       /* Wait until the endpoint queue is completely empty.  Most HCDs
+        * will have done this already in their endpoint_disable method,
+        * but some might not.  And there could be root-hub control URBs
+        * still pending since they aren't affected by the HCDs'
+        * endpoint_disable methods.
+        */
+       while (!list_empty (&ep->urb_list)) {
+               spin_lock_irq (&hcd_data_lock);
+
+               /* The list may have changed while we acquired the spinlock */
+               urb = NULL;
+               if (!list_empty (&ep->urb_list)) {
+                       urb = list_entry (ep->urb_list.prev, struct urb,
+                                       urb_list);
+                       usb_get_urb (urb);
+               }
+               spin_unlock_irq (&hcd_data_lock);
+
+               if (urb) {
+                       usb_kill_urb (urb);
+                       usb_put_urb (urb);
+               }
+       }
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1476,50 +1431,6 @@ int hcd_bus_resume (struct usb_bus *bus)
        return status;
 }
 
-/*
- * usb_hcd_suspend_root_hub - HCD autosuspends downstream ports
- * @hcd: host controller for this root hub
- *
- * This call arranges that usb_hcd_resume_root_hub() is safe to call later;
- * that the HCD's root hub polling is deactivated; and that the root's hub
- * driver is suspended.  HCDs may call this to autosuspend when their root
- * hub's downstream ports are all inactive:  unpowered, disconnected,
- * disabled, or suspended.
- *
- * The HCD will autoresume on device connect change detection (using SRP
- * or a D+/D- pullup).  The HCD also autoresumes on remote wakeup signaling
- * from any ports that are suspended (if that is enabled).  In most cases,
- * overcurrent signaling (on powered ports) will also start autoresume.
- *
- * Always called with IRQs blocked.
- */
-void usb_hcd_suspend_root_hub (struct usb_hcd *hcd)
-{
-       struct urb      *urb;
-
-       spin_lock (&hcd_root_hub_lock);
-       usb_suspend_root_hub (hcd->self.root_hub);
-
-       /* force status urb to complete/unlink while suspended */
-       if (hcd->status_urb) {
-               urb = hcd->status_urb;
-               urb->status = -ECONNRESET;
-               urb->hcpriv = NULL;
-               urb->actual_length = 0;
-
-               del_timer (&hcd->rh_timer);
-               hcd->poll_pending = 0;
-               hcd->status_urb = NULL;
-       } else
-               urb = NULL;
-       spin_unlock (&hcd_root_hub_lock);
-       hcd->state = HC_STATE_SUSPENDED;
-
-       if (urb)
-               usb_hcd_giveback_urb (hcd, urb, NULL);
-}
-EXPORT_SYMBOL_GPL(usb_hcd_suspend_root_hub);
-
 /**
  * usb_hcd_resume_root_hub - called by HCD to resume its root hub 
  * @hcd: host controller for this root hub
@@ -1583,20 +1494,6 @@ EXPORT_SYMBOL (usb_bus_start_enum);
 
 /*-------------------------------------------------------------------------*/
 
-/*
- * usb_hcd_operations - adapts usb_bus framework to HCD framework (bus glue)
- */
-static struct usb_operations usb_hcd_operations = {
-       .get_frame_number =     hcd_get_frame_number,
-       .submit_urb =           hcd_submit_urb,
-       .unlink_urb =           hcd_unlink_urb,
-       .buffer_alloc =         hcd_buffer_alloc,
-       .buffer_free =          hcd_buffer_free,
-       .disable =              hcd_endpoint_disable,
-};
-
-/*-------------------------------------------------------------------------*/
-
 /**
  * usb_hcd_giveback_urb - return URB from HCD to device driver
  * @hcd: host controller returning the URB
@@ -1617,8 +1514,9 @@ void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs
        at_root_hub = (urb->dev == hcd->self.root_hub);
        urb_unlink (urb);
 
-       /* lower level hcd code should use *_dma exclusively */
-       if (hcd->self.controller->dma_mask && !at_root_hub) {
+       /* lower level hcd code should use *_dma exclusively if the
+        * host controller does DMA */
+       if (hcd->self.uses_dma && !at_root_hub) {
                if (usb_pipecontrol (urb->pipe)
                        && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
                        dma_unmap_single (hcd->self.controller, urb->setup_dma,
@@ -1704,14 +1602,6 @@ EXPORT_SYMBOL_GPL (usb_hc_died);
 
 /*-------------------------------------------------------------------------*/
 
-static void hcd_release (struct usb_bus *bus)
-{
-       struct usb_hcd *hcd;
-
-       hcd = container_of(bus, struct usb_hcd, self);
-       kfree(hcd);
-}
-
 /**
  * usb_create_hcd - create and initialize an HCD structure
  * @driver: HC driver that will use this hcd
@@ -1736,13 +1626,12 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
                return NULL;
        }
        dev_set_drvdata(dev, hcd);
+       kref_init(&hcd->kref);
 
        usb_bus_init(&hcd->self);
-       hcd->self.op = &usb_hcd_operations;
-       hcd->self.hcpriv = hcd;
-       hcd->self.release = &hcd_release;
        hcd->self.controller = dev;
        hcd->self.bus_name = bus_name;
+       hcd->self.uses_dma = (dev->dma_mask != NULL);
 
        init_timer(&hcd->rh_timer);
        hcd->rh_timer.function = rh_timer_func;
@@ -1756,10 +1645,25 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
 }
 EXPORT_SYMBOL (usb_create_hcd);
 
+static void hcd_release (struct kref *kref)
+{
+       struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref);
+
+       kfree(hcd);
+}
+
+struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd)
+{
+       if (hcd)
+               kref_get (&hcd->kref);
+       return hcd;
+}
+EXPORT_SYMBOL (usb_get_hcd);
+
 void usb_put_hcd (struct usb_hcd *hcd)
 {
-       dev_set_drvdata(hcd->self.controller, NULL);
-       usb_bus_put(&hcd->self);
+       if (hcd)
+               kref_put (&hcd->kref, hcd_release);
 }
 EXPORT_SYMBOL (usb_put_hcd);
 
@@ -1915,6 +1819,16 @@ void usb_remove_hcd(struct usb_hcd *hcd)
 }
 EXPORT_SYMBOL (usb_remove_hcd);
 
+void
+usb_hcd_platform_shutdown(struct platform_device* dev)
+{
+       struct usb_hcd *hcd = platform_get_drvdata(dev);
+
+       if (hcd->driver->shutdown)
+               hcd->driver->shutdown(hcd);
+}
+EXPORT_SYMBOL (usb_hcd_platform_shutdown);
+
 /*-------------------------------------------------------------------------*/
 
 #if defined(CONFIG_USB_MON)
index 7022aaf..676877c 100644 (file)
 
 /*-------------------------------------------------------------------------*/
 
-struct usb_hcd {       /* usb_bus.hcpriv points to this */
+struct usb_hcd {
 
        /*
         * housekeeping
         */
        struct usb_bus          self;           /* hcd is-a bus */
+       struct kref             kref;           /* reference counter */
 
        const char              *product_desc;  /* product/vendor string */
        char                    irq_descr[24];  /* driver + bus # */
@@ -85,6 +86,7 @@ struct usb_hcd {      /* usb_bus.hcpriv points to this */
        unsigned                uses_new_polling:1;
        unsigned                poll_rh:1;      /* poll for rh status? */
        unsigned                poll_pending:1; /* status has changed? */
+       unsigned                wireless:1;     /* Wireless USB HCD */
 
        int                     irq;            /* irq allocated */
        void __iomem            *regs;          /* device memory/io */
@@ -128,8 +130,10 @@ static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd)
        return &hcd->self;
 }
 
-
-// urb.hcpriv is really hardware-specific
+static inline struct usb_hcd *bus_to_hcd (struct usb_bus *bus)
+{
+       return container_of(bus, struct usb_hcd, self);
+}
 
 struct hcd_timeout {   /* timeouts we allocate */
        struct list_head        timeout_list;
@@ -138,28 +142,6 @@ struct hcd_timeout {       /* timeouts we allocate */
 
 /*-------------------------------------------------------------------------*/
 
-/*
- * FIXME usb_operations should vanish or become hc_driver,
- * when usb_bus and usb_hcd become the same thing.
- */
-
-struct usb_operations {
-       int (*get_frame_number) (struct usb_device *usb_dev);
-       int (*submit_urb) (struct urb *urb, gfp_t mem_flags);
-       int (*unlink_urb) (struct urb *urb, int status);
-
-       /* allocate dma-consistent buffer for URB_DMA_NOMAPPING */
-       void *(*buffer_alloc)(struct usb_bus *bus, size_t size,
-                       gfp_t mem_flags,
-                       dma_addr_t *dma);
-       void (*buffer_free)(struct usb_bus *bus, size_t size,
-                       void *addr, dma_addr_t dma);
-
-       void (*disable)(struct usb_device *udev,
-                       struct usb_host_endpoint *ep);
-};
-
-/* each driver provides one of these, and hardware init support */
 
 struct pt_regs;
 
@@ -192,6 +174,9 @@ struct hc_driver {
        /* cleanly make HCD stop writing memory and doing I/O */
        void    (*stop) (struct usb_hcd *hcd);
 
+       /* shutdown HCD */
+       void    (*shutdown) (struct usb_hcd *hcd);
+
        /* return current frame number */
        int     (*get_frame_number) (struct usb_hcd *hcd);
 
@@ -218,15 +203,25 @@ struct hc_driver {
                /* Needed only if port-change IRQs are level-triggered */
 };
 
-extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs);
+extern int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags);
+extern int usb_hcd_unlink_urb (struct urb *urb, int status);
+extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb,
+               struct pt_regs *regs);
+extern void usb_hcd_endpoint_disable (struct usb_device *udev,
+               struct usb_host_endpoint *ep);
+extern int usb_hcd_get_frame_number (struct usb_device *udev);
 
 extern struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
                struct device *dev, char *bus_name);
+extern struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd);
 extern void usb_put_hcd (struct usb_hcd *hcd);
 extern int usb_add_hcd(struct usb_hcd *hcd,
                unsigned int irqnum, unsigned long irqflags);
 extern void usb_remove_hcd(struct usb_hcd *hcd);
 
+struct platform_device;
+extern void usb_hcd_platform_shutdown(struct platform_device* dev);
+
 #ifdef CONFIG_PCI
 struct pci_dev;
 struct pci_device_id;
@@ -239,6 +234,8 @@ extern int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t state);
 extern int usb_hcd_pci_resume (struct pci_dev *dev);
 #endif /* CONFIG_PM */
 
+extern void usb_hcd_pci_shutdown (struct pci_dev *dev);
+
 #endif /* CONFIG_PCI */
 
 /* pci-ish (pdev null is ok) buffer alloc/mapping support */
@@ -352,8 +349,6 @@ extern long usb_calc_bus_time (int speed, int is_input,
 
 /*-------------------------------------------------------------------------*/
 
-extern struct usb_bus *usb_alloc_bus (struct usb_operations *);
-
 extern void usb_set_device_state(struct usb_device *udev,
                enum usb_device_state new_state);
 
@@ -365,9 +360,6 @@ extern struct list_head usb_bus_list;
 extern struct mutex usb_bus_list_lock;
 extern wait_queue_head_t usb_kill_urb_queue;
 
-extern struct usb_bus *usb_bus_get (struct usb_bus *bus);
-extern void usb_bus_put (struct usb_bus *bus);
-
 extern void usb_enable_root_hub_irq (struct usb_bus *bus);
 
 extern int usb_find_interface_driver (struct usb_device *dev,
@@ -376,17 +368,11 @@ extern int usb_find_interface_driver (struct usb_device *dev,
 #define usb_endpoint_out(ep_dir)       (!((ep_dir) & USB_DIR_IN))
 
 #ifdef CONFIG_PM
-extern void usb_hcd_suspend_root_hub (struct usb_hcd *hcd);
 extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd);
 extern void usb_root_hub_lost_power (struct usb_device *rhdev);
 extern int hcd_bus_suspend (struct usb_bus *bus);
 extern int hcd_bus_resume (struct usb_bus *bus);
 #else
-static inline void usb_hcd_suspend_root_hub(struct usb_hcd *hcd)
-{
-       return;
-}
-
 static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd)
 {
        return;
index 26c8cb5..2a8cb3c 100644 (file)
@@ -293,7 +293,7 @@ void usb_kick_khubd(struct usb_device *hdev)
 /* completion function, fires on port status changes and various faults */
 static void hub_irq(struct urb *urb, struct pt_regs *regs)
 {
-       struct usb_hub *hub = (struct usb_hub *)urb->context;
+       struct usb_hub *hub = urb->context;
        int status;
        int i;
        unsigned long bits;
@@ -311,7 +311,7 @@ static void hub_irq(struct urb *urb, struct pt_regs *regs)
                        goto resubmit;
                hub->error = urb->status;
                /* FALL THROUGH */
-       
+
        /* let khubd handle things */
        case 0:                 /* we got data:  port status changed */
                bits = 0;
@@ -452,18 +452,14 @@ static void hub_power_on(struct usb_hub *hub)
        msleep(max(pgood_delay, (unsigned) 100));
 }
 
-static inline void __hub_quiesce(struct usb_hub *hub)
+static void hub_quiesce(struct usb_hub *hub)
 {
        /* (nonblocking) khubd and related activity won't re-trigger */
        hub->quiescing = 1;
        hub->activating = 0;
        hub->resume_root_hub = 0;
-}
 
-static void hub_quiesce(struct usb_hub *hub)
-{
        /* (blocking) stop khubd and related activity */
-       __hub_quiesce(hub);
        usb_kill_urb(hub->urb);
        if (hub->has_indicators)
                cancel_delayed_work(&hub->leds);
@@ -868,13 +864,8 @@ descriptor_error:
 
        endpoint = &desc->endpoint[0].desc;
 
-       /* Output endpoint? Curiouser and curiouser.. */
-       if (!(endpoint->bEndpointAddress & USB_DIR_IN))
-               goto descriptor_error;
-
-       /* If it's not an interrupt endpoint, we'd better punt! */
-       if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-                       != USB_ENDPOINT_XFER_INT)
+       /* If it's not an interrupt in endpoint, we'd better punt! */
+       if (!usb_endpoint_is_int_in(endpoint))
                goto descriptor_error;
 
        /* We found a hub */
@@ -1022,26 +1013,29 @@ void usb_set_device_state(struct usb_device *udev,
        if (udev->state == USB_STATE_NOTATTACHED)
                ;       /* do nothing */
        else if (new_state != USB_STATE_NOTATTACHED) {
-               udev->state = new_state;
 
                /* root hub wakeup capabilities are managed out-of-band
                 * and may involve silicon errata ... ignore them here.
                 */
                if (udev->parent) {
-                       if (new_state == USB_STATE_CONFIGURED)
+                       if (udev->state == USB_STATE_SUSPENDED
+                                       || new_state == USB_STATE_SUSPENDED)
+                               ;       /* No change to wakeup settings */
+                       else if (new_state == USB_STATE_CONFIGURED)
                                device_init_wakeup(&udev->dev,
                                        (udev->actconfig->desc.bmAttributes
                                         & USB_CONFIG_ATT_WAKEUP));
-                       else if (new_state != USB_STATE_SUSPENDED)
+                       else
                                device_init_wakeup(&udev->dev, 0);
                }
+               udev->state = new_state;
        } else
                recursively_mark_NOTATTACHED(udev);
        spin_unlock_irqrestore(&device_state_lock, flags);
 }
 
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM
 
 /**
  * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power
@@ -1059,6 +1053,12 @@ void usb_root_hub_lost_power(struct usb_device *rhdev)
        unsigned long flags;
 
        dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
+
+       /* Make sure no potential wakeup events get lost,
+        * by forcing the root hub to be resumed.
+        */
+       rhdev->dev.power.prev_state.event = PM_EVENT_ON;
+
        spin_lock_irqsave(&device_state_lock, flags);
        hub = hdev_to_hub(rhdev);
        for (port1 = 1; port1 <= rhdev->maxchild; ++port1) {
@@ -1072,7 +1072,7 @@ void usb_root_hub_lost_power(struct usb_device *rhdev)
 }
 EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
 
-#endif
+#endif /* CONFIG_PM */
 
 static void choose_address(struct usb_device *udev)
 {
@@ -1148,144 +1148,28 @@ void usb_disconnect(struct usb_device **pdev)
         * cleaning up all state associated with the current configuration
         * so that the hardware is now fully quiesced.
         */
+       dev_dbg (&udev->dev, "unregistering device\n");
        usb_disable_device(udev, 0);
 
-       usb_notify_remove_device(udev);
+       usb_unlock_device(udev);
 
-       /* Free the device number, remove the /proc/bus/usb entry and
-        * the sysfs attributes, and delete the parent's children[]
+       /* Unregister the device.  The device driver is responsible
+        * for removing the device files from usbfs and sysfs and for
+        * de-configuring the device.
+        */
+       device_del(&udev->dev);
+
+       /* Free the device number and delete the parent's children[]
         * (or root_hub) pointer.
         */
-       dev_dbg (&udev->dev, "unregistering device\n");
        release_address(udev);
-       usb_remove_sysfs_dev_files(udev);
 
        /* Avoid races with recursively_mark_NOTATTACHED() */
        spin_lock_irq(&device_state_lock);
        *pdev = NULL;
        spin_unlock_irq(&device_state_lock);
 
-       usb_unlock_device(udev);
-
-       device_unregister(&udev->dev);
-}
-
-static inline const char *plural(int n)
-{
-       return (n == 1 ? "" : "s");
-}
-
-static int choose_configuration(struct usb_device *udev)
-{
-       int i;
-       int num_configs;
-       int insufficient_power = 0;
-       struct usb_host_config *c, *best;
-
-       best = NULL;
-       c = udev->config;
-       num_configs = udev->descriptor.bNumConfigurations;
-       for (i = 0; i < num_configs; (i++, c++)) {
-               struct usb_interface_descriptor *desc = NULL;
-
-               /* It's possible that a config has no interfaces! */
-               if (c->desc.bNumInterfaces > 0)
-                       desc = &c->intf_cache[0]->altsetting->desc;
-
-               /*
-                * HP's USB bus-powered keyboard has only one configuration
-                * and it claims to be self-powered; other devices may have
-                * similar errors in their descriptors.  If the next test
-                * were allowed to execute, such configurations would always
-                * be rejected and the devices would not work as expected.
-                * In the meantime, we run the risk of selecting a config
-                * that requires external power at a time when that power
-                * isn't available.  It seems to be the lesser of two evils.
-                *
-                * Bugzilla #6448 reports a device that appears to crash
-                * when it receives a GET_DEVICE_STATUS request!  We don't
-                * have any other way to tell whether a device is self-powered,
-                * but since we don't use that information anywhere but here,
-                * the call has been removed.
-                *
-                * Maybe the GET_DEVICE_STATUS call and the test below can
-                * be reinstated when device firmwares become more reliable.
-                * Don't hold your breath.
-                */
-#if 0
-               /* Rule out self-powered configs for a bus-powered device */
-               if (bus_powered && (c->desc.bmAttributes &
-                                       USB_CONFIG_ATT_SELFPOWER))
-                       continue;
-#endif
-
-               /*
-                * The next test may not be as effective as it should be.
-                * Some hubs have errors in their descriptor, claiming
-                * to be self-powered when they are really bus-powered.
-                * We will overestimate the amount of current such hubs
-                * make available for each port.
-                *
-                * This is a fairly benign sort of failure.  It won't
-                * cause us to reject configurations that we should have
-                * accepted.
-                */
-
-               /* Rule out configs that draw too much bus current */
-               if (c->desc.bMaxPower * 2 > udev->bus_mA) {
-                       insufficient_power++;
-                       continue;
-               }
-
-               /* If the first config's first interface is COMM/2/0xff
-                * (MSFT RNDIS), rule it out unless Linux has host-side
-                * RNDIS support. */
-               if (i == 0 && desc
-                               && desc->bInterfaceClass == USB_CLASS_COMM
-                               && desc->bInterfaceSubClass == 2
-                               && desc->bInterfaceProtocol == 0xff) {
-#ifndef CONFIG_USB_NET_RNDIS_HOST
-                       continue;
-#else
-                       best = c;
-#endif
-               }
-
-               /* From the remaining configs, choose the first one whose
-                * first interface is for a non-vendor-specific class.
-                * Reason: Linux is more likely to have a class driver
-                * than a vendor-specific driver. */
-               else if (udev->descriptor.bDeviceClass !=
-                                               USB_CLASS_VENDOR_SPEC &&
-                               (!desc || desc->bInterfaceClass !=
-                                               USB_CLASS_VENDOR_SPEC)) {
-                       best = c;
-                       break;
-               }
-
-               /* If all the remaining configs are vendor-specific,
-                * choose the first one. */
-               else if (!best)
-                       best = c;
-       }
-
-       if (insufficient_power > 0)
-               dev_info(&udev->dev, "rejected %d configuration%s "
-                       "due to insufficient available bus power\n",
-                       insufficient_power, plural(insufficient_power));
-
-       if (best) {
-               i = best->desc.bConfigurationValue;
-               dev_info(&udev->dev,
-                       "configuration #%d chosen from %d choice%s\n",
-                       i, num_configs, plural(num_configs));
-       } else {
-               i = -1;
-               dev_warn(&udev->dev,
-                       "no configuration chosen from %d choice%s\n",
-                       num_configs, plural(num_configs));
-       }
-       return i;
+       put_device(&udev->dev);
 }
 
 #ifdef DEBUG
@@ -1328,7 +1212,6 @@ static inline void show_string(struct usb_device *udev, char *id, char *string)
 int usb_new_device(struct usb_device *udev)
 {
        int err;
-       int c;
 
        err = usb_get_configuration(udev);
        if (err < 0) {
@@ -1371,8 +1254,7 @@ int usb_new_device(struct usb_device *udev)
                                        USB_DT_OTG, (void **) &desc) == 0) {
                        if (desc->bmAttributes & USB_OTG_HNP) {
                                unsigned                port1 = udev->portnum;
-                               struct usb_device       *root = udev->parent;
-                               
+
                                dev_info(&udev->dev,
                                        "Dual-Role OTG device on %sHNP port\n",
                                        (port1 == bus->otg_port)
@@ -1407,9 +1289,9 @@ int usb_new_device(struct usb_device *udev)
                 * (Includes HNP test device.)
                 */
                if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
-                       static int __usb_suspend_device(struct usb_device *,
+                       static int __usb_port_suspend(struct usb_device *,
                                                int port1);
-                       err = __usb_suspend_device(udev, udev->bus->otg_port);
+                       err = __usb_port_suspend(udev, udev->bus->otg_port);
                        if (err < 0)
                                dev_dbg(&udev->dev, "HNP fail, %d\n", err);
                }
@@ -1418,34 +1300,15 @@ int usb_new_device(struct usb_device *udev)
        }
 #endif
 
-       /* put device-specific files into sysfs */
+       /* Register the device.  The device driver is responsible
+        * for adding the device files to usbfs and sysfs and for
+        * configuring the device.
+        */
        err = device_add (&udev->dev);
        if (err) {
                dev_err(&udev->dev, "can't device_add, error %d\n", err);
                goto fail;
        }
-       usb_create_sysfs_dev_files (udev);
-
-       usb_lock_device(udev);
-
-       /* choose and set the configuration. that registers the interfaces
-        * with the driver core, and lets usb device drivers bind to them.
-        */
-       c = choose_configuration(udev);
-       if (c >= 0) {
-               err = usb_set_configuration(udev, c);
-               if (err) {
-                       dev_err(&udev->dev, "can't set config #%d, error %d\n",
-                                       c, err);
-                       /* This need not be fatal.  The user can try to
-                        * set other configurations. */
-               }
-       }
-
-       /* USB device state == configured ... usable */
-       usb_notify_add_device(udev);
-
-       usb_unlock_device(udev);
 
        return 0;
 
@@ -1472,6 +1335,18 @@ static int hub_port_status(struct usb_hub *hub, int port1,
        return ret;
 }
 
+
+/* Returns 1 if @hub is a WUSB root hub, 0 otherwise */
+static unsigned hub_is_wusb(struct usb_hub *hub)
+{
+       struct usb_hcd *hcd;
+       if (hub->hdev->parent != NULL)  /* not a root hub? */
+               return 0;
+       hcd = container_of(hub->hdev->bus, struct usb_hcd, self);
+       return hcd->wireless;
+}
+
+
 #define PORT_RESET_TRIES       5
 #define SET_ADDRESS_TRIES      2
 #define GET_DESCRIPTOR_TRIES   2
@@ -1512,7 +1387,9 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                /* if we`ve finished resetting, then break out of the loop */
                if (!(portstatus & USB_PORT_STAT_RESET) &&
                    (portstatus & USB_PORT_STAT_ENABLE)) {
-                       if (portstatus & USB_PORT_STAT_HIGH_SPEED)
+                       if (hub_is_wusb(hub))
+                               udev->speed = USB_SPEED_VARIABLE;
+                       else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
                                udev->speed = USB_SPEED_HIGH;
                        else if (portstatus & USB_PORT_STAT_LOW_SPEED)
                                udev->speed = USB_SPEED_LOW;
@@ -1607,6 +1484,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
        kick_khubd(hub);
 }
 
+#ifdef CONFIG_PM
 
 #ifdef CONFIG_USB_SUSPEND
 
@@ -1633,7 +1511,7 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
         * NOTE:  OTG devices may issue remote wakeup (or SRP) even when
         * we don't explicitly enable it here.
         */
-       if (device_may_wakeup(&udev->dev)) {
+       if (udev->do_remote_wakeup) {
                status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
                                USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
                                USB_DEVICE_REMOTE_WAKEUP, 0,
@@ -1659,7 +1537,8 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
                                USB_CTRL_SET_TIMEOUT);
        } else {
                /* device has up to 10 msec to fully suspend */
-               dev_dbg(&udev->dev, "usb suspend\n");
+               dev_dbg(&udev->dev, "usb %ssuspend\n",
+                               udev->auto_pm ? "auto-" : "");
                usb_set_device_state(udev, USB_STATE_SUSPENDED);
                msleep(10);
        }
@@ -1684,7 +1563,7 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
  * the root hub for their bus goes into global suspend ... so we don't
  * (falsely) update the device power state to say it suspended.
  */
-static int __usb_suspend_device (struct usb_device *udev, int port1)
+static int __usb_port_suspend (struct usb_device *udev, int port1)
 {
        int     status = 0;
 
@@ -1692,49 +1571,29 @@ static int __usb_suspend_device (struct usb_device *udev, int port1)
        if (port1 < 0)
                return port1;
 
-       if (udev->state == USB_STATE_SUSPENDED
-                       || udev->state == USB_STATE_NOTATTACHED) {
-               return 0;
-       }
-
-       /* all interfaces must already be suspended */
-       if (udev->actconfig) {
-               int     i;
-
-               for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
-                       struct usb_interface    *intf;
-
-                       intf = udev->actconfig->interface[i];
-                       if (is_active(intf)) {
-                               dev_dbg(&intf->dev, "nyet suspended\n");
-                               return -EBUSY;
-                       }
-               }
-       }
-
-       /* we only change a device's upstream USB link.
-        * root hubs have no upstream USB link.
+       /* we change the device's upstream USB link,
+        * but root hubs have no upstream USB link.
         */
        if (udev->parent)
                status = hub_port_suspend(hdev_to_hub(udev->parent), port1,
                                udev);
-
-       if (status == 0)
-               udev->dev.power.power_state = PMSG_SUSPEND;
+       else {
+               dev_dbg(&udev->dev, "usb %ssuspend\n",
+                               udev->auto_pm ? "auto-" : "");
+               usb_set_device_state(udev, USB_STATE_SUSPENDED);
+       }
        return status;
 }
 
-#endif
-
 /*
- * usb_suspend_device - suspend a usb device
+ * usb_port_suspend - suspend a usb device's upstream port
  * @udev: device that's no longer in active use
  * Context: must be able to sleep; device not locked; pm locks held
  *
  * Suspends a USB device that isn't in active use, conserving power.
  * Devices may wake out of a suspend, if anything important happens,
  * using the remote wakeup mechanism.  They may also be taken out of
- * suspend by the host, using usb_resume_device().  It's also routine
+ * suspend by the host, using usb_port_resume().  It's also routine
  * to disconnect devices while they are suspended.
  *
  * This only affects the USB hardware for a device; its interfaces
@@ -1746,17 +1605,9 @@ static int __usb_suspend_device (struct usb_device *udev, int port1)
  *
  * Returns 0 on success, else negative errno.
  */
-int usb_suspend_device(struct usb_device *udev)
+int usb_port_suspend(struct usb_device *udev)
 {
-#ifdef CONFIG_USB_SUSPEND
-       if (udev->state == USB_STATE_NOTATTACHED)
-               return -ENODEV;
-       return __usb_suspend_device(udev, udev->portnum);
-#else
-       /* NOTE:  udev->state unchanged, it's not lying ... */
-       udev->dev.power.power_state = PMSG_SUSPEND;
-       return 0;
-#endif
+       return __usb_port_suspend(udev, udev->portnum);
 }
 
 /*
@@ -1767,7 +1618,7 @@ int usb_suspend_device(struct usb_device *udev)
  * resume (by host) or remote wakeup (by device) ... now see what changed
  * in the tree that's rooted at this device.
  */
-static int finish_device_resume(struct usb_device *udev)
+static int finish_port_resume(struct usb_device *udev)
 {
        int     status;
        u16     devstatus;
@@ -1783,7 +1634,6 @@ static int finish_device_resume(struct usb_device *udev)
        usb_set_device_state(udev, udev->actconfig
                        ? USB_STATE_CONFIGURED
                        : USB_STATE_ADDRESS);
-       udev->dev.power.power_state = PMSG_ON;
 
        /* 10.5.4.5 says be sure devices in the tree are still there.
         * For now let's assume the device didn't go crazy on resume,
@@ -1798,9 +1648,6 @@ static int finish_device_resume(struct usb_device *udev)
                        "gone after usb resume? status %d\n",
                        status);
        else if (udev->actconfig) {
-               unsigned        i;
-               int             (*resume)(struct device *);
-
                le16_to_cpus(&devstatus);
                if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
                                && udev->parent) {
@@ -1811,24 +1658,9 @@ static int finish_device_resume(struct usb_device *udev)
                                        USB_DEVICE_REMOTE_WAKEUP, 0,
                                        NULL, 0,
                                        USB_CTRL_SET_TIMEOUT);
-                       if (status) {
+                       if (status)
                                dev_dbg(&udev->dev, "disable remote "
                                        "wakeup, status %d\n", status);
-                               status = 0;
-                       }
-               }
-
-               /* resume interface drivers; if this is a hub, it
-                * may have a child resume event to deal with soon
-                */
-               resume = udev->dev.bus->resume;
-               for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
-                       struct device *dev =
-                                       &udev->actconfig->interface[i]->dev;
-
-                       down(&dev->sem);
-                       (void) resume(dev);
-                       up(&dev->sem);
                }
                status = 0;
 
@@ -1839,8 +1671,6 @@ static int finish_device_resume(struct usb_device *udev)
        return status;
 }
 
-#ifdef CONFIG_USB_SUSPEND
-
 static int
 hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
 {
@@ -1848,6 +1678,8 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
 
        // dev_dbg(hub->intfdev, "resume port %d\n", port1);
 
+       set_bit(port1, hub->busy_bits);
+
        /* see 7.1.7.7; affects power usage, but not budgeting */
        status = clear_port_feature(hub->hdev,
                        port1, USB_PORT_FEAT_SUSPEND);
@@ -1861,7 +1693,8 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
 
                /* drive resume for at least 20 msec */
                if (udev)
-                       dev_dbg(&udev->dev, "RESUME\n");
+                       dev_dbg(&udev->dev, "usb %sresume\n",
+                                       udev->auto_pm ? "auto-" : "");
                msleep(25);
 
 #define LIVE_FLAGS     ( USB_PORT_STAT_POWER \
@@ -1891,19 +1724,21 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
                        /* TRSMRCY = 10 msec */
                        msleep(10);
                        if (udev)
-                               status = finish_device_resume(udev);
+                               status = finish_port_resume(udev);
                }
        }
        if (status < 0)
                hub_port_logical_disconnect(hub, port1);
 
+       clear_bit(port1, hub->busy_bits);
+       if (!hub->hdev->parent && !hub->busy_bits[0])
+               usb_enable_root_hub_irq(hub->hdev->bus);
+
        return status;
 }
 
-#endif
-
 /*
- * usb_resume_device - re-activate a suspended usb device
+ * usb_port_resume - re-activate a suspended usb device's upstream port
  * @udev: device to re-activate
  * Context: must be able to sleep; device not locked; pm locks held
  *
@@ -1915,36 +1750,24 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
  *
  * Returns 0 on success, else negative errno.
  */
-int usb_resume_device(struct usb_device *udev)
+int usb_port_resume(struct usb_device *udev)
 {
        int     status;
 
-       if (udev->state == USB_STATE_NOTATTACHED)
-               return -ENODEV;
-
-       /* selective resume of one downstream hub-to-device port */
+       /* we change the device's upstream USB link,
+        * but root hubs have no upstream USB link.
+        */
        if (udev->parent) {
-#ifdef CONFIG_USB_SUSPEND
-               if (udev->state == USB_STATE_SUSPENDED) {
-                       // NOTE swsusp may bork us, device state being wrong...
-                       // NOTE this fails if parent is also suspended...
-                       status = hub_port_resume(hdev_to_hub(udev->parent),
-                                       udev->portnum, udev);
-               } else
-#endif
-                       status = 0;
-       } else
-               status = finish_device_resume(udev);
-       if (status < 0)
-               dev_dbg(&udev->dev, "can't resume, status %d\n",
-                       status);
-
-       /* rebind drivers that had no suspend() */
-       if (status == 0) {
-               usb_unlock_device(udev);
-               bus_rescan_devices(&usb_bus_type);
-               usb_lock_device(udev);
+               // NOTE this fails if parent is also suspended...
+               status = hub_port_resume(hdev_to_hub(udev->parent),
+                               udev->portnum, udev);
+       } else {
+               dev_dbg(&udev->dev, "usb %sresume\n",
+                               udev->auto_pm ? "auto-" : "");
+               status = finish_port_resume(udev);
        }
+       if (status < 0)
+               dev_dbg(&udev->dev, "can't resume, status %d\n", status);
        return status;
 }
 
@@ -1952,23 +1775,60 @@ static int remote_wakeup(struct usb_device *udev)
 {
        int     status = 0;
 
-#ifdef CONFIG_USB_SUSPEND
+       /* All this just to avoid sending a port-resume message
+        * to the parent hub! */
 
-       /* don't repeat RESUME sequence if this device
-        * was already woken up by some other task
-        */
        usb_lock_device(udev);
+       mutex_lock_nested(&udev->pm_mutex, udev->level);
        if (udev->state == USB_STATE_SUSPENDED) {
-               dev_dbg(&udev->dev, "RESUME (wakeup)\n");
+               dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
                /* TRSMRCY = 10 msec */
                msleep(10);
-               status = finish_device_resume(udev);
+               status = finish_port_resume(udev);
+               if (status == 0)
+                       udev->dev.power.power_state.event = PM_EVENT_ON;
        }
+       mutex_unlock(&udev->pm_mutex);
+
+       if (status == 0)
+               usb_autoresume_device(udev, 0);
        usb_unlock_device(udev);
-#endif
        return status;
 }
 
+#else  /* CONFIG_USB_SUSPEND */
+
+/* When CONFIG_USB_SUSPEND isn't set, we never suspend or resume any ports. */
+
+int usb_port_suspend(struct usb_device *udev)
+{
+       return 0;
+}
+
+static inline int
+finish_port_resume(struct usb_device *udev)
+{
+       return 0;
+}
+
+static inline int
+hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
+{
+       return 0;
+}
+
+int usb_port_resume(struct usb_device *udev)
+{
+       return 0;
+}
+
+static inline int remote_wakeup(struct usb_device *udev)
+{
+       return 0;
+}
+
+#endif
+
 static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
 {
        struct usb_hub          *hub = usb_get_intfdata (intf);
@@ -1980,13 +1840,17 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
                struct usb_device       *udev;
 
                udev = hdev->children [port1-1];
-               if (udev && (udev->dev.power.power_state.event
-                                       == PM_EVENT_ON
+               if (udev && msg.event == PM_EVENT_SUSPEND &&
 #ifdef CONFIG_USB_SUSPEND
-                               || udev->state != USB_STATE_SUSPENDED
+                               udev->state != USB_STATE_SUSPENDED
+#else
+                               udev->dev.power.power_state.event
+                                       == PM_EVENT_ON
 #endif
-                               )) {
-                       dev_dbg(&intf->dev, "port %d nyet suspended\n", port1);
+                               ) {
+                       if (!hdev->auto_pm)
+                               dev_dbg(&intf->dev, "port %d nyet suspended\n",
+                                               port1);
                        return -EBUSY;
                }
        }
@@ -2035,66 +1899,22 @@ static int hub_resume(struct usb_interface *intf)
                }
        }
 
+       /* tell khubd to look for changes on this hub */
        hub_activate(hub);
-
-       /* REVISIT:  this recursion probably shouldn't exist.  Remove
-        * this code sometime, after retesting with different root and
-        * external hubs.
-        */
-#ifdef CONFIG_USB_SUSPEND
-       {
-       unsigned                port1;
-
-       for (port1 = 1; port1 <= hdev->maxchild; port1++) {
-               struct usb_device       *udev;
-               u16                     portstat, portchange;
-
-               udev = hdev->children [port1-1];
-               status = hub_port_status(hub, port1, &portstat, &portchange);
-               if (status == 0) {
-                       if (portchange & USB_PORT_STAT_C_SUSPEND) {
-                               clear_port_feature(hdev, port1,
-                                       USB_PORT_FEAT_C_SUSPEND);
-                               portchange &= ~USB_PORT_STAT_C_SUSPEND;
-                       }
-
-                       /* let khubd handle disconnects etc */
-                       if (portchange)
-                               continue;
-               }
-
-               if (!udev || status < 0)
-                       continue;
-               usb_lock_device(udev);
-               if (portstat & USB_PORT_STAT_SUSPEND)
-                       status = hub_port_resume(hub, port1, udev);
-               else {
-                       status = finish_device_resume(udev);
-                       if (status < 0) {
-                               dev_dbg(&intf->dev, "resume port %d --> %d\n",
-                                       port1, status);
-                               hub_port_logical_disconnect(hub, port1);
-                       }
-               }
-               usb_unlock_device(udev);
-       }
-       }
-#endif
        return 0;
 }
 
-void usb_suspend_root_hub(struct usb_device *hdev)
-{
-       struct usb_hub *hub = hdev_to_hub(hdev);
+#else  /* CONFIG_PM */
 
-       /* This also makes any led blinker stop retriggering.  We're called
-        * from irq, so the blinker might still be scheduled.  Caller promises
-        * that the root hub status URB will be canceled.
-        */
-       __hub_quiesce(hub);
-       mark_quiesced(to_usb_interface(hub->intfdev));
+static inline int remote_wakeup(struct usb_device *udev)
+{
+       return 0;
 }
 
+#define hub_suspend NULL
+#define hub_resume NULL
+#endif
+
 void usb_resume_root_hub(struct usb_device *hdev)
 {
        struct usb_hub *hub = hdev_to_hub(hdev);
@@ -2214,6 +2034,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        int                     i, j, retval;
        unsigned                delay = HUB_SHORT_RESET_TIME;
        enum usb_device_speed   oldspeed = udev->speed;
+       char                    *speed, *type;
 
        /* root hub ports have a slightly longer reset period
         * (from USB 2.0 spec, section 7.1.7.5)
@@ -2246,8 +2067,13 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
   
        /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
         * it's fixed size except for full speed devices.
+        * For Wireless USB devices, ep0 max packet is always 512 (tho
+        * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
         */
        switch (udev->speed) {
+       case USB_SPEED_VARIABLE:        /* fixed at 512 */
+               udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(512);
+               break;
        case USB_SPEED_HIGH:            /* fixed at 64 */
                udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);
                break;
@@ -2265,17 +2091,21 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                goto fail;
        }
  
+       type = "";
+       switch (udev->speed) {
+       case USB_SPEED_LOW:     speed = "low";  break;
+       case USB_SPEED_FULL:    speed = "full"; break;
+       case USB_SPEED_HIGH:    speed = "high"; break;
+       case USB_SPEED_VARIABLE:
+                               speed = "variable";
+                               type = "Wireless ";
+                               break;
+       default:                speed = "?";    break;
+       }
        dev_info (&udev->dev,
-                       "%s %s speed USB device using %s and address %d\n",
-                       (udev->config) ? "reset" : "new",
-                       ({ char *speed; switch (udev->speed) {
-                       case USB_SPEED_LOW:     speed = "low";  break;
-                       case USB_SPEED_FULL:    speed = "full"; break;
-                       case USB_SPEED_HIGH:    speed = "high"; break;
-                       default:                speed = "?";    break;
-                       }; speed;}),
-                       udev->bus->controller->driver->name,
-                       udev->devnum);
+                 "%s %s speed %sUSB device using %s and address %d\n",
+                 (udev->config) ? "reset" : "new", speed, type,
+                 udev->bus->controller->driver->name, udev->devnum);
 
        /* Set up TT records, if needed  */
        if (hdev->tt) {
@@ -2317,6 +2147,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                         * down tremendously by NAKing the unexpectedly
                         * early status stage.  Also, retry on all errors;
                         * some devices are flakey.
+                        * 255 is for WUSB devices, we actually need to use 512.
+                        * WUSB1.0[4.8.1].
                         */
                        for (j = 0; j < 3; ++j) {
                                buf->bMaxPacketSize0 = 0;
@@ -2326,7 +2158,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                                        buf, GET_DESCRIPTOR_BUFSIZE,
                                        (i ? USB_CTRL_GET_TIMEOUT : 1000));
                                switch (buf->bMaxPacketSize0) {
-                               case 8: case 16: case 32: case 64:
+                               case 8: case 16: case 32: case 64: case 255:
                                        if (buf->bDescriptorType ==
                                                        USB_DT_DEVICE) {
                                                r = 0;
@@ -2400,7 +2232,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        if (retval)
                goto fail;
 
-       i = udev->descriptor.bMaxPacketSize0;
+       i = udev->descriptor.bMaxPacketSize0 == 0xff?
+           512 : udev->descriptor.bMaxPacketSize0;
        if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
                if (udev->speed != USB_SPEED_FULL ||
                                !(i == 8 || i == 16 || i == 32 || i == 64)) {
@@ -2585,6 +2418,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                usb_set_device_state(udev, USB_STATE_POWERED);
                udev->speed = USB_SPEED_UNKNOWN;
                udev->bus_mA = hub->mA_per_port;
+               udev->level = hdev->level + 1;
 
                /* set the address */
                choose_address(udev);
@@ -2736,17 +2570,6 @@ static void hub_events(void)
                usb_get_intf(intf);
                spin_unlock_irq(&hub_event_lock);
 
-               /* Is this is a root hub wanting to reactivate the downstream
-                * ports?  If so, be sure the interface resumes even if its
-                * stub "device" node was never suspended.
-                */
-               if (i) {
-                       dpm_runtime_resume(&hdev->dev);
-                       dpm_runtime_resume(&intf->dev);
-                       usb_put_intf(intf);
-                       continue;
-               }
-
                /* Lock the device, then check to see if we were
                 * disconnected while waiting for the lock to succeed. */
                if (locktree(hdev) < 0) {
@@ -2763,6 +2586,13 @@ static void hub_events(void)
                        goto loop;
                }
 
+               /* Is this is a root hub wanting to reactivate the downstream
+                * ports?  If so, be sure the interface resumes even if its
+                * stub "device" node was never suspended.
+                */
+               if (i)
+                       usb_autoresume_device(hdev, 0);
+
                /* If this is an inactive or suspended hub, do nothing */
                if (hub->quiescing)
                        goto loop;
@@ -2900,7 +2730,7 @@ static void hub_events(void)
 
                /* If this is a root hub, tell the HCD it's okay to
                 * re-enable port-change interrupts now. */
-               if (!hdev->parent)
+               if (!hdev->parent && !hub->busy_bits[0])
                        usb_enable_root_hub_irq(hdev->bus);
 
 loop:
@@ -3075,6 +2905,9 @@ int usb_reset_device(struct usb_device *udev)
                        break;
        }
        clear_bit(port1, parent_hub->busy_bits);
+       if (!parent_hdev->parent && !parent_hub->busy_bits[0])
+               usb_enable_root_hub_irq(parent_hdev->bus);
+
        if (ret < 0)
                goto re_enumerate;
  
@@ -3128,6 +2961,7 @@ re_enumerate:
        hub_port_logical_disconnect(parent_hub, port1);
        return -ENODEV;
 }
+EXPORT_SYMBOL(usb_reset_device);
 
 /**
  * usb_reset_composite_device - warn interface drivers and perform a USB port reset
@@ -3163,6 +2997,9 @@ int usb_reset_composite_device(struct usb_device *udev,
                return -EINVAL;
        }
 
+       /* Prevent autosuspend during the reset */
+       usb_autoresume_device(udev, 1);
+
        if (iface && iface->condition != USB_INTERFACE_BINDING)
                iface = NULL;
 
@@ -3204,5 +3041,7 @@ int usb_reset_composite_device(struct usb_device *udev,
                }
        }
 
+       usb_autosuspend_device(udev, 1);
        return ret;
 }
+EXPORT_SYMBOL(usb_reset_composite_device);
index 29d5f45..0f8e82a 100644 (file)
@@ -212,7 +212,8 @@ struct usb_hub {
        unsigned long           event_bits[1];  /* status change bitmask */
        unsigned long           change_bits[1]; /* ports with logical connect
                                                        status change */
-       unsigned long           busy_bits[1];   /* ports being reset */
+       unsigned long           busy_bits[1];   /* ports being reset or
+                                                       resumed */
 #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
 #error event_bits[] is too short!
 #endif
index 58b4b10..df3d152 100644 (file)
@@ -44,7 +44,7 @@
 #include "hcd.h"
 
 static struct super_operations usbfs_ops;
-static struct file_operations default_file_operations;
+static const struct file_operations default_file_operations;
 static struct vfsmount *usbfs_mount;
 static int usbfs_mount_count;  /* = 0 */
 static int ignore_mount = 0;
@@ -407,7 +407,7 @@ static int default_open (struct inode *inode, struct file *file)
        return 0;
 }
 
-static struct file_operations default_file_operations = {
+static const struct file_operations default_file_operations = {
        .read =         default_read_file,
        .write =        default_write_file,
        .open =         default_open,
@@ -494,7 +494,7 @@ static int fs_create_by_name (const char *name, mode_t mode,
 
 static struct dentry *fs_create_file (const char *name, mode_t mode,
                                      struct dentry *parent, void *data,
-                                     struct file_operations *fops,
+                                     const struct file_operations *fops,
                                      uid_t uid, gid_t gid)
 {
        struct dentry *dentry;
index 4cc8d3e..85b1cd1 100644 (file)
@@ -23,59 +23,44 @@ static void usb_api_blocking_completion(struct urb *urb, struct pt_regs *regs)
 }
 
 
-static void timeout_kill(unsigned long data)
-{
-       struct urb      *urb = (struct urb *) data;
-
-       usb_unlink_urb(urb);
-}
-
-// Starts urb and waits for completion or timeout
-// note that this call is NOT interruptible, while
-// many device driver i/o requests should be interruptible
-static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
+/*
+ * Starts urb and waits for completion or timeout. Note that this call
+ * is NOT interruptible. Many device driver i/o requests should be
+ * interruptible and therefore these drivers should implement their
+ * own interruptible routines.
+ */
+static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
 { 
-       struct completion       done;
-       struct timer_list       timer;
-       int                     status;
+       struct completion done;
+       unsigned long expire;
+       int status;
 
        init_completion(&done);         
        urb->context = &done;
        urb->actual_length = 0;
        status = usb_submit_urb(urb, GFP_NOIO);
-
-       if (status == 0) {
-               if (timeout > 0) {
-                       init_timer(&timer);
-                       timer.expires = jiffies + msecs_to_jiffies(timeout);
-                       timer.data = (unsigned long)urb;
-                       timer.function = timeout_kill;
-                       /* grr.  timeout _should_ include submit delays. */
-                       add_timer(&timer);
-               }
-               wait_for_completion(&done);
+       if (unlikely(status))
+               goto out;
+
+       expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
+       if (!wait_for_completion_timeout(&done, expire)) {
+
+               dev_dbg(&urb->dev->dev,
+                       "%s timed out on ep%d%s len=%d/%d\n",
+                       current->comm,
+                       usb_pipeendpoint(urb->pipe),
+                       usb_pipein(urb->pipe) ? "in" : "out",
+                       urb->actual_length,
+                       urb->transfer_buffer_length);
+
+               usb_kill_urb(urb);
+               status = urb->status == -ENOENT ? -ETIMEDOUT : urb->status;
+       } else
                status = urb->status;
-               /* note:  HCDs return ETIMEDOUT for other reasons too */
-               if (status == -ECONNRESET) {
-                       dev_dbg(&urb->dev->dev,
-                               "%s timed out on ep%d%s len=%d/%d\n",
-                               current->comm,
-                               usb_pipeendpoint(urb->pipe),
-                               usb_pipein(urb->pipe) ? "in" : "out",
-                               urb->actual_length,
-                               urb->transfer_buffer_length
-                               );
-                       if (urb->actual_length > 0)
-                               status = 0;
-                       else
-                               status = -ETIMEDOUT;
-               }
-               if (timeout > 0)
-                       del_timer_sync(&timer);
-       }
-
+out:
        if (actual_length)
                *actual_length = urb->actual_length;
+
        usb_free_urb(urb);
        return status;
 }
@@ -263,7 +248,7 @@ static void sg_clean (struct usb_sg_request *io)
 
 static void sg_complete (struct urb *urb, struct pt_regs *regs)
 {
-       struct usb_sg_request   *io = (struct usb_sg_request *) urb->context;
+       struct usb_sg_request   *io = urb->context;
 
        spin_lock (&io->lock);
 
@@ -999,8 +984,8 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
                ep = dev->ep_in[epnum];
                dev->ep_in[epnum] = NULL;
        }
-       if (ep && dev->bus && dev->bus->op && dev->bus->op->disable)
-               dev->bus->op->disable(dev, ep);
+       if (ep && dev->bus)
+               usb_hcd_endpoint_disable(dev, ep);
 }
 
 /**
@@ -1381,9 +1366,6 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
        if (cp && configuration == 0)
                dev_warn(&dev->dev, "config 0 descriptor??\n");
 
-       if (dev->state == USB_STATE_SUSPENDED)
-               return -EHOSTUNREACH;
-
        /* Allocate memory for new interfaces before doing anything else,
         * so that if we run out then nothing will have changed. */
        n = nintf = 0;
@@ -1418,6 +1400,11 @@ free_interfaces:
                                        configuration, -i);
        }
 
+       /* Wake up the device so we can send it the Set-Config request */
+       ret = usb_autoresume_device(dev, 1);
+       if (ret)
+               goto free_interfaces;
+
        /* if it's already configured, clear out old state first.
         * getting rid of old interfaces means unbinding their drivers.
         */
@@ -1437,6 +1424,7 @@ free_interfaces:
        dev->actconfig = cp;
        if (!cp) {
                usb_set_device_state(dev, USB_STATE_ADDRESS);
+               usb_autosuspend_device(dev, 1);
                goto free_interfaces;
        }
        usb_set_device_state(dev, USB_STATE_CONFIGURED);
@@ -1505,8 +1493,68 @@ free_interfaces:
                usb_create_sysfs_intf_files (intf);
        }
 
+       usb_autosuspend_device(dev, 1);
+       return 0;
+}
+
+struct set_config_request {
+       struct usb_device       *udev;
+       int                     config;
+       struct work_struct      work;
+};
+
+/* Worker routine for usb_driver_set_configuration() */
+static void driver_set_config_work(void *_req)
+{
+       struct set_config_request *req = _req;
+
+       usb_lock_device(req->udev);
+       usb_set_configuration(req->udev, req->config);
+       usb_unlock_device(req->udev);
+       usb_put_dev(req->udev);
+       kfree(req);
+}
+
+/**
+ * usb_driver_set_configuration - Provide a way for drivers to change device configurations
+ * @udev: the device whose configuration is being updated
+ * @config: the configuration being chosen.
+ * Context: In process context, must be able to sleep
+ *
+ * Device interface drivers are not allowed to change device configurations.
+ * This is because changing configurations will destroy the interface the
+ * driver is bound to and create new ones; it would be like a floppy-disk
+ * driver telling the computer to replace the floppy-disk drive with a
+ * tape drive!
+ *
+ * Still, in certain specialized circumstances the need may arise.  This
+ * routine gets around the normal restrictions by using a work thread to
+ * submit the change-config request.
+ *
+ * Returns 0 if the request was succesfully queued, error code otherwise.
+ * The caller has no way to know whether the queued request will eventually
+ * succeed.
+ */
+int usb_driver_set_configuration(struct usb_device *udev, int config)
+{
+       struct set_config_request *req;
+
+       req = kmalloc(sizeof(*req), GFP_KERNEL);
+       if (!req)
+               return -ENOMEM;
+       req->udev = udev;
+       req->config = config;
+       INIT_WORK(&req->work, driver_set_config_work, req);
+
+       usb_get_dev(udev);
+       if (!schedule_work(&req->work)) {
+               usb_put_dev(udev);
+               kfree(req);
+               return -EINVAL;
+       }
        return 0;
 }
+EXPORT_SYMBOL_GPL(usb_driver_set_configuration);
 
 // synchronous request completion model
 EXPORT_SYMBOL(usb_control_msg);
index b042676..6b36897 100644 (file)
@@ -50,8 +50,11 @@ void usb_notify_add_device(struct usb_device *udev)
 
 void usb_notify_remove_device(struct usb_device *udev)
 {
+       /* Protect against simultaneous usbfs open */
+       mutex_lock(&usbfs_mutex);
        blocking_notifier_call_chain(&usb_notifier_list,
                        USB_DEVICE_REMOVE, udev);
+       mutex_unlock(&usbfs_mutex);
 }
 
 void usb_notify_add_bus(struct usb_bus *ubus)
index dec973a..55d8f57 100644 (file)
@@ -60,7 +60,7 @@ static ssize_t
 set_bConfigurationValue (struct device *dev, struct device_attribute *attr,
                const char *buf, size_t count)
 {
-       struct usb_device       *udev = udev = to_usb_device (dev);
+       struct usb_device       *udev = to_usb_device (dev);
        int                     config, value;
 
        if (sscanf (buf, "%u", &config) != 1 || config > 255)
@@ -186,6 +186,7 @@ usb_descriptor_attr (bMaxPacketSize0, "%d\n")
 
 static struct attribute *dev_attrs[] = {
        /* current configuration's attributes */
+       &dev_attr_configuration.attr,
        &dev_attr_bNumInterfaces.attr,
        &dev_attr_bConfigurationValue.attr,
        &dev_attr_bmAttributes.attr,
@@ -209,20 +210,40 @@ static struct attribute_group dev_attr_grp = {
        .attrs = dev_attrs,
 };
 
-void usb_create_sysfs_dev_files (struct usb_device *udev)
+int usb_create_sysfs_dev_files(struct usb_device *udev)
 {
        struct device *dev = &udev->dev;
+       int retval;
 
-       sysfs_create_group(&dev->kobj, &dev_attr_grp);
+       retval = sysfs_create_group(&dev->kobj, &dev_attr_grp);
+       if (retval)
+               return retval;
 
-       if (udev->manufacturer)
-               device_create_file (dev, &dev_attr_manufacturer);
-       if (udev->product)
-               device_create_file (dev, &dev_attr_product);
-       if (udev->serial)
-               device_create_file (dev, &dev_attr_serial);
-       device_create_file (dev, &dev_attr_configuration);
-       usb_create_ep_files(dev, &udev->ep0, udev);
+       if (udev->manufacturer) {
+               retval = device_create_file (dev, &dev_attr_manufacturer);
+               if (retval)
+                       goto error;
+       }
+       if (udev->product) {
+               retval = device_create_file (dev, &dev_attr_product);
+               if (retval)
+                       goto error;
+       }
+       if (udev->serial) {
+               retval = device_create_file (dev, &dev_attr_serial);
+               if (retval)
+                       goto error;
+       }
+       retval = usb_create_ep_files(dev, &udev->ep0, udev);
+       if (retval)
+               goto error;
+       return 0;
+error:
+       usb_remove_ep_files(&udev->ep0);
+       device_remove_file(dev, &dev_attr_manufacturer);
+       device_remove_file(dev, &dev_attr_product);
+       device_remove_file(dev, &dev_attr_serial);
+       return retval;
 }
 
 void usb_remove_sysfs_dev_files (struct usb_device *udev)
@@ -238,7 +259,6 @@ void usb_remove_sysfs_dev_files (struct usb_device *udev)
                device_remove_file(dev, &dev_attr_product);
        if (udev->serial)
                device_remove_file(dev, &dev_attr_serial);
-       device_remove_file (dev, &dev_attr_configuration);
 }
 
 /* Interface fields */
@@ -340,18 +360,28 @@ static inline void usb_remove_intf_ep_files(struct usb_interface *intf)
                usb_remove_ep_files(&iface_desc->endpoint[i]);
 }
 
-void usb_create_sysfs_intf_files (struct usb_interface *intf)
+int usb_create_sysfs_intf_files(struct usb_interface *intf)
 {
        struct usb_device *udev = interface_to_usbdev(intf);
        struct usb_host_interface *alt = intf->cur_altsetting;
+       int retval;
 
-       sysfs_create_group(&intf->dev.kobj, &intf_attr_grp);
+       retval = sysfs_create_group(&intf->dev.kobj, &intf_attr_grp);
+       if (retval)
+               goto error;
 
        if (alt->string == NULL)
                alt->string = usb_cache_string(udev, alt->desc.iInterface);
        if (alt->string)
-               device_create_file(&intf->dev, &dev_attr_interface);
+               retval = device_create_file(&intf->dev, &dev_attr_interface);
        usb_create_intf_ep_files(intf, udev);
+       return 0;
+error:
+       if (alt->string)
+               device_remove_file(&intf->dev, &dev_attr_interface);
+       sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp);
+       usb_remove_intf_ep_files(intf);
+       return retval;
 }
 
 void usb_remove_sysfs_intf_files (struct usb_interface *intf)
index 9864988..9801d08 100644 (file)
@@ -57,7 +57,7 @@ struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
 {
        struct urb *urb;
 
-       urb = (struct urb *)kmalloc(sizeof(struct urb) + 
+       urb = kmalloc(sizeof(struct urb) +
                iso_packets * sizeof(struct usb_iso_packet_descriptor),
                mem_flags);
        if (!urb) {
@@ -221,7 +221,6 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
 {
        int                     pipe, temp, max;
        struct usb_device       *dev;
-       struct usb_operations   *op;
        int                     is_out;
 
        if (!urb || urb->hcpriv || !urb->complete)
@@ -233,8 +232,6 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
        if (dev->bus->controller->power.power_state.event != PM_EVENT_ON
                        || dev->state == USB_STATE_SUSPENDED)
                return -EHOSTUNREACH;
-       if (!(op = dev->bus->op) || !op->submit_urb)
-               return -ENODEV;
 
        urb->status = -EINPROGRESS;
        urb->actual_length = 0;
@@ -376,7 +373,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
                urb->interval = temp;
        }
 
-       return op->submit_urb (urb, mem_flags);
+       return usb_hcd_submit_urb (urb, mem_flags);
 }
 
 /*-------------------------------------------------------------------*/
@@ -440,9 +437,9 @@ int usb_unlink_urb(struct urb *urb)
 {
        if (!urb)
                return -EINVAL;
-       if (!(urb->dev && urb->dev->bus && urb->dev->bus->op))
+       if (!(urb->dev && urb->dev->bus))
                return -ENODEV;
-       return urb->dev->bus->op->unlink_urb(urb, -ECONNRESET);
+       return usb_hcd_unlink_urb(urb, -ECONNRESET);
 }
 
 /**
@@ -468,13 +465,13 @@ int usb_unlink_urb(struct urb *urb)
 void usb_kill_urb(struct urb *urb)
 {
        might_sleep();
-       if (!(urb && urb->dev && urb->dev->bus && urb->dev->bus->op))
+       if (!(urb && urb->dev && urb->dev->bus))
                return;
        spin_lock_irq(&urb->lock);
        ++urb->reject;
        spin_unlock_irq(&urb->lock);
 
-       urb->dev->bus->op->unlink_urb(urb, -ENOENT);
+       usb_hcd_unlink_urb(urb, -ENOENT);
        wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
 
        spin_lock_irq(&urb->lock);
index 184c246..60ef4ef 100644 (file)
@@ -67,7 +67,8 @@ static int nousb;     /* Disable USB when built into kernel image */
  * Don't call this function unless you are bound to one of the interfaces
  * on this device or you have locked the device!
  */
-struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
+struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev,
+                                     unsigned ifnum)
 {
        struct usb_host_config *config = dev->actconfig;
        int i;
@@ -100,8 +101,8 @@ struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
  * Don't call this function unless you are bound to the intf interface
  * or you have locked the device!
  */
-struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf,
-               unsigned int altnum)
+struct usb_host_interface *usb_altnum_to_altsetting(const struct usb_interface *intf,
+                                                   unsigned int altnum)
 {
        int i;
 
@@ -112,87 +113,6 @@ struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf,
        return NULL;
 }
 
-/**
- * usb_driver_claim_interface - bind a driver to an interface
- * @driver: the driver to be bound
- * @iface: the interface to which it will be bound; must be in the
- *     usb device's active configuration
- * @priv: driver data associated with that interface
- *
- * This is used by usb device drivers that need to claim more than one
- * interface on a device when probing (audio and acm are current examples).
- * No device driver should directly modify internal usb_interface or
- * usb_device structure members.
- *
- * Few drivers should need to use this routine, since the most natural
- * way to bind to an interface is to return the private data from
- * the driver's probe() method.
- *
- * Callers must own the device lock and the driver model's usb_bus_type.subsys
- * writelock.  So driver probe() entries don't need extra locking,
- * but other call contexts may need to explicitly claim those locks.
- */
-int usb_driver_claim_interface(struct usb_driver *driver,
-                               struct usb_interface *iface, void* priv)
-{
-       struct device *dev = &iface->dev;
-
-       if (dev->driver)
-               return -EBUSY;
-
-       dev->driver = &driver->driver;
-       usb_set_intfdata(iface, priv);
-       iface->condition = USB_INTERFACE_BOUND;
-       mark_active(iface);
-
-       /* if interface was already added, bind now; else let
-        * the future device_add() bind it, bypassing probe()
-        */
-       if (device_is_registered(dev))
-               device_bind_driver(dev);
-
-       return 0;
-}
-
-/**
- * usb_driver_release_interface - unbind a driver from an interface
- * @driver: the driver to be unbound
- * @iface: the interface from which it will be unbound
- *
- * This can be used by drivers to release an interface without waiting
- * for their disconnect() methods to be called.  In typical cases this
- * also causes the driver disconnect() method to be called.
- *
- * This call is synchronous, and may not be used in an interrupt context.
- * Callers must own the device lock and the driver model's usb_bus_type.subsys
- * writelock.  So driver disconnect() entries don't need extra locking,
- * but other call contexts may need to explicitly claim those locks.
- */
-void usb_driver_release_interface(struct usb_driver *driver,
-                                       struct usb_interface *iface)
-{
-       struct device *dev = &iface->dev;
-
-       /* this should never happen, don't release something that's not ours */
-       if (!dev->driver || dev->driver != &driver->driver)
-               return;
-
-       /* don't release from within disconnect() */
-       if (iface->condition != USB_INTERFACE_BOUND)
-               return;
-
-       /* don't release if the interface hasn't been added yet */
-       if (device_is_registered(dev)) {
-               iface->condition = USB_INTERFACE_UNBINDING;
-               device_release_driver(dev);
-       }
-
-       dev->driver = NULL;
-       usb_set_intfdata(iface, NULL);
-       iface->condition = USB_INTERFACE_UNBOUND;
-       mark_quiesced(iface);
-}
-
 struct find_interface_arg {
        int minor;
        struct usb_interface *interface;
@@ -204,7 +124,7 @@ static int __find_interface(struct device * dev, void * data)
        struct usb_interface *intf;
 
        /* can't look at usb devices, only interfaces */
-       if (dev->driver == &usb_generic_driver)
+       if (is_usb_device(dev))
                return 0;
 
        intf = to_usb_interface(dev);
@@ -227,127 +147,16 @@ static int __find_interface(struct device * dev, void * data)
 struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
 {
        struct find_interface_arg argb;
+       int retval;
 
        argb.minor = minor;
        argb.interface = NULL;
-       driver_for_each_device(&drv->driver, NULL, &argb, __find_interface);
+       /* eat the error, it will be in argb.interface */
+       retval = driver_for_each_device(&drv->drvwrap.driver, NULL, &argb,
+                                       __find_interface);
        return argb.interface;
 }
 
-#ifdef CONFIG_HOTPLUG
-
-/*
- * This sends an uevent to userspace, typically helping to load driver
- * or other modules, configure the device, and more.  Drivers can provide
- * a MODULE_DEVICE_TABLE to help with module loading subtasks.
- *
- * We're called either from khubd (the typical case) or from root hub
- * (init, kapmd, modprobe, rmmod, etc), but the agents need to handle
- * delays in event delivery.  Use sysfs (and DEVPATH) to make sure the
- * device (and this configuration!) are still present.
- */
-static int usb_uevent(struct device *dev, char **envp, int num_envp,
-                     char *buffer, int buffer_size)
-{
-       struct usb_interface *intf;
-       struct usb_device *usb_dev;
-       struct usb_host_interface *alt;
-       int i = 0;
-       int length = 0;
-
-       if (!dev)
-               return -ENODEV;
-
-       /* driver is often null here; dev_dbg() would oops */
-       pr_debug ("usb %s: uevent\n", dev->bus_id);
-
-       /* Must check driver_data here, as on remove driver is always NULL */
-       if ((dev->driver == &usb_generic_driver) || 
-           (dev->driver_data == &usb_generic_driver_data))
-               return 0;
-
-       intf = to_usb_interface(dev);
-       usb_dev = interface_to_usbdev (intf);
-       alt = intf->cur_altsetting;
-
-       if (usb_dev->devnum < 0) {
-               pr_debug ("usb %s: already deleted?\n", dev->bus_id);
-               return -ENODEV;
-       }
-       if (!usb_dev->bus) {
-               pr_debug ("usb %s: bus removed?\n", dev->bus_id);
-               return -ENODEV;
-       }
-
-#ifdef CONFIG_USB_DEVICEFS
-       /* If this is available, userspace programs can directly read
-        * all the device descriptors we don't tell them about.  Or
-        * even act as usermode drivers.
-        *
-        * FIXME reduce hardwired intelligence here
-        */
-       if (add_uevent_var(envp, num_envp, &i,
-                          buffer, buffer_size, &length,
-                          "DEVICE=/proc/bus/usb/%03d/%03d",
-                          usb_dev->bus->busnum, usb_dev->devnum))
-               return -ENOMEM;
-#endif
-
-       /* per-device configurations are common */
-       if (add_uevent_var(envp, num_envp, &i,
-                          buffer, buffer_size, &length,
-                          "PRODUCT=%x/%x/%x",
-                          le16_to_cpu(usb_dev->descriptor.idVendor),
-                          le16_to_cpu(usb_dev->descriptor.idProduct),
-                          le16_to_cpu(usb_dev->descriptor.bcdDevice)))
-               return -ENOMEM;
-
-       /* class-based driver binding models */
-       if (add_uevent_var(envp, num_envp, &i,
-                          buffer, buffer_size, &length,
-                          "TYPE=%d/%d/%d",
-                          usb_dev->descriptor.bDeviceClass,
-                          usb_dev->descriptor.bDeviceSubClass,
-                          usb_dev->descriptor.bDeviceProtocol))
-               return -ENOMEM;
-
-       if (add_uevent_var(envp, num_envp, &i,
-                          buffer, buffer_size, &length,
-                          "INTERFACE=%d/%d/%d",
-                          alt->desc.bInterfaceClass,
-                          alt->desc.bInterfaceSubClass,
-                          alt->desc.bInterfaceProtocol))
-               return -ENOMEM;
-
-       if (add_uevent_var(envp, num_envp, &i,
-                          buffer, buffer_size, &length,
-                          "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
-                          le16_to_cpu(usb_dev->descriptor.idVendor),
-                          le16_to_cpu(usb_dev->descriptor.idProduct),
-                          le16_to_cpu(usb_dev->descriptor.bcdDevice),
-                          usb_dev->descriptor.bDeviceClass,
-                          usb_dev->descriptor.bDeviceSubClass,
-                          usb_dev->descriptor.bDeviceProtocol,
-                          alt->desc.bInterfaceClass,
-                          alt->desc.bInterfaceSubClass,
-                          alt->desc.bInterfaceProtocol))
-               return -ENOMEM;
-
-       envp[i] = NULL;
-
-       return 0;
-}
-
-#else
-
-static int usb_uevent(struct device *dev, char **envp,
-                       int num_envp, char *buffer, int buffer_size)
-{
-       return -ENODEV;
-}
-
-#endif /* CONFIG_HOTPLUG */
-
 /**
  * usb_release_dev - free a usb device structure when all users of it are finished.
  * @dev: device that's been disconnected
@@ -361,14 +170,33 @@ static void usb_release_dev(struct device *dev)
 
        udev = to_usb_device(dev);
 
+#ifdef CONFIG_PM
+       cancel_delayed_work(&udev->autosuspend);
+       flush_scheduled_work();
+#endif
        usb_destroy_configuration(udev);
-       usb_bus_put(udev->bus);
+       usb_put_hcd(bus_to_hcd(udev->bus));
        kfree(udev->product);
        kfree(udev->manufacturer);
        kfree(udev->serial);
        kfree(udev);
 }
 
+#ifdef CONFIG_PM
+
+/* usb_autosuspend_work - callback routine to autosuspend a USB device */
+static void usb_autosuspend_work(void *_udev)
+{
+       struct usb_device       *udev = _udev;
+
+       mutex_lock_nested(&udev->pm_mutex, udev->level);
+       udev->auto_pm = 1;
+       usb_suspend_both(udev, PMSG_SUSPEND);
+       mutex_unlock(&udev->pm_mutex);
+}
+
+#endif
+
 /**
  * usb_alloc_dev - usb device constructor (usbcore-internal)
  * @parent: hub to which device is connected; null to allocate a root hub
@@ -390,8 +218,7 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
        if (!dev)
                return NULL;
 
-       bus = usb_bus_get(bus);
-       if (!bus) {
+       if (!usb_get_hcd(bus_to_hcd(bus))) {
                kfree(dev);
                return NULL;
        }
@@ -399,11 +226,12 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
        device_initialize(&dev->dev);
        dev->dev.bus = &usb_bus_type;
        dev->dev.dma_mask = bus->controller->dma_mask;
-       dev->dev.driver_data = &usb_generic_driver_data;
-       dev->dev.driver = &usb_generic_driver;
        dev->dev.release = usb_release_dev;
        dev->state = USB_STATE_ATTACHED;
 
+       /* This magic assignment distinguishes devices from interfaces */
+       dev->dev.platform_data = &usb_generic_driver;
+
        INIT_LIST_HEAD(&dev->ep0.urb_list);
        dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
        dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
@@ -444,6 +272,10 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
        dev->parent = parent;
        INIT_LIST_HEAD(&dev->filelist);
 
+#ifdef CONFIG_PM
+       mutex_init(&dev->pm_mutex);
+       INIT_WORK(&dev->autosuspend, usb_autosuspend_work, dev);
+#endif
        return dev;
 }
 
@@ -549,7 +381,7 @@ void usb_put_intf(struct usb_interface *intf)
  * case the driver already owns the device lock.)
  */
 int usb_lock_device_for_reset(struct usb_device *udev,
-               struct usb_interface *iface)
+                             const struct usb_interface *iface)
 {
        unsigned long jiffies_expire = jiffies + HZ;
 
@@ -672,7 +504,139 @@ exit:
  */
 int usb_get_current_frame_number(struct usb_device *dev)
 {
-       return dev->bus->op->get_frame_number (dev);
+       return usb_hcd_get_frame_number (dev);
+}
+
+/**
+ * usb_endpoint_dir_in - check if the endpoint has IN direction
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type IN, otherwise it returns false.
+ */
+int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd)
+{
+       return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN);
+}
+
+/**
+ * usb_endpoint_dir_out - check if the endpoint has OUT direction
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type OUT, otherwise it returns false.
+ */
+int usb_endpoint_dir_out(const struct usb_endpoint_descriptor *epd)
+{
+       return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
+}
+
+/**
+ * usb_endpoint_xfer_bulk - check if the endpoint has bulk transfer type
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type bulk, otherwise it returns false.
+ */
+int usb_endpoint_xfer_bulk(const struct usb_endpoint_descriptor *epd)
+{
+       return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+               USB_ENDPOINT_XFER_BULK);
+}
+
+/**
+ * usb_endpoint_xfer_int - check if the endpoint has interrupt transfer type
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type interrupt, otherwise it returns
+ * false.
+ */
+int usb_endpoint_xfer_int(const struct usb_endpoint_descriptor *epd)
+{
+       return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+               USB_ENDPOINT_XFER_INT);
+}
+
+/**
+ * usb_endpoint_xfer_isoc - check if the endpoint has isochronous transfer type
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type isochronous, otherwise it returns
+ * false.
+ */
+int usb_endpoint_xfer_isoc(const struct usb_endpoint_descriptor *epd)
+{
+       return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+               USB_ENDPOINT_XFER_ISOC);
+}
+
+/**
+ * usb_endpoint_is_bulk_in - check if the endpoint is bulk IN
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has bulk transfer type and IN direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_bulk_in(const struct usb_endpoint_descriptor *epd)
+{
+       return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_in(epd));
+}
+
+/**
+ * usb_endpoint_is_bulk_out - check if the endpoint is bulk OUT
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has bulk transfer type and OUT direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_bulk_out(const struct usb_endpoint_descriptor *epd)
+{
+       return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_out(epd));
+}
+
+/**
+ * usb_endpoint_is_int_in - check if the endpoint is interrupt IN
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has interrupt transfer type and IN direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_int_in(const struct usb_endpoint_descriptor *epd)
+{
+       return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd));
+}
+
+/**
+ * usb_endpoint_is_int_out - check if the endpoint is interrupt OUT
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has interrupt transfer type and OUT direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_int_out(const struct usb_endpoint_descriptor *epd)
+{
+       return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_out(epd));
+}
+
+/**
+ * usb_endpoint_is_isoc_in - check if the endpoint is isochronous IN
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has isochronous transfer type and IN direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_isoc_in(const struct usb_endpoint_descriptor *epd)
+{
+       return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_in(epd));
+}
+
+/**
+ * usb_endpoint_is_isoc_out - check if the endpoint is isochronous OUT
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has isochronous transfer type and OUT direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_isoc_out(const struct usb_endpoint_descriptor *epd)
+{
+       return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_out(epd));
 }
 
 /*-------------------------------------------------------------------*/
@@ -737,9 +701,9 @@ void *usb_buffer_alloc (
        dma_addr_t *dma
 )
 {
-       if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_alloc)
+       if (!dev || !dev->bus)
                return NULL;
-       return dev->bus->op->buffer_alloc (dev->bus, size, mem_flags, dma);
+       return hcd_buffer_alloc (dev->bus, size, mem_flags, dma);
 }
 
 /**
@@ -760,9 +724,11 @@ void usb_buffer_free (
        dma_addr_t dma
 )
 {
-       if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_free)
-               return;
-       dev->bus->op->buffer_free (dev->bus, size, addr, dma);
+       if (!dev || !dev->bus)
+               return;
+       if (!addr)
+               return;
+       hcd_buffer_free (dev->bus, size, addr, dma);
 }
 
 /**
@@ -911,8 +877,8 @@ void usb_buffer_unmap (struct urb *urb)
  *
  * Reverse the effect of this call with usb_buffer_unmap_sg().
  */
-int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
-               struct scatterlist *sg, int nents)
+int usb_buffer_map_sg(const struct usb_device *dev, unsigned pipe,
+                     struct scatterlist *sg, int nents)
 {
        struct usb_bus          *bus;
        struct device           *controller;
@@ -946,8 +912,8 @@ int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
  * Use this when you are re-using a scatterlist's data buffers for
  * another USB request.
  */
-void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
-               struct scatterlist *sg, int n_hw_ents)
+void usb_buffer_dmasync_sg(const struct usb_device *dev, unsigned pipe,
+                          struct scatterlist *sg, int n_hw_ents)
 {
        struct usb_bus          *bus;
        struct device           *controller;
@@ -972,8 +938,8 @@ void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
  *
  * Reverses the effect of usb_buffer_map_sg().
  */
-void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
-               struct scatterlist *sg, int n_hw_ents)
+void usb_buffer_unmap_sg(const struct usb_device *dev, unsigned pipe,
+                        struct scatterlist *sg, int n_hw_ents)
 {
        struct usb_bus          *bus;
        struct device           *controller;
@@ -988,116 +954,6 @@ void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
                        usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
 }
 
-static int verify_suspended(struct device *dev, void *unused)
-{
-       if (dev->driver == NULL)
-               return 0;
-       return (dev->power.power_state.event == PM_EVENT_ON) ? -EBUSY : 0;
-}
-
-static int usb_generic_suspend(struct device *dev, pm_message_t message)
-{
-       struct usb_interface    *intf;
-       struct usb_driver       *driver;
-       int                     status;
-
-       /* USB devices enter SUSPEND state through their hubs, but can be
-        * marked for FREEZE as soon as their children are already idled.
-        * But those semantics are useless, so we equate the two (sigh).
-        */
-       if (dev->driver == &usb_generic_driver) {
-               if (dev->power.power_state.event == message.event)
-                       return 0;
-               /* we need to rule out bogus requests through sysfs */
-               status = device_for_each_child(dev, NULL, verify_suspended);
-               if (status)
-                       return status;
-               return usb_suspend_device (to_usb_device(dev));
-       }
-
-       if ((dev->driver == NULL) ||
-           (dev->driver_data == &usb_generic_driver_data))
-               return 0;
-
-       intf = to_usb_interface(dev);
-       driver = to_usb_driver(dev->driver);
-
-       /* with no hardware, USB interfaces only use FREEZE and ON states */
-       if (!is_active(intf))
-               return 0;
-
-       if (driver->suspend && driver->resume) {
-               status = driver->suspend(intf, message);
-               if (status)
-                       dev_err(dev, "%s error %d\n", "suspend", status);
-               else
-                       mark_quiesced(intf);
-       } else {
-               // FIXME else if there's no suspend method, disconnect...
-               dev_warn(dev, "no suspend for driver %s?\n", driver->name);
-               mark_quiesced(intf);
-               status = 0;
-       }
-       return status;
-}
-
-static int usb_generic_resume(struct device *dev)
-{
-       struct usb_interface    *intf;
-       struct usb_driver       *driver;
-       struct usb_device       *udev;
-       int                     status;
-
-       if (dev->power.power_state.event == PM_EVENT_ON)
-               return 0;
-
-       /* mark things as "on" immediately, no matter what errors crop up */
-       dev->power.power_state.event = PM_EVENT_ON;
-
-       /* devices resume through their hubs */
-       if (dev->driver == &usb_generic_driver) {
-               udev = to_usb_device(dev);
-               if (udev->state == USB_STATE_NOTATTACHED)
-                       return 0;
-               return usb_resume_device (to_usb_device(dev));
-       }
-
-       if ((dev->driver == NULL) ||
-           (dev->driver_data == &usb_generic_driver_data)) {
-               dev->power.power_state.event = PM_EVENT_FREEZE;
-               return 0;
-       }
-
-       intf = to_usb_interface(dev);
-       driver = to_usb_driver(dev->driver);
-
-       udev = interface_to_usbdev(intf);
-       if (udev->state == USB_STATE_NOTATTACHED)
-               return 0;
-
-       /* if driver was suspended, it has a resume method;
-        * however, sysfs can wrongly mark things as suspended
-        * (on the "no suspend method" FIXME path above)
-        */
-       if (driver->resume) {
-               status = driver->resume(intf);
-               if (status) {
-                       dev_err(dev, "%s error %d\n", "resume", status);
-                       mark_quiesced(intf);
-               }
-       } else
-               dev_warn(dev, "no resume for driver %s?\n", driver->name);
-       return 0;
-}
-
-struct bus_type usb_bus_type = {
-       .name =         "usb",
-       .match =        usb_device_match,
-       .uevent =       usb_uevent,
-       .suspend =      usb_generic_suspend,
-       .resume =       usb_generic_resume,
-};
-
 /* format to disable USB on kernel command line is: nousb */
 __module_param_call("", nousb, param_set_bool, param_get_bool, &nousb, 0444);
 
@@ -1141,7 +997,7 @@ static int __init usb_init(void)
        retval = usb_hub_init();
        if (retval)
                goto hub_init_failed;
-       retval = driver_register(&usb_generic_driver);
+       retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
        if (!retval)
                goto out;
 
@@ -1171,7 +1027,7 @@ static void __exit usb_exit(void)
        if (nousb)
                return;
 
-       driver_unregister(&usb_generic_driver);
+       usb_deregister_device_driver(&usb_generic_driver);
        usb_major_cleanup();
        usbfs_cleanup();
        usb_deregister(&usbfs_driver);
@@ -1201,20 +1057,27 @@ EXPORT_SYMBOL(usb_hub_tt_clear_buffer);
 
 EXPORT_SYMBOL(usb_lock_device_for_reset);
 
-EXPORT_SYMBOL(usb_driver_claim_interface);
-EXPORT_SYMBOL(usb_driver_release_interface);
 EXPORT_SYMBOL(usb_find_interface);
 EXPORT_SYMBOL(usb_ifnum_to_if);
 EXPORT_SYMBOL(usb_altnum_to_altsetting);
 
-EXPORT_SYMBOL(usb_reset_device);
-EXPORT_SYMBOL(usb_reset_composite_device);
-
 EXPORT_SYMBOL(__usb_get_extra_descriptor);
 
 EXPORT_SYMBOL(usb_find_device);
 EXPORT_SYMBOL(usb_get_current_frame_number);
 
+EXPORT_SYMBOL_GPL(usb_endpoint_dir_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_dir_out);
+EXPORT_SYMBOL_GPL(usb_endpoint_xfer_bulk);
+EXPORT_SYMBOL_GPL(usb_endpoint_xfer_int);
+EXPORT_SYMBOL_GPL(usb_endpoint_xfer_isoc);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_bulk_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_bulk_out);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_int_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_int_out);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_isoc_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_isoc_out);
+
 EXPORT_SYMBOL (usb_buffer_alloc);
 EXPORT_SYMBOL (usb_buffer_free);
 
index 49f6923..0c09ecc 100644 (file)
@@ -1,10 +1,10 @@
 /* Functions local to drivers/usb/core/ */
 
-extern void usb_create_sysfs_dev_files (struct usb_device *dev);
+extern int usb_create_sysfs_dev_files (struct usb_device *dev);
 extern void usb_remove_sysfs_dev_files (struct usb_device *dev);
-extern void usb_create_sysfs_intf_files (struct usb_interface *intf);
+extern int usb_create_sysfs_intf_files (struct usb_interface *intf);
 extern void usb_remove_sysfs_intf_files (struct usb_interface *intf);
-extern void usb_create_ep_files(struct device *parent, struct usb_host_endpoint *endpoint,
+extern int usb_create_ep_files(struct device *parent, struct usb_host_endpoint *endpoint,
                                struct usb_device *udev);
 extern void usb_remove_ep_files(struct usb_host_endpoint *endpoint);
 
@@ -20,7 +20,6 @@ extern char *usb_cache_string(struct usb_device *udev, int index);
 extern int usb_set_configuration(struct usb_device *dev, int configuration);
 
 extern void usb_kick_khubd(struct usb_device *dev);
-extern void usb_suspend_root_hub(struct usb_device *hdev);
 extern void usb_resume_root_hub(struct usb_device *dev);
 
 extern int  usb_hub_init(void);
@@ -30,28 +29,74 @@ extern void usb_major_cleanup(void);
 extern int usb_host_init(void);
 extern void usb_host_cleanup(void);
 
-extern int usb_suspend_device(struct usb_device *dev);
-extern int usb_resume_device(struct usb_device *dev);
+#ifdef CONFIG_PM
 
-extern struct device_driver usb_generic_driver;
-extern int usb_generic_driver_data;
-extern int usb_device_match(struct device *dev, struct device_driver *drv);
+extern int usb_suspend_both(struct usb_device *udev, pm_message_t msg);
+extern int usb_resume_both(struct usb_device *udev);
+extern int usb_port_suspend(struct usb_device *dev);
+extern int usb_port_resume(struct usb_device *dev);
+
+#else
+
+#define usb_suspend_both(udev, msg)    0
+static inline int usb_resume_both(struct usb_device *udev)
+{
+       return 0;
+}
+#define usb_port_suspend(dev)          0
+#define usb_port_resume(dev)           0
+
+#endif
+
+#ifdef CONFIG_USB_SUSPEND
+
+#define USB_AUTOSUSPEND_DELAY  (HZ*2)
+
+extern void usb_autosuspend_device(struct usb_device *udev, int dec_busy_cnt);
+extern int usb_autoresume_device(struct usb_device *udev, int inc_busy_cnt);
+
+#else
+
+#define usb_autosuspend_device(udev, dec_busy_cnt)     do {} while (0)
+#define usb_autoresume_device(udev, inc_busy_cnt)      0
+
+#endif
+
+extern struct bus_type usb_bus_type;
+extern struct usb_device_driver usb_generic_driver;
+
+/* Here's how we tell apart devices and interfaces.  Luckily there's
+ * no such thing as a platform USB device, so we can steal the use
+ * of the platform_data field. */
+
+static inline int is_usb_device(const struct device *dev)
+{
+       return dev->platform_data == &usb_generic_driver;
+}
+
+/* Do the same for device drivers and interface drivers. */
+
+static inline int is_usb_device_driver(struct device_driver *drv)
+{
+       return container_of(drv, struct usbdrv_wrap, driver)->
+                       for_devices;
+}
 
 /* Interfaces and their "power state" are owned by usbcore */
 
 static inline void mark_active(struct usb_interface *f)
 {
-       f->dev.power.power_state.event = PM_EVENT_ON;
+       f->is_active = 1;
 }
 
 static inline void mark_quiesced(struct usb_interface *f)
 {
-       f->dev.power.power_state.event = PM_EVENT_FREEZE;
+       f->is_active = 0;
 }
 
-static inline int is_active(struct usb_interface *f)
+static inline int is_active(const struct usb_interface *f)
 {
-       return f->dev.power.power_state.event == PM_EVENT_ON;
+       return f->is_active;
 }
 
 
@@ -59,9 +104,10 @@ static inline int is_active(struct usb_interface *f)
 extern const char *usbcore_name;
 
 /* usbfs stuff */
+extern struct mutex usbfs_mutex;
 extern struct usb_driver usbfs_driver;
-extern struct file_operations usbfs_devices_fops;
-extern struct file_operations usbfs_device_file_operations;
+extern const struct file_operations usbfs_devices_fops;
+extern const struct file_operations usbfs_device_file_operations;
 extern void usbfs_conn_disc_event(void);
 
 extern int usbdev_init(void);
index 1a32d96..8e5dd6f 100644 (file)
@@ -26,7 +26,7 @@ config USB_GADGET
           you need a low level bus controller driver, and some software
           talking to it.  Peripheral controllers are often discrete silicon,
           or are integrated with the CPU in a microcontroller.  The more
-          familiar host side controllers have names like like "EHCI", "OHCI",
+          familiar host side controllers have names like "EHCI", "OHCI",
           or "UHCI", and are usually integrated into southbridges on PC
           motherboards.
 
@@ -404,6 +404,20 @@ config USB_G_SERIAL
          which includes instructions and a "driver info file" needed to
          make MS-Windows work with this driver.
 
+config USB_MIDI_GADGET
+       tristate "MIDI Gadget (EXPERIMENTAL)"
+       depends on SND && EXPERIMENTAL
+       select SND_RAWMIDI
+       help
+         The MIDI Gadget acts as a USB Audio device, with one MIDI
+         input and one MIDI output. These MIDI jacks appear as
+         a sound "card" in the ALSA sound system. Other MIDI
+         connections can then be made on the gadget system, using
+         ALSA's aconnect utility etc.
+
+         Say "y" to link the driver statically, or "m" to build a
+         dynamically linked module called "g_midi".
+
 
 # put drivers that need isochronous transfer support (for audio
 # or video class gadget drivers), or specific hardware, here.
index 5a28e61..e71e086 100644 (file)
@@ -15,6 +15,7 @@ obj-$(CONFIG_USB_AT91)                += at91_udc.o
 g_zero-objs                    := zero.o usbstring.o config.o epautoconf.o
 g_ether-objs                   := ether.o usbstring.o config.o epautoconf.o
 g_serial-objs                  := serial.o usbstring.o config.o epautoconf.o
+g_midi-objs                    := gmidi.o usbstring.o config.o epautoconf.o
 gadgetfs-objs                  := inode.o
 g_file_storage-objs            := file_storage.o usbstring.o config.o \
                                        epautoconf.o
@@ -28,4 +29,5 @@ obj-$(CONFIG_USB_ETH)         += g_ether.o
 obj-$(CONFIG_USB_GADGETFS)     += gadgetfs.o
 obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o
 obj-$(CONFIG_USB_G_SERIAL)     += g_serial.o
+obj-$(CONFIG_USB_MIDI_GADGET)  += g_midi.o
 
index cfebca0..d00958a 100644 (file)
@@ -247,7 +247,7 @@ static int proc_udc_open(struct inode *inode, struct file *file)
        return single_open(file, proc_udc_show, PDE(inode)->data);
 }
 
-static struct file_operations proc_ops = {
+static const struct file_operations proc_ops = {
        .open           = proc_udc_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
index 7d1c22c..fdab97a 100644 (file)
@@ -889,11 +889,9 @@ EXPORT_SYMBOL (net2280_set_fifo_mode);
 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
+       usb_put_hcd (dummy_to_hcd (dum));
 }
 
 static int dummy_udc_probe (struct platform_device *pdev)
@@ -915,9 +913,7 @@ static int dummy_udc_probe (struct platform_device *pdev)
        if (rc < 0)
                return rc;
 
-#if 0          /* usb_bus_get isn't EXPORTed! */
-       usb_bus_get (&dummy_to_hcd (dum)->self);
-#endif
+       usb_get_hcd (dummy_to_hcd (dum));
 
        platform_set_drvdata (pdev, dum);
        device_create_file (&dum->gadget.dev, &dev_attr_function);
index 30299c6..366dc0a 100644 (file)
@@ -262,7 +262,7 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
 #define DEV_CONFIG_CDC
 #endif
 
-#ifdef CONFIG_USB_GADGET_MUSBHDRC
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
 #define DEV_CONFIG_CDC
 #endif
 
@@ -2014,7 +2014,7 @@ rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req)
 static int rndis_control_ack (struct net_device *net)
 {
        struct eth_dev          *dev = netdev_priv(net);
-       u32                     length;
+       int                     length;
        struct usb_request      *resp = dev->stat_req;
 
        /* in case RNDIS calls this after disconnect */
@@ -2230,6 +2230,9 @@ eth_bind (struct usb_gadget *gadget)
        if (gadget_is_pxa (gadget)) {
                /* pxa doesn't support altsettings */
                cdc = 0;
+       } else if (gadget_is_musbhdrc(gadget)) {
+               /* reduce tx dma overhead by avoiding special cases */
+               zlp = 0;
        } else if (gadget_is_sh(gadget)) {
                /* sh doesn't support multiple interfaces or configs */
                cdc = 0;
@@ -2564,7 +2567,7 @@ static struct usb_gadget_driver eth_driver = {
 
        .function       = (char *) driver_desc,
        .bind           = eth_bind,
-       .unbind         = __exit_p(eth_unbind),
+       .unbind         = eth_unbind,
 
        .setup          = eth_setup,
        .disconnect     = eth_disconnect,
diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c
new file mode 100644 (file)
index 0000000..b68cecd
--- /dev/null
@@ -0,0 +1,1337 @@
+/*
+ * gmidi.c -- USB MIDI Gadget Driver
+ *
+ * Copyright (C) 2006 Thumtronics Pty Ltd.
+ * Developed for Thumtronics by Grey Innovation
+ * Ben Williamson <ben.williamson@greyinnovation.com>
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * License ("GPL") version 2, as published by the Free Software Foundation.
+ *
+ * This code is based in part on:
+ *
+ * Gadget Zero driver, Copyright (C) 2003-2004 David Brownell.
+ * USB Audio driver, Copyright (C) 2002 by Takashi Iwai.
+ * USB MIDI driver, Copyright (C) 2002-2005 Clemens Ladisch.
+ *
+ * Refer to the USB Device Class Definition for MIDI Devices:
+ * http://www.usb.org/developers/devclass_docs/midi10.pdf
+ */
+
+#define DEBUG 1
+// #define VERBOSE
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/utsname.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/midi.h>
+
+#include "gadget_chips.h"
+
+MODULE_AUTHOR("Ben Williamson");
+MODULE_LICENSE("GPL v2");
+
+#define DRIVER_VERSION "25 Jul 2006"
+
+static const char shortname[] = "g_midi";
+static const char longname[] = "MIDI Gadget";
+
+static int index = SNDRV_DEFAULT_IDX1;
+static char *id = SNDRV_DEFAULT_STR1;
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for the USB MIDI Gadget adapter.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter.");
+
+/* Some systems will want different product identifers published in the
+ * device descriptor, either numbers or strings or both.  These string
+ * parameters are in UTF-8 (superset of ASCII's 7 bit characters).
+ */
+
+static ushort idVendor;
+module_param(idVendor, ushort, S_IRUGO);
+MODULE_PARM_DESC(idVendor, "USB Vendor ID");
+
+static ushort idProduct;
+module_param(idProduct, ushort, S_IRUGO);
+MODULE_PARM_DESC(idProduct, "USB Product ID");
+
+static ushort bcdDevice;
+module_param(bcdDevice, ushort, S_IRUGO);
+MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
+
+static char *iManufacturer;
+module_param(iManufacturer, charp, S_IRUGO);
+MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
+
+static char *iProduct;
+module_param(iProduct, charp, S_IRUGO);
+MODULE_PARM_DESC(iProduct, "USB Product string");
+
+static char *iSerialNumber;
+module_param(iSerialNumber, charp, S_IRUGO);
+MODULE_PARM_DESC(iSerialNumber, "SerialNumber");
+
+/*
+ * this version autoconfigures as much as possible,
+ * which is reasonable for most "bulk-only" drivers.
+ */
+static const char *EP_IN_NAME;
+static const char *EP_OUT_NAME;
+
+
+/* big enough to hold our biggest descriptor */
+#define USB_BUFSIZ 256
+
+
+/* This is a gadget, and the IN/OUT naming is from the host's perspective.
+   USB -> OUT endpoint -> rawmidi
+   USB <- IN endpoint  <- rawmidi */
+struct gmidi_in_port {
+       struct gmidi_device* dev;
+       int active;
+       uint8_t cable;          /* cable number << 4 */
+       uint8_t state;
+#define STATE_UNKNOWN  0
+#define STATE_1PARAM   1
+#define STATE_2PARAM_1 2
+#define STATE_2PARAM_2 3
+#define STATE_SYSEX_0  4
+#define STATE_SYSEX_1  5
+#define STATE_SYSEX_2  6
+       uint8_t data[2];
+};
+
+struct gmidi_device {
+       spinlock_t              lock;
+       struct usb_gadget       *gadget;
+       struct usb_request      *req;           /* for control responses */
+       u8                      config;
+       struct usb_ep           *in_ep, *out_ep;
+       struct snd_card         *card;
+       struct snd_rawmidi      *rmidi;
+       struct snd_rawmidi_substream *in_substream;
+       struct snd_rawmidi_substream *out_substream;
+
+       /* For the moment we only support one port in
+          each direction, but in_port is kept as a
+          separate struct so we can have more later. */
+       struct gmidi_in_port    in_port;
+       unsigned long           out_triggered;
+       struct tasklet_struct   tasklet;
+};
+
+static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req);
+
+
+#define xprintk(d,level,fmt,args...) \
+       dev_printk(level , &(d)->gadget->dev , fmt , ## args)
+
+#ifdef DEBUG
+#define DBG(dev,fmt,args...) \
+       xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DBG(dev,fmt,args...) \
+       do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE
+#define VDBG   DBG
+#else
+#define VDBG(dev,fmt,args...) \
+       do { } while (0)
+#endif /* VERBOSE */
+
+#define ERROR(dev,fmt,args...) \
+       xprintk(dev , KERN_ERR , fmt , ## args)
+#define WARN(dev,fmt,args...) \
+       xprintk(dev , KERN_WARNING , fmt , ## args)
+#define INFO(dev,fmt,args...) \
+       xprintk(dev , KERN_INFO , fmt , ## args)
+
+
+static unsigned buflen = 256;
+static unsigned qlen = 32;
+
+module_param(buflen, uint, S_IRUGO);
+module_param(qlen, uint, S_IRUGO);
+
+
+/* Thanks to Grey Innovation for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
+ */
+#define DRIVER_VENDOR_NUM      0x17b3          /* Grey Innovation */
+#define DRIVER_PRODUCT_NUM     0x0004          /* Linux-USB "MIDI Gadget" */
+
+
+/*
+ * DESCRIPTORS ... most are static, but strings and (full)
+ * configuration descriptors are built on demand.
+ */
+
+#define STRING_MANUFACTURER    25
+#define STRING_PRODUCT         42
+#define STRING_SERIAL          101
+#define STRING_MIDI_GADGET     250
+
+/* We only have the one configuration, it's number 1. */
+#define        GMIDI_CONFIG            1
+
+/* We have two interfaces- AudioControl and MIDIStreaming */
+#define GMIDI_AC_INTERFACE     0
+#define GMIDI_MS_INTERFACE     1
+#define GMIDI_NUM_INTERFACES   2
+
+DECLARE_USB_AC_HEADER_DESCRIPTOR(1);
+DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
+DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(1);
+
+/* B.1  Device Descriptor */
+static struct usb_device_descriptor device_desc = {
+       .bLength =              USB_DT_DEVICE_SIZE,
+       .bDescriptorType =      USB_DT_DEVICE,
+       .bcdUSB =               __constant_cpu_to_le16(0x0200),
+       .bDeviceClass =         USB_CLASS_PER_INTERFACE,
+       .idVendor =             __constant_cpu_to_le16(DRIVER_VENDOR_NUM),
+       .idProduct =            __constant_cpu_to_le16(DRIVER_PRODUCT_NUM),
+       .iManufacturer =        STRING_MANUFACTURER,
+       .iProduct =             STRING_PRODUCT,
+       .bNumConfigurations =   1,
+};
+
+/* B.2  Configuration Descriptor */
+static struct usb_config_descriptor config_desc = {
+       .bLength =              USB_DT_CONFIG_SIZE,
+       .bDescriptorType =      USB_DT_CONFIG,
+       /* compute wTotalLength on the fly */
+       .bNumInterfaces =       GMIDI_NUM_INTERFACES,
+       .bConfigurationValue =  GMIDI_CONFIG,
+       .iConfiguration =       STRING_MIDI_GADGET,
+       /*
+        * FIXME: When embedding this driver in a device,
+        * these need to be set to reflect the actual
+        * power properties of the device. Is it selfpowered?
+        */
+       .bmAttributes =         USB_CONFIG_ATT_ONE,
+       .bMaxPower =            1,
+};
+
+/* B.3.1  Standard AC Interface Descriptor */
+static const struct usb_interface_descriptor ac_interface_desc = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       .bInterfaceNumber =     GMIDI_AC_INTERFACE,
+       .bNumEndpoints =        0,
+       .bInterfaceClass =      USB_CLASS_AUDIO,
+       .bInterfaceSubClass =   USB_SUBCLASS_AUDIOCONTROL,
+       .iInterface =           STRING_MIDI_GADGET,
+};
+
+/* B.3.2  Class-Specific AC Interface Descriptor */
+static const struct usb_ac_header_descriptor_1 ac_header_desc = {
+       .bLength =              USB_DT_AC_HEADER_SIZE(1),
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   USB_MS_HEADER,
+       .bcdADC =               __constant_cpu_to_le16(0x0100),
+       .wTotalLength =         USB_DT_AC_HEADER_SIZE(1),
+       .bInCollection =        1,
+       .baInterfaceNr = {
+               [0] =           GMIDI_MS_INTERFACE,
+       }
+};
+
+/* B.4.1  Standard MS Interface Descriptor */
+static const struct usb_interface_descriptor ms_interface_desc = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       .bInterfaceNumber =     GMIDI_MS_INTERFACE,
+       .bNumEndpoints =        2,
+       .bInterfaceClass =      USB_CLASS_AUDIO,
+       .bInterfaceSubClass =   USB_SUBCLASS_MIDISTREAMING,
+       .iInterface =           STRING_MIDI_GADGET,
+};
+
+/* B.4.2  Class-Specific MS Interface Descriptor */
+static const struct usb_ms_header_descriptor ms_header_desc = {
+       .bLength =              USB_DT_MS_HEADER_SIZE,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   USB_MS_HEADER,
+       .bcdMSC =               __constant_cpu_to_le16(0x0100),
+       .wTotalLength =         USB_DT_MS_HEADER_SIZE
+                               + 2*USB_DT_MIDI_IN_SIZE
+                               + 2*USB_DT_MIDI_OUT_SIZE(1),
+};
+
+#define JACK_IN_EMB    1
+#define JACK_IN_EXT    2
+#define JACK_OUT_EMB   3
+#define JACK_OUT_EXT   4
+
+/* B.4.3  MIDI IN Jack Descriptors */
+static const struct usb_midi_in_jack_descriptor jack_in_emb_desc = {
+       .bLength =              USB_DT_MIDI_IN_SIZE,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   USB_MS_MIDI_IN_JACK,
+       .bJackType =            USB_MS_EMBEDDED,
+       .bJackID =              JACK_IN_EMB,
+};
+
+static const struct usb_midi_in_jack_descriptor jack_in_ext_desc = {
+       .bLength =              USB_DT_MIDI_IN_SIZE,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   USB_MS_MIDI_IN_JACK,
+       .bJackType =            USB_MS_EXTERNAL,
+       .bJackID =              JACK_IN_EXT,
+};
+
+/* B.4.4  MIDI OUT Jack Descriptors */
+static const struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc = {
+       .bLength =              USB_DT_MIDI_OUT_SIZE(1),
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   USB_MS_MIDI_OUT_JACK,
+       .bJackType =            USB_MS_EMBEDDED,
+       .bJackID =              JACK_OUT_EMB,
+       .bNrInputPins =         1,
+       .pins = {
+               [0] = {
+                       .baSourceID =   JACK_IN_EXT,
+                       .baSourcePin =  1,
+               }
+       }
+};
+
+static const struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc = {
+       .bLength =              USB_DT_MIDI_OUT_SIZE(1),
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   USB_MS_MIDI_OUT_JACK,
+       .bJackType =            USB_MS_EXTERNAL,
+       .bJackID =              JACK_OUT_EXT,
+       .bNrInputPins =         1,
+       .pins = {
+               [0] = {
+                       .baSourceID =   JACK_IN_EMB,
+                       .baSourcePin =  1,
+               }
+       }
+};
+
+/* B.5.1  Standard Bulk OUT Endpoint Descriptor */
+static struct usb_endpoint_descriptor bulk_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_AUDIO_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+/* B.5.2  Class-specific MS Bulk OUT Endpoint Descriptor */
+static const struct usb_ms_endpoint_descriptor_1 ms_out_desc = {
+       .bLength =              USB_DT_MS_ENDPOINT_SIZE(1),
+       .bDescriptorType =      USB_DT_CS_ENDPOINT,
+       .bDescriptorSubtype =   USB_MS_GENERAL,
+       .bNumEmbMIDIJack =      1,
+       .baAssocJackID = {
+               [0] =           JACK_IN_EMB,
+       }
+};
+
+/* B.6.1  Standard Bulk IN Endpoint Descriptor */
+static struct usb_endpoint_descriptor bulk_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_AUDIO_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+/* B.6.2  Class-specific MS Bulk IN Endpoint Descriptor */
+static const struct usb_ms_endpoint_descriptor_1 ms_in_desc = {
+       .bLength =              USB_DT_MS_ENDPOINT_SIZE(1),
+       .bDescriptorType =      USB_DT_CS_ENDPOINT,
+       .bDescriptorSubtype =   USB_MS_GENERAL,
+       .bNumEmbMIDIJack =      1,
+       .baAssocJackID = {
+               [0] =           JACK_OUT_EMB,
+       }
+};
+
+static const struct usb_descriptor_header *gmidi_function [] = {
+       (struct usb_descriptor_header *)&ac_interface_desc,
+       (struct usb_descriptor_header *)&ac_header_desc,
+       (struct usb_descriptor_header *)&ms_interface_desc,
+
+       (struct usb_descriptor_header *)&ms_header_desc,
+       (struct usb_descriptor_header *)&jack_in_emb_desc,
+       (struct usb_descriptor_header *)&jack_in_ext_desc,
+       (struct usb_descriptor_header *)&jack_out_emb_desc,
+       (struct usb_descriptor_header *)&jack_out_ext_desc,
+       /* If you add more jacks, update ms_header_desc.wTotalLength */
+
+       (struct usb_descriptor_header *)&bulk_out_desc,
+       (struct usb_descriptor_header *)&ms_out_desc,
+       (struct usb_descriptor_header *)&bulk_in_desc,
+       (struct usb_descriptor_header *)&ms_in_desc,
+       NULL,
+};
+
+static char manufacturer[50];
+static char product_desc[40] = "MIDI Gadget";
+static char serial_number[20];
+
+/* static strings, in UTF-8 */
+static struct usb_string strings [] = {
+       { STRING_MANUFACTURER, manufacturer, },
+       { STRING_PRODUCT, product_desc, },
+       { STRING_SERIAL, serial_number, },
+       { STRING_MIDI_GADGET, longname, },
+       {  }                    /* end of list */
+};
+
+static struct usb_gadget_strings stringtab = {
+       .language       = 0x0409,       /* en-us */
+       .strings        = strings,
+};
+
+static int config_buf(struct usb_gadget *gadget,
+               u8 *buf, u8 type, unsigned index)
+{
+       int len;
+
+       /* only one configuration */
+       if (index != 0) {
+               return -EINVAL;
+       }
+       len = usb_gadget_config_buf(&config_desc,
+                       buf, USB_BUFSIZ, gmidi_function);
+       if (len < 0) {
+               return len;
+       }
+       ((struct usb_config_descriptor *)buf)->bDescriptorType = type;
+       return len;
+}
+
+static struct usb_request* alloc_ep_req(struct usb_ep *ep, unsigned length)
+{
+       struct usb_request      *req;
+
+       req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+       if (req) {
+               req->length = length;
+               req->buf = kmalloc(length, GFP_ATOMIC);
+               if (!req->buf) {
+                       usb_ep_free_request(ep, req);
+                       req = NULL;
+               }
+       }
+       return req;
+}
+
+static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
+{
+       kfree(req->buf);
+       usb_ep_free_request(ep, req);
+}
+
+static const uint8_t gmidi_cin_length[] = {
+       0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
+};
+
+/*
+ * Receives a chunk of MIDI data.
+ */
+static void gmidi_read_data(struct usb_ep *ep, int cable,
+                                  uint8_t* data, int length)
+{
+       struct gmidi_device *dev = ep->driver_data;
+       /* cable is ignored, because for now we only have one. */
+
+       if (!dev->out_substream) {
+               /* Nobody is listening - throw it on the floor. */
+               return;
+       }
+       if (!test_bit(dev->out_substream->number, &dev->out_triggered)) {
+               return;
+       }
+       snd_rawmidi_receive(dev->out_substream, data, length);
+}
+
+static void gmidi_handle_out_data(struct usb_ep *ep, struct usb_request *req)
+{
+       unsigned i;
+       u8 *buf = req->buf;
+
+       for (i = 0; i + 3 < req->actual; i += 4) {
+               if (buf[i] != 0) {
+                       int cable = buf[i] >> 4;
+                       int length = gmidi_cin_length[buf[i] & 0x0f];
+                       gmidi_read_data(ep, cable, &buf[i + 1], length);
+               }
+       }
+}
+
+static void gmidi_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct gmidi_device *dev = ep->driver_data;
+       int status = req->status;
+
+       switch (status) {
+       case 0:                         /* normal completion */
+               if (ep == dev->out_ep) {
+                       /* we received stuff.
+                          req is queued again, below */
+                       gmidi_handle_out_data(ep, req);
+               } else if (ep == dev->in_ep) {
+                       /* our transmit completed.
+                          see if there's more to go.
+                          gmidi_transmit eats req, don't queue it again. */
+                       gmidi_transmit(dev, req);
+                       return;
+               }
+               break;
+
+       /* this endpoint is normally active while we're configured */
+       case -ECONNABORTED:             /* hardware forced ep reset */
+       case -ECONNRESET:               /* request dequeued */
+       case -ESHUTDOWN:                /* disconnect from host */
+               VDBG(dev, "%s gone (%d), %d/%d\n", ep->name, status,
+                               req->actual, req->length);
+               if (ep == dev->out_ep) {
+                       gmidi_handle_out_data(ep, req);
+               }
+               free_ep_req(ep, req);
+               return;
+
+       case -EOVERFLOW:                /* buffer overrun on read means that
+                                        * we didn't provide a big enough
+                                        * buffer.
+                                        */
+       default:
+               DBG(dev, "%s complete --> %d, %d/%d\n", ep->name,
+                               status, req->actual, req->length);
+               break;
+       case -EREMOTEIO:                /* short read */
+               break;
+       }
+
+       status = usb_ep_queue(ep, req, GFP_ATOMIC);
+       if (status) {
+               ERROR(dev, "kill %s:  resubmit %d bytes --> %d\n",
+                               ep->name, req->length, status);
+               usb_ep_set_halt(ep);
+               /* FIXME recover later ... somehow */
+       }
+}
+
+static int set_gmidi_config(struct gmidi_device *dev, gfp_t gfp_flags)
+{
+       int err = 0;
+       struct usb_request *req;
+       struct usb_ep* ep;
+       unsigned i;
+
+       err = usb_ep_enable(dev->in_ep, &bulk_in_desc);
+       if (err) {
+               ERROR(dev, "can't start %s: %d\n", dev->in_ep->name, err);
+               goto fail;
+       }
+       dev->in_ep->driver_data = dev;
+
+       err = usb_ep_enable(dev->out_ep, &bulk_out_desc);
+       if (err) {
+               ERROR(dev, "can't start %s: %d\n", dev->out_ep->name, err);
+               goto fail;
+       }
+       dev->out_ep->driver_data = dev;
+
+       /* allocate a bunch of read buffers and queue them all at once. */
+       ep = dev->out_ep;
+       for (i = 0; i < qlen && err == 0; i++) {
+               req = alloc_ep_req(ep, buflen);
+               if (req) {
+                       req->complete = gmidi_complete;
+                       err = usb_ep_queue(ep, req, GFP_ATOMIC);
+                       if (err) {
+                               DBG(dev, "%s queue req: %d\n", ep->name, err);
+                       }
+               } else {
+                       err = -ENOMEM;
+               }
+       }
+fail:
+       /* caller is responsible for cleanup on error */
+       return err;
+}
+
+
+static void gmidi_reset_config(struct gmidi_device *dev)
+{
+       if (dev->config == 0) {
+               return;
+       }
+
+       DBG(dev, "reset config\n");
+
+       /* just disable endpoints, forcing completion of pending i/o.
+        * all our completion handlers free their requests in this case.
+        */
+       usb_ep_disable(dev->in_ep);
+       usb_ep_disable(dev->out_ep);
+       dev->config = 0;
+}
+
+/* change our operational config.  this code must agree with the code
+ * that returns config descriptors, and altsetting code.
+ *
+ * it's also responsible for power management interactions. some
+ * configurations might not work with our current power sources.
+ *
+ * note that some device controller hardware will constrain what this
+ * code can do, perhaps by disallowing more than one configuration or
+ * by limiting configuration choices (like the pxa2xx).
+ */
+static int
+gmidi_set_config(struct gmidi_device *dev, unsigned number, gfp_t gfp_flags)
+{
+       int result = 0;
+       struct usb_gadget *gadget = dev->gadget;
+
+#if 0
+       /* FIXME */
+       /* Hacking this bit out fixes a bug where on receipt of two
+          USB_REQ_SET_CONFIGURATION messages, we end up with no
+          buffered OUT requests waiting for data. This is clearly
+          hiding a bug elsewhere, because if the config didn't
+          change then we really shouldn't do anything. */
+       /* Having said that, when we do "change" from config 1
+          to config 1, we at least gmidi_reset_config() which
+          clears out any requests on endpoints, so it's not like
+          we leak or anything. */
+       if (number == dev->config) {
+               return 0;
+       }
+#endif
+
+       if (gadget_is_sa1100(gadget) && dev->config) {
+               /* tx fifo is full, but we can't clear it...*/
+               INFO(dev, "can't change configurations\n");
+               return -ESPIPE;
+       }
+       gmidi_reset_config(dev);
+
+       switch (number) {
+       case GMIDI_CONFIG:
+               result = set_gmidi_config(dev, gfp_flags);
+               break;
+       default:
+               result = -EINVAL;
+               /* FALL THROUGH */
+       case 0:
+               return result;
+       }
+
+       if (!result && (!dev->in_ep || !dev->out_ep)) {
+               result = -ENODEV;
+       }
+       if (result) {
+               gmidi_reset_config(dev);
+       } else {
+               char *speed;
+
+               switch (gadget->speed) {
+               case USB_SPEED_LOW:     speed = "low"; break;
+               case USB_SPEED_FULL:    speed = "full"; break;
+               case USB_SPEED_HIGH:    speed = "high"; break;
+               default:                speed = "?"; break;
+               }
+
+               dev->config = number;
+               INFO(dev, "%s speed\n", speed);
+       }
+       return result;
+}
+
+
+static void gmidi_setup_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       if (req->status || req->actual != req->length) {
+               DBG((struct gmidi_device *) ep->driver_data,
+                               "setup complete --> %d, %d/%d\n",
+                               req->status, req->actual, req->length);
+       }
+}
+
+/*
+ * The setup() callback implements all the ep0 functionality that's
+ * not handled lower down, in hardware or the hardware driver (like
+ * device and endpoint feature flags, and their status).  It's all
+ * housekeeping for the gadget function we're implementing.  Most of
+ * the work is in config-specific setup.
+ */
+static int gmidi_setup(struct usb_gadget *gadget,
+                       const struct usb_ctrlrequest *ctrl)
+{
+       struct gmidi_device *dev = get_gadget_data(gadget);
+       struct usb_request *req = dev->req;
+       int value = -EOPNOTSUPP;
+       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.
+        */
+       req->zero = 0;
+       switch (ctrl->bRequest) {
+
+       case USB_REQ_GET_DESCRIPTOR:
+               if (ctrl->bRequestType != USB_DIR_IN) {
+                       goto unknown;
+               }
+               switch (w_value >> 8) {
+
+               case USB_DT_DEVICE:
+                       value = min(w_length, (u16) sizeof(device_desc));
+                       memcpy(req->buf, &device_desc, value);
+                       break;
+               case USB_DT_CONFIG:
+                       value = config_buf(gadget, req->buf,
+                                       w_value >> 8,
+                                       w_value & 0xff);
+                       if (value >= 0) {
+                               value = min(w_length, (u16)value);
+                       }
+                       break;
+
+               case USB_DT_STRING:
+                       /* wIndex == language code.
+                        * this driver only handles one language, you can
+                        * add string tables for other languages, using
+                        * any UTF-8 characters
+                        */
+                       value = usb_gadget_get_string(&stringtab,
+                                       w_value & 0xff, req->buf);
+                       if (value >= 0) {
+                               value = min(w_length, (u16)value);
+                       }
+                       break;
+               }
+               break;
+
+       /* currently two configs, two speeds */
+       case USB_REQ_SET_CONFIGURATION:
+               if (ctrl->bRequestType != 0) {
+                       goto unknown;
+               }
+               if (gadget->a_hnp_support) {
+                       DBG(dev, "HNP available\n");
+               } else if (gadget->a_alt_hnp_support) {
+                       DBG(dev, "HNP needs a different root port\n");
+               } else {
+                       VDBG(dev, "HNP inactive\n");
+               }
+               spin_lock(&dev->lock);
+               value = gmidi_set_config(dev, w_value, GFP_ATOMIC);
+               spin_unlock(&dev->lock);
+               break;
+       case USB_REQ_GET_CONFIGURATION:
+               if (ctrl->bRequestType != USB_DIR_IN) {
+                       goto unknown;
+               }
+               *(u8 *)req->buf = dev->config;
+               value = min(w_length, (u16)1);
+               break;
+
+       /* until we add altsetting support, or other interfaces,
+        * only 0/0 are possible.  pxa2xx only supports 0/0 (poorly)
+        * and already killed pending endpoint I/O.
+        */
+       case USB_REQ_SET_INTERFACE:
+               if (ctrl->bRequestType != USB_RECIP_INTERFACE) {
+                       goto unknown;
+               }
+               spin_lock(&dev->lock);
+               if (dev->config && w_index < GMIDI_NUM_INTERFACES
+                       && w_value == 0)
+               {
+                       u8 config = dev->config;
+
+                       /* resets interface configuration, forgets about
+                        * previous transaction state (queued bufs, etc)
+                        * and re-inits endpoint state (toggle etc)
+                        * no response queued, just zero status == success.
+                        * if we had more than one interface we couldn't
+                        * use this "reset the config" shortcut.
+                        */
+                       gmidi_reset_config(dev);
+                       gmidi_set_config(dev, config, GFP_ATOMIC);
+                       value = 0;
+               }
+               spin_unlock(&dev->lock);
+               break;
+       case USB_REQ_GET_INTERFACE:
+               if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) {
+                       goto unknown;
+               }
+               if (!dev->config) {
+                       break;
+               }
+               if (w_index >= GMIDI_NUM_INTERFACES) {
+                       value = -EDOM;
+                       break;
+               }
+               *(u8 *)req->buf = 0;
+               value = min(w_length, (u16)1);
+               break;
+
+       default:
+unknown:
+               VDBG(dev, "unknown control req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+       }
+
+       /* respond with data transfer before status phase? */
+       if (value >= 0) {
+               req->length = value;
+               req->zero = value < w_length;
+               value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+               if (value < 0) {
+                       DBG(dev, "ep_queue --> %d\n", value);
+                       req->status = 0;
+                       gmidi_setup_complete(gadget->ep0, req);
+               }
+       }
+
+       /* device either stalls (value < 0) or reports success */
+       return value;
+}
+
+static void gmidi_disconnect(struct usb_gadget *gadget)
+{
+       struct gmidi_device *dev = get_gadget_data(gadget);
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       gmidi_reset_config(dev);
+
+       /* a more significant application might have some non-usb
+        * activities to quiesce here, saving resources like power
+        * or pushing the notification up a network stack.
+        */
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       /* next we may get setup() calls to enumerate new connections;
+        * or an unbind() during shutdown (including removing module).
+        */
+}
+
+static void /* __init_or_exit */ gmidi_unbind(struct usb_gadget *gadget)
+{
+       struct gmidi_device *dev = get_gadget_data(gadget);
+       struct snd_card* card;
+
+       DBG(dev, "unbind\n");
+
+       card = dev->card;
+       dev->card = NULL;
+       if (card) {
+               snd_card_free(card);
+       }
+
+       /* we've already been disconnected ... no i/o is active */
+       if (dev->req) {
+               dev->req->length = USB_BUFSIZ;
+               free_ep_req(gadget->ep0, dev->req);
+       }
+       kfree(dev);
+       set_gadget_data(gadget, NULL);
+}
+
+static int gmidi_snd_free(struct snd_device *device)
+{
+       return 0;
+}
+
+static void gmidi_transmit_packet(struct usb_request* req, uint8_t p0,
+                                       uint8_t p1, uint8_t p2, uint8_t p3)
+{
+       unsigned length = req->length;
+
+       uint8_t* buf = (uint8_t*)req->buf + length;
+       buf[0] = p0;
+       buf[1] = p1;
+       buf[2] = p2;
+       buf[3] = p3;
+       req->length = length + 4;
+}
+
+/*
+ * Converts MIDI commands to USB MIDI packets.
+ */
+static void gmidi_transmit_byte(struct usb_request* req,
+                               struct gmidi_in_port* port, uint8_t b)
+{
+       uint8_t p0 = port->cable;
+
+       if (b >= 0xf8) {
+               gmidi_transmit_packet(req, p0 | 0x0f, b, 0, 0);
+       } else if (b >= 0xf0) {
+               switch (b) {
+               case 0xf0:
+                       port->data[0] = b;
+                       port->state = STATE_SYSEX_1;
+                       break;
+               case 0xf1:
+               case 0xf3:
+                       port->data[0] = b;
+                       port->state = STATE_1PARAM;
+                       break;
+               case 0xf2:
+                       port->data[0] = b;
+                       port->state = STATE_2PARAM_1;
+                       break;
+               case 0xf4:
+               case 0xf5:
+                       port->state = STATE_UNKNOWN;
+                       break;
+               case 0xf6:
+                       gmidi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0);
+                       port->state = STATE_UNKNOWN;
+                       break;
+               case 0xf7:
+                       switch (port->state) {
+                       case STATE_SYSEX_0:
+                               gmidi_transmit_packet(req,
+                                       p0 | 0x05, 0xf7, 0, 0);
+                               break;
+                       case STATE_SYSEX_1:
+                               gmidi_transmit_packet(req,
+                                       p0 | 0x06, port->data[0], 0xf7, 0);
+                               break;
+                       case STATE_SYSEX_2:
+                               gmidi_transmit_packet(req,
+                                       p0 | 0x07, port->data[0],
+                                       port->data[1], 0xf7);
+                               break;
+                       }
+                       port->state = STATE_UNKNOWN;
+                       break;
+               }
+       } else if (b >= 0x80) {
+               port->data[0] = b;
+               if (b >= 0xc0 && b <= 0xdf)
+                       port->state = STATE_1PARAM;
+               else
+                       port->state = STATE_2PARAM_1;
+       } else { /* b < 0x80 */
+               switch (port->state) {
+               case STATE_1PARAM:
+                       if (port->data[0] < 0xf0) {
+                               p0 |= port->data[0] >> 4;
+                       } else {
+                               p0 |= 0x02;
+                               port->state = STATE_UNKNOWN;
+                       }
+                       gmidi_transmit_packet(req, p0, port->data[0], b, 0);
+                       break;
+               case STATE_2PARAM_1:
+                       port->data[1] = b;
+                       port->state = STATE_2PARAM_2;
+                       break;
+               case STATE_2PARAM_2:
+                       if (port->data[0] < 0xf0) {
+                               p0 |= port->data[0] >> 4;
+                               port->state = STATE_2PARAM_1;
+                       } else {
+                               p0 |= 0x03;
+                               port->state = STATE_UNKNOWN;
+                       }
+                       gmidi_transmit_packet(req,
+                               p0, port->data[0], port->data[1], b);
+                       break;
+               case STATE_SYSEX_0:
+                       port->data[0] = b;
+                       port->state = STATE_SYSEX_1;
+                       break;
+               case STATE_SYSEX_1:
+                       port->data[1] = b;
+                       port->state = STATE_SYSEX_2;
+                       break;
+               case STATE_SYSEX_2:
+                       gmidi_transmit_packet(req,
+                               p0 | 0x04, port->data[0], port->data[1], b);
+                       port->state = STATE_SYSEX_0;
+                       break;
+               }
+       }
+}
+
+static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req)
+{
+       struct usb_ep* ep = dev->in_ep;
+       struct gmidi_in_port* port = &dev->in_port;
+
+       if (!ep) {
+               return;
+       }
+       if (!req) {
+               req = alloc_ep_req(ep, buflen);
+       }
+       if (!req) {
+               ERROR(dev, "gmidi_transmit: alloc_ep_request failed\n");
+               return;
+       }
+       req->length = 0;
+       req->complete = gmidi_complete;
+
+       if (port->active) {
+               while (req->length + 3 < buflen) {
+                       uint8_t b;
+                       if (snd_rawmidi_transmit(dev->in_substream, &b, 1)
+                               != 1)
+                       {
+                               port->active = 0;
+                               break;
+                       }
+                       gmidi_transmit_byte(req, port, b);
+               }
+       }
+       if (req->length > 0) {
+               usb_ep_queue(ep, req, GFP_ATOMIC);
+       } else {
+               free_ep_req(ep, req);
+       }
+}
+
+static void gmidi_in_tasklet(unsigned long data)
+{
+       struct gmidi_device* dev = (struct gmidi_device*)data;
+
+       gmidi_transmit(dev, NULL);
+}
+
+static int gmidi_in_open(struct snd_rawmidi_substream *substream)
+{
+       struct gmidi_device* dev = substream->rmidi->private_data;
+
+       VDBG(dev, "gmidi_in_open\n");
+       dev->in_substream = substream;
+       dev->in_port.state = STATE_UNKNOWN;
+       return 0;
+}
+
+static int gmidi_in_close(struct snd_rawmidi_substream *substream)
+{
+       VDBG(dev, "gmidi_in_close\n");
+       return 0;
+}
+
+static void gmidi_in_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+       struct gmidi_device* dev = substream->rmidi->private_data;
+
+       VDBG(dev, "gmidi_in_trigger %d\n", up);
+       dev->in_port.active = up;
+       if (up) {
+               tasklet_hi_schedule(&dev->tasklet);
+       }
+}
+
+static int gmidi_out_open(struct snd_rawmidi_substream *substream)
+{
+       struct gmidi_device* dev = substream->rmidi->private_data;
+
+       VDBG(dev, "gmidi_out_open\n");
+       dev->out_substream = substream;
+       return 0;
+}
+
+static int gmidi_out_close(struct snd_rawmidi_substream *substream)
+{
+       VDBG(dev, "gmidi_out_close\n");
+       return 0;
+}
+
+static void gmidi_out_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+       struct gmidi_device* dev = substream->rmidi->private_data;
+
+       VDBG(dev, "gmidi_out_trigger %d\n", up);
+       if (up) {
+               set_bit(substream->number, &dev->out_triggered);
+       } else {
+               clear_bit(substream->number, &dev->out_triggered);
+       }
+}
+
+static struct snd_rawmidi_ops gmidi_in_ops = {
+       .open = gmidi_in_open,
+       .close = gmidi_in_close,
+       .trigger = gmidi_in_trigger,
+};
+
+static struct snd_rawmidi_ops gmidi_out_ops = {
+       .open = gmidi_out_open,
+       .close = gmidi_out_close,
+       .trigger = gmidi_out_trigger
+};
+
+/* register as a sound "card" */
+static int gmidi_register_card(struct gmidi_device *dev)
+{
+       struct snd_card *card;
+       struct snd_rawmidi *rmidi;
+       int err;
+       int out_ports = 1;
+       int in_ports = 1;
+       static struct snd_device_ops ops = {
+               .dev_free = gmidi_snd_free,
+       };
+
+       card = snd_card_new(index, id, THIS_MODULE, 0);
+       if (!card) {
+               ERROR(dev, "snd_card_new failed\n");
+               err = -ENOMEM;
+               goto fail;
+       }
+       dev->card = card;
+
+       err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, dev, &ops);
+       if (err < 0) {
+               ERROR(dev, "snd_device_new failed: error %d\n", err);
+               goto fail;
+       }
+
+       strcpy(card->driver, longname);
+       strcpy(card->longname, longname);
+       strcpy(card->shortname, shortname);
+
+       /* Set up rawmidi */
+       dev->in_port.dev = dev;
+       dev->in_port.active = 0;
+       snd_component_add(card, "MIDI");
+       err = snd_rawmidi_new(card, "USB MIDI Gadget", 0,
+                             out_ports, in_ports, &rmidi);
+       if (err < 0) {
+               ERROR(dev, "snd_rawmidi_new failed: error %d\n", err);
+               goto fail;
+       }
+       dev->rmidi = rmidi;
+       strcpy(rmidi->name, card->shortname);
+       rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+                           SNDRV_RAWMIDI_INFO_INPUT |
+                           SNDRV_RAWMIDI_INFO_DUPLEX;
+       rmidi->private_data = dev;
+
+       /* Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT.
+          It's an upside-down world being a gadget. */
+       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops);
+       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops);
+
+       snd_card_set_dev(card, &dev->gadget->dev);
+
+       /* register it - we're ready to go */
+       err = snd_card_register(card);
+       if (err < 0) {
+               ERROR(dev, "snd_card_register failed\n");
+               goto fail;
+       }
+
+       VDBG(dev, "gmidi_register_card finished ok\n");
+       return 0;
+
+fail:
+       if (dev->card) {
+               snd_card_free(dev->card);
+               dev->card = NULL;
+       }
+       return err;
+}
+
+/*
+ * Creates an output endpoint, and initializes output ports.
+ */
+static int __devinit gmidi_bind(struct usb_gadget *gadget)
+{
+       struct gmidi_device *dev;
+       struct usb_ep *in_ep, *out_ep;
+       int gcnum, err = 0;
+
+       /* support optional vendor/distro customization */
+       if (idVendor) {
+               if (!idProduct) {
+                       printk(KERN_ERR "idVendor needs idProduct!\n");
+                       return -ENODEV;
+               }
+               device_desc.idVendor = cpu_to_le16(idVendor);
+               device_desc.idProduct = cpu_to_le16(idProduct);
+               if (bcdDevice) {
+                       device_desc.bcdDevice = cpu_to_le16(bcdDevice);
+               }
+       }
+       if (iManufacturer) {
+               strlcpy(manufacturer, iManufacturer, sizeof(manufacturer));
+       } else {
+               snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s",
+                       system_utsname.sysname, system_utsname.release,
+                       gadget->name);
+       }
+       if (iProduct) {
+               strlcpy(product_desc, iProduct, sizeof(product_desc));
+       }
+       if (iSerialNumber) {
+               device_desc.iSerialNumber = STRING_SERIAL,
+               strlcpy(serial_number, iSerialNumber, sizeof(serial_number));
+       }
+
+       /* Bulk-only drivers like this one SHOULD be able to
+        * autoconfigure on any sane usb controller driver,
+        * but there may also be important quirks to address.
+        */
+       usb_ep_autoconfig_reset(gadget);
+       in_ep = usb_ep_autoconfig(gadget, &bulk_in_desc);
+       if (!in_ep) {
+autoconf_fail:
+               printk(KERN_ERR "%s: can't autoconfigure on %s\n",
+                       shortname, gadget->name);
+               return -ENODEV;
+       }
+       EP_IN_NAME = in_ep->name;
+       in_ep->driver_data = in_ep;     /* claim */
+
+       out_ep = usb_ep_autoconfig(gadget, &bulk_out_desc);
+       if (!out_ep) {
+               goto autoconf_fail;
+       }
+       EP_OUT_NAME = out_ep->name;
+       out_ep->driver_data = out_ep;   /* claim */
+
+       gcnum = usb_gadget_controller_number(gadget);
+       if (gcnum >= 0) {
+               device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
+       } else {
+               /* gmidi is so simple (no altsettings) that
+                * it SHOULD NOT have problems with bulk-capable hardware.
+                * so warn about unrecognized controllers, don't panic.
+                */
+               printk(KERN_WARNING "%s: controller '%s' not recognized\n",
+                       shortname, gadget->name);
+               device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
+       }
+
+
+       /* ok, we made sense of the hardware ... */
+       dev = kzalloc(sizeof(*dev), SLAB_KERNEL);
+       if (!dev) {
+               return -ENOMEM;
+       }
+       spin_lock_init(&dev->lock);
+       dev->gadget = gadget;
+       dev->in_ep = in_ep;
+       dev->out_ep = out_ep;
+       set_gadget_data(gadget, dev);
+       tasklet_init(&dev->tasklet, gmidi_in_tasklet, (unsigned long)dev);
+
+       /* preallocate control response and buffer */
+       dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+       if (!dev->req) {
+               err = -ENOMEM;
+               goto fail;
+       }
+       dev->req->buf = usb_ep_alloc_buffer(gadget->ep0, USB_BUFSIZ,
+                               &dev->req->dma, GFP_KERNEL);
+       if (!dev->req->buf) {
+               err = -ENOMEM;
+               goto fail;
+       }
+
+       dev->req->complete = gmidi_setup_complete;
+
+       device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
+
+       gadget->ep0->driver_data = dev;
+
+       INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname);
+       INFO(dev, "using %s, OUT %s IN %s\n", gadget->name,
+               EP_OUT_NAME, EP_IN_NAME);
+
+       /* register as an ALSA sound card */
+       err = gmidi_register_card(dev);
+       if (err < 0) {
+               goto fail;
+       }
+
+       VDBG(dev, "gmidi_bind finished ok\n");
+       return 0;
+
+fail:
+       gmidi_unbind(gadget);
+       return err;
+}
+
+
+static void gmidi_suspend(struct usb_gadget *gadget)
+{
+       struct gmidi_device *dev = get_gadget_data(gadget);
+
+       if (gadget->speed == USB_SPEED_UNKNOWN) {
+               return;
+       }
+
+       DBG(dev, "suspend\n");
+}
+
+static void gmidi_resume(struct usb_gadget *gadget)
+{
+       struct gmidi_device *dev = get_gadget_data(gadget);
+
+       DBG(dev, "resume\n");
+}
+
+
+static struct usb_gadget_driver gmidi_driver = {
+       .speed          = USB_SPEED_FULL,
+       .function       = (char *)longname,
+       .bind           = gmidi_bind,
+       .unbind         = __exit_p(gmidi_unbind),
+
+       .setup          = gmidi_setup,
+       .disconnect     = gmidi_disconnect,
+
+       .suspend        = gmidi_suspend,
+       .resume         = gmidi_resume,
+
+       .driver         = {
+               .name           = (char *)shortname,
+               .owner          = THIS_MODULE,
+       },
+};
+
+static int __init gmidi_init(void)
+{
+       return usb_gadget_register_driver(&gmidi_driver);
+}
+module_init(gmidi_init);
+
+static void __exit gmidi_cleanup(void)
+{
+       usb_gadget_unregister_driver(&gmidi_driver);
+}
+module_exit(gmidi_cleanup);
+
index 2a7162d..4655522 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/compiler.h>
 #include <asm/uaccess.h>
 #include <linux/slab.h>
+#include <linux/poll.h>
 
 #include <linux/device.h>
 #include <linux/moduleparam.h>
@@ -222,7 +223,6 @@ static void put_ep (struct ep_data *data)
        /* needs no more cleanup */
        BUG_ON (!list_empty (&data->epfiles));
        BUG_ON (waitqueue_active (&data->wait));
-       BUG_ON (down_trylock (&data->lock) != 0);
        kfree (data);
 }
 
@@ -477,6 +477,10 @@ static int
 ep_release (struct inode *inode, struct file *fd)
 {
        struct ep_data          *data = fd->private_data;
+       int value;
+
+       if ((value = down_interruptible(&data->lock)) < 0)
+               return value;
 
        /* clean up if this can be reopened */
        if (data->state != STATE_EP_UNBOUND) {
@@ -485,6 +489,7 @@ ep_release (struct inode *inode, struct file *fd)
                data->hs_desc.bDescriptorType = 0;
                usb_ep_disable(data->ep);
        }
+       up (&data->lock);
        put_ep (data);
        return 0;
 }
@@ -709,7 +714,7 @@ ep_aio_write(struct kiocb *iocb, const char __user *ubuf, size_t len, loff_t o)
 /*----------------------------------------------------------------------*/
 
 /* used after endpoint configuration */
-static struct file_operations ep_io_operations = {
+static const struct file_operations ep_io_operations = {
        .owner =        THIS_MODULE,
        .llseek =       no_llseek,
 
@@ -741,7 +746,7 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
        struct ep_data          *data = fd->private_data;
        struct usb_ep           *ep;
        u32                     tag;
-       int                     value;
+       int                     value, length = len;
 
        if ((value = down_interruptible (&data->lock)) < 0)
                return value;
@@ -792,7 +797,6 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
                        goto fail0;
                }
        }
-       value = len;
 
        spin_lock_irq (&data->dev->lock);
        if (data->dev->state == STATE_DEV_UNBOUND) {
@@ -822,8 +826,10 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
                                data->name);
                data->state = STATE_EP_DEFER_ENABLE;
        }
-       if (value == 0)
+       if (value == 0) {
                fd->f_op = &ep_io_operations;
+               value = length;
+       }
 gone:
        spin_unlock_irq (&data->dev->lock);
        if (value < 0) {
@@ -867,7 +873,7 @@ ep_open (struct inode *inode, struct file *fd)
 }
 
 /* used before endpoint configuration */
-static struct file_operations ep_config_operations = {
+static const struct file_operations ep_config_operations = {
        .owner =        THIS_MODULE,
        .llseek =       no_llseek,
 
@@ -1009,7 +1015,7 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
                        else {
                                len = min (len, (size_t)dev->req->actual);
 // FIXME don't call this with the spinlock held ...
-                               if (copy_to_user (buf, &dev->req->buf, len))
+                               if (copy_to_user (buf, dev->req->buf, len))
                                        retval = -EFAULT;
                                clean_req (dev->gadget->ep0, dev->req);
                                /* NOTE userspace can't yet choose to stall */
@@ -1229,6 +1235,35 @@ dev_release (struct inode *inode, struct file *fd)
        return 0;
 }
 
+static unsigned int
+ep0_poll (struct file *fd, poll_table *wait)
+{
+       struct dev_data         *dev = fd->private_data;
+       int                     mask = 0;
+
+       poll_wait(fd, &dev->wait, wait);
+
+       spin_lock_irq (&dev->lock);
+
+       /* report fd mode change before acting on it */
+       if (dev->setup_abort) {
+               dev->setup_abort = 0;
+               mask = POLLHUP;
+               goto out;
+       }
+
+       if (dev->state == STATE_SETUP) {
+               if (dev->setup_in || dev->setup_can_stall)
+                       mask = POLLOUT;
+       } else {
+               if (dev->ev_next != 0)
+                       mask = POLLIN;
+       }
+out:
+       spin_unlock_irq(&dev->lock);
+       return mask;
+}
+
 static int dev_ioctl (struct inode *inode, struct file *fd,
                unsigned code, unsigned long value)
 {
@@ -1241,14 +1276,14 @@ static int dev_ioctl (struct inode *inode, struct file *fd,
 }
 
 /* used after device configuration */
-static struct file_operations ep0_io_operations = {
+static const struct file_operations ep0_io_operations = {
        .owner =        THIS_MODULE,
        .llseek =       no_llseek,
 
        .read =         ep0_read,
        .write =        ep0_write,
        .fasync =       ep0_fasync,
-       // .poll =      ep0_poll,
+       .poll =         ep0_poll,
        .ioctl =        dev_ioctl,
        .release =      dev_release,
 };
@@ -1696,16 +1731,17 @@ gadgetfs_disconnect (struct usb_gadget *gadget)
 {
        struct dev_data         *dev = get_gadget_data (gadget);
 
+       spin_lock (&dev->lock);
        if (dev->state == STATE_UNCONNECTED) {
                DBG (dev, "already unconnected\n");
-               return;
+               goto exit;
        }
        dev->state = STATE_UNCONNECTED;
 
        INFO (dev, "disconnected\n");
-       spin_lock (&dev->lock);
        next_event (dev, GADGETFS_DISCONNECT);
        ep0_readable (dev);
+exit:
        spin_unlock (&dev->lock);
 }
 
@@ -1922,7 +1958,7 @@ dev_open (struct inode *inode, struct file *fd)
        return value;
 }
 
-static struct file_operations dev_init_operations = {
+static const struct file_operations dev_init_operations = {
        .owner =        THIS_MODULE,
        .llseek =       no_llseek,
 
index 0924323..3bda37f 100644 (file)
@@ -2,7 +2,7 @@
  * Driver for the PLX NET2280 USB device controller.
  * Specs and errata are available from <http://www.plxtech.com>.
  *
- * PLX Technology Inc. (formerly NetChip Technology) supported the 
+ * PLX Technology Inc. (formerly NetChip Technology) supported the
  * development of this driver.
  *
  *
@@ -26,7 +26,8 @@
  * Copyright (C) 2003 David Brownell
  * Copyright (C) 2003-2005 PLX Technology, Inc.
  *
- * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility with 2282 chip
+ * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility
+ *     with 2282 chip
  *
  * 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
@@ -85,7 +86,7 @@ static const char driver_name [] = "net2280";
 static const char driver_desc [] = DRIVER_DESC;
 
 static const char ep0name [] = "ep0";
-static const char *ep_name [] = {
+static const char *const ep_name [] = {
        ep0name,
        "ep-a", "ep-b", "ep-c", "ep-d",
        "ep-e", "ep-f",
@@ -225,7 +226,9 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
        if (!ep->is_in)
                writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
        else if (dev->pdev->device != 0x2280) {
-               /* Added for 2282, Don't use nak packets on an in endpoint, this was ignored on 2280 */
+               /* Added for 2282, Don't use nak packets on an in endpoint,
+                * this was ignored on 2280
+                */
                writel ((1 << CLEAR_NAK_OUT_PACKETS)
                        | (1 << CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp);
        }
@@ -288,7 +291,7 @@ static int handshake (u32 __iomem *ptr, u32 mask, u32 done, int usec)
        return -ETIMEDOUT;
 }
 
-static struct usb_ep_ops net2280_ep_ops;
+static const struct usb_ep_ops net2280_ep_ops;
 
 static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep)
 {
@@ -449,34 +452,15 @@ net2280_free_request (struct usb_ep *_ep, struct usb_request *_req)
 
 /*-------------------------------------------------------------------------*/
 
-#undef USE_KMALLOC
-
-/* many common platforms have dma-coherent caches, which means that it's
- * safe to use kmalloc() memory for all i/o buffers without using any
- * cache flushing calls.  (unless you're trying to share cache lines
- * between dma and non-dma activities, which is a slow idea in any case.)
+/*
+ * dma-coherent memory allocation (for dma-capable endpoints)
  *
- * other platforms need more care, with 2.5 having a moderately general
- * solution (which falls down for allocations smaller than one page)
- * that improves significantly on the 2.4 PCI allocators by removing
- * the restriction that memory never be freed in_interrupt().
+ * NOTE: the dma_*_coherent() API calls suck.  Most implementations are
+ * (a) page-oriented, so small buffers lose big; and (b) asymmetric with
+ * respect to calls with irqs disabled:  alloc is safe, free is not.
+ * We currently work around (b), but not (a).
  */
-#if    defined(CONFIG_X86)
-#define USE_KMALLOC
-
-#elif  defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE)
-#define USE_KMALLOC
 
-#elif  defined(CONFIG_MIPS) && !defined(CONFIG_DMA_NONCOHERENT)
-#define USE_KMALLOC
-
-/* FIXME there are other cases, including an x86-64 one ...  */
-#endif
-
-/* allocating buffers this way eliminates dma mapping overhead, which
- * on some platforms will mean eliminating a per-io buffer copy.  with
- * some kinds of system caches, further tweaks may still be needed.
- */
 static void *
 net2280_alloc_buffer (
        struct usb_ep           *_ep,
@@ -493,43 +477,71 @@ net2280_alloc_buffer (
                return NULL;
        *dma = DMA_ADDR_INVALID;
 
-#if    defined(USE_KMALLOC)
-       retval = kmalloc(bytes, gfp_flags);
-       if (retval)
-               *dma = virt_to_phys(retval);
-#else
-       if (ep->dma) {
-               /* the main problem with this call is that it wastes memory
-                * on typical 1/N page allocations: it allocates 1-N pages.
-                */
-#warning Using dma_alloc_coherent even with buffers smaller than a page.
+       if (ep->dma)
                retval = dma_alloc_coherent(&ep->dev->pdev->dev,
                                bytes, dma, gfp_flags);
-       else
+       else
                retval = kmalloc(bytes, gfp_flags);
-#endif
        return retval;
 }
 
+static DEFINE_SPINLOCK(buflock);
+static LIST_HEAD(buffers);
+
+struct free_record {
+       struct list_head        list;
+       struct device           *dev;
+       unsigned                bytes;
+       dma_addr_t              dma;
+};
+
+static void do_free(unsigned long ignored)
+{
+       spin_lock_irq(&buflock);
+       while (!list_empty(&buffers)) {
+               struct free_record      *buf;
+
+               buf = list_entry(buffers.next, struct free_record, list);
+               list_del(&buf->list);
+               spin_unlock_irq(&buflock);
+
+               dma_free_coherent(buf->dev, buf->bytes, buf, buf->dma);
+
+               spin_lock_irq(&buflock);
+       }
+       spin_unlock_irq(&buflock);
+}
+
+static DECLARE_TASKLET(deferred_free, do_free, 0);
+
 static void
 net2280_free_buffer (
        struct usb_ep *_ep,
-       void *buf,
+       void *address,
        dma_addr_t dma,
        unsigned bytes
 ) {
        /* free memory into the right allocator */
-#ifndef        USE_KMALLOC
        if (dma != DMA_ADDR_INVALID) {
                struct net2280_ep       *ep;
+               struct free_record      *buf = address;
+               unsigned long           flags;
 
                ep = container_of(_ep, struct net2280_ep, ep);
                if (!_ep)
                        return;
-               dma_free_coherent(&ep->dev->pdev->dev, bytes, buf, dma);
+
+               ep = container_of (_ep, struct net2280_ep, ep);
+               buf->dev = &ep->dev->pdev->dev;
+               buf->bytes = bytes;
+               buf->dma = dma;
+
+               spin_lock_irqsave(&buflock, flags);
+               list_add_tail(&buf->list, &buffers);
+               tasklet_schedule(&deferred_free);
+               spin_unlock_irqrestore(&buflock, flags);
        } else
-#endif
-               kfree (buf);
+               kfree (address);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -737,7 +749,8 @@ fill_dma_desc (struct net2280_ep *ep, struct net2280_request *req, int valid)
         */
        if (ep->is_in)
                dmacount |= (1 << DMA_DIRECTION);
-       if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0) || ep->dev->pdev->device != 0x2280)
+       if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0)
+                       || ep->dev->pdev->device != 0x2280)
                dmacount |= (1 << END_OF_CHAIN);
 
        req->valid = valid;
@@ -812,7 +825,7 @@ static void start_dma (struct net2280_ep *ep, struct net2280_request *req)
 
        /* previous OUT packet might have been short */
        if (!ep->is_in && ((tmp = readl (&ep->regs->ep_stat))
-                               & (1 << NAK_OUT_PACKETS)) != 0) {
+                               & (1 << NAK_OUT_PACKETS)) != 0) {
                writel ((1 << SHORT_PACKET_TRANSFERRED_INTERRUPT),
                        &ep->regs->ep_stat);
 
@@ -1373,7 +1386,7 @@ net2280_fifo_flush (struct usb_ep *_ep)
        (void) readl (&ep->regs->ep_rsp);
 }
 
-static struct usb_ep_ops net2280_ep_ops = {
+static const struct usb_ep_ops net2280_ep_ops = {
        .enable         = net2280_enable,
        .disable        = net2280_disable,
 
@@ -1631,7 +1644,7 @@ show_registers (struct device *_dev, struct device_attribute *attr, char *buf)
        }
 
        /* Indexed Registers */
-               // none yet 
+               // none yet
 
        /* Statistics */
        t = scnprintf (next, size, "\nirqs:  ");
@@ -1691,11 +1704,11 @@ show_queues (struct device *_dev, struct device_attribute *attr, char *buf)
                                ({ char *val;
                                 switch (d->bmAttributes & 0x03) {
                                 case USB_ENDPOINT_XFER_BULK:
-                                       val = "bulk"; break;
+                                       val = "bulk"; break;
                                 case USB_ENDPOINT_XFER_INT:
-                                       val = "intr"; break;
+                                       val = "intr"; break;
                                 default:
-                                       val = "iso"; break;
+                                       val = "iso"; break;
                                 }; val; }),
                                le16_to_cpu (d->wMaxPacketSize) & 0x1fff,
                                ep->dma ? "dma" : "pio", ep->fifo_size
@@ -1808,8 +1821,8 @@ 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
  * @mode: 0 for default, four 1kB buffers (ep-a through ep-d);
- *     1 for two 2kB buffers (ep-a and ep-b only);
- *     2 for one 2kB buffer (ep-a) and two 1kB ones (ep-b, ep-c).
+ *     1 for two 2kB buffers (ep-a and ep-b only);
+ *     2 for one 2kB buffer (ep-a) and two 1kB ones (ep-b, ep-c).
  *
  * returns zero on success, else negative errno.  when this succeeds,
  * the contents of gadget->ep_list may have changed.
@@ -2241,7 +2254,8 @@ static void handle_ep_small (struct net2280_ep *ep)
                                req->td->dmacount = 0;
                                t = readl (&ep->regs->ep_avail);
                                dma_done (ep, req, count,
-                                       (ep->out_overflow || t) ? -EOVERFLOW : 0);
+                                       (ep->out_overflow || t)
+                                               ? -EOVERFLOW : 0);
                        }
 
                        /* also flush to prevent erratum 0106 trouble */
@@ -2411,7 +2425,7 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat)
                        , &ep->regs->ep_stat);
                u.raw [0] = readl (&dev->usb->setup0123);
                u.raw [1] = readl (&dev->usb->setup4567);
-               
+
                cpu_to_le32s (&u.raw [0]);
                cpu_to_le32s (&u.raw [1]);
 
@@ -2578,14 +2592,16 @@ static void handle_stat1_irqs (struct net2280 *dev, u32 stat)
 
        /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set.
         * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRRUPT set and
-        * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT 
+        * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT
         * only indicates a change in the reset state).
         */
        if (stat & tmp) {
                writel (tmp, &dev->regs->irqstat1);
-               if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) && 
-                               ((readl (&dev->usb->usbstat) & mask) == 0))
-                               || ((readl (&dev->usb->usbctl) & (1 << VBUS_PIN)) == 0) 
+               if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT))
+                                       && ((readl (&dev->usb->usbstat) & mask)
+                                                       == 0))
+                               || ((readl (&dev->usb->usbctl)
+                                       & (1 << VBUS_PIN)) == 0)
                            ) && ( dev->gadget.speed != USB_SPEED_UNKNOWN)) {
                        DEBUG (dev, "disconnect %s\n",
                                        dev->driver->driver.name);
@@ -2852,7 +2868,7 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
 
        /* now all the pci goodies ... */
        if (pci_enable_device (pdev) < 0) {
-               retval = -ENODEV;
+               retval = -ENODEV;
                goto done;
        }
        dev->enabled = 1;
@@ -2870,6 +2886,10 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
        }
        dev->region = 1;
 
+       /* FIXME provide firmware download interface to put
+        * 8051 code into the chip, e.g. to turn on PCI PM.
+        */
+
        base = ioremap_nocache (resource, len);
        if (base == NULL) {
                DEBUG (dev, "can't map memory\n");
@@ -2984,16 +3004,16 @@ static void net2280_shutdown (struct pci_dev *pdev)
 
 /*-------------------------------------------------------------------------*/
 
-static struct pci_device_id pci_ids [] = { {
-       .class =        ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
-       .class_mask =   ~0,
+static const struct pci_device_id pci_ids [] = { {
+       .class =        ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+       .class_mask =   ~0,
        .vendor =       0x17cc,
        .device =       0x2280,
        .subvendor =    PCI_ANY_ID,
        .subdevice =    PCI_ANY_ID,
 }, {
-       .class =        ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
-       .class_mask =   ~0,
+       .class =        ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+       .class_mask =   ~0,
        .vendor =       0x17cc,
        .device =       0x2282,
        .subvendor =    PCI_ANY_ID,
index 2de9748..0a64504 100644 (file)
@@ -40,7 +40,7 @@
 #include <linux/platform_device.h>
 #include <linux/usb_ch9.h>
 #include <linux/usb_gadget.h>
-#include <linux/usb_otg.h>
+#include <linux/usb/otg.h>
 #include <linux/dma-mapping.h>
 
 #include <asm/byteorder.h>
@@ -2437,7 +2437,7 @@ static int proc_udc_open(struct inode *inode, struct file *file)
        return single_open(file, proc_udc_show, NULL);
 }
 
-static struct file_operations proc_ops = {
+static const struct file_operations proc_ops = {
        .open           = proc_udc_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
index fff027d..f1adcf8 100644 (file)
@@ -150,6 +150,39 @@ MODULE_PARM_DESC (fifo_mode, "pxa2xx udc fifo mode");
 static void pxa2xx_ep_fifo_flush (struct usb_ep *ep);
 static void nuke (struct pxa2xx_ep *, int status);
 
+/* one GPIO should be used to detect VBUS from the host */
+static int is_vbus_present(void)
+{
+       struct pxa2xx_udc_mach_info             *mach = the_controller->mach;
+
+       if (mach->gpio_vbus)
+               return pxa_gpio_get(mach->gpio_vbus);
+       if (mach->udc_is_connected)
+               return mach->udc_is_connected();
+       return 1;
+}
+
+/* one GPIO should control a D+ pullup, so host sees this device (or not) */
+static void pullup_off(void)
+{
+       struct pxa2xx_udc_mach_info             *mach = the_controller->mach;
+
+       if (mach->gpio_pullup)
+               pxa_gpio_set(mach->gpio_pullup, 0);
+       else if (mach->udc_command)
+               mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
+}
+
+static void pullup_on(void)
+{
+       struct pxa2xx_udc_mach_info             *mach = the_controller->mach;
+
+       if (mach->gpio_pullup)
+               pxa_gpio_set(mach->gpio_pullup, 1);
+       else if (mach->udc_command)
+               mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+}
+
 static void pio_irq_enable(int bEndpointAddress)
 {
         bEndpointAddress &= 0xf;
@@ -1721,6 +1754,16 @@ lubbock_vbus_irq(int irq, void *_dev, struct pt_regs *r)
 
 #endif
 
+static irqreturn_t
+udc_vbus_irq(int irq, void *_dev, struct pt_regs *r)
+{
+       struct pxa2xx_udc       *dev = _dev;
+       int                     vbus = pxa_gpio_get(dev->mach->gpio_vbus);
+
+       pxa2xx_udc_vbus_session(&dev->gadget, vbus);
+       return IRQ_HANDLED;
+}
+
 
 /*-------------------------------------------------------------------------*/
 
@@ -2438,7 +2481,7 @@ static struct pxa2xx_udc memory = {
 static int __init pxa2xx_udc_probe(struct platform_device *pdev)
 {
        struct pxa2xx_udc *dev = &memory;
-       int retval, out_dma = 1;
+       int retval, out_dma = 1, vbus_irq;
        u32 chiprev;
 
        /* insist on Intel/ARM/XScale */
@@ -2502,6 +2545,16 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
        /* other non-static parts of init */
        dev->dev = &pdev->dev;
        dev->mach = pdev->dev.platform_data;
+       if (dev->mach->gpio_vbus) {
+               vbus_irq = IRQ_GPIO(dev->mach->gpio_vbus & GPIO_MD_MASK_NR);
+               pxa_gpio_mode((dev->mach->gpio_vbus & GPIO_MD_MASK_NR)
+                               | GPIO_IN);
+               set_irq_type(vbus_irq, IRQT_BOTHEDGE);
+       } else
+               vbus_irq = 0;
+       if (dev->mach->gpio_pullup)
+               pxa_gpio_mode((dev->mach->gpio_pullup & GPIO_MD_MASK_NR)
+                               | GPIO_OUT | GPIO_DFLT_LOW);
 
        init_timer(&dev->timer);
        dev->timer.function = udc_watchdog;
@@ -2557,8 +2610,19 @@ lubbock_fail0:
                HEX_DISPLAY(dev->stats.irqs);
                LUB_DISC_BLNK_LED &= 0xff;
 #endif
-       }
+       } else
 #endif
+       if (vbus_irq) {
+               retval = request_irq(vbus_irq, udc_vbus_irq,
+                               SA_INTERRUPT | SA_SAMPLE_RANDOM,
+                               driver_name, dev);
+               if (retval != 0) {
+                       printk(KERN_ERR "%s: can't get irq %i, err %d\n",
+                               driver_name, vbus_irq, retval);
+                       free_irq(IRQ_USB, dev);
+                       return -EBUSY;
+               }
+       }
        create_proc_files();
 
        return 0;
@@ -2587,6 +2651,8 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev)
                free_irq(LUBBOCK_USB_IRQ, dev);
        }
 #endif
+       if (dev->mach->gpio_vbus)
+               free_irq(IRQ_GPIO(dev->mach->gpio_vbus), dev);
        platform_set_drvdata(pdev, NULL);
        the_controller = NULL;
        return 0;
index 19a883f..8e598c8 100644 (file)
@@ -177,27 +177,19 @@ struct pxa2xx_udc {
 
 static struct pxa2xx_udc *the_controller;
 
-/* one GPIO should be used to detect VBUS from the host */
-static inline int is_vbus_present(void)
+static inline int pxa_gpio_get(unsigned gpio)
 {
-       if (!the_controller->mach->udc_is_connected)
-               return 1;
-       return the_controller->mach->udc_is_connected();
+       return (GPLR(gpio) & GPIO_bit(gpio)) != 0;
 }
 
-/* one GPIO should control a D+ pullup, so host sees this device (or not) */
-static inline void pullup_off(void)
+static inline void pxa_gpio_set(unsigned gpio, int is_on)
 {
-       if (!the_controller->mach->udc_command)
-               return;
-       the_controller->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
-}
+       int mask = GPIO_bit(gpio);
 
-static inline void pullup_on(void)
-{
-       if (!the_controller->mach->udc_command)
-               return;
-       the_controller->mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+       if (is_on)
+               GPSR(gpio) = mask;
+       else
+               GPCR(gpio) = mask;
 }
 
 /*-------------------------------------------------------------------------*/
index e762aa1..b893e31 100644 (file)
@@ -1120,12 +1120,15 @@ static int gs_send(struct gs_dev *dev)
 gs_debug_level(3, "gs_send: len=%d, 0x%2.2x 0x%2.2x 0x%2.2x ...\n", len, *((unsigned char *)req->buf), *((unsigned char *)req->buf+1), *((unsigned char *)req->buf+2));
                        list_del(&req_entry->re_entry);
                        req->length = len;
+                       spin_unlock_irqrestore(&dev->dev_lock, flags);
                        if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) {
                                printk(KERN_ERR
                                "gs_send: cannot queue read request, ret=%d\n",
                                        ret);
+                               spin_lock_irqsave(&dev->dev_lock, flags);
                                break;
                        }
+                       spin_lock_irqsave(&dev->dev_lock, flags);
                } else {
                        break;
                }
index b93d71d..cf10cbc 100644 (file)
@@ -83,6 +83,7 @@ config USB_OHCI_HCD
        tristate "OHCI HCD support"
        depends on USB && USB_ARCH_HAS_OHCI
        select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
+       select I2C if ARCH_PNX4008
        ---help---
          The Open Host Controller Interface (OHCI) is a standard for accessing
          USB 1.1 host controller hardware.  It does more in hardware than Intel's
@@ -141,6 +142,34 @@ config USB_UHCI_HCD
          To compile this driver as a module, choose M here: the
          module will be called uhci-hcd.
 
+config USB_U132_HCD
+       tristate "Elan U132 Adapter Host Controller"
+       depends on USB && USB_FTDI_ELAN
+       default M
+       help
+         The U132 adapter is a USB to CardBus adapter specifically designed
+         for PC cards that contain an OHCI host controller. Typical PC cards
+         are the Orange Mobile 3G Option GlobeTrotter Fusion card. The U132
+         adapter will *NOT* work with PC cards that do not contain an OHCI
+         controller.
+
+         For those PC cards that contain multiple OHCI controllers only ther
+         first one is used.
+
+         The driver consists of two modules, the "ftdi-elan" module is a
+         USB client driver that interfaces to the FTDI chip within ELAN's
+         USB-to-PCMCIA adapter, and this "u132-hcd" module is a USB host
+         controller driver that talks to the OHCI controller within the
+         CardBus cards that are inserted in the U132 adapter.
+
+         This driver has been tested with a CardBus OHCI USB adapter, and
+         worked with a USB PEN Drive inserted into the first USB port of
+         the PCCARD. A rather pointless thing to do, but useful for testing.
+
+         It is safe to say M here.
+
+         See also <http://www.elandigitalsystems.com/support/ufaq/u132linux.php>
+
 config USB_SL811_HCD
        tristate "SL811HS HCD support"
        depends on USB
index e3020f4..a2e58c8 100644 (file)
@@ -14,4 +14,5 @@ obj-$(CONFIG_USB_OHCI_HCD)    += ohci-hcd.o
 obj-$(CONFIG_USB_UHCI_HCD)     += uhci-hcd.o
 obj-$(CONFIG_USB_SL811_HCD)    += sl811-hcd.o
 obj-$(CONFIG_USB_SL811_CS)     += sl811_cs.o
+obj-$(CONFIG_USB_U132_HCD)     += u132-hcd.o
 obj-$(CONFIG_ETRAX_ARCH_V10)   += hc_crisv10.o
index 26ed757..5d1b12a 100644 (file)
@@ -200,6 +200,7 @@ static const struct hc_driver ehci_au1xxx_hc_driver = {
        .reset = ehci_init,
        .start = ehci_run,
        .stop = ehci_stop,
+       .shutdown = ehci_shutdown,
 
        /*
         * managing i/o requests and associated device resources
@@ -268,6 +269,7 @@ MODULE_ALIAS("au1xxx-ehci");
 static struct platform_driver ehci_hcd_au1xxx_driver = {
        .probe = ehci_hcd_au1xxx_drv_probe,
        .remove = ehci_hcd_au1xxx_drv_remove,
+       .shutdown = usb_hcd_platform_shutdown,
        /*.suspend      = ehci_hcd_au1xxx_drv_suspend, */
        /*.resume       = ehci_hcd_au1xxx_drv_resume, */
        .driver = {
index 65ac9fe..23b95b2 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2001-2002 by David Brownell
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -65,7 +65,7 @@ static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
                for (i = 0; i < HCS_N_PORTS (params); i++) {
                        // FIXME MIPS won't readb() ...
                        byte = readb (&ehci->caps->portroute[(i>>1)]);
-                       sprintf(tmp, "%d ", 
+                       sprintf(tmp, "%d ",
                                ((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf)));
                        strcat(buf, tmp);
                }
@@ -141,12 +141,12 @@ dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
 }
 
 static void __attribute__((__unused__))
-dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd) 
+dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
 {
        ehci_dbg (ehci, "%s [%d] itd %p, next %08x, urb %p\n",
                label, itd->frame, itd, le32_to_cpu(itd->hw_next), itd->urb);
        ehci_dbg (ehci,
-               "  trans: %08x %08x %08x %08x %08x %08x %08x %08x\n", 
+               "  trans: %08x %08x %08x %08x %08x %08x %08x %08x\n",
                le32_to_cpu(itd->hw_transaction[0]),
                le32_to_cpu(itd->hw_transaction[1]),
                le32_to_cpu(itd->hw_transaction[2]),
@@ -156,7 +156,7 @@ dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
                le32_to_cpu(itd->hw_transaction[6]),
                le32_to_cpu(itd->hw_transaction[7]));
        ehci_dbg (ehci,
-               "  buf:   %08x %08x %08x %08x %08x %08x %08x\n", 
+               "  buf:   %08x %08x %08x %08x %08x %08x %08x\n",
                le32_to_cpu(itd->hw_bufp[0]),
                le32_to_cpu(itd->hw_bufp[1]),
                le32_to_cpu(itd->hw_bufp[2]),
@@ -171,12 +171,12 @@ dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
 }
 
 static void __attribute__((__unused__))
-dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd) 
+dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd)
 {
        ehci_dbg (ehci, "%s [%d] sitd %p, next %08x, urb %p\n",
                label, sitd->frame, sitd, le32_to_cpu(sitd->hw_next), sitd->urb);
        ehci_dbg (ehci,
-               "  addr %08x sched %04x result %08x buf %08x %08x\n", 
+               "  addr %08x sched %04x result %08x buf %08x %08x\n",
                le32_to_cpu(sitd->hw_fullspeed_ep),
                le32_to_cpu(sitd->hw_uframe),
                le32_to_cpu(sitd->hw_results),
@@ -451,7 +451,7 @@ show_async (struct class_device *class_dev, char *buf)
        *buf = 0;
 
        bus = class_get_devdata(class_dev);
-       hcd = bus->hcpriv;
+       hcd = bus_to_hcd(bus);
        ehci = hcd_to_ehci (hcd);
        next = buf;
        size = PAGE_SIZE;
@@ -497,7 +497,7 @@ show_periodic (struct class_device *class_dev, char *buf)
        seen_count = 0;
 
        bus = class_get_devdata(class_dev);
-       hcd = bus->hcpriv;
+       hcd = bus_to_hcd(bus);
        ehci = hcd_to_ehci (hcd);
        next = buf;
        size = PAGE_SIZE;
@@ -634,7 +634,7 @@ show_registers (struct class_device *class_dev, char *buf)
        static char             label [] = "";
 
        bus = class_get_devdata(class_dev);
-       hcd = bus->hcpriv;
+       hcd = bus_to_hcd(bus);
        ehci = hcd_to_ehci (hcd);
        next = buf;
        size = PAGE_SIZE;
@@ -754,9 +754,7 @@ show_registers (struct class_device *class_dev, char *buf)
        }
 
        if (ehci->reclaim) {
-               temp = scnprintf (next, size, "reclaim qh %p%s\n",
-                               ehci->reclaim,
-                               ehci->reclaim_ready ? " ready" : "");
+               temp = scnprintf (next, size, "reclaim qh %p\n", ehci->reclaim);
                size -= temp;
                next += temp;
        }
@@ -785,10 +783,11 @@ static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
 static inline void create_debug_files (struct ehci_hcd *ehci)
 {
        struct class_device *cldev = ehci_to_hcd(ehci)->self.class_dev;
+       int retval;
 
-       class_device_create_file(cldev, &class_device_attr_async);
-       class_device_create_file(cldev, &class_device_attr_periodic);
-       class_device_create_file(cldev, &class_device_attr_registers);
+       retval = class_device_create_file(cldev, &class_device_attr_async);
+       retval = class_device_create_file(cldev, &class_device_attr_periodic);
+       retval = class_device_create_file(cldev, &class_device_attr_registers);
 }
 
 static inline void remove_debug_files (struct ehci_hcd *ehci)
index d030516..1a915e9 100644 (file)
@@ -285,6 +285,7 @@ static const struct hc_driver ehci_fsl_hc_driver = {
        .resume = ehci_bus_resume,
 #endif
        .stop = ehci_stop,
+       .shutdown = ehci_shutdown,
 
        /*
         * managing i/o requests and associated device resources
@@ -329,6 +330,7 @@ MODULE_ALIAS("fsl-ehci");
 static struct platform_driver ehci_fsl_driver = {
        .probe = ehci_fsl_drv_probe,
        .remove = ehci_fsl_drv_remove,
+       .shutdown = usb_hcd_platform_shutdown,
        .driver = {
                   .name = "fsl-ehci",
                   },
index d63177a..5ac9185 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2000-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
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -70,7 +70,7 @@
  * 2002-08-06  Handling for bulk and interrupt transfers is mostly shared;
  *     only scheduling is different, no arbitrary limitations.
  * 2002-07-25  Sanity check PCI reads, mostly for better cardbus support,
- *     clean up HC run state handshaking.
+ *     clean up HC run state handshaking.
  * 2002-05-24  Preliminary FS/LS interrupts, using scheduling shortcuts
  * 2002-05-11  Clear TT errors for FS/LS ctrl/bulk.  Fill in some other
  *     missing pieces:  enabling 64bit dma, handoff from BIOS/SMM.
@@ -111,7 +111,7 @@ static const char   hcd_name [] = "ehci_hcd";
 #define        EHCI_TUNE_MULT_TT       1
 #define        EHCI_TUNE_FLS           2       /* (small) 256 frame schedule */
 
-#define EHCI_IAA_JIFFIES       (HZ/100)        /* arbitrary; ~10 msec */
+#define EHCI_IAA_MSECS         10              /* arbitrary */
 #define EHCI_IO_JIFFIES                (HZ/10)         /* io watchdog > irq_thresh */
 #define EHCI_ASYNC_JIFFIES     (HZ/20)         /* async idle timeout */
 #define EHCI_SHRINK_JIFFIES    (HZ/200)        /* async qh unlink delay */
@@ -254,6 +254,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
 
 /*-------------------------------------------------------------------------*/
 
+static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs);
 static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
 
 #include "ehci-hub.c"
@@ -263,28 +264,39 @@ static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
 
 /*-------------------------------------------------------------------------*/
 
-static void ehci_watchdog (unsigned long param)
+static void ehci_iaa_watchdog (unsigned long param)
 {
        struct ehci_hcd         *ehci = (struct ehci_hcd *) param;
        unsigned long           flags;
+       u32                     status;
 
        spin_lock_irqsave (&ehci->lock, flags);
+       WARN_ON(!ehci->reclaim);
 
-       /* lost IAA irqs wedge things badly; seen with a vt8235 */
+       /* lost IAA irqs wedge things badly; seen first with a vt8235 */
        if (ehci->reclaim) {
-               u32             status = readl (&ehci->regs->status);
-
+               status = readl (&ehci->regs->status);
                if (status & STS_IAA) {
                        ehci_vdbg (ehci, "lost IAA\n");
                        COUNT (ehci->stats.lost_iaa);
                        writel (STS_IAA, &ehci->regs->status);
-                       ehci->reclaim_ready = 1;
+                       end_unlink_async (ehci, NULL);
                }
        }
 
-       /* stop async processing after it's idled a bit */
+       spin_unlock_irqrestore (&ehci->lock, flags);
+}
+
+static void ehci_watchdog (unsigned long param)
+{
+       struct ehci_hcd         *ehci = (struct ehci_hcd *) param;
+       unsigned long           flags;
+
+       spin_lock_irqsave (&ehci->lock, flags);
+
+       /* stop async processing after it's idled a bit */
        if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
-               start_unlink_async (ehci, ehci->async);
+               start_unlink_async (ehci, ehci->async);
 
        /* ehci could run by timer, without IRQs ... */
        ehci_work (ehci, NULL);
@@ -292,21 +304,20 @@ static void ehci_watchdog (unsigned long param)
        spin_unlock_irqrestore (&ehci->lock, flags);
 }
 
-/* Reboot notifiers kick in for silicon on any bus (not just pci, etc).
+/* ehci_shutdown kick in for silicon on any bus (not just pci, etc).
  * This forcibly disables dma and IRQs, helping kexec and other cases
  * where the next system software may expect clean state.
  */
-static int
-ehci_reboot (struct notifier_block *self, unsigned long code, void *null)
+static void
+ehci_shutdown (struct usb_hcd *hcd)
 {
-       struct ehci_hcd         *ehci;
+       struct ehci_hcd *ehci;
 
-       ehci = container_of (self, struct ehci_hcd, reboot_notifier);
+       ehci = hcd_to_ehci (hcd);
        (void) ehci_halt (ehci);
 
        /* make BIOS/etc use companion controller during reboot */
        writel (0, &ehci->regs->configured_flag);
-       return 0;
 }
 
 static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
@@ -334,8 +345,6 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
 static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
 {
        timer_action_done (ehci, TIMER_IO_WATCHDOG);
-       if (ehci->reclaim_ready)
-               end_unlink_async (ehci, regs);
 
        /* another CPU may drop ehci->lock during a schedule scan while
         * it reports urb completions.  this flag guards against bogus
@@ -370,6 +379,7 @@ static void ehci_stop (struct usb_hcd *hcd)
 
        /* no more interrupts ... */
        del_timer_sync (&ehci->watchdog);
+       del_timer_sync (&ehci->iaa_watchdog);
 
        spin_lock_irq(&ehci->lock);
        if (HC_IS_RUNNING (hcd->state))
@@ -381,7 +391,6 @@ static void ehci_stop (struct usb_hcd *hcd)
 
        /* let companion controllers work when we aren't */
        writel (0, &ehci->regs->configured_flag);
-       unregister_reboot_notifier (&ehci->reboot_notifier);
 
        remove_debug_files (ehci);
 
@@ -417,6 +426,10 @@ static int ehci_init(struct usb_hcd *hcd)
        ehci->watchdog.function = ehci_watchdog;
        ehci->watchdog.data = (unsigned long) ehci;
 
+       init_timer(&ehci->iaa_watchdog);
+       ehci->iaa_watchdog.function = ehci_iaa_watchdog;
+       ehci->iaa_watchdog.data = (unsigned long) ehci;
+
        /*
         * hw default: 1K periodic list heads, one per frame.
         * periodic_size can shrink by USBCMD update if hcc_params allows.
@@ -427,13 +440,12 @@ static int ehci_init(struct usb_hcd *hcd)
 
        /* controllers may cache some of the periodic schedule ... */
        hcc_params = readl(&ehci->caps->hcc_params);
-       if (HCC_ISOC_CACHE(hcc_params))         // full frame cache
+       if (HCC_ISOC_CACHE(hcc_params))         // full frame cache
                ehci->i_thresh = 8;
        else                                    // N microframes cached
                ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
 
        ehci->reclaim = NULL;
-       ehci->reclaim_ready = 0;
        ehci->next_uframe = -1;
 
        /*
@@ -483,9 +495,6 @@ static int ehci_init(struct usb_hcd *hcd)
        }
        ehci->command = temp;
 
-       ehci->reboot_notifier.notifier_call = ehci_reboot;
-       register_reboot_notifier(&ehci->reboot_notifier);
-
        return 0;
 }
 
@@ -499,7 +508,6 @@ static int ehci_run (struct usb_hcd *hcd)
 
        /* EHCI spec section 4.1 */
        if ((retval = ehci_reset(ehci)) != 0) {
-               unregister_reboot_notifier(&ehci->reboot_notifier);
                ehci_mem_cleanup(ehci);
                return retval;
        }
@@ -611,7 +619,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
        /* complete the unlinking of some qh [4.15.2.3] */
        if (status & STS_IAA) {
                COUNT (ehci->stats.reclaim);
-               ehci->reclaim_ready = 1;
+               end_unlink_async (ehci, regs);
                bh = 1;
        }
 
@@ -715,10 +723,14 @@ static int ehci_urb_enqueue (
 
 static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
-       /* if we need to use IAA and it's busy, defer */
-       if (qh->qh_state == QH_STATE_LINKED
-                       && ehci->reclaim
-                       && HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) {
+       // BUG_ON(qh->qh_state != QH_STATE_LINKED);
+
+       /* failfast */
+       if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
+               end_unlink_async (ehci, NULL);
+
+       /* defer till later if busy */
+       else if (ehci->reclaim) {
                struct ehci_qh          *last;
 
                for (last = ehci->reclaim;
@@ -728,12 +740,8 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
                qh->qh_state = QH_STATE_UNLINK_WAIT;
                last->reclaim = qh;
 
-       /* bypass IAA if the hc can't care */
-       } else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim)
-               end_unlink_async (ehci, NULL);
-
-       /* something else might have unlinked the qh by now */
-       if (qh->qh_state == QH_STATE_LINKED)
+       /* start IAA cycle */
+       } else
                start_unlink_async (ehci, qh);
 }
 
@@ -755,7 +763,19 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
                qh = (struct ehci_qh *) urb->hcpriv;
                if (!qh)
                        break;
-               unlink_async (ehci, qh);
+               switch (qh->qh_state) {
+               case QH_STATE_LINKED:
+               case QH_STATE_COMPLETING:
+                       unlink_async (ehci, qh);
+                       break;
+               case QH_STATE_UNLINK:
+               case QH_STATE_UNLINK_WAIT:
+                       /* already started */
+                       break;
+               case QH_STATE_IDLE:
+                       WARN_ON(1);
+                       break;
+               }
                break;
 
        case PIPE_INTERRUPT:
@@ -847,6 +867,7 @@ rescan:
                unlink_async (ehci, qh);
                /* FALL THROUGH */
        case QH_STATE_UNLINK:           /* wait for hw to finish? */
+       case QH_STATE_UNLINK_WAIT:
 idle_timeout:
                spin_unlock_irqrestore (&ehci->lock, flags);
                schedule_timeout_uninterruptible(1);
index d03e3ca..b2ee13c 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * 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
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -48,7 +48,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
        }
        ehci->command = readl (&ehci->regs->command);
        if (ehci->reclaim)
-               ehci->reclaim_ready = 1;
+               end_unlink_async (ehci, NULL);
        ehci_work(ehci, NULL);
 
        /* suspend any active/unsuspended ports, maybe allow wakeup */
@@ -103,10 +103,10 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
 
        /* re-init operational registers in case we lost power */
        if (readl (&ehci->regs->intr_enable) == 0) {
-               /* at least some APM implementations will try to deliver
+               /* at least some APM implementations will try to deliver
                 * IRQs right away, so delay them until we're ready.
-                */
-               intr_enable = 1;
+                */
+               intr_enable = 1;
                writel (0, &ehci->regs->segment);
                writel (ehci->periodic_dma, &ehci->regs->frame_list);
                writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
@@ -232,7 +232,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
                buf [1] = 0;
                retval++;
        }
-       
+
        /* no hub change reports (bit 0) for now (power, ...) */
 
        /* port N changes (bit N)? */
@@ -304,7 +304,7 @@ ehci_hub_descriptor (
 
 /*-------------------------------------------------------------------------*/
 
-#define        PORT_WAKE_BITS  (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
+#define        PORT_WAKE_BITS  (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
 
 static int ehci_hub_control (
        struct usb_hcd  *hcd,
index 766061e..a8ba2e1 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2001 by David Brownell
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -25,7 +25,7 @@
  *     - data used only by the HCD ... kmalloc is fine
  *     - async and periodic schedules, shared by HC and HCD ... these
  *       need to use dma_pool or dma_alloc_coherent
- *     - driver buffers, read/written by HC ... single shot DMA mapped 
+ *     - driver buffers, read/written by HC ... single shot DMA mapped
  *
  * There's also PCI "register" data, which is memory mapped.
  * No memory seen by this driver is pageable.
@@ -119,7 +119,7 @@ static inline void qh_put (struct ehci_qh *qh)
 
 /*-------------------------------------------------------------------------*/
 
-/* The queue heads and transfer descriptors are managed from pools tied 
+/* The queue heads and transfer descriptors are managed from pools tied
  * to each of the "per device" structures.
  * This is the initialisation and cleanup code.
  */
@@ -165,7 +165,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
        int i;
 
        /* QTDs for control/bulk/intr transfers */
-       ehci->qtd_pool = dma_pool_create ("ehci_qtd", 
+       ehci->qtd_pool = dma_pool_create ("ehci_qtd",
                        ehci_to_hcd(ehci)->self.controller,
                        sizeof (struct ehci_qtd),
                        32 /* byte alignment (for hw parts) */,
@@ -175,7 +175,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
        }
 
        /* QHs for control/bulk/intr transfers */
-       ehci->qh_pool = dma_pool_create ("ehci_qh", 
+       ehci->qh_pool = dma_pool_create ("ehci_qh",
                        ehci_to_hcd(ehci)->self.controller,
                        sizeof (struct ehci_qh),
                        32 /* byte alignment (for hw parts) */,
@@ -189,7 +189,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
        }
 
        /* ITD for high speed ISO transfers */
-       ehci->itd_pool = dma_pool_create ("ehci_itd", 
+       ehci->itd_pool = dma_pool_create ("ehci_itd",
                        ehci_to_hcd(ehci)->self.controller,
                        sizeof (struct ehci_itd),
                        32 /* byte alignment (for hw parts) */,
@@ -199,7 +199,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
        }
 
        /* SITD for full/low speed split ISO transfers */
-       ehci->sitd_pool = dma_pool_create ("ehci_sitd", 
+       ehci->sitd_pool = dma_pool_create ("ehci_sitd",
                        ehci_to_hcd(ehci)->self.controller,
                        sizeof (struct ehci_sitd),
                        32 /* byte alignment (for hw parts) */,
index 6967ab7..08d0472 100644 (file)
@@ -303,7 +303,7 @@ restart:
        /* emptying the schedule aborts any urbs */
        spin_lock_irq(&ehci->lock);
        if (ehci->reclaim)
-               ehci->reclaim_ready = 1;
+               end_unlink_async (ehci, NULL);
        ehci_work(ehci, NULL);
        spin_unlock_irq(&ehci->lock);
 
@@ -338,6 +338,7 @@ static const struct hc_driver ehci_pci_hc_driver = {
        .resume =               ehci_pci_resume,
 #endif
        .stop =                 ehci_stop,
+       .shutdown =             ehci_shutdown,
 
        /*
         * managing i/o requests and associated device resources
@@ -384,4 +385,5 @@ static struct pci_driver ehci_pci_driver = {
        .suspend =      usb_hcd_pci_suspend,
        .resume =       usb_hcd_pci_resume,
 #endif
+       .shutdown =     usb_hcd_pci_shutdown,
 };
index e469221..7fc25b6 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * 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
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -31,7 +31,7 @@
  * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with
  * interrupts) needs careful scheduling.  Performance improvements can be
  * an ongoing challenge.  That's in "ehci-sched.c".
- * 
+ *
  * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs,
  * or otherwise through transaction translators (TTs) in USB 2.0 hubs using
  * (b) special fields in qh entries or (c) split iso entries.  TTs will
@@ -199,7 +199,7 @@ static void qtd_copy_status (
                                && ((token & QTD_STS_MMF) != 0
                                        || QTD_CERR(token) == 0)
                                && (!ehci_is_TDI(ehci)
-                                       || urb->dev->tt->hub !=
+                                       || urb->dev->tt->hub !=
                                           ehci_to_hcd(ehci)->self.root_hub)) {
 #ifdef DEBUG
                        struct usb_device *tt = urb->dev->tt->hub;
@@ -364,7 +364,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs)
                         */
                        if (likely (urb->status == -EINPROGRESS))
                                continue;
-                       
+
                        /* issue status after short control reads */
                        if (unlikely (do_status != 0)
                                        && QTD_PID (token) == 0 /* OUT */) {
@@ -388,7 +388,7 @@ halt:
                                wmb ();
                        }
                }
+
                /* remove it from the queue */
                spin_lock (&urb->lock);
                qtd_copy_status (ehci, urb, qtd->length, token);
@@ -518,7 +518,7 @@ qh_urb_transaction (
                /* for zero length DATA stages, STATUS is always IN */
                if (len == 0)
                        token |= (1 /* "in" */ << 8);
-       } 
+       }
 
        /*
         * data transfer stage:  buffer setup
@@ -759,7 +759,7 @@ qh_make (
                }
                break;
        default:
-               dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed);
+               dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed);
 done:
                qh_put (qh);
                return NULL;
@@ -967,17 +967,16 @@ static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs)
        struct ehci_qh          *qh = ehci->reclaim;
        struct ehci_qh          *next;
 
-       timer_action_done (ehci, TIMER_IAA_WATCHDOG);
+       iaa_watchdog_done (ehci);
 
        // qh->hw_next = cpu_to_le32 (qh->qh_dma);
        qh->qh_state = QH_STATE_IDLE;
        qh->qh_next.qh = NULL;
-       qh_put (qh);                    // refcount from reclaim 
+       qh_put (qh);                    // refcount from reclaim
 
        /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
        next = qh->reclaim;
        ehci->reclaim = next;
-       ehci->reclaim_ready = 0;
        qh->reclaim = NULL;
 
        qh_completions (ehci, qh, regs);
@@ -1031,7 +1030,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
                        timer_action_done (ehci, TIMER_ASYNC_OFF);
                }
                return;
-       } 
+       }
 
        qh->qh_state = QH_STATE_UNLINK;
        ehci->reclaim = qh = qh_get (qh);
@@ -1046,17 +1045,16 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 
        if (unlikely (ehci_to_hcd(ehci)->state == HC_STATE_HALT)) {
                /* if (unlikely (qh->reclaim != 0))
-                *      this will recurse, probably not much
+                *      this will recurse, probably not much
                 */
                end_unlink_async (ehci, NULL);
                return;
        }
 
-       ehci->reclaim_ready = 0;
        cmd |= CMD_IAAD;
        writel (cmd, &ehci->regs->command);
        (void) readl (&ehci->regs->command);
-       timer_action (ehci, TIMER_IAA_WATCHDOG);
+       iaa_watchdog_start (ehci);
 }
 
 /*-------------------------------------------------------------------------*/
index 4859900..e5e9c65 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 2001-2004 by David Brownell
  * Copyright (c) 2003 Michal Sojka, for high-speed iso transfers
- * 
+ *
  * 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
@@ -613,7 +613,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
 /*-------------------------------------------------------------------------*/
 
 static int check_period (
-       struct ehci_hcd *ehci, 
+       struct ehci_hcd *ehci,
        unsigned        frame,
        unsigned        uframe,
        unsigned        period,
@@ -629,7 +629,7 @@ static int check_period (
 
        /*
         * 80% periodic == 100 usec/uframe available
-        * convert "usecs we need" to "max already claimed" 
+        * convert "usecs we need" to "max already claimed"
         */
        usecs = 100 - usecs;
 
@@ -659,14 +659,14 @@ static int check_period (
 }
 
 static int check_intr_schedule (
-       struct ehci_hcd         *ehci, 
+       struct ehci_hcd         *ehci,
        unsigned                frame,
        unsigned                uframe,
        const struct ehci_qh    *qh,
        __le32                  *c_maskp
 )
 {
-       int             retval = -ENOSPC;
+       int             retval = -ENOSPC;
        u8              mask = 0;
 
        if (qh->c_usecs && uframe >= 6)         /* FSTN territory? */
@@ -701,7 +701,7 @@ static int check_intr_schedule (
        /* Make sure this tt's buffer is also available for CSPLITs.
         * We pessimize a bit; probably the typical full speed case
         * doesn't need the second CSPLIT.
-        * 
+        *
         * NOTE:  both SPLIT and CSPLIT could be checked in just
         * one smart pass...
         */
@@ -728,7 +728,7 @@ done:
  */
 static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
-       int             status;
+       int             status;
        unsigned        uframe;
        __le32          c_mask;
        unsigned        frame;          /* 0..(qh->period - 1), or NO_FRAME */
@@ -784,7 +784,7 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
                ehci_dbg (ehci, "reused qh %p schedule\n", qh);
 
        /* stuff into the periodic schedule */
-       status = qh_link_periodic (ehci, qh);
+       status = qh_link_periodic (ehci, qh);
 done:
        return status;
 }
@@ -1681,7 +1681,7 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb,
                status = -ESHUTDOWN;
        else
                status = iso_stream_schedule (ehci, urb, stream);
-       if (likely (status == 0))
+       if (likely (status == 0))
                itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
        spin_unlock_irqrestore (&ehci->lock, flags);
 
@@ -1738,7 +1738,7 @@ sitd_sched_init (
                if (packet->buf1 != (buf & ~(u64)0x0fff))
                        packet->cross = 1;
 
-               /* OUT uses multiple start-splits */ 
+               /* OUT uses multiple start-splits */
                if (stream->bEndpointAddress & USB_DIR_IN)
                        continue;
                length = (length + 187) / 188;
@@ -1925,7 +1925,7 @@ sitd_link_urb (
 /*-------------------------------------------------------------------------*/
 
 #define        SITD_ERRS (SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE \
-                               | SITD_STS_XACT | SITD_STS_MMF)
+                               | SITD_STS_XACT | SITD_STS_MMF)
 
 static unsigned
 sitd_complete (
@@ -2043,7 +2043,7 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb,
                status = -ESHUTDOWN;
        else
                status = iso_stream_schedule (ehci, urb, stream);
-       if (status == 0)
+       if (status == 0)
                sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
        spin_unlock_irqrestore (&ehci->lock, flags);
 
@@ -2226,5 +2226,5 @@ restart:
                        now_uframe++;
                        now_uframe %= mod;
                }
-       } 
+       }
 }
index 679c1cd..6aac39f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2001-2002 by David Brownell
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -58,7 +58,6 @@ struct ehci_hcd {                     /* one per controller */
        /* async schedule support */
        struct ehci_qh          *async;
        struct ehci_qh          *reclaim;
-       unsigned                reclaim_ready : 1;
        unsigned                scanning : 1;
 
        /* periodic schedule support */
@@ -81,8 +80,8 @@ struct ehci_hcd {                     /* one per controller */
        struct dma_pool         *itd_pool;      /* itd per iso urb */
        struct dma_pool         *sitd_pool;     /* sitd per split iso urb */
 
+       struct timer_list       iaa_watchdog;
        struct timer_list       watchdog;
-       struct notifier_block   reboot_notifier;
        unsigned long           actions;
        unsigned                stamp;
        unsigned long           next_statechange;
@@ -104,7 +103,7 @@ struct ehci_hcd {                   /* one per controller */
 #endif
 };
 
-/* convert between an HCD pointer and the corresponding EHCI_HCD */ 
+/* convert between an HCD pointer and the corresponding EHCI_HCD */
 static inline struct ehci_hcd *hcd_to_ehci (struct usb_hcd *hcd)
 {
        return (struct ehci_hcd *) (hcd->hcd_priv);
@@ -115,9 +114,21 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
 }
 
 
+static inline void
+iaa_watchdog_start (struct ehci_hcd *ehci)
+{
+       WARN_ON(timer_pending(&ehci->iaa_watchdog));
+       mod_timer (&ehci->iaa_watchdog,
+                       jiffies + msecs_to_jiffies(EHCI_IAA_MSECS));
+}
+
+static inline void iaa_watchdog_done (struct ehci_hcd *ehci)
+{
+       del_timer (&ehci->iaa_watchdog);
+}
+
 enum ehci_timer_action {
        TIMER_IO_WATCHDOG,
-       TIMER_IAA_WATCHDOG,
        TIMER_ASYNC_SHRINK,
        TIMER_ASYNC_OFF,
 };
@@ -135,9 +146,6 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
                unsigned long t;
 
                switch (action) {
-               case TIMER_IAA_WATCHDOG:
-                       t = EHCI_IAA_JIFFIES;
-                       break;
                case TIMER_IO_WATCHDOG:
                        t = EHCI_IO_JIFFIES;
                        break;
@@ -154,8 +162,7 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
                // async queue SHRINK often precedes IAA.  while it's ready
                // to go OFF neither can matter, and afterwards the IO
                // watchdog stops unless there's still periodic traffic.
-               if (action != TIMER_IAA_WATCHDOG
-                               && t > ehci->watchdog.expires
+               if (time_before_eq(t, ehci->watchdog.expires)
                                && timer_pending (&ehci->watchdog))
                        return;
                mod_timer (&ehci->watchdog, t);
@@ -179,8 +186,8 @@ struct ehci_caps {
 #define HCS_INDICATOR(p)       ((p)&(1 << 16)) /* true: has port indicators */
 #define HCS_N_CC(p)            (((p)>>12)&0xf) /* bits 15:12, #companion HCs */
 #define HCS_N_PCC(p)           (((p)>>8)&0xf)  /* bits 11:8, ports per CC */
-#define HCS_PORTROUTED(p)      ((p)&(1 << 7))  /* true: port routing */ 
-#define HCS_PPC(p)             ((p)&(1 << 4))  /* true: port power control */ 
+#define HCS_PORTROUTED(p)      ((p)&(1 << 7))  /* true: port routing */
+#define HCS_PPC(p)             ((p)&(1 << 4))  /* true: port power control */
 #define HCS_N_PORTS(p)         (((p)>>0)&0xf)  /* bits 3:0, ports on HC */
 
        u32             hcc_params;      /* HCCPARAMS - offset 0x8 */
@@ -205,7 +212,7 @@ struct ehci_regs {
 #define CMD_LRESET     (1<<7)          /* partial reset (no ports, etc) */
 #define CMD_IAAD       (1<<6)          /* "doorbell" interrupt async advance */
 #define CMD_ASE                (1<<5)          /* async schedule enable */
-#define CMD_PSE        (1<<4)          /* periodic schedule enable */
+#define CMD_PSE                (1<<4)          /* periodic schedule enable */
 /* 3:2 is periodic frame list size */
 #define CMD_RESET      (1<<1)          /* reset HC not bus */
 #define CMD_RUN                (1<<0)          /* start/stop HC */
@@ -231,9 +238,9 @@ struct ehci_regs {
        /* FRINDEX: offset 0x0C */
        u32             frame_index;    /* current microframe number */
        /* CTRLDSSEGMENT: offset 0x10 */
-       u32             segment;        /* address bits 63:32 if needed */
+       u32             segment;        /* address bits 63:32 if needed */
        /* PERIODICLISTBASE: offset 0x14 */
-       u32             frame_list;     /* points to periodic list */
+       u32             frame_list;     /* points to periodic list */
        /* ASYNCLISTADDR: offset 0x18 */
        u32             async_next;     /* address of next async queue head */
 
@@ -302,7 +309,7 @@ struct ehci_dbg_port {
 
 /*
  * EHCI Specification 0.95 Section 3.5
- * QTD: describe data transfer components (buffer, direction, ...) 
+ * QTD: describe data transfer components (buffer, direction, ...)
  * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
  *
  * These are associated only with "QH" (Queue Head) structures,
@@ -312,7 +319,7 @@ struct ehci_qtd {
        /* first part defined by EHCI spec */
        __le32                  hw_next;          /* see EHCI 3.5.1 */
        __le32                  hw_alt_next;      /* see EHCI 3.5.2 */
-       __le32                  hw_token;         /* see EHCI 3.5.3 */       
+       __le32                  hw_token;         /* see EHCI 3.5.3 */
 #define        QTD_TOGGLE      (1 << 31)       /* data toggle */
 #define        QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
 #define        QTD_IOC         (1 << 15)       /* interrupt on complete */
@@ -349,8 +356,8 @@ struct ehci_qtd {
 /* values for that type tag */
 #define Q_TYPE_ITD     __constant_cpu_to_le32 (0 << 1)
 #define Q_TYPE_QH      __constant_cpu_to_le32 (1 << 1)
-#define Q_TYPE_SITD    __constant_cpu_to_le32 (2 << 1)
-#define Q_TYPE_FSTN    __constant_cpu_to_le32 (3 << 1)
+#define Q_TYPE_SITD    __constant_cpu_to_le32 (2 << 1)
+#define Q_TYPE_FSTN    __constant_cpu_to_le32 (3 << 1)
 
 /* next async queue entry, or pointer to interrupt/periodic QH */
 #define        QH_NEXT(dma)    (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH)
@@ -367,7 +374,7 @@ struct ehci_qtd {
  * For entries in the async schedule, the type tag always says "qh".
  */
 union ehci_shadow {
-       struct ehci_qh          *qh;            /* Q_TYPE_QH */
+       struct ehci_qh          *qh;            /* Q_TYPE_QH */
        struct ehci_itd         *itd;           /* Q_TYPE_ITD */
        struct ehci_sitd        *sitd;          /* Q_TYPE_SITD */
        struct ehci_fstn        *fstn;          /* Q_TYPE_FSTN */
@@ -397,7 +404,7 @@ struct ehci_qh {
 #define        QH_HUBPORT      0x3f800000
 #define        QH_MULT         0xc0000000
        __le32                  hw_current;      /* qtd list - see EHCI 3.6.4 */
-       
+
        /* qtd overlay (hardware parts of a struct ehci_qtd) */
        __le32                  hw_qtd_next;
        __le32                  hw_alt_next;
@@ -472,7 +479,7 @@ struct ehci_iso_stream {
        struct list_head        td_list;        /* queued itds/sitds */
        struct list_head        free_list;      /* list of unused itds/sitds */
        struct usb_device       *udev;
-       struct usb_host_endpoint *ep;
+       struct usb_host_endpoint *ep;
 
        /* output of (re)scheduling */
        unsigned long           start;          /* jiffies */
@@ -492,8 +499,8 @@ struct ehci_iso_stream {
        unsigned                bandwidth;
 
        /* This is used to initialize iTD's hw_bufp fields */
-       __le32                  buf0;           
-       __le32                  buf1;           
+       __le32                  buf0;
+       __le32                  buf1;
        __le32                  buf2;
 
        /* this is used to initialize sITD's tt info */
@@ -521,7 +528,7 @@ struct ehci_itd {
 
 #define ITD_ACTIVE     __constant_cpu_to_le32(EHCI_ISOC_ACTIVE)
 
-       __le32                  hw_bufp [7];    /* see EHCI 3.3.3 */ 
+       __le32                  hw_bufp [7];    /* see EHCI 3.3.3 */
        __le32                  hw_bufp_hi [7]; /* Appendix B */
 
        /* the rest is HCD-private */
@@ -542,7 +549,7 @@ struct ehci_itd {
 /*-------------------------------------------------------------------------*/
 
 /*
- * EHCI Specification 0.95 Section 3.4 
+ * EHCI Specification 0.95 Section 3.4
  * siTD, aka split-transaction isochronous Transfer Descriptor
  *       ... describe full speed iso xfers through TT in hubs
  * see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD)
index 8c6b38a..a72e041 100644 (file)
@@ -1207,7 +1207,7 @@ static int isp116x_open_seq(struct inode *inode, struct file *file)
        return single_open(file, isp116x_show_dbg, inode->i_private);
 }
 
-static struct file_operations isp116x_debug_fops = {
+static const struct file_operations isp116x_debug_fops = {
        .open = isp116x_open_seq,
        .read = seq_read,
        .llseek = seq_lseek,
index a1b7c38..b91e2ed 100644 (file)
@@ -233,7 +233,7 @@ static const int cc_to_error[16] = {
        /* Bit Stuff  */ -EPROTO,
        /* Data Togg  */ -EILSEQ,
        /* Stall      */ -EPIPE,
-       /* DevNotResp */ -ETIMEDOUT,
+       /* DevNotResp */ -ETIME,
        /* PIDCheck   */ -EPROTO,
        /* UnExpPID   */ -EPROTO,
        /* DataOver   */ -EOVERFLOW,
index 85cc059..b466581 100644 (file)
@@ -193,7 +193,7 @@ ohci_at91_start (struct usb_hcd *hcd)
        if ((ret = ohci_init(ohci)) < 0)
                return ret;
 
-       root->maxchild = board->ports;
+       ohci->num_ports = board->ports;
 
        if ((ret = ohci_run(ohci)) < 0) {
                err("can't start %s", hcd->self.bus_name);
@@ -221,6 +221,7 @@ static const struct hc_driver ohci_at91_hc_driver = {
         */
        .start =                ohci_at91_start,
        .stop =                 ohci_stop,
+       .shutdown =             ohci_shutdown,
 
        /*
         * managing i/o requests and associated device resources
@@ -239,7 +240,7 @@ static const struct hc_driver ohci_at91_hc_driver = {
         */
        .hub_status_data =      ohci_hub_status_data,
        .hub_control =          ohci_hub_control,
-
+       .hub_irq_enable =       ohci_rhsc_enable,
 #ifdef CONFIG_PM
        .bus_suspend =          ohci_bus_suspend,
        .bus_resume =           ohci_bus_resume,
@@ -296,6 +297,7 @@ static int ohci_hcd_at91_drv_resume(struct platform_device *pdev)
        if (!clocked) {
                clk_enable(iclk);
                clk_enable(fclk);
+               clocked = 1;
        }
 
        return 0;
@@ -310,6 +312,7 @@ MODULE_ALIAS("at91_ohci");
 static struct platform_driver ohci_hcd_at91_driver = {
        .probe          = ohci_hcd_at91_drv_probe,
        .remove         = ohci_hcd_at91_drv_remove,
+       .shutdown       = usb_hcd_platform_shutdown,
        .suspend        = ohci_hcd_at91_drv_suspend,
        .resume         = ohci_hcd_at91_drv_resume,
        .driver         = {
index f7a975d..24e23c5 100644 (file)
@@ -268,11 +268,8 @@ static const struct hc_driver ohci_au1xxx_hc_driver = {
         * basic lifecycle operations
         */
        .start =                ohci_au1xxx_start,
-#ifdef CONFIG_PM
-       /* suspend:             ohci_au1xxx_suspend,  -- tbd */
-       /* resume:              ohci_au1xxx_resume,   -- tbd */
-#endif /*CONFIG_PM*/
        .stop =                 ohci_stop,
+       .shutdown =             ohci_shutdown,
 
        /*
         * managing i/o requests and associated device resources
@@ -291,6 +288,7 @@ static const struct hc_driver ohci_au1xxx_hc_driver = {
         */
        .hub_status_data =      ohci_hub_status_data,
        .hub_control =          ohci_hub_control,
+       .hub_irq_enable =       ohci_rhsc_enable,
 #ifdef CONFIG_PM
        .bus_suspend =          ohci_bus_suspend,
        .bus_resume =           ohci_bus_resume,
@@ -338,6 +336,7 @@ static int ohci_hcd_au1xxx_drv_resume(struct platform_device *dev)
 static struct platform_driver ohci_hcd_au1xxx_driver = {
        .probe          = ohci_hcd_au1xxx_drv_probe,
        .remove         = ohci_hcd_au1xxx_drv_remove,
+       .shutdown       = usb_hcd_platform_shutdown,
        /*.suspend      = ohci_hcd_au1xxx_drv_suspend, */
        /*.resume       = ohci_hcd_au1xxx_drv_resume, */
        .driver         = {
index 7bfffcb..8293c1d 100644 (file)
@@ -477,7 +477,7 @@ show_async (struct class_device *class_dev, char *buf)
        unsigned long           flags;
 
        bus = class_get_devdata(class_dev);
-       hcd = bus->hcpriv;
+       hcd = bus_to_hcd(bus);
        ohci = hcd_to_ohci(hcd);
 
        /* display control and bulk lists together, for simplicity */
@@ -510,7 +510,7 @@ show_periodic (struct class_device *class_dev, char *buf)
        seen_count = 0;
 
        bus = class_get_devdata(class_dev);
-       hcd = bus->hcpriv;
+       hcd = bus_to_hcd(bus);
        ohci = hcd_to_ohci(hcd);
        next = buf;
        size = PAGE_SIZE;
@@ -607,7 +607,7 @@ show_registers (struct class_device *class_dev, char *buf)
        u32                     rdata;
 
        bus = class_get_devdata(class_dev);
-       hcd = bus->hcpriv;
+       hcd = bus_to_hcd(bus);
        ohci = hcd_to_ohci(hcd);
        regs = ohci->regs;
        next = buf;
@@ -667,6 +667,11 @@ show_registers (struct class_device *class_dev, char *buf)
        size -= temp;
        next += temp;
 
+       temp = scnprintf (next, size, "hub poll timer %s\n",
+                       ohci_to_hcd(ohci)->poll_rh ? "ON" : "off");
+       size -= temp;
+       next += temp;
+
        /* roothub */
        ohci_dump_roothub (ohci, 1, &next, &size);
 
@@ -680,10 +685,11 @@ static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
 static inline void create_debug_files (struct ohci_hcd *ohci)
 {
        struct class_device *cldev = ohci_to_hcd(ohci)->self.class_dev;
+       int retval;
 
-       class_device_create_file(cldev, &class_device_attr_async);
-       class_device_create_file(cldev, &class_device_attr_periodic);
-       class_device_create_file(cldev, &class_device_attr_registers);
+       retval = class_device_create_file(cldev, &class_device_attr_async);
+       retval = class_device_create_file(cldev, &class_device_attr_periodic);
+       retval = class_device_create_file(cldev, &class_device_attr_registers);
        ohci_dbg (ohci, "created debug files\n");
 }
 
index 6531c4d..1bf5e7a 100644 (file)
@@ -128,12 +128,14 @@ static struct hc_driver ohci_ep93xx_hc_driver = {
        .flags                  = HCD_USB11 | HCD_MEMORY,
        .start                  = ohci_ep93xx_start,
        .stop                   = ohci_stop,
+       .shutdown               = ohci_shutdown,
        .urb_enqueue            = ohci_urb_enqueue,
        .urb_dequeue            = ohci_urb_dequeue,
        .endpoint_disable       = ohci_endpoint_disable,
        .get_frame_number       = ohci_get_frame,
        .hub_status_data        = ohci_hub_status_data,
        .hub_control            = ohci_hub_control,
+       .hub_irq_enable         = ohci_rhsc_enable,
 #ifdef CONFIG_PM
        .bus_suspend            = ohci_bus_suspend,
        .bus_resume             = ohci_bus_resume,
@@ -202,6 +204,7 @@ static int ohci_hcd_ep93xx_drv_resume(struct platform_device *pdev)
 static struct platform_driver ohci_hcd_ep93xx_driver = {
        .probe          = ohci_hcd_ep93xx_drv_probe,
        .remove         = ohci_hcd_ep93xx_drv_remove,
+       .shutdown       = usb_hcd_platform_shutdown,
 #ifdef CONFIG_PM
        .suspend        = ohci_hcd_ep93xx_drv_suspend,
        .resume         = ohci_hcd_ep93xx_drv_resume,
index 94d8cf4..1027aa0 100644 (file)
@@ -88,7 +88,7 @@
 #include <linux/timer.h>
 #include <linux/list.h>
 #include <linux/usb.h>
-#include <linux/usb_otg.h>
+#include <linux/usb/otg.h>
 #include <linux/dma-mapping.h> 
 #include <linux/dmapool.h>
 #include <linux/reboot.h>
 
 #include "../core/hcd.h"
 
-#define DRIVER_VERSION "2005 April 22"
+#define DRIVER_VERSION "2006 August 04"
 #define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
 #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
 
 #undef OHCI_VERBOSE_DEBUG      /* not always helpful */
 
 /* For initializing controller (mask in an HCFS mode too) */
-#define        OHCI_CONTROL_INIT       OHCI_CTRL_CBSR
+#define        OHCI_CONTROL_INIT       OHCI_CTRL_CBSR
 #define        OHCI_INTR_INIT \
-       (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH)
+               (OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_UE \
+               | OHCI_INTR_RD | OHCI_INTR_WDH)
 
 #ifdef __hppa__
 /* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
 
 static const char      hcd_name [] = "ohci_hcd";
 
+#define        STATECHANGE_DELAY       msecs_to_jiffies(300)
+
 #include "ohci.h"
 
 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"
@@ -416,21 +418,20 @@ 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
+/* ohci_shutdown 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)
+static void
+ohci_shutdown (struct usb_hcd *hcd)
 {
        struct ohci_hcd *ohci;
 
-       ohci = container_of (block, struct ohci_hcd, reboot_notifier);
+       ohci = hcd_to_ohci (hcd);
        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;
 }
 
 /*-------------------------------------------------------------------------*
@@ -446,7 +447,6 @@ static int ohci_init (struct ohci_hcd *ohci)
 
        disable (ohci);
        ohci->regs = hcd->regs;
-       ohci->next_statechange = jiffies;
 
        /* REVISIT this BIOS handshake is now moved into PCI "quirks", and
         * was never needed for most non-PCI systems ... remove the code?
@@ -502,7 +502,6 @@ static int ohci_init (struct ohci_hcd *ohci)
        if ((ret = ohci_mem_init (ohci)) < 0)
                ohci_stop (hcd);
        else {
-               register_reboot_notifier (&ohci->reboot_notifier);
                create_debug_files (ohci);
        }
 
@@ -637,10 +636,14 @@ retry:
                return -EOVERFLOW;
        }
 
-       /* start controller operations */
+       /* use rhsc irqs after khubd is fully initialized */
+       hcd->poll_rh = 1;
+       hcd->uses_new_polling = 1;
+
+       /* start controller operations */
        ohci->hc_control &= OHCI_CTRL_RWC;
-       ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
-       ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+       ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
+       ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
        hcd->state = HC_STATE_RUNNING;
 
        /* wake on ConnectStatusChange, matching external hubs */
@@ -648,7 +651,7 @@ retry:
 
        /* Choose the interrupts we care about now, others later on demand */
        mask = OHCI_INTR_INIT;
-       ohci_writel (ohci, mask, &ohci->regs->intrstatus);
+       ohci_writel (ohci, ~0, &ohci->regs->intrstatus);
        ohci_writel (ohci, mask, &ohci->regs->intrenable);
 
        /* handle root hub init quirks ... */
@@ -672,6 +675,7 @@ retry:
        // flush those writes
        (void) ohci_readl (ohci, &ohci->regs->control);
 
+       ohci->next_statechange = jiffies + STATECHANGE_DELAY;
        spin_unlock_irq (&ohci->lock);
 
        // POTPGT delay is bits 24-31, in 2 ms units.
@@ -709,7 +713,23 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
        /* interrupt for some other device? */
        } else if ((ints &= ohci_readl (ohci, &regs->intrenable)) == 0) {
                return IRQ_NOTMINE;
-       } 
+       }
+
+       /* NOTE:  vendors didn't always make the same implementation
+        * choices for RHSC.  Sometimes it triggers on an edge (like
+        * setting and maybe clearing a port status change bit); and
+        * it's level-triggered on other silicon, active until khubd
+        * clears all active port status change bits.  Poll by timer
+        * til it's fully debounced and the difference won't matter.
+        */
+       if (ints & OHCI_INTR_RHSC) {
+               ohci_vdbg (ohci, "rhsc\n");
+               ohci_writel (ohci, OHCI_INTR_RHSC, &regs->intrdisable);
+               hcd->poll_rh = 1;
+               ohci->next_statechange = jiffies + STATECHANGE_DELAY;
+               ohci_writel (ohci, OHCI_INTR_RHSC, &regs->intrstatus);
+               usb_hcd_poll_rh_status(hcd);
+       }
 
        if (ints & OHCI_INTR_UE) {
                disable (ohci);
@@ -775,9 +795,10 @@ static void ohci_stop (struct usb_hcd *hcd)
 
        ohci_usb_reset (ohci);
        ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
-       
+       free_irq(hcd->irq, hcd);
+       hcd->irq = -1;
+
        remove_debug_files (ohci);
-       unregister_reboot_notifier (&ohci->reboot_notifier);
        ohci_mem_cleanup (ohci);
        if (ohci->hcca) {
                dma_free_coherent (hcd->self.controller, 
@@ -917,6 +938,10 @@ MODULE_LICENSE ("GPL");
 #include "ohci-at91.c"
 #endif
 
+#ifdef CONFIG_ARCH_PNX4008
+#include "ohci-pnx4008.c"
+#endif
+
 #if !(defined(CONFIG_PCI) \
       || defined(CONFIG_SA1111) \
       || defined(CONFIG_ARCH_S3C2410) \
@@ -928,6 +953,7 @@ MODULE_LICENSE ("GPL");
       || defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \
       || defined (CONFIG_ARCH_AT91RM9200) \
       || defined (CONFIG_ARCH_AT91SAM9261) \
+      || defined (CONFIG_ARCH_PNX4008) \
        )
 #error "missing bus glue for ohci-hcd"
 #endif
index 5b0a23f..0b89933 100644 (file)
 
 /*-------------------------------------------------------------------------*/
 
+/* hcd->hub_irq_enable() */
+static void ohci_rhsc_enable (struct usb_hcd *hcd)
+{
+       struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
+
+       ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
+}
+
 #ifdef CONFIG_PM
 
 #define OHCI_SCHED_ENABLES \
@@ -123,10 +131,10 @@ static int ohci_bus_suspend (struct usb_hcd *hcd)
        /* no resumes until devices finish suspending */
        ohci->next_statechange = jiffies + msecs_to_jiffies (5);
 
+       /* no timer polling */
+       hcd->poll_rh = 0;
+
 done:
-       /* external suspend vs self autosuspend ... same effect */
-       if (status == 0)
-               usb_hcd_suspend_root_hub(hcd);
        spin_unlock_irqrestore (&ohci->lock, flags);
        return status;
 }
@@ -256,8 +264,8 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
        /* TRSMRCY */
        msleep (10);
 
-       /* keep it alive for ~5x suspend + resume costs */
-       ohci->next_statechange = jiffies + msecs_to_jiffies (250);
+       /* keep it alive for more than ~5x suspend + resume costs */
+       ohci->next_statechange = jiffies + STATECHANGE_DELAY;
 
        /* maybe turn schedules back on */
        enables = 0;
@@ -302,9 +310,10 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
 {
        struct ohci_hcd *ohci = hcd_to_ohci (hcd);
        int             i, changed = 0, length = 1;
-       int             can_suspend = device_may_wakeup(&hcd->self.root_hub->dev);
+       int             can_suspend;
        unsigned long   flags;
 
+       can_suspend = device_may_wakeup(&hcd->self.root_hub->dev);
        spin_lock_irqsave (&ohci->lock, flags);
 
        /* handle autosuspended root:  finish resuming before
@@ -339,6 +348,10 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
        for (i = 0; i < ohci->num_ports; i++) {
                u32     status = roothub_portstatus (ohci, i);
 
+               /* can't autosuspend with active ports */
+               if ((status & RH_PS_PES) && !(status & RH_PS_PSS))
+                       can_suspend = 0;
+
                if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
                                | RH_PS_OCIC | RH_PS_PRSC)) {
                        changed = 1;
@@ -348,32 +361,41 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
                            buf [1] |= 1 << (i - 7);
                        continue;
                }
+       }
 
-               /* can suspend if no ports are enabled; or if all all
-                * enabled ports are suspended AND remote wakeup is on.
-                */
-               if (!(status & RH_PS_CCS))
-                       continue;
-               if ((status & RH_PS_PSS) && can_suspend)
-                       continue;
+       /* after root hub changes, stop polling after debouncing
+        * for a while and maybe kicking in autosuspend
+        */
+       if (changed) {
+               ohci->next_statechange = jiffies + STATECHANGE_DELAY;
                can_suspend = 0;
+       } else if (time_before (jiffies, ohci->next_statechange)) {
+               can_suspend = 0;
+       } else {
+#ifdef CONFIG_PM
+               can_suspend = can_suspend
+                       && !ohci->ed_rm_list
+                       && ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
+                                       & ohci->hc_control)
+                               == OHCI_USB_OPER;
+#endif
+               if (hcd->uses_new_polling) {
+                       hcd->poll_rh = 0;
+                       /* use INTR_RHSC iff INTR_RD won't apply */
+                       if (!can_suspend)
+                               ohci_writel (ohci, OHCI_INTR_RHSC,
+                                               &ohci->regs->intrenable);
+               }
        }
+
 done:
        spin_unlock_irqrestore (&ohci->lock, flags);
 
-#ifdef CONFIG_PM
-       /* save power by suspending idle root hubs;
+#ifdef CONFIG_PM
+       /* save power by autosuspending idle root hubs;
         * INTR_RD wakes us when there's work
         */
-       if (can_suspend
-                       && !changed
-                       && !ohci->ed_rm_list
-                       && ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
-                                       & ohci->hc_control)
-                               == OHCI_USB_OPER
-                       && time_after (jiffies, ohci->next_statechange)
-                       && usb_trylock_device (hcd->self.root_hub) == 0
-                       ) {
+       if (can_suspend && usb_trylock_device (hcd->self.root_hub) == 0) {
                ohci_vdbg (ohci, "autosuspend\n");
                (void) ohci_bus_suspend (hcd);
                usb_unlock_device (hcd->self.root_hub);
index 5602da9..e121d97 100644 (file)
@@ -173,11 +173,8 @@ static const struct hc_driver ohci_lh7a404_hc_driver = {
         * basic lifecycle operations
         */
        .start =                ohci_lh7a404_start,
-#ifdef CONFIG_PM
-       /* suspend:             ohci_lh7a404_suspend,  -- tbd */
-       /* resume:              ohci_lh7a404_resume,   -- tbd */
-#endif /*CONFIG_PM*/
        .stop =                 ohci_stop,
+       .shutdown =             ohci_shutdown,
 
        /*
         * managing i/o requests and associated device resources
@@ -196,6 +193,7 @@ static const struct hc_driver ohci_lh7a404_hc_driver = {
         */
        .hub_status_data =      ohci_hub_status_data,
        .hub_control =          ohci_hub_control,
+       .hub_irq_enable =       ohci_rhsc_enable,
 #ifdef CONFIG_PM
        .bus_suspend =          ohci_bus_suspend,
        .bus_resume =           ohci_bus_resume,
@@ -244,6 +242,7 @@ static int ohci_hcd_lh7a404_drv_resume(struct platform_device *dev)
 static struct platform_driver ohci_hcd_lh7a404_driver = {
        .probe          = ohci_hcd_lh7a404_drv_probe,
        .remove         = ohci_hcd_lh7a404_drv_remove,
+       .shutdown       = usb_hcd_platform_shutdown,
        /*.suspend      = ohci_hcd_lh7a404_drv_suspend, */
        /*.resume       = ohci_hcd_lh7a404_drv_resume, */
        .driver         = {
index bfbe328..d976614 100644 (file)
@@ -28,7 +28,6 @@ static void ohci_hcd_init (struct ohci_hcd *ohci)
        ohci->next_statechange = jiffies;
        spin_lock_init (&ohci->lock);
        INIT_LIST_HEAD (&ohci->pending);
-       ohci->reboot_notifier.notifier_call = ohci_reboot;
 }
 
 /*-------------------------------------------------------------------------*/
index c4c4bab..9c02177 100644 (file)
@@ -4,7 +4,7 @@
  * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
  * (C) Copyright 2000-2005 David Brownell
  * (C) Copyright 2002 Hewlett-Packard Company
- * 
+ *
  * OMAP Bus Glue
  *
  * Modified for OMAP by Tony Lindgren <tony@atomide.com>
@@ -66,15 +66,20 @@ extern int usb_disabled(void);
 extern int ocpi_enable(void);
 
 static struct clk *usb_host_ck;
+static struct clk *usb_dc_ck;
+static int host_enabled;
+static int host_initialized;
 
 static void omap_ohci_clock_power(int on)
 {
        if (on) {
+               clk_enable(usb_dc_ck);
                clk_enable(usb_host_ck);
                /* guesstimate for T5 == 1x 32K clock + APLL lock time */
                udelay(100);
        } else {
                clk_disable(usb_host_ck);
+               clk_disable(usb_dc_ck);
        }
 }
 
@@ -87,14 +92,14 @@ static int omap_ohci_transceiver_power(int on)
        if (on) {
                if (machine_is_omap_innovator() && cpu_is_omap1510())
                        fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL)
-                               | ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), 
+                               | ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
                               INNOVATOR_FPGA_CAM_USB_CONTROL);
                else if (machine_is_omap_osk())
                        tps65010_set_gpio_out_value(GPIO1, LOW);
        } else {
                if (machine_is_omap_innovator() && cpu_is_omap1510())
                        fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL)
-                               & ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), 
+                               & ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
                               INNOVATOR_FPGA_CAM_USB_CONTROL);
                else if (machine_is_omap_osk())
                        tps65010_set_gpio_out_value(GPIO1, HIGH);
@@ -103,6 +108,7 @@ static int omap_ohci_transceiver_power(int on)
        return 0;
 }
 
+#ifdef CONFIG_ARCH_OMAP15XX
 /*
  * OMAP-1510 specific Local Bus clock on/off
  */
@@ -121,8 +127,8 @@ static int omap_1510_local_bus_power(int on)
 /*
  * OMAP-1510 specific Local Bus initialization
  * NOTE: This assumes 32MB memory size in OMAP1510LB_MEMSIZE.
- *       See also arch/mach-omap/memory.h for __virt_to_dma() and 
- *       __dma_to_virt() which need to match with the physical 
+ *       See also arch/mach-omap/memory.h for __virt_to_dma() and
+ *       __dma_to_virt() which need to match with the physical
  *       Local Bus address below.
  */
 static int omap_1510_local_bus_init(void)
@@ -130,7 +136,7 @@ static int omap_1510_local_bus_init(void)
        unsigned int tlb;
        unsigned long lbaddr, physaddr;
 
-       omap_writel((omap_readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4, 
+       omap_writel((omap_readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4,
               OMAP1510_LB_CLOCK_DIV);
 
        /* Configure the Local Bus MMU table */
@@ -138,7 +144,7 @@ static int omap_1510_local_bus_init(void)
                lbaddr = tlb * 0x00100000 + OMAP1510_LB_OFFSET;
                physaddr = tlb * 0x00100000 + PHYS_OFFSET;
                omap_writel((lbaddr & 0x0fffffff) >> 22, OMAP1510_LB_MMU_CAM_H);
-               omap_writel(((lbaddr & 0x003ffc00) >> 6) | 0xc, 
+               omap_writel(((lbaddr & 0x003ffc00) >> 6) | 0xc,
                       OMAP1510_LB_MMU_CAM_L);
                omap_writel(physaddr >> 16, OMAP1510_LB_MMU_RAM_H);
                omap_writel((physaddr & 0x0000fc00) | 0x300, OMAP1510_LB_MMU_RAM_L);
@@ -152,6 +158,10 @@ static int omap_1510_local_bus_init(void)
 
        return 0;
 }
+#else
+#define omap_1510_local_bus_power(x)   {}
+#define omap_1510_local_bus_init()     {}
+#endif
 
 #ifdef CONFIG_USB_OTG
 
@@ -173,13 +183,14 @@ static void start_hnp(struct ohci_hcd *ohci)
 
 /*-------------------------------------------------------------------------*/
 
-static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
+static int ohci_omap_init(struct usb_hcd *hcd)
 {
-       struct omap_usb_config  *config = pdev->dev.platform_data;
+       struct ohci_hcd         *ohci = hcd_to_ohci(hcd);
+       struct omap_usb_config  *config = hcd->self.controller->platform_data;
        int                     need_transceiver = (config->otg != 0);
        int                     ret;
 
-       dev_dbg(&pdev->dev, "starting USB Controller\n");
+       dev_dbg(hcd->self.controller, "starting USB Controller\n");
 
        if (config->otg) {
                ohci_to_hcd(ohci)->self.otg_port = config->otg;
@@ -200,7 +211,7 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
                if (ohci->transceiver) {
                        int     status = otg_set_host(ohci->transceiver,
                                                &ohci_to_hcd(ohci)->self);
-                       dev_dbg(&pdev->dev, "init %s transceiver, status %d\n",
+                       dev_dbg(hcd->self.controller, "init %s transceiver, status %d\n",
                                        ohci->transceiver->label, status);
                        if (status) {
                                if (ohci->transceiver)
@@ -208,7 +219,7 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
                                return status;
                        }
                } else {
-                       dev_err(&pdev->dev, "can't find transceiver\n");
+                       dev_err(hcd->self.controller, "can't find transceiver\n");
                        return -ENODEV;
                }
        }
@@ -247,6 +258,10 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
                }
                ohci_writel(ohci, rh, &ohci->regs->roothub.a);
                distrust_firmware = 0;
+       } else if (machine_is_nokia770()) {
+               /* We require a self-powered hub, which should have
+                * plenty of power. */
+               ohci_to_hcd(ohci)->power_budget = 0;
        }
 
        /* FIXME khubd hub requests should manage power switching */
@@ -260,21 +275,15 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
        return 0;
 }
 
-static void omap_stop_hc(struct platform_device *pdev)
+static void ohci_omap_stop(struct usb_hcd *hcd)
 {
-       dev_dbg(&pdev->dev, "stopping USB Controller\n");
+       dev_dbg(hcd->self.controller, "stopping USB Controller\n");
        omap_ohci_clock_power(0);
 }
 
 
 /*-------------------------------------------------------------------------*/
 
-void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *);
-
-/* configure so an HC device and id are always provided */
-/* always called with process context; sleeping is OK */
-
-
 /**
  * usb_hcd_omap_probe - initialize OMAP-based HCDs
  * Context: !in_interrupt()
@@ -283,7 +292,7 @@ void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *);
  * then invokes the start() method for the HCD associated with it
  * through the hotplug entry's driver_data.
  */
-int usb_hcd_omap_probe (const struct hc_driver *driver,
+static int usb_hcd_omap_probe (const struct hc_driver *driver,
                          struct platform_device *pdev)
 {
        int retval, irq;
@@ -291,12 +300,12 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
        struct ohci_hcd *ohci;
 
        if (pdev->num_resources != 2) {
-               printk(KERN_ERR "hcd probe: invalid num_resources: %i\n", 
+               printk(KERN_ERR "hcd probe: invalid num_resources: %i\n",
                       pdev->num_resources);
                return -ENODEV;
        }
 
-       if (pdev->resource[0].flags != IORESOURCE_MEM 
+       if (pdev->resource[0].flags != IORESOURCE_MEM
                        || pdev->resource[1].flags != IORESOURCE_IRQ) {
                printk(KERN_ERR "hcd probe: invalid resource type\n");
                return -ENODEV;
@@ -306,6 +315,17 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
        if (IS_ERR(usb_host_ck))
                return PTR_ERR(usb_host_ck);
 
+       if (!cpu_is_omap1510())
+               usb_dc_ck = clk_get(0, "usb_dc_ck");
+       else
+               usb_dc_ck = clk_get(0, "lb_ck");
+
+       if (IS_ERR(usb_dc_ck)) {
+               clk_put(usb_host_ck);
+               return PTR_ERR(usb_dc_ck);
+       }
+
+
        hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id);
        if (!hcd) {
                retval = -ENOMEM;
@@ -325,9 +345,8 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
        ohci = hcd_to_ohci(hcd);
        ohci_hcd_init(ohci);
 
-       retval = omap_start_hc(ohci, pdev);
-       if (retval < 0)
-               goto err2;
+       host_initialized = 0;
+       host_enabled = 1;
 
        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
@@ -335,15 +354,21 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
                goto err2;
        }
        retval = usb_add_hcd(hcd, irq, IRQF_DISABLED);
-       if (retval == 0)
-               return retval;
+       if (retval)
+               goto err2;
+
+       host_initialized = 1;
+
+       if (!host_enabled)
+               omap_ohci_clock_power(0);
 
-       omap_stop_hc(pdev);
+       return 0;
 err2:
        release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
 err1:
        usb_put_hcd(hcd);
 err0:
+       clk_put(usb_dc_ck);
        clk_put(usb_host_ck);
        return retval;
 }
@@ -359,31 +384,41 @@ err0:
  * Reverses the effect of usb_hcd_omap_probe(), first invoking
  * the HCD's stop() method.  It is always called from a thread
  * context, normally "rmmod", "apmd", or something similar.
- *
  */
-void usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
+static inline void
+usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
 {
+       struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
+
        usb_remove_hcd(hcd);
+       if (ohci->transceiver) {
+               (void) otg_set_host(ohci->transceiver, 0);
+               put_device(ohci->transceiver->dev);
+       }
        if (machine_is_omap_osk())
                omap_free_gpio(9);
-       omap_stop_hc(pdev);
        release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
        usb_put_hcd(hcd);
+       clk_put(usb_dc_ck);
        clk_put(usb_host_ck);
 }
 
 /*-------------------------------------------------------------------------*/
 
-static int __devinit
+static int
 ohci_omap_start (struct usb_hcd *hcd)
 {
        struct omap_usb_config *config;
        struct ohci_hcd *ohci = hcd_to_ohci (hcd);
        int             ret;
 
+       if (!host_enabled)
+               return 0;
        config = hcd->self.controller->platform_data;
-       if (config->otg || config->rwc)
+       if (config->otg || config->rwc) {
+               ohci->hc_control = OHCI_CTRL_RWC;
                writel(OHCI_CTRL_RWC, &ohci->regs->control);
+       }
 
        if ((ret = ohci_run (ohci)) < 0) {
                dev_err(hcd->self.controller, "can't start\n");
@@ -409,8 +444,10 @@ static const struct hc_driver ohci_omap_hc_driver = {
        /*
         * basic lifecycle operations
         */
+       .reset =                ohci_omap_init,
        .start =                ohci_omap_start,
-       .stop =                 ohci_stop,
+       .stop =                 ohci_omap_stop,
+       .shutdown =             ohci_shutdown,
 
        /*
         * managing i/o requests and associated device resources
@@ -429,6 +466,7 @@ static const struct hc_driver ohci_omap_hc_driver = {
         */
        .hub_status_data =      ohci_hub_status_data,
        .hub_control =          ohci_hub_control,
+       .hub_irq_enable =       ohci_rhsc_enable,
 #ifdef CONFIG_PM
        .bus_suspend =          ohci_bus_suspend,
        .bus_resume =           ohci_bus_resume,
@@ -446,13 +484,8 @@ static int ohci_hcd_omap_drv_probe(struct platform_device *dev)
 static int ohci_hcd_omap_drv_remove(struct platform_device *dev)
 {
        struct usb_hcd          *hcd = platform_get_drvdata(dev);
-       struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
 
        usb_hcd_omap_remove(hcd, dev);
-       if (ohci->transceiver) {
-               (void) otg_set_host(ohci->transceiver, 0);
-               put_device(ohci->transceiver->dev);
-       }
        platform_set_drvdata(dev, NULL);
 
        return 0;
@@ -472,7 +505,7 @@ static int ohci_omap_suspend(struct platform_device *dev, pm_message_t message)
 
        omap_ohci_clock_power(0);
        ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
-       dev->power.power_state = PMSG_SUSPEND;
+       dev->dev.power.power_state = PMSG_SUSPEND;
        return 0;
 }
 
@@ -485,8 +518,8 @@ static int ohci_omap_resume(struct platform_device *dev)
        ohci->next_statechange = jiffies;
 
        omap_ohci_clock_power(1);
-       dev->power.power_state = PMSG_ON;
-       usb_hcd_resume_root_hub(dev_get_drvdata(dev));
+       dev->dev.power.power_state = PMSG_ON;
+       usb_hcd_resume_root_hub(platform_get_drvdata(dev));
        return 0;
 }
 
@@ -500,6 +533,7 @@ static int ohci_omap_resume(struct platform_device *dev)
 static struct platform_driver ohci_hcd_omap_driver = {
        .probe          = ohci_hcd_omap_drv_probe,
        .remove         = ohci_hcd_omap_drv_remove,
+       .shutdown       = usb_hcd_platform_shutdown,
 #ifdef CONFIG_PM
        .suspend        = ohci_omap_suspend,
        .resume         = ohci_omap_resume,
index 37e1228..3732db7 100644 (file)
@@ -176,11 +176,14 @@ static const struct hc_driver ohci_pci_hc_driver = {
         */
        .reset =                ohci_pci_reset,
        .start =                ohci_pci_start,
+       .stop =                 ohci_stop,
+       .shutdown =             ohci_shutdown,
+
 #ifdef CONFIG_PM
+       /* these suspend/resume entries are for upstream PCI glue ONLY */
        .suspend =              ohci_pci_suspend,
        .resume =               ohci_pci_resume,
 #endif
-       .stop =                 ohci_stop,
 
        /*
         * managing i/o requests and associated device resources
@@ -199,6 +202,7 @@ static const struct hc_driver ohci_pci_hc_driver = {
         */
        .hub_status_data =      ohci_hub_status_data,
        .hub_control =          ohci_hub_control,
+       .hub_irq_enable =       ohci_rhsc_enable,
 #ifdef CONFIG_PM
        .bus_suspend =          ohci_bus_suspend,
        .bus_resume =           ohci_bus_resume,
@@ -229,6 +233,8 @@ static struct pci_driver ohci_pci_driver = {
        .suspend =      usb_hcd_pci_suspend,
        .resume =       usb_hcd_pci_resume,
 #endif
+
+       .shutdown =     usb_hcd_pci_shutdown,
 };
 
  
diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c
new file mode 100644 (file)
index 0000000..82cb22f
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ * drivers/usb/host/ohci-pnx4008.c
+ *
+ * driver for Philips PNX4008 USB Host
+ *
+ * Authors: Dmitry Chigirev <source@mvista.com>
+ *         Vitaly Wool <vitalywool@gmail.com>
+ *
+ * register initialization is based on code examples provided by Philips
+ * Copyright (c) 2005 Koninklijke Philips Electronics N.V.
+ *
+ * NOTE: This driver does not have suspend/resume functionality
+ * This driver is intended for engineering development purposes only
+ *
+ * 2005-2006 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+
+#include <asm/arch/platform.h>
+#include <asm/arch/irqs.h>
+#include <asm/arch/gpio.h>
+
+#define USB_CTRL       IO_ADDRESS(PNX4008_PWRMAN_BASE + 0x64)
+
+/* USB_CTRL bit defines */
+#define USB_SLAVE_HCLK_EN      (1 << 24)
+#define USB_HOST_NEED_CLK_EN   (1 << 21)
+
+#define USB_OTG_CLK_CTRL       IO_ADDRESS(PNX4008_USB_CONFIG_BASE + 0xFF4)
+#define USB_OTG_CLK_STAT       IO_ADDRESS(PNX4008_USB_CONFIG_BASE + 0xFF8)
+
+/* USB_OTG_CLK_CTRL bit defines */
+#define AHB_M_CLOCK_ON         (1 << 4)
+#define OTG_CLOCK_ON           (1 << 3)
+#define I2C_CLOCK_ON           (1 << 2)
+#define DEV_CLOCK_ON           (1 << 1)
+#define HOST_CLOCK_ON          (1 << 0)
+
+#define USB_OTG_STAT_CONTROL   IO_ADDRESS(PNX4008_USB_CONFIG_BASE + 0x110)
+
+/* USB_OTG_STAT_CONTROL bit defines */
+#define TRANSPARENT_I2C_EN     (1 << 7)
+#define HOST_EN                        (1 << 0)
+
+/* ISP1301 USB transceiver I2C registers */
+#define        ISP1301_MODE_CONTROL_1          0x04    /* u8 read, set, +1 clear */
+
+#define        MC1_SPEED_REG           (1 << 0)
+#define        MC1_SUSPEND_REG         (1 << 1)
+#define        MC1_DAT_SE0             (1 << 2)
+#define        MC1_TRANSPARENT         (1 << 3)
+#define        MC1_BDIS_ACON_EN        (1 << 4)
+#define        MC1_OE_INT_EN           (1 << 5)
+#define        MC1_UART_EN             (1 << 6)
+#define        MC1_MASK                0x7f
+
+#define        ISP1301_MODE_CONTROL_2          0x12    /* u8 read, set, +1 clear */
+
+#define        MC2_GLOBAL_PWR_DN       (1 << 0)
+#define        MC2_SPD_SUSP_CTRL       (1 << 1)
+#define        MC2_BI_DI               (1 << 2)
+#define        MC2_TRANSP_BDIR0        (1 << 3)
+#define        MC2_TRANSP_BDIR1        (1 << 4)
+#define        MC2_AUDIO_EN            (1 << 5)
+#define        MC2_PSW_EN              (1 << 6)
+#define        MC2_EN2V7               (1 << 7)
+
+#define        ISP1301_OTG_CONTROL_1           0x06    /* u8 read, set, +1 clear */
+#      define  OTG1_DP_PULLUP          (1 << 0)
+#      define  OTG1_DM_PULLUP          (1 << 1)
+#      define  OTG1_DP_PULLDOWN        (1 << 2)
+#      define  OTG1_DM_PULLDOWN        (1 << 3)
+#      define  OTG1_ID_PULLDOWN        (1 << 4)
+#      define  OTG1_VBUS_DRV           (1 << 5)
+#      define  OTG1_VBUS_DISCHRG       (1 << 6)
+#      define  OTG1_VBUS_CHRG          (1 << 7)
+#define        ISP1301_OTG_STATUS              0x10    /* u8 readonly */
+#      define  OTG_B_SESS_END          (1 << 6)
+#      define  OTG_B_SESS_VLD          (1 << 7)
+
+#define ISP1301_I2C_ADDR 0x2C
+
+#define ISP1301_I2C_MODE_CONTROL_1 0x4
+#define ISP1301_I2C_MODE_CONTROL_2 0x12
+#define ISP1301_I2C_OTG_CONTROL_1 0x6
+#define ISP1301_I2C_OTG_CONTROL_2 0x10
+#define ISP1301_I2C_INTERRUPT_SOURCE 0x8
+#define ISP1301_I2C_INTERRUPT_LATCH 0xA
+#define ISP1301_I2C_INTERRUPT_FALLING 0xC
+#define ISP1301_I2C_INTERRUPT_RISING 0xE
+#define ISP1301_I2C_REG_CLEAR_ADDR 1
+
+struct i2c_driver isp1301_driver;
+struct i2c_client *isp1301_i2c_client;
+
+extern int usb_disabled(void);
+extern int ocpi_enable(void);
+
+static struct clk *usb_clk;
+
+static int isp1301_probe(struct i2c_adapter *adap);
+static int isp1301_detach(struct i2c_client *client);
+static int isp1301_command(struct i2c_client *client, unsigned int cmd,
+                          void *arg);
+
+static unsigned short normal_i2c[] =
+    { ISP1301_I2C_ADDR, ISP1301_I2C_ADDR + 1, I2C_CLIENT_END };
+static unsigned short dummy_i2c_addrlist[] = { I2C_CLIENT_END };
+
+static struct i2c_client_address_data addr_data = {
+       .normal_i2c = normal_i2c,
+       .probe = dummy_i2c_addrlist,
+       .ignore = dummy_i2c_addrlist,
+};
+
+struct i2c_driver isp1301_driver = {
+       .id = I2C_DRIVERID_I2CDEV,      /* Fake Id */
+       .class = I2C_CLASS_HWMON,
+       .attach_adapter = isp1301_probe,
+       .detach_client = isp1301_detach,
+       .command = isp1301_command
+};
+
+static int isp1301_attach(struct i2c_adapter *adap, int addr, int kind)
+{
+       struct i2c_client *c;
+
+       c = (struct i2c_client *)kzalloc(sizeof(*c), SLAB_KERNEL);
+
+       if (!c)
+               return -ENOMEM;
+
+       strcpy(c->name, "isp1301");
+       c->flags = 0;
+       c->addr = addr;
+       c->adapter = adap;
+       c->driver = &isp1301_driver;
+
+       isp1301_i2c_client = c;
+
+       return i2c_attach_client(c);
+}
+
+static int isp1301_probe(struct i2c_adapter *adap)
+{
+       return i2c_probe(adap, &addr_data, isp1301_attach);
+}
+
+static int isp1301_detach(struct i2c_client *client)
+{
+       i2c_detach_client(client);
+       kfree(isp1301_i2c_client);
+       return 0;
+}
+
+/* No commands defined */
+static int isp1301_command(struct i2c_client *client, unsigned int cmd,
+                          void *arg)
+{
+       return 0;
+}
+
+static void i2c_write(u8 buf, u8 subaddr)
+{
+       char tmpbuf[2];
+
+       tmpbuf[0] = subaddr;    /*register number */
+       tmpbuf[1] = buf;        /*register data */
+       i2c_master_send(isp1301_i2c_client, &tmpbuf[0], 2);
+}
+
+static void isp1301_configure(void)
+{
+       /* PNX4008 only supports DAT_SE0 USB mode */
+       /* PNX4008 R2A requires setting the MAX603 to output 3.6V */
+       /* Power up externel charge-pump */
+
+       i2c_write(MC1_DAT_SE0 | MC1_SPEED_REG, ISP1301_I2C_MODE_CONTROL_1);
+       i2c_write(~(MC1_DAT_SE0 | MC1_SPEED_REG),
+                 ISP1301_I2C_MODE_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR);
+       i2c_write(MC2_BI_DI | MC2_PSW_EN | MC2_SPD_SUSP_CTRL,
+                 ISP1301_I2C_MODE_CONTROL_2);
+       i2c_write(~(MC2_BI_DI | MC2_PSW_EN | MC2_SPD_SUSP_CTRL),
+                 ISP1301_I2C_MODE_CONTROL_2 | ISP1301_I2C_REG_CLEAR_ADDR);
+       i2c_write(OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN,
+                 ISP1301_I2C_OTG_CONTROL_1);
+       i2c_write(~(OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN),
+                 ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR);
+       i2c_write(0xFF,
+                 ISP1301_I2C_INTERRUPT_LATCH | ISP1301_I2C_REG_CLEAR_ADDR);
+       i2c_write(0xFF,
+                 ISP1301_I2C_INTERRUPT_FALLING | ISP1301_I2C_REG_CLEAR_ADDR);
+       i2c_write(0xFF,
+                 ISP1301_I2C_INTERRUPT_RISING | ISP1301_I2C_REG_CLEAR_ADDR);
+
+}
+
+static inline void isp1301_vbus_on(void)
+{
+       i2c_write(OTG1_VBUS_DRV, ISP1301_I2C_OTG_CONTROL_1);
+}
+
+static inline void isp1301_vbus_off(void)
+{
+       i2c_write(OTG1_VBUS_DRV,
+                 ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR);
+}
+
+static void pnx4008_start_hc(void)
+{
+       unsigned long tmp = __raw_readl(USB_OTG_STAT_CONTROL) | HOST_EN;
+       __raw_writel(tmp, USB_OTG_STAT_CONTROL);
+       isp1301_vbus_on();
+}
+
+static void pnx4008_stop_hc(void)
+{
+       unsigned long tmp;
+       isp1301_vbus_off();
+       tmp = __raw_readl(USB_OTG_STAT_CONTROL) & ~HOST_EN;
+       __raw_writel(tmp, USB_OTG_STAT_CONTROL);
+}
+
+static int __devinit ohci_pnx4008_start(struct usb_hcd *hcd)
+{
+       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+       int ret;
+
+       if ((ret = ohci_init(ohci)) < 0)
+               return ret;
+
+       if ((ret = ohci_run(ohci)) < 0) {
+               dev_err(hcd->self.controller, "can't start\n");
+               ohci_stop(hcd);
+               return ret;
+       }
+       return 0;
+}
+
+static const struct hc_driver ohci_pnx4008_hc_driver = {
+       .description = hcd_name,
+       .product_desc =         "pnx4008 OHCI",
+
+       /*
+        * generic hardware linkage
+        */
+       .irq = ohci_irq,
+       .flags = HCD_USB11 | HCD_MEMORY,
+
+       .hcd_priv_size =        sizeof(struct ohci_hcd),
+       /*
+        * basic lifecycle operations
+        */
+       .start = ohci_pnx4008_start,
+       .stop = ohci_stop,
+
+       /*
+        * managing i/o requests and associated device resources
+        */
+       .urb_enqueue = ohci_urb_enqueue,
+       .urb_dequeue = ohci_urb_dequeue,
+       .endpoint_disable = ohci_endpoint_disable,
+
+       /*
+        * scheduling support
+        */
+       .get_frame_number = ohci_get_frame,
+
+       /*
+        * root hub support
+        */
+       .hub_status_data = ohci_hub_status_data,
+       .hub_control = ohci_hub_control,
+
+       .start_port_reset = ohci_start_port_reset,
+};
+
+#define USB_CLOCK_MASK (AHB_M_CLOCK_ON| OTG_CLOCK_ON | HOST_CLOCK_ON | I2C_CLOCK_ON)
+
+static void pnx4008_set_usb_bits(void)
+{
+       start_int_set_falling_edge(SE_USB_OTG_ATX_INT_N);
+       start_int_ack(SE_USB_OTG_ATX_INT_N);
+       start_int_umask(SE_USB_OTG_ATX_INT_N);
+
+       start_int_set_rising_edge(SE_USB_OTG_TIMER_INT);
+       start_int_ack(SE_USB_OTG_TIMER_INT);
+       start_int_umask(SE_USB_OTG_TIMER_INT);
+
+       start_int_set_rising_edge(SE_USB_I2C_INT);
+       start_int_ack(SE_USB_I2C_INT);
+       start_int_umask(SE_USB_I2C_INT);
+
+       start_int_set_rising_edge(SE_USB_INT);
+       start_int_ack(SE_USB_INT);
+       start_int_umask(SE_USB_INT);
+
+       start_int_set_rising_edge(SE_USB_NEED_CLK_INT);
+       start_int_ack(SE_USB_NEED_CLK_INT);
+       start_int_umask(SE_USB_NEED_CLK_INT);
+
+       start_int_set_rising_edge(SE_USB_AHB_NEED_CLK_INT);
+       start_int_ack(SE_USB_AHB_NEED_CLK_INT);
+       start_int_umask(SE_USB_AHB_NEED_CLK_INT);
+}
+
+static void pnx4008_unset_usb_bits(void)
+{
+       start_int_mask(SE_USB_OTG_ATX_INT_N);
+       start_int_mask(SE_USB_OTG_TIMER_INT);
+       start_int_mask(SE_USB_I2C_INT);
+       start_int_mask(SE_USB_INT);
+       start_int_mask(SE_USB_NEED_CLK_INT);
+       start_int_mask(SE_USB_AHB_NEED_CLK_INT);
+}
+
+static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev)
+{
+       struct usb_hcd *hcd = 0;
+       struct ohci_hcd *ohci;
+       const struct hc_driver *driver = &ohci_pnx4008_hc_driver;
+
+       int ret = 0, irq;
+
+       dev_dbg(&pdev->dev, "%s: " DRIVER_INFO " (pnx4008)\n", hcd_name);
+       if (usb_disabled()) {
+               err("USB is disabled");
+               ret = -ENODEV;
+               goto out;
+       }
+
+       if (pdev->num_resources != 2
+           || pdev->resource[0].flags != IORESOURCE_MEM
+           || pdev->resource[1].flags != IORESOURCE_IRQ) {
+               err("Invalid resource configuration");
+               ret = -ENODEV;
+               goto out;
+       }
+
+       /* Enable AHB slave USB clock, needed for further USB clock control */
+       __raw_writel(USB_SLAVE_HCLK_EN | (1 << 19), USB_CTRL);
+
+       ret = i2c_add_driver(&isp1301_driver);
+       if (ret < 0) {
+               err("failed to connect I2C to ISP1301 USB Transceiver");
+               goto out;
+       }
+
+       isp1301_configure();
+
+       /* Enable USB PLL */
+       usb_clk = clk_get(&pdev->dev, "ck_pll5");
+       if (IS_ERR(usb_clk)) {
+               err("failed to acquire USB PLL");
+               ret = PTR_ERR(usb_clk);
+               goto out1;
+       }
+
+       ret = clk_enable(usb_clk);
+       if (ret < 0) {
+               err("failed to start USB PLL");
+               goto out2;
+       }
+
+       ret = clk_set_rate(usb_clk, 48000);
+       if (ret < 0) {
+               err("failed to set USB clock rate");
+               goto out3;
+       }
+
+       __raw_writel(__raw_readl(USB_CTRL) | USB_HOST_NEED_CLK_EN, USB_CTRL);
+
+       /* Set to enable all needed USB clocks */
+       __raw_writel(USB_CLOCK_MASK, USB_OTG_CLK_CTRL);
+
+       while ((__raw_readl(USB_OTG_CLK_STAT) & USB_CLOCK_MASK) !=
+              USB_CLOCK_MASK) ;
+
+       hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id);
+       if (!hcd) {
+               err("Failed to allocate HC buffer");
+               ret = -ENOMEM;
+               goto out3;
+       }
+
+       /* Set all USB bits in the Start Enable register */
+       pnx4008_set_usb_bits();
+
+       hcd->rsrc_start = pdev->resource[0].start;
+       hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
+       if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+               dev_dbg(&pdev->dev, "request_mem_region failed\n");
+               ret =  -ENOMEM;
+               goto out4;
+       }
+       hcd->regs = (void __iomem *)pdev->resource[0].start;
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               ret = -ENXIO;
+               goto out4;
+       }
+
+       hcd->self.hcpriv = (void *)hcd;
+
+       pnx4008_start_hc();
+       platform_set_drvdata(pdev, hcd);
+       ohci = hcd_to_ohci(hcd);
+       ohci_hcd_init(ohci);
+
+       dev_info(&pdev->dev, "at 0x%p, irq %d\n", hcd->regs, hcd->irq);
+       ret = usb_add_hcd(hcd, irq, SA_INTERRUPT);
+       if (ret == 0)
+               return ret;
+
+       pnx4008_stop_hc();
+out4:
+       pnx4008_unset_usb_bits();
+       usb_put_hcd(hcd);
+out3:
+       clk_disable(usb_clk);
+out2:
+       clk_put(usb_clk);
+out1:
+       i2c_del_driver(&isp1301_driver);
+out:
+       return ret;
+}
+
+static int usb_hcd_pnx4008_remove(struct platform_device *pdev)
+{
+       struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+       usb_remove_hcd(hcd);
+       pnx4008_stop_hc();
+       release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+       usb_put_hcd(hcd);
+       pnx4008_unset_usb_bits();
+       clk_disable(usb_clk);
+       clk_put(usb_clk);
+       i2c_del_driver(&isp1301_driver);
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static struct platform_driver usb_hcd_pnx4008_driver = {
+       .driver = {
+               .name = "usb-ohci",
+       },
+       .probe = usb_hcd_pnx4008_probe,
+       .remove = usb_hcd_pnx4008_remove,
+};
+
+static int __init usb_hcd_pnx4008_init(void)
+{
+       return platform_driver_register(&usb_hcd_pnx4008_driver);
+}
+
+static void __exit usb_hcd_pnx4008_cleanup(void)
+{
+       return platform_driver_unregister(&usb_hcd_pnx4008_driver);
+}
+
+module_init(usb_hcd_pnx4008_init);
+module_exit(usb_hcd_pnx4008_cleanup);
index 9fe56ff..d9d1ae2 100644 (file)
@@ -148,6 +148,7 @@ static const struct hc_driver ohci_ppc_soc_hc_driver = {
         */
        .start =                ohci_ppc_soc_start,
        .stop =                 ohci_stop,
+       .shutdown =             ohci_shutdown,
 
        /*
         * managing i/o requests and associated device resources
@@ -166,6 +167,7 @@ static const struct hc_driver ohci_ppc_soc_hc_driver = {
         */
        .hub_status_data =      ohci_hub_status_data,
        .hub_control =          ohci_hub_control,
+       .hub_irq_enable =       ohci_rhsc_enable,
 #ifdef CONFIG_PM
        .bus_suspend =          ohci_bus_suspend,
        .bus_resume =           ohci_bus_resume,
@@ -195,6 +197,7 @@ static int ohci_hcd_ppc_soc_drv_remove(struct platform_device *pdev)
 static struct platform_driver ohci_hcd_ppc_soc_driver = {
        .probe          = ohci_hcd_ppc_soc_drv_probe,
        .remove         = ohci_hcd_ppc_soc_drv_remove,
+       .shutdown       = usb_hcd_platform_shutdown,
 #ifdef CONFIG_PM
        /*.suspend      = ohci_hcd_ppc_soc_drv_suspend,*/
        /*.resume       = ohci_hcd_ppc_soc_drv_resume,*/
index 6f559e1..e176b04 100644 (file)
@@ -270,6 +270,7 @@ static const struct hc_driver ohci_pxa27x_hc_driver = {
         */
        .start =                ohci_pxa27x_start,
        .stop =                 ohci_stop,
+       .shutdown =             ohci_shutdown,
 
        /*
         * managing i/o requests and associated device resources
@@ -288,6 +289,7 @@ static const struct hc_driver ohci_pxa27x_hc_driver = {
         */
        .hub_status_data =      ohci_hub_status_data,
        .hub_control =          ohci_hub_control,
+       .hub_irq_enable =       ohci_rhsc_enable,
 #ifdef  CONFIG_PM
        .bus_suspend =          ohci_bus_suspend,
        .bus_resume =           ohci_bus_resume,
@@ -357,6 +359,7 @@ static int ohci_hcd_pxa27x_drv_resume(struct platform_device *pdev)
 static struct platform_driver ohci_hcd_pxa27x_driver = {
        .probe          = ohci_hcd_pxa27x_drv_probe,
        .remove         = ohci_hcd_pxa27x_drv_remove,
+       .shutdown       = usb_hcd_platform_shutdown,
 #ifdef CONFIG_PM
        .suspend        = ohci_hcd_pxa27x_drv_suspend, 
        .resume         = ohci_hcd_pxa27x_drv_resume,
index d2fc696..59e4364 100644 (file)
@@ -370,7 +370,7 @@ static int usb_hcd_s3c2410_probe (const struct hc_driver *driver,
                goto err_mem;
        }
 
-       usb_clk = clk_get(&dev->dev, "upll");
+       usb_clk = clk_get(&dev->dev, "usb-bus-host");
        if (IS_ERR(usb_clk)) {
                dev_err(&dev->dev, "cannot get usb-host clock\n");
                retval = -ENOENT;
@@ -447,6 +447,7 @@ static const struct hc_driver ohci_s3c2410_hc_driver = {
         */
        .start =                ohci_s3c2410_start,
        .stop =                 ohci_stop,
+       .shutdown =             ohci_shutdown,
 
        /*
         * managing i/o requests and associated device resources
@@ -465,6 +466,7 @@ static const struct hc_driver ohci_s3c2410_hc_driver = {
         */
        .hub_status_data =      ohci_s3c2410_hub_status_data,
        .hub_control =          ohci_s3c2410_hub_control,
+       .hub_irq_enable =       ohci_rhsc_enable,
 #ifdef CONFIG_PM
        .bus_suspend =          ohci_bus_suspend,
        .bus_resume =           ohci_bus_resume,
@@ -490,6 +492,7 @@ static int ohci_hcd_s3c2410_drv_remove(struct platform_device *pdev)
 static struct platform_driver ohci_hcd_s3c2410_driver = {
        .probe          = ohci_hcd_s3c2410_drv_probe,
        .remove         = ohci_hcd_s3c2410_drv_remove,
+       .shutdown       = usb_hcd_platform_shutdown,
        /*.suspend      = ohci_hcd_s3c2410_drv_suspend, */
        /*.resume       = ohci_hcd_s3c2410_drv_resume, */
        .driver         = {
index ce3de10..71371de 100644 (file)
@@ -212,10 +212,6 @@ static const struct hc_driver ohci_sa1111_hc_driver = {
         * basic lifecycle operations
         */
        .start =                ohci_sa1111_start,
-#ifdef CONFIG_PM
-       /* suspend:             ohci_sa1111_suspend,  -- tbd */
-       /* resume:              ohci_sa1111_resume,   -- tbd */
-#endif
        .stop =                 ohci_stop,
 
        /*
@@ -235,6 +231,7 @@ static const struct hc_driver ohci_sa1111_hc_driver = {
         */
        .hub_status_data =      ohci_hub_status_data,
        .hub_control =          ohci_hub_control,
+       .hub_irq_enable =       ohci_rhsc_enable,
 #ifdef CONFIG_PM
        .bus_suspend =          ohci_bus_suspend,
        .bus_resume =           ohci_bus_resume,
index caacf14..93fdc3c 100644 (file)
@@ -159,7 +159,7 @@ static const int cc_to_error [16] = {
        /* Bit Stuff  */               -EPROTO,
        /* Data Togg  */               -EILSEQ,
        /* Stall      */               -EPIPE,
-       /* DevNotResp */               -ETIMEDOUT,
+       /* DevNotResp */               -ETIME,
        /* PIDCheck   */               -EPROTO,
        /* UnExpPID   */               -EPROTO,
        /* DataOver   */               -EOVERFLOW,
@@ -389,8 +389,6 @@ struct ohci_hcd {
        unsigned long           next_statechange;       /* suspend/resume */
        u32                     fminterval;             /* saved register */
 
-       struct notifier_block   reboot_notifier;
-
        unsigned long           flags;          /* for HC bugs */
 #define        OHCI_QUIRK_AMD756       0x01                    /* erratum #4 */
 #define        OHCI_QUIRK_SUPERIO      0x02                    /* natsemi */
index 9de115d..3a586aa 100644 (file)
@@ -597,7 +597,7 @@ done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank, struct pt_regs *regs)
        /* error? retry, until "3 strikes" */
        } else if (++ep->error_count >= 3) {
                if (status & SL11H_STATMASK_TMOUT)
-                       urbstat = -ETIMEDOUT;
+                       urbstat = -ETIME;
                else if (status & SL11H_STATMASK_OVF)
                        urbstat = -EOVERFLOW;
                else
@@ -1517,7 +1517,7 @@ static int proc_sl811h_open(struct inode *inode, struct file *file)
        return single_open(file, proc_sl811h_show, PDE(inode)->data);
 }
 
-static struct file_operations proc_ops = {
+static const struct file_operations proc_ops = {
        .open           = proc_sl811h_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
new file mode 100644 (file)
index 0000000..cb2e2a6
--- /dev/null
@@ -0,0 +1,3295 @@
+/*
+* Host Controller Driver for the Elan Digital Systems U132 adapter
+*
+* Copyright(C) 2006 Elan Digital Systems Limited
+* http://www.elandigitalsystems.com
+*
+* Author and Maintainer - Tony Olech - Elan Digital Systems
+* tony.olech@elandigitalsystems.com
+*
+* This program is free software;you can redistribute it and/or
+* modify it under the terms of the GNU General Public License as
+* published by the Free Software Foundation, version 2.
+*
+*
+* This driver was written by Tony Olech(tony.olech@elandigitalsystems.com)
+* based on various USB host drivers in the 2.6.15 linux kernel
+* with constant reference to the 3rd Edition of Linux Device Drivers
+* published by O'Reilly
+*
+* The U132 adapter is a USB to CardBus adapter specifically designed
+* for PC cards that contain an OHCI host controller. Typical PC cards
+* are the Orange Mobile 3G Option GlobeTrotter Fusion card.
+*
+* The U132 adapter will *NOT *work with PC cards that do not contain
+* an OHCI controller. A simple way to test whether a PC card has an
+* OHCI controller as an interface is to insert the PC card directly
+* into a laptop(or desktop) with a CardBus slot and if "lspci" shows
+* a new USB controller and "lsusb -v" shows a new OHCI Host Controller
+* then there is a good chance that the U132 adapter will support the
+* PC card.(you also need the specific client driver for the PC card)
+*
+* Please inform the Author and Maintainer about any PC cards that
+* contain OHCI Host Controller and work when directly connected to
+* an embedded CardBus slot but do not work when they are connected
+* via an ELAN U132 adapter.
+*
+*/
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/pci_ids.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include "../core/hcd.h"
+#include "ohci.h"
+#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
+#define OHCI_INTR_INIT (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | \
+        OHCI_INTR_WDH)
+MODULE_AUTHOR("Tony Olech - Elan Digital Systems Limited");
+MODULE_DESCRIPTION("U132 USB Host Controller Driver");
+MODULE_LICENSE("GPL");
+#define INT_MODULE_PARM(n, v) static int n = v;module_param(n, int, 0444)
+INT_MODULE_PARM(testing, 0);
+/* Some boards misreport power switching/overcurrent*/
+static int distrust_firmware = 1;
+module_param(distrust_firmware, bool, 0);
+MODULE_PARM_DESC(distrust_firmware, "true to distrust firmware power/overcurren"
+        "t setup");
+DECLARE_WAIT_QUEUE_HEAD(u132_hcd_wait);
+/*
+* u132_module_lock exists to protect access to global variables
+*
+*/
+static struct semaphore u132_module_lock;
+static int u132_exiting = 0;
+static int u132_instances = 0;
+static struct list_head u132_static_list;
+/*
+* end of the global variables protected by u132_module_lock
+*/
+static struct workqueue_struct *workqueue;
+#define MAX_U132_PORTS 7
+#define MAX_U132_ADDRS 128
+#define MAX_U132_UDEVS 4
+#define MAX_U132_ENDPS 100
+#define MAX_U132_RINGS 4
+static const char *cc_to_text[16] = {
+        "No Error ",
+        "CRC Error ",
+        "Bit Stuff ",
+        "Data Togg ",
+        "Stall ",
+        "DevNotResp ",
+        "PIDCheck ",
+        "UnExpPID ",
+        "DataOver ",
+        "DataUnder ",
+        "(for hw) ",
+        "(for hw) ",
+        "BufferOver ",
+        "BuffUnder ",
+        "(for HCD) ",
+        "(for HCD) "
+};
+struct u132_port {
+        struct u132 *u132;
+        int reset;
+        int enable;
+        int power;
+        int Status;
+};
+struct u132_addr {
+        u8 address;
+};
+struct u132_udev {
+        struct kref kref;
+        struct usb_device *usb_device;
+        u8 enumeration;
+        u8 udev_number;
+        u8 usb_addr;
+        u8 portnumber;
+        u8 endp_number_in[16];
+        u8 endp_number_out[16];
+};
+#define ENDP_QUEUE_SHIFT 3
+#define ENDP_QUEUE_SIZE (1<<ENDP_QUEUE_SHIFT)
+#define ENDP_QUEUE_MASK (ENDP_QUEUE_SIZE-1)
+struct u132_urbq {
+        struct list_head urb_more;
+        struct urb *urb;
+};
+struct u132_spin {
+        spinlock_t slock;
+};
+struct u132_endp {
+        struct kref kref;
+        u8 udev_number;
+        u8 endp_number;
+        u8 usb_addr;
+        u8 usb_endp;
+        struct u132 *u132;
+        struct list_head endp_ring;
+        struct u132_ring *ring;
+        unsigned toggle_bits:2;
+        unsigned active:1;
+        unsigned delayed:1;
+        unsigned input:1;
+        unsigned output:1;
+        unsigned pipetype:2;
+        unsigned dequeueing:1;
+        unsigned edset_flush:1;
+        unsigned spare_bits:14;
+        unsigned long jiffies;
+        struct usb_host_endpoint *hep;
+        struct u132_spin queue_lock;
+        u16 queue_size;
+        u16 queue_last;
+        u16 queue_next;
+        struct urb *urb_list[ENDP_QUEUE_SIZE];
+        struct list_head urb_more;
+        struct work_struct scheduler;
+};
+struct u132_ring {
+        unsigned in_use:1;
+        unsigned length:7;
+        u8 number;
+        struct u132 *u132;
+        struct u132_endp *curr_endp;
+        struct work_struct scheduler;
+};
+#define OHCI_QUIRK_AMD756 0x01
+#define OHCI_QUIRK_SUPERIO 0x02
+#define OHCI_QUIRK_INITRESET 0x04
+#define OHCI_BIG_ENDIAN 0x08
+#define OHCI_QUIRK_ZFMICRO 0x10
+struct u132 {
+        struct kref kref;
+        struct list_head u132_list;
+        struct semaphore sw_lock;
+        struct semaphore scheduler_lock;
+        struct u132_platform_data *board;
+        struct platform_device *platform_dev;
+        struct u132_ring ring[MAX_U132_RINGS];
+        int sequence_num;
+        int going;
+        int power;
+        int reset;
+        int num_ports;
+        u32 hc_control;
+        u32 hc_fminterval;
+        u32 hc_roothub_status;
+        u32 hc_roothub_a;
+        u32 hc_roothub_portstatus[MAX_ROOT_PORTS];
+        int flags;
+        unsigned long next_statechange;
+        struct work_struct monitor;
+        int num_endpoints;
+        struct u132_addr addr[MAX_U132_ADDRS];
+        struct u132_udev udev[MAX_U132_UDEVS];
+        struct u132_port port[MAX_U132_PORTS];
+        struct u132_endp *endp[MAX_U132_ENDPS];
+};
+int usb_ftdi_elan_read_reg(struct platform_device *pdev, u32 *data);
+int usb_ftdi_elan_read_pcimem(struct platform_device *pdev, u8 addressofs,
+        u8 width, u32 *data);
+int usb_ftdi_elan_write_pcimem(struct platform_device *pdev, u8 addressofs,
+        u8 width, u32 data);
+/*
+* these can not be inlines because we need the structure offset!!
+* Does anyone have a better way?????
+*/
+#define u132_read_pcimem(u132, member, data) \
+        usb_ftdi_elan_read_pcimem(u132->platform_dev, offsetof(struct \
+        ohci_regs, member), 0, data);
+#define u132_write_pcimem(u132, member, data) \
+        usb_ftdi_elan_write_pcimem(u132->platform_dev, offsetof(struct \
+        ohci_regs, member), 0, data);
+#define u132_write_pcimem_byte(u132, member, data) \
+        usb_ftdi_elan_write_pcimem(u132->platform_dev, offsetof(struct \
+        ohci_regs, member), 0x0e, data);
+static inline struct u132 *udev_to_u132(struct u132_udev *udev)
+{
+        u8 udev_number = udev->udev_number;
+        return container_of(udev, struct u132, udev[udev_number]);
+}
+
+static inline struct u132 *hcd_to_u132(struct usb_hcd *hcd)
+{
+        return (struct u132 *)(hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *u132_to_hcd(struct u132 *u132)
+{
+        return container_of((void *)u132, struct usb_hcd, hcd_priv);
+}
+
+static inline void u132_disable(struct u132 *u132)
+{
+        u132_to_hcd(u132)->state = HC_STATE_HALT;
+}
+
+
+#define kref_to_u132(d) container_of(d, struct u132, kref)
+#define kref_to_u132_endp(d) container_of(d, struct u132_endp, kref)
+#define kref_to_u132_udev(d) container_of(d, struct u132_udev, kref)
+#include "../misc/usb_u132.h"
+static const char hcd_name[] = "u132_hcd";
+#define PORT_C_MASK ((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 void u132_hcd_delete(struct kref *kref)
+{
+        struct u132 *u132 = kref_to_u132(kref);
+        struct platform_device *pdev = u132->platform_dev;
+        struct usb_hcd *hcd = u132_to_hcd(u132);
+        u132->going += 1;
+        down(&u132_module_lock);
+        list_del_init(&u132->u132_list);
+        u132_instances -= 1;
+        up(&u132_module_lock);
+        dev_warn(&u132->platform_dev->dev, "FREEING the hcd=%p and thus the u13"
+                "2=%p going=%d pdev=%p\n", hcd, u132, u132->going, pdev);
+        usb_put_hcd(hcd);
+}
+
+static inline void u132_u132_put_kref(struct u132 *u132)
+{
+        kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static inline void u132_u132_init_kref(struct u132 *u132)
+{
+        kref_init(&u132->kref);
+}
+
+static void u132_udev_delete(struct kref *kref)
+{
+        struct u132_udev *udev = kref_to_u132_udev(kref);
+        udev->udev_number = 0;
+        udev->usb_device = NULL;
+        udev->usb_addr = 0;
+        udev->enumeration = 0;
+}
+
+static inline void u132_udev_put_kref(struct u132 *u132, struct u132_udev *udev)
+{
+        kref_put(&udev->kref, u132_udev_delete);
+}
+
+static inline void u132_udev_get_kref(struct u132 *u132, struct u132_udev *udev)
+{
+        kref_get(&udev->kref);
+}
+
+static inline void u132_udev_init_kref(struct u132 *u132,
+        struct u132_udev *udev)
+{
+        kref_init(&udev->kref);
+}
+
+static inline void u132_ring_put_kref(struct u132 *u132, struct u132_ring *ring)
+{
+        kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static void u132_ring_requeue_work(struct u132 *u132, struct u132_ring *ring,
+        unsigned int delta)
+{
+        if (delta > 0) {
+                if (queue_delayed_work(workqueue, &ring->scheduler, delta))
+                        return;
+        } else if (queue_work(workqueue, &ring->scheduler))
+                return;
+        kref_put(&u132->kref, u132_hcd_delete);
+        return;
+}
+
+static void u132_ring_queue_work(struct u132 *u132, struct u132_ring *ring,
+        unsigned int delta)
+{
+        kref_get(&u132->kref);
+        u132_ring_requeue_work(u132, ring, delta);
+        return;
+}
+
+static void u132_ring_cancel_work(struct u132 *u132, struct u132_ring *ring)
+{
+        if (cancel_delayed_work(&ring->scheduler)) {
+                kref_put(&u132->kref, u132_hcd_delete);
+        }
+}
+
+static void u132_endp_delete(struct kref *kref)
+{
+        struct u132_endp *endp = kref_to_u132_endp(kref);
+        struct u132 *u132 = endp->u132;
+        u8 usb_addr = endp->usb_addr;
+        u8 usb_endp = endp->usb_endp;
+        u8 address = u132->addr[usb_addr].address;
+        struct u132_udev *udev = &u132->udev[address];
+        u8 endp_number = endp->endp_number;
+        struct usb_host_endpoint *hep = endp->hep;
+        struct u132_ring *ring = endp->ring;
+        struct list_head *head = &endp->endp_ring;
+        ring->length -= 1;
+        if (endp == ring->curr_endp) {
+                if (list_empty(head)) {
+                        ring->curr_endp = NULL;
+                        list_del(head);
+                } else {
+                        struct u132_endp *next_endp = list_entry(head->next,
+                                struct u132_endp, endp_ring);
+                        ring->curr_endp = next_endp;
+                        list_del(head);
+        }} else
+                list_del(head);
+        if (endp->input) {
+                udev->endp_number_in[usb_endp] = 0;
+                u132_udev_put_kref(u132, udev);
+        }
+        if (endp->output) {
+                udev->endp_number_out[usb_endp] = 0;
+                u132_udev_put_kref(u132, udev);
+        }
+        u132->endp[endp_number - 1] = NULL;
+        hep->hcpriv = NULL;
+        kfree(endp);
+        u132_u132_put_kref(u132);
+}
+
+static inline void u132_endp_put_kref(struct u132 *u132, struct u132_endp *endp)
+{
+        kref_put(&endp->kref, u132_endp_delete);
+}
+
+static inline void u132_endp_get_kref(struct u132 *u132, struct u132_endp *endp)
+{
+        kref_get(&endp->kref);
+}
+
+static inline void u132_endp_init_kref(struct u132 *u132,
+        struct u132_endp *endp)
+{
+        kref_init(&endp->kref);
+        kref_get(&u132->kref);
+}
+
+static void u132_endp_queue_work(struct u132 *u132, struct u132_endp *endp,
+        unsigned int delta)
+{
+        if (delta > 0) {
+                if (queue_delayed_work(workqueue, &endp->scheduler, delta))
+                        kref_get(&endp->kref);
+        } else if (queue_work(workqueue, &endp->scheduler))
+                kref_get(&endp->kref);
+        return;
+}
+
+static void u132_endp_cancel_work(struct u132 *u132, struct u132_endp *endp)
+{
+        if (cancel_delayed_work(&endp->scheduler))
+                kref_put(&endp->kref, u132_endp_delete);
+}
+
+static inline void u132_monitor_put_kref(struct u132 *u132)
+{
+        kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static void u132_monitor_queue_work(struct u132 *u132, unsigned int delta)
+{
+        if (delta > 0) {
+                if (queue_delayed_work(workqueue, &u132->monitor, delta)) {
+                        kref_get(&u132->kref);
+                }
+        } else if (queue_work(workqueue, &u132->monitor))
+                kref_get(&u132->kref);
+        return;
+}
+
+static void u132_monitor_requeue_work(struct u132 *u132, unsigned int delta)
+{
+        if (delta > 0) {
+                if (queue_delayed_work(workqueue, &u132->monitor, delta))
+                        return;
+        } else if (queue_work(workqueue, &u132->monitor))
+                return;
+        kref_put(&u132->kref, u132_hcd_delete);
+        return;
+}
+
+static void u132_monitor_cancel_work(struct u132 *u132)
+{
+        if (cancel_delayed_work(&u132->monitor))
+                kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static int read_roothub_info(struct u132 *u132)
+{
+        u32 revision;
+        int retval;
+        retval = u132_read_pcimem(u132, revision, &revision);
+        if (retval) {
+                dev_err(&u132->platform_dev->dev, "error %d accessing device co"
+                        "ntrol\n", retval);
+                return retval;
+        } else if ((revision & 0xFF) == 0x10) {
+        } else if ((revision & 0xFF) == 0x11) {
+        } else {
+                dev_err(&u132->platform_dev->dev, "device revision is not valid"
+                        " %08X\n", revision);
+                return -ENODEV;
+        }
+        retval = u132_read_pcimem(u132, control, &u132->hc_control);
+        if (retval) {
+                dev_err(&u132->platform_dev->dev, "error %d accessing device co"
+                        "ntrol\n", retval);
+                return retval;
+        }
+        retval = u132_read_pcimem(u132, roothub.status,
+                &u132->hc_roothub_status);
+        if (retval) {
+                dev_err(&u132->platform_dev->dev, "error %d accessing device re"
+                        "g roothub.status\n", retval);
+                return retval;
+        }
+        retval = u132_read_pcimem(u132, roothub.a, &u132->hc_roothub_a);
+        if (retval) {
+                dev_err(&u132->platform_dev->dev, "error %d accessing device re"
+                        "g roothub.a\n", retval);
+                return retval;
+        }
+        {
+                int I = u132->num_ports;
+                int i = 0;
+                while (I-- > 0) {
+                        retval = u132_read_pcimem(u132, roothub.portstatus[i],
+                                &u132->hc_roothub_portstatus[i]);
+                        if (retval) {
+                                dev_err(&u132->platform_dev->dev, "error %d acc"
+                                        "essing device roothub.portstatus[%d]\n"
+                                        , retval, i);
+                                return retval;
+                        } else
+                                i += 1;
+                }
+        }
+        return 0;
+}
+
+static void u132_hcd_monitor_work(void *data)
+{
+        struct u132 *u132 = data;
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                u132_monitor_put_kref(u132);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                u132_monitor_put_kref(u132);
+                return;
+        } else {
+                int retval;
+                down(&u132->sw_lock);
+                retval = read_roothub_info(u132);
+                if (retval) {
+                        struct usb_hcd *hcd = u132_to_hcd(u132);
+                        u132_disable(u132);
+                        u132->going = 1;
+                        up(&u132->sw_lock);
+                        usb_hc_died(hcd);
+                        ftdi_elan_gone_away(u132->platform_dev);
+                        u132_monitor_put_kref(u132);
+                        return;
+                } else {
+                        u132_monitor_requeue_work(u132, 500);
+                        up(&u132->sw_lock);
+                        return;
+                }
+        }
+}
+
+static void u132_hcd_giveback_urb(struct u132 *u132, struct u132_endp *endp,
+        struct urb *urb, int status)
+{
+        struct u132_ring *ring;
+        unsigned long irqs;
+        struct usb_hcd *hcd = u132_to_hcd(u132);
+        urb->error_count = 0;
+        urb->status = status;
+        urb->hcpriv = NULL;
+        spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+        endp->queue_next += 1;
+        if (ENDP_QUEUE_SIZE > --endp->queue_size) {
+                endp->active = 0;
+                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+        } else {
+                struct list_head *next = endp->urb_more.next;
+                struct u132_urbq *urbq = list_entry(next, struct u132_urbq,
+                        urb_more);
+                list_del(next);
+                endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] =
+                        urbq->urb;
+                endp->active = 0;
+                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+                kfree(urbq);
+        } down(&u132->scheduler_lock);
+        ring = endp->ring;
+        ring->in_use = 0;
+        u132_ring_cancel_work(u132, ring);
+        u132_ring_queue_work(u132, ring, 0);
+        up(&u132->scheduler_lock);
+        u132_endp_put_kref(u132, endp);
+        usb_hcd_giveback_urb(hcd, urb, NULL);
+        return;
+}
+
+static void u132_hcd_forget_urb(struct u132 *u132, struct u132_endp *endp,
+        struct urb *urb, int status)
+{
+        u132_endp_put_kref(u132, endp);
+}
+
+static void u132_hcd_abandon_urb(struct u132 *u132, struct u132_endp *endp,
+        struct urb *urb, int status)
+{
+        unsigned long irqs;
+        struct usb_hcd *hcd = u132_to_hcd(u132);
+        urb->error_count = 0;
+        urb->status = status;
+        urb->hcpriv = NULL;
+        spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+        endp->queue_next += 1;
+        if (ENDP_QUEUE_SIZE > --endp->queue_size) {
+                endp->active = 0;
+                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+        } else {
+                struct list_head *next = endp->urb_more.next;
+                struct u132_urbq *urbq = list_entry(next, struct u132_urbq,
+                        urb_more);
+                list_del(next);
+                endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] =
+                        urbq->urb;
+                endp->active = 0;
+                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+                kfree(urbq);
+        } usb_hcd_giveback_urb(hcd, urb, NULL);
+        return;
+}
+
+static inline int edset_input(struct u132 *u132, struct u132_ring *ring,
+        struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null))
+{
+        return usb_ftdi_elan_edset_input(u132->platform_dev, ring->number, endp,
+                 urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+static inline int edset_setup(struct u132 *u132, struct u132_ring *ring,
+        struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null))
+{
+        return usb_ftdi_elan_edset_setup(u132->platform_dev, ring->number, endp,
+                 urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+static inline int edset_single(struct u132 *u132, struct u132_ring *ring,
+        struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null))
+{
+        return usb_ftdi_elan_edset_single(u132->platform_dev, ring->number,
+                endp, urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+static inline int edset_output(struct u132 *u132, struct u132_ring *ring,
+        struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null))
+{
+        return usb_ftdi_elan_edset_output(u132->platform_dev, ring->number,
+                endp, urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+
+/*
+* must not LOCK sw_lock
+*
+*/
+static void u132_hcd_interrupt_recv(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        u8 address = u132->addr[endp->usb_addr].address;
+        struct u132_udev *udev = &u132->udev[address];
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                struct u132_ring *ring = endp->ring;
+                u8 *u = urb->transfer_buffer + urb->actual_length;
+                u8 *b = buf;
+                int L = len;
+                while (L-- > 0) {
+                        *u++ = *b++;
+                }
+                urb->actual_length += len;
+                if ((condition_code == TD_CC_NOERROR) &&
+                        (urb->transfer_buffer_length > urb->actual_length)) {
+                        endp->toggle_bits = toggle_bits;
+                        usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+                                1 & toggle_bits);
+                        if (urb->actual_length > 0) {
+                                int retval;
+                                up(&u132->scheduler_lock);
+                                retval = edset_single(u132, ring, endp, urb,
+                                        address, endp->toggle_bits,
+                                        u132_hcd_interrupt_recv);
+                                if (retval == 0) {
+                                } else
+                                        u132_hcd_giveback_urb(u132, endp, urb,
+                                                retval);
+                        } else {
+                                ring->in_use = 0;
+                                endp->active = 0;
+                                endp->jiffies = jiffies +
+                                        msecs_to_jiffies(urb->interval);
+                                u132_ring_cancel_work(u132, ring);
+                                u132_ring_queue_work(u132, ring, 0);
+                                up(&u132->scheduler_lock);
+                                u132_endp_put_kref(u132, endp);
+                        }
+                        return;
+                } else if ((condition_code == TD_DATAUNDERRUN) &&
+                        ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0)) {
+                        endp->toggle_bits = toggle_bits;
+                        usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+                                1 & toggle_bits);
+                        up(&u132->scheduler_lock);
+                        u132_hcd_giveback_urb(u132, endp, urb, 0);
+                        return;
+                } else {
+                        if (condition_code == TD_CC_NOERROR) {
+                                endp->toggle_bits = toggle_bits;
+                                usb_settoggle(udev->usb_device, endp->usb_endp,
+                                        0, 1 & toggle_bits);
+                        } else if (condition_code == TD_CC_STALL) {
+                                endp->toggle_bits = 0x2;
+                                usb_settoggle(udev->usb_device, endp->usb_endp,
+                                        0, 0);
+                        } else {
+                                endp->toggle_bits = 0x2;
+                                usb_settoggle(udev->usb_device, endp->usb_endp,
+                                        0, 0);
+                                dev_err(&u132->platform_dev->dev, "urb=%p givin"
+                                        "g back INTERRUPT %s\n", urb,
+                                        cc_to_text[condition_code]);
+                        }
+                        up(&u132->scheduler_lock);
+                        u132_hcd_giveback_urb(u132, endp, urb,
+                                cc_to_error[condition_code]);
+                        return;
+                }
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_bulk_output_sent(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        u8 address = u132->addr[endp->usb_addr].address;
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                struct u132_ring *ring = endp->ring;
+                urb->actual_length += len;
+                endp->toggle_bits = toggle_bits;
+                if (urb->transfer_buffer_length > urb->actual_length) {
+                        int retval;
+                        up(&u132->scheduler_lock);
+                        retval = edset_output(u132, ring, endp, urb, address,
+                                endp->toggle_bits, u132_hcd_bulk_output_sent);
+                        if (retval == 0) {
+                        } else
+                                u132_hcd_giveback_urb(u132, endp, urb, retval);
+                        return;
+                } else {
+                        up(&u132->scheduler_lock);
+                        u132_hcd_giveback_urb(u132, endp, urb, 0);
+                        return;
+                }
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_bulk_input_recv(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        u8 address = u132->addr[endp->usb_addr].address;
+        struct u132_udev *udev = &u132->udev[address];
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                struct u132_ring *ring = endp->ring;
+                u8 *u = urb->transfer_buffer + urb->actual_length;
+                u8 *b = buf;
+                int L = len;
+                while (L-- > 0) {
+                        *u++ = *b++;
+                }
+                urb->actual_length += len;
+                if ((condition_code == TD_CC_NOERROR) &&
+                        (urb->transfer_buffer_length > urb->actual_length)) {
+                        int retval;
+                        endp->toggle_bits = toggle_bits;
+                        usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+                                1 & toggle_bits);
+                        up(&u132->scheduler_lock);
+                        retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+                                ring->number, endp, urb, address,
+                                endp->usb_endp, endp->toggle_bits,
+                                u132_hcd_bulk_input_recv);
+                        if (retval == 0) {
+                        } else
+                                u132_hcd_giveback_urb(u132, endp, urb, retval);
+                        return;
+                } else if (condition_code == TD_CC_NOERROR) {
+                        endp->toggle_bits = toggle_bits;
+                        usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+                                1 & toggle_bits);
+                        up(&u132->scheduler_lock);
+                        u132_hcd_giveback_urb(u132, endp, urb,
+                                cc_to_error[condition_code]);
+                        return;
+                } else if ((condition_code == TD_DATAUNDERRUN) &&
+                        ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0)) {
+                        endp->toggle_bits = toggle_bits;
+                        usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+                                1 & toggle_bits);
+                        up(&u132->scheduler_lock);
+                        u132_hcd_giveback_urb(u132, endp, urb, 0);
+                        return;
+                } else if (condition_code == TD_DATAUNDERRUN) {
+                        endp->toggle_bits = toggle_bits;
+                        usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+                                1 & toggle_bits);
+                        dev_warn(&u132->platform_dev->dev, "urb=%p(SHORT NOT OK"
+                                ") giving back BULK IN %s\n", urb,
+                                cc_to_text[condition_code]);
+                        up(&u132->scheduler_lock);
+                        u132_hcd_giveback_urb(u132, endp, urb, 0);
+                        return;
+                } else if (condition_code == TD_CC_STALL) {
+                        endp->toggle_bits = 0x2;
+                        usb_settoggle(udev->usb_device, endp->usb_endp, 0, 0);
+                        up(&u132->scheduler_lock);
+                        u132_hcd_giveback_urb(u132, endp, urb,
+                                cc_to_error[condition_code]);
+                        return;
+                } else {
+                        endp->toggle_bits = 0x2;
+                        usb_settoggle(udev->usb_device, endp->usb_endp, 0, 0);
+                        dev_err(&u132->platform_dev->dev, "urb=%p giving back B"
+                                "ULK IN code=%d %s\n", urb, condition_code,
+                                cc_to_text[condition_code]);
+                        up(&u132->scheduler_lock);
+                        u132_hcd_giveback_urb(u132, endp, urb,
+                                cc_to_error[condition_code]);
+                        return;
+                }
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_configure_empty_sent(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, 0);
+                return;
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_configure_input_recv(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        u8 address = u132->addr[endp->usb_addr].address;
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                struct u132_ring *ring = endp->ring;
+                u8 *u = urb->transfer_buffer;
+                u8 *b = buf;
+                int L = len;
+                while (L-- > 0) {
+                        *u++ = *b++;
+                }
+                urb->actual_length = len;
+                if ((condition_code == TD_CC_NOERROR) || ((condition_code ==
+                        TD_DATAUNDERRUN) && ((urb->transfer_flags &
+                        URB_SHORT_NOT_OK) == 0))) {
+                        int retval;
+                        up(&u132->scheduler_lock);
+                        retval = usb_ftdi_elan_edset_empty(u132->platform_dev,
+                                ring->number, endp, urb, address,
+                                endp->usb_endp, 0x3,
+                                u132_hcd_configure_empty_sent);
+                        if (retval == 0) {
+                        } else
+                                u132_hcd_giveback_urb(u132, endp, urb, retval);
+                        return;
+                } else if (condition_code == TD_CC_STALL) {
+                        up(&u132->scheduler_lock);
+                        dev_warn(&u132->platform_dev->dev, "giving back SETUP I"
+                                "NPUT STALL urb %p\n", urb);
+                        u132_hcd_giveback_urb(u132, endp, urb,
+                                cc_to_error[condition_code]);
+                        return;
+                } else {
+                        up(&u132->scheduler_lock);
+                        dev_err(&u132->platform_dev->dev, "giving back SETUP IN"
+                                "PUT %s urb %p\n", cc_to_text[condition_code],
+                                urb);
+                        u132_hcd_giveback_urb(u132, endp, urb,
+                                cc_to_error[condition_code]);
+                        return;
+                }
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_configure_empty_recv(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, 0);
+                return;
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_configure_setup_sent(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        u8 address = u132->addr[endp->usb_addr].address;
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                if (usb_pipein(urb->pipe)) {
+                        int retval;
+                        struct u132_ring *ring = endp->ring;
+                        up(&u132->scheduler_lock);
+                        retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+                                ring->number, endp, urb, address,
+                                endp->usb_endp, 0,
+                                u132_hcd_configure_input_recv);
+                        if (retval == 0) {
+                        } else
+                                u132_hcd_giveback_urb(u132, endp, urb, retval);
+                        return;
+                } else {
+                        int retval;
+                        struct u132_ring *ring = endp->ring;
+                        up(&u132->scheduler_lock);
+                        retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+                                ring->number, endp, urb, address,
+                                endp->usb_endp, 0,
+                                u132_hcd_configure_empty_recv);
+                        if (retval == 0) {
+                        } else
+                                u132_hcd_giveback_urb(u132, endp, urb, retval);
+                        return;
+                }
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_enumeration_empty_recv(void *data, struct urb *urb,
+        u8 *buf, int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        u8 address = u132->addr[endp->usb_addr].address;
+        struct u132_udev *udev = &u132->udev[address];
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                u132->addr[0].address = 0;
+                endp->usb_addr = udev->usb_addr;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, 0);
+                return;
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_enumeration_address_sent(void *data, struct urb *urb,
+        u8 *buf, int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                int retval;
+                struct u132_ring *ring = endp->ring;
+                up(&u132->scheduler_lock);
+                retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+                        ring->number, endp, urb, 0, endp->usb_endp, 0,
+                        u132_hcd_enumeration_empty_recv);
+                if (retval == 0) {
+                } else
+                        u132_hcd_giveback_urb(u132, endp, urb, retval);
+                return;
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_initial_empty_sent(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, 0);
+                return;
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_initial_input_recv(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        u8 address = u132->addr[endp->usb_addr].address;
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                int retval;
+                struct u132_ring *ring = endp->ring;
+                u8 *u = urb->transfer_buffer;
+                u8 *b = buf;
+                int L = len;
+                while (L-- > 0) {
+                        *u++ = *b++;
+                }
+                urb->actual_length = len;
+                up(&u132->scheduler_lock);
+                retval = usb_ftdi_elan_edset_empty(u132->platform_dev,
+                        ring->number, endp, urb, address, endp->usb_endp, 0x3,
+                        u132_hcd_initial_empty_sent);
+                if (retval == 0) {
+                } else
+                        u132_hcd_giveback_urb(u132, endp, urb, retval);
+                return;
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_initial_setup_sent(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        u8 address = u132->addr[endp->usb_addr].address;
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                int retval;
+                struct u132_ring *ring = endp->ring;
+                up(&u132->scheduler_lock);
+                retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+                        ring->number, endp, urb, address, endp->usb_endp, 0,
+                        u132_hcd_initial_input_recv);
+                if (retval == 0) {
+                } else
+                        u132_hcd_giveback_urb(u132, endp, urb, retval);
+                return;
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_ring_work_scheduler(void *data);
+static void u132_hcd_endp_work_scheduler(void *data);
+/*
+* this work function is only executed from the work queue
+*
+*/
+static void u132_hcd_ring_work_scheduler(void *data)
+{
+        struct u132_ring *ring = data;
+        struct u132 *u132 = ring->u132;
+        down(&u132->scheduler_lock);
+        if (ring->in_use) {
+                up(&u132->scheduler_lock);
+                u132_ring_put_kref(u132, ring);
+                return;
+        } else if (ring->curr_endp) {
+                struct u132_endp *last_endp = ring->curr_endp;
+                struct list_head *scan;
+                struct list_head *head = &last_endp->endp_ring;
+                unsigned long wakeup = 0;
+                list_for_each(scan, head) {
+                        struct u132_endp *endp = list_entry(scan,
+                                struct u132_endp, endp_ring);
+                        if (endp->queue_next == endp->queue_last) {
+                        } else if ((endp->delayed == 0)
+                                || time_after_eq(jiffies, endp->jiffies)) {
+                                ring->curr_endp = endp;
+                                u132_endp_cancel_work(u132, last_endp);
+                                u132_endp_queue_work(u132, last_endp, 0);
+                                up(&u132->scheduler_lock);
+                                u132_ring_put_kref(u132, ring);
+                                return;
+                        } else {
+                                unsigned long delta = endp->jiffies - jiffies;
+                                if (delta > wakeup)
+                                        wakeup = delta;
+                        }
+                }
+                if (last_endp->queue_next == last_endp->queue_last) {
+                } else if ((last_endp->delayed == 0) || time_after_eq(jiffies,
+                        last_endp->jiffies)) {
+                        u132_endp_cancel_work(u132, last_endp);
+                        u132_endp_queue_work(u132, last_endp, 0);
+                        up(&u132->scheduler_lock);
+                        u132_ring_put_kref(u132, ring);
+                        return;
+                } else {
+                        unsigned long delta = last_endp->jiffies - jiffies;
+                        if (delta > wakeup)
+                                wakeup = delta;
+                }
+                if (wakeup > 0) {
+                        u132_ring_requeue_work(u132, ring, wakeup);
+                        up(&u132->scheduler_lock);
+                        return;
+                } else {
+                        up(&u132->scheduler_lock);
+                        u132_ring_put_kref(u132, ring);
+                        return;
+                }
+        } else {
+                up(&u132->scheduler_lock);
+                u132_ring_put_kref(u132, ring);
+                return;
+        }
+}
+
+static void u132_hcd_endp_work_scheduler(void *data)
+{
+        struct u132_ring *ring;
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        down(&u132->scheduler_lock);
+        ring = endp->ring;
+        if (endp->edset_flush) {
+                endp->edset_flush = 0;
+                if (endp->dequeueing)
+                        usb_ftdi_elan_edset_flush(u132->platform_dev,
+                                ring->number, endp);
+                up(&u132->scheduler_lock);
+                u132_endp_put_kref(u132, endp);
+                return;
+        } else if (endp->active) {
+                up(&u132->scheduler_lock);
+                u132_endp_put_kref(u132, endp);
+                return;
+        } else if (ring->in_use) {
+                up(&u132->scheduler_lock);
+                u132_endp_put_kref(u132, endp);
+                return;
+        } else if (endp->queue_next == endp->queue_last) {
+                up(&u132->scheduler_lock);
+                u132_endp_put_kref(u132, endp);
+                return;
+        } else if (endp->pipetype == PIPE_INTERRUPT) {
+                u8 address = u132->addr[endp->usb_addr].address;
+                if (ring->in_use) {
+                        up(&u132->scheduler_lock);
+                        u132_endp_put_kref(u132, endp);
+                        return;
+                } else {
+                        int retval;
+                        struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+                                endp->queue_next];
+                        endp->active = 1;
+                        ring->curr_endp = endp;
+                        ring->in_use = 1;
+                        up(&u132->scheduler_lock);
+                        retval = edset_single(u132, ring, endp, urb, address,
+                                endp->toggle_bits, u132_hcd_interrupt_recv);
+                        if (retval == 0) {
+                        } else
+                                u132_hcd_giveback_urb(u132, endp, urb, retval);
+                        return;
+                }
+        } else if (endp->pipetype == PIPE_CONTROL) {
+                u8 address = u132->addr[endp->usb_addr].address;
+                if (ring->in_use) {
+                        up(&u132->scheduler_lock);
+                        u132_endp_put_kref(u132, endp);
+                        return;
+                } else if (address == 0) {
+                        int retval;
+                        struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+                                endp->queue_next];
+                        endp->active = 1;
+                        ring->curr_endp = endp;
+                        ring->in_use = 1;
+                        up(&u132->scheduler_lock);
+                        retval = edset_setup(u132, ring, endp, urb, address,
+                                0x2, u132_hcd_initial_setup_sent);
+                        if (retval == 0) {
+                        } else
+                                u132_hcd_giveback_urb(u132, endp, urb, retval);
+                        return;
+                } else if (endp->usb_addr == 0) {
+                        int retval;
+                        struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+                                endp->queue_next];
+                        endp->active = 1;
+                        ring->curr_endp = endp;
+                        ring->in_use = 1;
+                        up(&u132->scheduler_lock);
+                        retval = edset_setup(u132, ring, endp, urb, 0, 0x2,
+                                u132_hcd_enumeration_address_sent);
+                        if (retval == 0) {
+                        } else
+                                u132_hcd_giveback_urb(u132, endp, urb, retval);
+                        return;
+                } else {
+                        int retval;
+                        u8 address = u132->addr[endp->usb_addr].address;
+                        struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+                                endp->queue_next];
+                        endp->active = 1;
+                        ring->curr_endp = endp;
+                        ring->in_use = 1;
+                        up(&u132->scheduler_lock);
+                        retval = edset_setup(u132, ring, endp, urb, address,
+                                0x2, u132_hcd_configure_setup_sent);
+                        if (retval == 0) {
+                        } else
+                                u132_hcd_giveback_urb(u132, endp, urb, retval);
+                        return;
+                }
+        } else {
+                if (endp->input) {
+                        u8 address = u132->addr[endp->usb_addr].address;
+                        if (ring->in_use) {
+                                up(&u132->scheduler_lock);
+                                u132_endp_put_kref(u132, endp);
+                                return;
+                        } else {
+                                int retval;
+                                struct urb *urb = endp->urb_list[
+                                        ENDP_QUEUE_MASK & endp->queue_next];
+                                endp->active = 1;
+                                ring->curr_endp = endp;
+                                ring->in_use = 1;
+                                up(&u132->scheduler_lock);
+                                retval = edset_input(u132, ring, endp, urb,
+                                        address, endp->toggle_bits,
+                                        u132_hcd_bulk_input_recv);
+                                if (retval == 0) {
+                                } else
+                                        u132_hcd_giveback_urb(u132, endp, urb,
+                                                retval);
+                                return;
+                        }
+                } else {        /* output pipe */
+                        u8 address = u132->addr[endp->usb_addr].address;
+                        if (ring->in_use) {
+                                up(&u132->scheduler_lock);
+                                u132_endp_put_kref(u132, endp);
+                                return;
+                        } else {
+                                int retval;
+                                struct urb *urb = endp->urb_list[
+                                        ENDP_QUEUE_MASK & endp->queue_next];
+                                endp->active = 1;
+                                ring->curr_endp = endp;
+                                ring->in_use = 1;
+                                up(&u132->scheduler_lock);
+                                retval = edset_output(u132, ring, endp, urb,
+                                        address, endp->toggle_bits,
+                                        u132_hcd_bulk_output_sent);
+                                if (retval == 0) {
+                                } else
+                                        u132_hcd_giveback_urb(u132, endp, urb,
+                                                retval);
+                                return;
+                        }
+                }
+        }
+}
+
+static void port_power(struct u132 *u132, int pn, int is_on)
+{
+        u132->port[pn].power = is_on;
+}
+
+static void u132_power(struct u132 *u132, int is_on)
+{
+        struct usb_hcd *hcd = u132_to_hcd(u132)
+                ;        /* hub is inactive unless the port is powered */
+        if (is_on) {
+                if (u132->power)
+                        return;
+                u132->power = 1;
+                hcd->self.controller->power.power_state = PMSG_ON;
+        } else {
+                u132->power = 0;
+                hcd->state = HC_STATE_HALT;
+                hcd->self.controller->power.power_state = PMSG_SUSPEND;
+        }
+}
+
+static int u132_periodic_reinit(struct u132 *u132)
+{
+        int retval;
+        u32 fi = u132->hc_fminterval & 0x03fff;
+        u32 fit;
+        u32 fminterval;
+        retval = u132_read_pcimem(u132, fminterval, &fminterval);
+        if (retval)
+                return retval;
+        fit = fminterval & FIT;
+        retval = u132_write_pcimem(u132, fminterval,
+                (fit ^ FIT) | u132->hc_fminterval);
+        if (retval)
+                return retval;
+        retval = u132_write_pcimem(u132, periodicstart,
+                ((9 *fi) / 10) & 0x3fff);
+        if (retval)
+                return retval;
+        return 0;
+}
+
+static char *hcfs2string(int state)
+{
+        switch (state) {
+        case OHCI_USB_RESET:
+                return "reset";
+        case OHCI_USB_RESUME:
+                return "resume";
+        case OHCI_USB_OPER:
+                return "operational";
+        case OHCI_USB_SUSPEND:
+                return "suspend";
+        }
+        return "?";
+}
+
+static int u132_usb_reset(struct u132 *u132)
+{
+        int retval;
+        retval = u132_read_pcimem(u132, control, &u132->hc_control);
+        if (retval)
+                return retval;
+        u132->hc_control &= OHCI_CTRL_RWC;
+        retval = u132_write_pcimem(u132, control, u132->hc_control);
+        if (retval)
+                return retval;
+        return 0;
+}
+
+static int u132_init(struct u132 *u132)
+{
+        int retval;
+        u32 control;
+        u132_disable(u132);
+        u132->next_statechange =
+                jiffies; /* SMM owns the HC? not for long! */  {
+                u32 control;
+                retval = u132_read_pcimem(u132, control, &control);
+                if (retval)
+                        return retval;
+                if (control & OHCI_CTRL_IR) {
+                        u32 temp = 50;
+                        retval = u132_write_pcimem(u132, intrenable,
+                                OHCI_INTR_OC);
+                        if (retval)
+                                return retval;
+                        retval = u132_write_pcimem_byte(u132, cmdstatus,
+                                OHCI_OCR);
+                        if (retval)
+                                return retval;
+                      check:{
+                                retval = u132_read_pcimem(u132, control,
+                                        &control);
+                                if (retval)
+                                        return retval;
+                        }
+                        if (control & OHCI_CTRL_IR) {
+                                msleep(10);
+                                if (--temp == 0) {
+                                        dev_err(&u132->platform_dev->dev, "USB "
+                                                "HC takeover failed!(BIOS/SMM b"
+                                                "ug) control=%08X\n", control);
+                                        return -EBUSY;
+                                }
+                                goto check;
+                        }
+                        u132_usb_reset(u132);
+                }
+        }
+        retval = u132_write_pcimem(u132, intrdisable, OHCI_INTR_MIE);
+        if (retval)
+                return retval;
+        retval = u132_read_pcimem(u132, control, &control);
+        if (retval)
+                return retval;
+        if (u132->num_ports == 0) {
+                u32 rh_a = -1;
+                retval = u132_read_pcimem(u132, roothub.a, &rh_a);
+                if (retval)
+                        return retval;
+                u132->num_ports = rh_a & RH_A_NDP;
+                retval = read_roothub_info(u132);
+                if (retval)
+                        return retval;
+        }
+        if (u132->num_ports > MAX_U132_PORTS) {
+                return -EINVAL;
+        }
+        return 0;
+}
+
+
+/* Start an OHCI controller, set the BUS operational
+* resets USB and controller
+* enable interrupts
+*/
+static int u132_run(struct u132 *u132)
+{
+        int retval;
+        u32 control;
+        u32 status;
+        u32 fminterval;
+        u32 periodicstart;
+        u32 cmdstatus;
+        u32 roothub_a;
+        int mask = OHCI_INTR_INIT;
+        int first = u132->hc_fminterval == 0;
+        int sleep_time = 0;
+        int reset_timeout = 30;        /* ... allow extra time */
+        u132_disable(u132);
+        if (first) {
+                u32 temp;
+                retval = u132_read_pcimem(u132, fminterval, &temp);
+                if (retval)
+                        return retval;
+                u132->hc_fminterval = temp & 0x3fff;
+                if (u132->hc_fminterval != FI) {
+                }
+                u132->hc_fminterval |= FSMP(u132->hc_fminterval) << 16;
+        }
+        retval = u132_read_pcimem(u132, control, &u132->hc_control);
+        if (retval)
+                return retval;
+        dev_info(&u132->platform_dev->dev, "resetting from state '%s', control "
+                "= %08X\n", hcfs2string(u132->hc_control & OHCI_CTRL_HCFS),
+                u132->hc_control);
+        switch (u132->hc_control & OHCI_CTRL_HCFS) {
+        case OHCI_USB_OPER:
+                sleep_time = 0;
+                break;
+        case OHCI_USB_SUSPEND:
+        case OHCI_USB_RESUME:
+                u132->hc_control &= OHCI_CTRL_RWC;
+                u132->hc_control |= OHCI_USB_RESUME;
+                sleep_time = 10;
+                break;
+        default:
+                u132->hc_control &= OHCI_CTRL_RWC;
+                u132->hc_control |= OHCI_USB_RESET;
+                sleep_time = 50;
+                break;
+        }
+        retval = u132_write_pcimem(u132, control, u132->hc_control);
+        if (retval)
+                return retval;
+        retval = u132_read_pcimem(u132, control, &control);
+        if (retval)
+                return retval;
+        msleep(sleep_time);
+        retval = u132_read_pcimem(u132, roothub.a, &roothub_a);
+        if (retval)
+                return retval;
+        if (!(roothub_a & RH_A_NPS)) {
+                int temp;        /* power down each port */
+                for (temp = 0; temp < u132->num_ports; temp++) {
+                        retval = u132_write_pcimem(u132,
+                                roothub.portstatus[temp], RH_PS_LSDA);
+                        if (retval)
+                                return retval;
+                }
+        }
+        retval = u132_read_pcimem(u132, control, &control);
+        if (retval)
+                return retval;
+      retry:retval = u132_read_pcimem(u132, cmdstatus, &status);
+        if (retval)
+                return retval;
+        retval = u132_write_pcimem_byte(u132, cmdstatus, OHCI_HCR);
+        if (retval)
+                return retval;
+      extra:{
+                retval = u132_read_pcimem(u132, cmdstatus, &status);
+                if (retval)
+                        return retval;
+                if (0 != (status & OHCI_HCR)) {
+                        if (--reset_timeout == 0) {
+                                dev_err(&u132->platform_dev->dev, "USB HC reset"
+                                        " timed out!\n");
+                                return -ENODEV;
+                        } else {
+                                msleep(5);
+                                goto extra;
+                        }
+                }
+        }
+        if (u132->flags & OHCI_QUIRK_INITRESET) {
+                retval = u132_write_pcimem(u132, control, u132->hc_control);
+                if (retval)
+                        return retval;
+                retval = u132_read_pcimem(u132, control, &control);
+                if (retval)
+                        return retval;
+        }
+        retval = u132_write_pcimem(u132, ed_controlhead, 0x00000000);
+        if (retval)
+                return retval;
+        retval = u132_write_pcimem(u132, ed_bulkhead, 0x11000000);
+        if (retval)
+                return retval;
+        retval = u132_write_pcimem(u132, hcca, 0x00000000);
+        if (retval)
+                return retval;
+        retval = u132_periodic_reinit(u132);
+        if (retval)
+                return retval;
+        retval = u132_read_pcimem(u132, fminterval, &fminterval);
+        if (retval)
+                return retval;
+        retval = u132_read_pcimem(u132, periodicstart, &periodicstart);
+        if (retval)
+                return retval;
+        if (0 == (fminterval & 0x3fff0000) || 0 == periodicstart) {
+                if (!(u132->flags & OHCI_QUIRK_INITRESET)) {
+                        u132->flags |= OHCI_QUIRK_INITRESET;
+                        goto retry;
+                } else
+                        dev_err(&u132->platform_dev->dev, "init err(%08x %04x)"
+                                "\n", fminterval, periodicstart);
+        }                        /* start controller operations */
+        u132->hc_control &= OHCI_CTRL_RWC;
+        u132->hc_control |= OHCI_CONTROL_INIT | OHCI_CTRL_BLE | OHCI_USB_OPER;
+        retval = u132_write_pcimem(u132, control, u132->hc_control);
+        if (retval)
+                return retval;
+        retval = u132_write_pcimem_byte(u132, cmdstatus, OHCI_BLF);
+        if (retval)
+                return retval;
+        retval = u132_read_pcimem(u132, cmdstatus, &cmdstatus);
+        if (retval)
+                return retval;
+        retval = u132_read_pcimem(u132, control, &control);
+        if (retval)
+                return retval;
+        u132_to_hcd(u132)->state = HC_STATE_RUNNING;
+        retval = u132_write_pcimem(u132, roothub.status, RH_HS_DRWE);
+        if (retval)
+                return retval;
+        retval = u132_write_pcimem(u132, intrstatus, mask);
+        if (retval)
+                return retval;
+        retval = u132_write_pcimem(u132, intrdisable,
+                OHCI_INTR_MIE | OHCI_INTR_OC | OHCI_INTR_RHSC | OHCI_INTR_FNO |
+                OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_SF | OHCI_INTR_WDH |
+                OHCI_INTR_SO);
+        if (retval)
+                return retval;        /* handle root hub init quirks ... */
+        retval = u132_read_pcimem(u132, roothub.a, &roothub_a);
+        if (retval)
+                return retval;
+        roothub_a &= ~(RH_A_PSM | RH_A_OCPM);
+        if (u132->flags & OHCI_QUIRK_SUPERIO) {
+                roothub_a |= RH_A_NOCP;
+                roothub_a &= ~(RH_A_POTPGT | RH_A_NPS);
+                retval = u132_write_pcimem(u132, roothub.a, roothub_a);
+                if (retval)
+                        return retval;
+        } else if ((u132->flags & OHCI_QUIRK_AMD756) || distrust_firmware) {
+                roothub_a |= RH_A_NPS;
+                retval = u132_write_pcimem(u132, roothub.a, roothub_a);
+                if (retval)
+                        return retval;
+        }
+        retval = u132_write_pcimem(u132, roothub.status, RH_HS_LPSC);
+        if (retval)
+                return retval;
+        retval = u132_write_pcimem(u132, roothub.b,
+                (roothub_a & RH_A_NPS) ? 0 : RH_B_PPCM);
+        if (retval)
+                return retval;
+        retval = u132_read_pcimem(u132, control, &control);
+        if (retval)
+                return retval;
+        mdelay((roothub_a >> 23) & 0x1fe);
+        u132_to_hcd(u132)->state = HC_STATE_RUNNING;
+        return 0;
+}
+
+static void u132_hcd_stop(struct usb_hcd *hcd)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device hcd=%p is being remov"
+                        "ed\n", hcd);
+        } else {
+                down(&u132->sw_lock);
+                msleep(100);
+                u132_power(u132, 0);
+                up(&u132->sw_lock);
+        }
+}
+
+static int u132_hcd_start(struct usb_hcd *hcd)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else if (hcd->self.controller) {
+                int retval;
+                struct platform_device *pdev =
+                        to_platform_device(hcd->self.controller);
+                u16 vendor = ((struct u132_platform_data *)
+                        (pdev->dev.platform_data))->vendor;
+                u16 device = ((struct u132_platform_data *)
+                        (pdev->dev.platform_data))->device;
+                down(&u132->sw_lock);
+                msleep(10);
+                if (vendor == PCI_VENDOR_ID_AMD && device == 0x740c) {
+                        u132->flags = OHCI_QUIRK_AMD756;
+                } else if (vendor == PCI_VENDOR_ID_OPTI && device == 0xc861) {
+                        dev_err(&u132->platform_dev->dev, "WARNING: OPTi workar"
+                                "ounds unavailable\n");
+                } else if (vendor == PCI_VENDOR_ID_COMPAQ && device == 0xa0f8)
+                        u132->flags |= OHCI_QUIRK_ZFMICRO;
+                retval = u132_run(u132);
+                if (retval) {
+                        u132_disable(u132);
+                        u132->going = 1;
+                }
+                msleep(100);
+                up(&u132->sw_lock);
+                return retval;
+        } else {
+                dev_err(&u132->platform_dev->dev, "platform_device missing\n");
+                return -ENODEV;
+        }
+}
+
+static int u132_hcd_reset(struct usb_hcd *hcd)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else {
+                int retval;
+                down(&u132->sw_lock);
+                retval = u132_init(u132);
+                if (retval) {
+                        u132_disable(u132);
+                        u132->going = 1;
+                }
+                up(&u132->sw_lock);
+                return retval;
+        }
+}
+
+static int create_endpoint_and_queue_int(struct u132 *u132,
+        struct u132_udev *udev, struct usb_host_endpoint *hep, struct urb *urb,
+        struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp, u8 address,
+        gfp_t mem_flags)
+{
+        struct u132_ring *ring;
+        unsigned long irqs;
+        u8 endp_number = ++u132->num_endpoints;
+        struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] =
+                kmalloc(sizeof(struct u132_endp), mem_flags);
+        if (!endp) {
+                return -ENOMEM;
+        }
+        INIT_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler, (void *)endp);
+        spin_lock_init(&endp->queue_lock.slock);
+        INIT_LIST_HEAD(&endp->urb_more);
+        ring = endp->ring = &u132->ring[0];
+        if (ring->curr_endp) {
+                list_add_tail(&endp->endp_ring, &ring->curr_endp->endp_ring);
+        } else {
+                INIT_LIST_HEAD(&endp->endp_ring);
+                ring->curr_endp = endp;
+        }
+        ring->length += 1;
+        endp->dequeueing = 0;
+        endp->edset_flush = 0;
+        endp->active = 0;
+        endp->delayed = 0;
+        endp->endp_number = endp_number;
+        endp->u132 = u132;
+        endp->hep = hep;
+        endp->pipetype = usb_pipetype(urb->pipe);
+        u132_endp_init_kref(u132, endp);
+        if (usb_pipein(urb->pipe)) {
+                endp->toggle_bits = 0x2;
+                usb_settoggle(udev->usb_device, usb_endp, 0, 0);
+                endp->input = 1;
+                endp->output = 0;
+                udev->endp_number_in[usb_endp] = endp_number;
+                u132_udev_get_kref(u132, udev);
+        } else {
+                endp->toggle_bits = 0x2;
+                usb_settoggle(udev->usb_device, usb_endp, 1, 0);
+                endp->input = 0;
+                endp->output = 1;
+                udev->endp_number_out[usb_endp] = endp_number;
+                u132_udev_get_kref(u132, udev);
+        }
+        urb->hcpriv = u132;
+        spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+        endp->delayed = 1;
+        endp->jiffies = jiffies + msecs_to_jiffies(urb->interval);
+        endp->udev_number = address;
+        endp->usb_addr = usb_addr;
+        endp->usb_endp = usb_endp;
+        endp->queue_size = 1;
+        endp->queue_last = 0;
+        endp->queue_next = 0;
+        endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+        spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+        u132_endp_queue_work(u132, endp, msecs_to_jiffies(urb->interval));
+        return 0;
+}
+
+static int queue_int_on_old_endpoint(struct u132 *u132, struct u132_udev *udev,
+        struct usb_host_endpoint *hep, struct urb *urb,
+        struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
+        u8 usb_endp, u8 address)
+{
+        urb->hcpriv = u132;
+        endp->delayed = 1;
+        endp->jiffies = jiffies + msecs_to_jiffies(urb->interval);
+        if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+                endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+        } else {
+                struct u132_urbq *urbq = kmalloc(sizeof(struct u132_urbq),
+                        GFP_ATOMIC);
+                if (urbq == NULL) {
+                        endp->queue_size -= 1;
+                        return -ENOMEM;
+                } else {
+                        list_add_tail(&urbq->urb_more, &endp->urb_more);
+                        urbq->urb = urb;
+                }
+        }
+        return 0;
+}
+
+static int create_endpoint_and_queue_bulk(struct u132 *u132,
+        struct u132_udev *udev, struct usb_host_endpoint *hep, struct urb *urb,
+        struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp, u8 address,
+        gfp_t mem_flags)
+{
+        int ring_number;
+        struct u132_ring *ring;
+        unsigned long irqs;
+        u8 endp_number = ++u132->num_endpoints;
+        struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] =
+                kmalloc(sizeof(struct u132_endp), mem_flags);
+        if (!endp) {
+                return -ENOMEM;
+        }
+        INIT_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler, (void *)endp);
+        spin_lock_init(&endp->queue_lock.slock);
+        INIT_LIST_HEAD(&endp->urb_more);
+        endp->dequeueing = 0;
+        endp->edset_flush = 0;
+        endp->active = 0;
+        endp->delayed = 0;
+        endp->endp_number = endp_number;
+        endp->u132 = u132;
+        endp->hep = hep;
+        endp->pipetype = usb_pipetype(urb->pipe);
+        u132_endp_init_kref(u132, endp);
+        if (usb_pipein(urb->pipe)) {
+                endp->toggle_bits = 0x2;
+                usb_settoggle(udev->usb_device, usb_endp, 0, 0);
+                ring_number = 3;
+                endp->input = 1;
+                endp->output = 0;
+                udev->endp_number_in[usb_endp] = endp_number;
+                u132_udev_get_kref(u132, udev);
+        } else {
+                endp->toggle_bits = 0x2;
+                usb_settoggle(udev->usb_device, usb_endp, 1, 0);
+                ring_number = 2;
+                endp->input = 0;
+                endp->output = 1;
+                udev->endp_number_out[usb_endp] = endp_number;
+                u132_udev_get_kref(u132, udev);
+        }
+        ring = endp->ring = &u132->ring[ring_number - 1];
+        if (ring->curr_endp) {
+                list_add_tail(&endp->endp_ring, &ring->curr_endp->endp_ring);
+        } else {
+                INIT_LIST_HEAD(&endp->endp_ring);
+                ring->curr_endp = endp;
+        }
+        ring->length += 1;
+        urb->hcpriv = u132;
+        spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+        endp->udev_number = address;
+        endp->usb_addr = usb_addr;
+        endp->usb_endp = usb_endp;
+        endp->queue_size = 1;
+        endp->queue_last = 0;
+        endp->queue_next = 0;
+        endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+        spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+        u132_endp_queue_work(u132, endp, 0);
+        return 0;
+}
+
+static int queue_bulk_on_old_endpoint(struct u132 *u132, struct u132_udev *udev,
+         struct usb_host_endpoint *hep, struct urb *urb,
+        struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
+        u8 usb_endp, u8 address)
+{
+        urb->hcpriv = u132;
+        if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+                endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+        } else {
+                struct u132_urbq *urbq = kmalloc(sizeof(struct u132_urbq),
+                        GFP_ATOMIC);
+                if (urbq == NULL) {
+                        endp->queue_size -= 1;
+                        return -ENOMEM;
+                } else {
+                        list_add_tail(&urbq->urb_more, &endp->urb_more);
+                        urbq->urb = urb;
+                }
+        }
+        return 0;
+}
+
+static int create_endpoint_and_queue_control(struct u132 *u132,
+        struct usb_host_endpoint *hep, struct urb *urb,
+        struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp,
+        gfp_t mem_flags)
+{
+        struct u132_ring *ring;
+        u8 endp_number = ++u132->num_endpoints;
+        struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] =
+                kmalloc(sizeof(struct u132_endp), mem_flags);
+        if (!endp) {
+                return -ENOMEM;
+        }
+        INIT_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler, (void *)endp);
+        spin_lock_init(&endp->queue_lock.slock);
+        INIT_LIST_HEAD(&endp->urb_more);
+        ring = endp->ring = &u132->ring[0];
+        if (ring->curr_endp) {
+                list_add_tail(&endp->endp_ring, &ring->curr_endp->endp_ring);
+        } else {
+                INIT_LIST_HEAD(&endp->endp_ring);
+                ring->curr_endp = endp;
+        }
+        ring->length += 1;
+        endp->dequeueing = 0;
+        endp->edset_flush = 0;
+        endp->active = 0;
+        endp->delayed = 0;
+        endp->endp_number = endp_number;
+        endp->u132 = u132;
+        endp->hep = hep;
+        u132_endp_init_kref(u132, endp);
+        u132_endp_get_kref(u132, endp);
+        if (usb_addr == 0) {
+                unsigned long irqs;
+                u8 address = u132->addr[usb_addr].address;
+                struct u132_udev *udev = &u132->udev[address];
+                endp->udev_number = address;
+                endp->usb_addr = usb_addr;
+                endp->usb_endp = usb_endp;
+                endp->input = 1;
+                endp->output = 1;
+                endp->pipetype = usb_pipetype(urb->pipe);
+                u132_udev_init_kref(u132, udev);
+                u132_udev_get_kref(u132, udev);
+                udev->endp_number_in[usb_endp] = endp_number;
+                udev->endp_number_out[usb_endp] = endp_number;
+                urb->hcpriv = u132;
+                spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+                endp->queue_size = 1;
+                endp->queue_last = 0;
+                endp->queue_next = 0;
+                endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+                u132_endp_queue_work(u132, endp, 0);
+                return 0;
+        } else {                /*(usb_addr > 0) */
+                unsigned long irqs;
+                u8 address = u132->addr[usb_addr].address;
+                struct u132_udev *udev = &u132->udev[address];
+                endp->udev_number = address;
+                endp->usb_addr = usb_addr;
+                endp->usb_endp = usb_endp;
+                endp->input = 1;
+                endp->output = 1;
+                endp->pipetype = usb_pipetype(urb->pipe);
+                u132_udev_get_kref(u132, udev);
+                udev->enumeration = 2;
+                udev->endp_number_in[usb_endp] = endp_number;
+                udev->endp_number_out[usb_endp] = endp_number;
+                urb->hcpriv = u132;
+                spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+                endp->queue_size = 1;
+                endp->queue_last = 0;
+                endp->queue_next = 0;
+                endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+                u132_endp_queue_work(u132, endp, 0);
+                return 0;
+        }
+}
+
+static int queue_control_on_old_endpoint(struct u132 *u132,
+        struct usb_host_endpoint *hep, struct urb *urb,
+        struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
+        u8 usb_endp)
+{
+        if (usb_addr == 0) {
+                if (usb_pipein(urb->pipe)) {
+                        urb->hcpriv = u132;
+                        if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+                                endp->urb_list[ENDP_QUEUE_MASK &
+                                        endp->queue_last++] = urb;
+                        } else {
+                                struct u132_urbq *urbq =
+                                        kmalloc(sizeof(struct u132_urbq),
+                                        GFP_ATOMIC);
+                                if (urbq == NULL) {
+                                        endp->queue_size -= 1;
+                                        return -ENOMEM;
+                                } else {
+                                        list_add_tail(&urbq->urb_more,
+                                                &endp->urb_more);
+                                        urbq->urb = urb;
+                                }
+                        }
+                        return 0;
+                } else {        /* usb_pipeout(urb->pipe) */
+                        struct u132_addr *addr = &u132->addr[usb_dev->devnum];
+                        int I = MAX_U132_UDEVS;
+                        int i = 0;
+                        while (--I > 0) {
+                                struct u132_udev *udev = &u132->udev[++i];
+                                if (udev->usb_device) {
+                                        continue;
+                                } else {
+                                        udev->enumeration = 1;
+                                        u132->addr[0].address = i;
+                                        endp->udev_number = i;
+                                        udev->udev_number = i;
+                                        udev->usb_addr = usb_dev->devnum;
+                                        u132_udev_init_kref(u132, udev);
+                                        udev->endp_number_in[usb_endp] =
+                                                endp->endp_number;
+                                        u132_udev_get_kref(u132, udev);
+                                        udev->endp_number_out[usb_endp] =
+                                                endp->endp_number;
+                                        udev->usb_device = usb_dev;
+                                        ((u8 *) (urb->setup_packet))[2] =
+                                                addr->address = i;
+                                        u132_udev_get_kref(u132, udev);
+                                        break;
+                                }
+                        }
+                        if (I == 0) {
+                                dev_err(&u132->platform_dev->dev, "run out of d"
+                                        "evice space\n");
+                                return -EINVAL;
+                        }
+                        urb->hcpriv = u132;
+                        if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+                                endp->urb_list[ENDP_QUEUE_MASK &
+                                        endp->queue_last++] = urb;
+                        } else {
+                                struct u132_urbq *urbq =
+                                        kmalloc(sizeof(struct u132_urbq),
+                                        GFP_ATOMIC);
+                                if (urbq == NULL) {
+                                        endp->queue_size -= 1;
+                                        return -ENOMEM;
+                                } else {
+                                        list_add_tail(&urbq->urb_more,
+                                                &endp->urb_more);
+                                        urbq->urb = urb;
+                                }
+                        }
+                        return 0;
+                }
+        } else {                /*(usb_addr > 0) */
+                u8 address = u132->addr[usb_addr].address;
+                struct u132_udev *udev = &u132->udev[address];
+                urb->hcpriv = u132;
+                if (udev->enumeration == 2) {
+                } else
+                        udev->enumeration = 2;
+                if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+                        endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] =
+                                urb;
+                } else {
+                        struct u132_urbq *urbq =
+                                kmalloc(sizeof(struct u132_urbq), GFP_ATOMIC);
+                        if (urbq == NULL) {
+                                endp->queue_size -= 1;
+                                return -ENOMEM;
+                        } else {
+                                list_add_tail(&urbq->urb_more, &endp->urb_more);
+                                urbq->urb = urb;
+                        }
+                }
+                return 0;
+        }
+}
+
+static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep,
+        struct urb *urb, gfp_t mem_flags)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (irqs_disabled()) {
+                if (__GFP_WAIT & mem_flags) {
+                        printk(KERN_ERR "invalid context for function that migh"
+                                "t sleep\n");
+                        return -EINVAL;
+                }
+        }
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                return -ESHUTDOWN;
+        } else {
+                u8 usb_addr = usb_pipedevice(urb->pipe);
+                u8 usb_endp = usb_pipeendpoint(urb->pipe);
+                struct usb_device *usb_dev = urb->dev;
+                if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+                        u8 address = u132->addr[usb_addr].address;
+                        struct u132_udev *udev = &u132->udev[address];
+                        struct u132_endp *endp = hep->hcpriv;
+                        urb->actual_length = 0;
+                        if (endp) {
+                                unsigned long irqs;
+                                int retval;
+                                spin_lock_irqsave(&endp->queue_lock.slock,
+                                        irqs);
+                                retval = queue_int_on_old_endpoint(u132, udev,
+                                        hep, urb, usb_dev, endp, usb_addr,
+                                        usb_endp, address);
+                                spin_unlock_irqrestore(&endp->queue_lock.slock,
+                                        irqs);
+                                if (retval) {
+                                        return retval;
+                                } else {
+                                        u132_endp_queue_work(u132, endp,
+                                                msecs_to_jiffies(urb->interval))
+                                                ;
+                                        return 0;
+                                }
+                        } else if (u132->num_endpoints == MAX_U132_ENDPS) {
+                                return -EINVAL;
+                        } else {        /*(endp == NULL) */
+                                return create_endpoint_and_queue_int(u132, udev,
+                                         hep, urb, usb_dev, usb_addr, usb_endp,
+                                        address, mem_flags);
+                        }
+                } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+                        dev_err(&u132->platform_dev->dev, "the hardware does no"
+                                "t support PIPE_ISOCHRONOUS\n");
+                        return -EINVAL;
+                } else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+                        u8 address = u132->addr[usb_addr].address;
+                        struct u132_udev *udev = &u132->udev[address];
+                        struct u132_endp *endp = hep->hcpriv;
+                        urb->actual_length = 0;
+                        if (endp) {
+                                unsigned long irqs;
+                                int retval;
+                                spin_lock_irqsave(&endp->queue_lock.slock,
+                                        irqs);
+                                retval = queue_bulk_on_old_endpoint(u132, udev,
+                                        hep, urb, usb_dev, endp, usb_addr,
+                                        usb_endp, address);
+                                spin_unlock_irqrestore(&endp->queue_lock.slock,
+                                        irqs);
+                                if (retval) {
+                                        return retval;
+                                } else {
+                                        u132_endp_queue_work(u132, endp, 0);
+                                        return 0;
+                                }
+                        } else if (u132->num_endpoints == MAX_U132_ENDPS) {
+                                return -EINVAL;
+                        } else
+                                return create_endpoint_and_queue_bulk(u132,
+                                        udev, hep, urb, usb_dev, usb_addr,
+                                        usb_endp, address, mem_flags);
+                } else {
+                        struct u132_endp *endp = hep->hcpriv;
+                        u16 urb_size = 8;
+                        u8 *b = urb->setup_packet;
+                        int i = 0;
+                        char data[30 *3 + 4];
+                        char *d = data;
+                        int m = (sizeof(data) - 1) / 3;
+                        int l = 0;
+                        data[0] = 0;
+                        while (urb_size-- > 0) {
+                                if (i > m) {
+                                } else if (i++ < m) {
+                                        int w = sprintf(d, " %02X", *b++);
+                                        d += w;
+                                        l += w;
+                                } else
+                                        d += sprintf(d, " ..");
+                        }
+                        if (endp) {
+                                unsigned long irqs;
+                                int retval;
+                                spin_lock_irqsave(&endp->queue_lock.slock,
+                                        irqs);
+                                retval = queue_control_on_old_endpoint(u132,
+                                        hep, urb, usb_dev, endp, usb_addr,
+                                        usb_endp);
+                                spin_unlock_irqrestore(&endp->queue_lock.slock,
+                                        irqs);
+                                if (retval) {
+                                        return retval;
+                                } else {
+                                        u132_endp_queue_work(u132, endp, 0);
+                                        return 0;
+                                }
+                        } else if (u132->num_endpoints == MAX_U132_ENDPS) {
+                                return -EINVAL;
+                        } else
+                                return create_endpoint_and_queue_control(u132,
+                                        hep, urb, usb_dev, usb_addr, usb_endp,
+                                        mem_flags);
+                }
+        }
+}
+
+static int dequeue_from_overflow_chain(struct u132 *u132,
+        struct u132_endp *endp, struct urb *urb)
+{
+        struct list_head *scan;
+        struct list_head *head = &endp->urb_more;
+        list_for_each(scan, head) {
+                struct u132_urbq *urbq = list_entry(scan, struct u132_urbq,
+                        urb_more);
+                if (urbq->urb == urb) {
+                        struct usb_hcd *hcd = u132_to_hcd(u132);
+                        list_del(scan);
+                        endp->queue_size -= 1;
+                        urb->error_count = 0;
+                        urb->hcpriv = NULL;
+                        usb_hcd_giveback_urb(hcd, urb, NULL);
+                        return 0;
+                } else
+                        continue;
+        }
+        dev_err(&u132->platform_dev->dev, "urb=%p not found in endp[%d]=%p ring"
+                "[%d] %c%c usb_endp=%d usb_addr=%d size=%d next=%04X last=%04X"
+                "\n", urb, endp->endp_number, endp, endp->ring->number,
+                endp->input ? 'I' : ' ', endp->output ? 'O' : ' ',
+                endp->usb_endp, endp->usb_addr, endp->queue_size,
+                endp->queue_next, endp->queue_last);
+        return -EINVAL;
+}
+
+static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp,
+        struct urb *urb)
+{
+        unsigned long irqs;
+        spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+        if (endp->queue_size == 0) {
+                dev_err(&u132->platform_dev->dev, "urb=%p not found in endp[%d]"
+                        "=%p ring[%d] %c%c usb_endp=%d usb_addr=%d\n", urb,
+                        endp->endp_number, endp, endp->ring->number,
+                        endp->input ? 'I' : ' ', endp->output ? 'O' : ' ',
+                        endp->usb_endp, endp->usb_addr);
+                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+                return -EINVAL;
+        }
+        if (urb == endp->urb_list[ENDP_QUEUE_MASK & endp->queue_next]) {
+                if (endp->active) {
+                        endp->dequeueing = 1;
+                        endp->edset_flush = 1;
+                        u132_endp_queue_work(u132, endp, 0);
+                        spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+                        urb->hcpriv = NULL;
+                        return 0;
+                } else {
+                        spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+                        u132_hcd_abandon_urb(u132, endp, urb, urb->status);
+                        return 0;
+                }
+        } else {
+                u16 queue_list = 0;
+                u16 queue_size = endp->queue_size;
+                u16 queue_scan = endp->queue_next;
+                struct urb **urb_slot = NULL;
+                while (++queue_list < ENDP_QUEUE_SIZE && --queue_size > 0) {
+                        if (urb == endp->urb_list[ENDP_QUEUE_MASK &
+                                ++queue_scan]) {
+                                urb_slot = &endp->urb_list[ENDP_QUEUE_MASK &
+                                        queue_scan];
+                                break;
+                        } else
+                                continue;
+                }
+                while (++queue_list < ENDP_QUEUE_SIZE && --queue_size > 0) {
+                        *urb_slot = endp->urb_list[ENDP_QUEUE_MASK &
+                                ++queue_scan];
+                        urb_slot = &endp->urb_list[ENDP_QUEUE_MASK &
+                                queue_scan];
+                }
+                if (urb_slot) {
+                        struct usb_hcd *hcd = u132_to_hcd(u132);
+                        endp->queue_size -= 1;
+                        if (list_empty(&endp->urb_more)) {
+                                spin_unlock_irqrestore(&endp->queue_lock.slock,
+                                        irqs);
+                        } else {
+                                struct list_head *next = endp->urb_more.next;
+                                struct u132_urbq *urbq = list_entry(next,
+                                        struct u132_urbq, urb_more);
+                                list_del(next);
+                                *urb_slot = urbq->urb;
+                                spin_unlock_irqrestore(&endp->queue_lock.slock,
+                                        irqs);
+                                kfree(urbq);
+                        } urb->error_count = 0;
+                        urb->hcpriv = NULL;
+                        usb_hcd_giveback_urb(hcd, urb, NULL);
+                        return 0;
+                } else if (list_empty(&endp->urb_more)) {
+                        dev_err(&u132->platform_dev->dev, "urb=%p not found in "
+                                "endp[%d]=%p ring[%d] %c%c usb_endp=%d usb_addr"
+                                "=%d size=%d next=%04X last=%04X\n", urb,
+                                endp->endp_number, endp, endp->ring->number,
+                                endp->input ? 'I' : ' ',
+                                endp->output ? 'O' : ' ', endp->usb_endp,
+                                endp->usb_addr, endp->queue_size,
+                                endp->queue_next, endp->queue_last);
+                        spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+                        return -EINVAL;
+                } else {
+                        int retval = dequeue_from_overflow_chain(u132, endp,
+                                urb);
+                        spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+                        return retval;
+                }
+        }
+}
+
+static int u132_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 2) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else {
+                u8 usb_addr = usb_pipedevice(urb->pipe);
+                u8 usb_endp = usb_pipeendpoint(urb->pipe);
+                u8 address = u132->addr[usb_addr].address;
+                struct u132_udev *udev = &u132->udev[address];
+                if (usb_pipein(urb->pipe)) {
+                        u8 endp_number = udev->endp_number_in[usb_endp];
+                        struct u132_endp *endp = u132->endp[endp_number - 1];
+                        return u132_endp_urb_dequeue(u132, endp, urb);
+                } else {
+                        u8 endp_number = udev->endp_number_out[usb_endp];
+                        struct u132_endp *endp = u132->endp[endp_number - 1];
+                        return u132_endp_urb_dequeue(u132, endp, urb);
+                }
+        }
+}
+
+static void u132_endpoint_disable(struct usb_hcd *hcd,
+        struct usb_host_endpoint *hep)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 2) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+        } else {
+                struct u132_endp *endp = hep->hcpriv;
+                if (endp)
+                        u132_endp_put_kref(u132, endp);
+        }
+}
+
+static int u132_get_frame(struct usb_hcd *hcd)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else {
+                int frame = 0;
+                dev_err(&u132->platform_dev->dev, "TODO: u132_get_frame\n");
+                msleep(100);
+                return frame;
+        }
+}
+
+static int u132_roothub_descriptor(struct u132 *u132,
+        struct usb_hub_descriptor *desc)
+{
+        int retval;
+        u16 temp;
+        u32 rh_a = -1;
+        u32 rh_b = -1;
+        retval = u132_read_pcimem(u132, roothub.a, &rh_a);
+        if (retval)
+                return retval;
+        desc->bDescriptorType = 0x29;
+        desc->bPwrOn2PwrGood = (rh_a & RH_A_POTPGT) >> 24;
+        desc->bHubContrCurrent = 0;
+        desc->bNbrPorts = u132->num_ports;
+        temp = 1 + (u132->num_ports / 8);
+        desc->bDescLength = 7 + 2 *temp;
+        temp = 0;
+        if (rh_a & RH_A_NPS)
+                temp |= 0x0002;
+        if (rh_a & RH_A_PSM)
+                temp |= 0x0001;
+        if (rh_a & RH_A_NOCP) {
+                temp |= 0x0010;
+        } else if (rh_a & RH_A_OCPM)
+                temp |= 0x0008;
+        desc->wHubCharacteristics = cpu_to_le16(temp);
+        retval = u132_read_pcimem(u132, roothub.b, &rh_b);
+        if (retval)
+                return retval;
+        memset(desc->bitmap, 0xff, sizeof(desc->bitmap));
+        desc->bitmap[0] = rh_b & RH_B_DR;
+        if (u132->num_ports > 7) {
+                desc->bitmap[1] = (rh_b & RH_B_DR) >> 8;
+                desc->bitmap[2] = 0xff;
+        } else
+                desc->bitmap[1] = 0xff;
+        return 0;
+}
+
+static int u132_roothub_status(struct u132 *u132, __le32 *desc)
+{
+        u32 rh_status = -1;
+        int ret_status = u132_read_pcimem(u132, roothub.status, &rh_status);
+        *desc = cpu_to_le32(rh_status);
+        return ret_status;
+}
+
+static int u132_roothub_portstatus(struct u132 *u132, __le32 *desc, u16 wIndex)
+{
+        if (wIndex == 0 || wIndex > u132->num_ports) {
+                return -EINVAL;
+        } else {
+                int port = wIndex - 1;
+                u32 rh_portstatus = -1;
+                int ret_portstatus = u132_read_pcimem(u132,
+                        roothub.portstatus[port], &rh_portstatus);
+                *desc = cpu_to_le32(rh_portstatus);
+                if (*(u16 *) (desc + 2)) {
+                        dev_info(&u132->platform_dev->dev, "Port %d Status Chan"
+                                "ge = %08X\n", port, *desc);
+                }
+                return ret_portstatus;
+        }
+}
+
+
+/* this timer value might be vendor-specific ... */
+#define PORT_RESET_HW_MSEC 10
+#define PORT_RESET_MSEC 10
+/* wrap-aware logic morphed from <linux/jiffies.h> */
+#define tick_before(t1, t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0)
+static int u132_roothub_portreset(struct u132 *u132, int port_index)
+{
+        int retval;
+        u32 fmnumber;
+        u16 now;
+        u16 reset_done;
+        retval = u132_read_pcimem(u132, fmnumber, &fmnumber);
+        if (retval)
+                return retval;
+        now = fmnumber;
+        reset_done = now + PORT_RESET_MSEC;
+        do {
+                u32 portstat;
+                do {
+                        retval = u132_read_pcimem(u132,
+                                roothub.portstatus[port_index], &portstat);
+                        if (retval)
+                                return retval;
+                        if (RH_PS_PRS & portstat) {
+                                continue;
+                        } else
+                                break;
+                } while (tick_before(now, reset_done));
+                if (RH_PS_PRS & portstat)
+                        return -ENODEV;
+                if (RH_PS_CCS & portstat) {
+                        if (RH_PS_PRSC & portstat) {
+                                retval = u132_write_pcimem(u132,
+                                        roothub.portstatus[port_index],
+                                        RH_PS_PRSC);
+                                if (retval)
+                                        return retval;
+                        }
+                } else
+                        break;        /* start the next reset,
+                                sleep till it's probably done */
+                retval = u132_write_pcimem(u132, roothub.portstatus[port_index],
+                         RH_PS_PRS);
+                if (retval)
+                        return retval;
+                msleep(PORT_RESET_HW_MSEC);
+                retval = u132_read_pcimem(u132, fmnumber, &fmnumber);
+                if (retval)
+                        return retval;
+                now = fmnumber;
+        } while (tick_before(now, reset_done));
+        return 0;
+}
+
+static int u132_roothub_setportfeature(struct u132 *u132, u16 wValue,
+        u16 wIndex)
+{
+        if (wIndex == 0 || wIndex > u132->num_ports) {
+                return -EINVAL;
+        } else {
+                int retval;
+                int port_index = wIndex - 1;
+                struct u132_port *port = &u132->port[port_index];
+                port->Status &= ~(1 << wValue);
+                switch (wValue) {
+                case USB_PORT_FEAT_SUSPEND:
+                        retval = u132_write_pcimem(u132,
+                                roothub.portstatus[port_index], RH_PS_PSS);
+                        if (retval)
+                                return retval;
+                        return 0;
+                case USB_PORT_FEAT_POWER:
+                        retval = u132_write_pcimem(u132,
+                                roothub.portstatus[port_index], RH_PS_PPS);
+                        if (retval)
+                                return retval;
+                        return 0;
+                case USB_PORT_FEAT_RESET:
+                        retval = u132_roothub_portreset(u132, port_index);
+                        if (retval)
+                                return retval;
+                        return 0;
+                default:
+                        return -EPIPE;
+                }
+        }
+}
+
+static int u132_roothub_clearportfeature(struct u132 *u132, u16 wValue,
+        u16 wIndex)
+{
+        if (wIndex == 0 || wIndex > u132->num_ports) {
+                return -EINVAL;
+        } else {
+                int port_index = wIndex - 1;
+                u32 temp;
+                int retval;
+                struct u132_port *port = &u132->port[port_index];
+                port->Status &= ~(1 << wValue);
+                switch (wValue) {
+                case USB_PORT_FEAT_ENABLE:
+                        temp = RH_PS_CCS;
+                        break;
+                case USB_PORT_FEAT_C_ENABLE:
+                        temp = RH_PS_PESC;
+                        break;
+                case USB_PORT_FEAT_SUSPEND:
+                        temp = RH_PS_POCI;
+                        if ((u132->hc_control & OHCI_CTRL_HCFS)
+                                != OHCI_USB_OPER) {
+                                dev_err(&u132->platform_dev->dev, "TODO resume_"
+                                        "root_hub\n");
+                        }
+                        break;
+                case USB_PORT_FEAT_C_SUSPEND:
+                        temp = RH_PS_PSSC;
+                        break;
+                case USB_PORT_FEAT_POWER:
+                        temp = RH_PS_LSDA;
+                        break;
+                case USB_PORT_FEAT_C_CONNECTION:
+                        temp = RH_PS_CSC;
+                        break;
+                case USB_PORT_FEAT_C_OVER_CURRENT:
+                        temp = RH_PS_OCIC;
+                        break;
+                case USB_PORT_FEAT_C_RESET:
+                        temp = RH_PS_PRSC;
+                        break;
+                default:
+                        return -EPIPE;
+                }
+                retval = u132_write_pcimem(u132, roothub.portstatus[port_index],
+                         temp);
+                if (retval)
+                        return retval;
+                return 0;
+        }
+}
+
+
+/* the virtual root hub timer IRQ checks for hub status*/
+static int u132_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device hcd=%p has been remov"
+                        "ed %d\n", hcd, u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device hcd=%p is being remov"
+                        "ed\n", hcd);
+                dump_stack();
+                return -ESHUTDOWN;
+        } else {
+                int i, changed = 0, length = 1;
+                if (u132->flags & OHCI_QUIRK_AMD756) {
+                        if ((u132->hc_roothub_a & RH_A_NDP) > MAX_ROOT_PORTS) {
+                                dev_err(&u132->platform_dev->dev, "bogus NDP, r"
+                                        "ereads as NDP=%d\n",
+                                        u132->hc_roothub_a & RH_A_NDP);
+                                goto done;
+                        }
+                }
+                if (u132->hc_roothub_status & (RH_HS_LPSC | RH_HS_OCIC)) {
+                        buf[0] = changed = 1;
+                } else
+                        buf[0] = 0;
+                if (u132->num_ports > 7) {
+                        buf[1] = 0;
+                        length++;
+                }
+                for (i = 0; i < u132->num_ports; i++) {
+                        if (u132->hc_roothub_portstatus[i] & (RH_PS_CSC |
+                                RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC |
+                                RH_PS_PRSC)) {
+                                changed = 1;
+                                if (i < 7) {
+                                        buf[0] |= 1 << (i + 1);
+                                } else
+                                        buf[1] |= 1 << (i - 7);
+                                continue;
+                        }
+                        if (!(u132->hc_roothub_portstatus[i] & RH_PS_CCS)) {
+                                continue;
+                        }
+                        if ((u132->hc_roothub_portstatus[i] & RH_PS_PSS)) {
+                                continue;
+                        }
+                }
+              done:return changed ? length : 0;
+        }
+}
+
+static int u132_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+        u16 wIndex, char *buf, u16 wLength)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else {
+                int retval = 0;
+                down(&u132->sw_lock);
+                switch (typeReq) {
+                case ClearHubFeature:
+                        switch (wValue) {
+                        case C_HUB_OVER_CURRENT:
+                        case C_HUB_LOCAL_POWER:
+                                break;
+                        default:
+                                goto stall;
+                        }
+                        break;
+                case SetHubFeature:
+                        switch (wValue) {
+                        case C_HUB_OVER_CURRENT:
+                        case C_HUB_LOCAL_POWER:
+                                break;
+                        default:
+                                goto stall;
+                        }
+                        break;
+                case ClearPortFeature:{
+                                retval = u132_roothub_clearportfeature(u132,
+                                        wValue, wIndex);
+                                if (retval)
+                                        goto error;
+                                break;
+                        }
+                case GetHubDescriptor:{
+                                retval = u132_roothub_descriptor(u132,
+                                        (struct usb_hub_descriptor *)buf);
+                                if (retval)
+                                        goto error;
+                                break;
+                        }
+                case GetHubStatus:{
+                                retval = u132_roothub_status(u132,
+                                        (__le32 *) buf);
+                                if (retval)
+                                        goto error;
+                                break;
+                        }
+                case GetPortStatus:{
+                                retval = u132_roothub_portstatus(u132,
+                                        (__le32 *) buf, wIndex);
+                                if (retval)
+                                        goto error;
+                                break;
+                        }
+                case SetPortFeature:{
+                                retval = u132_roothub_setportfeature(u132,
+                                        wValue, wIndex);
+                                if (retval)
+                                        goto error;
+                                break;
+                        }
+                default:
+                        goto stall;
+                      error:u132_disable(u132);
+                        u132->going = 1;
+                        break;
+                      stall:retval = -EPIPE;
+                        break;
+                }
+                up(&u132->sw_lock);
+                return retval;
+        }
+}
+
+static int u132_start_port_reset(struct usb_hcd *hcd, unsigned port_num)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else
+                return 0;
+}
+
+static void u132_hub_irq_enable(struct usb_hcd *hcd)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+        } else if (u132->going > 0)
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+}
+
+
+#ifdef CONFIG_PM
+static int u132_hcd_suspend(struct usb_hcd *hcd, pm_message_t message)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else
+                return 0;
+}
+
+static int u132_hcd_resume(struct usb_hcd *hcd)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else
+                return 0;
+}
+
+static int u132_bus_suspend(struct usb_hcd *hcd)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else
+                return 0;
+}
+
+static int u132_bus_resume(struct usb_hcd *hcd)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else
+                return 0;
+}
+
+#else
+#define u132_hcd_suspend NULL
+#define u132_hcd_resume NULL
+#define u132_bus_suspend NULL
+#define u132_bus_resume NULL
+#endif
+static struct hc_driver u132_hc_driver = {
+        .description = hcd_name,
+        .hcd_priv_size = sizeof(struct u132),
+        .irq = NULL,
+        .flags = HCD_USB11 | HCD_MEMORY,
+        .reset = u132_hcd_reset,
+        .start = u132_hcd_start,
+        .suspend = u132_hcd_suspend,
+        .resume = u132_hcd_resume,
+        .stop = u132_hcd_stop,
+        .urb_enqueue = u132_urb_enqueue,
+        .urb_dequeue = u132_urb_dequeue,
+        .endpoint_disable = u132_endpoint_disable,
+        .get_frame_number = u132_get_frame,
+        .hub_status_data = u132_hub_status_data,
+        .hub_control = u132_hub_control,
+        .bus_suspend = u132_bus_suspend,
+        .bus_resume = u132_bus_resume,
+        .start_port_reset = u132_start_port_reset,
+        .hub_irq_enable = u132_hub_irq_enable,
+};
+
+/*
+* This function may be called by the USB core whilst the "usb_all_devices_rwsem"
+* is held for writing, thus this module must not call usb_remove_hcd()
+* synchronously - but instead should immediately stop activity to the
+* device and ansynchronously call usb_remove_hcd()
+*/
+static int __devexit u132_remove(struct platform_device *pdev)
+{
+        struct usb_hcd *hcd = platform_get_drvdata(pdev);
+        if (hcd) {
+                struct u132 *u132 = hcd_to_u132(hcd);
+                dump_stack();
+                if (u132->going++ > 1) {
+                        return -ENODEV;
+                } else {
+                        int rings = MAX_U132_RINGS;
+                        int endps = MAX_U132_ENDPS;
+                        msleep(100);
+                        down(&u132->sw_lock);
+                        u132_monitor_cancel_work(u132);
+                        while (rings-- > 0) {
+                                struct u132_ring *ring = &u132->ring[rings];
+                                u132_ring_cancel_work(u132, ring);
+                        } while (endps-- > 0) {
+                                struct u132_endp *endp = u132->endp[endps];
+                                if (endp)
+                                        u132_endp_cancel_work(u132, endp);
+                        }
+                        u132->going += 1;
+                        printk(KERN_INFO "removing device u132.%d\n",
+                                u132->sequence_num);
+                        up(&u132->sw_lock);
+                        usb_remove_hcd(hcd);
+                        u132_u132_put_kref(u132);
+                        return 0;
+                }
+        } else
+                return 0;
+}
+
+static void u132_initialise(struct u132 *u132, struct platform_device *pdev)
+{
+        int rings = MAX_U132_RINGS;
+        int ports = MAX_U132_PORTS;
+        int addrs = MAX_U132_ADDRS;
+        int udevs = MAX_U132_UDEVS;
+        int endps = MAX_U132_ENDPS;
+        u132->board = pdev->dev.platform_data;
+        u132->platform_dev = pdev;
+        u132->power = 0;
+        u132->reset = 0;
+        init_MUTEX(&u132->sw_lock);
+        init_MUTEX(&u132->scheduler_lock);
+        while (rings-- > 0) {
+                struct u132_ring *ring = &u132->ring[rings];
+                ring->u132 = u132;
+                ring->number = rings + 1;
+                ring->length = 0;
+                ring->curr_endp = NULL;
+                INIT_WORK(&ring->scheduler, u132_hcd_ring_work_scheduler,
+                        (void *)ring);
+        } down(&u132->sw_lock);
+        INIT_WORK(&u132->monitor, u132_hcd_monitor_work, (void *)u132);
+        while (ports-- > 0) {
+                struct u132_port *port = &u132->port[ports];
+                port->u132 = u132;
+                port->reset = 0;
+                port->enable = 0;
+                port->power = 0;
+                port->Status = 0;
+        } while (addrs-- > 0) {
+                struct u132_addr *addr = &u132->addr[addrs];
+                addr->address = 0;
+        } while (udevs-- > 0) {
+                struct u132_udev *udev = &u132->udev[udevs];
+                int i = ARRAY_SIZE(udev->endp_number_in);
+                int o = ARRAY_SIZE(udev->endp_number_out);
+                udev->usb_device = NULL;
+                udev->udev_number = 0;
+                udev->usb_addr = 0;
+                udev->portnumber = 0;
+                while (i-- > 0) {
+                        udev->endp_number_in[i] = 0;
+                }
+                while (o-- > 0) {
+                        udev->endp_number_out[o] = 0;
+                }
+        }
+        while (endps-- > 0) {
+                u132->endp[endps] = NULL;
+        }
+        up(&u132->sw_lock);
+        return;
+}
+
+static int __devinit u132_probe(struct platform_device *pdev)
+{
+        struct usb_hcd *hcd;
+        msleep(100);
+        if (u132_exiting > 0) {
+                return -ENODEV;
+        }                        /* refuse to confuse usbcore */
+        if (pdev->dev.dma_mask) {
+                return -EINVAL;
+        }
+        hcd = usb_create_hcd(&u132_hc_driver, &pdev->dev, pdev->dev.bus_id);
+        if (!hcd) {
+                printk(KERN_ERR "failed to create the usb hcd struct for U132\n"
+                        );
+                ftdi_elan_gone_away(pdev);
+                return -ENOMEM;
+        } else {
+                int retval = 0;
+                struct u132 *u132 = hcd_to_u132(hcd);
+                hcd->rsrc_start = 0;
+                down(&u132_module_lock);
+                list_add_tail(&u132->u132_list, &u132_static_list);
+                u132->sequence_num = ++u132_instances;
+                up(&u132_module_lock);
+                u132_u132_init_kref(u132);
+                u132_initialise(u132, pdev);
+                hcd->product_desc = "ELAN U132 Host Controller";
+                retval = usb_add_hcd(hcd, 0, 0);
+                if (retval != 0) {
+                        dev_err(&u132->platform_dev->dev, "init error %d\n",
+                                retval);
+                        u132_u132_put_kref(u132);
+                        return retval;
+                } else {
+                        u132_monitor_queue_work(u132, 100);
+                        return 0;
+                }
+        }
+}
+
+
+#ifdef CONFIG_PM
+/* for this device there's no useful distinction between the controller
+* and its root hub, except that the root hub only gets direct PM calls
+* when CONFIG_USB_SUSPEND is enabled.
+*/
+static int u132_suspend(struct platform_device *pdev, pm_message_t state)
+{
+        struct usb_hcd *hcd = platform_get_drvdata(pdev);
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else {
+                int retval = 0;
+                if (state.event == PM_EVENT_FREEZE) {
+                        retval = u132_bus_suspend(hcd);
+                } else if (state.event == PM_EVENT_SUSPEND) {
+                        int ports = MAX_U132_PORTS;
+                        while (ports-- > 0) {
+                                port_power(u132, ports, 0);
+                        }
+                }
+                if (retval == 0)
+                        pdev->dev.power.power_state = state;
+                return retval;
+        }
+}
+
+static int u132_resume(struct platform_device *pdev)
+{
+        struct usb_hcd *hcd = platform_get_drvdata(pdev);
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else {
+                int retval = 0;
+                if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
+                        int ports = MAX_U132_PORTS;
+                        while (ports-- > 0) {
+                                port_power(u132, ports, 1);
+                        }
+                        retval = 0;
+                } else {
+                        pdev->dev.power.power_state = PMSG_ON;
+                        retval = u132_bus_resume(hcd);
+                }
+                return retval;
+        }
+}
+
+#else
+#define u132_suspend NULL
+#define u132_resume NULL
+#endif
+/*
+* this driver is loaded explicitely by ftdi_u132
+*
+* the platform_driver struct is static because it is per type of module
+*/
+static struct platform_driver u132_platform_driver = {
+        .probe = u132_probe,
+        .remove = __devexit_p(u132_remove),
+        .suspend = u132_suspend,
+        .resume = u132_resume,
+        .driver = {
+                   .name = (char *)hcd_name,
+                   .owner = THIS_MODULE,
+                   },
+};
+static int __init u132_hcd_init(void)
+{
+        int retval;
+        INIT_LIST_HEAD(&u132_static_list);
+        u132_instances = 0;
+        u132_exiting = 0;
+        init_MUTEX(&u132_module_lock);
+        if (usb_disabled())
+                return -ENODEV;
+        printk(KERN_INFO "driver %s built at %s on %s\n", hcd_name, __TIME__,
+                __DATE__);
+        workqueue = create_singlethread_workqueue("u132");
+        retval = platform_driver_register(&u132_platform_driver);
+        return retval;
+}
+
+
+module_init(u132_hcd_init);
+static void __exit u132_hcd_exit(void)
+{
+        struct u132 *u132;
+        struct u132 *temp;
+        down(&u132_module_lock);
+        u132_exiting += 1;
+        up(&u132_module_lock);
+        list_for_each_entry_safe(u132, temp, &u132_static_list, u132_list) {
+                platform_device_unregister(u132->platform_dev);
+        } platform_driver_unregister(&u132_platform_driver);
+        printk(KERN_INFO "u132-hcd driver deregistered\n");
+        wait_event(u132_hcd_wait, u132_instances == 0);
+        flush_workqueue(workqueue);
+        destroy_workqueue(workqueue);
+}
+
+
+module_exit(u132_hcd_exit);
+MODULE_LICENSE("GPL");
index d1372cb..e345f15 100644 (file)
@@ -16,7 +16,7 @@
 
 #include "uhci-hcd.h"
 
-#define uhci_debug_operations (* (struct file_operations *) NULL)
+#define uhci_debug_operations (* (const struct file_operations *) NULL)
 static struct dentry *uhci_debugfs_root;
 
 #ifdef DEBUG
@@ -500,7 +500,7 @@ static int uhci_debug_release(struct inode *inode, struct file *file)
 }
 
 #undef uhci_debug_operations
-static struct file_operations uhci_debug_operations = {
+static const struct file_operations uhci_debug_operations = {
        .owner =        THIS_MODULE,
        .open =         uhci_debug_open,
        .llseek =       uhci_debug_lseek,
index c545ef9..16fb72e 100644 (file)
@@ -84,6 +84,7 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
                unsigned long port_addr)
 {
        int status;
+       int i;
 
        if (inw(port_addr) & (USBPORTSC_SUSP | USBPORTSC_RD)) {
                CLR_RH_PORTSTAT(USBPORTSC_SUSP | USBPORTSC_RD);
@@ -92,9 +93,14 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
 
                /* The controller won't actually turn off the RD bit until
                 * it has had a chance to send a low-speed EOP sequence,
-                * which takes 3 bit times (= 2 microseconds).  We'll delay
-                * slightly longer for good luck. */
-               udelay(4);
+                * which is supposed to take 3 bit times (= 2 microseconds).
+                * Experiments show that some controllers take longer, so
+                * we'll poll for completion. */
+               for (i = 0; i < 10; ++i) {
+                       if (!(inw(port_addr) & USBPORTSC_RD))
+                               break;
+                       udelay(1);
+               }
        }
        clear_bit(port, &uhci->resuming_ports);
 }
index 08daf40..ca6305c 100644 (file)
@@ -424,7 +424,7 @@ static void mdc800_usb_download_notify (struct urb *urb, struct pt_regs *res)
  ***************************************************************************/
 
 static struct usb_driver mdc800_usb_driver;
-static struct file_operations mdc800_device_ops;
+static const struct file_operations mdc800_device_ops;
 static struct usb_class_driver mdc800_class = {
        .name =         "mdc800%d",
        .fops =         &mdc800_device_ops,
@@ -941,7 +941,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s
 ****************************************************************************/
 
 /* File Operations of this drivers */
-static struct file_operations mdc800_device_ops =
+static const struct file_operations mdc800_device_ops =
 {
        .owner =        THIS_MODULE,
        .read =         mdc800_device_read,
index 650103b..a102a58 100644 (file)
@@ -205,10 +205,12 @@ config USB_TOUCHSCREEN
        depends on USB && INPUT
        ---help---
          USB Touchscreen driver for:
-         - eGalax Touchkit USB
+         - eGalax Touchkit USB (also includes eTurboTouch CT-410/510/700)
          - PanJit TouchSet USB
-         - 3M MicroTouch USB
+         - 3M MicroTouch USB (EX II series)
          - ITM
+         - some other eTurboTouch
+         - Gunze AHL61
 
          Have a look at <http://linux.chapter7.ch/touchkit/> for
          a usage description and the required user-space stuff.
@@ -218,7 +220,7 @@ config USB_TOUCHSCREEN
 
 config USB_TOUCHSCREEN_EGALAX
        default y
-       bool "eGalax device support" if EMBEDDED
+       bool "eGalax, eTurboTouch CT-410/510/700 device support" if EMBEDDED
        depends on USB_TOUCHSCREEN
 
 config USB_TOUCHSCREEN_PANJIT
@@ -228,7 +230,7 @@ config USB_TOUCHSCREEN_PANJIT
 
 config USB_TOUCHSCREEN_3M
        default y
-       bool "3M/Microtouch device support" if EMBEDDED
+       bool "3M/Microtouch EX II series device support" if EMBEDDED
        depends on USB_TOUCHSCREEN
 
 config USB_TOUCHSCREEN_ITM
@@ -236,6 +238,16 @@ config USB_TOUCHSCREEN_ITM
        bool "ITM device support" if EMBEDDED
        depends on USB_TOUCHSCREEN
 
+config USB_TOUCHSCREEN_ETURBO
+       default y
+       bool "eTurboTouch (non-eGalax compatible) device support" if EMBEDDED
+       depends on USB_TOUCHSCREEN
+
+config USB_TOUCHSCREEN_GUNZE
+       default y
+       bool "Gunze AHL61 device support" if EMBEDDED
+       depends on USB_TOUCHSCREEN
+
 config USB_YEALINK
        tristate "Yealink usb-p1k voip phone"
        depends on USB && INPUT && EXPERIMENTAL
@@ -326,3 +338,13 @@ config USB_APPLETOUCH
 
          To compile this driver as a module, choose M here: the
          module will be called appletouch.
+
+config USB_TRANCEVIBRATOR
+       tristate "PlayStation 2 Trance Vibrator driver support"
+       depends on USB
+       help
+         Say Y here if you want to connect a PlayStation 2 Trance Vibrator
+         device to your computer's USB port.
+
+         To compile this driver as a module, choose M here: the
+         module will be called trancevibrator.
index 7641145..48551be 100644 (file)
@@ -3,6 +3,7 @@
 #
 
 # Multipart objects.
+wacom-objs     := wacom_sys.o wacom_wac.o
 usbhid-objs    := hid-core.o
 
 # Optional parts of multipart objects.
@@ -44,6 +45,7 @@ obj-$(CONFIG_USB_ACECAD)      += acecad.o
 obj-$(CONFIG_USB_YEALINK)      += yealink.o
 obj-$(CONFIG_USB_XPAD)         += xpad.o
 obj-$(CONFIG_USB_APPLETOUCH)   += appletouch.o
+obj-$(CONFIG_USB_TRANCEVIBRATOR)       += trancevibrator.o
 
 ifeq ($(CONFIG_USB_DEBUG),y)
 EXTRA_CFLAGS += -DDEBUG
index 18c10e1..d83603b 100644 (file)
@@ -141,10 +141,7 @@ static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_
 
        endpoint = &interface->endpoint[0].desc;
 
-       if (!(endpoint->bEndpointAddress & 0x80))
-               return -ENODEV;
-
-       if ((endpoint->bmAttributes & 3) != 3)
+       if (!usb_endpoint_is_int_in(endpoint))
                return -ENODEV;
 
        pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
index 044faa0..0aa9cc2 100644 (file)
@@ -436,10 +436,7 @@ static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id
        iface_desc = iface->cur_altsetting;
        for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
                endpoint = &iface_desc->endpoint[i].desc;
-               if (!int_in_endpointAddr &&
-                   (endpoint->bEndpointAddress & USB_DIR_IN) &&
-                   ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-                                       == USB_ENDPOINT_XFER_INT)) {
+               if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) {
                        /* we found an interrupt in endpoint */
                        int_in_endpointAddr = endpoint->bEndpointAddress;
                        break;
index 3719fcb..3558d7e 100644 (file)
@@ -732,12 +732,8 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de
        endpoint_in = &iface_host->endpoint[0].desc;
        endpoint_out = &iface_host->endpoint[1].desc;
 
-       if (!(endpoint_in->bEndpointAddress & USB_DIR_IN)) {
-               err("%s: Unexpected endpoint_in->bEndpointAddress\n", __FUNCTION__);
-               return -ENODEV;
-       }
-       if ((endpoint_in->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) {
-               err("%s: Unexpected endpoint_in->bmAttributes\n", __FUNCTION__);
+       if (!usb_endpoint_is_int_in(endpoint_in)) {
+               err("%s: Unexpected endpoint_in\n", __FUNCTION__);
                return -ENODEV;
        }
        if (le16_to_cpu(endpoint_in->wMaxPacketSize) == 0) {
index 3305fb6..2a3e9e9 100644 (file)
@@ -1023,7 +1023,8 @@ static void hid_irq_in(struct urb *urb, struct pt_regs *regs)
                        return;
                case -EILSEQ:           /* protocol error or unplug */
                case -EPROTO:           /* protocol error or unplug */
-               case -ETIMEDOUT:        /* NAK */
+               case -ETIME:            /* protocol error or unplug */
+               case -ETIMEDOUT:        /* Should never happen, but... */
                        clear_bit(HID_IN_RUNNING, &hid->iofl);
                        hid_io_error(hid);
                        return;
@@ -1535,13 +1536,17 @@ void hid_init_reports(struct hid_device *hid)
 #define USB_VENDOR_ID_GLAB             0x06c2
 #define USB_DEVICE_ID_4_PHIDGETSERVO_30        0x0038
 #define USB_DEVICE_ID_1_PHIDGETSERVO_30        0x0039
-#define USB_DEVICE_ID_8_8_8_IF_KIT     0x0045
 #define USB_DEVICE_ID_0_0_4_IF_KIT     0x0040
+#define USB_DEVICE_ID_0_16_16_IF_KIT   0x0044
+#define USB_DEVICE_ID_8_8_8_IF_KIT     0x0045
+#define USB_DEVICE_ID_0_8_7_IF_KIT     0x0051
 #define USB_DEVICE_ID_0_8_8_IF_KIT     0x0053
+#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL     0x0058
 
 #define USB_VENDOR_ID_WISEGROUP                0x0925
 #define USB_DEVICE_ID_1_PHIDGETSERVO_20        0x8101
 #define USB_DEVICE_ID_4_PHIDGETSERVO_20        0x8104
+#define USB_DEVICE_ID_8_8_4_IF_KIT     0x8201
 #define USB_DEVICE_ID_DUAL_USB_JOYPAD   0x8866
 
 #define USB_VENDOR_ID_WISEGROUP_LTD    0x6677
@@ -1591,6 +1596,10 @@ void hid_init_reports(struct hid_device *hid)
 
 #define USB_VENDOR_ID_YEALINK          0x6993
 #define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K      0xb001
+
+#define USB_VENDOR_ID_ALCOR            0x058f
+#define USB_DEVICE_ID_ALCOR_USBRS232   0x9720
+
 /*
  * Alphabetically sorted blacklist by quirk type.
  */
@@ -1608,6 +1617,7 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE },
+       { USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24, HID_QUIRK_IGNORE },
@@ -1620,9 +1630,12 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE },
+       { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT, HID_QUIRK_IGNORE },
+       { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
+       { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE },
+       { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE },
@@ -1690,7 +1703,11 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE },
+       { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20, HID_QUIRK_IGNORE },
+       { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE },
+       { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108, HID_QUIRK_IGNORE },
+       { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE },
@@ -1701,6 +1718,7 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
+       { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE },
 
        { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE },
index f6b839c..a2b419d 100644 (file)
@@ -722,7 +722,7 @@ inval:
        return -EINVAL;
 }
 
-static struct file_operations hiddev_fops = {
+static const struct file_operations hiddev_fops = {
        .owner =        THIS_MODULE,
        .read =         hiddev_read,
        .write =        hiddev_write,
index 86acb5f..61966d7 100644 (file)
@@ -87,7 +87,7 @@ static void itmtouch_irq(struct urb *urb, struct pt_regs *regs)
        case 0:
                /* success */
                break;
-       case -ETIMEDOUT:
+       case -ETIME:
                /* this urb is timing out */
                dbg("%s - urb timed out - was the device unplugged?",
                    __FUNCTION__);
index 4723b31..a903595 100644 (file)
@@ -420,8 +420,7 @@ static struct usb_endpoint_descriptor *keyspan_get_in_endpoint(struct usb_host_i
        for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
                endpoint = &iface->endpoint[i].desc;
 
-               if ((endpoint->bEndpointAddress & USB_DIR_IN) &&
-                   ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
+               if (usb_endpoint_is_int_in(endpoint)) {
                        /* we found our interrupt in endpoint */
                        return endpoint;
                }
index a9ccda8..5dce951 100644 (file)
@@ -107,7 +107,7 @@ static void mtouchusb_irq(struct urb *urb, struct pt_regs *regs)
        case 0:
                /* success */
                break;
-       case -ETIMEDOUT:
+       case -ETIME:
                /* this urb is timing out */
                dbg("%s - urb timed out - was the device unplugged?",
                    __FUNCTION__);
index b3c0d0c..f0f8db6 100644 (file)
@@ -313,9 +313,7 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i
 
        interface = intf->cur_altsetting;
        endpoint = &interface->endpoint[0].desc;
-       if (!(endpoint->bEndpointAddress & 0x80))
-               return -EIO;
-       if ((endpoint->bmAttributes & 3) != 3)
+       if (!usb_endpoint_is_int_in(endpoint))
                return -EIO;
 
        usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
index 0149043..30b9f82 100644 (file)
@@ -201,7 +201,7 @@ static void touchkit_irq(struct urb *urb, struct pt_regs *regs)
        case 0:
                /* success */
                break;
-       case -ETIMEDOUT:
+       case -ETIME:
                /* this urb is timing out */
                dbg("%s - urb timed out - was the device unplugged?",
                    __FUNCTION__);
diff --git a/drivers/usb/input/trancevibrator.c b/drivers/usb/input/trancevibrator.c
new file mode 100644 (file)
index 0000000..33cd91d
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * PlayStation 2 Trance Vibrator driver
+ *
+ * Copyright (C) 2006 Sam Hocevar <sam@zoy.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Standard include files */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+/* Version Information */
+#define DRIVER_VERSION "v1.1"
+#define DRIVER_AUTHOR "Sam Hocevar, sam@zoy.org"
+#define DRIVER_DESC "PlayStation 2 Trance Vibrator driver"
+
+#define TRANCEVIBRATOR_VENDOR_ID       0x0b49  /* ASCII Corporation */
+#define TRANCEVIBRATOR_PRODUCT_ID      0x064f  /* Trance Vibrator */
+
+static struct usb_device_id id_table [] = {
+       { USB_DEVICE(TRANCEVIBRATOR_VENDOR_ID, TRANCEVIBRATOR_PRODUCT_ID) },
+       { },
+};
+MODULE_DEVICE_TABLE (usb, id_table);
+
+/* Driver-local specific stuff */
+struct trancevibrator {
+       struct usb_device *udev;
+       unsigned int speed;
+};
+
+static ssize_t show_speed(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       struct usb_interface *intf = to_usb_interface(dev);
+       struct trancevibrator *tv = usb_get_intfdata(intf);
+
+       return sprintf(buf, "%d\n", tv->speed);
+}
+
+static ssize_t set_speed(struct device *dev, struct device_attribute *attr,
+                        const char *buf, size_t count)
+{
+       struct usb_interface *intf = to_usb_interface(dev);
+       struct trancevibrator *tv = usb_get_intfdata(intf);
+       int temp, retval;
+
+       temp = simple_strtoul(buf, NULL, 10);
+       if (temp > 255)
+               temp = 255;
+       else if (temp < 0)
+               temp = 0;
+       tv->speed = temp;
+
+       dev_dbg(&tv->udev->dev, "speed = %d\n", tv->speed);
+
+       /* Set speed */
+       retval = usb_control_msg(tv->udev, usb_sndctrlpipe(tv->udev, 0),
+                                0x01, /* vendor request: set speed */
+                                USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+                                tv->speed, /* speed value */
+                                0, NULL, 0, USB_CTRL_GET_TIMEOUT);
+       if (retval) {
+               dev_dbg(&tv->udev->dev, "retval = %d\n", retval);
+               return retval;
+       }
+       return count;
+}
+
+static DEVICE_ATTR(speed, S_IWUGO | S_IRUGO, show_speed, set_speed);
+
+static int tv_probe(struct usb_interface *interface,
+                   const struct usb_device_id *id)
+{
+       struct usb_device *udev = interface_to_usbdev(interface);
+       struct trancevibrator *dev;
+       int retval;
+
+       dev = kzalloc(sizeof(struct trancevibrator), GFP_KERNEL);
+       if (dev == NULL) {
+               dev_err(&interface->dev, "Out of memory\n");
+               retval = -ENOMEM;
+               goto error;
+       }
+
+       dev->udev = usb_get_dev(udev);
+       usb_set_intfdata(interface, dev);
+       retval = device_create_file(&interface->dev, &dev_attr_speed);
+       if (retval)
+               goto error_create_file;
+
+       return 0;
+
+error_create_file:
+       usb_put_dev(udev);
+       usb_set_intfdata(interface, NULL);
+error:
+       kfree(dev);
+       return retval;
+}
+
+static void tv_disconnect(struct usb_interface *interface)
+{
+       struct trancevibrator *dev;
+
+       dev = usb_get_intfdata (interface);
+       usb_set_intfdata(interface, NULL);
+       device_remove_file(&interface->dev, &dev_attr_speed);
+       usb_put_dev(dev->udev);
+       kfree(dev);
+}
+
+/* USB subsystem object */
+static struct usb_driver tv_driver = {
+       .name =         "trancevibrator",
+       .probe =        tv_probe,
+       .disconnect =   tv_disconnect,
+       .id_table =     id_table,
+};
+
+static int __init tv_init(void)
+{
+       int retval = usb_register(&tv_driver);
+       if (retval) {
+               err("usb_register failed. Error number %d", retval);
+               return retval;
+       }
+
+       info(DRIVER_VERSION ":" DRIVER_DESC);
+       return 0;
+}
+
+static void __exit tv_exit(void)
+{
+       usb_deregister(&tv_driver);
+}
+
+module_init (tv_init);
+module_exit (tv_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
index 446935b..0fb792b 100644 (file)
@@ -218,7 +218,7 @@ static void usb_mouse_disconnect(struct usb_interface *intf)
 
 static struct usb_device_id usb_mouse_id_table [] = {
        { USB_INTERFACE_INFO(3, 1, 2) },
-    { }                                                /* Terminating entry */
+       { }     /* Terminating entry */
 };
 
 MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
index a338bf4..4640d10 100644 (file)
@@ -2,9 +2,12 @@
  * usbtouchscreen.c
  * Driver for USB Touchscreens, supporting those devices:
  *  - eGalax Touchkit
- *  - 3M/Microtouch
+ *    includes eTurboTouch CT-410/510/700
+ *  - 3M/Microtouch  EX II series
  *  - ITM
  *  - PanJit TouchSet
+ *  - eTurboTouch
+ *  - Gunze AHL61
  *
  * Copyright (C) 2004-2006 by Daniel Ritz <daniel.ritz@gmx.ch>
  * Copyright (C) by Todd E. Johnson (mtouchusb.c)
@@ -42,7 +45,7 @@
 #include <linux/usb/input.h>
 
 
-#define DRIVER_VERSION         "v0.3"
+#define DRIVER_VERSION         "v0.4"
 #define DRIVER_AUTHOR          "Daniel Ritz <daniel.ritz@gmx.ch>"
 #define DRIVER_DESC            "USB Touchscreen Driver"
 
@@ -60,6 +63,7 @@ struct usbtouch_device_info {
        int flags;
 
        void (*process_pkt) (struct usbtouch_usb *usbtouch, struct pt_regs *regs, unsigned char *pkt, int len);
+       int  (*get_pkt_len) (unsigned char *pkt, int len);
        int  (*read_data)   (unsigned char *pkt, int *x, int *y, int *touch, int *press);
        int  (*init)        (struct usbtouch_usb *usbtouch);
 };
@@ -81,8 +85,16 @@ struct usbtouch_usb {
        char phys[64];
 };
 
-static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch,
-                                 struct pt_regs *regs, unsigned char *pkt, int len);
+
+#if defined(CONFIG_USB_TOUCHSCREEN_EGALAX) || defined(CONFIG_USB_TOUCHSCREEN_ETURBO)
+#define MULTI_PACKET
+#endif
+
+#ifdef MULTI_PACKET
+static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
+                                   struct pt_regs *regs,
+                                   unsigned char *pkt, int len);
+#endif
 
 /* device types */
 enum {
@@ -91,14 +103,19 @@ enum {
        DEVTYPE_PANJIT,
        DEVTYPE_3M,
        DEVTYPE_ITM,
+       DEVTYPE_ETURBO,
+       DEVTYPE_GUNZE,
 };
 
 static struct usb_device_id usbtouch_devices[] = {
 #ifdef CONFIG_USB_TOUCHSCREEN_EGALAX
        {USB_DEVICE(0x3823, 0x0001), .driver_info = DEVTYPE_EGALAX},
+       {USB_DEVICE(0x3823, 0x0002), .driver_info = DEVTYPE_EGALAX},
        {USB_DEVICE(0x0123, 0x0001), .driver_info = DEVTYPE_EGALAX},
        {USB_DEVICE(0x0eef, 0x0001), .driver_info = DEVTYPE_EGALAX},
        {USB_DEVICE(0x0eef, 0x0002), .driver_info = DEVTYPE_EGALAX},
+       {USB_DEVICE(0x1234, 0x0001), .driver_info = DEVTYPE_EGALAX},
+       {USB_DEVICE(0x1234, 0x0002), .driver_info = DEVTYPE_EGALAX},
 #endif
 
 #ifdef CONFIG_USB_TOUCHSCREEN_PANJIT
@@ -116,6 +133,14 @@ static struct usb_device_id usbtouch_devices[] = {
        {USB_DEVICE(0x0403, 0xf9e9), .driver_info = DEVTYPE_ITM},
 #endif
 
+#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO
+       {USB_DEVICE(0x1234, 0x5678), .driver_info = DEVTYPE_ETURBO},
+#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE
+       {USB_DEVICE(0x0637, 0x0001), .driver_info = DEVTYPE_GUNZE},
+#endif
+
        {}
 };
 
@@ -140,82 +165,23 @@ static int egalax_read_data(unsigned char *pkt, int *x, int *y, int *touch, int
        *touch = pkt[0] & 0x01;
 
        return 1;
-
 }
 
-static int egalax_get_pkt_len(unsigned char *buf)
+static int egalax_get_pkt_len(unsigned char *buf, int len)
 {
        switch (buf[0] & EGALAX_PKT_TYPE_MASK) {
        case EGALAX_PKT_TYPE_REPT:
                return 5;
 
        case EGALAX_PKT_TYPE_DIAG:
+               if (len < 2)
+                       return -1;
+
                return buf[1] + 2;
        }
 
        return 0;
 }
-
-static void egalax_process(struct usbtouch_usb *usbtouch, struct pt_regs *regs,
-                           unsigned char *pkt, int len)
-{
-       unsigned char *buffer;
-       int pkt_len, buf_len, pos;
-
-       /* if the buffer contains data, append */
-       if (unlikely(usbtouch->buf_len)) {
-               int tmp;
-
-               /* if only 1 byte in buffer, add another one to get length */
-               if (usbtouch->buf_len == 1)
-                       usbtouch->buffer[1] = pkt[0];
-
-               pkt_len = egalax_get_pkt_len(usbtouch->buffer);
-
-               /* unknown packet: drop everything */
-               if (!pkt_len)
-                       return;
-
-               /* append, process */
-               tmp = pkt_len - usbtouch->buf_len;
-               memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, tmp);
-               usbtouch_process_pkt(usbtouch, regs, usbtouch->buffer, pkt_len);
-
-               buffer = pkt + tmp;
-               buf_len = len - tmp;
-       } else {
-               buffer = pkt;
-               buf_len = len;
-       }
-
-       /* only one byte left in buffer */
-       if (unlikely(buf_len == 1)) {
-               usbtouch->buffer[0] = buffer[0];
-               usbtouch->buf_len = 1;
-               return;
-       }
-
-       /* loop over the buffer */
-       pos = 0;
-       while (pos < buf_len) {
-               /* get packet len */
-               pkt_len = egalax_get_pkt_len(buffer + pos);
-
-               /* unknown packet: drop everything */
-               if (unlikely(!pkt_len))
-                       return;
-
-               /* full packet: process */
-               if (likely(pkt_len <= buf_len)) {
-                       usbtouch_process_pkt(usbtouch, regs, buffer + pos, pkt_len);
-               } else {
-                       /* incomplete packet: save in buffer */
-                       memcpy(usbtouch->buffer, buffer + pos, buf_len - pos);
-                       usbtouch->buf_len = buf_len - pos;
-               }
-               pos += pkt_len;
-       }
-}
 #endif
 
 
@@ -254,7 +220,7 @@ static int mtouch_read_data(unsigned char *pkt, int *x, int *y, int *touch, int
 
 static int mtouch_init(struct usbtouch_usb *usbtouch)
 {
-       int ret;
+       int ret, i;
 
        ret = usb_control_msg(usbtouch->udev, usb_rcvctrlpipe(usbtouch->udev, 0),
                              MTOUCHUSB_RESET,
@@ -264,15 +230,20 @@ static int mtouch_init(struct usbtouch_usb *usbtouch)
            __FUNCTION__, ret);
        if (ret < 0)
                return ret;
-
-       ret = usb_control_msg(usbtouch->udev, usb_rcvctrlpipe(usbtouch->udev, 0),
-                             MTOUCHUSB_ASYNC_REPORT,
-                             USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-                             1, 1, NULL, 0, USB_CTRL_SET_TIMEOUT);
-       dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d",
-           __FUNCTION__, ret);
-       if (ret < 0)
-               return ret;
+       msleep(150);
+
+       for (i = 0; i < 3; i++) {
+               ret = usb_control_msg(usbtouch->udev, usb_rcvctrlpipe(usbtouch->udev, 0),
+                                     MTOUCHUSB_ASYNC_REPORT,
+                                     USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                                     1, 1, NULL, 0, USB_CTRL_SET_TIMEOUT);
+               dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d",
+                   __FUNCTION__, ret);
+               if (ret >= 0)
+                       break;
+               if (ret != -EPIPE)
+                       return ret;
+       }
 
        return 0;
 }
@@ -295,6 +266,54 @@ static int itm_read_data(unsigned char *pkt, int *x, int *y, int *touch, int *pr
 #endif
 
 
+/*****************************************************************************
+ * eTurboTouch part
+ */
+#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO
+static int eturbo_read_data(unsigned char *pkt, int *x, int *y, int *touch, int *press)
+{
+       unsigned int shift;
+
+       /* packets should start with sync */
+       if (!(pkt[0] & 0x80))
+               return 0;
+
+       shift = (6 - (pkt[0] & 0x03));
+       *x = ((pkt[3] << 7) | pkt[4]) >> shift;
+       *y = ((pkt[1] << 7) | pkt[2]) >> shift;
+       *touch = (pkt[0] & 0x10) ? 1 : 0;
+
+       return 1;
+}
+
+static int eturbo_get_pkt_len(unsigned char *buf, int len)
+{
+       if (buf[0] & 0x80)
+               return 5;
+       if (buf[0] == 0x01)
+               return 3;
+       return 0;
+}
+#endif
+
+
+/*****************************************************************************
+ * Gunze part
+ */
+#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE
+static int gunze_read_data(unsigned char *pkt, int *x, int *y, int *touch, int *press)
+{
+       if (!(pkt[0] & 0x80) || ((pkt[1] | pkt[2] | pkt[3]) & 0x80))
+               return 0;
+
+       *x = ((pkt[0] & 0x1F) << 7) | (pkt[2] & 0x7F);
+       *y = ((pkt[1] & 0x1F) << 7) | (pkt[3] & 0x7F);
+       *touch = pkt[0] & 0x20;
+
+       return 1;
+}
+#endif
+
 /*****************************************************************************
  * the different device descriptors
  */
@@ -307,7 +326,8 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
                .max_yc         = 0x07ff,
                .rept_size      = 16,
                .flags          = USBTOUCH_FLG_BUFFER,
-               .process_pkt    = egalax_process,
+               .process_pkt    = usbtouch_process_multi,
+               .get_pkt_len    = egalax_get_pkt_len,
                .read_data      = egalax_read_data,
        },
 #endif
@@ -346,6 +366,31 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
                .read_data      = itm_read_data,
        },
 #endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO
+       [DEVTYPE_ETURBO] = {
+               .min_xc         = 0x0,
+               .max_xc         = 0x07ff,
+               .min_yc         = 0x0,
+               .max_yc         = 0x07ff,
+               .rept_size      = 8,
+               .flags          = USBTOUCH_FLG_BUFFER,
+               .process_pkt    = usbtouch_process_multi,
+               .get_pkt_len    = eturbo_get_pkt_len,
+               .read_data      = eturbo_read_data,
+       },
+#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE
+       [DEVTYPE_GUNZE] = {
+               .min_xc         = 0x0,
+               .max_xc         = 0x0fff,
+               .min_yc         = 0x0,
+               .max_yc         = 0x0fff,
+               .rept_size      = 4,
+               .read_data      = gunze_read_data,
+       },
+#endif
 };
 
 
@@ -377,6 +422,83 @@ static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch,
 }
 
 
+#ifdef MULTI_PACKET
+static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
+                                   struct pt_regs *regs,
+                                   unsigned char *pkt, int len)
+{
+       unsigned char *buffer;
+       int pkt_len, pos, buf_len, tmp;
+
+       /* process buffer */
+       if (unlikely(usbtouch->buf_len)) {
+               /* try to get size */
+               pkt_len = usbtouch->type->get_pkt_len(
+                               usbtouch->buffer, usbtouch->buf_len);
+
+               /* drop? */
+               if (unlikely(!pkt_len))
+                       goto out_flush_buf;
+
+               /* need to append -pkt_len bytes before able to get size */
+               if (unlikely(pkt_len < 0)) {
+                       int append = -pkt_len;
+                       if (unlikely(append > len))
+                              append = len;
+                       if (usbtouch->buf_len + append >= usbtouch->type->rept_size)
+                               goto out_flush_buf;
+                       memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, append);
+                       usbtouch->buf_len += append;
+
+                       pkt_len = usbtouch->type->get_pkt_len(
+                                       usbtouch->buffer, usbtouch->buf_len);
+                       if (pkt_len < 0)
+                               return;
+               }
+
+               /* append */
+               tmp = pkt_len - usbtouch->buf_len;
+               if (usbtouch->buf_len + tmp >= usbtouch->type->rept_size)
+                       goto out_flush_buf;
+               memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, tmp);
+               usbtouch_process_pkt(usbtouch, regs, usbtouch->buffer, pkt_len);
+
+               buffer = pkt + tmp;
+               buf_len = len - tmp;
+       } else {
+               buffer = pkt;
+               buf_len = len;
+       }
+
+       /* loop over the received packet, process */
+       pos = 0;
+       while (pos < buf_len) {
+               /* get packet len */
+               pkt_len = usbtouch->type->get_pkt_len(buffer + pos, len);
+
+               /* unknown packet: drop everything */
+               if (unlikely(!pkt_len))
+                       goto out_flush_buf;
+
+               /* full packet: process */
+               if (likely((pkt_len > 0) && (pkt_len <= buf_len - pos))) {
+                       usbtouch_process_pkt(usbtouch, regs, buffer + pos, pkt_len);
+               } else {
+                       /* incomplete packet: save in buffer */
+                       memcpy(usbtouch->buffer, buffer + pos, buf_len - pos);
+                       usbtouch->buf_len = buf_len - pos;
+                       return;
+               }
+               pos += pkt_len;
+       }
+
+out_flush_buf:
+       usbtouch->buf_len = 0;
+       return;
+}
+#endif
+
+
 static void usbtouch_irq(struct urb *urb, struct pt_regs *regs)
 {
        struct usbtouch_usb *usbtouch = urb->context;
@@ -386,7 +508,7 @@ static void usbtouch_irq(struct urb *urb, struct pt_regs *regs)
        case 0:
                /* success */
                break;
-       case -ETIMEDOUT:
+       case -ETIME:
                /* this urb is timing out */
                dbg("%s - urb timed out - was the device unplugged?",
                    __FUNCTION__);
@@ -452,7 +574,7 @@ static int usbtouch_probe(struct usb_interface *intf,
        struct usb_endpoint_descriptor *endpoint;
        struct usb_device *udev = interface_to_usbdev(intf);
        struct usbtouch_device_info *type;
-       int err;
+       int err = -ENOMEM;
 
        interface = intf->cur_altsetting;
        endpoint = &interface->endpoint[0].desc;
@@ -526,6 +648,7 @@ static int usbtouch_probe(struct usb_interface *intf,
                         usbtouch->data, type->rept_size,
                         usbtouch_irq, usbtouch, endpoint->bInterval);
 
+       usbtouch->irq->dev = usbtouch->udev;
        usbtouch->irq->transfer_dma = usbtouch->data_dma;
        usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 
@@ -553,7 +676,7 @@ out_free_buffers:
 out_free:
        input_free_device(input_dev);
        kfree(usbtouch);
-       return -ENOMEM;
+       return err;
 }
 
 static void usbtouch_disconnect(struct usb_interface *intf)
diff --git a/drivers/usb/input/wacom.c b/drivers/usb/input/wacom.c
deleted file mode 100644 (file)
index 369461a..0000000
+++ /dev/null
@@ -1,1003 +0,0 @@
-/*
- *  USB Wacom Graphire and Wacom Intuos tablet support
- *
- *  Copyright (c) 2000-2004 Vojtech Pavlik     <vojtech@ucw.cz>
- *  Copyright (c) 2000 Andreas Bach Aaen       <abach@stofanet.dk>
- *  Copyright (c) 2000 Clifford Wolf           <clifford@clifford.at>
- *  Copyright (c) 2000 Sam Mosel               <sam.mosel@computer.org>
- *  Copyright (c) 2000 James E. Blair          <corvus@gnu.org>
- *  Copyright (c) 2000 Daniel Egger            <egger@suse.de>
- *  Copyright (c) 2001 Frederic Lepied         <flepied@mandrakesoft.com>
- *  Copyright (c) 2004 Panagiotis Issaris      <panagiotis.issaris@mech.kuleuven.ac.be>
- *  Copyright (c) 2002-2006 Ping Cheng         <pingc@wacom.com>
- *
- *  ChangeLog:
- *      v0.1 (vp)  - Initial release
- *      v0.2 (aba) - Support for all buttons / combinations
- *      v0.3 (vp)  - Support for Intuos added
- *     v0.4 (sm)  - Support for more Intuos models, menustrip
- *                     relative mode, proximity.
- *     v0.5 (vp)  - Big cleanup, nifty features removed,
- *                     they belong in userspace
- *     v1.8 (vp)  - Submit URB only when operating, moved to CVS,
- *                     use input_report_key instead of report_btn and
- *                     other cleanups
- *     v1.11 (vp) - Add URB ->dev setting for new kernels
- *     v1.11 (jb) - Add support for the 4D Mouse & Lens
- *     v1.12 (de) - Add support for two more inking pen IDs
- *     v1.14 (vp) - Use new USB device id probing scheme.
- *                  Fix Wacom Graphire mouse wheel
- *     v1.18 (vp) - Fix mouse wheel direction
- *                  Make mouse relative
- *      v1.20 (fl) - Report tool id for Intuos devices
- *                 - Multi tools support
- *                 - Corrected Intuos protocol decoding (airbrush, 4D mouse, lens cursor...)
- *                 - Add PL models support
- *                - Fix Wacom Graphire mouse wheel again
- *     v1.21 (vp) - Removed protocol descriptions
- *                - Added MISC_SERIAL for tool serial numbers
- *           (gb) - Identify version on module load.
- *    v1.21.1 (fl) - added Graphire2 support
- *    v1.21.2 (fl) - added Intuos2 support
- *                 - added all the PL ids
- *    v1.21.3 (fl) - added another eraser id from Neil Okamoto
- *                 - added smooth filter for Graphire from Peri Hankey
- *                 - added PenPartner support from Olaf van Es
- *                 - new tool ids from Ole Martin Bjoerndalen
- *     v1.29 (pc) - Add support for more tablets
- *                - Fix pressure reporting
- *     v1.30 (vp) - Merge 2.4 and 2.5 drivers
- *                - Since 2.5 now has input_sync(), remove MSC_SERIAL abuse
- *                - Cleanups here and there
- *    v1.30.1 (pi) - Added Graphire3 support
- *     v1.40 (pc) - Add support for several new devices, fix eraser reporting, ...
- *     v1.43 (pc) - Added support for Cintiq 21UX
- *                - Fixed a Graphire bug
- *                - Merged wacom_intuos3_irq into wacom_intuos_irq
- *     v1.44 (pc) - Added support for Graphire4, Cintiq 710, Intuos3 6x11, etc.
- *                - Report Device IDs
- *     v1.45 (pc) - Added support for DTF 521, Intuos3 12x12 and 12x19
- *                - Minor data report fix
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/usb/input.h>
-#include <asm/unaligned.h>
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v1.45"
-#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
-#define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver"
-#define DRIVER_LICENSE "GPL"
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE(DRIVER_LICENSE);
-
-#define USB_VENDOR_ID_WACOM    0x056a
-#define STYLUS_DEVICE_ID       0x02
-#define CURSOR_DEVICE_ID       0x06
-#define ERASER_DEVICE_ID       0x0A
-
-enum {
-       PENPARTNER = 0,
-       GRAPHIRE,
-       WACOM_G4,
-       PL,
-       INTUOS,
-       INTUOS3,
-       INTUOS312,
-       INTUOS319,
-       CINTIQ,
-       MAX_TYPE
-};
-
-struct wacom_features {
-       char *name;
-       int pktlen;
-       int x_max;
-       int y_max;
-       int pressure_max;
-       int distance_max;
-       int type;
-       usb_complete_t irq;
-};
-
-struct wacom {
-       signed char *data;
-       dma_addr_t data_dma;
-       struct input_dev *dev;
-       struct usb_device *usbdev;
-       struct urb *irq;
-       struct wacom_features *features;
-       int tool[2];
-       int id[2];
-       __u32 serial[2];
-       char phys[32];
-};
-
-#define USB_REQ_GET_REPORT     0x01
-#define USB_REQ_SET_REPORT     0x09
-
-static int usb_get_report(struct usb_interface *intf, unsigned char type,
-                               unsigned char id, void *buf, int size)
-{
-       return usb_control_msg(interface_to_usbdev(intf),
-               usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
-               USB_REQ_GET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-               (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
-               buf, size, 100);
-}
-
-static int usb_set_report(struct usb_interface *intf, unsigned char type,
-                               unsigned char id, void *buf, int size)
-{
-       return usb_control_msg(interface_to_usbdev(intf),
-               usb_sndctrlpipe(interface_to_usbdev(intf), 0),
-                USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-                (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
-               buf, size, 1000);
-}
-
-static void wacom_pl_irq(struct urb *urb, struct pt_regs *regs)
-{
-       struct wacom *wacom = urb->context;
-       unsigned char *data = wacom->data;
-       struct input_dev *dev = wacom->dev;
-       int prox, pressure, id;
-       int retval;
-
-       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", __FUNCTION__, urb->status);
-               return;
-       default:
-               dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
-               goto exit;
-       }
-
-       if (data[0] != 2) {
-               dbg("wacom_pl_irq: received unknown report #%d", data[0]);
-               goto exit;
-       }
-
-       prox = data[1] & 0x40;
-
-       input_regs(dev, regs);
-
-       id = ERASER_DEVICE_ID;
-       if (prox) {
-
-               pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
-               if (wacom->features->pressure_max > 255)
-                       pressure = (pressure << 1) | ((data[4] >> 6) & 1);
-               pressure += (wacom->features->pressure_max + 1) / 2;
-
-               /*
-                * if going from out of proximity into proximity select between the eraser
-                * and the pen based on the state of the stylus2 button, choose eraser if
-                * pressed else choose pen. if not a proximity change from out to in, send
-                * an out of proximity for previous tool then a in for new tool.
-                */
-               if (!wacom->tool[0]) {
-                       /* Eraser bit set for DTF */
-                       if (data[1] & 0x10)
-                               wacom->tool[1] = BTN_TOOL_RUBBER;
-                       else
-                               /* Going into proximity select tool */
-                               wacom->tool[1] = (data[4] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
-               } else {
-                       /* was entered with stylus2 pressed */
-                       if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) {
-                               /* report out proximity for previous tool */
-                               input_report_key(dev, wacom->tool[1], 0);
-                               input_sync(dev);
-                               wacom->tool[1] = BTN_TOOL_PEN;
-                               goto exit;
-                       }
-               }
-               if (wacom->tool[1] != BTN_TOOL_RUBBER) {
-                       /* Unknown tool selected default to pen tool */
-                       wacom->tool[1] = BTN_TOOL_PEN;
-                       id = STYLUS_DEVICE_ID;
-               }
-               input_report_key(dev, wacom->tool[1], prox); /* report in proximity for tool */
-               input_report_abs(dev, ABS_MISC, id); /* report tool id */
-               input_report_abs(dev, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
-               input_report_abs(dev, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
-               input_report_abs(dev, ABS_PRESSURE, pressure);
-
-               input_report_key(dev, BTN_TOUCH, data[4] & 0x08);
-               input_report_key(dev, BTN_STYLUS, data[4] & 0x10);
-               /* Only allow the stylus2 button to be reported for the pen tool. */
-               input_report_key(dev, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20));
-       } else {
-               /* report proximity-out of a (valid) tool */
-               if (wacom->tool[1] != BTN_TOOL_RUBBER) {
-                       /* Unknown tool selected default to pen tool */
-                       wacom->tool[1] = BTN_TOOL_PEN;
-               }
-               input_report_key(dev, wacom->tool[1], prox);
-       }
-
-       wacom->tool[0] = prox; /* Save proximity state */
-       input_sync(dev);
-
- exit:
-       retval = usb_submit_urb (urb, GFP_ATOMIC);
-       if (retval)
-               err ("%s - usb_submit_urb failed with result %d",
-                    __FUNCTION__, retval);
-}
-
-static void wacom_ptu_irq(struct urb *urb, struct pt_regs *regs)
-{
-       struct wacom *wacom = urb->context;
-       unsigned char *data = wacom->data;
-       struct input_dev *dev = wacom->dev;
-       int retval, id;
-
-       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", __FUNCTION__, urb->status);
-               return;
-       default:
-               dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
-               goto exit;
-       }
-
-       if (data[0] != 2) {
-               printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]);
-               goto exit;
-       }
-
-       input_regs(dev, regs);
-       if (data[1] & 0x04) {
-               input_report_key(dev, BTN_TOOL_RUBBER, data[1] & 0x20);
-               input_report_key(dev, BTN_TOUCH, data[1] & 0x08);
-               id = ERASER_DEVICE_ID;
-       } else {
-               input_report_key(dev, BTN_TOOL_PEN, data[1] & 0x20);
-               input_report_key(dev, BTN_TOUCH, data[1] & 0x01);
-               id = STYLUS_DEVICE_ID;
-       }
-       input_report_abs(dev, ABS_MISC, id); /* report tool id */
-       input_report_abs(dev, ABS_X, le16_to_cpu(*(__le16 *) &data[2]));
-       input_report_abs(dev, ABS_Y, le16_to_cpu(*(__le16 *) &data[4]));
-       input_report_abs(dev, ABS_PRESSURE, le16_to_cpu(*(__le16 *) &data[6]));
-       input_report_key(dev, BTN_STYLUS, data[1] & 0x02);
-       input_report_key(dev, BTN_STYLUS2, data[1] & 0x10);
-
-       input_sync(dev);
-
- exit:
-       retval = usb_submit_urb (urb, GFP_ATOMIC);
-       if (retval)
-               err ("%s - usb_submit_urb failed with result %d",
-                    __FUNCTION__, retval);
-}
-
-static void wacom_penpartner_irq(struct urb *urb, struct pt_regs *regs)
-{
-       struct wacom *wacom = urb->context;
-       unsigned char *data = wacom->data;
-       struct input_dev *dev = wacom->dev;
-       int retval;
-
-       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", __FUNCTION__, urb->status);
-               return;
-       default:
-               dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
-               goto exit;
-       }
-
-       if (data[0] != 2) {
-               printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]);
-               goto exit;
-       }
-
-       input_regs(dev, regs);
-       input_report_key(dev, BTN_TOOL_PEN, 1);
-       input_report_abs(dev, ABS_MISC, STYLUS_DEVICE_ID); /* report tool id */
-       input_report_abs(dev, ABS_X, le16_to_cpu(*(__le16 *) &data[1]));
-       input_report_abs(dev, ABS_Y, le16_to_cpu(*(__le16 *) &data[3]));
-       input_report_abs(dev, ABS_PRESSURE, (signed char)data[6] + 127);
-       input_report_key(dev, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20));
-       input_report_key(dev, BTN_STYLUS, (data[5] & 0x40));
-       input_sync(dev);
-
- exit:
-       retval = usb_submit_urb (urb, GFP_ATOMIC);
-       if (retval)
-               err ("%s - usb_submit_urb failed with result %d",
-                    __FUNCTION__, retval);
-}
-
-static void wacom_graphire_irq(struct urb *urb, struct pt_regs *regs)
-{
-       struct wacom *wacom = urb->context;
-       unsigned char *data = wacom->data;
-       struct input_dev *dev = wacom->dev;
-       int x, y, id, rw;
-       int retval;
-
-       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", __FUNCTION__, urb->status);
-               return;
-       default:
-               dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
-               goto exit;
-       }
-
-       if (data[0] == 99) return; /* for Volito tablets */
-
-       if (data[0] != 2) {
-               dbg("wacom_graphire_irq: received unknown report #%d", data[0]);
-               goto exit;
-       }
-
-       input_regs(dev, regs);
-
-       id = STYLUS_DEVICE_ID;
-       if (data[1] & 0x10) { /* in prox */
-
-               switch ((data[1] >> 5) & 3) {
-
-                       case 0: /* Pen */
-                               wacom->tool[0] = BTN_TOOL_PEN;
-                               break;
-
-                       case 1: /* Rubber */
-                               wacom->tool[0] = BTN_TOOL_RUBBER;
-                               id = ERASER_DEVICE_ID;
-                               break;
-
-                       case 2: /* Mouse with wheel */
-                               input_report_key(dev, BTN_MIDDLE, data[1] & 0x04);
-                               if (wacom->features->type == WACOM_G4) {
-                                       rw = data[7] & 0x04 ? (data[7] & 0x03)-4 : (data[7] & 0x03);
-                                       input_report_rel(dev, REL_WHEEL, -rw);
-                               } else
-                                       input_report_rel(dev, REL_WHEEL, -(signed char) data[6]);
-                               /* fall through */
-
-                       case 3: /* Mouse without wheel */
-                               wacom->tool[0] = BTN_TOOL_MOUSE;
-                               id = CURSOR_DEVICE_ID;
-                               input_report_key(dev, BTN_LEFT, data[1] & 0x01);
-                               input_report_key(dev, BTN_RIGHT, data[1] & 0x02);
-                               if (wacom->features->type == WACOM_G4)
-                                       input_report_abs(dev, ABS_DISTANCE, data[6]);
-                               else
-                                       input_report_abs(dev, ABS_DISTANCE, data[7]);
-                               break;
-               }
-       }
-
-       if (data[1] & 0x90) {
-               x = le16_to_cpu(*(__le16 *) &data[2]);
-               y = le16_to_cpu(*(__le16 *) &data[4]);
-               input_report_abs(dev, ABS_X, x);
-               input_report_abs(dev, ABS_Y, y);
-               if (wacom->tool[0] != BTN_TOOL_MOUSE) {
-                       input_report_abs(dev, ABS_PRESSURE, data[6] | ((data[7] & 0x01) << 8));
-                       input_report_key(dev, BTN_TOUCH, data[1] & 0x01);
-                       input_report_key(dev, BTN_STYLUS, data[1] & 0x02);
-                       input_report_key(dev, BTN_STYLUS2, data[1] & 0x04);
-               }
-       }
-
-       if (data[1] & 0x10)
-               input_report_abs(dev, ABS_MISC, id); /* report tool id */
-       else
-               input_report_abs(dev, ABS_MISC, 0); /* reset tool id */
-       input_report_key(dev, wacom->tool[0], data[1] & 0x10);
-       input_sync(dev);
-
-       /* send pad data */
-       if (wacom->features->type == WACOM_G4) {
-               if ((wacom->serial[1] & 0xc0) != (data[7] & 0xf8)) {
-                       wacom->id[1] = 1;
-                       wacom->serial[1] = (data[7] & 0xf8);
-                       input_report_key(dev, BTN_0, (data[7] & 0x40));
-                       input_report_key(dev, BTN_4, (data[7] & 0x80));
-                       rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3);
-                       input_report_rel(dev, REL_WHEEL, rw);
-                       input_report_key(dev, BTN_TOOL_FINGER, 0xf0);
-                       input_event(dev, EV_MSC, MSC_SERIAL, 0xf0);
-               } else if (wacom->id[1]) {
-                       wacom->id[1] = 0;
-                       input_report_key(dev, BTN_TOOL_FINGER, 0);
-                       input_event(dev, EV_MSC, MSC_SERIAL, 0xf0);
-               }
-               input_sync(dev);
-       }
- exit:
-       retval = usb_submit_urb (urb, GFP_ATOMIC);
-       if (retval)
-               err ("%s - usb_submit_urb failed with result %d",
-                    __FUNCTION__, retval);
-}
-
-static int wacom_intuos_inout(struct urb *urb)
-{
-       struct wacom *wacom = urb->context;
-       unsigned char *data = wacom->data;
-       struct input_dev *dev = wacom->dev;
-       int idx;
-
-       /* tool number */
-       idx = data[1] & 0x01;
-
-       /* Enter report */
-       if ((data[1] & 0xfc) == 0xc0) {
-               /* serial number of the tool */
-               wacom->serial[idx] = ((data[3] & 0x0f) << 28) +
-                       (data[4] << 20) + (data[5] << 12) +
-                       (data[6] << 4) + (data[7] >> 4);
-
-               wacom->id[idx] = (data[2] << 4) | (data[3] >> 4);
-               switch (wacom->id[idx]) {
-                       case 0x812: /* Inking pen */
-                       case 0x801: /* Intuos3 Inking pen */
-                       case 0x012:
-                               wacom->tool[idx] = BTN_TOOL_PENCIL;
-                               break;
-                       case 0x822: /* Pen */
-                       case 0x842:
-                       case 0x852:
-                       case 0x823: /* Intuos3 Grip Pen */
-                       case 0x813: /* Intuos3 Classic Pen */
-                       case 0x885: /* Intuos3 Marker Pen */
-                       case 0x022:
-                               wacom->tool[idx] = BTN_TOOL_PEN;
-                               break;
-                       case 0x832: /* Stroke pen */
-                       case 0x032:
-                               wacom->tool[idx] = BTN_TOOL_BRUSH;
-                               break;
-                       case 0x007: /* Mouse 4D and 2D */
-                       case 0x09c:
-                       case 0x094:
-                       case 0x017: /* Intuos3 2D Mouse */
-                               wacom->tool[idx] = BTN_TOOL_MOUSE;
-                               break;
-                       case 0x096: /* Lens cursor */
-                       case 0x097: /* Intuos3 Lens cursor */
-                               wacom->tool[idx] = BTN_TOOL_LENS;
-                               break;
-                       case 0x82a: /* Eraser */
-                       case 0x85a:
-                       case 0x91a:
-                       case 0xd1a:
-                       case 0x0fa:
-                       case 0x82b: /* Intuos3 Grip Pen Eraser */
-                       case 0x81b: /* Intuos3 Classic Pen Eraser */
-                       case 0x91b: /* Intuos3 Airbrush Eraser */
-                               wacom->tool[idx] = BTN_TOOL_RUBBER;
-                               break;
-                       case 0xd12:
-                       case 0x912:
-                       case 0x112:
-                       case 0x913: /* Intuos3 Airbrush */
-                               wacom->tool[idx] = BTN_TOOL_AIRBRUSH;
-                               break;
-                       default: /* Unknown tool */
-                               wacom->tool[idx] = BTN_TOOL_PEN;
-               }
-               if(!((wacom->tool[idx] == BTN_TOOL_LENS) &&
-                               ((wacom->features->type == INTUOS312)
-                                       || (wacom->features->type == INTUOS319)))) {
-                       input_report_abs(dev, ABS_MISC, wacom->id[idx]); /* report tool id */
-                       input_report_key(dev, wacom->tool[idx], 1);
-                       input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
-                       input_sync(dev);
-               }
-               return 1;
-       }
-
-       /* Exit report */
-       if ((data[1] & 0xfe) == 0x80) {
-               input_report_key(dev, wacom->tool[idx], 0);
-               input_report_abs(dev, ABS_MISC, 0); /* reset tool id */
-               input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
-               input_sync(dev);
-               return 1;
-       }
-
-       if((wacom->tool[idx] == BTN_TOOL_LENS) && ((wacom->features->type == INTUOS312)
-                       || (wacom->features->type == INTUOS319)))
-               return 1;
-       else
-               return 0;
-}
-
-static void wacom_intuos_general(struct urb *urb)
-{
-       struct wacom *wacom = urb->context;
-       unsigned char *data = wacom->data;
-       struct input_dev *dev = wacom->dev;
-       unsigned int t;
-
-       /* general pen packet */
-       if ((data[1] & 0xb8) == 0xa0) {
-               t = (data[6] << 2) | ((data[7] >> 6) & 3);
-               input_report_abs(dev, ABS_PRESSURE, t);
-               input_report_abs(dev, ABS_TILT_X,
-                               ((data[7] << 1) & 0x7e) | (data[8] >> 7));
-               input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
-               input_report_key(dev, BTN_STYLUS, data[1] & 2);
-               input_report_key(dev, BTN_STYLUS2, data[1] & 4);
-               input_report_key(dev, BTN_TOUCH, t > 10);
-       }
-
-       /* airbrush second packet */
-       if ((data[1] & 0xbc) == 0xb4) {
-               input_report_abs(dev, ABS_WHEEL,
-                               (data[6] << 2) | ((data[7] >> 6) & 3));
-               input_report_abs(dev, ABS_TILT_X,
-                               ((data[7] << 1) & 0x7e) | (data[8] >> 7));
-               input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
-       }
-       return;
-}
-
-static void wacom_intuos_irq(struct urb *urb, struct pt_regs *regs)
-{
-       struct wacom *wacom = urb->context;
-       unsigned char *data = wacom->data;
-       struct input_dev *dev = wacom->dev;
-       unsigned int t;
-       int idx;
-       int retval;
-
-       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", __FUNCTION__, urb->status);
-               return;
-       default:
-               dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
-               goto exit;
-       }
-
-       if (data[0] != 2 && data[0] != 5 && data[0] != 6 && data[0] != 12) {
-               dbg("wacom_intuos_irq: received unknown report #%d", data[0]);
-               goto exit;
-       }
-
-       input_regs(dev, regs);
-
-       /* tool number */
-       idx = data[1] & 0x01;
-
-       /* pad packets. Works as a second tool and is always in prox */
-       if (data[0] == 12) {
-               /* initiate the pad as a device */
-               if (wacom->tool[1] != BTN_TOOL_FINGER)
-                       wacom->tool[1] = BTN_TOOL_FINGER;
-
-               input_report_key(dev, BTN_0, (data[5] & 0x01));
-               input_report_key(dev, BTN_1, (data[5] & 0x02));
-               input_report_key(dev, BTN_2, (data[5] & 0x04));
-               input_report_key(dev, BTN_3, (data[5] & 0x08));
-               input_report_key(dev, BTN_4, (data[6] & 0x01));
-               input_report_key(dev, BTN_5, (data[6] & 0x02));
-               input_report_key(dev, BTN_6, (data[6] & 0x04));
-               input_report_key(dev, BTN_7, (data[6] & 0x08));
-               input_report_abs(dev, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]);
-               input_report_abs(dev, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
-
-               if((data[5] & 0x0f) | (data[6] & 0x0f) | (data[1] & 0x1f) | data[2])
-                       input_report_key(dev, wacom->tool[1], 1);
-               else
-                       input_report_key(dev, wacom->tool[1], 0);
-               input_event(dev, EV_MSC, MSC_SERIAL, 0xffffffff);
-               input_sync(dev);
-               goto exit;
-       }
-
-       /* process in/out prox events */
-       if (wacom_intuos_inout(urb))
-               goto exit;
-
-       /* Cintiq doesn't send data when RDY bit isn't set */
-       if ((wacom->features->type == CINTIQ) && !(data[1] & 0x40))
-               goto exit;
-
-       if (wacom->features->type >= INTUOS3) {
-               input_report_abs(dev, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1));
-               input_report_abs(dev, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1));
-               input_report_abs(dev, ABS_DISTANCE, ((data[9] >> 2) & 0x3f));
-       } else {
-               input_report_abs(dev, ABS_X, be16_to_cpu(*(__be16 *) &data[2]));
-               input_report_abs(dev, ABS_Y, be16_to_cpu(*(__be16 *) &data[4]));
-               input_report_abs(dev, ABS_DISTANCE, ((data[9] >> 3) & 0x1f));
-       }
-
-       /* process general packets */
-       wacom_intuos_general(urb);
-
-       /* 4D mouse, 2D mouse, marker pen rotation, or Lens cursor packets */
-       if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) {
-
-               if (data[1] & 0x02) {
-                       /* Rotation packet */
-                       if (wacom->features->type >= INTUOS3) {
-                               /* I3 marker pen rotation reported as wheel
-                                * due to valuator limitation
-                                */
-                               t = (data[6] << 3) | ((data[7] >> 5) & 7);
-                               t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) :
-                                       ((t-1) / 2 + 450)) : (450 - t / 2) ;
-                               input_report_abs(dev, ABS_WHEEL, t);
-                       } else {
-                               /* 4D mouse rotation packet */
-                               t = (data[6] << 3) | ((data[7] >> 5) & 7);
-                               input_report_abs(dev, ABS_RZ, (data[7] & 0x20) ?
-                                       ((t - 1) / 2) : -t / 2);
-                       }
-
-               } else if (!(data[1] & 0x10) && wacom->features->type < INTUOS3) {
-                       /* 4D mouse packet */
-                       input_report_key(dev, BTN_LEFT,   data[8] & 0x01);
-                       input_report_key(dev, BTN_MIDDLE, data[8] & 0x02);
-                       input_report_key(dev, BTN_RIGHT,  data[8] & 0x04);
-
-                       input_report_key(dev, BTN_SIDE,   data[8] & 0x20);
-                       input_report_key(dev, BTN_EXTRA,  data[8] & 0x10);
-                       t = (data[6] << 2) | ((data[7] >> 6) & 3);
-                       input_report_abs(dev, ABS_THROTTLE, (data[8] & 0x08) ? -t : t);
-
-               } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) {
-                       /* 2D mouse packet */
-                       input_report_key(dev, BTN_LEFT,   data[8] & 0x04);
-                       input_report_key(dev, BTN_MIDDLE, data[8] & 0x08);
-                       input_report_key(dev, BTN_RIGHT,  data[8] & 0x10);
-                       input_report_rel(dev, REL_WHEEL, (data[8] & 0x01)
-                                                - ((data[8] & 0x02) >> 1));
-
-                       /* I3 2D mouse side buttons */
-                       if (wacom->features->type == INTUOS3) {
-                               input_report_key(dev, BTN_SIDE,   data[8] & 0x40);
-                               input_report_key(dev, BTN_EXTRA,  data[8] & 0x20);
-                       }
-
-               } else if (wacom->features->type < INTUOS3) {
-                       /* Lens cursor packets */
-                       input_report_key(dev, BTN_LEFT,   data[8] & 0x01);
-                       input_report_key(dev, BTN_MIDDLE, data[8] & 0x02);
-                       input_report_key(dev, BTN_RIGHT,  data[8] & 0x04);
-                       input_report_key(dev, BTN_SIDE,   data[8] & 0x10);
-                       input_report_key(dev, BTN_EXTRA,  data[8] & 0x08);
-               }
-       }
-
-       input_report_abs(dev, ABS_MISC, wacom->id[idx]); /* report tool id */
-       input_report_key(dev, wacom->tool[idx], 1);
-       input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
-       input_sync(dev);
-
-exit:
-       retval = usb_submit_urb (urb, GFP_ATOMIC);
-       if (retval)
-               err ("%s - usb_submit_urb failed with result %d",
-                   __FUNCTION__, retval);
-}
-
-static struct wacom_features wacom_features[] = {
-       { "Wacom Penpartner",    7,   5040,  3780,  255, 32, PENPARTNER, wacom_penpartner_irq },
-        { "Wacom Graphire",      8,  10206,  7422,  511, 32, GRAPHIRE,   wacom_graphire_irq },
-       { "Wacom Graphire2 4x5", 8,  10206,  7422,  511, 32, GRAPHIRE,   wacom_graphire_irq },
-       { "Wacom Graphire2 5x7", 8,  13918, 10206,  511, 32, GRAPHIRE,   wacom_graphire_irq },
-       { "Wacom Graphire3",     8,  10208,  7424,  511, 32, GRAPHIRE,   wacom_graphire_irq },
-       { "Wacom Graphire3 6x8", 8,  16704, 12064,  511, 32, GRAPHIRE,   wacom_graphire_irq },
-       { "Wacom Graphire4 4x5", 8,  10208,  7424,  511, 32, WACOM_G4,   wacom_graphire_irq },
-       { "Wacom Graphire4 6x8", 8,  16704, 12064,  511, 32, WACOM_G4,   wacom_graphire_irq },
-       { "Wacom Volito",        8,   5104,  3712,  511, 32, GRAPHIRE,   wacom_graphire_irq },
-       { "Wacom PenStation2",   8,   3250,  2320,  255, 32, GRAPHIRE,   wacom_graphire_irq },
-       { "Wacom Volito2 4x5",   8,   5104,  3712,  511, 32, GRAPHIRE,   wacom_graphire_irq },
-       { "Wacom Volito2 2x3",   8,   3248,  2320,  511, 32, GRAPHIRE,   wacom_graphire_irq },
-       { "Wacom PenPartner2",   8,   3250,  2320,  255, 32, GRAPHIRE,   wacom_graphire_irq },
-       { "Wacom Intuos 4x5",   10,  12700, 10600, 1023, 15, INTUOS,     wacom_intuos_irq },
-       { "Wacom Intuos 6x8",   10,  20320, 16240, 1023, 15, INTUOS,     wacom_intuos_irq },
-       { "Wacom Intuos 9x12",  10,  30480, 24060, 1023, 15, INTUOS,     wacom_intuos_irq },
-       { "Wacom Intuos 12x12", 10,  30480, 31680, 1023, 15, INTUOS,     wacom_intuos_irq },
-       { "Wacom Intuos 12x18", 10,  45720, 31680, 1023, 15, INTUOS,     wacom_intuos_irq },
-       { "Wacom PL400",         8,   5408,  4056,  255, 32, PL,         wacom_pl_irq },
-       { "Wacom PL500",         8,   6144,  4608,  255, 32, PL,         wacom_pl_irq },
-       { "Wacom PL600",         8,   6126,  4604,  255, 32, PL,         wacom_pl_irq },
-       { "Wacom PL600SX",       8,   6260,  5016,  255, 32, PL,         wacom_pl_irq },
-       { "Wacom PL550",         8,   6144,  4608,  511, 32, PL,         wacom_pl_irq },
-       { "Wacom PL800",         8,   7220,  5780,  511, 32, PL,         wacom_pl_irq },
-       { "Wacom PL700",         8,   6758,  5406,  511, 32, PL,         wacom_pl_irq },
-       { "Wacom PL510",         8,   6282,  4762,  511, 32, PL,         wacom_pl_irq },
-       { "Wacom DTU710",        8,  34080, 27660,  511, 32, PL,         wacom_pl_irq },
-       { "Wacom DTF521",        8,   6282,  4762,  511, 32, PL,         wacom_pl_irq },
-       { "Wacom DTF720",        8,   6858,  5506,  511, 32, PL,         wacom_pl_irq },
-       { "Wacom Cintiq Partner",8,  20480, 15360,  511, 32, PL,         wacom_ptu_irq },
-       { "Wacom Intuos2 4x5",   10, 12700, 10600, 1023, 15, INTUOS,     wacom_intuos_irq },
-       { "Wacom Intuos2 6x8",   10, 20320, 16240, 1023, 15, INTUOS,     wacom_intuos_irq },
-       { "Wacom Intuos2 9x12",  10, 30480, 24060, 1023, 15, INTUOS,     wacom_intuos_irq },
-       { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 15, INTUOS,     wacom_intuos_irq },
-       { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 15, INTUOS,     wacom_intuos_irq },
-       { "Wacom Intuos3 4x5",   10, 25400, 20320, 1023, 15, INTUOS3,    wacom_intuos_irq },
-       { "Wacom Intuos3 6x8",   10, 40640, 30480, 1023, 15, INTUOS3,    wacom_intuos_irq },
-       { "Wacom Intuos3 9x12",  10, 60960, 45720, 1023, 15, INTUOS3,    wacom_intuos_irq },
-       { "Wacom Intuos3 12x12", 10, 60960, 60960, 1023, 15, INTUOS312,  wacom_intuos_irq },
-       { "Wacom Intuos3 12x19", 10, 97536, 60960, 1023, 15, INTUOS319,  wacom_intuos_irq },
-       { "Wacom Intuos3 6x11",  10, 54204, 31750, 1023, 15, INTUOS3,    wacom_intuos_irq },
-       { "Wacom Cintiq 21UX",   10, 87200, 65600, 1023, 15, CINTIQ,     wacom_intuos_irq },
-       { "Wacom Intuos2 6x8",   10, 20320, 16240, 1023, 15, INTUOS,     wacom_intuos_irq },
-       { }
-};
-
-static struct usb_device_id wacom_ids[] = {
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x00) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x10) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x11) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x12) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x13) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x14) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x15) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x16) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x60) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x61) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x62) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x63) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x64) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x20) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x21) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x22) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x23) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x24) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x30) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x31) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x32) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x33) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x34) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x35) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x37) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x38) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x39) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC0) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC3) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x03) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x41) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x42) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x43) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x44) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x45) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB0) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB1) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB2) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB3) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB4) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB5) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x3F) },
-       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) },
-       { }
-};
-
-MODULE_DEVICE_TABLE(usb, wacom_ids);
-
-static int wacom_open(struct input_dev *dev)
-{
-       struct wacom *wacom = dev->private;
-
-       wacom->irq->dev = wacom->usbdev;
-       if (usb_submit_urb(wacom->irq, GFP_KERNEL))
-               return -EIO;
-
-       return 0;
-}
-
-static void wacom_close(struct input_dev *dev)
-{
-       struct wacom *wacom = dev->private;
-
-       usb_kill_urb(wacom->irq);
-}
-
-static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
-{
-       struct usb_device *dev = interface_to_usbdev(intf);
-       struct usb_endpoint_descriptor *endpoint;
-       struct wacom *wacom;
-       struct input_dev *input_dev;
-       char rep_data[2], limit = 0;
-
-       wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
-       input_dev = input_allocate_device();
-       if (!wacom || !input_dev)
-               goto fail1;
-
-       wacom->data = usb_buffer_alloc(dev, 10, GFP_KERNEL, &wacom->data_dma);
-       if (!wacom->data)
-               goto fail1;
-
-       wacom->irq = usb_alloc_urb(0, GFP_KERNEL);
-       if (!wacom->irq)
-               goto fail2;
-
-       wacom->usbdev = dev;
-       wacom->dev = input_dev;
-       usb_make_path(dev, wacom->phys, sizeof(wacom->phys));
-       strlcat(wacom->phys, "/input0", sizeof(wacom->phys));
-
-       wacom->features = wacom_features + (id - wacom_ids);
-       if (wacom->features->pktlen > 10)
-               BUG();
-
-       input_dev->name = wacom->features->name;
-       usb_to_input_id(dev, &input_dev->id);
-
-       input_dev->cdev.dev = &intf->dev;
-       input_dev->private = wacom;
-       input_dev->open = wacom_open;
-       input_dev->close = wacom_close;
-
-       input_dev->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS);
-       input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH) | BIT(BTN_STYLUS);
-       input_set_abs_params(input_dev, ABS_X, 0, wacom->features->x_max, 4, 0);
-       input_set_abs_params(input_dev, ABS_Y, 0, wacom->features->y_max, 4, 0);
-       input_set_abs_params(input_dev, ABS_PRESSURE, 0, wacom->features->pressure_max, 0, 0);
-       input_dev->absbit[LONG(ABS_MISC)] |= BIT(ABS_MISC);
-
-       switch (wacom->features->type) {
-               case WACOM_G4:
-                       input_dev->evbit[0] |= BIT(EV_MSC);
-                       input_dev->mscbit[0] |= BIT(MSC_SERIAL);
-                       input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
-                       input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
-                       /* fall through */
-
-               case GRAPHIRE:
-                       input_dev->evbit[0] |= BIT(EV_REL);
-                       input_dev->relbit[0] |= BIT(REL_WHEEL);
-                       input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
-                       input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_STYLUS2);
-                       input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom->features->distance_max, 0, 0);
-                       break;
-
-               case INTUOS3:
-               case INTUOS312:
-               case INTUOS319:
-               case CINTIQ:
-                       input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
-                       input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
-                       input_set_abs_params(input_dev, ABS_RX, 0, 4097, 0, 0);
-                       input_set_abs_params(input_dev, ABS_RY, 0, 4097, 0, 0);
-                       /* fall through */
-
-               case INTUOS:
-                       input_dev->evbit[0] |= BIT(EV_MSC) | BIT(EV_REL);
-                       input_dev->mscbit[0] |= BIT(MSC_SERIAL);
-                       input_dev->relbit[0] |= BIT(REL_WHEEL);
-                       input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE) | BIT(BTN_SIDE) | BIT(BTN_EXTRA);
-                       input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_TOOL_BRUSH)
-                                                         | BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS) | BIT(BTN_STYLUS2);
-                       input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom->features->distance_max, 0, 0);
-                       input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0);
-                       input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0);
-                       input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0);
-                       input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0);
-                       input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
-                       break;
-
-               case PL:
-                       input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2) | BIT(BTN_TOOL_RUBBER);
-                       break;
-       }
-
-       endpoint = &intf->cur_altsetting->endpoint[0].desc;
-
-       if (wacom->features->pktlen > 10)
-               BUG();
-
-       usb_fill_int_urb(wacom->irq, dev,
-                        usb_rcvintpipe(dev, endpoint->bEndpointAddress),
-                        wacom->data, wacom->features->pktlen,
-                        wacom->features->irq, wacom, endpoint->bInterval);
-       wacom->irq->transfer_dma = wacom->data_dma;
-       wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-
-       input_register_device(wacom->dev);
-
-       /* Ask the tablet to report tablet data. Repeat until it succeeds */
-       do {
-               rep_data[0] = 2;
-               rep_data[1] = 2;
-               usb_set_report(intf, 3, 2, rep_data, 2);
-               usb_get_report(intf, 3, 2, rep_data, 2);
-       } while (rep_data[1] != 2 && limit++ < 5);
-
-       usb_set_intfdata(intf, wacom);
-       return 0;
-
-fail2: usb_buffer_free(dev, 10, wacom->data, wacom->data_dma);
-fail1: input_free_device(input_dev);
-       kfree(wacom);
-       return -ENOMEM;
-}
-
-static void wacom_disconnect(struct usb_interface *intf)
-{
-       struct wacom *wacom = usb_get_intfdata (intf);
-
-       usb_set_intfdata(intf, NULL);
-       if (wacom) {
-               usb_kill_urb(wacom->irq);
-               input_unregister_device(wacom->dev);
-               usb_free_urb(wacom->irq);
-               usb_buffer_free(interface_to_usbdev(intf), 10, wacom->data, wacom->data_dma);
-               kfree(wacom);
-       }
-}
-
-static struct usb_driver wacom_driver = {
-       .name =         "wacom",
-       .probe =        wacom_probe,
-       .disconnect =   wacom_disconnect,
-       .id_table =     wacom_ids,
-};
-
-static int __init wacom_init(void)
-{
-       int result = usb_register(&wacom_driver);
-       if (result == 0)
-               info(DRIVER_VERSION ":" DRIVER_DESC);
-       return result;
-}
-
-static void __exit wacom_exit(void)
-{
-       usb_deregister(&wacom_driver);
-}
-
-module_init(wacom_init);
-module_exit(wacom_exit);
diff --git a/drivers/usb/input/wacom.h b/drivers/usb/input/wacom.h
new file mode 100644 (file)
index 0000000..832737b
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * drivers/usb/input/wacom.h
+ *
+ *  USB Wacom Graphire and Wacom Intuos tablet support
+ *
+ *  Copyright (c) 2000-2004 Vojtech Pavlik     <vojtech@ucw.cz>
+ *  Copyright (c) 2000 Andreas Bach Aaen       <abach@stofanet.dk>
+ *  Copyright (c) 2000 Clifford Wolf           <clifford@clifford.at>
+ *  Copyright (c) 2000 Sam Mosel               <sam.mosel@computer.org>
+ *  Copyright (c) 2000 James E. Blair          <corvus@gnu.org>
+ *  Copyright (c) 2000 Daniel Egger            <egger@suse.de>
+ *  Copyright (c) 2001 Frederic Lepied         <flepied@mandrakesoft.com>
+ *  Copyright (c) 2004 Panagiotis Issaris      <panagiotis.issaris@mech.kuleuven.ac.be>
+ *  Copyright (c) 2002-2006 Ping Cheng         <pingc@wacom.com>
+ *
+ *  ChangeLog:
+ *      v0.1 (vp)  - Initial release
+ *      v0.2 (aba) - Support for all buttons / combinations
+ *      v0.3 (vp)  - Support for Intuos added
+ *     v0.4 (sm)  - Support for more Intuos models, menustrip
+ *                     relative mode, proximity.
+ *     v0.5 (vp)  - Big cleanup, nifty features removed,
+ *                     they belong in userspace
+ *     v1.8 (vp)  - Submit URB only when operating, moved to CVS,
+ *                     use input_report_key instead of report_btn and
+ *                     other cleanups
+ *     v1.11 (vp) - Add URB ->dev setting for new kernels
+ *     v1.11 (jb) - Add support for the 4D Mouse & Lens
+ *     v1.12 (de) - Add support for two more inking pen IDs
+ *     v1.14 (vp) - Use new USB device id probing scheme.
+ *                  Fix Wacom Graphire mouse wheel
+ *     v1.18 (vp) - Fix mouse wheel direction
+ *                  Make mouse relative
+ *      v1.20 (fl) - Report tool id for Intuos devices
+ *                 - Multi tools support
+ *                 - Corrected Intuos protocol decoding (airbrush, 4D mouse, lens cursor...)
+ *                 - Add PL models support
+ *                - Fix Wacom Graphire mouse wheel again
+ *     v1.21 (vp) - Removed protocol descriptions
+ *                - Added MISC_SERIAL for tool serial numbers
+ *           (gb) - Identify version on module load.
+ *    v1.21.1 (fl) - added Graphire2 support
+ *    v1.21.2 (fl) - added Intuos2 support
+ *                 - added all the PL ids
+ *    v1.21.3 (fl) - added another eraser id from Neil Okamoto
+ *                 - added smooth filter for Graphire from Peri Hankey
+ *                 - added PenPartner support from Olaf van Es
+ *                 - new tool ids from Ole Martin Bjoerndalen
+ *     v1.29 (pc) - Add support for more tablets
+ *                - Fix pressure reporting
+ *     v1.30 (vp) - Merge 2.4 and 2.5 drivers
+ *                - Since 2.5 now has input_sync(), remove MSC_SERIAL abuse
+ *                - Cleanups here and there
+ *    v1.30.1 (pi) - Added Graphire3 support
+ *     v1.40 (pc) - Add support for several new devices, fix eraser reporting, ...
+ *     v1.43 (pc) - Added support for Cintiq 21UX
+ *                - Fixed a Graphire bug
+ *                - Merged wacom_intuos3_irq into wacom_intuos_irq
+ *     v1.44 (pc) - Added support for Graphire4, Cintiq 710, Intuos3 6x11, etc.
+ *                - Report Device IDs
+ *      v1.45 (pc) - Added support for DTF 521, Intuos3 12x12 and 12x19
+ *                 - Minor data report fix
+ *      v1.46 (pc) - Split wacom.c into wacom_sys.c and wacom_wac.c,
+ *                - where wacom_sys.c deals with system specific code,
+ *                - and wacom_wac.c deals with Wacom specific code
+ */
+
+/*
+ * 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.
+ */
+#ifndef WACOM_H
+#define WACOM_H
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb/input.h>
+#include <asm/unaligned.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.46"
+#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
+#define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_WACOM    0x056a
+
+struct wacom {
+       dma_addr_t data_dma;
+       struct input_dev *dev;
+       struct usb_device *usbdev;
+       struct urb *irq;
+       struct wacom_wac * wacom_wac;
+       char phys[32];
+};
+
+struct wacom_combo {
+       struct wacom * wacom;
+       struct urb * urb;
+       struct pt_regs *regs;
+};
+
+extern int wacom_wac_irq(struct wacom_wac * wacom_wac, void * wcombo);
+extern void wacom_sys_irq(struct urb *urb, struct pt_regs *regs);
+extern void wacom_report_abs(void *wcombo, unsigned int abs_type, int abs_data);
+extern void wacom_report_rel(void *wcombo, unsigned int rel_type, int rel_data);
+extern void wacom_report_key(void *wcombo, unsigned int key_type, int key_data);
+extern void wacom_input_event(void *wcombo, unsigned int type, unsigned int code, int value);
+extern void wacom_input_regs(void *wcombo);
+extern void wacom_input_sync(void *wcombo);
+extern void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern __u16 wacom_le16_to_cpu(unsigned char *data);
+extern __u16 wacom_be16_to_cpu(unsigned char *data);
+extern struct wacom_features * get_wacom_feature(const struct usb_device_id *id);
+extern const struct usb_device_id * get_device_table(void);
+
+#endif
diff --git a/drivers/usb/input/wacom_sys.c b/drivers/usb/input/wacom_sys.c
new file mode 100644 (file)
index 0000000..7c3b52b
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * drivers/usb/input/wacom_sys.c
+ *
+ *  USB Wacom Graphire and Wacom Intuos tablet support - system specific code
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "wacom.h"
+#include "wacom_wac.h"
+
+#define USB_REQ_GET_REPORT     0x01
+#define USB_REQ_SET_REPORT     0x09
+
+static int usb_get_report(struct usb_interface *intf, unsigned char type,
+                               unsigned char id, void *buf, int size)
+{
+       return usb_control_msg(interface_to_usbdev(intf),
+               usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
+               USB_REQ_GET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+               (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
+               buf, size, 100);
+}
+
+static int usb_set_report(struct usb_interface *intf, unsigned char type,
+                               unsigned char id, void *buf, int size)
+{
+       return usb_control_msg(interface_to_usbdev(intf),
+               usb_sndctrlpipe(interface_to_usbdev(intf), 0),
+                USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
+               buf, size, 1000);
+}
+
+static struct input_dev * get_input_dev(struct wacom_combo *wcombo)
+{
+       return wcombo->wacom->dev;
+}
+
+void wacom_sys_irq(struct urb *urb, struct pt_regs *regs)
+{
+       struct wacom *wacom = urb->context;
+       struct wacom_combo wcombo;
+       int retval;
+
+       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", __FUNCTION__, urb->status);
+               return;
+       default:
+               dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+               goto exit;
+       }
+
+       wcombo.wacom = wacom;
+       wcombo.urb = urb;
+       wcombo.regs = regs;
+
+       if (wacom_wac_irq(wacom->wacom_wac, (void *)&wcombo))
+               input_sync(get_input_dev(&wcombo));
+
+ exit:
+       retval = usb_submit_urb (urb, GFP_ATOMIC);
+       if (retval)
+               err ("%s - usb_submit_urb failed with result %d",
+                    __FUNCTION__, retval);
+}
+
+void wacom_report_key(void *wcombo, unsigned int key_type, int key_data)
+{
+       input_report_key(get_input_dev((struct wacom_combo *)wcombo), key_type, key_data);
+       return;
+}
+
+void wacom_report_abs(void *wcombo, unsigned int abs_type, int abs_data)
+{
+       input_report_abs(get_input_dev((struct wacom_combo *)wcombo), abs_type, abs_data);
+       return;
+}
+
+void wacom_report_rel(void *wcombo, unsigned int rel_type, int rel_data)
+{
+       input_report_rel(get_input_dev((struct wacom_combo *)wcombo), rel_type, rel_data);
+       return;
+}
+
+void wacom_input_event(void *wcombo, unsigned int type, unsigned int code, int value)
+{
+       input_event(get_input_dev((struct wacom_combo *)wcombo), type, code, value);
+       return;
+}
+
+__u16 wacom_be16_to_cpu(unsigned char *data)
+{
+       __u16 value;
+       value = be16_to_cpu(*(__be16 *) data);
+       return value;
+}
+
+__u16 wacom_le16_to_cpu(unsigned char *data)
+{
+       __u16 value;
+       value = be16_to_cpu(*(__be16 *) data);
+       return value;
+}
+
+void wacom_input_regs(void *wcombo)
+{
+       input_regs(get_input_dev((struct wacom_combo *)wcombo), ((struct wacom_combo *)wcombo)->regs);
+       return;
+}
+
+void wacom_input_sync(void *wcombo)
+{
+       input_sync(get_input_dev((struct wacom_combo *)wcombo));
+       return;
+}
+
+static int wacom_open(struct input_dev *dev)
+{
+       struct wacom *wacom = dev->private;
+
+       wacom->irq->dev = wacom->usbdev;
+       if (usb_submit_urb(wacom->irq, GFP_KERNEL))
+               return -EIO;
+
+       return 0;
+}
+
+static void wacom_close(struct input_dev *dev)
+{
+       struct wacom *wacom = dev->private;
+
+       usb_kill_urb(wacom->irq);
+}
+
+void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+       input_dev->evbit[0] |= BIT(EV_MSC);
+       input_dev->mscbit[0] |= BIT(MSC_SERIAL);
+       input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
+       input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
+}
+
+void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+       input_dev->evbit[0] |= BIT(EV_REL);
+       input_dev->relbit[0] |= BIT(REL_WHEEL);
+       input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
+       input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_STYLUS2);
+       input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom_wac->features->distance_max, 0, 0);
+}
+
+void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+       input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
+       input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
+       input_set_abs_params(input_dev, ABS_RX, 0, 4097, 0, 0);
+       input_set_abs_params(input_dev, ABS_RY, 0, 4097, 0, 0);
+}
+
+void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+       input_dev->evbit[0] |= BIT(EV_MSC) | BIT(EV_REL);
+       input_dev->mscbit[0] |= BIT(MSC_SERIAL);
+       input_dev->relbit[0] |= BIT(REL_WHEEL);
+       input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE) | BIT(BTN_SIDE) | BIT(BTN_EXTRA);
+       input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_TOOL_BRUSH)
+               | BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS) | BIT(BTN_STYLUS2);
+       input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom_wac->features->distance_max, 0, 0);
+       input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0);
+       input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0);
+       input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0);
+       input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0);
+       input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
+}
+
+void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+       input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2) | BIT(BTN_TOOL_RUBBER);
+}
+
+void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+       input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER);
+}
+
+static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+       struct usb_device *dev = interface_to_usbdev(intf);
+       struct usb_endpoint_descriptor *endpoint;
+       struct wacom *wacom;
+       struct wacom_wac *wacom_wac;
+       struct input_dev *input_dev;
+       char rep_data[2], limit = 0;
+
+       wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
+       wacom_wac = kzalloc(sizeof(struct wacom_wac), GFP_KERNEL);
+       input_dev = input_allocate_device();
+       if (!wacom || !input_dev || !wacom_wac)
+               goto fail1;
+
+       wacom_wac->data = usb_buffer_alloc(dev, 10, GFP_KERNEL, &wacom->data_dma);
+       if (!wacom_wac->data)
+               goto fail1;
+
+       wacom->irq = usb_alloc_urb(0, GFP_KERNEL);
+       if (!wacom->irq)
+               goto fail2;
+
+       wacom->usbdev = dev;
+       wacom->dev = input_dev;
+       usb_make_path(dev, wacom->phys, sizeof(wacom->phys));
+       strlcat(wacom->phys, "/input0", sizeof(wacom->phys));
+
+       wacom_wac->features = get_wacom_feature(id);
+       if (wacom_wac->features->pktlen > 10)
+               BUG();
+
+       input_dev->name = wacom_wac->features->name;
+       wacom->wacom_wac = wacom_wac;
+       usb_to_input_id(dev, &input_dev->id);
+
+       input_dev->cdev.dev = &intf->dev;
+       input_dev->private = wacom;
+       input_dev->open = wacom_open;
+       input_dev->close = wacom_close;
+
+       input_dev->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS);
+       input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH) | BIT(BTN_STYLUS);
+       input_set_abs_params(input_dev, ABS_X, 0, wacom_wac->features->x_max, 4, 0);
+       input_set_abs_params(input_dev, ABS_Y, 0, wacom_wac->features->y_max, 4, 0);
+       input_set_abs_params(input_dev, ABS_PRESSURE, 0, wacom_wac->features->pressure_max, 0, 0);
+       input_dev->absbit[LONG(ABS_MISC)] |= BIT(ABS_MISC);
+
+       wacom_init_input_dev(input_dev, wacom_wac);
+
+       endpoint = &intf->cur_altsetting->endpoint[0].desc;
+
+       usb_fill_int_urb(wacom->irq, dev,
+                        usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+                        wacom_wac->data, wacom_wac->features->pktlen,
+                        wacom_wac->features->irq, wacom, endpoint->bInterval);
+       wacom->irq->transfer_dma = wacom->data_dma;
+       wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+       input_register_device(wacom->dev);
+
+       /* Ask the tablet to report tablet data. Repeat until it succeeds */
+       do {
+               rep_data[0] = 2;
+               rep_data[1] = 2;
+               usb_set_report(intf, 3, 2, rep_data, 2);
+               usb_get_report(intf, 3, 2, rep_data, 2);
+       } while (rep_data[1] != 2 && limit++ < 5);
+
+       usb_set_intfdata(intf, wacom);
+       return 0;
+
+fail2: usb_buffer_free(dev, 10, wacom_wac->data, wacom->data_dma);
+fail1: input_free_device(input_dev);
+       kfree(wacom);
+       kfree(wacom_wac);
+       return -ENOMEM;
+}
+
+static void wacom_disconnect(struct usb_interface *intf)
+{
+       struct wacom *wacom = usb_get_intfdata (intf);
+
+       usb_set_intfdata(intf, NULL);
+       if (wacom) {
+               usb_kill_urb(wacom->irq);
+               input_unregister_device(wacom->dev);
+               usb_free_urb(wacom->irq);
+               usb_buffer_free(interface_to_usbdev(intf), 10, wacom->wacom_wac->data, wacom->data_dma);
+               kfree(wacom);
+               kfree(wacom->wacom_wac);
+       }
+}
+
+static struct usb_driver wacom_driver = {
+       .name =         "wacom",
+       .probe =        wacom_probe,
+       .disconnect =   wacom_disconnect,
+};
+
+static int __init wacom_init(void)
+{
+       int result;
+       wacom_driver.id_table = get_device_table();
+       result = usb_register(&wacom_driver);
+       if (result == 0)
+               info(DRIVER_VERSION ":" DRIVER_DESC);
+       return result;
+}
+
+static void __exit wacom_exit(void)
+{
+       usb_deregister(&wacom_driver);
+}
+
+module_init(wacom_init);
+module_exit(wacom_exit);
diff --git a/drivers/usb/input/wacom_wac.c b/drivers/usb/input/wacom_wac.c
new file mode 100644 (file)
index 0000000..85d458c
--- /dev/null
@@ -0,0 +1,646 @@
+/*
+ * drivers/usb/input/wacom_wac.c
+ *
+ *  USB Wacom Graphire and Wacom Intuos tablet support - Wacom specific code
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include "wacom.h"
+#include "wacom_wac.h"
+
+static int wacom_penpartner_irq(struct wacom_wac *wacom, void *wcombo)
+{
+       unsigned char *data = wacom->data;
+
+       switch (data[0]) {
+               case 1:
+                       wacom_input_regs(wcombo);
+                       if (data[5] & 0x80) {
+                               wacom->tool[0] = (data[5] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+                               wacom->id[0] = (data[5] & 0x20) ? ERASER_DEVICE_ID : STYLUS_DEVICE_ID;
+                               wacom_report_key(wcombo, wacom->tool[0], 1);
+                               wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); /* report tool id */
+                               wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1]));
+                               wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3]));
+                               wacom_report_abs(wcombo, ABS_PRESSURE, (signed char)data[6] + 127);
+                               wacom_report_key(wcombo, BTN_TOUCH, ((signed char)data[6] > -127));
+                               wacom_report_key(wcombo, BTN_STYLUS, (data[5] & 0x40));
+                       } else {
+                               wacom_report_key(wcombo, wacom->tool[0], 0);
+                               wacom_report_abs(wcombo, ABS_MISC, 0); /* report tool id */
+                               wacom_report_abs(wcombo, ABS_PRESSURE, -1);
+                               wacom_report_key(wcombo, BTN_TOUCH, 0);
+                       }
+                       break;
+               case 2:
+                       wacom_input_regs(wcombo);
+                       wacom_report_key(wcombo, BTN_TOOL_PEN, 1);
+                       wacom_report_abs(wcombo, ABS_MISC, STYLUS_DEVICE_ID); /* report tool id */
+                       wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1]));
+                       wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3]));
+                       wacom_report_abs(wcombo, ABS_PRESSURE, (signed char)data[6] + 127);
+                       wacom_report_key(wcombo, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20));
+                       wacom_report_key(wcombo, BTN_STYLUS, (data[5] & 0x40));
+                       break;
+               default:
+                       printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]);
+                       return 0;
+        }
+       return 1;
+}
+
+static int wacom_pl_irq(struct wacom_wac *wacom, void *wcombo)
+{
+       unsigned char *data = wacom->data;
+       int prox, id, pressure;
+
+       if (data[0] != 2) {
+               dbg("wacom_pl_irq: received unknown report #%d", data[0]);
+               return 0;
+       }
+
+       prox = data[1] & 0x40;
+
+       wacom_input_regs(wcombo);
+
+       id = ERASER_DEVICE_ID;
+       if (prox) {
+
+               pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
+               if (wacom->features->pressure_max > 255)
+                       pressure = (pressure << 1) | ((data[4] >> 6) & 1);
+               pressure += (wacom->features->pressure_max + 1) / 2;
+
+               /*
+                * if going from out of proximity into proximity select between the eraser
+                * and the pen based on the state of the stylus2 button, choose eraser if
+                * pressed else choose pen. if not a proximity change from out to in, send
+                * an out of proximity for previous tool then a in for new tool.
+                */
+               if (!wacom->tool[0]) {
+                       /* Eraser bit set for DTF */
+                       if (data[1] & 0x10)
+                               wacom->tool[1] = BTN_TOOL_RUBBER;
+                       else
+                               /* Going into proximity select tool */
+                               wacom->tool[1] = (data[4] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+               } else {
+                       /* was entered with stylus2 pressed */
+                       if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) {
+                               /* report out proximity for previous tool */
+                               wacom_report_key(wcombo, wacom->tool[1], 0);
+                               wacom_input_sync(wcombo);
+                               wacom->tool[1] = BTN_TOOL_PEN;
+                               return 0;
+                       }
+               }
+               if (wacom->tool[1] != BTN_TOOL_RUBBER) {
+                       /* Unknown tool selected default to pen tool */
+                       wacom->tool[1] = BTN_TOOL_PEN;
+                       id = STYLUS_DEVICE_ID;
+               }
+               wacom_report_key(wcombo, wacom->tool[1], prox); /* report in proximity for tool */
+               wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
+               wacom_report_abs(wcombo, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
+               wacom_report_abs(wcombo, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
+               wacom_report_abs(wcombo, ABS_PRESSURE, pressure);
+
+               wacom_report_key(wcombo, BTN_TOUCH, data[4] & 0x08);
+               wacom_report_key(wcombo, BTN_STYLUS, data[4] & 0x10);
+               /* Only allow the stylus2 button to be reported for the pen tool. */
+               wacom_report_key(wcombo, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20));
+       } else {
+               /* report proximity-out of a (valid) tool */
+               if (wacom->tool[1] != BTN_TOOL_RUBBER) {
+                       /* Unknown tool selected default to pen tool */
+                       wacom->tool[1] = BTN_TOOL_PEN;
+               }
+               wacom_report_key(wcombo, wacom->tool[1], prox);
+       }
+
+       wacom->tool[0] = prox; /* Save proximity state */
+       return 1;
+}
+
+static int wacom_ptu_irq(struct wacom_wac *wacom, void *wcombo)
+{
+       unsigned char *data = wacom->data;
+       int id;
+
+       if (data[0] != 2) {
+               printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]);
+               return 0;
+       }
+
+       wacom_input_regs(wcombo);
+       if (data[1] & 0x04) {
+               wacom_report_key(wcombo, BTN_TOOL_RUBBER, data[1] & 0x20);
+               wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x08);
+               id = ERASER_DEVICE_ID;
+       } else {
+               wacom_report_key(wcombo, BTN_TOOL_PEN, data[1] & 0x20);
+               wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x01);
+               id = STYLUS_DEVICE_ID;
+       }
+       wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
+       wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[2]));
+       wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[4]));
+       wacom_report_abs(wcombo, ABS_PRESSURE, wacom_le16_to_cpu(&data[6]));
+       wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02);
+       wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x10);
+       return 1;
+}
+
+static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
+{
+       unsigned char *data = wacom->data;
+       int x, y, id, rw;
+
+       if (data[0] != 2) {
+               dbg("wacom_graphire_irq: received unknown report #%d", data[0]);
+               return 0;
+       }
+
+       wacom_input_regs(wcombo);
+
+       id = STYLUS_DEVICE_ID;
+       if (data[1] & 0x10) { /* in prox */
+
+               switch ((data[1] >> 5) & 3) {
+
+                       case 0: /* Pen */
+                               wacom->tool[0] = BTN_TOOL_PEN;
+                               break;
+
+                       case 1: /* Rubber */
+                               wacom->tool[0] = BTN_TOOL_RUBBER;
+                               id = ERASER_DEVICE_ID;
+                               break;
+
+                       case 2: /* Mouse with wheel */
+                               wacom_report_key(wcombo, BTN_MIDDLE, data[1] & 0x04);
+                               if (wacom->features->type == WACOM_G4) {
+                                       rw = data[7] & 0x04 ? (data[7] & 0x03)-4 : (data[7] & 0x03);
+                                       wacom_report_rel(wcombo, REL_WHEEL, -rw);
+                               } else
+                                       wacom_report_rel(wcombo, REL_WHEEL, -(signed char) data[6]);
+                               /* fall through */
+
+                       case 3: /* Mouse without wheel */
+                               wacom->tool[0] = BTN_TOOL_MOUSE;
+                               id = CURSOR_DEVICE_ID;
+                               wacom_report_key(wcombo, BTN_LEFT, data[1] & 0x01);
+                               wacom_report_key(wcombo, BTN_RIGHT, data[1] & 0x02);
+                               if (wacom->features->type == WACOM_G4)
+                                       wacom_report_abs(wcombo, ABS_DISTANCE, data[6]);
+                               else
+                                       wacom_report_abs(wcombo, ABS_DISTANCE, data[7]);
+                               break;
+               }
+       }
+
+       if (data[1] & 0x90) {
+               x = wacom_le16_to_cpu(&data[2]);
+               y = wacom_le16_to_cpu(&data[4]);
+               wacom_report_abs(wcombo, ABS_X, x);
+               wacom_report_abs(wcombo, ABS_Y, y);
+               if (wacom->tool[0] != BTN_TOOL_MOUSE) {
+                       wacom_report_abs(wcombo, ABS_PRESSURE, data[6] | ((data[7] & 0x01) << 8));
+                       wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x01);
+                       wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02);
+                       wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x04);
+               }
+       }
+
+       if (data[1] & 0x10)
+               wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
+       else
+               wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
+       wacom_report_key(wcombo, wacom->tool[0], data[1] & 0x10);
+       wacom_input_sync(wcombo);
+
+       /* send pad data */
+       if (wacom->features->type == WACOM_G4) {
+               if ( (wacom->serial[1] & 0xc0) != (data[7] & 0xf8) ) {
+                       wacom->id[1] = 1;
+                       wacom->serial[1] = (data[7] & 0xf8);
+                       wacom_report_key(wcombo, BTN_0, (data[7] & 0x40));
+                       wacom_report_key(wcombo, BTN_4, (data[7] & 0x80));
+                       rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3);
+                       wacom_report_rel(wcombo, REL_WHEEL, rw);
+                       wacom_report_key(wcombo, BTN_TOOL_FINGER, 0xf0);
+                       wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
+               } else if (wacom->id[1]) {
+                       wacom->id[1] = 0;
+                       wacom_report_key(wcombo, BTN_TOOL_FINGER, 0);
+                       wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
+               }
+       }
+       return 1;
+}
+
+static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
+{
+       unsigned char *data = wacom->data;
+       int idx;
+
+       /* tool number */
+       idx = data[1] & 0x01;
+
+       /* Enter report */
+       if ((data[1] & 0xfc) == 0xc0) {
+               /* serial number of the tool */
+               wacom->serial[idx] = ((data[3] & 0x0f) << 28) +
+                       (data[4] << 20) + (data[5] << 12) +
+                       (data[6] << 4) + (data[7] >> 4);
+
+               wacom->id[idx] = (data[2] << 4) | (data[3] >> 4);
+               switch (wacom->id[idx]) {
+                       case 0x812: /* Inking pen */
+                       case 0x801: /* Intuos3 Inking pen */
+                       case 0x012:
+                               wacom->tool[idx] = BTN_TOOL_PENCIL;
+                               break;
+                       case 0x822: /* Pen */
+                       case 0x842:
+                       case 0x852:
+                       case 0x823: /* Intuos3 Grip Pen */
+                       case 0x813: /* Intuos3 Classic Pen */
+                       case 0x885: /* Intuos3 Marker Pen */
+                       case 0x022:
+                               wacom->tool[idx] = BTN_TOOL_PEN;
+                               break;
+                       case 0x832: /* Stroke pen */
+                       case 0x032:
+                               wacom->tool[idx] = BTN_TOOL_BRUSH;
+                               break;
+                       case 0x007: /* Mouse 4D and 2D */
+                       case 0x09c:
+                       case 0x094:
+                       case 0x017: /* Intuos3 2D Mouse */
+                               wacom->tool[idx] = BTN_TOOL_MOUSE;
+                               break;
+                       case 0x096: /* Lens cursor */
+                       case 0x097: /* Intuos3 Lens cursor */
+                               wacom->tool[idx] = BTN_TOOL_LENS;
+                               break;
+                       case 0x82a: /* Eraser */
+                       case 0x85a:
+                       case 0x91a:
+                       case 0xd1a:
+                       case 0x0fa:
+                       case 0x82b: /* Intuos3 Grip Pen Eraser */
+                       case 0x81b: /* Intuos3 Classic Pen Eraser */
+                       case 0x91b: /* Intuos3 Airbrush Eraser */
+                               wacom->tool[idx] = BTN_TOOL_RUBBER;
+                               break;
+                       case 0xd12:
+                       case 0x912:
+                       case 0x112:
+                       case 0x913: /* Intuos3 Airbrush */
+                               wacom->tool[idx] = BTN_TOOL_AIRBRUSH;
+                               break;
+                       default: /* Unknown tool */
+                               wacom->tool[idx] = BTN_TOOL_PEN;
+               }
+               /* only large I3 support Lens Cursor */
+               if(!((wacom->tool[idx] == BTN_TOOL_LENS) &&
+                               (wacom->features->type == INTUOS3))) {
+                       wacom_report_abs(wcombo, ABS_MISC, wacom->id[idx]); /* report tool id */
+                       wacom_report_key(wcombo, wacom->tool[idx], 1);
+                       wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+                       return 2;
+               }
+               return 1;
+       }
+
+       /* Exit report */
+       if ((data[1] & 0xfe) == 0x80) {
+               wacom_report_key(wcombo, wacom->tool[idx], 0);
+               wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
+               wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+               return 2;
+       }
+       return 0;
+}
+
+static void wacom_intuos_general(struct wacom_wac *wacom, void *wcombo)
+{
+       unsigned char *data = wacom->data;
+       unsigned int t;
+
+       /* general pen packet */
+       if ((data[1] & 0xb8) == 0xa0) {
+               t = (data[6] << 2) | ((data[7] >> 6) & 3);
+               wacom_report_abs(wcombo, ABS_PRESSURE, t);
+               wacom_report_abs(wcombo, ABS_TILT_X,
+                               ((data[7] << 1) & 0x7e) | (data[8] >> 7));
+               wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f);
+               wacom_report_key(wcombo, BTN_STYLUS, data[1] & 2);
+               wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 4);
+               wacom_report_key(wcombo, BTN_TOUCH, t > 10);
+       }
+
+       /* airbrush second packet */
+       if ((data[1] & 0xbc) == 0xb4) {
+               wacom_report_abs(wcombo, ABS_WHEEL,
+                               (data[6] << 2) | ((data[7] >> 6) & 3));
+               wacom_report_abs(wcombo, ABS_TILT_X,
+                               ((data[7] << 1) & 0x7e) | (data[8] >> 7));
+               wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f);
+       }
+       return;
+}
+
+static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
+{
+       unsigned char *data = wacom->data;
+       unsigned int t;
+       int idx, result;
+
+       if (data[0] != 2 && data[0] != 5 && data[0] != 6 && data[0] != 12) {
+               dbg("wacom_intuos_irq: received unknown report #%d", data[0]);
+                return 0;
+       }
+
+       wacom_input_regs(wcombo);
+
+       /* tool number */
+       idx = data[1] & 0x01;
+
+       /* pad packets. Works as a second tool and is always in prox */
+       if (data[0] == 12) {
+               /* initiate the pad as a device */
+               if (wacom->tool[1] != BTN_TOOL_FINGER)
+                       wacom->tool[1] = BTN_TOOL_FINGER;
+
+               wacom_report_key(wcombo, BTN_0, (data[5] & 0x01));
+               wacom_report_key(wcombo, BTN_1, (data[5] & 0x02));
+               wacom_report_key(wcombo, BTN_2, (data[5] & 0x04));
+               wacom_report_key(wcombo, BTN_3, (data[5] & 0x08));
+               wacom_report_key(wcombo, BTN_4, (data[6] & 0x01));
+               wacom_report_key(wcombo, BTN_5, (data[6] & 0x02));
+               wacom_report_key(wcombo, BTN_6, (data[6] & 0x04));
+               wacom_report_key(wcombo, BTN_7, (data[6] & 0x08));
+               wacom_report_abs(wcombo, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]);
+               wacom_report_abs(wcombo, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
+
+               if((data[5] & 0x0f) | (data[6] & 0x0f) | (data[1] & 0x1f) | data[2])
+                       wacom_report_key(wcombo, wacom->tool[1], 1);
+               else
+                       wacom_report_key(wcombo, wacom->tool[1], 0);
+               wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xffffffff);
+                return 1;
+       }
+
+       /* process in/out prox events */
+       result = wacom_intuos_inout(wacom, wcombo);
+       if (result)
+                return result-1;
+
+       /* Cintiq doesn't send data when RDY bit isn't set */
+       if ((wacom->features->type == CINTIQ) && !(data[1] & 0x40))
+                 return 0;
+
+       if (wacom->features->type >= INTUOS3) {
+               wacom_report_abs(wcombo, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1));
+               wacom_report_abs(wcombo, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1));
+               wacom_report_abs(wcombo, ABS_DISTANCE, ((data[9] >> 2) & 0x3f));
+       } else {
+               wacom_report_abs(wcombo, ABS_X, wacom_be16_to_cpu(&data[2]));
+               wacom_report_abs(wcombo, ABS_Y, wacom_be16_to_cpu(&data[4]));
+               wacom_report_abs(wcombo, ABS_DISTANCE, ((data[9] >> 3) & 0x1f));
+       }
+
+       /* process general packets */
+       wacom_intuos_general(wacom, wcombo);
+
+       /* 4D mouse, 2D mouse, marker pen rotation, or Lens cursor packets */
+       if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) {
+
+               if (data[1] & 0x02) {
+                       /* Rotation packet */
+                       if (wacom->features->type >= INTUOS3) {
+                               /* I3 marker pen rotation reported as wheel
+                                * due to valuator limitation
+                                */
+                               t = (data[6] << 3) | ((data[7] >> 5) & 7);
+                               t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) :
+                                       ((t-1) / 2 + 450)) : (450 - t / 2) ;
+                               wacom_report_abs(wcombo, ABS_WHEEL, t);
+                       } else {
+                               /* 4D mouse rotation packet */
+                               t = (data[6] << 3) | ((data[7] >> 5) & 7);
+                               wacom_report_abs(wcombo, ABS_RZ, (data[7] & 0x20) ?
+                                       ((t - 1) / 2) : -t / 2);
+                       }
+
+               } else if (!(data[1] & 0x10) && wacom->features->type < INTUOS3) {
+                       /* 4D mouse packet */
+                       wacom_report_key(wcombo, BTN_LEFT,   data[8] & 0x01);
+                       wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02);
+                       wacom_report_key(wcombo, BTN_RIGHT,  data[8] & 0x04);
+
+                       wacom_report_key(wcombo, BTN_SIDE,   data[8] & 0x20);
+                       wacom_report_key(wcombo, BTN_EXTRA,  data[8] & 0x10);
+                       t = (data[6] << 2) | ((data[7] >> 6) & 3);
+                       wacom_report_abs(wcombo, ABS_THROTTLE, (data[8] & 0x08) ? -t : t);
+
+               } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) {
+                       /* 2D mouse packet */
+                       wacom_report_key(wcombo, BTN_LEFT,   data[8] & 0x04);
+                       wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x08);
+                       wacom_report_key(wcombo, BTN_RIGHT,  data[8] & 0x10);
+                       wacom_report_rel(wcombo, REL_WHEEL, (data[8] & 0x01)
+                                                - ((data[8] & 0x02) >> 1));
+
+                       /* I3 2D mouse side buttons */
+                       if (wacom->features->type == INTUOS3) {
+                               wacom_report_key(wcombo, BTN_SIDE,   data[8] & 0x40);
+                               wacom_report_key(wcombo, BTN_EXTRA,  data[8] & 0x20);
+                       }
+
+               } else if (wacom->features->type < INTUOS3) {
+                       /* Lens cursor packets */
+                       wacom_report_key(wcombo, BTN_LEFT,   data[8] & 0x01);
+                       wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02);
+                       wacom_report_key(wcombo, BTN_RIGHT,  data[8] & 0x04);
+                       wacom_report_key(wcombo, BTN_SIDE,   data[8] & 0x10);
+                       wacom_report_key(wcombo, BTN_EXTRA,  data[8] & 0x08);
+               }
+       }
+
+       wacom_report_abs(wcombo, ABS_MISC, wacom->id[idx]); /* report tool id */
+       wacom_report_key(wcombo, wacom->tool[idx], 1);
+       wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+       return 1;
+}
+
+int wacom_wac_irq(struct wacom_wac *wacom_wac, void *wcombo)
+{
+       switch (wacom_wac->features->type) {
+               case PENPARTNER:
+                       return (wacom_penpartner_irq(wacom_wac, wcombo));
+                       break;
+               case PL:
+                       return (wacom_pl_irq(wacom_wac, wcombo));
+                       break;
+               case WACOM_G4:
+               case GRAPHIRE:
+                       return (wacom_graphire_irq(wacom_wac, wcombo));
+                       break;
+               case PTU:
+                       return (wacom_ptu_irq(wacom_wac, wcombo));
+                       break;
+               case INTUOS:
+               case INTUOS3:
+               case INTUOS3L:
+               case CINTIQ:
+                       return (wacom_intuos_irq(wacom_wac, wcombo));
+                       break;
+               default:
+                       return 0;
+       }
+       return 0;
+}
+
+void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+       switch (wacom_wac->features->type) {
+               case WACOM_G4:
+                       input_dev_g4(input_dev, wacom_wac);
+                       /* fall through */
+               case GRAPHIRE:
+                       input_dev_g(input_dev, wacom_wac);
+                       break;
+               case INTUOS3:
+               case INTUOS3L:
+               case CINTIQ:
+                       input_dev_i3(input_dev, wacom_wac);
+                       /* fall through */
+               case INTUOS:
+                       input_dev_i(input_dev, wacom_wac);
+                       break;
+               case PL:
+               case PTU:
+                       input_dev_pl(input_dev, wacom_wac);
+                       break;
+               case PENPARTNER:
+                       input_dev_pt(input_dev, wacom_wac);
+                       break;
+       }
+       return;
+}
+
+static struct wacom_features wacom_features[] = {
+       { "Wacom Penpartner",    7,   5040,  3780,  255, 32, PENPARTNER,        wacom_sys_irq },
+        { "Wacom Graphire",      8,  10206,  7422,  511, 32, GRAPHIRE, wacom_sys_irq },
+       { "Wacom Graphire2 4x5", 8,  10206,  7422,  511, 32, GRAPHIRE,  wacom_sys_irq },
+       { "Wacom Graphire2 5x7", 8,  13918, 10206,  511, 32, GRAPHIRE,  wacom_sys_irq },
+       { "Wacom Graphire3",     8,  10208,  7424,  511, 32, GRAPHIRE,  wacom_sys_irq },
+       { "Wacom Graphire3 6x8", 8,  16704, 12064,  511, 32, GRAPHIRE,  wacom_sys_irq },
+       { "Wacom Graphire4 4x5", 8,  10208,  7424,  511, 32, WACOM_G4,  wacom_sys_irq },
+       { "Wacom Graphire4 6x8", 8,  16704, 12064,  511, 32, WACOM_G4,  wacom_sys_irq },
+       { "Wacom Volito",        8,   5104,  3712,  511, 32, GRAPHIRE,  wacom_sys_irq },
+       { "Wacom PenStation2",   8,   3250,  2320,  255, 32, GRAPHIRE,  wacom_sys_irq },
+       { "Wacom Volito2 4x5",   8,   5104,  3712,  511, 32, GRAPHIRE,  wacom_sys_irq },
+       { "Wacom Volito2 2x3",   8,   3248,  2320,  511, 32, GRAPHIRE,  wacom_sys_irq },
+       { "Wacom PenPartner2",   8,   3250,  2320,  255, 32, GRAPHIRE,  wacom_sys_irq },
+       { "Wacom Intuos 4x5",   10,  12700, 10600, 1023, 15, INTUOS,    wacom_sys_irq},
+       { "Wacom Intuos 6x8",   10,  20320, 16240, 1023, 15, INTUOS,    wacom_sys_irq },
+       { "Wacom Intuos 9x12",  10,  30480, 24060, 1023, 15, INTUOS,    wacom_sys_irq },
+       { "Wacom Intuos 12x12", 10,  30480, 31680, 1023, 15, INTUOS,    wacom_sys_irq },
+       { "Wacom Intuos 12x18", 10,  45720, 31680, 1023, 15, INTUOS,    wacom_sys_irq},
+       { "Wacom PL400",         8,   5408,  4056,  255, 32, PL,        wacom_sys_irq },
+       { "Wacom PL500",         8,   6144,  4608,  255, 32, PL,        wacom_sys_irq },
+       { "Wacom PL600",         8,   6126,  4604,  255, 32, PL,        wacom_sys_irq },
+       { "Wacom PL600SX",       8,   6260,  5016,  255, 32, PL,        wacom_sys_irq },
+       { "Wacom PL550",         8,   6144,  4608,  511, 32, PL,        wacom_sys_irq },
+       { "Wacom PL800",         8,   7220,  5780,  511, 32, PL,        wacom_sys_irq },
+       { "Wacom PL700",         8,   6758,  5406,  511, 32, PL,        wacom_sys_irq },
+       { "Wacom PL510",         8,   6282,  4762,  511, 32, PL,        wacom_sys_irq },
+       { "Wacom DTU710",        8,  34080, 27660,  511, 32, PL,        wacom_sys_irq },
+       { "Wacom DTF521",        8,   6282,  4762,  511, 32, PL,        wacom_sys_irq },
+       { "Wacom DTF720",        8,   6858,  5506,  511, 32, PL,        wacom_sys_irq },
+       { "Wacom Cintiq Partner",8,  20480, 15360,  511, 32, PTU,       wacom_sys_irq },
+       { "Wacom Intuos2 4x5",   10, 12700, 10600, 1023, 15, INTUOS,    wacom_sys_irq },
+       { "Wacom Intuos2 6x8",   10, 20320, 16240, 1023, 15, INTUOS,    wacom_sys_irq },
+       { "Wacom Intuos2 9x12",  10, 30480, 24060, 1023, 15, INTUOS,    wacom_sys_irq },
+       { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 15, INTUOS,    wacom_sys_irq },
+       { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 15, INTUOS,    wacom_sys_irq },
+       { "Wacom Intuos3 4x5",   10, 25400, 20320, 1023, 15, INTUOS3,   wacom_sys_irq },
+       { "Wacom Intuos3 6x8",   10, 40640, 30480, 1023, 15, INTUOS3,   wacom_sys_irq },
+       { "Wacom Intuos3 9x12",  10, 60960, 45720, 1023, 15, INTUOS3,   wacom_sys_irq },
+       { "Wacom Intuos3 12x12", 10, 60960, 60960, 1023, 15, INTUOS3L,  wacom_sys_irq },
+       { "Wacom Intuos3 12x19", 10, 97536, 60960, 1023, 15, INTUOS3L,  wacom_sys_irq },
+       { "Wacom Intuos3 6x11",  10, 54204, 31750, 1023, 15, INTUOS3,   wacom_sys_irq },
+       { "Wacom Cintiq 21UX",   10, 87200, 65600, 1023, 15, CINTIQ,    wacom_sys_irq },
+       { "Wacom Intuos2 6x8",   10, 20320, 16240, 1023, 15, INTUOS,    wacom_sys_irq },
+       { }
+};
+
+static struct usb_device_id wacom_ids[] = {
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x00) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x10) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x11) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x12) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x13) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x14) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x15) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x16) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x60) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x61) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x62) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x63) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x64) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x20) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x21) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x22) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x23) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x24) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x30) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x31) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x32) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x33) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x34) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x35) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x37) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x38) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x39) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC0) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC4) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x03) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x41) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x42) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x43) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x44) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x45) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB0) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB1) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB2) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB3) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB4) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB5) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x3F) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) },
+       { }
+};
+
+const struct usb_device_id * get_device_table(void) {
+        const struct usb_device_id * id_table = wacom_ids;
+        return id_table;
+}
+
+struct wacom_features * get_wacom_feature(const struct usb_device_id * id) {
+        int index = id - wacom_ids;
+        struct wacom_features *wf = &wacom_features[index];
+        return wf;
+}
+
+MODULE_DEVICE_TABLE(usb, wacom_ids);
diff --git a/drivers/usb/input/wacom_wac.h b/drivers/usb/input/wacom_wac.h
new file mode 100644 (file)
index 0000000..ceae7bf
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * drivers/usb/input/wacom_wac.h
+ *
+ * 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.
+ */
+#ifndef WACOM_WAC_H
+#define WACOM_WAC_H
+
+#define STYLUS_DEVICE_ID       0x02
+#define CURSOR_DEVICE_ID       0x06
+#define ERASER_DEVICE_ID       0x0A
+
+enum {
+       PENPARTNER = 0,
+       GRAPHIRE,
+       WACOM_G4,
+       PTU,
+       PL,
+       INTUOS,
+       INTUOS3,
+       INTUOS3L,
+       CINTIQ,
+       MAX_TYPE
+};
+
+struct wacom_features {
+       char *name;
+       int pktlen;
+       int x_max;
+       int y_max;
+       int pressure_max;
+       int distance_max;
+       int type;
+       usb_complete_t irq;
+};
+
+struct wacom_wac {
+       signed char *data;
+        int tool[2];
+        int id[2];
+        __u32 serial[2];
+       struct wacom_features *features;
+};
+
+#endif
index 7b45fd3..7291e7a 100644 (file)
@@ -971,7 +971,7 @@ static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
                        DRIVER_VERSION, sizeof(DRIVER_VERSION));
 
        /* Register sysfs hooks (don't care about failure) */
-       sysfs_create_group(&intf->dev.kobj, &yld_attr_group);
+       ret = sysfs_create_group(&intf->dev.kobj, &yld_attr_group);
        return 0;
 }
 
index 88928a4..c29658f 100644 (file)
@@ -32,6 +32,16 @@ config USB_EMI26
          To compile this driver as a module, choose M here: the
          module will be called emi26.
 
+config USB_ADUTUX
+       tristate "ADU devices from Ontrak Control Systems (EXPERIMENTAL)"
+       depends on USB && EXPERIMENTAL
+       help
+         Say Y if you want to use an ADU device from Ontrak Control
+         Systems.
+
+         To compile this driver as a module, choose M here.  The module
+         will be called adutux.
+
 config USB_AUERSWALD
        tristate "USB Auerswald ISDN support (EXPERIMENTAL)"
        depends on USB && EXPERIMENTAL
@@ -115,19 +125,36 @@ config USB_CYTHERM
          To compile this driver as a module, choose M here: the
          module will be called cytherm.
 
-config USB_PHIDGETKIT
-       tristate "USB PhidgetKit support"
+config USB_PHIDGET
+       tristate "USB Phidgets drivers"
        depends on USB
        help
-         Say Y here if you want to connect a PhidgetKit USB device from
-         Phidgets Inc.
+         Say Y here to enable the various drivers for devices from
+         Phidgets inc.
+
+config USB_PHIDGETKIT
+       tristate "USB PhidgetInterfaceKit support"
+       depends on USB_PHIDGET
+       help
+         Say Y here if you want to connect a PhidgetInterfaceKit USB device
+         from Phidgets Inc.
 
          To compile this driver as a module, choose M here: the
          module will be called phidgetkit.
 
+config USB_PHIDGETMOTORCONTROL
+       tristate "USB PhidgetMotorControl support"
+       depends on USB_PHIDGET
+       help
+         Say Y here if you want to connect a PhidgetMotorControl USB device
+         from Phidgets Inc.
+
+         To compile this driver as a module, choose M here: the
+         module will be called phidgetmotorcontrol.
+
 config USB_PHIDGETSERVO
        tristate "USB PhidgetServo support"
-       depends on USB
+       depends on USB_PHIDGET
        help
          Say Y here if you want to connect an 1 or 4 Motor PhidgetServo 
          servo controller version 2.0 or 3.0.
@@ -151,6 +178,30 @@ config USB_IDMOUSE
 
          See also <http://www.fs.tum.de/~echtler/idmouse/>.
 
+config USB_FTDI_ELAN
+       tristate "Elan PCMCIA CardBus Adapter USB Client"
+       depends on USB
+       default M
+       help
+         ELAN's Uxxx series of adapters are USB to PCMCIA CardBus adapters.
+         Currently only the U132 adapter is available.
+
+         The U132 is specifically designed for CardBus PC cards that contain
+         an OHCI host controller. Typical PC cards are the Orange Mobile 3G
+         Option GlobeTrotter Fusion card. The U132 adapter will *NOT* work
+         with PC cards that do not contain an OHCI controller. To use a U132
+         adapter you will need this "ftdi-elan" module as well as the "u132-hcd"
+         module which is a USB host controller driver that talks to the OHCI
+         controller within CardBus card that are inserted in the U132 adapter.
+
+         This driver has been tested with a CardBus OHCI USB adapter, and
+         worked with a USB PEN Drive inserted into the first USB port of
+         the PCCARD. A rather pointless thing to do, but useful for testing.
+
+         See also the USB_U132_HCD entry "Elan U132 Adapter Host Controller"
+
+         It is safe to say M here.
+
 config USB_APPLEDISPLAY
        tristate "Apple Cinema Display support"
        depends on USB
index 2927260..2be70fa 100644 (file)
@@ -3,22 +3,25 @@
 # (the ones that don't fit into any other categories)
 #
 
+obj-$(CONFIG_USB_ADUTUX)       += adutux.o
 obj-$(CONFIG_USB_AUERSWALD)    += auerswald.o
 obj-$(CONFIG_USB_CYPRESS_CY7C63)+= cypress_cy7c63.o
 obj-$(CONFIG_USB_CYTHERM)      += cytherm.o
 obj-$(CONFIG_USB_EMI26)                += emi26.o
 obj-$(CONFIG_USB_EMI62)                += emi62.o
+obj-$(CONFIG_USB_FTDI_ELAN)    += ftdi-elan.o
 obj-$(CONFIG_USB_IDMOUSE)      += idmouse.o
 obj-$(CONFIG_USB_LCD)          += usblcd.o
 obj-$(CONFIG_USB_LD)           += ldusb.o
 obj-$(CONFIG_USB_LED)          += usbled.o
 obj-$(CONFIG_USB_LEGOTOWER)    += legousbtower.o
+obj-$(CONFIG_USB_PHIDGET)      += phidget.o
 obj-$(CONFIG_USB_PHIDGETKIT)   += phidgetkit.o
+obj-$(CONFIG_USB_PHIDGETMOTORCONTROL)  += phidgetmotorcontrol.o
 obj-$(CONFIG_USB_PHIDGETSERVO) += phidgetservo.o
 obj-$(CONFIG_USB_RIO500)       += rio500.o
 obj-$(CONFIG_USB_TEST)         += usbtest.o
 obj-$(CONFIG_USB_USS720)       += uss720.o
-obj-$(CONFIG_USB_APPLEDISPLAY) += appledisplay.o
 
 obj-$(CONFIG_USB_SISUSBVGA)    += sisusbvga/
 
diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c
new file mode 100644 (file)
index 0000000..d396319
--- /dev/null
@@ -0,0 +1,900 @@
+/*
+ * adutux - driver for ADU devices from Ontrak Control Systems
+ * This is an experimental driver. Use at your own risk.
+ * This driver is not supported by Ontrak Control Systems.
+ *
+ * Copyright (c) 2003 John Homppi (SCO, leave this notice here)
+ *
+ * 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.
+ *
+ * derived from the Lego USB Tower driver 0.56:
+ * Copyright (c) 2003 David Glance <davidgsf@sourceforge.net>
+ *               2001 Juergen Stuber <stuber@loria.fr>
+ * that was derived from USB Skeleton driver - 0.5
+ * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_USB_DEBUG
+static int debug = 5;
+#else
+static int debug = 1;
+#endif
+
+/* Use our own dbg macro */
+#undef dbg
+#define dbg(lvl, format, arg...)                                       \
+do {                                                                   \
+       if (debug >= lvl)                                               \
+               printk(KERN_DEBUG __FILE__ " : " format " \n", ## arg); \
+} while (0)
+
+
+/* Version Information */
+#define DRIVER_VERSION "v0.0.13"
+#define DRIVER_AUTHOR "John Homppi"
+#define DRIVER_DESC "adutux (see www.ontrak.net)"
+
+/* Module parameters */
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+/* Define these values to match your device */
+#define ADU_VENDOR_ID 0x0a07
+#define ADU_PRODUCT_ID 0x0064
+
+/* table of devices that work with this driver */
+static struct usb_device_id device_table [] = {
+       { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID) },          /* ADU100 */
+       { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+20) },       /* ADU120 */
+       { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+30) },       /* ADU130 */
+       { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+100) },      /* ADU200 */
+       { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+108) },      /* ADU208 */
+       { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+118) },      /* ADU218 */
+       { }/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define ADU_MINOR_BASE 0
+#else
+#define ADU_MINOR_BASE 67
+#endif
+
+/* we can have up to this number of device plugged in at once */
+#define MAX_DEVICES    16
+
+#define COMMAND_TIMEOUT        (2*HZ)  /* 60 second timeout for a command */
+
+/* Structure to hold all of our device specific stuff */
+struct adu_device {
+       struct semaphore        sem; /* locks this structure */
+       struct usb_device*      udev; /* save off the usb device pointer */
+       struct usb_interface*   interface;
+       unsigned char           minor; /* the starting minor number for this device */
+       char                    serial_number[8];
+
+       int                     open_count; /* number of times this port has been opened */
+
+       char*                   read_buffer_primary;
+       int                     read_buffer_length;
+       char*                   read_buffer_secondary;
+       int                     secondary_head;
+       int                     secondary_tail;
+       spinlock_t              buflock;
+
+       wait_queue_head_t       read_wait;
+       wait_queue_head_t       write_wait;
+
+       char*                   interrupt_in_buffer;
+       struct usb_endpoint_descriptor* interrupt_in_endpoint;
+       struct urb*             interrupt_in_urb;
+       int                     read_urb_finished;
+
+       char*                   interrupt_out_buffer;
+       struct usb_endpoint_descriptor* interrupt_out_endpoint;
+       struct urb*             interrupt_out_urb;
+};
+
+/* prevent races between open() and disconnect */
+static DEFINE_MUTEX(disconnect_mutex);
+static struct usb_driver adu_driver;
+
+static void adu_debug_data(int level, const char *function, int size,
+                          const unsigned char *data)
+{
+       int i;
+
+       if (debug < level)
+               return;
+
+       printk(KERN_DEBUG __FILE__": %s - length = %d, data = ",
+              function, size);
+       for (i = 0; i < size; ++i)
+               printk("%.2x ", data[i]);
+       printk("\n");
+}
+
+/**
+ * adu_abort_transfers
+ *      aborts transfers and frees associated data structures
+ */
+static void adu_abort_transfers(struct adu_device *dev)
+{
+       dbg(2," %s : enter", __FUNCTION__);
+
+       if (dev == NULL) {
+               dbg(1," %s : dev is null", __FUNCTION__);
+               goto exit;
+       }
+
+       if (dev->udev == NULL) {
+               dbg(1," %s : udev is null", __FUNCTION__);
+               goto exit;
+       }
+
+       dbg(2," %s : udev state %d", __FUNCTION__, dev->udev->state);
+       if (dev->udev->state == USB_STATE_NOTATTACHED) {
+               dbg(1," %s : udev is not attached", __FUNCTION__);
+               goto exit;
+       }
+
+       /* shutdown transfer */
+       usb_unlink_urb(dev->interrupt_in_urb);
+       usb_unlink_urb(dev->interrupt_out_urb);
+
+exit:
+       dbg(2," %s : leave", __FUNCTION__);
+}
+
+static void adu_delete(struct adu_device *dev)
+{
+       dbg(2, "%s enter", __FUNCTION__);
+
+       adu_abort_transfers(dev);
+
+       /* free data structures */
+       usb_free_urb(dev->interrupt_in_urb);
+       usb_free_urb(dev->interrupt_out_urb);
+       kfree(dev->read_buffer_primary);
+       kfree(dev->read_buffer_secondary);
+       kfree(dev->interrupt_in_buffer);
+       kfree(dev->interrupt_out_buffer);
+       kfree(dev);
+
+       dbg(2, "%s : leave", __FUNCTION__);
+}
+
+static void adu_interrupt_in_callback(struct urb *urb, struct pt_regs *regs)
+{
+       struct adu_device *dev = urb->context;
+
+       dbg(4," %s : enter, status %d", __FUNCTION__, urb->status);
+       adu_debug_data(5, __FUNCTION__, urb->actual_length,
+                      urb->transfer_buffer);
+
+       spin_lock(&dev->buflock);
+
+       if (urb->status != 0) {
+               if ((urb->status != -ENOENT) && (urb->status != -ECONNRESET)) {
+                       dbg(1," %s : nonzero status received: %d",
+                           __FUNCTION__, urb->status);
+               }
+               goto exit;
+       }
+
+       if (urb->actual_length > 0 && dev->interrupt_in_buffer[0] != 0x00) {
+               if (dev->read_buffer_length <
+                   (4 * le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize)) -
+                    (urb->actual_length)) {
+                       memcpy (dev->read_buffer_primary +
+                               dev->read_buffer_length,
+                               dev->interrupt_in_buffer, urb->actual_length);
+
+                       dev->read_buffer_length += urb->actual_length;
+                       dbg(2," %s reading  %d ", __FUNCTION__,
+                           urb->actual_length);
+               } else {
+                       dbg(1," %s : read_buffer overflow", __FUNCTION__);
+               }
+       }
+
+exit:
+       dev->read_urb_finished = 1;
+       spin_unlock(&dev->buflock);
+       /* always wake up so we recover from errors */
+       wake_up_interruptible(&dev->read_wait);
+       adu_debug_data(5, __FUNCTION__, urb->actual_length,
+                      urb->transfer_buffer);
+       dbg(4," %s : leave, status %d", __FUNCTION__, urb->status);
+}
+
+static void adu_interrupt_out_callback(struct urb *urb, struct pt_regs *regs)
+{
+       struct adu_device *dev = urb->context;
+
+       dbg(4," %s : enter, status %d", __FUNCTION__, urb->status);
+       adu_debug_data(5,__FUNCTION__, urb->actual_length, urb->transfer_buffer);
+
+       if (urb->status != 0) {
+               if ((urb->status != -ENOENT) &&
+                   (urb->status != -ECONNRESET)) {
+                       dbg(1, " %s :nonzero status received: %d",
+                           __FUNCTION__, urb->status);
+               }
+               goto exit;
+       }
+
+       wake_up_interruptible(&dev->write_wait);
+exit:
+
+       adu_debug_data(5, __FUNCTION__, urb->actual_length,
+                      urb->transfer_buffer);
+       dbg(4," %s : leave, status %d", __FUNCTION__, urb->status);
+}
+
+static int adu_open(struct inode *inode, struct file *file)
+{
+       struct adu_device *dev = NULL;
+       struct usb_interface *interface;
+       int subminor;
+       int retval = 0;
+
+       dbg(2,"%s : enter", __FUNCTION__);
+
+       subminor = iminor(inode);
+
+       mutex_lock(&disconnect_mutex);
+
+       interface = usb_find_interface(&adu_driver, subminor);
+       if (!interface) {
+               err("%s - error, can't find device for minor %d",
+                   __FUNCTION__, subminor);
+               retval = -ENODEV;
+               goto exit_no_device;
+       }
+
+       dev = usb_get_intfdata(interface);
+       if (!dev) {
+               retval = -ENODEV;
+               goto exit_no_device;
+       }
+
+       /* lock this device */
+       if ((retval = down_interruptible(&dev->sem))) {
+               dbg(2, "%s : sem down failed", __FUNCTION__);
+               goto exit_no_device;
+       }
+
+       /* increment our usage count for the device */
+       ++dev->open_count;
+       dbg(2,"%s : open count %d", __FUNCTION__, dev->open_count);
+
+       /* save device in the file's private structure */
+       file->private_data = dev;
+
+       /* initialize in direction */
+       dev->read_buffer_length = 0;
+
+       /* fixup first read by having urb waiting for it */
+       usb_fill_int_urb(dev->interrupt_in_urb,dev->udev,
+                        usb_rcvintpipe(dev->udev,
+                                       dev->interrupt_in_endpoint->bEndpointAddress),
+                        dev->interrupt_in_buffer,
+                        le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
+                        adu_interrupt_in_callback, dev,
+                        dev->interrupt_in_endpoint->bInterval);
+       /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */
+       dev->read_urb_finished = 0;
+       usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
+       /* we ignore failure */
+       /* end of fixup for first read */
+
+       up(&dev->sem);
+
+exit_no_device:
+       mutex_unlock(&disconnect_mutex);
+       dbg(2,"%s : leave, return value %d ", __FUNCTION__, retval);
+
+       return retval;
+}
+
+static int adu_release_internal(struct adu_device *dev)
+{
+       int retval = 0;
+
+       dbg(2," %s : enter", __FUNCTION__);
+
+       if (dev->udev == NULL) {
+               /* the device was unplugged before the file was released */
+               adu_delete(dev);
+               goto exit;
+       }
+
+       /* decrement our usage count for the device */
+       --dev->open_count;
+       dbg(2," %s : open count %d", __FUNCTION__, dev->open_count);
+       if (dev->open_count <= 0) {
+               adu_abort_transfers(dev);
+               dev->open_count = 0;
+       }
+
+exit:
+       dbg(2," %s : leave", __FUNCTION__);
+       return retval;
+}
+
+static int adu_release(struct inode *inode, struct file *file)
+{
+       struct adu_device *dev = NULL;
+       int retval = 0;
+
+       dbg(2," %s : enter", __FUNCTION__);
+
+       if (file == NULL) {
+               dbg(1," %s : file is NULL", __FUNCTION__);
+               retval = -ENODEV;
+               goto exit;
+       }
+
+       dev = file->private_data;
+
+       if (dev == NULL) {
+               dbg(1," %s : object is NULL", __FUNCTION__);
+               retval = -ENODEV;
+               goto exit;
+       }
+
+       /* lock our device */
+       down(&dev->sem); /* not interruptible */
+
+       if (dev->open_count <= 0) {
+               dbg(1," %s : device not opened", __FUNCTION__);
+               retval = -ENODEV;
+               goto exit;
+       }
+
+       /* do the work */
+       retval = adu_release_internal(dev);
+
+exit:
+       up(&dev->sem);
+       dbg(2," %s : leave, return value %d", __FUNCTION__, retval);
+       return retval;
+}
+
+static ssize_t adu_read(struct file *file, __user char *buffer, size_t count,
+                       loff_t *ppos)
+{
+       struct adu_device *dev;
+       size_t bytes_read = 0;
+       size_t bytes_to_read = count;
+       int i;
+       int retval = 0;
+       int timeout = 0;
+       int should_submit = 0;
+       unsigned long flags;
+       DECLARE_WAITQUEUE(wait, current);
+
+       dbg(2," %s : enter, count = %Zd, file=%p", __FUNCTION__, count, file);
+
+       dev = file->private_data;
+       dbg(2," %s : dev=%p", __FUNCTION__, dev);
+       /* lock this object */
+       if (down_interruptible(&dev->sem))
+               return -ERESTARTSYS;
+
+       /* verify that the device wasn't unplugged */
+       if (dev->udev == NULL || dev->minor == 0) {
+               retval = -ENODEV;
+               err("No device or device unplugged %d", retval);
+               goto exit;
+       }
+
+       /* verify that some data was requested */
+       if (count == 0) {
+               dbg(1," %s : read request of 0 bytes", __FUNCTION__);
+               goto exit;
+       }
+
+       timeout = COMMAND_TIMEOUT;
+       dbg(2," %s : about to start looping", __FUNCTION__);
+       while (bytes_to_read) {
+               int data_in_secondary = dev->secondary_tail - dev->secondary_head;
+               dbg(2," %s : while, data_in_secondary=%d, status=%d",
+                   __FUNCTION__, data_in_secondary,
+                   dev->interrupt_in_urb->status);
+
+               if (data_in_secondary) {
+                       /* drain secondary buffer */
+                       int amount = bytes_to_read < data_in_secondary ? bytes_to_read : data_in_secondary;
+                       i = copy_to_user(buffer, dev->read_buffer_secondary+dev->secondary_head, amount);
+                       if (i < 0) {
+                               retval = -EFAULT;
+                               goto exit;
+                       }
+                       dev->secondary_head += (amount - i);
+                       bytes_read += (amount - i);
+                       bytes_to_read -= (amount - i);
+                       if (i) {
+                               retval = bytes_read ? bytes_read : -EFAULT;
+                               goto exit;
+                       }
+               } else {
+                       /* we check the primary buffer */
+                       spin_lock_irqsave (&dev->buflock, flags);
+                       if (dev->read_buffer_length) {
+                               /* we secure access to the primary */
+                               char *tmp;
+                               dbg(2," %s : swap, read_buffer_length = %d",
+                                   __FUNCTION__, dev->read_buffer_length);
+                               tmp = dev->read_buffer_secondary;
+                               dev->read_buffer_secondary = dev->read_buffer_primary;
+                               dev->read_buffer_primary = tmp;
+                               dev->secondary_head = 0;
+                               dev->secondary_tail = dev->read_buffer_length;
+                               dev->read_buffer_length = 0;
+                               spin_unlock_irqrestore(&dev->buflock, flags);
+                               /* we have a free buffer so use it */
+                               should_submit = 1;
+                       } else {
+                               /* even the primary was empty - we may need to do IO */
+                               if (dev->interrupt_in_urb->status == -EINPROGRESS) {
+                                       /* somebody is doing IO */
+                                       spin_unlock_irqrestore(&dev->buflock, flags);
+                                       dbg(2," %s : submitted already", __FUNCTION__);
+                               } else {
+                                       /* we must initiate input */
+                                       dbg(2," %s : initiate input", __FUNCTION__);
+                                       dev->read_urb_finished = 0;
+
+                                       usb_fill_int_urb(dev->interrupt_in_urb,dev->udev,
+                                                        usb_rcvintpipe(dev->udev,
+                                                                       dev->interrupt_in_endpoint->bEndpointAddress),
+                                                        dev->interrupt_in_buffer,
+                                                        le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
+                                                        adu_interrupt_in_callback,
+                                                        dev,
+                                                        dev->interrupt_in_endpoint->bInterval);
+                                       retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
+                                       if (!retval) {
+                                               spin_unlock_irqrestore(&dev->buflock, flags);
+                                               dbg(2," %s : submitted OK", __FUNCTION__);
+                                       } else {
+                                               if (retval == -ENOMEM) {
+                                                       retval = bytes_read ? bytes_read : -ENOMEM;
+                                               }
+                                               spin_unlock_irqrestore(&dev->buflock, flags);
+                                               dbg(2," %s : submit failed", __FUNCTION__);
+                                               goto exit;
+                                       }
+                               }
+
+                               /* we wait for I/O to complete */
+                               set_current_state(TASK_INTERRUPTIBLE);
+                               add_wait_queue(&dev->read_wait, &wait);
+                               if (!dev->read_urb_finished)
+                                       timeout = schedule_timeout(COMMAND_TIMEOUT);
+                               else
+                                       set_current_state(TASK_RUNNING);
+                               remove_wait_queue(&dev->read_wait, &wait);
+
+                               if (timeout <= 0) {
+                                       dbg(2," %s : timeout", __FUNCTION__);
+                                       retval = bytes_read ? bytes_read : -ETIMEDOUT;
+                                       goto exit;
+                               }
+
+                               if (signal_pending(current)) {
+                                       dbg(2," %s : signal pending", __FUNCTION__);
+                                       retval = bytes_read ? bytes_read : -EINTR;
+                                       goto exit;
+                               }
+                       }
+               }
+       }
+
+       retval = bytes_read;
+       /* if the primary buffer is empty then use it */
+       if (should_submit && !dev->interrupt_in_urb->status==-EINPROGRESS) {
+               usb_fill_int_urb(dev->interrupt_in_urb,dev->udev,
+                                usb_rcvintpipe(dev->udev,
+                                               dev->interrupt_in_endpoint->bEndpointAddress),
+                                               dev->interrupt_in_buffer,
+                                               le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
+                                               adu_interrupt_in_callback,
+                                               dev,
+                                               dev->interrupt_in_endpoint->bInterval);
+               /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */
+               dev->read_urb_finished = 0;
+               usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
+               /* we ignore failure */
+       }
+
+exit:
+       /* unlock the device */
+       up(&dev->sem);
+
+       dbg(2," %s : leave, return value %d", __FUNCTION__, retval);
+       return retval;
+}
+
+static ssize_t adu_write(struct file *file, const __user char *buffer,
+                        size_t count, loff_t *ppos)
+{
+       struct adu_device *dev;
+       size_t bytes_written = 0;
+       size_t bytes_to_write;
+       size_t buffer_size;
+       int retval = 0;
+       int timeout = 0;
+
+       dbg(2," %s : enter, count = %Zd", __FUNCTION__, count);
+
+       dev = file->private_data;
+
+       /* lock this object */
+       down_interruptible(&dev->sem);
+
+       /* verify that the device wasn't unplugged */
+       if (dev->udev == NULL || dev->minor == 0) {
+               retval = -ENODEV;
+               err("No device or device unplugged %d", retval);
+               goto exit;
+       }
+
+       /* verify that we actually have some data to write */
+       if (count == 0) {
+               dbg(1," %s : write request of 0 bytes", __FUNCTION__);
+               goto exit;
+       }
+
+
+       while (count > 0) {
+               if (dev->interrupt_out_urb->status == -EINPROGRESS) {
+                       timeout = COMMAND_TIMEOUT;
+
+                       while (timeout > 0) {
+                               if (signal_pending(current)) {
+                               dbg(1," %s : interrupted", __FUNCTION__);
+                               retval = -EINTR;
+                               goto exit;
+                       }
+                       up(&dev->sem);
+                       timeout = interruptible_sleep_on_timeout(&dev->write_wait, timeout);
+                       down_interruptible(&dev->sem);
+                       if (timeout > 0) {
+                               break;
+                       }
+                       dbg(1," %s : interrupted timeout: %d", __FUNCTION__, timeout);
+               }
+
+
+               dbg(1," %s : final timeout: %d", __FUNCTION__, timeout);
+
+               if (timeout == 0) {
+                       dbg(1, "%s - command timed out.", __FUNCTION__);
+                       retval = -ETIMEDOUT;
+                       goto exit;
+               }
+
+               dbg(4," %s : in progress, count = %Zd", __FUNCTION__, count);
+
+               } else {
+                       dbg(4," %s : sending, count = %Zd", __FUNCTION__, count);
+
+                       /* write the data into interrupt_out_buffer from userspace */
+                       buffer_size = le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize);
+                       bytes_to_write = count > buffer_size ? buffer_size : count;
+                       dbg(4," %s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd",
+                           __FUNCTION__, buffer_size, count, bytes_to_write);
+
+                       if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write) != 0) {
+                               retval = -EFAULT;
+                               goto exit;
+                       }
+
+                       /* send off the urb */
+                       usb_fill_int_urb(
+                               dev->interrupt_out_urb,
+                               dev->udev,
+                               usb_sndintpipe(dev->udev, dev->interrupt_out_endpoint->bEndpointAddress),
+                               dev->interrupt_out_buffer,
+                               bytes_to_write,
+                               adu_interrupt_out_callback,
+                               dev,
+                               dev->interrupt_in_endpoint->bInterval);
+                       /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */
+                       dev->interrupt_out_urb->actual_length = bytes_to_write;
+                       retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL);
+                       if (retval < 0) {
+                               err("Couldn't submit interrupt_out_urb %d", retval);
+                               goto exit;
+                       }
+
+                       buffer += bytes_to_write;
+                       count -= bytes_to_write;
+
+                       bytes_written += bytes_to_write;
+               }
+       }
+
+       retval = bytes_written;
+
+exit:
+       /* unlock the device */
+       up(&dev->sem);
+
+       dbg(2," %s : leave, return value %d", __FUNCTION__, retval);
+
+       return retval;
+}
+
+/* file operations needed when we register this driver */
+static struct file_operations adu_fops = {
+       .owner = THIS_MODULE,
+       .read  = adu_read,
+       .write = adu_write,
+       .open = adu_open,
+       .release = adu_release,
+};
+
+/*
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with devfs and the driver core
+ */
+static struct usb_class_driver adu_class = {
+       .name = "usb/adutux%d",
+       .fops = &adu_fops,
+       .minor_base = ADU_MINOR_BASE,
+};
+
+/**
+ * adu_probe
+ *
+ * Called by the usb core when a new device is connected that it thinks
+ * this driver might be interested in.
+ */
+static int adu_probe(struct usb_interface *interface,
+                    const struct usb_device_id *id)
+{
+       struct usb_device *udev = interface_to_usbdev(interface);
+       struct adu_device *dev = NULL;
+       struct usb_host_interface *iface_desc;
+       struct usb_endpoint_descriptor *endpoint;
+       int retval = -ENODEV;
+       int in_end_size;
+       int out_end_size;
+       int i;
+
+       dbg(2," %s : enter", __FUNCTION__);
+
+       if (udev == NULL) {
+               dev_err(&interface->dev, "udev is NULL.\n");
+               goto exit;
+       }
+
+       /* allocate memory for our device state and intialize it */
+       dev = kzalloc(sizeof(struct adu_device), GFP_KERNEL);
+       if (dev == NULL) {
+               dev_err(&interface->dev, "Out of memory\n");
+               retval = -ENOMEM;
+               goto exit;
+       }
+
+       init_MUTEX(&dev->sem);
+       spin_lock_init(&dev->buflock);
+       dev->udev = udev;
+       init_waitqueue_head(&dev->read_wait);
+       init_waitqueue_head(&dev->write_wait);
+
+       iface_desc = &interface->altsetting[0];
+
+       /* set up the endpoint information */
+       for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+               endpoint = &iface_desc->endpoint[i].desc;
+
+               if (usb_endpoint_is_int_in(endpoint))
+                       dev->interrupt_in_endpoint = endpoint;
+
+               if (usb_endpoint_is_int_out(endpoint))
+                       dev->interrupt_out_endpoint = endpoint;
+       }
+       if (dev->interrupt_in_endpoint == NULL) {
+               dev_err(&interface->dev, "interrupt in endpoint not found\n");
+               goto error;
+       }
+       if (dev->interrupt_out_endpoint == NULL) {
+               dev_err(&interface->dev, "interrupt out endpoint not found\n");
+               goto error;
+       }
+
+       in_end_size = le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize);
+       out_end_size = le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize);
+
+       dev->read_buffer_primary = kmalloc((4 * in_end_size), GFP_KERNEL);
+       if (!dev->read_buffer_primary) {
+               dev_err(&interface->dev, "Couldn't allocate read_buffer_primary\n");
+               retval = -ENOMEM;
+               goto error;
+       }
+
+       /* debug code prime the buffer */
+       memset(dev->read_buffer_primary, 'a', in_end_size);
+       memset(dev->read_buffer_primary + in_end_size, 'b', in_end_size);
+       memset(dev->read_buffer_primary + (2 * in_end_size), 'c', in_end_size);
+       memset(dev->read_buffer_primary + (3 * in_end_size), 'd', in_end_size);
+
+       dev->read_buffer_secondary = kmalloc((4 * in_end_size), GFP_KERNEL);
+       if (!dev->read_buffer_secondary) {
+               dev_err(&interface->dev, "Couldn't allocate read_buffer_secondary\n");
+               retval = -ENOMEM;
+               goto error;
+       }
+
+       /* debug code prime the buffer */
+       memset(dev->read_buffer_secondary, 'e', in_end_size);
+       memset(dev->read_buffer_secondary + in_end_size, 'f', in_end_size);
+       memset(dev->read_buffer_secondary + (2 * in_end_size), 'g', in_end_size);
+       memset(dev->read_buffer_secondary + (3 * in_end_size), 'h', in_end_size);
+
+       dev->interrupt_in_buffer = kmalloc(in_end_size, GFP_KERNEL);
+       if (!dev->interrupt_in_buffer) {
+               dev_err(&interface->dev, "Couldn't allocate interrupt_in_buffer\n");
+               goto error;
+       }
+
+       /* debug code prime the buffer */
+       memset(dev->interrupt_in_buffer, 'i', in_end_size);
+
+       dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!dev->interrupt_in_urb) {
+               dev_err(&interface->dev, "Couldn't allocate interrupt_in_urb\n");
+               goto error;
+       }
+       dev->interrupt_out_buffer = kmalloc(out_end_size, GFP_KERNEL);
+       if (!dev->interrupt_out_buffer) {
+               dev_err(&interface->dev, "Couldn't allocate interrupt_out_buffer\n");
+               goto error;
+       }
+       dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!dev->interrupt_out_urb) {
+               dev_err(&interface->dev, "Couldn't allocate interrupt_out_urb\n");
+               goto error;
+       }
+
+       if (!usb_string(udev, udev->descriptor.iSerialNumber, dev->serial_number,
+                       sizeof(dev->serial_number))) {
+               dev_err(&interface->dev, "Could not retrieve serial number\n");
+               goto error;
+       }
+       dbg(2," %s : serial_number=%s", __FUNCTION__, dev->serial_number);
+
+       /* we can register the device now, as it is ready */
+       usb_set_intfdata(interface, dev);
+
+       retval = usb_register_dev(interface, &adu_class);
+
+       if (retval) {
+               /* something prevented us from registering this driver */
+               dev_err(&interface->dev, "Not able to get a minor for this device.\n");
+               usb_set_intfdata(interface, NULL);
+               goto error;
+       }
+
+       dev->minor = interface->minor;
+
+       /* let the user know what node this device is now attached to */
+       dev_info(&interface->dev, "ADU%d %s now attached to /dev/usb/adutux%d",
+                udev->descriptor.idProduct, dev->serial_number,
+                (dev->minor - ADU_MINOR_BASE));
+exit:
+       dbg(2," %s : leave, return value %p (dev)", __FUNCTION__, dev);
+
+       return retval;
+
+error:
+       adu_delete(dev);
+       return retval;
+}
+
+/**
+ * adu_disconnect
+ *
+ * Called by the usb core when the device is removed from the system.
+ */
+static void adu_disconnect(struct usb_interface *interface)
+{
+       struct adu_device *dev;
+       int minor;
+
+       dbg(2," %s : enter", __FUNCTION__);
+
+       mutex_lock(&disconnect_mutex); /* not interruptible */
+
+       dev = usb_get_intfdata(interface);
+       usb_set_intfdata(interface, NULL);
+
+       down(&dev->sem); /* not interruptible */
+
+       minor = dev->minor;
+
+       /* give back our minor */
+       usb_deregister_dev(interface, &adu_class);
+       dev->minor = 0;
+
+       /* if the device is not opened, then we clean up right now */
+       dbg(2," %s : open count %d", __FUNCTION__, dev->open_count);
+       if (!dev->open_count) {
+               up(&dev->sem);
+               adu_delete(dev);
+       } else {
+               dev->udev = NULL;
+               up(&dev->sem);
+       }
+
+       mutex_unlock(&disconnect_mutex);
+
+       dev_info(&interface->dev, "ADU device adutux%d now disconnected",
+                (minor - ADU_MINOR_BASE));
+
+       dbg(2," %s : leave", __FUNCTION__);
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver adu_driver = {
+       .name = "adutux",
+       .probe = adu_probe,
+       .disconnect = adu_disconnect,
+       .id_table = device_table,
+};
+
+static int __init adu_init(void)
+{
+       int result;
+
+       dbg(2," %s : enter", __FUNCTION__);
+
+       /* register this driver with the USB subsystem */
+       result = usb_register(&adu_driver);
+       if (result < 0) {
+               err("usb_register failed for the "__FILE__" driver. "
+                   "Error number %d", result);
+               goto exit;
+       }
+
+       info("adutux " DRIVER_DESC " " DRIVER_VERSION);
+       info("adutux is an experimental driver. Use at your own risk");
+
+exit:
+       dbg(2," %s : leave, return value %d", __FUNCTION__, result);
+
+       return result;
+}
+
+static void __exit adu_exit(void)
+{
+       dbg(2," %s : enter", __FUNCTION__);
+       /* deregister this driver with the USB subsystem */
+       usb_deregister(&adu_driver);
+       dbg(2," %s : leave", __FUNCTION__);
+}
+
+module_init(adu_init);
+module_exit(adu_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
index 1fef36e..4fd2110 100644 (file)
@@ -806,7 +806,7 @@ static void auerbuf_releasebuf( pauerbuf_t bp)
 0              Initial, OK
 -EINPROGRESS   during submission until end
 -ENOENT                if urb is unlinked
--ETIMEDOUT     Transfer timed out, NAK
+-ETIME         Device did not respond
 -ENOMEM                Memory Overflow
 -ENODEV                Specified USB-device or bus doesn't exist
 -ENXIO         URB already queued
@@ -832,7 +832,7 @@ static int auerswald_status_retry (int status)
 {
        switch (status) {
        case 0:
-       case -ETIMEDOUT:
+       case -ETIME:
        case -EOVERFLOW:
        case -EAGAIN:
        case -EPIPE:
@@ -1858,7 +1858,7 @@ static int auerchar_release (struct inode *inode, struct file *file)
 
 /*----------------------------------------------------------------------*/
 /* File operation structure                                             */
-static struct file_operations auerswald_fops =
+static const struct file_operations auerswald_fops =
 {
        .owner =        THIS_MODULE,
        .llseek =       no_llseek,
index 9c46746..b63b5f3 100644 (file)
@@ -209,7 +209,7 @@ static int cypress_probe(struct usb_interface *interface,
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
        if (dev == NULL) {
                dev_err(&interface->dev, "Out of memory!\n");
-               goto error;
+               goto error_mem;
        }
 
        dev->udev = usb_get_dev(interface_to_usbdev(interface));
@@ -218,15 +218,26 @@ static int cypress_probe(struct usb_interface *interface,
        usb_set_intfdata(interface, dev);
 
        /* create device attribute files */
-       device_create_file(&interface->dev, &dev_attr_port0);
-       device_create_file(&interface->dev, &dev_attr_port1);
+       retval = device_create_file(&interface->dev, &dev_attr_port0);
+       if (retval)
+               goto error;
+       retval = device_create_file(&interface->dev, &dev_attr_port1);
+       if (retval)
+               goto error;
 
        /* let the user know that the device is now attached */
        dev_info(&interface->dev,
                 "Cypress CY7C63xxx device now attached\n");
+       return 0;
 
-       retval = 0;
 error:
+       device_remove_file(&interface->dev, &dev_attr_port0);
+       device_remove_file(&interface->dev, &dev_attr_port1);
+       usb_set_intfdata(interface, NULL);
+       usb_put_dev(dev->udev);
+       kfree(dev);
+
+error_mem:
        return retval;
 }
 
index b20bec4..04e87ac 100644 (file)
@@ -353,7 +353,7 @@ static int cytherm_probe(struct usb_interface *interface,
        dev = kzalloc (sizeof(struct usb_cytherm), GFP_KERNEL);
        if (dev == NULL) {
                dev_err (&interface->dev, "Out of memory\n");
-               goto error;
+               goto error_mem;
        }
 
        dev->udev = usb_get_dev(udev);
@@ -362,18 +362,35 @@ static int cytherm_probe(struct usb_interface *interface,
 
        dev->brightness = 0xFF;
 
-       device_create_file(&interface->dev, &dev_attr_brightness);   
-       device_create_file(&interface->dev, &dev_attr_temp);
-       device_create_file(&interface->dev, &dev_attr_button);
-       device_create_file(&interface->dev, &dev_attr_port0);
-       device_create_file(&interface->dev, &dev_attr_port1);
+       retval = device_create_file(&interface->dev, &dev_attr_brightness);
+       if (retval)
+               goto error;
+       retval = device_create_file(&interface->dev, &dev_attr_temp);
+       if (retval)
+               goto error;
+       retval = device_create_file(&interface->dev, &dev_attr_button);
+       if (retval)
+               goto error;
+       retval = device_create_file(&interface->dev, &dev_attr_port0);
+       if (retval)
+               goto error;
+       retval = device_create_file(&interface->dev, &dev_attr_port1);
+       if (retval)
+               goto error;
 
-       dev_info (&interface->dev, 
+       dev_info (&interface->dev,
                  "Cypress thermometer device now attached\n");
        return 0;
-
- error:
+error:
+       device_remove_file(&interface->dev, &dev_attr_brightness);
+       device_remove_file(&interface->dev, &dev_attr_temp);
+       device_remove_file(&interface->dev, &dev_attr_button);
+       device_remove_file(&interface->dev, &dev_attr_port0);
+       device_remove_file(&interface->dev, &dev_attr_port1);
+       usb_set_intfdata (interface, NULL);
+       usb_put_dev(dev->udev);
        kfree(dev);
+error_mem:
        return retval;
 }
 
diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c
new file mode 100644 (file)
index 0000000..b88a094
--- /dev/null
@@ -0,0 +1,2809 @@
+/*
+* USB FTDI client driver for Elan Digital Systems's Uxxx adapters
+*
+* Copyright(C) 2006 Elan Digital Systems Limited
+* http://www.elandigitalsystems.com
+*
+* Author and Maintainer - Tony Olech - Elan Digital Systems
+* tony.olech@elandigitalsystems.com
+*
+* This program is free software;you can redistribute it and/or
+* modify it under the terms of the GNU General Public License as
+* published by the Free Software Foundation, version 2.
+*
+*
+* This driver was written by Tony Olech(tony.olech@elandigitalsystems.com)
+* based on various USB client drivers in the 2.6.15 linux kernel
+* with constant reference to the 3rd Edition of Linux Device Drivers
+* published by O'Reilly
+*
+* The U132 adapter is a USB to CardBus adapter specifically designed
+* for PC cards that contain an OHCI host controller. Typical PC cards
+* are the Orange Mobile 3G Option GlobeTrotter Fusion card.
+*
+* The U132 adapter will *NOT *work with PC cards that do not contain
+* an OHCI controller. A simple way to test whether a PC card has an
+* OHCI controller as an interface is to insert the PC card directly
+* into a laptop(or desktop) with a CardBus slot and if "lspci" shows
+* a new USB controller and "lsusb -v" shows a new OHCI Host Controller
+* then there is a good chance that the U132 adapter will support the
+* PC card.(you also need the specific client driver for the PC card)
+*
+* Please inform the Author and Maintainer about any PC cards that
+* contain OHCI Host Controller and work when directly connected to
+* an embedded CardBus slot but do not work when they are connected
+* via an ELAN U132 adapter.
+*
+*/
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+MODULE_AUTHOR("Tony Olech");
+MODULE_DESCRIPTION("FTDI ELAN driver");
+MODULE_LICENSE("GPL");
+#define INT_MODULE_PARM(n, v) static int n = v;module_param(n, int, 0444)
+extern struct platform_driver u132_platform_driver;
+static struct workqueue_struct *status_queue;
+static struct workqueue_struct *command_queue;
+static struct workqueue_struct *respond_queue;
+/*
+* ftdi_module_lock exists to protect access to global variables
+*
+*/
+static struct semaphore ftdi_module_lock;
+static int ftdi_instances = 0;
+static struct list_head ftdi_static_list;
+/*
+* end of the global variables protected by ftdi_module_lock
+*/
+#include "usb_u132.h"
+#define TD_DEVNOTRESP 5
+/* Define these values to match your devices*/
+#define USB_FTDI_ELAN_VENDOR_ID 0x0403
+#define USB_FTDI_ELAN_PRODUCT_ID 0xd6ea
+/* table of devices that work with this driver*/
+static struct usb_device_id ftdi_elan_table[] = {
+        {USB_DEVICE(USB_FTDI_ELAN_VENDOR_ID, USB_FTDI_ELAN_PRODUCT_ID)},
+        { /* Terminating entry */ }
+};
+
+MODULE_DEVICE_TABLE(usb, ftdi_elan_table);
+/* only the jtag(firmware upgrade device) interface requires
+* a device file and corresponding minor number, but the
+* interface is created unconditionally - I suppose it could
+* be configured or not according to a module parameter.
+* But since we(now) require one interface per device,
+* and since it unlikely that a normal installation would
+* require more than a couple of elan-ftdi devices, 8 seems
+* like a reasonable limit to have here, and if someone
+* really requires more than 8 devices, then they can frig the
+* code and recompile
+*/
+#define USB_FTDI_ELAN_MINOR_BASE 192
+#define COMMAND_BITS 5
+#define COMMAND_SIZE (1<<COMMAND_BITS)
+#define COMMAND_MASK (COMMAND_SIZE-1)
+struct u132_command {
+        u8 header;
+        u16 length;
+        u8 address;
+        u8 width;
+        u32 value;
+        int follows;
+        void *buffer;
+};
+#define RESPOND_BITS 5
+#define RESPOND_SIZE (1<<RESPOND_BITS)
+#define RESPOND_MASK (RESPOND_SIZE-1)
+struct u132_respond {
+        u8 header;
+        u8 address;
+        u32 *value;
+        int *result;
+        struct completion wait_completion;
+};
+struct u132_target {
+        void *endp;
+        struct urb *urb;
+        int toggle_bits;
+        int error_count;
+        int condition_code;
+        int repeat_number;
+        int halted;
+        int skipped;
+        int actual;
+        int non_null;
+        int active;
+        int abandoning;
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+                int toggle_bits, int error_count, int condition_code,
+                int repeat_number, int halted, int skipped, int actual,
+                int non_null);
+};
+/* Structure to hold all of our device specific stuff*/
+struct usb_ftdi {
+        struct list_head ftdi_list;
+        struct semaphore u132_lock;
+        int command_next;
+        int command_head;
+        struct u132_command command[COMMAND_SIZE];
+        int respond_next;
+        int respond_head;
+        struct u132_respond respond[RESPOND_SIZE];
+        struct u132_target target[4];
+        char device_name[16];
+        unsigned synchronized:1;
+        unsigned enumerated:1;
+        unsigned registered:1;
+        unsigned initialized:1;
+        unsigned card_ejected:1;
+        int function;
+        int sequence_num;
+        int disconnected;
+        int gone_away;
+        int stuck_status;
+        int status_queue_delay;
+        struct semaphore sw_lock;
+        struct usb_device *udev;
+        struct usb_interface *interface;
+        struct usb_class_driver *class;
+        struct work_struct status_work;
+        struct work_struct command_work;
+        struct work_struct respond_work;
+        struct u132_platform_data platform_data;
+        struct resource resources[0];
+        struct platform_device platform_dev;
+        unsigned char *bulk_in_buffer;
+        size_t bulk_in_size;
+        size_t bulk_in_last;
+        size_t bulk_in_left;
+        __u8 bulk_in_endpointAddr;
+        __u8 bulk_out_endpointAddr;
+        struct kref kref;
+        u32 controlreg;
+        u8 response[4 + 1024];
+        int expected;
+        int recieved;
+        int ed_found;
+};
+#define kref_to_usb_ftdi(d) container_of(d, struct usb_ftdi, kref)
+#define platform_device_to_usb_ftdi(d) container_of(d, struct usb_ftdi, \
+        platform_dev)
+static struct usb_driver ftdi_elan_driver;
+static void ftdi_elan_delete(struct kref *kref)
+{
+        struct usb_ftdi *ftdi = kref_to_usb_ftdi(kref);
+        dev_warn(&ftdi->udev->dev, "FREEING ftdi=%p\n", ftdi);
+        usb_put_dev(ftdi->udev);
+        ftdi->disconnected += 1;
+        down(&ftdi_module_lock);
+        list_del_init(&ftdi->ftdi_list);
+        ftdi_instances -= 1;
+        up(&ftdi_module_lock);
+        kfree(ftdi->bulk_in_buffer);
+        ftdi->bulk_in_buffer = NULL;
+}
+
+static void ftdi_elan_put_kref(struct usb_ftdi *ftdi)
+{
+        kref_put(&ftdi->kref, ftdi_elan_delete);
+}
+
+static void ftdi_elan_get_kref(struct usb_ftdi *ftdi)
+{
+        kref_get(&ftdi->kref);
+}
+
+static void ftdi_elan_init_kref(struct usb_ftdi *ftdi)
+{
+        kref_init(&ftdi->kref);
+}
+
+static void ftdi_status_requeue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+        if (delta > 0) {
+                if (queue_delayed_work(status_queue, &ftdi->status_work, delta))
+                        return;
+        } else if (queue_work(status_queue, &ftdi->status_work))
+                return;
+        kref_put(&ftdi->kref, ftdi_elan_delete);
+        return;
+}
+
+static void ftdi_status_queue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+        if (delta > 0) {
+                if (queue_delayed_work(status_queue, &ftdi->status_work, delta))
+                        kref_get(&ftdi->kref);
+        } else if (queue_work(status_queue, &ftdi->status_work))
+                kref_get(&ftdi->kref);
+        return;
+}
+
+static void ftdi_status_cancel_work(struct usb_ftdi *ftdi)
+{
+        if (cancel_delayed_work(&ftdi->status_work))
+                kref_put(&ftdi->kref, ftdi_elan_delete);
+}
+
+static void ftdi_command_requeue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+        if (delta > 0) {
+                if (queue_delayed_work(command_queue, &ftdi->command_work,
+                        delta))
+                        return;
+        } else if (queue_work(command_queue, &ftdi->command_work))
+                return;
+        kref_put(&ftdi->kref, ftdi_elan_delete);
+        return;
+}
+
+static void ftdi_command_queue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+        if (delta > 0) {
+                if (queue_delayed_work(command_queue, &ftdi->command_work,
+                        delta))
+                        kref_get(&ftdi->kref);
+        } else if (queue_work(command_queue, &ftdi->command_work))
+                kref_get(&ftdi->kref);
+        return;
+}
+
+static void ftdi_command_cancel_work(struct usb_ftdi *ftdi)
+{
+        if (cancel_delayed_work(&ftdi->command_work))
+                kref_put(&ftdi->kref, ftdi_elan_delete);
+}
+
+static void ftdi_response_requeue_work(struct usb_ftdi *ftdi,
+        unsigned int delta)
+{
+        if (delta > 0) {
+                if (queue_delayed_work(respond_queue, &ftdi->respond_work,
+                        delta))
+                        return;
+        } else if (queue_work(respond_queue, &ftdi->respond_work))
+                return;
+        kref_put(&ftdi->kref, ftdi_elan_delete);
+        return;
+}
+
+static void ftdi_respond_queue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+        if (delta > 0) {
+                if (queue_delayed_work(respond_queue, &ftdi->respond_work,
+                        delta))
+                        kref_get(&ftdi->kref);
+        } else if (queue_work(respond_queue, &ftdi->respond_work))
+                kref_get(&ftdi->kref);
+        return;
+}
+
+static void ftdi_response_cancel_work(struct usb_ftdi *ftdi)
+{
+        if (cancel_delayed_work(&ftdi->respond_work))
+                kref_put(&ftdi->kref, ftdi_elan_delete);
+}
+
+void ftdi_elan_gone_away(struct platform_device *pdev)
+{
+        struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+        ftdi->gone_away += 1;
+        ftdi_elan_put_kref(ftdi);
+}
+
+
+EXPORT_SYMBOL_GPL(ftdi_elan_gone_away);
+void ftdi_release_platform_dev(struct device *dev)
+{
+        dev->parent = NULL;
+}
+
+static void ftdi_elan_do_callback(struct usb_ftdi *ftdi,
+        struct u132_target *target, u8 *buffer, int length);
+static void ftdi_elan_kick_command_queue(struct usb_ftdi *ftdi);
+static void ftdi_elan_kick_respond_queue(struct usb_ftdi *ftdi);
+static int ftdi_elan_setupOHCI(struct usb_ftdi *ftdi);
+static int ftdi_elan_checkingPCI(struct usb_ftdi *ftdi);
+static int ftdi_elan_enumeratePCI(struct usb_ftdi *ftdi);
+static int ftdi_elan_synchronize(struct usb_ftdi *ftdi);
+static int ftdi_elan_stuck_waiting(struct usb_ftdi *ftdi);
+static int ftdi_elan_command_engine(struct usb_ftdi *ftdi);
+static int ftdi_elan_respond_engine(struct usb_ftdi *ftdi);
+static int ftdi_elan_hcd_init(struct usb_ftdi *ftdi)
+{
+        int result;
+        if (ftdi->platform_dev.dev.parent)
+                return -EBUSY;
+        ftdi_elan_get_kref(ftdi);
+        ftdi->platform_data.potpg = 100;
+        ftdi->platform_data.reset = NULL;
+        ftdi->platform_dev.id = ftdi->sequence_num;
+        ftdi->platform_dev.resource = ftdi->resources;
+        ftdi->platform_dev.num_resources = ARRAY_SIZE(ftdi->resources);
+        ftdi->platform_dev.dev.platform_data = &ftdi->platform_data;
+        ftdi->platform_dev.dev.parent = NULL;
+        ftdi->platform_dev.dev.release = ftdi_release_platform_dev;
+        ftdi->platform_dev.dev.dma_mask = NULL;
+        snprintf(ftdi->device_name, sizeof(ftdi->device_name), "u132_hcd");
+        ftdi->platform_dev.name = ftdi->device_name;
+        dev_info(&ftdi->udev->dev, "requesting module '%s'\n", "u132_hcd");
+        request_module("u132_hcd");
+        dev_info(&ftdi->udev->dev, "registering '%s'\n",
+                ftdi->platform_dev.name);
+        result = platform_device_register(&ftdi->platform_dev);
+        return result;
+}
+
+static void ftdi_elan_abandon_completions(struct usb_ftdi *ftdi)
+{
+        down(&ftdi->u132_lock);
+        while (ftdi->respond_next > ftdi->respond_head) {
+                struct u132_respond *respond = &ftdi->respond[RESPOND_MASK &
+                        ftdi->respond_head++];
+                *respond->result = -ESHUTDOWN;
+                *respond->value = 0;
+                complete(&respond->wait_completion);
+        } up(&ftdi->u132_lock);
+}
+
+static void ftdi_elan_abandon_targets(struct usb_ftdi *ftdi)
+{
+        int ed_number = 4;
+        down(&ftdi->u132_lock);
+        while (ed_number-- > 0) {
+                struct u132_target *target = &ftdi->target[ed_number];
+                if (target->active == 1) {
+                        target->condition_code = TD_DEVNOTRESP;
+                        up(&ftdi->u132_lock);
+                        ftdi_elan_do_callback(ftdi, target, NULL, 0);
+                        down(&ftdi->u132_lock);
+                }
+        }
+        ftdi->recieved = 0;
+        ftdi->expected = 4;
+        ftdi->ed_found = 0;
+        up(&ftdi->u132_lock);
+}
+
+static void ftdi_elan_flush_targets(struct usb_ftdi *ftdi)
+{
+        int ed_number = 4;
+        down(&ftdi->u132_lock);
+        while (ed_number-- > 0) {
+                struct u132_target *target = &ftdi->target[ed_number];
+                target->abandoning = 1;
+              wait_1:if (target->active == 1) {
+                        int command_size = ftdi->command_next -
+                                ftdi->command_head;
+                        if (command_size < COMMAND_SIZE) {
+                                struct u132_command *command = &ftdi->command[
+                                        COMMAND_MASK & ftdi->command_next];
+                                command->header = 0x80 | (ed_number << 5) | 0x4;
+                                command->length = 0x00;
+                                command->address = 0x00;
+                                command->width = 0x00;
+                                command->follows = 0;
+                                command->value = 0;
+                                command->buffer = &command->value;
+                                ftdi->command_next += 1;
+                                ftdi_elan_kick_command_queue(ftdi);
+                        } else {
+                                up(&ftdi->u132_lock);
+                                msleep(100);
+                                down(&ftdi->u132_lock);
+                                goto wait_1;
+                        }
+                }
+              wait_2:if (target->active == 1) {
+                        int command_size = ftdi->command_next -
+                                ftdi->command_head;
+                        if (command_size < COMMAND_SIZE) {
+                                struct u132_command *command = &ftdi->command[
+                                        COMMAND_MASK & ftdi->command_next];
+                                command->header = 0x90 | (ed_number << 5);
+                                command->length = 0x00;
+                                command->address = 0x00;
+                                command->width = 0x00;
+                                command->follows = 0;
+                                command->value = 0;
+                                command->buffer = &command->value;
+                                ftdi->command_next += 1;
+                                ftdi_elan_kick_command_queue(ftdi);
+                        } else {
+                                up(&ftdi->u132_lock);
+                                msleep(100);
+                                down(&ftdi->u132_lock);
+                                goto wait_2;
+                        }
+                }
+        }
+        ftdi->recieved = 0;
+        ftdi->expected = 4;
+        ftdi->ed_found = 0;
+        up(&ftdi->u132_lock);
+}
+
+static void ftdi_elan_cancel_targets(struct usb_ftdi *ftdi)
+{
+        int ed_number = 4;
+        down(&ftdi->u132_lock);
+        while (ed_number-- > 0) {
+                struct u132_target *target = &ftdi->target[ed_number];
+                target->abandoning = 1;
+              wait:if (target->active == 1) {
+                        int command_size = ftdi->command_next -
+                                ftdi->command_head;
+                        if (command_size < COMMAND_SIZE) {
+                                struct u132_command *command = &ftdi->command[
+                                        COMMAND_MASK & ftdi->command_next];
+                                command->header = 0x80 | (ed_number << 5) | 0x4;
+                                command->length = 0x00;
+                                command->address = 0x00;
+                                command->width = 0x00;
+                                command->follows = 0;
+                                command->value = 0;
+                                command->buffer = &command->value;
+                                ftdi->command_next += 1;
+                                ftdi_elan_kick_command_queue(ftdi);
+                        } else {
+                                up(&ftdi->u132_lock);
+                                msleep(100);
+                                down(&ftdi->u132_lock);
+                                goto wait;
+                        }
+                }
+        }
+        ftdi->recieved = 0;
+        ftdi->expected = 4;
+        ftdi->ed_found = 0;
+        up(&ftdi->u132_lock);
+}
+
+static void ftdi_elan_kick_command_queue(struct usb_ftdi *ftdi)
+{
+        ftdi_command_queue_work(ftdi, 0);
+        return;
+}
+
+static void ftdi_elan_command_work(void *data)
+{
+        struct usb_ftdi *ftdi = data;
+        if (ftdi->disconnected > 0) {
+                ftdi_elan_put_kref(ftdi);
+                return;
+        } else {
+                int retval = ftdi_elan_command_engine(ftdi);
+                if (retval == -ESHUTDOWN) {
+                        ftdi->disconnected += 1;
+                } else if (retval == -ENODEV) {
+                        ftdi->disconnected += 1;
+                } else if (retval)
+                        dev_err(&ftdi->udev->dev, "command error %d\n", retval);
+                ftdi_command_requeue_work(ftdi, msecs_to_jiffies(10));
+                return;
+        }
+}
+
+static void ftdi_elan_kick_respond_queue(struct usb_ftdi *ftdi)
+{
+        ftdi_respond_queue_work(ftdi, 0);
+        return;
+}
+
+static void ftdi_elan_respond_work(void *data)
+{
+        struct usb_ftdi *ftdi = data;
+        if (ftdi->disconnected > 0) {
+                ftdi_elan_put_kref(ftdi);
+                return;
+        } else {
+                int retval = ftdi_elan_respond_engine(ftdi);
+                if (retval == 0) {
+                } else if (retval == -ESHUTDOWN) {
+                        ftdi->disconnected += 1;
+                } else if (retval == -ENODEV) {
+                        ftdi->disconnected += 1;
+                } else if (retval == -ENODEV) {
+                        ftdi->disconnected += 1;
+                } else if (retval == -EILSEQ) {
+                        ftdi->disconnected += 1;
+                } else {
+                        ftdi->disconnected += 1;
+                        dev_err(&ftdi->udev->dev, "respond error %d\n", retval);
+                }
+                if (ftdi->disconnected > 0) {
+                        ftdi_elan_abandon_completions(ftdi);
+                        ftdi_elan_abandon_targets(ftdi);
+                }
+                ftdi_response_requeue_work(ftdi, msecs_to_jiffies(10));
+                return;
+        }
+}
+
+
+/*
+* the sw_lock is initially held and will be freed
+* after the FTDI has been synchronized
+*
+*/
+static void ftdi_elan_status_work(void *data)
+{
+        struct usb_ftdi *ftdi = data;
+        int work_delay_in_msec = 0;
+        if (ftdi->disconnected > 0) {
+                ftdi_elan_put_kref(ftdi);
+                return;
+        } else if (ftdi->synchronized == 0) {
+                down(&ftdi->sw_lock);
+                if (ftdi_elan_synchronize(ftdi) == 0) {
+                        ftdi->synchronized = 1;
+                        ftdi_command_queue_work(ftdi, 1);
+                        ftdi_respond_queue_work(ftdi, 1);
+                        up(&ftdi->sw_lock);
+                        work_delay_in_msec = 100;
+                } else {
+                        dev_err(&ftdi->udev->dev, "synchronize failed\n");
+                        up(&ftdi->sw_lock);
+                        work_delay_in_msec = 10 *1000;
+                }
+        } else if (ftdi->stuck_status > 0) {
+                if (ftdi_elan_stuck_waiting(ftdi) == 0) {
+                        ftdi->stuck_status = 0;
+                        ftdi->synchronized = 0;
+                } else if ((ftdi->stuck_status++ % 60) == 1) {
+                        dev_err(&ftdi->udev->dev, "WRONG type of card inserted "
+                                "- please remove\n");
+                } else
+                        dev_err(&ftdi->udev->dev, "WRONG type of card inserted "
+                                "- checked %d times\n", ftdi->stuck_status);
+                work_delay_in_msec = 100;
+        } else if (ftdi->enumerated == 0) {
+                if (ftdi_elan_enumeratePCI(ftdi) == 0) {
+                        ftdi->enumerated = 1;
+                        work_delay_in_msec = 250;
+                } else
+                        work_delay_in_msec = 1000;
+        } else if (ftdi->initialized == 0) {
+                if (ftdi_elan_setupOHCI(ftdi) == 0) {
+                        ftdi->initialized = 1;
+                        work_delay_in_msec = 500;
+                } else {
+                        dev_err(&ftdi->udev->dev, "initialized failed - trying "
+                                "again in 10 seconds\n");
+                        work_delay_in_msec = 10 *1000;
+                }
+        } else if (ftdi->registered == 0) {
+                work_delay_in_msec = 10;
+                if (ftdi_elan_hcd_init(ftdi) == 0) {
+                        ftdi->registered = 1;
+                } else
+                        dev_err(&ftdi->udev->dev, "register failed\n");
+                work_delay_in_msec = 250;
+        } else {
+                if (ftdi_elan_checkingPCI(ftdi) == 0) {
+                        work_delay_in_msec = 250;
+                } else if (ftdi->controlreg & 0x00400000) {
+                        if (ftdi->gone_away > 0) {
+                                dev_err(&ftdi->udev->dev, "PCI device eject con"
+                                        "firmed platform_dev.dev.parent=%p plat"
+                                        "form_dev.dev=%p\n",
+                                        ftdi->platform_dev.dev.parent,
+                                        &ftdi->platform_dev.dev);
+                                platform_device_unregister(&ftdi->platform_dev);
+                                ftdi->platform_dev.dev.parent = NULL;
+                                ftdi->registered = 0;
+                                ftdi->enumerated = 0;
+                                ftdi->card_ejected = 0;
+                                ftdi->initialized = 0;
+                                ftdi->gone_away = 0;
+                        } else
+                                ftdi_elan_flush_targets(ftdi);
+                        work_delay_in_msec = 250;
+                } else {
+                        dev_err(&ftdi->udev->dev, "PCI device has disappeared\n"
+                                );
+                        ftdi_elan_cancel_targets(ftdi);
+                        work_delay_in_msec = 500;
+                        ftdi->enumerated = 0;
+                        ftdi->initialized = 0;
+                }
+        }
+        if (ftdi->disconnected > 0) {
+                ftdi_elan_put_kref(ftdi);
+                return;
+        } else {
+                ftdi_status_requeue_work(ftdi,
+                        msecs_to_jiffies(work_delay_in_msec));
+                return;
+        }
+}
+
+
+/*
+* file_operations for the jtag interface
+*
+* the usage count for the device is incremented on open()
+* and decremented on release()
+*/
+static int ftdi_elan_open(struct inode *inode, struct file *file)
+{
+        int subminor = iminor(inode);
+        struct usb_interface *interface = usb_find_interface(&ftdi_elan_driver,
+                subminor);
+        if (!interface) {
+                printk(KERN_ERR "can't find device for minor %d\n", subminor);
+                return -ENODEV;
+        } else {
+                struct usb_ftdi *ftdi = usb_get_intfdata(interface);
+                if (!ftdi) {
+                        return -ENODEV;
+                } else {
+                        if (down_interruptible(&ftdi->sw_lock)) {
+                                return -EINTR;
+                        } else {
+                                ftdi_elan_get_kref(ftdi);
+                                file->private_data = ftdi;
+                                return 0;
+                        }
+                }
+        }
+}
+
+static int ftdi_elan_release(struct inode *inode, struct file *file)
+{
+        struct usb_ftdi *ftdi = (struct usb_ftdi *)file->private_data;
+        if (ftdi == NULL)
+                return -ENODEV;
+        up(&ftdi->sw_lock);        /* decrement the count on our device */
+        ftdi_elan_put_kref(ftdi);
+        return 0;
+}
+
+
+#define FTDI_ELAN_IOC_MAGIC 0xA1
+#define FTDI_ELAN_IOCDEBUG _IOC(_IOC_WRITE, FTDI_ELAN_IOC_MAGIC, 1, 132)
+static int ftdi_elan_ioctl(struct inode *inode, struct file *file,
+        unsigned int cmd, unsigned long arg)
+{
+        switch (cmd) {
+        case FTDI_ELAN_IOCDEBUG:{
+                        char line[132];
+                        int size = strncpy_from_user(line,
+                                (const char __user *)arg, sizeof(line));
+                        if (size < 0) {
+                                return -EINVAL;
+                        } else {
+                                printk(KERN_ERR "TODO: ioctl %s\n", line);
+                                return 0;
+                        }
+                }
+        default:
+                return -EFAULT;
+        }
+}
+
+
+/*
+*
+* blocking bulk reads are used to get data from the device
+*
+*/
+static ssize_t ftdi_elan_read(struct file *file, char __user *buffer,
+                             size_t count, loff_t *ppos)
+{
+        char data[30 *3 + 4];
+        char *d = data;
+        int m = (sizeof(data) - 1) / 3;
+        int bytes_read = 0;
+        int retry_on_empty = 10;
+        int retry_on_timeout = 5;
+        struct usb_ftdi *ftdi = (struct usb_ftdi *)file->private_data;
+        if (ftdi->disconnected > 0) {
+                return -ENODEV;
+        }
+        data[0] = 0;
+      have:if (ftdi->bulk_in_left > 0) {
+                if (count-- > 0) {
+                        char *p = ++ftdi->bulk_in_last + ftdi->bulk_in_buffer;
+                        ftdi->bulk_in_left -= 1;
+                        if (bytes_read < m) {
+                                d += sprintf(d, " %02X", 0x000000FF & *p);
+                        } else if (bytes_read > m) {
+                        } else
+                                d += sprintf(d, " ..");
+                        if (copy_to_user(buffer++, p, 1)) {
+                                return -EFAULT;
+                        } else {
+                                bytes_read += 1;
+                                goto have;
+                        }
+                } else
+                        return bytes_read;
+        }
+      more:if (count > 0) {
+                int packet_bytes = 0;
+                int retval = usb_bulk_msg(ftdi->udev,
+                        usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),
+                         ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+                        &packet_bytes, msecs_to_jiffies(50));
+                if (packet_bytes > 2) {
+                        ftdi->bulk_in_left = packet_bytes - 2;
+                        ftdi->bulk_in_last = 1;
+                        goto have;
+                } else if (retval == -ETIMEDOUT) {
+                        if (retry_on_timeout-- > 0) {
+                                goto more;
+                        } else if (bytes_read > 0) {
+                                return bytes_read;
+                        } else
+                                return retval;
+                } else if (retval == 0) {
+                        if (retry_on_empty-- > 0) {
+                                goto more;
+                        } else
+                                return bytes_read;
+                } else
+                        return retval;
+        } else
+                return bytes_read;
+}
+
+static void ftdi_elan_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+        struct usb_ftdi *ftdi = (struct usb_ftdi *)urb->context;
+        if (urb->status && !(urb->status == -ENOENT || urb->status ==
+                -ECONNRESET || urb->status == -ESHUTDOWN)) {
+                dev_err(&ftdi->udev->dev, "urb=%p write bulk status received: %"
+                        "d\n", urb, urb->status);
+        }
+        usb_buffer_free(urb->dev, urb->transfer_buffer_length,
+                urb->transfer_buffer, urb->transfer_dma);
+}
+
+static int fill_buffer_with_all_queued_commands(struct usb_ftdi *ftdi,
+        char *buf, int command_size, int total_size)
+{
+        int ed_commands = 0;
+        int b = 0;
+        int I = command_size;
+        int i = ftdi->command_head;
+        while (I-- > 0) {
+                struct u132_command *command = &ftdi->command[COMMAND_MASK &
+                        i++];
+                int F = command->follows;
+                u8 *f = command->buffer;
+                if (command->header & 0x80) {
+                        ed_commands |= 1 << (0x3 & (command->header >> 5));
+                }
+                buf[b++] = command->header;
+                buf[b++] = (command->length >> 0) & 0x00FF;
+                buf[b++] = (command->length >> 8) & 0x00FF;
+                buf[b++] = command->address;
+                buf[b++] = command->width;
+                while (F-- > 0) {
+                        buf[b++] = *f++;
+                }
+        }
+        return ed_commands;
+}
+
+static int ftdi_elan_total_command_size(struct usb_ftdi *ftdi, int command_size)
+{
+        int total_size = 0;
+        int I = command_size;
+        int i = ftdi->command_head;
+        while (I-- > 0) {
+                struct u132_command *command = &ftdi->command[COMMAND_MASK &
+                        i++];
+                total_size += 5 + command->follows;
+        } return total_size;
+}
+
+static int ftdi_elan_command_engine(struct usb_ftdi *ftdi)
+{
+        int retval;
+        char *buf;
+        int ed_commands;
+        int total_size;
+        struct urb *urb;
+        int command_size = ftdi->command_next - ftdi->command_head;
+        if (command_size == 0)
+                return 0;
+        total_size = ftdi_elan_total_command_size(ftdi, command_size);
+        urb = usb_alloc_urb(0, GFP_KERNEL);
+        if (!urb) {
+                dev_err(&ftdi->udev->dev, "could not get a urb to write %d comm"
+                        "ands totaling %d bytes to the Uxxx\n", command_size,
+                        total_size);
+                return -ENOMEM;
+        }
+        buf = usb_buffer_alloc(ftdi->udev, total_size, GFP_KERNEL,
+                &urb->transfer_dma);
+        if (!buf) {
+                dev_err(&ftdi->udev->dev, "could not get a buffer to write %d c"
+                        "ommands totaling %d bytes to the Uxxx\n", command_size,
+                         total_size);
+                usb_free_urb(urb);
+                return -ENOMEM;
+        }
+        ed_commands = fill_buffer_with_all_queued_commands(ftdi, buf,
+                command_size, total_size);
+        usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev,
+                ftdi->bulk_out_endpointAddr), buf, total_size,
+                ftdi_elan_write_bulk_callback, ftdi);
+        urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+        if (ed_commands) {
+                char diag[40 *3 + 4];
+                char *d = diag;
+                int m = total_size;
+                u8 *c = buf;
+                int s = (sizeof(diag) - 1) / 3;
+                diag[0] = 0;
+                while (s-- > 0 && m-- > 0) {
+                        if (s > 0 || m == 0) {
+                                d += sprintf(d, " %02X", *c++);
+                        } else
+                                d += sprintf(d, " ..");
+                }
+        }
+        retval = usb_submit_urb(urb, GFP_KERNEL);
+        if (retval) {
+                dev_err(&ftdi->udev->dev, "failed %d to submit urb %p to write "
+                        "%d commands totaling %d bytes to the Uxxx\n", retval,
+                        urb, command_size, total_size);
+                usb_buffer_free(ftdi->udev, total_size, buf, urb->transfer_dma);
+                usb_free_urb(urb);
+                return retval;
+        }
+        usb_free_urb(urb);        /* release our reference to this urb,
+                the USB core will eventually free it entirely */
+        ftdi->command_head += command_size;
+        ftdi_elan_kick_respond_queue(ftdi);
+        return 0;
+}
+
+static void ftdi_elan_do_callback(struct usb_ftdi *ftdi,
+        struct u132_target *target, u8 *buffer, int length)
+{
+        struct urb *urb = target->urb;
+        int halted = target->halted;
+        int skipped = target->skipped;
+        int actual = target->actual;
+        int non_null = target->non_null;
+        int toggle_bits = target->toggle_bits;
+        int error_count = target->error_count;
+        int condition_code = target->condition_code;
+        int repeat_number = target->repeat_number;
+        void (*callback) (void *, struct urb *, u8 *, int, int, int, int, int,
+                int, int, int, int) = target->callback;
+        target->active -= 1;
+        target->callback = NULL;
+        (*callback) (target->endp, urb, buffer, length, toggle_bits,
+                error_count, condition_code, repeat_number, halted, skipped,
+                actual, non_null);
+}
+
+static char *have_ed_set_response(struct usb_ftdi *ftdi,
+        struct u132_target *target, u16 ed_length, int ed_number, int ed_type,
+        char *b)
+{
+        int payload = (ed_length >> 0) & 0x07FF;
+        down(&ftdi->u132_lock);
+        target->actual = 0;
+        target->non_null = (ed_length >> 15) & 0x0001;
+        target->repeat_number = (ed_length >> 11) & 0x000F;
+        if (ed_type == 0x02) {
+                if (payload == 0 || target->abandoning > 0) {
+                        target->abandoning = 0;
+                        up(&ftdi->u132_lock);
+                        ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+                                payload);
+                        ftdi->recieved = 0;
+                        ftdi->expected = 4;
+                        ftdi->ed_found = 0;
+                        return ftdi->response;
+                } else {
+                        ftdi->expected = 4 + payload;
+                        ftdi->ed_found = 1;
+                        up(&ftdi->u132_lock);
+                        return b;
+                }
+        } else if (ed_type == 0x03) {
+                if (payload == 0 || target->abandoning > 0) {
+                        target->abandoning = 0;
+                        up(&ftdi->u132_lock);
+                        ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+                                payload);
+                        ftdi->recieved = 0;
+                        ftdi->expected = 4;
+                        ftdi->ed_found = 0;
+                        return ftdi->response;
+                } else {
+                        ftdi->expected = 4 + payload;
+                        ftdi->ed_found = 1;
+                        up(&ftdi->u132_lock);
+                        return b;
+                }
+        } else if (ed_type == 0x01) {
+                target->abandoning = 0;
+                up(&ftdi->u132_lock);
+                ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+                        payload);
+                ftdi->recieved = 0;
+                ftdi->expected = 4;
+                ftdi->ed_found = 0;
+                return ftdi->response;
+        } else {
+                target->abandoning = 0;
+                up(&ftdi->u132_lock);
+                ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+                        payload);
+                ftdi->recieved = 0;
+                ftdi->expected = 4;
+                ftdi->ed_found = 0;
+                return ftdi->response;
+        }
+}
+
+static char *have_ed_get_response(struct usb_ftdi *ftdi,
+        struct u132_target *target, u16 ed_length, int ed_number, int ed_type,
+        char *b)
+{
+        down(&ftdi->u132_lock);
+        target->condition_code = TD_DEVNOTRESP;
+        target->actual = (ed_length >> 0) & 0x01FF;
+        target->non_null = (ed_length >> 15) & 0x0001;
+        target->repeat_number = (ed_length >> 11) & 0x000F;
+        up(&ftdi->u132_lock);
+        if (target->active)
+                ftdi_elan_do_callback(ftdi, target, NULL, 0);
+        target->abandoning = 0;
+        ftdi->recieved = 0;
+        ftdi->expected = 4;
+        ftdi->ed_found = 0;
+        return ftdi->response;
+}
+
+
+/*
+* The engine tries to empty the FTDI fifo
+*
+* all responses found in the fifo data are dispatched thus
+* the response buffer can only ever hold a maximum sized
+* response from the Uxxx.
+*
+*/
+static int ftdi_elan_respond_engine(struct usb_ftdi *ftdi)
+{
+        u8 *b = ftdi->response + ftdi->recieved;
+        int bytes_read = 0;
+        int retry_on_empty = 1;
+        int retry_on_timeout = 3;
+        int empty_packets = 0;
+      read:{
+                int packet_bytes = 0;
+                int retval = usb_bulk_msg(ftdi->udev,
+                        usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),
+                         ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+                        &packet_bytes, msecs_to_jiffies(500));
+                char diag[30 *3 + 4];
+                char *d = diag;
+                int m = packet_bytes;
+                u8 *c = ftdi->bulk_in_buffer;
+                int s = (sizeof(diag) - 1) / 3;
+                diag[0] = 0;
+                while (s-- > 0 && m-- > 0) {
+                        if (s > 0 || m == 0) {
+                                d += sprintf(d, " %02X", *c++);
+                        } else
+                                d += sprintf(d, " ..");
+                }
+                if (packet_bytes > 2) {
+                        ftdi->bulk_in_left = packet_bytes - 2;
+                        ftdi->bulk_in_last = 1;
+                        goto have;
+                } else if (retval == -ETIMEDOUT) {
+                        if (retry_on_timeout-- > 0) {
+                                dev_err(&ftdi->udev->dev, "TIMED OUT with packe"
+                                        "t_bytes = %d with total %d bytes%s\n",
+                                        packet_bytes, bytes_read, diag);
+                                goto more;
+                        } else if (bytes_read > 0) {
+                                dev_err(&ftdi->udev->dev, "ONLY %d bytes%s\n",
+                                        bytes_read, diag);
+                                return -ENOMEM;
+                        } else {
+                                dev_err(&ftdi->udev->dev, "TIMED OUT with packe"
+                                        "t_bytes = %d with total %d bytes%s\n",
+                                        packet_bytes, bytes_read, diag);
+                                return -ENOMEM;
+                        }
+                } else if (retval == -EILSEQ) {
+                        dev_err(&ftdi->udev->dev, "error = %d with packet_bytes"
+                                " = %d with total %d bytes%s\n", retval,
+                                packet_bytes, bytes_read, diag);
+                        return retval;
+                } else if (retval) {
+                        dev_err(&ftdi->udev->dev, "error = %d with packet_bytes"
+                                " = %d with total %d bytes%s\n", retval,
+                                packet_bytes, bytes_read, diag);
+                        return retval;
+                } else if (packet_bytes == 2) {
+                        unsigned char s0 = ftdi->bulk_in_buffer[0];
+                        unsigned char s1 = ftdi->bulk_in_buffer[1];
+                        empty_packets += 1;
+                        if (s0 == 0x31 && s1 == 0x60) {
+                                if (retry_on_empty-- > 0) {
+                                        goto more;
+                                } else
+                                        return 0;
+                        } else if (s0 == 0x31 && s1 == 0x00) {
+                                if (retry_on_empty-- > 0) {
+                                        goto more;
+                                } else
+                                        return 0;
+                        } else {
+                                if (retry_on_empty-- > 0) {
+                                        goto more;
+                                } else
+                                        return 0;
+                        }
+                } else if (packet_bytes == 1) {
+                        if (retry_on_empty-- > 0) {
+                                goto more;
+                        } else
+                                return 0;
+                } else {
+                        if (retry_on_empty-- > 0) {
+                                goto more;
+                        } else
+                                return 0;
+                }
+        }
+      more:{
+                goto read;
+        }
+      have:if (ftdi->bulk_in_left > 0) {
+                u8 c = ftdi->bulk_in_buffer[++ftdi->bulk_in_last];
+                bytes_read += 1;
+                ftdi->bulk_in_left -= 1;
+                if (ftdi->recieved == 0 && c == 0xFF) {
+                        goto have;
+                } else
+                        *b++ = c;
+                if (++ftdi->recieved < ftdi->expected) {
+                        goto have;
+                } else if (ftdi->ed_found) {
+                        int ed_number = (ftdi->response[0] >> 5) & 0x03;
+                        u16 ed_length = (ftdi->response[2] << 8) |
+                                ftdi->response[1];
+                        struct u132_target *target = &ftdi->target[ed_number];
+                        int payload = (ed_length >> 0) & 0x07FF;
+                        char diag[30 *3 + 4];
+                        char *d = diag;
+                        int m = payload;
+                        u8 *c = 4 + ftdi->response;
+                        int s = (sizeof(diag) - 1) / 3;
+                        diag[0] = 0;
+                        while (s-- > 0 && m-- > 0) {
+                                if (s > 0 || m == 0) {
+                                        d += sprintf(d, " %02X", *c++);
+                                } else
+                                        d += sprintf(d, " ..");
+                        }
+                        ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+                                payload);
+                        ftdi->recieved = 0;
+                        ftdi->expected = 4;
+                        ftdi->ed_found = 0;
+                        b = ftdi->response;
+                        goto have;
+                } else if (ftdi->expected == 8) {
+                        u8 buscmd;
+                        int respond_head = ftdi->respond_head++;
+                        struct u132_respond *respond = &ftdi->respond[
+                                RESPOND_MASK & respond_head];
+                        u32 data = ftdi->response[7];
+                        data <<= 8;
+                        data |= ftdi->response[6];
+                        data <<= 8;
+                        data |= ftdi->response[5];
+                        data <<= 8;
+                        data |= ftdi->response[4];
+                        *respond->value = data;
+                        *respond->result = 0;
+                        complete(&respond->wait_completion);
+                        ftdi->recieved = 0;
+                        ftdi->expected = 4;
+                        ftdi->ed_found = 0;
+                        b = ftdi->response;
+                        buscmd = (ftdi->response[0] >> 0) & 0x0F;
+                        if (buscmd == 0x00) {
+                        } else if (buscmd == 0x02) {
+                        } else if (buscmd == 0x06) {
+                        } else if (buscmd == 0x0A) {
+                        } else
+                                dev_err(&ftdi->udev->dev, "Uxxx unknown(%0X) va"
+                                        "lue = %08X\n", buscmd, data);
+                        goto have;
+                } else {
+                        if ((ftdi->response[0] & 0x80) == 0x00) {
+                                ftdi->expected = 8;
+                                goto have;
+                        } else {
+                                int ed_number = (ftdi->response[0] >> 5) & 0x03;
+                                int ed_type = (ftdi->response[0] >> 0) & 0x03;
+                                u16 ed_length = (ftdi->response[2] << 8) |
+                                        ftdi->response[1];
+                                struct u132_target *target = &ftdi->target[
+                                        ed_number];
+                                target->halted = (ftdi->response[0] >> 3) &
+                                        0x01;
+                                target->skipped = (ftdi->response[0] >> 2) &
+                                        0x01;
+                                target->toggle_bits = (ftdi->response[3] >> 6)
+                                        & 0x03;
+                                target->error_count = (ftdi->response[3] >> 4)
+                                        & 0x03;
+                                target->condition_code = (ftdi->response[
+                                        3] >> 0) & 0x0F;
+                                if ((ftdi->response[0] & 0x10) == 0x00) {
+                                        b = have_ed_set_response(ftdi, target,
+                                                ed_length, ed_number, ed_type,
+                                                b);
+                                        goto have;
+                                } else {
+                                        b = have_ed_get_response(ftdi, target,
+                                                ed_length, ed_number, ed_type,
+                                                b);
+                                        goto have;
+                                }
+                        }
+                }
+        } else
+                goto more;
+}
+
+
+/*
+* create a urb, and a buffer for it, and copy the data to the urb
+*
+*/
+static ssize_t ftdi_elan_write(struct file *file,
+                              const char __user *user_buffer, size_t count,
+                              loff_t *ppos)
+{
+        int retval = 0;
+        struct urb *urb;
+        char *buf;
+        char data[30 *3 + 4];
+        char *d = data;
+        const char __user *s = user_buffer;
+        int m = (sizeof(data) - 1) / 3;
+        struct usb_ftdi *ftdi = (struct usb_ftdi *)file->private_data;
+        if (ftdi->disconnected > 0) {
+                return -ENODEV;
+        }
+        if (count == 0) {
+                goto exit;
+        }
+        urb = usb_alloc_urb(0, GFP_KERNEL);
+        if (!urb) {
+                retval = -ENOMEM;
+                goto error_1;
+        }
+        buf = usb_buffer_alloc(ftdi->udev, count, GFP_KERNEL,
+                &urb->transfer_dma);
+        if (!buf) {
+                retval = -ENOMEM;
+                goto error_2;
+        }
+        if (copy_from_user(buf, user_buffer, count)) {
+                retval = -EFAULT;
+                goto error_3;
+        }
+        usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev,
+                ftdi->bulk_out_endpointAddr), buf, count,
+                ftdi_elan_write_bulk_callback, ftdi);
+        urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+        retval = usb_submit_urb(urb, GFP_KERNEL);
+        if (retval) {
+                dev_err(&ftdi->udev->dev, "failed submitting write urb, error %"
+                        "d\n", retval);
+                goto error_4;
+        }
+        usb_free_urb(urb);
+      exit:;
+        if (count > m) {
+                int I = m - 1;
+                while (I-- > 0) {
+                        d += sprintf(d, " %02X", 0x000000FF & *s++);
+                }
+                d += sprintf(d, " ..");
+        } else {
+                int I = count;
+                while (I-- > 0) {
+                        d += sprintf(d, " %02X", 0x000000FF & *s++);
+                }
+        }
+        return count;
+      error_4: error_3:usb_buffer_free(ftdi->udev, count, buf,
+              urb->transfer_dma);
+      error_2:usb_free_urb(urb);
+      error_1:return retval;
+}
+
+static struct file_operations ftdi_elan_fops = {
+        .owner = THIS_MODULE,
+        .llseek = no_llseek,
+        .ioctl = ftdi_elan_ioctl,
+        .read = ftdi_elan_read,
+        .write = ftdi_elan_write,
+        .open = ftdi_elan_open,
+        .release = ftdi_elan_release,
+};
+
+/*
+* usb class driver info in order to get a minor number from the usb core,
+* and to have the device registered with the driver core
+*/
+static struct usb_class_driver ftdi_elan_jtag_class = {
+        .name = "ftdi-%d-jtag",
+        .fops = &ftdi_elan_fops,
+        .minor_base = USB_FTDI_ELAN_MINOR_BASE,
+};
+
+/*
+* the following definitions are for the
+* ELAN FPGA state machgine processor that
+* lies on the other side of the FTDI chip
+*/
+#define cPCIu132rd 0x0
+#define cPCIu132wr 0x1
+#define cPCIiord 0x2
+#define cPCIiowr 0x3
+#define cPCImemrd 0x6
+#define cPCImemwr 0x7
+#define cPCIcfgrd 0xA
+#define cPCIcfgwr 0xB
+#define cPCInull 0xF
+#define cU132cmd_status 0x0
+#define cU132flash 0x1
+#define cPIDsetup 0x0
+#define cPIDout 0x1
+#define cPIDin 0x2
+#define cPIDinonce 0x3
+#define cCCnoerror 0x0
+#define cCCcrc 0x1
+#define cCCbitstuff 0x2
+#define cCCtoggle 0x3
+#define cCCstall 0x4
+#define cCCnoresp 0x5
+#define cCCbadpid1 0x6
+#define cCCbadpid2 0x7
+#define cCCdataoverrun 0x8
+#define cCCdataunderrun 0x9
+#define cCCbuffoverrun 0xC
+#define cCCbuffunderrun 0xD
+#define cCCnotaccessed 0xF
+static int ftdi_elan_write_reg(struct usb_ftdi *ftdi, u32 data)
+{
+      wait:if (ftdi->disconnected > 0) {
+                return -ENODEV;
+        } else {
+                int command_size;
+                down(&ftdi->u132_lock);
+                command_size = ftdi->command_next - ftdi->command_head;
+                if (command_size < COMMAND_SIZE) {
+                        struct u132_command *command = &ftdi->command[
+                                COMMAND_MASK & ftdi->command_next];
+                        command->header = 0x00 | cPCIu132wr;
+                        command->length = 0x04;
+                        command->address = 0x00;
+                        command->width = 0x00;
+                        command->follows = 4;
+                        command->value = data;
+                        command->buffer = &command->value;
+                        ftdi->command_next += 1;
+                        ftdi_elan_kick_command_queue(ftdi);
+                        up(&ftdi->u132_lock);
+                        return 0;
+                } else {
+                        up(&ftdi->u132_lock);
+                        msleep(100);
+                        goto wait;
+                }
+        }
+}
+
+static int ftdi_elan_write_config(struct usb_ftdi *ftdi, int config_offset,
+        u8 width, u32 data)
+{
+        u8 addressofs = config_offset / 4;
+      wait:if (ftdi->disconnected > 0) {
+                return -ENODEV;
+        } else {
+                int command_size;
+                down(&ftdi->u132_lock);
+                command_size = ftdi->command_next - ftdi->command_head;
+                if (command_size < COMMAND_SIZE) {
+                        struct u132_command *command = &ftdi->command[
+                                COMMAND_MASK & ftdi->command_next];
+                        command->header = 0x00 | (cPCIcfgwr & 0x0F);
+                        command->length = 0x04;
+                        command->address = addressofs;
+                        command->width = 0x00 | (width & 0x0F);
+                        command->follows = 4;
+                        command->value = data;
+                        command->buffer = &command->value;
+                        ftdi->command_next += 1;
+                        ftdi_elan_kick_command_queue(ftdi);
+                        up(&ftdi->u132_lock);
+                        return 0;
+                } else {
+                        up(&ftdi->u132_lock);
+                        msleep(100);
+                        goto wait;
+                }
+        }
+}
+
+static int ftdi_elan_write_pcimem(struct usb_ftdi *ftdi, int mem_offset,
+        u8 width, u32 data)
+{
+        u8 addressofs = mem_offset / 4;
+      wait:if (ftdi->disconnected > 0) {
+                return -ENODEV;
+        } else {
+                int command_size;
+                down(&ftdi->u132_lock);
+                command_size = ftdi->command_next - ftdi->command_head;
+                if (command_size < COMMAND_SIZE) {
+                        struct u132_command *command = &ftdi->command[
+                                COMMAND_MASK & ftdi->command_next];
+                        command->header = 0x00 | (cPCImemwr & 0x0F);
+                        command->length = 0x04;
+                        command->address = addressofs;
+                        command->width = 0x00 | (width & 0x0F);
+                        command->follows = 4;
+                        command->value = data;
+                        command->buffer = &command->value;
+                        ftdi->command_next += 1;
+                        ftdi_elan_kick_command_queue(ftdi);
+                        up(&ftdi->u132_lock);
+                        return 0;
+                } else {
+                        up(&ftdi->u132_lock);
+                        msleep(100);
+                        goto wait;
+                }
+        }
+}
+
+int usb_ftdi_elan_write_pcimem(struct platform_device *pdev, int mem_offset,
+        u8 width, u32 data)
+{
+        struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+        return ftdi_elan_write_pcimem(ftdi, mem_offset, width, data);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_write_pcimem);
+static int ftdi_elan_read_reg(struct usb_ftdi *ftdi, u32 *data)
+{
+      wait:if (ftdi->disconnected > 0) {
+                return -ENODEV;
+        } else {
+                int command_size;
+                int respond_size;
+                down(&ftdi->u132_lock);
+                command_size = ftdi->command_next - ftdi->command_head;
+                respond_size = ftdi->respond_next - ftdi->respond_head;
+                if (command_size < COMMAND_SIZE && respond_size < RESPOND_SIZE)
+                        {
+                        struct u132_command *command = &ftdi->command[
+                                COMMAND_MASK & ftdi->command_next];
+                        struct u132_respond *respond = &ftdi->respond[
+                                RESPOND_MASK & ftdi->respond_next];
+                        int result = -ENODEV;
+                        respond->result = &result;
+                        respond->header = command->header = 0x00 | cPCIu132rd;
+                        command->length = 0x04;
+                        respond->address = command->address = cU132cmd_status;
+                        command->width = 0x00;
+                        command->follows = 0;
+                        command->value = 0;
+                        command->buffer = NULL;
+                        respond->value = data;
+                        init_completion(&respond->wait_completion);
+                        ftdi->command_next += 1;
+                        ftdi->respond_next += 1;
+                        ftdi_elan_kick_command_queue(ftdi);
+                        up(&ftdi->u132_lock);
+                        wait_for_completion(&respond->wait_completion);
+                        return result;
+                } else {
+                        up(&ftdi->u132_lock);
+                        msleep(100);
+                        goto wait;
+                }
+        }
+}
+
+int usb_ftdi_elan_read_reg(struct platform_device *pdev, u32 *data)
+{
+        struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+        return ftdi_elan_read_reg(ftdi, data);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_read_reg);
+static int ftdi_elan_read_config(struct usb_ftdi *ftdi, int config_offset,
+        u8 width, u32 *data)
+{
+        u8 addressofs = config_offset / 4;
+      wait:if (ftdi->disconnected > 0) {
+                return -ENODEV;
+        } else {
+                int command_size;
+                int respond_size;
+                down(&ftdi->u132_lock);
+                command_size = ftdi->command_next - ftdi->command_head;
+                respond_size = ftdi->respond_next - ftdi->respond_head;
+                if (command_size < COMMAND_SIZE && respond_size < RESPOND_SIZE)
+                        {
+                        struct u132_command *command = &ftdi->command[
+                                COMMAND_MASK & ftdi->command_next];
+                        struct u132_respond *respond = &ftdi->respond[
+                                RESPOND_MASK & ftdi->respond_next];
+                        int result = -ENODEV;
+                        respond->result = &result;
+                        respond->header = command->header = 0x00 | (cPCIcfgrd &
+                                0x0F);
+                        command->length = 0x04;
+                        respond->address = command->address = addressofs;
+                        command->width = 0x00 | (width & 0x0F);
+                        command->follows = 0;
+                        command->value = 0;
+                        command->buffer = NULL;
+                        respond->value = data;
+                        init_completion(&respond->wait_completion);
+                        ftdi->command_next += 1;
+                        ftdi->respond_next += 1;
+                        ftdi_elan_kick_command_queue(ftdi);
+                        up(&ftdi->u132_lock);
+                        wait_for_completion(&respond->wait_completion);
+                        return result;
+                } else {
+                        up(&ftdi->u132_lock);
+                        msleep(100);
+                        goto wait;
+                }
+        }
+}
+
+static int ftdi_elan_read_pcimem(struct usb_ftdi *ftdi, int mem_offset,
+        u8 width, u32 *data)
+{
+        u8 addressofs = mem_offset / 4;
+      wait:if (ftdi->disconnected > 0) {
+                return -ENODEV;
+        } else {
+                int command_size;
+                int respond_size;
+                down(&ftdi->u132_lock);
+                command_size = ftdi->command_next - ftdi->command_head;
+                respond_size = ftdi->respond_next - ftdi->respond_head;
+                if (command_size < COMMAND_SIZE && respond_size < RESPOND_SIZE)
+                        {
+                        struct u132_command *command = &ftdi->command[
+                                COMMAND_MASK & ftdi->command_next];
+                        struct u132_respond *respond = &ftdi->respond[
+                                RESPOND_MASK & ftdi->respond_next];
+                        int result = -ENODEV;
+                        respond->result = &result;
+                        respond->header = command->header = 0x00 | (cPCImemrd &
+                                0x0F);
+                        command->length = 0x04;
+                        respond->address = command->address = addressofs;
+                        command->width = 0x00 | (width & 0x0F);
+                        command->follows = 0;
+                        command->value = 0;
+                        command->buffer = NULL;
+                        respond->value = data;
+                        init_completion(&respond->wait_completion);
+                        ftdi->command_next += 1;
+                        ftdi->respond_next += 1;
+                        ftdi_elan_kick_command_queue(ftdi);
+                        up(&ftdi->u132_lock);
+                        wait_for_completion(&respond->wait_completion);
+                        return result;
+                } else {
+                        up(&ftdi->u132_lock);
+                        msleep(100);
+                        goto wait;
+                }
+        }
+}
+
+int usb_ftdi_elan_read_pcimem(struct platform_device *pdev, int mem_offset,
+        u8 width, u32 *data)
+{
+        struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+        if (ftdi->initialized == 0) {
+                return -ENODEV;
+        } else
+                return ftdi_elan_read_pcimem(ftdi, mem_offset, width, data);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_read_pcimem);
+static int ftdi_elan_edset_setup(struct usb_ftdi *ftdi, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null))
+{
+        u8 ed = ed_number - 1;
+      wait:if (ftdi->disconnected > 0) {
+                return -ENODEV;
+        } else if (ftdi->initialized == 0) {
+                return -ENODEV;
+        } else {
+                int command_size;
+                down(&ftdi->u132_lock);
+                command_size = ftdi->command_next - ftdi->command_head;
+                if (command_size < COMMAND_SIZE) {
+                        struct u132_target *target = &ftdi->target[ed];
+                        struct u132_command *command = &ftdi->command[
+                                COMMAND_MASK & ftdi->command_next];
+                        command->header = 0x80 | (ed << 5);
+                        command->length = 0x8007;
+                        command->address = (toggle_bits << 6) | (ep_number << 2)
+                                | (address << 0);
+                        command->width = usb_maxpacket(urb->dev, urb->pipe,
+                                usb_pipeout(urb->pipe));
+                        command->follows = 8;
+                        command->value = 0;
+                        command->buffer = urb->setup_packet;
+                        target->callback = callback;
+                        target->endp = endp;
+                        target->urb = urb;
+                        target->active = 1;
+                        ftdi->command_next += 1;
+                        ftdi_elan_kick_command_queue(ftdi);
+                        up(&ftdi->u132_lock);
+                        return 0;
+                } else {
+                        up(&ftdi->u132_lock);
+                        msleep(100);
+                        goto wait;
+                }
+        }
+}
+
+int usb_ftdi_elan_edset_setup(struct platform_device *pdev, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null))
+{
+        struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+        return ftdi_elan_edset_setup(ftdi, ed_number, endp, urb, address,
+                ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_setup);
+static int ftdi_elan_edset_input(struct usb_ftdi *ftdi, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null))
+{
+        u8 ed = ed_number - 1;
+      wait:if (ftdi->disconnected > 0) {
+                return -ENODEV;
+        } else if (ftdi->initialized == 0) {
+                return -ENODEV;
+        } else {
+                int command_size;
+                down(&ftdi->u132_lock);
+                command_size = ftdi->command_next - ftdi->command_head;
+                if (command_size < COMMAND_SIZE) {
+                        struct u132_target *target = &ftdi->target[ed];
+                        struct u132_command *command = &ftdi->command[
+                                COMMAND_MASK & ftdi->command_next];
+                        int remaining_length = urb->transfer_buffer_length -
+                                urb->actual_length;
+                        command->header = 0x82 | (ed << 5);
+                        if (remaining_length == 0) {
+                                command->length = 0x0000;
+                        } else if (remaining_length > 1024) {
+                                command->length = 0x8000 | 1023;
+                        } else
+                                command->length = 0x8000 | (remaining_length -
+                                        1);
+                        command->address = (toggle_bits << 6) | (ep_number << 2)
+                                | (address << 0);
+                        command->width = usb_maxpacket(urb->dev, urb->pipe,
+                                usb_pipeout(urb->pipe));
+                        command->follows = 0;
+                        command->value = 0;
+                        command->buffer = NULL;
+                        target->callback = callback;
+                        target->endp = endp;
+                        target->urb = urb;
+                        target->active = 1;
+                        ftdi->command_next += 1;
+                        ftdi_elan_kick_command_queue(ftdi);
+                        up(&ftdi->u132_lock);
+                        return 0;
+                } else {
+                        up(&ftdi->u132_lock);
+                        msleep(100);
+                        goto wait;
+                }
+        }
+}
+
+int usb_ftdi_elan_edset_input(struct platform_device *pdev, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null))
+{
+        struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+        return ftdi_elan_edset_input(ftdi, ed_number, endp, urb, address,
+                ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_input);
+static int ftdi_elan_edset_empty(struct usb_ftdi *ftdi, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null))
+{
+        u8 ed = ed_number - 1;
+      wait:if (ftdi->disconnected > 0) {
+                return -ENODEV;
+        } else if (ftdi->initialized == 0) {
+                return -ENODEV;
+        } else {
+                int command_size;
+                down(&ftdi->u132_lock);
+                command_size = ftdi->command_next - ftdi->command_head;
+                if (command_size < COMMAND_SIZE) {
+                        struct u132_target *target = &ftdi->target[ed];
+                        struct u132_command *command = &ftdi->command[
+                                COMMAND_MASK & ftdi->command_next];
+                        command->header = 0x81 | (ed << 5);
+                        command->length = 0x0000;
+                        command->address = (toggle_bits << 6) | (ep_number << 2)
+                                | (address << 0);
+                        command->width = usb_maxpacket(urb->dev, urb->pipe,
+                                usb_pipeout(urb->pipe));
+                        command->follows = 0;
+                        command->value = 0;
+                        command->buffer = NULL;
+                        target->callback = callback;
+                        target->endp = endp;
+                        target->urb = urb;
+                        target->active = 1;
+                        ftdi->command_next += 1;
+                        ftdi_elan_kick_command_queue(ftdi);
+                        up(&ftdi->u132_lock);
+                        return 0;
+                } else {
+                        up(&ftdi->u132_lock);
+                        msleep(100);
+                        goto wait;
+                }
+        }
+}
+
+int usb_ftdi_elan_edset_empty(struct platform_device *pdev, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null))
+{
+        struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+        return ftdi_elan_edset_empty(ftdi, ed_number, endp, urb, address,
+                ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_empty);
+static int ftdi_elan_edset_output(struct usb_ftdi *ftdi, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null))
+{
+        u8 ed = ed_number - 1;
+      wait:if (ftdi->disconnected > 0) {
+                return -ENODEV;
+        } else if (ftdi->initialized == 0) {
+                return -ENODEV;
+        } else {
+                int command_size;
+                down(&ftdi->u132_lock);
+                command_size = ftdi->command_next - ftdi->command_head;
+                if (command_size < COMMAND_SIZE) {
+                        u8 *b;
+                        u16 urb_size;
+                        int i = 0;
+                        char data[30 *3 + 4];
+                        char *d = data;
+                        int m = (sizeof(data) - 1) / 3;
+                        int l = 0;
+                        struct u132_target *target = &ftdi->target[ed];
+                        struct u132_command *command = &ftdi->command[
+                                COMMAND_MASK & ftdi->command_next];
+                        command->header = 0x81 | (ed << 5);
+                        command->address = (toggle_bits << 6) | (ep_number << 2)
+                                | (address << 0);
+                        command->width = usb_maxpacket(urb->dev, urb->pipe,
+                                usb_pipeout(urb->pipe));
+                        command->follows = min(1024,
+                                urb->transfer_buffer_length -
+                                urb->actual_length);
+                        command->value = 0;
+                        command->buffer = urb->transfer_buffer +
+                                urb->actual_length;
+                        command->length = 0x8000 | (command->follows - 1);
+                        b = command->buffer;
+                        urb_size = command->follows;
+                        data[0] = 0;
+                        while (urb_size-- > 0) {
+                                if (i > m) {
+                                } else if (i++ < m) {
+                                        int w = sprintf(d, " %02X", *b++);
+                                        d += w;
+                                        l += w;
+                                } else
+                                        d += sprintf(d, " ..");
+                        }
+                        target->callback = callback;
+                        target->endp = endp;
+                        target->urb = urb;
+                        target->active = 1;
+                        ftdi->command_next += 1;
+                        ftdi_elan_kick_command_queue(ftdi);
+                        up(&ftdi->u132_lock);
+                        return 0;
+                } else {
+                        up(&ftdi->u132_lock);
+                        msleep(100);
+                        goto wait;
+                }
+        }
+}
+
+int usb_ftdi_elan_edset_output(struct platform_device *pdev, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null))
+{
+        struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+        return ftdi_elan_edset_output(ftdi, ed_number, endp, urb, address,
+                ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_output);
+static int ftdi_elan_edset_single(struct usb_ftdi *ftdi, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null))
+{
+        u8 ed = ed_number - 1;
+      wait:if (ftdi->disconnected > 0) {
+                return -ENODEV;
+        } else if (ftdi->initialized == 0) {
+                return -ENODEV;
+        } else {
+                int command_size;
+                down(&ftdi->u132_lock);
+                command_size = ftdi->command_next - ftdi->command_head;
+                if (command_size < COMMAND_SIZE) {
+                        int remaining_length = urb->transfer_buffer_length -
+                                urb->actual_length;
+                        struct u132_target *target = &ftdi->target[ed];
+                        struct u132_command *command = &ftdi->command[
+                                COMMAND_MASK & ftdi->command_next];
+                        command->header = 0x83 | (ed << 5);
+                        if (remaining_length == 0) {
+                                command->length = 0x0000;
+                        } else if (remaining_length > 1024) {
+                                command->length = 0x8000 | 1023;
+                        } else
+                                command->length = 0x8000 | (remaining_length -
+                                        1);
+                        command->address = (toggle_bits << 6) | (ep_number << 2)
+                                | (address << 0);
+                        command->width = usb_maxpacket(urb->dev, urb->pipe,
+                                usb_pipeout(urb->pipe));
+                        command->follows = 0;
+                        command->value = 0;
+                        command->buffer = NULL;
+                        target->callback = callback;
+                        target->endp = endp;
+                        target->urb = urb;
+                        target->active = 1;
+                        ftdi->command_next += 1;
+                        ftdi_elan_kick_command_queue(ftdi);
+                        up(&ftdi->u132_lock);
+                        return 0;
+                } else {
+                        up(&ftdi->u132_lock);
+                        msleep(100);
+                        goto wait;
+                }
+        }
+}
+
+int usb_ftdi_elan_edset_single(struct platform_device *pdev, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null))
+{
+        struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+        return ftdi_elan_edset_single(ftdi, ed_number, endp, urb, address,
+                ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_single);
+static int ftdi_elan_edset_flush(struct usb_ftdi *ftdi, u8 ed_number,
+        void *endp)
+{
+        u8 ed = ed_number - 1;
+        if (ftdi->disconnected > 0) {
+                return -ENODEV;
+        } else if (ftdi->initialized == 0) {
+                return -ENODEV;
+        } else {
+                struct u132_target *target = &ftdi->target[ed];
+                down(&ftdi->u132_lock);
+                if (target->abandoning > 0) {
+                        up(&ftdi->u132_lock);
+                        return 0;
+                } else {
+                        target->abandoning = 1;
+                      wait_1:if (target->active == 1) {
+                                int command_size = ftdi->command_next -
+                                        ftdi->command_head;
+                                if (command_size < COMMAND_SIZE) {
+                                        struct u132_command *command =
+                                                &ftdi->command[COMMAND_MASK &
+                                                ftdi->command_next];
+                                        command->header = 0x80 | (ed << 5) |
+                                                0x4;
+                                        command->length = 0x00;
+                                        command->address = 0x00;
+                                        command->width = 0x00;
+                                        command->follows = 0;
+                                        command->value = 0;
+                                        command->buffer = &command->value;
+                                        ftdi->command_next += 1;
+                                        ftdi_elan_kick_command_queue(ftdi);
+                                } else {
+                                        up(&ftdi->u132_lock);
+                                        msleep(100);
+                                        down(&ftdi->u132_lock);
+                                        goto wait_1;
+                                }
+                        }
+                        up(&ftdi->u132_lock);
+                        return 0;
+                }
+        }
+}
+
+int usb_ftdi_elan_edset_flush(struct platform_device *pdev, u8 ed_number,
+        void *endp)
+{
+        struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+        return ftdi_elan_edset_flush(ftdi, ed_number, endp);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_flush);
+static int ftdi_elan_flush_input_fifo(struct usb_ftdi *ftdi)
+{
+        int retry_on_empty = 10;
+        int retry_on_timeout = 5;
+        int retry_on_status = 20;
+      more:{
+                int packet_bytes = 0;
+                int retval = usb_bulk_msg(ftdi->udev,
+                        usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),
+                         ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+                        &packet_bytes, msecs_to_jiffies(100));
+                if (packet_bytes > 2) {
+                        char diag[30 *3 + 4];
+                        char *d = diag;
+                        int m = (sizeof(diag) - 1) / 3;
+                        char *b = ftdi->bulk_in_buffer;
+                        int bytes_read = 0;
+                        diag[0] = 0;
+                        while (packet_bytes-- > 0) {
+                                char c = *b++;
+                                if (bytes_read < m) {
+                                        d += sprintf(d, " %02X",
+                                                0x000000FF & c);
+                                } else if (bytes_read > m) {
+                                } else
+                                        d += sprintf(d, " ..");
+                                bytes_read += 1;
+                                continue;
+                        }
+                        goto more;
+                } else if (packet_bytes > 1) {
+                        char s1 = ftdi->bulk_in_buffer[0];
+                        char s2 = ftdi->bulk_in_buffer[1];
+                        if (s1 == 0x31 && s2 == 0x60) {
+                                return 0;
+                        } else if (retry_on_status-- > 0) {
+                                goto more;
+                        } else {
+                                dev_err(&ftdi->udev->dev, "STATUS ERROR retry l"
+                                        "imit reached\n");
+                                return -EFAULT;
+                        }
+                } else if (packet_bytes > 0) {
+                        char b1 = ftdi->bulk_in_buffer[0];
+                        dev_err(&ftdi->udev->dev, "only one byte flushed from F"
+                                "TDI = %02X\n", b1);
+                        if (retry_on_status-- > 0) {
+                                goto more;
+                        } else {
+                                dev_err(&ftdi->udev->dev, "STATUS ERROR retry l"
+                                        "imit reached\n");
+                                return -EFAULT;
+                        }
+                } else if (retval == -ETIMEDOUT) {
+                        if (retry_on_timeout-- > 0) {
+                                goto more;
+                        } else {
+                                dev_err(&ftdi->udev->dev, "TIMED OUT retry limi"
+                                        "t reached\n");
+                                return -ENOMEM;
+                        }
+                } else if (retval == 0) {
+                        if (retry_on_empty-- > 0) {
+                                goto more;
+                        } else {
+                                dev_err(&ftdi->udev->dev, "empty packet retry l"
+                                        "imit reached\n");
+                                return -ENOMEM;
+                        }
+                } else {
+                        dev_err(&ftdi->udev->dev, "error = %d\n", retval);
+                        return retval;
+                }
+        }
+        return -1;
+}
+
+
+/*
+* send the long flush sequence
+*
+*/
+static int ftdi_elan_synchronize_flush(struct usb_ftdi *ftdi)
+{
+        int retval;
+        struct urb *urb;
+        char *buf;
+        int I = 257;
+        int i = 0;
+        urb = usb_alloc_urb(0, GFP_KERNEL);
+        if (!urb) {
+                dev_err(&ftdi->udev->dev, "could not alloc a urb for flush sequ"
+                        "ence\n");
+                return -ENOMEM;
+        }
+        buf = usb_buffer_alloc(ftdi->udev, I, GFP_KERNEL, &urb->transfer_dma);
+        if (!buf) {
+                dev_err(&ftdi->udev->dev, "could not get a buffer for flush seq"
+                        "uence\n");
+                usb_free_urb(urb);
+                return -ENOMEM;
+        }
+        while (I-- > 0)
+                buf[i++] = 0x55;
+        usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev,
+                ftdi->bulk_out_endpointAddr), buf, i,
+                ftdi_elan_write_bulk_callback, ftdi);
+        urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+        retval = usb_submit_urb(urb, GFP_KERNEL);
+        if (retval) {
+                dev_err(&ftdi->udev->dev, "failed to submit urb containing the "
+                        "flush sequence\n");
+                usb_buffer_free(ftdi->udev, i, buf, urb->transfer_dma);
+                usb_free_urb(urb);
+                return -ENOMEM;
+        }
+        usb_free_urb(urb);
+        return 0;
+}
+
+
+/*
+* send the reset sequence
+*
+*/
+static int ftdi_elan_synchronize_reset(struct usb_ftdi *ftdi)
+{
+        int retval;
+        struct urb *urb;
+        char *buf;
+        int I = 4;
+        int i = 0;
+        urb = usb_alloc_urb(0, GFP_KERNEL);
+        if (!urb) {
+                dev_err(&ftdi->udev->dev, "could not get a urb for the reset se"
+                        "quence\n");
+                return -ENOMEM;
+        }
+        buf = usb_buffer_alloc(ftdi->udev, I, GFP_KERNEL, &urb->transfer_dma);
+        if (!buf) {
+                dev_err(&ftdi->udev->dev, "could not get a buffer for the reset"
+                        " sequence\n");
+                usb_free_urb(urb);
+                return -ENOMEM;
+        }
+        buf[i++] = 0x55;
+        buf[i++] = 0xAA;
+        buf[i++] = 0x5A;
+        buf[i++] = 0xA5;
+        usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev,
+                ftdi->bulk_out_endpointAddr), buf, i,
+                ftdi_elan_write_bulk_callback, ftdi);
+        urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+        retval = usb_submit_urb(urb, GFP_KERNEL);
+        if (retval) {
+                dev_err(&ftdi->udev->dev, "failed to submit urb containing the "
+                        "reset sequence\n");
+                usb_buffer_free(ftdi->udev, i, buf, urb->transfer_dma);
+                usb_free_urb(urb);
+                return -ENOMEM;
+        }
+        usb_free_urb(urb);
+        return 0;
+}
+
+static int ftdi_elan_synchronize(struct usb_ftdi *ftdi)
+{
+        int retval;
+        int long_stop = 10;
+        int retry_on_timeout = 5;
+        int retry_on_empty = 10;
+        int err_count = 0;
+        retval = ftdi_elan_flush_input_fifo(ftdi);
+        if (retval)
+                return retval;
+        ftdi->bulk_in_left = 0;
+        ftdi->bulk_in_last = -1;
+        while (long_stop-- > 0) {
+                int read_stop;
+                int read_stuck;
+                retval = ftdi_elan_synchronize_flush(ftdi);
+                if (retval)
+                        return retval;
+                retval = ftdi_elan_flush_input_fifo(ftdi);
+                if (retval)
+                        return retval;
+              reset:retval = ftdi_elan_synchronize_reset(ftdi);
+                if (retval)
+                        return retval;
+                read_stop = 100;
+                read_stuck = 10;
+              read:{
+                        int packet_bytes = 0;
+                        retval = usb_bulk_msg(ftdi->udev,
+                                usb_rcvbulkpipe(ftdi->udev,
+                                ftdi->bulk_in_endpointAddr),
+                                ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+                                &packet_bytes, msecs_to_jiffies(500));
+                        if (packet_bytes > 2) {
+                                char diag[30 *3 + 4];
+                                char *d = diag;
+                                int m = (sizeof(diag) - 1) / 3;
+                                char *b = ftdi->bulk_in_buffer;
+                                int bytes_read = 0;
+                                unsigned char c = 0;
+                                diag[0] = 0;
+                                while (packet_bytes-- > 0) {
+                                        c = *b++;
+                                        if (bytes_read < m) {
+                                                d += sprintf(d, " %02X", c);
+                                        } else if (bytes_read > m) {
+                                        } else
+                                                d += sprintf(d, " ..");
+                                        bytes_read += 1;
+                                        continue;
+                                }
+                                if (c == 0x7E) {
+                                        return 0;
+                                } else {
+                                        if (c == 0x55) {
+                                                goto read;
+                                        } else if (read_stop-- > 0) {
+                                                goto read;
+                                        } else {
+                                                dev_err(&ftdi->udev->dev, "retr"
+                                                        "y limit reached\n");
+                                                continue;
+                                        }
+                                }
+                        } else if (packet_bytes > 1) {
+                                unsigned char s1 = ftdi->bulk_in_buffer[0];
+                                unsigned char s2 = ftdi->bulk_in_buffer[1];
+                                if (s1 == 0x31 && s2 == 0x00) {
+                                        if (read_stuck-- > 0) {
+                                                goto read;
+                                        } else
+                                                goto reset;
+                                } else if (s1 == 0x31 && s2 == 0x60) {
+                                        if (read_stop-- > 0) {
+                                                goto read;
+                                        } else {
+                                                dev_err(&ftdi->udev->dev, "retr"
+                                                        "y limit reached\n");
+                                                continue;
+                                        }
+                                } else {
+                                        if (read_stop-- > 0) {
+                                                goto read;
+                                        } else {
+                                                dev_err(&ftdi->udev->dev, "retr"
+                                                        "y limit reached\n");
+                                                continue;
+                                        }
+                                }
+                        } else if (packet_bytes > 0) {
+                                if (read_stop-- > 0) {
+                                        goto read;
+                                } else {
+                                        dev_err(&ftdi->udev->dev, "retry limit "
+                                                "reached\n");
+                                        continue;
+                                }
+                        } else if (retval == -ETIMEDOUT) {
+                                if (retry_on_timeout-- > 0) {
+                                        goto read;
+                                } else {
+                                        dev_err(&ftdi->udev->dev, "TIMED OUT re"
+                                                "try limit reached\n");
+                                        continue;
+                                }
+                        } else if (retval == 0) {
+                                if (retry_on_empty-- > 0) {
+                                        goto read;
+                                } else {
+                                        dev_err(&ftdi->udev->dev, "empty packet"
+                                                " retry limit reached\n");
+                                        continue;
+                                }
+                        } else {
+                                err_count += 1;
+                                dev_err(&ftdi->udev->dev, "error = %d\n",
+                                        retval);
+                                if (read_stop-- > 0) {
+                                        goto read;
+                                } else {
+                                        dev_err(&ftdi->udev->dev, "retry limit "
+                                                "reached\n");
+                                        continue;
+                                }
+                        }
+                }
+        }
+        dev_err(&ftdi->udev->dev, "failed to synchronize\n");
+        return -EFAULT;
+}
+
+static int ftdi_elan_stuck_waiting(struct usb_ftdi *ftdi)
+{
+        int retry_on_empty = 10;
+        int retry_on_timeout = 5;
+        int retry_on_status = 50;
+      more:{
+                int packet_bytes = 0;
+                int retval = usb_bulk_msg(ftdi->udev,
+                        usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),
+                         ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+                        &packet_bytes, msecs_to_jiffies(1000));
+                if (packet_bytes > 2) {
+                        char diag[30 *3 + 4];
+                        char *d = diag;
+                        int m = (sizeof(diag) - 1) / 3;
+                        char *b = ftdi->bulk_in_buffer;
+                        int bytes_read = 0;
+                        diag[0] = 0;
+                        while (packet_bytes-- > 0) {
+                                char c = *b++;
+                                if (bytes_read < m) {
+                                        d += sprintf(d, " %02X",
+                                                0x000000FF & c);
+                                } else if (bytes_read > m) {
+                                } else
+                                        d += sprintf(d, " ..");
+                                bytes_read += 1;
+                                continue;
+                        }
+                        goto more;
+                } else if (packet_bytes > 1) {
+                        char s1 = ftdi->bulk_in_buffer[0];
+                        char s2 = ftdi->bulk_in_buffer[1];
+                        if (s1 == 0x31 && s2 == 0x60) {
+                                return 0;
+                        } else if (retry_on_status-- > 0) {
+                                msleep(5);
+                                goto more;
+                        } else
+                                return -EFAULT;
+                } else if (packet_bytes > 0) {
+                        char b1 = ftdi->bulk_in_buffer[0];
+                        dev_err(&ftdi->udev->dev, "only one byte flushed from F"
+                                "TDI = %02X\n", b1);
+                        if (retry_on_status-- > 0) {
+                                msleep(5);
+                                goto more;
+                        } else {
+                                dev_err(&ftdi->udev->dev, "STATUS ERROR retry l"
+                                        "imit reached\n");
+                                return -EFAULT;
+                        }
+                } else if (retval == -ETIMEDOUT) {
+                        if (retry_on_timeout-- > 0) {
+                                goto more;
+                        } else {
+                                dev_err(&ftdi->udev->dev, "TIMED OUT retry limi"
+                                        "t reached\n");
+                                return -ENOMEM;
+                        }
+                } else if (retval == 0) {
+                        if (retry_on_empty-- > 0) {
+                                goto more;
+                        } else {
+                                dev_err(&ftdi->udev->dev, "empty packet retry l"
+                                        "imit reached\n");
+                                return -ENOMEM;
+                        }
+                } else {
+                        dev_err(&ftdi->udev->dev, "error = %d\n", retval);
+                        return -ENOMEM;
+                }
+        }
+        return -1;
+}
+
+static int ftdi_elan_checkingPCI(struct usb_ftdi *ftdi)
+{
+        int UxxxStatus = ftdi_elan_read_reg(ftdi, &ftdi->controlreg);
+        if (UxxxStatus)
+                return UxxxStatus;
+        if (ftdi->controlreg & 0x00400000) {
+                if (ftdi->card_ejected) {
+                } else {
+                        ftdi->card_ejected = 1;
+                        dev_err(&ftdi->udev->dev, "CARD EJECTED - controlreg = "
+                                "%08X\n", ftdi->controlreg);
+                }
+                return -ENODEV;
+        } else {
+                u8 fn = ftdi->function - 1;
+                int activePCIfn = fn << 8;
+                u32 pcidata;
+                u32 pciVID;
+                u32 pciPID;
+                int reg = 0;
+                UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+                        &pcidata);
+                if (UxxxStatus)
+                        return UxxxStatus;
+                pciVID = pcidata & 0xFFFF;
+                pciPID = (pcidata >> 16) & 0xFFFF;
+                if (pciVID == ftdi->platform_data.vendor && pciPID ==
+                        ftdi->platform_data.device) {
+                        return 0;
+                } else {
+                        dev_err(&ftdi->udev->dev, "vendor=%04X pciVID=%04X devi"
+                                "ce=%04X pciPID=%04X\n",
+                                ftdi->platform_data.vendor, pciVID,
+                                ftdi->platform_data.device, pciPID);
+                        return -ENODEV;
+                }
+        }
+}
+
+static int ftdi_elan_enumeratePCI(struct usb_ftdi *ftdi)
+{
+        u32 latence_timer;
+        u32 controlreg;
+        int UxxxStatus;
+        u32 pcidata;
+        int reg = 0;
+        int foundOHCI = 0;
+        u8 fn;
+        int activePCIfn = 0;
+        u32 pciVID = 0;
+        u32 pciPID = 0;
+        UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+        if (UxxxStatus)
+                return UxxxStatus;
+        UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000000L);
+        if (UxxxStatus)
+                return UxxxStatus;
+        msleep(750);
+        UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000200L | 0x100);
+        if (UxxxStatus)
+                return UxxxStatus;
+        UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000200L | 0x500);
+        if (UxxxStatus)
+                return UxxxStatus;
+        UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+        if (UxxxStatus)
+                return UxxxStatus;
+        UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020CL | 0x000);
+        if (UxxxStatus)
+                return UxxxStatus;
+        UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020DL | 0x000);
+        if (UxxxStatus)
+                return UxxxStatus;
+        msleep(250);
+        UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020FL | 0x000);
+        if (UxxxStatus)
+                return UxxxStatus;
+        UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+        if (UxxxStatus)
+                return UxxxStatus;
+        UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000025FL | 0x800);
+        if (UxxxStatus)
+                return UxxxStatus;
+        UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+        if (UxxxStatus)
+                return UxxxStatus;
+        UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+        if (UxxxStatus)
+                return UxxxStatus;
+        msleep(1000);
+        for (fn = 0; (fn < 4) && (!foundOHCI); fn++) {
+                activePCIfn = fn << 8;
+                ftdi->function = fn + 1;
+                UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+                        &pcidata);
+                if (UxxxStatus)
+                        return UxxxStatus;
+                pciVID = pcidata & 0xFFFF;
+                pciPID = (pcidata >> 16) & 0xFFFF;
+                if ((pciVID == 0x1045) && (pciPID == 0xc861)) {
+                        foundOHCI = 1;
+                } else if ((pciVID == 0x1033) && (pciPID == 0x0035)) {
+                        foundOHCI = 1;
+                } else if ((pciVID == 0x10b9) && (pciPID == 0x5237)) {
+                        foundOHCI = 1;
+                } else if ((pciVID == 0x11c1) && (pciPID == 0x5802)) {
+                        foundOHCI = 1;
+                } else if ((pciVID == 0x11AB) && (pciPID == 0x1FA6)) {
+                }
+        }
+        if (foundOHCI == 0) {
+                return -ENXIO;
+        }
+        ftdi->platform_data.vendor = pciVID;
+        ftdi->platform_data.device = pciPID;
+        UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000025FL | 0x2800);
+        if (UxxxStatus)
+                return UxxxStatus;
+        reg = 16;
+        UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0,
+                0xFFFFFFFF);
+        if (UxxxStatus)
+                return UxxxStatus;
+        UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+                &pcidata);
+        if (UxxxStatus)
+                return UxxxStatus;
+        UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0,
+                0xF0000000);
+        if (UxxxStatus)
+                return UxxxStatus;
+        UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+                &pcidata);
+        if (UxxxStatus)
+                return UxxxStatus;
+        reg = 12;
+        UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+                &latence_timer);
+        if (UxxxStatus)
+                return UxxxStatus;
+        latence_timer &= 0xFFFF00FF;
+        latence_timer |= 0x00001600;
+        UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0x00,
+                latence_timer);
+        if (UxxxStatus)
+                return UxxxStatus;
+        UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+                &pcidata);
+        if (UxxxStatus)
+                return UxxxStatus;
+        reg = 4;
+        UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0x00,
+                0x06);
+        if (UxxxStatus)
+                return UxxxStatus;
+        UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+                &pcidata);
+        if (UxxxStatus)
+                return UxxxStatus;
+        return 0;
+}
+
+static int ftdi_elan_setupOHCI(struct usb_ftdi *ftdi)
+{
+        u32 pcidata;
+        int U132Status;
+        int reg;
+        int reset_repeat = 0;
+      do_reset:reg = 8;
+        U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x0e, 0x01);
+        if (U132Status)
+                return U132Status;
+      reset_check:{
+                U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+                if (U132Status)
+                        return U132Status;
+                if (pcidata & 1) {
+                        msleep(500);
+                        if (reset_repeat++ > 100) {
+                                reset_repeat = 0;
+                                goto do_reset;
+                        } else
+                                goto reset_check;
+                }
+        }
+        goto dump_regs;
+        msleep(500);
+        reg = 0x28;
+        U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x11000000);
+        if (U132Status)
+                return U132Status;
+        U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+        if (U132Status)
+                return U132Status;
+        reg = 0x40;
+        U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x2edf);
+        if (U132Status)
+                return U132Status;
+        U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+        if (U132Status)
+                return U132Status;
+        reg = 0x34;
+        U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x2edf2edf);
+        if (U132Status)
+                return U132Status;
+        U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+        if (U132Status)
+                return U132Status;
+        reg = 4;
+        U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0xA0);
+        if (U132Status)
+                return U132Status;
+        U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+        if (U132Status)
+                return U132Status;
+        msleep(250);
+        reg = 8;
+        U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x0e, 0x04);
+        if (U132Status)
+                return U132Status;
+        U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+        if (U132Status)
+                return U132Status;
+        reg = 0x28;
+        U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+        if (U132Status)
+                return U132Status;
+        reg = 8;
+        U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+        if (U132Status)
+                return U132Status;
+        reg = 0x48;
+        U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x00001200);
+        if (U132Status)
+                return U132Status;
+        U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+        if (U132Status)
+                return U132Status;
+        reg = 0x54;
+        U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+        if (U132Status)
+                return U132Status;
+        reg = 0x58;
+        U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+        if (U132Status)
+                return U132Status;
+        reg = 0x34;
+        U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x28002edf);
+        if (U132Status)
+                return U132Status;
+        U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+        if (U132Status)
+                return U132Status;
+        msleep(100);
+        reg = 0x50;
+        U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x10000);
+        if (U132Status)
+                return U132Status;
+        reg = 0x54;
+      power_check:U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+        if (U132Status)
+                return U132Status;
+        if (!(pcidata & 1)) {
+                msleep(500);
+                goto power_check;
+        }
+        msleep(3000);
+        reg = 0x54;
+        U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+        if (U132Status)
+                return U132Status;
+        reg = 0x58;
+        U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+        if (U132Status)
+                return U132Status;
+        reg = 0x54;
+        U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x02);
+        if (U132Status)
+                return U132Status;
+        U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+        if (U132Status)
+                return U132Status;
+        reg = 0x54;
+        U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x10);
+        if (U132Status)
+                return U132Status;
+        U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+        if (U132Status)
+                return U132Status;
+        msleep(750);
+        reg = 0x54;
+        if (0) {
+                U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x02);
+                if (U132Status)
+                        return U132Status;
+        }
+        if (0) {
+                U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+                if (U132Status)
+                        return U132Status;
+        }
+        reg = 0x54;
+        U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+        if (U132Status)
+                return U132Status;
+        reg = 0x58;
+        U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+        if (U132Status)
+                return U132Status;
+      dump_regs:for (reg = 0; reg <= 0x54; reg += 4) {
+                U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+                if (U132Status)
+                        return U132Status;
+        }
+        return 0;
+}
+
+
+/*
+* we use only the first bulk-in and bulk-out endpoints
+*/
+static int ftdi_elan_probe(struct usb_interface *interface,
+        const struct usb_device_id *id)
+{
+        struct usb_host_interface *iface_desc;
+        struct usb_endpoint_descriptor *endpoint;
+        size_t buffer_size;
+        int i;
+        int retval = -ENOMEM;
+        struct usb_ftdi *ftdi = kmalloc(sizeof(struct usb_ftdi), GFP_KERNEL);
+        if (ftdi == NULL) {
+                printk(KERN_ERR "Out of memory\n");
+                return -ENOMEM;
+        }
+        memset(ftdi, 0x00, sizeof(struct usb_ftdi));
+        down(&ftdi_module_lock);
+        list_add_tail(&ftdi->ftdi_list, &ftdi_static_list);
+        ftdi->sequence_num = ++ftdi_instances;
+        up(&ftdi_module_lock);
+        ftdi_elan_init_kref(ftdi);
+        init_MUTEX(&ftdi->sw_lock);
+        ftdi->udev = usb_get_dev(interface_to_usbdev(interface));
+        ftdi->interface = interface;
+        init_MUTEX(&ftdi->u132_lock);
+        ftdi->expected = 4;
+        iface_desc = interface->cur_altsetting;
+        for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+                endpoint = &iface_desc->endpoint[i].desc;
+                if (!ftdi->bulk_in_endpointAddr &&
+                        ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+                        == USB_DIR_IN) && ((endpoint->bmAttributes &
+                        USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK))
+                        {
+                        buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+                        ftdi->bulk_in_size = buffer_size;
+                        ftdi->bulk_in_endpointAddr = endpoint->bEndpointAddress;
+                        ftdi->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
+                        if (!ftdi->bulk_in_buffer) {
+                                dev_err(&ftdi->udev->dev, "Could not allocate b"
+                                        "ulk_in_buffer\n");
+                                retval = -ENOMEM;
+                                goto error;
+                        }
+                }
+                if (!ftdi->bulk_out_endpointAddr &&
+                        ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+                        == USB_DIR_OUT) && ((endpoint->bmAttributes &
+                        USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK))
+                        {
+                        ftdi->bulk_out_endpointAddr =
+                                endpoint->bEndpointAddress;
+                }
+        }
+        if (!(ftdi->bulk_in_endpointAddr && ftdi->bulk_out_endpointAddr)) {
+                dev_err(&ftdi->udev->dev, "Could not find both bulk-in and bulk"
+                        "-out endpoints\n");
+                retval = -ENODEV;
+                goto error;
+        }
+        dev_info(&ftdi->udev->dev, "interface %d has I=%02X O=%02X\n",
+                iface_desc->desc.bInterfaceNumber, ftdi->bulk_in_endpointAddr,
+                ftdi->bulk_out_endpointAddr);
+        usb_set_intfdata(interface, ftdi);
+        if (iface_desc->desc.bInterfaceNumber == 0 &&
+                ftdi->bulk_in_endpointAddr == 0x81 &&
+                ftdi->bulk_out_endpointAddr == 0x02) {
+                retval = usb_register_dev(interface, &ftdi_elan_jtag_class);
+                if (retval) {
+                        dev_err(&ftdi->udev->dev, "Not able to get a minor for "
+                                "this device.\n");
+                        usb_set_intfdata(interface, NULL);
+                        retval = -ENOMEM;
+                        goto error;
+                } else {
+                        ftdi->class = &ftdi_elan_jtag_class;
+                        dev_info(&ftdi->udev->dev, "USB FDTI=%p JTAG interface "
+                                "%d now attached to ftdi%d\n", ftdi,
+                                iface_desc->desc.bInterfaceNumber,
+                                interface->minor);
+                        return 0;
+                }
+        } else if (iface_desc->desc.bInterfaceNumber == 1 &&
+                ftdi->bulk_in_endpointAddr == 0x83 &&
+                ftdi->bulk_out_endpointAddr == 0x04) {
+                ftdi->class = NULL;
+                dev_info(&ftdi->udev->dev, "USB FDTI=%p ELAN interface %d now a"
+                        "ctivated\n", ftdi, iface_desc->desc.bInterfaceNumber);
+                INIT_WORK(&ftdi->status_work, ftdi_elan_status_work,
+                        (void *)ftdi);
+                INIT_WORK(&ftdi->command_work, ftdi_elan_command_work,
+                        (void *)ftdi);
+                INIT_WORK(&ftdi->respond_work, ftdi_elan_respond_work,
+                        (void *)ftdi);
+                ftdi_status_queue_work(ftdi, msecs_to_jiffies(3 *1000));
+                return 0;
+        } else {
+                dev_err(&ftdi->udev->dev,
+                        "Could not find ELAN's U132 device\n");
+                retval = -ENODEV;
+                goto error;
+        }
+      error:if (ftdi) {
+                ftdi_elan_put_kref(ftdi);
+        }
+        return retval;
+}
+
+static void ftdi_elan_disconnect(struct usb_interface *interface)
+{
+        struct usb_ftdi *ftdi = usb_get_intfdata(interface);
+        ftdi->disconnected += 1;
+        if (ftdi->class) {
+                int minor = interface->minor;
+                struct usb_class_driver *class = ftdi->class;
+                usb_set_intfdata(interface, NULL);
+                usb_deregister_dev(interface, class);
+                dev_info(&ftdi->udev->dev, "USB FTDI U132 jtag interface on min"
+                        "or %d now disconnected\n", minor);
+        } else {
+                ftdi_status_cancel_work(ftdi);
+                ftdi_command_cancel_work(ftdi);
+                ftdi_response_cancel_work(ftdi);
+                ftdi_elan_abandon_completions(ftdi);
+                ftdi_elan_abandon_targets(ftdi);
+                if (ftdi->registered) {
+                        platform_device_unregister(&ftdi->platform_dev);
+                        ftdi->synchronized = 0;
+                        ftdi->enumerated = 0;
+                        ftdi->registered = 0;
+                }
+                flush_workqueue(status_queue);
+                flush_workqueue(command_queue);
+                flush_workqueue(respond_queue);
+                ftdi->disconnected += 1;
+                usb_set_intfdata(interface, NULL);
+                dev_info(&ftdi->udev->dev, "USB FTDI U132 host controller inter"
+                        "face now disconnected\n");
+        }
+        ftdi_elan_put_kref(ftdi);
+}
+
+static struct usb_driver ftdi_elan_driver = {
+        .name = "ftdi-elan",
+        .probe = ftdi_elan_probe,
+        .disconnect = ftdi_elan_disconnect,
+        .id_table = ftdi_elan_table,
+};
+static int __init ftdi_elan_init(void)
+{
+        int result;
+        printk(KERN_INFO "driver %s built at %s on %s\n", ftdi_elan_driver.name,
+                 __TIME__, __DATE__);
+        init_MUTEX(&ftdi_module_lock);
+        INIT_LIST_HEAD(&ftdi_static_list);
+        status_queue = create_singlethread_workqueue("ftdi-status-control");
+        command_queue = create_singlethread_workqueue("ftdi-command-engine");
+        respond_queue = create_singlethread_workqueue("ftdi-respond-engine");
+        result = usb_register(&ftdi_elan_driver);
+        if (result)
+                printk(KERN_ERR "usb_register failed. Error number %d\n",
+                        result);
+        return result;
+}
+
+static void __exit ftdi_elan_exit(void)
+{
+        struct usb_ftdi *ftdi;
+        struct usb_ftdi *temp;
+        usb_deregister(&ftdi_elan_driver);
+        printk(KERN_INFO "ftdi_u132 driver deregistered\n");
+        list_for_each_entry_safe(ftdi, temp, &ftdi_static_list, ftdi_list) {
+                ftdi_status_cancel_work(ftdi);
+                ftdi_command_cancel_work(ftdi);
+                ftdi_response_cancel_work(ftdi);
+        } flush_workqueue(status_queue);
+        destroy_workqueue(status_queue);
+        status_queue = NULL;
+        flush_workqueue(command_queue);
+        destroy_workqueue(command_queue);
+        command_queue = NULL;
+        flush_workqueue(respond_queue);
+        destroy_workqueue(respond_queue);
+        respond_queue = NULL;
+}
+
+
+module_init(ftdi_elan_init);
+module_exit(ftdi_elan_exit);
index fcd69c5..8e6e195 100644 (file)
@@ -98,7 +98,7 @@ static int idmouse_probe(struct usb_interface *interface,
 static void idmouse_disconnect(struct usb_interface *interface);
 
 /* file operation pointers */
-static struct file_operations idmouse_fops = {
+static const struct file_operations idmouse_fops = {
        .owner = THIS_MODULE,
        .read = idmouse_read,
        .open = idmouse_open,
index f30ab1f..10b6403 100644 (file)
@@ -589,7 +589,7 @@ exit:
 }
 
 /* file operations needed when we register this driver */
-static struct file_operations ld_usb_fops = {
+static const struct file_operations ld_usb_fops = {
        .owner =        THIS_MODULE,
        .read  =        ld_usb_read,
        .write =        ld_usb_write,
@@ -657,15 +657,11 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id *
        for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
                endpoint = &iface_desc->endpoint[i].desc;
 
-               if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) &&
-                   ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
+               if (usb_endpoint_is_int_in(endpoint))
                        dev->interrupt_in_endpoint = endpoint;
-               }
 
-               if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) &&
-                   ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
+               if (usb_endpoint_is_int_out(endpoint))
                        dev->interrupt_out_endpoint = endpoint;
-               }
        }
        if (dev->interrupt_in_endpoint == NULL) {
                dev_err(&intf->dev, "Interrupt in endpoint not found\n");
index 7699d97..77c36e6 100644 (file)
@@ -259,7 +259,7 @@ static void tower_disconnect        (struct usb_interface *interface);
 static DEFINE_MUTEX (disconnect_mutex);
 
 /* file operations needed when we register this driver */
-static struct file_operations tower_fops = {
+static const struct file_operations tower_fops = {
        .owner =        THIS_MODULE,
        .read  =        tower_read,
        .write =        tower_write,
diff --git a/drivers/usb/misc/phidget.c b/drivers/usb/misc/phidget.c
new file mode 100644 (file)
index 0000000..735ed33
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * USB Phidgets class
+ *
+ * Copyright (C) 2006  Sean Young <sean@mess.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/device.h>
+
+struct class *phidget_class;
+
+static int __init init_phidget(void)
+{
+       phidget_class = class_create(THIS_MODULE, "phidget");
+
+       if (IS_ERR(phidget_class))
+               return PTR_ERR(phidget_class);
+
+       return 0;
+}
+
+static void __exit cleanup_phidget(void)
+{
+       class_destroy(phidget_class);
+}
+
+EXPORT_SYMBOL_GPL(phidget_class);
+
+module_init(init_phidget);
+module_exit(cleanup_phidget);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_DESCRIPTION("Container module for phidget class");
+
diff --git a/drivers/usb/misc/phidget.h b/drivers/usb/misc/phidget.h
new file mode 100644 (file)
index 0000000..c401190
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * USB Phidgets class
+ *
+ * Copyright (C) 2006  Sean Young <sean@mess.org>
+ *
+ * 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.
+ */
+
+extern struct class *phidget_class;
index bfbbbfb..9a8d137 100644 (file)
@@ -20,6 +20,8 @@
 #include <linux/module.h>
 #include <linux/usb.h>
 
+#include "phidget.h"
+
 #define DRIVER_AUTHOR "Sean Young <sean@mess.org>"
 #define DRIVER_DESC "USB PhidgetInterfaceKit Driver"
 
@@ -57,11 +59,15 @@ ifkit(8, 8, 4, 0);
 ifkit(0, 8, 8, 1);
 ifkit(0, 16, 16, 0);
 
+static unsigned long device_no;
+
 struct interfacekit {
        struct usb_device *udev;
        struct usb_interface *intf;
        struct driver_interfacekit *ifkit;
+       struct device *dev;
        unsigned long outputs;
+       int dev_no;
        u8 inputs[MAX_INTERFACES];
        u16 sensors[MAX_INTERFACES];
        u8 lcd_files_on;
@@ -180,21 +186,24 @@ exit:
 }
 
 #define set_lcd_line(number)   \
-static ssize_t lcd_line_##number(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)     \
-{                                                                                      \
-       struct usb_interface *intf = to_usb_interface(dev);                             \
-       struct interfacekit *kit = usb_get_intfdata(intf);                              \
-       change_string(kit, buf, number - 1);                                            \
-       return count;                                                                   \
-}                                                                                      \
-static DEVICE_ATTR(lcd_line_##number, S_IWUGO, NULL, lcd_line_##number);
+static ssize_t lcd_line_##number(struct device *dev,                   \
+                                       struct device_attribute *attr,  \
+                                       const char *buf, size_t count)  \
+{                                                                      \
+       struct interfacekit *kit = dev_get_drvdata(dev);                \
+       change_string(kit, buf, number - 1);                            \
+       return count;                                                   \
+}
+
+#define lcd_line_attr(number)                                          \
+       __ATTR(lcd_line_##number, S_IWUGO, NULL, lcd_line_##number)
+
 set_lcd_line(1);
 set_lcd_line(2);
 
 static ssize_t set_backlight(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
-       struct usb_interface *intf = to_usb_interface(dev);
-       struct interfacekit *kit = usb_get_intfdata(intf);
+       struct interfacekit *kit = dev_get_drvdata(dev);
        int enabled;
        unsigned char *buffer;
        int retval = -ENOMEM;
@@ -226,23 +235,30 @@ exit:
        kfree(buffer);
        return retval;
 }
-static DEVICE_ATTR(backlight, S_IWUGO, NULL, set_backlight);
+
+static struct device_attribute dev_lcd_line_attrs[] = {
+       lcd_line_attr(1),
+       lcd_line_attr(2),
+       __ATTR(backlight, S_IWUGO, NULL, set_backlight)
+};
 
 static void remove_lcd_files(struct interfacekit *kit)
 {
+       int i;
+
        if (kit->lcd_files_on) {
                dev_dbg(&kit->udev->dev, "Removing lcd files\n");
-               device_remove_file(&kit->intf->dev, &dev_attr_lcd_line_1);
-               device_remove_file(&kit->intf->dev, &dev_attr_lcd_line_2);
-               device_remove_file(&kit->intf->dev, &dev_attr_backlight);
+
+               for (i=0; i<ARRAY_SIZE(dev_lcd_line_attrs); i++)
+                       device_remove_file(kit->dev, &dev_lcd_line_attrs[i]);
        }
 }
 
 static ssize_t enable_lcd_files(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
-       struct usb_interface *intf = to_usb_interface(dev);
-       struct interfacekit *kit = usb_get_intfdata(intf);
+       struct interfacekit *kit = dev_get_drvdata(dev);
        int enable;
+       int i, rc;
        
        if (kit->ifkit->has_lcd == 0)
                return -ENODEV;
@@ -253,9 +269,12 @@ static ssize_t enable_lcd_files(struct device *dev, struct device_attribute *att
        if (enable) {
                if (!kit->lcd_files_on) {
                        dev_dbg(&kit->udev->dev, "Adding lcd files\n");
-                       device_create_file(&kit->intf->dev, &dev_attr_lcd_line_1);
-                       device_create_file(&kit->intf->dev, &dev_attr_lcd_line_2);
-                       device_create_file(&kit->intf->dev, &dev_attr_backlight);
+                       for (i=0; i<ARRAY_SIZE(dev_lcd_line_attrs); i++) {
+                               rc = device_create_file(kit->dev,
+                                       &dev_lcd_line_attrs[i]);
+                               if (rc)
+                                       goto out;
+                       }
                        kit->lcd_files_on = 1;
                }
        } else {
@@ -266,7 +285,13 @@ static ssize_t enable_lcd_files(struct device *dev, struct device_attribute *att
        }
        
        return count;
+out:
+       while (i-- > 0)
+               device_remove_file(kit->dev, &dev_lcd_line_attrs[i]);
+
+       return rc;
 }
+
 static DEVICE_ATTR(lcd, S_IWUGO, NULL, enable_lcd_files);
 
 static void interfacekit_irq(struct urb *urb, struct pt_regs *regs)
@@ -362,24 +387,24 @@ static void do_notify(void *data)
        for (i=0; i<kit->ifkit->inputs; i++) {
                if (test_and_clear_bit(i, &kit->input_events)) {
                        sprintf(sysfs_file, "input%d", i + 1);
-                       sysfs_notify(&kit->intf->dev.kobj, NULL, sysfs_file);
+                       sysfs_notify(&kit->dev->kobj, NULL, sysfs_file);
                }
        }
 
        for (i=0; i<kit->ifkit->sensors; i++) {
                if (test_and_clear_bit(i, &kit->sensor_events)) {
                        sprintf(sysfs_file, "sensor%d", i + 1);
-                       sysfs_notify(&kit->intf->dev.kobj, NULL, sysfs_file);
+                       sysfs_notify(&kit->dev->kobj, NULL, sysfs_file);
                }
        }
 }
 
 #define show_set_output(value)         \
-static ssize_t set_output##value(struct device *dev, struct device_attribute *attr, const char *buf,   \
-                                                       size_t count)   \
+static ssize_t set_output##value(struct device *dev,                   \
+                                       struct device_attribute *attr,  \
+                                       const char *buf, size_t count)  \
 {                                                                      \
-       struct usb_interface *intf = to_usb_interface(dev);             \
-       struct interfacekit *kit = usb_get_intfdata(intf);              \
+       struct interfacekit *kit = dev_get_drvdata(dev);                \
        int enabled;                                                    \
        int retval;                                                     \
                                                                        \
@@ -391,15 +416,19 @@ static ssize_t set_output##value(struct device *dev, struct device_attribute *at
        return retval ? retval : count;                                 \
 }                                                                      \
                                                                        \
-static ssize_t show_output##value(struct device *dev, struct device_attribute *attr, char *buf)        \
+static ssize_t show_output##value(struct device *dev,                  \
+                                       struct device_attribute *attr,  \
+                                       char *buf)                      \
 {                                                                      \
-       struct usb_interface *intf = to_usb_interface(dev);             \
-       struct interfacekit *kit = usb_get_intfdata(intf);              \
+       struct interfacekit *kit = dev_get_drvdata(dev);                \
                                                                        \
        return sprintf(buf, "%d\n", !!test_bit(value - 1, &kit->outputs));\
-}                                                                      \
-static DEVICE_ATTR(output##value, S_IWUGO | S_IRUGO,                   \
-               show_output##value, set_output##value);
+}
+
+#define output_attr(value)                                             \
+       __ATTR(output##value, S_IWUGO | S_IRUGO,                        \
+               show_output##value, set_output##value)
+
 show_set_output(1);
 show_set_output(2);
 show_set_output(3);
@@ -417,15 +446,24 @@ show_set_output(14);
 show_set_output(15);
 show_set_output(16);
 
+static struct device_attribute dev_output_attrs[] = {
+       output_attr(1), output_attr(2), output_attr(3), output_attr(4),
+       output_attr(5), output_attr(6), output_attr(7), output_attr(8),
+       output_attr(9), output_attr(10), output_attr(11), output_attr(12),
+       output_attr(13), output_attr(14), output_attr(15), output_attr(16)
+};
+
 #define show_input(value)      \
-static ssize_t show_input##value(struct device *dev, struct device_attribute *attr, char *buf) \
+static ssize_t show_input##value(struct device *dev,                   \
+                       struct device_attribute *attr, char *buf)       \
 {                                                                      \
-       struct usb_interface *intf = to_usb_interface(dev);             \
-       struct interfacekit *kit = usb_get_intfdata(intf);              \
+       struct interfacekit *kit = dev_get_drvdata(dev);                \
                                                                        \
        return sprintf(buf, "%d\n", (int)kit->inputs[value - 1]);       \
-}                                                                      \
-static DEVICE_ATTR(input##value, S_IRUGO, show_input##value, NULL);
+}
+
+#define input_attr(value)                                              \
+       __ATTR(input##value, S_IRUGO, show_input##value, NULL)
 
 show_input(1);
 show_input(2);
@@ -444,15 +482,25 @@ show_input(14);
 show_input(15);
 show_input(16);
 
+static struct device_attribute dev_input_attrs[] = {
+       input_attr(1), input_attr(2), input_attr(3), input_attr(4),
+       input_attr(5), input_attr(6), input_attr(7), input_attr(8),
+       input_attr(9), input_attr(10), input_attr(11), input_attr(12),
+       input_attr(13), input_attr(14), input_attr(15), input_attr(16)
+};
+
 #define show_sensor(value)     \
-static ssize_t show_sensor##value(struct device *dev, struct device_attribute *attr, char *buf)        \
+static ssize_t show_sensor##value(struct device *dev,                  \
+                                       struct device_attribute *attr,  \
+                                       char *buf)                      \
 {                                                                      \
-       struct usb_interface *intf = to_usb_interface(dev);             \
-       struct interfacekit *kit = usb_get_intfdata(intf);              \
+       struct interfacekit *kit = dev_get_drvdata(dev);                \
                                                                        \
        return sprintf(buf, "%d\n", (int)kit->sensors[value - 1]);      \
-}                                                                      \
-static DEVICE_ATTR(sensor##value, S_IRUGO, show_sensor##value, NULL);
+}
+
+#define sensor_attr(value)                                             \
+       __ATTR(sensor##value, S_IRUGO, show_sensor##value, NULL)
 
 show_sensor(1);
 show_sensor(2);
@@ -463,6 +511,11 @@ show_sensor(6);
 show_sensor(7);
 show_sensor(8);
 
+static struct device_attribute dev_sensor_attrs[] = {
+       sensor_attr(1), sensor_attr(2), sensor_attr(3), sensor_attr(4),
+       sensor_attr(5), sensor_attr(6), sensor_attr(7), sensor_attr(8)
+};
+
 static int interfacekit_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
        struct usb_device *dev = interface_to_usbdev(intf);
@@ -471,6 +524,7 @@ static int interfacekit_probe(struct usb_interface *intf, const struct usb_devic
        struct interfacekit *kit;
        struct driver_interfacekit *ifkit;
        int pipe, maxp, rc = -ENOMEM;
+       int bit, value, i;
 
        ifkit = (struct driver_interfacekit *)id->driver_info;
        if (!ifkit)
@@ -493,6 +547,7 @@ static int interfacekit_probe(struct usb_interface *intf, const struct usb_devic
        if (!kit)
                goto out;
 
+       kit->dev_no = -1;
        kit->ifkit = ifkit;
        kit->data = usb_buffer_alloc(dev, URB_INT_SIZE, SLAB_ATOMIC, &kit->data_dma);
        if (!kit->data)
@@ -513,85 +568,80 @@ static int interfacekit_probe(struct usb_interface *intf, const struct usb_devic
 
        usb_set_intfdata(intf, kit);
 
+        do {
+                bit = find_first_zero_bit(&device_no, sizeof(device_no));
+                value = test_and_set_bit(bit, &device_no);
+        } while(value);
+        kit->dev_no = bit;
+
+        kit->dev = device_create(phidget_class, &kit->udev->dev, 0,
+                               "interfacekit%d", kit->dev_no);
+        if (IS_ERR(kit->dev)) {
+                rc = PTR_ERR(kit->dev);
+                kit->dev = NULL;
+                goto out;
+        }
+       dev_set_drvdata(kit->dev, kit);
+
        if (usb_submit_urb(kit->irq, GFP_KERNEL)) {
                rc = -EIO;
                goto out;
        }
 
-       if (ifkit->outputs >= 4) {
-               device_create_file(&intf->dev, &dev_attr_output1);
-               device_create_file(&intf->dev, &dev_attr_output2);
-               device_create_file(&intf->dev, &dev_attr_output3);
-               device_create_file(&intf->dev, &dev_attr_output4);
-       }
-       if (ifkit->outputs >= 8) {
-               device_create_file(&intf->dev, &dev_attr_output5);
-               device_create_file(&intf->dev, &dev_attr_output6);
-               device_create_file(&intf->dev, &dev_attr_output7);
-               device_create_file(&intf->dev, &dev_attr_output8);
-       } 
-       if (ifkit->outputs == 16) {
-               device_create_file(&intf->dev, &dev_attr_output9);
-               device_create_file(&intf->dev, &dev_attr_output10);
-               device_create_file(&intf->dev, &dev_attr_output11);
-               device_create_file(&intf->dev, &dev_attr_output12);
-               device_create_file(&intf->dev, &dev_attr_output13);
-               device_create_file(&intf->dev, &dev_attr_output14);
-               device_create_file(&intf->dev, &dev_attr_output15);
-               device_create_file(&intf->dev, &dev_attr_output16);
+       for (i=0; i<ifkit->outputs; i++ ) {
+               rc = device_create_file(kit->dev, &dev_output_attrs[i]);
+               if (rc)
+                       goto out2;
        }
 
-       if (ifkit->inputs >= 4) {
-               device_create_file(&intf->dev, &dev_attr_input1);
-               device_create_file(&intf->dev, &dev_attr_input2);
-               device_create_file(&intf->dev, &dev_attr_input3);
-               device_create_file(&intf->dev, &dev_attr_input4);
-       }
-       if (ifkit->inputs >= 8) {
-               device_create_file(&intf->dev, &dev_attr_input5);
-               device_create_file(&intf->dev, &dev_attr_input6);
-               device_create_file(&intf->dev, &dev_attr_input7);
-               device_create_file(&intf->dev, &dev_attr_input8);
-       }
-       if (ifkit->inputs == 16) {
-               device_create_file(&intf->dev, &dev_attr_input9);
-               device_create_file(&intf->dev, &dev_attr_input10);
-               device_create_file(&intf->dev, &dev_attr_input11);
-               device_create_file(&intf->dev, &dev_attr_input12);
-               device_create_file(&intf->dev, &dev_attr_input13);
-               device_create_file(&intf->dev, &dev_attr_input14);
-               device_create_file(&intf->dev, &dev_attr_input15);
-               device_create_file(&intf->dev, &dev_attr_input16);
+       for (i=0; i<ifkit->inputs; i++ ) {
+               rc = device_create_file(kit->dev, &dev_input_attrs[i]);
+               if (rc)
+                       goto out3;
        }
 
-       if (ifkit->sensors >= 4) {
-               device_create_file(&intf->dev, &dev_attr_sensor1);
-               device_create_file(&intf->dev, &dev_attr_sensor2);
-               device_create_file(&intf->dev, &dev_attr_sensor3);
-               device_create_file(&intf->dev, &dev_attr_sensor4);
-       }
-       if (ifkit->sensors >= 7) {
-               device_create_file(&intf->dev, &dev_attr_sensor5);
-               device_create_file(&intf->dev, &dev_attr_sensor6);
-               device_create_file(&intf->dev, &dev_attr_sensor7);
+       for (i=0; i<ifkit->sensors; i++ ) {
+               rc = device_create_file(kit->dev, &dev_sensor_attrs[i]);
+               if (rc)
+                       goto out4;
        }
-       if (ifkit->sensors == 8)
-               device_create_file(&intf->dev, &dev_attr_sensor8);
 
-       if (ifkit->has_lcd)
-               device_create_file(&intf->dev, &dev_attr_lcd);
+       if (ifkit->has_lcd) {
+               rc = device_create_file(kit->dev, &dev_attr_lcd);
+               if (rc)
+                       goto out4;
+
+       }
 
        dev_info(&intf->dev, "USB PhidgetInterfaceKit %d/%d/%d attached\n",
                        ifkit->sensors, ifkit->inputs, ifkit->outputs);
 
        return 0;
 
+out4:
+       while (i-- > 0)
+               device_remove_file(kit->dev, &dev_sensor_attrs[i]);
+
+       i = ifkit->inputs;
+out3:
+       while (i-- > 0)
+               device_remove_file(kit->dev, &dev_input_attrs[i]);
+
+       i = ifkit->outputs;
+out2:
+       while (i-- > 0)
+               device_remove_file(kit->dev, &dev_output_attrs[i]);
 out:
        if (kit) {
                if (kit->irq)
                        usb_free_urb(kit->irq);
                if (kit->data)
                        usb_buffer_free(dev, URB_INT_SIZE, kit->data, kit->data_dma);
+               if (kit->dev)
+                       device_unregister(kit->dev);
+               if (kit->dev_no >= 0)
+                       clear_bit(kit->dev_no, &device_no);
+
                kfree(kit);
        }
 
@@ -601,6 +651,7 @@ out:
 static void interfacekit_disconnect(struct usb_interface *interface)
 {
        struct interfacekit *kit;
+       int i;
 
        kit = usb_get_intfdata(interface);
        usb_set_intfdata(interface, NULL);
@@ -613,73 +664,28 @@ static void interfacekit_disconnect(struct usb_interface *interface)
 
        cancel_delayed_work(&kit->do_notify);
 
-       if (kit->ifkit->outputs >= 4) {
-               device_remove_file(&interface->dev, &dev_attr_output1);
-               device_remove_file(&interface->dev, &dev_attr_output2);
-               device_remove_file(&interface->dev, &dev_attr_output3);
-               device_remove_file(&interface->dev, &dev_attr_output4);
-       }
-       if (kit->ifkit->outputs >= 8) {
-               device_remove_file(&interface->dev, &dev_attr_output5);
-               device_remove_file(&interface->dev, &dev_attr_output6);
-               device_remove_file(&interface->dev, &dev_attr_output7);
-               device_remove_file(&interface->dev, &dev_attr_output8);
-       }
-       if (kit->ifkit->outputs == 16) {
-               device_remove_file(&interface->dev, &dev_attr_output9);
-               device_remove_file(&interface->dev, &dev_attr_output10);
-               device_remove_file(&interface->dev, &dev_attr_output11);
-               device_remove_file(&interface->dev, &dev_attr_output12);
-               device_remove_file(&interface->dev, &dev_attr_output13);
-               device_remove_file(&interface->dev, &dev_attr_output14);
-               device_remove_file(&interface->dev, &dev_attr_output15);
-               device_remove_file(&interface->dev, &dev_attr_output16);
-       }
+       for (i=0; i<kit->ifkit->outputs; i++)
+               device_remove_file(kit->dev, &dev_output_attrs[i]);
 
-       if (kit->ifkit->inputs >= 4) {
-               device_remove_file(&interface->dev, &dev_attr_input1);
-               device_remove_file(&interface->dev, &dev_attr_input2);
-               device_remove_file(&interface->dev, &dev_attr_input3);
-               device_remove_file(&interface->dev, &dev_attr_input4);
-       }
-       if (kit->ifkit->inputs >= 8) {
-               device_remove_file(&interface->dev, &dev_attr_input5);
-               device_remove_file(&interface->dev, &dev_attr_input6);
-               device_remove_file(&interface->dev, &dev_attr_input7);
-               device_remove_file(&interface->dev, &dev_attr_input8);
-       }
-       if (kit->ifkit->inputs == 16) {
-               device_remove_file(&interface->dev, &dev_attr_input9);
-               device_remove_file(&interface->dev, &dev_attr_input10);
-               device_remove_file(&interface->dev, &dev_attr_input11);
-               device_remove_file(&interface->dev, &dev_attr_input12);
-               device_remove_file(&interface->dev, &dev_attr_input13);
-               device_remove_file(&interface->dev, &dev_attr_input14);
-               device_remove_file(&interface->dev, &dev_attr_input15);
-               device_remove_file(&interface->dev, &dev_attr_input16);
-       }
+       for (i=0; i<kit->ifkit->inputs; i++)
+               device_remove_file(kit->dev, &dev_input_attrs[i]);
 
-       if (kit->ifkit->sensors >= 4) {
-               device_remove_file(&interface->dev, &dev_attr_sensor1);
-               device_remove_file(&interface->dev, &dev_attr_sensor2);
-               device_remove_file(&interface->dev, &dev_attr_sensor3);
-               device_remove_file(&interface->dev, &dev_attr_sensor4);
-       }
-       if (kit->ifkit->sensors >= 7) {
-               device_remove_file(&interface->dev, &dev_attr_sensor5);
-               device_remove_file(&interface->dev, &dev_attr_sensor6);
-               device_remove_file(&interface->dev, &dev_attr_sensor7);
+       for (i=0; i<kit->ifkit->sensors; i++)
+               device_remove_file(kit->dev, &dev_sensor_attrs[i]);
+
+       if (kit->ifkit->has_lcd) {
+               device_remove_file(kit->dev, &dev_attr_lcd);
+               remove_lcd_files(kit);
        }
-       if (kit->ifkit->sensors == 8)
-               device_remove_file(&interface->dev, &dev_attr_sensor8);
 
-       if (kit->ifkit->has_lcd)
-               device_remove_file(&interface->dev, &dev_attr_lcd);
+       device_unregister(kit->dev);
 
        dev_info(&interface->dev, "USB PhidgetInterfaceKit %d/%d/%d detached\n",
                kit->ifkit->sensors, kit->ifkit->inputs, kit->ifkit->outputs);
 
        usb_put_dev(kit->udev);
+       clear_bit(kit->dev_no, &device_no);
+
        kfree(kit);
 }
 
diff --git a/drivers/usb/misc/phidgetmotorcontrol.c b/drivers/usb/misc/phidgetmotorcontrol.c
new file mode 100644 (file)
index 0000000..6b59b62
--- /dev/null
@@ -0,0 +1,466 @@
+/*
+ * USB Phidget MotorControl driver
+ *
+ * Copyright (C) 2006  Sean Young <sean@mess.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "phidget.h"
+
+#define DRIVER_AUTHOR "Sean Young <sean@mess.org>"
+#define DRIVER_DESC "USB PhidgetMotorControl Driver"
+
+#define USB_VENDOR_ID_GLAB             0x06c2
+#define USB_DEVICE_ID_MOTORCONTROL     0x0058
+
+#define URB_INT_SIZE                   8
+
+static unsigned long device_no;
+
+struct motorcontrol {
+       struct usb_device *udev;
+       struct usb_interface *intf;
+       struct device *dev;
+       int dev_no;
+       u8 inputs[4];
+       s8 desired_speed[2];
+       s8 speed[2];
+       s16 _current[2];
+       s8 acceleration[2];
+       struct urb *irq;
+       unsigned char *data;
+       dma_addr_t data_dma;
+
+       struct work_struct do_notify;
+       unsigned long input_events;
+       unsigned long speed_events;
+       unsigned long exceed_events;
+};
+
+static struct usb_device_id id_table[] = {
+       { USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_MOTORCONTROL) },
+       {}
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static int set_motor(struct motorcontrol *mc, int motor)
+{
+       u8 *buffer;
+       int speed, speed2, acceleration;
+       int retval;
+
+       buffer = kzalloc(8, GFP_KERNEL);
+       if (!buffer) {
+               dev_err(&mc->intf->dev, "%s - out of memory\n", __FUNCTION__);
+               return -ENOMEM;
+       }
+
+       acceleration = mc->acceleration[motor] * 10;
+       /* -127 <= speed <= 127 */
+       speed = (mc->desired_speed[motor] * 127) / 100;
+       /* -0x7300 <= speed2 <= 0x7300 */
+       speed2 = (mc->desired_speed[motor] * 230 * 128) / 100;
+
+       buffer[0] = motor;
+       buffer[1] = speed;
+       buffer[2] = acceleration >> 8;
+       buffer[3] = acceleration;
+       buffer[4] = speed2 >> 8;
+       buffer[5] = speed2;
+
+       retval = usb_control_msg(mc->udev,
+                        usb_sndctrlpipe(mc->udev, 0),
+                        0x09, 0x21, 0x0200, 0x0000, buffer, 8, 2000);
+
+       if (retval != 8)
+               dev_err(&mc->intf->dev, "usb_control_msg returned %d\n",
+                               retval);
+       kfree(buffer);
+
+       return retval < 0 ? retval : 0;
+}
+
+static void motorcontrol_irq(struct urb *urb, struct pt_regs *regs)
+{
+       struct motorcontrol *mc = urb->context;
+       unsigned char *buffer = mc->data;
+       int i, level;
+       int status;
+
+       switch (urb->status) {
+       case 0:                 /* success */
+               break;
+       case -ECONNRESET:       /* unlink */
+       case -ENOENT:
+       case -ESHUTDOWN:
+               return;
+       /* -EPIPE:  should clear the halt */
+       default:                /* error */
+               goto resubmit;
+       }
+
+       /* digital inputs */
+       for (i=0; i<4; i++) {
+               level = (buffer[0] >> i) & 1;
+               if (mc->inputs[i] != level) {
+                       mc->inputs[i] = level;
+                       set_bit(i, &mc->input_events);
+               }
+       }
+
+       /* motor speed */
+       if (buffer[2] == 0) {
+               for (i=0; i<2; i++) {
+               level = ((s8)buffer[4+i]) * 100 / 127;
+                       if (mc->speed[i] != level) {
+                               mc->speed[i] = level;
+                               set_bit(i, &mc->speed_events);
+                       }
+               }
+       } else {
+               int index = buffer[3] & 1;
+
+               level = ((s8)buffer[4] << 8) | buffer[5];
+               level = level * 100 / 29440;
+               if (mc->speed[index] != level) {
+                       mc->speed[index] = level;
+                       set_bit(index, &mc->speed_events);
+               }
+
+               level = ((s8)buffer[6] << 8) | buffer[7];
+               mc->_current[index] = level * 100 / 1572;
+       }
+
+       if (buffer[1] & 1)
+               set_bit(0, &mc->exceed_events);
+
+       if (buffer[1] & 2)
+               set_bit(1, &mc->exceed_events);
+
+       if (mc->input_events || mc->exceed_events || mc->speed_events)
+               schedule_work(&mc->do_notify);
+
+resubmit:
+       status = usb_submit_urb(urb, SLAB_ATOMIC);
+       if (status)
+               dev_err(&mc->intf->dev,
+                       "can't resubmit intr, %s-%s/motorcontrol0, status %d",
+                       mc->udev->bus->bus_name,
+                       mc->udev->devpath, status);
+}
+
+static void do_notify(void *data)
+{
+       struct motorcontrol *mc = data;
+       int i;
+       char sysfs_file[8];
+
+       for (i=0; i<4; i++) {
+               if (test_and_clear_bit(i, &mc->input_events)) {
+                       sprintf(sysfs_file, "input%d", i);
+                       sysfs_notify(&mc->dev->kobj, NULL, sysfs_file);
+               }
+       }
+
+       for (i=0; i<2; i++) {
+               if (test_and_clear_bit(i, &mc->speed_events)) {
+                       sprintf(sysfs_file, "speed%d", i);
+                       sysfs_notify(&mc->dev->kobj, NULL, sysfs_file);
+               }
+       }
+
+       for (i=0; i<2; i++) {
+               if (test_and_clear_bit(i, &mc->exceed_events))
+                       dev_warn(&mc->intf->dev,
+                               "motor #%d exceeds 1.5 Amp current limit\n", i);
+       }
+}
+
+#define show_set_speed(value)          \
+static ssize_t set_speed##value(struct device *dev,                    \
+                                       struct device_attribute *attr,  \
+                                       const char *buf, size_t count)  \
+{                                                                      \
+       struct motorcontrol *mc = dev_get_drvdata(dev);                 \
+       int speed;                                                      \
+       int retval;                                                     \
+                                                                       \
+       if (sscanf(buf, "%d", &speed) < 1)                              \
+               return -EINVAL;                                         \
+                                                                       \
+       if (speed < -100 || speed > 100)                                \
+               return -EINVAL;                                         \
+                                                                       \
+       mc->desired_speed[value] = speed;                               \
+                                                                       \
+       retval = set_motor(mc, value);                                  \
+                                                                       \
+       return retval ? retval : count;                                 \
+}                                                                      \
+                                                                       \
+static ssize_t show_speed##value(struct device *dev,                   \
+                                       struct device_attribute *attr,  \
+                                       char *buf)                      \
+{                                                                      \
+       struct motorcontrol *mc = dev_get_drvdata(dev);                 \
+                                                                       \
+       return sprintf(buf, "%d\n", mc->speed[value]);                  \
+}
+
+#define speed_attr(value)                                              \
+       __ATTR(speed##value, S_IWUGO | S_IRUGO,                         \
+               show_speed##value, set_speed##value)
+
+show_set_speed(0);
+show_set_speed(1);
+
+#define show_set_acceleration(value)           \
+static ssize_t set_acceleration##value(struct device *dev,             \
+                                       struct device_attribute *attr,  \
+                                       const char *buf, size_t count)  \
+{                                                                      \
+       struct motorcontrol *mc = dev_get_drvdata(dev);                 \
+       int acceleration;                                               \
+       int retval;                                                     \
+                                                                       \
+       if (sscanf(buf, "%d", &acceleration) < 1)                       \
+               return -EINVAL;                                         \
+                                                                       \
+       if (acceleration < 0 || acceleration > 100)                     \
+               return -EINVAL;                                         \
+                                                                       \
+       mc->acceleration[value] = acceleration;                         \
+                                                                       \
+       retval = set_motor(mc, value);                                  \
+                                                                       \
+       return retval ? retval : count;                                 \
+}                                                                      \
+                                                                       \
+static ssize_t show_acceleration##value(struct device *dev,            \
+                                       struct device_attribute *attr,  \
+                                                       char *buf)      \
+{                                                                      \
+       struct motorcontrol *mc = dev_get_drvdata(dev);                 \
+                                                                       \
+       return sprintf(buf, "%d\n", mc->acceleration[value]);           \
+}
+
+#define acceleration_attr(value)       \
+       __ATTR(acceleration##value, S_IWUGO | S_IRUGO,                  \
+               show_acceleration##value, set_acceleration##value)
+
+show_set_acceleration(0);
+show_set_acceleration(1);
+
+#define show_current(value)    \
+static ssize_t show_current##value(struct device *dev,                 \
+                                       struct device_attribute *attr,  \
+                                       char *buf)                      \
+{                                                                      \
+       struct motorcontrol *mc = dev_get_drvdata(dev);                 \
+                                                                       \
+       return sprintf(buf, "%dmA\n", (int)mc->_current[value]);        \
+}
+
+#define current_attr(value)    \
+       __ATTR(current##value, S_IRUGO, show_current##value, NULL)
+
+show_current(0);
+show_current(1);
+
+#define show_input(value)      \
+static ssize_t show_input##value(struct device *dev,                   \
+                                       struct device_attribute *attr,  \
+                                       char *buf)                      \
+{                                                                      \
+       struct motorcontrol *mc = dev_get_drvdata(dev);                 \
+                                                                       \
+       return sprintf(buf, "%d\n", (int)mc->inputs[value]);            \
+}
+
+#define input_attr(value)      \
+       __ATTR(input##value, S_IRUGO, show_input##value, NULL)
+
+show_input(0);
+show_input(1);
+show_input(2);
+show_input(3);
+
+static struct device_attribute dev_attrs[] = {
+       input_attr(0),
+       input_attr(1),
+       input_attr(2),
+       input_attr(3),
+       speed_attr(0),
+       speed_attr(1),
+       acceleration_attr(0),
+       acceleration_attr(1),
+       current_attr(0),
+       current_attr(1)
+};
+
+static int motorcontrol_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+       struct usb_device *dev = interface_to_usbdev(intf);
+       struct usb_host_interface *interface;
+       struct usb_endpoint_descriptor *endpoint;
+       struct motorcontrol *mc;
+       int pipe, maxp, rc = -ENOMEM;
+       int bit, value, i;
+
+       interface = intf->cur_altsetting;
+       if (interface->desc.bNumEndpoints != 1)
+               return -ENODEV;
+
+       endpoint = &interface->endpoint[0].desc;
+       if (!(endpoint->bEndpointAddress & 0x80))
+               return -ENODEV;
+
+       /*
+        * bmAttributes
+        */
+       pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+       maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+       mc = kzalloc(sizeof(*mc), GFP_KERNEL);
+       if (!mc)
+               goto out;
+
+       mc->dev_no = -1;
+       mc->data = usb_buffer_alloc(dev, URB_INT_SIZE, SLAB_ATOMIC, &mc->data_dma);
+       if (!mc->data)
+               goto out;
+
+       mc->irq = usb_alloc_urb(0, GFP_KERNEL);
+       if (!mc->irq)
+               goto out;
+
+       mc->udev = usb_get_dev(dev);
+       mc->intf = intf;
+       mc->acceleration[0] = mc->acceleration[1] = 10;
+       INIT_WORK(&mc->do_notify, do_notify, mc);
+       usb_fill_int_urb(mc->irq, mc->udev, pipe, mc->data,
+                       maxp > URB_INT_SIZE ? URB_INT_SIZE : maxp,
+                       motorcontrol_irq, mc, endpoint->bInterval);
+       mc->irq->transfer_dma = mc->data_dma;
+       mc->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+       usb_set_intfdata(intf, mc);
+
+       do {
+               bit = find_first_zero_bit(&device_no, sizeof(device_no));
+               value = test_and_set_bit(bit, &device_no);
+       } while(value);
+       mc->dev_no = bit;
+
+       mc->dev = device_create(phidget_class, &mc->udev->dev, 0,
+                               "motorcontrol%d", mc->dev_no);
+       if (IS_ERR(mc->dev)) {
+               rc = PTR_ERR(mc->dev);
+               mc->dev = NULL;
+               goto out;
+       }
+
+       dev_set_drvdata(mc->dev, mc);
+
+       if (usb_submit_urb(mc->irq, GFP_KERNEL)) {
+               rc = -EIO;
+               goto out;
+       }
+
+       for (i=0; i<ARRAY_SIZE(dev_attrs); i++) {
+               rc = device_create_file(mc->dev, &dev_attrs[i]);
+               if (rc)
+                       goto out2;
+       }
+
+       dev_info(&intf->dev, "USB PhidgetMotorControl attached\n");
+
+       return 0;
+out2:
+       while (i-- > 0)
+               device_remove_file(mc->dev, &dev_attrs[i]);
+out:
+       if (mc) {
+               if (mc->irq)
+                       usb_free_urb(mc->irq);
+               if (mc->data)
+                       usb_buffer_free(dev, URB_INT_SIZE, mc->data, mc->data_dma);
+               if (mc->dev)
+                       device_unregister(mc->dev);
+               if (mc->dev_no >= 0)
+                       clear_bit(mc->dev_no, &device_no);
+
+               kfree(mc);
+       }
+
+       return rc;
+}
+
+static void motorcontrol_disconnect(struct usb_interface *interface)
+{
+       struct motorcontrol *mc;
+       int i;
+
+       mc = usb_get_intfdata(interface);
+       usb_set_intfdata(interface, NULL);
+       if (!mc)
+               return;
+
+       usb_kill_urb(mc->irq);
+       usb_free_urb(mc->irq);
+       usb_buffer_free(mc->udev, URB_INT_SIZE, mc->data, mc->data_dma);
+
+       cancel_delayed_work(&mc->do_notify);
+
+       for (i=0; i<ARRAY_SIZE(dev_attrs); i++)
+               device_remove_file(mc->dev, &dev_attrs[i]);
+
+       device_unregister(mc->dev);
+
+       usb_put_dev(mc->udev);
+       clear_bit(mc->dev_no, &device_no);
+       kfree(mc);
+
+       dev_info(&interface->dev, "USB PhidgetMotorControl detached\n");
+}
+
+static struct usb_driver motorcontrol_driver = {
+       .name = "phidgetmotorcontrol",
+       .probe = motorcontrol_probe,
+       .disconnect = motorcontrol_disconnect,
+       .id_table = id_table
+};
+
+static int __init motorcontrol_init(void)
+{
+       int retval = 0;
+
+       retval = usb_register(&motorcontrol_driver);
+       if (retval)
+               err("usb_register failed. Error number %d", retval);
+
+       return retval;
+}
+
+static void __exit motorcontrol_exit(void)
+{
+       usb_deregister(&motorcontrol_driver);
+}
+
+module_init(motorcontrol_init);
+module_exit(motorcontrol_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
index c0df79c..7163f05 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * USB PhidgetServo driver 1.0
  *
- * Copyright (C) 2004 Sean Young <sean@mess.org>
+ * Copyright (C) 2004, 2006 Sean Young <sean@mess.org>
  *
  * 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
  *
  * CAUTION: Generally you should use 0 < degrees < 180 as anything else
  * is probably beyond the range of your servo and may damage it.
- *
- * Jun 16, 2004: Sean Young <sean@mess.org>
- *  - cleanups
- *  - was using memory after kfree()
- * Aug 8, 2004: Sean Young <sean@mess.org>
- *  - set the highest angle as high as the hardware allows, there are 
- *    some odd servos out there
- *
  */
 
 #include <linux/kernel.h>
@@ -32,6 +24,8 @@
 #include <linux/module.h>
 #include <linux/usb.h>
 
+#include "phidget.h"
+
 #define DRIVER_AUTHOR "Sean Young <sean@mess.org>"
 #define DRIVER_DESC "USB PhidgetServo Driver"
 
@@ -70,8 +64,12 @@ static struct usb_device_id id_table[] = {
 
 MODULE_DEVICE_TABLE(usb, id_table);
 
+static int unsigned long device_no;
+
 struct phidget_servo {
        struct usb_device *udev;
+       struct device *dev;
+       int dev_no;
        ulong type;
        int pulse[4];
        int degrees[4];
@@ -203,16 +201,16 @@ change_position_v20(struct phidget_servo *servo, int servo_no, int degrees,
 }
 
 #define show_set(value)        \
-static ssize_t set_servo##value (struct device *dev, struct device_attribute *attr,                    \
+static ssize_t set_servo##value (struct device *dev,                   \
+                                       struct device_attribute *attr,  \
                                        const char *buf, size_t count)  \
 {                                                                      \
        int degrees, minutes, retval;                                   \
-       struct usb_interface *intf = to_usb_interface (dev);            \
-       struct phidget_servo *servo = usb_get_intfdata (intf);          \
+       struct phidget_servo *servo = dev_get_drvdata(dev);             \
                                                                        \
        minutes = 0;                                                    \
        /* must at least convert degrees */                             \
-       if (sscanf (buf, "%d.%d", &degrees, &minutes) < 1) {            \
+       if (sscanf(buf, "%d.%d", &degrees, &minutes) < 1) {             \
                return -EINVAL;                                         \
        }                                                               \
                                                                        \
@@ -220,86 +218,127 @@ static ssize_t set_servo##value (struct device *dev, struct device_attribute *at
                return -EINVAL;                                         \
                                                                        \
        if (servo->type & SERVO_VERSION_30)                             \
-               retval = change_position_v30 (servo, value, degrees,    \
+               retval = change_position_v30(servo, value, degrees,     \
                                                        minutes);       \
        else                                                            \
-               retval = change_position_v20 (servo, value, degrees,    \
+               retval = change_position_v20(servo, value, degrees,     \
                                                        minutes);       \
                                                                        \
        return retval < 0 ? retval : count;                             \
 }                                                                      \
                                                                        \
-static ssize_t show_servo##value (struct device *dev, struct device_attribute *attr, char *buf)        \
+static ssize_t show_servo##value (struct device *dev,                  \
+                                       struct device_attribute *attr,  \
+                                       char *buf)                      \
 {                                                                      \
-       struct usb_interface *intf = to_usb_interface (dev);            \
-       struct phidget_servo *servo = usb_get_intfdata (intf);          \
+       struct phidget_servo *servo = dev_get_drvdata(dev);             \
                                                                        \
-       return sprintf (buf, "%d.%02d\n", servo->degrees[value],        \
+       return sprintf(buf, "%d.%02d\n", servo->degrees[value],         \
                                servo->minutes[value]);                 \
-}                                                                      \
-static DEVICE_ATTR(servo##value, S_IWUGO | S_IRUGO,                    \
-         show_servo##value, set_servo##value);
+}
 
+#define servo_attr(value)                                              \
+       __ATTR(servo##value, S_IWUGO | S_IRUGO,                         \
+               show_servo##value, set_servo##value)
 show_set(0);
 show_set(1);
 show_set(2);
 show_set(3);
 
+static struct device_attribute dev_attrs[] = {
+       servo_attr(0), servo_attr(1), servo_attr(2), servo_attr(3)
+};
+
 static int
 servo_probe(struct usb_interface *interface, const struct usb_device_id *id)
 {
        struct usb_device *udev = interface_to_usbdev(interface);
        struct phidget_servo *dev;
+       int bit, value, rc;
+       int servo_count, i;
 
        dev = kzalloc(sizeof (struct phidget_servo), GFP_KERNEL);
        if (dev == NULL) {
                dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__);
-               return -ENOMEM;
+               rc = -ENOMEM;
+               goto out;
        }
 
        dev->udev = usb_get_dev(udev);
        dev->type = id->driver_info;
+       dev->dev_no = -1;
        usb_set_intfdata(interface, dev);
 
-       device_create_file(&interface->dev, &dev_attr_servo0);
-       if (dev->type & SERVO_COUNT_QUAD) {
-               device_create_file(&interface->dev, &dev_attr_servo1);
-               device_create_file(&interface->dev, &dev_attr_servo2);
-               device_create_file(&interface->dev, &dev_attr_servo3);
+        do {
+                bit = find_first_zero_bit(&device_no, sizeof(device_no));
+                value = test_and_set_bit(bit, &device_no);
+        } while (value);
+       dev->dev_no = bit;
+
+       dev->dev = device_create(phidget_class, &dev->udev->dev, 0,
+                                "servo%d", dev->dev_no);
+       if (IS_ERR(dev->dev)) {
+               rc = PTR_ERR(dev->dev);
+               dev->dev = NULL;
+               goto out;
+       }
+
+       servo_count = dev->type & SERVO_COUNT_QUAD ? 4 : 1;
+
+       for (i=0; i<servo_count; i++) {
+               rc = device_create_file(dev->dev, &dev_attrs[i]);
+               if (rc)
+                       goto out2;
        }
 
        dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 attached\n",
-               dev->type & SERVO_COUNT_QUAD ? 4 : 1,
-               dev->type & SERVO_VERSION_30 ? 3 : 2);
+               servo_count, dev->type & SERVO_VERSION_30 ? 3 : 2);
 
-       if(!(dev->type & SERVO_VERSION_30))
+       if (!(dev->type & SERVO_VERSION_30))
                dev_info(&interface->dev,
                         "WARNING: v2.0 not tested! Please report if it works.\n");
 
        return 0;
+out2:
+       while (i-- > 0)
+               device_remove_file(dev->dev, &dev_attrs[i]);
+out:
+       if (dev) {
+               if (dev->dev)
+                       device_unregister(dev->dev);
+               if (dev->dev_no >= 0)
+                       clear_bit(dev->dev_no, &device_no);
+
+               kfree(dev);
+       }
+
+       return rc;
 }
 
 static void
 servo_disconnect(struct usb_interface *interface)
 {
        struct phidget_servo *dev;
+       int servo_count, i;
 
        dev = usb_get_intfdata(interface);
        usb_set_intfdata(interface, NULL);
 
-       device_remove_file(&interface->dev, &dev_attr_servo0);
-       if (dev->type & SERVO_COUNT_QUAD) {
-               device_remove_file(&interface->dev, &dev_attr_servo1);
-               device_remove_file(&interface->dev, &dev_attr_servo2);
-               device_remove_file(&interface->dev, &dev_attr_servo3);
-       }
+       if (!dev)
+               return;
+
+       servo_count = dev->type & SERVO_COUNT_QUAD ? 4 : 1;
+
+       for (i=0; i<servo_count; i++)
+               device_remove_file(dev->dev, &dev_attrs[i]);
 
+       device_unregister(dev->dev);
        usb_put_dev(dev->udev);
 
        dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 detached\n",
-               dev->type & SERVO_COUNT_QUAD ? 4 : 1,
-               dev->type & SERVO_VERSION_30 ? 3 : 2);
+               servo_count, dev->type & SERVO_VERSION_30 ? 3 : 2);
 
+       clear_bit(dev->dev_no, &device_no);
        kfree(dev);
 }
 
index e16582f..a44124c 100644 (file)
@@ -3179,7 +3179,7 @@ sisusb_compat_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
 }
 #endif
 
-static struct file_operations usb_sisusb_fops = {
+static const struct file_operations usb_sisusb_fops = {
        .owner =        THIS_MODULE,
        .open =         sisusb_open,
        .release =      sisusb_release,
diff --git a/drivers/usb/misc/usb_u132.h b/drivers/usb/misc/usb_u132.h
new file mode 100644 (file)
index 0000000..551ba89
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+* Common Header File for the Elan Digital Systems U132 adapter
+* this file should be included by both the "ftdi-u132" and
+* the "u132-hcd" modules.
+*
+* Copyright(C) 2006 Elan Digital Systems Limited
+*(http://www.elandigitalsystems.com)
+*
+* Author and Maintainer - Tony Olech - Elan Digital Systems
+*(tony.olech@elandigitalsystems.com)
+*
+* This program is free software;you can redistribute it and/or
+* modify it under the terms of the GNU General Public License as
+* published by the Free Software Foundation, version 2.
+*
+*
+* The driver was written by Tony Olech(tony.olech@elandigitalsystems.com)
+* based on various USB client drivers in the 2.6.15 linux kernel
+* with constant reference to the 3rd Edition of Linux Device Drivers
+* published by O'Reilly
+*
+* The U132 adapter is a USB to CardBus adapter specifically designed
+* for PC cards that contain an OHCI host controller. Typical PC cards
+* are the Orange Mobile 3G Option GlobeTrotter Fusion card.
+*
+* The U132 adapter will *NOT *work with PC cards that do not contain
+* an OHCI controller. A simple way to test whether a PC card has an
+* OHCI controller as an interface is to insert the PC card directly
+* into a laptop(or desktop) with a CardBus slot and if "lspci" shows
+* a new USB controller and "lsusb -v" shows a new OHCI Host Controller
+* then there is a good chance that the U132 adapter will support the
+* PC card.(you also need the specific client driver for the PC card)
+*
+* Please inform the Author and Maintainer about any PC cards that
+* contain OHCI Host Controller and work when directly connected to
+* an embedded CardBus slot but do not work when they are connected
+* via an ELAN U132 adapter.
+*
+* The driver consists of two modules, the "ftdi-u132" module is
+* a USB client driver that interfaces to the FTDI chip within
+* the U132 adapter manufactured by Elan Digital Systems, and the
+* "u132-hcd" module is a USB host controller driver that talks
+* to the OHCI controller within CardBus card that are inserted
+* in the U132 adapter.
+*
+* The "ftdi-u132" module should be loaded automatically by the
+* hot plug system when the U132 adapter is plugged in. The module
+* initialises the adapter which mostly consists of synchronising
+* the FTDI chip, before continuously polling the adapter to detect
+* PC card insertions. As soon as a PC card containing a recognised
+* OHCI controller is seen the "ftdi-u132" module explicitly requests
+* the kernel to load the "u132-hcd" module.
+*
+* The "ftdi-u132" module provides the interface to the inserted
+* PC card and the "u132-hcd" module uses the API to send and recieve
+* data. The API features call-backs, so that part of the "u132-hcd"
+* module code will run in the context of one of the kernel threads
+* of the "ftdi-u132" module.
+*
+*/
+int ftdi_elan_switch_on_diagnostics(int number);
+void ftdi_elan_gone_away(struct platform_device *pdev);
+void start_usb_lock_device_tracing(void);
+struct u132_platform_data {
+        u16 vendor;
+        u16 device;
+        u8 potpg;
+        void (*port_power) (struct device *dev, int is_on);
+        void (*reset) (struct device *dev);
+};
+int usb_ftdi_elan_edset_single(struct platform_device *pdev, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_output(struct platform_device *pdev, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_empty(struct platform_device *pdev, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_input(struct platform_device *pdev, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_setup(struct platform_device *pdev, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_flush(struct platform_device *pdev, u8 ed_number,
+        void *endp);
index e095772..dbaca9f 100644 (file)
@@ -239,7 +239,7 @@ error:
        return retval;
 }
 
-static struct file_operations lcd_fops = {
+static const struct file_operations lcd_fops = {
         .owner =        THIS_MODULE,
         .read =         lcd_read,
         .write =        lcd_write,
@@ -290,9 +290,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id
                endpoint = &iface_desc->endpoint[i].desc;
 
                if (!dev->bulk_in_endpointAddr &&
-                   (endpoint->bEndpointAddress & USB_DIR_IN) &&
-                   ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-                                       == USB_ENDPOINT_XFER_BULK)) {
+                   usb_endpoint_is_bulk_in(endpoint)) {
                        /* we found a bulk in endpoint */
                        buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
                        dev->bulk_in_size = buffer_size;
@@ -305,9 +303,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id
                }
 
                if (!dev->bulk_out_endpointAddr &&
-                   !(endpoint->bEndpointAddress & USB_DIR_IN) &&
-                   ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-                                       == USB_ENDPOINT_XFER_BULK)) {
+                   usb_endpoint_is_bulk_out(endpoint)) {
                        /* we found a bulk out endpoint */
                        dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
                }
index 0c5ee0a..49c5c5c 100644 (file)
@@ -108,22 +108,34 @@ static int led_probe(struct usb_interface *interface, const struct usb_device_id
        dev = kzalloc(sizeof(struct usb_led), GFP_KERNEL);
        if (dev == NULL) {
                dev_err(&interface->dev, "Out of memory\n");
-               goto error;
+               goto error_mem;
        }
 
        dev->udev = usb_get_dev(udev);
 
        usb_set_intfdata (interface, dev);
 
-       device_create_file(&interface->dev, &dev_attr_blue);
-       device_create_file(&interface->dev, &dev_attr_red);
-       device_create_file(&interface->dev, &dev_attr_green);
+       retval = device_create_file(&interface->dev, &dev_attr_blue);
+       if (retval)
+               goto error;
+       retval = device_create_file(&interface->dev, &dev_attr_red);
+       if (retval)
+               goto error;
+       retval = device_create_file(&interface->dev, &dev_attr_green);
+       if (retval)
+               goto error;
 
        dev_info(&interface->dev, "USB LED device now attached\n");
        return 0;
 
 error:
+       device_remove_file(&interface->dev, &dev_attr_blue);
+       device_remove_file(&interface->dev, &dev_attr_red);
+       device_remove_file(&interface->dev, &dev_attr_green);
+       usb_set_intfdata (interface, NULL);
+       usb_put_dev(dev->udev);
        kfree(dev);
+error_mem:
        return retval;
 }
 
index 275a66f..394bbf2 100644 (file)
@@ -265,7 +265,6 @@ static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus)
        ubus->mon_bus = NULL;
        mbus->u_bus = NULL;
        mb();
-       // usb_bus_put(ubus);
 }
 
 /*
@@ -297,12 +296,12 @@ static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus)
        INIT_LIST_HEAD(&mbus->r_list);
 
        /*
-        * This usb_bus_get here is superfluous, because we receive
-        * a notification if usb_bus is about to be removed.
+        * We don't need to take a reference to ubus, because we receive
+        * a notification if the bus is about to be removed.
         */
-       // usb_bus_get(ubus);
        mbus->u_bus = ubus;
        ubus->mon_bus = mbus;
+       mbus->uses_dma = ubus->uses_dma;
 
        rc = snprintf(name, NAMESZ, "%dt", ubus->busnum);
        if (rc <= 0 || rc >= NAMESZ)
index 86ad2b3..f6d1491 100644 (file)
@@ -62,7 +62,7 @@ static int mon_stat_release(struct inode *inode, struct file *file)
        return 0;
 }
 
-struct file_operations mon_fops_stat = {
+const struct file_operations mon_fops_stat = {
        .owner =        THIS_MODULE,
        .open =         mon_stat_open,
        .llseek =       no_llseek,
index 2fd39b4..7a2346c 100644 (file)
@@ -75,13 +75,13 @@ static void mon_text_ctor(void *, kmem_cache_t *, unsigned long);
  */
 
 static inline char mon_text_get_setup(struct mon_event_text *ep,
-    struct urb *urb, char ev_type)
+    struct urb *urb, char ev_type, struct mon_bus *mbus)
 {
 
        if (!usb_pipecontrol(urb->pipe) || ev_type != 'S')
                return '-';
 
-       if (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)
+       if (mbus->uses_dma && (urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
                return mon_dmapeek(ep->setup, urb->setup_dma, SETUP_MAX);
        if (urb->setup_packet == NULL)
                return 'Z';     /* '0' would be not as pretty. */
@@ -91,7 +91,7 @@ static inline char mon_text_get_setup(struct mon_event_text *ep,
 }
 
 static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
-    int len, char ev_type)
+    int len, char ev_type, struct mon_bus *mbus)
 {
        int pipe = urb->pipe;
 
@@ -117,7 +117,7 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
         * contain non-NULL garbage in case the upper level promised to
         * set DMA for the HCD.
         */
-       if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
+       if (mbus->uses_dma && (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
                return mon_dmapeek(ep->data, urb->transfer_dma, len);
 
        if (urb->transfer_buffer == NULL)
@@ -161,8 +161,9 @@ static void mon_text_event(struct mon_reader_text *rp, struct urb *urb,
        /* Collecting status makes debugging sense for submits, too */
        ep->status = urb->status;
 
-       ep->setup_flag = mon_text_get_setup(ep, urb, ev_type);
-       ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type);
+       ep->setup_flag = mon_text_get_setup(ep, urb, ev_type, rp->r.m_bus);
+       ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type,
+                       rp->r.m_bus);
 
        rp->nevents++;
        list_add_tail(&ep->e_link, &rp->e_list);
@@ -435,7 +436,7 @@ static int mon_text_release(struct inode *inode, struct file *file)
        return 0;
 }
 
-struct file_operations mon_fops_text = {
+const struct file_operations mon_fops_text = {
        .owner =        THIS_MODULE,
        .open =         mon_text_open,
        .llseek =       no_llseek,
index 33678c2..ab9d02d 100644 (file)
@@ -20,6 +20,7 @@ struct mon_bus {
        struct dentry *dent_s;          /* Debugging file */
        struct dentry *dent_t;          /* Text interface file */
        struct usb_bus *u_bus;
+       int uses_dma;
 
        /* Ref */
        int nreaders;                   /* Under mon_lock AND mbus->lock */
@@ -53,7 +54,7 @@ extern char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len);
 
 extern struct mutex mon_lock;
 
-extern struct file_operations mon_fops_text;
-extern struct file_operations mon_fops_stat;
+extern const struct file_operations mon_fops_text;
+extern const struct file_operations mon_fops_stat;
 
 #endif /* __USB_MON_H */
index 2e2bbc0..9b97aa6 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * ASIX AX8817X based USB 2.0 Ethernet Devices
- * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ * Copyright (C) 2003-2006 David Hollis <dhollis@davehollis.com>
  * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net>
+ * Copyright (C) 2006 James Painter <jamie.painter@iname.com>
  * Copyright (c) 2002-2003 TiVo Inc.
  *
  * This program is free software; you can redistribute it and/or modify
@@ -36,6 +37,9 @@
 
 #include "usbnet.h"
 
+#define DRIVER_VERSION "14-Jun-2006"
+static const char driver_name [] = "asix";
+
 /* ASIX AX8817X based USB 2.0 Ethernet Devices */
 
 #define AX_CMD_SET_SW_MII              0x06
 #define AX_CMD_WRITE_EEPROM            0x0c
 #define AX_CMD_WRITE_ENABLE            0x0d
 #define AX_CMD_WRITE_DISABLE           0x0e
+#define AX_CMD_READ_RX_CTL             0x0f
 #define AX_CMD_WRITE_RX_CTL            0x10
 #define AX_CMD_READ_IPG012             0x11
 #define AX_CMD_WRITE_IPG0              0x12
 #define AX_CMD_WRITE_IPG1              0x13
+#define AX_CMD_READ_NODE_ID            0x13
 #define AX_CMD_WRITE_IPG2              0x14
 #define AX_CMD_WRITE_MULTI_FILTER      0x16
-#define AX_CMD_READ_NODE_ID            0x17
+#define AX88172_CMD_READ_NODE_ID       0x17
 #define AX_CMD_READ_PHY_ID             0x19
 #define AX_CMD_READ_MEDIUM_STATUS      0x1a
 #define AX_CMD_WRITE_MEDIUM_MODE       0x1b
 #define AX_CMD_READ_MONITOR_MODE       0x1c
 #define AX_CMD_WRITE_MONITOR_MODE      0x1d
+#define AX_CMD_READ_GPIOS              0x1e
 #define AX_CMD_WRITE_GPIOS             0x1f
 #define AX_CMD_SW_RESET                        0x20
 #define AX_CMD_SW_PHY_STATUS           0x21
 #define AX_CMD_SW_PHY_SELECT           0x22
-#define AX88772_CMD_READ_NODE_ID       0x13
 
 #define AX_MONITOR_MODE                        0x01
 #define AX_MONITOR_LINK                        0x02
 #define AX_MONITOR_HSFS                        0x10
 
 /* AX88172 Medium Status Register values */
-#define AX_MEDIUM_FULL_DUPLEX          0x02
-#define AX_MEDIUM_TX_ABORT_ALLOW       0x04
-#define AX_MEDIUM_FLOW_CONTROL_EN      0x10
+#define AX88172_MEDIUM_FD              0x02
+#define AX88172_MEDIUM_TX              0x04
+#define AX88172_MEDIUM_FC              0x10
+#define AX88172_MEDIUM_DEFAULT \
+               ( AX88172_MEDIUM_FD | AX88172_MEDIUM_TX | AX88172_MEDIUM_FC )
 
 #define AX_MCAST_FILTER_SIZE           8
 #define AX_MAX_MCAST                   64
 
-#define AX_EEPROM_LEN                  0x40
-
 #define AX_SWRESET_CLEAR               0x00
 #define AX_SWRESET_RR                  0x01
 #define AX_SWRESET_RT                  0x02
 #define AX88772_IPG1_DEFAULT           0x0c
 #define AX88772_IPG2_DEFAULT           0x12
 
-#define AX88772_MEDIUM_FULL_DUPLEX     0x0002
-#define AX88772_MEDIUM_RESERVED                0x0004
-#define AX88772_MEDIUM_RX_FC_ENABLE    0x0010
-#define AX88772_MEDIUM_TX_FC_ENABLE    0x0020
-#define AX88772_MEDIUM_PAUSE_FORMAT    0x0080
-#define AX88772_MEDIUM_RX_ENABLE       0x0100
-#define AX88772_MEDIUM_100MB           0x0200
-#define AX88772_MEDIUM_DEFAULT \
-       (AX88772_MEDIUM_FULL_DUPLEX | AX88772_MEDIUM_RX_FC_ENABLE | \
-        AX88772_MEDIUM_TX_FC_ENABLE | AX88772_MEDIUM_100MB | \
-        AX88772_MEDIUM_RESERVED | AX88772_MEDIUM_RX_ENABLE )
+/* AX88772 & AX88178 Medium Mode Register */
+#define AX_MEDIUM_PF           0x0080
+#define AX_MEDIUM_JFE          0x0040
+#define AX_MEDIUM_TFC          0x0020
+#define AX_MEDIUM_RFC          0x0010
+#define AX_MEDIUM_ENCK         0x0008
+#define AX_MEDIUM_AC           0x0004
+#define AX_MEDIUM_FD           0x0002
+#define AX_MEDIUM_GM           0x0001
+#define AX_MEDIUM_SM           0x1000
+#define AX_MEDIUM_SBP          0x0800
+#define AX_MEDIUM_PS           0x0200
+#define AX_MEDIUM_RE           0x0100
+
+#define AX88178_MEDIUM_DEFAULT \
+       (AX_MEDIUM_PS | AX_MEDIUM_FD | AX_MEDIUM_AC | \
+        AX_MEDIUM_RFC | AX_MEDIUM_TFC | AX_MEDIUM_JFE | \
+        AX_MEDIUM_RE )
 
-#define AX_EEPROM_MAGIC                        0xdeadbeef
+#define AX88772_MEDIUM_DEFAULT \
+       (AX_MEDIUM_FD | AX_MEDIUM_RFC | \
+        AX_MEDIUM_TFC | AX_MEDIUM_PS | \
+        AX_MEDIUM_AC | AX_MEDIUM_RE )
+
+/* AX88772 & AX88178 RX_CTL values */
+#define AX_RX_CTL_SO                   0x0080
+#define AX_RX_CTL_AP                   0x0020
+#define AX_RX_CTL_AM                   0x0010
+#define AX_RX_CTL_AB                   0x0008
+#define AX_RX_CTL_SEP                  0x0004
+#define AX_RX_CTL_AMALL                        0x0002
+#define AX_RX_CTL_PRO                  0x0001
+#define AX_RX_CTL_MFB_2048             0x0000
+#define AX_RX_CTL_MFB_4096             0x0100
+#define AX_RX_CTL_MFB_8192             0x0200
+#define AX_RX_CTL_MFB_16384            0x0300
+
+#define AX_DEFAULT_RX_CTL      \
+       (AX_RX_CTL_SO | AX_RX_CTL_AB )
+
+/* GPIO 0 .. 2 toggles */
+#define AX_GPIO_GPO0EN         0x01    /* GPIO0 Output enable */
+#define AX_GPIO_GPO_0          0x02    /* GPIO0 Output value */
+#define AX_GPIO_GPO1EN         0x04    /* GPIO1 Output enable */
+#define AX_GPIO_GPO_1          0x08    /* GPIO1 Output value */
+#define AX_GPIO_GPO2EN         0x10    /* GPIO2 Output enable */
+#define AX_GPIO_GPO_2          0x20    /* GPIO2 Output value */
+#define AX_GPIO_RESERVED       0x40    /* Reserved */
+#define AX_GPIO_RSE            0x80    /* Reload serial EEPROM */
+
+#define AX_EEPROM_MAGIC                0xdeadbeef
+#define AX88172_EEPROM_LEN     0x40
+#define AX88772_EEPROM_LEN     0xff
+
+#define PHY_MODE_MARVELL       0x0000
+#define MII_MARVELL_LED_CTRL   0x0018
+#define MII_MARVELL_STATUS     0x001b
+#define MII_MARVELL_CTRL       0x0014
+
+#define MARVELL_LED_MANUAL     0x0019
+
+#define MARVELL_STATUS_HWCFG   0x0004
+
+#define MARVELL_CTRL_TXDELAY   0x0002
+#define MARVELL_CTRL_RXDELAY   0x0080
 
 /* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */
 struct asix_data {
        u8 multi_filter[AX_MCAST_FILTER_SIZE];
+       u8 phymode;
+       u8 ledmode;
+       u8 eeprom_len;
 };
 
 struct ax88172_int_data {
@@ -122,6 +183,8 @@ struct ax88172_int_data {
 static int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
                            u16 size, void *data)
 {
+       devdbg(dev,"asix_read_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d",
+               cmd, value, index, size);
        return usb_control_msg(
                dev->udev,
                usb_rcvctrlpipe(dev->udev, 0),
@@ -137,6 +200,8 @@ static int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
 static int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
                             u16 size, void *data)
 {
+       devdbg(dev,"asix_write_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d",
+               cmd, value, index, size);
        return usb_control_msg(
                dev->udev,
                usb_sndctrlpipe(dev->udev, 0),
@@ -161,12 +226,167 @@ static void asix_async_cmd_callback(struct urb *urb, struct pt_regs *regs)
        usb_free_urb(urb);
 }
 
+static void
+asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+                                   u16 size, void *data)
+{
+       struct usb_ctrlrequest *req;
+       int status;
+       struct urb *urb;
+
+       devdbg(dev,"asix_write_cmd_async() cmd=0x%02x value=0x%04x index=0x%04x size=%d",
+               cmd, value, index, size);
+       if ((urb = usb_alloc_urb(0, GFP_ATOMIC)) == NULL) {
+               deverr(dev, "Error allocating URB in write_cmd_async!");
+               return;
+       }
+
+       if ((req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC)) == NULL) {
+               deverr(dev, "Failed to allocate memory for control request");
+               usb_free_urb(urb);
+               return;
+       }
+
+       req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+       req->bRequest = cmd;
+       req->wValue = value;
+       req->wIndex = index;
+       req->wLength = size;
+
+       usb_fill_control_urb(urb, dev->udev,
+                            usb_sndctrlpipe(dev->udev, 0),
+                            (void *)req, data, size,
+                            asix_async_cmd_callback, req);
+
+       if((status = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+               deverr(dev, "Error submitting the control message: status=%d",
+                               status);
+               kfree(req);
+               usb_free_urb(urb);
+       }
+}
+
+static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+       u8  *head;
+       u32  header;
+       char *packet;
+       struct sk_buff *ax_skb;
+       u16 size;
+
+       head = (u8 *) skb->data;
+       memcpy(&header, head, sizeof(header));
+       le32_to_cpus(&header);
+       packet = head + sizeof(header);
+
+       skb_pull(skb, 4);
+
+       while (skb->len > 0) {
+               if ((short)(header & 0x0000ffff) !=
+                   ~((short)((header & 0xffff0000) >> 16))) {
+                       deverr(dev,"asix_rx_fixup() Bad Header Length");
+               }
+               /* get the packet length */
+               size = (u16) (header & 0x0000ffff);
+
+               if ((skb->len) - ((size + 1) & 0xfffe) == 0)
+                       return 2;
+               if (size > ETH_FRAME_LEN) {
+                       deverr(dev,"asix_rx_fixup() Bad RX Length %d", size);
+                       return 0;
+               }
+               ax_skb = skb_clone(skb, GFP_ATOMIC);
+               if (ax_skb) {
+                       ax_skb->len = size;
+                       ax_skb->data = packet;
+                       ax_skb->tail = packet + size;
+                       usbnet_skb_return(dev, ax_skb);
+               } else {
+                       return 0;
+               }
+
+               skb_pull(skb, (size + 1) & 0xfffe);
+
+               if (skb->len == 0)
+                       break;
+
+               head = (u8 *) skb->data;
+               memcpy(&header, head, sizeof(header));
+               le32_to_cpus(&header);
+               packet = head + sizeof(header);
+               skb_pull(skb, 4);
+       }
+
+       if (skb->len < 0) {
+               deverr(dev,"asix_rx_fixup() Bad SKB Length %d", skb->len);
+               return 0;
+       }
+       return 1;
+}
+
+static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
+                                       gfp_t flags)
+{
+       int padlen;
+       int headroom = skb_headroom(skb);
+       int tailroom = skb_tailroom(skb);
+       u32 packet_len;
+       u32 padbytes = 0xffff0000;
+
+       padlen = ((skb->len + 4) % 512) ? 0 : 4;
+
+       if ((!skb_cloned(skb))
+           && ((headroom + tailroom) >= (4 + padlen))) {
+               if ((headroom < 4) || (tailroom < padlen)) {
+                       skb->data = memmove(skb->head + 4, skb->data, skb->len);
+                       skb->tail = skb->data + skb->len;
+               }
+       } else {
+               struct sk_buff *skb2;
+               skb2 = skb_copy_expand(skb, 4, padlen, flags);
+               dev_kfree_skb_any(skb);
+               skb = skb2;
+               if (!skb)
+                       return NULL;
+       }
+
+       skb_push(skb, 4);
+       packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4);
+       memcpy(skb->data, &packet_len, sizeof(packet_len));
+
+       if ((skb->len % 512) == 0) {
+               memcpy( skb->tail, &padbytes, sizeof(padbytes));
+               skb_put(skb, sizeof(padbytes));
+       }
+       return skb;
+}
+
+static void asix_status(struct usbnet *dev, struct urb *urb)
+{
+       struct ax88172_int_data *event;
+       int link;
+
+       if (urb->actual_length < 8)
+               return;
+
+       event = urb->transfer_buffer;
+       link = event->link & 0x01;
+       if (netif_carrier_ok(dev->net) != link) {
+               if (link) {
+                       netif_carrier_on(dev->net);
+                       usbnet_defer_kevent (dev, EVENT_LINK_RESET );
+               } else
+                       netif_carrier_off(dev->net);
+               devdbg(dev, "Link Status is: %d", link);
+       }
+}
+
 static inline int asix_set_sw_mii(struct usbnet *dev)
 {
        int ret;
        ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL);
        if (ret < 0)
-               devdbg(dev, "Failed to enable software MII access");
+               deverr(dev, "Failed to enable software MII access");
        return ret;
 }
 
@@ -175,24 +395,27 @@ static inline int asix_set_hw_mii(struct usbnet *dev)
        int ret;
        ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL);
        if (ret < 0)
-               devdbg(dev, "Failed to enable hardware MII access");
+               deverr(dev, "Failed to enable hardware MII access");
        return ret;
 }
 
-static inline int asix_get_phyid(struct usbnet *dev)
+static inline int asix_get_phy_addr(struct usbnet *dev)
 {
        int ret = 0;
        void *buf;
 
+       devdbg(dev, "asix_get_phy_addr()");
+
        buf = kmalloc(2, GFP_KERNEL);
        if (!buf)
                goto out1;
 
        if ((ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID,
                                    0, 0, 2, buf)) < 2) {
-               devdbg(dev, "Error reading PHYID register: %02x", ret);
+               deverr(dev, "Error reading PHYID register: %02x", ret);
                goto out2;
        }
+       devdbg(dev, "asix_get_phy_addr() returning 0x%04x", *((u16 *)buf));
        ret = *((u8 *)buf + 1);
 out2:
        kfree(buf);
@@ -206,8 +429,29 @@ static int asix_sw_reset(struct usbnet *dev, u8 flags)
 
         ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL);
        if (ret < 0)
-               devdbg(dev,"Failed to send software reset: %02x", ret);
+               deverr(dev,"Failed to send software reset: %02x", ret);
+
+       return ret;
+}
 
+static u16 asix_read_rx_ctl(struct usbnet *dev)
+{
+       u16 ret = 0;
+       void *buf;
+
+       buf = kmalloc(2, GFP_KERNEL);
+       if (!buf)
+               goto out1;
+
+       if ((ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL,
+                                   0, 0, 2, buf)) < 2) {
+               deverr(dev, "Error reading RX_CTL register: %02x", ret);
+               goto out2;
+       }
+       ret = le16_to_cpu(*((u16 *)buf));
+out2:
+       kfree(buf);
+out1:
        return ret;
 }
 
@@ -215,82 +459,79 @@ static int asix_write_rx_ctl(struct usbnet *dev, u16 mode)
 {
        int ret;
 
+       devdbg(dev,"asix_write_rx_ctl() - mode = 0x%04x", mode);
        ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL);
        if (ret < 0)
-                devdbg(dev, "Failed to write RX_CTL mode: %02x", ret);
+               deverr(dev, "Failed to write RX_CTL mode to 0x%04x: %02x",
+                      mode, ret);
 
        return ret;
 }
 
-static void asix_status(struct usbnet *dev, struct urb *urb)
+static u16 asix_read_medium_status(struct usbnet *dev)
 {
-       struct ax88172_int_data *event;
-       int link;
+       u16 ret = 0;
+       void *buf;
 
-       if (urb->actual_length < 8)
-               return;
+       buf = kmalloc(2, GFP_KERNEL);
+       if (!buf)
+               goto out1;
 
-       event = urb->transfer_buffer;
-       link = event->link & 0x01;
-       if (netif_carrier_ok(dev->net) != link) {
-               if (link) {
-                       netif_carrier_on(dev->net);
-                       usbnet_defer_kevent (dev, EVENT_LINK_RESET );
-               } else
-                       netif_carrier_off(dev->net);
-               devdbg(dev, "Link Status is: %d", link);
+       if ((ret = asix_read_cmd(dev, AX_CMD_READ_MEDIUM_STATUS,
+                                   0, 0, 2, buf)) < 2) {
+               deverr(dev, "Error reading Medium Status register: %02x", ret);
+               goto out2;
        }
+       ret = le16_to_cpu(*((u16 *)buf));
+out2:
+       kfree(buf);
+out1:
+       return ret;
 }
 
-static void
-asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
-                                   u16 size, void *data)
+static int asix_write_medium_mode(struct usbnet *dev, u16 mode)
 {
-       struct usb_ctrlrequest *req;
-       int status;
-       struct urb *urb;
+       int ret;
 
-       if ((urb = usb_alloc_urb(0, GFP_ATOMIC)) == NULL) {
-               devdbg(dev, "Error allocating URB in write_cmd_async!");
-               return;
-       }
+       devdbg(dev,"asix_write_medium_mode() - mode = 0x%04x", mode);
+       ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
+       if (ret < 0)
+               deverr(dev, "Failed to write Medium Mode mode to 0x%04x: %02x",
+                       mode, ret);
 
-       if ((req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC)) == NULL) {
-               deverr(dev, "Failed to allocate memory for control request");
-               usb_free_urb(urb);
-               return;
-       }
+       return ret;
+}
 
-       req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
-       req->bRequest = cmd;
-       req->wValue = cpu_to_le16(value);
-       req->wIndex = cpu_to_le16(index);
-       req->wLength = cpu_to_le16(size);
+static int asix_write_gpio(struct usbnet *dev, u16 value, int sleep)
+{
+       int ret;
 
-       usb_fill_control_urb(urb, dev->udev,
-                            usb_sndctrlpipe(dev->udev, 0),
-                            (void *)req, data, size,
-                            asix_async_cmd_callback, req);
+       devdbg(dev,"asix_write_gpio() - value = 0x%04x", value);
+       ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL);
+       if (ret < 0)
+               deverr(dev, "Failed to write GPIO value 0x%04x: %02x",
+                       value, ret);
 
-       if((status = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
-               deverr(dev, "Error submitting the control message: status=%d",
-                               status);
-               kfree(req);
-               usb_free_urb(urb);
-       }
+       if (sleep)
+               msleep(sleep);
+
+       return ret;
 }
 
+/*
+ * AX88772 & AX88178 have a 16-bit RX_CTL value
+ */
 static void asix_set_multicast(struct net_device *net)
 {
        struct usbnet *dev = netdev_priv(net);
        struct asix_data *data = (struct asix_data *)&dev->data;
-       u8 rx_ctl = 0x8c;
+       u16 rx_ctl = AX_DEFAULT_RX_CTL;
 
        if (net->flags & IFF_PROMISC) {
-               rx_ctl |= 0x01;
+               rx_ctl |= AX_RX_CTL_PRO;
        } else if (net->flags & IFF_ALLMULTI
                   || net->mc_count > AX_MAX_MCAST) {
-               rx_ctl |= 0x02;
+               rx_ctl |= AX_RX_CTL_AMALL;
        } else if (net->mc_count == 0) {
                /* just broadcast and directed */
        } else {
@@ -317,7 +558,7 @@ static void asix_set_multicast(struct net_device *net)
                asix_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
                                   AX_MCAST_FILTER_SIZE, data->multi_filter);
 
-               rx_ctl |= 0x10;
+               rx_ctl |= AX_RX_CTL_AM;
        }
 
        asix_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL);
@@ -333,50 +574,43 @@ static int asix_mdio_read(struct net_device *netdev, int phy_id, int loc)
                                (__u16)loc, 2, (u16 *)&res);
        asix_set_hw_mii(dev);
 
-       return res & 0xffff;
-}
+       devdbg(dev, "asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x", phy_id, loc, le16_to_cpu(res & 0xffff));
 
-/* same as above, but converts resulting value to cpu byte order */
-static int asix_mdio_read_le(struct net_device *netdev, int phy_id, int loc)
-{
-       return le16_to_cpu(asix_mdio_read(netdev,phy_id, loc));
+       return le16_to_cpu(res & 0xffff);
 }
 
 static void
 asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
 {
        struct usbnet *dev = netdev_priv(netdev);
-       u16 res = val;
+       u16 res = cpu_to_le16(val);
 
+       devdbg(dev, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x", phy_id, loc, val);
        asix_set_sw_mii(dev);
        asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
                                (__u16)loc, 2, (u16 *)&res);
        asix_set_hw_mii(dev);
 }
 
-/* same as above, but converts new value to le16 byte order before writing */
-static void
-asix_mdio_write_le(struct net_device *netdev, int phy_id, int loc, int val)
+/* Get the PHY Identifier from the PHYSID1 & PHYSID2 MII registers */
+static u32 asix_get_phyid(struct usbnet *dev)
 {
-       asix_mdio_write( netdev, phy_id, loc, cpu_to_le16(val) );
-}
+       int phy_reg;
+       u32 phy_id;
 
-static int ax88172_link_reset(struct usbnet *dev)
-{
-       u16 lpa;
-       u16 adv;
-       u16 res;
-       u8 mode;
+       phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID1);
+       if (phy_reg < 0)
+               return 0;
 
-       mode = AX_MEDIUM_TX_ABORT_ALLOW | AX_MEDIUM_FLOW_CONTROL_EN;
-       lpa = asix_mdio_read_le(dev->net, dev->mii.phy_id, MII_LPA);
-       adv = asix_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
-       res = mii_nway_result(lpa|adv);
-       if (res & LPA_DUPLEX)
-               mode |= AX_MEDIUM_FULL_DUPLEX;
-       asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
+       phy_id = (phy_reg & 0xffff) << 16;
 
-       return 0;
+       phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID2);
+       if (phy_reg < 0)
+               return 0;
+
+       phy_id |= (phy_reg & 0xffff);
+
+       return phy_id;
 }
 
 static void
@@ -423,7 +657,10 @@ asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
 
 static int asix_get_eeprom_len(struct net_device *net)
 {
-       return AX_EEPROM_LEN;
+       struct usbnet *dev = netdev_priv(net);
+       struct asix_data *data = (struct asix_data *)&dev->data;
+
+       return data->eeprom_len;
 }
 
 static int asix_get_eeprom(struct net_device *net,
@@ -453,9 +690,14 @@ static int asix_get_eeprom(struct net_device *net,
 static void asix_get_drvinfo (struct net_device *net,
                                 struct ethtool_drvinfo *info)
 {
+       struct usbnet *dev = netdev_priv(net);
+       struct asix_data *data = (struct asix_data *)&dev->data;
+
        /* Inherit standard device info */
        usbnet_get_drvinfo(net, info);
-       info->eedump_len = 0x3e;
+       strncpy (info->driver, driver_name, sizeof info->driver);
+       strncpy (info->version, DRIVER_VERSION, sizeof info->version);
+       info->eedump_len = data->eeprom_len;
 }
 
 static int asix_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
@@ -468,17 +710,44 @@ static int asix_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
 static int asix_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
 {
        struct usbnet *dev = netdev_priv(net);
+       int res = mii_ethtool_sset(&dev->mii,cmd);
+
+       /* link speed/duplex might have changed */
+       if (dev->driver_info->link_reset)
+               dev->driver_info->link_reset(dev);
 
-       return mii_ethtool_sset(&dev->mii,cmd);
+       return res;
 }
 
-/* We need to override some ethtool_ops so we require our
-   own structure so we don't interfere with other usbnet
-   devices that may be connected at the same time. */
-static struct ethtool_ops ax88172_ethtool_ops = {
-       .get_drvinfo            = asix_get_drvinfo,
-       .get_link               = ethtool_op_get_link,
-       .get_msglevel           = usbnet_get_msglevel,
+static int asix_nway_reset(struct net_device *net)
+{
+       struct usbnet *dev = netdev_priv(net);
+
+       return mii_nway_restart(&dev->mii);
+}
+
+static u32 asix_get_link(struct net_device *net)
+{
+       struct usbnet *dev = netdev_priv(net);
+
+       return mii_link_ok(&dev->mii);
+}
+
+static int asix_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
+{
+       struct usbnet *dev = netdev_priv(net);
+
+       return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
+}
+
+/* We need to override some ethtool_ops so we require our
+   own structure so we don't interfere with other usbnet
+   devices that may be connected at the same time. */
+static struct ethtool_ops ax88172_ethtool_ops = {
+       .get_drvinfo            = asix_get_drvinfo,
+       .get_link               = asix_get_link,
+       .nway_reset             = asix_nway_reset,
+       .get_msglevel           = usbnet_get_msglevel,
        .set_msglevel           = usbnet_set_msglevel,
        .get_wol                = asix_get_wol,
        .set_wol                = asix_set_wol,
@@ -488,11 +757,66 @@ static struct ethtool_ops ax88172_ethtool_ops = {
        .set_settings           = asix_set_settings,
 };
 
-static int asix_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
+static void ax88172_set_multicast(struct net_device *net)
 {
        struct usbnet *dev = netdev_priv(net);
+       struct asix_data *data = (struct asix_data *)&dev->data;
+       u8 rx_ctl = 0x8c;
 
-       return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
+       if (net->flags & IFF_PROMISC) {
+               rx_ctl |= 0x01;
+       } else if (net->flags & IFF_ALLMULTI
+                  || net->mc_count > AX_MAX_MCAST) {
+               rx_ctl |= 0x02;
+       } else if (net->mc_count == 0) {
+               /* just broadcast and directed */
+       } else {
+               /* We use the 20 byte dev->data
+                * for our 8 byte filter buffer
+                * to avoid allocating memory that
+                * is tricky to free later */
+               struct dev_mc_list *mc_list = net->mc_list;
+               u32 crc_bits;
+               int i;
+
+               memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE);
+
+               /* Build the multicast hash filter. */
+               for (i = 0; i < net->mc_count; i++) {
+                       crc_bits =
+                           ether_crc(ETH_ALEN,
+                                     mc_list->dmi_addr) >> 26;
+                       data->multi_filter[crc_bits >> 3] |=
+                           1 << (crc_bits & 7);
+                       mc_list = mc_list->next;
+               }
+
+               asix_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
+                                  AX_MCAST_FILTER_SIZE, data->multi_filter);
+
+               rx_ctl |= 0x10;
+       }
+
+       asix_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL);
+}
+
+static int ax88172_link_reset(struct usbnet *dev)
+{
+       u8 mode;
+       struct ethtool_cmd ecmd;
+
+       mii_check_media(&dev->mii, 1, 1);
+       mii_ethtool_gset(&dev->mii, &ecmd);
+       mode = AX88172_MEDIUM_DEFAULT;
+
+       if (ecmd.duplex != DUPLEX_FULL)
+               mode |= ~AX88172_MEDIUM_FD;
+
+       devdbg(dev, "ax88172_link_reset() speed: %d duplex: %d setting mode to 0x%04x", ecmd.speed, ecmd.duplex, mode);
+
+       asix_write_medium_mode(dev, mode);
+
+       return 0;
 }
 
 static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
@@ -501,6 +825,9 @@ static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
        void *buf;
        int i;
        unsigned long gpio_bits = dev->driver_info->data;
+       struct asix_data *data = (struct asix_data *)&dev->data;
+
+       data->eeprom_len = AX88172_EEPROM_LEN;
 
        usbnet_get_endpoints(dev,intf);
 
@@ -519,12 +846,12 @@ static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
                msleep(5);
        }
 
-       if ((ret = asix_write_rx_ctl(dev,0x80)) < 0)
+       if ((ret = asix_write_rx_ctl(dev, 0x80)) < 0)
                goto out2;
 
        /* Get the MAC address */
        memset(buf, 0, ETH_ALEN);
-       if ((ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID,
+       if ((ret = asix_read_cmd(dev, AX88172_CMD_READ_NODE_ID,
                                0, 0, 6, buf)) < 0) {
                dbg("read AX_CMD_READ_NODE_ID failed: %d", ret);
                goto out2;
@@ -537,14 +864,14 @@ static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
        dev->mii.mdio_write = asix_mdio_write;
        dev->mii.phy_id_mask = 0x3f;
        dev->mii.reg_num_mask = 0x1f;
-       dev->mii.phy_id = asix_get_phyid(dev);
+       dev->mii.phy_id = asix_get_phy_addr(dev);
        dev->net->do_ioctl = asix_ioctl;
 
-       dev->net->set_multicast_list = asix_set_multicast;
+       dev->net->set_multicast_list = ax88172_set_multicast;
        dev->net->ethtool_ops = &ax88172_ethtool_ops;
 
-       asix_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
-       asix_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+       asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+       asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
                ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
        mii_nway_restart(&dev->mii);
 
@@ -557,7 +884,8 @@ out1:
 
 static struct ethtool_ops ax88772_ethtool_ops = {
        .get_drvinfo            = asix_get_drvinfo,
-       .get_link               = ethtool_op_get_link,
+       .get_link               = asix_get_link,
+       .nway_reset             = asix_nway_reset,
        .get_msglevel           = usbnet_get_msglevel,
        .set_msglevel           = usbnet_set_msglevel,
        .get_wol                = asix_get_wol,
@@ -568,10 +896,37 @@ static struct ethtool_ops ax88772_ethtool_ops = {
        .set_settings           = asix_set_settings,
 };
 
+static int ax88772_link_reset(struct usbnet *dev)
+{
+       u16 mode;
+       struct ethtool_cmd ecmd;
+
+       mii_check_media(&dev->mii, 1, 1);
+       mii_ethtool_gset(&dev->mii, &ecmd);
+       mode = AX88772_MEDIUM_DEFAULT;
+
+       if (ecmd.speed != SPEED_100)
+               mode &= ~AX_MEDIUM_PS;
+
+       if (ecmd.duplex != DUPLEX_FULL)
+               mode &= ~AX_MEDIUM_FD;
+
+       devdbg(dev, "ax88772_link_reset() speed: %d duplex: %d setting mode to 0x%04x", ecmd.speed, ecmd.duplex, mode);
+
+       asix_write_medium_mode(dev, mode);
+
+       return 0;
+}
+
 static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
 {
        int ret;
        void *buf;
+       u16 rx_ctl;
+       struct asix_data *data = (struct asix_data *)&dev->data;
+       u32 phyid;
+
+       data->eeprom_len = AX88772_EEPROM_LEN;
 
        usbnet_get_endpoints(dev,intf);
 
@@ -582,13 +937,12 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
                goto out1;
        }
 
-       if ((ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS,
-                                    0x00B0, 0, 0, buf)) < 0)
+       if ((ret = asix_write_gpio(dev,
+                       AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5)) < 0)
                goto out2;
 
-       msleep(5);
        if ((ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT,
-                               0x0001, 0, 0, buf)) < 0) {
+                               0x0000, 0, 0, buf)) < 0) {
                dbg("Select PHY #1 failed: %d", ret);
                goto out2;
        }
@@ -605,36 +959,34 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
                goto out2;
 
        msleep(150);
-       if ((ret = asix_write_rx_ctl(dev, 0x00)) < 0)
+       rx_ctl = asix_read_rx_ctl(dev);
+       dbg("RX_CTL is 0x%04x after software reset", rx_ctl);
+       if ((ret = asix_write_rx_ctl(dev, 0x0000)) < 0)
                goto out2;
 
+       rx_ctl = asix_read_rx_ctl(dev);
+       dbg("RX_CTL is 0x%04x setting to 0x0000", rx_ctl);
+
        /* Get the MAC address */
        memset(buf, 0, ETH_ALEN);
-       if ((ret = asix_read_cmd(dev, AX88772_CMD_READ_NODE_ID,
+       if ((ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID,
                                0, 0, ETH_ALEN, buf)) < 0) {
                dbg("Failed to read MAC address: %d", ret);
                goto out2;
        }
        memcpy(dev->net->dev_addr, buf, ETH_ALEN);
 
-       if ((ret = asix_set_sw_mii(dev)) < 0)
-               goto out2;
-
-       if (((ret = asix_read_cmd(dev, AX_CMD_READ_MII_REG,
-                               0x0010, 2, 2, buf)) < 0)
-                       || (*((u16 *)buf) != 0x003b)) {
-               dbg("Read PHY register 2 must be 0x3b00: %d", ret);
-               goto out2;
-       }
-
        /* Initialize MII structure */
        dev->mii.dev = dev->net;
        dev->mii.mdio_read = asix_mdio_read;
        dev->mii.mdio_write = asix_mdio_write;
-       dev->mii.phy_id_mask = 0xff;
-       dev->mii.reg_num_mask = 0xff;
+       dev->mii.phy_id_mask = 0x1f;
+       dev->mii.reg_num_mask = 0x1f;
        dev->net->do_ioctl = asix_ioctl;
-       dev->mii.phy_id = asix_get_phyid(dev);
+       dev->mii.phy_id = asix_get_phy_addr(dev);
+
+       phyid = asix_get_phyid(dev);
+       dbg("PHYID=0x%08x", phyid);
 
        if ((ret = asix_sw_reset(dev, AX_SWRESET_PRL)) < 0)
                goto out2;
@@ -649,16 +1001,13 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
        dev->net->set_multicast_list = asix_set_multicast;
        dev->net->ethtool_ops = &ax88772_ethtool_ops;
 
-       asix_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
-       asix_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+       asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+       asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
                        ADVERTISE_ALL | ADVERTISE_CSMA);
        mii_nway_restart(&dev->mii);
 
-       if ((ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
-                               AX88772_MEDIUM_DEFAULT, 0, 0, buf)) < 0) {
-               dbg("Write medium mode register: %d", ret);
+       if ((ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT)) < 0)
                goto out2;
-       }
 
        if ((ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
                                AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
@@ -666,13 +1015,17 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
                dbg("Write IPG,IPG1,IPG2 failed: %d", ret);
                goto out2;
        }
-       if ((ret = asix_set_hw_mii(dev)) < 0)
-               goto out2;
 
        /* Set RX_CTL to default values with 2k buffer, and enable cactus */
-       if ((ret = asix_write_rx_ctl(dev, 0x0088)) < 0)
+       if ((ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL)) < 0)
                goto out2;
 
+       rx_ctl = asix_read_rx_ctl(dev);
+       dbg("RX_CTL is 0x%04x after all initializations", rx_ctl);
+
+       rx_ctl = asix_read_medium_status(dev);
+       dbg("Medium Status is 0x%04x after all initializations", rx_ctl);
+
        kfree(buf);
 
        /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
@@ -690,120 +1043,285 @@ out1:
        return ret;
 }
 
-static int ax88772_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+static struct ethtool_ops ax88178_ethtool_ops = {
+       .get_drvinfo            = asix_get_drvinfo,
+       .get_link               = asix_get_link,
+       .nway_reset             = asix_nway_reset,
+       .get_msglevel           = usbnet_get_msglevel,
+       .set_msglevel           = usbnet_set_msglevel,
+       .get_wol                = asix_get_wol,
+       .set_wol                = asix_set_wol,
+       .get_eeprom_len         = asix_get_eeprom_len,
+       .get_eeprom             = asix_get_eeprom,
+       .get_settings           = asix_get_settings,
+       .set_settings           = asix_set_settings,
+};
+
+static int marvell_phy_init(struct usbnet *dev)
 {
-       u8  *head;
-       u32  header;
-       char *packet;
-       struct sk_buff *ax_skb;
-       u16 size;
+       struct asix_data *data = (struct asix_data *)&dev->data;
+       u16 reg;
 
-       head = (u8 *) skb->data;
-       memcpy(&header, head, sizeof(header));
-       le32_to_cpus(&header);
-       packet = head + sizeof(header);
+       devdbg(dev,"marvell_phy_init()");
 
-       skb_pull(skb, 4);
+       reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_MARVELL_STATUS);
+       devdbg(dev,"MII_MARVELL_STATUS = 0x%04x", reg);
 
-       while (skb->len > 0) {
-               if ((short)(header & 0x0000ffff) !=
-                   ~((short)((header & 0xffff0000) >> 16))) {
-                       devdbg(dev,"header length data is error");
-               }
-               /* get the packet length */
-               size = (u16) (header & 0x0000ffff);
+       asix_mdio_write(dev->net, dev->mii.phy_id, MII_MARVELL_CTRL,
+                       MARVELL_CTRL_RXDELAY | MARVELL_CTRL_TXDELAY);
 
-               if ((skb->len) - ((size + 1) & 0xfffe) == 0)
-                       return 2;
-               if (size > ETH_FRAME_LEN) {
-                       devdbg(dev,"invalid rx length %d", size);
-                       return 0;
-               }
-               ax_skb = skb_clone(skb, GFP_ATOMIC);
-               if (ax_skb) {
-                       ax_skb->len = size;
-                       ax_skb->data = packet;
-                       ax_skb->tail = packet + size;
-                       usbnet_skb_return(dev, ax_skb);
-               } else {
-                       return 0;
-               }
+       if (data->ledmode) {
+               reg = asix_mdio_read(dev->net, dev->mii.phy_id,
+                       MII_MARVELL_LED_CTRL);
+               devdbg(dev,"MII_MARVELL_LED_CTRL (1) = 0x%04x", reg);
 
-               skb_pull(skb, (size + 1) & 0xfffe);
+               reg &= 0xf8ff;
+               reg |= (1 + 0x0100);
+               asix_mdio_write(dev->net, dev->mii.phy_id,
+                       MII_MARVELL_LED_CTRL, reg);
 
-               if (skb->len == 0)
-                       break;
+               reg = asix_mdio_read(dev->net, dev->mii.phy_id,
+                       MII_MARVELL_LED_CTRL);
+               devdbg(dev,"MII_MARVELL_LED_CTRL (2) = 0x%04x", reg);
+               reg &= 0xfc0f;
+       }
 
-               head = (u8 *) skb->data;
-               memcpy(&header, head, sizeof(header));
-               le32_to_cpus(&header);
-               packet = head + sizeof(header);
-               skb_pull(skb, 4);
+       return 0;
+}
+
+static int marvell_led_status(struct usbnet *dev, u16 speed)
+{
+       u16 reg = asix_mdio_read(dev->net, dev->mii.phy_id, MARVELL_LED_MANUAL);
+
+       devdbg(dev, "marvell_led_status() read 0x%04x", reg);
+
+       /* Clear out the center LED bits - 0x03F0 */
+       reg &= 0xfc0f;
+
+       switch (speed) {
+               case SPEED_1000:
+                       reg |= 0x03e0;
+                       break;
+               case SPEED_100:
+                       reg |= 0x03b0;
+                       break;
+               default:
+                       reg |= 0x02f0;
        }
 
-       if (skb->len < 0) {
-               devdbg(dev,"invalid rx length %d", skb->len);
-               return 0;
+       devdbg(dev, "marvell_led_status() writing 0x%04x", reg);
+       asix_mdio_write(dev->net, dev->mii.phy_id, MARVELL_LED_MANUAL, reg);
+
+       return 0;
+}
+
+static int ax88178_link_reset(struct usbnet *dev)
+{
+       u16 mode;
+       struct ethtool_cmd ecmd;
+       struct asix_data *data = (struct asix_data *)&dev->data;
+
+       devdbg(dev,"ax88178_link_reset()");
+
+       mii_check_media(&dev->mii, 1, 1);
+       mii_ethtool_gset(&dev->mii, &ecmd);
+       mode = AX88178_MEDIUM_DEFAULT;
+
+       if (ecmd.speed == SPEED_1000)
+               mode |= AX_MEDIUM_GM | AX_MEDIUM_ENCK;
+       else if (ecmd.speed == SPEED_100)
+               mode |= AX_MEDIUM_PS;
+       else
+               mode &= ~(AX_MEDIUM_PS | AX_MEDIUM_GM);
+
+       if (ecmd.duplex == DUPLEX_FULL)
+               mode |= AX_MEDIUM_FD;
+       else
+               mode &= ~AX_MEDIUM_FD;
+
+       devdbg(dev, "ax88178_link_reset() speed: %d duplex: %d setting mode to 0x%04x", ecmd.speed, ecmd.duplex, mode);
+
+       asix_write_medium_mode(dev, mode);
+
+       if (data->phymode == PHY_MODE_MARVELL && data->ledmode)
+               marvell_led_status(dev, ecmd.speed);
+
+       return 0;
+}
+
+static void ax88178_set_mfb(struct usbnet *dev)
+{
+       u16 mfb = AX_RX_CTL_MFB_16384;
+       u16 rxctl;
+       u16 medium;
+       int old_rx_urb_size = dev->rx_urb_size;
+
+       if (dev->hard_mtu < 2048) {
+               dev->rx_urb_size = 2048;
+               mfb = AX_RX_CTL_MFB_2048;
+       } else if (dev->hard_mtu < 4096) {
+               dev->rx_urb_size = 4096;
+               mfb = AX_RX_CTL_MFB_4096;
+       } else if (dev->hard_mtu < 8192) {
+               dev->rx_urb_size = 8192;
+               mfb = AX_RX_CTL_MFB_8192;
+       } else if (dev->hard_mtu < 16384) {
+               dev->rx_urb_size = 16384;
+               mfb = AX_RX_CTL_MFB_16384;
        }
-       return 1;
+
+       rxctl = asix_read_rx_ctl(dev);
+       asix_write_rx_ctl(dev, (rxctl & ~AX_RX_CTL_MFB_16384) | mfb);
+
+       medium = asix_read_medium_status(dev);
+       if (dev->net->mtu > 1500)
+               medium |= AX_MEDIUM_JFE;
+       else
+               medium &= ~AX_MEDIUM_JFE;
+       asix_write_medium_mode(dev, medium);
+
+       if (dev->rx_urb_size > old_rx_urb_size)
+               usbnet_unlink_rx_urbs(dev);
 }
 
-static struct sk_buff *ax88772_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
-                                       gfp_t flags)
+static int ax88178_change_mtu(struct net_device *net, int new_mtu)
 {
-       int padlen;
-       int headroom = skb_headroom(skb);
-       int tailroom = skb_tailroom(skb);
-       u32 packet_len;
-       u32 padbytes = 0xffff0000;
+       struct usbnet *dev = netdev_priv(net);
+       int ll_mtu = new_mtu + net->hard_header_len + 4;
 
-       padlen = ((skb->len + 4) % 512) ? 0 : 4;
+       devdbg(dev, "ax88178_change_mtu() new_mtu=%d", new_mtu);
 
-       if ((!skb_cloned(skb))
-           && ((headroom + tailroom) >= (4 + padlen))) {
-               if ((headroom < 4) || (tailroom < padlen)) {
-                       skb->data = memmove(skb->head + 4, skb->data, skb->len);
-                       skb->tail = skb->data + skb->len;
-               }
+       if (new_mtu <= 0 || ll_mtu > 16384)
+               return -EINVAL;
+
+       if ((ll_mtu % dev->maxpacket) == 0)
+               return -EDOM;
+
+       net->mtu = new_mtu;
+       dev->hard_mtu = net->mtu + net->hard_header_len;
+       ax88178_set_mfb(dev);
+
+       return 0;
+}
+
+static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+       struct asix_data *data = (struct asix_data *)&dev->data;
+       int ret;
+       void *buf;
+       u16 eeprom;
+       int gpio0 = 0;
+       u32 phyid;
+
+       usbnet_get_endpoints(dev,intf);
+
+       buf = kmalloc(6, GFP_KERNEL);
+       if(!buf) {
+               dbg ("Cannot allocate memory for buffer");
+               ret = -ENOMEM;
+               goto out1;
+       }
+
+       eeprom = 0;
+       asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &eeprom);
+       dbg("GPIO Status: 0x%04x", eeprom);
+
+       asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL);
+       asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom);
+       asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL);
+
+       dbg("EEPROM index 0x17 is 0x%04x", eeprom);
+
+       if (eeprom == 0xffff) {
+               data->phymode = PHY_MODE_MARVELL;
+               data->ledmode = 0;
+               gpio0 = 1;
        } else {
-               struct sk_buff *skb2;
-               skb2 = skb_copy_expand(skb, 4, padlen, flags);
-               dev_kfree_skb_any(skb);
-               skb = skb2;
-               if (!skb)
-                       return NULL;
+               data->phymode = eeprom & 7;
+               data->ledmode = eeprom >> 8;
+               gpio0 = (eeprom & 0x80) ? 0 : 1;
        }
+       dbg("GPIO0: %d, PhyMode: %d", gpio0, data->phymode);
 
-       skb_push(skb, 4);
-       packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4);
-       memcpy(skb->data, &packet_len, sizeof(packet_len));
+       asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_1 | AX_GPIO_GPO1EN, 40);
+       if ((eeprom >> 8) != 1) {
+               asix_write_gpio(dev, 0x003c, 30);
+               asix_write_gpio(dev, 0x001c, 300);
+               asix_write_gpio(dev, 0x003c, 30);
+       } else {
+               dbg("gpio phymode == 1 path");
+               asix_write_gpio(dev, AX_GPIO_GPO1EN, 30);
+               asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30);
+       }
 
-       if ((skb->len % 512) == 0) {
-               memcpy( skb->tail, &padbytes, sizeof(padbytes));
-               skb_put(skb, sizeof(padbytes));
+       asix_sw_reset(dev, 0);
+       msleep(150);
+
+       asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD);
+       msleep(150);
+
+       asix_write_rx_ctl(dev, 0);
+
+       /* Get the MAC address */
+       memset(buf, 0, ETH_ALEN);
+       if ((ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID,
+                               0, 0, ETH_ALEN, buf)) < 0) {
+               dbg("Failed to read MAC address: %d", ret);
+               goto out2;
        }
-       return skb;
-}
+       memcpy(dev->net->dev_addr, buf, ETH_ALEN);
 
-static int ax88772_link_reset(struct usbnet *dev)
-{
-       u16 lpa;
-       u16 adv;
-       u16 res;
-       u16 mode;
+       /* Initialize MII structure */
+       dev->mii.dev = dev->net;
+       dev->mii.mdio_read = asix_mdio_read;
+       dev->mii.mdio_write = asix_mdio_write;
+       dev->mii.phy_id_mask = 0x1f;
+       dev->mii.reg_num_mask = 0xff;
+       dev->mii.supports_gmii = 1;
+       dev->net->do_ioctl = asix_ioctl;
+       dev->mii.phy_id = asix_get_phy_addr(dev);
+       dev->net->set_multicast_list = asix_set_multicast;
+       dev->net->ethtool_ops = &ax88178_ethtool_ops;
+       dev->net->change_mtu = &ax88178_change_mtu;
 
-       mode = AX88772_MEDIUM_DEFAULT;
-       lpa = asix_mdio_read_le(dev->net, dev->mii.phy_id, MII_LPA);
-       adv = asix_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
-       res = mii_nway_result(lpa|adv);
+       phyid = asix_get_phyid(dev);
+       dbg("PHYID=0x%08x", phyid);
 
-       if ((res & LPA_DUPLEX) == 0)
-               mode &= ~AX88772_MEDIUM_FULL_DUPLEX;
-       if ((res & LPA_100) == 0)
-               mode &= ~AX88772_MEDIUM_100MB;
-       asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
+       if (data->phymode == PHY_MODE_MARVELL) {
+               marvell_phy_init(dev);
+               msleep(60);
+       }
+
+       asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR,
+                       BMCR_RESET | BMCR_ANENABLE);
+       asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+                       ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
+       asix_mdio_write(dev->net, dev->mii.phy_id, MII_CTRL1000,
+                       ADVERTISE_1000FULL);
+
+       mii_nway_restart(&dev->mii);
+
+       if ((ret = asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT)) < 0)
+               goto out2;
+
+       if ((ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL)) < 0)
+               goto out2;
+
+       kfree(buf);
+
+       /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
+       if (dev->driver_info->flags & FLAG_FRAMING_AX) {
+               /* hard_mtu  is still the default - the device does not support
+                  jumbo eth frames */
+               dev->rx_urb_size = 2048;
+       }
 
        return 0;
+
+out2:
+       kfree(buf);
+out1:
+       return ret;
 }
 
 static const struct driver_info ax8817x_info = {
@@ -853,8 +1371,19 @@ static const struct driver_info ax88772_info = {
        .link_reset = ax88772_link_reset,
        .reset = ax88772_link_reset,
        .flags = FLAG_ETHER | FLAG_FRAMING_AX,
-       .rx_fixup = ax88772_rx_fixup,
-       .tx_fixup = ax88772_tx_fixup,
+       .rx_fixup = asix_rx_fixup,
+       .tx_fixup = asix_tx_fixup,
+};
+
+static const struct driver_info ax88178_info = {
+       .description = "ASIX AX88178 USB 2.0 Ethernet",
+       .bind = ax88178_bind,
+       .status = asix_status,
+       .link_reset = ax88178_link_reset,
+       .reset = ax88178_link_reset,
+       .flags = FLAG_ETHER | FLAG_FRAMING_AX,
+       .rx_fixup = asix_rx_fixup,
+       .tx_fixup = asix_tx_fixup,
 };
 
 static const struct usb_device_id      products [] = {
@@ -913,7 +1442,7 @@ static const struct usb_device_id  products [] = {
 }, {
        // ASIX AX88178 10/100/1000
        USB_DEVICE (0x0b95, 0x1780),
-       .driver_info = (unsigned long) &ax88772_info,
+       .driver_info = (unsigned long) &ax88178_info,
 }, {
        // Linksys USB200M Rev 2
        USB_DEVICE (0x13b1, 0x0018),
@@ -922,6 +1451,14 @@ static const struct usb_device_id products [] = {
        // 0Q0 cable ethernet
        USB_DEVICE (0x1557, 0x7720),
        .driver_info = (unsigned long) &ax88772_info,
+}, {
+       // DLink DUB-E100 H/W Ver B1
+       USB_DEVICE (0x07d1, 0x3c05),
+       .driver_info = (unsigned long) &ax88772_info,
+}, {
+       // Linksys USB1000
+       USB_DEVICE (0x1737, 0x0039),
+       .driver_info = (unsigned long) &ax88178_info,
 },
        { },            // END
 };
index a9b6eea..301baa7 100644 (file)
@@ -498,25 +498,24 @@ static int net1080_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
 static struct sk_buff *
 net1080_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
 {
-       int                     padlen;
        struct sk_buff          *skb2;
        struct nc_header        *header = NULL;
        struct nc_trailer       *trailer = NULL;
+       int                     padlen = sizeof (struct nc_trailer);
        int                     len = skb->len;
 
-       padlen = ((len + sizeof (struct nc_header)
-                       + sizeof (struct nc_trailer)) & 0x01) ? 0 : 1;
+       if (!((len + padlen + sizeof (struct nc_header)) & 0x01))
+               padlen++;
        if (!skb_cloned(skb)) {
                int     headroom = skb_headroom(skb);
                int     tailroom = skb_tailroom(skb);
 
-               if ((padlen + sizeof (struct nc_trailer)) <= tailroom
-                           && sizeof (struct nc_header) <= headroom)
+               if (padlen <= tailroom &&
+                   sizeof(struct nc_header) <= headroom)
                        /* There's enough head and tail room */
                        goto encapsulate;
 
-               if ((sizeof (struct nc_header) + padlen
-                                       + sizeof (struct nc_trailer)) <
+               if ((sizeof (struct nc_header) + padlen) <
                                (headroom + tailroom)) {
                        /* There's enough total room, so just readjust */
                        skb->data = memmove(skb->head
@@ -530,7 +529,7 @@ net1080_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
        /* Create a new skb to use with the correct size */
        skb2 = skb_copy_expand(skb,
                                sizeof (struct nc_header),
-                               sizeof (struct nc_trailer) + padlen,
+                               padlen,
                                flags);
        dev_kfree_skb_any(skb);
        if (!skb2)
index ab21f96..b8e25af 100644 (file)
@@ -619,7 +619,7 @@ static void read_bulk_callback(struct urb *urb, struct pt_regs *regs)
        switch (urb->status) {
        case 0:
                break;
-       case -ETIMEDOUT:
+       case -ETIME:
                if (netif_msg_rx_err(pegasus))
                        pr_debug("%s: reset MAC\n", net->name);
                pegasus->flags &= ~PEGASUS_RX_BUSY;
index a72685b..2364c20 100644 (file)
@@ -438,7 +438,7 @@ static void read_bulk_callback(struct urb *urb, struct pt_regs *regs)
                break;
        case -ENOENT:
                return; /* the urb is in unlink state */
-       case -ETIMEDOUT:
+       case -ETIME:
                warn("may be reset is needed?..");
                goto goon;
        default:
index 54183e1..98a522f 100644 (file)
  * let the USB host controller be busy for 5msec or more before an irq
  * is required, under load.  Jumbograms change the equation.
  */
-#define        RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)
-#define        TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)
+#define RX_MAX_QUEUE_MEMORY (60 * 1518)
+#define        RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \
+                       (RX_MAX_QUEUE_MEMORY/(dev)->rx_urb_size) : 4)
+#define        TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \
+                       (RX_MAX_QUEUE_MEMORY/(dev)->hard_mtu) : 4)
 
 // reawaken network queue this soon after stopping; else watchdog barks
 #define TX_TIMEOUT_JIFFIES     (5*HZ)
@@ -227,13 +230,23 @@ static int usbnet_change_mtu (struct net_device *net, int new_mtu)
 {
        struct usbnet   *dev = netdev_priv(net);
        int             ll_mtu = new_mtu + net->hard_header_len;
+       int             old_hard_mtu = dev->hard_mtu;
+       int             old_rx_urb_size = dev->rx_urb_size;
 
-       if (new_mtu <= 0 || ll_mtu > dev->hard_mtu)
+       if (new_mtu <= 0)
                return -EINVAL;
        // no second zero-length packet read wanted after mtu-sized packets
        if ((ll_mtu % dev->maxpacket) == 0)
                return -EDOM;
        net->mtu = new_mtu;
+
+       dev->hard_mtu = net->mtu + net->hard_header_len;
+       if (dev->rx_urb_size == old_hard_mtu) {
+               dev->rx_urb_size = dev->hard_mtu;
+               if (dev->rx_urb_size > old_rx_urb_size)
+                       usbnet_unlink_rx_urbs(dev);
+       }
+
        return 0;
 }
 
@@ -412,9 +425,9 @@ static void rx_complete (struct urb *urb, struct pt_regs *regs)
            // we get controller i/o faults during khubd disconnect() delays.
            // throttle down resubmits, to avoid log floods; just temporarily,
            // so we still recover when the fault isn't a khubd delay.
-           case -EPROTO:               // ehci
-           case -ETIMEDOUT:            // ohci
-           case -EILSEQ:               // uhci
+           case -EPROTO:
+           case -ETIME:
+           case -EILSEQ:
                dev->stats.rx_errors++;
                if (!timer_pending (&dev->delay)) {
                        mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES);
@@ -521,6 +534,17 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
        return count;
 }
 
+// Flush all pending rx urbs
+// minidrivers may need to do this when the MTU changes
+
+void usbnet_unlink_rx_urbs(struct usbnet *dev)
+{
+       if (netif_running(dev->net)) {
+               (void) unlink_urbs (dev, &dev->rxq);
+               tasklet_schedule(&dev->bh);
+       }
+}
+EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs);
 
 /*-------------------------------------------------------------------------*/
 
@@ -629,7 +653,7 @@ static int usbnet_open (struct net_device *net)
 
                devinfo (dev, "open: enable queueing "
                                "(rx %d, tx %d) mtu %d %s framing",
-                       RX_QLEN (dev), TX_QLEN (dev), dev->net->mtu,
+                       (int)RX_QLEN (dev), (int)TX_QLEN (dev), dev->net->mtu,
                        framing);
        }
 
@@ -797,9 +821,9 @@ static void tx_complete (struct urb *urb, struct pt_regs *regs)
 
                // like rx, tx gets controller i/o faults during khubd delays
                // and so it uses the same throttling mechanism.
-               case -EPROTO:           // ehci
-               case -ETIMEDOUT:        // ohci
-               case -EILSEQ:           // uhci
+               case -EPROTO:
+               case -ETIME:
+               case -EILSEQ:
                        if (!timer_pending (&dev->delay)) {
                                mod_timer (&dev->delay,
                                        jiffies + THROTTLE_JIFFIES);
index 89fc495..c0746f0 100644 (file)
@@ -166,6 +166,7 @@ struct skb_data {   /* skb->cb is one of these */
 extern int usbnet_get_endpoints(struct usbnet *, struct usb_interface *);
 extern void usbnet_defer_kevent (struct usbnet *, int);
 extern void usbnet_skb_return (struct usbnet *, struct sk_buff *);
+extern void usbnet_unlink_rx_urbs(struct usbnet *);
 
 extern u32 usbnet_get_msglevel (struct net_device *);
 extern void usbnet_set_msglevel (struct net_device *, u32);
index f5b9438..5076b9d 100644 (file)
@@ -53,6 +53,15 @@ config USB_SERIAL_GENERIC
          support" be compiled as a module for this driver to be used
          properly.
 
+config USB_SERIAL_AIRCABLE
+       tristate "AIRcable USB Bluetooth Dongle Driver (EXPERIMENTAL)"
+       depends on USB_SERIAL && EXPERIMENTAL
+       help
+           Say Y here if you want to use AIRcable USB Bluetoot Dongle.
+
+           To compile this driver as a module, choose M here: the module
+           will be called aircable.
+
 config USB_SERIAL_AIRPRIME
        tristate "USB AirPrime CDMA Wireless Driver"
        depends on USB_SERIAL
@@ -413,6 +422,21 @@ config USB_SERIAL_MCT_U232
          To compile this driver as a module, choose M here: the
          module will be called mct_u232.
 
+config USB_SERIAL_MOS7840
+       tristate "USB Moschip 7840/7820 USB Serial Driver"
+       depends on USB_SERIAL
+       ---help---
+         Say Y here if you want to use a MCS7840 Quad-Serial or MCS7820
+         Dual-Serial port device from MosChip Semiconductor.
+
+         The MCS7840 and MCS7820 have been developed to connect a wide range
+         of standard serial devices to a USB host.  The MCS7840 has a USB
+         device controller connected to four (4) individual UARTs while the
+         MCS7820 controller connects to two (2) individual UARTs.
+
+         To compile this driver as a module, choose M here: the
+         module will be called mos7840.  If unsure, choose N.
+
 config USB_SERIAL_NAVMAN
        tristate "USB Navman GPS device"
        depends on USB_SERIAL
@@ -526,5 +550,6 @@ config USB_EZUSB
        depends on USB_SERIAL_KEYSPAN_PDA || USB_SERIAL_XIRCOM || USB_SERIAL_KEYSPAN || USB_SERIAL_WHITEHEAT
        default y
 
+
 endmenu
 
index 8efed2c..8dce833 100644 (file)
@@ -11,6 +11,7 @@ usbserial-obj-$(CONFIG_USB_EZUSB)             += ezusb.o
 
 usbserial-objs := usb-serial.o generic.o bus.o $(usbserial-obj-y)
 
+obj-$(CONFIG_USB_SERIAL_AIRCABLE)              += aircable.o
 obj-$(CONFIG_USB_SERIAL_AIRPRIME)              += airprime.o
 obj-$(CONFIG_USB_SERIAL_ARK3116)               += ark3116.o
 obj-$(CONFIG_USB_SERIAL_BELKIN)                        += belkin_sa.o
@@ -33,6 +34,7 @@ obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA)          += keyspan_pda.o
 obj-$(CONFIG_USB_SERIAL_KLSI)                  += kl5kusb105.o
 obj-$(CONFIG_USB_SERIAL_KOBIL_SCT)             += kobil_sct.o
 obj-$(CONFIG_USB_SERIAL_MCT_U232)              += mct_u232.o
+obj-$(CONFIG_USB_SERIAL_MOS7840)               += mos7840.o
 obj-$(CONFIG_USB_SERIAL_NAVMAN)                        += navman.o
 obj-$(CONFIG_USB_SERIAL_OMNINET)               += omninet.o
 obj-$(CONFIG_USB_SERIAL_OPTION)                        += option.o
diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c
new file mode 100644 (file)
index 0000000..2ccd9de
--- /dev/null
@@ -0,0 +1,625 @@
+/*
+ * AIRcable USB Bluetooth Dongle Driver.
+ *
+ * Copyright (C) 2006 Manuel Francisco Naranjo (naranjo.manuel@gmail.com)
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ * The device works as an standard CDC device, it has 2 interfaces, the first
+ * one is for firmware access and the second is the serial one.
+ * The protocol is very simply, there are two posibilities reading or writing.
+ * When writting the first urb must have a Header that starts with 0x20 0x29 the
+ * next two bytes must say how much data will be sended.
+ * When reading the process is almost equal except that the header starts with
+ * 0x00 0x20.
+ *
+ * The device simply need some stuff to understand data comming from the usb
+ * buffer: The First and Second byte is used for a Header, the Third and Fourth
+ * tells the  device the amount of information the package holds.
+ * Packages are 60 bytes long Header Stuff.
+ * When writting to the device the first two bytes of the header are 0x20 0x29
+ * When reading the bytes are 0x00 0x20, or 0x00 0x10, there is an strange
+ * situation, when too much data arrives to the device because it sends the data
+ * but with out the header. I will use a simply hack to override this situation,
+ * if there is data coming that does not contain any header, then that is data
+ * that must go directly to the tty, as there is no documentation about if there
+ * is any other control code, I will simply check for the first
+ * one.
+ *
+ * The driver registers himself with the USB-serial core and the USB Core. I had
+ * to implement a probe function agains USB-serial, because other way, the
+ * driver was attaching himself to both interfaces. I have tryed with different
+ * configurations of usb_serial_driver with out exit, only the probe function
+ * could handle this correctly.
+ *
+ * I have taken some info from a Greg Kroah-Hartman article:
+ * http://www.linuxjournal.com/article/6573
+ * And from Linux Device Driver Kit CD, which is a great work, the authors taken
+ * the work to recompile lots of information an knowladge in drivers development
+ * and made it all avaible inside a cd.
+ * URL: http://kernel.org/pub/linux/kernel/people/gregkh/ddk/
+ *
+ */
+
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/circ_buf.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+static int debug;
+
+/* Vendor and Product ID */
+#define AIRCABLE_VID           0x16CA
+#define AIRCABLE_USB_PID       0x1502
+
+/* write buffer size defines */
+#define AIRCABLE_BUF_SIZE      2048
+
+/* Protocol Stuff */
+#define HCI_HEADER_LENGTH      0x4
+#define TX_HEADER_0            0x20
+#define TX_HEADER_1            0x29
+#define RX_HEADER_0            0x00
+#define RX_HEADER_1            0x20
+#define MAX_HCI_FRAMESIZE      60
+#define HCI_COMPLETE_FRAME     64
+
+/* rx_flags */
+#define THROTTLED              0x01
+#define ACTUALLY_THROTTLED     0x02
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.0b2"
+#define DRIVER_AUTHOR "Naranjo, Manuel Francisco <naranjo.manuel@gmail.com>"
+#define DRIVER_DESC "AIRcable USB Driver"
+
+/* ID table that will be registered with USB core */
+static struct usb_device_id id_table [] = {
+       { USB_DEVICE(AIRCABLE_VID, AIRCABLE_USB_PID) },
+       { },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+
+/* Internal Structure */
+struct aircable_private {
+       spinlock_t rx_lock;             /* spinlock for the receive lines */
+       struct circ_buf *tx_buf;        /* write buffer */
+       struct circ_buf *rx_buf;        /* read buffer */
+       int rx_flags;                   /* for throttilng */
+       struct work_struct rx_work;     /* work cue for the receiving line */
+};
+
+/* Private methods */
+
+/* Circular Buffer Methods, code from ti_usb_3410_5052 used */
+/*
+ * serial_buf_clear
+ *
+ * Clear out all data in the circular buffer.
+ */
+static void serial_buf_clear(struct circ_buf *cb)
+{
+       cb->head = cb->tail = 0;
+}
+
+/*
+ * serial_buf_alloc
+ *
+ * Allocate a circular buffer and all associated memory.
+ */
+static struct circ_buf *serial_buf_alloc(void)
+{
+       struct circ_buf *cb;
+       cb = kmalloc(sizeof(struct circ_buf), GFP_KERNEL);
+       if (cb == NULL)
+               return NULL;
+       cb->buf = kmalloc(AIRCABLE_BUF_SIZE, GFP_KERNEL);
+       if (cb->buf == NULL) {
+               kfree(cb);
+               return NULL;
+       }
+       serial_buf_clear(cb);
+       return cb;
+}
+
+/*
+ * serial_buf_free
+ *
+ * Free the buffer and all associated memory.
+ */
+static void serial_buf_free(struct circ_buf *cb)
+{
+       kfree(cb->buf);
+       kfree(cb);
+}
+
+/*
+ * serial_buf_data_avail
+ *
+ * Return the number of bytes of data available in the circular
+ * buffer.
+ */
+static int serial_buf_data_avail(struct circ_buf *cb)
+{
+       return CIRC_CNT(cb->head,cb->tail,AIRCABLE_BUF_SIZE);
+}
+
+/*
+ * serial_buf_put
+ *
+ * Copy data data from a user buffer and put it into the circular buffer.
+ * Restrict to the amount of space available.
+ *
+ * Return the number of bytes copied.
+ */
+static int serial_buf_put(struct circ_buf *cb, const char *buf, int count)
+{
+       int c, ret = 0;
+       while (1) {
+               c = CIRC_SPACE_TO_END(cb->head, cb->tail, AIRCABLE_BUF_SIZE);
+               if (count < c)
+                       c = count;
+               if (c <= 0)
+                       break;
+               memcpy(cb->buf + cb->head, buf, c);
+               cb->head = (cb->head + c) & (AIRCABLE_BUF_SIZE-1);
+               buf += c;
+               count -= c;
+               ret= c;
+       }
+       return ret;
+}
+
+/*
+ * serial_buf_get
+ *
+ * Get data from the circular buffer and copy to the given buffer.
+ * Restrict to the amount of data available.
+ *
+ * Return the number of bytes copied.
+ */
+static int serial_buf_get(struct circ_buf *cb, char *buf, int count)
+{
+       int c, ret = 0;
+       while (1) {
+               c = CIRC_CNT_TO_END(cb->head, cb->tail, AIRCABLE_BUF_SIZE);
+               if (count < c)
+                       c = count;
+               if (c <= 0)
+                       break;
+               memcpy(buf, cb->buf + cb->tail, c);
+               cb->tail = (cb->tail + c) & (AIRCABLE_BUF_SIZE-1);
+               buf += c;
+               count -= c;
+               ret= c;
+       }
+       return ret;
+}
+
+/* End of circula buffer methods */
+
+static void aircable_send(struct usb_serial_port *port)
+{
+       int count, result;
+       struct aircable_private *priv = usb_get_serial_port_data(port);
+       unsigned char* buf;
+       dbg("%s - port %d", __FUNCTION__, port->number);
+       if (port->write_urb_busy)
+               return;
+
+       count = min(serial_buf_data_avail(priv->tx_buf), MAX_HCI_FRAMESIZE);
+       if (count == 0)
+               return;
+
+       buf = kzalloc(count + HCI_HEADER_LENGTH, GFP_ATOMIC);
+       if (!buf) {
+               err("%s- kzalloc(%d) failed.", __FUNCTION__,
+                   count + HCI_HEADER_LENGTH);
+               return;
+       }
+
+       buf[0] = TX_HEADER_0;
+       buf[1] = TX_HEADER_1;
+       buf[2] = (unsigned char)count;
+       buf[3] = (unsigned char)(count >> 8);
+       serial_buf_get(priv->tx_buf,buf + HCI_HEADER_LENGTH, MAX_HCI_FRAMESIZE);
+
+       memcpy(port->write_urb->transfer_buffer, buf,
+              count + HCI_HEADER_LENGTH);
+
+       kfree(buf);
+       port->write_urb_busy = 1;
+       usb_serial_debug_data(debug, &port->dev, __FUNCTION__,
+                             count + HCI_HEADER_LENGTH,
+                             port->write_urb->transfer_buffer);
+       port->write_urb->transfer_buffer_length = count + HCI_HEADER_LENGTH;
+       port->write_urb->dev = port->serial->dev;
+       result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+
+       if (result) {
+               dev_err(&port->dev,
+                       "%s - failed submitting write urb, error %d\n",
+                       __FUNCTION__, result);
+               port->write_urb_busy = 0;
+       }
+
+       schedule_work(&port->work);
+}
+
+static void aircable_read(void *params)
+{
+       struct usb_serial_port *port = params;
+       struct aircable_private *priv = usb_get_serial_port_data(port);
+       struct tty_struct *tty;
+       unsigned char *data;
+       int count;
+       if (priv->rx_flags & THROTTLED){
+               if (priv->rx_flags & ACTUALLY_THROTTLED)
+                       schedule_work(&priv->rx_work);
+               return;
+       }
+
+       /* By now I will flush data to the tty in packages of no more than
+        * 64 bytes, to ensure I do not get throttled.
+        * Ask USB mailing list for better aproach.
+        */
+       tty = port->tty;
+
+       if (!tty)
+               schedule_work(&priv->rx_work);
+
+       count = min(64, serial_buf_data_avail(priv->rx_buf));
+
+       if (count <= 0)
+               return; //We have finished sending everything.
+
+       tty_prepare_flip_string(tty, &data, count);
+       if (!data){
+               err("%s- kzalloc(%d) failed.", __FUNCTION__, count);
+               return;
+       }
+
+       serial_buf_get(priv->rx_buf, data, count);
+
+       tty_flip_buffer_push(tty);
+
+       if (serial_buf_data_avail(priv->rx_buf))
+               schedule_work(&priv->rx_work);
+
+       return;
+}
+/* End of private methods */
+
+static int aircable_probe(struct usb_serial *serial,
+                         const struct usb_device_id *id)
+{
+       struct usb_host_interface *iface_desc = serial->interface->cur_altsetting;
+       struct usb_endpoint_descriptor *endpoint;
+       int num_bulk_out=0;
+       int i;
+
+       for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
+               endpoint = &iface_desc->endpoint[i].desc;
+               if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
+                       ((endpoint->bmAttributes & 3) == 0x02)) {
+                       /* we found our bulk out endpoint */
+                       dbg("found bulk out on endpoint %d", i);
+                       ++num_bulk_out;
+               }
+       }
+
+       if (num_bulk_out == 0) {
+               dbg("Invalid interface, discarding");
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static int aircable_attach (struct usb_serial *serial)
+{
+       struct usb_serial_port *port = serial->port[0];
+       struct aircable_private *priv;
+
+       priv = kzalloc(sizeof(struct aircable_private), GFP_KERNEL);
+       if (!priv){
+               err("%s- kmalloc(%Zd) failed.", __FUNCTION__,
+                       sizeof(struct aircable_private));
+               return -ENOMEM;
+       }
+
+       /* Allocation of Circular Buffers */
+       priv->tx_buf = serial_buf_alloc();
+       if (priv->tx_buf == NULL) {
+               kfree(priv);
+               return -ENOMEM;
+       }
+
+       priv->rx_buf = serial_buf_alloc();
+       if (priv->rx_buf == NULL) {
+               kfree(priv->tx_buf);
+               kfree(priv);
+               return -ENOMEM;
+       }
+
+       priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
+       INIT_WORK(&priv->rx_work, aircable_read, port);
+
+       usb_set_serial_port_data(serial->port[0], priv);
+
+       return 0;
+}
+
+static void aircable_shutdown(struct usb_serial *serial)
+{
+
+       struct usb_serial_port *port = serial->port[0];
+       struct aircable_private *priv = usb_get_serial_port_data(port);
+
+       dbg("%s", __FUNCTION__);
+
+       if (priv) {
+               serial_buf_free(priv->tx_buf);
+               serial_buf_free(priv->rx_buf);
+               usb_set_serial_port_data(port, NULL);
+               kfree(priv);
+       }
+}
+
+static int aircable_write_room(struct usb_serial_port *port)
+{
+       struct aircable_private *priv = usb_get_serial_port_data(port);
+       return serial_buf_data_avail(priv->tx_buf);
+}
+
+static int aircable_write(struct usb_serial_port *port,
+                         const unsigned char *source, int count)
+{
+       struct aircable_private *priv = usb_get_serial_port_data(port);
+       int temp;
+
+       dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count);
+
+       usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, source);
+
+       if (!count){
+               dbg("%s - write request of 0 bytes", __FUNCTION__);
+               return count;
+       }
+
+       temp = serial_buf_put(priv->tx_buf, source, count);
+
+       aircable_send(port);
+
+       if (count > AIRCABLE_BUF_SIZE)
+               count = AIRCABLE_BUF_SIZE;
+
+       return count;
+
+}
+
+static void aircable_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+       struct usb_serial_port *port = urb->context;
+       int result;
+
+       dbg("%s - urb->status: %d", __FUNCTION__ , urb->status);
+
+       /* This has been taken from cypress_m8.c cypress_write_int_callback */
+       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",
+                           __FUNCTION__, urb->status);
+                       port->write_urb_busy = 0;
+                       return;
+               default:
+                       /* error in the urb, so we have to resubmit it */
+                       dbg("%s - Overflow in write", __FUNCTION__);
+                       dbg("%s - nonzero write bulk status received: %d",
+                           __FUNCTION__, urb->status);
+                       port->write_urb->transfer_buffer_length = 1;
+                       port->write_urb->dev = port->serial->dev;
+                       result = usb_submit_urb(port->write_urb, GFP_KERNEL);
+                       if (result)
+                               dev_err(&urb->dev->dev,
+                                       "%s - failed resubmitting write urb, error %d\n",
+                                       __FUNCTION__, result);
+                       else
+                               return;
+       }
+
+       port->write_urb_busy = 0;
+
+       aircable_send(port);
+}
+
+static void aircable_read_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+       struct usb_serial_port *port = urb->context;
+       struct aircable_private *priv = usb_get_serial_port_data(port);
+       struct tty_struct *tty;
+       unsigned long no_packages, remaining, package_length, i;
+       int result, shift = 0;
+       unsigned char *temp;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (urb->status) {
+               dbg("%s - urb->status = %d", __FUNCTION__, urb->status);
+               if (!port->open_count) {
+                       dbg("%s - port is closed, exiting.", __FUNCTION__);
+                       return;
+               }
+               if (urb->status == -EPROTO) {
+                       dbg("%s - caught -EPROTO, resubmitting the urb",
+                           __FUNCTION__);
+                       usb_fill_bulk_urb(port->read_urb, port->serial->dev,
+                                         usb_rcvbulkpipe(port->serial->dev,
+                                                         port->bulk_in_endpointAddress),
+                                         port->read_urb->transfer_buffer,
+                                         port->read_urb->transfer_buffer_length,
+                                         aircable_read_bulk_callback, port);
+
+                       result = usb_submit_urb(urb, GFP_ATOMIC);
+                       if (result)
+                               dev_err(&urb->dev->dev,
+                                       "%s - failed resubmitting read urb, error %d\n",
+                                       __FUNCTION__, result);
+                       return;
+               }
+               dbg("%s - unable to handle the error, exiting.", __FUNCTION__);
+               return;
+       }
+
+       usb_serial_debug_data(debug, &port->dev, __FUNCTION__,
+                               urb->actual_length,urb->transfer_buffer);
+
+       tty = port->tty;
+       if (tty && urb->actual_length) {
+               if (urb->actual_length <= 2) {
+                       /* This is an incomplete package */
+                       serial_buf_put(priv->rx_buf, urb->transfer_buffer,
+                                      urb->actual_length);
+               } else {
+                       temp = urb->transfer_buffer;
+                       if (temp[0] == RX_HEADER_0)
+                               shift = HCI_HEADER_LENGTH;
+
+                       remaining = urb->actual_length;
+                       no_packages = urb->actual_length / (HCI_COMPLETE_FRAME);
+
+                       if (urb->actual_length % HCI_COMPLETE_FRAME != 0)
+                               no_packages+=1;
+
+                       for (i = 0; i < no_packages ;i++) {
+                               if (remaining > (HCI_COMPLETE_FRAME))
+                                       package_length = HCI_COMPLETE_FRAME;
+                               else
+                                       package_length = remaining;
+                               remaining -= package_length;
+
+                               serial_buf_put(priv->rx_buf,
+                                       urb->transfer_buffer + shift +
+                                       (HCI_COMPLETE_FRAME) * (i),
+                                       package_length - shift);
+                       }
+               }
+               aircable_read(port);
+       }
+
+       /* Schedule the next read _if_ we are still open */
+       if (port->open_count) {
+               usb_fill_bulk_urb(port->read_urb, port->serial->dev,
+                                 usb_rcvbulkpipe(port->serial->dev,
+                                                 port->bulk_in_endpointAddress),
+                                 port->read_urb->transfer_buffer,
+                                 port->read_urb->transfer_buffer_length,
+                                 aircable_read_bulk_callback, port);
+
+               result = usb_submit_urb(urb, GFP_ATOMIC);
+               if (result)
+                       dev_err(&urb->dev->dev,
+                               "%s - failed resubmitting read urb, error %d\n",
+                               __FUNCTION__, result);
+       }
+
+       return;
+}
+
+/* Based on ftdi_sio.c throttle */
+static void aircable_throttle(struct usb_serial_port *port)
+{
+       struct aircable_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       spin_lock_irqsave(&priv->rx_lock, flags);
+       priv->rx_flags |= THROTTLED;
+       spin_unlock_irqrestore(&priv->rx_lock, flags);
+}
+
+/* Based on ftdi_sio.c unthrottle */
+static void aircable_unthrottle(struct usb_serial_port *port)
+{
+       struct aircable_private *priv = usb_get_serial_port_data(port);
+       int actually_throttled;
+       unsigned long flags;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       spin_lock_irqsave(&priv->rx_lock, flags);
+       actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED;
+       priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
+       spin_unlock_irqrestore(&priv->rx_lock, flags);
+
+       if (actually_throttled)
+               schedule_work(&priv->rx_work);
+}
+
+static struct usb_serial_driver aircable_device = {
+       .description =          "aircable",
+       .id_table =             id_table,
+       .num_ports =            1,
+       .attach =               aircable_attach,
+       .probe =                aircable_probe,
+       .shutdown =             aircable_shutdown,
+       .write =                aircable_write,
+       .write_room =           aircable_write_room,
+       .write_bulk_callback =  aircable_write_bulk_callback,
+       .read_bulk_callback =   aircable_read_bulk_callback,
+       .throttle =             aircable_throttle,
+       .unthrottle =           aircable_unthrottle,
+};
+
+static struct usb_driver aircable_driver = {
+       .name =         "aircable",
+       .probe =        usb_serial_probe,
+       .disconnect =   usb_serial_disconnect,
+       .id_table =     id_table,
+};
+
+static int __init aircable_init (void)
+{
+       int retval;
+       retval = usb_serial_register(&aircable_device);
+       if (retval)
+               goto failed_serial_register;
+       retval = usb_register(&aircable_driver);
+       if (retval)
+               goto failed_usb_register;
+       return 0;
+
+failed_serial_register:
+       usb_serial_deregister(&aircable_device);
+failed_usb_register:
+       return retval;
+}
+
+static void __exit aircable_exit (void)
+{
+       usb_deregister(&aircable_driver);
+       usb_serial_deregister(&aircable_device);
+}
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+module_init(aircable_init);
+module_exit(aircable_exit);
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
index 6208253..6e1a84a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * AirPrime CDMA Wireless Serial USB driver
  *
- * Copyright (C) 2005 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (C) 2005-2006 Greg Kroah-Hartman <gregkh@suse.de>
  *
  *     This program is free software; you can redistribute it and/or
  *     modify it under the terms of the GNU General Public License version
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/tty.h>
+#include <linux/tty_flip.h>
 #include <linux/module.h>
 #include <linux/usb.h>
 #include <linux/usb/serial.h>
 
 static struct usb_device_id id_table [] = {
        { USB_DEVICE(0x0c88, 0x17da) }, /* Kyocera Wireless KPC650/Passport */
-       { USB_DEVICE(0xf3d, 0x0112) },  /* AirPrime CDMA Wireless PC Card */
-       { USB_DEVICE(0x1410, 0x1110) }, /* Novatel Wireless Merlin CDMA */
+       { USB_DEVICE(0x0f3d, 0x0112) }, /* AirPrime CDMA Wireless PC Card */
+       { USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */
+       { USB_DEVICE(0x1199, 0x0018) }, /* Sierra Wireless MC5720 */
        { USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless Aircard 580 */
        { USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */
+       { USB_DEVICE(0x1410, 0x1110) }, /* Novatel Wireless Merlin CDMA */
        { },
 };
 MODULE_DEVICE_TABLE(usb, id_table);
 
+#define URB_TRANSFER_BUFFER_SIZE       4096
+#define NUM_READ_URBS                  4
+#define NUM_WRITE_URBS                 4
+#define NUM_BULK_EPS                   3
+#define MAX_BULK_EPS                   6
+
+/* if overridden by the user, then use their value for the size of the
+ * read and write urbs, and the number of endpoints */
+static int buffer_size = URB_TRANSFER_BUFFER_SIZE;
+static int endpoints = NUM_BULK_EPS;
+static int debug;
+struct airprime_private {
+       spinlock_t lock;
+       int outstanding_urbs;
+       int throttled;
+       struct urb *read_urbp[NUM_READ_URBS];
+};
+
+static void airprime_read_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+       struct usb_serial_port *port = urb->context;
+       unsigned char *data = urb->transfer_buffer;
+       struct tty_struct *tty;
+       int result;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (urb->status) {
+               dbg("%s - nonzero read bulk status received: %d",
+                   __FUNCTION__, urb->status);
+               /* something happened, so free up the memory for this urb */
+               if (urb->transfer_buffer) {
+                       kfree (urb->transfer_buffer);
+                       urb->transfer_buffer = NULL;
+               }
+               return;
+       }
+       usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
+
+       tty = port->tty;
+       if (tty && urb->actual_length) {
+               tty_insert_flip_string (tty, data, urb->actual_length);
+               tty_flip_buffer_push (tty);
+       }
+
+       result = usb_submit_urb (urb, GFP_ATOMIC);
+       if (result)
+               dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n",
+                       __FUNCTION__, result);
+       return;
+}
+
+static void airprime_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+       struct usb_serial_port *port = urb->context;
+       struct airprime_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       /* free up the transfer buffer, as usb_free_urb() does not do this */
+       kfree (urb->transfer_buffer);
+
+       if (urb->status)
+               dbg("%s - nonzero write bulk status received: %d",
+                   __FUNCTION__, urb->status);
+       spin_lock_irqsave(&priv->lock, flags);
+       --priv->outstanding_urbs;
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       usb_serial_port_softint(port);
+}
+
+static int airprime_open(struct usb_serial_port *port, struct file *filp)
+{
+       struct airprime_private *priv = usb_get_serial_port_data(port);
+       struct usb_serial *serial = port->serial;
+       struct urb *urb;
+       char *buffer = NULL;
+       int i;
+       int result = 0;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       /* initialize our private data structure if it isn't already created */
+       if (!priv) {
+               priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+               if (!priv) {
+                       result = -ENOMEM;
+                       goto out;
+               }
+               spin_lock_init(&priv->lock);
+               usb_set_serial_port_data(port, priv);
+       }
+
+       for (i = 0; i < NUM_READ_URBS; ++i) {
+               buffer = kmalloc(buffer_size, GFP_KERNEL);
+               if (!buffer) {
+                       dev_err(&port->dev, "%s - out of memory.\n",
+                               __FUNCTION__);
+                       result = -ENOMEM;
+                       goto errout;
+               }
+               urb = usb_alloc_urb(0, GFP_KERNEL);
+               if (!urb) {
+                       dev_err(&port->dev, "%s - no more urbs?\n",
+                               __FUNCTION__);
+                       result = -ENOMEM;
+                       goto errout;
+               }
+               usb_fill_bulk_urb(urb, serial->dev,
+                                 usb_rcvbulkpipe(serial->dev,
+                                                 port->bulk_out_endpointAddress),
+                                 buffer, buffer_size,
+                                 airprime_read_bulk_callback, port);
+               result = usb_submit_urb(urb, GFP_KERNEL);
+               if (result) {
+                       dev_err(&port->dev,
+                               "%s - failed submitting read urb %d for port %d, error %d\n",
+                               __FUNCTION__, i, port->number, result);
+                       goto errout;
+               }
+               /* remember this urb so we can kill it when the port is closed */
+               priv->read_urbp[i] = urb;
+       }
+       goto out;
+
+ errout:
+       /* some error happened, cancel any submitted urbs and clean up anything that
+          got allocated successfully */
+
+       for ( ; i >= 0; --i) {
+               urb = priv->read_urbp[i];
+               if (urb) {
+                       /* This urb was submitted successfully. So we have to
+                          cancel it.
+                          Unlinking the urb will invoke read_bulk_callback()
+                          with an error status, so its transfer buffer will
+                          be freed there */
+                       if (usb_unlink_urb (urb) != -EINPROGRESS) {
+                               /* comments in drivers/usb/core/urb.c say this
+                                  can only happen if the urb was never submitted,
+                                  or has completed already.
+                                  Either way we may have to free the transfer
+                                  buffer here. */
+                               if (urb->transfer_buffer) {
+                                       kfree (urb->transfer_buffer);
+                                       urb->transfer_buffer = NULL;
+                               }
+                       }
+                       usb_free_urb (urb);
+               }
+       }
+
+ out:
+       return result;
+}
+
+static void airprime_close(struct usb_serial_port *port, struct file * filp)
+{
+       struct airprime_private *priv = usb_get_serial_port_data(port);
+       int i;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       /* killing the urb will invoke read_bulk_callback() with an error status,
+          so the transfer buffer will be freed there */
+       for (i = 0; i < NUM_READ_URBS; ++i) {
+               usb_kill_urb (priv->read_urbp[i]);
+               usb_free_urb (priv->read_urbp[i]);
+       }
+
+       /* free up private structure */
+       kfree (priv);
+       usb_set_serial_port_data(port, NULL);
+}
+
+static int airprime_write(struct usb_serial_port *port,
+                         const unsigned char *buf, int count)
+{
+       struct airprime_private *priv = usb_get_serial_port_data(port);
+       struct usb_serial *serial = port->serial;
+       struct urb *urb;
+       unsigned char *buffer;
+       unsigned long flags;
+       int status;
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (priv->outstanding_urbs > NUM_WRITE_URBS) {
+               spin_unlock_irqrestore(&priv->lock, flags);
+               dbg("%s - write limit hit\n", __FUNCTION__);
+               return 0;
+       }
+       spin_unlock_irqrestore(&priv->lock, flags);
+       buffer = kmalloc(count, GFP_ATOMIC);
+       if (!buffer) {
+               dev_err(&port->dev, "out of memory\n");
+               return -ENOMEM;
+       }
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (!urb) {
+               dev_err(&port->dev, "no more free urbs\n");
+               kfree (buffer);
+               return -ENOMEM;
+       }
+       memcpy (buffer, buf, count);
+
+       usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, buffer);
+
+       usb_fill_bulk_urb(urb, serial->dev,
+                         usb_sndbulkpipe(serial->dev,
+                                         port->bulk_out_endpointAddress),
+                         buffer, count,
+                         airprime_write_bulk_callback, port);
+
+       /* send it down the pipe */
+       status = usb_submit_urb(urb, GFP_ATOMIC);
+       if (status) {
+               dev_err(&port->dev,
+                       "%s - usb_submit_urb(write bulk) failed with status = %d\n",
+                       __FUNCTION__, status);
+               count = status;
+               kfree (buffer);
+       } else {
+               spin_lock_irqsave(&priv->lock, flags);
+               ++priv->outstanding_urbs;
+               spin_unlock_irqrestore(&priv->lock, flags);
+       }
+       /* we are done with this urb, so let the host driver
+        * really free it when it is finished with it */
+       usb_free_urb (urb);
+       return count;
+}
+
 static struct usb_driver airprime_driver = {
        .name =         "airprime",
        .probe =        usb_serial_probe,
        .disconnect =   usb_serial_disconnect,
        .id_table =     id_table,
-       .no_dynamic_id =        1,
+       .no_dynamic_id =        1,
 };
 
 static struct usb_serial_driver airprime_device = {
@@ -42,13 +280,17 @@ static struct usb_serial_driver airprime_device = {
        .num_interrupt_in =     NUM_DONT_CARE,
        .num_bulk_in =          NUM_DONT_CARE,
        .num_bulk_out =         NUM_DONT_CARE,
-       .num_ports =            1,
+       .open =                 airprime_open,
+       .close =                airprime_close,
+       .write =                airprime_write,
 };
 
 static int __init airprime_init(void)
 {
        int retval;
 
+       airprime_device.num_ports =
+               (endpoints > 0 && endpoints <= MAX_BULK_EPS) ? endpoints : NUM_BULK_EPS;
        retval = usb_serial_register(&airprime_device);
        if (retval)
                return retval;
@@ -60,6 +302,8 @@ static int __init airprime_init(void)
 
 static void __exit airprime_exit(void)
 {
+       dbg("%s", __FUNCTION__);
+
        usb_deregister(&airprime_driver);
        usb_serial_deregister(&airprime_device);
 }
@@ -67,3 +311,10 @@ static void __exit airprime_exit(void)
 module_init(airprime_init);
 module_exit(airprime_exit);
 MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled");
+module_param(buffer_size, int, 0);
+MODULE_PARM_DESC(buffer_size, "Size of the transfer buffers in bytes (default 4096)");
+module_param(endpoints, int, 0);
+MODULE_PARM_DESC(endpoints, "Number of bulk EPs to configure (default 3)");
index 970d9ef..ca52f12 100644 (file)
@@ -1,4 +1,7 @@
 /*
+ * Copyright (C) 2006
+ *   Simon Schulz (ark3116_driver <at> auctionant.de)
+ *
  * ark3116
  * - implements a driver for the arkmicro ark3116 chipset (vendor=0x6547,
  *   productid=0x0232) (used in a datacable called KQ-U8A)
@@ -8,8 +11,6 @@
  *
  *  - based on logs created by usbsnoopy
  *
- *  Author   : Simon Schulz [ark3116_driver<AT>auctionant.de]
- *
  * 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
@@ -22,6 +23,8 @@
 #include <linux/module.h>
 #include <linux/usb.h>
 #include <linux/usb/serial.h>
+#include <linux/serial.h>
+#include <asm/uaccess.h>
 
 
 static int debug;
@@ -43,10 +46,10 @@ static inline void ARK3116_SND(struct usb_serial *serial, int seq,
 {
        int result;
        result = usb_control_msg(serial->dev,
-                                usb_sndctrlpipe(serial->dev,0),
+                                usb_sndctrlpipe(serial->dev, 0),
                                 request, requesttype, value, index,
-                                NULL,0x00, 1000);
-       dbg("%03d > ok",seq);
+                                NULL, 0x00, 1000);
+       dbg("%03d > ok", seq);
 }
 
 static inline void ARK3116_RCV(struct usb_serial *serial, int seq,
@@ -56,27 +59,25 @@ static inline void ARK3116_RCV(struct usb_serial *serial, int seq,
 {
        int result;
        result = usb_control_msg(serial->dev,
-                             usb_rcvctrlpipe(serial->dev,0),
-                             request, requesttype, value, index,
-                             buf, 0x0000001, 1000);
+                                usb_rcvctrlpipe(serial->dev, 0),
+                                request, requesttype, value, index,
+                                buf, 0x0000001, 1000);
        if (result)
-               dbg("%03d < %d bytes [0x%02X]",seq, result, buf[0]);
+               dbg("%03d < %d bytes [0x%02X]", seq, result, buf[0]);
        else
                dbg("%03d < 0 bytes", seq);
 }
 
-
 static inline void ARK3116_RCV_QUIET(struct usb_serial *serial,
                                     __u8 request, __u8 requesttype,
                                     __u16 value, __u16 index, char *buf)
 {
        usb_control_msg(serial->dev,
-                       usb_rcvctrlpipe(serial->dev,0),
+                       usb_rcvctrlpipe(serial->dev, 0),
                        request, requesttype, value, index,
                        buf, 0x0000001, 1000);
 }
 
-
 static int ark3116_attach(struct usb_serial *serial)
 {
        char *buf;
@@ -84,10 +85,10 @@ static int ark3116_attach(struct usb_serial *serial)
        int i;
 
        for (i = 0; i < serial->num_ports; ++i) {
-               priv = kmalloc (sizeof (struct ark3116_private), GFP_KERNEL);
+               priv = kmalloc(sizeof (struct ark3116_private), GFP_KERNEL);
                if (!priv)
                        goto cleanup;
-               memset (priv, 0x00, sizeof (struct ark3116_private));
+               memset(priv, 0x00, sizeof (struct ark3116_private));
                spin_lock_init(&priv->lock);
 
                usb_set_serial_port_data(serial->port[i], priv);
@@ -95,63 +96,62 @@ static int ark3116_attach(struct usb_serial *serial)
 
        buf = kmalloc(1, GFP_KERNEL);
        if (!buf) {
-               dbg("error kmalloc -> out of mem ?");
+               dbg("error kmalloc -> out of mem?");
                goto cleanup;
        }
 
        /* 3 */
-       ARK3116_SND(serial, 3,0xFE,0x40,0x0008,0x0002);
-       ARK3116_SND(serial, 4,0xFE,0x40,0x0008,0x0001);
-       ARK3116_SND(serial, 5,0xFE,0x40,0x0000,0x0008);
-       ARK3116_SND(serial, 6,0xFE,0x40,0x0000,0x000B);
+       ARK3116_SND(serial, 3, 0xFE, 0x40, 0x0008, 0x0002);
+       ARK3116_SND(serial, 4, 0xFE, 0x40, 0x0008, 0x0001);
+       ARK3116_SND(serial, 5, 0xFE, 0x40, 0x0000, 0x0008);
+       ARK3116_SND(serial, 6, 0xFE, 0x40, 0x0000, 0x000B);
 
        /* <-- seq7 */
-       ARK3116_RCV(serial, 7,0xFE,0xC0,0x0000,0x0003, 0x00, buf);
-       ARK3116_SND(serial, 8,0xFE,0x40,0x0080,0x0003);
-       ARK3116_SND(serial, 9,0xFE,0x40,0x001A,0x0000);
-       ARK3116_SND(serial,10,0xFE,0x40,0x0000,0x0001);
-       ARK3116_SND(serial,11,0xFE,0x40,0x0000,0x0003);
+       ARK3116_RCV(serial,  7, 0xFE, 0xC0, 0x0000, 0x0003, 0x00, buf);
+       ARK3116_SND(serial,  8, 0xFE, 0x40, 0x0080, 0x0003);
+       ARK3116_SND(serial,  9, 0xFE, 0x40, 0x001A, 0x0000);
+       ARK3116_SND(serial, 10, 0xFE, 0x40, 0x0000, 0x0001);
+       ARK3116_SND(serial, 11, 0xFE, 0x40, 0x0000, 0x0003);
 
        /* <-- seq12 */
-       ARK3116_RCV(serial,12,0xFE,0xC0,0x0000,0x0004, 0x00, buf);
-       ARK3116_SND(serial,13,0xFE,0x40,0x0000,0x0004);
+       ARK3116_RCV(serial, 12, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf);
+       ARK3116_SND(serial, 13, 0xFE, 0x40, 0x0000, 0x0004);
 
        /* 14 */
-       ARK3116_RCV(serial,14,0xFE,0xC0,0x0000,0x0004, 0x00, buf);
-       ARK3116_SND(serial,15,0xFE,0x40,0x0000,0x0004);
+       ARK3116_RCV(serial, 14, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf);
+       ARK3116_SND(serial, 15, 0xFE, 0x40, 0x0000, 0x0004);
 
        /* 16 */
-       ARK3116_RCV(serial,16,0xFE,0xC0,0x0000,0x0004, 0x00, buf);
+       ARK3116_RCV(serial, 16, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf);
        /* --> seq17 */
-       ARK3116_SND(serial,17,0xFE,0x40,0x0001,0x0004);
+       ARK3116_SND(serial, 17, 0xFE, 0x40, 0x0001, 0x0004);
 
        /* <-- seq18 */
-       ARK3116_RCV(serial,18,0xFE,0xC0,0x0000,0x0004, 0x01, buf);
+       ARK3116_RCV(serial, 18, 0xFE, 0xC0, 0x0000, 0x0004, 0x01, buf);
 
        /* --> seq19 */
-       ARK3116_SND(serial,19,0xFE,0x40,0x0003,0x0004);
-
+       ARK3116_SND(serial, 19, 0xFE, 0x40, 0x0003, 0x0004);
 
        /* <-- seq20 */
-       /* seems like serial port status info (RTS, CTS,...) */
-       /* returns modem control line status ?! */
-       ARK3116_RCV(serial,20,0xFE,0xC0,0x0000,0x0006, 0xFF, buf);
-
-       /* set 9600 baud & do some init ?! */
-       ARK3116_SND(serial,147,0xFE,0x40,0x0083,0x0003);
-       ARK3116_SND(serial,148,0xFE,0x40,0x0038,0x0000);
-       ARK3116_SND(serial,149,0xFE,0x40,0x0001,0x0001);
-       ARK3116_SND(serial,150,0xFE,0x40,0x0003,0x0003);
-       ARK3116_RCV(serial,151,0xFE,0xC0,0x0000,0x0004,0x03, buf);
-       ARK3116_SND(serial,152,0xFE,0x40,0x0000,0x0003);
-       ARK3116_RCV(serial,153,0xFE,0xC0,0x0000,0x0003,0x00, buf);
-       ARK3116_SND(serial,154,0xFE,0x40,0x0003,0x0003);
+       /* seems like serial port status info (RTS, CTS, ...) */
+       /* returns modem control line status?! */
+       ARK3116_RCV(serial, 20, 0xFE, 0xC0, 0x0000, 0x0006, 0xFF, buf);
+
+       /* set 9600 baud & do some init?! */
+       ARK3116_SND(serial, 147, 0xFE, 0x40, 0x0083, 0x0003);
+       ARK3116_SND(serial, 148, 0xFE, 0x40, 0x0038, 0x0000);
+       ARK3116_SND(serial, 149, 0xFE, 0x40, 0x0001, 0x0001);
+       ARK3116_SND(serial, 150, 0xFE, 0x40, 0x0003, 0x0003);
+       ARK3116_RCV(serial, 151, 0xFE, 0xC0, 0x0000, 0x0004, 0x03, buf);
+       ARK3116_SND(serial, 152, 0xFE, 0x40, 0x0000, 0x0003);
+       ARK3116_RCV(serial, 153, 0xFE, 0xC0, 0x0000, 0x0003, 0x00, buf);
+       ARK3116_SND(serial, 154, 0xFE, 0x40, 0x0003, 0x0003);
 
        kfree(buf);
-       return(0);
+       return 0;
 
 cleanup:
-       for (--i; i>=0; --i)
+       for (--i; i >= 0; --i)
                usb_set_serial_port_data(serial->port[i], NULL);
        return -ENOMEM;
 }
@@ -180,7 +180,8 @@ static void ark3116_set_termios(struct usb_serial_port *port,
        spin_lock_irqsave(&priv->lock, flags);
        if (!priv->termios_initialized) {
                *(port->tty->termios) = tty_std_termios;
-               port->tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+               port->tty->termios->c_cflag = B9600 | CS8
+                                             | CREAD | HUPCL | CLOCAL;
                priv->termios_initialized = 1;
        }
        spin_unlock_irqrestore(&priv->lock, flags);
@@ -204,8 +205,8 @@ static void ark3116_set_termios(struct usb_serial_port *port,
        }
 
        /* set data bit count (8/7/6/5) */
-       if (cflag & CSIZE){
-               switch (cflag & CSIZE){
+       if (cflag & CSIZE) {
+               switch (cflag & CSIZE) {
                case CS5:
                        config |= 0x00;
                        dbg("setting CS5");
@@ -219,7 +220,8 @@ static void ark3116_set_termios(struct usb_serial_port *port,
                        dbg("setting CS7");
                        break;
                default:
-                       err ("CSIZE was set but not CS5-CS8, using CS8!");
+                       err("CSIZE was set but not CS5-CS8, using CS8!");
+                       /* fall through */
                case CS8:
                        config |= 0x03;
                        dbg("setting CS8");
@@ -227,8 +229,8 @@ static void ark3116_set_termios(struct usb_serial_port *port,
                }
        }
 
-       /* set parity (NONE,EVEN,ODD) */
-       if (cflag & PARENB){
+       /* set parity (NONE/EVEN/ODD) */
+       if (cflag & PARENB) {
                if (cflag & PARODD) {
                        config |= 0x08;
                        dbg("setting parity to ODD");
@@ -240,20 +242,19 @@ static void ark3116_set_termios(struct usb_serial_port *port,
                dbg("setting parity to NONE");
        }
 
-       /* SET STOPBIT (1/2) */
+       /* set stop bit (1/2) */
        if (cflag & CSTOPB) {
                config |= 0x04;
-               dbg ("setting 2 stop bits");
+               dbg("setting 2 stop bits");
        } else {
-               dbg ("setting 1 stop bit");
+               dbg("setting 1 stop bit");
        }
 
-
-       /* set baudrate: */
+       /* set baudrate */
        baud = 0;
-       switch (cflag & CBAUD){
+       switch (cflag & CBAUD) {
                case B0:
-                       err("can't set 0baud, using 9600 instead");
+                       err("can't set 0 baud, using 9600 instead");
                        break;
                case B75:       baud = 75;      break;
                case B150:      baud = 150;     break;
@@ -285,38 +286,40 @@ static void ark3116_set_termios(struct usb_serial_port *port,
         */
        if (baud == 460800)
                /* strange, for 460800 the formula is wrong
-                * (dont use round(), then 9600baud is wrong) */
+                * if using round() then 9600baud is wrong) */
                ark3116_baud = 7;
        else
                ark3116_baud = 3000000 / baud;
 
        /* ? */
-       ARK3116_RCV(serial,0,0xFE,0xC0,0x0000,0x0003, 0x03, buf);
+       ARK3116_RCV(serial, 0, 0xFE, 0xC0, 0x0000, 0x0003, 0x03, buf);
+
        /* offset = buf[0]; */
        /* offset = 0x03; */
-       /* dbg("using 0x%04X as target for 0x0003:",0x0080+offset); */
-
+       /* dbg("using 0x%04X as target for 0x0003:", 0x0080 + offset); */
 
        /* set baudrate */
-       dbg("setting baudrate to %d (->reg=%d)",baud,ark3116_baud);
-       ARK3116_SND(serial,147,0xFE,0x40,0x0083,0x0003);
-       ARK3116_SND(serial,148,0xFE,0x40,(ark3116_baud & 0x00FF)   ,0x0000);
-       ARK3116_SND(serial,149,0xFE,0x40,(ark3116_baud & 0xFF00)>>8,0x0001);
-       ARK3116_SND(serial,150,0xFE,0x40,0x0003,0x0003);
+       dbg("setting baudrate to %d (->reg=%d)", baud, ark3116_baud);
+       ARK3116_SND(serial, 147, 0xFE, 0x40, 0x0083, 0x0003);
+       ARK3116_SND(serial, 148, 0xFE, 0x40,
+                           (ark3116_baud & 0x00FF), 0x0000);
+       ARK3116_SND(serial, 149, 0xFE, 0x40,
+                           (ark3116_baud & 0xFF00) >> 8, 0x0001);
+       ARK3116_SND(serial, 150, 0xFE, 0x40, 0x0003, 0x0003);
 
        /* ? */
-       ARK3116_RCV(serial,151,0xFE,0xC0,0x0000,0x0004,0x03, buf);
-       ARK3116_SND(serial,152,0xFE,0x40,0x0000,0x0003);
+       ARK3116_RCV(serial, 151, 0xFE, 0xC0, 0x0000, 0x0004, 0x03, buf);
+       ARK3116_SND(serial, 152, 0xFE, 0x40, 0x0000, 0x0003);
 
        /* set data bit count, stop bit count & parity: */
        dbg("updating bit count, stop bit or parity (cfg=0x%02X)", config);
-       ARK3116_RCV(serial,153,0xFE,0xC0,0x0000,0x0003,0x00, buf);
-       ARK3116_SND(serial,154,0xFE,0x40,config,0x0003);
+       ARK3116_RCV(serial, 153, 0xFE, 0xC0, 0x0000, 0x0003, 0x00, buf);
+       ARK3116_SND(serial, 154, 0xFE, 0x40, config, 0x0003);
 
        if (cflag & CRTSCTS)
-               dbg("CRTSCTS not supported by chipset ?!");
+               dbg("CRTSCTS not supported by chipset?!");
 
-       /* TEST ARK3116_SND(154,0xFE,0x40,0xFFFF, 0x0006); */
+       /* TEST ARK3116_SND(154, 0xFE, 0x40, 0xFFFF, 0x0006); */
 
        kfree(buf);
        return;
@@ -329,11 +332,11 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp)
        char *buf;
        int result = 0;
 
-       dbg("%s -  port %d", __FUNCTION__, port->number);
+       dbg("%s - port %d", __FUNCTION__, port->number);
 
        buf = kmalloc(1, GFP_KERNEL);
        if (!buf) {
-               dbg("error kmalloc -> out of mem ?");
+               dbg("error kmalloc -> out of mem?");
                return -ENOMEM;
        }
 
@@ -342,44 +345,68 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp)
                return result;
 
        /* open */
-       ARK3116_RCV(serial,111,0xFE,0xC0,0x0000,0x0003, 0x02, buf);
+       ARK3116_RCV(serial, 111, 0xFE, 0xC0, 0x0000, 0x0003, 0x02, buf);
 
-       ARK3116_SND(serial,112,0xFE,0x40,0x0082,0x0003);
-       ARK3116_SND(serial,113,0xFE,0x40,0x001A,0x0000);
-       ARK3116_SND(serial,114,0xFE,0x40,0x0000,0x0001);
-       ARK3116_SND(serial,115,0xFE,0x40,0x0002,0x0003);
+       ARK3116_SND(serial, 112, 0xFE, 0x40, 0x0082, 0x0003);
+       ARK3116_SND(serial, 113, 0xFE, 0x40, 0x001A, 0x0000);
+       ARK3116_SND(serial, 114, 0xFE, 0x40, 0x0000, 0x0001);
+       ARK3116_SND(serial, 115, 0xFE, 0x40, 0x0002, 0x0003);
 
-       ARK3116_RCV(serial,116,0xFE,0xC0,0x0000,0x0004, 0x03, buf);
-       ARK3116_SND(serial,117,0xFE,0x40,0x0002,0x0004);
+       ARK3116_RCV(serial, 116, 0xFE, 0xC0, 0x0000, 0x0004, 0x03, buf);
+       ARK3116_SND(serial, 117, 0xFE, 0x40, 0x0002, 0x0004);
 
-       ARK3116_RCV(serial,118,0xFE,0xC0,0x0000,0x0004, 0x02, buf);
-       ARK3116_SND(serial,119,0xFE,0x40,0x0000,0x0004);
+       ARK3116_RCV(serial, 118, 0xFE, 0xC0, 0x0000, 0x0004, 0x02, buf);
+       ARK3116_SND(serial, 119, 0xFE, 0x40, 0x0000, 0x0004);
 
-       ARK3116_RCV(serial,120,0xFE,0xC0,0x0000,0x0004, 0x00, buf);
+       ARK3116_RCV(serial, 120, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf);
 
-       ARK3116_SND(serial,121,0xFE,0x40,0x0001,0x0004);
+       ARK3116_SND(serial, 121, 0xFE, 0x40, 0x0001, 0x0004);
 
-       ARK3116_RCV(serial,122,0xFE,0xC0,0x0000,0x0004, 0x01, buf);
+       ARK3116_RCV(serial, 122, 0xFE, 0xC0, 0x0000, 0x0004, 0x01, buf);
 
-       ARK3116_SND(serial,123,0xFE,0x40,0x0003,0x0004);
+       ARK3116_SND(serial, 123, 0xFE, 0x40, 0x0003, 0x0004);
 
-       /* returns different values (control lines ?!) */
-       ARK3116_RCV(serial,124,0xFE,0xC0,0x0000,0x0006, 0xFF, buf);
+       /* returns different values (control lines?!) */
+       ARK3116_RCV(serial, 124, 0xFE, 0xC0, 0x0000, 0x0006, 0xFF, buf);
 
-       /* initialise termios: */
+       /* initialise termios */
        if (port->tty)
                ark3116_set_termios(port, &tmp_termios);
 
        kfree(buf);
 
        return result;
-
 }
 
 static int ark3116_ioctl(struct usb_serial_port *port, struct file *file,
                         unsigned int cmd, unsigned long arg)
 {
-       dbg("ioctl not supported yet...");
+       struct serial_struct serstruct;
+       void __user *user_arg = (void __user *)arg;
+
+       switch (cmd) {
+       case TIOCGSERIAL:
+               /* XXX: Some of these values are probably wrong. */
+               memset(&serstruct, 0, sizeof (serstruct));
+               serstruct.type = PORT_16654;
+               serstruct.line = port->serial->minor;
+               serstruct.port = port->number;
+               serstruct.custom_divisor = 0;
+               serstruct.baud_base = 460800;
+
+               if (copy_to_user(user_arg, &serstruct, sizeof (serstruct)))
+                       return -EFAULT;
+
+               return 0;
+       case TIOCSSERIAL:
+               if (copy_from_user(&serstruct, user_arg, sizeof (serstruct)))
+                       return -EFAULT;
+               return 0;
+       default:
+               dbg("%s cmd 0x%04x not supported", __FUNCTION__, cmd);
+               break;
+       }
+
        return -ENOIOCTLCMD;
 }
 
@@ -389,7 +416,7 @@ static int ark3116_tiocmget(struct usb_serial_port *port, struct file *file)
        char *buf;
        char temp;
 
-       /* seems like serial port status info (RTS, CTS,...) is stored
+       /* seems like serial port status info (RTS, CTS, ...) is stored
         * in reg(?) 0x0006
         * pcb connection point 11 = GND -> sets bit4 of response
         * pcb connection point  7 = GND -> sets bit6 of response
@@ -401,16 +428,16 @@ static int ark3116_tiocmget(struct usb_serial_port *port, struct file *file)
                return -ENOMEM;
        }
 
-       /* read register: */
-       ARK3116_RCV_QUIET(serial,0xFE,0xC0,0x0000,0x0006,buf);
+       /* read register */
+       ARK3116_RCV_QUIET(serial, 0xFE, 0xC0, 0x0000, 0x0006, buf);
        temp = buf[0];
        kfree(buf);
 
-       /* i do not really know if bit4=CTS and bit6=DSR... was just a
-        * quick guess !!
+       /* i do not really know if bit4=CTS and bit6=DSR... just a
+        * quick guess!
         */
-       return  (temp & (1<<4) ? TIOCM_CTS : 0) |
-               (temp & (1<<6) ? TIOCM_DSR : 0);
+       return (temp & (1<<4) ? TIOCM_CTS : 0)
+              | (temp & (1<<6) ? TIOCM_DSR : 0);
 }
 
 static struct usb_driver ark3116_driver = {
index ee70fdd..e1173c1 100644 (file)
@@ -129,6 +129,9 @@ struct cypress_private {
        int cmd_ctrl;                      /* always set this to 1 before issuing a command */
        struct cypress_buf *buf;           /* write buffer */
        int write_urb_in_use;              /* write urb in use indicator */
+       int write_urb_interval;            /* interval to use for write urb */
+       int read_urb_interval;             /* interval to use for read urb */
+       int comm_is_ok;                    /* true if communication is (still) ok */
        int termios_initialized;
        __u8 line_control;                 /* holds dtr / rts value */
        __u8 current_status;               /* received from last read - info on dsr,cts,cd,ri,etc */
@@ -168,6 +171,7 @@ static int  cypress_tiocmset                (struct usb_serial_port *port, struct file *file,
 static int  cypress_chars_in_buffer    (struct usb_serial_port *port);
 static void cypress_throttle           (struct usb_serial_port *port);
 static void cypress_unthrottle         (struct usb_serial_port *port);
+static void cypress_set_dead           (struct usb_serial_port *port);
 static void cypress_read_int_callback  (struct urb *urb, struct pt_regs *regs);
 static void cypress_write_int_callback (struct urb *urb, struct pt_regs *regs);
 /* baud helper functions */
@@ -288,6 +292,9 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m
        
        priv = usb_get_serial_port_data(port);
 
+       if (!priv->comm_is_ok)
+               return -ENODEV;
+
        switch(cypress_request_type) {
                case CYPRESS_SET_CONFIG:
 
@@ -365,13 +372,12 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m
                                if (tries++ >= 3)
                                        break;
 
-                               if (retval == EPIPE)
-                                       usb_clear_halt(port->serial->dev, 0x00);
-                       } while (retval != 8 && retval != ENODEV);
+                       } while (retval != 8 && retval != -ENODEV);
 
-                       if (retval != 8)
+                       if (retval != 8) {
                                err("%s - failed sending serial line settings - %d", __FUNCTION__, retval);
-                       else {
+                               cypress_set_dead(port);
+                       } else {
                                spin_lock_irqsave(&priv->lock, flags);
                                priv->baud_rate = new_baudrate;
                                priv->cbr_mask = baud_mask;
@@ -392,12 +398,11 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m
                                if (tries++ >= 3)
                                        break;
 
-                               if (retval == EPIPE)
-                                       usb_clear_halt(port->serial->dev, 0x00);
-                       } while (retval != 5 && retval != ENODEV);
+                       } while (retval != 5 && retval != -ENODEV);
 
                        if (retval != 5) {
                                err("%s - failed to retrieve serial line settings - %d", __FUNCTION__, retval);
+                               cypress_set_dead(port);
                                return retval;
                        } else {
                                spin_lock_irqsave(&priv->lock, flags);
@@ -419,6 +424,24 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m
 } /* cypress_serial_control */
 
 
+static void cypress_set_dead(struct usb_serial_port *port)
+{
+       struct cypress_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (!priv->comm_is_ok) {
+               spin_unlock_irqrestore(&priv->lock, flags);
+               return;
+       }
+       priv->comm_is_ok = 0;
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       err("cypress_m8 suspending failing port %d - interval might be too short",
+           port->number);
+}
+
+
 /* given a baud mask, it will return integer baud on success */
 static int mask_to_rate (unsigned mask)
 {
@@ -472,13 +495,15 @@ static unsigned rate_to_mask (int rate)
 static int generic_startup (struct usb_serial *serial)
 {
        struct cypress_private *priv;
+       struct usb_serial_port *port = serial->port[0];
 
-       dbg("%s - port %d", __FUNCTION__, serial->port[0]->number);
+       dbg("%s - port %d", __FUNCTION__, port->number);
 
        priv = kzalloc(sizeof (struct cypress_private), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
+       priv->comm_is_ok = !0;
        spin_lock_init(&priv->lock);
        priv->buf = cypress_buf_alloc(CYPRESS_BUF_SIZE);
        if (priv->buf == NULL) {
@@ -489,13 +514,24 @@ static int generic_startup (struct usb_serial *serial)
        
        usb_reset_configuration (serial->dev);
        
-       interval = 1;
        priv->cmd_ctrl = 0;
        priv->line_control = 0;
        priv->termios_initialized = 0;
        priv->rx_flags = 0;
        priv->cbr_mask = B300;
-       usb_set_serial_port_data(serial->port[0], priv);
+       if (interval > 0) {
+               priv->write_urb_interval = interval;
+               priv->read_urb_interval = interval;
+               dbg("%s - port %d read & write intervals forced to %d",
+                   __FUNCTION__,port->number,interval);
+       } else {
+               priv->write_urb_interval = port->interrupt_out_urb->interval;
+               priv->read_urb_interval = port->interrupt_in_urb->interval;
+               dbg("%s - port %d intervals: read=%d write=%d",
+                   __FUNCTION__,port->number,
+                   priv->read_urb_interval,priv->write_urb_interval);
+       }
+       usb_set_serial_port_data(port, priv);
        
        return 0;
 }
@@ -585,6 +621,9 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp)
 
        dbg("%s - port %d", __FUNCTION__, port->number);
 
+       if (!priv->comm_is_ok)
+               return -EIO;
+
        /* clear halts before open */
        usb_clear_halt(serial->dev, 0x81);
        usb_clear_halt(serial->dev, 0x02);
@@ -624,11 +663,12 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp)
        usb_fill_int_urb(port->interrupt_in_urb, serial->dev,
                usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress),
                port->interrupt_in_urb->transfer_buffer, port->interrupt_in_urb->transfer_buffer_length,
-               cypress_read_int_callback, port, interval);
+               cypress_read_int_callback, port, priv->read_urb_interval);
        result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
 
        if (result){
                dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __FUNCTION__, result);
+               cypress_set_dead(port);
        }
 
        return result;
@@ -733,6 +773,9 @@ static void cypress_send(struct usb_serial_port *port)
        struct cypress_private *priv = usb_get_serial_port_data(port);
        unsigned long flags;
        
+       if (!priv->comm_is_ok)
+               return;
+
        dbg("%s - port %d", __FUNCTION__, port->number);
        dbg("%s - interrupt out size is %d", __FUNCTION__, port->interrupt_out_size);
        
@@ -806,14 +849,16 @@ send:
        usb_serial_debug_data(debug, &port->dev, __FUNCTION__, port->interrupt_out_size,
                              port->interrupt_out_urb->transfer_buffer);
 
-       port->interrupt_out_urb->transfer_buffer_length = actual_size;
-       port->interrupt_out_urb->dev = port->serial->dev;
-       port->interrupt_out_urb->interval = interval;
+       usb_fill_int_urb(port->interrupt_out_urb, port->serial->dev,
+               usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress),
+               port->interrupt_out_buffer, port->interrupt_out_size,
+               cypress_write_int_callback, port, priv->write_urb_interval);
        result = usb_submit_urb (port->interrupt_out_urb, GFP_ATOMIC);
        if (result) {
                dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__,
                        result);
                priv->write_urb_in_use = 0;
+               cypress_set_dead(port);
        }
 
        spin_lock_irqsave(&priv->lock, flags);
@@ -1214,13 +1259,18 @@ static void cypress_unthrottle (struct usb_serial_port *port)
        priv->rx_flags = 0;
        spin_unlock_irqrestore(&priv->lock, flags);
 
+       if (!priv->comm_is_ok)
+               return;
+
        if (actually_throttled) {
                port->interrupt_in_urb->dev = port->serial->dev;
 
                result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
-               if (result)
+               if (result) {
                        dev_err(&port->dev, "%s - failed submitting read urb, "
                                        "error %d\n", __FUNCTION__, result);
+                       cypress_set_dead(port);
+               }
        }
 }
 
@@ -1240,9 +1290,22 @@ static void cypress_read_int_callback(struct urb *urb, struct pt_regs *regs)
 
        dbg("%s - port %d", __FUNCTION__, port->number);
 
-       if (urb->status) {
-               dbg("%s - nonzero read status received: %d", __FUNCTION__,
-                               urb->status);
+       switch (urb->status) {
+       case 0: /* success */
+               break;
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               /* precursor to disconnect so just go away */
+               return;
+       case -EPIPE:
+               usb_clear_halt(port->serial->dev,0x81);
+               break;
+       default:
+               /* something ugly is going on... */
+               dev_err(&urb->dev->dev,"%s - unexpected nonzero read status received: %d\n",
+                       __FUNCTION__,urb->status);
+               cypress_set_dead(port);
                return;
        }
 
@@ -1343,18 +1406,20 @@ continue_read:
 
        /* Continue trying to always read... unless the port has closed. */
 
-       if (port->open_count > 0) {
+       if (port->open_count > 0 && priv->comm_is_ok) {
                usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev,
                                usb_rcvintpipe(port->serial->dev,
                                        port->interrupt_in_endpointAddress),
                                port->interrupt_in_urb->transfer_buffer,
                                port->interrupt_in_urb->transfer_buffer_length,
-                               cypress_read_int_callback, port, interval);
+                               cypress_read_int_callback, port, priv->read_urb_interval);
                result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
-               if (result)
+               if (result) {
                        dev_err(&urb->dev->dev, "%s - failed resubmitting "
                                        "read urb, error %d\n", __FUNCTION__,
                                        result);
+                       cypress_set_dead(port);
+               }
        }
 
        return;
@@ -1380,20 +1445,26 @@ static void cypress_write_int_callback(struct urb *urb, struct pt_regs *regs)
                        dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
                        priv->write_urb_in_use = 0;
                        return;
-               case -EPIPE: /* no break needed */
+               case -EPIPE: /* no break needed; clear halt and resubmit */
+                       if (!priv->comm_is_ok)
+                               break;
                        usb_clear_halt(port->serial->dev, 0x02);
-               default:
                        /* error in the urb, so we have to resubmit it */
-                       dbg("%s - Overflow in write", __FUNCTION__);
                        dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
                        port->interrupt_out_urb->transfer_buffer_length = 1;
                        port->interrupt_out_urb->dev = port->serial->dev;
                        result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC);
-                       if (result)
-                               dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n",
-                                       __FUNCTION__, result);
-                       else
+                       if (!result)
                                return;
+                       dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n",
+                               __FUNCTION__, result);
+                       cypress_set_dead(port);
+                       break;
+               default:
+                       dev_err(&urb->dev->dev,"%s - unexpected nonzero write status received: %d\n",
+                               __FUNCTION__,urb->status);
+                       cypress_set_dead(port);
+                       break;
        }
        
        priv->write_urb_in_use = 0;
index c6115aa..1f7b725 100644 (file)
@@ -1101,25 +1101,29 @@ static ssize_t store_event_char(struct device *dev, struct device_attribute *att
 static DEVICE_ATTR(latency_timer, S_IWUSR | S_IRUGO, show_latency_timer, store_latency_timer);
 static DEVICE_ATTR(event_char, S_IWUSR, NULL, store_event_char);
 
-static void create_sysfs_attrs(struct usb_serial *serial)
-{      
+static int create_sysfs_attrs(struct usb_serial *serial)
+{
        struct ftdi_private *priv;
        struct usb_device *udev;
+       int retval = 0;
 
        dbg("%s",__FUNCTION__);
-       
+
        priv = usb_get_serial_port_data(serial->port[0]);
        udev = serial->dev;
-       
+
        /* XXX I've no idea if the original SIO supports the event_char
         * sysfs parameter, so I'm playing it safe.  */
        if (priv->chip_type != SIO) {
                dbg("sysfs attributes for %s", ftdi_chip_name[priv->chip_type]);
-               device_create_file(&udev->dev, &dev_attr_event_char);
-               if (priv->chip_type == FT232BM || priv->chip_type == FT2232C) {
-                       device_create_file(&udev->dev, &dev_attr_latency_timer);
+               retval = device_create_file(&udev->dev, &dev_attr_event_char);
+               if ((!retval) &&
+                   (priv->chip_type == FT232BM || priv->chip_type == FT2232C)) {
+                       retval = device_create_file(&udev->dev,
+                                                   &dev_attr_latency_timer);
                }
        }
+       return retval;
 }
 
 static void remove_sysfs_attrs(struct usb_serial *serial)
@@ -1162,7 +1166,8 @@ static int ftdi_sio_attach (struct usb_serial *serial)
        struct usb_serial_port *port = serial->port[0];
        struct ftdi_private *priv;
        struct ftdi_sio_quirk *quirk;
-       
+       int retval;
+
        dbg("%s",__FUNCTION__);
 
        priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL);
@@ -1203,15 +1208,18 @@ static int ftdi_sio_attach (struct usb_serial *serial)
        usb_set_serial_port_data(serial->port[0], priv);
 
        ftdi_determine_type (serial->port[0]);
-       create_sysfs_attrs(serial);
+       retval = create_sysfs_attrs(serial);
+       if (retval)
+               dev_err(&serial->dev->dev, "Error creating sysfs files, "
+                       "continuing\n");
 
        /* Check for device requiring special set up. */
        quirk = (struct ftdi_sio_quirk *)usb_get_serial_data(serial);
        if (quirk && quirk->setup) {
                quirk->setup(serial);
        }
-       
-       return (0);
+
+       return 0;
 } /* ftdi_sio_attach */
 
 
index 7278526..4b1196a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Garmin GPS driver
  *
- * Copyright (C) 2004 Hermann Kneissel herkne@users.sourceforge.net
+ * Copyright (C) 2006 Hermann Kneissel herkne@users.sourceforge.net
  *
  * The latest version of the driver can be found at
  * http://sourceforge.net/projects/garmin-gps/
@@ -37,6 +37,8 @@
 #include <linux/usb.h>
 #include <linux/usb/serial.h>
 
+#include <linux/version.h>
+
 /* the mode to be set when the port ist opened */
 static int initial_mode = 1;
 
@@ -50,7 +52,7 @@ static int debug = 0;
  */
 
 #define VERSION_MAJOR  0
-#define VERSION_MINOR  23
+#define VERSION_MINOR  28
 
 #define _STR(s) #s
 #define _DRIVER_VERSION(a,b) "v" _STR(a) "." _STR(b)
@@ -164,7 +166,8 @@ struct garmin_data {
 #define FLAGS_SESSION_REPLY1_SEEN 0x0080
 #define FLAGS_SESSION_REPLY2_SEEN 0x0040
 #define FLAGS_BULK_IN_ACTIVE      0x0020
-#define FLAGS_THROTTLED           0x0010
+#define FLAGS_BULK_IN_RESTART     0x0010
+#define FLAGS_THROTTLED           0x0008
 #define CLEAR_HALT_REQUIRED       0x0001
 
 #define FLAGS_QUEUING             0x0100
@@ -224,7 +227,7 @@ static struct usb_driver garmin_driver = {
        .probe =        usb_serial_probe,
        .disconnect =   usb_serial_disconnect,
        .id_table =     id_table,
-       .no_dynamic_id =        1,
+       .no_dynamic_id = 1,
 };
 
 
@@ -270,7 +273,7 @@ static inline int isAbortTrfCmnd(const unsigned char *buf)
 
 
 static void send_to_tty(struct usb_serial_port *port,
-                        char *data, unsigned int actual_length)
+                       char *data, unsigned int actual_length)
 {
        struct tty_struct *tty = port->tty;
 
@@ -294,15 +297,15 @@ static void send_to_tty(struct usb_serial_port *port,
  * queue a received (usb-)packet for later processing
  */
 static int pkt_add(struct garmin_data * garmin_data_p,
-                   unsigned char *data, unsigned int data_length)
+                  unsigned char *data, unsigned int data_length)
 {
+       int state = 0;
        int result = 0;
        unsigned long flags;
        struct garmin_packet *pkt;
 
        /* process only packets containg data ... */
        if (data_length) {
-               garmin_data_p->flags |= FLAGS_QUEUING;
                pkt = kmalloc(sizeof(struct garmin_packet)+data_length,
                              GFP_ATOMIC);
                if (pkt == NULL) {
@@ -313,14 +316,16 @@ static int pkt_add(struct garmin_data * garmin_data_p,
                memcpy(pkt->data, data, data_length);
 
                spin_lock_irqsave(&garmin_data_p->lock, flags);
+               garmin_data_p->flags |= FLAGS_QUEUING;
                result = list_empty(&garmin_data_p->pktlist);
                pkt->seq = garmin_data_p->seq_counter++;
                list_add_tail(&pkt->list, &garmin_data_p->pktlist);
+               state = garmin_data_p->state;
                spin_unlock_irqrestore(&garmin_data_p->lock, flags);
 
                /* in serial mode, if someone is waiting for data from
                   the device, iconvert and send the next packet to tty. */
-               if (result && (garmin_data_p->state == STATE_GSP_WAIT_DATA)) {
+               if (result && (state == STATE_GSP_WAIT_DATA)) {
                        gsp_next_packet(garmin_data_p);
                }
        }
@@ -370,9 +375,9 @@ static void pkt_clear(struct garmin_data * garmin_data_p)
 static int gsp_send_ack(struct garmin_data * garmin_data_p, __u8 pkt_id)
 {
        __u8 pkt[10];
-        __u8 cksum = 0;
-        __u8 *ptr = pkt;
-        unsigned  l = 0;
+       __u8 cksum = 0;
+       __u8 *ptr = pkt;
+       unsigned  l = 0;
 
        dbg("%s - pkt-id: 0x%X.", __FUNCTION__, 0xFF & pkt_id);
 
@@ -416,7 +421,7 @@ static int gsp_send_ack(struct garmin_data * garmin_data_p, __u8 pkt_id)
 static int gsp_rec_packet(struct garmin_data * garmin_data_p, int count)
 {
        const __u8* recpkt = garmin_data_p->inbuffer+GSP_INITIAL_OFFSET;
-        __le32 *usbdata = (__le32 *) garmin_data_p->inbuffer;
+       __le32 *usbdata = (__le32 *) garmin_data_p->inbuffer;
 
        int cksum = 0;
        int n = 0;
@@ -447,11 +452,11 @@ static int gsp_rec_packet(struct garmin_data * garmin_data_p, int count)
                n++;
        }
 
-       if ((0xff & (cksum + *recpkt)) != 0) {
-                dbg("%s - invalid checksum, expected %02x, got %02x",
-                        __FUNCTION__, 0xff & -cksum, 0xff & *recpkt);
-                return -EINVPKT;
-        }
+       if ((0xff & (cksum + *recpkt)) != 0) {
+               dbg("%s - invalid checksum, expected %02x, got %02x",
+                       __FUNCTION__, 0xff & -cksum, 0xff & *recpkt);
+               return -EINVPKT;
+       }
 
        usbdata[0] = __cpu_to_le32(GARMIN_LAYERID_APPL);
        usbdata[1] = __cpu_to_le32(pktid);
@@ -491,20 +496,28 @@ static int gsp_rec_packet(struct garmin_data * garmin_data_p, int count)
  */
 
 static int gsp_receive(struct garmin_data * garmin_data_p,
-                       const unsigned char *buf, int count)
+                      const unsigned char *buf, int count)
 {
+       unsigned long flags;
        int offs = 0;
        int ack_or_nak_seen = 0;
        int i = 0;
-       __u8 *dest = garmin_data_p->inbuffer;
-       int size = garmin_data_p->insize;
+       __u8 *dest;
+       int size;
        // dleSeen: set if last byte read was a DLE
-       int dleSeen = garmin_data_p->flags & FLAGS_GSP_DLESEEN;
+       int dleSeen;
        // skip: if set, skip incoming data until possible start of
        //       new packet
-       int skip = garmin_data_p->flags & FLAGS_GSP_SKIP;
+       int skip;
        __u8 data;
 
+       spin_lock_irqsave(&garmin_data_p->lock, flags);
+       dest = garmin_data_p->inbuffer;
+       size = garmin_data_p->insize;
+       dleSeen = garmin_data_p->flags & FLAGS_GSP_DLESEEN;
+       skip = garmin_data_p->flags & FLAGS_GSP_SKIP;
+       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+
        dbg("%s - dle=%d skip=%d size=%d count=%d",
                __FUNCTION__, dleSeen, skip, size, count);
 
@@ -572,6 +585,8 @@ static int gsp_receive(struct garmin_data * garmin_data_p,
                }
        }
 
+       spin_lock_irqsave(&garmin_data_p->lock, flags);
+
        garmin_data_p->insize = size;
 
        // copy flags back to structure
@@ -587,6 +602,11 @@ static int gsp_receive(struct garmin_data * garmin_data_p,
 
        if (ack_or_nak_seen) {
                garmin_data_p->state = STATE_GSP_WAIT_DATA;
+       }
+
+       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+
+       if (ack_or_nak_seen) {
                gsp_next_packet(garmin_data_p);
        }
 
@@ -676,7 +696,7 @@ static int gsp_send(struct garmin_data * garmin_data_p,
        src = garmin_data_p->outbuffer+GARMIN_PKTHDR_LENGTH;
        if (k > (GARMIN_PKTHDR_LENGTH-2)) {
                /* can't add stuffing DLEs in place, move data to end 
-                   of buffer ... */
+                  of buffer ... */
                dst = garmin_data_p->outbuffer+GPS_OUT_BUFSIZ-datalen;
                memcpy(dst, src, datalen);
                src = dst;
@@ -755,8 +775,9 @@ static void gsp_next_packet(struct garmin_data * garmin_data_p)
  * or even incomplete packets
  */
 static int nat_receive(struct garmin_data * garmin_data_p,
-                       const unsigned char *buf, int count)
+                      const unsigned char *buf, int count)
 {
+       unsigned long flags;
        __u8 * dest;
        int offs = 0;
        int result = count;
@@ -803,7 +824,9 @@ static int nat_receive(struct garmin_data * garmin_data_p,
                                /* if this was an abort-transfer command,
                                   flush all queued data. */
                                if (isAbortTrfCmnd(garmin_data_p->inbuffer)) {
+                                       spin_lock_irqsave(&garmin_data_p->lock, flags);
                                        garmin_data_p->flags |= FLAGS_DROP_DATA;
+                                       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
                                        pkt_clear(garmin_data_p);
                                }
                        }
@@ -839,12 +862,15 @@ static void priv_status_resp(struct usb_serial_port *port)
 
 static int process_resetdev_request(struct usb_serial_port *port)
 {
+       unsigned long flags;
        int status;
        struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
 
+       spin_lock_irqsave(&garmin_data_p->lock, flags);
        garmin_data_p->flags &= ~(CLEAR_HALT_REQUIRED);
        garmin_data_p->state = STATE_RESET;
        garmin_data_p->serial_num = 0;
+       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
 
        usb_kill_urb (port->interrupt_in_urb);
        dbg("%s - usb_reset_device", __FUNCTION__ );
@@ -862,6 +888,7 @@ static int process_resetdev_request(struct usb_serial_port *port)
  */
 static int garmin_clear(struct garmin_data * garmin_data_p)
 {
+       unsigned long flags;
        int status = 0;
 
        struct usb_serial_port *port = garmin_data_p->port;
@@ -875,8 +902,10 @@ static int garmin_clear(struct garmin_data * garmin_data_p)
        /* flush all queued data */
        pkt_clear(garmin_data_p);
 
+       spin_lock_irqsave(&garmin_data_p->lock, flags);
        garmin_data_p->insize = 0;
        garmin_data_p->outsize = 0;
+       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
 
        return status;
 }
@@ -888,6 +917,7 @@ static int garmin_clear(struct garmin_data * garmin_data_p)
 
 static int garmin_init_session(struct usb_serial_port *port)
 {
+       unsigned long flags;
        struct usb_serial *serial = port->serial;
        struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
        int status = 0;
@@ -913,7 +943,9 @@ static int garmin_init_session(struct usb_serial_port *port)
 
                if (status >= 0) {
 
+                       spin_lock_irqsave(&garmin_data_p->lock, flags);
                        garmin_data_p->ignorePkts++;
+                       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
 
                        /* not needed, but the win32 driver does it too ... */
                        status = garmin_write_bulk(port,
@@ -921,7 +953,9 @@ static int garmin_init_session(struct usb_serial_port *port)
                                                   sizeof(GARMIN_START_SESSION_REQ2));
                        if (status >= 0) {
                                status = 0;
+                               spin_lock_irqsave(&garmin_data_p->lock, flags);
                                garmin_data_p->ignorePkts++;
+                               spin_unlock_irqrestore(&garmin_data_p->lock, flags);
                        }
                }
        }
@@ -935,6 +969,7 @@ static int garmin_init_session(struct usb_serial_port *port)
 
 static int garmin_open (struct usb_serial_port *port, struct file *filp)
 {
+       unsigned long flags;
        int status = 0;
        struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
 
@@ -948,9 +983,11 @@ static int garmin_open (struct usb_serial_port *port, struct file *filp)
        if (port->tty)
                port->tty->low_latency = 1;
 
+       spin_lock_irqsave(&garmin_data_p->lock, flags);
        garmin_data_p->mode  = initial_mode;
        garmin_data_p->count = 0;
        garmin_data_p->flags = 0;
+       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
 
        /* shutdown any bulk reads that might be going on */
        usb_kill_urb (port->write_urb);
@@ -996,6 +1033,7 @@ static void garmin_close (struct usb_serial_port *port, struct file * filp)
 
 static void garmin_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
 {
+       unsigned long flags;
        struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
        struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
 
@@ -1007,7 +1045,9 @@ static void garmin_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
        if (urb->status) {
                dbg("%s - nonzero write bulk status received: %d",
                        __FUNCTION__, urb->status);
+               spin_lock_irqsave(&garmin_data_p->lock, flags);
                garmin_data_p->flags |= CLEAR_HALT_REQUIRED;
+               spin_unlock_irqrestore(&garmin_data_p->lock, flags);
        }
 
        usb_serial_port_softint(port);
@@ -1015,8 +1055,9 @@ static void garmin_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
 
 
 static int garmin_write_bulk (struct usb_serial_port *port,
-                              const unsigned char *buf, int count)
+                             const unsigned char *buf, int count)
 {
+       unsigned long flags;
        struct usb_serial *serial = port->serial;
        struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
        struct urb *urb;
@@ -1026,7 +1067,9 @@ static int garmin_write_bulk (struct usb_serial_port *port,
        dbg("%s - port %d, state %d", __FUNCTION__, port->number,
                garmin_data_p->state);
 
+       spin_lock_irqsave(&garmin_data_p->lock, flags);
        garmin_data_p->flags &= ~FLAGS_DROP_DATA;
+       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
 
        buffer = kmalloc (count, GFP_ATOMIC);
        if (!buffer) {
@@ -1053,7 +1096,9 @@ static int garmin_write_bulk (struct usb_serial_port *port,
        urb->transfer_flags |= URB_ZERO_PACKET;
 
        if (GARMIN_LAYERID_APPL == getLayerId(buffer)) {
+               spin_lock_irqsave(&garmin_data_p->lock, flags);
                garmin_data_p->flags |= FLAGS_APP_REQ_SEEN;
+               spin_unlock_irqrestore(&garmin_data_p->lock, flags);
                if (garmin_data_p->mode == MODE_GARMIN_SERIAL)  {
                        pkt_clear(garmin_data_p);
                        garmin_data_p->state = STATE_GSP_WAIT_DATA;
@@ -1087,8 +1132,9 @@ static int garmin_write_bulk (struct usb_serial_port *port,
 
 
 static int garmin_write (struct usb_serial_port *port,
-                         const unsigned char *buf, int count)
+                        const unsigned char *buf, int count)
 {
+       unsigned long flags;
        int pktid, pktsiz, len;
        struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
        __le32 *privpkt = (__le32 *)garmin_data_p->privpkt;
@@ -1139,7 +1185,9 @@ static int garmin_write (struct usb_serial_port *port,
                                break;
 
                        case PRIV_PKTID_RESET_REQ:
+                               spin_lock_irqsave(&garmin_data_p->lock, flags);
                                garmin_data_p->flags |= FLAGS_APP_REQ_SEEN;
+                               spin_unlock_irqrestore(&garmin_data_p->lock, flags);
                                break;
 
                        case PRIV_PKTID_SET_DEF_MODE:
@@ -1155,6 +1203,8 @@ static int garmin_write (struct usb_serial_port *port,
                }
        }
 
+       garmin_data_p->ignorePkts = 0;
+
        if (garmin_data_p->mode == MODE_GARMIN_SERIAL) {
                return gsp_receive(garmin_data_p, buf, count);
        } else {        /* MODE_NATIVE */
@@ -1177,10 +1227,10 @@ static int garmin_chars_in_buffer (struct usb_serial_port *port)
 {
        /*
         * Report back the number of bytes currently in our input buffer.
-         * Will this lock up the driver - the buffer contains an incomplete
-         * package which will not be written to the device until it
-         * has been completed ?
-         */
+        * Will this lock up the driver - the buffer contains an incomplete
+        * package which will not be written to the device until it
+        * has been completed ?
+        */
        //struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
        //return garmin_data_p->insize;
        return 0;
@@ -1190,6 +1240,8 @@ static int garmin_chars_in_buffer (struct usb_serial_port *port)
 static void garmin_read_process(struct garmin_data * garmin_data_p,
                                 unsigned char *data, unsigned data_length)
 {
+       unsigned long flags;
+
        if (garmin_data_p->flags & FLAGS_DROP_DATA) {
                /* abort-transfer cmd is actice */
                dbg("%s - pkt dropped", __FUNCTION__);
@@ -1200,11 +1252,14 @@ static void garmin_read_process(struct garmin_data * garmin_data_p,
                   if a reset is required or not when closing
                   the device */
                if (0 == memcmp(data, GARMIN_APP_LAYER_REPLY,
-                               sizeof(GARMIN_APP_LAYER_REPLY)))
+                               sizeof(GARMIN_APP_LAYER_REPLY))) {
+                       spin_lock_irqsave(&garmin_data_p->lock, flags);
                        garmin_data_p->flags |= FLAGS_APP_RESP_SEEN;
+                       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+               }
 
                /* if throttling is active or postprecessing is required
-                  put the received data in th input queue, otherwise
+                  put the received data in the input queue, otherwise
                   send it directly to the tty port */
                if (garmin_data_p->flags & FLAGS_QUEUING) {
                        pkt_add(garmin_data_p, data, data_length);
@@ -1221,6 +1276,7 @@ static void garmin_read_process(struct garmin_data * garmin_data_p,
 
 static void garmin_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
 {
+       unsigned long flags;
        struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
        struct usb_serial *serial =  port->serial;
        struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
@@ -1245,19 +1301,30 @@ static void garmin_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
 
        garmin_read_process(garmin_data_p, data, urb->actual_length);
 
-       /* Continue trying to read until nothing more is received  */
-       if (urb->actual_length > 0) {
-               usb_fill_bulk_urb (port->read_urb, serial->dev,
-                          usb_rcvbulkpipe (serial->dev,
-                                           port->bulk_in_endpointAddress),
-                          port->read_urb->transfer_buffer,
-                          port->read_urb->transfer_buffer_length,
-                          garmin_read_bulk_callback, port);
+       if (urb->actual_length == 0 &&
+                       0 != (garmin_data_p->flags & FLAGS_BULK_IN_RESTART)) {
+               spin_lock_irqsave(&garmin_data_p->lock, flags);
+               garmin_data_p->flags &= ~FLAGS_BULK_IN_RESTART;
+               spin_unlock_irqrestore(&garmin_data_p->lock, flags);
                status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
                if (status)
                        dev_err(&port->dev,
                                "%s - failed resubmitting read urb, error %d\n",
                                __FUNCTION__, status);
+       } else if (urb->actual_length > 0) {
+               /* Continue trying to read until nothing more is received  */
+               if (0 == (garmin_data_p->flags & FLAGS_THROTTLED)) {
+                       status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+                       if (status)
+                               dev_err(&port->dev,
+                                       "%s - failed resubmitting read urb, error %d\n",
+                                       __FUNCTION__, status);
+               }
+       } else {
+               dbg("%s - end of bulk data", __FUNCTION__);
+               spin_lock_irqsave(&garmin_data_p->lock, flags);
+               garmin_data_p->flags &= ~FLAGS_BULK_IN_ACTIVE;
+               spin_unlock_irqrestore(&garmin_data_p->lock, flags);
        }
        return;
 }
@@ -1265,6 +1332,7 @@ static void garmin_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
 
 static void garmin_read_int_callback (struct urb *urb, struct pt_regs *regs)
 {
+       unsigned long flags;
        int status;
        struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
        struct usb_serial *serial = port->serial;
@@ -1297,25 +1365,41 @@ static void garmin_read_int_callback (struct urb *urb, struct pt_regs *regs)
 
                dbg("%s - bulk data available.", __FUNCTION__);
 
-               /* bulk data available */
-               usb_fill_bulk_urb (port->read_urb, serial->dev,
-                               usb_rcvbulkpipe (serial->dev,
-                               port->bulk_in_endpointAddress),
-                               port->read_urb->transfer_buffer,
-                               port->read_urb->transfer_buffer_length,
-                               garmin_read_bulk_callback, port);
-               status = usb_submit_urb(port->read_urb, GFP_KERNEL);
-               if (status) {
-                       dev_err(&port->dev,
-                               "%s - failed submitting read urb, error %d\n",
-                       __FUNCTION__, status);
+               if (0 == (garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE)) {
+
+                       /* bulk data available */
+                       usb_fill_bulk_urb (port->read_urb, serial->dev,
+                                       usb_rcvbulkpipe (serial->dev,
+                                       port->bulk_in_endpointAddress),
+                                       port->read_urb->transfer_buffer,
+                                       port->read_urb->transfer_buffer_length,
+                                       garmin_read_bulk_callback, port);
+                       status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+                       if (status) {
+                               dev_err(&port->dev,
+                                       "%s - failed submitting read urb, error %d\n",
+                               __FUNCTION__, status);
+                       } else {
+                               spin_lock_irqsave(&garmin_data_p->lock, flags);
+                               garmin_data_p->flags |= FLAGS_BULK_IN_ACTIVE;
+                               /* do not send this packet to the user */
+                               garmin_data_p->ignorePkts = 1;
+                               spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+                       }
+               } else {
+                       /* bulk-in transfer still active */
+                       spin_lock_irqsave(&garmin_data_p->lock, flags);
+                       garmin_data_p->flags |= FLAGS_BULK_IN_RESTART;
+                       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
                }
 
        } else if (urb->actual_length == (4+sizeof(GARMIN_START_SESSION_REPLY))
                         && 0 == memcmp(data, GARMIN_START_SESSION_REPLY,
                                        sizeof(GARMIN_START_SESSION_REPLY))) {
 
+               spin_lock_irqsave(&garmin_data_p->lock, flags);
                garmin_data_p->flags |= FLAGS_SESSION_REPLY1_SEEN;
+               spin_unlock_irqrestore(&garmin_data_p->lock, flags);
 
                /* save the serial number */
                garmin_data_p->serial_num 
@@ -1330,7 +1414,9 @@ static void garmin_read_int_callback (struct urb *urb, struct pt_regs *regs)
                   ignore it. */
                dbg("%s - pkt ignored (%d)",
                        __FUNCTION__, garmin_data_p->ignorePkts);
+               spin_lock_irqsave(&garmin_data_p->lock, flags);
                garmin_data_p->ignorePkts--;
+               spin_unlock_irqrestore(&garmin_data_p->lock, flags);
        } else {
                garmin_read_process(garmin_data_p, data, urb->actual_length);
        }
@@ -1351,18 +1437,20 @@ static void garmin_read_int_callback (struct urb *urb, struct pt_regs *regs)
  */
 static int garmin_flush_queue(struct garmin_data * garmin_data_p)
 {
+       unsigned long flags;
        struct garmin_packet *pkt;
 
        if ((garmin_data_p->flags & FLAGS_THROTTLED) == 0) {
                pkt = pkt_pop(garmin_data_p);
                if (pkt != NULL) {
-
                        send_to_tty(garmin_data_p->port, pkt->data, pkt->size);
                        kfree(pkt);
                        mod_timer(&garmin_data_p->timer, (1)+jiffies);
 
                } else {
+                       spin_lock_irqsave(&garmin_data_p->lock, flags);
                        garmin_data_p->flags &= ~FLAGS_QUEUING;
+                       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
                }
        }
        return 0;
@@ -1371,26 +1459,41 @@ static int garmin_flush_queue(struct garmin_data * garmin_data_p)
 
 static void garmin_throttle (struct usb_serial_port *port)
 {
+       unsigned long flags;
        struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
 
        dbg("%s - port %d", __FUNCTION__, port->number);
        /* set flag, data received will be put into a queue
           for later processing */
+       spin_lock_irqsave(&garmin_data_p->lock, flags);
        garmin_data_p->flags |= FLAGS_QUEUING|FLAGS_THROTTLED;
+       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
 }
 
 
 static void garmin_unthrottle (struct usb_serial_port *port)
 {
+       unsigned long flags;
        struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+       int status;
 
        dbg("%s - port %d", __FUNCTION__, port->number);
+       spin_lock_irqsave(&garmin_data_p->lock, flags);
        garmin_data_p->flags &= ~FLAGS_THROTTLED;
+       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
 
        /* in native mode send queued data to tty, in
           serial mode nothing needs to be done here */
        if (garmin_data_p->mode == MODE_NATIVE)
                garmin_flush_queue(garmin_data_p);
+
+       if (0 != (garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE)) {
+               status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+               if (status)
+                       dev_err(&port->dev,
+                               "%s - failed resubmitting read urb, error %d\n",
+                               __FUNCTION__, status);
+       }
 }
 
 
@@ -1420,11 +1523,12 @@ static int garmin_attach (struct usb_serial *serial)
 
        dbg("%s", __FUNCTION__);
 
-       garmin_data_p = kzalloc(sizeof(struct garmin_data), GFP_KERNEL);
+       garmin_data_p = kmalloc (sizeof(struct garmin_data), GFP_KERNEL);
        if (garmin_data_p == NULL) {
                dev_err(&port->dev, "%s - Out of memory\n", __FUNCTION__);
                return -ENOMEM;
        }
+       memset (garmin_data_p, 0, sizeof(struct garmin_data));
        init_timer(&garmin_data_p->timer);
        spin_lock_init(&garmin_data_p->lock);
        INIT_LIST_HEAD(&garmin_data_p->pktlist);
@@ -1459,10 +1563,10 @@ static void garmin_shutdown (struct usb_serial *serial)
 /* All of the device info needed */
 static struct usb_serial_driver garmin_device = {
        .driver = {
-               .owner =        THIS_MODULE,
-               .name =         "garmin_gps",
+               .owner       = THIS_MODULE,
+               .name        = "garmin_gps",
        },
-       .description =          "Garmin GPS usb/tty",
+       .description         = "Garmin GPS usb/tty",
        .id_table            = id_table,
        .num_interrupt_in    = 1,
        .num_bulk_in         = 1,
@@ -1483,6 +1587,7 @@ static struct usb_serial_driver garmin_device = {
 };
 
 
+
 static int __init garmin_init (void)
 {
        int retval;
index 9840bad..bfc6998 100644 (file)
@@ -652,11 +652,6 @@ static int ipaq_open(struct usb_serial_port *port, struct file *filp)
        port->bulk_out_size = port->write_urb->transfer_buffer_length = URBDATA_SIZE;
        
        msleep(1000*initial_wait);
-       /* Start reading from the device */
-       usb_fill_bulk_urb(port->read_urb, serial->dev, 
-                     usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
-                     port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
-                     ipaq_read_bulk_callback, port);
 
        /*
         * Send out control message observed in win98 sniffs. Not sure what
@@ -670,18 +665,31 @@ static int ipaq_open(struct usb_serial_port *port, struct file *filp)
                result = usb_control_msg(serial->dev,
                                usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21,
                                0x1, 0, NULL, 0, 100);
-               if (result == 0) {
-                       result = usb_submit_urb(port->read_urb, GFP_KERNEL);
-                       if (result) {
-                               err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
-                               goto error;
-                       }
-                       return 0;
-               }
+               if (!result)
+                       break;
+
                msleep(1000);
        }
-       err("%s - failed doing control urb, error %d", __FUNCTION__, result);
-       goto error;
+
+       if (!retries && result) {
+               err("%s - failed doing control urb, error %d", __FUNCTION__,
+                   result);
+               goto error;
+       }
+
+       /* Start reading from the device */
+       usb_fill_bulk_urb(port->read_urb, serial->dev,
+                     usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+                     port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
+                     ipaq_read_bulk_callback, port);
+
+       result = usb_submit_urb(port->read_urb, GFP_KERNEL);
+       if (result) {
+               err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
+               goto error;
+       }
+
+       return 0;
 
 enomem:
        result = -ENOMEM;
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
new file mode 100644 (file)
index 0000000..95bf571
--- /dev/null
@@ -0,0 +1,2962 @@
+/*
+ * 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
+ *
+ * Clean ups from Moschip version and a few ioctl implementations by:
+ *     Paul B Schroeder <pschroeder "at" uplogix "dot" com>
+ *
+ * Originally based on drivers/usb/serial/io_edgeport.c which is:
+ *      Copyright (C) 2000 Inside Out Networks, All rights reserved.
+ *      Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <asm/uaccess.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "1.3.1"
+#define DRIVER_DESC "Moschip 7840/7820 USB Serial Driver"
+
+/*
+ * 16C50 UART register defines
+ */
+
+#define LCR_BITS_5             0x00    /* 5 bits/char */
+#define LCR_BITS_6             0x01    /* 6 bits/char */
+#define LCR_BITS_7             0x02    /* 7 bits/char */
+#define LCR_BITS_8             0x03    /* 8 bits/char */
+#define LCR_BITS_MASK          0x03    /* Mask for bits/char field */
+
+#define LCR_STOP_1             0x00    /* 1 stop bit */
+#define LCR_STOP_1_5           0x04    /* 1.5 stop bits (if 5   bits/char) */
+#define LCR_STOP_2             0x04    /* 2 stop bits   (if 6-8 bits/char) */
+#define LCR_STOP_MASK          0x04    /* Mask for stop bits field */
+
+#define LCR_PAR_NONE           0x00    /* No parity */
+#define LCR_PAR_ODD            0x08    /* Odd parity */
+#define LCR_PAR_EVEN           0x18    /* Even parity */
+#define LCR_PAR_MARK           0x28    /* Force parity bit to 1 */
+#define LCR_PAR_SPACE          0x38    /* Force parity bit to 0 */
+#define LCR_PAR_MASK           0x38    /* Mask for parity field */
+
+#define LCR_SET_BREAK          0x40    /* Set Break condition */
+#define LCR_DL_ENABLE          0x80    /* Enable access to divisor latch */
+
+#define MCR_DTR                0x01    /* Assert DTR */
+#define MCR_RTS                0x02    /* Assert RTS */
+#define MCR_OUT1               0x04    /* Loopback only: Sets state of RI */
+#define MCR_MASTER_IE          0x08    /* Enable interrupt outputs */
+#define MCR_LOOPBACK           0x10    /* Set internal (digital) loopback mode */
+#define MCR_XON_ANY            0x20    /* Enable any char to exit XOFF mode */
+
+#define MOS7840_MSR_CTS        0x10    /* Current state of CTS */
+#define MOS7840_MSR_DSR        0x20    /* Current state of DSR */
+#define MOS7840_MSR_RI         0x40    /* Current state of RI */
+#define MOS7840_MSR_CD         0x80    /* Current state of CD */
+
+/*
+ * Defines used for sending commands to port
+ */
+
+#define WAIT_FOR_EVER   (HZ * 0 )      /* timeout urb is wait for ever */
+#define MOS_WDR_TIMEOUT (HZ * 5 )      /* default urb timeout */
+
+#define MOS_PORT1       0x0200
+#define MOS_PORT2       0x0300
+#define MOS_VENREG      0x0000
+#define MOS_MAX_PORT   0x02
+#define MOS_WRITE       0x0E
+#define MOS_READ        0x0D
+
+/* Requests */
+#define MCS_RD_RTYPE    0xC0
+#define MCS_WR_RTYPE    0x40
+#define MCS_RDREQ       0x0D
+#define MCS_WRREQ       0x0E
+#define MCS_CTRL_TIMEOUT        500
+#define VENDOR_READ_LENGTH      (0x01)
+
+#define MAX_NAME_LEN    64
+
+#define ZLP_REG1  0x3A         //Zero_Flag_Reg1    58
+#define ZLP_REG5  0x3E         //Zero_Flag_Reg5    62
+
+/* For higher baud Rates use TIOCEXBAUD */
+#define TIOCEXBAUD     0x5462
+
+/* vendor id and device id defines */
+
+#define USB_VENDOR_ID_MOSCHIP           0x9710
+#define MOSCHIP_DEVICE_ID_7840          0x7840
+#define MOSCHIP_DEVICE_ID_7820          0x7820
+
+/* Interrupt Rotinue Defines    */
+
+#define SERIAL_IIR_RLS      0x06
+#define SERIAL_IIR_MS       0x00
+
+/*
+ *  Emulation of the bit mask on the LINE STATUS REGISTER.
+ */
+#define SERIAL_LSR_DR       0x0001
+#define SERIAL_LSR_OE       0x0002
+#define SERIAL_LSR_PE       0x0004
+#define SERIAL_LSR_FE       0x0008
+#define SERIAL_LSR_BI       0x0010
+
+#define MOS_MSR_DELTA_CTS   0x10
+#define MOS_MSR_DELTA_DSR   0x20
+#define MOS_MSR_DELTA_RI    0x40
+#define MOS_MSR_DELTA_CD    0x80
+
+// Serial Port register Address
+#define INTERRUPT_ENABLE_REGISTER  ((__u16)(0x01))
+#define FIFO_CONTROL_REGISTER      ((__u16)(0x02))
+#define LINE_CONTROL_REGISTER      ((__u16)(0x03))
+#define MODEM_CONTROL_REGISTER     ((__u16)(0x04))
+#define LINE_STATUS_REGISTER       ((__u16)(0x05))
+#define MODEM_STATUS_REGISTER      ((__u16)(0x06))
+#define SCRATCH_PAD_REGISTER       ((__u16)(0x07))
+#define DIVISOR_LATCH_LSB          ((__u16)(0x00))
+#define DIVISOR_LATCH_MSB          ((__u16)(0x01))
+
+#define CLK_MULTI_REGISTER         ((__u16)(0x02))
+#define CLK_START_VALUE_REGISTER   ((__u16)(0x03))
+
+#define SERIAL_LCR_DLAB            ((__u16)(0x0080))
+
+/*
+ * URB POOL related defines
+ */
+#define NUM_URBS                        16     /* URB Count */
+#define URB_TRANSFER_BUFFER_SIZE        32     /* URB Size  */
+
+
+static struct usb_device_id moschip_port_id_table[] = {
+       {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)},
+       {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)},
+       {}                      /* terminating entry */
+};
+
+static __devinitdata struct usb_device_id moschip_id_table_combined[] = {
+       {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)},
+       {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)},
+       {}                      /* terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, moschip_id_table_combined);
+
+/* This structure holds all of the local port information */
+
+struct moschip_port {
+       int port_num;           /*Actual port number in the device(1,2,etc) */
+       struct urb *write_urb;  /* write URB for this port */
+       struct urb *read_urb;   /* read URB for this port */
+       __u8 shadowLCR;         /* last LCR value received */
+       __u8 shadowMCR;         /* last MCR value received */
+       char open;
+       wait_queue_head_t wait_chase;   /* for handling sleeping while waiting for chase to finish */
+       wait_queue_head_t delta_msr_wait;       /* for handling sleeping while waiting for msr change to happen */
+       int delta_msr_cond;
+       struct async_icount icount;
+       struct usb_serial_port *port;   /* loop back to the owner of this object */
+
+       /*Offsets */
+       __u8 SpRegOffset;
+       __u8 ControlRegOffset;
+       __u8 DcrRegOffset;
+       //for processing control URBS in interrupt context
+       struct urb *control_urb;
+       char *ctrl_buf;
+       int MsrLsr;
+
+       struct urb *write_urb_pool[NUM_URBS];
+};
+
+
+static int debug;
+static int mos7840_num_ports;  //this says the number of ports in the device
+static int mos7840_num_open_ports;
+
+
+/*
+ * mos7840_set_reg_sync
+ *     To set the Control register by calling usb_fill_control_urb function
+ *     by passing usb_sndctrlpipe function as parameter.
+ */
+
+static int mos7840_set_reg_sync(struct usb_serial_port *port, __u16 reg,
+                               __u16 val)
+{
+       struct usb_device *dev = port->serial->dev;
+       val = val & 0x00ff;
+       dbg("mos7840_set_reg_sync offset is %x, value %x\n", reg, val);
+
+       return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ,
+                              MCS_WR_RTYPE, val, reg, NULL, 0,
+                              MOS_WDR_TIMEOUT);
+}
+
+/*
+ * mos7840_get_reg_sync
+ *     To set the Uart register by calling usb_fill_control_urb function by
+ *     passing usb_rcvctrlpipe function as parameter.
+ */
+
+static int mos7840_get_reg_sync(struct usb_serial_port *port, __u16 reg,
+                               __u16 * val)
+{
+       struct usb_device *dev = port->serial->dev;
+       int ret = 0;
+
+       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
+                             MCS_RD_RTYPE, 0, reg, val, VENDOR_READ_LENGTH,
+                             MOS_WDR_TIMEOUT);
+       dbg("mos7840_get_reg_sync offset is %x, return val %x\n", reg, *val);
+       *val = (*val) & 0x00ff;
+       return ret;
+}
+
+/*
+ * mos7840_set_uart_reg
+ *     To set the Uart register by calling usb_fill_control_urb function by
+ *     passing usb_sndctrlpipe function as parameter.
+ */
+
+static int mos7840_set_uart_reg(struct usb_serial_port *port, __u16 reg,
+                               __u16 val)
+{
+
+       struct usb_device *dev = port->serial->dev;
+       val = val & 0x00ff;
+       // For the UART control registers, the application number need to be Or'ed
+       if (mos7840_num_ports == 4) {
+               val |=
+                   (((__u16) port->number - (__u16) (port->serial->minor)) +
+                    1) << 8;
+               dbg("mos7840_set_uart_reg application number is %x\n", val);
+       } else {
+               if (((__u16) port->number - (__u16) (port->serial->minor)) == 0) {
+                       val |=
+                           (((__u16) port->number -
+                             (__u16) (port->serial->minor)) + 1) << 8;
+                       dbg("mos7840_set_uart_reg application number is %x\n",
+                           val);
+               } else {
+                       val |=
+                           (((__u16) port->number -
+                             (__u16) (port->serial->minor)) + 2) << 8;
+                       dbg("mos7840_set_uart_reg application number is %x\n",
+                           val);
+               }
+       }
+       return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ,
+                              MCS_WR_RTYPE, val, reg, NULL, 0,
+                              MOS_WDR_TIMEOUT);
+
+}
+
+/*
+ * mos7840_get_uart_reg
+ *     To set the Control register by calling usb_fill_control_urb function
+ *     by passing usb_rcvctrlpipe function as parameter.
+ */
+static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg,
+                               __u16 * val)
+{
+       struct usb_device *dev = port->serial->dev;
+       int ret = 0;
+       __u16 Wval;
+
+       //dbg("application number is %4x \n",(((__u16)port->number - (__u16)(port->serial->minor))+1)<<8);
+       /*Wval  is same as application number */
+       if (mos7840_num_ports == 4) {
+               Wval =
+                   (((__u16) port->number - (__u16) (port->serial->minor)) +
+                    1) << 8;
+               dbg("mos7840_get_uart_reg application number is %x\n", Wval);
+       } else {
+               if (((__u16) port->number - (__u16) (port->serial->minor)) == 0) {
+                       Wval =
+                           (((__u16) port->number -
+                             (__u16) (port->serial->minor)) + 1) << 8;
+                       dbg("mos7840_get_uart_reg application number is %x\n",
+                           Wval);
+               } else {
+                       Wval =
+                           (((__u16) port->number -
+                             (__u16) (port->serial->minor)) + 2) << 8;
+                       dbg("mos7840_get_uart_reg application number is %x\n",
+                           Wval);
+               }
+       }
+       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
+                             MCS_RD_RTYPE, Wval, reg, val, VENDOR_READ_LENGTH,
+                             MOS_WDR_TIMEOUT);
+       *val = (*val) & 0x00ff;
+       return ret;
+}
+
+static void mos7840_dump_serial_port(struct moschip_port *mos7840_port)
+{
+
+       dbg("***************************************\n");
+       dbg("SpRegOffset is %2x\n", mos7840_port->SpRegOffset);
+       dbg("ControlRegOffset is %2x \n", mos7840_port->ControlRegOffset);
+       dbg("DCRRegOffset is %2x \n", mos7840_port->DcrRegOffset);
+       dbg("***************************************\n");
+
+}
+
+/************************************************************************/
+/************************************************************************/
+/*             I N T E R F A C E   F U N C T I O N S                   */
+/*             I N T E R F A C E   F U N C T I O N S                   */
+/************************************************************************/
+/************************************************************************/
+
+static inline void mos7840_set_port_private(struct usb_serial_port *port,
+                                           struct moschip_port *data)
+{
+       usb_set_serial_port_data(port, (void *)data);
+}
+
+static inline struct moschip_port *mos7840_get_port_private(struct
+                                                           usb_serial_port
+                                                           *port)
+{
+       return (struct moschip_port *)usb_get_serial_port_data(port);
+}
+
+static int mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr)
+{
+       struct moschip_port *mos7840_port;
+       struct async_icount *icount;
+       mos7840_port = port;
+       icount = &mos7840_port->icount;
+       if (new_msr &
+           (MOS_MSR_DELTA_CTS | MOS_MSR_DELTA_DSR | MOS_MSR_DELTA_RI |
+            MOS_MSR_DELTA_CD)) {
+               icount = &mos7840_port->icount;
+
+               /* update input line counters */
+               if (new_msr & MOS_MSR_DELTA_CTS) {
+                       icount->cts++;
+               }
+               if (new_msr & MOS_MSR_DELTA_DSR) {
+                       icount->dsr++;
+               }
+               if (new_msr & MOS_MSR_DELTA_CD) {
+                       icount->dcd++;
+               }
+               if (new_msr & MOS_MSR_DELTA_RI) {
+                       icount->rng++;
+               }
+       }
+
+       return 0;
+}
+
+static int mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr)
+{
+       struct async_icount *icount;
+
+       dbg("%s - %02x", __FUNCTION__, new_lsr);
+
+       if (new_lsr & SERIAL_LSR_BI) {
+               //
+               // Parity and Framing errors only count if they
+               // occur exclusive of a break being
+               // received.
+               //
+               new_lsr &= (__u8) (SERIAL_LSR_OE | SERIAL_LSR_BI);
+       }
+
+       /* update input line counters */
+       icount = &port->icount;
+       if (new_lsr & SERIAL_LSR_BI) {
+               icount->brk++;
+       }
+       if (new_lsr & SERIAL_LSR_OE) {
+               icount->overrun++;
+       }
+       if (new_lsr & SERIAL_LSR_PE) {
+               icount->parity++;
+       }
+       if (new_lsr & SERIAL_LSR_FE) {
+               icount->frame++;
+       }
+
+       return 0;
+}
+
+/************************************************************************/
+/************************************************************************/
+/*            U S B  C A L L B A C K   F U N C T I O N S                */
+/*            U S B  C A L L B A C K   F U N C T I O N S                */
+/************************************************************************/
+/************************************************************************/
+
+static void mos7840_control_callback(struct urb *urb, struct pt_regs *regs)
+{
+       unsigned char *data;
+       struct moschip_port *mos7840_port;
+       __u8 regval = 0x0;
+
+       if (!urb) {
+               dbg("%s", "Invalid Pointer !!!!:\n");
+               return;
+       }
+
+       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", __FUNCTION__,
+                   urb->status);
+               return;
+       default:
+               dbg("%s - nonzero urb status received: %d", __FUNCTION__,
+                   urb->status);
+               goto exit;
+       }
+
+       mos7840_port = (struct moschip_port *)urb->context;
+
+       dbg("%s urb buffer size is %d\n", __FUNCTION__, urb->actual_length);
+       dbg("%s mos7840_port->MsrLsr is %d port %d\n", __FUNCTION__,
+           mos7840_port->MsrLsr, mos7840_port->port_num);
+       data = urb->transfer_buffer;
+       regval = (__u8) data[0];
+       dbg("%s data is %x\n", __FUNCTION__, regval);
+       if (mos7840_port->MsrLsr == 0)
+               mos7840_handle_new_msr(mos7840_port, regval);
+       else if (mos7840_port->MsrLsr == 1)
+               mos7840_handle_new_lsr(mos7840_port, regval);
+
+      exit:
+       return;
+}
+
+static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg,
+                          __u16 * val)
+{
+       struct usb_device *dev = mcs->port->serial->dev;
+       struct usb_ctrlrequest *dr = NULL;
+       unsigned char *buffer = NULL;
+       int ret = 0;
+       buffer = (__u8 *) mcs->ctrl_buf;
+
+//      dr=(struct usb_ctrlrequest *)(buffer);
+       dr = (void *)(buffer + 2);
+       dr->bRequestType = MCS_RD_RTYPE;
+       dr->bRequest = MCS_RDREQ;
+       dr->wValue = cpu_to_le16(Wval); //0;
+       dr->wIndex = cpu_to_le16(reg);
+       dr->wLength = cpu_to_le16(2);
+
+       usb_fill_control_urb(mcs->control_urb, dev, usb_rcvctrlpipe(dev, 0),
+                            (unsigned char *)dr, buffer, 2,
+                            mos7840_control_callback, mcs);
+       mcs->control_urb->transfer_buffer_length = 2;
+       ret = usb_submit_urb(mcs->control_urb, GFP_ATOMIC);
+       return ret;
+}
+
+/*****************************************************************************
+ * mos7840_interrupt_callback
+ *     this is the callback function for when we have received data on the
+ *     interrupt endpoint.
+ *****************************************************************************/
+
+static void mos7840_interrupt_callback(struct urb *urb, struct pt_regs *regs)
+{
+       int result;
+       int length;
+       struct moschip_port *mos7840_port;
+       struct usb_serial *serial;
+       __u16 Data;
+       unsigned char *data;
+       __u8 sp[5], st;
+       int i;
+       __u16 wval;
+
+       dbg("%s", " : Entering\n");
+       if (!urb) {
+               dbg("%s", "Invalid Pointer !!!!:\n");
+               return;
+       }
+
+       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", __FUNCTION__,
+                   urb->status);
+               return;
+       default:
+               dbg("%s - nonzero urb status received: %d", __FUNCTION__,
+                   urb->status);
+               goto exit;
+       }
+
+       length = urb->actual_length;
+       data = urb->transfer_buffer;
+
+       serial = (struct usb_serial *)urb->context;
+
+       /* Moschip get 5 bytes
+        * Byte 1 IIR Port 1 (port.number is 0)
+        * Byte 2 IIR Port 2 (port.number is 1)
+        * Byte 3 IIR Port 3 (port.number is 2)
+        * Byte 4 IIR Port 4 (port.number is 3)
+        * Byte 5 FIFO status for both */
+
+       if (length && length > 5) {
+               dbg("%s \n", "Wrong data !!!");
+               return;
+       }
+
+       sp[0] = (__u8) data[0];
+       sp[1] = (__u8) data[1];
+       sp[2] = (__u8) data[2];
+       sp[3] = (__u8) data[3];
+       st = (__u8) data[4];
+
+       for (i = 0; i < serial->num_ports; i++) {
+               mos7840_port = mos7840_get_port_private(serial->port[i]);
+               wval =
+                   (((__u16) serial->port[i]->number -
+                     (__u16) (serial->minor)) + 1) << 8;
+               if (mos7840_port->open) {
+                       if (sp[i] & 0x01) {
+                               dbg("SP%d No Interrupt !!!\n", i);
+                       } else {
+                               switch (sp[i] & 0x0f) {
+                               case SERIAL_IIR_RLS:
+                                       dbg("Serial Port %d: Receiver status error or ", i);
+                                       dbg("address bit detected in 9-bit mode\n");
+                                       mos7840_port->MsrLsr = 1;
+                                       mos7840_get_reg(mos7840_port, wval,
+                                                       LINE_STATUS_REGISTER,
+                                                       &Data);
+                                       break;
+                               case SERIAL_IIR_MS:
+                                       dbg("Serial Port %d: Modem status change\n", i);
+                                       mos7840_port->MsrLsr = 0;
+                                       mos7840_get_reg(mos7840_port, wval,
+                                                       MODEM_STATUS_REGISTER,
+                                                       &Data);
+                                       break;
+                               }
+                       }
+               }
+       }
+      exit:
+       result = usb_submit_urb(urb, GFP_ATOMIC);
+       if (result) {
+               dev_err(&urb->dev->dev,
+                       "%s - Error %d submitting interrupt urb\n",
+                       __FUNCTION__, result);
+       }
+
+       return;
+
+}
+
+static int mos7840_port_paranoia_check(struct usb_serial_port *port,
+                                      const char *function)
+{
+       if (!port) {
+               dbg("%s - port == NULL", function);
+               return -1;
+       }
+       if (!port->serial) {
+               dbg("%s - port->serial == NULL", function);
+               return -1;
+       }
+
+       return 0;
+}
+
+/* Inline functions to check the sanity of a pointer that is passed to us */
+static int mos7840_serial_paranoia_check(struct usb_serial *serial,
+                                        const char *function)
+{
+       if (!serial) {
+               dbg("%s - serial == NULL", function);
+               return -1;
+       }
+       if (!serial->type) {
+               dbg("%s - serial->type == NULL!", function);
+               return -1;
+       }
+
+       return 0;
+}
+
+static struct usb_serial *mos7840_get_usb_serial(struct usb_serial_port *port,
+                                                const char *function)
+{
+       /* if no port was specified, or it fails a paranoia check */
+       if (!port ||
+           mos7840_port_paranoia_check(port, function) ||
+           mos7840_serial_paranoia_check(port->serial, function)) {
+               /* then say that we don't have a valid usb_serial thing, which will                  * end up genrating -ENODEV return values */
+               return NULL;
+       }
+
+       return port->serial;
+}
+
+/*****************************************************************************
+ * mos7840_bulk_in_callback
+ *     this is the callback function for when we have received data on the
+ *     bulk in endpoint.
+ *****************************************************************************/
+
+static void mos7840_bulk_in_callback(struct urb *urb, struct pt_regs *regs)
+{
+       int status;
+       unsigned char *data;
+       struct usb_serial *serial;
+       struct usb_serial_port *port;
+       struct moschip_port *mos7840_port;
+       struct tty_struct *tty;
+
+       if (!urb) {
+               dbg("%s", "Invalid Pointer !!!!:\n");
+               return;
+       }
+
+       if (urb->status) {
+               dbg("nonzero read bulk status received: %d", urb->status);
+               return;
+       }
+
+       mos7840_port = (struct moschip_port *)urb->context;
+       if (!mos7840_port) {
+               dbg("%s", "NULL mos7840_port pointer \n");
+               return;
+       }
+
+       port = (struct usb_serial_port *)mos7840_port->port;
+       if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+               dbg("%s", "Port Paranoia failed \n");
+               return;
+       }
+
+       serial = mos7840_get_usb_serial(port, __FUNCTION__);
+       if (!serial) {
+               dbg("%s\n", "Bad serial pointer ");
+               return;
+       }
+
+       dbg("%s\n", "Entering... \n");
+
+       data = urb->transfer_buffer;
+
+       dbg("%s", "Entering ........... \n");
+
+       if (urb->actual_length) {
+               tty = mos7840_port->port->tty;
+               if (tty) {
+                       tty_buffer_request_room(tty, urb->actual_length);
+                       tty_insert_flip_string(tty, data, urb->actual_length);
+                       dbg(" %s \n", data);
+                       tty_flip_buffer_push(tty);
+               }
+               mos7840_port->icount.rx += urb->actual_length;
+               dbg("mos7840_port->icount.rx is %d:\n",
+                   mos7840_port->icount.rx);
+       }
+
+       if (!mos7840_port->read_urb) {
+               dbg("%s", "URB KILLED !!!\n");
+               return;
+       }
+
+       if (mos7840_port->read_urb->status != -EINPROGRESS) {
+               mos7840_port->read_urb->dev = serial->dev;
+
+               status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
+
+               if (status) {
+                       dbg(" usb_submit_urb(read bulk) failed, status = %d",
+                           status);
+               }
+       }
+}
+
+/*****************************************************************************
+ * mos7840_bulk_out_data_callback
+ *     this is the callback function for when we have finished sending serial data
+ *     on the bulk out endpoint.
+ *****************************************************************************/
+
+static void mos7840_bulk_out_data_callback(struct urb *urb,
+                                          struct pt_regs *regs)
+{
+       struct moschip_port *mos7840_port;
+       struct tty_struct *tty;
+       if (!urb) {
+               dbg("%s", "Invalid Pointer !!!!:\n");
+               return;
+       }
+
+       if (urb->status) {
+               dbg("nonzero write bulk status received:%d\n", urb->status);
+               return;
+       }
+
+       mos7840_port = (struct moschip_port *)urb->context;
+       if (!mos7840_port) {
+               dbg("%s", "NULL mos7840_port pointer \n");
+               return;
+       }
+
+       if (mos7840_port_paranoia_check(mos7840_port->port, __FUNCTION__)) {
+               dbg("%s", "Port Paranoia failed \n");
+               return;
+       }
+
+       dbg("%s \n", "Entering .........");
+
+       tty = mos7840_port->port->tty;
+
+       if (tty && mos7840_port->open) {
+               /* let the tty driver wakeup if it has a special *
+                * write_wakeup function                         */
+
+               if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+                   && tty->ldisc.write_wakeup) {
+                       (tty->ldisc.write_wakeup) (tty);
+               }
+
+               /* tell the tty driver that something has changed */
+               wake_up_interruptible(&tty->write_wait);
+       }
+
+}
+
+/************************************************************************/
+/*       D R I V E R  T T Y  I N T E R F A C E  F U N C T I O N S       */
+/************************************************************************/
+#ifdef MCSSerialProbe
+static int mos7840_serial_probe(struct usb_serial *serial,
+                               const struct usb_device_id *id)
+{
+
+       /*need to implement the mode_reg reading and updating\
+          structures usb_serial_ device_type\
+          (i.e num_ports, num_bulkin,bulkout etc) */
+       /* Also we can update the changes  attach */
+       return 1;
+}
+#endif
+
+/*****************************************************************************
+ * mos7840_open
+ *     this function is called by the tty driver when a port is opened
+ *     If successful, we return 0
+ *     Otherwise we return a negative error number.
+ *****************************************************************************/
+
+static int mos7840_open(struct usb_serial_port *port, struct file *filp)
+{
+       int response;
+       int j;
+       struct usb_serial *serial;
+       struct urb *urb;
+       __u16 Data;
+       int status;
+       struct moschip_port *mos7840_port;
+
+       if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+               dbg("%s", "Port Paranoia failed \n");
+               return -ENODEV;
+       }
+
+       mos7840_num_open_ports++;
+       serial = port->serial;
+
+       if (mos7840_serial_paranoia_check(serial, __FUNCTION__)) {
+               dbg("%s", "Serial Paranoia failed \n");
+               return -ENODEV;
+       }
+
+       mos7840_port = mos7840_get_port_private(port);
+
+       if (mos7840_port == NULL)
+               return -ENODEV;
+
+       usb_clear_halt(serial->dev, port->write_urb->pipe);
+       usb_clear_halt(serial->dev, port->read_urb->pipe);
+
+       /* Initialising the write urb pool */
+       for (j = 0; j < NUM_URBS; ++j) {
+               urb = usb_alloc_urb(0, SLAB_ATOMIC);
+               mos7840_port->write_urb_pool[j] = urb;
+
+               if (urb == NULL) {
+                       err("No more urbs???");
+                       continue;
+               }
+
+               urb->transfer_buffer = NULL;
+               urb->transfer_buffer =
+                   kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
+               if (!urb->transfer_buffer) {
+                       err("%s-out of memory for urb buffers.", __FUNCTION__);
+                       continue;
+               }
+       }
+
+/*****************************************************************************
+ * Initialize MCS7840 -- Write Init values to corresponding Registers
+ *
+ * Register Index
+ * 1 : IER
+ * 2 : FCR
+ * 3 : LCR
+ * 4 : MCR
+ *
+ * 0x08 : SP1/2 Control Reg
+ *****************************************************************************/
+
+//NEED to check the following Block
+
+       status = 0;
+       Data = 0x0;
+       status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data);
+       if (status < 0) {
+               dbg("Reading Spreg failed\n");
+               return -1;
+       }
+       Data |= 0x80;
+       status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+       if (status < 0) {
+               dbg("writing Spreg failed\n");
+               return -1;
+       }
+
+       Data &= ~0x80;
+       status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+       if (status < 0) {
+               dbg("writing Spreg failed\n");
+               return -1;
+       }
+//End of block to be checked
+
+       status = 0;
+       Data = 0x0;
+       status =
+           mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, &Data);
+       if (status < 0) {
+               dbg("Reading Controlreg failed\n");
+               return -1;
+       }
+       Data |= 0x08;           //Driver done bit
+       Data |= 0x20;           //rx_disable
+       status = 0;
+       status =
+           mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data);
+       if (status < 0) {
+               dbg("writing Controlreg failed\n");
+               return -1;
+       }
+       //do register settings here
+       // Set all regs to the device default values.
+       ////////////////////////////////////
+       // First Disable all interrupts.
+       ////////////////////////////////////
+
+       Data = 0x00;
+       status = 0;
+       status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+       if (status < 0) {
+               dbg("disableing interrupts failed\n");
+               return -1;
+       }
+       // Set FIFO_CONTROL_REGISTER to the default value
+       Data = 0x00;
+       status = 0;
+       status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
+       if (status < 0) {
+               dbg("Writing FIFO_CONTROL_REGISTER  failed\n");
+               return -1;
+       }
+
+       Data = 0xcf;
+       status = 0;
+       status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
+       if (status < 0) {
+               dbg("Writing FIFO_CONTROL_REGISTER  failed\n");
+               return -1;
+       }
+
+       Data = 0x03;
+       status = 0;
+       status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+       mos7840_port->shadowLCR = Data;
+
+       Data = 0x0b;
+       status = 0;
+       status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+       mos7840_port->shadowMCR = Data;
+
+       Data = 0x00;
+       status = 0;
+       status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
+       mos7840_port->shadowLCR = Data;
+
+       Data |= SERIAL_LCR_DLAB;        //data latch enable in LCR 0x80
+       status = 0;
+       status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+
+       Data = 0x0c;
+       status = 0;
+       status = mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data);
+
+       Data = 0x0;
+       status = 0;
+       status = mos7840_set_uart_reg(port, DIVISOR_LATCH_MSB, Data);
+
+       Data = 0x00;
+       status = 0;
+       status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
+
+       Data = Data & ~SERIAL_LCR_DLAB;
+       status = 0;
+       status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+       mos7840_port->shadowLCR = Data;
+
+       //clearing Bulkin and Bulkout Fifo
+       Data = 0x0;
+       status = 0;
+       status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data);
+
+       Data = Data | 0x0c;
+       status = 0;
+       status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+
+       Data = Data & ~0x0c;
+       status = 0;
+       status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+       //Finally enable all interrupts
+       Data = 0x0;
+       Data = 0x0c;
+       status = 0;
+       status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+
+       //clearing rx_disable
+       Data = 0x0;
+       status = 0;
+       status =
+           mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, &Data);
+       Data = Data & ~0x20;
+       status = 0;
+       status =
+           mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data);
+
+       // rx_negate
+       Data = 0x0;
+       status = 0;
+       status =
+           mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, &Data);
+       Data = Data | 0x10;
+       status = 0;
+       status =
+           mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data);
+
+       /* force low_latency on so that our tty_push actually forces *
+        * the data through,otherwise it is scheduled, and with      *
+        * high data rates (like with OHCI) data can get lost.       */
+
+       if (port->tty)
+               port->tty->low_latency = 1;
+/* Check to see if we've set up our endpoint info yet    *
+     * (can't set it up in mos7840_startup as the structures *
+     * were not set up at that time.)                        */
+       if (mos7840_num_open_ports == 1) {
+               if (serial->port[0]->interrupt_in_buffer == NULL) {
+
+                       /* set up interrupt urb */
+
+                       usb_fill_int_urb(serial->port[0]->interrupt_in_urb,
+                                        serial->dev,
+                                        usb_rcvintpipe(serial->dev,
+                                                       serial->port[0]->
+                                                       interrupt_in_endpointAddress),
+                                        serial->port[0]->interrupt_in_buffer,
+                                        serial->port[0]->interrupt_in_urb->
+                                        transfer_buffer_length,
+                                        mos7840_interrupt_callback,
+                                        serial,
+                                        serial->port[0]->interrupt_in_urb->
+                                        interval);
+
+                       /* start interrupt read for mos7840               *
+                        * will continue as long as mos7840 is connected  */
+
+                       response =
+                           usb_submit_urb(serial->port[0]->interrupt_in_urb,
+                                          GFP_KERNEL);
+                       if (response) {
+                               err("%s - Error %d submitting interrupt urb",
+                                   __FUNCTION__, response);
+                       }
+
+               }
+
+       }
+
+       /* see if we've set up our endpoint info yet   *
+        * (can't set it up in mos7840_startup as the  *
+        * structures were not set up at that time.)   */
+
+       dbg("port number is %d \n", port->number);
+       dbg("serial number is %d \n", port->serial->minor);
+       dbg("Bulkin endpoint is %d \n", port->bulk_in_endpointAddress);
+       dbg("BulkOut endpoint is %d \n", port->bulk_out_endpointAddress);
+       dbg("Interrupt endpoint is %d \n", port->interrupt_in_endpointAddress);
+       dbg("port's number in the device is %d\n", mos7840_port->port_num);
+       mos7840_port->read_urb = port->read_urb;
+
+       /* set up our bulk in urb */
+
+       usb_fill_bulk_urb(mos7840_port->read_urb,
+                         serial->dev,
+                         usb_rcvbulkpipe(serial->dev,
+                                         port->bulk_in_endpointAddress),
+                         port->bulk_in_buffer,
+                         mos7840_port->read_urb->transfer_buffer_length,
+                         mos7840_bulk_in_callback, mos7840_port);
+
+       dbg("mos7840_open: bulkin endpoint is %d\n",
+           port->bulk_in_endpointAddress);
+       response = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL);
+       if (response) {
+               err("%s - Error %d submitting control urb", __FUNCTION__,
+                   response);
+       }
+
+       /* initialize our wait queues */
+       init_waitqueue_head(&mos7840_port->wait_chase);
+       init_waitqueue_head(&mos7840_port->delta_msr_wait);
+
+       /* initialize our icount structure */
+       memset(&(mos7840_port->icount), 0x00, sizeof(mos7840_port->icount));
+
+       /* initialize our port settings */
+       mos7840_port->shadowMCR = MCR_MASTER_IE;        /* Must set to enable ints! */
+       /* send a open port command */
+       mos7840_port->open = 1;
+       //mos7840_change_port_settings(mos7840_port,old_termios);
+       mos7840_port->icount.tx = 0;
+       mos7840_port->icount.rx = 0;
+
+       dbg("\n\nusb_serial serial:%x       mos7840_port:%x\n      usb_serial_port port:%x\n\n", (unsigned int)serial, (unsigned int)mos7840_port, (unsigned int)port);
+
+       return 0;
+
+}
+
+/*****************************************************************************
+ * mos7840_chars_in_buffer
+ *     this function is called by the tty driver when it wants to know how many
+ *     bytes of data we currently have outstanding in the port (data that has
+ *     been written, but hasn't made it out the port yet)
+ *     If successful, we return the number of bytes left to be written in the
+ *     system,
+ *     Otherwise we return a negative error number.
+ *****************************************************************************/
+
+static int mos7840_chars_in_buffer(struct usb_serial_port *port)
+{
+       int i;
+       int chars = 0;
+       struct moschip_port *mos7840_port;
+
+       dbg("%s \n", " mos7840_chars_in_buffer:entering ...........");
+
+       if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+               dbg("%s", "Invalid port \n");
+               return -1;
+       }
+
+       mos7840_port = mos7840_get_port_private(port);
+       if (mos7840_port == NULL) {
+               dbg("%s \n", "mos7840_break:leaving ...........");
+               return -1;
+       }
+
+       for (i = 0; i < NUM_URBS; ++i) {
+               if (mos7840_port->write_urb_pool[i]->status == -EINPROGRESS) {
+                       chars += URB_TRANSFER_BUFFER_SIZE;
+               }
+       }
+       dbg("%s - returns %d", __FUNCTION__, chars);
+       return (chars);
+
+}
+
+/************************************************************************
+ *
+ * mos7840_block_until_tx_empty
+ *
+ *     This function will block the close until one of the following:
+ *             1. TX count are 0
+ *             2. The mos7840 has stopped
+ *             3. A timout of 3 seconds without activity has expired
+ *
+ ************************************************************************/
+static void mos7840_block_until_tx_empty(struct moschip_port *mos7840_port)
+{
+       int timeout = HZ / 10;
+       int wait = 30;
+       int count;
+
+       while (1) {
+
+               count = mos7840_chars_in_buffer(mos7840_port->port);
+
+               /* Check for Buffer status */
+               if (count <= 0) {
+                       return;
+               }
+
+               /* Block the thread for a while */
+               interruptible_sleep_on_timeout(&mos7840_port->wait_chase,
+                                              timeout);
+
+               /* No activity.. count down section */
+               wait--;
+               if (wait == 0) {
+                       dbg("%s - TIMEOUT", __FUNCTION__);
+                       return;
+               } else {
+                       /* Reset timout value back to seconds */
+                       wait = 30;
+               }
+       }
+}
+
+/*****************************************************************************
+ * mos7840_close
+ *     this function is called by the tty driver when a port is closed
+ *****************************************************************************/
+
+static void mos7840_close(struct usb_serial_port *port, struct file *filp)
+{
+       struct usb_serial *serial;
+       struct moschip_port *mos7840_port;
+       int j;
+       __u16 Data;
+
+       dbg("%s\n", "mos7840_close:entering...");
+
+       if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+               dbg("%s", "Port Paranoia failed \n");
+               return;
+       }
+
+       serial = mos7840_get_usb_serial(port, __FUNCTION__);
+       if (!serial) {
+               dbg("%s", "Serial Paranoia failed \n");
+               return;
+       }
+
+       mos7840_port = mos7840_get_port_private(port);
+
+       if (mos7840_port == NULL) {
+               return;
+       }
+
+       for (j = 0; j < NUM_URBS; ++j)
+               usb_kill_urb(mos7840_port->write_urb_pool[j]);
+
+       /* Freeing Write URBs */
+       for (j = 0; j < NUM_URBS; ++j) {
+               if (mos7840_port->write_urb_pool[j]) {
+                       if (mos7840_port->write_urb_pool[j]->transfer_buffer)
+                               kfree(mos7840_port->write_urb_pool[j]->
+                                     transfer_buffer);
+
+                       usb_free_urb(mos7840_port->write_urb_pool[j]);
+               }
+       }
+
+       if (serial->dev) {
+               /* flush and block until tx is empty */
+               mos7840_block_until_tx_empty(mos7840_port);
+       }
+
+       /* While closing port, shutdown all bulk read, write  *
+        * and interrupt read if they exists                  */
+       if (serial->dev) {
+
+               if (mos7840_port->write_urb) {
+                       dbg("%s", "Shutdown bulk write\n");
+                       usb_kill_urb(mos7840_port->write_urb);
+               }
+
+               if (mos7840_port->read_urb) {
+                       dbg("%s", "Shutdown bulk read\n");
+                       usb_kill_urb(mos7840_port->read_urb);
+               }
+               if ((&mos7840_port->control_urb)) {
+                       dbg("%s", "Shutdown control read\n");
+                       //      usb_kill_urb (mos7840_port->control_urb);
+
+               }
+       }
+//              if(mos7840_port->ctrl_buf != NULL)
+//                      kfree(mos7840_port->ctrl_buf);
+       mos7840_num_open_ports--;
+       dbg("mos7840_num_open_ports in close%d:in port%d\n",
+           mos7840_num_open_ports, port->number);
+       if (mos7840_num_open_ports == 0) {
+               if (serial->port[0]->interrupt_in_urb) {
+                       dbg("%s", "Shutdown interrupt_in_urb\n");
+               }
+       }
+
+       if (mos7840_port->write_urb) {
+               /* if this urb had a transfer buffer already (old tx) free it */
+
+               if (mos7840_port->write_urb->transfer_buffer != NULL) {
+                       kfree(mos7840_port->write_urb->transfer_buffer);
+               }
+               usb_free_urb(mos7840_port->write_urb);
+       }
+
+       Data = 0x0;
+       mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+
+       Data = 0x00;
+       mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+
+       mos7840_port->open = 0;
+
+       dbg("%s \n", "Leaving ............");
+}
+
+/************************************************************************
+ *
+ * mos7840_block_until_chase_response
+ *
+ *     This function will block the close until one of the following:
+ *             1. Response to our Chase comes from mos7840
+ *             2. A timout of 10 seconds without activity has expired
+ *                (1K of mos7840 data @ 2400 baud ==> 4 sec to empty)
+ *
+ ************************************************************************/
+
+static void mos7840_block_until_chase_response(struct moschip_port
+                                              *mos7840_port)
+{
+       int timeout = 1 * HZ;
+       int wait = 10;
+       int count;
+
+       while (1) {
+               count = mos7840_chars_in_buffer(mos7840_port->port);
+
+               /* Check for Buffer status */
+               if (count <= 0) {
+                       return;
+               }
+
+               /* Block the thread for a while */
+               interruptible_sleep_on_timeout(&mos7840_port->wait_chase,
+                                              timeout);
+               /* No activity.. count down section */
+               wait--;
+               if (wait == 0) {
+                       dbg("%s - TIMEOUT", __FUNCTION__);
+                       return;
+               } else {
+                       /* Reset timout value back to seconds */
+                       wait = 10;
+               }
+       }
+
+}
+
+/*****************************************************************************
+ * mos7840_break
+ *     this function sends a break to the port
+ *****************************************************************************/
+static void mos7840_break(struct usb_serial_port *port, int break_state)
+{
+       unsigned char data;
+       struct usb_serial *serial;
+       struct moschip_port *mos7840_port;
+
+       dbg("%s \n", "Entering ...........");
+       dbg("mos7840_break: Start\n");
+
+       if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+               dbg("%s", "Port Paranoia failed \n");
+               return;
+       }
+
+       serial = mos7840_get_usb_serial(port, __FUNCTION__);
+       if (!serial) {
+               dbg("%s", "Serial Paranoia failed \n");
+               return;
+       }
+
+       mos7840_port = mos7840_get_port_private(port);
+
+       if (mos7840_port == NULL) {
+               return;
+       }
+
+       if (serial->dev) {
+
+               /* flush and block until tx is empty */
+               mos7840_block_until_chase_response(mos7840_port);
+       }
+
+       if (break_state == -1) {
+               data = mos7840_port->shadowLCR | LCR_SET_BREAK;
+       } else {
+               data = mos7840_port->shadowLCR & ~LCR_SET_BREAK;
+       }
+
+       mos7840_port->shadowLCR = data;
+       dbg("mcs7840_break mos7840_port->shadowLCR is %x\n",
+           mos7840_port->shadowLCR);
+       mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER,
+                            mos7840_port->shadowLCR);
+
+       return;
+}
+
+/*****************************************************************************
+ * mos7840_write_room
+ *     this function is called by the tty driver when it wants to know how many
+ *     bytes of data we can accept for a specific port.
+ *     If successful, we return the amount of room that we have for this port
+ *     Otherwise we return a negative error number.
+ *****************************************************************************/
+
+static int mos7840_write_room(struct usb_serial_port *port)
+{
+       int i;
+       int room = 0;
+       struct moschip_port *mos7840_port;
+
+       dbg("%s \n", " mos7840_write_room:entering ...........");
+
+       if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+               dbg("%s", "Invalid port \n");
+               dbg("%s \n", " mos7840_write_room:leaving ...........");
+               return -1;
+       }
+
+       mos7840_port = mos7840_get_port_private(port);
+       if (mos7840_port == NULL) {
+               dbg("%s \n", "mos7840_break:leaving ...........");
+               return -1;
+       }
+
+       for (i = 0; i < NUM_URBS; ++i) {
+               if (mos7840_port->write_urb_pool[i]->status != -EINPROGRESS) {
+                       room += URB_TRANSFER_BUFFER_SIZE;
+               }
+       }
+
+       dbg("%s - returns %d", __FUNCTION__, room);
+       return (room);
+
+}
+
+/*****************************************************************************
+ * mos7840_write
+ *     this function is called by the tty driver when data should be written to
+ *     the port.
+ *     If successful, we return the number of bytes written, otherwise we
+ *      return a negative error number.
+ *****************************************************************************/
+
+static int mos7840_write(struct usb_serial_port *port,
+                        const unsigned char *data, int count)
+{
+       int status;
+       int i;
+       int bytes_sent = 0;
+       int transfer_size;
+       int from_user = 0;
+
+       struct moschip_port *mos7840_port;
+       struct usb_serial *serial;
+       struct urb *urb;
+       //__u16 Data;
+       const unsigned char *current_position = data;
+       unsigned char *data1;
+       dbg("%s \n", "entering ...........");
+       //dbg("mos7840_write: mos7840_port->shadowLCR is %x\n",mos7840_port->shadowLCR);
+
+#ifdef NOTMOS7840
+       Data = 0x00;
+       status = 0;
+       status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
+       mos7840_port->shadowLCR = Data;
+       dbg("mos7840_write: LINE_CONTROL_REGISTER is %x\n", Data);
+       dbg("mos7840_write: mos7840_port->shadowLCR is %x\n",
+           mos7840_port->shadowLCR);
+
+       //Data = 0x03;
+       //status = mos7840_set_uart_reg(port,LINE_CONTROL_REGISTER,Data);
+       //mos7840_port->shadowLCR=Data;//Need to add later
+
+       Data |= SERIAL_LCR_DLAB;        //data latch enable in LCR 0x80
+       status = 0;
+       status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+
+       //Data = 0x0c;
+       //status = mos7840_set_uart_reg(port,DIVISOR_LATCH_LSB,Data);
+       Data = 0x00;
+       status = 0;
+       status = mos7840_get_uart_reg(port, DIVISOR_LATCH_LSB, &Data);
+       dbg("mos7840_write:DLL value is %x\n", Data);
+
+       Data = 0x0;
+       status = 0;
+       status = mos7840_get_uart_reg(port, DIVISOR_LATCH_MSB, &Data);
+       dbg("mos7840_write:DLM value is %x\n", Data);
+
+       Data = Data & ~SERIAL_LCR_DLAB;
+       dbg("mos7840_write: mos7840_port->shadowLCR is %x\n",
+           mos7840_port->shadowLCR);
+       status = 0;
+       status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+#endif
+
+       if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+               dbg("%s", "Port Paranoia failed \n");
+               return -1;
+       }
+
+       serial = port->serial;
+       if (mos7840_serial_paranoia_check(serial, __FUNCTION__)) {
+               dbg("%s", "Serial Paranoia failed \n");
+               return -1;
+       }
+
+       mos7840_port = mos7840_get_port_private(port);
+       if (mos7840_port == NULL) {
+               dbg("%s", "mos7840_port is NULL\n");
+               return -1;
+       }
+
+       /* try to find a free urb in the list */
+       urb = NULL;
+
+       for (i = 0; i < NUM_URBS; ++i) {
+               if (mos7840_port->write_urb_pool[i]->status != -EINPROGRESS) {
+                       urb = mos7840_port->write_urb_pool[i];
+                       dbg("\nURB:%d", i);
+                       break;
+               }
+       }
+
+       if (urb == NULL) {
+               dbg("%s - no more free urbs", __FUNCTION__);
+               goto exit;
+       }
+
+       if (urb->transfer_buffer == NULL) {
+               urb->transfer_buffer =
+                   kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
+
+               if (urb->transfer_buffer == NULL) {
+                       err("%s no more kernel memory...", __FUNCTION__);
+                       goto exit;
+               }
+       }
+       transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE);
+
+       if (from_user) {
+               if (copy_from_user
+                   (urb->transfer_buffer, current_position, transfer_size)) {
+                       bytes_sent = -EFAULT;
+                       goto exit;
+               }
+       } else {
+               memcpy(urb->transfer_buffer, current_position, transfer_size);
+       }
+
+       /* fill urb with data and submit  */
+       usb_fill_bulk_urb(urb,
+                         serial->dev,
+                         usb_sndbulkpipe(serial->dev,
+                                         port->bulk_out_endpointAddress),
+                         urb->transfer_buffer,
+                         transfer_size,
+                         mos7840_bulk_out_data_callback, mos7840_port);
+
+       data1 = urb->transfer_buffer;
+       dbg("\nbulkout endpoint is %d", port->bulk_out_endpointAddress);
+
+       /* send it down the pipe */
+       status = usb_submit_urb(urb, GFP_ATOMIC);
+
+       if (status) {
+               err("%s - usb_submit_urb(write bulk) failed with status = %d",
+                   __FUNCTION__, status);
+               bytes_sent = status;
+               goto exit;
+       }
+       bytes_sent = transfer_size;
+       mos7840_port->icount.tx += transfer_size;
+       dbg("mos7840_port->icount.tx is %d:\n", mos7840_port->icount.tx);
+      exit:
+
+       return bytes_sent;
+
+}
+
+/*****************************************************************************
+ * mos7840_throttle
+ *     this function is called by the tty driver when it wants to stop the data
+ *     being read from the port.
+ *****************************************************************************/
+
+static void mos7840_throttle(struct usb_serial_port *port)
+{
+       struct moschip_port *mos7840_port;
+       struct tty_struct *tty;
+       int status;
+
+       if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+               dbg("%s", "Invalid port \n");
+               return;
+       }
+
+       dbg("- port %d\n", port->number);
+
+       mos7840_port = mos7840_get_port_private(port);
+
+       if (mos7840_port == NULL)
+               return;
+
+       if (!mos7840_port->open) {
+               dbg("%s\n", "port not opened");
+               return;
+       }
+
+       dbg("%s", "Entering .......... \n");
+
+       tty = port->tty;
+       if (!tty) {
+               dbg("%s - no tty available", __FUNCTION__);
+               return;
+       }
+
+       /* if we are implementing XON/XOFF, send the stop character */
+       if (I_IXOFF(tty)) {
+               unsigned char stop_char = STOP_CHAR(tty);
+               status = mos7840_write(port, &stop_char, 1);
+               if (status <= 0) {
+                       return;
+               }
+       }
+
+       /* if we are implementing RTS/CTS, toggle that line */
+       if (tty->termios->c_cflag & CRTSCTS) {
+               mos7840_port->shadowMCR &= ~MCR_RTS;
+               status = 0;
+               status =
+                   mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
+                                        mos7840_port->shadowMCR);
+
+               if (status < 0) {
+                       return;
+               }
+       }
+
+       return;
+}
+
+/*****************************************************************************
+ * mos7840_unthrottle
+ *     this function is called by the tty driver when it wants to resume the data
+ *     being read from the port (called after SerialThrottle is called)
+ *****************************************************************************/
+static void mos7840_unthrottle(struct usb_serial_port *port)
+{
+       struct tty_struct *tty;
+       int status;
+       struct moschip_port *mos7840_port = mos7840_get_port_private(port);
+
+       if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+               dbg("%s", "Invalid port \n");
+               return;
+       }
+
+       if (mos7840_port == NULL)
+               return;
+
+       if (!mos7840_port->open) {
+               dbg("%s - port not opened", __FUNCTION__);
+               return;
+       }
+
+       dbg("%s", "Entering .......... \n");
+
+       tty = port->tty;
+       if (!tty) {
+               dbg("%s - no tty available", __FUNCTION__);
+               return;
+       }
+
+       /* if we are implementing XON/XOFF, send the start character */
+       if (I_IXOFF(tty)) {
+               unsigned char start_char = START_CHAR(tty);
+               status = mos7840_write(port, &start_char, 1);
+               if (status <= 0) {
+                       return;
+               }
+       }
+
+       /* if we are implementing RTS/CTS, toggle that line */
+       if (tty->termios->c_cflag & CRTSCTS) {
+               mos7840_port->shadowMCR |= MCR_RTS;
+               status = 0;
+               status =
+                   mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
+                                        mos7840_port->shadowMCR);
+               if (status < 0) {
+                       return;
+               }
+       }
+
+       return;
+}
+
+static int mos7840_tiocmget(struct usb_serial_port *port, struct file *file)
+{
+       struct moschip_port *mos7840_port;
+       unsigned int result;
+       __u16 msr;
+       __u16 mcr;
+       int status = 0;
+       mos7840_port = mos7840_get_port_private(port);
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (mos7840_port == NULL)
+               return -ENODEV;
+
+       status = mos7840_get_uart_reg(port, MODEM_STATUS_REGISTER, &msr);
+       status = mos7840_get_uart_reg(port, MODEM_CONTROL_REGISTER, &mcr);
+       result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0)
+           | ((mcr & MCR_RTS) ? TIOCM_RTS : 0)
+           | ((mcr & MCR_LOOPBACK) ? TIOCM_LOOP : 0)
+           | ((msr & MOS7840_MSR_CTS) ? TIOCM_CTS : 0)
+           | ((msr & MOS7840_MSR_CD) ? TIOCM_CAR : 0)
+           | ((msr & MOS7840_MSR_RI) ? TIOCM_RI : 0)
+           | ((msr & MOS7840_MSR_DSR) ? TIOCM_DSR : 0);
+
+       dbg("%s - 0x%04X", __FUNCTION__, result);
+
+       return result;
+}
+
+static int mos7840_tiocmset(struct usb_serial_port *port, struct file *file,
+                           unsigned int set, unsigned int clear)
+{
+       struct moschip_port *mos7840_port;
+       unsigned int mcr;
+       unsigned int status;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       mos7840_port = mos7840_get_port_private(port);
+
+       if (mos7840_port == NULL)
+               return -ENODEV;
+
+       mcr = mos7840_port->shadowMCR;
+       if (clear & TIOCM_RTS)
+               mcr &= ~MCR_RTS;
+       if (clear & TIOCM_DTR)
+               mcr &= ~MCR_DTR;
+       if (clear & TIOCM_LOOP)
+               mcr &= ~MCR_LOOPBACK;
+
+       if (set & TIOCM_RTS)
+               mcr |= MCR_RTS;
+       if (set & TIOCM_DTR)
+               mcr |= MCR_DTR;
+       if (set & TIOCM_LOOP)
+               mcr |= MCR_LOOPBACK;
+
+       mos7840_port->shadowMCR = mcr;
+
+       status = 0;
+       status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, mcr);
+       if (status < 0) {
+               dbg("setting MODEM_CONTROL_REGISTER Failed\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+/*****************************************************************************
+ * mos7840_calc_baud_rate_divisor
+ *     this function calculates the proper baud rate divisor for the specified
+ *     baud rate.
+ *****************************************************************************/
+static int mos7840_calc_baud_rate_divisor(int baudRate, int *divisor,
+                                         __u16 * clk_sel_val)
+{
+
+       dbg("%s - %d", __FUNCTION__, baudRate);
+
+       if (baudRate <= 115200) {
+               *divisor = 115200 / baudRate;
+               *clk_sel_val = 0x0;
+       }
+       if ((baudRate > 115200) && (baudRate <= 230400)) {
+               *divisor = 230400 / baudRate;
+               *clk_sel_val = 0x10;
+       } else if ((baudRate > 230400) && (baudRate <= 403200)) {
+               *divisor = 403200 / baudRate;
+               *clk_sel_val = 0x20;
+       } else if ((baudRate > 403200) && (baudRate <= 460800)) {
+               *divisor = 460800 / baudRate;
+               *clk_sel_val = 0x30;
+       } else if ((baudRate > 460800) && (baudRate <= 806400)) {
+               *divisor = 806400 / baudRate;
+               *clk_sel_val = 0x40;
+       } else if ((baudRate > 806400) && (baudRate <= 921600)) {
+               *divisor = 921600 / baudRate;
+               *clk_sel_val = 0x50;
+       } else if ((baudRate > 921600) && (baudRate <= 1572864)) {
+               *divisor = 1572864 / baudRate;
+               *clk_sel_val = 0x60;
+       } else if ((baudRate > 1572864) && (baudRate <= 3145728)) {
+               *divisor = 3145728 / baudRate;
+               *clk_sel_val = 0x70;
+       }
+       return 0;
+
+#ifdef NOTMCS7840
+
+       for (i = 0; i < ARRAY_SIZE(mos7840_divisor_table); i++) {
+               if (mos7840_divisor_table[i].BaudRate == baudrate) {
+                       *divisor = mos7840_divisor_table[i].Divisor;
+                       return 0;
+               }
+       }
+
+       /* After trying for all the standard baud rates    *
+        * Try calculating the divisor for this baud rate  */
+
+       if (baudrate > 75 && baudrate < 230400) {
+               /* get the divisor */
+               custom = (__u16) (230400L / baudrate);
+
+               /* Check for round off */
+               round1 = (__u16) (2304000L / baudrate);
+               round = (__u16) (round1 - (custom * 10));
+               if (round > 4) {
+                       custom++;
+               }
+               *divisor = custom;
+
+               dbg(" Baud %d = %d\n", baudrate, custom);
+               return 0;
+       }
+
+       dbg("%s\n", " Baud calculation Failed...");
+       return -1;
+#endif
+}
+
+/*****************************************************************************
+ * mos7840_send_cmd_write_baud_rate
+ *     this function sends the proper command to change the baud rate of the
+ *     specified port.
+ *****************************************************************************/
+
+static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port,
+                                           int baudRate)
+{
+       int divisor = 0;
+       int status;
+       __u16 Data;
+       unsigned char number;
+       __u16 clk_sel_val;
+       struct usb_serial_port *port;
+
+       if (mos7840_port == NULL)
+               return -1;
+
+       port = (struct usb_serial_port *)mos7840_port->port;
+       if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+               dbg("%s", "Invalid port \n");
+               return -1;
+       }
+
+       if (mos7840_serial_paranoia_check(port->serial, __FUNCTION__)) {
+               dbg("%s", "Invalid Serial \n");
+               return -1;
+       }
+
+       dbg("%s", "Entering .......... \n");
+
+       number = mos7840_port->port->number - mos7840_port->port->serial->minor;
+
+       dbg("%s - port = %d, baud = %d", __FUNCTION__,
+           mos7840_port->port->number, baudRate);
+       //reset clk_uart_sel in spregOffset
+       if (baudRate > 115200) {
+#ifdef HW_flow_control
+               //NOTE: need to see the pther register to modify
+               //setting h/w flow control bit to 1;
+               status = 0;
+               Data = 0x2b;
+               mos7840_port->shadowMCR = Data;
+               status =
+                   mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+               if (status < 0) {
+                       dbg("Writing spreg failed in set_serial_baud\n");
+                       return -1;
+               }
+#endif
+
+       } else {
+#ifdef HW_flow_control
+               //setting h/w flow control bit to 0;
+               status = 0;
+               Data = 0xb;
+               mos7840_port->shadowMCR = Data;
+               status =
+                   mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+               if (status < 0) {
+                       dbg("Writing spreg failed in set_serial_baud\n");
+                       return -1;
+               }
+#endif
+
+       }
+
+       if (1)                  //baudRate <= 115200)
+       {
+               clk_sel_val = 0x0;
+               Data = 0x0;
+               status = 0;
+               status =
+                   mos7840_calc_baud_rate_divisor(baudRate, &divisor,
+                                                  &clk_sel_val);
+               status =
+                   mos7840_get_reg_sync(port, mos7840_port->SpRegOffset,
+                                        &Data);
+               if (status < 0) {
+                       dbg("reading spreg failed in set_serial_baud\n");
+                       return -1;
+               }
+               Data = (Data & 0x8f) | clk_sel_val;
+               status = 0;
+               status =
+                   mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+               if (status < 0) {
+                       dbg("Writing spreg failed in set_serial_baud\n");
+                       return -1;
+               }
+               /* Calculate the Divisor */
+
+               if (status) {
+                       err("%s - bad baud rate", __FUNCTION__);
+                       dbg("%s\n", "bad baud rate");
+                       return status;
+               }
+               /* Enable access to divisor latch */
+               Data = mos7840_port->shadowLCR | SERIAL_LCR_DLAB;
+               mos7840_port->shadowLCR = Data;
+               mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+
+               /* Write the divisor */
+               Data = (unsigned char)(divisor & 0xff);
+               dbg("set_serial_baud Value to write DLL is %x\n", Data);
+               mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data);
+
+               Data = (unsigned char)((divisor & 0xff00) >> 8);
+               dbg("set_serial_baud Value to write DLM is %x\n", Data);
+               mos7840_set_uart_reg(port, DIVISOR_LATCH_MSB, Data);
+
+               /* Disable access to divisor latch */
+               Data = mos7840_port->shadowLCR & ~SERIAL_LCR_DLAB;
+               mos7840_port->shadowLCR = Data;
+               mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+
+       }
+
+       return status;
+}
+
+/*****************************************************************************
+ * mos7840_change_port_settings
+ *     This routine is called to set the UART on the device to match
+ *      the specified new settings.
+ *****************************************************************************/
+
+static void mos7840_change_port_settings(struct moschip_port *mos7840_port,
+                                        struct termios *old_termios)
+{
+       struct tty_struct *tty;
+       int baud;
+       unsigned cflag;
+       unsigned iflag;
+       __u8 lData;
+       __u8 lParity;
+       __u8 lStop;
+       int status;
+       __u16 Data;
+       struct usb_serial_port *port;
+       struct usb_serial *serial;
+
+       if (mos7840_port == NULL)
+               return;
+
+       port = (struct usb_serial_port *)mos7840_port->port;
+
+       if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+               dbg("%s", "Invalid port \n");
+               return;
+       }
+
+       if (mos7840_serial_paranoia_check(port->serial, __FUNCTION__)) {
+               dbg("%s", "Invalid Serial \n");
+               return;
+       }
+
+       serial = port->serial;
+
+       dbg("%s - port %d", __FUNCTION__, mos7840_port->port->number);
+
+       if (!mos7840_port->open) {
+               dbg("%s - port not opened", __FUNCTION__);
+               return;
+       }
+
+       tty = mos7840_port->port->tty;
+
+       if ((!tty) || (!tty->termios)) {
+               dbg("%s - no tty structures", __FUNCTION__);
+               return;
+       }
+
+       dbg("%s", "Entering .......... \n");
+
+       lData = LCR_BITS_8;
+       lStop = LCR_STOP_1;
+       lParity = LCR_PAR_NONE;
+
+       cflag = tty->termios->c_cflag;
+       iflag = tty->termios->c_iflag;
+
+       /* Change the number of bits */
+       if (cflag & CSIZE) {
+               switch (cflag & CSIZE) {
+               case CS5:
+                       lData = LCR_BITS_5;
+                       break;
+
+               case CS6:
+                       lData = LCR_BITS_6;
+                       break;
+
+               case CS7:
+                       lData = LCR_BITS_7;
+                       break;
+               default:
+               case CS8:
+                       lData = LCR_BITS_8;
+                       break;
+               }
+       }
+       /* Change the Parity bit */
+       if (cflag & PARENB) {
+               if (cflag & PARODD) {
+                       lParity = LCR_PAR_ODD;
+                       dbg("%s - parity = odd", __FUNCTION__);
+               } else {
+                       lParity = LCR_PAR_EVEN;
+                       dbg("%s - parity = even", __FUNCTION__);
+               }
+
+       } else {
+               dbg("%s - parity = none", __FUNCTION__);
+       }
+
+       if (cflag & CMSPAR) {
+               lParity = lParity | 0x20;
+       }
+
+       /* Change the Stop bit */
+       if (cflag & CSTOPB) {
+               lStop = LCR_STOP_2;
+               dbg("%s - stop bits = 2", __FUNCTION__);
+       } else {
+               lStop = LCR_STOP_1;
+               dbg("%s - stop bits = 1", __FUNCTION__);
+       }
+
+       /* Update the LCR with the correct value */
+       mos7840_port->shadowLCR &=
+           ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK);
+       mos7840_port->shadowLCR |= (lData | lParity | lStop);
+
+       dbg("mos7840_change_port_settings mos7840_port->shadowLCR is %x\n",
+           mos7840_port->shadowLCR);
+       /* Disable Interrupts */
+       Data = 0x00;
+       mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+
+       Data = 0x00;
+       mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
+
+       Data = 0xcf;
+       mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
+
+       /* Send the updated LCR value to the mos7840 */
+       Data = mos7840_port->shadowLCR;
+
+       mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+
+       Data = 0x00b;
+       mos7840_port->shadowMCR = Data;
+       mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+       Data = 0x00b;
+       mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+
+       /* set up the MCR register and send it to the mos7840 */
+
+       mos7840_port->shadowMCR = MCR_MASTER_IE;
+       if (cflag & CBAUD) {
+               mos7840_port->shadowMCR |= (MCR_DTR | MCR_RTS);
+       }
+
+       if (cflag & CRTSCTS) {
+               mos7840_port->shadowMCR |= (MCR_XON_ANY);
+
+       } else {
+               mos7840_port->shadowMCR &= ~(MCR_XON_ANY);
+       }
+
+       Data = mos7840_port->shadowMCR;
+       mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+
+       /* Determine divisor based on baud rate */
+       baud = tty_get_baud_rate(tty);
+
+       if (!baud) {
+               /* pick a default, any default... */
+               dbg("%s\n", "Picked default baud...");
+               baud = 9600;
+       }
+
+       dbg("%s - baud rate = %d", __FUNCTION__, baud);
+       status = mos7840_send_cmd_write_baud_rate(mos7840_port, baud);
+
+       /* Enable Interrupts */
+       Data = 0x0c;
+       mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+
+       if (mos7840_port->read_urb->status != -EINPROGRESS) {
+               mos7840_port->read_urb->dev = serial->dev;
+
+               status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
+
+               if (status) {
+                       dbg(" usb_submit_urb(read bulk) failed, status = %d",
+                           status);
+               }
+       }
+       wake_up(&mos7840_port->delta_msr_wait);
+       mos7840_port->delta_msr_cond = 1;
+       dbg("mos7840_change_port_settings mos7840_port->shadowLCR is End %x\n",
+           mos7840_port->shadowLCR);
+
+       return;
+}
+
+/*****************************************************************************
+ * mos7840_set_termios
+ *     this function is called by the tty driver when it wants to change
+ *     the termios structure
+ *****************************************************************************/
+
+static void mos7840_set_termios(struct usb_serial_port *port,
+                               struct termios *old_termios)
+{
+       int status;
+       unsigned int cflag;
+       struct usb_serial *serial;
+       struct moschip_port *mos7840_port;
+       struct tty_struct *tty;
+       dbg("mos7840_set_termios: START\n");
+       if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+               dbg("%s", "Invalid port \n");
+               return;
+       }
+
+       serial = port->serial;
+
+       if (mos7840_serial_paranoia_check(serial, __FUNCTION__)) {
+               dbg("%s", "Invalid Serial \n");
+               return;
+       }
+
+       mos7840_port = mos7840_get_port_private(port);
+
+       if (mos7840_port == NULL)
+               return;
+
+       tty = port->tty;
+
+       if (!port->tty || !port->tty->termios) {
+               dbg("%s - no tty or termios", __FUNCTION__);
+               return;
+       }
+
+       if (!mos7840_port->open) {
+               dbg("%s - port not opened", __FUNCTION__);
+               return;
+       }
+
+       dbg("%s\n", "setting termios - ");
+
+       cflag = tty->termios->c_cflag;
+
+       if (!cflag) {
+               dbg("%s %s\n", __FUNCTION__, "cflag is NULL");
+               return;
+       }
+
+       /* check that they really want us to change something */
+       if (old_termios) {
+               if ((cflag == old_termios->c_cflag) &&
+                   (RELEVANT_IFLAG(tty->termios->c_iflag) ==
+                    RELEVANT_IFLAG(old_termios->c_iflag))) {
+                       dbg("%s\n", "Nothing to change");
+                       return;
+               }
+       }
+
+       dbg("%s - clfag %08x iflag %08x", __FUNCTION__,
+           tty->termios->c_cflag, RELEVANT_IFLAG(tty->termios->c_iflag));
+
+       if (old_termios) {
+               dbg("%s - old clfag %08x old iflag %08x", __FUNCTION__,
+                   old_termios->c_cflag, RELEVANT_IFLAG(old_termios->c_iflag));
+       }
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       /* change the port settings to the new ones specified */
+
+       mos7840_change_port_settings(mos7840_port, old_termios);
+
+       if (!mos7840_port->read_urb) {
+               dbg("%s", "URB KILLED !!!!!\n");
+               return;
+       }
+
+       if (mos7840_port->read_urb->status != -EINPROGRESS) {
+               mos7840_port->read_urb->dev = serial->dev;
+               status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
+               if (status) {
+                       dbg(" usb_submit_urb(read bulk) failed, status = %d",
+                           status);
+               }
+       }
+       return;
+}
+
+/*****************************************************************************
+ * mos7840_get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ *         is emptied.  On bus types like RS485, the transmitter must
+ *         release the bus after transmitting. This must be done when
+ *         the transmit shift register is empty, not be done when the
+ *         transmit holding register is empty.  This functionality
+ *         allows an RS485 driver to be written in user space.
+ *****************************************************************************/
+
+static int mos7840_get_lsr_info(struct moschip_port *mos7840_port,
+                               unsigned int *value)
+{
+       int count;
+       unsigned int result = 0;
+
+       count = mos7840_chars_in_buffer(mos7840_port->port);
+       if (count == 0) {
+               dbg("%s -- Empty", __FUNCTION__);
+               result = TIOCSER_TEMT;
+       }
+
+       if (copy_to_user(value, &result, sizeof(int)))
+               return -EFAULT;
+       return 0;
+}
+
+/*****************************************************************************
+ * mos7840_get_bytes_avail - get number of bytes available
+ *
+ * Purpose: Let user call ioctl to get the count of number of bytes available.
+ *****************************************************************************/
+
+static int mos7840_get_bytes_avail(struct moschip_port *mos7840_port,
+                                  unsigned int *value)
+{
+       unsigned int result = 0;
+       struct tty_struct *tty = mos7840_port->port->tty;
+
+       if (!tty)
+               return -ENOIOCTLCMD;
+
+       result = tty->read_cnt;
+
+       dbg("%s(%d) = %d", __FUNCTION__, mos7840_port->port->number, result);
+       if (copy_to_user(value, &result, sizeof(int)))
+               return -EFAULT;
+
+       return -ENOIOCTLCMD;
+}
+
+/*****************************************************************************
+ * mos7840_set_modem_info
+ *      function to set modem info
+ *****************************************************************************/
+
+static int mos7840_set_modem_info(struct moschip_port *mos7840_port,
+                                 unsigned int cmd, unsigned int *value)
+{
+       unsigned int mcr;
+       unsigned int arg;
+       __u16 Data;
+       int status;
+       struct usb_serial_port *port;
+
+       if (mos7840_port == NULL)
+               return -1;
+
+       port = (struct usb_serial_port *)mos7840_port->port;
+       if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+               dbg("%s", "Invalid port \n");
+               return -1;
+       }
+
+       mcr = mos7840_port->shadowMCR;
+
+       if (copy_from_user(&arg, value, sizeof(int)))
+               return -EFAULT;
+
+       switch (cmd) {
+       case TIOCMBIS:
+               if (arg & TIOCM_RTS)
+                       mcr |= MCR_RTS;
+               if (arg & TIOCM_DTR)
+                       mcr |= MCR_RTS;
+               if (arg & TIOCM_LOOP)
+                       mcr |= MCR_LOOPBACK;
+               break;
+
+       case TIOCMBIC:
+               if (arg & TIOCM_RTS)
+                       mcr &= ~MCR_RTS;
+               if (arg & TIOCM_DTR)
+                       mcr &= ~MCR_RTS;
+               if (arg & TIOCM_LOOP)
+                       mcr &= ~MCR_LOOPBACK;
+               break;
+
+       case TIOCMSET:
+               /* turn off the RTS and DTR and LOOPBACK
+                * and then only turn on what was asked to */
+               mcr &= ~(MCR_RTS | MCR_DTR | MCR_LOOPBACK);
+               mcr |= ((arg & TIOCM_RTS) ? MCR_RTS : 0);
+               mcr |= ((arg & TIOCM_DTR) ? MCR_DTR : 0);
+               mcr |= ((arg & TIOCM_LOOP) ? MCR_LOOPBACK : 0);
+               break;
+       }
+
+       mos7840_port->shadowMCR = mcr;
+
+       Data = mos7840_port->shadowMCR;
+       status = 0;
+       status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+       if (status < 0) {
+               dbg("setting MODEM_CONTROL_REGISTER Failed\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+/*****************************************************************************
+ * mos7840_get_modem_info
+ *      function to get modem info
+ *****************************************************************************/
+
+static int mos7840_get_modem_info(struct moschip_port *mos7840_port,
+                                 unsigned int *value)
+{
+       unsigned int result = 0;
+       __u16 msr;
+       unsigned int mcr = mos7840_port->shadowMCR;
+       int status = 0;
+       status =
+           mos7840_get_uart_reg(mos7840_port->port, MODEM_STATUS_REGISTER,
+                                &msr);
+       result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0)      /* 0x002 */
+           |((mcr & MCR_RTS) ? TIOCM_RTS : 0)  /* 0x004 */
+           |((msr & MOS7840_MSR_CTS) ? TIOCM_CTS : 0)  /* 0x020 */
+           |((msr & MOS7840_MSR_CD) ? TIOCM_CAR : 0)   /* 0x040 */
+           |((msr & MOS7840_MSR_RI) ? TIOCM_RI : 0)    /* 0x080 */
+           |((msr & MOS7840_MSR_DSR) ? TIOCM_DSR : 0); /* 0x100 */
+
+       dbg("%s -- %x", __FUNCTION__, result);
+
+       if (copy_to_user(value, &result, sizeof(int)))
+               return -EFAULT;
+       return 0;
+}
+
+/*****************************************************************************
+ * mos7840_get_serial_info
+ *      function to get information about serial port
+ *****************************************************************************/
+
+static int mos7840_get_serial_info(struct moschip_port *mos7840_port,
+                                  struct serial_struct *retinfo)
+{
+       struct serial_struct tmp;
+
+       if (mos7840_port == NULL)
+               return -1;
+
+       if (!retinfo)
+               return -EFAULT;
+
+       memset(&tmp, 0, sizeof(tmp));
+
+       tmp.type = PORT_16550A;
+       tmp.line = mos7840_port->port->serial->minor;
+       tmp.port = mos7840_port->port->number;
+       tmp.irq = 0;
+       tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
+       tmp.xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE;
+       tmp.baud_base = 9600;
+       tmp.close_delay = 5 * HZ;
+       tmp.closing_wait = 30 * HZ;
+
+       if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+               return -EFAULT;
+       return 0;
+}
+
+/*****************************************************************************
+ * SerialIoctl
+ *     this function handles any ioctl calls to the driver
+ *****************************************************************************/
+
+static int mos7840_ioctl(struct usb_serial_port *port, struct file *file,
+                        unsigned int cmd, unsigned long arg)
+{
+       struct moschip_port *mos7840_port;
+       struct tty_struct *tty;
+
+       struct async_icount cnow;
+       struct async_icount cprev;
+       struct serial_icounter_struct icount;
+       int mosret = 0;
+       int retval;
+       struct tty_ldisc *ld;
+
+       if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+               dbg("%s", "Invalid port \n");
+               return -1;
+       }
+
+       mos7840_port = mos7840_get_port_private(port);
+       tty = mos7840_port->port->tty;
+
+       if (mos7840_port == NULL)
+               return -1;
+
+       dbg("%s - port %d, cmd = 0x%x", __FUNCTION__, port->number, cmd);
+
+       switch (cmd) {
+               /* return number of bytes available */
+
+       case TIOCINQ:
+               dbg("%s (%d) TIOCINQ", __FUNCTION__, port->number);
+               return mos7840_get_bytes_avail(mos7840_port,
+                                              (unsigned int *)arg);
+               break;
+
+       case TIOCOUTQ:
+               dbg("%s (%d) TIOCOUTQ", __FUNCTION__, port->number);
+               return put_user(tty->driver->chars_in_buffer ?
+                               tty->driver->chars_in_buffer(tty) : 0,
+                               (int __user *)arg);
+               break;
+
+       case TCFLSH:
+               retval = tty_check_change(tty);
+               if (retval)
+                       return retval;
+
+               ld = tty_ldisc_ref(tty);
+               switch (arg) {
+               case TCIFLUSH:
+                       if (ld && ld->flush_buffer)
+                               ld->flush_buffer(tty);
+                       break;
+               case TCIOFLUSH:
+                       if (ld && ld->flush_buffer)
+                               ld->flush_buffer(tty);
+                       /* fall through */
+               case TCOFLUSH:
+                       if (tty->driver->flush_buffer)
+                               tty->driver->flush_buffer(tty);
+                       break;
+               default:
+                       tty_ldisc_deref(ld);
+                       return -EINVAL;
+               }
+               tty_ldisc_deref(ld);
+               return 0;
+
+       case TCGETS:
+               if (kernel_termios_to_user_termios
+                   ((struct termios __user *)arg, tty->termios))
+                       return -EFAULT;
+               return 0;
+
+       case TIOCSERGETLSR:
+               dbg("%s (%d) TIOCSERGETLSR", __FUNCTION__, port->number);
+               return mos7840_get_lsr_info(mos7840_port, (unsigned int *)arg);
+               return 0;
+
+       case TIOCMBIS:
+       case TIOCMBIC:
+       case TIOCMSET:
+               dbg("%s (%d) TIOCMSET/TIOCMBIC/TIOCMSET", __FUNCTION__,
+                   port->number);
+               mosret =
+                   mos7840_set_modem_info(mos7840_port, cmd,
+                                          (unsigned int *)arg);
+               return mosret;
+
+       case TIOCMGET:
+               dbg("%s (%d) TIOCMGET", __FUNCTION__, port->number);
+               return mos7840_get_modem_info(mos7840_port,
+                                             (unsigned int *)arg);
+
+       case TIOCGSERIAL:
+               dbg("%s (%d) TIOCGSERIAL", __FUNCTION__, port->number);
+               return mos7840_get_serial_info(mos7840_port,
+                                              (struct serial_struct *)arg);
+
+       case TIOCSSERIAL:
+               dbg("%s (%d) TIOCSSERIAL", __FUNCTION__, port->number);
+               break;
+
+       case TIOCMIWAIT:
+               dbg("%s (%d) TIOCMIWAIT", __FUNCTION__, port->number);
+               cprev = mos7840_port->icount;
+               while (1) {
+                       //interruptible_sleep_on(&mos7840_port->delta_msr_wait);
+                       mos7840_port->delta_msr_cond = 0;
+                       wait_event_interruptible(mos7840_port->delta_msr_wait,
+                                                (mos7840_port->
+                                                 delta_msr_cond == 1));
+
+                       /* see if a signal did it */
+                       if (signal_pending(current))
+                               return -ERESTARTSYS;
+                       cnow = mos7840_port->icount;
+                       if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+                           cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+                               return -EIO;    /* no change => error */
+                       if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+                           ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+                           ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
+                           ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {
+                               return 0;
+                       }
+                       cprev = cnow;
+               }
+               /* NOTREACHED */
+               break;
+
+       case TIOCGICOUNT:
+               cnow = mos7840_port->icount;
+               icount.cts = cnow.cts;
+               icount.dsr = cnow.dsr;
+               icount.rng = cnow.rng;
+               icount.dcd = cnow.dcd;
+               icount.rx = cnow.rx;
+               icount.tx = cnow.tx;
+               icount.frame = cnow.frame;
+               icount.overrun = cnow.overrun;
+               icount.parity = cnow.parity;
+               icount.brk = cnow.brk;
+               icount.buf_overrun = cnow.buf_overrun;
+
+               dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d", __FUNCTION__,
+                   port->number, icount.rx, icount.tx);
+               if (copy_to_user((void *)arg, &icount, sizeof(icount)))
+                       return -EFAULT;
+               return 0;
+
+       case TIOCEXBAUD:
+               return 0;
+       default:
+               break;
+       }
+
+       return -ENOIOCTLCMD;
+}
+
+static int mos7840_calc_num_ports(struct usb_serial *serial)
+{
+
+       dbg("numberofendpoints: %d \n",
+           (int)serial->interface->cur_altsetting->desc.bNumEndpoints);
+       dbg("numberofendpoints: %d \n",
+           (int)serial->interface->altsetting->desc.bNumEndpoints);
+       if (serial->interface->cur_altsetting->desc.bNumEndpoints == 5) {
+               mos7840_num_ports = 2;
+               serial->type->num_ports = 2;
+       } else if (serial->interface->cur_altsetting->desc.bNumEndpoints == 9) {
+               mos7840_num_ports = 4;
+               serial->type->num_bulk_in = 4;
+               serial->type->num_bulk_out = 4;
+               serial->type->num_ports = 4;
+       }
+
+       return mos7840_num_ports;
+}
+
+/****************************************************************************
+ * mos7840_startup
+ ****************************************************************************/
+
+static int mos7840_startup(struct usb_serial *serial)
+{
+       struct moschip_port *mos7840_port;
+       struct usb_device *dev;
+       int i, status;
+
+       __u16 Data;
+       dbg("%s \n", " mos7840_startup :entering..........");
+
+       if (!serial) {
+               dbg("%s\n", "Invalid Handler");
+               return -1;
+       }
+
+       dev = serial->dev;
+
+       dbg("%s\n", "Entering...");
+
+       /* we set up the pointers to the endpoints in the mos7840_open *
+        * function, as the structures aren't created yet.             */
+
+       /* set up port private structures */
+       for (i = 0; i < serial->num_ports; ++i) {
+               mos7840_port = kmalloc(sizeof(struct moschip_port), GFP_KERNEL);
+               if (mos7840_port == NULL) {
+                       err("%s - Out of memory", __FUNCTION__);
+                       return -ENOMEM;
+               }
+               memset(mos7840_port, 0, sizeof(struct moschip_port));
+
+               /* Initialize all port interrupt end point to port 0 int endpoint *
+                * Our device has only one interrupt end point comman to all port */
+
+               mos7840_port->port = serial->port[i];
+               mos7840_set_port_private(serial->port[i], mos7840_port);
+
+               mos7840_port->port_num = ((serial->port[i]->number -
+                                          (serial->port[i]->serial->minor)) +
+                                         1);
+
+               if (mos7840_port->port_num == 1) {
+                       mos7840_port->SpRegOffset = 0x0;
+                       mos7840_port->ControlRegOffset = 0x1;
+                       mos7840_port->DcrRegOffset = 0x4;
+               } else if ((mos7840_port->port_num == 2)
+                          && (mos7840_num_ports == 4)) {
+                       mos7840_port->SpRegOffset = 0x8;
+                       mos7840_port->ControlRegOffset = 0x9;
+                       mos7840_port->DcrRegOffset = 0x16;
+               } else if ((mos7840_port->port_num == 2)
+                          && (mos7840_num_ports == 2)) {
+                       mos7840_port->SpRegOffset = 0xa;
+                       mos7840_port->ControlRegOffset = 0xb;
+                       mos7840_port->DcrRegOffset = 0x19;
+               } else if ((mos7840_port->port_num == 3)
+                          && (mos7840_num_ports == 4)) {
+                       mos7840_port->SpRegOffset = 0xa;
+                       mos7840_port->ControlRegOffset = 0xb;
+                       mos7840_port->DcrRegOffset = 0x19;
+               } else if ((mos7840_port->port_num == 4)
+                          && (mos7840_num_ports == 4)) {
+                       mos7840_port->SpRegOffset = 0xc;
+                       mos7840_port->ControlRegOffset = 0xd;
+                       mos7840_port->DcrRegOffset = 0x1c;
+               }
+               mos7840_dump_serial_port(mos7840_port);
+
+               mos7840_set_port_private(serial->port[i], mos7840_port);
+
+               //enable rx_disable bit in control register
+
+               status =
+                   mos7840_get_reg_sync(serial->port[i],
+                                        mos7840_port->ControlRegOffset, &Data);
+               if (status < 0) {
+                       dbg("Reading ControlReg failed status-0x%x\n", status);
+                       break;
+               } else
+                       dbg("ControlReg Reading success val is %x, status%d\n",
+                           Data, status);
+               Data |= 0x08;   //setting driver done bit
+               Data |= 0x04;   //sp1_bit to have cts change reflect in modem status reg
+
+               //Data |= 0x20; //rx_disable bit
+               status = 0;
+               status =
+                   mos7840_set_reg_sync(serial->port[i],
+                                        mos7840_port->ControlRegOffset, Data);
+               if (status < 0) {
+                       dbg("Writing ControlReg failed(rx_disable) status-0x%x\n", status);
+                       break;
+               } else
+                       dbg("ControlReg Writing success(rx_disable) status%d\n",
+                           status);
+
+               //Write default values in DCR (i.e 0x01 in DCR0, 0x05 in DCR2 and 0x24 in DCR3
+               Data = 0x01;
+               status = 0;
+               status =
+                   mos7840_set_reg_sync(serial->port[i],
+                                        (__u16) (mos7840_port->DcrRegOffset +
+                                                 0), Data);
+               if (status < 0) {
+                       dbg("Writing DCR0 failed status-0x%x\n", status);
+                       break;
+               } else
+                       dbg("DCR0 Writing success status%d\n", status);
+
+               Data = 0x05;
+               status = 0;
+               status =
+                   mos7840_set_reg_sync(serial->port[i],
+                                        (__u16) (mos7840_port->DcrRegOffset +
+                                                 1), Data);
+               if (status < 0) {
+                       dbg("Writing DCR1 failed status-0x%x\n", status);
+                       break;
+               } else
+                       dbg("DCR1 Writing success status%d\n", status);
+
+               Data = 0x24;
+               status = 0;
+               status =
+                   mos7840_set_reg_sync(serial->port[i],
+                                        (__u16) (mos7840_port->DcrRegOffset +
+                                                 2), Data);
+               if (status < 0) {
+                       dbg("Writing DCR2 failed status-0x%x\n", status);
+                       break;
+               } else
+                       dbg("DCR2 Writing success status%d\n", status);
+
+               // write values in clkstart0x0 and clkmulti 0x20
+               Data = 0x0;
+               status = 0;
+               status =
+                   mos7840_set_reg_sync(serial->port[i],
+                                        CLK_START_VALUE_REGISTER, Data);
+               if (status < 0) {
+                       dbg("Writing CLK_START_VALUE_REGISTER failed status-0x%x\n", status);
+                       break;
+               } else
+                       dbg("CLK_START_VALUE_REGISTER Writing success status%d\n", status);
+
+               Data = 0x20;
+               status = 0;
+               status =
+                   mos7840_set_reg_sync(serial->port[i], CLK_MULTI_REGISTER,
+                                        Data);
+               if (status < 0) {
+                       dbg("Writing CLK_MULTI_REGISTER failed status-0x%x\n",
+                           status);
+                       break;
+               } else
+                       dbg("CLK_MULTI_REGISTER Writing success status%d\n",
+                           status);
+
+               //write value 0x0 to scratchpad register
+               Data = 0x00;
+               status = 0;
+               status =
+                   mos7840_set_uart_reg(serial->port[i], SCRATCH_PAD_REGISTER,
+                                        Data);
+               if (status < 0) {
+                       dbg("Writing SCRATCH_PAD_REGISTER failed status-0x%x\n",
+                           status);
+                       break;
+               } else
+                       dbg("SCRATCH_PAD_REGISTER Writing success status%d\n",
+                           status);
+
+               //Zero Length flag register
+               if ((mos7840_port->port_num != 1)
+                   && (mos7840_num_ports == 2)) {
+
+                       Data = 0xff;
+                       status = 0;
+                       status = mos7840_set_reg_sync(serial->port[i],
+                                                     (__u16) (ZLP_REG1 +
+                                                              ((__u16)
+                                                               mos7840_port->
+                                                               port_num)),
+                                                     Data);
+                       dbg("ZLIP offset%x\n",
+                           (__u16) (ZLP_REG1 +
+                                    ((__u16) mos7840_port->port_num)));
+                       if (status < 0) {
+                               dbg("Writing ZLP_REG%d failed status-0x%x\n",
+                                   i + 2, status);
+                               break;
+                       } else
+                               dbg("ZLP_REG%d Writing success status%d\n",
+                                   i + 2, status);
+               } else {
+                       Data = 0xff;
+                       status = 0;
+                       status = mos7840_set_reg_sync(serial->port[i],
+                                                     (__u16) (ZLP_REG1 +
+                                                              ((__u16)
+                                                               mos7840_port->
+                                                               port_num) -
+                                                              0x1), Data);
+                       dbg("ZLIP offset%x\n",
+                           (__u16) (ZLP_REG1 +
+                                    ((__u16) mos7840_port->port_num) - 0x1));
+                       if (status < 0) {
+                               dbg("Writing ZLP_REG%d failed status-0x%x\n",
+                                   i + 1, status);
+                               break;
+                       } else
+                               dbg("ZLP_REG%d Writing success status%d\n",
+                                   i + 1, status);
+
+               }
+               mos7840_port->control_urb = usb_alloc_urb(0, SLAB_ATOMIC);
+               mos7840_port->ctrl_buf = kmalloc(16, GFP_KERNEL);
+
+       }
+
+       //Zero Length flag enable
+       Data = 0x0f;
+       status = 0;
+       status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, Data);
+       if (status < 0) {
+               dbg("Writing ZLP_REG5 failed status-0x%x\n", status);
+               return -1;
+       } else
+               dbg("ZLP_REG5 Writing success status%d\n", status);
+
+       /* setting configuration feature to one */
+       usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+                       (__u8) 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 5 * HZ);
+       return 0;
+}
+
+/****************************************************************************
+ * mos7840_shutdown
+ *     This function is called whenever the device is removed from the usb bus.
+ ****************************************************************************/
+
+static void mos7840_shutdown(struct usb_serial *serial)
+{
+       int i;
+       struct moschip_port *mos7840_port;
+       dbg("%s \n", " shutdown :entering..........");
+
+       if (!serial) {
+               dbg("%s", "Invalid Handler \n");
+               return;
+       }
+
+       /*      check for the ports to be closed,close the ports and disconnect         */
+
+       /* free private structure allocated for serial port  *
+        * stop reads and writes on all ports                */
+
+       for (i = 0; i < serial->num_ports; ++i) {
+               mos7840_port = mos7840_get_port_private(serial->port[i]);
+               kfree(mos7840_port->ctrl_buf);
+               usb_kill_urb(mos7840_port->control_urb);
+               kfree(mos7840_port);
+               mos7840_set_port_private(serial->port[i], NULL);
+       }
+
+       dbg("%s\n", "Thank u :: ");
+
+}
+
+static struct usb_serial_driver moschip7840_4port_device = {
+       .driver = {
+                  .owner = THIS_MODULE,
+                  .name = "mos7840",
+                  },
+       .description = DRIVER_DESC,
+       .id_table = moschip_port_id_table,
+       .num_interrupt_in = 1,  //NUM_DONT_CARE,//1,
+#ifdef check
+       .num_bulk_in = 4,
+       .num_bulk_out = 4,
+       .num_ports = 4,
+#endif
+       .open = mos7840_open,
+       .close = mos7840_close,
+       .write = mos7840_write,
+       .write_room = mos7840_write_room,
+       .chars_in_buffer = mos7840_chars_in_buffer,
+       .throttle = mos7840_throttle,
+       .unthrottle = mos7840_unthrottle,
+       .calc_num_ports = mos7840_calc_num_ports,
+#ifdef MCSSerialProbe
+       .probe = mos7840_serial_probe,
+#endif
+       .ioctl = mos7840_ioctl,
+       .set_termios = mos7840_set_termios,
+       .break_ctl = mos7840_break,
+       .tiocmget = mos7840_tiocmget,
+       .tiocmset = mos7840_tiocmset,
+       .attach = mos7840_startup,
+       .shutdown = mos7840_shutdown,
+       .read_bulk_callback = mos7840_bulk_in_callback,
+       .read_int_callback = mos7840_interrupt_callback,
+};
+
+static struct usb_driver io_driver = {
+       .name = "mos7840",
+       .probe = usb_serial_probe,
+       .disconnect = usb_serial_disconnect,
+       .id_table = moschip_id_table_combined,
+};
+
+/****************************************************************************
+ * moschip7840_init
+ *     This is called by the module subsystem, or on startup to initialize us
+ ****************************************************************************/
+static int __init moschip7840_init(void)
+{
+       int retval;
+
+       dbg("%s \n", " mos7840_init :entering..........");
+
+       /* Register with the usb serial */
+       retval = usb_serial_register(&moschip7840_4port_device);
+
+       if (retval)
+               goto failed_port_device_register;
+
+       dbg("%s\n", "Entring...");
+       info(DRIVER_DESC " " DRIVER_VERSION);
+
+       /* Register with the usb */
+       retval = usb_register(&io_driver);
+
+       if (retval)
+               goto failed_usb_register;
+
+       if (retval == 0) {
+               dbg("%s\n", "Leaving...");
+               return 0;
+       }
+
+      failed_usb_register:
+       usb_serial_deregister(&moschip7840_4port_device);
+
+      failed_port_device_register:
+
+       return retval;
+}
+
+/****************************************************************************
+ * moschip7840_exit
+ *     Called when the driver is about to be unloaded.
+ ****************************************************************************/
+static void __exit moschip7840_exit(void)
+{
+
+       dbg("%s \n", " mos7840_exit :entering..........");
+
+       usb_deregister(&io_driver);
+
+       usb_serial_deregister(&moschip7840_4port_device);
+
+       dbg("%s\n", "Entring...");
+}
+
+module_init(moschip7840_init);
+module_exit(moschip7840_exit);
+
+/* Module information */
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
index 65e4d04..1036d43 100644 (file)
@@ -81,10 +81,11 @@ static struct usb_device_id id_table [] = {
        { USB_DEVICE(SPEEDDRAGON_VENDOR_ID, SPEEDDRAGON_PRODUCT_ID) },
        { USB_DEVICE(DATAPILOT_U2_VENDOR_ID, DATAPILOT_U2_PRODUCT_ID) },
        { USB_DEVICE(BELKIN_VENDOR_ID, BELKIN_PRODUCT_ID) },
+       { USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID) },
        { }                                     /* Terminating entry */
 };
 
-MODULE_DEVICE_TABLE (usb, id_table);
+MODULE_DEVICE_TABLE(usb, id_table);
 
 static struct usb_driver pl2303_driver = {
        .name =         "pl2303",
@@ -127,65 +128,6 @@ static struct usb_driver pl2303_driver = {
 #define UART_OVERRUN_ERROR             0x40
 #define UART_CTS                       0x80
 
-/* function prototypes for a PL2303 serial converter */
-static int pl2303_open (struct usb_serial_port *port, struct file *filp);
-static void pl2303_close (struct usb_serial_port *port, struct file *filp);
-static void pl2303_set_termios (struct usb_serial_port *port,
-                               struct termios *old);
-static int pl2303_ioctl (struct usb_serial_port *port, struct file *file,
-                        unsigned int cmd, unsigned long arg);
-static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs);
-static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs);
-static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs);
-static int pl2303_write (struct usb_serial_port *port,
-                        const unsigned char *buf, int count);
-static void pl2303_send (struct usb_serial_port *port);
-static int pl2303_write_room(struct usb_serial_port *port);
-static int pl2303_chars_in_buffer(struct usb_serial_port *port);
-static void pl2303_break_ctl(struct usb_serial_port *port,int break_state);
-static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file);
-static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
-                           unsigned int set, unsigned int clear);
-static int pl2303_startup (struct usb_serial *serial);
-static void pl2303_shutdown (struct usb_serial *serial);
-static struct pl2303_buf *pl2303_buf_alloc(unsigned int size);
-static void pl2303_buf_free(struct pl2303_buf *pb);
-static void pl2303_buf_clear(struct pl2303_buf *pb);
-static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb);
-static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb);
-static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
-       unsigned int count);
-static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
-       unsigned int count);
-
-
-/* All of the device info needed for the PL2303 SIO serial converter */
-static struct usb_serial_driver pl2303_device = {
-       .driver = {
-               .owner =        THIS_MODULE,
-               .name =         "pl2303",
-       },
-       .id_table =             id_table,
-       .num_interrupt_in =     NUM_DONT_CARE,
-       .num_bulk_in =          1,
-       .num_bulk_out =         1,
-       .num_ports =            1,
-       .open =                 pl2303_open,
-       .close =                pl2303_close,
-       .write =                pl2303_write,
-       .ioctl =                pl2303_ioctl,
-       .break_ctl =            pl2303_break_ctl,
-       .set_termios =          pl2303_set_termios,
-       .tiocmget =             pl2303_tiocmget,
-       .tiocmset =             pl2303_tiocmset,
-       .read_bulk_callback =   pl2303_read_bulk_callback,
-       .read_int_callback =    pl2303_read_int_callback,
-       .write_bulk_callback =  pl2303_write_bulk_callback,
-       .write_room =           pl2303_write_room,
-       .chars_in_buffer =      pl2303_chars_in_buffer,
-       .attach =               pl2303_startup,
-       .shutdown =             pl2303_shutdown,
-};
 
 enum pl2303_type {
        type_0,         /* don't know the difference between type 0 and */
@@ -204,8 +146,166 @@ struct pl2303_private {
        enum pl2303_type type;
 };
 
+/*
+ * pl2303_buf_alloc
+ *
+ * Allocate a circular buffer and all associated memory.
+ */
+static struct pl2303_buf *pl2303_buf_alloc(unsigned int size)
+{
+       struct pl2303_buf *pb;
+
+       if (size == 0)
+               return NULL;
+
+       pb = (struct pl2303_buf *)kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL);
+       if (pb == NULL)
+               return NULL;
+
+       pb->buf_buf = kmalloc(size, GFP_KERNEL);
+       if (pb->buf_buf == NULL) {
+               kfree(pb);
+               return NULL;
+       }
+
+       pb->buf_size = size;
+       pb->buf_get = pb->buf_put = pb->buf_buf;
+
+       return pb;
+}
+
+/*
+ * pl2303_buf_free
+ *
+ * Free the buffer and all associated memory.
+ */
+static void pl2303_buf_free(struct pl2303_buf *pb)
+{
+       if (pb) {
+               kfree(pb->buf_buf);
+               kfree(pb);
+       }
+}
+
+/*
+ * pl2303_buf_clear
+ *
+ * Clear out all data in the circular buffer.
+ */
+static void pl2303_buf_clear(struct pl2303_buf *pb)
+{
+       if (pb != NULL)
+               pb->buf_get = pb->buf_put;
+               /* equivalent to a get of all data available */
+}
+
+/*
+ * pl2303_buf_data_avail
+ *
+ * Return the number of bytes of data available in the circular
+ * buffer.
+ */
+static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb)
+{
+       if (pb == NULL)
+               return 0;
+
+       return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size);
+}
+
+/*
+ * pl2303_buf_space_avail
+ *
+ * Return the number of bytes of space available in the circular
+ * buffer.
+ */
+static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb)
+{
+       if (pb == NULL)
+               return 0;
+
+       return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size);
+}
+
+/*
+ * pl2303_buf_put
+ *
+ * Copy data data from a user buffer and put it into the circular buffer.
+ * Restrict to the amount of space available.
+ *
+ * Return the number of bytes copied.
+ */
+static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
+                                  unsigned int count)
+{
+       unsigned int len;
+
+       if (pb == NULL)
+               return 0;
+
+       len  = pl2303_buf_space_avail(pb);
+       if (count > len)
+               count = len;
+
+       if (count == 0)
+               return 0;
+
+       len = pb->buf_buf + pb->buf_size - pb->buf_put;
+       if (count > len) {
+               memcpy(pb->buf_put, buf, len);
+               memcpy(pb->buf_buf, buf+len, count - len);
+               pb->buf_put = pb->buf_buf + count - len;
+       } else {
+               memcpy(pb->buf_put, buf, count);
+               if (count < len)
+                       pb->buf_put += count;
+               else /* count == len */
+                       pb->buf_put = pb->buf_buf;
+       }
+
+       return count;
+}
+
+/*
+ * pl2303_buf_get
+ *
+ * Get data from the circular buffer and copy to the given buffer.
+ * Restrict to the amount of data available.
+ *
+ * Return the number of bytes copied.
+ */
+static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
+                                  unsigned int count)
+{
+       unsigned int len;
+
+       if (pb == NULL)
+               return 0;
+
+       len = pl2303_buf_data_avail(pb);
+       if (count > len)
+               count = len;
+
+       if (count == 0)
+               return 0;
+
+       len = pb->buf_buf + pb->buf_size - pb->buf_get;
+       if (count > len) {
+               memcpy(buf, pb->buf_get, len);
+               memcpy(buf+len, pb->buf_buf, count - len);
+               pb->buf_get = pb->buf_buf + count - len;
+       } else {
+               memcpy(buf, pb->buf_get, count);
+               if (count < len)
+                       pb->buf_get += count;
+               else /* count == len */
+                       pb->buf_get = pb->buf_buf;
+       }
+
+       return count;
+}
 
-static int pl2303_startup (struct usb_serial *serial)
+static int pl2303_startup(struct usb_serial *serial)
 {
        struct pl2303_private *priv;
        enum pl2303_type type = type_0;
@@ -247,36 +347,17 @@ cleanup:
        return -ENOMEM;
 }
 
-static int set_control_lines (struct usb_device *dev, u8 value)
+static int set_control_lines(struct usb_device *dev, u8 value)
 {
        int retval;
        
-       retval = usb_control_msg (dev, usb_sndctrlpipe (dev, 0),
-                                 SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
-                                 value, 0, NULL, 0, 100);
+       retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                                SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
+                                value, 0, NULL, 0, 100);
        dbg("%s - value = %d, retval = %d", __FUNCTION__, value, retval);
        return retval;
 }
 
-static int pl2303_write (struct usb_serial_port *port,  const unsigned char *buf, int count)
-{
-       struct pl2303_private *priv = usb_get_serial_port_data(port);
-       unsigned long flags;
-
-       dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count);
-
-       if (!count)
-               return count;
-
-       spin_lock_irqsave(&priv->lock, flags);
-       count = pl2303_buf_put(priv->buf, buf, count);
-       spin_unlock_irqrestore(&priv->lock, flags);
-
-       pl2303_send(port);
-
-       return count;
-}
-
 static void pl2303_send(struct usb_serial_port *port)
 {
        int count, result;
@@ -293,7 +374,7 @@ static void pl2303_send(struct usb_serial_port *port)
        }
 
        count = pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer,
-               port->bulk_out_size);
+                              port->bulk_out_size);
 
        if (count == 0) {
                spin_unlock_irqrestore(&priv->lock, flags);
@@ -304,13 +385,15 @@ static void pl2303_send(struct usb_serial_port *port)
 
        spin_unlock_irqrestore(&priv->lock, flags);
 
-       usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, port->write_urb->transfer_buffer);
+       usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count,
+                             port->write_urb->transfer_buffer);
 
        port->write_urb->transfer_buffer_length = count;
        port->write_urb->dev = port->serial->dev;
-       result = usb_submit_urb (port->write_urb, GFP_ATOMIC);
+       result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
        if (result) {
-               dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result);
+               dev_err(&port->dev, "%s - failed submitting write urb,"
+                       " error %d\n", __FUNCTION__, result);
                priv->write_urb_in_use = 0;
                // TODO: reschedule pl2303_send
        }
@@ -318,6 +401,26 @@ static void pl2303_send(struct usb_serial_port *port)
        usb_serial_port_softint(port);
 }
 
+static int pl2303_write(struct usb_serial_port *port, const unsigned char *buf,
+                       int count)
+{
+       struct pl2303_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
+
+       dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count);
+
+       if (!count)
+               return count;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       count = pl2303_buf_put(priv->buf, buf, count);
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       pl2303_send(port);
+
+       return count;
+}
+
 static int pl2303_write_room(struct usb_serial_port *port)
 {
        struct pl2303_private *priv = usb_get_serial_port_data(port);
@@ -350,7 +453,8 @@ static int pl2303_chars_in_buffer(struct usb_serial_port *port)
        return chars;
 }
 
-static void pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios)
+static void pl2303_set_termios(struct usb_serial_port *port,
+                              struct termios *old_termios)
 {
        struct usb_serial *serial = port->serial;
        struct pl2303_private *priv = usb_get_serial_port_data(port);
@@ -371,7 +475,8 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
        spin_lock_irqsave(&priv->lock, flags);
        if (!priv->termios_initialized) {
                *(port->tty->termios) = tty_std_termios;
-               port->tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+               port->tty->termios->c_cflag = B9600 | CS8 | CREAD |
+                                             HUPCL | CLOCAL;
                priv->termios_initialized = 1;
        }
        spin_unlock_irqrestore(&priv->lock, flags);
@@ -380,24 +485,24 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
        /* check that they really want us to change something */
        if (old_termios) {
                if ((cflag == old_termios->c_cflag) &&
-                   (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
-                   dbg("%s - nothing to change...", __FUNCTION__);
-                   return;
+                   (RELEVANT_IFLAG(port->tty->termios->c_iflag) ==
+                    RELEVANT_IFLAG(old_termios->c_iflag))) {
+                       dbg("%s - nothing to change...", __FUNCTION__);
+                       return;
                }
        }
 
-       buf = kzalloc (7, GFP_KERNEL);
+       buf = kzalloc(7, GFP_KERNEL);
        if (!buf) {
                dev_err(&port->dev, "%s - out of memory.\n", __FUNCTION__);
                return;
        }
-       
-       i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0),
-                            GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
-                            0, 0, buf, 7, 100);
-       dbg ("0xa1:0x21:0:0  %d - %x %x %x %x %x %x %x", i,
-            buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
 
+       i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+                           GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
+                           0, 0, buf, 7, 100);
+       dbg("0xa1:0x21:0:0  %d - %x %x %x %x %x %x %x", i,
+           buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
 
        if (cflag & CSIZE) {
                switch (cflag & CSIZE) {
@@ -429,7 +534,8 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
                case B230400:   baud = 230400;  break;
                case B460800:   baud = 460800;  break;
                default:
-                       dev_err(&port->dev, "pl2303 driver does not support the baudrate requested (fix it)\n");
+                       dev_err(&port->dev, "pl2303 driver does not support"
+                               " the baudrate requested (fix it)\n");
                        break;
        }
        dbg("%s - baud = %d", __FUNCTION__, baud);
@@ -469,10 +575,10 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
                dbg("%s - parity = none", __FUNCTION__);
        }
 
-       i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
-                            SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE, 
-                            0, 0, buf, 7, 100);
-       dbg ("0x21:0x20:0:0  %d", i);
+       i = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+                           SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE,
+                           0, 0, buf, 7, 100);
+       dbg("0x21:0x20:0:0  %d", i);
 
        /* change control lines if we are switching to or from B0 */
        spin_lock_irqsave(&priv->lock, flags);
@@ -488,13 +594,13 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
        } else {
                spin_unlock_irqrestore(&priv->lock, flags);
        }
-       
+
        buf[0] = buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = 0;
 
-       i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0),
-                            GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
-                            0, 0, buf, 7, 100);
-       dbg ("0xa1:0x21:0:0  %d - %x %x %x %x %x %x %x", i,
+       i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+                           GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
+                           0, 0, buf, 7, 100);
+       dbg("0xa1:0x21:0:0  %d - %x %x %x %x %x %x %x", i,
             buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
 
        if (cflag & CRTSCTS) {
@@ -503,18 +609,82 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
                        index = 0x61;
                else
                        index = 0x41;
-               i = usb_control_msg(serial->dev, 
+               i = usb_control_msg(serial->dev,
                                    usb_sndctrlpipe(serial->dev, 0),
                                    VENDOR_WRITE_REQUEST,
                                    VENDOR_WRITE_REQUEST_TYPE,
                                    0x0, index, NULL, 0, 100);
-               dbg ("0x40:0x1:0x0:0x%x  %d", index, i);
+               dbg("0x40:0x1:0x0:0x%x  %d", index, i);
        }
 
-       kfree (buf);
+       kfree(buf);
 }
 
-static int pl2303_open (struct usb_serial_port *port, struct file *filp)
+static void pl2303_close(struct usb_serial_port *port, struct file *filp)
+{
+       struct pl2303_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
+       unsigned int c_cflag;
+       int bps;
+       long timeout;
+       wait_queue_t wait;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       /* wait for data to drain from the buffer */
+       spin_lock_irqsave(&priv->lock, flags);
+       timeout = PL2303_CLOSING_WAIT;
+       init_waitqueue_entry(&wait, current);
+       add_wait_queue(&port->tty->write_wait, &wait);
+       for (;;) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (pl2303_buf_data_avail(priv->buf) == 0 ||
+                   timeout == 0 || signal_pending(current) ||
+                   !usb_get_intfdata(port->serial->interface)) /* disconnect */
+                       break;
+               spin_unlock_irqrestore(&priv->lock, flags);
+               timeout = schedule_timeout(timeout);
+               spin_lock_irqsave(&priv->lock, flags);
+       }
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(&port->tty->write_wait, &wait);
+       /* clear out any remaining data in the buffer */
+       pl2303_buf_clear(priv->buf);
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       /* wait for characters to drain from the device */
+       /* (this is long enough for the entire 256 byte */
+       /* pl2303 hardware buffer to drain with no flow */
+       /* control for data rates of 1200 bps or more, */
+       /* for lower rates we should really know how much */
+       /* data is in the buffer to compute a delay */
+       /* that is not unnecessarily long) */
+       bps = tty_get_baud_rate(port->tty);
+       if (bps > 1200)
+               timeout = max((HZ*2560)/bps,HZ/10);
+       else
+               timeout = 2*HZ;
+       schedule_timeout_interruptible(timeout);
+
+       /* shutdown our urbs */
+       dbg("%s - shutting down urbs", __FUNCTION__);
+       usb_kill_urb(port->write_urb);
+       usb_kill_urb(port->read_urb);
+       usb_kill_urb(port->interrupt_in_urb);
+
+       if (port->tty) {
+               c_cflag = port->tty->termios->c_cflag;
+               if (c_cflag & HUPCL) {
+                       /* drop DTR and RTS */
+                       spin_lock_irqsave(&priv->lock, flags);
+                       priv->line_control = 0;
+                       spin_unlock_irqrestore(&priv->lock, flags);
+                       set_control_lines(port->serial->dev, 0);
+               }
+       }
+}
+
+static int pl2303_open(struct usb_serial_port *port, struct file *filp)
 {
        struct termios tmp_termios;
        struct usb_serial *serial = port->serial;
@@ -566,100 +736,37 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp)
 
        kfree(buf);
 
-       /* Setup termios */
-       if (port->tty) {
-               pl2303_set_termios (port, &tmp_termios);
-       }
-
-       //FIXME: need to assert RTS and DTR if CRTSCTS off
-
-       dbg("%s - submitting read urb", __FUNCTION__);
-       port->read_urb->dev = serial->dev;
-       result = usb_submit_urb (port->read_urb, GFP_KERNEL);
-       if (result) {
-               dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __FUNCTION__, result);
-               pl2303_close (port, NULL);
-               return -EPROTO;
-       }
-
-       dbg("%s - submitting interrupt urb", __FUNCTION__);
-       port->interrupt_in_urb->dev = serial->dev;
-       result = usb_submit_urb (port->interrupt_in_urb, GFP_KERNEL);
-       if (result) {
-               dev_err(&port->dev, "%s - failed submitting interrupt urb, error %d\n", __FUNCTION__, result);
-               pl2303_close (port, NULL);
-               return -EPROTO;
-       }
-       return 0;
-}
-
-
-static void pl2303_close (struct usb_serial_port *port, struct file *filp)
-{
-       struct pl2303_private *priv = usb_get_serial_port_data(port);
-       unsigned long flags;
-       unsigned int c_cflag;
-       int bps;
-       long timeout;
-       wait_queue_t wait;
-
-       dbg("%s - port %d", __FUNCTION__, port->number);
-
-       /* wait for data to drain from the buffer */
-       spin_lock_irqsave(&priv->lock, flags);
-       timeout = PL2303_CLOSING_WAIT;
-       init_waitqueue_entry(&wait, current);
-       add_wait_queue(&port->tty->write_wait, &wait);
-       for (;;) {
-               set_current_state(TASK_INTERRUPTIBLE);
-               if (pl2303_buf_data_avail(priv->buf) == 0
-               || timeout == 0 || signal_pending(current)
-               || !usb_get_intfdata(port->serial->interface))  /* disconnect */
-                       break;
-               spin_unlock_irqrestore(&priv->lock, flags);
-               timeout = schedule_timeout(timeout);
-               spin_lock_irqsave(&priv->lock, flags);
-       }
-       set_current_state(TASK_RUNNING);
-       remove_wait_queue(&port->tty->write_wait, &wait);
-       /* clear out any remaining data in the buffer */
-       pl2303_buf_clear(priv->buf);
-       spin_unlock_irqrestore(&priv->lock, flags);
-
-       /* wait for characters to drain from the device */
-       /* (this is long enough for the entire 256 byte */
-       /* pl2303 hardware buffer to drain with no flow */
-       /* control for data rates of 1200 bps or more, */
-       /* for lower rates we should really know how much */
-       /* data is in the buffer to compute a delay */
-       /* that is not unnecessarily long) */
-       bps = tty_get_baud_rate(port->tty);
-       if (bps > 1200)
-               timeout = max((HZ*2560)/bps,HZ/10);
-       else
-               timeout = 2*HZ;
-       schedule_timeout_interruptible(timeout);
-
-       /* shutdown our urbs */
-       dbg("%s - shutting down urbs", __FUNCTION__);
-       usb_kill_urb(port->write_urb);
-       usb_kill_urb(port->read_urb);
-       usb_kill_urb(port->interrupt_in_urb);
-
+       /* Setup termios */
        if (port->tty) {
-               c_cflag = port->tty->termios->c_cflag;
-               if (c_cflag & HUPCL) {
-                       /* drop DTR and RTS */
-                       spin_lock_irqsave(&priv->lock, flags);
-                       priv->line_control = 0;
-                       spin_unlock_irqrestore (&priv->lock, flags);
-                       set_control_lines (port->serial->dev, 0);
-               }
+               pl2303_set_termios(port, &tmp_termios);
+       }
+
+       //FIXME: need to assert RTS and DTR if CRTSCTS off
+
+       dbg("%s - submitting read urb", __FUNCTION__);
+       port->read_urb->dev = serial->dev;
+       result = usb_submit_urb(port->read_urb, GFP_KERNEL);
+       if (result) {
+               dev_err(&port->dev, "%s - failed submitting read urb,"
+                       " error %d\n", __FUNCTION__, result);
+               pl2303_close(port, NULL);
+               return -EPROTO;
+       }
+
+       dbg("%s - submitting interrupt urb", __FUNCTION__);
+       port->interrupt_in_urb->dev = serial->dev;
+       result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+       if (result) {
+               dev_err(&port->dev, "%s - failed submitting interrupt urb,"
+                       " error %d\n", __FUNCTION__, result);
+               pl2303_close(port, NULL);
+               return -EPROTO;
        }
+       return 0;
 }
 
-static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
-                           unsigned int set, unsigned int clear)
+static int pl2303_tiocmset(struct usb_serial_port *port, struct file *file,
+                          unsigned int set, unsigned int clear)
 {
        struct pl2303_private *priv = usb_get_serial_port_data(port);
        unsigned long flags;
@@ -668,7 +775,7 @@ static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
        if (!usb_get_intfdata(port->serial->interface))
                return -ENODEV;
 
-       spin_lock_irqsave (&priv->lock, flags);
+       spin_lock_irqsave(&priv->lock, flags);
        if (set & TIOCM_RTS)
                priv->line_control |= CONTROL_RTS;
        if (set & TIOCM_DTR)
@@ -678,12 +785,12 @@ static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
        if (clear & TIOCM_DTR)
                priv->line_control &= ~CONTROL_DTR;
        control = priv->line_control;
-       spin_unlock_irqrestore (&priv->lock, flags);
+       spin_unlock_irqrestore(&priv->lock, flags);
 
-       return set_control_lines (port->serial->dev, control);
+       return set_control_lines(port->serial->dev, control);
 }
 
-static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file)
+static int pl2303_tiocmget(struct usb_serial_port *port, struct file *file)
 {
        struct pl2303_private *priv = usb_get_serial_port_data(port);
        unsigned long flags;
@@ -696,10 +803,10 @@ static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file)
        if (!usb_get_intfdata(port->serial->interface))
                return -ENODEV;
 
-       spin_lock_irqsave (&priv->lock, flags);
+       spin_lock_irqsave(&priv->lock, flags);
        mcr = priv->line_control;
        status = priv->line_status;
-       spin_unlock_irqrestore (&priv->lock, flags);
+       spin_unlock_irqrestore(&priv->lock, flags);
 
        result = ((mcr & CONTROL_DTR)           ? TIOCM_DTR : 0)
                  | ((mcr & CONTROL_RTS)        ? TIOCM_RTS : 0)
@@ -721,22 +828,22 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
        unsigned int status;
        unsigned int changed;
 
-       spin_lock_irqsave (&priv->lock, flags);
+       spin_lock_irqsave(&priv->lock, flags);
        prevstatus = priv->line_status;
-       spin_unlock_irqrestore (&priv->lock, flags);
+       spin_unlock_irqrestore(&priv->lock, flags);
 
        while (1) {
                interruptible_sleep_on(&priv->delta_msr_wait);
                /* see if a signal did it */
                if (signal_pending(current))
                        return -ERESTARTSYS;
-               
-               spin_lock_irqsave (&priv->lock, flags);
+
+               spin_lock_irqsave(&priv->lock, flags);
                status = priv->line_status;
-               spin_unlock_irqrestore (&priv->lock, flags);
-               
+               spin_unlock_irqrestore(&priv->lock, flags);
+
                changed=prevstatus^status;
-               
+
                if (((arg & TIOCM_RNG) && (changed & UART_RING)) ||
                    ((arg & TIOCM_DSR) && (changed & UART_DSR)) ||
                    ((arg & TIOCM_CD)  && (changed & UART_DCD)) ||
@@ -749,7 +856,8 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
        return 0;
 }
 
-static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg)
+static int pl2303_ioctl(struct usb_serial_port *port, struct file *file,
+                       unsigned int cmd, unsigned long arg)
 {
        dbg("%s (%d) cmd = 0x%04x", __FUNCTION__, port->number, cmd);
 
@@ -766,7 +874,7 @@ static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsign
        return -ENOIOCTLCMD;
 }
 
-static void pl2303_break_ctl (struct usb_serial_port *port, int break_state)
+static void pl2303_break_ctl(struct usb_serial_port *port, int break_state)
 {
        struct usb_serial *serial = port->serial;
        u16 state;
@@ -780,15 +888,14 @@ static void pl2303_break_ctl (struct usb_serial_port *port, int break_state)
                state = BREAK_ON;
        dbg("%s - turning break %s", __FUNCTION__, state==BREAK_OFF ? "off" : "on");
 
-       result = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
-                                 BREAK_REQUEST, BREAK_REQUEST_TYPE, state, 
-                                 0, NULL, 0, 100);
+       result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+                                BREAK_REQUEST, BREAK_REQUEST_TYPE, state,
+                                0, NULL, 0, 100);
        if (result)
                dbg("%s - error sending break = %d", __FUNCTION__, result);
 }
 
-
-static void pl2303_shutdown (struct usb_serial *serial)
+static void pl2303_shutdown(struct usb_serial *serial)
 {
        int i;
        struct pl2303_private *priv;
@@ -802,7 +909,7 @@ static void pl2303_shutdown (struct usb_serial *serial)
                        kfree(priv);
                        usb_set_serial_port_data(serial->port[i], NULL);
                }
-       }               
+       }
 }
 
 static void pl2303_update_line_status(struct usb_serial_port *port,
@@ -814,29 +921,33 @@ static void pl2303_update_line_status(struct usb_serial_port *port,
        unsigned long flags;
        u8 status_idx = UART_STATE;
        u8 length = UART_STATE + 1;
+       u16 idv, idp;
+
+       idv = le16_to_cpu(port->serial->dev->descriptor.idVendor);
+       idp = le16_to_cpu(port->serial->dev->descriptor.idProduct);
 
-       if ((le16_to_cpu(port->serial->dev->descriptor.idVendor) == SIEMENS_VENDOR_ID) &&
-           (le16_to_cpu(port->serial->dev->descriptor.idProduct) == SIEMENS_PRODUCT_ID_X65 ||
-            le16_to_cpu(port->serial->dev->descriptor.idProduct) == SIEMENS_PRODUCT_ID_SX1 ||
-            le16_to_cpu(port->serial->dev->descriptor.idProduct) == SIEMENS_PRODUCT_ID_X75)) {
-               length = 1;
-               status_idx = 0;
+
+       if (idv == SIEMENS_VENDOR_ID) {
+               if (idp == SIEMENS_PRODUCT_ID_X65 ||
+                   idp == SIEMENS_PRODUCT_ID_SX1 ||
+                   idp == SIEMENS_PRODUCT_ID_X75) {
+
+                       length = 1;
+                       status_idx = 0;
+               }
        }
 
        if (actual_length < length)
-               goto exit;
+               return;
 
         /* Save off the uart status for others to look at */
        spin_lock_irqsave(&priv->lock, flags);
        priv->line_status = data[status_idx];
        spin_unlock_irqrestore(&priv->lock, flags);
-       wake_up_interruptible (&priv->delta_msr_wait);
-
-exit:
-       return;
+       wake_up_interruptible(&priv->delta_msr_wait);
 }
 
-static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs)
+static void pl2303_read_int_callback(struct urb *urb, struct pt_regs *regs)
 {
        struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
        unsigned char *data = urb->transfer_buffer;
@@ -853,25 +964,29 @@ static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs)
        case -ENOENT:
        case -ESHUTDOWN:
                /* this urb is terminated, clean up */
-               dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+               dbg("%s - urb shutting down with status: %d", __FUNCTION__,
+                   urb->status);
                return;
        default:
-               dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+               dbg("%s - nonzero urb status received: %d", __FUNCTION__,
+                   urb->status);
                goto exit;
        }
 
-       usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, urb->transfer_buffer);
+       usb_serial_debug_data(debug, &port->dev, __FUNCTION__,
+                             urb->actual_length, urb->transfer_buffer);
+
        pl2303_update_line_status(port, data, actual_length);
 
 exit:
-       status = usb_submit_urb (urb, GFP_ATOMIC);
+       status = usb_submit_urb(urb, GFP_ATOMIC);
        if (status)
-               dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with result %d\n",
+               dev_err(&urb->dev->dev,
+                       "%s - usb_submit_urb failed with result %d\n",
                        __FUNCTION__, status);
 }
 
-
-static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
+static void pl2303_read_bulk_callback(struct urb *urb, struct pt_regs *regs)
 {
        struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
        struct pl2303_private *priv = usb_get_serial_port_data(port);
@@ -892,20 +1007,25 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
                        return;
                }
                if (urb->status == -EPROTO) {
-                       /* PL2303 mysteriously fails with -EPROTO reschedule the read */
-                       dbg("%s - caught -EPROTO, resubmitting the urb", __FUNCTION__);
+                       /* PL2303 mysteriously fails with -EPROTO reschedule
+                        * the read */
+                       dbg("%s - caught -EPROTO, resubmitting the urb",
+                           __FUNCTION__);
                        urb->status = 0;
                        urb->dev = port->serial->dev;
                        result = usb_submit_urb(urb, GFP_ATOMIC);
                        if (result)
-                               dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
+                               dev_err(&urb->dev->dev, "%s - failed"
+                                       " resubmitting read urb, error %d\n",
+                                       __FUNCTION__, result);
                        return;
                }
                dbg("%s - unable to handle the error, exiting.", __FUNCTION__);
                return;
        }
 
-       usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
+       usb_serial_debug_data(debug, &port->dev, __FUNCTION__,
+                             urb->actual_length, data);
 
        /* get tty_flag from status */
        tty_flag = TTY_NORMAL;
@@ -914,7 +1034,7 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
        status = priv->line_status;
        priv->line_status &= ~UART_STATE_TRANSIENT_MASK;
        spin_unlock_irqrestore(&priv->lock, flags);
-       wake_up_interruptible (&priv->delta_msr_wait);
+       wake_up_interruptible(&priv->delta_msr_wait);
 
        /* break takes precedence over parity, */
        /* which takes precedence over framing errors */
@@ -933,8 +1053,8 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
                if (status & UART_OVERRUN_ERROR)
                        tty_insert_flip_char(tty, 0, TTY_OVERRUN);
                for (i = 0; i < urb->actual_length; ++i)
-                       tty_insert_flip_char (tty, data[i], tty_flag);
-               tty_flip_buffer_push (tty);
+                       tty_insert_flip_char(tty, data[i], tty_flag);
+               tty_flip_buffer_push(tty);
        }
 
        /* Schedule the next read _if_ we are still open */
@@ -942,15 +1062,14 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
                urb->dev = port->serial->dev;
                result = usb_submit_urb(urb, GFP_ATOMIC);
                if (result)
-                       dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
+                       dev_err(&urb->dev->dev, "%s - failed resubmitting"
+                               " read urb, error %d\n", __FUNCTION__, result);
        }
 
        return;
 }
 
-
-
-static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
+static void pl2303_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
 {
        struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
        struct pl2303_private *priv = usb_get_serial_port_data(port);
@@ -966,18 +1085,21 @@ static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
        case -ENOENT:
        case -ESHUTDOWN:
                /* this urb is terminated, clean up */
-               dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+               dbg("%s - urb shutting down with status: %d", __FUNCTION__,
+                   urb->status);
                priv->write_urb_in_use = 0;
                return;
        default:
                /* error in the urb, so we have to resubmit it */
                dbg("%s - Overflow in write", __FUNCTION__);
-               dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+               dbg("%s - nonzero write bulk status received: %d", __FUNCTION__,
+                   urb->status);
                port->write_urb->transfer_buffer_length = 1;
                port->write_urb->dev = port->serial->dev;
-               result = usb_submit_urb (port->write_urb, GFP_ATOMIC);
+               result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
                if (result)
-                       dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", __FUNCTION__, result);
+                       dev_err(&urb->dev->dev, "%s - failed resubmitting write"
+                               " urb, error %d\n", __FUNCTION__, result);
                else
                        return;
        }
@@ -988,191 +1110,38 @@ static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
        pl2303_send(port);
 }
 
+/* All of the device info needed for the PL2303 SIO serial converter */
+static struct usb_serial_driver pl2303_device = {
+       .driver = {
+               .owner =        THIS_MODULE,
+               .name =         "pl2303",
+       },
+       .id_table =             id_table,
+       .num_interrupt_in =     NUM_DONT_CARE,
+       .num_bulk_in =          1,
+       .num_bulk_out =         1,
+       .num_ports =            1,
+       .open =                 pl2303_open,
+       .close =                pl2303_close,
+       .write =                pl2303_write,
+       .ioctl =                pl2303_ioctl,
+       .break_ctl =            pl2303_break_ctl,
+       .set_termios =          pl2303_set_termios,
+       .tiocmget =             pl2303_tiocmget,
+       .tiocmset =             pl2303_tiocmset,
+       .read_bulk_callback =   pl2303_read_bulk_callback,
+       .read_int_callback =    pl2303_read_int_callback,
+       .write_bulk_callback =  pl2303_write_bulk_callback,
+       .write_room =           pl2303_write_room,
+       .chars_in_buffer =      pl2303_chars_in_buffer,
+       .attach =               pl2303_startup,
+       .shutdown =             pl2303_shutdown,
+};
 
-/*
- * pl2303_buf_alloc
- *
- * Allocate a circular buffer and all associated memory.
- */
-
-static struct pl2303_buf *pl2303_buf_alloc(unsigned int size)
-{
-
-       struct pl2303_buf *pb;
-
-
-       if (size == 0)
-               return NULL;
-
-       pb = (struct pl2303_buf *)kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL);
-       if (pb == NULL)
-               return NULL;
-
-       pb->buf_buf = kmalloc(size, GFP_KERNEL);
-       if (pb->buf_buf == NULL) {
-               kfree(pb);
-               return NULL;
-       }
-
-       pb->buf_size = size;
-       pb->buf_get = pb->buf_put = pb->buf_buf;
-
-       return pb;
-
-}
-
-
-/*
- * pl2303_buf_free
- *
- * Free the buffer and all associated memory.
- */
-
-static void pl2303_buf_free(struct pl2303_buf *pb)
-{
-       if (pb) {
-               kfree(pb->buf_buf);
-               kfree(pb);
-       }
-}
-
-
-/*
- * pl2303_buf_clear
- *
- * Clear out all data in the circular buffer.
- */
-
-static void pl2303_buf_clear(struct pl2303_buf *pb)
-{
-       if (pb != NULL)
-               pb->buf_get = pb->buf_put;
-               /* equivalent to a get of all data available */
-}
-
-
-/*
- * pl2303_buf_data_avail
- *
- * Return the number of bytes of data available in the circular
- * buffer.
- */
-
-static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb)
-{
-       if (pb != NULL)
-               return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size);
-       else
-               return 0;
-}
-
-
-/*
- * pl2303_buf_space_avail
- *
- * Return the number of bytes of space available in the circular
- * buffer.
- */
-
-static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb)
-{
-       if (pb != NULL)
-               return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size);
-       else
-               return 0;
-}
-
-
-/*
- * pl2303_buf_put
- *
- * Copy data data from a user buffer and put it into the circular buffer.
- * Restrict to the amount of space available.
- *
- * Return the number of bytes copied.
- */
-
-static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
-       unsigned int count)
-{
-
-       unsigned int len;
-
-
-       if (pb == NULL)
-               return 0;
-
-       len  = pl2303_buf_space_avail(pb);
-       if (count > len)
-               count = len;
-
-       if (count == 0)
-               return 0;
-
-       len = pb->buf_buf + pb->buf_size - pb->buf_put;
-       if (count > len) {
-               memcpy(pb->buf_put, buf, len);
-               memcpy(pb->buf_buf, buf+len, count - len);
-               pb->buf_put = pb->buf_buf + count - len;
-       } else {
-               memcpy(pb->buf_put, buf, count);
-               if (count < len)
-                       pb->buf_put += count;
-               else /* count == len */
-                       pb->buf_put = pb->buf_buf;
-       }
-
-       return count;
-
-}
-
-
-/*
- * pl2303_buf_get
- *
- * Get data from the circular buffer and copy to the given buffer.
- * Restrict to the amount of data available.
- *
- * Return the number of bytes copied.
- */
-
-static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
-       unsigned int count)
-{
-
-       unsigned int len;
-
-
-       if (pb == NULL)
-               return 0;
-
-       len = pl2303_buf_data_avail(pb);
-       if (count > len)
-               count = len;
-
-       if (count == 0)
-               return 0;
-
-       len = pb->buf_buf + pb->buf_size - pb->buf_get;
-       if (count > len) {
-               memcpy(buf, pb->buf_get, len);
-               memcpy(buf+len, pb->buf_buf, count - len);
-               pb->buf_get = pb->buf_buf + count - len;
-       } else {
-               memcpy(buf, pb->buf_get, count);
-               if (count < len)
-                       pb->buf_get += count;
-               else /* count == len */
-                       pb->buf_get = pb->buf_buf;
-       }
-
-       return count;
-
-}
-
-static int __init pl2303_init (void)
+static int __init pl2303_init(void)
 {
        int retval;
+
        retval = usb_serial_register(&pl2303_device);
        if (retval)
                goto failed_usb_serial_register;
@@ -1187,14 +1156,12 @@ failed_usb_serial_register:
        return retval;
 }
 
-
-static void __exit pl2303_exit (void)
+static void __exit pl2303_exit(void)
 {
-       usb_deregister (&pl2303_driver);
-       usb_serial_deregister (&pl2303_device);
+       usb_deregister(&pl2303_driver);
+       usb_serial_deregister(&pl2303_device);
 }
 
-
 module_init(pl2303_init);
 module_exit(pl2303_exit);
 
index 55195e7..762cc29 100644 (file)
@@ -89,3 +89,7 @@
 /* Belkin "F5U257" Serial Adapter */
 #define BELKIN_VENDOR_ID       0x050d
 #define BELKIN_PRODUCT_ID      0x0257
+
+/* Alcor Micro Corp. USB 2.0 TO RS-232 */
+#define ALCOR_VENDOR_ID                0x058F
+#define ALCOR_PRODUCT_ID       0x9720
index e06a41b..0222d92 100644 (file)
@@ -676,33 +676,29 @@ int usb_serial_probe(struct usb_interface *interface,
        iface_desc = interface->cur_altsetting;
        for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
                endpoint = &iface_desc->endpoint[i].desc;
-               
-               if ((endpoint->bEndpointAddress & 0x80) &&
-                   ((endpoint->bmAttributes & 3) == 0x02)) {
+
+               if (usb_endpoint_is_bulk_in(endpoint)) {
                        /* we found a bulk in endpoint */
                        dbg("found bulk in on endpoint %d", i);
                        bulk_in_endpoint[num_bulk_in] = endpoint;
                        ++num_bulk_in;
                }
 
-               if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
-                   ((endpoint->bmAttributes & 3) == 0x02)) {
+               if (usb_endpoint_is_bulk_out(endpoint)) {
                        /* we found a bulk out endpoint */
                        dbg("found bulk out on endpoint %d", i);
                        bulk_out_endpoint[num_bulk_out] = endpoint;
                        ++num_bulk_out;
                }
-               
-               if ((endpoint->bEndpointAddress & 0x80) &&
-                   ((endpoint->bmAttributes & 3) == 0x03)) {
+
+               if (usb_endpoint_is_int_in(endpoint)) {
                        /* we found a interrupt in endpoint */
                        dbg("found interrupt in on endpoint %d", i);
                        interrupt_in_endpoint[num_interrupt_in] = endpoint;
                        ++num_interrupt_in;
                }
 
-               if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
-                   ((endpoint->bmAttributes & 3) == 0x03)) {
+               if (usb_endpoint_is_int_out(endpoint)) {
                        /* we found an interrupt out endpoint */
                        dbg("found interrupt out on endpoint %d", i);
                        interrupt_out_endpoint[num_interrupt_out] = endpoint;
@@ -716,14 +712,15 @@ int usb_serial_probe(struct usb_interface *interface,
        if (((le16_to_cpu(dev->descriptor.idVendor) == PL2303_VENDOR_ID) &&
             (le16_to_cpu(dev->descriptor.idProduct) == PL2303_PRODUCT_ID)) ||
            ((le16_to_cpu(dev->descriptor.idVendor) == ATEN_VENDOR_ID) &&
-            (le16_to_cpu(dev->descriptor.idProduct) == ATEN_PRODUCT_ID))) {
+            (le16_to_cpu(dev->descriptor.idProduct) == ATEN_PRODUCT_ID)) ||
+           ((le16_to_cpu(dev->descriptor.idVendor) == ALCOR_VENDOR_ID) &&
+            (le16_to_cpu(dev->descriptor.idProduct) == ALCOR_PRODUCT_ID))) {
                if (interface != dev->actconfig->interface[0]) {
                        /* check out the endpoints of the other interface*/
                        iface_desc = dev->actconfig->interface[0]->cur_altsetting;
                        for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
                                endpoint = &iface_desc->endpoint[i].desc;
-                               if ((endpoint->bEndpointAddress & 0x80) &&
-                                   ((endpoint->bmAttributes & 3) == 0x03)) {
+                               if (usb_endpoint_is_int_in(endpoint)) {
                                        /* we found a interrupt in endpoint */
                                        dbg("found interrupt in for Prolific device on separate interface");
                                        interrupt_in_endpoint[num_interrupt_in] = endpoint;
@@ -937,7 +934,10 @@ int usb_serial_probe(struct usb_interface *interface,
 
                snprintf (&port->dev.bus_id[0], sizeof(port->dev.bus_id), "ttyUSB%d", port->number);
                dbg ("%s - registering %s", __FUNCTION__, port->dev.bus_id);
-               device_register (&port->dev);
+               retval = device_register(&port->dev);
+               if (retval)
+                       dev_err(&port->dev, "Error registering port device, "
+                               "continuing\n");
        }
 
        usb_serial_console_init (debug, minor);
index be9eec2..86e48c4 100644 (file)
@@ -135,6 +135,18 @@ config USB_STORAGE_ONETOUCH
          this input in any keybinding software. (e.g. gnome's keyboard short-
          cuts)
 
+config USB_STORAGE_KARMA
+       bool "Support for Rio Karma music player"
+       depends on USB_STORAGE
+       help
+         Say Y here to include additional code to support the Rio Karma
+         USB interface.
+
+         This code places the Rio Karma into mass storage mode, enabling
+         it to be mounted as an ordinary filesystem. Performing an eject
+         on the resulting scsi device node returns the Karma to normal
+         operation.
+
 config USB_LIBUSUAL
        bool "The shared table of common (or usual) storage devices"
        depends on USB
index 8cbba22..023969b 100644 (file)
@@ -20,6 +20,7 @@ usb-storage-obj-$(CONFIG_USB_STORAGE_DATAFAB) += datafab.o
 usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += jumpshot.o
 usb-storage-obj-$(CONFIG_USB_STORAGE_ALAUDA)   += alauda.o
 usb-storage-obj-$(CONFIG_USB_STORAGE_ONETOUCH) += onetouch.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_KARMA)    += karma.o
 
 usb-storage-objs :=    scsiglue.o protocol.o transport.o usb.o \
                        initializers.o $(usb-storage-obj-y)
index ab173b3..5b06f92 100644 (file)
 #include "debug.h"
 #include "transport.h"
 
-#define RIO_MSC 0x08
-#define RIOP_INIT "RIOP\x00\x01\x08"
-#define RIOP_INIT_LEN 7
-#define RIO_SEND_LEN 40
-#define RIO_RECV_LEN 0x200
-
 /* This places the Shuttle/SCM USB<->SCSI bridge devices in multi-target
  * mode */
 int usb_stor_euscsi_init(struct us_data *us)
@@ -97,70 +91,3 @@ int usb_stor_ucr61s2b_init(struct us_data *us)
 
        return (res ? -1 : 0);
 }
-
-/* Place the Rio Karma into mass storage mode.
- *
- * The initialization begins by sending 40 bytes starting
- * RIOP\x00\x01\x08\x00, which the device will ack with a 512-byte
- * packet with the high four bits set and everything else null.
- *
- * Next, we send RIOP\x80\x00\x08\x00.  Each time, a 512 byte response
- * must be read, but we must loop until byte 5 in the response is 0x08,
- * indicating success.  */
-int rio_karma_init(struct us_data *us)
-{
-       int result, partial;
-       char *recv;
-       unsigned long timeout;
-
-       // us->iobuf is big enough to hold cmd but not receive
-       if (!(recv = kmalloc(RIO_RECV_LEN, GFP_KERNEL)))
-               goto die_nomem;
-
-       US_DEBUGP("Initializing Karma...\n");
-
-       memset(us->iobuf, 0, RIO_SEND_LEN);
-       memcpy(us->iobuf, RIOP_INIT, RIOP_INIT_LEN);
-
-       result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
-               us->iobuf, RIO_SEND_LEN, &partial);
-       if (result != USB_STOR_XFER_GOOD)
-               goto die;
-
-       result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
-               recv, RIO_RECV_LEN, &partial);
-       if (result != USB_STOR_XFER_GOOD)
-               goto die;
-
-       us->iobuf[4] = 0x80;
-       us->iobuf[5] = 0;
-       timeout = jiffies + msecs_to_jiffies(3000);
-       for (;;) {
-               US_DEBUGP("Sending init command\n");
-               result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
-                       us->iobuf, RIO_SEND_LEN, &partial);
-               if (result != USB_STOR_XFER_GOOD)
-                       goto die;
-
-               result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
-                       recv, RIO_RECV_LEN, &partial);
-               if (result != USB_STOR_XFER_GOOD)
-                       goto die;
-
-               if (recv[5] == RIO_MSC)
-                       break;
-               if (time_after(jiffies, timeout))
-                       goto die;
-               msleep(10);
-       }
-       US_DEBUGP("Karma initialized.\n");
-       kfree(recv);
-       return 0;
-
-die:
-       kfree(recv);
-die_nomem:
-       US_DEBUGP("Could not initialize karma.\n");
-       return USB_STOR_TRANSPORT_FAILED;
-}
-
index 927f778..e2967a4 100644 (file)
@@ -47,4 +47,3 @@ int usb_stor_euscsi_init(struct us_data *us);
 /* This function is required to activate all four slots on the UCR-61S2B
  * flash reader */
 int usb_stor_ucr61s2b_init(struct us_data *us);
-int rio_karma_init(struct us_data *us);
diff --git a/drivers/usb/storage/karma.c b/drivers/usb/storage/karma.c
new file mode 100644 (file)
index 0000000..0d79ae5
--- /dev/null
@@ -0,0 +1,155 @@
+/* Driver for Rio Karma
+ *
+ *   (c) 2006 Bob Copeland <me@bobcopeland.com>
+ *   (c) 2006 Keith Bennett <keith@mcs.st-and.ac.uk>
+ *
+ * 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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+
+#include "usb.h"
+#include "transport.h"
+#include "debug.h"
+#include "karma.h"
+
+#define RIO_PREFIX "RIOP\x00"
+#define RIO_PREFIX_LEN 5
+#define RIO_SEND_LEN 40
+#define RIO_RECV_LEN 0x200
+
+#define RIO_ENTER_STORAGE 0x1
+#define RIO_LEAVE_STORAGE 0x2
+#define RIO_RESET 0xC
+
+extern int usb_stor_Bulk_transport(struct scsi_cmnd *, struct us_data *);
+
+struct karma_data {
+       int in_storage;
+       char *recv;
+};
+
+/*
+ * Send commands to Rio Karma.
+ *
+ * For each command we send 40 bytes starting 'RIOP\0' followed by
+ * the command number and a sequence number, which the device will ack
+ * with a 512-byte packet with the high four bits set and everything
+ * else null.  Then we send 'RIOP\x80' followed by a zero and the
+ * sequence number, until byte 5 in the response repeats the sequence
+ * number.
+ */
+static int rio_karma_send_command(char cmd, struct us_data *us)
+{
+       int result, partial;
+       unsigned long timeout;
+       static unsigned char seq = 1;
+       struct karma_data *data = (struct karma_data *) us->extra;
+
+       US_DEBUGP("karma: sending command %04x\n", cmd);
+       memset(us->iobuf, 0, RIO_SEND_LEN);
+       memcpy(us->iobuf, RIO_PREFIX, RIO_PREFIX_LEN);
+       us->iobuf[5] = cmd;
+       us->iobuf[6] = seq;
+
+       timeout = jiffies + msecs_to_jiffies(6000);
+       for (;;) {
+               result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
+                       us->iobuf, RIO_SEND_LEN, &partial);
+               if (result != USB_STOR_XFER_GOOD)
+                       goto err;
+
+               result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
+                       data->recv, RIO_RECV_LEN, &partial);
+               if (result != USB_STOR_XFER_GOOD)
+                       goto err;
+
+               if (data->recv[5] == seq)
+                       break;
+
+               if (time_after(jiffies, timeout))
+                       goto err;
+
+               us->iobuf[4] = 0x80;
+               us->iobuf[5] = 0;
+               msleep(50);
+       }
+
+       seq++;
+       if (seq == 0)
+               seq = 1;
+
+       US_DEBUGP("karma: sent command %04x\n", cmd);
+       return 0;
+err:
+       US_DEBUGP("karma: command %04x failed\n", cmd);
+       return USB_STOR_TRANSPORT_FAILED;
+}
+
+/*
+ * Trap START_STOP and READ_10 to leave/re-enter storage mode.
+ * Everything else is propagated to the normal bulk layer.
+ */
+int rio_karma_transport(struct scsi_cmnd *srb, struct us_data *us)
+{
+       int ret;
+       struct karma_data *data = (struct karma_data *) us->extra;
+
+       if (srb->cmnd[0] == READ_10 && !data->in_storage) {
+               ret = rio_karma_send_command(RIO_ENTER_STORAGE, us);
+               if (ret)
+                       return ret;
+
+               data->in_storage = 1;
+               return usb_stor_Bulk_transport(srb, us);
+       } else if (srb->cmnd[0] == START_STOP) {
+               ret = rio_karma_send_command(RIO_LEAVE_STORAGE, us);
+               if (ret)
+                       return ret;
+
+               data->in_storage = 0;
+               return rio_karma_send_command(RIO_RESET, us);
+       }
+       return usb_stor_Bulk_transport(srb, us);
+}
+
+static void rio_karma_destructor(void *extra)
+{
+       struct karma_data *data = (struct karma_data *) extra;
+       kfree(data->recv);
+}
+
+int rio_karma_init(struct us_data *us)
+{
+       int ret = 0;
+       struct karma_data *data = kzalloc(sizeof(struct karma_data), GFP_NOIO);
+       if (!data)
+               goto out;
+
+       data->recv = kmalloc(RIO_RECV_LEN, GFP_NOIO);
+       if (!data->recv) {
+               kfree(data);
+               goto out;
+       }
+
+       us->extra = data;
+       us->extra_destructor = rio_karma_destructor;
+       ret = rio_karma_send_command(RIO_ENTER_STORAGE, us);
+       data->in_storage = (ret == 0);
+out:
+       return ret;
+}
diff --git a/drivers/usb/storage/karma.h b/drivers/usb/storage/karma.h
new file mode 100644 (file)
index 0000000..8a60972
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef _KARMA_USB_H
+#define _KARMA_USB_H
+
+extern int rio_karma_init(struct us_data *us);
+extern int rio_karma_transport(struct scsi_cmnd *srb, struct us_data *us);
+
+#endif
index b1ec4a7..599ad10 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/usb.h>
 #include <linux/usb_usual.h>
 #include <linux/vmalloc.h>
+#include <linux/kthread.h>
 
 /*
  */
@@ -117,7 +118,7 @@ static int usu_probe(struct usb_interface *intf,
                         const struct usb_device_id *id)
 {
        unsigned long type;
-       int rc;
+       struct task_struct* task;
        unsigned long flags;
 
        type = USB_US_TYPE(id->driver_info);
@@ -132,8 +133,9 @@ static int usu_probe(struct usb_interface *intf,
        stat[type].fls |= USU_MOD_FL_THREAD;
        spin_unlock_irqrestore(&usu_lock, flags);
 
-       rc = kernel_thread(usu_probe_thread, (void*)type, CLONE_VM);
-       if (rc < 0) {
+       task = kthread_run(usu_probe_thread, (void*)type, "libusual_%d", type);
+       if (IS_ERR(task)) {
+               int rc = PTR_ERR(task);
                printk(KERN_WARNING "libusual: "
                    "Unable to start the thread for %s: %d\n",
                    bias_names[type], rc);
@@ -175,8 +177,6 @@ static int usu_probe_thread(void *arg)
        int rc;
        unsigned long flags;
 
-       daemonize("libusual_%d", type); /* "usb-storage" is kinda too long */
-
        /* A completion does not work here because it's counted. */
        down(&usu_init_notify);
        up(&usu_init_notify);
index 313920d..f843a0b 100644 (file)
@@ -135,6 +135,7 @@ int onetouch_connect_input(struct us_data *ss)
        struct usb_onetouch *onetouch;
        struct input_dev *input_dev;
        int pipe, maxp;
+       int error = -ENOMEM;
 
        interface = ss->pusb_intf->cur_altsetting;
 
@@ -211,15 +212,18 @@ int onetouch_connect_input(struct us_data *ss)
        ss->suspend_resume_hook = usb_onetouch_pm_hook;
 #endif
 
-       input_register_device(onetouch->dev);
+       error = input_register_device(onetouch->dev);
+       if (error)
+               goto fail3;
 
        return 0;
 
+ fail3:        usb_free_urb(onetouch->irq);
  fail2:        usb_buffer_free(udev, ONETOUCH_PKT_LEN,
                        onetouch->data, onetouch->data_dma);
  fail1:        kfree(onetouch);
        input_free_device(input_dev);
-       return -ENOMEM;
+       return error;
 }
 
 void onetouch_release_input(void *onetouch_)
index a4b7df9..e1072d5 100644 (file)
@@ -72,12 +72,27 @@ static const char* host_info(struct Scsi_Host *host)
 
 static int slave_alloc (struct scsi_device *sdev)
 {
+       struct us_data *us = host_to_us(sdev->host);
+
        /*
         * Set the INQUIRY transfer length to 36.  We don't use any of
         * the extra data and many devices choke if asked for more or
         * less than 36 bytes.
         */
        sdev->inquiry_len = 36;
+
+       /*
+        * The UFI spec treates the Peripheral Qualifier bits in an
+        * INQUIRY result as reserved and requires devices to set them
+        * to 0.  However the SCSI spec requires these bits to be set
+        * to 3 to indicate when a LUN is not present.
+        *
+        * Let the scanning code know if this target merely sets
+        * Peripheral Device Type to 0x1f to indicate no LUN.
+        */
+       if (us->subclass == US_SC_UFI)
+               sdev->sdev_target->pdt_1f_for_no_lun = 1;
+
        return 0;
 }
 
index d6acc92..f23514c 100644 (file)
@@ -294,11 +294,6 @@ static int interpret_urb_result(struct us_data *us, unsigned int pipe,
                        return USB_STOR_XFER_ERROR;
                return USB_STOR_XFER_STALLED;
 
-       /* timeout or excessively long NAK */
-       case -ETIMEDOUT:
-               US_DEBUGP("-- timeout or NAK\n");
-               return USB_STOR_XFER_ERROR;
-
        /* babble - the device tried to send more than we wanted to read */
        case -EOVERFLOW:
                US_DEBUGP("-- babble\n");
index b130e17..40bf159 100644 (file)
@@ -218,10 +218,12 @@ UNUSUAL_DEV(  0x0457, 0x0151, 0x0100, 0x0100,
                 US_SC_DEVICE, US_PR_DEVICE, NULL,
                 US_FL_NOT_LOCKABLE ),
 
+#ifdef CONFIG_USB_STORAGE_KARMA
 UNUSUAL_DEV(  0x045a, 0x5210, 0x0101, 0x0101,
                "Rio",
                "Rio Karma",
-               US_SC_SCSI, US_PR_BULK, rio_karma_init, 0),
+               US_SC_SCSI, US_PR_KARMA, rio_karma_init, 0),
+#endif
 
 /* Patch submitted by Philipp Friedrich <philipp@void.at> */
 UNUSUAL_DEV(  0x0482, 0x0100, 0x0100, 0x0100,
@@ -631,6 +633,13 @@ UNUSUAL_DEV(  0x0595, 0x4343, 0x0000, 0x2210,
                "Digital Camera EX-20 DSC",
                US_SC_8070, US_PR_DEVICE, NULL, 0 ),
 
+/* Reported by <Hendryk.Pfeiffer@gmx.de> */
+UNUSUAL_DEV(  0x059f, 0x0643, 0x0000, 0x0000,
+               "LaCie",
+               "DVD+-RW",
+               US_SC_DEVICE, US_PR_DEVICE, NULL,
+               US_FL_GO_SLOW ),
+
 /* Submitted by Joel Bourquard <numlock@freesurf.ch>
  * Some versions of this device need the SubClass and Protocol overrides
  * while others don't.
@@ -1254,6 +1263,13 @@ UNUSUAL_DEV(  0x0fce, 0xd008, 0x0000, 0x0000,
                US_SC_DEVICE, US_PR_DEVICE, NULL,
                US_FL_NO_WP_DETECT ),
 
+/* Reported by Jan Mate <mate@fiit.stuba.sk> */
+UNUSUAL_DEV(  0x0fce, 0xe030, 0x0000, 0x0000,
+               "Sony Ericsson",
+               "P990i",
+               US_SC_DEVICE, US_PR_DEVICE, NULL,
+               US_FL_FIX_CAPACITY ),
+
 /* Reported by Emmanuel Vasilakis <evas@forthnet.gr> */
 UNUSUAL_DEV(  0x0fce, 0xe031, 0x0000, 0x0000,
                "Sony Ericsson",
index 8d7bdcb..b8d6031 100644 (file)
@@ -98,6 +98,9 @@
 #ifdef CONFIG_USB_STORAGE_ALAUDA
 #include "alauda.h"
 #endif
+#ifdef CONFIG_USB_STORAGE_KARMA
+#include "karma.h"
+#endif
 
 /* Some informational data */
 MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
@@ -646,6 +649,14 @@ static int get_transport(struct us_data *us)
                break;
 #endif
 
+#ifdef CONFIG_USB_STORAGE_KARMA
+       case US_PR_KARMA:
+               us->transport_name = "Rio Karma/Bulk";
+               us->transport = rio_karma_transport;
+               us->transport_reset = usb_stor_Bulk_reset;
+               break;
+#endif
+
        default:
                return -EIO;
        }
index b362039..1b51d31 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * USB Skeleton driver - 2.0
+ * USB Skeleton driver - 2.2
  *
  * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
  *
@@ -7,9 +7,8 @@
  *     modify it under the terms of the GNU General Public License as
  *     published by the Free Software Foundation, version 2.
  *
- * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c 
- * but has been rewritten to be easy to read and use, as no locks are now
- * needed anymore.
+ * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c
+ * but has been rewritten to be easier to read and use.
  *
  */
 
@@ -21,6 +20,7 @@
 #include <linux/kref.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
+#include <linux/mutex.h>
 
 
 /* Define these values to match your devices */
@@ -32,38 +32,39 @@ static struct usb_device_id skel_table [] = {
        { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
        { }                                     /* Terminating entry */
 };
-MODULE_DEVICE_TABLE (usb, skel_table);
+MODULE_DEVICE_TABLE(usb, skel_table);
 
 
 /* Get a minor range for your devices from the usb maintainer */
 #define USB_SKEL_MINOR_BASE    192
 
 /* our private defines. if this grows any larger, use your own .h file */
-#define MAX_TRANSFER           ( PAGE_SIZE - 512 )
+#define MAX_TRANSFER           (PAGE_SIZE - 512)
 #define WRITES_IN_FLIGHT       8
 
 /* Structure to hold all of our device specific stuff */
 struct usb_skel {
-       struct usb_device *     udev;                   /* the usb device for this device */
-       struct usb_interface *  interface;              /* the interface for this device */
+       struct usb_device       *dev;                   /* the usb device for this device */
+       struct usb_interface    *interface;             /* the interface for this device */
        struct semaphore        limit_sem;              /* limiting the number of writes in progress */
-       unsigned char *         bulk_in_buffer;         /* the buffer to receive data */
+       unsigned char           *bulk_in_buffer;        /* the buffer to receive data */
        size_t                  bulk_in_size;           /* the size of the receive buffer */
        __u8                    bulk_in_endpointAddr;   /* the address of the bulk in endpoint */
        __u8                    bulk_out_endpointAddr;  /* the address of the bulk out endpoint */
        struct kref             kref;
+       struct mutex            io_mutex;               /* synchronize I/O with disconnect */
 };
 #define to_skel_dev(d) container_of(d, struct usb_skel, kref)
 
 static struct usb_driver skel_driver;
 
 static void skel_delete(struct kref *kref)
-{      
+{
        struct usb_skel *dev = to_skel_dev(kref);
 
        usb_put_dev(dev->udev);
-       kfree (dev->bulk_in_buffer);
-       kfree (dev);
+       kfree(dev->bulk_in_buffer);
+       kfree(dev);
 }
 
 static int skel_open(struct inode *inode, struct file *file)
@@ -89,6 +90,11 @@ static int skel_open(struct inode *inode, struct file *file)
                goto exit;
        }
 
+       /* prevent the device from being autosuspended */
+       retval = usb_autopm_get_interface(interface);
+       if (retval)
+               goto exit;
+
        /* increment our usage count for the device */
        kref_get(&dev->kref);
 
@@ -107,6 +113,12 @@ static int skel_release(struct inode *inode, struct file *file)
        if (dev == NULL)
                return -ENODEV;
 
+       /* allow the device to be autosuspended */
+       mutex_lock(&dev->io_mutex);
+       if (dev->interface)
+               usb_autopm_put_interface(dev->interface);
+       mutex_unlock(&dev->io_mutex);
+
        /* decrement the count on our device */
        kref_put(&dev->kref, skel_delete);
        return 0;
@@ -115,11 +127,17 @@ static int skel_release(struct inode *inode, struct file *file)
 static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
        struct usb_skel *dev;
-       int retval = 0;
+       int retval;
        int bytes_read;
 
        dev = (struct usb_skel *)file->private_data;
-       
+
+       mutex_lock(&dev->io_mutex);
+       if (!dev->interface) {          /* disconnect() was called */
+               retval = -ENODEV;
+               goto exit;
+       }
+
        /* do a blocking bulk read to get data from the device */
        retval = usb_bulk_msg(dev->udev,
                              usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
@@ -135,6 +153,8 @@ static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *
                        retval = bytes_read;
        }
 
+exit:
+       mutex_unlock(&dev->io_mutex);
        return retval;
 }
 
@@ -145,16 +165,16 @@ static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
        dev = (struct usb_skel *)urb->context;
 
        /* sync/async unlink faults aren't errors */
-       if (urb->status && 
-           !(urb->status == -ENOENT || 
+       if (urb->status &&
+           !(urb->status == -ENOENT ||
              urb->status == -ECONNRESET ||
              urb->status == -ESHUTDOWN)) {
-               dbg("%s - nonzero write bulk status received: %d",
+               err("%s - nonzero write bulk status received: %d",
                    __FUNCTION__, urb->status);
        }
 
        /* free up our allocated buffer */
-       usb_buffer_free(urb->dev, urb->transfer_buffer_length, 
+       usb_buffer_free(urb->dev, urb->transfer_buffer_length,
                        urb->transfer_buffer, urb->transfer_dma);
        up(&dev->limit_sem);
 }
@@ -179,6 +199,12 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
                goto exit;
        }
 
+       mutex_lock(&dev->io_mutex);
+       if (!dev->interface) {          /* disconnect() was called */
+               retval = -ENODEV;
+               goto error;
+       }
+
        /* create a urb, and a buffer for it, and copy the data to the urb */
        urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!urb) {
@@ -213,17 +239,22 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
        /* release our reference to this urb, the USB core will eventually free it entirely */
        usb_free_urb(urb);
 
-exit:
+       mutex_unlock(&dev->io_mutex);
        return writesize;
 
 error:
-       usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
-       usb_free_urb(urb);
+       if (urb) {
+               usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
+               usb_free_urb(urb);
+       }
+       mutex_unlock(&dev->io_mutex);
        up(&dev->limit_sem);
+
+exit:
        return retval;
 }
 
-static struct file_operations skel_fops = {
+static const struct file_operations skel_fops = {
        .owner =        THIS_MODULE,
        .read =         skel_read,
        .write =        skel_write,
@@ -231,7 +262,7 @@ static struct file_operations skel_fops = {
        .release =      skel_release,
 };
 
-/* 
+/*
  * usb class driver info in order to get a minor number from the usb core,
  * and to have the device registered with the driver core
  */
@@ -243,7 +274,7 @@ static struct usb_class_driver skel_class = {
 
 static int skel_probe(struct usb_interface *interface, const struct usb_device_id *id)
 {
-       struct usb_skel *dev = NULL;
+       struct usb_skel *dev;
        struct usb_host_interface *iface_desc;
        struct usb_endpoint_descriptor *endpoint;
        size_t buffer_size;
@@ -252,12 +283,13 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
 
        /* allocate memory for our device state and initialize it */
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-       if (dev == NULL) {
+       if (!dev) {
                err("Out of memory");
                goto error;
        }
        kref_init(&dev->kref);
        sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
+       mutex_init(&dev->io_mutex);
 
        dev->udev = usb_get_dev(interface_to_usbdev(interface));
        dev->interface = interface;
@@ -269,10 +301,7 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
                endpoint = &iface_desc->endpoint[i].desc;
 
                if (!dev->bulk_in_endpointAddr &&
-                   ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
-                                       == USB_DIR_IN) &&
-                   ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-                                       == USB_ENDPOINT_XFER_BULK)) {
+                   usb_endpoint_is_bulk_in(endpoint)) {
                        /* we found a bulk in endpoint */
                        buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
                        dev->bulk_in_size = buffer_size;
@@ -285,10 +314,7 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
                }
 
                if (!dev->bulk_out_endpointAddr &&
-                   ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
-                                       == USB_DIR_OUT) &&
-                   ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-                                       == USB_ENDPOINT_XFER_BULK)) {
+                   usb_endpoint_is_bulk_out(endpoint)) {
                        /* we found a bulk out endpoint */
                        dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
                }
@@ -334,6 +360,11 @@ static void skel_disconnect(struct usb_interface *interface)
        /* give back our minor */
        usb_deregister_dev(interface, &skel_class);
 
+       /* prevent more I/O from starting */
+       mutex_lock(&dev->io_mutex);
+       dev->interface = NULL;
+       mutex_unlock(&dev->io_mutex);
+
        unlock_kernel();
 
        /* decrement our usage count */
@@ -367,7 +398,7 @@ static void __exit usb_skel_exit(void)
        usb_deregister(&skel_driver);
 }
 
-module_init (usb_skel_init);
-module_exit (usb_skel_exit);
+module_init(usb_skel_init);
+module_exit(usb_skel_exit);
 
 MODULE_LICENSE("GPL");
index 30548a3..121cd24 100644 (file)
@@ -12,6 +12,14 @@ struct pxa2xx_udc_mach_info {
         void (*udc_command)(int cmd);
 #define        PXA2XX_UDC_CMD_CONNECT          0       /* let host see us */
 #define        PXA2XX_UDC_CMD_DISCONNECT       1       /* so host won't see us */
+
+       /* Boards following the design guidelines in the developer's manual,
+        * with on-chip GPIOs not Lubbock's wierd hardware, can have a sane
+        * VBUS IRQ and omit the methods above.  Store the GPIO number
+        * here; for GPIO 0, also mask in one of the pxa_gpio_mode() bits.
+        */
+       u16     gpio_vbus;                      /* high == vbus present */
+       u16     gpio_pullup;                    /* high == pullup activated */
 };
 
 extern void pxa_set_udc_info(struct pxa2xx_udc_mach_info *info);
index d2bd0c8..0da15b0 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/fs.h>          /* for struct file_operations */
 #include <linux/completion.h>  /* for struct completion */
 #include <linux/sched.h>       /* for current && schedule_timeout */
+#include <linux/mutex.h>       /* for struct mutex */
 
 struct usb_device;
 struct usb_driver;
@@ -102,8 +103,13 @@ enum usb_interface_condition {
  *     number from the USB core by calling usb_register_dev().
  * @condition: binding state of the interface: not bound, binding
  *     (in probe()), bound to a driver, or unbinding (in disconnect())
+ * @is_active: flag set when the interface is bound and not suspended.
+ * @needs_remote_wakeup: flag set when the driver requires remote-wakeup
+ *     capability during autosuspend.
  * @dev: driver model's view of this device
  * @class_dev: driver model's class view of this device.
+ * @pm_usage_cnt: PM usage counter for this interface; autosuspend is not
+ *     allowed unless the counter is 0.
  *
  * USB device drivers attach to interfaces on a physical device.  Each
  * interface encapsulates a single high level function, such as feeding
@@ -142,8 +148,12 @@ struct usb_interface {
        int minor;                      /* minor number this interface is
                                         * bound to */
        enum usb_interface_condition condition;         /* state of binding */
+       unsigned is_active:1;           /* the interface is not suspended */
+       unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */
+
        struct device dev;              /* interface specific device info */
        struct class_device *class_dev;
+       int pm_usage_cnt;               /* usage counter for autosuspend */
 };
 #define        to_usb_interface(d) container_of(d, struct usb_interface, dev)
 #define        interface_to_usbdev(intf) \
@@ -254,8 +264,6 @@ int __usb_get_extra_descriptor(char *buffer, unsigned size,
 
 /* ----------------------------------------------------------------------- */
 
-struct usb_operations;
-
 /* USB device number allocation bitmap */
 struct usb_devmap {
        unsigned long devicemap[128 / (8*sizeof(unsigned long))];
@@ -268,6 +276,7 @@ struct usb_bus {
        struct device *controller;      /* host/master side hardware */
        int busnum;                     /* Bus number (in order of reg) */
        char *bus_name;                 /* stable id (PCI slot_name etc) */
+       u8 uses_dma;                    /* Does the host controller use DMA? */
        u8 otg_port;                    /* 0, or number of OTG/HNP port */
        unsigned is_b_host:1;           /* true during some HNP roleswitches */
        unsigned b_hnp_enable:1;        /* OTG: did A-Host enable HNP? */
@@ -276,10 +285,8 @@ struct usb_bus {
                                         * round-robin allocation */
 
        struct usb_devmap devmap;       /* device address allocation map */
-       struct usb_operations *op;      /* Operations (specific to the HC) */
        struct usb_device *root_hub;    /* Root hub */
        struct list_head bus_list;      /* list of busses */
-       void *hcpriv;                   /* Host Controller private data */
 
        int bandwidth_allocated;        /* on this bus: how much of the time
                                         * reserved for periodic (intr/iso)
@@ -294,8 +301,6 @@ struct usb_bus {
        struct dentry *usbfs_dentry;    /* usbfs dentry entry for the bus */
 
        struct class_device *class_dev; /* class device for this bus */
-       struct kref kref;               /* reference counting for this bus */
-       void (*release)(struct usb_bus *bus);
 
 #if defined(CONFIG_USB_MON)
        struct mon_bus *mon_bus;        /* non-null when associated */
@@ -350,6 +355,7 @@ struct usb_device {
 
        unsigned short bus_mA;          /* Current available from the bus */
        u8 portnum;                     /* Parent port number (origin 1) */
+       u8 level;                       /* Number of USB hub ancestors */
 
        int have_langid;                /* whether string_langid is valid */
        int string_langid;              /* language ID for strings */
@@ -373,6 +379,15 @@ struct usb_device {
 
        int maxchild;                   /* Number of ports if hub */
        struct usb_device *children[USB_MAXCHILDREN];
+
+#ifdef CONFIG_PM
+       struct work_struct autosuspend; /* for delayed autosuspends */
+       struct mutex pm_mutex;          /* protects PM operations */
+       int pm_usage_cnt;               /* usage counter for autosuspend */
+
+       unsigned auto_pm:1;             /* autosuspend/resume in progress */
+       unsigned do_remote_wakeup:1;    /* remote wakeup should be enabled */
+#endif
 };
 #define        to_usb_device(d) container_of(d, struct usb_device, dev)
 
@@ -384,7 +399,7 @@ extern void usb_put_dev(struct usb_device *dev);
 #define usb_unlock_device(udev)                up(&(udev)->dev.sem)
 #define usb_trylock_device(udev)       down_trylock(&(udev)->dev.sem)
 extern int usb_lock_device_for_reset(struct usb_device *udev,
-               struct usb_interface *iface);
+                                    const struct usb_interface *iface);
 
 /* USB port reset for device reinitialization */
 extern int usb_reset_device(struct usb_device *dev);
@@ -393,6 +408,17 @@ extern int usb_reset_composite_device(struct usb_device *dev,
 
 extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id);
 
+/* USB autosuspend and autoresume */
+#ifdef CONFIG_USB_SUSPEND
+extern int usb_autopm_get_interface(struct usb_interface *intf);
+extern void usb_autopm_put_interface(struct usb_interface *intf);
+
+#else
+#define usb_autopm_get_interface(intf)         0
+#define usb_autopm_put_interface(intf)         do {} while (0)
+#endif
+
+
 /*-------------------------------------------------------------------------*/
 
 /* for drivers using iso endpoints */
@@ -423,10 +449,10 @@ const struct usb_device_id *usb_match_id(struct usb_interface *interface,
 
 extern struct usb_interface *usb_find_interface(struct usb_driver *drv,
                int minor);
-extern struct usb_interface *usb_ifnum_to_if(struct usb_device *dev,
+extern struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev,
                unsigned ifnum);
 extern struct usb_host_interface *usb_altnum_to_altsetting(
-               struct usb_interface *intf, unsigned int altnum);
+               const struct usb_interface *intf, unsigned int altnum);
 
 
 /**
@@ -464,6 +490,20 @@ static inline int usb_make_path (struct usb_device *dev, char *buf,
 
 /*-------------------------------------------------------------------------*/
 
+extern int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd);
+extern int usb_endpoint_dir_out(const struct usb_endpoint_descriptor *epd);
+extern int usb_endpoint_xfer_bulk(const struct usb_endpoint_descriptor *epd);
+extern int usb_endpoint_xfer_int(const struct usb_endpoint_descriptor *epd);
+extern int usb_endpoint_xfer_isoc(const struct usb_endpoint_descriptor *epd);
+extern int usb_endpoint_is_bulk_in(const struct usb_endpoint_descriptor *epd);
+extern int usb_endpoint_is_bulk_out(const struct usb_endpoint_descriptor *epd);
+extern int usb_endpoint_is_int_in(const struct usb_endpoint_descriptor *epd);
+extern int usb_endpoint_is_int_out(const struct usb_endpoint_descriptor *epd);
+extern int usb_endpoint_is_isoc_in(const struct usb_endpoint_descriptor *epd);
+extern int usb_endpoint_is_isoc_out(const struct usb_endpoint_descriptor *epd);
+
+/*-------------------------------------------------------------------------*/
+
 #define USB_DEVICE_ID_MATCH_DEVICE \
                (USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT)
 #define USB_DEVICE_ID_MATCH_DEV_RANGE \
@@ -540,7 +580,17 @@ struct usb_dynids {
 };
 
 /**
- * struct usb_driver - identifies USB driver to usbcore
+ * struct usbdrv_wrap - wrapper for driver-model structure
+ * @driver: The driver-model core driver structure.
+ * @for_devices: Non-zero for device drivers, 0 for interface drivers.
+ */
+struct usbdrv_wrap {
+       struct device_driver driver;
+       int for_devices;
+};
+
+/**
+ * struct usb_driver - identifies USB interface driver to usbcore
  * @name: The driver name should be unique among USB drivers,
  *     and should normally be the same as the module name.
  * @probe: Called to see if the driver is willing to manage a particular
@@ -567,12 +617,14 @@ struct usb_dynids {
  *     or your driver's probe function will never get called.
  * @dynids: used internally to hold the list of dynamically added device
  *     ids for this driver.
- * @driver: the driver model core driver structure.
+ * @drvwrap: Driver-model core structure wrapper.
  * @no_dynamic_id: if set to 1, the USB core will not allow dynamic ids to be
  *     added to this driver by preventing the sysfs file from being created.
+ * @supports_autosuspend: if set to 0, the USB core will not allow autosuspend
+ *     for interfaces bound to this driver.
  *
- * USB drivers must provide a name, probe() and disconnect() methods,
- * and an id_table.  Other driver fields are optional.
+ * USB interface drivers must provide a name, probe() and disconnect()
+ * methods, and an id_table.  Other driver fields are optional.
  *
  * The id_table is used in hotplugging.  It holds a set of descriptors,
  * and specialized data may be associated with each entry.  That table
@@ -606,10 +658,44 @@ struct usb_driver {
        const struct usb_device_id *id_table;
 
        struct usb_dynids dynids;
-       struct device_driver driver;
+       struct usbdrv_wrap drvwrap;
        unsigned int no_dynamic_id:1;
+       unsigned int supports_autosuspend:1;
 };
-#define        to_usb_driver(d) container_of(d, struct usb_driver, driver)
+#define        to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver)
+
+/**
+ * struct usb_device_driver - identifies USB device driver to usbcore
+ * @name: The driver name should be unique among USB drivers,
+ *     and should normally be the same as the module name.
+ * @probe: Called to see if the driver is willing to manage a particular
+ *     device.  If it is, probe returns zero and uses dev_set_drvdata()
+ *     to associate driver-specific data with the device.  If unwilling
+ *     to manage the device, return a negative errno value.
+ * @disconnect: Called when the device is no longer accessible, usually
+ *     because it has been (or is being) disconnected or the driver's
+ *     module is being unloaded.
+ * @suspend: Called when the device is going to be suspended by the system.
+ * @resume: Called when the device is being resumed by the system.
+ * @drvwrap: Driver-model core structure wrapper.
+ * @supports_autosuspend: if set to 0, the USB core will not allow autosuspend
+ *     for devices bound to this driver.
+ *
+ * USB drivers must provide all the fields listed above except drvwrap.
+ */
+struct usb_device_driver {
+       const char *name;
+
+       int (*probe) (struct usb_device *udev);
+       void (*disconnect) (struct usb_device *udev);
+
+       int (*suspend) (struct usb_device *udev, pm_message_t message);
+       int (*resume) (struct usb_device *udev);
+       struct usbdrv_wrap drvwrap;
+       unsigned int supports_autosuspend:1;
+};
+#define        to_usb_device_driver(d) container_of(d, struct usb_device_driver, \
+               drvwrap.driver)
 
 extern struct bus_type usb_bus_type;
 
@@ -633,13 +719,17 @@ struct usb_class_driver {
  * use these in module_init()/module_exit()
  * and don't forget MODULE_DEVICE_TABLE(usb, ...)
  */
-int usb_register_driver(struct usb_driver *, struct module *);
+extern int usb_register_driver(struct usb_driver *, struct module *);
 static inline int usb_register(struct usb_driver *driver)
 {
        return usb_register_driver(driver, THIS_MODULE);
 }
 extern void usb_deregister(struct usb_driver *);
 
+extern int usb_register_device_driver(struct usb_device_driver *,
+                       struct module *);
+extern void usb_deregister_device_driver(struct usb_device_driver *);
+
 extern int usb_register_dev(struct usb_interface *intf,
                            struct usb_class_driver *class_driver);
 extern void usb_deregister_dev(struct usb_interface *intf,
@@ -885,7 +975,7 @@ struct urb
  * @setup_packet: pointer to the setup_packet buffer
  * @transfer_buffer: pointer to the transfer buffer
  * @buffer_length: length of the transfer buffer
- * @complete: pointer to the usb_complete_t function
+ * @complete_fn: pointer to the usb_complete_t function
  * @context: what to set the urb context to.
  *
  * Initializes a control urb with the proper information needed to submit
@@ -897,7 +987,7 @@ static inline void usb_fill_control_urb (struct urb *urb,
                                         unsigned char *setup_packet,
                                         void *transfer_buffer,
                                         int buffer_length,
-                                        usb_complete_t complete,
+                                        usb_complete_t complete_fn,
                                         void *context)
 {
        spin_lock_init(&urb->lock);
@@ -906,7 +996,7 @@ static inline void usb_fill_control_urb (struct urb *urb,
        urb->setup_packet = setup_packet;
        urb->transfer_buffer = transfer_buffer;
        urb->transfer_buffer_length = buffer_length;
-       urb->complete = complete;
+       urb->complete = complete_fn;
        urb->context = context;
 }
 
@@ -917,7 +1007,7 @@ static inline void usb_fill_control_urb (struct urb *urb,
  * @pipe: the endpoint pipe
  * @transfer_buffer: pointer to the transfer buffer
  * @buffer_length: length of the transfer buffer
- * @complete: pointer to the usb_complete_t function
+ * @complete_fn: pointer to the usb_complete_t function
  * @context: what to set the urb context to.
  *
  * Initializes a bulk urb with the proper information needed to submit it
@@ -928,7 +1018,7 @@ static inline void usb_fill_bulk_urb (struct urb *urb,
                                      unsigned int pipe,
                                      void *transfer_buffer,
                                      int buffer_length,
-                                     usb_complete_t complete,
+                                     usb_complete_t complete_fn,
                                      void *context)
 {
        spin_lock_init(&urb->lock);
@@ -936,7 +1026,7 @@ static inline void usb_fill_bulk_urb (struct urb *urb,
        urb->pipe = pipe;
        urb->transfer_buffer = transfer_buffer;
        urb->transfer_buffer_length = buffer_length;
-       urb->complete = complete;
+       urb->complete = complete_fn;
        urb->context = context;
 }
 
@@ -947,7 +1037,7 @@ static inline void usb_fill_bulk_urb (struct urb *urb,
  * @pipe: the endpoint pipe
  * @transfer_buffer: pointer to the transfer buffer
  * @buffer_length: length of the transfer buffer
- * @complete: pointer to the usb_complete_t function
+ * @complete_fn: pointer to the usb_complete_t function
  * @context: what to set the urb context to.
  * @interval: what to set the urb interval to, encoded like
  *     the endpoint descriptor's bInterval value.
@@ -963,7 +1053,7 @@ static inline void usb_fill_int_urb (struct urb *urb,
                                     unsigned int pipe,
                                     void *transfer_buffer,
                                     int buffer_length,
-                                    usb_complete_t complete,
+                                    usb_complete_t complete_fn,
                                     void *context,
                                     int interval)
 {
@@ -972,7 +1062,7 @@ static inline void usb_fill_int_urb (struct urb *urb,
        urb->pipe = pipe;
        urb->transfer_buffer = transfer_buffer;
        urb->transfer_buffer_length = buffer_length;
-       urb->complete = complete;
+       urb->complete = complete_fn;
        urb->context = context;
        if (dev->speed == USB_SPEED_HIGH)
                urb->interval = 1 << (interval - 1);
@@ -990,7 +1080,6 @@ extern int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
 extern int usb_unlink_urb(struct urb *urb);
 extern void usb_kill_urb(struct urb *urb);
 
-#define HAVE_USB_BUFFERS
 void *usb_buffer_alloc (struct usb_device *dev, size_t size,
        gfp_t mem_flags, dma_addr_t *dma);
 void usb_buffer_free (struct usb_device *dev, size_t size,
@@ -1003,14 +1092,14 @@ void usb_buffer_unmap (struct urb *urb);
 #endif
 
 struct scatterlist;
-int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
-               struct scatterlist *sg, int nents);
+int usb_buffer_map_sg(const struct usb_device *dev, unsigned pipe,
+                     struct scatterlist *sg, int nents);
 #if 0
-void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
-               struct scatterlist *sg, int n_hw_ents);
+void usb_buffer_dmasync_sg(const struct usb_device *dev, unsigned pipe,
+                          struct scatterlist *sg, int n_hw_ents);
 #endif
-void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
-               struct scatterlist *sg, int n_hw_ents);
+void usb_buffer_unmap_sg(const struct usb_device *dev, unsigned pipe,
+                        struct scatterlist *sg, int n_hw_ents);
 
 /*-------------------------------------------------------------------*
  *                         SYNCHRONOUS CALL SUPPORT                  *
@@ -1038,6 +1127,9 @@ extern int usb_clear_halt(struct usb_device *dev, int pipe);
 extern int usb_reset_configuration(struct usb_device *dev);
 extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate);
 
+/* this request isn't really synchronous, but it belongs with the others */
+extern int usb_driver_set_configuration(struct usb_device *udev, int config);
+
 /*
  * timeouts, in milliseconds, used for sending/receiving control messages
  * they typically complete within a few frames (msec) after they're issued
diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h
new file mode 100644 (file)
index 0000000..6bd2359
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * <linux/usb/audio.h> -- USB Audio definitions.
+ *
+ * Copyright (C) 2006 Thumtronics Pty Ltd.
+ * Developed for Thumtronics by Grey Innovation
+ * Ben Williamson <ben.williamson@greyinnovation.com>
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * License ("GPL") version 2, as published by the Free Software Foundation.
+ *
+ * This file holds USB constants and structures defined
+ * by the USB Device Class Definition for Audio Devices.
+ * Comments below reference relevant sections of that document:
+ *
+ * http://www.usb.org/developers/devclass_docs/audio10.pdf
+ */
+
+#ifndef __LINUX_USB_AUDIO_H
+#define __LINUX_USB_AUDIO_H
+
+#include <linux/types.h>
+
+/* A.2 Audio Interface Subclass Codes */
+#define USB_SUBCLASS_AUDIOCONTROL      0x01
+#define USB_SUBCLASS_AUDIOSTREAMING    0x02
+#define USB_SUBCLASS_MIDISTREAMING     0x03
+
+/* 4.3.2  Class-Specific AC Interface Descriptor */
+struct usb_ac_header_descriptor {
+       __u8  bLength;                  // 8+n
+       __u8  bDescriptorType;          // USB_DT_CS_INTERFACE
+       __u8  bDescriptorSubtype;       // USB_MS_HEADER
+       __le16 bcdADC;                  // 0x0100
+       __le16 wTotalLength;            // includes Unit and Terminal desc.
+       __u8  bInCollection;            // n
+       __u8  baInterfaceNr[];          // [n]
+} __attribute__ ((packed));
+
+#define USB_DT_AC_HEADER_SIZE(n)       (8+(n))
+
+/* As above, but more useful for defining your own descriptors: */
+#define DECLARE_USB_AC_HEADER_DESCRIPTOR(n)                    \
+struct usb_ac_header_descriptor_##n {                          \
+       __u8  bLength;                                          \
+       __u8  bDescriptorType;                                  \
+       __u8  bDescriptorSubtype;                               \
+       __le16 bcdADC;                                          \
+       __le16 wTotalLength;                                    \
+       __u8  bInCollection;                                    \
+       __u8  baInterfaceNr[n];                                 \
+} __attribute__ ((packed))
+
+#endif
diff --git a/include/linux/usb/midi.h b/include/linux/usb/midi.h
new file mode 100644 (file)
index 0000000..11a97d5
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * <linux/usb/midi.h> -- USB MIDI definitions.
+ *
+ * Copyright (C) 2006 Thumtronics Pty Ltd.
+ * Developed for Thumtronics by Grey Innovation
+ * Ben Williamson <ben.williamson@greyinnovation.com>
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * License ("GPL") version 2, as published by the Free Software Foundation.
+ *
+ * This file holds USB constants and structures defined
+ * by the USB Device Class Definition for MIDI Devices.
+ * Comments below reference relevant sections of that document:
+ *
+ * http://www.usb.org/developers/devclass_docs/midi10.pdf
+ */
+
+#ifndef __LINUX_USB_MIDI_H
+#define __LINUX_USB_MIDI_H
+
+#include <linux/types.h>
+
+/* A.1  MS Class-Specific Interface Descriptor Subtypes */
+#define USB_MS_HEADER          0x01
+#define USB_MS_MIDI_IN_JACK    0x02
+#define USB_MS_MIDI_OUT_JACK   0x03
+#define USB_MS_ELEMENT         0x04
+
+/* A.2  MS Class-Specific Endpoint Descriptor Subtypes */
+#define USB_MS_GENERAL         0x01
+
+/* A.3  MS MIDI IN and OUT Jack Types */
+#define USB_MS_EMBEDDED                0x01
+#define USB_MS_EXTERNAL                0x02
+
+/* 6.1.2.1  Class-Specific MS Interface Header Descriptor */
+struct usb_ms_header_descriptor {
+       __u8  bLength;
+       __u8  bDescriptorType;
+       __u8  bDescriptorSubtype;
+       __le16 bcdMSC;
+       __le16 wTotalLength;
+} __attribute__ ((packed));
+
+#define USB_DT_MS_HEADER_SIZE  7
+
+/* 6.1.2.2  MIDI IN Jack Descriptor */
+struct usb_midi_in_jack_descriptor {
+       __u8  bLength;
+       __u8  bDescriptorType;          // USB_DT_CS_INTERFACE
+       __u8  bDescriptorSubtype;       // USB_MS_MIDI_IN_JACK
+       __u8  bJackType;                // USB_MS_EMBEDDED/EXTERNAL
+       __u8  bJackID;
+       __u8  iJack;
+} __attribute__ ((packed));
+
+#define USB_DT_MIDI_IN_SIZE    6
+
+struct usb_midi_source_pin {
+       __u8  baSourceID;
+       __u8  baSourcePin;
+} __attribute__ ((packed));
+
+/* 6.1.2.3  MIDI OUT Jack Descriptor */
+struct usb_midi_out_jack_descriptor {
+       __u8  bLength;
+       __u8  bDescriptorType;          // USB_DT_CS_INTERFACE
+       __u8  bDescriptorSubtype;       // USB_MS_MIDI_OUT_JACK
+       __u8  bJackType;                // USB_MS_EMBEDDED/EXTERNAL
+       __u8  bJackID;
+       __u8  bNrInputPins;             // p
+       struct usb_midi_source_pin pins[]; // [p]
+       /*__u8  iJack;  -- ommitted due to variable-sized pins[] */
+} __attribute__ ((packed));
+
+#define USB_DT_MIDI_OUT_SIZE(p)        (7 + 2 * (p))
+
+/* As above, but more useful for defining your own descriptors: */
+#define DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(p)                        \
+struct usb_midi_out_jack_descriptor_##p {                      \
+       __u8  bLength;                                          \
+       __u8  bDescriptorType;                                  \
+       __u8  bDescriptorSubtype;                               \
+       __u8  bJackType;                                        \
+       __u8  bJackID;                                          \
+       __u8  bNrInputPins;                                     \
+       struct usb_midi_source_pin pins[p];                     \
+       __u8  iJack;                                            \
+} __attribute__ ((packed))
+
+/* 6.2.2  Class-Specific MS Bulk Data Endpoint Descriptor */
+struct usb_ms_endpoint_descriptor {
+       __u8  bLength;                  // 4+n
+       __u8  bDescriptorType;          // USB_DT_CS_ENDPOINT
+       __u8  bDescriptorSubtype;       // USB_MS_GENERAL
+       __u8  bNumEmbMIDIJack;          // n
+       __u8  baAssocJackID[];          // [n]
+} __attribute__ ((packed));
+
+#define USB_DT_MS_ENDPOINT_SIZE(n)     (4 + (n))
+
+/* As above, but more useful for defining your own descriptors: */
+#define DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(n)                  \
+struct usb_ms_endpoint_descriptor_##n {                                \
+       __u8  bLength;                                          \
+       __u8  bDescriptorType;                                  \
+       __u8  bDescriptorSubtype;                               \
+       __u8  bNumEmbMIDIJack;                                  \
+       __u8  baAssocJackID[n];                                 \
+} __attribute__ ((packed))
+
+#endif
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
new file mode 100644 (file)
index 0000000..9897f7a
--- /dev/null
@@ -0,0 +1,131 @@
+// include/linux/usb/otg.h
+
+/*
+ * These APIs may be used between USB controllers.  USB device drivers
+ * (for either host or peripheral roles) don't use these calls; they
+ * continue to use just usb_device and usb_gadget.
+ */
+
+
+/* OTG defines lots of enumeration states before device reset */
+enum usb_otg_state {
+       OTG_STATE_UNDEFINED = 0,
+
+       /* single-role peripheral, and dual-role default-b */
+       OTG_STATE_B_IDLE,
+       OTG_STATE_B_SRP_INIT,
+       OTG_STATE_B_PERIPHERAL,
+
+       /* extra dual-role default-b states */
+       OTG_STATE_B_WAIT_ACON,
+       OTG_STATE_B_HOST,
+
+       /* dual-role default-a */
+       OTG_STATE_A_IDLE,
+       OTG_STATE_A_WAIT_VRISE,
+       OTG_STATE_A_WAIT_BCON,
+       OTG_STATE_A_HOST,
+       OTG_STATE_A_SUSPEND,
+       OTG_STATE_A_PERIPHERAL,
+       OTG_STATE_A_WAIT_VFALL,
+       OTG_STATE_A_VBUS_ERR,
+};
+
+/*
+ * the otg driver needs to interact with both device side and host side
+ * usb controllers.  it decides which controller is active at a given
+ * moment, using the transceiver, ID signal, HNP and sometimes static
+ * configuration information (including "board isn't wired for otg").
+ */
+struct otg_transceiver {
+       struct device           *dev;
+       const char              *label;
+
+       u8                      default_a;
+       enum usb_otg_state      state;
+
+       struct usb_bus          *host;
+       struct usb_gadget       *gadget;
+
+       /* to pass extra port status to the root hub */
+       u16                     port_status;
+       u16                     port_change;
+
+       /* bind/unbind the host controller */
+       int     (*set_host)(struct otg_transceiver *otg,
+                               struct usb_bus *host);
+
+       /* bind/unbind the peripheral controller */
+       int     (*set_peripheral)(struct otg_transceiver *otg,
+                               struct usb_gadget *gadget);
+
+       /* effective for B devices, ignored for A-peripheral */
+       int     (*set_power)(struct otg_transceiver *otg,
+                               unsigned mA);
+
+       /* for non-OTG B devices: set transceiver into suspend mode */
+       int     (*set_suspend)(struct otg_transceiver *otg,
+                               int suspend);
+
+       /* for B devices only:  start session with A-Host */
+       int     (*start_srp)(struct otg_transceiver *otg);
+
+       /* start or continue HNP role switch */
+       int     (*start_hnp)(struct otg_transceiver *otg);
+
+};
+
+
+/* for board-specific init logic */
+extern int otg_set_transceiver(struct otg_transceiver *);
+
+
+/* for usb host and peripheral controller drivers */
+extern struct otg_transceiver *otg_get_transceiver(void);
+
+static inline int
+otg_start_hnp(struct otg_transceiver *otg)
+{
+       return otg->start_hnp(otg);
+}
+
+
+/* for HCDs */
+static inline int
+otg_set_host(struct otg_transceiver *otg, struct usb_bus *host)
+{
+       return otg->set_host(otg, host);
+}
+
+
+/* for usb peripheral controller drivers */
+static inline int
+otg_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *periph)
+{
+       return otg->set_peripheral(otg, periph);
+}
+
+static inline int
+otg_set_power(struct otg_transceiver *otg, unsigned mA)
+{
+       return otg->set_power(otg, mA);
+}
+
+static inline int
+otg_set_suspend(struct otg_transceiver *otg, int suspend)
+{
+       if (otg->set_suspend != NULL)
+               return otg->set_suspend(otg, suspend);
+       else
+               return 0;
+}
+
+static inline int
+otg_start_srp(struct otg_transceiver *otg)
+{
+       return otg->start_srp(otg);
+}
+
+
+/* for OTG controller drivers (and maybe other stuff) */
+extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
diff --git a/include/linux/usb_otg.h b/include/linux/usb_otg.h
deleted file mode 100644 (file)
index f827f6e..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-// include/linux/usb_otg.h 
-
-/*
- * These APIs may be used between USB controllers.  USB device drivers
- * (for either host or peripheral roles) don't use these calls; they
- * continue to use just usb_device and usb_gadget.
- */
-
-
-/* OTG defines lots of enumeration states before device reset */
-enum usb_otg_state {
-       OTG_STATE_UNDEFINED = 0,
-
-       /* single-role peripheral, and dual-role default-b */
-       OTG_STATE_B_IDLE,
-       OTG_STATE_B_SRP_INIT,
-       OTG_STATE_B_PERIPHERAL,
-
-       /* extra dual-role default-b states */
-       OTG_STATE_B_WAIT_ACON,
-       OTG_STATE_B_HOST,
-
-       /* dual-role default-a */
-       OTG_STATE_A_IDLE,
-       OTG_STATE_A_WAIT_VRISE,
-       OTG_STATE_A_WAIT_BCON,
-       OTG_STATE_A_HOST,
-       OTG_STATE_A_SUSPEND,
-       OTG_STATE_A_PERIPHERAL,
-       OTG_STATE_A_WAIT_VFALL,
-       OTG_STATE_A_VBUS_ERR,
-};
-
-/*
- * the otg driver needs to interact with both device side and host side
- * usb controllers.  it decides which controller is active at a given
- * moment, using the transceiver, ID signal, HNP and sometimes static
- * configuration information (including "board isn't wired for otg").
- */
-struct otg_transceiver {
-       struct device           *dev;
-       const char              *label;
-
-       u8                      default_a;
-       enum usb_otg_state      state;
-
-       struct usb_bus          *host;
-       struct usb_gadget       *gadget;
-
-       /* to pass extra port status to the root hub */
-       u16                     port_status;
-       u16                     port_change;
-
-       /* bind/unbind the host controller */
-       int     (*set_host)(struct otg_transceiver *otg,
-                               struct usb_bus *host);
-
-       /* bind/unbind the peripheral controller */
-       int     (*set_peripheral)(struct otg_transceiver *otg,
-                               struct usb_gadget *gadget);
-
-       /* effective for B devices, ignored for A-peripheral */
-       int     (*set_power)(struct otg_transceiver *otg,
-                               unsigned mA);
-
-       /* for non-OTG B devices: set transceiver into suspend mode */
-       int     (*set_suspend)(struct otg_transceiver *otg,
-                               int suspend);
-
-       /* for B devices only:  start session with A-Host */
-       int     (*start_srp)(struct otg_transceiver *otg);
-
-       /* start or continue HNP role switch */
-       int     (*start_hnp)(struct otg_transceiver *otg);
-
-};
-
-
-/* for board-specific init logic */
-extern int otg_set_transceiver(struct otg_transceiver *);
-
-
-/* for usb host and peripheral controller drivers */
-extern struct otg_transceiver *otg_get_transceiver(void);
-
-static inline int
-otg_start_hnp(struct otg_transceiver *otg)
-{
-       return otg->start_hnp(otg);
-}
-
-
-/* for HCDs */
-static inline int
-otg_set_host(struct otg_transceiver *otg, struct usb_bus *host)
-{
-       return otg->set_host(otg, host);
-}
-
-
-/* for usb peripheral controller drivers */
-static inline int
-otg_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *periph)
-{
-       return otg->set_peripheral(otg, periph);
-}
-
-static inline int
-otg_set_power(struct otg_transceiver *otg, unsigned mA)
-{
-       return otg->set_power(otg, mA);
-}
-
-static inline int
-otg_set_suspend(struct otg_transceiver *otg, int suspend)
-{
-       if (otg->set_suspend != NULL)
-               return otg->set_suspend(otg, suspend);
-       else
-               return 0;
-}
-
-static inline int
-otg_start_srp(struct otg_transceiver *otg)
-{
-       return otg->start_srp(otg);
-}
-
-
-/* for OTG controller drivers (and maybe other stuff) */
-extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
index e7fc5fe..2ae76fe 100644 (file)
@@ -108,6 +108,9 @@ enum { US_DO_ALL_FLAGS };
 #ifdef CONFIG_USB_STORAGE_ALAUDA
 #define US_PR_ALAUDA    0xf4           /* Alauda chipsets */
 #endif
+#ifdef CONFIG_USB_STORAGE_KARMA
+#define US_PR_KARMA     0xf5           /* Rio Karma */
+#endif
 
 #define US_PR_DEVICE   0xff            /* Use device's value */
 
index 5105b6b..abe29da 100644 (file)
@@ -181,9 +181,9 @@ static int snd_usbmidi_urb_error(int status)
        case -ENODEV:
                return -ENODEV;
        /* errors that might occur during unplugging */
-       case -EPROTO:    /* EHCI */
-       case -ETIMEDOUT: /* OHCI */
-       case -EILSEQ:    /* UHCI */
+       case -EPROTO:
+       case -ETIME:
+       case -EILSEQ:
                return -EIO;
        default:
                snd_printk(KERN_ERR "urb status %d\n", status);