Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6

* master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6:
  USB: export autosuspend delay in sysfs
  sysfs: allow attributes to be added to groups
  USB: make autosuspend delay a module parameter
  USB: minor cleanups for sysfs.c
  USB: add a blacklist for devices that can't handle some things we throw at them.
  USB: refactor usb device matching and create usb_device_match
  USB: Wacom driver updates
  gadgetfs: Fixed bug in ep_aio_read_retry.
  USB: Use USB defines in usbmouse.c and usbkbd.c
  USB: add rationale on why usb descriptor structures have to be packed
  USB: ftdi_sio: Adding VID and PID for Tellstick
  UHCI: Eliminate asynchronous skeleton Queue Headers
  UHCI: Add macros for computing DMA values
  USB: Davicom DM9601 usbnet driver
  USB: asix.c - Add JVC-PRX1 ids
  usbmon: Remove erroneous __exit
  USB: add driver for iowarrior devices.
  USB: option: add a bunch of new device ids
  USB: option: remove duplicate device id table
This commit is contained in:
Linus Torvalds 2007-02-26 11:41:08 -08:00
commit 92320cec61
38 changed files with 2391 additions and 274 deletions

View File

@ -1758,6 +1758,13 @@ and is between 256 and 4096 characters. It is defined in the file
Note that genuine overcurrent events won't be Note that genuine overcurrent events won't be
reported either. reported either.
usbcore.autosuspend=
[USB] The autosuspend time delay (in seconds) used
for newly-detected USB devices (default 2). This
is the time required before an idle device will be
autosuspended. Devices for which the delay is set
to 0 won't be autosuspended at all.
usbhid.mousepoll= usbhid.mousepoll=
[USBHID] The interval which mice are to be polled at. [USBHID] The interval which mice are to be polled at.

View File

@ -3392,6 +3392,13 @@ L: linux-usb-devel@lists.sourceforge.net
S: Maintained S: Maintained
W: http://www.kroah.com/linux-usb/ W: http://www.kroah.com/linux-usb/
USB DAVICOM DM9601 DRIVER
P: Peter Korsgaard
M: jacmet@sunsite.dk
L: linux-usb-devel@lists.sourceforge.net
W: http://www.linux-usb.org/usbnet
S: Maintained
USB EHCI DRIVER USB EHCI DRIVER
P: David Brownell P: David Brownell
M: dbrownell@users.sourceforge.net M: dbrownell@users.sourceforge.net

View File

@ -4,7 +4,7 @@
usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \ usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \
config.o file.o buffer.o sysfs.o endpoint.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) ifeq ($(CONFIG_PCI),y)
usbcore-objs += hcd-pci.o usbcore-objs += hcd-pci.o

View File

@ -366,19 +366,8 @@ void usb_driver_release_interface(struct usb_driver *driver,
EXPORT_SYMBOL(usb_driver_release_interface); EXPORT_SYMBOL(usb_driver_release_interface);
/* returns 0 if no match, 1 if match */ /* returns 0 if no match, 1 if match */
int usb_match_one_id(struct usb_interface *interface, int usb_match_device(struct usb_device *dev, const struct usb_device_id *id)
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) && if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
id->idVendor != le16_to_cpu(dev->descriptor.idVendor)) id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
return 0; return 0;
@ -409,6 +398,26 @@ int usb_match_one_id(struct usb_interface *interface,
(id->bDeviceProtocol != dev->descriptor.bDeviceProtocol)) (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
return 0; 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 /* The interface class, subclass, and protocol should never be
* checked for a match if the device class is Vendor Specific, * checked for a match if the device class is Vendor Specific,
* unless the match record specifies the Vendor ID. */ * unless the match record specifies the Vendor ID. */
@ -954,12 +963,16 @@ static int autosuspend_check(struct usb_device *udev)
int i; int i;
struct usb_interface *intf; struct usb_interface *intf;
/* For autosuspend, fail fast if anything is in use. /* For autosuspend, fail fast if anything is in use or autosuspend
* Also fail if any interfaces require remote wakeup but it * is disabled. Also fail if any interfaces require remote wakeup
* isn't available. */ * but it isn't available.
*/
udev->do_remote_wakeup = device_may_wakeup(&udev->dev); udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
if (udev->pm_usage_cnt > 0) if (udev->pm_usage_cnt > 0)
return -EBUSY; return -EBUSY;
if (!udev->autosuspend_delay)
return -EPERM;
if (udev->actconfig) { if (udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
intf = udev->actconfig->interface[i]; intf = udev->actconfig->interface[i];
@ -982,7 +995,7 @@ static int autosuspend_check(struct usb_device *udev)
#define autosuspend_check(udev) 0 #define autosuspend_check(udev) 0
#endif #endif /* CONFIG_USB_SUSPEND */
/** /**
* usb_suspend_both - suspend a USB device and its interfaces * usb_suspend_both - suspend a USB device and its interfaces
@ -1177,7 +1190,7 @@ static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt)
udev->pm_usage_cnt -= inc_usage_cnt; udev->pm_usage_cnt -= inc_usage_cnt;
} else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0) } else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0)
queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
USB_AUTOSUSPEND_DELAY); udev->autosuspend_delay);
usb_pm_unlock(udev); usb_pm_unlock(udev);
return status; return status;
} }
@ -1211,6 +1224,26 @@ void usb_autosuspend_device(struct usb_device *udev)
// __FUNCTION__, udev->pm_usage_cnt); // __FUNCTION__, udev->pm_usage_cnt);
} }
/**
* usb_try_autosuspend_device - attempt an autosuspend of a USB device and its interfaces
* @udev: the usb_device to autosuspend
*
* This routine should be called when a core subsystem thinks @udev may
* be ready to autosuspend.
*
* @udev's usage counter left unchanged. If it or any of the usage counters
* for an active interface is greater than 0, or autosuspend is not allowed
* for any other reason, no autosuspend request will be queued.
*
* This routine can run only in process context.
*/
void usb_try_autosuspend_device(struct usb_device *udev)
{
usb_autopm_do_device(udev, 0);
// dev_dbg(&udev->dev, "%s: cnt %d\n",
// __FUNCTION__, udev->pm_usage_cnt);
}
/** /**
* usb_autoresume_device - immediately autoresume a USB device and its interfaces * usb_autoresume_device - immediately autoresume a USB device and its interfaces
* @udev: the usb_device to autoresume * @udev: the usb_device to autoresume
@ -1261,7 +1294,7 @@ static int usb_autopm_do_interface(struct usb_interface *intf,
intf->pm_usage_cnt -= inc_usage_cnt; intf->pm_usage_cnt -= inc_usage_cnt;
} else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0) } else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0)
queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
USB_AUTOSUSPEND_DELAY); udev->autosuspend_delay);
} }
usb_pm_unlock(udev); usb_pm_unlock(udev);
return status; return status;

View File

@ -1287,6 +1287,9 @@ int usb_new_device(struct usb_device *udev)
if (!try_module_get(THIS_MODULE)) if (!try_module_get(THIS_MODULE))
return -EINVAL; return -EINVAL;
/* Determine quirks */
usb_detect_quirks(udev);
err = usb_get_configuration(udev); err = usb_get_configuration(udev);
if (err < 0) { if (err < 0) {
dev_err(&udev->dev, "can't read configurations, error %d\n", dev_err(&udev->dev, "can't read configurations, error %d\n",

View File

@ -11,6 +11,7 @@
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/usb/quirks.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <asm/scatterlist.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 /* Try to read the string descriptor by asking for the maximum
* possible number of bytes */ * 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 /* If that failed try to read the descriptor length, then
* ask for just that many bytes */ * ask for just that many bytes */

77
drivers/usb/core/quirks.c Normal file
View File

@ -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);
}

View File

@ -148,6 +148,75 @@ show_maxchild(struct device *dev, struct device_attribute *attr, char *buf)
} }
static DEVICE_ATTR(maxchild, S_IRUGO, show_maxchild, NULL); static 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 */ /* Descriptor fields */
#define usb_descriptor_attr_le16(field, format_string) \ #define usb_descriptor_attr_le16(field, format_string) \
static ssize_t \ static ssize_t \
@ -204,6 +273,7 @@ static struct attribute *dev_attrs[] = {
&dev_attr_devnum.attr, &dev_attr_devnum.attr,
&dev_attr_version.attr, &dev_attr_version.attr,
&dev_attr_maxchild.attr, &dev_attr_maxchild.attr,
&dev_attr_quirks.attr,
NULL, NULL,
}; };
static struct attribute_group dev_attr_grp = { static struct attribute_group dev_attr_grp = {
@ -219,6 +289,10 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
if (retval) if (retval)
return retval; return retval;
retval = add_power_attributes(dev);
if (retval)
goto error;
if (udev->manufacturer) { if (udev->manufacturer) {
retval = device_create_file(dev, &dev_attr_manufacturer); retval = device_create_file(dev, &dev_attr_manufacturer);
if (retval) if (retval)
@ -239,10 +313,7 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
goto error; goto error;
return 0; return 0;
error: error:
usb_remove_ep_files(&udev->ep0); usb_remove_sysfs_dev_files(udev);
device_remove_file(dev, &dev_attr_manufacturer);
device_remove_file(dev, &dev_attr_product);
device_remove_file(dev, &dev_attr_serial);
return retval; return retval;
} }
@ -251,14 +322,11 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev)
struct device *dev = &udev->dev; struct device *dev = &udev->dev;
usb_remove_ep_files(&udev->ep0); 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); 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 */ /* 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) 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_device *udev = interface_to_usbdev(intf);
struct usb_host_interface *alt = intf->cur_altsetting; struct usb_host_interface *alt = intf->cur_altsetting;
int retval; int retval;
retval = sysfs_create_group(&intf->dev.kobj, &intf_attr_grp); retval = sysfs_create_group(&dev->kobj, &intf_attr_grp);
if (retval) if (retval)
goto error; return retval;
if (alt->string == NULL) if (alt->string == NULL)
alt->string = usb_cache_string(udev, alt->desc.iInterface); alt->string = usb_cache_string(udev, alt->desc.iInterface);
if (alt->string) 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); usb_create_intf_ep_files(intf, udev);
return 0; 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) void usb_remove_sysfs_intf_files(struct usb_interface *intf)
{ {
usb_remove_intf_ep_files(intf); struct device *dev = &intf->dev;
sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp);
if (intf->cur_altsetting->string) usb_remove_intf_ep_files(intf);
device_remove_file(&intf->dev, &dev_attr_interface); device_remove_file(dev, &dev_attr_interface);
sysfs_remove_group(&dev->kobj, &intf_attr_grp);
} }

View File

@ -22,6 +22,7 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/slab.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 */ 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 * 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 #ifdef CONFIG_PM
mutex_init(&dev->pm_mutex); mutex_init(&dev->pm_mutex);
INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work); INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work);
dev->autosuspend_delay = usb_autosuspend_delay * HZ;
#endif #endif
return dev; return dev;
} }

