Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6
authorLinus Torvalds <torvalds@woody.linux-foundation.org>
Mon, 26 Feb 2007 19:41:08 +0000 (11:41 -0800)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Mon, 26 Feb 2007 19:41:08 +0000 (11:41 -0800)
* master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6:
  USB: export autosuspend delay in sysfs
  sysfs: allow attributes to be added to groups
  USB: make autosuspend delay a module parameter
  USB: minor cleanups for sysfs.c
  USB: add a blacklist for devices that can't handle some things we throw at them.
  USB: refactor usb device matching and create usb_device_match
  USB: Wacom driver updates
  gadgetfs: Fixed bug in ep_aio_read_retry.
  USB: Use USB defines in usbmouse.c and usbkbd.c
  USB: add rationale on why usb descriptor structures have to be packed
  USB: ftdi_sio: Adding VID and PID for Tellstick
  UHCI: Eliminate asynchronous skeleton Queue Headers
  UHCI: Add macros for computing DMA values
  USB: Davicom DM9601 usbnet driver
  USB: asix.c - Add JVC-PRX1 ids
  usbmon: Remove erroneous __exit
  USB: add driver for iowarrior devices.
  USB: option: add a bunch of new device ids
  USB: option: remove duplicate device id table

38 files changed:
Documentation/kernel-parameters.txt
MAINTAINERS
drivers/usb/core/Makefile
drivers/usb/core/driver.c
drivers/usb/core/hub.c
drivers/usb/core/message.c
drivers/usb/core/quirks.c [new file with mode: 0644]
drivers/usb/core/sysfs.c
drivers/usb/core/usb.c
drivers/usb/core/usb.h
drivers/usb/gadget/inode.c
drivers/usb/host/uhci-debug.c
drivers/usb/host/uhci-hcd.c
drivers/usb/host/uhci-hcd.h
drivers/usb/host/uhci-q.c
drivers/usb/input/usbkbd.c
drivers/usb/input/usbmouse.c
drivers/usb/input/wacom_wac.c
drivers/usb/input/wacom_wac.h
drivers/usb/misc/Kconfig
drivers/usb/misc/Makefile
drivers/usb/misc/iowarrior.c [new file with mode: 0644]
drivers/usb/mon/mon_bin.c
drivers/usb/mon/mon_text.c
drivers/usb/mon/usb_mon.h
drivers/usb/net/Kconfig
drivers/usb/net/Makefile
drivers/usb/net/asix.c
drivers/usb/net/dm9601.c [new file with mode: 0644]
drivers/usb/serial/ftdi_sio.c
drivers/usb/serial/ftdi_sio.h
drivers/usb/serial/option.c
fs/sysfs/file.c
include/linux/sysfs.h
include/linux/usb.h
include/linux/usb/ch9.h
include/linux/usb/iowarrior.h [new file with mode: 0644]
include/linux/usb/quirks.h [new file with mode: 0644]

index c479d30eeaa384c019550194d12c94c10bf1e948..03eb5ed503f7745e9e30f177fd35baba8ff96779 100644 (file)
@@ -1758,6 +1758,13 @@ and is between 256 and 4096 characters. It is defined in the file
                        Note that genuine overcurrent events won't be
                        reported either.
 
+       usbcore.autosuspend=
+                       [USB] The autosuspend time delay (in seconds) used
+                       for newly-detected USB devices (default 2).  This
+                       is the time required before an idle device will be
+                       autosuspended.  Devices for which the delay is set
+                       to 0 won't be autosuspended at all.
+
        usbhid.mousepoll=
                        [USBHID] The interval which mice are to be polled at.
 
index c268b51e4518f85ac2aaa5ca7585f988585260fa..a9c13196f6bdd054099f5259e0d82b6c7bf05e1e 100644 (file)
@@ -3392,6 +3392,13 @@ L:       linux-usb-devel@lists.sourceforge.net
 S:     Maintained
 W:     http://www.kroah.com/linux-usb/
 
+USB DAVICOM DM9601 DRIVER
+P:     Peter Korsgaard
+M:     jacmet@sunsite.dk
+L:     linux-usb-devel@lists.sourceforge.net
+W:     http://www.linux-usb.org/usbnet
+S:     Maintained
+
 USB EHCI DRIVER
 P:     David Brownell
 M:     dbrownell@users.sourceforge.net
index 34e9bac319b4e5af731727e5da58907f28612a61..b6078706fb939d7f1d3aeb9466f6498f8857f366 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 generic.o
+                       devio.o notify.o generic.o quirks.o
 
 ifeq ($(CONFIG_PCI),y)
        usbcore-objs    += hcd-pci.o
index 2aded261f42c3acb5ffdd8a605a6c36b70049a6e..9e3e943f313c55dc3479be6b96f4ffbc58c988d2 100644 (file)
@@ -366,19 +366,8 @@ void usb_driver_release_interface(struct usb_driver *driver,
 EXPORT_SYMBOL(usb_driver_release_interface);
 
 /* returns 0 if no match, 1 if match */
-int usb_match_one_id(struct usb_interface *interface,
-                    const struct usb_device_id *id)
+int usb_match_device(struct usb_device *dev, const struct usb_device_id *id)
 {
-       struct usb_host_interface *intf;
-       struct usb_device *dev;
-
-       /* proc_connectinfo in devio.c may call us with id == NULL. */
-       if (id == NULL)
-               return 0;
-
-       intf = interface->cur_altsetting;
-       dev = interface_to_usbdev(interface);
-
        if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
            id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
                return 0;
@@ -409,6 +398,26 @@ int usb_match_one_id(struct usb_interface *interface,
            (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
                return 0;
 
+       return 1;
+}
+
+/* returns 0 if no match, 1 if match */
+int usb_match_one_id(struct usb_interface *interface,
+                    const struct usb_device_id *id)
+{
+       struct usb_host_interface *intf;
+       struct usb_device *dev;
+
+       /* proc_connectinfo in devio.c may call us with id == NULL. */
+       if (id == NULL)
+               return 0;
+
+       intf = interface->cur_altsetting;
+       dev = interface_to_usbdev(interface);
+
+       if (!usb_match_device(dev, id))
+               return 0;
+
        /* The interface class, subclass, and protocol should never be
         * checked for a match if the device class is Vendor Specific,
         * unless the match record specifies the Vendor ID. */
@@ -954,12 +963,16 @@ static int autosuspend_check(struct usb_device *udev)
        int                     i;
        struct usb_interface    *intf;
 
-       /* For autosuspend, fail fast if anything is in use.
-        * Also fail if any interfaces require remote wakeup but it
-        * isn't available. */
+       /* For autosuspend, fail fast if anything is in use or autosuspend
+        * is disabled.  Also fail if any interfaces require remote wakeup
+        * but it isn't available.
+        */
        udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
        if (udev->pm_usage_cnt > 0)
                return -EBUSY;
+       if (!udev->autosuspend_delay)
+               return -EPERM;
+
        if (udev->actconfig) {
                for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
                        intf = udev->actconfig->interface[i];
@@ -982,7 +995,7 @@ static int autosuspend_check(struct usb_device *udev)
 
 #define autosuspend_check(udev)                0
 
-#endif
+#endif /* CONFIG_USB_SUSPEND */
 
 /**
  * usb_suspend_both - suspend a USB device and its interfaces
@@ -1177,7 +1190,7 @@ static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt)
                        udev->pm_usage_cnt -= inc_usage_cnt;
        } else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0)
                queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
-                               USB_AUTOSUSPEND_DELAY);
+                               udev->autosuspend_delay);
        usb_pm_unlock(udev);
        return status;
 }
@@ -1211,6 +1224,26 @@ void usb_autosuspend_device(struct usb_device *udev)
        //              __FUNCTION__, udev->pm_usage_cnt);
 }
 
+/**
+ * usb_try_autosuspend_device - attempt an autosuspend of a USB device and its interfaces
+ * @udev: the usb_device to autosuspend
+ *
+ * This routine should be called when a core subsystem thinks @udev may
+ * be ready to autosuspend.
+ *
+ * @udev's usage counter left unchanged.  If it or any of the usage counters
+ * for an active interface is greater than 0, or autosuspend is not allowed
+ * for any other reason, no autosuspend request will be queued.
+ *
+ * This routine can run only in process context.
+ */
+void usb_try_autosuspend_device(struct usb_device *udev)
+{
+       usb_autopm_do_device(udev, 0);
+       // 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
@@ -1261,7 +1294,7 @@ static int usb_autopm_do_interface(struct usb_interface *intf,
                                intf->pm_usage_cnt -= inc_usage_cnt;
                } else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0)
                        queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
-                                       USB_AUTOSUSPEND_DELAY);
+                                       udev->autosuspend_delay);
        }
        usb_pm_unlock(udev);
        return status;
index 50c0db15304aef513c30262b585b41db670c567e..41400743ce2cc85b5a173d7360b6c0066cc54b8a 100644 (file)
@@ -1287,6 +1287,9 @@ int usb_new_device(struct usb_device *udev)
        if (!try_module_get(THIS_MODULE))
                return -EINVAL;
 
+       /* Determine quirks */
+       usb_detect_quirks(udev);
+
        err = usb_get_configuration(udev);
        if (err < 0) {
                dev_err(&udev->dev, "can't read configurations, error %d\n",
index 74edaea5665d58367f6f62e48cec2d4a2b63313e..2f17468b5c1efe19fdfa8c7a52e3f9fc3a670865 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/timer.h>
 #include <linux/ctype.h>
 #include <linux/device.h>
+#include <linux/usb/quirks.h>
 #include <asm/byteorder.h>
 #include <asm/scatterlist.h>
 
@@ -685,7 +686,10 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid,
 
        /* Try to read the string descriptor by asking for the maximum
         * possible number of bytes */
-       rc = usb_get_string(dev, langid, index, buf, 255);
+       if (dev->quirks & USB_QUIRK_STRING_FETCH_255)
+               rc = -EIO;
+       else
+               rc = usb_get_string(dev, langid, index, buf, 255);
 
        /* If that failed try to read the descriptor length, then
         * ask for just that many bytes */
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
new file mode 100644 (file)
index 0000000..0e5c646
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * USB device quirk handling logic and table
+ *
+ * Copyright (c) 2007 Oliver Neukum
+ * Copyright (c) 2007 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 as published by the Free
+ * Software Foundation, version 2.
+ *
+ *
+ */
+
+#include <linux/usb.h>
+#include <linux/usb/quirks.h>
+#include "usb.h"
+
+/* List of quirky USB devices.  Please keep this list ordered by:
+ *     1) Vendor ID
+ *     2) Product ID
+ *     3) Class ID
+ *
+ * as we want specific devices to be overridden first, and only after that, any
+ * class specific quirks.
+ *
+ * Right now the logic aborts if it finds a valid device in the table, we might
+ * want to change that in the future if it turns out that a whole class of
+ * devices is broken...
+ */
+static const struct usb_device_id usb_quirk_list[] = {
+       /* HP 5300/5370C scanner */
+       { USB_DEVICE(0x03f0, 0x0701), .driver_info = USB_QUIRK_STRING_FETCH_255 },
+
+       /* Elsa MicroLink 56k (V.250) */
+       { USB_DEVICE(0x05cc, 0x2267), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
+
+       { }  /* terminating entry must be last */
+};
+
+static void usb_autosuspend_quirk(struct usb_device *udev)
+{
+#ifdef CONFIG_USB_SUSPEND
+       /* disable autosuspend, but allow the user to re-enable it via sysfs */
+       udev->autosuspend_delay = 0;
+#endif
+}
+
+static const struct usb_device_id *find_id(struct usb_device *udev)
+{
+       const struct usb_device_id *id = usb_quirk_list;
+
+       for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass ||
+                       id->driver_info; id++) {
+               if (usb_match_device(udev, id))
+                       return id;
+       }
+       return NULL;
+}
+
+/*
+ * Detect any quirks the device has, and do any housekeeping for it if needed.
+ */
+void usb_detect_quirks(struct usb_device *udev)
+{
+       const struct usb_device_id *id = usb_quirk_list;
+
+       id = find_id(udev);
+       if (id)
+               udev->quirks = (u32)(id->driver_info);
+       if (udev->quirks)
+               dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
+                               udev->quirks);
+
+       /* do any special quirk handling here if needed */
+       if (udev->quirks & USB_QUIRK_NO_AUTOSUSPEND)
+               usb_autosuspend_quirk(udev);
+}
index 0edfbafd702cced2fcda7638f2002be4c055fb52..311d5df80386c91174e6614e79ea5e337c09d9a6 100644 (file)
@@ -148,6 +148,75 @@ show_maxchild(struct device *dev, struct device_attribute *attr, char *buf)
 }
 static DEVICE_ATTR(maxchild, S_IRUGO, show_maxchild, NULL);
 
+static ssize_t
+show_quirks(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct usb_device *udev;
+
+       udev = to_usb_device(dev);
+       return sprintf(buf, "0x%x\n", udev->quirks);
+}
+static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL);
+
+#ifdef CONFIG_USB_SUSPEND
+
+static ssize_t
+show_autosuspend(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct usb_device *udev = to_usb_device(dev);
+
+       return sprintf(buf, "%u\n", udev->autosuspend_delay / HZ);
+}
+
+static ssize_t
+set_autosuspend(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct usb_device *udev = to_usb_device(dev);
+       unsigned value, old;
+
+       if (sscanf(buf, "%u", &value) != 1 || value >= INT_MAX/HZ)
+               return -EINVAL;
+       value *= HZ;
+
+       old = udev->autosuspend_delay;
+       udev->autosuspend_delay = value;
+       if (value > 0 && old == 0)
+               usb_try_autosuspend_device(udev);
+
+       return count;
+}
+
+static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR,
+               show_autosuspend, set_autosuspend);
+
+static char power_group[] = "power";
+
+static int add_power_attributes(struct device *dev)
+{
+       int rc = 0;
+
+       if (is_usb_device(dev))
+               rc = sysfs_add_file_to_group(&dev->kobj,
+                               &dev_attr_autosuspend.attr,
+                               power_group);
+       return rc;
+}
+
+static void remove_power_attributes(struct device *dev)
+{
+       sysfs_remove_file_from_group(&dev->kobj,
+                       &dev_attr_autosuspend.attr,
+                       power_group);
+}
+
+#else
+
+#define add_power_attributes(dev)      0
+#define remove_power_attributes(dev)   do {} while (0)
+
+#endif /* CONFIG_USB_SUSPEND */
+
 /* Descriptor fields */
 #define usb_descriptor_attr_le16(field, format_string)                 \
 static ssize_t                                                         \
@@ -204,6 +273,7 @@ static struct attribute *dev_attrs[] = {
        &dev_attr_devnum.attr,
        &dev_attr_version.attr,
        &dev_attr_maxchild.attr,
+       &dev_attr_quirks.attr,
        NULL,
 };
 static struct attribute_group dev_attr_grp = {
@@ -219,6 +289,10 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
        if (retval)
                return retval;
 
+       retval = add_power_attributes(dev);
+       if (retval)
+               goto error;
+
        if (udev->manufacturer) {
                retval = device_create_file(dev, &dev_attr_manufacturer);
                if (retval)
@@ -239,10 +313,7 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
                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);
+       usb_remove_sysfs_dev_files(udev);
        return retval;
 }
 
@@ -251,14 +322,11 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev)
        struct device *dev = &udev->dev;
 
        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);
+       remove_power_attributes(dev);
        sysfs_remove_group(&dev->kobj, &dev_attr_grp);
-
-       if (udev->manufacturer)
-               device_remove_file(dev, &dev_attr_manufacturer);
-       if (udev->product)
-               device_remove_file(dev, &dev_attr_product);
-       if (udev->serial)
-               device_remove_file(dev, &dev_attr_serial);
 }
 
 /* Interface fields */
@@ -362,33 +430,28 @@ static inline void usb_remove_intf_ep_files(struct usb_interface *intf)
 
 int usb_create_sysfs_intf_files(struct usb_interface *intf)
 {
+       struct device *dev = &intf->dev;
        struct usb_device *udev = interface_to_usbdev(intf);
        struct usb_host_interface *alt = intf->cur_altsetting;
        int retval;
 
-       retval = sysfs_create_group(&intf->dev.kobj, &intf_attr_grp);
+       retval = sysfs_create_group(&dev->kobj, &intf_attr_grp);
        if (retval)
-               goto error;
+               return retval;
 
        if (alt->string == NULL)
                alt->string = usb_cache_string(udev, alt->desc.iInterface);
        if (alt->string)
-               retval = device_create_file(&intf->dev, &dev_attr_interface);
+               retval = device_create_file(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)
 {
-       usb_remove_intf_ep_files(intf);
-       sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp);
+       struct device *dev = &intf->dev;
 
-       if (intf->cur_altsetting->string)
-               device_remove_file(&intf->dev, &dev_attr_interface);
+       usb_remove_intf_ep_files(intf);
+       device_remove_file(dev, &dev_attr_interface);
+       sysfs_remove_group(&dev->kobj, &intf_attr_grp);
 }
