Creation of Cybook 2416 (actually Gen4) repository

This commit is contained in:
mlt
2009-12-18 17:10:00 +00:00
committed by godzil
commit 76f20f4d40
13791 changed files with 6812321 additions and 0 deletions

370
drivers/usb/input/Kconfig Normal file
View File

@@ -0,0 +1,370 @@
#
# USB Input driver configuration
#
comment "USB Input Devices"
depends on USB
config USB_HID
tristate "USB Human Interface Device (full HID) support"
default y
depends on USB && INPUT
select HID
---help---
Say Y here if you want full HID support to connect USB keyboards,
mice, joysticks, graphic tablets, or any other HID based devices
to your computer via USB, as well as Uninterruptible Power Supply
(UPS) and monitor control devices.
You can't use this driver and the HIDBP (Boot Protocol) keyboard
and mouse drivers at the same time. More information is available:
<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_POWERBOOK
bool "Enable support for iBook/PowerBook special keys"
default n
depends on USB_HID
help
Say Y here if you want support for the special keys (Fn, Numlock) on
Apple iBooks and PowerBooks.
If unsure, say N.
config HID_FF
bool "Force feedback support (EXPERIMENTAL)"
depends on USB_HID && EXPERIMENTAL
help
Say Y here is you want force feedback support for a few HID devices.
See below for a list of supported devices.
See <file:Documentation/input/ff.txt> for a description of the force
feedback API.
If unsure, say N.
config HID_PID
bool "PID device support"
depends on HID_FF
help
Say Y here if you have a PID-compliant device and wish to enable force
feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such
devices.
config LOGITECH_FF
bool "Logitech devices support"
depends on HID_FF
select INPUT_FF_MEMLESS if USB_HID
help
Say Y here if you have one of these devices:
- Logitech WingMan Cordless RumblePad
- Logitech WingMan Cordless RumblePad 2
- Logitech WingMan Force 3D
- Logitech Formula Force EX
- Logitech MOMO Force wheel
and if you want to enable force feedback for them.
Note: if you say N here, this device will still be supported, but without
force feedback.
config PANTHERLORD_FF
bool "PantherLord USB/PS2 2in1 Adapter support"
depends on HID_FF
select INPUT_FF_MEMLESS if USB_HID
help
Say Y here if you have a PantherLord USB/PS2 2in1 Adapter and want
to enable force feedback support for it.
config THRUSTMASTER_FF
bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)"
depends on HID_FF && EXPERIMENTAL
select INPUT_FF_MEMLESS if USB_HID
help
Say Y here if you have a THRUSTMASTER FireStore Dual Power 2,
and want to enable force feedback support for it.
Note: if you say N here, this device will still be supported, but without
force feedback.
config ZEROPLUS_FF
bool "Zeroplus based game controller support"
depends on HID_FF
select INPUT_FF_MEMLESS if USB_HID
help
Say Y here if you have a Zeroplus based game controller and want to
enable force feedback for it.
config USB_HIDDEV
bool "/dev/hiddev raw HID device support"
depends on USB_HID
help
Say Y here if you want to support HID devices (from the USB
specification standpoint) that aren't strictly user interface
devices, like monitor controls and Uninterruptable Power Supplies.
This module supports these devices separately using a separate
event interface on /dev/usb/hiddevX (char 180:96 to 180:111).
If unsure, say Y.
menu "USB HID Boot Protocol drivers"
depends on USB!=n && USB_HID!=y
config USB_KBD
tristate "USB HIDBP Keyboard (simple Boot) support"
depends on USB && INPUT
---help---
Say Y here only if you are absolutely sure that you don't want
to use the generic HID driver for your USB keyboard and prefer
to use the keyboard in its limited Boot Protocol mode instead.
This is almost certainly not what you want. This is mostly
useful for embedded applications or simple keyboards.
To compile this driver as a module, choose M here: the
module will be called usbkbd.
If even remotely unsure, say N.
config USB_MOUSE
tristate "USB HIDBP Mouse (simple Boot) support"
depends on USB && INPUT
---help---
Say Y here only if you are absolutely sure that you don't want
to use the generic HID driver for your USB mouse and prefer
to use the mouse in its limited Boot Protocol mode instead.
This is almost certainly not what you want. This is mostly
useful for embedded applications or simple mice.
To compile this driver as a module, choose M here: the
module will be called usbmouse.
If even remotely unsure, say N.
endmenu
config USB_AIPTEK
tristate "Aiptek 6000U/8000U tablet support"
depends on USB && INPUT
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_ACECAD
tristate "Acecad Flair tablet support"
depends on USB && INPUT
help
Say Y here if you want to use the USB version of the Acecad Flair
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 acecad.
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_TOUCHSCREEN
tristate "USB Touchscreen Driver"
depends on USB && INPUT
---help---
USB Touchscreen driver for:
- eGalax Touchkit USB (also includes eTurboTouch CT-410/510/700)
- PanJit TouchSet USB
- 3M MicroTouch USB (EX II series)
- ITM
- some other eTurboTouch
- Gunze AHL61
- DMC TSC-10/25
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 usbtouchscreen.
config USB_TOUCHSCREEN_EGALAX
default y
bool "eGalax, eTurboTouch CT-410/510/700 device support" if EMBEDDED
depends on USB_TOUCHSCREEN
config USB_TOUCHSCREEN_PANJIT
default y
bool "PanJit device support" if EMBEDDED
depends on USB_TOUCHSCREEN
config USB_TOUCHSCREEN_3M
default y
bool "3M/Microtouch EX II series device support" if EMBEDDED
depends on USB_TOUCHSCREEN
config USB_TOUCHSCREEN_ITM
default y
bool "ITM device support" if EMBEDDED
depends on USB_TOUCHSCREEN
config USB_TOUCHSCREEN_ETURBO
default y
bool "eTurboTouch (non-eGalax compatible) device support" if EMBEDDED
depends on USB_TOUCHSCREEN
config USB_TOUCHSCREEN_GUNZE
default y
bool "Gunze AHL61 device support" if EMBEDDED
depends on USB_TOUCHSCREEN
config USB_TOUCHSCREEN_DMC_TSC10
default y
bool "DMC TSC-10/25 device support" if EMBEDDED
depends on USB_TOUCHSCREEN
config USB_YEALINK
tristate "Yealink usb-p1k voip phone"
depends on USB && INPUT && EXPERIMENTAL
---help---
Say Y here if you want to enable keyboard and LCD functions of the
Yealink usb-p1k usb phones. The audio part is enabled by the generic
usb sound driver, so you might want to enable that as well.
For information about how to use these additional functions, see
<file:Documentation/input/yealink.txt>.
To compile this driver as a module, choose M here: the module will be
called yealink.
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.
config USB_ATI_REMOTE2
tristate "ATI / Philips USB RF remote control"
depends on USB && INPUT
---help---
Say Y here if you want to use an ATI or Philips USB RF remote control.
These are RF remotes with USB receivers.
ATI Remote Wonder II comes with some ATI's All-In-Wonder video cards
and is also available as a separate product.
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_remote2.
config USB_KEYSPAN_REMOTE
tristate "Keyspan DMR USB remote control (EXPERIMENTAL)"
depends on USB && INPUT && EXPERIMENTAL
---help---
Say Y here if you want to use a Keyspan DMR USB remote control.
Currently only the UIA-11 type of receiver has been tested. The tag
on the receiver that connects to the USB port should have a P/N that
will tell you what type of DMR you have. The UIA-10 type is not
supported at this time. This driver maps all buttons to keypress
events.
To compile this driver as a module, choose M here: the module will
be called keyspan_remote.
config USB_APPLETOUCH
tristate "Apple USB Touchpad support"
depends on USB && INPUT
---help---
Say Y here if you want to use an Apple USB Touchpad.
These are the touchpads that can be found on post-February 2005
Apple Powerbooks (prior models have a Synaptics touchpad connected
to the ADB bus).
This driver provides a basic mouse driver but can be interfaced
with the synaptics X11 driver to provide acceleration and
scrolling in X11.
For further information, see
<file:Documentation/input/appletouch.txt>.
To compile this driver as a module, choose M here: the
module will be called appletouch.
config USB_GTCO
tristate "GTCO CalComp/InterWrite USB Support"
depends on USB && INPUT
---help---
Say Y here if you want to use the USB version of the GTCO
CalComp/InterWrite 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 gtco.

View File

@@ -0,0 +1,55 @@
#
# Makefile for the USB input drivers
#
# Multipart objects.
wacom-objs := wacom_wac.o wacom_sys.o
usbhid-objs := hid-core.o
# Optional parts of multipart objects.
ifeq ($(CONFIG_USB_HIDDEV),y)
usbhid-objs += hiddev.o
endif
ifeq ($(CONFIG_HID_PID),y)
usbhid-objs += hid-pidff.o
endif
ifeq ($(CONFIG_LOGITECH_FF),y)
usbhid-objs += hid-lgff.o
endif
ifeq ($(CONFIG_PANTHERLORD_FF),y)
usbhid-objs += hid-plff.o
endif
ifeq ($(CONFIG_THRUSTMASTER_FF),y)
usbhid-objs += hid-tmff.o
endif
ifeq ($(CONFIG_ZEROPLUS_FF),y)
usbhid-objs += hid-zpff.o
endif
ifeq ($(CONFIG_HID_FF),y)
usbhid-objs += hid-ff.o
endif
obj-$(CONFIG_USB_AIPTEK) += aiptek.o
obj-$(CONFIG_USB_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_USB_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_USB_HID) += usbhid.o
obj-$(CONFIG_USB_KBD) += usbkbd.o
obj-$(CONFIG_USB_KBTAB) += kbtab.o
obj-$(CONFIG_USB_KEYSPAN_REMOTE) += keyspan_remote.o
obj-$(CONFIG_USB_MOUSE) += usbmouse.o
obj-$(CONFIG_USB_MTOUCH) += mtouchusb.o
obj-$(CONFIG_USB_ITMTOUCH) += itmtouch.o
obj-$(CONFIG_USB_EGALAX) += touchkitusb.o
obj-$(CONFIG_USB_TOUCHSCREEN) += usbtouchscreen.o
obj-$(CONFIG_USB_POWERMATE) += powermate.o
obj-$(CONFIG_USB_WACOM) += wacom.o
obj-$(CONFIG_USB_ACECAD) += acecad.o
obj-$(CONFIG_USB_YEALINK) += yealink.o
obj-$(CONFIG_USB_XPAD) += xpad.o
obj-$(CONFIG_USB_APPLETOUCH) += appletouch.o
obj-$(CONFIG_USB_GTCO) += gtco.o
ifeq ($(CONFIG_USB_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif

279
drivers/usb/input/acecad.c Normal file
View File

@@ -0,0 +1,279 @@
/*
* Copyright (c) 2001-2005 Edouard TISSERANT <edouard.tisserant@wanadoo.fr>
* Copyright (c) 2004-2005 Stephane VOLTZ <svoltz@numericable.fr>
*
* USB Acecad "Acecad Flair" tablet support
*
* Changelog:
* v3.2 - Added sysfs 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
*
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb/input.h>
/*
* Version Information
*/
#define DRIVER_VERSION "v3.2"
#define DRIVER_DESC "USB Acecad Flair tablet driver"
#define DRIVER_LICENSE "GPL"
#define DRIVER_AUTHOR "Edouard TISSERANT <edouard.tisserant@wanadoo.fr>"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);
#define USB_VENDOR_ID_ACECAD 0x0460
#define USB_DEVICE_ID_FLAIR 0x0004
#define USB_DEVICE_ID_302 0x0008
struct usb_acecad {
char name[128];
char phys[64];
struct usb_device *usbdev;
struct input_dev *input;
struct urb *irq;
signed char *data;
dma_addr_t data_dma;
};
static void usb_acecad_irq(struct urb *urb)
{
struct usb_acecad *acecad = urb->context;
unsigned char *data = acecad->data;
struct input_dev *dev = acecad->input;
int prox, status;
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 resubmit;
}
prox = (data[0] & 0x04) >> 2;
input_report_key(dev, BTN_TOOL_PEN, prox);
if (prox) {
int x = data[1] | (data[2] << 8);
int y = data[3] | (data[4] << 8);
/* Pressure should compute the same way for flair and 302 */
int pressure = data[5] | (data[6] << 8);
int touch = data[0] & 0x01;
int stylus = (data[0] & 0x10) >> 4;
int stylus2 = (data[0] & 0x20) >> 5;
input_report_abs(dev, ABS_X, x);
input_report_abs(dev, ABS_Y, y);
input_report_abs(dev, ABS_PRESSURE, pressure);
input_report_key(dev, BTN_TOUCH, touch);
input_report_key(dev, BTN_STYLUS, stylus);
input_report_key(dev, BTN_STYLUS2, stylus2);
}
/* event termination */
input_sync(dev);
resubmit:
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status)
err("can't resubmit intr, %s-%s/input0, status %d",
acecad->usbdev->bus->bus_name, acecad->usbdev->devpath, status);
}
static int usb_acecad_open(struct input_dev *dev)
{
struct usb_acecad *acecad = dev->private;
acecad->irq->dev = acecad->usbdev;
if (usb_submit_urb(acecad->irq, GFP_KERNEL))
return -EIO;
return 0;
}
static void usb_acecad_close(struct input_dev *dev)
{
struct usb_acecad *acecad = dev->private;
usb_kill_urb(acecad->irq);
}
static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface = intf->cur_altsetting;
struct usb_endpoint_descriptor *endpoint;
struct usb_acecad *acecad;
struct input_dev *input_dev;
int pipe, maxp;
if (interface->desc.bNumEndpoints != 1)
return -ENODEV;
endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
acecad = kzalloc(sizeof(struct usb_acecad), GFP_KERNEL);
input_dev = input_allocate_device();
if (!acecad || !input_dev)
goto fail1;
acecad->data = usb_buffer_alloc(dev, 8, GFP_KERNEL, &acecad->data_dma);
if (!acecad->data)
goto fail1;
acecad->irq = usb_alloc_urb(0, GFP_KERNEL);
if (!acecad->irq)
goto fail2;
acecad->usbdev = dev;
acecad->input = input_dev;
if (dev->manufacturer)
strlcpy(acecad->name, dev->manufacturer, sizeof(acecad->name));
if (dev->product) {
if (dev->manufacturer)
strlcat(acecad->name, " ", sizeof(acecad->name));
strlcat(acecad->name, dev->product, sizeof(acecad->name));
}
usb_make_path(dev, acecad->phys, sizeof(acecad->phys));
strlcat(acecad->phys, "/input0", sizeof(acecad->phys));
input_dev->name = acecad->name;
input_dev->phys = acecad->phys;
usb_to_input_id(dev, &input_dev->id);
input_dev->cdev.dev = &intf->dev;
input_dev->private = acecad;
input_dev->open = usb_acecad_open;
input_dev->close = usb_acecad_close;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
input_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
input_dev->keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
input_dev->keybit[LONG(BTN_DIGI)] = BIT(BTN_TOOL_PEN) |BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2);
switch (id->driver_info) {
case 0:
input_dev->absmax[ABS_X] = 5000;
input_dev->absmax[ABS_Y] = 3750;
input_dev->absmax[ABS_PRESSURE] = 512;
if (!strlen(acecad->name))
snprintf(acecad->name, sizeof(acecad->name),
"USB Acecad Flair Tablet %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
break;
case 1:
input_dev->absmax[ABS_X] = 3000;
input_dev->absmax[ABS_Y] = 2250;
input_dev->absmax[ABS_PRESSURE] = 1024;
if (!strlen(acecad->name))
snprintf(acecad->name, sizeof(acecad->name),
"USB Acecad 302 Tablet %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
break;
}
input_dev->absfuzz[ABS_X] = 4;
input_dev->absfuzz[ABS_Y] = 4;
usb_fill_int_urb(acecad->irq, dev, pipe,
acecad->data, maxp > 8 ? 8 : maxp,
usb_acecad_irq, acecad, endpoint->bInterval);
acecad->irq->transfer_dma = acecad->data_dma;
acecad->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
input_register_device(acecad->input);
usb_set_intfdata(intf, acecad);
return 0;
fail2: usb_buffer_free(dev, 8, acecad->data, acecad->data_dma);
fail1: input_free_device(input_dev);
kfree(acecad);
return -ENOMEM;
}
static void usb_acecad_disconnect(struct usb_interface *intf)
{
struct usb_acecad *acecad = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL);
if (acecad) {
usb_kill_urb(acecad->irq);
input_unregister_device(acecad->input);
usb_free_urb(acecad->irq);
usb_buffer_free(interface_to_usbdev(intf), 10, acecad->data, acecad->data_dma);
kfree(acecad);
}
}
static struct usb_device_id usb_acecad_id_table [] = {
{ USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_FLAIR), .driver_info = 0 },
{ USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_302), .driver_info = 1 },
{ }
};
MODULE_DEVICE_TABLE(usb, usb_acecad_id_table);
static struct usb_driver usb_acecad_driver = {
.name = "usb_acecad",
.probe = usb_acecad_probe,
.disconnect = usb_acecad_disconnect,
.id_table = usb_acecad_id_table,
};
static int __init usb_acecad_init(void)
{
int result = usb_register(&usb_acecad_driver);
if (result == 0)
info(DRIVER_VERSION ":" DRIVER_DESC);
return result;
}
static void __exit usb_acecad_exit(void)
{
usb_deregister(&usb_acecad_driver);
}
module_init(usb_acecad_init);
module_exit(usb_acecad_exit);

2234
drivers/usb/input/aiptek.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,705 @@
/*
* Apple USB Touchpad (for post-February 2005 PowerBooks and MacBooks) driver
*
* Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net)
* Copyright (C) 2005 Stelian Pop (stelian@popies.net)
* Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de)
* Copyright (C) 2005 Peter Osterlund (petero2@telia.com)
* Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch)
* Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch)
*
* Thanks to Alex Harper <basilisk@foobox.net> for his inputs.
*
* 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.
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb/input.h>
/* Apple has powerbooks which have the keyboard with different Product IDs */
#define APPLE_VENDOR_ID 0x05AC
/* These names come from Info.plist in AppleUSBTrackpad.kext */
#define FOUNTAIN_ANSI_PRODUCT_ID 0x020E
#define FOUNTAIN_ISO_PRODUCT_ID 0x020F
#define FOUNTAIN_TP_ONLY_PRODUCT_ID 0x030A
#define GEYSER1_TP_ONLY_PRODUCT_ID 0x030B
#define GEYSER_ANSI_PRODUCT_ID 0x0214
#define GEYSER_ISO_PRODUCT_ID 0x0215
#define GEYSER_JIS_PRODUCT_ID 0x0216
/* MacBook devices */
#define GEYSER3_ANSI_PRODUCT_ID 0x0217
#define GEYSER3_ISO_PRODUCT_ID 0x0218
#define GEYSER3_JIS_PRODUCT_ID 0x0219
/*
* Geyser IV: same as Geyser III according to Info.plist in AppleUSBTrackpad.kext
* -> same IOClass (AppleUSBGrIIITrackpad), same acceleration tables
*/
#define GEYSER4_ANSI_PRODUCT_ID 0x021A
#define GEYSER4_ISO_PRODUCT_ID 0x021B
#define GEYSER4_JIS_PRODUCT_ID 0x021C
#define ATP_DEVICE(prod) \
.match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
USB_DEVICE_ID_MATCH_INT_CLASS | \
USB_DEVICE_ID_MATCH_INT_PROTOCOL, \
.idVendor = APPLE_VENDOR_ID, \
.idProduct = (prod), \
.bInterfaceClass = 0x03, \
.bInterfaceProtocol = 0x02
/* table of devices that work with this driver */
static struct usb_device_id atp_table [] = {
{ ATP_DEVICE(FOUNTAIN_ANSI_PRODUCT_ID) },
{ ATP_DEVICE(FOUNTAIN_ISO_PRODUCT_ID) },
{ ATP_DEVICE(FOUNTAIN_TP_ONLY_PRODUCT_ID) },
{ ATP_DEVICE(GEYSER1_TP_ONLY_PRODUCT_ID) },
/* PowerBooks Oct 2005 */
{ ATP_DEVICE(GEYSER_ANSI_PRODUCT_ID) },
{ ATP_DEVICE(GEYSER_ISO_PRODUCT_ID) },
{ ATP_DEVICE(GEYSER_JIS_PRODUCT_ID) },
/* Core Duo MacBook & MacBook Pro */
{ ATP_DEVICE(GEYSER3_ANSI_PRODUCT_ID) },
{ ATP_DEVICE(GEYSER3_ISO_PRODUCT_ID) },
{ ATP_DEVICE(GEYSER3_JIS_PRODUCT_ID) },
/* Core2 Duo MacBook & MacBook Pro */
{ ATP_DEVICE(GEYSER4_ANSI_PRODUCT_ID) },
{ ATP_DEVICE(GEYSER4_ISO_PRODUCT_ID) },
{ ATP_DEVICE(GEYSER4_JIS_PRODUCT_ID) },
/* Terminating entry */
{ }
};
MODULE_DEVICE_TABLE (usb, atp_table);
/*
* number of sensors. Note that only 16 instead of 26 X (horizontal)
* sensors exist on 12" and 15" PowerBooks. All models have 16 Y
* (vertical) sensors.
*/
#define ATP_XSENSORS 26
#define ATP_YSENSORS 16
/* amount of fuzz this touchpad generates */
#define ATP_FUZZ 16
/* maximum pressure this driver will report */
#define ATP_PRESSURE 300
/*
* multiplication factor for the X and Y coordinates.
* We try to keep the touchpad aspect ratio while still doing only simple
* arithmetics.
* The factors below give coordinates like:
* 0 <= x < 960 on 12" and 15" Powerbooks
* 0 <= x < 1600 on 17" Powerbooks
* 0 <= y < 646
*/
#define ATP_XFACT 64
#define ATP_YFACT 43
/*
* Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is
* ignored.
*/
#define ATP_THRESHOLD 5
/* MacBook Pro (Geyser 3 & 4) initialization constants */
#define ATP_GEYSER3_MODE_READ_REQUEST_ID 1
#define ATP_GEYSER3_MODE_WRITE_REQUEST_ID 9
#define ATP_GEYSER3_MODE_REQUEST_VALUE 0x300
#define ATP_GEYSER3_MODE_REQUEST_INDEX 0
#define ATP_GEYSER3_MODE_VENDOR_VALUE 0x04
/* Structure to hold all of our device specific stuff */
struct atp {
char phys[64];
struct usb_device * udev; /* usb device */
struct urb * urb; /* usb request block */
signed char * data; /* transferred data */
int open; /* non-zero if opened */
struct input_dev *input; /* input dev */
int valid; /* are the sensors valid ? */
int x_old; /* last reported x/y, */
int y_old; /* used for smoothing */
/* current value of the sensors */
signed char xy_cur[ATP_XSENSORS + ATP_YSENSORS];
/* last value of the sensors */
signed char xy_old[ATP_XSENSORS + ATP_YSENSORS];
/* accumulated sensors */
int xy_acc[ATP_XSENSORS + ATP_YSENSORS];
int overflowwarn; /* overflow warning printed? */
int datalen; /* size of an USB urb transfer */
};
#define dbg_dump(msg, tab) \
if (debug > 1) { \
int i; \
printk("appletouch: %s %lld", msg, (long long)jiffies); \
for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) \
printk(" %02x", tab[i]); \
printk("\n"); \
}
#define dprintk(format, a...) \
do { \
if (debug) printk(format, ##a); \
} while (0)
MODULE_AUTHOR("Johannes Berg, Stelian Pop, Frank Arnold, Michael Hanselmann");
MODULE_DESCRIPTION("Apple PowerBooks USB touchpad driver");
MODULE_LICENSE("GPL");
/*
* Make the threshold a module parameter
*/
static int threshold = ATP_THRESHOLD;
module_param(threshold, int, 0644);
MODULE_PARM_DESC(threshold, "Discards any change in data from a sensor (trackpad has hundreds of these sensors) less than this value");
static int debug = 1;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Activate debugging output");
/* Checks if the device a Geyser 2 (ANSI, ISO, JIS) */
static inline int atp_is_geyser_2(struct atp *dev)
{
u16 productId = le16_to_cpu(dev->udev->descriptor.idProduct);
return (productId == GEYSER_ANSI_PRODUCT_ID) ||
(productId == GEYSER_ISO_PRODUCT_ID) ||
(productId == GEYSER_JIS_PRODUCT_ID);
}
static inline int atp_is_geyser_3(struct atp *dev)
{
u16 productId = le16_to_cpu(dev->udev->descriptor.idProduct);
return (productId == GEYSER3_ANSI_PRODUCT_ID) ||
(productId == GEYSER3_ISO_PRODUCT_ID) ||
(productId == GEYSER3_JIS_PRODUCT_ID) ||
(productId == GEYSER4_ANSI_PRODUCT_ID) ||
(productId == GEYSER4_ISO_PRODUCT_ID) ||
(productId == GEYSER4_JIS_PRODUCT_ID);
}
static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
int *z, int *fingers)
{
int i;
/* values to calculate mean */
int pcum = 0, psum = 0;
int is_increasing = 0;
*fingers = 0;
for (i = 0; i < nb_sensors; i++) {
if (xy_sensors[i] < threshold) {
if (is_increasing)
is_increasing = 0;
continue;
}
/*
* Makes the finger detection more versatile. For example,
* two fingers with no gap will be detected. Also, my
* tests show it less likely to have intermittent loss
* of multiple finger readings while moving around (scrolling).
*
* Changes the multiple finger detection to counting humps on
* sensors (transitions from nonincreasing to increasing)
* instead of counting transitions from low sensors (no
* finger reading) to high sensors (finger above
* sensor)
*
* - Jason Parekh <jasonparekh@gmail.com>
*/
if (i < 1 || (!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) {
(*fingers)++;
is_increasing = 1;
} else if (i > 0 && xy_sensors[i - 1] >= xy_sensors[i]) {
is_increasing = 0;
}
/*
* Subtracts threshold so a high sensor that just passes the threshold
* won't skew the calculated absolute coordinate. Fixes an issue
* where slowly moving the mouse would occassionaly jump a number of
* pixels (let me restate--slowly moving the mouse makes this issue
* most apparent).
*/
pcum += (xy_sensors[i] - threshold) * i;
psum += (xy_sensors[i] - threshold);
}
if (psum > 0) {
*z = psum;
return pcum * fact / psum;
}
return 0;
}
static inline void atp_report_fingers(struct input_dev *input, int fingers)
{
input_report_key(input, BTN_TOOL_FINGER, fingers == 1);
input_report_key(input, BTN_TOOL_DOUBLETAP, fingers == 2);
input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2);
}
static void atp_complete(struct urb* urb)
{
int x, y, x_z, y_z, x_f, y_f;
int retval, i, j;
struct atp *dev = urb->context;
switch (urb->status) {
case 0:
/* success */
break;
case -EOVERFLOW:
if(!dev->overflowwarn) {
printk("appletouch: OVERFLOW with data "
"length %d, actual length is %d\n",
dev->datalen, dev->urb->actual_length);
dev->overflowwarn = 1;
}
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;
}
/* drop incomplete datasets */
if (dev->urb->actual_length != dev->datalen) {
dprintk("appletouch: incomplete data package"
" (first byte: %d, length: %d).\n",
dev->data[0], dev->urb->actual_length);
goto exit;
}
/* reorder the sensors values */
if (atp_is_geyser_3(dev)) {
memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
/*
* The values are laid out like this:
* -, Y1, Y2, -, Y3, Y4, -, ..., -, X1, X2, -, X3, X4, ...
* '-' is an unused value.
*/
/* read X values */
for (i = 0, j = 19; i < 20; i += 2, j += 3) {
dev->xy_cur[i] = dev->data[j + 1];
dev->xy_cur[i + 1] = dev->data[j + 2];
}
/* read Y values */
for (i = 0, j = 1; i < 9; i += 2, j += 3) {
dev->xy_cur[ATP_XSENSORS + i] = dev->data[j + 1];
dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 2];
}
} else if (atp_is_geyser_2(dev)) {
memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
/*
* The values are laid out like this:
* Y1, Y2, -, Y3, Y4, -, ..., X1, X2, -, X3, X4, -, ...
* '-' is an unused value.
*/
/* read X values */
for (i = 0, j = 19; i < 20; i += 2, j += 3) {
dev->xy_cur[i] = dev->data[j];
dev->xy_cur[i + 1] = dev->data[j + 1];
}
/* read Y values */
for (i = 0, j = 1; i < 9; i += 2, j += 3) {
dev->xy_cur[ATP_XSENSORS + i] = dev->data[j];
dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 1];
}
} else {
for (i = 0; i < 8; i++) {
/* X values */
dev->xy_cur[i ] = dev->data[5 * i + 2];
dev->xy_cur[i + 8] = dev->data[5 * i + 4];
dev->xy_cur[i + 16] = dev->data[5 * i + 42];
if (i < 2)
dev->xy_cur[i + 24] = dev->data[5 * i + 44];
/* Y values */
dev->xy_cur[i + 26] = dev->data[5 * i + 1];
dev->xy_cur[i + 34] = dev->data[5 * i + 3];
}
}
dbg_dump("sample", dev->xy_cur);
if (!dev->valid) {
/* first sample */
dev->valid = 1;
dev->x_old = dev->y_old = -1;
memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
if (atp_is_geyser_3(dev)) /* No 17" Macbooks (yet) */
goto exit;
/* 17" Powerbooks have extra X sensors */
for (i = (atp_is_geyser_2(dev)?15:16); i < ATP_XSENSORS; i++) {
if (!dev->xy_cur[i]) continue;
printk("appletouch: 17\" model detected.\n");
if(atp_is_geyser_2(dev))
input_set_abs_params(dev->input, ABS_X, 0,
(20 - 1) *
ATP_XFACT - 1,
ATP_FUZZ, 0);
else
input_set_abs_params(dev->input, ABS_X, 0,
(ATP_XSENSORS - 1) *
ATP_XFACT - 1,
ATP_FUZZ, 0);
break;
}
goto exit;
}
for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
/* accumulate the change */
signed char change = dev->xy_old[i] - dev->xy_cur[i];
dev->xy_acc[i] -= change;
/* prevent down drifting */
if (dev->xy_acc[i] < 0)
dev->xy_acc[i] = 0;
}
memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
dbg_dump("accumulator", dev->xy_acc);
x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS,
ATP_XFACT, &x_z, &x_f);
y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
ATP_YFACT, &y_z, &y_f);
if (x && y) {
if (dev->x_old != -1) {
x = (dev->x_old * 3 + x) >> 2;
y = (dev->y_old * 3 + y) >> 2;
dev->x_old = x;
dev->y_old = y;
if (debug > 1)
printk("appletouch: X: %3d Y: %3d "
"Xz: %3d Yz: %3d\n",
x, y, x_z, y_z);
input_report_key(dev->input, BTN_TOUCH, 1);
input_report_abs(dev->input, ABS_X, x);
input_report_abs(dev->input, ABS_Y, y);
input_report_abs(dev->input, ABS_PRESSURE,
min(ATP_PRESSURE, x_z + y_z));
atp_report_fingers(dev->input, max(x_f, y_f));
}
dev->x_old = x;
dev->y_old = y;
}
else if (!x && !y) {
dev->x_old = dev->y_old = -1;
input_report_key(dev->input, BTN_TOUCH, 0);
input_report_abs(dev->input, ABS_PRESSURE, 0);
atp_report_fingers(dev->input, 0);
/* reset the accumulator on release */
memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
}
input_report_key(dev->input, BTN_LEFT,
!!dev->data[dev->datalen - 1]);
input_sync(dev->input);
exit:
retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
if (retval) {
err("%s - usb_submit_urb failed with result %d",
__FUNCTION__, retval);
}
}
static int atp_open(struct input_dev *input)
{
struct atp *dev = input->private;
if (usb_submit_urb(dev->urb, GFP_ATOMIC))
return -EIO;
dev->open = 1;
return 0;
}
static void atp_close(struct input_dev *input)
{
struct atp *dev = input->private;
usb_kill_urb(dev->urb);
dev->open = 0;
}
static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id)
{
struct atp *dev;
struct input_dev *input_dev;
struct usb_device *udev = interface_to_usbdev(iface);
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
int int_in_endpointAddr = 0;
int i, retval = -ENOMEM;
/* set up the endpoint information */
/* use only the first interrupt-in endpoint */
iface_desc = iface->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
endpoint = &iface_desc->endpoint[i].desc;
if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) {
/* we found an interrupt in endpoint */
int_in_endpointAddr = endpoint->bEndpointAddress;
break;
}
}
if (!int_in_endpointAddr) {
err("Could not find int-in endpoint");
return -EIO;
}
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(struct atp), GFP_KERNEL);
input_dev = input_allocate_device();
if (!dev || !input_dev) {
err("Out of memory");
goto err_free_devs;
}
dev->udev = udev;
dev->input = input_dev;
dev->overflowwarn = 0;
if (atp_is_geyser_3(dev))
dev->datalen = 64;
else if (atp_is_geyser_2(dev))
dev->datalen = 64;
else
dev->datalen = 81;
if (atp_is_geyser_3(dev)) {
/*
* By default Geyser 3 device sends standard USB HID mouse
* packets (Report ID 2). This code changes device mode, so it
* sends raw sensor reports (Report ID 5).
*/
char data[8];
int size;
size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
ATP_GEYSER3_MODE_READ_REQUEST_ID,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
ATP_GEYSER3_MODE_REQUEST_VALUE,
ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000);
if (size != 8) {
err("Could not do mode read request from device"
" (Geyser 3 mode)");
goto err_free_devs;
}
/* Apply the mode switch */
data[0] = ATP_GEYSER3_MODE_VENDOR_VALUE;
size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
ATP_GEYSER3_MODE_WRITE_REQUEST_ID,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
ATP_GEYSER3_MODE_REQUEST_VALUE,
ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000);
if (size != 8) {
err("Could not do mode write request to device"
" (Geyser 3 mode)");
goto err_free_devs;
}
printk("appletouch Geyser 3 inited.\n");
}
dev->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->urb) {
retval = -ENOMEM;
goto err_free_devs;
}
dev->data = usb_buffer_alloc(dev->udev, dev->datalen, GFP_KERNEL,
&dev->urb->transfer_dma);
if (!dev->data) {
retval = -ENOMEM;
goto err_free_urb;
}
usb_fill_int_urb(dev->urb, udev,
usb_rcvintpipe(udev, int_in_endpointAddr),
dev->data, dev->datalen, atp_complete, dev, 1);
usb_make_path(udev, dev->phys, sizeof(dev->phys));
strlcat(dev->phys, "/input0", sizeof(dev->phys));
input_dev->name = "appletouch";
input_dev->phys = dev->phys;
usb_to_input_id(dev->udev, &input_dev->id);
input_dev->cdev.dev = &iface->dev;
input_dev->private = dev;
input_dev->open = atp_open;
input_dev->close = atp_close;
set_bit(EV_ABS, input_dev->evbit);
if (atp_is_geyser_3(dev)) {
/*
* MacBook have 20 X sensors, 10 Y sensors
*/
input_set_abs_params(input_dev, ABS_X, 0,
((20 - 1) * ATP_XFACT) - 1, ATP_FUZZ, 0);
input_set_abs_params(input_dev, ABS_Y, 0,
((10 - 1) * ATP_YFACT) - 1, ATP_FUZZ, 0);
} else if (atp_is_geyser_2(dev)) {
/*
* Oct 2005 15" PowerBooks have 15 X sensors, 17" are detected
* later.
*/
input_set_abs_params(input_dev, ABS_X, 0,
((15 - 1) * ATP_XFACT) - 1, ATP_FUZZ, 0);
input_set_abs_params(input_dev, ABS_Y, 0,
((9 - 1) * ATP_YFACT) - 1, ATP_FUZZ, 0);
} else {
/*
* 12" and 15" Powerbooks only have 16 x sensors,
* 17" models are detected later.
*/
input_set_abs_params(input_dev, ABS_X, 0,
(16 - 1) * ATP_XFACT - 1, ATP_FUZZ, 0);
input_set_abs_params(input_dev, ABS_Y, 0,
(ATP_YSENSORS - 1) * ATP_YFACT - 1, ATP_FUZZ, 0);
}
input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);
set_bit(EV_KEY, input_dev->evbit);
set_bit(BTN_TOUCH, input_dev->keybit);
set_bit(BTN_TOOL_FINGER, input_dev->keybit);
set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
set_bit(BTN_LEFT, input_dev->keybit);
input_register_device(dev->input);
/* save our data pointer in this interface device */
usb_set_intfdata(iface, dev);
return 0;
err_free_urb:
usb_free_urb(dev->urb);
err_free_devs:
usb_set_intfdata(iface, NULL);
kfree(dev);
input_free_device(input_dev);
return retval;
}
static void atp_disconnect(struct usb_interface *iface)
{
struct atp *dev = usb_get_intfdata(iface);
usb_set_intfdata(iface, NULL);
if (dev) {
usb_kill_urb(dev->urb);
input_unregister_device(dev->input);
usb_buffer_free(dev->udev, dev->datalen,
dev->data, dev->urb->transfer_dma);
usb_free_urb(dev->urb);
kfree(dev);
}
printk(KERN_INFO "input: appletouch disconnected\n");
}
static int atp_suspend(struct usb_interface *iface, pm_message_t message)
{
struct atp *dev = usb_get_intfdata(iface);
usb_kill_urb(dev->urb);
dev->valid = 0;
return 0;
}
static int atp_resume(struct usb_interface *iface)
{
struct atp *dev = usb_get_intfdata(iface);
if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
return -EIO;
return 0;
}
static struct usb_driver atp_driver = {
.name = "appletouch",
.probe = atp_probe,
.disconnect = atp_disconnect,
.suspend = atp_suspend,
.resume = atp_resume,
.id_table = atp_table,
};
static int __init atp_init(void)
{
return usb_register(&atp_driver);
}
static void __exit atp_exit(void)
{
usb_deregister(&atp_driver);
}
module_init(atp_init);
module_exit(atp_exit);

View File

@@ -0,0 +1,842 @@
/*
* 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/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/usb/input.h>
#include <linux/wait.h>
#include <linux/jiffies.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 */
/*
* Duplicate event filtering time.
* Sequential, identical KIND_FILTERED inputs with less than
* FILTER_TIME milliseconds 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.
*/
#define FILTER_TIME 60 /* msec */
static unsigned long channel_mask;
module_param(channel_mask, ulong, 0644);
MODULE_PARM_DESC(channel_mask, "Bitmask of remote control channels to ignore");
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
static int repeat_filter = FILTER_TIME;
module_param(repeat_filter, int, 0644);
MODULE_PARM_DESC(repeat_filter, "Repeat filter time, default = 60 msec");
#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 };
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;
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 const 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 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);
static void ati_remote_irq_in (struct urb *urb);
static void ati_remote_input_report (struct urb *urb);
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 = {
.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;
/* 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__);
return -EIO;
}
return 0;
}
/*
* ati_remote_close
*/
static void ati_remote_close(struct input_dev *inputdev)
{
struct ati_remote *ati_remote = inputdev->private;
usb_kill_urb(ati_remote->irq_urb);
}
/*
* ati_remote_irq_out
*/
static void ati_remote_irq_out(struct urb *urb)
{
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_compute_accel
*
* Implements acceleration curve for directional control pad
* 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.
*/
static int ati_remote_compute_accel(struct ati_remote *ati_remote)
{
static const char accel[] = { 1, 2, 4, 6, 9, 13, 20 };
unsigned long now = jiffies;
int acc;
if (time_after(now, ati_remote->old_jiffies + msecs_to_jiffies(250))) {
acc = 1;
ati_remote->acc_jiffies = now;
}
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(125)))
acc = accel[0];
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(250)))
acc = accel[1];
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(500)))
acc = accel[2];
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1000)))
acc = accel[3];
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1500)))
acc = accel[4];
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(2000)))
acc = accel[5];
else
acc = accel[6];
return acc;
}
/*
* ati_remote_report_input
*/
static void ati_remote_input_report(struct urb *urb)
{
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_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] &&
time_before(jiffies, ati_remote->old_jiffies + msecs_to_jiffies(repeat_filter))) {
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_event(dev, ati_remote_tbl[index].type,
ati_remote_tbl[index].code, 1);
input_sync(dev);
input_event(dev, ati_remote_tbl[index].type,
ati_remote_tbl[index].code, 0);
input_sync(dev);
} else {
/*
* 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.
*/
acc = ati_remote_compute_accel(ati_remote);
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 ati_remote *ati_remote = urb->context;
int retval;
switch (urb->status) {
case 0: /* success */
ati_remote_input_report(urb);
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, GFP_ATOMIC);
if (retval)
dev_err(&ati_remote->interface->dev, "%s: usb_submit_urb()=%d\n",
__FUNCTION__, retval);
}
/*
* ati_remote_alloc_buffers
*/
static int ati_remote_alloc_buffers(struct usb_device *udev,
struct ati_remote *ati_remote)
{
ati_remote->inbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, GFP_ATOMIC,
&ati_remote->inbuf_dma);
if (!ati_remote->inbuf)
return -1;
ati_remote->outbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, GFP_ATOMIC,
&ati_remote->outbuf_dma);
if (!ati_remote->outbuf)
return -1;
ati_remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!ati_remote->irq_urb)
return -1;
ati_remote->out_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!ati_remote->out_urb)
return -1;
return 0;
}
/*
* ati_remote_free_buffers
*/
static void ati_remote_free_buffers(struct ati_remote *ati_remote)
{
usb_free_urb(ati_remote->irq_urb);
usb_free_urb(ati_remote->out_urb);
usb_buffer_free(ati_remote->udev, DATA_BUFSIZE,
ati_remote->inbuf, ati_remote->inbuf_dma);
usb_buffer_free(ati_remote->udev, DATA_BUFSIZE,
ati_remote->outbuf, ati_remote->outbuf_dma);
}
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;
usb_to_input_id(ati_remote->udev, &idev->id);
idev->cdev.dev = &ati_remote->udev->dev;
}
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 -EIO;
}
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 usb_host_interface *iface_host = interface->cur_altsetting;
struct usb_endpoint_descriptor *endpoint_in, *endpoint_out;
struct ati_remote *ati_remote;
struct input_dev *input_dev;
int err = -ENOMEM;
if (iface_host->desc.bNumEndpoints != 2) {
err("%s: Unexpected desc.bNumEndpoints\n", __FUNCTION__);
return -ENODEV;
}
endpoint_in = &iface_host->endpoint[0].desc;
endpoint_out = &iface_host->endpoint[1].desc;
if (!usb_endpoint_is_int_in(endpoint_in)) {
err("%s: Unexpected endpoint_in\n", __FUNCTION__);
return -ENODEV;
}
if (le16_to_cpu(endpoint_in->wMaxPacketSize) == 0) {
err("%s: endpoint_in message size==0? \n", __FUNCTION__);
return -ENODEV;
}
ati_remote = kzalloc(sizeof (struct ati_remote), GFP_KERNEL);
input_dev = input_allocate_device();
if (!ati_remote || !input_dev)
goto fail1;
/* Allocate URB buffers, URBs */
if (ati_remote_alloc_buffers(udev, ati_remote))
goto fail2;
ati_remote->endpoint_in = endpoint_in;
ati_remote->endpoint_out = endpoint_out;
ati_remote->udev = udev;
ati_remote->idev = input_dev;
ati_remote->interface = interface;
usb_make_path(udev, ati_remote->phys, sizeof(ati_remote->phys));
strlcpy(ati_remote->phys, "/input0", sizeof(ati_remote->phys));
if (udev->manufacturer)
strlcpy(ati_remote->name, udev->manufacturer, sizeof(ati_remote->name));
if (udev->product)
snprintf(ati_remote->name, sizeof(ati_remote->name),
"%s %s", ati_remote->name, udev->product);
if (!strlen(ati_remote->name))
snprintf(ati_remote->name, sizeof(ati_remote->name),
DRIVER_DESC "(%04x,%04x)",
le16_to_cpu(ati_remote->udev->descriptor.idVendor),
le16_to_cpu(ati_remote->udev->descriptor.idProduct));
ati_remote_input_init(ati_remote);
/* Device Hardware Initialization - fills in ati_remote->idev from udev. */
err = ati_remote_initialize(ati_remote);
if (err)
goto fail3;
/* Set up and register input device */
input_register_device(ati_remote->idev);
usb_set_intfdata(interface, ati_remote);
return 0;
fail3: usb_kill_urb(ati_remote->irq_urb);
usb_kill_urb(ati_remote->out_urb);
fail2: ati_remote_free_buffers(ati_remote);
fail1: input_free_device(input_dev);
kfree(ati_remote);
return err;
}
/*
* ati_remote_disconnect
*/
static void ati_remote_disconnect(struct usb_interface *interface)
{
struct ati_remote *ati_remote;
ati_remote = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
if (!ati_remote) {
warn("%s - null device?\n", __FUNCTION__);
return;
}
usb_kill_urb(ati_remote->irq_urb);
usb_kill_urb(ati_remote->out_urb);
input_unregister_device(ati_remote->idev);
ati_remote_free_buffers(ati_remote);
kfree(ati_remote);
}
/*
* 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");

View File

@@ -0,0 +1,474 @@
/*
* ati_remote2 - ATI/Philips USB RF remote driver
*
* Copyright (C) 2005 Ville Syrjala <syrjala@sci.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/usb/input.h>
#define DRIVER_DESC "ATI/Philips USB RF remote driver"
#define DRIVER_VERSION "0.1"
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>");
MODULE_LICENSE("GPL");
static unsigned int mode_mask = 0x1F;
module_param(mode_mask, uint, 0644);
MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>");
static struct usb_device_id ati_remote2_id_table[] = {
{ USB_DEVICE(0x0471, 0x0602) }, /* ATI Remote Wonder II */
{ }
};
MODULE_DEVICE_TABLE(usb, ati_remote2_id_table);
static struct {
int hw_code;
int key_code;
} ati_remote2_key_table[] = {
{ 0x00, KEY_0 },
{ 0x01, KEY_1 },
{ 0x02, KEY_2 },
{ 0x03, KEY_3 },
{ 0x04, KEY_4 },
{ 0x05, KEY_5 },
{ 0x06, KEY_6 },
{ 0x07, KEY_7 },
{ 0x08, KEY_8 },
{ 0x09, KEY_9 },
{ 0x0c, KEY_POWER },
{ 0x0d, KEY_MUTE },
{ 0x10, KEY_VOLUMEUP },
{ 0x11, KEY_VOLUMEDOWN },
{ 0x20, KEY_CHANNELUP },
{ 0x21, KEY_CHANNELDOWN },
{ 0x28, KEY_FORWARD },
{ 0x29, KEY_REWIND },
{ 0x2c, KEY_PLAY },
{ 0x30, KEY_PAUSE },
{ 0x31, KEY_STOP },
{ 0x37, KEY_RECORD },
{ 0x38, KEY_DVD },
{ 0x39, KEY_TV },
{ 0x54, KEY_MENU },
{ 0x58, KEY_UP },
{ 0x59, KEY_DOWN },
{ 0x5a, KEY_LEFT },
{ 0x5b, KEY_RIGHT },
{ 0x5c, KEY_OK },
{ 0x78, KEY_A },
{ 0x79, KEY_B },
{ 0x7a, KEY_C },
{ 0x7b, KEY_D },
{ 0x7c, KEY_E },
{ 0x7d, KEY_F },
{ 0x82, KEY_ENTER },
{ 0x8e, KEY_VENDOR },
{ 0x96, KEY_COFFEE },
{ 0xa9, BTN_LEFT },
{ 0xaa, BTN_RIGHT },
{ 0xbe, KEY_QUESTION },
{ 0xd5, KEY_FRONT },
{ 0xd0, KEY_EDIT },
{ 0xf9, KEY_INFO },
{ (0x00 << 8) | 0x3f, KEY_PROG1 },
{ (0x01 << 8) | 0x3f, KEY_PROG2 },
{ (0x02 << 8) | 0x3f, KEY_PROG3 },
{ (0x03 << 8) | 0x3f, KEY_PROG4 },
{ (0x04 << 8) | 0x3f, KEY_PC },
{ 0, KEY_RESERVED }
};
struct ati_remote2 {
struct input_dev *idev;
struct usb_device *udev;
struct usb_interface *intf[2];
struct usb_endpoint_descriptor *ep[2];
struct urb *urb[2];
void *buf[2];
dma_addr_t buf_dma[2];
unsigned long jiffies;
int mode;
char name[64];
char phys[64];
};
static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id);
static void ati_remote2_disconnect(struct usb_interface *interface);
static struct usb_driver ati_remote2_driver = {
.name = "ati_remote2",
.probe = ati_remote2_probe,
.disconnect = ati_remote2_disconnect,
.id_table = ati_remote2_id_table,
};
static int ati_remote2_open(struct input_dev *idev)
{
struct ati_remote2 *ar2 = idev->private;
int r;
r = usb_submit_urb(ar2->urb[0], GFP_KERNEL);
if (r) {
dev_err(&ar2->intf[0]->dev,
"%s: usb_submit_urb() = %d\n", __FUNCTION__, r);
return r;
}
r = usb_submit_urb(ar2->urb[1], GFP_KERNEL);
if (r) {
usb_kill_urb(ar2->urb[0]);
dev_err(&ar2->intf[1]->dev,
"%s: usb_submit_urb() = %d\n", __FUNCTION__, r);
return r;
}
return 0;
}
static void ati_remote2_close(struct input_dev *idev)
{
struct ati_remote2 *ar2 = idev->private;
usb_kill_urb(ar2->urb[0]);
usb_kill_urb(ar2->urb[1]);
}
static void ati_remote2_input_mouse(struct ati_remote2 *ar2)
{
struct input_dev *idev = ar2->idev;
u8 *data = ar2->buf[0];
if (data[0] > 4) {
dev_err(&ar2->intf[0]->dev,
"Unknown mode byte (%02x %02x %02x %02x)\n",
data[3], data[2], data[1], data[0]);
return;
}
if (!((1 << data[0]) & mode_mask))
return;
input_event(idev, EV_REL, REL_X, (s8) data[1]);
input_event(idev, EV_REL, REL_Y, (s8) data[2]);
input_sync(idev);
}
static int ati_remote2_lookup(unsigned int hw_code)
{
int i;
for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++)
if (ati_remote2_key_table[i].hw_code == hw_code)
return i;
return -1;
}
static void ati_remote2_input_key(struct ati_remote2 *ar2)
{
struct input_dev *idev = ar2->idev;
u8 *data = ar2->buf[1];
int hw_code, index;
if (data[0] > 4) {
dev_err(&ar2->intf[1]->dev,
"Unknown mode byte (%02x %02x %02x %02x)\n",
data[3], data[2], data[1], data[0]);
return;
}
hw_code = data[2];
/*
* Mode keys (AUX1-AUX4, PC) all generate the same code byte.
* Use the mode byte to figure out which one was pressed.
*/
if (hw_code == 0x3f) {
/*
* For some incomprehensible reason the mouse pad generates
* events which look identical to the events from the last
* pressed mode key. Naturally we don't want to generate key
* events for the mouse pad so we filter out any subsequent
* events from the same mode key.
*/
if (ar2->mode == data[0])
return;
if (data[1] == 0)
ar2->mode = data[0];
hw_code |= data[0] << 8;
}
if (!((1 << data[0]) & mode_mask))
return;
index = ati_remote2_lookup(hw_code);
if (index < 0) {
dev_err(&ar2->intf[1]->dev,
"Unknown code byte (%02x %02x %02x %02x)\n",
data[3], data[2], data[1], data[0]);
return;
}
switch (data[1]) {
case 0: /* release */
break;
case 1: /* press */
ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_DELAY]);
break;
case 2: /* repeat */
/* No repeat for mouse buttons. */
if (ati_remote2_key_table[index].key_code == BTN_LEFT ||
ati_remote2_key_table[index].key_code == BTN_RIGHT)
return;
if (!time_after_eq(jiffies, ar2->jiffies))
return;
ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_PERIOD]);
break;
default:
dev_err(&ar2->intf[1]->dev,
"Unknown state byte (%02x %02x %02x %02x)\n",
data[3], data[2], data[1], data[0]);
return;
}
input_event(idev, EV_KEY, ati_remote2_key_table[index].key_code, data[1]);
input_sync(idev);
}
static void ati_remote2_complete_mouse(struct urb *urb)
{
struct ati_remote2 *ar2 = urb->context;
int r;
switch (urb->status) {
case 0:
ati_remote2_input_mouse(ar2);
break;
case -ENOENT:
case -EILSEQ:
case -ECONNRESET:
case -ESHUTDOWN:
dev_dbg(&ar2->intf[0]->dev,
"%s(): urb status = %d\n", __FUNCTION__, urb->status);
return;
default:
dev_err(&ar2->intf[0]->dev,
"%s(): urb status = %d\n", __FUNCTION__, urb->status);
}
r = usb_submit_urb(urb, GFP_ATOMIC);
if (r)
dev_err(&ar2->intf[0]->dev,
"%s(): usb_submit_urb() = %d\n", __FUNCTION__, r);
}
static void ati_remote2_complete_key(struct urb *urb)
{
struct ati_remote2 *ar2 = urb->context;
int r;
switch (urb->status) {
case 0:
ati_remote2_input_key(ar2);
break;
case -ENOENT:
case -EILSEQ:
case -ECONNRESET:
case -ESHUTDOWN:
dev_dbg(&ar2->intf[1]->dev,
"%s(): urb status = %d\n", __FUNCTION__, urb->status);
return;
default:
dev_err(&ar2->intf[1]->dev,
"%s(): urb status = %d\n", __FUNCTION__, urb->status);
}
r = usb_submit_urb(urb, GFP_ATOMIC);
if (r)
dev_err(&ar2->intf[1]->dev,
"%s(): usb_submit_urb() = %d\n", __FUNCTION__, r);
}
static int ati_remote2_input_init(struct ati_remote2 *ar2)
{
struct input_dev *idev;
int i;
idev = input_allocate_device();
if (!idev)
return -ENOMEM;
ar2->idev = idev;
idev->private = ar2;
idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_REL);
idev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT);
idev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++)
set_bit(ati_remote2_key_table[i].key_code, idev->keybit);
idev->rep[REP_DELAY] = 250;
idev->rep[REP_PERIOD] = 33;
idev->open = ati_remote2_open;
idev->close = ati_remote2_close;
idev->name = ar2->name;
idev->phys = ar2->phys;
usb_to_input_id(ar2->udev, &idev->id);
idev->cdev.dev = &ar2->udev->dev;
i = input_register_device(idev);
if (i)
input_free_device(idev);
return i;
}
static int ati_remote2_urb_init(struct ati_remote2 *ar2)
{
struct usb_device *udev = ar2->udev;
int i, pipe, maxp;
for (i = 0; i < 2; i++) {
ar2->buf[i] = usb_buffer_alloc(udev, 4, GFP_KERNEL, &ar2->buf_dma[i]);
if (!ar2->buf[i])
return -ENOMEM;
ar2->urb[i] = usb_alloc_urb(0, GFP_KERNEL);
if (!ar2->urb[i])
return -ENOMEM;
pipe = usb_rcvintpipe(udev, ar2->ep[i]->bEndpointAddress);
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
maxp = maxp > 4 ? 4 : maxp;
usb_fill_int_urb(ar2->urb[i], udev, pipe, ar2->buf[i], maxp,
i ? ati_remote2_complete_key : ati_remote2_complete_mouse,
ar2, ar2->ep[i]->bInterval);
ar2->urb[i]->transfer_dma = ar2->buf_dma[i];
ar2->urb[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
}
return 0;
}
static void ati_remote2_urb_cleanup(struct ati_remote2 *ar2)
{
int i;
for (i = 0; i < 2; i++) {
usb_free_urb(ar2->urb[i]);
if (ar2->buf[i])
usb_buffer_free(ar2->udev, 4, ar2->buf[i], ar2->buf_dma[i]);
}
}
static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct usb_host_interface *alt = interface->cur_altsetting;
struct ati_remote2 *ar2;
int r;
if (alt->desc.bInterfaceNumber)
return -ENODEV;
ar2 = kzalloc(sizeof (struct ati_remote2), GFP_KERNEL);
if (!ar2)
return -ENOMEM;
ar2->udev = udev;
ar2->intf[0] = interface;
ar2->ep[0] = &alt->endpoint[0].desc;
ar2->intf[1] = usb_ifnum_to_if(udev, 1);
r = usb_driver_claim_interface(&ati_remote2_driver, ar2->intf[1], ar2);
if (r)
goto fail1;
alt = ar2->intf[1]->cur_altsetting;
ar2->ep[1] = &alt->endpoint[0].desc;
r = ati_remote2_urb_init(ar2);
if (r)
goto fail2;
usb_make_path(udev, ar2->phys, sizeof(ar2->phys));
strlcat(ar2->phys, "/input0", sizeof(ar2->phys));
strlcat(ar2->name, "ATI Remote Wonder II", sizeof(ar2->name));
r = ati_remote2_input_init(ar2);
if (r)
goto fail2;
usb_set_intfdata(interface, ar2);
return 0;
fail2:
ati_remote2_urb_cleanup(ar2);
usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
fail1:
kfree(ar2);
return r;
}
static void ati_remote2_disconnect(struct usb_interface *interface)
{
struct ati_remote2 *ar2;
struct usb_host_interface *alt = interface->cur_altsetting;
if (alt->desc.bInterfaceNumber)
return;
ar2 = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
input_unregister_device(ar2->idev);
ati_remote2_urb_cleanup(ar2);
usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
kfree(ar2);
}
static int __init ati_remote2_init(void)
{
int r;
r = usb_register(&ati_remote2_driver);
if (r)
printk(KERN_ERR "ati_remote2: usb_register() = %d\n", r);
else
printk(KERN_INFO "ati_remote2: " DRIVER_DESC " " DRIVER_VERSION "\n");
return r;
}
static void __exit ati_remote2_exit(void)
{
usb_deregister(&ati_remote2_driver);
}
module_init(ati_remote2_init);
module_exit(ati_remote2_exit);

1104
drivers/usb/input/gtco.c Normal file

File diff suppressed because it is too large Load Diff

1477
drivers/usb/input/hid-core.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,89 @@
/*
* $Id: hid-ff.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu 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 <linux/hid.h>
#include "usbhid.h"
/*
* This table contains pointers to initializers. To add support for new
* devices, you need to add the USB vendor and product ids here.
*/
struct hid_ff_initializer {
u16 idVendor;
u16 idProduct;
int (*init)(struct hid_device*);
};
/*
* We try pidff when no other driver is found because PID is the
* standards compliant way of implementing force feedback in HID.
* pidff_init() will quickly abort if the device doesn't appear to
* be a PID device
*/
static struct hid_ff_initializer inits[] = {
#ifdef CONFIG_LOGITECH_FF
{ 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */
{ 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */
{ 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */
{ 0x46d, 0xc294, hid_lgff_init }, /* Logitech Formula Force EX */
{ 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */
{ 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */
#endif
#ifdef CONFIG_PANTHERLORD_FF
{ 0x810, 0x0001, hid_plff_init },
#endif
#ifdef CONFIG_THRUSTMASTER_FF
{ 0x44f, 0xb304, hid_tmff_init },
#endif
#ifdef CONFIG_ZEROPLUS_FF
{ 0xc12, 0x0005, hid_zpff_init },
{ 0xc12, 0x0030, hid_zpff_init },
#endif
{ 0, 0, hid_pidff_init} /* Matches anything */
};
int hid_ff_init(struct hid_device* hid)
{
struct hid_ff_initializer *init;
int vendor = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idVendor);
int product = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idProduct);
for (init = inits; init->idVendor; init++)
if (init->idVendor == vendor && init->idProduct == product)
break;
return init->init(hid);
}
EXPORT_SYMBOL_GPL(hid_ff_init);

View File

@@ -0,0 +1,150 @@
/*
* Force feedback support for hid-compliant for some of the devices from
* Logitech, namely:
* - WingMan Cordless RumblePad
* - WingMan Force 3D
*
* Copyright (c) 2002-2004 Johann Deneux
* Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.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
*
* 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/usb.h>
#include <linux/hid.h>
#include "usbhid.h"
struct dev_type {
u16 idVendor;
u16 idProduct;
const signed short *ff;
};
static const signed short ff_rumble[] = {
FF_RUMBLE,
-1
};
static const signed short ff_joystick[] = {
FF_CONSTANT,
-1
};
static const struct dev_type devices[] = {
{ 0x046d, 0xc211, ff_rumble },
{ 0x046d, 0xc219, ff_rumble },
{ 0x046d, 0xc283, ff_joystick },
{ 0x046d, 0xc294, ff_joystick },
{ 0x046d, 0xc295, ff_joystick },
{ 0x046d, 0xca03, ff_joystick },
};
static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
{
struct hid_device *hid = dev->private;
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
int x, y;
unsigned int left, right;
#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
switch (effect->type) {
case FF_CONSTANT:
x = effect->u.ramp.start_level + 0x7f; /* 0x7f is center */
y = effect->u.ramp.end_level + 0x7f;
CLAMP(x);
CLAMP(y);
report->field[0]->value[0] = 0x51;
report->field[0]->value[1] = 0x08;
report->field[0]->value[2] = x;
report->field[0]->value[3] = y;
dbg("(x, y)=(%04x, %04x)", x, y);
usbhid_submit_report(hid, report, USB_DIR_OUT);
break;
case FF_RUMBLE:
right = effect->u.rumble.strong_magnitude;
left = effect->u.rumble.weak_magnitude;
right = right * 0xff / 0xffff;
left = left * 0xff / 0xffff;
CLAMP(left);
CLAMP(right);
report->field[0]->value[0] = 0x42;
report->field[0]->value[1] = 0x00;
report->field[0]->value[2] = left;
report->field[0]->value[3] = right;
dbg("(left, right)=(%04x, %04x)", left, right);
usbhid_submit_report(hid, report, USB_DIR_OUT);
break;
}
return 0;
}
int hid_lgff_init(struct hid_device* hid)
{
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct input_dev *dev = hidinput->input;
struct hid_report *report;
struct hid_field *field;
const signed short *ff_bits = ff_joystick;
int error;
int i;
/* Find the report to use */
if (list_empty(report_list)) {
err("No output report found");
return -1;
}
/* Check that the report looks ok */
report = list_entry(report_list->next, struct hid_report, list);
if (!report) {
err("NULL output report");
return -1;
}
field = report->field[0];
if (!field) {
err("NULL field");
return -1;
}
for (i = 0; i < ARRAY_SIZE(devices); i++) {
if (dev->id.vendor == devices[i].idVendor &&
dev->id.product == devices[i].idProduct) {
ff_bits = devices[i].ff;
break;
}
}
for (i = 0; ff_bits[i] >= 0; i++)
set_bit(ff_bits[i], dev->ffbit);
error = input_ff_create_memless(dev, NULL, hid_lgff_play);
if (error)
return error;
printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>\n");
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,129 @@
/*
* Force feedback support for PantherLord USB/PS2 2in1 Adapter devices
*
* Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.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
*/
/* #define DEBUG */
#define debug(format, arg...) pr_debug("hid-plff: " format "\n" , ## arg)
#include <linux/input.h>
#include <linux/usb.h>
#include <linux/hid.h>
#include "usbhid.h"
struct plff_device {
struct hid_report *report;
};
static int hid_plff_play(struct input_dev *dev, void *data,
struct ff_effect *effect)
{
struct hid_device *hid = dev->private;
struct plff_device *plff = data;
int left, right;
left = effect->u.rumble.strong_magnitude;
right = effect->u.rumble.weak_magnitude;
debug("called with 0x%04x 0x%04x", left, right);
left = left * 0x7f / 0xffff;
right = right * 0x7f / 0xffff;
plff->report->field[0]->value[2] = left;
plff->report->field[0]->value[3] = right;
debug("running with 0x%02x 0x%02x", left, right);
usbhid_submit_report(hid, plff->report, USB_DIR_OUT);
return 0;
}
int hid_plff_init(struct hid_device *hid)
{
struct plff_device *plff;
struct hid_report *report;
struct hid_input *hidinput;
struct list_head *report_list =
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct list_head *report_ptr = report_list;
struct input_dev *dev;
int error;
/* The device contains 2 output reports (one for each
HID_QUIRK_MULTI_INPUT device), both containing 1 field, which
contains 4 ff00.0002 usages and 4 16bit absolute values.
The 2 input reports also contain a field which contains
8 ff00.0001 usages and 8 boolean values. Their meaning is
currently unknown. */
if (list_empty(report_list)) {
printk(KERN_ERR "hid-plff: no output reports found\n");
return -ENODEV;
}
list_for_each_entry(hidinput, &hid->inputs, list) {
report_ptr = report_ptr->next;
if (report_ptr == report_list) {
printk(KERN_ERR "hid-plff: required output report is missing\n");
return -ENODEV;
}
report = list_entry(report_ptr, struct hid_report, list);
if (report->maxfield < 1) {
printk(KERN_ERR "hid-plff: no fields in the report\n");
return -ENODEV;
}
if (report->field[0]->report_count < 4) {
printk(KERN_ERR "hid-plff: not enough values in the field\n");
return -ENODEV;
}
plff = kzalloc(sizeof(struct plff_device), GFP_KERNEL);
if (!plff)
return -ENOMEM;
dev = hidinput->input;
set_bit(FF_RUMBLE, dev->ffbit);
error = input_ff_create_memless(dev, plff, hid_plff_play);
if (error) {
kfree(plff);
return error;
}
plff->report = report;
plff->report->field[0]->value[0] = 0x00;
plff->report->field[0]->value[1] = 0x00;
plff->report->field[0]->value[2] = 0x00;
plff->report->field[0]->value[3] = 0x00;
usbhid_submit_report(hid, plff->report, USB_DIR_OUT);
}
printk(KERN_INFO "hid-plff: Force feedback for PantherLord USB/PS2 "
"2in1 Adapters by Anssi Hannula <anssi.hannula@gmail.com>\n");
return 0;
}

View File

@@ -0,0 +1,147 @@
/*
* Force feedback support for various HID compliant devices by ThrustMaster:
* ThrustMaster FireStorm Dual Power 2
* and possibly others whose device ids haven't been added.
*
* Modified to support ThrustMaster devices by Zinx Verituse
* on 2003-01-25 from the Logitech force feedback driver,
* which is by Johann Deneux.
*
* Copyright (c) 2003 Zinx Verituse <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>
#undef DEBUG
#include <linux/usb.h>
#include <linux/hid.h>
#include "usbhid.h"
/* Usages for thrustmaster devices I know about */
#define THRUSTMASTER_USAGE_RUMBLE_LR (HID_UP_GENDESK | 0xbb)
struct tmff_device {
struct hid_report *report;
struct hid_field *rumble;
};
/* Changes values from 0 to 0xffff into values from minimum to maximum */
static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum)
{
int ret;
ret = (in * (maximum - minimum) / 0xffff) + minimum;
if (ret < minimum)
return minimum;
if (ret > maximum)
return maximum;
return ret;
}
static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
{
struct hid_device *hid = dev->private;
struct tmff_device *tmff = data;
int left, right; /* Rumbling */
left = hid_tmff_scale(effect->u.rumble.weak_magnitude,
tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
right = hid_tmff_scale(effect->u.rumble.strong_magnitude,
tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
tmff->rumble->value[0] = left;
tmff->rumble->value[1] = right;
dbg("(left,right)=(%08x, %08x)", left, right);
usbhid_submit_report(hid, tmff->report, USB_DIR_OUT);
return 0;
}
int hid_tmff_init(struct hid_device *hid)
{
struct tmff_device *tmff;
struct list_head *pos;
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
struct input_dev *input_dev = hidinput->input;
int error;
tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
if (!tmff)
return -ENOMEM;
/* Find the report to use */
__list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) {
struct hid_report *report = (struct hid_report *)pos;
int fieldnum;
for (fieldnum = 0; fieldnum < report->maxfield; ++fieldnum) {
struct hid_field *field = report->field[fieldnum];
if (field->maxusage <= 0)
continue;
switch (field->usage[0].hid) {
case THRUSTMASTER_USAGE_RUMBLE_LR:
if (field->report_count < 2) {
warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with report_count < 2");
continue;
}
if (field->logical_maximum == field->logical_minimum) {
warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with logical_maximum == logical_minimum");
continue;
}
if (tmff->report && tmff->report != report) {
warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR in other report");
continue;
}
if (tmff->rumble && tmff->rumble != field) {
warn("ignoring duplicate THRUSTMASTER_USAGE_RUMBLE_LR");
continue;
}
tmff->report = report;
tmff->rumble = field;
set_bit(FF_RUMBLE, input_dev->ffbit);
break;
default:
warn("ignoring unknown output usage %08x", field->usage[0].hid);
continue;
}
}
}
error = input_ff_create_memless(input_dev, tmff, hid_tmff_play);
if (error) {
kfree(tmff);
return error;
}
info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse <zinx@epicsol.org>");
return 0;
}

View File

@@ -0,0 +1,111 @@
/*
* Force feedback support for Zeroplus based devices
*
* Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@gmail.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
*/
/* #define DEBUG */
#define debug(format, arg...) pr_debug("hid-zpff: " format "\n" , ## arg)
#include <linux/input.h>
#include <linux/usb.h>
#include <linux/hid.h>
#include "usbhid.h"
struct zpff_device {
struct hid_report *report;
};
static int hid_zpff_play(struct input_dev *dev, void *data,
struct ff_effect *effect)
{
struct hid_device *hid = dev->private;
struct zpff_device *zpff = data;
int left, right;
/*
* The following is specified the other way around in the Zeroplus
* datasheet but the order below is correct for the XFX Executioner;
* however it is possible that the XFX Executioner is an exception
*/
left = effect->u.rumble.strong_magnitude;
right = effect->u.rumble.weak_magnitude;
debug("called with 0x%04x 0x%04x", left, right);
left = left * 0x7f / 0xffff;
right = right * 0x7f / 0xffff;
zpff->report->field[2]->value[0] = left;
zpff->report->field[3]->value[0] = right;
debug("running with 0x%02x 0x%02x", left, right);
usbhid_submit_report(hid, zpff->report, USB_DIR_OUT);
return 0;
}
int hid_zpff_init(struct hid_device *hid)
{
struct zpff_device *zpff;
struct hid_report *report;
struct hid_input *hidinput = list_entry(hid->inputs.next,
struct hid_input, list);
struct list_head *report_list =
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct input_dev *dev = hidinput->input;
int error;
if (list_empty(report_list)) {
printk(KERN_ERR "hid-zpff: no output report found\n");
return -ENODEV;
}
report = list_entry(report_list->next, struct hid_report, list);
if (report->maxfield < 4) {
printk(KERN_ERR "hid-zpff: not enough fields in report\n");
return -ENODEV;
}
zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL);
if (!zpff)
return -ENOMEM;
set_bit(FF_RUMBLE, dev->ffbit);
error = input_ff_create_memless(dev, zpff, hid_zpff_play);
if (error) {
kfree(zpff);
return error;
}
zpff->report = report;
zpff->report->field[0]->value[0] = 0x00;
zpff->report->field[1]->value[0] = 0x02;
zpff->report->field[2]->value[0] = 0x00;
zpff->report->field[3]->value[0] = 0x00;
usbhid_submit_report(hid, zpff->report, USB_DIR_OUT);
printk(KERN_INFO "Force feedback for Zeroplus based devices by "
"Anssi Hannula <anssi.hannula@gmail.com>\n");
return 0;
}

861
drivers/usb/input/hiddev.c Normal file
View File

@@ -0,0 +1,861 @@
/*
* 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/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 <linux/hid.h>
#include <linux/hiddev.h>
#include "usbhid.h"
#ifdef CONFIG_USB_DYNAMIC_MINORS
#define HIDDEV_MINOR_BASE 0
#define HIDDEV_MINORS 256
#else
#define HIDDEV_MINOR_BASE 96
#define HIDDEV_MINORS 16
#endif
#define HIDDEV_BUFFER_SIZE 64
struct hiddev {
int exist;
int open;
wait_queue_head_t wait;
struct hid_device *hid;
struct list_head list;
spinlock_t list_lock;
};
struct hiddev_list {
struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE];
int head;
int tail;
unsigned flags;
struct fasync_struct *fasync;
struct hiddev *hiddev;
struct list_head node;
};
static struct hiddev *hiddev_table[HIDDEV_MINORS];
/*
* Find a report, given the report's type and ID. The ID can be specified
* indirectly by REPORT_ID_FIRST (which returns the first report of the given
* type) or by (REPORT_ID_NEXT | old_id), which returns the next report of the
* given type which follows old_id.
*/
static struct hid_report *
hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo)
{
unsigned int flags = rinfo->report_id & ~HID_REPORT_ID_MASK;
unsigned int rid = rinfo->report_id & HID_REPORT_ID_MASK;
struct hid_report_enum *report_enum;
struct hid_report *report;
struct list_head *list;
if (rinfo->report_type < HID_REPORT_TYPE_MIN ||
rinfo->report_type > HID_REPORT_TYPE_MAX)
return NULL;
report_enum = hid->report_enum +
(rinfo->report_type - HID_REPORT_TYPE_MIN);
switch (flags) {
case 0: /* Nothing to do -- report_id is already set correctly */
break;
case HID_REPORT_ID_FIRST:
if (list_empty(&report_enum->report_list))
return NULL;
list = report_enum->report_list.next;
report = list_entry(list, struct hid_report, list);
rinfo->report_id = report->id;
break;
case HID_REPORT_ID_NEXT:
report = report_enum->report_id_hash[rid];
if (!report)
return NULL;
list = report->list.next;
if (list == &report_enum->report_list)
return NULL;
report = list_entry(list, struct hid_report, list);
rinfo->report_id = report->id;
break;
default:
return NULL;
}
return report_enum->report_id_hash[rinfo->report_id];
}
/*
* Perform an exhaustive search of the report table for a usage, given its
* type and usage id.
*/
static struct hid_field *
hiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref)
{
int i, j;
struct hid_report *report;
struct hid_report_enum *report_enum;
struct hid_field *field;
if (uref->report_type < HID_REPORT_TYPE_MIN ||
uref->report_type > HID_REPORT_TYPE_MAX)
return NULL;
report_enum = hid->report_enum +
(uref->report_type - HID_REPORT_TYPE_MIN);
list_for_each_entry(report, &report_enum->report_list, list) {
for (i = 0; i < report->maxfield; i++) {
field = report->field[i];
for (j = 0; j < field->maxusage; j++) {
if (field->usage[j].hid == uref->usage_code) {
uref->report_id = report->id;
uref->field_index = i;
uref->usage_index = j;
return field;
}
}
}
}
return NULL;
}
static void hiddev_send_event(struct hid_device *hid,
struct hiddev_usage_ref *uref)
{
struct hiddev *hiddev = hid->hiddev;
struct hiddev_list *list;
unsigned long flags;
spin_lock_irqsave(&hiddev->list_lock, flags);
list_for_each_entry(list, &hiddev->list, node) {
if (uref->field_index != HID_FIELD_INDEX_NONE ||
(list->flags & HIDDEV_FLAG_REPORT) != 0) {
list->buffer[list->head] = *uref;
list->head = (list->head + 1) &
(HIDDEV_BUFFER_SIZE - 1);
kill_fasync(&list->fasync, SIGIO, POLL_IN);
}
}
spin_unlock_irqrestore(&hiddev->list_lock, flags);
wake_up_interruptible(&hiddev->wait);
}
/*
* This is where hid.c calls into hiddev to pass an event that occurred over
* the interrupt pipe
*/
void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
unsigned type = field->report_type;
struct hiddev_usage_ref uref;
uref.report_type =
(type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT :
((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT :
((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0));
uref.report_id = field->report->id;
uref.field_index = field->index;
uref.usage_index = (usage - field->usage);
uref.usage_code = usage->hid;
uref.value = value;
hiddev_send_event(hid, &uref);
}
EXPORT_SYMBOL_GPL(hiddev_hid_event);
void hiddev_report_event(struct hid_device *hid, struct hid_report *report)
{
unsigned type = report->type;
struct hiddev_usage_ref uref;
memset(&uref, 0, sizeof(uref));
uref.report_type =
(type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT :
((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT :
((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0));
uref.report_id = report->id;
uref.field_index = HID_FIELD_INDEX_NONE;
hiddev_send_event(hid, &uref);
}
/*
* fasync file op
*/
static int hiddev_fasync(int fd, struct file *file, int on)
{
int retval;
struct hiddev_list *list = file->private_data;
retval = fasync_helper(fd, file, on, &list->fasync);
return retval < 0 ? retval : 0;
}
/*
* release file op
*/
static int hiddev_release(struct inode * inode, struct file * file)
{
struct hiddev_list *list = file->private_data;
unsigned long flags;
hiddev_fasync(-1, file, 0);
spin_lock_irqsave(&list->hiddev->list_lock, flags);
list_del(&list->node);
spin_unlock_irqrestore(&list->hiddev->list_lock, flags);
if (!--list->hiddev->open) {
if (list->hiddev->exist)
usbhid_close(list->hiddev->hid);
else
kfree(list->hiddev);
}
kfree(list);
return 0;
}
/*
* open file op
*/
static int hiddev_open(struct inode *inode, struct file *file)
{
struct hiddev_list *list;
unsigned long flags;
int i = iminor(inode) - HIDDEV_MINOR_BASE;
if (i >= HIDDEV_MINORS || !hiddev_table[i])
return -ENODEV;
if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL)))
return -ENOMEM;
list->hiddev = hiddev_table[i];
spin_lock_irqsave(&list->hiddev->list_lock, flags);
list_add_tail(&list->node, &hiddev_table[i]->list);
spin_unlock_irqrestore(&list->hiddev->list_lock, flags);
file->private_data = list;
if (!list->hiddev->open++)
if (list->hiddev->exist)
usbhid_open(hiddev_table[i]->hid);
return 0;
}
/*
* "write" file op
*/
static ssize_t hiddev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
{
return -EINVAL;
}
/*
* "read" file op
*/
static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
{
DECLARE_WAITQUEUE(wait, current);
struct hiddev_list *list = file->private_data;
int event_size;
int retval = 0;
event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ?
sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event);
if (count < event_size)
return 0;
while (retval == 0) {
if (list->head == list->tail) {
add_wait_queue(&list->hiddev->wait, &wait);
set_current_state(TASK_INTERRUPTIBLE);
while (list->head == list->tail) {
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
if (!list->hiddev->exist) {
retval = -EIO;
break;
}
schedule();
set_current_state(TASK_INTERRUPTIBLE);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&list->hiddev->wait, &wait);
}
if (retval)
return retval;
while (list->head != list->tail &&
retval + event_size <= count) {
if ((list->flags & HIDDEV_FLAG_UREF) == 0) {
if (list->buffer[list->tail].field_index !=
HID_FIELD_INDEX_NONE) {
struct hiddev_event event;
event.hid = list->buffer[list->tail].usage_code;
event.value = list->buffer[list->tail].value;
if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event)))
return -EFAULT;
retval += sizeof(struct hiddev_event);
}
} else {
if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE ||
(list->flags & HIDDEV_FLAG_REPORT) != 0) {
if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref)))
return -EFAULT;
retval += sizeof(struct hiddev_usage_ref);
}
}
list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1);
}
}
return retval;
}
/*
* "poll" file op
* No kernel lock - fine
*/
static unsigned int hiddev_poll(struct file *file, poll_table *wait)
{
struct hiddev_list *list = file->private_data;
poll_wait(file, &list->hiddev->wait, wait);
if (list->head != list->tail)
return POLLIN | POLLRDNORM;
if (!list->hiddev->exist)
return POLLERR | POLLHUP;
return 0;
}
/*
* "ioctl" file op
*/
static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct hiddev_list *list = file->private_data;
struct hiddev *hiddev = list->hiddev;
struct hid_device *hid = hiddev->hid;
struct usb_device *dev = hid_to_usb_dev(hid);
struct hiddev_collection_info cinfo;
struct hiddev_report_info rinfo;
struct hiddev_field_info finfo;
struct hiddev_usage_ref_multi *uref_multi = NULL;
struct hiddev_usage_ref *uref;
struct hiddev_devinfo dinfo;
struct hid_report *report;
struct hid_field *field;
struct usbhid_device *usbhid = hid->driver_data;
void __user *user_arg = (void __user *)arg;
int i;
if (!hiddev->exist)
return -EIO;
switch (cmd) {
case HIDIOCGVERSION:
return put_user(HID_VERSION, (int __user *)arg);
case HIDIOCAPPLICATION:
if (arg < 0 || arg >= hid->maxapplication)
return -EINVAL;
for (i = 0; i < hid->maxcollection; i++)
if (hid->collection[i].type ==
HID_COLLECTION_APPLICATION && arg-- == 0)
break;
if (i == hid->maxcollection)
return -EINVAL;
return hid->collection[i].usage;
case HIDIOCGDEVINFO:
dinfo.bustype = BUS_USB;
dinfo.busnum = dev->bus->busnum;
dinfo.devnum = dev->devnum;
dinfo.ifnum = usbhid->ifnum;
dinfo.vendor = le16_to_cpu(dev->descriptor.idVendor);
dinfo.product = le16_to_cpu(dev->descriptor.idProduct);
dinfo.version = le16_to_cpu(dev->descriptor.bcdDevice);
dinfo.num_applications = hid->maxapplication;
if (copy_to_user(user_arg, &dinfo, sizeof(dinfo)))
return -EFAULT;
return 0;
case HIDIOCGFLAG:
if (put_user(list->flags, (int __user *)arg))
return -EFAULT;
return 0;
case HIDIOCSFLAG:
{
int newflags;
if (get_user(newflags, (int __user *)arg))
return -EFAULT;
if ((newflags & ~HIDDEV_FLAGS) != 0 ||
((newflags & HIDDEV_FLAG_REPORT) != 0 &&
(newflags & HIDDEV_FLAG_UREF) == 0))
return -EINVAL;
list->flags = newflags;
return 0;
}
case HIDIOCGSTRING:
{
int idx, len;
char *buf;
if (get_user(idx, (int __user *)arg))
return -EFAULT;
if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL)
return -ENOMEM;
if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) {
kfree(buf);
return -EINVAL;
}
if (copy_to_user(user_arg+sizeof(int), buf, len+1)) {
kfree(buf);
return -EFAULT;
}
kfree(buf);
return len;
}
case HIDIOCINITREPORT:
usbhid_init_reports(hid);
return 0;
case HIDIOCGREPORT:
if (copy_from_user(&rinfo, user_arg, sizeof(rinfo)))
return -EFAULT;
if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT)
return -EINVAL;
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
return -EINVAL;
usbhid_submit_report(hid, report, USB_DIR_IN);
usbhid_wait_io(hid);
return 0;
case HIDIOCSREPORT:
if (copy_from_user(&rinfo, user_arg, sizeof(rinfo)))
return -EFAULT;
if (rinfo.report_type == HID_REPORT_TYPE_INPUT)
return -EINVAL;
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
return -EINVAL;
usbhid_submit_report(hid, report, USB_DIR_OUT);
usbhid_wait_io(hid);
return 0;
case HIDIOCGREPORTINFO:
if (copy_from_user(&rinfo, user_arg, sizeof(rinfo)))
return -EFAULT;
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
return -EINVAL;
rinfo.num_fields = report->maxfield;
if (copy_to_user(user_arg, &rinfo, sizeof(rinfo)))
return -EFAULT;
return 0;
case HIDIOCGFIELDINFO:
if (copy_from_user(&finfo, user_arg, sizeof(finfo)))
return -EFAULT;
rinfo.report_type = finfo.report_type;
rinfo.report_id = finfo.report_id;
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
return -EINVAL;
if (finfo.field_index >= report->maxfield)
return -EINVAL;
field = report->field[finfo.field_index];
memset(&finfo, 0, sizeof(finfo));
finfo.report_type = rinfo.report_type;
finfo.report_id = rinfo.report_id;
finfo.field_index = field->report_count - 1;
finfo.maxusage = field->maxusage;
finfo.flags = field->flags;
finfo.physical = field->physical;
finfo.logical = field->logical;
finfo.application = field->application;
finfo.logical_minimum = field->logical_minimum;
finfo.logical_maximum = field->logical_maximum;
finfo.physical_minimum = field->physical_minimum;
finfo.physical_maximum = field->physical_maximum;
finfo.unit_exponent = field->unit_exponent;
finfo.unit = field->unit;
if (copy_to_user(user_arg, &finfo, sizeof(finfo)))
return -EFAULT;
return 0;
case HIDIOCGUCODE:
uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
if (!uref_multi)
return -ENOMEM;
uref = &uref_multi->uref;
if (copy_from_user(uref, user_arg, sizeof(*uref)))
goto fault;
rinfo.report_type = uref->report_type;
rinfo.report_id = uref->report_id;
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
goto inval;
if (uref->field_index >= report->maxfield)
goto inval;
field = report->field[uref->field_index];
if (uref->usage_index >= field->maxusage)
goto inval;
uref->usage_code = field->usage[uref->usage_index].hid;
if (copy_to_user(user_arg, uref, sizeof(*uref)))
goto fault;
kfree(uref_multi);
return 0;
case HIDIOCGUSAGE:
case HIDIOCSUSAGE:
case HIDIOCGUSAGES:
case HIDIOCSUSAGES:
case HIDIOCGCOLLECTIONINDEX:
uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
if (!uref_multi)
return -ENOMEM;
uref = &uref_multi->uref;
if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
if (copy_from_user(uref_multi, user_arg,
sizeof(*uref_multi)))
goto fault;
} else {
if (copy_from_user(uref, user_arg, sizeof(*uref)))
goto fault;
}
if (cmd != HIDIOCGUSAGE &&
cmd != HIDIOCGUSAGES &&
uref->report_type == HID_REPORT_TYPE_INPUT)
goto inval;
if (uref->report_id == HID_REPORT_ID_UNKNOWN) {
field = hiddev_lookup_usage(hid, uref);
if (field == NULL)
goto inval;
} else {
rinfo.report_type = uref->report_type;
rinfo.report_id = uref->report_id;
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
goto inval;
if (uref->field_index >= report->maxfield)
goto inval;
field = report->field[uref->field_index];
if (cmd == HIDIOCGCOLLECTIONINDEX) {
if (uref->usage_index >= field->maxusage)
goto inval;
} else if (uref->usage_index >= field->report_count)
goto inval;
else if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) &&
(uref_multi->num_values > HID_MAX_MULTI_USAGES ||
uref->usage_index + uref_multi->num_values > field->report_count))
goto inval;
}
switch (cmd) {
case HIDIOCGUSAGE:
uref->value = field->value[uref->usage_index];
if (copy_to_user(user_arg, uref, sizeof(*uref)))
goto fault;
goto goodreturn;
case HIDIOCSUSAGE:
field->value[uref->usage_index] = uref->value;
goto goodreturn;
case HIDIOCGCOLLECTIONINDEX:
kfree(uref_multi);
return field->usage[uref->usage_index].collection_index;
case HIDIOCGUSAGES:
for (i = 0; i < uref_multi->num_values; i++)
uref_multi->values[i] =
field->value[uref->usage_index + i];
if (copy_to_user(user_arg, uref_multi,
sizeof(*uref_multi)))
goto fault;
goto goodreturn;
case HIDIOCSUSAGES:
for (i = 0; i < uref_multi->num_values; i++)
field->value[uref->usage_index + i] =
uref_multi->values[i];
goto goodreturn;
}
goodreturn:
kfree(uref_multi);
return 0;
fault:
kfree(uref_multi);
return -EFAULT;
inval:
kfree(uref_multi);
return -EINVAL;
case HIDIOCGCOLLECTIONINFO:
if (copy_from_user(&cinfo, user_arg, sizeof(cinfo)))
return -EFAULT;
if (cinfo.index >= hid->maxcollection)
return -EINVAL;
cinfo.type = hid->collection[cinfo.index].type;
cinfo.usage = hid->collection[cinfo.index].usage;
cinfo.level = hid->collection[cinfo.index].level;
if (copy_to_user(user_arg, &cinfo, sizeof(cinfo)))
return -EFAULT;
return 0;
default:
if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ)
return -EINVAL;
if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) {
int len;
if (!hid->name)
return 0;
len = strlen(hid->name) + 1;
if (len > _IOC_SIZE(cmd))
len = _IOC_SIZE(cmd);
return copy_to_user(user_arg, hid->name, len) ?
-EFAULT : len;
}
if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGPHYS(0))) {
int len;
if (!hid->phys)
return 0;
len = strlen(hid->phys) + 1;
if (len > _IOC_SIZE(cmd))
len = _IOC_SIZE(cmd);
return copy_to_user(user_arg, hid->phys, len) ?
-EFAULT : len;
}
}
return -EINVAL;
}
static const struct file_operations hiddev_fops = {
.owner = THIS_MODULE,
.read = hiddev_read,
.write = hiddev_write,
.poll = hiddev_poll,
.open = hiddev_open,
.release = hiddev_release,
.ioctl = hiddev_ioctl,
.fasync = hiddev_fasync,
};
static struct usb_class_driver hiddev_class = {
.name = "hiddev%d",
.fops = &hiddev_fops,
.minor_base = HIDDEV_MINOR_BASE,
};
/*
* This is where hid.c calls us to connect a hid device to the hiddev driver
*/
int hiddev_connect(struct hid_device *hid)
{
struct hiddev *hiddev;
struct usbhid_device *usbhid = hid->driver_data;
int i;
int retval;
for (i = 0; i < hid->maxcollection; i++)
if (hid->collection[i].type ==
HID_COLLECTION_APPLICATION &&
!IS_INPUT_APPLICATION(hid->collection[i].usage))
break;
if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDDEV) == 0)
return -1;
if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL)))
return -1;
retval = usb_register_dev(usbhid->intf, &hiddev_class);
if (retval) {
err("Not able to get a minor for this device.");
kfree(hiddev);
return -1;
}
init_waitqueue_head(&hiddev->wait);
INIT_LIST_HEAD(&hiddev->list);
spin_lock_init(&hiddev->list_lock);
hiddev->hid = hid;
hiddev->exist = 1;
hid->minor = usbhid->intf->minor;
hid->hiddev = hiddev;
hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev;
return 0;
}
/*
* This is where hid.c calls us to disconnect a hiddev device from the
* corresponding hid device (usually because the usb device has disconnected)
*/
static struct usb_class_driver hiddev_class;
void hiddev_disconnect(struct hid_device *hid)
{
struct hiddev *hiddev = hid->hiddev;
struct usbhid_device *usbhid = hid->driver_data;
hiddev->exist = 0;
hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL;
usb_deregister_dev(usbhid->intf, &hiddev_class);
if (hiddev->open) {
usbhid_close(hiddev->hid);
wake_up_interruptible(&hiddev->wait);
} else {
kfree(hiddev);
}
}
/* Currently this driver is a USB driver. It's not a conventional one in
* the sense that it doesn't probe at the USB level. Instead it waits to
* be connected by HID through the hiddev_connect / hiddev_disconnect
* routines. The reason to register as a USB device is to gain part of the
* minor number space from the USB major.
*
* In theory, should the HID code be generalized to more than one physical
* medium (say, IEEE 1384), this driver will probably need to register its
* own major number, and in doing so, no longer need to register with USB.
* At that point the probe routine and hiddev_driver struct below will no
* longer be useful.
*/
/* We never attach in this manner, and rely on HID to connect us. This
* is why there is no disconnect routine defined in the usb_driver either.
*/
static int hiddev_usbd_probe(struct usb_interface *intf,
const struct usb_device_id *hiddev_info)
{
return -ENODEV;
}
static /* const */ struct usb_driver hiddev_driver = {
.name = "hiddev",
.probe = hiddev_usbd_probe,
};
int __init hiddev_init(void)
{
return usb_register(&hiddev_driver);
}
void hiddev_exit(void)
{
usb_deregister(&hiddev_driver);
}

