diff options
Diffstat (limited to 'drivers/usb')
65 files changed, 2815 insertions, 492 deletions
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 825bf884537a..8b7ff467d262 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_USB_SERIAL) += serial/ obj-$(CONFIG_USB_ADUTUX) += misc/ obj-$(CONFIG_USB_APPLEDISPLAY) += misc/ obj-$(CONFIG_USB_AUERSWALD) += misc/ +obj-$(CONFIG_USB_BERRY_CHARGE) += misc/ obj-$(CONFIG_USB_CYPRESS_CY7C63)+= misc/ obj-$(CONFIG_USB_CYTHERM) += misc/ obj-$(CONFIG_USB_EMI26) += misc/ diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index dae4ef1e8fe5..4973e147bc79 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -61,6 +61,7 @@ #include <linux/usb.h> #include <linux/firmware.h> #include <linux/ctype.h> +#include <linux/sched.h> #include <linux/kthread.h> #include <linux/version.h> #include <linux/mutex.h> diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 98199628e394..d38a25f36ea5 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -326,10 +326,16 @@ static void acm_rx_tasklet(unsigned long _acm) struct tty_struct *tty = acm->tty; struct acm_ru *rcv; unsigned long flags; - int i = 0; + unsigned char throttled; dbg("Entering acm_rx_tasklet"); - if (!ACM_READY(acm) || acm->throttle) + if (!ACM_READY(acm)) + return; + + spin_lock(&acm->throttle_lock); + throttled = acm->throttle; + spin_unlock(&acm->throttle_lock); + if (throttled) return; next_buffer: @@ -346,22 +352,20 @@ next_buffer: dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size); tty_buffer_request_room(tty, buf->size); - if (!acm->throttle) + spin_lock(&acm->throttle_lock); + throttled = acm->throttle; + spin_unlock(&acm->throttle_lock); + if (!throttled) tty_insert_flip_string(tty, buf->base, buf->size); tty_flip_buffer_push(tty); - spin_lock(&acm->throttle_lock); - if (acm->throttle) { - dbg("Throtteling noticed"); - memmove(buf->base, buf->base + i, buf->size - i); - buf->size -= i; - spin_unlock(&acm->throttle_lock); + if (throttled) { + dbg("Throttling noticed"); spin_lock_irqsave(&acm->read_lock, flags); list_add(&buf->list, &acm->filled_read_bufs); spin_unlock_irqrestore(&acm->read_lock, flags); return; } - spin_unlock(&acm->throttle_lock); spin_lock_irqsave(&acm->read_lock, flags); list_add(&buf->list, &acm->spare_read_bufs); @@ -467,7 +471,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) goto bail_out; } - if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS)) + if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) && + (acm->ctrl_caps & USB_CDC_CAP_LINE)) goto full_bailout; INIT_LIST_HEAD(&acm->spare_read_urbs); @@ -480,6 +485,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) list_add(&(acm->rb[i].list), &acm->spare_read_bufs); } + acm->throttle = 0; + tasklet_schedule(&acm->urb_task); done: @@ -1092,6 +1099,10 @@ static struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */ .driver_info = SINGLE_RX_URB, /* firmware bug */ }, + { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, + /* control interfaces with various AT-command sets */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_ACM_PROTO_AT_V25TER) }, diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index 34e9bac319b4..b6078706fb93 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -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 diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index a47c30b2d764..aefc7987120d 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -604,10 +604,6 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct lock_kernel(); if (!st) { st = kmalloc(sizeof(struct usb_device_status), GFP_KERNEL); - if (!st) { - unlock_kernel(); - return POLLIN; - } /* we may have dropped BKL - need to check for having lost the race */ if (file->private_data) { @@ -615,6 +611,11 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct st = file->private_data; goto lost_race; } + /* we haven't lost - check for allocation failure now */ + if (!st) { + unlock_kernel(); + return POLLIN; + } /* * need to prevent the module from being unloaded, since diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 2087766f9e88..274f14f1633e 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -857,11 +857,11 @@ static int proc_setintf(struct dev_state *ps, void __user *arg) static int proc_setconfig(struct dev_state *ps, void __user *arg) { - unsigned int u; + int u; int status = 0; struct usb_host_config *actconfig; - if (get_user(u, (unsigned int __user *)arg)) + if (get_user(u, (int __user *)arg)) return -EFAULT; actconfig = ps->dev->actconfig; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 600d1bc8272a..9e3e943f313c 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -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. */ @@ -743,6 +752,7 @@ EXPORT_SYMBOL_GPL(usb_deregister_device_driver); * usb_register_driver - register a USB interface driver * @new_driver: USB operations for the interface driver * @owner: module owner of this driver. + * @mod_name: module name string * * Registers a USB interface driver with the USB core. The list of * unattached interfaces will be rescanned whenever a new driver is @@ -953,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]; @@ -981,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 @@ -1176,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 +1225,26 @@ void usb_autosuspend_device(struct usb_device *udev) } /** + * 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 * @@ -1260,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; diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 5e628ae3aec7..e0ec7045e865 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -229,7 +229,7 @@ static int init_endpoint_class(void) kref_init(&ep_class->kref); ep_class->class = class_create(THIS_MODULE, "usb_endpoint"); if (IS_ERR(ep_class->class)) { - result = IS_ERR(ep_class->class); + result = PTR_ERR(ep_class->class); goto class_create_error; } diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index b531a4fd30c2..9bbcb20e2d94 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -184,7 +184,7 @@ static void generic_disconnect(struct usb_device *udev) /* if this is only an unbind, not a physical disconnect, then * unconfigure the device */ if (udev->actconfig) - usb_set_configuration(udev, 0); + usb_set_configuration(udev, -1); usb_remove_sysfs_dev_files(udev); } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 590ec82d0515..41400743ce2c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -44,6 +44,7 @@ struct usb_hub { struct usb_hub_status hub; struct usb_port_status port; } *status; /* buffer for status reports */ + struct mutex status_mutex; /* for the status buffer */ int error; /* last reported error */ int nerrors; /* track consecutive errors */ @@ -535,6 +536,7 @@ static int hub_hub_status(struct usb_hub *hub, { int ret; + mutex_lock(&hub->status_mutex); ret = get_hub_status(hub->hdev, &hub->status->hub); if (ret < 0) dev_err (hub->intfdev, @@ -544,6 +546,7 @@ static int hub_hub_status(struct usb_hub *hub, *change = le16_to_cpu(hub->status->hub.wHubChange); ret = 0; } + mutex_unlock(&hub->status_mutex); return ret; } @@ -617,6 +620,7 @@ static int hub_configure(struct usb_hub *hub, ret = -ENOMEM; goto fail; } + mutex_init(&hub->status_mutex); hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL); if (!hub->descriptor) { @@ -1283,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", @@ -1396,6 +1403,7 @@ static int hub_port_status(struct usb_hub *hub, int port1, { int ret; + mutex_lock(&hub->status_mutex); ret = get_port_status(hub->hdev, port1, &hub->status->port); if (ret < 4) { dev_err (hub->intfdev, @@ -1407,6 +1415,7 @@ static int hub_port_status(struct usb_hub *hub, int port1, *change = le16_to_cpu(hub->status->port.wPortChange); ret = 0; } + mutex_unlock(&hub->status_mutex); return ret; } @@ -1904,6 +1913,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) struct usb_hub *hub = usb_get_intfdata (intf); struct usb_device *hdev = hub->hdev; unsigned port1; + int status = 0; /* fail if children aren't already suspended */ for (port1 = 1; port1 <= hdev->maxchild; port1++) { @@ -1927,24 +1937,18 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) dev_dbg(&intf->dev, "%s\n", __FUNCTION__); + /* stop khubd and related activity */ + hub_quiesce(hub); + /* "global suspend" of the downstream HC-to-USB interface */ if (!hdev->parent) { - struct usb_bus *bus = hdev->bus; - if (bus) { - int status = hcd_bus_suspend (bus); - - if (status != 0) { - dev_dbg(&hdev->dev, "'global' suspend %d\n", - status); - return status; - } - } else - return -EOPNOTSUPP; + status = hcd_bus_suspend(hdev->bus); + if (status != 0) { + dev_dbg(&hdev->dev, "'global' suspend %d\n", status); + hub_activate(hub); + } } - - /* stop khubd and related activity */ - hub_quiesce(hub); - return 0; + return status; } static int hub_resume(struct usb_interface *intf) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 8aca3574c2b5..2f17468b5c1e 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -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 */ @@ -1316,6 +1320,14 @@ static void release_interface(struct device *dev) * use this kind of configurability; many devices only have one * configuration. * + * @configuration is the value of the configuration to be installed. + * According to the USB spec (e.g. section 9.1.1.5), configuration values + * must be non-zero; a value of zero indicates that the device in + * unconfigured. However some devices erroneously use 0 as one of their + * configuration values. To help manage such devices, this routine will + * accept @configuration = -1 as indicating the device should be put in + * an unconfigured state. + * * USB device configurations may affect Linux interoperability, * power consumption and the functionality available. For example, * the default configuration is limited to using 100mA of bus power, @@ -1347,10 +1359,15 @@ int usb_set_configuration(struct usb_device *dev, int configuration) struct usb_interface **new_interfaces = NULL; int n, nintf; - for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { - if (dev->config[i].desc.bConfigurationValue == configuration) { - cp = &dev->config[i]; - break; + if (configuration == -1) + configuration = 0; + else { + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { + if (dev->config[i].desc.bConfigurationValue == + configuration) { + cp = &dev->config[i]; + break; + } } } if ((!cp && configuration != 0)) @@ -1359,6 +1376,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration) /* The USB spec says configuration 0 means unconfigured. * But if a device includes a configuration numbered 0, * we will accept it as a correctly configured state. + * Use -1 if you really want to unconfigure the device. */ if (cp && configuration == 0) dev_warn(&dev->dev, "config 0 descriptor??\n"); diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h index 627a5a2fc9cf..7f31a495a25d 100644 --- a/drivers/usb/core/otg_whitelist.h +++ b/drivers/usb/core/otg_whitelist.h @@ -31,7 +31,7 @@ static struct usb_device_id whitelist_table [] = { { USB_DEVICE_INFO(7, 1, 3) }, #endif -#ifdef CONFIG_USB_CDCETHER +#ifdef CONFIG_USB_NET_CDCETHER /* Linux-USB CDC Ethernet gadget */ { USB_DEVICE(0x0525, 0xa4a1), }, /* Linux-USB CDC Ethernet + RNDIS gadget */ diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c new file mode 100644 index 000000000000..0e5c646cb4f6 --- /dev/null +++ b/drivers/usb/core/quirks.c @@ -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); +} diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 4eaa0ee8e72f..311d5df80386 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -63,7 +63,7 @@ set_bConfigurationValue(struct device *dev, struct device_attribute *attr, struct usb_device *udev = to_usb_device(dev); int config, value; - if (sscanf(buf, "%u", &config) != 1 || config > 255) + if (sscanf(buf, "%d", &config) != 1 || config < -1 || config > 255) return -EINVAL; usb_lock_device(udev); value = usb_set_configuration(udev, config); @@ -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); } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 3db721cd557a..54b42ce311c1 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -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; } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 17830a81be14..08b5a04e3755 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -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; diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 36b36e0175fc..a4677802fb20 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -784,7 +784,7 @@ static int at91_ep_set_halt(struct usb_ep *_ep, int value) return status; } -static struct usb_ep_ops at91_ep_ops = { +static const struct usb_ep_ops at91_ep_ops = { .enable = at91_ep_enable, .disable = at91_ep_disable, .alloc_request = at91_ep_alloc_request, @@ -912,7 +912,7 @@ static void pullup(struct at91_udc *udc, int is_on) at91_udp_write(udc, AT91_UDP_TXVC, 0); if (cpu_is_at91rm9200()) at91_set_gpio_value(udc->board.pullup_pin, 1); - else if (cpu_is_at91sam9260()) { + else if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) { u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC); txvc |= AT91_UDP_TXVC_PUON; @@ -929,7 +929,7 @@ static void pullup(struct at91_udc *udc, int is_on) at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS); if (cpu_is_at91rm9200()) at91_set_gpio_value(udc->board.pullup_pin, 0); - else if (cpu_is_at91sam9260()) { + else if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) { u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC); txvc &= ~AT91_UDP_TXVC_PUON; @@ -1651,7 +1651,7 @@ static void at91udc_shutdown(struct platform_device *dev) pullup(platform_get_drvdata(dev), 0); } -static int __devinit at91udc_probe(struct platform_device *pdev) +static int __init at91udc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct at91_udc *udc; @@ -1762,7 +1762,7 @@ fail0: return retval; } -static int __devexit at91udc_remove(struct platform_device *pdev) +static int __exit at91udc_remove(struct platform_device *pdev) { struct at91_udc *udc = platform_get_drvdata(pdev); struct resource *res; @@ -1836,8 +1836,7 @@ static int at91udc_resume(struct platform_device *pdev) #endif static struct platform_driver at91_udc = { - .probe = at91udc_probe, - .remove = __devexit_p(at91udc_remove), + .remove = __exit_p(at91udc_remove), .shutdown = at91udc_shutdown, .suspend = at91udc_suspend, .resume = at91udc_resume, @@ -1847,13 +1846,13 @@ static struct platform_driver at91_udc = { }, }; -static int __devinit udc_init_module(void) +static int __init udc_init_module(void) { - return platform_driver_register(&at91_udc); + return platform_driver_probe(&at91_udc, at91udc_probe); } module_init(udc_init_module); -static void __devexit udc_exit_module(void) +static void __exit udc_exit_module(void) { platform_driver_unregister(&at91_udc); } diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 34296e79edcf..188c74a95216 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -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; } diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c index 27904a56494b..f01890dc8751 100644 --- a/drivers/usb/gadget/pxa2xx_udc.c +++ b/drivers/usb/gadget/pxa2xx_udc.c @@ -155,7 +155,7 @@ static int is_vbus_present(void) struct pxa2xx_udc_mach_info *mach = the_controller->mach; if (mach->gpio_vbus) - return pxa_gpio_get(mach->gpio_vbus); + return udc_gpio_get(mach->gpio_vbus); if (mach->udc_is_connected) return mach->udc_is_connected(); return 1; @@ -167,7 +167,7 @@ static void pullup_off(void) struct pxa2xx_udc_mach_info *mach = the_controller->mach; if (mach->gpio_pullup) - pxa_gpio_set(mach->gpio_pullup, 0); + udc_gpio_set(mach->gpio_pullup, 0); else if (mach->udc_command) mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); } @@ -177,7 +177,7 @@ static void pullup_on(void) struct pxa2xx_udc_mach_info *mach = the_controller->mach; if (mach->gpio_pullup) - pxa_gpio_set(mach->gpio_pullup, 1); + udc_gpio_set(mach->gpio_pullup, 1); else if (mach->udc_command) mach->udc_command(PXA2XX_UDC_CMD_CONNECT); } @@ -1755,7 +1755,7 @@ lubbock_vbus_irq(int irq, void *_dev) static irqreturn_t udc_vbus_irq(int irq, void *_dev) { struct pxa2xx_udc *dev = _dev; - int vbus = pxa_gpio_get(dev->mach->gpio_vbus); + int vbus = udc_gpio_get(dev->mach->gpio_vbus); pxa2xx_udc_vbus_session(&dev->gadget, vbus); return IRQ_HANDLED; @@ -2545,15 +2545,13 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->mach = pdev->dev.platform_data; if (dev->mach->gpio_vbus) { - vbus_irq = IRQ_GPIO(dev->mach->gpio_vbus & GPIO_MD_MASK_NR); - pxa_gpio_mode((dev->mach->gpio_vbus & GPIO_MD_MASK_NR) - | GPIO_IN); + udc_gpio_init_vbus(dev->mach->gpio_vbus); + vbus_irq = udc_gpio_to_irq(dev->mach->gpio_vbus); set_irq_type(vbus_irq, IRQT_BOTHEDGE); } else vbus_irq = 0; if (dev->mach->gpio_pullup) - pxa_gpio_mode((dev->mach->gpio_pullup & GPIO_MD_MASK_NR) - | GPIO_OUT | GPIO_DFLT_LOW); + udc_gpio_init_pullup(dev->mach->gpio_pullup); init_timer(&dev->timer); dev->timer.function = udc_watchdog; diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h index 8e598c8bf4e3..773e549aff3f 100644 --- a/drivers/usb/gadget/pxa2xx_udc.h +++ b/drivers/usb/gadget/pxa2xx_udc.h @@ -177,21 +177,6 @@ struct pxa2xx_udc { static struct pxa2xx_udc *the_controller; -static inline int pxa_gpio_get(unsigned gpio) -{ - return (GPLR(gpio) & GPIO_bit(gpio)) != 0; -} - -static inline void pxa_gpio_set(unsigned gpio, int is_on) -{ - int mask = GPIO_bit(gpio); - - if (is_on) - GPSR(gpio) = mask; - else - GPCR(gpio) = mask; -} - /*-------------------------------------------------------------------------*/ /* diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index e6c19aa4bef3..e552668d36b3 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -1699,6 +1699,7 @@ static int gs_setup_class(struct usb_gadget *gadget, memcpy(&port->port_line_coding, req->buf, ret); spin_unlock(&port->port_lock); } + ret = 0; break; case USB_CDC_REQ_GET_LINE_CODING: diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 246afea9e83b..43eddaecc3dd 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -322,7 +322,7 @@ static inline void remove_debug_files (struct ehci_hcd *bus) { } #else -/* troubleshooting help: expose state in driverfs */ +/* troubleshooting help: expose state in sysfs */ #define speed_char(info1) ({ char tmp; \ switch (info1 & (3 << 12)) { \ diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 185721dba42b..a74056488234 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -42,6 +42,9 @@ #include <asm/irq.h> #include <asm/system.h> #include <asm/unaligned.h> +#ifdef CONFIG_PPC_PS3 +#include <asm/firmware.h> +#endif /*-------------------------------------------------------------------------*/ @@ -299,6 +302,19 @@ static void ehci_watchdog (unsigned long param) spin_unlock_irqrestore (&ehci->lock, flags); } +/* On some systems, leaving remote wakeup enabled prevents system shutdown. + * The firmware seems to think that powering off is a wakeup event! + * This routine turns off remote wakeup and everything else, on all ports. + */ +static void ehci_turn_off_all_ports(struct ehci_hcd *ehci) +{ + int port = HCS_N_PORTS(ehci->hcs_params); + + while (port--) + ehci_writel(ehci, PORT_RWC_BITS, + &ehci->regs->port_status[port]); +} + /* ehci_shutdown kick in for silicon on any bus (not just pci, etc). * This forcibly disables dma and IRQs, helping kexec and other cases * where the next system software may expect clean state. @@ -310,9 +326,13 @@ ehci_shutdown (struct usb_hcd *hcd) ehci = hcd_to_ehci (hcd); (void) ehci_halt (ehci); + ehci_turn_off_all_ports(ehci); /* make BIOS/etc use companion controller during reboot */ ehci_writel(ehci, 0, &ehci->regs->configured_flag); + + /* unblock posted writes */ + ehci_readl(ehci, &ehci->regs->configured_flag); } static void ehci_port_power (struct ehci_hcd *ehci, int is_on) @@ -951,15 +971,18 @@ static int __init ehci_hcd_init(void) #endif #ifdef PS3_SYSTEM_BUS_DRIVER - retval = ps3_system_bus_driver_register(&PS3_SYSTEM_BUS_DRIVER); - if (retval < 0) { + if (firmware_has_feature(FW_FEATURE_PS3_LV1)) { + retval = ps3_system_bus_driver_register( + &PS3_SYSTEM_BUS_DRIVER); + if (retval < 0) { #ifdef PLATFORM_DRIVER - platform_driver_unregister(&PLATFORM_DRIVER); + platform_driver_unregister(&PLATFORM_DRIVER); #endif #ifdef PCI_DRIVER - pci_unregister_driver(&PCI_DRIVER); + pci_unregister_driver(&PCI_DRIVER); #endif - return retval; + return retval; + } } #endif @@ -976,7 +999,8 @@ static void __exit ehci_hcd_cleanup(void) pci_unregister_driver(&PCI_DRIVER); #endif #ifdef PS3_SYSTEM_BUS_DRIVER - ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); + if (firmware_has_feature(FW_FEATURE_PS3_LV1)) + ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); #endif } module_exit(ehci_hcd_cleanup); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 0d83c6df1a3b..9af529d22b3e 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -36,6 +36,8 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) int port; int mask; + ehci_dbg(ehci, "suspend root hub\n"); + if (time_before (jiffies, ehci->next_statechange)) msleep(5); diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 2718b5dc4ec1..46873f2534b5 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1577,7 +1577,7 @@ static int isp116x_remove(struct platform_device *pdev) #define resource_len(r) (((r)->end - (r)->start) + 1) -static int __init isp116x_probe(struct platform_device *pdev) +static int __devinit isp116x_probe(struct platform_device *pdev) { struct usb_hcd *hcd; struct isp116x *isp116x; diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 930346487278..d849c809acbd 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -18,19 +18,38 @@ #include <asm/mach-types.h> #include <asm/hardware.h> #include <asm/arch/board.h> +#include <asm/arch/cpu.h> #ifndef CONFIG_ARCH_AT91 #error "CONFIG_ARCH_AT91 must be defined." #endif -/* interface and function clocks */ -static struct clk *iclk, *fclk; +/* interface and function clocks; sometimes also an AHB clock */ +static struct clk *iclk, *fclk, *hclk; static int clocked; extern int usb_disabled(void); /*-------------------------------------------------------------------------*/ +static void at91_start_clock(void) +{ + if (cpu_is_at91sam9261()) + clk_enable(hclk); + clk_enable(iclk); + clk_enable(fclk); + clocked = 1; +} + +static void at91_stop_clock(void) +{ + clk_disable(fclk); + clk_disable(iclk); + if (cpu_is_at91sam9261()) + clk_disable(hclk); + clocked = 0; +} + static void at91_start_hc(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); @@ -41,9 +60,7 @@ static void at91_start_hc(struct platform_device *pdev) /* * Start the USB clocks. */ - clk_enable(iclk); - clk_enable(fclk); - clocked = 1; + at91_start_clock(); /* * The USB host controller must remain in reset. @@ -66,9 +83,7 @@ static void at91_stop_hc(struct platform_device *pdev) /* * Stop the USB clocks. */ - clk_disable(fclk); - clk_disable(iclk); - clocked = 0; + at91_stop_clock(); } @@ -126,6 +141,8 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, iclk = clk_get(&pdev->dev, "ohci_clk"); fclk = clk_get(&pdev->dev, "uhpck"); + if (cpu_is_at91sam9261()) + hclk = clk_get(&pdev->dev, "hck0"); at91_start_hc(pdev); ohci_hcd_init(hcd_to_ohci(hcd)); @@ -137,6 +154,8 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, /* Error handling */ at91_stop_hc(pdev); + if (cpu_is_at91sam9261()) + clk_put(hclk); clk_put(fclk); clk_put(iclk); @@ -171,9 +190,11 @@ static int usb_hcd_at91_remove(struct usb_hcd *hcd, iounmap(hcd->regs); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + if (cpu_is_at91sam9261()) + clk_put(hclk); clk_put(fclk); clk_put(iclk); - fclk = iclk = NULL; + fclk = iclk = hclk = NULL; dev_set_drvdata(&pdev->dev, NULL); return 0; @@ -280,9 +301,7 @@ ohci_hcd_at91_drv_suspend(struct platform_device *pdev, pm_message_t mesg) */ if (at91_suspend_entering_slow_clock()) { ohci_usb_reset (ohci); - clk_disable(fclk); - clk_disable(iclk); - clocked = 0; + at91_stop_clock(); } return 0; @@ -295,11 +314,8 @@ static int ohci_hcd_at91_drv_resume(struct platform_device *pdev) if (device_may_wakeup(&pdev->dev)) disable_irq_wake(hcd->irq); - if (!clocked) { - clk_enable(iclk); - clk_enable(fclk); - clocked = 1; - } + if (!clocked) + at91_start_clock(); return 0; } diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index fa6a7ceaa0db..f0d29eda3c6d 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -42,6 +42,9 @@ #include <asm/system.h> #include <asm/unaligned.h> #include <asm/byteorder.h> +#ifdef CONFIG_PPC_PS3 +#include <asm/firmware.h> +#endif #include "../core/hcd.h" @@ -944,9 +947,12 @@ static int __init ohci_hcd_mod_init(void) sizeof (struct ed), sizeof (struct td)); #ifdef PS3_SYSTEM_BUS_DRIVER - retval = ps3_system_bus_driver_register(&PS3_SYSTEM_BUS_DRIVER); - if (retval < 0) - goto error_ps3; + if (firmware_has_feature(FW_FEATURE_PS3_LV1)) { + retval = ps3_system_bus_driver_register( + &PS3_SYSTEM_BUS_DRIVER); + if (retval < 0) + goto error_ps3; + } #endif #ifdef PLATFORM_DRIVER @@ -992,7 +998,8 @@ static int __init ohci_hcd_mod_init(void) error_platform: #endif #ifdef PS3_SYSTEM_BUS_DRIVER - ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); + if (firmware_has_feature(FW_FEATURE_PS3_LV1)) + ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); error_ps3: #endif return retval; @@ -1014,7 +1021,8 @@ static void __exit ohci_hcd_mod_exit(void) platform_driver_unregister(&PLATFORM_DRIVER); #endif #ifdef PS3_SYSTEM_BUS_DRIVER - ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); + if (firmware_has_feature(FW_FEATURE_PS3_LV1)) + ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); #endif } module_exit(ohci_hcd_mod_exit); diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index 5d6c06bc4524..8d24d3dc0a61 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -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; diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index ded4df30a631..44da4334f1d6 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -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 diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 74469b5bcb61..1b3d23406ac4 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -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 diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 68e66b33e726..f4ebdb3e488f 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -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; diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig index 2e71d3cca198..69a9f3b6d0a9 100644 --- a/drivers/usb/input/Kconfig +++ b/drivers/usb/input/Kconfig @@ -58,13 +58,17 @@ config HID_PID devices. config LOGITECH_FF - bool "Logitech WingMan *3D support" + bool "Logitech devices support" depends on HID_FF select INPUT_FF_MEMLESS if USB_HID help Say Y here if you have one of these devices: - Logitech WingMan Cordless RumblePad + - Logitech WingMan Cordless RumblePad 2 - Logitech WingMan Force 3D + - Logitech Formula Force EX + - Logitech MOMO Force wheel + and if you want to enable force feedback for them. Note: if you say N here, this device will still be supported, but without force feedback. diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index 4d8ed3d71a15..827a75a186ba 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -4,7 +4,7 @@ * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc - * Copyright (c) 2006 Jiri Kosina + * Copyright (c) 2006-2007 Jiri Kosina */ /* @@ -27,9 +27,6 @@ #include <linux/input.h> #include <linux/wait.h> -#undef DEBUG -#undef DEBUG_DATA - #include <linux/usb.h> #include <linux/hid.h> @@ -515,6 +512,7 @@ void usbhid_close(struct hid_device *hid) #define USB_VENDOR_ID_TURBOX 0x062a #define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201 +#define USB_VENDOR_ID_CIDC 0x1677 /* * Initialize all reports @@ -548,7 +546,6 @@ void usbhid_init_reports(struct hid_device *hid) } #define USB_VENDOR_ID_GTCO 0x078c -#define USB_VENDOR_ID_GTCO_IPANEL_2 0x5543 #define USB_DEVICE_ID_GTCO_90 0x0090 #define USB_DEVICE_ID_GTCO_100 0x0100 #define USB_DEVICE_ID_GTCO_101 0x0101 @@ -594,8 +591,6 @@ void usbhid_init_reports(struct hid_device *hid) #define USB_DEVICE_ID_GTCO_1004 0x1004 #define USB_DEVICE_ID_GTCO_1005 0x1005 #define USB_DEVICE_ID_GTCO_1006 0x1006 -#define USB_DEVICE_ID_GTCO_8 0x0008 -#define USB_DEVICE_ID_GTCO_d 0x000d #define USB_VENDOR_ID_WACOM 0x056a @@ -691,10 +686,8 @@ void usbhid_init_reports(struct hid_device *hid) #define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802 #define USB_VENDOR_ID_CODEMERCS 0x07c0 -#define USB_DEVICE_ID_CODEMERCS_IOW40 0x1500 -#define USB_DEVICE_ID_CODEMERCS_IOW24 0x1501 -#define USB_DEVICE_ID_CODEMERCS_IOW48 0x1502 -#define USB_DEVICE_ID_CODEMERCS_IOW28 0x1503 +#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500 +#define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff #define USB_VENDOR_ID_DELORME 0x1163 #define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100 @@ -760,6 +753,8 @@ void usbhid_init_reports(struct hid_device *hid) #define USB_VENDOR_ID_LOGITECH 0x046d #define USB_DEVICE_ID_LOGITECH_USB_RECEIVER 0xc101 +#define USB_DEVICE_ID_LOGITECH_USB_RECEIVER_2 0xc517 +#define USB_DEVICE_ID_DINOVO_EDGE 0xc714 #define USB_VENDOR_ID_IMATION 0x0718 #define USB_DEVICE_ID_DISC_STAKKA 0xd000 @@ -780,6 +775,8 @@ static const struct hid_blacklist { unsigned quirks; } hid_blacklist[] = { + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20, HID_QUIRK_IGNORE }, @@ -790,10 +787,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW48, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW28, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE }, @@ -854,8 +847,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO_IPANEL_2, USB_DEVICE_ID_GTCO_8, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO_IPANEL_2, USB_DEVICE_ID_GTCO_d, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY, HID_QUIRK_IGNORE }, @@ -948,11 +939,14 @@ static const struct hid_blacklist { { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_USB_RECEIVER, HID_QUIRK_BAD_RELATIVE_KEYS }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_USB_RECEIVER_2, HID_QUIRK_LOGITECH_S510_DESCRIPTOR }, { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, { USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER }, + { USB_VENDOR_ID_CIDC, 0x0103, HID_QUIRK_IGNORE }, + { 0, 0 } }; @@ -1043,6 +1037,22 @@ static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum) kfree(buf); } +/* + * Logitech S510 keyboard sends in report #3 keys which are far + * above the logical maximum described in descriptor. This extends + * the original value of 0x28c of logical maximum to 0x104d + */ +static void hid_fixup_s510_descriptor(unsigned char *rdesc, int rsize) +{ + if (rsize >= 90 && rdesc[83] == 0x26 + && rdesc[84] == 0x8c + && rdesc[85] == 0x02) { + info("Fixing up Logitech S510 report descriptor"); + rdesc[84] = rdesc[89] = 0x4d; + rdesc[85] = rdesc[90] = 0x10; + } +} + static struct hid_device *usb_hid_configure(struct usb_interface *intf) { struct usb_host_interface *interface = intf->cur_altsetting; @@ -1054,9 +1064,14 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) int n, len, insize = 0; struct usbhid_device *usbhid; - /* Ignore all Wacom devices */ - if (le16_to_cpu(dev->descriptor.idVendor) == USB_VENDOR_ID_WACOM) - return NULL; + /* Ignore all Wacom devices */ + if (le16_to_cpu(dev->descriptor.idVendor) == USB_VENDOR_ID_WACOM) + return NULL; + /* ignore all Code Mercenaries IOWarrior devices */ + if (le16_to_cpu(dev->descriptor.idVendor) == USB_VENDOR_ID_CODEMERCS) + if (le16_to_cpu(dev->descriptor.idProduct) >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST && + le16_to_cpu(dev->descriptor.idProduct) <= USB_DEVICE_ID_CODEMERCS_IOW_LAST) + return NULL; for (n = 0; hid_blacklist[n].idVendor; n++) if ((hid_blacklist[n].idVendor == le16_to_cpu(dev->descriptor.idVendor)) && @@ -1111,7 +1126,10 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) if ((quirks & HID_QUIRK_CYMOTION)) hid_fixup_cymotion_descriptor(rdesc, rsize); -#ifdef DEBUG_DATA + if (quirks & HID_QUIRK_LOGITECH_S510_DESCRIPTOR) + hid_fixup_s510_descriptor(rdesc, rsize); + +#ifdef CONFIG_HID_DEBUG printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n); for (n = 0; n < rsize; n++) printk(" %02x", (unsigned char) rdesc[n]); @@ -1227,8 +1245,8 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) le16_to_cpu(dev->descriptor.idProduct)); hid->bus = BUS_USB; - hid->vendor = dev->descriptor.idVendor; - hid->product = dev->descriptor.idProduct; + hid->vendor = le16_to_cpu(dev->descriptor.idVendor); + hid->product = le16_to_cpu(dev->descriptor.idProduct); usb_make_path(dev, hid->phys, sizeof(hid->phys)); strlcat(hid->phys, "/input", sizeof(hid->phys)); diff --git a/drivers/usb/input/hid-ff.c b/drivers/usb/input/hid-ff.c index bc7f8e6f8c97..e431faaa6abc 100644 --- a/drivers/usb/input/hid-ff.c +++ b/drivers/usb/input/hid-ff.c @@ -54,9 +54,10 @@ struct hid_ff_initializer { static struct hid_ff_initializer inits[] = { #ifdef CONFIG_LOGITECH_FF { 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */ + { 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */ { 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */ + { 0x46d, 0xc294, hid_lgff_init }, /* Logitech Formula Force EX */ { 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */ - { 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */ { 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */ #endif #ifdef CONFIG_PANTHERLORD_FF diff --git a/drivers/usb/input/hid-lgff.c b/drivers/usb/input/hid-lgff.c index 4df0968f852e..e6f3af3e66d1 100644 --- a/drivers/usb/input/hid-lgff.c +++ b/drivers/usb/input/hid-lgff.c @@ -52,8 +52,9 @@ static const struct dev_type devices[] = { { 0x046d, 0xc211, ff_rumble }, { 0x046d, 0xc219, ff_rumble }, { 0x046d, 0xc283, ff_joystick }, + { 0x046d, 0xc294, ff_joystick }, + { 0x046d, 0xc295, ff_joystick }, { 0x046d, 0xca03, ff_joystick }, - { 0x0000, 0x0000, ff_joystick } }; static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect) @@ -105,8 +106,9 @@ int hid_lgff_init(struct hid_device* hid) struct input_dev *dev = hidinput->input; struct hid_report *report; struct hid_field *field; + const signed short *ff_bits = ff_joystick; int error; - int i, j; + int i; /* Find the report to use */ if (list_empty(report_list)) { @@ -130,12 +132,14 @@ int hid_lgff_init(struct hid_device* hid) for (i = 0; i < ARRAY_SIZE(devices); i++) { if (dev->id.vendor == devices[i].idVendor && dev->id.product == devices[i].idProduct) { - for (j = 0; devices[i].ff[j] >= 0; j++) - set_bit(devices[i].ff[j], dev->ffbit); + ff_bits = devices[i].ff; break; } } + for (i = 0; ff_bits[i] >= 0; i++) + set_bit(ff_bits[i], dev->ffbit); + error = input_ff_create_memless(dev, NULL, hid_lgff_play); if (error) return error; diff --git a/drivers/usb/input/usbkbd.c b/drivers/usb/input/usbkbd.c index 8505824848f6..3749f4a235f9 100644 --- a/drivers/usb/input/usbkbd.c +++ b/drivers/usb/input/usbkbd.c @@ -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 */ }; diff --git a/drivers/usb/input/usbmouse.c b/drivers/usb/input/usbmouse.c index 64a33e420cfb..692fd6087779 100644 --- a/drivers/usb/input/usbmouse.c +++ b/drivers/usb/input/usbmouse.c @@ -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 */ }; diff --git a/drivers/usb/input/wacom_wac.c b/drivers/usb/input/wacom_wac.c index 4142e36730fc..4f3e9bc7177d 100644 --- a/drivers/usb/input/wacom_wac.c +++ b/drivers/usb/input/wacom_wac.c @@ -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 }, { } }; diff --git a/drivers/usb/input/wacom_wac.h b/drivers/usb/input/wacom_wac.h index a1d9ce007970..a23022287248 100644 --- a/drivers/usb/input/wacom_wac.h +++ b/drivers/usb/input/wacom_wac.h @@ -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, diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index a74bf8617e7f..9c7eb6144d02 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -88,6 +88,17 @@ config USB_LCD To compile this driver as a module, choose M here: the module will be called usblcd. +config USB_BERRY_CHARGE + tristate "USB BlackBerry recharge support" + depends on USB + help + Say Y here if you want to connect a BlackBerry device to your + computer's USB port and have it automatically switch to "recharge" + mode. + + To compile this driver as a module, choose M here: the + module will be called berry_charge. + config USB_LED tristate "USB LED driver support" depends on USB @@ -233,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 diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 2cba07d31971..b68e6b774f1a 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -6,12 +6,14 @@ obj-$(CONFIG_USB_ADUTUX) += adutux.o obj-$(CONFIG_USB_APPLEDISPLAY) += appledisplay.o obj-$(CONFIG_USB_AUERSWALD) += auerswald.o +obj-$(CONFIG_USB_BERRY_CHARGE) += berry_charge.o obj-$(CONFIG_USB_CYPRESS_CY7C63)+= cypress_cy7c63.o obj-$(CONFIG_USB_CYTHERM) += cytherm.o obj-$(CONFIG_USB_EMI26) += emi26.o obj-$(CONFIG_USB_EMI62) += emi62.o obj-$(CONFIG_USB_FTDI_ELAN) += ftdi-elan.o obj-$(CONFIG_USB_IDMOUSE) += idmouse.o +obj-$(CONFIG_USB_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/appledisplay.c b/drivers/usb/misc/appledisplay.c index e573c8ba9785..cf70c16f0e3f 100644 --- a/drivers/usb/misc/appledisplay.c +++ b/drivers/usb/misc/appledisplay.c @@ -141,7 +141,7 @@ static int appledisplay_bl_update_status(struct backlight_device *bd) int retval; pdata->msgdata[0] = 0x10; - pdata->msgdata[1] = bd->props->brightness; + pdata->msgdata[1] = bd->props.brightness; retval = usb_control_msg( pdata->udev, @@ -177,11 +177,9 @@ static int appledisplay_bl_get_brightness(struct backlight_device *bd) return pdata->msgdata[1]; } -static struct backlight_properties appledisplay_bl_data = { - .owner = THIS_MODULE, +static struct backlight_ops appledisplay_bl_data = { .get_brightness = appledisplay_bl_get_brightness, .update_status = appledisplay_bl_update_status, - .max_brightness = 0xFF }; static void appledisplay_work(struct work_struct *work) @@ -190,11 +188,9 @@ static void appledisplay_work(struct work_struct *work) container_of(work, struct appledisplay, work.work); int retval; - up(&pdata->bd->sem); retval = appledisplay_bl_get_brightness(pdata->bd); if (retval >= 0) - pdata->bd->props->brightness = retval; - down(&pdata->bd->sem); + pdata->bd->props.brightness = retval; /* Poll again in about 125ms if there's still a button pressed */ if (pdata->button_pressed) @@ -288,10 +284,10 @@ static int appledisplay_probe(struct usb_interface *iface, goto error; } + pdata->bd->props.max_brightness = 0xff; + /* Try to get brightness */ - up(&pdata->bd->sem); brightness = appledisplay_bl_get_brightness(pdata->bd); - down(&pdata->bd->sem); if (brightness < 0) { retval = brightness; @@ -300,9 +296,7 @@ static int appledisplay_probe(struct usb_interface *iface, } /* Set brightness in backlight device */ - up(&pdata->bd->sem); - pdata->bd->props->brightness = brightness; - down(&pdata->bd->sem); + pdata->bd->props.brightness = brightness; /* save our data pointer in the interface device */ usb_set_intfdata(iface, pdata); diff --git a/drivers/usb/misc/berry_charge.c b/drivers/usb/misc/berry_charge.c new file mode 100644 index 000000000000..60893c6c8221 --- /dev/null +++ b/drivers/usb/misc/berry_charge.c @@ -0,0 +1,140 @@ +/* + * USB BlackBerry charging module + * + * 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. + * + * Information on how to switch configs was taken by the bcharge.cc file + * created by the barry.sf.net project. + * + * bcharge.cc has the following copyright: + * Copyright (C) 2006, Net Direct Inc. (http://www.netdirect.ca/) + * and is released under the GPLv2. + * + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/usb.h> + +#define RIM_VENDOR 0x0fca +#define BLACKBERRY 0x0001 + +static int debug; + +#ifdef dbg +#undef dbg +#endif +#define dbg(dev, format, arg...) \ + if (debug) \ + dev_printk(KERN_DEBUG , dev , format , ## arg) + +static struct usb_device_id id_table [] = { + { USB_DEVICE(RIM_VENDOR, BLACKBERRY) }, + { }, /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, id_table); + +static int magic_charge(struct usb_device *udev) +{ + char *dummy_buffer = kzalloc(2, GFP_KERNEL); + int retval; + + if (!dummy_buffer) + return -ENOMEM; + + /* send two magic commands and then set the configuration. The device + * will then reset itself with the new power usage and should start + * charging. */ + + /* Note, with testing, it only seems that the first message is really + * needed (at least for the 8700c), but to be safe, we emulate what + * other operating systems seem to be sending to their device. We + * really need to get some specs for this device to be sure about what + * is going on here. + */ + dbg(&udev->dev, "Sending first magic command\n"); + retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + 0xa5, 0xc0, 0, 1, dummy_buffer, 2, 100); + if (retval != 2) { + dev_err(&udev->dev, "First magic command failed: %d.\n", + retval); + return retval; + } + + dbg(&udev->dev, "Sending first magic command\n"); + retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + 0xa2, 0x40, 0, 1, dummy_buffer, 0, 100); + if (retval != 0) { + dev_err(&udev->dev, "Second magic command failed: %d.\n", + retval); + return retval; + } + + dbg(&udev->dev, "Calling set_configuration\n"); + retval = usb_driver_set_configuration(udev, 1); + if (retval) + dev_err(&udev->dev, "Set Configuration failed :%d.\n", retval); + + return retval; +} + +static int berry_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + + dbg(&udev->dev, "Power is set to %dmA\n", + udev->actconfig->desc.bMaxPower * 2); + + /* check the power usage so we don't try to enable something that is + * already enabled */ + if ((udev->actconfig->desc.bMaxPower * 2) == 500) { + dbg(&udev->dev, "device is already charging, power is " + "set to %dmA\n", udev->actconfig->desc.bMaxPower * 2); + return -ENODEV; + } + + /* turn the power on */ + magic_charge(udev); + + /* we don't really want to bind to the device, userspace programs can + * handle the syncing just fine, so get outta here. */ + return -ENODEV; +} + +static void berry_disconnect(struct usb_interface *intf) +{ +} + +static struct usb_driver berry_driver = { + .name = "berry_charge", + .probe = berry_probe, + .disconnect = berry_disconnect, + .id_table = id_table, +}; + +static int __init berry_init(void) +{ + return usb_register(&berry_driver); +} + +static void __exit berry_exit(void) +{ + usb_deregister(&berry_driver); +} + +module_init(berry_init); +module_exit(berry_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>"); +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled or not"); diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c new file mode 100644 index 000000000000..d69665c8de02 --- /dev/null +++ b/drivers/usb/misc/iowarrior.c @@ -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); diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index c01dfe603672..b2bedd974ac3 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -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); diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index d38a1279d9d9..494ee3b9a226 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -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); } diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h index 4f949ce8a7f3..efdfd8993d9e 100644 --- a/drivers/usb/mon/usb_mon.h +++ b/drivers/usb/mon/usb_mon.h @@ -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. diff --git a/drivers/usb/net/Kconfig b/drivers/usb/net/Kconfig index a2b94ef512bc..3de564b23147 100644 --- a/drivers/usb/net/Kconfig +++ b/drivers/usb/net/Kconfig @@ -84,6 +84,7 @@ config USB_PEGASUS config USB_RTL8150 tristate "USB RTL8150 based ethernet device support (EXPERIMENTAL)" depends on EXPERIMENTAL + select MII help Say Y here if you have RTL8150 based usb-ethernet adapter. Send me <petkan@users.sourceforge.net> any comments you may have. @@ -98,7 +99,7 @@ config USB_USBNET_MII config USB_USBNET tristate "Multi-purpose USB Networking Framework" - select MII if USBNET_MII != n + select MII if USB_USBNET_MII != n ---help--- This driver supports several kinds of network links over USB, with "minidrivers" built around a common network driver core @@ -185,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 @@ -239,6 +249,7 @@ config USB_NET_RNDIS_HOST config USB_NET_CDC_SUBSET tristate "Simple USB Network Links (CDC Ethernet subset)" depends on USB_USBNET + default y help This driver module supports USB network devices that can work without any device-specific information. Select it if you have @@ -298,6 +309,13 @@ config USB_EPSON2888 Choose this option to support the usb networking links used by some sample firmware from Epson. +config USB_KC2190 + boolean "KT Technology KC2190 based cables (InstaNet)" + depends on USB_NET_CDC_SUBSET && EXPERIMENTAL + help + Choose this option if you're using a host-to-host cable + with one of these chips. + config USB_NET_ZAURUS tristate "Sharp Zaurus (stock ROMs) and compatible" depends on USB_USBNET diff --git a/drivers/usb/net/Makefile b/drivers/usb/net/Makefile index 7b51964de171..595a539f8384 100644 --- a/drivers/usb/net/Makefile +++ b/drivers/usb/net/Makefile @@ -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 diff --git a/drivers/usb/net/asix.c b/drivers/usb/net/asix.c index bd357e178e55..5808ea082459 100644 --- a/drivers/usb/net/asix.c +++ b/drivers/usb/net/asix.c @@ -351,9 +351,11 @@ static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, skb_push(skb, 4); packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4); + cpu_to_le32s(&packet_len); memcpy(skb->data, &packet_len, sizeof(packet_len)); if ((skb->len % 512) == 0) { + cpu_to_le32s(&padbytes); memcpy( skb->tail, &padbytes, sizeof(padbytes)); skb_put(skb, sizeof(padbytes)); } @@ -1393,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), @@ -1421,9 +1423,13 @@ static const struct usb_device_id products [] = { 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/cdc_subset.c b/drivers/usb/net/cdc_subset.c index ae8fb06cf38e..bc62b012602b 100644 --- a/drivers/usb/net/cdc_subset.c +++ b/drivers/usb/net/cdc_subset.c @@ -79,13 +79,19 @@ static int always_connected (struct usbnet *dev) * * ALi M5632 driver ... does high speed * + * NOTE that the MS-Windows drivers for this chip use some funky and + * (naturally) undocumented 7-byte prefix to each packet, so this is a + * case where we don't currently interoperate. Also, once you unplug + * one end of the cable, you need to replug the other end too ... since + * chip docs are unavailable, there's no way to reset the relevant state + * short of a power cycle. + * *-------------------------------------------------------------------------*/ static const struct driver_info ali_m5632_info = { .description = "ALi M5632", }; - #endif @@ -159,6 +165,11 @@ static const struct driver_info epson2888_info = { #endif /* CONFIG_USB_EPSON2888 */ +/*------------------------------------------------------------------------- + * + * info from Jonathan McDowell <noodles@earth.li> + * + *-------------------------------------------------------------------------*/ #ifdef CONFIG_USB_KC2190 #define HAVE_HARDWARE static const struct driver_info kc2190_info = { @@ -223,6 +234,10 @@ static const struct usb_device_id products [] = { USB_DEVICE (0x0402, 0x5632), // ALi defaults .driver_info = (unsigned long) &ali_m5632_info, }, +{ + USB_DEVICE (0x182d,0x207c), // SiteCom CN-124 + .driver_info = (unsigned long) &ali_m5632_info, +}, #endif #ifdef CONFIG_USB_AN2720 @@ -314,13 +329,13 @@ static struct usb_driver cdc_subset_driver = { static int __init cdc_subset_init(void) { - return usb_register(&cdc_subset_driver); + return usb_register(&cdc_subset_driver); } module_init(cdc_subset_init); static void __exit cdc_subset_exit(void) { - usb_deregister(&cdc_subset_driver); + usb_deregister(&cdc_subset_driver); } module_exit(cdc_subset_exit); diff --git a/drivers/usb/net/dm9601.c b/drivers/usb/net/dm9601.c new file mode 100644 index 000000000000..4a932e1cd93b --- /dev/null +++ b/drivers/usb/net/dm9601.c @@ -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"); diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c index 43ba61abfcc5..de69b183bd2f 100644 --- a/drivers/usb/net/usbnet.c +++ b/drivers/usb/net/usbnet.c @@ -147,7 +147,7 @@ int usbnet_get_endpoints(struct usbnet *dev, struct usb_interface *intf) if (tmp < 0) return tmp; } - + dev->in = usb_rcvbulkpipe (dev->udev, in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); dev->out = usb_sndbulkpipe (dev->udev, @@ -327,7 +327,7 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags) if (netif_running (dev->net) && netif_device_present (dev->net) && !test_bit (EVENT_RX_HALT, &dev->flags)) { - switch (retval = usb_submit_urb (urb, GFP_ATOMIC)){ + switch (retval = usb_submit_urb (urb, GFP_ATOMIC)){ case -EPIPE: usbnet_defer_kevent (dev, EVENT_RX_HALT); break; @@ -443,7 +443,7 @@ block: case -EOVERFLOW: dev->stats.rx_over_errors++; // FALLTHROUGH - + default: entry->state = rx_cleanup; dev->stats.rx_errors++; @@ -560,7 +560,7 @@ static int usbnet_stop (struct net_device *net) if (netif_msg_ifdown (dev)) devinfo (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld", - dev->stats.rx_packets, dev->stats.tx_packets, + dev->stats.rx_packets, dev->stats.tx_packets, dev->stats.rx_errors, dev->stats.tx_errors ); @@ -578,7 +578,7 @@ static int usbnet_stop (struct net_device *net) devdbg (dev, "waited for %d urb completions", temp); } dev->wait = NULL; - remove_wait_queue (&unlink_wakeup, &wait); + remove_wait_queue (&unlink_wakeup, &wait); usb_kill_urb(dev->interrupt); @@ -834,7 +834,7 @@ kevent (struct work_struct *work) } if (test_bit (EVENT_LINK_RESET, &dev->flags)) { - struct driver_info *info = dev->driver_info; + struct driver_info *info = dev->driver_info; int retval = 0; clear_bit (EVENT_LINK_RESET, &dev->flags); @@ -1066,7 +1066,7 @@ static void usbnet_bh (unsigned long param) * USB Device Driver support * *-------------------------------------------------------------------------*/ - + // precondition: never called in_interrupt void usbnet_disconnect (struct usb_interface *intf) @@ -1087,7 +1087,7 @@ void usbnet_disconnect (struct usb_interface *intf) intf->dev.driver->name, xdev->bus->bus_name, xdev->devpath, dev->driver_info->description); - + net = dev->net; unregister_netdev (net); @@ -1111,7 +1111,7 @@ int usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) { struct usbnet *dev; - struct net_device *net; + struct net_device *net; struct usb_host_interface *interface; struct driver_info *info; struct usb_device *xdev; @@ -1181,6 +1181,9 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) // NOTE net->name still not usable ... if (info->bind) { status = info->bind (dev, udev); + if (status < 0) + goto out1; + // heuristic: "usb%d" for links we know are two-host, // else "eth%d" when there's reasonable doubt. userspace // can rename the link if it knows better. @@ -1207,12 +1210,12 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) if (status == 0 && dev->status) status = init_status (dev, udev); if (status < 0) - goto out1; + goto out3; if (!dev->rx_urb_size) dev->rx_urb_size = dev->hard_mtu; dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1); - + SET_NETDEV_DEV(net, &udev->dev); status = register_netdev (net); if (status) @@ -1255,7 +1258,7 @@ EXPORT_SYMBOL_GPL(usbnet_probe); int usbnet_suspend (struct usb_interface *intf, pm_message_t message) { struct usbnet *dev = usb_get_intfdata(intf); - + /* accelerate emptying of the rx and queues, to avoid * having everything error out. */ @@ -1286,7 +1289,7 @@ static int __init usbnet_init(void) < sizeof (struct skb_data)); random_ether_addr(node_id); - return 0; + return 0; } module_init(usbnet_init); diff --git a/drivers/usb/serial/airprime.c b/drivers/usb/serial/airprime.c index 0af42e32fa0a..18816bf96a4d 100644 --- a/drivers/usb/serial/airprime.c +++ b/drivers/usb/serial/airprime.c @@ -58,11 +58,6 @@ static void airprime_read_bulk_callback(struct urb *urb) if (urb->status) { dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); - /* something happened, so free up the memory for this urb */ - if (urb->transfer_buffer) { - kfree (urb->transfer_buffer); - urb->transfer_buffer = NULL; - } return; } usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); @@ -146,6 +141,8 @@ static int airprime_open(struct usb_serial_port *port, struct file *filp) airprime_read_bulk_callback, port); result = usb_submit_urb(urb, GFP_KERNEL); if (result) { + usb_free_urb(urb); + kfree(buffer); dev_err(&port->dev, "%s - failed submitting read urb %d for port %d, error %d\n", __FUNCTION__, i, port->number, result); @@ -160,27 +157,12 @@ static int airprime_open(struct usb_serial_port *port, struct file *filp) /* some error happened, cancel any submitted urbs and clean up anything that got allocated successfully */ - for ( ; i >= 0; --i) { + while (i-- != 0) { urb = priv->read_urbp[i]; - if (urb) { - /* This urb was submitted successfully. So we have to - cancel it. - Unlinking the urb will invoke read_bulk_callback() - with an error status, so its transfer buffer will - be freed there */ - if (usb_unlink_urb (urb) != -EINPROGRESS) { - /* comments in drivers/usb/core/urb.c say this - can only happen if the urb was never submitted, - or has completed already. - Either way we may have to free the transfer - buffer here. */ - if (urb->transfer_buffer) { - kfree (urb->transfer_buffer); - urb->transfer_buffer = NULL; - } - } - usb_free_urb (urb); - } + buffer = urb->transfer_buffer; + usb_kill_urb (urb); + usb_free_urb (urb); + kfree (buffer); } out: @@ -194,10 +176,9 @@ static void airprime_close(struct usb_serial_port *port, struct file * filp) dbg("%s - port %d", __FUNCTION__, port->number); - /* killing the urb will invoke read_bulk_callback() with an error status, - so the transfer buffer will be freed there */ for (i = 0; i < NUM_READ_URBS; ++i) { usb_kill_urb (priv->read_urbp[i]); + kfree (priv->read_urbp[i]->transfer_buffer); usb_free_urb (priv->read_urbp[i]); } diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index 3ec24870bca9..db623e754899 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -69,6 +69,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */ { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ + { USB_DEVICE(0x10C5, 0xEA61) }, /* Silicon Labs MobiData GPRS USB Modem */ { USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */ { USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */ { } /* Terminating Entry */ diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 4695952b6470..c525b42dadde 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -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 */ }; diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 7eff1c03ba80..1bdda935f7d9 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -491,6 +491,12 @@ #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 */ diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 601e0648dec6..53baeec8f265 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -66,6 +66,8 @@ struct usb_serial_driver usb_serial_generic_device = { .num_bulk_out = NUM_DONT_CARE, .num_ports = 1, .shutdown = usb_serial_generic_shutdown, + .throttle = usb_serial_generic_throttle, + .unthrottle = usb_serial_generic_unthrottle, }; static int generic_probe(struct usb_interface *interface, @@ -115,6 +117,7 @@ int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; int result = 0; + unsigned long flags; dbg("%s - port %d", __FUNCTION__, port->number); @@ -124,7 +127,13 @@ int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp) if (port->tty) port->tty->low_latency = 1; - /* if we have a bulk interrupt, start reading from it */ + /* clear the throttle flags */ + spin_lock_irqsave(&port->lock, flags); + port->throttled = 0; + port->throttle_req = 0; + spin_unlock_irqrestore(&port->lock, flags); + + /* if we have a bulk endpoint, start reading from it */ if (serial->num_bulk_in) { /* Start reading from the device */ usb_fill_bulk_urb (port->read_urb, serial->dev, @@ -253,31 +262,22 @@ int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port) return (chars); } -void usb_serial_generic_read_bulk_callback (struct urb *urb) +/* Push data to tty layer and resubmit the bulk read URB */ +static void flush_and_resubmit_read_urb (struct usb_serial_port *port) { - struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial = port->serial; - struct tty_struct *tty; - unsigned char *data = urb->transfer_buffer; + struct urb *urb = port->read_urb; + struct tty_struct *tty = port->tty; int result; - dbg("%s - port %d", __FUNCTION__, port->number); - - if (urb->status) { - dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); - return; - } - - usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); - - tty = port->tty; + /* Push data to tty */ if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); - tty_insert_flip_string(tty, data, urb->actual_length); - tty_flip_buffer_push(tty); + tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length); + tty_flip_buffer_push(tty); /* is this allowed from an URB callback ? */ } - /* Continue trying to always read */ + /* Continue reading from device */ usb_fill_bulk_urb (port->read_urb, serial->dev, usb_rcvbulkpipe (serial->dev, port->bulk_in_endpointAddress), @@ -290,6 +290,40 @@ void usb_serial_generic_read_bulk_callback (struct urb *urb) if (result) dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result); } + +void usb_serial_generic_read_bulk_callback (struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + unsigned char *data = urb->transfer_buffer; + int is_throttled; + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + + if (urb->status) { + dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); + return; + } + + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); + + /* Throttle the device if requested by tty */ + if (urb->actual_length) { + spin_lock_irqsave(&port->lock, flags); + is_throttled = port->throttled = port->throttle_req; + spin_unlock_irqrestore(&port->lock, flags); + if (is_throttled) { + /* Let the received data linger in the read URB; + * usb_serial_generic_unthrottle() will pick it + * up later. */ + dbg("%s - throttling device", __FUNCTION__); + return; + } + } + + /* Handle data and continue reading from device */ + flush_and_resubmit_read_urb(port); +} EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); void usb_serial_generic_write_bulk_callback (struct urb *urb) @@ -308,6 +342,38 @@ void usb_serial_generic_write_bulk_callback (struct urb *urb) } EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback); +void usb_serial_generic_throttle (struct usb_serial_port *port) +{ + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + + /* Set the throttle request flag. It will be picked up + * by usb_serial_generic_read_bulk_callback(). */ + spin_lock_irqsave(&port->lock, flags); + port->throttle_req = 1; + spin_unlock_irqrestore(&port->lock, flags); +} + +void usb_serial_generic_unthrottle (struct usb_serial_port *port) +{ + int was_throttled; + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + + /* Clear the throttle flags */ + spin_lock_irqsave(&port->lock, flags); + was_throttled = port->throttled; + port->throttled = port->throttle_req = 0; + spin_unlock_irqrestore(&port->lock, flags); + + if (was_throttled) { + /* Handle pending data and resume reading from device */ + flush_and_resubmit_read_urb(port); + } +} + void usb_serial_generic_shutdown (struct usb_serial *serial) { int i; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index ced9f32b29d9..db92a7fb1f7c 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -67,54 +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 AUDIOVOX_VENDOR_ID 0x0F3D -#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 AUDIOVOX_PRODUCT_AIRCARD 0x0112 -#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(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) }, { 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(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) }, - { 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 = { @@ -136,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, diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 6c083d4e2c9b..83dfae93a45d 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -83,6 +83,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(BELKIN_VENDOR_ID, BELKIN_PRODUCT_ID) }, { USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID) }, { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ID) }, + { USB_DEVICE(WS002IN_VENDOR_ID, WS002IN_PRODUCT_ID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 65a5039665e7..f9a71d0c102e 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -97,3 +97,8 @@ /* Huawei E620 UMTS/HSDPA card (ID: 12d1:1001) */ #define HUAWEI_VENDOR_ID 0x12d1 #define HUAWEI_PRODUCT_ID 0x1001 + +/* Willcom WS002IN Data Driver (by NetIndex Inc.) */ +#define WS002IN_VENDOR_ID 0x11f6 +#define WS002IN_PRODUCT_ID 0x2001 + diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 70234f5dbeeb..e227f64d5641 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -153,6 +153,12 @@ static int slave_configure(struct scsi_device *sdev) if (us->flags & US_FL_FIX_CAPACITY) sdev->fix_capacity = 1; + /* A few disks have two indistinguishable version, one of + * which reports the correct capacity and the other does not. + * The sd driver has to guess which is the case. */ + if (us->flags & US_FL_CAPACITY_HEURISTICS) + sdev->guess_capacity = 1; + /* Some devices report a SCSI revision level above 2 but are * unable to handle the REPORT LUNS command (for which * support is mandatory at level 3). Since we already have diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index f49a62fc32d2..9644a8ea4aa7 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1101,6 +1101,15 @@ UNUSUAL_DEV( 0x08bd, 0x1100, 0x0000, 0x0000, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN), +/* Submitted by Dylan Taft <d13f00l@gmail.com> + * US_FL_IGNORE_RESIDUE Needed + */ +UNUSUAL_DEV( 0x08ca, 0x3103, 0x0100, 0x0100, + "AIPTEK", + "Aiptek USB Keychain MP3 Player", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE), + /* Entry needed for flags. Moreover, all devices with this ID use * bulk-only transport, but _some_ falsely report Control/Bulk instead. * One example is "Trumpion Digital Research MYMP3". @@ -1311,12 +1320,13 @@ UNUSUAL_DEV( 0x0fce, 0xd008, 0x0000, 0x0000, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_NO_WP_DETECT ), -/* Reported by Jan Mate <mate@fiit.stuba.sk> */ +/* Reported by Jan Mate <mate@fiit.stuba.sk> + * and by Soeren Sonnenburg <kernel@nn7.de> */ UNUSUAL_DEV( 0x0fce, 0xe030, 0x0000, 0x0000, "Sony Ericsson", "P990i", US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY ), + US_FL_FIX_CAPACITY | US_FL_IGNORE_RESIDUE ), /* Reported by Emmanuel Vasilakis <evas@forthnet.gr> */ UNUSUAL_DEV( 0x0fce, 0xe031, 0x0000, 0x0000, @@ -1385,6 +1395,16 @@ UNUSUAL_DEV( 0x1652, 0x6600, 0x0201, 0x0201, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE ), +/* Reported by Thomas Baechler <thomas@archlinux.org> + * Fixes I/O errors with Teac HD-35PU devices + */ + +UNUSUAL_DEV( 0x1652, 0x6600, 0x0201, 0x0201, + "Super Top", + "USB 2.0 IDE DEVICE", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE), + /* patch submitted by Davide Perini <perini.davide@dpsoftware.org> * and Renato Perini <rperini@email.it> */ @@ -1423,7 +1443,7 @@ UNUSUAL_DEV( 0xed06, 0x4500, 0x0001, 0x0001, "DataStor", "USB4500 FW1.04", US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY), + US_FL_CAPACITY_HEURISTICS), /* Control/Bulk transport for all SubClass values */ USUAL_DEV(US_SC_RBC, US_PR_CB, USB_US_TYPE_STOR), diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 7e7ec29782f1..8e898e3d861e 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -55,7 +55,7 @@ #include <linux/slab.h> #include <linux/kthread.h> #include <linux/mutex.h> -#include <linux/utsrelease.h> +#include <linux/utsname.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> @@ -547,7 +547,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id) idesc->bInterfaceSubClass, idesc->bInterfaceProtocol, msgs[msg], - UTS_RELEASE); + utsname()->release); } return 0; diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index 296b091cf168..46929a1b6f24 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -90,13 +90,15 @@ static int skel_open(struct inode *inode, struct file *file) goto exit; } + /* increment our usage count for the device */ + kref_get(&dev->kref); + /* prevent the device from being autosuspended */ retval = usb_autopm_get_interface(interface); - if (retval) + if (retval) { + kref_put(&dev->kref, skel_delete); goto exit; - - /* increment our usage count for the device */ - kref_get(&dev->kref); + } /* save our object in the file's private structure */ file->private_data = dev; |