From 6db3dfefa28739e7c9c60809c3a5aef7cc088b97 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Thu, 8 Mar 2007 16:47:49 +0100 Subject: USB HID: move usbhid code from drivers/usb/input to drivers/hid/usbhid Separate usbhid code into dedicated drivers/hid/usbhid directory as discussed previously with Greg, so that it eases maintaineance process. Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/Kconfig | 2 + drivers/hid/Makefile | 4 + drivers/hid/usbhid/Kconfig | 149 ++++ drivers/hid/usbhid/Makefile | 35 + drivers/hid/usbhid/hid-core.c | 1477 ++++++++++++++++++++++++++++++++++++++++ drivers/hid/usbhid/hid-ff.c | 89 +++ drivers/hid/usbhid/hid-lgff.c | 150 ++++ drivers/hid/usbhid/hid-pidff.c | 1331 ++++++++++++++++++++++++++++++++++++ drivers/hid/usbhid/hid-plff.c | 129 ++++ drivers/hid/usbhid/hid-tmff.c | 147 ++++ drivers/hid/usbhid/hid-zpff.c | 111 +++ drivers/hid/usbhid/hiddev.c | 847 +++++++++++++++++++++++ drivers/hid/usbhid/usbhid.h | 87 +++ drivers/hid/usbhid/usbkbd.c | 362 ++++++++++ drivers/hid/usbhid/usbmouse.c | 245 +++++++ drivers/usb/Makefile | 3 - drivers/usb/input/Kconfig | 145 ---- drivers/usb/input/Makefile | 28 - drivers/usb/input/hid-core.c | 1477 ---------------------------------------- drivers/usb/input/hid-ff.c | 89 --- drivers/usb/input/hid-lgff.c | 150 ---- drivers/usb/input/hid-pidff.c | 1331 ------------------------------------ drivers/usb/input/hid-plff.c | 129 ---- drivers/usb/input/hid-tmff.c | 147 ---- drivers/usb/input/hid-zpff.c | 111 --- drivers/usb/input/hiddev.c | 847 ----------------------- drivers/usb/input/usbhid.h | 87 --- drivers/usb/input/usbkbd.c | 362 ---------- drivers/usb/input/usbmouse.c | 245 ------- 29 files changed, 5165 insertions(+), 5151 deletions(-) create mode 100644 drivers/hid/usbhid/Kconfig create mode 100644 drivers/hid/usbhid/Makefile create mode 100644 drivers/hid/usbhid/hid-core.c create mode 100644 drivers/hid/usbhid/hid-ff.c create mode 100644 drivers/hid/usbhid/hid-lgff.c create mode 100644 drivers/hid/usbhid/hid-pidff.c create mode 100644 drivers/hid/usbhid/hid-plff.c create mode 100644 drivers/hid/usbhid/hid-tmff.c create mode 100644 drivers/hid/usbhid/hid-zpff.c create mode 100644 drivers/hid/usbhid/hiddev.c create mode 100644 drivers/hid/usbhid/usbhid.h create mode 100644 drivers/hid/usbhid/usbkbd.c create mode 100644 drivers/hid/usbhid/usbmouse.c delete mode 100644 drivers/usb/input/hid-core.c delete mode 100644 drivers/usb/input/hid-ff.c delete mode 100644 drivers/usb/input/hid-lgff.c delete mode 100644 drivers/usb/input/hid-pidff.c delete mode 100644 drivers/usb/input/hid-plff.c delete mode 100644 drivers/usb/input/hid-tmff.c delete mode 100644 drivers/usb/input/hid-zpff.c delete mode 100644 drivers/usb/input/hiddev.c delete mode 100644 drivers/usb/input/usbhid.h delete mode 100644 drivers/usb/input/usbkbd.c delete mode 100644 drivers/usb/input/usbmouse.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 850788f4dd2e..8fbe9fdac128 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -36,5 +36,7 @@ config HID_DEBUG If unsure, say N +source "drivers/hid/usbhid/Kconfig" + endmenu diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 52e97d8f3c95..68d1376a53fb 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -6,3 +6,7 @@ hid-objs := hid-core.o hid-input.o obj-$(CONFIG_HID) += hid.o hid-$(CONFIG_HID_DEBUG) += hid-debug.o +obj-$(CONFIG_USB_HID) += usbhid/ +obj-$(CONFIG_USB_MOUSE) += usbhid/ +obj-$(CONFIG_USB_KBD) += usbhid/ + diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig new file mode 100644 index 000000000000..7c87bdc538bc --- /dev/null +++ b/drivers/hid/usbhid/Kconfig @@ -0,0 +1,149 @@ +comment "USB Input Devices" + depends on USB + +config USB_HID + tristate "USB Human Interface Device (full HID) support" + default y + depends on USB && INPUT + select HID + ---help--- + Say Y here if you want full HID support to connect USB keyboards, + mice, joysticks, graphic tablets, or any other HID based devices + to your computer via USB, as well as Uninterruptible Power Supply + (UPS) and monitor control devices. + + You can't use this driver and the HIDBP (Boot Protocol) keyboard + and mouse drivers at the same time. More information is available: + . + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called usbhid. + +comment "Input core support is needed for USB HID input layer or HIDBP support" + depends on USB_HID && INPUT=n + +config USB_HIDINPUT_POWERBOOK + bool "Enable support for iBook/PowerBook special keys" + default n + depends on USB_HID + help + Say Y here if you want support for the special keys (Fn, Numlock) on + Apple iBooks and PowerBooks. + + If unsure, say N. + +config HID_FF + bool "Force feedback support (EXPERIMENTAL)" + depends on USB_HID && EXPERIMENTAL + help + Say Y here is you want force feedback support for a few HID devices. + See below for a list of supported devices. + + See for a description of the force + feedback API. + + If unsure, say N. + +config HID_PID + bool "PID device support" + depends on HID_FF + help + Say Y here if you have a PID-compliant device and wish to enable force + feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such + devices. + +config LOGITECH_FF + bool "Logitech devices support" + depends on HID_FF + select INPUT_FF_MEMLESS if USB_HID + help + Say Y here if you have one of these devices: + - Logitech WingMan Cordless RumblePad + - Logitech WingMan Cordless RumblePad 2 + - Logitech WingMan Force 3D + - Logitech Formula Force EX + - Logitech MOMO Force wheel + + and if you want to enable force feedback for them. + Note: if you say N here, this device will still be supported, but without + force feedback. + +config PANTHERLORD_FF + bool "PantherLord USB/PS2 2in1 Adapter support" + depends on HID_FF + select INPUT_FF_MEMLESS if USB_HID + help + Say Y here if you have a PantherLord USB/PS2 2in1 Adapter and want + to enable force feedback support for it. + +config THRUSTMASTER_FF + bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)" + depends on HID_FF && EXPERIMENTAL + select INPUT_FF_MEMLESS if USB_HID + help + Say Y here if you have a THRUSTMASTER FireStore Dual Power 2, + and want to enable force feedback support for it. + Note: if you say N here, this device will still be supported, but without + force feedback. + +config ZEROPLUS_FF + bool "Zeroplus based game controller support" + depends on HID_FF + select INPUT_FF_MEMLESS if USB_HID + help + Say Y here if you have a Zeroplus based game controller and want to + enable force feedback for it. + +config USB_HIDDEV + bool "/dev/hiddev raw HID device support" + depends on USB_HID + help + Say Y here if you want to support HID devices (from the USB + specification standpoint) that aren't strictly user interface + devices, like monitor controls and Uninterruptable Power Supplies. + + This module supports these devices separately using a separate + event interface on /dev/usb/hiddevX (char 180:96 to 180:111). + + If unsure, say Y. + +menu "USB HID Boot Protocol drivers" + depends on USB!=n && USB_HID!=y + +config USB_KBD + tristate "USB HIDBP Keyboard (simple Boot) support" + depends on USB && INPUT + ---help--- + Say Y here only if you are absolutely sure that you don't want + to use the generic HID driver for your USB keyboard and prefer + to use the keyboard in its limited Boot Protocol mode instead. + + This is almost certainly not what you want. This is mostly + useful for embedded applications or simple keyboards. + + To compile this driver as a module, choose M here: the + module will be called usbkbd. + + If even remotely unsure, say N. + +config USB_MOUSE + tristate "USB HIDBP Mouse (simple Boot) support" + depends on USB && INPUT + ---help--- + Say Y here only if you are absolutely sure that you don't want + to use the generic HID driver for your USB mouse and prefer + to use the mouse in its limited Boot Protocol mode instead. + + This is almost certainly not what you want. This is mostly + useful for embedded applications or simple mice. + + To compile this driver as a module, choose M here: the + module will be called usbmouse. + + If even remotely unsure, say N. + +endmenu + + diff --git a/drivers/hid/usbhid/Makefile b/drivers/hid/usbhid/Makefile new file mode 100644 index 000000000000..de9bc1fe3eaa --- /dev/null +++ b/drivers/hid/usbhid/Makefile @@ -0,0 +1,35 @@ +# +# Makefile for the USB input drivers +# + +# Multipart objects. +usbhid-objs := hid-core.o + +# Optional parts of multipart objects. + +ifeq ($(CONFIG_USB_HIDDEV),y) + usbhid-objs += hiddev.o +endif +ifeq ($(CONFIG_HID_PID),y) + usbhid-objs += hid-pidff.o +endif +ifeq ($(CONFIG_LOGITECH_FF),y) + usbhid-objs += hid-lgff.o +endif +ifeq ($(CONFIG_PANTHERLORD_FF),y) + usbhid-objs += hid-plff.o +endif +ifeq ($(CONFIG_THRUSTMASTER_FF),y) + usbhid-objs += hid-tmff.o +endif +ifeq ($(CONFIG_ZEROPLUS_FF),y) + usbhid-objs += hid-zpff.o +endif +ifeq ($(CONFIG_HID_FF),y) + usbhid-objs += hid-ff.o +endif + +obj-$(CONFIG_USB_HID) += usbhid.o +obj-$(CONFIG_USB_KBD) += usbkbd.o +obj-$(CONFIG_USB_MOUSE) += usbmouse.o + diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c new file mode 100644 index 000000000000..827a75a186ba --- /dev/null +++ b/drivers/hid/usbhid/hid-core.c @@ -0,0 +1,1477 @@ +/* + * USB HID support for Linux + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include "usbhid.h" + +/* + * Version Information + */ + +#define DRIVER_VERSION "v2.6" +#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"); + +/* + * 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; + struct usbhid_device *usbhid = hid->driver_data; + + 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); + if (rc != 0) + clear_bit(HID_IN_RUNNING, &usbhid->iofl); + } + spin_unlock_irqrestore(&usbhid->inlock, flags); + return rc; +} + +/* I/O retry timer routine */ +static void hid_retry_timeout(unsigned long _hid) +{ + struct hid_device *hid = (struct hid_device *) _hid; + struct usbhid_device *usbhid = hid->driver_data; + + dev_dbg(&usbhid->intf->dev, "retrying intr urb\n"); + if (hid_start_in(hid)) + hid_io_error(hid); +} + +/* Workqueue routine to reset the device or clear a halt */ +static void hid_reset(struct work_struct *work) +{ + struct usbhid_device *usbhid = + container_of(work, struct usbhid_device, reset_work); + struct hid_device *hid = usbhid->hid; + int rc_lock, rc = 0; + + if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) { + dev_dbg(&usbhid->intf->dev, "clear halt\n"); + rc = usb_clear_halt(hid_to_usb_dev(hid), usbhid->urbin->pipe); + clear_bit(HID_CLEAR_HALT, &usbhid->iofl); + hid_start_in(hid); + } + + 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(hid_to_usb_dev(hid), usbhid->intf); + if (rc_lock >= 0) { + rc = usb_reset_composite_device(hid_to_usb_dev(hid), usbhid->intf); + if (rc_lock) + usb_unlock_device(hid_to_usb_dev(hid)); + } + clear_bit(HID_RESET_PENDING, &usbhid->iofl); + } + + switch (rc) { + case 0: + if (!test_bit(HID_IN_RUNNING, &usbhid->iofl)) + hid_io_error(hid); + break; + default: + err("can't reset device, %s-%s/input%d, status %d", + hid_to_usb_dev(hid)->bus->bus_name, + hid_to_usb_dev(hid)->devpath, + usbhid->ifnum, rc); + /* FALLTHROUGH */ + case -EHOSTUNREACH: + case -ENODEV: + case -EINTR: + break; + } +} + +/* Main I/O error handler */ +static void hid_io_error(struct hid_device *hid) +{ + unsigned long flags; + struct usbhid_device *usbhid = hid->driver_data; + + spin_lock_irqsave(&usbhid->inlock, flags); + + /* Stop when disconnected */ + if (usb_get_intfdata(usbhid->intf) == NULL) + goto done; + + /* When an error occurs, retry at increasing intervals */ + 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; + + if (time_after(jiffies, usbhid->stop_retry)) { + + /* Retries failed, so do a port reset */ + if (!test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) { + schedule_work(&usbhid->reset_work); + goto done; + } + } + + mod_timer(&usbhid->io_retry, + jiffies + msecs_to_jiffies(usbhid->retry_delay)); +done: + spin_unlock_irqrestore(&usbhid->inlock, flags); +} + +/* + * Input interrupt completion handler. + */ + +static void hid_irq_in(struct urb *urb) +{ + struct hid_device *hid = urb->context; + struct usbhid_device *usbhid = hid->driver_data; + int status; + + switch (urb->status) { + case 0: /* success */ + usbhid->retry_delay = 0; + hid_input_report(urb->context, HID_INPUT_REPORT, + urb->transfer_buffer, + urb->actual_length, 1); + break; + case -EPIPE: /* stall */ + clear_bit(HID_IN_RUNNING, &usbhid->iofl); + set_bit(HID_CLEAR_HALT, &usbhid->iofl); + schedule_work(&usbhid->reset_work); + return; + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: /* unplug */ + clear_bit(HID_IN_RUNNING, &usbhid->iofl); + return; + case -EILSEQ: /* protocol error or unplug */ + case -EPROTO: /* protocol error or unplug */ + case -ETIME: /* protocol error or unplug */ + case -ETIMEDOUT: /* Should never happen, but... */ + clear_bit(HID_IN_RUNNING, &usbhid->iofl); + hid_io_error(hid); + return; + default: /* error */ + warn("input irq status %d received", urb->status); + } + + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) { + clear_bit(HID_IN_RUNNING, &usbhid->iofl); + if (status != -EPERM) { + err("can't resubmit intr, %s-%s/input%d, status %d", + hid_to_usb_dev(hid)->bus->bus_name, + hid_to_usb_dev(hid)->devpath, + usbhid->ifnum, status); + hid_io_error(hid); + } + } +} + +static int hid_submit_out(struct hid_device *hid) +{ + struct hid_report *report; + struct usbhid_device *usbhid = hid->driver_data; + + report = usbhid->out[usbhid->outtail]; + + hid_output_report(report, usbhid->outbuf); + usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0); + usbhid->urbout->dev = hid_to_usb_dev(hid); + + dbg("submitting out urb"); + + if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) { + 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; + struct usbhid_device *usbhid = hid->driver_data; + + report = usbhid->ctrl[usbhid->ctrltail].report; + dir = usbhid->ctrl[usbhid->ctrltail].dir; + + len = ((report->size - 1) >> 3) + 1 + (report->id > 0); + if (dir == USB_DIR_OUT) { + hid_output_report(report, usbhid->ctrlbuf); + usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0); + usbhid->urbctrl->transfer_buffer_length = len; + } else { + int maxpacket, padlen; + + usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0); + maxpacket = usb_maxpacket(hid_to_usb_dev(hid), usbhid->urbctrl->pipe, 0); + if (maxpacket > 0) { + padlen = (len + maxpacket - 1) / maxpacket; + padlen *= maxpacket; + if (padlen > usbhid->bufsize) + padlen = usbhid->bufsize; + } else + padlen = 0; + usbhid->urbctrl->transfer_buffer_length = padlen; + } + usbhid->urbctrl->dev = hid_to_usb_dev(hid); + + 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); + + dbg("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u", + usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report", + usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength); + + if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) { + err("usb_submit_urb(ctrl) failed"); + return -1; + } + + return 0; +} + +/* + * Output interrupt completion handler. + */ + +static void hid_irq_out(struct urb *urb) +{ + struct hid_device *hid = urb->context; + struct usbhid_device *usbhid = hid->driver_data; + unsigned long flags; + int unplug = 0; + + switch (urb->status) { + case 0: /* success */ + break; + case -ESHUTDOWN: /* unplug */ + unplug = 1; + case -EILSEQ: /* protocol error or unplug */ + case -EPROTO: /* protocol error or unplug */ + case -ECONNRESET: /* unlink */ + case -ENOENT: + break; + default: /* error */ + warn("output irq status %d received", urb->status); + } + + spin_lock_irqsave(&usbhid->outlock, flags); + + if (unplug) + usbhid->outtail = usbhid->outhead; + else + usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1); + + if (usbhid->outhead != usbhid->outtail) { + if (hid_submit_out(hid)) { + clear_bit(HID_OUT_RUNNING, &usbhid->iofl); + wake_up(&hid->wait); + } + spin_unlock_irqrestore(&usbhid->outlock, flags); + return; + } + + clear_bit(HID_OUT_RUNNING, &usbhid->iofl); + spin_unlock_irqrestore(&usbhid->outlock, flags); + wake_up(&hid->wait); +} + +/* + * Control pipe completion handler. + */ + +static void hid_ctrl(struct urb *urb) +{ + struct hid_device *hid = urb->context; + struct usbhid_device *usbhid = hid->driver_data; + unsigned long flags; + int unplug = 0; + + spin_lock_irqsave(&usbhid->ctrllock, flags); + + switch (urb->status) { + case 0: /* success */ + if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN) + hid_input_report(urb->context, usbhid->ctrl[usbhid->ctrltail].report->type, + urb->transfer_buffer, urb->actual_length, 0); + break; + case -ESHUTDOWN: /* unplug */ + unplug = 1; + case -EILSEQ: /* protocol error or unplug */ + case -EPROTO: /* protocol error or unplug */ + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -EPIPE: /* report not available */ + break; + default: /* error */ + warn("ctrl urb status %d received", urb->status); + } + + if (unplug) + usbhid->ctrltail = usbhid->ctrlhead; + else + usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1); + + if (usbhid->ctrlhead != usbhid->ctrltail) { + if (hid_submit_ctrl(hid)) { + clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); + wake_up(&hid->wait); + } + spin_unlock_irqrestore(&usbhid->ctrllock, flags); + return; + } + + clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); + spin_unlock_irqrestore(&usbhid->ctrllock, flags); + wake_up(&hid->wait); +} + +void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) +{ + int head; + unsigned long flags; + struct usbhid_device *usbhid = hid->driver_data; + + if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN) + return; + + if (usbhid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) { + + spin_lock_irqsave(&usbhid->outlock, flags); + + if ((head = (usbhid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == usbhid->outtail) { + spin_unlock_irqrestore(&usbhid->outlock, flags); + warn("output queue full"); + return; + } + + usbhid->out[usbhid->outhead] = report; + usbhid->outhead = head; + + if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) + if (hid_submit_out(hid)) + clear_bit(HID_OUT_RUNNING, &usbhid->iofl); + + spin_unlock_irqrestore(&usbhid->outlock, flags); + return; + } + + spin_lock_irqsave(&usbhid->ctrllock, flags); + + if ((head = (usbhid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == usbhid->ctrltail) { + spin_unlock_irqrestore(&usbhid->ctrllock, flags); + warn("control queue full"); + return; + } + + usbhid->ctrl[usbhid->ctrlhead].report = report; + usbhid->ctrl[usbhid->ctrlhead].dir = dir; + usbhid->ctrlhead = head; + + if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) + if (hid_submit_ctrl(hid)) + clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); + + spin_unlock_irqrestore(&usbhid->ctrllock, flags); +} + +static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + 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); + usbhid_submit_report(hid, field->report, USB_DIR_OUT); + + return 0; +} + +int usbhid_wait_io(struct hid_device *hid) +{ + 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)), + 10*HZ)) { + dbg("timeout waiting for ctrl or out queue to clear"); + return -1; + } + + return 0; +} + +static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, (idle << 8) | report, + ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT); +} + +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); + + 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; +} + +int usbhid_open(struct hid_device *hid) +{ + ++hid->open; + if (hid_start_in(hid)) + hid_io_error(hid); + return 0; +} + +void usbhid_close(struct hid_device *hid) +{ + struct usbhid_device *usbhid = hid->driver_data; + + if (!--hid->open) + usb_kill_urb(usbhid->urbin); +} + +#define USB_VENDOR_ID_PANJIT 0x134c + +#define USB_VENDOR_ID_TURBOX 0x062a +#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201 +#define USB_VENDOR_ID_CIDC 0x1677 + +/* + * Initialize all reports + */ + +void usbhid_init_reports(struct hid_device *hid) +{ + struct hid_report *report; + struct usbhid_device *usbhid = hid->driver_data; + int err, ret; + + list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) + usbhid_submit_report(hid, report, USB_DIR_IN); + + list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list) + usbhid_submit_report(hid, report, USB_DIR_IN); + + err = 0; + ret = usbhid_wait_io(hid); + while (ret) { + err |= ret; + 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); + } + + if (err) + warn("timeout initializing reports"); +} + +#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 +#define USB_DEVICE_ID_GTCO_405 0x0405 +#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 + +#define USB_VENDOR_ID_WACOM 0x056a + +#define USB_VENDOR_ID_ACECAD 0x0460 +#define USB_DEVICE_ID_ACECAD_FLAIR 0x0004 +#define USB_DEVICE_ID_ACECAD_302 0x0008 + +#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 + +#define USB_VENDOR_ID_AASHIMA 0x06d6 +#define USB_DEVICE_ID_AASHIMA_GAMEPAD 0x0025 +#define USB_DEVICE_ID_AASHIMA_PREDATOR 0x0026 + +#define USB_VENDOR_ID_CYPRESS 0x04b4 +#define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001 +#define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500 +#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417 + +#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 +#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 +#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053 +#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058 + +#define USB_VENDOR_ID_WISEGROUP 0x0925 +#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101 +#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104 +#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201 +#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866 + +#define USB_VENDOR_ID_WISEGROUP_LTD 0x6677 +#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802 + +#define USB_VENDOR_ID_CODEMERCS 0x07c0 +#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500 +#define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff + +#define USB_VENDOR_ID_DELORME 0x1163 +#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100 +#define USB_DEVICE_ID_DELORME_EM_LT20 0x0200 + +#define USB_VENDOR_ID_MCC 0x09db +#define USB_DEVICE_ID_MCC_PMD1024LS 0x0076 +#define USB_DEVICE_ID_MCC_PMD1208LS 0x007a + +#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 + +#define USB_VENDOR_ID_LD 0x0f11 +#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 + +#define USB_VENDOR_ID_APPLE 0x05ac +#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304 +#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 +#define USB_DEVICE_ID_APPLE_IR 0x8240 + +#define USB_VENDOR_ID_CHERRY 0x046a +#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023 + +#define USB_VENDOR_ID_YEALINK 0x6993 +#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001 + +#define USB_VENDOR_ID_ALCOR 0x058f +#define USB_DEVICE_ID_ALCOR_USBRS232 0x9720 + +#define USB_VENDOR_ID_SUN 0x0430 +#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab + +#define USB_VENDOR_ID_AIRCABLE 0x16CA +#define USB_DEVICE_ID_AIRCABLE1 0x1502 + +#define USB_VENDOR_ID_LOGITECH 0x046d +#define USB_DEVICE_ID_LOGITECH_USB_RECEIVER 0xc101 +#define USB_DEVICE_ID_LOGITECH_USB_RECEIVER_2 0xc517 +#define USB_DEVICE_ID_DINOVO_EDGE 0xc714 + +#define USB_VENDOR_ID_IMATION 0x0718 +#define USB_DEVICE_ID_DISC_STAKKA 0xd000 + +#define USB_VENDOR_ID_PANTHERLORD 0x0810 +#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK 0x0001 + +#define USB_VENDOR_ID_SONY 0x054c +#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 + +/* + * Alphabetically sorted blacklist by quirk type. + */ + +static const struct hid_blacklist { + __u16 idVendor; + __u16 idProduct; + unsigned quirks; +} hid_blacklist[] = { + + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES }, + + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20, HID_QUIRK_IGNORE }, + { 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 }, + { USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20, HID_QUIRK_IGNORE }, + { 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 }, + { 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 }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE }, + { 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 }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405, HID_QUIRK_IGNORE }, + { 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 }, + { USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY, HID_QUIRK_IGNORE }, + { 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 }, + { 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 }, + { 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 }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE }, + + { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE }, + + { 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 }, + { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, + + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL }, + { 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 }, + + { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD }, + { 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 }, + + { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION }, + + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IR, HID_QUIRK_IGNORE }, + + { 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 }, + + { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, + + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_USB_RECEIVER, HID_QUIRK_BAD_RELATIVE_KEYS }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_USB_RECEIVER_2, HID_QUIRK_LOGITECH_S510_DESCRIPTOR }, + + { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, + + { USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER }, + + { USB_VENDOR_ID_CIDC, 0x0103, HID_QUIRK_IGNORE }, + + { 0, 0 } +}; + +/* + * 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; + } +} + +static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid) +{ + struct usbhid_device *usbhid = hid->driver_data; + + if (!(usbhid->inbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->inbuf_dma))) + return -1; + if (!(usbhid->outbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->outbuf_dma))) + return -1; + if (!(usbhid->cr = usb_buffer_alloc(dev, sizeof(*(usbhid->cr)), GFP_ATOMIC, &usbhid->cr_dma))) + return -1; + if (!(usbhid->ctrlbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->ctrlbuf_dma))) + return -1; + + return 0; +} + +static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) +{ + 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); +} + +/* + * 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; + } +} + +/* + * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller + * to "operational". Without this, the ps3 controller will not report any + * events. + */ +static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum) +{ + int result; + char *buf = kmalloc(18, GFP_KERNEL); + + if (!buf) + return; + + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + HID_REQ_GET_REPORT, + USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, + (3 << 8) | 0xf2, ifnum, buf, 17, + USB_CTRL_GET_TIMEOUT); + + if (result < 0) + err("%s failed: %d\n", __func__, result); + + kfree(buf); +} + +/* + * Logitech S510 keyboard sends in report #3 keys which are far + * above the logical maximum described in descriptor. This extends + * the original value of 0x28c of logical maximum to 0x104d + */ +static void hid_fixup_s510_descriptor(unsigned char *rdesc, int rsize) +{ + if (rsize >= 90 && rdesc[83] == 0x26 + && rdesc[84] == 0x8c + && rdesc[85] == 0x02) { + info("Fixing up Logitech S510 report descriptor"); + rdesc[84] = rdesc[89] = 0x4d; + rdesc[85] = rdesc[90] = 0x10; + } +} + +static struct hid_device *usb_hid_configure(struct usb_interface *intf) +{ + struct usb_host_interface *interface = intf->cur_altsetting; + struct usb_device *dev = interface_to_usbdev (intf); + struct hid_descriptor *hdesc; + struct hid_device *hid; + unsigned quirks = 0, rsize = 0; + char *rdesc; + int n, len, insize = 0; + struct usbhid_device *usbhid; + + /* Ignore all Wacom devices */ + if (le16_to_cpu(dev->descriptor.idVendor) == USB_VENDOR_ID_WACOM) + return NULL; + /* ignore all Code Mercenaries IOWarrior devices */ + if (le16_to_cpu(dev->descriptor.idVendor) == USB_VENDOR_ID_CODEMERCS) + if (le16_to_cpu(dev->descriptor.idProduct) >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST && + le16_to_cpu(dev->descriptor.idProduct) <= USB_DEVICE_ID_CODEMERCS_IOW_LAST) + return NULL; + + for (n = 0; hid_blacklist[n].idVendor; n++) + if ((hid_blacklist[n].idVendor == le16_to_cpu(dev->descriptor.idVendor)) && + (hid_blacklist[n].idProduct == le16_to_cpu(dev->descriptor.idProduct))) + quirks = hid_blacklist[n].quirks; + + /* 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; + } + + if (quirks & HID_QUIRK_IGNORE) + return NULL; + + if ((quirks & HID_QUIRK_IGNORE_MOUSE) && + (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)) + return NULL; + + + 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; + } + + 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; + } + + hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0); + + 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; + } + + if ((quirks & HID_QUIRK_CYMOTION)) + hid_fixup_cymotion_descriptor(rdesc, rsize); + + if (quirks & HID_QUIRK_LOGITECH_S510_DESCRIPTOR) + hid_fixup_s510_descriptor(rdesc, rsize); + +#ifdef CONFIG_HID_DEBUG + printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n); + for (n = 0; n < rsize; n++) + printk(" %02x", (unsigned char) rdesc[n]); + printk("\n"); +#endif + + if (!(hid = hid_parse_report(rdesc, n))) { + dbg("parsing report descriptor failed"); + kfree(rdesc); + return NULL; + } + + kfree(rdesc); + hid->quirks = quirks; + + 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); + + if (usbhid->bufsize > HID_MAX_BUFFER_SIZE) + usbhid->bufsize = HID_MAX_BUFFER_SIZE; + + hid_find_max_report(hid, HID_INPUT_REPORT, &insize); + + if (insize > HID_MAX_BUFFER_SIZE) + insize = HID_MAX_BUFFER_SIZE; + + 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; + + if (usb_endpoint_dir_in(endpoint)) { + if (usbhid->urbin) + continue; + if (!(usbhid->urbin = usb_alloc_urb(0, GFP_KERNEL))) + goto fail; + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + usb_fill_int_urb(usbhid->urbin, dev, pipe, usbhid->inbuf, insize, + hid_irq_in, hid, interval); + usbhid->urbin->transfer_dma = usbhid->inbuf_dma; + usbhid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + } else { + if (usbhid->urbout) + continue; + if (!(usbhid->urbout = usb_alloc_urb(0, GFP_KERNEL))) + goto fail; + pipe = usb_sndintpipe(dev, endpoint->bEndpointAddress); + usb_fill_int_urb(usbhid->urbout, dev, pipe, usbhid->outbuf, 0, + hid_irq_out, hid, interval); + usbhid->urbout->transfer_dma = usbhid->outbuf_dma; + usbhid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + } + } + + if (!usbhid->urbin) { + err("couldn't find an input interrupt endpoint"); + goto fail; + } + + init_waitqueue_head(&hid->wait); + + INIT_WORK(&usbhid->reset_work, hid_reset); + setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid); + + spin_lock_init(&usbhid->inlock); + spin_lock_init(&usbhid->outlock); + spin_lock_init(&usbhid->ctrllock); + + hid->version = le16_to_cpu(hdesc->bcdHID); + hid->country = hdesc->bCountryCode; + hid->dev = &intf->dev; + usbhid->intf = intf; + usbhid->ifnum = interface->desc.bInterfaceNumber; + + hid->name[0] = 0; + + 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)); + + hid->bus = BUS_USB; + hid->vendor = le16_to_cpu(dev->descriptor.idVendor); + hid->product = le16_to_cpu(dev->descriptor.idProduct); + + usb_make_path(dev, hid->phys, sizeof(hid->phys)); + strlcat(hid->phys, "/input", sizeof(hid->phys)); + len = strlen(hid->phys); + if (len < sizeof(hid->phys) - 1) + snprintf(hid->phys + len, sizeof(hid->phys) - len, + "%d", intf->altsetting[0].desc.bInterfaceNumber); + + if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0) + hid->uniq[0] = 0; + + usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL); + if (!usbhid->urbctrl) + goto fail; + + 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); + hid->hidinput_input_event = usb_hidinput_input_event; + hid->hid_open = usbhid_open; + hid->hid_close = usbhid_close; +#ifdef CONFIG_USB_HIDDEV + hid->hiddev_hid_event = hiddev_hid_event; + hid->hiddev_report_event = hiddev_report_event; +#endif + return hid; + +fail: + usb_free_urb(usbhid->urbin); + usb_free_urb(usbhid->urbout); + usb_free_urb(usbhid->urbctrl); + 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); + struct usbhid_device *usbhid; + + if (!hid) + return; + + usbhid = hid->driver_data; + + spin_lock_irq(&usbhid->inlock); /* Sync with error handler */ + usb_set_intfdata(intf, NULL); + spin_unlock_irq(&usbhid->inlock); + usb_kill_urb(usbhid->urbin); + usb_kill_urb(usbhid->urbout); + usb_kill_urb(usbhid->urbctrl); + + del_timer_sync(&usbhid->io_retry); + flush_scheduled_work(); + + if (hid->claimed & HID_CLAIMED_INPUT) + hidinput_disconnect(hid); + if (hid->claimed & HID_CLAIMED_HIDDEV) + hiddev_disconnect(hid); + + usb_free_urb(usbhid->urbin); + usb_free_urb(usbhid->urbctrl); + usb_free_urb(usbhid->urbout); + + hid_free_buffers(hid_to_usb_dev(hid), hid); + 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))) + return -ENODEV; + + usbhid_init_reports(hid); + 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); + return -ENODEV; + } + + if ((hid->claimed & HID_CLAIMED_INPUT)) + hid_ff_init(hid); + + if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER) + hid_fixup_sony_ps3_controller(interface_to_usbdev(intf), + intf->cur_altsetting->desc.bInterfaceNumber); + + 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; +} + +static int hid_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct hid_device *hid = usb_get_intfdata (intf); + struct usbhid_device *usbhid = hid->driver_data; + + 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); + dev_dbg(&intf->dev, "suspend\n"); + return 0; +} + +static int hid_resume(struct usb_interface *intf) +{ + struct hid_device *hid = usb_get_intfdata (intf); + struct usbhid_device *usbhid = hid->driver_data; + int status; + + clear_bit(HID_SUSPENDED, &usbhid->iofl); + usbhid->retry_delay = 0; + status = hid_start_in(hid); + dev_dbg(&intf->dev, "resume status %d\n", status); + return status; +} + +/* 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); +} + +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, + .pre_reset = hid_pre_reset, + .post_reset = hid_post_reset, + .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); diff --git a/drivers/hid/usbhid/hid-ff.c b/drivers/hid/usbhid/hid-ff.c new file mode 100644 index 000000000000..e431faaa6abc --- /dev/null +++ b/drivers/hid/usbhid/hid-ff.c @@ -0,0 +1,89 @@ +/* + * $Id: hid-ff.c,v 1.2 2002/04/18 22:02:47 jdeneux Exp $ + * + * Force feedback support for hid devices. + * Not all hid devices use the same protocol. For example, some use PID, + * other use their own proprietary procotol. + * + * Copyright (c) 2002-2004 Johann Deneux + */ + +/* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so by + * e-mail - mail your message to + */ + +#include + +#undef DEBUG +#include + +#include +#include "usbhid.h" + +/* + * This table contains pointers to initializers. To add support for new + * devices, you need to add the USB vendor and product ids here. + */ +struct hid_ff_initializer { + u16 idVendor; + u16 idProduct; + int (*init)(struct hid_device*); +}; + +/* + * We try pidff when no other driver is found because PID is the + * standards compliant way of implementing force feedback in HID. + * pidff_init() will quickly abort if the device doesn't appear to + * be a PID device + */ +static struct hid_ff_initializer inits[] = { +#ifdef CONFIG_LOGITECH_FF + { 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */ + { 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */ + { 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */ + { 0x46d, 0xc294, hid_lgff_init }, /* Logitech Formula Force EX */ + { 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */ + { 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */ +#endif +#ifdef CONFIG_PANTHERLORD_FF + { 0x810, 0x0001, hid_plff_init }, +#endif +#ifdef CONFIG_THRUSTMASTER_FF + { 0x44f, 0xb304, hid_tmff_init }, +#endif +#ifdef CONFIG_ZEROPLUS_FF + { 0xc12, 0x0005, hid_zpff_init }, + { 0xc12, 0x0030, hid_zpff_init }, +#endif + { 0, 0, hid_pidff_init} /* Matches anything */ +}; + +int hid_ff_init(struct hid_device* hid) +{ + struct hid_ff_initializer *init; + int vendor = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idVendor); + int product = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idProduct); + + for (init = inits; init->idVendor; init++) + if (init->idVendor == vendor && init->idProduct == product) + break; + + return init->init(hid); +} +EXPORT_SYMBOL_GPL(hid_ff_init); + diff --git a/drivers/hid/usbhid/hid-lgff.c b/drivers/hid/usbhid/hid-lgff.c new file mode 100644 index 000000000000..e6f3af3e66d1 --- /dev/null +++ b/drivers/hid/usbhid/hid-lgff.c @@ -0,0 +1,150 @@ +/* + * Force feedback support for hid-compliant for some of the devices from + * Logitech, namely: + * - WingMan Cordless RumblePad + * - WingMan Force 3D + * + * Copyright (c) 2002-2004 Johann Deneux + * Copyright (c) 2006 Anssi Hannula + */ + +/* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so by + * e-mail - mail your message to + */ + +#include +#include +#include +#include "usbhid.h" + +struct dev_type { + u16 idVendor; + u16 idProduct; + const signed short *ff; +}; + +static const signed short ff_rumble[] = { + FF_RUMBLE, + -1 +}; + +static const signed short ff_joystick[] = { + FF_CONSTANT, + -1 +}; + +static const struct dev_type devices[] = { + { 0x046d, 0xc211, ff_rumble }, + { 0x046d, 0xc219, ff_rumble }, + { 0x046d, 0xc283, ff_joystick }, + { 0x046d, 0xc294, ff_joystick }, + { 0x046d, 0xc295, ff_joystick }, + { 0x046d, 0xca03, ff_joystick }, +}; + +static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect) +{ + struct hid_device *hid = dev->private; + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct hid_report *report = list_entry(report_list->next, struct hid_report, list); + int x, y; + unsigned int left, right; + +#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff + + switch (effect->type) { + case FF_CONSTANT: + x = effect->u.ramp.start_level + 0x7f; /* 0x7f is center */ + y = effect->u.ramp.end_level + 0x7f; + CLAMP(x); + CLAMP(y); + report->field[0]->value[0] = 0x51; + report->field[0]->value[1] = 0x08; + report->field[0]->value[2] = x; + report->field[0]->value[3] = y; + dbg("(x, y)=(%04x, %04x)", x, y); + usbhid_submit_report(hid, report, USB_DIR_OUT); + break; + + case FF_RUMBLE: + right = effect->u.rumble.strong_magnitude; + left = effect->u.rumble.weak_magnitude; + right = right * 0xff / 0xffff; + left = left * 0xff / 0xffff; + CLAMP(left); + CLAMP(right); + report->field[0]->value[0] = 0x42; + report->field[0]->value[1] = 0x00; + report->field[0]->value[2] = left; + report->field[0]->value[3] = right; + dbg("(left, right)=(%04x, %04x)", left, right); + usbhid_submit_report(hid, report, USB_DIR_OUT); + break; + } + return 0; +} + +int hid_lgff_init(struct hid_device* hid) +{ + struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct input_dev *dev = hidinput->input; + struct hid_report *report; + struct hid_field *field; + const signed short *ff_bits = ff_joystick; + int error; + int i; + + /* Find the report to use */ + if (list_empty(report_list)) { + err("No output report found"); + return -1; + } + + /* Check that the report looks ok */ + report = list_entry(report_list->next, struct hid_report, list); + if (!report) { + err("NULL output report"); + return -1; + } + + field = report->field[0]; + if (!field) { + err("NULL field"); + return -1; + } + + for (i = 0; i < ARRAY_SIZE(devices); i++) { + if (dev->id.vendor == devices[i].idVendor && + dev->id.product == devices[i].idProduct) { + ff_bits = devices[i].ff; + break; + } + } + + for (i = 0; ff_bits[i] >= 0; i++) + set_bit(ff_bits[i], dev->ffbit); + + error = input_ff_create_memless(dev, NULL, hid_lgff_play); + if (error) + return error; + + printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux \n"); + + return 0; +} diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c new file mode 100644 index 000000000000..f5a90e950e6b --- /dev/null +++ b/drivers/hid/usbhid/hid-pidff.c @@ -0,0 +1,1331 @@ +/* + * Force feedback driver for USB HID PID compliant devices + * + * Copyright (c) 2005, 2006 Anssi Hannula + */ + +/* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define DEBUG */ + +#define debug(format, arg...) pr_debug("hid-pidff: " format "\n" , ## arg) + +#include +#include + +#include + +#include "usbhid.h" + +#define PID_EFFECTS_MAX 64 + +/* Report usage table used to put reports into an array */ + +#define PID_SET_EFFECT 0 +#define PID_EFFECT_OPERATION 1 +#define PID_DEVICE_GAIN 2 +#define PID_POOL 3 +#define PID_BLOCK_LOAD 4 +#define PID_BLOCK_FREE 5 +#define PID_DEVICE_CONTROL 6 +#define PID_CREATE_NEW_EFFECT 7 + +#define PID_REQUIRED_REPORTS 7 + +#define PID_SET_ENVELOPE 8 +#define PID_SET_CONDITION 9 +#define PID_SET_PERIODIC 10 +#define PID_SET_CONSTANT 11 +#define PID_SET_RAMP 12 +static const u8 pidff_reports[] = { + 0x21, 0x77, 0x7d, 0x7f, 0x89, 0x90, 0x96, 0xab, + 0x5a, 0x5f, 0x6e, 0x73, 0x74 +}; + +/* device_control is really 0x95, but 0x96 specified as it is the usage of +the only field in that report */ + +/* Value usage tables used to put fields and values into arrays */ + +#define PID_EFFECT_BLOCK_INDEX 0 + +#define PID_DURATION 1 +#define PID_GAIN 2 +#define PID_TRIGGER_BUTTON 3 +#define PID_TRIGGER_REPEAT_INT 4 +#define PID_DIRECTION_ENABLE 5 +#define PID_START_DELAY 6 +static const u8 pidff_set_effect[] = { + 0x22, 0x50, 0x52, 0x53, 0x54, 0x56, 0xa7 +}; + +#define PID_ATTACK_LEVEL 1 +#define PID_ATTACK_TIME 2 +#define PID_FADE_LEVEL 3 +#define PID_FADE_TIME 4 +static const u8 pidff_set_envelope[] = { 0x22, 0x5b, 0x5c, 0x5d, 0x5e }; + +#define PID_PARAM_BLOCK_OFFSET 1 +#define PID_CP_OFFSET 2 +#define PID_POS_COEFFICIENT 3 +#define PID_NEG_COEFFICIENT 4 +#define PID_POS_SATURATION 5 +#define PID_NEG_SATURATION 6 +#define PID_DEAD_BAND 7 +static const u8 pidff_set_condition[] = { + 0x22, 0x23, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65 +}; + +#define PID_MAGNITUDE 1 +#define PID_OFFSET 2 +#define PID_PHASE 3 +#define PID_PERIOD 4 +static const u8 pidff_set_periodic[] = { 0x22, 0x70, 0x6f, 0x71, 0x72 }; +static const u8 pidff_set_constant[] = { 0x22, 0x70 }; + +#define PID_RAMP_START 1 +#define PID_RAMP_END 2 +static const u8 pidff_set_ramp[] = { 0x22, 0x75, 0x76 }; + +#define PID_RAM_POOL_AVAILABLE 1 +static const u8 pidff_block_load[] = { 0x22, 0xac }; + +#define PID_LOOP_COUNT 1 +static const u8 pidff_effect_operation[] = { 0x22, 0x7c }; + +static const u8 pidff_block_free[] = { 0x22 }; + +#define PID_DEVICE_GAIN_FIELD 0 +static const u8 pidff_device_gain[] = { 0x7e }; + +#define PID_RAM_POOL_SIZE 0 +#define PID_SIMULTANEOUS_MAX 1 +#define PID_DEVICE_MANAGED_POOL 2 +static const u8 pidff_pool[] = { 0x80, 0x83, 0xa9 }; + +/* Special field key tables used to put special field keys into arrays */ + +#define PID_ENABLE_ACTUATORS 0 +#define PID_RESET 1 +static const u8 pidff_device_control[] = { 0x97, 0x9a }; + +#define PID_CONSTANT 0 +#define PID_RAMP 1 +#define PID_SQUARE 2 +#define PID_SINE 3 +#define PID_TRIANGLE 4 +#define PID_SAW_UP 5 +#define PID_SAW_DOWN 6 +#define PID_SPRING 7 +#define PID_DAMPER 8 +#define PID_INERTIA 9 +#define PID_FRICTION 10 +static const u8 pidff_effect_types[] = { + 0x26, 0x27, 0x30, 0x31, 0x32, 0x33, 0x34, + 0x40, 0x41, 0x42, 0x43 +}; + +#define PID_BLOCK_LOAD_SUCCESS 0 +#define PID_BLOCK_LOAD_FULL 1 +static const u8 pidff_block_load_status[] = { 0x8c, 0x8d }; + +#define PID_EFFECT_START 0 +#define PID_EFFECT_STOP 1 +static const u8 pidff_effect_operation_status[] = { 0x79, 0x7b }; + +struct pidff_usage { + struct hid_field *field; + s32 *value; +}; + +struct pidff_device { + struct hid_device *hid; + + struct hid_report *reports[sizeof(pidff_reports)]; + + struct pidff_usage set_effect[sizeof(pidff_set_effect)]; + struct pidff_usage set_envelope[sizeof(pidff_set_envelope)]; + struct pidff_usage set_condition[sizeof(pidff_set_condition)]; + struct pidff_usage set_periodic[sizeof(pidff_set_periodic)]; + struct pidff_usage set_constant[sizeof(pidff_set_constant)]; + struct pidff_usage set_ramp[sizeof(pidff_set_ramp)]; + + struct pidff_usage device_gain[sizeof(pidff_device_gain)]; + struct pidff_usage block_load[sizeof(pidff_block_load)]; + struct pidff_usage pool[sizeof(pidff_pool)]; + struct pidff_usage effect_operation[sizeof(pidff_effect_operation)]; + struct pidff_usage block_free[sizeof(pidff_block_free)]; + + /* Special field is a field that is not composed of + usage<->value pairs that pidff_usage values are */ + + /* Special field in create_new_effect */ + struct hid_field *create_new_effect_type; + + /* Special fields in set_effect */ + struct hid_field *set_effect_type; + struct hid_field *effect_direction; + + /* Special field in device_control */ + struct hid_field *device_control; + + /* Special field in block_load */ + struct hid_field *block_load_status; + + /* Special field in effect_operation */ + struct hid_field *effect_operation_status; + + int control_id[sizeof(pidff_device_control)]; + int type_id[sizeof(pidff_effect_types)]; + int status_id[sizeof(pidff_block_load_status)]; + int operation_id[sizeof(pidff_effect_operation_status)]; + + int pid_id[PID_EFFECTS_MAX]; +}; + +/* + * Scale an unsigned value with range 0..max for the given field + */ +static int pidff_rescale(int i, int max, struct hid_field *field) +{ + return i * (field->logical_maximum - field->logical_minimum) / max + + field->logical_minimum; +} + +/* + * Scale a signed value in range -0x8000..0x7fff for the given field + */ +static int pidff_rescale_signed(int i, struct hid_field *field) +{ + return i == 0 ? 0 : i > + 0 ? i * field->logical_maximum / 0x7fff : i * + field->logical_minimum / -0x8000; +} + +static void pidff_set(struct pidff_usage *usage, u16 value) +{ + usage->value[0] = pidff_rescale(value, 0xffff, usage->field); + debug("calculated from %d to %d", value, usage->value[0]); +} + +static void pidff_set_signed(struct pidff_usage *usage, s16 value) +{ + if (usage->field->logical_minimum < 0) + usage->value[0] = pidff_rescale_signed(value, usage->field); + else { + if (value < 0) + usage->value[0] = + pidff_rescale(-value, 0x8000, usage->field); + else + usage->value[0] = + pidff_rescale(value, 0x7fff, usage->field); + } + debug("calculated from %d to %d", value, usage->value[0]); +} + +/* + * Send envelope report to the device + */ +static void pidff_set_envelope_report(struct pidff_device *pidff, + struct ff_envelope *envelope) +{ + pidff->set_envelope[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + + pidff->set_envelope[PID_ATTACK_LEVEL].value[0] = + pidff_rescale(envelope->attack_level > + 0x7fff ? 0x7fff : envelope->attack_level, 0x7fff, + pidff->set_envelope[PID_ATTACK_LEVEL].field); + pidff->set_envelope[PID_FADE_LEVEL].value[0] = + pidff_rescale(envelope->fade_level > + 0x7fff ? 0x7fff : envelope->fade_level, 0x7fff, + pidff->set_envelope[PID_FADE_LEVEL].field); + + pidff->set_envelope[PID_ATTACK_TIME].value[0] = envelope->attack_length; + pidff->set_envelope[PID_FADE_TIME].value[0] = envelope->fade_length; + + debug("attack %u => %d", envelope->attack_level, + pidff->set_envelope[PID_ATTACK_LEVEL].value[0]); + + usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_ENVELOPE], + USB_DIR_OUT); +} + +/* + * Test if the new envelope differs from old one + */ +static int pidff_needs_set_envelope(struct ff_envelope *envelope, + struct ff_envelope *old) +{ + return envelope->attack_level != old->attack_level || + envelope->fade_level != old->fade_level || + envelope->attack_length != old->attack_length || + envelope->fade_length != old->fade_length; +} + +/* + * Send constant force report to the device + */ +static void pidff_set_constant_force_report(struct pidff_device *pidff, + struct ff_effect *effect) +{ + pidff->set_constant[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + pidff_set_signed(&pidff->set_constant[PID_MAGNITUDE], + effect->u.constant.level); + + usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONSTANT], + USB_DIR_OUT); +} + +/* + * Test if the constant parameters have changed between effects + */ +static int pidff_needs_set_constant(struct ff_effect *effect, + struct ff_effect *old) +{ + return effect->u.constant.level != old->u.constant.level; +} + +/* + * Send set effect report to the device + */ +static void pidff_set_effect_report(struct pidff_device *pidff, + struct ff_effect *effect) +{ + pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + pidff->set_effect_type->value[0] = + pidff->create_new_effect_type->value[0]; + pidff->set_effect[PID_DURATION].value[0] = effect->replay.length; + pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = effect->trigger.button; + pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = + effect->trigger.interval; + pidff->set_effect[PID_GAIN].value[0] = + pidff->set_effect[PID_GAIN].field->logical_maximum; + pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1; + pidff->effect_direction->value[0] = + pidff_rescale(effect->direction, 0xffff, + pidff->effect_direction); + pidff->set_effect[PID_START_DELAY].value[0] = effect->replay.delay; + + usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT], + USB_DIR_OUT); +} + +/* + * Test if the values used in set_effect have changed + */ +static int pidff_needs_set_effect(struct ff_effect *effect, + struct ff_effect *old) +{ + return effect->replay.length != old->replay.length || + effect->trigger.interval != old->trigger.interval || + effect->trigger.button != old->trigger.button || + effect->direction != old->direction || + effect->replay.delay != old->replay.delay; +} + +/* + * Send periodic effect report to the device + */ +static void pidff_set_periodic_report(struct pidff_device *pidff, + struct ff_effect *effect) +{ + pidff->set_periodic[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + pidff_set_signed(&pidff->set_periodic[PID_MAGNITUDE], + effect->u.periodic.magnitude); + pidff_set_signed(&pidff->set_periodic[PID_OFFSET], + effect->u.periodic.offset); + pidff_set(&pidff->set_periodic[PID_PHASE], effect->u.periodic.phase); + pidff->set_periodic[PID_PERIOD].value[0] = effect->u.periodic.period; + + usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_PERIODIC], + USB_DIR_OUT); + +} + +/* + * Test if periodic effect parameters have changed + */ +static int pidff_needs_set_periodic(struct ff_effect *effect, + struct ff_effect *old) +{ + return effect->u.periodic.magnitude != old->u.periodic.magnitude || + effect->u.periodic.offset != old->u.periodic.offset || + effect->u.periodic.phase != old->u.periodic.phase || + effect->u.periodic.period != old->u.periodic.period; +} + +/* + * Send condition effect reports to the device + */ +static void pidff_set_condition_report(struct pidff_device *pidff, + struct ff_effect *effect) +{ + int i; + + pidff->set_condition[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + + for (i = 0; i < 2; i++) { + pidff->set_condition[PID_PARAM_BLOCK_OFFSET].value[0] = i; + pidff_set_signed(&pidff->set_condition[PID_CP_OFFSET], + effect->u.condition[i].center); + pidff_set_signed(&pidff->set_condition[PID_POS_COEFFICIENT], + effect->u.condition[i].right_coeff); + pidff_set_signed(&pidff->set_condition[PID_NEG_COEFFICIENT], + effect->u.condition[i].left_coeff); + pidff_set(&pidff->set_condition[PID_POS_SATURATION], + effect->u.condition[i].right_saturation); + pidff_set(&pidff->set_condition[PID_NEG_SATURATION], + effect->u.condition[i].left_saturation); + pidff_set(&pidff->set_condition[PID_DEAD_BAND], + effect->u.condition[i].deadband); + usbhid_wait_io(pidff->hid); + usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONDITION], + USB_DIR_OUT); + } +} + +/* + * Test if condition effect parameters have changed + */ +static int pidff_needs_set_condition(struct ff_effect *effect, + struct ff_effect *old) +{ + int i; + int ret = 0; + + for (i = 0; i < 2; i++) { + struct ff_condition_effect *cond = &effect->u.condition[i]; + struct ff_condition_effect *old_cond = &old->u.condition[i]; + + ret |= cond->center != old_cond->center || + cond->right_coeff != old_cond->right_coeff || + cond->left_coeff != old_cond->left_coeff || + cond->right_saturation != old_cond->right_saturation || + cond->left_saturation != old_cond->left_saturation || + cond->deadband != old_cond->deadband; + } + + return ret; +} + +/* + * Send ramp force report to the device + */ +static void pidff_set_ramp_force_report(struct pidff_device *pidff, + struct ff_effect *effect) +{ + pidff->set_ramp[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + pidff_set_signed(&pidff->set_ramp[PID_RAMP_START], + effect->u.ramp.start_level); + pidff_set_signed(&pidff->set_ramp[PID_RAMP_END], + effect->u.ramp.end_level); + usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_RAMP], + USB_DIR_OUT); +} + +/* + * Test if ramp force parameters have changed + */ +static int pidff_needs_set_ramp(struct ff_effect *effect, struct ff_effect *old) +{ + return effect->u.ramp.start_level != old->u.ramp.start_level || + effect->u.ramp.end_level != old->u.ramp.end_level; +} + +/* + * Send a request for effect upload to the device + * + * Returns 0 if device reported success, -ENOSPC if the device reported memory + * is full. Upon unknown response the function will retry for 60 times, if + * still unsuccessful -EIO is returned. + */ +static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum) +{ + int j; + + pidff->create_new_effect_type->value[0] = efnum; + usbhid_submit_report(pidff->hid, pidff->reports[PID_CREATE_NEW_EFFECT], + USB_DIR_OUT); + debug("create_new_effect sent, type: %d", efnum); + + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] = 0; + pidff->block_load_status->value[0] = 0; + usbhid_wait_io(pidff->hid); + + for (j = 0; j < 60; j++) { + debug("pid_block_load requested"); + usbhid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_LOAD], + USB_DIR_IN); + usbhid_wait_io(pidff->hid); + if (pidff->block_load_status->value[0] == + pidff->status_id[PID_BLOCK_LOAD_SUCCESS]) { + debug("device reported free memory: %d bytes", + pidff->block_load[PID_RAM_POOL_AVAILABLE].value ? + pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1); + return 0; + } + if (pidff->block_load_status->value[0] == + pidff->status_id[PID_BLOCK_LOAD_FULL]) { + debug("not enough memory free: %d bytes", + pidff->block_load[PID_RAM_POOL_AVAILABLE].value ? + pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1); + return -ENOSPC; + } + } + printk(KERN_ERR "hid-pidff: pid_block_load failed 60 times\n"); + return -EIO; +} + +/* + * Play the effect with PID id n times + */ +static void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n) +{ + pidff->effect_operation[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id; + + if (n == 0) { + pidff->effect_operation_status->value[0] = + pidff->operation_id[PID_EFFECT_STOP]; + } else { + pidff->effect_operation_status->value[0] = + pidff->operation_id[PID_EFFECT_START]; + pidff->effect_operation[PID_LOOP_COUNT].value[0] = n; + } + + usbhid_wait_io(pidff->hid); + usbhid_submit_report(pidff->hid, pidff->reports[PID_EFFECT_OPERATION], + USB_DIR_OUT); +} + +/** + * Play the effect with effect id @effect_id for @value times + */ +static int pidff_playback(struct input_dev *dev, int effect_id, int value) +{ + struct pidff_device *pidff = dev->ff->private; + + pidff_playback_pid(pidff, pidff->pid_id[effect_id], value); + + return 0; +} + +/* + * Erase effect with PID id + */ +static void pidff_erase_pid(struct pidff_device *pidff, int pid_id) +{ + pidff->block_free[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id; + usbhid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_FREE], + USB_DIR_OUT); +} + +/* + * Stop and erase effect with effect_id + */ +static int pidff_erase_effect(struct input_dev *dev, int effect_id) +{ + struct pidff_device *pidff = dev->ff->private; + int pid_id = pidff->pid_id[effect_id]; + + debug("starting to erase %d/%d", effect_id, pidff->pid_id[effect_id]); + pidff_playback_pid(pidff, pid_id, 0); + pidff_erase_pid(pidff, pid_id); + + return 0; +} + +/* + * Effect upload handler + */ +static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect, + struct ff_effect *old) +{ + struct pidff_device *pidff = dev->ff->private; + int type_id; + int error; + + switch (effect->type) { + case FF_CONSTANT: + if (!old) { + error = pidff_request_effect_upload(pidff, + pidff->type_id[PID_CONSTANT]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_constant(effect, old)) + pidff_set_constant_force_report(pidff, effect); + if (!old || + pidff_needs_set_envelope(&effect->u.constant.envelope, + &old->u.constant.envelope)) + pidff_set_envelope_report(pidff, + &effect->u.constant.envelope); + break; + + case FF_PERIODIC: + if (!old) { + switch (effect->u.periodic.waveform) { + case FF_SQUARE: + type_id = PID_SQUARE; + break; + case FF_TRIANGLE: + type_id = PID_TRIANGLE; + break; + case FF_SINE: + type_id = PID_SINE; + break; + case FF_SAW_UP: + type_id = PID_SAW_UP; + break; + case FF_SAW_DOWN: + type_id = PID_SAW_DOWN; + break; + default: + printk(KERN_ERR + "hid-pidff: invalid waveform\n"); + return -EINVAL; + } + + error = pidff_request_effect_upload(pidff, + pidff->type_id[type_id]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_periodic(effect, old)) + pidff_set_periodic_report(pidff, effect); + if (!old || + pidff_needs_set_envelope(&effect->u.periodic.envelope, + &old->u.periodic.envelope)) + pidff_set_envelope_report(pidff, + &effect->u.periodic.envelope); + break; + + case FF_RAMP: + if (!old) { + error = pidff_request_effect_upload(pidff, + pidff->type_id[PID_RAMP]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_ramp(effect, old)) + pidff_set_ramp_force_report(pidff, effect); + if (!old || + pidff_needs_set_envelope(&effect->u.ramp.envelope, + &old->u.ramp.envelope)) + pidff_set_envelope_report(pidff, + &effect->u.ramp.envelope); + break; + + case FF_SPRING: + if (!old) { + error = pidff_request_effect_upload(pidff, + pidff->type_id[PID_SPRING]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_condition(effect, old)) + pidff_set_condition_report(pidff, effect); + break; + + case FF_FRICTION: + if (!old) { + error = pidff_request_effect_upload(pidff, + pidff->type_id[PID_FRICTION]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_condition(effect, old)) + pidff_set_condition_report(pidff, effect); + break; + + case FF_DAMPER: + if (!old) { + error = pidff_request_effect_upload(pidff, + pidff->type_id[PID_DAMPER]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_condition(effect, old)) + pidff_set_condition_report(pidff, effect); + break; + + case FF_INERTIA: + if (!old) { + error = pidff_request_effect_upload(pidff, + pidff->type_id[PID_INERTIA]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_condition(effect, old)) + pidff_set_condition_report(pidff, effect); + break; + + default: + printk(KERN_ERR "hid-pidff: invalid type\n"); + return -EINVAL; + } + + if (!old) + pidff->pid_id[effect->id] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + + debug("uploaded"); + + return 0; +} + +/* + * set_gain() handler + */ +static void pidff_set_gain(struct input_dev *dev, u16 gain) +{ + struct pidff_device *pidff = dev->ff->private; + + pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], gain); + usbhid_submit_report(pidff->hid, pidff->reports[PID_DEVICE_GAIN], + USB_DIR_OUT); +} + +static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude) +{ + struct hid_field *field = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].field; + + if (!magnitude) { + pidff_playback_pid(pidff, field->logical_minimum, 0); + return; + } + + pidff_playback_pid(pidff, field->logical_minimum, 1); + + pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum; + pidff->set_effect_type->value[0] = pidff->type_id[PID_SPRING]; + pidff->set_effect[PID_DURATION].value[0] = 0; + pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = 0; + pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = 0; + pidff_set(&pidff->set_effect[PID_GAIN], magnitude); + pidff->set_effect[PID_START_DELAY].value[0] = 0; + + usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT], + USB_DIR_OUT); +} + +/* + * pidff_set_autocenter() handler + */ +static void pidff_set_autocenter(struct input_dev *dev, u16 magnitude) +{ + struct pidff_device *pidff = dev->ff->private; + + pidff_autocenter(pidff, magnitude); +} + +/* + * Find fields from a report and fill a pidff_usage + */ +static int pidff_find_fields(struct pidff_usage *usage, const u8 *table, + struct hid_report *report, int count, int strict) +{ + int i, j, k, found; + + for (k = 0; k < count; k++) { + found = 0; + for (i = 0; i < report->maxfield; i++) { + if (report->field[i]->maxusage != + report->field[i]->report_count) { + debug("maxusage and report_count do not match, " + "skipping"); + continue; + } + for (j = 0; j < report->field[i]->maxusage; j++) { + if (report->field[i]->usage[j].hid == + (HID_UP_PID | table[k])) { + debug("found %d at %d->%d", k, i, j); + usage[k].field = report->field[i]; + usage[k].value = + &report->field[i]->value[j]; + found = 1; + break; + } + } + if (found) + break; + } + if (!found && strict) { + debug("failed to locate %d", k); + return -1; + } + } + return 0; +} + +/* + * Return index into pidff_reports for the given usage + */ +static int pidff_check_usage(int usage) +{ + int i; + + for (i = 0; i < sizeof(pidff_reports); i++) + if (usage == (HID_UP_PID | pidff_reports[i])) + return i; + + return -1; +} + +/* + * Find the reports and fill pidff->reports[] + * report_type specifies either OUTPUT or FEATURE reports + */ +static void pidff_find_reports(struct hid_device *hid, int report_type, + struct pidff_device *pidff) +{ + struct hid_report *report; + int i, ret; + + list_for_each_entry(report, + &hid->report_enum[report_type].report_list, list) { + if (report->maxfield < 1) + continue; + ret = pidff_check_usage(report->field[0]->logical); + if (ret != -1) { + debug("found usage 0x%02x from field->logical", + pidff_reports[ret]); + pidff->reports[ret] = report; + continue; + } + + /* + * Sometimes logical collections are stacked to indicate + * different usages for the report and the field, in which + * case we want the usage of the parent. However, Linux HID + * implementation hides this fact, so we have to dig it up + * ourselves + */ + i = report->field[0]->usage[0].collection_index; + if (i <= 0 || + hid->collection[i - 1].type != HID_COLLECTION_LOGICAL) + continue; + ret = pidff_check_usage(hid->collection[i - 1].usage); + if (ret != -1 && !pidff->reports[ret]) { + debug("found usage 0x%02x from collection array", + pidff_reports[ret]); + pidff->reports[ret] = report; + } + } +} + +/* + * Test if the required reports have been found + */ +static int pidff_reports_ok(struct pidff_device *pidff) +{ + int i; + + for (i = 0; i <= PID_REQUIRED_REPORTS; i++) { + if (!pidff->reports[i]) { + debug("%d missing", i); + return 0; + } + } + + return 1; +} + +/* + * Find a field with a specific usage within a report + */ +static struct hid_field *pidff_find_special_field(struct hid_report *report, + int usage, int enforce_min) +{ + int i; + + for (i = 0; i < report->maxfield; i++) { + if (report->field[i]->logical == (HID_UP_PID | usage) && + report->field[i]->report_count > 0) { + if (!enforce_min || + report->field[i]->logical_minimum == 1) + return report->field[i]; + else { + printk(KERN_ERR "hid-pidff: logical_minimum " + "is not 1 as it should be\n"); + return NULL; + } + } + } + return NULL; +} + +/* + * Fill a pidff->*_id struct table + */ +static int pidff_find_special_keys(int *keys, struct hid_field *fld, + const u8 *usagetable, int count) +{ + + int i, j; + int found = 0; + + for (i = 0; i < count; i++) { + for (j = 0; j < fld->maxusage; j++) { + if (fld->usage[j].hid == (HID_UP_PID | usagetable[i])) { + keys[i] = j + 1; + found++; + break; + } + } + } + return found; +} + +#define PIDFF_FIND_SPECIAL_KEYS(keys, field, name) \ + pidff_find_special_keys(pidff->keys, pidff->field, pidff_ ## name, \ + sizeof(pidff_ ## name)) + +/* + * Find and check the special fields + */ +static int pidff_find_special_fields(struct pidff_device *pidff) +{ + debug("finding special fields"); + + pidff->create_new_effect_type = + pidff_find_special_field(pidff->reports[PID_CREATE_NEW_EFFECT], + 0x25, 1); + pidff->set_effect_type = + pidff_find_special_field(pidff->reports[PID_SET_EFFECT], + 0x25, 1); + pidff->effect_direction = + pidff_find_special_field(pidff->reports[PID_SET_EFFECT], + 0x57, 0); + pidff->device_control = + pidff_find_special_field(pidff->reports[PID_DEVICE_CONTROL], + 0x96, 1); + pidff->block_load_status = + pidff_find_special_field(pidff->reports[PID_BLOCK_LOAD], + 0x8b, 1); + pidff->effect_operation_status = + pidff_find_special_field(pidff->reports[PID_EFFECT_OPERATION], + 0x78, 1); + + debug("search done"); + + if (!pidff->create_new_effect_type || !pidff->set_effect_type) { + printk(KERN_ERR "hid-pidff: effect lists not found\n"); + return -1; + } + + if (!pidff->effect_direction) { + printk(KERN_ERR "hid-pidff: direction field not found\n"); + return -1; + } + + if (!pidff->device_control) { + printk(KERN_ERR "hid-pidff: device control field not found\n"); + return -1; + } + + if (!pidff->block_load_status) { + printk(KERN_ERR + "hid-pidff: block load status field not found\n"); + return -1; + } + + if (!pidff->effect_operation_status) { + printk(KERN_ERR + "hid-pidff: effect operation field not found\n"); + return -1; + } + + pidff_find_special_keys(pidff->control_id, pidff->device_control, + pidff_device_control, + sizeof(pidff_device_control)); + + PIDFF_FIND_SPECIAL_KEYS(control_id, device_control, device_control); + + if (!PIDFF_FIND_SPECIAL_KEYS(type_id, create_new_effect_type, + effect_types)) { + printk(KERN_ERR "hid-pidff: no effect types found\n"); + return -1; + } + + if (PIDFF_FIND_SPECIAL_KEYS(status_id, block_load_status, + block_load_status) != + sizeof(pidff_block_load_status)) { + printk(KERN_ERR + "hidpidff: block load status identifiers not found\n"); + return -1; + } + + if (PIDFF_FIND_SPECIAL_KEYS(operation_id, effect_operation_status, + effect_operation_status) != + sizeof(pidff_effect_operation_status)) { + printk(KERN_ERR + "hidpidff: effect operation identifiers not found\n"); + return -1; + } + + return 0; +} + +/** + * Find the implemented effect types + */ +static int pidff_find_effects(struct pidff_device *pidff, + struct input_dev *dev) +{ + int i; + + for (i = 0; i < sizeof(pidff_effect_types); i++) { + int pidff_type = pidff->type_id[i]; + if (pidff->set_effect_type->usage[pidff_type].hid != + pidff->create_new_effect_type->usage[pidff_type].hid) { + printk(KERN_ERR "hid-pidff: " + "effect type number %d is invalid\n", i); + return -1; + } + } + + if (pidff->type_id[PID_CONSTANT]) + set_bit(FF_CONSTANT, dev->ffbit); + if (pidff->type_id[PID_RAMP]) + set_bit(FF_RAMP, dev->ffbit); + if (pidff->type_id[PID_SQUARE]) { + set_bit(FF_SQUARE, dev->ffbit); + set_bit(FF_PERIODIC, dev->ffbit); + } + if (pidff->type_id[PID_SINE]) { + set_bit(FF_SINE, dev->ffbit); + set_bit(FF_PERIODIC, dev->ffbit); + } + if (pidff->type_id[PID_TRIANGLE]) { + set_bit(FF_TRIANGLE, dev->ffbit); + set_bit(FF_PERIODIC, dev->ffbit); + } + if (pidff->type_id[PID_SAW_UP]) { + set_bit(FF_SAW_UP, dev->ffbit); + set_bit(FF_PERIODIC, dev->ffbit); + } + if (pidff->type_id[PID_SAW_DOWN]) { + set_bit(FF_SAW_DOWN, dev->ffbit); + set_bit(FF_PERIODIC, dev->ffbit); + } + if (pidff->type_id[PID_SPRING]) + set_bit(FF_SPRING, dev->ffbit); + if (pidff->type_id[PID_DAMPER]) + set_bit(FF_DAMPER, dev->ffbit); + if (pidff->type_id[PID_INERTIA]) + set_bit(FF_INERTIA, dev->ffbit); + if (pidff->type_id[PID_FRICTION]) + set_bit(FF_FRICTION, dev->ffbit); + + return 0; + +} + +#define PIDFF_FIND_FIELDS(name, report, strict) \ + pidff_find_fields(pidff->name, pidff_ ## name, \ + pidff->reports[report], \ + sizeof(pidff_ ## name), strict) + +/* + * Fill and check the pidff_usages + */ +static int pidff_init_fields(struct pidff_device *pidff, struct input_dev *dev) +{ + int envelope_ok = 0; + + if (PIDFF_FIND_FIELDS(set_effect, PID_SET_EFFECT, 1)) { + printk(KERN_ERR + "hid-pidff: unknown set_effect report layout\n"); + return -ENODEV; + } + + PIDFF_FIND_FIELDS(block_load, PID_BLOCK_LOAD, 0); + if (!pidff->block_load[PID_EFFECT_BLOCK_INDEX].value) { + printk(KERN_ERR + "hid-pidff: unknown pid_block_load report layout\n"); + return -ENODEV; + } + + if (PIDFF_FIND_FIELDS(effect_operation, PID_EFFECT_OPERATION, 1)) { + printk(KERN_ERR + "hid-pidff: unknown effect_operation report layout\n"); + return -ENODEV; + } + + if (PIDFF_FIND_FIELDS(block_free, PID_BLOCK_FREE, 1)) { + printk(KERN_ERR + "hid-pidff: unknown pid_block_free report layout\n"); + return -ENODEV; + } + + if (!PIDFF_FIND_FIELDS(set_envelope, PID_SET_ENVELOPE, 1)) + envelope_ok = 1; + + if (pidff_find_special_fields(pidff) || pidff_find_effects(pidff, dev)) + return -ENODEV; + + if (!envelope_ok) { + if (test_and_clear_bit(FF_CONSTANT, dev->ffbit)) + printk(KERN_WARNING "hid-pidff: " + "has constant effect but no envelope\n"); + if (test_and_clear_bit(FF_RAMP, dev->ffbit)) + printk(KERN_WARNING "hid-pidff: " + "has ramp effect but no envelope\n"); + + if (test_and_clear_bit(FF_PERIODIC, dev->ffbit)) + printk(KERN_WARNING "hid-pidff: " + "has periodic effect but no envelope\n"); + } + + if (test_bit(FF_CONSTANT, dev->ffbit) && + PIDFF_FIND_FIELDS(set_constant, PID_SET_CONSTANT, 1)) { + printk(KERN_WARNING + "hid-pidff: unknown constant effect layout\n"); + clear_bit(FF_CONSTANT, dev->ffbit); + } + + if (test_bit(FF_RAMP, dev->ffbit) && + PIDFF_FIND_FIELDS(set_ramp, PID_SET_RAMP, 1)) { + printk(KERN_WARNING "hid-pidff: unknown ramp effect layout\n"); + clear_bit(FF_RAMP, dev->ffbit); + } + + if ((test_bit(FF_SPRING, dev->ffbit) || + test_bit(FF_DAMPER, dev->ffbit) || + test_bit(FF_FRICTION, dev->ffbit) || + test_bit(FF_INERTIA, dev->ffbit)) && + PIDFF_FIND_FIELDS(set_condition, PID_SET_CONDITION, 1)) { + printk(KERN_WARNING + "hid-pidff: unknown condition effect layout\n"); + clear_bit(FF_SPRING, dev->ffbit); + clear_bit(FF_DAMPER, dev->ffbit); + clear_bit(FF_FRICTION, dev->ffbit); + clear_bit(FF_INERTIA, dev->ffbit); + } + + if (test_bit(FF_PERIODIC, dev->ffbit) && + PIDFF_FIND_FIELDS(set_periodic, PID_SET_PERIODIC, 1)) { + printk(KERN_WARNING + "hid-pidff: unknown periodic effect layout\n"); + clear_bit(FF_PERIODIC, dev->ffbit); + } + + PIDFF_FIND_FIELDS(pool, PID_POOL, 0); + + if (!PIDFF_FIND_FIELDS(device_gain, PID_DEVICE_GAIN, 1)) + set_bit(FF_GAIN, dev->ffbit); + + return 0; +} + +/* + * Reset the device + */ +static void pidff_reset(struct pidff_device *pidff) +{ + struct hid_device *hid = pidff->hid; + int i = 0; + + pidff->device_control->value[0] = pidff->control_id[PID_RESET]; + /* We reset twice as sometimes hid_wait_io isn't waiting long enough */ + usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); + usbhid_wait_io(hid); + usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); + usbhid_wait_io(hid); + + pidff->device_control->value[0] = + pidff->control_id[PID_ENABLE_ACTUATORS]; + usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); + usbhid_wait_io(hid); + + /* pool report is sometimes messed up, refetch it */ + usbhid_submit_report(hid, pidff->reports[PID_POOL], USB_DIR_IN); + usbhid_wait_io(hid); + + if (pidff->pool[PID_SIMULTANEOUS_MAX].value) { + int sim_effects = pidff->pool[PID_SIMULTANEOUS_MAX].value[0]; + while (sim_effects < 2) { + if (i++ > 20) { + printk(KERN_WARNING "hid-pidff: device reports " + "%d simultaneous effects\n", + sim_effects); + break; + } + debug("pid_pool requested again"); + usbhid_submit_report(hid, pidff->reports[PID_POOL], + USB_DIR_IN); + usbhid_wait_io(hid); + } + } +} + +/* + * Test if autocenter modification is using the supported method + */ +static int pidff_check_autocenter(struct pidff_device *pidff, + struct input_dev *dev) +{ + int error; + + /* + * Let's find out if autocenter modification is supported + * Specification doesn't specify anything, so we request an + * effect upload and cancel it immediately. If the approved + * effect id was one above the minimum, then we assume the first + * effect id is a built-in spring type effect used for autocenter + */ + + error = pidff_request_effect_upload(pidff, 1); + if (error) { + printk(KERN_ERR "hid-pidff: upload request failed\n"); + return error; + } + + if (pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] == + pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum + 1) { + pidff_autocenter(pidff, 0xffff); + set_bit(FF_AUTOCENTER, dev->ffbit); + } else { + printk(KERN_NOTICE "hid-pidff: " + "device has unknown autocenter control method\n"); + } + + pidff_erase_pid(pidff, + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]); + + return 0; + +} + +/* + * Check if the device is PID and initialize it + */ +int hid_pidff_init(struct hid_device *hid) +{ + struct pidff_device *pidff; + struct hid_input *hidinput = list_entry(hid->inputs.next, + struct hid_input, list); + struct input_dev *dev = hidinput->input; + struct ff_device *ff; + int max_effects; + int error; + + debug("starting pid init"); + + if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) { + debug("not a PID device, no output report"); + return -ENODEV; + } + + pidff = kzalloc(sizeof(*pidff), GFP_KERNEL); + if (!pidff) + return -ENOMEM; + + pidff->hid = hid; + + pidff_find_reports(hid, HID_OUTPUT_REPORT, pidff); + pidff_find_reports(hid, HID_FEATURE_REPORT, pidff); + + if (!pidff_reports_ok(pidff)) { + debug("reports not ok, aborting"); + error = -ENODEV; + goto fail; + } + + error = pidff_init_fields(pidff, dev); + if (error) + goto fail; + + pidff_reset(pidff); + + if (test_bit(FF_GAIN, dev->ffbit)) { + pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], 0xffff); + usbhid_submit_report(pidff->hid, pidff->reports[PID_DEVICE_GAIN], + USB_DIR_OUT); + } + + error = pidff_check_autocenter(pidff, dev); + if (error) + goto fail; + + max_effects = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_maximum - + pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum + + 1; + debug("max effects is %d", max_effects); + + if (max_effects > PID_EFFECTS_MAX) + max_effects = PID_EFFECTS_MAX; + + if (pidff->pool[PID_SIMULTANEOUS_MAX].value) + debug("max simultaneous effects is %d", + pidff->pool[PID_SIMULTANEOUS_MAX].value[0]); + + if (pidff->pool[PID_RAM_POOL_SIZE].value) + debug("device memory size is %d bytes", + pidff->pool[PID_RAM_POOL_SIZE].value[0]); + + if (pidff->pool[PID_DEVICE_MANAGED_POOL].value && + pidff->pool[PID_DEVICE_MANAGED_POOL].value[0] == 0) { + printk(KERN_NOTICE "hid-pidff: " + "device does not support device managed pool\n"); + goto fail; + } + + error = input_ff_create(dev, max_effects); + if (error) + goto fail; + + ff = dev->ff; + ff->private = pidff; + ff->upload = pidff_upload_effect; + ff->erase = pidff_erase_effect; + ff->set_gain = pidff_set_gain; + ff->set_autocenter = pidff_set_autocenter; + ff->playback = pidff_playback; + + printk(KERN_INFO "Force feedback for USB HID PID devices by " + "Anssi Hannula \n"); + + return 0; + + fail: + kfree(pidff); + return error; +} diff --git a/drivers/hid/usbhid/hid-plff.c b/drivers/hid/usbhid/hid-plff.c new file mode 100644 index 000000000000..76d2e6e14db4 --- /dev/null +++ b/drivers/hid/usbhid/hid-plff.c @@ -0,0 +1,129 @@ +/* + * Force feedback support for PantherLord USB/PS2 2in1 Adapter devices + * + * Copyright (c) 2007 Anssi Hannula + */ + +/* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* #define DEBUG */ + +#define debug(format, arg...) pr_debug("hid-plff: " format "\n" , ## arg) + +#include +#include +#include +#include "usbhid.h" + +struct plff_device { + struct hid_report *report; +}; + +static int hid_plff_play(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct hid_device *hid = dev->private; + struct plff_device *plff = data; + int left, right; + + left = effect->u.rumble.strong_magnitude; + right = effect->u.rumble.weak_magnitude; + debug("called with 0x%04x 0x%04x", left, right); + + left = left * 0x7f / 0xffff; + right = right * 0x7f / 0xffff; + + plff->report->field[0]->value[2] = left; + plff->report->field[0]->value[3] = right; + debug("running with 0x%02x 0x%02x", left, right); + usbhid_submit_report(hid, plff->report, USB_DIR_OUT); + + return 0; +} + +int hid_plff_init(struct hid_device *hid) +{ + struct plff_device *plff; + struct hid_report *report; + struct hid_input *hidinput; + struct list_head *report_list = + &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct list_head *report_ptr = report_list; + struct input_dev *dev; + int error; + + /* The device contains 2 output reports (one for each + HID_QUIRK_MULTI_INPUT device), both containing 1 field, which + contains 4 ff00.0002 usages and 4 16bit absolute values. + + The 2 input reports also contain a field which contains + 8 ff00.0001 usages and 8 boolean values. Their meaning is + currently unknown. */ + + if (list_empty(report_list)) { + printk(KERN_ERR "hid-plff: no output reports found\n"); + return -ENODEV; + } + + list_for_each_entry(hidinput, &hid->inputs, list) { + + report_ptr = report_ptr->next; + + if (report_ptr == report_list) { + printk(KERN_ERR "hid-plff: required output report is missing\n"); + return -ENODEV; + } + + report = list_entry(report_ptr, struct hid_report, list); + if (report->maxfield < 1) { + printk(KERN_ERR "hid-plff: no fields in the report\n"); + return -ENODEV; + } + + if (report->field[0]->report_count < 4) { + printk(KERN_ERR "hid-plff: not enough values in the field\n"); + return -ENODEV; + } + + plff = kzalloc(sizeof(struct plff_device), GFP_KERNEL); + if (!plff) + return -ENOMEM; + + dev = hidinput->input; + + set_bit(FF_RUMBLE, dev->ffbit); + + error = input_ff_create_memless(dev, plff, hid_plff_play); + if (error) { + kfree(plff); + return error; + } + + plff->report = report; + plff->report->field[0]->value[0] = 0x00; + plff->report->field[0]->value[1] = 0x00; + plff->report->field[0]->value[2] = 0x00; + plff->report->field[0]->value[3] = 0x00; + usbhid_submit_report(hid, plff->report, USB_DIR_OUT); + } + + printk(KERN_INFO "hid-plff: Force feedback for PantherLord USB/PS2 " + "2in1 Adapters by Anssi Hannula \n"); + + return 0; +} diff --git a/drivers/hid/usbhid/hid-tmff.c b/drivers/hid/usbhid/hid-tmff.c new file mode 100644 index 000000000000..ab67331620d0 --- /dev/null +++ b/drivers/hid/usbhid/hid-tmff.c @@ -0,0 +1,147 @@ +/* + * Force feedback support for various HID compliant devices by ThrustMaster: + * ThrustMaster FireStorm Dual Power 2 + * and possibly others whose device ids haven't been added. + * + * Modified to support ThrustMaster devices by Zinx Verituse + * on 2003-01-25 from the Logitech force feedback driver, + * which is by Johann Deneux. + * + * Copyright (c) 2003 Zinx Verituse + * Copyright (c) 2002 Johann Deneux + */ + +/* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#undef DEBUG +#include + +#include +#include "usbhid.h" + +/* Usages for thrustmaster devices I know about */ +#define THRUSTMASTER_USAGE_RUMBLE_LR (HID_UP_GENDESK | 0xbb) + + +struct tmff_device { + struct hid_report *report; + struct hid_field *rumble; +}; + +/* Changes values from 0 to 0xffff into values from minimum to maximum */ +static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum) +{ + int ret; + + ret = (in * (maximum - minimum) / 0xffff) + minimum; + if (ret < minimum) + return minimum; + if (ret > maximum) + return maximum; + return ret; +} + +static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect) +{ + struct hid_device *hid = dev->private; + struct tmff_device *tmff = data; + int left, right; /* Rumbling */ + + left = hid_tmff_scale(effect->u.rumble.weak_magnitude, + tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); + right = hid_tmff_scale(effect->u.rumble.strong_magnitude, + tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); + + tmff->rumble->value[0] = left; + tmff->rumble->value[1] = right; + dbg("(left,right)=(%08x, %08x)", left, right); + usbhid_submit_report(hid, tmff->report, USB_DIR_OUT); + + return 0; +} + +int hid_tmff_init(struct hid_device *hid) +{ + struct tmff_device *tmff; + struct list_head *pos; + struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); + struct input_dev *input_dev = hidinput->input; + int error; + + tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL); + if (!tmff) + return -ENOMEM; + + /* Find the report to use */ + __list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) { + struct hid_report *report = (struct hid_report *)pos; + int fieldnum; + + for (fieldnum = 0; fieldnum < report->maxfield; ++fieldnum) { + struct hid_field *field = report->field[fieldnum]; + + if (field->maxusage <= 0) + continue; + + switch (field->usage[0].hid) { + case THRUSTMASTER_USAGE_RUMBLE_LR: + if (field->report_count < 2) { + warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with report_count < 2"); + continue; + } + + if (field->logical_maximum == field->logical_minimum) { + warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with logical_maximum == logical_minimum"); + continue; + } + + if (tmff->report && tmff->report != report) { + warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR in other report"); + continue; + } + + if (tmff->rumble && tmff->rumble != field) { + warn("ignoring duplicate THRUSTMASTER_USAGE_RUMBLE_LR"); + continue; + } + + tmff->report = report; + tmff->rumble = field; + + set_bit(FF_RUMBLE, input_dev->ffbit); + break; + + default: + warn("ignoring unknown output usage %08x", field->usage[0].hid); + continue; + } + } + } + + error = input_ff_create_memless(input_dev, tmff, hid_tmff_play); + if (error) { + kfree(tmff); + return error; + } + + info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse "); + + return 0; +} + diff --git a/drivers/hid/usbhid/hid-zpff.c b/drivers/hid/usbhid/hid-zpff.c new file mode 100644 index 000000000000..7bd8238ca212 --- /dev/null +++ b/drivers/hid/usbhid/hid-zpff.c @@ -0,0 +1,111 @@ +/* + * Force feedback support for Zeroplus based devices + * + * Copyright (c) 2005, 2006 Anssi Hannula + */ + +/* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* #define DEBUG */ + +#define debug(format, arg...) pr_debug("hid-zpff: " format "\n" , ## arg) + +#include +#include +#include +#include "usbhid.h" + +struct zpff_device { + struct hid_report *report; +}; + +static int hid_zpff_play(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct hid_device *hid = dev->private; + struct zpff_device *zpff = data; + int left, right; + + /* + * The following is specified the other way around in the Zeroplus + * datasheet but the order below is correct for the XFX Executioner; + * however it is possible that the XFX Executioner is an exception + */ + + left = effect->u.rumble.strong_magnitude; + right = effect->u.rumble.weak_magnitude; + debug("called with 0x%04x 0x%04x", left, right); + + left = left * 0x7f / 0xffff; + right = right * 0x7f / 0xffff; + + zpff->report->field[2]->value[0] = left; + zpff->report->field[3]->value[0] = right; + debug("running with 0x%02x 0x%02x", left, right); + usbhid_submit_report(hid, zpff->report, USB_DIR_OUT); + + return 0; +} + +int hid_zpff_init(struct hid_device *hid) +{ + struct zpff_device *zpff; + struct hid_report *report; + struct hid_input *hidinput = list_entry(hid->inputs.next, + struct hid_input, list); + struct list_head *report_list = + &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct input_dev *dev = hidinput->input; + int error; + + if (list_empty(report_list)) { + printk(KERN_ERR "hid-zpff: no output report found\n"); + return -ENODEV; + } + + report = list_entry(report_list->next, struct hid_report, list); + + if (report->maxfield < 4) { + printk(KERN_ERR "hid-zpff: not enough fields in report\n"); + return -ENODEV; + } + + zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL); + if (!zpff) + return -ENOMEM; + + set_bit(FF_RUMBLE, dev->ffbit); + + error = input_ff_create_memless(dev, zpff, hid_zpff_play); + if (error) { + kfree(zpff); + return error; + } + + zpff->report = report; + zpff->report->field[0]->value[0] = 0x00; + zpff->report->field[1]->value[0] = 0x02; + zpff->report->field[2]->value[0] = 0x00; + zpff->report->field[3]->value[0] = 0x00; + usbhid_submit_report(hid, zpff->report, USB_DIR_OUT); + + printk(KERN_INFO "Force feedback for Zeroplus based devices by " + "Anssi Hannula \n"); + + return 0; +} diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c new file mode 100644 index 000000000000..a8b3d66cd498 --- /dev/null +++ b/drivers/hid/usbhid/hiddev.c @@ -0,0 +1,847 @@ +/* + * Copyright (c) 2001 Paul Stewart + * Copyright (c) 2001 Vojtech Pavlik + * + * HID char devices, giving access to raw HID device events. + * + */ + +/* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to Paul Stewart + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "usbhid.h" + +#ifdef CONFIG_USB_DYNAMIC_MINORS +#define HIDDEV_MINOR_BASE 0 +#define HIDDEV_MINORS 256 +#else +#define HIDDEV_MINOR_BASE 96 +#define HIDDEV_MINORS 16 +#endif +#define HIDDEV_BUFFER_SIZE 64 + +struct hiddev { + int exist; + int open; + wait_queue_head_t wait; + struct hid_device *hid; + struct list_head list; +}; + +struct hiddev_list { + struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE]; + int head; + int tail; + unsigned flags; + struct fasync_struct *fasync; + struct hiddev *hiddev; + struct list_head node; +}; + +static struct hiddev *hiddev_table[HIDDEV_MINORS]; + +/* + * Find a report, given the report's type and ID. The ID can be specified + * indirectly by REPORT_ID_FIRST (which returns the first report of the given + * type) or by (REPORT_ID_NEXT | old_id), which returns the next report of the + * given type which follows old_id. + */ +static struct hid_report * +hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo) +{ + unsigned int flags = rinfo->report_id & ~HID_REPORT_ID_MASK; + unsigned int rid = rinfo->report_id & HID_REPORT_ID_MASK; + struct hid_report_enum *report_enum; + struct hid_report *report; + struct list_head *list; + + if (rinfo->report_type < HID_REPORT_TYPE_MIN || + rinfo->report_type > HID_REPORT_TYPE_MAX) + return NULL; + + report_enum = hid->report_enum + + (rinfo->report_type - HID_REPORT_TYPE_MIN); + + switch (flags) { + case 0: /* Nothing to do -- report_id is already set correctly */ + break; + + case HID_REPORT_ID_FIRST: + if (list_empty(&report_enum->report_list)) + return NULL; + + list = report_enum->report_list.next; + report = list_entry(list, struct hid_report, list); + rinfo->report_id = report->id; + break; + + case HID_REPORT_ID_NEXT: + report = report_enum->report_id_hash[rid]; + if (!report) + return NULL; + + list = report->list.next; + if (list == &report_enum->report_list) + return NULL; + + report = list_entry(list, struct hid_report, list); + rinfo->report_id = report->id; + break; + + default: + return NULL; + } + + return report_enum->report_id_hash[rinfo->report_id]; +} + +/* + * Perform an exhaustive search of the report table for a usage, given its + * type and usage id. + */ +static struct hid_field * +hiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref) +{ + int i, j; + struct hid_report *report; + struct hid_report_enum *report_enum; + struct hid_field *field; + + if (uref->report_type < HID_REPORT_TYPE_MIN || + uref->report_type > HID_REPORT_TYPE_MAX) + return NULL; + + report_enum = hid->report_enum + + (uref->report_type - HID_REPORT_TYPE_MIN); + + list_for_each_entry(report, &report_enum->report_list, list) { + for (i = 0; i < report->maxfield; i++) { + field = report->field[i]; + for (j = 0; j < field->maxusage; j++) { + if (field->usage[j].hid == uref->usage_code) { + uref->report_id = report->id; + uref->field_index = i; + uref->usage_index = j; + return field; + } + } + } + } + + return NULL; +} + +static void hiddev_send_event(struct hid_device *hid, + struct hiddev_usage_ref *uref) +{ + struct hiddev *hiddev = hid->hiddev; + struct hiddev_list *list; + + list_for_each_entry(list, &hiddev->list, node) { + if (uref->field_index != HID_FIELD_INDEX_NONE || + (list->flags & HIDDEV_FLAG_REPORT) != 0) { + list->buffer[list->head] = *uref; + list->head = (list->head + 1) & + (HIDDEV_BUFFER_SIZE - 1); + kill_fasync(&list->fasync, SIGIO, POLL_IN); + } + } + + wake_up_interruptible(&hiddev->wait); +} + +/* + * This is where hid.c calls into hiddev to pass an event that occurred over + * the interrupt pipe + */ +void hiddev_hid_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + unsigned type = field->report_type; + struct hiddev_usage_ref uref; + + uref.report_type = + (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : + ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : + ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0)); + uref.report_id = field->report->id; + uref.field_index = field->index; + uref.usage_index = (usage - field->usage); + uref.usage_code = usage->hid; + uref.value = value; + + hiddev_send_event(hid, &uref); +} +EXPORT_SYMBOL_GPL(hiddev_hid_event); + +void hiddev_report_event(struct hid_device *hid, struct hid_report *report) +{ + unsigned type = report->type; + struct hiddev_usage_ref uref; + + memset(&uref, 0, sizeof(uref)); + uref.report_type = + (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : + ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : + ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0)); + uref.report_id = report->id; + uref.field_index = HID_FIELD_INDEX_NONE; + + hiddev_send_event(hid, &uref); +} + +/* + * fasync file op + */ +static int hiddev_fasync(int fd, struct file *file, int on) +{ + int retval; + struct hiddev_list *list = file->private_data; + + retval = fasync_helper(fd, file, on, &list->fasync); + + return retval < 0 ? retval : 0; +} + + +/* + * release file op + */ +static int hiddev_release(struct inode * inode, struct file * file) +{ + struct hiddev_list *list = file->private_data; + + hiddev_fasync(-1, file, 0); + list_del(&list->node); + + if (!--list->hiddev->open) { + if (list->hiddev->exist) + usbhid_close(list->hiddev->hid); + else + kfree(list->hiddev); + } + + kfree(list); + + return 0; +} + +/* + * open file op + */ +static int hiddev_open(struct inode *inode, struct file *file) +{ + struct hiddev_list *list; + + int i = iminor(inode) - HIDDEV_MINOR_BASE; + + if (i >= HIDDEV_MINORS || !hiddev_table[i]) + return -ENODEV; + + if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL))) + return -ENOMEM; + + list->hiddev = hiddev_table[i]; + list_add_tail(&list->node, &hiddev_table[i]->list); + file->private_data = list; + + if (!list->hiddev->open++) + if (list->hiddev->exist) + usbhid_open(hiddev_table[i]->hid); + + return 0; +} + +/* + * "write" file op + */ +static ssize_t hiddev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +/* + * "read" file op + */ +static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + struct hiddev_list *list = file->private_data; + int event_size; + int retval = 0; + + event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ? + sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event); + + if (count < event_size) + return 0; + + while (retval == 0) { + if (list->head == list->tail) { + add_wait_queue(&list->hiddev->wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + while (list->head == list->tail) { + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + if (!list->hiddev->exist) { + retval = -EIO; + break; + } + + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&list->hiddev->wait, &wait); + } + + if (retval) + return retval; + + + while (list->head != list->tail && + retval + event_size <= count) { + if ((list->flags & HIDDEV_FLAG_UREF) == 0) { + if (list->buffer[list->tail].field_index != + HID_FIELD_INDEX_NONE) { + struct hiddev_event event; + event.hid = list->buffer[list->tail].usage_code; + event.value = list->buffer[list->tail].value; + if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event))) + return -EFAULT; + retval += sizeof(struct hiddev_event); + } + } else { + if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE || + (list->flags & HIDDEV_FLAG_REPORT) != 0) { + if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref))) + return -EFAULT; + retval += sizeof(struct hiddev_usage_ref); + } + } + list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1); + } + + } + + return retval; +} + +/* + * "poll" file op + * No kernel lock - fine + */ +static unsigned int hiddev_poll(struct file *file, poll_table *wait) +{ + struct hiddev_list *list = file->private_data; + + poll_wait(file, &list->hiddev->wait, wait); + if (list->head != list->tail) + return POLLIN | POLLRDNORM; + if (!list->hiddev->exist) + return POLLERR | POLLHUP; + return 0; +} + +/* + * "ioctl" file op + */ +static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct hiddev_list *list = file->private_data; + struct hiddev *hiddev = list->hiddev; + struct hid_device *hid = hiddev->hid; + struct usb_device *dev = hid_to_usb_dev(hid); + struct hiddev_collection_info cinfo; + struct hiddev_report_info rinfo; + struct hiddev_field_info finfo; + struct hiddev_usage_ref_multi *uref_multi = NULL; + struct hiddev_usage_ref *uref; + struct hiddev_devinfo dinfo; + struct hid_report *report; + struct hid_field *field; + struct usbhid_device *usbhid = hid->driver_data; + void __user *user_arg = (void __user *)arg; + int i; + + if (!hiddev->exist) + return -EIO; + + switch (cmd) { + + case HIDIOCGVERSION: + return put_user(HID_VERSION, (int __user *)arg); + + case HIDIOCAPPLICATION: + if (arg < 0 || arg >= hid->maxapplication) + return -EINVAL; + + for (i = 0; i < hid->maxcollection; i++) + if (hid->collection[i].type == + HID_COLLECTION_APPLICATION && arg-- == 0) + break; + + if (i == hid->maxcollection) + return -EINVAL; + + return hid->collection[i].usage; + + case HIDIOCGDEVINFO: + dinfo.bustype = BUS_USB; + dinfo.busnum = dev->bus->busnum; + dinfo.devnum = dev->devnum; + dinfo.ifnum = usbhid->ifnum; + dinfo.vendor = le16_to_cpu(dev->descriptor.idVendor); + dinfo.product = le16_to_cpu(dev->descriptor.idProduct); + dinfo.version = le16_to_cpu(dev->descriptor.bcdDevice); + dinfo.num_applications = hid->maxapplication; + if (copy_to_user(user_arg, &dinfo, sizeof(dinfo))) + return -EFAULT; + + return 0; + + case HIDIOCGFLAG: + if (put_user(list->flags, (int __user *)arg)) + return -EFAULT; + + return 0; + + case HIDIOCSFLAG: + { + int newflags; + if (get_user(newflags, (int __user *)arg)) + return -EFAULT; + + if ((newflags & ~HIDDEV_FLAGS) != 0 || + ((newflags & HIDDEV_FLAG_REPORT) != 0 && + (newflags & HIDDEV_FLAG_UREF) == 0)) + return -EINVAL; + + list->flags = newflags; + + return 0; + } + + case HIDIOCGSTRING: + { + int idx, len; + char *buf; + + if (get_user(idx, (int __user *)arg)) + return -EFAULT; + + if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL) + return -ENOMEM; + + if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) { + kfree(buf); + return -EINVAL; + } + + if (copy_to_user(user_arg+sizeof(int), buf, len+1)) { + kfree(buf); + return -EFAULT; + } + + kfree(buf); + + return len; + } + + case HIDIOCINITREPORT: + usbhid_init_reports(hid); + + return 0; + + case HIDIOCGREPORT: + if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) + return -EFAULT; + + if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT) + return -EINVAL; + + if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) + return -EINVAL; + + usbhid_submit_report(hid, report, USB_DIR_IN); + usbhid_wait_io(hid); + + return 0; + + case HIDIOCSREPORT: + if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) + return -EFAULT; + + if (rinfo.report_type == HID_REPORT_TYPE_INPUT) + return -EINVAL; + + if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) + return -EINVAL; + + usbhid_submit_report(hid, report, USB_DIR_OUT); + usbhid_wait_io(hid); + + return 0; + + case HIDIOCGREPORTINFO: + if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) + return -EFAULT; + + if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) + return -EINVAL; + + rinfo.num_fields = report->maxfield; + + if (copy_to_user(user_arg, &rinfo, sizeof(rinfo))) + return -EFAULT; + + return 0; + + case HIDIOCGFIELDINFO: + if (copy_from_user(&finfo, user_arg, sizeof(finfo))) + return -EFAULT; + rinfo.report_type = finfo.report_type; + rinfo.report_id = finfo.report_id; + if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) + return -EINVAL; + + if (finfo.field_index >= report->maxfield) + return -EINVAL; + + field = report->field[finfo.field_index]; + memset(&finfo, 0, sizeof(finfo)); + finfo.report_type = rinfo.report_type; + finfo.report_id = rinfo.report_id; + finfo.field_index = field->report_count - 1; + finfo.maxusage = field->maxusage; + finfo.flags = field->flags; + finfo.physical = field->physical; + finfo.logical = field->logical; + finfo.application = field->application; + finfo.logical_minimum = field->logical_minimum; + finfo.logical_maximum = field->logical_maximum; + finfo.physical_minimum = field->physical_minimum; + finfo.physical_maximum = field->physical_maximum; + finfo.unit_exponent = field->unit_exponent; + finfo.unit = field->unit; + + if (copy_to_user(user_arg, &finfo, sizeof(finfo))) + return -EFAULT; + + return 0; + + case HIDIOCGUCODE: + uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL); + if (!uref_multi) + return -ENOMEM; + uref = &uref_multi->uref; + if (copy_from_user(uref, user_arg, sizeof(*uref))) + goto fault; + + rinfo.report_type = uref->report_type; + rinfo.report_id = uref->report_id; + if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) + goto inval; + + if (uref->field_index >= report->maxfield) + goto inval; + + field = report->field[uref->field_index]; + if (uref->usage_index >= field->maxusage) + goto inval; + + uref->usage_code = field->usage[uref->usage_index].hid; + + if (copy_to_user(user_arg, uref, sizeof(*uref))) + goto fault; + + kfree(uref_multi); + return 0; + + case HIDIOCGUSAGE: + case HIDIOCSUSAGE: + case HIDIOCGUSAGES: + case HIDIOCSUSAGES: + case HIDIOCGCOLLECTIONINDEX: + uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL); + if (!uref_multi) + return -ENOMEM; + uref = &uref_multi->uref; + if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) { + if (copy_from_user(uref_multi, user_arg, + sizeof(*uref_multi))) + goto fault; + } else { + if (copy_from_user(uref, user_arg, sizeof(*uref))) + goto fault; + } + + if (cmd != HIDIOCGUSAGE && + cmd != HIDIOCGUSAGES && + uref->report_type == HID_REPORT_TYPE_INPUT) + goto inval; + + if (uref->report_id == HID_REPORT_ID_UNKNOWN) { + field = hiddev_lookup_usage(hid, uref); + if (field == NULL) + goto inval; + } else { + rinfo.report_type = uref->report_type; + rinfo.report_id = uref->report_id; + if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) + goto inval; + + if (uref->field_index >= report->maxfield) + goto inval; + + field = report->field[uref->field_index]; + + if (cmd == HIDIOCGCOLLECTIONINDEX) { + if (uref->usage_index >= field->maxusage) + goto inval; + } else if (uref->usage_index >= field->report_count) + goto inval; + + else if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) && + (uref_multi->num_values > HID_MAX_MULTI_USAGES || + uref->usage_index + uref_multi->num_values > field->report_count)) + goto inval; + } + + switch (cmd) { + case HIDIOCGUSAGE: + uref->value = field->value[uref->usage_index]; + if (copy_to_user(user_arg, uref, sizeof(*uref))) + goto fault; + goto goodreturn; + + case HIDIOCSUSAGE: + field->value[uref->usage_index] = uref->value; + goto goodreturn; + + case HIDIOCGCOLLECTIONINDEX: + kfree(uref_multi); + return field->usage[uref->usage_index].collection_index; + case HIDIOCGUSAGES: + for (i = 0; i < uref_multi->num_values; i++) + uref_multi->values[i] = + field->value[uref->usage_index + i]; + if (copy_to_user(user_arg, uref_multi, + sizeof(*uref_multi))) + goto fault; + goto goodreturn; + case HIDIOCSUSAGES: + for (i = 0; i < uref_multi->num_values; i++) + field->value[uref->usage_index + i] = + uref_multi->values[i]; + goto goodreturn; + } + +goodreturn: + kfree(uref_multi); + return 0; +fault: + kfree(uref_multi); + return -EFAULT; +inval: + kfree(uref_multi); + return -EINVAL; + + case HIDIOCGCOLLECTIONINFO: + if (copy_from_user(&cinfo, user_arg, sizeof(cinfo))) + return -EFAULT; + + if (cinfo.index >= hid->maxcollection) + return -EINVAL; + + cinfo.type = hid->collection[cinfo.index].type; + cinfo.usage = hid->collection[cinfo.index].usage; + cinfo.level = hid->collection[cinfo.index].level; + + if (copy_to_user(user_arg, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + default: + + if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) + return -EINVAL; + + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) { + int len; + if (!hid->name) + return 0; + len = strlen(hid->name) + 1; + if (len > _IOC_SIZE(cmd)) + len = _IOC_SIZE(cmd); + return copy_to_user(user_arg, hid->name, len) ? + -EFAULT : len; + } + + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGPHYS(0))) { + int len; + if (!hid->phys) + return 0; + len = strlen(hid->phys) + 1; + if (len > _IOC_SIZE(cmd)) + len = _IOC_SIZE(cmd); + return copy_to_user(user_arg, hid->phys, len) ? + -EFAULT : len; + } + } + return -EINVAL; +} + +static const struct file_operations hiddev_fops = { + .owner = THIS_MODULE, + .read = hiddev_read, + .write = hiddev_write, + .poll = hiddev_poll, + .open = hiddev_open, + .release = hiddev_release, + .ioctl = hiddev_ioctl, + .fasync = hiddev_fasync, +}; + +static struct usb_class_driver hiddev_class = { + .name = "hiddev%d", + .fops = &hiddev_fops, + .minor_base = HIDDEV_MINOR_BASE, +}; + +/* + * This is where hid.c calls us to connect a hid device to the hiddev driver + */ +int hiddev_connect(struct hid_device *hid) +{ + struct hiddev *hiddev; + struct usbhid_device *usbhid = hid->driver_data; + int i; + int retval; + + for (i = 0; i < hid->maxcollection; i++) + if (hid->collection[i].type == + HID_COLLECTION_APPLICATION && + !IS_INPUT_APPLICATION(hid->collection[i].usage)) + break; + + if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDDEV) == 0) + return -1; + + if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL))) + return -1; + + retval = usb_register_dev(usbhid->intf, &hiddev_class); + if (retval) { + err("Not able to get a minor for this device."); + kfree(hiddev); + return -1; + } + + init_waitqueue_head(&hiddev->wait); + INIT_LIST_HEAD(&hiddev->list); + hiddev->hid = hid; + hiddev->exist = 1; + + hid->minor = usbhid->intf->minor; + hid->hiddev = hiddev; + + hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev; + + return 0; +} + +/* + * This is where hid.c calls us to disconnect a hiddev device from the + * corresponding hid device (usually because the usb device has disconnected) + */ +static struct usb_class_driver hiddev_class; +void hiddev_disconnect(struct hid_device *hid) +{ + struct hiddev *hiddev = hid->hiddev; + struct usbhid_device *usbhid = hid->driver_data; + + hiddev->exist = 0; + + hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL; + usb_deregister_dev(usbhid->intf, &hiddev_class); + + if (hiddev->open) { + usbhid_close(hiddev->hid); + wake_up_interruptible(&hiddev->wait); + } else { + kfree(hiddev); + } +} + +/* Currently this driver is a USB driver. It's not a conventional one in + * the sense that it doesn't probe at the USB level. Instead it waits to + * be connected by HID through the hiddev_connect / hiddev_disconnect + * routines. The reason to register as a USB device is to gain part of the + * minor number space from the USB major. + * + * In theory, should the HID code be generalized to more than one physical + * medium (say, IEEE 1384), this driver will probably need to register its + * own major number, and in doing so, no longer need to register with USB. + * At that point the probe routine and hiddev_driver struct below will no + * longer be useful. + */ + + +/* We never attach in this manner, and rely on HID to connect us. This + * is why there is no disconnect routine defined in the usb_driver either. + */ +static int hiddev_usbd_probe(struct usb_interface *intf, + const struct usb_device_id *hiddev_info) +{ + return -ENODEV; +} + + +static /* const */ struct usb_driver hiddev_driver = { + .name = "hiddev", + .probe = hiddev_usbd_probe, +}; + +int __init hiddev_init(void) +{ + return usb_register(&hiddev_driver); +} + +void hiddev_exit(void) +{ + usb_deregister(&hiddev_driver); +} diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h new file mode 100644 index 000000000000..0023f96d4294 --- /dev/null +++ b/drivers/hid/usbhid/usbhid.h @@ -0,0 +1,87 @@ +#ifndef __USBHID_H +#define __USBHID_H + +/* + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2001 Vojtech Pavlik + * Copyright (c) 2006 Jiri Kosina + */ + +/* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +/* API provided by hid-core.c for USB HID drivers */ +int usbhid_wait_io(struct hid_device* hid); +void usbhid_close(struct hid_device *hid); +int usbhid_open(struct hid_device *hid); +void usbhid_init_reports(struct hid_device *hid); +void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir); + +/* + * USB-specific HID struct, to be pointed to + * from struct hid_device->driver_data + */ + +struct usbhid_device { + struct hid_device *hid; /* pointer to corresponding HID dev */ + + struct usb_interface *intf; /* USB interface */ + int ifnum; /* USB interface number */ + + unsigned int bufsize; /* URB buffer size */ + + struct urb *urbin; /* Input URB */ + char *inbuf; /* Input buffer */ + dma_addr_t inbuf_dma; /* Input buffer dma */ + spinlock_t inlock; /* Input fifo spinlock */ + + struct urb *urbctrl; /* Control URB */ + struct usb_ctrlrequest *cr; /* Control request struct */ + dma_addr_t cr_dma; /* Control request struct dma */ + struct hid_control_fifo ctrl[HID_CONTROL_FIFO_SIZE]; /* Control fifo */ + unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */ + char *ctrlbuf; /* Control buffer */ + dma_addr_t ctrlbuf_dma; /* Control buffer dma */ + spinlock_t ctrllock; /* Control fifo spinlock */ + + struct urb *urbout; /* Output URB */ + struct hid_report *out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */ + unsigned char outhead, outtail; /* Output pipe fifo head & tail */ + char *outbuf; /* Output buffer */ + dma_addr_t outbuf_dma; /* Output buffer dma */ + spinlock_t outlock; /* Output fifo spinlock */ + + unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */ + struct timer_list io_retry; /* Retry timer */ + unsigned long stop_retry; /* Time to give up, in jiffies */ + unsigned int retry_delay; /* Delay length in ms */ + struct work_struct reset_work; /* Task context for resets */ + +}; + +#define hid_to_usb_dev(hid_dev) \ + container_of(hid_dev->dev->parent, struct usb_device, dev) + +#endif + diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c new file mode 100644 index 000000000000..3749f4a235f9 --- /dev/null +++ b/drivers/hid/usbhid/usbkbd.c @@ -0,0 +1,362 @@ +/* + * $Id: usbkbd.c,v 1.27 2001/12/27 10:37:41 vojtech Exp $ + * + * Copyright (c) 1999-2001 Vojtech Pavlik + * + * USB HIDBP Keyboard support + */ + +/* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +/* + * Version Information + */ +#define DRIVER_VERSION "" +#define DRIVER_AUTHOR "Vojtech Pavlik " +#define DRIVER_DESC "USB HID Boot Protocol keyboard driver" +#define DRIVER_LICENSE "GPL" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); + +static unsigned char usb_kbd_keycode[256] = { + 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, + 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, + 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106, + 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, + 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190, + 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113, + 115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0, + 122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113, + 150,158,159,128,136,177,178,176,142,152,173,140 +}; + +struct usb_kbd { + struct input_dev *dev; + struct usb_device *usbdev; + unsigned char old[8]; + struct urb *irq, *led; + unsigned char newleds; + char name[128]; + char phys[64]; + + unsigned char *new; + struct usb_ctrlrequest *cr; + unsigned char *leds; + dma_addr_t cr_dma; + dma_addr_t new_dma; + dma_addr_t leds_dma; +}; + +static void usb_kbd_irq(struct urb *urb) +{ + struct usb_kbd *kbd = urb->context; + int i; + + switch (urb->status) { + case 0: /* success */ + break; + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: + return; + /* -EPIPE: should clear the halt */ + default: /* error */ + goto resubmit; + } + + for (i = 0; i < 8; i++) + input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1); + + for (i = 2; i < 8; i++) { + + if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) { + if (usb_kbd_keycode[kbd->old[i]]) + input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0); + else + info("Unknown key (scancode %#x) released.", kbd->old[i]); + } + + if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) { + if (usb_kbd_keycode[kbd->new[i]]) + input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1); + else + info("Unknown key (scancode %#x) pressed.", kbd->new[i]); + } + } + + input_sync(kbd->dev); + + memcpy(kbd->old, kbd->new, 8); + +resubmit: + i = usb_submit_urb (urb, GFP_ATOMIC); + if (i) + err ("can't resubmit intr, %s-%s/input0, status %d", + kbd->usbdev->bus->bus_name, + kbd->usbdev->devpath, i); +} + +static int usb_kbd_event(struct input_dev *dev, unsigned int type, + unsigned int code, int value) +{ + struct usb_kbd *kbd = dev->private; + + if (type != EV_LED) + return -1; + + + kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) | + (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) | + (!!test_bit(LED_NUML, dev->led)); + + if (kbd->led->status == -EINPROGRESS) + return 0; + + if (*(kbd->leds) == kbd->newleds) + return 0; + + *(kbd->leds) = kbd->newleds; + kbd->led->dev = kbd->usbdev; + if (usb_submit_urb(kbd->led, GFP_ATOMIC)) + err("usb_submit_urb(leds) failed"); + + return 0; +} + +static void usb_kbd_led(struct urb *urb) +{ + struct usb_kbd *kbd = urb->context; + + if (urb->status) + warn("led urb status %d received", urb->status); + + if (*(kbd->leds) == kbd->newleds) + return; + + *(kbd->leds) = kbd->newleds; + kbd->led->dev = kbd->usbdev; + if (usb_submit_urb(kbd->led, GFP_ATOMIC)) + err("usb_submit_urb(leds) failed"); +} + +static int usb_kbd_open(struct input_dev *dev) +{ + struct usb_kbd *kbd = dev->private; + + kbd->irq->dev = kbd->usbdev; + if (usb_submit_urb(kbd->irq, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void usb_kbd_close(struct input_dev *dev) +{ + struct usb_kbd *kbd = dev->private; + + usb_kill_urb(kbd->irq); +} + +static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd) +{ + if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL))) + return -1; + if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL))) + return -1; + if (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma))) + return -1; + if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma))) + return -1; + if (!(kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma))) + return -1; + + return 0; +} + +static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd) +{ + usb_free_urb(kbd->irq); + usb_free_urb(kbd->led); + if (kbd->new) + usb_buffer_free(dev, 8, kbd->new, kbd->new_dma); + if (kbd->cr) + usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma); + if (kbd->leds) + usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma); +} + +static int usb_kbd_probe(struct usb_interface *iface, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(iface); + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_kbd *kbd; + struct input_dev *input_dev; + int i, pipe, maxp; + + interface = iface->cur_altsetting; + + if (interface->desc.bNumEndpoints != 1) + return -ENODEV; + + endpoint = &interface->endpoint[0].desc; + if (!usb_endpoint_is_int_in(endpoint)) + return -ENODEV; + + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!kbd || !input_dev) + goto fail1; + + if (usb_kbd_alloc_mem(dev, kbd)) + goto fail2; + + kbd->usbdev = dev; + kbd->dev = input_dev; + + if (dev->manufacturer) + strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name)); + + if (dev->product) { + if (dev->manufacturer) + strlcat(kbd->name, " ", sizeof(kbd->name)); + strlcat(kbd->name, dev->product, sizeof(kbd->name)); + } + + if (!strlen(kbd->name)) + snprintf(kbd->name, sizeof(kbd->name), + "USB HIDBP Keyboard %04x:%04x", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + usb_make_path(dev, kbd->phys, sizeof(kbd->phys)); + strlcpy(kbd->phys, "/input0", sizeof(kbd->phys)); + + input_dev->name = kbd->name; + input_dev->phys = kbd->phys; + usb_to_input_id(dev, &input_dev->id); + input_dev->cdev.dev = &iface->dev; + input_dev->private = kbd; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP); + input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA); + + for (i = 0; i < 255; i++) + set_bit(usb_kbd_keycode[i], input_dev->keybit); + clear_bit(0, input_dev->keybit); + + input_dev->event = usb_kbd_event; + input_dev->open = usb_kbd_open; + input_dev->close = usb_kbd_close; + + usb_fill_int_urb(kbd->irq, dev, pipe, + kbd->new, (maxp > 8 ? 8 : maxp), + usb_kbd_irq, kbd, endpoint->bInterval); + kbd->irq->transfer_dma = kbd->new_dma; + kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE; + kbd->cr->bRequest = 0x09; + kbd->cr->wValue = cpu_to_le16(0x200); + kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber); + kbd->cr->wLength = cpu_to_le16(1); + + usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0), + (void *) kbd->cr, kbd->leds, 1, + usb_kbd_led, kbd); + kbd->led->setup_dma = kbd->cr_dma; + kbd->led->transfer_dma = kbd->leds_dma; + kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP); + + input_register_device(kbd->dev); + + usb_set_intfdata(iface, kbd); + return 0; + +fail2: usb_kbd_free_mem(dev, kbd); +fail1: input_free_device(input_dev); + kfree(kbd); + return -ENOMEM; +} + +static void usb_kbd_disconnect(struct usb_interface *intf) +{ + struct usb_kbd *kbd = usb_get_intfdata (intf); + + usb_set_intfdata(intf, NULL); + if (kbd) { + usb_kill_urb(kbd->irq); + input_unregister_device(kbd->dev); + usb_kbd_free_mem(interface_to_usbdev(intf), kbd); + kfree(kbd); + } +} + +static struct usb_device_id usb_kbd_id_table [] = { + { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, + USB_INTERFACE_PROTOCOL_KEYBOARD) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, usb_kbd_id_table); + +static struct usb_driver usb_kbd_driver = { + .name = "usbkbd", + .probe = usb_kbd_probe, + .disconnect = usb_kbd_disconnect, + .id_table = usb_kbd_id_table, +}; + +static int __init usb_kbd_init(void) +{ + int result = usb_register(&usb_kbd_driver); + if (result == 0) + info(DRIVER_VERSION ":" DRIVER_DESC); + return result; +} + +static void __exit usb_kbd_exit(void) +{ + usb_deregister(&usb_kbd_driver); +} + +module_init(usb_kbd_init); +module_exit(usb_kbd_exit); diff --git a/drivers/hid/usbhid/usbmouse.c b/drivers/hid/usbhid/usbmouse.c new file mode 100644 index 000000000000..692fd6087779 --- /dev/null +++ b/drivers/hid/usbhid/usbmouse.c @@ -0,0 +1,245 @@ +/* + * $Id: usbmouse.c,v 1.15 2001/12/27 10:37:41 vojtech Exp $ + * + * Copyright (c) 1999-2001 Vojtech Pavlik + * + * USB HIDBP Mouse support + */ + +/* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +/* + * Version Information + */ +#define DRIVER_VERSION "v1.6" +#define DRIVER_AUTHOR "Vojtech Pavlik " +#define DRIVER_DESC "USB HID Boot Protocol mouse driver" +#define DRIVER_LICENSE "GPL" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); + +struct usb_mouse { + char name[128]; + char phys[64]; + struct usb_device *usbdev; + struct input_dev *dev; + struct urb *irq; + + signed char *data; + dma_addr_t data_dma; +}; + +static void usb_mouse_irq(struct urb *urb) +{ + struct usb_mouse *mouse = urb->context; + signed char *data = mouse->data; + struct input_dev *dev = mouse->dev; + int status; + + switch (urb->status) { + case 0: /* success */ + break; + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: + return; + /* -EPIPE: should clear the halt */ + default: /* error */ + goto resubmit; + } + + input_report_key(dev, BTN_LEFT, data[0] & 0x01); + input_report_key(dev, BTN_RIGHT, data[0] & 0x02); + input_report_key(dev, BTN_MIDDLE, data[0] & 0x04); + input_report_key(dev, BTN_SIDE, data[0] & 0x08); + input_report_key(dev, BTN_EXTRA, data[0] & 0x10); + + input_report_rel(dev, REL_X, data[1]); + input_report_rel(dev, REL_Y, data[2]); + input_report_rel(dev, REL_WHEEL, data[3]); + + input_sync(dev); +resubmit: + status = usb_submit_urb (urb, GFP_ATOMIC); + if (status) + err ("can't resubmit intr, %s-%s/input0, status %d", + mouse->usbdev->bus->bus_name, + mouse->usbdev->devpath, status); +} + +static int usb_mouse_open(struct input_dev *dev) +{ + struct usb_mouse *mouse = dev->private; + + mouse->irq->dev = mouse->usbdev; + if (usb_submit_urb(mouse->irq, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void usb_mouse_close(struct input_dev *dev) +{ + struct usb_mouse *mouse = dev->private; + + usb_kill_urb(mouse->irq); +} + +static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_mouse *mouse; + struct input_dev *input_dev; + int pipe, maxp; + + interface = intf->cur_altsetting; + + if (interface->desc.bNumEndpoints != 1) + return -ENODEV; + + endpoint = &interface->endpoint[0].desc; + if (!usb_endpoint_is_int_in(endpoint)) + return -ENODEV; + + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!mouse || !input_dev) + goto fail1; + + mouse->data = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &mouse->data_dma); + if (!mouse->data) + goto fail1; + + mouse->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!mouse->irq) + goto fail2; + + mouse->usbdev = dev; + mouse->dev = input_dev; + + if (dev->manufacturer) + strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name)); + + if (dev->product) { + if (dev->manufacturer) + strlcat(mouse->name, " ", sizeof(mouse->name)); + strlcat(mouse->name, dev->product, sizeof(mouse->name)); + } + + if (!strlen(mouse->name)) + snprintf(mouse->name, sizeof(mouse->name), + "USB HIDBP Mouse %04x:%04x", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + usb_make_path(dev, mouse->phys, sizeof(mouse->phys)); + strlcat(mouse->phys, "/input0", sizeof(mouse->phys)); + + input_dev->name = mouse->name; + input_dev->phys = mouse->phys; + usb_to_input_id(dev, &input_dev->id); + input_dev->cdev.dev = &intf->dev; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); + input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); + input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y); + input_dev->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA); + input_dev->relbit[0] |= BIT(REL_WHEEL); + + input_dev->private = mouse; + input_dev->open = usb_mouse_open; + input_dev->close = usb_mouse_close; + + usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data, + (maxp > 8 ? 8 : maxp), + usb_mouse_irq, mouse, endpoint->bInterval); + mouse->irq->transfer_dma = mouse->data_dma; + mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + input_register_device(mouse->dev); + + usb_set_intfdata(intf, mouse); + return 0; + +fail2: usb_buffer_free(dev, 8, mouse->data, mouse->data_dma); +fail1: input_free_device(input_dev); + kfree(mouse); + return -ENOMEM; +} + +static void usb_mouse_disconnect(struct usb_interface *intf) +{ + struct usb_mouse *mouse = usb_get_intfdata (intf); + + usb_set_intfdata(intf, NULL); + if (mouse) { + usb_kill_urb(mouse->irq); + input_unregister_device(mouse->dev); + usb_free_urb(mouse->irq); + usb_buffer_free(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma); + kfree(mouse); + } +} + +static struct usb_device_id usb_mouse_id_table [] = { + { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, + USB_INTERFACE_PROTOCOL_MOUSE) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, usb_mouse_id_table); + +static struct usb_driver usb_mouse_driver = { + .name = "usbmouse", + .probe = usb_mouse_probe, + .disconnect = usb_mouse_disconnect, + .id_table = usb_mouse_id_table, +}; + +static int __init usb_mouse_init(void) +{ + int retval = usb_register(&usb_mouse_driver); + if (retval == 0) + info(DRIVER_VERSION ":" DRIVER_DESC); + return retval; +} + +static void __exit usb_mouse_exit(void) +{ + usb_deregister(&usb_mouse_driver); +} + +module_init(usb_mouse_init); +module_exit(usb_mouse_exit); diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 8b7ff467d262..f972fcc798b6 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -27,10 +27,7 @@ obj-$(CONFIG_USB) += storage/ obj-$(CONFIG_USB_ACECAD) += input/ obj-$(CONFIG_USB_AIPTEK) += input/ obj-$(CONFIG_USB_ATI_REMOTE) += input/ -obj-$(CONFIG_USB_HID) += input/ -obj-$(CONFIG_USB_KBD) += input/ obj-$(CONFIG_USB_KBTAB) += input/ -obj-$(CONFIG_USB_MOUSE) += input/ obj-$(CONFIG_USB_MTOUCH) += input/ obj-$(CONFIG_USB_POWERMATE) += input/ obj-$(CONFIG_USB_WACOM) += input/ diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig index 69a9f3b6d0a9..a792e42f58af 100644 --- a/drivers/usb/input/Kconfig +++ b/drivers/usb/input/Kconfig @@ -4,151 +4,6 @@ comment "USB Input Devices" depends on USB -config USB_HID - tristate "USB Human Interface Device (full HID) support" - default y - depends on USB && INPUT - select HID - ---help--- - Say Y here if you want full HID support to connect USB keyboards, - mice, joysticks, graphic tablets, or any other HID based devices - to your computer via USB, as well as Uninterruptible Power Supply - (UPS) and monitor control devices. - - You can't use this driver and the HIDBP (Boot Protocol) keyboard - and mouse drivers at the same time. More information is available: - . - - If unsure, say Y. - - To compile this driver as a module, choose M here: the - module will be called usbhid. - -comment "Input core support is needed for USB HID input layer or HIDBP support" - depends on USB_HID && INPUT=n - -config USB_HIDINPUT_POWERBOOK - bool "Enable support for iBook/PowerBook special keys" - default n - depends on USB_HID - help - Say Y here if you want support for the special keys (Fn, Numlock) on - Apple iBooks and PowerBooks. - - If unsure, say N. - -config HID_FF - bool "Force feedback support (EXPERIMENTAL)" - depends on USB_HID && EXPERIMENTAL - help - Say Y here is you want force feedback support for a few HID devices. - See below for a list of supported devices. - - See for a description of the force - feedback API. - - If unsure, say N. - -config HID_PID - bool "PID device support" - depends on HID_FF - help - Say Y here if you have a PID-compliant device and wish to enable force - feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such - devices. - -config LOGITECH_FF - bool "Logitech devices support" - depends on HID_FF - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have one of these devices: - - Logitech WingMan Cordless RumblePad - - Logitech WingMan Cordless RumblePad 2 - - Logitech WingMan Force 3D - - Logitech Formula Force EX - - Logitech MOMO Force wheel - - and if you want to enable force feedback for them. - Note: if you say N here, this device will still be supported, but without - force feedback. - -config PANTHERLORD_FF - bool "PantherLord USB/PS2 2in1 Adapter support" - depends on HID_FF - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have a PantherLord USB/PS2 2in1 Adapter and want - to enable force feedback support for it. - -config THRUSTMASTER_FF - bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)" - depends on HID_FF && EXPERIMENTAL - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have a THRUSTMASTER FireStore Dual Power 2, - and want to enable force feedback support for it. - Note: if you say N here, this device will still be supported, but without - force feedback. - -config ZEROPLUS_FF - bool "Zeroplus based game controller support" - depends on HID_FF - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have a Zeroplus based game controller and want to - enable force feedback for it. - -config USB_HIDDEV - bool "/dev/hiddev raw HID device support" - depends on USB_HID - help - Say Y here if you want to support HID devices (from the USB - specification standpoint) that aren't strictly user interface - devices, like monitor controls and Uninterruptable Power Supplies. - - This module supports these devices separately using a separate - event interface on /dev/usb/hiddevX (char 180:96 to 180:111). - - If unsure, say Y. - -menu "USB HID Boot Protocol drivers" - depends on USB!=n && USB_HID!=y - -config USB_KBD - tristate "USB HIDBP Keyboard (simple Boot) support" - depends on USB && INPUT - ---help--- - Say Y here only if you are absolutely sure that you don't want - to use the generic HID driver for your USB keyboard and prefer - to use the keyboard in its limited Boot Protocol mode instead. - - This is almost certainly not what you want. This is mostly - useful for embedded applications or simple keyboards. - - To compile this driver as a module, choose M here: the - module will be called usbkbd. - - If even remotely unsure, say N. - -config USB_MOUSE - tristate "USB HIDBP Mouse (simple Boot) support" - depends on USB && INPUT - ---help--- - Say Y here only if you are absolutely sure that you don't want - to use the generic HID driver for your USB mouse and prefer - to use the mouse in its limited Boot Protocol mode instead. - - This is almost certainly not what you want. This is mostly - useful for embedded applications or simple mice. - - To compile this driver as a module, choose M here: the - module will be called usbmouse. - - If even remotely unsure, say N. - -endmenu - config USB_AIPTEK tristate "Aiptek 6000U/8000U tablet support" depends on USB && INPUT diff --git a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile index a9d206c945e9..9bf420eef77f 100644 --- a/drivers/usb/input/Makefile +++ b/drivers/usb/input/Makefile @@ -4,40 +4,12 @@ # Multipart objects. wacom-objs := wacom_wac.o wacom_sys.o -usbhid-objs := hid-core.o - -# Optional parts of multipart objects. - -ifeq ($(CONFIG_USB_HIDDEV),y) - usbhid-objs += hiddev.o -endif -ifeq ($(CONFIG_HID_PID),y) - usbhid-objs += hid-pidff.o -endif -ifeq ($(CONFIG_LOGITECH_FF),y) - usbhid-objs += hid-lgff.o -endif -ifeq ($(CONFIG_PANTHERLORD_FF),y) - usbhid-objs += hid-plff.o -endif -ifeq ($(CONFIG_THRUSTMASTER_FF),y) - usbhid-objs += hid-tmff.o -endif -ifeq ($(CONFIG_ZEROPLUS_FF),y) - usbhid-objs += hid-zpff.o -endif -ifeq ($(CONFIG_HID_FF),y) - usbhid-objs += hid-ff.o -endif obj-$(CONFIG_USB_AIPTEK) += aiptek.o obj-$(CONFIG_USB_ATI_REMOTE) += ati_remote.o obj-$(CONFIG_USB_ATI_REMOTE2) += ati_remote2.o -obj-$(CONFIG_USB_HID) += usbhid.o -obj-$(CONFIG_USB_KBD) += usbkbd.o obj-$(CONFIG_USB_KBTAB) += kbtab.o obj-$(CONFIG_USB_KEYSPAN_REMOTE) += keyspan_remote.o -obj-$(CONFIG_USB_MOUSE) += usbmouse.o obj-$(CONFIG_USB_MTOUCH) += mtouchusb.o obj-$(CONFIG_USB_ITMTOUCH) += itmtouch.o obj-$(CONFIG_USB_EGALAX) += touchkitusb.o diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c deleted file mode 100644 index 827a75a186ba..000000000000 --- a/drivers/usb/input/hid-core.c +++ /dev/null @@ -1,1477 +0,0 @@ -/* - * USB HID support for Linux - * - * Copyright (c) 1999 Andreas Gal - * Copyright (c) 2000-2005 Vojtech Pavlik - * Copyright (c) 2005 Michael Haboustak for Concept2, Inc - * Copyright (c) 2006-2007 Jiri Kosina - */ - -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include "usbhid.h" - -/* - * Version Information - */ - -#define DRIVER_VERSION "v2.6" -#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"); - -/* - * 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; - struct usbhid_device *usbhid = hid->driver_data; - - 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); - if (rc != 0) - clear_bit(HID_IN_RUNNING, &usbhid->iofl); - } - spin_unlock_irqrestore(&usbhid->inlock, flags); - return rc; -} - -/* I/O retry timer routine */ -static void hid_retry_timeout(unsigned long _hid) -{ - struct hid_device *hid = (struct hid_device *) _hid; - struct usbhid_device *usbhid = hid->driver_data; - - dev_dbg(&usbhid->intf->dev, "retrying intr urb\n"); - if (hid_start_in(hid)) - hid_io_error(hid); -} - -/* Workqueue routine to reset the device or clear a halt */ -static void hid_reset(struct work_struct *work) -{ - struct usbhid_device *usbhid = - container_of(work, struct usbhid_device, reset_work); - struct hid_device *hid = usbhid->hid; - int rc_lock, rc = 0; - - if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) { - dev_dbg(&usbhid->intf->dev, "clear halt\n"); - rc = usb_clear_halt(hid_to_usb_dev(hid), usbhid->urbin->pipe); - clear_bit(HID_CLEAR_HALT, &usbhid->iofl); - hid_start_in(hid); - } - - 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(hid_to_usb_dev(hid), usbhid->intf); - if (rc_lock >= 0) { - rc = usb_reset_composite_device(hid_to_usb_dev(hid), usbhid->intf); - if (rc_lock) - usb_unlock_device(hid_to_usb_dev(hid)); - } - clear_bit(HID_RESET_PENDING, &usbhid->iofl); - } - - switch (rc) { - case 0: - if (!test_bit(HID_IN_RUNNING, &usbhid->iofl)) - hid_io_error(hid); - break; - default: - err("can't reset device, %s-%s/input%d, status %d", - hid_to_usb_dev(hid)->bus->bus_name, - hid_to_usb_dev(hid)->devpath, - usbhid->ifnum, rc); - /* FALLTHROUGH */ - case -EHOSTUNREACH: - case -ENODEV: - case -EINTR: - break; - } -} - -/* Main I/O error handler */ -static void hid_io_error(struct hid_device *hid) -{ - unsigned long flags; - struct usbhid_device *usbhid = hid->driver_data; - - spin_lock_irqsave(&usbhid->inlock, flags); - - /* Stop when disconnected */ - if (usb_get_intfdata(usbhid->intf) == NULL) - goto done; - - /* When an error occurs, retry at increasing intervals */ - 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; - - if (time_after(jiffies, usbhid->stop_retry)) { - - /* Retries failed, so do a port reset */ - if (!test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) { - schedule_work(&usbhid->reset_work); - goto done; - } - } - - mod_timer(&usbhid->io_retry, - jiffies + msecs_to_jiffies(usbhid->retry_delay)); -done: - spin_unlock_irqrestore(&usbhid->inlock, flags); -} - -/* - * Input interrupt completion handler. - */ - -static void hid_irq_in(struct urb *urb) -{ - struct hid_device *hid = urb->context; - struct usbhid_device *usbhid = hid->driver_data; - int status; - - switch (urb->status) { - case 0: /* success */ - usbhid->retry_delay = 0; - hid_input_report(urb->context, HID_INPUT_REPORT, - urb->transfer_buffer, - urb->actual_length, 1); - break; - case -EPIPE: /* stall */ - clear_bit(HID_IN_RUNNING, &usbhid->iofl); - set_bit(HID_CLEAR_HALT, &usbhid->iofl); - schedule_work(&usbhid->reset_work); - return; - case -ECONNRESET: /* unlink */ - case -ENOENT: - case -ESHUTDOWN: /* unplug */ - clear_bit(HID_IN_RUNNING, &usbhid->iofl); - return; - case -EILSEQ: /* protocol error or unplug */ - case -EPROTO: /* protocol error or unplug */ - case -ETIME: /* protocol error or unplug */ - case -ETIMEDOUT: /* Should never happen, but... */ - clear_bit(HID_IN_RUNNING, &usbhid->iofl); - hid_io_error(hid); - return; - default: /* error */ - warn("input irq status %d received", urb->status); - } - - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) { - clear_bit(HID_IN_RUNNING, &usbhid->iofl); - if (status != -EPERM) { - err("can't resubmit intr, %s-%s/input%d, status %d", - hid_to_usb_dev(hid)->bus->bus_name, - hid_to_usb_dev(hid)->devpath, - usbhid->ifnum, status); - hid_io_error(hid); - } - } -} - -static int hid_submit_out(struct hid_device *hid) -{ - struct hid_report *report; - struct usbhid_device *usbhid = hid->driver_data; - - report = usbhid->out[usbhid->outtail]; - - hid_output_report(report, usbhid->outbuf); - usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0); - usbhid->urbout->dev = hid_to_usb_dev(hid); - - dbg("submitting out urb"); - - if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) { - 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; - struct usbhid_device *usbhid = hid->driver_data; - - report = usbhid->ctrl[usbhid->ctrltail].report; - dir = usbhid->ctrl[usbhid->ctrltail].dir; - - len = ((report->size - 1) >> 3) + 1 + (report->id > 0); - if (dir == USB_DIR_OUT) { - hid_output_report(report, usbhid->ctrlbuf); - usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0); - usbhid->urbctrl->transfer_buffer_length = len; - } else { - int maxpacket, padlen; - - usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0); - maxpacket = usb_maxpacket(hid_to_usb_dev(hid), usbhid->urbctrl->pipe, 0); - if (maxpacket > 0) { - padlen = (len + maxpacket - 1) / maxpacket; - padlen *= maxpacket; - if (padlen > usbhid->bufsize) - padlen = usbhid->bufsize; - } else - padlen = 0; - usbhid->urbctrl->transfer_buffer_length = padlen; - } - usbhid->urbctrl->dev = hid_to_usb_dev(hid); - - 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); - - dbg("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u", - usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report", - usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength); - - if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) { - err("usb_submit_urb(ctrl) failed"); - return -1; - } - - return 0; -} - -/* - * Output interrupt completion handler. - */ - -static void hid_irq_out(struct urb *urb) -{ - struct hid_device *hid = urb->context; - struct usbhid_device *usbhid = hid->driver_data; - unsigned long flags; - int unplug = 0; - - switch (urb->status) { - case 0: /* success */ - break; - case -ESHUTDOWN: /* unplug */ - unplug = 1; - case -EILSEQ: /* protocol error or unplug */ - case -EPROTO: /* protocol error or unplug */ - case -ECONNRESET: /* unlink */ - case -ENOENT: - break; - default: /* error */ - warn("output irq status %d received", urb->status); - } - - spin_lock_irqsave(&usbhid->outlock, flags); - - if (unplug) - usbhid->outtail = usbhid->outhead; - else - usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1); - - if (usbhid->outhead != usbhid->outtail) { - if (hid_submit_out(hid)) { - clear_bit(HID_OUT_RUNNING, &usbhid->iofl); - wake_up(&hid->wait); - } - spin_unlock_irqrestore(&usbhid->outlock, flags); - return; - } - - clear_bit(HID_OUT_RUNNING, &usbhid->iofl); - spin_unlock_irqrestore(&usbhid->outlock, flags); - wake_up(&hid->wait); -} - -/* - * Control pipe completion handler. - */ - -static void hid_ctrl(struct urb *urb) -{ - struct hid_device *hid = urb->context; - struct usbhid_device *usbhid = hid->driver_data; - unsigned long flags; - int unplug = 0; - - spin_lock_irqsave(&usbhid->ctrllock, flags); - - switch (urb->status) { - case 0: /* success */ - if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN) - hid_input_report(urb->context, usbhid->ctrl[usbhid->ctrltail].report->type, - urb->transfer_buffer, urb->actual_length, 0); - break; - case -ESHUTDOWN: /* unplug */ - unplug = 1; - case -EILSEQ: /* protocol error or unplug */ - case -EPROTO: /* protocol error or unplug */ - case -ECONNRESET: /* unlink */ - case -ENOENT: - case -EPIPE: /* report not available */ - break; - default: /* error */ - warn("ctrl urb status %d received", urb->status); - } - - if (unplug) - usbhid->ctrltail = usbhid->ctrlhead; - else - usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1); - - if (usbhid->ctrlhead != usbhid->ctrltail) { - if (hid_submit_ctrl(hid)) { - clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); - wake_up(&hid->wait); - } - spin_unlock_irqrestore(&usbhid->ctrllock, flags); - return; - } - - clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); - spin_unlock_irqrestore(&usbhid->ctrllock, flags); - wake_up(&hid->wait); -} - -void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) -{ - int head; - unsigned long flags; - struct usbhid_device *usbhid = hid->driver_data; - - if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN) - return; - - if (usbhid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) { - - spin_lock_irqsave(&usbhid->outlock, flags); - - if ((head = (usbhid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == usbhid->outtail) { - spin_unlock_irqrestore(&usbhid->outlock, flags); - warn("output queue full"); - return; - } - - usbhid->out[usbhid->outhead] = report; - usbhid->outhead = head; - - if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) - if (hid_submit_out(hid)) - clear_bit(HID_OUT_RUNNING, &usbhid->iofl); - - spin_unlock_irqrestore(&usbhid->outlock, flags); - return; - } - - spin_lock_irqsave(&usbhid->ctrllock, flags); - - if ((head = (usbhid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == usbhid->ctrltail) { - spin_unlock_irqrestore(&usbhid->ctrllock, flags); - warn("control queue full"); - return; - } - - usbhid->ctrl[usbhid->ctrlhead].report = report; - usbhid->ctrl[usbhid->ctrlhead].dir = dir; - usbhid->ctrlhead = head; - - if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) - if (hid_submit_ctrl(hid)) - clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); - - spin_unlock_irqrestore(&usbhid->ctrllock, flags); -} - -static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) -{ - 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); - usbhid_submit_report(hid, field->report, USB_DIR_OUT); - - return 0; -} - -int usbhid_wait_io(struct hid_device *hid) -{ - 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)), - 10*HZ)) { - dbg("timeout waiting for ctrl or out queue to clear"); - return -1; - } - - return 0; -} - -static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, (idle << 8) | report, - ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT); -} - -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); - - 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; -} - -int usbhid_open(struct hid_device *hid) -{ - ++hid->open; - if (hid_start_in(hid)) - hid_io_error(hid); - return 0; -} - -void usbhid_close(struct hid_device *hid) -{ - struct usbhid_device *usbhid = hid->driver_data; - - if (!--hid->open) - usb_kill_urb(usbhid->urbin); -} - -#define USB_VENDOR_ID_PANJIT 0x134c - -#define USB_VENDOR_ID_TURBOX 0x062a -#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201 -#define USB_VENDOR_ID_CIDC 0x1677 - -/* - * Initialize all reports - */ - -void usbhid_init_reports(struct hid_device *hid) -{ - struct hid_report *report; - struct usbhid_device *usbhid = hid->driver_data; - int err, ret; - - list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) - usbhid_submit_report(hid, report, USB_DIR_IN); - - list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list) - usbhid_submit_report(hid, report, USB_DIR_IN); - - err = 0; - ret = usbhid_wait_io(hid); - while (ret) { - err |= ret; - 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); - } - - if (err) - warn("timeout initializing reports"); -} - -#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 -#define USB_DEVICE_ID_GTCO_405 0x0405 -#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 - -#define USB_VENDOR_ID_WACOM 0x056a - -#define USB_VENDOR_ID_ACECAD 0x0460 -#define USB_DEVICE_ID_ACECAD_FLAIR 0x0004 -#define USB_DEVICE_ID_ACECAD_302 0x0008 - -#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 - -#define USB_VENDOR_ID_AASHIMA 0x06d6 -#define USB_DEVICE_ID_AASHIMA_GAMEPAD 0x0025 -#define USB_DEVICE_ID_AASHIMA_PREDATOR 0x0026 - -#define USB_VENDOR_ID_CYPRESS 0x04b4 -#define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001 -#define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500 -#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417 - -#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 -#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 -#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053 -#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058 - -#define USB_VENDOR_ID_WISEGROUP 0x0925 -#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101 -#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104 -#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201 -#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866 - -#define USB_VENDOR_ID_WISEGROUP_LTD 0x6677 -#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802 - -#define USB_VENDOR_ID_CODEMERCS 0x07c0 -#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500 -#define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff - -#define USB_VENDOR_ID_DELORME 0x1163 -#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100 -#define USB_DEVICE_ID_DELORME_EM_LT20 0x0200 - -#define USB_VENDOR_ID_MCC 0x09db -#define USB_DEVICE_ID_MCC_PMD1024LS 0x0076 -#define USB_DEVICE_ID_MCC_PMD1208LS 0x007a - -#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 - -#define USB_VENDOR_ID_LD 0x0f11 -#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 - -#define USB_VENDOR_ID_APPLE 0x05ac -#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304 -#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 -#define USB_DEVICE_ID_APPLE_IR 0x8240 - -#define USB_VENDOR_ID_CHERRY 0x046a -#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023 - -#define USB_VENDOR_ID_YEALINK 0x6993 -#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001 - -#define USB_VENDOR_ID_ALCOR 0x058f -#define USB_DEVICE_ID_ALCOR_USBRS232 0x9720 - -#define USB_VENDOR_ID_SUN 0x0430 -#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab - -#define USB_VENDOR_ID_AIRCABLE 0x16CA -#define USB_DEVICE_ID_AIRCABLE1 0x1502 - -#define USB_VENDOR_ID_LOGITECH 0x046d -#define USB_DEVICE_ID_LOGITECH_USB_RECEIVER 0xc101 -#define USB_DEVICE_ID_LOGITECH_USB_RECEIVER_2 0xc517 -#define USB_DEVICE_ID_DINOVO_EDGE 0xc714 - -#define USB_VENDOR_ID_IMATION 0x0718 -#define USB_DEVICE_ID_DISC_STAKKA 0xd000 - -#define USB_VENDOR_ID_PANTHERLORD 0x0810 -#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK 0x0001 - -#define USB_VENDOR_ID_SONY 0x054c -#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 - -/* - * Alphabetically sorted blacklist by quirk type. - */ - -static const struct hid_blacklist { - __u16 idVendor; - __u16 idProduct; - unsigned quirks; -} hid_blacklist[] = { - - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES }, - - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20, HID_QUIRK_IGNORE }, - { 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 }, - { USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20, HID_QUIRK_IGNORE }, - { 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 }, - { 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 }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE }, - { 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 }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405, HID_QUIRK_IGNORE }, - { 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 }, - { USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY, HID_QUIRK_IGNORE }, - { 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 }, - { 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 }, - { 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 }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE }, - - { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE }, - - { 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 }, - { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, - - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL }, - { 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 }, - - { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD }, - { 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 }, - - { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION }, - - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IR, HID_QUIRK_IGNORE }, - - { 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 }, - - { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, - - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_USB_RECEIVER, HID_QUIRK_BAD_RELATIVE_KEYS }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_USB_RECEIVER_2, HID_QUIRK_LOGITECH_S510_DESCRIPTOR }, - - { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, - - { USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER }, - - { USB_VENDOR_ID_CIDC, 0x0103, HID_QUIRK_IGNORE }, - - { 0, 0 } -}; - -/* - * 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; - } -} - -static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid) -{ - struct usbhid_device *usbhid = hid->driver_data; - - if (!(usbhid->inbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->inbuf_dma))) - return -1; - if (!(usbhid->outbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->outbuf_dma))) - return -1; - if (!(usbhid->cr = usb_buffer_alloc(dev, sizeof(*(usbhid->cr)), GFP_ATOMIC, &usbhid->cr_dma))) - return -1; - if (!(usbhid->ctrlbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->ctrlbuf_dma))) - return -1; - - return 0; -} - -static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) -{ - 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); -} - -/* - * 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; - } -} - -/* - * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller - * to "operational". Without this, the ps3 controller will not report any - * events. - */ -static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum) -{ - int result; - char *buf = kmalloc(18, GFP_KERNEL); - - if (!buf) - return; - - result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - HID_REQ_GET_REPORT, - USB_DIR_IN | USB_TYPE_CLASS | - USB_RECIP_INTERFACE, - (3 << 8) | 0xf2, ifnum, buf, 17, - USB_CTRL_GET_TIMEOUT); - - if (result < 0) - err("%s failed: %d\n", __func__, result); - - kfree(buf); -} - -/* - * Logitech S510 keyboard sends in report #3 keys which are far - * above the logical maximum described in descriptor. This extends - * the original value of 0x28c of logical maximum to 0x104d - */ -static void hid_fixup_s510_descriptor(unsigned char *rdesc, int rsize) -{ - if (rsize >= 90 && rdesc[83] == 0x26 - && rdesc[84] == 0x8c - && rdesc[85] == 0x02) { - info("Fixing up Logitech S510 report descriptor"); - rdesc[84] = rdesc[89] = 0x4d; - rdesc[85] = rdesc[90] = 0x10; - } -} - -static struct hid_device *usb_hid_configure(struct usb_interface *intf) -{ - struct usb_host_interface *interface = intf->cur_altsetting; - struct usb_device *dev = interface_to_usbdev (intf); - struct hid_descriptor *hdesc; - struct hid_device *hid; - unsigned quirks = 0, rsize = 0; - char *rdesc; - int n, len, insize = 0; - struct usbhid_device *usbhid; - - /* Ignore all Wacom devices */ - if (le16_to_cpu(dev->descriptor.idVendor) == USB_VENDOR_ID_WACOM) - return NULL; - /* ignore all Code Mercenaries IOWarrior devices */ - if (le16_to_cpu(dev->descriptor.idVendor) == USB_VENDOR_ID_CODEMERCS) - if (le16_to_cpu(dev->descriptor.idProduct) >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST && - le16_to_cpu(dev->descriptor.idProduct) <= USB_DEVICE_ID_CODEMERCS_IOW_LAST) - return NULL; - - for (n = 0; hid_blacklist[n].idVendor; n++) - if ((hid_blacklist[n].idVendor == le16_to_cpu(dev->descriptor.idVendor)) && - (hid_blacklist[n].idProduct == le16_to_cpu(dev->descriptor.idProduct))) - quirks = hid_blacklist[n].quirks; - - /* 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; - } - - if (quirks & HID_QUIRK_IGNORE) - return NULL; - - if ((quirks & HID_QUIRK_IGNORE_MOUSE) && - (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)) - return NULL; - - - 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; - } - - 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; - } - - hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0); - - 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; - } - - if ((quirks & HID_QUIRK_CYMOTION)) - hid_fixup_cymotion_descriptor(rdesc, rsize); - - if (quirks & HID_QUIRK_LOGITECH_S510_DESCRIPTOR) - hid_fixup_s510_descriptor(rdesc, rsize); - -#ifdef CONFIG_HID_DEBUG - printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n); - for (n = 0; n < rsize; n++) - printk(" %02x", (unsigned char) rdesc[n]); - printk("\n"); -#endif - - if (!(hid = hid_parse_report(rdesc, n))) { - dbg("parsing report descriptor failed"); - kfree(rdesc); - return NULL; - } - - kfree(rdesc); - hid->quirks = quirks; - - 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); - - if (usbhid->bufsize > HID_MAX_BUFFER_SIZE) - usbhid->bufsize = HID_MAX_BUFFER_SIZE; - - hid_find_max_report(hid, HID_INPUT_REPORT, &insize); - - if (insize > HID_MAX_BUFFER_SIZE) - insize = HID_MAX_BUFFER_SIZE; - - 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; - - if (usb_endpoint_dir_in(endpoint)) { - if (usbhid->urbin) - continue; - if (!(usbhid->urbin = usb_alloc_urb(0, GFP_KERNEL))) - goto fail; - pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); - usb_fill_int_urb(usbhid->urbin, dev, pipe, usbhid->inbuf, insize, - hid_irq_in, hid, interval); - usbhid->urbin->transfer_dma = usbhid->inbuf_dma; - usbhid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - } else { - if (usbhid->urbout) - continue; - if (!(usbhid->urbout = usb_alloc_urb(0, GFP_KERNEL))) - goto fail; - pipe = usb_sndintpipe(dev, endpoint->bEndpointAddress); - usb_fill_int_urb(usbhid->urbout, dev, pipe, usbhid->outbuf, 0, - hid_irq_out, hid, interval); - usbhid->urbout->transfer_dma = usbhid->outbuf_dma; - usbhid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - } - } - - if (!usbhid->urbin) { - err("couldn't find an input interrupt endpoint"); - goto fail; - } - - init_waitqueue_head(&hid->wait); - - INIT_WORK(&usbhid->reset_work, hid_reset); - setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid); - - spin_lock_init(&usbhid->inlock); - spin_lock_init(&usbhid->outlock); - spin_lock_init(&usbhid->ctrllock); - - hid->version = le16_to_cpu(hdesc->bcdHID); - hid->country = hdesc->bCountryCode; - hid->dev = &intf->dev; - usbhid->intf = intf; - usbhid->ifnum = interface->desc.bInterfaceNumber; - - hid->name[0] = 0; - - 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)); - - hid->bus = BUS_USB; - hid->vendor = le16_to_cpu(dev->descriptor.idVendor); - hid->product = le16_to_cpu(dev->descriptor.idProduct); - - usb_make_path(dev, hid->phys, sizeof(hid->phys)); - strlcat(hid->phys, "/input", sizeof(hid->phys)); - len = strlen(hid->phys); - if (len < sizeof(hid->phys) - 1) - snprintf(hid->phys + len, sizeof(hid->phys) - len, - "%d", intf->altsetting[0].desc.bInterfaceNumber); - - if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0) - hid->uniq[0] = 0; - - usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL); - if (!usbhid->urbctrl) - goto fail; - - 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); - hid->hidinput_input_event = usb_hidinput_input_event; - hid->hid_open = usbhid_open; - hid->hid_close = usbhid_close; -#ifdef CONFIG_USB_HIDDEV - hid->hiddev_hid_event = hiddev_hid_event; - hid->hiddev_report_event = hiddev_report_event; -#endif - return hid; - -fail: - usb_free_urb(usbhid->urbin); - usb_free_urb(usbhid->urbout); - usb_free_urb(usbhid->urbctrl); - 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); - struct usbhid_device *usbhid; - - if (!hid) - return; - - usbhid = hid->driver_data; - - spin_lock_irq(&usbhid->inlock); /* Sync with error handler */ - usb_set_intfdata(intf, NULL); - spin_unlock_irq(&usbhid->inlock); - usb_kill_urb(usbhid->urbin); - usb_kill_urb(usbhid->urbout); - usb_kill_urb(usbhid->urbctrl); - - del_timer_sync(&usbhid->io_retry); - flush_scheduled_work(); - - if (hid->claimed & HID_CLAIMED_INPUT) - hidinput_disconnect(hid); - if (hid->claimed & HID_CLAIMED_HIDDEV) - hiddev_disconnect(hid); - - usb_free_urb(usbhid->urbin); - usb_free_urb(usbhid->urbctrl); - usb_free_urb(usbhid->urbout); - - hid_free_buffers(hid_to_usb_dev(hid), hid); - 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))) - return -ENODEV; - - usbhid_init_reports(hid); - 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); - return -ENODEV; - } - - if ((hid->claimed & HID_CLAIMED_INPUT)) - hid_ff_init(hid); - - if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER) - hid_fixup_sony_ps3_controller(interface_to_usbdev(intf), - intf->cur_altsetting->desc.bInterfaceNumber); - - 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; -} - -static int hid_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct hid_device *hid = usb_get_intfdata (intf); - struct usbhid_device *usbhid = hid->driver_data; - - 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); - dev_dbg(&intf->dev, "suspend\n"); - return 0; -} - -static int hid_resume(struct usb_interface *intf) -{ - struct hid_device *hid = usb_get_intfdata (intf); - struct usbhid_device *usbhid = hid->driver_data; - int status; - - clear_bit(HID_SUSPENDED, &usbhid->iofl); - usbhid->retry_delay = 0; - status = hid_start_in(hid); - dev_dbg(&intf->dev, "resume status %d\n", status); - return status; -} - -/* 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); -} - -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, - .pre_reset = hid_pre_reset, - .post_reset = hid_post_reset, - .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); diff --git a/drivers/usb/input/hid-ff.c b/drivers/usb/input/hid-ff.c deleted file mode 100644 index e431faaa6abc..000000000000 --- a/drivers/usb/input/hid-ff.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * $Id: hid-ff.c,v 1.2 2002/04/18 22:02:47 jdeneux Exp $ - * - * Force feedback support for hid devices. - * Not all hid devices use the same protocol. For example, some use PID, - * other use their own proprietary procotol. - * - * Copyright (c) 2002-2004 Johann Deneux - */ - -/* - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so by - * e-mail - mail your message to - */ - -#include - -#undef DEBUG -#include - -#include -#include "usbhid.h" - -/* - * This table contains pointers to initializers. To add support for new - * devices, you need to add the USB vendor and product ids here. - */ -struct hid_ff_initializer { - u16 idVendor; - u16 idProduct; - int (*init)(struct hid_device*); -}; - -/* - * We try pidff when no other driver is found because PID is the - * standards compliant way of implementing force feedback in HID. - * pidff_init() will quickly abort if the device doesn't appear to - * be a PID device - */ -static struct hid_ff_initializer inits[] = { -#ifdef CONFIG_LOGITECH_FF - { 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */ - { 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */ - { 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */ - { 0x46d, 0xc294, hid_lgff_init }, /* Logitech Formula Force EX */ - { 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */ - { 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */ -#endif -#ifdef CONFIG_PANTHERLORD_FF - { 0x810, 0x0001, hid_plff_init }, -#endif -#ifdef CONFIG_THRUSTMASTER_FF - { 0x44f, 0xb304, hid_tmff_init }, -#endif -#ifdef CONFIG_ZEROPLUS_FF - { 0xc12, 0x0005, hid_zpff_init }, - { 0xc12, 0x0030, hid_zpff_init }, -#endif - { 0, 0, hid_pidff_init} /* Matches anything */ -}; - -int hid_ff_init(struct hid_device* hid) -{ - struct hid_ff_initializer *init; - int vendor = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idVendor); - int product = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idProduct); - - for (init = inits; init->idVendor; init++) - if (init->idVendor == vendor && init->idProduct == product) - break; - - return init->init(hid); -} -EXPORT_SYMBOL_GPL(hid_ff_init); - diff --git a/drivers/usb/input/hid-lgff.c b/drivers/usb/input/hid-lgff.c deleted file mode 100644 index e6f3af3e66d1..000000000000 --- a/drivers/usb/input/hid-lgff.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Force feedback support for hid-compliant for some of the devices from - * Logitech, namely: - * - WingMan Cordless RumblePad - * - WingMan Force 3D - * - * Copyright (c) 2002-2004 Johann Deneux - * Copyright (c) 2006 Anssi Hannula - */ - -/* - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so by - * e-mail - mail your message to - */ - -#include -#include -#include -#include "usbhid.h" - -struct dev_type { - u16 idVendor; - u16 idProduct; - const signed short *ff; -}; - -static const signed short ff_rumble[] = { - FF_RUMBLE, - -1 -}; - -static const signed short ff_joystick[] = { - FF_CONSTANT, - -1 -}; - -static const struct dev_type devices[] = { - { 0x046d, 0xc211, ff_rumble }, - { 0x046d, 0xc219, ff_rumble }, - { 0x046d, 0xc283, ff_joystick }, - { 0x046d, 0xc294, ff_joystick }, - { 0x046d, 0xc295, ff_joystick }, - { 0x046d, 0xca03, ff_joystick }, -}; - -static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect) -{ - struct hid_device *hid = dev->private; - struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct hid_report *report = list_entry(report_list->next, struct hid_report, list); - int x, y; - unsigned int left, right; - -#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff - - switch (effect->type) { - case FF_CONSTANT: - x = effect->u.ramp.start_level + 0x7f; /* 0x7f is center */ - y = effect->u.ramp.end_level + 0x7f; - CLAMP(x); - CLAMP(y); - report->field[0]->value[0] = 0x51; - report->field[0]->value[1] = 0x08; - report->field[0]->value[2] = x; - report->field[0]->value[3] = y; - dbg("(x, y)=(%04x, %04x)", x, y); - usbhid_submit_report(hid, report, USB_DIR_OUT); - break; - - case FF_RUMBLE: - right = effect->u.rumble.strong_magnitude; - left = effect->u.rumble.weak_magnitude; - right = right * 0xff / 0xffff; - left = left * 0xff / 0xffff; - CLAMP(left); - CLAMP(right); - report->field[0]->value[0] = 0x42; - report->field[0]->value[1] = 0x00; - report->field[0]->value[2] = left; - report->field[0]->value[3] = right; - dbg("(left, right)=(%04x, %04x)", left, right); - usbhid_submit_report(hid, report, USB_DIR_OUT); - break; - } - return 0; -} - -int hid_lgff_init(struct hid_device* hid) -{ - struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); - struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct input_dev *dev = hidinput->input; - struct hid_report *report; - struct hid_field *field; - const signed short *ff_bits = ff_joystick; - int error; - int i; - - /* Find the report to use */ - if (list_empty(report_list)) { - err("No output report found"); - return -1; - } - - /* Check that the report looks ok */ - report = list_entry(report_list->next, struct hid_report, list); - if (!report) { - err("NULL output report"); - return -1; - } - - field = report->field[0]; - if (!field) { - err("NULL field"); - return -1; - } - - for (i = 0; i < ARRAY_SIZE(devices); i++) { - if (dev->id.vendor == devices[i].idVendor && - dev->id.product == devices[i].idProduct) { - ff_bits = devices[i].ff; - break; - } - } - - for (i = 0; ff_bits[i] >= 0; i++) - set_bit(ff_bits[i], dev->ffbit); - - error = input_ff_create_memless(dev, NULL, hid_lgff_play); - if (error) - return error; - - printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux \n"); - - return 0; -} diff --git a/drivers/usb/input/hid-pidff.c b/drivers/usb/input/hid-pidff.c deleted file mode 100644 index f5a90e950e6b..000000000000 --- a/drivers/usb/input/hid-pidff.c +++ /dev/null @@ -1,1331 +0,0 @@ -/* - * Force feedback driver for USB HID PID compliant devices - * - * Copyright (c) 2005, 2006 Anssi Hannula - */ - -/* - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* #define DEBUG */ - -#define debug(format, arg...) pr_debug("hid-pidff: " format "\n" , ## arg) - -#include -#include - -#include - -#include "usbhid.h" - -#define PID_EFFECTS_MAX 64 - -/* Report usage table used to put reports into an array */ - -#define PID_SET_EFFECT 0 -#define PID_EFFECT_OPERATION 1 -#define PID_DEVICE_GAIN 2 -#define PID_POOL 3 -#define PID_BLOCK_LOAD 4 -#define PID_BLOCK_FREE 5 -#define PID_DEVICE_CONTROL 6 -#define PID_CREATE_NEW_EFFECT 7 - -#define PID_REQUIRED_REPORTS 7 - -#define PID_SET_ENVELOPE 8 -#define PID_SET_CONDITION 9 -#define PID_SET_PERIODIC 10 -#define PID_SET_CONSTANT 11 -#define PID_SET_RAMP 12 -static const u8 pidff_reports[] = { - 0x21, 0x77, 0x7d, 0x7f, 0x89, 0x90, 0x96, 0xab, - 0x5a, 0x5f, 0x6e, 0x73, 0x74 -}; - -/* device_control is really 0x95, but 0x96 specified as it is the usage of -the only field in that report */ - -/* Value usage tables used to put fields and values into arrays */ - -#define PID_EFFECT_BLOCK_INDEX 0 - -#define PID_DURATION 1 -#define PID_GAIN 2 -#define PID_TRIGGER_BUTTON 3 -#define PID_TRIGGER_REPEAT_INT 4 -#define PID_DIRECTION_ENABLE 5 -#define PID_START_DELAY 6 -static const u8 pidff_set_effect[] = { - 0x22, 0x50, 0x52, 0x53, 0x54, 0x56, 0xa7 -}; - -#define PID_ATTACK_LEVEL 1 -#define PID_ATTACK_TIME 2 -#define PID_FADE_LEVEL 3 -#define PID_FADE_TIME 4 -static const u8 pidff_set_envelope[] = { 0x22, 0x5b, 0x5c, 0x5d, 0x5e }; - -#define PID_PARAM_BLOCK_OFFSET 1 -#define PID_CP_OFFSET 2 -#define PID_POS_COEFFICIENT 3 -#define PID_NEG_COEFFICIENT 4 -#define PID_POS_SATURATION 5 -#define PID_NEG_SATURATION 6 -#define PID_DEAD_BAND 7 -static const u8 pidff_set_condition[] = { - 0x22, 0x23, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65 -}; - -#define PID_MAGNITUDE 1 -#define PID_OFFSET 2 -#define PID_PHASE 3 -#define PID_PERIOD 4 -static const u8 pidff_set_periodic[] = { 0x22, 0x70, 0x6f, 0x71, 0x72 }; -static const u8 pidff_set_constant[] = { 0x22, 0x70 }; - -#define PID_RAMP_START 1 -#define PID_RAMP_END 2 -static const u8 pidff_set_ramp[] = { 0x22, 0x75, 0x76 }; - -#define PID_RAM_POOL_AVAILABLE 1 -static const u8 pidff_block_load[] = { 0x22, 0xac }; - -#define PID_LOOP_COUNT 1 -static const u8 pidff_effect_operation[] = { 0x22, 0x7c }; - -static const u8 pidff_block_free[] = { 0x22 }; - -#define PID_DEVICE_GAIN_FIELD 0 -static const u8 pidff_device_gain[] = { 0x7e }; - -#define PID_RAM_POOL_SIZE 0 -#define PID_SIMULTANEOUS_MAX 1 -#define PID_DEVICE_MANAGED_POOL 2 -static const u8 pidff_pool[] = { 0x80, 0x83, 0xa9 }; - -/* Special field key tables used to put special field keys into arrays */ - -#define PID_ENABLE_ACTUATORS 0 -#define PID_RESET 1 -static const u8 pidff_device_control[] = { 0x97, 0x9a }; - -#define PID_CONSTANT 0 -#define PID_RAMP 1 -#define PID_SQUARE 2 -#define PID_SINE 3 -#define PID_TRIANGLE 4 -#define PID_SAW_UP 5 -#define PID_SAW_DOWN 6 -#define PID_SPRING 7 -#define PID_DAMPER 8 -#define PID_INERTIA 9 -#define PID_FRICTION 10 -static const u8 pidff_effect_types[] = { - 0x26, 0x27, 0x30, 0x31, 0x32, 0x33, 0x34, - 0x40, 0x41, 0x42, 0x43 -}; - -#define PID_BLOCK_LOAD_SUCCESS 0 -#define PID_BLOCK_LOAD_FULL 1 -static const u8 pidff_block_load_status[] = { 0x8c, 0x8d }; - -#define PID_EFFECT_START 0 -#define PID_EFFECT_STOP 1 -static const u8 pidff_effect_operation_status[] = { 0x79, 0x7b }; - -struct pidff_usage { - struct hid_field *field; - s32 *value; -}; - -struct pidff_device { - struct hid_device *hid; - - struct hid_report *reports[sizeof(pidff_reports)]; - - struct pidff_usage set_effect[sizeof(pidff_set_effect)]; - struct pidff_usage set_envelope[sizeof(pidff_set_envelope)]; - struct pidff_usage set_condition[sizeof(pidff_set_condition)]; - struct pidff_usage set_periodic[sizeof(pidff_set_periodic)]; - struct pidff_usage set_constant[sizeof(pidff_set_constant)]; - struct pidff_usage set_ramp[sizeof(pidff_set_ramp)]; - - struct pidff_usage device_gain[sizeof(pidff_device_gain)]; - struct pidff_usage block_load[sizeof(pidff_block_load)]; - struct pidff_usage pool[sizeof(pidff_pool)]; - struct pidff_usage effect_operation[sizeof(pidff_effect_operation)]; - struct pidff_usage block_free[sizeof(pidff_block_free)]; - - /* Special field is a field that is not composed of - usage<->value pairs that pidff_usage values are */ - - /* Special field in create_new_effect */ - struct hid_field *create_new_effect_type; - - /* Special fields in set_effect */ - struct hid_field *set_effect_type; - struct hid_field *effect_direction; - - /* Special field in device_control */ - struct hid_field *device_control; - - /* Special field in block_load */ - struct hid_field *block_load_status; - - /* Special field in effect_operation */ - struct hid_field *effect_operation_status; - - int control_id[sizeof(pidff_device_control)]; - int type_id[sizeof(pidff_effect_types)]; - int status_id[sizeof(pidff_block_load_status)]; - int operation_id[sizeof(pidff_effect_operation_status)]; - - int pid_id[PID_EFFECTS_MAX]; -}; - -/* - * Scale an unsigned value with range 0..max for the given field - */ -static int pidff_rescale(int i, int max, struct hid_field *field) -{ - return i * (field->logical_maximum - field->logical_minimum) / max + - field->logical_minimum; -} - -/* - * Scale a signed value in range -0x8000..0x7fff for the given field - */ -static int pidff_rescale_signed(int i, struct hid_field *field) -{ - return i == 0 ? 0 : i > - 0 ? i * field->logical_maximum / 0x7fff : i * - field->logical_minimum / -0x8000; -} - -static void pidff_set(struct pidff_usage *usage, u16 value) -{ - usage->value[0] = pidff_rescale(value, 0xffff, usage->field); - debug("calculated from %d to %d", value, usage->value[0]); -} - -static void pidff_set_signed(struct pidff_usage *usage, s16 value) -{ - if (usage->field->logical_minimum < 0) - usage->value[0] = pidff_rescale_signed(value, usage->field); - else { - if (value < 0) - usage->value[0] = - pidff_rescale(-value, 0x8000, usage->field); - else - usage->value[0] = - pidff_rescale(value, 0x7fff, usage->field); - } - debug("calculated from %d to %d", value, usage->value[0]); -} - -/* - * Send envelope report to the device - */ -static void pidff_set_envelope_report(struct pidff_device *pidff, - struct ff_envelope *envelope) -{ - pidff->set_envelope[PID_EFFECT_BLOCK_INDEX].value[0] = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; - - pidff->set_envelope[PID_ATTACK_LEVEL].value[0] = - pidff_rescale(envelope->attack_level > - 0x7fff ? 0x7fff : envelope->attack_level, 0x7fff, - pidff->set_envelope[PID_ATTACK_LEVEL].field); - pidff->set_envelope[PID_FADE_LEVEL].value[0] = - pidff_rescale(envelope->fade_level > - 0x7fff ? 0x7fff : envelope->fade_level, 0x7fff, - pidff->set_envelope[PID_FADE_LEVEL].field); - - pidff->set_envelope[PID_ATTACK_TIME].value[0] = envelope->attack_length; - pidff->set_envelope[PID_FADE_TIME].value[0] = envelope->fade_length; - - debug("attack %u => %d", envelope->attack_level, - pidff->set_envelope[PID_ATTACK_LEVEL].value[0]); - - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_ENVELOPE], - USB_DIR_OUT); -} - -/* - * Test if the new envelope differs from old one - */ -static int pidff_needs_set_envelope(struct ff_envelope *envelope, - struct ff_envelope *old) -{ - return envelope->attack_level != old->attack_level || - envelope->fade_level != old->fade_level || - envelope->attack_length != old->attack_length || - envelope->fade_length != old->fade_length; -} - -/* - * Send constant force report to the device - */ -static void pidff_set_constant_force_report(struct pidff_device *pidff, - struct ff_effect *effect) -{ - pidff->set_constant[PID_EFFECT_BLOCK_INDEX].value[0] = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; - pidff_set_signed(&pidff->set_constant[PID_MAGNITUDE], - effect->u.constant.level); - - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONSTANT], - USB_DIR_OUT); -} - -/* - * Test if the constant parameters have changed between effects - */ -static int pidff_needs_set_constant(struct ff_effect *effect, - struct ff_effect *old) -{ - return effect->u.constant.level != old->u.constant.level; -} - -/* - * Send set effect report to the device - */ -static void pidff_set_effect_report(struct pidff_device *pidff, - struct ff_effect *effect) -{ - pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; - pidff->set_effect_type->value[0] = - pidff->create_new_effect_type->value[0]; - pidff->set_effect[PID_DURATION].value[0] = effect->replay.length; - pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = effect->trigger.button; - pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = - effect->trigger.interval; - pidff->set_effect[PID_GAIN].value[0] = - pidff->set_effect[PID_GAIN].field->logical_maximum; - pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1; - pidff->effect_direction->value[0] = - pidff_rescale(effect->direction, 0xffff, - pidff->effect_direction); - pidff->set_effect[PID_START_DELAY].value[0] = effect->replay.delay; - - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT], - USB_DIR_OUT); -} - -/* - * Test if the values used in set_effect have changed - */ -static int pidff_needs_set_effect(struct ff_effect *effect, - struct ff_effect *old) -{ - return effect->replay.length != old->replay.length || - effect->trigger.interval != old->trigger.interval || - effect->trigger.button != old->trigger.button || - effect->direction != old->direction || - effect->replay.delay != old->replay.delay; -} - -/* - * Send periodic effect report to the device - */ -static void pidff_set_periodic_report(struct pidff_device *pidff, - struct ff_effect *effect) -{ - pidff->set_periodic[PID_EFFECT_BLOCK_INDEX].value[0] = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; - pidff_set_signed(&pidff->set_periodic[PID_MAGNITUDE], - effect->u.periodic.magnitude); - pidff_set_signed(&pidff->set_periodic[PID_OFFSET], - effect->u.periodic.offset); - pidff_set(&pidff->set_periodic[PID_PHASE], effect->u.periodic.phase); - pidff->set_periodic[PID_PERIOD].value[0] = effect->u.periodic.period; - - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_PERIODIC], - USB_DIR_OUT); - -} - -/* - * Test if periodic effect parameters have changed - */ -static int pidff_needs_set_periodic(struct ff_effect *effect, - struct ff_effect *old) -{ - return effect->u.periodic.magnitude != old->u.periodic.magnitude || - effect->u.periodic.offset != old->u.periodic.offset || - effect->u.periodic.phase != old->u.periodic.phase || - effect->u.periodic.period != old->u.periodic.period; -} - -/* - * Send condition effect reports to the device - */ -static void pidff_set_condition_report(struct pidff_device *pidff, - struct ff_effect *effect) -{ - int i; - - pidff->set_condition[PID_EFFECT_BLOCK_INDEX].value[0] = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; - - for (i = 0; i < 2; i++) { - pidff->set_condition[PID_PARAM_BLOCK_OFFSET].value[0] = i; - pidff_set_signed(&pidff->set_condition[PID_CP_OFFSET], - effect->u.condition[i].center); - pidff_set_signed(&pidff->set_condition[PID_POS_COEFFICIENT], - effect->u.condition[i].right_coeff); - pidff_set_signed(&pidff->set_condition[PID_NEG_COEFFICIENT], - effect->u.condition[i].left_coeff); - pidff_set(&pidff->set_condition[PID_POS_SATURATION], - effect->u.condition[i].right_saturation); - pidff_set(&pidff->set_condition[PID_NEG_SATURATION], - effect->u.condition[i].left_saturation); - pidff_set(&pidff->set_condition[PID_DEAD_BAND], - effect->u.condition[i].deadband); - usbhid_wait_io(pidff->hid); - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONDITION], - USB_DIR_OUT); - } -} - -/* - * Test if condition effect parameters have changed - */ -static int pidff_needs_set_condition(struct ff_effect *effect, - struct ff_effect *old) -{ - int i; - int ret = 0; - - for (i = 0; i < 2; i++) { - struct ff_condition_effect *cond = &effect->u.condition[i]; - struct ff_condition_effect *old_cond = &old->u.condition[i]; - - ret |= cond->center != old_cond->center || - cond->right_coeff != old_cond->right_coeff || - cond->left_coeff != old_cond->left_coeff || - cond->right_saturation != old_cond->right_saturation || - cond->left_saturation != old_cond->left_saturation || - cond->deadband != old_cond->deadband; - } - - return ret; -} - -/* - * Send ramp force report to the device - */ -static void pidff_set_ramp_force_report(struct pidff_device *pidff, - struct ff_effect *effect) -{ - pidff->set_ramp[PID_EFFECT_BLOCK_INDEX].value[0] = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; - pidff_set_signed(&pidff->set_ramp[PID_RAMP_START], - effect->u.ramp.start_level); - pidff_set_signed(&pidff->set_ramp[PID_RAMP_END], - effect->u.ramp.end_level); - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_RAMP], - USB_DIR_OUT); -} - -/* - * Test if ramp force parameters have changed - */ -static int pidff_needs_set_ramp(struct ff_effect *effect, struct ff_effect *old) -{ - return effect->u.ramp.start_level != old->u.ramp.start_level || - effect->u.ramp.end_level != old->u.ramp.end_level; -} - -/* - * Send a request for effect upload to the device - * - * Returns 0 if device reported success, -ENOSPC if the device reported memory - * is full. Upon unknown response the function will retry for 60 times, if - * still unsuccessful -EIO is returned. - */ -static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum) -{ - int j; - - pidff->create_new_effect_type->value[0] = efnum; - usbhid_submit_report(pidff->hid, pidff->reports[PID_CREATE_NEW_EFFECT], - USB_DIR_OUT); - debug("create_new_effect sent, type: %d", efnum); - - pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] = 0; - pidff->block_load_status->value[0] = 0; - usbhid_wait_io(pidff->hid); - - for (j = 0; j < 60; j++) { - debug("pid_block_load requested"); - usbhid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_LOAD], - USB_DIR_IN); - usbhid_wait_io(pidff->hid); - if (pidff->block_load_status->value[0] == - pidff->status_id[PID_BLOCK_LOAD_SUCCESS]) { - debug("device reported free memory: %d bytes", - pidff->block_load[PID_RAM_POOL_AVAILABLE].value ? - pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1); - return 0; - } - if (pidff->block_load_status->value[0] == - pidff->status_id[PID_BLOCK_LOAD_FULL]) { - debug("not enough memory free: %d bytes", - pidff->block_load[PID_RAM_POOL_AVAILABLE].value ? - pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1); - return -ENOSPC; - } - } - printk(KERN_ERR "hid-pidff: pid_block_load failed 60 times\n"); - return -EIO; -} - -/* - * Play the effect with PID id n times - */ -static void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n) -{ - pidff->effect_operation[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id; - - if (n == 0) { - pidff->effect_operation_status->value[0] = - pidff->operation_id[PID_EFFECT_STOP]; - } else { - pidff->effect_operation_status->value[0] = - pidff->operation_id[PID_EFFECT_START]; - pidff->effect_operation[PID_LOOP_COUNT].value[0] = n; - } - - usbhid_wait_io(pidff->hid); - usbhid_submit_report(pidff->hid, pidff->reports[PID_EFFECT_OPERATION], - USB_DIR_OUT); -} - -/** - * Play the effect with effect id @effect_id for @value times - */ -static int pidff_playback(struct input_dev *dev, int effect_id, int value) -{ - struct pidff_device *pidff = dev->ff->private; - - pidff_playback_pid(pidff, pidff->pid_id[effect_id], value); - - return 0; -} - -/* - * Erase effect with PID id - */ -static void pidff_erase_pid(struct pidff_device *pidff, int pid_id) -{ - pidff->block_free[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id; - usbhid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_FREE], - USB_DIR_OUT); -} - -/* - * Stop and erase effect with effect_id - */ -static int pidff_erase_effect(struct input_dev *dev, int effect_id) -{ - struct pidff_device *pidff = dev->ff->private; - int pid_id = pidff->pid_id[effect_id]; - - debug("starting to erase %d/%d", effect_id, pidff->pid_id[effect_id]); - pidff_playback_pid(pidff, pid_id, 0); - pidff_erase_pid(pidff, pid_id); - - return 0; -} - -/* - * Effect upload handler - */ -static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect, - struct ff_effect *old) -{ - struct pidff_device *pidff = dev->ff->private; - int type_id; - int error; - - switch (effect->type) { - case FF_CONSTANT: - if (!old) { - error = pidff_request_effect_upload(pidff, - pidff->type_id[PID_CONSTANT]); - if (error) - return error; - } - if (!old || pidff_needs_set_effect(effect, old)) - pidff_set_effect_report(pidff, effect); - if (!old || pidff_needs_set_constant(effect, old)) - pidff_set_constant_force_report(pidff, effect); - if (!old || - pidff_needs_set_envelope(&effect->u.constant.envelope, - &old->u.constant.envelope)) - pidff_set_envelope_report(pidff, - &effect->u.constant.envelope); - break; - - case FF_PERIODIC: - if (!old) { - switch (effect->u.periodic.waveform) { - case FF_SQUARE: - type_id = PID_SQUARE; - break; - case FF_TRIANGLE: - type_id = PID_TRIANGLE; - break; - case FF_SINE: - type_id = PID_SINE; - break; - case FF_SAW_UP: - type_id = PID_SAW_UP; - break; - case FF_SAW_DOWN: - type_id = PID_SAW_DOWN; - break; - default: - printk(KERN_ERR - "hid-pidff: invalid waveform\n"); - return -EINVAL; - } - - error = pidff_request_effect_upload(pidff, - pidff->type_id[type_id]); - if (error) - return error; - } - if (!old || pidff_needs_set_effect(effect, old)) - pidff_set_effect_report(pidff, effect); - if (!old || pidff_needs_set_periodic(effect, old)) - pidff_set_periodic_report(pidff, effect); - if (!old || - pidff_needs_set_envelope(&effect->u.periodic.envelope, - &old->u.periodic.envelope)) - pidff_set_envelope_report(pidff, - &effect->u.periodic.envelope); - break; - - case FF_RAMP: - if (!old) { - error = pidff_request_effect_upload(pidff, - pidff->type_id[PID_RAMP]); - if (error) - return error; - } - if (!old || pidff_needs_set_effect(effect, old)) - pidff_set_effect_report(pidff, effect); - if (!old || pidff_needs_set_ramp(effect, old)) - pidff_set_ramp_force_report(pidff, effect); - if (!old || - pidff_needs_set_envelope(&effect->u.ramp.envelope, - &old->u.ramp.envelope)) - pidff_set_envelope_report(pidff, - &effect->u.ramp.envelope); - break; - - case FF_SPRING: - if (!old) { - error = pidff_request_effect_upload(pidff, - pidff->type_id[PID_SPRING]); - if (error) - return error; - } - if (!old || pidff_needs_set_effect(effect, old)) - pidff_set_effect_report(pidff, effect); - if (!old || pidff_needs_set_condition(effect, old)) - pidff_set_condition_report(pidff, effect); - break; - - case FF_FRICTION: - if (!old) { - error = pidff_request_effect_upload(pidff, - pidff->type_id[PID_FRICTION]); - if (error) - return error; - } - if (!old || pidff_needs_set_effect(effect, old)) - pidff_set_effect_report(pidff, effect); - if (!old || pidff_needs_set_condition(effect, old)) - pidff_set_condition_report(pidff, effect); - break; - - case FF_DAMPER: - if (!old) { - error = pidff_request_effect_upload(pidff, - pidff->type_id[PID_DAMPER]); - if (error) - return error; - } - if (!old || pidff_needs_set_effect(effect, old)) - pidff_set_effect_report(pidff, effect); - if (!old || pidff_needs_set_condition(effect, old)) - pidff_set_condition_report(pidff, effect); - break; - - case FF_INERTIA: - if (!old) { - error = pidff_request_effect_upload(pidff, - pidff->type_id[PID_INERTIA]); - if (error) - return error; - } - if (!old || pidff_needs_set_effect(effect, old)) - pidff_set_effect_report(pidff, effect); - if (!old || pidff_needs_set_condition(effect, old)) - pidff_set_condition_report(pidff, effect); - break; - - default: - printk(KERN_ERR "hid-pidff: invalid type\n"); - return -EINVAL; - } - - if (!old) - pidff->pid_id[effect->id] = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; - - debug("uploaded"); - - return 0; -} - -/* - * set_gain() handler - */ -static void pidff_set_gain(struct input_dev *dev, u16 gain) -{ - struct pidff_device *pidff = dev->ff->private; - - pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], gain); - usbhid_submit_report(pidff->hid, pidff->reports[PID_DEVICE_GAIN], - USB_DIR_OUT); -} - -static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude) -{ - struct hid_field *field = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].field; - - if (!magnitude) { - pidff_playback_pid(pidff, field->logical_minimum, 0); - return; - } - - pidff_playback_pid(pidff, field->logical_minimum, 1); - - pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum; - pidff->set_effect_type->value[0] = pidff->type_id[PID_SPRING]; - pidff->set_effect[PID_DURATION].value[0] = 0; - pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = 0; - pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = 0; - pidff_set(&pidff->set_effect[PID_GAIN], magnitude); - pidff->set_effect[PID_START_DELAY].value[0] = 0; - - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT], - USB_DIR_OUT); -} - -/* - * pidff_set_autocenter() handler - */ -static void pidff_set_autocenter(struct input_dev *dev, u16 magnitude) -{ - struct pidff_device *pidff = dev->ff->private; - - pidff_autocenter(pidff, magnitude); -} - -/* - * Find fields from a report and fill a pidff_usage - */ -static int pidff_find_fields(struct pidff_usage *usage, const u8 *table, - struct hid_report *report, int count, int strict) -{ - int i, j, k, found; - - for (k = 0; k < count; k++) { - found = 0; - for (i = 0; i < report->maxfield; i++) { - if (report->field[i]->maxusage != - report->field[i]->report_count) { - debug("maxusage and report_count do not match, " - "skipping"); - continue; - } - for (j = 0; j < report->field[i]->maxusage; j++) { - if (report->field[i]->usage[j].hid == - (HID_UP_PID | table[k])) { - debug("found %d at %d->%d", k, i, j); - usage[k].field = report->field[i]; - usage[k].value = - &report->field[i]->value[j]; - found = 1; - break; - } - } - if (found) - break; - } - if (!found && strict) { - debug("failed to locate %d", k); - return -1; - } - } - return 0; -} - -/* - * Return index into pidff_reports for the given usage - */ -static int pidff_check_usage(int usage) -{ - int i; - - for (i = 0; i < sizeof(pidff_reports); i++) - if (usage == (HID_UP_PID | pidff_reports[i])) - return i; - - return -1; -} - -/* - * Find the reports and fill pidff->reports[] - * report_type specifies either OUTPUT or FEATURE reports - */ -static void pidff_find_reports(struct hid_device *hid, int report_type, - struct pidff_device *pidff) -{ - struct hid_report *report; - int i, ret; - - list_for_each_entry(report, - &hid->report_enum[report_type].report_list, list) { - if (report->maxfield < 1) - continue; - ret = pidff_check_usage(report->field[0]->logical); - if (ret != -1) { - debug("found usage 0x%02x from field->logical", - pidff_reports[ret]); - pidff->reports[ret] = report; - continue; - } - - /* - * Sometimes logical collections are stacked to indicate - * different usages for the report and the field, in which - * case we want the usage of the parent. However, Linux HID - * implementation hides this fact, so we have to dig it up - * ourselves - */ - i = report->field[0]->usage[0].collection_index; - if (i <= 0 || - hid->collection[i - 1].type != HID_COLLECTION_LOGICAL) - continue; - ret = pidff_check_usage(hid->collection[i - 1].usage); - if (ret != -1 && !pidff->reports[ret]) { - debug("found usage 0x%02x from collection array", - pidff_reports[ret]); - pidff->reports[ret] = report; - } - } -} - -/* - * Test if the required reports have been found - */ -static int pidff_reports_ok(struct pidff_device *pidff) -{ - int i; - - for (i = 0; i <= PID_REQUIRED_REPORTS; i++) { - if (!pidff->reports[i]) { - debug("%d missing", i); - return 0; - } - } - - return 1; -} - -/* - * Find a field with a specific usage within a report - */ -static struct hid_field *pidff_find_special_field(struct hid_report *report, - int usage, int enforce_min) -{ - int i; - - for (i = 0; i < report->maxfield; i++) { - if (report->field[i]->logical == (HID_UP_PID | usage) && - report->field[i]->report_count > 0) { - if (!enforce_min || - report->field[i]->logical_minimum == 1) - return report->field[i]; - else { - printk(KERN_ERR "hid-pidff: logical_minimum " - "is not 1 as it should be\n"); - return NULL; - } - } - } - return NULL; -} - -/* - * Fill a pidff->*_id struct table - */ -static int pidff_find_special_keys(int *keys, struct hid_field *fld, - const u8 *usagetable, int count) -{ - - int i, j; - int found = 0; - - for (i = 0; i < count; i++) { - for (j = 0; j < fld->maxusage; j++) { - if (fld->usage[j].hid == (HID_UP_PID | usagetable[i])) { - keys[i] = j + 1; - found++; - break; - } - } - } - return found; -} - -#define PIDFF_FIND_SPECIAL_KEYS(keys, field, name) \ - pidff_find_special_keys(pidff->keys, pidff->field, pidff_ ## name, \ - sizeof(pidff_ ## name)) - -/* - * Find and check the special fields - */ -static int pidff_find_special_fields(struct pidff_device *pidff) -{ - debug("finding special fields"); - - pidff->create_new_effect_type = - pidff_find_special_field(pidff->reports[PID_CREATE_NEW_EFFECT], - 0x25, 1); - pidff->set_effect_type = - pidff_find_special_field(pidff->reports[PID_SET_EFFECT], - 0x25, 1); - pidff->effect_direction = - pidff_find_special_field(pidff->reports[PID_SET_EFFECT], - 0x57, 0); - pidff->device_control = - pidff_find_special_field(pidff->reports[PID_DEVICE_CONTROL], - 0x96, 1); - pidff->block_load_status = - pidff_find_special_field(pidff->reports[PID_BLOCK_LOAD], - 0x8b, 1); - pidff->effect_operation_status = - pidff_find_special_field(pidff->reports[PID_EFFECT_OPERATION], - 0x78, 1); - - debug("search done"); - - if (!pidff->create_new_effect_type || !pidff->set_effect_type) { - printk(KERN_ERR "hid-pidff: effect lists not found\n"); - return -1; - } - - if (!pidff->effect_direction) { - printk(KERN_ERR "hid-pidff: direction field not found\n"); - return -1; - } - - if (!pidff->device_control) { - printk(KERN_ERR "hid-pidff: device control field not found\n"); - return -1; - } - - if (!pidff->block_load_status) { - printk(KERN_ERR - "hid-pidff: block load status field not found\n"); - return -1; - } - - if (!pidff->effect_operation_status) { - printk(KERN_ERR - "hid-pidff: effect operation field not found\n"); - return -1; - } - - pidff_find_special_keys(pidff->control_id, pidff->device_control, - pidff_device_control, - sizeof(pidff_device_control)); - - PIDFF_FIND_SPECIAL_KEYS(control_id, device_control, device_control); - - if (!PIDFF_FIND_SPECIAL_KEYS(type_id, create_new_effect_type, - effect_types)) { - printk(KERN_ERR "hid-pidff: no effect types found\n"); - return -1; - } - - if (PIDFF_FIND_SPECIAL_KEYS(status_id, block_load_status, - block_load_status) != - sizeof(pidff_block_load_status)) { - printk(KERN_ERR - "hidpidff: block load status identifiers not found\n"); - return -1; - } - - if (PIDFF_FIND_SPECIAL_KEYS(operation_id, effect_operation_status, - effect_operation_status) != - sizeof(pidff_effect_operation_status)) { - printk(KERN_ERR - "hidpidff: effect operation identifiers not found\n"); - return -1; - } - - return 0; -} - -/** - * Find the implemented effect types - */ -static int pidff_find_effects(struct pidff_device *pidff, - struct input_dev *dev) -{ - int i; - - for (i = 0; i < sizeof(pidff_effect_types); i++) { - int pidff_type = pidff->type_id[i]; - if (pidff->set_effect_type->usage[pidff_type].hid != - pidff->create_new_effect_type->usage[pidff_type].hid) { - printk(KERN_ERR "hid-pidff: " - "effect type number %d is invalid\n", i); - return -1; - } - } - - if (pidff->type_id[PID_CONSTANT]) - set_bit(FF_CONSTANT, dev->ffbit); - if (pidff->type_id[PID_RAMP]) - set_bit(FF_RAMP, dev->ffbit); - if (pidff->type_id[PID_SQUARE]) { - set_bit(FF_SQUARE, dev->ffbit); - set_bit(FF_PERIODIC, dev->ffbit); - } - if (pidff->type_id[PID_SINE]) { - set_bit(FF_SINE, dev->ffbit); - set_bit(FF_PERIODIC, dev->ffbit); - } - if (pidff->type_id[PID_TRIANGLE]) { - set_bit(FF_TRIANGLE, dev->ffbit); - set_bit(FF_PERIODIC, dev->ffbit); - } - if (pidff->type_id[PID_SAW_UP]) { - set_bit(FF_SAW_UP, dev->ffbit); - set_bit(FF_PERIODIC, dev->ffbit); - } - if (pidff->type_id[PID_SAW_DOWN]) { - set_bit(FF_SAW_DOWN, dev->ffbit); - set_bit(FF_PERIODIC, dev->ffbit); - } - if (pidff->type_id[PID_SPRING]) - set_bit(FF_SPRING, dev->ffbit); - if (pidff->type_id[PID_DAMPER]) - set_bit(FF_DAMPER, dev->ffbit); - if (pidff->type_id[PID_INERTIA]) - set_bit(FF_INERTIA, dev->ffbit); - if (pidff->type_id[PID_FRICTION]) - set_bit(FF_FRICTION, dev->ffbit); - - return 0; - -} - -#define PIDFF_FIND_FIELDS(name, report, strict) \ - pidff_find_fields(pidff->name, pidff_ ## name, \ - pidff->reports[report], \ - sizeof(pidff_ ## name), strict) - -/* - * Fill and check the pidff_usages - */ -static int pidff_init_fields(struct pidff_device *pidff, struct input_dev *dev) -{ - int envelope_ok = 0; - - if (PIDFF_FIND_FIELDS(set_effect, PID_SET_EFFECT, 1)) { - printk(KERN_ERR - "hid-pidff: unknown set_effect report layout\n"); - return -ENODEV; - } - - PIDFF_FIND_FIELDS(block_load, PID_BLOCK_LOAD, 0); - if (!pidff->block_load[PID_EFFECT_BLOCK_INDEX].value) { - printk(KERN_ERR - "hid-pidff: unknown pid_block_load report layout\n"); - return -ENODEV; - } - - if (PIDFF_FIND_FIELDS(effect_operation, PID_EFFECT_OPERATION, 1)) { - printk(KERN_ERR - "hid-pidff: unknown effect_operation report layout\n"); - return -ENODEV; - } - - if (PIDFF_FIND_FIELDS(block_free, PID_BLOCK_FREE, 1)) { - printk(KERN_ERR - "hid-pidff: unknown pid_block_free report layout\n"); - return -ENODEV; - } - - if (!PIDFF_FIND_FIELDS(set_envelope, PID_SET_ENVELOPE, 1)) - envelope_ok = 1; - - if (pidff_find_special_fields(pidff) || pidff_find_effects(pidff, dev)) - return -ENODEV; - - if (!envelope_ok) { - if (test_and_clear_bit(FF_CONSTANT, dev->ffbit)) - printk(KERN_WARNING "hid-pidff: " - "has constant effect but no envelope\n"); - if (test_and_clear_bit(FF_RAMP, dev->ffbit)) - printk(KERN_WARNING "hid-pidff: " - "has ramp effect but no envelope\n"); - - if (test_and_clear_bit(FF_PERIODIC, dev->ffbit)) - printk(KERN_WARNING "hid-pidff: " - "has periodic effect but no envelope\n"); - } - - if (test_bit(FF_CONSTANT, dev->ffbit) && - PIDFF_FIND_FIELDS(set_constant, PID_SET_CONSTANT, 1)) { - printk(KERN_WARNING - "hid-pidff: unknown constant effect layout\n"); - clear_bit(FF_CONSTANT, dev->ffbit); - } - - if (test_bit(FF_RAMP, dev->ffbit) && - PIDFF_FIND_FIELDS(set_ramp, PID_SET_RAMP, 1)) { - printk(KERN_WARNING "hid-pidff: unknown ramp effect layout\n"); - clear_bit(FF_RAMP, dev->ffbit); - } - - if ((test_bit(FF_SPRING, dev->ffbit) || - test_bit(FF_DAMPER, dev->ffbit) || - test_bit(FF_FRICTION, dev->ffbit) || - test_bit(FF_INERTIA, dev->ffbit)) && - PIDFF_FIND_FIELDS(set_condition, PID_SET_CONDITION, 1)) { - printk(KERN_WARNING - "hid-pidff: unknown condition effect layout\n"); - clear_bit(FF_SPRING, dev->ffbit); - clear_bit(FF_DAMPER, dev->ffbit); - clear_bit(FF_FRICTION, dev->ffbit); - clear_bit(FF_INERTIA, dev->ffbit); - } - - if (test_bit(FF_PERIODIC, dev->ffbit) && - PIDFF_FIND_FIELDS(set_periodic, PID_SET_PERIODIC, 1)) { - printk(KERN_WARNING - "hid-pidff: unknown periodic effect layout\n"); - clear_bit(FF_PERIODIC, dev->ffbit); - } - - PIDFF_FIND_FIELDS(pool, PID_POOL, 0); - - if (!PIDFF_FIND_FIELDS(device_gain, PID_DEVICE_GAIN, 1)) - set_bit(FF_GAIN, dev->ffbit); - - return 0; -} - -/* - * Reset the device - */ -static void pidff_reset(struct pidff_device *pidff) -{ - struct hid_device *hid = pidff->hid; - int i = 0; - - pidff->device_control->value[0] = pidff->control_id[PID_RESET]; - /* We reset twice as sometimes hid_wait_io isn't waiting long enough */ - usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); - usbhid_wait_io(hid); - usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); - usbhid_wait_io(hid); - - pidff->device_control->value[0] = - pidff->control_id[PID_ENABLE_ACTUATORS]; - usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); - usbhid_wait_io(hid); - - /* pool report is sometimes messed up, refetch it */ - usbhid_submit_report(hid, pidff->reports[PID_POOL], USB_DIR_IN); - usbhid_wait_io(hid); - - if (pidff->pool[PID_SIMULTANEOUS_MAX].value) { - int sim_effects = pidff->pool[PID_SIMULTANEOUS_MAX].value[0]; - while (sim_effects < 2) { - if (i++ > 20) { - printk(KERN_WARNING "hid-pidff: device reports " - "%d simultaneous effects\n", - sim_effects); - break; - } - debug("pid_pool requested again"); - usbhid_submit_report(hid, pidff->reports[PID_POOL], - USB_DIR_IN); - usbhid_wait_io(hid); - } - } -} - -/* - * Test if autocenter modification is using the supported method - */ -static int pidff_check_autocenter(struct pidff_device *pidff, - struct input_dev *dev) -{ - int error; - - /* - * Let's find out if autocenter modification is supported - * Specification doesn't specify anything, so we request an - * effect upload and cancel it immediately. If the approved - * effect id was one above the minimum, then we assume the first - * effect id is a built-in spring type effect used for autocenter - */ - - error = pidff_request_effect_upload(pidff, 1); - if (error) { - printk(KERN_ERR "hid-pidff: upload request failed\n"); - return error; - } - - if (pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] == - pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum + 1) { - pidff_autocenter(pidff, 0xffff); - set_bit(FF_AUTOCENTER, dev->ffbit); - } else { - printk(KERN_NOTICE "hid-pidff: " - "device has unknown autocenter control method\n"); - } - - pidff_erase_pid(pidff, - pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]); - - return 0; - -} - -/* - * Check if the device is PID and initialize it - */ -int hid_pidff_init(struct hid_device *hid) -{ - struct pidff_device *pidff; - struct hid_input *hidinput = list_entry(hid->inputs.next, - struct hid_input, list); - struct input_dev *dev = hidinput->input; - struct ff_device *ff; - int max_effects; - int error; - - debug("starting pid init"); - - if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) { - debug("not a PID device, no output report"); - return -ENODEV; - } - - pidff = kzalloc(sizeof(*pidff), GFP_KERNEL); - if (!pidff) - return -ENOMEM; - - pidff->hid = hid; - - pidff_find_reports(hid, HID_OUTPUT_REPORT, pidff); - pidff_find_reports(hid, HID_FEATURE_REPORT, pidff); - - if (!pidff_reports_ok(pidff)) { - debug("reports not ok, aborting"); - error = -ENODEV; - goto fail; - } - - error = pidff_init_fields(pidff, dev); - if (error) - goto fail; - - pidff_reset(pidff); - - if (test_bit(FF_GAIN, dev->ffbit)) { - pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], 0xffff); - usbhid_submit_report(pidff->hid, pidff->reports[PID_DEVICE_GAIN], - USB_DIR_OUT); - } - - error = pidff_check_autocenter(pidff, dev); - if (error) - goto fail; - - max_effects = - pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_maximum - - pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum + - 1; - debug("max effects is %d", max_effects); - - if (max_effects > PID_EFFECTS_MAX) - max_effects = PID_EFFECTS_MAX; - - if (pidff->pool[PID_SIMULTANEOUS_MAX].value) - debug("max simultaneous effects is %d", - pidff->pool[PID_SIMULTANEOUS_MAX].value[0]); - - if (pidff->pool[PID_RAM_POOL_SIZE].value) - debug("device memory size is %d bytes", - pidff->pool[PID_RAM_POOL_SIZE].value[0]); - - if (pidff->pool[PID_DEVICE_MANAGED_POOL].value && - pidff->pool[PID_DEVICE_MANAGED_POOL].value[0] == 0) { - printk(KERN_NOTICE "hid-pidff: " - "device does not support device managed pool\n"); - goto fail; - } - - error = input_ff_create(dev, max_effects); - if (error) - goto fail; - - ff = dev->ff; - ff->private = pidff; - ff->upload = pidff_upload_effect; - ff->erase = pidff_erase_effect; - ff->set_gain = pidff_set_gain; - ff->set_autocenter = pidff_set_autocenter; - ff->playback = pidff_playback; - - printk(KERN_INFO "Force feedback for USB HID PID devices by " - "Anssi Hannula \n"); - - return 0; - - fail: - kfree(pidff); - return error; -} diff --git a/drivers/usb/input/hid-plff.c b/drivers/usb/input/hid-plff.c deleted file mode 100644 index 76d2e6e14db4..000000000000 --- a/drivers/usb/input/hid-plff.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Force feedback support for PantherLord USB/PS2 2in1 Adapter devices - * - * Copyright (c) 2007 Anssi Hannula - */ - -/* - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -/* #define DEBUG */ - -#define debug(format, arg...) pr_debug("hid-plff: " format "\n" , ## arg) - -#include -#include -#include -#include "usbhid.h" - -struct plff_device { - struct hid_report *report; -}; - -static int hid_plff_play(struct input_dev *dev, void *data, - struct ff_effect *effect) -{ - struct hid_device *hid = dev->private; - struct plff_device *plff = data; - int left, right; - - left = effect->u.rumble.strong_magnitude; - right = effect->u.rumble.weak_magnitude; - debug("called with 0x%04x 0x%04x", left, right); - - left = left * 0x7f / 0xffff; - right = right * 0x7f / 0xffff; - - plff->report->field[0]->value[2] = left; - plff->report->field[0]->value[3] = right; - debug("running with 0x%02x 0x%02x", left, right); - usbhid_submit_report(hid, plff->report, USB_DIR_OUT); - - return 0; -} - -int hid_plff_init(struct hid_device *hid) -{ - struct plff_device *plff; - struct hid_report *report; - struct hid_input *hidinput; - struct list_head *report_list = - &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct list_head *report_ptr = report_list; - struct input_dev *dev; - int error; - - /* The device contains 2 output reports (one for each - HID_QUIRK_MULTI_INPUT device), both containing 1 field, which - contains 4 ff00.0002 usages and 4 16bit absolute values. - - The 2 input reports also contain a field which contains - 8 ff00.0001 usages and 8 boolean values. Their meaning is - currently unknown. */ - - if (list_empty(report_list)) { - printk(KERN_ERR "hid-plff: no output reports found\n"); - return -ENODEV; - } - - list_for_each_entry(hidinput, &hid->inputs, list) { - - report_ptr = report_ptr->next; - - if (report_ptr == report_list) { - printk(KERN_ERR "hid-plff: required output report is missing\n"); - return -ENODEV; - } - - report = list_entry(report_ptr, struct hid_report, list); - if (report->maxfield < 1) { - printk(KERN_ERR "hid-plff: no fields in the report\n"); - return -ENODEV; - } - - if (report->field[0]->report_count < 4) { - printk(KERN_ERR "hid-plff: not enough values in the field\n"); - return -ENODEV; - } - - plff = kzalloc(sizeof(struct plff_device), GFP_KERNEL); - if (!plff) - return -ENOMEM; - - dev = hidinput->input; - - set_bit(FF_RUMBLE, dev->ffbit); - - error = input_ff_create_memless(dev, plff, hid_plff_play); - if (error) { - kfree(plff); - return error; - } - - plff->report = report; - plff->report->field[0]->value[0] = 0x00; - plff->report->field[0]->value[1] = 0x00; - plff->report->field[0]->value[2] = 0x00; - plff->report->field[0]->value[3] = 0x00; - usbhid_submit_report(hid, plff->report, USB_DIR_OUT); - } - - printk(KERN_INFO "hid-plff: Force feedback for PantherLord USB/PS2 " - "2in1 Adapters by Anssi Hannula \n"); - - return 0; -} diff --git a/drivers/usb/input/hid-tmff.c b/drivers/usb/input/hid-tmff.c deleted file mode 100644 index ab67331620d0..000000000000 --- a/drivers/usb/input/hid-tmff.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Force feedback support for various HID compliant devices by ThrustMaster: - * ThrustMaster FireStorm Dual Power 2 - * and possibly others whose device ids haven't been added. - * - * Modified to support ThrustMaster devices by Zinx Verituse - * on 2003-01-25 from the Logitech force feedback driver, - * which is by Johann Deneux. - * - * Copyright (c) 2003 Zinx Verituse - * Copyright (c) 2002 Johann Deneux - */ - -/* - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - -#undef DEBUG -#include - -#include -#include "usbhid.h" - -/* Usages for thrustmaster devices I know about */ -#define THRUSTMASTER_USAGE_RUMBLE_LR (HID_UP_GENDESK | 0xbb) - - -struct tmff_device { - struct hid_report *report; - struct hid_field *rumble; -}; - -/* Changes values from 0 to 0xffff into values from minimum to maximum */ -static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum) -{ - int ret; - - ret = (in * (maximum - minimum) / 0xffff) + minimum; - if (ret < minimum) - return minimum; - if (ret > maximum) - return maximum; - return ret; -} - -static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect) -{ - struct hid_device *hid = dev->private; - struct tmff_device *tmff = data; - int left, right; /* Rumbling */ - - left = hid_tmff_scale(effect->u.rumble.weak_magnitude, - tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); - right = hid_tmff_scale(effect->u.rumble.strong_magnitude, - tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); - - tmff->rumble->value[0] = left; - tmff->rumble->value[1] = right; - dbg("(left,right)=(%08x, %08x)", left, right); - usbhid_submit_report(hid, tmff->report, USB_DIR_OUT); - - return 0; -} - -int hid_tmff_init(struct hid_device *hid) -{ - struct tmff_device *tmff; - struct list_head *pos; - struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); - struct input_dev *input_dev = hidinput->input; - int error; - - tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL); - if (!tmff) - return -ENOMEM; - - /* Find the report to use */ - __list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) { - struct hid_report *report = (struct hid_report *)pos; - int fieldnum; - - for (fieldnum = 0; fieldnum < report->maxfield; ++fieldnum) { - struct hid_field *field = report->field[fieldnum]; - - if (field->maxusage <= 0) - continue; - - switch (field->usage[0].hid) { - case THRUSTMASTER_USAGE_RUMBLE_LR: - if (field->report_count < 2) { - warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with report_count < 2"); - continue; - } - - if (field->logical_maximum == field->logical_minimum) { - warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with logical_maximum == logical_minimum"); - continue; - } - - if (tmff->report && tmff->report != report) { - warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR in other report"); - continue; - } - - if (tmff->rumble && tmff->rumble != field) { - warn("ignoring duplicate THRUSTMASTER_USAGE_RUMBLE_LR"); - continue; - } - - tmff->report = report; - tmff->rumble = field; - - set_bit(FF_RUMBLE, input_dev->ffbit); - break; - - default: - warn("ignoring unknown output usage %08x", field->usage[0].hid); - continue; - } - } - } - - error = input_ff_create_memless(input_dev, tmff, hid_tmff_play); - if (error) { - kfree(tmff); - return error; - } - - info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse "); - - return 0; -} - diff --git a/drivers/usb/input/hid-zpff.c b/drivers/usb/input/hid-zpff.c deleted file mode 100644 index 7bd8238ca212..000000000000 --- a/drivers/usb/input/hid-zpff.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Force feedback support for Zeroplus based devices - * - * Copyright (c) 2005, 2006 Anssi Hannula - */ - -/* - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -/* #define DEBUG */ - -#define debug(format, arg...) pr_debug("hid-zpff: " format "\n" , ## arg) - -#include -#include -#include -#include "usbhid.h" - -struct zpff_device { - struct hid_report *report; -}; - -static int hid_zpff_play(struct input_dev *dev, void *data, - struct ff_effect *effect) -{ - struct hid_device *hid = dev->private; - struct zpff_device *zpff = data; - int left, right; - - /* - * The following is specified the other way around in the Zeroplus - * datasheet but the order below is correct for the XFX Executioner; - * however it is possible that the XFX Executioner is an exception - */ - - left = effect->u.rumble.strong_magnitude; - right = effect->u.rumble.weak_magnitude; - debug("called with 0x%04x 0x%04x", left, right); - - left = left * 0x7f / 0xffff; - right = right * 0x7f / 0xffff; - - zpff->report->field[2]->value[0] = left; - zpff->report->field[3]->value[0] = right; - debug("running with 0x%02x 0x%02x", left, right); - usbhid_submit_report(hid, zpff->report, USB_DIR_OUT); - - return 0; -} - -int hid_zpff_init(struct hid_device *hid) -{ - struct zpff_device *zpff; - struct hid_report *report; - struct hid_input *hidinput = list_entry(hid->inputs.next, - struct hid_input, list); - struct list_head *report_list = - &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct input_dev *dev = hidinput->input; - int error; - - if (list_empty(report_list)) { - printk(KERN_ERR "hid-zpff: no output report found\n"); - return -ENODEV; - } - - report = list_entry(report_list->next, struct hid_report, list); - - if (report->maxfield < 4) { - printk(KERN_ERR "hid-zpff: not enough fields in report\n"); - return -ENODEV; - } - - zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL); - if (!zpff) - return -ENOMEM; - - set_bit(FF_RUMBLE, dev->ffbit); - - error = input_ff_create_memless(dev, zpff, hid_zpff_play); - if (error) { - kfree(zpff); - return error; - } - - zpff->report = report; - zpff->report->field[0]->value[0] = 0x00; - zpff->report->field[1]->value[0] = 0x02; - zpff->report->field[2]->value[0] = 0x00; - zpff->report->field[3]->value[0] = 0x00; - usbhid_submit_report(hid, zpff->report, USB_DIR_OUT); - - printk(KERN_INFO "Force feedback for Zeroplus based devices by " - "Anssi Hannula \n"); - - return 0; -} diff --git a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c deleted file mode 100644 index a8b3d66cd498..000000000000 --- a/drivers/usb/input/hiddev.c +++ /dev/null @@ -1,847 +0,0 @@ -/* - * Copyright (c) 2001 Paul Stewart - * Copyright (c) 2001 Vojtech Pavlik - * - * HID char devices, giving access to raw HID device events. - * - */ - -/* - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to Paul Stewart - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "usbhid.h" - -#ifdef CONFIG_USB_DYNAMIC_MINORS -#define HIDDEV_MINOR_BASE 0 -#define HIDDEV_MINORS 256 -#else -#define HIDDEV_MINOR_BASE 96 -#define HIDDEV_MINORS 16 -#endif -#define HIDDEV_BUFFER_SIZE 64 - -struct hiddev { - int exist; - int open; - wait_queue_head_t wait; - struct hid_device *hid; - struct list_head list; -}; - -struct hiddev_list { - struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE]; - int head; - int tail; - unsigned flags; - struct fasync_struct *fasync; - struct hiddev *hiddev; - struct list_head node; -}; - -static struct hiddev *hiddev_table[HIDDEV_MINORS]; - -/* - * Find a report, given the report's type and ID. The ID can be specified - * indirectly by REPORT_ID_FIRST (which returns the first report of the given - * type) or by (REPORT_ID_NEXT | old_id), which returns the next report of the - * given type which follows old_id. - */ -static struct hid_report * -hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo) -{ - unsigned int flags = rinfo->report_id & ~HID_REPORT_ID_MASK; - unsigned int rid = rinfo->report_id & HID_REPORT_ID_MASK; - struct hid_report_enum *report_enum; - struct hid_report *report; - struct list_head *list; - - if (rinfo->report_type < HID_REPORT_TYPE_MIN || - rinfo->report_type > HID_REPORT_TYPE_MAX) - return NULL; - - report_enum = hid->report_enum + - (rinfo->report_type - HID_REPORT_TYPE_MIN); - - switch (flags) { - case 0: /* Nothing to do -- report_id is already set correctly */ - break; - - case HID_REPORT_ID_FIRST: - if (list_empty(&report_enum->report_list)) - return NULL; - - list = report_enum->report_list.next; - report = list_entry(list, struct hid_report, list); - rinfo->report_id = report->id; - break; - - case HID_REPORT_ID_NEXT: - report = report_enum->report_id_hash[rid]; - if (!report) - return NULL; - - list = report->list.next; - if (list == &report_enum->report_list) - return NULL; - - report = list_entry(list, struct hid_report, list); - rinfo->report_id = report->id; - break; - - default: - return NULL; - } - - return report_enum->report_id_hash[rinfo->report_id]; -} - -/* - * Perform an exhaustive search of the report table for a usage, given its - * type and usage id. - */ -static struct hid_field * -hiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref) -{ - int i, j; - struct hid_report *report; - struct hid_report_enum *report_enum; - struct hid_field *field; - - if (uref->report_type < HID_REPORT_TYPE_MIN || - uref->report_type > HID_REPORT_TYPE_MAX) - return NULL; - - report_enum = hid->report_enum + - (uref->report_type - HID_REPORT_TYPE_MIN); - - list_for_each_entry(report, &report_enum->report_list, list) { - for (i = 0; i < report->maxfield; i++) { - field = report->field[i]; - for (j = 0; j < field->maxusage; j++) { - if (field->usage[j].hid == uref->usage_code) { - uref->report_id = report->id; - uref->field_index = i; - uref->usage_index = j; - return field; - } - } - } - } - - return NULL; -} - -static void hiddev_send_event(struct hid_device *hid, - struct hiddev_usage_ref *uref) -{ - struct hiddev *hiddev = hid->hiddev; - struct hiddev_list *list; - - list_for_each_entry(list, &hiddev->list, node) { - if (uref->field_index != HID_FIELD_INDEX_NONE || - (list->flags & HIDDEV_FLAG_REPORT) != 0) { - list->buffer[list->head] = *uref; - list->head = (list->head + 1) & - (HIDDEV_BUFFER_SIZE - 1); - kill_fasync(&list->fasync, SIGIO, POLL_IN); - } - } - - wake_up_interruptible(&hiddev->wait); -} - -/* - * This is where hid.c calls into hiddev to pass an event that occurred over - * the interrupt pipe - */ -void hiddev_hid_event(struct hid_device *hid, struct hid_field *field, - struct hid_usage *usage, __s32 value) -{ - unsigned type = field->report_type; - struct hiddev_usage_ref uref; - - uref.report_type = - (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : - ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : - ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0)); - uref.report_id = field->report->id; - uref.field_index = field->index; - uref.usage_index = (usage - field->usage); - uref.usage_code = usage->hid; - uref.value = value; - - hiddev_send_event(hid, &uref); -} -EXPORT_SYMBOL_GPL(hiddev_hid_event); - -void hiddev_report_event(struct hid_device *hid, struct hid_report *report) -{ - unsigned type = report->type; - struct hiddev_usage_ref uref; - - memset(&uref, 0, sizeof(uref)); - uref.report_type = - (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : - ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : - ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0)); - uref.report_id = report->id; - uref.field_index = HID_FIELD_INDEX_NONE; - - hiddev_send_event(hid, &uref); -} - -/* - * fasync file op - */ -static int hiddev_fasync(int fd, struct file *file, int on) -{ - int retval; - struct hiddev_list *list = file->private_data; - - retval = fasync_helper(fd, file, on, &list->fasync); - - return retval < 0 ? retval : 0; -} - - -/* - * release file op - */ -static int hiddev_release(struct inode * inode, struct file * file) -{ - struct hiddev_list *list = file->private_data; - - hiddev_fasync(-1, file, 0); - list_del(&list->node); - - if (!--list->hiddev->open) { - if (list->hiddev->exist) - usbhid_close(list->hiddev->hid); - else - kfree(list->hiddev); - } - - kfree(list); - - return 0; -} - -/* - * open file op - */ -static int hiddev_open(struct inode *inode, struct file *file) -{ - struct hiddev_list *list; - - int i = iminor(inode) - HIDDEV_MINOR_BASE; - - if (i >= HIDDEV_MINORS || !hiddev_table[i]) - return -ENODEV; - - if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL))) - return -ENOMEM; - - list->hiddev = hiddev_table[i]; - list_add_tail(&list->node, &hiddev_table[i]->list); - file->private_data = list; - - if (!list->hiddev->open++) - if (list->hiddev->exist) - usbhid_open(hiddev_table[i]->hid); - - return 0; -} - -/* - * "write" file op - */ -static ssize_t hiddev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos) -{ - return -EINVAL; -} - -/* - * "read" file op - */ -static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos) -{ - DECLARE_WAITQUEUE(wait, current); - struct hiddev_list *list = file->private_data; - int event_size; - int retval = 0; - - event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ? - sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event); - - if (count < event_size) - return 0; - - while (retval == 0) { - if (list->head == list->tail) { - add_wait_queue(&list->hiddev->wait, &wait); - set_current_state(TASK_INTERRUPTIBLE); - - while (list->head == list->tail) { - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - if (!list->hiddev->exist) { - retval = -EIO; - break; - } - - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - } - - set_current_state(TASK_RUNNING); - remove_wait_queue(&list->hiddev->wait, &wait); - } - - if (retval) - return retval; - - - while (list->head != list->tail && - retval + event_size <= count) { - if ((list->flags & HIDDEV_FLAG_UREF) == 0) { - if (list->buffer[list->tail].field_index != - HID_FIELD_INDEX_NONE) { - struct hiddev_event event; - event.hid = list->buffer[list->tail].usage_code; - event.value = list->buffer[list->tail].value; - if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event))) - return -EFAULT; - retval += sizeof(struct hiddev_event); - } - } else { - if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE || - (list->flags & HIDDEV_FLAG_REPORT) != 0) { - if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref))) - return -EFAULT; - retval += sizeof(struct hiddev_usage_ref); - } - } - list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1); - } - - } - - return retval; -} - -/* - * "poll" file op - * No kernel lock - fine - */ -static unsigned int hiddev_poll(struct file *file, poll_table *wait) -{ - struct hiddev_list *list = file->private_data; - - poll_wait(file, &list->hiddev->wait, wait); - if (list->head != list->tail) - return POLLIN | POLLRDNORM; - if (!list->hiddev->exist) - return POLLERR | POLLHUP; - return 0; -} - -/* - * "ioctl" file op - */ -static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct hiddev_list *list = file->private_data; - struct hiddev *hiddev = list->hiddev; - struct hid_device *hid = hiddev->hid; - struct usb_device *dev = hid_to_usb_dev(hid); - struct hiddev_collection_info cinfo; - struct hiddev_report_info rinfo; - struct hiddev_field_info finfo; - struct hiddev_usage_ref_multi *uref_multi = NULL; - struct hiddev_usage_ref *uref; - struct hiddev_devinfo dinfo; - struct hid_report *report; - struct hid_field *field; - struct usbhid_device *usbhid = hid->driver_data; - void __user *user_arg = (void __user *)arg; - int i; - - if (!hiddev->exist) - return -EIO; - - switch (cmd) { - - case HIDIOCGVERSION: - return put_user(HID_VERSION, (int __user *)arg); - - case HIDIOCAPPLICATION: - if (arg < 0 || arg >= hid->maxapplication) - return -EINVAL; - - for (i = 0; i < hid->maxcollection; i++) - if (hid->collection[i].type == - HID_COLLECTION_APPLICATION && arg-- == 0) - break; - - if (i == hid->maxcollection) - return -EINVAL; - - return hid->collection[i].usage; - - case HIDIOCGDEVINFO: - dinfo.bustype = BUS_USB; - dinfo.busnum = dev->bus->busnum; - dinfo.devnum = dev->devnum; - dinfo.ifnum = usbhid->ifnum; - dinfo.vendor = le16_to_cpu(dev->descriptor.idVendor); - dinfo.product = le16_to_cpu(dev->descriptor.idProduct); - dinfo.version = le16_to_cpu(dev->descriptor.bcdDevice); - dinfo.num_applications = hid->maxapplication; - if (copy_to_user(user_arg, &dinfo, sizeof(dinfo))) - return -EFAULT; - - return 0; - - case HIDIOCGFLAG: - if (put_user(list->flags, (int __user *)arg)) - return -EFAULT; - - return 0; - - case HIDIOCSFLAG: - { - int newflags; - if (get_user(newflags, (int __user *)arg)) - return -EFAULT; - - if ((newflags & ~HIDDEV_FLAGS) != 0 || - ((newflags & HIDDEV_FLAG_REPORT) != 0 && - (newflags & HIDDEV_FLAG_UREF) == 0)) - return -EINVAL; - - list->flags = newflags; - - return 0; - } - - case HIDIOCGSTRING: - { - int idx, len; - char *buf; - - if (get_user(idx, (int __user *)arg)) - return -EFAULT; - - if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL) - return -ENOMEM; - - if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) { - kfree(buf); - return -EINVAL; - } - - if (copy_to_user(user_arg+sizeof(int), buf, len+1)) { - kfree(buf); - return -EFAULT; - } - - kfree(buf); - - return len; - } - - case HIDIOCINITREPORT: - usbhid_init_reports(hid); - - return 0; - - case HIDIOCGREPORT: - if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) - return -EFAULT; - - if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT) - return -EINVAL; - - if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) - return -EINVAL; - - usbhid_submit_report(hid, report, USB_DIR_IN); - usbhid_wait_io(hid); - - return 0; - - case HIDIOCSREPORT: - if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) - return -EFAULT; - - if (rinfo.report_type == HID_REPORT_TYPE_INPUT) - return -EINVAL; - - if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) - return -EINVAL; - - usbhid_submit_report(hid, report, USB_DIR_OUT); - usbhid_wait_io(hid); - - return 0; - - case HIDIOCGREPORTINFO: - if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) - return -EFAULT; - - if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) - return -EINVAL; - - rinfo.num_fields = report->maxfield; - - if (copy_to_user(user_arg, &rinfo, sizeof(rinfo))) - return -EFAULT; - - return 0; - - case HIDIOCGFIELDINFO: - if (copy_from_user(&finfo, user_arg, sizeof(finfo))) - return -EFAULT; - rinfo.report_type = finfo.report_type; - rinfo.report_id = finfo.report_id; - if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) - return -EINVAL; - - if (finfo.field_index >= report->maxfield) - return -EINVAL; - - field = report->field[finfo.field_index]; - memset(&finfo, 0, sizeof(finfo)); - finfo.report_type = rinfo.report_type; - finfo.report_id = rinfo.report_id; - finfo.field_index = field->report_count - 1; - finfo.maxusage = field->maxusage; - finfo.flags = field->flags; - finfo.physical = field->physical; - finfo.logical = field->logical; - finfo.application = field->application; - finfo.logical_minimum = field->logical_minimum; - finfo.logical_maximum = field->logical_maximum; - finfo.physical_minimum = field->physical_minimum; - finfo.physical_maximum = field->physical_maximum; - finfo.unit_exponent = field->unit_exponent; - finfo.unit = field->unit; - - if (copy_to_user(user_arg, &finfo, sizeof(finfo))) - return -EFAULT; - - return 0; - - case HIDIOCGUCODE: - uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL); - if (!uref_multi) - return -ENOMEM; - uref = &uref_multi->uref; - if (copy_from_user(uref, user_arg, sizeof(*uref))) - goto fault; - - rinfo.report_type = uref->report_type; - rinfo.report_id = uref->report_id; - if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) - goto inval; - - if (uref->field_index >= report->maxfield) - goto inval; - - field = report->field[uref->field_index]; - if (uref->usage_index >= field->maxusage) - goto inval; - - uref->usage_code = field->usage[uref->usage_index].hid; - - if (copy_to_user(user_arg, uref, sizeof(*uref))) - goto fault; - - kfree(uref_multi); - return 0; - - case HIDIOCGUSAGE: - case HIDIOCSUSAGE: - case HIDIOCGUSAGES: - case HIDIOCSUSAGES: - case HIDIOCGCOLLECTIONINDEX: - uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL); - if (!uref_multi) - return -ENOMEM; - uref = &uref_multi->uref; - if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) { - if (copy_from_user(uref_multi, user_arg, - sizeof(*uref_multi))) - goto fault; - } else { - if (copy_from_user(uref, user_arg, sizeof(*uref))) - goto fault; - } - - if (cmd != HIDIOCGUSAGE && - cmd != HIDIOCGUSAGES && - uref->report_type == HID_REPORT_TYPE_INPUT) - goto inval; - - if (uref->report_id == HID_REPORT_ID_UNKNOWN) { - field = hiddev_lookup_usage(hid, uref); - if (field == NULL) - goto inval; - } else { - rinfo.report_type = uref->report_type; - rinfo.report_id = uref->report_id; - if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) - goto inval; - - if (uref->field_index >= report->maxfield) - goto inval; - - field = report->field[uref->field_index]; - - if (cmd == HIDIOCGCOLLECTIONINDEX) { - if (uref->usage_index >= field->maxusage) - goto inval; - } else if (uref->usage_index >= field->report_count) - goto inval; - - else if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) && - (uref_multi->num_values > HID_MAX_MULTI_USAGES || - uref->usage_index + uref_multi->num_values > field->report_count)) - goto inval; - } - - switch (cmd) { - case HIDIOCGUSAGE: - uref->value = field->value[uref->usage_index]; - if (copy_to_user(user_arg, uref, sizeof(*uref))) - goto fault; - goto goodreturn; - - case HIDIOCSUSAGE: - field->value[uref->usage_index] = uref->value; - goto goodreturn; - - case HIDIOCGCOLLECTIONINDEX: - kfree(uref_multi); - return field->usage[uref->usage_index].collection_index; - case HIDIOCGUSAGES: - for (i = 0; i < uref_multi->num_values; i++) - uref_multi->values[i] = - field->value[uref->usage_index + i]; - if (copy_to_user(user_arg, uref_multi, - sizeof(*uref_multi))) - goto fault; - goto goodreturn; - case HIDIOCSUSAGES: - for (i = 0; i < uref_multi->num_values; i++) - field->value[uref->usage_index + i] = - uref_multi->values[i]; - goto goodreturn; - } - -goodreturn: - kfree(uref_multi); - return 0; -fault: - kfree(uref_multi); - return -EFAULT; -inval: - kfree(uref_multi); - return -EINVAL; - - case HIDIOCGCOLLECTIONINFO: - if (copy_from_user(&cinfo, user_arg, sizeof(cinfo))) - return -EFAULT; - - if (cinfo.index >= hid->maxcollection) - return -EINVAL; - - cinfo.type = hid->collection[cinfo.index].type; - cinfo.usage = hid->collection[cinfo.index].usage; - cinfo.level = hid->collection[cinfo.index].level; - - if (copy_to_user(user_arg, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - default: - - if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) - return -EINVAL; - - if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) { - int len; - if (!hid->name) - return 0; - len = strlen(hid->name) + 1; - if (len > _IOC_SIZE(cmd)) - len = _IOC_SIZE(cmd); - return copy_to_user(user_arg, hid->name, len) ? - -EFAULT : len; - } - - if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGPHYS(0))) { - int len; - if (!hid->phys) - return 0; - len = strlen(hid->phys) + 1; - if (len > _IOC_SIZE(cmd)) - len = _IOC_SIZE(cmd); - return copy_to_user(user_arg, hid->phys, len) ? - -EFAULT : len; - } - } - return -EINVAL; -} - -static const struct file_operations hiddev_fops = { - .owner = THIS_MODULE, - .read = hiddev_read, - .write = hiddev_write, - .poll = hiddev_poll, - .open = hiddev_open, - .release = hiddev_release, - .ioctl = hiddev_ioctl, - .fasync = hiddev_fasync, -}; - -static struct usb_class_driver hiddev_class = { - .name = "hiddev%d", - .fops = &hiddev_fops, - .minor_base = HIDDEV_MINOR_BASE, -}; - -/* - * This is where hid.c calls us to connect a hid device to the hiddev driver - */ -int hiddev_connect(struct hid_device *hid) -{ - struct hiddev *hiddev; - struct usbhid_device *usbhid = hid->driver_data; - int i; - int retval; - - for (i = 0; i < hid->maxcollection; i++) - if (hid->collection[i].type == - HID_COLLECTION_APPLICATION && - !IS_INPUT_APPLICATION(hid->collection[i].usage)) - break; - - if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDDEV) == 0) - return -1; - - if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL))) - return -1; - - retval = usb_register_dev(usbhid->intf, &hiddev_class); - if (retval) { - err("Not able to get a minor for this device."); - kfree(hiddev); - return -1; - } - - init_waitqueue_head(&hiddev->wait); - INIT_LIST_HEAD(&hiddev->list); - hiddev->hid = hid; - hiddev->exist = 1; - - hid->minor = usbhid->intf->minor; - hid->hiddev = hiddev; - - hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev; - - return 0; -} - -/* - * This is where hid.c calls us to disconnect a hiddev device from the - * corresponding hid device (usually because the usb device has disconnected) - */ -static struct usb_class_driver hiddev_class; -void hiddev_disconnect(struct hid_device *hid) -{ - struct hiddev *hiddev = hid->hiddev; - struct usbhid_device *usbhid = hid->driver_data; - - hiddev->exist = 0; - - hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL; - usb_deregister_dev(usbhid->intf, &hiddev_class); - - if (hiddev->open) { - usbhid_close(hiddev->hid); - wake_up_interruptible(&hiddev->wait); - } else { - kfree(hiddev); - } -} - -/* Currently this driver is a USB driver. It's not a conventional one in - * the sense that it doesn't probe at the USB level. Instead it waits to - * be connected by HID through the hiddev_connect / hiddev_disconnect - * routines. The reason to register as a USB device is to gain part of the - * minor number space from the USB major. - * - * In theory, should the HID code be generalized to more than one physical - * medium (say, IEEE 1384), this driver will probably need to register its - * own major number, and in doing so, no longer need to register with USB. - * At that point the probe routine and hiddev_driver struct below will no - * longer be useful. - */ - - -/* We never attach in this manner, and rely on HID to connect us. This - * is why there is no disconnect routine defined in the usb_driver either. - */ -static int hiddev_usbd_probe(struct usb_interface *intf, - const struct usb_device_id *hiddev_info) -{ - return -ENODEV; -} - - -static /* const */ struct usb_driver hiddev_driver = { - .name = "hiddev", - .probe = hiddev_usbd_probe, -}; - -int __init hiddev_init(void) -{ - return usb_register(&hiddev_driver); -} - -void hiddev_exit(void) -{ - usb_deregister(&hiddev_driver); -} diff --git a/drivers/usb/input/usbhid.h b/drivers/usb/input/usbhid.h deleted file mode 100644 index 0023f96d4294..000000000000 --- a/drivers/usb/input/usbhid.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef __USBHID_H -#define __USBHID_H - -/* - * Copyright (c) 1999 Andreas Gal - * Copyright (c) 2000-2001 Vojtech Pavlik - * Copyright (c) 2006 Jiri Kosina - */ - -/* - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include -#include -#include -#include - -/* API provided by hid-core.c for USB HID drivers */ -int usbhid_wait_io(struct hid_device* hid); -void usbhid_close(struct hid_device *hid); -int usbhid_open(struct hid_device *hid); -void usbhid_init_reports(struct hid_device *hid); -void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir); - -/* - * USB-specific HID struct, to be pointed to - * from struct hid_device->driver_data - */ - -struct usbhid_device { - struct hid_device *hid; /* pointer to corresponding HID dev */ - - struct usb_interface *intf; /* USB interface */ - int ifnum; /* USB interface number */ - - unsigned int bufsize; /* URB buffer size */ - - struct urb *urbin; /* Input URB */ - char *inbuf; /* Input buffer */ - dma_addr_t inbuf_dma; /* Input buffer dma */ - spinlock_t inlock; /* Input fifo spinlock */ - - struct urb *urbctrl; /* Control URB */ - struct usb_ctrlrequest *cr; /* Control request struct */ - dma_addr_t cr_dma; /* Control request struct dma */ - struct hid_control_fifo ctrl[HID_CONTROL_FIFO_SIZE]; /* Control fifo */ - unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */ - char *ctrlbuf; /* Control buffer */ - dma_addr_t ctrlbuf_dma; /* Control buffer dma */ - spinlock_t ctrllock; /* Control fifo spinlock */ - - struct urb *urbout; /* Output URB */ - struct hid_report *out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */ - unsigned char outhead, outtail; /* Output pipe fifo head & tail */ - char *outbuf; /* Output buffer */ - dma_addr_t outbuf_dma; /* Output buffer dma */ - spinlock_t outlock; /* Output fifo spinlock */ - - unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */ - struct timer_list io_retry; /* Retry timer */ - unsigned long stop_retry; /* Time to give up, in jiffies */ - unsigned int retry_delay; /* Delay length in ms */ - struct work_struct reset_work; /* Task context for resets */ - -}; - -#define hid_to_usb_dev(hid_dev) \ - container_of(hid_dev->dev->parent, struct usb_device, dev) - -#endif - diff --git a/drivers/usb/input/usbkbd.c b/drivers/usb/input/usbkbd.c deleted file mode 100644 index 3749f4a235f9..000000000000 --- a/drivers/usb/input/usbkbd.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * $Id: usbkbd.c,v 1.27 2001/12/27 10:37:41 vojtech Exp $ - * - * Copyright (c) 1999-2001 Vojtech Pavlik - * - * USB HIDBP Keyboard support - */ - -/* - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include - -/* - * Version Information - */ -#define DRIVER_VERSION "" -#define DRIVER_AUTHOR "Vojtech Pavlik " -#define DRIVER_DESC "USB HID Boot Protocol keyboard driver" -#define DRIVER_LICENSE "GPL" - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE(DRIVER_LICENSE); - -static unsigned char usb_kbd_keycode[256] = { - 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, - 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, - 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, - 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64, - 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106, - 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, - 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190, - 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113, - 115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0, - 122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113, - 150,158,159,128,136,177,178,176,142,152,173,140 -}; - -struct usb_kbd { - struct input_dev *dev; - struct usb_device *usbdev; - unsigned char old[8]; - struct urb *irq, *led; - unsigned char newleds; - char name[128]; - char phys[64]; - - unsigned char *new; - struct usb_ctrlrequest *cr; - unsigned char *leds; - dma_addr_t cr_dma; - dma_addr_t new_dma; - dma_addr_t leds_dma; -}; - -static void usb_kbd_irq(struct urb *urb) -{ - struct usb_kbd *kbd = urb->context; - int i; - - switch (urb->status) { - case 0: /* success */ - break; - case -ECONNRESET: /* unlink */ - case -ENOENT: - case -ESHUTDOWN: - return; - /* -EPIPE: should clear the halt */ - default: /* error */ - goto resubmit; - } - - for (i = 0; i < 8; i++) - input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1); - - for (i = 2; i < 8; i++) { - - if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) { - if (usb_kbd_keycode[kbd->old[i]]) - input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0); - else - info("Unknown key (scancode %#x) released.", kbd->old[i]); - } - - if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) { - if (usb_kbd_keycode[kbd->new[i]]) - input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1); - else - info("Unknown key (scancode %#x) pressed.", kbd->new[i]); - } - } - - input_sync(kbd->dev); - - memcpy(kbd->old, kbd->new, 8); - -resubmit: - i = usb_submit_urb (urb, GFP_ATOMIC); - if (i) - err ("can't resubmit intr, %s-%s/input0, status %d", - kbd->usbdev->bus->bus_name, - kbd->usbdev->devpath, i); -} - -static int usb_kbd_event(struct input_dev *dev, unsigned int type, - unsigned int code, int value) -{ - struct usb_kbd *kbd = dev->private; - - if (type != EV_LED) - return -1; - - - kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) | - (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) | - (!!test_bit(LED_NUML, dev->led)); - - if (kbd->led->status == -EINPROGRESS) - return 0; - - if (*(kbd->leds) == kbd->newleds) - return 0; - - *(kbd->leds) = kbd->newleds; - kbd->led->dev = kbd->usbdev; - if (usb_submit_urb(kbd->led, GFP_ATOMIC)) - err("usb_submit_urb(leds) failed"); - - return 0; -} - -static void usb_kbd_led(struct urb *urb) -{ - struct usb_kbd *kbd = urb->context; - - if (urb->status) - warn("led urb status %d received", urb->status); - - if (*(kbd->leds) == kbd->newleds) - return; - - *(kbd->leds) = kbd->newleds; - kbd->led->dev = kbd->usbdev; - if (usb_submit_urb(kbd->led, GFP_ATOMIC)) - err("usb_submit_urb(leds) failed"); -} - -static int usb_kbd_open(struct input_dev *dev) -{ - struct usb_kbd *kbd = dev->private; - - kbd->irq->dev = kbd->usbdev; - if (usb_submit_urb(kbd->irq, GFP_KERNEL)) - return -EIO; - - return 0; -} - -static void usb_kbd_close(struct input_dev *dev) -{ - struct usb_kbd *kbd = dev->private; - - usb_kill_urb(kbd->irq); -} - -static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd) -{ - if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL))) - return -1; - if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL))) - return -1; - if (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma))) - return -1; - if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma))) - return -1; - if (!(kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma))) - return -1; - - return 0; -} - -static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd) -{ - usb_free_urb(kbd->irq); - usb_free_urb(kbd->led); - if (kbd->new) - usb_buffer_free(dev, 8, kbd->new, kbd->new_dma); - if (kbd->cr) - usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma); - if (kbd->leds) - usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma); -} - -static int usb_kbd_probe(struct usb_interface *iface, - const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(iface); - struct usb_host_interface *interface; - struct usb_endpoint_descriptor *endpoint; - struct usb_kbd *kbd; - struct input_dev *input_dev; - int i, pipe, maxp; - - interface = iface->cur_altsetting; - - if (interface->desc.bNumEndpoints != 1) - return -ENODEV; - - endpoint = &interface->endpoint[0].desc; - if (!usb_endpoint_is_int_in(endpoint)) - return -ENODEV; - - pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); - maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - - kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!kbd || !input_dev) - goto fail1; - - if (usb_kbd_alloc_mem(dev, kbd)) - goto fail2; - - kbd->usbdev = dev; - kbd->dev = input_dev; - - if (dev->manufacturer) - strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name)); - - if (dev->product) { - if (dev->manufacturer) - strlcat(kbd->name, " ", sizeof(kbd->name)); - strlcat(kbd->name, dev->product, sizeof(kbd->name)); - } - - if (!strlen(kbd->name)) - snprintf(kbd->name, sizeof(kbd->name), - "USB HIDBP Keyboard %04x:%04x", - le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - - usb_make_path(dev, kbd->phys, sizeof(kbd->phys)); - strlcpy(kbd->phys, "/input0", sizeof(kbd->phys)); - - input_dev->name = kbd->name; - input_dev->phys = kbd->phys; - usb_to_input_id(dev, &input_dev->id); - input_dev->cdev.dev = &iface->dev; - input_dev->private = kbd; - - input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP); - input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA); - - for (i = 0; i < 255; i++) - set_bit(usb_kbd_keycode[i], input_dev->keybit); - clear_bit(0, input_dev->keybit); - - input_dev->event = usb_kbd_event; - input_dev->open = usb_kbd_open; - input_dev->close = usb_kbd_close; - - usb_fill_int_urb(kbd->irq, dev, pipe, - kbd->new, (maxp > 8 ? 8 : maxp), - usb_kbd_irq, kbd, endpoint->bInterval); - kbd->irq->transfer_dma = kbd->new_dma; - kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - - kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE; - kbd->cr->bRequest = 0x09; - kbd->cr->wValue = cpu_to_le16(0x200); - kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber); - kbd->cr->wLength = cpu_to_le16(1); - - usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0), - (void *) kbd->cr, kbd->leds, 1, - usb_kbd_led, kbd); - kbd->led->setup_dma = kbd->cr_dma; - kbd->led->transfer_dma = kbd->leds_dma; - kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP); - - input_register_device(kbd->dev); - - usb_set_intfdata(iface, kbd); - return 0; - -fail2: usb_kbd_free_mem(dev, kbd); -fail1: input_free_device(input_dev); - kfree(kbd); - return -ENOMEM; -} - -static void usb_kbd_disconnect(struct usb_interface *intf) -{ - struct usb_kbd *kbd = usb_get_intfdata (intf); - - usb_set_intfdata(intf, NULL); - if (kbd) { - usb_kill_urb(kbd->irq); - input_unregister_device(kbd->dev); - usb_kbd_free_mem(interface_to_usbdev(intf), kbd); - kfree(kbd); - } -} - -static struct usb_device_id usb_kbd_id_table [] = { - { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, - USB_INTERFACE_PROTOCOL_KEYBOARD) }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, usb_kbd_id_table); - -static struct usb_driver usb_kbd_driver = { - .name = "usbkbd", - .probe = usb_kbd_probe, - .disconnect = usb_kbd_disconnect, - .id_table = usb_kbd_id_table, -}; - -static int __init usb_kbd_init(void) -{ - int result = usb_register(&usb_kbd_driver); - if (result == 0) - info(DRIVER_VERSION ":" DRIVER_DESC); - return result; -} - -static void __exit usb_kbd_exit(void) -{ - usb_deregister(&usb_kbd_driver); -} - -module_init(usb_kbd_init); -module_exit(usb_kbd_exit); diff --git a/drivers/usb/input/usbmouse.c b/drivers/usb/input/usbmouse.c deleted file mode 100644 index 692fd6087779..000000000000 --- a/drivers/usb/input/usbmouse.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * $Id: usbmouse.c,v 1.15 2001/12/27 10:37:41 vojtech Exp $ - * - * Copyright (c) 1999-2001 Vojtech Pavlik - * - * USB HIDBP Mouse support - */ - -/* - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include - -/* - * Version Information - */ -#define DRIVER_VERSION "v1.6" -#define DRIVER_AUTHOR "Vojtech Pavlik " -#define DRIVER_DESC "USB HID Boot Protocol mouse driver" -#define DRIVER_LICENSE "GPL" - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE(DRIVER_LICENSE); - -struct usb_mouse { - char name[128]; - char phys[64]; - struct usb_device *usbdev; - struct input_dev *dev; - struct urb *irq; - - signed char *data; - dma_addr_t data_dma; -}; - -static void usb_mouse_irq(struct urb *urb) -{ - struct usb_mouse *mouse = urb->context; - signed char *data = mouse->data; - struct input_dev *dev = mouse->dev; - int status; - - switch (urb->status) { - case 0: /* success */ - break; - case -ECONNRESET: /* unlink */ - case -ENOENT: - case -ESHUTDOWN: - return; - /* -EPIPE: should clear the halt */ - default: /* error */ - goto resubmit; - } - - input_report_key(dev, BTN_LEFT, data[0] & 0x01); - input_report_key(dev, BTN_RIGHT, data[0] & 0x02); - input_report_key(dev, BTN_MIDDLE, data[0] & 0x04); - input_report_key(dev, BTN_SIDE, data[0] & 0x08); - input_report_key(dev, BTN_EXTRA, data[0] & 0x10); - - input_report_rel(dev, REL_X, data[1]); - input_report_rel(dev, REL_Y, data[2]); - input_report_rel(dev, REL_WHEEL, data[3]); - - input_sync(dev); -resubmit: - status = usb_submit_urb (urb, GFP_ATOMIC); - if (status) - err ("can't resubmit intr, %s-%s/input0, status %d", - mouse->usbdev->bus->bus_name, - mouse->usbdev->devpath, status); -} - -static int usb_mouse_open(struct input_dev *dev) -{ - struct usb_mouse *mouse = dev->private; - - mouse->irq->dev = mouse->usbdev; - if (usb_submit_urb(mouse->irq, GFP_KERNEL)) - return -EIO; - - return 0; -} - -static void usb_mouse_close(struct input_dev *dev) -{ - struct usb_mouse *mouse = dev->private; - - usb_kill_urb(mouse->irq); -} - -static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - struct usb_host_interface *interface; - struct usb_endpoint_descriptor *endpoint; - struct usb_mouse *mouse; - struct input_dev *input_dev; - int pipe, maxp; - - interface = intf->cur_altsetting; - - if (interface->desc.bNumEndpoints != 1) - return -ENODEV; - - endpoint = &interface->endpoint[0].desc; - if (!usb_endpoint_is_int_in(endpoint)) - return -ENODEV; - - pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); - maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - - mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!mouse || !input_dev) - goto fail1; - - mouse->data = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &mouse->data_dma); - if (!mouse->data) - goto fail1; - - mouse->irq = usb_alloc_urb(0, GFP_KERNEL); - if (!mouse->irq) - goto fail2; - - mouse->usbdev = dev; - mouse->dev = input_dev; - - if (dev->manufacturer) - strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name)); - - if (dev->product) { - if (dev->manufacturer) - strlcat(mouse->name, " ", sizeof(mouse->name)); - strlcat(mouse->name, dev->product, sizeof(mouse->name)); - } - - if (!strlen(mouse->name)) - snprintf(mouse->name, sizeof(mouse->name), - "USB HIDBP Mouse %04x:%04x", - le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - - usb_make_path(dev, mouse->phys, sizeof(mouse->phys)); - strlcat(mouse->phys, "/input0", sizeof(mouse->phys)); - - input_dev->name = mouse->name; - input_dev->phys = mouse->phys; - usb_to_input_id(dev, &input_dev->id); - input_dev->cdev.dev = &intf->dev; - - input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); - input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); - input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y); - input_dev->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA); - input_dev->relbit[0] |= BIT(REL_WHEEL); - - input_dev->private = mouse; - input_dev->open = usb_mouse_open; - input_dev->close = usb_mouse_close; - - usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data, - (maxp > 8 ? 8 : maxp), - usb_mouse_irq, mouse, endpoint->bInterval); - mouse->irq->transfer_dma = mouse->data_dma; - mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - - input_register_device(mouse->dev); - - usb_set_intfdata(intf, mouse); - return 0; - -fail2: usb_buffer_free(dev, 8, mouse->data, mouse->data_dma); -fail1: input_free_device(input_dev); - kfree(mouse); - return -ENOMEM; -} - -static void usb_mouse_disconnect(struct usb_interface *intf) -{ - struct usb_mouse *mouse = usb_get_intfdata (intf); - - usb_set_intfdata(intf, NULL); - if (mouse) { - usb_kill_urb(mouse->irq); - input_unregister_device(mouse->dev); - usb_free_urb(mouse->irq); - usb_buffer_free(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma); - kfree(mouse); - } -} - -static struct usb_device_id usb_mouse_id_table [] = { - { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, - USB_INTERFACE_PROTOCOL_MOUSE) }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, usb_mouse_id_table); - -static struct usb_driver usb_mouse_driver = { - .name = "usbmouse", - .probe = usb_mouse_probe, - .disconnect = usb_mouse_disconnect, - .id_table = usb_mouse_id_table, -}; - -static int __init usb_mouse_init(void) -{ - int retval = usb_register(&usb_mouse_driver); - if (retval == 0) - info(DRIVER_VERSION ":" DRIVER_DESC); - return retval; -} - -static void __exit usb_mouse_exit(void) -{ - usb_deregister(&usb_mouse_driver); -} - -module_init(usb_mouse_init); -module_exit(usb_mouse_exit); -- cgit v1.2.3-55-g7522