2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* USB HID support for Linux
|
|
|
|
*
|
|
|
|
* Copyright (c) 1999 Andreas Gal
|
2005-09-05 13:12:01 +08:00
|
|
|
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
|
|
|
|
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
|
2006-12-09 01:40:44 +08:00
|
|
|
* Copyright (c) 2006 Jiri Kosina
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
|
|
|
* any later version.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/smp_lock.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <asm/unaligned.h>
|
|
|
|
#include <asm/byteorder.h>
|
|
|
|
#include <linux/input.h>
|
|
|
|
#include <linux/wait.h>
|
|
|
|
|
|
|
|
#undef DEBUG
|
|
|
|
#undef DEBUG_DATA
|
|
|
|
|
|
|
|
#include <linux/usb.h>
|
|
|
|
|
2006-12-09 01:40:44 +08:00
|
|
|
#include <linux/hid.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/hiddev.h>
|
2006-12-09 01:41:03 +08:00
|
|
|
#include "usbhid.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Version Information
|
|
|
|
*/
|
|
|
|
|
2005-09-05 13:12:01 +08:00
|
|
|
#define DRIVER_VERSION "v2.6"
|
2005-04-17 06:20:36 +08:00
|
|
|
#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik"
|
|
|
|
#define DRIVER_DESC "USB HID core driver"
|
|
|
|
#define DRIVER_LICENSE "GPL"
|
|
|
|
|
|
|
|
static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick",
|
|
|
|
"Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"};
|
|
|
|
/*
|
|
|
|
* Module parameters.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static unsigned int hid_mousepoll_interval;
|
|
|
|
module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644);
|
|
|
|
MODULE_PARM_DESC(mousepoll, "Polling interval of mice");
|
|
|
|
|
2006-02-01 01:58:38 +08:00
|
|
|
/*
|
|
|
|
* Input submission and I/O error handler.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void hid_io_error(struct hid_device *hid);
|
|
|
|
|
|
|
|
/* Start up the input URB */
|
|
|
|
static int hid_start_in(struct hid_device *hid)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
int rc = 0;
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2006-02-01 01:58:38 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
spin_lock_irqsave(&usbhid->inlock, flags);
|
|
|
|
if (hid->open > 0 && !test_bit(HID_SUSPENDED, &usbhid->iofl) &&
|
|
|
|
!test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
|
|
|
|
rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
|
2006-02-01 01:58:38 +08:00
|
|
|
if (rc != 0)
|
2006-12-09 01:41:03 +08:00
|
|
|
clear_bit(HID_IN_RUNNING, &usbhid->iofl);
|
2006-02-01 01:58:38 +08:00
|
|
|
}
|
2006-12-09 01:41:03 +08:00
|
|
|
spin_unlock_irqrestore(&usbhid->inlock, flags);
|
2006-02-01 01:58:38 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* I/O retry timer routine */
|
|
|
|
static void hid_retry_timeout(unsigned long _hid)
|
|
|
|
{
|
|
|
|
struct hid_device *hid = (struct hid_device *) _hid;
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2006-02-01 01:58:38 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
dev_dbg(&usbhid->intf->dev, "retrying intr urb\n");
|
2006-02-01 01:58:38 +08:00
|
|
|
if (hid_start_in(hid))
|
|
|
|
hid_io_error(hid);
|
|
|
|
}
|
|
|
|
|
2006-10-19 00:35:24 +08:00
|
|
|
/* Workqueue routine to reset the device or clear a halt */
|
2006-11-22 22:57:56 +08:00
|
|
|
static void hid_reset(struct work_struct *work)
|
2006-02-01 01:58:38 +08:00
|
|
|
{
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid =
|
|
|
|
container_of(work, struct usbhid_device, reset_work);
|
|
|
|
struct hid_device *hid = usbhid->hid;
|
2006-10-19 00:35:24 +08:00
|
|
|
int rc_lock, rc = 0;
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) {
|
|
|
|
dev_dbg(&usbhid->intf->dev, "clear halt\n");
|
|
|
|
rc = usb_clear_halt(to_usb_device(hid->dev), usbhid->urbin->pipe);
|
|
|
|
clear_bit(HID_CLEAR_HALT, &usbhid->iofl);
|
2006-10-19 00:35:24 +08:00
|
|
|
hid_start_in(hid);
|
|
|
|
}
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
else if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) {
|
|
|
|
dev_dbg(&usbhid->intf->dev, "resetting device\n");
|
|
|
|
rc = rc_lock = usb_lock_device_for_reset(to_usb_device(hid->dev), usbhid->intf);
|
2006-10-19 00:35:24 +08:00
|
|
|
if (rc_lock >= 0) {
|
2006-12-09 01:41:03 +08:00
|
|
|
rc = usb_reset_composite_device(to_usb_device(hid->dev), usbhid->intf);
|
2006-10-19 00:35:24 +08:00
|
|
|
if (rc_lock)
|
2006-12-09 01:41:03 +08:00
|
|
|
usb_unlock_device(to_usb_device(hid->dev));
|
2006-10-19 00:35:24 +08:00
|
|
|
}
|
2006-12-09 01:41:03 +08:00
|
|
|
clear_bit(HID_RESET_PENDING, &usbhid->iofl);
|
2006-02-01 01:58:38 +08:00
|
|
|
}
|
|
|
|
|
2006-06-02 01:55:28 +08:00
|
|
|
switch (rc) {
|
|
|
|
case 0:
|
2006-12-09 01:41:03 +08:00
|
|
|
if (!test_bit(HID_IN_RUNNING, &usbhid->iofl))
|
2006-02-01 01:58:38 +08:00
|
|
|
hid_io_error(hid);
|
2006-06-02 01:55:28 +08:00
|
|
|
break;
|
|
|
|
default:
|
2006-02-01 01:58:38 +08:00
|
|
|
err("can't reset device, %s-%s/input%d, status %d",
|
2006-12-09 01:41:03 +08:00
|
|
|
to_usb_device(hid->dev)->bus->bus_name,
|
|
|
|
to_usb_device(hid->dev)->devpath,
|
|
|
|
usbhid->ifnum, rc);
|
2006-06-02 01:55:28 +08:00
|
|
|
/* FALLTHROUGH */
|
|
|
|
case -EHOSTUNREACH:
|
|
|
|
case -ENODEV:
|
|
|
|
case -EINTR:
|
|
|
|
break;
|
|
|
|
}
|
2006-02-01 01:58:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Main I/O error handler */
|
|
|
|
static void hid_io_error(struct hid_device *hid)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2006-02-01 01:58:38 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
spin_lock_irqsave(&usbhid->inlock, flags);
|
2006-02-01 01:58:38 +08:00
|
|
|
|
|
|
|
/* Stop when disconnected */
|
2006-12-09 01:41:03 +08:00
|
|
|
if (usb_get_intfdata(usbhid->intf) == NULL)
|
2006-02-01 01:58:38 +08:00
|
|
|
goto done;
|
|
|
|
|
|
|
|
/* When an error occurs, retry at increasing intervals */
|
2006-12-09 01:41:03 +08:00
|
|
|
if (usbhid->retry_delay == 0) {
|
|
|
|
usbhid->retry_delay = 13; /* Then 26, 52, 104, 104, ... */
|
|
|
|
usbhid->stop_retry = jiffies + msecs_to_jiffies(1000);
|
|
|
|
} else if (usbhid->retry_delay < 100)
|
|
|
|
usbhid->retry_delay *= 2;
|
2006-02-01 01:58:38 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
if (time_after(jiffies, usbhid->stop_retry)) {
|
2006-02-01 01:58:38 +08:00
|
|
|
|
|
|
|
/* Retries failed, so do a port reset */
|
2006-12-09 01:41:03 +08:00
|
|
|
if (!test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) {
|
|
|
|
schedule_work(&usbhid->reset_work);
|
2006-10-19 00:35:24 +08:00
|
|
|
goto done;
|
2006-02-01 01:58:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
mod_timer(&usbhid->io_retry,
|
|
|
|
jiffies + msecs_to_jiffies(usbhid->retry_delay));
|
2006-02-01 01:58:38 +08:00
|
|
|
done:
|
2006-12-09 01:41:03 +08:00
|
|
|
spin_unlock_irqrestore(&usbhid->inlock, flags);
|
2006-02-01 01:58:38 +08:00
|
|
|
}
|
|
|
|
|
2006-12-09 01:40:44 +08:00
|
|
|
|
|
|
|
static int hid_input_report(int type, struct urb *urb, int interrupt)
|
|
|
|
{
|
|
|
|
struct hid_device *hid = urb->context;
|
|
|
|
struct hid_report_enum *report_enum = hid->report_enum + type;
|
|
|
|
u8 *data = urb->transfer_buffer;
|
|
|
|
int len = urb->actual_length;
|
|
|
|
struct hid_report *report;
|
|
|
|
int n, size;
|
|
|
|
|
|
|
|
if (!len) {
|
|
|
|
dbg("empty report");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_DATA
|
|
|
|
printk(KERN_DEBUG __FILE__ ": report (size %u) (%snumbered)\n", len, report_enum->numbered ? "" : "un");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
n = 0; /* Normally report number is 0 */
|
|
|
|
if (report_enum->numbered) { /* Device uses numbered reports, data[0] is report number */
|
|
|
|
n = *data++;
|
|
|
|
len--;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_DATA
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
printk(KERN_DEBUG __FILE__ ": report %d (size %u) = ", n, len);
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
printk(" %02x", data[i]);
|
|
|
|
printk("\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!(report = report_enum->report_id_hash[n])) {
|
|
|
|
dbg("undefined report_id %d received", n);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
size = ((report->size - 1) >> 3) + 1;
|
|
|
|
|
|
|
|
if (len < size) {
|
|
|
|
dbg("report %d is too short, (%d < %d)", report->id, len, size);
|
|
|
|
memset(data + len, 0, size - len);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hid->claimed & HID_CLAIMED_HIDDEV)
|
|
|
|
hiddev_report_event(hid, report);
|
|
|
|
|
|
|
|
for (n = 0; n < report->maxfield; n++)
|
|
|
|
hid_input_field(hid, report->field[n], data, interrupt);
|
|
|
|
|
|
|
|
if (hid->claimed & HID_CLAIMED_INPUT)
|
|
|
|
hidinput_report_event(hid, report);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Input interrupt completion handler.
|
|
|
|
*/
|
|
|
|
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
static void hid_irq_in(struct urb *urb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct hid_device *hid = urb->context;
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
int status;
|
|
|
|
|
|
|
|
switch (urb->status) {
|
|
|
|
case 0: /* success */
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid->retry_delay = 0;
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
hid_input_report(HID_INPUT_REPORT, urb, 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2006-10-19 00:35:24 +08:00
|
|
|
case -EPIPE: /* stall */
|
2006-12-09 01:41:03 +08:00
|
|
|
clear_bit(HID_IN_RUNNING, &usbhid->iofl);
|
|
|
|
set_bit(HID_CLEAR_HALT, &usbhid->iofl);
|
|
|
|
schedule_work(&usbhid->reset_work);
|
2006-10-19 00:35:24 +08:00
|
|
|
return;
|
2005-04-17 06:20:36 +08:00
|
|
|
case -ECONNRESET: /* unlink */
|
|
|
|
case -ENOENT:
|
|
|
|
case -ESHUTDOWN: /* unplug */
|
2006-12-09 01:41:03 +08:00
|
|
|
clear_bit(HID_IN_RUNNING, &usbhid->iofl);
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
2006-02-01 01:58:38 +08:00
|
|
|
case -EILSEQ: /* protocol error or unplug */
|
|
|
|
case -EPROTO: /* protocol error or unplug */
|
2006-09-19 13:49:02 +08:00
|
|
|
case -ETIME: /* protocol error or unplug */
|
|
|
|
case -ETIMEDOUT: /* Should never happen, but... */
|
2006-12-09 01:41:03 +08:00
|
|
|
clear_bit(HID_IN_RUNNING, &usbhid->iofl);
|
2006-02-01 01:58:38 +08:00
|
|
|
hid_io_error(hid);
|
|
|
|
return;
|
2005-04-17 06:20:36 +08:00
|
|
|
default: /* error */
|
|
|
|
warn("input irq status %d received", urb->status);
|
|
|
|
}
|
|
|
|
|
2006-12-07 12:33:16 +08:00
|
|
|
status = usb_submit_urb(urb, GFP_ATOMIC);
|
2006-02-01 01:58:38 +08:00
|
|
|
if (status) {
|
2006-12-09 01:41:03 +08:00
|
|
|
clear_bit(HID_IN_RUNNING, &usbhid->iofl);
|
2006-02-01 01:58:38 +08:00
|
|
|
if (status != -EPERM) {
|
|
|
|
err("can't resubmit intr, %s-%s/input%d, status %d",
|
2006-12-09 01:41:03 +08:00
|
|
|
to_usb_device(hid->dev)->bus->bus_name,
|
|
|
|
to_usb_device(hid->dev)->devpath,
|
|
|
|
usbhid->ifnum, status);
|
2006-02-01 01:58:38 +08:00
|
|
|
hid_io_error(hid);
|
|
|
|
}
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find a report field with a specified HID usage.
|
|
|
|
*/
|
2006-08-05 11:00:02 +08:00
|
|
|
#if 0
|
2005-04-17 06:20:36 +08:00
|
|
|
struct hid_field *hid_find_field_by_usage(struct hid_device *hid, __u32 wanted_usage, int type)
|
|
|
|
{
|
|
|
|
struct hid_report *report;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
list_for_each_entry(report, &hid->report_enum[type].report_list, list)
|
|
|
|
for (i = 0; i < report->maxfield; i++)
|
|
|
|
if (report->field[i]->logical == wanted_usage)
|
|
|
|
return report->field[i];
|
|
|
|
return NULL;
|
|
|
|
}
|
2006-08-05 11:00:02 +08:00
|
|
|
#endif /* 0 */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static int hid_submit_out(struct hid_device *hid)
|
|
|
|
{
|
|
|
|
struct hid_report *report;
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
report = usbhid->out[usbhid->outtail];
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
hid_output_report(report, usbhid->outbuf);
|
|
|
|
usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0);
|
|
|
|
usbhid->urbout->dev = to_usb_device(hid->dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
dbg("submitting out urb");
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
err("usb_submit_urb(out) failed");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hid_submit_ctrl(struct hid_device *hid)
|
|
|
|
{
|
|
|
|
struct hid_report *report;
|
|
|
|
unsigned char dir;
|
|
|
|
int len;
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
report = usbhid->ctrl[usbhid->ctrltail].report;
|
|
|
|
dir = usbhid->ctrl[usbhid->ctrltail].dir;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
|
|
|
|
if (dir == USB_DIR_OUT) {
|
2006-12-09 01:41:03 +08:00
|
|
|
hid_output_report(report, usbhid->ctrlbuf);
|
|
|
|
usbhid->urbctrl->pipe = usb_sndctrlpipe(to_usb_device(hid->dev), 0);
|
|
|
|
usbhid->urbctrl->transfer_buffer_length = len;
|
2005-04-17 06:20:36 +08:00
|
|
|
} else {
|
|
|
|
int maxpacket, padlen;
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid->urbctrl->pipe = usb_rcvctrlpipe(to_usb_device(hid->dev), 0);
|
|
|
|
maxpacket = usb_maxpacket(to_usb_device(hid->dev), usbhid->urbctrl->pipe, 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (maxpacket > 0) {
|
|
|
|
padlen = (len + maxpacket - 1) / maxpacket;
|
|
|
|
padlen *= maxpacket;
|
2006-12-09 01:41:03 +08:00
|
|
|
if (padlen > usbhid->bufsize)
|
|
|
|
padlen = usbhid->bufsize;
|
2005-04-17 06:20:36 +08:00
|
|
|
} else
|
|
|
|
padlen = 0;
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid->urbctrl->transfer_buffer_length = padlen;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid->urbctrl->dev = to_usb_device(hid->dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
|
|
|
|
usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT;
|
|
|
|
usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id);
|
|
|
|
usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum);
|
|
|
|
usbhid->cr->wLength = cpu_to_le16(len);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
dbg("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u",
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report",
|
|
|
|
usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
err("usb_submit_urb(ctrl) failed");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Output interrupt completion handler.
|
|
|
|
*/
|
|
|
|
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
static void hid_irq_out(struct urb *urb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct hid_device *hid = urb->context;
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long flags;
|
|
|
|
int unplug = 0;
|
|
|
|
|
|
|
|
switch (urb->status) {
|
|
|
|
case 0: /* success */
|
2005-09-05 13:13:03 +08:00
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
case -ESHUTDOWN: /* unplug */
|
|
|
|
unplug = 1;
|
2006-02-01 01:58:38 +08:00
|
|
|
case -EILSEQ: /* protocol error or unplug */
|
|
|
|
case -EPROTO: /* protocol error or unplug */
|
2005-04-17 06:20:36 +08:00
|
|
|
case -ECONNRESET: /* unlink */
|
|
|
|
case -ENOENT:
|
|
|
|
break;
|
|
|
|
default: /* error */
|
|
|
|
warn("output irq status %d received", urb->status);
|
|
|
|
}
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
spin_lock_irqsave(&usbhid->outlock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (unplug)
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid->outtail = usbhid->outhead;
|
2005-04-17 06:20:36 +08:00
|
|
|
else
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
if (usbhid->outhead != usbhid->outtail) {
|
2005-04-17 06:20:36 +08:00
|
|
|
if (hid_submit_out(hid)) {
|
2006-12-09 01:41:03 +08:00
|
|
|
clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
2005-04-17 06:20:36 +08:00
|
|
|
wake_up(&hid->wait);
|
|
|
|
}
|
2006-12-09 01:41:03 +08:00
|
|
|
spin_unlock_irqrestore(&usbhid->outlock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
|
|
|
spin_unlock_irqrestore(&usbhid->outlock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
wake_up(&hid->wait);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Control pipe completion handler.
|
|
|
|
*/
|
|
|
|
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
static void hid_ctrl(struct urb *urb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct hid_device *hid = urb->context;
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long flags;
|
|
|
|
int unplug = 0;
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
spin_lock_irqsave(&usbhid->ctrllock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
switch (urb->status) {
|
|
|
|
case 0: /* success */
|
2006-12-09 01:41:03 +08:00
|
|
|
if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN)
|
|
|
|
hid_input_report(usbhid->ctrl[usbhid->ctrltail].report->type, urb, 0);
|
2005-09-05 13:13:03 +08:00
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
case -ESHUTDOWN: /* unplug */
|
|
|
|
unplug = 1;
|
2006-02-01 01:58:38 +08:00
|
|
|
case -EILSEQ: /* protocol error or unplug */
|
|
|
|
case -EPROTO: /* protocol error or unplug */
|
2005-04-17 06:20:36 +08:00
|
|
|
case -ECONNRESET: /* unlink */
|
|
|
|
case -ENOENT:
|
|
|
|
case -EPIPE: /* report not available */
|
|
|
|
break;
|
|
|
|
default: /* error */
|
|
|
|
warn("ctrl urb status %d received", urb->status);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unplug)
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid->ctrltail = usbhid->ctrlhead;
|
2005-04-17 06:20:36 +08:00
|
|
|
else
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
if (usbhid->ctrlhead != usbhid->ctrltail) {
|
2005-04-17 06:20:36 +08:00
|
|
|
if (hid_submit_ctrl(hid)) {
|
2006-12-09 01:41:03 +08:00
|
|
|
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
2005-04-17 06:20:36 +08:00
|
|
|
wake_up(&hid->wait);
|
|
|
|
}
|
2006-12-09 01:41:03 +08:00
|
|
|
spin_unlock_irqrestore(&usbhid->ctrllock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
|
|
|
spin_unlock_irqrestore(&usbhid->ctrllock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
wake_up(&hid->wait);
|
|
|
|
}
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int head;
|
|
|
|
unsigned long flags;
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN)
|
|
|
|
return;
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
if (usbhid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) {
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
spin_lock_irqsave(&usbhid->outlock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
if ((head = (usbhid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == usbhid->outtail) {
|
|
|
|
spin_unlock_irqrestore(&usbhid->outlock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
warn("output queue full");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid->out[usbhid->outhead] = report;
|
|
|
|
usbhid->outhead = head;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl))
|
2005-04-17 06:20:36 +08:00
|
|
|
if (hid_submit_out(hid))
|
2006-12-09 01:41:03 +08:00
|
|
|
clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
spin_unlock_irqrestore(&usbhid->outlock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
spin_lock_irqsave(&usbhid->ctrllock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
if ((head = (usbhid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == usbhid->ctrltail) {
|
|
|
|
spin_unlock_irqrestore(&usbhid->ctrllock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
warn("control queue full");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid->ctrl[usbhid->ctrlhead].report = report;
|
|
|
|
usbhid->ctrl[usbhid->ctrlhead].dir = dir;
|
|
|
|
usbhid->ctrlhead = head;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl))
|
2005-04-17 06:20:36 +08:00
|
|
|
if (hid_submit_ctrl(hid))
|
2006-12-09 01:41:03 +08:00
|
|
|
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
spin_unlock_irqrestore(&usbhid->ctrllock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-12-09 01:40:53 +08:00
|
|
|
static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
|
2006-12-09 01:40:44 +08:00
|
|
|
{
|
|
|
|
struct hid_device *hid = dev->private;
|
|
|
|
struct hid_field *field;
|
|
|
|
int offset;
|
|
|
|
|
|
|
|
if (type == EV_FF)
|
|
|
|
return input_ff_event(dev, type, code, value);
|
|
|
|
|
|
|
|
if (type != EV_LED)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) {
|
|
|
|
warn("event field not found");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
hid_set_field(field, offset, value);
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid_submit_report(hid, field->report, USB_DIR_OUT);
|
2006-12-09 01:40:44 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
int usbhid_wait_io(struct hid_device *hid)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
|
|
|
|
|
|
|
if (!wait_event_timeout(hid->wait, (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl) &&
|
|
|
|
!test_bit(HID_OUT_RUNNING, &usbhid->iofl)),
|
2005-04-17 06:20:36 +08:00
|
|
|
10*HZ)) {
|
|
|
|
dbg("timeout waiting for ctrl or out queue to clear");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-05-29 15:28:00 +08:00
|
|
|
static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle)
|
|
|
|
{
|
2005-05-29 15:28:14 +08:00
|
|
|
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
2005-05-29 15:28:00 +08:00
|
|
|
HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, (idle << 8) | report,
|
|
|
|
ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int hid_get_class_descriptor(struct usb_device *dev, int ifnum,
|
|
|
|
unsigned char type, void *buf, int size)
|
|
|
|
{
|
|
|
|
int result, retries = 4;
|
|
|
|
|
|
|
|
memset(buf,0,size); // Make sure we parse really received data
|
|
|
|
|
|
|
|
do {
|
|
|
|
result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
|
|
|
|
USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN,
|
|
|
|
(type << 8), ifnum, buf, size, USB_CTRL_GET_TIMEOUT);
|
|
|
|
retries--;
|
|
|
|
} while (result < size && retries);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
int usbhid_open(struct hid_device *hid)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-02-01 01:58:38 +08:00
|
|
|
++hid->open;
|
|
|
|
if (hid_start_in(hid))
|
|
|
|
hid_io_error(hid);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
void usbhid_close(struct hid_device *hid)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!--hid->open)
|
2006-12-09 01:41:03 +08:00
|
|
|
usb_kill_urb(usbhid->urbin);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-12-09 01:40:44 +08:00
|
|
|
static int hidinput_open(struct input_dev *dev)
|
|
|
|
{
|
|
|
|
struct hid_device *hid = dev->private;
|
2006-12-09 01:41:03 +08:00
|
|
|
return usbhid_open(hid);
|
2006-12-09 01:40:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void hidinput_close(struct input_dev *dev)
|
|
|
|
{
|
|
|
|
struct hid_device *hid = dev->private;
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid_close(hid);
|
2006-12-09 01:40:44 +08:00
|
|
|
}
|
|
|
|
|
2006-03-30 04:41:07 +08:00
|
|
|
#define USB_VENDOR_ID_PANJIT 0x134c
|
|
|
|
|
2006-10-18 20:47:37 +08:00
|
|
|
#define USB_VENDOR_ID_TURBOX 0x062a
|
|
|
|
#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Initialize all reports
|
|
|
|
*/
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
void usbhid_init_reports(struct hid_device *hid)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct hid_report *report;
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
int err, ret;
|
|
|
|
|
2005-09-05 13:12:01 +08:00
|
|
|
list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list)
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid_submit_report(hid, report, USB_DIR_IN);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid_submit_report(hid, report, USB_DIR_IN);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
err = 0;
|
2006-12-09 01:41:03 +08:00
|
|
|
ret = usbhid_wait_io(hid);
|
2005-04-17 06:20:36 +08:00
|
|
|
while (ret) {
|
|
|
|
err |= ret;
|
2006-12-09 01:41:03 +08:00
|
|
|
if (test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
|
|
|
|
usb_kill_urb(usbhid->urbctrl);
|
|
|
|
if (test_bit(HID_OUT_RUNNING, &usbhid->iofl))
|
|
|
|
usb_kill_urb(usbhid->urbout);
|
|
|
|
ret = usbhid_wait_io(hid);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (err)
|
2006-01-06 19:45:28 +08:00
|
|
|
warn("timeout initializing reports");
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-08-29 10:58:28 +08:00
|
|
|
#define USB_VENDOR_ID_GTCO 0x078c
|
|
|
|
#define USB_DEVICE_ID_GTCO_90 0x0090
|
|
|
|
#define USB_DEVICE_ID_GTCO_100 0x0100
|
|
|
|
#define USB_DEVICE_ID_GTCO_101 0x0101
|
|
|
|
#define USB_DEVICE_ID_GTCO_103 0x0103
|
|
|
|
#define USB_DEVICE_ID_GTCO_104 0x0104
|
|
|
|
#define USB_DEVICE_ID_GTCO_105 0x0105
|
|
|
|
#define USB_DEVICE_ID_GTCO_106 0x0106
|
|
|
|
#define USB_DEVICE_ID_GTCO_107 0x0107
|
|
|
|
#define USB_DEVICE_ID_GTCO_108 0x0108
|
|
|
|
#define USB_DEVICE_ID_GTCO_200 0x0200
|
|
|
|
#define USB_DEVICE_ID_GTCO_201 0x0201
|
|
|
|
#define USB_DEVICE_ID_GTCO_202 0x0202
|
|
|
|
#define USB_DEVICE_ID_GTCO_203 0x0203
|
|
|
|
#define USB_DEVICE_ID_GTCO_204 0x0204
|
|
|
|
#define USB_DEVICE_ID_GTCO_205 0x0205
|
|
|
|
#define USB_DEVICE_ID_GTCO_206 0x0206
|
|
|
|
#define USB_DEVICE_ID_GTCO_207 0x0207
|
|
|
|
#define USB_DEVICE_ID_GTCO_300 0x0300
|
|
|
|
#define USB_DEVICE_ID_GTCO_301 0x0301
|
|
|
|
#define USB_DEVICE_ID_GTCO_302 0x0302
|
|
|
|
#define USB_DEVICE_ID_GTCO_303 0x0303
|
|
|
|
#define USB_DEVICE_ID_GTCO_304 0x0304
|
|
|
|
#define USB_DEVICE_ID_GTCO_305 0x0305
|
|
|
|
#define USB_DEVICE_ID_GTCO_306 0x0306
|
|
|
|
#define USB_DEVICE_ID_GTCO_307 0x0307
|
|
|
|
#define USB_DEVICE_ID_GTCO_308 0x0308
|
|
|
|
#define USB_DEVICE_ID_GTCO_309 0x0309
|
|
|
|
#define USB_DEVICE_ID_GTCO_400 0x0400
|
|
|
|
#define USB_DEVICE_ID_GTCO_401 0x0401
|
|
|
|
#define USB_DEVICE_ID_GTCO_402 0x0402
|
|
|
|
#define USB_DEVICE_ID_GTCO_403 0x0403
|
|
|
|
#define USB_DEVICE_ID_GTCO_404 0x0404
|
2006-09-04 19:41:10 +08:00
|
|
|
#define USB_DEVICE_ID_GTCO_405 0x0405
|
2006-08-29 10:58:28 +08:00
|
|
|
#define USB_DEVICE_ID_GTCO_500 0x0500
|
|
|
|
#define USB_DEVICE_ID_GTCO_501 0x0501
|
|
|
|
#define USB_DEVICE_ID_GTCO_502 0x0502
|
|
|
|
#define USB_DEVICE_ID_GTCO_503 0x0503
|
|
|
|
#define USB_DEVICE_ID_GTCO_504 0x0504
|
|
|
|
#define USB_DEVICE_ID_GTCO_1000 0x1000
|
|
|
|
#define USB_DEVICE_ID_GTCO_1001 0x1001
|
|
|
|
#define USB_DEVICE_ID_GTCO_1002 0x1002
|
|
|
|
#define USB_DEVICE_ID_GTCO_1003 0x1003
|
|
|
|
#define USB_DEVICE_ID_GTCO_1004 0x1004
|
|
|
|
#define USB_DEVICE_ID_GTCO_1005 0x1005
|
|
|
|
#define USB_DEVICE_ID_GTCO_1006 0x1006
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#define USB_VENDOR_ID_WACOM 0x056a
|
|
|
|
|
2005-06-06 15:22:37 +08:00
|
|
|
#define USB_VENDOR_ID_ACECAD 0x0460
|
|
|
|
#define USB_DEVICE_ID_ACECAD_FLAIR 0x0004
|
|
|
|
#define USB_DEVICE_ID_ACECAD_302 0x0008
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#define USB_VENDOR_ID_KBGEAR 0x084e
|
|
|
|
#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001
|
|
|
|
|
|
|
|
#define USB_VENDOR_ID_AIPTEK 0x08ca
|
|
|
|
#define USB_DEVICE_ID_AIPTEK_01 0x0001
|
|
|
|
#define USB_DEVICE_ID_AIPTEK_10 0x0010
|
|
|
|
#define USB_DEVICE_ID_AIPTEK_20 0x0020
|
|
|
|
#define USB_DEVICE_ID_AIPTEK_21 0x0021
|
|
|
|
#define USB_DEVICE_ID_AIPTEK_22 0x0022
|
|
|
|
#define USB_DEVICE_ID_AIPTEK_23 0x0023
|
|
|
|
#define USB_DEVICE_ID_AIPTEK_24 0x0024
|
|
|
|
|
|
|
|
#define USB_VENDOR_ID_GRIFFIN 0x077d
|
|
|
|
#define USB_DEVICE_ID_POWERMATE 0x0410
|
|
|
|
#define USB_DEVICE_ID_SOUNDKNOB 0x04AA
|
|
|
|
|
|
|
|
#define USB_VENDOR_ID_ATEN 0x0557
|
|
|
|
#define USB_DEVICE_ID_ATEN_UC100KM 0x2004
|
|
|
|
#define USB_DEVICE_ID_ATEN_CS124U 0x2202
|
|
|
|
#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204
|
|
|
|
#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205
|
|
|
|
#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208
|
|
|
|
|
|
|
|
#define USB_VENDOR_ID_TOPMAX 0x0663
|
|
|
|
#define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103
|
|
|
|
|
|
|
|
#define USB_VENDOR_ID_HAPP 0x078b
|
|
|
|
#define USB_DEVICE_ID_UGCI_DRIVING 0x0010
|
|
|
|
#define USB_DEVICE_ID_UGCI_FLYING 0x0020
|
|
|
|
#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030
|
|
|
|
|
|
|
|
#define USB_VENDOR_ID_MGE 0x0463
|
|
|
|
#define USB_DEVICE_ID_MGE_UPS 0xffff
|
|
|
|
#define USB_DEVICE_ID_MGE_UPS1 0x0001
|
|
|
|
|
|
|
|
#define USB_VENDOR_ID_ONTRAK 0x0a07
|
|
|
|
#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064
|
|
|
|
|
|
|
|
#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f
|
|
|
|
#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
|
|
|
|
|
|
|
|
#define USB_VENDOR_ID_A4TECH 0x09da
|
|
|
|
#define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006
|
|
|
|
|
2005-09-05 13:13:32 +08:00
|
|
|
#define USB_VENDOR_ID_AASHIMA 0x06d6
|
2005-07-11 14:08:40 +08:00
|
|
|
#define USB_DEVICE_ID_AASHIMA_GAMEPAD 0x0025
|
2005-09-05 13:13:32 +08:00
|
|
|
#define USB_DEVICE_ID_AASHIMA_PREDATOR 0x0026
|
2005-07-11 14:08:40 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#define USB_VENDOR_ID_CYPRESS 0x04b4
|
|
|
|
#define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001
|
|
|
|
#define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500
|
2005-09-05 14:57:41 +08:00
|
|
|
#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#define USB_VENDOR_ID_BERKSHIRE 0x0c98
|
|
|
|
#define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140
|
|
|
|
|
|
|
|
#define USB_VENDOR_ID_ALPS 0x0433
|
|
|
|
#define USB_DEVICE_ID_IBM_GAMEPAD 0x1101
|
|
|
|
|
|
|
|
#define USB_VENDOR_ID_SAITEK 0x06a3
|
|
|
|
#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
|
|
|
|
|
|
|
|
#define USB_VENDOR_ID_NEC 0x073e
|
|
|
|
#define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301
|
|
|
|
|
|
|
|
#define USB_VENDOR_ID_CHIC 0x05fe
|
|
|
|
#define USB_DEVICE_ID_CHIC_GAMEPAD 0x0014
|
|
|
|
|
|
|
|
#define USB_VENDOR_ID_GLAB 0x06c2
|
|
|
|
#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
|
|
|
|
#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039
|
|
|
|
#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040
|
2006-07-09 21:01:02 +08:00
|
|
|
#define USB_DEVICE_ID_0_16_16_IF_KIT 0x0044
|
|
|
|
#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045
|
|
|
|
#define USB_DEVICE_ID_0_8_7_IF_KIT 0x0051
|
2005-04-17 06:20:36 +08:00
|
|
|
#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053
|
2006-07-09 21:01:02 +08:00
|
|
|
#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#define USB_VENDOR_ID_WISEGROUP 0x0925
|
|
|
|
#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101
|
|
|
|
#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104
|
2006-07-09 21:01:02 +08:00
|
|
|
#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201
|
2006-02-25 22:52:27 +08:00
|
|
|
#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-06-30 15:44:03 +08:00
|
|
|
#define USB_VENDOR_ID_WISEGROUP_LTD 0x6677
|
|
|
|
#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#define USB_VENDOR_ID_CODEMERCS 0x07c0
|
|
|
|
#define USB_DEVICE_ID_CODEMERCS_IOW40 0x1500
|
|
|
|
#define USB_DEVICE_ID_CODEMERCS_IOW24 0x1501
|
|
|
|
#define USB_DEVICE_ID_CODEMERCS_IOW48 0x1502
|
|
|
|
#define USB_DEVICE_ID_CODEMERCS_IOW28 0x1503
|
|
|
|
|
|
|
|
#define USB_VENDOR_ID_DELORME 0x1163
|
|
|
|
#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100
|
2005-05-10 13:17:17 +08:00
|
|
|
#define USB_DEVICE_ID_DELORME_EM_LT20 0x0200
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#define USB_VENDOR_ID_MCC 0x09db
|
|
|
|
#define USB_DEVICE_ID_MCC_PMD1024LS 0x0076
|
|
|
|
#define USB_DEVICE_ID_MCC_PMD1208LS 0x007a
|
|
|
|
|
2005-06-03 13:18:12 +08:00
|
|
|
#define USB_VENDOR_ID_VERNIER 0x08f7
|
|
|
|
#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001
|
|
|
|
#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002
|
|
|
|
#define USB_DEVICE_ID_VERNIER_SKIP 0x0003
|
|
|
|
#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004
|
|
|
|
|
2005-06-28 04:44:22 +08:00
|
|
|
#define USB_VENDOR_ID_LD 0x0f11
|
2006-02-02 16:36:43 +08:00
|
|
|
#define USB_DEVICE_ID_LD_CASSY 0x1000
|
|
|
|
#define USB_DEVICE_ID_LD_POCKETCASSY 0x1010
|
|
|
|
#define USB_DEVICE_ID_LD_MOBILECASSY 0x1020
|
|
|
|
#define USB_DEVICE_ID_LD_JWM 0x1080
|
|
|
|
#define USB_DEVICE_ID_LD_DMMP 0x1081
|
|
|
|
#define USB_DEVICE_ID_LD_UMIP 0x1090
|
|
|
|
#define USB_DEVICE_ID_LD_XRAY1 0x1100
|
|
|
|
#define USB_DEVICE_ID_LD_XRAY2 0x1101
|
|
|
|
#define USB_DEVICE_ID_LD_VIDEOCOM 0x1200
|
|
|
|
#define USB_DEVICE_ID_LD_COM3LAB 0x2000
|
|
|
|
#define USB_DEVICE_ID_LD_TELEPORT 0x2010
|
|
|
|
#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020
|
|
|
|
#define USB_DEVICE_ID_LD_POWERCONTROL 0x2030
|
|
|
|
#define USB_DEVICE_ID_LD_MACHINETEST 0x2040
|
2005-06-28 04:44:22 +08:00
|
|
|
|
2005-09-05 13:13:15 +08:00
|
|
|
#define USB_VENDOR_ID_APPLE 0x05ac
|
[PATCH] USB HID/HIDBP, INPUT DRIVERS: fix various usb/input/hid-input.c bugs that make Apple Mighty Mouse work poorly
Transposed lines of code in drivers/usb/input/hid-input.c causes the
capability bits for a new HID device to be set before quirks are applied
at configuration time. When an HID event is then sent up to the input
layer, it may then be discarded as irrelevant because the wrong
capability bit is set.
Further, the quirks for the Apple Mighty Mouse are not quite right: the
horizontal scrolling needs its axis reversed, and the left and center
buttons are transposed. Also, the mouse is labeled in the kernel with
its earlier name (I think) of Apple PowerMouse.
Steps to reproduce problem: Plug in an Apple Mighty Mouse. Note that
horizontal scrolling doesn't work at all, and in fact doesn't generate
any input events on /dev/input/eventN. Note also that pushing the
middle button performs the right button action, and vice versa. Once
you have the horizontal scrolling working, note that it is backward WRT
both to vertical scrolling and to common sense.
This patch maybe should be broken up, as it does address two problems.
The transposed code in hidinput_configure_usage() probably creates bugs
beyond just the Mighty Mouse. The rest of the patch renames POWERMOUSE
to MIGHTYMOUSE everywhere (which I *believe* is correct), fixes the
MIGHTYMOUSE quirk to swap the center and right mouse buttons, and adds a
new quirk HID_QUIRK_INVERT_HWHEEL also assigned to the MIGHTYMOUSE with
code in hidinput_hid_event() to implement it.
Signed-off-by: Bart Massey <bart@cs.pdx.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2006-05-09 05:40:13 +08:00
|
|
|
#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304
|
2006-11-15 13:00:17 +08:00
|
|
|
#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e
|
|
|
|
#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f
|
|
|
|
#define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214
|
|
|
|
#define USB_DEVICE_ID_APPLE_GEYSER_ISO 0x0215
|
|
|
|
#define USB_DEVICE_ID_APPLE_GEYSER_JIS 0x0216
|
|
|
|
#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI 0x0217
|
|
|
|
#define USB_DEVICE_ID_APPLE_GEYSER3_ISO 0x0218
|
|
|
|
#define USB_DEVICE_ID_APPLE_GEYSER3_JIS 0x0219
|
|
|
|
#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI 0x021a
|
|
|
|
#define USB_DEVICE_ID_APPLE_GEYSER4_ISO 0x021b
|
|
|
|
#define USB_DEVICE_ID_APPLE_GEYSER4_JIS 0x021c
|
|
|
|
#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
|
|
|
|
#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-01-14 13:25:39 +08:00
|
|
|
#define USB_VENDOR_ID_CHERRY 0x046a
|
|
|
|
#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023
|
|
|
|
|
2006-05-15 18:34:43 +08:00
|
|
|
#define USB_VENDOR_ID_YEALINK 0x6993
|
|
|
|
#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001
|
2006-09-16 22:17:34 +08:00
|
|
|
|
|
|
|
#define USB_VENDOR_ID_ALCOR 0x058f
|
|
|
|
#define USB_DEVICE_ID_ALCOR_USBRS232 0x9720
|
|
|
|
|
2006-09-15 22:23:35 +08:00
|
|
|
#define USB_VENDOR_ID_SUN 0x0430
|
|
|
|
#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab
|
|
|
|
|
2006-10-28 03:08:54 +08:00
|
|
|
#define USB_VENDOR_ID_AIRCABLE 0x16CA
|
|
|
|
#define USB_DEVICE_ID_AIRCABLE1 0x1502
|
|
|
|
|
2006-11-05 11:49:53 +08:00
|
|
|
#define USB_VENDOR_ID_LOGITECH 0x046d
|
|
|
|
#define USB_DEVICE_ID_LOGITECH_USB_RECEIVER 0xc101
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Alphabetically sorted blacklist by quirk type.
|
|
|
|
*/
|
|
|
|
|
2005-11-29 16:43:42 +08:00
|
|
|
static const struct hid_blacklist {
|
2005-04-17 06:20:36 +08:00
|
|
|
__u16 idVendor;
|
|
|
|
__u16 idProduct;
|
|
|
|
unsigned quirks;
|
|
|
|
} hid_blacklist[] = {
|
|
|
|
|
|
|
|
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE },
|
2006-10-28 03:08:54 +08:00
|
|
|
{ USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1, HID_QUIRK_IGNORE },
|
2006-09-16 22:17:34 +08:00
|
|
|
{ USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE },
|
2005-04-17 06:20:36 +08:00
|
|
|
{ USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW48, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW28, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM, HID_QUIRK_IGNORE },
|
2005-09-05 14:57:41 +08:00
|
|
|
{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE, HID_QUIRK_IGNORE },
|
2005-04-17 06:20:36 +08:00
|
|
|
{ USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE },
|
2005-05-10 13:17:17 +08:00
|
|
|
{ USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20, HID_QUIRK_IGNORE },
|
2005-04-17 06:20:36 +08:00
|
|
|
{ USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE },
|
2006-07-09 21:01:02 +08:00
|
|
|
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT, HID_QUIRK_IGNORE },
|
2005-04-17 06:20:36 +08:00
|
|
|
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE },
|
2006-07-09 21:01:02 +08:00
|
|
|
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE },
|
2005-04-17 06:20:36 +08:00
|
|
|
{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE },
|
2006-08-29 10:58:28 +08:00
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404, HID_QUIRK_IGNORE },
|
2006-09-04 19:41:10 +08:00
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405, HID_QUIRK_IGNORE },
|
2006-08-29 10:58:28 +08:00
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006, HID_QUIRK_IGNORE },
|
2005-04-17 06:20:36 +08:00
|
|
|
{ USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE },
|
2006-02-02 16:36:43 +08:00
|
|
|
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST, HID_QUIRK_IGNORE },
|
2005-04-17 06:20:36 +08:00
|
|
|
{ USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE },
|
2006-08-09 05:42:06 +08:00
|
|
|
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30, HID_QUIRK_IGNORE },
|
2005-04-17 06:20:36 +08:00
|
|
|
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE },
|
2006-08-09 05:42:06 +08:00
|
|
|
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118, HID_QUIRK_IGNORE },
|
2005-04-17 06:20:36 +08:00
|
|
|
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE },
|
2005-06-03 13:18:12 +08:00
|
|
|
{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE },
|
2005-04-17 06:20:36 +08:00
|
|
|
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
|
2006-07-09 21:01:02 +08:00
|
|
|
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE },
|
2006-05-15 18:34:43 +08:00
|
|
|
{ USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE },
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-06-06 15:22:37 +08:00
|
|
|
{ USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE },
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
|
|
|
|
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
|
|
|
|
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
|
|
|
|
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
|
|
|
|
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
|
2006-09-15 22:23:35 +08:00
|
|
|
{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
|
2006-02-25 22:52:27 +08:00
|
|
|
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
|
2006-06-30 15:44:03 +08:00
|
|
|
{ USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
|
2005-04-17 06:20:36 +08:00
|
|
|
|
[PATCH] USB HID/HIDBP, INPUT DRIVERS: fix various usb/input/hid-input.c bugs that make Apple Mighty Mouse work poorly
Transposed lines of code in drivers/usb/input/hid-input.c causes the
capability bits for a new HID device to be set before quirks are applied
at configuration time. When an HID event is then sent up to the input
layer, it may then be discarded as irrelevant because the wrong
capability bit is set.
Further, the quirks for the Apple Mighty Mouse are not quite right: the
horizontal scrolling needs its axis reversed, and the left and center
buttons are transposed. Also, the mouse is labeled in the kernel with
its earlier name (I think) of Apple PowerMouse.
Steps to reproduce problem: Plug in an Apple Mighty Mouse. Note that
horizontal scrolling doesn't work at all, and in fact doesn't generate
any input events on /dev/input/eventN. Note also that pushing the
middle button performs the right button action, and vice versa. Once
you have the horizontal scrolling working, note that it is backward WRT
both to vertical scrolling and to common sense.
This patch maybe should be broken up, as it does address two problems.
The transposed code in hidinput_configure_usage() probably creates bugs
beyond just the Mighty Mouse. The rest of the patch renames POWERMOUSE
to MIGHTYMOUSE everywhere (which I *believe* is correct), fixes the
MIGHTYMOUSE quirk to swap the center and right mouse buttons, and adds a
new quirk HID_QUIRK_INVERT_HWHEEL also assigned to the MIGHTYMOUSE with
code in hidinput_hid_event() to implement it.
Signed-off-by: Bart Massey <bart@cs.pdx.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2006-05-09 05:40:13 +08:00
|
|
|
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL },
|
2005-04-17 06:20:36 +08:00
|
|
|
{ USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_7 },
|
|
|
|
{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 },
|
|
|
|
|
2005-07-11 14:08:40 +08:00
|
|
|
{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD },
|
2005-09-05 13:13:32 +08:00
|
|
|
{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD },
|
2005-04-17 06:20:36 +08:00
|
|
|
{ USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD },
|
|
|
|
{ USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD },
|
|
|
|
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
|
|
|
|
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
|
|
|
|
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
|
|
|
|
{ USB_VENDOR_ID_NEC, USB_DEVICE_ID_NEC_USB_GAME_PAD, HID_QUIRK_BADPAD },
|
|
|
|
{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
|
|
|
|
{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
|
|
|
|
|
2006-01-14 13:25:39 +08:00
|
|
|
{ USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION },
|
|
|
|
|
2006-11-15 13:00:17 +08:00
|
|
|
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI, HID_QUIRK_POWERBOOK_HAS_FN },
|
|
|
|
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO, HID_QUIRK_POWERBOOK_HAS_FN },
|
|
|
|
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI, HID_QUIRK_POWERBOOK_HAS_FN },
|
|
|
|
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
|
|
|
|
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS, HID_QUIRK_POWERBOOK_HAS_FN },
|
|
|
|
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI, HID_QUIRK_POWERBOOK_HAS_FN },
|
|
|
|
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
|
|
|
|
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS, HID_QUIRK_POWERBOOK_HAS_FN },
|
|
|
|
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI, HID_QUIRK_POWERBOOK_HAS_FN },
|
|
|
|
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO, HID_QUIRK_POWERBOOK_HAS_FN },
|
|
|
|
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_POWERBOOK_HAS_FN },
|
|
|
|
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN },
|
|
|
|
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN },
|
2006-01-14 23:08:06 +08:00
|
|
|
|
2006-03-30 04:41:07 +08:00
|
|
|
{ USB_VENDOR_ID_PANJIT, 0x0001, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_PANJIT, 0x0002, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_PANJIT, 0x0003, HID_QUIRK_IGNORE },
|
|
|
|
{ USB_VENDOR_ID_PANJIT, 0x0004, HID_QUIRK_IGNORE },
|
|
|
|
|
2006-10-18 20:47:37 +08:00
|
|
|
{ USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET },
|
2006-11-05 11:49:53 +08:00
|
|
|
|
|
|
|
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_USB_RECEIVER, HID_QUIRK_BAD_RELATIVE_KEYS },
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
{ 0, 0 }
|
|
|
|
};
|
|
|
|
|
2005-09-05 13:12:01 +08:00
|
|
|
/*
|
|
|
|
* Traverse the supplied list of reports and find the longest
|
|
|
|
*/
|
|
|
|
static void hid_find_max_report(struct hid_device *hid, unsigned int type, int *max)
|
|
|
|
{
|
|
|
|
struct hid_report *report;
|
|
|
|
int size;
|
|
|
|
|
|
|
|
list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
|
|
|
|
size = ((report->size - 1) >> 3) + 1;
|
|
|
|
if (type == HID_INPUT_REPORT && hid->report_enum[type].numbered)
|
|
|
|
size++;
|
|
|
|
if (*max < size)
|
|
|
|
*max = size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid)
|
|
|
|
{
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
|
|
|
|
|
|
|
if (!(usbhid->inbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->inbuf_dma)))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -1;
|
2006-12-09 01:41:03 +08:00
|
|
|
if (!(usbhid->outbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->outbuf_dma)))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -1;
|
2006-12-09 01:41:03 +08:00
|
|
|
if (!(usbhid->cr = usb_buffer_alloc(dev, sizeof(*(usbhid->cr)), GFP_ATOMIC, &usbhid->cr_dma)))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -1;
|
2006-12-09 01:41:03 +08:00
|
|
|
if (!(usbhid->ctrlbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->ctrlbuf_dma)))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
|
|
|
|
{
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
|
|
|
|
|
|
|
if (usbhid->inbuf)
|
|
|
|
usb_buffer_free(dev, usbhid->bufsize, usbhid->inbuf, usbhid->inbuf_dma);
|
|
|
|
if (usbhid->outbuf)
|
|
|
|
usb_buffer_free(dev, usbhid->bufsize, usbhid->outbuf, usbhid->outbuf_dma);
|
|
|
|
if (usbhid->cr)
|
|
|
|
usb_buffer_free(dev, sizeof(*(usbhid->cr)), usbhid->cr, usbhid->cr_dma);
|
|
|
|
if (usbhid->ctrlbuf)
|
|
|
|
usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-01-14 13:25:39 +08:00
|
|
|
/*
|
|
|
|
* Cherry Cymotion keyboard have an invalid HID report descriptor,
|
|
|
|
* that needs fixing before we can parse it.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void hid_fixup_cymotion_descriptor(char *rdesc, int rsize)
|
|
|
|
{
|
|
|
|
if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) {
|
|
|
|
info("Fixing up Cherry Cymotion report descriptor");
|
|
|
|
rdesc[11] = rdesc[16] = 0xff;
|
|
|
|
rdesc[12] = rdesc[17] = 0x03;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static struct hid_device *usb_hid_configure(struct usb_interface *intf)
|
|
|
|
{
|
|
|
|
struct usb_host_interface *interface = intf->cur_altsetting;
|
|
|
|
struct usb_device *dev = interface_to_usbdev (intf);
|
|
|
|
struct hid_descriptor *hdesc;
|
|
|
|
struct hid_device *hid;
|
|
|
|
unsigned quirks = 0, rsize = 0;
|
2005-09-15 15:01:47 +08:00
|
|
|
char *rdesc;
|
|
|
|
int n, len, insize = 0;
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-07-15 07:50:04 +08:00
|
|
|
/* Ignore all Wacom devices */
|
2006-09-23 08:29:34 +08:00
|
|
|
if (le16_to_cpu(dev->descriptor.idVendor) == USB_VENDOR_ID_WACOM)
|
2006-07-15 07:50:04 +08:00
|
|
|
return NULL;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
for (n = 0; hid_blacklist[n].idVendor; n++)
|
|
|
|
if ((hid_blacklist[n].idVendor == le16_to_cpu(dev->descriptor.idVendor)) &&
|
|
|
|
(hid_blacklist[n].idProduct == le16_to_cpu(dev->descriptor.idProduct)))
|
|
|
|
quirks = hid_blacklist[n].quirks;
|
|
|
|
|
2006-05-16 02:49:04 +08:00
|
|
|
/* Many keyboards and mice don't like to be polled for reports,
|
|
|
|
* so we will always set the HID_QUIRK_NOGET flag for them. */
|
|
|
|
if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) {
|
|
|
|
if (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD ||
|
|
|
|
interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)
|
|
|
|
quirks |= HID_QUIRK_NOGET;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (quirks & HID_QUIRK_IGNORE)
|
|
|
|
return NULL;
|
|
|
|
|
2005-09-15 15:01:47 +08:00
|
|
|
if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) &&
|
|
|
|
(!interface->desc.bNumEndpoints ||
|
|
|
|
usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
|
|
|
|
dbg("class descriptor not present\n");
|
|
|
|
return NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for (n = 0; n < hdesc->bNumDescriptors; n++)
|
|
|
|
if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)
|
|
|
|
rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);
|
|
|
|
|
|
|
|
if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
|
|
|
|
dbg("weird size of report descriptor (%u)", rsize);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
|
|
|
|
dbg("couldn't allocate rdesc memory");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2005-05-29 15:28:00 +08:00
|
|
|
hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) {
|
|
|
|
dbg("reading report descriptor failed");
|
|
|
|
kfree(rdesc);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-01-14 13:25:39 +08:00
|
|
|
if ((quirks & HID_QUIRK_CYMOTION))
|
|
|
|
hid_fixup_cymotion_descriptor(rdesc, rsize);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifdef DEBUG_DATA
|
|
|
|
printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n);
|
|
|
|
for (n = 0; n < rsize; n++)
|
|
|
|
printk(" %02x", (unsigned char) rdesc[n]);
|
|
|
|
printk("\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!(hid = hid_parse_report(rdesc, n))) {
|
|
|
|
dbg("parsing report descriptor failed");
|
|
|
|
kfree(rdesc);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(rdesc);
|
|
|
|
hid->quirks = quirks;
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
if (!(usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL)))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
hid->driver_data = usbhid;
|
|
|
|
usbhid->hid = hid;
|
|
|
|
|
|
|
|
usbhid->bufsize = HID_MIN_BUFFER_SIZE;
|
|
|
|
hid_find_max_report(hid, HID_INPUT_REPORT, &usbhid->bufsize);
|
|
|
|
hid_find_max_report(hid, HID_OUTPUT_REPORT, &usbhid->bufsize);
|
|
|
|
hid_find_max_report(hid, HID_FEATURE_REPORT, &usbhid->bufsize);
|
2005-09-05 13:12:01 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
if (usbhid->bufsize > HID_MAX_BUFFER_SIZE)
|
|
|
|
usbhid->bufsize = HID_MAX_BUFFER_SIZE;
|
2005-09-05 13:12:01 +08:00
|
|
|
|
|
|
|
hid_find_max_report(hid, HID_INPUT_REPORT, &insize);
|
|
|
|
|
|
|
|
if (insize > HID_MAX_BUFFER_SIZE)
|
|
|
|
insize = HID_MAX_BUFFER_SIZE;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (hid_alloc_buffers(dev, hid)) {
|
|
|
|
hid_free_buffers(dev, hid);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (n = 0; n < interface->desc.bNumEndpoints; n++) {
|
|
|
|
|
|
|
|
struct usb_endpoint_descriptor *endpoint;
|
|
|
|
int pipe;
|
|
|
|
int interval;
|
|
|
|
|
|
|
|
endpoint = &interface->endpoint[n].desc;
|
|
|
|
if ((endpoint->bmAttributes & 3) != 3) /* Not an interrupt endpoint */
|
|
|
|
continue;
|
|
|
|
|
|
|
|
interval = endpoint->bInterval;
|
|
|
|
|
|
|
|
/* Change the polling interval of mice. */
|
|
|
|
if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0)
|
|
|
|
interval = hid_mousepoll_interval;
|
2005-05-29 15:29:01 +08:00
|
|
|
|
2006-10-27 00:02:51 +08:00
|
|
|
if (usb_endpoint_dir_in(endpoint)) {
|
2006-12-09 01:41:03 +08:00
|
|
|
if (usbhid->urbin)
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
2006-12-09 01:41:03 +08:00
|
|
|
if (!(usbhid->urbin = usb_alloc_urb(0, GFP_KERNEL)))
|
2005-04-17 06:20:36 +08:00
|
|
|
goto fail;
|
|
|
|
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
|
2006-12-09 01:41:03 +08:00
|
|
|
usb_fill_int_urb(usbhid->urbin, dev, pipe, usbhid->inbuf, insize,
|
2005-04-17 06:20:36 +08:00
|
|
|
hid_irq_in, hid, interval);
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid->urbin->transfer_dma = usbhid->inbuf_dma;
|
|
|
|
usbhid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
2005-04-17 06:20:36 +08:00
|
|
|
} else {
|
2006-12-09 01:41:03 +08:00
|
|
|
if (usbhid->urbout)
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
2006-12-09 01:41:03 +08:00
|
|
|
if (!(usbhid->urbout = usb_alloc_urb(0, GFP_KERNEL)))
|
2005-04-17 06:20:36 +08:00
|
|
|
goto fail;
|
|
|
|
pipe = usb_sndintpipe(dev, endpoint->bEndpointAddress);
|
2006-12-09 01:41:03 +08:00
|
|
|
usb_fill_int_urb(usbhid->urbout, dev, pipe, usbhid->outbuf, 0,
|
2005-04-17 06:20:36 +08:00
|
|
|
hid_irq_out, hid, interval);
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid->urbout->transfer_dma = usbhid->outbuf_dma;
|
|
|
|
usbhid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
if (!usbhid->urbin) {
|
2005-04-17 06:20:36 +08:00
|
|
|
err("couldn't find an input interrupt endpoint");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
init_waitqueue_head(&hid->wait);
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
INIT_WORK(&usbhid->reset_work, hid_reset);
|
|
|
|
setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
|
2006-02-01 01:58:38 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
spin_lock_init(&usbhid->inlock);
|
|
|
|
spin_lock_init(&usbhid->outlock);
|
|
|
|
spin_lock_init(&usbhid->ctrllock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
hid->version = le16_to_cpu(hdesc->bcdHID);
|
|
|
|
hid->country = hdesc->bCountryCode;
|
2006-12-09 01:41:03 +08:00
|
|
|
hid->dev = &dev->dev;
|
|
|
|
usbhid->intf = intf;
|
|
|
|
usbhid->ifnum = interface->desc.bInterfaceNumber;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
hid->name[0] = 0;
|
|
|
|
|
2005-09-15 15:01:47 +08:00
|
|
|
if (dev->manufacturer)
|
|
|
|
strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));
|
|
|
|
|
|
|
|
if (dev->product) {
|
|
|
|
if (dev->manufacturer)
|
|
|
|
strlcat(hid->name, " ", sizeof(hid->name));
|
|
|
|
strlcat(hid->name, dev->product, sizeof(hid->name));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strlen(hid->name))
|
|
|
|
snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
|
|
|
|
le16_to_cpu(dev->descriptor.idVendor),
|
|
|
|
le16_to_cpu(dev->descriptor.idProduct));
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-09 01:40:53 +08:00
|
|
|
hid->bus = BUS_USB;
|
|
|
|
hid->vendor = dev->descriptor.idVendor;
|
|
|
|
hid->product = dev->descriptor.idProduct;
|
|
|
|
|
2005-09-15 15:01:47 +08:00
|
|
|
usb_make_path(dev, hid->phys, sizeof(hid->phys));
|
|
|
|
strlcat(hid->phys, "/input", sizeof(hid->phys));
|
|
|
|
len = strlen(hid->phys);
|
|
|
|
if (len < sizeof(hid->phys) - 1)
|
|
|
|
snprintf(hid->phys + len, sizeof(hid->phys) - len,
|
|
|
|
"%d", intf->altsetting[0].desc.bInterfaceNumber);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
|
|
|
|
hid->uniq[0] = 0;
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
|
|
|
|
if (!usbhid->urbctrl)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto fail;
|
2005-09-15 15:01:47 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr,
|
|
|
|
usbhid->ctrlbuf, 1, hid_ctrl, hid);
|
|
|
|
usbhid->urbctrl->setup_dma = usbhid->cr_dma;
|
|
|
|
usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
|
|
|
|
usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
|
2006-12-09 01:40:53 +08:00
|
|
|
hid->hidinput_input_event = usb_hidinput_input_event;
|
|
|
|
hid->hidinput_open = hidinput_open;
|
|
|
|
hid->hidinput_close = hidinput_close;
|
2006-12-09 01:41:10 +08:00
|
|
|
#ifdef CONFIG_USB_HIDDEV
|
|
|
|
hid->hiddev_hid_event = hiddev_hid_event;
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return hid;
|
|
|
|
|
|
|
|
fail:
|
2006-12-09 01:41:03 +08:00
|
|
|
usb_free_urb(usbhid->urbin);
|
|
|
|
usb_free_urb(usbhid->urbout);
|
|
|
|
usb_free_urb(usbhid->urbctrl);
|
2005-04-17 06:20:36 +08:00
|
|
|
hid_free_buffers(dev, hid);
|
|
|
|
hid_free_device(hid);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hid_disconnect(struct usb_interface *intf)
|
|
|
|
{
|
|
|
|
struct hid_device *hid = usb_get_intfdata (intf);
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!hid)
|
|
|
|
return;
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid = hid->driver_data;
|
|
|
|
|
|
|
|
spin_lock_irq(&usbhid->inlock); /* Sync with error handler */
|
2005-04-17 06:20:36 +08:00
|
|
|
usb_set_intfdata(intf, NULL);
|
2006-12-09 01:41:03 +08:00
|
|
|
spin_unlock_irq(&usbhid->inlock);
|
|
|
|
usb_kill_urb(usbhid->urbin);
|
|
|
|
usb_kill_urb(usbhid->urbout);
|
|
|
|
usb_kill_urb(usbhid->urbctrl);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
del_timer_sync(&usbhid->io_retry);
|
2006-02-01 01:58:38 +08:00
|
|
|
flush_scheduled_work();
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (hid->claimed & HID_CLAIMED_INPUT)
|
|
|
|
hidinput_disconnect(hid);
|
|
|
|
if (hid->claimed & HID_CLAIMED_HIDDEV)
|
|
|
|
hiddev_disconnect(hid);
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
usb_free_urb(usbhid->urbin);
|
|
|
|
usb_free_urb(usbhid->urbctrl);
|
|
|
|
usb_free_urb(usbhid->urbout);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
hid_free_buffers(to_usb_device(hid->dev), hid);
|
2005-04-17 06:20:36 +08:00
|
|
|
hid_free_device(hid);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
|
|
|
{
|
|
|
|
struct hid_device *hid;
|
|
|
|
char path[64];
|
|
|
|
int i;
|
|
|
|
char *c;
|
|
|
|
|
|
|
|
dbg("HID probe called for ifnum %d",
|
|
|
|
intf->altsetting->desc.bInterfaceNumber);
|
|
|
|
|
|
|
|
if (!(hid = usb_hid_configure(intf)))
|
2005-06-22 23:53:28 +08:00
|
|
|
return -ENODEV;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid_init_reports(hid);
|
2005-04-17 06:20:36 +08:00
|
|
|
hid_dump_device(hid);
|
|
|
|
|
|
|
|
if (!hidinput_connect(hid))
|
|
|
|
hid->claimed |= HID_CLAIMED_INPUT;
|
|
|
|
if (!hiddev_connect(hid))
|
|
|
|
hid->claimed |= HID_CLAIMED_HIDDEV;
|
|
|
|
|
|
|
|
usb_set_intfdata(intf, hid);
|
|
|
|
|
|
|
|
if (!hid->claimed) {
|
|
|
|
printk ("HID device not claimed by input or hiddev\n");
|
|
|
|
hid_disconnect(intf);
|
2005-06-22 23:53:28 +08:00
|
|
|
return -ENODEV;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
/* This only gets called when we are a single-input (most of the
|
|
|
|
* time). IOW, not a HID_QUIRK_MULTI_INPUT. The hid_ff_init() is
|
|
|
|
* only useful in this case, and not for multi-input quirks. */
|
|
|
|
if ((hid->claimed & HID_CLAIMED_INPUT) &&
|
|
|
|
!(hid->quirks & HID_QUIRK_MULTI_INPUT))
|
|
|
|
hid_ff_init(hid);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
printk(KERN_INFO);
|
|
|
|
|
|
|
|
if (hid->claimed & HID_CLAIMED_INPUT)
|
|
|
|
printk("input");
|
|
|
|
if (hid->claimed == (HID_CLAIMED_INPUT | HID_CLAIMED_HIDDEV))
|
|
|
|
printk(",");
|
|
|
|
if (hid->claimed & HID_CLAIMED_HIDDEV)
|
|
|
|
printk("hiddev%d", hid->minor);
|
|
|
|
|
|
|
|
c = "Device";
|
|
|
|
for (i = 0; i < hid->maxcollection; i++) {
|
|
|
|
if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
|
|
|
|
(hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&
|
|
|
|
(hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) {
|
|
|
|
c = hid_types[hid->collection[i].usage & 0xffff];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
usb_make_path(interface_to_usbdev(intf), path, 63);
|
|
|
|
|
|
|
|
printk(": USB HID v%x.%02x %s [%s] on %s\n",
|
|
|
|
hid->version >> 8, hid->version & 0xff, c, hid->name, path);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-19 08:39:22 +08:00
|
|
|
static int hid_suspend(struct usb_interface *intf, pm_message_t message)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct hid_device *hid = usb_get_intfdata (intf);
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
spin_lock_irq(&usbhid->inlock); /* Sync with error handler */
|
|
|
|
set_bit(HID_SUSPENDED, &usbhid->iofl);
|
|
|
|
spin_unlock_irq(&usbhid->inlock);
|
|
|
|
del_timer(&usbhid->io_retry);
|
|
|
|
usb_kill_urb(usbhid->urbin);
|
2005-04-17 06:20:36 +08:00
|
|
|
dev_dbg(&intf->dev, "suspend\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hid_resume(struct usb_interface *intf)
|
|
|
|
{
|
|
|
|
struct hid_device *hid = usb_get_intfdata (intf);
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
int status;
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
clear_bit(HID_SUSPENDED, &usbhid->iofl);
|
|
|
|
usbhid->retry_delay = 0;
|
2006-02-01 01:58:38 +08:00
|
|
|
status = hid_start_in(hid);
|
2005-04-17 06:20:36 +08:00
|
|
|
dev_dbg(&intf->dev, "resume status %d\n", status);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2006-06-02 01:55:28 +08:00
|
|
|
/* Treat USB reset pretty much the same as suspend/resume */
|
|
|
|
static void hid_pre_reset(struct usb_interface *intf)
|
|
|
|
{
|
|
|
|
/* FIXME: What if the interface is already suspended? */
|
|
|
|
hid_suspend(intf, PMSG_ON);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hid_post_reset(struct usb_interface *intf)
|
|
|
|
{
|
|
|
|
struct usb_device *dev = interface_to_usbdev (intf);
|
|
|
|
|
|
|
|
hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0);
|
|
|
|
/* FIXME: Any more reinitialization needed? */
|
|
|
|
|
|
|
|
hid_resume(intf);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static struct usb_device_id hid_usb_ids [] = {
|
|
|
|
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
|
|
|
|
.bInterfaceClass = USB_INTERFACE_CLASS_HID },
|
|
|
|
{ } /* Terminating entry */
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE (usb, hid_usb_ids);
|
|
|
|
|
|
|
|
static struct usb_driver hid_driver = {
|
|
|
|
.name = "usbhid",
|
|
|
|
.probe = hid_probe,
|
|
|
|
.disconnect = hid_disconnect,
|
|
|
|
.suspend = hid_suspend,
|
|
|
|
.resume = hid_resume,
|
2006-06-02 01:55:28 +08:00
|
|
|
.pre_reset = hid_pre_reset,
|
|
|
|
.post_reset = hid_post_reset,
|
2005-04-17 06:20:36 +08:00
|
|
|
.id_table = hid_usb_ids,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init hid_init(void)
|
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
retval = hiddev_init();
|
|
|
|
if (retval)
|
|
|
|
goto hiddev_init_fail;
|
|
|
|
retval = usb_register(&hid_driver);
|
|
|
|
if (retval)
|
|
|
|
goto usb_register_fail;
|
|
|
|
info(DRIVER_VERSION ":" DRIVER_DESC);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
usb_register_fail:
|
|
|
|
hiddev_exit();
|
|
|
|
hiddev_init_fail:
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit hid_exit(void)
|
|
|
|
{
|
|
|
|
usb_deregister(&hid_driver);
|
|
|
|
hiddev_exit();
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(hid_init);
|
|
|
|
module_exit(hid_exit);
|
|
|
|
|
|
|
|
MODULE_AUTHOR(DRIVER_AUTHOR);
|
|
|
|
MODULE_DESCRIPTION(DRIVER_DESC);
|
|
|
|
MODULE_LICENSE(DRIVER_LICENSE);
|