Commit f2e1d89f authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (40 commits)
  Input: use full RCU API
  Input: remove tsdev interface
  Input: add support for Blackfin BF54x Keypad controller
  Input: appletouch - another fix for idle reset logic
  HWMON: hdaps - switch to using input-polldev
  Input: add support for SEGA Dreamcast keyboard
  Input: omap-keyboard - don't pretend we support changing keymap
  Input: lifebook - fix X and Y axis range
  Input: usbtouchscreen - add support for GeneralTouch devices
  Input: fix open count handling in input interfaces
  Input: keyboard - add CapsShift lock
  Input: adbhid - produce all CapsLock key events
  Input: ALPS - add signature for ThinkPad R61
  Input: jornada720_kbd - send MSC_SCAN events
  Input: add support for the HP Jornada 7xx (710/720/728) touchscreen
  Input: add support for HP Jornada 7xx onboard keyboard
  Input: add support for HP Jornada onboard keyboard (HP6XX)
  Input: ucb1400_ts - use schedule_timeout_uninterruptible
  Input: xpad - fix dependancy on LEDS class
  Input: auto-select INPUT for MAC_EMUMOUSEBTN option
  ...

Resolved conflicts manually in drivers/hwmon/applesmc.c: converting from
a class device to a device and converting to use input-polldev created a
few apparently trivial clashes..
parents 85ffdd28 82ba56c2
...@@ -205,20 +205,6 @@ Who: Len Brown <len.brown@intel.com> ...@@ -205,20 +205,6 @@ Who: Len Brown <len.brown@intel.com>
--------------------------- ---------------------------
What: Compaq touchscreen device emulation
When: Oct 2007
Files: drivers/input/tsdev.c
Why: The code says it was obsolete when it was written in 2001.
tslib is a userspace library which does anything tsdev can do and
much more besides in userspace where this code belongs. There is no
longer any need for tsdev and applications should have converted to
use tslib by now.
The name "tsdev" is also extremely confusing and lots of people have
it loaded when they don't need/use it.
Who: Richard Purdie <rpurdie@rpsys.net>
---------------------------
What: i2c-ixp2000, i2c-ixp4xx and scx200_i2c drivers What: i2c-ixp2000, i2c-ixp4xx and scx200_i2c drivers
When: September 2007 When: September 2007
Why: Obsolete. The new i2c-gpio driver replaces all hardware-specific Why: Obsolete. The new i2c-gpio driver replaces all hardware-specific
......
...@@ -1890,9 +1890,6 @@ and is between 256 and 4096 characters. It is defined in the file ...@@ -1890,9 +1890,6 @@ and is between 256 and 4096 characters. It is defined in the file
Format: Format:
<io>,<irq>,<dma>,<dma2>,<sb_io>,<sb_irq>,<sb_dma>,<mpu_io>,<mpu_irq> <io>,<irq>,<dma>,<dma2>,<sb_io>,<sb_irq>,<sb_dma>,<mpu_io>,<mpu_irq>
tsdev.xres= [TS] Horizontal screen resolution.
tsdev.yres= [TS] Vertical screen resolution.
turbografx.map[2|3]= [HW,JOY] turbografx.map[2|3]= [HW,JOY]
TurboGraFX parallel port interface TurboGraFX parallel port interface
Format: Format:
......
...@@ -88,7 +88,7 @@ static struct platform_device bf54x_lq043_device = { ...@@ -88,7 +88,7 @@ static struct platform_device bf54x_lq043_device = {
#endif #endif
#if defined(CONFIG_KEYBOARD_BFIN) || defined(CONFIG_KEYBOARD_BFIN_MODULE) #if defined(CONFIG_KEYBOARD_BFIN) || defined(CONFIG_KEYBOARD_BFIN_MODULE)
static int bf548_keymap[] = { static const unsigned int bf548_keymap[] = {
KEYVAL(0, 0, KEY_ENTER), KEYVAL(0, 0, KEY_ENTER),
KEYVAL(0, 1, KEY_HELP), KEYVAL(0, 1, KEY_HELP),
KEYVAL(0, 2, KEY_0), KEYVAL(0, 2, KEY_0),
...@@ -110,8 +110,8 @@ static int bf548_keymap[] = { ...@@ -110,8 +110,8 @@ static int bf548_keymap[] = {
static struct bfin_kpad_platform_data bf54x_kpad_data = { static struct bfin_kpad_platform_data bf54x_kpad_data = {
.rows = 4, .rows = 4,
.cols = 4, .cols = 4,
.keymap = bf548_keymap, .keymap = bf548_keymap,
.keymapsize = ARRAY_SIZE(bf548_keymap), .keymapsize = ARRAY_SIZE(bf548_keymap),
.repeat = 0, .repeat = 0,
.debounce_time = 5000, /* ns (5ms) */ .debounce_time = 5000, /* ns (5ms) */
.coldrive_time = 1000, /* ns (1ms) */ .coldrive_time = 1000, /* ns (1ms) */
......
/*
* linux/drivers/char/ec3104_keyb.c
*
* Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
*
* based on linux/drivers/char/pc_keyb.c, which had the following comments:
*
* Separation of the PC low-level part by Geert Uytterhoeven, May 1997
* See keyboard.c for the whole history.
*
* Major cleanup by Martin Mares, May 1997
*
* Combined the keyboard and PS/2 mouse handling into one file,
* because they share the same hardware.
* Johan Myreen <jem@iki.fi> 1998-10-08.
*
* Code fixes to handle mouse ACKs properly.
* C. Scott Ananian <cananian@alumni.princeton.edu> 1999-01-29.
*/
/* EC3104 note:
* This code was written without any documentation about the EC3104 chip. While
* I hope I got most of the basic functionality right, the register names I use
* are most likely completely different from those in the chip documentation.
*
* If you have any further information about the EC3104, please tell me
* (prumpf@tux.org).
*/
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/mm.h>
#include <linux/signal.h>
#include <linux/init.h>
#include <linux/kbd_ll.h>
#include <linux/delay.h>
#include <linux/random.h>
#include <linux/poll.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/kbd_kern.h>
#include <linux/bitops.h>
#include <asm/keyboard.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/ec3104.h>
#include <asm/io.h>
/* Some configuration switches are present in the include file... */
#include <linux/pc_keyb.h>
#define MSR_CTS 0x10
#define MCR_RTS 0x02
#define LSR_DR 0x01
#define LSR_BOTH_EMPTY 0x60
static struct e5_struct {
u8 packet[8];
int pos;
int length;
u8 cached_mcr;
u8 last_msr;
} ec3104_keyb;
/* Simple translation table for the SysRq keys */
#ifdef CONFIG_MAGIC_SYSRQ
unsigned char ec3104_kbd_sysrq_xlate[128] =
"\000\0331234567890-=\177\t" /* 0x00 - 0x0f */
"qwertyuiop[]\r\000as" /* 0x10 - 0x1f */
"dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */
"bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */
"\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */
"230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */
"\r\000/"; /* 0x60 - 0x6f */
#endif
static void kbd_write_command_w(int data);
static void kbd_write_output_w(int data);
#ifdef CONFIG_PSMOUSE
static void aux_write_ack(int val);
static void __aux_write_ack(int val);
#endif
static DEFINE_SPINLOCK(kbd_controller_lock);
static unsigned char handle_kbd_event(void);
/* used only by send_data - set by keyboard_interrupt */
static volatile unsigned char reply_expected;
static volatile unsigned char acknowledge;
static volatile unsigned char resend;
int ec3104_kbd_setkeycode(unsigned int scancode, unsigned int keycode)
{
return 0;
}
int ec3104_kbd_getkeycode(unsigned int scancode)
{
return 0;
}
/* yes, it probably would be faster to use an array. I don't care. */
static inline unsigned char ec3104_scan2key(unsigned char scancode)
{
switch (scancode) {
case 1: /* '`' */
return 41;
case 2 ... 27:
return scancode;
case 28: /* '\\' */
return 43;
case 29 ... 39:
return scancode + 1;
case 40: /* '\r' */
return 28;
case 41 ... 50:
return scancode + 3;
case 51: /* ' ' */
return 57;
case 52: /* escape */
return 1;
case 54: /* insert/delete (labelled delete) */
/* this should arguably be 110, but I'd like to have ctrl-alt-del
* working with a standard keymap */
return 111;
case 55: /* left */
return 105;
case 56: /* home */
return 102;
case 57: /* end */
return 107;
case 58: /* up */
return 103;
case 59: /* down */
return 108;
case 60: /* pgup */
return 104;
case 61: /* pgdown */
return 109;
case 62: /* right */
return 106;
case 79 ... 88: /* f1 - f10 */
return scancode - 20;
case 89 ... 90: /* f11 - f12 */
return scancode - 2;
case 91: /* left shift */
return 42;
case 92: /* right shift */
return 54;
case 93: /* left alt */
return 56;
case 94: /* right alt */
return 100;
case 95: /* left ctrl */
return 29;
case 96: /* right ctrl */
return 97;
case 97: /* caps lock */
return 58;
case 102: /* left windows */
return 125;
case 103: /* right windows */
return 126;
case 106: /* Fn */
/* this is wrong. */
return 84;
default:
return 0;
}
}
int ec3104_kbd_translate(unsigned char scancode, unsigned char *keycode,
char raw_mode)
{
scancode &= 0x7f;
*keycode = ec3104_scan2key(scancode);
return 1;
}
char ec3104_kbd_unexpected_up(unsigned char keycode)
{
return 0200;
}
static inline void handle_keyboard_event(unsigned char scancode)
{
#ifdef CONFIG_VT
handle_scancode(scancode, !(scancode & 0x80));
#endif
tasklet_schedule(&keyboard_tasklet);
}
void ec3104_kbd_leds(unsigned char leds)
{
}
static u8 e5_checksum(u8 *packet, int count)
{
int i;
u8 sum = 0;
for (i=0; i<count; i++)
sum ^= packet[i];
if (sum & 0x80)
sum ^= 0xc0;
return sum;
}
static void e5_wait_for_cts(struct e5_struct *k)
{
u8 msr;
do {
msr = ctrl_inb(EC3104_SER4_MSR);
} while (!(msr & MSR_CTS));
}
static void e5_send_byte(u8 byte, struct e5_struct *k)
{
u8 status;
do {
status = ctrl_inb(EC3104_SER4_LSR);
} while ((status & LSR_BOTH_EMPTY) != LSR_BOTH_EMPTY);
printk("<%02x>", byte);
ctrl_outb(byte, EC3104_SER4_DATA);
do {
status = ctrl_inb(EC3104_SER4_LSR);
} while ((status & LSR_BOTH_EMPTY) != LSR_BOTH_EMPTY);
}
static int e5_send_packet(u8 *packet, int count, struct e5_struct *k)
{
int i;
disable_irq(EC3104_IRQ_SER4);
if (k->cached_mcr & MCR_RTS) {
printk("e5_send_packet: too slow\n");
enable_irq(EC3104_IRQ_SER4);
return -EAGAIN;
}
k->cached_mcr |= MCR_RTS;
ctrl_outb(k->cached_mcr, EC3104_SER4_MCR);
e5_wait_for_cts(k);
printk("p: ");
for(i=0; i<count; i++)
e5_send_byte(packet[i], k);
e5_send_byte(e5_checksum(packet, count), k);
printk("\n");
udelay(1500);
k->cached_mcr &= ~MCR_RTS;
ctrl_outb(k->cached_mcr, EC3104_SER4_MCR);
set_current_state(TASK_UNINTERRUPTIBLE);
enable_irq(EC3104_IRQ_SER4);
return 0;
}
/*
* E5 packets we know about:
* E5->host 0x80 0x05 <checksum> - resend packet
* host->E5 0x83 0x43 <contrast> - set LCD contrast
* host->E5 0x85 0x41 0x02 <brightness> 0x02 - set LCD backlight
* E5->host 0x87 <ps2 packet> 0x00 <checksum> - external PS2
* E5->host 0x88 <scancode> <checksum> - key press
*/
static void e5_receive(struct e5_struct *k)
{
k->packet[k->pos++] = ctrl_inb(EC3104_SER4_DATA);
if (k->pos == 1) {
switch(k->packet[0]) {
case 0x80:
k->length = 3;
break;
case 0x87: /* PS2 ext */
k->length = 6;
break;
case 0x88: /* keyboard */
k->length = 3;
break;
default:
k->length = 1;
printk(KERN_WARNING "unknown E5 packet %02x\n",
k->packet[0]);
}
}
if (k->pos == k->length) {
int i;
if (e5_checksum(k->packet, k->length) != 0)
printk(KERN_WARNING "E5: wrong checksum\n");
#if 0
printk("E5 packet [");
for(i=0; i<k->length; i++) {
printk("%02x ", k->packet[i]);
}
printk("(%02x)]\n", e5_checksum(k->packet, k->length-1));
#endif
switch(k->packet[0]) {
case 0x80:
case 0x88:
handle_keyboard_event(k->packet[1]);
break;
}
k->pos = k->length = 0;
}
}
static void ec3104_keyb_interrupt(int irq, void *data)
{
struct e5_struct *k = &ec3104_keyb;
u8 msr, lsr;
msr = ctrl_inb(EC3104_SER4_MSR);
if ((msr & MSR_CTS) && !(k->last_msr & MSR_CTS)) {
if (k->cached_mcr & MCR_RTS)
printk("confused: RTS already high\n");
/* CTS went high. Send RTS. */
k->cached_mcr |= MCR_RTS;
ctrl_outb(k->cached_mcr, EC3104_SER4_MCR);
} else if ((!(msr & MSR_CTS)) && (k->last_msr & MSR_CTS)) {
/* CTS went low. */
if (!(k->cached_mcr & MCR_RTS))
printk("confused: RTS already low\n");
k->cached_mcr &= ~MCR_RTS;
ctrl_outb(k->cached_mcr, EC3104_SER4_MCR);
}
k->last_msr = msr;
lsr = ctrl_inb(EC3104_SER4_LSR);
if (lsr & LSR_DR)
e5_receive(k);
}
static void ec3104_keyb_clear_state(void)
{
struct e5_struct *k = &ec3104_keyb;
u8 msr, lsr;
/* we want CTS to be low */
k->last_msr = 0;
for (;;) {
msleep(100);
msr = ctrl_inb(EC3104_SER4_MSR);
lsr = ctrl_inb(EC3104_SER4_LSR);
if (lsr & LSR_DR) {
e5_receive(k);
continue;
}
if ((msr & MSR_CTS) && !(k->last_msr & MSR_CTS)) {
if (k->cached_mcr & MCR_RTS)
printk("confused: RTS already high\n");
/* CTS went high. Send RTS. */
k->cached_mcr |= MCR_RTS;
ctrl_outb(k->cached_mcr, EC3104_SER4_MCR);
} else if ((!(msr & MSR_CTS)) && (k->last_msr & MSR_CTS)) {
/* CTS went low. */
if (!(k->cached_mcr & MCR_RTS))
printk("confused: RTS already low\n");
k->cached_mcr &= ~MCR_RTS;
ctrl_outb(k->cached_mcr, EC3104_SER4_MCR);
} else
break;
k->last_msr = msr;
continue;
}
}
void __init ec3104_kbd_init_hw(void)
{
ec3104_keyb.last_msr = ctrl_inb(EC3104_SER4_MSR);
ec3104_keyb.cached_mcr = ctrl_inb(EC3104_SER4_MCR);
ec3104_keyb_clear_state();
/* Ok, finally allocate the IRQ, and off we go.. */
request_irq(EC3104_IRQ_SER4, ec3104_keyb_interrupt, 0, "keyboard", NULL);
}
...@@ -158,6 +158,7 @@ config SENSORS_K8TEMP ...@@ -158,6 +158,7 @@ config SENSORS_K8TEMP
config SENSORS_AMS config SENSORS_AMS
tristate "Apple Motion Sensor driver" tristate "Apple Motion Sensor driver"
depends on PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL depends on PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL
select INPUT_POLLDEV
help help
Support for the motion sensor included in PowerBooks. Includes Support for the motion sensor included in PowerBooks. Includes
implementations for PMU and I2C. implementations for PMU and I2C.
...@@ -701,6 +702,7 @@ config SENSORS_W83627EHF ...@@ -701,6 +702,7 @@ config SENSORS_W83627EHF
config SENSORS_HDAPS config SENSORS_HDAPS
tristate "IBM Hard Drive Active Protection System (hdaps)" tristate "IBM Hard Drive Active Protection System (hdaps)"
depends on INPUT && X86 depends on INPUT && X86
select INPUT_POLLDEV
default n default n
help help
This driver provides support for the IBM Hard Drive Active Protection This driver provides support for the IBM Hard Drive Active Protection
...@@ -722,6 +724,7 @@ config SENSORS_APPLESMC ...@@ -722,6 +724,7 @@ config SENSORS_APPLESMC
depends on INPUT && X86 depends on INPUT && X86
select NEW_LEDS select NEW_LEDS
select LEDS_CLASS select LEDS_CLASS
select INPUT_POLLDEV
default n default n
help help
This driver provides support for the Apple System Management This driver provides support for the Apple System Management
......
...@@ -27,47 +27,32 @@ static unsigned int invert; ...@@ -27,47 +27,32 @@ static unsigned int invert;
module_param(invert, bool, 0644); module_param(invert, bool, 0644);
MODULE_PARM_DESC(invert, "Invert input data on X and Y axis"); MODULE_PARM_DESC(invert, "Invert input data on X and Y axis");
static int ams_input_kthread(void *data) static void ams_idev_poll(struct input_polled_dev *dev)
{ {
struct input_dev *idev = dev->input;
s8 x, y, z; s8 x, y, z;
while (!kthread_should_stop()) { mutex_lock(&ams_info.lock);
mutex_lock(&ams_info.lock);
ams_sensors(&x, &y, &z);
x -= ams_info.xcalib;
y -= ams_info.ycalib;
z -= ams_info.zcalib;
input_report_abs(ams_info.idev, ABS_X, invert ? -x : x);
input_report_abs(ams_info.idev, ABS_Y, invert ? -y : y);
input_report_abs(ams_info.idev, ABS_Z, z);
input_sync(ams_info.idev); ams_sensors(&x, &y, &z);
mutex_unlock(&ams_info.lock); x -= ams_info.xcalib;
y -= ams_info.ycalib;
z -= ams_info.zcalib;
msleep(25); input_report_abs(idev, ABS_X, invert ? -x : x);
} input_report_abs(idev, ABS_Y, invert ? -y : y);
input_report_abs(idev, ABS_Z, z);
return 0; input_sync(idev);
}
static int ams_input_open(struct input_dev *dev) mutex_unlock(&ams_info.lock);
{
ams_info.kthread = kthread_run(ams_input_kthread, NULL, "kams");
return IS_ERR(ams_info.kthread) ? PTR_ERR(ams_info.kthread) : 0;
}
static void ams_input_close(struct input_dev *dev)
{
kthread_stop(ams_info.kthread);
} }
/* Call with ams_info.lock held! */ /* Call with ams_info.lock held! */
static void ams_input_enable(void) static void ams_input_enable(void)
{ {
struct input_dev *input;
s8 x, y, z; s8 x, y, z;
if (ams_info.idev) if (ams_info.idev)
...@@ -78,27 +63,29 @@ static void ams_input_enable(void) ...@@ -78,27 +63,29 @@ static void ams_input_enable(void)
ams_info.ycalib = y; ams_info.ycalib = y;
ams_info.zcalib = z; ams_info.zcalib = z;
ams_info.idev = input_allocate_device(); ams_info.idev = input_allocate_polled_device();
if (!ams_info.idev) if (!ams_info.idev)
return; return;
ams_info.idev->name = "Apple Motion Sensor"; ams_info.idev->poll = ams_idev_poll;
ams_info.idev->id.bustype = ams_info.bustype; ams_info.idev->poll_interval = 25;
ams_info.idev->id.vendor = 0;
ams_info.idev->open = ams_input_open; input = ams_info.idev->input;
ams_info.idev->close = ams_input_close; input->name = "Apple Motion Sensor";
ams_info.idev->dev.parent = &ams_info.of_dev->dev; input->id.bustype = ams_info.bustype;
input->id.vendor = 0;
input->dev.parent = &ams_info.of_dev->dev;
input_set_abs_params(ams_info.idev, ABS_X, -50, 50, 3, 0); input_set_abs_params(input, ABS_X, -50, 50, 3, 0);
input_set_abs_params(ams_info.idev, ABS_Y, -50, 50, 3, 0); input_set_abs_params(input, ABS_Y, -50, 50, 3, 0);
input_set_abs_params(ams_info.idev, ABS_Z, -50, 50, 3, 0); input_set_abs_params(input, ABS_Z, -50, 50, 3, 0);
set_bit(EV_ABS, ams_info.idev->evbit); set_bit(EV_ABS, input->evbit);
set_bit(EV_KEY, ams_info.idev->evbit); set_bit(EV_KEY, input->evbit);
set_bit(BTN_TOUCH, ams_info.idev->keybit); set_bit(BTN_TOUCH, input->keybit);
if (input_register_device(ams_info.idev)) { if (input_register_polled_device(ams_info.idev)) {
input_free_device(ams_info.idev); input_free_polled_device(ams_info.idev);
ams_info.idev = NULL; ams_info.idev = NULL;
return; return;
} }
...@@ -108,7 +95,8 @@ static void ams_input_enable(void) ...@@ -108,7 +95,8 @@ static void ams_input_enable(void)
static void ams_input_disable(void) static void ams_input_disable(void)
{ {
if (ams_info.idev) { if (ams_info.idev) {
input_unregister_device(ams_info.idev); input_unregister_polled_device(ams_info.idev);
input_free_polled_device(ams_info.idev);
ams_info.idev = NULL; ams_info.idev = NULL;
} }
} }
......
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/input.h> #include <linux/input-polldev.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
...@@ -52,8 +52,7 @@ struct ams { ...@@ -52,8 +52,7 @@ struct ams {
#endif #endif
/* Joystick emulation */ /* Joystick emulation */
struct task_struct *kthread; struct input_polled_dev *idev;
struct input_dev *idev;
__u16 bustype; __u16 bustype;
/* calibrated null values */ /* calibrated null values */
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/input.h> #include <linux/input-polldev.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/timer.h> #include <linux/timer.h>
...@@ -59,9 +59,9 @@ ...@@ -59,9 +59,9 @@
#define LIGHT_SENSOR_LEFT_KEY "ALV0" /* r-o {alv (6 bytes) */ #define LIGHT_SENSOR_LEFT_KEY "ALV0" /* r-o {alv (6 bytes) */
#define LIGHT_SENSOR_RIGHT_KEY "ALV1" /* r-o {alv (6 bytes) */ #define LIGHT_SENSOR_RIGHT_KEY "ALV1" /* r-o {alv (6 bytes) */
#define BACKLIGHT_KEY "LKSB" /* w-o {lkb (2 bytes) */ #define BACKLIGHT_KEY "LKSB" /* w-o {lkb (2 bytes) */
#define CLAMSHELL_KEY "MSLD" /* r-o ui8 (unused) */ #define CLAMSHELL_KEY "MSLD" /* r-o ui8 (unused) */
#define MOTION_SENSOR_X_KEY "MO_X" /* r-o sp78 (2 bytes) */ #define MOTION_SENSOR_X_KEY "MO_X" /* r-o sp78 (2 bytes) */
#define MOTION_SENSOR_Y_KEY "MO_Y" /* r-o sp78 (2 bytes) */ #define MOTION_SENSOR_Y_KEY "MO_Y" /* r-o sp78 (2 bytes) */
...@@ -103,7 +103,7 @@ static const char* fan_speed_keys[] = { ...@@ -103,7 +103,7 @@ static const char* fan_speed_keys[] = {
#define INIT_TIMEOUT_MSECS 5000 /* wait up to 5s for device init ... */ #define INIT_TIMEOUT_MSECS 5000 /* wait up to 5s for device init ... */
#define INIT_WAIT_MSECS 50 /* ... in 50ms increments */ #define INIT_WAIT_MSECS 50 /* ... in 50ms increments */
#define APPLESMC_POLL_PERIOD (HZ/20) /* poll for input every 1/20s */ #define APPLESMC_POLL_INTERVAL 50 /* msecs */
#define APPLESMC_INPUT_FUZZ 4 /* input event threshold */ #define APPLESMC_INPUT_FUZZ 4 /* input event threshold */
#define APPLESMC_INPUT_FLAT 4 #define APPLESMC_INPUT_FLAT 4
...@@ -125,9 +125,8 @@ static const int debug; ...@@ -125,9 +125,8 @@ static const int debug;
static struct platform_device *pdev; static struct platform_device *pdev;
static s16 rest_x; static s16 rest_x;
static s16 rest_y; static s16 rest_y;
static struct timer_list applesmc_timer;
static struct input_dev *applesmc_idev;
static struct device *hwmon_dev; static struct device *hwmon_dev;
static struct input_polled_dev *applesmc_idev;
/* Indicates whether this computer has an accelerometer. */ /* Indicates whether this computer has an accelerometer. */
static unsigned int applesmc_accelerometer; static unsigned int applesmc_accelerometer;
...@@ -138,7 +137,7 @@ static unsigned int applesmc_light; ...@@ -138,7 +137,7 @@ static unsigned int applesmc_light;
/* Indicates which temperature sensors set to use. */ /* Indicates which temperature sensors set to use. */
static unsigned int applesmc_temperature_set; static unsigned int applesmc_temperature_set;
static struct mutex applesmc_lock; static DEFINE_MUTEX(applesmc_lock);
/* /*
* Last index written to key_at_index sysfs file, and value to use for all other * Last index written to key_at_index sysfs file, and value to use for all other
...@@ -455,27 +454,12 @@ static void applesmc_calibrate(void) ...@@ -455,27 +454,12 @@ static void applesmc_calibrate(void)
rest_x = -rest_x; rest_x = -rest_x;
} }
static int applesmc_idev_open(struct input_dev *dev) static void applesmc_idev_poll(struct input_polled_dev *dev)
{
add_timer(&applesmc_timer);
return 0;
}
static void applesmc_idev_close(struct input_dev *dev)
{
del_timer_sync(&applesmc_timer);
}
static void applesmc_idev_poll(unsigned long unused)
{ {
struct input_dev *idev = dev->input;
s16 x, y; s16 x, y;
/* Cannot sleep. Try nonblockingly. If we fail, try again later. */ mutex_lock(&applesmc_lock);
if (!mutex_trylock(&applesmc_lock)) {
mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
return;
}
if (applesmc_read_motion_sensor(SENSOR_X, &x)) if (applesmc_read_motion_sensor(SENSOR_X, &x))
goto out; goto out;
...@@ -483,13 +467,11 @@ static void applesmc_idev_poll(unsigned long unused) ...@@ -483,13 +467,11 @@ static void applesmc_idev_poll(unsigned long unused)
goto out; goto out;
x = -x; x = -x;
input_report_abs(applesmc_idev, ABS_X, x - rest_x); input_report_abs(idev, ABS_X, x - rest_x);
input_report_abs(applesmc_idev, ABS_Y, y - rest_y); input_report_abs(idev, ABS_Y, y - rest_y);
input_sync(applesmc_idev); input_sync(idev);
out: out:
mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
mutex_unlock(&applesmc_lock); mutex_unlock(&applesmc_lock);
} }
...@@ -821,8 +803,7 @@ static ssize_t applesmc_key_at_index_read_show(struct device *dev, ...@@ -821,8 +803,7 @@ static ssize_t applesmc_key_at_index_read_show(struct device *dev,
if (!ret) { if (!ret) {
return info[0]; return info[0];
} } else {
else {
return ret; return ret;
} }
} }
...@@ -1093,6 +1074,7 @@ static int applesmc_dmi_match(const struct dmi_system_id *id) ...@@ -1093,6 +1074,7 @@ static int applesmc_dmi_match(const struct dmi_system_id *id)
/* Create accelerometer ressources */ /* Create accelerometer ressources */
static int applesmc_create_accelerometer(void) static int applesmc_create_accelerometer(void)
{ {
struct input_dev *idev;
int ret; int ret;
ret = sysfs_create_group(&pdev->dev.kobj, ret = sysfs_create_group(&pdev->dev.kobj,
...@@ -1100,40 +1082,37 @@ static int applesmc_create_accelerometer(void) ...@@ -1100,40 +1082,37 @@ static int applesmc_create_accelerometer(void)
if (ret) if (ret)
goto out; goto out;
applesmc_idev = input_allocate_device(); applesmc_idev = input_allocate_polled_device();
if (!applesmc_idev) { if (!applesmc_idev) {
ret = -ENOMEM; ret = -ENOMEM;
goto out_sysfs; goto out_sysfs;
} }
applesmc_idev->poll = applesmc_idev_poll;
applesmc_idev->poll_interval = APPLESMC_POLL_INTERVAL;
/* initial calibrate for the input device */ /* initial calibrate for the input device */
applesmc_calibrate(); applesmc_calibrate();
/* initialize the input class */ /* initialize the input device */
applesmc_idev->name = "applesmc"; idev = applesmc_idev->input;
applesmc_idev->id.bustype = BUS_HOST; idev->name = "applesmc";
applesmc_idev->dev.parent = &pdev->dev; idev->id.bustype = BUS_HOST;
applesmc_idev->evbit[0] = BIT(EV_ABS); idev->dev.parent = &pdev->dev;
applesmc_idev->open = applesmc_idev_open; idev->evbit[0] = BIT(EV_ABS);
applesmc_idev->close = applesmc_idev_close; input_set_abs_params(idev, ABS_X,
input_set_abs_params(applesmc_idev, ABS_X,
-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT); -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
input_set_abs_params(applesmc_idev, ABS_Y, input_set_abs_params(idev, ABS_Y,
-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT); -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
ret = input_register_device(applesmc_idev); ret = input_register_polled_device(applesmc_idev);
if (ret) if (ret)
goto out_idev; goto out_idev;
/* start up our timer for the input device */
init_timer(&applesmc_timer);
applesmc_timer.function = applesmc_idev_poll;
applesmc_timer.expires = jiffies + APPLESMC_POLL_PERIOD;
return 0; return 0;
out_idev: out_idev:
input_free_device(applesmc_idev); input_free_polled_device(applesmc_idev);
out_sysfs: out_sysfs:
sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group); sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
...@@ -1146,8 +1125,8 @@ static int applesmc_create_accelerometer(void) ...@@ -1146,8 +1125,8 @@ static int applesmc_create_accelerometer(void)
/* Release all ressources used by the accelerometer */ /* Release all ressources used by the accelerometer */
static void applesmc_release_accelerometer(void) static void applesmc_release_accelerometer(void)
{ {
del_timer_sync(&applesmc_timer); input_unregister_polled_device(applesmc_idev);
input_unregister_device(applesmc_idev); input_free_polled_device(applesmc_idev);
sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group); sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
} }
...@@ -1184,8 +1163,6 @@ static int __init applesmc_init(void) ...@@ -1184,8 +1163,6 @@ static int __init applesmc_init(void)
int count; int count;
int i; int i;
mutex_init(&applesmc_lock);
if (!dmi_check_system(applesmc_whitelist)) { if (!dmi_check_system(applesmc_whitelist)) {
printk(KERN_WARNING "applesmc: supported laptop not found!\n"); printk(KERN_WARNING "applesmc: supported laptop not found!\n");
ret = -ENODEV; ret = -ENODEV;
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/input.h> #include <linux/input-polldev.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -61,13 +61,12 @@ ...@@ -61,13 +61,12 @@
#define INIT_TIMEOUT_MSECS 4000 /* wait up to 4s for device init ... */ #define INIT_TIMEOUT_MSECS 4000 /* wait up to 4s for device init ... */
#define INIT_WAIT_MSECS 200 /* ... in 200ms increments */ #define INIT_WAIT_MSECS 200 /* ... in 200ms increments */
#define HDAPS_POLL_PERIOD (HZ/20) /* poll for input every 1/20s */ #define HDAPS_POLL_INTERVAL 50 /* poll for input every 1/20s (50 ms)*/
#define HDAPS_INPUT_FUZZ 4 /* input event threshold */ #define HDAPS_INPUT_FUZZ 4 /* input event threshold */
#define HDAPS_INPUT_FLAT 4 #define HDAPS_INPUT_FLAT 4
static struct timer_list hdaps_timer;
static struct platform_device *pdev; static struct platform_device *pdev;
static struct input_dev *hdaps_idev; static struct input_polled_dev *hdaps_idev;
static unsigned int hdaps_invert; static unsigned int hdaps_invert;
static u8 km_activity; static u8 km_activity;
static int rest_x; static int rest_x;
...@@ -323,24 +322,19 @@ static void hdaps_calibrate(void) ...@@ -323,24 +322,19 @@ static void hdaps_calibrate(void)
__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &rest_x, &rest_y); __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &rest_x, &rest_y);
} }
static void hdaps_mousedev_poll(unsigned long unused) static void hdaps_mousedev_poll(struct input_polled_dev *dev)
{ {
struct input_dev *input_dev = dev->input;
int x, y; int x, y;
/* Cannot sleep. Try nonblockingly. If we fail, try again later. */ mutex_lock(&hdaps_mtx);
if (mutex_trylock(&hdaps_mtx)) {
mod_timer(&hdaps_timer,jiffies + HDAPS_POLL_PERIOD);
return;
}
if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y)) if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y))
goto out; goto out;
input_report_abs(hdaps_idev, ABS_X, x - rest_x); input_report_abs(input_dev, ABS_X, x - rest_x);
input_report_abs(hdaps_idev, ABS_Y, y - rest_y); input_report_abs(input_dev, ABS_Y, y - rest_y);
input_sync(hdaps_idev); input_sync(input_dev);
mod_timer(&hdaps_timer, jiffies + HDAPS_POLL_PERIOD);
out: out:
mutex_unlock(&hdaps_mtx); mutex_unlock(&hdaps_mtx);
...@@ -536,6 +530,7 @@ static struct dmi_system_id __initdata hdaps_whitelist[] = { ...@@ -536,6 +530,7 @@ static struct dmi_system_id __initdata hdaps_whitelist[] = {
static int __init hdaps_init(void) static int __init hdaps_init(void)
{ {
struct input_dev *idev;
int ret; int ret;
if (!dmi_check_system(hdaps_whitelist)) { if (!dmi_check_system(hdaps_whitelist)) {
...@@ -563,39 +558,37 @@ static int __init hdaps_init(void) ...@@ -563,39 +558,37 @@ static int __init hdaps_init(void)
if (ret) if (ret)
goto out_device; goto out_device;
hdaps_idev = input_allocate_device(); hdaps_idev = input_allocate_polled_device();
if (!hdaps_idev) { if (!hdaps_idev) {
ret = -ENOMEM; ret = -ENOMEM;
goto out_group; goto out_group;
} }
hdaps_idev->poll = hdaps_mousedev_poll;
hdaps_idev->poll_interval = HDAPS_POLL_INTERVAL;
/* initial calibrate for the input device */ /* initial calibrate for the input device */
hdaps_calibrate(); hdaps_calibrate();
/* initialize the input class */ /* initialize the input class */
hdaps_idev->name = "hdaps"; idev = hdaps_idev->input;
hdaps_idev->dev.parent = &pdev->dev; idev->name = "hdaps";
hdaps_idev->evbit[0] = BIT(EV_ABS); idev->dev.parent = &pdev->dev;
input_set_abs_params(hdaps_idev, ABS_X, idev->evbit[0] = BIT(EV_ABS);
input_set_abs_params(idev, ABS_X,
-256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT); -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
input_set_abs_params(hdaps_idev, ABS_Y, input_set_abs_params(idev, ABS_Y,
-256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT); -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
ret = input_register_device(hdaps_idev); ret = input_register_polled_device(hdaps_idev);
if (ret) if (ret)
goto out_idev; goto out_idev;
/* start up our timer for the input device */
init_timer(&hdaps_timer);
hdaps_timer.function = hdaps_mousedev_poll;
hdaps_timer.expires = jiffies + HDAPS_POLL_PERIOD;
add_timer(&hdaps_timer);
printk(KERN_INFO "hdaps: driver successfully loaded.\n"); printk(KERN_INFO "hdaps: driver successfully loaded.\n");
return 0; return 0;
out_idev: out_idev:
input_free_device(hdaps_idev); input_free_polled_device(hdaps_idev);
out_group: out_group:
sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
out_device: out_device:
...@@ -611,8 +604,8 @@ static int __init hdaps_init(void) ...@@ -611,8 +604,8 @@ static int __init hdaps_init(void)
static void __exit hdaps_exit(void) static void __exit hdaps_exit(void)
{ {
del_timer_sync(&hdaps_timer); input_unregister_polled_device(hdaps_idev);
input_unregister_device(hdaps_idev); input_free_polled_device(hdaps_idev);
sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
platform_device_unregister(pdev); platform_device_unregister(pdev);
platform_driver_unregister(&hdaps_driver); platform_driver_unregister(&hdaps_driver);
......
...@@ -114,28 +114,6 @@ config INPUT_JOYDEV ...@@ -114,28 +114,6 @@ config INPUT_JOYDEV
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called joydev. module will be called joydev.
config INPUT_TSDEV
tristate "Touchscreen interface"
---help---
Say Y here if you have an application that only can understand the
Compaq touchscreen protocol for absolute pointer data. This is
useful namely for embedded configurations.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called tsdev.
config INPUT_TSDEV_SCREEN_X
int "Horizontal screen resolution"
depends on INPUT_TSDEV
default "240"
config INPUT_TSDEV_SCREEN_Y
int "Vertical screen resolution"
depends on INPUT_TSDEV
default "320"
config INPUT_EVDEV config INPUT_EVDEV
tristate "Event interface" tristate "Event interface"
help help
......
...@@ -13,7 +13,6 @@ obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o ...@@ -13,7 +13,6 @@ obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o
obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
obj-$(CONFIG_INPUT_JOYDEV) += joydev.o obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
obj-$(CONFIG_INPUT_EVDEV) += evdev.o obj-$(CONFIG_INPUT_EVDEV) += evdev.o
obj-$(CONFIG_INPUT_TSDEV) += tsdev.o
obj-$(CONFIG_INPUT_EVBUG) += evbug.o obj-$(CONFIG_INPUT_EVBUG) += evbug.o
obj-$(CONFIG_INPUT_KEYBOARD) += keyboard/ obj-$(CONFIG_INPUT_KEYBOARD) += keyboard/
......
...@@ -30,6 +30,8 @@ struct evdev { ...@@ -30,6 +30,8 @@ struct evdev {
wait_queue_head_t wait; wait_queue_head_t wait;
struct evdev_client *grab; struct evdev_client *grab;
struct list_head client_list; struct list_head client_list;
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev; struct device dev;
}; };
...@@ -37,39 +39,54 @@ struct evdev_client { ...@@ -37,39 +39,54 @@ struct evdev_client {
struct input_event buffer[EVDEV_BUFFER_SIZE]; struct input_event buffer[EVDEV_BUFFER_SIZE];
int head; int head;
int tail; int tail;
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct fasync_struct *fasync; struct fasync_struct *fasync;
struct evdev *evdev; struct evdev *evdev;
struct list_head node; struct list_head node;
}; };
static struct evdev *evdev_table[EVDEV_MINORS]; static struct evdev *evdev_table[EVDEV_MINORS];
static DEFINE_MUTEX(evdev_table_mutex);
static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) static void evdev_pass_event(struct evdev_client *client,
struct input_event *event)
{
/*
* Interrupts are disabled, just acquire the lock
*/
spin_lock(&client->buffer_lock);
client->buffer[client->head++] = *event;
client->head &= EVDEV_BUFFER_SIZE - 1;
spin_unlock(&client->buffer_lock);
kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
/*
* Pass incoming event to all connected clients.
*/
static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{ {
struct evdev *evdev = handle->private; struct evdev *evdev = handle->private;
struct evdev_client *client; struct evdev_client *client;
struct input_event event;
if (evdev->grab) { do_gettimeofday(&event.time);
client = evdev->grab; event.type = type;
event.code = code;
event.value = value;
do_gettimeofday(&client->buffer[client->head].time); rcu_read_lock();
client->buffer[client->head].type = type;
client->buffer[client->head].code = code;
client->buffer[client->head].value = value;
client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);
kill_fasync(&client->fasync, SIGIO, POLL_IN); client = rcu_dereference(evdev->grab);
} else if (client)
list_for_each_entry(client, &evdev->client_list, node) { evdev_pass_event(client, &event);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client, &event);
do_gettimeofday(&client->buffer[client->head].time); rcu_read_unlock();
client->buffer[client->head].type = type;
client->buffer[client->head].code = code;
client->buffer[client->head].value = value;
client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);
kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
wake_up_interruptible(&evdev->wait); wake_up_interruptible(&evdev->wait);
} }
...@@ -88,38 +105,140 @@ static int evdev_flush(struct file *file, fl_owner_t id) ...@@ -88,38 +105,140 @@ static int evdev_flush(struct file *file, fl_owner_t id)
{ {
struct evdev_client *client = file->private_data; struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev; struct evdev *evdev = client->evdev;
int retval;
retval = mutex_lock_interruptible(&evdev->mutex);
if (retval)
return retval;
if (!evdev->exist) if (!evdev->exist)
return -ENODEV; retval = -ENODEV;
else
retval = input_flush_device(&evdev->handle, file);
return input_flush_device(&evdev->handle, file); mutex_unlock(&evdev->mutex);
return retval;
} }
static void evdev_free(struct device *dev) static void evdev_free(struct device *dev)
{ {
struct evdev *evdev = container_of(dev, struct evdev, dev); struct evdev *evdev = container_of(dev, struct evdev, dev);
evdev_table[evdev->minor] = NULL;
kfree(evdev); kfree(evdev);
} }
/*
* Grabs an event device (along with underlying input device).
* This function is called with evdev->mutex taken.
*/
static int evdev_grab(struct evdev *evdev, struct evdev_client *client)
{
int error;
if (evdev->grab)
return -EBUSY;
error = input_grab_device(&evdev->handle);
if (error)
return error;
rcu_assign_pointer(evdev->grab, client);
synchronize_rcu();
return 0;
}
static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client)
{
if (evdev->grab != client)
return -EINVAL;
rcu_assign_pointer(evdev->grab, NULL);
synchronize_rcu();
input_release_device(&evdev->handle);
return 0;
}
static void evdev_attach_client(struct evdev *evdev,
struct evdev_client *client)
{
spin_lock(&evdev->client_lock);
list_add_tail_rcu(&client->node, &evdev->client_list);
spin_unlock(&evdev->client_lock);
synchronize_rcu();
}
static void evdev_detach_client(struct evdev *evdev,
struct evdev_client *client)
{
spin_lock(&evdev->client_lock);
list_del_rcu(&client->node);
spin_unlock(&evdev->client_lock);
synchronize_rcu();
}
static int evdev_open_device(struct evdev *evdev)
{
int retval;
retval = mutex_lock_interruptible(&evdev->mutex);
if (retval)
return retval;
if (!evdev->exist)
retval = -ENODEV;
else if (!evdev->open++) {
retval = input_open_device(&evdev->handle);
if (retval)
evdev->open--;
}
mutex_unlock(&evdev->mutex);
return retval;
}
static void evdev_close_device(struct evdev *evdev)
{
mutex_lock(&evdev->mutex);
if (evdev->exist && !--evdev->open)
input_close_device(&evdev->handle);
mutex_unlock(&evdev->mutex);
}
/*
* Wake up users waiting for IO so they can disconnect from
* dead device.
*/
static void evdev_hangup(struct evdev *evdev)
{
struct evdev_client *client;
spin_lock(&evdev->client_lock);
list_for_each_entry(client, &evdev->client_list, node)
kill_fasync(&client->fasync, SIGIO, POLL_HUP);
spin_unlock(&evdev->client_lock);
wake_up_interruptible(&evdev->wait);
}
static int evdev_release(struct inode *inode, struct file *file) static int evdev_release(struct inode *inode, struct file *file)
{ {
struct evdev_client *client = file->private_data; struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev; struct evdev *evdev = client->evdev;
if (evdev->grab == client) { mutex_lock(&evdev->mutex);
input_release_device(&evdev->handle); if (evdev->grab == client)
evdev->grab = NULL; evdev_ungrab(evdev, client);
} mutex_unlock(&evdev->mutex);
evdev_fasync(-1, file, 0); evdev_fasync(-1, file, 0);
list_del(&client->node); evdev_detach_client(evdev, client);
kfree(client); kfree(client);
if (!--evdev->open && evdev->exist) evdev_close_device(evdev);
input_close_device(&evdev->handle);
put_device(&evdev->dev); put_device(&evdev->dev);
return 0; return 0;
...@@ -127,41 +246,44 @@ static int evdev_release(struct inode *inode, struct file *file) ...@@ -127,41 +246,44 @@ static int evdev_release(struct inode *inode, struct file *file)
static int evdev_open(struct inode *inode, struct file *file) static int evdev_open(struct inode *inode, struct file *file)
{ {
struct evdev_client *client;
struct evdev *evdev; struct evdev *evdev;
struct evdev_client *client;
int i = iminor(inode) - EVDEV_MINOR_BASE; int i = iminor(inode) - EVDEV_MINOR_BASE;
int error; int error;
if (i >= EVDEV_MINORS) if (i >= EVDEV_MINORS)
return -ENODEV; return -ENODEV;
error = mutex_lock_interruptible(&evdev_table_mutex);
if (error)
return error;
evdev = evdev_table[i]; evdev = evdev_table[i];
if (evdev)
get_device(&evdev->dev);
mutex_unlock(&evdev_table_mutex);
if (!evdev || !evdev->exist) if (!evdev)
return -ENODEV; return -ENODEV;
get_device(&evdev->dev);
client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL); client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
if (!client) { if (!client) {
error = -ENOMEM; error = -ENOMEM;
goto err_put_evdev; goto err_put_evdev;
} }
spin_lock_init(&client->buffer_lock);
client->evdev = evdev; client->evdev = evdev;
list_add_tail(&client->node, &evdev->client_list); evdev_attach_client(evdev, client);
if (!evdev->open++ && evdev->exist) { error = evdev_open_device(evdev);
error = input_open_device(&evdev->handle); if (error)
if (error) goto err_free_client;
goto err_free_client;
}
file->private_data = client; file->private_data = client;
return 0; return 0;
err_free_client: err_free_client:
list_del(&client->node); evdev_detach_client(evdev, client);
kfree(client); kfree(client);
err_put_evdev: err_put_evdev:
put_device(&evdev->dev); put_device(&evdev->dev);
...@@ -197,12 +319,14 @@ static inline size_t evdev_event_size(void) ...@@ -197,12 +319,14 @@ static inline size_t evdev_event_size(void)
sizeof(struct input_event_compat) : sizeof(struct input_event); sizeof(struct input_event_compat) : sizeof(struct input_event);
} }
static int evdev_event_from_user(const char __user *buffer, struct input_event *event) static int evdev_event_from_user(const char __user *buffer,
struct input_event *event)
{ {
if (COMPAT_TEST) { if (COMPAT_TEST) {
struct input_event_compat compat_event; struct input_event_compat compat_event;
if (copy_from_user(&compat_event, buffer, sizeof(struct input_event_compat))) if (copy_from_user(&compat_event, buffer,
sizeof(struct input_event_compat)))
return -EFAULT; return -EFAULT;
event->time.tv_sec = compat_event.time.tv_sec; event->time.tv_sec = compat_event.time.tv_sec;
...@@ -219,7 +343,8 @@ static int evdev_event_from_user(const char __user *buffer, struct input_event * ...@@ -219,7 +343,8 @@ static int evdev_event_from_user(const char __user *buffer, struct input_event *
return 0; return 0;
} }
static int evdev_event_to_user(char __user *buffer, const struct input_event *event) static int evdev_event_to_user(char __user *buffer,
const struct input_event *event)
{ {
if (COMPAT_TEST) { if (COMPAT_TEST) {
struct input_event_compat compat_event; struct input_event_compat compat_event;
...@@ -230,7 +355,8 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev ...@@ -230,7 +355,8 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev
compat_event.code = event->code; compat_event.code = event->code;
compat_event.value = event->value; compat_event.value = event->value;
if (copy_to_user(buffer, &compat_event, sizeof(struct input_event_compat))) if (copy_to_user(buffer, &compat_event,
sizeof(struct input_event_compat)))
return -EFAULT; return -EFAULT;
} else { } else {
...@@ -248,7 +374,8 @@ static inline size_t evdev_event_size(void) ...@@ -248,7 +374,8 @@ static inline size_t evdev_event_size(void)
return sizeof(struct input_event); return sizeof(struct input_event);
} }
static int evdev_event_from_user(const char __user *buffer, struct input_event *event) static int evdev_event_from_user(const char __user *buffer,
struct input_event *event)
{ {
if (copy_from_user(event, buffer, sizeof(struct input_event))) if (copy_from_user(event, buffer, sizeof(struct input_event)))
return -EFAULT; return -EFAULT;
...@@ -256,7 +383,8 @@ static int evdev_event_from_user(const char __user *buffer, struct input_event * ...@@ -256,7 +383,8 @@ static int evdev_event_from_user(const char __user *buffer, struct input_event *
return 0; return 0;
} }
static int evdev_event_to_user(char __user *buffer, const struct input_event *event) static int evdev_event_to_user(char __user *buffer,
const struct input_event *event)
{ {
if (copy_to_user(buffer, event, sizeof(struct input_event))) if (copy_to_user(buffer, event, sizeof(struct input_event)))
return -EFAULT; return -EFAULT;
...@@ -266,37 +394,71 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev ...@@ -266,37 +394,71 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev
#endif /* CONFIG_COMPAT */ #endif /* CONFIG_COMPAT */
static ssize_t evdev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) static ssize_t evdev_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{ {
struct evdev_client *client = file->private_data; struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev; struct evdev *evdev = client->evdev;
struct input_event event; struct input_event event;
int retval = 0; int retval;
if (!evdev->exist) retval = mutex_lock_interruptible(&evdev->mutex);
return -ENODEV; if (retval)
return retval;
if (!evdev->exist) {
retval = -ENODEV;
goto out;
}
while (retval < count) { while (retval < count) {
if (evdev_event_from_user(buffer + retval, &event)) if (evdev_event_from_user(buffer + retval, &event)) {
return -EFAULT; retval = -EFAULT;
input_inject_event(&evdev->handle, event.type, event.code, event.value); goto out;
}
input_inject_event(&evdev->handle,
event.type, event.code, event.value);
retval += evdev_event_size(); retval += evdev_event_size();
} }
out:
mutex_unlock(&evdev->mutex);
return retval; return retval;
} }
static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) static int evdev_fetch_next_event(struct evdev_client *client,
struct input_event *event)
{
int have_event;
spin_lock_irq(&client->buffer_lock);
have_event = client->head != client->tail;
if (have_event) {
*event = client->buffer[client->tail++];
client->tail &= EVDEV_BUFFER_SIZE - 1;
}
spin_unlock_irq(&client->buffer_lock);
return have_event;
}
static ssize_t evdev_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{ {
struct evdev_client *client = file->private_data; struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev; struct evdev *evdev = client->evdev;
struct input_event event;
int retval; int retval;
if (count < evdev_event_size()) if (count < evdev_event_size())
return -EINVAL; return -EINVAL;
if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK)) if (client->head == client->tail && evdev->exist &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN; return -EAGAIN;
retval = wait_event_interruptible(evdev->wait, retval = wait_event_interruptible(evdev->wait,
...@@ -307,14 +469,12 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, ...@@ -307,14 +469,12 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count,
if (!evdev->exist) if (!evdev->exist)
return -ENODEV; return -ENODEV;
while (client->head != client->tail && retval + evdev_event_size() <= count) { while (retval + evdev_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {
struct input_event *event = (struct input_event *) client->buffer + client->tail;
if (evdev_event_to_user(buffer + retval, event)) if (evdev_event_to_user(buffer + retval, &event))
return -EFAULT; return -EFAULT;
client->tail = (client->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
retval += evdev_event_size(); retval += evdev_event_size();
} }
...@@ -409,8 +569,8 @@ static int str_to_user(const char *str, unsigned int maxlen, void __user *p) ...@@ -409,8 +569,8 @@ static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
return copy_to_user(p, str, len) ? -EFAULT : len; return copy_to_user(p, str, len) ? -EFAULT : len;
} }
static long evdev_ioctl_handler(struct file *file, unsigned int cmd, static long evdev_do_ioctl(struct file *file, unsigned int cmd,
void __user *p, int compat_mode) void __user *p, int compat_mode)
{ {
struct evdev_client *client = file->private_data; struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev; struct evdev *evdev = client->evdev;
...@@ -421,215 +581,289 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd, ...@@ -421,215 +581,289 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
int i, t, u, v; int i, t, u, v;
int error; int error;
if (!evdev->exist)
return -ENODEV;
switch (cmd) { switch (cmd) {
case EVIOCGVERSION: case EVIOCGVERSION:
return put_user(EV_VERSION, ip); return put_user(EV_VERSION, ip);
case EVIOCGID: case EVIOCGID:
if (copy_to_user(p, &dev->id, sizeof(struct input_id))) if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
return -EFAULT; return -EFAULT;
return 0; return 0;
case EVIOCGREP: case EVIOCGREP:
if (!test_bit(EV_REP, dev->evbit)) if (!test_bit(EV_REP, dev->evbit))
return -ENOSYS; return -ENOSYS;
if (put_user(dev->rep[REP_DELAY], ip)) if (put_user(dev->rep[REP_DELAY], ip))
return -EFAULT; return -EFAULT;
if (put_user(dev->rep[REP_PERIOD], ip + 1)) if (put_user(dev->rep[REP_PERIOD], ip + 1))
return -EFAULT; return -EFAULT;
return 0; return 0;
case EVIOCSREP: case EVIOCSREP:
if (!test_bit(EV_REP, dev->evbit)) if (!test_bit(EV_REP, dev->evbit))
return -ENOSYS; return -ENOSYS;
if (get_user(u, ip)) if (get_user(u, ip))
return -EFAULT; return -EFAULT;
if (get_user(v, ip + 1)) if (get_user(v, ip + 1))
return -EFAULT; return -EFAULT;
input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u); input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u);
input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v); input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v);
return 0; return 0;
case EVIOCGKEYCODE: case EVIOCGKEYCODE:
if (get_user(t, ip)) if (get_user(t, ip))
return -EFAULT; return -EFAULT;
error = dev->getkeycode(dev, t, &v); error = dev->getkeycode(dev, t, &v);
if (error) if (error)
return error; return error;
if (put_user(v, ip + 1)) if (put_user(v, ip + 1))
return -EFAULT; return -EFAULT;
return 0; return 0;
case EVIOCSKEYCODE: case EVIOCSKEYCODE:
if (get_user(t, ip) || get_user(v, ip + 1)) if (get_user(t, ip) || get_user(v, ip + 1))
return -EFAULT; return -EFAULT;
return dev->setkeycode(dev, t, v); return dev->setkeycode(dev, t, v);
case EVIOCSFF: case EVIOCSFF:
if (copy_from_user(&effect, p, sizeof(effect))) if (copy_from_user(&effect, p, sizeof(effect)))
return -EFAULT; return -EFAULT;
error = input_ff_upload(dev, &effect, file); error = input_ff_upload(dev, &effect, file);
if (put_user(effect.id, &(((struct ff_effect __user *)p)->id))) if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
return -EFAULT; return -EFAULT;
return error; return error;
case EVIOCRMFF: case EVIOCRMFF:
return input_ff_erase(dev, (int)(unsigned long) p, file); return input_ff_erase(dev, (int)(unsigned long) p, file);
case EVIOCGEFFECTS: case EVIOCGEFFECTS:
i = test_bit(EV_FF, dev->evbit) ? dev->ff->max_effects : 0; i = test_bit(EV_FF, dev->evbit) ?
if (put_user(i, ip)) dev->ff->max_effects : 0;
return -EFAULT; if (put_user(i, ip))
return 0; return -EFAULT;
return 0;
case EVIOCGRAB:
if (p) { case EVIOCGRAB:
if (evdev->grab) if (p)
return -EBUSY; return evdev_grab(evdev, client);
if (input_grab_device(&evdev->handle)) else
return -EBUSY; return evdev_ungrab(evdev, client);
evdev->grab = client;
return 0;
} else {
if (evdev->grab != client)
return -EINVAL;
input_release_device(&evdev->handle);
evdev->grab = NULL;
return 0;
}
default: default:
if (_IOC_TYPE(cmd) != 'E') if (_IOC_TYPE(cmd) != 'E')
return -EINVAL; return -EINVAL;
if (_IOC_DIR(cmd) == _IOC_READ) { if (_IOC_DIR(cmd) == _IOC_READ) {
if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) { if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0))) {
unsigned long *bits; unsigned long *bits;
int len; int len;
switch (_IOC_NR(cmd) & EV_MAX) { switch (_IOC_NR(cmd) & EV_MAX) {
case 0: bits = dev->evbit; len = EV_MAX; break;
case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
case EV_REL: bits = dev->relbit; len = REL_MAX; break;
case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break;
case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
case EV_FF: bits = dev->ffbit; len = FF_MAX; break;
case EV_SW: bits = dev->swbit; len = SW_MAX; break;
default: return -EINVAL;
}
return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode);
}
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0))) case 0: bits = dev->evbit; len = EV_MAX; break;
return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd), case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
p, compat_mode); case EV_REL: bits = dev->relbit; len = REL_MAX; break;
case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break;
case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
case EV_FF: bits = dev->ffbit; len = FF_MAX; break;
case EV_SW: bits = dev->swbit; len = SW_MAX; break;
default: return -EINVAL;
}
return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode);
}
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0))) if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0)))
return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd), return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd),
p, compat_mode); p, compat_mode);
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0))) if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0)))
return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd), return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd),
p, compat_mode); p, compat_mode);
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0))) if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd), return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd),
p, compat_mode); p, compat_mode);
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
return str_to_user(dev->name, _IOC_SIZE(cmd), p); return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd),
p, compat_mode);
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0))) if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0)))
return str_to_user(dev->phys, _IOC_SIZE(cmd), p); return str_to_user(dev->name, _IOC_SIZE(cmd), p);
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0))) if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
return str_to_user(dev->uniq, _IOC_SIZE(cmd), p); return str_to_user(dev->phys, _IOC_SIZE(cmd), p);
if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0)))
return str_to_user(dev->uniq, _IOC_SIZE(cmd), p);
t = _IOC_NR(cmd) & ABS_MAX; if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
abs.value = dev->abs[t]; t = _IOC_NR(cmd) & ABS_MAX;
abs.minimum = dev->absmin[t];
abs.maximum = dev->absmax[t];
abs.fuzz = dev->absfuzz[t];
abs.flat = dev->absflat[t];
if (copy_to_user(p, &abs, sizeof(struct input_absinfo))) abs.value = dev->abs[t];
return -EFAULT; abs.minimum = dev->absmin[t];
abs.maximum = dev->absmax[t];
abs.fuzz = dev->absfuzz[t];
abs.flat = dev->absflat[t];
return 0; if (copy_to_user(p, &abs, sizeof(struct input_absinfo)))
} return -EFAULT;
return 0;
} }
if (_IOC_DIR(cmd) == _IOC_WRITE) { }
if (_IOC_DIR(cmd) == _IOC_WRITE) {
if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) { if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
t = _IOC_NR(cmd) & ABS_MAX; t = _IOC_NR(cmd) & ABS_MAX;
if (copy_from_user(&abs, p, sizeof(struct input_absinfo))) if (copy_from_user(&abs, p,
return -EFAULT; sizeof(struct input_absinfo)))
return -EFAULT;
dev->abs[t] = abs.value; /*
dev->absmin[t] = abs.minimum; * Take event lock to ensure that we are not
dev->absmax[t] = abs.maximum; * changing device parameters in the middle
dev->absfuzz[t] = abs.fuzz; * of event.
dev->absflat[t] = abs.flat; */
spin_lock_irq(&dev->event_lock);
return 0; dev->abs[t] = abs.value;
} dev->absmin[t] = abs.minimum;
dev->absmax[t] = abs.maximum;
dev->absfuzz[t] = abs.fuzz;
dev->absflat[t] = abs.flat;
spin_unlock_irq(&dev->event_lock);
return 0;
} }
}
} }
return -EINVAL; return -EINVAL;
} }
static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
void __user *p, int compat_mode)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
int retval;
retval = mutex_lock_interruptible(&evdev->mutex);
if (retval)
return retval;
if (!evdev->exist) {
retval = -ENODEV;
goto out;
}
retval = evdev_do_ioctl(file, cmd, p, compat_mode);
out:
mutex_unlock(&evdev->mutex);
return retval;
}
static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{ {
return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0); return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);
} }
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) static long evdev_ioctl_compat(struct file *file,
unsigned int cmd, unsigned long arg)
{ {
return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1); return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1);
} }
#endif #endif
static const struct file_operations evdev_fops = { static const struct file_operations evdev_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.read = evdev_read, .read = evdev_read,
.write = evdev_write, .write = evdev_write,
.poll = evdev_poll, .poll = evdev_poll,
.open = evdev_open, .open = evdev_open,
.release = evdev_release, .release = evdev_release,
.unlocked_ioctl = evdev_ioctl, .unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
.compat_ioctl = evdev_ioctl_compat, .compat_ioctl = evdev_ioctl_compat,
#endif #endif
.fasync = evdev_fasync, .fasync = evdev_fasync,
.flush = evdev_flush .flush = evdev_flush
}; };
static int evdev_install_chrdev(struct evdev *evdev)
{
/*
* No need to do any locking here as calls to connect and
* disconnect are serialized by the input core
*/
evdev_table[evdev->minor] = evdev;
return 0;
}
static void evdev_remove_chrdev(struct evdev *evdev)
{
/*
* Lock evdev table to prevent race with evdev_open()
*/
mutex_lock(&evdev_table_mutex);
evdev_table[evdev->minor] = NULL;
mutex_unlock(&evdev_table_mutex);
}
/*
* Mark device non-existent. This disables writes, ioctls and
* prevents new users from opening the device. Already posted
* blocking reads will stay, however new ones will fail.
*/
static void evdev_mark_dead(struct evdev *evdev)
{
mutex_lock(&evdev->mutex);
evdev->exist = 0;
mutex_unlock(&evdev->mutex);
}
static void evdev_cleanup(struct evdev *evdev)
{
struct input_handle *handle = &evdev->handle;
evdev_mark_dead(evdev);
evdev_hangup(evdev);
evdev_remove_chrdev(evdev);
/* evdev is marked dead so no one else accesses evdev->open */
if (evdev->open) {
input_flush_device(handle, NULL);
input_close_device(handle);
}
}
/*
* Create new evdev device. Note that input core serializes calls
* to connect and disconnect so we don't need to lock evdev_table here.
*/
static int evdev_connect(struct input_handler *handler, struct input_dev *dev, static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id) const struct input_device_id *id)
{ {
...@@ -637,7 +871,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, ...@@ -637,7 +871,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
int minor; int minor;
int error; int error;
for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++); for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!evdev_table[minor])
break;
if (minor == EVDEV_MINORS) { if (minor == EVDEV_MINORS) {
printk(KERN_ERR "evdev: no more free evdev devices\n"); printk(KERN_ERR "evdev: no more free evdev devices\n");
return -ENFILE; return -ENFILE;
...@@ -648,38 +885,44 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, ...@@ -648,38 +885,44 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
return -ENOMEM; return -ENOMEM;
INIT_LIST_HEAD(&evdev->client_list); INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait); init_waitqueue_head(&evdev->wait);
snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
evdev->exist = 1; evdev->exist = 1;
evdev->minor = minor; evdev->minor = minor;
evdev->handle.dev = dev; evdev->handle.dev = dev;
evdev->handle.name = evdev->name; evdev->handle.name = evdev->name;
evdev->handle.handler = handler; evdev->handle.handler = handler;
evdev->handle.private = evdev; evdev->handle.private = evdev;
snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
snprintf(evdev->dev.bus_id, sizeof(evdev->dev.bus_id), strlcpy(evdev->dev.bus_id, evdev->name, sizeof(evdev->dev.bus_id));
"event%d", minor); evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
evdev->dev.class = &input_class; evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev; evdev->dev.parent = &dev->dev;
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
evdev->dev.release = evdev_free; evdev->dev.release = evdev_free;
device_initialize(&evdev->dev); device_initialize(&evdev->dev);
evdev_table[minor] = evdev; error = input_register_handle(&evdev->handle);
error = device_add(&evdev->dev);
if (error) if (error)
goto err_free_evdev; goto err_free_evdev;
error = input_register_handle(&evdev->handle); error = evdev_install_chrdev(evdev);
if (error)
goto err_unregister_handle;
error = device_add(&evdev->dev);
if (error) if (error)
goto err_delete_evdev; goto err_cleanup_evdev;
return 0; return 0;
err_delete_evdev: err_cleanup_evdev:
device_del(&evdev->dev); evdev_cleanup(evdev);
err_unregister_handle:
input_unregister_handle(&evdev->handle);
err_free_evdev: err_free_evdev:
put_device(&evdev->dev); put_device(&evdev->dev);
return error; return error;
...@@ -688,21 +931,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, ...@@ -688,21 +931,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
static void evdev_disconnect(struct input_handle *handle) static void evdev_disconnect(struct input_handle *handle)
{ {
struct evdev *evdev = handle->private; struct evdev *evdev = handle->private;
struct evdev_client *client;
input_unregister_handle(handle);
device_del(&evdev->dev); device_del(&evdev->dev);
evdev_cleanup(evdev);
evdev->exist = 0; input_unregister_handle(handle);
if (evdev->open) {
input_flush_device(handle, NULL);
input_close_device(handle);
list_for_each_entry(client, &evdev->client_list, node)
kill_fasync(&client->fasync, SIGIO, POLL_HUP);
wake_up_interruptible(&evdev->wait);
}
put_device(&evdev->dev); put_device(&evdev->dev);
} }
...@@ -714,13 +946,13 @@ static const struct input_device_id evdev_ids[] = { ...@@ -714,13 +946,13 @@ static const struct input_device_id evdev_ids[] = {
MODULE_DEVICE_TABLE(input, evdev_ids); MODULE_DEVICE_TABLE(input, evdev_ids);
static struct input_handler evdev_handler = { static struct input_handler evdev_handler = {
.event = evdev_event, .event = evdev_event,
.connect = evdev_connect, .connect = evdev_connect,
.disconnect = evdev_disconnect, .disconnect = evdev_disconnect,
.fops = &evdev_fops, .fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE, .minor = EVDEV_MINOR_BASE,
.name = "evdev", .name = "evdev",
.id_table = evdev_ids, .id_table = evdev_ids,
}; };
static int __init evdev_init(void) static int __init evdev_init(void)
......
...@@ -70,6 +70,7 @@ static int input_open_polled_device(struct input_dev *input) ...@@ -70,6 +70,7 @@ static int input_open_polled_device(struct input_dev *input)
{ {
struct input_polled_dev *dev = input->private; struct input_polled_dev *dev = input->private;
int error; int error;
unsigned long ticks;
error = input_polldev_start_workqueue(); error = input_polldev_start_workqueue();
if (error) if (error)
...@@ -78,8 +79,10 @@ static int input_open_polled_device(struct input_dev *input) ...@@ -78,8 +79,10 @@ static int input_open_polled_device(struct input_dev *input)
if (dev->flush) if (dev->flush)
dev->flush(dev); dev->flush(dev);
queue_delayed_work(polldev_wq, &dev->work, ticks = msecs_to_jiffies(dev->poll_interval);
msecs_to_jiffies(dev->poll_interval)); if (ticks >= HZ)
ticks = round_jiffies(ticks);
queue_delayed_work(polldev_wq, &dev->work, ticks);
return 0; return 0;
} }
......
...@@ -17,10 +17,10 @@ ...@@ -17,10 +17,10 @@
#include <linux/major.h> #include <linux/major.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/interrupt.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/rcupdate.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("Input core"); MODULE_DESCRIPTION("Input core");
...@@ -31,167 +31,245 @@ MODULE_LICENSE("GPL"); ...@@ -31,167 +31,245 @@ MODULE_LICENSE("GPL");
static LIST_HEAD(input_dev_list); static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list); static LIST_HEAD(input_handler_list);
/*
* input_mutex protects access to both input_dev_list and input_handler_list.
* This also causes input_[un]register_device and input_[un]register_handler
* be mutually exclusive which simplifies locking in drivers implementing
* input handlers.
*/
static DEFINE_MUTEX(input_mutex);
static struct input_handler *input_table[8]; static struct input_handler *input_table[8];
/** static inline int is_event_supported(unsigned int code,
* input_event() - report new input event unsigned long *bm, unsigned int max)
* @dev: device that generated the event
* @type: type of the event
* @code: event code
* @value: value of the event
*
* This function should be used by drivers implementing various input devices
* See also input_inject_event()
*/
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{ {
struct input_handle *handle; return code <= max && test_bit(code, bm);
}
if (type > EV_MAX || !test_bit(type, dev->evbit)) static int input_defuzz_abs_event(int value, int old_val, int fuzz)
return; {
if (fuzz) {
if (value > old_val - fuzz / 2 && value < old_val + fuzz / 2)
return old_val;
add_input_randomness(type, code, value); if (value > old_val - fuzz && value < old_val + fuzz)
return (old_val * 3 + value) / 4;
switch (type) { if (value > old_val - fuzz * 2 && value < old_val + fuzz * 2)
return (old_val + value) / 2;
case EV_SYN: }
switch (code) {
case SYN_CONFIG:
if (dev->event)
dev->event(dev, type, code, value);
break;
case SYN_REPORT:
if (dev->sync)
return;
dev->sync = 1;
break;
}
break;
case EV_KEY: return value;
}
if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value) /*
return; * Pass event through all open handles. This function is called with
* dev->event_lock held and interrupts disabled.
*/
static void input_pass_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
struct input_handle *handle;
if (value == 2) rcu_read_lock();
break;
change_bit(code, dev->key); handle = rcu_dereference(dev->grab);
if (handle)
handle->handler->event(handle, type, code, value);
else
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle,
type, code, value);
rcu_read_unlock();
}
if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) { /*
dev->repeat_key = code; * Generate software autorepeat event. Note that we take
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY])); * dev->event_lock here to avoid racing with input_event
} * which may cause keys get "stuck".
*/
static void input_repeat_key(unsigned long data)
{
struct input_dev *dev = (void *) data;
unsigned long flags;
break; spin_lock_irqsave(&dev->event_lock, flags);
case EV_SW: if (test_bit(dev->repeat_key, dev->key) &&
is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {
if (code > SW_MAX || !test_bit(code, dev->swbit) || !!test_bit(code, dev->sw) == value) input_pass_event(dev, EV_KEY, dev->repeat_key, 2);
return;
change_bit(code, dev->sw); if (dev->sync) {
/*
* Only send SYN_REPORT if we are not in a middle
* of driver parsing a new hardware packet.
* Otherwise assume that the driver will send
* SYN_REPORT once it's done.
*/
input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
}
break; if (dev->rep[REP_PERIOD])
mod_timer(&dev->timer, jiffies +
msecs_to_jiffies(dev->rep[REP_PERIOD]));
}
case EV_ABS: spin_unlock_irqrestore(&dev->event_lock, flags);
}
if (code > ABS_MAX || !test_bit(code, dev->absbit)) static void input_start_autorepeat(struct input_dev *dev, int code)
return; {
if (test_bit(EV_REP, dev->evbit) &&
dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&
dev->timer.data) {
dev->repeat_key = code;
mod_timer(&dev->timer,
jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
}
}
if (dev->absfuzz[code]) { #define INPUT_IGNORE_EVENT 0
if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) && #define INPUT_PASS_TO_HANDLERS 1
(value < dev->abs[code] + (dev->absfuzz[code] >> 1))) #define INPUT_PASS_TO_DEVICE 2
return; #define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
if ((value > dev->abs[code] - dev->absfuzz[code]) && static void input_handle_event(struct input_dev *dev,
(value < dev->abs[code] + dev->absfuzz[code])) unsigned int type, unsigned int code, int value)
value = (dev->abs[code] * 3 + value) >> 2; {
int disposition = INPUT_IGNORE_EVENT;
if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) && switch (type) {
(value < dev->abs[code] + (dev->absfuzz[code] << 1)))
value = (dev->abs[code] + value) >> 1;
}
if (dev->abs[code] == value) case EV_SYN:
return; switch (code) {
case SYN_CONFIG:
disposition = INPUT_PASS_TO_ALL;
break;
dev->abs[code] = value; case SYN_REPORT:
if (!dev->sync) {
dev->sync = 1;
disposition = INPUT_PASS_TO_HANDLERS;
}
break; break;
}
break;
case EV_REL: case EV_KEY:
if (is_event_supported(code, dev->keybit, KEY_MAX) &&
!!test_bit(code, dev->key) != value) {
if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0)) if (value != 2) {
return; __change_bit(code, dev->key);
if (value)
input_start_autorepeat(dev, code);
}
break; disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_MSC: case EV_SW:
if (is_event_supported(code, dev->swbit, SW_MAX) &&
!!test_bit(code, dev->sw) != value) {
if (code > MSC_MAX || !test_bit(code, dev->mscbit)) __change_bit(code, dev->sw);
return; disposition = INPUT_PASS_TO_HANDLERS;
}
break;
if (dev->event) case EV_ABS:
dev->event(dev, type, code, value); if (is_event_supported(code, dev->absbit, ABS_MAX)) {
break; value = input_defuzz_abs_event(value,
dev->abs[code], dev->absfuzz[code]);
if (dev->abs[code] != value) {
dev->abs[code] = value;
disposition = INPUT_PASS_TO_HANDLERS;
}
}
break;
case EV_LED: case EV_REL:
if (is_event_supported(code, dev->relbit, REL_MAX) && value)
disposition = INPUT_PASS_TO_HANDLERS;
if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value) break;
return;
change_bit(code, dev->led); case EV_MSC:
if (is_event_supported(code, dev->mscbit, MSC_MAX))
disposition = INPUT_PASS_TO_ALL;
if (dev->event) break;
dev->event(dev, type, code, value);
break; case EV_LED:
if (is_event_supported(code, dev->ledbit, LED_MAX) &&
!!test_bit(code, dev->led) != value) {
case EV_SND: __change_bit(code, dev->led);
disposition = INPUT_PASS_TO_ALL;
}
break;
if (code > SND_MAX || !test_bit(code, dev->sndbit)) case EV_SND:
return; if (is_event_supported(code, dev->sndbit, SND_MAX)) {
if (!!test_bit(code, dev->snd) != !!value) if (!!test_bit(code, dev->snd) != !!value)
change_bit(code, dev->snd); __change_bit(code, dev->snd);
disposition = INPUT_PASS_TO_ALL;
}
break;
if (dev->event) case EV_REP:
dev->event(dev, type, code, value); if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
dev->rep[code] = value;
disposition = INPUT_PASS_TO_ALL;
}
break;
break; case EV_FF:
if (value >= 0)
disposition = INPUT_PASS_TO_ALL;
break;
}
case EV_REP: if (type != EV_SYN)
dev->sync = 0;
if (code > REP_MAX || value < 0 || dev->rep[code] == value) if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
return; dev->event(dev, type, code, value);
dev->rep[code] = value; if (disposition & INPUT_PASS_TO_HANDLERS)
if (dev->event) input_pass_event(dev, type, code, value);
dev->event(dev, type, code, value); }
break; /**
* input_event() - report new input event
* @dev: device that generated the event
* @type: type of the event
* @code: event code
* @value: value of the event
*
* This function should be used by drivers implementing various input
* devices. See also input_inject_event().
*/
case EV_FF: void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
unsigned long flags;
if (value < 0) if (is_event_supported(type, dev->evbit, EV_MAX)) {
return;
if (dev->event) spin_lock_irqsave(&dev->event_lock, flags);
dev->event(dev, type, code, value); add_input_randomness(type, code, value);
break; input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
} }
if (type != EV_SYN)
dev->sync = 0;
if (dev->grab)
dev->grab->handler->event(dev->grab, type, code, value);
else
list_for_each_entry(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle, type, code, value);
} }
EXPORT_SYMBOL(input_event); EXPORT_SYMBOL(input_event);
...@@ -202,102 +280,228 @@ EXPORT_SYMBOL(input_event); ...@@ -202,102 +280,228 @@ EXPORT_SYMBOL(input_event);
* @code: event code * @code: event code
* @value: value of the event * @value: value of the event
* *
* Similar to input_event() but will ignore event if device is "grabbed" and handle * Similar to input_event() but will ignore event if device is
* injecting event is not the one that owns the device. * "grabbed" and handle injecting event is not the one that owns
* the device.
*/ */
void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) void input_inject_event(struct input_handle *handle,
{ unsigned int type, unsigned int code, int value)
if (!handle->dev->grab || handle->dev->grab == handle)
input_event(handle->dev, type, code, value);
}
EXPORT_SYMBOL(input_inject_event);
static void input_repeat_key(unsigned long data)
{ {
struct input_dev *dev = (void *) data; struct input_dev *dev = handle->dev;
struct input_handle *grab;
unsigned long flags;
if (!test_bit(dev->repeat_key, dev->key)) if (is_event_supported(type, dev->evbit, EV_MAX)) {
return; spin_lock_irqsave(&dev->event_lock, flags);
input_event(dev, EV_KEY, dev->repeat_key, 2); rcu_read_lock();
input_sync(dev); grab = rcu_dereference(dev->grab);
if (!grab || grab == handle)
input_handle_event(dev, type, code, value);
rcu_read_unlock();
if (dev->rep[REP_PERIOD]) spin_unlock_irqrestore(&dev->event_lock, flags);
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD])); }
} }
EXPORT_SYMBOL(input_inject_event);
/**
* input_grab_device - grabs device for exclusive use
* @handle: input handle that wants to own the device
*
* When a device is grabbed by an input handle all events generated by
* the device are delivered only to this handle. Also events injected
* by other input handles are ignored while device is grabbed.
*/
int input_grab_device(struct input_handle *handle) int input_grab_device(struct input_handle *handle)
{ {
if (handle->dev->grab) struct input_dev *dev = handle->dev;
return -EBUSY; int retval;
handle->dev->grab = handle; retval = mutex_lock_interruptible(&dev->mutex);
return 0; if (retval)
return retval;
if (dev->grab) {
retval = -EBUSY;
goto out;
}
rcu_assign_pointer(dev->grab, handle);
synchronize_rcu();
out:
mutex_unlock(&dev->mutex);
return retval;
} }
EXPORT_SYMBOL(input_grab_device); EXPORT_SYMBOL(input_grab_device);
void input_release_device(struct input_handle *handle) static void __input_release_device(struct input_handle *handle)
{ {
struct input_dev *dev = handle->dev; struct input_dev *dev = handle->dev;
if (dev->grab == handle) { if (dev->grab == handle) {
dev->grab = NULL; rcu_assign_pointer(dev->grab, NULL);
/* Make sure input_pass_event() notices that grab is gone */
synchronize_rcu();
list_for_each_entry(handle, &dev->h_list, d_node) list_for_each_entry(handle, &dev->h_list, d_node)
if (handle->handler->start) if (handle->open && handle->handler->start)
handle->handler->start(handle); handle->handler->start(handle);
} }
} }
/**
* input_release_device - release previously grabbed device
* @handle: input handle that owns the device
*
* Releases previously grabbed device so that other input handles can
* start receiving input events. Upon release all handlers attached
* to the device have their start() method called so they have a change
* to synchronize device state with the rest of the system.
*/
void input_release_device(struct input_handle *handle)
{
struct input_dev *dev = handle->dev;
mutex_lock(&dev->mutex);
__input_release_device(handle);
mutex_unlock(&dev->mutex);
}
EXPORT_SYMBOL(input_release_device); EXPORT_SYMBOL(input_release_device);
/**
* input_open_device - open input device
* @handle: handle through which device is being accessed
*
* This function should be called by input handlers when they
* want to start receive events from given input device.
*/
int input_open_device(struct input_handle *handle) int input_open_device(struct input_handle *handle)
{ {
struct input_dev *dev = handle->dev; struct input_dev *dev = handle->dev;
int err; int retval;
err = mutex_lock_interruptible(&dev->mutex); retval = mutex_lock_interruptible(&dev->mutex);
if (err) if (retval)
return err; return retval;
if (dev->going_away) {
retval = -ENODEV;
goto out;
}
handle->open++; handle->open++;
if (!dev->users++ && dev->open) if (!dev->users++ && dev->open)
err = dev->open(dev); retval = dev->open(dev);
if (err) if (retval) {
handle->open--; dev->users--;
if (!--handle->open) {
/*
* Make sure we are not delivering any more events
* through this handle
*/
synchronize_rcu();
}
}
out:
mutex_unlock(&dev->mutex); mutex_unlock(&dev->mutex);
return retval;
return err;
} }
EXPORT_SYMBOL(input_open_device); EXPORT_SYMBOL(input_open_device);
int input_flush_device(struct input_handle* handle, struct file* file) int input_flush_device(struct input_handle *handle, struct file *file)
{ {
if (handle->dev->flush) struct input_dev *dev = handle->dev;
return handle->dev->flush(handle->dev, file); int retval;
return 0; retval = mutex_lock_interruptible(&dev->mutex);
if (retval)
return retval;
if (dev->flush)
retval = dev->flush(dev, file);
mutex_unlock(&dev->mutex);
return retval;
} }
EXPORT_SYMBOL(input_flush_device); EXPORT_SYMBOL(input_flush_device);
/**
* input_close_device - close input device
* @handle: handle through which device is being accessed
*
* This function should be called by input handlers when they
* want to stop receive events from given input device.
*/
void input_close_device(struct input_handle *handle) void input_close_device(struct input_handle *handle)
{ {
struct input_dev *dev = handle->dev; struct input_dev *dev = handle->dev;
input_release_device(handle);
mutex_lock(&dev->mutex); mutex_lock(&dev->mutex);
__input_release_device(handle);
if (!--dev->users && dev->close) if (!--dev->users && dev->close)
dev->close(dev); dev->close(dev);
handle->open--;
if (!--handle->open) {
/*
* synchronize_rcu() makes sure that input_pass_event()
* completed and that no more input events are delivered
* through this handle
*/
synchronize_rcu();
}
mutex_unlock(&dev->mutex); mutex_unlock(&dev->mutex);
} }
EXPORT_SYMBOL(input_close_device); EXPORT_SYMBOL(input_close_device);
/*
* Prepare device for unregistering
*/
static void input_disconnect_device(struct input_dev *dev)
{
struct input_handle *handle;
int code;
/*
* Mark device as going away. Note that we take dev->mutex here
* not to protect access to dev->going_away but rather to ensure
* that there are no threads in the middle of input_open_device()
*/
mutex_lock(&dev->mutex);
dev->going_away = 1;
mutex_unlock(&dev->mutex);
spin_lock_irq(&dev->event_lock);
/*
* Simulate keyup events for all pressed keys so that handlers
* are not left with "stuck" keys. The driver may continue
* generate events even after we done here but they will not
* reach any handlers.
*/
if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) {
for (code = 0; code <= KEY_MAX; code++) {
if (is_event_supported(code, dev->keybit, KEY_MAX) &&
test_bit(code, dev->key)) {
input_pass_event(dev, EV_KEY, code, 0);
}
}
input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
}
list_for_each_entry(handle, &dev->h_list, d_node)
handle->open = 0;
spin_unlock_irq(&dev->event_lock);
}
static int input_fetch_keycode(struct input_dev *dev, int scancode) static int input_fetch_keycode(struct input_dev *dev, int scancode)
{ {
switch (dev->keycodesize) { switch (dev->keycodesize) {
...@@ -473,7 +677,8 @@ static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait) ...@@ -473,7 +677,8 @@ static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait)
static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos) static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos)
{ {
/* acquire lock here ... Yes, we do need locking, I knowi, I know... */ if (mutex_lock_interruptible(&input_mutex))
return NULL;
return seq_list_start(&input_dev_list, *pos); return seq_list_start(&input_dev_list, *pos);
} }
...@@ -485,7 +690,7 @@ static void *input_devices_seq_next(struct seq_file *seq, void *v, loff_t *pos) ...@@ -485,7 +690,7 @@ static void *input_devices_seq_next(struct seq_file *seq, void *v, loff_t *pos)
static void input_devices_seq_stop(struct seq_file *seq, void *v) static void input_devices_seq_stop(struct seq_file *seq, void *v)
{ {
/* release lock here */ mutex_unlock(&input_mutex);
} }
static void input_seq_print_bitmap(struct seq_file *seq, const char *name, static void input_seq_print_bitmap(struct seq_file *seq, const char *name,
...@@ -569,7 +774,9 @@ static const struct file_operations input_devices_fileops = { ...@@ -569,7 +774,9 @@ static const struct file_operations input_devices_fileops = {
static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos) static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos)
{ {
/* acquire lock here ... Yes, we do need locking, I knowi, I know... */ if (mutex_lock_interruptible(&input_mutex))
return NULL;
seq->private = (void *)(unsigned long)*pos; seq->private = (void *)(unsigned long)*pos;
return seq_list_start(&input_handler_list, *pos); return seq_list_start(&input_handler_list, *pos);
} }
...@@ -582,7 +789,7 @@ static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos) ...@@ -582,7 +789,7 @@ static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos)
static void input_handlers_seq_stop(struct seq_file *seq, void *v) static void input_handlers_seq_stop(struct seq_file *seq, void *v)
{ {
/* release lock here */ mutex_unlock(&input_mutex);
} }
static int input_handlers_seq_show(struct seq_file *seq, void *v) static int input_handlers_seq_show(struct seq_file *seq, void *v)
...@@ -983,6 +1190,7 @@ struct input_dev *input_allocate_device(void) ...@@ -983,6 +1190,7 @@ struct input_dev *input_allocate_device(void)
dev->dev.class = &input_class; dev->dev.class = &input_class;
device_initialize(&dev->dev); device_initialize(&dev->dev);
mutex_init(&dev->mutex); mutex_init(&dev->mutex);
spin_lock_init(&dev->event_lock);
INIT_LIST_HEAD(&dev->h_list); INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node); INIT_LIST_HEAD(&dev->node);
...@@ -1000,7 +1208,7 @@ EXPORT_SYMBOL(input_allocate_device); ...@@ -1000,7 +1208,7 @@ EXPORT_SYMBOL(input_allocate_device);
* This function should only be used if input_register_device() * This function should only be used if input_register_device()
* was not called yet or if it failed. Once device was registered * was not called yet or if it failed. Once device was registered
* use input_unregister_device() and memory will be freed once last * use input_unregister_device() and memory will be freed once last
* refrence to the device is dropped. * reference to the device is dropped.
* *
* Device should be allocated by input_allocate_device(). * Device should be allocated by input_allocate_device().
* *
...@@ -1070,6 +1278,18 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int ...@@ -1070,6 +1278,18 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int
} }
EXPORT_SYMBOL(input_set_capability); EXPORT_SYMBOL(input_set_capability);
/**
* input_register_device - register device with input core
* @dev: device to be registered
*
* This function registers device with input core. The device must be
* allocated with input_allocate_device() and all it's capabilities
* set up before registering.
* If function fails the device must be freed with input_free_device().
* Once device has been successfully registered it can be unregistered
* with input_unregister_device(); input_free_device() should not be
* called in this case.
*/
int input_register_device(struct input_dev *dev) int input_register_device(struct input_dev *dev)
{ {
static atomic_t input_no = ATOMIC_INIT(0); static atomic_t input_no = ATOMIC_INIT(0);
...@@ -1077,7 +1297,7 @@ int input_register_device(struct input_dev *dev) ...@@ -1077,7 +1297,7 @@ int input_register_device(struct input_dev *dev)
const char *path; const char *path;
int error; int error;
set_bit(EV_SYN, dev->evbit); __set_bit(EV_SYN, dev->evbit);
/* /*
* If delay and period are pre-set by the driver, then autorepeating * If delay and period are pre-set by the driver, then autorepeating
...@@ -1098,8 +1318,6 @@ int input_register_device(struct input_dev *dev) ...@@ -1098,8 +1318,6 @@ int input_register_device(struct input_dev *dev)
if (!dev->setkeycode) if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode; dev->setkeycode = input_default_setkeycode;
list_add_tail(&dev->node, &input_dev_list);
snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id), snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1); "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
...@@ -1115,49 +1333,79 @@ int input_register_device(struct input_dev *dev) ...@@ -1115,49 +1333,79 @@ int input_register_device(struct input_dev *dev)
dev->name ? dev->name : "Unspecified device", path ? path : "N/A"); dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
kfree(path); kfree(path);
error = mutex_lock_interruptible(&input_mutex);
if (error) {
device_del(&dev->dev);
return error;
}
list_add_tail(&dev->node, &input_dev_list);
list_for_each_entry(handler, &input_handler_list, node) list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler); input_attach_handler(dev, handler);
input_wakeup_procfs_readers(); input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
return 0; return 0;
} }
EXPORT_SYMBOL(input_register_device); EXPORT_SYMBOL(input_register_device);
/**
* input_unregister_device - unregister previously registered device
* @dev: device to be unregistered
*
* This function unregisters an input device. Once device is unregistered
* the caller should not try to access it as it may get freed at any moment.
*/
void input_unregister_device(struct input_dev *dev) void input_unregister_device(struct input_dev *dev)
{ {
struct input_handle *handle, *next; struct input_handle *handle, *next;
int code;
for (code = 0; code <= KEY_MAX; code++) input_disconnect_device(dev);
if (test_bit(code, dev->key))
input_report_key(dev, code, 0);
input_sync(dev);
del_timer_sync(&dev->timer); mutex_lock(&input_mutex);
list_for_each_entry_safe(handle, next, &dev->h_list, d_node) list_for_each_entry_safe(handle, next, &dev->h_list, d_node)
handle->handler->disconnect(handle); handle->handler->disconnect(handle);
WARN_ON(!list_empty(&dev->h_list)); WARN_ON(!list_empty(&dev->h_list));
del_timer_sync(&dev->timer);
list_del_init(&dev->node); list_del_init(&dev->node);
device_unregister(&dev->dev);
input_wakeup_procfs_readers(); input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
device_unregister(&dev->dev);
} }
EXPORT_SYMBOL(input_unregister_device); EXPORT_SYMBOL(input_unregister_device);
/**
* input_register_handler - register a new input handler
* @handler: handler to be registered
*
* This function registers a new input handler (interface) for input
* devices in the system and attaches it to all input devices that
* are compatible with the handler.
*/
int input_register_handler(struct input_handler *handler) int input_register_handler(struct input_handler *handler)
{ {
struct input_dev *dev; struct input_dev *dev;
int retval;
retval = mutex_lock_interruptible(&input_mutex);
if (retval)
return retval;
INIT_LIST_HEAD(&handler->h_list); INIT_LIST_HEAD(&handler->h_list);
if (handler->fops != NULL) { if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) if (input_table[handler->minor >> 5]) {
return -EBUSY; retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler; input_table[handler->minor >> 5] = handler;
} }
...@@ -1167,14 +1415,26 @@ int input_register_handler(struct input_handler *handler) ...@@ -1167,14 +1415,26 @@ int input_register_handler(struct input_handler *handler)
input_attach_handler(dev, handler); input_attach_handler(dev, handler);
input_wakeup_procfs_readers(); input_wakeup_procfs_readers();
return 0;
out:
mutex_unlock(&input_mutex);
return retval;
} }
EXPORT_SYMBOL(input_register_handler); EXPORT_SYMBOL(input_register_handler);
/**
* input_unregister_handler - unregisters an input handler
* @handler: handler to be unregistered
*
* This function disconnects a handler from its input devices and
* removes it from lists of known handlers.
*/
void input_unregister_handler(struct input_handler *handler) void input_unregister_handler(struct input_handler *handler)
{ {
struct input_handle *handle, *next; struct input_handle *handle, *next;
mutex_lock(&input_mutex);
list_for_each_entry_safe(handle, next, &handler->h_list, h_node) list_for_each_entry_safe(handle, next, &handler->h_list, h_node)
handler->disconnect(handle); handler->disconnect(handle);
WARN_ON(!list_empty(&handler->h_list)); WARN_ON(!list_empty(&handler->h_list));
...@@ -1185,14 +1445,45 @@ void input_unregister_handler(struct input_handler *handler) ...@@ -1185,14 +1445,45 @@ void input_unregister_handler(struct input_handler *handler)
input_table[handler->minor >> 5] = NULL; input_table[handler->minor >> 5] = NULL;
input_wakeup_procfs_readers(); input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
} }
EXPORT_SYMBOL(input_unregister_handler); EXPORT_SYMBOL(input_unregister_handler);
/**
* input_register_handle - register a new input handle
* @handle: handle to register
*
* This function puts a new input handle onto device's
* and handler's lists so that events can flow through
* it once it is opened using input_open_device().
*
* This function is supposed to be called from handler's
* connect() method.
*/
int input_register_handle(struct input_handle *handle) int input_register_handle(struct input_handle *handle)
{ {
struct input_handler *handler = handle->handler; struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
int error;
/*
* We take dev->mutex here to prevent race with
* input_release_device().
*/
error = mutex_lock_interruptible(&dev->mutex);
if (error)
return error;
list_add_tail_rcu(&handle->d_node, &dev->h_list);
mutex_unlock(&dev->mutex);
synchronize_rcu();
list_add_tail(&handle->d_node, &handle->dev->h_list); /*
* Since we are supposed to be called from ->connect()
* which is mutually exclusive with ->disconnect()
* we can't be racing with input_unregister_handle()
* and so separate lock is not needed here.
*/
list_add_tail(&handle->h_node, &handler->h_list); list_add_tail(&handle->h_node, &handler->h_list);
if (handler->start) if (handler->start)
...@@ -1202,10 +1493,29 @@ int input_register_handle(struct input_handle *handle) ...@@ -1202,10 +1493,29 @@ int input_register_handle(struct input_handle *handle)
} }
EXPORT_SYMBOL(input_register_handle); EXPORT_SYMBOL(input_register_handle);
/**
* input_unregister_handle - unregister an input handle
* @handle: handle to unregister
*
* This function removes input handle from device's
* and handler's lists.
*
* This function is supposed to be called from handler's
* disconnect() method.
*/
void input_unregister_handle(struct input_handle *handle) void input_unregister_handle(struct input_handle *handle)
{ {
struct input_dev *dev = handle->dev;
list_del_init(&handle->h_node); list_del_init(&handle->h_node);
list_del_init(&handle->d_node);
/*
* Take dev->mutex to prevent race with input_release_device().
*/
mutex_lock(&dev->mutex);
list_del_rcu(&handle->d_node);
mutex_unlock(&dev->mutex);
synchronize_rcu();
} }
EXPORT_SYMBOL(input_unregister_handle); EXPORT_SYMBOL(input_unregister_handle);
......
...@@ -43,6 +43,8 @@ struct joydev { ...@@ -43,6 +43,8 @@ struct joydev {
struct input_handle handle; struct input_handle handle;
wait_queue_head_t wait; wait_queue_head_t wait;
struct list_head client_list; struct list_head client_list;
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev; struct device dev;
struct js_corr corr[ABS_MAX + 1]; struct js_corr corr[ABS_MAX + 1];
...@@ -61,31 +63,61 @@ struct joydev_client { ...@@ -61,31 +63,61 @@ struct joydev_client {
int head; int head;
int tail; int tail;
int startup; int startup;
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct fasync_struct *fasync; struct fasync_struct *fasync;
struct joydev *joydev; struct joydev *joydev;
struct list_head node; struct list_head node;
}; };
static struct joydev *joydev_table[JOYDEV_MINORS]; static struct joydev *joydev_table[JOYDEV_MINORS];
static DEFINE_MUTEX(joydev_table_mutex);
static int joydev_correct(int value, struct js_corr *corr) static int joydev_correct(int value, struct js_corr *corr)
{ {
switch (corr->type) { switch (corr->type) {
case JS_CORR_NONE:
break; case JS_CORR_NONE:
case JS_CORR_BROKEN: break;
value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 :
((corr->coef[3] * (value - corr->coef[1])) >> 14)) : case JS_CORR_BROKEN:
((corr->coef[2] * (value - corr->coef[0])) >> 14); value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 :
break; ((corr->coef[3] * (value - corr->coef[1])) >> 14)) :
default: ((corr->coef[2] * (value - corr->coef[0])) >> 14);
return 0; break;
default:
return 0;
} }
return value < -32767 ? -32767 : (value > 32767 ? 32767 : value); return value < -32767 ? -32767 : (value > 32767 ? 32767 : value);
} }
static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) static void joydev_pass_event(struct joydev_client *client,
struct js_event *event)
{
struct joydev *joydev = client->joydev;
/*
* IRQs already disabled, just acquire the lock
*/
spin_lock(&client->buffer_lock);
client->buffer[client->head] = *event;
if (client->startup == joydev->nabs + joydev->nkey) {
client->head++;
client->head &= JOYDEV_BUFFER_SIZE - 1;
if (client->tail == client->head)
client->startup = 0;
}
spin_unlock(&client->buffer_lock);
kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
static void joydev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{ {
struct joydev *joydev = handle->private; struct joydev *joydev = handle->private;
struct joydev_client *client; struct joydev_client *client;
...@@ -93,39 +125,34 @@ static void joydev_event(struct input_handle *handle, unsigned int type, unsigne ...@@ -93,39 +125,34 @@ static void joydev_event(struct input_handle *handle, unsigned int type, unsigne
switch (type) { switch (type) {
case EV_KEY: case EV_KEY:
if (code < BTN_MISC || value == 2) if (code < BTN_MISC || value == 2)
return; return;
event.type = JS_EVENT_BUTTON; event.type = JS_EVENT_BUTTON;
event.number = joydev->keymap[code - BTN_MISC]; event.number = joydev->keymap[code - BTN_MISC];
event.value = value; event.value = value;
break; break;
case EV_ABS:
event.type = JS_EVENT_AXIS;
event.number = joydev->absmap[code];
event.value = joydev_correct(value, joydev->corr + event.number);
if (event.value == joydev->abs[event.number])
return;
joydev->abs[event.number] = event.value;
break;
default: case EV_ABS:
event.type = JS_EVENT_AXIS;
event.number = joydev->absmap[code];
event.value = joydev_correct(value,
&joydev->corr[event.number]);
if (event.value == joydev->abs[event.number])
return; return;
joydev->abs[event.number] = event.value;
break;
default:
return;
} }
event.time = jiffies_to_msecs(jiffies); event.time = jiffies_to_msecs(jiffies);
list_for_each_entry(client, &joydev->client_list, node) { rcu_read_lock();
list_for_each_entry_rcu(client, &joydev->client_list, node)
memcpy(client->buffer + client->head, &event, sizeof(struct js_event)); joydev_pass_event(client, &event);
rcu_read_unlock();
if (client->startup == joydev->nabs + joydev->nkey)
if (client->tail == (client->head = (client->head + 1) & (JOYDEV_BUFFER_SIZE - 1)))
client->startup = 0;
kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
wake_up_interruptible(&joydev->wait); wake_up_interruptible(&joydev->wait);
} }
...@@ -144,23 +171,83 @@ static void joydev_free(struct device *dev) ...@@ -144,23 +171,83 @@ static void joydev_free(struct device *dev)
{ {
struct joydev *joydev = container_of(dev, struct joydev, dev); struct joydev *joydev = container_of(dev, struct joydev, dev);
joydev_table[joydev->minor] = NULL;
kfree(joydev); kfree(joydev);
} }
static void joydev_attach_client(struct joydev *joydev,
struct joydev_client *client)
{
spin_lock(&joydev->client_lock);
list_add_tail_rcu(&client->node, &joydev->client_list);
spin_unlock(&joydev->client_lock);
synchronize_rcu();
}
static void joydev_detach_client(struct joydev *joydev,
struct joydev_client *client)
{
spin_lock(&joydev->client_lock);
list_del_rcu(&client->node);
spin_unlock(&joydev->client_lock);
synchronize_rcu();
}
static int joydev_open_device(struct joydev *joydev)
{
int retval;
retval = mutex_lock_interruptible(&joydev->mutex);
if (retval)
return retval;
if (!joydev->exist)
retval = -ENODEV;
else if (!joydev->open++) {
retval = input_open_device(&joydev->handle);
if (retval)
joydev->open--;
}
mutex_unlock(&joydev->mutex);
return retval;
}
static void joydev_close_device(struct joydev *joydev)
{
mutex_lock(&joydev->mutex);
if (joydev->exist && !--joydev->open)
input_close_device(&joydev->handle);
mutex_unlock(&joydev->mutex);
}
/*
* Wake up users waiting for IO so they can disconnect from
* dead device.
*/
static void joydev_hangup(struct joydev *joydev)
{
struct joydev_client *client;
spin_lock(&joydev->client_lock);
list_for_each_entry(client, &joydev->client_list, node)
kill_fasync(&client->fasync, SIGIO, POLL_HUP);
spin_unlock(&joydev->client_lock);
wake_up_interruptible(&joydev->wait);
}
static int joydev_release(struct inode *inode, struct file *file) static int joydev_release(struct inode *inode, struct file *file)
{ {
struct joydev_client *client = file->private_data; struct joydev_client *client = file->private_data;
struct joydev *joydev = client->joydev; struct joydev *joydev = client->joydev;
joydev_fasync(-1, file, 0); joydev_fasync(-1, file, 0);
joydev_detach_client(joydev, client);
list_del(&client->node);
kfree(client); kfree(client);
if (!--joydev->open && joydev->exist) joydev_close_device(joydev);
input_close_device(&joydev->handle);
put_device(&joydev->dev); put_device(&joydev->dev);
return 0; return 0;
...@@ -176,11 +263,16 @@ static int joydev_open(struct inode *inode, struct file *file) ...@@ -176,11 +263,16 @@ static int joydev_open(struct inode *inode, struct file *file)
if (i >= JOYDEV_MINORS) if (i >= JOYDEV_MINORS)
return -ENODEV; return -ENODEV;
error = mutex_lock_interruptible(&joydev_table_mutex);
if (error)
return error;
joydev = joydev_table[i]; joydev = joydev_table[i];
if (!joydev || !joydev->exist) if (joydev)
return -ENODEV; get_device(&joydev->dev);
mutex_unlock(&joydev_table_mutex);
get_device(&joydev->dev); if (!joydev)
return -ENODEV;
client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL); client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL);
if (!client) { if (!client) {
...@@ -188,37 +280,129 @@ static int joydev_open(struct inode *inode, struct file *file) ...@@ -188,37 +280,129 @@ static int joydev_open(struct inode *inode, struct file *file)
goto err_put_joydev; goto err_put_joydev;
} }
spin_lock_init(&client->buffer_lock);
client->joydev = joydev; client->joydev = joydev;
list_add_tail(&client->node, &joydev->client_list); joydev_attach_client(joydev, client);
if (!joydev->open++ && joydev->exist) { error = joydev_open_device(joydev);
error = input_open_device(&joydev->handle); if (error)
if (error) goto err_free_client;
goto err_free_client;
}
file->private_data = client; file->private_data = client;
return 0; return 0;
err_free_client: err_free_client:
list_del(&client->node); joydev_detach_client(joydev, client);
kfree(client); kfree(client);
err_put_joydev: err_put_joydev:
put_device(&joydev->dev); put_device(&joydev->dev);
return error; return error;
} }
static ssize_t joydev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) static int joydev_generate_startup_event(struct joydev_client *client,
struct input_dev *input,
struct js_event *event)
{ {
return -EINVAL; struct joydev *joydev = client->joydev;
int have_event;
spin_lock_irq(&client->buffer_lock);
have_event = client->startup < joydev->nabs + joydev->nkey;
if (have_event) {
event->time = jiffies_to_msecs(jiffies);
if (client->startup < joydev->nkey) {
event->type = JS_EVENT_BUTTON | JS_EVENT_INIT;
event->number = client->startup;
event->value = !!test_bit(joydev->keypam[event->number],
input->key);
} else {
event->type = JS_EVENT_AXIS | JS_EVENT_INIT;
event->number = client->startup - joydev->nkey;
event->value = joydev->abs[event->number];
}
client->startup++;
}
spin_unlock_irq(&client->buffer_lock);
return have_event;
}
static int joydev_fetch_next_event(struct joydev_client *client,
struct js_event *event)
{
int have_event;
spin_lock_irq(&client->buffer_lock);
have_event = client->head != client->tail;
if (have_event) {
*event = client->buffer[client->tail++];
client->tail &= JOYDEV_BUFFER_SIZE - 1;
}
spin_unlock_irq(&client->buffer_lock);
return have_event;
}
/*
* Old joystick interface
*/
static ssize_t joydev_0x_read(struct joydev_client *client,
struct input_dev *input,
char __user *buf)
{
struct joydev *joydev = client->joydev;
struct JS_DATA_TYPE data;
int i;
spin_lock_irq(&input->event_lock);
/*
* Get device state
*/
for (data.buttons = i = 0; i < 32 && i < joydev->nkey; i++)
data.buttons |=
test_bit(joydev->keypam[i], input->key) ? (1 << i) : 0;
data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x;
data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y;
/*
* Reset reader's event queue
*/
spin_lock(&client->buffer_lock);
client->startup = 0;
client->tail = client->head;
spin_unlock(&client->buffer_lock);
spin_unlock_irq(&input->event_lock);
if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)))
return -EFAULT;
return sizeof(struct JS_DATA_TYPE);
}
static inline int joydev_data_pending(struct joydev_client *client)
{
struct joydev *joydev = client->joydev;
return client->startup < joydev->nabs + joydev->nkey ||
client->head != client->tail;
} }
static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) static ssize_t joydev_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{ {
struct joydev_client *client = file->private_data; struct joydev_client *client = file->private_data;
struct joydev *joydev = client->joydev; struct joydev *joydev = client->joydev;
struct input_dev *input = joydev->handle.dev; struct input_dev *input = joydev->handle.dev;
int retval = 0; struct js_event event;
int retval;
if (!joydev->exist) if (!joydev->exist)
return -ENODEV; return -ENODEV;
...@@ -226,68 +410,35 @@ static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, lo ...@@ -226,68 +410,35 @@ static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, lo
if (count < sizeof(struct js_event)) if (count < sizeof(struct js_event))
return -EINVAL; return -EINVAL;
if (count == sizeof(struct JS_DATA_TYPE)) { if (count == sizeof(struct JS_DATA_TYPE))
return joydev_0x_read(client, input, buf);
struct JS_DATA_TYPE data;
int i;
for (data.buttons = i = 0; i < 32 && i < joydev->nkey; i++)
data.buttons |= test_bit(joydev->keypam[i], input->key) ? (1 << i) : 0;
data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x;
data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y;
if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)))
return -EFAULT;
client->startup = 0;
client->tail = client->head;
return sizeof(struct JS_DATA_TYPE); if (!joydev_data_pending(client) && (file->f_flags & O_NONBLOCK))
}
if (client->startup == joydev->nabs + joydev->nkey &&
client->head == client->tail && (file->f_flags & O_NONBLOCK))
return -EAGAIN; return -EAGAIN;
retval = wait_event_interruptible(joydev->wait, retval = wait_event_interruptible(joydev->wait,
!joydev->exist || !joydev->exist || joydev_data_pending(client));
client->startup < joydev->nabs + joydev->nkey ||
client->head != client->tail);
if (retval) if (retval)
return retval; return retval;
if (!joydev->exist) if (!joydev->exist)
return -ENODEV; return -ENODEV;
while (client->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) { while (retval + sizeof(struct js_event) <= count &&
joydev_generate_startup_event(client, input, &event)) {
struct js_event event;
event.time = jiffies_to_msecs(jiffies);
if (client->startup < joydev->nkey) {
event.type = JS_EVENT_BUTTON | JS_EVENT_INIT;
event.number = client->startup;
event.value = !!test_bit(joydev->keypam[event.number], input->key);
} else {
event.type = JS_EVENT_AXIS | JS_EVENT_INIT;
event.number = client->startup - joydev->nkey;
event.value = joydev->abs[event.number];
}
if (copy_to_user(buf + retval, &event, sizeof(struct js_event))) if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))
return -EFAULT; return -EFAULT;
client->startup++;
retval += sizeof(struct js_event); retval += sizeof(struct js_event);
} }
while (client->head != client->tail && retval + sizeof(struct js_event) <= count) { while (retval + sizeof(struct js_event) <= count &&
joydev_fetch_next_event(client, &event)) {
if (copy_to_user(buf + retval, client->buffer + client->tail, sizeof(struct js_event))) if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))
return -EFAULT; return -EFAULT;
client->tail = (client->tail + 1) & (JOYDEV_BUFFER_SIZE - 1);
retval += sizeof(struct js_event); retval += sizeof(struct js_event);
} }
...@@ -301,126 +452,144 @@ static unsigned int joydev_poll(struct file *file, poll_table *wait) ...@@ -301,126 +452,144 @@ static unsigned int joydev_poll(struct file *file, poll_table *wait)
struct joydev *joydev = client->joydev; struct joydev *joydev = client->joydev;
poll_wait(file, &joydev->wait, wait); poll_wait(file, &joydev->wait, wait);
return ((client->head != client->tail || client->startup < joydev->nabs + joydev->nkey) ? return (joydev_data_pending(client) ? (POLLIN | POLLRDNORM) : 0) |
(POLLIN | POLLRDNORM) : 0) | (joydev->exist ? 0 : (POLLHUP | POLLERR)); (joydev->exist ? 0 : (POLLHUP | POLLERR));
} }
static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __user *argp) static int joydev_ioctl_common(struct joydev *joydev,
unsigned int cmd, void __user *argp)
{ {
struct input_dev *dev = joydev->handle.dev; struct input_dev *dev = joydev->handle.dev;
int i, j; int i, j;
switch (cmd) { switch (cmd) {
case JS_SET_CAL: case JS_SET_CAL:
return copy_from_user(&joydev->glue.JS_CORR, argp, return copy_from_user(&joydev->glue.JS_CORR, argp,
sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0; sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0;
case JS_GET_CAL: case JS_GET_CAL:
return copy_to_user(argp, &joydev->glue.JS_CORR, return copy_to_user(argp, &joydev->glue.JS_CORR,
sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0; sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0;
case JS_SET_TIMEOUT: case JS_SET_TIMEOUT:
return get_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp); return get_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp);
case JS_GET_TIMEOUT: case JS_GET_TIMEOUT:
return put_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp); return put_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp);
case JSIOCGVERSION: case JSIOCGVERSION:
return put_user(JS_VERSION, (__u32 __user *) argp); return put_user(JS_VERSION, (__u32 __user *) argp);
case JSIOCGAXES: case JSIOCGAXES:
return put_user(joydev->nabs, (__u8 __user *) argp); return put_user(joydev->nabs, (__u8 __user *) argp);
case JSIOCGBUTTONS: case JSIOCGBUTTONS:
return put_user(joydev->nkey, (__u8 __user *) argp); return put_user(joydev->nkey, (__u8 __user *) argp);
case JSIOCSCORR: case JSIOCSCORR:
if (copy_from_user(joydev->corr, argp, if (copy_from_user(joydev->corr, argp,
sizeof(joydev->corr[0]) * joydev->nabs)) sizeof(joydev->corr[0]) * joydev->nabs))
return -EFAULT; return -EFAULT;
for (i = 0; i < joydev->nabs; i++) {
j = joydev->abspam[i];
joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i);
}
return 0;
case JSIOCGCORR: for (i = 0; i < joydev->nabs; i++) {
return copy_to_user(argp, joydev->corr, j = joydev->abspam[i];
sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0; joydev->abs[i] = joydev_correct(dev->abs[j],
&joydev->corr[i]);
}
return 0;
case JSIOCSAXMAP: case JSIOCGCORR:
if (copy_from_user(joydev->abspam, argp, sizeof(__u8) * (ABS_MAX + 1))) return copy_to_user(argp, joydev->corr,
return -EFAULT; sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0;
for (i = 0; i < joydev->nabs; i++) {
if (joydev->abspam[i] > ABS_MAX) case JSIOCSAXMAP:
return -EINVAL; if (copy_from_user(joydev->abspam, argp,
joydev->absmap[joydev->abspam[i]] = i; sizeof(__u8) * (ABS_MAX + 1)))
} return -EFAULT;
return 0;
for (i = 0; i < joydev->nabs; i++) {
case JSIOCGAXMAP: if (joydev->abspam[i] > ABS_MAX)
return copy_to_user(argp, joydev->abspam, return -EINVAL;
sizeof(__u8) * (ABS_MAX + 1)) ? -EFAULT : 0; joydev->absmap[joydev->abspam[i]] = i;
}
case JSIOCSBTNMAP: return 0;
if (copy_from_user(joydev->keypam, argp, sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)))
case JSIOCGAXMAP:
return copy_to_user(argp, joydev->abspam,
sizeof(__u8) * (ABS_MAX + 1)) ? -EFAULT : 0;
case JSIOCSBTNMAP:
if (copy_from_user(joydev->keypam, argp,
sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)))
return -EFAULT;
for (i = 0; i < joydev->nkey; i++) {
if (joydev->keypam[i] > KEY_MAX ||
joydev->keypam[i] < BTN_MISC)
return -EINVAL;
joydev->keymap[joydev->keypam[i] - BTN_MISC] = i;
}
return 0;
case JSIOCGBTNMAP:
return copy_to_user(argp, joydev->keypam,
sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)) ? -EFAULT : 0;
default:
if ((cmd & ~IOCSIZE_MASK) == JSIOCGNAME(0)) {
int len;
if (!dev->name)
return 0;
len = strlen(dev->name) + 1;
if (len > _IOC_SIZE(cmd))
len = _IOC_SIZE(cmd);
if (copy_to_user(argp, dev->name, len))
return -EFAULT; return -EFAULT;
for (i = 0; i < joydev->nkey; i++) { return len;
if (joydev->keypam[i] > KEY_MAX || joydev->keypam[i] < BTN_MISC) }
return -EINVAL;
joydev->keymap[joydev->keypam[i] - BTN_MISC] = i;
}
return 0;
case JSIOCGBTNMAP:
return copy_to_user(argp, joydev->keypam,
sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)) ? -EFAULT : 0;
default:
if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) {
int len;
if (!dev->name)
return 0;
len = strlen(dev->name) + 1;
if (len > _IOC_SIZE(cmd))
len = _IOC_SIZE(cmd);
if (copy_to_user(argp, dev->name, len))
return -EFAULT;
return len;
}
} }
return -EINVAL; return -EINVAL;
} }
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static long joydev_compat_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{ {
struct joydev_client *client = file->private_data; struct joydev_client *client = file->private_data;
struct joydev *joydev = client->joydev; struct joydev *joydev = client->joydev;
void __user *argp = (void __user *)arg; void __user *argp = (void __user *)arg;
s32 tmp32; s32 tmp32;
struct JS_DATA_SAVE_TYPE_32 ds32; struct JS_DATA_SAVE_TYPE_32 ds32;
int err; int retval;
if (!joydev->exist) retval = mutex_lock_interruptible(&joydev->mutex);
return -ENODEV; if (retval)
return retval;
if (!joydev->exist) {
retval = -ENODEV;
goto out;
}
switch (cmd) {
switch(cmd) {
case JS_SET_TIMELIMIT: case JS_SET_TIMELIMIT:
err = get_user(tmp32, (s32 __user *) arg); retval = get_user(tmp32, (s32 __user *) arg);
if (err == 0) if (retval == 0)
joydev->glue.JS_TIMELIMIT = tmp32; joydev->glue.JS_TIMELIMIT = tmp32;
break; break;
case JS_GET_TIMELIMIT: case JS_GET_TIMELIMIT:
tmp32 = joydev->glue.JS_TIMELIMIT; tmp32 = joydev->glue.JS_TIMELIMIT;
err = put_user(tmp32, (s32 __user *) arg); retval = put_user(tmp32, (s32 __user *) arg);
break; break;
case JS_SET_ALL: case JS_SET_ALL:
err = copy_from_user(&ds32, argp, retval = copy_from_user(&ds32, argp,
sizeof(ds32)) ? -EFAULT : 0; sizeof(ds32)) ? -EFAULT : 0;
if (err == 0) { if (retval == 0) {
joydev->glue.JS_TIMEOUT = ds32.JS_TIMEOUT; joydev->glue.JS_TIMEOUT = ds32.JS_TIMEOUT;
joydev->glue.BUSY = ds32.BUSY; joydev->glue.BUSY = ds32.BUSY;
joydev->glue.JS_EXPIRETIME = ds32.JS_EXPIRETIME; joydev->glue.JS_EXPIRETIME = ds32.JS_EXPIRETIME;
...@@ -438,55 +607,119 @@ static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo ...@@ -438,55 +607,119 @@ static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo
ds32.JS_SAVE = joydev->glue.JS_SAVE; ds32.JS_SAVE = joydev->glue.JS_SAVE;
ds32.JS_CORR = joydev->glue.JS_CORR; ds32.JS_CORR = joydev->glue.JS_CORR;
err = copy_to_user(argp, &ds32, sizeof(ds32)) ? -EFAULT : 0; retval = copy_to_user(argp, &ds32, sizeof(ds32)) ? -EFAULT : 0;
break; break;
default: default:
err = joydev_ioctl_common(joydev, cmd, argp); retval = joydev_ioctl_common(joydev, cmd, argp);
break;
} }
return err;
out:
mutex_unlock(&joydev->mutex);
return retval;
} }
#endif /* CONFIG_COMPAT */ #endif /* CONFIG_COMPAT */
static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) static long joydev_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{ {
struct joydev_client *client = file->private_data; struct joydev_client *client = file->private_data;
struct joydev *joydev = client->joydev; struct joydev *joydev = client->joydev;
void __user *argp = (void __user *)arg; void __user *argp = (void __user *)arg;
int retval;
if (!joydev->exist) retval = mutex_lock_interruptible(&joydev->mutex);
return -ENODEV; if (retval)
return retval;
if (!joydev->exist) {
retval = -ENODEV;
goto out;
}
switch (cmd) {
case JS_SET_TIMELIMIT:
retval = get_user(joydev->glue.JS_TIMELIMIT,
(long __user *) arg);
break;
case JS_GET_TIMELIMIT:
retval = put_user(joydev->glue.JS_TIMELIMIT,
(long __user *) arg);
break;
case JS_SET_ALL:
retval = copy_from_user(&joydev->glue, argp,
sizeof(joydev->glue)) ? -EFAULT: 0;
break;
case JS_GET_ALL:
retval = copy_to_user(argp, &joydev->glue,
sizeof(joydev->glue)) ? -EFAULT : 0;
break;
switch(cmd) { default:
case JS_SET_TIMELIMIT: retval = joydev_ioctl_common(joydev, cmd, argp);
return get_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg); break;
case JS_GET_TIMELIMIT:
return put_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg);
case JS_SET_ALL:
return copy_from_user(&joydev->glue, argp,
sizeof(joydev->glue)) ? -EFAULT : 0;
case JS_GET_ALL:
return copy_to_user(argp, &joydev->glue,
sizeof(joydev->glue)) ? -EFAULT : 0;
default:
return joydev_ioctl_common(joydev, cmd, argp);
} }
out:
mutex_unlock(&joydev->mutex);
return retval;
} }
static const struct file_operations joydev_fops = { static const struct file_operations joydev_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.read = joydev_read, .read = joydev_read,
.write = joydev_write, .poll = joydev_poll,
.poll = joydev_poll, .open = joydev_open,
.open = joydev_open, .release = joydev_release,
.release = joydev_release, .unlocked_ioctl = joydev_ioctl,
.ioctl = joydev_ioctl,
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
.compat_ioctl = joydev_compat_ioctl, .compat_ioctl = joydev_compat_ioctl,
#endif #endif
.fasync = joydev_fasync, .fasync = joydev_fasync,
}; };
static int joydev_install_chrdev(struct joydev *joydev)
{
joydev_table[joydev->minor] = joydev;
return 0;
}
static void joydev_remove_chrdev(struct joydev *joydev)
{
mutex_lock(&joydev_table_mutex);
joydev_table[joydev->minor] = NULL;
mutex_unlock(&joydev_table_mutex);
}
/*
* Mark device non-existant. This disables writes, ioctls and
* prevents new users from opening the device. Already posted
* blocking reads will stay, however new ones will fail.
*/
static void joydev_mark_dead(struct joydev *joydev)
{
mutex_lock(&joydev->mutex);
joydev->exist = 0;
mutex_unlock(&joydev->mutex);
}
static void joydev_cleanup(struct joydev *joydev)
{
struct input_handle *handle = &joydev->handle;
joydev_mark_dead(joydev);
joydev_hangup(joydev);
joydev_remove_chrdev(joydev);
/* joydev is marked dead so noone else accesses joydev->open */
if (joydev->open)
input_close_device(handle);
}
static int joydev_connect(struct input_handler *handler, struct input_dev *dev, static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id) const struct input_device_id *id)
{ {
...@@ -494,7 +727,10 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, ...@@ -494,7 +727,10 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
int i, j, t, minor; int i, j, t, minor;
int error; int error;
for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++); for (minor = 0; minor < JOYDEV_MINORS; minor++)
if (!joydev_table[minor])
break;
if (minor == JOYDEV_MINORS) { if (minor == JOYDEV_MINORS) {
printk(KERN_ERR "joydev: no more free joydev devices\n"); printk(KERN_ERR "joydev: no more free joydev devices\n");
return -ENFILE; return -ENFILE;
...@@ -505,15 +741,19 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, ...@@ -505,15 +741,19 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
return -ENOMEM; return -ENOMEM;
INIT_LIST_HEAD(&joydev->client_list); INIT_LIST_HEAD(&joydev->client_list);
spin_lock_init(&joydev->client_lock);
mutex_init(&joydev->mutex);
init_waitqueue_head(&joydev->wait); init_waitqueue_head(&joydev->wait);
snprintf(joydev->name, sizeof(joydev->name), "js%d", minor);
joydev->exist = 1;
joydev->minor = minor; joydev->minor = minor;
joydev->exist = 1; joydev->exist = 1;
joydev->handle.dev = dev; joydev->handle.dev = dev;
joydev->handle.name = joydev->name; joydev->handle.name = joydev->name;
joydev->handle.handler = handler; joydev->handle.handler = handler;
joydev->handle.private = joydev; joydev->handle.private = joydev;
snprintf(joydev->name, sizeof(joydev->name), "js%d", minor);
for (i = 0; i < ABS_MAX + 1; i++) for (i = 0; i < ABS_MAX + 1; i++)
if (test_bit(i, dev->absbit)) { if (test_bit(i, dev->absbit)) {
...@@ -545,67 +785,65 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, ...@@ -545,67 +785,65 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
} }
joydev->corr[i].type = JS_CORR_BROKEN; joydev->corr[i].type = JS_CORR_BROKEN;
joydev->corr[i].prec = dev->absfuzz[j]; joydev->corr[i].prec = dev->absfuzz[j];
joydev->corr[i].coef[0] = (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j]; joydev->corr[i].coef[0] =
joydev->corr[i].coef[1] = (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j]; (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j];
if (!(t = ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]))) joydev->corr[i].coef[1] =
continue; (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j];
joydev->corr[i].coef[2] = (1 << 29) / t;
joydev->corr[i].coef[3] = (1 << 29) / t; t = (dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j];
if (t) {
joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i); joydev->corr[i].coef[2] = (1 << 29) / t;
joydev->corr[i].coef[3] = (1 << 29) / t;
joydev->abs[i] = joydev_correct(dev->abs[j],
joydev->corr + i);
}
} }
snprintf(joydev->dev.bus_id, sizeof(joydev->dev.bus_id), strlcpy(joydev->dev.bus_id, joydev->name, sizeof(joydev->dev.bus_id));
"js%d", minor); joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor);
joydev->dev.class = &input_class; joydev->dev.class = &input_class;
joydev->dev.parent = &dev->dev; joydev->dev.parent = &dev->dev;
joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor);
joydev->dev.release = joydev_free; joydev->dev.release = joydev_free;
device_initialize(&joydev->dev); device_initialize(&joydev->dev);
joydev_table[minor] = joydev; error = input_register_handle(&joydev->handle);
error = device_add(&joydev->dev);
if (error) if (error)
goto err_free_joydev; goto err_free_joydev;
error = input_register_handle(&joydev->handle); error = joydev_install_chrdev(joydev);
if (error) if (error)
goto err_delete_joydev; goto err_unregister_handle;
error = device_add(&joydev->dev);
if (error)
goto err_cleanup_joydev;
return 0; return 0;
err_delete_joydev: err_cleanup_joydev:
device_del(&joydev->dev); joydev_cleanup(joydev);
err_unregister_handle:
input_unregister_handle(&joydev->handle);
err_free_joydev: err_free_joydev:
put_device(&joydev->dev); put_device(&joydev->dev);
return error; return error;
} }
static void joydev_disconnect(struct input_handle *handle) static void joydev_disconnect(struct input_handle *handle)
{ {
struct joydev *joydev = handle->private; struct joydev *joydev = handle->private;
struct joydev_client *client;
input_unregister_handle(handle);
device_del(&joydev->dev); device_del(&joydev->dev);
joydev_cleanup(joydev);
joydev->exist = 0; input_unregister_handle(handle);
if (joydev->open) {
input_close_device(handle);
list_for_each_entry(client, &joydev->client_list, node)
kill_fasync(&client->fasync, SIGIO, POLL_HUP);
wake_up_interruptible(&joydev->wait);
}
put_device(&joydev->dev); put_device(&joydev->dev);
} }
static const struct input_device_id joydev_blacklist[] = { static const struct input_device_id joydev_blacklist[] = {
{ {
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT,
.evbit = { BIT(EV_KEY) }, .evbit = { BIT(EV_KEY) },
.keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) }, .keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
}, /* Avoid itouchpads, touchscreens and tablets */ }, /* Avoid itouchpads, touchscreens and tablets */
...@@ -614,17 +852,20 @@ static const struct input_device_id joydev_blacklist[] = { ...@@ -614,17 +852,20 @@ static const struct input_device_id joydev_blacklist[] = {
static const struct input_device_id joydev_ids[] = { static const struct input_device_id joydev_ids[] = {
{ {
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_ABS) }, .evbit = { BIT(EV_ABS) },
.absbit = { BIT(ABS_X) }, .absbit = { BIT(ABS_X) },
}, },
{ {
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_ABS) }, .evbit = { BIT(EV_ABS) },
.absbit = { BIT(ABS_WHEEL) }, .absbit = { BIT(ABS_WHEEL) },
}, },
{ {
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_ABS) }, .evbit = { BIT(EV_ABS) },
.absbit = { BIT(ABS_THROTTLE) }, .absbit = { BIT(ABS_THROTTLE) },
}, },
...@@ -634,14 +875,14 @@ static const struct input_device_id joydev_ids[] = { ...@@ -634,14 +875,14 @@ static const struct input_device_id joydev_ids[] = {
MODULE_DEVICE_TABLE(input, joydev_ids); MODULE_DEVICE_TABLE(input, joydev_ids);
static struct input_handler joydev_handler = { static struct input_handler joydev_handler = {
.event = joydev_event, .event = joydev_event,
.connect = joydev_connect, .connect = joydev_connect,
.disconnect = joydev_disconnect, .disconnect = joydev_disconnect,
.fops = &joydev_fops, .fops = &joydev_fops,
.minor = JOYDEV_MINOR_BASE, .minor = JOYDEV_MINOR_BASE,
.name = "joydev", .name = "joydev",
.id_table = joydev_ids, .id_table = joydev_ids,
.blacklist = joydev_blacklist, .blacklist = joydev_blacklist,
}; };
static int __init joydev_init(void) static int __init joydev_init(void)
......
...@@ -223,12 +223,16 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d ...@@ -223,12 +223,16 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
struct input_dev *dev = xpad->dev; struct input_dev *dev = xpad->dev;
/* left stick */ /* left stick */
input_report_abs(dev, ABS_X, (__s16) (((__s16)data[13] << 8) | data[12])); input_report_abs(dev, ABS_X,
input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[15] << 8) | data[14])); (__s16) le16_to_cpup((__le16 *)(data + 12)));
input_report_abs(dev, ABS_Y,
(__s16) le16_to_cpup((__le16 *)(data + 14)));
/* right stick */ /* right stick */
input_report_abs(dev, ABS_RX, (__s16) (((__s16)data[17] << 8) | data[16])); input_report_abs(dev, ABS_RX,
input_report_abs(dev, ABS_RY, (__s16) (((__s16)data[19] << 8) | data[18])); (__s16) le16_to_cpup((__le16 *)(data + 16)));
input_report_abs(dev, ABS_RY,
(__s16) le16_to_cpup((__le16 *)(data + 18)));
/* triggers left/right */ /* triggers left/right */
input_report_abs(dev, ABS_Z, data[10]); input_report_abs(dev, ABS_Z, data[10]);
...@@ -236,8 +240,10 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d ...@@ -236,8 +240,10 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
/* digital pad */ /* digital pad */
if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) { if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) {
input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04)); input_report_abs(dev, ABS_HAT0X,
input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01)); !!(data[2] & 0x08) - !!(data[2] & 0x04));
input_report_abs(dev, ABS_HAT0Y,
!!(data[2] & 0x02) - !!(data[2] & 0x01));
} else /* xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS */ { } else /* xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS */ {
input_report_key(dev, BTN_LEFT, data[2] & 0x04); input_report_key(dev, BTN_LEFT, data[2] & 0x04);
input_report_key(dev, BTN_RIGHT, data[2] & 0x08); input_report_key(dev, BTN_RIGHT, data[2] & 0x08);
...@@ -274,14 +280,17 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d ...@@ -274,14 +280,17 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
* http://www.free60.org/wiki/Gamepad * http://www.free60.org/wiki/Gamepad
*/ */
static void xpad360_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) static void xpad360_process_packet(struct usb_xpad *xpad,
u16 cmd, unsigned char *data)
{ {
struct input_dev *dev = xpad->dev; struct input_dev *dev = xpad->dev;
/* digital pad */ /* digital pad */
if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) { if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) {
input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04)); input_report_abs(dev, ABS_HAT0X,
input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01)); !!(data[2] & 0x08) - !!(data[2] & 0x04));
input_report_abs(dev, ABS_HAT0Y,
!!(data[2] & 0x02) - !!(data[2] & 0x01));
} else if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS) { } else if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS) {
/* dpad as buttons (right, left, down, up) */ /* dpad as buttons (right, left, down, up) */
input_report_key(dev, BTN_LEFT, data[2] & 0x04); input_report_key(dev, BTN_LEFT, data[2] & 0x04);
...@@ -308,12 +317,16 @@ static void xpad360_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char ...@@ -308,12 +317,16 @@ static void xpad360_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char
input_report_key(dev, BTN_MODE, data[3] & 0x04); input_report_key(dev, BTN_MODE, data[3] & 0x04);
/* left stick */ /* left stick */
input_report_abs(dev, ABS_X, (__s16) (((__s16)data[7] << 8) | (__s16)data[6])); input_report_abs(dev, ABS_X,
input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[9] << 8) | (__s16)data[8])); (__s16) le16_to_cpup((__le16 *)(data + 6)));
input_report_abs(dev, ABS_Y,
(__s16) le16_to_cpup((__le16 *)(data + 8)));
/* right stick */ /* right stick */
input_report_abs(dev, ABS_RX, (__s16) (((__s16)data[11] << 8) | (__s16)data[10])); input_report_abs(dev, ABS_RX,
input_report_abs(dev, ABS_RY, (__s16) (((__s16)data[13] << 8) | (__s16)data[12])); (__s16) le16_to_cpup((__le16 *)(data + 10)));
input_report_abs(dev, ABS_RY,
(__s16) le16_to_cpup((__le16 *)(data + 12)));
/* triggers left/right */ /* triggers left/right */
input_report_abs(dev, ABS_Z, data[4]); input_report_abs(dev, ABS_Z, data[4]);
...@@ -335,10 +348,12 @@ static void xpad_irq_in(struct urb *urb) ...@@ -335,10 +348,12 @@ static void xpad_irq_in(struct urb *urb)
case -ENOENT: case -ENOENT:
case -ESHUTDOWN: case -ESHUTDOWN:
/* this urb is terminated, clean up */ /* this urb is terminated, clean up */
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); dbg("%s - urb shutting down with status: %d",
__FUNCTION__, urb->status);
return; return;
default: default:
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); dbg("%s - nonzero urb status received: %d",
__FUNCTION__, urb->status);
goto exit; goto exit;
} }
...@@ -367,10 +382,12 @@ static void xpad_irq_out(struct urb *urb) ...@@ -367,10 +382,12 @@ static void xpad_irq_out(struct urb *urb)
case -ENOENT: case -ENOENT:
case -ESHUTDOWN: case -ESHUTDOWN:
/* this urb is terminated, clean up */ /* this urb is terminated, clean up */
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); dbg("%s - urb shutting down with status: %d",
__FUNCTION__, urb->status);
return; return;
default: default:
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); dbg("%s - nonzero urb status received: %d",
__FUNCTION__, urb->status);
goto exit; goto exit;
} }
...@@ -378,7 +395,7 @@ static void xpad_irq_out(struct urb *urb) ...@@ -378,7 +395,7 @@ static void xpad_irq_out(struct urb *urb)
retval = usb_submit_urb(urb, GFP_ATOMIC); retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval) if (retval)
err("%s - usb_submit_urb failed with result %d", err("%s - usb_submit_urb failed with result %d",
__FUNCTION__, retval); __FUNCTION__, retval);
} }
static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
...@@ -595,7 +612,7 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs) ...@@ -595,7 +612,7 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id) static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
{ {
struct usb_device *udev = interface_to_usbdev (intf); struct usb_device *udev = interface_to_usbdev(intf);
struct usb_xpad *xpad; struct usb_xpad *xpad;
struct input_dev *input_dev; struct input_dev *input_dev;
struct usb_endpoint_descriptor *ep_irq_in; struct usb_endpoint_descriptor *ep_irq_in;
......
...@@ -208,6 +208,27 @@ config KEYBOARD_HIL ...@@ -208,6 +208,27 @@ config KEYBOARD_HIL
This driver implements support for HIL-keyboards attached This driver implements support for HIL-keyboards attached
to your machine, so normally you should say Y here. to your machine, so normally you should say Y here.
config KEYBOARD_HP6XX
tristate "HP Jornada 6XX Keyboard support"
depends on SH_HP6XX
select INPUT_POLLDEV
help
This adds support for the onboard keyboard found on
HP Jornada 620/660/680/690.
To compile this driver as a module, choose M here: the
module will be called jornada680_kbd.
config KEYBOARD_HP7XX
tristate "HP Jornada 7XX Keyboard Driver"
depends on SA1100_JORNADA720_SSP && SA1100_SSP
help
Say Y here to add support for the HP Jornada 7xx (710/720/728)
onboard keyboard.
To compile this driver as a module, choose M here: the
module will be called jornada720_kbd.
config KEYBOARD_OMAP config KEYBOARD_OMAP
tristate "TI OMAP keypad support" tristate "TI OMAP keypad support"
depends on (ARCH_OMAP1 || ARCH_OMAP2) depends on (ARCH_OMAP1 || ARCH_OMAP2)
...@@ -253,4 +274,23 @@ config KEYBOARD_GPIO ...@@ -253,4 +274,23 @@ config KEYBOARD_GPIO
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called gpio-keys. module will be called gpio-keys.
config KEYBOARD_MAPLE
tristate "Maple bus keyboard"
depends on SH_DREAMCAST && MAPLE
help
Say Y here if you have a Dreamcast console running Linux and have
a keyboard attached to its Maple bus.
To compile this driver as a module, choose M here: the
module will be called maple_keyb.
config KEYBOARD_BFIN
tristate "Blackfin BF54x keypad support"
depends on BF54x
help
Say Y here if you want to use the BF54x keypad.
To compile this driver as a module, choose M here: the
module will be called bf54x-keys.
endif endif
...@@ -21,4 +21,7 @@ obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o ...@@ -21,4 +21,7 @@ obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keyboard.o obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keyboard.o
obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
/*
* File: drivers/input/keyboard/bf54x-keys.c
* Based on:
* Author: Michael Hennerich <hennerich@blackfin.uclinux.org>
*
* Created:
* Description: keypad driver for Analog Devices Blackfin BF54x Processors
*
*
* Modified:
* Copyright 2007 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* 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, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <asm/portmux.h>
#include <asm/mach/bf54x_keys.h>
#define DRV_NAME "bf54x-keys"
#define TIME_SCALE 100 /* 100 ns */
#define MAX_MULT (0xFF * TIME_SCALE)
#define MAX_RC 8 /* Max Row/Col */
static const u16 per_rows[] = {
P_KEY_ROW7,
P_KEY_ROW6,
P_KEY_ROW5,
P_KEY_ROW4,
P_KEY_ROW3,
P_KEY_ROW2,
P_KEY_ROW1,
P_KEY_ROW0,
0
};
static const u16 per_cols[] = {
P_KEY_COL7,
P_KEY_COL6,
P_KEY_COL5,
P_KEY_COL4,
P_KEY_COL3,
P_KEY_COL2,
P_KEY_COL1,
P_KEY_COL0,
0
};
struct bf54x_kpad {
struct input_dev *input;
int irq;
unsigned short lastkey;
unsigned short *keycode;
struct timer_list timer;
unsigned int keyup_test_jiffies;
};
static inline int bfin_kpad_find_key(struct bf54x_kpad *bf54x_kpad,
struct input_dev *input, u16 keyident)
{
u16 i;
for (i = 0; i < input->keycodemax; i++)
if (bf54x_kpad->keycode[i + input->keycodemax] == keyident)
return bf54x_kpad->keycode[i];
return -1;
}
static inline void bfin_keycodecpy(unsigned short *keycode,
const unsigned int *pdata_kc,
unsigned short keymapsize)
{
unsigned int i;
for (i = 0; i < keymapsize; i++) {
keycode[i] = pdata_kc[i] & 0xffff;
keycode[i + keymapsize] = pdata_kc[i] >> 16;
}
}
static inline u16 bfin_kpad_get_prescale(u32 timescale)
{
u32 sclk = get_sclk();
return ((((sclk / 1000) * timescale) / 1024) - 1);
}
static inline u16 bfin_kpad_get_keypressed(struct bf54x_kpad *bf54x_kpad)
{
return (bfin_read_KPAD_STAT() & KPAD_PRESSED);
}
static inline void bfin_kpad_clear_irq(void)
{
bfin_write_KPAD_STAT(0xFFFF);
bfin_write_KPAD_ROWCOL(0xFFFF);
}
static void bfin_kpad_timer(unsigned long data)
{
struct platform_device *pdev = (struct platform_device *) data;
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
if (bfin_kpad_get_keypressed(bf54x_kpad)) {
/* Try again later */
mod_timer(&bf54x_kpad->timer,
jiffies + bf54x_kpad->keyup_test_jiffies);
return;
}
input_report_key(bf54x_kpad->input, bf54x_kpad->lastkey, 0);
input_sync(bf54x_kpad->input);
/* Clear IRQ Status */
bfin_kpad_clear_irq();
enable_irq(bf54x_kpad->irq);
}
static irqreturn_t bfin_kpad_isr(int irq, void *dev_id)
{
struct platform_device *pdev = dev_id;
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
struct input_dev *input = bf54x_kpad->input;
int key;
u16 rowcol = bfin_read_KPAD_ROWCOL();
key = bfin_kpad_find_key(bf54x_kpad, input, rowcol);
input_report_key(input, key, 1);
input_sync(input);
if (bfin_kpad_get_keypressed(bf54x_kpad)) {
disable_irq(bf54x_kpad->irq);
bf54x_kpad->lastkey = key;
mod_timer(&bf54x_kpad->timer,
jiffies + bf54x_kpad->keyup_test_jiffies);
} else {
input_report_key(input, key, 0);
input_sync(input);
bfin_kpad_clear_irq();
}
return IRQ_HANDLED;
}
static int __devinit bfin_kpad_probe(struct platform_device *pdev)
{
struct bf54x_kpad *bf54x_kpad;
struct bfin_kpad_platform_data *pdata = pdev->dev.platform_data;
struct input_dev *input;
int i, error;
if (!pdata->rows || !pdata->cols || !pdata->keymap) {
printk(KERN_ERR DRV_NAME
": No rows, cols or keymap from pdata\n");
return -EINVAL;
}
if (!pdata->keymapsize ||
pdata->keymapsize > (pdata->rows * pdata->cols)) {
printk(KERN_ERR DRV_NAME ": Invalid keymapsize\n");
return -EINVAL;
}
bf54x_kpad = kzalloc(sizeof(struct bf54x_kpad), GFP_KERNEL);
if (!bf54x_kpad)
return -ENOMEM;
platform_set_drvdata(pdev, bf54x_kpad);
/* Allocate memory for keymap followed by private LUT */
bf54x_kpad->keycode = kmalloc(pdata->keymapsize *
sizeof(unsigned short) * 2, GFP_KERNEL);
if (!bf54x_kpad->keycode) {
error = -ENOMEM;
goto out;
}
if (!pdata->debounce_time || !pdata->debounce_time > MAX_MULT ||
!pdata->coldrive_time || !pdata->coldrive_time > MAX_MULT) {
printk(KERN_ERR DRV_NAME
": Invalid Debounce/Columdrive Time from pdata\n");
bfin_write_KPAD_MSEL(0xFF0); /* Default MSEL */
} else {
bfin_write_KPAD_MSEL(
((pdata->debounce_time / TIME_SCALE)
& DBON_SCALE) |
(((pdata->coldrive_time / TIME_SCALE) << 8)
& COLDRV_SCALE));
}
if (!pdata->keyup_test_interval)
bf54x_kpad->keyup_test_jiffies = msecs_to_jiffies(50);
else
bf54x_kpad->keyup_test_jiffies =
msecs_to_jiffies(pdata->keyup_test_interval);
if (peripheral_request_list((u16 *)&per_rows[MAX_RC - pdata->rows],
DRV_NAME)) {
printk(KERN_ERR DRV_NAME
": Requesting Peripherals failed\n");
error = -EFAULT;
goto out0;
}
if (peripheral_request_list((u16 *)&per_cols[MAX_RC - pdata->cols],
DRV_NAME)) {
printk(KERN_ERR DRV_NAME
": Requesting Peripherals failed\n");
error = -EFAULT;
goto out1;
}
bf54x_kpad->irq = platform_get_irq(pdev, 0);
if (bf54x_kpad->irq < 0) {
error = -ENODEV;
goto out2;
}
error = request_irq(bf54x_kpad->irq, bfin_kpad_isr,
IRQF_SAMPLE_RANDOM, DRV_NAME, pdev);
if (error) {
printk(KERN_ERR DRV_NAME
": unable to claim irq %d; error %d\n",
bf54x_kpad->irq, error);
error = -EBUSY;
goto out2;
}
input = input_allocate_device();
if (!input) {
error = -ENOMEM;
goto out3;
}
bf54x_kpad->input = input;
input->name = pdev->name;
input->phys = "bf54x-keys/input0";
input->dev.parent = &pdev->dev;
input_set_drvdata(input, bf54x_kpad);
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
input->keycodesize = sizeof(unsigned short);
input->keycodemax = pdata->keymapsize;
input->keycode = bf54x_kpad->keycode;
bfin_keycodecpy(bf54x_kpad->keycode, pdata->keymap, pdata->keymapsize);
/* setup input device */
__set_bit(EV_KEY, input->evbit);
if (pdata->repeat)
__set_bit(EV_REP, input->evbit);
for (i = 0; i < input->keycodemax; i++)
__set_bit(bf54x_kpad->keycode[i] & KEY_MAX, input->keybit);
__clear_bit(KEY_RESERVED, input->keybit);
error = input_register_device(input);
if (error) {
printk(KERN_ERR DRV_NAME
": Unable to register input device (%d)\n", error);
goto out4;
}
/* Init Keypad Key Up/Release test timer */
setup_timer(&bf54x_kpad->timer, bfin_kpad_timer, (unsigned long) pdev);
bfin_write_KPAD_PRESCALE(bfin_kpad_get_prescale(TIME_SCALE));
bfin_write_KPAD_CTL((((pdata->cols - 1) << 13) & KPAD_COLEN) |
(((pdata->rows - 1) << 10) & KPAD_ROWEN) |
(2 & KPAD_IRQMODE));
bfin_write_KPAD_CTL(bfin_read_KPAD_CTL() | KPAD_EN);
printk(KERN_ERR DRV_NAME
": Blackfin BF54x Keypad registered IRQ %d\n", bf54x_kpad->irq);
return 0;
out4:
input_free_device(input);
out3:
free_irq(bf54x_kpad->irq, pdev);
out2:
peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);
out1:
peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);
out0:
kfree(bf54x_kpad->keycode);
out:
kfree(bf54x_kpad);
platform_set_drvdata(pdev, NULL);
return error;
}
static int __devexit bfin_kpad_remove(struct platform_device *pdev)
{
struct bfin_kpad_platform_data *pdata = pdev->dev.platform_data;
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
del_timer_sync(&bf54x_kpad->timer);
free_irq(bf54x_kpad->irq, pdev);
input_unregister_device(bf54x_kpad->input);
peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);
peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);
kfree(bf54x_kpad->keycode);
kfree(bf54x_kpad);
platform_set_drvdata(pdev, NULL);
return 0;
}
struct platform_driver bfin_kpad_device_driver = {
.probe = bfin_kpad_probe,
.remove = __devexit_p(bfin_kpad_remove),
.driver = {
.name = DRV_NAME,
}
};
static int __init bfin_kpad_init(void)
{
return platform_driver_register(&bfin_kpad_device_driver);
}
static void __exit bfin_kpad_exit(void)
{
platform_driver_unregister(&bfin_kpad_device_driver);
}
module_init(bfin_kpad_init);
module_exit(bfin_kpad_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("Keypad driver for BF54x Processors");
...@@ -54,6 +54,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) ...@@ -54,6 +54,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct input_dev *input; struct input_dev *input;
int i, error; int i, error;
int wakeup = 0;
input = input_allocate_device(); input = input_allocate_device();
if (!input) if (!input)
...@@ -77,31 +78,51 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) ...@@ -77,31 +78,51 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
int irq = gpio_to_irq(button->gpio); int irq = gpio_to_irq(button->gpio);
unsigned int type = button->type ?: EV_KEY; unsigned int type = button->type ?: EV_KEY;
set_irq_type(irq, IRQ_TYPE_EDGE_BOTH); if (irq < 0) {
error = request_irq(irq, gpio_keys_isr, IRQF_SAMPLE_RANDOM, error = irq;
button->desc ? button->desc : "gpio_keys", printk(KERN_ERR
pdev); "gpio-keys: "
"Unable to get irq number for GPIO %d,"
"error %d\n",
button->gpio, error);
goto fail;
}
error = request_irq(irq, gpio_keys_isr,
IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
button->desc ? button->desc : "gpio_keys",
pdev);
if (error) { if (error) {
printk(KERN_ERR "gpio-keys: unable to claim irq %d; error %d\n", printk(KERN_ERR
"gpio-keys: Unable to claim irq %d; error %d\n",
irq, error); irq, error);
goto fail; goto fail;
} }
if (button->wakeup)
wakeup = 1;
input_set_capability(input, type, button->code); input_set_capability(input, type, button->code);
} }
error = input_register_device(input); error = input_register_device(input);
if (error) { if (error) {
printk(KERN_ERR "Unable to register gpio-keys input device\n"); printk(KERN_ERR
"gpio-keys: Unable to register input device, "
"error: %d\n", error);
goto fail; goto fail;
} }
device_init_wakeup(&pdev->dev, wakeup);
return 0; return 0;
fail: fail:
for (i = i - 1; i >= 0; i--) while (--i >= 0)
free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev); free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev);
platform_set_drvdata(pdev, NULL);
input_free_device(input); input_free_device(input);
return error; return error;
...@@ -113,6 +134,8 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) ...@@ -113,6 +134,8 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
struct input_dev *input = platform_get_drvdata(pdev); struct input_dev *input = platform_get_drvdata(pdev);
int i; int i;
device_init_wakeup(&pdev->dev, 0);
for (i = 0; i < pdata->nbuttons; i++) { for (i = 0; i < pdata->nbuttons; i++) {
int irq = gpio_to_irq(pdata->buttons[i].gpio); int irq = gpio_to_irq(pdata->buttons[i].gpio);
free_irq(irq, pdev); free_irq(irq, pdev);
...@@ -123,9 +146,53 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) ...@@ -123,9 +146,53 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int gpio_keys_suspend(struct platform_device *pdev, pm_message_t state)
{
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
int i;
if (device_may_wakeup(&pdev->dev)) {
for (i = 0; i < pdata->nbuttons; i++) {
struct gpio_keys_button *button = &pdata->buttons[i];
if (button->wakeup) {
int irq = gpio_to_irq(button->gpio);
enable_irq_wake(irq);
}
}
}
return 0;
}
static int gpio_keys_resume(struct platform_device *pdev)
{
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
int i;
if (device_may_wakeup(&pdev->dev)) {
for (i = 0; i < pdata->nbuttons; i++) {
struct gpio_keys_button *button = &pdata->buttons[i];
if (button->wakeup) {
int irq = gpio_to_irq(button->gpio);
disable_irq_wake(irq);
}
}
}
return 0;
}
#else
#define gpio_keys_suspend NULL
#define gpio_keys_resume NULL
#endif
struct platform_driver gpio_keys_device_driver = { struct platform_driver gpio_keys_device_driver = {
.probe = gpio_keys_probe, .probe = gpio_keys_probe,
.remove = __devexit_p(gpio_keys_remove), .remove = __devexit_p(gpio_keys_remove),
.suspend = gpio_keys_suspend,
.resume = gpio_keys_resume,
.driver = { .driver = {
.name = "gpio-keys", .name = "gpio-keys",
} }
......
/*
* drivers/input/keyboard/jornada680_kbd.c
*
* HP Jornada 620/660/680/690 scan keyboard platform driver
* Copyright (C) 2007 Kristoffer Ericson <Kristoffer.Ericson@gmail.com>
*
* Based on hp680_keyb.c
* Copyright (C) 2006 Paul Mundt
* Copyright (C) 2005 Andriy Skulysh
* Split from drivers/input/keyboard/hp600_keyb.c
* Copyright (C) 2000 Yaegashi Takeshi (hp6xx kbd scan routine and translation table)
* Copyright (C) 2000 Niibe Yutaka (HP620 Keyb translation table)
*
* 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/input.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input-polldev.h>
#include <linux/jiffies.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <asm/delay.h>
#include <asm/io.h>
#define PCCR 0xa4000104
#define PDCR 0xa4000106
#define PECR 0xa4000108
#define PFCR 0xa400010a
#define PCDR 0xa4000124
#define PDDR 0xa4000126
#define PEDR 0xa4000128
#define PFDR 0xa400012a
#define PGDR 0xa400012c
#define PHDR 0xa400012e
#define PJDR 0xa4000130
#define PKDR 0xa4000132
#define PLDR 0xa4000134
static const unsigned short jornada_scancodes[] = {
/* PTD1 */ KEY_CAPSLOCK, KEY_MACRO, KEY_LEFTCTRL, 0, KEY_ESC, 0, 0, 0, /* 1 -> 8 */
KEY_F1, KEY_F2, KEY_F3, KEY_F8, KEY_F7, KEY_F2, KEY_F4, KEY_F5, /* 9 -> 16 */
/* PTD5 */ KEY_SLASH, KEY_APOSTROPHE, KEY_ENTER, 0, KEY_Z, 0, 0, 0, /* 17 -> 24 */
KEY_X, KEY_C, KEY_V, KEY_DOT, KEY_COMMA, KEY_M, KEY_B, KEY_N, /* 25 -> 32 */
/* PTD7 */ KEY_KP2, KEY_KP6, 0, 0, 0, 0, 0, 0, /* 33 -> 40 */
0, 0, 0, KEY_KP4, 0, 0, KEY_LEFTALT, KEY_HANJA, /* 41 -> 48 */
/* PTE0 */ 0, 0, 0, 0, KEY_FINANCE, 0, 0, 0, /* 49 -> 56 */
KEY_LEFTCTRL, 0, KEY_SPACE, KEY_KPDOT, KEY_VOLUMEUP, 249, 0, 0, /* 57 -> 64 */
/* PTE1 */ KEY_SEMICOLON, KEY_RIGHTBRACE, KEY_BACKSLASH, 0, KEY_A, 0, 0, 0,/* 65 -> 72 */
KEY_S, KEY_D, KEY_F, KEY_L, KEY_K, KEY_J, KEY_G, KEY_H, /* 73 -> 80 */
/* PTE3 */ KEY_KP8, KEY_LEFTMETA, KEY_RIGHTSHIFT, 0, KEY_TAB, 0, 0,0, /* 81 -> 88 */
0, KEY_LEFTSHIFT, 0, 0, 0, 0, 0, 0, /* 89 -> 96 */
/* PTE6 */ KEY_P, KEY_LEFTBRACE, KEY_BACKSPACE, 0, KEY_Q, 0, 0, 0, /* 97 -> 104 */
KEY_W, KEY_E, KEY_R, KEY_O, KEY_I, KEY_U, KEY_T, KEY_R, /* 105 -> 112 */
/* PTE7 */ KEY_0, KEY_MINUS, KEY_EQUAL, 0, KEY_1, 0, 0, 0, /* 113 -> 120 */
KEY_2, KEY_3, KEY_4, KEY_9, KEY_8, KEY_7, KEY_5, KEY_6, /* 121 -> 128 */
/* **** */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0
};
#define JORNADA_SCAN_SIZE 18
struct jornadakbd {
struct input_polled_dev *poll_dev;
unsigned short keymap[ARRAY_SIZE(jornada_scancodes)];
unsigned char length;
unsigned char old_scan[JORNADA_SCAN_SIZE];
unsigned char new_scan[JORNADA_SCAN_SIZE];
};
static void jornada_parse_kbd(struct jornadakbd *jornadakbd)
{
struct input_dev *input_dev = jornadakbd->poll_dev->input;
unsigned short *keymap = jornadakbd->keymap;
unsigned int sync_me = 0;
unsigned int i, j;
for (i = 0; i < JORNADA_SCAN_SIZE; i++) {
unsigned char new = jornadakbd->new_scan[i];
unsigned char old = jornadakbd->old_scan[i];
unsigned int xor = new ^ old;
if (xor == 0)
continue;
for (j = 0; j < 8; j++) {
unsigned int bit = 1 << j;
if (xor & bit) {
unsigned int scancode = (i << 3) + j;
input_event(input_dev,
EV_MSC, MSC_SCAN, scancode);
input_report_key(input_dev,
keymap[scancode],
!(new & bit));
sync_me = 1;
}
}
}
if (sync_me)
input_sync(input_dev);
}
static void jornada_scan_keyb(unsigned char *s)
{
int i;
unsigned short ec_static, dc_static; /* = UINT16_t */
unsigned char matrix_switch[] = {
0xfd, 0xff, /* PTD1 PD(1) */
0xdf, 0xff, /* PTD5 PD(5) */
0x7f, 0xff, /* PTD7 PD(7) */
0xff, 0xfe, /* PTE0 PE(0) */
0xff, 0xfd, /* PTE1 PE(1) */
0xff, 0xf7, /* PTE3 PE(3) */
0xff, 0xbf, /* PTE6 PE(6) */
0xff, 0x7f, /* PTE7 PE(7) */
}, *t = matrix_switch;
/* PD(x) :
1. 0xcc0c & (1~(1 << (2*(x)+1)))))
2. (0xf0cf & 0xfffff) */
/* PE(x) :
1. 0xcc0c & 0xffff
2. 0xf0cf & (1~(1 << (2*(x)+1))))) */
unsigned short matrix_PDE[] = {
0xcc04, 0xf0cf, /* PD(1) */
0xc40c, 0xf0cf, /* PD(5) */
0x4c0c, 0xf0cf, /* PD(7) */
0xcc0c, 0xf0cd, /* PE(0) */
0xcc0c, 0xf0c7, /* PE(1) */
0xcc0c, 0xf04f, /* PE(3) */
0xcc0c, 0xd0cf, /* PE(6) */
0xcc0c, 0x70cf, /* PE(7) */
}, *y = matrix_PDE;
/* Save these control reg bits */
dc_static = (ctrl_inw(PDCR) & (~0xcc0c));
ec_static = (ctrl_inw(PECR) & (~0xf0cf));
for (i = 0; i < 8; i++) {
/* disable output for all but the one we want to scan */
ctrl_outw((dc_static | *y++), PDCR);
ctrl_outw((ec_static | *y++), PECR);
udelay(5);
/* Get scanline row */
ctrl_outb(*t++, PDDR);
ctrl_outb(*t++, PEDR);
udelay(50);
/* Read data */
*s++ = ctrl_inb(PCDR);
*s++ = ctrl_inb(PFDR);
}
/* Scan no lines */
ctrl_outb(0xff, PDDR);
ctrl_outb(0xff, PEDR);
/* Enable all scanlines */
ctrl_outw((dc_static | (0x5555 & 0xcc0c)),PDCR);
ctrl_outw((ec_static | (0x5555 & 0xf0cf)),PECR);
/* Ignore extra keys and events */
*s++ = ctrl_inb(PGDR);
*s++ = ctrl_inb(PHDR);
}
static void jornadakbd680_poll(struct input_polled_dev *dev)
{
struct jornadakbd *jornadakbd = dev->private;
jornada_scan_keyb(jornadakbd->new_scan);
jornada_parse_kbd(jornadakbd);
memcpy(jornadakbd->old_scan, jornadakbd->new_scan, JORNADA_SCAN_SIZE);
}
static int __devinit jornada680kbd_probe(struct platform_device *pdev)
{
struct jornadakbd *jornadakbd;
struct input_polled_dev *poll_dev;
struct input_dev *input_dev;
int i, error;
jornadakbd = kzalloc(sizeof(struct jornadakbd), GFP_KERNEL);
if (!jornadakbd)
return -ENOMEM;
poll_dev = input_allocate_polled_device();
if (!poll_dev) {
error = -ENOMEM;
goto failed;
}
platform_set_drvdata(pdev, jornadakbd);
jornadakbd->poll_dev = poll_dev;
memcpy(jornadakbd->keymap, jornada_scancodes,
sizeof(jornadakbd->keymap));
poll_dev->private = jornadakbd;
poll_dev->poll = jornadakbd680_poll;
poll_dev->poll_interval = 50; /* msec */
input_dev = poll_dev->input;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
input_dev->name = "HP Jornada 680 keyboard";
input_dev->phys = "jornadakbd/input0";
input_dev->keycode = jornadakbd->keymap;
input_dev->keycodesize = sizeof(unsigned short);
input_dev->keycodemax = ARRAY_SIZE(jornada_scancodes);
input_dev->dev.parent = &pdev->dev;
input_dev->id.bustype = BUS_HOST;
for (i = 0; i < 128; i++)
if (jornadakbd->keymap[i])
__set_bit(jornadakbd->keymap[i], input_dev->keybit);
__clear_bit(KEY_RESERVED, input_dev->keybit);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
error = input_register_polled_device(jornadakbd->poll_dev);
if (error)
goto failed;
return 0;
failed:
printk(KERN_ERR "Jornadakbd: failed to register driver, error: %d\n",
error);
platform_set_drvdata(pdev, NULL);
input_free_polled_device(poll_dev);
kfree(jornadakbd);
return error;
}
static int __devexit jornada680kbd_remove(struct platform_device *pdev)
{
struct jornadakbd *jornadakbd = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
input_unregister_polled_device(jornadakbd->poll_dev);
input_free_polled_device(jornadakbd->poll_dev);
kfree(jornadakbd);
return 0;
}
static struct platform_driver jornada680kbd_driver = {
.driver = {
.name = "jornada680_kbd",
},
.probe = jornada680kbd_probe,
.remove = __devexit_p(jornada680kbd_remove),
};
static int __init jornada680kbd_init(void)
{
return platform_driver_register(&jornada680kbd_driver);
}
static void __exit jornada680kbd_exit(void)
{
platform_driver_unregister(&jornada680kbd_driver);
}
module_init(jornada680kbd_init);
module_exit(jornada680kbd_exit);
MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>");
MODULE_DESCRIPTION("HP Jornada 620/660/680/690 Keyboard Driver");
MODULE_LICENSE("GPLv2");
/*
* drivers/input/keyboard/jornada720_kbd.c
*
* HP Jornada 720 keyboard platform driver
*
* Copyright (C) 2006/2007 Kristoffer Ericson <Kristoffer.Ericson@Gmail.com>
*
* Copyright (C) 2006 jornada 720 kbd driver by
Filip Zyzniewsk <Filip.Zyzniewski@tefnet.plX
* based on (C) 2004 jornada 720 kbd driver by
Alex Lange <chicken@handhelds.org>
*
* 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/device.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/arch/jornada720.h>
#include <asm/hardware.h>
MODULE_AUTHOR("Kristoffer Ericson <Kristoffer.Ericson@gmail.com>");
MODULE_DESCRIPTION("HP Jornada 710/720/728 keyboard driver");
MODULE_LICENSE("GPLv2");
static unsigned short jornada_std_keymap[128] = { /* ROW */
0, KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, /* #1 */
KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE, /* -> */
0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, /* #2 */
KEY_0, KEY_MINUS, KEY_EQUAL,0, 0, 0, /* -> */
0, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, /* #3 */
KEY_P, KEY_BACKSLASH, KEY_BACKSPACE, 0, 0, 0, /* -> */
0, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, /* #4 */
KEY_SEMICOLON, KEY_LEFTBRACE, KEY_RIGHTBRACE, 0, 0, 0, /* -> */
0, KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, KEY_COMMA, /* #5 */
KEY_DOT, KEY_KPMINUS, KEY_APOSTROPHE, KEY_ENTER, 0, 0,0, /* -> */
0, KEY_TAB, 0, KEY_LEFTSHIFT, 0, KEY_APOSTROPHE, 0, 0, 0, 0, /* #6 */
KEY_UP, 0, KEY_RIGHTSHIFT, 0, 0, 0,0, 0, 0, 0, 0, KEY_LEFTALT, KEY_GRAVE, /* -> */
0, 0, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0,0, KEY_KPASTERISK, /* -> */
KEY_LEFTCTRL, 0, KEY_SPACE, 0, 0, 0, KEY_SLASH, KEY_DELETE, 0, 0, /* -> */
0, 0, 0, KEY_POWER, /* -> */
};
struct jornadakbd {
unsigned short keymap[ARRAY_SIZE(jornada_std_keymap)];
struct input_dev *input;
};
static irqreturn_t jornada720_kbd_interrupt(int irq, void *dev_id)
{
struct platform_device *pdev = dev_id;
struct jornadakbd *jornadakbd = platform_get_drvdata(pdev);
struct input_dev *input = jornadakbd->input;
u8 count, kbd_data, scan_code;
/* startup ssp with spinlock */
jornada_ssp_start();
if (jornada_ssp_inout(GETSCANKEYCODE) != TXDUMMY) {
printk(KERN_DEBUG
"jornada720_kbd: "
"GetKeycode command failed with ETIMEDOUT, "
"flushed bus\n");
} else {
/* How many keycodes are waiting for us? */
count = jornada_ssp_byte(TXDUMMY);
/* Lets drag them out one at a time */
while (count--) {
/* Exchange TxDummy for location (keymap[kbddata]) */
kbd_data = jornada_ssp_byte(TXDUMMY);
scan_code = kbd_data & 0x7f;
input_event(input, EV_MSC, MSC_SCAN, scan_code);
input_report_key(input, jornadakbd->keymap[scan_code],
!(kbd_data & 0x80));
input_sync(input);
}
}
/* release spinlock and turn off ssp */
jornada_ssp_end();
return IRQ_HANDLED;
};
static int __devinit jornada720_kbd_probe(struct platform_device *pdev)
{
struct jornadakbd *jornadakbd;
struct input_dev *input_dev;
int i, err;
jornadakbd = kzalloc(sizeof(struct jornadakbd), GFP_KERNEL);
input_dev = input_allocate_device();
if (!jornadakbd || !input_dev) {
err = -ENOMEM;
goto fail1;
}
platform_set_drvdata(pdev, jornadakbd);
memcpy(jornadakbd->keymap, jornada_std_keymap,
sizeof(jornada_std_keymap));
jornadakbd->input = input_dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
input_dev->name = "HP Jornada 720 keyboard";
input_dev->phys = "jornadakbd/input0";
input_dev->keycode = jornadakbd->keymap;
input_dev->keycodesize = sizeof(unsigned short);
input_dev->keycodemax = ARRAY_SIZE(jornada_std_keymap);
input_dev->id.bustype = BUS_HOST;
input_dev->dev.parent = &pdev->dev;
for (i = 0; i < ARRAY_SIZE(jornadakbd->keymap); i++)
__set_bit(jornadakbd->keymap[i], input_dev->keybit);
__clear_bit(KEY_RESERVED, input_dev->keybit);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
err = request_irq(IRQ_GPIO0,
jornada720_kbd_interrupt,
IRQF_DISABLED | IRQF_TRIGGER_FALLING,
"jornadakbd", pdev);
if (err) {
printk(KERN_INFO "jornadakbd720_kbd: Unable to grab IRQ\n");
goto fail1;
}
err = input_register_device(jornadakbd->input);
if (err)
goto fail2;
return 0;
fail2: /* IRQ, DEVICE, MEMORY */
free_irq(IRQ_GPIO0, pdev);
fail1: /* DEVICE, MEMORY */
platform_set_drvdata(pdev, NULL);
input_free_device(input_dev);
kfree(jornadakbd);
return err;
};
static int __devexit jornada720_kbd_remove(struct platform_device *pdev)
{
struct jornadakbd *jornadakbd = platform_get_drvdata(pdev);
free_irq(IRQ_GPIO0, pdev);
platform_set_drvdata(pdev, NULL);
input_unregister_device(jornadakbd->input);
kfree(jornadakbd);
return 0;
}
static struct platform_driver jornada720_kbd_driver = {
.driver = {
.name = "jornada720_kbd",
},
.probe = jornada720_kbd_probe,
.remove = __devexit_p(jornada720_kbd_remove),
};
static int __init jornada720_kbd_init(void)
{
return platform_driver_register(&jornada720_kbd_driver);
}
static void __exit jornada720_kbd_exit(void)
{
platform_driver_unregister(&jornada720_kbd_driver);
}
module_init(jornada720_kbd_init);
module_exit(jornada720_kbd_exit);
/*
* SEGA Dreamcast keyboard driver
* Based on drivers/usb/usbkbd.c
* Copyright YAEGASHI Takeshi, 2001
* Porting to 2.6 Copyright Adrian McMenamin, 2007
*
* 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, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/maple.h>
#include <asm/mach/maple.h>
/* Very simple mutex to ensure proper cleanup */
static DEFINE_MUTEX(maple_keyb_mutex);
#define NR_SCANCODES 256
MODULE_AUTHOR("YAEGASHI Takeshi, Adrian McMenamin");
MODULE_DESCRIPTION("SEGA Dreamcast keyboard driver");
MODULE_LICENSE("GPL");
struct dc_kbd {
struct input_dev *dev;
unsigned short keycode[NR_SCANCODES];
unsigned char new[8];
unsigned char old[8];
};
static const unsigned short dc_kbd_keycode[NR_SCANCODES] = {
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_A, KEY_B, KEY_C, KEY_D,
KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L,
KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T,
KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_1, KEY_2,
KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0,
KEY_ENTER, KEY_ESC, KEY_BACKSPACE, KEY_TAB, KEY_SPACE, KEY_MINUS, KEY_EQUAL, KEY_LEFTBRACE,
KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_BACKSLASH, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA,
KEY_DOT, KEY_SLASH, KEY_CAPSLOCK, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6,
KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_SYSRQ,
KEY_SCROLLLOCK, KEY_PAUSE, KEY_INSERT, KEY_HOME, KEY_PAGEUP, KEY_DELETE,
KEY_END, KEY_PAGEDOWN, KEY_RIGHT, KEY_LEFT, KEY_DOWN, KEY_UP,
KEY_NUMLOCK, KEY_KPSLASH, KEY_KPASTERISK, KEY_KPMINUS, KEY_KPPLUS, KEY_KPENTER, KEY_KP1, KEY_KP2,
KEY_KP3, KEY_KP4, KEY_KP5, KEY_KP6, KEY_KP7, KEY_KP8, KEY_KP9, KEY_KP0, KEY_KPDOT,
KEY_102ND, KEY_COMPOSE, KEY_POWER, KEY_KPEQUAL, KEY_F13, KEY_F14, KEY_F15,
KEY_F16, KEY_F17, KEY_F18, KEY_F19, KEY_F20,
KEY_F21, KEY_F22, KEY_F23, KEY_F24, KEY_OPEN, KEY_HELP, KEY_PROPS, KEY_FRONT,
KEY_STOP, KEY_AGAIN, KEY_UNDO, KEY_CUT, KEY_COPY, KEY_PASTE, KEY_FIND, KEY_MUTE,
KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_KPCOMMA, KEY_RESERVED, KEY_RO, KEY_KATAKANAHIRAGANA , KEY_YEN,
KEY_HENKAN, KEY_MUHENKAN, KEY_KPJPCOMMA, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_HANGEUL, KEY_HANJA, KEY_KATAKANA, KEY_HIRAGANA, KEY_ZENKAKUHANKAKU, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_LEFTCTRL, KEY_LEFTSHIFT, KEY_LEFTALT, KEY_LEFTMETA, KEY_RIGHTCTRL, KEY_RIGHTSHIFT, KEY_RIGHTALT, KEY_RIGHTMETA,
KEY_PLAYPAUSE, KEY_STOPCD, KEY_PREVIOUSSONG, KEY_NEXTSONG, KEY_EJECTCD, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE,
KEY_WWW, KEY_BACK, KEY_FORWARD, KEY_STOP, KEY_FIND, KEY_SCROLLUP, KEY_SCROLLDOWN, KEY_EDIT, KEY_SLEEP,
KEY_SCREENLOCK, KEY_REFRESH, KEY_CALC, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED
};
static void dc_scan_kbd(struct dc_kbd *kbd)
{
struct input_dev *dev = kbd->dev;
void *ptr;
int code, keycode;
int i;
for (i = 0; i < 8; i++) {
code = i + 224;
keycode = kbd->keycode[code];
input_event(dev, EV_MSC, MSC_SCAN, code);
input_report_key(dev, keycode, (kbd->new[0] >> i) & 1);
}
for (i = 2; i < 8; i++) {
ptr = memchr(kbd->new + 2, kbd->old[i], 6);
code = kbd->old[i];
if (code > 3 && ptr == NULL) {
keycode = kbd->keycode[code];
if (keycode) {
input_event(dev, EV_MSC, MSC_SCAN, code);
input_report_key(dev, keycode, 0);
} else
printk(KERN_DEBUG "maple_keyb: "
"Unknown key (scancode %#x) released.",
code);
}
ptr = memchr(kbd->old + 2, kbd->new[i], 6);
code = kbd->new[i];
if (code > 3 && ptr) {
keycode = kbd->keycode[code];
if (keycode) {
input_event(dev, EV_MSC, MSC_SCAN, code);
input_report_key(dev, keycode, 1);
} else
printk(KERN_DEBUG "maple_keyb: "
"Unknown key (scancode %#x) pressed.",
code);
}
}
input_sync(dev);
memcpy(kbd->old, kbd->new, 8);
}
static void dc_kbd_callback(struct mapleq *mq)
{
struct maple_device *mapledev = mq->dev;
struct dc_kbd *kbd = mapledev->private_data;
unsigned long *buf = mq->recvbuf;
/*
* We should always be getting the lock because the only
* time it may be locked if driver is in cleanup phase.
*/
if (likely(mutex_trylock(&maple_keyb_mutex))) {
if (buf[1] == mapledev->function) {
memcpy(kbd->new, buf + 2, 8);
dc_scan_kbd(kbd);
}
mutex_unlock(&maple_keyb_mutex);
}
}
static int dc_kbd_connect(struct maple_device *mdev)
{
int i, error;
struct dc_kbd *kbd;
struct input_dev *dev;
if (!(mdev->function & MAPLE_FUNC_KEYBOARD))
return -EINVAL;
kbd = kzalloc(sizeof(struct dc_kbd), GFP_KERNEL);
dev = input_allocate_device();
if (!kbd || !dev) {
error = -ENOMEM;
goto fail;
}
mdev->private_data = kbd;
kbd->dev = dev;
memcpy(kbd->keycode, dc_kbd_keycode, sizeof(kbd->keycode));
dev->name = mdev->product_name;
dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
dev->keycode = kbd->keycode;
dev->keycodesize = sizeof (unsigned short);
dev->keycodemax = ARRAY_SIZE(kbd->keycode);
dev->id.bustype = BUS_HOST;
dev->dev.parent = &mdev->dev;
for (i = 0; i < NR_SCANCODES; i++)
__set_bit(dc_kbd_keycode[i], dev->keybit);
__clear_bit(KEY_RESERVED, dev->keybit);
input_set_capability(dev, EV_MSC, MSC_SCAN);
input_set_drvdata(dev, kbd);
error = input_register_device(dev);
if (error)
goto fail;
/* Maple polling is locked to VBLANK - which may be just 50/s */
maple_getcond_callback(mdev, dc_kbd_callback, HZ/50, MAPLE_FUNC_KEYBOARD);
return 0;
fail:
input_free_device(dev);
kfree(kbd);
mdev->private_data = NULL;
return error;
}
static void dc_kbd_disconnect(struct maple_device *mdev)
{
struct dc_kbd *kbd;
mutex_lock(&maple_keyb_mutex);
kbd = mdev->private_data;
mdev->private_data = NULL;
input_unregister_device(kbd->dev);
kfree(kbd);
mutex_unlock(&maple_keyb_mutex);
}
/* allow the keyboard to be used */
static int probe_maple_kbd(struct device *dev)
{
struct maple_device *mdev = to_maple_dev(dev);
struct maple_driver *mdrv = to_maple_driver(dev->driver);
int error;
error = dc_kbd_connect(mdev);
if (error)
return error;
mdev->driver = mdrv;
mdev->registered = 1;
return 0;
}
static struct maple_driver dc_kbd_driver = {
.function = MAPLE_FUNC_KEYBOARD,
.connect = dc_kbd_connect,
.disconnect = dc_kbd_disconnect,
.drv = {
.name = "Dreamcast_keyboard",
.probe = probe_maple_kbd,
},
};
static int __init dc_kbd_init(void)
{
return maple_driver_register(&dc_kbd_driver.drv);
}
static void __exit dc_kbd_exit(void)
{
driver_unregister(&dc_kbd_driver.drv);
}
module_init(dc_kbd_init);
module_exit(dc_kbd_exit);
...@@ -233,7 +233,7 @@ static void omap_kp_tasklet(unsigned long data) ...@@ -233,7 +233,7 @@ static void omap_kp_tasklet(unsigned long data)
omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
kp_cur_group = -1; kp_cur_group = -1;
} }
} }
} }
static ssize_t omap_kp_enable_show(struct device *dev, static ssize_t omap_kp_enable_show(struct device *dev,
...@@ -318,7 +318,7 @@ static int __init omap_kp_probe(struct platform_device *pdev) ...@@ -318,7 +318,7 @@ static int __init omap_kp_probe(struct platform_device *pdev)
keymap = pdata->keymap; keymap = pdata->keymap;
if (pdata->rep) if (pdata->rep)
set_bit(EV_REP, input_dev->evbit); __set_bit(EV_REP, input_dev->evbit);
if (pdata->delay) if (pdata->delay)
omap_kp->delay = pdata->delay; omap_kp->delay = pdata->delay;
...@@ -365,9 +365,9 @@ static int __init omap_kp_probe(struct platform_device *pdev) ...@@ -365,9 +365,9 @@ static int __init omap_kp_probe(struct platform_device *pdev)
goto err2; goto err2;
/* setup input device */ /* setup input device */
set_bit(EV_KEY, input_dev->evbit); __set_bit(EV_KEY, input_dev->evbit);
for (i = 0; keymap[i] != 0; i++) for (i = 0; keymap[i] != 0; i++)
set_bit(keymap[i] & KEY_MAX, input_dev->keybit); __set_bit(keymap[i] & KEY_MAX, input_dev->keybit);
input_dev->name = "omap-keypad"; input_dev->name = "omap-keypad";
input_dev->phys = "omap-keypad/input0"; input_dev->phys = "omap-keypad/input0";
input_dev->dev.parent = &pdev->dev; input_dev->dev.parent = &pdev->dev;
...@@ -377,10 +377,6 @@ static int __init omap_kp_probe(struct platform_device *pdev) ...@@ -377,10 +377,6 @@ static int __init omap_kp_probe(struct platform_device *pdev)
input_dev->id.product = 0x0001; input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100; input_dev->id.version = 0x0100;
input_dev->keycode = keymap;
input_dev->keycodesize = sizeof(unsigned int);
input_dev->keycodemax = pdata->keymapsize;
ret = input_register_device(omap_kp->input); ret = input_register_device(omap_kp->input);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "Unable to register omap-keypad input device\n"); printk(KERN_ERR "Unable to register omap-keypad input device\n");
...@@ -403,15 +399,15 @@ static int __init omap_kp_probe(struct platform_device *pdev) ...@@ -403,15 +399,15 @@ static int __init omap_kp_probe(struct platform_device *pdev)
} else { } else {
for (irq_idx = 0; irq_idx < omap_kp->rows; irq_idx++) { for (irq_idx = 0; irq_idx < omap_kp->rows; irq_idx++) {
if (request_irq(OMAP_GPIO_IRQ(row_gpios[irq_idx]), if (request_irq(OMAP_GPIO_IRQ(row_gpios[irq_idx]),
omap_kp_interrupt, omap_kp_interrupt,
IRQF_TRIGGER_FALLING, IRQF_TRIGGER_FALLING,
"omap-keypad", omap_kp) < 0) "omap-keypad", omap_kp) < 0)
goto err5; goto err5;
} }
} }
return 0; return 0;
err5: err5:
for (i = irq_idx-1; i >=0; i--) for (i = irq_idx - 1; i >=0; i--)
free_irq(row_gpios[i], 0); free_irq(row_gpios[i], 0);
err4: err4:
input_unregister_device(omap_kp->input); input_unregister_device(omap_kp->input);
...@@ -440,9 +436,9 @@ static int omap_kp_remove(struct platform_device *pdev) ...@@ -440,9 +436,9 @@ static int omap_kp_remove(struct platform_device *pdev)
if (cpu_is_omap24xx()) { if (cpu_is_omap24xx()) {
int i; int i;
for (i = 0; i < omap_kp->cols; i++) for (i = 0; i < omap_kp->cols; i++)
omap_free_gpio(col_gpios[i]); omap_free_gpio(col_gpios[i]);
for (i = 0; i < omap_kp->rows; i++) { for (i = 0; i < omap_kp->rows; i++) {
omap_free_gpio(row_gpios[i]); omap_free_gpio(row_gpios[i]);
free_irq(OMAP_GPIO_IRQ(row_gpios[i]), 0); free_irq(OMAP_GPIO_IRQ(row_gpios[i]), 0);
} }
} else { } else {
......
...@@ -48,11 +48,13 @@ static const struct alps_model_info alps_model_data[] = { ...@@ -48,11 +48,13 @@ static const struct alps_model_info alps_model_data[] = {
{ { 0x63, 0x02, 0x50 }, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */ { { 0x63, 0x02, 0x50 }, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */
{ { 0x63, 0x02, 0x64 }, 0xf8, 0xf8, 0 }, { { 0x63, 0x02, 0x64 }, 0xf8, 0xf8, 0 },
{ { 0x63, 0x03, 0xc8 }, 0xf8, 0xf8, ALPS_PASS }, /* Dell Latitude D800 */ { { 0x63, 0x03, 0xc8 }, 0xf8, 0xf8, ALPS_PASS }, /* Dell Latitude D800 */
{ { 0x73, 0x00, 0x0a }, 0xf8, 0xf8, ALPS_DUALPOINT }, /* ThinkPad R61 8918-5QG */
{ { 0x73, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, { { 0x73, 0x02, 0x0a }, 0xf8, 0xf8, 0 },
{ { 0x73, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */ { { 0x73, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */
{ { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */ { { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */
{ { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, { { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },
{ { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ { { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */
{ { 0x73, 0x02, 0x50 }, 0xcf, 0xff, ALPS_FW_BK_1 } /* Dell Vostro 1400 */
}; };
/* /*
......
...@@ -502,18 +502,23 @@ static void atp_complete(struct urb* urb) ...@@ -502,18 +502,23 @@ static void atp_complete(struct urb* urb)
/* reset the accumulator on release */ /* reset the accumulator on release */
memset(dev->xy_acc, 0, sizeof(dev->xy_acc)); memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
}
/* Geyser 3 will continue to send packets continually after
the first touch unless reinitialised. Do so if it's been
idle for a while in order to avoid waking the kernel up
several hundred times a second */
/* Geyser 3 will continue to send packets continually after if (atp_is_geyser_3(dev)) {
the first touch unless reinitialised. Do so if it's been if (!x && !y && !key) {
idle for a while in order to avoid waking the kernel up
several hundred times a second */
if (!key && atp_is_geyser_3(dev)) {
dev->idlecount++; dev->idlecount++;
if (dev->idlecount == 10) { if (dev->idlecount == 10) {
dev->valid = 0; dev->valid = 0;
schedule_work(&dev->work); schedule_work(&dev->work);
} }
} }
else
dev->idlecount = 0;
} }
input_report_key(dev->input, BTN_LEFT, key); input_report_key(dev->input, BTN_LEFT, key);
......
...@@ -96,6 +96,14 @@ static const struct dmi_system_id lifebook_dmi_table[] = { ...@@ -96,6 +96,14 @@ static const struct dmi_system_id lifebook_dmi_table[] = {
}, },
.callback = lifebook_set_6byte_proto, .callback = lifebook_set_6byte_proto,
}, },
{
.ident = "CF-72",
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "CF-72"),
},
.callback = lifebook_set_serio_phys,
.driver_data = "isa0060/serio3",
},
{ {
.ident = "Lifebook B142", .ident = "Lifebook B142",
.matches = { .matches = {
...@@ -282,7 +290,7 @@ static int lifebook_create_relative_device(struct psmouse *psmouse) ...@@ -282,7 +290,7 @@ static int lifebook_create_relative_device(struct psmouse *psmouse)
int lifebook_init(struct psmouse *psmouse) int lifebook_init(struct psmouse *psmouse)
{ {
struct input_dev *dev1 = psmouse->dev; struct input_dev *dev1 = psmouse->dev;
int max_coord = lifebook_use_6byte_proto ? 1024 : 4096; int max_coord = lifebook_use_6byte_proto ? 4096 : 1024;
if (lifebook_absolute_mode(psmouse)) if (lifebook_absolute_mode(psmouse))
return -1; return -1;
......
...@@ -648,9 +648,10 @@ static int psmouse_extensions(struct psmouse *psmouse, ...@@ -648,9 +648,10 @@ static int psmouse_extensions(struct psmouse *psmouse,
/* /*
* Reset to defaults in case the device got confused by extended * Reset to defaults in case the device got confused by extended
* protocol probes. Note that we do full reset becuase some mice * protocol probes. Note that we follow up with full reset because
* put themselves to sleep when see PSMOUSE_RESET_DIS. * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS.
*/ */
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
psmouse_reset(psmouse); psmouse_reset(psmouse);
if (max_proto >= PSMOUSE_IMEX && im_explorer_detect(psmouse, set_properties) == 0) if (max_proto >= PSMOUSE_IMEX && im_explorer_detect(psmouse, set_properties) == 0)
......
...@@ -61,9 +61,11 @@ struct mousedev { ...@@ -61,9 +61,11 @@ struct mousedev {
int open; int open;
int minor; int minor;
char name[16]; char name[16];
struct input_handle handle;
wait_queue_head_t wait; wait_queue_head_t wait;
struct list_head client_list; struct list_head client_list;
struct input_handle handle; spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev; struct device dev;
struct list_head mixdev_node; struct list_head mixdev_node;
...@@ -113,108 +115,137 @@ static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 }; ...@@ -113,108 +115,137 @@ static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 };
static struct input_handler mousedev_handler; static struct input_handler mousedev_handler;
static struct mousedev *mousedev_table[MOUSEDEV_MINORS]; static struct mousedev *mousedev_table[MOUSEDEV_MINORS];
static DEFINE_MUTEX(mousedev_table_mutex);
static struct mousedev *mousedev_mix; static struct mousedev *mousedev_mix;
static LIST_HEAD(mousedev_mix_list); static LIST_HEAD(mousedev_mix_list);
static void mixdev_open_devices(void);
static void mixdev_close_devices(void);
#define fx(i) (mousedev->old_x[(mousedev->pkt_count - (i)) & 03]) #define fx(i) (mousedev->old_x[(mousedev->pkt_count - (i)) & 03])
#define fy(i) (mousedev->old_y[(mousedev->pkt_count - (i)) & 03]) #define fy(i) (mousedev->old_y[(mousedev->pkt_count - (i)) & 03])
static void mousedev_touchpad_event(struct input_dev *dev, struct mousedev *mousedev, unsigned int code, int value) static void mousedev_touchpad_event(struct input_dev *dev,
struct mousedev *mousedev,
unsigned int code, int value)
{ {
int size, tmp; int size, tmp;
enum { FRACTION_DENOM = 128 }; enum { FRACTION_DENOM = 128 };
switch (code) { switch (code) {
case ABS_X:
fx(0) = value;
if (mousedev->touch && mousedev->pkt_count >= 2) {
size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
if (size == 0)
size = 256 * 2;
tmp = ((value - fx(2)) * (256 * FRACTION_DENOM)) / size;
tmp += mousedev->frac_dx;
mousedev->packet.dx = tmp / FRACTION_DENOM;
mousedev->frac_dx = tmp - mousedev->packet.dx * FRACTION_DENOM;
}
break;
case ABS_Y: case ABS_X:
fy(0) = value; fx(0) = value;
if (mousedev->touch && mousedev->pkt_count >= 2) { if (mousedev->touch && mousedev->pkt_count >= 2) {
/* use X size to keep the same scale */ size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
size = dev->absmax[ABS_X] - dev->absmin[ABS_X]; if (size == 0)
if (size == 0) size = 256 * 2;
size = 256 * 2; tmp = ((value - fx(2)) * 256 * FRACTION_DENOM) / size;
tmp = -((value - fy(2)) * (256 * FRACTION_DENOM)) / size; tmp += mousedev->frac_dx;
tmp += mousedev->frac_dy; mousedev->packet.dx = tmp / FRACTION_DENOM;
mousedev->packet.dy = tmp / FRACTION_DENOM; mousedev->frac_dx =
mousedev->frac_dy = tmp - mousedev->packet.dy * FRACTION_DENOM; tmp - mousedev->packet.dx * FRACTION_DENOM;
} }
break; break;
case ABS_Y:
fy(0) = value;
if (mousedev->touch && mousedev->pkt_count >= 2) {
/* use X size to keep the same scale */
size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
if (size == 0)
size = 256 * 2;
tmp = -((value - fy(2)) * 256 * FRACTION_DENOM) / size;
tmp += mousedev->frac_dy;
mousedev->packet.dy = tmp / FRACTION_DENOM;
mousedev->frac_dy = tmp -
mousedev->packet.dy * FRACTION_DENOM;
}
break;
} }
} }
static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev, unsigned int code, int value) static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev,
unsigned int code, int value)
{ {
int size; int size;
switch (code) { switch (code) {
case ABS_X:
size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
if (size == 0)
size = xres ? : 1;
if (value > dev->absmax[ABS_X])
value = dev->absmax[ABS_X];
if (value < dev->absmin[ABS_X])
value = dev->absmin[ABS_X];
mousedev->packet.x = ((value - dev->absmin[ABS_X]) * xres) / size;
mousedev->packet.abs_event = 1;
break;
case ABS_Y: case ABS_X:
size = dev->absmax[ABS_Y] - dev->absmin[ABS_Y]; size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
if (size == 0) if (size == 0)
size = yres ? : 1; size = xres ? : 1;
if (value > dev->absmax[ABS_Y]) if (value > dev->absmax[ABS_X])
value = dev->absmax[ABS_Y]; value = dev->absmax[ABS_X];
if (value < dev->absmin[ABS_Y]) if (value < dev->absmin[ABS_X])
value = dev->absmin[ABS_Y]; value = dev->absmin[ABS_X];
mousedev->packet.y = yres - ((value - dev->absmin[ABS_Y]) * yres) / size; mousedev->packet.x =
mousedev->packet.abs_event = 1; ((value - dev->absmin[ABS_X]) * xres) / size;
break; mousedev->packet.abs_event = 1;
break;
case ABS_Y:
size = dev->absmax[ABS_Y] - dev->absmin[ABS_Y];
if (size == 0)
size = yres ? : 1;
if (value > dev->absmax[ABS_Y])
value = dev->absmax[ABS_Y];
if (value < dev->absmin[ABS_Y])
value = dev->absmin[ABS_Y];
mousedev->packet.y = yres -
((value - dev->absmin[ABS_Y]) * yres) / size;
mousedev->packet.abs_event = 1;
break;
} }
} }
static void mousedev_rel_event(struct mousedev *mousedev, unsigned int code, int value) static void mousedev_rel_event(struct mousedev *mousedev,
unsigned int code, int value)
{ {
switch (code) { switch (code) {
case REL_X: mousedev->packet.dx += value; break; case REL_X:
case REL_Y: mousedev->packet.dy -= value; break; mousedev->packet.dx += value;
case REL_WHEEL: mousedev->packet.dz -= value; break; break;
case REL_Y:
mousedev->packet.dy -= value;
break;
case REL_WHEEL:
mousedev->packet.dz -= value;
break;
} }
} }
static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int value) static void mousedev_key_event(struct mousedev *mousedev,
unsigned int code, int value)
{ {
int index; int index;
switch (code) { switch (code) {
case BTN_TOUCH:
case BTN_0: case BTN_TOUCH:
case BTN_LEFT: index = 0; break; case BTN_0:
case BTN_STYLUS: case BTN_LEFT: index = 0; break;
case BTN_1:
case BTN_RIGHT: index = 1; break; case BTN_STYLUS:
case BTN_2: case BTN_1:
case BTN_FORWARD: case BTN_RIGHT: index = 1; break;
case BTN_STYLUS2:
case BTN_MIDDLE: index = 2; break; case BTN_2:
case BTN_3: case BTN_FORWARD:
case BTN_BACK: case BTN_STYLUS2:
case BTN_SIDE: index = 3; break; case BTN_MIDDLE: index = 2; break;
case BTN_4:
case BTN_EXTRA: index = 4; break; case BTN_3:
default: return; case BTN_BACK:
case BTN_SIDE: index = 3; break;
case BTN_4:
case BTN_EXTRA: index = 4; break;
default: return;
} }
if (value) { if (value) {
...@@ -226,19 +257,23 @@ static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int ...@@ -226,19 +257,23 @@ static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int
} }
} }
static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_hw_data *packet) static void mousedev_notify_readers(struct mousedev *mousedev,
struct mousedev_hw_data *packet)
{ {
struct mousedev_client *client; struct mousedev_client *client;
struct mousedev_motion *p; struct mousedev_motion *p;
unsigned long flags; unsigned int new_head;
int wake_readers = 0; int wake_readers = 0;
list_for_each_entry(client, &mousedev->client_list, node) { rcu_read_lock();
spin_lock_irqsave(&client->packet_lock, flags); list_for_each_entry_rcu(client, &mousedev->client_list, node) {
/* Just acquire the lock, interrupts already disabled */
spin_lock(&client->packet_lock);
p = &client->packets[client->head]; p = &client->packets[client->head];
if (client->ready && p->buttons != mousedev->packet.buttons) { if (client->ready && p->buttons != mousedev->packet.buttons) {
unsigned int new_head = (client->head + 1) % PACKET_QUEUE_LEN; new_head = (client->head + 1) % PACKET_QUEUE_LEN;
if (new_head != client->tail) { if (new_head != client->tail) {
p = &client->packets[client->head = new_head]; p = &client->packets[client->head = new_head];
memset(p, 0, sizeof(struct mousedev_motion)); memset(p, 0, sizeof(struct mousedev_motion));
...@@ -253,25 +288,29 @@ static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_h ...@@ -253,25 +288,29 @@ static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_h
} }
client->pos_x += packet->dx; client->pos_x += packet->dx;
client->pos_x = client->pos_x < 0 ? 0 : (client->pos_x >= xres ? xres : client->pos_x); client->pos_x = client->pos_x < 0 ?
0 : (client->pos_x >= xres ? xres : client->pos_x);
client->pos_y += packet->dy; client->pos_y += packet->dy;
client->pos_y = client->pos_y < 0 ? 0 : (client->pos_y >= yres ? yres : client->pos_y); client->pos_y = client->pos_y < 0 ?
0 : (client->pos_y >= yres ? yres : client->pos_y);
p->dx += packet->dx; p->dx += packet->dx;
p->dy += packet->dy; p->dy += packet->dy;
p->dz += packet->dz; p->dz += packet->dz;
p->buttons = mousedev->packet.buttons; p->buttons = mousedev->packet.buttons;
if (p->dx || p->dy || p->dz || p->buttons != client->last_buttons) if (p->dx || p->dy || p->dz ||
p->buttons != client->last_buttons)
client->ready = 1; client->ready = 1;
spin_unlock_irqrestore(&client->packet_lock, flags); spin_unlock(&client->packet_lock);
if (client->ready) { if (client->ready) {
kill_fasync(&client->fasync, SIGIO, POLL_IN); kill_fasync(&client->fasync, SIGIO, POLL_IN);
wake_readers = 1; wake_readers = 1;
} }
} }
rcu_read_unlock();
if (wake_readers) if (wake_readers)
wake_up_interruptible(&mousedev->wait); wake_up_interruptible(&mousedev->wait);
...@@ -281,7 +320,8 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value) ...@@ -281,7 +320,8 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value)
{ {
if (!value) { if (!value) {
if (mousedev->touch && if (mousedev->touch &&
time_before(jiffies, mousedev->touch + msecs_to_jiffies(tap_time))) { time_before(jiffies,
mousedev->touch + msecs_to_jiffies(tap_time))) {
/* /*
* Toggle left button to emulate tap. * Toggle left button to emulate tap.
* We rely on the fact that mousedev_mix always has 0 * We rely on the fact that mousedev_mix always has 0
...@@ -290,7 +330,8 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value) ...@@ -290,7 +330,8 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value)
set_bit(0, &mousedev->packet.buttons); set_bit(0, &mousedev->packet.buttons);
set_bit(0, &mousedev_mix->packet.buttons); set_bit(0, &mousedev_mix->packet.buttons);
mousedev_notify_readers(mousedev, &mousedev_mix->packet); mousedev_notify_readers(mousedev, &mousedev_mix->packet);
mousedev_notify_readers(mousedev_mix, &mousedev_mix->packet); mousedev_notify_readers(mousedev_mix,
&mousedev_mix->packet);
clear_bit(0, &mousedev->packet.buttons); clear_bit(0, &mousedev->packet.buttons);
clear_bit(0, &mousedev_mix->packet.buttons); clear_bit(0, &mousedev_mix->packet.buttons);
} }
...@@ -302,54 +343,61 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value) ...@@ -302,54 +343,61 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value)
mousedev->touch = jiffies; mousedev->touch = jiffies;
} }
static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) static void mousedev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{ {
struct mousedev *mousedev = handle->private; struct mousedev *mousedev = handle->private;
switch (type) { switch (type) {
case EV_ABS:
/* Ignore joysticks */
if (test_bit(BTN_TRIGGER, handle->dev->keybit))
return;
if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit)) case EV_ABS:
mousedev_touchpad_event(handle->dev, mousedev, code, value); /* Ignore joysticks */
else if (test_bit(BTN_TRIGGER, handle->dev->keybit))
mousedev_abs_event(handle->dev, mousedev, code, value); return;
break; if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
mousedev_touchpad_event(handle->dev,
mousedev, code, value);
else
mousedev_abs_event(handle->dev, mousedev, code, value);
case EV_REL: break;
mousedev_rel_event(mousedev, code, value);
break;
case EV_KEY: case EV_REL:
if (value != 2) { mousedev_rel_event(mousedev, code, value);
if (code == BTN_TOUCH && test_bit(BTN_TOOL_FINGER, handle->dev->keybit)) break;
mousedev_touchpad_touch(mousedev, value);
else
mousedev_key_event(mousedev, code, value);
}
break;
case EV_SYN: case EV_KEY:
if (code == SYN_REPORT) { if (value != 2) {
if (mousedev->touch) { if (code == BTN_TOUCH &&
mousedev->pkt_count++; test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
/* Input system eats duplicate events, but we need all of them mousedev_touchpad_touch(mousedev, value);
* to do correct averaging so apply present one forward else
*/ mousedev_key_event(mousedev, code, value);
fx(0) = fx(1); }
fy(0) = fy(1); break;
}
case EV_SYN:
mousedev_notify_readers(mousedev, &mousedev->packet); if (code == SYN_REPORT) {
mousedev_notify_readers(mousedev_mix, &mousedev->packet); if (mousedev->touch) {
mousedev->pkt_count++;
mousedev->packet.dx = mousedev->packet.dy = mousedev->packet.dz = 0; /*
mousedev->packet.abs_event = 0; * Input system eats duplicate events,
* but we need all of them to do correct
* averaging so apply present one forward
*/
fx(0) = fx(1);
fy(0) = fy(1);
} }
break;
mousedev_notify_readers(mousedev, &mousedev->packet);
mousedev_notify_readers(mousedev_mix, &mousedev->packet);
mousedev->packet.dx = mousedev->packet.dy =
mousedev->packet.dz = 0;
mousedev->packet.abs_event = 0;
}
break;
} }
} }
...@@ -367,41 +415,48 @@ static void mousedev_free(struct device *dev) ...@@ -367,41 +415,48 @@ static void mousedev_free(struct device *dev)
{ {
struct mousedev *mousedev = container_of(dev, struct mousedev, dev); struct mousedev *mousedev = container_of(dev, struct mousedev, dev);
mousedev_table[mousedev->minor] = NULL;
kfree(mousedev); kfree(mousedev);
} }
static int mixdev_add_device(struct mousedev *mousedev) static int mousedev_open_device(struct mousedev *mousedev)
{ {
int error; int retval;
if (mousedev_mix->open) { retval = mutex_lock_interruptible(&mousedev->mutex);
error = input_open_device(&mousedev->handle); if (retval)
if (error) return retval;
return error;
mousedev->open++; if (mousedev->minor == MOUSEDEV_MIX)
mousedev->mixdev_open = 1; mixdev_open_devices();
else if (!mousedev->exist)
retval = -ENODEV;
else if (!mousedev->open++) {
retval = input_open_device(&mousedev->handle);
if (retval)
mousedev->open--;
} }
get_device(&mousedev->dev); mutex_unlock(&mousedev->mutex);
list_add_tail(&mousedev->mixdev_node, &mousedev_mix_list); return retval;
return 0;
} }
static void mixdev_remove_device(struct mousedev *mousedev) static void mousedev_close_device(struct mousedev *mousedev)
{ {
if (mousedev->mixdev_open) { mutex_lock(&mousedev->mutex);
mousedev->mixdev_open = 0;
if (!--mousedev->open && mousedev->exist)
input_close_device(&mousedev->handle);
}
list_del_init(&mousedev->mixdev_node); if (mousedev->minor == MOUSEDEV_MIX)
put_device(&mousedev->dev); mixdev_close_devices();
else if (mousedev->exist && !--mousedev->open)
input_close_device(&mousedev->handle);
mutex_unlock(&mousedev->mutex);
} }
/*
* Open all available devices so they can all be multiplexed in one.
* stream. Note that this function is called with mousedev_mix->mutex
* held.
*/
static void mixdev_open_devices(void) static void mixdev_open_devices(void)
{ {
struct mousedev *mousedev; struct mousedev *mousedev;
...@@ -411,16 +466,19 @@ static void mixdev_open_devices(void) ...@@ -411,16 +466,19 @@ static void mixdev_open_devices(void)
list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) { list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
if (!mousedev->mixdev_open) { if (!mousedev->mixdev_open) {
if (!mousedev->open && mousedev->exist) if (mousedev_open_device(mousedev))
if (input_open_device(&mousedev->handle)) continue;
continue;
mousedev->open++;
mousedev->mixdev_open = 1; mousedev->mixdev_open = 1;
} }
} }
} }
/*
* Close all devices that were opened as part of multiplexed
* device. Note that this function is called with mousedev_mix->mutex
* held.
*/
static void mixdev_close_devices(void) static void mixdev_close_devices(void)
{ {
struct mousedev *mousedev; struct mousedev *mousedev;
...@@ -431,33 +489,45 @@ static void mixdev_close_devices(void) ...@@ -431,33 +489,45 @@ static void mixdev_close_devices(void)
list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) { list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
if (mousedev->mixdev_open) { if (mousedev->mixdev_open) {
mousedev->mixdev_open = 0; mousedev->mixdev_open = 0;
if (!--mousedev->open && mousedev->exist) mousedev_close_device(mousedev);
input_close_device(&mousedev->handle);
} }
} }
} }
static void mousedev_attach_client(struct mousedev *mousedev,
struct mousedev_client *client)
{
spin_lock(&mousedev->client_lock);
list_add_tail_rcu(&client->node, &mousedev->client_list);
spin_unlock(&mousedev->client_lock);
synchronize_rcu();
}
static void mousedev_detach_client(struct mousedev *mousedev,
struct mousedev_client *client)
{
spin_lock(&mousedev->client_lock);
list_del_rcu(&client->node);
spin_unlock(&mousedev->client_lock);
synchronize_rcu();
}
static int mousedev_release(struct inode *inode, struct file *file) static int mousedev_release(struct inode *inode, struct file *file)
{ {
struct mousedev_client *client = file->private_data; struct mousedev_client *client = file->private_data;
struct mousedev *mousedev = client->mousedev; struct mousedev *mousedev = client->mousedev;
mousedev_fasync(-1, file, 0); mousedev_fasync(-1, file, 0);
mousedev_detach_client(mousedev, client);
list_del(&client->node);
kfree(client); kfree(client);
if (mousedev->minor == MOUSEDEV_MIX) mousedev_close_device(mousedev);
mixdev_close_devices();
else if (!--mousedev->open && mousedev->exist)
input_close_device(&mousedev->handle);
put_device(&mousedev->dev); put_device(&mousedev->dev);
return 0; return 0;
} }
static int mousedev_open(struct inode *inode, struct file *file) static int mousedev_open(struct inode *inode, struct file *file)
{ {
struct mousedev_client *client; struct mousedev_client *client;
...@@ -475,12 +545,17 @@ static int mousedev_open(struct inode *inode, struct file *file) ...@@ -475,12 +545,17 @@ static int mousedev_open(struct inode *inode, struct file *file)
if (i >= MOUSEDEV_MINORS) if (i >= MOUSEDEV_MINORS)
return -ENODEV; return -ENODEV;
error = mutex_lock_interruptible(&mousedev_table_mutex);
if (error)
return error;
mousedev = mousedev_table[i]; mousedev = mousedev_table[i];
if (mousedev)
get_device(&mousedev->dev);
mutex_unlock(&mousedev_table_mutex);
if (!mousedev) if (!mousedev)
return -ENODEV; return -ENODEV;
get_device(&mousedev->dev);
client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL); client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL);
if (!client) { if (!client) {
error = -ENOMEM; error = -ENOMEM;
...@@ -491,21 +566,17 @@ static int mousedev_open(struct inode *inode, struct file *file) ...@@ -491,21 +566,17 @@ static int mousedev_open(struct inode *inode, struct file *file)
client->pos_x = xres / 2; client->pos_x = xres / 2;
client->pos_y = yres / 2; client->pos_y = yres / 2;
client->mousedev = mousedev; client->mousedev = mousedev;
list_add_tail(&client->node, &mousedev->client_list); mousedev_attach_client(mousedev, client);
if (mousedev->minor == MOUSEDEV_MIX) error = mousedev_open_device(mousedev);
mixdev_open_devices(); if (error)
else if (!mousedev->open++ && mousedev->exist) { goto err_free_client;
error = input_open_device(&mousedev->handle);
if (error)
goto err_free_client;
}
file->private_data = client; file->private_data = client;
return 0; return 0;
err_free_client: err_free_client:
list_del(&client->node); mousedev_detach_client(mousedev, client);
kfree(client); kfree(client);
err_put_mousedev: err_put_mousedev:
put_device(&mousedev->dev); put_device(&mousedev->dev);
...@@ -517,41 +588,41 @@ static inline int mousedev_limit_delta(int delta, int limit) ...@@ -517,41 +588,41 @@ static inline int mousedev_limit_delta(int delta, int limit)
return delta > limit ? limit : (delta < -limit ? -limit : delta); return delta > limit ? limit : (delta < -limit ? -limit : delta);
} }
static void mousedev_packet(struct mousedev_client *client, signed char *ps2_data) static void mousedev_packet(struct mousedev_client *client,
signed char *ps2_data)
{ {
struct mousedev_motion *p; struct mousedev_motion *p = &client->packets[client->tail];
unsigned long flags;
spin_lock_irqsave(&client->packet_lock, flags);
p = &client->packets[client->tail];
ps2_data[0] = 0x08 | ((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07); ps2_data[0] = 0x08 |
((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07);
ps2_data[1] = mousedev_limit_delta(p->dx, 127); ps2_data[1] = mousedev_limit_delta(p->dx, 127);
ps2_data[2] = mousedev_limit_delta(p->dy, 127); ps2_data[2] = mousedev_limit_delta(p->dy, 127);
p->dx -= ps2_data[1]; p->dx -= ps2_data[1];
p->dy -= ps2_data[2]; p->dy -= ps2_data[2];
switch (client->mode) { switch (client->mode) {
case MOUSEDEV_EMUL_EXPS: case MOUSEDEV_EMUL_EXPS:
ps2_data[3] = mousedev_limit_delta(p->dz, 7); ps2_data[3] = mousedev_limit_delta(p->dz, 7);
p->dz -= ps2_data[3]; p->dz -= ps2_data[3];
ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1); ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1);
client->bufsiz = 4; client->bufsiz = 4;
break; break;
case MOUSEDEV_EMUL_IMPS: case MOUSEDEV_EMUL_IMPS:
ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1); ps2_data[0] |=
ps2_data[3] = mousedev_limit_delta(p->dz, 127); ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
p->dz -= ps2_data[3]; ps2_data[3] = mousedev_limit_delta(p->dz, 127);
client->bufsiz = 4; p->dz -= ps2_data[3];
break; client->bufsiz = 4;
break;
case MOUSEDEV_EMUL_PS2:
default: case MOUSEDEV_EMUL_PS2:
ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1); default:
p->dz = 0; ps2_data[0] |=
client->bufsiz = 3; ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
break; p->dz = 0;
client->bufsiz = 3;
break;
} }
if (!p->dx && !p->dy && !p->dz) { if (!p->dx && !p->dy && !p->dz) {
...@@ -561,12 +632,56 @@ static void mousedev_packet(struct mousedev_client *client, signed char *ps2_dat ...@@ -561,12 +632,56 @@ static void mousedev_packet(struct mousedev_client *client, signed char *ps2_dat
} else } else
client->tail = (client->tail + 1) % PACKET_QUEUE_LEN; client->tail = (client->tail + 1) % PACKET_QUEUE_LEN;
} }
spin_unlock_irqrestore(&client->packet_lock, flags);
} }
static void mousedev_generate_response(struct mousedev_client *client,
int command)
{
client->ps2[0] = 0xfa; /* ACK */
switch (command) {
static ssize_t mousedev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) case 0xeb: /* Poll */
mousedev_packet(client, &client->ps2[1]);
client->bufsiz++; /* account for leading ACK */
break;
case 0xf2: /* Get ID */
switch (client->mode) {
case MOUSEDEV_EMUL_PS2:
client->ps2[1] = 0;
break;
case MOUSEDEV_EMUL_IMPS:
client->ps2[1] = 3;
break;
case MOUSEDEV_EMUL_EXPS:
client->ps2[1] = 4;
break;
}
client->bufsiz = 2;
break;
case 0xe9: /* Get info */
client->ps2[1] = 0x60; client->ps2[2] = 3; client->ps2[3] = 200;
client->bufsiz = 4;
break;
case 0xff: /* Reset */
client->impsseq = client->imexseq = 0;
client->mode = MOUSEDEV_EMUL_PS2;
client->ps2[1] = 0xaa; client->ps2[2] = 0x00;
client->bufsiz = 3;
break;
default:
client->bufsiz = 1;
break;
}
client->buffer = client->bufsiz;
}
static ssize_t mousedev_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{ {
struct mousedev_client *client = file->private_data; struct mousedev_client *client = file->private_data;
unsigned char c; unsigned char c;
...@@ -577,6 +692,8 @@ static ssize_t mousedev_write(struct file *file, const char __user *buffer, size ...@@ -577,6 +692,8 @@ static ssize_t mousedev_write(struct file *file, const char __user *buffer, size
if (get_user(c, buffer + i)) if (get_user(c, buffer + i))
return -EFAULT; return -EFAULT;
spin_lock_irq(&client->packet_lock);
if (c == mousedev_imex_seq[client->imexseq]) { if (c == mousedev_imex_seq[client->imexseq]) {
if (++client->imexseq == MOUSEDEV_SEQ_LEN) { if (++client->imexseq == MOUSEDEV_SEQ_LEN) {
client->imexseq = 0; client->imexseq = 0;
...@@ -593,68 +710,39 @@ static ssize_t mousedev_write(struct file *file, const char __user *buffer, size ...@@ -593,68 +710,39 @@ static ssize_t mousedev_write(struct file *file, const char __user *buffer, size
} else } else
client->impsseq = 0; client->impsseq = 0;
client->ps2[0] = 0xfa; mousedev_generate_response(client, c);
switch (c) {
case 0xeb: /* Poll */
mousedev_packet(client, &client->ps2[1]);
client->bufsiz++; /* account for leading ACK */
break;
case 0xf2: /* Get ID */
switch (client->mode) {
case MOUSEDEV_EMUL_PS2: client->ps2[1] = 0; break;
case MOUSEDEV_EMUL_IMPS: client->ps2[1] = 3; break;
case MOUSEDEV_EMUL_EXPS: client->ps2[1] = 4; break;
}
client->bufsiz = 2;
break;
case 0xe9: /* Get info */
client->ps2[1] = 0x60; client->ps2[2] = 3; client->ps2[3] = 200;
client->bufsiz = 4;
break;
case 0xff: /* Reset */
client->impsseq = client->imexseq = 0;
client->mode = MOUSEDEV_EMUL_PS2;
client->ps2[1] = 0xaa; client->ps2[2] = 0x00;
client->bufsiz = 3;
break;
default:
client->bufsiz = 1;
break;
}
client->buffer = client->bufsiz; spin_unlock_irq(&client->packet_lock);
} }
kill_fasync(&client->fasync, SIGIO, POLL_IN); kill_fasync(&client->fasync, SIGIO, POLL_IN);
wake_up_interruptible(&client->mousedev->wait); wake_up_interruptible(&client->mousedev->wait);
return count; return count;
} }
static ssize_t mousedev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) static ssize_t mousedev_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{ {
struct mousedev_client *client = file->private_data; struct mousedev_client *client = file->private_data;
struct mousedev *mousedev = client->mousedev;
signed char data[sizeof(client->ps2)];
int retval = 0; int retval = 0;
if (!client->ready && !client->buffer && (file->f_flags & O_NONBLOCK)) if (!client->ready && !client->buffer && mousedev->exist &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN; return -EAGAIN;
retval = wait_event_interruptible(client->mousedev->wait, retval = wait_event_interruptible(mousedev->wait,
!client->mousedev->exist || client->ready || client->buffer); !mousedev->exist || client->ready || client->buffer);
if (retval) if (retval)
return retval; return retval;
if (!client->mousedev->exist) if (!mousedev->exist)
return -ENODEV; return -ENODEV;
spin_lock_irq(&client->packet_lock);
if (!client->buffer && client->ready) { if (!client->buffer && client->ready) {
mousedev_packet(client, client->ps2); mousedev_packet(client, client->ps2);
client->buffer = client->bufsiz; client->buffer = client->bufsiz;
...@@ -663,9 +751,12 @@ static ssize_t mousedev_read(struct file *file, char __user *buffer, size_t coun ...@@ -663,9 +751,12 @@ static ssize_t mousedev_read(struct file *file, char __user *buffer, size_t coun
if (count > client->buffer) if (count > client->buffer)
count = client->buffer; count = client->buffer;
memcpy(data, client->ps2 + client->bufsiz - client->buffer, count);
client->buffer -= count; client->buffer -= count;
if (copy_to_user(buffer, client->ps2 + client->bufsiz - client->buffer - count, count)) spin_unlock_irq(&client->packet_lock);
if (copy_to_user(buffer, data, count))
return -EFAULT; return -EFAULT;
return count; return count;
...@@ -692,6 +783,60 @@ static const struct file_operations mousedev_fops = { ...@@ -692,6 +783,60 @@ static const struct file_operations mousedev_fops = {
.fasync = mousedev_fasync, .fasync = mousedev_fasync,
}; };
static int mousedev_install_chrdev(struct mousedev *mousedev)
{
mousedev_table[mousedev->minor] = mousedev;
return 0;
}
static void mousedev_remove_chrdev(struct mousedev *mousedev)
{
mutex_lock(&mousedev_table_mutex);
mousedev_table[mousedev->minor] = NULL;
mutex_unlock(&mousedev_table_mutex);
}
/*
* Mark device non-existent. This disables writes, ioctls and
* prevents new users from opening the device. Already posted
* blocking reads will stay, however new ones will fail.
*/
static void mousedev_mark_dead(struct mousedev *mousedev)
{
mutex_lock(&mousedev->mutex);
mousedev->exist = 0;
mutex_unlock(&mousedev->mutex);
}
/*
* Wake up users waiting for IO so they can disconnect from
* dead device.
*/
static void mousedev_hangup(struct mousedev *mousedev)
{
struct mousedev_client *client;
spin_lock(&mousedev->client_lock);
list_for_each_entry(client, &mousedev->client_list, node)
kill_fasync(&client->fasync, SIGIO, POLL_HUP);
spin_unlock(&mousedev->client_lock);
wake_up_interruptible(&mousedev->wait);
}
static void mousedev_cleanup(struct mousedev *mousedev)
{
struct input_handle *handle = &mousedev->handle;
mousedev_mark_dead(mousedev);
mousedev_hangup(mousedev);
mousedev_remove_chrdev(mousedev);
/* mousedev is marked dead so no one else accesses mousedev->open */
if (mousedev->open)
input_close_device(handle);
}
static struct mousedev *mousedev_create(struct input_dev *dev, static struct mousedev *mousedev_create(struct input_dev *dev,
struct input_handler *handler, struct input_handler *handler,
int minor) int minor)
...@@ -707,6 +852,10 @@ static struct mousedev *mousedev_create(struct input_dev *dev, ...@@ -707,6 +852,10 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
INIT_LIST_HEAD(&mousedev->client_list); INIT_LIST_HEAD(&mousedev->client_list);
INIT_LIST_HEAD(&mousedev->mixdev_node); INIT_LIST_HEAD(&mousedev->mixdev_node);
spin_lock_init(&mousedev->client_lock);
mutex_init(&mousedev->mutex);
lockdep_set_subclass(&mousedev->mutex,
minor == MOUSEDEV_MIX ? MOUSEDEV_MIX : 0);
init_waitqueue_head(&mousedev->wait); init_waitqueue_head(&mousedev->wait);
if (minor == MOUSEDEV_MIX) if (minor == MOUSEDEV_MIX)
...@@ -731,14 +880,27 @@ static struct mousedev *mousedev_create(struct input_dev *dev, ...@@ -731,14 +880,27 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
mousedev->dev.release = mousedev_free; mousedev->dev.release = mousedev_free;
device_initialize(&mousedev->dev); device_initialize(&mousedev->dev);
mousedev_table[minor] = mousedev; if (minor != MOUSEDEV_MIX) {
error = input_register_handle(&mousedev->handle);
if (error)
goto err_free_mousedev;
}
error = mousedev_install_chrdev(mousedev);
if (error)
goto err_unregister_handle;
error = device_add(&mousedev->dev); error = device_add(&mousedev->dev);
if (error) if (error)
goto err_free_mousedev; goto err_cleanup_mousedev;
return mousedev; return mousedev;
err_cleanup_mousedev:
mousedev_cleanup(mousedev);
err_unregister_handle:
if (minor != MOUSEDEV_MIX)
input_unregister_handle(&mousedev->handle);
err_free_mousedev: err_free_mousedev:
put_device(&mousedev->dev); put_device(&mousedev->dev);
err_out: err_out:
...@@ -747,29 +909,64 @@ static struct mousedev *mousedev_create(struct input_dev *dev, ...@@ -747,29 +909,64 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
static void mousedev_destroy(struct mousedev *mousedev) static void mousedev_destroy(struct mousedev *mousedev)
{ {
struct mousedev_client *client;
device_del(&mousedev->dev); device_del(&mousedev->dev);
mousedev->exist = 0; mousedev_cleanup(mousedev);
if (mousedev->minor != MOUSEDEV_MIX)
input_unregister_handle(&mousedev->handle);
put_device(&mousedev->dev);
}
if (mousedev->open) { static int mixdev_add_device(struct mousedev *mousedev)
input_close_device(&mousedev->handle); {
list_for_each_entry(client, &mousedev->client_list, node) int retval;
kill_fasync(&client->fasync, SIGIO, POLL_HUP);
wake_up_interruptible(&mousedev->wait); retval = mutex_lock_interruptible(&mousedev_mix->mutex);
if (retval)
return retval;
if (mousedev_mix->open) {
retval = mousedev_open_device(mousedev);
if (retval)
goto out;
mousedev->mixdev_open = 1;
} }
get_device(&mousedev->dev);
list_add_tail(&mousedev->mixdev_node, &mousedev_mix_list);
out:
mutex_unlock(&mousedev_mix->mutex);
return retval;
}
static void mixdev_remove_device(struct mousedev *mousedev)
{
mutex_lock(&mousedev_mix->mutex);
if (mousedev->mixdev_open) {
mousedev->mixdev_open = 0;
mousedev_close_device(mousedev);
}
list_del_init(&mousedev->mixdev_node);
mutex_unlock(&mousedev_mix->mutex);
put_device(&mousedev->dev); put_device(&mousedev->dev);
} }
static int mousedev_connect(struct input_handler *handler, struct input_dev *dev, static int mousedev_connect(struct input_handler *handler,
struct input_dev *dev,
const struct input_device_id *id) const struct input_device_id *id)
{ {
struct mousedev *mousedev; struct mousedev *mousedev;
int minor; int minor;
int error; int error;
for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++); for (minor = 0; minor < MOUSEDEV_MINORS; minor++)
if (!mousedev_table[minor])
break;
if (minor == MOUSEDEV_MINORS) { if (minor == MOUSEDEV_MINORS) {
printk(KERN_ERR "mousedev: no more free mousedev devices\n"); printk(KERN_ERR "mousedev: no more free mousedev devices\n");
return -ENFILE; return -ENFILE;
...@@ -779,21 +976,13 @@ static int mousedev_connect(struct input_handler *handler, struct input_dev *dev ...@@ -779,21 +976,13 @@ static int mousedev_connect(struct input_handler *handler, struct input_dev *dev
if (IS_ERR(mousedev)) if (IS_ERR(mousedev))
return PTR_ERR(mousedev); return PTR_ERR(mousedev);
error = input_register_handle(&mousedev->handle);
if (error)
goto err_delete_mousedev;
error = mixdev_add_device(mousedev); error = mixdev_add_device(mousedev);
if (error) if (error) {
goto err_unregister_handle; mousedev_destroy(mousedev);
return error;
}
return 0; return 0;
err_unregister_handle:
input_unregister_handle(&mousedev->handle);
err_delete_mousedev:
device_unregister(&mousedev->dev);
return error;
} }
static void mousedev_disconnect(struct input_handle *handle) static void mousedev_disconnect(struct input_handle *handle)
...@@ -801,33 +990,42 @@ static void mousedev_disconnect(struct input_handle *handle) ...@@ -801,33 +990,42 @@ static void mousedev_disconnect(struct input_handle *handle)
struct mousedev *mousedev = handle->private; struct mousedev *mousedev = handle->private;
mixdev_remove_device(mousedev); mixdev_remove_device(mousedev);
input_unregister_handle(handle);
mousedev_destroy(mousedev); mousedev_destroy(mousedev);
} }
static const struct input_device_id mousedev_ids[] = { static const struct input_device_id mousedev_ids[] = {
{ {
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT, .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT |
INPUT_DEVICE_ID_MATCH_RELBIT,
.evbit = { BIT(EV_KEY) | BIT(EV_REL) }, .evbit = { BIT(EV_KEY) | BIT(EV_REL) },
.keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) }, .keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) },
.relbit = { BIT(REL_X) | BIT(REL_Y) }, .relbit = { BIT(REL_X) | BIT(REL_Y) },
}, /* A mouse like device, at least one button, two relative axes */ }, /* A mouse like device, at least one button,
two relative axes */
{ {
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_RELBIT, .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_RELBIT,
.evbit = { BIT(EV_KEY) | BIT(EV_REL) }, .evbit = { BIT(EV_KEY) | BIT(EV_REL) },
.relbit = { BIT(REL_WHEEL) }, .relbit = { BIT(REL_WHEEL) },
}, /* A separate scrollwheel */ }, /* A separate scrollwheel */
{ {
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT |
INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_KEY) | BIT(EV_ABS) }, .evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
.keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) }, .keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
.absbit = { BIT(ABS_X) | BIT(ABS_Y) }, .absbit = { BIT(ABS_X) | BIT(ABS_Y) },
}, /* A tablet like device, at least touch detection, two absolute axes */ }, /* A tablet like device, at least touch detection,
two absolute axes */
{ {
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT |
INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_KEY) | BIT(EV_ABS) }, .evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
.keybit = { [LONG(BTN_TOOL_FINGER)] = BIT(BTN_TOOL_FINGER) }, .keybit = { [LONG(BTN_TOOL_FINGER)] = BIT(BTN_TOOL_FINGER) },
.absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_TOOL_WIDTH) }, .absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) |
BIT(ABS_TOOL_WIDTH) },
}, /* A touchpad */ }, /* A touchpad */
{ }, /* Terminating entry */ { }, /* Terminating entry */
......
...@@ -385,6 +385,8 @@ static int i8042_enable_kbd_port(void) ...@@ -385,6 +385,8 @@ static int i8042_enable_kbd_port(void)
i8042_ctr |= I8042_CTR_KBDINT; i8042_ctr |= I8042_CTR_KBDINT;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
i8042_ctr &= ~I8042_CTR_KBDINT;
i8042_ctr |= I8042_CTR_KBDDIS;
printk(KERN_ERR "i8042.c: Failed to enable KBD port.\n"); printk(KERN_ERR "i8042.c: Failed to enable KBD port.\n");
return -EIO; return -EIO;
} }
...@@ -402,6 +404,8 @@ static int i8042_enable_aux_port(void) ...@@ -402,6 +404,8 @@ static int i8042_enable_aux_port(void)
i8042_ctr |= I8042_CTR_AUXINT; i8042_ctr |= I8042_CTR_AUXINT;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
i8042_ctr &= ~I8042_CTR_AUXINT;
i8042_ctr |= I8042_CTR_AUXDIS;
printk(KERN_ERR "i8042.c: Failed to enable AUX port.\n"); printk(KERN_ERR "i8042.c: Failed to enable AUX port.\n");
return -EIO; return -EIO;
} }
......
...@@ -126,6 +126,16 @@ config TOUCHSCREEN_HP600 ...@@ -126,6 +126,16 @@ config TOUCHSCREEN_HP600
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called hp680_ts_input. module will be called hp680_ts_input.
config TOUCHSCREEN_HP7XX
tristate "HP Jornada 710/720/728 touchscreen"
depends on SA1100_JORNADA720_SSP
help
Say Y here if you have a HP Jornada 710/720/728 and want
to support the built-in touchscreen.
To compile this driver as a module, choose M here: the
module will be called jornada720_ts.
config TOUCHSCREEN_PENMOUNT config TOUCHSCREEN_PENMOUNT
tristate "Penmount serial touchscreen" tristate "Penmount serial touchscreen"
select SERIO select SERIO
...@@ -191,6 +201,7 @@ config TOUCHSCREEN_USB_COMPOSITE ...@@ -191,6 +201,7 @@ config TOUCHSCREEN_USB_COMPOSITE
- Gunze AHL61 - Gunze AHL61
- DMC TSC-10/25 - DMC TSC-10/25
- IRTOUCHSYSTEMS/UNITOP - IRTOUCHSYSTEMS/UNITOP
- IdealTEK URTC1000
Have a look at <http://linux.chapter7.ch/touchkit/> for Have a look at <http://linux.chapter7.ch/touchkit/> for
a usage description and the required user-space stuff. a usage description and the required user-space stuff.
...@@ -238,4 +249,14 @@ config TOUCHSCREEN_USB_IRTOUCH ...@@ -238,4 +249,14 @@ config TOUCHSCREEN_USB_IRTOUCH
bool "IRTOUCHSYSTEMS/UNITOP device support" if EMBEDDED bool "IRTOUCHSYSTEMS/UNITOP device support" if EMBEDDED
depends on TOUCHSCREEN_USB_COMPOSITE depends on TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_USB_IDEALTEK
default y
bool "IdealTEK URTC1000 device support" if EMBEDDED
depends on TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_USB_GENERAL_TOUCH
default y
bool "GeneralTouch Touchscreen device support" if EMBEDDED
depends on TOUCHSCREEN_USB_COMPOSITE
endif endif
...@@ -13,6 +13,7 @@ obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o ...@@ -13,6 +13,7 @@ obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o
obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
......
/*
* drivers/input/touchscreen/jornada720_ts.c
*
* Copyright (C) 2007 Kristoffer Ericson <Kristoffer.Ericson@gmail.com>
*
* Copyright (C) 2006 Filip Zyzniewski <filip.zyzniewski@tefnet.pl>
* based on HP Jornada 56x touchscreen driver by Alex Lange <chicken@handhelds.org>
*
* 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.
*
* HP Jornada 710/720/729 Touchscreen Driver
*/
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <asm/hardware.h>
#include <asm/arch/jornada720.h>
MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>");
MODULE_DESCRIPTION("HP Jornada 710/720/728 touchscreen driver");
MODULE_LICENSE("GPLv2");
struct jornada_ts {
struct input_dev *dev;
int x_data[4]; /* X sample values */
int y_data[4]; /* Y sample values */
};
static void jornada720_ts_collect_data(struct jornada_ts *jornada_ts)
{
/* 3 low word X samples */
jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY);
jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY);
jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY);
/* 3 low word Y samples */
jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY);
jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY);
jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY);
/* combined x samples bits */
jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY);
/* combined y samples bits */
jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY);
}
static int jornada720_ts_average(int coords[4])
{
int coord, high_bits = coords[3];
coord = coords[0] | ((high_bits & 0x03) << 8);
coord += coords[1] | ((high_bits & 0x0c) << 6);
coord += coords[2] | ((high_bits & 0x30) << 4);
return coord / 3;
}
static irqreturn_t jornada720_ts_interrupt(int irq, void *dev_id)
{
struct platform_device *pdev = dev_id;
struct jornada_ts *jornada_ts = platform_get_drvdata(pdev);
struct input_dev *input = jornada_ts->dev;
int x, y;
/* If GPIO_GPIO9 is set to high then report pen up */
if (GPLR & GPIO_GPIO(9)) {
input_report_key(input, BTN_TOUCH, 0);
input_sync(input);
} else {
jornada_ssp_start();
/* proper reply to request is always TXDUMMY */
if (jornada_ssp_inout(GETTOUCHSAMPLES) == TXDUMMY) {
jornada720_ts_collect_data(jornada_ts);
x = jornada720_ts_average(jornada_ts->x_data);
y = jornada720_ts_average(jornada_ts->y_data);
input_report_key(input, BTN_TOUCH, 1);
input_report_abs(input, ABS_X, x);
input_report_abs(input, ABS_Y, y);
input_sync(input);
}
jornada_ssp_end();
}
return IRQ_HANDLED;
}
static int __devinit jornada720_ts_probe(struct platform_device *pdev)
{
struct jornada_ts *jornada_ts;
struct input_dev *input_dev;
int error;
jornada_ts = kzalloc(sizeof(struct jornada_ts), GFP_KERNEL);
input_dev = input_allocate_device();
if (!jornada_ts || !input_dev) {
error = -ENOMEM;
goto fail1;
}
platform_set_drvdata(pdev, jornada_ts);
jornada_ts->dev = input_dev;
input_dev->name = "HP Jornada 7xx Touchscreen";
input_dev->phys = "jornadats/input0";
input_dev->id.bustype = BUS_HOST;
input_dev->dev.parent = &pdev->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
input_set_abs_params(input_dev, ABS_X, 270, 3900, 0, 0);
input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0);
error = request_irq(IRQ_GPIO9,
jornada720_ts_interrupt,
IRQF_DISABLED | IRQF_TRIGGER_RISING,
"HP7XX Touchscreen driver", pdev);
if (error) {
printk(KERN_INFO "HP7XX TS : Unable to acquire irq!\n");
goto fail1;
}
error = input_register_device(jornada_ts->dev);
if (error)
goto fail2;
return 0;
fail2:
free_irq(IRQ_GPIO9, pdev);
fail1:
platform_set_drvdata(pdev, NULL);
input_free_device(input_dev);
kfree(jornada_ts);
return error;
}
static int __devexit jornada720_ts_remove(struct platform_device *pdev)
{
struct jornada_ts *jornada_ts = platform_get_drvdata(pdev);
free_irq(IRQ_GPIO9, pdev);
platform_set_drvdata(pdev, NULL);
input_unregister_device(jornada_ts->dev);
kfree(jornada_ts);
return 0;
}
static struct platform_driver jornada720_ts_driver = {
.probe = jornada720_ts_probe,
.remove = __devexit_p(jornada720_ts_remove),
.driver = {
.name = "jornada_ts",
},
};
static int __init jornada720_ts_init(void)
{
return platform_driver_register(&jornada720_ts_driver);
}
static void __exit jornada720_ts_exit(void)
{
platform_driver_unregister(&jornada720_ts_driver);
}
module_init(jornada720_ts_init);
module_exit(jornada720_ts_exit);
...@@ -130,8 +130,7 @@ static unsigned int ucb1400_adc_read(struct ucb1400 *ucb, u16 adc_channel) ...@@ -130,8 +130,7 @@ static unsigned int ucb1400_adc_read(struct ucb1400 *ucb, u16 adc_channel)
if (val & UCB_ADC_DAT_VALID) if (val & UCB_ADC_DAT_VALID)
break; break;
/* yield to other processes */ /* yield to other processes */
set_current_state(TASK_INTERRUPTIBLE); schedule_timeout_uninterruptible(1);
schedule_timeout(1);
} }
return UCB_ADC_DAT_VALUE(val); return UCB_ADC_DAT_VALUE(val);
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
* - Gunze AHL61 * - Gunze AHL61
* - DMC TSC-10/25 * - DMC TSC-10/25
* - IRTOUCHSYSTEMS/UNITOP * - IRTOUCHSYSTEMS/UNITOP
* - IdealTEK URTC1000
* *
* Copyright (C) 2004-2006 by Daniel Ritz <daniel.ritz@gmx.ch> * Copyright (C) 2004-2006 by Daniel Ritz <daniel.ritz@gmx.ch>
* Copyright (C) by Todd E. Johnson (mtouchusb.c) * Copyright (C) by Todd E. Johnson (mtouchusb.c)
...@@ -92,7 +93,7 @@ struct usbtouch_usb { ...@@ -92,7 +93,7 @@ struct usbtouch_usb {
}; };
#if defined(CONFIG_TOUCHSCREEN_USB_EGALAX) || defined(CONFIG_TOUCHSCREEN_USB_ETURBO) #if defined(CONFIG_TOUCHSCREEN_USB_EGALAX) || defined(CONFIG_TOUCHSCREEN_USB_ETURBO) || defined(CONFIG_TOUCHSCREEN_USB_IDEALTEK)
#define MULTI_PACKET #define MULTI_PACKET
#endif #endif
...@@ -112,6 +113,8 @@ enum { ...@@ -112,6 +113,8 @@ enum {
DEVTYPE_GUNZE, DEVTYPE_GUNZE,
DEVTYPE_DMC_TSC10, DEVTYPE_DMC_TSC10,
DEVTYPE_IRTOUCH, DEVTYPE_IRTOUCH,
DEVTYPE_IDEALTEK,
DEVTYPE_GENERAL_TOUCH,
}; };
static struct usb_device_id usbtouch_devices[] = { static struct usb_device_id usbtouch_devices[] = {
...@@ -157,6 +160,14 @@ static struct usb_device_id usbtouch_devices[] = { ...@@ -157,6 +160,14 @@ static struct usb_device_id usbtouch_devices[] = {
{USB_DEVICE(0x6615, 0x0001), .driver_info = DEVTYPE_IRTOUCH}, {USB_DEVICE(0x6615, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
#endif #endif
#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
{USB_DEVICE(0x1391, 0x1000), .driver_info = DEVTYPE_IDEALTEK},
#endif
#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
{USB_DEVICE(0x0dfc, 0x0001), .driver_info = DEVTYPE_GENERAL_TOUCH},
#endif
{} {}
}; };
...@@ -396,7 +407,8 @@ static int dmc_tsc10_init(struct usbtouch_usb *usbtouch) ...@@ -396,7 +407,8 @@ static int dmc_tsc10_init(struct usbtouch_usb *usbtouch)
TSC10_RATE_150, 0, buf, 2, USB_CTRL_SET_TIMEOUT); TSC10_RATE_150, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (buf[0] != 0x06 || buf[1] != 0x00) if ((buf[0] != 0x06 || buf[1] != 0x00) &&
(buf[0] != 0x15 || buf[1] != 0x01))
return -ENODEV; return -ENODEV;
/* start sending data */ /* start sending data */
...@@ -437,6 +449,57 @@ static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) ...@@ -437,6 +449,57 @@ static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
#endif #endif
/*****************************************************************************
* IdealTEK URTC1000 Part
*/
#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
static int idealtek_get_pkt_len(unsigned char *buf, int len)
{
if (buf[0] & 0x80)
return 5;
if (buf[0] == 0x01)
return len;
return 0;
}
static int idealtek_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
{
switch (pkt[0] & 0x98) {
case 0x88:
/* touch data in IdealTEK mode */
dev->x = (pkt[1] << 5) | (pkt[2] >> 2);
dev->y = (pkt[3] << 5) | (pkt[4] >> 2);
dev->touch = (pkt[0] & 0x40) ? 1 : 0;
return 1;
case 0x98:
/* touch data in MT emulation mode */
dev->x = (pkt[2] << 5) | (pkt[1] >> 2);
dev->y = (pkt[4] << 5) | (pkt[3] >> 2);
dev->touch = (pkt[0] & 0x40) ? 1 : 0;
return 1;
default:
return 0;
}
}
#endif
/*****************************************************************************
* General Touch Part
*/
#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
static int general_touch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
{
dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1] ;
dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3] ;
dev->press = pkt[5] & 0xff;
dev->touch = pkt[0] & 0x01;
return 1;
}
#endif
/***************************************************************************** /*****************************************************************************
* the different device descriptors * the different device descriptors
*/ */
...@@ -537,6 +600,32 @@ static struct usbtouch_device_info usbtouch_dev_info[] = { ...@@ -537,6 +600,32 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
.read_data = irtouch_read_data, .read_data = irtouch_read_data,
}, },
#endif #endif
#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
[DEVTYPE_IDEALTEK] = {
.min_xc = 0x0,
.max_xc = 0x0fff,
.min_yc = 0x0,
.max_yc = 0x0fff,
.rept_size = 8,
.flags = USBTOUCH_FLG_BUFFER,
.process_pkt = usbtouch_process_multi,
.get_pkt_len = idealtek_get_pkt_len,
.read_data = idealtek_read_data,
},
#endif
#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
[DEVTYPE_GENERAL_TOUCH] = {
.min_xc = 0x0,
.max_xc = 0x0500,
.min_yc = 0x0,
.max_yc = 0x0500,
.rept_size = 7,
.read_data = general_touch_read_data,
}
#endif
}; };
......
/*
* $Id: tsdev.c,v 1.15 2002/04/10 16:50:19 jsimmons Exp $
*
* Copyright (c) 2001 "Crazy" james Simmons
*
* Compaq touchscreen protocol driver. The protocol emulated by this driver
* is obsolete; for new programs use the tslib library which can read directly
* from evdev and perform dejittering, variance filtering and calibration -
* all in user space, not at kernel level. The meaning of this driver is
* to allow usage of newer input drivers with old applications that use the
* old /dev/h3600_ts and /dev/h3600_tsraw devices.
*
* 09-Apr-2004: Andrew Zabolotny <zap@homelink.ru>
* Fixed to actually work, not just output random numbers.
* Added support for both h3600_ts and h3600_tsraw protocol
* emulation.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <jsimmons@infradead.org>.
*/
#define TSDEV_MINOR_BASE 128
#define TSDEV_MINORS 32
/* First 16 devices are h3600_ts compatible; second 16 are h3600_tsraw */
#define TSDEV_MINOR_MASK 15
#define TSDEV_BUFFER_SIZE 64
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/major.h>
#include <linux/random.h>
#include <linux/time.h>
#include <linux/device.h>
#ifndef CONFIG_INPUT_TSDEV_SCREEN_X
#define CONFIG_INPUT_TSDEV_SCREEN_X 240
#endif
#ifndef CONFIG_INPUT_TSDEV_SCREEN_Y
#define CONFIG_INPUT_TSDEV_SCREEN_Y 320
#endif
/* This driver emulates both protocols of the old h3600_ts and h3600_tsraw
* devices. The first one must output X/Y data in 'cooked' format, e.g.
* filtered, dejittered and calibrated. Second device just outputs raw
* data received from the hardware.
*
* This driver doesn't support filtering and dejittering; it supports only
* calibration. Filtering and dejittering must be done in the low-level
* driver, if needed, because it may gain additional benefits from knowing
* the low-level details, the nature of noise and so on.
*
* The driver precomputes a calibration matrix given the initial xres and
* yres values (quite innacurate for most touchscreens) that will result
* in a more or less expected range of output values. The driver supports
* the TS_SET_CAL ioctl, which will replace the calibration matrix with a
* new one, supposedly generated from the values taken from the raw device.
*/
MODULE_AUTHOR("James Simmons <jsimmons@transvirtual.com>");
MODULE_DESCRIPTION("Input driver to touchscreen converter");
MODULE_LICENSE("GPL");
static int xres = CONFIG_INPUT_TSDEV_SCREEN_X;
module_param(xres, uint, 0);
MODULE_PARM_DESC(xres, "Horizontal screen resolution (can be negative for X-mirror)");
static int yres = CONFIG_INPUT_TSDEV_SCREEN_Y;
module_param(yres, uint, 0);
MODULE_PARM_DESC(yres, "Vertical screen resolution (can be negative for Y-mirror)");
/* From Compaq's Touch Screen Specification version 0.2 (draft) */
struct ts_event {
short pressure;
short x;
short y;
short millisecs;
};
struct ts_calibration {
int xscale;
int xtrans;
int yscale;
int ytrans;
int xyswap;
};
struct tsdev {
int exist;
int open;
int minor;
char name[8];
struct input_handle handle;
wait_queue_head_t wait;
struct list_head client_list;
struct device dev;
int x, y, pressure;
struct ts_calibration cal;
};
struct tsdev_client {
struct fasync_struct *fasync;
struct list_head node;
struct tsdev *tsdev;
int head, tail;
struct ts_event event[TSDEV_BUFFER_SIZE];
int raw;
};
/* The following ioctl codes are defined ONLY for backward compatibility.
* Don't use tsdev for new developement; use the tslib library instead.
* Touchscreen calibration is a fully userspace task.
*/
/* Use 'f' as magic number */
#define IOC_H3600_TS_MAGIC 'f'
#define TS_GET_CAL _IOR(IOC_H3600_TS_MAGIC, 10, struct ts_calibration)
#define TS_SET_CAL _IOW(IOC_H3600_TS_MAGIC, 11, struct ts_calibration)
static struct tsdev *tsdev_table[TSDEV_MINORS/2];
static int tsdev_fasync(int fd, struct file *file, int on)
{
struct tsdev_client *client = file->private_data;
int retval;
retval = fasync_helper(fd, file, on, &client->fasync);
return retval < 0 ? retval : 0;
}
static int tsdev_open(struct inode *inode, struct file *file)
{
int i = iminor(inode) - TSDEV_MINOR_BASE;
struct tsdev_client *client;
struct tsdev *tsdev;
int error;
printk(KERN_WARNING "tsdev (compaq touchscreen emulation) is scheduled "
"for removal.\nSee Documentation/feature-removal-schedule.txt "
"for details.\n");
if (i >= TSDEV_MINORS)
return -ENODEV;
tsdev = tsdev_table[i & TSDEV_MINOR_MASK];
if (!tsdev || !tsdev->exist)
return -ENODEV;
get_device(&tsdev->dev);
client = kzalloc(sizeof(struct tsdev_client), GFP_KERNEL);
if (!client) {
error = -ENOMEM;
goto err_put_tsdev;
}
client->tsdev = tsdev;
client->raw = (i >= TSDEV_MINORS / 2) ? 1 : 0;
list_add_tail(&client->node, &tsdev->client_list);
if (!tsdev->open++ && tsdev->exist) {
error = input_open_device(&tsdev->handle);
if (error)
goto err_free_client;
}
file->private_data = client;
return 0;
err_free_client:
list_del(&client->node);
kfree(client);
err_put_tsdev:
put_device(&tsdev->dev);
return error;
}
static void tsdev_free(struct device *dev)
{
struct tsdev *tsdev = container_of(dev, struct tsdev, dev);
tsdev_table[tsdev->minor] = NULL;
kfree(tsdev);
}
static int tsdev_release(struct inode *inode, struct file *file)
{
struct tsdev_client *client = file->private_data;
struct tsdev *tsdev = client->tsdev;
tsdev_fasync(-1, file, 0);
list_del(&client->node);
kfree(client);
if (!--tsdev->open && tsdev->exist)
input_close_device(&tsdev->handle);
put_device(&tsdev->dev);
return 0;
}
static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count,
loff_t *ppos)
{
struct tsdev_client *client = file->private_data;
struct tsdev *tsdev = client->tsdev;
int retval = 0;
if (client->head == client->tail && tsdev->exist && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
retval = wait_event_interruptible(tsdev->wait,
client->head != client->tail || !tsdev->exist);
if (retval)
return retval;
if (!tsdev->exist)
return -ENODEV;
while (client->head != client->tail &&
retval + sizeof (struct ts_event) <= count) {
if (copy_to_user (buffer + retval, client->event + client->tail,
sizeof (struct ts_event)))
return -EFAULT;
client->tail = (client->tail + 1) & (TSDEV_BUFFER_SIZE - 1);
retval += sizeof (struct ts_event);
}
return retval;
}
/* No kernel lock - fine */
static unsigned int tsdev_poll(struct file *file, poll_table *wait)
{
struct tsdev_client *client = file->private_data;
struct tsdev *tsdev = client->tsdev;
poll_wait(file, &tsdev->wait, wait);
return ((client->head == client->tail) ? 0 : (POLLIN | POLLRDNORM)) |
(tsdev->exist ? 0 : (POLLHUP | POLLERR));
}
static int tsdev_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct tsdev_client *client = file->private_data;
struct tsdev *tsdev = client->tsdev;
int retval = 0;
switch (cmd) {
case TS_GET_CAL:
if (copy_to_user((void __user *)arg, &tsdev->cal,
sizeof (struct ts_calibration)))
retval = -EFAULT;
break;
case TS_SET_CAL:
if (copy_from_user(&tsdev->cal, (void __user *)arg,
sizeof (struct ts_calibration)))
retval = -EFAULT;
break;
default:
retval = -EINVAL;
break;
}
return retval;
}
static const struct file_operations tsdev_fops = {
.owner = THIS_MODULE,
.open = tsdev_open,
.release = tsdev_release,
.read = tsdev_read,
.poll = tsdev_poll,
.fasync = tsdev_fasync,
.ioctl = tsdev_ioctl,
};
static void tsdev_event(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
{
struct tsdev *tsdev = handle->private;
struct tsdev_client *client;
struct timeval time;
switch (type) {
case EV_ABS:
switch (code) {
case ABS_X:
tsdev->x = value;
break;
case ABS_Y:
tsdev->y = value;
break;
case ABS_PRESSURE:
if (value > handle->dev->absmax[ABS_PRESSURE])
value = handle->dev->absmax[ABS_PRESSURE];
value -= handle->dev->absmin[ABS_PRESSURE];
if (value < 0)
value = 0;
tsdev->pressure = value;
break;
}
break;
case EV_REL:
switch (code) {
case REL_X:
tsdev->x += value;
if (tsdev->x < 0)
tsdev->x = 0;
else if (tsdev->x > xres)
tsdev->x = xres;
break;
case REL_Y:
tsdev->y += value;
if (tsdev->y < 0)
tsdev->y = 0;
else if (tsdev->y > yres)
tsdev->y = yres;
break;
}
break;
case EV_KEY:
if (code == BTN_TOUCH || code == BTN_MOUSE) {
switch (value) {
case 0:
tsdev->pressure = 0;
break;
case 1:
if (!tsdev->pressure)
tsdev->pressure = 1;
break;
}
}
break;
}
if (type != EV_SYN || code != SYN_REPORT)
return;
list_for_each_entry(client, &tsdev->client_list, node) {
int x, y, tmp;
do_gettimeofday(&time);
client->event[client->head].millisecs = time.tv_usec / 1000;
client->event[client->head].pressure = tsdev->pressure;
x = tsdev->x;
y = tsdev->y;
/* Calibration */
if (!client->raw) {
x = ((x * tsdev->cal.xscale) >> 8) + tsdev->cal.xtrans;
y = ((y * tsdev->cal.yscale) >> 8) + tsdev->cal.ytrans;
if (tsdev->cal.xyswap) {
tmp = x; x = y; y = tmp;
}
}
client->event[client->head].x = x;
client->event[client->head].y = y;
client->head = (client->head + 1) & (TSDEV_BUFFER_SIZE - 1);
kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
wake_up_interruptible(&tsdev->wait);
}
static int tsdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct tsdev *tsdev;
int minor, delta;
int error;
for (minor = 0; minor < TSDEV_MINORS / 2 && tsdev_table[minor]; minor++);
if (minor >= TSDEV_MINORS / 2) {
printk(KERN_ERR
"tsdev: You have way too many touchscreens\n");
return -ENFILE;
}
tsdev = kzalloc(sizeof(struct tsdev), GFP_KERNEL);
if (!tsdev)
return -ENOMEM;
INIT_LIST_HEAD(&tsdev->client_list);
init_waitqueue_head(&tsdev->wait);
tsdev->exist = 1;
tsdev->minor = minor;
tsdev->handle.dev = dev;
tsdev->handle.name = tsdev->name;
tsdev->handle.handler = handler;
tsdev->handle.private = tsdev;
snprintf(tsdev->name, sizeof(tsdev->name), "ts%d", minor);
/* Precompute the rough calibration matrix */
delta = dev->absmax [ABS_X] - dev->absmin [ABS_X] + 1;
if (delta == 0)
delta = 1;
tsdev->cal.xscale = (xres << 8) / delta;
tsdev->cal.xtrans = - ((dev->absmin [ABS_X] * tsdev->cal.xscale) >> 8);
delta = dev->absmax [ABS_Y] - dev->absmin [ABS_Y] + 1;
if (delta == 0)
delta = 1;
tsdev->cal.yscale = (yres << 8) / delta;
tsdev->cal.ytrans = - ((dev->absmin [ABS_Y] * tsdev->cal.yscale) >> 8);
snprintf(tsdev->dev.bus_id, sizeof(tsdev->dev.bus_id),
"ts%d", minor);
tsdev->dev.class = &input_class;
tsdev->dev.parent = &dev->dev;
tsdev->dev.devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor);
tsdev->dev.release = tsdev_free;
device_initialize(&tsdev->dev);
tsdev_table[minor] = tsdev;
error = device_add(&tsdev->dev);
if (error)
goto err_free_tsdev;
error = input_register_handle(&tsdev->handle);
if (error)
goto err_delete_tsdev;
return 0;
err_delete_tsdev:
device_del(&tsdev->dev);
err_free_tsdev:
put_device(&tsdev->dev);
return error;
}
static void tsdev_disconnect(struct input_handle *handle)
{
struct tsdev *tsdev = handle->private;
struct tsdev_client *client;
input_unregister_handle(handle);
device_del(&tsdev->dev);
tsdev->exist = 0;
if (tsdev->open) {
input_close_device(handle);
list_for_each_entry(client, &tsdev->client_list, node)
kill_fasync(&client->fasync, SIGIO, POLL_HUP);
wake_up_interruptible(&tsdev->wait);
}
put_device(&tsdev->dev);
}
static const struct input_device_id tsdev_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
.keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) },
.relbit = { BIT(REL_X) | BIT(REL_Y) },
}, /* A mouse like device, at least one button, two relative axes */
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
.keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
.absbit = { BIT(ABS_X) | BIT(ABS_Y) },
}, /* A tablet like device, at least touch detection, two absolute axes */
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_ABS) },
.absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) },
}, /* A tablet like device with several gradations of pressure */
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE(input, tsdev_ids);
static struct input_handler tsdev_handler = {
.event = tsdev_event,
.connect = tsdev_connect,
.disconnect = tsdev_disconnect,
.fops = &tsdev_fops,
.minor = TSDEV_MINOR_BASE,
.name = "tsdev",
.id_table = tsdev_ids,
};
static int __init tsdev_init(void)
{
return input_register_handler(&tsdev_handler);
}
static void __exit tsdev_exit(void)
{
input_unregister_handler(&tsdev_handler);
}
module_init(tsdev_init);
module_exit(tsdev_exit);
...@@ -172,6 +172,7 @@ config INPUT_ADBHID ...@@ -172,6 +172,7 @@ config INPUT_ADBHID
config MAC_EMUMOUSEBTN config MAC_EMUMOUSEBTN
bool "Support for mouse button 2+3 emulation" bool "Support for mouse button 2+3 emulation"
select INPUT
help help
This provides generic support for emulating the 2nd and 3rd mouse This provides generic support for emulating the 2nd and 3rd mouse
button with keypresses. If you say Y here, the emulation is still button with keypresses. If you say Y here, the emulation is still
......
...@@ -52,6 +52,11 @@ ...@@ -52,6 +52,11 @@
MODULE_AUTHOR("Franz Sirl <Franz.Sirl-kernel@lauterbach.com>"); MODULE_AUTHOR("Franz Sirl <Franz.Sirl-kernel@lauterbach.com>");
static int restore_capslock_events;
module_param(restore_capslock_events, int, 0644);
MODULE_PARM_DESC(restore_capslock_events,
"Produce keypress events for capslock on both keyup and keydown.");
#define KEYB_KEYREG 0 /* register # for key up/down data */ #define KEYB_KEYREG 0 /* register # for key up/down data */
#define KEYB_LEDREG 2 /* register # for leds on ADB keyboard */ #define KEYB_LEDREG 2 /* register # for leds on ADB keyboard */
#define MOUSE_DATAREG 0 /* reg# for movement/button codes from mouse */ #define MOUSE_DATAREG 0 /* reg# for movement/button codes from mouse */
...@@ -217,6 +222,8 @@ struct adbhid { ...@@ -217,6 +222,8 @@ struct adbhid {
#define FLAG_FN_KEY_PRESSED 0x00000001 #define FLAG_FN_KEY_PRESSED 0x00000001
#define FLAG_POWER_FROM_FN 0x00000002 #define FLAG_POWER_FROM_FN 0x00000002
#define FLAG_EMU_FWDEL_DOWN 0x00000004 #define FLAG_EMU_FWDEL_DOWN 0x00000004
#define FLAG_CAPSLOCK_TRANSLATE 0x00000008
#define FLAG_CAPSLOCK_DOWN 0x00000010
static struct adbhid *adbhid[16]; static struct adbhid *adbhid[16];
...@@ -272,19 +279,50 @@ adbhid_keyboard_input(unsigned char *data, int nb, int apoll) ...@@ -272,19 +279,50 @@ adbhid_keyboard_input(unsigned char *data, int nb, int apoll)
} }
static void static void
adbhid_input_keycode(int id, int keycode, int repeat) adbhid_input_keycode(int id, int scancode, int repeat)
{ {
struct adbhid *ahid = adbhid[id]; struct adbhid *ahid = adbhid[id];
int up_flag, key; int keycode, up_flag;
up_flag = (keycode & 0x80); keycode = scancode & 0x7f;
keycode &= 0x7f; up_flag = scancode & 0x80;
if (restore_capslock_events) {
if (keycode == ADB_KEY_CAPSLOCK && !up_flag) {
/* Key pressed, turning on the CapsLock LED.
* The next 0xff will be interpreted as a release. */
ahid->flags |= FLAG_CAPSLOCK_TRANSLATE
| FLAG_CAPSLOCK_DOWN;
} else if (scancode == 0xff) {
/* Scancode 0xff usually signifies that the capslock
* key was either pressed or released. */
if (ahid->flags & FLAG_CAPSLOCK_TRANSLATE) {
keycode = ADB_KEY_CAPSLOCK;
if (ahid->flags & FLAG_CAPSLOCK_DOWN) {
/* Key released */
up_flag = 1;
ahid->flags &= ~FLAG_CAPSLOCK_DOWN;
} else {
/* Key pressed */
up_flag = 0;
ahid->flags &= ~FLAG_CAPSLOCK_TRANSLATE;
}
} else {
printk(KERN_INFO "Spurious caps lock event "
"(scancode 0xff).");
}
}
}
switch (keycode) { switch (keycode) {
case ADB_KEY_CAPSLOCK: /* Generate down/up events for CapsLock everytime. */ case ADB_KEY_CAPSLOCK:
input_report_key(ahid->input, KEY_CAPSLOCK, 1); if (!restore_capslock_events) {
input_report_key(ahid->input, KEY_CAPSLOCK, 0); /* Generate down/up events for CapsLock everytime. */
input_sync(ahid->input); input_report_key(ahid->input, KEY_CAPSLOCK, 1);
input_sync(ahid->input);
input_report_key(ahid->input, KEY_CAPSLOCK, 0);
input_sync(ahid->input);
}
return; return;
#ifdef CONFIG_PPC_PMAC #ifdef CONFIG_PPC_PMAC
case ADB_KEY_POWER_OLD: /* Power key on PBook 3400 needs remapping */ case ADB_KEY_POWER_OLD: /* Power key on PBook 3400 needs remapping */
...@@ -296,7 +334,7 @@ adbhid_input_keycode(int id, int keycode, int repeat) ...@@ -296,7 +334,7 @@ adbhid_input_keycode(int id, int keycode, int repeat)
keycode = ADB_KEY_POWER; keycode = ADB_KEY_POWER;
} }
break; break;
case ADB_KEY_POWER: case ADB_KEY_POWER:
/* Fn + Command will produce a bogus "power" keycode */ /* Fn + Command will produce a bogus "power" keycode */
if (ahid->flags & FLAG_FN_KEY_PRESSED) { if (ahid->flags & FLAG_FN_KEY_PRESSED) {
keycode = ADB_KEY_CMD; keycode = ADB_KEY_CMD;
......
#ifndef _BFIN_KPAD_H
#define _BFIN_KPAD_H
struct bfin_kpad_platform_data {
int rows;
int cols;
const unsigned int *keymap;
unsigned short keymapsize;
unsigned short repeat;
u32 debounce_time; /* in ns */
u32 coldrive_time; /* in ns */
u32 keyup_test_interval; /* in ms */
};
#define KEYVAL(col, row, val) (((1 << col) << 24) | ((1 << row) << 16) | (val))
#endif
...@@ -8,6 +8,7 @@ struct gpio_keys_button { ...@@ -8,6 +8,7 @@ struct gpio_keys_button {
int active_low; int active_low;
char *desc; char *desc;
int type; /* input event type (EV_KEY, EV_SW) */ int type; /* input event type (EV_KEY, EV_SW) */
int wakeup; /* configure the button as a wake-up source */
}; };
struct gpio_keys_platform_data { struct gpio_keys_platform_data {
......
...@@ -856,7 +856,7 @@ struct ff_rumble_effect { ...@@ -856,7 +856,7 @@ struct ff_rumble_effect {
* defining effect parameters * defining effect parameters
* *
* This structure is sent through ioctl from the application to the driver. * This structure is sent through ioctl from the application to the driver.
* To create a new effect aplication should set its @id to -1; the kernel * To create a new effect application should set its @id to -1; the kernel
* will return assigned @id which can later be used to update or delete * will return assigned @id which can later be used to update or delete
* this effect. * this effect.
* *
...@@ -936,9 +936,82 @@ struct ff_effect { ...@@ -936,9 +936,82 @@ struct ff_effect {
#define BIT(x) (1UL<<((x)%BITS_PER_LONG)) #define BIT(x) (1UL<<((x)%BITS_PER_LONG))
#define LONG(x) ((x)/BITS_PER_LONG) #define LONG(x) ((x)/BITS_PER_LONG)
/**
* struct input_dev - represents an input device
* @name: name of the device
* @phys: physical path to the device in the system hierarchy
* @uniq: unique identification code for the device (if device has it)
* @id: id of the device (struct input_id)
* @evbit: bitmap of types of events supported by the device (EV_KEY,
* EV_REL, etc.)
* @keybit: bitmap of keys/buttons this device has
* @relbit: bitmap of relative axes for the device
* @absbit: bitmap of absolute axes for the device
* @mscbit: bitmap of miscellaneous events supported by the device
* @ledbit: bitmap of leds present on the device
* @sndbit: bitmap of sound effects supported by the device
* @ffbit: bitmap of force feedback effects supported by the device
* @swbit: bitmap of switches present on the device
* @keycodemax: size of keycode table
* @keycodesize: size of elements in keycode table
* @keycode: map of scancodes to keycodes for this device
* @setkeycode: optional method to alter current keymap, used to implement
* sparse keymaps. If not supplied default mechanism will be used
* @getkeycode: optional method to retrieve current keymap. If not supplied
* default mechanism will be used
* @ff: force feedback structure associated with the device if device
* supports force feedback effects
* @repeat_key: stores key code of the last key pressed; used to implement
* software autorepeat
* @timer: timer for software autorepeat
* @sync: set to 1 when there were no new events since last EV_SYNC
* @abs: current values for reports from absolute axes
* @rep: current values for autorepeat parameters (delay, rate)
* @key: reflects current state of device's keys/buttons
* @led: reflects current state of device's LEDs
* @snd: reflects current state of sound effects
* @sw: reflects current state of device's switches
* @absmax: maximum values for events coming from absolute axes
* @absmin: minimum values for events coming from absolute axes
* @absfuzz: describes noisiness for axes
* @absflat: size of the center flat position (used by joydev)
* @open: this method is called when the very first user calls
* input_open_device(). The driver must prepare the device
* to start generating events (start polling thread,
* request an IRQ, submit URB, etc.)
* @close: this method is called when the very last user calls
* input_close_device().
* @flush: purges the device. Most commonly used to get rid of force
* feedback effects loaded into the device when disconnecting
* from it
* @event: event handler for events sent _to_ the device, like EV_LED
* or EV_SND. The device is expected to carry out the requested
* action (turn on a LED, play sound, etc.) The call is protected
* by @event_lock and must not sleep
* @grab: input handle that currently has the device grabbed (via
* EVIOCGRAB ioctl). When a handle grabs a device it becomes sole
* recipient for all input events coming from the device
* @event_lock: this spinlock is is taken when input core receives
* and processes a new event for the device (in input_event()).
* Code that accesses and/or modifies parameters of a device
* (such as keymap or absmin, absmax, absfuzz, etc.) after device
* has been registered with input core must take this lock.
* @mutex: serializes calls to open(), close() and flush() methods
* @users: stores number of users (input handlers) that opened this
* device. It is used by input_open_device() and input_close_device()
* to make sure that dev->open() is only called when the first
* user opens device and dev->close() is called when the very
* last user closes the device
* @going_away: marks devices that are in a middle of unregistering and
* causes input_open_device*() fail with -ENODEV.
* @dev: driver model's view of this device
* @h_list: list of input handles associated with the device. When
* accessing the list dev->mutex must be held
* @node: used to place the device onto input_dev_list
*/
struct input_dev { struct input_dev {
void *private; void *private; /* do not use */
const char *name; const char *name;
const char *phys; const char *phys;
...@@ -966,8 +1039,6 @@ struct input_dev { ...@@ -966,8 +1039,6 @@ struct input_dev {
unsigned int repeat_key; unsigned int repeat_key;
struct timer_list timer; struct timer_list timer;
int state;
int sync; int sync;
int abs[ABS_MAX + 1]; int abs[ABS_MAX + 1];
...@@ -990,8 +1061,11 @@ struct input_dev { ...@@ -990,8 +1061,11 @@ struct input_dev {
struct input_handle *grab; struct input_handle *grab;
struct mutex mutex; /* serializes open and close operations */ spinlock_t event_lock;
struct mutex mutex;
unsigned int users; unsigned int users;
int going_away;
struct device dev; struct device dev;
union { /* temporarily so while we switching to struct device */ union { /* temporarily so while we switching to struct device */
...@@ -1057,7 +1131,9 @@ struct input_handle; ...@@ -1057,7 +1131,9 @@ struct input_handle;
/** /**
* struct input_handler - implements one of interfaces for input devices * struct input_handler - implements one of interfaces for input devices
* @private: driver-specific data * @private: driver-specific data
* @event: event handler * @event: event handler. This method is being called by input core with
* interrupts disabled and dev->event_lock spinlock held and so
* it may not sleep
* @connect: called when attaching a handler to an input device * @connect: called when attaching a handler to an input device
* @disconnect: disconnects a handler from input device * @disconnect: disconnects a handler from input device
* @start: starts handler for given handle. This function is called by * @start: starts handler for given handle. This function is called by
...@@ -1069,10 +1145,18 @@ struct input_handle; ...@@ -1069,10 +1145,18 @@ struct input_handle;
* @name: name of the handler, to be shown in /proc/bus/input/handlers * @name: name of the handler, to be shown in /proc/bus/input/handlers
* @id_table: pointer to a table of input_device_ids this driver can * @id_table: pointer to a table of input_device_ids this driver can
* handle * handle
* @blacklist: prointer to a table of input_device_ids this driver should * @blacklist: pointer to a table of input_device_ids this driver should
* ignore even if they match @id_table * ignore even if they match @id_table
* @h_list: list of input handles associated with the handler * @h_list: list of input handles associated with the handler
* @node: for placing the driver onto input_handler_list * @node: for placing the driver onto input_handler_list
*
* Input handlers attach to input devices and create input handles. There
* are likely several handlers attached to any given input device at the
* same time. All of them will get their copy of input event generated by
* the device.
*
* Note that input core serializes calls to connect() and disconnect()
* methods.
*/ */
struct input_handler { struct input_handler {
...@@ -1094,6 +1178,18 @@ struct input_handler { ...@@ -1094,6 +1178,18 @@ struct input_handler {
struct list_head node; struct list_head node;
}; };
/**
* struct input_handle - links input device with an input handler
* @private: handler-specific data
* @open: counter showing whether the handle is 'open', i.e. should deliver
* events from its device
* @name: name given to the handle by handler that created it
* @dev: input device the handle is attached to
* @handler: handler that works with the device through this handle
* @d_node: used to put the handle on device's list of attached handles
* @h_node: used to put the handle on handler's list of handles from which
* it gets events
*/
struct input_handle { struct input_handle {
void *private; void *private;
...@@ -1136,10 +1232,10 @@ static inline void input_set_drvdata(struct input_dev *dev, void *data) ...@@ -1136,10 +1232,10 @@ static inline void input_set_drvdata(struct input_dev *dev, void *data)
dev->private = data; dev->private = data;
} }
int input_register_device(struct input_dev *); int __must_check input_register_device(struct input_dev *);
void input_unregister_device(struct input_dev *); void input_unregister_device(struct input_dev *);
int input_register_handler(struct input_handler *); int __must_check input_register_handler(struct input_handler *);
void input_unregister_handler(struct input_handler *); void input_unregister_handler(struct input_handler *);
int input_register_handle(struct input_handle *); int input_register_handle(struct input_handle *);
...@@ -1216,7 +1312,7 @@ extern struct class input_class; ...@@ -1216,7 +1312,7 @@ extern struct class input_class;
* @max_effects: maximum number of effects supported by device * @max_effects: maximum number of effects supported by device
* @effects: pointer to an array of effects currently loaded into device * @effects: pointer to an array of effects currently loaded into device
* @effect_owners: array of effect owners; when file handle owning * @effect_owners: array of effect owners; when file handle owning
* an effect gets closed the effcet is automatically erased * an effect gets closed the effect is automatically erased
* *
* Every force-feedback device must implement upload() and playback() * Every force-feedback device must implement upload() and playback()
* methods; erase() is optional. set_gain() and set_autocenter() need * methods; erase() is optional. set_gain() and set_autocenter() need
......
...@@ -416,6 +416,7 @@ extern unsigned short plain_map[NR_KEYS]; ...@@ -416,6 +416,7 @@ extern unsigned short plain_map[NR_KEYS];
#define K_SHIFTRLOCK K(KT_LOCK,KG_SHIFTR) #define K_SHIFTRLOCK K(KT_LOCK,KG_SHIFTR)
#define K_CTRLLLOCK K(KT_LOCK,KG_CTRLL) #define K_CTRLLLOCK K(KT_LOCK,KG_CTRLL)
#define K_CTRLRLOCK K(KT_LOCK,KG_CTRLR) #define K_CTRLRLOCK K(KT_LOCK,KG_CTRLR)
#define K_CAPSSHIFTLOCK K(KT_LOCK,KG_CAPSSHIFT)
#define K_SHIFT_SLOCK K(KT_SLOCK,KG_SHIFT) #define K_SHIFT_SLOCK K(KT_SLOCK,KG_SHIFT)
#define K_CTRL_SLOCK K(KT_SLOCK,KG_CTRL) #define K_CTRL_SLOCK K(KT_SLOCK,KG_CTRL)
...@@ -425,8 +426,9 @@ extern unsigned short plain_map[NR_KEYS]; ...@@ -425,8 +426,9 @@ extern unsigned short plain_map[NR_KEYS];
#define K_SHIFTR_SLOCK K(KT_SLOCK,KG_SHIFTR) #define K_SHIFTR_SLOCK K(KT_SLOCK,KG_SHIFTR)
#define K_CTRLL_SLOCK K(KT_SLOCK,KG_CTRLL) #define K_CTRLL_SLOCK K(KT_SLOCK,KG_CTRLL)
#define K_CTRLR_SLOCK K(KT_SLOCK,KG_CTRLR) #define K_CTRLR_SLOCK K(KT_SLOCK,KG_CTRLR)
#define K_CAPSSHIFT_SLOCK K(KT_SLOCK,KG_CAPSSHIFT)
#define NR_LOCK 8 #define NR_LOCK 9
#define K_BRL_BLANK K(KT_BRL, 0) #define K_BRL_BLANK K(KT_BRL, 0)
#define K_BRL_DOT1 K(KT_BRL, 1) #define K_BRL_DOT1 K(KT_BRL, 1)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment