Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
370
drivers/usb/input/Kconfig
Normal file
370
drivers/usb/input/Kconfig
Normal file
@@ -0,0 +1,370 @@
|
||||
#
|
||||
# USB Input driver configuration
|
||||
#
|
||||
comment "USB Input Devices"
|
||||
depends on USB
|
||||
|
||||
config USB_HID
|
||||
tristate "USB Human Interface Device (full HID) support"
|
||||
default y
|
||||
depends on USB && INPUT
|
||||
select HID
|
||||
---help---
|
||||
Say Y here if you want full HID support to connect USB keyboards,
|
||||
mice, joysticks, graphic tablets, or any other HID based devices
|
||||
to your computer via USB, as well as Uninterruptible Power Supply
|
||||
(UPS) and monitor control devices.
|
||||
|
||||
You can't use this driver and the HIDBP (Boot Protocol) keyboard
|
||||
and mouse drivers at the same time. More information is available:
|
||||
<file:Documentation/input/input.txt>.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called usbhid.
|
||||
|
||||
comment "Input core support is needed for USB HID input layer or HIDBP support"
|
||||
depends on USB_HID && INPUT=n
|
||||
|
||||
config USB_HIDINPUT_POWERBOOK
|
||||
bool "Enable support for iBook/PowerBook special keys"
|
||||
default n
|
||||
depends on USB_HID
|
||||
help
|
||||
Say Y here if you want support for the special keys (Fn, Numlock) on
|
||||
Apple iBooks and PowerBooks.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config HID_FF
|
||||
bool "Force feedback support (EXPERIMENTAL)"
|
||||
depends on USB_HID && EXPERIMENTAL
|
||||
help
|
||||
Say Y here is you want force feedback support for a few HID devices.
|
||||
See below for a list of supported devices.
|
||||
|
||||
See <file:Documentation/input/ff.txt> for a description of the force
|
||||
feedback API.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config HID_PID
|
||||
bool "PID device support"
|
||||
depends on HID_FF
|
||||
help
|
||||
Say Y here if you have a PID-compliant device and wish to enable force
|
||||
feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such
|
||||
devices.
|
||||
|
||||
config LOGITECH_FF
|
||||
bool "Logitech devices support"
|
||||
depends on HID_FF
|
||||
select INPUT_FF_MEMLESS if USB_HID
|
||||
help
|
||||
Say Y here if you have one of these devices:
|
||||
- Logitech WingMan Cordless RumblePad
|
||||
- Logitech WingMan Cordless RumblePad 2
|
||||
- Logitech WingMan Force 3D
|
||||
- Logitech Formula Force EX
|
||||
- Logitech MOMO Force wheel
|
||||
|
||||
and if you want to enable force feedback for them.
|
||||
Note: if you say N here, this device will still be supported, but without
|
||||
force feedback.
|
||||
|
||||
config PANTHERLORD_FF
|
||||
bool "PantherLord USB/PS2 2in1 Adapter support"
|
||||
depends on HID_FF
|
||||
select INPUT_FF_MEMLESS if USB_HID
|
||||
help
|
||||
Say Y here if you have a PantherLord USB/PS2 2in1 Adapter and want
|
||||
to enable force feedback support for it.
|
||||
|
||||
config THRUSTMASTER_FF
|
||||
bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)"
|
||||
depends on HID_FF && EXPERIMENTAL
|
||||
select INPUT_FF_MEMLESS if USB_HID
|
||||
help
|
||||
Say Y here if you have a THRUSTMASTER FireStore Dual Power 2,
|
||||
and want to enable force feedback support for it.
|
||||
Note: if you say N here, this device will still be supported, but without
|
||||
force feedback.
|
||||
|
||||
config ZEROPLUS_FF
|
||||
bool "Zeroplus based game controller support"
|
||||
depends on HID_FF
|
||||
select INPUT_FF_MEMLESS if USB_HID
|
||||
help
|
||||
Say Y here if you have a Zeroplus based game controller and want to
|
||||
enable force feedback for it.
|
||||
|
||||
config USB_HIDDEV
|
||||
bool "/dev/hiddev raw HID device support"
|
||||
depends on USB_HID
|
||||
help
|
||||
Say Y here if you want to support HID devices (from the USB
|
||||
specification standpoint) that aren't strictly user interface
|
||||
devices, like monitor controls and Uninterruptable Power Supplies.
|
||||
|
||||
This module supports these devices separately using a separate
|
||||
event interface on /dev/usb/hiddevX (char 180:96 to 180:111).
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
menu "USB HID Boot Protocol drivers"
|
||||
depends on USB!=n && USB_HID!=y
|
||||
|
||||
config USB_KBD
|
||||
tristate "USB HIDBP Keyboard (simple Boot) support"
|
||||
depends on USB && INPUT
|
||||
---help---
|
||||
Say Y here only if you are absolutely sure that you don't want
|
||||
to use the generic HID driver for your USB keyboard and prefer
|
||||
to use the keyboard in its limited Boot Protocol mode instead.
|
||||
|
||||
This is almost certainly not what you want. This is mostly
|
||||
useful for embedded applications or simple keyboards.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called usbkbd.
|
||||
|
||||
If even remotely unsure, say N.
|
||||
|
||||
config USB_MOUSE
|
||||
tristate "USB HIDBP Mouse (simple Boot) support"
|
||||
depends on USB && INPUT
|
||||
---help---
|
||||
Say Y here only if you are absolutely sure that you don't want
|
||||
to use the generic HID driver for your USB mouse and prefer
|
||||
to use the mouse in its limited Boot Protocol mode instead.
|
||||
|
||||
This is almost certainly not what you want. This is mostly
|
||||
useful for embedded applications or simple mice.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called usbmouse.
|
||||
|
||||
If even remotely unsure, say N.
|
||||
|
||||
endmenu
|
||||
|
||||
config USB_AIPTEK
|
||||
tristate "Aiptek 6000U/8000U tablet support"
|
||||
depends on USB && INPUT
|
||||
help
|
||||
Say Y here if you want to use the USB version of the Aiptek 6000U
|
||||
or Aiptek 8000U tablet. Make sure to say Y to "Mouse support"
|
||||
(CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
|
||||
(CONFIG_INPUT_EVDEV) as well.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called aiptek.
|
||||
|
||||
config USB_WACOM
|
||||
tristate "Wacom Intuos/Graphire tablet support"
|
||||
depends on USB && INPUT
|
||||
help
|
||||
Say Y here if you want to use the USB version of the Wacom Intuos
|
||||
or Graphire tablet. Make sure to say Y to "Mouse support"
|
||||
(CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
|
||||
(CONFIG_INPUT_EVDEV) as well.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called wacom.
|
||||
|
||||
config USB_ACECAD
|
||||
tristate "Acecad Flair tablet support"
|
||||
depends on USB && INPUT
|
||||
help
|
||||
Say Y here if you want to use the USB version of the Acecad Flair
|
||||
tablet. Make sure to say Y to "Mouse support"
|
||||
(CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
|
||||
(CONFIG_INPUT_EVDEV) as well.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called acecad.
|
||||
|
||||
config USB_KBTAB
|
||||
tristate "KB Gear JamStudio tablet support"
|
||||
depends on USB && INPUT
|
||||
help
|
||||
Say Y here if you want to use the USB version of the KB Gear
|
||||
JamStudio tablet. Make sure to say Y to "Mouse support"
|
||||
(CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
|
||||
(CONFIG_INPUT_EVDEV) as well.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called kbtab.
|
||||
|
||||
config USB_POWERMATE
|
||||
tristate "Griffin PowerMate and Contour Jog support"
|
||||
depends on USB && INPUT
|
||||
---help---
|
||||
Say Y here if you want to use Griffin PowerMate or Contour Jog devices.
|
||||
These are aluminum dials which can measure clockwise and anticlockwise
|
||||
rotation. The dial also acts as a pushbutton. The base contains an LED
|
||||
which can be instructed to pulse or to switch to a particular intensity.
|
||||
|
||||
You can download userspace tools from
|
||||
<http://sowerbutts.com/powermate/>.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called powermate.
|
||||
|
||||
config USB_TOUCHSCREEN
|
||||
tristate "USB Touchscreen Driver"
|
||||
depends on USB && INPUT
|
||||
---help---
|
||||
USB Touchscreen driver for:
|
||||
- eGalax Touchkit USB (also includes eTurboTouch CT-410/510/700)
|
||||
- PanJit TouchSet USB
|
||||
- 3M MicroTouch USB (EX II series)
|
||||
- ITM
|
||||
- some other eTurboTouch
|
||||
- Gunze AHL61
|
||||
- DMC TSC-10/25
|
||||
|
||||
Have a look at <http://linux.chapter7.ch/touchkit/> for
|
||||
a usage description and the required user-space stuff.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called usbtouchscreen.
|
||||
|
||||
config USB_TOUCHSCREEN_EGALAX
|
||||
default y
|
||||
bool "eGalax, eTurboTouch CT-410/510/700 device support" if EMBEDDED
|
||||
depends on USB_TOUCHSCREEN
|
||||
|
||||
config USB_TOUCHSCREEN_PANJIT
|
||||
default y
|
||||
bool "PanJit device support" if EMBEDDED
|
||||
depends on USB_TOUCHSCREEN
|
||||
|
||||
config USB_TOUCHSCREEN_3M
|
||||
default y
|
||||
bool "3M/Microtouch EX II series device support" if EMBEDDED
|
||||
depends on USB_TOUCHSCREEN
|
||||
|
||||
config USB_TOUCHSCREEN_ITM
|
||||
default y
|
||||
bool "ITM device support" if EMBEDDED
|
||||
depends on USB_TOUCHSCREEN
|
||||
|
||||
config USB_TOUCHSCREEN_ETURBO
|
||||
default y
|
||||
bool "eTurboTouch (non-eGalax compatible) device support" if EMBEDDED
|
||||
depends on USB_TOUCHSCREEN
|
||||
|
||||
config USB_TOUCHSCREEN_GUNZE
|
||||
default y
|
||||
bool "Gunze AHL61 device support" if EMBEDDED
|
||||
depends on USB_TOUCHSCREEN
|
||||
|
||||
config USB_TOUCHSCREEN_DMC_TSC10
|
||||
default y
|
||||
bool "DMC TSC-10/25 device support" if EMBEDDED
|
||||
depends on USB_TOUCHSCREEN
|
||||
|
||||
config USB_YEALINK
|
||||
tristate "Yealink usb-p1k voip phone"
|
||||
depends on USB && INPUT && EXPERIMENTAL
|
||||
---help---
|
||||
Say Y here if you want to enable keyboard and LCD functions of the
|
||||
Yealink usb-p1k usb phones. The audio part is enabled by the generic
|
||||
usb sound driver, so you might want to enable that as well.
|
||||
|
||||
For information about how to use these additional functions, see
|
||||
<file:Documentation/input/yealink.txt>.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called yealink.
|
||||
|
||||
config USB_XPAD
|
||||
tristate "X-Box gamepad support"
|
||||
depends on USB && INPUT
|
||||
---help---
|
||||
Say Y here if you want to use the X-Box pad with your computer.
|
||||
Make sure to say Y to "Joystick support" (CONFIG_INPUT_JOYDEV)
|
||||
and/or "Event interface support" (CONFIG_INPUT_EVDEV) as well.
|
||||
|
||||
For information about how to connect the X-Box pad to USB, see
|
||||
<file:Documentation/input/xpad.txt>.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called xpad.
|
||||
|
||||
config USB_ATI_REMOTE
|
||||
tristate "ATI / X10 USB RF remote control"
|
||||
depends on USB && INPUT
|
||||
---help---
|
||||
Say Y here if you want to use an ATI or X10 "Lola" USB remote control.
|
||||
These are RF remotes with USB receivers.
|
||||
The ATI remote comes with many of ATI's All-In-Wonder video cards.
|
||||
The X10 "Lola" remote is available at:
|
||||
<http://www.x10.com/products/lola_sg1.htm>
|
||||
This driver provides mouse pointer, left and right mouse buttons,
|
||||
and maps all the other remote buttons to keypress events.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ati_remote.
|
||||
|
||||
config USB_ATI_REMOTE2
|
||||
tristate "ATI / Philips USB RF remote control"
|
||||
depends on USB && INPUT
|
||||
---help---
|
||||
Say Y here if you want to use an ATI or Philips USB RF remote control.
|
||||
These are RF remotes with USB receivers.
|
||||
ATI Remote Wonder II comes with some ATI's All-In-Wonder video cards
|
||||
and is also available as a separate product.
|
||||
This driver provides mouse pointer, left and right mouse buttons,
|
||||
and maps all the other remote buttons to keypress events.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ati_remote2.
|
||||
|
||||
config USB_KEYSPAN_REMOTE
|
||||
tristate "Keyspan DMR USB remote control (EXPERIMENTAL)"
|
||||
depends on USB && INPUT && EXPERIMENTAL
|
||||
---help---
|
||||
Say Y here if you want to use a Keyspan DMR USB remote control.
|
||||
Currently only the UIA-11 type of receiver has been tested. The tag
|
||||
on the receiver that connects to the USB port should have a P/N that
|
||||
will tell you what type of DMR you have. The UIA-10 type is not
|
||||
supported at this time. This driver maps all buttons to keypress
|
||||
events.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called keyspan_remote.
|
||||
|
||||
config USB_APPLETOUCH
|
||||
tristate "Apple USB Touchpad support"
|
||||
depends on USB && INPUT
|
||||
---help---
|
||||
Say Y here if you want to use an Apple USB Touchpad.
|
||||
|
||||
These are the touchpads that can be found on post-February 2005
|
||||
Apple Powerbooks (prior models have a Synaptics touchpad connected
|
||||
to the ADB bus).
|
||||
|
||||
This driver provides a basic mouse driver but can be interfaced
|
||||
with the synaptics X11 driver to provide acceleration and
|
||||
scrolling in X11.
|
||||
|
||||
For further information, see
|
||||
<file:Documentation/input/appletouch.txt>.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called appletouch.
|
||||
|
||||
config USB_GTCO
|
||||
tristate "GTCO CalComp/InterWrite USB Support"
|
||||
depends on USB && INPUT
|
||||
---help---
|
||||
Say Y here if you want to use the USB version of the GTCO
|
||||
CalComp/InterWrite Tablet. Make sure to say Y to "Mouse support"
|
||||
(CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
|
||||
(CONFIG_INPUT_EVDEV) as well.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called gtco.
|
||||
55
drivers/usb/input/Makefile
Normal file
55
drivers/usb/input/Makefile
Normal file
@@ -0,0 +1,55 @@
|
||||
#
|
||||
# Makefile for the USB input drivers
|
||||
#
|
||||
|
||||
# Multipart objects.
|
||||
wacom-objs := wacom_wac.o wacom_sys.o
|
||||
usbhid-objs := hid-core.o
|
||||
|
||||
# Optional parts of multipart objects.
|
||||
|
||||
ifeq ($(CONFIG_USB_HIDDEV),y)
|
||||
usbhid-objs += hiddev.o
|
||||
endif
|
||||
ifeq ($(CONFIG_HID_PID),y)
|
||||
usbhid-objs += hid-pidff.o
|
||||
endif
|
||||
ifeq ($(CONFIG_LOGITECH_FF),y)
|
||||
usbhid-objs += hid-lgff.o
|
||||
endif
|
||||
ifeq ($(CONFIG_PANTHERLORD_FF),y)
|
||||
usbhid-objs += hid-plff.o
|
||||
endif
|
||||
ifeq ($(CONFIG_THRUSTMASTER_FF),y)
|
||||
usbhid-objs += hid-tmff.o
|
||||
endif
|
||||
ifeq ($(CONFIG_ZEROPLUS_FF),y)
|
||||
usbhid-objs += hid-zpff.o
|
||||
endif
|
||||
ifeq ($(CONFIG_HID_FF),y)
|
||||
usbhid-objs += hid-ff.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_USB_AIPTEK) += aiptek.o
|
||||
obj-$(CONFIG_USB_ATI_REMOTE) += ati_remote.o
|
||||
obj-$(CONFIG_USB_ATI_REMOTE2) += ati_remote2.o
|
||||
obj-$(CONFIG_USB_HID) += usbhid.o
|
||||
obj-$(CONFIG_USB_KBD) += usbkbd.o
|
||||
obj-$(CONFIG_USB_KBTAB) += kbtab.o
|
||||
obj-$(CONFIG_USB_KEYSPAN_REMOTE) += keyspan_remote.o
|
||||
obj-$(CONFIG_USB_MOUSE) += usbmouse.o
|
||||
obj-$(CONFIG_USB_MTOUCH) += mtouchusb.o
|
||||
obj-$(CONFIG_USB_ITMTOUCH) += itmtouch.o
|
||||
obj-$(CONFIG_USB_EGALAX) += touchkitusb.o
|
||||
obj-$(CONFIG_USB_TOUCHSCREEN) += usbtouchscreen.o
|
||||
obj-$(CONFIG_USB_POWERMATE) += powermate.o
|
||||
obj-$(CONFIG_USB_WACOM) += wacom.o
|
||||
obj-$(CONFIG_USB_ACECAD) += acecad.o
|
||||
obj-$(CONFIG_USB_YEALINK) += yealink.o
|
||||
obj-$(CONFIG_USB_XPAD) += xpad.o
|
||||
obj-$(CONFIG_USB_APPLETOUCH) += appletouch.o
|
||||
obj-$(CONFIG_USB_GTCO) += gtco.o
|
||||
|
||||
ifeq ($(CONFIG_USB_DEBUG),y)
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
endif
|
||||
279
drivers/usb/input/acecad.c
Normal file
279
drivers/usb/input/acecad.c
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright (c) 2001-2005 Edouard TISSERANT <edouard.tisserant@wanadoo.fr>
|
||||
* Copyright (c) 2004-2005 Stephane VOLTZ <svoltz@numericable.fr>
|
||||
*
|
||||
* USB Acecad "Acecad Flair" tablet support
|
||||
*
|
||||
* Changelog:
|
||||
* v3.2 - Added sysfs support
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
*/
|
||||
#define DRIVER_VERSION "v3.2"
|
||||
#define DRIVER_DESC "USB Acecad Flair tablet driver"
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
#define DRIVER_AUTHOR "Edouard TISSERANT <edouard.tisserant@wanadoo.fr>"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE(DRIVER_LICENSE);
|
||||
|
||||
#define USB_VENDOR_ID_ACECAD 0x0460
|
||||
#define USB_DEVICE_ID_FLAIR 0x0004
|
||||
#define USB_DEVICE_ID_302 0x0008
|
||||
|
||||
struct usb_acecad {
|
||||
char name[128];
|
||||
char phys[64];
|
||||
struct usb_device *usbdev;
|
||||
struct input_dev *input;
|
||||
struct urb *irq;
|
||||
|
||||
signed char *data;
|
||||
dma_addr_t data_dma;
|
||||
};
|
||||
|
||||
static void usb_acecad_irq(struct urb *urb)
|
||||
{
|
||||
struct usb_acecad *acecad = urb->context;
|
||||
unsigned char *data = acecad->data;
|
||||
struct input_dev *dev = acecad->input;
|
||||
int prox, status;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
|
||||
goto resubmit;
|
||||
}
|
||||
|
||||
prox = (data[0] & 0x04) >> 2;
|
||||
input_report_key(dev, BTN_TOOL_PEN, prox);
|
||||
|
||||
if (prox) {
|
||||
int x = data[1] | (data[2] << 8);
|
||||
int y = data[3] | (data[4] << 8);
|
||||
/* Pressure should compute the same way for flair and 302 */
|
||||
int pressure = data[5] | (data[6] << 8);
|
||||
int touch = data[0] & 0x01;
|
||||
int stylus = (data[0] & 0x10) >> 4;
|
||||
int stylus2 = (data[0] & 0x20) >> 5;
|
||||
input_report_abs(dev, ABS_X, x);
|
||||
input_report_abs(dev, ABS_Y, y);
|
||||
input_report_abs(dev, ABS_PRESSURE, pressure);
|
||||
input_report_key(dev, BTN_TOUCH, touch);
|
||||
input_report_key(dev, BTN_STYLUS, stylus);
|
||||
input_report_key(dev, BTN_STYLUS2, stylus2);
|
||||
}
|
||||
|
||||
/* event termination */
|
||||
input_sync(dev);
|
||||
|
||||
resubmit:
|
||||
status = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (status)
|
||||
err("can't resubmit intr, %s-%s/input0, status %d",
|
||||
acecad->usbdev->bus->bus_name, acecad->usbdev->devpath, status);
|
||||
}
|
||||
|
||||
static int usb_acecad_open(struct input_dev *dev)
|
||||
{
|
||||
struct usb_acecad *acecad = dev->private;
|
||||
|
||||
acecad->irq->dev = acecad->usbdev;
|
||||
if (usb_submit_urb(acecad->irq, GFP_KERNEL))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_acecad_close(struct input_dev *dev)
|
||||
{
|
||||
struct usb_acecad *acecad = dev->private;
|
||||
|
||||
usb_kill_urb(acecad->irq);
|
||||
}
|
||||
|
||||
static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
struct usb_host_interface *interface = intf->cur_altsetting;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct usb_acecad *acecad;
|
||||
struct input_dev *input_dev;
|
||||
int pipe, maxp;
|
||||
|
||||
if (interface->desc.bNumEndpoints != 1)
|
||||
return -ENODEV;
|
||||
|
||||
endpoint = &interface->endpoint[0].desc;
|
||||
|
||||
if (!usb_endpoint_is_int_in(endpoint))
|
||||
return -ENODEV;
|
||||
|
||||
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
|
||||
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
|
||||
|
||||
acecad = kzalloc(sizeof(struct usb_acecad), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!acecad || !input_dev)
|
||||
goto fail1;
|
||||
|
||||
acecad->data = usb_buffer_alloc(dev, 8, GFP_KERNEL, &acecad->data_dma);
|
||||
if (!acecad->data)
|
||||
goto fail1;
|
||||
|
||||
acecad->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!acecad->irq)
|
||||
goto fail2;
|
||||
|
||||
acecad->usbdev = dev;
|
||||
acecad->input = input_dev;
|
||||
|
||||
if (dev->manufacturer)
|
||||
strlcpy(acecad->name, dev->manufacturer, sizeof(acecad->name));
|
||||
|
||||
if (dev->product) {
|
||||
if (dev->manufacturer)
|
||||
strlcat(acecad->name, " ", sizeof(acecad->name));
|
||||
strlcat(acecad->name, dev->product, sizeof(acecad->name));
|
||||
}
|
||||
|
||||
usb_make_path(dev, acecad->phys, sizeof(acecad->phys));
|
||||
strlcat(acecad->phys, "/input0", sizeof(acecad->phys));
|
||||
|
||||
input_dev->name = acecad->name;
|
||||
input_dev->phys = acecad->phys;
|
||||
usb_to_input_id(dev, &input_dev->id);
|
||||
input_dev->cdev.dev = &intf->dev;
|
||||
input_dev->private = acecad;
|
||||
|
||||
input_dev->open = usb_acecad_open;
|
||||
input_dev->close = usb_acecad_close;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||
input_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
|
||||
input_dev->keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
|
||||
input_dev->keybit[LONG(BTN_DIGI)] = BIT(BTN_TOOL_PEN) |BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2);
|
||||
|
||||
switch (id->driver_info) {
|
||||
case 0:
|
||||
input_dev->absmax[ABS_X] = 5000;
|
||||
input_dev->absmax[ABS_Y] = 3750;
|
||||
input_dev->absmax[ABS_PRESSURE] = 512;
|
||||
if (!strlen(acecad->name))
|
||||
snprintf(acecad->name, sizeof(acecad->name),
|
||||
"USB Acecad Flair Tablet %04x:%04x",
|
||||
le16_to_cpu(dev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->descriptor.idProduct));
|
||||
break;
|
||||
case 1:
|
||||
input_dev->absmax[ABS_X] = 3000;
|
||||
input_dev->absmax[ABS_Y] = 2250;
|
||||
input_dev->absmax[ABS_PRESSURE] = 1024;
|
||||
if (!strlen(acecad->name))
|
||||
snprintf(acecad->name, sizeof(acecad->name),
|
||||
"USB Acecad 302 Tablet %04x:%04x",
|
||||
le16_to_cpu(dev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->descriptor.idProduct));
|
||||
break;
|
||||
}
|
||||
|
||||
input_dev->absfuzz[ABS_X] = 4;
|
||||
input_dev->absfuzz[ABS_Y] = 4;
|
||||
|
||||
usb_fill_int_urb(acecad->irq, dev, pipe,
|
||||
acecad->data, maxp > 8 ? 8 : maxp,
|
||||
usb_acecad_irq, acecad, endpoint->bInterval);
|
||||
acecad->irq->transfer_dma = acecad->data_dma;
|
||||
acecad->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
input_register_device(acecad->input);
|
||||
|
||||
usb_set_intfdata(intf, acecad);
|
||||
|
||||
return 0;
|
||||
|
||||
fail2: usb_buffer_free(dev, 8, acecad->data, acecad->data_dma);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(acecad);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void usb_acecad_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_acecad *acecad = usb_get_intfdata(intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (acecad) {
|
||||
usb_kill_urb(acecad->irq);
|
||||
input_unregister_device(acecad->input);
|
||||
usb_free_urb(acecad->irq);
|
||||
usb_buffer_free(interface_to_usbdev(intf), 10, acecad->data, acecad->data_dma);
|
||||
kfree(acecad);
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_device_id usb_acecad_id_table [] = {
|
||||
{ USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_FLAIR), .driver_info = 0 },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_302), .driver_info = 1 },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, usb_acecad_id_table);
|
||||
|
||||
static struct usb_driver usb_acecad_driver = {
|
||||
.name = "usb_acecad",
|
||||
.probe = usb_acecad_probe,
|
||||
.disconnect = usb_acecad_disconnect,
|
||||
.id_table = usb_acecad_id_table,
|
||||
};
|
||||
|
||||
static int __init usb_acecad_init(void)
|
||||
{
|
||||
int result = usb_register(&usb_acecad_driver);
|
||||
if (result == 0)
|
||||
info(DRIVER_VERSION ":" DRIVER_DESC);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void __exit usb_acecad_exit(void)
|
||||
{
|
||||
usb_deregister(&usb_acecad_driver);
|
||||
}
|
||||
|
||||
module_init(usb_acecad_init);
|
||||
module_exit(usb_acecad_exit);
|
||||
2234
drivers/usb/input/aiptek.c
Normal file
2234
drivers/usb/input/aiptek.c
Normal file
File diff suppressed because it is too large
Load Diff
705
drivers/usb/input/appletouch.c
Normal file
705
drivers/usb/input/appletouch.c
Normal file
@@ -0,0 +1,705 @@
|
||||
/*
|
||||
* Apple USB Touchpad (for post-February 2005 PowerBooks and MacBooks) driver
|
||||
*
|
||||
* Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net)
|
||||
* Copyright (C) 2005 Stelian Pop (stelian@popies.net)
|
||||
* Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de)
|
||||
* Copyright (C) 2005 Peter Osterlund (petero2@telia.com)
|
||||
* Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch)
|
||||
* Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch)
|
||||
*
|
||||
* Thanks to Alex Harper <basilisk@foobox.net> for his inputs.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
/* Apple has powerbooks which have the keyboard with different Product IDs */
|
||||
#define APPLE_VENDOR_ID 0x05AC
|
||||
|
||||
/* These names come from Info.plist in AppleUSBTrackpad.kext */
|
||||
#define FOUNTAIN_ANSI_PRODUCT_ID 0x020E
|
||||
#define FOUNTAIN_ISO_PRODUCT_ID 0x020F
|
||||
|
||||
#define FOUNTAIN_TP_ONLY_PRODUCT_ID 0x030A
|
||||
|
||||
#define GEYSER1_TP_ONLY_PRODUCT_ID 0x030B
|
||||
|
||||
#define GEYSER_ANSI_PRODUCT_ID 0x0214
|
||||
#define GEYSER_ISO_PRODUCT_ID 0x0215
|
||||
#define GEYSER_JIS_PRODUCT_ID 0x0216
|
||||
|
||||
/* MacBook devices */
|
||||
#define GEYSER3_ANSI_PRODUCT_ID 0x0217
|
||||
#define GEYSER3_ISO_PRODUCT_ID 0x0218
|
||||
#define GEYSER3_JIS_PRODUCT_ID 0x0219
|
||||
|
||||
/*
|
||||
* Geyser IV: same as Geyser III according to Info.plist in AppleUSBTrackpad.kext
|
||||
* -> same IOClass (AppleUSBGrIIITrackpad), same acceleration tables
|
||||
*/
|
||||
#define GEYSER4_ANSI_PRODUCT_ID 0x021A
|
||||
#define GEYSER4_ISO_PRODUCT_ID 0x021B
|
||||
#define GEYSER4_JIS_PRODUCT_ID 0x021C
|
||||
|
||||
#define ATP_DEVICE(prod) \
|
||||
.match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
|
||||
USB_DEVICE_ID_MATCH_INT_CLASS | \
|
||||
USB_DEVICE_ID_MATCH_INT_PROTOCOL, \
|
||||
.idVendor = APPLE_VENDOR_ID, \
|
||||
.idProduct = (prod), \
|
||||
.bInterfaceClass = 0x03, \
|
||||
.bInterfaceProtocol = 0x02
|
||||
|
||||
/* table of devices that work with this driver */
|
||||
static struct usb_device_id atp_table [] = {
|
||||
{ ATP_DEVICE(FOUNTAIN_ANSI_PRODUCT_ID) },
|
||||
{ ATP_DEVICE(FOUNTAIN_ISO_PRODUCT_ID) },
|
||||
{ ATP_DEVICE(FOUNTAIN_TP_ONLY_PRODUCT_ID) },
|
||||
{ ATP_DEVICE(GEYSER1_TP_ONLY_PRODUCT_ID) },
|
||||
|
||||
/* PowerBooks Oct 2005 */
|
||||
{ ATP_DEVICE(GEYSER_ANSI_PRODUCT_ID) },
|
||||
{ ATP_DEVICE(GEYSER_ISO_PRODUCT_ID) },
|
||||
{ ATP_DEVICE(GEYSER_JIS_PRODUCT_ID) },
|
||||
|
||||
/* Core Duo MacBook & MacBook Pro */
|
||||
{ ATP_DEVICE(GEYSER3_ANSI_PRODUCT_ID) },
|
||||
{ ATP_DEVICE(GEYSER3_ISO_PRODUCT_ID) },
|
||||
{ ATP_DEVICE(GEYSER3_JIS_PRODUCT_ID) },
|
||||
|
||||
/* Core2 Duo MacBook & MacBook Pro */
|
||||
{ ATP_DEVICE(GEYSER4_ANSI_PRODUCT_ID) },
|
||||
{ ATP_DEVICE(GEYSER4_ISO_PRODUCT_ID) },
|
||||
{ ATP_DEVICE(GEYSER4_JIS_PRODUCT_ID) },
|
||||
|
||||
/* Terminating entry */
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE (usb, atp_table);
|
||||
|
||||
/*
|
||||
* number of sensors. Note that only 16 instead of 26 X (horizontal)
|
||||
* sensors exist on 12" and 15" PowerBooks. All models have 16 Y
|
||||
* (vertical) sensors.
|
||||
*/
|
||||
#define ATP_XSENSORS 26
|
||||
#define ATP_YSENSORS 16
|
||||
|
||||
/* amount of fuzz this touchpad generates */
|
||||
#define ATP_FUZZ 16
|
||||
|
||||
/* maximum pressure this driver will report */
|
||||
#define ATP_PRESSURE 300
|
||||
/*
|
||||
* multiplication factor for the X and Y coordinates.
|
||||
* We try to keep the touchpad aspect ratio while still doing only simple
|
||||
* arithmetics.
|
||||
* The factors below give coordinates like:
|
||||
* 0 <= x < 960 on 12" and 15" Powerbooks
|
||||
* 0 <= x < 1600 on 17" Powerbooks
|
||||
* 0 <= y < 646
|
||||
*/
|
||||
#define ATP_XFACT 64
|
||||
#define ATP_YFACT 43
|
||||
|
||||
/*
|
||||
* Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is
|
||||
* ignored.
|
||||
*/
|
||||
#define ATP_THRESHOLD 5
|
||||
|
||||
/* MacBook Pro (Geyser 3 & 4) initialization constants */
|
||||
#define ATP_GEYSER3_MODE_READ_REQUEST_ID 1
|
||||
#define ATP_GEYSER3_MODE_WRITE_REQUEST_ID 9
|
||||
#define ATP_GEYSER3_MODE_REQUEST_VALUE 0x300
|
||||
#define ATP_GEYSER3_MODE_REQUEST_INDEX 0
|
||||
#define ATP_GEYSER3_MODE_VENDOR_VALUE 0x04
|
||||
|
||||
/* Structure to hold all of our device specific stuff */
|
||||
struct atp {
|
||||
char phys[64];
|
||||
struct usb_device * udev; /* usb device */
|
||||
struct urb * urb; /* usb request block */
|
||||
signed char * data; /* transferred data */
|
||||
int open; /* non-zero if opened */
|
||||
struct input_dev *input; /* input dev */
|
||||
int valid; /* are the sensors valid ? */
|
||||
int x_old; /* last reported x/y, */
|
||||
int y_old; /* used for smoothing */
|
||||
/* current value of the sensors */
|
||||
signed char xy_cur[ATP_XSENSORS + ATP_YSENSORS];
|
||||
/* last value of the sensors */
|
||||
signed char xy_old[ATP_XSENSORS + ATP_YSENSORS];
|
||||
/* accumulated sensors */
|
||||
int xy_acc[ATP_XSENSORS + ATP_YSENSORS];
|
||||
int overflowwarn; /* overflow warning printed? */
|
||||
int datalen; /* size of an USB urb transfer */
|
||||
};
|
||||
|
||||
#define dbg_dump(msg, tab) \
|
||||
if (debug > 1) { \
|
||||
int i; \
|
||||
printk("appletouch: %s %lld", msg, (long long)jiffies); \
|
||||
for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) \
|
||||
printk(" %02x", tab[i]); \
|
||||
printk("\n"); \
|
||||
}
|
||||
|
||||
#define dprintk(format, a...) \
|
||||
do { \
|
||||
if (debug) printk(format, ##a); \
|
||||
} while (0)
|
||||
|
||||
MODULE_AUTHOR("Johannes Berg, Stelian Pop, Frank Arnold, Michael Hanselmann");
|
||||
MODULE_DESCRIPTION("Apple PowerBooks USB touchpad driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* Make the threshold a module parameter
|
||||
*/
|
||||
static int threshold = ATP_THRESHOLD;
|
||||
module_param(threshold, int, 0644);
|
||||
MODULE_PARM_DESC(threshold, "Discards any change in data from a sensor (trackpad has hundreds of these sensors) less than this value");
|
||||
|
||||
static int debug = 1;
|
||||
module_param(debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "Activate debugging output");
|
||||
|
||||
/* Checks if the device a Geyser 2 (ANSI, ISO, JIS) */
|
||||
static inline int atp_is_geyser_2(struct atp *dev)
|
||||
{
|
||||
u16 productId = le16_to_cpu(dev->udev->descriptor.idProduct);
|
||||
|
||||
return (productId == GEYSER_ANSI_PRODUCT_ID) ||
|
||||
(productId == GEYSER_ISO_PRODUCT_ID) ||
|
||||
(productId == GEYSER_JIS_PRODUCT_ID);
|
||||
}
|
||||
|
||||
static inline int atp_is_geyser_3(struct atp *dev)
|
||||
{
|
||||
u16 productId = le16_to_cpu(dev->udev->descriptor.idProduct);
|
||||
|
||||
return (productId == GEYSER3_ANSI_PRODUCT_ID) ||
|
||||
(productId == GEYSER3_ISO_PRODUCT_ID) ||
|
||||
(productId == GEYSER3_JIS_PRODUCT_ID) ||
|
||||
(productId == GEYSER4_ANSI_PRODUCT_ID) ||
|
||||
(productId == GEYSER4_ISO_PRODUCT_ID) ||
|
||||
(productId == GEYSER4_JIS_PRODUCT_ID);
|
||||
}
|
||||
|
||||
static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
|
||||
int *z, int *fingers)
|
||||
{
|
||||
int i;
|
||||
/* values to calculate mean */
|
||||
int pcum = 0, psum = 0;
|
||||
int is_increasing = 0;
|
||||
|
||||
*fingers = 0;
|
||||
|
||||
for (i = 0; i < nb_sensors; i++) {
|
||||
if (xy_sensors[i] < threshold) {
|
||||
if (is_increasing)
|
||||
is_increasing = 0;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Makes the finger detection more versatile. For example,
|
||||
* two fingers with no gap will be detected. Also, my
|
||||
* tests show it less likely to have intermittent loss
|
||||
* of multiple finger readings while moving around (scrolling).
|
||||
*
|
||||
* Changes the multiple finger detection to counting humps on
|
||||
* sensors (transitions from nonincreasing to increasing)
|
||||
* instead of counting transitions from low sensors (no
|
||||
* finger reading) to high sensors (finger above
|
||||
* sensor)
|
||||
*
|
||||
* - Jason Parekh <jasonparekh@gmail.com>
|
||||
*/
|
||||
if (i < 1 || (!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) {
|
||||
(*fingers)++;
|
||||
is_increasing = 1;
|
||||
} else if (i > 0 && xy_sensors[i - 1] >= xy_sensors[i]) {
|
||||
is_increasing = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Subtracts threshold so a high sensor that just passes the threshold
|
||||
* won't skew the calculated absolute coordinate. Fixes an issue
|
||||
* where slowly moving the mouse would occassionaly jump a number of
|
||||
* pixels (let me restate--slowly moving the mouse makes this issue
|
||||
* most apparent).
|
||||
*/
|
||||
pcum += (xy_sensors[i] - threshold) * i;
|
||||
psum += (xy_sensors[i] - threshold);
|
||||
}
|
||||
|
||||
if (psum > 0) {
|
||||
*z = psum;
|
||||
return pcum * fact / psum;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void atp_report_fingers(struct input_dev *input, int fingers)
|
||||
{
|
||||
input_report_key(input, BTN_TOOL_FINGER, fingers == 1);
|
||||
input_report_key(input, BTN_TOOL_DOUBLETAP, fingers == 2);
|
||||
input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2);
|
||||
}
|
||||
|
||||
static void atp_complete(struct urb* urb)
|
||||
{
|
||||
int x, y, x_z, y_z, x_f, y_f;
|
||||
int retval, i, j;
|
||||
struct atp *dev = urb->context;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -EOVERFLOW:
|
||||
if(!dev->overflowwarn) {
|
||||
printk("appletouch: OVERFLOW with data "
|
||||
"length %d, actual length is %d\n",
|
||||
dev->datalen, dev->urb->actual_length);
|
||||
dev->overflowwarn = 1;
|
||||
}
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* This urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d",
|
||||
__FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d",
|
||||
__FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* drop incomplete datasets */
|
||||
if (dev->urb->actual_length != dev->datalen) {
|
||||
dprintk("appletouch: incomplete data package"
|
||||
" (first byte: %d, length: %d).\n",
|
||||
dev->data[0], dev->urb->actual_length);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* reorder the sensors values */
|
||||
if (atp_is_geyser_3(dev)) {
|
||||
memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
|
||||
|
||||
/*
|
||||
* The values are laid out like this:
|
||||
* -, Y1, Y2, -, Y3, Y4, -, ..., -, X1, X2, -, X3, X4, ...
|
||||
* '-' is an unused value.
|
||||
*/
|
||||
|
||||
/* read X values */
|
||||
for (i = 0, j = 19; i < 20; i += 2, j += 3) {
|
||||
dev->xy_cur[i] = dev->data[j + 1];
|
||||
dev->xy_cur[i + 1] = dev->data[j + 2];
|
||||
}
|
||||
/* read Y values */
|
||||
for (i = 0, j = 1; i < 9; i += 2, j += 3) {
|
||||
dev->xy_cur[ATP_XSENSORS + i] = dev->data[j + 1];
|
||||
dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 2];
|
||||
}
|
||||
} else if (atp_is_geyser_2(dev)) {
|
||||
memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
|
||||
|
||||
/*
|
||||
* The values are laid out like this:
|
||||
* Y1, Y2, -, Y3, Y4, -, ..., X1, X2, -, X3, X4, -, ...
|
||||
* '-' is an unused value.
|
||||
*/
|
||||
|
||||
/* read X values */
|
||||
for (i = 0, j = 19; i < 20; i += 2, j += 3) {
|
||||
dev->xy_cur[i] = dev->data[j];
|
||||
dev->xy_cur[i + 1] = dev->data[j + 1];
|
||||
}
|
||||
|
||||
/* read Y values */
|
||||
for (i = 0, j = 1; i < 9; i += 2, j += 3) {
|
||||
dev->xy_cur[ATP_XSENSORS + i] = dev->data[j];
|
||||
dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 1];
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < 8; i++) {
|
||||
/* X values */
|
||||
dev->xy_cur[i ] = dev->data[5 * i + 2];
|
||||
dev->xy_cur[i + 8] = dev->data[5 * i + 4];
|
||||
dev->xy_cur[i + 16] = dev->data[5 * i + 42];
|
||||
if (i < 2)
|
||||
dev->xy_cur[i + 24] = dev->data[5 * i + 44];
|
||||
|
||||
/* Y values */
|
||||
dev->xy_cur[i + 26] = dev->data[5 * i + 1];
|
||||
dev->xy_cur[i + 34] = dev->data[5 * i + 3];
|
||||
}
|
||||
}
|
||||
|
||||
dbg_dump("sample", dev->xy_cur);
|
||||
|
||||
if (!dev->valid) {
|
||||
/* first sample */
|
||||
dev->valid = 1;
|
||||
dev->x_old = dev->y_old = -1;
|
||||
memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
|
||||
|
||||
if (atp_is_geyser_3(dev)) /* No 17" Macbooks (yet) */
|
||||
goto exit;
|
||||
|
||||
/* 17" Powerbooks have extra X sensors */
|
||||
for (i = (atp_is_geyser_2(dev)?15:16); i < ATP_XSENSORS; i++) {
|
||||
if (!dev->xy_cur[i]) continue;
|
||||
|
||||
printk("appletouch: 17\" model detected.\n");
|
||||
if(atp_is_geyser_2(dev))
|
||||
input_set_abs_params(dev->input, ABS_X, 0,
|
||||
(20 - 1) *
|
||||
ATP_XFACT - 1,
|
||||
ATP_FUZZ, 0);
|
||||
else
|
||||
input_set_abs_params(dev->input, ABS_X, 0,
|
||||
(ATP_XSENSORS - 1) *
|
||||
ATP_XFACT - 1,
|
||||
ATP_FUZZ, 0);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
|
||||
/* accumulate the change */
|
||||
signed char change = dev->xy_old[i] - dev->xy_cur[i];
|
||||
dev->xy_acc[i] -= change;
|
||||
|
||||
/* prevent down drifting */
|
||||
if (dev->xy_acc[i] < 0)
|
||||
dev->xy_acc[i] = 0;
|
||||
}
|
||||
|
||||
memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
|
||||
|
||||
dbg_dump("accumulator", dev->xy_acc);
|
||||
|
||||
x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS,
|
||||
ATP_XFACT, &x_z, &x_f);
|
||||
y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
|
||||
ATP_YFACT, &y_z, &y_f);
|
||||
|
||||
if (x && y) {
|
||||
if (dev->x_old != -1) {
|
||||
x = (dev->x_old * 3 + x) >> 2;
|
||||
y = (dev->y_old * 3 + y) >> 2;
|
||||
dev->x_old = x;
|
||||
dev->y_old = y;
|
||||
|
||||
if (debug > 1)
|
||||
printk("appletouch: X: %3d Y: %3d "
|
||||
"Xz: %3d Yz: %3d\n",
|
||||
x, y, x_z, y_z);
|
||||
|
||||
input_report_key(dev->input, BTN_TOUCH, 1);
|
||||
input_report_abs(dev->input, ABS_X, x);
|
||||
input_report_abs(dev->input, ABS_Y, y);
|
||||
input_report_abs(dev->input, ABS_PRESSURE,
|
||||
min(ATP_PRESSURE, x_z + y_z));
|
||||
atp_report_fingers(dev->input, max(x_f, y_f));
|
||||
}
|
||||
dev->x_old = x;
|
||||
dev->y_old = y;
|
||||
}
|
||||
else if (!x && !y) {
|
||||
|
||||
dev->x_old = dev->y_old = -1;
|
||||
input_report_key(dev->input, BTN_TOUCH, 0);
|
||||
input_report_abs(dev->input, ABS_PRESSURE, 0);
|
||||
atp_report_fingers(dev->input, 0);
|
||||
|
||||
/* reset the accumulator on release */
|
||||
memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
|
||||
}
|
||||
|
||||
input_report_key(dev->input, BTN_LEFT,
|
||||
!!dev->data[dev->datalen - 1]);
|
||||
|
||||
input_sync(dev->input);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
|
||||
if (retval) {
|
||||
err("%s - usb_submit_urb failed with result %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
}
|
||||
|
||||
static int atp_open(struct input_dev *input)
|
||||
{
|
||||
struct atp *dev = input->private;
|
||||
|
||||
if (usb_submit_urb(dev->urb, GFP_ATOMIC))
|
||||
return -EIO;
|
||||
|
||||
dev->open = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atp_close(struct input_dev *input)
|
||||
{
|
||||
struct atp *dev = input->private;
|
||||
|
||||
usb_kill_urb(dev->urb);
|
||||
dev->open = 0;
|
||||
}
|
||||
|
||||
static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id)
|
||||
{
|
||||
struct atp *dev;
|
||||
struct input_dev *input_dev;
|
||||
struct usb_device *udev = interface_to_usbdev(iface);
|
||||
struct usb_host_interface *iface_desc;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
int int_in_endpointAddr = 0;
|
||||
int i, retval = -ENOMEM;
|
||||
|
||||
|
||||
/* set up the endpoint information */
|
||||
/* use only the first interrupt-in endpoint */
|
||||
iface_desc = iface->cur_altsetting;
|
||||
for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
|
||||
endpoint = &iface_desc->endpoint[i].desc;
|
||||
if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) {
|
||||
/* we found an interrupt in endpoint */
|
||||
int_in_endpointAddr = endpoint->bEndpointAddress;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!int_in_endpointAddr) {
|
||||
err("Could not find int-in endpoint");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* allocate memory for our device state and initialize it */
|
||||
dev = kzalloc(sizeof(struct atp), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!dev || !input_dev) {
|
||||
err("Out of memory");
|
||||
goto err_free_devs;
|
||||
}
|
||||
|
||||
dev->udev = udev;
|
||||
dev->input = input_dev;
|
||||
dev->overflowwarn = 0;
|
||||
if (atp_is_geyser_3(dev))
|
||||
dev->datalen = 64;
|
||||
else if (atp_is_geyser_2(dev))
|
||||
dev->datalen = 64;
|
||||
else
|
||||
dev->datalen = 81;
|
||||
|
||||
if (atp_is_geyser_3(dev)) {
|
||||
/*
|
||||
* By default Geyser 3 device sends standard USB HID mouse
|
||||
* packets (Report ID 2). This code changes device mode, so it
|
||||
* sends raw sensor reports (Report ID 5).
|
||||
*/
|
||||
char data[8];
|
||||
int size;
|
||||
|
||||
size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
|
||||
ATP_GEYSER3_MODE_READ_REQUEST_ID,
|
||||
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
ATP_GEYSER3_MODE_REQUEST_VALUE,
|
||||
ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000);
|
||||
|
||||
if (size != 8) {
|
||||
err("Could not do mode read request from device"
|
||||
" (Geyser 3 mode)");
|
||||
goto err_free_devs;
|
||||
}
|
||||
|
||||
/* Apply the mode switch */
|
||||
data[0] = ATP_GEYSER3_MODE_VENDOR_VALUE;
|
||||
|
||||
size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||
ATP_GEYSER3_MODE_WRITE_REQUEST_ID,
|
||||
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
ATP_GEYSER3_MODE_REQUEST_VALUE,
|
||||
ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000);
|
||||
|
||||
if (size != 8) {
|
||||
err("Could not do mode write request to device"
|
||||
" (Geyser 3 mode)");
|
||||
goto err_free_devs;
|
||||
}
|
||||
printk("appletouch Geyser 3 inited.\n");
|
||||
}
|
||||
|
||||
dev->urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!dev->urb) {
|
||||
retval = -ENOMEM;
|
||||
goto err_free_devs;
|
||||
}
|
||||
|
||||
dev->data = usb_buffer_alloc(dev->udev, dev->datalen, GFP_KERNEL,
|
||||
&dev->urb->transfer_dma);
|
||||
if (!dev->data) {
|
||||
retval = -ENOMEM;
|
||||
goto err_free_urb;
|
||||
}
|
||||
|
||||
usb_fill_int_urb(dev->urb, udev,
|
||||
usb_rcvintpipe(udev, int_in_endpointAddr),
|
||||
dev->data, dev->datalen, atp_complete, dev, 1);
|
||||
|
||||
usb_make_path(udev, dev->phys, sizeof(dev->phys));
|
||||
strlcat(dev->phys, "/input0", sizeof(dev->phys));
|
||||
|
||||
input_dev->name = "appletouch";
|
||||
input_dev->phys = dev->phys;
|
||||
usb_to_input_id(dev->udev, &input_dev->id);
|
||||
input_dev->cdev.dev = &iface->dev;
|
||||
|
||||
input_dev->private = dev;
|
||||
input_dev->open = atp_open;
|
||||
input_dev->close = atp_close;
|
||||
|
||||
set_bit(EV_ABS, input_dev->evbit);
|
||||
|
||||
if (atp_is_geyser_3(dev)) {
|
||||
/*
|
||||
* MacBook have 20 X sensors, 10 Y sensors
|
||||
*/
|
||||
input_set_abs_params(input_dev, ABS_X, 0,
|
||||
((20 - 1) * ATP_XFACT) - 1, ATP_FUZZ, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0,
|
||||
((10 - 1) * ATP_YFACT) - 1, ATP_FUZZ, 0);
|
||||
} else if (atp_is_geyser_2(dev)) {
|
||||
/*
|
||||
* Oct 2005 15" PowerBooks have 15 X sensors, 17" are detected
|
||||
* later.
|
||||
*/
|
||||
input_set_abs_params(input_dev, ABS_X, 0,
|
||||
((15 - 1) * ATP_XFACT) - 1, ATP_FUZZ, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0,
|
||||
((9 - 1) * ATP_YFACT) - 1, ATP_FUZZ, 0);
|
||||
} else {
|
||||
/*
|
||||
* 12" and 15" Powerbooks only have 16 x sensors,
|
||||
* 17" models are detected later.
|
||||
*/
|
||||
input_set_abs_params(input_dev, ABS_X, 0,
|
||||
(16 - 1) * ATP_XFACT - 1, ATP_FUZZ, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0,
|
||||
(ATP_YSENSORS - 1) * ATP_YFACT - 1, ATP_FUZZ, 0);
|
||||
}
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);
|
||||
|
||||
set_bit(EV_KEY, input_dev->evbit);
|
||||
set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
set_bit(BTN_TOOL_FINGER, input_dev->keybit);
|
||||
set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
|
||||
set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
|
||||
set_bit(BTN_LEFT, input_dev->keybit);
|
||||
|
||||
input_register_device(dev->input);
|
||||
|
||||
/* save our data pointer in this interface device */
|
||||
usb_set_intfdata(iface, dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_urb:
|
||||
usb_free_urb(dev->urb);
|
||||
err_free_devs:
|
||||
usb_set_intfdata(iface, NULL);
|
||||
kfree(dev);
|
||||
input_free_device(input_dev);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void atp_disconnect(struct usb_interface *iface)
|
||||
{
|
||||
struct atp *dev = usb_get_intfdata(iface);
|
||||
|
||||
usb_set_intfdata(iface, NULL);
|
||||
if (dev) {
|
||||
usb_kill_urb(dev->urb);
|
||||
input_unregister_device(dev->input);
|
||||
usb_buffer_free(dev->udev, dev->datalen,
|
||||
dev->data, dev->urb->transfer_dma);
|
||||
usb_free_urb(dev->urb);
|
||||
kfree(dev);
|
||||
}
|
||||
printk(KERN_INFO "input: appletouch disconnected\n");
|
||||
}
|
||||
|
||||
static int atp_suspend(struct usb_interface *iface, pm_message_t message)
|
||||
{
|
||||
struct atp *dev = usb_get_intfdata(iface);
|
||||
usb_kill_urb(dev->urb);
|
||||
dev->valid = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atp_resume(struct usb_interface *iface)
|
||||
{
|
||||
struct atp *dev = usb_get_intfdata(iface);
|
||||
if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_driver atp_driver = {
|
||||
.name = "appletouch",
|
||||
.probe = atp_probe,
|
||||
.disconnect = atp_disconnect,
|
||||
.suspend = atp_suspend,
|
||||
.resume = atp_resume,
|
||||
.id_table = atp_table,
|
||||
};
|
||||
|
||||
static int __init atp_init(void)
|
||||
{
|
||||
return usb_register(&atp_driver);
|
||||
}
|
||||
|
||||
static void __exit atp_exit(void)
|
||||
{
|
||||
usb_deregister(&atp_driver);
|
||||
}
|
||||
|
||||
module_init(atp_init);
|
||||
module_exit(atp_exit);
|
||||
842
drivers/usb/input/ati_remote.c
Normal file
842
drivers/usb/input/ati_remote.c
Normal file
@@ -0,0 +1,842 @@
|
||||
/*
|
||||
* USB ATI Remote support
|
||||
*
|
||||
* Version 2.2.0 Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>
|
||||
* Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev
|
||||
*
|
||||
* This 2.2.0 version is a rewrite / cleanup of the 2.1.1 driver, including
|
||||
* porting to the 2.6 kernel interfaces, along with other modification
|
||||
* to better match the style of the existing usb/input drivers. However, the
|
||||
* protocol and hardware handling is essentially unchanged from 2.1.1.
|
||||
*
|
||||
* The 2.1.1 driver was derived from the usbati_remote and usbkbd drivers by
|
||||
* Vojtech Pavlik.
|
||||
*
|
||||
* Changes:
|
||||
*
|
||||
* Feb 2004: Torrey Hoffman <thoffman@arnor.net>
|
||||
* Version 2.2.0
|
||||
* Jun 2004: Torrey Hoffman <thoffman@arnor.net>
|
||||
* Version 2.2.1
|
||||
* Added key repeat support contributed by:
|
||||
* Vincent Vanackere <vanackere@lif.univ-mrs.fr>
|
||||
* Added support for the "Lola" remote contributed by:
|
||||
* Seth Cohn <sethcohn@yahoo.com>
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Hardware & software notes
|
||||
*
|
||||
* These remote controls are distributed by ATI as part of their
|
||||
* "All-In-Wonder" video card packages. The receiver self-identifies as a
|
||||
* "USB Receiver" with manufacturer "X10 Wireless Technology Inc".
|
||||
*
|
||||
* The "Lola" remote is available from X10. See:
|
||||
* http://www.x10.com/products/lola_sg1.htm
|
||||
* The Lola is similar to the ATI remote but has no mouse support, and slightly
|
||||
* different keys.
|
||||
*
|
||||
* It is possible to use multiple receivers and remotes on multiple computers
|
||||
* simultaneously by configuring them to use specific channels.
|
||||
*
|
||||
* The RF protocol used by the remote supports 16 distinct channels, 1 to 16.
|
||||
* Actually, it may even support more, at least in some revisions of the
|
||||
* hardware.
|
||||
*
|
||||
* Each remote can be configured to transmit on one channel as follows:
|
||||
* - Press and hold the "hand icon" button.
|
||||
* - When the red LED starts to blink, let go of the "hand icon" button.
|
||||
* - When it stops blinking, input the channel code as two digits, from 01
|
||||
* to 16, and press the hand icon again.
|
||||
*
|
||||
* The timing can be a little tricky. Try loading the module with debug=1
|
||||
* to have the kernel print out messages about the remote control number
|
||||
* and mask. Note: debugging prints remote numbers as zero-based hexadecimal.
|
||||
*
|
||||
* The driver has a "channel_mask" parameter. This bitmask specifies which
|
||||
* channels will be ignored by the module. To mask out channels, just add
|
||||
* all the 2^channel_number values together.
|
||||
*
|
||||
* For instance, set channel_mask = 2^4 = 16 (binary 10000) to make ati_remote
|
||||
* ignore signals coming from remote controls transmitting on channel 4, but
|
||||
* accept all other channels.
|
||||
*
|
||||
* Or, set channel_mask = 65533, (0xFFFD), and all channels except 1 will be
|
||||
* ignored.
|
||||
*
|
||||
* The default is 0 (respond to all channels). Bit 0 and bits 17-32 of this
|
||||
* parameter are unused.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/usb/input.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
||||
/*
|
||||
* Module and Version Information, Module Parameters
|
||||
*/
|
||||
|
||||
#define ATI_REMOTE_VENDOR_ID 0x0bc7
|
||||
#define ATI_REMOTE_PRODUCT_ID 0x004
|
||||
#define LOLA_REMOTE_PRODUCT_ID 0x002
|
||||
#define MEDION_REMOTE_PRODUCT_ID 0x006
|
||||
|
||||
#define DRIVER_VERSION "2.2.1"
|
||||
#define DRIVER_AUTHOR "Torrey Hoffman <thoffman@arnor.net>"
|
||||
#define DRIVER_DESC "ATI/X10 RF USB Remote Control"
|
||||
|
||||
#define NAME_BUFSIZE 80 /* size of product name, path buffers */
|
||||
#define DATA_BUFSIZE 63 /* size of URB data buffers */
|
||||
|
||||
/*
|
||||
* Duplicate event filtering time.
|
||||
* Sequential, identical KIND_FILTERED inputs with less than
|
||||
* FILTER_TIME milliseconds between them are considered as repeat
|
||||
* events. The hardware generates 5 events for the first keypress
|
||||
* and we have to take this into account for an accurate repeat
|
||||
* behaviour.
|
||||
*/
|
||||
#define FILTER_TIME 60 /* msec */
|
||||
|
||||
static unsigned long channel_mask;
|
||||
module_param(channel_mask, ulong, 0644);
|
||||
MODULE_PARM_DESC(channel_mask, "Bitmask of remote control channels to ignore");
|
||||
|
||||
static int debug;
|
||||
module_param(debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
|
||||
|
||||
static int repeat_filter = FILTER_TIME;
|
||||
module_param(repeat_filter, int, 0644);
|
||||
MODULE_PARM_DESC(repeat_filter, "Repeat filter time, default = 60 msec");
|
||||
|
||||
#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
|
||||
#undef err
|
||||
#define err(format, arg...) printk(KERN_ERR format , ## arg)
|
||||
|
||||
static struct usb_device_id ati_remote_table[] = {
|
||||
{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID) },
|
||||
{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID) },
|
||||
{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID) },
|
||||
{} /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, ati_remote_table);
|
||||
|
||||
/* Get hi and low bytes of a 16-bits int */
|
||||
#define HI(a) ((unsigned char)((a) >> 8))
|
||||
#define LO(a) ((unsigned char)((a) & 0xff))
|
||||
|
||||
#define SEND_FLAG_IN_PROGRESS 1
|
||||
#define SEND_FLAG_COMPLETE 2
|
||||
|
||||
/* Device initialization strings */
|
||||
static char init1[] = { 0x01, 0x00, 0x20, 0x14 };
|
||||
static char init2[] = { 0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 };
|
||||
|
||||
struct ati_remote {
|
||||
struct input_dev *idev;
|
||||
struct usb_device *udev;
|
||||
struct usb_interface *interface;
|
||||
|
||||
struct urb *irq_urb;
|
||||
struct urb *out_urb;
|
||||
struct usb_endpoint_descriptor *endpoint_in;
|
||||
struct usb_endpoint_descriptor *endpoint_out;
|
||||
unsigned char *inbuf;
|
||||
unsigned char *outbuf;
|
||||
dma_addr_t inbuf_dma;
|
||||
dma_addr_t outbuf_dma;
|
||||
|
||||
unsigned char old_data[2]; /* Detect duplicate events */
|
||||
unsigned long old_jiffies;
|
||||
unsigned long acc_jiffies; /* handle acceleration */
|
||||
unsigned int repeat_count;
|
||||
|
||||
char name[NAME_BUFSIZE];
|
||||
char phys[NAME_BUFSIZE];
|
||||
|
||||
wait_queue_head_t wait;
|
||||
int send_flags;
|
||||
};
|
||||
|
||||
/* "Kinds" of messages sent from the hardware to the driver. */
|
||||
#define KIND_END 0
|
||||
#define KIND_LITERAL 1 /* Simply pass to input system */
|
||||
#define KIND_FILTERED 2 /* Add artificial key-up events, drop keyrepeats */
|
||||
#define KIND_LU 3 /* Directional keypad diagonals - left up, */
|
||||
#define KIND_RU 4 /* right up, */
|
||||
#define KIND_LD 5 /* left down, */
|
||||
#define KIND_RD 6 /* right down */
|
||||
#define KIND_ACCEL 7 /* Directional keypad - left, right, up, down.*/
|
||||
|
||||
/* Translation table from hardware messages to input events. */
|
||||
static const struct {
|
||||
short kind;
|
||||
unsigned char data1, data2;
|
||||
int type;
|
||||
unsigned int code;
|
||||
int value;
|
||||
} ati_remote_tbl[] = {
|
||||
/* Directional control pad axes */
|
||||
{KIND_ACCEL, 0x35, 0x70, EV_REL, REL_X, -1}, /* left */
|
||||
{KIND_ACCEL, 0x36, 0x71, EV_REL, REL_X, 1}, /* right */
|
||||
{KIND_ACCEL, 0x37, 0x72, EV_REL, REL_Y, -1}, /* up */
|
||||
{KIND_ACCEL, 0x38, 0x73, EV_REL, REL_Y, 1}, /* down */
|
||||
/* Directional control pad diagonals */
|
||||
{KIND_LU, 0x39, 0x74, EV_REL, 0, 0}, /* left up */
|
||||
{KIND_RU, 0x3a, 0x75, EV_REL, 0, 0}, /* right up */
|
||||
{KIND_LD, 0x3c, 0x77, EV_REL, 0, 0}, /* left down */
|
||||
{KIND_RD, 0x3b, 0x76, EV_REL, 0, 0}, /* right down */
|
||||
|
||||
/* "Mouse button" buttons */
|
||||
{KIND_LITERAL, 0x3d, 0x78, EV_KEY, BTN_LEFT, 1}, /* left btn down */
|
||||
{KIND_LITERAL, 0x3e, 0x79, EV_KEY, BTN_LEFT, 0}, /* left btn up */
|
||||
{KIND_LITERAL, 0x41, 0x7c, EV_KEY, BTN_RIGHT, 1},/* right btn down */
|
||||
{KIND_LITERAL, 0x42, 0x7d, EV_KEY, BTN_RIGHT, 0},/* right btn up */
|
||||
|
||||
/* Artificial "doubleclick" events are generated by the hardware.
|
||||
* They are mapped to the "side" and "extra" mouse buttons here. */
|
||||
{KIND_FILTERED, 0x3f, 0x7a, EV_KEY, BTN_SIDE, 1}, /* left dblclick */
|
||||
{KIND_FILTERED, 0x43, 0x7e, EV_KEY, BTN_EXTRA, 1},/* right dblclick */
|
||||
|
||||
/* keyboard. */
|
||||
{KIND_FILTERED, 0xd2, 0x0d, EV_KEY, KEY_1, 1},
|
||||
{KIND_FILTERED, 0xd3, 0x0e, EV_KEY, KEY_2, 1},
|
||||
{KIND_FILTERED, 0xd4, 0x0f, EV_KEY, KEY_3, 1},
|
||||
{KIND_FILTERED, 0xd5, 0x10, EV_KEY, KEY_4, 1},
|
||||
{KIND_FILTERED, 0xd6, 0x11, EV_KEY, KEY_5, 1},
|
||||
{KIND_FILTERED, 0xd7, 0x12, EV_KEY, KEY_6, 1},
|
||||
{KIND_FILTERED, 0xd8, 0x13, EV_KEY, KEY_7, 1},
|
||||
{KIND_FILTERED, 0xd9, 0x14, EV_KEY, KEY_8, 1},
|
||||
{KIND_FILTERED, 0xda, 0x15, EV_KEY, KEY_9, 1},
|
||||
{KIND_FILTERED, 0xdc, 0x17, EV_KEY, KEY_0, 1},
|
||||
{KIND_FILTERED, 0xc5, 0x00, EV_KEY, KEY_A, 1},
|
||||
{KIND_FILTERED, 0xc6, 0x01, EV_KEY, KEY_B, 1},
|
||||
{KIND_FILTERED, 0xde, 0x19, EV_KEY, KEY_C, 1},
|
||||
{KIND_FILTERED, 0xe0, 0x1b, EV_KEY, KEY_D, 1},
|
||||
{KIND_FILTERED, 0xe6, 0x21, EV_KEY, KEY_E, 1},
|
||||
{KIND_FILTERED, 0xe8, 0x23, EV_KEY, KEY_F, 1},
|
||||
|
||||
/* "special" keys */
|
||||
{KIND_FILTERED, 0xdd, 0x18, EV_KEY, KEY_KPENTER, 1}, /* "check" */
|
||||
{KIND_FILTERED, 0xdb, 0x16, EV_KEY, KEY_MENU, 1}, /* "menu" */
|
||||
{KIND_FILTERED, 0xc7, 0x02, EV_KEY, KEY_POWER, 1}, /* Power */
|
||||
{KIND_FILTERED, 0xc8, 0x03, EV_KEY, KEY_TV, 1}, /* TV */
|
||||
{KIND_FILTERED, 0xc9, 0x04, EV_KEY, KEY_DVD, 1}, /* DVD */
|
||||
{KIND_FILTERED, 0xca, 0x05, EV_KEY, KEY_WWW, 1}, /* WEB */
|
||||
{KIND_FILTERED, 0xcb, 0x06, EV_KEY, KEY_BOOKMARKS, 1}, /* "book" */
|
||||
{KIND_FILTERED, 0xcc, 0x07, EV_KEY, KEY_EDIT, 1}, /* "hand" */
|
||||
{KIND_FILTERED, 0xe1, 0x1c, EV_KEY, KEY_COFFEE, 1}, /* "timer" */
|
||||
{KIND_FILTERED, 0xe5, 0x20, EV_KEY, KEY_FRONT, 1}, /* "max" */
|
||||
{KIND_FILTERED, 0xe2, 0x1d, EV_KEY, KEY_LEFT, 1}, /* left */
|
||||
{KIND_FILTERED, 0xe4, 0x1f, EV_KEY, KEY_RIGHT, 1}, /* right */
|
||||
{KIND_FILTERED, 0xe7, 0x22, EV_KEY, KEY_DOWN, 1}, /* down */
|
||||
{KIND_FILTERED, 0xdf, 0x1a, EV_KEY, KEY_UP, 1}, /* up */
|
||||
{KIND_FILTERED, 0xe3, 0x1e, EV_KEY, KEY_OK, 1}, /* "OK" */
|
||||
{KIND_FILTERED, 0xce, 0x09, EV_KEY, KEY_VOLUMEDOWN, 1}, /* VOL + */
|
||||
{KIND_FILTERED, 0xcd, 0x08, EV_KEY, KEY_VOLUMEUP, 1}, /* VOL - */
|
||||
{KIND_FILTERED, 0xcf, 0x0a, EV_KEY, KEY_MUTE, 1}, /* MUTE */
|
||||
{KIND_FILTERED, 0xd0, 0x0b, EV_KEY, KEY_CHANNELUP, 1}, /* CH + */
|
||||
{KIND_FILTERED, 0xd1, 0x0c, EV_KEY, KEY_CHANNELDOWN, 1},/* CH - */
|
||||
{KIND_FILTERED, 0xec, 0x27, EV_KEY, KEY_RECORD, 1}, /* ( o) red */
|
||||
{KIND_FILTERED, 0xea, 0x25, EV_KEY, KEY_PLAY, 1}, /* ( >) */
|
||||
{KIND_FILTERED, 0xe9, 0x24, EV_KEY, KEY_REWIND, 1}, /* (<<) */
|
||||
{KIND_FILTERED, 0xeb, 0x26, EV_KEY, KEY_FORWARD, 1}, /* (>>) */
|
||||
{KIND_FILTERED, 0xed, 0x28, EV_KEY, KEY_STOP, 1}, /* ([]) */
|
||||
{KIND_FILTERED, 0xee, 0x29, EV_KEY, KEY_PAUSE, 1}, /* ('') */
|
||||
{KIND_FILTERED, 0xf0, 0x2b, EV_KEY, KEY_PREVIOUS, 1}, /* (<-) */
|
||||
{KIND_FILTERED, 0xef, 0x2a, EV_KEY, KEY_NEXT, 1}, /* (>+) */
|
||||
{KIND_FILTERED, 0xf2, 0x2D, EV_KEY, KEY_INFO, 1}, /* PLAYING */
|
||||
{KIND_FILTERED, 0xf3, 0x2E, EV_KEY, KEY_HOME, 1}, /* TOP */
|
||||
{KIND_FILTERED, 0xf4, 0x2F, EV_KEY, KEY_END, 1}, /* END */
|
||||
{KIND_FILTERED, 0xf5, 0x30, EV_KEY, KEY_SELECT, 1}, /* SELECT */
|
||||
|
||||
{KIND_END, 0x00, 0x00, EV_MAX + 1, 0, 0}
|
||||
};
|
||||
|
||||
/* Local function prototypes */
|
||||
static void ati_remote_dump (unsigned char *data, unsigned int actual_length);
|
||||
static int ati_remote_open (struct input_dev *inputdev);
|
||||
static void ati_remote_close (struct input_dev *inputdev);
|
||||
static int ati_remote_sendpacket (struct ati_remote *ati_remote, u16 cmd, unsigned char *data);
|
||||
static void ati_remote_irq_out (struct urb *urb);
|
||||
static void ati_remote_irq_in (struct urb *urb);
|
||||
static void ati_remote_input_report (struct urb *urb);
|
||||
static int ati_remote_initialize (struct ati_remote *ati_remote);
|
||||
static int ati_remote_probe (struct usb_interface *interface, const struct usb_device_id *id);
|
||||
static void ati_remote_disconnect (struct usb_interface *interface);
|
||||
|
||||
/* usb specific object to register with the usb subsystem */
|
||||
static struct usb_driver ati_remote_driver = {
|
||||
.name = "ati_remote",
|
||||
.probe = ati_remote_probe,
|
||||
.disconnect = ati_remote_disconnect,
|
||||
.id_table = ati_remote_table,
|
||||
};
|
||||
|
||||
/*
|
||||
* ati_remote_dump_input
|
||||
*/
|
||||
static void ati_remote_dump(unsigned char *data, unsigned int len)
|
||||
{
|
||||
if ((len == 1) && (data[0] != (unsigned char)0xff) && (data[0] != 0x00))
|
||||
warn("Weird byte 0x%02x", data[0]);
|
||||
else if (len == 4)
|
||||
warn("Weird key %02x %02x %02x %02x",
|
||||
data[0], data[1], data[2], data[3]);
|
||||
else
|
||||
warn("Weird data, len=%d %02x %02x %02x %02x %02x %02x ...",
|
||||
len, data[0], data[1], data[2], data[3], data[4], data[5]);
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_open
|
||||
*/
|
||||
static int ati_remote_open(struct input_dev *inputdev)
|
||||
{
|
||||
struct ati_remote *ati_remote = inputdev->private;
|
||||
|
||||
/* On first open, submit the read urb which was set up previously. */
|
||||
ati_remote->irq_urb->dev = ati_remote->udev;
|
||||
if (usb_submit_urb(ati_remote->irq_urb, GFP_KERNEL)) {
|
||||
dev_err(&ati_remote->interface->dev,
|
||||
"%s: usb_submit_urb failed!\n", __FUNCTION__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_close
|
||||
*/
|
||||
static void ati_remote_close(struct input_dev *inputdev)
|
||||
{
|
||||
struct ati_remote *ati_remote = inputdev->private;
|
||||
|
||||
usb_kill_urb(ati_remote->irq_urb);
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_irq_out
|
||||
*/
|
||||
static void ati_remote_irq_out(struct urb *urb)
|
||||
{
|
||||
struct ati_remote *ati_remote = urb->context;
|
||||
|
||||
if (urb->status) {
|
||||
dev_dbg(&ati_remote->interface->dev, "%s: status %d\n",
|
||||
__FUNCTION__, urb->status);
|
||||
return;
|
||||
}
|
||||
|
||||
ati_remote->send_flags |= SEND_FLAG_COMPLETE;
|
||||
wmb();
|
||||
wake_up(&ati_remote->wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_sendpacket
|
||||
*
|
||||
* Used to send device initialization strings
|
||||
*/
|
||||
static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigned char *data)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
/* Set up out_urb */
|
||||
memcpy(ati_remote->out_urb->transfer_buffer + 1, data, LO(cmd));
|
||||
((char *) ati_remote->out_urb->transfer_buffer)[0] = HI(cmd);
|
||||
|
||||
ati_remote->out_urb->transfer_buffer_length = LO(cmd) + 1;
|
||||
ati_remote->out_urb->dev = ati_remote->udev;
|
||||
ati_remote->send_flags = SEND_FLAG_IN_PROGRESS;
|
||||
|
||||
retval = usb_submit_urb(ati_remote->out_urb, GFP_ATOMIC);
|
||||
if (retval) {
|
||||
dev_dbg(&ati_remote->interface->dev,
|
||||
"sendpacket: usb_submit_urb failed: %d\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
wait_event_timeout(ati_remote->wait,
|
||||
((ati_remote->out_urb->status != -EINPROGRESS) ||
|
||||
(ati_remote->send_flags & SEND_FLAG_COMPLETE)),
|
||||
HZ);
|
||||
usb_kill_urb(ati_remote->out_urb);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_event_lookup
|
||||
*/
|
||||
static int ati_remote_event_lookup(int rem, unsigned char d1, unsigned char d2)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) {
|
||||
/*
|
||||
* Decide if the table entry matches the remote input.
|
||||
*/
|
||||
if ((((ati_remote_tbl[i].data1 & 0x0f) == (d1 & 0x0f))) &&
|
||||
((((ati_remote_tbl[i].data1 >> 4) -
|
||||
(d1 >> 4) + rem) & 0x0f) == 0x0f) &&
|
||||
(ati_remote_tbl[i].data2 == d2))
|
||||
return i;
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_compute_accel
|
||||
*
|
||||
* Implements acceleration curve for directional control pad
|
||||
* If elapsed time since last event is > 1/4 second, user "stopped",
|
||||
* so reset acceleration. Otherwise, user is probably holding the control
|
||||
* pad down, so we increase acceleration, ramping up over two seconds to
|
||||
* a maximum speed.
|
||||
*/
|
||||
static int ati_remote_compute_accel(struct ati_remote *ati_remote)
|
||||
{
|
||||
static const char accel[] = { 1, 2, 4, 6, 9, 13, 20 };
|
||||
unsigned long now = jiffies;
|
||||
int acc;
|
||||
|
||||
if (time_after(now, ati_remote->old_jiffies + msecs_to_jiffies(250))) {
|
||||
acc = 1;
|
||||
ati_remote->acc_jiffies = now;
|
||||
}
|
||||
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(125)))
|
||||
acc = accel[0];
|
||||
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(250)))
|
||||
acc = accel[1];
|
||||
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(500)))
|
||||
acc = accel[2];
|
||||
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1000)))
|
||||
acc = accel[3];
|
||||
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1500)))
|
||||
acc = accel[4];
|
||||
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(2000)))
|
||||
acc = accel[5];
|
||||
else
|
||||
acc = accel[6];
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_report_input
|
||||
*/
|
||||
static void ati_remote_input_report(struct urb *urb)
|
||||
{
|
||||
struct ati_remote *ati_remote = urb->context;
|
||||
unsigned char *data= ati_remote->inbuf;
|
||||
struct input_dev *dev = ati_remote->idev;
|
||||
int index, acc;
|
||||
int remote_num;
|
||||
|
||||
/* Deal with strange looking inputs */
|
||||
if ( (urb->actual_length != 4) || (data[0] != 0x14) ||
|
||||
((data[3] & 0x0f) != 0x00) ) {
|
||||
ati_remote_dump(data, urb->actual_length);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mask unwanted remote channels. */
|
||||
/* note: remote_num is 0-based, channel 1 on remote == 0 here */
|
||||
remote_num = (data[3] >> 4) & 0x0f;
|
||||
if (channel_mask & (1 << (remote_num + 1))) {
|
||||
dbginfo(&ati_remote->interface->dev,
|
||||
"Masked input from channel 0x%02x: data %02x,%02x, mask= 0x%02lx\n",
|
||||
remote_num, data[1], data[2], channel_mask);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Look up event code index in translation table */
|
||||
index = ati_remote_event_lookup(remote_num, data[1], data[2]);
|
||||
if (index < 0) {
|
||||
dev_warn(&ati_remote->interface->dev,
|
||||
"Unknown input from channel 0x%02x: data %02x,%02x\n",
|
||||
remote_num, data[1], data[2]);
|
||||
return;
|
||||
}
|
||||
dbginfo(&ati_remote->interface->dev,
|
||||
"channel 0x%02x; data %02x,%02x; index %d; keycode %d\n",
|
||||
remote_num, data[1], data[2], index, ati_remote_tbl[index].code);
|
||||
|
||||
if (ati_remote_tbl[index].kind == KIND_LITERAL) {
|
||||
input_event(dev, ati_remote_tbl[index].type,
|
||||
ati_remote_tbl[index].code,
|
||||
ati_remote_tbl[index].value);
|
||||
input_sync(dev);
|
||||
|
||||
ati_remote->old_jiffies = jiffies;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ati_remote_tbl[index].kind == KIND_FILTERED) {
|
||||
/* Filter duplicate events which happen "too close" together. */
|
||||
if (ati_remote->old_data[0] == data[1] &&
|
||||
ati_remote->old_data[1] == data[2] &&
|
||||
time_before(jiffies, ati_remote->old_jiffies + msecs_to_jiffies(repeat_filter))) {
|
||||
ati_remote->repeat_count++;
|
||||
} else {
|
||||
ati_remote->repeat_count = 0;
|
||||
}
|
||||
|
||||
ati_remote->old_data[0] = data[1];
|
||||
ati_remote->old_data[1] = data[2];
|
||||
ati_remote->old_jiffies = jiffies;
|
||||
|
||||
if (ati_remote->repeat_count > 0 &&
|
||||
ati_remote->repeat_count < 5)
|
||||
return;
|
||||
|
||||
|
||||
input_event(dev, ati_remote_tbl[index].type,
|
||||
ati_remote_tbl[index].code, 1);
|
||||
input_sync(dev);
|
||||
input_event(dev, ati_remote_tbl[index].type,
|
||||
ati_remote_tbl[index].code, 0);
|
||||
input_sync(dev);
|
||||
|
||||
} else {
|
||||
|
||||
/*
|
||||
* Other event kinds are from the directional control pad, and have an
|
||||
* acceleration factor applied to them. Without this acceleration, the
|
||||
* control pad is mostly unusable.
|
||||
*/
|
||||
acc = ati_remote_compute_accel(ati_remote);
|
||||
|
||||
switch (ati_remote_tbl[index].kind) {
|
||||
case KIND_ACCEL:
|
||||
input_event(dev, ati_remote_tbl[index].type,
|
||||
ati_remote_tbl[index].code,
|
||||
ati_remote_tbl[index].value * acc);
|
||||
break;
|
||||
case KIND_LU:
|
||||
input_report_rel(dev, REL_X, -acc);
|
||||
input_report_rel(dev, REL_Y, -acc);
|
||||
break;
|
||||
case KIND_RU:
|
||||
input_report_rel(dev, REL_X, acc);
|
||||
input_report_rel(dev, REL_Y, -acc);
|
||||
break;
|
||||
case KIND_LD:
|
||||
input_report_rel(dev, REL_X, -acc);
|
||||
input_report_rel(dev, REL_Y, acc);
|
||||
break;
|
||||
case KIND_RD:
|
||||
input_report_rel(dev, REL_X, acc);
|
||||
input_report_rel(dev, REL_Y, acc);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n",
|
||||
ati_remote_tbl[index].kind);
|
||||
}
|
||||
input_sync(dev);
|
||||
|
||||
ati_remote->old_jiffies = jiffies;
|
||||
ati_remote->old_data[0] = data[1];
|
||||
ati_remote->old_data[1] = data[2];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_irq_in
|
||||
*/
|
||||
static void ati_remote_irq_in(struct urb *urb)
|
||||
{
|
||||
struct ati_remote *ati_remote = urb->context;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0: /* success */
|
||||
ati_remote_input_report(urb);
|
||||
break;
|
||||
case -ECONNRESET: /* unlink */
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
dev_dbg(&ati_remote->interface->dev, "%s: urb error status, unlink? \n",
|
||||
__FUNCTION__);
|
||||
return;
|
||||
default: /* error */
|
||||
dev_dbg(&ati_remote->interface->dev, "%s: Nonzero urb status %d\n",
|
||||
__FUNCTION__, urb->status);
|
||||
}
|
||||
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
dev_err(&ati_remote->interface->dev, "%s: usb_submit_urb()=%d\n",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_alloc_buffers
|
||||
*/
|
||||
static int ati_remote_alloc_buffers(struct usb_device *udev,
|
||||
struct ati_remote *ati_remote)
|
||||
{
|
||||
ati_remote->inbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, GFP_ATOMIC,
|
||||
&ati_remote->inbuf_dma);
|
||||
if (!ati_remote->inbuf)
|
||||
return -1;
|
||||
|
||||
ati_remote->outbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, GFP_ATOMIC,
|
||||
&ati_remote->outbuf_dma);
|
||||
if (!ati_remote->outbuf)
|
||||
return -1;
|
||||
|
||||
ati_remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!ati_remote->irq_urb)
|
||||
return -1;
|
||||
|
||||
ati_remote->out_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!ati_remote->out_urb)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_free_buffers
|
||||
*/
|
||||
static void ati_remote_free_buffers(struct ati_remote *ati_remote)
|
||||
{
|
||||
usb_free_urb(ati_remote->irq_urb);
|
||||
usb_free_urb(ati_remote->out_urb);
|
||||
|
||||
usb_buffer_free(ati_remote->udev, DATA_BUFSIZE,
|
||||
ati_remote->inbuf, ati_remote->inbuf_dma);
|
||||
|
||||
usb_buffer_free(ati_remote->udev, DATA_BUFSIZE,
|
||||
ati_remote->outbuf, ati_remote->outbuf_dma);
|
||||
}
|
||||
|
||||
static void ati_remote_input_init(struct ati_remote *ati_remote)
|
||||
{
|
||||
struct input_dev *idev = ati_remote->idev;
|
||||
int i;
|
||||
|
||||
idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
|
||||
idev->keybit[LONG(BTN_MOUSE)] = ( BIT(BTN_LEFT) | BIT(BTN_RIGHT) |
|
||||
BIT(BTN_SIDE) | BIT(BTN_EXTRA) );
|
||||
idev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
|
||||
for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++)
|
||||
if (ati_remote_tbl[i].type == EV_KEY)
|
||||
set_bit(ati_remote_tbl[i].code, idev->keybit);
|
||||
|
||||
idev->private = ati_remote;
|
||||
idev->open = ati_remote_open;
|
||||
idev->close = ati_remote_close;
|
||||
|
||||
idev->name = ati_remote->name;
|
||||
idev->phys = ati_remote->phys;
|
||||
|
||||
usb_to_input_id(ati_remote->udev, &idev->id);
|
||||
idev->cdev.dev = &ati_remote->udev->dev;
|
||||
}
|
||||
|
||||
static int ati_remote_initialize(struct ati_remote *ati_remote)
|
||||
{
|
||||
struct usb_device *udev = ati_remote->udev;
|
||||
int pipe, maxp;
|
||||
|
||||
init_waitqueue_head(&ati_remote->wait);
|
||||
|
||||
/* Set up irq_urb */
|
||||
pipe = usb_rcvintpipe(udev, ati_remote->endpoint_in->bEndpointAddress);
|
||||
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
|
||||
maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
|
||||
|
||||
usb_fill_int_urb(ati_remote->irq_urb, udev, pipe, ati_remote->inbuf,
|
||||
maxp, ati_remote_irq_in, ati_remote,
|
||||
ati_remote->endpoint_in->bInterval);
|
||||
ati_remote->irq_urb->transfer_dma = ati_remote->inbuf_dma;
|
||||
ati_remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
/* Set up out_urb */
|
||||
pipe = usb_sndintpipe(udev, ati_remote->endpoint_out->bEndpointAddress);
|
||||
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
|
||||
maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
|
||||
|
||||
usb_fill_int_urb(ati_remote->out_urb, udev, pipe, ati_remote->outbuf,
|
||||
maxp, ati_remote_irq_out, ati_remote,
|
||||
ati_remote->endpoint_out->bInterval);
|
||||
ati_remote->out_urb->transfer_dma = ati_remote->outbuf_dma;
|
||||
ati_remote->out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
/* send initialization strings */
|
||||
if ((ati_remote_sendpacket(ati_remote, 0x8004, init1)) ||
|
||||
(ati_remote_sendpacket(ati_remote, 0x8007, init2))) {
|
||||
dev_err(&ati_remote->interface->dev,
|
||||
"Initializing ati_remote hardware failed.\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_probe
|
||||
*/
|
||||
static int ati_remote_probe(struct usb_interface *interface, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(interface);
|
||||
struct usb_host_interface *iface_host = interface->cur_altsetting;
|
||||
struct usb_endpoint_descriptor *endpoint_in, *endpoint_out;
|
||||
struct ati_remote *ati_remote;
|
||||
struct input_dev *input_dev;
|
||||
int err = -ENOMEM;
|
||||
|
||||
if (iface_host->desc.bNumEndpoints != 2) {
|
||||
err("%s: Unexpected desc.bNumEndpoints\n", __FUNCTION__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
endpoint_in = &iface_host->endpoint[0].desc;
|
||||
endpoint_out = &iface_host->endpoint[1].desc;
|
||||
|
||||
if (!usb_endpoint_is_int_in(endpoint_in)) {
|
||||
err("%s: Unexpected endpoint_in\n", __FUNCTION__);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (le16_to_cpu(endpoint_in->wMaxPacketSize) == 0) {
|
||||
err("%s: endpoint_in message size==0? \n", __FUNCTION__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ati_remote = kzalloc(sizeof (struct ati_remote), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!ati_remote || !input_dev)
|
||||
goto fail1;
|
||||
|
||||
/* Allocate URB buffers, URBs */
|
||||
if (ati_remote_alloc_buffers(udev, ati_remote))
|
||||
goto fail2;
|
||||
|
||||
ati_remote->endpoint_in = endpoint_in;
|
||||
ati_remote->endpoint_out = endpoint_out;
|
||||
ati_remote->udev = udev;
|
||||
ati_remote->idev = input_dev;
|
||||
ati_remote->interface = interface;
|
||||
|
||||
usb_make_path(udev, ati_remote->phys, sizeof(ati_remote->phys));
|
||||
strlcpy(ati_remote->phys, "/input0", sizeof(ati_remote->phys));
|
||||
|
||||
if (udev->manufacturer)
|
||||
strlcpy(ati_remote->name, udev->manufacturer, sizeof(ati_remote->name));
|
||||
|
||||
if (udev->product)
|
||||
snprintf(ati_remote->name, sizeof(ati_remote->name),
|
||||
"%s %s", ati_remote->name, udev->product);
|
||||
|
||||
if (!strlen(ati_remote->name))
|
||||
snprintf(ati_remote->name, sizeof(ati_remote->name),
|
||||
DRIVER_DESC "(%04x,%04x)",
|
||||
le16_to_cpu(ati_remote->udev->descriptor.idVendor),
|
||||
le16_to_cpu(ati_remote->udev->descriptor.idProduct));
|
||||
|
||||
ati_remote_input_init(ati_remote);
|
||||
|
||||
/* Device Hardware Initialization - fills in ati_remote->idev from udev. */
|
||||
err = ati_remote_initialize(ati_remote);
|
||||
if (err)
|
||||
goto fail3;
|
||||
|
||||
/* Set up and register input device */
|
||||
input_register_device(ati_remote->idev);
|
||||
|
||||
usb_set_intfdata(interface, ati_remote);
|
||||
return 0;
|
||||
|
||||
fail3: usb_kill_urb(ati_remote->irq_urb);
|
||||
usb_kill_urb(ati_remote->out_urb);
|
||||
fail2: ati_remote_free_buffers(ati_remote);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(ati_remote);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_disconnect
|
||||
*/
|
||||
static void ati_remote_disconnect(struct usb_interface *interface)
|
||||
{
|
||||
struct ati_remote *ati_remote;
|
||||
|
||||
ati_remote = usb_get_intfdata(interface);
|
||||
usb_set_intfdata(interface, NULL);
|
||||
if (!ati_remote) {
|
||||
warn("%s - null device?\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
usb_kill_urb(ati_remote->irq_urb);
|
||||
usb_kill_urb(ati_remote->out_urb);
|
||||
input_unregister_device(ati_remote->idev);
|
||||
ati_remote_free_buffers(ati_remote);
|
||||
kfree(ati_remote);
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_init
|
||||
*/
|
||||
static int __init ati_remote_init(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = usb_register(&ati_remote_driver);
|
||||
if (result)
|
||||
err("usb_register error #%d\n", result);
|
||||
else
|
||||
info("Registered USB driver " DRIVER_DESC " v. " DRIVER_VERSION);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_exit
|
||||
*/
|
||||
static void __exit ati_remote_exit(void)
|
||||
{
|
||||
usb_deregister(&ati_remote_driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* module specification
|
||||
*/
|
||||
|
||||
module_init(ati_remote_init);
|
||||
module_exit(ati_remote_exit);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
474
drivers/usb/input/ati_remote2.c
Normal file
474
drivers/usb/input/ati_remote2.c
Normal file
@@ -0,0 +1,474 @@
|
||||
/*
|
||||
* ati_remote2 - ATI/Philips USB RF remote driver
|
||||
*
|
||||
* Copyright (C) 2005 Ville Syrjala <syrjala@sci.fi>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
#define DRIVER_DESC "ATI/Philips USB RF remote driver"
|
||||
#define DRIVER_VERSION "0.1"
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static unsigned int mode_mask = 0x1F;
|
||||
module_param(mode_mask, uint, 0644);
|
||||
MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>");
|
||||
|
||||
static struct usb_device_id ati_remote2_id_table[] = {
|
||||
{ USB_DEVICE(0x0471, 0x0602) }, /* ATI Remote Wonder II */
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, ati_remote2_id_table);
|
||||
|
||||
static struct {
|
||||
int hw_code;
|
||||
int key_code;
|
||||
} ati_remote2_key_table[] = {
|
||||
{ 0x00, KEY_0 },
|
||||
{ 0x01, KEY_1 },
|
||||
{ 0x02, KEY_2 },
|
||||
{ 0x03, KEY_3 },
|
||||
{ 0x04, KEY_4 },
|
||||
{ 0x05, KEY_5 },
|
||||
{ 0x06, KEY_6 },
|
||||
{ 0x07, KEY_7 },
|
||||
{ 0x08, KEY_8 },
|
||||
{ 0x09, KEY_9 },
|
||||
{ 0x0c, KEY_POWER },
|
||||
{ 0x0d, KEY_MUTE },
|
||||
{ 0x10, KEY_VOLUMEUP },
|
||||
{ 0x11, KEY_VOLUMEDOWN },
|
||||
{ 0x20, KEY_CHANNELUP },
|
||||
{ 0x21, KEY_CHANNELDOWN },
|
||||
{ 0x28, KEY_FORWARD },
|
||||
{ 0x29, KEY_REWIND },
|
||||
{ 0x2c, KEY_PLAY },
|
||||
{ 0x30, KEY_PAUSE },
|
||||
{ 0x31, KEY_STOP },
|
||||
{ 0x37, KEY_RECORD },
|
||||
{ 0x38, KEY_DVD },
|
||||
{ 0x39, KEY_TV },
|
||||
{ 0x54, KEY_MENU },
|
||||
{ 0x58, KEY_UP },
|
||||
{ 0x59, KEY_DOWN },
|
||||
{ 0x5a, KEY_LEFT },
|
||||
{ 0x5b, KEY_RIGHT },
|
||||
{ 0x5c, KEY_OK },
|
||||
{ 0x78, KEY_A },
|
||||
{ 0x79, KEY_B },
|
||||
{ 0x7a, KEY_C },
|
||||
{ 0x7b, KEY_D },
|
||||
{ 0x7c, KEY_E },
|
||||
{ 0x7d, KEY_F },
|
||||
{ 0x82, KEY_ENTER },
|
||||
{ 0x8e, KEY_VENDOR },
|
||||
{ 0x96, KEY_COFFEE },
|
||||
{ 0xa9, BTN_LEFT },
|
||||
{ 0xaa, BTN_RIGHT },
|
||||
{ 0xbe, KEY_QUESTION },
|
||||
{ 0xd5, KEY_FRONT },
|
||||
{ 0xd0, KEY_EDIT },
|
||||
{ 0xf9, KEY_INFO },
|
||||
{ (0x00 << 8) | 0x3f, KEY_PROG1 },
|
||||
{ (0x01 << 8) | 0x3f, KEY_PROG2 },
|
||||
{ (0x02 << 8) | 0x3f, KEY_PROG3 },
|
||||
{ (0x03 << 8) | 0x3f, KEY_PROG4 },
|
||||
{ (0x04 << 8) | 0x3f, KEY_PC },
|
||||
{ 0, KEY_RESERVED }
|
||||
};
|
||||
|
||||
struct ati_remote2 {
|
||||
struct input_dev *idev;
|
||||
struct usb_device *udev;
|
||||
|
||||
struct usb_interface *intf[2];
|
||||
struct usb_endpoint_descriptor *ep[2];
|
||||
struct urb *urb[2];
|
||||
void *buf[2];
|
||||
dma_addr_t buf_dma[2];
|
||||
|
||||
unsigned long jiffies;
|
||||
int mode;
|
||||
|
||||
char name[64];
|
||||
char phys[64];
|
||||
};
|
||||
|
||||
static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id);
|
||||
static void ati_remote2_disconnect(struct usb_interface *interface);
|
||||
|
||||
static struct usb_driver ati_remote2_driver = {
|
||||
.name = "ati_remote2",
|
||||
.probe = ati_remote2_probe,
|
||||
.disconnect = ati_remote2_disconnect,
|
||||
.id_table = ati_remote2_id_table,
|
||||
};
|
||||
|
||||
static int ati_remote2_open(struct input_dev *idev)
|
||||
{
|
||||
struct ati_remote2 *ar2 = idev->private;
|
||||
int r;
|
||||
|
||||
r = usb_submit_urb(ar2->urb[0], GFP_KERNEL);
|
||||
if (r) {
|
||||
dev_err(&ar2->intf[0]->dev,
|
||||
"%s: usb_submit_urb() = %d\n", __FUNCTION__, r);
|
||||
return r;
|
||||
}
|
||||
r = usb_submit_urb(ar2->urb[1], GFP_KERNEL);
|
||||
if (r) {
|
||||
usb_kill_urb(ar2->urb[0]);
|
||||
dev_err(&ar2->intf[1]->dev,
|
||||
"%s: usb_submit_urb() = %d\n", __FUNCTION__, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ati_remote2_close(struct input_dev *idev)
|
||||
{
|
||||
struct ati_remote2 *ar2 = idev->private;
|
||||
|
||||
usb_kill_urb(ar2->urb[0]);
|
||||
usb_kill_urb(ar2->urb[1]);
|
||||
}
|
||||
|
||||
static void ati_remote2_input_mouse(struct ati_remote2 *ar2)
|
||||
{
|
||||
struct input_dev *idev = ar2->idev;
|
||||
u8 *data = ar2->buf[0];
|
||||
|
||||
if (data[0] > 4) {
|
||||
dev_err(&ar2->intf[0]->dev,
|
||||
"Unknown mode byte (%02x %02x %02x %02x)\n",
|
||||
data[3], data[2], data[1], data[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!((1 << data[0]) & mode_mask))
|
||||
return;
|
||||
|
||||
input_event(idev, EV_REL, REL_X, (s8) data[1]);
|
||||
input_event(idev, EV_REL, REL_Y, (s8) data[2]);
|
||||
input_sync(idev);
|
||||
}
|
||||
|
||||
static int ati_remote2_lookup(unsigned int hw_code)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++)
|
||||
if (ati_remote2_key_table[i].hw_code == hw_code)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ati_remote2_input_key(struct ati_remote2 *ar2)
|
||||
{
|
||||
struct input_dev *idev = ar2->idev;
|
||||
u8 *data = ar2->buf[1];
|
||||
int hw_code, index;
|
||||
|
||||
if (data[0] > 4) {
|
||||
dev_err(&ar2->intf[1]->dev,
|
||||
"Unknown mode byte (%02x %02x %02x %02x)\n",
|
||||
data[3], data[2], data[1], data[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
hw_code = data[2];
|
||||
/*
|
||||
* Mode keys (AUX1-AUX4, PC) all generate the same code byte.
|
||||
* Use the mode byte to figure out which one was pressed.
|
||||
*/
|
||||
if (hw_code == 0x3f) {
|
||||
/*
|
||||
* For some incomprehensible reason the mouse pad generates
|
||||
* events which look identical to the events from the last
|
||||
* pressed mode key. Naturally we don't want to generate key
|
||||
* events for the mouse pad so we filter out any subsequent
|
||||
* events from the same mode key.
|
||||
*/
|
||||
if (ar2->mode == data[0])
|
||||
return;
|
||||
|
||||
if (data[1] == 0)
|
||||
ar2->mode = data[0];
|
||||
|
||||
hw_code |= data[0] << 8;
|
||||
}
|
||||
|
||||
if (!((1 << data[0]) & mode_mask))
|
||||
return;
|
||||
|
||||
index = ati_remote2_lookup(hw_code);
|
||||
if (index < 0) {
|
||||
dev_err(&ar2->intf[1]->dev,
|
||||
"Unknown code byte (%02x %02x %02x %02x)\n",
|
||||
data[3], data[2], data[1], data[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (data[1]) {
|
||||
case 0: /* release */
|
||||
break;
|
||||
case 1: /* press */
|
||||
ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_DELAY]);
|
||||
break;
|
||||
case 2: /* repeat */
|
||||
|
||||
/* No repeat for mouse buttons. */
|
||||
if (ati_remote2_key_table[index].key_code == BTN_LEFT ||
|
||||
ati_remote2_key_table[index].key_code == BTN_RIGHT)
|
||||
return;
|
||||
|
||||
if (!time_after_eq(jiffies, ar2->jiffies))
|
||||
return;
|
||||
|
||||
ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_PERIOD]);
|
||||
break;
|
||||
default:
|
||||
dev_err(&ar2->intf[1]->dev,
|
||||
"Unknown state byte (%02x %02x %02x %02x)\n",
|
||||
data[3], data[2], data[1], data[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
input_event(idev, EV_KEY, ati_remote2_key_table[index].key_code, data[1]);
|
||||
input_sync(idev);
|
||||
}
|
||||
|
||||
static void ati_remote2_complete_mouse(struct urb *urb)
|
||||
{
|
||||
struct ati_remote2 *ar2 = urb->context;
|
||||
int r;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
ati_remote2_input_mouse(ar2);
|
||||
break;
|
||||
case -ENOENT:
|
||||
case -EILSEQ:
|
||||
case -ECONNRESET:
|
||||
case -ESHUTDOWN:
|
||||
dev_dbg(&ar2->intf[0]->dev,
|
||||
"%s(): urb status = %d\n", __FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dev_err(&ar2->intf[0]->dev,
|
||||
"%s(): urb status = %d\n", __FUNCTION__, urb->status);
|
||||
}
|
||||
|
||||
r = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (r)
|
||||
dev_err(&ar2->intf[0]->dev,
|
||||
"%s(): usb_submit_urb() = %d\n", __FUNCTION__, r);
|
||||
}
|
||||
|
||||
static void ati_remote2_complete_key(struct urb *urb)
|
||||
{
|
||||
struct ati_remote2 *ar2 = urb->context;
|
||||
int r;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
ati_remote2_input_key(ar2);
|
||||
break;
|
||||
case -ENOENT:
|
||||
case -EILSEQ:
|
||||
case -ECONNRESET:
|
||||
case -ESHUTDOWN:
|
||||
dev_dbg(&ar2->intf[1]->dev,
|
||||
"%s(): urb status = %d\n", __FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dev_err(&ar2->intf[1]->dev,
|
||||
"%s(): urb status = %d\n", __FUNCTION__, urb->status);
|
||||
}
|
||||
|
||||
r = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (r)
|
||||
dev_err(&ar2->intf[1]->dev,
|
||||
"%s(): usb_submit_urb() = %d\n", __FUNCTION__, r);
|
||||
}
|
||||
|
||||
static int ati_remote2_input_init(struct ati_remote2 *ar2)
|
||||
{
|
||||
struct input_dev *idev;
|
||||
int i;
|
||||
|
||||
idev = input_allocate_device();
|
||||
if (!idev)
|
||||
return -ENOMEM;
|
||||
|
||||
ar2->idev = idev;
|
||||
idev->private = ar2;
|
||||
|
||||
idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_REL);
|
||||
idev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT);
|
||||
idev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
|
||||
for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++)
|
||||
set_bit(ati_remote2_key_table[i].key_code, idev->keybit);
|
||||
|
||||
idev->rep[REP_DELAY] = 250;
|
||||
idev->rep[REP_PERIOD] = 33;
|
||||
|
||||
idev->open = ati_remote2_open;
|
||||
idev->close = ati_remote2_close;
|
||||
|
||||
idev->name = ar2->name;
|
||||
idev->phys = ar2->phys;
|
||||
|
||||
usb_to_input_id(ar2->udev, &idev->id);
|
||||
idev->cdev.dev = &ar2->udev->dev;
|
||||
|
||||
i = input_register_device(idev);
|
||||
if (i)
|
||||
input_free_device(idev);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int ati_remote2_urb_init(struct ati_remote2 *ar2)
|
||||
{
|
||||
struct usb_device *udev = ar2->udev;
|
||||
int i, pipe, maxp;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
ar2->buf[i] = usb_buffer_alloc(udev, 4, GFP_KERNEL, &ar2->buf_dma[i]);
|
||||
if (!ar2->buf[i])
|
||||
return -ENOMEM;
|
||||
|
||||
ar2->urb[i] = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!ar2->urb[i])
|
||||
return -ENOMEM;
|
||||
|
||||
pipe = usb_rcvintpipe(udev, ar2->ep[i]->bEndpointAddress);
|
||||
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
|
||||
maxp = maxp > 4 ? 4 : maxp;
|
||||
|
||||
usb_fill_int_urb(ar2->urb[i], udev, pipe, ar2->buf[i], maxp,
|
||||
i ? ati_remote2_complete_key : ati_remote2_complete_mouse,
|
||||
ar2, ar2->ep[i]->bInterval);
|
||||
ar2->urb[i]->transfer_dma = ar2->buf_dma[i];
|
||||
ar2->urb[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ati_remote2_urb_cleanup(struct ati_remote2 *ar2)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
usb_free_urb(ar2->urb[i]);
|
||||
|
||||
if (ar2->buf[i])
|
||||
usb_buffer_free(ar2->udev, 4, ar2->buf[i], ar2->buf_dma[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(interface);
|
||||
struct usb_host_interface *alt = interface->cur_altsetting;
|
||||
struct ati_remote2 *ar2;
|
||||
int r;
|
||||
|
||||
if (alt->desc.bInterfaceNumber)
|
||||
return -ENODEV;
|
||||
|
||||
ar2 = kzalloc(sizeof (struct ati_remote2), GFP_KERNEL);
|
||||
if (!ar2)
|
||||
return -ENOMEM;
|
||||
|
||||
ar2->udev = udev;
|
||||
|
||||
ar2->intf[0] = interface;
|
||||
ar2->ep[0] = &alt->endpoint[0].desc;
|
||||
|
||||
ar2->intf[1] = usb_ifnum_to_if(udev, 1);
|
||||
r = usb_driver_claim_interface(&ati_remote2_driver, ar2->intf[1], ar2);
|
||||
if (r)
|
||||
goto fail1;
|
||||
alt = ar2->intf[1]->cur_altsetting;
|
||||
ar2->ep[1] = &alt->endpoint[0].desc;
|
||||
|
||||
r = ati_remote2_urb_init(ar2);
|
||||
if (r)
|
||||
goto fail2;
|
||||
|
||||
usb_make_path(udev, ar2->phys, sizeof(ar2->phys));
|
||||
strlcat(ar2->phys, "/input0", sizeof(ar2->phys));
|
||||
|
||||
strlcat(ar2->name, "ATI Remote Wonder II", sizeof(ar2->name));
|
||||
|
||||
r = ati_remote2_input_init(ar2);
|
||||
if (r)
|
||||
goto fail2;
|
||||
|
||||
usb_set_intfdata(interface, ar2);
|
||||
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
ati_remote2_urb_cleanup(ar2);
|
||||
|
||||
usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
|
||||
fail1:
|
||||
kfree(ar2);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void ati_remote2_disconnect(struct usb_interface *interface)
|
||||
{
|
||||
struct ati_remote2 *ar2;
|
||||
struct usb_host_interface *alt = interface->cur_altsetting;
|
||||
|
||||
if (alt->desc.bInterfaceNumber)
|
||||
return;
|
||||
|
||||
ar2 = usb_get_intfdata(interface);
|
||||
usb_set_intfdata(interface, NULL);
|
||||
|
||||
input_unregister_device(ar2->idev);
|
||||
|
||||
ati_remote2_urb_cleanup(ar2);
|
||||
|
||||
usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
|
||||
|
||||
kfree(ar2);
|
||||
}
|
||||
|
||||
static int __init ati_remote2_init(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = usb_register(&ati_remote2_driver);
|
||||
if (r)
|
||||
printk(KERN_ERR "ati_remote2: usb_register() = %d\n", r);
|
||||
else
|
||||
printk(KERN_INFO "ati_remote2: " DRIVER_DESC " " DRIVER_VERSION "\n");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void __exit ati_remote2_exit(void)
|
||||
{
|
||||
usb_deregister(&ati_remote2_driver);
|
||||
}
|
||||
|
||||
module_init(ati_remote2_init);
|
||||
module_exit(ati_remote2_exit);
|
||||
1104
drivers/usb/input/gtco.c
Normal file
1104
drivers/usb/input/gtco.c
Normal file
File diff suppressed because it is too large
Load Diff
1477
drivers/usb/input/hid-core.c
Normal file
1477
drivers/usb/input/hid-core.c
Normal file
File diff suppressed because it is too large
Load Diff
89
drivers/usb/input/hid-ff.c
Normal file
89
drivers/usb/input/hid-ff.c
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* $Id: hid-ff.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Force feedback support for hid devices.
|
||||
* Not all hid devices use the same protocol. For example, some use PID,
|
||||
* other use their own proprietary procotol.
|
||||
*
|
||||
* Copyright (c) 2002-2004 Johann Deneux
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Should you need to contact me, the author, you can do so by
|
||||
* e-mail - mail your message to <johann.deneux@it.uu.se>
|
||||
*/
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
#undef DEBUG
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <linux/hid.h>
|
||||
#include "usbhid.h"
|
||||
|
||||
/*
|
||||
* This table contains pointers to initializers. To add support for new
|
||||
* devices, you need to add the USB vendor and product ids here.
|
||||
*/
|
||||
struct hid_ff_initializer {
|
||||
u16 idVendor;
|
||||
u16 idProduct;
|
||||
int (*init)(struct hid_device*);
|
||||
};
|
||||
|
||||
/*
|
||||
* We try pidff when no other driver is found because PID is the
|
||||
* standards compliant way of implementing force feedback in HID.
|
||||
* pidff_init() will quickly abort if the device doesn't appear to
|
||||
* be a PID device
|
||||
*/
|
||||
static struct hid_ff_initializer inits[] = {
|
||||
#ifdef CONFIG_LOGITECH_FF
|
||||
{ 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */
|
||||
{ 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */
|
||||
{ 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */
|
||||
{ 0x46d, 0xc294, hid_lgff_init }, /* Logitech Formula Force EX */
|
||||
{ 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */
|
||||
{ 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */
|
||||
#endif
|
||||
#ifdef CONFIG_PANTHERLORD_FF
|
||||
{ 0x810, 0x0001, hid_plff_init },
|
||||
#endif
|
||||
#ifdef CONFIG_THRUSTMASTER_FF
|
||||
{ 0x44f, 0xb304, hid_tmff_init },
|
||||
#endif
|
||||
#ifdef CONFIG_ZEROPLUS_FF
|
||||
{ 0xc12, 0x0005, hid_zpff_init },
|
||||
{ 0xc12, 0x0030, hid_zpff_init },
|
||||
#endif
|
||||
{ 0, 0, hid_pidff_init} /* Matches anything */
|
||||
};
|
||||
|
||||
int hid_ff_init(struct hid_device* hid)
|
||||
{
|
||||
struct hid_ff_initializer *init;
|
||||
int vendor = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idVendor);
|
||||
int product = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idProduct);
|
||||
|
||||
for (init = inits; init->idVendor; init++)
|
||||
if (init->idVendor == vendor && init->idProduct == product)
|
||||
break;
|
||||
|
||||
return init->init(hid);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_ff_init);
|
||||
|
||||
150
drivers/usb/input/hid-lgff.c
Normal file
150
drivers/usb/input/hid-lgff.c
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Force feedback support for hid-compliant for some of the devices from
|
||||
* Logitech, namely:
|
||||
* - WingMan Cordless RumblePad
|
||||
* - WingMan Force 3D
|
||||
*
|
||||
* Copyright (c) 2002-2004 Johann Deneux
|
||||
* Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Should you need to contact me, the author, you can do so by
|
||||
* e-mail - mail your message to <johann.deneux@it.uu.se>
|
||||
*/
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/hid.h>
|
||||
#include "usbhid.h"
|
||||
|
||||
struct dev_type {
|
||||
u16 idVendor;
|
||||
u16 idProduct;
|
||||
const signed short *ff;
|
||||
};
|
||||
|
||||
static const signed short ff_rumble[] = {
|
||||
FF_RUMBLE,
|
||||
-1
|
||||
};
|
||||
|
||||
static const signed short ff_joystick[] = {
|
||||
FF_CONSTANT,
|
||||
-1
|
||||
};
|
||||
|
||||
static const struct dev_type devices[] = {
|
||||
{ 0x046d, 0xc211, ff_rumble },
|
||||
{ 0x046d, 0xc219, ff_rumble },
|
||||
{ 0x046d, 0xc283, ff_joystick },
|
||||
{ 0x046d, 0xc294, ff_joystick },
|
||||
{ 0x046d, 0xc295, ff_joystick },
|
||||
{ 0x046d, 0xca03, ff_joystick },
|
||||
};
|
||||
|
||||
static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
|
||||
int x, y;
|
||||
unsigned int left, right;
|
||||
|
||||
#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
|
||||
|
||||
switch (effect->type) {
|
||||
case FF_CONSTANT:
|
||||
x = effect->u.ramp.start_level + 0x7f; /* 0x7f is center */
|
||||
y = effect->u.ramp.end_level + 0x7f;
|
||||
CLAMP(x);
|
||||
CLAMP(y);
|
||||
report->field[0]->value[0] = 0x51;
|
||||
report->field[0]->value[1] = 0x08;
|
||||
report->field[0]->value[2] = x;
|
||||
report->field[0]->value[3] = y;
|
||||
dbg("(x, y)=(%04x, %04x)", x, y);
|
||||
usbhid_submit_report(hid, report, USB_DIR_OUT);
|
||||
break;
|
||||
|
||||
case FF_RUMBLE:
|
||||
right = effect->u.rumble.strong_magnitude;
|
||||
left = effect->u.rumble.weak_magnitude;
|
||||
right = right * 0xff / 0xffff;
|
||||
left = left * 0xff / 0xffff;
|
||||
CLAMP(left);
|
||||
CLAMP(right);
|
||||
report->field[0]->value[0] = 0x42;
|
||||
report->field[0]->value[1] = 0x00;
|
||||
report->field[0]->value[2] = left;
|
||||
report->field[0]->value[3] = right;
|
||||
dbg("(left, right)=(%04x, %04x)", left, right);
|
||||
usbhid_submit_report(hid, report, USB_DIR_OUT);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hid_lgff_init(struct hid_device* hid)
|
||||
{
|
||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
struct input_dev *dev = hidinput->input;
|
||||
struct hid_report *report;
|
||||
struct hid_field *field;
|
||||
const signed short *ff_bits = ff_joystick;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
/* Find the report to use */
|
||||
if (list_empty(report_list)) {
|
||||
err("No output report found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check that the report looks ok */
|
||||
report = list_entry(report_list->next, struct hid_report, list);
|
||||
if (!report) {
|
||||
err("NULL output report");
|
||||
return -1;
|
||||
}
|
||||
|
||||
field = report->field[0];
|
||||
if (!field) {
|
||||
err("NULL field");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(devices); i++) {
|
||||
if (dev->id.vendor == devices[i].idVendor &&
|
||||
dev->id.product == devices[i].idProduct) {
|
||||
ff_bits = devices[i].ff;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; ff_bits[i] >= 0; i++)
|
||||
set_bit(ff_bits[i], dev->ffbit);
|
||||
|
||||
error = input_ff_create_memless(dev, NULL, hid_lgff_play);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
1331
drivers/usb/input/hid-pidff.c
Normal file
1331
drivers/usb/input/hid-pidff.c
Normal file
File diff suppressed because it is too large
Load Diff
129
drivers/usb/input/hid-plff.c
Normal file
129
drivers/usb/input/hid-plff.c
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Force feedback support for PantherLord USB/PS2 2in1 Adapter devices
|
||||
*
|
||||
* Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
/* #define DEBUG */
|
||||
|
||||
#define debug(format, arg...) pr_debug("hid-plff: " format "\n" , ## arg)
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/hid.h>
|
||||
#include "usbhid.h"
|
||||
|
||||
struct plff_device {
|
||||
struct hid_report *report;
|
||||
};
|
||||
|
||||
static int hid_plff_play(struct input_dev *dev, void *data,
|
||||
struct ff_effect *effect)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct plff_device *plff = data;
|
||||
int left, right;
|
||||
|
||||
left = effect->u.rumble.strong_magnitude;
|
||||
right = effect->u.rumble.weak_magnitude;
|
||||
debug("called with 0x%04x 0x%04x", left, right);
|
||||
|
||||
left = left * 0x7f / 0xffff;
|
||||
right = right * 0x7f / 0xffff;
|
||||
|
||||
plff->report->field[0]->value[2] = left;
|
||||
plff->report->field[0]->value[3] = right;
|
||||
debug("running with 0x%02x 0x%02x", left, right);
|
||||
usbhid_submit_report(hid, plff->report, USB_DIR_OUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hid_plff_init(struct hid_device *hid)
|
||||
{
|
||||
struct plff_device *plff;
|
||||
struct hid_report *report;
|
||||
struct hid_input *hidinput;
|
||||
struct list_head *report_list =
|
||||
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
struct list_head *report_ptr = report_list;
|
||||
struct input_dev *dev;
|
||||
int error;
|
||||
|
||||
/* The device contains 2 output reports (one for each
|
||||
HID_QUIRK_MULTI_INPUT device), both containing 1 field, which
|
||||
contains 4 ff00.0002 usages and 4 16bit absolute values.
|
||||
|
||||
The 2 input reports also contain a field which contains
|
||||
8 ff00.0001 usages and 8 boolean values. Their meaning is
|
||||
currently unknown. */
|
||||
|
||||
if (list_empty(report_list)) {
|
||||
printk(KERN_ERR "hid-plff: no output reports found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
list_for_each_entry(hidinput, &hid->inputs, list) {
|
||||
|
||||
report_ptr = report_ptr->next;
|
||||
|
||||
if (report_ptr == report_list) {
|
||||
printk(KERN_ERR "hid-plff: required output report is missing\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
report = list_entry(report_ptr, struct hid_report, list);
|
||||
if (report->maxfield < 1) {
|
||||
printk(KERN_ERR "hid-plff: no fields in the report\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (report->field[0]->report_count < 4) {
|
||||
printk(KERN_ERR "hid-plff: not enough values in the field\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
plff = kzalloc(sizeof(struct plff_device), GFP_KERNEL);
|
||||
if (!plff)
|
||||
return -ENOMEM;
|
||||
|
||||
dev = hidinput->input;
|
||||
|
||||
set_bit(FF_RUMBLE, dev->ffbit);
|
||||
|
||||
error = input_ff_create_memless(dev, plff, hid_plff_play);
|
||||
if (error) {
|
||||
kfree(plff);
|
||||
return error;
|
||||
}
|
||||
|
||||
plff->report = report;
|
||||
plff->report->field[0]->value[0] = 0x00;
|
||||
plff->report->field[0]->value[1] = 0x00;
|
||||
plff->report->field[0]->value[2] = 0x00;
|
||||
plff->report->field[0]->value[3] = 0x00;
|
||||
usbhid_submit_report(hid, plff->report, USB_DIR_OUT);
|
||||
}
|
||||
|
||||
printk(KERN_INFO "hid-plff: Force feedback for PantherLord USB/PS2 "
|
||||
"2in1 Adapters by Anssi Hannula <anssi.hannula@gmail.com>\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
147
drivers/usb/input/hid-tmff.c
Normal file
147
drivers/usb/input/hid-tmff.c
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Force feedback support for various HID compliant devices by ThrustMaster:
|
||||
* ThrustMaster FireStorm Dual Power 2
|
||||
* and possibly others whose device ids haven't been added.
|
||||
*
|
||||
* Modified to support ThrustMaster devices by Zinx Verituse
|
||||
* on 2003-01-25 from the Logitech force feedback driver,
|
||||
* which is by Johann Deneux.
|
||||
*
|
||||
* Copyright (c) 2003 Zinx Verituse <zinx@epicsol.org>
|
||||
* Copyright (c) 2002 Johann Deneux
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
#undef DEBUG
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <linux/hid.h>
|
||||
#include "usbhid.h"
|
||||
|
||||
/* Usages for thrustmaster devices I know about */
|
||||
#define THRUSTMASTER_USAGE_RUMBLE_LR (HID_UP_GENDESK | 0xbb)
|
||||
|
||||
|
||||
struct tmff_device {
|
||||
struct hid_report *report;
|
||||
struct hid_field *rumble;
|
||||
};
|
||||
|
||||
/* Changes values from 0 to 0xffff into values from minimum to maximum */
|
||||
static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = (in * (maximum - minimum) / 0xffff) + minimum;
|
||||
if (ret < minimum)
|
||||
return minimum;
|
||||
if (ret > maximum)
|
||||
return maximum;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct tmff_device *tmff = data;
|
||||
int left, right; /* Rumbling */
|
||||
|
||||
left = hid_tmff_scale(effect->u.rumble.weak_magnitude,
|
||||
tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
|
||||
right = hid_tmff_scale(effect->u.rumble.strong_magnitude,
|
||||
tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
|
||||
|
||||
tmff->rumble->value[0] = left;
|
||||
tmff->rumble->value[1] = right;
|
||||
dbg("(left,right)=(%08x, %08x)", left, right);
|
||||
usbhid_submit_report(hid, tmff->report, USB_DIR_OUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hid_tmff_init(struct hid_device *hid)
|
||||
{
|
||||
struct tmff_device *tmff;
|
||||
struct list_head *pos;
|
||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||
struct input_dev *input_dev = hidinput->input;
|
||||
int error;
|
||||
|
||||
tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
|
||||
if (!tmff)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Find the report to use */
|
||||
__list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) {
|
||||
struct hid_report *report = (struct hid_report *)pos;
|
||||
int fieldnum;
|
||||
|
||||
for (fieldnum = 0; fieldnum < report->maxfield; ++fieldnum) {
|
||||
struct hid_field *field = report->field[fieldnum];
|
||||
|
||||
if (field->maxusage <= 0)
|
||||
continue;
|
||||
|
||||
switch (field->usage[0].hid) {
|
||||
case THRUSTMASTER_USAGE_RUMBLE_LR:
|
||||
if (field->report_count < 2) {
|
||||
warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with report_count < 2");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (field->logical_maximum == field->logical_minimum) {
|
||||
warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with logical_maximum == logical_minimum");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tmff->report && tmff->report != report) {
|
||||
warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR in other report");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tmff->rumble && tmff->rumble != field) {
|
||||
warn("ignoring duplicate THRUSTMASTER_USAGE_RUMBLE_LR");
|
||||
continue;
|
||||
}
|
||||
|
||||
tmff->report = report;
|
||||
tmff->rumble = field;
|
||||
|
||||
set_bit(FF_RUMBLE, input_dev->ffbit);
|
||||
break;
|
||||
|
||||
default:
|
||||
warn("ignoring unknown output usage %08x", field->usage[0].hid);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error = input_ff_create_memless(input_dev, tmff, hid_tmff_play);
|
||||
if (error) {
|
||||
kfree(tmff);
|
||||
return error;
|
||||
}
|
||||
|
||||
info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse <zinx@epicsol.org>");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
111
drivers/usb/input/hid-zpff.c
Normal file
111
drivers/usb/input/hid-zpff.c
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Force feedback support for Zeroplus based devices
|
||||
*
|
||||
* Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@gmail.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
/* #define DEBUG */
|
||||
|
||||
#define debug(format, arg...) pr_debug("hid-zpff: " format "\n" , ## arg)
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/hid.h>
|
||||
#include "usbhid.h"
|
||||
|
||||
struct zpff_device {
|
||||
struct hid_report *report;
|
||||
};
|
||||
|
||||
static int hid_zpff_play(struct input_dev *dev, void *data,
|
||||
struct ff_effect *effect)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct zpff_device *zpff = data;
|
||||
int left, right;
|
||||
|
||||
/*
|
||||
* The following is specified the other way around in the Zeroplus
|
||||
* datasheet but the order below is correct for the XFX Executioner;
|
||||
* however it is possible that the XFX Executioner is an exception
|
||||
*/
|
||||
|
||||
left = effect->u.rumble.strong_magnitude;
|
||||
right = effect->u.rumble.weak_magnitude;
|
||||
debug("called with 0x%04x 0x%04x", left, right);
|
||||
|
||||
left = left * 0x7f / 0xffff;
|
||||
right = right * 0x7f / 0xffff;
|
||||
|
||||
zpff->report->field[2]->value[0] = left;
|
||||
zpff->report->field[3]->value[0] = right;
|
||||
debug("running with 0x%02x 0x%02x", left, right);
|
||||
usbhid_submit_report(hid, zpff->report, USB_DIR_OUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hid_zpff_init(struct hid_device *hid)
|
||||
{
|
||||
struct zpff_device *zpff;
|
||||
struct hid_report *report;
|
||||
struct hid_input *hidinput = list_entry(hid->inputs.next,
|
||||
struct hid_input, list);
|
||||
struct list_head *report_list =
|
||||
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
struct input_dev *dev = hidinput->input;
|
||||
int error;
|
||||
|
||||
if (list_empty(report_list)) {
|
||||
printk(KERN_ERR "hid-zpff: no output report found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
report = list_entry(report_list->next, struct hid_report, list);
|
||||
|
||||
if (report->maxfield < 4) {
|
||||
printk(KERN_ERR "hid-zpff: not enough fields in report\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL);
|
||||
if (!zpff)
|
||||
return -ENOMEM;
|
||||
|
||||
set_bit(FF_RUMBLE, dev->ffbit);
|
||||
|
||||
error = input_ff_create_memless(dev, zpff, hid_zpff_play);
|
||||
if (error) {
|
||||
kfree(zpff);
|
||||
return error;
|
||||
}
|
||||
|
||||
zpff->report = report;
|
||||
zpff->report->field[0]->value[0] = 0x00;
|
||||
zpff->report->field[1]->value[0] = 0x02;
|
||||
zpff->report->field[2]->value[0] = 0x00;
|
||||
zpff->report->field[3]->value[0] = 0x00;
|
||||
usbhid_submit_report(hid, zpff->report, USB_DIR_OUT);
|
||||
|
||||
printk(KERN_INFO "Force feedback for Zeroplus based devices by "
|
||||
"Anssi Hannula <anssi.hannula@gmail.com>\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
861
drivers/usb/input/hiddev.c
Normal file
861
drivers/usb/input/hiddev.c
Normal file
@@ -0,0 +1,861 @@
|
||||
/*
|
||||
* Copyright (c) 2001 Paul Stewart
|
||||
* Copyright (c) 2001 Vojtech Pavlik
|
||||
*
|
||||
* HID char devices, giving access to raw HID device events.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Should you need to contact me, the author, you can do so either by
|
||||
* e-mail - mail your message to Paul Stewart <stewart@wetlogic.net>
|
||||
*/
|
||||
|
||||
#include <linux/poll.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/hiddev.h>
|
||||
#include "usbhid.h"
|
||||
|
||||
#ifdef CONFIG_USB_DYNAMIC_MINORS
|
||||
#define HIDDEV_MINOR_BASE 0
|
||||
#define HIDDEV_MINORS 256
|
||||
#else
|
||||
#define HIDDEV_MINOR_BASE 96
|
||||
#define HIDDEV_MINORS 16
|
||||
#endif
|
||||
#define HIDDEV_BUFFER_SIZE 64
|
||||
|
||||
struct hiddev {
|
||||
int exist;
|
||||
int open;
|
||||
wait_queue_head_t wait;
|
||||
struct hid_device *hid;
|
||||
struct list_head list;
|
||||
spinlock_t list_lock;
|
||||
};
|
||||
|
||||
struct hiddev_list {
|
||||
struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE];
|
||||
int head;
|
||||
int tail;
|
||||
unsigned flags;
|
||||
struct fasync_struct *fasync;
|
||||
struct hiddev *hiddev;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
static struct hiddev *hiddev_table[HIDDEV_MINORS];
|
||||
|
||||
/*
|
||||
* Find a report, given the report's type and ID. The ID can be specified
|
||||
* indirectly by REPORT_ID_FIRST (which returns the first report of the given
|
||||
* type) or by (REPORT_ID_NEXT | old_id), which returns the next report of the
|
||||
* given type which follows old_id.
|
||||
*/
|
||||
static struct hid_report *
|
||||
hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo)
|
||||
{
|
||||
unsigned int flags = rinfo->report_id & ~HID_REPORT_ID_MASK;
|
||||
unsigned int rid = rinfo->report_id & HID_REPORT_ID_MASK;
|
||||
struct hid_report_enum *report_enum;
|
||||
struct hid_report *report;
|
||||
struct list_head *list;
|
||||
|
||||
if (rinfo->report_type < HID_REPORT_TYPE_MIN ||
|
||||
rinfo->report_type > HID_REPORT_TYPE_MAX)
|
||||
return NULL;
|
||||
|
||||
report_enum = hid->report_enum +
|
||||
(rinfo->report_type - HID_REPORT_TYPE_MIN);
|
||||
|
||||
switch (flags) {
|
||||
case 0: /* Nothing to do -- report_id is already set correctly */
|
||||
break;
|
||||
|
||||
case HID_REPORT_ID_FIRST:
|
||||
if (list_empty(&report_enum->report_list))
|
||||
return NULL;
|
||||
|
||||
list = report_enum->report_list.next;
|
||||
report = list_entry(list, struct hid_report, list);
|
||||
rinfo->report_id = report->id;
|
||||
break;
|
||||
|
||||
case HID_REPORT_ID_NEXT:
|
||||
report = report_enum->report_id_hash[rid];
|
||||
if (!report)
|
||||
return NULL;
|
||||
|
||||
list = report->list.next;
|
||||
if (list == &report_enum->report_list)
|
||||
return NULL;
|
||||
|
||||
report = list_entry(list, struct hid_report, list);
|
||||
rinfo->report_id = report->id;
|
||||
break;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return report_enum->report_id_hash[rinfo->report_id];
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform an exhaustive search of the report table for a usage, given its
|
||||
* type and usage id.
|
||||
*/
|
||||
static struct hid_field *
|
||||
hiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref)
|
||||
{
|
||||
int i, j;
|
||||
struct hid_report *report;
|
||||
struct hid_report_enum *report_enum;
|
||||
struct hid_field *field;
|
||||
|
||||
if (uref->report_type < HID_REPORT_TYPE_MIN ||
|
||||
uref->report_type > HID_REPORT_TYPE_MAX)
|
||||
return NULL;
|
||||
|
||||
report_enum = hid->report_enum +
|
||||
(uref->report_type - HID_REPORT_TYPE_MIN);
|
||||
|
||||
list_for_each_entry(report, &report_enum->report_list, list) {
|
||||
for (i = 0; i < report->maxfield; i++) {
|
||||
field = report->field[i];
|
||||
for (j = 0; j < field->maxusage; j++) {
|
||||
if (field->usage[j].hid == uref->usage_code) {
|
||||
uref->report_id = report->id;
|
||||
uref->field_index = i;
|
||||
uref->usage_index = j;
|
||||
return field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void hiddev_send_event(struct hid_device *hid,
|
||||
struct hiddev_usage_ref *uref)
|
||||
{
|
||||
struct hiddev *hiddev = hid->hiddev;
|
||||
struct hiddev_list *list;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hiddev->list_lock, flags);
|
||||
list_for_each_entry(list, &hiddev->list, node) {
|
||||
if (uref->field_index != HID_FIELD_INDEX_NONE ||
|
||||
(list->flags & HIDDEV_FLAG_REPORT) != 0) {
|
||||
list->buffer[list->head] = *uref;
|
||||
list->head = (list->head + 1) &
|
||||
(HIDDEV_BUFFER_SIZE - 1);
|
||||
kill_fasync(&list->fasync, SIGIO, POLL_IN);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&hiddev->list_lock, flags);
|
||||
|
||||
wake_up_interruptible(&hiddev->wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is where hid.c calls into hiddev to pass an event that occurred over
|
||||
* the interrupt pipe
|
||||
*/
|
||||
void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
unsigned type = field->report_type;
|
||||
struct hiddev_usage_ref uref;
|
||||
|
||||
uref.report_type =
|
||||
(type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT :
|
||||
((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT :
|
||||
((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0));
|
||||
uref.report_id = field->report->id;
|
||||
uref.field_index = field->index;
|
||||
uref.usage_index = (usage - field->usage);
|
||||
uref.usage_code = usage->hid;
|
||||
uref.value = value;
|
||||
|
||||
hiddev_send_event(hid, &uref);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hiddev_hid_event);
|
||||
|
||||
void hiddev_report_event(struct hid_device *hid, struct hid_report *report)
|
||||
{
|
||||
unsigned type = report->type;
|
||||
struct hiddev_usage_ref uref;
|
||||
|
||||
memset(&uref, 0, sizeof(uref));
|
||||
uref.report_type =
|
||||
(type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT :
|
||||
((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT :
|
||||
((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0));
|
||||
uref.report_id = report->id;
|
||||
uref.field_index = HID_FIELD_INDEX_NONE;
|
||||
|
||||
hiddev_send_event(hid, &uref);
|
||||
}
|
||||
|
||||
/*
|
||||
* fasync file op
|
||||
*/
|
||||
static int hiddev_fasync(int fd, struct file *file, int on)
|
||||
{
|
||||
int retval;
|
||||
struct hiddev_list *list = file->private_data;
|
||||
|
||||
retval = fasync_helper(fd, file, on, &list->fasync);
|
||||
|
||||
return retval < 0 ? retval : 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* release file op
|
||||
*/
|
||||
static int hiddev_release(struct inode * inode, struct file * file)
|
||||
{
|
||||
struct hiddev_list *list = file->private_data;
|
||||
unsigned long flags;
|
||||
|
||||
hiddev_fasync(-1, file, 0);
|
||||
|
||||
spin_lock_irqsave(&list->hiddev->list_lock, flags);
|
||||
list_del(&list->node);
|
||||
spin_unlock_irqrestore(&list->hiddev->list_lock, flags);
|
||||
|
||||
if (!--list->hiddev->open) {
|
||||
if (list->hiddev->exist)
|
||||
usbhid_close(list->hiddev->hid);
|
||||
else
|
||||
kfree(list->hiddev);
|
||||
}
|
||||
|
||||
kfree(list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* open file op
|
||||
*/
|
||||
static int hiddev_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct hiddev_list *list;
|
||||
unsigned long flags;
|
||||
|
||||
int i = iminor(inode) - HIDDEV_MINOR_BASE;
|
||||
|
||||
if (i >= HIDDEV_MINORS || !hiddev_table[i])
|
||||
return -ENODEV;
|
||||
|
||||
if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
|
||||
list->hiddev = hiddev_table[i];
|
||||
|
||||
spin_lock_irqsave(&list->hiddev->list_lock, flags);
|
||||
list_add_tail(&list->node, &hiddev_table[i]->list);
|
||||
spin_unlock_irqrestore(&list->hiddev->list_lock, flags);
|
||||
|
||||
file->private_data = list;
|
||||
|
||||
if (!list->hiddev->open++)
|
||||
if (list->hiddev->exist)
|
||||
usbhid_open(hiddev_table[i]->hid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* "write" file op
|
||||
*/
|
||||
static ssize_t hiddev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* "read" file op
|
||||
*/
|
||||
static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
struct hiddev_list *list = file->private_data;
|
||||
int event_size;
|
||||
int retval = 0;
|
||||
|
||||
event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ?
|
||||
sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event);
|
||||
|
||||
if (count < event_size)
|
||||
return 0;
|
||||
|
||||
while (retval == 0) {
|
||||
if (list->head == list->tail) {
|
||||
add_wait_queue(&list->hiddev->wait, &wait);
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
while (list->head == list->tail) {
|
||||
if (file->f_flags & O_NONBLOCK) {
|
||||
retval = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
if (signal_pending(current)) {
|
||||
retval = -ERESTARTSYS;
|
||||
break;
|
||||
}
|
||||
if (!list->hiddev->exist) {
|
||||
retval = -EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
schedule();
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
}
|
||||
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&list->hiddev->wait, &wait);
|
||||
}
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
|
||||
while (list->head != list->tail &&
|
||||
retval + event_size <= count) {
|
||||
if ((list->flags & HIDDEV_FLAG_UREF) == 0) {
|
||||
if (list->buffer[list->tail].field_index !=
|
||||
HID_FIELD_INDEX_NONE) {
|
||||
struct hiddev_event event;
|
||||
event.hid = list->buffer[list->tail].usage_code;
|
||||
event.value = list->buffer[list->tail].value;
|
||||
if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event)))
|
||||
return -EFAULT;
|
||||
retval += sizeof(struct hiddev_event);
|
||||
}
|
||||
} else {
|
||||
if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE ||
|
||||
(list->flags & HIDDEV_FLAG_REPORT) != 0) {
|
||||
if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref)))
|
||||
return -EFAULT;
|
||||
retval += sizeof(struct hiddev_usage_ref);
|
||||
}
|
||||
}
|
||||
list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* "poll" file op
|
||||
* No kernel lock - fine
|
||||
*/
|
||||
static unsigned int hiddev_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct hiddev_list *list = file->private_data;
|
||||
|
||||
poll_wait(file, &list->hiddev->wait, wait);
|
||||
if (list->head != list->tail)
|
||||
return POLLIN | POLLRDNORM;
|
||||
if (!list->hiddev->exist)
|
||||
return POLLERR | POLLHUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* "ioctl" file op
|
||||
*/
|
||||
static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct hiddev_list *list = file->private_data;
|
||||
struct hiddev *hiddev = list->hiddev;
|
||||
struct hid_device *hid = hiddev->hid;
|
||||
struct usb_device *dev = hid_to_usb_dev(hid);
|
||||
struct hiddev_collection_info cinfo;
|
||||
struct hiddev_report_info rinfo;
|
||||
struct hiddev_field_info finfo;
|
||||
struct hiddev_usage_ref_multi *uref_multi = NULL;
|
||||
struct hiddev_usage_ref *uref;
|
||||
struct hiddev_devinfo dinfo;
|
||||
struct hid_report *report;
|
||||
struct hid_field *field;
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
void __user *user_arg = (void __user *)arg;
|
||||
int i;
|
||||
|
||||
if (!hiddev->exist)
|
||||
return -EIO;
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case HIDIOCGVERSION:
|
||||
return put_user(HID_VERSION, (int __user *)arg);
|
||||
|
||||
case HIDIOCAPPLICATION:
|
||||
if (arg < 0 || arg >= hid->maxapplication)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < hid->maxcollection; i++)
|
||||
if (hid->collection[i].type ==
|
||||
HID_COLLECTION_APPLICATION && arg-- == 0)
|
||||
break;
|
||||
|
||||
if (i == hid->maxcollection)
|
||||
return -EINVAL;
|
||||
|
||||
return hid->collection[i].usage;
|
||||
|
||||
case HIDIOCGDEVINFO:
|
||||
dinfo.bustype = BUS_USB;
|
||||
dinfo.busnum = dev->bus->busnum;
|
||||
dinfo.devnum = dev->devnum;
|
||||
dinfo.ifnum = usbhid->ifnum;
|
||||
dinfo.vendor = le16_to_cpu(dev->descriptor.idVendor);
|
||||
dinfo.product = le16_to_cpu(dev->descriptor.idProduct);
|
||||
dinfo.version = le16_to_cpu(dev->descriptor.bcdDevice);
|
||||
dinfo.num_applications = hid->maxapplication;
|
||||
if (copy_to_user(user_arg, &dinfo, sizeof(dinfo)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
|
||||
case HIDIOCGFLAG:
|
||||
if (put_user(list->flags, (int __user *)arg))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
|
||||
case HIDIOCSFLAG:
|
||||
{
|
||||
int newflags;
|
||||
if (get_user(newflags, (int __user *)arg))
|
||||
return -EFAULT;
|
||||
|
||||
if ((newflags & ~HIDDEV_FLAGS) != 0 ||
|
||||
((newflags & HIDDEV_FLAG_REPORT) != 0 &&
|
||||
(newflags & HIDDEV_FLAG_UREF) == 0))
|
||||
return -EINVAL;
|
||||
|
||||
list->flags = newflags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case HIDIOCGSTRING:
|
||||
{
|
||||
int idx, len;
|
||||
char *buf;
|
||||
|
||||
if (get_user(idx, (int __user *)arg))
|
||||
return -EFAULT;
|
||||
|
||||
if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) {
|
||||
kfree(buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (copy_to_user(user_arg+sizeof(int), buf, len+1)) {
|
||||
kfree(buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
case HIDIOCINITREPORT:
|
||||
usbhid_init_reports(hid);
|
||||
|
||||
return 0;
|
||||
|
||||
case HIDIOCGREPORT:
|
||||
if (copy_from_user(&rinfo, user_arg, sizeof(rinfo)))
|
||||
return -EFAULT;
|
||||
|
||||
if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT)
|
||||
return -EINVAL;
|
||||
|
||||
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
usbhid_submit_report(hid, report, USB_DIR_IN);
|
||||
usbhid_wait_io(hid);
|
||||
|
||||
return 0;
|
||||
|
||||
case HIDIOCSREPORT:
|
||||
if (copy_from_user(&rinfo, user_arg, sizeof(rinfo)))
|
||||
return -EFAULT;
|
||||
|
||||
if (rinfo.report_type == HID_REPORT_TYPE_INPUT)
|
||||
return -EINVAL;
|
||||
|
||||
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
usbhid_submit_report(hid, report, USB_DIR_OUT);
|
||||
usbhid_wait_io(hid);
|
||||
|
||||
return 0;
|
||||
|
||||
case HIDIOCGREPORTINFO:
|
||||
if (copy_from_user(&rinfo, user_arg, sizeof(rinfo)))
|
||||
return -EFAULT;
|
||||
|
||||
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
rinfo.num_fields = report->maxfield;
|
||||
|
||||
if (copy_to_user(user_arg, &rinfo, sizeof(rinfo)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
|
||||
case HIDIOCGFIELDINFO:
|
||||
if (copy_from_user(&finfo, user_arg, sizeof(finfo)))
|
||||
return -EFAULT;
|
||||
rinfo.report_type = finfo.report_type;
|
||||
rinfo.report_id = finfo.report_id;
|
||||
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (finfo.field_index >= report->maxfield)
|
||||
return -EINVAL;
|
||||
|
||||
field = report->field[finfo.field_index];
|
||||
memset(&finfo, 0, sizeof(finfo));
|
||||
finfo.report_type = rinfo.report_type;
|
||||
finfo.report_id = rinfo.report_id;
|
||||
finfo.field_index = field->report_count - 1;
|
||||
finfo.maxusage = field->maxusage;
|
||||
finfo.flags = field->flags;
|
||||
finfo.physical = field->physical;
|
||||
finfo.logical = field->logical;
|
||||
finfo.application = field->application;
|
||||
finfo.logical_minimum = field->logical_minimum;
|
||||
finfo.logical_maximum = field->logical_maximum;
|
||||
finfo.physical_minimum = field->physical_minimum;
|
||||
finfo.physical_maximum = field->physical_maximum;
|
||||
finfo.unit_exponent = field->unit_exponent;
|
||||
finfo.unit = field->unit;
|
||||
|
||||
if (copy_to_user(user_arg, &finfo, sizeof(finfo)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
|
||||
case HIDIOCGUCODE:
|
||||
uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
|
||||
if (!uref_multi)
|
||||
return -ENOMEM;
|
||||
uref = &uref_multi->uref;
|
||||
if (copy_from_user(uref, user_arg, sizeof(*uref)))
|
||||
goto fault;
|
||||
|
||||
rinfo.report_type = uref->report_type;
|
||||
rinfo.report_id = uref->report_id;
|
||||
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
|
||||
goto inval;
|
||||
|
||||
if (uref->field_index >= report->maxfield)
|
||||
goto inval;
|
||||
|
||||
field = report->field[uref->field_index];
|
||||
if (uref->usage_index >= field->maxusage)
|
||||
goto inval;
|
||||
|
||||
uref->usage_code = field->usage[uref->usage_index].hid;
|
||||
|
||||
if (copy_to_user(user_arg, uref, sizeof(*uref)))
|
||||
goto fault;
|
||||
|
||||
kfree(uref_multi);
|
||||
return 0;
|
||||
|
||||
case HIDIOCGUSAGE:
|
||||
case HIDIOCSUSAGE:
|
||||
case HIDIOCGUSAGES:
|
||||
case HIDIOCSUSAGES:
|
||||
case HIDIOCGCOLLECTIONINDEX:
|
||||
uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
|
||||
if (!uref_multi)
|
||||
return -ENOMEM;
|
||||
uref = &uref_multi->uref;
|
||||
if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
|
||||
if (copy_from_user(uref_multi, user_arg,
|
||||
sizeof(*uref_multi)))
|
||||
goto fault;
|
||||
} else {
|
||||
if (copy_from_user(uref, user_arg, sizeof(*uref)))
|
||||
goto fault;
|
||||
}
|
||||
|
||||
if (cmd != HIDIOCGUSAGE &&
|
||||
cmd != HIDIOCGUSAGES &&
|
||||
uref->report_type == HID_REPORT_TYPE_INPUT)
|
||||
goto inval;
|
||||
|
||||
if (uref->report_id == HID_REPORT_ID_UNKNOWN) {
|
||||
field = hiddev_lookup_usage(hid, uref);
|
||||
if (field == NULL)
|
||||
goto inval;
|
||||
} else {
|
||||
rinfo.report_type = uref->report_type;
|
||||
rinfo.report_id = uref->report_id;
|
||||
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
|
||||
goto inval;
|
||||
|
||||
if (uref->field_index >= report->maxfield)
|
||||
goto inval;
|
||||
|
||||
field = report->field[uref->field_index];
|
||||
|
||||
if (cmd == HIDIOCGCOLLECTIONINDEX) {
|
||||
if (uref->usage_index >= field->maxusage)
|
||||
goto inval;
|
||||
} else if (uref->usage_index >= field->report_count)
|
||||
goto inval;
|
||||
|
||||
else if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) &&
|
||||
(uref_multi->num_values > HID_MAX_MULTI_USAGES ||
|
||||
uref->usage_index + uref_multi->num_values > field->report_count))
|
||||
goto inval;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case HIDIOCGUSAGE:
|
||||
uref->value = field->value[uref->usage_index];
|
||||
if (copy_to_user(user_arg, uref, sizeof(*uref)))
|
||||
goto fault;
|
||||
goto goodreturn;
|
||||
|
||||
case HIDIOCSUSAGE:
|
||||
field->value[uref->usage_index] = uref->value;
|
||||
goto goodreturn;
|
||||
|
||||
case HIDIOCGCOLLECTIONINDEX:
|
||||
kfree(uref_multi);
|
||||
return field->usage[uref->usage_index].collection_index;
|
||||
case HIDIOCGUSAGES:
|
||||
for (i = 0; i < uref_multi->num_values; i++)
|
||||
uref_multi->values[i] =
|
||||
field->value[uref->usage_index + i];
|
||||
if (copy_to_user(user_arg, uref_multi,
|
||||
sizeof(*uref_multi)))
|
||||
goto fault;
|
||||
goto goodreturn;
|
||||
case HIDIOCSUSAGES:
|
||||
for (i = 0; i < uref_multi->num_values; i++)
|
||||
field->value[uref->usage_index + i] =
|
||||
uref_multi->values[i];
|
||||
goto goodreturn;
|
||||
}
|
||||
|
||||
goodreturn:
|
||||
kfree(uref_multi);
|
||||
return 0;
|
||||
fault:
|
||||
kfree(uref_multi);
|
||||
return -EFAULT;
|
||||
inval:
|
||||
kfree(uref_multi);
|
||||
return -EINVAL;
|
||||
|
||||
case HIDIOCGCOLLECTIONINFO:
|
||||
if (copy_from_user(&cinfo, user_arg, sizeof(cinfo)))
|
||||
return -EFAULT;
|
||||
|
||||
if (cinfo.index >= hid->maxcollection)
|
||||
return -EINVAL;
|
||||
|
||||
cinfo.type = hid->collection[cinfo.index].type;
|
||||
cinfo.usage = hid->collection[cinfo.index].usage;
|
||||
cinfo.level = hid->collection[cinfo.index].level;
|
||||
|
||||
if (copy_to_user(user_arg, &cinfo, sizeof(cinfo)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
|
||||
if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ)
|
||||
return -EINVAL;
|
||||
|
||||
if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) {
|
||||
int len;
|
||||
if (!hid->name)
|
||||
return 0;
|
||||
len = strlen(hid->name) + 1;
|
||||
if (len > _IOC_SIZE(cmd))
|
||||
len = _IOC_SIZE(cmd);
|
||||
return copy_to_user(user_arg, hid->name, len) ?
|
||||
-EFAULT : len;
|
||||
}
|
||||
|
||||
if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGPHYS(0))) {
|
||||
int len;
|
||||
if (!hid->phys)
|
||||
return 0;
|
||||
len = strlen(hid->phys) + 1;
|
||||
if (len > _IOC_SIZE(cmd))
|
||||
len = _IOC_SIZE(cmd);
|
||||
return copy_to_user(user_arg, hid->phys, len) ?
|
||||
-EFAULT : len;
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct file_operations hiddev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = hiddev_read,
|
||||
.write = hiddev_write,
|
||||
.poll = hiddev_poll,
|
||||
.open = hiddev_open,
|
||||
.release = hiddev_release,
|
||||
.ioctl = hiddev_ioctl,
|
||||
.fasync = hiddev_fasync,
|
||||
};
|
||||
|
||||
static struct usb_class_driver hiddev_class = {
|
||||
.name = "hiddev%d",
|
||||
.fops = &hiddev_fops,
|
||||
.minor_base = HIDDEV_MINOR_BASE,
|
||||
};
|
||||
|
||||
/*
|
||||
* This is where hid.c calls us to connect a hid device to the hiddev driver
|
||||
*/
|
||||
int hiddev_connect(struct hid_device *hid)
|
||||
{
|
||||
struct hiddev *hiddev;
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
int i;
|
||||
int retval;
|
||||
|
||||
for (i = 0; i < hid->maxcollection; i++)
|
||||
if (hid->collection[i].type ==
|
||||
HID_COLLECTION_APPLICATION &&
|
||||
!IS_INPUT_APPLICATION(hid->collection[i].usage))
|
||||
break;
|
||||
|
||||
if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDDEV) == 0)
|
||||
return -1;
|
||||
|
||||
if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL)))
|
||||
return -1;
|
||||
|
||||
retval = usb_register_dev(usbhid->intf, &hiddev_class);
|
||||
if (retval) {
|
||||
err("Not able to get a minor for this device.");
|
||||
kfree(hiddev);
|
||||
return -1;
|
||||
}
|
||||
|
||||
init_waitqueue_head(&hiddev->wait);
|
||||
INIT_LIST_HEAD(&hiddev->list);
|
||||
spin_lock_init(&hiddev->list_lock);
|
||||
hiddev->hid = hid;
|
||||
hiddev->exist = 1;
|
||||
|
||||
hid->minor = usbhid->intf->minor;
|
||||
hid->hiddev = hiddev;
|
||||
|
||||
hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is where hid.c calls us to disconnect a hiddev device from the
|
||||
* corresponding hid device (usually because the usb device has disconnected)
|
||||
*/
|
||||
static struct usb_class_driver hiddev_class;
|
||||
void hiddev_disconnect(struct hid_device *hid)
|
||||
{
|
||||
struct hiddev *hiddev = hid->hiddev;
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
|
||||
hiddev->exist = 0;
|
||||
|
||||
hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL;
|
||||
usb_deregister_dev(usbhid->intf, &hiddev_class);
|
||||
|
||||
if (hiddev->open) {
|
||||
usbhid_close(hiddev->hid);
|
||||
wake_up_interruptible(&hiddev->wait);
|
||||
} else {
|
||||
kfree(hiddev);
|
||||
}
|
||||
}
|
||||
|
||||
/* Currently this driver is a USB driver. It's not a conventional one in
|
||||
* the sense that it doesn't probe at the USB level. Instead it waits to
|
||||
* be connected by HID through the hiddev_connect / hiddev_disconnect
|
||||
* routines. The reason to register as a USB device is to gain part of the
|
||||
* minor number space from the USB major.
|
||||
*
|
||||
* In theory, should the HID code be generalized to more than one physical
|
||||
* medium (say, IEEE 1384), this driver will probably need to register its
|
||||
* own major number, and in doing so, no longer need to register with USB.
|
||||
* At that point the probe routine and hiddev_driver struct below will no
|
||||
* longer be useful.
|
||||
*/
|
||||
|
||||
|
||||
/* We never attach in this manner, and rely on HID to connect us. This
|
||||
* is why there is no disconnect routine defined in the usb_driver either.
|
||||
*/
|
||||
static int hiddev_usbd_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *hiddev_info)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
static /* const */ struct usb_driver hiddev_driver = {
|
||||
.name = "hiddev",
|
||||
.probe = hiddev_usbd_probe,
|
||||
};
|
||||
|
||||
int __init hiddev_init(void)
|
||||
{
|
||||
return usb_register(&hiddev_driver);
|
||||
}
|
||||
|
||||
void hiddev_exit(void)
|
||||
{
|
||||
usb_deregister(&hiddev_driver);
|
||||
}
|
||||
271
drivers/usb/input/itmtouch.c
Normal file
271
drivers/usb/input/itmtouch.c
Normal file
@@ -0,0 +1,271 @@
|
||||
/******************************************************************************
|
||||
* itmtouch.c -- Driver for ITM touchscreen panel
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* Based upon original work by Chris Collins <xfire-itmtouch@xware.cx>.
|
||||
*
|
||||
* Kudos to ITM for providing me with the datasheet for the panel,
|
||||
* even though it was a day later than I had finished writing this
|
||||
* driver.
|
||||
*
|
||||
* It has meant that I've been able to correct my interpretation of the
|
||||
* protocol packets however.
|
||||
*
|
||||
* CC -- 2003/9/29
|
||||
*
|
||||
* History
|
||||
* 1.0 & 1.1 2003 (CC) vojtech@suse.cz
|
||||
* Original version for 2.4.x kernels
|
||||
*
|
||||
* 1.2 02/03/2005 (HCE) hc@mivu.no
|
||||
* Complete rewrite to support Linux 2.6.10, thanks to mtouchusb.c for hints.
|
||||
* Unfortunately no calibration support at this time.
|
||||
*
|
||||
* 1.2.1 09/03/2005 (HCE) hc@mivu.no
|
||||
* Code cleanup and adjusting syntax to start matching kernel standards
|
||||
*
|
||||
* 1.2.2 10/05/2006 (MJA) massad@gmail.com
|
||||
* Flag for detecting if the screen was being touch was incorrectly
|
||||
* inverted, so no touch events were being detected.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
/* only an 8 byte buffer necessary for a single packet */
|
||||
#define ITM_BUFSIZE 8
|
||||
#define PATH_SIZE 64
|
||||
|
||||
#define USB_VENDOR_ID_ITMINC 0x0403
|
||||
#define USB_PRODUCT_ID_TOUCHPANEL 0xf9e9
|
||||
|
||||
#define DRIVER_AUTHOR "Hans-Christian Egtvedt <hc@mivu.no>"
|
||||
#define DRIVER_VERSION "v1.2.2"
|
||||
#define DRIVER_DESC "USB ITM Inc Touch Panel Driver"
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
MODULE_AUTHOR( DRIVER_AUTHOR );
|
||||
MODULE_DESCRIPTION( DRIVER_DESC );
|
||||
MODULE_LICENSE( DRIVER_LICENSE );
|
||||
|
||||
struct itmtouch_dev {
|
||||
struct usb_device *usbdev; /* usb device */
|
||||
struct input_dev *inputdev; /* input device */
|
||||
struct urb *readurb; /* urb */
|
||||
char rbuf[ITM_BUFSIZE]; /* data */
|
||||
int users;
|
||||
char name[128];
|
||||
char phys[64];
|
||||
};
|
||||
|
||||
static struct usb_device_id itmtouch_ids [] = {
|
||||
{ USB_DEVICE(USB_VENDOR_ID_ITMINC, USB_PRODUCT_ID_TOUCHPANEL) },
|
||||
{ }
|
||||
};
|
||||
|
||||
static void itmtouch_irq(struct urb *urb)
|
||||
{
|
||||
struct itmtouch_dev *itmtouch = urb->context;
|
||||
unsigned char *data = urb->transfer_buffer;
|
||||
struct input_dev *dev = itmtouch->inputdev;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ETIME:
|
||||
/* this urb is timing out */
|
||||
dbg("%s - urb timed out - was the device unplugged?",
|
||||
__FUNCTION__);
|
||||
return;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d",
|
||||
__FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d",
|
||||
__FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* if pressure has been released, then don't report X/Y */
|
||||
if (!(data[7] & 0x20)) {
|
||||
input_report_abs(dev, ABS_X, (data[0] & 0x1F) << 7 | (data[3] & 0x7F));
|
||||
input_report_abs(dev, ABS_Y, (data[1] & 0x1F) << 7 | (data[4] & 0x7F));
|
||||
}
|
||||
|
||||
input_report_abs(dev, ABS_PRESSURE, (data[2] & 1) << 7 | (data[5] & 0x7F));
|
||||
input_report_key(dev, BTN_TOUCH, ~data[7] & 0x20);
|
||||
input_sync(dev);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
printk(KERN_ERR "%s - usb_submit_urb failed with result: %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
static int itmtouch_open(struct input_dev *input)
|
||||
{
|
||||
struct itmtouch_dev *itmtouch = input->private;
|
||||
|
||||
itmtouch->readurb->dev = itmtouch->usbdev;
|
||||
|
||||
if (usb_submit_urb(itmtouch->readurb, GFP_KERNEL))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void itmtouch_close(struct input_dev *input)
|
||||
{
|
||||
struct itmtouch_dev *itmtouch = input->private;
|
||||
|
||||
usb_kill_urb(itmtouch->readurb);
|
||||
}
|
||||
|
||||
static int itmtouch_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct itmtouch_dev *itmtouch;
|
||||
struct input_dev *input_dev;
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
unsigned int pipe;
|
||||
unsigned int maxp;
|
||||
|
||||
interface = intf->cur_altsetting;
|
||||
endpoint = &interface->endpoint[0].desc;
|
||||
|
||||
itmtouch = kzalloc(sizeof(struct itmtouch_dev), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!itmtouch || !input_dev) {
|
||||
err("%s - Out of memory.", __FUNCTION__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
itmtouch->usbdev = udev;
|
||||
itmtouch->inputdev = input_dev;
|
||||
|
||||
if (udev->manufacturer)
|
||||
strlcpy(itmtouch->name, udev->manufacturer, sizeof(itmtouch->name));
|
||||
|
||||
if (udev->product) {
|
||||
if (udev->manufacturer)
|
||||
strlcat(itmtouch->name, " ", sizeof(itmtouch->name));
|
||||
strlcat(itmtouch->name, udev->product, sizeof(itmtouch->name));
|
||||
}
|
||||
|
||||
if (!strlen(itmtouch->name))
|
||||
sprintf(itmtouch->name, "USB ITM touchscreen");
|
||||
|
||||
usb_make_path(udev, itmtouch->phys, sizeof(itmtouch->phys));
|
||||
strlcpy(itmtouch->phys, "/input0", sizeof(itmtouch->phys));
|
||||
|
||||
input_dev->name = itmtouch->name;
|
||||
input_dev->phys = itmtouch->phys;
|
||||
usb_to_input_id(udev, &input_dev->id);
|
||||
input_dev->cdev.dev = &intf->dev;
|
||||
input_dev->private = itmtouch;
|
||||
|
||||
input_dev->open = itmtouch_open;
|
||||
input_dev->close = itmtouch_close;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||
input_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
|
||||
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||
|
||||
/* device limits */
|
||||
/* as specified by the ITM datasheet, X and Y are 12bit,
|
||||
* Z (pressure) is 8 bit. However, the fields are defined up
|
||||
* to 14 bits for future possible expansion.
|
||||
*/
|
||||
input_set_abs_params(input_dev, ABS_X, 0, 0x0FFF, 2, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, 0x0FFF, 2, 0);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0xFF, 2, 0);
|
||||
|
||||
/* initialise the URB so we can read from the transport stream */
|
||||
pipe = usb_rcvintpipe(itmtouch->usbdev, endpoint->bEndpointAddress);
|
||||
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
|
||||
|
||||
if (maxp > ITM_BUFSIZE)
|
||||
maxp = ITM_BUFSIZE;
|
||||
|
||||
itmtouch->readurb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!itmtouch->readurb) {
|
||||
dbg("%s - usb_alloc_urb failed: itmtouch->readurb", __FUNCTION__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
usb_fill_int_urb(itmtouch->readurb, itmtouch->usbdev, pipe, itmtouch->rbuf,
|
||||
maxp, itmtouch_irq, itmtouch, endpoint->bInterval);
|
||||
|
||||
input_register_device(itmtouch->inputdev);
|
||||
|
||||
usb_set_intfdata(intf, itmtouch);
|
||||
|
||||
return 0;
|
||||
|
||||
fail: input_free_device(input_dev);
|
||||
kfree(itmtouch);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void itmtouch_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct itmtouch_dev *itmtouch = usb_get_intfdata(intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
|
||||
if (itmtouch) {
|
||||
input_unregister_device(itmtouch->inputdev);
|
||||
usb_kill_urb(itmtouch->readurb);
|
||||
usb_free_urb(itmtouch->readurb);
|
||||
kfree(itmtouch);
|
||||
}
|
||||
}
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, itmtouch_ids);
|
||||
|
||||
static struct usb_driver itmtouch_driver = {
|
||||
.name = "itmtouch",
|
||||
.probe = itmtouch_probe,
|
||||
.disconnect = itmtouch_disconnect,
|
||||
.id_table = itmtouch_ids,
|
||||
};
|
||||
|
||||
static int __init itmtouch_init(void)
|
||||
{
|
||||
info(DRIVER_DESC " " DRIVER_VERSION);
|
||||
info(DRIVER_AUTHOR);
|
||||
return usb_register(&itmtouch_driver);
|
||||
}
|
||||
|
||||
static void __exit itmtouch_exit(void)
|
||||
{
|
||||
usb_deregister(&itmtouch_driver);
|
||||
}
|
||||
|
||||
module_init(itmtouch_init);
|
||||
module_exit(itmtouch_exit);
|
||||
220
drivers/usb/input/kbtab.c
Normal file
220
drivers/usb/input/kbtab.c
Normal file
@@ -0,0 +1,220 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb/input.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
* v0.0.1 - Original, extremely basic version, 2.4.xx only
|
||||
* v0.0.2 - Updated, works with 2.5.62 and 2.4.20;
|
||||
* - added pressure-threshold modules param code from
|
||||
* Alex Perry <alex.perry@ieee.org>
|
||||
*/
|
||||
|
||||
#define DRIVER_VERSION "v0.0.2"
|
||||
#define DRIVER_AUTHOR "Josh Myer <josh@joshisanerd.com>"
|
||||
#define DRIVER_DESC "USB KB Gear JamStudio Tablet driver"
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE(DRIVER_LICENSE);
|
||||
|
||||
#define USB_VENDOR_ID_KBGEAR 0x084e
|
||||
|
||||
static int kb_pressure_click = 0x10;
|
||||
module_param(kb_pressure_click, int, 0);
|
||||
MODULE_PARM_DESC(kb_pressure_click, "pressure threshold for clicks");
|
||||
|
||||
struct kbtab {
|
||||
signed char *data;
|
||||
dma_addr_t data_dma;
|
||||
struct input_dev *dev;
|
||||
struct usb_device *usbdev;
|
||||
struct urb *irq;
|
||||
int x, y;
|
||||
int button;
|
||||
int pressure;
|
||||
__u32 serial[2];
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
static void kbtab_irq(struct urb *urb)
|
||||
{
|
||||
struct kbtab *kbtab = urb->context;
|
||||
unsigned char *data = kbtab->data;
|
||||
struct input_dev *dev = kbtab->dev;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
kbtab->x = le16_to_cpu(get_unaligned((__le16 *) &data[1]));
|
||||
kbtab->y = le16_to_cpu(get_unaligned((__le16 *) &data[3]));
|
||||
|
||||
kbtab->pressure = (data[5]);
|
||||
|
||||
input_report_key(dev, BTN_TOOL_PEN, 1);
|
||||
|
||||
input_report_abs(dev, ABS_X, kbtab->x);
|
||||
input_report_abs(dev, ABS_Y, kbtab->y);
|
||||
|
||||
/*input_report_key(dev, BTN_TOUCH , data[0] & 0x01);*/
|
||||
input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
|
||||
|
||||
if (-1 == kb_pressure_click) {
|
||||
input_report_abs(dev, ABS_PRESSURE, kbtab->pressure);
|
||||
} else {
|
||||
input_report_key(dev, BTN_LEFT, (kbtab->pressure > kb_pressure_click) ? 1 : 0);
|
||||
};
|
||||
|
||||
input_sync(dev);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err ("%s - usb_submit_urb failed with result %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
static struct usb_device_id kbtab_ids[] = {
|
||||
{ USB_DEVICE(USB_VENDOR_ID_KBGEAR, 0x1001), .driver_info = 0 },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, kbtab_ids);
|
||||
|
||||
static int kbtab_open(struct input_dev *dev)
|
||||
{
|
||||
struct kbtab *kbtab = dev->private;
|
||||
|
||||
kbtab->irq->dev = kbtab->usbdev;
|
||||
if (usb_submit_urb(kbtab->irq, GFP_KERNEL))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kbtab_close(struct input_dev *dev)
|
||||
{
|
||||
struct kbtab *kbtab = dev->private;
|
||||
|
||||
usb_kill_urb(kbtab->irq);
|
||||
}
|
||||
|
||||
static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct kbtab *kbtab;
|
||||
struct input_dev *input_dev;
|
||||
|
||||
kbtab = kzalloc(sizeof(struct kbtab), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!kbtab || !input_dev)
|
||||
goto fail1;
|
||||
|
||||
kbtab->data = usb_buffer_alloc(dev, 8, GFP_KERNEL, &kbtab->data_dma);
|
||||
if (!kbtab->data)
|
||||
goto fail1;
|
||||
|
||||
kbtab->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!kbtab->irq)
|
||||
goto fail2;
|
||||
|
||||
kbtab->usbdev = dev;
|
||||
kbtab->dev = input_dev;
|
||||
|
||||
usb_make_path(dev, kbtab->phys, sizeof(kbtab->phys));
|
||||
strlcat(kbtab->phys, "/input0", sizeof(kbtab->phys));
|
||||
|
||||
input_dev->name = "KB Gear Tablet";
|
||||
input_dev->phys = kbtab->phys;
|
||||
usb_to_input_id(dev, &input_dev->id);
|
||||
input_dev->cdev.dev = &intf->dev;
|
||||
input_dev->private = kbtab;
|
||||
|
||||
input_dev->open = kbtab_open;
|
||||
input_dev->close = kbtab_close;
|
||||
|
||||
input_dev->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_MSC);
|
||||
input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
|
||||
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH);
|
||||
input_dev->mscbit[0] |= BIT(MSC_SERIAL);
|
||||
input_set_abs_params(input_dev, ABS_X, 0, 0x2000, 4, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, 0x1750, 4, 0);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0xff, 0, 0);
|
||||
|
||||
endpoint = &intf->cur_altsetting->endpoint[0].desc;
|
||||
|
||||
usb_fill_int_urb(kbtab->irq, dev,
|
||||
usb_rcvintpipe(dev, endpoint->bEndpointAddress),
|
||||
kbtab->data, 8,
|
||||
kbtab_irq, kbtab, endpoint->bInterval);
|
||||
kbtab->irq->transfer_dma = kbtab->data_dma;
|
||||
kbtab->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
input_register_device(kbtab->dev);
|
||||
|
||||
usb_set_intfdata(intf, kbtab);
|
||||
return 0;
|
||||
|
||||
fail2: usb_buffer_free(dev, 10, kbtab->data, kbtab->data_dma);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(kbtab);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void kbtab_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct kbtab *kbtab = usb_get_intfdata(intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (kbtab) {
|
||||
usb_kill_urb(kbtab->irq);
|
||||
input_unregister_device(kbtab->dev);
|
||||
usb_free_urb(kbtab->irq);
|
||||
usb_buffer_free(interface_to_usbdev(intf), 10, kbtab->data, kbtab->data_dma);
|
||||
kfree(kbtab);
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_driver kbtab_driver = {
|
||||
.name = "kbtab",
|
||||
.probe = kbtab_probe,
|
||||
.disconnect = kbtab_disconnect,
|
||||
.id_table = kbtab_ids,
|
||||
};
|
||||
|
||||
static int __init kbtab_init(void)
|
||||
{
|
||||
int retval;
|
||||
retval = usb_register(&kbtab_driver);
|
||||
if (retval)
|
||||
goto out;
|
||||
info(DRIVER_VERSION ":" DRIVER_DESC);
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit kbtab_exit(void)
|
||||
{
|
||||
usb_deregister(&kbtab_driver);
|
||||
}
|
||||
|
||||
module_init(kbtab_init);
|
||||
module_exit(kbtab_exit);
|
||||
589
drivers/usb/input/keyspan_remote.c
Normal file
589
drivers/usb/input/keyspan_remote.c
Normal file
@@ -0,0 +1,589 @@
|
||||
/*
|
||||
* keyspan_remote: USB driver for the Keyspan DMR
|
||||
*
|
||||
* Copyright (C) 2005 Zymeta Corporation - Michael Downey (downey@zymeta.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, version 2.
|
||||
*
|
||||
* This driver has been put together with the support of Innosys, Inc.
|
||||
* and Keyspan, Inc the manufacturers of the Keyspan USB DMR product.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
#define DRIVER_VERSION "v0.1"
|
||||
#define DRIVER_AUTHOR "Michael Downey <downey@zymeta.com>"
|
||||
#define DRIVER_DESC "Driver for the USB Keyspan remote control."
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
/* Parameters that can be passed to the driver. */
|
||||
static int debug;
|
||||
module_param(debug, int, 0444);
|
||||
MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
|
||||
|
||||
/* Vendor and product ids */
|
||||
#define USB_KEYSPAN_VENDOR_ID 0x06CD
|
||||
#define USB_KEYSPAN_PRODUCT_UIA11 0x0202
|
||||
|
||||
/* Defines for converting the data from the remote. */
|
||||
#define ZERO 0x18
|
||||
#define ZERO_MASK 0x1F /* 5 bits for a 0 */
|
||||
#define ONE 0x3C
|
||||
#define ONE_MASK 0x3F /* 6 bits for a 1 */
|
||||
#define SYNC 0x3F80
|
||||
#define SYNC_MASK 0x3FFF /* 14 bits for a SYNC sequence */
|
||||
#define STOP 0x00
|
||||
#define STOP_MASK 0x1F /* 5 bits for the STOP sequence */
|
||||
#define GAP 0xFF
|
||||
|
||||
#define RECV_SIZE 8 /* The UIA-11 type have a 8 byte limit. */
|
||||
|
||||
/* table of devices that work with this driver */
|
||||
static struct usb_device_id keyspan_table[] = {
|
||||
{ USB_DEVICE(USB_KEYSPAN_VENDOR_ID, USB_KEYSPAN_PRODUCT_UIA11) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
/* Structure to store all the real stuff that a remote sends to us. */
|
||||
struct keyspan_message {
|
||||
u16 system;
|
||||
u8 button;
|
||||
u8 toggle;
|
||||
};
|
||||
|
||||
/* Structure used for all the bit testing magic needed to be done. */
|
||||
struct bit_tester {
|
||||
u32 tester;
|
||||
int len;
|
||||
int pos;
|
||||
int bits_left;
|
||||
u8 buffer[32];
|
||||
};
|
||||
|
||||
/* Structure to hold all of our driver specific stuff */
|
||||
struct usb_keyspan {
|
||||
char name[128];
|
||||
char phys[64];
|
||||
struct usb_device* udev;
|
||||
struct input_dev *input;
|
||||
struct usb_interface* interface;
|
||||
struct usb_endpoint_descriptor* in_endpoint;
|
||||
struct urb* irq_urb;
|
||||
int open;
|
||||
dma_addr_t in_dma;
|
||||
unsigned char* in_buffer;
|
||||
|
||||
/* variables used to parse messages from remote. */
|
||||
struct bit_tester data;
|
||||
int stage;
|
||||
int toggle;
|
||||
};
|
||||
|
||||
/*
|
||||
* Table that maps the 31 possible keycodes to input keys.
|
||||
* Currently there are 15 and 17 button models so RESERVED codes
|
||||
* are blank areas in the mapping.
|
||||
*/
|
||||
static const int keyspan_key_table[] = {
|
||||
KEY_RESERVED, /* 0 is just a place holder. */
|
||||
KEY_RESERVED,
|
||||
KEY_STOP,
|
||||
KEY_PLAYCD,
|
||||
KEY_RESERVED,
|
||||
KEY_PREVIOUSSONG,
|
||||
KEY_REWIND,
|
||||
KEY_FORWARD,
|
||||
KEY_NEXTSONG,
|
||||
KEY_RESERVED,
|
||||
KEY_RESERVED,
|
||||
KEY_RESERVED,
|
||||
KEY_PAUSE,
|
||||
KEY_VOLUMEUP,
|
||||
KEY_RESERVED,
|
||||
KEY_RESERVED,
|
||||
KEY_RESERVED,
|
||||
KEY_VOLUMEDOWN,
|
||||
KEY_RESERVED,
|
||||
KEY_UP,
|
||||
KEY_RESERVED,
|
||||
KEY_MUTE,
|
||||
KEY_LEFT,
|
||||
KEY_ENTER,
|
||||
KEY_RIGHT,
|
||||
KEY_RESERVED,
|
||||
KEY_RESERVED,
|
||||
KEY_DOWN,
|
||||
KEY_RESERVED,
|
||||
KEY_KPASTERISK,
|
||||
KEY_RESERVED,
|
||||
KEY_MENU
|
||||
};
|
||||
|
||||
static struct usb_driver keyspan_driver;
|
||||
|
||||
/*
|
||||
* Debug routine that prints out what we've received from the remote.
|
||||
*/
|
||||
static void keyspan_print(struct usb_keyspan* dev) /*unsigned char* data)*/
|
||||
{
|
||||
char codes[4 * RECV_SIZE];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < RECV_SIZE; i++)
|
||||
snprintf(codes + i * 3, 4, "%02x ", dev->in_buffer[i]);
|
||||
|
||||
dev_info(&dev->udev->dev, "%s\n", codes);
|
||||
}
|
||||
|
||||
/*
|
||||
* Routine that manages the bit_tester structure. It makes sure that there are
|
||||
* at least bits_needed bits loaded into the tester.
|
||||
*/
|
||||
static int keyspan_load_tester(struct usb_keyspan* dev, int bits_needed)
|
||||
{
|
||||
if (dev->data.bits_left >= bits_needed)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Somehow we've missed the last message. The message will be repeated
|
||||
* though so it's not too big a deal
|
||||
*/
|
||||
if (dev->data.pos >= dev->data.len) {
|
||||
dev_dbg(&dev->udev->dev,
|
||||
"%s - Error ran out of data. pos: %d, len: %d\n",
|
||||
__FUNCTION__, dev->data.pos, dev->data.len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Load as much as we can into the tester. */
|
||||
while ((dev->data.bits_left + 7 < (sizeof(dev->data.tester) * 8)) &&
|
||||
(dev->data.pos < dev->data.len)) {
|
||||
dev->data.tester += (dev->data.buffer[dev->data.pos++] << dev->data.bits_left);
|
||||
dev->data.bits_left += 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Routine that handles all the logic needed to parse out the message from the remote.
|
||||
*/
|
||||
static void keyspan_check_data(struct usb_keyspan *remote)
|
||||
{
|
||||
int i;
|
||||
int found = 0;
|
||||
struct keyspan_message message;
|
||||
|
||||
switch(remote->stage) {
|
||||
case 0:
|
||||
/*
|
||||
* In stage 0 we want to find the start of a message. The remote sends a 0xFF as filler.
|
||||
* So the first byte that isn't a FF should be the start of a new message.
|
||||
*/
|
||||
for (i = 0; i < RECV_SIZE && remote->in_buffer[i] == GAP; ++i);
|
||||
|
||||
if (i < RECV_SIZE) {
|
||||
memcpy(remote->data.buffer, remote->in_buffer, RECV_SIZE);
|
||||
remote->data.len = RECV_SIZE;
|
||||
remote->data.pos = 0;
|
||||
remote->data.tester = 0;
|
||||
remote->data.bits_left = 0;
|
||||
remote->stage = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/*
|
||||
* Stage 1 we should have 16 bytes and should be able to detect a
|
||||
* SYNC. The SYNC is 14 bits, 7 0's and then 7 1's.
|
||||
*/
|
||||
memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE);
|
||||
remote->data.len += RECV_SIZE;
|
||||
|
||||
found = 0;
|
||||
while ((remote->data.bits_left >= 14 || remote->data.pos < remote->data.len) && !found) {
|
||||
for (i = 0; i < 8; ++i) {
|
||||
if (keyspan_load_tester(remote, 14) != 0) {
|
||||
remote->stage = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((remote->data.tester & SYNC_MASK) == SYNC) {
|
||||
remote->data.tester = remote->data.tester >> 14;
|
||||
remote->data.bits_left -= 14;
|
||||
found = 1;
|
||||
break;
|
||||
} else {
|
||||
remote->data.tester = remote->data.tester >> 1;
|
||||
--remote->data.bits_left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
remote->stage = 0;
|
||||
remote->data.len = 0;
|
||||
} else {
|
||||
remote->stage = 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/*
|
||||
* Stage 2 we should have 24 bytes which will be enough for a full
|
||||
* message. We need to parse out the system code, button code,
|
||||
* toggle code, and stop.
|
||||
*/
|
||||
memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE);
|
||||
remote->data.len += RECV_SIZE;
|
||||
|
||||
message.system = 0;
|
||||
for (i = 0; i < 9; i++) {
|
||||
keyspan_load_tester(remote, 6);
|
||||
|
||||
if ((remote->data.tester & ZERO_MASK) == ZERO) {
|
||||
message.system = message.system << 1;
|
||||
remote->data.tester = remote->data.tester >> 5;
|
||||
remote->data.bits_left -= 5;
|
||||
} else if ((remote->data.tester & ONE_MASK) == ONE) {
|
||||
message.system = (message.system << 1) + 1;
|
||||
remote->data.tester = remote->data.tester >> 6;
|
||||
remote->data.bits_left -= 6;
|
||||
} else {
|
||||
err("%s - Unknown sequence found in system data.\n", __FUNCTION__);
|
||||
remote->stage = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
message.button = 0;
|
||||
for (i = 0; i < 5; i++) {
|
||||
keyspan_load_tester(remote, 6);
|
||||
|
||||
if ((remote->data.tester & ZERO_MASK) == ZERO) {
|
||||
message.button = message.button << 1;
|
||||
remote->data.tester = remote->data.tester >> 5;
|
||||
remote->data.bits_left -= 5;
|
||||
} else if ((remote->data.tester & ONE_MASK) == ONE) {
|
||||
message.button = (message.button << 1) + 1;
|
||||
remote->data.tester = remote->data.tester >> 6;
|
||||
remote->data.bits_left -= 6;
|
||||
} else {
|
||||
err("%s - Unknown sequence found in button data.\n", __FUNCTION__);
|
||||
remote->stage = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
keyspan_load_tester(remote, 6);
|
||||
if ((remote->data.tester & ZERO_MASK) == ZERO) {
|
||||
message.toggle = 0;
|
||||
remote->data.tester = remote->data.tester >> 5;
|
||||
remote->data.bits_left -= 5;
|
||||
} else if ((remote->data.tester & ONE_MASK) == ONE) {
|
||||
message.toggle = 1;
|
||||
remote->data.tester = remote->data.tester >> 6;
|
||||
remote->data.bits_left -= 6;
|
||||
} else {
|
||||
err("%s - Error in message, invalid toggle.\n", __FUNCTION__);
|
||||
remote->stage = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
keyspan_load_tester(remote, 5);
|
||||
if ((remote->data.tester & STOP_MASK) == STOP) {
|
||||
remote->data.tester = remote->data.tester >> 5;
|
||||
remote->data.bits_left -= 5;
|
||||
} else {
|
||||
err("Bad message recieved, no stop bit found.\n");
|
||||
}
|
||||
|
||||
dev_dbg(&remote->udev->dev,
|
||||
"%s found valid message: system: %d, button: %d, toggle: %d\n",
|
||||
__FUNCTION__, message.system, message.button, message.toggle);
|
||||
|
||||
if (message.toggle != remote->toggle) {
|
||||
input_report_key(remote->input, keyspan_key_table[message.button], 1);
|
||||
input_report_key(remote->input, keyspan_key_table[message.button], 0);
|
||||
input_sync(remote->input);
|
||||
remote->toggle = message.toggle;
|
||||
}
|
||||
|
||||
remote->stage = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Routine for sending all the initialization messages to the remote.
|
||||
*/
|
||||
static int keyspan_setup(struct usb_device* dev)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
0x11, 0x40, 0x5601, 0x0, NULL, 0, 0);
|
||||
if (retval) {
|
||||
dev_dbg(&dev->dev, "%s - failed to set bit rate due to error: %d\n",
|
||||
__FUNCTION__, retval);
|
||||
return(retval);
|
||||
}
|
||||
|
||||
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
0x44, 0x40, 0x0, 0x0, NULL, 0, 0);
|
||||
if (retval) {
|
||||
dev_dbg(&dev->dev, "%s - failed to set resume sensitivity due to error: %d\n",
|
||||
__FUNCTION__, retval);
|
||||
return(retval);
|
||||
}
|
||||
|
||||
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
0x22, 0x40, 0x0, 0x0, NULL, 0, 0);
|
||||
if (retval) {
|
||||
dev_dbg(&dev->dev, "%s - failed to turn receive on due to error: %d\n",
|
||||
__FUNCTION__, retval);
|
||||
return(retval);
|
||||
}
|
||||
|
||||
dev_dbg(&dev->dev, "%s - Setup complete.\n", __FUNCTION__);
|
||||
return(retval);
|
||||
}
|
||||
|
||||
/*
|
||||
* Routine used to handle a new message that has come in.
|
||||
*/
|
||||
static void keyspan_irq_recv(struct urb *urb)
|
||||
{
|
||||
struct usb_keyspan *dev = urb->context;
|
||||
int retval;
|
||||
|
||||
/* Check our status in case we need to bail out early. */
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
/* Device went away so don't keep trying to read from it. */
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
return;
|
||||
|
||||
default:
|
||||
goto resubmit;
|
||||
break;
|
||||
}
|
||||
|
||||
if (debug)
|
||||
keyspan_print(dev);
|
||||
|
||||
keyspan_check_data(dev);
|
||||
|
||||
resubmit:
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err ("%s - usb_submit_urb failed with result: %d", __FUNCTION__, retval);
|
||||
}
|
||||
|
||||
static int keyspan_open(struct input_dev *dev)
|
||||
{
|
||||
struct usb_keyspan *remote = dev->private;
|
||||
|
||||
remote->irq_urb->dev = remote->udev;
|
||||
if (usb_submit_urb(remote->irq_urb, GFP_KERNEL))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void keyspan_close(struct input_dev *dev)
|
||||
{
|
||||
struct usb_keyspan *remote = dev->private;
|
||||
|
||||
usb_kill_urb(remote->irq_urb);
|
||||
}
|
||||
|
||||
static struct usb_endpoint_descriptor *keyspan_get_in_endpoint(struct usb_host_interface *iface)
|
||||
{
|
||||
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
|
||||
endpoint = &iface->endpoint[i].desc;
|
||||
|
||||
if (usb_endpoint_is_int_in(endpoint)) {
|
||||
/* we found our interrupt in endpoint */
|
||||
return endpoint;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Routine that sets up the driver to handle a specific USB device detected on the bus.
|
||||
*/
|
||||
static int keyspan_probe(struct usb_interface *interface, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(interface);
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct usb_keyspan *remote;
|
||||
struct input_dev *input_dev;
|
||||
int i, retval;
|
||||
|
||||
endpoint = keyspan_get_in_endpoint(interface->cur_altsetting);
|
||||
if (!endpoint)
|
||||
return -ENODEV;
|
||||
|
||||
remote = kzalloc(sizeof(*remote), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!remote || !input_dev) {
|
||||
retval = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
remote->udev = udev;
|
||||
remote->input = input_dev;
|
||||
remote->interface = interface;
|
||||
remote->in_endpoint = endpoint;
|
||||
remote->toggle = -1; /* Set to -1 so we will always not match the toggle from the first remote message. */
|
||||
|
||||
remote->in_buffer = usb_buffer_alloc(udev, RECV_SIZE, GFP_ATOMIC, &remote->in_dma);
|
||||
if (!remote->in_buffer) {
|
||||
retval = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!remote->irq_urb) {
|
||||
retval = -ENOMEM;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
retval = keyspan_setup(udev);
|
||||
if (retval) {
|
||||
retval = -ENODEV;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
if (udev->manufacturer)
|
||||
strlcpy(remote->name, udev->manufacturer, sizeof(remote->name));
|
||||
|
||||
if (udev->product) {
|
||||
if (udev->manufacturer)
|
||||
strlcat(remote->name, " ", sizeof(remote->name));
|
||||
strlcat(remote->name, udev->product, sizeof(remote->name));
|
||||
}
|
||||
|
||||
if (!strlen(remote->name))
|
||||
snprintf(remote->name, sizeof(remote->name),
|
||||
"USB Keyspan Remote %04x:%04x",
|
||||
le16_to_cpu(udev->descriptor.idVendor),
|
||||
le16_to_cpu(udev->descriptor.idProduct));
|
||||
|
||||
usb_make_path(udev, remote->phys, sizeof(remote->phys));
|
||||
strlcat(remote->phys, "/input0", sizeof(remote->phys));
|
||||
|
||||
input_dev->name = remote->name;
|
||||
input_dev->phys = remote->phys;
|
||||
usb_to_input_id(udev, &input_dev->id);
|
||||
input_dev->cdev.dev = &interface->dev;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY); /* We will only report KEY events. */
|
||||
for (i = 0; i < ARRAY_SIZE(keyspan_key_table); i++)
|
||||
if (keyspan_key_table[i] != KEY_RESERVED)
|
||||
set_bit(keyspan_key_table[i], input_dev->keybit);
|
||||
|
||||
input_dev->private = remote;
|
||||
input_dev->open = keyspan_open;
|
||||
input_dev->close = keyspan_close;
|
||||
|
||||
/*
|
||||
* Initialize the URB to access the device. The urb gets sent to the device in keyspan_open()
|
||||
*/
|
||||
usb_fill_int_urb(remote->irq_urb,
|
||||
remote->udev, usb_rcvintpipe(remote->udev, remote->in_endpoint->bEndpointAddress),
|
||||
remote->in_buffer, RECV_SIZE, keyspan_irq_recv, remote,
|
||||
remote->in_endpoint->bInterval);
|
||||
remote->irq_urb->transfer_dma = remote->in_dma;
|
||||
remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
/* we can register the device now, as it is ready */
|
||||
input_register_device(remote->input);
|
||||
|
||||
/* save our data pointer in this interface device */
|
||||
usb_set_intfdata(interface, remote);
|
||||
|
||||
return 0;
|
||||
|
||||
fail3: usb_free_urb(remote->irq_urb);
|
||||
fail2: usb_buffer_free(udev, RECV_SIZE, remote->in_buffer, remote->in_dma);
|
||||
fail1: kfree(remote);
|
||||
input_free_device(input_dev);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Routine called when a device is disconnected from the USB.
|
||||
*/
|
||||
static void keyspan_disconnect(struct usb_interface *interface)
|
||||
{
|
||||
struct usb_keyspan *remote;
|
||||
|
||||
remote = usb_get_intfdata(interface);
|
||||
usb_set_intfdata(interface, NULL);
|
||||
|
||||
if (remote) { /* We have a valid driver structure so clean up everything we allocated. */
|
||||
input_unregister_device(remote->input);
|
||||
usb_kill_urb(remote->irq_urb);
|
||||
usb_free_urb(remote->irq_urb);
|
||||
usb_buffer_free(remote->udev, RECV_SIZE, remote->in_buffer, remote->in_dma);
|
||||
kfree(remote);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Standard driver set up sections
|
||||
*/
|
||||
static struct usb_driver keyspan_driver =
|
||||
{
|
||||
.name = "keyspan_remote",
|
||||
.probe = keyspan_probe,
|
||||
.disconnect = keyspan_disconnect,
|
||||
.id_table = keyspan_table
|
||||
};
|
||||
|
||||
static int __init usb_keyspan_init(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
/* register this driver with the USB subsystem */
|
||||
result = usb_register(&keyspan_driver);
|
||||
if (result)
|
||||
err("usb_register failed. Error number %d\n", result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void __exit usb_keyspan_exit(void)
|
||||
{
|
||||
/* deregister this driver with the USB subsystem */
|
||||
usb_deregister(&keyspan_driver);
|
||||
}
|
||||
|
||||
module_init(usb_keyspan_init);
|
||||
module_exit(usb_keyspan_exit);
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, keyspan_table);
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE(DRIVER_LICENSE);
|
||||
189
drivers/usb/input/map_to_7segment.h
Normal file
189
drivers/usb/input/map_to_7segment.h
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* drivers/usb/input/map_to_7segment.h
|
||||
*
|
||||
* Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef MAP_TO_7SEGMENT_H
|
||||
#define MAP_TO_7SEGMENT_H
|
||||
|
||||
/* This file provides translation primitives and tables for the conversion
|
||||
* of (ASCII) characters to a 7-segments notation.
|
||||
*
|
||||
* The 7 segment's wikipedia notation below is used as standard.
|
||||
* See: http://en.wikipedia.org/wiki/Seven_segment_display
|
||||
*
|
||||
* Notation: +-a-+
|
||||
* f b
|
||||
* +-g-+
|
||||
* e c
|
||||
* +-d-+
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* Register a map variable, and fill it with a character set:
|
||||
* static SEG7_DEFAULT_MAP(map_seg7);
|
||||
*
|
||||
*
|
||||
* Then use for conversion:
|
||||
* seg7 = map_to_seg7(&map_seg7, some_char);
|
||||
* ...
|
||||
*
|
||||
* In device drivers it is recommended, if required, to make the char map
|
||||
* accessible via the sysfs interface using the following scheme:
|
||||
*
|
||||
* static ssize_t show_map(struct device *dev, char *buf) {
|
||||
* memcpy(buf, &map_seg7, sizeof(map_seg7));
|
||||
* return sizeof(map_seg7);
|
||||
* }
|
||||
* static ssize_t store_map(struct device *dev, const char *buf, size_t cnt) {
|
||||
* if(cnt != sizeof(map_seg7))
|
||||
* return -EINVAL;
|
||||
* memcpy(&map_seg7, buf, cnt);
|
||||
* return cnt;
|
||||
* }
|
||||
* static DEVICE_ATTR(map_seg7, PERMS_RW, show_map, store_map);
|
||||
*
|
||||
* History:
|
||||
* 2005-05-31 RFC linux-kernel@vger.kernel.org
|
||||
*/
|
||||
#include <linux/errno.h>
|
||||
|
||||
|
||||
#define BIT_SEG7_A 0
|
||||
#define BIT_SEG7_B 1
|
||||
#define BIT_SEG7_C 2
|
||||
#define BIT_SEG7_D 3
|
||||
#define BIT_SEG7_E 4
|
||||
#define BIT_SEG7_F 5
|
||||
#define BIT_SEG7_G 6
|
||||
#define BIT_SEG7_RESERVED 7
|
||||
|
||||
struct seg7_conversion_map {
|
||||
unsigned char table[128];
|
||||
};
|
||||
|
||||
static inline int map_to_seg7(struct seg7_conversion_map *map, int c)
|
||||
{
|
||||
return c >= 0 && c < sizeof(map->table) ? map->table[c] : -EINVAL;
|
||||
}
|
||||
|
||||
#define SEG7_CONVERSION_MAP(_name, _map) \
|
||||
struct seg7_conversion_map _name = { .table = { _map } }
|
||||
|
||||
/*
|
||||
* It is recommended to use a facility that allows user space to redefine
|
||||
* custom character sets for LCD devices. Please use a sysfs interface
|
||||
* as described above.
|
||||
*/
|
||||
#define MAP_TO_SEG7_SYSFS_FILE "map_seg7"
|
||||
|
||||
/*******************************************************************************
|
||||
* ASCII conversion table
|
||||
******************************************************************************/
|
||||
|
||||
#define _SEG7(l,a,b,c,d,e,f,g) \
|
||||
( a<<BIT_SEG7_A | b<<BIT_SEG7_B | c<<BIT_SEG7_C | d<<BIT_SEG7_D | \
|
||||
e<<BIT_SEG7_E | f<<BIT_SEG7_F | g<<BIT_SEG7_G )
|
||||
|
||||
#define _MAP_0_32_ASCII_SEG7_NON_PRINTABLE \
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
|
||||
#define _MAP_33_47_ASCII_SEG7_SYMBOL \
|
||||
_SEG7('!',0,0,0,0,1,1,0), _SEG7('"',0,1,0,0,0,1,0), _SEG7('#',0,1,1,0,1,1,0),\
|
||||
_SEG7('$',1,0,1,1,0,1,1), _SEG7('%',0,0,1,0,0,1,0), _SEG7('&',1,0,1,1,1,1,1),\
|
||||
_SEG7('\'',0,0,0,0,0,1,0),_SEG7('(',1,0,0,1,1,1,0), _SEG7(')',1,1,1,1,0,0,0),\
|
||||
_SEG7('*',0,1,1,0,1,1,1), _SEG7('+',0,1,1,0,0,0,1), _SEG7(',',0,0,0,0,1,0,0),\
|
||||
_SEG7('-',0,0,0,0,0,0,1), _SEG7('.',0,0,0,0,1,0,0), _SEG7('/',0,1,0,0,1,0,1),
|
||||
|
||||
#define _MAP_48_57_ASCII_SEG7_NUMERIC \
|
||||
_SEG7('0',1,1,1,1,1,1,0), _SEG7('1',0,1,1,0,0,0,0), _SEG7('2',1,1,0,1,1,0,1),\
|
||||
_SEG7('3',1,1,1,1,0,0,1), _SEG7('4',0,1,1,0,0,1,1), _SEG7('5',1,0,1,1,0,1,1),\
|
||||
_SEG7('6',1,0,1,1,1,1,1), _SEG7('7',1,1,1,0,0,0,0), _SEG7('8',1,1,1,1,1,1,1),\
|
||||
_SEG7('9',1,1,1,1,0,1,1),
|
||||
|
||||
#define _MAP_58_64_ASCII_SEG7_SYMBOL \
|
||||
_SEG7(':',0,0,0,1,0,0,1), _SEG7(';',0,0,0,1,0,0,1), _SEG7('<',1,0,0,0,0,1,1),\
|
||||
_SEG7('=',0,0,0,1,0,0,1), _SEG7('>',1,1,0,0,0,0,1), _SEG7('?',1,1,1,0,0,1,0),\
|
||||
_SEG7('@',1,1,0,1,1,1,1),
|
||||
|
||||
#define _MAP_65_90_ASCII_SEG7_ALPHA_UPPR \
|
||||
_SEG7('A',1,1,1,0,1,1,1), _SEG7('B',1,1,1,1,1,1,1), _SEG7('C',1,0,0,1,1,1,0),\
|
||||
_SEG7('D',1,1,1,1,1,1,0), _SEG7('E',1,0,0,1,1,1,1), _SEG7('F',1,0,0,0,1,1,1),\
|
||||
_SEG7('G',1,1,1,1,0,1,1), _SEG7('H',0,1,1,0,1,1,1), _SEG7('I',0,1,1,0,0,0,0),\
|
||||
_SEG7('J',0,1,1,1,0,0,0), _SEG7('K',0,1,1,0,1,1,1), _SEG7('L',0,0,0,1,1,1,0),\
|
||||
_SEG7('M',1,1,1,0,1,1,0), _SEG7('N',1,1,1,0,1,1,0), _SEG7('O',1,1,1,1,1,1,0),\
|
||||
_SEG7('P',1,1,0,0,1,1,1), _SEG7('Q',1,1,1,1,1,1,0), _SEG7('R',1,1,1,0,1,1,1),\
|
||||
_SEG7('S',1,0,1,1,0,1,1), _SEG7('T',0,0,0,1,1,1,1), _SEG7('U',0,1,1,1,1,1,0),\
|
||||
_SEG7('V',0,1,1,1,1,1,0), _SEG7('W',0,1,1,1,1,1,1), _SEG7('X',0,1,1,0,1,1,1),\
|
||||
_SEG7('Y',0,1,1,0,0,1,1), _SEG7('Z',1,1,0,1,1,0,1),
|
||||
|
||||
#define _MAP_91_96_ASCII_SEG7_SYMBOL \
|
||||
_SEG7('[',1,0,0,1,1,1,0), _SEG7('\\',0,0,1,0,0,1,1),_SEG7(']',1,1,1,1,0,0,0),\
|
||||
_SEG7('^',1,1,0,0,0,1,0), _SEG7('_',0,0,0,1,0,0,0), _SEG7('`',0,1,0,0,0,0,0),
|
||||
|
||||
#define _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
|
||||
_SEG7('A',1,1,1,0,1,1,1), _SEG7('b',0,0,1,1,1,1,1), _SEG7('c',0,0,0,1,1,0,1),\
|
||||
_SEG7('d',0,1,1,1,1,0,1), _SEG7('E',1,0,0,1,1,1,1), _SEG7('F',1,0,0,0,1,1,1),\
|
||||
_SEG7('G',1,1,1,1,0,1,1), _SEG7('h',0,0,1,0,1,1,1), _SEG7('i',0,0,1,0,0,0,0),\
|
||||
_SEG7('j',0,0,1,1,0,0,0), _SEG7('k',0,0,1,0,1,1,1), _SEG7('L',0,0,0,1,1,1,0),\
|
||||
_SEG7('M',1,1,1,0,1,1,0), _SEG7('n',0,0,1,0,1,0,1), _SEG7('o',0,0,1,1,1,0,1),\
|
||||
_SEG7('P',1,1,0,0,1,1,1), _SEG7('q',1,1,1,0,0,1,1), _SEG7('r',0,0,0,0,1,0,1),\
|
||||
_SEG7('S',1,0,1,1,0,1,1), _SEG7('T',0,0,0,1,1,1,1), _SEG7('u',0,0,1,1,1,0,0),\
|
||||
_SEG7('v',0,0,1,1,1,0,0), _SEG7('W',0,1,1,1,1,1,1), _SEG7('X',0,1,1,0,1,1,1),\
|
||||
_SEG7('y',0,1,1,1,0,1,1), _SEG7('Z',1,1,0,1,1,0,1),
|
||||
|
||||
#define _MAP_123_126_ASCII_SEG7_SYMBOL \
|
||||
_SEG7('{',1,0,0,1,1,1,0), _SEG7('|',0,0,0,0,1,1,0), _SEG7('}',1,1,1,1,0,0,0),\
|
||||
_SEG7('~',1,0,0,0,0,0,0),
|
||||
|
||||
/* Maps */
|
||||
|
||||
/* This set tries to map as close as possible to the visible characteristics
|
||||
* of the ASCII symbol, lowercase and uppercase letters may differ in
|
||||
* presentation on the display.
|
||||
*/
|
||||
#define MAP_ASCII7SEG_ALPHANUM \
|
||||
_MAP_0_32_ASCII_SEG7_NON_PRINTABLE \
|
||||
_MAP_33_47_ASCII_SEG7_SYMBOL \
|
||||
_MAP_48_57_ASCII_SEG7_NUMERIC \
|
||||
_MAP_58_64_ASCII_SEG7_SYMBOL \
|
||||
_MAP_65_90_ASCII_SEG7_ALPHA_UPPR \
|
||||
_MAP_91_96_ASCII_SEG7_SYMBOL \
|
||||
_MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
|
||||
_MAP_123_126_ASCII_SEG7_SYMBOL
|
||||
|
||||
/* This set tries to map as close as possible to the symbolic characteristics
|
||||
* of the ASCII character for maximum discrimination.
|
||||
* For now this means all alpha chars are in lower case representations.
|
||||
* (This for example facilitates the use of hex numbers with uppercase input.)
|
||||
*/
|
||||
#define MAP_ASCII7SEG_ALPHANUM_LC \
|
||||
_MAP_0_32_ASCII_SEG7_NON_PRINTABLE \
|
||||
_MAP_33_47_ASCII_SEG7_SYMBOL \
|
||||
_MAP_48_57_ASCII_SEG7_NUMERIC \
|
||||
_MAP_58_64_ASCII_SEG7_SYMBOL \
|
||||
_MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
|
||||
_MAP_91_96_ASCII_SEG7_SYMBOL \
|
||||
_MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
|
||||
_MAP_123_126_ASCII_SEG7_SYMBOL
|
||||
|
||||
#define SEG7_DEFAULT_MAP(_name) \
|
||||
SEG7_CONVERSION_MAP(_name,MAP_ASCII7SEG_ALPHANUM)
|
||||
|
||||
#endif /* MAP_TO_7SEGMENT_H */
|
||||
|
||||
332
drivers/usb/input/mtouchusb.c
Normal file
332
drivers/usb/input/mtouchusb.c
Normal file
@@ -0,0 +1,332 @@
|
||||
/******************************************************************************
|
||||
* mtouchusb.c -- Driver for Microtouch (Now 3M) USB Touchscreens
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* Based upon original work by Radoslaw Garbacz (usb-support@ite.pl)
|
||||
* (http://freshmeat.net/projects/3mtouchscreendriver)
|
||||
*
|
||||
* History
|
||||
*
|
||||
* 0.3 & 0.4 2002 (TEJ) tejohnson@yahoo.com
|
||||
* Updated to 2.4.18, then 2.4.19
|
||||
* Old version still relied on stealing a minor
|
||||
*
|
||||
* 0.5 02/26/2004 (TEJ) tejohnson@yahoo.com
|
||||
* Complete rewrite using Linux Input in 2.6.3
|
||||
* Unfortunately no calibration support at this time
|
||||
*
|
||||
* 1.4 04/25/2004 (TEJ) tejohnson@yahoo.com
|
||||
* Changed reset from standard USB dev reset to vendor reset
|
||||
* Changed data sent to host from compensated to raw coordinates
|
||||
* Eliminated vendor/product module params
|
||||
* Performed multiple successful tests with an EXII-5010UC
|
||||
*
|
||||
* 1.5 02/27/2005 ddstreet@ieee.org
|
||||
* Added module parameter to select raw or hw-calibrated coordinate reporting
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
#define MTOUCHUSB_MIN_XC 0x0
|
||||
#define MTOUCHUSB_MAX_RAW_XC 0x4000
|
||||
#define MTOUCHUSB_MAX_CALIB_XC 0xffff
|
||||
#define MTOUCHUSB_XC_FUZZ 0x0
|
||||
#define MTOUCHUSB_XC_FLAT 0x0
|
||||
#define MTOUCHUSB_MIN_YC 0x0
|
||||
#define MTOUCHUSB_MAX_RAW_YC 0x4000
|
||||
#define MTOUCHUSB_MAX_CALIB_YC 0xffff
|
||||
#define MTOUCHUSB_YC_FUZZ 0x0
|
||||
#define MTOUCHUSB_YC_FLAT 0x0
|
||||
|
||||
#define MTOUCHUSB_ASYNC_REPORT 1
|
||||
#define MTOUCHUSB_RESET 7
|
||||
#define MTOUCHUSB_REPORT_DATA_SIZE 11
|
||||
#define MTOUCHUSB_REQ_CTRLLR_ID 10
|
||||
|
||||
#define MTOUCHUSB_GET_RAW_XC(data) (data[8]<<8 | data[7])
|
||||
#define MTOUCHUSB_GET_CALIB_XC(data) (data[4]<<8 | data[3])
|
||||
#define MTOUCHUSB_GET_RAW_YC(data) (data[10]<<8 | data[9])
|
||||
#define MTOUCHUSB_GET_CALIB_YC(data) (data[6]<<8 | data[5])
|
||||
#define MTOUCHUSB_GET_XC(data) (raw_coordinates ? \
|
||||
MTOUCHUSB_GET_RAW_XC(data) : \
|
||||
MTOUCHUSB_GET_CALIB_XC(data))
|
||||
#define MTOUCHUSB_GET_YC(data) (raw_coordinates ? \
|
||||
MTOUCHUSB_GET_RAW_YC(data) : \
|
||||
MTOUCHUSB_GET_CALIB_YC(data))
|
||||
#define MTOUCHUSB_GET_TOUCHED(data) ((data[2] & 0x40) ? 1:0)
|
||||
|
||||
#define DRIVER_VERSION "v1.5"
|
||||
#define DRIVER_AUTHOR "Todd E. Johnson, tejohnson@yahoo.com"
|
||||
#define DRIVER_DESC "3M USB Touchscreen Driver"
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
static int raw_coordinates = 1;
|
||||
|
||||
module_param(raw_coordinates, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(raw_coordinates, "report raw coordinate values (y, default) or hardware-calibrated coordinate values (n)");
|
||||
|
||||
struct mtouch_usb {
|
||||
unsigned char *data;
|
||||
dma_addr_t data_dma;
|
||||
struct urb *irq;
|
||||
struct usb_device *udev;
|
||||
struct input_dev *input;
|
||||
char name[128];
|
||||
char phys[64];
|
||||
};
|
||||
|
||||
static struct usb_device_id mtouchusb_devices[] = {
|
||||
{ USB_DEVICE(0x0596, 0x0001) },
|
||||
{ }
|
||||
};
|
||||
|
||||
static void mtouchusb_irq(struct urb *urb)
|
||||
{
|
||||
struct mtouch_usb *mtouch = urb->context;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ETIME:
|
||||
/* this urb is timing out */
|
||||
dbg("%s - urb timed out - was the device unplugged?",
|
||||
__FUNCTION__);
|
||||
return;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d",
|
||||
__FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d",
|
||||
__FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
input_report_key(mtouch->input, BTN_TOUCH,
|
||||
MTOUCHUSB_GET_TOUCHED(mtouch->data));
|
||||
input_report_abs(mtouch->input, ABS_X, MTOUCHUSB_GET_XC(mtouch->data));
|
||||
input_report_abs(mtouch->input, ABS_Y,
|
||||
(raw_coordinates ? MTOUCHUSB_MAX_RAW_YC : MTOUCHUSB_MAX_CALIB_YC)
|
||||
- MTOUCHUSB_GET_YC(mtouch->data));
|
||||
input_sync(mtouch->input);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err("%s - usb_submit_urb failed with result: %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
static int mtouchusb_open(struct input_dev *input)
|
||||
{
|
||||
struct mtouch_usb *mtouch = input->private;
|
||||
|
||||
mtouch->irq->dev = mtouch->udev;
|
||||
|
||||
if (usb_submit_urb(mtouch->irq, GFP_ATOMIC))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtouchusb_close(struct input_dev *input)
|
||||
{
|
||||
struct mtouch_usb *mtouch = input->private;
|
||||
|
||||
usb_kill_urb(mtouch->irq);
|
||||
}
|
||||
|
||||
static int mtouchusb_alloc_buffers(struct usb_device *udev, struct mtouch_usb *mtouch)
|
||||
{
|
||||
dbg("%s - called", __FUNCTION__);
|
||||
|
||||
mtouch->data = usb_buffer_alloc(udev, MTOUCHUSB_REPORT_DATA_SIZE,
|
||||
GFP_ATOMIC, &mtouch->data_dma);
|
||||
|
||||
if (!mtouch->data)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtouchusb_free_buffers(struct usb_device *udev, struct mtouch_usb *mtouch)
|
||||
{
|
||||
dbg("%s - called", __FUNCTION__);
|
||||
|
||||
if (mtouch->data)
|
||||
usb_buffer_free(udev, MTOUCHUSB_REPORT_DATA_SIZE,
|
||||
mtouch->data, mtouch->data_dma);
|
||||
}
|
||||
|
||||
static int mtouchusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct mtouch_usb *mtouch;
|
||||
struct input_dev *input_dev;
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
int nRet;
|
||||
|
||||
dbg("%s - called", __FUNCTION__);
|
||||
|
||||
dbg("%s - setting interface", __FUNCTION__);
|
||||
interface = intf->cur_altsetting;
|
||||
|
||||
dbg("%s - setting endpoint", __FUNCTION__);
|
||||
endpoint = &interface->endpoint[0].desc;
|
||||
|
||||
mtouch = kzalloc(sizeof(struct mtouch_usb), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!mtouch || !input_dev) {
|
||||
err("%s - Out of memory.", __FUNCTION__);
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
dbg("%s - allocating buffers", __FUNCTION__);
|
||||
if (mtouchusb_alloc_buffers(udev, mtouch))
|
||||
goto fail2;
|
||||
|
||||
mtouch->udev = udev;
|
||||
mtouch->input = input_dev;
|
||||
|
||||
if (udev->manufacturer)
|
||||
strlcpy(mtouch->name, udev->manufacturer, sizeof(mtouch->name));
|
||||
|
||||
if (udev->product) {
|
||||
if (udev->manufacturer)
|
||||
strlcat(mtouch->name, " ", sizeof(mtouch->name));
|
||||
strlcat(mtouch->name, udev->product, sizeof(mtouch->name));
|
||||
}
|
||||
|
||||
if (!strlen(mtouch->name))
|
||||
snprintf(mtouch->name, sizeof(mtouch->name),
|
||||
"USB Touchscreen %04x:%04x",
|
||||
le16_to_cpu(udev->descriptor.idVendor),
|
||||
le16_to_cpu(udev->descriptor.idProduct));
|
||||
|
||||
usb_make_path(udev, mtouch->phys, sizeof(mtouch->phys));
|
||||
strlcpy(mtouch->phys, "/input0", sizeof(mtouch->phys));
|
||||
|
||||
input_dev->name = mtouch->name;
|
||||
input_dev->phys = mtouch->phys;
|
||||
usb_to_input_id(udev, &input_dev->id);
|
||||
input_dev->cdev.dev = &intf->dev;
|
||||
input_dev->private = mtouch;
|
||||
|
||||
input_dev->open = mtouchusb_open;
|
||||
input_dev->close = mtouchusb_close;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||
input_set_abs_params(input_dev, ABS_X, MTOUCHUSB_MIN_XC,
|
||||
raw_coordinates ? MTOUCHUSB_MAX_RAW_XC : MTOUCHUSB_MAX_CALIB_XC,
|
||||
MTOUCHUSB_XC_FUZZ, MTOUCHUSB_XC_FLAT);
|
||||
input_set_abs_params(input_dev, ABS_Y, MTOUCHUSB_MIN_YC,
|
||||
raw_coordinates ? MTOUCHUSB_MAX_RAW_YC : MTOUCHUSB_MAX_CALIB_YC,
|
||||
MTOUCHUSB_YC_FUZZ, MTOUCHUSB_YC_FLAT);
|
||||
|
||||
nRet = usb_control_msg(mtouch->udev, usb_rcvctrlpipe(udev, 0),
|
||||
MTOUCHUSB_RESET,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
dbg("%s - usb_control_msg - MTOUCHUSB_RESET - bytes|err: %d",
|
||||
__FUNCTION__, nRet);
|
||||
|
||||
dbg("%s - usb_alloc_urb: mtouch->irq", __FUNCTION__);
|
||||
mtouch->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!mtouch->irq) {
|
||||
dbg("%s - usb_alloc_urb failed: mtouch->irq", __FUNCTION__);
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
dbg("%s - usb_fill_int_urb", __FUNCTION__);
|
||||
usb_fill_int_urb(mtouch->irq, mtouch->udev,
|
||||
usb_rcvintpipe(mtouch->udev, 0x81),
|
||||
mtouch->data, MTOUCHUSB_REPORT_DATA_SIZE,
|
||||
mtouchusb_irq, mtouch, endpoint->bInterval);
|
||||
|
||||
dbg("%s - input_register_device", __FUNCTION__);
|
||||
input_register_device(mtouch->input);
|
||||
|
||||
nRet = usb_control_msg(mtouch->udev, usb_rcvctrlpipe(udev, 0),
|
||||
MTOUCHUSB_ASYNC_REPORT,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
1, 1, NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d",
|
||||
__FUNCTION__, nRet);
|
||||
|
||||
usb_set_intfdata(intf, mtouch);
|
||||
return 0;
|
||||
|
||||
fail2: mtouchusb_free_buffers(udev, mtouch);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(mtouch);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void mtouchusb_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct mtouch_usb *mtouch = usb_get_intfdata(intf);
|
||||
|
||||
dbg("%s - called", __FUNCTION__);
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (mtouch) {
|
||||
dbg("%s - mtouch is initialized, cleaning up", __FUNCTION__);
|
||||
usb_kill_urb(mtouch->irq);
|
||||
input_unregister_device(mtouch->input);
|
||||
usb_free_urb(mtouch->irq);
|
||||
mtouchusb_free_buffers(interface_to_usbdev(intf), mtouch);
|
||||
kfree(mtouch);
|
||||
}
|
||||
}
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, mtouchusb_devices);
|
||||
|
||||
static struct usb_driver mtouchusb_driver = {
|
||||
.name = "mtouchusb",
|
||||
.probe = mtouchusb_probe,
|
||||
.disconnect = mtouchusb_disconnect,
|
||||
.id_table = mtouchusb_devices,
|
||||
};
|
||||
|
||||
static int __init mtouchusb_init(void)
|
||||
{
|
||||
dbg("%s - called", __FUNCTION__);
|
||||
return usb_register(&mtouchusb_driver);
|
||||
}
|
||||
|
||||
static void __exit mtouchusb_cleanup(void)
|
||||
{
|
||||
dbg("%s - called", __FUNCTION__);
|
||||
usb_deregister(&mtouchusb_driver);
|
||||
}
|
||||
|
||||
module_init(mtouchusb_init);
|
||||
module_exit(mtouchusb_cleanup);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
460
drivers/usb/input/powermate.c
Normal file
460
drivers/usb/input/powermate.c
Normal file
@@ -0,0 +1,460 @@
|
||||
/*
|
||||
* A driver for the Griffin Technology, Inc. "PowerMate" USB controller dial.
|
||||
*
|
||||
* v1.1, (c)2002 William R Sowerbutts <will@sowerbutts.com>
|
||||
*
|
||||
* This device is a anodised aluminium knob which connects over USB. It can measure
|
||||
* clockwise and anticlockwise rotation. The dial also acts as a pushbutton with
|
||||
* a spring for automatic release. The base contains a pair of LEDs which illuminate
|
||||
* the translucent base. It rotates without limit and reports its relative rotation
|
||||
* back to the host when polled by the USB controller.
|
||||
*
|
||||
* Testing with the knob I have has shown that it measures approximately 94 "clicks"
|
||||
* for one full rotation. Testing with my High Speed Rotation Actuator (ok, it was
|
||||
* a variable speed cordless electric drill) has shown that the device can measure
|
||||
* speeds of up to 7 clicks either clockwise or anticlockwise between pollings from
|
||||
* the host. If it counts more than 7 clicks before it is polled, it will wrap back
|
||||
* to zero and start counting again. This was at quite high speed, however, almost
|
||||
* certainly faster than the human hand could turn it. Griffin say that it loses a
|
||||
* pulse or two on a direction change; the granularity is so fine that I never
|
||||
* noticed this in practice.
|
||||
*
|
||||
* The device's microcontroller can be programmed to set the LED to either a constant
|
||||
* intensity, or to a rhythmic pulsing. Several patterns and speeds are available.
|
||||
*
|
||||
* Griffin were very happy to provide documentation and free hardware for development.
|
||||
*
|
||||
* Some userspace tools are available on the web: http://sowerbutts.com/powermate/
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
#define POWERMATE_VENDOR 0x077d /* Griffin Technology, Inc. */
|
||||
#define POWERMATE_PRODUCT_NEW 0x0410 /* Griffin PowerMate */
|
||||
#define POWERMATE_PRODUCT_OLD 0x04AA /* Griffin soundKnob */
|
||||
|
||||
#define CONTOUR_VENDOR 0x05f3 /* Contour Design, Inc. */
|
||||
#define CONTOUR_JOG 0x0240 /* Jog and Shuttle */
|
||||
|
||||
/* these are the command codes we send to the device */
|
||||
#define SET_STATIC_BRIGHTNESS 0x01
|
||||
#define SET_PULSE_ASLEEP 0x02
|
||||
#define SET_PULSE_AWAKE 0x03
|
||||
#define SET_PULSE_MODE 0x04
|
||||
|
||||
/* these refer to bits in the powermate_device's requires_update field. */
|
||||
#define UPDATE_STATIC_BRIGHTNESS (1<<0)
|
||||
#define UPDATE_PULSE_ASLEEP (1<<1)
|
||||
#define UPDATE_PULSE_AWAKE (1<<2)
|
||||
#define UPDATE_PULSE_MODE (1<<3)
|
||||
|
||||
/* at least two versions of the hardware exist, with differing payload
|
||||
sizes. the first three bytes always contain the "interesting" data in
|
||||
the relevant format. */
|
||||
#define POWERMATE_PAYLOAD_SIZE_MAX 6
|
||||
#define POWERMATE_PAYLOAD_SIZE_MIN 3
|
||||
struct powermate_device {
|
||||
signed char *data;
|
||||
dma_addr_t data_dma;
|
||||
struct urb *irq, *config;
|
||||
struct usb_ctrlrequest *configcr;
|
||||
dma_addr_t configcr_dma;
|
||||
struct usb_device *udev;
|
||||
struct input_dev *input;
|
||||
spinlock_t lock;
|
||||
int static_brightness;
|
||||
int pulse_speed;
|
||||
int pulse_table;
|
||||
int pulse_asleep;
|
||||
int pulse_awake;
|
||||
int requires_update; // physical settings which are out of sync
|
||||
char phys[64];
|
||||
};
|
||||
|
||||
static char pm_name_powermate[] = "Griffin PowerMate";
|
||||
static char pm_name_soundknob[] = "Griffin SoundKnob";
|
||||
|
||||
static void powermate_config_complete(struct urb *urb);
|
||||
|
||||
/* Callback for data arriving from the PowerMate over the USB interrupt pipe */
|
||||
static void powermate_irq(struct urb *urb)
|
||||
{
|
||||
struct powermate_device *pm = urb->context;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* handle updates to device state */
|
||||
input_report_key(pm->input, BTN_0, pm->data[0] & 0x01);
|
||||
input_report_rel(pm->input, REL_DIAL, pm->data[1]);
|
||||
input_sync(pm->input);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err ("%s - usb_submit_urb failed with result %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
/* Decide if we need to issue a control message and do so. Must be called with pm->lock taken */
|
||||
static void powermate_sync_state(struct powermate_device *pm)
|
||||
{
|
||||
if (pm->requires_update == 0)
|
||||
return; /* no updates are required */
|
||||
if (pm->config->status == -EINPROGRESS)
|
||||
return; /* an update is already in progress; it'll issue this update when it completes */
|
||||
|
||||
if (pm->requires_update & UPDATE_PULSE_ASLEEP){
|
||||
pm->configcr->wValue = cpu_to_le16( SET_PULSE_ASLEEP );
|
||||
pm->configcr->wIndex = cpu_to_le16( pm->pulse_asleep ? 1 : 0 );
|
||||
pm->requires_update &= ~UPDATE_PULSE_ASLEEP;
|
||||
}else if (pm->requires_update & UPDATE_PULSE_AWAKE){
|
||||
pm->configcr->wValue = cpu_to_le16( SET_PULSE_AWAKE );
|
||||
pm->configcr->wIndex = cpu_to_le16( pm->pulse_awake ? 1 : 0 );
|
||||
pm->requires_update &= ~UPDATE_PULSE_AWAKE;
|
||||
}else if (pm->requires_update & UPDATE_PULSE_MODE){
|
||||
int op, arg;
|
||||
/* the powermate takes an operation and an argument for its pulse algorithm.
|
||||
the operation can be:
|
||||
0: divide the speed
|
||||
1: pulse at normal speed
|
||||
2: multiply the speed
|
||||
the argument only has an effect for operations 0 and 2, and ranges between
|
||||
1 (least effect) to 255 (maximum effect).
|
||||
|
||||
thus, several states are equivalent and are coalesced into one state.
|
||||
|
||||
we map this onto a range from 0 to 510, with:
|
||||
0 -- 254 -- use divide (0 = slowest)
|
||||
255 -- use normal speed
|
||||
256 -- 510 -- use multiple (510 = fastest).
|
||||
|
||||
Only values of 'arg' quite close to 255 are particularly useful/spectacular.
|
||||
*/
|
||||
if (pm->pulse_speed < 255) {
|
||||
op = 0; // divide
|
||||
arg = 255 - pm->pulse_speed;
|
||||
} else if (pm->pulse_speed > 255) {
|
||||
op = 2; // multiply
|
||||
arg = pm->pulse_speed - 255;
|
||||
} else {
|
||||
op = 1; // normal speed
|
||||
arg = 0; // can be any value
|
||||
}
|
||||
pm->configcr->wValue = cpu_to_le16( (pm->pulse_table << 8) | SET_PULSE_MODE );
|
||||
pm->configcr->wIndex = cpu_to_le16( (arg << 8) | op );
|
||||
pm->requires_update &= ~UPDATE_PULSE_MODE;
|
||||
} else if (pm->requires_update & UPDATE_STATIC_BRIGHTNESS) {
|
||||
pm->configcr->wValue = cpu_to_le16( SET_STATIC_BRIGHTNESS );
|
||||
pm->configcr->wIndex = cpu_to_le16( pm->static_brightness );
|
||||
pm->requires_update &= ~UPDATE_STATIC_BRIGHTNESS;
|
||||
} else {
|
||||
printk(KERN_ERR "powermate: unknown update required");
|
||||
pm->requires_update = 0; /* fudge the bug */
|
||||
return;
|
||||
}
|
||||
|
||||
/* printk("powermate: %04x %04x\n", pm->configcr->wValue, pm->configcr->wIndex); */
|
||||
|
||||
pm->configcr->bRequestType = 0x41; /* vendor request */
|
||||
pm->configcr->bRequest = 0x01;
|
||||
pm->configcr->wLength = 0;
|
||||
|
||||
usb_fill_control_urb(pm->config, pm->udev, usb_sndctrlpipe(pm->udev, 0),
|
||||
(void *) pm->configcr, NULL, 0,
|
||||
powermate_config_complete, pm);
|
||||
pm->config->setup_dma = pm->configcr_dma;
|
||||
pm->config->transfer_flags |= URB_NO_SETUP_DMA_MAP;
|
||||
|
||||
if (usb_submit_urb(pm->config, GFP_ATOMIC))
|
||||
printk(KERN_ERR "powermate: usb_submit_urb(config) failed");
|
||||
}
|
||||
|
||||
/* Called when our asynchronous control message completes. We may need to issue another immediately */
|
||||
static void powermate_config_complete(struct urb *urb)
|
||||
{
|
||||
struct powermate_device *pm = urb->context;
|
||||
unsigned long flags;
|
||||
|
||||
if (urb->status)
|
||||
printk(KERN_ERR "powermate: config urb returned %d\n", urb->status);
|
||||
|
||||
spin_lock_irqsave(&pm->lock, flags);
|
||||
powermate_sync_state(pm);
|
||||
spin_unlock_irqrestore(&pm->lock, flags);
|
||||
}
|
||||
|
||||
/* Set the LED up as described and begin the sync with the hardware if required */
|
||||
static void powermate_pulse_led(struct powermate_device *pm, int static_brightness, int pulse_speed,
|
||||
int pulse_table, int pulse_asleep, int pulse_awake)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (pulse_speed < 0)
|
||||
pulse_speed = 0;
|
||||
if (pulse_table < 0)
|
||||
pulse_table = 0;
|
||||
if (pulse_speed > 510)
|
||||
pulse_speed = 510;
|
||||
if (pulse_table > 2)
|
||||
pulse_table = 2;
|
||||
|
||||
pulse_asleep = !!pulse_asleep;
|
||||
pulse_awake = !!pulse_awake;
|
||||
|
||||
|
||||
spin_lock_irqsave(&pm->lock, flags);
|
||||
|
||||
/* mark state updates which are required */
|
||||
if (static_brightness != pm->static_brightness) {
|
||||
pm->static_brightness = static_brightness;
|
||||
pm->requires_update |= UPDATE_STATIC_BRIGHTNESS;
|
||||
}
|
||||
if (pulse_asleep != pm->pulse_asleep) {
|
||||
pm->pulse_asleep = pulse_asleep;
|
||||
pm->requires_update |= (UPDATE_PULSE_ASLEEP | UPDATE_STATIC_BRIGHTNESS);
|
||||
}
|
||||
if (pulse_awake != pm->pulse_awake) {
|
||||
pm->pulse_awake = pulse_awake;
|
||||
pm->requires_update |= (UPDATE_PULSE_AWAKE | UPDATE_STATIC_BRIGHTNESS);
|
||||
}
|
||||
if (pulse_speed != pm->pulse_speed || pulse_table != pm->pulse_table) {
|
||||
pm->pulse_speed = pulse_speed;
|
||||
pm->pulse_table = pulse_table;
|
||||
pm->requires_update |= UPDATE_PULSE_MODE;
|
||||
}
|
||||
|
||||
powermate_sync_state(pm);
|
||||
|
||||
spin_unlock_irqrestore(&pm->lock, flags);
|
||||
}
|
||||
|
||||
/* Callback from the Input layer when an event arrives from userspace to configure the LED */
|
||||
static int powermate_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int _value)
|
||||
{
|
||||
unsigned int command = (unsigned int)_value;
|
||||
struct powermate_device *pm = dev->private;
|
||||
|
||||
if (type == EV_MSC && code == MSC_PULSELED){
|
||||
/*
|
||||
bits 0- 7: 8 bits: LED brightness
|
||||
bits 8-16: 9 bits: pulsing speed modifier (0 ... 510); 0-254 = slower, 255 = standard, 256-510 = faster.
|
||||
bits 17-18: 2 bits: pulse table (0, 1, 2 valid)
|
||||
bit 19: 1 bit : pulse whilst asleep?
|
||||
bit 20: 1 bit : pulse constantly?
|
||||
*/
|
||||
int static_brightness = command & 0xFF; // bits 0-7
|
||||
int pulse_speed = (command >> 8) & 0x1FF; // bits 8-16
|
||||
int pulse_table = (command >> 17) & 0x3; // bits 17-18
|
||||
int pulse_asleep = (command >> 19) & 0x1; // bit 19
|
||||
int pulse_awake = (command >> 20) & 0x1; // bit 20
|
||||
|
||||
powermate_pulse_led(pm, static_brightness, pulse_speed, pulse_table, pulse_asleep, pulse_awake);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_device *pm)
|
||||
{
|
||||
pm->data = usb_buffer_alloc(udev, POWERMATE_PAYLOAD_SIZE_MAX,
|
||||
GFP_ATOMIC, &pm->data_dma);
|
||||
if (!pm->data)
|
||||
return -1;
|
||||
|
||||
pm->configcr = usb_buffer_alloc(udev, sizeof(*(pm->configcr)),
|
||||
GFP_ATOMIC, &pm->configcr_dma);
|
||||
if (!pm->configcr)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void powermate_free_buffers(struct usb_device *udev, struct powermate_device *pm)
|
||||
{
|
||||
if (pm->data)
|
||||
usb_buffer_free(udev, POWERMATE_PAYLOAD_SIZE_MAX,
|
||||
pm->data, pm->data_dma);
|
||||
if (pm->configcr)
|
||||
usb_buffer_free(udev, sizeof(*(pm->configcr)),
|
||||
pm->configcr, pm->configcr_dma);
|
||||
}
|
||||
|
||||
/* Called whenever a USB device matching one in our supported devices table is connected */
|
||||
static int powermate_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev (intf);
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct powermate_device *pm;
|
||||
struct input_dev *input_dev;
|
||||
int pipe, maxp;
|
||||
int err = -ENOMEM;
|
||||
|
||||
interface = intf->cur_altsetting;
|
||||
endpoint = &interface->endpoint[0].desc;
|
||||
if (!usb_endpoint_is_int_in(endpoint))
|
||||
return -EIO;
|
||||
|
||||
usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||
0x0a, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
0, interface->desc.bInterfaceNumber, NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
pm = kzalloc(sizeof(struct powermate_device), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!pm || !input_dev)
|
||||
goto fail1;
|
||||
|
||||
if (powermate_alloc_buffers(udev, pm))
|
||||
goto fail2;
|
||||
|
||||
pm->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!pm->irq)
|
||||
goto fail2;
|
||||
|
||||
pm->config = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!pm->config)
|
||||
goto fail3;
|
||||
|
||||
pm->udev = udev;
|
||||
pm->input = input_dev;
|
||||
|
||||
usb_make_path(udev, pm->phys, sizeof(pm->phys));
|
||||
strlcpy(pm->phys, "/input0", sizeof(pm->phys));
|
||||
|
||||
spin_lock_init(&pm->lock);
|
||||
|
||||
switch (le16_to_cpu(udev->descriptor.idProduct)) {
|
||||
case POWERMATE_PRODUCT_NEW:
|
||||
input_dev->name = pm_name_powermate;
|
||||
break;
|
||||
case POWERMATE_PRODUCT_OLD:
|
||||
input_dev->name = pm_name_soundknob;
|
||||
break;
|
||||
default:
|
||||
input_dev->name = pm_name_soundknob;
|
||||
printk(KERN_WARNING "powermate: unknown product id %04x\n",
|
||||
le16_to_cpu(udev->descriptor.idProduct));
|
||||
}
|
||||
|
||||
input_dev->phys = pm->phys;
|
||||
usb_to_input_id(udev, &input_dev->id);
|
||||
input_dev->cdev.dev = &intf->dev;
|
||||
input_dev->private = pm;
|
||||
|
||||
input_dev->event = powermate_input_event;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_MSC);
|
||||
input_dev->keybit[LONG(BTN_0)] = BIT(BTN_0);
|
||||
input_dev->relbit[LONG(REL_DIAL)] = BIT(REL_DIAL);
|
||||
input_dev->mscbit[LONG(MSC_PULSELED)] = BIT(MSC_PULSELED);
|
||||
|
||||
/* get a handle to the interrupt data pipe */
|
||||
pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
|
||||
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
|
||||
|
||||
if (maxp < POWERMATE_PAYLOAD_SIZE_MIN || maxp > POWERMATE_PAYLOAD_SIZE_MAX) {
|
||||
printk(KERN_WARNING "powermate: Expected payload of %d--%d bytes, found %d bytes!\n",
|
||||
POWERMATE_PAYLOAD_SIZE_MIN, POWERMATE_PAYLOAD_SIZE_MAX, maxp);
|
||||
maxp = POWERMATE_PAYLOAD_SIZE_MAX;
|
||||
}
|
||||
|
||||
usb_fill_int_urb(pm->irq, udev, pipe, pm->data,
|
||||
maxp, powermate_irq,
|
||||
pm, endpoint->bInterval);
|
||||
pm->irq->transfer_dma = pm->data_dma;
|
||||
pm->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
/* register our interrupt URB with the USB system */
|
||||
if (usb_submit_urb(pm->irq, GFP_KERNEL)) {
|
||||
err = -EIO;
|
||||
goto fail4;
|
||||
}
|
||||
|
||||
input_register_device(pm->input);
|
||||
|
||||
/* force an update of everything */
|
||||
pm->requires_update = UPDATE_PULSE_ASLEEP | UPDATE_PULSE_AWAKE | UPDATE_PULSE_MODE | UPDATE_STATIC_BRIGHTNESS;
|
||||
powermate_pulse_led(pm, 0x80, 255, 0, 1, 0); // set default pulse parameters
|
||||
|
||||
usb_set_intfdata(intf, pm);
|
||||
return 0;
|
||||
|
||||
fail4: usb_free_urb(pm->config);
|
||||
fail3: usb_free_urb(pm->irq);
|
||||
fail2: powermate_free_buffers(udev, pm);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(pm);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Called when a USB device we've accepted ownership of is removed */
|
||||
static void powermate_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct powermate_device *pm = usb_get_intfdata (intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (pm) {
|
||||
pm->requires_update = 0;
|
||||
usb_kill_urb(pm->irq);
|
||||
input_unregister_device(pm->input);
|
||||
usb_free_urb(pm->irq);
|
||||
usb_free_urb(pm->config);
|
||||
powermate_free_buffers(interface_to_usbdev(intf), pm);
|
||||
|
||||
kfree(pm);
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_device_id powermate_devices [] = {
|
||||
{ USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_NEW) },
|
||||
{ USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_OLD) },
|
||||
{ USB_DEVICE(CONTOUR_VENDOR, CONTOUR_JOG) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE (usb, powermate_devices);
|
||||
|
||||
static struct usb_driver powermate_driver = {
|
||||
.name = "powermate",
|
||||
.probe = powermate_probe,
|
||||
.disconnect = powermate_disconnect,
|
||||
.id_table = powermate_devices,
|
||||
};
|
||||
|
||||
static int __init powermate_init(void)
|
||||
{
|
||||
return usb_register(&powermate_driver);
|
||||
}
|
||||
|
||||
static void __exit powermate_cleanup(void)
|
||||
{
|
||||
usb_deregister(&powermate_driver);
|
||||
}
|
||||
|
||||
module_init(powermate_init);
|
||||
module_exit(powermate_cleanup);
|
||||
|
||||
MODULE_AUTHOR( "William R Sowerbutts" );
|
||||
MODULE_DESCRIPTION( "Griffin Technology, Inc PowerMate driver" );
|
||||
MODULE_LICENSE("GPL");
|
||||
392
drivers/usb/input/touchkitusb.c
Normal file
392
drivers/usb/input/touchkitusb.c
Normal file
@@ -0,0 +1,392 @@
|
||||
/******************************************************************************
|
||||
* touchkitusb.c -- Driver for eGalax TouchKit USB Touchscreens
|
||||
*
|
||||
* Copyright (C) 2004-2005 by Daniel Ritz <daniel.ritz@gmx.ch>
|
||||
* Copyright (C) by Todd E. Johnson (mtouchusb.c)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* Based upon mtouchusb.c
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
#define TOUCHKIT_MIN_XC 0x0
|
||||
#define TOUCHKIT_MAX_XC 0x07ff
|
||||
#define TOUCHKIT_XC_FUZZ 0x0
|
||||
#define TOUCHKIT_XC_FLAT 0x0
|
||||
#define TOUCHKIT_MIN_YC 0x0
|
||||
#define TOUCHKIT_MAX_YC 0x07ff
|
||||
#define TOUCHKIT_YC_FUZZ 0x0
|
||||
#define TOUCHKIT_YC_FLAT 0x0
|
||||
#define TOUCHKIT_REPORT_DATA_SIZE 16
|
||||
|
||||
#define TOUCHKIT_DOWN 0x01
|
||||
|
||||
#define TOUCHKIT_PKT_TYPE_MASK 0xFE
|
||||
#define TOUCHKIT_PKT_TYPE_REPT 0x80
|
||||
#define TOUCHKIT_PKT_TYPE_DIAG 0x0A
|
||||
|
||||
#define DRIVER_VERSION "v0.1"
|
||||
#define DRIVER_AUTHOR "Daniel Ritz <daniel.ritz@gmx.ch>"
|
||||
#define DRIVER_DESC "eGalax TouchKit USB HID Touchscreen Driver"
|
||||
|
||||
static int swap_xy;
|
||||
module_param(swap_xy, bool, 0644);
|
||||
MODULE_PARM_DESC(swap_xy, "If set X and Y axes are swapped.");
|
||||
|
||||
struct touchkit_usb {
|
||||
unsigned char *data;
|
||||
dma_addr_t data_dma;
|
||||
char buffer[TOUCHKIT_REPORT_DATA_SIZE];
|
||||
int buf_len;
|
||||
struct urb *irq;
|
||||
struct usb_device *udev;
|
||||
struct input_dev *input;
|
||||
char name[128];
|
||||
char phys[64];
|
||||
};
|
||||
|
||||
static struct usb_device_id touchkit_devices[] = {
|
||||
{USB_DEVICE(0x3823, 0x0001)},
|
||||
{USB_DEVICE(0x0123, 0x0001)},
|
||||
{USB_DEVICE(0x0eef, 0x0001)},
|
||||
{USB_DEVICE(0x0eef, 0x0002)},
|
||||
{}
|
||||
};
|
||||
|
||||
/* helpers to read the data */
|
||||
static inline int touchkit_get_touched(char *data)
|
||||
{
|
||||
return (data[0] & TOUCHKIT_DOWN) ? 1 : 0;
|
||||
}
|
||||
|
||||
static inline int touchkit_get_x(char *data)
|
||||
{
|
||||
return ((data[3] & 0x0F) << 7) | (data[4] & 0x7F);
|
||||
}
|
||||
|
||||
static inline int touchkit_get_y(char *data)
|
||||
{
|
||||
return ((data[1] & 0x0F) << 7) | (data[2] & 0x7F);
|
||||
}
|
||||
|
||||
|
||||
/* processes one input packet. */
|
||||
static void touchkit_process_pkt(struct touchkit_usb *touchkit, char *pkt)
|
||||
{
|
||||
int x, y;
|
||||
|
||||
/* only process report packets */
|
||||
if ((pkt[0] & TOUCHKIT_PKT_TYPE_MASK) != TOUCHKIT_PKT_TYPE_REPT)
|
||||
return;
|
||||
|
||||
if (swap_xy) {
|
||||
y = touchkit_get_x(pkt);
|
||||
x = touchkit_get_y(pkt);
|
||||
} else {
|
||||
x = touchkit_get_x(pkt);
|
||||
y = touchkit_get_y(pkt);
|
||||
}
|
||||
|
||||
input_report_key(touchkit->input, BTN_TOUCH, touchkit_get_touched(pkt));
|
||||
input_report_abs(touchkit->input, ABS_X, x);
|
||||
input_report_abs(touchkit->input, ABS_Y, y);
|
||||
input_sync(touchkit->input);
|
||||
}
|
||||
|
||||
|
||||
static int touchkit_get_pkt_len(char *buf)
|
||||
{
|
||||
switch (buf[0] & TOUCHKIT_PKT_TYPE_MASK) {
|
||||
case TOUCHKIT_PKT_TYPE_REPT:
|
||||
return 5;
|
||||
|
||||
case TOUCHKIT_PKT_TYPE_DIAG:
|
||||
return buf[1] + 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void touchkit_process(struct touchkit_usb *touchkit, int len)
|
||||
{
|
||||
char *buffer;
|
||||
int pkt_len, buf_len, pos;
|
||||
|
||||
/* if the buffer contains data, append */
|
||||
if (unlikely(touchkit->buf_len)) {
|
||||
int tmp;
|
||||
|
||||
/* if only 1 byte in buffer, add another one to get length */
|
||||
if (touchkit->buf_len == 1)
|
||||
touchkit->buffer[1] = touchkit->data[0];
|
||||
|
||||
pkt_len = touchkit_get_pkt_len(touchkit->buffer);
|
||||
|
||||
/* unknown packet: drop everything */
|
||||
if (!pkt_len)
|
||||
return;
|
||||
|
||||
/* append, process */
|
||||
tmp = pkt_len - touchkit->buf_len;
|
||||
memcpy(touchkit->buffer + touchkit->buf_len, touchkit->data, tmp);
|
||||
touchkit_process_pkt(touchkit, touchkit->buffer);
|
||||
|
||||
buffer = touchkit->data + tmp;
|
||||
buf_len = len - tmp;
|
||||
} else {
|
||||
buffer = touchkit->data;
|
||||
buf_len = len;
|
||||
}
|
||||
|
||||
/* only one byte left in buffer */
|
||||
if (unlikely(buf_len == 1)) {
|
||||
touchkit->buffer[0] = buffer[0];
|
||||
touchkit->buf_len = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* loop over the buffer */
|
||||
pos = 0;
|
||||
while (pos < buf_len) {
|
||||
/* get packet len */
|
||||
pkt_len = touchkit_get_pkt_len(buffer + pos);
|
||||
|
||||
/* unknown packet: drop everything */
|
||||
if (unlikely(!pkt_len))
|
||||
return;
|
||||
|
||||
/* full packet: process */
|
||||
if (likely(pkt_len <= buf_len)) {
|
||||
touchkit_process_pkt(touchkit, buffer + pos);
|
||||
} else {
|
||||
/* incomplete packet: save in buffer */
|
||||
memcpy(touchkit->buffer, buffer + pos, buf_len - pos);
|
||||
touchkit->buf_len = buf_len - pos;
|
||||
}
|
||||
pos += pkt_len;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void touchkit_irq(struct urb *urb)
|
||||
{
|
||||
struct touchkit_usb *touchkit = urb->context;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ETIME:
|
||||
/* this urb is timing out */
|
||||
dbg("%s - urb timed out - was the device unplugged?",
|
||||
__FUNCTION__);
|
||||
return;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d",
|
||||
__FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d",
|
||||
__FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
touchkit_process(touchkit, urb->actual_length);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err("%s - usb_submit_urb failed with result: %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
static int touchkit_open(struct input_dev *input)
|
||||
{
|
||||
struct touchkit_usb *touchkit = input->private;
|
||||
|
||||
touchkit->irq->dev = touchkit->udev;
|
||||
|
||||
if (usb_submit_urb(touchkit->irq, GFP_ATOMIC))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void touchkit_close(struct input_dev *input)
|
||||
{
|
||||
struct touchkit_usb *touchkit = input->private;
|
||||
|
||||
usb_kill_urb(touchkit->irq);
|
||||
}
|
||||
|
||||
static int touchkit_alloc_buffers(struct usb_device *udev,
|
||||
struct touchkit_usb *touchkit)
|
||||
{
|
||||
touchkit->data = usb_buffer_alloc(udev, TOUCHKIT_REPORT_DATA_SIZE,
|
||||
GFP_ATOMIC, &touchkit->data_dma);
|
||||
|
||||
if (!touchkit->data)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void touchkit_free_buffers(struct usb_device *udev,
|
||||
struct touchkit_usb *touchkit)
|
||||
{
|
||||
if (touchkit->data)
|
||||
usb_buffer_free(udev, TOUCHKIT_REPORT_DATA_SIZE,
|
||||
touchkit->data, touchkit->data_dma);
|
||||
}
|
||||
|
||||
static int touchkit_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct touchkit_usb *touchkit;
|
||||
struct input_dev *input_dev;
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
|
||||
interface = intf->cur_altsetting;
|
||||
endpoint = &interface->endpoint[0].desc;
|
||||
|
||||
touchkit = kzalloc(sizeof(struct touchkit_usb), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!touchkit || !input_dev)
|
||||
goto out_free;
|
||||
|
||||
if (touchkit_alloc_buffers(udev, touchkit))
|
||||
goto out_free;
|
||||
|
||||
touchkit->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!touchkit->irq) {
|
||||
dbg("%s - usb_alloc_urb failed: touchkit->irq", __FUNCTION__);
|
||||
goto out_free_buffers;
|
||||
}
|
||||
|
||||
touchkit->udev = udev;
|
||||
touchkit->input = input_dev;
|
||||
|
||||
if (udev->manufacturer)
|
||||
strlcpy(touchkit->name, udev->manufacturer, sizeof(touchkit->name));
|
||||
|
||||
if (udev->product) {
|
||||
if (udev->manufacturer)
|
||||
strlcat(touchkit->name, " ", sizeof(touchkit->name));
|
||||
strlcat(touchkit->name, udev->product, sizeof(touchkit->name));
|
||||
}
|
||||
|
||||
if (!strlen(touchkit->name))
|
||||
snprintf(touchkit->name, sizeof(touchkit->name),
|
||||
"USB Touchscreen %04x:%04x",
|
||||
le16_to_cpu(udev->descriptor.idVendor),
|
||||
le16_to_cpu(udev->descriptor.idProduct));
|
||||
|
||||
usb_make_path(udev, touchkit->phys, sizeof(touchkit->phys));
|
||||
strlcpy(touchkit->phys, "/input0", sizeof(touchkit->phys));
|
||||
|
||||
input_dev->name = touchkit->name;
|
||||
input_dev->phys = touchkit->phys;
|
||||
usb_to_input_id(udev, &input_dev->id);
|
||||
input_dev->cdev.dev = &intf->dev;
|
||||
input_dev->private = touchkit;
|
||||
input_dev->open = touchkit_open;
|
||||
input_dev->close = touchkit_close;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||
input_set_abs_params(input_dev, ABS_X, TOUCHKIT_MIN_XC, TOUCHKIT_MAX_XC,
|
||||
TOUCHKIT_XC_FUZZ, TOUCHKIT_XC_FLAT);
|
||||
input_set_abs_params(input_dev, ABS_Y, TOUCHKIT_MIN_YC, TOUCHKIT_MAX_YC,
|
||||
TOUCHKIT_YC_FUZZ, TOUCHKIT_YC_FLAT);
|
||||
|
||||
usb_fill_int_urb(touchkit->irq, touchkit->udev,
|
||||
usb_rcvintpipe(touchkit->udev, 0x81),
|
||||
touchkit->data, TOUCHKIT_REPORT_DATA_SIZE,
|
||||
touchkit_irq, touchkit, endpoint->bInterval);
|
||||
|
||||
touchkit->irq->transfer_dma = touchkit->data_dma;
|
||||
touchkit->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
input_register_device(touchkit->input);
|
||||
|
||||
usb_set_intfdata(intf, touchkit);
|
||||
return 0;
|
||||
|
||||
out_free_buffers:
|
||||
touchkit_free_buffers(udev, touchkit);
|
||||
out_free:
|
||||
input_free_device(input_dev);
|
||||
kfree(touchkit);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void touchkit_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct touchkit_usb *touchkit = usb_get_intfdata(intf);
|
||||
|
||||
dbg("%s - called", __FUNCTION__);
|
||||
|
||||
if (!touchkit)
|
||||
return;
|
||||
|
||||
dbg("%s - touchkit is initialized, cleaning up", __FUNCTION__);
|
||||
usb_set_intfdata(intf, NULL);
|
||||
usb_kill_urb(touchkit->irq);
|
||||
input_unregister_device(touchkit->input);
|
||||
usb_free_urb(touchkit->irq);
|
||||
touchkit_free_buffers(interface_to_usbdev(intf), touchkit);
|
||||
kfree(touchkit);
|
||||
}
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, touchkit_devices);
|
||||
|
||||
static struct usb_driver touchkit_driver = {
|
||||
.name = "touchkitusb",
|
||||
.probe = touchkit_probe,
|
||||
.disconnect = touchkit_disconnect,
|
||||
.id_table = touchkit_devices,
|
||||
};
|
||||
|
||||
static int __init touchkit_init(void)
|
||||
{
|
||||
return usb_register(&touchkit_driver);
|
||||
}
|
||||
|
||||
static void __exit touchkit_cleanup(void)
|
||||
{
|
||||
usb_deregister(&touchkit_driver);
|
||||
}
|
||||
|
||||
module_init(touchkit_init);
|
||||
module_exit(touchkit_cleanup);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
87
drivers/usb/input/usbhid.h
Normal file
87
drivers/usb/input/usbhid.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#ifndef __USBHID_H
|
||||
#define __USBHID_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 1999 Andreas Gal
|
||||
* Copyright (c) 2000-2001 Vojtech Pavlik
|
||||
* Copyright (c) 2006 Jiri Kosina
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
/* API provided by hid-core.c for USB HID drivers */
|
||||
int usbhid_wait_io(struct hid_device* hid);
|
||||
void usbhid_close(struct hid_device *hid);
|
||||
int usbhid_open(struct hid_device *hid);
|
||||
void usbhid_init_reports(struct hid_device *hid);
|
||||
void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir);
|
||||
|
||||
/*
|
||||
* USB-specific HID struct, to be pointed to
|
||||
* from struct hid_device->driver_data
|
||||
*/
|
||||
|
||||
struct usbhid_device {
|
||||
struct hid_device *hid; /* pointer to corresponding HID dev */
|
||||
|
||||
struct usb_interface *intf; /* USB interface */
|
||||
int ifnum; /* USB interface number */
|
||||
|
||||
unsigned int bufsize; /* URB buffer size */
|
||||
|
||||
struct urb *urbin; /* Input URB */
|
||||
char *inbuf; /* Input buffer */
|
||||
dma_addr_t inbuf_dma; /* Input buffer dma */
|
||||
spinlock_t inlock; /* Input fifo spinlock */
|
||||
|
||||
struct urb *urbctrl; /* Control URB */
|
||||
struct usb_ctrlrequest *cr; /* Control request struct */
|
||||
dma_addr_t cr_dma; /* Control request struct dma */
|
||||
struct hid_control_fifo ctrl[HID_CONTROL_FIFO_SIZE]; /* Control fifo */
|
||||
unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */
|
||||
char *ctrlbuf; /* Control buffer */
|
||||
dma_addr_t ctrlbuf_dma; /* Control buffer dma */
|
||||
spinlock_t ctrllock; /* Control fifo spinlock */
|
||||
|
||||
struct urb *urbout; /* Output URB */
|
||||
struct hid_report *out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */
|
||||
unsigned char outhead, outtail; /* Output pipe fifo head & tail */
|
||||
char *outbuf; /* Output buffer */
|
||||
dma_addr_t outbuf_dma; /* Output buffer dma */
|
||||
spinlock_t outlock; /* Output fifo spinlock */
|
||||
|
||||
unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
|
||||
struct timer_list io_retry; /* Retry timer */
|
||||
unsigned long stop_retry; /* Time to give up, in jiffies */
|
||||
unsigned int retry_delay; /* Delay length in ms */
|
||||
struct work_struct reset_work; /* Task context for resets */
|
||||
|
||||
};
|
||||
|
||||
#define hid_to_usb_dev(hid_dev) \
|
||||
container_of(hid_dev->dev->parent, struct usb_device, dev)
|
||||
|
||||
#endif
|
||||
|
||||
362
drivers/usb/input/usbkbd.c
Normal file
362
drivers/usb/input/usbkbd.c
Normal file
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
* $Id: usbkbd.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Copyright (c) 1999-2001 Vojtech Pavlik
|
||||
*
|
||||
* USB HIDBP Keyboard support
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Should you need to contact me, the author, you can do so either by
|
||||
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
|
||||
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb/input.h>
|
||||
#include <linux/hid.h>
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
*/
|
||||
#define DRIVER_VERSION ""
|
||||
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
|
||||
#define DRIVER_DESC "USB HID Boot Protocol keyboard driver"
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE(DRIVER_LICENSE);
|
||||
|
||||
static unsigned char usb_kbd_keycode[256] = {
|
||||
0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
|
||||
50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
|
||||
4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
|
||||
27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
|
||||
65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
|
||||
105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
|
||||
72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
|
||||
191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
|
||||
115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
|
||||
122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
|
||||
150,158,159,128,136,177,178,176,142,152,173,140
|
||||
};
|
||||
|
||||
struct usb_kbd {
|
||||
struct input_dev *dev;
|
||||
struct usb_device *usbdev;
|
||||
unsigned char old[8];
|
||||
struct urb *irq, *led;
|
||||
unsigned char newleds;
|
||||
char name[128];
|
||||
char phys[64];
|
||||
|
||||
unsigned char *new;
|
||||
struct usb_ctrlrequest *cr;
|
||||
unsigned char *leds;
|
||||
dma_addr_t cr_dma;
|
||||
dma_addr_t new_dma;
|
||||
dma_addr_t leds_dma;
|
||||
};
|
||||
|
||||
static void usb_kbd_irq(struct urb *urb)
|
||||
{
|
||||
struct usb_kbd *kbd = urb->context;
|
||||
int i;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0: /* success */
|
||||
break;
|
||||
case -ECONNRESET: /* unlink */
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
return;
|
||||
/* -EPIPE: should clear the halt */
|
||||
default: /* error */
|
||||
goto resubmit;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);
|
||||
|
||||
for (i = 2; i < 8; i++) {
|
||||
|
||||
if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
|
||||
if (usb_kbd_keycode[kbd->old[i]])
|
||||
input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);
|
||||
else
|
||||
info("Unknown key (scancode %#x) released.", kbd->old[i]);
|
||||
}
|
||||
|
||||
if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
|
||||
if (usb_kbd_keycode[kbd->new[i]])
|
||||
input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);
|
||||
else
|
||||
info("Unknown key (scancode %#x) pressed.", kbd->new[i]);
|
||||
}
|
||||
}
|
||||
|
||||
input_sync(kbd->dev);
|
||||
|
||||
memcpy(kbd->old, kbd->new, 8);
|
||||
|
||||
resubmit:
|
||||
i = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (i)
|
||||
err ("can't resubmit intr, %s-%s/input0, status %d",
|
||||
kbd->usbdev->bus->bus_name,
|
||||
kbd->usbdev->devpath, i);
|
||||
}
|
||||
|
||||
static int usb_kbd_event(struct input_dev *dev, unsigned int type,
|
||||
unsigned int code, int value)
|
||||
{
|
||||
struct usb_kbd *kbd = dev->private;
|
||||
|
||||
if (type != EV_LED)
|
||||
return -1;
|
||||
|
||||
|
||||
kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
|
||||
(!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) |
|
||||
(!!test_bit(LED_NUML, dev->led));
|
||||
|
||||
if (kbd->led->status == -EINPROGRESS)
|
||||
return 0;
|
||||
|
||||
if (*(kbd->leds) == kbd->newleds)
|
||||
return 0;
|
||||
|
||||
*(kbd->leds) = kbd->newleds;
|
||||
kbd->led->dev = kbd->usbdev;
|
||||
if (usb_submit_urb(kbd->led, GFP_ATOMIC))
|
||||
err("usb_submit_urb(leds) failed");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_kbd_led(struct urb *urb)
|
||||
{
|
||||
struct usb_kbd *kbd = urb->context;
|
||||
|
||||
if (urb->status)
|
||||
warn("led urb status %d received", urb->status);
|
||||
|
||||
if (*(kbd->leds) == kbd->newleds)
|
||||
return;
|
||||
|
||||
*(kbd->leds) = kbd->newleds;
|
||||
kbd->led->dev = kbd->usbdev;
|
||||
if (usb_submit_urb(kbd->led, GFP_ATOMIC))
|
||||
err("usb_submit_urb(leds) failed");
|
||||
}
|
||||
|
||||
static int usb_kbd_open(struct input_dev *dev)
|
||||
{
|
||||
struct usb_kbd *kbd = dev->private;
|
||||
|
||||
kbd->irq->dev = kbd->usbdev;
|
||||
if (usb_submit_urb(kbd->irq, GFP_KERNEL))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_kbd_close(struct input_dev *dev)
|
||||
{
|
||||
struct usb_kbd *kbd = dev->private;
|
||||
|
||||
usb_kill_urb(kbd->irq);
|
||||
}
|
||||
|
||||
static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)
|
||||
{
|
||||
if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))
|
||||
return -1;
|
||||
if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))
|
||||
return -1;
|
||||
if (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma)))
|
||||
return -1;
|
||||
if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma)))
|
||||
return -1;
|
||||
if (!(kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma)))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)
|
||||
{
|
||||
usb_free_urb(kbd->irq);
|
||||
usb_free_urb(kbd->led);
|
||||
if (kbd->new)
|
||||
usb_buffer_free(dev, 8, kbd->new, kbd->new_dma);
|
||||
if (kbd->cr)
|
||||
usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma);
|
||||
if (kbd->leds)
|
||||
usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma);
|
||||
}
|
||||
|
||||
static int usb_kbd_probe(struct usb_interface *iface,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(iface);
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct usb_kbd *kbd;
|
||||
struct input_dev *input_dev;
|
||||
int i, pipe, maxp;
|
||||
|
||||
interface = iface->cur_altsetting;
|
||||
|
||||
if (interface->desc.bNumEndpoints != 1)
|
||||
return -ENODEV;
|
||||
|
||||
endpoint = &interface->endpoint[0].desc;
|
||||
if (!usb_endpoint_is_int_in(endpoint))
|
||||
return -ENODEV;
|
||||
|
||||
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
|
||||
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
|
||||
|
||||
kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!kbd || !input_dev)
|
||||
goto fail1;
|
||||
|
||||
if (usb_kbd_alloc_mem(dev, kbd))
|
||||
goto fail2;
|
||||
|
||||
kbd->usbdev = dev;
|
||||
kbd->dev = input_dev;
|
||||
|
||||
if (dev->manufacturer)
|
||||
strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
|
||||
|
||||
if (dev->product) {
|
||||
if (dev->manufacturer)
|
||||
strlcat(kbd->name, " ", sizeof(kbd->name));
|
||||
strlcat(kbd->name, dev->product, sizeof(kbd->name));
|
||||
}
|
||||
|
||||
if (!strlen(kbd->name))
|
||||
snprintf(kbd->name, sizeof(kbd->name),
|
||||
"USB HIDBP Keyboard %04x:%04x",
|
||||
le16_to_cpu(dev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->descriptor.idProduct));
|
||||
|
||||
usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
|
||||
strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));
|
||||
|
||||
input_dev->name = kbd->name;
|
||||
input_dev->phys = kbd->phys;
|
||||
usb_to_input_id(dev, &input_dev->id);
|
||||
input_dev->cdev.dev = &iface->dev;
|
||||
input_dev->private = kbd;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
|
||||
input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA);
|
||||
|
||||
for (i = 0; i < 255; i++)
|
||||
set_bit(usb_kbd_keycode[i], input_dev->keybit);
|
||||
clear_bit(0, input_dev->keybit);
|
||||
|
||||
input_dev->event = usb_kbd_event;
|
||||
input_dev->open = usb_kbd_open;
|
||||
input_dev->close = usb_kbd_close;
|
||||
|
||||
usb_fill_int_urb(kbd->irq, dev, pipe,
|
||||
kbd->new, (maxp > 8 ? 8 : maxp),
|
||||
usb_kbd_irq, kbd, endpoint->bInterval);
|
||||
kbd->irq->transfer_dma = kbd->new_dma;
|
||||
kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
|
||||
kbd->cr->bRequest = 0x09;
|
||||
kbd->cr->wValue = cpu_to_le16(0x200);
|
||||
kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
|
||||
kbd->cr->wLength = cpu_to_le16(1);
|
||||
|
||||
usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),
|
||||
(void *) kbd->cr, kbd->leds, 1,
|
||||
usb_kbd_led, kbd);
|
||||
kbd->led->setup_dma = kbd->cr_dma;
|
||||
kbd->led->transfer_dma = kbd->leds_dma;
|
||||
kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
|
||||
|
||||
input_register_device(kbd->dev);
|
||||
|
||||
usb_set_intfdata(iface, kbd);
|
||||
return 0;
|
||||
|
||||
fail2: usb_kbd_free_mem(dev, kbd);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(kbd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void usb_kbd_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_kbd *kbd = usb_get_intfdata (intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (kbd) {
|
||||
usb_kill_urb(kbd->irq);
|
||||
input_unregister_device(kbd->dev);
|
||||
usb_kbd_free_mem(interface_to_usbdev(intf), kbd);
|
||||
kfree(kbd);
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_device_id usb_kbd_id_table [] = {
|
||||
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
|
||||
USB_INTERFACE_PROTOCOL_KEYBOARD) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);
|
||||
|
||||
static struct usb_driver usb_kbd_driver = {
|
||||
.name = "usbkbd",
|
||||
.probe = usb_kbd_probe,
|
||||
.disconnect = usb_kbd_disconnect,
|
||||
.id_table = usb_kbd_id_table,
|
||||
};
|
||||
|
||||
static int __init usb_kbd_init(void)
|
||||
{
|
||||
int result = usb_register(&usb_kbd_driver);
|
||||
if (result == 0)
|
||||
info(DRIVER_VERSION ":" DRIVER_DESC);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void __exit usb_kbd_exit(void)
|
||||
{
|
||||
usb_deregister(&usb_kbd_driver);
|
||||
}
|
||||
|
||||
module_init(usb_kbd_init);
|
||||
module_exit(usb_kbd_exit);
|
||||
245
drivers/usb/input/usbmouse.c
Normal file
245
drivers/usb/input/usbmouse.c
Normal file
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* $Id: usbmouse.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Copyright (c) 1999-2001 Vojtech Pavlik
|
||||
*
|
||||
* USB HIDBP Mouse support
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Should you need to contact me, the author, you can do so either by
|
||||
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
|
||||
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb/input.h>
|
||||
#include <linux/hid.h>
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
*/
|
||||
#define DRIVER_VERSION "v1.6"
|
||||
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
|
||||
#define DRIVER_DESC "USB HID Boot Protocol mouse driver"
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE(DRIVER_LICENSE);
|
||||
|
||||
struct usb_mouse {
|
||||
char name[128];
|
||||
char phys[64];
|
||||
struct usb_device *usbdev;
|
||||
struct input_dev *dev;
|
||||
struct urb *irq;
|
||||
|
||||
signed char *data;
|
||||
dma_addr_t data_dma;
|
||||
};
|
||||
|
||||
static void usb_mouse_irq(struct urb *urb)
|
||||
{
|
||||
struct usb_mouse *mouse = urb->context;
|
||||
signed char *data = mouse->data;
|
||||
struct input_dev *dev = mouse->dev;
|
||||
int status;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0: /* success */
|
||||
break;
|
||||
case -ECONNRESET: /* unlink */
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
return;
|
||||
/* -EPIPE: should clear the halt */
|
||||
default: /* error */
|
||||
goto resubmit;
|
||||
}
|
||||
|
||||
input_report_key(dev, BTN_LEFT, data[0] & 0x01);
|
||||
input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
|
||||
input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);
|
||||
input_report_key(dev, BTN_SIDE, data[0] & 0x08);
|
||||
input_report_key(dev, BTN_EXTRA, data[0] & 0x10);
|
||||
|
||||
input_report_rel(dev, REL_X, data[1]);
|
||||
input_report_rel(dev, REL_Y, data[2]);
|
||||
input_report_rel(dev, REL_WHEEL, data[3]);
|
||||
|
||||
input_sync(dev);
|
||||
resubmit:
|
||||
status = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (status)
|
||||
err ("can't resubmit intr, %s-%s/input0, status %d",
|
||||
mouse->usbdev->bus->bus_name,
|
||||
mouse->usbdev->devpath, status);
|
||||
}
|
||||
|
||||
static int usb_mouse_open(struct input_dev *dev)
|
||||
{
|
||||
struct usb_mouse *mouse = dev->private;
|
||||
|
||||
mouse->irq->dev = mouse->usbdev;
|
||||
if (usb_submit_urb(mouse->irq, GFP_KERNEL))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_mouse_close(struct input_dev *dev)
|
||||
{
|
||||
struct usb_mouse *mouse = dev->private;
|
||||
|
||||
usb_kill_urb(mouse->irq);
|
||||
}
|
||||
|
||||
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct usb_mouse *mouse;
|
||||
struct input_dev *input_dev;
|
||||
int pipe, maxp;
|
||||
|
||||
interface = intf->cur_altsetting;
|
||||
|
||||
if (interface->desc.bNumEndpoints != 1)
|
||||
return -ENODEV;
|
||||
|
||||
endpoint = &interface->endpoint[0].desc;
|
||||
if (!usb_endpoint_is_int_in(endpoint))
|
||||
return -ENODEV;
|
||||
|
||||
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
|
||||
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
|
||||
|
||||
mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!mouse || !input_dev)
|
||||
goto fail1;
|
||||
|
||||
mouse->data = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &mouse->data_dma);
|
||||
if (!mouse->data)
|
||||
goto fail1;
|
||||
|
||||
mouse->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!mouse->irq)
|
||||
goto fail2;
|
||||
|
||||
mouse->usbdev = dev;
|
||||
mouse->dev = input_dev;
|
||||
|
||||
if (dev->manufacturer)
|
||||
strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));
|
||||
|
||||
if (dev->product) {
|
||||
if (dev->manufacturer)
|
||||
strlcat(mouse->name, " ", sizeof(mouse->name));
|
||||
strlcat(mouse->name, dev->product, sizeof(mouse->name));
|
||||
}
|
||||
|
||||
if (!strlen(mouse->name))
|
||||
snprintf(mouse->name, sizeof(mouse->name),
|
||||
"USB HIDBP Mouse %04x:%04x",
|
||||
le16_to_cpu(dev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->descriptor.idProduct));
|
||||
|
||||
usb_make_path(dev, mouse->phys, sizeof(mouse->phys));
|
||||
strlcat(mouse->phys, "/input0", sizeof(mouse->phys));
|
||||
|
||||
input_dev->name = mouse->name;
|
||||
input_dev->phys = mouse->phys;
|
||||
usb_to_input_id(dev, &input_dev->id);
|
||||
input_dev->cdev.dev = &intf->dev;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
|
||||
input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
|
||||
input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
|
||||
input_dev->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
|
||||
input_dev->relbit[0] |= BIT(REL_WHEEL);
|
||||
|
||||
input_dev->private = mouse;
|
||||
input_dev->open = usb_mouse_open;
|
||||
input_dev->close = usb_mouse_close;
|
||||
|
||||
usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
|
||||
(maxp > 8 ? 8 : maxp),
|
||||
usb_mouse_irq, mouse, endpoint->bInterval);
|
||||
mouse->irq->transfer_dma = mouse->data_dma;
|
||||
mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
input_register_device(mouse->dev);
|
||||
|
||||
usb_set_intfdata(intf, mouse);
|
||||
return 0;
|
||||
|
||||
fail2: usb_buffer_free(dev, 8, mouse->data, mouse->data_dma);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(mouse);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void usb_mouse_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_mouse *mouse = usb_get_intfdata (intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (mouse) {
|
||||
usb_kill_urb(mouse->irq);
|
||||
input_unregister_device(mouse->dev);
|
||||
usb_free_urb(mouse->irq);
|
||||
usb_buffer_free(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma);
|
||||
kfree(mouse);
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_device_id usb_mouse_id_table [] = {
|
||||
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
|
||||
USB_INTERFACE_PROTOCOL_MOUSE) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
|
||||
|
||||
static struct usb_driver usb_mouse_driver = {
|
||||
.name = "usbmouse",
|
||||
.probe = usb_mouse_probe,
|
||||
.disconnect = usb_mouse_disconnect,
|
||||
.id_table = usb_mouse_id_table,
|
||||
};
|
||||
|
||||
static int __init usb_mouse_init(void)
|
||||
{
|
||||
int retval = usb_register(&usb_mouse_driver);
|
||||
if (retval == 0)
|
||||
info(DRIVER_VERSION ":" DRIVER_DESC);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit usb_mouse_exit(void)
|
||||
{
|
||||
usb_deregister(&usb_mouse_driver);
|
||||
}
|
||||
|
||||
module_init(usb_mouse_init);
|
||||
module_exit(usb_mouse_exit);
|
||||
838
drivers/usb/input/usbtouchscreen.c
Normal file
838
drivers/usb/input/usbtouchscreen.c
Normal file
@@ -0,0 +1,838 @@
|
||||
/******************************************************************************
|
||||
* usbtouchscreen.c
|
||||
* Driver for USB Touchscreens, supporting those devices:
|
||||
* - eGalax Touchkit
|
||||
* includes eTurboTouch CT-410/510/700
|
||||
* - 3M/Microtouch EX II series
|
||||
* - ITM
|
||||
* - PanJit TouchSet
|
||||
* - eTurboTouch
|
||||
* - Gunze AHL61
|
||||
* - DMC TSC-10/25
|
||||
*
|
||||
* Copyright (C) 2004-2006 by Daniel Ritz <daniel.ritz@gmx.ch>
|
||||
* Copyright (C) by Todd E. Johnson (mtouchusb.c)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* Driver is based on touchkitusb.c
|
||||
* - ITM parts are from itmtouch.c
|
||||
* - 3M parts are from mtouchusb.c
|
||||
* - PanJit parts are from an unmerged driver by Lanslott Gish
|
||||
* - DMC TSC 10/25 are from Holger Schurig, with ideas from an unmerged
|
||||
* driver from Marius Vollmer
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
|
||||
#define DRIVER_VERSION "v0.5"
|
||||
#define DRIVER_AUTHOR "Daniel Ritz <daniel.ritz@gmx.ch>"
|
||||
#define DRIVER_DESC "USB Touchscreen Driver"
|
||||
|
||||
static int swap_xy;
|
||||
module_param(swap_xy, bool, 0644);
|
||||
MODULE_PARM_DESC(swap_xy, "If set X and Y axes are swapped.");
|
||||
|
||||
/* device specifc data/functions */
|
||||
struct usbtouch_usb;
|
||||
struct usbtouch_device_info {
|
||||
int min_xc, max_xc;
|
||||
int min_yc, max_yc;
|
||||
int min_press, max_press;
|
||||
int rept_size;
|
||||
int flags;
|
||||
|
||||
void (*process_pkt) (struct usbtouch_usb *usbtouch, unsigned char *pkt, int len);
|
||||
int (*get_pkt_len) (unsigned char *pkt, int len);
|
||||
int (*read_data) (struct usbtouch_usb *usbtouch, unsigned char *pkt);
|
||||
int (*init) (struct usbtouch_usb *usbtouch);
|
||||
};
|
||||
|
||||
#define USBTOUCH_FLG_BUFFER 0x01
|
||||
|
||||
|
||||
/* a usbtouch device */
|
||||
struct usbtouch_usb {
|
||||
unsigned char *data;
|
||||
dma_addr_t data_dma;
|
||||
unsigned char *buffer;
|
||||
int buf_len;
|
||||
struct urb *irq;
|
||||
struct usb_device *udev;
|
||||
struct input_dev *input;
|
||||
struct usbtouch_device_info *type;
|
||||
char name[128];
|
||||
char phys[64];
|
||||
|
||||
int x, y;
|
||||
int touch, press;
|
||||
};
|
||||
|
||||
|
||||
#if defined(CONFIG_USB_TOUCHSCREEN_EGALAX) || defined(CONFIG_USB_TOUCHSCREEN_ETURBO)
|
||||
#define MULTI_PACKET
|
||||
#endif
|
||||
|
||||
#ifdef MULTI_PACKET
|
||||
static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
|
||||
unsigned char *pkt, int len);
|
||||
#endif
|
||||
|
||||
/* device types */
|
||||
enum {
|
||||
DEVTPYE_DUMMY = -1,
|
||||
DEVTYPE_EGALAX,
|
||||
DEVTYPE_PANJIT,
|
||||
DEVTYPE_3M,
|
||||
DEVTYPE_ITM,
|
||||
DEVTYPE_ETURBO,
|
||||
DEVTYPE_GUNZE,
|
||||
DEVTYPE_DMC_TSC10,
|
||||
};
|
||||
|
||||
static struct usb_device_id usbtouch_devices[] = {
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_EGALAX
|
||||
{USB_DEVICE(0x3823, 0x0001), .driver_info = DEVTYPE_EGALAX},
|
||||
{USB_DEVICE(0x3823, 0x0002), .driver_info = DEVTYPE_EGALAX},
|
||||
{USB_DEVICE(0x0123, 0x0001), .driver_info = DEVTYPE_EGALAX},
|
||||
{USB_DEVICE(0x0eef, 0x0001), .driver_info = DEVTYPE_EGALAX},
|
||||
{USB_DEVICE(0x0eef, 0x0002), .driver_info = DEVTYPE_EGALAX},
|
||||
{USB_DEVICE(0x1234, 0x0001), .driver_info = DEVTYPE_EGALAX},
|
||||
{USB_DEVICE(0x1234, 0x0002), .driver_info = DEVTYPE_EGALAX},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_PANJIT
|
||||
{USB_DEVICE(0x134c, 0x0001), .driver_info = DEVTYPE_PANJIT},
|
||||
{USB_DEVICE(0x134c, 0x0002), .driver_info = DEVTYPE_PANJIT},
|
||||
{USB_DEVICE(0x134c, 0x0003), .driver_info = DEVTYPE_PANJIT},
|
||||
{USB_DEVICE(0x134c, 0x0004), .driver_info = DEVTYPE_PANJIT},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_3M
|
||||
{USB_DEVICE(0x0596, 0x0001), .driver_info = DEVTYPE_3M},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_ITM
|
||||
{USB_DEVICE(0x0403, 0xf9e9), .driver_info = DEVTYPE_ITM},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO
|
||||
{USB_DEVICE(0x1234, 0x5678), .driver_info = DEVTYPE_ETURBO},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE
|
||||
{USB_DEVICE(0x0637, 0x0001), .driver_info = DEVTYPE_GUNZE},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_DMC_TSC10
|
||||
{USB_DEVICE(0x0afa, 0x03e8), .driver_info = DEVTYPE_DMC_TSC10},
|
||||
#endif
|
||||
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* eGalax part
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_EGALAX
|
||||
|
||||
#define EGALAX_PKT_TYPE_MASK 0xFE
|
||||
#define EGALAX_PKT_TYPE_REPT 0x80
|
||||
#define EGALAX_PKT_TYPE_DIAG 0x0A
|
||||
|
||||
static int egalax_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
|
||||
{
|
||||
if ((pkt[0] & EGALAX_PKT_TYPE_MASK) != EGALAX_PKT_TYPE_REPT)
|
||||
return 0;
|
||||
|
||||
dev->x = ((pkt[3] & 0x0F) << 7) | (pkt[4] & 0x7F);
|
||||
dev->y = ((pkt[1] & 0x0F) << 7) | (pkt[2] & 0x7F);
|
||||
dev->touch = pkt[0] & 0x01;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int egalax_get_pkt_len(unsigned char *buf, int len)
|
||||
{
|
||||
switch (buf[0] & EGALAX_PKT_TYPE_MASK) {
|
||||
case EGALAX_PKT_TYPE_REPT:
|
||||
return 5;
|
||||
|
||||
case EGALAX_PKT_TYPE_DIAG:
|
||||
if (len < 2)
|
||||
return -1;
|
||||
|
||||
return buf[1] + 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* PanJit Part
|
||||
*/
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_PANJIT
|
||||
static int panjit_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
|
||||
{
|
||||
dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1];
|
||||
dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3];
|
||||
dev->touch = pkt[0] & 0x01;
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* 3M/Microtouch Part
|
||||
*/
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_3M
|
||||
|
||||
#define MTOUCHUSB_ASYNC_REPORT 1
|
||||
#define MTOUCHUSB_RESET 7
|
||||
#define MTOUCHUSB_REQ_CTRLLR_ID 10
|
||||
|
||||
static int mtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
|
||||
{
|
||||
dev->x = (pkt[8] << 8) | pkt[7];
|
||||
dev->y = (pkt[10] << 8) | pkt[9];
|
||||
dev->touch = (pkt[2] & 0x40) ? 1 : 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int mtouch_init(struct usbtouch_usb *usbtouch)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
ret = usb_control_msg(usbtouch->udev, usb_rcvctrlpipe(usbtouch->udev, 0),
|
||||
MTOUCHUSB_RESET,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
dbg("%s - usb_control_msg - MTOUCHUSB_RESET - bytes|err: %d",
|
||||
__FUNCTION__, ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
msleep(150);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
ret = usb_control_msg(usbtouch->udev, usb_rcvctrlpipe(usbtouch->udev, 0),
|
||||
MTOUCHUSB_ASYNC_REPORT,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
1, 1, NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d",
|
||||
__FUNCTION__, ret);
|
||||
if (ret >= 0)
|
||||
break;
|
||||
if (ret != -EPIPE)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* ITM Part
|
||||
*/
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_ITM
|
||||
static int itm_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
|
||||
{
|
||||
int touch;
|
||||
/*
|
||||
* ITM devices report invalid x/y data if not touched.
|
||||
* if the screen was touched before but is not touched any more
|
||||
* report touch as 0 with the last valid x/y data once. then stop
|
||||
* reporting data until touched again.
|
||||
*/
|
||||
dev->press = ((pkt[2] & 0x01) << 7) | (pkt[5] & 0x7F);
|
||||
|
||||
touch = ~pkt[7] & 0x20;
|
||||
if (!touch) {
|
||||
if (dev->touch) {
|
||||
dev->touch = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev->x = ((pkt[0] & 0x1F) << 7) | (pkt[3] & 0x7F);
|
||||
dev->y = ((pkt[1] & 0x1F) << 7) | (pkt[4] & 0x7F);
|
||||
dev->touch = touch;
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* eTurboTouch part
|
||||
*/
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO
|
||||
static int eturbo_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
|
||||
{
|
||||
unsigned int shift;
|
||||
|
||||
/* packets should start with sync */
|
||||
if (!(pkt[0] & 0x80))
|
||||
return 0;
|
||||
|
||||
shift = (6 - (pkt[0] & 0x03));
|
||||
dev->x = ((pkt[3] << 7) | pkt[4]) >> shift;
|
||||
dev->y = ((pkt[1] << 7) | pkt[2]) >> shift;
|
||||
dev->touch = (pkt[0] & 0x10) ? 1 : 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int eturbo_get_pkt_len(unsigned char *buf, int len)
|
||||
{
|
||||
if (buf[0] & 0x80)
|
||||
return 5;
|
||||
if (buf[0] == 0x01)
|
||||
return 3;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Gunze part
|
||||
*/
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE
|
||||
static int gunze_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
|
||||
{
|
||||
if (!(pkt[0] & 0x80) || ((pkt[1] | pkt[2] | pkt[3]) & 0x80))
|
||||
return 0;
|
||||
|
||||
dev->x = ((pkt[0] & 0x1F) << 7) | (pkt[2] & 0x7F);
|
||||
dev->y = ((pkt[1] & 0x1F) << 7) | (pkt[3] & 0x7F);
|
||||
dev->touch = pkt[0] & 0x20;
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*****************************************************************************
|
||||
* DMC TSC-10/25 Part
|
||||
*
|
||||
* Documentation about the controller and it's protocol can be found at
|
||||
* http://www.dmccoltd.com/files/controler/tsc10usb_pi_e.pdf
|
||||
* http://www.dmccoltd.com/files/controler/tsc25_usb_e.pdf
|
||||
*/
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_DMC_TSC10
|
||||
|
||||
/* supported data rates. currently using 130 */
|
||||
#define TSC10_RATE_POINT 0x50
|
||||
#define TSC10_RATE_30 0x40
|
||||
#define TSC10_RATE_50 0x41
|
||||
#define TSC10_RATE_80 0x42
|
||||
#define TSC10_RATE_100 0x43
|
||||
#define TSC10_RATE_130 0x44
|
||||
#define TSC10_RATE_150 0x45
|
||||
|
||||
/* commands */
|
||||
#define TSC10_CMD_RESET 0x55
|
||||
#define TSC10_CMD_RATE 0x05
|
||||
#define TSC10_CMD_DATA1 0x01
|
||||
|
||||
static int dmc_tsc10_init(struct usbtouch_usb *usbtouch)
|
||||
{
|
||||
struct usb_device *dev = usbtouch->udev;
|
||||
int ret;
|
||||
unsigned char buf[2];
|
||||
|
||||
/* reset */
|
||||
buf[0] = buf[1] = 0xFF;
|
||||
ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
|
||||
TSC10_CMD_RESET,
|
||||
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
0, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (buf[0] != 0x06 || buf[1] != 0x00)
|
||||
return -ENODEV;
|
||||
|
||||
/* set coordinate output rate */
|
||||
buf[0] = buf[1] = 0xFF;
|
||||
ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
|
||||
TSC10_CMD_RATE,
|
||||
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
TSC10_RATE_150, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (buf[0] != 0x06 || buf[1] != 0x00)
|
||||
return -ENODEV;
|
||||
|
||||
/* start sending data */
|
||||
ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
|
||||
TSC10_CMD_DATA1,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int dmc_tsc10_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
|
||||
{
|
||||
dev->x = ((pkt[2] & 0x03) << 8) | pkt[1];
|
||||
dev->y = ((pkt[4] & 0x03) << 8) | pkt[3];
|
||||
dev->touch = pkt[0] & 0x01;
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* the different device descriptors
|
||||
*/
|
||||
static struct usbtouch_device_info usbtouch_dev_info[] = {
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_EGALAX
|
||||
[DEVTYPE_EGALAX] = {
|
||||
.min_xc = 0x0,
|
||||
.max_xc = 0x07ff,
|
||||
.min_yc = 0x0,
|
||||
.max_yc = 0x07ff,
|
||||
.rept_size = 16,
|
||||
.flags = USBTOUCH_FLG_BUFFER,
|
||||
.process_pkt = usbtouch_process_multi,
|
||||
.get_pkt_len = egalax_get_pkt_len,
|
||||
.read_data = egalax_read_data,
|
||||
},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_PANJIT
|
||||
[DEVTYPE_PANJIT] = {
|
||||
.min_xc = 0x0,
|
||||
.max_xc = 0x0fff,
|
||||
.min_yc = 0x0,
|
||||
.max_yc = 0x0fff,
|
||||
.rept_size = 8,
|
||||
.read_data = panjit_read_data,
|
||||
},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_3M
|
||||
[DEVTYPE_3M] = {
|
||||
.min_xc = 0x0,
|
||||
.max_xc = 0x4000,
|
||||
.min_yc = 0x0,
|
||||
.max_yc = 0x4000,
|
||||
.rept_size = 11,
|
||||
.read_data = mtouch_read_data,
|
||||
.init = mtouch_init,
|
||||
},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_ITM
|
||||
[DEVTYPE_ITM] = {
|
||||
.min_xc = 0x0,
|
||||
.max_xc = 0x0fff,
|
||||
.min_yc = 0x0,
|
||||
.max_yc = 0x0fff,
|
||||
.max_press = 0xff,
|
||||
.rept_size = 8,
|
||||
.read_data = itm_read_data,
|
||||
},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO
|
||||
[DEVTYPE_ETURBO] = {
|
||||
.min_xc = 0x0,
|
||||
.max_xc = 0x07ff,
|
||||
.min_yc = 0x0,
|
||||
.max_yc = 0x07ff,
|
||||
.rept_size = 8,
|
||||
.flags = USBTOUCH_FLG_BUFFER,
|
||||
.process_pkt = usbtouch_process_multi,
|
||||
.get_pkt_len = eturbo_get_pkt_len,
|
||||
.read_data = eturbo_read_data,
|
||||
},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE
|
||||
[DEVTYPE_GUNZE] = {
|
||||
.min_xc = 0x0,
|
||||
.max_xc = 0x0fff,
|
||||
.min_yc = 0x0,
|
||||
.max_yc = 0x0fff,
|
||||
.rept_size = 4,
|
||||
.read_data = gunze_read_data,
|
||||
},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_TOUCHSCREEN_DMC_TSC10
|
||||
[DEVTYPE_DMC_TSC10] = {
|
||||
.min_xc = 0x0,
|
||||
.max_xc = 0x03ff,
|
||||
.min_yc = 0x0,
|
||||
.max_yc = 0x03ff,
|
||||
.rept_size = 5,
|
||||
.init = dmc_tsc10_init,
|
||||
.read_data = dmc_tsc10_read_data,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Generic Part
|
||||
*/
|
||||
static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch,
|
||||
unsigned char *pkt, int len)
|
||||
{
|
||||
struct usbtouch_device_info *type = usbtouch->type;
|
||||
|
||||
if (!type->read_data(usbtouch, pkt))
|
||||
return;
|
||||
|
||||
input_report_key(usbtouch->input, BTN_TOUCH, usbtouch->touch);
|
||||
|
||||
if (swap_xy) {
|
||||
input_report_abs(usbtouch->input, ABS_X, usbtouch->y);
|
||||
input_report_abs(usbtouch->input, ABS_Y, usbtouch->x);
|
||||
} else {
|
||||
input_report_abs(usbtouch->input, ABS_X, usbtouch->x);
|
||||
input_report_abs(usbtouch->input, ABS_Y, usbtouch->y);
|
||||
}
|
||||
if (type->max_press)
|
||||
input_report_abs(usbtouch->input, ABS_PRESSURE, usbtouch->press);
|
||||
input_sync(usbtouch->input);
|
||||
}
|
||||
|
||||
|
||||
#ifdef MULTI_PACKET
|
||||
static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
|
||||
unsigned char *pkt, int len)
|
||||
{
|
||||
unsigned char *buffer;
|
||||
int pkt_len, pos, buf_len, tmp;
|
||||
|
||||
/* process buffer */
|
||||
if (unlikely(usbtouch->buf_len)) {
|
||||
/* try to get size */
|
||||
pkt_len = usbtouch->type->get_pkt_len(
|
||||
usbtouch->buffer, usbtouch->buf_len);
|
||||
|
||||
/* drop? */
|
||||
if (unlikely(!pkt_len))
|
||||
goto out_flush_buf;
|
||||
|
||||
/* need to append -pkt_len bytes before able to get size */
|
||||
if (unlikely(pkt_len < 0)) {
|
||||
int append = -pkt_len;
|
||||
if (unlikely(append > len))
|
||||
append = len;
|
||||
if (usbtouch->buf_len + append >= usbtouch->type->rept_size)
|
||||
goto out_flush_buf;
|
||||
memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, append);
|
||||
usbtouch->buf_len += append;
|
||||
|
||||
pkt_len = usbtouch->type->get_pkt_len(
|
||||
usbtouch->buffer, usbtouch->buf_len);
|
||||
if (pkt_len < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
/* append */
|
||||
tmp = pkt_len - usbtouch->buf_len;
|
||||
if (usbtouch->buf_len + tmp >= usbtouch->type->rept_size)
|
||||
goto out_flush_buf;
|
||||
memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, tmp);
|
||||
usbtouch_process_pkt(usbtouch, usbtouch->buffer, pkt_len);
|
||||
|
||||
buffer = pkt + tmp;
|
||||
buf_len = len - tmp;
|
||||
} else {
|
||||
buffer = pkt;
|
||||
buf_len = len;
|
||||
}
|
||||
|
||||
/* loop over the received packet, process */
|
||||
pos = 0;
|
||||
while (pos < buf_len) {
|
||||
/* get packet len */
|
||||
pkt_len = usbtouch->type->get_pkt_len(buffer + pos, len);
|
||||
|
||||
/* unknown packet: drop everything */
|
||||
if (unlikely(!pkt_len))
|
||||
goto out_flush_buf;
|
||||
|
||||
/* full packet: process */
|
||||
if (likely((pkt_len > 0) && (pkt_len <= buf_len - pos))) {
|
||||
usbtouch_process_pkt(usbtouch, buffer + pos, pkt_len);
|
||||
} else {
|
||||
/* incomplete packet: save in buffer */
|
||||
memcpy(usbtouch->buffer, buffer + pos, buf_len - pos);
|
||||
usbtouch->buf_len = buf_len - pos;
|
||||
return;
|
||||
}
|
||||
pos += pkt_len;
|
||||
}
|
||||
|
||||
out_flush_buf:
|
||||
usbtouch->buf_len = 0;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void usbtouch_irq(struct urb *urb)
|
||||
{
|
||||
struct usbtouch_usb *usbtouch = urb->context;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ETIME:
|
||||
/* this urb is timing out */
|
||||
dbg("%s - urb timed out - was the device unplugged?",
|
||||
__FUNCTION__);
|
||||
return;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d",
|
||||
__FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d",
|
||||
__FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err("%s - usb_submit_urb failed with result: %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
static int usbtouch_open(struct input_dev *input)
|
||||
{
|
||||
struct usbtouch_usb *usbtouch = input->private;
|
||||
|
||||
usbtouch->irq->dev = usbtouch->udev;
|
||||
|
||||
if (usb_submit_urb(usbtouch->irq, GFP_KERNEL))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usbtouch_close(struct input_dev *input)
|
||||
{
|
||||
struct usbtouch_usb *usbtouch = input->private;
|
||||
|
||||
usb_kill_urb(usbtouch->irq);
|
||||
}
|
||||
|
||||
|
||||
static void usbtouch_free_buffers(struct usb_device *udev,
|
||||
struct usbtouch_usb *usbtouch)
|
||||
{
|
||||
if (usbtouch->data)
|
||||
usb_buffer_free(udev, usbtouch->type->rept_size,
|
||||
usbtouch->data, usbtouch->data_dma);
|
||||
kfree(usbtouch->buffer);
|
||||
}
|
||||
|
||||
|
||||
static int usbtouch_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usbtouch_usb *usbtouch;
|
||||
struct input_dev *input_dev;
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
struct usbtouch_device_info *type;
|
||||
int err = -ENOMEM;
|
||||
|
||||
interface = intf->cur_altsetting;
|
||||
endpoint = &interface->endpoint[0].desc;
|
||||
|
||||
usbtouch = kzalloc(sizeof(struct usbtouch_usb), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!usbtouch || !input_dev)
|
||||
goto out_free;
|
||||
|
||||
type = &usbtouch_dev_info[id->driver_info];
|
||||
usbtouch->type = type;
|
||||
if (!type->process_pkt)
|
||||
type->process_pkt = usbtouch_process_pkt;
|
||||
|
||||
usbtouch->data = usb_buffer_alloc(udev, type->rept_size,
|
||||
GFP_KERNEL, &usbtouch->data_dma);
|
||||
if (!usbtouch->data)
|
||||
goto out_free;
|
||||
|
||||
if (type->flags & USBTOUCH_FLG_BUFFER) {
|
||||
usbtouch->buffer = kmalloc(type->rept_size, GFP_KERNEL);
|
||||
if (!usbtouch->buffer)
|
||||
goto out_free_buffers;
|
||||
}
|
||||
|
||||
usbtouch->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!usbtouch->irq) {
|
||||
dbg("%s - usb_alloc_urb failed: usbtouch->irq", __FUNCTION__);
|
||||
goto out_free_buffers;
|
||||
}
|
||||
|
||||
usbtouch->udev = udev;
|
||||
usbtouch->input = input_dev;
|
||||
|
||||
if (udev->manufacturer)
|
||||
strlcpy(usbtouch->name, udev->manufacturer, sizeof(usbtouch->name));
|
||||
|
||||
if (udev->product) {
|
||||
if (udev->manufacturer)
|
||||
strlcat(usbtouch->name, " ", sizeof(usbtouch->name));
|
||||
strlcat(usbtouch->name, udev->product, sizeof(usbtouch->name));
|
||||
}
|
||||
|
||||
if (!strlen(usbtouch->name))
|
||||
snprintf(usbtouch->name, sizeof(usbtouch->name),
|
||||
"USB Touchscreen %04x:%04x",
|
||||
le16_to_cpu(udev->descriptor.idVendor),
|
||||
le16_to_cpu(udev->descriptor.idProduct));
|
||||
|
||||
usb_make_path(udev, usbtouch->phys, sizeof(usbtouch->phys));
|
||||
strlcpy(usbtouch->phys, "/input0", sizeof(usbtouch->phys));
|
||||
|
||||
input_dev->name = usbtouch->name;
|
||||
input_dev->phys = usbtouch->phys;
|
||||
usb_to_input_id(udev, &input_dev->id);
|
||||
input_dev->cdev.dev = &intf->dev;
|
||||
input_dev->private = usbtouch;
|
||||
input_dev->open = usbtouch_open;
|
||||
input_dev->close = usbtouch_close;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||
input_set_abs_params(input_dev, ABS_X, type->min_xc, type->max_xc, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, type->min_yc, type->max_yc, 0, 0);
|
||||
if (type->max_press)
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, type->min_press,
|
||||
type->max_press, 0, 0);
|
||||
|
||||
usb_fill_int_urb(usbtouch->irq, usbtouch->udev,
|
||||
usb_rcvintpipe(usbtouch->udev, endpoint->bEndpointAddress),
|
||||
usbtouch->data, type->rept_size,
|
||||
usbtouch_irq, usbtouch, endpoint->bInterval);
|
||||
|
||||
usbtouch->irq->dev = usbtouch->udev;
|
||||
usbtouch->irq->transfer_dma = usbtouch->data_dma;
|
||||
usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
/* device specific init */
|
||||
if (type->init) {
|
||||
err = type->init(usbtouch);
|
||||
if (err) {
|
||||
dbg("%s - type->init() failed, err: %d", __FUNCTION__, err);
|
||||
goto out_free_buffers;
|
||||
}
|
||||
}
|
||||
|
||||
err = input_register_device(usbtouch->input);
|
||||
if (err) {
|
||||
dbg("%s - input_register_device failed, err: %d", __FUNCTION__, err);
|
||||
goto out_free_buffers;
|
||||
}
|
||||
|
||||
usb_set_intfdata(intf, usbtouch);
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_buffers:
|
||||
usbtouch_free_buffers(udev, usbtouch);
|
||||
out_free:
|
||||
input_free_device(input_dev);
|
||||
kfree(usbtouch);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void usbtouch_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
|
||||
|
||||
dbg("%s - called", __FUNCTION__);
|
||||
|
||||
if (!usbtouch)
|
||||
return;
|
||||
|
||||
dbg("%s - usbtouch is initialized, cleaning up", __FUNCTION__);
|
||||
usb_set_intfdata(intf, NULL);
|
||||
usb_kill_urb(usbtouch->irq);
|
||||
input_unregister_device(usbtouch->input);
|
||||
usb_free_urb(usbtouch->irq);
|
||||
usbtouch_free_buffers(interface_to_usbdev(intf), usbtouch);
|
||||
kfree(usbtouch);
|
||||
}
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, usbtouch_devices);
|
||||
|
||||
static struct usb_driver usbtouch_driver = {
|
||||
.name = "usbtouchscreen",
|
||||
.probe = usbtouch_probe,
|
||||
.disconnect = usbtouch_disconnect,
|
||||
.id_table = usbtouch_devices,
|
||||
};
|
||||
|
||||
static int __init usbtouch_init(void)
|
||||
{
|
||||
return usb_register(&usbtouch_driver);
|
||||
}
|
||||
|
||||
static void __exit usbtouch_cleanup(void)
|
||||
{
|
||||
usb_deregister(&usbtouch_driver);
|
||||
}
|
||||
|
||||
module_init(usbtouch_init);
|
||||
module_exit(usbtouch_cleanup);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
MODULE_ALIAS("touchkitusb");
|
||||
MODULE_ALIAS("itmtouch");
|
||||
MODULE_ALIAS("mtouchusb");
|
||||
131
drivers/usb/input/wacom.h
Normal file
131
drivers/usb/input/wacom.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* drivers/usb/input/wacom.h
|
||||
*
|
||||
* USB Wacom Graphire and Wacom Intuos tablet support
|
||||
*
|
||||
* Copyright (c) 2000-2004 Vojtech Pavlik <vojtech@ucw.cz>
|
||||
* Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk>
|
||||
* Copyright (c) 2000 Clifford Wolf <clifford@clifford.at>
|
||||
* Copyright (c) 2000 Sam Mosel <sam.mosel@computer.org>
|
||||
* Copyright (c) 2000 James E. Blair <corvus@gnu.org>
|
||||
* Copyright (c) 2000 Daniel Egger <egger@suse.de>
|
||||
* Copyright (c) 2001 Frederic Lepied <flepied@mandrakesoft.com>
|
||||
* Copyright (c) 2004 Panagiotis Issaris <panagiotis.issaris@mech.kuleuven.ac.be>
|
||||
* Copyright (c) 2002-2006 Ping Cheng <pingc@wacom.com>
|
||||
*
|
||||
* ChangeLog:
|
||||
* v0.1 (vp) - Initial release
|
||||
* v0.2 (aba) - Support for all buttons / combinations
|
||||
* v0.3 (vp) - Support for Intuos added
|
||||
* v0.4 (sm) - Support for more Intuos models, menustrip
|
||||
* relative mode, proximity.
|
||||
* v0.5 (vp) - Big cleanup, nifty features removed,
|
||||
* they belong in userspace
|
||||
* v1.8 (vp) - Submit URB only when operating, moved to CVS,
|
||||
* use input_report_key instead of report_btn and
|
||||
* other cleanups
|
||||
* v1.11 (vp) - Add URB ->dev setting for new kernels
|
||||
* v1.11 (jb) - Add support for the 4D Mouse & Lens
|
||||
* v1.12 (de) - Add support for two more inking pen IDs
|
||||
* v1.14 (vp) - Use new USB device id probing scheme.
|
||||
* Fix Wacom Graphire mouse wheel
|
||||
* v1.18 (vp) - Fix mouse wheel direction
|
||||
* Make mouse relative
|
||||
* v1.20 (fl) - Report tool id for Intuos devices
|
||||
* - Multi tools support
|
||||
* - Corrected Intuos protocol decoding (airbrush, 4D mouse, lens cursor...)
|
||||
* - Add PL models support
|
||||
* - Fix Wacom Graphire mouse wheel again
|
||||
* v1.21 (vp) - Removed protocol descriptions
|
||||
* - Added MISC_SERIAL for tool serial numbers
|
||||
* (gb) - Identify version on module load.
|
||||
* v1.21.1 (fl) - added Graphire2 support
|
||||
* v1.21.2 (fl) - added Intuos2 support
|
||||
* - added all the PL ids
|
||||
* v1.21.3 (fl) - added another eraser id from Neil Okamoto
|
||||
* - added smooth filter for Graphire from Peri Hankey
|
||||
* - added PenPartner support from Olaf van Es
|
||||
* - new tool ids from Ole Martin Bjoerndalen
|
||||
* v1.29 (pc) - Add support for more tablets
|
||||
* - Fix pressure reporting
|
||||
* v1.30 (vp) - Merge 2.4 and 2.5 drivers
|
||||
* - Since 2.5 now has input_sync(), remove MSC_SERIAL abuse
|
||||
* - Cleanups here and there
|
||||
* v1.30.1 (pi) - Added Graphire3 support
|
||||
* v1.40 (pc) - Add support for several new devices, fix eraser reporting, ...
|
||||
* v1.43 (pc) - Added support for Cintiq 21UX
|
||||
* - Fixed a Graphire bug
|
||||
* - Merged wacom_intuos3_irq into wacom_intuos_irq
|
||||
* v1.44 (pc) - Added support for Graphire4, Cintiq 710, Intuos3 6x11, etc.
|
||||
* - Report Device IDs
|
||||
* v1.45 (pc) - Added support for DTF 521, Intuos3 12x12 and 12x19
|
||||
* - Minor data report fix
|
||||
* v1.46 (pc) - Split wacom.c into wacom_sys.c and wacom_wac.c,
|
||||
* - where wacom_sys.c deals with system specific code,
|
||||
* - and wacom_wac.c deals with Wacom specific code
|
||||
* - Support Intuos3 4x6
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
#ifndef WACOM_H
|
||||
#define WACOM_H
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb/input.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
*/
|
||||
#define DRIVER_VERSION "v1.46"
|
||||
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
|
||||
#define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver"
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE(DRIVER_LICENSE);
|
||||
|
||||
#define USB_VENDOR_ID_WACOM 0x056a
|
||||
|
||||
struct wacom {
|
||||
dma_addr_t data_dma;
|
||||
struct input_dev *dev;
|
||||
struct usb_device *usbdev;
|
||||
struct urb *irq;
|
||||
struct wacom_wac * wacom_wac;
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
struct wacom_combo {
|
||||
struct wacom * wacom;
|
||||
struct urb * urb;
|
||||
};
|
||||
|
||||
extern int wacom_wac_irq(struct wacom_wac * wacom_wac, void * wcombo);
|
||||
extern void wacom_report_abs(void *wcombo, unsigned int abs_type, int abs_data);
|
||||
extern void wacom_report_rel(void *wcombo, unsigned int rel_type, int rel_data);
|
||||
extern void wacom_report_key(void *wcombo, unsigned int key_type, int key_data);
|
||||
extern void wacom_input_event(void *wcombo, unsigned int type, unsigned int code, int value);
|
||||
extern void wacom_input_sync(void *wcombo);
|
||||
extern void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
|
||||
extern void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
|
||||
extern void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
|
||||
extern void input_dev_i3s(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
|
||||
extern void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
|
||||
extern void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
|
||||
extern void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
|
||||
extern void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
|
||||
extern __u16 wacom_le16_to_cpu(unsigned char *data);
|
||||
extern __u16 wacom_be16_to_cpu(unsigned char *data);
|
||||
extern struct wacom_features * get_wacom_feature(const struct usb_device_id *id);
|
||||
extern const struct usb_device_id * get_device_table(void);
|
||||
|
||||
#endif
|
||||
312
drivers/usb/input/wacom_sys.c
Normal file
312
drivers/usb/input/wacom_sys.c
Normal file
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* drivers/usb/input/wacom_sys.c
|
||||
*
|
||||
* USB Wacom Graphire and Wacom Intuos tablet support - system specific code
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "wacom.h"
|
||||
#include "wacom_wac.h"
|
||||
|
||||
#define USB_REQ_GET_REPORT 0x01
|
||||
#define USB_REQ_SET_REPORT 0x09
|
||||
|
||||
static int usb_get_report(struct usb_interface *intf, unsigned char type,
|
||||
unsigned char id, void *buf, int size)
|
||||
{
|
||||
return usb_control_msg(interface_to_usbdev(intf),
|
||||
usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
|
||||
USB_REQ_GET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
(type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
|
||||
buf, size, 100);
|
||||
}
|
||||
|
||||
static int usb_set_report(struct usb_interface *intf, unsigned char type,
|
||||
unsigned char id, void *buf, int size)
|
||||
{
|
||||
return usb_control_msg(interface_to_usbdev(intf),
|
||||
usb_sndctrlpipe(interface_to_usbdev(intf), 0),
|
||||
USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
(type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
|
||||
buf, size, 1000);
|
||||
}
|
||||
|
||||
static struct input_dev * get_input_dev(struct wacom_combo *wcombo)
|
||||
{
|
||||
return wcombo->wacom->dev;
|
||||
}
|
||||
|
||||
static void wacom_sys_irq(struct urb *urb)
|
||||
{
|
||||
struct wacom *wacom = urb->context;
|
||||
struct wacom_combo wcombo;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
wcombo.wacom = wacom;
|
||||
wcombo.urb = urb;
|
||||
|
||||
if (wacom_wac_irq(wacom->wacom_wac, (void *)&wcombo))
|
||||
input_sync(get_input_dev(&wcombo));
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err ("%s - usb_submit_urb failed with result %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
void wacom_report_key(void *wcombo, unsigned int key_type, int key_data)
|
||||
{
|
||||
input_report_key(get_input_dev((struct wacom_combo *)wcombo), key_type, key_data);
|
||||
return;
|
||||
}
|
||||
|
||||
void wacom_report_abs(void *wcombo, unsigned int abs_type, int abs_data)
|
||||
{
|
||||
input_report_abs(get_input_dev((struct wacom_combo *)wcombo), abs_type, abs_data);
|
||||
return;
|
||||
}
|
||||
|
||||
void wacom_report_rel(void *wcombo, unsigned int rel_type, int rel_data)
|
||||
{
|
||||
input_report_rel(get_input_dev((struct wacom_combo *)wcombo), rel_type, rel_data);
|
||||
return;
|
||||
}
|
||||
|
||||
void wacom_input_event(void *wcombo, unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
input_event(get_input_dev((struct wacom_combo *)wcombo), type, code, value);
|
||||
return;
|
||||
}
|
||||
|
||||
__u16 wacom_be16_to_cpu(unsigned char *data)
|
||||
{
|
||||
__u16 value;
|
||||
value = be16_to_cpu(*(__be16 *) data);
|
||||
return value;
|
||||
}
|
||||
|
||||
__u16 wacom_le16_to_cpu(unsigned char *data)
|
||||
{
|
||||
__u16 value;
|
||||
value = le16_to_cpu(*(__le16 *) data);
|
||||
return value;
|
||||
}
|
||||
|
||||
void wacom_input_sync(void *wcombo)
|
||||
{
|
||||
input_sync(get_input_dev((struct wacom_combo *)wcombo));
|
||||
return;
|
||||
}
|
||||
|
||||
static int wacom_open(struct input_dev *dev)
|
||||
{
|
||||
struct wacom *wacom = dev->private;
|
||||
|
||||
wacom->irq->dev = wacom->usbdev;
|
||||
if (usb_submit_urb(wacom->irq, GFP_KERNEL))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wacom_close(struct input_dev *dev)
|
||||
{
|
||||
struct wacom *wacom = dev->private;
|
||||
|
||||
usb_kill_urb(wacom->irq);
|
||||
}
|
||||
|
||||
void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
|
||||
{
|
||||
input_dev->evbit[0] |= BIT(EV_MSC);
|
||||
input_dev->mscbit[0] |= BIT(MSC_SERIAL);
|
||||
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
|
||||
input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_4);
|
||||
}
|
||||
|
||||
void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
|
||||
{
|
||||
input_dev->evbit[0] |= BIT(EV_REL);
|
||||
input_dev->relbit[0] |= BIT(REL_WHEEL);
|
||||
input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
|
||||
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_STYLUS2);
|
||||
input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom_wac->features->distance_max, 0, 0);
|
||||
}
|
||||
|
||||
void input_dev_i3s(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
|
||||
{
|
||||
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
|
||||
input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3);
|
||||
input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
|
||||
}
|
||||
|
||||
void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
|
||||
{
|
||||
input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
|
||||
input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
|
||||
}
|
||||
|
||||
void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
|
||||
{
|
||||
input_dev->evbit[0] |= BIT(EV_MSC) | BIT(EV_REL);
|
||||
input_dev->mscbit[0] |= BIT(MSC_SERIAL);
|
||||
input_dev->relbit[0] |= BIT(REL_WHEEL);
|
||||
input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE) | BIT(BTN_SIDE) | BIT(BTN_EXTRA);
|
||||
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_TOOL_BRUSH)
|
||||
| BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS) | BIT(BTN_STYLUS2);
|
||||
input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom_wac->features->distance_max, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
|
||||
}
|
||||
|
||||
void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
|
||||
{
|
||||
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2) | BIT(BTN_TOOL_RUBBER);
|
||||
}
|
||||
|
||||
void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
|
||||
{
|
||||
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER);
|
||||
}
|
||||
|
||||
static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct wacom *wacom;
|
||||
struct wacom_wac *wacom_wac;
|
||||
struct input_dev *input_dev;
|
||||
char rep_data[2], limit = 0;
|
||||
|
||||
wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
|
||||
wacom_wac = kzalloc(sizeof(struct wacom_wac), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!wacom || !input_dev || !wacom_wac)
|
||||
goto fail1;
|
||||
|
||||
wacom_wac->data = usb_buffer_alloc(dev, 10, GFP_KERNEL, &wacom->data_dma);
|
||||
if (!wacom_wac->data)
|
||||
goto fail1;
|
||||
|
||||
wacom->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!wacom->irq)
|
||||
goto fail2;
|
||||
|
||||
wacom->usbdev = dev;
|
||||
wacom->dev = input_dev;
|
||||
usb_make_path(dev, wacom->phys, sizeof(wacom->phys));
|
||||
strlcat(wacom->phys, "/input0", sizeof(wacom->phys));
|
||||
|
||||
wacom_wac->features = get_wacom_feature(id);
|
||||
BUG_ON(wacom_wac->features->pktlen > 10);
|
||||
|
||||
input_dev->name = wacom_wac->features->name;
|
||||
wacom->wacom_wac = wacom_wac;
|
||||
usb_to_input_id(dev, &input_dev->id);
|
||||
|
||||
input_dev->cdev.dev = &intf->dev;
|
||||
input_dev->private = wacom;
|
||||
input_dev->open = wacom_open;
|
||||
input_dev->close = wacom_close;
|
||||
|
||||
input_dev->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS);
|
||||
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH) | BIT(BTN_STYLUS);
|
||||
input_set_abs_params(input_dev, ABS_X, 0, wacom_wac->features->x_max, 4, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, wacom_wac->features->y_max, 4, 0);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, wacom_wac->features->pressure_max, 0, 0);
|
||||
input_dev->absbit[LONG(ABS_MISC)] |= BIT(ABS_MISC);
|
||||
|
||||
wacom_init_input_dev(input_dev, wacom_wac);
|
||||
|
||||
endpoint = &intf->cur_altsetting->endpoint[0].desc;
|
||||
|
||||
usb_fill_int_urb(wacom->irq, dev,
|
||||
usb_rcvintpipe(dev, endpoint->bEndpointAddress),
|
||||
wacom_wac->data, wacom_wac->features->pktlen,
|
||||
wacom_sys_irq, wacom, endpoint->bInterval);
|
||||
wacom->irq->transfer_dma = wacom->data_dma;
|
||||
wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
input_register_device(wacom->dev);
|
||||
|
||||
/* Ask the tablet to report tablet data. Repeat until it succeeds */
|
||||
do {
|
||||
rep_data[0] = 2;
|
||||
rep_data[1] = 2;
|
||||
usb_set_report(intf, 3, 2, rep_data, 2);
|
||||
usb_get_report(intf, 3, 2, rep_data, 2);
|
||||
} while (rep_data[1] != 2 && limit++ < 5);
|
||||
|
||||
usb_set_intfdata(intf, wacom);
|
||||
return 0;
|
||||
|
||||
fail2: usb_buffer_free(dev, 10, wacom_wac->data, wacom->data_dma);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(wacom);
|
||||
kfree(wacom_wac);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void wacom_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct wacom *wacom = usb_get_intfdata (intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (wacom) {
|
||||
usb_kill_urb(wacom->irq);
|
||||
input_unregister_device(wacom->dev);
|
||||
usb_free_urb(wacom->irq);
|
||||
usb_buffer_free(interface_to_usbdev(intf), 10, wacom->wacom_wac->data, wacom->data_dma);
|
||||
kfree(wacom->wacom_wac);
|
||||
kfree(wacom);
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_driver wacom_driver = {
|
||||
.name = "wacom",
|
||||
.probe = wacom_probe,
|
||||
.disconnect = wacom_disconnect,
|
||||
};
|
||||
|
||||
static int __init wacom_init(void)
|
||||
{
|
||||
int result;
|
||||
wacom_driver.id_table = get_device_table();
|
||||
result = usb_register(&wacom_driver);
|
||||
if (result == 0)
|
||||
info(DRIVER_VERSION ":" DRIVER_DESC);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void __exit wacom_exit(void)
|
||||
{
|
||||
usb_deregister(&wacom_driver);
|
||||
}
|
||||
|
||||
module_init(wacom_init);
|
||||
module_exit(wacom_exit);
|
||||
675
drivers/usb/input/wacom_wac.c
Normal file
675
drivers/usb/input/wacom_wac.c
Normal file
@@ -0,0 +1,675 @@
|
||||
/*
|
||||
* drivers/usb/input/wacom_wac.c
|
||||
*
|
||||
* USB Wacom Graphire and Wacom Intuos tablet support - Wacom specific code
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
#include "wacom.h"
|
||||
#include "wacom_wac.h"
|
||||
|
||||
static int wacom_penpartner_irq(struct wacom_wac *wacom, void *wcombo)
|
||||
{
|
||||
unsigned char *data = wacom->data;
|
||||
|
||||
switch (data[0]) {
|
||||
case 1:
|
||||
if (data[5] & 0x80) {
|
||||
wacom->tool[0] = (data[5] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
|
||||
wacom->id[0] = (data[5] & 0x20) ? ERASER_DEVICE_ID : STYLUS_DEVICE_ID;
|
||||
wacom_report_key(wcombo, wacom->tool[0], 1);
|
||||
wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); /* report tool id */
|
||||
wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1]));
|
||||
wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3]));
|
||||
wacom_report_abs(wcombo, ABS_PRESSURE, (signed char)data[6] + 127);
|
||||
wacom_report_key(wcombo, BTN_TOUCH, ((signed char)data[6] > -127));
|
||||
wacom_report_key(wcombo, BTN_STYLUS, (data[5] & 0x40));
|
||||
} else {
|
||||
wacom_report_key(wcombo, wacom->tool[0], 0);
|
||||
wacom_report_abs(wcombo, ABS_MISC, 0); /* report tool id */
|
||||
wacom_report_abs(wcombo, ABS_PRESSURE, -1);
|
||||
wacom_report_key(wcombo, BTN_TOUCH, 0);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
wacom_report_key(wcombo, BTN_TOOL_PEN, 1);
|
||||
wacom_report_abs(wcombo, ABS_MISC, STYLUS_DEVICE_ID); /* report tool id */
|
||||
wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1]));
|
||||
wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3]));
|
||||
wacom_report_abs(wcombo, ABS_PRESSURE, (signed char)data[6] + 127);
|
||||
wacom_report_key(wcombo, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20));
|
||||
wacom_report_key(wcombo, BTN_STYLUS, (data[5] & 0x40));
|
||||
break;
|
||||
default:
|
||||
printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int wacom_pl_irq(struct wacom_wac *wacom, void *wcombo)
|
||||
{
|
||||
unsigned char *data = wacom->data;
|
||||
int prox, id, pressure;
|
||||
|
||||
if (data[0] != 2) {
|
||||
dbg("wacom_pl_irq: received unknown report #%d", data[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
prox = data[1] & 0x40;
|
||||
|
||||
id = ERASER_DEVICE_ID;
|
||||
if (prox) {
|
||||
|
||||
pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
|
||||
if (wacom->features->pressure_max > 255)
|
||||
pressure = (pressure << 1) | ((data[4] >> 6) & 1);
|
||||
pressure += (wacom->features->pressure_max + 1) / 2;
|
||||
|
||||
/*
|
||||
* if going from out of proximity into proximity select between the eraser
|
||||
* and the pen based on the state of the stylus2 button, choose eraser if
|
||||
* pressed else choose pen. if not a proximity change from out to in, send
|
||||
* an out of proximity for previous tool then a in for new tool.
|
||||
*/
|
||||
if (!wacom->tool[0]) {
|
||||
/* Eraser bit set for DTF */
|
||||
if (data[1] & 0x10)
|
||||
wacom->tool[1] = BTN_TOOL_RUBBER;
|
||||
else
|
||||
/* Going into proximity select tool */
|
||||
wacom->tool[1] = (data[4] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
|
||||
} else {
|
||||
/* was entered with stylus2 pressed */
|
||||
if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) {
|
||||
/* report out proximity for previous tool */
|
||||
wacom_report_key(wcombo, wacom->tool[1], 0);
|
||||
wacom_input_sync(wcombo);
|
||||
wacom->tool[1] = BTN_TOOL_PEN;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (wacom->tool[1] != BTN_TOOL_RUBBER) {
|
||||
/* Unknown tool selected default to pen tool */
|
||||
wacom->tool[1] = BTN_TOOL_PEN;
|
||||
id = STYLUS_DEVICE_ID;
|
||||
}
|
||||
wacom_report_key(wcombo, wacom->tool[1], prox); /* report in proximity for tool */
|
||||
wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
|
||||
wacom_report_abs(wcombo, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
|
||||
wacom_report_abs(wcombo, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
|
||||
wacom_report_abs(wcombo, ABS_PRESSURE, pressure);
|
||||
|
||||
wacom_report_key(wcombo, BTN_TOUCH, data[4] & 0x08);
|
||||
wacom_report_key(wcombo, BTN_STYLUS, data[4] & 0x10);
|
||||
/* Only allow the stylus2 button to be reported for the pen tool. */
|
||||
wacom_report_key(wcombo, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20));
|
||||
} else {
|
||||
/* report proximity-out of a (valid) tool */
|
||||
if (wacom->tool[1] != BTN_TOOL_RUBBER) {
|
||||
/* Unknown tool selected default to pen tool */
|
||||
wacom->tool[1] = BTN_TOOL_PEN;
|
||||
}
|
||||
wacom_report_key(wcombo, wacom->tool[1], prox);
|
||||
}
|
||||
|
||||
wacom->tool[0] = prox; /* Save proximity state */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int wacom_ptu_irq(struct wacom_wac *wacom, void *wcombo)
|
||||
{
|
||||
unsigned char *data = wacom->data;
|
||||
int id;
|
||||
|
||||
if (data[0] != 2) {
|
||||
printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (data[1] & 0x04) {
|
||||
wacom_report_key(wcombo, BTN_TOOL_RUBBER, data[1] & 0x20);
|
||||
wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x08);
|
||||
id = ERASER_DEVICE_ID;
|
||||
} else {
|
||||
wacom_report_key(wcombo, BTN_TOOL_PEN, data[1] & 0x20);
|
||||
wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x01);
|
||||
id = STYLUS_DEVICE_ID;
|
||||
}
|
||||
wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
|
||||
wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[2]));
|
||||
wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[4]));
|
||||
wacom_report_abs(wcombo, ABS_PRESSURE, wacom_le16_to_cpu(&data[6]));
|
||||
wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02);
|
||||
wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x10);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
|
||||
{
|
||||
unsigned char *data = wacom->data;
|
||||
int x, y, id, rw;
|
||||
|
||||
if (data[0] != 2) {
|
||||
dbg("wacom_graphire_irq: received unknown report #%d", data[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
id = STYLUS_DEVICE_ID;
|
||||
if (data[1] & 0x80) { /* in prox */
|
||||
|
||||
switch ((data[1] >> 5) & 3) {
|
||||
|
||||
case 0: /* Pen */
|
||||
wacom->tool[0] = BTN_TOOL_PEN;
|
||||
break;
|
||||
|
||||
case 1: /* Rubber */
|
||||
wacom->tool[0] = BTN_TOOL_RUBBER;
|
||||
id = ERASER_DEVICE_ID;
|
||||
break;
|
||||
|
||||
case 2: /* Mouse with wheel */
|
||||
wacom_report_key(wcombo, BTN_MIDDLE, data[1] & 0x04);
|
||||
if (wacom->features->type == WACOM_G4) {
|
||||
rw = data[7] & 0x04 ? (data[7] & 0x03)-4 : (data[7] & 0x03);
|
||||
wacom_report_rel(wcombo, REL_WHEEL, -rw);
|
||||
} else
|
||||
wacom_report_rel(wcombo, REL_WHEEL, -(signed char) data[6]);
|
||||
/* fall through */
|
||||
|
||||
case 3: /* Mouse without wheel */
|
||||
wacom->tool[0] = BTN_TOOL_MOUSE;
|
||||
id = CURSOR_DEVICE_ID;
|
||||
wacom_report_key(wcombo, BTN_LEFT, data[1] & 0x01);
|
||||
wacom_report_key(wcombo, BTN_RIGHT, data[1] & 0x02);
|
||||
if (wacom->features->type == WACOM_G4)
|
||||
wacom_report_abs(wcombo, ABS_DISTANCE, data[6] & 0x3f);
|
||||
else
|
||||
wacom_report_abs(wcombo, ABS_DISTANCE, data[7] & 0x3f);
|
||||
break;
|
||||
}
|
||||
x = wacom_le16_to_cpu(&data[2]);
|
||||
y = wacom_le16_to_cpu(&data[4]);
|
||||
wacom_report_abs(wcombo, ABS_X, x);
|
||||
wacom_report_abs(wcombo, ABS_Y, y);
|
||||
if (wacom->tool[0] != BTN_TOOL_MOUSE) {
|
||||
wacom_report_abs(wcombo, ABS_PRESSURE, data[6] | ((data[7] & 0x01) << 8));
|
||||
wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x01);
|
||||
wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02);
|
||||
wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x04);
|
||||
}
|
||||
wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
|
||||
wacom_report_key(wcombo, wacom->tool[0], 1);
|
||||
} else if (!(data[1] & 0x90)) {
|
||||
wacom_report_abs(wcombo, ABS_X, 0);
|
||||
wacom_report_abs(wcombo, ABS_Y, 0);
|
||||
if (wacom->tool[0] == BTN_TOOL_MOUSE) {
|
||||
wacom_report_key(wcombo, BTN_LEFT, 0);
|
||||
wacom_report_key(wcombo, BTN_RIGHT, 0);
|
||||
wacom_report_abs(wcombo, ABS_DISTANCE, 0);
|
||||
} else {
|
||||
wacom_report_abs(wcombo, ABS_PRESSURE, 0);
|
||||
wacom_report_key(wcombo, BTN_TOUCH, 0);
|
||||
wacom_report_key(wcombo, BTN_STYLUS, 0);
|
||||
wacom_report_key(wcombo, BTN_STYLUS2, 0);
|
||||
}
|
||||
wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
|
||||
wacom_report_key(wcombo, wacom->tool[0], 0);
|
||||
}
|
||||
|
||||
/* send pad data */
|
||||
if (wacom->features->type == WACOM_G4) {
|
||||
if (data[7] & 0xf8) {
|
||||
wacom_input_sync(wcombo); /* sync last event */
|
||||
wacom->id[1] = 1;
|
||||
wacom->serial[1] = (data[7] & 0xf8);
|
||||
wacom_report_key(wcombo, BTN_0, (data[7] & 0x40));
|
||||
wacom_report_key(wcombo, BTN_4, (data[7] & 0x80));
|
||||
rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3);
|
||||
wacom_report_rel(wcombo, REL_WHEEL, rw);
|
||||
wacom_report_key(wcombo, BTN_TOOL_FINGER, 0xf0);
|
||||
wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID);
|
||||
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
|
||||
} else if (wacom->id[1]) {
|
||||
wacom_input_sync(wcombo); /* sync last event */
|
||||
wacom->id[1] = 0;
|
||||
wacom_report_key(wcombo, BTN_0, (data[7] & 0x40));
|
||||
wacom_report_key(wcombo, BTN_4, (data[7] & 0x80));
|
||||
wacom_report_key(wcombo, BTN_TOOL_FINGER, 0);
|
||||
wacom_report_abs(wcombo, ABS_MISC, 0);
|
||||
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
|
||||
{
|
||||
unsigned char *data = wacom->data;
|
||||
int idx;
|
||||
|
||||
/* tool number */
|
||||
idx = data[1] & 0x01;
|
||||
|
||||
/* Enter report */
|
||||
if ((data[1] & 0xfc) == 0xc0) {
|
||||
/* serial number of the tool */
|
||||
wacom->serial[idx] = ((data[3] & 0x0f) << 28) +
|
||||
(data[4] << 20) + (data[5] << 12) +
|
||||
(data[6] << 4) + (data[7] >> 4);
|
||||
|
||||
wacom->id[idx] = (data[2] << 4) | (data[3] >> 4);
|
||||
switch (wacom->id[idx]) {
|
||||
case 0x812: /* Inking pen */
|
||||
case 0x801: /* Intuos3 Inking pen */
|
||||
case 0x012:
|
||||
wacom->tool[idx] = BTN_TOOL_PENCIL;
|
||||
break;
|
||||
case 0x822: /* Pen */
|
||||
case 0x842:
|
||||
case 0x852:
|
||||
case 0x823: /* Intuos3 Grip Pen */
|
||||
case 0x813: /* Intuos3 Classic Pen */
|
||||
case 0x885: /* Intuos3 Marker Pen */
|
||||
case 0x022:
|
||||
wacom->tool[idx] = BTN_TOOL_PEN;
|
||||
break;
|
||||
case 0x832: /* Stroke pen */
|
||||
case 0x032:
|
||||
wacom->tool[idx] = BTN_TOOL_BRUSH;
|
||||
break;
|
||||
case 0x007: /* Mouse 4D and 2D */
|
||||
case 0x09c:
|
||||
case 0x094:
|
||||
case 0x017: /* Intuos3 2D Mouse */
|
||||
wacom->tool[idx] = BTN_TOOL_MOUSE;
|
||||
break;
|
||||
case 0x096: /* Lens cursor */
|
||||
case 0x097: /* Intuos3 Lens cursor */
|
||||
wacom->tool[idx] = BTN_TOOL_LENS;
|
||||
break;
|
||||
case 0x82a: /* Eraser */
|
||||
case 0x85a:
|
||||
case 0x91a:
|
||||
case 0xd1a:
|
||||
case 0x0fa:
|
||||
case 0x82b: /* Intuos3 Grip Pen Eraser */
|
||||
case 0x81b: /* Intuos3 Classic Pen Eraser */
|
||||
case 0x91b: /* Intuos3 Airbrush Eraser */
|
||||
wacom->tool[idx] = BTN_TOOL_RUBBER;
|
||||
break;
|
||||
case 0xd12:
|
||||
case 0x912:
|
||||
case 0x112:
|
||||
case 0x913: /* Intuos3 Airbrush */
|
||||
wacom->tool[idx] = BTN_TOOL_AIRBRUSH;
|
||||
break;
|
||||
default: /* Unknown tool */
|
||||
wacom->tool[idx] = BTN_TOOL_PEN;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Exit report */
|
||||
if ((data[1] & 0xfe) == 0x80) {
|
||||
wacom_report_abs(wcombo, ABS_X, 0);
|
||||
wacom_report_abs(wcombo, ABS_Y, 0);
|
||||
wacom_report_abs(wcombo, ABS_DISTANCE, 0);
|
||||
if (wacom->tool[idx] >= BTN_TOOL_MOUSE) {
|
||||
wacom_report_key(wcombo, BTN_LEFT, 0);
|
||||
wacom_report_key(wcombo, BTN_MIDDLE, 0);
|
||||
wacom_report_key(wcombo, BTN_RIGHT, 0);
|
||||
wacom_report_key(wcombo, BTN_SIDE, 0);
|
||||
wacom_report_key(wcombo, BTN_EXTRA, 0);
|
||||
wacom_report_abs(wcombo, ABS_THROTTLE, 0);
|
||||
wacom_report_abs(wcombo, ABS_RZ, 0);
|
||||
} else {
|
||||
wacom_report_abs(wcombo, ABS_PRESSURE, 0);
|
||||
wacom_report_abs(wcombo, ABS_TILT_X, 0);
|
||||
wacom_report_abs(wcombo, ABS_TILT_Y, 0);
|
||||
wacom_report_key(wcombo, BTN_STYLUS, 0);
|
||||
wacom_report_key(wcombo, BTN_STYLUS2, 0);
|
||||
wacom_report_key(wcombo, BTN_TOUCH, 0);
|
||||
wacom_report_abs(wcombo, ABS_WHEEL, 0);
|
||||
}
|
||||
wacom_report_key(wcombo, wacom->tool[idx], 0);
|
||||
wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
|
||||
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wacom_intuos_general(struct wacom_wac *wacom, void *wcombo)
|
||||
{
|
||||
unsigned char *data = wacom->data;
|
||||
unsigned int t;
|
||||
|
||||
/* general pen packet */
|
||||
if ((data[1] & 0xb8) == 0xa0) {
|
||||
t = (data[6] << 2) | ((data[7] >> 6) & 3);
|
||||
wacom_report_abs(wcombo, ABS_PRESSURE, t);
|
||||
wacom_report_abs(wcombo, ABS_TILT_X,
|
||||
((data[7] << 1) & 0x7e) | (data[8] >> 7));
|
||||
wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f);
|
||||
wacom_report_key(wcombo, BTN_STYLUS, data[1] & 2);
|
||||
wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 4);
|
||||
wacom_report_key(wcombo, BTN_TOUCH, t > 10);
|
||||
}
|
||||
|
||||
/* airbrush second packet */
|
||||
if ((data[1] & 0xbc) == 0xb4) {
|
||||
wacom_report_abs(wcombo, ABS_WHEEL,
|
||||
(data[6] << 2) | ((data[7] >> 6) & 3));
|
||||
wacom_report_abs(wcombo, ABS_TILT_X,
|
||||
((data[7] << 1) & 0x7e) | (data[8] >> 7));
|
||||
wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
|
||||
{
|
||||
unsigned char *data = wacom->data;
|
||||
unsigned int t;
|
||||
int idx, result;
|
||||
|
||||
if (data[0] != 2 && data[0] != 5 && data[0] != 6 && data[0] != 12) {
|
||||
dbg("wacom_intuos_irq: received unknown report #%d", data[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* tool number */
|
||||
idx = data[1] & 0x01;
|
||||
|
||||
/* pad packets. Works as a second tool and is always in prox */
|
||||
if (data[0] == 12) {
|
||||
/* initiate the pad as a device */
|
||||
if (wacom->tool[1] != BTN_TOOL_FINGER)
|
||||
wacom->tool[1] = BTN_TOOL_FINGER;
|
||||
|
||||
wacom_report_key(wcombo, BTN_0, (data[5] & 0x01));
|
||||
wacom_report_key(wcombo, BTN_1, (data[5] & 0x02));
|
||||
wacom_report_key(wcombo, BTN_2, (data[5] & 0x04));
|
||||
wacom_report_key(wcombo, BTN_3, (data[5] & 0x08));
|
||||
wacom_report_key(wcombo, BTN_4, (data[6] & 0x01));
|
||||
wacom_report_key(wcombo, BTN_5, (data[6] & 0x02));
|
||||
wacom_report_key(wcombo, BTN_6, (data[6] & 0x04));
|
||||
wacom_report_key(wcombo, BTN_7, (data[6] & 0x08));
|
||||
wacom_report_abs(wcombo, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]);
|
||||
wacom_report_abs(wcombo, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
|
||||
|
||||
if((data[5] & 0x0f) | (data[6] & 0x0f) | (data[1] & 0x1f) |
|
||||
data[2] | (data[3] & 0x1f) | data[4])
|
||||
wacom_report_key(wcombo, wacom->tool[1], 1);
|
||||
else
|
||||
wacom_report_key(wcombo, wacom->tool[1], 0);
|
||||
wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID);
|
||||
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xffffffff);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* process in/out prox events */
|
||||
result = wacom_intuos_inout(wacom, wcombo);
|
||||
if (result)
|
||||
return result-1;
|
||||
|
||||
/* Only large I3 and I1 & I2 support Lense Cursor */
|
||||
if((wacom->tool[idx] == BTN_TOOL_LENS)
|
||||
&& ((wacom->features->type == INTUOS3)
|
||||
|| (wacom->features->type == INTUOS3S)))
|
||||
return 0;
|
||||
|
||||
/* Cintiq doesn't send data when RDY bit isn't set */
|
||||
if ((wacom->features->type == CINTIQ) && !(data[1] & 0x40))
|
||||
return 0;
|
||||
|
||||
if (wacom->features->type >= INTUOS3S) {
|
||||
wacom_report_abs(wcombo, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1));
|
||||
wacom_report_abs(wcombo, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1));
|
||||
wacom_report_abs(wcombo, ABS_DISTANCE, ((data[9] >> 2) & 0x3f));
|
||||
} else {
|
||||
wacom_report_abs(wcombo, ABS_X, wacom_be16_to_cpu(&data[2]));
|
||||
wacom_report_abs(wcombo, ABS_Y, wacom_be16_to_cpu(&data[4]));
|
||||
wacom_report_abs(wcombo, ABS_DISTANCE, ((data[9] >> 3) & 0x1f));
|
||||
}
|
||||
|
||||
/* process general packets */
|
||||
wacom_intuos_general(wacom, wcombo);
|
||||
|
||||
/* 4D mouse, 2D mouse, marker pen rotation, or Lens cursor packets */
|
||||
if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) {
|
||||
|
||||
if (data[1] & 0x02) {
|
||||
/* Rotation packet */
|
||||
if (wacom->features->type >= INTUOS3S) {
|
||||
/* I3 marker pen rotation reported as wheel
|
||||
* due to valuator limitation
|
||||
*/
|
||||
t = (data[6] << 3) | ((data[7] >> 5) & 7);
|
||||
t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) :
|
||||
((t-1) / 2 + 450)) : (450 - t / 2) ;
|
||||
wacom_report_abs(wcombo, ABS_WHEEL, t);
|
||||
} else {
|
||||
/* 4D mouse rotation packet */
|
||||
t = (data[6] << 3) | ((data[7] >> 5) & 7);
|
||||
wacom_report_abs(wcombo, ABS_RZ, (data[7] & 0x20) ?
|
||||
((t - 1) / 2) : -t / 2);
|
||||
}
|
||||
|
||||
} else if (!(data[1] & 0x10) && wacom->features->type < INTUOS3S) {
|
||||
/* 4D mouse packet */
|
||||
wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x01);
|
||||
wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02);
|
||||
wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x04);
|
||||
|
||||
wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x20);
|
||||
wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x10);
|
||||
t = (data[6] << 2) | ((data[7] >> 6) & 3);
|
||||
wacom_report_abs(wcombo, ABS_THROTTLE, (data[8] & 0x08) ? -t : t);
|
||||
|
||||
} else if (wacom->tool[idx] == BTN_TOOL_MOUSE) {
|
||||
/* 2D mouse packet */
|
||||
wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x04);
|
||||
wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x08);
|
||||
wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x10);
|
||||
wacom_report_rel(wcombo, REL_WHEEL, (data[8] & 0x01)
|
||||
- ((data[8] & 0x02) >> 1));
|
||||
|
||||
/* I3 2D mouse side buttons */
|
||||
if (wacom->features->type >= INTUOS3S && wacom->features->type <= INTUOS3L) {
|
||||
wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x40);
|
||||
wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x20);
|
||||
}
|
||||
|
||||
} else if (wacom->features->type < INTUOS3S || wacom->features->type == INTUOS3L) {
|
||||
/* Lens cursor packets */
|
||||
wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x01);
|
||||
wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02);
|
||||
wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x04);
|
||||
wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x10);
|
||||
wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x08);
|
||||
}
|
||||
}
|
||||
|
||||
wacom_report_abs(wcombo, ABS_MISC, wacom->id[idx]); /* report tool id */
|
||||
wacom_report_key(wcombo, wacom->tool[idx], 1);
|
||||
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int wacom_wac_irq(struct wacom_wac *wacom_wac, void *wcombo)
|
||||
{
|
||||
switch (wacom_wac->features->type) {
|
||||
case PENPARTNER:
|
||||
return (wacom_penpartner_irq(wacom_wac, wcombo));
|
||||
break;
|
||||
case PL:
|
||||
return (wacom_pl_irq(wacom_wac, wcombo));
|
||||
break;
|
||||
case WACOM_G4:
|
||||
case GRAPHIRE:
|
||||
return (wacom_graphire_irq(wacom_wac, wcombo));
|
||||
break;
|
||||
case PTU:
|
||||
return (wacom_ptu_irq(wacom_wac, wcombo));
|
||||
break;
|
||||
case INTUOS:
|
||||
case INTUOS3S:
|
||||
case INTUOS3:
|
||||
case INTUOS3L:
|
||||
case CINTIQ:
|
||||
return (wacom_intuos_irq(wacom_wac, wcombo));
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
|
||||
{
|
||||
switch (wacom_wac->features->type) {
|
||||
case WACOM_G4:
|
||||
input_dev_g4(input_dev, wacom_wac);
|
||||
/* fall through */
|
||||
case GRAPHIRE:
|
||||
input_dev_g(input_dev, wacom_wac);
|
||||
break;
|
||||
case INTUOS3:
|
||||
case INTUOS3L:
|
||||
case CINTIQ:
|
||||
input_dev_i3(input_dev, wacom_wac);
|
||||
/* fall through */
|
||||
case INTUOS3S:
|
||||
input_dev_i3s(input_dev, wacom_wac);
|
||||
case INTUOS:
|
||||
input_dev_i(input_dev, wacom_wac);
|
||||
break;
|
||||
case PL:
|
||||
case PTU:
|
||||
input_dev_pl(input_dev, wacom_wac);
|
||||
break;
|
||||
case PENPARTNER:
|
||||
input_dev_pt(input_dev, wacom_wac);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static struct wacom_features wacom_features[] = {
|
||||
{ "Wacom Penpartner", 7, 5040, 3780, 255, 0, PENPARTNER },
|
||||
{ "Wacom Graphire", 8, 10206, 7422, 511, 63, GRAPHIRE },
|
||||
{ "Wacom Graphire2 4x5", 8, 10206, 7422, 511, 63, GRAPHIRE },
|
||||
{ "Wacom Graphire2 5x7", 8, 13918, 10206, 511, 63, GRAPHIRE },
|
||||
{ "Wacom Graphire3", 8, 10208, 7424, 511, 63, GRAPHIRE },
|
||||
{ "Wacom Graphire3 6x8", 8, 16704, 12064, 511, 63, GRAPHIRE },
|
||||
{ "Wacom Graphire4 4x5", 8, 10208, 7424, 511, 63, WACOM_G4 },
|
||||
{ "Wacom Graphire4 6x8", 8, 16704, 12064, 511, 63, WACOM_G4 },
|
||||
{ "Wacom Volito", 8, 5104, 3712, 511, 63, GRAPHIRE },
|
||||
{ "Wacom PenStation2", 8, 3250, 2320, 255, 63, GRAPHIRE },
|
||||
{ "Wacom Volito2 4x5", 8, 5104, 3712, 511, 63, GRAPHIRE },
|
||||
{ "Wacom Volito2 2x3", 8, 3248, 2320, 511, 63, GRAPHIRE },
|
||||
{ "Wacom PenPartner2", 8, 3250, 2320, 255, 63, GRAPHIRE },
|
||||
{ "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 31, INTUOS },
|
||||
{ "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 31, INTUOS },
|
||||
{ "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 31, INTUOS },
|
||||
{ "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 31, INTUOS },
|
||||
{ "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 31, INTUOS },
|
||||
{ "Wacom PL400", 8, 5408, 4056, 255, 0, PL },
|
||||
{ "Wacom PL500", 8, 6144, 4608, 255, 0, PL },
|
||||
{ "Wacom PL600", 8, 6126, 4604, 255, 0, PL },
|
||||
{ "Wacom PL600SX", 8, 6260, 5016, 255, 0, PL },
|
||||
{ "Wacom PL550", 8, 6144, 4608, 511, 0, PL },
|
||||
{ "Wacom PL800", 8, 7220, 5780, 511, 0, PL },
|
||||
{ "Wacom PL700", 8, 6758, 5406, 511, 0, PL },
|
||||
{ "Wacom PL510", 8, 6282, 4762, 511, 0, PL },
|
||||
{ "Wacom DTU710", 8, 34080, 27660, 511, 0, PL },
|
||||
{ "Wacom DTF521", 8, 6282, 4762, 511, 0, PL },
|
||||
{ "Wacom DTF720", 8, 6858, 5506, 511, 0, PL },
|
||||
{ "Wacom Cintiq Partner",8, 20480, 15360, 511, 0, PTU },
|
||||
{ "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 31, INTUOS },
|
||||
{ "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 31, INTUOS },
|
||||
{ "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 31, INTUOS },
|
||||
{ "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 31, INTUOS },
|
||||
{ "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 31, INTUOS },
|
||||
{ "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 63, INTUOS3S },
|
||||
{ "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 63, INTUOS3 },
|
||||
{ "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 63, INTUOS3 },
|
||||
{ "Wacom Intuos3 12x12", 10, 60960, 60960, 1023, 63, INTUOS3L },
|
||||
{ "Wacom Intuos3 12x19", 10, 97536, 60960, 1023, 63, INTUOS3L },
|
||||
{ "Wacom Intuos3 6x11", 10, 54204, 31750, 1023, 63, INTUOS3 },
|
||||
{ "Wacom Intuos3 4x6", 10, 31496, 19685, 1023, 63, INTUOS3S },
|
||||
{ "Wacom Cintiq 21UX", 10, 87200, 65600, 1023, 63, CINTIQ },
|
||||
{ "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 31, INTUOS },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct usb_device_id wacom_ids[] = {
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x00) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x10) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x11) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x12) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x13) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x14) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x15) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x16) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x60) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x61) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x62) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x63) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x64) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x20) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x21) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x22) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x23) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x24) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x30) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x31) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x32) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x33) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x34) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x35) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x37) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x38) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x39) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC0) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC4) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x03) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x41) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x42) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x43) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x44) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x45) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB0) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB1) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB2) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB3) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB4) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB5) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB7) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x3F) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) },
|
||||
{ }
|
||||
};
|
||||
|
||||
const struct usb_device_id * get_device_table(void) {
|
||||
const struct usb_device_id * id_table = wacom_ids;
|
||||
return id_table;
|
||||
}
|
||||
|
||||
struct wacom_features * get_wacom_feature(const struct usb_device_id * id) {
|
||||
int index = id - wacom_ids;
|
||||
struct wacom_features *wf = &wacom_features[index];
|
||||
return wf;
|
||||
}
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, wacom_ids);
|
||||
49
drivers/usb/input/wacom_wac.h
Normal file
49
drivers/usb/input/wacom_wac.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* drivers/usb/input/wacom_wac.h
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
#ifndef WACOM_WAC_H
|
||||
#define WACOM_WAC_H
|
||||
|
||||
#define STYLUS_DEVICE_ID 0x02
|
||||
#define CURSOR_DEVICE_ID 0x06
|
||||
#define ERASER_DEVICE_ID 0x0A
|
||||
#define PAD_DEVICE_ID 0x0F
|
||||
|
||||
enum {
|
||||
PENPARTNER = 0,
|
||||
GRAPHIRE,
|
||||
WACOM_G4,
|
||||
PTU,
|
||||
PL,
|
||||
INTUOS,
|
||||
INTUOS3S,
|
||||
INTUOS3,
|
||||
INTUOS3L,
|
||||
CINTIQ,
|
||||
MAX_TYPE
|
||||
};
|
||||
|
||||
struct wacom_features {
|
||||
char *name;
|
||||
int pktlen;
|
||||
int x_max;
|
||||
int y_max;
|
||||
int pressure_max;
|
||||
int distance_max;
|
||||
int type;
|
||||
};
|
||||
|
||||
struct wacom_wac {
|
||||
signed char *data;
|
||||
int tool[2];
|
||||
int id[2];
|
||||
__u32 serial[2];
|
||||
struct wacom_features *features;
|
||||
};
|
||||
|
||||
#endif
|
||||
428
drivers/usb/input/xpad.c
Normal file
428
drivers/usb/input/xpad.c
Normal file
@@ -0,0 +1,428 @@
|
||||
/*
|
||||
* X-Box gamepad - v0.0.6
|
||||
*
|
||||
* Copyright (c) 2002 Marko Friedemann <mfr@bmx-chemnitz.de>
|
||||
* 2004 Oliver Schwartz <Oliver.Schwartz@gmx.de>,
|
||||
* Steven Toth <steve@toth.demon.co.uk>,
|
||||
* Franz Lehner <franz@caos.at>,
|
||||
* Ivan Hawkes <blackhawk@ivanhawkes.com>
|
||||
* 2005 Dominic Cerquetti <binary1230@yahoo.com>
|
||||
* 2006 Adam Buchbinder <adam.buchbinder@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*
|
||||
* This driver is based on:
|
||||
* - information from http://euc.jp/periphs/xbox-controller.ja.html
|
||||
* - the iForce driver drivers/char/joystick/iforce.c
|
||||
* - the skeleton-driver drivers/usb/usb-skeleton.c
|
||||
*
|
||||
* Thanks to:
|
||||
* - ITO Takayuki for providing essential xpad information on his website
|
||||
* - Vojtech Pavlik - iforce driver / input subsystem
|
||||
* - Greg Kroah-Hartman - usb-skeleton driver
|
||||
* - XBOX Linux project - extra USB id's
|
||||
*
|
||||
* TODO:
|
||||
* - fine tune axes (especially trigger axes)
|
||||
* - fix "analog" buttons (reported as digital now)
|
||||
* - get rumble working
|
||||
* - need USB IDs for other dance pads
|
||||
*
|
||||
* History:
|
||||
*
|
||||
* 2002-06-27 - 0.0.1 : first version, just said "XBOX HID controller"
|
||||
*
|
||||
* 2002-07-02 - 0.0.2 : basic working version
|
||||
* - all axes and 9 of the 10 buttons work (german InterAct device)
|
||||
* - the black button does not work
|
||||
*
|
||||
* 2002-07-14 - 0.0.3 : rework by Vojtech Pavlik
|
||||
* - indentation fixes
|
||||
* - usb + input init sequence fixes
|
||||
*
|
||||
* 2002-07-16 - 0.0.4 : minor changes, merge with Vojtech's v0.0.3
|
||||
* - verified the lack of HID and report descriptors
|
||||
* - verified that ALL buttons WORK
|
||||
* - fixed d-pad to axes mapping
|
||||
*
|
||||
* 2002-07-17 - 0.0.5 : simplified d-pad handling
|
||||
*
|
||||
* 2004-10-02 - 0.0.6 : DDR pad support
|
||||
* - borrowed from the XBOX linux kernel
|
||||
* - USB id's for commonly used dance pads are present
|
||||
* - dance pads will map D-PAD to buttons, not axes
|
||||
* - pass the module paramater 'dpad_to_buttons' to force
|
||||
* the D-PAD to map to buttons if your pad is not detected
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
#define DRIVER_VERSION "v0.0.6"
|
||||
#define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>"
|
||||
#define DRIVER_DESC "X-Box pad driver"
|
||||
|
||||
#define XPAD_PKT_LEN 32
|
||||
|
||||
/* xbox d-pads should map to buttons, as is required for DDR pads
|
||||
but we map them to axes when possible to simplify things */
|
||||
#define MAP_DPAD_TO_BUTTONS 0
|
||||
#define MAP_DPAD_TO_AXES 1
|
||||
#define MAP_DPAD_UNKNOWN -1
|
||||
|
||||
static int dpad_to_buttons;
|
||||
module_param(dpad_to_buttons, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads");
|
||||
|
||||
static const struct xpad_device {
|
||||
u16 idVendor;
|
||||
u16 idProduct;
|
||||
char *name;
|
||||
u8 dpad_mapping;
|
||||
} xpad_device[] = {
|
||||
{ 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", MAP_DPAD_TO_AXES },
|
||||
{ 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", MAP_DPAD_TO_AXES },
|
||||
{ 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", MAP_DPAD_TO_AXES },
|
||||
{ 0x045e, 0x0287, "Microsoft Xbox Controller S", MAP_DPAD_TO_AXES },
|
||||
{ 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", MAP_DPAD_TO_BUTTONS },
|
||||
{ 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", MAP_DPAD_TO_AXES },
|
||||
{ 0x046d, 0xca84, "Logitech Xbox Cordless Controller", MAP_DPAD_TO_AXES },
|
||||
{ 0x046d, 0xca88, "Logitech Compact Controller for Xbox", MAP_DPAD_TO_AXES },
|
||||
{ 0x05fd, 0x1007, "Mad Catz Controller (unverified)", MAP_DPAD_TO_AXES },
|
||||
{ 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)", MAP_DPAD_TO_AXES },
|
||||
{ 0x0738, 0x4516, "Mad Catz Control Pad", MAP_DPAD_TO_AXES },
|
||||
{ 0x0738, 0x4522, "Mad Catz LumiCON", MAP_DPAD_TO_AXES },
|
||||
{ 0x0738, 0x4526, "Mad Catz Control Pad Pro", MAP_DPAD_TO_AXES },
|
||||
{ 0x0738, 0x4536, "Mad Catz MicroCON", MAP_DPAD_TO_AXES },
|
||||
{ 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS },
|
||||
{ 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", MAP_DPAD_TO_AXES },
|
||||
{ 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS },
|
||||
{ 0x0c12, 0x8802, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES },
|
||||
{ 0x0c12, 0x8810, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES },
|
||||
{ 0x0c12, 0x9902, "HAMA VibraX - *FAULTY HARDWARE*", MAP_DPAD_TO_AXES },
|
||||
{ 0x0e4c, 0x1097, "Radica Gamester Controller", MAP_DPAD_TO_AXES },
|
||||
{ 0x0e4c, 0x2390, "Radica Games Jtech Controller", MAP_DPAD_TO_AXES},
|
||||
{ 0x0e6f, 0x0003, "Logic3 Freebird wireless Controller", MAP_DPAD_TO_AXES },
|
||||
{ 0x0e6f, 0x0005, "Eclipse wireless Controller", MAP_DPAD_TO_AXES },
|
||||
{ 0x0e6f, 0x0006, "Edge wireless Controller", MAP_DPAD_TO_AXES },
|
||||
{ 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", MAP_DPAD_TO_AXES },
|
||||
{ 0x0f30, 0x0202, "Joytech Advanced Controller", MAP_DPAD_TO_AXES },
|
||||
{ 0x0f30, 0x8888, "BigBen XBMiniPad Controller", MAP_DPAD_TO_AXES },
|
||||
{ 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", MAP_DPAD_TO_AXES },
|
||||
{ 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS },
|
||||
{ 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS },
|
||||
{ 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES },
|
||||
{ 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN }
|
||||
};
|
||||
|
||||
static const signed short xpad_btn[] = {
|
||||
BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, /* "analog" buttons */
|
||||
BTN_START, BTN_BACK, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */
|
||||
-1 /* terminating entry */
|
||||
};
|
||||
|
||||
/* only used if MAP_DPAD_TO_BUTTONS */
|
||||
static const signed short xpad_btn_pad[] = {
|
||||
BTN_LEFT, BTN_RIGHT, /* d-pad left, right */
|
||||
BTN_0, BTN_1, /* d-pad up, down (XXX names??) */
|
||||
-1 /* terminating entry */
|
||||
};
|
||||
|
||||
static const signed short xpad_abs[] = {
|
||||
ABS_X, ABS_Y, /* left stick */
|
||||
ABS_RX, ABS_RY, /* right stick */
|
||||
ABS_Z, ABS_RZ, /* triggers left/right */
|
||||
-1 /* terminating entry */
|
||||
};
|
||||
|
||||
/* only used if MAP_DPAD_TO_AXES */
|
||||
static const signed short xpad_abs_pad[] = {
|
||||
ABS_HAT0X, ABS_HAT0Y, /* d-pad axes */
|
||||
-1 /* terminating entry */
|
||||
};
|
||||
|
||||
static struct usb_device_id xpad_table [] = {
|
||||
{ USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE (usb, xpad_table);
|
||||
|
||||
struct usb_xpad {
|
||||
struct input_dev *dev; /* input device interface */
|
||||
struct usb_device *udev; /* usb device */
|
||||
|
||||
struct urb *irq_in; /* urb for interrupt in report */
|
||||
unsigned char *idata; /* input data */
|
||||
dma_addr_t idata_dma;
|
||||
|
||||
char phys[65]; /* physical device path */
|
||||
|
||||
int dpad_mapping; /* map d-pad to buttons or to axes */
|
||||
};
|
||||
|
||||
/*
|
||||
* xpad_process_packet
|
||||
*
|
||||
* Completes a request by converting the data into events for the
|
||||
* input subsystem.
|
||||
*
|
||||
* The used report descriptor was taken from ITO Takayukis website:
|
||||
* http://euc.jp/periphs/xbox-controller.ja.html
|
||||
*/
|
||||
|
||||
static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
|
||||
{
|
||||
struct input_dev *dev = xpad->dev;
|
||||
|
||||
/* left stick */
|
||||
input_report_abs(dev, ABS_X, (__s16) (((__s16)data[13] << 8) | data[12]));
|
||||
input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[15] << 8) | data[14]));
|
||||
|
||||
/* right stick */
|
||||
input_report_abs(dev, ABS_RX, (__s16) (((__s16)data[17] << 8) | data[16]));
|
||||
input_report_abs(dev, ABS_RY, (__s16) (((__s16)data[19] << 8) | data[18]));
|
||||
|
||||
/* triggers left/right */
|
||||
input_report_abs(dev, ABS_Z, data[10]);
|
||||
input_report_abs(dev, ABS_RZ, data[11]);
|
||||
|
||||
/* digital pad */
|
||||
if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) {
|
||||
input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04));
|
||||
input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01));
|
||||
} else /* xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS */ {
|
||||
input_report_key(dev, BTN_LEFT, data[2] & 0x04);
|
||||
input_report_key(dev, BTN_RIGHT, data[2] & 0x08);
|
||||
input_report_key(dev, BTN_0, data[2] & 0x01); // up
|
||||
input_report_key(dev, BTN_1, data[2] & 0x02); // down
|
||||
}
|
||||
|
||||
/* start/back buttons and stick press left/right */
|
||||
input_report_key(dev, BTN_START, data[2] & 0x10);
|
||||
input_report_key(dev, BTN_BACK, data[2] & 0x20);
|
||||
input_report_key(dev, BTN_THUMBL, data[2] & 0x40);
|
||||
input_report_key(dev, BTN_THUMBR, data[2] & 0x80);
|
||||
|
||||
/* "analog" buttons A, B, X, Y */
|
||||
input_report_key(dev, BTN_A, data[4]);
|
||||
input_report_key(dev, BTN_B, data[5]);
|
||||
input_report_key(dev, BTN_X, data[6]);
|
||||
input_report_key(dev, BTN_Y, data[7]);
|
||||
|
||||
/* "analog" buttons black, white */
|
||||
input_report_key(dev, BTN_C, data[8]);
|
||||
input_report_key(dev, BTN_Z, data[9]);
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
static void xpad_irq_in(struct urb *urb)
|
||||
{
|
||||
struct usb_xpad *xpad = urb->context;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
xpad_process_packet(xpad, 0, xpad->idata);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err ("%s - usb_submit_urb failed with result %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
static int xpad_open (struct input_dev *dev)
|
||||
{
|
||||
struct usb_xpad *xpad = dev->private;
|
||||
|
||||
xpad->irq_in->dev = xpad->udev;
|
||||
if (usb_submit_urb(xpad->irq_in, GFP_KERNEL))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xpad_close (struct input_dev *dev)
|
||||
{
|
||||
struct usb_xpad *xpad = dev->private;
|
||||
|
||||
usb_kill_urb(xpad->irq_in);
|
||||
}
|
||||
|
||||
static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
|
||||
{
|
||||
set_bit(abs, input_dev->absbit);
|
||||
|
||||
switch (abs) {
|
||||
case ABS_X:
|
||||
case ABS_Y:
|
||||
case ABS_RX:
|
||||
case ABS_RY: /* the two sticks */
|
||||
input_set_abs_params(input_dev, abs, -32768, 32767, 16, 128);
|
||||
break;
|
||||
case ABS_Z:
|
||||
case ABS_RZ: /* the triggers */
|
||||
input_set_abs_params(input_dev, abs, 0, 255, 0, 0);
|
||||
break;
|
||||
case ABS_HAT0X:
|
||||
case ABS_HAT0Y: /* the d-pad (only if MAP_DPAD_TO_AXES) */
|
||||
input_set_abs_params(input_dev, abs, -1, 1, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev (intf);
|
||||
struct usb_xpad *xpad;
|
||||
struct input_dev *input_dev;
|
||||
struct usb_endpoint_descriptor *ep_irq_in;
|
||||
int i;
|
||||
|
||||
for (i = 0; xpad_device[i].idVendor; i++) {
|
||||
if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) &&
|
||||
(le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct))
|
||||
break;
|
||||
}
|
||||
|
||||
xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!xpad || !input_dev)
|
||||
goto fail1;
|
||||
|
||||
xpad->idata = usb_buffer_alloc(udev, XPAD_PKT_LEN,
|
||||
GFP_ATOMIC, &xpad->idata_dma);
|
||||
if (!xpad->idata)
|
||||
goto fail1;
|
||||
|
||||
xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!xpad->irq_in)
|
||||
goto fail2;
|
||||
|
||||
xpad->udev = udev;
|
||||
xpad->dpad_mapping = xpad_device[i].dpad_mapping;
|
||||
if (xpad->dpad_mapping == MAP_DPAD_UNKNOWN)
|
||||
xpad->dpad_mapping = dpad_to_buttons;
|
||||
xpad->dev = input_dev;
|
||||
usb_make_path(udev, xpad->phys, sizeof(xpad->phys));
|
||||
strlcat(xpad->phys, "/input0", sizeof(xpad->phys));
|
||||
|
||||
input_dev->name = xpad_device[i].name;
|
||||
input_dev->phys = xpad->phys;
|
||||
usb_to_input_id(udev, &input_dev->id);
|
||||
input_dev->cdev.dev = &intf->dev;
|
||||
input_dev->private = xpad;
|
||||
input_dev->open = xpad_open;
|
||||
input_dev->close = xpad_close;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||
|
||||
/* set up buttons */
|
||||
for (i = 0; xpad_btn[i] >= 0; i++)
|
||||
set_bit(xpad_btn[i], input_dev->keybit);
|
||||
if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS)
|
||||
for (i = 0; xpad_btn_pad[i] >= 0; i++)
|
||||
set_bit(xpad_btn_pad[i], input_dev->keybit);
|
||||
|
||||
/* set up axes */
|
||||
for (i = 0; xpad_abs[i] >= 0; i++)
|
||||
xpad_set_up_abs(input_dev, xpad_abs[i]);
|
||||
if (xpad->dpad_mapping == MAP_DPAD_TO_AXES)
|
||||
for (i = 0; xpad_abs_pad[i] >= 0; i++)
|
||||
xpad_set_up_abs(input_dev, xpad_abs_pad[i]);
|
||||
|
||||
ep_irq_in = &intf->cur_altsetting->endpoint[0].desc;
|
||||
usb_fill_int_urb(xpad->irq_in, udev,
|
||||
usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress),
|
||||
xpad->idata, XPAD_PKT_LEN, xpad_irq_in,
|
||||
xpad, ep_irq_in->bInterval);
|
||||
xpad->irq_in->transfer_dma = xpad->idata_dma;
|
||||
xpad->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
input_register_device(xpad->dev);
|
||||
|
||||
usb_set_intfdata(intf, xpad);
|
||||
return 0;
|
||||
|
||||
fail2: usb_buffer_free(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(xpad);
|
||||
return -ENOMEM;
|
||||
|
||||
}
|
||||
|
||||
static void xpad_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_xpad *xpad = usb_get_intfdata (intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (xpad) {
|
||||
usb_kill_urb(xpad->irq_in);
|
||||
input_unregister_device(xpad->dev);
|
||||
usb_free_urb(xpad->irq_in);
|
||||
usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN,
|
||||
xpad->idata, xpad->idata_dma);
|
||||
kfree(xpad);
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_driver xpad_driver = {
|
||||
.name = "xpad",
|
||||
.probe = xpad_probe,
|
||||
.disconnect = xpad_disconnect,
|
||||
.id_table = xpad_table,
|
||||
};
|
||||
|
||||
static int __init usb_xpad_init(void)
|
||||
{
|
||||
int result = usb_register(&xpad_driver);
|
||||
if (result == 0)
|
||||
info(DRIVER_DESC ":" DRIVER_VERSION);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void __exit usb_xpad_exit(void)
|
||||
{
|
||||
usb_deregister(&xpad_driver);
|
||||
}
|
||||
|
||||
module_init(usb_xpad_init);
|
||||
module_exit(usb_xpad_exit);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
1002
drivers/usb/input/yealink.c
Normal file
1002
drivers/usb/input/yealink.c
Normal file
File diff suppressed because it is too large
Load Diff
220
drivers/usb/input/yealink.h
Normal file
220
drivers/usb/input/yealink.h
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* drivers/usb/input/yealink.h
|
||||
*
|
||||
* Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef INPUT_YEALINK_H
|
||||
#define INPUT_YEALINK_H
|
||||
|
||||
/* Using the control channel on interface 3 various aspects of the phone
|
||||
* can be controlled like LCD, LED, dialtone and the ringtone.
|
||||
*/
|
||||
|
||||
struct yld_ctl_packet {
|
||||
u8 cmd; /* command code, see below */
|
||||
u8 size; /* 1-11, size of used data bytes. */
|
||||
u16 offset; /* internal packet offset */
|
||||
u8 data[11];
|
||||
s8 sum; /* negative sum of 15 preceding bytes */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define USB_PKT_LEN sizeof(struct yld_ctl_packet)
|
||||
|
||||
/* The following yld_ctl_packet's are available: */
|
||||
|
||||
/* Init registers
|
||||
*
|
||||
* cmd 0x8e
|
||||
* size 10
|
||||
* offset 0
|
||||
* data 0,0,0,0....
|
||||
*/
|
||||
#define CMD_INIT 0x8e
|
||||
|
||||
/* Request key scan
|
||||
*
|
||||
* cmd 0x80
|
||||
* size 1
|
||||
* offset 0
|
||||
* data[0] on return returns the key number, if it changes there's a new
|
||||
* key pressed.
|
||||
*/
|
||||
#define CMD_KEYPRESS 0x80
|
||||
|
||||
/* Request scancode
|
||||
*
|
||||
* cmd 0x81
|
||||
* size 1
|
||||
* offset key number [0-1f]
|
||||
* data[0] on return returns the scancode
|
||||
*/
|
||||
#define CMD_SCANCODE 0x81
|
||||
|
||||
/* Set LCD
|
||||
*
|
||||
* cmd 0x04
|
||||
* size 1-11
|
||||
* offset 0-23
|
||||
* data segment bits
|
||||
*/
|
||||
#define CMD_LCD 0x04
|
||||
|
||||
/* Set led
|
||||
*
|
||||
* cmd 0x05
|
||||
* size 1
|
||||
* offset 0
|
||||
* data[0] 0 OFF / 1 ON
|
||||
*/
|
||||
#define CMD_LED 0x05
|
||||
|
||||
/* Set ringtone volume
|
||||
*
|
||||
* cmd 0x11
|
||||
* size 1
|
||||
* offset 0
|
||||
* data[0] 0-0xff volume
|
||||
*/
|
||||
#define CMD_RING_VOLUME 0x11
|
||||
|
||||
/* Set ringtone notes
|
||||
*
|
||||
* cmd 0x02
|
||||
* size 1-11
|
||||
* offset 0->
|
||||
* data binary representation LE16(-freq), LE16(duration) ....
|
||||
*/
|
||||
#define CMD_RING_NOTE 0x02
|
||||
|
||||
/* Sound ringtone via the speaker on the back
|
||||
*
|
||||
* cmd 0x03
|
||||
* size 1
|
||||
* offset 0
|
||||
* data[0] 0 OFF / 0x24 ON
|
||||
*/
|
||||
#define CMD_RINGTONE 0x03
|
||||
|
||||
/* Sound dial tone via the ear speaker
|
||||
*
|
||||
* cmd 0x09
|
||||
* size 1
|
||||
* offset 0
|
||||
* data[0] 0 OFF / 1 ON
|
||||
*/
|
||||
#define CMD_DIALTONE 0x09
|
||||
|
||||
#endif /* INPUT_YEALINK_H */
|
||||
|
||||
|
||||
#if defined(_SEG) && defined(_PIC)
|
||||
/* This table maps the LCD segments onto individual bit positions in the
|
||||
* yld_status struct.
|
||||
*/
|
||||
|
||||
/* LCD, each segment must be driven seperately.
|
||||
*
|
||||
* Layout:
|
||||
*
|
||||
* |[] [][] [][] [][] in |[][]
|
||||
* |[] M [][] D [][] : [][] out |[][]
|
||||
* store
|
||||
*
|
||||
* NEW REP SU MO TU WE TH FR SA
|
||||
*
|
||||
* [] [] [] [] [] [] [] [] [] [] [] []
|
||||
* [] [] [] [] [] [] [] [] [] [] [] []
|
||||
*/
|
||||
|
||||
/* Line 1
|
||||
* Format : 18.e8.M8.88...188
|
||||
* Icon names : M D : IN OUT STORE
|
||||
*/
|
||||
#define LCD_LINE1_OFFSET 0
|
||||
#define LCD_LINE1_SIZE 17
|
||||
|
||||
/* Note: first g then f => ! ! */
|
||||
/* _SEG( type a b c d e g f ) */
|
||||
_SEG('1', 0,0 , 22,2 , 22,2 , 0,0 , 0,0 , 0,0 , 0,0 ),
|
||||
_SEG('8', 20,1 , 20,2 , 20,4 , 20,8 , 21,4 , 21,2 , 21,1 ),
|
||||
_PIC('.', 22,1 , "M" ),
|
||||
_SEG('e', 18,1 , 18,2 , 18,4 , 18,1 , 19,2 , 18,1 , 19,1 ),
|
||||
_SEG('8', 16,1 , 16,2 , 16,4 , 16,8 , 17,4 , 17,2 , 17,1 ),
|
||||
_PIC('.', 15,8 , "D" ),
|
||||
_SEG('M', 14,1 , 14,2 , 14,4 , 14,1 , 15,4 , 15,2 , 15,1 ),
|
||||
_SEG('8', 12,1 , 12,2 , 12,4 , 12,8 , 13,4 , 13,2 , 13,1 ),
|
||||
_PIC('.', 11,8 , ":" ),
|
||||
_SEG('8', 10,1 , 10,2 , 10,4 , 10,8 , 11,4 , 11,2 , 11,1 ),
|
||||
_SEG('8', 8,1 , 8,2 , 8,4 , 8,8 , 9,4 , 9,2 , 9,1 ),
|
||||
_PIC('.', 7,1 , "IN" ),
|
||||
_PIC('.', 7,2 , "OUT" ),
|
||||
_PIC('.', 7,4 , "STORE" ),
|
||||
_SEG('1', 0,0 , 5,1 , 5,1 , 0,0 , 0,0 , 0,0 , 0,0 ),
|
||||
_SEG('8', 4,1 , 4,2 , 4,4 , 4,8 , 5,8 , 5,4 , 5,2 ),
|
||||
_SEG('8', 2,1 , 2,2 , 2,4 , 2,8 , 3,4 , 3,2 , 3,1 ),
|
||||
|
||||
/* Line 2
|
||||
* Format : .........
|
||||
* Pict. name : NEW REP SU MO TU WE TH FR SA
|
||||
*/
|
||||
#define LCD_LINE2_OFFSET LCD_LINE1_OFFSET + LCD_LINE1_SIZE
|
||||
#define LCD_LINE2_SIZE 9
|
||||
|
||||
_PIC('.', 23,2 , "NEW" ),
|
||||
_PIC('.', 23,4 , "REP" ),
|
||||
_PIC('.', 1,8 , "SU" ),
|
||||
_PIC('.', 1,4 , "MO" ),
|
||||
_PIC('.', 1,2 , "TU" ),
|
||||
_PIC('.', 1,1 , "WE" ),
|
||||
_PIC('.', 0,1 , "TH" ),
|
||||
_PIC('.', 0,2 , "FR" ),
|
||||
_PIC('.', 0,4 , "SA" ),
|
||||
|
||||
/* Line 3
|
||||
* Format : 888888888888
|
||||
*/
|
||||
#define LCD_LINE3_OFFSET LCD_LINE2_OFFSET + LCD_LINE2_SIZE
|
||||
#define LCD_LINE3_SIZE 12
|
||||
|
||||
_SEG('8', 22,16, 22,32, 22,64, 22,128, 23,128, 23,64, 23,32 ),
|
||||
_SEG('8', 20,16, 20,32, 20,64, 20,128, 21,128, 21,64, 21,32 ),
|
||||
_SEG('8', 18,16, 18,32, 18,64, 18,128, 19,128, 19,64, 19,32 ),
|
||||
_SEG('8', 16,16, 16,32, 16,64, 16,128, 17,128, 17,64, 17,32 ),
|
||||
_SEG('8', 14,16, 14,32, 14,64, 14,128, 15,128, 15,64, 15,32 ),
|
||||
_SEG('8', 12,16, 12,32, 12,64, 12,128, 13,128, 13,64, 13,32 ),
|
||||
_SEG('8', 10,16, 10,32, 10,64, 10,128, 11,128, 11,64, 11,32 ),
|
||||
_SEG('8', 8,16, 8,32, 8,64, 8,128, 9,128, 9,64, 9,32 ),
|
||||
_SEG('8', 6,16, 6,32, 6,64, 6,128, 7,128, 7,64, 7,32 ),
|
||||
_SEG('8', 4,16, 4,32, 4,64, 4,128, 5,128, 5,64, 5,32 ),
|
||||
_SEG('8', 2,16, 2,32, 2,64, 2,128, 3,128, 3,64, 3,32 ),
|
||||
_SEG('8', 0,16, 0,32, 0,64, 0,128, 1,128, 1,64, 1,32 ),
|
||||
|
||||
/* Line 4
|
||||
*
|
||||
* The LED, DIALTONE and RINGTONE are implemented as icons and use the same
|
||||
* sysfs interface.
|
||||
*/
|
||||
#define LCD_LINE4_OFFSET LCD_LINE3_OFFSET + LCD_LINE3_SIZE
|
||||
|
||||
_PIC('.', offsetof(struct yld_status, led) , 0x01, "LED" ),
|
||||
_PIC('.', offsetof(struct yld_status, dialtone) , 0x01, "DIALTONE" ),
|
||||
_PIC('.', offsetof(struct yld_status, ringtone) , 0x24, "RINGTONE" ),
|
||||
|
||||
#undef _SEG
|
||||
#undef _PIC
|
||||
#endif /* _SEG && _PIC */
|
||||
Reference in New Issue
Block a user