View File

@@ -0,0 +1,271 @@
/******************************************************************************
* itmtouch.c -- Driver for ITM touchscreen panel
*
* 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 Chris Collins <xfire-itmtouch@xware.cx>.
*
* Kudos to ITM for providing me with the datasheet for the panel,
* even though it was a day later than I had finished writing this
* driver.
*
* It has meant that I've been able to correct my interpretation of the
* protocol packets however.
*
* CC -- 2003/9/29
*
* History
* 1.0 & 1.1 2003 (CC) vojtech@suse.cz
* Original version for 2.4.x kernels
*
* 1.2 02/03/2005 (HCE) hc@mivu.no
* Complete rewrite to support Linux 2.6.10, thanks to mtouchusb.c for hints.
* Unfortunately no calibration support at this time.
*
* 1.2.1 09/03/2005 (HCE) hc@mivu.no
* Code cleanup and adjusting syntax to start matching kernel standards
*
* 1.2.2 10/05/2006 (MJA) massad@gmail.com
* Flag for detecting if the screen was being touch was incorrectly
* inverted, so no touch events were being detected.
*
*****************************************************************************/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb/input.h>
/* only an 8 byte buffer necessary for a single packet */
#define ITM_BUFSIZE 8
#define PATH_SIZE 64
#define USB_VENDOR_ID_ITMINC 0x0403
#define USB_PRODUCT_ID_TOUCHPANEL 0xf9e9
#define DRIVER_AUTHOR "Hans-Christian Egtvedt <hc@mivu.no>"
#define DRIVER_VERSION "v1.2.2"
#define DRIVER_DESC "USB ITM Inc Touch Panel Driver"
#define DRIVER_LICENSE "GPL"
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE( DRIVER_LICENSE );
struct itmtouch_dev {
struct usb_device *usbdev; /* usb device */
struct input_dev *inputdev; /* input device */
struct urb *readurb; /* urb */
char rbuf[ITM_BUFSIZE]; /* data */
int users;
char name[128];
char phys[64];
};
static struct usb_device_id itmtouch_ids [] = {
{ USB_DEVICE(USB_VENDOR_ID_ITMINC, USB_PRODUCT_ID_TOUCHPANEL) },
{ }
};
static void itmtouch_irq(struct urb *urb)
{
struct itmtouch_dev *itmtouch = urb->context;
unsigned char *data = urb->transfer_buffer;
struct input_dev *dev = itmtouch->inputdev;
int retval;
switch (urb->status) {
case 0:
/* success */
break;
case -ETIME:
/* 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 pressure has been released, then don't report X/Y */
if (!(data[7] & 0x20)) {
input_report_abs(dev, ABS_X, (data[0] & 0x1F) << 7 | (data[3] & 0x7F));
input_report_abs(dev, ABS_Y, (data[1] & 0x1F) << 7 | (data[4] & 0x7F));
}
input_report_abs(dev, ABS_PRESSURE, (data[2] & 1) << 7 | (data[5] & 0x7F));
input_report_key(dev, BTN_TOUCH, ~data[7] & 0x20);
input_sync(dev);
exit:
retval = usb_submit_urb (urb, GFP_ATOMIC);
if (retval)
printk(KERN_ERR "%s - usb_submit_urb failed with result: %d",
__FUNCTION__, retval);
}
static int itmtouch_open(struct input_dev *input)
{
struct itmtouch_dev *itmtouch = input->private;
itmtouch->readurb->dev = itmtouch->usbdev;
if (usb_submit_urb(itmtouch->readurb, GFP_KERNEL))
return -EIO;
return 0;
}
static void itmtouch_close(struct input_dev *input)
{
struct itmtouch_dev *itmtouch = input->private;
usb_kill_urb(itmtouch->readurb);
}
static int itmtouch_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct itmtouch_dev *itmtouch;
struct input_dev *input_dev;
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_device *udev = interface_to_usbdev(intf);
unsigned int pipe;
unsigned int maxp;
interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;
itmtouch = kzalloc(sizeof(struct itmtouch_dev), GFP_KERNEL);
input_dev = input_allocate_device();
if (!itmtouch || !input_dev) {
err("%s - Out of memory.", __FUNCTION__);
goto fail;
}
itmtouch->usbdev = udev;
itmtouch->inputdev = input_dev;
if (udev->manufacturer)
strlcpy(itmtouch->name, udev->manufacturer, sizeof(itmtouch->name));
if (udev->product) {
if (udev->manufacturer)
strlcat(itmtouch->name, " ", sizeof(itmtouch->name));
strlcat(itmtouch->name, udev->product, sizeof(itmtouch->name));
}
if (!strlen(itmtouch->name))
sprintf(itmtouch->name, "USB ITM touchscreen");
usb_make_path(udev, itmtouch->phys, sizeof(itmtouch->phys));
strlcpy(itmtouch->phys, "/input0", sizeof(itmtouch->phys));
input_dev->name = itmtouch->name;
input_dev->phys = itmtouch->phys;
usb_to_input_id(udev, &input_dev->id);
input_dev->cdev.dev = &intf->dev;
input_dev->private = itmtouch;
input_dev->open = itmtouch_open;
input_dev->close = itmtouch_close;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
input_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
/* device limits */
/* as specified by the ITM datasheet, X and Y are 12bit,
* Z (pressure) is 8 bit. However, the fields are defined up
* to 14 bits for future possible expansion.
*/
input_set_abs_params(input_dev, ABS_X, 0, 0x0FFF, 2, 0);
input_set_abs_params(input_dev, ABS_Y, 0, 0x0FFF, 2, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0xFF, 2, 0);
/* initialise the URB so we can read from the transport stream */
pipe = usb_rcvintpipe(itmtouch->usbdev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
if (maxp > ITM_BUFSIZE)
maxp = ITM_BUFSIZE;
itmtouch->readurb = usb_alloc_urb(0, GFP_KERNEL);
if (!itmtouch->readurb) {
dbg("%s - usb_alloc_urb failed: itmtouch->readurb", __FUNCTION__);
goto fail;
}
usb_fill_int_urb(itmtouch->readurb, itmtouch->usbdev, pipe, itmtouch->rbuf,
maxp, itmtouch_irq, itmtouch, endpoint->bInterval);
input_register_device(itmtouch->inputdev);
usb_set_intfdata(intf, itmtouch);
return 0;
fail: input_free_device(input_dev);
kfree(itmtouch);
return -ENOMEM;
}
static void itmtouch_disconnect(struct usb_interface *intf)
{
struct itmtouch_dev *itmtouch = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL);
if (itmtouch) {
input_unregister_device(itmtouch->inputdev);
usb_kill_urb(itmtouch->readurb);
usb_free_urb(itmtouch->readurb);
kfree(itmtouch);
}
}
MODULE_DEVICE_TABLE(usb, itmtouch_ids);
static struct usb_driver itmtouch_driver = {
.name = "itmtouch",
.probe = itmtouch_probe,
.disconnect = itmtouch_disconnect,
.id_table = itmtouch_ids,
};
static int __init itmtouch_init(void)
{
info(DRIVER_DESC " " DRIVER_VERSION);
info(DRIVER_AUTHOR);
return usb_register(&itmtouch_driver);
}
static void __exit itmtouch_exit(void)
{
usb_deregister(&itmtouch_driver);
}
module_init(itmtouch_init);
module_exit(itmtouch_exit);

220
drivers/usb/input/kbtab.c Normal file
View File

@@ -0,0 +1,220 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb/input.h>
#include <asm/unaligned.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 x, y;
int button;
int pressure;
__u32 serial[2];
char phys[32];
};
static void kbtab_irq(struct urb *urb)
{
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;
kbtab->irq->dev = kbtab->usbdev;
if (usb_submit_urb(kbtab->irq, GFP_KERNEL))
return -EIO;
return 0;
}
static void kbtab_close(struct input_dev *dev)
{
struct kbtab *kbtab = dev->private;
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;
struct input_dev *input_dev;
kbtab = kzalloc(sizeof(struct kbtab), GFP_KERNEL);
input_dev = input_allocate_device();
if (!kbtab || !input_dev)
goto fail1;
kbtab->data = usb_buffer_alloc(dev, 8, GFP_KERNEL, &kbtab->data_dma);
if (!kbtab->data)
goto fail1;
kbtab->irq = usb_alloc_urb(0, GFP_KERNEL);
if (!kbtab->irq)
goto fail2;
kbtab->usbdev = dev;
kbtab->dev = input_dev;
usb_make_path(dev, kbtab->phys, sizeof(kbtab->phys));
strlcat(kbtab->phys, "/input0", sizeof(kbtab->phys));
input_dev->name = "KB Gear Tablet";
input_dev->phys = kbtab->phys;
usb_to_input_id(dev, &input_dev->id);
input_dev->cdev.dev = &intf->dev;
input_dev->private = kbtab;
input_dev->open = kbtab_open;
input_dev->close = kbtab_close;
input_dev->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_MSC);
input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH);
input_dev->mscbit[0] |= BIT(MSC_SERIAL);
input_set_abs_params(input_dev, ABS_X, 0, 0x2000, 4, 0);
input_set_abs_params(input_dev, ABS_Y, 0, 0x1750, 4, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0xff, 0, 0);
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);
usb_set_intfdata(intf, kbtab);
return 0;
fail2: usb_buffer_free(dev, 10, kbtab->data, kbtab->data_dma);
fail1: input_free_device(input_dev);
kfree(kbtab);
return -ENOMEM;
}
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 = {
.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);

View File

@@ -0,0 +1,589 @@
/*
* keyspan_remote: USB driver for the Keyspan DMR
*
* Copyright (C) 2005 Zymeta Corporation - Michael Downey (downey@zymeta.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, version 2.
*
* This driver has been put together with the support of Innosys, Inc.
* and Keyspan, Inc the manufacturers of the Keyspan USB DMR product.
*/
#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/usb/input.h>
#define DRIVER_VERSION "v0.1"
#define DRIVER_AUTHOR "Michael Downey <downey@zymeta.com>"
#define DRIVER_DESC "Driver for the USB Keyspan remote control."
#define DRIVER_LICENSE "GPL"
/* Parameters that can be passed to the driver. */
static int debug;
module_param(debug, int, 0444);
MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
/* Vendor and product ids */
#define USB_KEYSPAN_VENDOR_ID 0x06CD
#define USB_KEYSPAN_PRODUCT_UIA11 0x0202
/* Defines for converting the data from the remote. */
#define ZERO 0x18
#define ZERO_MASK 0x1F /* 5 bits for a 0 */
#define ONE 0x3C
#define ONE_MASK 0x3F /* 6 bits for a 1 */
#define SYNC 0x3F80
#define SYNC_MASK 0x3FFF /* 14 bits for a SYNC sequence */
#define STOP 0x00
#define STOP_MASK 0x1F /* 5 bits for the STOP sequence */
#define GAP 0xFF
#define RECV_SIZE 8 /* The UIA-11 type have a 8 byte limit. */
/* table of devices that work with this driver */
static struct usb_device_id keyspan_table[] = {
{ USB_DEVICE(USB_KEYSPAN_VENDOR_ID, USB_KEYSPAN_PRODUCT_UIA11) },
{ } /* Terminating entry */
};
/* Structure to store all the real stuff that a remote sends to us. */
struct keyspan_message {
u16 system;
u8 button;
u8 toggle;
};
/* Structure used for all the bit testing magic needed to be done. */
struct bit_tester {
u32 tester;
int len;
int pos;
int bits_left;
u8 buffer[32];
};
/* Structure to hold all of our driver specific stuff */
struct usb_keyspan {
char name[128];
char phys[64];
struct usb_device* udev;
struct input_dev *input;
struct usb_interface* interface;
struct usb_endpoint_descriptor* in_endpoint;
struct urb* irq_urb;
int open;
dma_addr_t in_dma;
unsigned char* in_buffer;
/* variables used to parse messages from remote. */
struct bit_tester data;
int stage;
int toggle;
};
/*
* Table that maps the 31 possible keycodes to input keys.
* Currently there are 15 and 17 button models so RESERVED codes
* are blank areas in the mapping.
*/
static const int keyspan_key_table[] = {
KEY_RESERVED, /* 0 is just a place holder. */
KEY_RESERVED,
KEY_STOP,
KEY_PLAYCD,
KEY_RESERVED,
KEY_PREVIOUSSONG,
KEY_REWIND,
KEY_FORWARD,
KEY_NEXTSONG,
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
KEY_PAUSE,
KEY_VOLUMEUP,
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
KEY_VOLUMEDOWN,
KEY_RESERVED,
KEY_UP,
KEY_RESERVED,
KEY_MUTE,
KEY_LEFT,
KEY_ENTER,
KEY_RIGHT,
KEY_RESERVED,
KEY_RESERVED,
KEY_DOWN,
KEY_RESERVED,
KEY_KPASTERISK,
KEY_RESERVED,
KEY_MENU
};
static struct usb_driver keyspan_driver;
/*
* Debug routine that prints out what we've received from the remote.
*/
static void keyspan_print(struct usb_keyspan* dev) /*unsigned char* data)*/
{
char codes[4 * RECV_SIZE];
int i;
for (i = 0; i < RECV_SIZE; i++)
snprintf(codes + i * 3, 4, "%02x ", dev->in_buffer[i]);
dev_info(&dev->udev->dev, "%s\n", codes);
}
/*
* Routine that manages the bit_tester structure. It makes sure that there are
* at least bits_needed bits loaded into the tester.
*/
static int keyspan_load_tester(struct usb_keyspan* dev, int bits_needed)
{
if (dev->data.bits_left >= bits_needed)
return 0;
/*
* Somehow we've missed the last message. The message will be repeated
* though so it's not too big a deal
*/
if (dev->data.pos >= dev->data.len) {
dev_dbg(&dev->udev->dev,
"%s - Error ran out of data. pos: %d, len: %d\n",
__FUNCTION__, dev->data.pos, dev->data.len);
return -1;
}
/* Load as much as we can into the tester. */
while ((dev->data.bits_left + 7 < (sizeof(dev->data.tester) * 8)) &&
(dev->data.pos < dev->data.len)) {
dev->data.tester += (dev->data.buffer[dev->data.pos++] << dev->data.bits_left);
dev->data.bits_left += 8;
}
return 0;
}
/*
* Routine that handles all the logic needed to parse out the message from the remote.
*/
static void keyspan_check_data(struct usb_keyspan *remote)
{
int i;
int found = 0;
struct keyspan_message message;
switch(remote->stage) {
case 0:
/*
* In stage 0 we want to find the start of a message. The remote sends a 0xFF as filler.
* So the first byte that isn't a FF should be the start of a new message.
*/
for (i = 0; i < RECV_SIZE && remote->in_buffer[i] == GAP; ++i);
if (i < RECV_SIZE) {
memcpy(remote->data.buffer, remote->in_buffer, RECV_SIZE);
remote->data.len = RECV_SIZE;
remote->data.pos = 0;
remote->data.tester = 0;
remote->data.bits_left = 0;
remote->stage = 1;
}
break;
case 1:
/*
* Stage 1 we should have 16 bytes and should be able to detect a
* SYNC. The SYNC is 14 bits, 7 0's and then 7 1's.
*/
memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE);
remote->data.len += RECV_SIZE;
found = 0;
while ((remote->data.bits_left >= 14 || remote->data.pos < remote->data.len) && !found) {
for (i = 0; i < 8; ++i) {
if (keyspan_load_tester(remote, 14) != 0) {
remote->stage = 0;
return;
}
if ((remote->data.tester & SYNC_MASK) == SYNC) {
remote->data.tester = remote->data.tester >> 14;
remote->data.bits_left -= 14;
found = 1;
break;
} else {
remote->data.tester = remote->data.tester >> 1;
--remote->data.bits_left;
}
}
}
if (!found) {
remote->stage = 0;
remote->data.len = 0;
} else {
remote->stage = 2;
}
break;
case 2:
/*
* Stage 2 we should have 24 bytes which will be enough for a full
* message. We need to parse out the system code, button code,
* toggle code, and stop.
*/
memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE);
remote->data.len += RECV_SIZE;
message.system = 0;
for (i = 0; i < 9; i++) {
keyspan_load_tester(remote, 6);
if ((remote->data.tester & ZERO_MASK) == ZERO) {
message.system = message.system << 1;
remote->data.tester = remote->data.tester >> 5;
remote->data.bits_left -= 5;
} else if ((remote->data.tester & ONE_MASK) == ONE) {
message.system = (message.system << 1) + 1;
remote->data.tester = remote->data.tester >> 6;
remote->data.bits_left -= 6;
} else {
err("%s - Unknown sequence found in system data.\n", __FUNCTION__);
remote->stage = 0;
return;
}
}
message.button = 0;
for (i = 0; i < 5; i++) {
keyspan_load_tester(remote, 6);
if ((remote->data.tester & ZERO_MASK) == ZERO) {
message.button = message.button << 1;
remote->data.tester = remote->data.tester >> 5;
remote->data.bits_left -= 5;
} else if ((remote->data.tester & ONE_MASK) == ONE) {
message.button = (message.button << 1) + 1;
remote->data.tester = remote->data.tester >> 6;
remote->data.bits_left -= 6;
} else {
err("%s - Unknown sequence found in button data.\n", __FUNCTION__);
remote->stage = 0;
return;
}
}
keyspan_load_tester(remote, 6);
if ((remote->data.tester & ZERO_MASK) == ZERO) {
message.toggle = 0;
remote->data.tester = remote->data.tester >> 5;
remote->data.bits_left -= 5;
} else if ((remote->data.tester & ONE_MASK) == ONE) {
message.toggle = 1;
remote->data.tester = remote->data.tester >> 6;
remote->data.bits_left -= 6;
} else {
err("%s - Error in message, invalid toggle.\n", __FUNCTION__);
remote->stage = 0;
return;
}
keyspan_load_tester(remote, 5);
if ((remote->data.tester & STOP_MASK) == STOP) {
remote->data.tester = remote->data.tester >> 5;
remote->data.bits_left -= 5;
} else {
err("Bad message recieved, no stop bit found.\n");
}
dev_dbg(&remote->udev->dev,
"%s found valid message: system: %d, button: %d, toggle: %d\n",
__FUNCTION__, message.system, message.button, message.toggle);
if (message.toggle != remote->toggle) {
input_report_key(remote->input, keyspan_key_table[message.button], 1);
input_report_key(remote->input, keyspan_key_table[message.button], 0);
input_sync(remote->input);
remote->toggle = message.toggle;
}
remote->stage = 0;
break;
}
}
/*
* Routine for sending all the initialization messages to the remote.
*/
static int keyspan_setup(struct usb_device* dev)
{
int retval = 0;
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x11, 0x40, 0x5601, 0x0, NULL, 0, 0);
if (retval) {
dev_dbg(&dev->dev, "%s - failed to set bit rate due to error: %d\n",
__FUNCTION__, retval);
return(retval);
}
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x44, 0x40, 0x0, 0x0, NULL, 0, 0);
if (retval) {
dev_dbg(&dev->dev, "%s - failed to set resume sensitivity due to error: %d\n",
__FUNCTION__, retval);
return(retval);
}
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x22, 0x40, 0x0, 0x0, NULL, 0, 0);
if (retval) {
dev_dbg(&dev->dev, "%s - failed to turn receive on due to error: %d\n",
__FUNCTION__, retval);
return(retval);
}
dev_dbg(&dev->dev, "%s - Setup complete.\n", __FUNCTION__);
return(retval);
}
/*
* Routine used to handle a new message that has come in.
*/
static void keyspan_irq_recv(struct urb *urb)
{
struct usb_keyspan *dev = urb->context;
int retval;
/* Check our status in case we need to bail out early. */
switch (urb->status) {
case 0:
break;
/* Device went away so don't keep trying to read from it. */
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
return;
default:
goto resubmit;
break;
}
if (debug)
keyspan_print(dev);
keyspan_check_data(dev);
resubmit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
err ("%s - usb_submit_urb failed with result: %d", __FUNCTION__, retval);
}
static int keyspan_open(struct input_dev *dev)
{
struct usb_keyspan *remote = dev->private;
remote->irq_urb->dev = remote->udev;
if (usb_submit_urb(remote->irq_urb, GFP_KERNEL))
return -EIO;
return 0;
}
static void keyspan_close(struct input_dev *dev)
{
struct usb_keyspan *remote = dev->private;
usb_kill_urb(remote->irq_urb);
}
static struct usb_endpoint_descriptor *keyspan_get_in_endpoint(struct usb_host_interface *iface)
{
struct usb_endpoint_descriptor *endpoint;
int i;
for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
endpoint = &iface->endpoint[i].desc;
if (usb_endpoint_is_int_in(endpoint)) {
/* we found our interrupt in endpoint */
return endpoint;
}
}
return NULL;
}
/*
* Routine that sets up the driver to handle a specific USB device detected on the bus.
*/
static int keyspan_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct usb_endpoint_descriptor *endpoint;
struct usb_keyspan *remote;
struct input_dev *input_dev;
int i, retval;
endpoint = keyspan_get_in_endpoint(interface->cur_altsetting);
if (!endpoint)
return -ENODEV;
remote = kzalloc(sizeof(*remote), GFP_KERNEL);
input_dev = input_allocate_device();
if (!remote || !input_dev) {
retval = -ENOMEM;
goto fail1;
}
remote->udev = udev;
remote->input = input_dev;
remote->interface = interface;
remote->in_endpoint = endpoint;
remote->toggle = -1; /* Set to -1 so we will always not match the toggle from the first remote message. */
remote->in_buffer = usb_buffer_alloc(udev, RECV_SIZE, GFP_ATOMIC, &remote->in_dma);
if (!remote->in_buffer) {
retval = -ENOMEM;
goto fail1;
}
remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!remote->irq_urb) {
retval = -ENOMEM;
goto fail2;
}
retval = keyspan_setup(udev);
if (retval) {
retval = -ENODEV;
goto fail3;
}
if (udev->manufacturer)
strlcpy(remote->name, udev->manufacturer, sizeof(remote->name));
if (udev->product) {
if (udev->manufacturer)
strlcat(remote->name, " ", sizeof(remote->name));
strlcat(remote->name, udev->product, sizeof(remote->name));
}
if (!strlen(remote->name))
snprintf(remote->name, sizeof(remote->name),
"USB Keyspan Remote %04x:%04x",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
usb_make_path(udev, remote->phys, sizeof(remote->phys));
strlcat(remote->phys, "/input0", sizeof(remote->phys));
input_dev->name = remote->name;
input_dev->phys = remote->phys;
usb_to_input_id(udev, &input_dev->id);
input_dev->cdev.dev = &interface->dev;
input_dev->evbit[0] = BIT(EV_KEY); /* We will only report KEY events. */
for (i = 0; i < ARRAY_SIZE(keyspan_key_table); i++)
if (keyspan_key_table[i] != KEY_RESERVED)
set_bit(keyspan_key_table[i], input_dev->keybit);
input_dev->private = remote;
input_dev->open = keyspan_open;
input_dev->close = keyspan_close;
/*
* Initialize the URB to access the device. The urb gets sent to the device in keyspan_open()
*/
usb_fill_int_urb(remote->irq_urb,
remote->udev, usb_rcvintpipe(remote->udev, remote->in_endpoint->bEndpointAddress),
remote->in_buffer, RECV_SIZE, keyspan_irq_recv, remote,
remote->in_endpoint->bInterval);
remote->irq_urb->transfer_dma = remote->in_dma;
remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* we can register the device now, as it is ready */
input_register_device(remote->input);
/* save our data pointer in this interface device */
usb_set_intfdata(interface, remote);
return 0;
fail3: usb_free_urb(remote->irq_urb);
fail2: usb_buffer_free(udev, RECV_SIZE, remote->in_buffer, remote->in_dma);
fail1: kfree(remote);
input_free_device(input_dev);
return retval;
}
/*
* Routine called when a device is disconnected from the USB.
*/
static void keyspan_disconnect(struct usb_interface *interface)
{
struct usb_keyspan *remote;
remote = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
if (remote) { /* We have a valid driver structure so clean up everything we allocated. */
input_unregister_device(remote->input);
usb_kill_urb(remote->irq_urb);
usb_free_urb(remote->irq_urb);
usb_buffer_free(remote->udev, RECV_SIZE, remote->in_buffer, remote->in_dma);
kfree(remote);
}
}
/*
* Standard driver set up sections
*/
static struct usb_driver keyspan_driver =
{
.name = "keyspan_remote",
.probe = keyspan_probe,
.disconnect = keyspan_disconnect,
.id_table = keyspan_table
};
static int __init usb_keyspan_init(void)
{
int result;
/* register this driver with the USB subsystem */
result = usb_register(&keyspan_driver);
if (result)
err("usb_register failed. Error number %d\n", result);
return result;
}
static void __exit usb_keyspan_exit(void)
{
/* deregister this driver with the USB subsystem */
usb_deregister(&keyspan_driver);
}
module_init(usb_keyspan_init);
module_exit(usb_keyspan_exit);
MODULE_DEVICE_TABLE(usb, keyspan_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);