View File

@ -13,6 +13,7 @@ extern void usb_disable_interface (struct usb_device *dev,
struct usb_interface *intf); struct usb_interface *intf);
extern void usb_release_interface_cache(struct kref *ref); extern void usb_release_interface_cache(struct kref *ref);
extern void usb_disable_device (struct usb_device *dev, int skip_ep0); 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, extern int usb_get_device_descriptor(struct usb_device *dev,
unsigned int size); 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_kick_khubd(struct usb_device *dev);
extern void usb_resume_root_hub(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 int usb_hub_init(void);
extern void usb_hub_cleanup(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 #ifdef CONFIG_USB_SUSPEND
#define USB_AUTOSUSPEND_DELAY (HZ*2)
extern void usb_autosuspend_device(struct usb_device *udev); 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); extern int usb_autoresume_device(struct usb_device *udev);
#else #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) static inline int usb_autoresume_device(struct usb_device *udev)
{ {
return 0; return 0;

View File

@ -553,6 +553,7 @@ static ssize_t ep_aio_read_retry(struct kiocb *iocb)
{ {
struct kiocb_priv *priv = iocb->private; struct kiocb_priv *priv = iocb->private;
ssize_t len, total; ssize_t len, total;
void *to_copy;
int i; int i;
/* we "retry" to get the right mm context for this: */ /* 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 */ /* copy stuff into user buffers */
total = priv->actual; total = priv->actual;
len = 0; len = 0;
to_copy = priv->buf;
for (i=0; i < priv->nr_segs; i++) { for (i=0; i < priv->nr_segs; i++) {
ssize_t this = min((ssize_t)(priv->iv[i].iov_len), total); 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) if (len == 0)
len = -EFAULT; len = -EFAULT;
break; break;
@ -571,6 +573,7 @@ static ssize_t ep_aio_read_retry(struct kiocb *iocb)
total -= this; total -= this;
len += this; len += this;
to_copy += this;
if (total == 0) if (total == 0)
break; break;
} }

View File

@ -196,7 +196,7 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
struct uhci_td *td = list_entry(urbp->td_list.next, struct uhci_td *td = list_entry(urbp->td_list.next,
struct uhci_td, list); 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", out += sprintf(out, "%*s Element != First TD\n",
space, ""); space, "");
i = nurbs = 0; 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; 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) static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
{ {
char *out = buf; 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 uhci_td *td;
struct list_head *tmp, *head; struct list_head *tmp, *head;
int nframes, nerrs; 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 += uhci_show_root_hub_state(uhci, out, len - (out - buf));
out += sprintf(out, "HC status\n"); 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; nframes = 10;
nerrs = 0; nerrs = 0;
for (i = 0; i < UHCI_NUMFRAMES; ++i) { for (i = 0; i < UHCI_NUMFRAMES; ++i) {
__le32 link, qh_dma; __le32 qh_dma;
j = 0; j = 0;
td = uhci->frame_cpu[i]; td = uhci->frame_cpu[i];
@ -393,7 +389,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
do { do {
td = list_entry(tmp, struct uhci_td, fl_list); td = list_entry(tmp, struct uhci_td, fl_list);
tmp = tmp->next; tmp = tmp->next;
if (cpu_to_le32(td->dma_handle) != link) { if (link != LINK_TO_TD(td)) {
if (nframes > 0) if (nframes > 0)
out += sprintf(out, " link does " out += sprintf(out, " link does "
"not match list entry!\n"); "not match list entry!\n");
@ -430,23 +426,21 @@ check_link:
for (i = 0; i < UHCI_NUM_SKELQH; ++i) { for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
int cnt = 0; int cnt = 0;
__le32 fsbr_link = 0;
qh = uhci->skelqh[i]; 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); out += uhci_show_qh(qh, out, len - (out - buf), 4);
/* Last QH is the Terminating QH, it's different */ /* Last QH is the Terminating QH, it's different */
if (i == UHCI_NUM_SKELQH - 1) { if (i == SKEL_TERM) {
if (qh->link != UHCI_PTR_TERM) if (qh_element(qh) != LINK_TO_TD(uhci->term_td))
out += sprintf(out, " bandwidth reclamation on!\n");
if (qh_element(qh) != cpu_to_le32(uhci->term_td->dma_handle))
out += sprintf(out, " skel_term_qh element is not set to term_td!\n"); 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; continue;
} }
j = (i < 9) ? 9 : i+1; /* Next skeleton */
head = &qh->node; head = &qh->node;
tmp = head->next; tmp = head->next;
@ -456,15 +450,26 @@ check_link:
if (++cnt <= 10) if (++cnt <= 10)
out += uhci_show_qh(qh, out, out += uhci_show_qh(qh, out,
len - (out - buf), 4); len - (out - buf), 4);
if (!fsbr_link && qh->skel >= SKEL_FSBR)
fsbr_link = LINK_TO_QH(qh);
} }
if ((cnt -= 10) > 0) if ((cnt -= 10) > 0)
out += sprintf(out, " Skipped %d QHs\n", cnt); out += sprintf(out, " Skipped %d QHs\n", cnt);
if (i > 1 && i < UHCI_NUM_SKELQH - 1) { link = UHCI_PTR_TERM;
if (qh->link != if (i <= SKEL_ISO)
(cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH)) ;
out += sprintf(out, " last QH not linked to next skeleton!\n"); 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; return out - buf;

View File

@ -13,7 +13,7 @@
* (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
* support from usb-ohci.c by Adam Richter, adam@yggdrasil.com). * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
* (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c) * (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 * Intel documents this fairly well, and as far as I know there
* are no royalties or anything like that, but even so there are * 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. * interrupt QHs, which will help spread out bandwidth utilization.
* *
* ffs (Find First bit Set) does exactly what we need: * ffs (Find First bit Set) does exactly what we need:
* 1,3,5,... => ffs = 0 => use skel_int2_qh = skelqh[8], * 1,3,5,... => ffs = 0 => use period-2 QH = skelqh[8],
* 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[7], etc. * 2,6,10,... => ffs = 1 => use period-4 QH = skelqh[7], etc.
* ffs >= 7 => not on any high-period queue, so use * 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. * Add in UHCI_NUMFRAMES to insure at least one bit is set.
*/ */
skelnum = 8 - (int) __ffs(frame | UHCI_NUMFRAMES); skelnum = 8 - (int) __ffs(frame | UHCI_NUMFRAMES);
if (skelnum <= 1) if (skelnum <= 1)
skelnum = 9; 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" #include "uhci-debug.c"
@ -540,16 +540,18 @@ static void uhci_shutdown(struct pci_dev *pdev)
* *
* The hardware doesn't really know any difference * The hardware doesn't really know any difference
* in the queues, but the order does matter for the * 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 * of the queues. We don't do that here, because
* we'll create the actual TD entries on demand. * we'll create the actual TD entries on demand.
* - The first queue is the interrupt queue. * - The first queue is the high-period interrupt queue.
* - The second queue is the control queue, split into low- and full-speed * - The second queue is the period-1 interrupt and async
* - The third queue is bulk queue. * (low-speed control, full-speed control, then bulk) queue.
* - The fourth queue is the bandwidth reclamation queue, which loops back * - The third queue is the terminating bandwidth reclamation queue,
* to the full-speed control 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) 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, * 8 Interrupt queues; link all higher int queues to int1 = async
* then link int1 to control and control to bulk
*/ */
uhci->skel_int128_qh->link = for (i = SKEL_ISO + 1; i < SKEL_ASYNC; ++i)
uhci->skel_int64_qh->link = uhci->skelqh[i]->link = LINK_TO_QH(uhci->skel_async_qh);
uhci->skel_int32_qh->link = uhci->skel_async_qh->link = uhci->skel_term_qh->link = UHCI_PTR_TERM;
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);
/* This dummy TD is to work around a bug in Intel PIIX controllers */ /* This dummy TD is to work around a bug in Intel PIIX controllers */
uhci_fill_td(uhci->term_td, 0, uhci_explen(0) | uhci_fill_td(uhci->term_td, 0, uhci_explen(0) |
(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0); (0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
uhci->term_td->link = cpu_to_le32(uhci->term_td->dma_handle); uhci->term_td->link = UHCI_PTR_TERM;
uhci->skel_async_qh->element = uhci->skel_term_qh->element =
uhci->skel_term_qh->link = UHCI_PTR_TERM; LINK_TO_TD(uhci->term_td);
uhci->skel_term_qh->element = cpu_to_le32(uhci->term_td->dma_handle);
/* /*
* Fill the frame list: make all entries point to the proper * Fill the frame list: make all entries point to the proper

View File

@ -129,11 +129,12 @@ struct uhci_qh {
__le32 element; /* Queue element (TD) pointer */ __le32 element; /* Queue element (TD) pointer */
/* Software fields */ /* Software fields */
dma_addr_t dma_handle;
struct list_head node; /* Node in the list of QHs */ struct list_head node; /* Node in the list of QHs */
struct usb_host_endpoint *hep; /* Endpoint information */ struct usb_host_endpoint *hep; /* Endpoint information */
struct usb_device *udev; struct usb_device *udev;
struct list_head queue; /* Queue of urbps for this QH */ 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 *dummy_td; /* Dummy TD to end the queue */
struct uhci_td *post_td; /* Last TD completed */ struct uhci_td *post_td; /* Last TD completed */
@ -149,8 +150,7 @@ struct uhci_qh {
int state; /* QH_STATE_xxx; see above */ int state; /* QH_STATE_xxx; see above */
int type; /* Queue type (control, bulk, etc) */ int type; /* Queue type (control, bulk, etc) */
int skel; /* Skeleton queue number */
dma_addr_t dma_handle;
unsigned int initial_toggle:1; /* Endpoint's current toggle value */ unsigned int initial_toggle:1; /* Endpoint's current toggle value */
unsigned int needs_fixup:1; /* Must fix the TD toggle values */ 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; return element;
} }
#define LINK_TO_QH(qh) (UHCI_PTR_QH | cpu_to_le32((qh)->dma_handle))
/* /*
* Transfer Descriptors * Transfer Descriptors
@ -264,6 +266,8 @@ static inline u32 td_status(struct uhci_td *td) {
return le32_to_cpu(status); return le32_to_cpu(status);
} }
#define LINK_TO_TD(td) (cpu_to_le32((td)->dma_handle))
/* /*
* Skeleton Queue Headers * 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 * The UHCI driver uses QHs with Interrupt, Control and Bulk URBs for
* automatic queuing. To make it easy to insert entries into the schedule, * automatic queuing. To make it easy to insert entries into the schedule,
* we have a skeleton of QHs for each predefined Interrupt latency, * we have a skeleton of QHs for each predefined Interrupt latency.
* low-speed control, full-speed control, bulk, and terminating QH * Asynchronous QHs (low-speed control, full-speed control, and bulk)
* (see explanation for the terminating QH below). * 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 * When we want to add a new QH, we add it to the list starting from the
* skeleton QH. For instance, the schedule list can look like this: * appropriate skeleton QH. For instance, the schedule can look like this:
* *
* skel int128 QH * skel int128 QH
* dev 1 interrupt QH * dev 1 interrupt QH
@ -285,50 +290,47 @@ static inline u32 td_status(struct uhci_td *td) {
* skel int64 QH * skel int64 QH
* skel int32 QH * skel int32 QH
* ... * ...
* skel int1 QH * skel int1 + async QH
* skel low-speed control QH * dev 5 low-speed control QH
* dev 5 control QH
* skel full-speed control QH
* skel bulk QH
* dev 1 bulk QH * dev 1 bulk QH
* dev 2 bulk QH * dev 2 bulk QH
* skel terminating QH
* *
* The terminating QH is used for 2 reasons: * There is a special terminating QH used to keep full-speed bandwidth
* - To place a terminating TD which is used to workaround a PIIX bug * reclamation active when no full-speed control or bulk QHs are linked
* (see Intel errata for explanation), and * into the schedule. It has an inactive TD (to work around a PIIX bug,
* - To loop back to the full-speed control queue for full-speed bandwidth * see the Intel errata) and it points back to itself.
* reclamation.
* *
* There's a special skeleton QH for Isochronous QHs. It never appears * There's a special skeleton QH for Isochronous QHs which never appears
* on the schedule, and Isochronous TDs go on the schedule before the * on the schedule. Isochronous TDs go on the schedule before the
* the skeleton QHs. The hardware accesses them directly rather than * the skeleton QHs. The hardware accesses them directly rather than
* through their QH, which is used only for bookkeeping purposes. * through their QH, which is used only for bookkeeping purposes.
* While the UHCI spec doesn't forbid the use of QHs for Isochronous, * 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 * it doesn't use them either. And the spec says that queues never
* advance on an error completion status, which makes them totally * advance on an error completion status, which makes them totally
* unsuitable for Isochronous transfers. * 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 UHCI_NUM_SKELQH 11
#define skel_unlink_qh skelqh[0] #define SKEL_UNLINK 0
#define skel_iso_qh skelqh[1] #define skel_unlink_qh skelqh[SKEL_UNLINK]
#define skel_int128_qh skelqh[2] #define SKEL_ISO 1
#define skel_int64_qh skelqh[3] #define skel_iso_qh skelqh[SKEL_ISO]
#define skel_int32_qh skelqh[4] /* int128, int64, ..., int1 = 2, 3, ..., 9 */
#define skel_int16_qh skelqh[5] #define SKEL_INDEX(exponent) (9 - exponent)
#define skel_int8_qh skelqh[6] #define SKEL_ASYNC 9
#define skel_int4_qh skelqh[7] #define skel_async_qh skelqh[SKEL_ASYNC]
#define skel_int2_qh skelqh[8] #define SKEL_TERM 10
#define skel_int1_qh skelqh[9] #define skel_term_qh skelqh[SKEL_TERM]
#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)
/* 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 * The UHCI controller and root hub

View File

@ -13,7 +13,7 @@
* (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
* support from usb-ohci.c by Adam Richter, adam@yggdrasil.com). * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
* (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c) * (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) static void uhci_fsbr_on(struct uhci_hcd *uhci)
{ {
struct uhci_qh *fsbr_qh, *lqh, *tqh;
uhci->fsbr_is_on = 1; uhci->fsbr_is_on = 1;
uhci->skel_term_qh->link = cpu_to_le32( lqh = list_entry(uhci->skel_async_qh->node.prev,
uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH; 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) static void uhci_fsbr_off(struct uhci_hcd *uhci)
{ {
struct uhci_qh *lqh;
uhci->fsbr_is_on = 0; 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) 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; td->link = ltd->link;
wmb(); wmb();
ltd->link = cpu_to_le32(td->dma_handle); ltd->link = LINK_TO_TD(td);
} else { } else {
td->link = uhci->frame[framenum]; td->link = uhci->frame[framenum];
wmb(); wmb();
uhci->frame[framenum] = cpu_to_le32(td->dma_handle); uhci->frame[framenum] = LINK_TO_TD(td);
uhci->frame_cpu[framenum] = 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; struct uhci_td *ntd;
ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list); 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; uhci->frame_cpu[td->frame] = ntd;
} }
} else { } else {
@ -404,13 +432,82 @@ static void uhci_fixup_toggles(struct uhci_qh *qh, int skip_first)
qh->needs_fixup = 0; qh->needs_fixup = 0;
} }
/*
* Link an Isochronous QH into its skeleton's list
*/
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 * Put a QH on the schedule in both hardware and software
*/ */
static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
{ {
struct uhci_qh *pqh;
WARN_ON(list_empty(&qh->queue)); WARN_ON(list_empty(&qh->queue));
/* Set the element pointer if it isn't set already. /* 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 *td = list_entry(urbp->td_list.next,
struct uhci_td, list); 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 */ /* 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; return;
qh->state = QH_STATE_ACTIVE; 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 */ * skeleton's list */
if (qh == uhci->next_qh) if (qh == uhci->next_qh)
uhci->next_qh = list_entry(qh->node.next, struct uhci_qh, uhci->next_qh = list_entry(qh->node.next, struct uhci_qh,
node); 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); pqh = list_entry(qh->node.prev, struct uhci_qh, node);
qh->link = pqh->link; pqh->link = qh->link;
wmb(); mb();
pqh->link = UHCI_PTR_QH | cpu_to_le32(qh->dma_handle); }
/*
* 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) static void uhci_unlink_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
{ {
struct uhci_qh *pqh;
if (qh->state == QH_STATE_UNLINKING) if (qh->state == QH_STATE_UNLINKING)
return; return;
WARN_ON(qh->state != QH_STATE_ACTIVE || !qh->udev); WARN_ON(qh->state != QH_STATE_ACTIVE || !qh->udev);
qh->state = QH_STATE_UNLINKING; qh->state = QH_STATE_UNLINKING;
/* Unlink the QH from the schedule and record when we did it */ /* Unlink the QH from the schedule and record when we did it */
pqh = list_entry(qh->node.prev, struct uhci_qh, node); if (qh->skel == SKEL_ISO)
pqh->link = qh->link; ;
mb(); else if (qh->skel < SKEL_ASYNC)
unlink_interrupt(uhci, qh);
else
unlink_async(uhci, qh);
uhci_get_current_frame_number(uhci); uhci_get_current_frame_number(uhci);
qh->unlink_frame = uhci->frame_number; 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; dma_addr_t data = urb->transfer_dma;
__le32 *plink; __le32 *plink;
struct urb_priv *urbp = urb->hcpriv; struct urb_priv *urbp = urb->hcpriv;
int skel;
/* The "pipe" thing contains the destination in bits 8--18 */ /* The "pipe" thing contains the destination in bits 8--18 */
destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; 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); td = uhci_alloc_td(uhci);
if (!td) if (!td)
goto nomem; goto nomem;
*plink = cpu_to_le32(td->dma_handle); *plink = LINK_TO_TD(td);
/* Alternate Data0/1 (start with Data1) */ /* Alternate Data0/1 (start with Data1) */
destination ^= TD_TOKEN_TOGGLE; 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); td = uhci_alloc_td(uhci);
if (!td) if (!td)
goto nomem; 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 * 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); td = uhci_alloc_td(uhci);
if (!td) if (!td)
goto nomem; 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); uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0);
wmb(); wmb();
@ -797,11 +942,13 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
* isn't in the CONFIGURED state. */ * isn't in the CONFIGURED state. */
if (urb->dev->speed == USB_SPEED_LOW || if (urb->dev->speed == USB_SPEED_LOW ||
urb->dev->state != USB_STATE_CONFIGURED) urb->dev->state != USB_STATE_CONFIGURED)
qh->skel = uhci->skel_ls_control_qh; skel = SKEL_LS_CONTROL;
else { else {
qh->skel = uhci->skel_fs_control_qh; skel = SKEL_FS_CONTROL;
uhci_add_fsbr(uhci, urb); uhci_add_fsbr(uhci, urb);
} }
if (qh->state != QH_STATE_ACTIVE)
qh->skel = skel;
urb->actual_length = -8; /* Account for the SETUP packet */ urb->actual_length = -8; /* Account for the SETUP packet */
return 0; return 0;
@ -860,7 +1007,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
td = uhci_alloc_td(uhci); td = uhci_alloc_td(uhci);
if (!td) if (!td)
goto nomem; goto nomem;
*plink = cpu_to_le32(td->dma_handle); *plink = LINK_TO_TD(td);
} }
uhci_add_td_to_urbp(td, urbp); uhci_add_td_to_urbp(td, urbp);
uhci_fill_td(td, status, 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); td = uhci_alloc_td(uhci);
if (!td) if (!td)
goto nomem; goto nomem;
*plink = cpu_to_le32(td->dma_handle); *plink = LINK_TO_TD(td);
uhci_add_td_to_urbp(td, urbp); uhci_add_td_to_urbp(td, urbp);
uhci_fill_td(td, status, 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); td = uhci_alloc_td(uhci);
if (!td) if (!td)
goto nomem; 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); uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0);
wmb(); wmb();
@ -931,7 +1078,7 @@ nomem:
return -ENOMEM; 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) struct uhci_qh *qh)
{ {
int ret; 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) if (urb->dev->speed == USB_SPEED_LOW)
return -EINVAL; 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); ret = uhci_submit_common(uhci, urb, qh);
if (ret == 0) if (ret == 0)
uhci_add_fsbr(uhci, urb); uhci_add_fsbr(uhci, urb);
@ -968,7 +1116,7 @@ static int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,
if (exponent < 0) if (exponent < 0)
return -EINVAL; return -EINVAL;
qh->period = 1 << exponent; 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 /* For now, interrupt phase is fixed by the layout
* of the QH lists. */ * 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 queue at the status stage transaction, which is
* the last TD. */ * the last TD. */
WARN_ON(list_empty(&urbp->td_list)); 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; tmp = td->list.prev;
ret = -EINPROGRESS; ret = -EINPROGRESS;
@ -1216,7 +1364,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
qh->iso_status = 0; qh->iso_status = 0;
} }
qh->skel = uhci->skel_iso_qh; qh->skel = SKEL_ISO;
if (!qh->bandwidth_reserved) if (!qh->bandwidth_reserved)
uhci_reserve_bandwidth(uhci, qh); uhci_reserve_bandwidth(uhci, qh);
return 0; 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)) { if (time_after(jiffies, qh->advance_jiffies + QH_WAIT_TIMEOUT)) {
/* Detect the Intel bug and work around it */ /* Detect the Intel bug and work around it */
if (qh->post_td && qh_element(qh) == if (qh->post_td && qh_element(qh) == LINK_TO_TD(qh->post_td)) {
cpu_to_le32(qh->post_td->dma_handle)) {
qh->element = qh->post_td->link; qh->element = qh->post_td->link;
qh->advance_jiffies = jiffies; qh->advance_jiffies = jiffies;
ret = 1; ret = 1;

View File

@ -31,6 +31,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/usb/input.h> #include <linux/usb/input.h>
#include <linux/hid.h>
/* /*
* Version Information * Version Information
@ -330,7 +331,8 @@ static void usb_kbd_disconnect(struct usb_interface *intf)
} }
static struct usb_device_id usb_kbd_id_table [] = { 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 */ { } /* Terminating entry */
}; };

View File

@ -31,6 +31,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/usb/input.h> #include <linux/usb/input.h>
#include <linux/hid.h>
/* /*
* Version Information * Version Information
@ -213,7 +214,8 @@ static void usb_mouse_disconnect(struct usb_interface *intf)
} }
static struct usb_device_id usb_mouse_id_table [] = { 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 */ { } /* Terminating entry */
}; };

