Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
This commit is contained in:
237
drivers/usb/input/Kconfig
Normal file
237
drivers/usb/input/Kconfig
Normal file
@@ -0,0 +1,237 @@
|
||||
#
|
||||
# USB Input driver configuration
|
||||
#
|
||||
comment "USB Input Devices"
|
||||
depends on USB
|
||||
|
||||
config USB_HID
|
||||
tristate "USB Human Interface Device (full HID) support"
|
||||
depends on USB
|
||||
---help---
|
||||
Say Y here if you want full HID support to connect keyboards,
|
||||
mice, joysticks, graphic tablets, or any other HID based devices
|
||||
to your computer via USB. You also need to select HID Input layer
|
||||
support (below) if you want to use keyboards, mice, joysticks and
|
||||
the like ... 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:
|
||||
<file:Documentation/input/input.txt>.
|
||||
|
||||
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
|
||||
bool "HID input layer support"
|
||||
default y
|
||||
depends on INPUT && USB_HID
|
||||
help
|
||||
Say Y here if you want to use a USB keyboard, mouse or joystick,
|
||||
or any other HID input device.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config HID_FF
|
||||
bool "Force feedback support (EXPERIMENTAL)"
|
||||
depends on USB_HIDINPUT && 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 <file:Documentation/input/ff.txt> for a description of the force
|
||||
feedback API.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config HID_PID
|
||||
bool "PID Devices (Microsoft Sidewinder Force Feedback 2)"
|
||||
depends on HID_FF
|
||||
help
|
||||
Say Y here if you have a PID-compliant joystick and wish to enable force
|
||||
feedback for it. The Microsoft Sidewinder Force Feedback 2 is one such
|
||||
device.
|
||||
|
||||
config LOGITECH_FF
|
||||
bool "Logitech WingMan *3D support"
|
||||
depends on HID_FF
|
||||
help
|
||||
Say Y here if you have one of these devices:
|
||||
- Logitech WingMan Cordless RumblePad
|
||||
- Logitech WingMan Force 3D
|
||||
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 THRUSTMASTER_FF
|
||||
bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)"
|
||||
depends on HID_FF && EXPERIMENTAL
|
||||
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 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
|
||||
help
|
||||
Say Y here if you want to use the USB version of the Aiptek 6000U
|
||||
or Aiptek 8000U tablet. Make sure to say Y to "Mouse support"
|
||||
(CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
|
||||
(CONFIG_INPUT_EVDEV) as well.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called aiptek.
|
||||
|
||||
config USB_WACOM
|
||||
tristate "Wacom Intuos/Graphire tablet support"
|
||||
depends on USB && INPUT
|
||||
help
|
||||
Say Y here if you want to use the USB version of the Wacom Intuos
|
||||
or Graphire tablet. Make sure to say Y to "Mouse support"
|
||||
(CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
|
||||
(CONFIG_INPUT_EVDEV) as well.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called wacom.
|
||||
|
||||
config USB_KBTAB
|
||||
tristate "KB Gear JamStudio tablet support"
|
||||
depends on USB && INPUT
|
||||
help
|
||||
Say Y here if you want to use the USB version of the KB Gear
|
||||
JamStudio tablet. Make sure to say Y to "Mouse support"
|
||||
(CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
|
||||
(CONFIG_INPUT_EVDEV) as well.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called kbtab.
|
||||
|
||||
config USB_POWERMATE
|
||||
tristate "Griffin PowerMate and Contour Jog support"
|
||||
depends on USB && INPUT
|
||||
---help---
|
||||
Say Y here if you want to use Griffin PowerMate or Contour Jog devices.
|
||||
These are aluminum dials which can measure clockwise and anticlockwise
|
||||
rotation. The dial also acts as a pushbutton. The base contains an LED
|
||||
which can be instructed to pulse or to switch to a particular intensity.
|
||||
|
||||
You can download userspace tools from
|
||||
<http://sowerbutts.com/powermate/>.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called powermate.
|
||||
|
||||
config USB_MTOUCH
|
||||
tristate "MicroTouch USB Touchscreen Driver"
|
||||
depends on USB && INPUT
|
||||
---help---
|
||||
Say Y here if you want to use a MicroTouch (Now 3M) USB
|
||||
Touchscreen controller.
|
||||
|
||||
See <file:Documentation/usb/mtouch.txt> for additional information.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called mtouchusb.
|
||||
|
||||
config USB_EGALAX
|
||||
tristate "eGalax TouchKit USB Touchscreen Driver"
|
||||
depends on USB && INPUT
|
||||
---help---
|
||||
Say Y here if you want to use a eGalax TouchKit USB
|
||||
Touchscreen controller.
|
||||
|
||||
The driver has been tested on a Xenarc 700TSV monitor
|
||||
with eGalax touchscreen.
|
||||
|
||||
Have a look at <http://linux.chapter7.ch/touchkit/> for
|
||||
a usage description and the required user-space stuff.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called touchkitusb.
|
||||
|
||||
config USB_XPAD
|
||||
tristate "X-Box gamepad support"
|
||||
depends on USB && INPUT
|
||||
---help---
|
||||
Say Y here if you want to use the X-Box pad with your computer.
|
||||
Make sure to say Y to "Joystick support" (CONFIG_INPUT_JOYDEV)
|
||||
and/or "Event interface support" (CONFIG_INPUT_EVDEV) as well.
|
||||
|
||||
For information about how to connect the X-Box pad to USB, see
|
||||
<file:Documentation/input/xpad.txt>.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called xpad.
|
||||
|
||||
config USB_ATI_REMOTE
|
||||
tristate "ATI / X10 USB RF remote control"
|
||||
depends on USB && INPUT
|
||||
---help---
|
||||
Say Y here if you want to use an ATI or X10 "Lola" USB remote control.
|
||||
These are RF remotes with USB receivers.
|
||||
The ATI remote comes with many of ATI's All-In-Wonder video cards.
|
||||
The X10 "Lola" remote is available at:
|
||||
<http://www.x10.com/products/lola_sg1.htm>
|
||||
This driver provides mouse pointer, left and right mouse buttons,
|
||||
and maps all the other remote buttons to keypress events.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ati_remote.
|
||||
|
39
drivers/usb/input/Makefile
Normal file
39
drivers/usb/input/Makefile
Normal file
@@ -0,0 +1,39 @@
|
||||
#
|
||||
# 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_USB_HIDINPUT),y)
|
||||
usbhid-objs += hid-input.o
|
||||
endif
|
||||
ifeq ($(CONFIG_HID_PID),y)
|
||||
usbhid-objs += pid.o
|
||||
endif
|
||||
ifeq ($(CONFIG_LOGITECH_FF),y)
|
||||
usbhid-objs += hid-lgff.o
|
||||
endif
|
||||
ifeq ($(CONFIG_THRUSTMASTER_FF),y)
|
||||
usbhid-objs += hid-tmff.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_HID) += usbhid.o
|
||||
obj-$(CONFIG_USB_KBD) += usbkbd.o
|
||||
obj-$(CONFIG_USB_KBTAB) += kbtab.o
|
||||
obj-$(CONFIG_USB_MOUSE) += usbmouse.o
|
||||
obj-$(CONFIG_USB_MTOUCH) += mtouchusb.o
|
||||
obj-$(CONFIG_USB_EGALAX) += touchkitusb.o
|
||||
obj-$(CONFIG_USB_POWERMATE) += powermate.o
|
||||
obj-$(CONFIG_USB_WACOM) += wacom.o
|
||||
obj-$(CONFIG_USB_XPAD) += xpad.o
|
2283
drivers/usb/input/aiptek.c
Normal file
2283
drivers/usb/input/aiptek.c
Normal file
File diff suppressed because it is too large
Load Diff
850
drivers/usb/input/ati_remote.c
Normal file
850
drivers/usb/input/ati_remote.c
Normal file
@@ -0,0 +1,850 @@
|
||||
/*
|
||||
* USB ATI Remote support
|
||||
*
|
||||
* Version 2.2.0 Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>
|
||||
* Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev
|
||||
*
|
||||
* This 2.2.0 version is a rewrite / cleanup of the 2.1.1 driver, including
|
||||
* porting to the 2.6 kernel interfaces, along with other modification
|
||||
* to better match the style of the existing usb/input drivers. However, the
|
||||
* protocol and hardware handling is essentially unchanged from 2.1.1.
|
||||
*
|
||||
* The 2.1.1 driver was derived from the usbati_remote and usbkbd drivers by
|
||||
* Vojtech Pavlik.
|
||||
*
|
||||
* Changes:
|
||||
*
|
||||
* Feb 2004: Torrey Hoffman <thoffman@arnor.net>
|
||||
* Version 2.2.0
|
||||
* Jun 2004: Torrey Hoffman <thoffman@arnor.net>
|
||||
* Version 2.2.1
|
||||
* Added key repeat support contributed by:
|
||||
* Vincent Vanackere <vanackere@lif.univ-mrs.fr>
|
||||
* Added support for the "Lola" remote contributed by:
|
||||
* Seth Cohn <sethcohn@yahoo.com>
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Hardware & software notes
|
||||
*
|
||||
* These remote controls are distributed by ATI as part of their
|
||||
* "All-In-Wonder" video card packages. The receiver self-identifies as a
|
||||
* "USB Receiver" with manufacturer "X10 Wireless Technology Inc".
|
||||
*
|
||||
* The "Lola" remote is available from X10. See:
|
||||
* http://www.x10.com/products/lola_sg1.htm
|
||||
* The Lola is similar to the ATI remote but has no mouse support, and slightly
|
||||
* different keys.
|
||||
*
|
||||
* It is possible to use multiple receivers and remotes on multiple computers
|
||||
* simultaneously by configuring them to use specific channels.
|
||||
*
|
||||
* The RF protocol used by the remote supports 16 distinct channels, 1 to 16.
|
||||
* Actually, it may even support more, at least in some revisions of the
|
||||
* hardware.
|
||||
*
|
||||
* Each remote can be configured to transmit on one channel as follows:
|
||||
* - Press and hold the "hand icon" button.
|
||||
* - When the red LED starts to blink, let go of the "hand icon" button.
|
||||
* - When it stops blinking, input the channel code as two digits, from 01
|
||||
* to 16, and press the hand icon again.
|
||||
*
|
||||
* The timing can be a little tricky. Try loading the module with debug=1
|
||||
* to have the kernel print out messages about the remote control number
|
||||
* and mask. Note: debugging prints remote numbers as zero-based hexadecimal.
|
||||
*
|
||||
* The driver has a "channel_mask" parameter. This bitmask specifies which
|
||||
* channels will be ignored by the module. To mask out channels, just add
|
||||
* all the 2^channel_number values together.
|
||||
*
|
||||
* For instance, set channel_mask = 2^4 = 16 (binary 10000) to make ati_remote
|
||||
* ignore signals coming from remote controls transmitting on channel 4, but
|
||||
* accept all other channels.
|
||||
*
|
||||
* Or, set channel_mask = 65533, (0xFFFD), and all channels except 1 will be
|
||||
* ignored.
|
||||
*
|
||||
* The default is 0 (respond to all channels). Bit 0 and bits 17-32 of this
|
||||
* parameter are unused.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
/*
|
||||
* Module and Version Information, Module Parameters
|
||||
*/
|
||||
|
||||
#define ATI_REMOTE_VENDOR_ID 0x0bc7
|
||||
#define ATI_REMOTE_PRODUCT_ID 0x004
|
||||
#define LOLA_REMOTE_PRODUCT_ID 0x002
|
||||
#define MEDION_REMOTE_PRODUCT_ID 0x006
|
||||
|
||||
#define DRIVER_VERSION "2.2.1"
|
||||
#define DRIVER_AUTHOR "Torrey Hoffman <thoffman@arnor.net>"
|
||||
#define DRIVER_DESC "ATI/X10 RF USB Remote Control"
|
||||
|
||||
#define NAME_BUFSIZE 80 /* size of product name, path buffers */
|
||||
#define DATA_BUFSIZE 63 /* size of URB data buffers */
|
||||
#define ATI_INPUTNUM 1 /* Which input device to register as */
|
||||
|
||||
static unsigned long channel_mask = 0;
|
||||
module_param(channel_mask, ulong, 0444);
|
||||
MODULE_PARM_DESC(channel_mask, "Bitmask of remote control channels to ignore");
|
||||
|
||||
static int debug = 0;
|
||||
module_param(debug, int, 0444);
|
||||
MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
|
||||
|
||||
#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
|
||||
#undef err
|
||||
#define err(format, arg...) printk(KERN_ERR format , ## arg)
|
||||
|
||||
static struct usb_device_id ati_remote_table[] = {
|
||||
{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID) },
|
||||
{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID) },
|
||||
{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID) },
|
||||
{} /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, ati_remote_table);
|
||||
|
||||
/* Get hi and low bytes of a 16-bits int */
|
||||
#define HI(a) ((unsigned char)((a) >> 8))
|
||||
#define LO(a) ((unsigned char)((a) & 0xff))
|
||||
|
||||
#define SEND_FLAG_IN_PROGRESS 1
|
||||
#define SEND_FLAG_COMPLETE 2
|
||||
|
||||
/* Device initialization strings */
|
||||
static char init1[] = { 0x01, 0x00, 0x20, 0x14 };
|
||||
static char init2[] = { 0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 };
|
||||
|
||||
/* Acceleration curve for directional control pad */
|
||||
static char accel[] = { 1, 2, 4, 6, 9, 13, 20 };
|
||||
|
||||
/* Duplicate event filtering time.
|
||||
* Sequential, identical KIND_FILTERED inputs with less than
|
||||
* FILTER_TIME jiffies between them are considered as repeat
|
||||
* events. The hardware generates 5 events for the first keypress
|
||||
* and we have to take this into account for an accurate repeat
|
||||
* behaviour.
|
||||
* (HZ / 20) == 50 ms and works well for me.
|
||||
*/
|
||||
#define FILTER_TIME (HZ / 20)
|
||||
|
||||
static DECLARE_MUTEX(disconnect_sem);
|
||||
|
||||
struct ati_remote {
|
||||
struct input_dev idev;
|
||||
struct usb_device *udev;
|
||||
struct usb_interface *interface;
|
||||
|
||||
struct urb *irq_urb;
|
||||
struct urb *out_urb;
|
||||
struct usb_endpoint_descriptor *endpoint_in;
|
||||
struct usb_endpoint_descriptor *endpoint_out;
|
||||
unsigned char *inbuf;
|
||||
unsigned char *outbuf;
|
||||
dma_addr_t inbuf_dma;
|
||||
dma_addr_t outbuf_dma;
|
||||
|
||||
int open; /* open counter */
|
||||
|
||||
unsigned char old_data[2]; /* Detect duplicate events */
|
||||
unsigned long old_jiffies;
|
||||
unsigned long acc_jiffies; /* handle acceleration */
|
||||
unsigned int repeat_count;
|
||||
|
||||
char name[NAME_BUFSIZE];
|
||||
char phys[NAME_BUFSIZE];
|
||||
|
||||
wait_queue_head_t wait;
|
||||
int send_flags;
|
||||
};
|
||||
|
||||
/* "Kinds" of messages sent from the hardware to the driver. */
|
||||
#define KIND_END 0
|
||||
#define KIND_LITERAL 1 /* Simply pass to input system */
|
||||
#define KIND_FILTERED 2 /* Add artificial key-up events, drop keyrepeats */
|
||||
#define KIND_LU 3 /* Directional keypad diagonals - left up, */
|
||||
#define KIND_RU 4 /* right up, */
|
||||
#define KIND_LD 5 /* left down, */
|
||||
#define KIND_RD 6 /* right down */
|
||||
#define KIND_ACCEL 7 /* Directional keypad - left, right, up, down.*/
|
||||
|
||||
/* Translation table from hardware messages to input events. */
|
||||
static struct
|
||||
{
|
||||
short kind;
|
||||
unsigned char data1, data2;
|
||||
int type;
|
||||
unsigned int code;
|
||||
int value;
|
||||
} ati_remote_tbl[] =
|
||||
{
|
||||
/* Directional control pad axes */
|
||||
{KIND_ACCEL, 0x35, 0x70, EV_REL, REL_X, -1}, /* left */
|
||||
{KIND_ACCEL, 0x36, 0x71, EV_REL, REL_X, 1}, /* right */
|
||||
{KIND_ACCEL, 0x37, 0x72, EV_REL, REL_Y, -1}, /* up */
|
||||
{KIND_ACCEL, 0x38, 0x73, EV_REL, REL_Y, 1}, /* down */
|
||||
/* Directional control pad diagonals */
|
||||
{KIND_LU, 0x39, 0x74, EV_REL, 0, 0}, /* left up */
|
||||
{KIND_RU, 0x3a, 0x75, EV_REL, 0, 0}, /* right up */
|
||||
{KIND_LD, 0x3c, 0x77, EV_REL, 0, 0}, /* left down */
|
||||
{KIND_RD, 0x3b, 0x76, EV_REL, 0, 0}, /* right down */
|
||||
|
||||
/* "Mouse button" buttons */
|
||||
{KIND_LITERAL, 0x3d, 0x78, EV_KEY, BTN_LEFT, 1}, /* left btn down */
|
||||
{KIND_LITERAL, 0x3e, 0x79, EV_KEY, BTN_LEFT, 0}, /* left btn up */
|
||||
{KIND_LITERAL, 0x41, 0x7c, EV_KEY, BTN_RIGHT, 1},/* right btn down */
|
||||
{KIND_LITERAL, 0x42, 0x7d, EV_KEY, BTN_RIGHT, 0},/* right btn up */
|
||||
|
||||
/* Artificial "doubleclick" events are generated by the hardware.
|
||||
* They are mapped to the "side" and "extra" mouse buttons here. */
|
||||
{KIND_FILTERED, 0x3f, 0x7a, EV_KEY, BTN_SIDE, 1}, /* left dblclick */
|
||||
{KIND_FILTERED, 0x43, 0x7e, EV_KEY, BTN_EXTRA, 1},/* right dblclick */
|
||||
|
||||
/* keyboard. */
|
||||
{KIND_FILTERED, 0xd2, 0x0d, EV_KEY, KEY_1, 1},
|
||||
{KIND_FILTERED, 0xd3, 0x0e, EV_KEY, KEY_2, 1},
|
||||
{KIND_FILTERED, 0xd4, 0x0f, EV_KEY, KEY_3, 1},
|
||||
{KIND_FILTERED, 0xd5, 0x10, EV_KEY, KEY_4, 1},
|
||||
{KIND_FILTERED, 0xd6, 0x11, EV_KEY, KEY_5, 1},
|
||||
{KIND_FILTERED, 0xd7, 0x12, EV_KEY, KEY_6, 1},
|
||||
{KIND_FILTERED, 0xd8, 0x13, EV_KEY, KEY_7, 1},
|
||||
{KIND_FILTERED, 0xd9, 0x14, EV_KEY, KEY_8, 1},
|
||||
{KIND_FILTERED, 0xda, 0x15, EV_KEY, KEY_9, 1},
|
||||
{KIND_FILTERED, 0xdc, 0x17, EV_KEY, KEY_0, 1},
|
||||
{KIND_FILTERED, 0xc5, 0x00, EV_KEY, KEY_A, 1},
|
||||
{KIND_FILTERED, 0xc6, 0x01, EV_KEY, KEY_B, 1},
|
||||
{KIND_FILTERED, 0xde, 0x19, EV_KEY, KEY_C, 1},
|
||||
{KIND_FILTERED, 0xe0, 0x1b, EV_KEY, KEY_D, 1},
|
||||
{KIND_FILTERED, 0xe6, 0x21, EV_KEY, KEY_E, 1},
|
||||
{KIND_FILTERED, 0xe8, 0x23, EV_KEY, KEY_F, 1},
|
||||
|
||||
/* "special" keys */
|
||||
{KIND_FILTERED, 0xdd, 0x18, EV_KEY, KEY_KPENTER, 1}, /* "check" */
|
||||
{KIND_FILTERED, 0xdb, 0x16, EV_KEY, KEY_MENU, 1}, /* "menu" */
|
||||
{KIND_FILTERED, 0xc7, 0x02, EV_KEY, KEY_POWER, 1}, /* Power */
|
||||
{KIND_FILTERED, 0xc8, 0x03, EV_KEY, KEY_TV, 1}, /* TV */
|
||||
{KIND_FILTERED, 0xc9, 0x04, EV_KEY, KEY_DVD, 1}, /* DVD */
|
||||
{KIND_FILTERED, 0xca, 0x05, EV_KEY, KEY_WWW, 1}, /* WEB */
|
||||
{KIND_FILTERED, 0xcb, 0x06, EV_KEY, KEY_BOOKMARKS, 1}, /* "book" */
|
||||
{KIND_FILTERED, 0xcc, 0x07, EV_KEY, KEY_EDIT, 1}, /* "hand" */
|
||||
{KIND_FILTERED, 0xe1, 0x1c, EV_KEY, KEY_COFFEE, 1}, /* "timer" */
|
||||
{KIND_FILTERED, 0xe5, 0x20, EV_KEY, KEY_FRONT, 1}, /* "max" */
|
||||
{KIND_FILTERED, 0xe2, 0x1d, EV_KEY, KEY_LEFT, 1}, /* left */
|
||||
{KIND_FILTERED, 0xe4, 0x1f, EV_KEY, KEY_RIGHT, 1}, /* right */
|
||||
{KIND_FILTERED, 0xe7, 0x22, EV_KEY, KEY_DOWN, 1}, /* down */
|
||||
{KIND_FILTERED, 0xdf, 0x1a, EV_KEY, KEY_UP, 1}, /* up */
|
||||
{KIND_FILTERED, 0xe3, 0x1e, EV_KEY, KEY_OK, 1}, /* "OK" */
|
||||
{KIND_FILTERED, 0xce, 0x09, EV_KEY, KEY_VOLUMEDOWN, 1}, /* VOL + */
|
||||
{KIND_FILTERED, 0xcd, 0x08, EV_KEY, KEY_VOLUMEUP, 1}, /* VOL - */
|
||||
{KIND_FILTERED, 0xcf, 0x0a, EV_KEY, KEY_MUTE, 1}, /* MUTE */
|
||||
{KIND_FILTERED, 0xd0, 0x0b, EV_KEY, KEY_CHANNELUP, 1}, /* CH + */
|
||||
{KIND_FILTERED, 0xd1, 0x0c, EV_KEY, KEY_CHANNELDOWN, 1},/* CH - */
|
||||
{KIND_FILTERED, 0xec, 0x27, EV_KEY, KEY_RECORD, 1}, /* ( o) red */
|
||||
{KIND_FILTERED, 0xea, 0x25, EV_KEY, KEY_PLAY, 1}, /* ( >) */
|
||||
{KIND_FILTERED, 0xe9, 0x24, EV_KEY, KEY_REWIND, 1}, /* (<<) */
|
||||
{KIND_FILTERED, 0xeb, 0x26, EV_KEY, KEY_FORWARD, 1}, /* (>>) */
|
||||
{KIND_FILTERED, 0xed, 0x28, EV_KEY, KEY_STOP, 1}, /* ([]) */
|
||||
{KIND_FILTERED, 0xee, 0x29, EV_KEY, KEY_PAUSE, 1}, /* ('') */
|
||||
{KIND_FILTERED, 0xf0, 0x2b, EV_KEY, KEY_PREVIOUS, 1}, /* (<-) */
|
||||
{KIND_FILTERED, 0xef, 0x2a, EV_KEY, KEY_NEXT, 1}, /* (>+) */
|
||||
{KIND_FILTERED, 0xf2, 0x2D, EV_KEY, KEY_INFO, 1}, /* PLAYING */
|
||||
{KIND_FILTERED, 0xf3, 0x2E, EV_KEY, KEY_HOME, 1}, /* TOP */
|
||||
{KIND_FILTERED, 0xf4, 0x2F, EV_KEY, KEY_END, 1}, /* END */
|
||||
{KIND_FILTERED, 0xf5, 0x30, EV_KEY, KEY_SELECT, 1}, /* SELECT */
|
||||
|
||||
{KIND_END, 0x00, 0x00, EV_MAX + 1, 0, 0}
|
||||
};
|
||||
|
||||
/* Local function prototypes */
|
||||
static void ati_remote_dump (unsigned char *data, unsigned int actual_length);
|
||||
static void ati_remote_delete (struct ati_remote *dev);
|
||||
static int ati_remote_open (struct input_dev *inputdev);
|
||||
static void ati_remote_close (struct input_dev *inputdev);
|
||||
static int ati_remote_sendpacket (struct ati_remote *ati_remote, u16 cmd, unsigned char *data);
|
||||
static void ati_remote_irq_out (struct urb *urb, struct pt_regs *regs);
|
||||
static void ati_remote_irq_in (struct urb *urb, struct pt_regs *regs);
|
||||
static void ati_remote_input_report (struct urb *urb, struct pt_regs *regs);
|
||||
static int ati_remote_initialize (struct ati_remote *ati_remote);
|
||||
static int ati_remote_probe (struct usb_interface *interface, const struct usb_device_id *id);
|
||||
static void ati_remote_disconnect (struct usb_interface *interface);
|
||||
|
||||
/* usb specific object to register with the usb subsystem */
|
||||
static struct usb_driver ati_remote_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "ati_remote",
|
||||
.probe = ati_remote_probe,
|
||||
.disconnect = ati_remote_disconnect,
|
||||
.id_table = ati_remote_table,
|
||||
};
|
||||
|
||||
/*
|
||||
* ati_remote_dump_input
|
||||
*/
|
||||
static void ati_remote_dump(unsigned char *data, unsigned int len)
|
||||
{
|
||||
if ((len == 1) && (data[0] != (unsigned char)0xff) && (data[0] != 0x00))
|
||||
warn("Weird byte 0x%02x", data[0]);
|
||||
else if (len == 4)
|
||||
warn("Weird key %02x %02x %02x %02x",
|
||||
data[0], data[1], data[2], data[3]);
|
||||
else
|
||||
warn("Weird data, len=%d %02x %02x %02x %02x %02x %02x ...",
|
||||
len, data[0], data[1], data[2], data[3], data[4], data[5]);
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_open
|
||||
*/
|
||||
static int ati_remote_open(struct input_dev *inputdev)
|
||||
{
|
||||
struct ati_remote *ati_remote = inputdev->private;
|
||||
int retval = 0;
|
||||
|
||||
down(&disconnect_sem);
|
||||
|
||||
if (ati_remote->open++)
|
||||
goto exit;
|
||||
|
||||
/* On first open, submit the read urb which was set up previously. */
|
||||
ati_remote->irq_urb->dev = ati_remote->udev;
|
||||
if (usb_submit_urb(ati_remote->irq_urb, GFP_KERNEL)) {
|
||||
dev_err(&ati_remote->interface->dev,
|
||||
"%s: usb_submit_urb failed!\n", __FUNCTION__);
|
||||
ati_remote->open--;
|
||||
retval = -EIO;
|
||||
}
|
||||
|
||||
exit:
|
||||
up(&disconnect_sem);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_close
|
||||
*/
|
||||
static void ati_remote_close(struct input_dev *inputdev)
|
||||
{
|
||||
struct ati_remote *ati_remote = inputdev->private;
|
||||
|
||||
if (!--ati_remote->open)
|
||||
usb_kill_urb(ati_remote->irq_urb);
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_irq_out
|
||||
*/
|
||||
static void ati_remote_irq_out(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct ati_remote *ati_remote = urb->context;
|
||||
|
||||
if (urb->status) {
|
||||
dev_dbg(&ati_remote->interface->dev, "%s: status %d\n",
|
||||
__FUNCTION__, urb->status);
|
||||
return;
|
||||
}
|
||||
|
||||
ati_remote->send_flags |= SEND_FLAG_COMPLETE;
|
||||
wmb();
|
||||
wake_up(&ati_remote->wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_sendpacket
|
||||
*
|
||||
* Used to send device initialization strings
|
||||
*/
|
||||
static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigned char *data)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
/* Set up out_urb */
|
||||
memcpy(ati_remote->out_urb->transfer_buffer + 1, data, LO(cmd));
|
||||
((char *) ati_remote->out_urb->transfer_buffer)[0] = HI(cmd);
|
||||
|
||||
ati_remote->out_urb->transfer_buffer_length = LO(cmd) + 1;
|
||||
ati_remote->out_urb->dev = ati_remote->udev;
|
||||
ati_remote->send_flags = SEND_FLAG_IN_PROGRESS;
|
||||
|
||||
retval = usb_submit_urb(ati_remote->out_urb, GFP_ATOMIC);
|
||||
if (retval) {
|
||||
dev_dbg(&ati_remote->interface->dev,
|
||||
"sendpacket: usb_submit_urb failed: %d\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
wait_event_timeout(ati_remote->wait,
|
||||
((ati_remote->out_urb->status != -EINPROGRESS) ||
|
||||
(ati_remote->send_flags & SEND_FLAG_COMPLETE)),
|
||||
HZ);
|
||||
usb_kill_urb(ati_remote->out_urb);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_event_lookup
|
||||
*/
|
||||
static int ati_remote_event_lookup(int rem, unsigned char d1, unsigned char d2)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) {
|
||||
/*
|
||||
* Decide if the table entry matches the remote input.
|
||||
*/
|
||||
if ((((ati_remote_tbl[i].data1 & 0x0f) == (d1 & 0x0f))) &&
|
||||
((((ati_remote_tbl[i].data1 >> 4) -
|
||||
(d1 >> 4) + rem) & 0x0f) == 0x0f) &&
|
||||
(ati_remote_tbl[i].data2 == d2))
|
||||
return i;
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_report_input
|
||||
*/
|
||||
static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct ati_remote *ati_remote = urb->context;
|
||||
unsigned char *data= ati_remote->inbuf;
|
||||
struct input_dev *dev = &ati_remote->idev;
|
||||
int index, acc;
|
||||
int remote_num;
|
||||
|
||||
/* Deal with strange looking inputs */
|
||||
if ( (urb->actual_length != 4) || (data[0] != 0x14) ||
|
||||
((data[3] & 0x0f) != 0x00) ) {
|
||||
ati_remote_dump(data, urb->actual_length);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mask unwanted remote channels. */
|
||||
/* note: remote_num is 0-based, channel 1 on remote == 0 here */
|
||||
remote_num = (data[3] >> 4) & 0x0f;
|
||||
if (channel_mask & (1 << (remote_num + 1))) {
|
||||
dbginfo(&ati_remote->interface->dev,
|
||||
"Masked input from channel 0x%02x: data %02x,%02x, mask= 0x%02lx\n",
|
||||
remote_num, data[1], data[2], channel_mask);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Look up event code index in translation table */
|
||||
index = ati_remote_event_lookup(remote_num, data[1], data[2]);
|
||||
if (index < 0) {
|
||||
dev_warn(&ati_remote->interface->dev,
|
||||
"Unknown input from channel 0x%02x: data %02x,%02x\n",
|
||||
remote_num, data[1], data[2]);
|
||||
return;
|
||||
}
|
||||
dbginfo(&ati_remote->interface->dev,
|
||||
"channel 0x%02x; data %02x,%02x; index %d; keycode %d\n",
|
||||
remote_num, data[1], data[2], index, ati_remote_tbl[index].code);
|
||||
|
||||
if (ati_remote_tbl[index].kind == KIND_LITERAL) {
|
||||
input_regs(dev, regs);
|
||||
input_event(dev, ati_remote_tbl[index].type,
|
||||
ati_remote_tbl[index].code,
|
||||
ati_remote_tbl[index].value);
|
||||
input_sync(dev);
|
||||
|
||||
ati_remote->old_jiffies = jiffies;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ati_remote_tbl[index].kind == KIND_FILTERED) {
|
||||
/* Filter duplicate events which happen "too close" together. */
|
||||
if ((ati_remote->old_data[0] == data[1]) &&
|
||||
(ati_remote->old_data[1] == data[2]) &&
|
||||
((ati_remote->old_jiffies + FILTER_TIME) > jiffies)) {
|
||||
ati_remote->repeat_count++;
|
||||
}
|
||||
else {
|
||||
ati_remote->repeat_count = 0;
|
||||
}
|
||||
|
||||
ati_remote->old_data[0] = data[1];
|
||||
ati_remote->old_data[1] = data[2];
|
||||
ati_remote->old_jiffies = jiffies;
|
||||
|
||||
if ((ati_remote->repeat_count > 0)
|
||||
&& (ati_remote->repeat_count < 5))
|
||||
return;
|
||||
|
||||
|
||||
input_regs(dev, regs);
|
||||
input_event(dev, ati_remote_tbl[index].type,
|
||||
ati_remote_tbl[index].code, 1);
|
||||
input_event(dev, ati_remote_tbl[index].type,
|
||||
ati_remote_tbl[index].code, 0);
|
||||
input_sync(dev);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Other event kinds are from the directional control pad, and have an
|
||||
* acceleration factor applied to them. Without this acceleration, the
|
||||
* control pad is mostly unusable.
|
||||
*
|
||||
* If elapsed time since last event is > 1/4 second, user "stopped",
|
||||
* so reset acceleration. Otherwise, user is probably holding the control
|
||||
* pad down, so we increase acceleration, ramping up over two seconds to
|
||||
* a maximum speed. The acceleration curve is #defined above.
|
||||
*/
|
||||
if ((jiffies - ati_remote->old_jiffies) > (HZ >> 2)) {
|
||||
acc = 1;
|
||||
ati_remote->acc_jiffies = jiffies;
|
||||
}
|
||||
else if ((jiffies - ati_remote->acc_jiffies) < (HZ >> 3)) acc = accel[0];
|
||||
else if ((jiffies - ati_remote->acc_jiffies) < (HZ >> 2)) acc = accel[1];
|
||||
else if ((jiffies - ati_remote->acc_jiffies) < (HZ >> 1)) acc = accel[2];
|
||||
else if ((jiffies - ati_remote->acc_jiffies) < HZ ) acc = accel[3];
|
||||
else if ((jiffies - ati_remote->acc_jiffies) < HZ+(HZ>>1)) acc = accel[4];
|
||||
else if ((jiffies - ati_remote->acc_jiffies) < (HZ << 1)) acc = accel[5];
|
||||
else acc = accel[6];
|
||||
|
||||
input_regs(dev, regs);
|
||||
switch (ati_remote_tbl[index].kind) {
|
||||
case KIND_ACCEL:
|
||||
input_event(dev, ati_remote_tbl[index].type,
|
||||
ati_remote_tbl[index].code,
|
||||
ati_remote_tbl[index].value * acc);
|
||||
break;
|
||||
case KIND_LU:
|
||||
input_report_rel(dev, REL_X, -acc);
|
||||
input_report_rel(dev, REL_Y, -acc);
|
||||
break;
|
||||
case KIND_RU:
|
||||
input_report_rel(dev, REL_X, acc);
|
||||
input_report_rel(dev, REL_Y, -acc);
|
||||
break;
|
||||
case KIND_LD:
|
||||
input_report_rel(dev, REL_X, -acc);
|
||||
input_report_rel(dev, REL_Y, acc);
|
||||
break;
|
||||
case KIND_RD:
|
||||
input_report_rel(dev, REL_X, acc);
|
||||
input_report_rel(dev, REL_Y, acc);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n",
|
||||
ati_remote_tbl[index].kind);
|
||||
}
|
||||
input_sync(dev);
|
||||
|
||||
ati_remote->old_jiffies = jiffies;
|
||||
ati_remote->old_data[0] = data[1];
|
||||
ati_remote->old_data[1] = data[2];
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_irq_in
|
||||
*/
|
||||
static void ati_remote_irq_in(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct ati_remote *ati_remote = urb->context;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0: /* success */
|
||||
ati_remote_input_report(urb, regs);
|
||||
break;
|
||||
case -ECONNRESET: /* unlink */
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
dev_dbg(&ati_remote->interface->dev, "%s: urb error status, unlink? \n",
|
||||
__FUNCTION__);
|
||||
return;
|
||||
default: /* error */
|
||||
dev_dbg(&ati_remote->interface->dev, "%s: Nonzero urb status %d\n",
|
||||
__FUNCTION__, urb->status);
|
||||
}
|
||||
|
||||
retval = usb_submit_urb(urb, SLAB_ATOMIC);
|
||||
if (retval)
|
||||
dev_err(&ati_remote->interface->dev, "%s: usb_submit_urb()=%d\n",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_delete
|
||||
*/
|
||||
static void ati_remote_delete(struct ati_remote *ati_remote)
|
||||
{
|
||||
if (!ati_remote) return;
|
||||
|
||||
if (ati_remote->irq_urb)
|
||||
usb_kill_urb(ati_remote->irq_urb);
|
||||
|
||||
if (ati_remote->out_urb)
|
||||
usb_kill_urb(ati_remote->out_urb);
|
||||
|
||||
input_unregister_device(&ati_remote->idev);
|
||||
|
||||
if (ati_remote->inbuf)
|
||||
usb_buffer_free(ati_remote->udev, DATA_BUFSIZE,
|
||||
ati_remote->inbuf, ati_remote->inbuf_dma);
|
||||
|
||||
if (ati_remote->outbuf)
|
||||
usb_buffer_free(ati_remote->udev, DATA_BUFSIZE,
|
||||
ati_remote->inbuf, ati_remote->outbuf_dma);
|
||||
|
||||
if (ati_remote->irq_urb)
|
||||
usb_free_urb(ati_remote->irq_urb);
|
||||
|
||||
if (ati_remote->out_urb)
|
||||
usb_free_urb(ati_remote->out_urb);
|
||||
|
||||
kfree(ati_remote);
|
||||
}
|
||||
|
||||
static void ati_remote_input_init(struct ati_remote *ati_remote)
|
||||
{
|
||||
struct input_dev *idev = &(ati_remote->idev);
|
||||
int i;
|
||||
|
||||
idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
|
||||
idev->keybit[LONG(BTN_MOUSE)] = ( BIT(BTN_LEFT) | BIT(BTN_RIGHT) |
|
||||
BIT(BTN_SIDE) | BIT(BTN_EXTRA) );
|
||||
idev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
|
||||
for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++)
|
||||
if (ati_remote_tbl[i].type == EV_KEY)
|
||||
set_bit(ati_remote_tbl[i].code, idev->keybit);
|
||||
|
||||
idev->private = ati_remote;
|
||||
idev->open = ati_remote_open;
|
||||
idev->close = ati_remote_close;
|
||||
|
||||
idev->name = ati_remote->name;
|
||||
idev->phys = ati_remote->phys;
|
||||
|
||||
idev->id.bustype = BUS_USB;
|
||||
idev->id.vendor = le16_to_cpu(ati_remote->udev->descriptor.idVendor);
|
||||
idev->id.product = le16_to_cpu(ati_remote->udev->descriptor.idProduct);
|
||||
idev->id.version = le16_to_cpu(ati_remote->udev->descriptor.bcdDevice);
|
||||
}
|
||||
|
||||
static int ati_remote_initialize(struct ati_remote *ati_remote)
|
||||
{
|
||||
struct usb_device *udev = ati_remote->udev;
|
||||
int pipe, maxp;
|
||||
|
||||
init_waitqueue_head(&ati_remote->wait);
|
||||
|
||||
/* Set up irq_urb */
|
||||
pipe = usb_rcvintpipe(udev, ati_remote->endpoint_in->bEndpointAddress);
|
||||
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
|
||||
maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
|
||||
|
||||
usb_fill_int_urb(ati_remote->irq_urb, udev, pipe, ati_remote->inbuf,
|
||||
maxp, ati_remote_irq_in, ati_remote,
|
||||
ati_remote->endpoint_in->bInterval);
|
||||
ati_remote->irq_urb->transfer_dma = ati_remote->inbuf_dma;
|
||||
ati_remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
/* Set up out_urb */
|
||||
pipe = usb_sndintpipe(udev, ati_remote->endpoint_out->bEndpointAddress);
|
||||
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
|
||||
maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
|
||||
|
||||
usb_fill_int_urb(ati_remote->out_urb, udev, pipe, ati_remote->outbuf,
|
||||
maxp, ati_remote_irq_out, ati_remote,
|
||||
ati_remote->endpoint_out->bInterval);
|
||||
ati_remote->out_urb->transfer_dma = ati_remote->outbuf_dma;
|
||||
ati_remote->out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
/* send initialization strings */
|
||||
if ((ati_remote_sendpacket(ati_remote, 0x8004, init1)) ||
|
||||
(ati_remote_sendpacket(ati_remote, 0x8007, init2))) {
|
||||
dev_err(&ati_remote->interface->dev,
|
||||
"Initializing ati_remote hardware failed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_probe
|
||||
*/
|
||||
static int ati_remote_probe(struct usb_interface *interface, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(interface);
|
||||
struct ati_remote *ati_remote = NULL;
|
||||
struct usb_host_interface *iface_host;
|
||||
int retval = -ENOMEM;
|
||||
char path[64];
|
||||
|
||||
/* Allocate and clear an ati_remote struct */
|
||||
if (!(ati_remote = kmalloc(sizeof (struct ati_remote), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
memset(ati_remote, 0x00, sizeof (struct ati_remote));
|
||||
|
||||
iface_host = interface->cur_altsetting;
|
||||
if (iface_host->desc.bNumEndpoints != 2) {
|
||||
err("%s: Unexpected desc.bNumEndpoints\n", __FUNCTION__);
|
||||
retval = -ENODEV;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ati_remote->endpoint_in = &(iface_host->endpoint[0].desc);
|
||||
ati_remote->endpoint_out = &(iface_host->endpoint[1].desc);
|
||||
ati_remote->udev = udev;
|
||||
ati_remote->interface = interface;
|
||||
|
||||
if (!(ati_remote->endpoint_in->bEndpointAddress & 0x80)) {
|
||||
err("%s: Unexpected endpoint_in->bEndpointAddress\n", __FUNCTION__);
|
||||
retval = -ENODEV;
|
||||
goto error;
|
||||
}
|
||||
if ((ati_remote->endpoint_in->bmAttributes & 3) != 3) {
|
||||
err("%s: Unexpected endpoint_in->bmAttributes\n", __FUNCTION__);
|
||||
retval = -ENODEV;
|
||||
goto error;
|
||||
}
|
||||
if (le16_to_cpu(ati_remote->endpoint_in->wMaxPacketSize) == 0) {
|
||||
err("%s: endpoint_in message size==0? \n", __FUNCTION__);
|
||||
retval = -ENODEV;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Allocate URB buffers, URBs */
|
||||
ati_remote->inbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, SLAB_ATOMIC,
|
||||
&ati_remote->inbuf_dma);
|
||||
if (!ati_remote->inbuf)
|
||||
goto error;
|
||||
|
||||
ati_remote->outbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, SLAB_ATOMIC,
|
||||
&ati_remote->outbuf_dma);
|
||||
if (!ati_remote->outbuf)
|
||||
goto error;
|
||||
|
||||
ati_remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!ati_remote->irq_urb)
|
||||
goto error;
|
||||
|
||||
ati_remote->out_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!ati_remote->out_urb)
|
||||
goto error;
|
||||
|
||||
usb_make_path(udev, path, NAME_BUFSIZE);
|
||||
sprintf(ati_remote->phys, "%s/input%d", path, ATI_INPUTNUM);
|
||||
if (udev->manufacturer)
|
||||
strcat(ati_remote->name, udev->manufacturer);
|
||||
|
||||
if (udev->product)
|
||||
sprintf(ati_remote->name, "%s %s", ati_remote->name, udev->product);
|
||||
|
||||
if (!strlen(ati_remote->name))
|
||||
sprintf(ati_remote->name, DRIVER_DESC "(%04x,%04x)",
|
||||
le16_to_cpu(ati_remote->udev->descriptor.idVendor),
|
||||
le16_to_cpu(ati_remote->udev->descriptor.idProduct));
|
||||
|
||||
/* Device Hardware Initialization - fills in ati_remote->idev from udev. */
|
||||
retval = ati_remote_initialize(ati_remote);
|
||||
if (retval)
|
||||
goto error;
|
||||
|
||||
/* Set up and register input device */
|
||||
ati_remote_input_init(ati_remote);
|
||||
input_register_device(&ati_remote->idev);
|
||||
|
||||
dev_info(&ati_remote->interface->dev, "Input registered: %s on %s\n",
|
||||
ati_remote->name, path);
|
||||
|
||||
usb_set_intfdata(interface, ati_remote);
|
||||
|
||||
error:
|
||||
if (retval)
|
||||
ati_remote_delete(ati_remote);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_disconnect
|
||||
*/
|
||||
static void ati_remote_disconnect(struct usb_interface *interface)
|
||||
{
|
||||
struct ati_remote *ati_remote;
|
||||
|
||||
down(&disconnect_sem);
|
||||
|
||||
ati_remote = usb_get_intfdata(interface);
|
||||
usb_set_intfdata(interface, NULL);
|
||||
if (!ati_remote) {
|
||||
warn("%s - null device?\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
ati_remote_delete(ati_remote);
|
||||
|
||||
up(&disconnect_sem);
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_init
|
||||
*/
|
||||
static int __init ati_remote_init(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = usb_register(&ati_remote_driver);
|
||||
if (result)
|
||||
err("usb_register error #%d\n", result);
|
||||
else
|
||||
info("Registered USB driver " DRIVER_DESC " v. " DRIVER_VERSION);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_exit
|
||||
*/
|
||||
static void __exit ati_remote_exit(void)
|
||||
{
|
||||
usb_deregister(&ati_remote_driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* module specification
|
||||
*/
|
||||
|
||||
module_init(ati_remote_init);
|
||||
module_exit(ati_remote_exit);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
90
drivers/usb/input/fixp-arith.h
Normal file
90
drivers/usb/input/fixp-arith.h
Normal file
@@ -0,0 +1,90 @@
|
||||
#ifndef _FIXP_ARITH_H
|
||||
#define _FIXP_ARITH_H
|
||||
|
||||
/*
|
||||
* $$
|
||||
*
|
||||
* Simplistic fixed-point arithmetics.
|
||||
* Hmm, I'm probably duplicating some code :(
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* Should you need to contact me, the author, you can do so by
|
||||
* e-mail - mail your message to <deneux@ifrance.com>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
// The type representing fixed-point values
|
||||
typedef s16 fixp_t;
|
||||
|
||||
#define FRAC_N 8
|
||||
#define FRAC_MASK ((1<<FRAC_N)-1)
|
||||
|
||||
// Not to be used directly. Use fixp_{cos,sin}
|
||||
static fixp_t cos_table[45] = {
|
||||
0x0100, 0x00FF, 0x00FF, 0x00FE, 0x00FD, 0x00FC, 0x00FA, 0x00F8,
|
||||
0x00F6, 0x00F3, 0x00F0, 0x00ED, 0x00E9, 0x00E6, 0x00E2, 0x00DD,
|
||||
0x00D9, 0x00D4, 0x00CF, 0x00C9, 0x00C4, 0x00BE, 0x00B8, 0x00B1,
|
||||
0x00AB, 0x00A4, 0x009D, 0x0096, 0x008F, 0x0087, 0x0080, 0x0078,
|
||||
0x0070, 0x0068, 0x005F, 0x0057, 0x004F, 0x0046, 0x003D, 0x0035,
|
||||
0x002C, 0x0023, 0x001A, 0x0011, 0x0008
|
||||
};
|
||||
|
||||
|
||||
/* a: 123 -> 123.0 */
|
||||
static inline fixp_t fixp_new(s16 a)
|
||||
{
|
||||
return a<<FRAC_N;
|
||||
}
|
||||
|
||||
/* a: 0xFFFF -> -1.0
|
||||
0x8000 -> 1.0
|
||||
0x0000 -> 0.0
|
||||
*/
|
||||
static inline fixp_t fixp_new16(s16 a)
|
||||
{
|
||||
return ((s32)a)>>(16-FRAC_N);
|
||||
}
|
||||
|
||||
static inline fixp_t fixp_cos(unsigned int degrees)
|
||||
{
|
||||
int quadrant = (degrees / 90) & 3;
|
||||
unsigned int i = degrees % 90;
|
||||
|
||||
if (quadrant == 1 || quadrant == 3) {
|
||||
i = 89 - i;
|
||||
}
|
||||
|
||||
i >>= 1;
|
||||
|
||||
return (quadrant == 1 || quadrant == 2)? -cos_table[i] : cos_table[i];
|
||||
}
|
||||
|
||||
static inline fixp_t fixp_sin(unsigned int degrees)
|
||||
{
|
||||
return -fixp_cos(degrees + 90);
|
||||
}
|
||||
|
||||
static inline fixp_t fixp_mult(fixp_t a, fixp_t b)
|
||||
{
|
||||
return ((s32)(a*b))>>FRAC_N;
|
||||
}
|
||||
|
||||
#endif
|
1864
drivers/usb/input/hid-core.c
Normal file
1864
drivers/usb/input/hid-core.c
Normal file
File diff suppressed because it is too large
Load Diff
720
drivers/usb/input/hid-debug.h
Normal file
720
drivers/usb/input/hid-debug.h
Normal file
@@ -0,0 +1,720 @@
|
||||
/*
|
||||
* $Id: hid-debug.h,v 1.8 2001/09/25 09:37:57 vojtech Exp $
|
||||
*
|
||||
* (c) 1999 Andreas Gal <gal@cs.uni-magdeburg.de>
|
||||
* (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz>
|
||||
*
|
||||
* Some debug stuff for the HID parser.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <vojtech@ucw.cz>, or by paper mail:
|
||||
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
|
||||
*/
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
struct hid_usage_entry {
|
||||
unsigned page;
|
||||
unsigned usage;
|
||||
char *description;
|
||||
};
|
||||
|
||||
static const struct hid_usage_entry hid_usage_table[] = {
|
||||
{ 0, 0, "Undefined" },
|
||||
{ 1, 0, "GenericDesktop" },
|
||||
{0, 0x01, "Pointer"},
|
||||
{0, 0x02, "Mouse"},
|
||||
{0, 0x04, "Joystick"},
|
||||
{0, 0x05, "GamePad"},
|
||||
{0, 0x06, "Keyboard"},
|
||||
{0, 0x07, "Keypad"},
|
||||
{0, 0x08, "MultiAxis"},
|
||||
{0, 0x30, "X"},
|
||||
{0, 0x31, "Y"},
|
||||
{0, 0x32, "Z"},
|
||||
{0, 0x33, "Rx"},
|
||||
{0, 0x34, "Ry"},
|
||||
{0, 0x35, "Rz"},
|
||||
{0, 0x36, "Slider"},
|
||||
{0, 0x37, "Dial"},
|
||||
{0, 0x38, "Wheel"},
|
||||
{0, 0x39, "HatSwitch"},
|
||||
{0, 0x3a, "CountedBuffer"},
|
||||
{0, 0x3b, "ByteCount"},
|
||||
{0, 0x3c, "MotionWakeup"},
|
||||
{0, 0x3d, "Start"},
|
||||
{0, 0x3e, "Select"},
|
||||
{0, 0x40, "Vx"},
|
||||
{0, 0x41, "Vy"},
|
||||
{0, 0x42, "Vz"},
|
||||
{0, 0x43, "Vbrx"},
|
||||
{0, 0x44, "Vbry"},
|
||||
{0, 0x45, "Vbrz"},
|
||||
{0, 0x46, "Vno"},
|
||||
{0, 0x80, "SystemControl"},
|
||||
{0, 0x81, "SystemPowerDown"},
|
||||
{0, 0x82, "SystemSleep"},
|
||||
{0, 0x83, "SystemWakeUp"},
|
||||
{0, 0x84, "SystemContextMenu"},
|
||||
{0, 0x85, "SystemMainMenu"},
|
||||
{0, 0x86, "SystemAppMenu"},
|
||||
{0, 0x87, "SystemMenuHelp"},
|
||||
{0, 0x88, "SystemMenuExit"},
|
||||
{0, 0x89, "SystemMenuSelect"},
|
||||
{0, 0x8a, "SystemMenuRight"},
|
||||
{0, 0x8b, "SystemMenuLeft"},
|
||||
{0, 0x8c, "SystemMenuUp"},
|
||||
{0, 0x8d, "SystemMenuDown"},
|
||||
{0, 0x90, "D-PadUp"},
|
||||
{0, 0x91, "D-PadDown"},
|
||||
{0, 0x92, "D-PadRight"},
|
||||
{0, 0x93, "D-PadLeft"},
|
||||
{ 7, 0, "Keyboard" },
|
||||
{ 8, 0, "LED" },
|
||||
{0, 0x01, "NumLock"},
|
||||
{0, 0x02, "CapsLock"},
|
||||
{0, 0x03, "ScrollLock"},
|
||||
{0, 0x04, "Compose"},
|
||||
{0, 0x05, "Kana"},
|
||||
{ 9, 0, "Button" },
|
||||
{ 10, 0, "Ordinal" },
|
||||
{ 12, 0, "Consumer" },
|
||||
{0, 0x238, "HorizontalWheel"},
|
||||
{ 13, 0, "Digitizers" },
|
||||
{0, 0x01, "Digitizer"},
|
||||
{0, 0x02, "Pen"},
|
||||
{0, 0x03, "LightPen"},
|
||||
{0, 0x04, "TouchScreen"},
|
||||
{0, 0x05, "TouchPad"},
|
||||
{0, 0x20, "Stylus"},
|
||||
{0, 0x21, "Puck"},
|
||||
{0, 0x22, "Finger"},
|
||||
{0, 0x30, "TipPressure"},
|
||||
{0, 0x31, "BarrelPressure"},
|
||||
{0, 0x32, "InRange"},
|
||||
{0, 0x33, "Touch"},
|
||||
{0, 0x34, "UnTouch"},
|
||||
{0, 0x35, "Tap"},
|
||||
{0, 0x39, "TabletFunctionKey"},
|
||||
{0, 0x3a, "ProgramChangeKey"},
|
||||
{0, 0x3c, "Invert"},
|
||||
{0, 0x42, "TipSwitch"},
|
||||
{0, 0x43, "SecondaryTipSwitch"},
|
||||
{0, 0x44, "BarrelSwitch"},
|
||||
{0, 0x45, "Eraser"},
|
||||
{0, 0x46, "TabletPick"},
|
||||
{ 15, 0, "PhysicalInterfaceDevice" },
|
||||
{0, 0x00, "Undefined"},
|
||||
{0, 0x01, "Physical_Interface_Device"},
|
||||
{0, 0x20, "Normal"},
|
||||
{0, 0x21, "Set_Effect_Report"},
|
||||
{0, 0x22, "Effect_Block_Index"},
|
||||
{0, 0x23, "Parameter_Block_Offset"},
|
||||
{0, 0x24, "ROM_Flag"},
|
||||
{0, 0x25, "Effect_Type"},
|
||||
{0, 0x26, "ET_Constant_Force"},
|
||||
{0, 0x27, "ET_Ramp"},
|
||||
{0, 0x28, "ET_Custom_Force_Data"},
|
||||
{0, 0x30, "ET_Square"},
|
||||
{0, 0x31, "ET_Sine"},
|
||||
{0, 0x32, "ET_Triangle"},
|
||||
{0, 0x33, "ET_Sawtooth_Up"},
|
||||
{0, 0x34, "ET_Sawtooth_Down"},
|
||||
{0, 0x40, "ET_Spring"},
|
||||
{0, 0x41, "ET_Damper"},
|
||||
{0, 0x42, "ET_Inertia"},
|
||||
{0, 0x43, "ET_Friction"},
|
||||
{0, 0x50, "Duration"},
|
||||
{0, 0x51, "Sample_Period"},
|
||||
{0, 0x52, "Gain"},
|
||||
{0, 0x53, "Trigger_Button"},
|
||||
{0, 0x54, "Trigger_Repeat_Interval"},
|
||||
{0, 0x55, "Axes_Enable"},
|
||||
{0, 0x56, "Direction_Enable"},
|
||||
{0, 0x57, "Direction"},
|
||||
{0, 0x58, "Type_Specific_Block_Offset"},
|
||||
{0, 0x59, "Block_Type"},
|
||||
{0, 0x5A, "Set_Envelope_Report"},
|
||||
{0, 0x5B, "Attack_Level"},
|
||||
{0, 0x5C, "Attack_Time"},
|
||||
{0, 0x5D, "Fade_Level"},
|
||||
{0, 0x5E, "Fade_Time"},
|
||||
{0, 0x5F, "Set_Condition_Report"},
|
||||
{0, 0x60, "CP_Offset"},
|
||||
{0, 0x61, "Positive_Coefficient"},
|
||||
{0, 0x62, "Negative_Coefficient"},
|
||||
{0, 0x63, "Positive_Saturation"},
|
||||
{0, 0x64, "Negative_Saturation"},
|
||||
{0, 0x65, "Dead_Band"},
|
||||
{0, 0x66, "Download_Force_Sample"},
|
||||
{0, 0x67, "Isoch_Custom_Force_Enable"},
|
||||
{0, 0x68, "Custom_Force_Data_Report"},
|
||||
{0, 0x69, "Custom_Force_Data"},
|
||||
{0, 0x6A, "Custom_Force_Vendor_Defined_Data"},
|
||||
{0, 0x6B, "Set_Custom_Force_Report"},
|
||||
{0, 0x6C, "Custom_Force_Data_Offset"},
|
||||
{0, 0x6D, "Sample_Count"},
|
||||
{0, 0x6E, "Set_Periodic_Report"},
|
||||
{0, 0x6F, "Offset"},
|
||||
{0, 0x70, "Magnitude"},
|
||||
{0, 0x71, "Phase"},
|
||||
{0, 0x72, "Period"},
|
||||
{0, 0x73, "Set_Constant_Force_Report"},
|
||||
{0, 0x74, "Set_Ramp_Force_Report"},
|
||||
{0, 0x75, "Ramp_Start"},
|
||||
{0, 0x76, "Ramp_End"},
|
||||
{0, 0x77, "Effect_Operation_Report"},
|
||||
{0, 0x78, "Effect_Operation"},
|
||||
{0, 0x79, "Op_Effect_Start"},
|
||||
{0, 0x7A, "Op_Effect_Start_Solo"},
|
||||
{0, 0x7B, "Op_Effect_Stop"},
|
||||
{0, 0x7C, "Loop_Count"},
|
||||
{0, 0x7D, "Device_Gain_Report"},
|
||||
{0, 0x7E, "Device_Gain"},
|
||||
{0, 0x7F, "PID_Pool_Report"},
|
||||
{0, 0x80, "RAM_Pool_Size"},
|
||||
{0, 0x81, "ROM_Pool_Size"},
|
||||
{0, 0x82, "ROM_Effect_Block_Count"},
|
||||
{0, 0x83, "Simultaneous_Effects_Max"},
|
||||
{0, 0x84, "Pool_Alignment"},
|
||||
{0, 0x85, "PID_Pool_Move_Report"},
|
||||
{0, 0x86, "Move_Source"},
|
||||
{0, 0x87, "Move_Destination"},
|
||||
{0, 0x88, "Move_Length"},
|
||||
{0, 0x89, "PID_Block_Load_Report"},
|
||||
{0, 0x8B, "Block_Load_Status"},
|
||||
{0, 0x8C, "Block_Load_Success"},
|
||||
{0, 0x8D, "Block_Load_Full"},
|
||||
{0, 0x8E, "Block_Load_Error"},
|
||||
{0, 0x8F, "Block_Handle"},
|
||||
{0, 0x90, "PID_Block_Free_Report"},
|
||||
{0, 0x91, "Type_Specific_Block_Handle"},
|
||||
{0, 0x92, "PID_State_Report"},
|
||||
{0, 0x94, "Effect_Playing"},
|
||||
{0, 0x95, "PID_Device_Control_Report"},
|
||||
{0, 0x96, "PID_Device_Control"},
|
||||
{0, 0x97, "DC_Enable_Actuators"},
|
||||
{0, 0x98, "DC_Disable_Actuators"},
|
||||
{0, 0x99, "DC_Stop_All_Effects"},
|
||||
{0, 0x9A, "DC_Device_Reset"},
|
||||
{0, 0x9B, "DC_Device_Pause"},
|
||||
{0, 0x9C, "DC_Device_Continue"},
|
||||
{0, 0x9F, "Device_Paused"},
|
||||
{0, 0xA0, "Actuators_Enabled"},
|
||||
{0, 0xA4, "Safety_Switch"},
|
||||
{0, 0xA5, "Actuator_Override_Switch"},
|
||||
{0, 0xA6, "Actuator_Power"},
|
||||
{0, 0xA7, "Start_Delay"},
|
||||
{0, 0xA8, "Parameter_Block_Size"},
|
||||
{0, 0xA9, "Device_Managed_Pool"},
|
||||
{0, 0xAA, "Shared_Parameter_Blocks"},
|
||||
{0, 0xAB, "Create_New_Effect_Report"},
|
||||
{0, 0xAC, "RAM_Pool_Available"},
|
||||
{ 0x84, 0, "Power Device" },
|
||||
{ 0x84, 0x02, "PresentStatus" },
|
||||
{ 0x84, 0x03, "ChangeStatus" },
|
||||
{ 0x84, 0x04, "UPS" },
|
||||
{ 0x84, 0x05, "PowerSupply" },
|
||||
{ 0x84, 0x10, "BatterySystem" },
|
||||
{ 0x84, 0x11, "BatterySystemID" },
|
||||
{ 0x84, 0x12, "Battery" },
|
||||
{ 0x84, 0x13, "BatteryID" },
|
||||
{ 0x84, 0x14, "Charger" },
|
||||
{ 0x84, 0x15, "ChargerID" },
|
||||
{ 0x84, 0x16, "PowerConverter" },
|
||||
{ 0x84, 0x17, "PowerConverterID" },
|
||||
{ 0x84, 0x18, "OutletSystem" },
|
||||
{ 0x84, 0x19, "OutletSystemID" },
|
||||
{ 0x84, 0x1a, "Input" },
|
||||
{ 0x84, 0x1b, "InputID" },
|
||||
{ 0x84, 0x1c, "Output" },
|
||||
{ 0x84, 0x1d, "OutputID" },
|
||||
{ 0x84, 0x1e, "Flow" },
|
||||
{ 0x84, 0x1f, "FlowID" },
|
||||
{ 0x84, 0x20, "Outlet" },
|
||||
{ 0x84, 0x21, "OutletID" },
|
||||
{ 0x84, 0x22, "Gang" },
|
||||
{ 0x84, 0x24, "PowerSummary" },
|
||||
{ 0x84, 0x25, "PowerSummaryID" },
|
||||
{ 0x84, 0x30, "Voltage" },
|
||||
{ 0x84, 0x31, "Current" },
|
||||
{ 0x84, 0x32, "Frequency" },
|
||||
{ 0x84, 0x33, "ApparentPower" },
|
||||
{ 0x84, 0x35, "PercentLoad" },
|
||||
{ 0x84, 0x40, "ConfigVoltage" },
|
||||
{ 0x84, 0x41, "ConfigCurrent" },
|
||||
{ 0x84, 0x43, "ConfigApparentPower" },
|
||||
{ 0x84, 0x53, "LowVoltageTransfer" },
|
||||
{ 0x84, 0x54, "HighVoltageTransfer" },
|
||||
{ 0x84, 0x56, "DelayBeforeStartup" },
|
||||
{ 0x84, 0x57, "DelayBeforeShutdown" },
|
||||
{ 0x84, 0x58, "Test" },
|
||||
{ 0x84, 0x5a, "AudibleAlarmControl" },
|
||||
{ 0x84, 0x60, "Present" },
|
||||
{ 0x84, 0x61, "Good" },
|
||||
{ 0x84, 0x62, "InternalFailure" },
|
||||
{ 0x84, 0x65, "Overload" },
|
||||
{ 0x84, 0x66, "OverCharged" },
|
||||
{ 0x84, 0x67, "OverTemperature" },
|
||||
{ 0x84, 0x68, "ShutdownRequested" },
|
||||
{ 0x84, 0x69, "ShutdownImminent" },
|
||||
{ 0x84, 0x6b, "SwitchOn/Off" },
|
||||
{ 0x84, 0x6c, "Switchable" },
|
||||
{ 0x84, 0x6d, "Used" },
|
||||
{ 0x84, 0x6e, "Boost" },
|
||||
{ 0x84, 0x73, "CommunicationLost" },
|
||||
{ 0x84, 0xfd, "iManufacturer" },
|
||||
{ 0x84, 0xfe, "iProduct" },
|
||||
{ 0x84, 0xff, "iSerialNumber" },
|
||||
{ 0x85, 0, "Battery System" },
|
||||
{ 0x85, 0x01, "SMBBatteryMode" },
|
||||
{ 0x85, 0x02, "SMBBatteryStatus" },
|
||||
{ 0x85, 0x03, "SMBAlarmWarning" },
|
||||
{ 0x85, 0x04, "SMBChargerMode" },
|
||||
{ 0x85, 0x05, "SMBChargerStatus" },
|
||||
{ 0x85, 0x06, "SMBChargerSpecInfo" },
|
||||
{ 0x85, 0x07, "SMBSelectorState" },
|
||||
{ 0x85, 0x08, "SMBSelectorPresets" },
|
||||
{ 0x85, 0x09, "SMBSelectorInfo" },
|
||||
{ 0x85, 0x29, "RemainingCapacityLimit" },
|
||||
{ 0x85, 0x2c, "CapacityMode" },
|
||||
{ 0x85, 0x42, "BelowRemainingCapacityLimit" },
|
||||
{ 0x85, 0x44, "Charging" },
|
||||
{ 0x85, 0x45, "Discharging" },
|
||||
{ 0x85, 0x4b, "NeedReplacement" },
|
||||
{ 0x85, 0x66, "RemainingCapacity" },
|
||||
{ 0x85, 0x68, "RunTimeToEmpty" },
|
||||
{ 0x85, 0x6a, "AverageTimeToFull" },
|
||||
{ 0x85, 0x83, "DesignCapacity" },
|
||||
{ 0x85, 0x85, "ManufacturerDate" },
|
||||
{ 0x85, 0x89, "iDeviceChemistry" },
|
||||
{ 0x85, 0x8b, "Rechargable" },
|
||||
{ 0x85, 0x8f, "iOEMInformation" },
|
||||
{ 0x85, 0x8d, "CapacityGranularity1" },
|
||||
{ 0x85, 0xd0, "ACPresent" },
|
||||
/* pages 0xff00 to 0xffff are vendor-specific */
|
||||
{ 0xffff, 0, "Vendor-specific-FF" },
|
||||
{ 0, 0, NULL }
|
||||
};
|
||||
|
||||
static void resolv_usage_page(unsigned page) {
|
||||
const struct hid_usage_entry *p;
|
||||
|
||||
for (p = hid_usage_table; p->description; p++)
|
||||
if (p->page == page) {
|
||||
printk("%s", p->description);
|
||||
return;
|
||||
}
|
||||
printk("%04x", page);
|
||||
}
|
||||
|
||||
static void resolv_usage(unsigned usage) {
|
||||
const struct hid_usage_entry *p;
|
||||
|
||||
resolv_usage_page(usage >> 16);
|
||||
printk(".");
|
||||
for (p = hid_usage_table; p->description; p++)
|
||||
if (p->page == (usage >> 16)) {
|
||||
for(++p; p->description && p->usage != 0; p++)
|
||||
if (p->usage == (usage & 0xffff)) {
|
||||
printk("%s", p->description);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
printk("%04x", usage & 0xffff);
|
||||
}
|
||||
|
||||
__inline__ static void tab(int n) {
|
||||
while (n--) printk(" ");
|
||||
}
|
||||
|
||||
static void hid_dump_field(struct hid_field *field, int n) {
|
||||
int j;
|
||||
|
||||
if (field->physical) {
|
||||
tab(n);
|
||||
printk("Physical(");
|
||||
resolv_usage(field->physical); printk(")\n");
|
||||
}
|
||||
if (field->logical) {
|
||||
tab(n);
|
||||
printk("Logical(");
|
||||
resolv_usage(field->logical); printk(")\n");
|
||||
}
|
||||
tab(n); printk("Usage(%d)\n", field->maxusage);
|
||||
for (j = 0; j < field->maxusage; j++) {
|
||||
tab(n+2);resolv_usage(field->usage[j].hid); printk("\n");
|
||||
}
|
||||
if (field->logical_minimum != field->logical_maximum) {
|
||||
tab(n); printk("Logical Minimum(%d)\n", field->logical_minimum);
|
||||
tab(n); printk("Logical Maximum(%d)\n", field->logical_maximum);
|
||||
}
|
||||
if (field->physical_minimum != field->physical_maximum) {
|
||||
tab(n); printk("Physical Minimum(%d)\n", field->physical_minimum);
|
||||
tab(n); printk("Physical Maximum(%d)\n", field->physical_maximum);
|
||||
}
|
||||
if (field->unit_exponent) {
|
||||
tab(n); printk("Unit Exponent(%d)\n", field->unit_exponent);
|
||||
}
|
||||
if (field->unit) {
|
||||
char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" };
|
||||
char *units[5][8] = {
|
||||
{ "None", "None", "None", "None", "None", "None", "None", "None" },
|
||||
{ "None", "Centimeter", "Gram", "Seconds", "Kelvin", "Ampere", "Candela", "None" },
|
||||
{ "None", "Radians", "Gram", "Seconds", "Kelvin", "Ampere", "Candela", "None" },
|
||||
{ "None", "Inch", "Slug", "Seconds", "Fahrenheit", "Ampere", "Candela", "None" },
|
||||
{ "None", "Degrees", "Slug", "Seconds", "Fahrenheit", "Ampere", "Candela", "None" }
|
||||
};
|
||||
|
||||
int i;
|
||||
int sys;
|
||||
__u32 data = field->unit;
|
||||
|
||||
/* First nibble tells us which system we're in. */
|
||||
sys = data & 0xf;
|
||||
data >>= 4;
|
||||
|
||||
if(sys > 4) {
|
||||
tab(n); printk("Unit(Invalid)\n");
|
||||
}
|
||||
else {
|
||||
int earlier_unit = 0;
|
||||
|
||||
tab(n); printk("Unit(%s : ", systems[sys]);
|
||||
|
||||
for (i=1 ; i<sizeof(__u32)*2 ; i++) {
|
||||
char nibble = data & 0xf;
|
||||
data >>= 4;
|
||||
if (nibble != 0) {
|
||||
if(earlier_unit++ > 0)
|
||||
printk("*");
|
||||
printk("%s", units[sys][i]);
|
||||
if(nibble != 1) {
|
||||
/* This is a _signed_ nibble(!) */
|
||||
|
||||
int val = nibble & 0x7;
|
||||
if(nibble & 0x08)
|
||||
val = -((0x7 & ~val) +1);
|
||||
printk("^%d", val);
|
||||
}
|
||||
}
|
||||
}
|
||||
printk(")\n");
|
||||
}
|
||||
}
|
||||
tab(n); printk("Report Size(%u)\n", field->report_size);
|
||||
tab(n); printk("Report Count(%u)\n", field->report_count);
|
||||
tab(n); printk("Report Offset(%u)\n", field->report_offset);
|
||||
|
||||
tab(n); printk("Flags( ");
|
||||
j = field->flags;
|
||||
printk("%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : "");
|
||||
printk("%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array ");
|
||||
printk("%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute ");
|
||||
printk("%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : "");
|
||||
printk("%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : "");
|
||||
printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPrefferedState " : "");
|
||||
printk("%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : "");
|
||||
printk("%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : "");
|
||||
printk("%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : "");
|
||||
printk(")\n");
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) hid_dump_device(struct hid_device *device) {
|
||||
struct hid_report_enum *report_enum;
|
||||
struct hid_report *report;
|
||||
struct list_head *list;
|
||||
unsigned i,k;
|
||||
static char *table[] = {"INPUT", "OUTPUT", "FEATURE"};
|
||||
|
||||
for (i = 0; i < HID_REPORT_TYPES; i++) {
|
||||
report_enum = device->report_enum + i;
|
||||
list = report_enum->report_list.next;
|
||||
while (list != &report_enum->report_list) {
|
||||
report = (struct hid_report *) list;
|
||||
tab(2);
|
||||
printk("%s", table[i]);
|
||||
if (report->id)
|
||||
printk("(%d)", report->id);
|
||||
printk("[%s]", table[report->type]);
|
||||
printk("\n");
|
||||
for (k = 0; k < report->maxfield; k++) {
|
||||
tab(4);
|
||||
printk("Field(%d)\n", k);
|
||||
hid_dump_field(report->field[k], 6);
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) hid_dump_input(struct hid_usage *usage, __s32 value) {
|
||||
printk("hid-debug: input ");
|
||||
resolv_usage(usage->hid);
|
||||
printk(" = %d\n", value);
|
||||
}
|
||||
|
||||
|
||||
static char *events[EV_MAX + 1] = {
|
||||
[EV_SYN] = "Sync", [EV_KEY] = "Key",
|
||||
[EV_REL] = "Relative", [EV_ABS] = "Absolute",
|
||||
[EV_MSC] = "Misc", [EV_LED] = "LED",
|
||||
[EV_SND] = "Sound", [EV_REP] = "Repeat",
|
||||
[EV_FF] = "ForceFeedback", [EV_PWR] = "Power",
|
||||
[EV_FF_STATUS] = "ForceFeedbackStatus",
|
||||
};
|
||||
|
||||
static char *syncs[2] = {
|
||||
[SYN_REPORT] = "Report", [SYN_CONFIG] = "Config",
|
||||
};
|
||||
static char *keys[KEY_MAX + 1] = {
|
||||
[KEY_RESERVED] = "Reserved", [KEY_ESC] = "Esc",
|
||||
[KEY_1] = "1", [KEY_2] = "2",
|
||||
[KEY_3] = "3", [KEY_4] = "4",
|
||||
[KEY_5] = "5", [KEY_6] = "6",
|
||||
[KEY_7] = "7", [KEY_8] = "8",
|
||||
[KEY_9] = "9", [KEY_0] = "0",
|
||||
[KEY_MINUS] = "Minus", [KEY_EQUAL] = "Equal",
|
||||
[KEY_BACKSPACE] = "Backspace", [KEY_TAB] = "Tab",
|
||||
[KEY_Q] = "Q", [KEY_W] = "W",
|
||||
[KEY_E] = "E", [KEY_R] = "R",
|
||||
[KEY_T] = "T", [KEY_Y] = "Y",
|
||||
[KEY_U] = "U", [KEY_I] = "I",
|
||||
[KEY_O] = "O", [KEY_P] = "P",
|
||||
[KEY_LEFTBRACE] = "LeftBrace", [KEY_RIGHTBRACE] = "RightBrace",
|
||||
[KEY_ENTER] = "Enter", [KEY_LEFTCTRL] = "LeftControl",
|
||||
[KEY_A] = "A", [KEY_S] = "S",
|
||||
[KEY_D] = "D", [KEY_F] = "F",
|
||||
[KEY_G] = "G", [KEY_H] = "H",
|
||||
[KEY_J] = "J", [KEY_K] = "K",
|
||||
[KEY_L] = "L", [KEY_SEMICOLON] = "Semicolon",
|
||||
[KEY_APOSTROPHE] = "Apostrophe", [KEY_GRAVE] = "Grave",
|
||||
[KEY_LEFTSHIFT] = "LeftShift", [KEY_BACKSLASH] = "BackSlash",
|
||||
[KEY_Z] = "Z", [KEY_X] = "X",
|
||||
[KEY_C] = "C", [KEY_V] = "V",
|
||||
[KEY_B] = "B", [KEY_N] = "N",
|
||||
[KEY_M] = "M", [KEY_COMMA] = "Comma",
|
||||
[KEY_DOT] = "Dot", [KEY_SLASH] = "Slash",
|
||||
[KEY_RIGHTSHIFT] = "RightShift", [KEY_KPASTERISK] = "KPAsterisk",
|
||||
[KEY_LEFTALT] = "LeftAlt", [KEY_SPACE] = "Space",
|
||||
[KEY_CAPSLOCK] = "CapsLock", [KEY_F1] = "F1",
|
||||
[KEY_F2] = "F2", [KEY_F3] = "F3",
|
||||
[KEY_F4] = "F4", [KEY_F5] = "F5",
|
||||
[KEY_F6] = "F6", [KEY_F7] = "F7",
|
||||
[KEY_F8] = "F8", [KEY_F9] = "F9",
|
||||
[KEY_F10] = "F10", [KEY_NUMLOCK] = "NumLock",
|
||||
[KEY_SCROLLLOCK] = "ScrollLock", [KEY_KP7] = "KP7",
|
||||
[KEY_KP8] = "KP8", [KEY_KP9] = "KP9",
|
||||
[KEY_KPMINUS] = "KPMinus", [KEY_KP4] = "KP4",
|
||||
[KEY_KP5] = "KP5", [KEY_KP6] = "KP6",
|
||||
[KEY_KPPLUS] = "KPPlus", [KEY_KP1] = "KP1",
|
||||
[KEY_KP2] = "KP2", [KEY_KP3] = "KP3",
|
||||
[KEY_KP0] = "KP0", [KEY_KPDOT] = "KPDot",
|
||||
[KEY_ZENKAKUHANKAKU] = "Zenkaku/Hankaku", [KEY_102ND] = "102nd",
|
||||
[KEY_F11] = "F11", [KEY_F12] = "F12",
|
||||
[KEY_RO] = "RO", [KEY_KATAKANA] = "Katakana",
|
||||
[KEY_HIRAGANA] = "HIRAGANA", [KEY_HENKAN] = "Henkan",
|
||||
[KEY_KATAKANAHIRAGANA] = "Katakana/Hiragana", [KEY_MUHENKAN] = "Muhenkan",
|
||||
[KEY_KPJPCOMMA] = "KPJpComma", [KEY_KPENTER] = "KPEnter",
|
||||
[KEY_RIGHTCTRL] = "RightCtrl", [KEY_KPSLASH] = "KPSlash",
|
||||
[KEY_SYSRQ] = "SysRq", [KEY_RIGHTALT] = "RightAlt",
|
||||
[KEY_LINEFEED] = "LineFeed", [KEY_HOME] = "Home",
|
||||
[KEY_UP] = "Up", [KEY_PAGEUP] = "PageUp",
|
||||
[KEY_LEFT] = "Left", [KEY_RIGHT] = "Right",
|
||||
[KEY_END] = "End", [KEY_DOWN] = "Down",
|
||||
[KEY_PAGEDOWN] = "PageDown", [KEY_INSERT] = "Insert",
|
||||
[KEY_DELETE] = "Delete", [KEY_MACRO] = "Macro",
|
||||
[KEY_MUTE] = "Mute", [KEY_VOLUMEDOWN] = "VolumeDown",
|
||||
[KEY_VOLUMEUP] = "VolumeUp", [KEY_POWER] = "Power",
|
||||
[KEY_KPEQUAL] = "KPEqual", [KEY_KPPLUSMINUS] = "KPPlusMinus",
|
||||
[KEY_PAUSE] = "Pause", [KEY_KPCOMMA] = "KPComma",
|
||||
[KEY_HANGUEL] = "Hanguel", [KEY_HANJA] = "Hanja",
|
||||
[KEY_YEN] = "Yen", [KEY_LEFTMETA] = "LeftMeta",
|
||||
[KEY_RIGHTMETA] = "RightMeta", [KEY_COMPOSE] = "Compose",
|
||||
[KEY_STOP] = "Stop", [KEY_AGAIN] = "Again",
|
||||
[KEY_PROPS] = "Props", [KEY_UNDO] = "Undo",
|
||||
[KEY_FRONT] = "Front", [KEY_COPY] = "Copy",
|
||||
[KEY_OPEN] = "Open", [KEY_PASTE] = "Paste",
|
||||
[KEY_FIND] = "Find", [KEY_CUT] = "Cut",
|
||||
[KEY_HELP] = "Help", [KEY_MENU] = "Menu",
|
||||
[KEY_CALC] = "Calc", [KEY_SETUP] = "Setup",
|
||||
[KEY_SLEEP] = "Sleep", [KEY_WAKEUP] = "WakeUp",
|
||||
[KEY_FILE] = "File", [KEY_SENDFILE] = "SendFile",
|
||||
[KEY_DELETEFILE] = "DeleteFile", [KEY_XFER] = "X-fer",
|
||||
[KEY_PROG1] = "Prog1", [KEY_PROG2] = "Prog2",
|
||||
[KEY_WWW] = "WWW", [KEY_MSDOS] = "MSDOS",
|
||||
[KEY_COFFEE] = "Coffee", [KEY_DIRECTION] = "Direction",
|
||||
[KEY_CYCLEWINDOWS] = "CycleWindows", [KEY_MAIL] = "Mail",
|
||||
[KEY_BOOKMARKS] = "Bookmarks", [KEY_COMPUTER] = "Computer",
|
||||
[KEY_BACK] = "Back", [KEY_FORWARD] = "Forward",
|
||||
[KEY_CLOSECD] = "CloseCD", [KEY_EJECTCD] = "EjectCD",
|
||||
[KEY_EJECTCLOSECD] = "EjectCloseCD", [KEY_NEXTSONG] = "NextSong",
|
||||
[KEY_PLAYPAUSE] = "PlayPause", [KEY_PREVIOUSSONG] = "PreviousSong",
|
||||
[KEY_STOPCD] = "StopCD", [KEY_RECORD] = "Record",
|
||||
[KEY_REWIND] = "Rewind", [KEY_PHONE] = "Phone",
|
||||
[KEY_ISO] = "ISOKey", [KEY_CONFIG] = "Config",
|
||||
[KEY_HOMEPAGE] = "HomePage", [KEY_REFRESH] = "Refresh",
|
||||
[KEY_EXIT] = "Exit", [KEY_MOVE] = "Move",
|
||||
[KEY_EDIT] = "Edit", [KEY_SCROLLUP] = "ScrollUp",
|
||||
[KEY_SCROLLDOWN] = "ScrollDown", [KEY_KPLEFTPAREN] = "KPLeftParenthesis",
|
||||
[KEY_KPRIGHTPAREN] = "KPRightParenthesis", [KEY_F13] = "F13",
|
||||
[KEY_F14] = "F14", [KEY_F15] = "F15",
|
||||
[KEY_F16] = "F16", [KEY_F17] = "F17",
|
||||
[KEY_F18] = "F18", [KEY_F19] = "F19",
|
||||
[KEY_F20] = "F20", [KEY_F21] = "F21",
|
||||
[KEY_F22] = "F22", [KEY_F23] = "F23",
|
||||
[KEY_F24] = "F24", [KEY_PLAYCD] = "PlayCD",
|
||||
[KEY_PAUSECD] = "PauseCD", [KEY_PROG3] = "Prog3",
|
||||
[KEY_PROG4] = "Prog4", [KEY_SUSPEND] = "Suspend",
|
||||
[KEY_CLOSE] = "Close", [KEY_PLAY] = "Play",
|
||||
[KEY_FASTFORWARD] = "Fast Forward", [KEY_BASSBOOST] = "Bass Boost",
|
||||
[KEY_PRINT] = "Print", [KEY_HP] = "HP",
|
||||
[KEY_CAMERA] = "Camera", [KEY_SOUND] = "Sound",
|
||||
[KEY_QUESTION] = "Question", [KEY_EMAIL] = "Email",
|
||||
[KEY_CHAT] = "Chat", [KEY_SEARCH] = "Search",
|
||||
[KEY_CONNECT] = "Connect", [KEY_FINANCE] = "Finance",
|
||||
[KEY_SPORT] = "Sport", [KEY_SHOP] = "Shop",
|
||||
[KEY_ALTERASE] = "Alternate Erase", [KEY_CANCEL] = "Cancel",
|
||||
[KEY_BRIGHTNESSDOWN] = "Brightness down", [KEY_BRIGHTNESSUP] = "Brightness up",
|
||||
[KEY_MEDIA] = "Media", [KEY_UNKNOWN] = "Unknown",
|
||||
[BTN_0] = "Btn0", [BTN_1] = "Btn1",
|
||||
[BTN_2] = "Btn2", [BTN_3] = "Btn3",
|
||||
[BTN_4] = "Btn4", [BTN_5] = "Btn5",
|
||||
[BTN_6] = "Btn6", [BTN_7] = "Btn7",
|
||||
[BTN_8] = "Btn8", [BTN_9] = "Btn9",
|
||||
[BTN_LEFT] = "LeftBtn", [BTN_RIGHT] = "RightBtn",
|
||||
[BTN_MIDDLE] = "MiddleBtn", [BTN_SIDE] = "SideBtn",
|
||||
[BTN_EXTRA] = "ExtraBtn", [BTN_FORWARD] = "ForwardBtn",
|
||||
[BTN_BACK] = "BackBtn", [BTN_TASK] = "TaskBtn",
|
||||
[BTN_TRIGGER] = "Trigger", [BTN_THUMB] = "ThumbBtn",
|
||||
[BTN_THUMB2] = "ThumbBtn2", [BTN_TOP] = "TopBtn",
|
||||
[BTN_TOP2] = "TopBtn2", [BTN_PINKIE] = "PinkieBtn",
|
||||
[BTN_BASE] = "BaseBtn", [BTN_BASE2] = "BaseBtn2",
|
||||
[BTN_BASE3] = "BaseBtn3", [BTN_BASE4] = "BaseBtn4",
|
||||
[BTN_BASE5] = "BaseBtn5", [BTN_BASE6] = "BaseBtn6",
|
||||
[BTN_DEAD] = "BtnDead", [BTN_A] = "BtnA",
|
||||
[BTN_B] = "BtnB", [BTN_C] = "BtnC",
|
||||
[BTN_X] = "BtnX", [BTN_Y] = "BtnY",
|
||||
[BTN_Z] = "BtnZ", [BTN_TL] = "BtnTL",
|
||||
[BTN_TR] = "BtnTR", [BTN_TL2] = "BtnTL2",
|
||||
[BTN_TR2] = "BtnTR2", [BTN_SELECT] = "BtnSelect",
|
||||
[BTN_START] = "BtnStart", [BTN_MODE] = "BtnMode",
|
||||
[BTN_THUMBL] = "BtnThumbL", [BTN_THUMBR] = "BtnThumbR",
|
||||
[BTN_TOOL_PEN] = "ToolPen", [BTN_TOOL_RUBBER] = "ToolRubber",
|
||||
[BTN_TOOL_BRUSH] = "ToolBrush", [BTN_TOOL_PENCIL] = "ToolPencil",
|
||||
[BTN_TOOL_AIRBRUSH] = "ToolAirbrush", [BTN_TOOL_FINGER] = "ToolFinger",
|
||||
[BTN_TOOL_MOUSE] = "ToolMouse", [BTN_TOOL_LENS] = "ToolLens",
|
||||
[BTN_TOUCH] = "Touch", [BTN_STYLUS] = "Stylus",
|
||||
[BTN_STYLUS2] = "Stylus2", [BTN_TOOL_DOUBLETAP] = "Tool Doubletap",
|
||||
[BTN_TOOL_TRIPLETAP] = "Tool Tripletap", [BTN_GEAR_DOWN] = "WheelBtn",
|
||||
[BTN_GEAR_UP] = "Gear up", [KEY_OK] = "Ok",
|
||||
[KEY_SELECT] = "Select", [KEY_GOTO] = "Goto",
|
||||
[KEY_CLEAR] = "Clear", [KEY_POWER2] = "Power2",
|
||||
[KEY_OPTION] = "Option", [KEY_INFO] = "Info",
|
||||
[KEY_TIME] = "Time", [KEY_VENDOR] = "Vendor",
|
||||
[KEY_ARCHIVE] = "Archive", [KEY_PROGRAM] = "Program",
|
||||
[KEY_CHANNEL] = "Channel", [KEY_FAVORITES] = "Favorites",
|
||||
[KEY_EPG] = "EPG", [KEY_PVR] = "PVR",
|
||||
[KEY_MHP] = "MHP", [KEY_LANGUAGE] = "Language",
|
||||
[KEY_TITLE] = "Title", [KEY_SUBTITLE] = "Subtitle",
|
||||
[KEY_ANGLE] = "Angle", [KEY_ZOOM] = "Zoom",
|
||||
[KEY_MODE] = "Mode", [KEY_KEYBOARD] = "Keyboard",
|
||||
[KEY_SCREEN] = "Screen", [KEY_PC] = "PC",
|
||||
[KEY_TV] = "TV", [KEY_TV2] = "TV2",
|
||||
[KEY_VCR] = "VCR", [KEY_VCR2] = "VCR2",
|
||||
[KEY_SAT] = "Sat", [KEY_SAT2] = "Sat2",
|
||||
[KEY_CD] = "CD", [KEY_TAPE] = "Tape",
|
||||
[KEY_RADIO] = "Radio", [KEY_TUNER] = "Tuner",
|
||||
[KEY_PLAYER] = "Player", [KEY_TEXT] = "Text",
|
||||
[KEY_DVD] = "DVD", [KEY_AUX] = "Aux",
|
||||
[KEY_MP3] = "MP3", [KEY_AUDIO] = "Audio",
|
||||
[KEY_VIDEO] = "Video", [KEY_DIRECTORY] = "Directory",
|
||||
[KEY_LIST] = "List", [KEY_MEMO] = "Memo",
|
||||
[KEY_CALENDAR] = "Calendar", [KEY_RED] = "Red",
|
||||
[KEY_GREEN] = "Green", [KEY_YELLOW] = "Yellow",
|
||||
[KEY_BLUE] = "Blue", [KEY_CHANNELUP] = "ChannelUp",
|
||||
[KEY_CHANNELDOWN] = "ChannelDown", [KEY_FIRST] = "First",
|
||||
[KEY_LAST] = "Last", [KEY_AB] = "AB",
|
||||
[KEY_NEXT] = "Next", [KEY_RESTART] = "Restart",
|
||||
[KEY_SLOW] = "Slow", [KEY_SHUFFLE] = "Shuffle",
|
||||
[KEY_BREAK] = "Break", [KEY_PREVIOUS] = "Previous",
|
||||
[KEY_DIGITS] = "Digits", [KEY_TEEN] = "TEEN",
|
||||
[KEY_TWEN] = "TWEN", [KEY_DEL_EOL] = "DeleteEOL",
|
||||
[KEY_DEL_EOS] = "DeleteEOS", [KEY_INS_LINE] = "InsertLine",
|
||||
[KEY_DEL_LINE] = "DeleteLine",
|
||||
};
|
||||
|
||||
static char *relatives[REL_MAX + 1] = {
|
||||
[REL_X] = "X", [REL_Y] = "Y",
|
||||
[REL_Z] = "Z", [REL_HWHEEL] = "HWheel",
|
||||
[REL_DIAL] = "Dial", [REL_WHEEL] = "Wheel",
|
||||
[REL_MISC] = "Misc",
|
||||
};
|
||||
|
||||
static char *absolutes[ABS_MAX + 1] = {
|
||||
[ABS_X] = "X", [ABS_Y] = "Y",
|
||||
[ABS_Z] = "Z", [ABS_RX] = "Rx",
|
||||
[ABS_RY] = "Ry", [ABS_RZ] = "Rz",
|
||||
[ABS_THROTTLE] = "Throttle", [ABS_RUDDER] = "Rudder",
|
||||
[ABS_WHEEL] = "Wheel", [ABS_GAS] = "Gas",
|
||||
[ABS_BRAKE] = "Brake", [ABS_HAT0X] = "Hat0X",
|
||||
[ABS_HAT0Y] = "Hat0Y", [ABS_HAT1X] = "Hat1X",
|
||||
[ABS_HAT1Y] = "Hat1Y", [ABS_HAT2X] = "Hat2X",
|
||||
[ABS_HAT2Y] = "Hat2Y", [ABS_HAT3X] = "Hat3X",
|
||||
[ABS_HAT3Y] = "Hat 3Y", [ABS_PRESSURE] = "Pressure",
|
||||
[ABS_DISTANCE] = "Distance", [ABS_TILT_X] = "XTilt",
|
||||
[ABS_TILT_Y] = "YTilt", [ABS_TOOL_WIDTH] = "Tool Width",
|
||||
[ABS_VOLUME] = "Volume", [ABS_MISC] = "Misc",
|
||||
};
|
||||
|
||||
static char *misc[MSC_MAX + 1] = {
|
||||
[MSC_SERIAL] = "Serial", [MSC_PULSELED] = "Pulseled",
|
||||
[MSC_GESTURE] = "Gesture", [MSC_RAW] = "RawData"
|
||||
};
|
||||
|
||||
static char *leds[LED_MAX + 1] = {
|
||||
[LED_NUML] = "NumLock", [LED_CAPSL] = "CapsLock",
|
||||
[LED_SCROLLL] = "ScrollLock", [LED_COMPOSE] = "Compose",
|
||||
[LED_KANA] = "Kana", [LED_SLEEP] = "Sleep",
|
||||
[LED_SUSPEND] = "Suspend", [LED_MUTE] = "Mute",
|
||||
[LED_MISC] = "Misc",
|
||||
};
|
||||
|
||||
static char *repeats[REP_MAX + 1] = {
|
||||
[REP_DELAY] = "Delay", [REP_PERIOD] = "Period"
|
||||
};
|
||||
|
||||
static char *sounds[SND_MAX + 1] = {
|
||||
[SND_CLICK] = "Click", [SND_BELL] = "Bell",
|
||||
[SND_TONE] = "Tone"
|
||||
};
|
||||
|
||||
static char **names[EV_MAX + 1] = {
|
||||
[EV_SYN] = syncs, [EV_KEY] = keys,
|
||||
[EV_REL] = relatives, [EV_ABS] = absolutes,
|
||||
[EV_MSC] = misc, [EV_LED] = leds,
|
||||
[EV_SND] = sounds, [EV_REP] = repeats,
|
||||
};
|
||||
|
||||
static void __attribute__((unused)) resolv_event(__u8 type, __u16 code) {
|
||||
|
||||
printk("%s.%s", events[type] ? events[type] : "?",
|
||||
names[type] ? (names[type][code] ? names[type][code] : "?") : "?");
|
||||
}
|
94
drivers/usb/input/hid-ff.c
Normal file
94
drivers/usb/input/hid-ff.c
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* $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 <johann.deneux@it.uu.se>
|
||||
*/
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
#undef DEBUG
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "hid.h"
|
||||
|
||||
/* Drivers' initializing functions */
|
||||
extern int hid_lgff_init(struct hid_device* hid);
|
||||
extern int hid_lg3d_init(struct hid_device* hid);
|
||||
extern int hid_pid_init(struct hid_device* hid);
|
||||
extern int hid_tmff_init(struct hid_device* hid);
|
||||
|
||||
/*
|
||||
* 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*);
|
||||
};
|
||||
|
||||
static struct hid_ff_initializer inits[] = {
|
||||
#ifdef CONFIG_LOGITECH_FF
|
||||
{0x46d, 0xc211, hid_lgff_init}, // Logitech Cordless rumble pad
|
||||
{0x46d, 0xc283, hid_lgff_init}, // Logitech Wingman Force 3d
|
||||
{0x46d, 0xc295, hid_lgff_init}, // Logitech MOMO force wheel
|
||||
{0x46d, 0xc219, hid_lgff_init}, // Logitech Cordless rumble pad 2
|
||||
#endif
|
||||
#ifdef CONFIG_HID_PID
|
||||
{0x45e, 0x001b, hid_pid_init},
|
||||
#endif
|
||||
#ifdef CONFIG_THRUSTMASTER_FF
|
||||
{0x44f, 0xb304, hid_tmff_init},
|
||||
#endif
|
||||
{0, 0, NULL} /* Terminating entry */
|
||||
};
|
||||
|
||||
static struct hid_ff_initializer *hid_get_ff_init(__u16 idVendor,
|
||||
__u16 idProduct)
|
||||
{
|
||||
struct hid_ff_initializer *init;
|
||||
for (init = inits;
|
||||
init->idVendor
|
||||
&& !(init->idVendor == idVendor
|
||||
&& init->idProduct == idProduct);
|
||||
init++);
|
||||
|
||||
return init->idVendor? init : NULL;
|
||||
}
|
||||
|
||||
int hid_ff_init(struct hid_device* hid)
|
||||
{
|
||||
struct hid_ff_initializer *init;
|
||||
|
||||
init = hid_get_ff_init(le16_to_cpu(hid->dev->descriptor.idVendor),
|
||||
le16_to_cpu(hid->dev->descriptor.idProduct));
|
||||
|
||||
if (!init) {
|
||||
dbg("hid_ff_init could not find initializer");
|
||||
return -ENOSYS;
|
||||
}
|
||||
return init->init(hid);
|
||||
}
|
630
drivers/usb/input/hid-input.c
Normal file
630
drivers/usb/input/hid-input.c
Normal file
@@ -0,0 +1,630 @@
|
||||
/*
|
||||
* $Id: hid-input.c,v 1.2 2002/04/23 00:59:25 rdamazio Exp $
|
||||
*
|
||||
* Copyright (c) 2000-2001 Vojtech Pavlik
|
||||
*
|
||||
* USB HID to Linux Input mapping
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <vojtech@ucw.cz>, or by paper mail:
|
||||
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#include "hid.h"
|
||||
|
||||
#define unk KEY_UNKNOWN
|
||||
|
||||
static unsigned char hid_keyboard[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,unk,unk,unk,121,unk, 89, 93,124, 92, 94, 95,unk,unk,unk,
|
||||
122,123, 90, 91, 85,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
|
||||
unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
|
||||
unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
|
||||
unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
|
||||
unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
|
||||
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,unk,unk,unk,unk
|
||||
};
|
||||
|
||||
static struct {
|
||||
__s32 x;
|
||||
__s32 y;
|
||||
} hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
|
||||
|
||||
#define map_abs(c) do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0)
|
||||
#define map_rel(c) do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
|
||||
#define map_key(c) do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
|
||||
#define map_led(c) do { usage->code = c; usage->type = EV_LED; bit = input->ledbit; max = LED_MAX; } while (0)
|
||||
#define map_ff(c) do { usage->code = c; usage->type = EV_FF; bit = input->ffbit; max = FF_MAX; } while (0)
|
||||
|
||||
#define map_abs_clear(c) do { map_abs(c); clear_bit(c, bit); } while (0)
|
||||
#define map_key_clear(c) do { map_key(c); clear_bit(c, bit); } while (0)
|
||||
#define map_ff_effect(c) do { set_bit(c, input->ffbit); } while (0)
|
||||
|
||||
static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
|
||||
struct hid_usage *usage)
|
||||
{
|
||||
struct input_dev *input = &hidinput->input;
|
||||
struct hid_device *device = hidinput->input.private;
|
||||
int max, code;
|
||||
unsigned long *bit;
|
||||
|
||||
field->hidinput = hidinput;
|
||||
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG "Mapping: ");
|
||||
resolv_usage(usage->hid);
|
||||
printk(" ---> ");
|
||||
#endif
|
||||
|
||||
if (field->flags & HID_MAIN_ITEM_CONSTANT)
|
||||
goto ignore;
|
||||
|
||||
switch (usage->hid & HID_USAGE_PAGE) {
|
||||
|
||||
case HID_UP_UNDEFINED:
|
||||
goto ignore;
|
||||
|
||||
case HID_UP_KEYBOARD:
|
||||
|
||||
set_bit(EV_REP, input->evbit);
|
||||
|
||||
if ((usage->hid & HID_USAGE) < 256) {
|
||||
if (!hid_keyboard[usage->hid & HID_USAGE]) goto ignore;
|
||||
map_key_clear(hid_keyboard[usage->hid & HID_USAGE]);
|
||||
} else
|
||||
map_key(KEY_UNKNOWN);
|
||||
|
||||
break;
|
||||
|
||||
case HID_UP_BUTTON:
|
||||
|
||||
code = ((usage->hid - 1) & 0xf);
|
||||
|
||||
switch (field->application) {
|
||||
case HID_GD_MOUSE:
|
||||
case HID_GD_POINTER: code += 0x110; break;
|
||||
case HID_GD_JOYSTICK: code += 0x120; break;
|
||||
case HID_GD_GAMEPAD: code += 0x130; break;
|
||||
default:
|
||||
switch (field->physical) {
|
||||
case HID_GD_MOUSE:
|
||||
case HID_GD_POINTER: code += 0x110; break;
|
||||
case HID_GD_JOYSTICK: code += 0x120; break;
|
||||
case HID_GD_GAMEPAD: code += 0x130; break;
|
||||
default: code += 0x100;
|
||||
}
|
||||
}
|
||||
|
||||
map_key(code);
|
||||
break;
|
||||
|
||||
case HID_UP_GENDESK:
|
||||
|
||||
if ((usage->hid & 0xf0) == 0x80) { /* SystemControl */
|
||||
switch (usage->hid & 0xf) {
|
||||
case 0x1: map_key_clear(KEY_POWER); break;
|
||||
case 0x2: map_key_clear(KEY_SLEEP); break;
|
||||
case 0x3: map_key_clear(KEY_WAKEUP); break;
|
||||
default: goto unknown;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ((usage->hid & 0xf0) == 0x90) { /* D-pad */
|
||||
switch (usage->hid) {
|
||||
case HID_GD_UP: usage->hat_dir = 1; break;
|
||||
case HID_GD_DOWN: usage->hat_dir = 5; break;
|
||||
case HID_GD_RIGHT: usage->hat_dir = 3; break;
|
||||
case HID_GD_LEFT: usage->hat_dir = 7; break;
|
||||
default: goto unknown;
|
||||
}
|
||||
if (field->dpad) {
|
||||
map_abs(field->dpad);
|
||||
goto ignore;
|
||||
}
|
||||
map_abs(ABS_HAT0X);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (usage->hid) {
|
||||
|
||||
/* These usage IDs map directly to the usage codes. */
|
||||
case HID_GD_X: case HID_GD_Y: case HID_GD_Z:
|
||||
case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ:
|
||||
case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL:
|
||||
if (field->flags & HID_MAIN_ITEM_RELATIVE)
|
||||
map_rel(usage->hid & 0xf);
|
||||
else
|
||||
map_abs(usage->hid & 0xf);
|
||||
break;
|
||||
|
||||
case HID_GD_HATSWITCH:
|
||||
usage->hat_min = field->logical_minimum;
|
||||
usage->hat_max = field->logical_maximum;
|
||||
map_abs(ABS_HAT0X);
|
||||
break;
|
||||
|
||||
case HID_GD_START: map_key_clear(BTN_START); break;
|
||||
case HID_GD_SELECT: map_key_clear(BTN_SELECT); break;
|
||||
|
||||
default: goto unknown;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case HID_UP_LED:
|
||||
if (((usage->hid - 1) & 0xffff) >= LED_MAX)
|
||||
goto ignore;
|
||||
map_led((usage->hid - 1) & 0xffff);
|
||||
break;
|
||||
|
||||
case HID_UP_DIGITIZER:
|
||||
|
||||
switch (usage->hid & 0xff) {
|
||||
|
||||
case 0x30: /* TipPressure */
|
||||
if (!test_bit(BTN_TOUCH, input->keybit)) {
|
||||
device->quirks |= HID_QUIRK_NOTOUCH;
|
||||
set_bit(EV_KEY, input->evbit);
|
||||
set_bit(BTN_TOUCH, input->keybit);
|
||||
}
|
||||
|
||||
map_abs_clear(ABS_PRESSURE);
|
||||
break;
|
||||
|
||||
case 0x32: /* InRange */
|
||||
switch (field->physical & 0xff) {
|
||||
case 0x21: map_key(BTN_TOOL_MOUSE); break;
|
||||
case 0x22: map_key(BTN_TOOL_FINGER); break;
|
||||
default: map_key(BTN_TOOL_PEN); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x3c: /* Invert */
|
||||
map_key_clear(BTN_TOOL_RUBBER);
|
||||
break;
|
||||
|
||||
case 0x33: /* Touch */
|
||||
case 0x42: /* TipSwitch */
|
||||
case 0x43: /* TipSwitch2 */
|
||||
device->quirks &= ~HID_QUIRK_NOTOUCH;
|
||||
map_key_clear(BTN_TOUCH);
|
||||
break;
|
||||
|
||||
case 0x44: /* BarrelSwitch */
|
||||
map_key_clear(BTN_STYLUS);
|
||||
break;
|
||||
|
||||
default: goto unknown;
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_UP_CONSUMER: /* USB HUT v1.1, pages 56-62 */
|
||||
|
||||
switch (usage->hid & HID_USAGE) {
|
||||
case 0x000: goto ignore;
|
||||
case 0x034: map_key_clear(KEY_SLEEP); break;
|
||||
case 0x036: map_key_clear(BTN_MISC); break;
|
||||
case 0x08a: map_key_clear(KEY_WWW); break;
|
||||
case 0x095: map_key_clear(KEY_HELP); break;
|
||||
case 0x0b0: map_key_clear(KEY_PLAY); break;
|
||||
case 0x0b1: map_key_clear(KEY_PAUSE); break;
|
||||
case 0x0b2: map_key_clear(KEY_RECORD); break;
|
||||
case 0x0b3: map_key_clear(KEY_FASTFORWARD); break;
|
||||
case 0x0b4: map_key_clear(KEY_REWIND); break;
|
||||
case 0x0b5: map_key_clear(KEY_NEXTSONG); break;
|
||||
case 0x0b6: map_key_clear(KEY_PREVIOUSSONG); break;
|
||||
case 0x0b7: map_key_clear(KEY_STOPCD); break;
|
||||
case 0x0b8: map_key_clear(KEY_EJECTCD); break;
|
||||
case 0x0cd: map_key_clear(KEY_PLAYPAUSE); break;
|
||||
case 0x0e0: map_abs_clear(ABS_VOLUME); break;
|
||||
case 0x0e2: map_key_clear(KEY_MUTE); break;
|
||||
case 0x0e5: map_key_clear(KEY_BASSBOOST); break;
|
||||
case 0x0e9: map_key_clear(KEY_VOLUMEUP); break;
|
||||
case 0x0ea: map_key_clear(KEY_VOLUMEDOWN); break;
|
||||
case 0x183: map_key_clear(KEY_CONFIG); break;
|
||||
case 0x18a: map_key_clear(KEY_MAIL); break;
|
||||
case 0x192: map_key_clear(KEY_CALC); break;
|
||||
case 0x194: map_key_clear(KEY_FILE); break;
|
||||
case 0x21a: map_key_clear(KEY_UNDO); break;
|
||||
case 0x21b: map_key_clear(KEY_COPY); break;
|
||||
case 0x21c: map_key_clear(KEY_CUT); break;
|
||||
case 0x21d: map_key_clear(KEY_PASTE); break;
|
||||
case 0x221: map_key_clear(KEY_FIND); break;
|
||||
case 0x223: map_key_clear(KEY_HOMEPAGE); break;
|
||||
case 0x224: map_key_clear(KEY_BACK); break;
|
||||
case 0x225: map_key_clear(KEY_FORWARD); break;
|
||||
case 0x226: map_key_clear(KEY_STOP); break;
|
||||
case 0x227: map_key_clear(KEY_REFRESH); break;
|
||||
case 0x22a: map_key_clear(KEY_BOOKMARKS); break;
|
||||
case 0x238: map_rel(REL_HWHEEL); break;
|
||||
default: goto unknown;
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */
|
||||
|
||||
set_bit(EV_REP, input->evbit);
|
||||
switch (usage->hid & HID_USAGE) {
|
||||
case 0x021: map_key_clear(KEY_PRINT); break;
|
||||
case 0x070: map_key_clear(KEY_HP); break;
|
||||
case 0x071: map_key_clear(KEY_CAMERA); break;
|
||||
case 0x072: map_key_clear(KEY_SOUND); break;
|
||||
case 0x073: map_key_clear(KEY_QUESTION); break;
|
||||
case 0x080: map_key_clear(KEY_EMAIL); break;
|
||||
case 0x081: map_key_clear(KEY_CHAT); break;
|
||||
case 0x082: map_key_clear(KEY_SEARCH); break;
|
||||
case 0x083: map_key_clear(KEY_CONNECT); break;
|
||||
case 0x084: map_key_clear(KEY_FINANCE); break;
|
||||
case 0x085: map_key_clear(KEY_SPORT); break;
|
||||
case 0x086: map_key_clear(KEY_SHOP); break;
|
||||
default: goto ignore;
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_UP_MSVENDOR:
|
||||
|
||||
goto ignore;
|
||||
|
||||
case HID_UP_PID:
|
||||
|
||||
set_bit(EV_FF, input->evbit);
|
||||
switch(usage->hid & HID_USAGE) {
|
||||
case 0x26: map_ff_effect(FF_CONSTANT); goto ignore;
|
||||
case 0x27: map_ff_effect(FF_RAMP); goto ignore;
|
||||
case 0x28: map_ff_effect(FF_CUSTOM); goto ignore;
|
||||
case 0x30: map_ff_effect(FF_SQUARE); map_ff_effect(FF_PERIODIC); goto ignore;
|
||||
case 0x31: map_ff_effect(FF_SINE); map_ff_effect(FF_PERIODIC); goto ignore;
|
||||
case 0x32: map_ff_effect(FF_TRIANGLE); map_ff_effect(FF_PERIODIC); goto ignore;
|
||||
case 0x33: map_ff_effect(FF_SAW_UP); map_ff_effect(FF_PERIODIC); goto ignore;
|
||||
case 0x34: map_ff_effect(FF_SAW_DOWN); map_ff_effect(FF_PERIODIC); goto ignore;
|
||||
case 0x40: map_ff_effect(FF_SPRING); goto ignore;
|
||||
case 0x41: map_ff_effect(FF_DAMPER); goto ignore;
|
||||
case 0x42: map_ff_effect(FF_INERTIA); goto ignore;
|
||||
case 0x43: map_ff_effect(FF_FRICTION); goto ignore;
|
||||
case 0x7e: map_ff(FF_GAIN); break;
|
||||
case 0x83: input->ff_effects_max = field->value[0]; goto ignore;
|
||||
case 0x98: map_ff(FF_AUTOCENTER); break;
|
||||
case 0xa4: map_key_clear(BTN_DEAD); break;
|
||||
default: goto ignore;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
unknown:
|
||||
if (field->report_size == 1) {
|
||||
if (field->report->type == HID_OUTPUT_REPORT) {
|
||||
map_led(LED_MISC);
|
||||
break;
|
||||
}
|
||||
map_key(BTN_MISC);
|
||||
break;
|
||||
}
|
||||
if (field->flags & HID_MAIN_ITEM_RELATIVE) {
|
||||
map_rel(REL_MISC);
|
||||
break;
|
||||
}
|
||||
map_abs(ABS_MISC);
|
||||
break;
|
||||
}
|
||||
|
||||
set_bit(usage->type, input->evbit);
|
||||
|
||||
while (usage->code <= max && test_and_set_bit(usage->code, bit))
|
||||
usage->code = find_next_zero_bit(bit, max + 1, usage->code);
|
||||
|
||||
if (usage->code > max)
|
||||
goto ignore;
|
||||
|
||||
if ((device->quirks & (HID_QUIRK_2WHEEL_MOUSE_HACK_7 | HID_QUIRK_2WHEEL_MOUSE_HACK_5)) &&
|
||||
(usage->type == EV_REL) && (usage->code == REL_WHEEL))
|
||||
set_bit(REL_HWHEEL, bit);
|
||||
|
||||
if (((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005))
|
||||
|| ((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007)))
|
||||
goto ignore;
|
||||
|
||||
if (usage->type == EV_ABS) {
|
||||
|
||||
int a = field->logical_minimum;
|
||||
int b = field->logical_maximum;
|
||||
|
||||
if ((device->quirks & HID_QUIRK_BADPAD) && (usage->code == ABS_X || usage->code == ABS_Y)) {
|
||||
a = field->logical_minimum = 0;
|
||||
b = field->logical_maximum = 255;
|
||||
}
|
||||
|
||||
if (field->application == HID_GD_GAMEPAD || field->application == HID_GD_JOYSTICK)
|
||||
input_set_abs_params(input, usage->code, a, b, (b - a) >> 8, (b - a) >> 4);
|
||||
else input_set_abs_params(input, usage->code, a, b, 0, 0);
|
||||
|
||||
}
|
||||
|
||||
if (usage->hat_min < usage->hat_max || usage->hat_dir) {
|
||||
int i;
|
||||
for (i = usage->code; i < usage->code + 2 && i <= max; i++) {
|
||||
input_set_abs_params(input, i, -1, 1, 0, 0);
|
||||
set_bit(i, input->absbit);
|
||||
}
|
||||
if (usage->hat_dir && !field->dpad)
|
||||
field->dpad = usage->code;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
resolv_event(usage->type, usage->code);
|
||||
printk("\n");
|
||||
#endif
|
||||
return;
|
||||
|
||||
ignore:
|
||||
#ifdef DEBUG
|
||||
printk("IGNORED\n");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, struct pt_regs *regs)
|
||||
{
|
||||
struct input_dev *input = &field->hidinput->input;
|
||||
int *quirks = &hid->quirks;
|
||||
|
||||
if (!input)
|
||||
return;
|
||||
|
||||
input_regs(input, regs);
|
||||
|
||||
if (!usage->type)
|
||||
return;
|
||||
|
||||
if (((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005))
|
||||
|| ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007))) {
|
||||
if (value) hid->quirks |= HID_QUIRK_2WHEEL_MOUSE_HACK_ON;
|
||||
else hid->quirks &= ~HID_QUIRK_2WHEEL_MOUSE_HACK_ON;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_ON) && (usage->code == REL_WHEEL)) {
|
||||
input_event(input, usage->type, REL_HWHEEL, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (usage->hat_min < usage->hat_max || usage->hat_dir) {
|
||||
int hat_dir = usage->hat_dir;
|
||||
if (!hat_dir)
|
||||
hat_dir = (value - usage->hat_min) * 8 / (usage->hat_max - usage->hat_min + 1) + 1;
|
||||
if (hat_dir < 0 || hat_dir > 8) hat_dir = 0;
|
||||
input_event(input, usage->type, usage->code , hid_hat_to_axis[hat_dir].x);
|
||||
input_event(input, usage->type, usage->code + 1, hid_hat_to_axis[hat_dir].y);
|
||||
return;
|
||||
}
|
||||
|
||||
if (usage->hid == (HID_UP_DIGITIZER | 0x003c)) { /* Invert */
|
||||
*quirks = value ? (*quirks | HID_QUIRK_INVERT) : (*quirks & ~HID_QUIRK_INVERT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (usage->hid == (HID_UP_DIGITIZER | 0x0032)) { /* InRange */
|
||||
if (value) {
|
||||
input_event(input, usage->type, (*quirks & HID_QUIRK_INVERT) ? BTN_TOOL_RUBBER : usage->code, 1);
|
||||
return;
|
||||
}
|
||||
input_event(input, usage->type, usage->code, 0);
|
||||
input_event(input, usage->type, BTN_TOOL_RUBBER, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (usage->hid == (HID_UP_DIGITIZER | 0x0030) && (*quirks & HID_QUIRK_NOTOUCH)) { /* Pressure */
|
||||
int a = field->logical_minimum;
|
||||
int b = field->logical_maximum;
|
||||
input_event(input, EV_KEY, BTN_TOUCH, value > a + ((b - a) >> 3));
|
||||
}
|
||||
|
||||
if (usage->hid == (HID_UP_PID | 0x83UL)) { /* Simultaneous Effects Max */
|
||||
input->ff_effects_max = value;
|
||||
dbg("Maximum Effects - %d",input->ff_effects_max);
|
||||
return;
|
||||
}
|
||||
|
||||
if (usage->hid == (HID_UP_PID | 0x7fUL)) {
|
||||
dbg("PID Pool Report\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */
|
||||
return;
|
||||
|
||||
input_event(input, usage->type, usage->code, value);
|
||||
|
||||
if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY))
|
||||
input_event(input, usage->type, usage->code, 0);
|
||||
}
|
||||
|
||||
void hidinput_report_event(struct hid_device *hid, struct hid_report *report)
|
||||
{
|
||||
struct list_head *lh;
|
||||
struct hid_input *hidinput;
|
||||
|
||||
list_for_each (lh, &hid->inputs) {
|
||||
hidinput = list_entry(lh, struct hid_input, list);
|
||||
input_sync(&hidinput->input);
|
||||
}
|
||||
}
|
||||
|
||||
static int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field)
|
||||
{
|
||||
struct hid_report *report;
|
||||
int i, j;
|
||||
|
||||
list_for_each_entry(report, &hid->report_enum[HID_OUTPUT_REPORT].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].type == type && (*field)->usage[j].code == code)
|
||||
return j;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int 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 hid_ff_event(hid, 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);
|
||||
hid_submit_report(hid, field->report, USB_DIR_OUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hidinput_open(struct input_dev *dev)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
return hid_open(hid);
|
||||
}
|
||||
|
||||
static void hidinput_close(struct input_dev *dev)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
hid_close(hid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Register the input device; print a message.
|
||||
* Configure the input layer interface
|
||||
* Read all reports and initialize the absolute field values.
|
||||
*/
|
||||
|
||||
int hidinput_connect(struct hid_device *hid)
|
||||
{
|
||||
struct usb_device *dev = hid->dev;
|
||||
struct hid_report *report;
|
||||
struct hid_input *hidinput = NULL;
|
||||
int i, j, k;
|
||||
|
||||
INIT_LIST_HEAD(&hid->inputs);
|
||||
|
||||
for (i = 0; i < hid->maxcollection; i++)
|
||||
if (hid->collection[i].type == HID_COLLECTION_APPLICATION ||
|
||||
hid->collection[i].type == HID_COLLECTION_PHYSICAL)
|
||||
if (IS_INPUT_APPLICATION(hid->collection[i].usage))
|
||||
break;
|
||||
|
||||
if (i == hid->maxcollection)
|
||||
return -1;
|
||||
|
||||
for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++)
|
||||
list_for_each_entry(report, &hid->report_enum[k].report_list, list) {
|
||||
|
||||
if (!report->maxfield)
|
||||
continue;
|
||||
|
||||
if (!hidinput) {
|
||||
hidinput = kmalloc(sizeof(*hidinput), GFP_KERNEL);
|
||||
if (!hidinput) {
|
||||
err("Out of memory during hid input probe");
|
||||
return -1;
|
||||
}
|
||||
memset(hidinput, 0, sizeof(*hidinput));
|
||||
|
||||
list_add_tail(&hidinput->list, &hid->inputs);
|
||||
|
||||
hidinput->input.private = hid;
|
||||
hidinput->input.event = hidinput_input_event;
|
||||
hidinput->input.open = hidinput_open;
|
||||
hidinput->input.close = hidinput_close;
|
||||
|
||||
hidinput->input.name = hid->name;
|
||||
hidinput->input.phys = hid->phys;
|
||||
hidinput->input.uniq = hid->uniq;
|
||||
hidinput->input.id.bustype = BUS_USB;
|
||||
hidinput->input.id.vendor = le16_to_cpu(dev->descriptor.idVendor);
|
||||
hidinput->input.id.product = le16_to_cpu(dev->descriptor.idProduct);
|
||||
hidinput->input.id.version = le16_to_cpu(dev->descriptor.bcdDevice);
|
||||
hidinput->input.dev = &hid->intf->dev;
|
||||
}
|
||||
|
||||
for (i = 0; i < report->maxfield; i++)
|
||||
for (j = 0; j < report->field[i]->maxusage; j++)
|
||||
hidinput_configure_usage(hidinput, report->field[i],
|
||||
report->field[i]->usage + j);
|
||||
|
||||
if (hid->quirks & HID_QUIRK_MULTI_INPUT) {
|
||||
/* This will leave hidinput NULL, so that it
|
||||
* allocates another one if we have more inputs on
|
||||
* the same interface. Some devices (e.g. Happ's
|
||||
* UGCI) cram a lot of unrelated inputs into the
|
||||
* same interface. */
|
||||
hidinput->report = report;
|
||||
input_register_device(&hidinput->input);
|
||||
hidinput = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* This only gets called when we are a single-input (most of the
|
||||
* time). IOW, not a HID_QUIRK_MULTI_INPUT. The hid_ff_init() is
|
||||
* only useful in this case, and not for multi-input quirks. */
|
||||
if (hidinput) {
|
||||
hid_ff_init(hid);
|
||||
input_register_device(&hidinput->input);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hidinput_disconnect(struct hid_device *hid)
|
||||
{
|
||||
struct list_head *lh, *next;
|
||||
struct hid_input *hidinput;
|
||||
|
||||
list_for_each_safe(lh, next, &hid->inputs) {
|
||||
hidinput = list_entry(lh, struct hid_input, list);
|
||||
input_unregister_device(&hidinput->input);
|
||||
list_del(&hidinput->list);
|
||||
kfree(hidinput);
|
||||
}
|
||||
}
|
528
drivers/usb/input/hid-lgff.c
Normal file
528
drivers/usb/input/hid-lgff.c
Normal file
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
* $$
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <johann.deneux@it.uu.se>
|
||||
*/
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
//#define DEBUG
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <linux/circ_buf.h>
|
||||
|
||||
#include "hid.h"
|
||||
#include "fixp-arith.h"
|
||||
|
||||
|
||||
/* Periodicity of the update */
|
||||
#define PERIOD (HZ/10)
|
||||
|
||||
#define RUN_AT(t) (jiffies + (t))
|
||||
|
||||
/* Effect status */
|
||||
#define EFFECT_STARTED 0 /* Effect is going to play after some time
|
||||
(ff_replay.delay) */
|
||||
#define EFFECT_PLAYING 1 /* Effect is being played */
|
||||
#define EFFECT_USED 2
|
||||
|
||||
// For lgff_device::flags
|
||||
#define DEVICE_CLOSING 0 /* The driver is being unitialised */
|
||||
|
||||
/* Check that the current process can access an effect */
|
||||
#define CHECK_OWNERSHIP(effect) (current->pid == 0 \
|
||||
|| effect.owner == current->pid)
|
||||
|
||||
#define LGFF_CHECK_OWNERSHIP(i, l) \
|
||||
(i>=0 && i<LGFF_EFFECTS \
|
||||
&& test_bit(EFFECT_USED, l->effects[i].flags) \
|
||||
&& CHECK_OWNERSHIP(l->effects[i]))
|
||||
|
||||
#define LGFF_EFFECTS 8
|
||||
|
||||
struct device_type {
|
||||
u16 idVendor;
|
||||
u16 idProduct;
|
||||
signed short *ff;
|
||||
};
|
||||
|
||||
struct lgff_effect {
|
||||
pid_t owner;
|
||||
|
||||
struct ff_effect effect;
|
||||
|
||||
unsigned long flags[1];
|
||||
unsigned int count; /* Number of times left to play */
|
||||
unsigned long started_at; /* When the effect started to play */
|
||||
};
|
||||
|
||||
struct lgff_device {
|
||||
struct hid_device* hid;
|
||||
|
||||
struct hid_report* constant;
|
||||
struct hid_report* rumble;
|
||||
struct hid_report* condition;
|
||||
|
||||
struct lgff_effect effects[LGFF_EFFECTS];
|
||||
spinlock_t lock; /* device-level lock. Having locks on
|
||||
a per-effect basis could be nice, but
|
||||
isn't really necessary */
|
||||
|
||||
unsigned long flags[1]; /* Contains various information about the
|
||||
state of the driver for this device */
|
||||
|
||||
struct timer_list timer;
|
||||
};
|
||||
|
||||
/* Callbacks */
|
||||
static void hid_lgff_exit(struct hid_device* hid);
|
||||
static int hid_lgff_event(struct hid_device *hid, struct input_dev *input,
|
||||
unsigned int type, unsigned int code, int value);
|
||||
static int hid_lgff_flush(struct input_dev *input, struct file *file);
|
||||
static int hid_lgff_upload_effect(struct input_dev *input,
|
||||
struct ff_effect *effect);
|
||||
static int hid_lgff_erase(struct input_dev *input, int id);
|
||||
|
||||
/* Local functions */
|
||||
static void hid_lgff_input_init(struct hid_device* hid);
|
||||
static void hid_lgff_timer(unsigned long timer_data);
|
||||
static struct hid_report* hid_lgff_duplicate_report(struct hid_report*);
|
||||
static void hid_lgff_delete_report(struct hid_report*);
|
||||
|
||||
static signed short ff_rumble[] = {
|
||||
FF_RUMBLE,
|
||||
-1
|
||||
};
|
||||
|
||||
static signed short ff_joystick[] = {
|
||||
FF_CONSTANT,
|
||||
-1
|
||||
};
|
||||
|
||||
static struct device_type devices[] = {
|
||||
{0x046d, 0xc211, ff_rumble},
|
||||
{0x046d, 0xc219, ff_rumble},
|
||||
{0x046d, 0xc283, ff_joystick},
|
||||
{0x0000, 0x0000, ff_joystick}
|
||||
};
|
||||
|
||||
int hid_lgff_init(struct hid_device* hid)
|
||||
{
|
||||
struct lgff_device *private;
|
||||
struct hid_report* report;
|
||||
struct hid_field* field;
|
||||
|
||||
/* Find the report to use */
|
||||
if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) {
|
||||
err("No output report found");
|
||||
return -1;
|
||||
}
|
||||
/* Check that the report looks ok */
|
||||
report = (struct hid_report*)hid->report_enum[HID_OUTPUT_REPORT].report_list.next;
|
||||
if (!report) {
|
||||
err("NULL output report");
|
||||
return -1;
|
||||
}
|
||||
field = report->field[0];
|
||||
if (!field) {
|
||||
err("NULL field");
|
||||
return -1;
|
||||
}
|
||||
|
||||
private = kmalloc(sizeof(struct lgff_device), GFP_KERNEL);
|
||||
if (!private)
|
||||
return -1;
|
||||
memset(private, 0, sizeof(struct lgff_device));
|
||||
hid->ff_private = private;
|
||||
|
||||
/* Input init */
|
||||
hid_lgff_input_init(hid);
|
||||
|
||||
|
||||
private->constant = hid_lgff_duplicate_report(report);
|
||||
if (!private->constant) {
|
||||
kfree(private);
|
||||
return -1;
|
||||
}
|
||||
private->constant->field[0]->value[0] = 0x51;
|
||||
private->constant->field[0]->value[1] = 0x08;
|
||||
private->constant->field[0]->value[2] = 0x7f;
|
||||
private->constant->field[0]->value[3] = 0x7f;
|
||||
|
||||
private->rumble = hid_lgff_duplicate_report(report);
|
||||
if (!private->rumble) {
|
||||
hid_lgff_delete_report(private->constant);
|
||||
kfree(private);
|
||||
return -1;
|
||||
}
|
||||
private->rumble->field[0]->value[0] = 0x42;
|
||||
|
||||
|
||||
private->condition = hid_lgff_duplicate_report(report);
|
||||
if (!private->condition) {
|
||||
hid_lgff_delete_report(private->rumble);
|
||||
hid_lgff_delete_report(private->constant);
|
||||
kfree(private);
|
||||
return -1;
|
||||
}
|
||||
|
||||
private->hid = hid;
|
||||
|
||||
spin_lock_init(&private->lock);
|
||||
init_timer(&private->timer);
|
||||
private->timer.data = (unsigned long)private;
|
||||
private->timer.function = hid_lgff_timer;
|
||||
|
||||
/* Event and exit callbacks */
|
||||
hid->ff_exit = hid_lgff_exit;
|
||||
hid->ff_event = hid_lgff_event;
|
||||
|
||||
/* Start the update task */
|
||||
private->timer.expires = RUN_AT(PERIOD);
|
||||
add_timer(&private->timer); /*TODO: only run the timer when at least
|
||||
one effect is playing */
|
||||
|
||||
printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hid_report* hid_lgff_duplicate_report(struct hid_report* report)
|
||||
{
|
||||
struct hid_report* ret;
|
||||
|
||||
ret = kmalloc(sizeof(struct lgff_device), GFP_KERNEL);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
*ret = *report;
|
||||
|
||||
ret->field[0] = kmalloc(sizeof(struct hid_field), GFP_KERNEL);
|
||||
if (!ret->field[0]) {
|
||||
kfree(ret);
|
||||
return NULL;
|
||||
}
|
||||
*ret->field[0] = *report->field[0];
|
||||
|
||||
ret->field[0]->value = kmalloc(sizeof(s32[8]), GFP_KERNEL);
|
||||
if (!ret->field[0]->value) {
|
||||
kfree(ret->field[0]);
|
||||
kfree(ret);
|
||||
return NULL;
|
||||
}
|
||||
memset(ret->field[0]->value, 0, sizeof(s32[8]));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hid_lgff_delete_report(struct hid_report* report)
|
||||
{
|
||||
if (report) {
|
||||
kfree(report->field[0]->value);
|
||||
kfree(report->field[0]);
|
||||
kfree(report);
|
||||
}
|
||||
}
|
||||
|
||||
static void hid_lgff_input_init(struct hid_device* hid)
|
||||
{
|
||||
struct device_type* dev = devices;
|
||||
signed short* ff;
|
||||
u16 idVendor = le16_to_cpu(hid->dev->descriptor.idVendor);
|
||||
u16 idProduct = le16_to_cpu(hid->dev->descriptor.idProduct);
|
||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||
|
||||
while (dev->idVendor && (idVendor != dev->idVendor || idProduct != dev->idProduct))
|
||||
dev++;
|
||||
|
||||
ff = dev->ff;
|
||||
|
||||
while (*ff >= 0) {
|
||||
set_bit(*ff, hidinput->input.ffbit);
|
||||
++ff;
|
||||
}
|
||||
|
||||
hidinput->input.upload_effect = hid_lgff_upload_effect;
|
||||
hidinput->input.flush = hid_lgff_flush;
|
||||
|
||||
set_bit(EV_FF, hidinput->input.evbit);
|
||||
hidinput->input.ff_effects_max = LGFF_EFFECTS;
|
||||
}
|
||||
|
||||
static void hid_lgff_exit(struct hid_device* hid)
|
||||
{
|
||||
struct lgff_device *lgff = hid->ff_private;
|
||||
|
||||
set_bit(DEVICE_CLOSING, lgff->flags);
|
||||
del_timer_sync(&lgff->timer);
|
||||
|
||||
hid_lgff_delete_report(lgff->condition);
|
||||
hid_lgff_delete_report(lgff->rumble);
|
||||
hid_lgff_delete_report(lgff->constant);
|
||||
|
||||
kfree(lgff);
|
||||
}
|
||||
|
||||
static int hid_lgff_event(struct hid_device *hid, struct input_dev* input,
|
||||
unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
struct lgff_device *lgff = hid->ff_private;
|
||||
struct lgff_effect *effect = lgff->effects + code;
|
||||
unsigned long flags;
|
||||
|
||||
if (type != EV_FF) return -EINVAL;
|
||||
if (!LGFF_CHECK_OWNERSHIP(code, lgff)) return -EACCES;
|
||||
if (value < 0) return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&lgff->lock, flags);
|
||||
|
||||
if (value > 0) {
|
||||
if (test_bit(EFFECT_STARTED, effect->flags)) {
|
||||
spin_unlock_irqrestore(&lgff->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
if (test_bit(EFFECT_PLAYING, effect->flags)) {
|
||||
spin_unlock_irqrestore(&lgff->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
effect->count = value;
|
||||
|
||||
if (effect->effect.replay.delay) {
|
||||
set_bit(EFFECT_STARTED, effect->flags);
|
||||
} else {
|
||||
set_bit(EFFECT_PLAYING, effect->flags);
|
||||
}
|
||||
effect->started_at = jiffies;
|
||||
}
|
||||
else { /* value == 0 */
|
||||
clear_bit(EFFECT_STARTED, effect->flags);
|
||||
clear_bit(EFFECT_PLAYING, effect->flags);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&lgff->lock, flags);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* Erase all effects this process owns */
|
||||
static int hid_lgff_flush(struct input_dev *dev, struct file *file)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct lgff_device *lgff = hid->ff_private;
|
||||
int i;
|
||||
|
||||
for (i=0; i<dev->ff_effects_max; ++i) {
|
||||
|
||||
/*NOTE: no need to lock here. The only times EFFECT_USED is
|
||||
modified is when effects are uploaded or when an effect is
|
||||
erased. But a process cannot close its dev/input/eventX fd
|
||||
and perform ioctls on the same fd all at the same time */
|
||||
if ( current->pid == lgff->effects[i].owner
|
||||
&& test_bit(EFFECT_USED, lgff->effects[i].flags)) {
|
||||
|
||||
if (hid_lgff_erase(dev, i))
|
||||
warn("erase effect %d failed", i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_lgff_erase(struct input_dev *dev, int id)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct lgff_device *lgff = hid->ff_private;
|
||||
unsigned long flags;
|
||||
|
||||
if (!LGFF_CHECK_OWNERSHIP(id, lgff)) return -EACCES;
|
||||
|
||||
spin_lock_irqsave(&lgff->lock, flags);
|
||||
lgff->effects[id].flags[0] = 0;
|
||||
spin_unlock_irqrestore(&lgff->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_lgff_upload_effect(struct input_dev* input,
|
||||
struct ff_effect* effect)
|
||||
{
|
||||
struct hid_device *hid = input->private;
|
||||
struct lgff_device *lgff = hid->ff_private;
|
||||
struct lgff_effect new;
|
||||
int id;
|
||||
unsigned long flags;
|
||||
|
||||
dbg("ioctl rumble");
|
||||
|
||||
if (!test_bit(effect->type, input->ffbit)) return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&lgff->lock, flags);
|
||||
|
||||
if (effect->id == -1) {
|
||||
int i;
|
||||
|
||||
for (i=0; i<LGFF_EFFECTS && test_bit(EFFECT_USED, lgff->effects[i].flags); ++i);
|
||||
if (i >= LGFF_EFFECTS) {
|
||||
spin_unlock_irqrestore(&lgff->lock, flags);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
effect->id = i;
|
||||
lgff->effects[i].owner = current->pid;
|
||||
lgff->effects[i].flags[0] = 0;
|
||||
set_bit(EFFECT_USED, lgff->effects[i].flags);
|
||||
}
|
||||
else if (!LGFF_CHECK_OWNERSHIP(effect->id, lgff)) {
|
||||
spin_unlock_irqrestore(&lgff->lock, flags);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
id = effect->id;
|
||||
new = lgff->effects[id];
|
||||
|
||||
new.effect = *effect;
|
||||
|
||||
if (test_bit(EFFECT_STARTED, lgff->effects[id].flags)
|
||||
|| test_bit(EFFECT_STARTED, lgff->effects[id].flags)) {
|
||||
|
||||
/* Changing replay parameters is not allowed (for the time
|
||||
being) */
|
||||
if (new.effect.replay.delay != lgff->effects[id].effect.replay.delay
|
||||
|| new.effect.replay.length != lgff->effects[id].effect.replay.length) {
|
||||
spin_unlock_irqrestore(&lgff->lock, flags);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
lgff->effects[id] = new;
|
||||
|
||||
} else {
|
||||
lgff->effects[id] = new;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&lgff->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hid_lgff_timer(unsigned long timer_data)
|
||||
{
|
||||
struct lgff_device *lgff = (struct lgff_device*)timer_data;
|
||||
struct hid_device *hid = lgff->hid;
|
||||
unsigned long flags;
|
||||
int x = 0x7f, y = 0x7f; // Coordinates of constant effects
|
||||
unsigned int left = 0, right = 0; // Rumbling
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&lgff->lock, flags);
|
||||
|
||||
for (i=0; i<LGFF_EFFECTS; ++i) {
|
||||
struct lgff_effect* effect = lgff->effects +i;
|
||||
|
||||
if (test_bit(EFFECT_PLAYING, effect->flags)) {
|
||||
|
||||
switch (effect->effect.type) {
|
||||
case FF_CONSTANT: {
|
||||
//TODO: handle envelopes
|
||||
int degrees = effect->effect.direction * 360 >> 16;
|
||||
x += fixp_mult(fixp_sin(degrees),
|
||||
fixp_new16(effect->effect.u.constant.level));
|
||||
y += fixp_mult(-fixp_cos(degrees),
|
||||
fixp_new16(effect->effect.u.constant.level));
|
||||
} break;
|
||||
case FF_RUMBLE:
|
||||
right += effect->effect.u.rumble.strong_magnitude;
|
||||
left += effect->effect.u.rumble.weak_magnitude;
|
||||
break;
|
||||
};
|
||||
|
||||
/* One run of the effect is finished playing */
|
||||
if (time_after(jiffies,
|
||||
effect->started_at
|
||||
+ effect->effect.replay.delay*HZ/1000
|
||||
+ effect->effect.replay.length*HZ/1000)) {
|
||||
dbg("Finished playing once %d", i);
|
||||
if (--effect->count <= 0) {
|
||||
dbg("Stopped %d", i);
|
||||
clear_bit(EFFECT_PLAYING, effect->flags);
|
||||
}
|
||||
else {
|
||||
dbg("Start again %d", i);
|
||||
if (effect->effect.replay.length != 0) {
|
||||
clear_bit(EFFECT_PLAYING, effect->flags);
|
||||
set_bit(EFFECT_STARTED, effect->flags);
|
||||
}
|
||||
effect->started_at = jiffies;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (test_bit(EFFECT_STARTED, lgff->effects[i].flags)) {
|
||||
/* Check if we should start playing the effect */
|
||||
if (time_after(jiffies,
|
||||
lgff->effects[i].started_at
|
||||
+ lgff->effects[i].effect.replay.delay*HZ/1000)) {
|
||||
dbg("Now playing %d", i);
|
||||
clear_bit(EFFECT_STARTED, lgff->effects[i].flags);
|
||||
set_bit(EFFECT_PLAYING, lgff->effects[i].flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
|
||||
|
||||
// Clamp values
|
||||
CLAMP(x);
|
||||
CLAMP(y);
|
||||
CLAMP(left);
|
||||
CLAMP(right);
|
||||
|
||||
#undef CLAMP
|
||||
|
||||
if (x != lgff->constant->field[0]->value[2]
|
||||
|| y != lgff->constant->field[0]->value[3]) {
|
||||
lgff->constant->field[0]->value[2] = x;
|
||||
lgff->constant->field[0]->value[3] = y;
|
||||
dbg("(x,y)=(%04x, %04x)", x, y);
|
||||
hid_submit_report(hid, lgff->constant, USB_DIR_OUT);
|
||||
}
|
||||
|
||||
if (left != lgff->rumble->field[0]->value[2]
|
||||
|| right != lgff->rumble->field[0]->value[3]) {
|
||||
lgff->rumble->field[0]->value[2] = left;
|
||||
lgff->rumble->field[0]->value[3] = right;
|
||||
dbg("(left,right)=(%04x, %04x)", left, right);
|
||||
hid_submit_report(hid, lgff->rumble, USB_DIR_OUT);
|
||||
}
|
||||
|
||||
if (!test_bit(DEVICE_CLOSING, lgff->flags)) {
|
||||
lgff->timer.expires = RUN_AT(PERIOD);
|
||||
add_timer(&lgff->timer);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&lgff->lock, flags);
|
||||
}
|
463
drivers/usb/input/hid-tmff.c
Normal file
463
drivers/usb/input/hid-tmff.c
Normal file
@@ -0,0 +1,463 @@
|
||||
/*
|
||||
* 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 <zinx@epicsol.org>
|
||||
* 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 <linux/input.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#undef DEBUG
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <linux/circ_buf.h>
|
||||
|
||||
#include "hid.h"
|
||||
#include "fixp-arith.h"
|
||||
|
||||
/* Usages for thrustmaster devices I know about */
|
||||
#define THRUSTMASTER_USAGE_RUMBLE_LR (HID_UP_GENDESK | 0xbb)
|
||||
#define DELAY_CALC(t,delay) ((t) + (delay)*HZ/1000)
|
||||
|
||||
/* Effect status */
|
||||
#define EFFECT_STARTED 0 /* Effect is going to play after some time */
|
||||
#define EFFECT_PLAYING 1 /* Effect is playing */
|
||||
#define EFFECT_USED 2
|
||||
|
||||
/* For tmff_device::flags */
|
||||
#define DEVICE_CLOSING 0 /* The driver is being unitialised */
|
||||
|
||||
/* Check that the current process can access an effect */
|
||||
#define CHECK_OWNERSHIP(effect) (current->pid == 0 \
|
||||
|| effect.owner == current->pid)
|
||||
|
||||
#define TMFF_CHECK_ID(id) ((id) >= 0 && (id) < TMFF_EFFECTS)
|
||||
|
||||
#define TMFF_CHECK_OWNERSHIP(i, l) \
|
||||
(test_bit(EFFECT_USED, l->effects[i].flags) \
|
||||
&& CHECK_OWNERSHIP(l->effects[i]))
|
||||
|
||||
#define TMFF_EFFECTS 8
|
||||
|
||||
struct tmff_effect {
|
||||
pid_t owner;
|
||||
|
||||
struct ff_effect effect;
|
||||
|
||||
unsigned long flags[1];
|
||||
unsigned int count; /* Number of times left to play */
|
||||
|
||||
unsigned long play_at; /* When the effect starts to play */
|
||||
unsigned long stop_at; /* When the effect ends */
|
||||
};
|
||||
|
||||
struct tmff_device {
|
||||
struct hid_device *hid;
|
||||
|
||||
struct hid_report *report;
|
||||
|
||||
struct hid_field *rumble;
|
||||
|
||||
unsigned int effects_playing;
|
||||
struct tmff_effect effects[TMFF_EFFECTS];
|
||||
spinlock_t lock; /* device-level lock. Having locks on
|
||||
a per-effect basis could be nice, but
|
||||
isn't really necessary */
|
||||
|
||||
unsigned long flags[1]; /* Contains various information about the
|
||||
state of the driver for this device */
|
||||
|
||||
struct timer_list timer;
|
||||
};
|
||||
|
||||
/* Callbacks */
|
||||
static void hid_tmff_exit(struct hid_device *hid);
|
||||
static int hid_tmff_event(struct hid_device *hid, struct input_dev *input,
|
||||
unsigned int type, unsigned int code, int value);
|
||||
static int hid_tmff_flush(struct input_dev *input, struct file *file);
|
||||
static int hid_tmff_upload_effect(struct input_dev *input,
|
||||
struct ff_effect *effect);
|
||||
static int hid_tmff_erase(struct input_dev *input, int id);
|
||||
|
||||
/* Local functions */
|
||||
static void hid_tmff_recalculate_timer(struct tmff_device *tmff);
|
||||
static void hid_tmff_timer(unsigned long timer_data);
|
||||
|
||||
int hid_tmff_init(struct hid_device *hid)
|
||||
{
|
||||
struct tmff_device *private;
|
||||
struct list_head *pos;
|
||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||
|
||||
private = kmalloc(sizeof(struct tmff_device), GFP_KERNEL);
|
||||
if (!private)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(private, 0, sizeof(struct tmff_device));
|
||||
hid->ff_private = private;
|
||||
|
||||
/* 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 (private->report && private->report != report) {
|
||||
warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR in other report");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (private->rumble && private->rumble != field) {
|
||||
warn("ignoring duplicate THRUSTMASTER_USAGE_RUMBLE_LR");
|
||||
continue;
|
||||
}
|
||||
|
||||
private->report = report;
|
||||
private->rumble = field;
|
||||
|
||||
set_bit(FF_RUMBLE, hidinput->input.ffbit);
|
||||
break;
|
||||
|
||||
default:
|
||||
warn("ignoring unknown output usage %08x", field->usage[0].hid);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Fallthrough to here only when a valid usage is found */
|
||||
hidinput->input.upload_effect = hid_tmff_upload_effect;
|
||||
hidinput->input.flush = hid_tmff_flush;
|
||||
|
||||
set_bit(EV_FF, hidinput->input.evbit);
|
||||
hidinput->input.ff_effects_max = TMFF_EFFECTS;
|
||||
}
|
||||
}
|
||||
|
||||
private->hid = hid;
|
||||
|
||||
spin_lock_init(&private->lock);
|
||||
init_timer(&private->timer);
|
||||
private->timer.data = (unsigned long)private;
|
||||
private->timer.function = hid_tmff_timer;
|
||||
|
||||
/* Event and exit callbacks */
|
||||
hid->ff_exit = hid_tmff_exit;
|
||||
hid->ff_event = hid_tmff_event;
|
||||
|
||||
info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse <zinx@epicsol.org>");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hid_tmff_exit(struct hid_device *hid)
|
||||
{
|
||||
struct tmff_device *tmff = hid->ff_private;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tmff->lock, flags);
|
||||
|
||||
set_bit(DEVICE_CLOSING, tmff->flags);
|
||||
del_timer_sync(&tmff->timer);
|
||||
|
||||
spin_unlock_irqrestore(&tmff->lock, flags);
|
||||
|
||||
kfree(tmff);
|
||||
}
|
||||
|
||||
static int hid_tmff_event(struct hid_device *hid, struct input_dev *input,
|
||||
unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
struct tmff_device *tmff = hid->ff_private;
|
||||
struct tmff_effect *effect = &tmff->effects[code];
|
||||
unsigned long flags;
|
||||
|
||||
if (type != EV_FF)
|
||||
return -EINVAL;
|
||||
if (!TMFF_CHECK_ID(code))
|
||||
return -EINVAL;
|
||||
if (!TMFF_CHECK_OWNERSHIP(code, tmff))
|
||||
return -EACCES;
|
||||
if (value < 0)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&tmff->lock, flags);
|
||||
|
||||
if (value > 0) {
|
||||
set_bit(EFFECT_STARTED, effect->flags);
|
||||
clear_bit(EFFECT_PLAYING, effect->flags);
|
||||
effect->count = value;
|
||||
effect->play_at = DELAY_CALC(jiffies, effect->effect.replay.delay);
|
||||
} else {
|
||||
clear_bit(EFFECT_STARTED, effect->flags);
|
||||
clear_bit(EFFECT_PLAYING, effect->flags);
|
||||
}
|
||||
|
||||
hid_tmff_recalculate_timer(tmff);
|
||||
|
||||
spin_unlock_irqrestore(&tmff->lock, flags);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* Erase all effects this process owns */
|
||||
|
||||
static int hid_tmff_flush(struct input_dev *dev, struct file *file)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct tmff_device *tmff = hid->ff_private;
|
||||
int i;
|
||||
|
||||
for (i=0; i<dev->ff_effects_max; ++i)
|
||||
|
||||
/* NOTE: no need to lock here. The only times EFFECT_USED is
|
||||
modified is when effects are uploaded or when an effect is
|
||||
erased. But a process cannot close its dev/input/eventX fd
|
||||
and perform ioctls on the same fd all at the same time */
|
||||
|
||||
if (current->pid == tmff->effects[i].owner
|
||||
&& test_bit(EFFECT_USED, tmff->effects[i].flags))
|
||||
if (hid_tmff_erase(dev, i))
|
||||
warn("erase effect %d failed", i);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_tmff_erase(struct input_dev *dev, int id)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct tmff_device *tmff = hid->ff_private;
|
||||
unsigned long flags;
|
||||
|
||||
if (!TMFF_CHECK_ID(id))
|
||||
return -EINVAL;
|
||||
if (!TMFF_CHECK_OWNERSHIP(id, tmff))
|
||||
return -EACCES;
|
||||
|
||||
spin_lock_irqsave(&tmff->lock, flags);
|
||||
|
||||
tmff->effects[id].flags[0] = 0;
|
||||
hid_tmff_recalculate_timer(tmff);
|
||||
|
||||
spin_unlock_irqrestore(&tmff->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_tmff_upload_effect(struct input_dev *input,
|
||||
struct ff_effect *effect)
|
||||
{
|
||||
struct hid_device *hid = input->private;
|
||||
struct tmff_device *tmff = hid->ff_private;
|
||||
int id;
|
||||
unsigned long flags;
|
||||
|
||||
if (!test_bit(effect->type, input->ffbit))
|
||||
return -EINVAL;
|
||||
if (effect->id != -1 && !TMFF_CHECK_ID(effect->id))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&tmff->lock, flags);
|
||||
|
||||
if (effect->id == -1) {
|
||||
/* Find a free effect */
|
||||
for (id = 0; id < TMFF_EFFECTS && test_bit(EFFECT_USED, tmff->effects[id].flags); ++id);
|
||||
|
||||
if (id >= TMFF_EFFECTS) {
|
||||
spin_unlock_irqrestore(&tmff->lock, flags);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
effect->id = id;
|
||||
tmff->effects[id].owner = current->pid;
|
||||
tmff->effects[id].flags[0] = 0;
|
||||
set_bit(EFFECT_USED, tmff->effects[id].flags);
|
||||
|
||||
} else {
|
||||
/* Re-uploading an owned effect, to change parameters */
|
||||
id = effect->id;
|
||||
clear_bit(EFFECT_PLAYING, tmff->effects[id].flags);
|
||||
}
|
||||
|
||||
tmff->effects[id].effect = *effect;
|
||||
|
||||
hid_tmff_recalculate_timer(tmff);
|
||||
|
||||
spin_unlock_irqrestore(&tmff->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Start the timer for the next start/stop/delay */
|
||||
/* Always call this while tmff->lock is locked */
|
||||
|
||||
static void hid_tmff_recalculate_timer(struct tmff_device *tmff)
|
||||
{
|
||||
int i;
|
||||
int events = 0;
|
||||
unsigned long next_time;
|
||||
|
||||
next_time = 0; /* Shut up compiler's incorrect warning */
|
||||
|
||||
/* Find the next change in an effect's status */
|
||||
for (i = 0; i < TMFF_EFFECTS; ++i) {
|
||||
struct tmff_effect *effect = &tmff->effects[i];
|
||||
unsigned long play_time;
|
||||
|
||||
if (!test_bit(EFFECT_STARTED, effect->flags))
|
||||
continue;
|
||||
|
||||
effect->stop_at = DELAY_CALC(effect->play_at, effect->effect.replay.length);
|
||||
|
||||
if (!test_bit(EFFECT_PLAYING, effect->flags))
|
||||
play_time = effect->play_at;
|
||||
else
|
||||
play_time = effect->stop_at;
|
||||
|
||||
events++;
|
||||
|
||||
if (time_after(jiffies, play_time))
|
||||
play_time = jiffies;
|
||||
|
||||
if (events == 1)
|
||||
next_time = play_time;
|
||||
else {
|
||||
if (time_after(next_time, play_time))
|
||||
next_time = play_time;
|
||||
}
|
||||
}
|
||||
|
||||
if (!events && tmff->effects_playing) {
|
||||
/* Treat all effects turning off as an event */
|
||||
events = 1;
|
||||
next_time = jiffies;
|
||||
}
|
||||
|
||||
if (!events) {
|
||||
/* No events, no time, no need for a timer. */
|
||||
del_timer_sync(&tmff->timer);
|
||||
return;
|
||||
}
|
||||
|
||||
mod_timer(&tmff->timer, next_time);
|
||||
}
|
||||
|
||||
/* 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 void hid_tmff_timer(unsigned long timer_data)
|
||||
{
|
||||
struct tmff_device *tmff = (struct tmff_device *) timer_data;
|
||||
struct hid_device *hid = tmff->hid;
|
||||
unsigned long flags;
|
||||
int left = 0, right = 0; /* Rumbling */
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&tmff->lock, flags);
|
||||
|
||||
tmff->effects_playing = 0;
|
||||
|
||||
for (i = 0; i < TMFF_EFFECTS; ++i) {
|
||||
struct tmff_effect *effect = &tmff->effects[i];
|
||||
|
||||
if (!test_bit(EFFECT_STARTED, effect->flags))
|
||||
continue;
|
||||
|
||||
if (!time_after(jiffies, effect->play_at))
|
||||
continue;
|
||||
|
||||
if (time_after(jiffies, effect->stop_at)) {
|
||||
|
||||
dbg("Finished playing once %d", i);
|
||||
clear_bit(EFFECT_PLAYING, effect->flags);
|
||||
|
||||
if (--effect->count <= 0) {
|
||||
dbg("Stopped %d", i);
|
||||
clear_bit(EFFECT_STARTED, effect->flags);
|
||||
continue;
|
||||
} else {
|
||||
dbg("Start again %d", i);
|
||||
effect->play_at = DELAY_CALC(jiffies, effect->effect.replay.delay);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
++tmff->effects_playing;
|
||||
|
||||
set_bit(EFFECT_PLAYING, effect->flags);
|
||||
|
||||
switch (effect->effect.type) {
|
||||
case FF_RUMBLE:
|
||||
right += effect->effect.u.rumble.strong_magnitude;
|
||||
left += effect->effect.u.rumble.weak_magnitude;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
left = hid_tmff_scale(left, tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
|
||||
right = hid_tmff_scale(right, tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
|
||||
|
||||
if (left != tmff->rumble->value[0] || right != tmff->rumble->value[1]) {
|
||||
tmff->rumble->value[0] = left;
|
||||
tmff->rumble->value[1] = right;
|
||||
dbg("(left,right)=(%08x, %08x)", left, right);
|
||||
hid_submit_report(hid, tmff->report, USB_DIR_OUT);
|
||||
}
|
||||
|
||||
if (!test_bit(DEVICE_CLOSING, tmff->flags))
|
||||
hid_tmff_recalculate_timer(tmff);
|
||||
|
||||
spin_unlock_irqrestore(&tmff->lock, flags);
|
||||
}
|
510
drivers/usb/input/hid.h
Normal file
510
drivers/usb/input/hid.h
Normal file
@@ -0,0 +1,510 @@
|
||||
#ifndef __HID_H
|
||||
#define __HID_H
|
||||
|
||||
/*
|
||||
* $Id: hid.h,v 1.24 2001/12/27 10:37:41 vojtech Exp $
|
||||
*
|
||||
* Copyright (c) 1999 Andreas Gal
|
||||
* Copyright (c) 2000-2001 Vojtech Pavlik
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <vojtech@ucw.cz>, or by paper mail:
|
||||
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
/*
|
||||
* USB HID (Human Interface Device) interface class code
|
||||
*/
|
||||
|
||||
#define USB_INTERFACE_CLASS_HID 3
|
||||
|
||||
/*
|
||||
* HID class requests
|
||||
*/
|
||||
|
||||
#define HID_REQ_GET_REPORT 0x01
|
||||
#define HID_REQ_GET_IDLE 0x02
|
||||
#define HID_REQ_GET_PROTOCOL 0x03
|
||||
#define HID_REQ_SET_REPORT 0x09
|
||||
#define HID_REQ_SET_IDLE 0x0A
|
||||
#define HID_REQ_SET_PROTOCOL 0x0B
|
||||
|
||||
/*
|
||||
* HID class descriptor types
|
||||
*/
|
||||
|
||||
#define HID_DT_HID (USB_TYPE_CLASS | 0x01)
|
||||
#define HID_DT_REPORT (USB_TYPE_CLASS | 0x02)
|
||||
#define HID_DT_PHYSICAL (USB_TYPE_CLASS | 0x03)
|
||||
|
||||
/*
|
||||
* We parse each description item into this structure. Short items data
|
||||
* values are expanded to 32-bit signed int, long items contain a pointer
|
||||
* into the data area.
|
||||
*/
|
||||
|
||||
struct hid_item {
|
||||
unsigned format;
|
||||
__u8 size;
|
||||
__u8 type;
|
||||
__u8 tag;
|
||||
union {
|
||||
__u8 u8;
|
||||
__s8 s8;
|
||||
__u16 u16;
|
||||
__s16 s16;
|
||||
__u32 u32;
|
||||
__s32 s32;
|
||||
__u8 *longdata;
|
||||
} data;
|
||||
};
|
||||
|
||||
/*
|
||||
* HID report item format
|
||||
*/
|
||||
|
||||
#define HID_ITEM_FORMAT_SHORT 0
|
||||
#define HID_ITEM_FORMAT_LONG 1
|
||||
|
||||
/*
|
||||
* Special tag indicating long items
|
||||
*/
|
||||
|
||||
#define HID_ITEM_TAG_LONG 15
|
||||
|
||||
/*
|
||||
* HID report descriptor item type (prefix bit 2,3)
|
||||
*/
|
||||
|
||||
#define HID_ITEM_TYPE_MAIN 0
|
||||
#define HID_ITEM_TYPE_GLOBAL 1
|
||||
#define HID_ITEM_TYPE_LOCAL 2
|
||||
#define HID_ITEM_TYPE_RESERVED 3
|
||||
|
||||
/*
|
||||
* HID report descriptor main item tags
|
||||
*/
|
||||
|
||||
#define HID_MAIN_ITEM_TAG_INPUT 8
|
||||
#define HID_MAIN_ITEM_TAG_OUTPUT 9
|
||||
#define HID_MAIN_ITEM_TAG_FEATURE 11
|
||||
#define HID_MAIN_ITEM_TAG_BEGIN_COLLECTION 10
|
||||
#define HID_MAIN_ITEM_TAG_END_COLLECTION 12
|
||||
|
||||
/*
|
||||
* HID report descriptor main item contents
|
||||
*/
|
||||
|
||||
#define HID_MAIN_ITEM_CONSTANT 0x001
|
||||
#define HID_MAIN_ITEM_VARIABLE 0x002
|
||||
#define HID_MAIN_ITEM_RELATIVE 0x004
|
||||
#define HID_MAIN_ITEM_WRAP 0x008
|
||||
#define HID_MAIN_ITEM_NONLINEAR 0x010
|
||||
#define HID_MAIN_ITEM_NO_PREFERRED 0x020
|
||||
#define HID_MAIN_ITEM_NULL_STATE 0x040
|
||||
#define HID_MAIN_ITEM_VOLATILE 0x080
|
||||
#define HID_MAIN_ITEM_BUFFERED_BYTE 0x100
|
||||
|
||||
/*
|
||||
* HID report descriptor collection item types
|
||||
*/
|
||||
|
||||
#define HID_COLLECTION_PHYSICAL 0
|
||||
#define HID_COLLECTION_APPLICATION 1
|
||||
#define HID_COLLECTION_LOGICAL 2
|
||||
|
||||
/*
|
||||
* HID report descriptor global item tags
|
||||
*/
|
||||
|
||||
#define HID_GLOBAL_ITEM_TAG_USAGE_PAGE 0
|
||||
#define HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM 1
|
||||
#define HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM 2
|
||||
#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM 3
|
||||
#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM 4
|
||||
#define HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT 5
|
||||
#define HID_GLOBAL_ITEM_TAG_UNIT 6
|
||||
#define HID_GLOBAL_ITEM_TAG_REPORT_SIZE 7
|
||||
#define HID_GLOBAL_ITEM_TAG_REPORT_ID 8
|
||||
#define HID_GLOBAL_ITEM_TAG_REPORT_COUNT 9
|
||||
#define HID_GLOBAL_ITEM_TAG_PUSH 10
|
||||
#define HID_GLOBAL_ITEM_TAG_POP 11
|
||||
|
||||
/*
|
||||
* HID report descriptor local item tags
|
||||
*/
|
||||
|
||||
#define HID_LOCAL_ITEM_TAG_USAGE 0
|
||||
#define HID_LOCAL_ITEM_TAG_USAGE_MINIMUM 1
|
||||
#define HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM 2
|
||||
#define HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX 3
|
||||
#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM 4
|
||||
#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM 5
|
||||
#define HID_LOCAL_ITEM_TAG_STRING_INDEX 7
|
||||
#define HID_LOCAL_ITEM_TAG_STRING_MINIMUM 8
|
||||
#define HID_LOCAL_ITEM_TAG_STRING_MAXIMUM 9
|
||||
#define HID_LOCAL_ITEM_TAG_DELIMITER 10
|
||||
|
||||
/*
|
||||
* HID usage tables
|
||||
*/
|
||||
|
||||
#define HID_USAGE_PAGE 0xffff0000
|
||||
|
||||
#define HID_UP_UNDEFINED 0x00000000
|
||||
#define HID_UP_GENDESK 0x00010000
|
||||
#define HID_UP_KEYBOARD 0x00070000
|
||||
#define HID_UP_LED 0x00080000
|
||||
#define HID_UP_BUTTON 0x00090000
|
||||
#define HID_UP_ORDINAL 0x000a0000
|
||||
#define HID_UP_CONSUMER 0x000c0000
|
||||
#define HID_UP_DIGITIZER 0x000d0000
|
||||
#define HID_UP_PID 0x000f0000
|
||||
#define HID_UP_HPVENDOR 0xff7f0000
|
||||
#define HID_UP_MSVENDOR 0xff000000
|
||||
|
||||
#define HID_USAGE 0x0000ffff
|
||||
|
||||
#define HID_GD_POINTER 0x00010001
|
||||
#define HID_GD_MOUSE 0x00010002
|
||||
#define HID_GD_JOYSTICK 0x00010004
|
||||
#define HID_GD_GAMEPAD 0x00010005
|
||||
#define HID_GD_KEYBOARD 0x00010006
|
||||
#define HID_GD_KEYPAD 0x00010007
|
||||
#define HID_GD_MULTIAXIS 0x00010008
|
||||
#define HID_GD_X 0x00010030
|
||||
#define HID_GD_Y 0x00010031
|
||||
#define HID_GD_Z 0x00010032
|
||||
#define HID_GD_RX 0x00010033
|
||||
#define HID_GD_RY 0x00010034
|
||||
#define HID_GD_RZ 0x00010035
|
||||
#define HID_GD_SLIDER 0x00010036
|
||||
#define HID_GD_DIAL 0x00010037
|
||||
#define HID_GD_WHEEL 0x00010038
|
||||
#define HID_GD_HATSWITCH 0x00010039
|
||||
#define HID_GD_BUFFER 0x0001003a
|
||||
#define HID_GD_BYTECOUNT 0x0001003b
|
||||
#define HID_GD_MOTION 0x0001003c
|
||||
#define HID_GD_START 0x0001003d
|
||||
#define HID_GD_SELECT 0x0001003e
|
||||
#define HID_GD_VX 0x00010040
|
||||
#define HID_GD_VY 0x00010041
|
||||
#define HID_GD_VZ 0x00010042
|
||||
#define HID_GD_VBRX 0x00010043
|
||||
#define HID_GD_VBRY 0x00010044
|
||||
#define HID_GD_VBRZ 0x00010045
|
||||
#define HID_GD_VNO 0x00010046
|
||||
#define HID_GD_FEATURE 0x00010047
|
||||
#define HID_GD_UP 0x00010090
|
||||
#define HID_GD_DOWN 0x00010091
|
||||
#define HID_GD_RIGHT 0x00010092
|
||||
#define HID_GD_LEFT 0x00010093
|
||||
|
||||
/*
|
||||
* HID report types --- Ouch! HID spec says 1 2 3!
|
||||
*/
|
||||
|
||||
#define HID_INPUT_REPORT 0
|
||||
#define HID_OUTPUT_REPORT 1
|
||||
#define HID_FEATURE_REPORT 2
|
||||
|
||||
/*
|
||||
* HID device quirks.
|
||||
*/
|
||||
|
||||
#define HID_QUIRK_INVERT 0x001
|
||||
#define HID_QUIRK_NOTOUCH 0x002
|
||||
#define HID_QUIRK_IGNORE 0x004
|
||||
#define HID_QUIRK_NOGET 0x008
|
||||
#define HID_QUIRK_HIDDEV 0x010
|
||||
#define HID_QUIRK_BADPAD 0x020
|
||||
#define HID_QUIRK_MULTI_INPUT 0x040
|
||||
#define HID_QUIRK_2WHEEL_MOUSE_HACK_7 0x080
|
||||
#define HID_QUIRK_2WHEEL_MOUSE_HACK_5 0x100
|
||||
#define HID_QUIRK_2WHEEL_MOUSE_HACK_ON 0x200
|
||||
|
||||
/*
|
||||
* This is the global environment of the parser. This information is
|
||||
* persistent for main-items. The global environment can be saved and
|
||||
* restored with PUSH/POP statements.
|
||||
*/
|
||||
|
||||
struct hid_global {
|
||||
unsigned usage_page;
|
||||
__s32 logical_minimum;
|
||||
__s32 logical_maximum;
|
||||
__s32 physical_minimum;
|
||||
__s32 physical_maximum;
|
||||
__s32 unit_exponent;
|
||||
unsigned unit;
|
||||
unsigned report_id;
|
||||
unsigned report_size;
|
||||
unsigned report_count;
|
||||
};
|
||||
|
||||
/*
|
||||
* This is the local environment. It is persistent up the next main-item.
|
||||
*/
|
||||
|
||||
#define HID_MAX_DESCRIPTOR_SIZE 4096
|
||||
#define HID_MAX_USAGES 1024
|
||||
#define HID_DEFAULT_NUM_COLLECTIONS 16
|
||||
|
||||
struct hid_local {
|
||||
unsigned usage[HID_MAX_USAGES]; /* usage array */
|
||||
unsigned collection_index[HID_MAX_USAGES]; /* collection index array */
|
||||
unsigned usage_index;
|
||||
unsigned usage_minimum;
|
||||
unsigned delimiter_depth;
|
||||
unsigned delimiter_branch;
|
||||
};
|
||||
|
||||
/*
|
||||
* This is the collection stack. We climb up the stack to determine
|
||||
* application and function of each field.
|
||||
*/
|
||||
|
||||
struct hid_collection {
|
||||
unsigned type;
|
||||
unsigned usage;
|
||||
unsigned level;
|
||||
};
|
||||
|
||||
struct hid_usage {
|
||||
unsigned hid; /* hid usage code */
|
||||
unsigned collection_index; /* index into collection array */
|
||||
/* hidinput data */
|
||||
__u16 code; /* input driver code */
|
||||
__u8 type; /* input driver type */
|
||||
__s8 hat_min; /* hat switch fun */
|
||||
__s8 hat_max; /* ditto */
|
||||
__s8 hat_dir; /* ditto */
|
||||
};
|
||||
|
||||
struct hid_input;
|
||||
|
||||
struct hid_field {
|
||||
unsigned physical; /* physical usage for this field */
|
||||
unsigned logical; /* logical usage for this field */
|
||||
unsigned application; /* application usage for this field */
|
||||
struct hid_usage *usage; /* usage table for this function */
|
||||
unsigned maxusage; /* maximum usage index */
|
||||
unsigned flags; /* main-item flags (i.e. volatile,array,constant) */
|
||||
unsigned report_offset; /* bit offset in the report */
|
||||
unsigned report_size; /* size of this field in the report */
|
||||
unsigned report_count; /* number of this field in the report */
|
||||
unsigned report_type; /* (input,output,feature) */
|
||||
__s32 *value; /* last known value(s) */
|
||||
__s32 logical_minimum;
|
||||
__s32 logical_maximum;
|
||||
__s32 physical_minimum;
|
||||
__s32 physical_maximum;
|
||||
__s32 unit_exponent;
|
||||
unsigned unit;
|
||||
struct hid_report *report; /* associated report */
|
||||
unsigned index; /* index into report->field[] */
|
||||
/* hidinput data */
|
||||
struct hid_input *hidinput; /* associated input structure */
|
||||
__u16 dpad; /* dpad input code */
|
||||
};
|
||||
|
||||
#define HID_MAX_FIELDS 64
|
||||
|
||||
struct hid_report {
|
||||
struct list_head list;
|
||||
unsigned id; /* id of this report */
|
||||
unsigned type; /* report type */
|
||||
struct hid_field *field[HID_MAX_FIELDS]; /* fields of the report */
|
||||
unsigned maxfield; /* maximum valid field index */
|
||||
unsigned size; /* size of the report (bits) */
|
||||
struct hid_device *device; /* associated device */
|
||||
};
|
||||
|
||||
struct hid_report_enum {
|
||||
unsigned numbered;
|
||||
struct list_head report_list;
|
||||
struct hid_report *report_id_hash[256];
|
||||
};
|
||||
|
||||
#define HID_REPORT_TYPES 3
|
||||
|
||||
#define HID_BUFFER_SIZE 64 /* use 64 for compatibility with all possible packetlen */
|
||||
#define HID_CONTROL_FIFO_SIZE 256 /* to init devices with >100 reports */
|
||||
#define HID_OUTPUT_FIFO_SIZE 64
|
||||
|
||||
struct hid_control_fifo {
|
||||
unsigned char dir;
|
||||
struct hid_report *report;
|
||||
};
|
||||
|
||||
#define HID_CLAIMED_INPUT 1
|
||||
#define HID_CLAIMED_HIDDEV 2
|
||||
|
||||
#define HID_CTRL_RUNNING 1
|
||||
#define HID_OUT_RUNNING 2
|
||||
|
||||
struct hid_input {
|
||||
struct list_head list;
|
||||
struct hid_report *report;
|
||||
struct input_dev input;
|
||||
};
|
||||
|
||||
struct hid_device { /* device report descriptor */
|
||||
__u8 *rdesc;
|
||||
unsigned rsize;
|
||||
struct hid_collection *collection; /* List of HID collections */
|
||||
unsigned collection_size; /* Number of allocated hid_collections */
|
||||
unsigned maxcollection; /* Number of parsed collections */
|
||||
unsigned maxapplication; /* Number of applications */
|
||||
unsigned version; /* HID version */
|
||||
unsigned country; /* HID country */
|
||||
struct hid_report_enum report_enum[HID_REPORT_TYPES];
|
||||
|
||||
struct usb_device *dev; /* USB device */
|
||||
struct usb_interface *intf; /* USB interface */
|
||||
int ifnum; /* USB interface number */
|
||||
|
||||
unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
|
||||
|
||||
struct urb *urbin; /* Input URB */
|
||||
char *inbuf; /* Input buffer */
|
||||
dma_addr_t inbuf_dma; /* Input buffer dma */
|
||||
|
||||
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 claimed; /* Claimed by hidinput, hiddev? */
|
||||
unsigned quirks; /* Various quirks the device can pull on us */
|
||||
|
||||
struct list_head inputs; /* The list of inputs */
|
||||
void *hiddev; /* The hiddev structure */
|
||||
int minor; /* Hiddev minor number */
|
||||
|
||||
wait_queue_head_t wait; /* For sleeping */
|
||||
|
||||
int open; /* is the device open by anyone? */
|
||||
char name[128]; /* Device name */
|
||||
char phys[64]; /* Device physical location */
|
||||
char uniq[64]; /* Device unique identifier (serial #) */
|
||||
|
||||
void *ff_private; /* Private data for the force-feedback driver */
|
||||
void (*ff_exit)(struct hid_device*); /* Called by hid_exit_ff(hid) */
|
||||
int (*ff_event)(struct hid_device *hid, struct input_dev *input,
|
||||
unsigned int type, unsigned int code, int value);
|
||||
};
|
||||
|
||||
#define HID_GLOBAL_STACK_SIZE 4
|
||||
#define HID_COLLECTION_STACK_SIZE 4
|
||||
|
||||
struct hid_parser {
|
||||
struct hid_global global;
|
||||
struct hid_global global_stack[HID_GLOBAL_STACK_SIZE];
|
||||
unsigned global_stack_ptr;
|
||||
struct hid_local local;
|
||||
unsigned collection_stack[HID_COLLECTION_STACK_SIZE];
|
||||
unsigned collection_stack_ptr;
|
||||
struct hid_device *device;
|
||||
};
|
||||
|
||||
struct hid_class_descriptor {
|
||||
__u8 bDescriptorType;
|
||||
__u16 wDescriptorLength;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct hid_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u16 bcdHID;
|
||||
__u8 bCountryCode;
|
||||
__u8 bNumDescriptors;
|
||||
|
||||
struct hid_class_descriptor desc[1];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#ifdef DEBUG
|
||||
#include "hid-debug.h"
|
||||
#else
|
||||
#define hid_dump_input(a,b) do { } while (0)
|
||||
#define hid_dump_device(c) do { } while (0)
|
||||
#define hid_dump_field(a,b) do { } while (0)
|
||||
#define resolv_usage(a) do { } while (0)
|
||||
#define resolv_event(a,b) do { } while (0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_HIDINPUT
|
||||
/* Applications from HID Usage Tables 4/8/99 Version 1.1 */
|
||||
/* We ignore a few input applications that are not widely used */
|
||||
#define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001))
|
||||
extern void hidinput_hid_event(struct hid_device *, struct hid_field *, struct hid_usage *, __s32, struct pt_regs *regs);
|
||||
extern void hidinput_report_event(struct hid_device *hid, struct hid_report *report);
|
||||
extern int hidinput_connect(struct hid_device *);
|
||||
extern void hidinput_disconnect(struct hid_device *);
|
||||
#else
|
||||
#define IS_INPUT_APPLICATION(a) (0)
|
||||
static inline void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, struct pt_regs *regs) { }
|
||||
static inline void hidinput_report_event(struct hid_device *hid, struct hid_report *report) { }
|
||||
static inline int hidinput_connect(struct hid_device *hid) { return -ENODEV; }
|
||||
static inline void hidinput_disconnect(struct hid_device *hid) { }
|
||||
#endif
|
||||
|
||||
int hid_open(struct hid_device *);
|
||||
void hid_close(struct hid_device *);
|
||||
int hid_set_field(struct hid_field *, unsigned, __s32);
|
||||
void hid_submit_report(struct hid_device *, struct hid_report *, unsigned char dir);
|
||||
void hid_init_reports(struct hid_device *hid);
|
||||
struct hid_field *hid_find_field_by_usage(struct hid_device *hid, __u32 wanted_usage, int type);
|
||||
int hid_wait_io(struct hid_device* hid);
|
||||
|
||||
|
||||
#ifdef CONFIG_HID_FF
|
||||
int hid_ff_init(struct hid_device *hid);
|
||||
#else
|
||||
static inline int hid_ff_init(struct hid_device *hid) { return -1; }
|
||||
#endif
|
||||
static inline void hid_ff_exit(struct hid_device *hid)
|
||||
{
|
||||
if (hid->ff_exit)
|
||||
hid->ff_exit(hid);
|
||||
}
|
||||
static inline int hid_ff_event(struct hid_device *hid, struct input_dev *input,
|
||||
unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
if (hid->ff_event)
|
||||
return hid->ff_event(hid, input, type, code, value);
|
||||
return -ENOSYS;
|
||||
}
|
844
drivers/usb/input/hiddev.c
Normal file
844
drivers/usb/input/hiddev.c
Normal file
@@ -0,0 +1,844 @@
|
||||
/*
|
||||
* 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 <stewart@wetlogic.net>
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb.h>
|
||||
#include "hid.h"
|
||||
#include <linux/hiddev.h>
|
||||
#include <linux/devfs_fs_kernel.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 hiddev_list *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 hiddev_list *next;
|
||||
};
|
||||
|
||||
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 flags = rinfo->report_id & ~HID_REPORT_ID_MASK;
|
||||
struct hid_report_enum *report_enum;
|
||||
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:
|
||||
list = report_enum->report_list.next;
|
||||
if (list == &report_enum->report_list)
|
||||
return NULL;
|
||||
rinfo->report_id = ((struct hid_report *) list)->id;
|
||||
break;
|
||||
|
||||
case HID_REPORT_ID_NEXT:
|
||||
list = (struct list_head *)
|
||||
report_enum->report_id_hash[rinfo->report_id & HID_REPORT_ID_MASK];
|
||||
if (list == NULL)
|
||||
return NULL;
|
||||
list = list->next;
|
||||
if (list == &report_enum->report_list)
|
||||
return NULL;
|
||||
rinfo->report_id = ((struct hid_report *) list)->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 = hiddev->list;
|
||||
|
||||
while (list) {
|
||||
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);
|
||||
}
|
||||
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
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, struct pt_regs *regs)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
struct hiddev_list **listptr;
|
||||
|
||||
listptr = &list->hiddev->list;
|
||||
hiddev_fasync(-1, file, 0);
|
||||
|
||||
while (*listptr && (*listptr != list))
|
||||
listptr = &((*listptr)->next);
|
||||
*listptr = (*listptr)->next;
|
||||
|
||||
if (!--list->hiddev->open) {
|
||||
if (list->hiddev->exist)
|
||||
hid_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 = kmalloc(sizeof(struct hiddev_list), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
memset(list, 0, sizeof(struct hiddev_list));
|
||||
|
||||
list->hiddev = hiddev_table[i];
|
||||
list->next = hiddev_table[i]->list;
|
||||
hiddev_table[i]->list = list;
|
||||
|
||||
file->private_data = list;
|
||||
|
||||
if (!list->hiddev->open++)
|
||||
if (list->hiddev->exist)
|
||||
hid_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_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->dev;
|
||||
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;
|
||||
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 = hid->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:
|
||||
hid_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;
|
||||
|
||||
hid_submit_report(hid, report, USB_DIR_IN);
|
||||
hid_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;
|
||||
|
||||
hid_submit_report(hid, report, USB_DIR_OUT);
|
||||
|
||||
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 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 = "usb/hid/hiddev%d",
|
||||
.fops = &hiddev_fops,
|
||||
.mode = S_IFCHR | S_IRUGO | S_IWUSR,
|
||||
.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;
|
||||
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 = kmalloc(sizeof(struct hiddev), GFP_KERNEL)))
|
||||
return -1;
|
||||
memset(hiddev, 0, sizeof(struct hiddev));
|
||||
|
||||
retval = usb_register_dev(hid->intf, &hiddev_class);
|
||||
if (retval) {
|
||||
err("Not able to get a minor for this device.");
|
||||
kfree(hiddev);
|
||||
return -1;
|
||||
}
|
||||
|
||||
init_waitqueue_head(&hiddev->wait);
|
||||
|
||||
hiddev_table[hid->intf->minor - HIDDEV_MINOR_BASE] = hiddev;
|
||||
|
||||
hiddev->hid = hid;
|
||||
hiddev->exist = 1;
|
||||
|
||||
hid->minor = hid->intf->minor;
|
||||
hid->hiddev = 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;
|
||||
|
||||
hiddev->exist = 0;
|
||||
|
||||
hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL;
|
||||
usb_deregister_dev(hiddev->hid->intf, &hiddev_class);
|
||||
|
||||
if (hiddev->open) {
|
||||
hid_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 = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "hiddev",
|
||||
.probe = hiddev_usbd_probe,
|
||||
};
|
||||
|
||||
int __init hiddev_init(void)
|
||||
{
|
||||
devfs_mk_dir("usb/hid");
|
||||
return usb_register(&hiddev_driver);
|
||||
}
|
||||
|
||||
void hiddev_exit(void)
|
||||
{
|
||||
usb_deregister(&hiddev_driver);
|
||||
devfs_remove("usb/hid");
|
||||
}
|
241
drivers/usb/input/kbtab.c
Normal file
241
drivers/usb/input/kbtab.c
Normal file
@@ -0,0 +1,241 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
* v0.0.1 - Original, extremely basic version, 2.4.xx only
|
||||
* v0.0.2 - Updated, works with 2.5.62 and 2.4.20;
|
||||
* - added pressure-threshold modules param code from
|
||||
* Alex Perry <alex.perry@ieee.org>
|
||||
*/
|
||||
|
||||
#define DRIVER_VERSION "v0.0.2"
|
||||
#define DRIVER_AUTHOR "Josh Myer <josh@joshisanerd.com>"
|
||||
#define DRIVER_DESC "USB KB Gear JamStudio Tablet driver"
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE(DRIVER_LICENSE);
|
||||
|
||||
#define USB_VENDOR_ID_KBGEAR 0x084e
|
||||
|
||||
static int kb_pressure_click = 0x10;
|
||||
module_param(kb_pressure_click, int, 0);
|
||||
MODULE_PARM_DESC(kb_pressure_click, "pressure threshold for clicks");
|
||||
|
||||
struct kbtab {
|
||||
signed char *data;
|
||||
dma_addr_t data_dma;
|
||||
struct input_dev dev;
|
||||
struct usb_device *usbdev;
|
||||
struct urb *irq;
|
||||
int open;
|
||||
int x, y;
|
||||
int button;
|
||||
int pressure;
|
||||
__u32 serial[2];
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
static void kbtab_irq(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct kbtab *kbtab = urb->context;
|
||||
unsigned char *data = kbtab->data;
|
||||
struct input_dev *dev = &kbtab->dev;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
kbtab->x = le16_to_cpu(get_unaligned((__le16 *) &data[1]));
|
||||
kbtab->y = le16_to_cpu(get_unaligned((__le16 *) &data[3]));
|
||||
|
||||
kbtab->pressure = (data[5]);
|
||||
|
||||
input_report_key(dev, BTN_TOOL_PEN, 1);
|
||||
|
||||
input_report_abs(dev, ABS_X, kbtab->x);
|
||||
input_report_abs(dev, ABS_Y, kbtab->y);
|
||||
|
||||
/*input_report_key(dev, BTN_TOUCH , data[0] & 0x01);*/
|
||||
input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
|
||||
|
||||
if( -1 == kb_pressure_click){
|
||||
input_report_abs(dev, ABS_PRESSURE, kbtab->pressure);
|
||||
} else {
|
||||
input_report_key(dev, BTN_LEFT, (kbtab->pressure > kb_pressure_click) ? 1 : 0);
|
||||
};
|
||||
|
||||
input_sync(dev);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err ("%s - usb_submit_urb failed with result %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
static struct usb_device_id kbtab_ids[] = {
|
||||
{ USB_DEVICE(USB_VENDOR_ID_KBGEAR, 0x1001), .driver_info = 0 },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, kbtab_ids);
|
||||
|
||||
static int kbtab_open(struct input_dev *dev)
|
||||
{
|
||||
struct kbtab *kbtab = dev->private;
|
||||
|
||||
if (kbtab->open++)
|
||||
return 0;
|
||||
|
||||
kbtab->irq->dev = kbtab->usbdev;
|
||||
if (usb_submit_urb(kbtab->irq, GFP_KERNEL)) {
|
||||
kbtab->open--;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kbtab_close(struct input_dev *dev)
|
||||
{
|
||||
struct kbtab *kbtab = dev->private;
|
||||
|
||||
if (!--kbtab->open)
|
||||
usb_kill_urb(kbtab->irq);
|
||||
}
|
||||
|
||||
static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct kbtab *kbtab;
|
||||
char path[64];
|
||||
|
||||
if (!(kbtab = kmalloc(sizeof(struct kbtab), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
memset(kbtab, 0, sizeof(struct kbtab));
|
||||
|
||||
kbtab->data = usb_buffer_alloc(dev, 8, GFP_KERNEL, &kbtab->data_dma);
|
||||
if (!kbtab->data) {
|
||||
kfree(kbtab);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
kbtab->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!kbtab->irq) {
|
||||
usb_buffer_free(dev, 10, kbtab->data, kbtab->data_dma);
|
||||
kfree(kbtab);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
kbtab->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_MSC);
|
||||
kbtab->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
|
||||
|
||||
kbtab->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
|
||||
|
||||
kbtab->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH);
|
||||
|
||||
kbtab->dev.mscbit[0] |= BIT(MSC_SERIAL);
|
||||
|
||||
kbtab->dev.absmax[ABS_X] = 0x2000;
|
||||
kbtab->dev.absmax[ABS_Y] = 0x1750;
|
||||
kbtab->dev.absmax[ABS_PRESSURE] = 0xff;
|
||||
|
||||
kbtab->dev.absfuzz[ABS_X] = 4;
|
||||
kbtab->dev.absfuzz[ABS_Y] = 4;
|
||||
|
||||
kbtab->dev.private = kbtab;
|
||||
kbtab->dev.open = kbtab_open;
|
||||
kbtab->dev.close = kbtab_close;
|
||||
|
||||
usb_make_path(dev, path, 64);
|
||||
sprintf(kbtab->phys, "%s/input0", path);
|
||||
|
||||
kbtab->dev.name = "KB Gear Tablet";
|
||||
kbtab->dev.phys = kbtab->phys;
|
||||
kbtab->dev.id.bustype = BUS_USB;
|
||||
kbtab->dev.id.vendor = le16_to_cpu(dev->descriptor.idVendor);
|
||||
kbtab->dev.id.product = le16_to_cpu(dev->descriptor.idProduct);
|
||||
kbtab->dev.id.version = le16_to_cpu(dev->descriptor.bcdDevice);
|
||||
kbtab->dev.dev = &intf->dev;
|
||||
kbtab->usbdev = dev;
|
||||
|
||||
endpoint = &intf->cur_altsetting->endpoint[0].desc;
|
||||
|
||||
usb_fill_int_urb(kbtab->irq, dev,
|
||||
usb_rcvintpipe(dev, endpoint->bEndpointAddress),
|
||||
kbtab->data, 8,
|
||||
kbtab_irq, kbtab, endpoint->bInterval);
|
||||
kbtab->irq->transfer_dma = kbtab->data_dma;
|
||||
kbtab->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
input_register_device(&kbtab->dev);
|
||||
|
||||
printk(KERN_INFO "input: KB Gear Tablet on %s\n", path);
|
||||
|
||||
usb_set_intfdata(intf, kbtab);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kbtab_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct kbtab *kbtab = usb_get_intfdata (intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (kbtab) {
|
||||
usb_kill_urb(kbtab->irq);
|
||||
input_unregister_device(&kbtab->dev);
|
||||
usb_free_urb(kbtab->irq);
|
||||
usb_buffer_free(interface_to_usbdev(intf), 10, kbtab->data, kbtab->data_dma);
|
||||
kfree(kbtab);
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_driver kbtab_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "kbtab",
|
||||
.probe = kbtab_probe,
|
||||
.disconnect = kbtab_disconnect,
|
||||
.id_table = kbtab_ids,
|
||||
};
|
||||
|
||||
static int __init kbtab_init(void)
|
||||
{
|
||||
int retval;
|
||||
retval = usb_register(&kbtab_driver);
|
||||
if (retval)
|
||||
goto out;
|
||||
info(DRIVER_VERSION ":" DRIVER_DESC);
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit kbtab_exit(void)
|
||||
{
|
||||
usb_deregister(&kbtab_driver);
|
||||
}
|
||||
|
||||
module_init(kbtab_init);
|
||||
module_exit(kbtab_exit);
|
367
drivers/usb/input/mtouchusb.c
Normal file
367
drivers/usb/input/mtouchusb.c
Normal file
@@ -0,0 +1,367 @@
|
||||
/******************************************************************************
|
||||
* mtouchusb.c -- Driver for Microtouch (Now 3M) USB Touchscreens
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* Based upon original work by Radoslaw Garbacz (usb-support@ite.pl)
|
||||
* (http://freshmeat.net/projects/3mtouchscreendriver)
|
||||
*
|
||||
* History
|
||||
*
|
||||
* 0.3 & 0.4 2002 (TEJ) tejohnson@yahoo.com
|
||||
* Updated to 2.4.18, then 2.4.19
|
||||
* Old version still relied on stealing a minor
|
||||
*
|
||||
* 0.5 02/26/2004 (TEJ) tejohnson@yahoo.com
|
||||
* Complete rewrite using Linux Input in 2.6.3
|
||||
* Unfortunately no calibration support at this time
|
||||
*
|
||||
* 1.4 04/25/2004 (TEJ) tejohnson@yahoo.com
|
||||
* Changed reset from standard USB dev reset to vendor reset
|
||||
* Changed data sent to host from compensated to raw coordinates
|
||||
* Eliminated vendor/product module params
|
||||
* Performed multiple successfull tests with an EXII-5010UC
|
||||
*
|
||||
* 1.5 02/27/2005 ddstreet@ieee.org
|
||||
* Added module parameter to select raw or hw-calibrated coordinate reporting
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#include <linux/config.h>
|
||||
|
||||
#ifdef CONFIG_USB_DEBUG
|
||||
#define DEBUG
|
||||
#else
|
||||
#undef DEBUG
|
||||
#endif
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#define MTOUCHUSB_MIN_XC 0x0
|
||||
#define MTOUCHUSB_MAX_RAW_XC 0x4000
|
||||
#define MTOUCHUSB_MAX_CALIB_XC 0xffff
|
||||
#define MTOUCHUSB_XC_FUZZ 0x0
|
||||
#define MTOUCHUSB_XC_FLAT 0x0
|
||||
#define MTOUCHUSB_MIN_YC 0x0
|
||||
#define MTOUCHUSB_MAX_RAW_YC 0x4000
|
||||
#define MTOUCHUSB_MAX_CALIB_YC 0xffff
|
||||
#define MTOUCHUSB_YC_FUZZ 0x0
|
||||
#define MTOUCHUSB_YC_FLAT 0x0
|
||||
|
||||
#define MTOUCHUSB_ASYNC_REPORT 1
|
||||
#define MTOUCHUSB_RESET 7
|
||||
#define MTOUCHUSB_REPORT_DATA_SIZE 11
|
||||
#define MTOUCHUSB_REQ_CTRLLR_ID 10
|
||||
|
||||
#define MTOUCHUSB_GET_RAW_XC(data) (data[8]<<8 | data[7])
|
||||
#define MTOUCHUSB_GET_CALIB_XC(data) (data[4]<<8 | data[3])
|
||||
#define MTOUCHUSB_GET_RAW_YC(data) (data[10]<<8 | data[9])
|
||||
#define MTOUCHUSB_GET_CALIB_YC(data) (data[6]<<8 | data[5])
|
||||
#define MTOUCHUSB_GET_XC(data) (raw_coordinates ? \
|
||||
MTOUCHUSB_GET_RAW_XC(data) : \
|
||||
MTOUCHUSB_GET_CALIB_XC(data))
|
||||
#define MTOUCHUSB_GET_YC(data) (raw_coordinates ? \
|
||||
MTOUCHUSB_GET_RAW_YC(data) : \
|
||||
MTOUCHUSB_GET_CALIB_YC(data))
|
||||
#define MTOUCHUSB_GET_TOUCHED(data) ((data[2] & 0x40) ? 1:0)
|
||||
|
||||
#define DRIVER_VERSION "v1.5"
|
||||
#define DRIVER_AUTHOR "Todd E. Johnson, tejohnson@yahoo.com"
|
||||
#define DRIVER_DESC "3M USB Touchscreen Driver"
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
static int raw_coordinates = 1;
|
||||
|
||||
module_param(raw_coordinates, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(raw_coordinates, "report raw coordinate values (y, default) or hardware-calibrated coordinate values (n)");
|
||||
|
||||
struct mtouch_usb {
|
||||
unsigned char *data;
|
||||
dma_addr_t data_dma;
|
||||
struct urb *irq;
|
||||
struct usb_device *udev;
|
||||
struct input_dev input;
|
||||
int open;
|
||||
char name[128];
|
||||
char phys[64];
|
||||
};
|
||||
|
||||
static struct usb_device_id mtouchusb_devices [] = {
|
||||
{ USB_DEVICE(0x0596, 0x0001) },
|
||||
{ }
|
||||
};
|
||||
|
||||
static void mtouchusb_irq(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct mtouch_usb *mtouch = urb->context;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ETIMEDOUT:
|
||||
/* this urb is timing out */
|
||||
dbg("%s - urb timed out - was the device unplugged?",
|
||||
__FUNCTION__);
|
||||
return;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d",
|
||||
__FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d",
|
||||
__FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
input_regs(&mtouch->input, regs);
|
||||
input_report_key(&mtouch->input, BTN_TOUCH,
|
||||
MTOUCHUSB_GET_TOUCHED(mtouch->data));
|
||||
input_report_abs(&mtouch->input, ABS_X,
|
||||
MTOUCHUSB_GET_XC(mtouch->data));
|
||||
input_report_abs(&mtouch->input, ABS_Y,
|
||||
(raw_coordinates ? MTOUCHUSB_MAX_RAW_YC : MTOUCHUSB_MAX_CALIB_YC)
|
||||
- MTOUCHUSB_GET_YC(mtouch->data));
|
||||
input_sync(&mtouch->input);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err ("%s - usb_submit_urb failed with result: %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
static int mtouchusb_open (struct input_dev *input)
|
||||
{
|
||||
struct mtouch_usb *mtouch = input->private;
|
||||
|
||||
if (mtouch->open++)
|
||||
return 0;
|
||||
|
||||
mtouch->irq->dev = mtouch->udev;
|
||||
|
||||
if (usb_submit_urb (mtouch->irq, GFP_ATOMIC)) {
|
||||
mtouch->open--;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtouchusb_close (struct input_dev *input)
|
||||
{
|
||||
struct mtouch_usb *mtouch = input->private;
|
||||
|
||||
if (!--mtouch->open)
|
||||
usb_kill_urb (mtouch->irq);
|
||||
}
|
||||
|
||||
static int mtouchusb_alloc_buffers(struct usb_device *udev, struct mtouch_usb *mtouch)
|
||||
{
|
||||
dbg("%s - called", __FUNCTION__);
|
||||
|
||||
mtouch->data = usb_buffer_alloc(udev, MTOUCHUSB_REPORT_DATA_SIZE,
|
||||
SLAB_ATOMIC, &mtouch->data_dma);
|
||||
|
||||
if (!mtouch->data)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtouchusb_free_buffers(struct usb_device *udev, struct mtouch_usb *mtouch)
|
||||
{
|
||||
dbg("%s - called", __FUNCTION__);
|
||||
|
||||
if (mtouch->data)
|
||||
usb_buffer_free(udev, MTOUCHUSB_REPORT_DATA_SIZE,
|
||||
mtouch->data, mtouch->data_dma);
|
||||
}
|
||||
|
||||
static int mtouchusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct mtouch_usb *mtouch;
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct usb_device *udev = interface_to_usbdev (intf);
|
||||
char path[64];
|
||||
int nRet;
|
||||
|
||||
dbg("%s - called", __FUNCTION__);
|
||||
|
||||
dbg("%s - setting interface", __FUNCTION__);
|
||||
interface = intf->cur_altsetting;
|
||||
|
||||
dbg("%s - setting endpoint", __FUNCTION__);
|
||||
endpoint = &interface->endpoint[0].desc;
|
||||
|
||||
if (!(mtouch = kmalloc (sizeof (struct mtouch_usb), GFP_KERNEL))) {
|
||||
err("%s - Out of memory.", __FUNCTION__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(mtouch, 0, sizeof(struct mtouch_usb));
|
||||
mtouch->udev = udev;
|
||||
|
||||
dbg("%s - allocating buffers", __FUNCTION__);
|
||||
if (mtouchusb_alloc_buffers(udev, mtouch)) {
|
||||
mtouchusb_free_buffers(udev, mtouch);
|
||||
kfree(mtouch);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mtouch->input.private = mtouch;
|
||||
mtouch->input.open = mtouchusb_open;
|
||||
mtouch->input.close = mtouchusb_close;
|
||||
|
||||
usb_make_path(udev, path, 64);
|
||||
sprintf(mtouch->phys, "%s/input0", path);
|
||||
|
||||
mtouch->input.name = mtouch->name;
|
||||
mtouch->input.phys = mtouch->phys;
|
||||
mtouch->input.id.bustype = BUS_USB;
|
||||
mtouch->input.id.vendor = le16_to_cpu(udev->descriptor.idVendor);
|
||||
mtouch->input.id.product = le16_to_cpu(udev->descriptor.idProduct);
|
||||
mtouch->input.id.version = le16_to_cpu(udev->descriptor.bcdDevice);
|
||||
mtouch->input.dev = &intf->dev;
|
||||
|
||||
mtouch->input.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||
mtouch->input.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
|
||||
mtouch->input.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||
|
||||
/* Used to Scale Compensated Data and Flip Y */
|
||||
mtouch->input.absmin[ABS_X] = MTOUCHUSB_MIN_XC;
|
||||
mtouch->input.absmax[ABS_X] = raw_coordinates ? \
|
||||
MTOUCHUSB_MAX_RAW_XC : MTOUCHUSB_MAX_CALIB_XC;
|
||||
mtouch->input.absfuzz[ABS_X] = MTOUCHUSB_XC_FUZZ;
|
||||
mtouch->input.absflat[ABS_X] = MTOUCHUSB_XC_FLAT;
|
||||
mtouch->input.absmin[ABS_Y] = MTOUCHUSB_MIN_YC;
|
||||
mtouch->input.absmax[ABS_Y] = raw_coordinates ? \
|
||||
MTOUCHUSB_MAX_RAW_YC : MTOUCHUSB_MAX_CALIB_YC;
|
||||
mtouch->input.absfuzz[ABS_Y] = MTOUCHUSB_YC_FUZZ;
|
||||
mtouch->input.absflat[ABS_Y] = MTOUCHUSB_YC_FLAT;
|
||||
|
||||
if (udev->manufacturer)
|
||||
strcat(mtouch->name, udev->manufacturer);
|
||||
if (udev->product)
|
||||
sprintf(mtouch->name, "%s %s", mtouch->name, udev->product);
|
||||
|
||||
if (!strlen(mtouch->name))
|
||||
sprintf(mtouch->name, "USB Touchscreen %04x:%04x",
|
||||
mtouch->input.id.vendor, mtouch->input.id.product);
|
||||
|
||||
nRet = usb_control_msg(mtouch->udev,
|
||||
usb_rcvctrlpipe(udev, 0),
|
||||
MTOUCHUSB_RESET,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
1,
|
||||
0,
|
||||
NULL,
|
||||
0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
dbg("%s - usb_control_msg - MTOUCHUSB_RESET - bytes|err: %d",
|
||||
__FUNCTION__, nRet);
|
||||
|
||||
dbg("%s - usb_alloc_urb: mtouch->irq", __FUNCTION__);
|
||||
mtouch->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!mtouch->irq) {
|
||||
dbg("%s - usb_alloc_urb failed: mtouch->irq", __FUNCTION__);
|
||||
mtouchusb_free_buffers(udev, mtouch);
|
||||
kfree(mtouch);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dbg("%s - usb_fill_int_urb", __FUNCTION__);
|
||||
usb_fill_int_urb(mtouch->irq,
|
||||
mtouch->udev,
|
||||
usb_rcvintpipe(mtouch->udev, 0x81),
|
||||
mtouch->data,
|
||||
MTOUCHUSB_REPORT_DATA_SIZE,
|
||||
mtouchusb_irq,
|
||||
mtouch,
|
||||
endpoint->bInterval);
|
||||
|
||||
dbg("%s - input_register_device", __FUNCTION__);
|
||||
input_register_device(&mtouch->input);
|
||||
|
||||
nRet = usb_control_msg(mtouch->udev,
|
||||
usb_rcvctrlpipe(udev, 0),
|
||||
MTOUCHUSB_ASYNC_REPORT,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
1,
|
||||
1,
|
||||
NULL,
|
||||
0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d",
|
||||
__FUNCTION__, nRet);
|
||||
|
||||
printk(KERN_INFO "input: %s on %s\n", mtouch->name, path);
|
||||
usb_set_intfdata(intf, mtouch);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtouchusb_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct mtouch_usb *mtouch = usb_get_intfdata (intf);
|
||||
|
||||
dbg("%s - called", __FUNCTION__);
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (mtouch) {
|
||||
dbg("%s - mtouch is initialized, cleaning up", __FUNCTION__);
|
||||
usb_kill_urb(mtouch->irq);
|
||||
input_unregister_device(&mtouch->input);
|
||||
usb_free_urb(mtouch->irq);
|
||||
mtouchusb_free_buffers(interface_to_usbdev(intf), mtouch);
|
||||
kfree(mtouch);
|
||||
}
|
||||
}
|
||||
|
||||
MODULE_DEVICE_TABLE (usb, mtouchusb_devices);
|
||||
|
||||
static struct usb_driver mtouchusb_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "mtouchusb",
|
||||
.probe = mtouchusb_probe,
|
||||
.disconnect = mtouchusb_disconnect,
|
||||
.id_table = mtouchusb_devices,
|
||||
};
|
||||
|
||||
static int __init mtouchusb_init(void) {
|
||||
dbg("%s - called", __FUNCTION__);
|
||||
return usb_register(&mtouchusb_driver);
|
||||
}
|
||||
|
||||
static void __exit mtouchusb_cleanup(void) {
|
||||
dbg("%s - called", __FUNCTION__);
|
||||
usb_deregister(&mtouchusb_driver);
|
||||
}
|
||||
|
||||
module_init(mtouchusb_init);
|
||||
module_exit(mtouchusb_cleanup);
|
||||
|
||||
MODULE_AUTHOR( DRIVER_AUTHOR );
|
||||
MODULE_DESCRIPTION( DRIVER_DESC );
|
||||
MODULE_LICENSE("GPL");
|
295
drivers/usb/input/pid.c
Normal file
295
drivers/usb/input/pid.c
Normal file
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* PID Force feedback support for hid devices.
|
||||
*
|
||||
* Copyright (c) 2002 Rodrigo Damazio.
|
||||
* Portions by Johann Deneux and Bjorn Augustson
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <rdamazio@lsi.usp.br>
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb.h>
|
||||
#include "hid.h"
|
||||
#include "pid.h"
|
||||
|
||||
#define DEBUG
|
||||
|
||||
#define CHECK_OWNERSHIP(i, hid_pid) \
|
||||
((i) < FF_EFFECTS_MAX && i >= 0 && \
|
||||
test_bit(FF_PID_FLAGS_USED, &hid_pid->effects[(i)].flags) && \
|
||||
(current->pid == 0 || \
|
||||
(hid_pid)->effects[(i)].owner == current->pid))
|
||||
|
||||
/* Called when a transfer is completed */
|
||||
static void hid_pid_ctrl_out(struct urb *u, struct pt_regs *regs)
|
||||
{
|
||||
dev_dbg(&u->dev->dev, "hid_pid_ctrl_out - Transfer Completed\n");
|
||||
}
|
||||
|
||||
static void hid_pid_exit(struct hid_device *hid)
|
||||
{
|
||||
struct hid_ff_pid *private = hid->ff_private;
|
||||
|
||||
if (private->urbffout) {
|
||||
usb_kill_urb(private->urbffout);
|
||||
usb_free_urb(private->urbffout);
|
||||
}
|
||||
}
|
||||
|
||||
static int pid_upload_periodic(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update)
|
||||
{
|
||||
dev_info(&pid->hid->dev->dev, "requested periodic force upload\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pid_upload_constant(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update)
|
||||
{
|
||||
dev_info(&pid->hid->dev->dev, "requested constant force upload\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pid_upload_condition(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update)
|
||||
{
|
||||
dev_info(&pid->hid->dev->dev, "requested Condition force upload\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pid_upload_ramp(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update)
|
||||
{
|
||||
dev_info(&pid->hid->dev->dev, "request ramp force upload\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_pid_event(struct hid_device *hid, struct input_dev *input,
|
||||
unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
dev_dbg(&hid->dev->dev, "PID event received: type=%d,code=%d,value=%d.\n", type, code, value);
|
||||
|
||||
if (type != EV_FF)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Lock must be held by caller */
|
||||
static void hid_pid_ctrl_playback(struct hid_device *hid, struct hid_pid_effect *effect, int play)
|
||||
{
|
||||
if (play)
|
||||
set_bit(FF_PID_FLAGS_PLAYING, &effect->flags);
|
||||
else
|
||||
clear_bit(FF_PID_FLAGS_PLAYING, &effect->flags);
|
||||
}
|
||||
|
||||
static int hid_pid_erase(struct input_dev *dev, int id)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct hid_ff_pid *pid = hid->ff_private;
|
||||
struct hid_field *field;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (!CHECK_OWNERSHIP(id, pid))
|
||||
return -EACCES;
|
||||
|
||||
/* Find report */
|
||||
field = hid_find_field_by_usage(hid, HID_UP_PID | FF_PID_USAGE_BLOCK_FREE,
|
||||
HID_OUTPUT_REPORT);
|
||||
if (!field) {
|
||||
dev_err(&hid->dev->dev, "couldn't find report\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = hid_set_field(field, 0, pid->effects[id].device_id);
|
||||
if (ret) {
|
||||
dev_err(&hid->dev->dev, "couldn't set field\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
hid_submit_report(hid, field->report, USB_DIR_OUT);
|
||||
|
||||
spin_lock_irqsave(&pid->lock, flags);
|
||||
hid_pid_ctrl_playback(hid, pid->effects + id, 0);
|
||||
pid->effects[id].flags = 0;
|
||||
spin_unlock_irqrestore(&pid->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Erase all effects this process owns */
|
||||
static int hid_pid_flush(struct input_dev *dev, struct file *file)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct hid_ff_pid *pid = hid->ff_private;
|
||||
int i;
|
||||
|
||||
/*NOTE: no need to lock here. The only times EFFECT_USED is
|
||||
modified is when effects are uploaded or when an effect is
|
||||
erased. But a process cannot close its dev/input/eventX fd
|
||||
and perform ioctls on the same fd all at the same time */
|
||||
/*FIXME: multiple threads, anyone? */
|
||||
for (i = 0; i < dev->ff_effects_max; ++i)
|
||||
if (current->pid == pid->effects[i].owner
|
||||
&& test_bit(FF_PID_FLAGS_USED, &pid->effects[i].flags))
|
||||
if (hid_pid_erase(dev, i))
|
||||
dev_warn(&hid->dev->dev, "erase effect %d failed", i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_pid_upload_effect(struct input_dev *dev,
|
||||
struct ff_effect *effect)
|
||||
{
|
||||
struct hid_ff_pid *pid_private = (struct hid_ff_pid *)(dev->private);
|
||||
int ret;
|
||||
int is_update;
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(&pid_private->hid->dev->dev, "upload effect called: effect_type=%x\n", effect->type);
|
||||
/* Check this effect type is supported by this device */
|
||||
if (!test_bit(effect->type, dev->ffbit)) {
|
||||
dev_dbg(&pid_private->hid->dev->dev,
|
||||
"invalid kind of effect requested.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we want to create a new effect, get a free id
|
||||
*/
|
||||
if (effect->id == -1) {
|
||||
int id = 0;
|
||||
|
||||
// Spinlock so we don`t get a race condition when choosing IDs
|
||||
spin_lock_irqsave(&pid_private->lock, flags);
|
||||
|
||||
while (id < FF_EFFECTS_MAX)
|
||||
if (!test_and_set_bit(FF_PID_FLAGS_USED, &pid_private->effects[id++].flags))
|
||||
break;
|
||||
|
||||
if (id == FF_EFFECTS_MAX) {
|
||||
spin_unlock_irqrestore(&pid_private->lock, flags);
|
||||
// TEMP - We need to get ff_effects_max correctly first: || id >= dev->ff_effects_max) {
|
||||
dev_dbg(&pid_private->hid->dev->dev, "Not enough device memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
effect->id = id;
|
||||
dev_dbg(&pid_private->hid->dev->dev, "effect ID is %d\n.", id);
|
||||
pid_private->effects[id].owner = current->pid;
|
||||
pid_private->effects[id].flags = (1 << FF_PID_FLAGS_USED);
|
||||
spin_unlock_irqrestore(&pid_private->lock, flags);
|
||||
|
||||
is_update = FF_PID_FALSE;
|
||||
} else {
|
||||
/* We want to update an effect */
|
||||
if (!CHECK_OWNERSHIP(effect->id, pid_private))
|
||||
return -EACCES;
|
||||
|
||||
/* Parameter type cannot be updated */
|
||||
if (effect->type != pid_private->effects[effect->id].effect.type)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check the effect is not already being updated */
|
||||
if (test_bit(FF_PID_FLAGS_UPDATING, &pid_private->effects[effect->id].flags))
|
||||
return -EAGAIN;
|
||||
|
||||
is_update = FF_PID_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Upload the effect
|
||||
*/
|
||||
switch (effect->type) {
|
||||
case FF_PERIODIC:
|
||||
ret = pid_upload_periodic(pid_private, effect, is_update);
|
||||
break;
|
||||
|
||||
case FF_CONSTANT:
|
||||
ret = pid_upload_constant(pid_private, effect, is_update);
|
||||
break;
|
||||
|
||||
case FF_SPRING:
|
||||
case FF_FRICTION:
|
||||
case FF_DAMPER:
|
||||
case FF_INERTIA:
|
||||
ret = pid_upload_condition(pid_private, effect, is_update);
|
||||
break;
|
||||
|
||||
case FF_RAMP:
|
||||
ret = pid_upload_ramp(pid_private, effect, is_update);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_dbg(&pid_private->hid->dev->dev,
|
||||
"invalid type of effect requested - %x.\n",
|
||||
effect->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* If a packet was sent, forbid new updates until we are notified
|
||||
* that the packet was updated
|
||||
*/
|
||||
if (ret == 0)
|
||||
set_bit(FF_PID_FLAGS_UPDATING, &pid_private->effects[effect->id].flags);
|
||||
pid_private->effects[effect->id].effect = *effect;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hid_pid_init(struct hid_device *hid)
|
||||
{
|
||||
struct hid_ff_pid *private;
|
||||
struct hid_input *hidinput = list_entry(&hid->inputs, struct hid_input, list);
|
||||
|
||||
private = hid->ff_private = kcalloc(1, sizeof(struct hid_ff_pid), GFP_KERNEL);
|
||||
if (!private)
|
||||
return -ENOMEM;
|
||||
|
||||
private->hid = hid;
|
||||
|
||||
hid->ff_exit = hid_pid_exit;
|
||||
hid->ff_event = hid_pid_event;
|
||||
|
||||
/* Open output URB */
|
||||
if (!(private->urbffout = usb_alloc_urb(0, GFP_KERNEL))) {
|
||||
kfree(private);
|
||||
return -1;
|
||||
}
|
||||
|
||||
usb_fill_control_urb(private->urbffout, hid->dev, 0,
|
||||
(void *)&private->ffcr, private->ctrl_buffer, 8,
|
||||
hid_pid_ctrl_out, hid);
|
||||
hidinput->input.upload_effect = hid_pid_upload_effect;
|
||||
hidinput->input.flush = hid_pid_flush;
|
||||
hidinput->input.ff_effects_max = 8; // A random default
|
||||
set_bit(EV_FF, hidinput->input.evbit);
|
||||
set_bit(EV_FF_STATUS, hidinput->input.evbit);
|
||||
|
||||
spin_lock_init(&private->lock);
|
||||
|
||||
printk(KERN_INFO "Force feedback driver for PID devices by Rodrigo Damazio <rdamazio@lsi.usp.br>.\n");
|
||||
|
||||
return 0;
|
||||
}
|
62
drivers/usb/input/pid.h
Normal file
62
drivers/usb/input/pid.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* PID Force feedback support for hid devices.
|
||||
*
|
||||
* Copyright (c) 2002 Rodrigo Damazio.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <rdamazio@lsi.usp.br>
|
||||
*/
|
||||
|
||||
#define FF_EFFECTS_MAX 64
|
||||
|
||||
#define FF_PID_FLAGS_USED 1 /* If the effect exists */
|
||||
#define FF_PID_FLAGS_UPDATING 2 /* If the effect is being updated */
|
||||
#define FF_PID_FLAGS_PLAYING 3 /* If the effect is currently being played */
|
||||
|
||||
#define FF_PID_FALSE 0
|
||||
#define FF_PID_TRUE 1
|
||||
|
||||
struct hid_pid_effect {
|
||||
unsigned long flags;
|
||||
pid_t owner;
|
||||
unsigned int device_id; /* The device-assigned ID */
|
||||
struct ff_effect effect;
|
||||
};
|
||||
|
||||
struct hid_ff_pid {
|
||||
struct hid_device *hid;
|
||||
unsigned long gain;
|
||||
|
||||
struct urb *urbffout;
|
||||
struct usb_ctrlrequest ffcr;
|
||||
spinlock_t lock;
|
||||
|
||||
unsigned char ctrl_buffer[8];
|
||||
|
||||
struct hid_pid_effect effects[FF_EFFECTS_MAX];
|
||||
};
|
||||
|
||||
/*
|
||||
* Constants from the PID usage table (still far from complete)
|
||||
*/
|
||||
|
||||
#define FF_PID_USAGE_BLOCK_LOAD 0x89UL
|
||||
#define FF_PID_USAGE_BLOCK_FREE 0x90UL
|
||||
#define FF_PID_USAGE_NEW_EFFECT 0xABUL
|
||||
#define FF_PID_USAGE_POOL_REPORT 0x7FUL
|
464
drivers/usb/input/powermate.c
Normal file
464
drivers/usb/input/powermate.c
Normal file
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
* A driver for the Griffin Technology, Inc. "PowerMate" USB controller dial.
|
||||
*
|
||||
* v1.1, (c)2002 William R Sowerbutts <will@sowerbutts.com>
|
||||
*
|
||||
* This device is a anodised aluminium knob which connects over USB. It can measure
|
||||
* clockwise and anticlockwise rotation. The dial also acts as a pushbutton with
|
||||
* a spring for automatic release. The base contains a pair of LEDs which illuminate
|
||||
* the translucent base. It rotates without limit and reports its relative rotation
|
||||
* back to the host when polled by the USB controller.
|
||||
*
|
||||
* Testing with the knob I have has shown that it measures approximately 94 "clicks"
|
||||
* for one full rotation. Testing with my High Speed Rotation Actuator (ok, it was
|
||||
* a variable speed cordless electric drill) has shown that the device can measure
|
||||
* speeds of up to 7 clicks either clockwise or anticlockwise between pollings from
|
||||
* the host. If it counts more than 7 clicks before it is polled, it will wrap back
|
||||
* to zero and start counting again. This was at quite high speed, however, almost
|
||||
* certainly faster than the human hand could turn it. Griffin say that it loses a
|
||||
* pulse or two on a direction change; the granularity is so fine that I never
|
||||
* noticed this in practice.
|
||||
*
|
||||
* The device's microcontroller can be programmed to set the LED to either a constant
|
||||
* intensity, or to a rhythmic pulsing. Several patterns and speeds are available.
|
||||
*
|
||||
* Griffin were very happy to provide documentation and free hardware for development.
|
||||
*
|
||||
* Some userspace tools are available on the web: http://sowerbutts.com/powermate/
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#define POWERMATE_VENDOR 0x077d /* Griffin Technology, Inc. */
|
||||
#define POWERMATE_PRODUCT_NEW 0x0410 /* Griffin PowerMate */
|
||||
#define POWERMATE_PRODUCT_OLD 0x04AA /* Griffin soundKnob */
|
||||
|
||||
#define CONTOUR_VENDOR 0x05f3 /* Contour Design, Inc. */
|
||||
#define CONTOUR_JOG 0x0240 /* Jog and Shuttle */
|
||||
|
||||
/* these are the command codes we send to the device */
|
||||
#define SET_STATIC_BRIGHTNESS 0x01
|
||||
#define SET_PULSE_ASLEEP 0x02
|
||||
#define SET_PULSE_AWAKE 0x03
|
||||
#define SET_PULSE_MODE 0x04
|
||||
|
||||
/* these refer to bits in the powermate_device's requires_update field. */
|
||||
#define UPDATE_STATIC_BRIGHTNESS (1<<0)
|
||||
#define UPDATE_PULSE_ASLEEP (1<<1)
|
||||
#define UPDATE_PULSE_AWAKE (1<<2)
|
||||
#define UPDATE_PULSE_MODE (1<<3)
|
||||
|
||||
/* at least two versions of the hardware exist, with differing payload
|
||||
sizes. the first three bytes always contain the "interesting" data in
|
||||
the relevant format. */
|
||||
#define POWERMATE_PAYLOAD_SIZE_MAX 6
|
||||
#define POWERMATE_PAYLOAD_SIZE_MIN 3
|
||||
struct powermate_device {
|
||||
signed char *data;
|
||||
dma_addr_t data_dma;
|
||||
struct urb *irq, *config;
|
||||
struct usb_ctrlrequest *configcr;
|
||||
dma_addr_t configcr_dma;
|
||||
struct usb_device *udev;
|
||||
struct input_dev input;
|
||||
spinlock_t lock;
|
||||
int static_brightness;
|
||||
int pulse_speed;
|
||||
int pulse_table;
|
||||
int pulse_asleep;
|
||||
int pulse_awake;
|
||||
int requires_update; // physical settings which are out of sync
|
||||
char phys[64];
|
||||
};
|
||||
|
||||
static char pm_name_powermate[] = "Griffin PowerMate";
|
||||
static char pm_name_soundknob[] = "Griffin SoundKnob";
|
||||
|
||||
static void powermate_config_complete(struct urb *urb, struct pt_regs *regs);
|
||||
|
||||
/* Callback for data arriving from the PowerMate over the USB interrupt pipe */
|
||||
static void powermate_irq(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct powermate_device *pm = urb->context;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* handle updates to device state */
|
||||
input_regs(&pm->input, regs);
|
||||
input_report_key(&pm->input, BTN_0, pm->data[0] & 0x01);
|
||||
input_report_rel(&pm->input, REL_DIAL, pm->data[1]);
|
||||
input_sync(&pm->input);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err ("%s - usb_submit_urb failed with result %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
/* Decide if we need to issue a control message and do so. Must be called with pm->lock taken */
|
||||
static void powermate_sync_state(struct powermate_device *pm)
|
||||
{
|
||||
if (pm->requires_update == 0)
|
||||
return; /* no updates are required */
|
||||
if (pm->config->status == -EINPROGRESS)
|
||||
return; /* an update is already in progress; it'll issue this update when it completes */
|
||||
|
||||
if (pm->requires_update & UPDATE_PULSE_ASLEEP){
|
||||
pm->configcr->wValue = cpu_to_le16( SET_PULSE_ASLEEP );
|
||||
pm->configcr->wIndex = cpu_to_le16( pm->pulse_asleep ? 1 : 0 );
|
||||
pm->requires_update &= ~UPDATE_PULSE_ASLEEP;
|
||||
}else if (pm->requires_update & UPDATE_PULSE_AWAKE){
|
||||
pm->configcr->wValue = cpu_to_le16( SET_PULSE_AWAKE );
|
||||
pm->configcr->wIndex = cpu_to_le16( pm->pulse_awake ? 1 : 0 );
|
||||
pm->requires_update &= ~UPDATE_PULSE_AWAKE;
|
||||
}else if (pm->requires_update & UPDATE_PULSE_MODE){
|
||||
int op, arg;
|
||||
/* the powermate takes an operation and an argument for its pulse algorithm.
|
||||
the operation can be:
|
||||
0: divide the speed
|
||||
1: pulse at normal speed
|
||||
2: multiply the speed
|
||||
the argument only has an effect for operations 0 and 2, and ranges between
|
||||
1 (least effect) to 255 (maximum effect).
|
||||
|
||||
thus, several states are equivalent and are coalesced into one state.
|
||||
|
||||
we map this onto a range from 0 to 510, with:
|
||||
0 -- 254 -- use divide (0 = slowest)
|
||||
255 -- use normal speed
|
||||
256 -- 510 -- use multiple (510 = fastest).
|
||||
|
||||
Only values of 'arg' quite close to 255 are particularly useful/spectacular.
|
||||
*/
|
||||
if (pm->pulse_speed < 255){
|
||||
op = 0; // divide
|
||||
arg = 255 - pm->pulse_speed;
|
||||
} else if (pm->pulse_speed > 255){
|
||||
op = 2; // multiply
|
||||
arg = pm->pulse_speed - 255;
|
||||
} else {
|
||||
op = 1; // normal speed
|
||||
arg = 0; // can be any value
|
||||
}
|
||||
pm->configcr->wValue = cpu_to_le16( (pm->pulse_table << 8) | SET_PULSE_MODE );
|
||||
pm->configcr->wIndex = cpu_to_le16( (arg << 8) | op );
|
||||
pm->requires_update &= ~UPDATE_PULSE_MODE;
|
||||
}else if (pm->requires_update & UPDATE_STATIC_BRIGHTNESS){
|
||||
pm->configcr->wValue = cpu_to_le16( SET_STATIC_BRIGHTNESS );
|
||||
pm->configcr->wIndex = cpu_to_le16( pm->static_brightness );
|
||||
pm->requires_update &= ~UPDATE_STATIC_BRIGHTNESS;
|
||||
}else{
|
||||
printk(KERN_ERR "powermate: unknown update required");
|
||||
pm->requires_update = 0; /* fudge the bug */
|
||||
return;
|
||||
}
|
||||
|
||||
/* printk("powermate: %04x %04x\n", pm->configcr->wValue, pm->configcr->wIndex); */
|
||||
|
||||
pm->configcr->bRequestType = 0x41; /* vendor request */
|
||||
pm->configcr->bRequest = 0x01;
|
||||
pm->configcr->wLength = 0;
|
||||
|
||||
usb_fill_control_urb(pm->config, pm->udev, usb_sndctrlpipe(pm->udev, 0),
|
||||
(void *) pm->configcr, NULL, 0,
|
||||
powermate_config_complete, pm);
|
||||
pm->config->setup_dma = pm->configcr_dma;
|
||||
pm->config->transfer_flags |= URB_NO_SETUP_DMA_MAP;
|
||||
|
||||
if (usb_submit_urb(pm->config, GFP_ATOMIC))
|
||||
printk(KERN_ERR "powermate: usb_submit_urb(config) failed");
|
||||
}
|
||||
|
||||
/* Called when our asynchronous control message completes. We may need to issue another immediately */
|
||||
static void powermate_config_complete(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct powermate_device *pm = urb->context;
|
||||
unsigned long flags;
|
||||
|
||||
if (urb->status)
|
||||
printk(KERN_ERR "powermate: config urb returned %d\n", urb->status);
|
||||
|
||||
spin_lock_irqsave(&pm->lock, flags);
|
||||
powermate_sync_state(pm);
|
||||
spin_unlock_irqrestore(&pm->lock, flags);
|
||||
}
|
||||
|
||||
/* Set the LED up as described and begin the sync with the hardware if required */
|
||||
static void powermate_pulse_led(struct powermate_device *pm, int static_brightness, int pulse_speed,
|
||||
int pulse_table, int pulse_asleep, int pulse_awake)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (pulse_speed < 0)
|
||||
pulse_speed = 0;
|
||||
if (pulse_table < 0)
|
||||
pulse_table = 0;
|
||||
if (pulse_speed > 510)
|
||||
pulse_speed = 510;
|
||||
if (pulse_table > 2)
|
||||
pulse_table = 2;
|
||||
|
||||
pulse_asleep = !!pulse_asleep;
|
||||
pulse_awake = !!pulse_awake;
|
||||
|
||||
|
||||
spin_lock_irqsave(&pm->lock, flags);
|
||||
|
||||
/* mark state updates which are required */
|
||||
if (static_brightness != pm->static_brightness){
|
||||
pm->static_brightness = static_brightness;
|
||||
pm->requires_update |= UPDATE_STATIC_BRIGHTNESS;
|
||||
}
|
||||
if (pulse_asleep != pm->pulse_asleep){
|
||||
pm->pulse_asleep = pulse_asleep;
|
||||
pm->requires_update |= (UPDATE_PULSE_ASLEEP | UPDATE_STATIC_BRIGHTNESS);
|
||||
}
|
||||
if (pulse_awake != pm->pulse_awake){
|
||||
pm->pulse_awake = pulse_awake;
|
||||
pm->requires_update |= (UPDATE_PULSE_AWAKE | UPDATE_STATIC_BRIGHTNESS);
|
||||
}
|
||||
if (pulse_speed != pm->pulse_speed || pulse_table != pm->pulse_table){
|
||||
pm->pulse_speed = pulse_speed;
|
||||
pm->pulse_table = pulse_table;
|
||||
pm->requires_update |= UPDATE_PULSE_MODE;
|
||||
}
|
||||
|
||||
powermate_sync_state(pm);
|
||||
|
||||
spin_unlock_irqrestore(&pm->lock, flags);
|
||||
}
|
||||
|
||||
/* Callback from the Input layer when an event arrives from userspace to configure the LED */
|
||||
static int powermate_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int _value)
|
||||
{
|
||||
unsigned int command = (unsigned int)_value;
|
||||
struct powermate_device *pm = dev->private;
|
||||
|
||||
if (type == EV_MSC && code == MSC_PULSELED){
|
||||
/*
|
||||
bits 0- 7: 8 bits: LED brightness
|
||||
bits 8-16: 9 bits: pulsing speed modifier (0 ... 510); 0-254 = slower, 255 = standard, 256-510 = faster.
|
||||
bits 17-18: 2 bits: pulse table (0, 1, 2 valid)
|
||||
bit 19: 1 bit : pulse whilst asleep?
|
||||
bit 20: 1 bit : pulse constantly?
|
||||
*/
|
||||
int static_brightness = command & 0xFF; // bits 0-7
|
||||
int pulse_speed = (command >> 8) & 0x1FF; // bits 8-16
|
||||
int pulse_table = (command >> 17) & 0x3; // bits 17-18
|
||||
int pulse_asleep = (command >> 19) & 0x1; // bit 19
|
||||
int pulse_awake = (command >> 20) & 0x1; // bit 20
|
||||
|
||||
powermate_pulse_led(pm, static_brightness, pulse_speed, pulse_table, pulse_asleep, pulse_awake);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_device *pm)
|
||||
{
|
||||
pm->data = usb_buffer_alloc(udev, POWERMATE_PAYLOAD_SIZE_MAX,
|
||||
SLAB_ATOMIC, &pm->data_dma);
|
||||
if (!pm->data)
|
||||
return -1;
|
||||
pm->configcr = usb_buffer_alloc(udev, sizeof(*(pm->configcr)),
|
||||
SLAB_ATOMIC, &pm->configcr_dma);
|
||||
if (!pm->configcr)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void powermate_free_buffers(struct usb_device *udev, struct powermate_device *pm)
|
||||
{
|
||||
if (pm->data)
|
||||
usb_buffer_free(udev, POWERMATE_PAYLOAD_SIZE_MAX,
|
||||
pm->data, pm->data_dma);
|
||||
if (pm->configcr)
|
||||
usb_buffer_free(udev, sizeof(*(pm->configcr)),
|
||||
pm->configcr, pm->configcr_dma);
|
||||
}
|
||||
|
||||
/* Called whenever a USB device matching one in our supported devices table is connected */
|
||||
static int powermate_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev (intf);
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct powermate_device *pm;
|
||||
int pipe, maxp;
|
||||
char path[64];
|
||||
|
||||
interface = intf->cur_altsetting;
|
||||
endpoint = &interface->endpoint[0].desc;
|
||||
if (!(endpoint->bEndpointAddress & 0x80))
|
||||
return -EIO;
|
||||
if ((endpoint->bmAttributes & 3) != 3)
|
||||
return -EIO;
|
||||
|
||||
usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||
0x0a, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
0, interface->desc.bInterfaceNumber, NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
if (!(pm = kmalloc(sizeof(struct powermate_device), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
|
||||
memset(pm, 0, sizeof(struct powermate_device));
|
||||
pm->udev = udev;
|
||||
|
||||
if (powermate_alloc_buffers(udev, pm)) {
|
||||
powermate_free_buffers(udev, pm);
|
||||
kfree(pm);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pm->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!pm->irq) {
|
||||
powermate_free_buffers(udev, pm);
|
||||
kfree(pm);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pm->config = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!pm->config) {
|
||||
usb_free_urb(pm->irq);
|
||||
powermate_free_buffers(udev, pm);
|
||||
kfree(pm);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock_init(&pm->lock);
|
||||
init_input_dev(&pm->input);
|
||||
|
||||
/* get a handle to the interrupt data pipe */
|
||||
pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
|
||||
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
|
||||
|
||||
if(maxp < POWERMATE_PAYLOAD_SIZE_MIN || maxp > POWERMATE_PAYLOAD_SIZE_MAX){
|
||||
printk("powermate: Expected payload of %d--%d bytes, found %d bytes!\n",
|
||||
POWERMATE_PAYLOAD_SIZE_MIN, POWERMATE_PAYLOAD_SIZE_MAX, maxp);
|
||||
maxp = POWERMATE_PAYLOAD_SIZE_MAX;
|
||||
}
|
||||
|
||||
usb_fill_int_urb(pm->irq, udev, pipe, pm->data,
|
||||
maxp, powermate_irq,
|
||||
pm, endpoint->bInterval);
|
||||
pm->irq->transfer_dma = pm->data_dma;
|
||||
pm->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
/* register our interrupt URB with the USB system */
|
||||
if (usb_submit_urb(pm->irq, GFP_KERNEL)) {
|
||||
powermate_free_buffers(udev, pm);
|
||||
kfree(pm);
|
||||
return -EIO; /* failure */
|
||||
}
|
||||
|
||||
switch (le16_to_cpu(udev->descriptor.idProduct)) {
|
||||
case POWERMATE_PRODUCT_NEW: pm->input.name = pm_name_powermate; break;
|
||||
case POWERMATE_PRODUCT_OLD: pm->input.name = pm_name_soundknob; break;
|
||||
default:
|
||||
pm->input.name = pm_name_soundknob;
|
||||
printk(KERN_WARNING "powermate: unknown product id %04x\n",
|
||||
le16_to_cpu(udev->descriptor.idProduct));
|
||||
}
|
||||
|
||||
pm->input.private = pm;
|
||||
pm->input.evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_MSC);
|
||||
pm->input.keybit[LONG(BTN_0)] = BIT(BTN_0);
|
||||
pm->input.relbit[LONG(REL_DIAL)] = BIT(REL_DIAL);
|
||||
pm->input.mscbit[LONG(MSC_PULSELED)] = BIT(MSC_PULSELED);
|
||||
pm->input.id.bustype = BUS_USB;
|
||||
pm->input.id.vendor = le16_to_cpu(udev->descriptor.idVendor);
|
||||
pm->input.id.product = le16_to_cpu(udev->descriptor.idProduct);
|
||||
pm->input.id.version = le16_to_cpu(udev->descriptor.bcdDevice);
|
||||
pm->input.event = powermate_input_event;
|
||||
pm->input.dev = &intf->dev;
|
||||
pm->input.phys = pm->phys;
|
||||
|
||||
input_register_device(&pm->input);
|
||||
|
||||
usb_make_path(udev, path, 64);
|
||||
snprintf(pm->phys, 64, "%s/input0", path);
|
||||
printk(KERN_INFO "input: %s on %s\n", pm->input.name, pm->input.phys);
|
||||
|
||||
/* force an update of everything */
|
||||
pm->requires_update = UPDATE_PULSE_ASLEEP | UPDATE_PULSE_AWAKE | UPDATE_PULSE_MODE | UPDATE_STATIC_BRIGHTNESS;
|
||||
powermate_pulse_led(pm, 0x80, 255, 0, 1, 0); // set default pulse parameters
|
||||
|
||||
usb_set_intfdata(intf, pm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called when a USB device we've accepted ownership of is removed */
|
||||
static void powermate_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct powermate_device *pm = usb_get_intfdata (intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (pm) {
|
||||
pm->requires_update = 0;
|
||||
usb_kill_urb(pm->irq);
|
||||
input_unregister_device(&pm->input);
|
||||
usb_free_urb(pm->irq);
|
||||
usb_free_urb(pm->config);
|
||||
powermate_free_buffers(interface_to_usbdev(intf), pm);
|
||||
|
||||
kfree(pm);
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_device_id powermate_devices [] = {
|
||||
{ USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_NEW) },
|
||||
{ USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_OLD) },
|
||||
{ USB_DEVICE(CONTOUR_VENDOR, CONTOUR_JOG) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE (usb, powermate_devices);
|
||||
|
||||
static struct usb_driver powermate_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "powermate",
|
||||
.probe = powermate_probe,
|
||||
.disconnect = powermate_disconnect,
|
||||
.id_table = powermate_devices,
|
||||
};
|
||||
|
||||
static int __init powermate_init(void)
|
||||
{
|
||||
return usb_register(&powermate_driver);
|
||||
}
|
||||
|
||||
static void __exit powermate_cleanup(void)
|
||||
{
|
||||
usb_deregister(&powermate_driver);
|
||||
}
|
||||
|
||||
module_init(powermate_init);
|
||||
module_exit(powermate_cleanup);
|
||||
|
||||
MODULE_AUTHOR( "William R Sowerbutts" );
|
||||
MODULE_DESCRIPTION( "Griffin Technology, Inc PowerMate driver" );
|
||||
MODULE_LICENSE("GPL");
|
310
drivers/usb/input/touchkitusb.c
Normal file
310
drivers/usb/input/touchkitusb.c
Normal file
@@ -0,0 +1,310 @@
|
||||
/******************************************************************************
|
||||
* touchkitusb.c -- Driver for eGalax TouchKit USB Touchscreens
|
||||
*
|
||||
* Copyright (C) 2004 by Daniel Ritz
|
||||
* Copyright (C) by Todd E. Johnson (mtouchusb.c)
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* Based upon mtouchusb.c
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#if !defined(DEBUG) && defined(CONFIG_USB_DEBUG)
|
||||
#define DEBUG
|
||||
#endif
|
||||
#include <linux/usb.h>
|
||||
|
||||
|
||||
#define TOUCHKIT_MIN_XC 0x0
|
||||
#define TOUCHKIT_MAX_XC 0x07ff
|
||||
#define TOUCHKIT_XC_FUZZ 0x0
|
||||
#define TOUCHKIT_XC_FLAT 0x0
|
||||
#define TOUCHKIT_MIN_YC 0x0
|
||||
#define TOUCHKIT_MAX_YC 0x07ff
|
||||
#define TOUCHKIT_YC_FUZZ 0x0
|
||||
#define TOUCHKIT_YC_FLAT 0x0
|
||||
#define TOUCHKIT_REPORT_DATA_SIZE 8
|
||||
|
||||
#define TOUCHKIT_DOWN 0x01
|
||||
#define TOUCHKIT_POINT_TOUCH 0x81
|
||||
#define TOUCHKIT_POINT_NOTOUCH 0x80
|
||||
|
||||
#define TOUCHKIT_GET_TOUCHED(dat) ((((dat)[0]) & TOUCHKIT_DOWN) ? 1 : 0)
|
||||
#define TOUCHKIT_GET_X(dat) (((dat)[3] << 7) | (dat)[4])
|
||||
#define TOUCHKIT_GET_Y(dat) (((dat)[1] << 7) | (dat)[2])
|
||||
|
||||
#define DRIVER_VERSION "v0.1"
|
||||
#define DRIVER_AUTHOR "Daniel Ritz <daniel.ritz@gmx.ch>"
|
||||
#define DRIVER_DESC "eGalax TouchKit USB HID Touchscreen Driver"
|
||||
|
||||
static int swap_xy;
|
||||
module_param(swap_xy, bool, 0644);
|
||||
MODULE_PARM_DESC(swap_xy, "If set X and Y axes are swapped.");
|
||||
|
||||
struct touchkit_usb {
|
||||
unsigned char *data;
|
||||
dma_addr_t data_dma;
|
||||
struct urb *irq;
|
||||
struct usb_device *udev;
|
||||
struct input_dev input;
|
||||
int open;
|
||||
char name[128];
|
||||
char phys[64];
|
||||
};
|
||||
|
||||
static struct usb_device_id touchkit_devices[] = {
|
||||
{USB_DEVICE(0x3823, 0x0001)},
|
||||
{USB_DEVICE(0x0eef, 0x0001)},
|
||||
{}
|
||||
};
|
||||
|
||||
static void touchkit_irq(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct touchkit_usb *touchkit = urb->context;
|
||||
int retval;
|
||||
int x, y;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ETIMEDOUT:
|
||||
/* this urb is timing out */
|
||||
dbg("%s - urb timed out - was the device unplugged?",
|
||||
__FUNCTION__);
|
||||
return;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d",
|
||||
__FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d",
|
||||
__FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (swap_xy) {
|
||||
y = TOUCHKIT_GET_X(touchkit->data);
|
||||
x = TOUCHKIT_GET_Y(touchkit->data);
|
||||
} else {
|
||||
x = TOUCHKIT_GET_X(touchkit->data);
|
||||
y = TOUCHKIT_GET_Y(touchkit->data);
|
||||
}
|
||||
|
||||
input_regs(&touchkit->input, regs);
|
||||
input_report_key(&touchkit->input, BTN_TOUCH,
|
||||
TOUCHKIT_GET_TOUCHED(touchkit->data));
|
||||
input_report_abs(&touchkit->input, ABS_X, x);
|
||||
input_report_abs(&touchkit->input, ABS_Y, y);
|
||||
input_sync(&touchkit->input);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err("%s - usb_submit_urb failed with result: %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
static int touchkit_open(struct input_dev *input)
|
||||
{
|
||||
struct touchkit_usb *touchkit = input->private;
|
||||
|
||||
if (touchkit->open++)
|
||||
return 0;
|
||||
|
||||
touchkit->irq->dev = touchkit->udev;
|
||||
|
||||
if (usb_submit_urb(touchkit->irq, GFP_ATOMIC)) {
|
||||
touchkit->open--;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void touchkit_close(struct input_dev *input)
|
||||
{
|
||||
struct touchkit_usb *touchkit = input->private;
|
||||
|
||||
if (!--touchkit->open)
|
||||
usb_kill_urb(touchkit->irq);
|
||||
}
|
||||
|
||||
static int touchkit_alloc_buffers(struct usb_device *udev,
|
||||
struct touchkit_usb *touchkit)
|
||||
{
|
||||
touchkit->data = usb_buffer_alloc(udev, TOUCHKIT_REPORT_DATA_SIZE,
|
||||
SLAB_ATOMIC, &touchkit->data_dma);
|
||||
|
||||
if (!touchkit->data)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void touchkit_free_buffers(struct usb_device *udev,
|
||||
struct touchkit_usb *touchkit)
|
||||
{
|
||||
if (touchkit->data)
|
||||
usb_buffer_free(udev, TOUCHKIT_REPORT_DATA_SIZE,
|
||||
touchkit->data, touchkit->data_dma);
|
||||
}
|
||||
|
||||
static int touchkit_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct touchkit_usb *touchkit;
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
char path[64];
|
||||
|
||||
interface = intf->cur_altsetting;
|
||||
endpoint = &interface->endpoint[0].desc;
|
||||
|
||||
touchkit = kmalloc(sizeof(struct touchkit_usb), GFP_KERNEL);
|
||||
if (!touchkit)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(touchkit, 0, sizeof(struct touchkit_usb));
|
||||
touchkit->udev = udev;
|
||||
|
||||
if (touchkit_alloc_buffers(udev, touchkit)) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
touchkit->input.private = touchkit;
|
||||
touchkit->input.open = touchkit_open;
|
||||
touchkit->input.close = touchkit_close;
|
||||
|
||||
usb_make_path(udev, path, 64);
|
||||
sprintf(touchkit->phys, "%s/input0", path);
|
||||
|
||||
touchkit->input.name = touchkit->name;
|
||||
touchkit->input.phys = touchkit->phys;
|
||||
touchkit->input.id.bustype = BUS_USB;
|
||||
touchkit->input.id.vendor = le16_to_cpu(udev->descriptor.idVendor);
|
||||
touchkit->input.id.product = le16_to_cpu(udev->descriptor.idProduct);
|
||||
touchkit->input.id.version = le16_to_cpu(udev->descriptor.bcdDevice);
|
||||
touchkit->input.dev = &intf->dev;
|
||||
|
||||
touchkit->input.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||
touchkit->input.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
|
||||
touchkit->input.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||
|
||||
/* Used to Scale Compensated Data */
|
||||
touchkit->input.absmin[ABS_X] = TOUCHKIT_MIN_XC;
|
||||
touchkit->input.absmax[ABS_X] = TOUCHKIT_MAX_XC;
|
||||
touchkit->input.absfuzz[ABS_X] = TOUCHKIT_XC_FUZZ;
|
||||
touchkit->input.absflat[ABS_X] = TOUCHKIT_XC_FLAT;
|
||||
touchkit->input.absmin[ABS_Y] = TOUCHKIT_MIN_YC;
|
||||
touchkit->input.absmax[ABS_Y] = TOUCHKIT_MAX_YC;
|
||||
touchkit->input.absfuzz[ABS_Y] = TOUCHKIT_YC_FUZZ;
|
||||
touchkit->input.absflat[ABS_Y] = TOUCHKIT_YC_FLAT;
|
||||
|
||||
if (udev->manufacturer)
|
||||
strcat(touchkit->name, udev->manufacturer);
|
||||
if (udev->product)
|
||||
sprintf(touchkit->name, "%s %s", touchkit->name, udev->product);
|
||||
|
||||
if (!strlen(touchkit->name))
|
||||
sprintf(touchkit->name, "USB Touchscreen %04x:%04x",
|
||||
touchkit->input.id.vendor, touchkit->input.id.product);
|
||||
|
||||
touchkit->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!touchkit->irq) {
|
||||
dbg("%s - usb_alloc_urb failed: touchkit->irq", __FUNCTION__);
|
||||
ret = -ENOMEM;
|
||||
goto out_free_buffers;
|
||||
}
|
||||
|
||||
usb_fill_int_urb(touchkit->irq, touchkit->udev,
|
||||
usb_rcvintpipe(touchkit->udev, 0x81),
|
||||
touchkit->data, TOUCHKIT_REPORT_DATA_SIZE,
|
||||
touchkit_irq, touchkit, endpoint->bInterval);
|
||||
|
||||
input_register_device(&touchkit->input);
|
||||
|
||||
printk(KERN_INFO "input: %s on %s\n", touchkit->name, path);
|
||||
usb_set_intfdata(intf, touchkit);
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_buffers:
|
||||
touchkit_free_buffers(udev, touchkit);
|
||||
out_free:
|
||||
kfree(touchkit);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void touchkit_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct touchkit_usb *touchkit = usb_get_intfdata(intf);
|
||||
|
||||
dbg("%s - called", __FUNCTION__);
|
||||
|
||||
if (!touchkit)
|
||||
return;
|
||||
|
||||
dbg("%s - touchkit is initialized, cleaning up", __FUNCTION__);
|
||||
usb_set_intfdata(intf, NULL);
|
||||
input_unregister_device(&touchkit->input);
|
||||
usb_kill_urb(touchkit->irq);
|
||||
usb_free_urb(touchkit->irq);
|
||||
touchkit_free_buffers(interface_to_usbdev(intf), touchkit);
|
||||
kfree(touchkit);
|
||||
}
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, touchkit_devices);
|
||||
|
||||
static struct usb_driver touchkit_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "touchkitusb",
|
||||
.probe = touchkit_probe,
|
||||
.disconnect = touchkit_disconnect,
|
||||
.id_table = touchkit_devices,
|
||||
};
|
||||
|
||||
static int __init touchkit_init(void)
|
||||
{
|
||||
return usb_register(&touchkit_driver);
|
||||
}
|
||||
|
||||
static void __exit touchkit_cleanup(void)
|
||||
{
|
||||
usb_deregister(&touchkit_driver);
|
||||
}
|
||||
|
||||
module_init(touchkit_init);
|
||||
module_exit(touchkit_cleanup);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
370
drivers/usb/input/usbkbd.c
Normal file
370
drivers/usb/input/usbkbd.c
Normal file
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* $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 <vojtech@ucw.cz>, or by paper mail:
|
||||
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
*/
|
||||
#define DRIVER_VERSION ""
|
||||
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
|
||||
#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];
|
||||
int open;
|
||||
|
||||
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 pt_regs *regs)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
input_regs(&kbd->dev, regs);
|
||||
|
||||
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, SLAB_ATOMIC);
|
||||
if (i)
|
||||
err ("can't resubmit intr, %s-%s/input0, status %d",
|
||||
kbd->usbdev->bus->bus_name,
|
||||
kbd->usbdev->devpath, i);
|
||||
}
|
||||
|
||||
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 pt_regs *regs)
|
||||
{
|
||||
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;
|
||||
|
||||
if (kbd->open++)
|
||||
return 0;
|
||||
|
||||
kbd->irq->dev = kbd->usbdev;
|
||||
if (usb_submit_urb(kbd->irq, GFP_KERNEL)) {
|
||||
kbd->open--;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_kbd_close(struct input_dev *dev)
|
||||
{
|
||||
struct usb_kbd *kbd = dev->private;
|
||||
|
||||
if (!--kbd->open)
|
||||
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, SLAB_ATOMIC, &kbd->new_dma)))
|
||||
return -1;
|
||||
if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), SLAB_ATOMIC, &kbd->cr_dma)))
|
||||
return -1;
|
||||
if (!(kbd->leds = usb_buffer_alloc(dev, 1, SLAB_ATOMIC, &kbd->leds_dma)))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)
|
||||
{
|
||||
if (kbd->irq)
|
||||
usb_free_urb(kbd->irq);
|
||||
if (kbd->led)
|
||||
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;
|
||||
int i, pipe, maxp;
|
||||
char path[64];
|
||||
|
||||
interface = iface->cur_altsetting;
|
||||
|
||||
if (interface->desc.bNumEndpoints != 1)
|
||||
return -ENODEV;
|
||||
|
||||
endpoint = &interface->endpoint[0].desc;
|
||||
if (!(endpoint->bEndpointAddress & 0x80))
|
||||
return -ENODEV;
|
||||
if ((endpoint->bmAttributes & 3) != 3)
|
||||
return -ENODEV;
|
||||
|
||||
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
|
||||
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
|
||||
|
||||
if (!(kbd = kmalloc(sizeof(struct usb_kbd), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
memset(kbd, 0, sizeof(struct usb_kbd));
|
||||
|
||||
if (usb_kbd_alloc_mem(dev, kbd)) {
|
||||
usb_kbd_free_mem(dev, kbd);
|
||||
kfree(kbd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
kbd->usbdev = dev;
|
||||
|
||||
kbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
|
||||
kbd->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], kbd->dev.keybit);
|
||||
clear_bit(0, kbd->dev.keybit);
|
||||
|
||||
kbd->dev.private = kbd;
|
||||
kbd->dev.event = usb_kbd_event;
|
||||
kbd->dev.open = usb_kbd_open;
|
||||
kbd->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_make_path(dev, path, 64);
|
||||
sprintf(kbd->phys, "%s/input0", path);
|
||||
|
||||
kbd->dev.name = kbd->name;
|
||||
kbd->dev.phys = kbd->phys;
|
||||
kbd->dev.id.bustype = BUS_USB;
|
||||
kbd->dev.id.vendor = le16_to_cpu(dev->descriptor.idVendor);
|
||||
kbd->dev.id.product = le16_to_cpu(dev->descriptor.idProduct);
|
||||
kbd->dev.id.version = le16_to_cpu(dev->descriptor.bcdDevice);
|
||||
kbd->dev.dev = &iface->dev;
|
||||
|
||||
if (dev->manufacturer)
|
||||
strcat(kbd->name, dev->manufacturer);
|
||||
if (dev->product)
|
||||
sprintf(kbd->name, "%s %s", kbd->name, dev->product);
|
||||
|
||||
if (!strlen(kbd->name))
|
||||
sprintf(kbd->name, "USB HIDBP Keyboard %04x:%04x",
|
||||
kbd->dev.id.vendor, kbd->dev.id.product);
|
||||
|
||||
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);
|
||||
|
||||
printk(KERN_INFO "input: %s on %s\n", kbd->name, path);
|
||||
|
||||
usb_set_intfdata(iface, kbd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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(3, 1, 1) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);
|
||||
|
||||
static struct usb_driver usb_kbd_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.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);
|
252
drivers/usb/input/usbmouse.c
Normal file
252
drivers/usb/input/usbmouse.c
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* $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 <vojtech@ucw.cz>, or by paper mail:
|
||||
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
*/
|
||||
#define DRIVER_VERSION "v1.6"
|
||||
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
|
||||
#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;
|
||||
int open;
|
||||
|
||||
signed char *data;
|
||||
dma_addr_t data_dma;
|
||||
};
|
||||
|
||||
static void usb_mouse_irq(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
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_regs(dev, regs);
|
||||
|
||||
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, SLAB_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;
|
||||
|
||||
if (mouse->open++)
|
||||
return 0;
|
||||
|
||||
mouse->irq->dev = mouse->usbdev;
|
||||
if (usb_submit_urb(mouse->irq, GFP_KERNEL)) {
|
||||
mouse->open--;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_mouse_close(struct input_dev *dev)
|
||||
{
|
||||
struct usb_mouse *mouse = dev->private;
|
||||
|
||||
if (!--mouse->open)
|
||||
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;
|
||||
int pipe, maxp;
|
||||
char path[64];
|
||||
|
||||
interface = intf->cur_altsetting;
|
||||
|
||||
if (interface->desc.bNumEndpoints != 1)
|
||||
return -ENODEV;
|
||||
|
||||
endpoint = &interface->endpoint[0].desc;
|
||||
if (!(endpoint->bEndpointAddress & 0x80))
|
||||
return -ENODEV;
|
||||
if ((endpoint->bmAttributes & 3) != 3)
|
||||
return -ENODEV;
|
||||
|
||||
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
|
||||
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
|
||||
|
||||
if (!(mouse = kmalloc(sizeof(struct usb_mouse), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
memset(mouse, 0, sizeof(struct usb_mouse));
|
||||
|
||||
mouse->data = usb_buffer_alloc(dev, 8, SLAB_ATOMIC, &mouse->data_dma);
|
||||
if (!mouse->data) {
|
||||
kfree(mouse);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mouse->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!mouse->irq) {
|
||||
usb_buffer_free(dev, 8, mouse->data, mouse->data_dma);
|
||||
kfree(mouse);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mouse->usbdev = dev;
|
||||
|
||||
mouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
|
||||
mouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
|
||||
mouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
|
||||
mouse->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
|
||||
mouse->dev.relbit[0] |= BIT(REL_WHEEL);
|
||||
|
||||
mouse->dev.private = mouse;
|
||||
mouse->dev.open = usb_mouse_open;
|
||||
mouse->dev.close = usb_mouse_close;
|
||||
|
||||
usb_make_path(dev, path, 64);
|
||||
sprintf(mouse->phys, "%s/input0", path);
|
||||
|
||||
mouse->dev.name = mouse->name;
|
||||
mouse->dev.phys = mouse->phys;
|
||||
mouse->dev.id.bustype = BUS_USB;
|
||||
mouse->dev.id.vendor = le16_to_cpu(dev->descriptor.idVendor);
|
||||
mouse->dev.id.product = le16_to_cpu(dev->descriptor.idProduct);
|
||||
mouse->dev.id.version = le16_to_cpu(dev->descriptor.bcdDevice);
|
||||
mouse->dev.dev = &intf->dev;
|
||||
|
||||
if (dev->manufacturer)
|
||||
strcat(mouse->name, dev->manufacturer);
|
||||
if (dev->product)
|
||||
sprintf(mouse->name, "%s %s", mouse->name, dev->product);
|
||||
|
||||
if (!strlen(mouse->name))
|
||||
sprintf(mouse->name, "USB HIDBP Mouse %04x:%04x",
|
||||
mouse->dev.id.vendor, mouse->dev.id.product);
|
||||
|
||||
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);
|
||||
printk(KERN_INFO "input: %s on %s\n", mouse->name, path);
|
||||
|
||||
usb_set_intfdata(intf, mouse);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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(3, 1, 2) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
|
||||
|
||||
static struct usb_driver usb_mouse_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.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);
|
951
drivers/usb/input/wacom.c
Normal file
951
drivers/usb/input/wacom.c
Normal file
@@ -0,0 +1,951 @@
|
||||
/*
|
||||
* USB Wacom Graphire and Wacom Intuos tablet support
|
||||
*
|
||||
* Copyright (c) 2000-2004 Vojtech Pavlik <vojtech@ucw.cz>
|
||||
* Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk>
|
||||
* Copyright (c) 2000 Clifford Wolf <clifford@clifford.at>
|
||||
* Copyright (c) 2000 Sam Mosel <sam.mosel@computer.org>
|
||||
* Copyright (c) 2000 James E. Blair <corvus@gnu.org>
|
||||
* Copyright (c) 2000 Daniel Egger <egger@suse.de>
|
||||
* Copyright (c) 2001 Frederic Lepied <flepied@mandrakesoft.com>
|
||||
* Copyright (c) 2004 Panagiotis Issaris <panagiotis.issaris@mech.kuleuven.ac.be>
|
||||
* Copyright (c) 2002-2004 Ping Cheng <pingc@wacom.com>
|
||||
*
|
||||
* ChangeLog:
|
||||
* v0.1 (vp) - Initial release
|
||||
* v0.2 (aba) - Support for all buttons / combinations
|
||||
* v0.3 (vp) - Support for Intuos added
|
||||
* v0.4 (sm) - Support for more Intuos models, menustrip
|
||||
* relative mode, proximity.
|
||||
* v0.5 (vp) - Big cleanup, nifty features removed,
|
||||
* they belong in userspace
|
||||
* v1.8 (vp) - Submit URB only when operating, moved to CVS,
|
||||
* use input_report_key instead of report_btn and
|
||||
* other cleanups
|
||||
* v1.11 (vp) - Add URB ->dev setting for new kernels
|
||||
* v1.11 (jb) - Add support for the 4D Mouse & Lens
|
||||
* v1.12 (de) - Add support for two more inking pen IDs
|
||||
* v1.14 (vp) - Use new USB device id probing scheme.
|
||||
* Fix Wacom Graphire mouse wheel
|
||||
* v1.18 (vp) - Fix mouse wheel direction
|
||||
* Make mouse relative
|
||||
* v1.20 (fl) - Report tool id for Intuos devices
|
||||
* - Multi tools support
|
||||
* - Corrected Intuos protocol decoding (airbrush, 4D mouse, lens cursor...)
|
||||
* - Add PL models support
|
||||
* - Fix Wacom Graphire mouse wheel again
|
||||
* v1.21 (vp) - Removed protocol descriptions
|
||||
* - Added MISC_SERIAL for tool serial numbers
|
||||
* (gb) - Identify version on module load.
|
||||
* v1.21.1 (fl) - added Graphire2 support
|
||||
* v1.21.2 (fl) - added Intuos2 support
|
||||
* - added all the PL ids
|
||||
* v1.21.3 (fl) - added another eraser id from Neil Okamoto
|
||||
* - added smooth filter for Graphire from Peri Hankey
|
||||
* - added PenPartner support from Olaf van Es
|
||||
* - new tool ids from Ole Martin Bjoerndalen
|
||||
* v1.29 (pc) - Add support for more tablets
|
||||
* - Fix pressure reporting
|
||||
* v1.30 (vp) - Merge 2.4 and 2.5 drivers
|
||||
* - Since 2.5 now has input_sync(), remove MSC_SERIAL abuse
|
||||
* - Cleanups here and there
|
||||
* v1.30.1 (pi) - Added Graphire3 support
|
||||
* v1.40 (pc) - Add support for several new devices, fix eraser reporting, ...
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
*/
|
||||
#define DRIVER_VERSION "v1.40"
|
||||
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
|
||||
#define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver"
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE(DRIVER_LICENSE);
|
||||
|
||||
#define USB_VENDOR_ID_WACOM 0x056a
|
||||
|
||||
struct wacom_features {
|
||||
char *name;
|
||||
int pktlen;
|
||||
int x_max;
|
||||
int y_max;
|
||||
int pressure_max;
|
||||
int distance_max;
|
||||
int type;
|
||||
usb_complete_t irq;
|
||||
};
|
||||
|
||||
struct wacom {
|
||||
signed char *data;
|
||||
dma_addr_t data_dma;
|
||||
struct input_dev dev;
|
||||
struct usb_device *usbdev;
|
||||
struct urb *irq;
|
||||
struct wacom_features *features;
|
||||
int tool[2];
|
||||
int open;
|
||||
__u32 serial[2];
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
#define USB_REQ_SET_REPORT 0x09
|
||||
static int usb_set_report(struct usb_interface *intf, unsigned char type,
|
||||
unsigned char id, void *buf, int size)
|
||||
{
|
||||
return usb_control_msg(interface_to_usbdev(intf),
|
||||
usb_sndctrlpipe(interface_to_usbdev(intf), 0),
|
||||
USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
(type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
|
||||
buf, size, 1000);
|
||||
}
|
||||
|
||||
static void wacom_pl_irq(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct wacom *wacom = urb->context;
|
||||
unsigned char *data = wacom->data;
|
||||
struct input_dev *dev = &wacom->dev;
|
||||
int prox, pressure;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (data[0] != 2) {
|
||||
dbg("wacom_pl_irq: received unknown report #%d", data[0]);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
prox = data[1] & 0x40;
|
||||
|
||||
input_regs(dev, regs);
|
||||
|
||||
if (prox) {
|
||||
|
||||
pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
|
||||
if (wacom->features->pressure_max > 255)
|
||||
pressure = (pressure << 1) | ((data[4] >> 6) & 1);
|
||||
pressure += (wacom->features->pressure_max + 1) / 2;
|
||||
|
||||
/*
|
||||
* if going from out of proximity into proximity select between the eraser
|
||||
* and the pen based on the state of the stylus2 button, choose eraser if
|
||||
* pressed else choose pen. if not a proximity change from out to in, send
|
||||
* an out of proximity for previous tool then a in for new tool.
|
||||
*/
|
||||
if (!wacom->tool[0]) {
|
||||
/* Going into proximity select tool */
|
||||
wacom->tool[1] = (data[4] & 0x20)? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
|
||||
}
|
||||
else {
|
||||
/* was entered with stylus2 pressed */
|
||||
if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20) ) {
|
||||
/* report out proximity for previous tool */
|
||||
input_report_key(dev, wacom->tool[1], 0);
|
||||
input_sync(dev);
|
||||
wacom->tool[1] = BTN_TOOL_PEN;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
if (wacom->tool[1] != BTN_TOOL_RUBBER) {
|
||||
/* Unknown tool selected default to pen tool */
|
||||
wacom->tool[1] = BTN_TOOL_PEN;
|
||||
}
|
||||
input_report_key(dev, wacom->tool[1], prox); /* report in proximity for tool */
|
||||
input_report_abs(dev, ABS_X, data[3] | ((__u32)data[2] << 7) | ((__u32)(data[1] & 0x03) << 14));
|
||||
input_report_abs(dev, ABS_Y, data[6] | ((__u32)data[5] << 7) | ((__u32)(data[4] & 0x03) << 14));
|
||||
input_report_abs(dev, ABS_PRESSURE, pressure);
|
||||
|
||||
input_report_key(dev, BTN_TOUCH, data[4] & 0x08);
|
||||
input_report_key(dev, BTN_STYLUS, data[4] & 0x10);
|
||||
/* Only allow the stylus2 button to be reported for the pen tool. */
|
||||
input_report_key(dev, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20));
|
||||
}
|
||||
else {
|
||||
/* report proximity-out of a (valid) tool */
|
||||
if (wacom->tool[1] != BTN_TOOL_RUBBER) {
|
||||
/* Unknown tool selected default to pen tool */
|
||||
wacom->tool[1] = BTN_TOOL_PEN;
|
||||
}
|
||||
input_report_key(dev, wacom->tool[1], prox);
|
||||
}
|
||||
|
||||
wacom->tool[0] = prox; /* Save proximity state */
|
||||
input_sync(dev);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err ("%s - usb_submit_urb failed with result %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
static void wacom_ptu_irq(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct wacom *wacom = urb->context;
|
||||
unsigned char *data = wacom->data;
|
||||
struct input_dev *dev = &wacom->dev;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (data[0] != 2)
|
||||
{
|
||||
printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
input_regs(dev, regs);
|
||||
if (data[1] & 0x04)
|
||||
{
|
||||
input_report_key(dev, BTN_TOOL_RUBBER, data[1] & 0x20);
|
||||
input_report_key(dev, BTN_TOUCH, data[1] & 0x08);
|
||||
}
|
||||
else
|
||||
{
|
||||
input_report_key(dev, BTN_TOOL_PEN, data[1] & 0x20);
|
||||
input_report_key(dev, BTN_TOUCH, data[1] & 0x01);
|
||||
}
|
||||
input_report_abs(dev, ABS_X, le16_to_cpu(*(__le16 *) &data[2]));
|
||||
input_report_abs(dev, ABS_Y, le16_to_cpu(*(__le16 *) &data[4]));
|
||||
input_report_abs(dev, ABS_PRESSURE, le16_to_cpu(*(__le16 *) &data[6]));
|
||||
input_report_key(dev, BTN_STYLUS, data[1] & 0x02);
|
||||
input_report_key(dev, BTN_STYLUS2, data[1] & 0x10);
|
||||
|
||||
input_sync(dev);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err ("%s - usb_submit_urb failed with result %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
static void wacom_penpartner_irq(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct wacom *wacom = urb->context;
|
||||
unsigned char *data = wacom->data;
|
||||
struct input_dev *dev = &wacom->dev;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (data[0] != 2) {
|
||||
printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
input_regs(dev, regs);
|
||||
input_report_key(dev, BTN_TOOL_PEN, 1);
|
||||
input_report_abs(dev, ABS_X, le16_to_cpu(*(__le16 *) &data[1]));
|
||||
input_report_abs(dev, ABS_Y, le16_to_cpu(*(__le16 *) &data[3]));
|
||||
input_report_abs(dev, ABS_PRESSURE, (signed char)data[6] + 127);
|
||||
input_report_key(dev, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20));
|
||||
input_report_key(dev, BTN_STYLUS, (data[5] & 0x40));
|
||||
input_sync(dev);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err ("%s - usb_submit_urb failed with result %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
static void wacom_graphire_irq(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct wacom *wacom = urb->context;
|
||||
unsigned char *data = wacom->data;
|
||||
struct input_dev *dev = &wacom->dev;
|
||||
int x, y;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (data[0] != 2) {
|
||||
dbg("wacom_graphire_irq: received unknown report #%d", data[0]);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
x = le16_to_cpu(*(__le16 *) &data[2]);
|
||||
y = le16_to_cpu(*(__le16 *) &data[4]);
|
||||
|
||||
input_regs(dev, regs);
|
||||
|
||||
switch ((data[1] >> 5) & 3) {
|
||||
|
||||
case 0: /* Pen */
|
||||
input_report_key(dev, BTN_TOOL_PEN, data[1] & 0x80);
|
||||
break;
|
||||
|
||||
case 1: /* Rubber */
|
||||
input_report_key(dev, BTN_TOOL_RUBBER, data[1] & 0x80);
|
||||
break;
|
||||
|
||||
case 2: /* Mouse with wheel */
|
||||
input_report_key(dev, BTN_MIDDLE, data[1] & 0x04);
|
||||
input_report_rel(dev, REL_WHEEL, (signed char) data[6]);
|
||||
/* fall through */
|
||||
|
||||
case 3: /* Mouse without wheel */
|
||||
input_report_key(dev, BTN_TOOL_MOUSE, data[7] > 24);
|
||||
input_report_key(dev, BTN_LEFT, data[1] & 0x01);
|
||||
input_report_key(dev, BTN_RIGHT, data[1] & 0x02);
|
||||
input_report_abs(dev, ABS_DISTANCE, data[7]);
|
||||
|
||||
input_report_abs(dev, ABS_X, x);
|
||||
input_report_abs(dev, ABS_Y, y);
|
||||
|
||||
input_sync(dev);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (data[1] & 0x80) {
|
||||
input_report_abs(dev, ABS_X, x);
|
||||
input_report_abs(dev, ABS_Y, y);
|
||||
}
|
||||
|
||||
input_report_abs(dev, ABS_PRESSURE, le16_to_cpu(*(__le16 *) &data[6]));
|
||||
input_report_key(dev, BTN_TOUCH, data[1] & 0x01);
|
||||
input_report_key(dev, BTN_STYLUS, data[1] & 0x02);
|
||||
input_report_key(dev, BTN_STYLUS2, data[1] & 0x04);
|
||||
|
||||
input_sync(dev);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err ("%s - usb_submit_urb failed with result %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
static int wacom_intuos_inout(struct urb *urb)
|
||||
{
|
||||
struct wacom *wacom = urb->context;
|
||||
unsigned char *data = wacom->data;
|
||||
struct input_dev *dev = &wacom->dev;
|
||||
int idx;
|
||||
|
||||
/* tool number */
|
||||
idx = data[1] & 0x01;
|
||||
|
||||
/* Enter report */
|
||||
if ((data[1] & 0xfc) == 0xc0)
|
||||
{
|
||||
/* serial number of the tool */
|
||||
wacom->serial[idx] = ((__u32)(data[3] & 0x0f) << 28) +
|
||||
((__u32)data[4] << 20) + ((__u32)data[5] << 12) +
|
||||
((__u32)data[6] << 4) + (data[7] >> 4);
|
||||
|
||||
switch (((__u32)data[2] << 4) | (data[3] >> 4)) {
|
||||
case 0x812: /* Inking pen */
|
||||
case 0x801: /* Intuos3 Inking pen */
|
||||
case 0x012:
|
||||
wacom->tool[idx] = BTN_TOOL_PENCIL;
|
||||
break;
|
||||
case 0x822: /* Pen */
|
||||
case 0x842:
|
||||
case 0x852:
|
||||
case 0x823: /* Intuos3 Grip Pen */
|
||||
case 0x813: /* Intuos3 Classic Pen */
|
||||
case 0x885: /* Intuos3 Marker Pen */
|
||||
case 0x022:
|
||||
wacom->tool[idx] = BTN_TOOL_PEN;
|
||||
break;
|
||||
case 0x832: /* Stroke pen */
|
||||
case 0x032:
|
||||
wacom->tool[idx] = BTN_TOOL_BRUSH;
|
||||
break;
|
||||
case 0x007: /* Mouse 4D and 2D */
|
||||
case 0x09c:
|
||||
case 0x094:
|
||||
case 0x017: /* Intuos3 2D Mouse */
|
||||
wacom->tool[idx] = BTN_TOOL_MOUSE;
|
||||
break;
|
||||
case 0x096: /* Lens cursor */
|
||||
case 0x097: /* Intuos3 Lens cursor */
|
||||
wacom->tool[idx] = BTN_TOOL_LENS;
|
||||
break;
|
||||
case 0x82a: /* Eraser */
|
||||
case 0x85a:
|
||||
case 0x91a:
|
||||
case 0xd1a:
|
||||
case 0x0fa:
|
||||
case 0x82b: /* Intuos3 Grip Pen Eraser */
|
||||
case 0x81b: /* Intuos3 Classic Pen Eraser */
|
||||
case 0x91b: /* Intuos3 Airbrush Eraser */
|
||||
wacom->tool[idx] = BTN_TOOL_RUBBER;
|
||||
break;
|
||||
case 0xd12:
|
||||
case 0x912:
|
||||
case 0x112:
|
||||
case 0x913: /* Intuos3 Airbrush */
|
||||
wacom->tool[idx] = BTN_TOOL_AIRBRUSH;
|
||||
break; /* Airbrush */
|
||||
default: /* Unknown tool */
|
||||
wacom->tool[idx] = BTN_TOOL_PEN;
|
||||
}
|
||||
input_report_key(dev, wacom->tool[idx], 1);
|
||||
input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
|
||||
input_sync(dev);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Exit report */
|
||||
if ((data[1] & 0xfe) == 0x80) {
|
||||
input_report_key(dev, wacom->tool[idx], 0);
|
||||
input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
|
||||
input_sync(dev);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wacom_intuos_general(struct urb *urb)
|
||||
{
|
||||
struct wacom *wacom = urb->context;
|
||||
unsigned char *data = wacom->data;
|
||||
struct input_dev *dev = &wacom->dev;
|
||||
unsigned int t;
|
||||
|
||||
/* general pen packet */
|
||||
if ((data[1] & 0xb8) == 0xa0)
|
||||
{
|
||||
t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3);
|
||||
input_report_abs(dev, ABS_PRESSURE, t);
|
||||
input_report_abs(dev, ABS_TILT_X,
|
||||
((data[7] << 1) & 0x7e) | (data[8] >> 7));
|
||||
input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
|
||||
input_report_key(dev, BTN_STYLUS, data[1] & 2);
|
||||
input_report_key(dev, BTN_STYLUS2, data[1] & 4);
|
||||
input_report_key(dev, BTN_TOUCH, t > 10);
|
||||
}
|
||||
|
||||
/* airbrush second packet */
|
||||
if ((data[1] & 0xbc) == 0xb4)
|
||||
{
|
||||
input_report_abs(dev, ABS_WHEEL,
|
||||
((__u32)data[6] << 2) | ((data[7] >> 6) & 3));
|
||||
input_report_abs(dev, ABS_TILT_X,
|
||||
((data[7] << 1) & 0x7e) | (data[8] >> 7));
|
||||
input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void wacom_intuos_irq(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct wacom *wacom = urb->context;
|
||||
unsigned char *data = wacom->data;
|
||||
struct input_dev *dev = &wacom->dev;
|
||||
unsigned int t;
|
||||
int idx;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (data[0] != 2 && data[0] != 5 && data[0] != 6) {
|
||||
dbg("wacom_intuos_irq: received unknown report #%d", data[0]);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
input_regs(dev, regs);
|
||||
|
||||
/* tool number */
|
||||
idx = data[1] & 0x01;
|
||||
|
||||
/* process in/out prox events */
|
||||
if (wacom_intuos_inout(urb)) goto exit;
|
||||
|
||||
input_report_abs(dev, ABS_X, be16_to_cpu(*(__be16 *) &data[2]));
|
||||
input_report_abs(dev, ABS_Y, be16_to_cpu(*(__be16 *) &data[4]));
|
||||
input_report_abs(dev, ABS_DISTANCE, data[9]);
|
||||
|
||||
/* process general packets */
|
||||
wacom_intuos_general(urb);
|
||||
|
||||
if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) { /* 4D mouse or Lens cursor packets */
|
||||
|
||||
if (data[1] & 0x02) { /* Rotation packet */
|
||||
|
||||
t = ((__u32)data[6] << 3) | ((data[7] >> 5) & 7);
|
||||
input_report_abs(dev, ABS_RZ, (data[7] & 0x20) ? ((t - 1) / 2) : -t / 2);
|
||||
|
||||
} else {
|
||||
|
||||
if ((data[1] & 0x10) == 0) { /* 4D mouse packets */
|
||||
|
||||
input_report_key(dev, BTN_LEFT, data[8] & 0x01);
|
||||
input_report_key(dev, BTN_MIDDLE, data[8] & 0x02);
|
||||
input_report_key(dev, BTN_RIGHT, data[8] & 0x04);
|
||||
|
||||
input_report_key(dev, BTN_SIDE, data[8] & 0x20);
|
||||
input_report_key(dev, BTN_EXTRA, data[8] & 0x10);
|
||||
t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3);
|
||||
input_report_abs(dev, ABS_THROTTLE, (data[8] & 0x08) ? -t : t);
|
||||
|
||||
} else {
|
||||
if (wacom->tool[idx] == BTN_TOOL_MOUSE) { /* 2D mouse packets */
|
||||
input_report_key(dev, BTN_LEFT, data[8] & 0x04);
|
||||
input_report_key(dev, BTN_MIDDLE, data[8] & 0x08);
|
||||
input_report_key(dev, BTN_RIGHT, data[8] & 0x10);
|
||||
input_report_rel(dev, REL_WHEEL,
|
||||
(-(__u32)(data[8] & 0x01) + (__u32)((data[8] & 0x02) >> 1)));
|
||||
}
|
||||
else { /* Lens cursor packets */
|
||||
input_report_key(dev, BTN_LEFT, data[8] & 0x01);
|
||||
input_report_key(dev, BTN_MIDDLE, data[8] & 0x02);
|
||||
input_report_key(dev, BTN_RIGHT, data[8] & 0x04);
|
||||
input_report_key(dev, BTN_SIDE, data[8] & 0x10);
|
||||
input_report_key(dev, BTN_EXTRA, data[8] & 0x08);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input_report_key(dev, wacom->tool[idx], 1);
|
||||
input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
|
||||
input_sync(dev);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err ("%s - usb_submit_urb failed with result %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
static void wacom_intuos3_irq(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct wacom *wacom = urb->context;
|
||||
unsigned char *data = wacom->data;
|
||||
struct input_dev *dev = &wacom->dev;
|
||||
unsigned int t;
|
||||
int idx, retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* check for valid report */
|
||||
if (data[0] != 2 && data[0] != 5 && data[0] != 12)
|
||||
{
|
||||
printk(KERN_INFO "wacom_intuos3_irq: received unknown report #%d\n", data[0]);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
input_regs(dev, regs);
|
||||
|
||||
/* tool index is always 0 here since there is no dual input tool */
|
||||
idx = data[1] & 0x01;
|
||||
|
||||
/* pad packets. Works as a second tool and is always in prox */
|
||||
if (data[0] == 12)
|
||||
{
|
||||
/* initiate the pad as a device */
|
||||
if (wacom->tool[1] != BTN_TOOL_FINGER)
|
||||
{
|
||||
wacom->tool[1] = BTN_TOOL_FINGER;
|
||||
input_report_key(dev, wacom->tool[1], 1);
|
||||
}
|
||||
input_report_key(dev, BTN_0, (data[5] & 0x01));
|
||||
input_report_key(dev, BTN_1, (data[5] & 0x02));
|
||||
input_report_key(dev, BTN_2, (data[5] & 0x04));
|
||||
input_report_key(dev, BTN_3, (data[5] & 0x08));
|
||||
input_report_key(dev, BTN_4, (data[6] & 0x01));
|
||||
input_report_key(dev, BTN_5, (data[6] & 0x02));
|
||||
input_report_key(dev, BTN_6, (data[6] & 0x04));
|
||||
input_report_key(dev, BTN_7, (data[6] & 0x08));
|
||||
input_report_abs(dev, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]);
|
||||
input_report_abs(dev, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
|
||||
input_event(dev, EV_MSC, MSC_SERIAL, 0xffffffff);
|
||||
input_sync(dev);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* process in/out prox events */
|
||||
if (wacom_intuos_inout(urb)) goto exit;
|
||||
|
||||
input_report_abs(dev, ABS_X, ((__u32)data[2] << 9) | ((__u32)data[3] << 1) | ((data[9] >> 1) & 1));
|
||||
input_report_abs(dev, ABS_Y, ((__u32)data[4] << 9) | ((__u32)data[5] << 1) | (data[9] & 1));
|
||||
input_report_abs(dev, ABS_DISTANCE, ((data[9] >> 2) & 0x3f));
|
||||
|
||||
/* process general packets */
|
||||
wacom_intuos_general(urb);
|
||||
|
||||
if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0)
|
||||
{
|
||||
/* Marker pen rotation packet. Reported as wheel due to valuator limitation */
|
||||
if (data[1] & 0x02)
|
||||
{
|
||||
t = ((__u32)data[6] << 3) | ((data[7] >> 5) & 7);
|
||||
t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) :
|
||||
((t-1) / 2 + 450)) : (450 - t / 2) ;
|
||||
input_report_abs(dev, ABS_WHEEL, t);
|
||||
}
|
||||
|
||||
/* 2D mouse packets */
|
||||
if (wacom->tool[idx] == BTN_TOOL_MOUSE)
|
||||
{
|
||||
input_report_key(dev, BTN_LEFT, data[8] & 0x04);
|
||||
input_report_key(dev, BTN_MIDDLE, data[8] & 0x08);
|
||||
input_report_key(dev, BTN_RIGHT, data[8] & 0x10);
|
||||
input_report_key(dev, BTN_SIDE, data[8] & 0x40);
|
||||
input_report_key(dev, BTN_EXTRA, data[8] & 0x20);
|
||||
/* mouse wheel is positive when rolled backwards */
|
||||
input_report_rel(dev, REL_WHEEL, ((__u32)((data[8] & 0x02) >> 1)
|
||||
- (__u32)(data[8] & 0x01)));
|
||||
}
|
||||
}
|
||||
|
||||
input_report_key(dev, wacom->tool[idx], 1);
|
||||
input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
|
||||
input_sync(dev);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err ("%s - usb_submit_urb failed with result %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
static struct wacom_features wacom_features[] = {
|
||||
{ "Wacom Penpartner", 7, 5040, 3780, 255, 32, 0, wacom_penpartner_irq },
|
||||
{ "Wacom Graphire", 8, 10206, 7422, 511, 32, 1, wacom_graphire_irq },
|
||||
{ "Wacom Graphire2 4x5", 8, 10206, 7422, 511, 32, 1, wacom_graphire_irq },
|
||||
{ "Wacom Graphire2 5x7", 8, 13918, 10206, 511, 32, 1, wacom_graphire_irq },
|
||||
{ "Wacom Graphire3", 8, 10208, 7424, 511, 32, 1, wacom_graphire_irq },
|
||||
{ "Wacom Graphire3 6x8", 8, 16704, 12064, 511, 32, 1, wacom_graphire_irq },
|
||||
{ "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 15, 2, wacom_intuos_irq },
|
||||
{ "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 15, 2, wacom_intuos_irq },
|
||||
{ "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 15, 2, wacom_intuos_irq },
|
||||
{ "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 15, 2, wacom_intuos_irq },
|
||||
{ "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 15, 2, wacom_intuos_irq },
|
||||
{ "Wacom PL400", 8, 5408, 4056, 255, 32, 3, wacom_pl_irq },
|
||||
{ "Wacom PL500", 8, 6144, 4608, 255, 32, 3, wacom_pl_irq },
|
||||
{ "Wacom PL600", 8, 6126, 4604, 255, 32, 3, wacom_pl_irq },
|
||||
{ "Wacom PL600SX", 8, 6260, 5016, 255, 32, 3, wacom_pl_irq },
|
||||
{ "Wacom PL550", 8, 6144, 4608, 511, 32, 3, wacom_pl_irq },
|
||||
{ "Wacom PL800", 8, 7220, 5780, 511, 32, 3, wacom_pl_irq },
|
||||
{ "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 15, 2, wacom_intuos_irq },
|
||||
{ "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, 2, wacom_intuos_irq },
|
||||
{ "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 15, 2, wacom_intuos_irq },
|
||||
{ "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 15, 2, wacom_intuos_irq },
|
||||
{ "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 15, 2, wacom_intuos_irq },
|
||||
{ "Wacom Volito", 8, 5104, 3712, 511, 32, 1, wacom_graphire_irq },
|
||||
{ "Wacom Cintiq Partner",8, 20480, 15360, 511, 32, 3, wacom_ptu_irq },
|
||||
{ "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 15, 4, wacom_intuos3_irq },
|
||||
{ "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 15, 4, wacom_intuos3_irq },
|
||||
{ "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 15, 4, wacom_intuos3_irq },
|
||||
{ "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, 2, wacom_intuos_irq },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct usb_device_id wacom_ids[] = {
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x00) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x10) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x11) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x12) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x13) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x14) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x20) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x21) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x22) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x23) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x24) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x30) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x31) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x32) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x33) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x34) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x35) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x41) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x42) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x43) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x44) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x45) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x60) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x03) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB0) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB1) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB2) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, wacom_ids);
|
||||
|
||||
static int wacom_open(struct input_dev *dev)
|
||||
{
|
||||
struct wacom *wacom = dev->private;
|
||||
|
||||
if (wacom->open++)
|
||||
return 0;
|
||||
|
||||
wacom->irq->dev = wacom->usbdev;
|
||||
if (usb_submit_urb(wacom->irq, GFP_KERNEL)) {
|
||||
wacom->open--;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wacom_close(struct input_dev *dev)
|
||||
{
|
||||
struct wacom *wacom = dev->private;
|
||||
|
||||
if (!--wacom->open)
|
||||
usb_kill_urb(wacom->irq);
|
||||
}
|
||||
|
||||
static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
char rep_data[2] = {0x02, 0x02};
|
||||
struct wacom *wacom;
|
||||
char path[64];
|
||||
|
||||
if (!(wacom = kmalloc(sizeof(struct wacom), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
memset(wacom, 0, sizeof(struct wacom));
|
||||
|
||||
wacom->data = usb_buffer_alloc(dev, 10, GFP_KERNEL, &wacom->data_dma);
|
||||
if (!wacom->data) {
|
||||
kfree(wacom);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
wacom->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!wacom->irq) {
|
||||
usb_buffer_free(dev, 10, wacom->data, wacom->data_dma);
|
||||
kfree(wacom);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
wacom->features = wacom_features + (id - wacom_ids);
|
||||
|
||||
wacom->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS);
|
||||
wacom->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
|
||||
wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH) | BIT(BTN_STYLUS);
|
||||
|
||||
switch (wacom->features->type) {
|
||||
case 1:
|
||||
wacom->dev.evbit[0] |= BIT(EV_REL);
|
||||
wacom->dev.relbit[0] |= BIT(REL_WHEEL);
|
||||
wacom->dev.absbit[0] |= BIT(ABS_DISTANCE);
|
||||
wacom->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
|
||||
wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_STYLUS2);
|
||||
break;
|
||||
|
||||
case 4: /* new functions for Intuos3 */
|
||||
wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
|
||||
wacom->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
|
||||
wacom->dev.absbit[0] |= BIT(ABS_RX) | BIT(ABS_RY);
|
||||
/* fall through */
|
||||
|
||||
case 2:
|
||||
wacom->dev.evbit[0] |= BIT(EV_MSC) | BIT(EV_REL);
|
||||
wacom->dev.mscbit[0] |= BIT(MSC_SERIAL);
|
||||
wacom->dev.relbit[0] |= BIT(REL_WHEEL);
|
||||
wacom->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE) | BIT(BTN_SIDE) | BIT(BTN_EXTRA);
|
||||
wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_TOOL_BRUSH)
|
||||
| BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS) | BIT(BTN_STYLUS2);
|
||||
wacom->dev.absbit[0] |= BIT(ABS_DISTANCE) | BIT(ABS_WHEEL) | BIT(ABS_TILT_X) | BIT(ABS_TILT_Y) | BIT(ABS_RZ) | BIT(ABS_THROTTLE);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2) | BIT(BTN_TOOL_RUBBER);
|
||||
break;
|
||||
}
|
||||
|
||||
wacom->dev.absmax[ABS_X] = wacom->features->x_max;
|
||||
wacom->dev.absmax[ABS_Y] = wacom->features->y_max;
|
||||
wacom->dev.absmax[ABS_PRESSURE] = wacom->features->pressure_max;
|
||||
wacom->dev.absmax[ABS_DISTANCE] = wacom->features->distance_max;
|
||||
wacom->dev.absmax[ABS_TILT_X] = 127;
|
||||
wacom->dev.absmax[ABS_TILT_Y] = 127;
|
||||
wacom->dev.absmax[ABS_WHEEL] = 1023;
|
||||
|
||||
wacom->dev.absmax[ABS_RX] = 4097;
|
||||
wacom->dev.absmax[ABS_RY] = 4097;
|
||||
wacom->dev.absmin[ABS_RZ] = -900;
|
||||
wacom->dev.absmax[ABS_RZ] = 899;
|
||||
wacom->dev.absmin[ABS_THROTTLE] = -1023;
|
||||
wacom->dev.absmax[ABS_THROTTLE] = 1023;
|
||||
|
||||
wacom->dev.absfuzz[ABS_X] = 4;
|
||||
wacom->dev.absfuzz[ABS_Y] = 4;
|
||||
|
||||
wacom->dev.private = wacom;
|
||||
wacom->dev.open = wacom_open;
|
||||
wacom->dev.close = wacom_close;
|
||||
|
||||
usb_make_path(dev, path, 64);
|
||||
sprintf(wacom->phys, "%s/input0", path);
|
||||
|
||||
wacom->dev.name = wacom->features->name;
|
||||
wacom->dev.phys = wacom->phys;
|
||||
wacom->dev.id.bustype = BUS_USB;
|
||||
wacom->dev.id.vendor = le16_to_cpu(dev->descriptor.idVendor);
|
||||
wacom->dev.id.product = le16_to_cpu(dev->descriptor.idProduct);
|
||||
wacom->dev.id.version = le16_to_cpu(dev->descriptor.bcdDevice);
|
||||
wacom->dev.dev = &intf->dev;
|
||||
wacom->usbdev = dev;
|
||||
|
||||
endpoint = &intf->cur_altsetting->endpoint[0].desc;
|
||||
|
||||
if (wacom->features->pktlen > 10)
|
||||
BUG();
|
||||
|
||||
usb_fill_int_urb(wacom->irq, dev,
|
||||
usb_rcvintpipe(dev, endpoint->bEndpointAddress),
|
||||
wacom->data, wacom->features->pktlen,
|
||||
wacom->features->irq, wacom, endpoint->bInterval);
|
||||
wacom->irq->transfer_dma = wacom->data_dma;
|
||||
wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
input_register_device(&wacom->dev);
|
||||
|
||||
/* ask the tablet to report tablet data */
|
||||
usb_set_report(intf, 3, 2, rep_data, 2);
|
||||
/* repeat once (not sure why the first call often fails) */
|
||||
usb_set_report(intf, 3, 2, rep_data, 2);
|
||||
|
||||
printk(KERN_INFO "input: %s on %s\n", wacom->features->name, path);
|
||||
|
||||
usb_set_intfdata(intf, wacom);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wacom_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct wacom *wacom = usb_get_intfdata (intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (wacom) {
|
||||
usb_kill_urb(wacom->irq);
|
||||
input_unregister_device(&wacom->dev);
|
||||
usb_free_urb(wacom->irq);
|
||||
usb_buffer_free(interface_to_usbdev(intf), 10, wacom->data, wacom->data_dma);
|
||||
kfree(wacom);
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_driver wacom_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "wacom",
|
||||
.probe = wacom_probe,
|
||||
.disconnect = wacom_disconnect,
|
||||
.id_table = wacom_ids,
|
||||
};
|
||||
|
||||
static int __init wacom_init(void)
|
||||
{
|
||||
int result = usb_register(&wacom_driver);
|
||||
if (result == 0)
|
||||
info(DRIVER_VERSION ":" DRIVER_DESC);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void __exit wacom_exit(void)
|
||||
{
|
||||
usb_deregister(&wacom_driver);
|
||||
}
|
||||
|
||||
module_init(wacom_init);
|
||||
module_exit(wacom_exit);
|
362
drivers/usb/input/xpad.c
Normal file
362
drivers/usb/input/xpad.c
Normal file
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
* X-Box gamepad - v0.0.5
|
||||
*
|
||||
* Copyright (c) 2002 Marko Friedemann <mfr@bmx-chemnitz.de>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; 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
|
||||
*
|
||||
*
|
||||
* This driver is based on:
|
||||
* - information from http://euc.jp/periphs/xbox-controller.ja.html
|
||||
* - the iForce driver drivers/char/joystick/iforce.c
|
||||
* - the skeleton-driver drivers/usb/usb-skeleton.c
|
||||
*
|
||||
* Thanks to:
|
||||
* - ITO Takayuki for providing essential xpad information on his website
|
||||
* - Vojtech Pavlik - iforce driver / input subsystem
|
||||
* - Greg Kroah-Hartman - usb-skeleton driver
|
||||
*
|
||||
* TODO:
|
||||
* - fine tune axes
|
||||
* - fix "analog" buttons (reported as digital now)
|
||||
* - get rumble working
|
||||
*
|
||||
* History:
|
||||
*
|
||||
* 2002-06-27 - 0.0.1 : first version, just said "XBOX HID controller"
|
||||
*
|
||||
* 2002-07-02 - 0.0.2 : basic working version
|
||||
* - all axes and 9 of the 10 buttons work (german InterAct device)
|
||||
* - the black button does not work
|
||||
*
|
||||
* 2002-07-14 - 0.0.3 : rework by Vojtech Pavlik
|
||||
* - indentation fixes
|
||||
* - usb + input init sequence fixes
|
||||
*
|
||||
* 2002-07-16 - 0.0.4 : minor changes, merge with Vojtech's v0.0.3
|
||||
* - verified the lack of HID and report descriptors
|
||||
* - verified that ALL buttons WORK
|
||||
* - fixed d-pad to axes mapping
|
||||
*
|
||||
* 2002-07-17 - 0.0.5 : simplified d-pad handling
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#define DRIVER_VERSION "v0.0.5"
|
||||
#define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>"
|
||||
#define DRIVER_DESC "X-Box pad driver"
|
||||
|
||||
#define XPAD_PKT_LEN 32
|
||||
|
||||
static struct xpad_device {
|
||||
u16 idVendor;
|
||||
u16 idProduct;
|
||||
char *name;
|
||||
} xpad_device[] = {
|
||||
{ 0x045e, 0x0202, "Microsoft X-Box pad (US)" },
|
||||
{ 0x045e, 0x0285, "Microsoft X-Box pad (Japan)" },
|
||||
{ 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)" },
|
||||
{ 0x0000, 0x0000, "X-Box pad" }
|
||||
};
|
||||
|
||||
static signed short xpad_btn[] = {
|
||||
BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, /* "analog" buttons */
|
||||
BTN_START, BTN_BACK, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */
|
||||
-1 /* terminating entry */
|
||||
};
|
||||
|
||||
static signed short xpad_abs[] = {
|
||||
ABS_X, ABS_Y, /* left stick */
|
||||
ABS_RX, ABS_RY, /* right stick */
|
||||
ABS_Z, ABS_RZ, /* triggers left/right */
|
||||
ABS_HAT0X, ABS_HAT0Y, /* digital pad */
|
||||
-1 /* terminating entry */
|
||||
};
|
||||
|
||||
static struct usb_device_id xpad_table [] = {
|
||||
{ USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE (usb, xpad_table);
|
||||
|
||||
struct usb_xpad {
|
||||
struct input_dev dev; /* input device interface */
|
||||
struct usb_device *udev; /* usb device */
|
||||
|
||||
struct urb *irq_in; /* urb for interrupt in report */
|
||||
unsigned char *idata; /* input data */
|
||||
dma_addr_t idata_dma;
|
||||
|
||||
char phys[65]; /* physical device path */
|
||||
int open_count; /* reference count */
|
||||
};
|
||||
|
||||
/*
|
||||
* xpad_process_packet
|
||||
*
|
||||
* Completes a request by converting the data into events for the
|
||||
* input subsystem.
|
||||
*
|
||||
* The used report descriptor was taken from ITO Takayukis website:
|
||||
* http://euc.jp/periphs/xbox-controller.ja.html
|
||||
*/
|
||||
|
||||
static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data, struct pt_regs *regs)
|
||||
{
|
||||
struct input_dev *dev = &xpad->dev;
|
||||
|
||||
input_regs(dev, regs);
|
||||
|
||||
/* left stick */
|
||||
input_report_abs(dev, ABS_X, (__s16) (((__s16)data[13] << 8) | data[12]));
|
||||
input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[15] << 8) | data[14]));
|
||||
|
||||
/* right stick */
|
||||
input_report_abs(dev, ABS_RX, (__s16) (((__s16)data[17] << 8) | data[16]));
|
||||
input_report_abs(dev, ABS_RY, (__s16) (((__s16)data[19] << 8) | data[18]));
|
||||
|
||||
/* triggers left/right */
|
||||
input_report_abs(dev, ABS_Z, data[10]);
|
||||
input_report_abs(dev, ABS_RZ, data[11]);
|
||||
|
||||
/* digital pad */
|
||||
input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04));
|
||||
input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01));
|
||||
|
||||
/* start/back buttons and stick press left/right */
|
||||
input_report_key(dev, BTN_START, (data[2] & 0x10) >> 4);
|
||||
input_report_key(dev, BTN_BACK, (data[2] & 0x20) >> 5);
|
||||
input_report_key(dev, BTN_THUMBL, (data[2] & 0x40) >> 6);
|
||||
input_report_key(dev, BTN_THUMBR, data[2] >> 7);
|
||||
|
||||
/* "analog" buttons A, B, X, Y */
|
||||
input_report_key(dev, BTN_A, data[4]);
|
||||
input_report_key(dev, BTN_B, data[5]);
|
||||
input_report_key(dev, BTN_X, data[6]);
|
||||
input_report_key(dev, BTN_Y, data[7]);
|
||||
|
||||
/* "analog" buttons black, white */
|
||||
input_report_key(dev, BTN_C, data[8]);
|
||||
input_report_key(dev, BTN_Z, data[9]);
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
static void xpad_irq_in(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct usb_xpad *xpad = urb->context;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
xpad_process_packet(xpad, 0, xpad->idata, regs);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err ("%s - usb_submit_urb failed with result %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
static int xpad_open (struct input_dev *dev)
|
||||
{
|
||||
struct usb_xpad *xpad = dev->private;
|
||||
|
||||
if (xpad->open_count++)
|
||||
return 0;
|
||||
|
||||
xpad->irq_in->dev = xpad->udev;
|
||||
if (usb_submit_urb(xpad->irq_in, GFP_KERNEL)) {
|
||||
xpad->open_count--;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xpad_close (struct input_dev *dev)
|
||||
{
|
||||
struct usb_xpad *xpad = dev->private;
|
||||
|
||||
if (!--xpad->open_count)
|
||||
usb_kill_urb(xpad->irq_in);
|
||||
}
|
||||
|
||||
static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev (intf);
|
||||
struct usb_xpad *xpad = NULL;
|
||||
struct usb_endpoint_descriptor *ep_irq_in;
|
||||
char path[64];
|
||||
int i;
|
||||
|
||||
for (i = 0; xpad_device[i].idVendor; i++) {
|
||||
if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) &&
|
||||
(le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct))
|
||||
break;
|
||||
}
|
||||
|
||||
if ((xpad = kmalloc (sizeof(struct usb_xpad), GFP_KERNEL)) == NULL) {
|
||||
err("cannot allocate memory for new pad");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(xpad, 0, sizeof(struct usb_xpad));
|
||||
|
||||
xpad->idata = usb_buffer_alloc(udev, XPAD_PKT_LEN,
|
||||
SLAB_ATOMIC, &xpad->idata_dma);
|
||||
if (!xpad->idata) {
|
||||
kfree(xpad);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!xpad->irq_in) {
|
||||
err("cannot allocate memory for new pad irq urb");
|
||||
usb_buffer_free(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
|
||||
kfree(xpad);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ep_irq_in = &intf->cur_altsetting->endpoint[0].desc;
|
||||
|
||||
usb_fill_int_urb(xpad->irq_in, udev,
|
||||
usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress),
|
||||
xpad->idata, XPAD_PKT_LEN, xpad_irq_in,
|
||||
xpad, ep_irq_in->bInterval);
|
||||
xpad->irq_in->transfer_dma = xpad->idata_dma;
|
||||
xpad->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
xpad->udev = udev;
|
||||
|
||||
xpad->dev.id.bustype = BUS_USB;
|
||||
xpad->dev.id.vendor = le16_to_cpu(udev->descriptor.idVendor);
|
||||
xpad->dev.id.product = le16_to_cpu(udev->descriptor.idProduct);
|
||||
xpad->dev.id.version = le16_to_cpu(udev->descriptor.bcdDevice);
|
||||
xpad->dev.dev = &intf->dev;
|
||||
xpad->dev.private = xpad;
|
||||
xpad->dev.name = xpad_device[i].name;
|
||||
xpad->dev.phys = xpad->phys;
|
||||
xpad->dev.open = xpad_open;
|
||||
xpad->dev.close = xpad_close;
|
||||
|
||||
usb_make_path(udev, path, 64);
|
||||
snprintf(xpad->phys, 64, "%s/input0", path);
|
||||
|
||||
xpad->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||
|
||||
for (i = 0; xpad_btn[i] >= 0; i++)
|
||||
set_bit(xpad_btn[i], xpad->dev.keybit);
|
||||
|
||||
for (i = 0; xpad_abs[i] >= 0; i++) {
|
||||
|
||||
signed short t = xpad_abs[i];
|
||||
|
||||
set_bit(t, xpad->dev.absbit);
|
||||
|
||||
switch (t) {
|
||||
case ABS_X:
|
||||
case ABS_Y:
|
||||
case ABS_RX:
|
||||
case ABS_RY: /* the two sticks */
|
||||
xpad->dev.absmax[t] = 32767;
|
||||
xpad->dev.absmin[t] = -32768;
|
||||
xpad->dev.absflat[t] = 128;
|
||||
xpad->dev.absfuzz[t] = 16;
|
||||
break;
|
||||
case ABS_Z:
|
||||
case ABS_RZ: /* the triggers */
|
||||
xpad->dev.absmax[t] = 255;
|
||||
xpad->dev.absmin[t] = 0;
|
||||
break;
|
||||
case ABS_HAT0X:
|
||||
case ABS_HAT0Y: /* the d-pad */
|
||||
xpad->dev.absmax[t] = 1;
|
||||
xpad->dev.absmin[t] = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
input_register_device(&xpad->dev);
|
||||
|
||||
printk(KERN_INFO "input: %s on %s", xpad->dev.name, path);
|
||||
|
||||
usb_set_intfdata(intf, xpad);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xpad_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_xpad *xpad = usb_get_intfdata (intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (xpad) {
|
||||
usb_kill_urb(xpad->irq_in);
|
||||
input_unregister_device(&xpad->dev);
|
||||
usb_free_urb(xpad->irq_in);
|
||||
usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
|
||||
kfree(xpad);
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_driver xpad_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "xpad",
|
||||
.probe = xpad_probe,
|
||||
.disconnect = xpad_disconnect,
|
||||
.id_table = xpad_table,
|
||||
};
|
||||
|
||||
static int __init usb_xpad_init(void)
|
||||
{
|
||||
int result = usb_register(&xpad_driver);
|
||||
if (result == 0)
|
||||
info(DRIVER_DESC ":" DRIVER_VERSION);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void __exit usb_xpad_exit(void)
|
||||
{
|
||||
usb_deregister(&xpad_driver);
|
||||
}
|
||||
|
||||
module_init(usb_xpad_init);
|
||||
module_exit(usb_xpad_exit);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
Reference in New Issue
Block a user