index 3db721cd557a61eed125e2eba770389b28bc6d8a..54b42ce311c1887acf51ce225d91f9adfada78b8 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/string.h>
 #include <linux/bitops.h>
 #include <linux/slab.h>
@@ -50,6 +51,16 @@ static int nousb;    /* Disable USB when built into kernel image */
 
 struct workqueue_struct *ksuspend_usb_wq;      /* For autosuspend */
 
+#ifdef CONFIG_USB_SUSPEND
+static int usb_autosuspend_delay = 2;          /* Default delay value,
+                                                * in seconds */
+module_param_named(autosuspend, usb_autosuspend_delay, uint, 0644);
+MODULE_PARM_DESC(autosuspend, "default autosuspend delay");
+
+#else
+#define usb_autosuspend_delay          0
+#endif
+
 
 /**
  * usb_ifnum_to_if - get the interface object with a given interface number
@@ -306,6 +317,7 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
 #ifdef CONFIG_PM
        mutex_init(&dev->pm_mutex);
        INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work);
+       dev->autosuspend_delay = usb_autosuspend_delay * HZ;
 #endif
        return dev;
 }
index 17830a81be141743e06f5ac837ff0747ab314652..08b5a04e3755883652036d5f4f12f56240de9b62 100644 (file)
@@ -13,6 +13,7 @@ extern void usb_disable_interface (struct usb_device *dev,
                struct usb_interface *intf);
 extern void usb_release_interface_cache(struct kref *ref);
 extern void usb_disable_device (struct usb_device *dev, int skip_ep0);
+extern void usb_detect_quirks(struct usb_device *udev);
 
 extern int usb_get_device_descriptor(struct usb_device *dev,
                unsigned int size);
@@ -21,6 +22,8 @@ extern int usb_set_configuration(struct usb_device *dev, int configuration);
 
 extern void usb_kick_khubd(struct usb_device *dev);
 extern void usb_resume_root_hub(struct usb_device *dev);
+extern int usb_match_device(struct usb_device *dev,
+                           const struct usb_device_id *id);
 
 extern int  usb_hub_init(void);
 extern void usb_hub_cleanup(void);
@@ -62,14 +65,14 @@ static inline void usb_pm_unlock(struct usb_device *udev) {}
 
 #ifdef CONFIG_USB_SUSPEND
 
-#define USB_AUTOSUSPEND_DELAY  (HZ*2)
-
 extern void usb_autosuspend_device(struct usb_device *udev);
+extern void usb_try_autosuspend_device(struct usb_device *udev);
 extern int usb_autoresume_device(struct usb_device *udev);
 
 #else
 
-#define usb_autosuspend_device(udev)   do {} while (0)
+#define usb_autosuspend_device(udev)           do {} while (0)
+#define usb_try_autosuspend_device(udev)       do {} while (0)
 static inline int usb_autoresume_device(struct usb_device *udev)
 {
        return 0;
index 34296e79edcf0dee4a9d97733570a98f10394e44..188c74a95216860cb1740243211b94ef013cbddd 100644 (file)
@@ -553,6 +553,7 @@ static ssize_t ep_aio_read_retry(struct kiocb *iocb)
 {
        struct kiocb_priv       *priv = iocb->private;
        ssize_t                 len, total;
+       void                    *to_copy;
        int                     i;
 
        /* we "retry" to get the right mm context for this: */
@@ -560,10 +561,11 @@ static ssize_t ep_aio_read_retry(struct kiocb *iocb)
        /* copy stuff into user buffers */
        total = priv->actual;
        len = 0;
