mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 02:32:09 -04:00
Merge master.kernel.org:/pub/scm/linux/kernel/git/dtor/input
* master.kernel.org:/pub/scm/linux/kernel/git/dtor/input: Input: move USB miscellaneous devices under drivers/input/misc Input: move USB mice under drivers/input/mouse Input: move USB gamepads under drivers/input/joystick Input: move USB touchscreens under drivers/input/touchscreen Input: move USB tablets under drivers/input/tablet Input: i8042 - fix AUX port detection with some chips Input: aaed2000_kbd - convert to use polldev library Input: drivers/usb/input - usb_buffer_free() cleanup Input: synaptics - don't complain about failed resets Input: pull input.h into uinpit.h Input: drivers/usb/input - fix sparse warnings (signedness) Input: evdev - fix some sparse warnings (signedness, shadowing) Input: drivers/joystick - fix various sparse warnings Input: force feedback - make sure effect is present before playing
This commit is contained in:
commit
393bfca19e
47 changed files with 404 additions and 404 deletions
|
|
@ -85,8 +85,6 @@ source "drivers/usb/class/Kconfig"
|
|||
|
||||
source "drivers/usb/storage/Kconfig"
|
||||
|
||||
source "drivers/usb/input/Kconfig"
|
||||
|
||||
source "drivers/usb/image/Kconfig"
|
||||
|
||||
source "drivers/usb/net/Kconfig"
|
||||
|
|
|
|||
|
|
@ -23,15 +23,6 @@ obj-$(CONFIG_USB_PRINTER) += class/
|
|||
obj-$(CONFIG_USB_STORAGE) += storage/
|
||||
obj-$(CONFIG_USB) += storage/
|
||||
|
||||
obj-$(CONFIG_USB_ACECAD) += input/
|
||||
obj-$(CONFIG_USB_AIPTEK) += input/
|
||||
obj-$(CONFIG_USB_ATI_REMOTE) += input/
|
||||
obj-$(CONFIG_USB_KBTAB) += input/
|
||||
obj-$(CONFIG_USB_MTOUCH) += input/
|
||||
obj-$(CONFIG_USB_POWERMATE) += input/
|
||||
obj-$(CONFIG_USB_WACOM) += input/
|
||||
obj-$(CONFIG_USB_XPAD) += input/
|
||||
|
||||
obj-$(CONFIG_USB_CATC) += net/
|
||||
obj-$(CONFIG_USB_KAWETH) += net/
|
||||
obj-$(CONFIG_USB_PEGASUS) += net/
|
||||
|
|
|
|||
|
|
@ -1,225 +0,0 @@
|
|||
#
|
||||
# USB Input driver configuration
|
||||
#
|
||||
comment "USB Input Devices"
|
||||
depends on USB
|
||||
|
||||
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.
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
#
|
||||
# Makefile for the USB input drivers
|
||||
#
|
||||
|
||||
# Multipart objects.
|
||||
wacom-objs := wacom_wac.o wacom_sys.o
|
||||
|
||||
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_KBTAB) += kbtab.o
|
||||
obj-$(CONFIG_USB_KEYSPAN_REMOTE) += keyspan_remote.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
|
||||
|
|
@ -1,289 +0,0 @@
|
|||
/*
|
||||
* 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 <[email protected]>"
|
||||
|
||||
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 = input_get_drvdata(dev);
|
||||
|
||||
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 = input_get_drvdata(dev);
|
||||
|
||||
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;
|
||||
int err = -ENOMEM;
|
||||
|
||||
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) {
|
||||
err = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
acecad->data = usb_buffer_alloc(dev, 8, GFP_KERNEL, &acecad->data_dma);
|
||||
if (!acecad->data) {
|
||||
err= -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
acecad->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!acecad->irq) {
|
||||
err = -ENOMEM;
|
||||
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->dev.parent = &intf->dev;
|
||||
|
||||
input_set_drvdata(input_dev, 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;
|
||||
|
||||
err = input_register_device(acecad->input);
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
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 err;
|
||||
}
|
||||
|
||||
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);
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,706 +0,0 @@
|
|||
/*
|
||||
* 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_get_drvdata(input);
|
||||
|
||||
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_get_drvdata(input);
|
||||
|
||||
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, error = -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)
|
||||
goto err_free_devs;
|
||||
|
||||
dev->data = usb_buffer_alloc(dev->udev, dev->datalen, GFP_KERNEL,
|
||||
&dev->urb->transfer_dma);
|
||||
if (!dev->data)
|
||||
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->dev.parent = &iface->dev;
|
||||
|
||||
input_set_drvdata(input_dev, 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);
|
||||
|
||||
error = input_register_device(dev->input);
|
||||
if (error)
|
||||
goto err_free_buffer;
|
||||
|
||||
/* save our data pointer in this interface device */
|
||||
usb_set_intfdata(iface, dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_buffer:
|
||||
usb_buffer_free(dev->udev, dev->datalen,
|
||||
dev->data, dev->urb->transfer_dma);
|
||||
err_free_urb:
|
||||
usb_free_urb(dev->urb);
|
||||
err_free_devs:
|
||||
usb_set_intfdata(iface, NULL);
|
||||
kfree(dev);
|
||||
input_free_device(input_dev);
|
||||
return error;
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
@ -1,862 +0,0 @@
|
|||
/*
|
||||
* 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 <[email protected]>"
|
||||
#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 */
|
||||
#define REPEAT_DELAY 500 /* 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");
|
||||
|
||||
static int repeat_delay = REPEAT_DELAY;
|
||||
module_param(repeat_delay, int, 0644);
|
||||
MODULE_PARM_DESC(repeat_delay, "Delay before sending repeats, default = 500 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 long first_jiffies;
|
||||
|
||||
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 = input_get_drvdata(inputdev);
|
||||
|
||||
/* 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 = input_get_drvdata(inputdev);
|
||||
|
||||
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) {
|
||||
unsigned long now = jiffies;
|
||||
|
||||
/* 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(now, ati_remote->old_jiffies +
|
||||
msecs_to_jiffies(repeat_filter))) {
|
||||
ati_remote->repeat_count++;
|
||||
} else {
|
||||
ati_remote->repeat_count = 0;
|
||||
ati_remote->first_jiffies = now;
|
||||
}
|
||||
|
||||
ati_remote->old_data[0] = data[1];
|
||||
ati_remote->old_data[1] = data[2];
|
||||
ati_remote->old_jiffies = now;
|
||||
|
||||
/* Ensure we skip at least the 4 first duplicate events (generated
|
||||
* by a single keypress), and continue skipping until repeat_delay
|
||||
* msecs have passed
|
||||
*/
|
||||
if (ati_remote->repeat_count > 0 &&
|
||||
(ati_remote->repeat_count < 5 ||
|
||||
time_before(now, ati_remote->first_jiffies +
|
||||
msecs_to_jiffies(repeat_delay))))
|
||||
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);
|
||||
|
||||
input_set_drvdata(idev, 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->dev.parent = &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 */
|
||||
err = input_register_device(ati_remote->idev);
|
||||
if (err)
|
||||
goto fail3;
|
||||
|
||||
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");
|
||||
|
|
@ -1,545 +0,0 @@
|
|||
/*
|
||||
* ati_remote2 - ATI/Philips USB RF remote driver
|
||||
*
|
||||
* Copyright (C) 2005 Ville Syrjala <syrjala@sci.fi>
|
||||
* Copyright (C) 2007 Peter Stokes <linux@dadeos.freeserve.co.uk>
|
||||
*
|
||||
* 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.2"
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
MODULE_AUTHOR("Ville Syrjala <[email protected]>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* ATI Remote Wonder II Channel Configuration
|
||||
*
|
||||
* The remote control can by assigned one of sixteen "channels" in order to facilitate
|
||||
* the use of multiple remote controls within range of each other.
|
||||
* A remote's "channel" may be altered by pressing and holding the "PC" button for
|
||||
* approximately 3 seconds, after which the button will slowly flash the count of the
|
||||
* currently configured "channel", using the numeric keypad enter a number between 1 and
|
||||
* 16 and then the "PC" button again, the button will slowly flash the count of the
|
||||
* newly configured "channel".
|
||||
*/
|
||||
|
||||
static unsigned int channel_mask = 0xFFFF;
|
||||
module_param(channel_mask, uint, 0644);
|
||||
MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...<1:Channel2><0:Channel1>");
|
||||
|
||||
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 = input_get_drvdata(idev);
|
||||
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 = input_get_drvdata(idev);
|
||||
|
||||
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];
|
||||
int channel, mode;
|
||||
|
||||
channel = data[0] >> 4;
|
||||
|
||||
if (!((1 << channel) & channel_mask))
|
||||
return;
|
||||
|
||||
mode = data[0] & 0x0F;
|
||||
|
||||
if (mode > 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 << mode) & 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 channel, mode, hw_code, index;
|
||||
|
||||
channel = data[0] >> 4;
|
||||
|
||||
if (!((1 << channel) & channel_mask))
|
||||
return;
|
||||
|
||||
mode = data[0] & 0x0F;
|
||||
|
||||
if (mode > 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 == mode)
|
||||
return;
|
||||
|
||||
if (data[1] == 0)
|
||||
ar2->mode = mode;
|
||||
|
||||
hw_code |= mode << 8;
|
||||
}
|
||||
|
||||
if (!((1 << mode) & 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, retval;
|
||||
|
||||
idev = input_allocate_device();
|
||||
if (!idev)
|
||||
return -ENOMEM;
|
||||
|
||||
ar2->idev = idev;
|
||||
input_set_drvdata(idev, 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->dev.parent = &ar2->udev->dev;
|
||||
|
||||
retval = input_register_device(idev);
|
||||
if (retval)
|
||||
input_free_device(idev);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
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_setup(struct ati_remote2 *ar2)
|
||||
{
|
||||
int r, i, channel;
|
||||
|
||||
/*
|
||||
* Configure receiver to only accept input from remote "channel"
|
||||
* channel == 0 -> Accept input from any remote channel
|
||||
* channel == 1 -> Only accept input from remote channel 1
|
||||
* channel == 2 -> Only accept input from remote channel 2
|
||||
* ...
|
||||
* channel == 16 -> Only accept input from remote channel 16
|
||||
*/
|
||||
|
||||
channel = 0;
|
||||
for (i = 0; i < 16; i++) {
|
||||
if ((1 << i) & channel_mask) {
|
||||
if (!(~(1 << i) & 0xFFFF & channel_mask))
|
||||
channel = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
r = usb_control_msg(ar2->udev, usb_sndctrlpipe(ar2->udev, 0),
|
||||
0x20,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
|
||||
channel, 0x0, NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
if (r) {
|
||||
dev_err(&ar2->udev->dev, "%s - failed to set channel due to error: %d\n",
|
||||
__FUNCTION__, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
r = ati_remote2_setup(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);
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,226 +0,0 @@
|
|||
#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 <[email protected]>"
|
||||
#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 = input_get_drvdata(dev);
|
||||
|
||||
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 = input_get_drvdata(dev);
|
||||
|
||||
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;
|
||||
int error = -ENOMEM;
|
||||
|
||||
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->dev.parent = &intf->dev;
|
||||
|
||||
input_set_drvdata(input_dev, 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;
|
||||
|
||||
error = input_register_device(kbtab->dev);
|
||||
if (error)
|
||||
goto fail3;
|
||||
|
||||
usb_set_intfdata(intf, kbtab);
|
||||
|
||||
return 0;
|
||||
|
||||
fail3: usb_free_urb(kbtab->irq);
|
||||
fail2: usb_buffer_free(dev, 10, kbtab->data, kbtab->data_dma);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(kbtab);
|
||||
return error;
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
@ -1,592 +0,0 @@
|
|||
/*
|
||||
* 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 <[email protected]>"
|
||||
#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 = input_get_drvdata(dev);
|
||||
|
||||
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 = input_get_drvdata(dev);
|
||||
|
||||
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, error;
|
||||
|
||||
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) {
|
||||
error = -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) {
|
||||
error = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!remote->irq_urb) {
|
||||
error = -ENOMEM;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
error = keyspan_setup(udev);
|
||||
if (error) {
|
||||
error = -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->dev.parent = &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_set_drvdata(input_dev, 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 */
|
||||
error = input_register_device(remote->input);
|
||||
if (error)
|
||||
goto fail3;
|
||||
|
||||
/* 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 error;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
|
@ -1,189 +0,0 @@
|
|||
/*
|
||||
* 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 */
|
||||
|
||||
|
|
@ -1,465 +0,0 @@
|
|||
/*
|
||||
* 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 = input_get_drvdata(dev);
|
||||
|
||||
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 error = -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->dev.parent = &intf->dev;
|
||||
|
||||
input_set_drvdata(input_dev, 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)) {
|
||||
error = -EIO;
|
||||
goto fail4;
|
||||
}
|
||||
|
||||
error = input_register_device(pm->input);
|
||||
if (error)
|
||||
goto fail5;
|
||||
|
||||
|
||||
/* 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;
|
||||
|
||||
fail5: usb_kill_urb(pm->irq);
|
||||
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 error;
|
||||
}
|
||||
|
||||
/* 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");
|
||||
|
|
@ -1,840 +0,0 @@
|
|||
/******************************************************************************
|
||||
* 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 <[email protected]>"
|
||||
#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_get_drvdata(input);
|
||||
|
||||
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_get_drvdata(input);
|
||||
|
||||
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->dev.parent = &intf->dev;
|
||||
|
||||
input_set_drvdata(input_dev, 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");
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
/*
|
||||
* 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 <[email protected]>"
|
||||
#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
|
||||
|
|
@ -1,318 +0,0 @@
|
|||
/*
|
||||
* 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 = input_get_drvdata(dev);
|
||||
|
||||
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 = input_get_drvdata(dev);
|
||||
|
||||
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;
|
||||
int error = -ENOMEM;
|
||||
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->dev.parent = &intf->dev;
|
||||
|
||||
input_set_drvdata(input_dev, 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;
|
||||
|
||||
error = input_register_device(wacom->dev);
|
||||
if (error)
|
||||
goto fail3;
|
||||
|
||||
/* 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;
|
||||
|
||||
fail3: usb_free_urb(wacom->irq);
|
||||
fail2: usb_buffer_free(dev, 10, wacom_wac->data, wacom->data_dma);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(wacom);
|
||||
kfree(wacom_wac);
|
||||
return error;
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
@ -1,675 +0,0 @@
|
|||
/*
|
||||
* 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);
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
|
|
@ -1,433 +0,0 @@
|
|||
/*
|
||||
* 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/usb/input.h>
|
||||
|
||||
#define DRIVER_VERSION "v0.0.6"
|
||||
#define DRIVER_AUTHOR "Marko Friedemann <[email protected]>"
|
||||
#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 = input_get_drvdata(dev);
|
||||
|
||||
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 = input_get_drvdata(dev);
|
||||
|
||||
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;
|
||||
int error = -ENOMEM;
|
||||
|
||||
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->dev.parent = &intf->dev;
|
||||
|
||||
input_set_drvdata(input_dev, 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;
|
||||
|
||||
error = input_register_device(xpad->dev);
|
||||
if (error)
|
||||
goto fail3;
|
||||
|
||||
usb_set_intfdata(intf, xpad);
|
||||
return 0;
|
||||
|
||||
fail3: usb_free_urb(xpad->irq_in);
|
||||
fail2: usb_buffer_free(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(xpad);
|
||||
return error;
|
||||
|
||||
}
|
||||
|
||||
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");
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,220 +0,0 @@
|
|||
/*
|
||||
* 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 */
|
||||
Loading…
Reference in a new issue