mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 10:41:38 -04:00
Input: move USB miscellaneous devices under drivers/input/misc
This will allow concentrating all input devices in one place
in {menu|x|q}config.
Signed-off-by: Dmitry Torokhov <[email protected]>
Acked-by: Greg Kroah-Hartman <[email protected]>
This commit is contained in:
parent
b5da20f8f7
commit
ba0acb5ee3
13 changed files with 89 additions and 105 deletions
|
|
@ -85,8 +85,6 @@ source "drivers/usb/class/Kconfig"
|
|||
|
||||
source "drivers/usb/storage/Kconfig"
|
||||
|
||||
source "drivers/usb/input/Kconfig"
|
||||
|
||||
source "drivers/usb/image/Kconfig"
|
||||
|
||||
source "drivers/usb/net/Kconfig"
|
||||
|
|
|
|||
|
|
@ -23,9 +23,6 @@ obj-$(CONFIG_USB_PRINTER) += class/
|
|||
obj-$(CONFIG_USB_STORAGE) += storage/
|
||||
obj-$(CONFIG_USB) += storage/
|
||||
|
||||
obj-$(CONFIG_USB_ATI_REMOTE) += input/
|
||||
obj-$(CONFIG_USB_POWERMATE) += input/
|
||||
|
||||
obj-$(CONFIG_USB_CATC) += net/
|
||||
obj-$(CONFIG_USB_KAWETH) += net/
|
||||
obj-$(CONFIG_USB_PEGASUS) += net/
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
#
|
||||
# USB Input driver configuration
|
||||
#
|
||||
comment "USB Input Devices"
|
||||
depends on USB
|
||||
|
||||
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_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_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.
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
#
|
||||
# Makefile for the USB input drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_USB_ATI_REMOTE) += ati_remote.o
|
||||
obj-$(CONFIG_USB_ATI_REMOTE2) += ati_remote2.o
|
||||
obj-$(CONFIG_USB_KEYSPAN_REMOTE) += keyspan_remote.o
|
||||
obj-$(CONFIG_USB_POWERMATE) += powermate.o
|
||||
obj-$(CONFIG_USB_YEALINK) += yealink.o
|
||||
|
||||
ifeq ($(CONFIG_USB_DEBUG),y)
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
endif
|
||||
|
|
@ -1,862 +0,0 @@
|
|||
/*
|
||||
* USB ATI Remote support
|
||||
*
|
||||
* Version 2.2.0 Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>
|
||||
* Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev
|
||||
*
|
||||
* This 2.2.0 version is a rewrite / cleanup of the 2.1.1 driver, including
|
||||
* porting to the 2.6 kernel interfaces, along with other modification
|
||||
* to better match the style of the existing usb/input drivers. However, the
|
||||
* protocol and hardware handling is essentially unchanged from 2.1.1.
|
||||
*
|
||||
* The 2.1.1 driver was derived from the usbati_remote and usbkbd drivers by
|
||||
* Vojtech Pavlik.
|
||||
*
|
||||
* Changes:
|
||||
*
|
||||
* Feb 2004: Torrey Hoffman <thoffman@arnor.net>
|
||||
* Version 2.2.0
|
||||
* Jun 2004: Torrey Hoffman <thoffman@arnor.net>
|
||||
* Version 2.2.1
|
||||
* Added key repeat support contributed by:
|
||||
* Vincent Vanackere <vanackere@lif.univ-mrs.fr>
|
||||
* Added support for the "Lola" remote contributed by:
|
||||
* Seth Cohn <sethcohn@yahoo.com>
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Hardware & software notes
|
||||
*
|
||||
* These remote controls are distributed by ATI as part of their
|
||||
* "All-In-Wonder" video card packages. The receiver self-identifies as a
|
||||
* "USB Receiver" with manufacturer "X10 Wireless Technology Inc".
|
||||
*
|
||||
* The "Lola" remote is available from X10. See:
|
||||
* http://www.x10.com/products/lola_sg1.htm
|
||||
* The Lola is similar to the ATI remote but has no mouse support, and slightly
|
||||
* different keys.
|
||||
*
|
||||
* It is possible to use multiple receivers and remotes on multiple computers
|
||||
* simultaneously by configuring them to use specific channels.
|
||||
*
|
||||
* The RF protocol used by the remote supports 16 distinct channels, 1 to 16.
|
||||
* Actually, it may even support more, at least in some revisions of the
|
||||
* hardware.
|
||||
*
|
||||
* Each remote can be configured to transmit on one channel as follows:
|
||||
* - Press and hold the "hand icon" button.
|
||||
* - When the red LED starts to blink, let go of the "hand icon" button.
|
||||
* - When it stops blinking, input the channel code as two digits, from 01
|
||||
* to 16, and press the hand icon again.
|
||||
*
|
||||
* The timing can be a little tricky. Try loading the module with debug=1
|
||||
* to have the kernel print out messages about the remote control number
|
||||
* and mask. Note: debugging prints remote numbers as zero-based hexadecimal.
|
||||
*
|
||||
* The driver has a "channel_mask" parameter. This bitmask specifies which
|
||||
* channels will be ignored by the module. To mask out channels, just add
|
||||
* all the 2^channel_number values together.
|
||||
*
|
||||
* For instance, set channel_mask = 2^4 = 16 (binary 10000) to make ati_remote
|
||||
* ignore signals coming from remote controls transmitting on channel 4, but
|
||||
* accept all other channels.
|
||||
*
|
||||
* Or, set channel_mask = 65533, (0xFFFD), and all channels except 1 will be
|
||||
* ignored.
|
||||
*
|
||||
* The default is 0 (respond to all channels). Bit 0 and bits 17-32 of this
|
||||
* parameter are unused.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/usb/input.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
||||
/*
|
||||
* Module and Version Information, Module Parameters
|
||||
*/
|
||||
|
||||
#define ATI_REMOTE_VENDOR_ID 0x0bc7
|
||||
#define ATI_REMOTE_PRODUCT_ID 0x004
|
||||
#define LOLA_REMOTE_PRODUCT_ID 0x002
|
||||
#define MEDION_REMOTE_PRODUCT_ID 0x006
|
||||
|
||||
#define DRIVER_VERSION "2.2.1"
|
||||
#define DRIVER_AUTHOR "Torrey Hoffman <[email protected]>"
|
||||
#define DRIVER_DESC "ATI/X10 RF USB Remote Control"
|
||||
|
||||
#define NAME_BUFSIZE 80 /* size of product name, path buffers */
|
||||
#define DATA_BUFSIZE 63 /* size of URB data buffers */
|
||||
|
||||
/*
|
||||
* Duplicate event filtering time.
|
||||
* Sequential, identical KIND_FILTERED inputs with less than
|
||||
* FILTER_TIME milliseconds between them are considered as repeat
|
||||
* events. The hardware generates 5 events for the first keypress
|
||||
* and we have to take this into account for an accurate repeat
|
||||
* behaviour.
|
||||
*/
|
||||
#define FILTER_TIME 60 /* msec */
|
||||
#define REPEAT_DELAY 500 /* msec */
|
||||
|
||||
static unsigned long channel_mask;
|
||||
module_param(channel_mask, ulong, 0644);
|
||||
MODULE_PARM_DESC(channel_mask, "Bitmask of remote control channels to ignore");
|
||||
|
||||
static int debug;
|
||||
module_param(debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
|
||||
|
||||
static int repeat_filter = FILTER_TIME;
|
||||
module_param(repeat_filter, int, 0644);
|
||||
MODULE_PARM_DESC(repeat_filter, "Repeat filter time, default = 60 msec");
|
||||
|
||||
static int repeat_delay = REPEAT_DELAY;
|
||||
module_param(repeat_delay, int, 0644);
|
||||
MODULE_PARM_DESC(repeat_delay, "Delay before sending repeats, default = 500 msec");
|
||||
|
||||
#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
|
||||
#undef err
|
||||
#define err(format, arg...) printk(KERN_ERR format , ## arg)
|
||||
|
||||
static struct usb_device_id ati_remote_table[] = {
|
||||
{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID) },
|
||||
{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID) },
|
||||
{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID) },
|
||||
{} /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, ati_remote_table);
|
||||
|
||||
/* Get hi and low bytes of a 16-bits int */
|
||||
#define HI(a) ((unsigned char)((a) >> 8))
|
||||
#define LO(a) ((unsigned char)((a) & 0xff))
|
||||
|
||||
#define SEND_FLAG_IN_PROGRESS 1
|
||||
#define SEND_FLAG_COMPLETE 2
|
||||
|
||||
/* Device initialization strings */
|
||||
static char init1[] = { 0x01, 0x00, 0x20, 0x14 };
|
||||
static char init2[] = { 0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 };
|
||||
|
||||
struct ati_remote {
|
||||
struct input_dev *idev;
|
||||
struct usb_device *udev;
|
||||
struct usb_interface *interface;
|
||||
|
||||
struct urb *irq_urb;
|
||||
struct urb *out_urb;
|
||||
struct usb_endpoint_descriptor *endpoint_in;
|
||||
struct usb_endpoint_descriptor *endpoint_out;
|
||||
unsigned char *inbuf;
|
||||
unsigned char *outbuf;
|
||||
dma_addr_t inbuf_dma;
|
||||
dma_addr_t outbuf_dma;
|
||||
|
||||
unsigned char old_data[2]; /* Detect duplicate events */
|
||||
unsigned long old_jiffies;
|
||||
unsigned long acc_jiffies; /* handle acceleration */
|
||||
unsigned long first_jiffies;
|
||||
|
||||
unsigned int repeat_count;
|
||||
|
||||
char name[NAME_BUFSIZE];
|
||||
char phys[NAME_BUFSIZE];
|
||||
|
||||
wait_queue_head_t wait;
|
||||
int send_flags;
|
||||
};
|
||||
|
||||
/* "Kinds" of messages sent from the hardware to the driver. */
|
||||
#define KIND_END 0
|
||||
#define KIND_LITERAL 1 /* Simply pass to input system */
|
||||
#define KIND_FILTERED 2 /* Add artificial key-up events, drop keyrepeats */
|
||||
#define KIND_LU 3 /* Directional keypad diagonals - left up, */
|
||||
#define KIND_RU 4 /* right up, */
|
||||
#define KIND_LD 5 /* left down, */
|
||||
#define KIND_RD 6 /* right down */
|
||||
#define KIND_ACCEL 7 /* Directional keypad - left, right, up, down.*/
|
||||
|
||||
/* Translation table from hardware messages to input events. */
|
||||
static const struct {
|
||||
short kind;
|
||||
unsigned char data1, data2;
|
||||
int type;
|
||||
unsigned int code;
|
||||
int value;
|
||||
} ati_remote_tbl[] = {
|
||||
/* Directional control pad axes */
|
||||
{KIND_ACCEL, 0x35, 0x70, EV_REL, REL_X, -1}, /* left */
|
||||
{KIND_ACCEL, 0x36, 0x71, EV_REL, REL_X, 1}, /* right */
|
||||
{KIND_ACCEL, 0x37, 0x72, EV_REL, REL_Y, -1}, /* up */
|
||||
{KIND_ACCEL, 0x38, 0x73, EV_REL, REL_Y, 1}, /* down */
|
||||
/* Directional control pad diagonals */
|
||||
{KIND_LU, 0x39, 0x74, EV_REL, 0, 0}, /* left up */
|
||||
{KIND_RU, 0x3a, 0x75, EV_REL, 0, 0}, /* right up */
|
||||
{KIND_LD, 0x3c, 0x77, EV_REL, 0, 0}, /* left down */
|
||||
{KIND_RD, 0x3b, 0x76, EV_REL, 0, 0}, /* right down */
|
||||
|
||||
/* "Mouse button" buttons */
|
||||
{KIND_LITERAL, 0x3d, 0x78, EV_KEY, BTN_LEFT, 1}, /* left btn down */
|
||||
{KIND_LITERAL, 0x3e, 0x79, EV_KEY, BTN_LEFT, 0}, /* left btn up */
|
||||
{KIND_LITERAL, 0x41, 0x7c, EV_KEY, BTN_RIGHT, 1},/* right btn down */
|
||||
{KIND_LITERAL, 0x42, 0x7d, EV_KEY, BTN_RIGHT, 0},/* right btn up */
|
||||
|
||||
/* Artificial "doubleclick" events are generated by the hardware.
|
||||
* They are mapped to the "side" and "extra" mouse buttons here. */
|
||||
{KIND_FILTERED, 0x3f, 0x7a, EV_KEY, BTN_SIDE, 1}, /* left dblclick */
|
||||
{KIND_FILTERED, 0x43, 0x7e, EV_KEY, BTN_EXTRA, 1},/* right dblclick */
|
||||
|
||||
/* keyboard. */
|
||||
{KIND_FILTERED, 0xd2, 0x0d, EV_KEY, KEY_1, 1},
|
||||
{KIND_FILTERED, 0xd3, 0x0e, EV_KEY, KEY_2, 1},
|
||||
{KIND_FILTERED, 0xd4, 0x0f, EV_KEY, KEY_3, 1},
|
||||
{KIND_FILTERED, 0xd5, 0x10, EV_KEY, KEY_4, 1},
|
||||
{KIND_FILTERED, 0xd6, 0x11, EV_KEY, KEY_5, 1},
|
||||
{KIND_FILTERED, 0xd7, 0x12, EV_KEY, KEY_6, 1},
|
||||
{KIND_FILTERED, 0xd8, 0x13, EV_KEY, KEY_7, 1},
|
||||
{KIND_FILTERED, 0xd9, 0x14, EV_KEY, KEY_8, 1},
|
||||
{KIND_FILTERED, 0xda, 0x15, EV_KEY, KEY_9, 1},
|
||||
{KIND_FILTERED, 0xdc, 0x17, EV_KEY, KEY_0, 1},
|
||||
{KIND_FILTERED, 0xc5, 0x00, EV_KEY, KEY_A, 1},
|
||||
{KIND_FILTERED, 0xc6, 0x01, EV_KEY, KEY_B, 1},
|
||||
{KIND_FILTERED, 0xde, 0x19, EV_KEY, KEY_C, 1},
|
||||
{KIND_FILTERED, 0xe0, 0x1b, EV_KEY, KEY_D, 1},
|
||||
{KIND_FILTERED, 0xe6, 0x21, EV_KEY, KEY_E, 1},
|
||||
{KIND_FILTERED, 0xe8, 0x23, EV_KEY, KEY_F, 1},
|
||||
|
||||
/* "special" keys */
|
||||
{KIND_FILTERED, 0xdd, 0x18, EV_KEY, KEY_KPENTER, 1}, /* "check" */
|
||||
{KIND_FILTERED, 0xdb, 0x16, EV_KEY, KEY_MENU, 1}, /* "menu" */
|
||||
{KIND_FILTERED, 0xc7, 0x02, EV_KEY, KEY_POWER, 1}, /* Power */
|
||||
{KIND_FILTERED, 0xc8, 0x03, EV_KEY, KEY_TV, 1}, /* TV */
|
||||
{KIND_FILTERED, 0xc9, 0x04, EV_KEY, KEY_DVD, 1}, /* DVD */
|
||||
{KIND_FILTERED, 0xca, 0x05, EV_KEY, KEY_WWW, 1}, /* WEB */
|
||||
{KIND_FILTERED, 0xcb, 0x06, EV_KEY, KEY_BOOKMARKS, 1}, /* "book" */
|
||||
{KIND_FILTERED, 0xcc, 0x07, EV_KEY, KEY_EDIT, 1}, /* "hand" */
|
||||
{KIND_FILTERED, 0xe1, 0x1c, EV_KEY, KEY_COFFEE, 1}, /* "timer" */
|
||||
{KIND_FILTERED, 0xe5, 0x20, EV_KEY, KEY_FRONT, 1}, /* "max" */
|
||||
{KIND_FILTERED, 0xe2, 0x1d, EV_KEY, KEY_LEFT, 1}, /* left */
|
||||
{KIND_FILTERED, 0xe4, 0x1f, EV_KEY, KEY_RIGHT, 1}, /* right */
|
||||
{KIND_FILTERED, 0xe7, 0x22, EV_KEY, KEY_DOWN, 1}, /* down */
|
||||
{KIND_FILTERED, 0xdf, 0x1a, EV_KEY, KEY_UP, 1}, /* up */
|
||||
{KIND_FILTERED, 0xe3, 0x1e, EV_KEY, KEY_OK, 1}, /* "OK" */
|
||||
{KIND_FILTERED, 0xce, 0x09, EV_KEY, KEY_VOLUMEDOWN, 1}, /* VOL + */
|
||||
{KIND_FILTERED, 0xcd, 0x08, EV_KEY, KEY_VOLUMEUP, 1}, /* VOL - */
|
||||
{KIND_FILTERED, 0xcf, 0x0a, EV_KEY, KEY_MUTE, 1}, /* MUTE */
|
||||
{KIND_FILTERED, 0xd0, 0x0b, EV_KEY, KEY_CHANNELUP, 1}, /* CH + */
|
||||
{KIND_FILTERED, 0xd1, 0x0c, EV_KEY, KEY_CHANNELDOWN, 1},/* CH - */
|
||||
{KIND_FILTERED, 0xec, 0x27, EV_KEY, KEY_RECORD, 1}, /* ( o) red */
|
||||
{KIND_FILTERED, 0xea, 0x25, EV_KEY, KEY_PLAY, 1}, /* ( >) */
|
||||
{KIND_FILTERED, 0xe9, 0x24, EV_KEY, KEY_REWIND, 1}, /* (<<) */
|
||||
{KIND_FILTERED, 0xeb, 0x26, EV_KEY, KEY_FORWARD, 1}, /* (>>) */
|
||||
{KIND_FILTERED, 0xed, 0x28, EV_KEY, KEY_STOP, 1}, /* ([]) */
|
||||
{KIND_FILTERED, 0xee, 0x29, EV_KEY, KEY_PAUSE, 1}, /* ('') */
|
||||
{KIND_FILTERED, 0xf0, 0x2b, EV_KEY, KEY_PREVIOUS, 1}, /* (<-) */
|
||||
{KIND_FILTERED, 0xef, 0x2a, EV_KEY, KEY_NEXT, 1}, /* (>+) */
|
||||
{KIND_FILTERED, 0xf2, 0x2D, EV_KEY, KEY_INFO, 1}, /* PLAYING */
|
||||
{KIND_FILTERED, 0xf3, 0x2E, EV_KEY, KEY_HOME, 1}, /* TOP */
|
||||
{KIND_FILTERED, 0xf4, 0x2F, EV_KEY, KEY_END, 1}, /* END */
|
||||
{KIND_FILTERED, 0xf5, 0x30, EV_KEY, KEY_SELECT, 1}, /* SELECT */
|
||||
|
||||
{KIND_END, 0x00, 0x00, EV_MAX + 1, 0, 0}
|
||||
};
|
||||
|
||||
/* Local function prototypes */
|
||||
static void ati_remote_dump (unsigned char *data, unsigned int actual_length);
|
||||
static int ati_remote_open (struct input_dev *inputdev);
|
||||
static void ati_remote_close (struct input_dev *inputdev);
|
||||
static int ati_remote_sendpacket (struct ati_remote *ati_remote, u16 cmd, unsigned char *data);
|
||||
static void ati_remote_irq_out (struct urb *urb);
|
||||
static void ati_remote_irq_in (struct urb *urb);
|
||||
static void ati_remote_input_report (struct urb *urb);
|
||||
static int ati_remote_initialize (struct ati_remote *ati_remote);
|
||||
static int ati_remote_probe (struct usb_interface *interface, const struct usb_device_id *id);
|
||||
static void ati_remote_disconnect (struct usb_interface *interface);
|
||||
|
||||
/* usb specific object to register with the usb subsystem */
|
||||
static struct usb_driver ati_remote_driver = {
|
||||
.name = "ati_remote",
|
||||
.probe = ati_remote_probe,
|
||||
.disconnect = ati_remote_disconnect,
|
||||
.id_table = ati_remote_table,
|
||||
};
|
||||
|
||||
/*
|
||||
* ati_remote_dump_input
|
||||
*/
|
||||
static void ati_remote_dump(unsigned char *data, unsigned int len)
|
||||
{
|
||||
if ((len == 1) && (data[0] != (unsigned char)0xff) && (data[0] != 0x00))
|
||||
warn("Weird byte 0x%02x", data[0]);
|
||||
else if (len == 4)
|
||||
warn("Weird key %02x %02x %02x %02x",
|
||||
data[0], data[1], data[2], data[3]);
|
||||
else
|
||||
warn("Weird data, len=%d %02x %02x %02x %02x %02x %02x ...",
|
||||
len, data[0], data[1], data[2], data[3], data[4], data[5]);
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_open
|
||||
*/
|
||||
static int ati_remote_open(struct input_dev *inputdev)
|
||||
{
|
||||
struct ati_remote *ati_remote = input_get_drvdata(inputdev);
|
||||
|
||||
/* On first open, submit the read urb which was set up previously. */
|
||||
ati_remote->irq_urb->dev = ati_remote->udev;
|
||||
if (usb_submit_urb(ati_remote->irq_urb, GFP_KERNEL)) {
|
||||
dev_err(&ati_remote->interface->dev,
|
||||
"%s: usb_submit_urb failed!\n", __FUNCTION__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_close
|
||||
*/
|
||||
static void ati_remote_close(struct input_dev *inputdev)
|
||||
{
|
||||
struct ati_remote *ati_remote = input_get_drvdata(inputdev);
|
||||
|
||||
usb_kill_urb(ati_remote->irq_urb);
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_irq_out
|
||||
*/
|
||||
static void ati_remote_irq_out(struct urb *urb)
|
||||
{
|
||||
struct ati_remote *ati_remote = urb->context;
|
||||
|
||||
if (urb->status) {
|
||||
dev_dbg(&ati_remote->interface->dev, "%s: status %d\n",
|
||||
__FUNCTION__, urb->status);
|
||||
return;
|
||||
}
|
||||
|
||||
ati_remote->send_flags |= SEND_FLAG_COMPLETE;
|
||||
wmb();
|
||||
wake_up(&ati_remote->wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_sendpacket
|
||||
*
|
||||
* Used to send device initialization strings
|
||||
*/
|
||||
static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigned char *data)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
/* Set up out_urb */
|
||||
memcpy(ati_remote->out_urb->transfer_buffer + 1, data, LO(cmd));
|
||||
((char *) ati_remote->out_urb->transfer_buffer)[0] = HI(cmd);
|
||||
|
||||
ati_remote->out_urb->transfer_buffer_length = LO(cmd) + 1;
|
||||
ati_remote->out_urb->dev = ati_remote->udev;
|
||||
ati_remote->send_flags = SEND_FLAG_IN_PROGRESS;
|
||||
|
||||
retval = usb_submit_urb(ati_remote->out_urb, GFP_ATOMIC);
|
||||
if (retval) {
|
||||
dev_dbg(&ati_remote->interface->dev,
|
||||
"sendpacket: usb_submit_urb failed: %d\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
wait_event_timeout(ati_remote->wait,
|
||||
((ati_remote->out_urb->status != -EINPROGRESS) ||
|
||||
(ati_remote->send_flags & SEND_FLAG_COMPLETE)),
|
||||
HZ);
|
||||
usb_kill_urb(ati_remote->out_urb);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_event_lookup
|
||||
*/
|
||||
static int ati_remote_event_lookup(int rem, unsigned char d1, unsigned char d2)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) {
|
||||
/*
|
||||
* Decide if the table entry matches the remote input.
|
||||
*/
|
||||
if ((((ati_remote_tbl[i].data1 & 0x0f) == (d1 & 0x0f))) &&
|
||||
((((ati_remote_tbl[i].data1 >> 4) -
|
||||
(d1 >> 4) + rem) & 0x0f) == 0x0f) &&
|
||||
(ati_remote_tbl[i].data2 == d2))
|
||||
return i;
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_compute_accel
|
||||
*
|
||||
* Implements acceleration curve for directional control pad
|
||||
* If elapsed time since last event is > 1/4 second, user "stopped",
|
||||
* so reset acceleration. Otherwise, user is probably holding the control
|
||||
* pad down, so we increase acceleration, ramping up over two seconds to
|
||||
* a maximum speed.
|
||||
*/
|
||||
static int ati_remote_compute_accel(struct ati_remote *ati_remote)
|
||||
{
|
||||
static const char accel[] = { 1, 2, 4, 6, 9, 13, 20 };
|
||||
unsigned long now = jiffies;
|
||||
int acc;
|
||||
|
||||
if (time_after(now, ati_remote->old_jiffies + msecs_to_jiffies(250))) {
|
||||
acc = 1;
|
||||
ati_remote->acc_jiffies = now;
|
||||
}
|
||||
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(125)))
|
||||
acc = accel[0];
|
||||
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(250)))
|
||||
acc = accel[1];
|
||||
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(500)))
|
||||
acc = accel[2];
|
||||
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1000)))
|
||||
acc = accel[3];
|
||||
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1500)))
|
||||
acc = accel[4];
|
||||
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(2000)))
|
||||
acc = accel[5];
|
||||
else
|
||||
acc = accel[6];
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_report_input
|
||||
*/
|
||||
static void ati_remote_input_report(struct urb *urb)
|
||||
{
|
||||
struct ati_remote *ati_remote = urb->context;
|
||||
unsigned char *data= ati_remote->inbuf;
|
||||
struct input_dev *dev = ati_remote->idev;
|
||||
int index, acc;
|
||||
int remote_num;
|
||||
|
||||
/* Deal with strange looking inputs */
|
||||
if ( (urb->actual_length != 4) || (data[0] != 0x14) ||
|
||||
((data[3] & 0x0f) != 0x00) ) {
|
||||
ati_remote_dump(data, urb->actual_length);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mask unwanted remote channels. */
|
||||
/* note: remote_num is 0-based, channel 1 on remote == 0 here */
|
||||
remote_num = (data[3] >> 4) & 0x0f;
|
||||
if (channel_mask & (1 << (remote_num + 1))) {
|
||||
dbginfo(&ati_remote->interface->dev,
|
||||
"Masked input from channel 0x%02x: data %02x,%02x, mask= 0x%02lx\n",
|
||||
remote_num, data[1], data[2], channel_mask);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Look up event code index in translation table */
|
||||
index = ati_remote_event_lookup(remote_num, data[1], data[2]);
|
||||
if (index < 0) {
|
||||
dev_warn(&ati_remote->interface->dev,
|
||||
"Unknown input from channel 0x%02x: data %02x,%02x\n",
|
||||
remote_num, data[1], data[2]);
|
||||
return;
|
||||
}
|
||||
dbginfo(&ati_remote->interface->dev,
|
||||
"channel 0x%02x; data %02x,%02x; index %d; keycode %d\n",
|
||||
remote_num, data[1], data[2], index, ati_remote_tbl[index].code);
|
||||
|
||||
if (ati_remote_tbl[index].kind == KIND_LITERAL) {
|
||||
input_event(dev, ati_remote_tbl[index].type,
|
||||
ati_remote_tbl[index].code,
|
||||
ati_remote_tbl[index].value);
|
||||
input_sync(dev);
|
||||
|
||||
ati_remote->old_jiffies = jiffies;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ati_remote_tbl[index].kind == KIND_FILTERED) {
|
||||
unsigned long now = jiffies;
|
||||
|
||||
/* Filter duplicate events which happen "too close" together. */
|
||||
if (ati_remote->old_data[0] == data[1] &&
|
||||
ati_remote->old_data[1] == data[2] &&
|
||||
time_before(now, ati_remote->old_jiffies +
|
||||
msecs_to_jiffies(repeat_filter))) {
|
||||
ati_remote->repeat_count++;
|
||||
} else {
|
||||
ati_remote->repeat_count = 0;
|
||||
ati_remote->first_jiffies = now;
|
||||
}
|
||||
|
||||
ati_remote->old_data[0] = data[1];
|
||||
ati_remote->old_data[1] = data[2];
|
||||
ati_remote->old_jiffies = now;
|
||||
|
||||
/* Ensure we skip at least the 4 first duplicate events (generated
|
||||
* by a single keypress), and continue skipping until repeat_delay
|
||||
* msecs have passed
|
||||
*/
|
||||
if (ati_remote->repeat_count > 0 &&
|
||||
(ati_remote->repeat_count < 5 ||
|
||||
time_before(now, ati_remote->first_jiffies +
|
||||
msecs_to_jiffies(repeat_delay))))
|
||||
return;
|
||||
|
||||
|
||||
input_event(dev, ati_remote_tbl[index].type,
|
||||
ati_remote_tbl[index].code, 1);
|
||||
input_sync(dev);
|
||||
input_event(dev, ati_remote_tbl[index].type,
|
||||
ati_remote_tbl[index].code, 0);
|
||||
input_sync(dev);
|
||||
|
||||
} else {
|
||||
|
||||
/*
|
||||
* Other event kinds are from the directional control pad, and have an
|
||||
* acceleration factor applied to them. Without this acceleration, the
|
||||
* control pad is mostly unusable.
|
||||
*/
|
||||
acc = ati_remote_compute_accel(ati_remote);
|
||||
|
||||
switch (ati_remote_tbl[index].kind) {
|
||||
case KIND_ACCEL:
|
||||
input_event(dev, ati_remote_tbl[index].type,
|
||||
ati_remote_tbl[index].code,
|
||||
ati_remote_tbl[index].value * acc);
|
||||
break;
|
||||
case KIND_LU:
|
||||
input_report_rel(dev, REL_X, -acc);
|
||||
input_report_rel(dev, REL_Y, -acc);
|
||||
break;
|
||||
case KIND_RU:
|
||||
input_report_rel(dev, REL_X, acc);
|
||||
input_report_rel(dev, REL_Y, -acc);
|
||||
break;
|
||||
case KIND_LD:
|
||||
input_report_rel(dev, REL_X, -acc);
|
||||
input_report_rel(dev, REL_Y, acc);
|
||||
break;
|
||||
case KIND_RD:
|
||||
input_report_rel(dev, REL_X, acc);
|
||||
input_report_rel(dev, REL_Y, acc);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n",
|
||||
ati_remote_tbl[index].kind);
|
||||
}
|
||||
input_sync(dev);
|
||||
|
||||
ati_remote->old_jiffies = jiffies;
|
||||
ati_remote->old_data[0] = data[1];
|
||||
ati_remote->old_data[1] = data[2];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_irq_in
|
||||
*/
|
||||
static void ati_remote_irq_in(struct urb *urb)
|
||||
{
|
||||
struct ati_remote *ati_remote = urb->context;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0: /* success */
|
||||
ati_remote_input_report(urb);
|
||||
break;
|
||||
case -ECONNRESET: /* unlink */
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
dev_dbg(&ati_remote->interface->dev, "%s: urb error status, unlink? \n",
|
||||
__FUNCTION__);
|
||||
return;
|
||||
default: /* error */
|
||||
dev_dbg(&ati_remote->interface->dev, "%s: Nonzero urb status %d\n",
|
||||
__FUNCTION__, urb->status);
|
||||
}
|
||||
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
dev_err(&ati_remote->interface->dev, "%s: usb_submit_urb()=%d\n",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_alloc_buffers
|
||||
*/
|
||||
static int ati_remote_alloc_buffers(struct usb_device *udev,
|
||||
struct ati_remote *ati_remote)
|
||||
{
|
||||
ati_remote->inbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, GFP_ATOMIC,
|
||||
&ati_remote->inbuf_dma);
|
||||
if (!ati_remote->inbuf)
|
||||
return -1;
|
||||
|
||||
ati_remote->outbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, GFP_ATOMIC,
|
||||
&ati_remote->outbuf_dma);
|
||||
if (!ati_remote->outbuf)
|
||||
return -1;
|
||||
|
||||
ati_remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!ati_remote->irq_urb)
|
||||
return -1;
|
||||
|
||||
ati_remote->out_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!ati_remote->out_urb)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_free_buffers
|
||||
*/
|
||||
static void ati_remote_free_buffers(struct ati_remote *ati_remote)
|
||||
{
|
||||
usb_free_urb(ati_remote->irq_urb);
|
||||
usb_free_urb(ati_remote->out_urb);
|
||||
|
||||
usb_buffer_free(ati_remote->udev, DATA_BUFSIZE,
|
||||
ati_remote->inbuf, ati_remote->inbuf_dma);
|
||||
|
||||
usb_buffer_free(ati_remote->udev, DATA_BUFSIZE,
|
||||
ati_remote->outbuf, ati_remote->outbuf_dma);
|
||||
}
|
||||
|
||||
static void ati_remote_input_init(struct ati_remote *ati_remote)
|
||||
{
|
||||
struct input_dev *idev = ati_remote->idev;
|
||||
int i;
|
||||
|
||||
idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
|
||||
idev->keybit[LONG(BTN_MOUSE)] = ( BIT(BTN_LEFT) | BIT(BTN_RIGHT) |
|
||||
BIT(BTN_SIDE) | BIT(BTN_EXTRA) );
|
||||
idev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
|
||||
for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++)
|
||||
if (ati_remote_tbl[i].type == EV_KEY)
|
||||
set_bit(ati_remote_tbl[i].code, idev->keybit);
|
||||
|
||||
input_set_drvdata(idev, ati_remote);
|
||||
|
||||
idev->open = ati_remote_open;
|
||||
idev->close = ati_remote_close;
|
||||
|
||||
idev->name = ati_remote->name;
|
||||
idev->phys = ati_remote->phys;
|
||||
|
||||
usb_to_input_id(ati_remote->udev, &idev->id);
|
||||
idev->dev.parent = &ati_remote->udev->dev;
|
||||
}
|
||||
|
||||
static int ati_remote_initialize(struct ati_remote *ati_remote)
|
||||
{
|
||||
struct usb_device *udev = ati_remote->udev;
|
||||
int pipe, maxp;
|
||||
|
||||
init_waitqueue_head(&ati_remote->wait);
|
||||
|
||||
/* Set up irq_urb */
|
||||
pipe = usb_rcvintpipe(udev, ati_remote->endpoint_in->bEndpointAddress);
|
||||
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
|
||||
maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
|
||||
|
||||
usb_fill_int_urb(ati_remote->irq_urb, udev, pipe, ati_remote->inbuf,
|
||||
maxp, ati_remote_irq_in, ati_remote,
|
||||
ati_remote->endpoint_in->bInterval);
|
||||
ati_remote->irq_urb->transfer_dma = ati_remote->inbuf_dma;
|
||||
ati_remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
/* Set up out_urb */
|
||||
pipe = usb_sndintpipe(udev, ati_remote->endpoint_out->bEndpointAddress);
|
||||
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
|
||||
maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
|
||||
|
||||
usb_fill_int_urb(ati_remote->out_urb, udev, pipe, ati_remote->outbuf,
|
||||
maxp, ati_remote_irq_out, ati_remote,
|
||||
ati_remote->endpoint_out->bInterval);
|
||||
ati_remote->out_urb->transfer_dma = ati_remote->outbuf_dma;
|
||||
ati_remote->out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
/* send initialization strings */
|
||||
if ((ati_remote_sendpacket(ati_remote, 0x8004, init1)) ||
|
||||
(ati_remote_sendpacket(ati_remote, 0x8007, init2))) {
|
||||
dev_err(&ati_remote->interface->dev,
|
||||
"Initializing ati_remote hardware failed.\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_probe
|
||||
*/
|
||||
static int ati_remote_probe(struct usb_interface *interface, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(interface);
|
||||
struct usb_host_interface *iface_host = interface->cur_altsetting;
|
||||
struct usb_endpoint_descriptor *endpoint_in, *endpoint_out;
|
||||
struct ati_remote *ati_remote;
|
||||
struct input_dev *input_dev;
|
||||
int err = -ENOMEM;
|
||||
|
||||
if (iface_host->desc.bNumEndpoints != 2) {
|
||||
err("%s: Unexpected desc.bNumEndpoints\n", __FUNCTION__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
endpoint_in = &iface_host->endpoint[0].desc;
|
||||
endpoint_out = &iface_host->endpoint[1].desc;
|
||||
|
||||
if (!usb_endpoint_is_int_in(endpoint_in)) {
|
||||
err("%s: Unexpected endpoint_in\n", __FUNCTION__);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (le16_to_cpu(endpoint_in->wMaxPacketSize) == 0) {
|
||||
err("%s: endpoint_in message size==0? \n", __FUNCTION__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ati_remote = kzalloc(sizeof (struct ati_remote), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!ati_remote || !input_dev)
|
||||
goto fail1;
|
||||
|
||||
/* Allocate URB buffers, URBs */
|
||||
if (ati_remote_alloc_buffers(udev, ati_remote))
|
||||
goto fail2;
|
||||
|
||||
ati_remote->endpoint_in = endpoint_in;
|
||||
ati_remote->endpoint_out = endpoint_out;
|
||||
ati_remote->udev = udev;
|
||||
ati_remote->idev = input_dev;
|
||||
ati_remote->interface = interface;
|
||||
|
||||
usb_make_path(udev, ati_remote->phys, sizeof(ati_remote->phys));
|
||||
strlcpy(ati_remote->phys, "/input0", sizeof(ati_remote->phys));
|
||||
|
||||
if (udev->manufacturer)
|
||||
strlcpy(ati_remote->name, udev->manufacturer, sizeof(ati_remote->name));
|
||||
|
||||
if (udev->product)
|
||||
snprintf(ati_remote->name, sizeof(ati_remote->name),
|
||||
"%s %s", ati_remote->name, udev->product);
|
||||
|
||||
if (!strlen(ati_remote->name))
|
||||
snprintf(ati_remote->name, sizeof(ati_remote->name),
|
||||
DRIVER_DESC "(%04x,%04x)",
|
||||
le16_to_cpu(ati_remote->udev->descriptor.idVendor),
|
||||
le16_to_cpu(ati_remote->udev->descriptor.idProduct));
|
||||
|
||||
ati_remote_input_init(ati_remote);
|
||||
|
||||
/* Device Hardware Initialization - fills in ati_remote->idev from udev. */
|
||||
err = ati_remote_initialize(ati_remote);
|
||||
if (err)
|
||||
goto fail3;
|
||||
|
||||
/* Set up and register input device */
|
||||
err = input_register_device(ati_remote->idev);
|
||||
if (err)
|
||||
goto fail3;
|
||||
|
||||
usb_set_intfdata(interface, ati_remote);
|
||||
return 0;
|
||||
|
||||
fail3: usb_kill_urb(ati_remote->irq_urb);
|
||||
usb_kill_urb(ati_remote->out_urb);
|
||||
fail2: ati_remote_free_buffers(ati_remote);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(ati_remote);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_disconnect
|
||||
*/
|
||||
static void ati_remote_disconnect(struct usb_interface *interface)
|
||||
{
|
||||
struct ati_remote *ati_remote;
|
||||
|
||||
ati_remote = usb_get_intfdata(interface);
|
||||
usb_set_intfdata(interface, NULL);
|
||||
if (!ati_remote) {
|
||||
warn("%s - null device?\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
usb_kill_urb(ati_remote->irq_urb);
|
||||
usb_kill_urb(ati_remote->out_urb);
|
||||
input_unregister_device(ati_remote->idev);
|
||||
ati_remote_free_buffers(ati_remote);
|
||||
kfree(ati_remote);
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_init
|
||||
*/
|
||||
static int __init ati_remote_init(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = usb_register(&ati_remote_driver);
|
||||
if (result)
|
||||
err("usb_register error #%d\n", result);
|
||||
else
|
||||
info("Registered USB driver " DRIVER_DESC " v. " DRIVER_VERSION);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_exit
|
||||
*/
|
||||
static void __exit ati_remote_exit(void)
|
||||
{
|
||||
usb_deregister(&ati_remote_driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* module specification
|
||||
*/
|
||||
|
||||
module_init(ati_remote_init);
|
||||
module_exit(ati_remote_exit);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -1,543 +0,0 @@
|
|||
/*
|
||||
* ati_remote2 - ATI/Philips USB RF remote driver
|
||||
*
|
||||
* Copyright (C) 2005 Ville Syrjala <syrjala@sci.fi>
|
||||
* Copyright (C) 2007 Peter Stokes <linux@dadeos.freeserve.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
#define DRIVER_DESC "ATI/Philips USB RF remote driver"
|
||||
#define DRIVER_VERSION "0.2"
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
MODULE_AUTHOR("Ville Syrjala <[email protected]>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* ATI Remote Wonder II Channel Configuration
|
||||
*
|
||||
* The remote control can by assigned one of sixteen "channels" in order to facilitate
|
||||
* the use of multiple remote controls within range of each other.
|
||||
* A remote's "channel" may be altered by pressing and holding the "PC" button for
|
||||
* approximately 3 seconds, after which the button will slowly flash the count of the
|
||||
* currently configured "channel", using the numeric keypad enter a number between 1 and
|
||||
* 16 and then the "PC" button again, the button will slowly flash the count of the
|
||||
* newly configured "channel".
|
||||
*/
|
||||
|
||||
static unsigned int channel_mask = 0xFFFF;
|
||||
module_param(channel_mask, uint, 0644);
|
||||
MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...<1:Channel2><0:Channel1>");
|
||||
|
||||
static unsigned int mode_mask = 0x1F;
|
||||
module_param(mode_mask, uint, 0644);
|
||||
MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>");
|
||||
|
||||
static struct usb_device_id ati_remote2_id_table[] = {
|
||||
{ USB_DEVICE(0x0471, 0x0602) }, /* ATI Remote Wonder II */
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, ati_remote2_id_table);
|
||||
|
||||
static struct {
|
||||
int hw_code;
|
||||
int key_code;
|
||||
} ati_remote2_key_table[] = {
|
||||
{ 0x00, KEY_0 },
|
||||
{ 0x01, KEY_1 },
|
||||
{ 0x02, KEY_2 },
|
||||
{ 0x03, KEY_3 },
|
||||
{ 0x04, KEY_4 },
|
||||
{ 0x05, KEY_5 },
|
||||
{ 0x06, KEY_6 },
|
||||
{ 0x07, KEY_7 },
|
||||
{ 0x08, KEY_8 },
|
||||
{ 0x09, KEY_9 },
|
||||
{ 0x0c, KEY_POWER },
|
||||
{ 0x0d, KEY_MUTE },
|
||||
{ 0x10, KEY_VOLUMEUP },
|
||||
{ 0x11, KEY_VOLUMEDOWN },
|
||||
{ 0x20, KEY_CHANNELUP },
|
||||
{ 0x21, KEY_CHANNELDOWN },
|
||||
{ 0x28, KEY_FORWARD },
|
||||
{ 0x29, KEY_REWIND },
|
||||
{ 0x2c, KEY_PLAY },
|
||||
{ 0x30, KEY_PAUSE },
|
||||
{ 0x31, KEY_STOP },
|
||||
{ 0x37, KEY_RECORD },
|
||||
{ 0x38, KEY_DVD },
|
||||
{ 0x39, KEY_TV },
|
||||
{ 0x54, KEY_MENU },
|
||||
{ 0x58, KEY_UP },
|
||||
{ 0x59, KEY_DOWN },
|
||||
{ 0x5a, KEY_LEFT },
|
||||
{ 0x5b, KEY_RIGHT },
|
||||
{ 0x5c, KEY_OK },
|
||||
{ 0x78, KEY_A },
|
||||
{ 0x79, KEY_B },
|
||||
{ 0x7a, KEY_C },
|
||||
{ 0x7b, KEY_D },
|
||||
{ 0x7c, KEY_E },
|
||||
{ 0x7d, KEY_F },
|
||||
{ 0x82, KEY_ENTER },
|
||||
{ 0x8e, KEY_VENDOR },
|
||||
{ 0x96, KEY_COFFEE },
|
||||
{ 0xa9, BTN_LEFT },
|
||||
{ 0xaa, BTN_RIGHT },
|
||||
{ 0xbe, KEY_QUESTION },
|
||||
{ 0xd5, KEY_FRONT },
|
||||
{ 0xd0, KEY_EDIT },
|
||||
{ 0xf9, KEY_INFO },
|
||||
{ (0x00 << 8) | 0x3f, KEY_PROG1 },
|
||||
{ (0x01 << 8) | 0x3f, KEY_PROG2 },
|
||||
{ (0x02 << 8) | 0x3f, KEY_PROG3 },
|
||||
{ (0x03 << 8) | 0x3f, KEY_PROG4 },
|
||||
{ (0x04 << 8) | 0x3f, KEY_PC },
|
||||
{ 0, KEY_RESERVED }
|
||||
};
|
||||
|
||||
struct ati_remote2 {
|
||||
struct input_dev *idev;
|
||||
struct usb_device *udev;
|
||||
|
||||
struct usb_interface *intf[2];
|
||||
struct usb_endpoint_descriptor *ep[2];
|
||||
struct urb *urb[2];
|
||||
void *buf[2];
|
||||
dma_addr_t buf_dma[2];
|
||||
|
||||
unsigned long jiffies;
|
||||
int mode;
|
||||
|
||||
char name[64];
|
||||
char phys[64];
|
||||
};
|
||||
|
||||
static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id);
|
||||
static void ati_remote2_disconnect(struct usb_interface *interface);
|
||||
|
||||
static struct usb_driver ati_remote2_driver = {
|
||||
.name = "ati_remote2",
|
||||
.probe = ati_remote2_probe,
|
||||
.disconnect = ati_remote2_disconnect,
|
||||
.id_table = ati_remote2_id_table,
|
||||
};
|
||||
|
||||
static int ati_remote2_open(struct input_dev *idev)
|
||||
{
|
||||
struct ati_remote2 *ar2 = input_get_drvdata(idev);
|
||||
int r;
|
||||
|
||||
r = usb_submit_urb(ar2->urb[0], GFP_KERNEL);
|
||||
if (r) {
|
||||
dev_err(&ar2->intf[0]->dev,
|
||||
"%s: usb_submit_urb() = %d\n", __FUNCTION__, r);
|
||||
return r;
|
||||
}
|
||||
r = usb_submit_urb(ar2->urb[1], GFP_KERNEL);
|
||||
if (r) {
|
||||
usb_kill_urb(ar2->urb[0]);
|
||||
dev_err(&ar2->intf[1]->dev,
|
||||
"%s: usb_submit_urb() = %d\n", __FUNCTION__, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ati_remote2_close(struct input_dev *idev)
|
||||
{
|
||||
struct ati_remote2 *ar2 = input_get_drvdata(idev);
|
||||
|
||||
usb_kill_urb(ar2->urb[0]);
|
||||
usb_kill_urb(ar2->urb[1]);
|
||||
}
|
||||
|
||||
static void ati_remote2_input_mouse(struct ati_remote2 *ar2)
|
||||
{
|
||||
struct input_dev *idev = ar2->idev;
|
||||
u8 *data = ar2->buf[0];
|
||||
int channel, mode;
|
||||
|
||||
channel = data[0] >> 4;
|
||||
|
||||
if (!((1 << channel) & channel_mask))
|
||||
return;
|
||||
|
||||
mode = data[0] & 0x0F;
|
||||
|
||||
if (mode > 4) {
|
||||
dev_err(&ar2->intf[0]->dev,
|
||||
"Unknown mode byte (%02x %02x %02x %02x)\n",
|
||||
data[3], data[2], data[1], data[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!((1 << mode) & mode_mask))
|
||||
return;
|
||||
|
||||
input_event(idev, EV_REL, REL_X, (s8) data[1]);
|
||||
input_event(idev, EV_REL, REL_Y, (s8) data[2]);
|
||||
input_sync(idev);
|
||||
}
|
||||
|
||||
static int ati_remote2_lookup(unsigned int hw_code)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++)
|
||||
if (ati_remote2_key_table[i].hw_code == hw_code)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ati_remote2_input_key(struct ati_remote2 *ar2)
|
||||
{
|
||||
struct input_dev *idev = ar2->idev;
|
||||
u8 *data = ar2->buf[1];
|
||||
int channel, mode, hw_code, index;
|
||||
|
||||
channel = data[0] >> 4;
|
||||
|
||||
if (!((1 << channel) & channel_mask))
|
||||
return;
|
||||
|
||||
mode = data[0] & 0x0F;
|
||||
|
||||
if (mode > 4) {
|
||||
dev_err(&ar2->intf[1]->dev,
|
||||
"Unknown mode byte (%02x %02x %02x %02x)\n",
|
||||
data[3], data[2], data[1], data[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
hw_code = data[2];
|
||||
/*
|
||||
* Mode keys (AUX1-AUX4, PC) all generate the same code byte.
|
||||
* Use the mode byte to figure out which one was pressed.
|
||||
*/
|
||||
if (hw_code == 0x3f) {
|
||||
/*
|
||||
* For some incomprehensible reason the mouse pad generates
|
||||
* events which look identical to the events from the last
|
||||
* pressed mode key. Naturally we don't want to generate key
|
||||
* events for the mouse pad so we filter out any subsequent
|
||||
* events from the same mode key.
|
||||
*/
|
||||
if (ar2->mode == mode)
|
||||
return;
|
||||
|
||||
if (data[1] == 0)
|
||||
ar2->mode = mode;
|
||||
|
||||
hw_code |= mode << 8;
|
||||
}
|
||||
|
||||
if (!((1 << mode) & mode_mask))
|
||||
return;
|
||||
|
||||
index = ati_remote2_lookup(hw_code);
|
||||
if (index < 0) {
|
||||
dev_err(&ar2->intf[1]->dev,
|
||||
"Unknown code byte (%02x %02x %02x %02x)\n",
|
||||
data[3], data[2], data[1], data[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (data[1]) {
|
||||
case 0: /* release */
|
||||
break;
|
||||
case 1: /* press */
|
||||
ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_DELAY]);
|
||||
break;
|
||||
case 2: /* repeat */
|
||||
|
||||
/* No repeat for mouse buttons. */
|
||||
if (ati_remote2_key_table[index].key_code == BTN_LEFT ||
|
||||
ati_remote2_key_table[index].key_code == BTN_RIGHT)
|
||||
return;
|
||||
|
||||
if (!time_after_eq(jiffies, ar2->jiffies))
|
||||
return;
|
||||
|
||||
ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_PERIOD]);
|
||||
break;
|
||||
default:
|
||||
dev_err(&ar2->intf[1]->dev,
|
||||
"Unknown state byte (%02x %02x %02x %02x)\n",
|
||||
data[3], data[2], data[1], data[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
input_event(idev, EV_KEY, ati_remote2_key_table[index].key_code, data[1]);
|
||||
input_sync(idev);
|
||||
}
|
||||
|
||||
static void ati_remote2_complete_mouse(struct urb *urb)
|
||||
{
|
||||
struct ati_remote2 *ar2 = urb->context;
|
||||
int r;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
ati_remote2_input_mouse(ar2);
|
||||
break;
|
||||
case -ENOENT:
|
||||
case -EILSEQ:
|
||||
case -ECONNRESET:
|
||||
case -ESHUTDOWN:
|
||||
dev_dbg(&ar2->intf[0]->dev,
|
||||
"%s(): urb status = %d\n", __FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dev_err(&ar2->intf[0]->dev,
|
||||
"%s(): urb status = %d\n", __FUNCTION__, urb->status);
|
||||
}
|
||||
|
||||
r = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (r)
|
||||
dev_err(&ar2->intf[0]->dev,
|
||||
"%s(): usb_submit_urb() = %d\n", __FUNCTION__, r);
|
||||
}
|
||||
|
||||
static void ati_remote2_complete_key(struct urb *urb)
|
||||
{
|
||||
struct ati_remote2 *ar2 = urb->context;
|
||||
int r;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
ati_remote2_input_key(ar2);
|
||||
break;
|
||||
case -ENOENT:
|
||||
case -EILSEQ:
|
||||
case -ECONNRESET:
|
||||
case -ESHUTDOWN:
|
||||
dev_dbg(&ar2->intf[1]->dev,
|
||||
"%s(): urb status = %d\n", __FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dev_err(&ar2->intf[1]->dev,
|
||||
"%s(): urb status = %d\n", __FUNCTION__, urb->status);
|
||||
}
|
||||
|
||||
r = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (r)
|
||||
dev_err(&ar2->intf[1]->dev,
|
||||
"%s(): usb_submit_urb() = %d\n", __FUNCTION__, r);
|
||||
}
|
||||
|
||||
static int ati_remote2_input_init(struct ati_remote2 *ar2)
|
||||
{
|
||||
struct input_dev *idev;
|
||||
int i, retval;
|
||||
|
||||
idev = input_allocate_device();
|
||||
if (!idev)
|
||||
return -ENOMEM;
|
||||
|
||||
ar2->idev = idev;
|
||||
input_set_drvdata(idev, ar2);
|
||||
|
||||
idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_REL);
|
||||
idev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT);
|
||||
idev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
|
||||
for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++)
|
||||
set_bit(ati_remote2_key_table[i].key_code, idev->keybit);
|
||||
|
||||
idev->rep[REP_DELAY] = 250;
|
||||
idev->rep[REP_PERIOD] = 33;
|
||||
|
||||
idev->open = ati_remote2_open;
|
||||
idev->close = ati_remote2_close;
|
||||
|
||||
idev->name = ar2->name;
|
||||
idev->phys = ar2->phys;
|
||||
|
||||
usb_to_input_id(ar2->udev, &idev->id);
|
||||
idev->dev.parent = &ar2->udev->dev;
|
||||
|
||||
retval = input_register_device(idev);
|
||||
if (retval)
|
||||
input_free_device(idev);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int ati_remote2_urb_init(struct ati_remote2 *ar2)
|
||||
{
|
||||
struct usb_device *udev = ar2->udev;
|
||||
int i, pipe, maxp;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
ar2->buf[i] = usb_buffer_alloc(udev, 4, GFP_KERNEL, &ar2->buf_dma[i]);
|
||||
if (!ar2->buf[i])
|
||||
return -ENOMEM;
|
||||
|
||||
ar2->urb[i] = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!ar2->urb[i])
|
||||
return -ENOMEM;
|
||||
|
||||
pipe = usb_rcvintpipe(udev, ar2->ep[i]->bEndpointAddress);
|
||||
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
|
||||
maxp = maxp > 4 ? 4 : maxp;
|
||||
|
||||
usb_fill_int_urb(ar2->urb[i], udev, pipe, ar2->buf[i], maxp,
|
||||
i ? ati_remote2_complete_key : ati_remote2_complete_mouse,
|
||||
ar2, ar2->ep[i]->bInterval);
|
||||
ar2->urb[i]->transfer_dma = ar2->buf_dma[i];
|
||||
ar2->urb[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ati_remote2_urb_cleanup(struct ati_remote2 *ar2)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
usb_free_urb(ar2->urb[i]);
|
||||
usb_buffer_free(ar2->udev, 4, ar2->buf[i], ar2->buf_dma[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int ati_remote2_setup(struct ati_remote2 *ar2)
|
||||
{
|
||||
int r, i, channel;
|
||||
|
||||
/*
|
||||
* Configure receiver to only accept input from remote "channel"
|
||||
* channel == 0 -> Accept input from any remote channel
|
||||
* channel == 1 -> Only accept input from remote channel 1
|
||||
* channel == 2 -> Only accept input from remote channel 2
|
||||
* ...
|
||||
* channel == 16 -> Only accept input from remote channel 16
|
||||
*/
|
||||
|
||||
channel = 0;
|
||||
for (i = 0; i < 16; i++) {
|
||||
if ((1 << i) & channel_mask) {
|
||||
if (!(~(1 << i) & 0xFFFF & channel_mask))
|
||||
channel = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
r = usb_control_msg(ar2->udev, usb_sndctrlpipe(ar2->udev, 0),
|
||||
0x20,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
|
||||
channel, 0x0, NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
if (r) {
|
||||
dev_err(&ar2->udev->dev, "%s - failed to set channel due to error: %d\n",
|
||||
__FUNCTION__, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(interface);
|
||||
struct usb_host_interface *alt = interface->cur_altsetting;
|
||||
struct ati_remote2 *ar2;
|
||||
int r;
|
||||
|
||||
if (alt->desc.bInterfaceNumber)
|
||||
return -ENODEV;
|
||||
|
||||
ar2 = kzalloc(sizeof (struct ati_remote2), GFP_KERNEL);
|
||||
if (!ar2)
|
||||
return -ENOMEM;
|
||||
|
||||
ar2->udev = udev;
|
||||
|
||||
ar2->intf[0] = interface;
|
||||
ar2->ep[0] = &alt->endpoint[0].desc;
|
||||
|
||||
ar2->intf[1] = usb_ifnum_to_if(udev, 1);
|
||||
r = usb_driver_claim_interface(&ati_remote2_driver, ar2->intf[1], ar2);
|
||||
if (r)
|
||||
goto fail1;
|
||||
alt = ar2->intf[1]->cur_altsetting;
|
||||
ar2->ep[1] = &alt->endpoint[0].desc;
|
||||
|
||||
r = ati_remote2_urb_init(ar2);
|
||||
if (r)
|
||||
goto fail2;
|
||||
|
||||
r = ati_remote2_setup(ar2);
|
||||
if (r)
|
||||
goto fail2;
|
||||
|
||||
usb_make_path(udev, ar2->phys, sizeof(ar2->phys));
|
||||
strlcat(ar2->phys, "/input0", sizeof(ar2->phys));
|
||||
|
||||
strlcat(ar2->name, "ATI Remote Wonder II", sizeof(ar2->name));
|
||||
|
||||
r = ati_remote2_input_init(ar2);
|
||||
if (r)
|
||||
goto fail2;
|
||||
|
||||
usb_set_intfdata(interface, ar2);
|
||||
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
ati_remote2_urb_cleanup(ar2);
|
||||
|
||||
usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
|
||||
fail1:
|
||||
kfree(ar2);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void ati_remote2_disconnect(struct usb_interface *interface)
|
||||
{
|
||||
struct ati_remote2 *ar2;
|
||||
struct usb_host_interface *alt = interface->cur_altsetting;
|
||||
|
||||
if (alt->desc.bInterfaceNumber)
|
||||
return;
|
||||
|
||||
ar2 = usb_get_intfdata(interface);
|
||||
usb_set_intfdata(interface, NULL);
|
||||
|
||||
input_unregister_device(ar2->idev);
|
||||
|
||||
ati_remote2_urb_cleanup(ar2);
|
||||
|
||||
usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
|
||||
|
||||
kfree(ar2);
|
||||
}
|
||||
|
||||
static int __init ati_remote2_init(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = usb_register(&ati_remote2_driver);
|
||||
if (r)
|
||||
printk(KERN_ERR "ati_remote2: usb_register() = %d\n", r);
|
||||
else
|
||||
printk(KERN_INFO "ati_remote2: " DRIVER_DESC " " DRIVER_VERSION "\n");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void __exit ati_remote2_exit(void)
|
||||
{
|
||||
usb_deregister(&ati_remote2_driver);
|
||||
}
|
||||
|
||||
module_init(ati_remote2_init);
|
||||
module_exit(ati_remote2_exit);
|
||||
|
|
@ -1,592 +0,0 @@
|
|||
/*
|
||||
* keyspan_remote: USB driver for the Keyspan DMR
|
||||
*
|
||||
* Copyright (C) 2005 Zymeta Corporation - Michael Downey (downey@zymeta.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, version 2.
|
||||
*
|
||||
* This driver has been put together with the support of Innosys, Inc.
|
||||
* and Keyspan, Inc the manufacturers of the Keyspan USB DMR product.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
#define DRIVER_VERSION "v0.1"
|
||||
#define DRIVER_AUTHOR "Michael Downey <[email protected]>"
|
||||
#define DRIVER_DESC "Driver for the USB Keyspan remote control."
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
/* Parameters that can be passed to the driver. */
|
||||
static int debug;
|
||||
module_param(debug, int, 0444);
|
||||
MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
|
||||
|
||||
/* Vendor and product ids */
|
||||
#define USB_KEYSPAN_VENDOR_ID 0x06CD
|
||||
#define USB_KEYSPAN_PRODUCT_UIA11 0x0202
|
||||
|
||||
/* Defines for converting the data from the remote. */
|
||||
#define ZERO 0x18
|
||||
#define ZERO_MASK 0x1F /* 5 bits for a 0 */
|
||||
#define ONE 0x3C
|
||||
#define ONE_MASK 0x3F /* 6 bits for a 1 */
|
||||
#define SYNC 0x3F80
|
||||
#define SYNC_MASK 0x3FFF /* 14 bits for a SYNC sequence */
|
||||
#define STOP 0x00
|
||||
#define STOP_MASK 0x1F /* 5 bits for the STOP sequence */
|
||||
#define GAP 0xFF
|
||||
|
||||
#define RECV_SIZE 8 /* The UIA-11 type have a 8 byte limit. */
|
||||
|
||||
/* table of devices that work with this driver */
|
||||
static struct usb_device_id keyspan_table[] = {
|
||||
{ USB_DEVICE(USB_KEYSPAN_VENDOR_ID, USB_KEYSPAN_PRODUCT_UIA11) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
/* Structure to store all the real stuff that a remote sends to us. */
|
||||
struct keyspan_message {
|
||||
u16 system;
|
||||
u8 button;
|
||||
u8 toggle;
|
||||
};
|
||||
|
||||
/* Structure used for all the bit testing magic needed to be done. */
|
||||
struct bit_tester {
|
||||
u32 tester;
|
||||
int len;
|
||||
int pos;
|
||||
int bits_left;
|
||||
u8 buffer[32];
|
||||
};
|
||||
|
||||
/* Structure to hold all of our driver specific stuff */
|
||||
struct usb_keyspan {
|
||||
char name[128];
|
||||
char phys[64];
|
||||
struct usb_device* udev;
|
||||
struct input_dev *input;
|
||||
struct usb_interface* interface;
|
||||
struct usb_endpoint_descriptor* in_endpoint;
|
||||
struct urb* irq_urb;
|
||||
int open;
|
||||
dma_addr_t in_dma;
|
||||
unsigned char* in_buffer;
|
||||
|
||||
/* variables used to parse messages from remote. */
|
||||
struct bit_tester data;
|
||||
int stage;
|
||||
int toggle;
|
||||
};
|
||||
|
||||
/*
|
||||
* Table that maps the 31 possible keycodes to input keys.
|
||||
* Currently there are 15 and 17 button models so RESERVED codes
|
||||
* are blank areas in the mapping.
|
||||
*/
|
||||
static const int keyspan_key_table[] = {
|
||||
KEY_RESERVED, /* 0 is just a place holder. */
|
||||
KEY_RESERVED,
|
||||
KEY_STOP,
|
||||
KEY_PLAYCD,
|
||||
KEY_RESERVED,
|
||||
KEY_PREVIOUSSONG,
|
||||
KEY_REWIND,
|
||||
KEY_FORWARD,
|
||||
KEY_NEXTSONG,
|
||||
KEY_RESERVED,
|
||||
KEY_RESERVED,
|
||||
KEY_RESERVED,
|
||||
KEY_PAUSE,
|
||||
KEY_VOLUMEUP,
|
||||
KEY_RESERVED,
|
||||
KEY_RESERVED,
|
||||
KEY_RESERVED,
|
||||
KEY_VOLUMEDOWN,
|
||||
KEY_RESERVED,
|
||||
KEY_UP,
|
||||
KEY_RESERVED,
|
||||
KEY_MUTE,
|
||||
KEY_LEFT,
|
||||
KEY_ENTER,
|
||||
KEY_RIGHT,
|
||||
KEY_RESERVED,
|
||||
KEY_RESERVED,
|
||||
KEY_DOWN,
|
||||
KEY_RESERVED,
|
||||
KEY_KPASTERISK,
|
||||
KEY_RESERVED,
|
||||
KEY_MENU
|
||||
};
|
||||
|
||||
static struct usb_driver keyspan_driver;
|
||||
|
||||
/*
|
||||
* Debug routine that prints out what we've received from the remote.
|
||||
*/
|
||||
static void keyspan_print(struct usb_keyspan* dev) /*unsigned char* data)*/
|
||||
{
|
||||
char codes[4 * RECV_SIZE];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < RECV_SIZE; i++)
|
||||
snprintf(codes + i * 3, 4, "%02x ", dev->in_buffer[i]);
|
||||
|
||||
dev_info(&dev->udev->dev, "%s\n", codes);
|
||||
}
|
||||
|
||||
/*
|
||||
* Routine that manages the bit_tester structure. It makes sure that there are
|
||||
* at least bits_needed bits loaded into the tester.
|
||||
*/
|
||||
static int keyspan_load_tester(struct usb_keyspan* dev, int bits_needed)
|
||||
{
|
||||
if (dev->data.bits_left >= bits_needed)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Somehow we've missed the last message. The message will be repeated
|
||||
* though so it's not too big a deal
|
||||
*/
|
||||
if (dev->data.pos >= dev->data.len) {
|
||||
dev_dbg(&dev->udev->dev,
|
||||
"%s - Error ran out of data. pos: %d, len: %d\n",
|
||||
__FUNCTION__, dev->data.pos, dev->data.len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Load as much as we can into the tester. */
|
||||
while ((dev->data.bits_left + 7 < (sizeof(dev->data.tester) * 8)) &&
|
||||
(dev->data.pos < dev->data.len)) {
|
||||
dev->data.tester += (dev->data.buffer[dev->data.pos++] << dev->data.bits_left);
|
||||
dev->data.bits_left += 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Routine that handles all the logic needed to parse out the message from the remote.
|
||||
*/
|
||||
static void keyspan_check_data(struct usb_keyspan *remote)
|
||||
{
|
||||
int i;
|
||||
int found = 0;
|
||||
struct keyspan_message message;
|
||||
|
||||
switch(remote->stage) {
|
||||
case 0:
|
||||
/*
|
||||
* In stage 0 we want to find the start of a message. The remote sends a 0xFF as filler.
|
||||
* So the first byte that isn't a FF should be the start of a new message.
|
||||
*/
|
||||
for (i = 0; i < RECV_SIZE && remote->in_buffer[i] == GAP; ++i);
|
||||
|
||||
if (i < RECV_SIZE) {
|
||||
memcpy(remote->data.buffer, remote->in_buffer, RECV_SIZE);
|
||||
remote->data.len = RECV_SIZE;
|
||||
remote->data.pos = 0;
|
||||
remote->data.tester = 0;
|
||||
remote->data.bits_left = 0;
|
||||
remote->stage = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/*
|
||||
* Stage 1 we should have 16 bytes and should be able to detect a
|
||||
* SYNC. The SYNC is 14 bits, 7 0's and then 7 1's.
|
||||
*/
|
||||
memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE);
|
||||
remote->data.len += RECV_SIZE;
|
||||
|
||||
found = 0;
|
||||
while ((remote->data.bits_left >= 14 || remote->data.pos < remote->data.len) && !found) {
|
||||
for (i = 0; i < 8; ++i) {
|
||||
if (keyspan_load_tester(remote, 14) != 0) {
|
||||
remote->stage = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((remote->data.tester & SYNC_MASK) == SYNC) {
|
||||
remote->data.tester = remote->data.tester >> 14;
|
||||
remote->data.bits_left -= 14;
|
||||
found = 1;
|
||||
break;
|
||||
} else {
|
||||
remote->data.tester = remote->data.tester >> 1;
|
||||
--remote->data.bits_left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
remote->stage = 0;
|
||||
remote->data.len = 0;
|
||||
} else {
|
||||
remote->stage = 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/*
|
||||
* Stage 2 we should have 24 bytes which will be enough for a full
|
||||
* message. We need to parse out the system code, button code,
|
||||
* toggle code, and stop.
|
||||
*/
|
||||
memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE);
|
||||
remote->data.len += RECV_SIZE;
|
||||
|
||||
message.system = 0;
|
||||
for (i = 0; i < 9; i++) {
|
||||
keyspan_load_tester(remote, 6);
|
||||
|
||||
if ((remote->data.tester & ZERO_MASK) == ZERO) {
|
||||
message.system = message.system << 1;
|
||||
remote->data.tester = remote->data.tester >> 5;
|
||||
remote->data.bits_left -= 5;
|
||||
} else if ((remote->data.tester & ONE_MASK) == ONE) {
|
||||
message.system = (message.system << 1) + 1;
|
||||
remote->data.tester = remote->data.tester >> 6;
|
||||
remote->data.bits_left -= 6;
|
||||
} else {
|
||||
err("%s - Unknown sequence found in system data.\n", __FUNCTION__);
|
||||
remote->stage = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
message.button = 0;
|
||||
for (i = 0; i < 5; i++) {
|
||||
keyspan_load_tester(remote, 6);
|
||||
|
||||
if ((remote->data.tester & ZERO_MASK) == ZERO) {
|
||||
message.button = message.button << 1;
|
||||
remote->data.tester = remote->data.tester >> 5;
|
||||
remote->data.bits_left -= 5;
|
||||
} else if ((remote->data.tester & ONE_MASK) == ONE) {
|
||||
message.button = (message.button << 1) + 1;
|
||||
remote->data.tester = remote->data.tester >> 6;
|
||||
remote->data.bits_left -= 6;
|
||||
} else {
|
||||
err("%s - Unknown sequence found in button data.\n", __FUNCTION__);
|
||||
remote->stage = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
keyspan_load_tester(remote, 6);
|
||||
if ((remote->data.tester & ZERO_MASK) == ZERO) {
|
||||
message.toggle = 0;
|
||||
remote->data.tester = remote->data.tester >> 5;
|
||||
remote->data.bits_left -= 5;
|
||||
} else if ((remote->data.tester & ONE_MASK) == ONE) {
|
||||
message.toggle = 1;
|
||||
remote->data.tester = remote->data.tester >> 6;
|
||||
remote->data.bits_left -= 6;
|
||||
} else {
|
||||
err("%s - Error in message, invalid toggle.\n", __FUNCTION__);
|
||||
remote->stage = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
keyspan_load_tester(remote, 5);
|
||||
if ((remote->data.tester & STOP_MASK) == STOP) {
|
||||
remote->data.tester = remote->data.tester >> 5;
|
||||
remote->data.bits_left -= 5;
|
||||
} else {
|
||||
err("Bad message recieved, no stop bit found.\n");
|
||||
}
|
||||
|
||||
dev_dbg(&remote->udev->dev,
|
||||
"%s found valid message: system: %d, button: %d, toggle: %d\n",
|
||||
__FUNCTION__, message.system, message.button, message.toggle);
|
||||
|
||||
if (message.toggle != remote->toggle) {
|
||||
input_report_key(remote->input, keyspan_key_table[message.button], 1);
|
||||
input_report_key(remote->input, keyspan_key_table[message.button], 0);
|
||||
input_sync(remote->input);
|
||||
remote->toggle = message.toggle;
|
||||
}
|
||||
|
||||
remote->stage = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Routine for sending all the initialization messages to the remote.
|
||||
*/
|
||||
static int keyspan_setup(struct usb_device* dev)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
0x11, 0x40, 0x5601, 0x0, NULL, 0, 0);
|
||||
if (retval) {
|
||||
dev_dbg(&dev->dev, "%s - failed to set bit rate due to error: %d\n",
|
||||
__FUNCTION__, retval);
|
||||
return(retval);
|
||||
}
|
||||
|
||||
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
0x44, 0x40, 0x0, 0x0, NULL, 0, 0);
|
||||
if (retval) {
|
||||
dev_dbg(&dev->dev, "%s - failed to set resume sensitivity due to error: %d\n",
|
||||
__FUNCTION__, retval);
|
||||
return(retval);
|
||||
}
|
||||
|
||||
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
0x22, 0x40, 0x0, 0x0, NULL, 0, 0);
|
||||
if (retval) {
|
||||
dev_dbg(&dev->dev, "%s - failed to turn receive on due to error: %d\n",
|
||||
__FUNCTION__, retval);
|
||||
return(retval);
|
||||
}
|
||||
|
||||
dev_dbg(&dev->dev, "%s - Setup complete.\n", __FUNCTION__);
|
||||
return(retval);
|
||||
}
|
||||
|
||||
/*
|
||||
* Routine used to handle a new message that has come in.
|
||||
*/
|
||||
static void keyspan_irq_recv(struct urb *urb)
|
||||
{
|
||||
struct usb_keyspan *dev = urb->context;
|
||||
int retval;
|
||||
|
||||
/* Check our status in case we need to bail out early. */
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
/* Device went away so don't keep trying to read from it. */
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
return;
|
||||
|
||||
default:
|
||||
goto resubmit;
|
||||
break;
|
||||
}
|
||||
|
||||
if (debug)
|
||||
keyspan_print(dev);
|
||||
|
||||
keyspan_check_data(dev);
|
||||
|
||||
resubmit:
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err ("%s - usb_submit_urb failed with result: %d", __FUNCTION__, retval);
|
||||
}
|
||||
|
||||
static int keyspan_open(struct input_dev *dev)
|
||||
{
|
||||
struct usb_keyspan *remote = input_get_drvdata(dev);
|
||||
|
||||
remote->irq_urb->dev = remote->udev;
|
||||
if (usb_submit_urb(remote->irq_urb, GFP_KERNEL))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void keyspan_close(struct input_dev *dev)
|
||||
{
|
||||
struct usb_keyspan *remote = input_get_drvdata(dev);
|
||||
|
||||
usb_kill_urb(remote->irq_urb);
|
||||
}
|
||||
|
||||
static struct usb_endpoint_descriptor *keyspan_get_in_endpoint(struct usb_host_interface *iface)
|
||||
{
|
||||
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
|
||||
endpoint = &iface->endpoint[i].desc;
|
||||
|
||||
if (usb_endpoint_is_int_in(endpoint)) {
|
||||
/* we found our interrupt in endpoint */
|
||||
return endpoint;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Routine that sets up the driver to handle a specific USB device detected on the bus.
|
||||
*/
|
||||
static int keyspan_probe(struct usb_interface *interface, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(interface);
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct usb_keyspan *remote;
|
||||
struct input_dev *input_dev;
|
||||
int i, error;
|
||||
|
||||
endpoint = keyspan_get_in_endpoint(interface->cur_altsetting);
|
||||
if (!endpoint)
|
||||
return -ENODEV;
|
||||
|
||||
remote = kzalloc(sizeof(*remote), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!remote || !input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
remote->udev = udev;
|
||||
remote->input = input_dev;
|
||||
remote->interface = interface;
|
||||
remote->in_endpoint = endpoint;
|
||||
remote->toggle = -1; /* Set to -1 so we will always not match the toggle from the first remote message. */
|
||||
|
||||
remote->in_buffer = usb_buffer_alloc(udev, RECV_SIZE, GFP_ATOMIC, &remote->in_dma);
|
||||
if (!remote->in_buffer) {
|
||||
error = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!remote->irq_urb) {
|
||||
error = -ENOMEM;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
error = keyspan_setup(udev);
|
||||
if (error) {
|
||||
error = -ENODEV;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
if (udev->manufacturer)
|
||||
strlcpy(remote->name, udev->manufacturer, sizeof(remote->name));
|
||||
|
||||
if (udev->product) {
|
||||
if (udev->manufacturer)
|
||||
strlcat(remote->name, " ", sizeof(remote->name));
|
||||
strlcat(remote->name, udev->product, sizeof(remote->name));
|
||||
}
|
||||
|
||||
if (!strlen(remote->name))
|
||||
snprintf(remote->name, sizeof(remote->name),
|
||||
"USB Keyspan Remote %04x:%04x",
|
||||
le16_to_cpu(udev->descriptor.idVendor),
|
||||
le16_to_cpu(udev->descriptor.idProduct));
|
||||
|
||||
usb_make_path(udev, remote->phys, sizeof(remote->phys));
|
||||
strlcat(remote->phys, "/input0", sizeof(remote->phys));
|
||||
|
||||
input_dev->name = remote->name;
|
||||
input_dev->phys = remote->phys;
|
||||
usb_to_input_id(udev, &input_dev->id);
|
||||
input_dev->dev.parent = &interface->dev;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY); /* We will only report KEY events. */
|
||||
for (i = 0; i < ARRAY_SIZE(keyspan_key_table); i++)
|
||||
if (keyspan_key_table[i] != KEY_RESERVED)
|
||||
set_bit(keyspan_key_table[i], input_dev->keybit);
|
||||
|
||||
input_set_drvdata(input_dev, remote);
|
||||
|
||||
input_dev->open = keyspan_open;
|
||||
input_dev->close = keyspan_close;
|
||||
|
||||
/*
|
||||
* Initialize the URB to access the device. The urb gets sent to the device in keyspan_open()
|
||||
*/
|
||||
usb_fill_int_urb(remote->irq_urb,
|
||||
remote->udev, usb_rcvintpipe(remote->udev, remote->in_endpoint->bEndpointAddress),
|
||||
remote->in_buffer, RECV_SIZE, keyspan_irq_recv, remote,
|
||||
remote->in_endpoint->bInterval);
|
||||
remote->irq_urb->transfer_dma = remote->in_dma;
|
||||
remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
/* we can register the device now, as it is ready */
|
||||
error = input_register_device(remote->input);
|
||||
if (error)
|
||||
goto fail3;
|
||||
|
||||
/* save our data pointer in this interface device */
|
||||
usb_set_intfdata(interface, remote);
|
||||
|
||||
return 0;
|
||||
|
||||
fail3: usb_free_urb(remote->irq_urb);
|
||||
fail2: usb_buffer_free(udev, RECV_SIZE, remote->in_buffer, remote->in_dma);
|
||||
fail1: kfree(remote);
|
||||
input_free_device(input_dev);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Routine called when a device is disconnected from the USB.
|
||||
*/
|
||||
static void keyspan_disconnect(struct usb_interface *interface)
|
||||
{
|
||||
struct usb_keyspan *remote;
|
||||
|
||||
remote = usb_get_intfdata(interface);
|
||||
usb_set_intfdata(interface, NULL);
|
||||
|
||||
if (remote) { /* We have a valid driver structure so clean up everything we allocated. */
|
||||
input_unregister_device(remote->input);
|
||||
usb_kill_urb(remote->irq_urb);
|
||||
usb_free_urb(remote->irq_urb);
|
||||
usb_buffer_free(remote->udev, RECV_SIZE, remote->in_buffer, remote->in_dma);
|
||||
kfree(remote);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Standard driver set up sections
|
||||
*/
|
||||
static struct usb_driver keyspan_driver =
|
||||
{
|
||||
.name = "keyspan_remote",
|
||||
.probe = keyspan_probe,
|
||||
.disconnect = keyspan_disconnect,
|
||||
.id_table = keyspan_table
|
||||
};
|
||||
|
||||
static int __init usb_keyspan_init(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
/* register this driver with the USB subsystem */
|
||||
result = usb_register(&keyspan_driver);
|
||||
if (result)
|
||||
err("usb_register failed. Error number %d\n", result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void __exit usb_keyspan_exit(void)
|
||||
{
|
||||
/* deregister this driver with the USB subsystem */
|
||||
usb_deregister(&keyspan_driver);
|
||||
}
|
||||
|
||||
module_init(usb_keyspan_init);
|
||||
module_exit(usb_keyspan_exit);
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, keyspan_table);
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE(DRIVER_LICENSE);
|
||||
|
|
@ -1,189 +0,0 @@
|
|||
/*
|
||||
* drivers/usb/input/map_to_7segment.h
|
||||
*
|
||||
* Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef MAP_TO_7SEGMENT_H
|
||||
#define MAP_TO_7SEGMENT_H
|
||||
|
||||
/* This file provides translation primitives and tables for the conversion
|
||||
* of (ASCII) characters to a 7-segments notation.
|
||||
*
|
||||
* The 7 segment's wikipedia notation below is used as standard.
|
||||
* See: http://en.wikipedia.org/wiki/Seven_segment_display
|
||||
*
|
||||
* Notation: +-a-+
|
||||
* f b
|
||||
* +-g-+
|
||||
* e c
|
||||
* +-d-+
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* Register a map variable, and fill it with a character set:
|
||||
* static SEG7_DEFAULT_MAP(map_seg7);
|
||||
*
|
||||
*
|
||||
* Then use for conversion:
|
||||
* seg7 = map_to_seg7(&map_seg7, some_char);
|
||||
* ...
|
||||
*
|
||||
* In device drivers it is recommended, if required, to make the char map
|
||||
* accessible via the sysfs interface using the following scheme:
|
||||
*
|
||||
* static ssize_t show_map(struct device *dev, char *buf) {
|
||||
* memcpy(buf, &map_seg7, sizeof(map_seg7));
|
||||
* return sizeof(map_seg7);
|
||||
* }
|
||||
* static ssize_t store_map(struct device *dev, const char *buf, size_t cnt) {
|
||||
* if(cnt != sizeof(map_seg7))
|
||||
* return -EINVAL;
|
||||
* memcpy(&map_seg7, buf, cnt);
|
||||
* return cnt;
|
||||
* }
|
||||
* static DEVICE_ATTR(map_seg7, PERMS_RW, show_map, store_map);
|
||||
*
|
||||
* History:
|
||||
* 2005-05-31 RFC linux-kernel@vger.kernel.org
|
||||
*/
|
||||
#include <linux/errno.h>
|
||||
|
||||
|
||||
#define BIT_SEG7_A 0
|
||||
#define BIT_SEG7_B 1
|
||||
#define BIT_SEG7_C 2
|
||||
#define BIT_SEG7_D 3
|
||||
#define BIT_SEG7_E 4
|
||||
#define BIT_SEG7_F 5
|
||||
#define BIT_SEG7_G 6
|
||||
#define BIT_SEG7_RESERVED 7
|
||||
|
||||
struct seg7_conversion_map {
|
||||
unsigned char table[128];
|
||||
};
|
||||
|
||||
static inline int map_to_seg7(struct seg7_conversion_map *map, int c)
|
||||
{
|
||||
return c >= 0 && c < sizeof(map->table) ? map->table[c] : -EINVAL;
|
||||
}
|
||||
|
||||
#define SEG7_CONVERSION_MAP(_name, _map) \
|
||||
struct seg7_conversion_map _name = { .table = { _map } }
|
||||
|
||||
/*
|
||||
* It is recommended to use a facility that allows user space to redefine
|
||||
* custom character sets for LCD devices. Please use a sysfs interface
|
||||
* as described above.
|
||||
*/
|
||||
#define MAP_TO_SEG7_SYSFS_FILE "map_seg7"
|
||||
|
||||
/*******************************************************************************
|
||||
* ASCII conversion table
|
||||
******************************************************************************/
|
||||
|
||||
#define _SEG7(l,a,b,c,d,e,f,g) \
|
||||
( a<<BIT_SEG7_A | b<<BIT_SEG7_B | c<<BIT_SEG7_C | d<<BIT_SEG7_D | \
|
||||
e<<BIT_SEG7_E | f<<BIT_SEG7_F | g<<BIT_SEG7_G )
|
||||
|
||||
#define _MAP_0_32_ASCII_SEG7_NON_PRINTABLE \
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
|
||||
#define _MAP_33_47_ASCII_SEG7_SYMBOL \
|
||||
_SEG7('!',0,0,0,0,1,1,0), _SEG7('"',0,1,0,0,0,1,0), _SEG7('#',0,1,1,0,1,1,0),\
|
||||
_SEG7('$',1,0,1,1,0,1,1), _SEG7('%',0,0,1,0,0,1,0), _SEG7('&',1,0,1,1,1,1,1),\
|
||||
_SEG7('\'',0,0,0,0,0,1,0),_SEG7('(',1,0,0,1,1,1,0), _SEG7(')',1,1,1,1,0,0,0),\
|
||||
_SEG7('*',0,1,1,0,1,1,1), _SEG7('+',0,1,1,0,0,0,1), _SEG7(',',0,0,0,0,1,0,0),\
|
||||
_SEG7('-',0,0,0,0,0,0,1), _SEG7('.',0,0,0,0,1,0,0), _SEG7('/',0,1,0,0,1,0,1),
|
||||
|
||||
#define _MAP_48_57_ASCII_SEG7_NUMERIC \
|
||||
_SEG7('0',1,1,1,1,1,1,0), _SEG7('1',0,1,1,0,0,0,0), _SEG7('2',1,1,0,1,1,0,1),\
|
||||
_SEG7('3',1,1,1,1,0,0,1), _SEG7('4',0,1,1,0,0,1,1), _SEG7('5',1,0,1,1,0,1,1),\
|
||||
_SEG7('6',1,0,1,1,1,1,1), _SEG7('7',1,1,1,0,0,0,0), _SEG7('8',1,1,1,1,1,1,1),\
|
||||
_SEG7('9',1,1,1,1,0,1,1),
|
||||
|
||||
#define _MAP_58_64_ASCII_SEG7_SYMBOL \
|
||||
_SEG7(':',0,0,0,1,0,0,1), _SEG7(';',0,0,0,1,0,0,1), _SEG7('<',1,0,0,0,0,1,1),\
|
||||
_SEG7('=',0,0,0,1,0,0,1), _SEG7('>',1,1,0,0,0,0,1), _SEG7('?',1,1,1,0,0,1,0),\
|
||||
_SEG7('@',1,1,0,1,1,1,1),
|
||||
|
||||
#define _MAP_65_90_ASCII_SEG7_ALPHA_UPPR \
|
||||
_SEG7('A',1,1,1,0,1,1,1), _SEG7('B',1,1,1,1,1,1,1), _SEG7('C',1,0,0,1,1,1,0),\
|
||||
_SEG7('D',1,1,1,1,1,1,0), _SEG7('E',1,0,0,1,1,1,1), _SEG7('F',1,0,0,0,1,1,1),\
|
||||
_SEG7('G',1,1,1,1,0,1,1), _SEG7('H',0,1,1,0,1,1,1), _SEG7('I',0,1,1,0,0,0,0),\
|
||||
_SEG7('J',0,1,1,1,0,0,0), _SEG7('K',0,1,1,0,1,1,1), _SEG7('L',0,0,0,1,1,1,0),\
|
||||
_SEG7('M',1,1,1,0,1,1,0), _SEG7('N',1,1,1,0,1,1,0), _SEG7('O',1,1,1,1,1,1,0),\
|
||||
_SEG7('P',1,1,0,0,1,1,1), _SEG7('Q',1,1,1,1,1,1,0), _SEG7('R',1,1,1,0,1,1,1),\
|
||||
_SEG7('S',1,0,1,1,0,1,1), _SEG7('T',0,0,0,1,1,1,1), _SEG7('U',0,1,1,1,1,1,0),\
|
||||
_SEG7('V',0,1,1,1,1,1,0), _SEG7('W',0,1,1,1,1,1,1), _SEG7('X',0,1,1,0,1,1,1),\
|
||||
_SEG7('Y',0,1,1,0,0,1,1), _SEG7('Z',1,1,0,1,1,0,1),
|
||||
|
||||
#define _MAP_91_96_ASCII_SEG7_SYMBOL \
|
||||
_SEG7('[',1,0,0,1,1,1,0), _SEG7('\\',0,0,1,0,0,1,1),_SEG7(']',1,1,1,1,0,0,0),\
|
||||
_SEG7('^',1,1,0,0,0,1,0), _SEG7('_',0,0,0,1,0,0,0), _SEG7('`',0,1,0,0,0,0,0),
|
||||
|
||||
#define _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
|
||||
_SEG7('A',1,1,1,0,1,1,1), _SEG7('b',0,0,1,1,1,1,1), _SEG7('c',0,0,0,1,1,0,1),\
|
||||
_SEG7('d',0,1,1,1,1,0,1), _SEG7('E',1,0,0,1,1,1,1), _SEG7('F',1,0,0,0,1,1,1),\
|
||||
_SEG7('G',1,1,1,1,0,1,1), _SEG7('h',0,0,1,0,1,1,1), _SEG7('i',0,0,1,0,0,0,0),\
|
||||
_SEG7('j',0,0,1,1,0,0,0), _SEG7('k',0,0,1,0,1,1,1), _SEG7('L',0,0,0,1,1,1,0),\
|
||||
_SEG7('M',1,1,1,0,1,1,0), _SEG7('n',0,0,1,0,1,0,1), _SEG7('o',0,0,1,1,1,0,1),\
|
||||
_SEG7('P',1,1,0,0,1,1,1), _SEG7('q',1,1,1,0,0,1,1), _SEG7('r',0,0,0,0,1,0,1),\
|
||||
_SEG7('S',1,0,1,1,0,1,1), _SEG7('T',0,0,0,1,1,1,1), _SEG7('u',0,0,1,1,1,0,0),\
|
||||
_SEG7('v',0,0,1,1,1,0,0), _SEG7('W',0,1,1,1,1,1,1), _SEG7('X',0,1,1,0,1,1,1),\
|
||||
_SEG7('y',0,1,1,1,0,1,1), _SEG7('Z',1,1,0,1,1,0,1),
|
||||
|
||||
#define _MAP_123_126_ASCII_SEG7_SYMBOL \
|
||||
_SEG7('{',1,0,0,1,1,1,0), _SEG7('|',0,0,0,0,1,1,0), _SEG7('}',1,1,1,1,0,0,0),\
|
||||
_SEG7('~',1,0,0,0,0,0,0),
|
||||
|
||||
/* Maps */
|
||||
|
||||
/* This set tries to map as close as possible to the visible characteristics
|
||||
* of the ASCII symbol, lowercase and uppercase letters may differ in
|
||||
* presentation on the display.
|
||||
*/
|
||||
#define MAP_ASCII7SEG_ALPHANUM \
|
||||
_MAP_0_32_ASCII_SEG7_NON_PRINTABLE \
|
||||
_MAP_33_47_ASCII_SEG7_SYMBOL \
|
||||
_MAP_48_57_ASCII_SEG7_NUMERIC \
|
||||
_MAP_58_64_ASCII_SEG7_SYMBOL \
|
||||
_MAP_65_90_ASCII_SEG7_ALPHA_UPPR \
|
||||
_MAP_91_96_ASCII_SEG7_SYMBOL \
|
||||
_MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
|
||||
_MAP_123_126_ASCII_SEG7_SYMBOL
|
||||
|
||||
/* This set tries to map as close as possible to the symbolic characteristics
|
||||
* of the ASCII character for maximum discrimination.
|
||||
* For now this means all alpha chars are in lower case representations.
|
||||
* (This for example facilitates the use of hex numbers with uppercase input.)
|
||||
*/
|
||||
#define MAP_ASCII7SEG_ALPHANUM_LC \
|
||||
_MAP_0_32_ASCII_SEG7_NON_PRINTABLE \
|
||||
_MAP_33_47_ASCII_SEG7_SYMBOL \
|
||||
_MAP_48_57_ASCII_SEG7_NUMERIC \
|
||||
_MAP_58_64_ASCII_SEG7_SYMBOL \
|
||||
_MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
|
||||
_MAP_91_96_ASCII_SEG7_SYMBOL \
|
||||
_MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
|
||||
_MAP_123_126_ASCII_SEG7_SYMBOL
|
||||
|
||||
#define SEG7_DEFAULT_MAP(_name) \
|
||||
SEG7_CONVERSION_MAP(_name,MAP_ASCII7SEG_ALPHANUM)
|
||||
|
||||
#endif /* MAP_TO_7SEGMENT_H */
|
||||
|
||||
|
|
@ -1,463 +0,0 @@
|
|||
/*
|
||||
* A driver for the Griffin Technology, Inc. "PowerMate" USB controller dial.
|
||||
*
|
||||
* v1.1, (c)2002 William R Sowerbutts <will@sowerbutts.com>
|
||||
*
|
||||
* This device is a anodised aluminium knob which connects over USB. It can measure
|
||||
* clockwise and anticlockwise rotation. The dial also acts as a pushbutton with
|
||||
* a spring for automatic release. The base contains a pair of LEDs which illuminate
|
||||
* the translucent base. It rotates without limit and reports its relative rotation
|
||||
* back to the host when polled by the USB controller.
|
||||
*
|
||||
* Testing with the knob I have has shown that it measures approximately 94 "clicks"
|
||||
* for one full rotation. Testing with my High Speed Rotation Actuator (ok, it was
|
||||
* a variable speed cordless electric drill) has shown that the device can measure
|
||||
* speeds of up to 7 clicks either clockwise or anticlockwise between pollings from
|
||||
* the host. If it counts more than 7 clicks before it is polled, it will wrap back
|
||||
* to zero and start counting again. This was at quite high speed, however, almost
|
||||
* certainly faster than the human hand could turn it. Griffin say that it loses a
|
||||
* pulse or two on a direction change; the granularity is so fine that I never
|
||||
* noticed this in practice.
|
||||
*
|
||||
* The device's microcontroller can be programmed to set the LED to either a constant
|
||||
* intensity, or to a rhythmic pulsing. Several patterns and speeds are available.
|
||||
*
|
||||
* Griffin were very happy to provide documentation and free hardware for development.
|
||||
*
|
||||
* Some userspace tools are available on the web: http://sowerbutts.com/powermate/
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
#define POWERMATE_VENDOR 0x077d /* Griffin Technology, Inc. */
|
||||
#define POWERMATE_PRODUCT_NEW 0x0410 /* Griffin PowerMate */
|
||||
#define POWERMATE_PRODUCT_OLD 0x04AA /* Griffin soundKnob */
|
||||
|
||||
#define CONTOUR_VENDOR 0x05f3 /* Contour Design, Inc. */
|
||||
#define CONTOUR_JOG 0x0240 /* Jog and Shuttle */
|
||||
|
||||
/* these are the command codes we send to the device */
|
||||
#define SET_STATIC_BRIGHTNESS 0x01
|
||||
#define SET_PULSE_ASLEEP 0x02
|
||||
#define SET_PULSE_AWAKE 0x03
|
||||
#define SET_PULSE_MODE 0x04
|
||||
|
||||
/* these refer to bits in the powermate_device's requires_update field. */
|
||||
#define UPDATE_STATIC_BRIGHTNESS (1<<0)
|
||||
#define UPDATE_PULSE_ASLEEP (1<<1)
|
||||
#define UPDATE_PULSE_AWAKE (1<<2)
|
||||
#define UPDATE_PULSE_MODE (1<<3)
|
||||
|
||||
/* at least two versions of the hardware exist, with differing payload
|
||||
sizes. the first three bytes always contain the "interesting" data in
|
||||
the relevant format. */
|
||||
#define POWERMATE_PAYLOAD_SIZE_MAX 6
|
||||
#define POWERMATE_PAYLOAD_SIZE_MIN 3
|
||||
struct powermate_device {
|
||||
signed char *data;
|
||||
dma_addr_t data_dma;
|
||||
struct urb *irq, *config;
|
||||
struct usb_ctrlrequest *configcr;
|
||||
dma_addr_t configcr_dma;
|
||||
struct usb_device *udev;
|
||||
struct input_dev *input;
|
||||
spinlock_t lock;
|
||||
int static_brightness;
|
||||
int pulse_speed;
|
||||
int pulse_table;
|
||||
int pulse_asleep;
|
||||
int pulse_awake;
|
||||
int requires_update; // physical settings which are out of sync
|
||||
char phys[64];
|
||||
};
|
||||
|
||||
static char pm_name_powermate[] = "Griffin PowerMate";
|
||||
static char pm_name_soundknob[] = "Griffin SoundKnob";
|
||||
|
||||
static void powermate_config_complete(struct urb *urb);
|
||||
|
||||
/* Callback for data arriving from the PowerMate over the USB interrupt pipe */
|
||||
static void powermate_irq(struct urb *urb)
|
||||
{
|
||||
struct powermate_device *pm = urb->context;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* handle updates to device state */
|
||||
input_report_key(pm->input, BTN_0, pm->data[0] & 0x01);
|
||||
input_report_rel(pm->input, REL_DIAL, pm->data[1]);
|
||||
input_sync(pm->input);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err ("%s - usb_submit_urb failed with result %d",
|
||||
__FUNCTION__, retval);
|
||||
}
|
||||
|
||||
/* Decide if we need to issue a control message and do so. Must be called with pm->lock taken */
|
||||
static void powermate_sync_state(struct powermate_device *pm)
|
||||
{
|
||||
if (pm->requires_update == 0)
|
||||
return; /* no updates are required */
|
||||
if (pm->config->status == -EINPROGRESS)
|
||||
return; /* an update is already in progress; it'll issue this update when it completes */
|
||||
|
||||
if (pm->requires_update & UPDATE_PULSE_ASLEEP){
|
||||
pm->configcr->wValue = cpu_to_le16( SET_PULSE_ASLEEP );
|
||||
pm->configcr->wIndex = cpu_to_le16( pm->pulse_asleep ? 1 : 0 );
|
||||
pm->requires_update &= ~UPDATE_PULSE_ASLEEP;
|
||||
}else if (pm->requires_update & UPDATE_PULSE_AWAKE){
|
||||
pm->configcr->wValue = cpu_to_le16( SET_PULSE_AWAKE );
|
||||
pm->configcr->wIndex = cpu_to_le16( pm->pulse_awake ? 1 : 0 );
|
||||
pm->requires_update &= ~UPDATE_PULSE_AWAKE;
|
||||
}else if (pm->requires_update & UPDATE_PULSE_MODE){
|
||||
int op, arg;
|
||||
/* the powermate takes an operation and an argument for its pulse algorithm.
|
||||
the operation can be:
|
||||
0: divide the speed
|
||||
1: pulse at normal speed
|
||||
2: multiply the speed
|
||||
the argument only has an effect for operations 0 and 2, and ranges between
|
||||
1 (least effect) to 255 (maximum effect).
|
||||
|
||||
thus, several states are equivalent and are coalesced into one state.
|
||||
|
||||
we map this onto a range from 0 to 510, with:
|
||||
0 -- 254 -- use divide (0 = slowest)
|
||||
255 -- use normal speed
|
||||
256 -- 510 -- use multiple (510 = fastest).
|
||||
|
||||
Only values of 'arg' quite close to 255 are particularly useful/spectacular.
|
||||
*/
|
||||
if (pm->pulse_speed < 255) {
|
||||
op = 0; // divide
|
||||
arg = 255 - pm->pulse_speed;
|
||||
} else if (pm->pulse_speed > 255) {
|
||||
op = 2; // multiply
|
||||
arg = pm->pulse_speed - 255;
|
||||
} else {
|
||||
op = 1; // normal speed
|
||||
arg = 0; // can be any value
|
||||
}
|
||||
pm->configcr->wValue = cpu_to_le16( (pm->pulse_table << 8) | SET_PULSE_MODE );
|
||||
pm->configcr->wIndex = cpu_to_le16( (arg << 8) | op );
|
||||
pm->requires_update &= ~UPDATE_PULSE_MODE;
|
||||
} else if (pm->requires_update & UPDATE_STATIC_BRIGHTNESS) {
|
||||
pm->configcr->wValue = cpu_to_le16( SET_STATIC_BRIGHTNESS );
|
||||
pm->configcr->wIndex = cpu_to_le16( pm->static_brightness );
|
||||
pm->requires_update &= ~UPDATE_STATIC_BRIGHTNESS;
|
||||
} else {
|
||||
printk(KERN_ERR "powermate: unknown update required");
|
||||
pm->requires_update = 0; /* fudge the bug */
|
||||
return;
|
||||
}
|
||||
|
||||
/* printk("powermate: %04x %04x\n", pm->configcr->wValue, pm->configcr->wIndex); */
|
||||
|
||||
pm->configcr->bRequestType = 0x41; /* vendor request */
|
||||
pm->configcr->bRequest = 0x01;
|
||||
pm->configcr->wLength = 0;
|
||||
|
||||
usb_fill_control_urb(pm->config, pm->udev, usb_sndctrlpipe(pm->udev, 0),
|
||||
(void *) pm->configcr, NULL, 0,
|
||||
powermate_config_complete, pm);
|
||||
pm->config->setup_dma = pm->configcr_dma;
|
||||
pm->config->transfer_flags |= URB_NO_SETUP_DMA_MAP;
|
||||
|
||||
if (usb_submit_urb(pm->config, GFP_ATOMIC))
|
||||
printk(KERN_ERR "powermate: usb_submit_urb(config) failed");
|
||||
}
|
||||
|
||||
/* Called when our asynchronous control message completes. We may need to issue another immediately */
|
||||
static void powermate_config_complete(struct urb *urb)
|
||||
{
|
||||
struct powermate_device *pm = urb->context;
|
||||
unsigned long flags;
|
||||
|
||||
if (urb->status)
|
||||
printk(KERN_ERR "powermate: config urb returned %d\n", urb->status);
|
||||
|
||||
spin_lock_irqsave(&pm->lock, flags);
|
||||
powermate_sync_state(pm);
|
||||
spin_unlock_irqrestore(&pm->lock, flags);
|
||||
}
|
||||
|
||||
/* Set the LED up as described and begin the sync with the hardware if required */
|
||||
static void powermate_pulse_led(struct powermate_device *pm, int static_brightness, int pulse_speed,
|
||||
int pulse_table, int pulse_asleep, int pulse_awake)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (pulse_speed < 0)
|
||||
pulse_speed = 0;
|
||||
if (pulse_table < 0)
|
||||
pulse_table = 0;
|
||||
if (pulse_speed > 510)
|
||||
pulse_speed = 510;
|
||||
if (pulse_table > 2)
|
||||
pulse_table = 2;
|
||||
|
||||
pulse_asleep = !!pulse_asleep;
|
||||
pulse_awake = !!pulse_awake;
|
||||
|
||||
|
||||
spin_lock_irqsave(&pm->lock, flags);
|
||||
|
||||
/* mark state updates which are required */
|
||||
if (static_brightness != pm->static_brightness) {
|
||||
pm->static_brightness = static_brightness;
|
||||
pm->requires_update |= UPDATE_STATIC_BRIGHTNESS;
|
||||
}
|
||||
if (pulse_asleep != pm->pulse_asleep) {
|
||||
pm->pulse_asleep = pulse_asleep;
|
||||
pm->requires_update |= (UPDATE_PULSE_ASLEEP | UPDATE_STATIC_BRIGHTNESS);
|
||||
}
|
||||
if (pulse_awake != pm->pulse_awake) {
|
||||
pm->pulse_awake = pulse_awake;
|
||||
pm->requires_update |= (UPDATE_PULSE_AWAKE | UPDATE_STATIC_BRIGHTNESS);
|
||||
}
|
||||
if (pulse_speed != pm->pulse_speed || pulse_table != pm->pulse_table) {
|
||||
pm->pulse_speed = pulse_speed;
|
||||
pm->pulse_table = pulse_table;
|
||||
pm->requires_update |= UPDATE_PULSE_MODE;
|
||||
}
|
||||
|
||||
powermate_sync_state(pm);
|
||||
|
||||
spin_unlock_irqrestore(&pm->lock, flags);
|
||||
}
|
||||
|
||||
/* Callback from the Input layer when an event arrives from userspace to configure the LED */
|
||||
static int powermate_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int _value)
|
||||
{
|
||||
unsigned int command = (unsigned int)_value;
|
||||
struct powermate_device *pm = input_get_drvdata(dev);
|
||||
|
||||
if (type == EV_MSC && code == MSC_PULSELED){
|
||||
/*
|
||||
bits 0- 7: 8 bits: LED brightness
|
||||
bits 8-16: 9 bits: pulsing speed modifier (0 ... 510); 0-254 = slower, 255 = standard, 256-510 = faster.
|
||||
bits 17-18: 2 bits: pulse table (0, 1, 2 valid)
|
||||
bit 19: 1 bit : pulse whilst asleep?
|
||||
bit 20: 1 bit : pulse constantly?
|
||||
*/
|
||||
int static_brightness = command & 0xFF; // bits 0-7
|
||||
int pulse_speed = (command >> 8) & 0x1FF; // bits 8-16
|
||||
int pulse_table = (command >> 17) & 0x3; // bits 17-18
|
||||
int pulse_asleep = (command >> 19) & 0x1; // bit 19
|
||||
int pulse_awake = (command >> 20) & 0x1; // bit 20
|
||||
|
||||
powermate_pulse_led(pm, static_brightness, pulse_speed, pulse_table, pulse_asleep, pulse_awake);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_device *pm)
|
||||
{
|
||||
pm->data = usb_buffer_alloc(udev, POWERMATE_PAYLOAD_SIZE_MAX,
|
||||
GFP_ATOMIC, &pm->data_dma);
|
||||
if (!pm->data)
|
||||
return -1;
|
||||
|
||||
pm->configcr = usb_buffer_alloc(udev, sizeof(*(pm->configcr)),
|
||||
GFP_ATOMIC, &pm->configcr_dma);
|
||||
if (!pm->configcr)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void powermate_free_buffers(struct usb_device *udev, struct powermate_device *pm)
|
||||
{
|
||||
usb_buffer_free(udev, POWERMATE_PAYLOAD_SIZE_MAX,
|
||||
pm->data, pm->data_dma);
|
||||
usb_buffer_free(udev, sizeof(*(pm->configcr)),
|
||||
pm->configcr, pm->configcr_dma);
|
||||
}
|
||||
|
||||
/* Called whenever a USB device matching one in our supported devices table is connected */
|
||||
static int powermate_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev (intf);
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct powermate_device *pm;
|
||||
struct input_dev *input_dev;
|
||||
int pipe, maxp;
|
||||
int error = -ENOMEM;
|
||||
|
||||
interface = intf->cur_altsetting;
|
||||
endpoint = &interface->endpoint[0].desc;
|
||||
if (!usb_endpoint_is_int_in(endpoint))
|
||||
return -EIO;
|
||||
|
||||
usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||
0x0a, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
0, interface->desc.bInterfaceNumber, NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
pm = kzalloc(sizeof(struct powermate_device), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!pm || !input_dev)
|
||||
goto fail1;
|
||||
|
||||
if (powermate_alloc_buffers(udev, pm))
|
||||
goto fail2;
|
||||
|
||||
pm->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!pm->irq)
|
||||
goto fail2;
|
||||
|
||||
pm->config = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!pm->config)
|
||||
goto fail3;
|
||||
|
||||
pm->udev = udev;
|
||||
pm->input = input_dev;
|
||||
|
||||
usb_make_path(udev, pm->phys, sizeof(pm->phys));
|
||||
strlcpy(pm->phys, "/input0", sizeof(pm->phys));
|
||||
|
||||
spin_lock_init(&pm->lock);
|
||||
|
||||
switch (le16_to_cpu(udev->descriptor.idProduct)) {
|
||||
case POWERMATE_PRODUCT_NEW:
|
||||
input_dev->name = pm_name_powermate;
|
||||
break;
|
||||
case POWERMATE_PRODUCT_OLD:
|
||||
input_dev->name = pm_name_soundknob;
|
||||
break;
|
||||
default:
|
||||
input_dev->name = pm_name_soundknob;
|
||||
printk(KERN_WARNING "powermate: unknown product id %04x\n",
|
||||
le16_to_cpu(udev->descriptor.idProduct));
|
||||
}
|
||||
|
||||
input_dev->phys = pm->phys;
|
||||
usb_to_input_id(udev, &input_dev->id);
|
||||
input_dev->dev.parent = &intf->dev;
|
||||
|
||||
input_set_drvdata(input_dev, pm);
|
||||
|
||||
input_dev->event = powermate_input_event;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_MSC);
|
||||
input_dev->keybit[LONG(BTN_0)] = BIT(BTN_0);
|
||||
input_dev->relbit[LONG(REL_DIAL)] = BIT(REL_DIAL);
|
||||
input_dev->mscbit[LONG(MSC_PULSELED)] = BIT(MSC_PULSELED);
|
||||
|
||||
/* get a handle to the interrupt data pipe */
|
||||
pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
|
||||
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
|
||||
|
||||
if (maxp < POWERMATE_PAYLOAD_SIZE_MIN || maxp > POWERMATE_PAYLOAD_SIZE_MAX) {
|
||||
printk(KERN_WARNING "powermate: Expected payload of %d--%d bytes, found %d bytes!\n",
|
||||
POWERMATE_PAYLOAD_SIZE_MIN, POWERMATE_PAYLOAD_SIZE_MAX, maxp);
|
||||
maxp = POWERMATE_PAYLOAD_SIZE_MAX;
|
||||
}
|
||||
|
||||
usb_fill_int_urb(pm->irq, udev, pipe, pm->data,
|
||||
maxp, powermate_irq,
|
||||
pm, endpoint->bInterval);
|
||||
pm->irq->transfer_dma = pm->data_dma;
|
||||
pm->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
/* register our interrupt URB with the USB system */
|
||||
if (usb_submit_urb(pm->irq, GFP_KERNEL)) {
|
||||
error = -EIO;
|
||||
goto fail4;
|
||||
}
|
||||
|
||||
error = input_register_device(pm->input);
|
||||
if (error)
|
||||
goto fail5;
|
||||
|
||||
|
||||
/* force an update of everything */
|
||||
pm->requires_update = UPDATE_PULSE_ASLEEP | UPDATE_PULSE_AWAKE | UPDATE_PULSE_MODE | UPDATE_STATIC_BRIGHTNESS;
|
||||
powermate_pulse_led(pm, 0x80, 255, 0, 1, 0); // set default pulse parameters
|
||||
|
||||
usb_set_intfdata(intf, pm);
|
||||
return 0;
|
||||
|
||||
fail5: usb_kill_urb(pm->irq);
|
||||
fail4: usb_free_urb(pm->config);
|
||||
fail3: usb_free_urb(pm->irq);
|
||||
fail2: powermate_free_buffers(udev, pm);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(pm);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Called when a USB device we've accepted ownership of is removed */
|
||||
static void powermate_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct powermate_device *pm = usb_get_intfdata (intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (pm) {
|
||||
pm->requires_update = 0;
|
||||
usb_kill_urb(pm->irq);
|
||||
input_unregister_device(pm->input);
|
||||
usb_free_urb(pm->irq);
|
||||
usb_free_urb(pm->config);
|
||||
powermate_free_buffers(interface_to_usbdev(intf), pm);
|
||||
|
||||
kfree(pm);
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_device_id powermate_devices [] = {
|
||||
{ USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_NEW) },
|
||||
{ USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_OLD) },
|
||||
{ USB_DEVICE(CONTOUR_VENDOR, CONTOUR_JOG) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE (usb, powermate_devices);
|
||||
|
||||
static struct usb_driver powermate_driver = {
|
||||
.name = "powermate",
|
||||
.probe = powermate_probe,
|
||||
.disconnect = powermate_disconnect,
|
||||
.id_table = powermate_devices,
|
||||
};
|
||||
|
||||
static int __init powermate_init(void)
|
||||
{
|
||||
return usb_register(&powermate_driver);
|
||||
}
|
||||
|
||||
static void __exit powermate_cleanup(void)
|
||||
{
|
||||
usb_deregister(&powermate_driver);
|
||||
}
|
||||
|
||||
module_init(powermate_init);
|
||||
module_exit(powermate_cleanup);
|
||||
|
||||
MODULE_AUTHOR( "William R Sowerbutts" );
|
||||
MODULE_DESCRIPTION( "Griffin Technology, Inc PowerMate driver" );
|
||||
MODULE_LICENSE("GPL");
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,220 +0,0 @@
|
|||
/*
|
||||
* drivers/usb/input/yealink.h
|
||||
*
|
||||
* Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef INPUT_YEALINK_H
|
||||
#define INPUT_YEALINK_H
|
||||
|
||||
/* Using the control channel on interface 3 various aspects of the phone
|
||||
* can be controlled like LCD, LED, dialtone and the ringtone.
|
||||
*/
|
||||
|
||||
struct yld_ctl_packet {
|
||||
u8 cmd; /* command code, see below */
|
||||
u8 size; /* 1-11, size of used data bytes. */
|
||||
u16 offset; /* internal packet offset */
|
||||
u8 data[11];
|
||||
s8 sum; /* negative sum of 15 preceding bytes */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define USB_PKT_LEN sizeof(struct yld_ctl_packet)
|
||||
|
||||
/* The following yld_ctl_packet's are available: */
|
||||
|
||||
/* Init registers
|
||||
*
|
||||
* cmd 0x8e
|
||||
* size 10
|
||||
* offset 0
|
||||
* data 0,0,0,0....
|
||||
*/
|
||||
#define CMD_INIT 0x8e
|
||||
|
||||
/* Request key scan
|
||||
*
|
||||
* cmd 0x80
|
||||
* size 1
|
||||
* offset 0
|
||||
* data[0] on return returns the key number, if it changes there's a new
|
||||
* key pressed.
|
||||
*/
|
||||
#define CMD_KEYPRESS 0x80
|
||||
|
||||
/* Request scancode
|
||||
*
|
||||
* cmd 0x81
|
||||
* size 1
|
||||
* offset key number [0-1f]
|
||||
* data[0] on return returns the scancode
|
||||
*/
|
||||
#define CMD_SCANCODE 0x81
|
||||
|
||||
/* Set LCD
|
||||
*
|
||||
* cmd 0x04
|
||||
* size 1-11
|
||||
* offset 0-23
|
||||
* data segment bits
|
||||
*/
|
||||
#define CMD_LCD 0x04
|
||||
|
||||
/* Set led
|
||||
*
|
||||
* cmd 0x05
|
||||
* size 1
|
||||
* offset 0
|
||||
* data[0] 0 OFF / 1 ON
|
||||
*/
|
||||
#define CMD_LED 0x05
|
||||
|
||||
/* Set ringtone volume
|
||||
*
|
||||
* cmd 0x11
|
||||
* size 1
|
||||
* offset 0
|
||||
* data[0] 0-0xff volume
|
||||
*/
|
||||
#define CMD_RING_VOLUME 0x11
|
||||
|
||||
/* Set ringtone notes
|
||||
*
|
||||
* cmd 0x02
|
||||
* size 1-11
|
||||
* offset 0->
|
||||
* data binary representation LE16(-freq), LE16(duration) ....
|
||||
*/
|
||||
#define CMD_RING_NOTE 0x02
|
||||
|
||||
/* Sound ringtone via the speaker on the back
|
||||
*
|
||||
* cmd 0x03
|
||||
* size 1
|
||||
* offset 0
|
||||
* data[0] 0 OFF / 0x24 ON
|
||||
*/
|
||||
#define CMD_RINGTONE 0x03
|
||||
|
||||
/* Sound dial tone via the ear speaker
|
||||
*
|
||||
* cmd 0x09
|
||||
* size 1
|
||||
* offset 0
|
||||
* data[0] 0 OFF / 1 ON
|
||||
*/
|
||||
#define CMD_DIALTONE 0x09
|
||||
|
||||
#endif /* INPUT_YEALINK_H */
|
||||
|
||||
|
||||
#if defined(_SEG) && defined(_PIC)
|
||||
/* This table maps the LCD segments onto individual bit positions in the
|
||||
* yld_status struct.
|
||||
*/
|
||||
|
||||
/* LCD, each segment must be driven seperately.
|
||||
*
|
||||
* Layout:
|
||||
*
|
||||
* |[] [][] [][] [][] in |[][]
|
||||
* |[] M [][] D [][] : [][] out |[][]
|
||||
* store
|
||||
*
|
||||
* NEW REP SU MO TU WE TH FR SA
|
||||
*
|
||||
* [] [] [] [] [] [] [] [] [] [] [] []
|
||||
* [] [] [] [] [] [] [] [] [] [] [] []
|
||||
*/
|
||||
|
||||
/* Line 1
|
||||
* Format : 18.e8.M8.88...188
|
||||
* Icon names : M D : IN OUT STORE
|
||||
*/
|
||||
#define LCD_LINE1_OFFSET 0
|
||||
#define LCD_LINE1_SIZE 17
|
||||
|
||||
/* Note: first g then f => ! ! */
|
||||
/* _SEG( type a b c d e g f ) */
|
||||
_SEG('1', 0,0 , 22,2 , 22,2 , 0,0 , 0,0 , 0,0 , 0,0 ),
|
||||
_SEG('8', 20,1 , 20,2 , 20,4 , 20,8 , 21,4 , 21,2 , 21,1 ),
|
||||
_PIC('.', 22,1 , "M" ),
|
||||
_SEG('e', 18,1 , 18,2 , 18,4 , 18,1 , 19,2 , 18,1 , 19,1 ),
|
||||
_SEG('8', 16,1 , 16,2 , 16,4 , 16,8 , 17,4 , 17,2 , 17,1 ),
|
||||
_PIC('.', 15,8 , "D" ),
|
||||
_SEG('M', 14,1 , 14,2 , 14,4 , 14,1 , 15,4 , 15,2 , 15,1 ),
|
||||
_SEG('8', 12,1 , 12,2 , 12,4 , 12,8 , 13,4 , 13,2 , 13,1 ),
|
||||
_PIC('.', 11,8 , ":" ),
|
||||
_SEG('8', 10,1 , 10,2 , 10,4 , 10,8 , 11,4 , 11,2 , 11,1 ),
|
||||
_SEG('8', 8,1 , 8,2 , 8,4 , 8,8 , 9,4 , 9,2 , 9,1 ),
|
||||
_PIC('.', 7,1 , "IN" ),
|
||||
_PIC('.', 7,2 , "OUT" ),
|
||||
_PIC('.', 7,4 , "STORE" ),
|
||||
_SEG('1', 0,0 , 5,1 , 5,1 , 0,0 , 0,0 , 0,0 , 0,0 ),
|
||||
_SEG('8', 4,1 , 4,2 , 4,4 , 4,8 , 5,8 , 5,4 , 5,2 ),
|
||||
_SEG('8', 2,1 , 2,2 , 2,4 , 2,8 , 3,4 , 3,2 , 3,1 ),
|
||||
|
||||
/* Line 2
|
||||
* Format : .........
|
||||
* Pict. name : NEW REP SU MO TU WE TH FR SA
|
||||
*/
|
||||
#define LCD_LINE2_OFFSET LCD_LINE1_OFFSET + LCD_LINE1_SIZE
|
||||
#define LCD_LINE2_SIZE 9
|
||||
|
||||
_PIC('.', 23,2 , "NEW" ),
|
||||
_PIC('.', 23,4 , "REP" ),
|
||||
_PIC('.', 1,8 , "SU" ),
|
||||
_PIC('.', 1,4 , "MO" ),
|
||||
_PIC('.', 1,2 , "TU" ),
|
||||
_PIC('.', 1,1 , "WE" ),
|
||||
_PIC('.', 0,1 , "TH" ),
|
||||
_PIC('.', 0,2 , "FR" ),
|
||||
_PIC('.', 0,4 , "SA" ),
|
||||
|
||||
/* Line 3
|
||||
* Format : 888888888888
|
||||
*/
|
||||
#define LCD_LINE3_OFFSET LCD_LINE2_OFFSET + LCD_LINE2_SIZE
|
||||
#define LCD_LINE3_SIZE 12
|
||||
|
||||
_SEG('8', 22,16, 22,32, 22,64, 22,128, 23,128, 23,64, 23,32 ),
|
||||
_SEG('8', 20,16, 20,32, 20,64, 20,128, 21,128, 21,64, 21,32 ),
|
||||
_SEG('8', 18,16, 18,32, 18,64, 18,128, 19,128, 19,64, 19,32 ),
|
||||
_SEG('8', 16,16, 16,32, 16,64, 16,128, 17,128, 17,64, 17,32 ),
|
||||
_SEG('8', 14,16, 14,32, 14,64, 14,128, 15,128, 15,64, 15,32 ),
|
||||
_SEG('8', 12,16, 12,32, 12,64, 12,128, 13,128, 13,64, 13,32 ),
|
||||
_SEG('8', 10,16, 10,32, 10,64, 10,128, 11,128, 11,64, 11,32 ),
|
||||
_SEG('8', 8,16, 8,32, 8,64, 8,128, 9,128, 9,64, 9,32 ),
|
||||
_SEG('8', 6,16, 6,32, 6,64, 6,128, 7,128, 7,64, 7,32 ),
|
||||
_SEG('8', 4,16, 4,32, 4,64, 4,128, 5,128, 5,64, 5,32 ),
|
||||
_SEG('8', 2,16, 2,32, 2,64, 2,128, 3,128, 3,64, 3,32 ),
|
||||
_SEG('8', 0,16, 0,32, 0,64, 0,128, 1,128, 1,64, 1,32 ),
|
||||
|
||||
/* Line 4
|
||||
*
|
||||
* The LED, DIALTONE and RINGTONE are implemented as icons and use the same
|
||||
* sysfs interface.
|
||||
*/
|
||||
#define LCD_LINE4_OFFSET LCD_LINE3_OFFSET + LCD_LINE3_SIZE
|
||||
|
||||
_PIC('.', offsetof(struct yld_status, led) , 0x01, "LED" ),
|
||||
_PIC('.', offsetof(struct yld_status, dialtone) , 0x01, "DIALTONE" ),
|
||||
_PIC('.', offsetof(struct yld_status, ringtone) , 0x24, "RINGTONE" ),
|
||||
|
||||
#undef _SEG
|
||||
#undef _PIC
|
||||
#endif /* _SEG && _PIC */
|
||||
Loading…
Reference in a new issue