+       to_copy = priv->buf;
        for (i=0; i < priv->nr_segs; i++) {
                ssize_t this = min((ssize_t)(priv->iv[i].iov_len), total);
 
-               if (copy_to_user(priv->iv[i].iov_base, priv->buf, this)) {
+               if (copy_to_user(priv->iv[i].iov_base, to_copy, this)) {
                        if (len == 0)
                                len = -EFAULT;
                        break;
@@ -571,6 +573,7 @@ static ssize_t ep_aio_read_retry(struct kiocb *iocb)
 
                total -= this;
                len += this;
+               to_copy += this;
                if (total == 0)
                        break;
        }
index 5d6c06bc4524f10c1356583aedfad58396ecb66c..8d24d3dc0a6186173980988e010184b763e6c646 100644 (file)
@@ -196,7 +196,7 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
                struct uhci_td *td = list_entry(urbp->td_list.next,
                                struct uhci_td, list);
 
-               if (cpu_to_le32(td->dma_handle) != (element & ~UHCI_PTR_BITS))
+               if (element != LINK_TO_TD(td))
                        out += sprintf(out, "%*s Element != First TD\n",
                                        space, "");
                i = nurbs = 0;
@@ -220,16 +220,6 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
        return out - buf;
 }
 
-static const char * const qh_names[] = {
-  "skel_unlink_qh", "skel_iso_qh",
-  "skel_int128_qh", "skel_int64_qh",
-  "skel_int32_qh", "skel_int16_qh",
-  "skel_int8_qh", "skel_int4_qh",
-  "skel_int2_qh", "skel_int1_qh",
-  "skel_ls_control_qh", "skel_fs_control_qh",
-  "skel_bulk_qh", "skel_term_qh"
-};
-
 static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
 {
        char *out = buf;
@@ -352,6 +342,12 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
        struct uhci_td *td;
        struct list_head *tmp, *head;
        int nframes, nerrs;
+       __le32 link;
+
+       static const char * const qh_names[] = {
+               "unlink", "iso", "int128", "int64", "int32", "int16",
+               "int8", "int4", "int2", "async", "term"
+       };
 
        out += uhci_show_root_hub_state(uhci, out, len - (out - buf));
        out += sprintf(out, "HC status\n");
@@ -374,7 +370,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
        nframes = 10;
        nerrs = 0;
        for (i = 0; i < UHCI_NUMFRAMES; ++i) {
-               __le32 link, qh_dma;
+               __le32 qh_dma;
 
                j = 0;
                td = uhci->frame_cpu[i];
@@ -393,7 +389,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
                do {
                        td = list_entry(tmp, struct uhci_td, fl_list);
                        tmp = tmp->next;
-                       if (cpu_to_le32(td->dma_handle) != link) {
+                       if (link != LINK_TO_TD(td)) {
                                if (nframes > 0)
                                        out += sprintf(out, "    link does "
                                                "not match list entry!\n");
@@ -430,23 +426,21 @@ check_link:
 
        for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
                int cnt = 0;
+               __le32 fsbr_link = 0;
 
                qh = uhci->skelqh[i];
-               out += sprintf(out, "- %s\n", qh_names[i]); \
+               out += sprintf(out, "- skel_%s_qh\n", qh_names[i]); \
                out += uhci_show_qh(qh, out, len - (out - buf), 4);
 
                /* Last QH is the Terminating QH, it's different */
-               if (i == UHCI_NUM_SKELQH - 1) {
-                       if (qh->link != UHCI_PTR_TERM)
-                               out += sprintf(out, "    bandwidth reclamation on!\n");
-
-                       if (qh_element(qh) != cpu_to_le32(uhci->term_td->dma_handle))
+               if (i == SKEL_TERM) {
+                       if (qh_element(qh) != LINK_TO_TD(uhci->term_td))
                                out += sprintf(out, "    skel_term_qh element is not set to term_td!\n");
-
+                       if (link == LINK_TO_QH(uhci->skel_term_qh))
+                               goto check_qh_link;
                        continue;
                }
 
-               j = (i < 9) ? 9 : i+1;          /* Next skeleton */
                head = &qh->node;
                tmp = head->next;
 
@@ -456,15 +450,26 @@ check_link:
                        if (++cnt <= 10)
                                out += uhci_show_qh(qh, out,
                                                len - (out - buf), 4);
+                       if (!fsbr_link && qh->skel >= SKEL_FSBR)
+                               fsbr_link = LINK_TO_QH(qh);
                }
                if ((cnt -= 10) > 0)
                        out += sprintf(out, "    Skipped %d QHs\n", cnt);
 
-               if (i > 1 && i < UHCI_NUM_SKELQH - 1) {
-                       if (qh->link !=
-                           (cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH))
-                               out += sprintf(out, "    last QH not linked to next skeleton!\n");
-               }
+               link = UHCI_PTR_TERM;
+               if (i <= SKEL_ISO)
+                       ;
+               else if (i < SKEL_ASYNC)
+                       link = LINK_TO_QH(uhci->skel_async_qh);
+               else if (!uhci->fsbr_is_on)
+                       ;
+               else if (fsbr_link)
+                       link = fsbr_link;
+               else
+                       link = LINK_TO_QH(uhci->skel_term_qh);
+check_qh_link:
+               if (qh->link != link)
+                       out += sprintf(out, "    last QH not linked to next skeleton!\n");
        }
 
        return out - buf;
index ded4df30a63131f85e36096bfffca16237c36215..44da4334f1d63aa066b848dadaf0a9929f0bc98d 100644 (file)
@@ -13,7 +13,7 @@
  * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
  *               support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
  * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
- * (C) Copyright 2004-2006 Alan Stern, stern@rowland.harvard.edu
+ * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu
  *
  * Intel documents this fairly well, and as far as I know there
  * are no royalties or anything like that, but even so there are
@@ -107,16 +107,16 @@ static __le32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame)
         * interrupt QHs, which will help spread out bandwidth utilization.
         *
         * ffs (Find First bit Set) does exactly what we need:
-        * 1,3,5,...  => ffs = 0 => use skel_int2_qh = skelqh[8],
-        * 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[7], etc.
+        * 1,3,5,...  => ffs = 0 => use period-2 QH = skelqh[8],
+        * 2,6,10,... => ffs = 1 => use period-4 QH = skelqh[7], etc.
         * ffs >= 7 => not on any high-period queue, so use
-        *      skel_int1_qh = skelqh[9].
+        *      period-1 QH = skelqh[9].
         * Add in UHCI_NUMFRAMES to insure at least one bit is set.
         */
        skelnum = 8 - (int) __ffs(frame | UHCI_NUMFRAMES);
        if (skelnum <= 1)
                skelnum = 9;
-       return UHCI_PTR_QH | cpu_to_le32(uhci->skelqh[skelnum]->dma_handle);
+       return LINK_TO_QH(uhci->skelqh[skelnum]);
 }
 
 #include "uhci-debug.c"
@@ -540,16 +540,18 @@ static void uhci_shutdown(struct pci_dev *pdev)
  *
  * The hardware doesn't really know any difference
  * in the queues, but the order does matter for the
- * protocols higher up. The order is:
+ * protocols higher up.  The order in which the queues
+ * are encountered by the hardware is:
  *
- *  - any isochronous events handled before any
+ *  - All isochronous events are handled before any
  *    of the queues. We don't do that here, because
  *    we'll create the actual TD entries on demand.
- *  - The first queue is the interrupt queue.
- *  - The second queue is the control queue, split into low- and full-speed
- *  - The third queue is bulk queue.
- *  - The fourth queue is the bandwidth reclamation queue, which loops back
- *    to the full-speed control queue.
+ *  - The first queue is the high-period interrupt queue.
+ *  - The second queue is the period-1 interrupt and async
+ *    (low-speed control, full-speed control, then bulk) queue.
+ *  - The third queue is the terminating bandwidth reclamation queue,
+ *    which contains no members, loops back to itself, and is present
+ *    only when FSBR is on and there are no full-speed control or bulk QHs.
  */
 static int uhci_start(struct usb_hcd *hcd)
 {
@@ -626,34 +628,18 @@ static int uhci_start(struct usb_hcd *hcd)
        }
 
        /*
-        * 8 Interrupt queues; link all higher int queues to int1,
-        * then link int1 to control and control to bulk
+        * 8 Interrupt queues; link all higher int queues to int1 = async
         */
-       uhci->skel_int128_qh->link =
-                       uhci->skel_int64_qh->link =
-                       uhci->skel_int32_qh->link =
-                       uhci->skel_int16_qh->link =
-                       uhci->skel_int8_qh->link =
-                       uhci->skel_int4_qh->link =
-                       uhci->skel_int2_qh->link = UHCI_PTR_QH |
-                       cpu_to_le32(uhci->skel_int1_qh->dma_handle);
-
-       uhci->skel_int1_qh->link = UHCI_PTR_QH |
-                       cpu_to_le32(uhci->skel_ls_control_qh->dma_handle);
-       uhci->skel_ls_control_qh->link = UHCI_PTR_QH |
-                       cpu_to_le32(uhci->skel_fs_control_qh->dma_handle);
-       uhci->skel_fs_control_qh->link = UHCI_PTR_QH |
-                       cpu_to_le32(uhci->skel_bulk_qh->dma_handle);
-       uhci->skel_bulk_qh->link = UHCI_PTR_QH |
-                       cpu_to_le32(uhci->skel_term_qh->dma_handle);
+       for (i = SKEL_ISO + 1; i < SKEL_ASYNC; ++i)
+               uhci->skelqh[i]->link = LINK_TO_QH(uhci->skel_async_qh);
+       uhci->skel_async_qh->link = uhci->skel_term_qh->link = UHCI_PTR_TERM;
 
        /* This dummy TD is to work around a bug in Intel PIIX controllers */
        uhci_fill_td(uhci->term_td, 0, uhci_explen(0) |
-               (0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
-       uhci->term_td->link = cpu_to_le32(uhci->term_td->dma_handle);
-
-       uhci->skel_term_qh->link = UHCI_PTR_TERM;
-       uhci->skel_term_qh->element = cpu_to_le32(uhci->term_td->dma_handle);
+                       (0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
+       uhci->term_td->link = UHCI_PTR_TERM;
+       uhci->skel_async_qh->element = uhci->skel_term_qh->element =
+                       LINK_TO_TD(uhci->term_td);
 
        /*
         * Fill the frame list: make all entries point to the proper
index 74469b5bcb61a3e8af29b14ffcaf7b1ce6cd47f1..1b3d23406ac414005636d93212d2783968f8d17a 100644 (file)
@@ -129,11 +129,12 @@ struct uhci_qh {
        __le32 element;                 /* Queue element (TD) pointer */
 
        /* Software fields */
+       dma_addr_t dma_handle;
+
        struct list_head node;          /* Node in the list of QHs */
        struct usb_host_endpoint *hep;  /* Endpoint information */
        struct usb_device *udev;
        struct list_head queue;         /* Queue of urbps for this QH */
-       struct uhci_qh *skel;           /* Skeleton for this QH */
        struct uhci_td *dummy_td;       /* Dummy TD to end the queue */
        struct uhci_td *post_td;        /* Last TD completed */
 
@@ -149,8 +150,7 @@ struct uhci_qh {
 
        int state;                      /* QH_STATE_xxx; see above */
        int type;                       /* Queue type (control, bulk, etc) */
-
-       dma_addr_t dma_handle;
+       int skel;                       /* Skeleton queue number */
 
        unsigned int initial_toggle:1;  /* Endpoint's current toggle value */
        unsigned int needs_fixup:1;     /* Must fix the TD toggle values */
@@ -171,6 +171,8 @@ static inline __le32 qh_element(struct uhci_qh *qh) {
        return element;
 }
 
+#define LINK_TO_QH(qh)         (UHCI_PTR_QH | cpu_to_le32((qh)->dma_handle))
+
 
 /*
  *     Transfer Descriptors
@@ -264,6 +266,8 @@ static inline u32 td_status(struct uhci_td *td) {
        return le32_to_cpu(status);
 }
 
+#define LINK_TO_TD(td)         (cpu_to_le32((td)->dma_handle))
+
 
 /*
  *     Skeleton Queue Headers
@@ -272,12 +276,13 @@ static inline u32 td_status(struct uhci_td *td) {
 /*
  * The UHCI driver uses QHs with Interrupt, Control and Bulk URBs for
  * automatic queuing. To make it easy to insert entries into the schedule,
- * we have a skeleton of QHs for each predefined Interrupt latency,
- * low-speed control, full-speed control, bulk, and terminating QH
- * (see explanation for the terminating QH below).
+ * we have a skeleton of QHs for each predefined Interrupt latency.
+ * Asynchronous QHs (low-speed control, full-speed control, and bulk)
+ * go onto the period-1 interrupt list, since they all get accessed on
+ * every frame.
  *
- * When we want to add a new QH, we add it to the end of the list for the
- * skeleton QH.  For instance, the schedule list can look like this:
+ * When we want to add a new QH, we add it to the list starting from the
+ * appropriate skeleton QH.  For instance, the schedule can look like this:
  *
  * skel int128 QH
  * dev 1 interrupt QH
@@ -285,50 +290,47 @@ static inline u32 td_status(struct uhci_td *td) {
  * skel int64 QH
  * skel int32 QH
  * ...
- * skel int1 QH
- * skel low-speed control QH
- * dev 5 control QH
- * skel full-speed control QH
- * skel bulk QH
+ * skel int1 + async QH
+ * dev 5 low-speed control QH
  * dev 1 bulk QH
  * dev 2 bulk QH
- * skel terminating QH
  *
- * The terminating QH is used for 2 reasons:
- * - To place a terminating TD which is used to workaround a PIIX bug
- *   (see Intel errata for explanation), and
- * - To loop back to the full-speed control queue for full-speed bandwidth
- *   reclamation.
+ * There is a special terminating QH used to keep full-speed bandwidth
+ * reclamation active when no full-speed control or bulk QHs are linked
+ * into the schedule.  It has an inactive TD (to work around a PIIX bug,
+ * see the Intel errata) and it points back to itself.
  *
- * There's a special skeleton QH for Isochronous QHs.  It never appears
- * on the schedule, and Isochronous TDs go on the schedule before the
+ * There's a special skeleton QH for Isochronous QHs which never appears
+ * on the schedule Isochronous TDs go on the schedule before the
  * the skeleton QHs.  The hardware accesses them directly rather than
  * through their QH, which is used only for bookkeeping purposes.
  * While the UHCI spec doesn't forbid the use of QHs for Isochronous,
  * it doesn't use them either.  And the spec says that queues never
  * advance on an error completion status, which makes them totally
  * unsuitable for Isochronous transfers.
+ *
+ * There's also a special skeleton QH used for QHs which are in the process
+ * of unlinking and so may still be in use by the hardware.  It too never
+ * appears on the schedule.
  */
 
-#define UHCI_NUM_SKELQH                14
-#define skel_unlink_qh         skelqh[0]
-#define skel_iso_qh            skelqh[1]
-#define skel_int128_qh         skelqh[2]
-#define skel_int64_qh          skelqh[3]
-#define skel_int32_qh          skelqh[4]
-#define skel_int16_qh          skelqh[5]
-#define skel_int8_qh           skelqh[6]
-#define skel_int4_qh           skelqh[7]
-#define skel_int2_qh           skelqh[8]
-#define skel_int1_qh           skelqh[9]
-#define skel_ls_control_qh     skelqh[10]
-#define skel_fs_control_qh     skelqh[11]
-#define skel_bulk_qh           skelqh[12]
-#define skel_term_qh           skelqh[13]
-
-/* Find the skelqh entry corresponding to an interval exponent */
-#define UHCI_SKEL_INDEX(exponent)      (9 - exponent)
-
+#define UHCI_NUM_SKELQH                11
+#define SKEL_UNLINK            0
+#define skel_unlink_qh         skelqh[SKEL_UNLINK]
+#define SKEL_ISO               1
+#define skel_iso_qh            skelqh[SKEL_ISO]
+       /* int128, int64, ..., int1 = 2, 3, ..., 9 */
+#define SKEL_INDEX(exponent)   (9 - exponent)
+#define SKEL_ASYNC             9
+#define skel_async_qh          skelqh[SKEL_ASYNC]
+#define SKEL_TERM              10
+#define skel_term_qh           skelqh[SKEL_TERM]
+
+/* The following entries refer to sublists of skel_async_qh */
+#define SKEL_LS_CONTROL                20
+#define SKEL_FS_CONTROL                21
+#define SKEL_FSBR              SKEL_FS_CONTROL
+#define SKEL_BULK              22
 
 /*
  *     The UHCI controller and root hub
index 68e66b33e7269bfd1ba3972aadaee6e64b09bd79..f4ebdb3e488f59ad23d17ae7fc2a301b2aae0e9c 100644 (file)
@@ -13,7 +13,7 @@
  * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
  *               support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
  * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
- * (C) Copyright 2004-2006 Alan Stern, stern@rowland.harvard.edu
+ * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu
  */
 
 
@@ -45,15 +45,43 @@ static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci)
  */
 static void uhci_fsbr_on(struct uhci_hcd *uhci)
 {
+       struct uhci_qh *fsbr_qh, *lqh, *tqh;
+
        uhci->fsbr_is_on = 1;
-       uhci->skel_term_qh->link = cpu_to_le32(
-                       uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
+       lqh = list_entry(uhci->skel_async_qh->node.prev,
+                       struct uhci_qh, node);
+
+       /* Find the first FSBR QH.  Linear search through the list is
+        * acceptable because normally FSBR gets turned on as soon as
+        * one QH needs it. */
+       fsbr_qh = NULL;
+       list_for_each_entry_reverse(tqh, &uhci->skel_async_qh->node, node) {
+               if (tqh->skel < SKEL_FSBR)
+                       break;
+               fsbr_qh = tqh;
+       }
+
+       /* No FSBR QH means we must insert the terminating skeleton QH */
+       if (!fsbr_qh) {
+               uhci->skel_term_qh->link = LINK_TO_QH(uhci->skel_term_qh);
+               wmb();
+               lqh->link = uhci->skel_term_qh->link;
+
+       /* Otherwise loop the last QH to the first FSBR QH */
+       } else
+               lqh->link = LINK_TO_QH(fsbr_qh);
 }
 
 static void uhci_fsbr_off(struct uhci_hcd *uhci)
 {
+       struct uhci_qh *lqh;
+
        uhci->fsbr_is_on = 0;
-       uhci->skel_term_qh->link = UHCI_PTR_TERM;
+       lqh = list_entry(uhci->skel_async_qh->node.prev,
+                       struct uhci_qh, node);
+
+       /* End the async list normally and unlink the terminating QH */
+       lqh->link = uhci->skel_term_qh->link = UHCI_PTR_TERM;
 }
 
 static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb)
@@ -158,11 +186,11 @@ static inline void uhci_insert_td_in_frame_list(struct uhci_hcd *uhci,
 
                td->link = ltd->link;
                wmb();
-               ltd->link = cpu_to_le32(td->dma_handle);
+               ltd->link = LINK_TO_TD(td);
        } else {
                td->link = uhci->frame[framenum];
                wmb();
-               uhci->frame[framenum] = cpu_to_le32(td->dma_handle);
+               uhci->frame[framenum] = LINK_TO_TD(td);
                uhci->frame_cpu[framenum] = td;
        }
 }
@@ -184,7 +212,7 @@ static inline void uhci_remove_td_from_frame_list(struct uhci_hcd *uhci,
                        struct uhci_td *ntd;
 
                        ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list);
-                       uhci->frame[td->frame] = cpu_to_le32(ntd->dma_handle);
+                       uhci->frame[td->frame] = LINK_TO_TD(ntd);
                        uhci->frame_cpu[td->frame] = ntd;
                }
        } else {
@@ -405,12 +433,81 @@ static void uhci_fixup_toggles(struct uhci_qh *qh, int skip_first)
 }
 
 /*
- * Put a QH on the schedule in both hardware and software
+ * Link an Isochronous QH into its skeleton's list
  */
-static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+static inline void link_iso(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+       list_add_tail(&qh->node, &uhci->skel_iso_qh->node);
+
+       /* Isochronous QHs aren't linked by the hardware */
+}
+
+/*
+ * Link a high-period interrupt QH into the schedule at the end of its
+ * skeleton's list
+ */
+static void link_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh)
 {
        struct uhci_qh *pqh;
 
+       list_add_tail(&qh->node, &uhci->skelqh[qh->skel]->node);
+
+       pqh = list_entry(qh->node.prev, struct uhci_qh, node);
+       qh->link = pqh->link;
+       wmb();
+       pqh->link = LINK_TO_QH(qh);
+}
+
+/*
+ * Link a period-1 interrupt or async QH into the schedule at the
+ * correct spot in the async skeleton's list, and update the FSBR link
+ */
+static void link_async(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+       struct uhci_qh *pqh, *lqh;
+       __le32 link_to_new_qh;
+       __le32 *extra_link = &link_to_new_qh;
+
+       /* Find the predecessor QH for our new one and insert it in the list.
+        * The list of QHs is expected to be short, so linear search won't
+        * take too long. */
+       list_for_each_entry_reverse(pqh, &uhci->skel_async_qh->node, node) {
+               if (pqh->skel <= qh->skel)
+                       break;
+       }
+       list_add(&qh->node, &pqh->node);
+       qh->link = pqh->link;
+
+       link_to_new_qh = LINK_TO_QH(qh);
+
+       /* If this is now the first FSBR QH, take special action */
+       if (uhci->fsbr_is_on && pqh->skel < SKEL_FSBR &&
+                       qh->skel >= SKEL_FSBR) {
+               lqh = list_entry(uhci->skel_async_qh->node.prev,
+                               struct uhci_qh, node);
+
+               /* If the new QH is also the last one, we must unlink
+                * the terminating skeleton QH and make the new QH point
+                * back to itself. */
+               if (qh == lqh) {
+                       qh->link = link_to_new_qh;
+                       extra_link = &uhci->skel_term_qh->link;
+
+               /* Otherwise the last QH must point to the new QH */
+               } else
+                       extra_link = &lqh->link;
+       }
+
+       /* Link it into the schedule */
+       wmb();
+       *extra_link = pqh->link = link_to_new_qh;
+}
+
+/*
+ * Put a QH on the schedule in both hardware and software
+ */
+static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
        WARN_ON(list_empty(&qh->queue));
 
        /* Set the element pointer if it isn't set already.
@@ -421,7 +518,7 @@ static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
                struct uhci_td *td = list_entry(urbp->td_list.next,
                                struct uhci_td, list);
 
-               qh->element = cpu_to_le32(td->dma_handle);
+               qh->element = LINK_TO_TD(td);
        }
 
        /* Treat the queue as if it has just advanced */
@@ -432,18 +529,64 @@ static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
                return;
        qh->state = QH_STATE_ACTIVE;
 
-       /* Move the QH from its old list to the end of the appropriate
+       /* Move the QH from its old list to the correct spot in the appropriate
         * skeleton's list */
        if (qh == uhci->next_qh)
                uhci->next_qh = list_entry(qh->node.next, struct uhci_qh,
                                node);
-       list_move_tail(&qh->node, &qh->skel->node);
+       list_del(&qh->node);
+
+       if (qh->skel == SKEL_ISO)
+               link_iso(uhci, qh);
+       else if (qh->skel < SKEL_ASYNC)
+               link_interrupt(uhci, qh);
+       else
+               link_async(uhci, qh);
+}
+
+/*
+ * Unlink a high-period interrupt QH from the schedule
+ */
+static void unlink_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+       struct uhci_qh *pqh;
 
-       /* Link it into the schedule */
        pqh = list_entry(qh->node.prev, struct uhci_qh, node);
-       qh->link = pqh->link;
-       wmb();
-       pqh->link = UHCI_PTR_QH | cpu_to_le32(qh->dma_handle);
+       pqh->link = qh->link;
+       mb();
+}
+
+/*
+ * Unlink a period-1 interrupt or async QH from the schedule
+ */
+static void unlink_async(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+       struct uhci_qh *pqh, *lqh;
+       __le32 link_to_next_qh = qh->link;
+
+       pqh = list_entry(qh->node.prev, struct uhci_qh, node);
+
+       /* If this is the first FSBQ QH, take special action */
+       if (uhci->fsbr_is_on && pqh->skel < SKEL_FSBR &&
+                       qh->skel >= SKEL_FSBR) {
+               lqh = list_entry(uhci->skel_async_qh->node.prev,
+                               struct uhci_qh, node);
+
+               /* If this QH is also the last one, we must link in
+                * the terminating skeleton QH. */
+               if (qh == lqh) {
+                       link_to_next_qh = LINK_TO_QH(uhci->skel_term_qh);
+                       uhci->skel_term_qh->link = link_to_next_qh;
+                       wmb();
+                       qh->link = link_to_next_qh;
+
+               /* Otherwise the last QH must point to the new first FSBR QH */
+               } else
+                       lqh->link = link_to_next_qh;
+       }
+
+       pqh->link = link_to_next_qh;
+       mb();
 }
 
 /*
@@ -451,17 +594,18 @@ static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
  */
 static void uhci_unlink_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
 {
-       struct uhci_qh *pqh;
-
        if (qh->state == QH_STATE_UNLINKING)
                return;
        WARN_ON(qh->state != QH_STATE_ACTIVE || !qh->udev);
        qh->state = QH_STATE_UNLINKING;
 
        /* Unlink the QH from the schedule and record when we did it */
-       pqh = list_entry(qh->node.prev, struct uhci_qh, node);
-       pqh->link = qh->link;
-       mb();
+       if (qh->skel == SKEL_ISO)
+               ;
+       else if (qh->skel < SKEL_ASYNC)
+               unlink_interrupt(uhci, qh);
+       else
+               unlink_async(uhci, qh);
 
        uhci_get_current_frame_number(uhci);
        qh->unlink_frame = uhci->frame_number;
@@ -697,6 +841,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
        dma_addr_t data = urb->transfer_dma;
        __le32 *plink;
        struct urb_priv *urbp = urb->hcpriv;
+       int skel;
 
        /* The "pipe" thing contains the destination in bits 8--18 */
        destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
@@ -737,7 +882,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
                td = uhci_alloc_td(uhci);
                if (!td)
                        goto nomem;
-               *plink = cpu_to_le32(td->dma_handle);
+               *plink = LINK_TO_TD(td);
 
                /* Alternate Data0/1 (start with Data1) */
                destination ^= TD_TOKEN_TOGGLE;
@@ -757,7 +902,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
        td = uhci_alloc_td(uhci);
        if (!td)
                goto nomem;
-       *plink = cpu_to_le32(td->dma_handle);
+       *plink = LINK_TO_TD(td);
 
        /*
         * It's IN if the pipe is an output pipe or we're not expecting
@@ -784,7 +929,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
        td = uhci_alloc_td(uhci);
        if (!td)
                goto nomem;
-       *plink = cpu_to_le32(td->dma_handle);
+       *plink = LINK_TO_TD(td);
 
        uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0);
        wmb();
@@ -797,11 +942,13 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
         * isn't in the CONFIGURED state. */
        if (urb->dev->speed == USB_SPEED_LOW ||
                        urb->dev->state != USB_STATE_CONFIGURED)
-               qh->skel = uhci->skel_ls_control_qh;
+               skel = SKEL_LS_CONTROL;
        else {
-               qh->skel = uhci->skel_fs_control_qh;
+               skel = SKEL_FS_CONTROL;
                uhci_add_fsbr(uhci, urb);
        }
+       if (qh->state != QH_STATE_ACTIVE)
+               qh->skel = skel;
 
        urb->actual_length = -8;        /* Account for the SETUP packet */
        return 0;
@@ -860,7 +1007,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
                        td = uhci_alloc_td(uhci);
                        if (!td)
                                goto nomem;
-                       *plink = cpu_to_le32(td->dma_handle);
+                       *plink = LINK_TO_TD(td);
                }
                uhci_add_td_to_urbp(td, urbp);
                uhci_fill_td(td, status,
@@ -888,7 +1035,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
                td = uhci_alloc_td(uhci);
                if (!td)
                        goto nomem;
-               *plink = cpu_to_le32(td->dma_handle);
+               *plink = LINK_TO_TD(td);
 
                uhci_add_td_to_urbp(td, urbp);
                uhci_fill_td(td, status,
@@ -914,7 +1061,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
        td = uhci_alloc_td(uhci);
        if (!td)
                goto nomem;
-       *plink = cpu_to_le32(td->dma_handle);
+       *plink = LINK_TO_TD(td);
 
        uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0);
        wmb();
@@ -931,7 +1078,7 @@ nomem:
        return -ENOMEM;
 }
 