View File

@ -163,7 +163,7 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
} }
id = STYLUS_DEVICE_ID; id = STYLUS_DEVICE_ID;
if (data[1] & 0x10) { /* in prox */ if (data[1] & 0x80) { /* in prox */
switch ((data[1] >> 5) & 3) { 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); wacom_report_abs(wcombo, ABS_DISTANCE, data[7] & 0x3f);
break; break;
} }
}
if (data[1] & 0x90) {
x = wacom_le16_to_cpu(&data[2]); x = wacom_le16_to_cpu(&data[2]);
y = wacom_le16_to_cpu(&data[4]); y = wacom_le16_to_cpu(&data[4]);
wacom_report_abs(wcombo, ABS_X, x); 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_key(wcombo, BTN_STYLUS2, data[1] & 0x04);
} }
wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */ 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); 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_report_key(wcombo, wacom->tool[0], 0);
wacom_input_sync(wcombo); }
/* send pad data */ /* send pad data */
if (wacom->features->type == WACOM_G4) { 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->id[1] = 1;
wacom->serial[1] = (data[7] & 0xf8); wacom->serial[1] = (data[7] & 0xf8);
wacom_report_key(wcombo, BTN_0, (data[7] & 0x40)); 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); rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3);
wacom_report_rel(wcombo, REL_WHEEL, rw); wacom_report_rel(wcombo, REL_WHEEL, rw);
wacom_report_key(wcombo, BTN_TOOL_FINGER, 0xf0); 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); wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
} else if (wacom->id[1]) { } else if (wacom->id[1]) {
wacom_input_sync(wcombo); /* sync last event */
wacom->id[1] = 0; 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_key(wcombo, BTN_TOOL_FINGER, 0);
wacom_report_abs(wcombo, ABS_MISC, 0);
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); 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 */ default: /* Unknown tool */
wacom->tool[idx] = BTN_TOOL_PEN; 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; return 1;
} }
/* Exit report */ /* Exit report */
if ((data[1] & 0xfe) == 0x80) { if ((data[1] & 0xfe) == 0x80) {
if(!((wacom->tool[idx] == BTN_TOOL_LENS) wacom_report_abs(wcombo, ABS_X, 0);
&& ((wacom->features->type == INTUOS3) wacom_report_abs(wcombo, ABS_Y, 0);
|| (wacom->features->type == INTUOS3S)))) { wacom_report_abs(wcombo, ABS_DISTANCE, 0);
wacom_report_key(wcombo, wacom->tool[idx], 0); if (wacom->tool[idx] >= BTN_TOOL_MOUSE) {
wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */ wacom_report_key(wcombo, BTN_LEFT, 0);
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]); wacom_report_key(wcombo, BTN_MIDDLE, 0);
return 2; 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; 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); wacom_report_key(wcombo, wacom->tool[1], 1);
else else
wacom_report_key(wcombo, wacom->tool[1], 0); 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); wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xffffffff);
return 1; return 1;
} }
@ -403,6 +422,12 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
if (result) if (result)
return result-1; 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 */ /* Cintiq doesn't send data when RDY bit isn't set */
if ((wacom->features->type == CINTIQ) && !(data[1] & 0x40)) if ((wacom->features->type == CINTIQ) && !(data[1] & 0x40))
return 0; return 0;
@ -554,11 +579,11 @@ static struct wacom_features wacom_features[] = {
{ "Wacom Volito2 4x5", 8, 5104, 3712, 511, 63, GRAPHIRE }, { "Wacom Volito2 4x5", 8, 5104, 3712, 511, 63, GRAPHIRE },
{ "Wacom Volito2 2x3", 8, 3248, 2320, 511, 63, GRAPHIRE }, { "Wacom Volito2 2x3", 8, 3248, 2320, 511, 63, GRAPHIRE },
{ "Wacom PenPartner2", 8, 3250, 2320, 255, 63, GRAPHIRE }, { "Wacom PenPartner2", 8, 3250, 2320, 255, 63, GRAPHIRE },
{ "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 63, INTUOS }, { "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 31, INTUOS },
{ "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 63, INTUOS }, { "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 31, INTUOS },
{ "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 63, INTUOS }, { "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 31, INTUOS },
{ "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 63, INTUOS }, { "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 31, INTUOS },
{ "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 63, INTUOS }, { "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 31, INTUOS },
{ "Wacom PL400", 8, 5408, 4056, 255, 0, PL }, { "Wacom PL400", 8, 5408, 4056, 255, 0, PL },
{ "Wacom PL500", 8, 6144, 4608, 255, 0, PL }, { "Wacom PL500", 8, 6144, 4608, 255, 0, PL },
{ "Wacom PL600", 8, 6126, 4604, 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 DTF521", 8, 6282, 4762, 511, 0, PL },
{ "Wacom DTF720", 8, 6858, 5506, 511, 0, PL }, { "Wacom DTF720", 8, 6858, 5506, 511, 0, PL },
{ "Wacom Cintiq Partner",8, 20480, 15360, 511, 0, PTU }, { "Wacom Cintiq Partner",8, 20480, 15360, 511, 0, PTU },
{ "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 63, INTUOS }, { "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 31, INTUOS },
{ "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 63, INTUOS }, { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 31, INTUOS },
{ "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 63, INTUOS }, { "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 31, INTUOS },
{ "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 63, INTUOS }, { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 31, INTUOS },
{ "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 63, INTUOS }, { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 31, INTUOS },
{ "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 63, INTUOS3S }, { "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 63, INTUOS3S },
{ "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 63, INTUOS3 }, { "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 63, INTUOS3 },
{ "Wacom Intuos3 9x12", 10, 60960, 45720, 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 6x11", 10, 54204, 31750, 1023, 63, INTUOS3 },
{ "Wacom Intuos3 4x6", 10, 31496, 19685, 1023, 63, INTUOS3S }, { "Wacom Intuos3 4x6", 10, 31496, 19685, 1023, 63, INTUOS3S },
{ "Wacom Cintiq 21UX", 10, 87200, 65600, 1023, 63, CINTIQ }, { "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 },
{ } { }
}; };

View File

@ -12,6 +12,7 @@
#define STYLUS_DEVICE_ID 0x02 #define STYLUS_DEVICE_ID 0x02
#define CURSOR_DEVICE_ID 0x06 #define CURSOR_DEVICE_ID 0x06
#define ERASER_DEVICE_ID 0x0A #define ERASER_DEVICE_ID 0x0A
#define PAD_DEVICE_ID 0x0F
enum { enum {
PENPARTNER = 0, PENPARTNER = 0,

View File

@ -244,6 +244,20 @@ config USB_TRANCEVIBRATOR
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called trancevibrator. 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 config USB_TEST
tristate "USB testing driver (DEVELOPMENT)" tristate "USB testing driver (DEVELOPMENT)"
depends on USB && USB_DEVICEFS && EXPERIMENTAL depends on USB && USB_DEVICEFS && EXPERIMENTAL

View File

@ -13,6 +13,7 @@ obj-$(CONFIG_USB_EMI26) += emi26.o
obj-$(CONFIG_USB_EMI62) += emi62.o obj-$(CONFIG_USB_EMI62) += emi62.o
obj-$(CONFIG_USB_FTDI_ELAN) += ftdi-elan.o obj-$(CONFIG_USB_FTDI_ELAN) += ftdi-elan.o
obj-$(CONFIG_USB_IDMOUSE) += idmouse.o obj-$(CONFIG_USB_IDMOUSE) += idmouse.o
obj-$(CONFIG_USB_IOWARRIOR) += iowarrior.o
obj-$(CONFIG_USB_LCD) += usblcd.o obj-$(CONFIG_USB_LCD) += usblcd.o
obj-$(CONFIG_USB_LD) += ldusb.o obj-$(CONFIG_USB_LD) += ldusb.o
obj-$(CONFIG_USB_LED) += usbled.o obj-$(CONFIG_USB_LED) += usbled.o

View File

@ -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<EFBFBD>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);

View File

@ -1165,7 +1165,7 @@ err_dev:
return rc; return rc;
} }
void __exit mon_bin_exit(void) void mon_bin_exit(void)
{ {
cdev_del(&mon_bin_cdev); cdev_del(&mon_bin_cdev);
unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR); unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR);

View File

@ -520,7 +520,7 @@ int __init mon_text_init(void)
return 0; return 0;
} }
void __exit mon_text_exit(void) void mon_text_exit(void)
{ {
debugfs_remove(mon_dir); debugfs_remove(mon_dir);
} }

View File

@ -57,9 +57,9 @@ void mon_text_del(struct mon_bus *mbus);
// void mon_bin_add(struct mon_bus *); // void mon_bin_add(struct mon_bus *);
int __init mon_text_init(void); int __init mon_text_init(void);
void __exit mon_text_exit(void); void mon_text_exit(void);
int __init mon_bin_init(void); int __init mon_bin_init(void);
void __exit mon_bin_exit(void); void mon_bin_exit(void);
/* /*
* DMA interface. * DMA interface.

View File

@ -186,6 +186,15 @@ config USB_NET_CDCETHER
IEEE 802 "local assignment" bit is set in the address, a "usbX" IEEE 802 "local assignment" bit is set in the address, a "usbX"
name is used instead. 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 config USB_NET_GL620A
tristate "GeneSys GL620USB-A based cables" tristate "GeneSys GL620USB-A based cables"
depends on USB_USBNET depends on USB_USBNET

View File

@ -8,6 +8,7 @@ obj-$(CONFIG_USB_PEGASUS) += pegasus.o
obj-$(CONFIG_USB_RTL8150) += rtl8150.o obj-$(CONFIG_USB_RTL8150) += rtl8150.o
obj-$(CONFIG_USB_NET_AX8817X) += asix.o obj-$(CONFIG_USB_NET_AX8817X) += asix.o
obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.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_GL620A) += gl620a.o
obj-$(CONFIG_USB_NET_NET1080) += net1080.o obj-$(CONFIG_USB_NET_NET1080) += net1080.o
obj-$(CONFIG_USB_NET_PLUSB) += plusb.o obj-$(CONFIG_USB_NET_PLUSB) += plusb.o

View File

@ -1395,9 +1395,9 @@ static const struct usb_device_id products [] = {
USB_DEVICE (0x07b8, 0x420a), USB_DEVICE (0x07b8, 0x420a),
.driver_info = (unsigned long) &hawking_uf200_info, .driver_info = (unsigned long) &hawking_uf200_info,
}, { }, {
// Billionton Systems, USB2AR // Billionton Systems, USB2AR
USB_DEVICE (0x08dd, 0x90ff), USB_DEVICE (0x08dd, 0x90ff),
.driver_info = (unsigned long) &ax8817x_info, .driver_info = (unsigned long) &ax8817x_info,
}, { }, {
// ATEN UC210T // ATEN UC210T
USB_DEVICE (0x0557, 0x2009), USB_DEVICE (0x0557, 0x2009),
@ -1422,10 +1422,14 @@ static const struct usb_device_id products [] = {
// goodway corp usb gwusb2e // goodway corp usb gwusb2e
USB_DEVICE (0x1631, 0x6200), USB_DEVICE (0x1631, 0x6200),
.driver_info = (unsigned long) &ax8817x_info, .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 // ASIX AX88772 10/100
USB_DEVICE (0x0b95, 0x7720), USB_DEVICE (0x0b95, 0x7720),
.driver_info = (unsigned long) &ax88772_info, .driver_info = (unsigned long) &ax88772_info,
}, { }, {
// ASIX AX88178 10/100/1000 // ASIX AX88178 10/100/1000
USB_DEVICE (0x0b95, 0x1780), USB_DEVICE (0x0b95, 0x1780),

606
drivers/usb/net/dm9601.c Normal file
View File

@ -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");

View File

@ -513,6 +513,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13S_PID) }, { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13S_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) }, { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) },
{ USB_DEVICE(ELEKTOR_VID, ELEKTOR_FT323R_PID) }, { USB_DEVICE(ELEKTOR_VID, ELEKTOR_FT323R_PID) },
{ USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) },
{ }, /* Optional parameter entry */ { }, /* Optional parameter entry */
{ } /* Terminating entry */ { } /* Terminating entry */
}; };