View File

@@ -0,0 +1,189 @@
/*
* drivers/usb/input/map_to_7segment.h
*
* Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.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
*/
#ifndef MAP_TO_7SEGMENT_H
#define MAP_TO_7SEGMENT_H
/* This file provides translation primitives and tables for the conversion
* of (ASCII) characters to a 7-segments notation.
*
* The 7 segment's wikipedia notation below is used as standard.
* See: http://en.wikipedia.org/wiki/Seven_segment_display
*
* Notation: +-a-+
* f b
* +-g-+
* e c
* +-d-+
*
* Usage:
*
* Register a map variable, and fill it with a character set:
* static SEG7_DEFAULT_MAP(map_seg7);
*
*
* Then use for conversion:
* seg7 = map_to_seg7(&map_seg7, some_char);
* ...
*
* In device drivers it is recommended, if required, to make the char map
* accessible via the sysfs interface using the following scheme:
*
* static ssize_t show_map(struct device *dev, char *buf) {
* memcpy(buf, &map_seg7, sizeof(map_seg7));
* return sizeof(map_seg7);
* }
* static ssize_t store_map(struct device *dev, const char *buf, size_t cnt) {
* if(cnt != sizeof(map_seg7))
* return -EINVAL;
* memcpy(&map_seg7, buf, cnt);
* return cnt;
* }
* static DEVICE_ATTR(map_seg7, PERMS_RW, show_map, store_map);
*
* History:
* 2005-05-31 RFC linux-kernel@vger.kernel.org
*/
#include <linux/errno.h>
#define BIT_SEG7_A 0
#define BIT_SEG7_B 1
#define BIT_SEG7_C 2
#define BIT_SEG7_D 3
#define BIT_SEG7_E 4
#define BIT_SEG7_F 5
#define BIT_SEG7_G 6
#define BIT_SEG7_RESERVED 7
struct seg7_conversion_map {
unsigned char table[128];
};
static inline int map_to_seg7(struct seg7_conversion_map *map, int c)
{
return c >= 0 && c < sizeof(map->table) ? map->table[c] : -EINVAL;
}
#define SEG7_CONVERSION_MAP(_name, _map) \
struct seg7_conversion_map _name = { .table = { _map } }
/*
* It is recommended to use a facility that allows user space to redefine
* custom character sets for LCD devices. Please use a sysfs interface
* as described above.
*/
#define MAP_TO_SEG7_SYSFS_FILE "map_seg7"
/*******************************************************************************
* ASCII conversion table
******************************************************************************/
#define _SEG7(l,a,b,c,d,e,f,g) \
( a<<BIT_SEG7_A | b<<BIT_SEG7_B | c<<BIT_SEG7_C | d<<BIT_SEG7_D | \
e<<BIT_SEG7_E | f<<BIT_SEG7_F | g<<BIT_SEG7_G )
#define _MAP_0_32_ASCII_SEG7_NON_PRINTABLE \
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,
#define _MAP_33_47_ASCII_SEG7_SYMBOL \
_SEG7('!',0,0,0,0,1,1,0), _SEG7('"',0,1,0,0,0,1,0), _SEG7('#',0,1,1,0,1,1,0),\
_SEG7('$',1,0,1,1,0,1,1), _SEG7('%',0,0,1,0,0,1,0), _SEG7('&',1,0,1,1,1,1,1),\
_SEG7('\'',0,0,0,0,0,1,0),_SEG7('(',1,0,0,1,1,1,0), _SEG7(')',1,1,1,1,0,0,0),\
_SEG7('*',0,1,1,0,1,1,1), _SEG7('+',0,1,1,0,0,0,1), _SEG7(',',0,0,0,0,1,0,0),\
_SEG7('-',0,0,0,0,0,0,1), _SEG7('.',0,0,0,0,1,0,0), _SEG7('/',0,1,0,0,1,0,1),
#define _MAP_48_57_ASCII_SEG7_NUMERIC \
_SEG7('0',1,1,1,1,1,1,0), _SEG7('1',0,1,1,0,0,0,0), _SEG7('2',1,1,0,1,1,0,1),\
_SEG7('3',1,1,1,1,0,0,1), _SEG7('4',0,1,1,0,0,1,1), _SEG7('5',1,0,1,1,0,1,1),\
_SEG7('6',1,0,1,1,1,1,1), _SEG7('7',1,1,1,0,0,0,0), _SEG7('8',1,1,1,1,1,1,1),\
_SEG7('9',1,1,1,1,0,1,1),
#define _MAP_58_64_ASCII_SEG7_SYMBOL \
_SEG7(':',0,0,0,1,0,0,1), _SEG7(';',0,0,0,1,0,0,1), _SEG7('<',1,0,0,0,0,1,1),\
_SEG7('=',0,0,0,1,0,0,1), _SEG7('>',1,1,0,0,0,0,1), _SEG7('?',1,1,1,0,0,1,0),\
_SEG7('@',1,1,0,1,1,1,1),
#define _MAP_65_90_ASCII_SEG7_ALPHA_UPPR \
_SEG7('A',1,1,1,0,1,1,1), _SEG7('B',1,1,1,1,1,1,1), _SEG7('C',1,0,0,1,1,1,0),\
_SEG7('D',1,1,1,1,1,1,0), _SEG7('E',1,0,0,1,1,1,1), _SEG7('F',1,0,0,0,1,1,1),\
_SEG7('G',1,1,1,1,0,1,1), _SEG7('H',0,1,1,0,1,1,1), _SEG7('I',0,1,1,0,0,0,0),\
_SEG7('J',0,1,1,1,0,0,0), _SEG7('K',0,1,1,0,1,1,1), _SEG7('L',0,0,0,1,1,1,0),\
_SEG7('M',1,1,1,0,1,1,0), _SEG7('N',1,1,1,0,1,1,0), _SEG7('O',1,1,1,1,1,1,0),\
_SEG7('P',1,1,0,0,1,1,1), _SEG7('Q',1,1,1,1,1,1,0), _SEG7('R',1,1,1,0,1,1,1),\
_SEG7('S',1,0,1,1,0,1,1), _SEG7('T',0,0,0,1,1,1,1), _SEG7('U',0,1,1,1,1,1,0),\
_SEG7('V',0,1,1,1,1,1,0), _SEG7('W',0,1,1,1,1,1,1), _SEG7('X',0,1,1,0,1,1,1),\
_SEG7('Y',0,1,1,0,0,1,1), _SEG7('Z',1,1,0,1,1,0,1),
#define _MAP_91_96_ASCII_SEG7_SYMBOL \
_SEG7('[',1,0,0,1,1,1,0), _SEG7('\\',0,0,1,0,0,1,1),_SEG7(']',1,1,1,1,0,0,0),\
_SEG7('^',1,1,0,0,0,1,0), _SEG7('_',0,0,0,1,0,0,0), _SEG7('`',0,1,0,0,0,0,0),
#define _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
_SEG7('A',1,1,1,0,1,1,1), _SEG7('b',0,0,1,1,1,1,1), _SEG7('c',0,0,0,1,1,0,1),\
_SEG7('d',0,1,1,1,1,0,1), _SEG7('E',1,0,0,1,1,1,1), _SEG7('F',1,0,0,0,1,1,1),\
_SEG7('G',1,1,1,1,0,1,1), _SEG7('h',0,0,1,0,1,1,1), _SEG7('i',0,0,1,0,0,0,0),\
_SEG7('j',0,0,1,1,0,0,0), _SEG7('k',0,0,1,0,1,1,1), _SEG7('L',0,0,0,1,1,1,0),\
_SEG7('M',1,1,1,0,1,1,0), _SEG7('n',0,0,1,0,1,0,1), _SEG7('o',0,0,1,1,1,0,1),\
_SEG7('P',1,1,0,0,1,1,1), _SEG7('q',1,1,1,0,0,1,1), _SEG7('r',0,0,0,0,1,0,1),\
_SEG7('S',1,0,1,1,0,1,1), _SEG7('T',0,0,0,1,1,1,1), _SEG7('u',0,0,1,1,1,0,0),\
_SEG7('v',0,0,1,1,1,0,0), _SEG7('W',0,1,1,1,1,1,1), _SEG7('X',0,1,1,0,1,1,1),\
_SEG7('y',0,1,1,1,0,1,1), _SEG7('Z',1,1,0,1,1,0,1),
#define _MAP_123_126_ASCII_SEG7_SYMBOL \
_SEG7('{',1,0,0,1,1,1,0), _SEG7('|',0,0,0,0,1,1,0), _SEG7('}',1,1,1,1,0,0,0),\
_SEG7('~',1,0,0,0,0,0,0),
/* Maps */
/* This set tries to map as close as possible to the visible characteristics
* of the ASCII symbol, lowercase and uppercase letters may differ in
* presentation on the display.
*/
#define MAP_ASCII7SEG_ALPHANUM \
_MAP_0_32_ASCII_SEG7_NON_PRINTABLE \
_MAP_33_47_ASCII_SEG7_SYMBOL \
_MAP_48_57_ASCII_SEG7_NUMERIC \
_MAP_58_64_ASCII_SEG7_SYMBOL \
_MAP_65_90_ASCII_SEG7_ALPHA_UPPR \
_MAP_91_96_ASCII_SEG7_SYMBOL \
_MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
_MAP_123_126_ASCII_SEG7_SYMBOL
/* This set tries to map as close as possible to the symbolic characteristics
* of the ASCII character for maximum discrimination.
* For now this means all alpha chars are in lower case representations.
* (This for example facilitates the use of hex numbers with uppercase input.)
*/
#define MAP_ASCII7SEG_ALPHANUM_LC \
_MAP_0_32_ASCII_SEG7_NON_PRINTABLE \
_MAP_33_47_ASCII_SEG7_SYMBOL \
_MAP_48_57_ASCII_SEG7_NUMERIC \
_MAP_58_64_ASCII_SEG7_SYMBOL \
_MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
_MAP_91_96_ASCII_SEG7_SYMBOL \
_MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
_MAP_123_126_ASCII_SEG7_SYMBOL
#define SEG7_DEFAULT_MAP(_name) \
SEG7_CONVERSION_MAP(_name,MAP_ASCII7SEG_ALPHANUM)
#endif /* MAP_TO_7SEGMENT_H */

View File