-static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb,
+static int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb,
                struct uhci_qh *qh)
 {
        int ret;
@@ -940,7 +1087,8 @@ static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb,
        if (urb->dev->speed == USB_SPEED_LOW)
                return -EINVAL;
 
-       qh->skel = uhci->skel_bulk_qh;
+       if (qh->state != QH_STATE_ACTIVE)
+               qh->skel = SKEL_BULK;
        ret = uhci_submit_common(uhci, urb, qh);
        if (ret == 0)
                uhci_add_fsbr(uhci, urb);
@@ -968,7 +1116,7 @@ static int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,
                if (exponent < 0)
                        return -EINVAL;
                qh->period = 1 << exponent;
-               qh->skel = uhci->skelqh[UHCI_SKEL_INDEX(exponent)];
+               qh->skel = SKEL_INDEX(exponent);
 
                /* For now, interrupt phase is fixed by the layout
                 * of the QH lists. */
@@ -1005,7 +1153,7 @@ static int uhci_fixup_short_transfer(struct uhci_hcd *uhci,
                 * the queue at the status stage transaction, which is
                 * the last TD. */
                WARN_ON(list_empty(&urbp->td_list));
-               qh->element = cpu_to_le32(td->dma_handle);
+               qh->element = LINK_TO_TD(td);
                tmp = td->list.prev;
                ret = -EINPROGRESS;
 
@@ -1216,7 +1364,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
                qh->iso_status = 0;
        }
 
-       qh->skel = uhci->skel_iso_qh;
+       qh->skel = SKEL_ISO;
        if (!qh->bandwidth_reserved)
                uhci_reserve_bandwidth(uhci, qh);
        return 0;
@@ -1566,8 +1714,7 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh)
        if (time_after(jiffies, qh->advance_jiffies + QH_WAIT_TIMEOUT)) {
 
                /* Detect the Intel bug and work around it */
-               if (qh->post_td && qh_element(qh) ==
-                               cpu_to_le32(qh->post_td->dma_handle)) {
+               if (qh->post_td && qh_element(qh) == LINK_TO_TD(qh->post_td)) {
                        qh->element = qh->post_td->link;
                        qh->advance_jiffies = jiffies;
                        ret = 1;
index 8505824848f6b7995bc40bd1cab9ef1ad3038a9c..3749f4a235f9f9a064e89ac8fee83fbc82040d43 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/usb/input.h>
+#include <linux/hid.h>
 
 /*
  * Version Information
@@ -330,7 +331,8 @@ static void usb_kbd_disconnect(struct usb_interface *intf)
 }
 
 static struct usb_device_id usb_kbd_id_table [] = {
-       { USB_INTERFACE_INFO(3, 1, 1) },
+       { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
+               USB_INTERFACE_PROTOCOL_KEYBOARD) },
        { }                                             /* Terminating entry */
 };
 
index 64a33e420cfbabb3b6758f25353c67b4b02ee55e..692fd6087779a2a8b2c5a581122d860ee0a45595 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/usb/input.h>
+#include <linux/hid.h>
 
 /*
  * Version Information
@@ -213,7 +214,8 @@ static void usb_mouse_disconnect(struct usb_interface *intf)
 }
 
 static struct usb_device_id usb_mouse_id_table [] = {
-       { USB_INTERFACE_INFO(3, 1, 2) },
+       { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
+               USB_INTERFACE_PROTOCOL_MOUSE) },
        { }     /* Terminating entry */
 };
 
index 4142e36730fc7a359012a937def88fc8b7e299d6..4f3e9bc7177d236e9c1575111c0f9b4162d1c359 100644 (file)
@@ -163,7 +163,7 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
        }
 
        id = STYLUS_DEVICE_ID;
-       if (data[1] & 0x10) { /* in prox */
+       if (data[1] & 0x80) { /* in prox */
 
                switch ((data[1] >> 5) & 3) {
 
@@ -196,9 +196,6 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
                                        wacom_report_abs(wcombo, ABS_DISTANCE, data[7] & 0x3f);
                                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);
@@ -210,19 +207,28 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
                        wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x04);
                }
                wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
-       }
-       else
-               wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
-
-       if (data[1] & 0x10)  /* only report prox-in when in area */
                wacom_report_key(wcombo, wacom->tool[0], 1);
-       if (!(data[1] & 0x90))  /* report prox-out when physically out */
+       } else if (!(data[1] & 0x90)) {
+               wacom_report_abs(wcombo, ABS_X, 0);
+               wacom_report_abs(wcombo, ABS_Y, 0);
+               if (wacom->tool[0] == BTN_TOOL_MOUSE) {
+                       wacom_report_key(wcombo, BTN_LEFT, 0);
+                       wacom_report_key(wcombo, BTN_RIGHT, 0);
+                       wacom_report_abs(wcombo, ABS_DISTANCE, 0);
+               } else {
+                       wacom_report_abs(wcombo, ABS_PRESSURE, 0);
+                       wacom_report_key(wcombo, BTN_TOUCH, 0);
+                       wacom_report_key(wcombo, BTN_STYLUS, 0);
+                       wacom_report_key(wcombo, BTN_STYLUS2, 0);
+               }
+               wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
                wacom_report_key(wcombo, wacom->tool[0], 0);
-       wacom_input_sync(wcombo);
+       }
 
        /* send pad data */
        if (wacom->features->type == WACOM_G4) {
-               if ( (wacom->serial[1] & 0xc0) != (data[7] & 0xf8) ) {
+               if (data[7] & 0xf8) {
+                       wacom_input_sync(wcombo); /* sync last event */
                        wacom->id[1] = 1;
                        wacom->serial[1] = (data[7] & 0xf8);
                        wacom_report_key(wcombo, BTN_0, (data[7] & 0x40));
@@ -230,10 +236,15 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
                        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_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID);
                        wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
                } else if (wacom->id[1]) {
+                       wacom_input_sync(wcombo); /* sync last event */
                        wacom->id[1] = 0;
+                       wacom_report_key(wcombo, BTN_0, (data[7] & 0x40));
+                       wacom_report_key(wcombo, BTN_4, (data[7] & 0x80));
                        wacom_report_key(wcombo, BTN_TOOL_FINGER, 0);
+                       wacom_report_abs(wcombo, ABS_MISC, 0);
                        wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
                }
        }
@@ -304,28 +315,35 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
                        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->features->type == INTUOS3S)))) {
-                       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) {
-               if(!((wacom->tool[idx] == BTN_TOOL_LENS)
-                                && ((wacom->features->type == INTUOS3)
-                                || (wacom->features->type == INTUOS3S)))) {
-                       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;
+               wacom_report_abs(wcombo, ABS_X, 0);
+               wacom_report_abs(wcombo, ABS_Y, 0);
+               wacom_report_abs(wcombo, ABS_DISTANCE, 0);
+               if (wacom->tool[idx] >= BTN_TOOL_MOUSE) {
+                       wacom_report_key(wcombo, BTN_LEFT, 0);
+                       wacom_report_key(wcombo, BTN_MIDDLE, 0);
+                       wacom_report_key(wcombo, BTN_RIGHT, 0);
+                       wacom_report_key(wcombo, BTN_SIDE, 0);
+                       wacom_report_key(wcombo, BTN_EXTRA, 0);
+                       wacom_report_abs(wcombo, ABS_THROTTLE, 0);
+                       wacom_report_abs(wcombo, ABS_RZ, 0);
+               } else {
+                       wacom_report_abs(wcombo, ABS_PRESSURE, 0);
+                       wacom_report_abs(wcombo, ABS_TILT_X, 0);
+                       wacom_report_abs(wcombo, ABS_TILT_Y, 0);
+                       wacom_report_key(wcombo, BTN_STYLUS, 0);
+                       wacom_report_key(wcombo, BTN_STYLUS2, 0);
+                       wacom_report_key(wcombo, BTN_TOUCH, 0);
+                       wacom_report_abs(wcombo, ABS_WHEEL, 0);
                }
+               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;
 }
@@ -394,6 +412,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
                        wacom_report_key(wcombo, wacom->tool[1], 1);
                else
                        wacom_report_key(wcombo, wacom->tool[1], 0);
+               wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID);
                wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xffffffff);
                 return 1;
        }
@@ -403,6 +422,12 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
        if (result)
                 return result-1;
 
+       /* Only large I3 and I1 & I2 support Lense Cursor */
+       if((wacom->tool[idx] == BTN_TOOL_LENS)
+                       && ((wacom->features->type == INTUOS3)
+                       || (wacom->features->type == INTUOS3S)))
+               return 0;
+
        /* Cintiq doesn't send data when RDY bit isn't set */
        if ((wacom->features->type == CINTIQ) && !(data[1] & 0x40))
                  return 0;