View File

@ -491,6 +491,12 @@
#define FTDI_TACTRIX_OPENPORT_13S_PID 0xCC49 /* OpenPort 1.3 Subaru */ #define FTDI_TACTRIX_OPENPORT_13S_PID 0xCC49 /* OpenPort 1.3 Subaru */
#define FTDI_TACTRIX_OPENPORT_13U_PID 0xCC4A /* OpenPort 1.3 Universal */ #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 */ /* Commands */
#define FTDI_SIO_RESET 0 /* Reset the port */ #define FTDI_SIO_RESET 0 /* Reset the port */
#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */

View File

@ -67,50 +67,95 @@ static int option_tiocmset(struct usb_serial_port *port, struct file *file,
static int option_send_setup(struct usb_serial_port *port); static int option_send_setup(struct usb_serial_port *port);
/* Vendor and product IDs */ /* Vendor and product IDs */
#define OPTION_VENDOR_ID 0x0AF0 #define OPTION_VENDOR_ID 0x0AF0
#define HUAWEI_VENDOR_ID 0x12D1 #define OPTION_PRODUCT_COLT 0x5000
#define NOVATELWIRELESS_VENDOR_ID 0x1410 #define OPTION_PRODUCT_RICOLA 0x6000
#define ANYDATA_VENDOR_ID 0x16d5 #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 OPTION_PRODUCT_OLD 0x5000 #define HUAWEI_VENDOR_ID 0x12D1
#define OPTION_PRODUCT_FUSION 0x6000 #define HUAWEI_PRODUCT_E600 0x1001
#define OPTION_PRODUCT_FUSION2 0x6300 #define HUAWEI_PRODUCT_E220 0x1003
#define OPTION_PRODUCT_COBRA 0x6500
#define OPTION_PRODUCT_COBRA2 0x6600 #define NOVATELWIRELESS_VENDOR_ID 0x1410
#define OPTION_PRODUCT_GTMAX36 0x6701 #define NOVATELWIRELESS_PRODUCT_U740 0x1400
#define HUAWEI_PRODUCT_E600 0x1001
#define HUAWEI_PRODUCT_E220 0x1003 #define ANYDATA_VENDOR_ID 0x16d5
#define NOVATELWIRELESS_PRODUCT_U740 0x1400 #define ANYDATA_PRODUCT_ID 0x6501
#define ANYDATA_PRODUCT_ID 0x6501
static struct usb_device_id option_ids[] = { static struct usb_device_id option_ids[] = {
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) }, { 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_COBRA) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA_BUS) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GTMAX36) }, { 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_E600) },
{ USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220) }, { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) }, { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) },
{ } /* Terminating entry */ { } /* Terminating entry */
}; };
static struct usb_device_id option_ids1[] = {
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GTMAX36) },
{ USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
{ USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, option_ids); MODULE_DEVICE_TABLE(usb, option_ids);
static struct usb_driver option_driver = { static struct usb_driver option_driver = {
@ -132,7 +177,7 @@ static struct usb_serial_driver option_1port_device = {
}, },
.description = "GSM modem (1-port)", .description = "GSM modem (1-port)",
.usb_driver = &option_driver, .usb_driver = &option_driver,
.id_table = option_ids1, .id_table = option_ids,
.num_interrupt_in = NUM_DONT_CARE, .num_interrupt_in = NUM_DONT_CARE,
.num_bulk_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE,
.num_bulk_out = NUM_DONT_CARE, .num_bulk_out = NUM_DONT_CARE,

View File

@ -501,6 +501,30 @@ int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
} }
/**
* sysfs_add_file_to_group - add an attribute file to a pre-existing group.
* @kobj: object we're acting for.
* @attr: attribute descriptor.
* @group: group name.
*/
int sysfs_add_file_to_group(struct kobject *kobj,
const struct attribute *attr, const char *group)
{
struct dentry *dir;
int error;
dir = lookup_one_len(group, kobj->dentry, strlen(group));
if (IS_ERR(dir))
error = PTR_ERR(dir);
else {
error = sysfs_add_file(dir, attr, SYSFS_KOBJ_ATTR);
dput(dir);
}
return error;
}
EXPORT_SYMBOL_GPL(sysfs_add_file_to_group);
/** /**
* sysfs_update_file - update the modified timestamp on an object attribute. * sysfs_update_file - update the modified timestamp on an object attribute.
* @kobj: object we're acting for. * @kobj: object we're acting for.
@ -586,6 +610,26 @@ void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr)
} }
/**
* sysfs_remove_file_from_group - remove an attribute file from a group.
* @kobj: object we're acting for.
* @attr: attribute descriptor.
* @group: group name.
*/
void sysfs_remove_file_from_group(struct kobject *kobj,
const struct attribute *attr, const char *group)
{
struct dentry *dir;
dir = lookup_one_len(group, kobj->dentry, strlen(group));
if (!IS_ERR(dir)) {
sysfs_hash_and_remove(dir, attr->name);
dput(dir);
}
}
EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);
EXPORT_SYMBOL_GPL(sysfs_create_file); EXPORT_SYMBOL_GPL(sysfs_create_file);
EXPORT_SYMBOL_GPL(sysfs_remove_file); EXPORT_SYMBOL_GPL(sysfs_remove_file);
EXPORT_SYMBOL_GPL(sysfs_update_file); EXPORT_SYMBOL_GPL(sysfs_update_file);