@@ -0,0 +1,332 @@
/******************************************************************************
* 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 successful 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/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb/input.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;
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 mtouch_usb *mtouch = urb->context;
int retval;
switch (urb->status) {
case 0:
/* success */
break;
case -ETIME:
/* 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_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;
mtouch->irq->dev = mtouch->udev;
if (usb_submit_urb(mtouch->irq, GFP_ATOMIC))
return -EIO;
return 0;
}
static void mtouchusb_close(struct input_dev *input)
{
struct mtouch_usb *mtouch = input->private;
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,
GFP_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 input_dev *input_dev;
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_device *udev = interface_to_usbdev(intf);
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;
mtouch = kzalloc(sizeof(struct mtouch_usb), GFP_KERNEL);
input_dev = input_allocate_device();
if (!mtouch || !input_dev) {
err("%s - Out of memory.", __FUNCTION__);
goto fail1;
}
dbg("%s - allocating buffers", __FUNCTION__);
if (mtouchusb_alloc_buffers(udev, mtouch))
goto fail2;
mtouch->udev = udev;
mtouch->input = input_dev;
if (udev->manufacturer)
strlcpy(mtouch->name, udev->manufacturer, sizeof(mtouch->name));
if (udev->product) {
if (udev->manufacturer)
strlcat(mtouch->name, " ", sizeof(mtouch->name));
strlcat(mtouch->name, udev->product, sizeof(mtouch->name));
}
if (!strlen(mtouch->name))
snprintf(mtouch->name, sizeof(mtouch->name),
"USB Touchscreen %04x:%04x",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
usb_make_path(udev, mtouch->phys, sizeof(mtouch->phys));
strlcpy(mtouch->phys, "/input0", sizeof(mtouch->phys));
input_dev->name = mtouch->name;
input_dev->phys = mtouch->phys;
usb_to_input_id(udev, &input_dev->id);
input_dev->cdev.dev = &intf->dev;
input_dev->private = mtouch;
input_dev->open = mtouchusb_open;
input_dev->close = mtouchusb_close;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
input_set_abs_params(input_dev, ABS_X, MTOUCHUSB_MIN_XC,
raw_coordinates ? MTOUCHUSB_MAX_RAW_XC : MTOUCHUSB_MAX_CALIB_XC,
MTOUCHUSB_XC_FUZZ, MTOUCHUSB_XC_FLAT);
input_set_abs_params(input_dev, ABS_Y, MTOUCHUSB_MIN_YC,
raw_coordinates ? MTOUCHUSB_MAX_RAW_YC : MTOUCHUSB_MAX_CALIB_YC,
MTOUCHUSB_YC_FUZZ, MTOUCHUSB_YC_FLAT);
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__);
goto fail2;
}
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);
usb_set_intfdata(intf, mtouch);
return 0;
fail2: mtouchusb_free_buffers(udev, mtouch);
fail1: input_free_device(input_dev);
kfree(mtouch);
return -ENOMEM;
}
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 = {
.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");

View File

@@ -0,0 +1,460 @@
/*
* 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/module.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/usb/input.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);
/* Callback for data arriving from the PowerMate over the USB interrupt pipe */
static void powermate_irq(struct urb *urb)
{
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_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 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,
GFP_ATOMIC, &pm->data_dma);
if (!pm->data)
return -1;
pm->configcr = usb_buffer_alloc(udev, sizeof(*(pm->configcr)),
GFP_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;
struct input_dev *input_dev;
int pipe, maxp;
int err = -ENOMEM;
interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
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);
pm = kzalloc(sizeof(struct powermate_device), GFP_KERNEL);
input_dev = input_allocate_device();
if (!pm || !input_dev)
goto fail1;
if (powermate_alloc_buffers(udev, pm))
goto fail2;
pm->irq = usb_alloc_urb(0, GFP_KERNEL);
if (!pm->irq)
goto fail2;
pm->config = usb_alloc_urb(0, GFP_KERNEL);
if (!pm->config)
goto fail3;
pm->udev = udev;
pm->input = input_dev;
usb_make_path(udev, pm->phys, sizeof(pm->phys));
strlcpy(pm->phys, "/input0", sizeof(pm->phys));
spin_lock_init(&pm->lock);
switch (le16_to_cpu(udev->descriptor.idProduct)) {
case POWERMATE_PRODUCT_NEW:
input_dev->name = pm_name_powermate;
break;
case POWERMATE_PRODUCT_OLD:
input_dev->name = pm_name_soundknob;
break;
default:
input_dev->name = pm_name_soundknob;
printk(KERN_WARNING "powermate: unknown product id %04x\n",
le16_to_cpu(udev->descriptor.idProduct));
}
input_dev->phys = pm->phys;
usb_to_input_id(udev, &input_dev->id);
input_dev->cdev.dev = &intf->dev;
input_dev->private = pm;
input_dev->event = powermate_input_event;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_MSC);
input_dev->keybit[LONG(BTN_0)] = BIT(BTN_0);
input_dev->relbit[LONG(REL_DIAL)] = BIT(REL_DIAL);
input_dev->mscbit[LONG(MSC_PULSELED)] = BIT(MSC_PULSELED);
/* 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(KERN_WARNING "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)) {
err = -EIO;
goto fail4;
}
input_register_device(pm->input);
/* 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;
fail4: usb_free_urb(pm->config);
fail3: usb_free_urb(pm->irq);
fail2: powermate_free_buffers(udev, pm);
fail1: input_free_device(input_dev);
kfree(pm);
return err;
}
/* 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 = {
.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");

View File

@@ -0,0 +1,392 @@
/******************************************************************************
* touchkitusb.c -- Driver for eGalax TouchKit USB Touchscreens
*
* Copyright (C) 2004-2005 by Daniel Ritz <daniel.ritz@gmx.ch>
* 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/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb/input.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 16
#define TOUCHKIT_DOWN 0x01
#define TOUCHKIT_PKT_TYPE_MASK 0xFE
#define TOUCHKIT_PKT_TYPE_REPT 0x80
#define TOUCHKIT_PKT_TYPE_DIAG 0x0A
#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;
char buffer[TOUCHKIT_REPORT_DATA_SIZE];
int buf_len;
struct urb *irq;
struct usb_device *udev;
struct input_dev *input;
char name[128];
char phys[64];
};
static struct usb_device_id touchkit_devices[] = {
{USB_DEVICE(0x3823, 0x0001)},
{USB_DEVICE(0x0123, 0x0001)},
{USB_DEVICE(0x0eef, 0x0001)},
{USB_DEVICE(0x0eef, 0x0002)},
{}
};
/* helpers to read the data */
static inline int touchkit_get_touched(char *data)
{
return (data[0] & TOUCHKIT_DOWN) ? 1 : 0;
}
static inline int touchkit_get_x(char *data)
{
return ((data[3] & 0x0F) << 7) | (data[4] & 0x7F);
}
static inline int touchkit_get_y(char *data)
{
return ((data[1] & 0x0F) << 7) | (data[2] & 0x7F);
}
/* processes one input packet. */
static void touchkit_process_pkt(struct touchkit_usb *touchkit, char *pkt)
{
int x, y;
/* only process report packets */
if ((pkt[0] & TOUCHKIT_PKT_TYPE_MASK) != TOUCHKIT_PKT_TYPE_REPT)
return;
if (swap_xy) {
y = touchkit_get_x(pkt);
x = touchkit_get_y(pkt);
} else {
x = touchkit_get_x(pkt);
y = touchkit_get_y(pkt);
}
input_report_key(touchkit->input, BTN_TOUCH, touchkit_get_touched(pkt));
input_report_abs(touchkit->input, ABS_X, x);
input_report_abs(touchkit->input, ABS_Y, y);
input_sync(touchkit->input);
}
static int touchkit_get_pkt_len(char *buf)
{
switch (buf[0] & TOUCHKIT_PKT_TYPE_MASK) {
case TOUCHKIT_PKT_TYPE_REPT:
return 5;
case TOUCHKIT_PKT_TYPE_DIAG:
return buf[1] + 2;
}
return 0;
}
static void touchkit_process(struct touchkit_usb *touchkit, int len)
{
char *buffer;
int pkt_len, buf_len, pos;
/* if the buffer contains data, append */
if (unlikely(touchkit->buf_len)) {
int tmp;
/* if only 1 byte in buffer, add another one to get length */
if (touchkit->buf_len == 1)
touchkit->buffer[1] = touchkit->data[0];
pkt_len = touchkit_get_pkt_len(touchkit->buffer);
/* unknown packet: drop everything */
if (!pkt_len)
return;
/* append, process */
tmp = pkt_len - touchkit->buf_len;
memcpy(touchkit->buffer + touchkit->buf_len, touchkit->data, tmp);
touchkit_process_pkt(touchkit, touchkit->buffer);
buffer = touchkit->data + tmp;
buf_len = len - tmp;
} else {
buffer = touchkit->data;
buf_len = len;
}
/* only one byte left in buffer */
if (unlikely(buf_len == 1)) {
touchkit->buffer[0] = buffer[0];
touchkit->buf_len = 1;
return;
}
/* loop over the buffer */
pos = 0;
while (pos < buf_len) {
/* get packet len */
pkt_len = touchkit_get_pkt_len(buffer + pos);
/* unknown packet: drop everything */
if (unlikely(!pkt_len))
return;
/* full packet: process */
if (likely(pkt_len <= buf_len)) {
touchkit_process_pkt(touchkit, buffer + pos);
} else {
/* incomplete packet: save in buffer */
memcpy(touchkit->buffer, buffer + pos, buf_len - pos);
touchkit->buf_len = buf_len - pos;
}
pos += pkt_len;
}
}
static void touchkit_irq(struct urb *urb)
{
struct touchkit_usb *touchkit = urb->context;
int retval;
switch (urb->status) {
case 0:
/* success */
break;
case -ETIME:
/* 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;
}
touchkit_process(touchkit, urb->actual_length);
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;
touchkit->irq->dev = touchkit->udev;
if (usb_submit_urb(touchkit->irq, GFP_ATOMIC))
return -EIO;
return 0;
}
static void touchkit_close(struct input_dev *input)
{
struct touchkit_usb *touchkit = input->private;
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,
GFP_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)
{
struct touchkit_usb *touchkit;
struct input_dev *input_dev;
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_device *udev = interface_to_usbdev(intf);
interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;
touchkit = kzalloc(sizeof(struct touchkit_usb), GFP_KERNEL);
input_dev = input_allocate_device();
if (!touchkit || !input_dev)
goto out_free;
if (touchkit_alloc_buffers(udev, touchkit))
goto out_free;
touchkit->irq = usb_alloc_urb(0, GFP_KERNEL);
if (!touchkit->irq) {
dbg("%s - usb_alloc_urb failed: touchkit->irq", __FUNCTION__);
goto out_free_buffers;
}
touchkit->udev = udev;
touchkit->input = input_dev;
if (udev->manufacturer)
strlcpy(touchkit->name, udev->manufacturer, sizeof(touchkit->name));
if (udev->product) {
if (udev->manufacturer)
strlcat(touchkit->name, " ", sizeof(touchkit->name));
strlcat(touchkit->name, udev->product, sizeof(touchkit->name));
}
if (!strlen(touchkit->name))
snprintf(touchkit->name, sizeof(touchkit->name),
"USB Touchscreen %04x:%04x",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
usb_make_path(udev, touchkit->phys, sizeof(touchkit->phys));
strlcpy(touchkit->phys, "/input0", sizeof(touchkit->phys));
input_dev->name = touchkit->name;
input_dev->phys = touchkit->phys;
usb_to_input_id(udev, &input_dev->id);
input_dev->cdev.dev = &intf->dev;
input_dev->private = touchkit;
input_dev->open = touchkit_open;
input_dev->close = touchkit_close;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
input_set_abs_params(input_dev, ABS_X, TOUCHKIT_MIN_XC, TOUCHKIT_MAX_XC,
TOUCHKIT_XC_FUZZ, TOUCHKIT_XC_FLAT);
input_set_abs_params(input_dev, ABS_Y, TOUCHKIT_MIN_YC, TOUCHKIT_MAX_YC,
TOUCHKIT_YC_FUZZ, TOUCHKIT_YC_FLAT);
usb_fill_int_urb(touchkit->irq, touchkit->udev,
usb_rcvintpipe(touchkit->udev, 0x81),
touchkit->data, TOUCHKIT_REPORT_DATA_SIZE,
touchkit_irq, touchkit, endpoint->bInterval);
touchkit->irq->transfer_dma = touchkit->data_dma;
touchkit->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
input_register_device(touchkit->input);
usb_set_intfdata(intf, touchkit);
return 0;
out_free_buffers:
touchkit_free_buffers(udev, touchkit);
out_free:
input_free_device(input_dev);
kfree(touchkit);
return -ENOMEM;
}
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);
usb_kill_urb(touchkit->irq);
input_unregister_device(touchkit->input);
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 = {
.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");

View File

@@ -0,0 +1,87 @@
#ifndef __USBHID_H
#define __USBHID_H
/*
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2001 Vojtech Pavlik
* Copyright (c) 2006 Jiri Kosina
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <linux/input.h>
/* API provided by hid-core.c for USB HID drivers */
int usbhid_wait_io(struct hid_device* hid);
void usbhid_close(struct hid_device *hid);
int usbhid_open(struct hid_device *hid);
void usbhid_init_reports(struct hid_device *hid);
void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir);
/*
* USB-specific HID struct, to be pointed to
* from struct hid_device->driver_data
*/
struct usbhid_device {
struct hid_device *hid; /* pointer to corresponding HID dev */
struct usb_interface *intf; /* USB interface */
int ifnum; /* USB interface number */
unsigned int bufsize; /* URB buffer size */
struct urb *urbin; /* Input URB */
char *inbuf; /* Input buffer */
dma_addr_t inbuf_dma; /* Input buffer dma */
spinlock_t inlock; /* Input fifo spinlock */
struct urb *urbctrl; /* Control URB */
struct usb_ctrlrequest *cr; /* Control request struct */
dma_addr_t cr_dma; /* Control request struct dma */
struct hid_control_fifo ctrl[HID_CONTROL_FIFO_SIZE]; /* Control fifo */
unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */
char *ctrlbuf; /* Control buffer */
dma_addr_t ctrlbuf_dma; /* Control buffer dma */
spinlock_t ctrllock; /* Control fifo spinlock */
struct urb *urbout; /* Output URB */
struct hid_report *out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */
unsigned char outhead, outtail; /* Output pipe fifo head & tail */
char *outbuf; /* Output buffer */
dma_addr_t outbuf_dma; /* Output buffer dma */
spinlock_t outlock; /* Output fifo spinlock */
unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
struct timer_list io_retry; /* Retry timer */
unsigned long stop_retry; /* Time to give up, in jiffies */
unsigned int retry_delay; /* Delay length in ms */
struct work_struct reset_work; /* Task context for resets */
};
#define hid_to_usb_dev(hid_dev) \
container_of(hid_dev->dev->parent, struct usb_device, dev)
#endif

362
drivers/usb/input/usbkbd.c Normal file
View File

@@ -0,0 +1,362 @@
/*
* $Id: usbkbd.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu 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/init.h>
#include <linux/usb/input.h>
#include <linux/hid.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];
unsigned char *new;
struct usb_ctrlrequest *cr;
unsigned char *leds;
dma_addr_t cr_dma;
dma_addr_t new_dma;
dma_addr_t leds_dma;
};
static void usb_kbd_irq(struct urb *urb)
{
struct usb_kbd *kbd = urb->context;
int i;
switch (urb->status) {
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
}
for (i = 0; i < 8; i++)
input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);
for (i = 2; i < 8; i++) {
if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
if (usb_kbd_keycode[kbd->old[i]])
input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);
else
info("Unknown key (scancode %#x) released.", kbd->old[i]);
}
if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
if (usb_kbd_keycode[kbd->new[i]])
input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);
else
info("Unknown key (scancode %#x) pressed.", kbd->new[i]);
}
}
input_sync(kbd->dev);
memcpy(kbd->old, kbd->new, 8);
resubmit:
i = usb_submit_urb (urb, GFP_ATOMIC);
if (i)
err ("can't resubmit intr, %s-%s/input0, status %d",
kbd->usbdev->bus->bus_name,
kbd->usbdev->devpath, i);
}
static int usb_kbd_event(struct input_dev *dev, unsigned int type,
unsigned int code, int value)
{
struct usb_kbd *kbd = dev->private;
if (type != EV_LED)
return -1;
kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
(!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) |
(!!test_bit(LED_NUML, dev->led));
if (kbd->led->status == -EINPROGRESS)
return 0;
if (*(kbd->leds) == kbd->newleds)
return 0;
*(kbd->leds) = kbd->newleds;
kbd->led->dev = kbd->usbdev;
if (usb_submit_urb(kbd->led, GFP_ATOMIC))
err("usb_submit_urb(leds) failed");
return 0;
}
static void usb_kbd_led(struct urb *urb)
{
struct usb_kbd *kbd = urb->context;
if (urb->status)
warn("led urb status %d received", urb->status);
if (*(kbd->leds) == kbd->newleds)
return;
*(kbd->leds) = kbd->newleds;
kbd->led->dev = kbd->usbdev;
if (usb_submit_urb(kbd->led, GFP_ATOMIC))
err("usb_submit_urb(leds) failed");
}
static int usb_kbd_open(struct input_dev *dev)
{
struct usb_kbd *kbd = dev->private;
kbd->irq->dev = kbd->usbdev;
if (usb_submit_urb(kbd->irq, GFP_KERNEL))
return -EIO;
return 0;
}
static void usb_kbd_close(struct input_dev *dev)
{
struct usb_kbd *kbd = dev->private;
usb_kill_urb(kbd->irq);
}
static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)
{
if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))
return -1;
if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))
return -1;
if (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma)))
return -1;
if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma)))
return -1;
if (!(kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma)))
return -1;
return 0;
}
static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)
{
usb_free_urb(kbd->irq);
usb_free_urb(kbd->led);
if (kbd->new)
usb_buffer_free(dev, 8, kbd->new, kbd->new_dma);
if (kbd->cr)
usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma);
if (kbd->leds)
usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma);
}
static int usb_kbd_probe(struct usb_interface *iface,
const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(iface);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_kbd *kbd;
struct input_dev *input_dev;
int i, pipe, maxp;
interface = iface->cur_altsetting;
if (interface->desc.bNumEndpoints != 1)
return -ENODEV;
endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);
input_dev = input_allocate_device();
if (!kbd || !input_dev)
goto fail1;
if (usb_kbd_alloc_mem(dev, kbd))
goto fail2;
kbd->usbdev = dev;
kbd->dev = input_dev;
if (dev->manufacturer)
strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
if (dev->product) {
if (dev->manufacturer)
strlcat(kbd->name, " ", sizeof(kbd->name));
strlcat(kbd->name, dev->product, sizeof(kbd->name));
}
if (!strlen(kbd->name))
snprintf(kbd->name, sizeof(kbd->name),
"USB HIDBP Keyboard %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));
input_dev->name = kbd->name;
input_dev->phys = kbd->phys;
usb_to_input_id(dev, &input_dev->id);
input_dev->cdev.dev = &iface->dev;
input_dev->private = kbd;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA);
for (i = 0; i < 255; i++)
set_bit(usb_kbd_keycode[i], input_dev->keybit);
clear_bit(0, input_dev->keybit);
input_dev->event = usb_kbd_event;
input_dev->open = usb_kbd_open;
input_dev->close = usb_kbd_close;
usb_fill_int_urb(kbd->irq, dev, pipe,
kbd->new, (maxp > 8 ? 8 : maxp),
usb_kbd_irq, kbd, endpoint->bInterval);
kbd->irq->transfer_dma = kbd->new_dma;
kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
kbd->cr->bRequest = 0x09;
kbd->cr->wValue = cpu_to_le16(0x200);
kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
kbd->cr->wLength = cpu_to_le16(1);
usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),
(void *) kbd->cr, kbd->leds, 1,
usb_kbd_led, kbd);
kbd->led->setup_dma = kbd->cr_dma;
kbd->led->transfer_dma = kbd->leds_dma;
kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
input_register_device(kbd->dev);
usb_set_intfdata(iface, kbd);
return 0;
fail2: usb_kbd_free_mem(dev, kbd);
fail1: input_free_device(input_dev);
kfree(kbd);
return -ENOMEM;
}
static void usb_kbd_disconnect(struct usb_interface *intf)
{
struct usb_kbd *kbd = usb_get_intfdata (intf);
usb_set_intfdata(intf, NULL);
if (kbd) {
usb_kill_urb(kbd->irq);
input_unregister_device(kbd->dev);
usb_kbd_free_mem(interface_to_usbdev(intf), kbd);
kfree(kbd);
}
}
static struct usb_device_id usb_kbd_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_KEYBOARD) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);
static struct usb_driver usb_kbd_driver = {
.name = "usbkbd",
.probe = usb_kbd_probe,
.disconnect = usb_kbd_disconnect,
.id_table = usb_kbd_id_table,
};
static int __init usb_kbd_init(void)
{
int result = usb_register(&usb_kbd_driver);
if (result == 0)
info(DRIVER_VERSION ":" DRIVER_DESC);
return result;
}
static void __exit usb_kbd_exit(void)
{
usb_deregister(&usb_kbd_driver);
}
module_init(usb_kbd_init);
module_exit(usb_kbd_exit);

View File

@@ -0,0 +1,245 @@
/*
* $Id: usbmouse.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu 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/module.h>
#include <linux/init.h>
#include <linux/usb/input.h>
#include <linux/hid.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;
signed char *data;
dma_addr_t data_dma;
};
static void usb_mouse_irq(struct urb *urb)
{
struct usb_mouse *mouse = urb->context;
signed char *data = mouse->data;
struct input_dev *dev = mouse->dev;
int status;
switch (urb->status) {
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
}
input_report_key(dev, BTN_LEFT, data[0] & 0x01);
input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);
input_report_key(dev, BTN_SIDE, data[0] & 0x08);
input_report_key(dev, BTN_EXTRA, data[0] & 0x10);
input_report_rel(dev, REL_X, data[1]);
input_report_rel(dev, REL_Y, data[2]);
input_report_rel(dev, REL_WHEEL, data[3]);
input_sync(dev);
resubmit:
status = usb_submit_urb (urb, GFP_ATOMIC);
if (status)
err ("can't resubmit intr, %s-%s/input0, status %d",
mouse->usbdev->bus->bus_name,
mouse->usbdev->devpath, status);
}
static int usb_mouse_open(struct input_dev *dev)
{
struct usb_mouse *mouse = dev->private;
mouse->irq->dev = mouse->usbdev;
if (usb_submit_urb(mouse->irq, GFP_KERNEL))
return -EIO;
return 0;
}
static void usb_mouse_close(struct input_dev *dev)
{
struct usb_mouse *mouse = dev->private;
usb_kill_urb(mouse->irq);
}
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_mouse *mouse;
struct input_dev *input_dev;
int pipe, maxp;
interface = intf->cur_altsetting;
if (interface->desc.bNumEndpoints != 1)
return -ENODEV;
endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);
input_dev = input_allocate_device();
if (!mouse || !input_dev)
goto fail1;
mouse->data = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &mouse->data_dma);
if (!mouse->data)
goto fail1;
mouse->irq = usb_alloc_urb(0, GFP_KERNEL);
if (!mouse->irq)
goto fail2;
mouse->usbdev = dev;
mouse->dev = input_dev;
if (dev->manufacturer)
strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));
if (dev->product) {
if (dev->manufacturer)
strlcat(mouse->name, " ", sizeof(mouse->name));
strlcat(mouse->name, dev->product, sizeof(mouse->name));
}
if (!strlen(mouse->name))
snprintf(mouse->name, sizeof(mouse->name),
"USB HIDBP Mouse %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
usb_make_path(dev, mouse->phys, sizeof(mouse->phys));
strlcat(mouse->phys, "/input0", sizeof(mouse->phys));
input_dev->name = mouse->name;
input_dev->phys = mouse->phys;
usb_to_input_id(dev, &input_dev->id);
input_dev->cdev.dev = &intf->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
input_dev->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
input_dev->relbit[0] |= BIT(REL_WHEEL);
input_dev->private = mouse;
input_dev->open = usb_mouse_open;
input_dev->close = usb_mouse_close;
usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
(maxp > 8 ? 8 : maxp),
usb_mouse_irq, mouse, endpoint->bInterval);
mouse->irq->transfer_dma = mouse->data_dma;
mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
input_register_device(mouse->dev);
usb_set_intfdata(intf, mouse);
return 0;
fail2: usb_buffer_free(dev, 8, mouse->data, mouse->data_dma);
fail1: input_free_device(input_dev);
kfree(mouse);
return -ENOMEM;
}
static void usb_mouse_disconnect(struct usb_interface *intf)
{
struct usb_mouse *mouse = usb_get_intfdata (intf);
usb_set_intfdata(intf, NULL);
if (mouse) {
usb_kill_urb(mouse->irq);
input_unregister_device(mouse->dev);
usb_free_urb(mouse->irq);
usb_buffer_free(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma);
kfree(mouse);
}
}
static struct usb_device_id usb_mouse_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
static struct usb_driver usb_mouse_driver = {
.name = "usbmouse",
.probe = usb_mouse_probe,
.disconnect = usb_mouse_disconnect,
.id_table = usb_mouse_id_table,
};
static int __init usb_mouse_init(void)
{
int retval = usb_register(&usb_mouse_driver);
if (retval == 0)
info(DRIVER_VERSION ":" DRIVER_DESC);
return retval;
}
static void __exit usb_mouse_exit(void)
{
usb_deregister(&usb_mouse_driver);
}
module_init(usb_mouse_init);
module_exit(usb_mouse_exit);

View File

@@ -0,0 +1,838 @@
/******************************************************************************
* usbtouchscreen.c
* Driver for USB Touchscreens, supporting those devices:
* - eGalax Touchkit
* includes eTurboTouch CT-410/510/700
* - 3M/Microtouch EX II series
* - ITM
* - PanJit TouchSet
* - eTurboTouch
* - Gunze AHL61
* - DMC TSC-10/25
*
* Copyright (C) 2004-2006 by Daniel Ritz <daniel.ritz@gmx.ch>
* 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.
*
* Driver is based on touchkitusb.c
* - ITM parts are from itmtouch.c
* - 3M parts are from mtouchusb.c
* - PanJit parts are from an unmerged driver by Lanslott Gish
* - DMC TSC 10/25 are from Holger Schurig, with ideas from an unmerged
* driver from Marius Vollmer
*
*****************************************************************************/
//#define DEBUG
#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 <linux/usb/input.h>
#define DRIVER_VERSION "v0.5"
#define DRIVER_AUTHOR "Daniel Ritz <daniel.ritz@gmx.ch>"
#define DRIVER_DESC "USB 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.");
/* device specifc data/functions */
struct usbtouch_usb;
struct usbtouch_device_info {
int min_xc, max_xc;
int min_yc, max_yc;
int min_press, max_press;
int rept_size;
int flags;
void (*process_pkt) (struct usbtouch_usb *usbtouch, unsigned char *pkt, int len);
int (*get_pkt_len) (unsigned char *pkt, int len);
int (*read_data) (struct usbtouch_usb *usbtouch, unsigned char *pkt);
int (*init) (struct usbtouch_usb *usbtouch);
};
#define USBTOUCH_FLG_BUFFER 0x01
/* a usbtouch device */
struct usbtouch_usb {
unsigned char *data;
dma_addr_t data_dma;
unsigned char *buffer;
int buf_len;
struct urb *irq;
struct usb_device *udev;
struct input_dev *input;
struct usbtouch_device_info *type;
char name[128];
char phys[64];
int x, y;
int touch, press;
};
#if defined(CONFIG_USB_TOUCHSCREEN_EGALAX) || defined(CONFIG_USB_TOUCHSCREEN_ETURBO)
#define MULTI_PACKET
#endif
#ifdef MULTI_PACKET
static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
unsigned char *pkt, int len);
#endif
/* device types */
enum {
DEVTPYE_DUMMY = -1,
DEVTYPE_EGALAX,
DEVTYPE_PANJIT,
DEVTYPE_3M,
DEVTYPE_ITM,
DEVTYPE_ETURBO,
DEVTYPE_GUNZE,
DEVTYPE_DMC_TSC10,
};
static struct usb_device_id usbtouch_devices[] = {
#ifdef CONFIG_USB_TOUCHSCREEN_EGALAX
{USB_DEVICE(0x3823, 0x0001), .driver_info = DEVTYPE_EGALAX},
{USB_DEVICE(0x3823, 0x0002), .driver_info = DEVTYPE_EGALAX},
{USB_DEVICE(0x0123, 0x0001), .driver_info = DEVTYPE_EGALAX},
{USB_DEVICE(0x0eef, 0x0001), .driver_info = DEVTYPE_EGALAX},
{USB_DEVICE(0x0eef, 0x0002), .driver_info = DEVTYPE_EGALAX},
{USB_DEVICE(0x1234, 0x0001), .driver_info = DEVTYPE_EGALAX},
{USB_DEVICE(0x1234, 0x0002), .driver_info = DEVTYPE_EGALAX},
#endif
#ifdef CONFIG_USB_TOUCHSCREEN_PANJIT
{USB_DEVICE(0x134c, 0x0001), .driver_info = DEVTYPE_PANJIT},
{USB_DEVICE(0x134c, 0x0002), .driver_info = DEVTYPE_PANJIT},
{USB_DEVICE(0x134c, 0x0003), .driver_info = DEVTYPE_PANJIT},
{USB_DEVICE(0x134c, 0x0004), .driver_info = DEVTYPE_PANJIT},
#endif
#ifdef CONFIG_USB_TOUCHSCREEN_3M
{USB_DEVICE(0x0596, 0x0001), .driver_info = DEVTYPE_3M},
#endif
#ifdef CONFIG_USB_TOUCHSCREEN_ITM
{USB_DEVICE(0x0403, 0xf9e9), .driver_info = DEVTYPE_ITM},
#endif
#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO
{USB_DEVICE(0x1234, 0x5678), .driver_info = DEVTYPE_ETURBO},
#endif
#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE
{USB_DEVICE(0x0637, 0x0001), .driver_info = DEVTYPE_GUNZE},
#endif
#ifdef CONFIG_USB_TOUCHSCREEN_DMC_TSC10
{USB_DEVICE(0x0afa, 0x03e8), .driver_info = DEVTYPE_DMC_TSC10},
#endif
{}
};
/*****************************************************************************
* eGalax part
*/
#ifdef CONFIG_USB_TOUCHSCREEN_EGALAX
#define EGALAX_PKT_TYPE_MASK 0xFE
#define EGALAX_PKT_TYPE_REPT 0x80
#define EGALAX_PKT_TYPE_DIAG 0x0A
static int egalax_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
{
if ((pkt[0] & EGALAX_PKT_TYPE_MASK) != EGALAX_PKT_TYPE_REPT)
return 0;
dev->x = ((pkt[3] & 0x0F) << 7) | (pkt[4] & 0x7F);
dev->y = ((pkt[1] & 0x0F) << 7) | (pkt[2] & 0x7F);
dev->touch = pkt[0] & 0x01;
return 1;
}
static int egalax_get_pkt_len(unsigned char *buf, int len)
{
switch (buf[0] & EGALAX_PKT_TYPE_MASK) {
case EGALAX_PKT_TYPE_REPT:
return 5;
case EGALAX_PKT_TYPE_DIAG:
if (len < 2)
return -1;
return buf[1] + 2;
}
return 0;
}
#endif
/*****************************************************************************
* PanJit Part
*/
#ifdef CONFIG_USB_TOUCHSCREEN_PANJIT
static int panjit_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
{
dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1];
dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3];
dev->touch = pkt[0] & 0x01;
return 1;
}
#endif
/*****************************************************************************
* 3M/Microtouch Part
*/
#ifdef CONFIG_USB_TOUCHSCREEN_3M
#define MTOUCHUSB_ASYNC_REPORT 1
#define MTOUCHUSB_RESET 7
#define MTOUCHUSB_REQ_CTRLLR_ID 10
static int mtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
{
dev->x = (pkt[8] << 8) | pkt[7];
dev->y = (pkt[10] << 8) | pkt[9];
dev->touch = (pkt[2] & 0x40) ? 1 : 0;
return 1;
}
static int mtouch_init(struct usbtouch_usb *usbtouch)
{
int ret, i;
ret = usb_control_msg(usbtouch->udev, usb_rcvctrlpipe(usbtouch->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__, ret);
if (ret < 0)
return ret;
msleep(150);
for (i = 0; i < 3; i++) {
ret = usb_control_msg(usbtouch->udev, usb_rcvctrlpipe(usbtouch->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__, ret);
if (ret >= 0)
break;
if (ret != -EPIPE)
return ret;
}
return 0;
}
#endif
/*****************************************************************************
* ITM Part
*/
#ifdef CONFIG_USB_TOUCHSCREEN_ITM
static int itm_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
{
int touch;
/*
* ITM devices report invalid x/y data if not touched.
* if the screen was touched before but is not touched any more
* report touch as 0 with the last valid x/y data once. then stop
* reporting data until touched again.
*/
dev->press = ((pkt[2] & 0x01) << 7) | (pkt[5] & 0x7F);
touch = ~pkt[7] & 0x20;
if (!touch) {
if (dev->touch) {
dev->touch = 0;
return 1;
}
return 0;
}
dev->x = ((pkt[0] & 0x1F) << 7) | (pkt[3] & 0x7F);
dev->y = ((pkt[1] & 0x1F) << 7) | (pkt[4] & 0x7F);
dev->touch = touch;
return 1;
}
#endif
/*****************************************************************************
* eTurboTouch part
*/
#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO
static int eturbo_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
{
unsigned int shift;
/* packets should start with sync */
if (!(pkt[0] & 0x80))
return 0;
shift = (6 - (pkt[0] & 0x03));
dev->x = ((pkt[3] << 7) | pkt[4]) >> shift;
dev->y = ((pkt[1] << 7) | pkt[2]) >> shift;
dev->touch = (pkt[0] & 0x10) ? 1 : 0;
return 1;
}
static int eturbo_get_pkt_len(unsigned char *buf, int len)
{
if (buf[0] & 0x80)
return 5;
if (buf[0] == 0x01)
return 3;
return 0;
}
#endif
/*****************************************************************************
* Gunze part
*/
#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE
static int gunze_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
{
if (!(pkt[0] & 0x80) || ((pkt[1] | pkt[2] | pkt[3]) & 0x80))
return 0;
dev->x = ((pkt[0] & 0x1F) << 7) | (pkt[2] & 0x7F);
dev->y = ((pkt[1] & 0x1F) << 7) | (pkt[3] & 0x7F);
dev->touch = pkt[0] & 0x20;
return 1;
}
#endif
/*****************************************************************************
* DMC TSC-10/25 Part
*
* Documentation about the controller and it's protocol can be found at
* http://www.dmccoltd.com/files/controler/tsc10usb_pi_e.pdf
* http://www.dmccoltd.com/files/controler/tsc25_usb_e.pdf
*/
#ifdef CONFIG_USB_TOUCHSCREEN_DMC_TSC10
/* supported data rates. currently using 130 */
#define TSC10_RATE_POINT 0x50
#define TSC10_RATE_30 0x40
#define TSC10_RATE_50 0x41
#define TSC10_RATE_80 0x42
#define TSC10_RATE_100 0x43
#define TSC10_RATE_130 0x44
#define TSC10_RATE_150 0x45
/* commands */
#define TSC10_CMD_RESET 0x55
#define TSC10_CMD_RATE 0x05
#define TSC10_CMD_DATA1 0x01
static int dmc_tsc10_init(struct usbtouch_usb *usbtouch)
{
struct usb_device *dev = usbtouch->udev;
int ret;
unsigned char buf[2];
/* reset */
buf[0] = buf[1] = 0xFF;
ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
TSC10_CMD_RESET,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
if (ret < 0)
return ret;
if (buf[0] != 0x06 || buf[1] != 0x00)
return -ENODEV;
/* set coordinate output rate */
buf[0] = buf[1] = 0xFF;
ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
TSC10_CMD_RATE,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
TSC10_RATE_150, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
if (ret < 0)
return ret;
if (buf[0] != 0x06 || buf[1] != 0x00)
return -ENODEV;
/* start sending data */
ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
TSC10_CMD_DATA1,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
if (ret < 0)
return ret;
return 0;
}
static int dmc_tsc10_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
{
dev->x = ((pkt[2] & 0x03) << 8) | pkt[1];
dev->y = ((pkt[4] & 0x03) << 8) | pkt[3];
dev->touch = pkt[0] & 0x01;
return 1;
}
#endif
/*****************************************************************************
* the different device descriptors
*/
static struct usbtouch_device_info usbtouch_dev_info[] = {
#ifdef CONFIG_USB_TOUCHSCREEN_EGALAX
[DEVTYPE_EGALAX] = {
.min_xc = 0x0,
.max_xc = 0x07ff,
.min_yc = 0x0,
.max_yc = 0x07ff,
.rept_size = 16,
.flags = USBTOUCH_FLG_BUFFER,
.process_pkt = usbtouch_process_multi,
.get_pkt_len = egalax_get_pkt_len,
.read_data = egalax_read_data,
},
#endif
#ifdef CONFIG_USB_TOUCHSCREEN_PANJIT
[DEVTYPE_PANJIT] = {
.min_xc = 0x0,
.max_xc = 0x0fff,
.min_yc = 0x0,
.max_yc = 0x0fff,
.rept_size = 8,
.read_data = panjit_read_data,
},
#endif
#ifdef CONFIG_USB_TOUCHSCREEN_3M
[DEVTYPE_3M] = {
.min_xc = 0x0,
.max_xc = 0x4000,
.min_yc = 0x0,
.max_yc = 0x4000,
.rept_size = 11,
.read_data = mtouch_read_data,
.init = mtouch_init,
},
#endif
#ifdef CONFIG_USB_TOUCHSCREEN_ITM
[DEVTYPE_ITM] = {
.min_xc = 0x0,
.max_xc = 0x0fff,
.min_yc = 0x0,
.max_yc = 0x0fff,
.max_press = 0xff,
.rept_size = 8,
.read_data = itm_read_data,
},
#endif
#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO
[DEVTYPE_ETURBO] = {
.min_xc = 0x0,
.max_xc = 0x07ff,
.min_yc = 0x0,
.max_yc = 0x07ff,
.rept_size = 8,
.flags = USBTOUCH_FLG_BUFFER,
.process_pkt = usbtouch_process_multi,
.get_pkt_len = eturbo_get_pkt_len,
.read_data = eturbo_read_data,
},
#endif
#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE
[DEVTYPE_GUNZE] = {
.min_xc = 0x0,
.max_xc = 0x0fff,
.min_yc = 0x0,
.max_yc = 0x0fff,
.rept_size = 4,
.read_data = gunze_read_data,
},
#endif
#ifdef CONFIG_USB_TOUCHSCREEN_DMC_TSC10
[DEVTYPE_DMC_TSC10] = {
.min_xc = 0x0,
.max_xc = 0x03ff,
.min_yc = 0x0,
.max_yc = 0x03ff,
.rept_size = 5,
.init = dmc_tsc10_init,
.read_data = dmc_tsc10_read_data,
},
#endif
};
/*****************************************************************************
* Generic Part
*/
static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch,
unsigned char *pkt, int len)
{
struct usbtouch_device_info *type = usbtouch->type;
if (!type->read_data(usbtouch, pkt))
return;
input_report_key(usbtouch->input, BTN_TOUCH, usbtouch->touch);
if (swap_xy) {
input_report_abs(usbtouch->input, ABS_X, usbtouch->y);
input_report_abs(usbtouch->input, ABS_Y, usbtouch->x);
} else {
input_report_abs(usbtouch->input, ABS_X, usbtouch->x);
input_report_abs(usbtouch->input, ABS_Y, usbtouch->y);
}
if (type->max_press)
input_report_abs(usbtouch->input, ABS_PRESSURE, usbtouch->press);
input_sync(usbtouch->input);
}
#ifdef MULTI_PACKET
static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
unsigned char *pkt, int len)
{
unsigned char *buffer;
int pkt_len, pos, buf_len, tmp;
/* process buffer */
if (unlikely(usbtouch->buf_len)) {
/* try to get size */
pkt_len = usbtouch->type->get_pkt_len(
usbtouch->buffer, usbtouch->buf_len);
/* drop? */
if (unlikely(!pkt_len))
goto out_flush_buf;
/* need to append -pkt_len bytes before able to get size */
if (unlikely(pkt_len < 0)) {
int append = -pkt_len;
if (unlikely(append > len))
append = len;
if (usbtouch->buf_len + append >= usbtouch->type->rept_size)
goto out_flush_buf;
memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, append);
usbtouch->buf_len += append;
pkt_len = usbtouch->type->get_pkt_len(
usbtouch->buffer, usbtouch->buf_len);
if (pkt_len < 0)
return;
}
/* append */
tmp = pkt_len - usbtouch->buf_len;
if (usbtouch->buf_len + tmp >= usbtouch->type->rept_size)
goto out_flush_buf;
memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, tmp);
usbtouch_process_pkt(usbtouch, usbtouch->buffer, pkt_len);
buffer = pkt + tmp;
buf_len = len - tmp;
} else {
buffer = pkt;
buf_len = len;
}
/* loop over the received packet, process */
pos = 0;
while (pos < buf_len) {
/* get packet len */
pkt_len = usbtouch->type->get_pkt_len(buffer + pos, len);
/* unknown packet: drop everything */
if (unlikely(!pkt_len))
goto out_flush_buf;
/* full packet: process */
if (likely((pkt_len > 0) && (pkt_len <= buf_len - pos))) {
usbtouch_process_pkt(usbtouch, buffer + pos, pkt_len);
} else {
/* incomplete packet: save in buffer */
memcpy(usbtouch->buffer, buffer + pos, buf_len - pos);
usbtouch->buf_len = buf_len - pos;
return;
}
pos += pkt_len;
}
out_flush_buf:
usbtouch->buf_len = 0;
return;
}
#endif
static void usbtouch_irq(struct urb *urb)
{
struct usbtouch_usb *usbtouch = urb->context;
int retval;
switch (urb->status) {
case 0:
/* success */
break;
case -ETIME:
/* 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;
}
usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length);
exit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
err("%s - usb_submit_urb failed with result: %d",
__FUNCTION__, retval);
}
static int usbtouch_open(struct input_dev *input)
{
struct usbtouch_usb *usbtouch = input->private;
usbtouch->irq->dev = usbtouch->udev;
if (usb_submit_urb(usbtouch->irq, GFP_KERNEL))
return -EIO;
return 0;
}
static void usbtouch_close(struct input_dev *input)
{
struct usbtouch_usb *usbtouch = input->private;
usb_kill_urb(usbtouch->irq);
}
static void usbtouch_free_buffers(struct usb_device *udev,
struct usbtouch_usb *usbtouch)
{
if (usbtouch->data)
usb_buffer_free(udev, usbtouch->type->rept_size,
usbtouch->data, usbtouch->data_dma);
kfree(usbtouch->buffer);
}
static int usbtouch_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usbtouch_usb *usbtouch;
struct input_dev *input_dev;
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_device *udev = interface_to_usbdev(intf);
struct usbtouch_device_info *type;
int err = -ENOMEM;
interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;
usbtouch = kzalloc(sizeof(struct usbtouch_usb), GFP_KERNEL);
input_dev = input_allocate_device();
if (!usbtouch || !input_dev)
goto out_free;
type = &usbtouch_dev_info[id->driver_info];
usbtouch->type = type;
if (!type->process_pkt)
type->process_pkt = usbtouch_process_pkt;
usbtouch->data = usb_buffer_alloc(udev, type->rept_size,
GFP_KERNEL, &usbtouch->data_dma);
if (!usbtouch->data)
goto out_free;
if (type->flags & USBTOUCH_FLG_BUFFER) {
usbtouch->buffer = kmalloc(type->rept_size, GFP_KERNEL);
if (!usbtouch->buffer)
goto out_free_buffers;
}
usbtouch->irq = usb_alloc_urb(0, GFP_KERNEL);
if (!usbtouch->irq) {
dbg("%s - usb_alloc_urb failed: usbtouch->irq", __FUNCTION__);
goto out_free_buffers;
}
usbtouch->udev = udev;
usbtouch->input = input_dev;
if (udev->manufacturer)
strlcpy(usbtouch->name, udev->manufacturer, sizeof(usbtouch->name));
if (udev->product) {
if (udev->manufacturer)
strlcat(usbtouch->name, " ", sizeof(usbtouch->name));
strlcat(usbtouch->name, udev->product, sizeof(usbtouch->name));
}
if (!strlen(usbtouch->name))
snprintf(usbtouch->name, sizeof(usbtouch->name),
"USB Touchscreen %04x:%04x",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
usb_make_path(udev, usbtouch->phys, sizeof(usbtouch->phys));
strlcpy(usbtouch->phys, "/input0", sizeof(usbtouch->phys));
input_dev->name = usbtouch->name;
input_dev->phys = usbtouch->phys;
usb_to_input_id(udev, &input_dev->id);
input_dev->cdev.dev = &intf->dev;
input_dev->private = usbtouch;
input_dev->open = usbtouch_open;
input_dev->close = usbtouch_close;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
input_set_abs_params(input_dev, ABS_X, type->min_xc, type->max_xc, 0, 0);
input_set_abs_params(input_dev, ABS_Y, type->min_yc, type->max_yc, 0, 0);
if (type->max_press)
input_set_abs_params(input_dev, ABS_PRESSURE, type->min_press,
type->max_press, 0, 0);
usb_fill_int_urb(usbtouch->irq, usbtouch->udev,
usb_rcvintpipe(usbtouch->udev, endpoint->bEndpointAddress),
usbtouch->data, type->rept_size,
usbtouch_irq, usbtouch, endpoint->bInterval);
usbtouch->irq->dev = usbtouch->udev;
usbtouch->irq->transfer_dma = usbtouch->data_dma;
usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* device specific init */
if (type->init) {
err = type->init(usbtouch);
if (err) {
dbg("%s - type->init() failed, err: %d", __FUNCTION__, err);
goto out_free_buffers;
}
}
err = input_register_device(usbtouch->input);
if (err) {
dbg("%s - input_register_device failed, err: %d", __FUNCTION__, err);
goto out_free_buffers;
}
usb_set_intfdata(intf, usbtouch);
return 0;
out_free_buffers:
usbtouch_free_buffers(udev, usbtouch);
out_free:
input_free_device(input_dev);
kfree(usbtouch);
return err;
}
static void usbtouch_disconnect(struct usb_interface *intf)
{
struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
dbg("%s - called", __FUNCTION__);
if (!usbtouch)
return;
dbg("%s - usbtouch is initialized, cleaning up", __FUNCTION__);
usb_set_intfdata(intf, NULL);
usb_kill_urb(usbtouch->irq);
input_unregister_device(usbtouch->input);
usb_free_urb(usbtouch->irq);
usbtouch_free_buffers(interface_to_usbdev(intf), usbtouch);
kfree(usbtouch);
}
MODULE_DEVICE_TABLE(usb, usbtouch_devices);
static struct usb_driver usbtouch_driver = {
.name = "usbtouchscreen",
.probe = usbtouch_probe,
.disconnect = usbtouch_disconnect,
.id_table = usbtouch_devices,
};
static int __init usbtouch_init(void)
{
return usb_register(&usbtouch_driver);
}
static void __exit usbtouch_cleanup(void)
{
usb_deregister(&usbtouch_driver);
}
module_init(usbtouch_init);
module_exit(usbtouch_cleanup);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_ALIAS("touchkitusb");
MODULE_ALIAS("itmtouch");
MODULE_ALIAS("mtouchusb");

131
drivers/usb/input/wacom.h Normal file
View File

@@ -0,0 +1,131 @@
/*
* drivers/usb/input/wacom.h
*
* 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-2006 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, ...
* v1.43 (pc) - Added support for Cintiq 21UX
* - Fixed a Graphire bug
* - Merged wacom_intuos3_irq into wacom_intuos_irq
* v1.44 (pc) - Added support for Graphire4, Cintiq 710, Intuos3 6x11, etc.
* - Report Device IDs
* v1.45 (pc) - Added support for DTF 521, Intuos3 12x12 and 12x19
* - Minor data report fix
* v1.46 (pc) - Split wacom.c into wacom_sys.c and wacom_wac.c,
* - where wacom_sys.c deals with system specific code,
* - and wacom_wac.c deals with Wacom specific code
* - Support Intuos3 4x6
*/
/*
* 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.
*/
#ifndef WACOM_H
#define WACOM_H
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb/input.h>
#include <asm/unaligned.h>
/*
* Version Information
*/
#define DRIVER_VERSION "v1.46"
#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 {
dma_addr_t data_dma;
struct input_dev *dev;
struct usb_device *usbdev;
struct urb *irq;
struct wacom_wac * wacom_wac;
char phys[32];
};
struct wacom_combo {
struct wacom * wacom;
struct urb * urb;
};
extern int wacom_wac_irq(struct wacom_wac * wacom_wac, void * wcombo);
extern void wacom_report_abs(void *wcombo, unsigned int abs_type, int abs_data);
extern void wacom_report_rel(void *wcombo, unsigned int rel_type, int rel_data);
extern void wacom_report_key(void *wcombo, unsigned int key_type, int key_data);
extern void wacom_input_event(void *wcombo, unsigned int type, unsigned int code, int value);
extern void wacom_input_sync(void *wcombo);
extern void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_i3s(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern __u16 wacom_le16_to_cpu(unsigned char *data);
extern __u16 wacom_be16_to_cpu(unsigned char *data);
extern struct wacom_features * get_wacom_feature(const struct usb_device_id *id);
extern const struct usb_device_id * get_device_table(void);
#endif

View File

@@ -0,0 +1,312 @@
/*
* drivers/usb/input/wacom_sys.c
*
* USB Wacom Graphire and Wacom Intuos tablet support - system specific code
*/
/*
* 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 "wacom.h"
#include "wacom_wac.h"
#define USB_REQ_GET_REPORT 0x01
#define USB_REQ_SET_REPORT 0x09
static int usb_get_report(struct usb_interface *intf, unsigned char type,
unsigned char id, void *buf, int size)
{
return usb_control_msg(interface_to_usbdev(intf),
usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
USB_REQ_GET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
(type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
buf, size, 100);
}
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 struct input_dev * get_input_dev(struct wacom_combo *wcombo)
{
return wcombo->wacom->dev;
}
static void wacom_sys_irq(struct urb *urb)
{
struct wacom *wacom = urb->context;
struct wacom_combo wcombo;
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;
}
wcombo.wacom = wacom;
wcombo.urb = urb;
if (wacom_wac_irq(wacom->wacom_wac, (void *)&wcombo))
input_sync(get_input_dev(&wcombo));
exit:
retval = usb_submit_urb (urb, GFP_ATOMIC);
if (retval)
err ("%s - usb_submit_urb failed with result %d",
__FUNCTION__, retval);
}
void wacom_report_key(void *wcombo, unsigned int key_type, int key_data)
{
input_report_key(get_input_dev((struct wacom_combo *)wcombo), key_type, key_data);
return;
}
void wacom_report_abs(void *wcombo, unsigned int abs_type, int abs_data)
{
input_report_abs(get_input_dev((struct wacom_combo *)wcombo), abs_type, abs_data);
return;
}
void wacom_report_rel(void *wcombo, unsigned int rel_type, int rel_data)
{
input_report_rel(get_input_dev((struct wacom_combo *)wcombo), rel_type, rel_data);
return;
}
void wacom_input_event(void *wcombo, unsigned int type, unsigned int code, int value)
{
input_event(get_input_dev((struct wacom_combo *)wcombo), type, code, value);
return;
}
__u16 wacom_be16_to_cpu(unsigned char *data)
{
__u16 value;
value = be16_to_cpu(*(__be16 *) data);
return value;
}
__u16 wacom_le16_to_cpu(unsigned char *data)
{
__u16 value;
value = le16_to_cpu(*(__le16 *) data);
return value;
}
void wacom_input_sync(void *wcombo)
{
input_sync(get_input_dev((struct wacom_combo *)wcombo));
return;
}
static int wacom_open(struct input_dev *dev)
{
struct wacom *wacom = dev->private;
wacom->irq->dev = wacom->usbdev;
if (usb_submit_urb(wacom->irq, GFP_KERNEL))
return -EIO;
return 0;
}
static void wacom_close(struct input_dev *dev)
{
struct wacom *wacom = dev->private;
usb_kill_urb(wacom->irq);
}
void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->evbit[0] |= BIT(EV_MSC);
input_dev->mscbit[0] |= BIT(MSC_SERIAL);
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_4);
}
void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->evbit[0] |= BIT(EV_REL);
input_dev->relbit[0] |= BIT(REL_WHEEL);
input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_STYLUS2);
input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom_wac->features->distance_max, 0, 0);
}
void input_dev_i3s(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3);
input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
}
void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
}
void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->evbit[0] |= BIT(EV_MSC) | BIT(EV_REL);
input_dev->mscbit[0] |= BIT(MSC_SERIAL);
input_dev->relbit[0] |= BIT(REL_WHEEL);
input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE) | BIT(BTN_SIDE) | BIT(BTN_EXTRA);
input_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);
input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom_wac->features->distance_max, 0, 0);
input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0);
input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0);
input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0);
input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0);
input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
}
void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2) | BIT(BTN_TOOL_RUBBER);
}
void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER);
}
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;
struct wacom *wacom;
struct wacom_wac *wacom_wac;
struct input_dev *input_dev;
char rep_data[2], limit = 0;
wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
wacom_wac = kzalloc(sizeof(struct wacom_wac), GFP_KERNEL);
input_dev = input_allocate_device();
if (!wacom || !input_dev || !wacom_wac)
goto fail1;
wacom_wac->data = usb_buffer_alloc(dev, 10, GFP_KERNEL, &wacom->data_dma);
if (!wacom_wac->data)
goto fail1;
wacom->irq = usb_alloc_urb(0, GFP_KERNEL);
if (!wacom->irq)
goto fail2;
wacom->usbdev = dev;
wacom->dev = input_dev;
usb_make_path(dev, wacom->phys, sizeof(wacom->phys));
strlcat(wacom->phys, "/input0", sizeof(wacom->phys));
wacom_wac->features = get_wacom_feature(id);
BUG_ON(wacom_wac->features->pktlen > 10);
input_dev->name = wacom_wac->features->name;
wacom->wacom_wac = wacom_wac;
usb_to_input_id(dev, &input_dev->id);
input_dev->cdev.dev = &intf->dev;
input_dev->private = wacom;
input_dev->open = wacom_open;
input_dev->close = wacom_close;
input_dev->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS);
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH) | BIT(BTN_STYLUS);
input_set_abs_params(input_dev, ABS_X, 0, wacom_wac->features->x_max, 4, 0);
input_set_abs_params(input_dev, ABS_Y, 0, wacom_wac->features->y_max, 4, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, wacom_wac->features->pressure_max, 0, 0);
input_dev->absbit[LONG(ABS_MISC)] |= BIT(ABS_MISC);
wacom_init_input_dev(input_dev, wacom_wac);
endpoint = &intf->cur_altsetting->endpoint[0].desc;
usb_fill_int_urb(wacom->irq, dev,
usb_rcvintpipe(dev, endpoint->bEndpointAddress),
wacom_wac->data, wacom_wac->features->pktlen,
wacom_sys_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. Repeat until it succeeds */
do {
rep_data[0] = 2;
rep_data[1] = 2;
usb_set_report(intf, 3, 2, rep_data, 2);
usb_get_report(intf, 3, 2, rep_data, 2);
} while (rep_data[1] != 2 && limit++ < 5);
usb_set_intfdata(intf, wacom);
return 0;
fail2: usb_buffer_free(dev, 10, wacom_wac->data, wacom->data_dma);
fail1: input_free_device(input_dev);
kfree(wacom);
kfree(wacom_wac);
return -ENOMEM;
}
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->wacom_wac->data, wacom->data_dma);
kfree(wacom->wacom_wac);
kfree(wacom);
}
}
static struct usb_driver wacom_driver = {
.name = "wacom",
.probe = wacom_probe,
.disconnect = wacom_disconnect,
};
static int __init wacom_init(void)
{
int result;
wacom_driver.id_table = get_device_table();
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);