@@ -554,11 +579,11 @@ static struct wacom_features wacom_features[] = {
        { "Wacom Volito2 4x5",   8,   5104,  3712,  511, 63, GRAPHIRE },
        { "Wacom Volito2 2x3",   8,   3248,  2320,  511, 63, GRAPHIRE },
        { "Wacom PenPartner2",   8,   3250,  2320,  255, 63, GRAPHIRE },
-       { "Wacom Intuos 4x5",   10,  12700, 10600, 1023, 63, INTUOS },
-       { "Wacom Intuos 6x8",   10,  20320, 16240, 1023, 63, INTUOS },
-       { "Wacom Intuos 9x12",  10,  30480, 24060, 1023, 63, INTUOS },
-       { "Wacom Intuos 12x12", 10,  30480, 31680, 1023, 63, INTUOS },
-       { "Wacom Intuos 12x18", 10,  45720, 31680, 1023, 63, INTUOS },
+       { "Wacom Intuos 4x5",   10,  12700, 10600, 1023, 31, INTUOS },
+       { "Wacom Intuos 6x8",   10,  20320, 16240, 1023, 31, INTUOS },
+       { "Wacom Intuos 9x12",  10,  30480, 24060, 1023, 31, INTUOS },
+       { "Wacom Intuos 12x12", 10,  30480, 31680, 1023, 31, INTUOS },
+       { "Wacom Intuos 12x18", 10,  45720, 31680, 1023, 31, INTUOS },
        { "Wacom PL400",         8,   5408,  4056,  255,  0, PL },
        { "Wacom PL500",         8,   6144,  4608,  255,  0, PL },
        { "Wacom PL600",         8,   6126,  4604,  255,  0, PL },
@@ -571,11 +596,11 @@ static struct wacom_features wacom_features[] = {
        { "Wacom DTF521",        8,   6282,  4762,  511,  0, PL },
        { "Wacom DTF720",        8,   6858,  5506,  511,  0, PL },
        { "Wacom Cintiq Partner",8,  20480, 15360,  511,  0, PTU },
-       { "Wacom Intuos2 4x5",   10, 12700, 10600, 1023, 63, INTUOS },
-       { "Wacom Intuos2 6x8",   10, 20320, 16240, 1023, 63, INTUOS },
-       { "Wacom Intuos2 9x12",  10, 30480, 24060, 1023, 63, INTUOS },
-       { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 63, INTUOS },
-       { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 63, INTUOS },
+       { "Wacom Intuos2 4x5",   10, 12700, 10600, 1023, 31, INTUOS },
+       { "Wacom Intuos2 6x8",   10, 20320, 16240, 1023, 31, INTUOS },
+       { "Wacom Intuos2 9x12",  10, 30480, 24060, 1023, 31, INTUOS },
+       { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 31, INTUOS },
+       { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 31, INTUOS },
        { "Wacom Intuos3 4x5",   10, 25400, 20320, 1023, 63, INTUOS3S },
        { "Wacom Intuos3 6x8",   10, 40640, 30480, 1023, 63, INTUOS3 },
        { "Wacom Intuos3 9x12",  10, 60960, 45720, 1023, 63, INTUOS3 },
@@ -584,7 +609,7 @@ static struct wacom_features wacom_features[] = {
        { "Wacom Intuos3 6x11",  10, 54204, 31750, 1023, 63, INTUOS3 },
        { "Wacom Intuos3 4x6",   10, 31496, 19685, 1023, 63, INTUOS3S },
        { "Wacom Cintiq 21UX",   10, 87200, 65600, 1023, 63, CINTIQ },
-       { "Wacom Intuos2 6x8",   10, 20320, 16240, 1023, 63, INTUOS },
+       { "Wacom Intuos2 6x8",   10, 20320, 16240, 1023, 31, INTUOS },
        { }
 };
 
index a1d9ce007970b12084c78c81e51cd0551541d3d1..a23022287248e12805f1250de2f0a41c3a079ae8 100644 (file)
@@ -12,6 +12,7 @@
 #define STYLUS_DEVICE_ID       0x02
 #define CURSOR_DEVICE_ID       0x06
 #define ERASER_DEVICE_ID       0x0A
+#define PAD_DEVICE_ID          0x0F
 
 enum {
        PENPARTNER = 0,
index 4907e8b8007087b37a3141135312acd43b2b4f90..9c7eb6144d02d3f8852a3c1abdb6a79a2f8a690a 100644 (file)
@@ -244,6 +244,20 @@ config USB_TRANCEVIBRATOR
          To compile this driver as a module, choose M here: the
          module will be called trancevibrator.
 
+config USB_IOWARRIOR
+       tristate "IO Warrior driver support"
+       depends on USB
+       help
+         Say Y here if you want to support the IO Warrior devices from Code
+         Mercenaries.  This includes support for the following devices:
+               IO Warrior 40
+               IO Warrior 24
+               IO Warrior 56
+               IO Warrior 24 Power Vampire
+
+         To compile this driver as a module, choose M here: the
+         module will be called iowarrior.
+
 config USB_TEST
        tristate "USB testing driver (DEVELOPMENT)"
        depends on USB && USB_DEVICEFS && EXPERIMENTAL
index dac2d5b715666492aa75d446d4b2acd16bcef067..b68e6b774f1add605b8bf5e89a930e07f1ca3ac2 100644 (file)
@@ -13,6 +13,7 @@ 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_IOWARRIOR)    += iowarrior.o
 obj-$(CONFIG_USB_LCD)          += usblcd.o
 obj-$(CONFIG_USB_LD)           += ldusb.o
 obj-$(CONFIG_USB_LED)          += usbled.o
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
new file mode 100644 (file)
index 0000000..d69665c
--- /dev/null
@@ -0,0 +1,925 @@
+/*
+ *  Native support for the I/O-Warrior USB devices
+ *
+ *  Copyright (c) 2003-2005  Code Mercenaries GmbH
+ *  written by Christian Lucht <lucht@codemercs.com>
+ *
+ *  based on
+
+ *  usb-skeleton.c by Greg Kroah-Hartman  <greg@kroah.com>
+ *  brlvger.c by Stephane Dalton  <sdalton@videotron.ca>
+ *           and St�hane Doyon   <s.doyon@videotron.ca>
+ *
+ *  Released under the GPLv2.
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/version.h>
+#include <linux/usb/iowarrior.h>
+
+/* Version Information */
+#define DRIVER_VERSION "v0.4.0"
+#define DRIVER_AUTHOR "Christian Lucht <lucht@codemercs.com>"
+#define DRIVER_DESC "USB IO-Warrior driver (Linux 2.6.x)"
+
+#define USB_VENDOR_ID_CODEMERCS                1984
+/* low speed iowarrior */
+#define USB_DEVICE_ID_CODEMERCS_IOW40  0x1500
+#define USB_DEVICE_ID_CODEMERCS_IOW24  0x1501
+#define USB_DEVICE_ID_CODEMERCS_IOWPV1 0x1511
+#define USB_DEVICE_ID_CODEMERCS_IOWPV2 0x1512
+/* full speed iowarrior */
+#define USB_DEVICE_ID_CODEMERCS_IOW56  0x1503
+
+/* Get a minor range for your devices from the usb maintainer */
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define IOWARRIOR_MINOR_BASE   0
+#else
+#define IOWARRIOR_MINOR_BASE   208     // SKELETON_MINOR_BASE 192 + 16, not offical yet
+#endif
+
+/* interrupt input queue size */
+#define MAX_INTERRUPT_BUFFER 16
+/*
+   maximum number of urbs that are submitted for writes at the same time,
+   this applies to the IOWarrior56 only!
+   IOWarrior24 and IOWarrior40 use synchronous usb_control_msg calls.
+*/
+#define MAX_WRITES_IN_FLIGHT 4
+
+/* Use our own dbg macro */
+#undef dbg
+#define dbg( format, arg... ) do { if( debug ) printk( KERN_DEBUG __FILE__ ": " format "\n" , ## arg ); } while ( 0 )
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/* Module parameters */
+static int debug = 0;
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "debug=1 enables debugging messages");
+
+static struct usb_driver iowarrior_driver;
+
+/*--------------*/
+/*     data     */
+/*--------------*/
+
+/* Structure to hold all of our device specific stuff */
+struct iowarrior {
+       struct mutex mutex;                     /* locks this structure */
+       struct usb_device *udev;                /* save off the usb device pointer */
+       struct usb_interface *interface;        /* the interface for this device */
+       unsigned char minor;                    /* the starting minor number for this device */
+       struct usb_endpoint_descriptor *int_out_endpoint;       /* endpoint for reading (needed for IOW56 only) */
+       struct usb_endpoint_descriptor *int_in_endpoint;        /* endpoint for reading */
+       struct urb *int_in_urb;         /* the urb for reading data */
+       unsigned char *int_in_buffer;   /* buffer for data to be read */
+       unsigned char serial_number;    /* to detect lost packages */
+       unsigned char *read_queue;      /* size is MAX_INTERRUPT_BUFFER * packet size */
+       wait_queue_head_t read_wait;
+       wait_queue_head_t write_wait;   /* wait-queue for writing to the device */
+       atomic_t write_busy;            /* number of write-urbs submitted */
+       atomic_t read_idx;
+       atomic_t intr_idx;
+       spinlock_t intr_idx_lock;       /* protects intr_idx */
+       atomic_t overflow_flag;         /* signals an index 'rollover' */
+       int present;                    /* this is 1 as long as the device is connected */
+       int opened;                     /* this is 1 if the device is currently open */
+       char chip_serial[9];            /* the serial number string of the chip connected */
+       int report_size;                /* number of bytes in a report */
+       u16 product_id;
+};
+
+/*--------------*/
+/*    globals   */
+/*--------------*/
+/* prevent races between open() and disconnect() */
+static DECLARE_MUTEX(disconnect_sem);
+
+/*
+ *  USB spec identifies 5 second timeouts.
+ */
+#define GET_TIMEOUT 5
+#define USB_REQ_GET_REPORT  0x01
+//#if 0
+static int usb_get_report(struct usb_device *dev,
+                         struct usb_host_interface *inter, unsigned char type,
+                         unsigned char id, void *buf, int size)
+{
+       return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                              USB_REQ_GET_REPORT,
+                              USB_DIR_IN | USB_TYPE_CLASS |
+                              USB_RECIP_INTERFACE, (type << 8) + id,
+                              inter->desc.bInterfaceNumber, buf, size,
+                              GET_TIMEOUT);
+}
+//#endif
+
+#define USB_REQ_SET_REPORT 0x09
+
+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->cur_altsetting->desc.bInterfaceNumber, buf,
+                              size, 1);
+}
+
+/*---------------------*/
+/* driver registration */
+/*---------------------*/
+/* table of devices that work with this driver */
+static struct usb_device_id iowarrior_ids[] = {
+       {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40)},
+       {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24)},
+       {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV1)},
+       {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV2)},
+       {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW56)},
+       {}                      /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, iowarrior_ids);
+
+/*
+ * USB callback handler for reading data
+ */
+static void iowarrior_callback(struct urb *urb)
+{
+       struct iowarrior *dev = (struct iowarrior *)urb->context;
+       int intr_idx;
+       int read_idx;
+       int aux_idx;
+       int offset;
+       int status;
+
+       switch (urb->status) {
+       case 0:
+               /* success */
+               break;
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               return;
+       default:
+               goto exit;
+       }
+
+       spin_lock(&dev->intr_idx_lock);
+       intr_idx = atomic_read(&dev->intr_idx);
+       /* aux_idx become previous intr_idx */
+       aux_idx = (intr_idx == 0) ? (MAX_INTERRUPT_BUFFER - 1) : (intr_idx - 1);
+       read_idx = atomic_read(&dev->read_idx);
+
+       /* queue is not empty and it's interface 0 */
+       if ((intr_idx != read_idx)
+           && (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0)) {
+               /* + 1 for serial number */
+               offset = aux_idx * (dev->report_size + 1);
+               if (!memcmp
+                   (dev->read_queue + offset, urb->transfer_buffer,
+                    dev->report_size)) {
+                       /* equal values on interface 0 will be ignored */
+                       spin_unlock(&dev->intr_idx_lock);
+                       goto exit;
+               }
+       }
+
+       /* aux_idx become next intr_idx */
+       aux_idx = (intr_idx == (MAX_INTERRUPT_BUFFER - 1)) ? 0 : (intr_idx + 1);
+       if (read_idx == aux_idx) {
+               /* queue full, dropping oldest input */
+               read_idx = (++read_idx == MAX_INTERRUPT_BUFFER) ? 0 : read_idx;
+               atomic_set(&dev->read_idx, read_idx);
+               atomic_set(&dev->overflow_flag, 1);
+       }
+
+       /* +1 for serial number */
+       offset = intr_idx * (dev->report_size + 1);
+       memcpy(dev->read_queue + offset, urb->transfer_buffer,
+              dev->report_size);
+       *(dev->read_queue + offset + (dev->report_size)) = dev->serial_number++;
+
+       atomic_set(&dev->intr_idx, aux_idx);
+       spin_unlock(&dev->intr_idx_lock);
+       /* tell the blocking read about the new data */
+       wake_up_interruptible(&dev->read_wait);
+
+exit:
+       status = usb_submit_urb(urb, GFP_ATOMIC);
+       if (status)
+               dev_err(&dev->interface->dev, "%s - usb_submit_urb failed with result %d",
+                       __FUNCTION__, status);
+
+}
+
+/*
+ * USB Callback handler for write-ops
+ */
+static void iowarrior_write_callback(struct urb *urb)
+{
+       struct iowarrior *dev;
+       dev = (struct iowarrior *)urb->context;
+       /* sync/async unlink faults aren't errors */
+       if (urb->status &&
+           !(urb->status == -ENOENT ||
+             urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) {
+               dbg("%s - nonzero write bulk status received: %d",
+                   __func__, urb->status);
+       }
+       /* free up our allocated buffer */
+       usb_buffer_free(urb->dev, urb->transfer_buffer_length,
+                       urb->transfer_buffer, urb->transfer_dma);
+       /* tell a waiting writer the interrupt-out-pipe is available again */
+       atomic_dec(&dev->write_busy);
+       wake_up_interruptible(&dev->write_wait);
+}
+
+/**
+ *     iowarrior_delete
+ */
+static inline void iowarrior_delete(struct iowarrior *dev)
+{
+       dbg("%s - minor %d", __func__, dev->minor);
+       kfree(dev->int_in_buffer);
+       usb_free_urb(dev->int_in_urb);
+       kfree(dev->read_queue);
+       kfree(dev);
+}
+
+/*---------------------*/
+/* fops implementation */
+/*---------------------*/
+
+static int read_index(struct iowarrior *dev)
+{
+       int intr_idx, read_idx;
+
+       read_idx = atomic_read(&dev->read_idx);
+       intr_idx = atomic_read(&dev->intr_idx);
+
+       return (read_idx == intr_idx ? -1 : read_idx);
+}
+
+/**
+ *  iowarrior_read
+ */
+static ssize_t iowarrior_read(struct file *file, char __user *buffer,
+                             size_t count, loff_t *ppos)
+{
+       struct iowarrior *dev;
+       int read_idx;
+       int offset;
+
+       dev = (struct iowarrior *)file->private_data;
+
+       /* verify that the device wasn't unplugged */
+       if (dev == NULL || !dev->present)
+               return -ENODEV;
+
+       dbg("%s - minor %d, count = %zd", __func__, dev->minor, count);
+
+       /* read count must be packet size (+ time stamp) */
+       if ((count != dev->report_size)
+           && (count != (dev->report_size + 1)))
+               return -EINVAL;
+
+       /* repeat until no buffer overrun in callback handler occur */
+       do {
+               atomic_set(&dev->overflow_flag, 0);
+               if ((read_idx = read_index(dev)) == -1) {
+                       /* queue emty */
+                       if (file->f_flags & O_NONBLOCK)
+                               return -EAGAIN;
+                       else {
+                               //next line will return when there is either new data, or the device is unplugged
+                               int r = wait_event_interruptible(dev->read_wait,
+                                                                (!dev->present
+                                                                 || (read_idx =
+                                                                     read_index
+                                                                     (dev)) !=
+                                                                 -1));
+                               if (r) {
+                                       //we were interrupted by a signal
+                                       return -ERESTART;
+                               }
+                               if (!dev->present) {
+                                       //The device was unplugged
+                                       return -ENODEV;
+                               }
+                               if (read_idx == -1) {
+                                       // Can this happen ???
+                                       return 0;
+                               }
+                       }
+               }
+
+               offset = read_idx * (dev->report_size + 1);
+               if (copy_to_user(buffer, dev->read_queue + offset, count)) {
+                       return -EFAULT;
+               }
+       } while (atomic_read(&dev->overflow_flag));
+
+       read_idx = ++read_idx == MAX_INTERRUPT_BUFFER ? 0 : read_idx;
+       atomic_set(&dev->read_idx, read_idx);
+       return count;
+}
+
+/*
+ * iowarrior_write
+ */
+static ssize_t iowarrior_write(struct file *file,
+                              const char __user *user_buffer,
+                              size_t count, loff_t *ppos)
+{
+       struct iowarrior *dev;
+       int retval = 0;
+       char *buf = NULL;       /* for IOW24 and IOW56 we need a buffer */
+       struct urb *int_out_urb = NULL;
+
+       dev = (struct iowarrior *)file->private_data;
+
+       mutex_lock(&dev->mutex);
+       /* verify that the device wasn't unplugged */
+       if (dev == NULL || !dev->present) {
+               retval = -ENODEV;
+               goto exit;
+       }
+       dbg("%s - minor %d, count = %zd", __func__, dev->minor, count);
+       /* if count is 0 we're already done */
+       if (count == 0) {
+               retval = 0;
+               goto exit;
+       }
+       /* We only accept full reports */
+       if (count != dev->report_size) {
+               retval = -EINVAL;
+               goto exit;
+       }
+       switch (dev->product_id) {
+       case USB_DEVICE_ID_CODEMERCS_IOW24:
+       case USB_DEVICE_ID_CODEMERCS_IOWPV1:
+       case USB_DEVICE_ID_CODEMERCS_IOWPV2:
+       case USB_DEVICE_ID_CODEMERCS_IOW40:
+               /* IOW24 and IOW40 use a synchronous call */
+               buf = kmalloc(8, GFP_KERNEL);   /* 8 bytes are enough for both products */
+               if (!buf) {
+                       retval = -ENOMEM;
+                       goto exit;
+               }
+               if (copy_from_user(buf, user_buffer, count)) {
+                       retval = -EFAULT;
+                       kfree(buf);
+                       goto exit;
+               }
+               retval = usb_set_report(dev->interface, 2, 0, buf, count);
+               kfree(buf);
+               goto exit;
+               break;
+       case USB_DEVICE_ID_CODEMERCS_IOW56:
+               /* The IOW56 uses asynchronous IO and more urbs */
+               if (atomic_read(&dev->write_busy) == MAX_WRITES_IN_FLIGHT) {
+                       /* Wait until we are below the limit for submitted urbs */
+                       if (file->f_flags & O_NONBLOCK) {
+                               retval = -EAGAIN;
+                               goto exit;
+                       } else {
+                               retval = wait_event_interruptible(dev->write_wait,
+                                                                 (!dev->present || (atomic_read (&dev-> write_busy) < MAX_WRITES_IN_FLIGHT)));
+                               if (retval) {
+                                       /* we were interrupted by a signal */
+                                       retval = -ERESTART;
+                                       goto exit;
+                               }
+                               if (!dev->present) {
+                                       /* The device was unplugged */
+                                       retval = -ENODEV;
+                                       goto exit;
+                               }
+                               if (!dev->opened) {
+                                       /* We were closed while waiting for an URB */
+                                       retval = -ENODEV;
+                                       goto exit;
+                               }
+                       }
+               }
+               atomic_inc(&dev->write_busy);
+               int_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+               if (!int_out_urb) {
+                       retval = -ENOMEM;
+                       dbg("%s Unable to allocate urb ", __func__);
+                       goto error;
+               }
+               buf = usb_buffer_alloc(dev->udev, dev->report_size,
+                                      GFP_KERNEL, &int_out_urb->transfer_dma);
+               if (!buf) {
+                       retval = -ENOMEM;
+                       dbg("%s Unable to allocate buffer ", __func__);
+                       goto error;
+               }
+               usb_fill_int_urb(int_out_urb, dev->udev,
+                                usb_sndintpipe(dev->udev,
+                                               dev->int_out_endpoint->bEndpointAddress),
+                                buf, dev->report_size,
+                                iowarrior_write_callback, dev,
+                                dev->int_out_endpoint->bInterval);
+               int_out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+               if (copy_from_user(buf, user_buffer, count)) {
+                       retval = -EFAULT;
+                       goto error;
+               }
+               retval = usb_submit_urb(int_out_urb, GFP_KERNEL);
+               if (retval) {
+                       dbg("%s submit error %d for urb nr.%d", __func__,
+                           retval, atomic_read(&dev->write_busy));
+                       goto error;
+               }
+               /* submit was ok */
+               retval = count;
+               usb_free_urb(int_out_urb);
+               goto exit;
+               break;
+       default:
+               /* what do we have here ? An unsupported Product-ID ? */
+               dev_err(&dev->interface->dev, "%s - not supported for product=0x%x",
+                       __FUNCTION__, dev->product_id);
+               retval = -EFAULT;
+               goto exit;
+               break;
+       }
+error:
+       usb_buffer_free(dev->udev, dev->report_size, buf,
+                       int_out_urb->transfer_dma);
+       usb_free_urb(int_out_urb);
+       atomic_dec(&dev->write_busy);
+       wake_up_interruptible(&dev->write_wait);
+exit:
+       mutex_unlock(&dev->mutex);
+       return retval;
+}
+
+/**
+ *     iowarrior_ioctl
+ */
+static int iowarrior_ioctl(struct inode *inode, struct file *file,
+                          unsigned int cmd, unsigned long arg)
+{
+       struct iowarrior *dev = NULL;
+       __u8 *buffer;
+       __u8 __user *user_buffer;
+       int retval;
+       int io_res;             /* checks for bytes read/written and copy_to/from_user results */
+
+       dev = (struct iowarrior *)file->private_data;
+       if (dev == NULL) {
+               return -ENODEV;
+       }
+
+       buffer = kzalloc(dev->report_size, GFP_KERNEL);
+       if (!buffer)
+               return -ENOMEM;
+
+       /* lock this object */
+       mutex_lock(&dev->mutex);
+
+       /* verify that the device wasn't unplugged */
+       if (!dev->present) {
+               mutex_unlock(&dev->mutex);
+               return -ENODEV;
+       }
+
+       dbg("%s - minor %d, cmd 0x%.4x, arg %ld", __func__, dev->minor, cmd,
+           arg);
+
+       retval = 0;
+       io_res = 0;
+       switch (cmd) {
+       case IOW_WRITE:
+               if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW24 ||
+                   dev->product_id == USB_DEVICE_ID_CODEMERCS_IOWPV1 ||
+                   dev->product_id == USB_DEVICE_ID_CODEMERCS_IOWPV2 ||
+                   dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW40) {
+                       user_buffer = (__u8 __user *)arg;
+                       io_res = copy_from_user(buffer, user_buffer,
+                                               dev->report_size);
+                       if (io_res) {
+                               retval = -EFAULT;
+                       } else {
+                               io_res = usb_set_report(dev->interface, 2, 0,
+                                                       buffer,
+                                                       dev->report_size);
+                               if (io_res < 0)
+                                       retval = io_res;
+                       }
+               } else {
+                       retval = -EINVAL;
+                       dev_err(&dev->interface->dev,
+                               "ioctl 'IOW_WRITE' is not supported for product=0x%x.",
+                               dev->product_id);
+               }
+               break;
+       case IOW_READ:
+               user_buffer = (__u8 __user *)arg;
+               io_res = usb_get_report(dev->udev,
+                                       dev->interface->cur_altsetting, 1, 0,
+                                       buffer, dev->report_size);
+               if (io_res < 0)
+                       retval = io_res;
+               else {
+                       io_res = copy_to_user(user_buffer, buffer, dev->report_size);
+                       if (io_res < 0)
+                               retval = -EFAULT;
+               }
+               break;
+       case IOW_GETINFO:
+               {
+                       /* Report available information for the device */
+                       struct iowarrior_info info;
+                       /* needed for power consumption */
+                       struct usb_config_descriptor *cfg_descriptor = &dev->udev->actconfig->desc;
+
+                       /* directly from the descriptor */
+                       info.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
+                       info.product = dev->product_id;
+                       info.revision = le16_to_cpu(dev->udev->descriptor.bcdDevice);
+
+                       /* 0==UNKNOWN, 1==LOW(usb1.1) ,2=FULL(usb1.1), 3=HIGH(usb2.0) */
+                       info.speed = le16_to_cpu(dev->udev->speed);
+                       info.if_num = dev->interface->cur_altsetting->desc.bInterfaceNumber;
+                       info.report_size = dev->report_size;
+
+                       /* serial number string has been read earlier 8 chars or empty string */
+                       memcpy(info.serial, dev->chip_serial,
+                              sizeof(dev->chip_serial));
+                       if (cfg_descriptor == NULL) {
+                               info.power = -1;        /* no information available */
+                       } else {
+                               /* the MaxPower is stored in units of 2mA to make it fit into a byte-value */
+                               info.power = cfg_descriptor->bMaxPower * 2;
+                       }
+                       io_res = copy_to_user((struct iowarrior_info __user *)arg, &info,
+                                        sizeof(struct iowarrior_info));
+                       if (io_res < 0)
+                               retval = -EFAULT;
+                       break;
+               }
+       default:
+               /* return that we did not understand this ioctl call */
+               retval = -ENOTTY;
+               break;
+       }
+
+       /* unlock the device */
+       mutex_unlock(&dev->mutex);
+       return retval;
+}
+
+/**
+ *     iowarrior_open
+ */
+static int iowarrior_open(struct inode *inode, struct file *file)
+{
+       struct iowarrior *dev = NULL;
+       struct usb_interface *interface;
+       int subminor;
+       int retval = 0;
+
+       dbg("%s", __func__);
+
+       subminor = iminor(inode);
+
+       /* prevent disconnects */
+       down(&disconnect_sem);
+
+       interface = usb_find_interface(&iowarrior_driver, subminor);
+       if (!interface) {
+               err("%s - error, can't find device for minor %d", __FUNCTION__,
+                   subminor);
+               retval = -ENODEV;
+               goto out;
+       }
+
+       dev = usb_get_intfdata(interface);
+       if (!dev) {
+               retval = -ENODEV;
+               goto out;
+       }
+
+       /* Only one process can open each device, no sharing. */
+       if (dev->opened) {
+               retval = -EBUSY;
+               goto out;
+       }
+
+       /* setup interrupt handler for receiving values */
+       if ((retval = usb_submit_urb(dev->int_in_urb, GFP_KERNEL)) < 0) {
+               dev_err(&interface->dev, "Error %d while submitting URB\n", retval);
+               retval = -EFAULT;
+               goto out;
+       }
+       /* increment our usage count for the driver */
+       ++dev->opened;
+       /* save our object in the file's private structure */
+       file->private_data = dev;
+       retval = 0;
+
+out:
+       up(&disconnect_sem);
+       return retval;
+}
+
+/**
+ *     iowarrior_release
+ */
+static int iowarrior_release(struct inode *inode, struct file *file)
+{
+       struct iowarrior *dev;
+       int retval = 0;
+
+       dev = (struct iowarrior *)file->private_data;
+       if (dev == NULL) {
+               return -ENODEV;
+       }
+
+       dbg("%s - minor %d", __func__, dev->minor);
+
+       /* lock our device */
+       mutex_lock(&dev->mutex);
+
+       if (dev->opened <= 0) {
+               retval = -ENODEV;       /* close called more than once */
+               mutex_unlock(&dev->mutex);
+       } else {
+               dev->opened = 0;        /* we're closeing now */
+               retval = 0;
+               if (dev->present) {
+                       /*
+                          The device is still connected so we only shutdown
+                          pending read-/write-ops.
+                        */
+                       usb_kill_urb(dev->int_in_urb);
+                       wake_up_interruptible(&dev->read_wait);
+                       wake_up_interruptible(&dev->write_wait);
+                       mutex_unlock(&dev->mutex);
+               } else {
+                       /* The device was unplugged, cleanup resources */
+                       mutex_unlock(&dev->mutex);
+                       iowarrior_delete(dev);
+               }
+       }
+       return retval;
+}
+
+static unsigned iowarrior_poll(struct file *file, poll_table * wait)
+{
+       struct iowarrior *dev = file->private_data;
+       unsigned int mask = 0;
+
+       if (!dev->present)
+               return POLLERR | POLLHUP;
+
+       poll_wait(file, &dev->read_wait, wait);
+       poll_wait(file, &dev->write_wait, wait);
+
+       if (!dev->present)
+               return POLLERR | POLLHUP;
+
+       if (read_index(dev) != -1)
+               mask |= POLLIN | POLLRDNORM;
+
+       if (atomic_read(&dev->write_busy) < MAX_WRITES_IN_FLIGHT)
+               mask |= POLLOUT | POLLWRNORM;
+       return mask;
+}
+
+/*
+ * File operations needed when we register this driver.
+ * This assumes that this driver NEEDS file operations,
+ * of course, which means that the driver is expected
+ * to have a node in the /dev directory. If the USB
+ * device were for a network interface then the driver
+ * would use "struct net_driver" instead, and a serial
+ * device would use "struct tty_driver".
+ */
+static struct file_operations iowarrior_fops = {
+       .owner = THIS_MODULE,
+       .write = iowarrior_write,
+       .read = iowarrior_read,
+       .ioctl = iowarrior_ioctl,
+       .open = iowarrior_open,
+       .release = iowarrior_release,
+       .poll = iowarrior_poll,
+};
+
+/*
+ * 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 iowarrior_class = {
+       .name = "iowarrior%d",
+       .fops = &iowarrior_fops,
+       .minor_base = IOWARRIOR_MINOR_BASE,
+};
+
+/*---------------------------------*/
+/*  probe and disconnect functions */
+/*---------------------------------*/
+/**
+ *     iowarrior_probe
+ *
+ *     Called by the usb core when a new device is connected that it thinks
+ *     this driver might be interested in.
+ */
+static int iowarrior_probe(struct usb_interface *interface,
+                          const struct usb_device_id *id)
+{
+       struct usb_device *udev = interface_to_usbdev(interface);
+       struct iowarrior *dev = NULL;
+       struct usb_host_interface *iface_desc;
+       struct usb_endpoint_descriptor *endpoint;
+       int i;
+       int retval = -ENOMEM;
+       int idele = 0;
+
+       /* allocate memory for our device state and intialize it */
+       dev = kzalloc(sizeof(struct iowarrior), GFP_KERNEL);
+       if (dev == NULL) {
+               dev_err(&interface->dev, "Out of memory");
+               return retval;
+       }
+
+       mutex_init(&dev->mutex);
+
+       atomic_set(&dev->intr_idx, 0);
+       atomic_set(&dev->read_idx, 0);
+       spin_lock_init(&dev->intr_idx_lock);
+       atomic_set(&dev->overflow_flag, 0);
+       init_waitqueue_head(&dev->read_wait);
+       atomic_set(&dev->write_busy, 0);
+       init_waitqueue_head(&dev->write_wait);
+
+       dev->udev = udev;
+       dev->interface = interface;
+
+       iface_desc = interface->cur_altsetting;
+       dev->product_id = le16_to_cpu(udev->descriptor.idProduct);
+
+       /* 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->int_in_endpoint = endpoint;
+               if (usb_endpoint_is_int_out(endpoint))
+                       /* this one will match for the IOWarrior56 only */
+                       dev->int_out_endpoint = endpoint;
+       }
+       /* we have to check the report_size often, so remember it in the endianess suitable for our machine */
+       dev->report_size = le16_to_cpu(dev->int_in_endpoint->wMaxPacketSize);
+       if ((dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) &&
+           (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56))
+               /* IOWarrior56 has wMaxPacketSize different from report size */
+               dev->report_size = 7;
+
+       /* create the urb and buffer for reading */
+       dev->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!dev->int_in_urb) {
+               dev_err(&interface->dev, "Couldn't allocate interrupt_in_urb\n");
+               goto error;
+       }
+       dev->int_in_buffer = kmalloc(dev->report_size, GFP_KERNEL);
+       if (!dev->int_in_buffer) {
+               dev_err(&interface->dev, "Couldn't allocate int_in_buffer\n");
+               goto error;
+       }
+       usb_fill_int_urb(dev->int_in_urb, dev->udev,
+                        usb_rcvintpipe(dev->udev,
+                                       dev->int_in_endpoint->bEndpointAddress),
+                        dev->int_in_buffer, dev->report_size,
+                        iowarrior_callback, dev,
+                        dev->int_in_endpoint->bInterval);
+       /* create an internal buffer for interrupt data from the device */
+       dev->read_queue =
+           kmalloc(((dev->report_size + 1) * MAX_INTERRUPT_BUFFER),
+                   GFP_KERNEL);
+       if (!dev->read_queue) {
+               dev_err(&interface->dev, "Couldn't allocate read_queue\n");
+               goto error;
+       }
+       /* Get the serial-number of the chip */
+       memset(dev->chip_serial, 0x00, sizeof(dev->chip_serial));
+       usb_string(udev, udev->descriptor.iSerialNumber, dev->chip_serial,
+                  sizeof(dev->chip_serial));
+       if (strlen(dev->chip_serial) != 8)
+               memset(dev->chip_serial, 0x00, sizeof(dev->chip_serial));
+
+       /* Set the idle timeout to 0, if this is interface 0 */
+       if (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) {
+               idele = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                                       0x0A,
+                                       USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
+                                       0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+               dbg("idele = %d", idele);
+       }
+       /* allow device read and ioctl */
+       dev->present = 1;
+
+       /* we can register the device now, as it is ready */
+       usb_set_intfdata(interface, dev);
+
+       retval = usb_register_dev(interface, &iowarrior_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, "IOWarrior product=0x%x, serial=%s interface=%d "
+                "now attached to iowarrior%d\n", dev->product_id, dev->chip_serial,
+                iface_desc->desc.bInterfaceNumber, dev->minor - IOWARRIOR_MINOR_BASE);
+       return retval;
+
+error:
+       iowarrior_delete(dev);
+       return retval;
+}
+
+/**
+ *     iowarrior_disconnect
+ *
+ *     Called by the usb core when the device is removed from the system.
+ */
+static void iowarrior_disconnect(struct usb_interface *interface)
+{
+       struct iowarrior *dev;
+       int minor;
+
+       /* prevent races with open() */
+       down(&disconnect_sem);
+
+       dev = usb_get_intfdata(interface);
+       usb_set_intfdata(interface, NULL);
+
+       mutex_lock(&dev->mutex);
+
+       minor = dev->minor;
+
+       /* give back our minor */
+       usb_deregister_dev(interface, &iowarrior_class);
+
+       /* prevent device read, write and ioctl */
+       dev->present = 0;
+
+       mutex_unlock(&dev->mutex);
+
+       if (dev->opened) {
+               /* There is a process that holds a filedescriptor to the device ,
+                  so we only shutdown read-/write-ops going on.
+                  Deleting the device is postponed until close() was called.
+                */
+               usb_kill_urb(dev->int_in_urb);
+               wake_up_interruptible(&dev->read_wait);
+               wake_up_interruptible(&dev->write_wait);
+       } else {
+               /* no process is using the device, cleanup now */
+               iowarrior_delete(dev);
+       }
+       up(&disconnect_sem);
+
+       dev_info(&interface->dev, "I/O-Warror #%d now disconnected\n",
+                minor - IOWARRIOR_MINOR_BASE);
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver iowarrior_driver = {
+       .name = "iowarrior",
+       .probe = iowarrior_probe,
+       .disconnect = iowarrior_disconnect,
+       .id_table = iowarrior_ids,
+};
+
+static int __init iowarrior_init(void)
+{
+       return usb_register(&iowarrior_driver);
+}
+
+static void __exit iowarrior_exit(void)
+{
+       usb_deregister(&iowarrior_driver);
+}
+
+module_init(iowarrior_init);
+module_exit(iowarrior_exit);
index c01dfe603672987cfe6f5038fbdb59993fe2776b..b2bedd974ac3bcd9018087d3f0c6ae80106fac77 100644 (file)
@@ -1165,7 +1165,7 @@ err_dev:
        return rc;
 }
 