View File

@ -126,6 +126,11 @@ void sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr);
int __must_check sysfs_create_group(struct kobject *, int __must_check sysfs_create_group(struct kobject *,
const struct attribute_group *); const struct attribute_group *);
void sysfs_remove_group(struct kobject *, const struct attribute_group *); void sysfs_remove_group(struct kobject *, const struct attribute_group *);
int sysfs_add_file_to_group(struct kobject *kobj,
const struct attribute *attr, const char *group);
void sysfs_remove_file_from_group(struct kobject *kobj,
const struct attribute *attr, const char *group);
void sysfs_notify(struct kobject * k, char *dir, char *attr); void sysfs_notify(struct kobject * k, char *dir, char *attr);
@ -210,6 +215,18 @@ static inline void sysfs_remove_group(struct kobject * k, const struct attribute
; ;
} }
static inline int sysfs_add_file_to_group(struct kobject *kobj,
const struct attribute *attr, const char *group)
{
return 0;
}
static inline void sysfs_remove_file_from_group(struct kobject *kobj,
const struct attribute *attr, const char *group);
{
;
}
static inline void sysfs_notify(struct kobject * k, char *dir, char *attr) static inline void sysfs_notify(struct kobject * k, char *dir, char *attr)
{ {
} }

View File

@ -388,10 +388,14 @@ struct usb_device {
struct usb_device *children[USB_MAXCHILDREN]; struct usb_device *children[USB_MAXCHILDREN];
int pm_usage_cnt; /* usage counter for autosuspend */ int pm_usage_cnt; /* usage counter for autosuspend */
u32 quirks; /* quirks of the whole device */
#ifdef CONFIG_PM #ifdef CONFIG_PM
struct delayed_work autosuspend; /* for delayed autosuspends */ struct delayed_work autosuspend; /* for delayed autosuspends */
struct mutex pm_mutex; /* protects PM operations */ struct mutex pm_mutex; /* protects PM operations */
unsigned autosuspend_delay; /* in jiffies */
unsigned auto_pm:1; /* autosuspend/resume in progress */ unsigned auto_pm:1; /* autosuspend/resume in progress */
unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */ unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */
#endif #endif

View File

@ -1,8 +1,9 @@
/* /*
* This file holds USB constants and structures that are needed for USB * This file holds USB constants and structures that are needed for
* device APIs. These are used by the USB device model, which is defined * USB device APIs. These are used by the USB device model, which is
* in chapter 9 of the USB 2.0 specification. Linux has several APIs in C * defined in chapter 9 of the USB 2.0 specification and in the
* that need these: * Wireless USB 1.0 (spread around). Linux has several APIs in C that
* need these:
* *
* - the master/host side Linux-USB kernel driver API; * - the master/host side Linux-USB kernel driver API;
* - the "usbfs" user space API; and * - the "usbfs" user space API; and
@ -14,6 +15,19 @@
* *
* There's also "Wireless USB", using low power short range radios for * There's also "Wireless USB", using low power short range radios for
* peripheral interconnection but otherwise building on the USB framework. * peripheral interconnection but otherwise building on the USB framework.
*
* Note all descriptors are declared '__attribute__((packed))' so that:
*
* [a] they never get padded, either internally (USB spec writers
* probably handled that) or externally;
*
* [b] so that accessing bigger-than-a-bytes fields will never
* generate bus errors on any platform, even when the location of
* its descriptor inside a bundle isn't "naturally aligned", and
*
* [c] for consistency, removing all doubt even when it appears to
* someone that the two other points are non-issues for that
* particular descriptor type.
*/ */
#ifndef __LINUX_USB_CH9_H #ifndef __LINUX_USB_CH9_H

View File

@ -0,0 +1,33 @@
#ifndef _IOWARRIOR_H_
#define _IOWARRIOR_H_
#define CODEMERCS_MAGIC_NUMBER 0xC0 /* like COde Mercenaries */
/* Define the ioctl commands for reading and writing data */
#define IOW_WRITE _IOW(CODEMERCS_MAGIC_NUMBER, 1, __u8 *)
#define IOW_READ _IOW(CODEMERCS_MAGIC_NUMBER, 2, __u8 *)
/*
A struct for available device info which is read
with the ioctl IOW_GETINFO.
To be compatible with 2.4 userspace which didn't have an easy way to get
this information.
*/
struct iowarrior_info {
__u32 vendor; /* vendor id : supposed to be USB_VENDOR_ID_CODEMERCS in all cases */
__u32 product; /* product id : depends on type of chip (USB_DEVICE_ID_CODEMERCS_XXXXX) */
__u8 serial[9]; /* the serial number of our chip (if a serial-number is not available this is empty string) */
__u32 revision; /* revision number of the chip */
__u32 speed; /* USB-speed of the device (0=UNKNOWN, 1=LOW, 2=FULL 3=HIGH) */
__u32 power; /* power consumption of the device in mA */
__u32 if_num; /* the number of the endpoint */
__u32 report_size; /* size of the data-packets on this interface */
};
/*
Get some device-information (product-id , serial-number etc.)
in order to identify a chip.
*/
#define IOW_GETINFO _IOR(CODEMERCS_MAGIC_NUMBER, 3, struct iowarrior_info)
#endif /* _IOWARRIOR_H_ */

View File

@ -0,0 +1,11 @@
/*
* This file holds the definitions of quirks found in USB devices.
* Only quirks that affect the whole device, not an interface,
* belong here.
*/
/* device must not be autosuspended */
#define USB_QUIRK_NO_AUTOSUSPEND 0x00000001
/* string descriptors must not be fetched using a 255-byte read */
#define USB_QUIRK_STRING_FETCH_255 0x00000002