mirror of
https://github.com/torvalds/linux.git
synced 2024-11-16 00:52:01 +00:00
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:
commit
92320cec61
@ -1758,6 +1758,13 @@ and is between 256 and 4096 characters. It is defined in the file
|
||||
Note that genuine overcurrent events won't be
|
||||
reported either.
|
||||
|
||||
usbcore.autosuspend=
|
||||
[USB] The autosuspend time delay (in seconds) used
|
||||
for newly-detected USB devices (default 2). This
|
||||
is the time required before an idle device will be
|
||||
autosuspended. Devices for which the delay is set
|
||||
to 0 won't be autosuspended at all.
|
||||
|
||||
usbhid.mousepoll=
|
||||
[USBHID] The interval which mice are to be polled at.
|
||||
|
||||
|
@ -3392,6 +3392,13 @@ L: linux-usb-devel@lists.sourceforge.net
|
||||
S: Maintained
|
||||
W: http://www.kroah.com/linux-usb/
|
||||
|
||||
USB DAVICOM DM9601 DRIVER
|
||||
P: Peter Korsgaard
|
||||
M: jacmet@sunsite.dk
|
||||
L: linux-usb-devel@lists.sourceforge.net
|
||||
W: http://www.linux-usb.org/usbnet
|
||||
S: Maintained
|
||||
|
||||
USB EHCI DRIVER
|
||||
P: David Brownell
|
||||
M: dbrownell@users.sourceforge.net
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \
|
||||
config.o file.o buffer.o sysfs.o endpoint.o \
|
||||
devio.o notify.o generic.o
|
||||
devio.o notify.o generic.o quirks.o
|
||||
|
||||
ifeq ($(CONFIG_PCI),y)
|
||||
usbcore-objs += hcd-pci.o
|
||||
|
@ -366,19 +366,8 @@ void usb_driver_release_interface(struct usb_driver *driver,
|
||||
EXPORT_SYMBOL(usb_driver_release_interface);
|
||||
|
||||
/* returns 0 if no match, 1 if match */
|
||||
int usb_match_one_id(struct usb_interface *interface,
|
||||
const struct usb_device_id *id)
|
||||
int usb_match_device(struct usb_device *dev, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_host_interface *intf;
|
||||
struct usb_device *dev;
|
||||
|
||||
/* proc_connectinfo in devio.c may call us with id == NULL. */
|
||||
if (id == NULL)
|
||||
return 0;
|
||||
|
||||
intf = interface->cur_altsetting;
|
||||
dev = interface_to_usbdev(interface);
|
||||
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
|
||||
id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
|
||||
return 0;
|
||||
@ -409,6 +398,26 @@ int usb_match_one_id(struct usb_interface *interface,
|
||||
(id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns 0 if no match, 1 if match */
|
||||
int usb_match_one_id(struct usb_interface *interface,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_host_interface *intf;
|
||||
struct usb_device *dev;
|
||||
|
||||
/* proc_connectinfo in devio.c may call us with id == NULL. */
|
||||
if (id == NULL)
|
||||
return 0;
|
||||
|
||||
intf = interface->cur_altsetting;
|
||||
dev = interface_to_usbdev(interface);
|
||||
|
||||
if (!usb_match_device(dev, id))
|
||||
return 0;
|
||||
|
||||
/* The interface class, subclass, and protocol should never be
|
||||
* checked for a match if the device class is Vendor Specific,
|
||||
* unless the match record specifies the Vendor ID. */
|
||||
@ -954,12 +963,16 @@ static int autosuspend_check(struct usb_device *udev)
|
||||
int i;
|
||||
struct usb_interface *intf;
|
||||
|
||||
/* For autosuspend, fail fast if anything is in use.
|
||||
* Also fail if any interfaces require remote wakeup but it
|
||||
* isn't available. */
|
||||
/* For autosuspend, fail fast if anything is in use or autosuspend
|
||||
* is disabled. Also fail if any interfaces require remote wakeup
|
||||
* but it isn't available.
|
||||
*/
|
||||
udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
|
||||
if (udev->pm_usage_cnt > 0)
|
||||
return -EBUSY;
|
||||
if (!udev->autosuspend_delay)
|
||||
return -EPERM;
|
||||
|
||||
if (udev->actconfig) {
|
||||
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
|
||||
intf = udev->actconfig->interface[i];
|
||||
@ -982,7 +995,7 @@ static int autosuspend_check(struct usb_device *udev)
|
||||
|
||||
#define autosuspend_check(udev) 0
|
||||
|
||||
#endif
|
||||
#endif /* CONFIG_USB_SUSPEND */
|
||||
|
||||
/**
|
||||
* usb_suspend_both - suspend a USB device and its interfaces
|
||||
@ -1177,7 +1190,7 @@ static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt)
|
||||
udev->pm_usage_cnt -= inc_usage_cnt;
|
||||
} else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0)
|
||||
queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
|
||||
USB_AUTOSUSPEND_DELAY);
|
||||
udev->autosuspend_delay);
|
||||
usb_pm_unlock(udev);
|
||||
return status;
|
||||
}
|
||||
@ -1211,6 +1224,26 @@ void usb_autosuspend_device(struct usb_device *udev)
|
||||
// __FUNCTION__, udev->pm_usage_cnt);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_try_autosuspend_device - attempt an autosuspend of a USB device and its interfaces
|
||||
* @udev: the usb_device to autosuspend
|
||||
*
|
||||
* This routine should be called when a core subsystem thinks @udev may
|
||||
* be ready to autosuspend.
|
||||
*
|
||||
* @udev's usage counter left unchanged. If it or any of the usage counters
|
||||
* for an active interface is greater than 0, or autosuspend is not allowed
|
||||
* for any other reason, no autosuspend request will be queued.
|
||||
*
|
||||
* This routine can run only in process context.
|
||||
*/
|
||||
void usb_try_autosuspend_device(struct usb_device *udev)
|
||||
{
|
||||
usb_autopm_do_device(udev, 0);
|
||||
// dev_dbg(&udev->dev, "%s: cnt %d\n",
|
||||
// __FUNCTION__, udev->pm_usage_cnt);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_autoresume_device - immediately autoresume a USB device and its interfaces
|
||||
* @udev: the usb_device to autoresume
|
||||
@ -1261,7 +1294,7 @@ static int usb_autopm_do_interface(struct usb_interface *intf,
|
||||
intf->pm_usage_cnt -= inc_usage_cnt;
|
||||
} else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0)
|
||||
queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
|
||||
USB_AUTOSUSPEND_DELAY);
|
||||
udev->autosuspend_delay);
|
||||
}
|
||||
usb_pm_unlock(udev);
|
||||
return status;
|
||||
|
@ -1287,6 +1287,9 @@ int usb_new_device(struct usb_device *udev)
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return -EINVAL;
|
||||
|
||||
/* Determine quirks */
|
||||
usb_detect_quirks(udev);
|
||||
|
||||
err = usb_get_configuration(udev);
|
||||
if (err < 0) {
|
||||
dev_err(&udev->dev, "can't read configurations, error %d\n",
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/timer.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/usb/quirks.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/scatterlist.h>
|
||||
|
||||
@ -685,6 +686,9 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid,
|
||||
|
||||
/* Try to read the string descriptor by asking for the maximum
|
||||
* possible number of bytes */
|
||||
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
|
||||
|
77
drivers/usb/core/quirks.c
Normal file
77
drivers/usb/core/quirks.c
Normal 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);
|
||||
}
|
@ -148,6 +148,75 @@ show_maxchild(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
}
|
||||
static DEVICE_ATTR(maxchild, S_IRUGO, show_maxchild, NULL);
|
||||
|
||||
static ssize_t
|
||||
show_quirks(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
return sprintf(buf, "0x%x\n", udev->quirks);
|
||||
}
|
||||
static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL);
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
|
||||
static ssize_t
|
||||
show_autosuspend(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", udev->autosuspend_delay / HZ);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
set_autosuspend(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
unsigned value, old;
|
||||
|
||||
if (sscanf(buf, "%u", &value) != 1 || value >= INT_MAX/HZ)
|
||||
return -EINVAL;
|
||||
value *= HZ;
|
||||
|
||||
old = udev->autosuspend_delay;
|
||||
udev->autosuspend_delay = value;
|
||||
if (value > 0 && old == 0)
|
||||
usb_try_autosuspend_device(udev);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR,
|
||||
show_autosuspend, set_autosuspend);
|
||||
|
||||
static char power_group[] = "power";
|
||||
|
||||
static int add_power_attributes(struct device *dev)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (is_usb_device(dev))
|
||||
rc = sysfs_add_file_to_group(&dev->kobj,
|
||||
&dev_attr_autosuspend.attr,
|
||||
power_group);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void remove_power_attributes(struct device *dev)
|
||||
{
|
||||
sysfs_remove_file_from_group(&dev->kobj,
|
||||
&dev_attr_autosuspend.attr,
|
||||
power_group);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define add_power_attributes(dev) 0
|
||||
#define remove_power_attributes(dev) do {} while (0)
|
||||
|
||||
#endif /* CONFIG_USB_SUSPEND */
|
||||
|
||||
/* Descriptor fields */
|
||||
#define usb_descriptor_attr_le16(field, format_string) \
|
||||
static ssize_t \
|
||||
@ -204,6 +273,7 @@ static struct attribute *dev_attrs[] = {
|
||||
&dev_attr_devnum.attr,
|
||||
&dev_attr_version.attr,
|
||||
&dev_attr_maxchild.attr,
|
||||
&dev_attr_quirks.attr,
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group dev_attr_grp = {
|
||||
@ -219,6 +289,10 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = add_power_attributes(dev);
|
||||
if (retval)
|
||||
goto error;
|
||||
|
||||
if (udev->manufacturer) {
|
||||
retval = device_create_file(dev, &dev_attr_manufacturer);
|
||||
if (retval)
|
||||
@ -239,10 +313,7 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
|
||||
goto error;
|
||||
return 0;
|
||||
error:
|
||||
usb_remove_ep_files(&udev->ep0);
|
||||
device_remove_file(dev, &dev_attr_manufacturer);
|
||||
device_remove_file(dev, &dev_attr_product);
|
||||
device_remove_file(dev, &dev_attr_serial);
|
||||
usb_remove_sysfs_dev_files(udev);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -251,14 +322,11 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev)
|
||||
struct device *dev = &udev->dev;
|
||||
|
||||
usb_remove_ep_files(&udev->ep0);
|
||||
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);
|
||||
remove_power_attributes(dev);
|
||||
sysfs_remove_group(&dev->kobj, &dev_attr_grp);
|
||||
}
|
||||
|
||||
/* Interface fields */
|
||||
@ -362,33 +430,28 @@ static inline void usb_remove_intf_ep_files(struct usb_interface *intf)
|
||||
|
||||
int usb_create_sysfs_intf_files(struct usb_interface *intf)
|
||||
{
|
||||
struct device *dev = &intf->dev;
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
struct usb_host_interface *alt = intf->cur_altsetting;
|
||||
int retval;
|
||||
|
||||
retval = sysfs_create_group(&intf->dev.kobj, &intf_attr_grp);
|
||||
retval = sysfs_create_group(&dev->kobj, &intf_attr_grp);
|
||||
if (retval)
|
||||
goto error;
|
||||
return retval;
|
||||
|
||||
if (alt->string == NULL)
|
||||
alt->string = usb_cache_string(udev, alt->desc.iInterface);
|
||||
if (alt->string)
|
||||
retval = device_create_file(&intf->dev, &dev_attr_interface);
|
||||
retval = device_create_file(dev, &dev_attr_interface);
|
||||
usb_create_intf_ep_files(intf, udev);
|
||||
return 0;
|
||||
error:
|
||||
if (alt->string)
|
||||
device_remove_file(&intf->dev, &dev_attr_interface);
|
||||
sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp);
|
||||
usb_remove_intf_ep_files(intf);
|
||||
return retval;
|
||||
}
|
||||
|
||||
void usb_remove_sysfs_intf_files(struct usb_interface *intf)
|
||||
{
|
||||
usb_remove_intf_ep_files(intf);
|
||||
sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp);
|
||||
struct device *dev = &intf->dev;
|
||||
|
||||
if (intf->cur_altsetting->string)
|
||||
device_remove_file(&intf->dev, &dev_attr_interface);
|
||||
usb_remove_intf_ep_files(intf);
|
||||
device_remove_file(dev, &dev_attr_interface);
|
||||
sysfs_remove_group(&dev->kobj, &intf_attr_grp);
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/slab.h>
|
||||
@ -50,6 +51,16 @@ static int nousb; /* Disable USB when built into kernel image */
|
||||
|
||||
struct workqueue_struct *ksuspend_usb_wq; /* For autosuspend */
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
static int usb_autosuspend_delay = 2; /* Default delay value,
|
||||
* in seconds */
|
||||
module_param_named(autosuspend, usb_autosuspend_delay, uint, 0644);
|
||||
MODULE_PARM_DESC(autosuspend, "default autosuspend delay");
|
||||
|
||||
#else
|
||||
#define usb_autosuspend_delay 0
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* usb_ifnum_to_if - get the interface object with a given interface number
|
||||
@ -306,6 +317,7 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
|
||||
#ifdef CONFIG_PM
|
||||
mutex_init(&dev->pm_mutex);
|
||||
INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work);
|
||||
dev->autosuspend_delay = usb_autosuspend_delay * HZ;
|
||||
#endif
|
||||
return dev;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ extern void usb_disable_interface (struct usb_device *dev,
|
||||
struct usb_interface *intf);
|
||||
extern void usb_release_interface_cache(struct kref *ref);
|
||||
extern void usb_disable_device (struct usb_device *dev, int skip_ep0);
|
||||
extern void usb_detect_quirks(struct usb_device *udev);
|
||||
|
||||
extern int usb_get_device_descriptor(struct usb_device *dev,
|
||||
unsigned int size);
|
||||
@ -21,6 +22,8 @@ extern int usb_set_configuration(struct usb_device *dev, int configuration);
|
||||
|
||||
extern void usb_kick_khubd(struct usb_device *dev);
|
||||
extern void usb_resume_root_hub(struct usb_device *dev);
|
||||
extern int usb_match_device(struct usb_device *dev,
|
||||
const struct usb_device_id *id);
|
||||
|
||||
extern int usb_hub_init(void);
|
||||
extern void usb_hub_cleanup(void);
|
||||
@ -62,14 +65,14 @@ static inline void usb_pm_unlock(struct usb_device *udev) {}
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
|
||||
#define USB_AUTOSUSPEND_DELAY (HZ*2)
|
||||
|
||||
extern void usb_autosuspend_device(struct usb_device *udev);
|
||||
extern void usb_try_autosuspend_device(struct usb_device *udev);
|
||||
extern int usb_autoresume_device(struct usb_device *udev);
|
||||
|
||||
#else
|
||||
|
||||
#define usb_autosuspend_device(udev) do {} while (0)
|
||||
#define usb_try_autosuspend_device(udev) do {} while (0)
|
||||
static inline int usb_autoresume_device(struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
|
@ -553,6 +553,7 @@ static ssize_t ep_aio_read_retry(struct kiocb *iocb)
|
||||
{
|
||||
struct kiocb_priv *priv = iocb->private;
|
||||
ssize_t len, total;
|
||||
void *to_copy;
|
||||
int i;
|
||||
|
||||
/* we "retry" to get the right mm context for this: */
|
||||
@ -560,10 +561,11 @@ static ssize_t ep_aio_read_retry(struct kiocb *iocb)
|
||||
/* copy stuff into user buffers */
|
||||
total = priv->actual;
|
||||
len = 0;
|
||||
to_copy = priv->buf;
|
||||
for (i=0; i < priv->nr_segs; i++) {
|
||||
ssize_t this = min((ssize_t)(priv->iv[i].iov_len), total);
|
||||
|
||||
if (copy_to_user(priv->iv[i].iov_base, priv->buf, this)) {
|
||||
if (copy_to_user(priv->iv[i].iov_base, to_copy, this)) {
|
||||
if (len == 0)
|
||||
len = -EFAULT;
|
||||
break;
|
||||
@ -571,6 +573,7 @@ static ssize_t ep_aio_read_retry(struct kiocb *iocb)
|
||||
|
||||
total -= this;
|
||||
len += this;
|
||||
to_copy += this;
|
||||
if (total == 0)
|
||||
break;
|
||||
}
|
||||
|
@ -196,7 +196,7 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
|
||||
struct uhci_td *td = list_entry(urbp->td_list.next,
|
||||
struct uhci_td, list);
|
||||
|
||||
if (cpu_to_le32(td->dma_handle) != (element & ~UHCI_PTR_BITS))
|
||||
if (element != LINK_TO_TD(td))
|
||||
out += sprintf(out, "%*s Element != First TD\n",
|
||||
space, "");
|
||||
i = nurbs = 0;
|
||||
@ -220,16 +220,6 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
|
||||
return out - buf;
|
||||
}
|
||||
|
||||
static const char * const qh_names[] = {
|
||||
"skel_unlink_qh", "skel_iso_qh",
|
||||
"skel_int128_qh", "skel_int64_qh",
|
||||
"skel_int32_qh", "skel_int16_qh",
|
||||
"skel_int8_qh", "skel_int4_qh",
|
||||
"skel_int2_qh", "skel_int1_qh",
|
||||
"skel_ls_control_qh", "skel_fs_control_qh",
|
||||
"skel_bulk_qh", "skel_term_qh"
|
||||
};
|
||||
|
||||
static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
|
||||
{
|
||||
char *out = buf;
|
||||
@ -352,6 +342,12 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
|
||||
struct uhci_td *td;
|
||||
struct list_head *tmp, *head;
|
||||
int nframes, nerrs;
|
||||
__le32 link;
|
||||
|
||||
static const char * const qh_names[] = {
|
||||
"unlink", "iso", "int128", "int64", "int32", "int16",
|
||||
"int8", "int4", "int2", "async", "term"
|
||||
};
|
||||
|
||||
out += uhci_show_root_hub_state(uhci, out, len - (out - buf));
|
||||
out += sprintf(out, "HC status\n");
|
||||
@ -374,7 +370,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
|
||||
nframes = 10;
|
||||
nerrs = 0;
|
||||
for (i = 0; i < UHCI_NUMFRAMES; ++i) {
|
||||
__le32 link, qh_dma;
|
||||
__le32 qh_dma;
|
||||
|
||||
j = 0;
|
||||
td = uhci->frame_cpu[i];
|
||||
@ -393,7 +389,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
|
||||
do {
|
||||
td = list_entry(tmp, struct uhci_td, fl_list);
|
||||
tmp = tmp->next;
|
||||
if (cpu_to_le32(td->dma_handle) != link) {
|
||||
if (link != LINK_TO_TD(td)) {
|
||||
if (nframes > 0)
|
||||
out += sprintf(out, " link does "
|
||||
"not match list entry!\n");
|
||||
@ -430,23 +426,21 @@ check_link:
|
||||
|
||||
for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
|
||||
int cnt = 0;
|
||||
__le32 fsbr_link = 0;
|
||||
|
||||
qh = uhci->skelqh[i];
|
||||
out += sprintf(out, "- %s\n", qh_names[i]); \
|
||||
out += sprintf(out, "- skel_%s_qh\n", qh_names[i]); \
|
||||
out += uhci_show_qh(qh, out, len - (out - buf), 4);
|
||||
|
||||
/* Last QH is the Terminating QH, it's different */
|
||||
if (i == UHCI_NUM_SKELQH - 1) {
|
||||
if (qh->link != UHCI_PTR_TERM)
|
||||
out += sprintf(out, " bandwidth reclamation on!\n");
|
||||
|
||||
if (qh_element(qh) != cpu_to_le32(uhci->term_td->dma_handle))
|
||||
if (i == SKEL_TERM) {
|
||||
if (qh_element(qh) != LINK_TO_TD(uhci->term_td))
|
||||
out += sprintf(out, " skel_term_qh element is not set to term_td!\n");
|
||||
|
||||
if (link == LINK_TO_QH(uhci->skel_term_qh))
|
||||
goto check_qh_link;
|
||||
continue;
|
||||
}
|
||||
|
||||
j = (i < 9) ? 9 : i+1; /* Next skeleton */
|
||||
head = &qh->node;
|
||||
tmp = head->next;
|
||||
|
||||
@ -456,16 +450,27 @@ check_link:
|
||||
if (++cnt <= 10)
|
||||
out += uhci_show_qh(qh, out,
|
||||
len - (out - buf), 4);
|
||||
if (!fsbr_link && qh->skel >= SKEL_FSBR)
|
||||
fsbr_link = LINK_TO_QH(qh);
|
||||
}
|
||||
if ((cnt -= 10) > 0)
|
||||
out += sprintf(out, " Skipped %d QHs\n", cnt);
|
||||
|
||||
if (i > 1 && i < UHCI_NUM_SKELQH - 1) {
|
||||
if (qh->link !=
|
||||
(cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH))
|
||||
link = UHCI_PTR_TERM;
|
||||
if (i <= SKEL_ISO)
|
||||
;
|
||||
else if (i < SKEL_ASYNC)
|
||||
link = LINK_TO_QH(uhci->skel_async_qh);
|
||||
else if (!uhci->fsbr_is_on)
|
||||
;
|
||||
else if (fsbr_link)
|
||||
link = fsbr_link;
|
||||
else
|
||||
link = LINK_TO_QH(uhci->skel_term_qh);
|
||||
check_qh_link:
|
||||
if (qh->link != link)
|
||||
out += sprintf(out, " last QH not linked to next skeleton!\n");
|
||||
}
|
||||
}
|
||||
|
||||
return out - buf;
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
* (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
|
||||
* support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
|
||||
* (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
|
||||
* (C) Copyright 2004-2006 Alan Stern, stern@rowland.harvard.edu
|
||||
* (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu
|
||||
*
|
||||
* Intel documents this fairly well, and as far as I know there
|
||||
* are no royalties or anything like that, but even so there are
|
||||
@ -107,16 +107,16 @@ static __le32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame)
|
||||
* interrupt QHs, which will help spread out bandwidth utilization.
|
||||
*
|
||||
* ffs (Find First bit Set) does exactly what we need:
|
||||
* 1,3,5,... => ffs = 0 => use skel_int2_qh = skelqh[8],
|
||||
* 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[7], etc.
|
||||
* 1,3,5,... => ffs = 0 => use period-2 QH = skelqh[8],
|
||||
* 2,6,10,... => ffs = 1 => use period-4 QH = skelqh[7], etc.
|
||||
* ffs >= 7 => not on any high-period queue, so use
|
||||
* skel_int1_qh = skelqh[9].
|
||||
* period-1 QH = skelqh[9].
|
||||
* Add in UHCI_NUMFRAMES to insure at least one bit is set.
|
||||
*/
|
||||
skelnum = 8 - (int) __ffs(frame | UHCI_NUMFRAMES);
|
||||
if (skelnum <= 1)
|
||||
skelnum = 9;
|
||||
return UHCI_PTR_QH | cpu_to_le32(uhci->skelqh[skelnum]->dma_handle);
|
||||
return LINK_TO_QH(uhci->skelqh[skelnum]);
|
||||
}
|
||||
|
||||
#include "uhci-debug.c"
|
||||
@ -540,16 +540,18 @@ static void uhci_shutdown(struct pci_dev *pdev)
|
||||
*
|
||||
* The hardware doesn't really know any difference
|
||||
* in the queues, but the order does matter for the
|
||||
* protocols higher up. The order is:
|
||||
* protocols higher up. The order in which the queues
|
||||
* are encountered by the hardware is:
|
||||
*
|
||||
* - any isochronous events handled before any
|
||||
* - All isochronous events are handled before any
|
||||
* of the queues. We don't do that here, because
|
||||
* we'll create the actual TD entries on demand.
|
||||
* - The first queue is the interrupt queue.
|
||||
* - The second queue is the control queue, split into low- and full-speed
|
||||
* - The third queue is bulk queue.
|
||||
* - The fourth queue is the bandwidth reclamation queue, which loops back
|
||||
* to the full-speed control queue.
|
||||
* - The first queue is the high-period interrupt queue.
|
||||
* - The second queue is the period-1 interrupt and async
|
||||
* (low-speed control, full-speed control, then bulk) queue.
|
||||
* - The third queue is the terminating bandwidth reclamation queue,
|
||||
* which contains no members, loops back to itself, and is present
|
||||
* only when FSBR is on and there are no full-speed control or bulk QHs.
|
||||
*/
|
||||
static int uhci_start(struct usb_hcd *hcd)
|
||||
{
|
||||
@ -626,34 +628,18 @@ static int uhci_start(struct usb_hcd *hcd)
|
||||
}
|
||||
|
||||
/*
|
||||
* 8 Interrupt queues; link all higher int queues to int1,
|
||||
* then link int1 to control and control to bulk
|
||||
* 8 Interrupt queues; link all higher int queues to int1 = async
|
||||
*/
|
||||
uhci->skel_int128_qh->link =
|
||||
uhci->skel_int64_qh->link =
|
||||
uhci->skel_int32_qh->link =
|
||||
uhci->skel_int16_qh->link =
|
||||
uhci->skel_int8_qh->link =
|
||||
uhci->skel_int4_qh->link =
|
||||
uhci->skel_int2_qh->link = UHCI_PTR_QH |
|
||||
cpu_to_le32(uhci->skel_int1_qh->dma_handle);
|
||||
|
||||
uhci->skel_int1_qh->link = UHCI_PTR_QH |
|
||||
cpu_to_le32(uhci->skel_ls_control_qh->dma_handle);
|
||||
uhci->skel_ls_control_qh->link = UHCI_PTR_QH |
|
||||
cpu_to_le32(uhci->skel_fs_control_qh->dma_handle);
|
||||
uhci->skel_fs_control_qh->link = UHCI_PTR_QH |
|
||||
cpu_to_le32(uhci->skel_bulk_qh->dma_handle);
|
||||
uhci->skel_bulk_qh->link = UHCI_PTR_QH |
|
||||
cpu_to_le32(uhci->skel_term_qh->dma_handle);
|
||||
for (i = SKEL_ISO + 1; i < SKEL_ASYNC; ++i)
|
||||
uhci->skelqh[i]->link = LINK_TO_QH(uhci->skel_async_qh);
|
||||
uhci->skel_async_qh->link = uhci->skel_term_qh->link = UHCI_PTR_TERM;
|
||||
|
||||
/* This dummy TD is to work around a bug in Intel PIIX controllers */
|
||||
uhci_fill_td(uhci->term_td, 0, uhci_explen(0) |
|
||||
(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
|
||||
uhci->term_td->link = cpu_to_le32(uhci->term_td->dma_handle);
|
||||
|
||||
uhci->skel_term_qh->link = UHCI_PTR_TERM;
|
||||
uhci->skel_term_qh->element = cpu_to_le32(uhci->term_td->dma_handle);
|
||||
uhci->term_td->link = UHCI_PTR_TERM;
|
||||
uhci->skel_async_qh->element = uhci->skel_term_qh->element =
|
||||
LINK_TO_TD(uhci->term_td);
|
||||
|
||||
/*
|
||||
* Fill the frame list: make all entries point to the proper
|
||||
|
@ -129,11 +129,12 @@ struct uhci_qh {
|
||||
__le32 element; /* Queue element (TD) pointer */
|
||||
|
||||
/* Software fields */
|
||||
dma_addr_t dma_handle;
|
||||
|
||||
struct list_head node; /* Node in the list of QHs */
|
||||
struct usb_host_endpoint *hep; /* Endpoint information */
|
||||
struct usb_device *udev;
|
||||
struct list_head queue; /* Queue of urbps for this QH */
|
||||
struct uhci_qh *skel; /* Skeleton for this QH */
|
||||
struct uhci_td *dummy_td; /* Dummy TD to end the queue */
|
||||
struct uhci_td *post_td; /* Last TD completed */
|
||||
|
||||
@ -149,8 +150,7 @@ struct uhci_qh {
|
||||
|
||||
int state; /* QH_STATE_xxx; see above */
|
||||
int type; /* Queue type (control, bulk, etc) */
|
||||
|
||||
dma_addr_t dma_handle;
|
||||
int skel; /* Skeleton queue number */
|
||||
|
||||
unsigned int initial_toggle:1; /* Endpoint's current toggle value */
|
||||
unsigned int needs_fixup:1; /* Must fix the TD toggle values */
|
||||
@ -171,6 +171,8 @@ static inline __le32 qh_element(struct uhci_qh *qh) {
|
||||
return element;
|
||||
}
|
||||
|
||||
#define LINK_TO_QH(qh) (UHCI_PTR_QH | cpu_to_le32((qh)->dma_handle))
|
||||
|
||||
|
||||
/*
|
||||
* Transfer Descriptors
|
||||
@ -264,6 +266,8 @@ static inline u32 td_status(struct uhci_td *td) {
|
||||
return le32_to_cpu(status);
|
||||
}
|
||||
|
||||
#define LINK_TO_TD(td) (cpu_to_le32((td)->dma_handle))
|
||||
|
||||
|
||||
/*
|
||||
* Skeleton Queue Headers
|
||||
@ -272,12 +276,13 @@ static inline u32 td_status(struct uhci_td *td) {
|
||||
/*
|
||||
* The UHCI driver uses QHs with Interrupt, Control and Bulk URBs for
|
||||
* automatic queuing. To make it easy to insert entries into the schedule,
|
||||
* we have a skeleton of QHs for each predefined Interrupt latency,
|
||||
* low-speed control, full-speed control, bulk, and terminating QH
|
||||
* (see explanation for the terminating QH below).
|
||||
* we have a skeleton of QHs for each predefined Interrupt latency.
|
||||
* Asynchronous QHs (low-speed control, full-speed control, and bulk)
|
||||
* go onto the period-1 interrupt list, since they all get accessed on
|
||||
* every frame.
|
||||
*
|
||||
* When we want to add a new QH, we add it to the end of the list for the
|
||||
* skeleton QH. For instance, the schedule list can look like this:
|
||||
* When we want to add a new QH, we add it to the list starting from the
|
||||
* appropriate skeleton QH. For instance, the schedule can look like this:
|
||||
*
|
||||
* skel int128 QH
|
||||
* dev 1 interrupt QH
|
||||
@ -285,50 +290,47 @@ static inline u32 td_status(struct uhci_td *td) {
|
||||
* skel int64 QH
|
||||
* skel int32 QH
|
||||
* ...
|
||||
* skel int1 QH
|
||||
* skel low-speed control QH
|
||||
* dev 5 control QH
|
||||
* skel full-speed control QH
|
||||
* skel bulk QH
|
||||
* skel int1 + async QH
|
||||
* dev 5 low-speed control QH
|
||||
* dev 1 bulk QH
|
||||
* dev 2 bulk QH
|
||||
* skel terminating QH
|
||||
*
|
||||
* The terminating QH is used for 2 reasons:
|
||||
* - To place a terminating TD which is used to workaround a PIIX bug
|
||||
* (see Intel errata for explanation), and
|
||||
* - To loop back to the full-speed control queue for full-speed bandwidth
|
||||
* reclamation.
|
||||
* There is a special terminating QH used to keep full-speed bandwidth
|
||||
* reclamation active when no full-speed control or bulk QHs are linked
|
||||
* into the schedule. It has an inactive TD (to work around a PIIX bug,
|
||||
* see the Intel errata) and it points back to itself.
|
||||
*
|
||||
* There's a special skeleton QH for Isochronous QHs. It never appears
|
||||
* on the schedule, and Isochronous TDs go on the schedule before the
|
||||
* There's a special skeleton QH for Isochronous QHs which never appears
|
||||
* on the schedule. Isochronous TDs go on the schedule before the
|
||||
* the skeleton QHs. The hardware accesses them directly rather than
|
||||
* through their QH, which is used only for bookkeeping purposes.
|
||||
* While the UHCI spec doesn't forbid the use of QHs for Isochronous,
|
||||
* it doesn't use them either. And the spec says that queues never
|
||||
* advance on an error completion status, which makes them totally
|
||||
* unsuitable for Isochronous transfers.
|
||||
*
|
||||
* There's also a special skeleton QH used for QHs which are in the process
|
||||
* of unlinking and so may still be in use by the hardware. It too never
|
||||
* appears on the schedule.
|
||||
*/
|
||||
|
||||
#define UHCI_NUM_SKELQH 14
|
||||
#define skel_unlink_qh skelqh[0]
|
||||
#define skel_iso_qh skelqh[1]
|
||||
#define skel_int128_qh skelqh[2]
|
||||
#define skel_int64_qh skelqh[3]
|
||||
#define skel_int32_qh skelqh[4]
|
||||
#define skel_int16_qh skelqh[5]
|
||||
#define skel_int8_qh skelqh[6]
|
||||
#define skel_int4_qh skelqh[7]
|
||||
#define skel_int2_qh skelqh[8]
|
||||
#define skel_int1_qh skelqh[9]
|
||||
#define skel_ls_control_qh skelqh[10]
|
||||
#define skel_fs_control_qh skelqh[11]
|
||||
#define skel_bulk_qh skelqh[12]
|
||||
#define skel_term_qh skelqh[13]
|
||||
|
||||
/* Find the skelqh entry corresponding to an interval exponent */
|
||||
#define UHCI_SKEL_INDEX(exponent) (9 - exponent)
|
||||
#define UHCI_NUM_SKELQH 11
|
||||
#define SKEL_UNLINK 0
|
||||
#define skel_unlink_qh skelqh[SKEL_UNLINK]
|
||||
#define SKEL_ISO 1
|
||||
#define skel_iso_qh skelqh[SKEL_ISO]
|
||||
/* int128, int64, ..., int1 = 2, 3, ..., 9 */
|
||||
#define SKEL_INDEX(exponent) (9 - exponent)
|
||||
#define SKEL_ASYNC 9
|
||||
#define skel_async_qh skelqh[SKEL_ASYNC]
|
||||
#define SKEL_TERM 10
|
||||
#define skel_term_qh skelqh[SKEL_TERM]
|
||||
|
||||
/* The following entries refer to sublists of skel_async_qh */
|
||||
#define SKEL_LS_CONTROL 20
|
||||
#define SKEL_FS_CONTROL 21
|
||||
#define SKEL_FSBR SKEL_FS_CONTROL
|
||||
#define SKEL_BULK 22
|
||||
|
||||
/*
|
||||
* The UHCI controller and root hub
|
||||
|
@ -13,7 +13,7 @@
|
||||
* (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
|
||||
* support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
|
||||
* (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
|
||||
* (C) Copyright 2004-2006 Alan Stern, stern@rowland.harvard.edu
|
||||
* (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu
|
||||
*/
|
||||
|
||||
|
||||
@ -45,15 +45,43 @@ static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci)
|
||||
*/
|
||||
static void uhci_fsbr_on(struct uhci_hcd *uhci)
|
||||
{
|
||||
struct uhci_qh *fsbr_qh, *lqh, *tqh;
|
||||
|
||||
uhci->fsbr_is_on = 1;
|
||||
uhci->skel_term_qh->link = cpu_to_le32(
|
||||
uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
|
||||
lqh = list_entry(uhci->skel_async_qh->node.prev,
|
||||
struct uhci_qh, node);
|
||||
|
||||
/* Find the first FSBR QH. Linear search through the list is
|
||||
* acceptable because normally FSBR gets turned on as soon as
|
||||
* one QH needs it. */
|
||||
fsbr_qh = NULL;
|
||||
list_for_each_entry_reverse(tqh, &uhci->skel_async_qh->node, node) {
|
||||
if (tqh->skel < SKEL_FSBR)
|
||||
break;
|
||||
fsbr_qh = tqh;
|
||||
}
|
||||
|
||||
/* No FSBR QH means we must insert the terminating skeleton QH */
|
||||
if (!fsbr_qh) {
|
||||
uhci->skel_term_qh->link = LINK_TO_QH(uhci->skel_term_qh);
|
||||
wmb();
|
||||
lqh->link = uhci->skel_term_qh->link;
|
||||
|
||||
/* Otherwise loop the last QH to the first FSBR QH */
|
||||
} else
|
||||
lqh->link = LINK_TO_QH(fsbr_qh);
|
||||
}
|
||||
|
||||
static void uhci_fsbr_off(struct uhci_hcd *uhci)
|
||||
{
|
||||
struct uhci_qh *lqh;
|
||||
|
||||
uhci->fsbr_is_on = 0;
|
||||
uhci->skel_term_qh->link = UHCI_PTR_TERM;
|
||||
lqh = list_entry(uhci->skel_async_qh->node.prev,
|
||||
struct uhci_qh, node);
|
||||
|
||||
/* End the async list normally and unlink the terminating QH */
|
||||
lqh->link = uhci->skel_term_qh->link = UHCI_PTR_TERM;
|
||||
}
|
||||
|
||||
static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb)
|
||||
@ -158,11 +186,11 @@ static inline void uhci_insert_td_in_frame_list(struct uhci_hcd *uhci,
|
||||
|
||||
td->link = ltd->link;
|
||||
wmb();
|
||||
ltd->link = cpu_to_le32(td->dma_handle);
|
||||
ltd->link = LINK_TO_TD(td);
|
||||
} else {
|
||||
td->link = uhci->frame[framenum];
|
||||
wmb();
|
||||
uhci->frame[framenum] = cpu_to_le32(td->dma_handle);
|
||||
uhci->frame[framenum] = LINK_TO_TD(td);
|
||||
uhci->frame_cpu[framenum] = td;
|
||||
}
|
||||
}
|
||||
@ -184,7 +212,7 @@ static inline void uhci_remove_td_from_frame_list(struct uhci_hcd *uhci,
|
||||
struct uhci_td *ntd;
|
||||
|
||||
ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list);
|
||||
uhci->frame[td->frame] = cpu_to_le32(ntd->dma_handle);
|
||||
uhci->frame[td->frame] = LINK_TO_TD(ntd);
|
||||
uhci->frame_cpu[td->frame] = ntd;
|
||||
}
|
||||
} else {
|
||||
@ -404,13 +432,82 @@ static void uhci_fixup_toggles(struct uhci_qh *qh, int skip_first)
|
||||
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
|
||||
*/
|
||||
static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
||||
{
|
||||
struct uhci_qh *pqh;
|
||||
|
||||
WARN_ON(list_empty(&qh->queue));
|
||||
|
||||
/* Set the element pointer if it isn't set already.
|
||||
@ -421,7 +518,7 @@ static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
||||
struct uhci_td *td = list_entry(urbp->td_list.next,
|
||||
struct uhci_td, list);
|
||||
|
||||
qh->element = cpu_to_le32(td->dma_handle);
|
||||
qh->element = LINK_TO_TD(td);
|
||||
}
|
||||
|
||||
/* Treat the queue as if it has just advanced */
|
||||
@ -432,18 +529,64 @@ static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
||||
return;
|
||||
qh->state = QH_STATE_ACTIVE;
|
||||
|
||||
/* Move the QH from its old list to the end of the appropriate
|
||||
/* Move the QH from its old list to the correct spot in the appropriate
|
||||
* skeleton's list */
|
||||
if (qh == uhci->next_qh)
|
||||
uhci->next_qh = list_entry(qh->node.next, struct uhci_qh,
|
||||
node);
|
||||
list_move_tail(&qh->node, &qh->skel->node);
|
||||
list_del(&qh->node);
|
||||
|
||||
if (qh->skel == SKEL_ISO)
|
||||
link_iso(uhci, qh);
|
||||
else if (qh->skel < SKEL_ASYNC)
|
||||
link_interrupt(uhci, qh);
|
||||
else
|
||||
link_async(uhci, qh);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlink a high-period interrupt QH from the schedule
|
||||
*/
|
||||
static void unlink_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
||||
{
|
||||
struct uhci_qh *pqh;
|
||||
|
||||
/* Link it into the schedule */
|
||||
pqh = list_entry(qh->node.prev, struct uhci_qh, node);
|
||||
qh->link = pqh->link;
|
||||
pqh->link = qh->link;
|
||||
mb();
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlink a period-1 interrupt or async QH from the schedule
|
||||
*/
|
||||
static void unlink_async(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
||||
{
|
||||
struct uhci_qh *pqh, *lqh;
|
||||
__le32 link_to_next_qh = qh->link;
|
||||
|
||||
pqh = list_entry(qh->node.prev, struct uhci_qh, node);
|
||||
|
||||
/* If this is the first FSBQ QH, take special action */
|
||||
if (uhci->fsbr_is_on && pqh->skel < SKEL_FSBR &&
|
||||
qh->skel >= SKEL_FSBR) {
|
||||
lqh = list_entry(uhci->skel_async_qh->node.prev,
|
||||
struct uhci_qh, node);
|
||||
|
||||
/* If this QH is also the last one, we must link in
|
||||
* the terminating skeleton QH. */
|
||||
if (qh == lqh) {
|
||||
link_to_next_qh = LINK_TO_QH(uhci->skel_term_qh);
|
||||
uhci->skel_term_qh->link = link_to_next_qh;
|
||||
wmb();
|
||||
pqh->link = UHCI_PTR_QH | cpu_to_le32(qh->dma_handle);
|
||||
qh->link = link_to_next_qh;
|
||||
|
||||
/* Otherwise the last QH must point to the new first FSBR QH */
|
||||
} else
|
||||
lqh->link = link_to_next_qh;
|
||||
}
|
||||
|
||||
pqh->link = link_to_next_qh;
|
||||
mb();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -451,17 +594,18 @@ static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
||||
*/
|
||||
static void uhci_unlink_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
||||
{
|
||||
struct uhci_qh *pqh;
|
||||
|
||||
if (qh->state == QH_STATE_UNLINKING)
|
||||
return;
|
||||
WARN_ON(qh->state != QH_STATE_ACTIVE || !qh->udev);
|
||||
qh->state = QH_STATE_UNLINKING;
|
||||
|
||||
/* Unlink the QH from the schedule and record when we did it */
|
||||
pqh = list_entry(qh->node.prev, struct uhci_qh, node);
|
||||
pqh->link = qh->link;
|
||||
mb();
|
||||
if (qh->skel == SKEL_ISO)
|
||||
;
|
||||
else if (qh->skel < SKEL_ASYNC)
|
||||
unlink_interrupt(uhci, qh);
|
||||
else
|
||||
unlink_async(uhci, qh);
|
||||
|
||||
uhci_get_current_frame_number(uhci);
|
||||
qh->unlink_frame = uhci->frame_number;
|
||||
@ -697,6 +841,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
|
||||
dma_addr_t data = urb->transfer_dma;
|
||||
__le32 *plink;
|
||||
struct urb_priv *urbp = urb->hcpriv;
|
||||
int skel;
|
||||
|
||||
/* The "pipe" thing contains the destination in bits 8--18 */
|
||||
destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
|
||||
@ -737,7 +882,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
|
||||
td = uhci_alloc_td(uhci);
|
||||
if (!td)
|
||||
goto nomem;
|
||||
*plink = cpu_to_le32(td->dma_handle);
|
||||
*plink = LINK_TO_TD(td);
|
||||
|
||||
/* Alternate Data0/1 (start with Data1) */
|
||||
destination ^= TD_TOKEN_TOGGLE;
|
||||
@ -757,7 +902,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
|
||||
td = uhci_alloc_td(uhci);
|
||||
if (!td)
|
||||
goto nomem;
|
||||
*plink = cpu_to_le32(td->dma_handle);
|
||||
*plink = LINK_TO_TD(td);
|
||||
|
||||
/*
|
||||
* It's IN if the pipe is an output pipe or we're not expecting
|
||||
@ -784,7 +929,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
|
||||
td = uhci_alloc_td(uhci);
|
||||
if (!td)
|
||||
goto nomem;
|
||||
*plink = cpu_to_le32(td->dma_handle);
|
||||
*plink = LINK_TO_TD(td);
|
||||
|
||||
uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0);
|
||||
wmb();
|
||||
@ -797,11 +942,13 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
|
||||
* isn't in the CONFIGURED state. */
|
||||
if (urb->dev->speed == USB_SPEED_LOW ||
|
||||
urb->dev->state != USB_STATE_CONFIGURED)
|
||||
qh->skel = uhci->skel_ls_control_qh;
|
||||
skel = SKEL_LS_CONTROL;
|
||||
else {
|
||||
qh->skel = uhci->skel_fs_control_qh;
|
||||
skel = SKEL_FS_CONTROL;
|
||||
uhci_add_fsbr(uhci, urb);
|
||||
}
|
||||
if (qh->state != QH_STATE_ACTIVE)
|
||||
qh->skel = skel;
|
||||
|
||||
urb->actual_length = -8; /* Account for the SETUP packet */
|
||||
return 0;
|
||||
@ -860,7 +1007,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
|
||||
td = uhci_alloc_td(uhci);
|
||||
if (!td)
|
||||
goto nomem;
|
||||
*plink = cpu_to_le32(td->dma_handle);
|
||||
*plink = LINK_TO_TD(td);
|
||||
}
|
||||
uhci_add_td_to_urbp(td, urbp);
|
||||
uhci_fill_td(td, status,
|
||||
@ -888,7 +1035,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
|
||||
td = uhci_alloc_td(uhci);
|
||||
if (!td)
|
||||
goto nomem;
|
||||
*plink = cpu_to_le32(td->dma_handle);
|
||||
*plink = LINK_TO_TD(td);
|
||||
|
||||
uhci_add_td_to_urbp(td, urbp);
|
||||
uhci_fill_td(td, status,
|
||||
@ -914,7 +1061,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
|
||||
td = uhci_alloc_td(uhci);
|
||||
if (!td)
|
||||
goto nomem;
|
||||
*plink = cpu_to_le32(td->dma_handle);
|
||||
*plink = LINK_TO_TD(td);
|
||||
|
||||
uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0);
|
||||
wmb();
|
||||
@ -931,7 +1078,7 @@ nomem:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb,
|
||||
static int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb,
|
||||
struct uhci_qh *qh)
|
||||
{
|
||||
int ret;
|
||||
@ -940,7 +1087,8 @@ static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb,
|
||||
if (urb->dev->speed == USB_SPEED_LOW)
|
||||
return -EINVAL;
|
||||
|
||||
qh->skel = uhci->skel_bulk_qh;
|
||||
if (qh->state != QH_STATE_ACTIVE)
|
||||
qh->skel = SKEL_BULK;
|
||||
ret = uhci_submit_common(uhci, urb, qh);
|
||||
if (ret == 0)
|
||||
uhci_add_fsbr(uhci, urb);
|
||||
@ -968,7 +1116,7 @@ static int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,
|
||||
if (exponent < 0)
|
||||
return -EINVAL;
|
||||
qh->period = 1 << exponent;
|
||||
qh->skel = uhci->skelqh[UHCI_SKEL_INDEX(exponent)];
|
||||
qh->skel = SKEL_INDEX(exponent);
|
||||
|
||||
/* For now, interrupt phase is fixed by the layout
|
||||
* of the QH lists. */
|
||||
@ -1005,7 +1153,7 @@ static int uhci_fixup_short_transfer(struct uhci_hcd *uhci,
|
||||
* the queue at the status stage transaction, which is
|
||||
* the last TD. */
|
||||
WARN_ON(list_empty(&urbp->td_list));
|
||||
qh->element = cpu_to_le32(td->dma_handle);
|
||||
qh->element = LINK_TO_TD(td);
|
||||
tmp = td->list.prev;
|
||||
ret = -EINPROGRESS;
|
||||
|
||||
@ -1216,7 +1364,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
|
||||
qh->iso_status = 0;
|
||||
}
|
||||
|
||||
qh->skel = uhci->skel_iso_qh;
|
||||
qh->skel = SKEL_ISO;
|
||||
if (!qh->bandwidth_reserved)
|
||||
uhci_reserve_bandwidth(uhci, qh);
|
||||
return 0;
|
||||
@ -1566,8 +1714,7 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
||||
if (time_after(jiffies, qh->advance_jiffies + QH_WAIT_TIMEOUT)) {
|
||||
|
||||
/* Detect the Intel bug and work around it */
|
||||
if (qh->post_td && qh_element(qh) ==
|
||||
cpu_to_le32(qh->post_td->dma_handle)) {
|
||||
if (qh->post_td && qh_element(qh) == LINK_TO_TD(qh->post_td)) {
|
||||
qh->element = qh->post_td->link;
|
||||
qh->advance_jiffies = jiffies;
|
||||
ret = 1;
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb/input.h>
|
||||
#include <linux/hid.h>
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
@ -330,7 +331,8 @@ static void usb_kbd_disconnect(struct usb_interface *intf)
|
||||
}
|
||||
|
||||
static struct usb_device_id usb_kbd_id_table [] = {
|
||||
{ USB_INTERFACE_INFO(3, 1, 1) },
|
||||
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
|
||||
USB_INTERFACE_PROTOCOL_KEYBOARD) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb/input.h>
|
||||
#include <linux/hid.h>
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
@ -213,7 +214,8 @@ static void usb_mouse_disconnect(struct usb_interface *intf)
|
||||
}
|
||||
|
||||
static struct usb_device_id usb_mouse_id_table [] = {
|
||||
{ USB_INTERFACE_INFO(3, 1, 2) },
|
||||
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
|
||||
USB_INTERFACE_PROTOCOL_MOUSE) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
|
@ -163,7 +163,7 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
|
||||
}
|
||||
|
||||
id = STYLUS_DEVICE_ID;
|
||||
if (data[1] & 0x10) { /* in prox */
|
||||
if (data[1] & 0x80) { /* in prox */
|
||||
|
||||
switch ((data[1] >> 5) & 3) {
|
||||
|
||||
@ -196,9 +196,6 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
|
||||
wacom_report_abs(wcombo, ABS_DISTANCE, data[7] & 0x3f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (data[1] & 0x90) {
|
||||
x = wacom_le16_to_cpu(&data[2]);
|
||||
y = wacom_le16_to_cpu(&data[4]);
|
||||
wacom_report_abs(wcombo, ABS_X, x);
|
||||
@ -210,19 +207,28 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
|
||||
wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x04);
|
||||
}
|
||||
wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
|
||||
}
|
||||
else
|
||||
wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
|
||||
|
||||
if (data[1] & 0x10) /* only report prox-in when in area */
|
||||
wacom_report_key(wcombo, wacom->tool[0], 1);
|
||||
if (!(data[1] & 0x90)) /* report prox-out when physically out */
|
||||
} else if (!(data[1] & 0x90)) {
|
||||
wacom_report_abs(wcombo, ABS_X, 0);
|
||||
wacom_report_abs(wcombo, ABS_Y, 0);
|
||||
if (wacom->tool[0] == BTN_TOOL_MOUSE) {
|
||||
wacom_report_key(wcombo, BTN_LEFT, 0);
|
||||
wacom_report_key(wcombo, BTN_RIGHT, 0);
|
||||
wacom_report_abs(wcombo, ABS_DISTANCE, 0);
|
||||
} else {
|
||||
wacom_report_abs(wcombo, ABS_PRESSURE, 0);
|
||||
wacom_report_key(wcombo, BTN_TOUCH, 0);
|
||||
wacom_report_key(wcombo, BTN_STYLUS, 0);
|
||||
wacom_report_key(wcombo, BTN_STYLUS2, 0);
|
||||
}
|
||||
wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
|
||||
wacom_report_key(wcombo, wacom->tool[0], 0);
|
||||
wacom_input_sync(wcombo);
|
||||
}
|
||||
|
||||
/* send pad data */
|
||||
if (wacom->features->type == WACOM_G4) {
|
||||
if ( (wacom->serial[1] & 0xc0) != (data[7] & 0xf8) ) {
|
||||
if (data[7] & 0xf8) {
|
||||
wacom_input_sync(wcombo); /* sync last event */
|
||||
wacom->id[1] = 1;
|
||||
wacom->serial[1] = (data[7] & 0xf8);
|
||||
wacom_report_key(wcombo, BTN_0, (data[7] & 0x40));
|
||||
@ -230,10 +236,15 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
|
||||
rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3);
|
||||
wacom_report_rel(wcombo, REL_WHEEL, rw);
|
||||
wacom_report_key(wcombo, BTN_TOOL_FINGER, 0xf0);
|
||||
wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID);
|
||||
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
|
||||
} else if (wacom->id[1]) {
|
||||
wacom_input_sync(wcombo); /* sync last event */
|
||||
wacom->id[1] = 0;
|
||||
wacom_report_key(wcombo, BTN_0, (data[7] & 0x40));
|
||||
wacom_report_key(wcombo, BTN_4, (data[7] & 0x80));
|
||||
wacom_report_key(wcombo, BTN_TOOL_FINGER, 0);
|
||||
wacom_report_abs(wcombo, ABS_MISC, 0);
|
||||
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
|
||||
}
|
||||
}
|
||||
@ -304,29 +315,36 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
|
||||
default: /* Unknown tool */
|
||||
wacom->tool[idx] = BTN_TOOL_PEN;
|
||||
}
|
||||
/* only large I3 support Lens Cursor */
|
||||
if(!((wacom->tool[idx] == BTN_TOOL_LENS)
|
||||
&& ((wacom->features->type == INTUOS3)
|
||||
|| (wacom->features->type == INTUOS3S)))) {
|
||||
wacom_report_abs(wcombo, ABS_MISC, wacom->id[idx]); /* report tool id */
|
||||
wacom_report_key(wcombo, wacom->tool[idx], 1);
|
||||
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Exit report */
|
||||
if ((data[1] & 0xfe) == 0x80) {
|
||||
if(!((wacom->tool[idx] == BTN_TOOL_LENS)
|
||||
&& ((wacom->features->type == INTUOS3)
|
||||
|| (wacom->features->type == INTUOS3S)))) {
|
||||
wacom_report_abs(wcombo, ABS_X, 0);
|
||||
wacom_report_abs(wcombo, ABS_Y, 0);
|
||||
wacom_report_abs(wcombo, ABS_DISTANCE, 0);
|
||||
if (wacom->tool[idx] >= BTN_TOOL_MOUSE) {
|
||||
wacom_report_key(wcombo, BTN_LEFT, 0);
|
||||
wacom_report_key(wcombo, BTN_MIDDLE, 0);
|
||||
wacom_report_key(wcombo, BTN_RIGHT, 0);
|
||||
wacom_report_key(wcombo, BTN_SIDE, 0);
|
||||
wacom_report_key(wcombo, BTN_EXTRA, 0);
|
||||
wacom_report_abs(wcombo, ABS_THROTTLE, 0);
|
||||
wacom_report_abs(wcombo, ABS_RZ, 0);
|
||||
} else {
|
||||
wacom_report_abs(wcombo, ABS_PRESSURE, 0);
|
||||
wacom_report_abs(wcombo, ABS_TILT_X, 0);
|
||||
wacom_report_abs(wcombo, ABS_TILT_Y, 0);
|
||||
wacom_report_key(wcombo, BTN_STYLUS, 0);
|
||||
wacom_report_key(wcombo, BTN_STYLUS2, 0);
|
||||
wacom_report_key(wcombo, BTN_TOUCH, 0);
|
||||
wacom_report_abs(wcombo, ABS_WHEEL, 0);
|
||||
}
|
||||
wacom_report_key(wcombo, wacom->tool[idx], 0);
|
||||
wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
|
||||
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -394,6 +412,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
|
||||
wacom_report_key(wcombo, wacom->tool[1], 1);
|
||||
else
|
||||
wacom_report_key(wcombo, wacom->tool[1], 0);
|
||||
wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID);
|
||||
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xffffffff);
|
||||
return 1;
|
||||
}
|
||||
@ -403,6 +422,12 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
|
||||
if (result)
|
||||
return result-1;
|
||||
|
||||
/* Only large I3 and I1 & I2 support Lense Cursor */
|
||||
if((wacom->tool[idx] == BTN_TOOL_LENS)
|
||||
&& ((wacom->features->type == INTUOS3)
|
||||
|| (wacom->features->type == INTUOS3S)))
|
||||
return 0;
|
||||
|
||||
/* Cintiq doesn't send data when RDY bit isn't set */
|
||||
if ((wacom->features->type == CINTIQ) && !(data[1] & 0x40))
|
||||
return 0;
|
||||
@ -554,11 +579,11 @@ static struct wacom_features wacom_features[] = {
|
||||
{ "Wacom Volito2 4x5", 8, 5104, 3712, 511, 63, GRAPHIRE },
|
||||
{ "Wacom Volito2 2x3", 8, 3248, 2320, 511, 63, GRAPHIRE },
|
||||
{ "Wacom PenPartner2", 8, 3250, 2320, 255, 63, GRAPHIRE },
|
||||
{ "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 31, INTUOS },
|
||||
{ "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 31, INTUOS },
|
||||
{ "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 31, INTUOS },
|
||||
{ "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 31, INTUOS },
|
||||
{ "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 31, INTUOS },
|
||||
{ "Wacom PL400", 8, 5408, 4056, 255, 0, PL },
|
||||
{ "Wacom PL500", 8, 6144, 4608, 255, 0, PL },
|
||||
{ "Wacom PL600", 8, 6126, 4604, 255, 0, PL },
|
||||
@ -571,11 +596,11 @@ static struct wacom_features wacom_features[] = {
|
||||
{ "Wacom DTF521", 8, 6282, 4762, 511, 0, PL },
|
||||
{ "Wacom DTF720", 8, 6858, 5506, 511, 0, PL },
|
||||
{ "Wacom Cintiq Partner",8, 20480, 15360, 511, 0, PTU },
|
||||
{ "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 31, INTUOS },
|
||||
{ "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 31, INTUOS },
|
||||
{ "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 31, INTUOS },
|
||||
{ "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 31, INTUOS },
|
||||
{ "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 31, INTUOS },
|
||||
{ "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 63, INTUOS3S },
|
||||
{ "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 63, INTUOS3 },
|
||||
{ "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 63, INTUOS3 },
|
||||
@ -584,7 +609,7 @@ static struct wacom_features wacom_features[] = {
|
||||
{ "Wacom Intuos3 6x11", 10, 54204, 31750, 1023, 63, INTUOS3 },
|
||||
{ "Wacom Intuos3 4x6", 10, 31496, 19685, 1023, 63, INTUOS3S },
|
||||
{ "Wacom Cintiq 21UX", 10, 87200, 65600, 1023, 63, CINTIQ },
|
||||
{ "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 31, INTUOS },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#define STYLUS_DEVICE_ID 0x02
|
||||
#define CURSOR_DEVICE_ID 0x06
|
||||
#define ERASER_DEVICE_ID 0x0A
|
||||
#define PAD_DEVICE_ID 0x0F
|
||||
|
||||
enum {
|
||||
PENPARTNER = 0,
|
||||
|
@ -244,6 +244,20 @@ config USB_TRANCEVIBRATOR
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called trancevibrator.
|
||||
|
||||
config USB_IOWARRIOR
|
||||
tristate "IO Warrior driver support"
|
||||
depends on USB
|
||||
help
|
||||
Say Y here if you want to support the IO Warrior devices from Code
|
||||
Mercenaries. This includes support for the following devices:
|
||||
IO Warrior 40
|
||||
IO Warrior 24
|
||||
IO Warrior 56
|
||||
IO Warrior 24 Power Vampire
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called iowarrior.
|
||||
|
||||
config USB_TEST
|
||||
tristate "USB testing driver (DEVELOPMENT)"
|
||||
depends on USB && USB_DEVICEFS && EXPERIMENTAL
|
||||
|
@ -13,6 +13,7 @@ obj-$(CONFIG_USB_EMI26) += emi26.o
|
||||
obj-$(CONFIG_USB_EMI62) += emi62.o
|
||||
obj-$(CONFIG_USB_FTDI_ELAN) += ftdi-elan.o
|
||||
obj-$(CONFIG_USB_IDMOUSE) += idmouse.o
|
||||
obj-$(CONFIG_USB_IOWARRIOR) += iowarrior.o
|
||||
obj-$(CONFIG_USB_LCD) += usblcd.o
|
||||
obj-$(CONFIG_USB_LD) += ldusb.o
|
||||
obj-$(CONFIG_USB_LED) += usbled.o
|
||||
|
925
drivers/usb/misc/iowarrior.c
Normal file
925
drivers/usb/misc/iowarrior.c
Normal 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);
|
@ -1165,7 +1165,7 @@ err_dev:
|
||||
return rc;
|
||||
}
|
||||
|
||||
void __exit mon_bin_exit(void)
|
||||
void mon_bin_exit(void)
|
||||
{
|
||||
cdev_del(&mon_bin_cdev);
|
||||
unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR);
|
||||
|
@ -520,7 +520,7 @@ int __init mon_text_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __exit mon_text_exit(void)
|
||||
void mon_text_exit(void)
|
||||
{
|
||||
debugfs_remove(mon_dir);
|
||||
}
|
||||
|
@ -57,9 +57,9 @@ void mon_text_del(struct mon_bus *mbus);
|
||||
// void mon_bin_add(struct mon_bus *);
|
||||
|
||||
int __init mon_text_init(void);
|
||||
void __exit mon_text_exit(void);
|
||||
void mon_text_exit(void);
|
||||
int __init mon_bin_init(void);
|
||||
void __exit mon_bin_exit(void);
|
||||
void mon_bin_exit(void);
|
||||
|
||||
/*
|
||||
* DMA interface.
|
||||
|
@ -186,6 +186,15 @@ config USB_NET_CDCETHER
|
||||
IEEE 802 "local assignment" bit is set in the address, a "usbX"
|
||||
name is used instead.
|
||||
|
||||
config USB_NET_DM9601
|
||||
tristate "Davicom DM9601 based USB 1.1 10/100 ethernet devices"
|
||||
depends on USB_USBNET
|
||||
select CRC32
|
||||
select USB_USBNET_MII
|
||||
help
|
||||
This option adds support for Davicom DM9601 based USB 1.1
|
||||
10/100 Ethernet adapters.
|
||||
|
||||
config USB_NET_GL620A
|
||||
tristate "GeneSys GL620USB-A based cables"
|
||||
depends on USB_USBNET
|
||||
|
@ -8,6 +8,7 @@ obj-$(CONFIG_USB_PEGASUS) += pegasus.o
|
||||
obj-$(CONFIG_USB_RTL8150) += rtl8150.o
|
||||
obj-$(CONFIG_USB_NET_AX8817X) += asix.o
|
||||
obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o
|
||||
obj-$(CONFIG_USB_NET_DM9601) += dm9601.o
|
||||
obj-$(CONFIG_USB_NET_GL620A) += gl620a.o
|
||||
obj-$(CONFIG_USB_NET_NET1080) += net1080.o
|
||||
obj-$(CONFIG_USB_NET_PLUSB) += plusb.o
|
||||
|
@ -1422,6 +1422,10 @@ static const struct usb_device_id products [] = {
|
||||
// goodway corp usb gwusb2e
|
||||
USB_DEVICE (0x1631, 0x6200),
|
||||
.driver_info = (unsigned long) &ax8817x_info,
|
||||
}, {
|
||||
// JVC MP-PRX1 Port Replicator
|
||||
USB_DEVICE (0x04f1, 0x3008),
|
||||
.driver_info = (unsigned long) &ax8817x_info,
|
||||
}, {
|
||||
// ASIX AX88772 10/100
|
||||
USB_DEVICE (0x0b95, 0x7720),
|
||||
|
606
drivers/usb/net/dm9601.c
Normal file
606
drivers/usb/net/dm9601.c
Normal 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");
|
@ -513,6 +513,7 @@ static struct usb_device_id id_table_combined [] = {
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13S_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) },
|
||||
{ USB_DEVICE(ELEKTOR_VID, ELEKTOR_FT323R_PID) },
|
||||
{ USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) },
|
||||
{ }, /* Optional parameter entry */
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
@ -491,6 +491,12 @@
|
||||
#define FTDI_TACTRIX_OPENPORT_13S_PID 0xCC49 /* OpenPort 1.3 Subaru */
|
||||
#define FTDI_TACTRIX_OPENPORT_13U_PID 0xCC4A /* OpenPort 1.3 Universal */
|
||||
|
||||
/*
|
||||
* Telldus Technologies
|
||||
*/
|
||||
#define TELLDUS_VID 0x1781 /* Vendor ID */
|
||||
#define TELLDUS_TELLSTICK_PID 0x0C30 /* RF control dongle 433 MHz using FT232RL */
|
||||
|
||||
/* Commands */
|
||||
#define FTDI_SIO_RESET 0 /* Reset the port */
|
||||
#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */
|
||||
|
@ -68,49 +68,94 @@ static int option_send_setup(struct usb_serial_port *port);
|
||||
|
||||
/* Vendor and product IDs */
|
||||
#define OPTION_VENDOR_ID 0x0AF0
|
||||
#define HUAWEI_VENDOR_ID 0x12D1
|
||||
#define NOVATELWIRELESS_VENDOR_ID 0x1410
|
||||
#define ANYDATA_VENDOR_ID 0x16d5
|
||||
|
||||
#define OPTION_PRODUCT_OLD 0x5000
|
||||
#define OPTION_PRODUCT_FUSION 0x6000
|
||||
#define OPTION_PRODUCT_FUSION2 0x6300
|
||||
#define OPTION_PRODUCT_COLT 0x5000
|
||||
#define OPTION_PRODUCT_RICOLA 0x6000
|
||||
#define OPTION_PRODUCT_RICOLA_LIGHT 0x6100
|
||||
#define OPTION_PRODUCT_RICOLA_QUAD 0x6200
|
||||
#define OPTION_PRODUCT_RICOLA_QUAD_LIGHT 0x6300
|
||||
#define OPTION_PRODUCT_RICOLA_NDIS 0x6050
|
||||
#define OPTION_PRODUCT_RICOLA_NDIS_LIGHT 0x6150
|
||||
#define OPTION_PRODUCT_RICOLA_NDIS_QUAD 0x6250
|
||||
#define OPTION_PRODUCT_RICOLA_NDIS_QUAD_LIGHT 0x6350
|
||||
#define OPTION_PRODUCT_COBRA 0x6500
|
||||
#define OPTION_PRODUCT_COBRA2 0x6600
|
||||
#define OPTION_PRODUCT_GTMAX36 0x6701
|
||||
#define OPTION_PRODUCT_COBRA_BUS 0x6501
|
||||
#define OPTION_PRODUCT_VIPER 0x6600
|
||||
#define OPTION_PRODUCT_VIPER_BUS 0x6601
|
||||
#define OPTION_PRODUCT_GT_MAX_READY 0x6701
|
||||
#define OPTION_PRODUCT_GT_MAX 0x6711
|
||||
#define OPTION_PRODUCT_FUJI_MODEM_LIGHT 0x6721
|
||||
#define OPTION_PRODUCT_FUJI_MODEM_GT 0x6741
|
||||
#define OPTION_PRODUCT_FUJI_MODEM_EX 0x6761
|
||||
#define OPTION_PRODUCT_FUJI_NETWORK_LIGHT 0x6731
|
||||
#define OPTION_PRODUCT_FUJI_NETWORK_GT 0x6751
|
||||
#define OPTION_PRODUCT_FUJI_NETWORK_EX 0x6771
|
||||
#define OPTION_PRODUCT_KOI_MODEM 0x6800
|
||||
#define OPTION_PRODUCT_KOI_NETWORK 0x6811
|
||||
#define OPTION_PRODUCT_SCORPION_MODEM 0x6901
|
||||
#define OPTION_PRODUCT_SCORPION_NETWORK 0x6911
|
||||
#define OPTION_PRODUCT_ETNA_MODEM 0x7001
|
||||
#define OPTION_PRODUCT_ETNA_NETWORK 0x7011
|
||||
#define OPTION_PRODUCT_ETNA_MODEM_LITE 0x7021
|
||||
#define OPTION_PRODUCT_ETNA_MODEM_GT 0x7041
|
||||
#define OPTION_PRODUCT_ETNA_MODEM_EX 0x7061
|
||||
#define OPTION_PRODUCT_ETNA_NETWORK_LITE 0x7031
|
||||
#define OPTION_PRODUCT_ETNA_NETWORK_GT 0x7051
|
||||
#define OPTION_PRODUCT_ETNA_NETWORK_EX 0x7071
|
||||
#define OPTION_PRODUCT_ETNA_KOI_MODEM 0x7100
|
||||
#define OPTION_PRODUCT_ETNA_KOI_NETWORK 0x7111
|
||||
|
||||
#define HUAWEI_VENDOR_ID 0x12D1
|
||||
#define HUAWEI_PRODUCT_E600 0x1001
|
||||
#define HUAWEI_PRODUCT_E220 0x1003
|
||||
|
||||
#define NOVATELWIRELESS_VENDOR_ID 0x1410
|
||||
#define NOVATELWIRELESS_PRODUCT_U740 0x1400
|
||||
|
||||
#define ANYDATA_VENDOR_ID 0x16d5
|
||||
#define ANYDATA_PRODUCT_ID 0x6501
|
||||
|
||||
static struct usb_device_id option_ids[] = {
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_LIGHT) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD_LIGHT) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_LIGHT) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_QUAD) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_QUAD_LIGHT) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GTMAX36) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA_BUS) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_VIPER) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_VIPER_BUS) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GT_MAX_READY) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GT_MAX) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_LIGHT) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_GT) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_EX) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_LIGHT) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_GT) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_EX) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_KOI_MODEM) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_KOI_NETWORK) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_SCORPION_MODEM) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_SCORPION_NETWORK) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_LITE) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_GT) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_EX) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_LITE) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_GT) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_EX) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_MODEM) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_NETWORK) },
|
||||
{ USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
|
||||
{ USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220) },
|
||||
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
|
||||
{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
static struct usb_device_id option_ids1[] = {
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GTMAX36) },
|
||||
{ USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
|
||||
{ USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220) },
|
||||
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
|
||||
{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, option_ids);
|
||||
|
||||
static struct usb_driver option_driver = {
|
||||
@ -132,7 +177,7 @@ static struct usb_serial_driver option_1port_device = {
|
||||
},
|
||||
.description = "GSM modem (1-port)",
|
||||
.usb_driver = &option_driver,
|
||||
.id_table = option_ids1,
|
||||
.id_table = option_ids,
|
||||
.num_interrupt_in = NUM_DONT_CARE,
|
||||
.num_bulk_in = NUM_DONT_CARE,
|
||||
.num_bulk_out = NUM_DONT_CARE,
|
||||
|
@ -501,6 +501,30 @@ int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* sysfs_add_file_to_group - add an attribute file to a pre-existing group.
|
||||
* @kobj: object we're acting for.
|
||||
* @attr: attribute descriptor.
|
||||
* @group: group name.
|
||||
*/
|
||||
int sysfs_add_file_to_group(struct kobject *kobj,
|
||||
const struct attribute *attr, const char *group)
|
||||
{
|
||||
struct dentry *dir;
|
||||
int error;
|
||||
|
||||
dir = lookup_one_len(group, kobj->dentry, strlen(group));
|
||||
if (IS_ERR(dir))
|
||||
error = PTR_ERR(dir);
|
||||
else {
|
||||
error = sysfs_add_file(dir, attr, SYSFS_KOBJ_ATTR);
|
||||
dput(dir);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sysfs_add_file_to_group);
|
||||
|
||||
|
||||
/**
|
||||
* sysfs_update_file - update the modified timestamp on an object attribute.
|
||||
* @kobj: object we're acting for.
|
||||
@ -586,6 +610,26 @@ void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* sysfs_remove_file_from_group - remove an attribute file from a group.
|
||||
* @kobj: object we're acting for.
|
||||
* @attr: attribute descriptor.
|
||||
* @group: group name.
|
||||
*/
|
||||
void sysfs_remove_file_from_group(struct kobject *kobj,
|
||||
const struct attribute *attr, const char *group)
|
||||
{
|
||||
struct dentry *dir;
|
||||
|
||||
dir = lookup_one_len(group, kobj->dentry, strlen(group));
|
||||
if (!IS_ERR(dir)) {
|
||||
sysfs_hash_and_remove(dir, attr->name);
|
||||
dput(dir);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);
|
||||
|
||||
|
||||
EXPORT_SYMBOL_GPL(sysfs_create_file);
|
||||
EXPORT_SYMBOL_GPL(sysfs_remove_file);
|
||||
EXPORT_SYMBOL_GPL(sysfs_update_file);
|
||||
|
@ -126,6 +126,11 @@ void sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr);
|
||||
int __must_check sysfs_create_group(struct kobject *,
|
||||
const struct attribute_group *);
|
||||
void sysfs_remove_group(struct kobject *, const struct attribute_group *);
|
||||
int sysfs_add_file_to_group(struct kobject *kobj,
|
||||
const struct attribute *attr, const char *group);
|
||||
void sysfs_remove_file_from_group(struct kobject *kobj,
|
||||
const struct attribute *attr, const char *group);
|
||||
|
||||
void sysfs_notify(struct kobject * k, char *dir, char *attr);
|
||||
|
||||
|
||||
@ -210,6 +215,18 @@ static inline void sysfs_remove_group(struct kobject * k, const struct attribute
|
||||
;
|
||||
}
|
||||
|
||||
static inline int sysfs_add_file_to_group(struct kobject *kobj,
|
||||
const struct attribute *attr, const char *group)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void sysfs_remove_file_from_group(struct kobject *kobj,
|
||||
const struct attribute *attr, const char *group);
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
static inline void sysfs_notify(struct kobject * k, char *dir, char *attr)
|
||||
{
|
||||
}
|
||||
|
@ -388,10 +388,14 @@ struct usb_device {
|
||||
struct usb_device *children[USB_MAXCHILDREN];
|
||||
|
||||
int pm_usage_cnt; /* usage counter for autosuspend */
|
||||
u32 quirks; /* quirks of the whole device */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
struct delayed_work autosuspend; /* for delayed autosuspends */
|
||||
struct mutex pm_mutex; /* protects PM operations */
|
||||
|
||||
unsigned autosuspend_delay; /* in jiffies */
|
||||
|
||||
unsigned auto_pm:1; /* autosuspend/resume in progress */
|
||||
unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */
|
||||
#endif
|
||||
|
@ -1,8 +1,9 @@
|
||||
/*
|
||||
* This file holds USB constants and structures that are needed for USB
|
||||
* device APIs. These are used by the USB device model, which is defined
|
||||
* in chapter 9 of the USB 2.0 specification. Linux has several APIs in C
|
||||
* that need these:
|
||||
* This file holds USB constants and structures that are needed for
|
||||
* USB device APIs. These are used by the USB device model, which is
|
||||
* defined in chapter 9 of the USB 2.0 specification and in the
|
||||
* Wireless USB 1.0 (spread around). Linux has several APIs in C that
|
||||
* need these:
|
||||
*
|
||||
* - the master/host side Linux-USB kernel driver API;
|
||||
* - the "usbfs" user space API; and
|
||||
@ -14,6 +15,19 @@
|
||||
*
|
||||
* There's also "Wireless USB", using low power short range radios for
|
||||
* peripheral interconnection but otherwise building on the USB framework.
|
||||
*
|
||||
* Note all descriptors are declared '__attribute__((packed))' so that:
|
||||
*
|
||||
* [a] they never get padded, either internally (USB spec writers
|
||||
* probably handled that) or externally;
|
||||
*
|
||||
* [b] so that accessing bigger-than-a-bytes fields will never
|
||||
* generate bus errors on any platform, even when the location of
|
||||
* its descriptor inside a bundle isn't "naturally aligned", and
|
||||
*
|
||||
* [c] for consistency, removing all doubt even when it appears to
|
||||
* someone that the two other points are non-issues for that
|
||||
* particular descriptor type.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_USB_CH9_H
|
||||
|
33
include/linux/usb/iowarrior.h
Normal file
33
include/linux/usb/iowarrior.h
Normal 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_ */
|
11
include/linux/usb/quirks.h
Normal file
11
include/linux/usb/quirks.h
Normal 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
|
Loading…
Reference in New Issue
Block a user