-void __exit mon_bin_exit(void)
+void mon_bin_exit(void)
 {
        cdev_del(&mon_bin_cdev);
        unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR);
index d38a1279d9d9b071c0a9ddf0b078f094bf80a627..494ee3b9a22634afc9d7b724655a2e4450e848d8 100644 (file)
@@ -520,7 +520,7 @@ int __init mon_text_init(void)
        return 0;
 }
 
-void __exit mon_text_exit(void)
+void mon_text_exit(void)
 {
        debugfs_remove(mon_dir);
 }
index 4f949ce8a7f3b1c9617684e0e8919944827b526c..efdfd8993d9e2fc5b217ce498fbe48eed5d0d428 100644 (file)
@@ -57,9 +57,9 @@ void mon_text_del(struct mon_bus *mbus);
 // void mon_bin_add(struct mon_bus *);
 
 int __init mon_text_init(void);
-void __exit mon_text_exit(void);
+void mon_text_exit(void);
 int __init mon_bin_init(void);
-void __exit mon_bin_exit(void);
+void mon_bin_exit(void);
 
 /*
  * DMA interface.
index 0f3d7dbb537f43dca627e3544606562c5ff07e16..3de564b2314799b98a805d017f19ca2542db53e6 100644 (file)
@@ -186,6 +186,15 @@ config USB_NET_CDCETHER
          IEEE 802 "local assignment" bit is set in the address, a "usbX"
          name is used instead.
 
+config USB_NET_DM9601
+       tristate "Davicom DM9601 based USB 1.1 10/100 ethernet devices"
+       depends on USB_USBNET
+       select CRC32
+       select USB_USBNET_MII
+       help
+         This option adds support for Davicom DM9601 based USB 1.1
+         10/100 Ethernet adapters.
+
 config USB_NET_GL620A
        tristate "GeneSys GL620USB-A based cables"
        depends on USB_USBNET
index 7b51964de171416cc614c99c80b97f3494cdf841..595a539f83840ef7b7b05d4e825bc3f91d9c2eec 100644 (file)
@@ -8,6 +8,7 @@ obj-$(CONFIG_USB_PEGASUS)       += pegasus.o
 obj-$(CONFIG_USB_RTL8150)      += rtl8150.o
 obj-$(CONFIG_USB_NET_AX8817X)  += asix.o
 obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o
+obj-$(CONFIG_USB_NET_DM9601)   += dm9601.o
 obj-$(CONFIG_USB_NET_GL620A)   += gl620a.o
 obj-$(CONFIG_USB_NET_NET1080)  += net1080.o
 obj-$(CONFIG_USB_NET_PLUSB)    += plusb.o
index 7ef2e4b5e39bf986d559854458df5d6ea5bd5257..5808ea0824594adad543d4ac90fa13488c7903fb 100644 (file)
@@ -1395,9 +1395,9 @@ static const struct usb_device_id products [] = {
        USB_DEVICE (0x07b8, 0x420a),
        .driver_info =  (unsigned long) &hawking_uf200_info,
 }, {
-        // Billionton Systems, USB2AR
-        USB_DEVICE (0x08dd, 0x90ff),
-        .driver_info =  (unsigned long) &ax8817x_info,
+       // Billionton Systems, USB2AR
+       USB_DEVICE (0x08dd, 0x90ff),
+       .driver_info =  (unsigned long) &ax8817x_info,
 }, {
        // ATEN UC210T
        USB_DEVICE (0x0557, 0x2009),
@@ -1422,10 +1422,14 @@ static const struct usb_device_id       products [] = {
        // goodway corp usb gwusb2e
        USB_DEVICE (0x1631, 0x6200),
        .driver_info = (unsigned long) &ax8817x_info,
+}, {
+       // JVC MP-PRX1 Port Replicator
+       USB_DEVICE (0x04f1, 0x3008),
+       .driver_info = (unsigned long) &ax8817x_info,
 }, {
        // ASIX AX88772 10/100
-        USB_DEVICE (0x0b95, 0x7720),
-        .driver_info = (unsigned long) &ax88772_info,
+       USB_DEVICE (0x0b95, 0x7720),
+       .driver_info = (unsigned long) &ax88772_info,
 }, {
        // ASIX AX88178 10/100/1000
        USB_DEVICE (0x0b95, 0x1780),
diff --git a/drivers/usb/net/dm9601.c b/drivers/usb/net/dm9601.c
new file mode 100644 (file)
index 0000000..4a932e1
--- /dev/null
@@ -0,0 +1,606 @@
+/*
+ * Davicom DM9601 USB 1.1 10/100Mbps ethernet devices
+ *
+ * Peter Korsgaard <jacmet@sunsite.dk>
+ *
+ * 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.
+ */
+
+//#define DEBUG
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/crc32.h>
+
+#include "usbnet.h"
+
+/* datasheet:
+ http://www.davicom.com.tw/big5/download/Data%20Sheet/DM9601-DS-P01-930914.pdf
+*/
+
+/* control requests */
+#define DM_READ_REGS   0x00
+#define DM_WRITE_REGS  0x01
+#define DM_READ_MEMS   0x02
+#define DM_WRITE_REG   0x03
+#define DM_WRITE_MEMS  0x05
+#define DM_WRITE_MEM   0x07
+
+/* registers */
+#define DM_NET_CTRL    0x00
+#define DM_RX_CTRL     0x05
+#define DM_SHARED_CTRL 0x0b
+#define DM_SHARED_ADDR 0x0c
+#define DM_SHARED_DATA 0x0d    /* low + high */
+#define DM_PHY_ADDR    0x10    /* 6 bytes */
+#define DM_MCAST_ADDR  0x16    /* 8 bytes */
+#define DM_GPR_CTRL    0x1e
+#define DM_GPR_DATA    0x1f
+
+#define DM_MAX_MCAST   64
+#define DM_MCAST_SIZE  8
+#define DM_EEPROM_LEN  256
+#define DM_TX_OVERHEAD 2       /* 2 byte header */
+#define DM_RX_OVERHEAD 7       /* 3 byte header + 4 byte crc tail */
+#define DM_TIMEOUT     1000
+
+
+static int dm_read(struct usbnet *dev, u8 reg, u16 length, void *data)
+{
+       devdbg(dev, "dm_read() reg=0x%02x length=%d", reg, length);
+       return usb_control_msg(dev->udev,
+                              usb_rcvctrlpipe(dev->udev, 0),
+                              DM_READ_REGS,
+                              USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                              0, reg, data, length, USB_CTRL_SET_TIMEOUT);
+}
+
+static int dm_read_reg(struct usbnet *dev, u8 reg, u8 *value)
+{
+       return dm_read(dev, reg, 1, value);
+}
+
+static int dm_write(struct usbnet *dev, u8 reg, u16 length, void *data)
+{
+       devdbg(dev, "dm_write() reg=0x%02x, length=%d", reg, length);
+       return usb_control_msg(dev->udev,
+                              usb_sndctrlpipe(dev->udev, 0),
+                              DM_WRITE_REGS,
+                              USB_DIR_OUT | USB_TYPE_VENDOR |USB_RECIP_DEVICE,
+                              0, reg, data, length, USB_CTRL_SET_TIMEOUT);
+}
+
+static int dm_write_reg(struct usbnet *dev, u8 reg, u8 value)
+{
+       devdbg(dev, "dm_write_reg() reg=0x%02x, value=0x%02x", reg, value);
+       return usb_control_msg(dev->udev,
+                              usb_sndctrlpipe(dev->udev, 0),
+                              DM_WRITE_REG,
+                              USB_DIR_OUT | USB_TYPE_VENDOR |USB_RECIP_DEVICE,
+                              value, reg, 0, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+static void dm_write_async_callback(struct urb *urb)
+{
+       struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
+
+       if (urb->status < 0)
+               printk(KERN_DEBUG "dm_write_async_callback() failed with %d",
+                      urb->status);
+
+       kfree(req);
+       usb_free_urb(urb);
+}
+
+static void dm_write_async(struct usbnet *dev, u8 reg, u16 length, void *data)
+{
+       struct usb_ctrlrequest *req;
+       struct urb *urb;
+       int status;
+
+       devdbg(dev, "dm_write_async() reg=0x%02x length=%d", reg, length);
+
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (!urb) {
+               deverr(dev, "Error allocating URB in dm_write_async!");
+               return;
+       }
+
+       req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
+       if (!req) {
+               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 = DM_WRITE_REGS;
+       req->wValue = 0;
+       req->wIndex = cpu_to_le16(reg);
+       req->wLength = cpu_to_le16(length);
+
+       usb_fill_control_urb(urb, dev->udev,
+                            usb_sndctrlpipe(dev->udev, 0),
+                            (void *)req, data, length,
+                            dm_write_async_callback, req);
+
+       status = usb_submit_urb(urb, GFP_ATOMIC);
+       if (status < 0) {
+               deverr(dev, "Error submitting the control message: status=%d",
+                      status);
+               kfree(req);
+               usb_free_urb(urb);
+       }
+}
+
+static void dm_write_reg_async(struct usbnet *dev, u8 reg, u8 value)
+{
+       struct usb_ctrlrequest *req;
+       struct urb *urb;
+       int status;
+
+       devdbg(dev, "dm_write_reg_async() reg=0x%02x value=0x%02x",
+              reg, value);
+
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (!urb) {
+               deverr(dev, "Error allocating URB in dm_write_async!");
+               return;
+       }
+
+       req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
+       if (!req) {
+               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 = DM_WRITE_REG;
+       req->wValue = cpu_to_le16(value);
+       req->wIndex = cpu_to_le16(reg);
+       req->wLength = 0;
+
+       usb_fill_control_urb(urb, dev->udev,
+                            usb_sndctrlpipe(dev->udev, 0),
+                            (void *)req, 0, 0, dm_write_async_callback, req);
+
+       status = usb_submit_urb(urb, GFP_ATOMIC);
+       if (status < 0) {
+               deverr(dev, "Error submitting the control message: status=%d",
+                      status);
+               kfree(req);
+               usb_free_urb(urb);
+       }
+}
+
+static int dm_read_shared_word(struct usbnet *dev, int phy, u8 reg, u16 *value)
+{
+       int ret, i;
+
+       mutex_lock(&dev->phy_mutex);
+
+       dm_write_reg(dev, DM_SHARED_ADDR, phy ? (reg | 0x40) : reg);
+       dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0xc : 0x4);
+
+       for (i = 0; i < DM_TIMEOUT; i++) {
+               u8 tmp;
+
+               udelay(1);
+               ret = dm_read_reg(dev, DM_SHARED_CTRL, &tmp);
+               if (ret < 0)
+                       goto out;
+
+               /* ready */
+               if ((tmp & 1) == 0)
+                       break;
+       }
+
+       if (i == DM_TIMEOUT) {
+               deverr(dev, "%s read timed out!", phy ? "phy" : "eeprom");
+               ret = -EIO;
+               goto out;
+       }
+
+       dm_write_reg(dev, DM_SHARED_CTRL, 0x0);
+       ret = dm_read(dev, DM_SHARED_DATA, 2, value);
+
+       devdbg(dev, "read shared %d 0x%02x returned 0x%04x, %d",
+              phy, reg, *value, ret);
+
+ out:
+       mutex_unlock(&dev->phy_mutex);
+       return ret;
+}
+
+static int dm_write_shared_word(struct usbnet *dev, int phy, u8 reg, u16 value)
+{
+       int ret, i;
+
+       mutex_lock(&dev->phy_mutex);
+
+       ret = dm_write(dev, DM_SHARED_DATA, 2, &value);
+       if (ret < 0)
+               goto out;
+
+       dm_write_reg(dev, DM_SHARED_ADDR, phy ? (reg | 0x40) : reg);
+       dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0x1c : 0x14);
+
+       for (i = 0; i < DM_TIMEOUT; i++) {
+               u8 tmp;
+
+               udelay(1);
+               ret = dm_read_reg(dev, DM_SHARED_CTRL, &tmp);
+               if (ret < 0)
+                       goto out;
+
+               /* ready */
+               if ((tmp & 1) == 0)
+                       break;
+       }
+
+       if (i == DM_TIMEOUT) {
+               deverr(dev, "%s write timed out!", phy ? "phy" : "eeprom");
+               ret = -EIO;
+               goto out;
+       }
+
+       dm_write_reg(dev, DM_SHARED_CTRL, 0x0);
+
+out:
+       mutex_unlock(&dev->phy_mutex);
+       return ret;
+}
+
+static int dm_read_eeprom_word(struct usbnet *dev, u8 offset, void *value)
+{
+       return dm_read_shared_word(dev, 0, offset, value);
+}
+
+
+
+static int dm9601_get_eeprom_len(struct net_device *dev)
+{
+       return DM_EEPROM_LEN;
+}
+
+static int dm9601_get_eeprom(struct net_device *net,
+                            struct ethtool_eeprom *eeprom, u8 * data)
+{
+       struct usbnet *dev = netdev_priv(net);
+       u16 *ebuf = (u16 *) data;
+       int i;
+
+       /* access is 16bit */
+       if ((eeprom->offset % 2) || (eeprom->len % 2))
+               return -EINVAL;
+
+       for (i = 0; i < eeprom->len / 2; i++) {
+               if (dm_read_eeprom_word(dev, eeprom->offset / 2 + i,
+                                       &ebuf[i]) < 0)
+                       return -EINVAL;
+       }
+       return 0;
+}
+
+static int dm9601_mdio_read(struct net_device *netdev, int phy_id, int loc)
+{
+       struct usbnet *dev = netdev_priv(netdev);
+
+       u16 res;
+
+       if (phy_id) {
+               devdbg(dev, "Only internal phy supported");
+               return 0;
+       }
+
+       dm_read_shared_word(dev, 1, loc, &res);
+
+       devdbg(dev,
+              "dm9601_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x",
+              phy_id, loc, le16_to_cpu(res));
+
+       return le16_to_cpu(res);
+}
+
+static void dm9601_mdio_write(struct net_device *netdev, int phy_id, int loc,
+                             int val)
+{
+       struct usbnet *dev = netdev_priv(netdev);
+       u16 res = cpu_to_le16(val);
+
+       if (phy_id) {
+               devdbg(dev, "Only internal phy supported");
+               return;
+       }
+
+       devdbg(dev,"dm9601_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x",
+              phy_id, loc, val);
+
+       dm_write_shared_word(dev, 1, loc, res);
+}
+
+static void dm9601_get_drvinfo(struct net_device *net,
+                              struct ethtool_drvinfo *info)
+{
+       /* Inherit standard device info */
+       usbnet_get_drvinfo(net, info);
+       info->eedump_len = DM_EEPROM_LEN;
+}
+
+static u32 dm9601_get_link(struct net_device *net)
+{
+       struct usbnet *dev = netdev_priv(net);
+
+       return mii_link_ok(&dev->mii);
+}
+
+static int dm9601_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);
+}
+
+static struct ethtool_ops dm9601_ethtool_ops = {
+       .get_drvinfo    = dm9601_get_drvinfo,
+       .get_link       = dm9601_get_link,
+       .get_msglevel   = usbnet_get_msglevel,
+       .set_msglevel   = usbnet_set_msglevel,
+       .get_eeprom_len = dm9601_get_eeprom_len,
+       .get_eeprom     = dm9601_get_eeprom,
+       .get_settings   = usbnet_get_settings,
+       .set_settings   = usbnet_set_settings,
+       .nway_reset     = usbnet_nway_reset,
+};
+
+static void dm9601_set_multicast(struct net_device *net)
+{
+       struct usbnet *dev = netdev_priv(net);
+       /* We use the 20 byte dev->data for our 8 byte filter buffer
+        * to avoid allocating memory that is tricky to free later */
+       u8 *hashes = (u8 *) & dev->data;
+       u8 rx_ctl = 0x01;
+
+       memset(hashes, 0x00, DM_MCAST_SIZE);
+       hashes[DM_MCAST_SIZE - 1] |= 0x80;      /* broadcast address */
+
+       if (net->flags & IFF_PROMISC) {
+               rx_ctl |= 0x02;
+       } else if (net->flags & IFF_ALLMULTI || net->mc_count > DM_MAX_MCAST) {
+               rx_ctl |= 0x04;
+       } else if (net->mc_count) {
+               struct dev_mc_list *mc_list = net->mc_list;
+               int i;
+
+               for (i = 0; i < net->mc_count; i++) {
+                       u32 crc = ether_crc(ETH_ALEN, mc_list->dmi_addr) >> 26;
+                       hashes[crc >> 3] |= 1 << (crc & 0x7);
+               }
+       }
+
+       dm_write_async(dev, DM_MCAST_ADDR, DM_MCAST_SIZE, hashes);
+       dm_write_reg_async(dev, DM_RX_CTRL, rx_ctl);
+}
+
+static int dm9601_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+       int ret;
+
+       ret = usbnet_get_endpoints(dev, intf);
+       if (ret)
+               goto out;
+
+       dev->net->do_ioctl = dm9601_ioctl;
+       dev->net->set_multicast_list = dm9601_set_multicast;
+       dev->net->ethtool_ops = &dm9601_ethtool_ops;
+       dev->net->hard_header_len += DM_TX_OVERHEAD;
+       dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
+       dev->rx_urb_size = dev->net->mtu + DM_RX_OVERHEAD;
+
+       dev->mii.dev = dev->net;
+       dev->mii.mdio_read = dm9601_mdio_read;
+       dev->mii.mdio_write = dm9601_mdio_write;
+       dev->mii.phy_id_mask = 0x1f;
+       dev->mii.reg_num_mask = 0x1f;
+
+       /* reset */
+       ret = dm_write_reg(dev, DM_NET_CTRL, 1);
+       udelay(20);
+
+       /* read MAC */
+       ret = dm_read(dev, DM_PHY_ADDR, ETH_ALEN, dev->net->dev_addr);
+       if (ret < 0) {
+               printk(KERN_ERR "Error reading MAC address\n");
+               ret = -ENODEV;
+               goto out;
+       }
+
+
+       /* power up phy */
+       dm_write_reg(dev, DM_GPR_CTRL, 1);
+       dm_write_reg(dev, DM_GPR_DATA, 0);
+
+       /* receive broadcast packets */
+       dm9601_set_multicast(dev->net);
+
+       dm9601_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+       dm9601_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+                         ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
+       mii_nway_restart(&dev->mii);
+
+out:
+       return ret;
+}
+
+static int dm9601_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+       u8 status;
+       int len;
+
+       /* format:
+          b0: rx status
+          b1: packet length (incl crc) low
+          b2: packet length (incl crc) high
+          b3..n-4: packet data
+          bn-3..bn: ethernet crc
+        */
+
+       if (unlikely(skb->len < DM_RX_OVERHEAD)) {
+               dev_err(&dev->udev->dev, "unexpected tiny rx frame\n");
+               return 0;
+       }
+
+       status = skb->data[0];
+       len = (skb->data[1] | (skb->data[2] << 8)) - 4;
+
+       if (unlikely(status & 0xbf)) {
+               if (status & 0x01) dev->stats.rx_fifo_errors++;
+               if (status & 0x02) dev->stats.rx_crc_errors++;
+               if (status & 0x04) dev->stats.rx_frame_errors++;
+               if (status & 0x20) dev->stats.rx_missed_errors++;
+               if (status & 0x90) dev->stats.rx_length_errors++;
+               return 0;
+       }
+
+       skb_pull(skb, 3);
+       skb_trim(skb, len);
+
+       return 1;
+}
+
+static struct sk_buff *dm9601_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
+                                      gfp_t flags)
+{
+       int len;
+
+       /* format:
+          b0: packet length low
+          b1: packet length high
+          b3..n: packet data
+       */
+
+       if (skb_headroom(skb) < DM_TX_OVERHEAD) {
+               struct sk_buff *skb2;
+
+               skb2 = skb_copy_expand(skb, DM_TX_OVERHEAD, 0, flags);
+               dev_kfree_skb_any(skb);
+               skb = skb2;
+               if (!skb)
+                       return NULL;
+       }
+
+       __skb_push(skb, DM_TX_OVERHEAD);
+
+       len = skb->len;
+       /* usbnet adds padding if length is a multiple of packet size
+          if so, adjust length value in header */
+       if ((len % dev->maxpacket) == 0)
+               len++;
+
+       skb->data[0] = len;
+       skb->data[1] = len >> 8;
+
+       return skb;
+}
+
+static void dm9601_status(struct usbnet *dev, struct urb *urb)
+{
+       int link;
+       u8 *buf;
+
+       /* format:
+          b0: net status
+          b1: tx status 1
+          b2: tx status 2
+          b3: rx status
+          b4: rx overflow
+          b5: rx count
+          b6: tx count
+          b7: gpr
+       */
+
+       if (urb->actual_length < 8)
+               return;
+
+       buf = urb->transfer_buffer;
+
+       link = !!(buf[0] & 0x40);
+       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 int dm9601_link_reset(struct usbnet *dev)
+{
+       struct ethtool_cmd ecmd;
+
+       mii_check_media(&dev->mii, 1, 1);
+       mii_ethtool_gset(&dev->mii, &ecmd);
+
+       devdbg(dev, "link_reset() speed: %d duplex: %d",
+              ecmd.speed, ecmd.duplex);
+
+       return 0;
+}
+
+static const struct driver_info dm9601_info = {
+       .description    = "Davicom DM9601 USB Ethernet",
+       .flags          = FLAG_ETHER,
+       .bind           = dm9601_bind,
+       .rx_fixup       = dm9601_rx_fixup,
+       .tx_fixup       = dm9601_tx_fixup,
+       .status         = dm9601_status,
+       .link_reset     = dm9601_link_reset,
+       .reset          = dm9601_link_reset,
+};
+
+static const struct usb_device_id products[] = {
+       {
+        USB_DEVICE(0x0a46, 0x9601),    /* Davicom USB-100 */
+        .driver_info = (unsigned long)&dm9601_info,
+        },
+       {},                     // END
+};
+
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver dm9601_driver = {
+       .name = "dm9601",
+       .id_table = products,
+       .probe = usbnet_probe,
+       .disconnect = usbnet_disconnect,
+       .suspend = usbnet_suspend,
+       .resume = usbnet_resume,
+};
+
+static int __init dm9601_init(void)
+{
+       return usb_register(&dm9601_driver);
+}
+
+static void __exit dm9601_exit(void)
+{
+       usb_deregister(&dm9601_driver);
+}
+
+module_init(dm9601_init);
+module_exit(dm9601_exit);
+
+MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
+MODULE_DESCRIPTION("Davicom DM9601 USB 1.1 ethernet devices");
+MODULE_LICENSE("GPL");
index 4695952b6470bd2fd8adc29366dafad35464cb51..c525b42dadde28a3630c355aaaa7834884b5620a 100644 (file)
@@ -513,6 +513,7 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13S_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) },
        { USB_DEVICE(ELEKTOR_VID, ELEKTOR_FT323R_PID) },