View File

@@ -0,0 +1,675 @@
/*
* drivers/usb/input/wacom_wac.c
*
* USB Wacom Graphire and Wacom Intuos tablet support - Wacom specific code
*
*/
/*
* 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 "wacom.h"
#include "wacom_wac.h"
static int wacom_penpartner_irq(struct wacom_wac *wacom, void *wcombo)
{
unsigned char *data = wacom->data;
switch (data[0]) {
case 1:
if (data[5] & 0x80) {
wacom->tool[0] = (data[5] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
wacom->id[0] = (data[5] & 0x20) ? ERASER_DEVICE_ID : STYLUS_DEVICE_ID;
wacom_report_key(wcombo, wacom->tool[0], 1);
wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); /* report tool id */
wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1]));
wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3]));
wacom_report_abs(wcombo, ABS_PRESSURE, (signed char)data[6] + 127);
wacom_report_key(wcombo, BTN_TOUCH, ((signed char)data[6] > -127));
wacom_report_key(wcombo, BTN_STYLUS, (data[5] & 0x40));
} else {
wacom_report_key(wcombo, wacom->tool[0], 0);
wacom_report_abs(wcombo, ABS_MISC, 0); /* report tool id */
wacom_report_abs(wcombo, ABS_PRESSURE, -1);
wacom_report_key(wcombo, BTN_TOUCH, 0);
}
break;
case 2:
wacom_report_key(wcombo, BTN_TOOL_PEN, 1);
wacom_report_abs(wcombo, ABS_MISC, STYLUS_DEVICE_ID); /* report tool id */
wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1]));
wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3]));
wacom_report_abs(wcombo, ABS_PRESSURE, (signed char)data[6] + 127);
wacom_report_key(wcombo, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20));
wacom_report_key(wcombo, BTN_STYLUS, (data[5] & 0x40));
break;
default:
printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]);
return 0;
}
return 1;
}
static int wacom_pl_irq(struct wacom_wac *wacom, void *wcombo)
{
unsigned char *data = wacom->data;
int prox, id, pressure;
if (data[0] != 2) {
dbg("wacom_pl_irq: received unknown report #%d", data[0]);
return 0;
}
prox = data[1] & 0x40;
id = ERASER_DEVICE_ID;
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]) {
/* Eraser bit set for DTF */
if (data[1] & 0x10)
wacom->tool[1] = BTN_TOOL_RUBBER;
else
/* 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 */
wacom_report_key(wcombo, wacom->tool[1], 0);
wacom_input_sync(wcombo);
wacom->tool[1] = BTN_TOOL_PEN;
return 0;
}
}
if (wacom->tool[1] != BTN_TOOL_RUBBER) {
/* Unknown tool selected default to pen tool */
wacom->tool[1] = BTN_TOOL_PEN;
id = STYLUS_DEVICE_ID;
}
wacom_report_key(wcombo, wacom->tool[1], prox); /* report in proximity for tool */
wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
wacom_report_abs(wcombo, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
wacom_report_abs(wcombo, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
wacom_report_abs(wcombo, ABS_PRESSURE, pressure);
wacom_report_key(wcombo, BTN_TOUCH, data[4] & 0x08);
wacom_report_key(wcombo, BTN_STYLUS, data[4] & 0x10);
/* Only allow the stylus2 button to be reported for the pen tool. */
wacom_report_key(wcombo, 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;
}
wacom_report_key(wcombo, wacom->tool[1], prox);
}
wacom->tool[0] = prox; /* Save proximity state */
return 1;
}
static int wacom_ptu_irq(struct wacom_wac *wacom, void *wcombo)
{
unsigned char *data = wacom->data;
int id;
if (data[0] != 2) {
printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]);
return 0;
}
if (data[1] & 0x04) {
wacom_report_key(wcombo, BTN_TOOL_RUBBER, data[1] & 0x20);
wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x08);
id = ERASER_DEVICE_ID;
} else {
wacom_report_key(wcombo, BTN_TOOL_PEN, data[1] & 0x20);
wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x01);
id = STYLUS_DEVICE_ID;
}
wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[2]));
wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[4]));
wacom_report_abs(wcombo, ABS_PRESSURE, wacom_le16_to_cpu(&data[6]));
wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02);
wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x10);
return 1;
}
static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
{
unsigned char *data = wacom->data;
int x, y, id, rw;
if (data[0] != 2) {
dbg("wacom_graphire_irq: received unknown report #%d", data[0]);
return 0;
}
id = STYLUS_DEVICE_ID;
if (data[1] & 0x80) { /* in prox */
switch ((data[1] >> 5) & 3) {
case 0: /* Pen */
wacom->tool[0] = BTN_TOOL_PEN;
break;
case 1: /* Rubber */
wacom->tool[0] = BTN_TOOL_RUBBER;
id = ERASER_DEVICE_ID;
break;
case 2: /* Mouse with wheel */
wacom_report_key(wcombo, BTN_MIDDLE, data[1] & 0x04);
if (wacom->features->type == WACOM_G4) {
rw = data[7] & 0x04 ? (data[7] & 0x03)-4 : (data[7] & 0x03);
wacom_report_rel(wcombo, REL_WHEEL, -rw);
} else
wacom_report_rel(wcombo, REL_WHEEL, -(signed char) data[6]);
/* fall through */
case 3: /* Mouse without wheel */
wacom->tool[0] = BTN_TOOL_MOUSE;
id = CURSOR_DEVICE_ID;
wacom_report_key(wcombo, BTN_LEFT, data[1] & 0x01);
wacom_report_key(wcombo, BTN_RIGHT, data[1] & 0x02);
if (wacom->features->type == WACOM_G4)
wacom_report_abs(wcombo, ABS_DISTANCE, data[6] & 0x3f);
else
wacom_report_abs(wcombo, ABS_DISTANCE, data[7] & 0x3f);
break;
}
x = wacom_le16_to_cpu(&data[2]);
y = wacom_le16_to_cpu(&data[4]);
wacom_report_abs(wcombo, ABS_X, x);
wacom_report_abs(wcombo, ABS_Y, y);
if (wacom->tool[0] != BTN_TOOL_MOUSE) {
wacom_report_abs(wcombo, ABS_PRESSURE, data[6] | ((data[7] & 0x01) << 8));
wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x01);
wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02);
wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x04);
}
wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
wacom_report_key(wcombo, wacom->tool[0], 1);
} else if (!(data[1] & 0x90)) {
wacom_report_abs(wcombo, ABS_X, 0);
wacom_report_abs(wcombo, ABS_Y, 0);
if (wacom->tool[0] == BTN_TOOL_MOUSE) {
wacom_report_key(wcombo, BTN_LEFT, 0);
wacom_report_key(wcombo, BTN_RIGHT, 0);
wacom_report_abs(wcombo, ABS_DISTANCE, 0);
} else {
wacom_report_abs(wcombo, ABS_PRESSURE, 0);
wacom_report_key(wcombo, BTN_TOUCH, 0);
wacom_report_key(wcombo, BTN_STYLUS, 0);
wacom_report_key(wcombo, BTN_STYLUS2, 0);
}
wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
wacom_report_key(wcombo, wacom->tool[0], 0);
}
/* send pad data */
if (wacom->features->type == WACOM_G4) {
if (data[7] & 0xf8) {
wacom_input_sync(wcombo); /* sync last event */
wacom->id[1] = 1;
wacom->serial[1] = (data[7] & 0xf8);
wacom_report_key(wcombo, BTN_0, (data[7] & 0x40));
wacom_report_key(wcombo, BTN_4, (data[7] & 0x80));
rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3);
wacom_report_rel(wcombo, REL_WHEEL, rw);
wacom_report_key(wcombo, BTN_TOOL_FINGER, 0xf0);
wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID);
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
} else if (wacom->id[1]) {
wacom_input_sync(wcombo); /* sync last event */
wacom->id[1] = 0;
wacom_report_key(wcombo, BTN_0, (data[7] & 0x40));
wacom_report_key(wcombo, BTN_4, (data[7] & 0x80));
wacom_report_key(wcombo, BTN_TOOL_FINGER, 0);
wacom_report_abs(wcombo, ABS_MISC, 0);
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
}
}
return 1;
}
static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
{
unsigned char *data = wacom->data;
int idx;
/* tool number */
idx = data[1] & 0x01;
/* Enter report */
if ((data[1] & 0xfc) == 0xc0) {
/* serial number of the tool */
wacom->serial[idx] = ((data[3] & 0x0f) << 28) +
(data[4] << 20) + (data[5] << 12) +
(data[6] << 4) + (data[7] >> 4);
wacom->id[idx] = (data[2] << 4) | (data[3] >> 4);
switch (wacom->id[idx]) {
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;
default: /* Unknown tool */
wacom->tool[idx] = BTN_TOOL_PEN;
}
return 1;
}
/* Exit report */
if ((data[1] & 0xfe) == 0x80) {
wacom_report_abs(wcombo, ABS_X, 0);
wacom_report_abs(wcombo, ABS_Y, 0);
wacom_report_abs(wcombo, ABS_DISTANCE, 0);
if (wacom->tool[idx] >= BTN_TOOL_MOUSE) {
wacom_report_key(wcombo, BTN_LEFT, 0);
wacom_report_key(wcombo, BTN_MIDDLE, 0);
wacom_report_key(wcombo, BTN_RIGHT, 0);
wacom_report_key(wcombo, BTN_SIDE, 0);
wacom_report_key(wcombo, BTN_EXTRA, 0);
wacom_report_abs(wcombo, ABS_THROTTLE, 0);
wacom_report_abs(wcombo, ABS_RZ, 0);
} else {
wacom_report_abs(wcombo, ABS_PRESSURE, 0);
wacom_report_abs(wcombo, ABS_TILT_X, 0);
wacom_report_abs(wcombo, ABS_TILT_Y, 0);
wacom_report_key(wcombo, BTN_STYLUS, 0);
wacom_report_key(wcombo, BTN_STYLUS2, 0);
wacom_report_key(wcombo, BTN_TOUCH, 0);
wacom_report_abs(wcombo, ABS_WHEEL, 0);
}
wacom_report_key(wcombo, wacom->tool[idx], 0);
wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
return 2;
}
return 0;
}
static void wacom_intuos_general(struct wacom_wac *wacom, void *wcombo)
{
unsigned char *data = wacom->data;
unsigned int t;
/* general pen packet */
if ((data[1] & 0xb8) == 0xa0) {
t = (data[6] << 2) | ((data[7] >> 6) & 3);
wacom_report_abs(wcombo, ABS_PRESSURE, t);
wacom_report_abs(wcombo, ABS_TILT_X,
((data[7] << 1) & 0x7e) | (data[8] >> 7));
wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f);
wacom_report_key(wcombo, BTN_STYLUS, data[1] & 2);
wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 4);
wacom_report_key(wcombo, BTN_TOUCH, t > 10);
}
/* airbrush second packet */
if ((data[1] & 0xbc) == 0xb4) {
wacom_report_abs(wcombo, ABS_WHEEL,
(data[6] << 2) | ((data[7] >> 6) & 3));
wacom_report_abs(wcombo, ABS_TILT_X,
((data[7] << 1) & 0x7e) | (data[8] >> 7));
wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f);
}
return;
}
static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
{
unsigned char *data = wacom->data;
unsigned int t;
int idx, result;
if (data[0] != 2 && data[0] != 5 && data[0] != 6 && data[0] != 12) {
dbg("wacom_intuos_irq: received unknown report #%d", data[0]);
return 0;
}
/* tool number */
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;
wacom_report_key(wcombo, BTN_0, (data[5] & 0x01));
wacom_report_key(wcombo, BTN_1, (data[5] & 0x02));
wacom_report_key(wcombo, BTN_2, (data[5] & 0x04));
wacom_report_key(wcombo, BTN_3, (data[5] & 0x08));
wacom_report_key(wcombo, BTN_4, (data[6] & 0x01));
wacom_report_key(wcombo, BTN_5, (data[6] & 0x02));
wacom_report_key(wcombo, BTN_6, (data[6] & 0x04));
wacom_report_key(wcombo, BTN_7, (data[6] & 0x08));
wacom_report_abs(wcombo, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]);
wacom_report_abs(wcombo, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
if((data[5] & 0x0f) | (data[6] & 0x0f) | (data[1] & 0x1f) |
data[2] | (data[3] & 0x1f) | data[4])
wacom_report_key(wcombo, wacom->tool[1], 1);
else
wacom_report_key(wcombo, wacom->tool[1], 0);
wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID);
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xffffffff);
return 1;
}
/* process in/out prox events */
result = wacom_intuos_inout(wacom, wcombo);
if (result)
return result-1;
/* Only large I3 and I1 & I2 support Lense Cursor */
if((wacom->tool[idx] == BTN_TOOL_LENS)
&& ((wacom->features->type == INTUOS3)
|| (wacom->features->type == INTUOS3S)))
return 0;
/* Cintiq doesn't send data when RDY bit isn't set */
if ((wacom->features->type == CINTIQ) && !(data[1] & 0x40))
return 0;
if (wacom->features->type >= INTUOS3S) {
wacom_report_abs(wcombo, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1));
wacom_report_abs(wcombo, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1));
wacom_report_abs(wcombo, ABS_DISTANCE, ((data[9] >> 2) & 0x3f));
} else {
wacom_report_abs(wcombo, ABS_X, wacom_be16_to_cpu(&data[2]));
wacom_report_abs(wcombo, ABS_Y, wacom_be16_to_cpu(&data[4]));
wacom_report_abs(wcombo, ABS_DISTANCE, ((data[9] >> 3) & 0x1f));
}
/* process general packets */
wacom_intuos_general(wacom, wcombo);
/* 4D mouse, 2D mouse, marker pen rotation, or Lens cursor packets */
if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) {
if (data[1] & 0x02) {
/* Rotation packet */
if (wacom->features->type >= INTUOS3S) {
/* I3 marker pen rotation reported as wheel
* due to valuator limitation
*/
t = (data[6] << 3) | ((data[7] >> 5) & 7);
t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) :
((t-1) / 2 + 450)) : (450 - t / 2) ;
wacom_report_abs(wcombo, ABS_WHEEL, t);
} else {
/* 4D mouse rotation packet */
t = (data[6] << 3) | ((data[7] >> 5) & 7);
wacom_report_abs(wcombo, ABS_RZ, (data[7] & 0x20) ?
((t - 1) / 2) : -t / 2);
}
} else if (!(data[1] & 0x10) && wacom->features->type < INTUOS3S) {
/* 4D mouse packet */
wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x01);
wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02);
wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x04);
wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x20);
wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x10);
t = (data[6] << 2) | ((data[7] >> 6) & 3);
wacom_report_abs(wcombo, ABS_THROTTLE, (data[8] & 0x08) ? -t : t);
} else if (wacom->tool[idx] == BTN_TOOL_MOUSE) {
/* 2D mouse packet */
wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x04);
wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x08);
wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x10);
wacom_report_rel(wcombo, REL_WHEEL, (data[8] & 0x01)
- ((data[8] & 0x02) >> 1));
/* I3 2D mouse side buttons */
if (wacom->features->type >= INTUOS3S && wacom->features->type <= INTUOS3L) {
wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x40);
wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x20);
}
} else if (wacom->features->type < INTUOS3S || wacom->features->type == INTUOS3L) {
/* Lens cursor packets */
wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x01);
wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02);
wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x04);
wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x10);
wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x08);
}
}
wacom_report_abs(wcombo, ABS_MISC, wacom->id[idx]); /* report tool id */
wacom_report_key(wcombo, wacom->tool[idx], 1);
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
return 1;
}
int wacom_wac_irq(struct wacom_wac *wacom_wac, void *wcombo)
{
switch (wacom_wac->features->type) {
case PENPARTNER:
return (wacom_penpartner_irq(wacom_wac, wcombo));
break;
case PL:
return (wacom_pl_irq(wacom_wac, wcombo));
break;
case WACOM_G4:
case GRAPHIRE:
return (wacom_graphire_irq(wacom_wac, wcombo));
break;
case PTU:
return (wacom_ptu_irq(wacom_wac, wcombo));
break;
case INTUOS:
case INTUOS3S:
case INTUOS3:
case INTUOS3L:
case CINTIQ:
return (wacom_intuos_irq(wacom_wac, wcombo));
break;
default:
return 0;
}
return 0;
}
void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
switch (wacom_wac->features->type) {
case WACOM_G4:
input_dev_g4(input_dev, wacom_wac);
/* fall through */
case GRAPHIRE:
input_dev_g(input_dev, wacom_wac);
break;
case INTUOS3:
case INTUOS3L:
case CINTIQ:
input_dev_i3(input_dev, wacom_wac);
/* fall through */
case INTUOS3S:
input_dev_i3s(input_dev, wacom_wac);
case INTUOS:
input_dev_i(input_dev, wacom_wac);
break;
case PL:
case PTU:
input_dev_pl(input_dev, wacom_wac);
break;
case PENPARTNER:
input_dev_pt(input_dev, wacom_wac);
break;
}
return;
}
static struct wacom_features wacom_features[] = {
{ "Wacom Penpartner", 7, 5040, 3780, 255, 0, PENPARTNER },
{ "Wacom Graphire", 8, 10206, 7422, 511, 63, GRAPHIRE },
{ "Wacom Graphire2 4x5", 8, 10206, 7422, 511, 63, GRAPHIRE },
{ "Wacom Graphire2 5x7", 8, 13918, 10206, 511, 63, GRAPHIRE },
{ "Wacom Graphire3", 8, 10208, 7424, 511, 63, GRAPHIRE },
{ "Wacom Graphire3 6x8", 8, 16704, 12064, 511, 63, GRAPHIRE },
{ "Wacom Graphire4 4x5", 8, 10208, 7424, 511, 63, WACOM_G4 },
{ "Wacom Graphire4 6x8", 8, 16704, 12064, 511, 63, WACOM_G4 },
{ "Wacom Volito", 8, 5104, 3712, 511, 63, GRAPHIRE },
{ "Wacom PenStation2", 8, 3250, 2320, 255, 63, GRAPHIRE },
{ "Wacom Volito2 4x5", 8, 5104, 3712, 511, 63, GRAPHIRE },
{ "Wacom Volito2 2x3", 8, 3248, 2320, 511, 63, GRAPHIRE },
{ "Wacom PenPartner2", 8, 3250, 2320, 255, 63, GRAPHIRE },
{ "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 31, INTUOS },
{ "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 31, INTUOS },
{ "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 31, INTUOS },
{ "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 31, INTUOS },
{ "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 31, INTUOS },
{ "Wacom PL400", 8, 5408, 4056, 255, 0, PL },
{ "Wacom PL500", 8, 6144, 4608, 255, 0, PL },
{ "Wacom PL600", 8, 6126, 4604, 255, 0, PL },
{ "Wacom PL600SX", 8, 6260, 5016, 255, 0, PL },
{ "Wacom PL550", 8, 6144, 4608, 511, 0, PL },
{ "Wacom PL800", 8, 7220, 5780, 511, 0, PL },
{ "Wacom PL700", 8, 6758, 5406, 511, 0, PL },
{ "Wacom PL510", 8, 6282, 4762, 511, 0, PL },
{ "Wacom DTU710", 8, 34080, 27660, 511, 0, PL },
{ "Wacom DTF521", 8, 6282, 4762, 511, 0, PL },
{ "Wacom DTF720", 8, 6858, 5506, 511, 0, PL },
{ "Wacom Cintiq Partner",8, 20480, 15360, 511, 0, PTU },
{ "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 31, INTUOS },
{ "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 31, INTUOS },
{ "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 31, INTUOS },
{ "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 31, INTUOS },
{ "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 31, INTUOS },
{ "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 63, INTUOS3S },
{ "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 63, INTUOS3 },
{ "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 63, INTUOS3 },
{ "Wacom Intuos3 12x12", 10, 60960, 60960, 1023, 63, INTUOS3L },
{ "Wacom Intuos3 12x19", 10, 97536, 60960, 1023, 63, INTUOS3L },
{ "Wacom Intuos3 6x11", 10, 54204, 31750, 1023, 63, INTUOS3 },
{ "Wacom Intuos3 4x6", 10, 31496, 19685, 1023, 63, INTUOS3S },
{ "Wacom Cintiq 21UX", 10, 87200, 65600, 1023, 63, CINTIQ },
{ "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 31, INTUOS },
{ }
};
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, 0x15) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x16) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x60) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x61) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x62) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x63) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x64) },
{ 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, 0x37) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x38) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x39) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC0) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC4) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x03) },
{ 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, 0xB0) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB1) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB2) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB3) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB4) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB5) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB7) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x3F) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) },
{ }
};
const struct usb_device_id * get_device_table(void) {
const struct usb_device_id * id_table = wacom_ids;
return id_table;
}
struct wacom_features * get_wacom_feature(const struct usb_device_id * id) {
int index = id - wacom_ids;
struct wacom_features *wf = &wacom_features[index];
return wf;
}
MODULE_DEVICE_TABLE(usb, wacom_ids);