+       { USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) },
        { },                                    /* Optional parameter entry */
        { }                                     /* Terminating entry */
 };
index 7eff1c03ba80b37ac41b010625eb10c8951eaca3..1bdda935f7d991b34d7c812db78ea569b7e22856 100644 (file)
 #define FTDI_TACTRIX_OPENPORT_13S_PID  0xCC49  /* OpenPort 1.3 Subaru */
 #define FTDI_TACTRIX_OPENPORT_13U_PID  0xCC4A  /* OpenPort 1.3 Universal */
 
+/*
+ * Telldus Technologies
+ */
+#define TELLDUS_VID                    0x1781  /* Vendor ID */
+#define TELLDUS_TELLSTICK_PID          0x0C30  /* RF control dongle 433 MHz using FT232RL */
+
 /* Commands */
 #define FTDI_SIO_RESET                 0 /* Reset the port */
 #define FTDI_SIO_MODEM_CTRL    1 /* Set the modem control register */
index 9963a8b758401aed14435230348c8f9a07e41fab..db92a7fb1f7cbbe9782aef280d46799c7e8d7490 100644 (file)
@@ -67,50 +67,95 @@ static int  option_tiocmset(struct usb_serial_port *port, struct file *file,
 static int  option_send_setup(struct usb_serial_port *port);
 
 /* Vendor and product IDs */
-#define OPTION_VENDOR_ID                0x0AF0
-#define HUAWEI_VENDOR_ID                0x12D1
-#define NOVATELWIRELESS_VENDOR_ID       0x1410
-#define ANYDATA_VENDOR_ID               0x16d5
-
-#define OPTION_PRODUCT_OLD              0x5000
-#define OPTION_PRODUCT_FUSION           0x6000
-#define OPTION_PRODUCT_FUSION2          0x6300
-#define OPTION_PRODUCT_COBRA            0x6500
-#define OPTION_PRODUCT_COBRA2           0x6600
-#define OPTION_PRODUCT_GTMAX36          0x6701
-#define HUAWEI_PRODUCT_E600             0x1001
-#define HUAWEI_PRODUCT_E220             0x1003
-#define NOVATELWIRELESS_PRODUCT_U740    0x1400
-#define ANYDATA_PRODUCT_ID              0x6501
+#define OPTION_VENDOR_ID                       0x0AF0
+#define OPTION_PRODUCT_COLT                    0x5000
+#define OPTION_PRODUCT_RICOLA                  0x6000
+#define OPTION_PRODUCT_RICOLA_LIGHT            0x6100
+#define OPTION_PRODUCT_RICOLA_QUAD             0x6200
+#define OPTION_PRODUCT_RICOLA_QUAD_LIGHT       0x6300
+#define OPTION_PRODUCT_RICOLA_NDIS             0x6050
+#define OPTION_PRODUCT_RICOLA_NDIS_LIGHT       0x6150
+#define OPTION_PRODUCT_RICOLA_NDIS_QUAD                0x6250
+#define OPTION_PRODUCT_RICOLA_NDIS_QUAD_LIGHT  0x6350
+#define OPTION_PRODUCT_COBRA                   0x6500
+#define OPTION_PRODUCT_COBRA_BUS               0x6501
+#define OPTION_PRODUCT_VIPER                   0x6600
+#define OPTION_PRODUCT_VIPER_BUS               0x6601
+#define OPTION_PRODUCT_GT_MAX_READY            0x6701
+#define OPTION_PRODUCT_GT_MAX                  0x6711
+#define OPTION_PRODUCT_FUJI_MODEM_LIGHT                0x6721
+#define OPTION_PRODUCT_FUJI_MODEM_GT           0x6741
+#define OPTION_PRODUCT_FUJI_MODEM_EX           0x6761
+#define OPTION_PRODUCT_FUJI_NETWORK_LIGHT      0x6731
+#define OPTION_PRODUCT_FUJI_NETWORK_GT         0x6751
+#define OPTION_PRODUCT_FUJI_NETWORK_EX         0x6771
+#define OPTION_PRODUCT_KOI_MODEM               0x6800
+#define OPTION_PRODUCT_KOI_NETWORK             0x6811
+#define OPTION_PRODUCT_SCORPION_MODEM          0x6901
+#define OPTION_PRODUCT_SCORPION_NETWORK                0x6911
+#define OPTION_PRODUCT_ETNA_MODEM              0x7001
+#define OPTION_PRODUCT_ETNA_NETWORK            0x7011
+#define OPTION_PRODUCT_ETNA_MODEM_LITE         0x7021
+#define OPTION_PRODUCT_ETNA_MODEM_GT           0x7041
+#define OPTION_PRODUCT_ETNA_MODEM_EX           0x7061
+#define OPTION_PRODUCT_ETNA_NETWORK_LITE       0x7031
+#define OPTION_PRODUCT_ETNA_NETWORK_GT         0x7051
+#define OPTION_PRODUCT_ETNA_NETWORK_EX         0x7071
+#define OPTION_PRODUCT_ETNA_KOI_MODEM          0x7100
+#define OPTION_PRODUCT_ETNA_KOI_NETWORK                0x7111
+
+#define HUAWEI_VENDOR_ID                       0x12D1
+#define HUAWEI_PRODUCT_E600                    0x1001
+#define HUAWEI_PRODUCT_E220                    0x1003
+
+#define NOVATELWIRELESS_VENDOR_ID              0x1410
+#define NOVATELWIRELESS_PRODUCT_U740           0x1400
+
+#define ANYDATA_VENDOR_ID                      0x16d5
+#define ANYDATA_PRODUCT_ID                     0x6501
 
 static struct usb_device_id option_ids[] = {
-       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
-       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
-       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_LIGHT) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD_LIGHT) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_LIGHT) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_QUAD) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_QUAD_LIGHT) },
        { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
-       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) },
-       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GTMAX36) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA_BUS) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_VIPER) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_VIPER_BUS) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GT_MAX_READY) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GT_MAX) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_LIGHT) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_GT) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_EX) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_LIGHT) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_GT) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_EX) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_KOI_MODEM) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_KOI_NETWORK) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_SCORPION_MODEM) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_SCORPION_NETWORK) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_LITE) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_GT) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_EX) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_LITE) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_GT) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_EX) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_MODEM) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_NETWORK) },
        { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
        { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220) },
        { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
        { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) },
        { } /* Terminating entry */
 };
-
-static struct usb_device_id option_ids1[] = {
-       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
-       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
-       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
-       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
-       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) },
-       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GTMAX36) },
-       { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
-       { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220) },
-       { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
-       { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) },
-       { } /* Terminating entry */
-};
-
 MODULE_DEVICE_TABLE(usb, option_ids);
 
 static struct usb_driver option_driver = {
@@ -132,7 +177,7 @@ static struct usb_serial_driver option_1port_device = {
        },
        .description       = "GSM modem (1-port)",
        .usb_driver        = &option_driver,
-       .id_table          = option_ids1,
+       .id_table          = option_ids,
        .num_interrupt_in  = NUM_DONT_CARE,
        .num_bulk_in       = NUM_DONT_CARE,
        .num_bulk_out      = NUM_DONT_CARE,
index 98b0910ad80c43795456d56bd061966499bf7fa7..8d4d839a9d887eb4a753da824e55a98542b26f21 100644 (file)
@@ -501,6 +501,30 @@ int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
 }
 
 
+/**
+ * sysfs_add_file_to_group - add an attribute file to a pre-existing group.
+ * @kobj: object we're acting for.
+ * @attr: attribute descriptor.
+ * @group: group name.
+ */
+int sysfs_add_file_to_group(struct kobject *kobj,
+               const struct attribute *attr, const char *group)
+{
+       struct dentry *dir;
+       int error;
+
+       dir = lookup_one_len(group, kobj->dentry, strlen(group));
+       if (IS_ERR(dir))
+               error = PTR_ERR(dir);
+       else {
+               error = sysfs_add_file(dir, attr, SYSFS_KOBJ_ATTR);
+               dput(dir);
+       }
+       return error;
+}
+EXPORT_SYMBOL_GPL(sysfs_add_file_to_group);
+
+
 /**
  * sysfs_update_file - update the modified timestamp on an object attribute.
  * @kobj: object we're acting for.
@@ -586,6 +610,26 @@ void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr)
 }
 
 
+/**
+ * sysfs_remove_file_from_group - remove an attribute file from a group.
+ * @kobj: object we're acting for.
+ * @attr: attribute descriptor.
+ * @group: group name.
+ */
+void sysfs_remove_file_from_group(struct kobject *kobj,
+               const struct attribute *attr, const char *group)
+{
+       struct dentry *dir;
+
+       dir = lookup_one_len(group, kobj->dentry, strlen(group));
+       if (!IS_ERR(dir)) {
+               sysfs_hash_and_remove(dir, attr->name);
+               dput(dir);
+       }
+}
+EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);
+
+
 EXPORT_SYMBOL_GPL(sysfs_create_file);
 EXPORT_SYMBOL_GPL(sysfs_remove_file);
 EXPORT_SYMBOL_GPL(sysfs_update_file);
index 192de3afa96ba9244a5e69e98f0e66b17fc6df66..f45450b295c071c5f5e4511349c1b22bea18a034 100644 (file)
@@ -126,6 +126,11 @@ void sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr);
 int __must_check sysfs_create_group(struct kobject *,
                                        const struct attribute_group *);
 void sysfs_remove_group(struct kobject *, const struct attribute_group *);
+int sysfs_add_file_to_group(struct kobject *kobj,
+               const struct attribute *attr, const char *group);
+void sysfs_remove_file_from_group(struct kobject *kobj,
+               const struct attribute *attr, const char *group);
+
 void sysfs_notify(struct kobject * k, char *dir, char *attr);
 
 
@@ -210,6 +215,18 @@ static inline void sysfs_remove_group(struct kobject * k, const struct attribute
        ;
 }
 
+static inline int sysfs_add_file_to_group(struct kobject *kobj,
+               const struct attribute *attr, const char *group)
+{
+       return 0;
+}
+
+static inline void sysfs_remove_file_from_group(struct kobject *kobj,
+               const struct attribute *attr, const char *group);
+{
+       ;
+}
+
 static inline void sysfs_notify(struct kobject * k, char *dir, char *attr)
 {
 }
index a8e8d1ecebb17b4bbcd6e36c93d18947e78624b1..87dc75a6cee14e00a755cc49f9dd50e567373e19 100644 (file)
@@ -388,10 +388,14 @@ struct usb_device {
        struct usb_device *children[USB_MAXCHILDREN];
 
        int pm_usage_cnt;               /* usage counter for autosuspend */
+       u32 quirks;                     /* quirks of the whole device */
+
 #ifdef CONFIG_PM
        struct delayed_work autosuspend; /* for delayed autosuspends */
        struct mutex pm_mutex;          /* protects PM operations */
 
+       unsigned autosuspend_delay;     /* in jiffies */
+
        unsigned auto_pm:1;             /* autosuspend/resume in progress */
        unsigned do_remote_wakeup:1;    /* remote wakeup should be enabled */
 #endif
index 245c72531228346982598506248d68fa03d16461..1122a6c2c1a3dc3759c1dee7ef6988f24240dee3 100644 (file)
@@ -1,8 +1,9 @@
 /*
- * This file holds USB constants and structures that are needed for USB
- * device APIs.  These are used by the USB device model, which is defined
- * in chapter 9 of the USB 2.0 specification.  Linux has several APIs in C
- * that need these:
+ * This file holds USB constants and structures that are needed for
+ * USB device APIs.  These are used by the USB device model, which is
+ * defined in chapter 9 of the USB 2.0 specification and in the
+ * Wireless USB 1.0 (spread around).  Linux has several APIs in C that
+ * need these:
  *
  * - the master/host side Linux-USB kernel driver API;
  * - the "usbfs" user space API; and
  *
  * There's also "Wireless USB", using low power short range radios for
  * peripheral interconnection but otherwise building on the USB framework.
+ *
+ * Note all descriptors are declared '__attribute__((packed))' so that:
+ *
+ * [a] they never get padded, either internally (USB spec writers
+ *     probably handled that) or externally;
+ *
+ * [b] so that accessing bigger-than-a-bytes fields will never
+ *     generate bus errors on any platform, even when the location of
+ *     its descriptor inside a bundle isn't "naturally aligned", and
+ *
+ * [c] for consistency, removing all doubt even when it appears to
+ *     someone that the two other points are non-issues for that
+ *     particular descriptor type.
  */
 
 #ifndef __LINUX_USB_CH9_H
diff --git a/include/linux/usb/iowarrior.h b/include/linux/usb/iowarrior.h
new file mode 100644 (file)
index 0000000..cbbe020
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef _IOWARRIOR_H_
+#define _IOWARRIOR_H_
+
+#define CODEMERCS_MAGIC_NUMBER 0xC0    /* like COde Mercenaries */
+
+/* Define the ioctl commands for reading and writing data */
+#define IOW_WRITE      _IOW(CODEMERCS_MAGIC_NUMBER, 1, __u8 *)
+#define IOW_READ       _IOW(CODEMERCS_MAGIC_NUMBER, 2, __u8 *)
+
+/*
+   A struct for available device info which is read
+   with the ioctl IOW_GETINFO.
+   To be compatible with 2.4 userspace which didn't have an easy way to get
+   this information.
+*/
+struct iowarrior_info {
+       __u32 vendor;           /* vendor id : supposed to be USB_VENDOR_ID_CODEMERCS in all cases */
+       __u32 product;          /* product id : depends on type of chip (USB_DEVICE_ID_CODEMERCS_XXXXX) */
+       __u8 serial[9];         /* the serial number of our chip (if a serial-number is not available this is empty string) */
+       __u32 revision;         /* revision number of the chip */
+       __u32 speed;            /* USB-speed of the device (0=UNKNOWN, 1=LOW, 2=FULL 3=HIGH) */
+       __u32 power;            /* power consumption of the device in mA */
+       __u32 if_num;           /* the number of the endpoint */
+       __u32 report_size;      /* size of the data-packets on this interface */
+};
+
+/*
+  Get some device-information (product-id , serial-number etc.)
+  in order to identify a chip.
+*/
+#define IOW_GETINFO _IOR(CODEMERCS_MAGIC_NUMBER, 3, struct iowarrior_info)
+
+#endif  /* _IOWARRIOR_H_ */
diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
new file mode 100644 (file)
index 0000000..6bac8fa
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * This file holds the definitions of quirks found in USB devices.
+ * Only quirks that affect the whole device, not an interface,
+ * belong here.
+ */
+
+/* device must not be autosuspended */
+#define USB_QUIRK_NO_AUTOSUSPEND       0x00000001
+
+/* string descriptors must not be fetched using a 255-byte read */
+#define USB_QUIRK_STRING_FETCH_255     0x00000002