View File

@@ -0,0 +1,49 @@
/*
* drivers/usb/input/wacom_wac.h
*
* 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.
*/
#ifndef WACOM_WAC_H
#define WACOM_WAC_H
#define STYLUS_DEVICE_ID 0x02
#define CURSOR_DEVICE_ID 0x06
#define ERASER_DEVICE_ID 0x0A
#define PAD_DEVICE_ID 0x0F
enum {
PENPARTNER = 0,
GRAPHIRE,
WACOM_G4,
PTU,
PL,
INTUOS,
INTUOS3S,
INTUOS3,
INTUOS3L,
CINTIQ,
MAX_TYPE
};
struct wacom_features {
char *name;
int pktlen;
int x_max;
int y_max;
int pressure_max;
int distance_max;
int type;
};
struct wacom_wac {
signed char *data;
int tool[2];
int id[2];
__u32 serial[2];
struct wacom_features *features;
};
#endif

428
drivers/usb/input/xpad.c Normal file
View File

@@ -0,0 +1,428 @@
/*
* X-Box gamepad - v0.0.6
*
* Copyright (c) 2002 Marko Friedemann <mfr@bmx-chemnitz.de>
* 2004 Oliver Schwartz <Oliver.Schwartz@gmx.de>,
* Steven Toth <steve@toth.demon.co.uk>,
* Franz Lehner <franz@caos.at>,
* Ivan Hawkes <blackhawk@ivanhawkes.com>
* 2005 Dominic Cerquetti <binary1230@yahoo.com>
* 2006 Adam Buchbinder <adam.buchbinder@gmail.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
*
*
* 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
* - XBOX Linux project - extra USB id's
*
* TODO:
* - fine tune axes (especially trigger axes)
* - fix "analog" buttons (reported as digital now)
* - get rumble working
* - need USB IDs for other dance pads
*
* 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
*
* 2004-10-02 - 0.0.6 : DDR pad support
* - borrowed from the XBOX linux kernel
* - USB id's for commonly used dance pads are present
* - dance pads will map D-PAD to buttons, not axes
* - pass the module paramater 'dpad_to_buttons' to force
* the D-PAD to map to buttons if your pad is not detected
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/smp_lock.h>
#include <linux/usb/input.h>
#define DRIVER_VERSION "v0.0.6"
#define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>"
#define DRIVER_DESC "X-Box pad driver"
#define XPAD_PKT_LEN 32
/* xbox d-pads should map to buttons, as is required for DDR pads
but we map them to axes when possible to simplify things */
#define MAP_DPAD_TO_BUTTONS 0
#define MAP_DPAD_TO_AXES 1
#define MAP_DPAD_UNKNOWN -1
static int dpad_to_buttons;
module_param(dpad_to_buttons, bool, S_IRUGO);
MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads");
static const struct xpad_device {
u16 idVendor;
u16 idProduct;
char *name;
u8 dpad_mapping;
} xpad_device[] = {
{ 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", MAP_DPAD_TO_AXES },
{ 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", MAP_DPAD_TO_AXES },
{ 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", MAP_DPAD_TO_AXES },
{ 0x045e, 0x0287, "Microsoft Xbox Controller S", MAP_DPAD_TO_AXES },
{ 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", MAP_DPAD_TO_BUTTONS },
{ 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", MAP_DPAD_TO_AXES },
{ 0x046d, 0xca84, "Logitech Xbox Cordless Controller", MAP_DPAD_TO_AXES },
{ 0x046d, 0xca88, "Logitech Compact Controller for Xbox", MAP_DPAD_TO_AXES },
{ 0x05fd, 0x1007, "Mad Catz Controller (unverified)", MAP_DPAD_TO_AXES },
{ 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)", MAP_DPAD_TO_AXES },
{ 0x0738, 0x4516, "Mad Catz Control Pad", MAP_DPAD_TO_AXES },
{ 0x0738, 0x4522, "Mad Catz LumiCON", MAP_DPAD_TO_AXES },
{ 0x0738, 0x4526, "Mad Catz Control Pad Pro", MAP_DPAD_TO_AXES },
{ 0x0738, 0x4536, "Mad Catz MicroCON", MAP_DPAD_TO_AXES },
{ 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS },
{ 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", MAP_DPAD_TO_AXES },
{ 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS },
{ 0x0c12, 0x8802, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES },
{ 0x0c12, 0x8810, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES },
{ 0x0c12, 0x9902, "HAMA VibraX - *FAULTY HARDWARE*", MAP_DPAD_TO_AXES },
{ 0x0e4c, 0x1097, "Radica Gamester Controller", MAP_DPAD_TO_AXES },
{ 0x0e4c, 0x2390, "Radica Games Jtech Controller", MAP_DPAD_TO_AXES},
{ 0x0e6f, 0x0003, "Logic3 Freebird wireless Controller", MAP_DPAD_TO_AXES },
{ 0x0e6f, 0x0005, "Eclipse wireless Controller", MAP_DPAD_TO_AXES },
{ 0x0e6f, 0x0006, "Edge wireless Controller", MAP_DPAD_TO_AXES },
{ 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", MAP_DPAD_TO_AXES },
{ 0x0f30, 0x0202, "Joytech Advanced Controller", MAP_DPAD_TO_AXES },
{ 0x0f30, 0x8888, "BigBen XBMiniPad Controller", MAP_DPAD_TO_AXES },
{ 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", MAP_DPAD_TO_AXES },
{ 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS },
{ 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS },
{ 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES },
{ 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN }
};
static const 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 */
};
/* only used if MAP_DPAD_TO_BUTTONS */
static const signed short xpad_btn_pad[] = {
BTN_LEFT, BTN_RIGHT, /* d-pad left, right */
BTN_0, BTN_1, /* d-pad up, down (XXX names??) */
-1 /* terminating entry */
};
static const signed short xpad_abs[] = {
ABS_X, ABS_Y, /* left stick */
ABS_RX, ABS_RY, /* right stick */
ABS_Z, ABS_RZ, /* triggers left/right */
-1 /* terminating entry */
};
/* only used if MAP_DPAD_TO_AXES */
static const signed short xpad_abs_pad[] = {
ABS_HAT0X, ABS_HAT0Y, /* d-pad axes */
-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 dpad_mapping; /* map d-pad to buttons or to axes */
};
/*
* 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 input_dev *dev = xpad->dev;
/* 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 */
if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) {
input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04));
input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01));
} else /* xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS */ {
input_report_key(dev, BTN_LEFT, data[2] & 0x04);
input_report_key(dev, BTN_RIGHT, data[2] & 0x08);
input_report_key(dev, BTN_0, data[2] & 0x01); // up
input_report_key(dev, BTN_1, data[2] & 0x02); // down
}
/* start/back buttons and stick press left/right */
input_report_key(dev, BTN_START, data[2] & 0x10);
input_report_key(dev, BTN_BACK, data[2] & 0x20);
input_report_key(dev, BTN_THUMBL, data[2] & 0x40);
input_report_key(dev, BTN_THUMBR, data[2] & 0x80);
/* "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 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);
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;
xpad->irq_in->dev = xpad->udev;
if (usb_submit_urb(xpad->irq_in, GFP_KERNEL))
return -EIO;
return 0;
}
static void xpad_close (struct input_dev *dev)
{
struct usb_xpad *xpad = dev->private;
usb_kill_urb(xpad->irq_in);
}
static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
{
set_bit(abs, input_dev->absbit);
switch (abs) {
case ABS_X:
case ABS_Y:
case ABS_RX:
case ABS_RY: /* the two sticks */
input_set_abs_params(input_dev, abs, -32768, 32767, 16, 128);
break;
case ABS_Z:
case ABS_RZ: /* the triggers */
input_set_abs_params(input_dev, abs, 0, 255, 0, 0);
break;
case ABS_HAT0X:
case ABS_HAT0Y: /* the d-pad (only if MAP_DPAD_TO_AXES) */
input_set_abs_params(input_dev, abs, -1, 1, 0, 0);
break;
}
}
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;
struct input_dev *input_dev;
struct usb_endpoint_descriptor *ep_irq_in;
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;
}
xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL);
input_dev = input_allocate_device();
if (!xpad || !input_dev)
goto fail1;
xpad->idata = usb_buffer_alloc(udev, XPAD_PKT_LEN,
GFP_ATOMIC, &xpad->idata_dma);
if (!xpad->idata)
goto fail1;
xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL);
if (!xpad->irq_in)
goto fail2;
xpad->udev = udev;
xpad->dpad_mapping = xpad_device[i].dpad_mapping;
if (xpad->dpad_mapping == MAP_DPAD_UNKNOWN)
xpad->dpad_mapping = dpad_to_buttons;
xpad->dev = input_dev;
usb_make_path(udev, xpad->phys, sizeof(xpad->phys));
strlcat(xpad->phys, "/input0", sizeof(xpad->phys));
input_dev->name = xpad_device[i].name;
input_dev->phys = xpad->phys;
usb_to_input_id(udev, &input_dev->id);
input_dev->cdev.dev = &intf->dev;
input_dev->private = xpad;
input_dev->open = xpad_open;
input_dev->close = xpad_close;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
/* set up buttons */
for (i = 0; xpad_btn[i] >= 0; i++)
set_bit(xpad_btn[i], input_dev->keybit);
if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS)
for (i = 0; xpad_btn_pad[i] >= 0; i++)
set_bit(xpad_btn_pad[i], input_dev->keybit);
/* set up axes */
for (i = 0; xpad_abs[i] >= 0; i++)
xpad_set_up_abs(input_dev, xpad_abs[i]);
if (xpad->dpad_mapping == MAP_DPAD_TO_AXES)
for (i = 0; xpad_abs_pad[i] >= 0; i++)
xpad_set_up_abs(input_dev, xpad_abs_pad[i]);
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;
input_register_device(xpad->dev);
usb_set_intfdata(intf, xpad);
return 0;
fail2: usb_buffer_free(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
fail1: input_free_device(input_dev);
kfree(xpad);
return -ENOMEM;
}
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 = {
.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");

1002
drivers/usb/input/yealink.c Normal file

File diff suppressed because it is too large Load Diff

220
drivers/usb/input/yealink.h Normal file
View File

@@ -0,0 +1,220 @@
/*
* drivers/usb/input/yealink.h
*
* Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.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
*/
#ifndef INPUT_YEALINK_H
#define INPUT_YEALINK_H
/* Using the control channel on interface 3 various aspects of the phone
* can be controlled like LCD, LED, dialtone and the ringtone.
*/
struct yld_ctl_packet {
u8 cmd; /* command code, see below */
u8 size; /* 1-11, size of used data bytes. */
u16 offset; /* internal packet offset */
u8 data[11];
s8 sum; /* negative sum of 15 preceding bytes */
} __attribute__ ((packed));
#define USB_PKT_LEN sizeof(struct yld_ctl_packet)
/* The following yld_ctl_packet's are available: */
/* Init registers
*
* cmd 0x8e
* size 10
* offset 0
* data 0,0,0,0....
*/
#define CMD_INIT 0x8e
/* Request key scan
*
* cmd 0x80
* size 1
* offset 0
* data[0] on return returns the key number, if it changes there's a new
* key pressed.
*/
#define CMD_KEYPRESS 0x80
/* Request scancode
*
* cmd 0x81
* size 1
* offset key number [0-1f]
* data[0] on return returns the scancode
*/
#define CMD_SCANCODE 0x81
/* Set LCD
*
* cmd 0x04
* size 1-11
* offset 0-23
* data segment bits
*/
#define CMD_LCD 0x04
/* Set led
*
* cmd 0x05
* size 1
* offset 0
* data[0] 0 OFF / 1 ON
*/
#define CMD_LED 0x05
/* Set ringtone volume
*
* cmd 0x11
* size 1
* offset 0
* data[0] 0-0xff volume
*/
#define CMD_RING_VOLUME 0x11
/* Set ringtone notes
*
* cmd 0x02
* size 1-11
* offset 0->
* data binary representation LE16(-freq), LE16(duration) ....
*/
#define CMD_RING_NOTE 0x02
/* Sound ringtone via the speaker on the back
*
* cmd 0x03
* size 1
* offset 0
* data[0] 0 OFF / 0x24 ON
*/
#define CMD_RINGTONE 0x03
/* Sound dial tone via the ear speaker
*
* cmd 0x09
* size 1
* offset 0
* data[0] 0 OFF / 1 ON
*/
#define CMD_DIALTONE 0x09
#endif /* INPUT_YEALINK_H */
#if defined(_SEG) && defined(_PIC)
/* This table maps the LCD segments onto individual bit positions in the
* yld_status struct.
*/
/* LCD, each segment must be driven seperately.
*
* Layout:
*
* |[] [][] [][] [][] in |[][]
* |[] M [][] D [][] : [][] out |[][]
* store
*
* NEW REP SU MO TU WE TH FR SA
*
* [] [] [] [] [] [] [] [] [] [] [] []
* [] [] [] [] [] [] [] [] [] [] [] []
*/
/* Line 1
* Format : 18.e8.M8.88...188
* Icon names : M D : IN OUT STORE
*/
#define LCD_LINE1_OFFSET 0
#define LCD_LINE1_SIZE 17
/* Note: first g then f => ! ! */
/* _SEG( type a b c d e g f ) */
_SEG('1', 0,0 , 22,2 , 22,2 , 0,0 , 0,0 , 0,0 , 0,0 ),
_SEG('8', 20,1 , 20,2 , 20,4 , 20,8 , 21,4 , 21,2 , 21,1 ),
_PIC('.', 22,1 , "M" ),
_SEG('e', 18,1 , 18,2 , 18,4 , 18,1 , 19,2 , 18,1 , 19,1 ),
_SEG('8', 16,1 , 16,2 , 16,4 , 16,8 , 17,4 , 17,2 , 17,1 ),
_PIC('.', 15,8 , "D" ),
_SEG('M', 14,1 , 14,2 , 14,4 , 14,1 , 15,4 , 15,2 , 15,1 ),
_SEG('8', 12,1 , 12,2 , 12,4 , 12,8 , 13,4 , 13,2 , 13,1 ),
_PIC('.', 11,8 , ":" ),
_SEG('8', 10,1 , 10,2 , 10,4 , 10,8 , 11,4 , 11,2 , 11,1 ),
_SEG('8', 8,1 , 8,2 , 8,4 , 8,8 , 9,4 , 9,2 , 9,1 ),
_PIC('.', 7,1 , "IN" ),
_PIC('.', 7,2 , "OUT" ),
_PIC('.', 7,4 , "STORE" ),
_SEG('1', 0,0 , 5,1 , 5,1 , 0,0 , 0,0 , 0,0 , 0,0 ),
_SEG('8', 4,1 , 4,2 , 4,4 , 4,8 , 5,8 , 5,4 , 5,2 ),
_SEG('8', 2,1 , 2,2 , 2,4 , 2,8 , 3,4 , 3,2 , 3,1 ),
/* Line 2
* Format : .........
* Pict. name : NEW REP SU MO TU WE TH FR SA
*/
#define LCD_LINE2_OFFSET LCD_LINE1_OFFSET + LCD_LINE1_SIZE
#define LCD_LINE2_SIZE 9
_PIC('.', 23,2 , "NEW" ),
_PIC('.', 23,4 , "REP" ),
_PIC('.', 1,8 , "SU" ),
_PIC('.', 1,4 , "MO" ),
_PIC('.', 1,2 , "TU" ),
_PIC('.', 1,1 , "WE" ),
_PIC('.', 0,1 , "TH" ),
_PIC('.', 0,2 , "FR" ),
_PIC('.', 0,4 , "SA" ),
/* Line 3
* Format : 888888888888
*/
#define LCD_LINE3_OFFSET LCD_LINE2_OFFSET + LCD_LINE2_SIZE
#define LCD_LINE3_SIZE 12
_SEG('8', 22,16, 22,32, 22,64, 22,128, 23,128, 23,64, 23,32 ),
_SEG('8', 20,16, 20,32, 20,64, 20,128, 21,128, 21,64, 21,32 ),
_SEG('8', 18,16, 18,32, 18,64, 18,128, 19,128, 19,64, 19,32 ),
_SEG('8', 16,16, 16,32, 16,64, 16,128, 17,128, 17,64, 17,32 ),
_SEG('8', 14,16, 14,32, 14,64, 14,128, 15,128, 15,64, 15,32 ),
_SEG('8', 12,16, 12,32, 12,64, 12,128, 13,128, 13,64, 13,32 ),
_SEG('8', 10,16, 10,32, 10,64, 10,128, 11,128, 11,64, 11,32 ),
_SEG('8', 8,16, 8,32, 8,64, 8,128, 9,128, 9,64, 9,32 ),
_SEG('8', 6,16, 6,32, 6,64, 6,128, 7,128, 7,64, 7,32 ),
_SEG('8', 4,16, 4,32, 4,64, 4,128, 5,128, 5,64, 5,32 ),
_SEG('8', 2,16, 2,32, 2,64, 2,128, 3,128, 3,64, 3,32 ),
_SEG('8', 0,16, 0,32, 0,64, 0,128, 1,128, 1,64, 1,32 ),
/* Line 4
*
* The LED, DIALTONE and RINGTONE are implemented as icons and use the same
* sysfs interface.
*/
#define LCD_LINE4_OFFSET LCD_LINE3_OFFSET + LCD_LINE3_SIZE
_PIC('.', offsetof(struct yld_status, led) , 0x01, "LED" ),
_PIC('.', offsetof(struct yld_status, dialtone) , 0x01, "DIALTONE" ),
_PIC('.', offsetof(struct yld_status, ringtone) , 0x24, "RINGTONE" ),
#undef _SEG
#undef _PIC
#endif /* _SEG && _PIC */