]> nv-tegra.nvidia Code Review - linux-2.6.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
authorLinus Torvalds <torvalds@woody.linux-foundation.org>
Mon, 15 Oct 2007 20:41:39 +0000 (13:41 -0700)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Mon, 15 Oct 2007 20:41:39 +0000 (13:41 -0700)
* '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..

42 files changed:
Documentation/feature-removal-schedule.txt
Documentation/kernel-parameters.txt
arch/blackfin/mach-bf548/boards/ezkit.c
drivers/char/ec3104_keyb.c [deleted file]
drivers/hwmon/Kconfig
drivers/hwmon/ams/ams-input.c
drivers/hwmon/ams/ams.h
drivers/hwmon/applesmc.c
drivers/hwmon/hdaps.c
drivers/input/Kconfig
drivers/input/Makefile
drivers/input/evdev.c
drivers/input/input-polldev.c
drivers/input/input.c
drivers/input/joydev.c
drivers/input/joystick/xpad.c
drivers/input/keyboard/Kconfig
drivers/input/keyboard/Makefile
drivers/input/keyboard/bf54x-keys.c [new file with mode: 0644]
drivers/input/keyboard/gpio_keys.c
drivers/input/keyboard/jornada680_kbd.c [new file with mode: 0644]
drivers/input/keyboard/jornada720_kbd.c [new file with mode: 0644]
drivers/input/keyboard/maple_keyb.c [new file with mode: 0644]
drivers/input/keyboard/omap-keypad.c
drivers/input/mouse/alps.c
drivers/input/mouse/appletouch.c
drivers/input/mouse/lifebook.c
drivers/input/mouse/psmouse-base.c
drivers/input/mousedev.c
drivers/input/serio/i8042.c
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/jornada720_ts.c [new file with mode: 0644]
drivers/input/touchscreen/ucb1400_ts.c
drivers/input/touchscreen/usbtouchscreen.c
drivers/input/tsdev.c [deleted file]
drivers/macintosh/Kconfig
drivers/macintosh/adbhid.c
include/asm-blackfin/mach-bf548/bf54x_keys.h [new file with mode: 0644]
include/linux/gpio_keys.h
include/linux/input.h
include/linux/keyboard.h

index 63df2262d41a2625da0816a0383aaaece00963bc..fb8258ebc577bcfaa7923f9ee2ebc7736cb69a6a 100644 (file)
@@ -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
 When:  September 2007
 Why:   Obsolete. The new i2c-gpio driver replaces all hardware-specific
index fdd6dbcf864e127f69402f706fb27789ac0ac3d2..085e4a095eaaec661ae46a61dda993af3c3cb060 100644 (file)
@@ -1890,9 +1890,6 @@ and is between 256 and 4096 characters. It is defined in the file
                        Format:
                        <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 parallel port interface
                        Format:
index 2c47db494f7de50cef4858d1ee4394ec2852031a..046e6d84bbfc7d36f4640e8ab01ce020cc8950c4 100644 (file)
@@ -88,7 +88,7 @@ static struct platform_device bf54x_lq043_device = {
 #endif
 
 #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, 1, KEY_HELP),
        KEYVAL(0, 2, KEY_0),
@@ -110,8 +110,8 @@ static int bf548_keymap[] = {
 static struct bfin_kpad_platform_data bf54x_kpad_data = {
        .rows                   = 4,
        .cols                   = 4,
-       .keymap                 = bf548_keymap,
-       .keymapsize             = ARRAY_SIZE(bf548_keymap),
+       .keymap                 = bf548_keymap,
+       .keymapsize             = ARRAY_SIZE(bf548_keymap),
        .repeat                 = 0,
        .debounce_time          = 5000, /* ns (5ms) */
        .coldrive_time          = 1000, /* ns (1ms) */
diff --git a/drivers/char/ec3104_keyb.c b/drivers/char/ec3104_keyb.c
deleted file mode 100644 (file)
index 0200114..0000000
+++ /dev/null
@@ -1,457 +0,0 @@
-/*
- * 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);
-}
index e47f88170806f0cff4ec3ac56b9d63273e5cad9f..700a1657554f4b3b5f4910268343790ddb33c1eb 100644 (file)
@@ -158,6 +158,7 @@ config SENSORS_K8TEMP
 config SENSORS_AMS
        tristate "Apple Motion Sensor driver"
        depends on PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL
+       select INPUT_POLLDEV
        help
          Support for the motion sensor included in PowerBooks. Includes
          implementations for PMU and I2C.
@@ -701,6 +702,7 @@ config SENSORS_W83627EHF
 config SENSORS_HDAPS
        tristate "IBM Hard Drive Active Protection System (hdaps)"
        depends on INPUT && X86
+       select INPUT_POLLDEV
        default n
        help
          This driver provides support for the IBM Hard Drive Active Protection
@@ -722,6 +724,7 @@ config SENSORS_APPLESMC
        depends on INPUT && X86
        select NEW_LEDS
        select LEDS_CLASS
+       select INPUT_POLLDEV
        default n
        help
          This driver provides support for the Apple System Management
index ca7095d96ad0f0f1a786b095f414bb27138c8a00..7b81e0c2c2d9bb4f4a8bbacbfa0024e1edda03f9 100644 (file)
@@ -27,47 +27,32 @@ static unsigned int invert;
 module_param(invert, bool, 0644);
 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;
 
-       while (!kthread_should_stop()) {
-               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);
+       mutex_lock(&ams_info.lock);
 
-               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)
-{
-       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);
+       mutex_unlock(&ams_info.lock);
 }
 
 /* Call with ams_info.lock held! */
 static void ams_input_enable(void)
 {
+       struct input_dev *input;
        s8 x, y, z;
 
        if (ams_info.idev)
@@ -78,27 +63,29 @@ static void ams_input_enable(void)
        ams_info.ycalib = y;
        ams_info.zcalib = z;
 
-       ams_info.idev = input_allocate_device();
+       ams_info.idev = input_allocate_polled_device();
        if (!ams_info.idev)
                return;
 
-       ams_info.idev->name = "Apple Motion Sensor";
-       ams_info.idev->id.bustype = ams_info.bustype;
-       ams_info.idev->id.vendor = 0;
-       ams_info.idev->open = ams_input_open;
-       ams_info.idev->close = ams_input_close;
-       ams_info.idev->dev.parent = &ams_info.of_dev->dev;
+       ams_info.idev->poll = ams_idev_poll;
+       ams_info.idev->poll_interval = 25;
+
+       input = ams_info.idev->input;
+       input->name = "Apple Motion Sensor";
+       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(ams_info.idev, 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_X, -50, 50, 3, 0);
+       input_set_abs_params(input, ABS_Y, -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_KEY, ams_info.idev->evbit);
-       set_bit(BTN_TOUCH, ams_info.idev->keybit);
+       set_bit(EV_ABS, input->evbit);
+       set_bit(EV_KEY, input->evbit);
+       set_bit(BTN_TOUCH, input->keybit);
 
-       if (input_register_device(ams_info.idev)) {
-               input_free_device(ams_info.idev);
+       if (input_register_polled_device(ams_info.idev)) {
+               input_free_polled_device(ams_info.idev);
                ams_info.idev = NULL;
                return;
        }
@@ -108,7 +95,8 @@ static void ams_input_enable(void)
 static void ams_input_disable(void)
 {
        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;
        }
 }
index 240730e6bcde1c98f0b67862d8fb928c0d0d9ff9..a6221e5dd984292613634a6ddf28c775615e6465 100644 (file)
@@ -1,5 +1,5 @@
 #include <linux/i2c.h>
-#include <linux/input.h>
+#include <linux/input-polldev.h>
 #include <linux/kthread.h>
 #include <linux/mutex.h>
 #include <linux/spinlock.h>
@@ -52,8 +52,7 @@ struct ams {
 #endif
 
        /* Joystick emulation */
-       struct task_struct *kthread;
-       struct input_dev *idev;
+       struct input_polled_dev *idev;
        __u16 bustype;
 
        /* calibrated null values */
index f37fd7ebf65ad80b2173796b415d2b9cebacb51d..4879125b4cdc1963534928181aed8088a30f3900 100644 (file)
@@ -28,7 +28,7 @@
 
 #include <linux/delay.h>
 #include <linux/platform_device.h>
-#include <linux/input.h>
+#include <linux/input-polldev.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/timer.h>
@@ -59,9 +59,9 @@
 
 #define LIGHT_SENSOR_LEFT_KEY  "ALV0" /* 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_Y_KEY    "MO_Y" /* r-o sp78 (2 bytes) */
@@ -103,7 +103,7 @@ static const char* fan_speed_keys[] = {
 #define INIT_TIMEOUT_MSECS     5000    /* wait up to 5s for device init ... */
 #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_FLAT    4
 
@@ -125,9 +125,8 @@ static const int debug;
 static struct platform_device *pdev;
 static s16 rest_x;
 static s16 rest_y;
-static struct timer_list applesmc_timer;
-static struct input_dev *applesmc_idev;
 static struct device *hwmon_dev;
+static struct input_polled_dev *applesmc_idev;
 
 /* Indicates whether this computer has an accelerometer. */
 static unsigned int applesmc_accelerometer;
@@ -138,7 +137,7 @@ static unsigned int applesmc_light;
 /* Indicates which temperature sensors set to use. */
 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
@@ -455,27 +454,12 @@ static void applesmc_calibrate(void)
        rest_x = -rest_x;
 }
 
-static int applesmc_idev_open(struct input_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)
+static void applesmc_idev_poll(struct input_polled_dev *dev)
 {
+       struct input_dev *idev = dev->input;
        s16 x, y;
 
-       /* Cannot sleep.  Try nonblockingly.  If we fail, try again later. */
-       if (!mutex_trylock(&applesmc_lock)) {
-               mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
-               return;
-       }
+       mutex_lock(&applesmc_lock);
 
        if (applesmc_read_motion_sensor(SENSOR_X, &x))
                goto out;
@@ -483,13 +467,11 @@ static void applesmc_idev_poll(unsigned long unused)
                goto out;
 
        x = -x;
-       input_report_abs(applesmc_idev, ABS_X, x - rest_x);
-       input_report_abs(applesmc_idev, ABS_Y, y - rest_y);
-       input_sync(applesmc_idev);
+       input_report_abs(idev, ABS_X, x - rest_x);
+       input_report_abs(idev, ABS_Y, y - rest_y);
+       input_sync(idev);
 
 out:
-       mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
-
        mutex_unlock(&applesmc_lock);
 }
 
@@ -821,8 +803,7 @@ static ssize_t applesmc_key_at_index_read_show(struct device *dev,
 
        if (!ret) {
                return info[0];
-       }
-       else {
+       } else {
                return ret;
        }
 }
@@ -1093,6 +1074,7 @@ static int applesmc_dmi_match(const struct dmi_system_id *id)
 /* Create accelerometer ressources */
 static int applesmc_create_accelerometer(void)
 {
+       struct input_dev *idev;
        int ret;
 
        ret = sysfs_create_group(&pdev->dev.kobj,
@@ -1100,40 +1082,37 @@ static int applesmc_create_accelerometer(void)
        if (ret)
                goto out;
 
-       applesmc_idev = input_allocate_device();
+       applesmc_idev = input_allocate_polled_device();
        if (!applesmc_idev) {
                ret = -ENOMEM;
                goto out_sysfs;
        }
 
+       applesmc_idev->poll = applesmc_idev_poll;
+       applesmc_idev->poll_interval = APPLESMC_POLL_INTERVAL;
+
        /* initial calibrate for the input device */
        applesmc_calibrate();
 
-       /* initialize the input class */
-       applesmc_idev->name = "applesmc";
-       applesmc_idev->id.bustype = BUS_HOST;
-       applesmc_idev->dev.parent = &pdev->dev;
-       applesmc_idev->evbit[0] = BIT(EV_ABS);
-       applesmc_idev->open = applesmc_idev_open;
-       applesmc_idev->close = applesmc_idev_close;
-       input_set_abs_params(applesmc_idev, ABS_X,
+       /* initialize the input device */
+       idev = applesmc_idev->input;
+       idev->name = "applesmc";
+       idev->id.bustype = BUS_HOST;
+       idev->dev.parent = &pdev->dev;
+       idev->evbit[0] = BIT(EV_ABS);
+       input_set_abs_params(idev, ABS_X,
                        -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);
 
-       ret = input_register_device(applesmc_idev);
+       ret = input_register_polled_device(applesmc_idev);
        if (ret)
                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;
 
 out_idev:
-       input_free_device(applesmc_idev);
+       input_free_polled_device(applesmc_idev);
 
 out_sysfs:
        sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
@@ -1146,8 +1125,8 @@ out:
 /* Release all ressources used by the accelerometer */
 static void applesmc_release_accelerometer(void)
 {
-       del_timer_sync(&applesmc_timer);
-       input_unregister_device(applesmc_idev);
+       input_unregister_polled_device(applesmc_idev);
+       input_free_polled_device(applesmc_idev);
        sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
 }
 
@@ -1184,8 +1163,6 @@ static int __init applesmc_init(void)
        int count;
        int i;
 
-       mutex_init(&applesmc_lock);
-
        if (!dmi_check_system(applesmc_whitelist)) {
                printk(KERN_WARNING "applesmc: supported laptop not found!\n");
                ret = -ENODEV;
index a7c6d407572b43c53a417c939cb8cdc6fdacecf3..8a7ae03aeee4e5aa1adb629ce77055b10c2a1f77 100644 (file)
@@ -28,7 +28,7 @@
 
 #include <linux/delay.h>
 #include <linux/platform_device.h>
-#include <linux/input.h>
+#include <linux/input-polldev.h>
 #include <linux/kernel.h>
 #include <linux/mutex.h>
 #include <linux/module.h>
 #define INIT_TIMEOUT_MSECS     4000    /* wait up to 4s for device init ... */
 #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_FLAT       4
 
-static struct timer_list hdaps_timer;
 static struct platform_device *pdev;
-static struct input_dev *hdaps_idev;
+static struct input_polled_dev *hdaps_idev;
 static unsigned int hdaps_invert;
 static u8 km_activity;
 static int rest_x;
@@ -323,24 +322,19 @@ static void hdaps_calibrate(void)
        __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;
 
-       /* Cannot sleep.  Try nonblockingly.  If we fail, try again later. */
-       if (mutex_trylock(&hdaps_mtx)) {
-               mod_timer(&hdaps_timer,jiffies + HDAPS_POLL_PERIOD);
-               return;
-       }
+       mutex_lock(&hdaps_mtx);
 
        if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y))
                goto out;
 
-       input_report_abs(hdaps_idev, ABS_X, x - rest_x);
-       input_report_abs(hdaps_idev, ABS_Y, y - rest_y);
-       input_sync(hdaps_idev);
-
-       mod_timer(&hdaps_timer, jiffies + HDAPS_POLL_PERIOD);
+       input_report_abs(input_dev, ABS_X, x - rest_x);
+       input_report_abs(input_dev, ABS_Y, y - rest_y);
+       input_sync(input_dev);
 
 out:
        mutex_unlock(&hdaps_mtx);
@@ -536,6 +530,7 @@ static struct dmi_system_id __initdata hdaps_whitelist[] = {
 
 static int __init hdaps_init(void)
 {
+       struct input_dev *idev;
        int ret;
 
        if (!dmi_check_system(hdaps_whitelist)) {
@@ -563,39 +558,37 @@ static int __init hdaps_init(void)
        if (ret)
                goto out_device;
 
-       hdaps_idev = input_allocate_device();
+       hdaps_idev = input_allocate_polled_device();
        if (!hdaps_idev) {
                ret = -ENOMEM;
                goto out_group;
        }
 
+       hdaps_idev->poll = hdaps_mousedev_poll;
+       hdaps_idev->poll_interval = HDAPS_POLL_INTERVAL;
+
        /* initial calibrate for the input device */
        hdaps_calibrate();
 
        /* initialize the input class */
-       hdaps_idev->name = "hdaps";
-       hdaps_idev->dev.parent = &pdev->dev;
-       hdaps_idev->evbit[0] = BIT(EV_ABS);
-       input_set_abs_params(hdaps_idev, ABS_X,
+       idev = hdaps_idev->input;
+       idev->name = "hdaps";
+       idev->dev.parent = &pdev->dev;
+       idev->evbit[0] = BIT(EV_ABS);
+       input_set_abs_params(idev, ABS_X,
                        -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);
 
-       ret = input_register_device(hdaps_idev);
+       ret = input_register_polled_device(hdaps_idev);
        if (ret)
                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");
        return 0;
 
 out_idev:
-       input_free_device(hdaps_idev);
+       input_free_polled_device(hdaps_idev);
 out_group:
        sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
 out_device:
@@ -611,8 +604,8 @@ out:
 
 static void __exit hdaps_exit(void)
 {
-       del_timer_sync(&hdaps_timer);
-       input_unregister_device(hdaps_idev);
+       input_unregister_polled_device(hdaps_idev);
+       input_free_polled_device(hdaps_idev);
        sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
        platform_device_unregister(pdev);
        platform_driver_unregister(&hdaps_driver);
index 2d87357e2b2b5fa38217201fa5ea900f11afe6c8..63512d906f0201e085294845ae928c642dd4ff7d 100644 (file)
@@ -114,28 +114,6 @@ config INPUT_JOYDEV
          To compile this driver as a module, choose M here: the
          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
        tristate "Event interface"
        help
index 15eb752697b37d691e57adec9d5a805ad00265e2..99af903bd3ce57c747e8869da4e9883d2d566263 100644 (file)
@@ -13,7 +13,6 @@ obj-$(CONFIG_INPUT_POLLDEV)   += input-polldev.o
 obj-$(CONFIG_INPUT_MOUSEDEV)   += mousedev.o
 obj-$(CONFIG_INPUT_JOYDEV)     += joydev.o
 obj-$(CONFIG_INPUT_EVDEV)      += evdev.o
-obj-$(CONFIG_INPUT_TSDEV)      += tsdev.o
 obj-$(CONFIG_INPUT_EVBUG)      += evbug.o
 
 obj-$(CONFIG_INPUT_KEYBOARD)   += keyboard/
index f1c3d6cebd585a5392c96e0ed1a25e9cf275a73a..1d62c8b88e121307427a491a204a023c9510c3d5 100644 (file)
@@ -30,6 +30,8 @@ struct evdev {
        wait_queue_head_t wait;
        struct evdev_client *grab;
        struct list_head client_list;
+       spinlock_t client_lock; /* protects client_list */
+       struct mutex mutex;
        struct device dev;
 };
 
@@ -37,39 +39,54 @@ struct evdev_client {
        struct input_event buffer[EVDEV_BUFFER_SIZE];
        int head;
        int tail;
+       spinlock_t buffer_lock; /* protects access to buffer, head and tail */
        struct fasync_struct *fasync;
        struct evdev *evdev;
        struct list_head node;
 };
 
 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_client *client;
+       struct input_event event;
 
-       if (evdev->grab) {
-               client = evdev->grab;
+       do_gettimeofday(&event.time);
+       event.type = type;
+       event.code = code;
+       event.value = value;
 
-               do_gettimeofday(&client->buffer[client->head].time);
-               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);
+       rcu_read_lock();
 
-               kill_fasync(&client->fasync, SIGIO, POLL_IN);
-       } else
-               list_for_each_entry(client, &evdev->client_list, node) {
+       client = rcu_dereference(evdev->grab);
+       if (client)
+               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);
-                       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);
-               }
+       rcu_read_unlock();
 
        wake_up_interruptible(&evdev->wait);
 }
@@ -88,38 +105,140 @@ static int evdev_flush(struct file *file, fl_owner_t id)
 {
        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)
-               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)
 {
        struct evdev *evdev = container_of(dev, struct evdev, dev);
 
-       evdev_table[evdev->minor] = NULL;
        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)
 {
        struct evdev_client *client = file->private_data;
        struct evdev *evdev = client->evdev;
 
-       if (evdev->grab == client) {
-               input_release_device(&evdev->handle);
-               evdev->grab = NULL;
-       }
+       mutex_lock(&evdev->mutex);
+       if (evdev->grab == client)
+               evdev_ungrab(evdev, client);
+       mutex_unlock(&evdev->mutex);
 
        evdev_fasync(-1, file, 0);
-       list_del(&client->node);
+       evdev_detach_client(evdev, client);
        kfree(client);
 
-       if (!--evdev->open && evdev->exist)
-               input_close_device(&evdev->handle);
-
+       evdev_close_device(evdev);
        put_device(&evdev->dev);
 
        return 0;
@@ -127,41 +246,44 @@ static int evdev_release(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_client *client;
        int i = iminor(inode) - EVDEV_MINOR_BASE;
        int error;
 
        if (i >= EVDEV_MINORS)
                return -ENODEV;
 
+       error = mutex_lock_interruptible(&evdev_table_mutex);
+       if (error)
+               return error;
        evdev = evdev_table[i];
+       if (evdev)
+               get_device(&evdev->dev);
+       mutex_unlock(&evdev_table_mutex);
 
-       if (!evdev || !evdev->exist)
+       if (!evdev)
                return -ENODEV;
 
-       get_device(&evdev->dev);
-
        client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
        if (!client) {
                error = -ENOMEM;
                goto err_put_evdev;
        }
 
+       spin_lock_init(&client->buffer_lock);
        client->evdev = evdev;
-       list_add_tail(&client->node, &evdev->client_list);
+       evdev_attach_client(evdev, client);
 
-       if (!evdev->open++ && evdev->exist) {
-               error = input_open_device(&evdev->handle);
-               if (error)
-                       goto err_free_client;
-       }
+       error = evdev_open_device(evdev);
+       if (error)
+               goto err_free_client;
 
        file->private_data = client;
        return 0;
 
  err_free_client:
-       list_del(&client->node);
+       evdev_detach_client(evdev, client);
        kfree(client);
  err_put_evdev:
        put_device(&evdev->dev);
@@ -197,12 +319,14 @@ static inline size_t evdev_event_size(void)
                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) {
                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;
 
                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 *
        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) {
                struct input_event_compat compat_event;
@@ -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.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;
 
        } else {
@@ -248,7 +374,8 @@ static inline size_t evdev_event_size(void)
        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)))
                return -EFAULT;
@@ -256,7 +383,8 @@ static int evdev_event_from_user(const char __user *buffer, struct input_event *
        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)))
                return -EFAULT;
@@ -266,37 +394,71 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev
 
 #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 *evdev = client->evdev;
        struct input_event event;
-       int retval = 0;
+       int retval;
 
-       if (!evdev->exist)
-               return -ENODEV;
+       retval = mutex_lock_interruptible(&evdev->mutex);
+       if (retval)
+               return retval;
+
+       if (!evdev->exist) {
+               retval = -ENODEV;
+               goto out;
+       }
 
        while (retval < count) {
 
-               if (evdev_event_from_user(buffer + retval, &event))
-                       return -EFAULT;
-               input_inject_event(&evdev->handle, event.type, event.code, event.value);
+               if (evdev_event_from_user(buffer + retval, &event)) {
+                       retval = -EFAULT;
+                       goto out;
+               }
+
+               input_inject_event(&evdev->handle,
+                                  event.type, event.code, event.value);
                retval += evdev_event_size();
        }
 
+ out:
+       mutex_unlock(&evdev->mutex);
        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 *evdev = client->evdev;
+       struct input_event event;
        int retval;
 
        if (count < evdev_event_size())
                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;
 
        retval = wait_event_interruptible(evdev->wait,
@@ -307,14 +469,12 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count,
        if (!evdev->exist)
                return -ENODEV;
 
-       while (client->head != client->tail && retval + evdev_event_size() <= count) {
-
-               struct input_event *event = (struct input_event *) client->buffer + client->tail;
+       while (retval + evdev_event_size() <= count &&
+              evdev_fetch_next_event(client, &event)) {
 
-               if (evdev_event_to_user(buffer + retval, event))
+               if (evdev_event_to_user(buffer + retval, &event))
                        return -EFAULT;
 
-               client->tail = (client->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
                retval += evdev_event_size();
        }
 
@@ -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;
 }
 
-static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
-                               void __user *p, int compat_mode)
+static long evdev_do_ioctl(struct file *file, unsigned int cmd,
+                          void __user *p, int compat_mode)
 {
        struct evdev_client *client = file->private_data;
        struct evdev *evdev = client->evdev;
@@ -421,215 +581,289 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
        int i, t, u, v;
        int error;
 
-       if (!evdev->exist)
-               return -ENODEV;
-
        switch (cmd) {
 
-               case EVIOCGVERSION:
-                       return put_user(EV_VERSION, ip);
+       case EVIOCGVERSION:
+               return put_user(EV_VERSION, ip);
 
-               case EVIOCGID:
-                       if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
-                               return -EFAULT;
-                       return 0;
+       case EVIOCGID:
+               if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
+                       return -EFAULT;
+               return 0;
 
-               case EVIOCGREP:
-                       if (!test_bit(EV_REP, dev->evbit))
-                               return -ENOSYS;
-                       if (put_user(dev->rep[REP_DELAY], ip))
-                               return -EFAULT;
-                       if (put_user(dev->rep[REP_PERIOD], ip + 1))
-                               return -EFAULT;
-                       return 0;
+       case EVIOCGREP:
+               if (!test_bit(EV_REP, dev->evbit))
+                       return -ENOSYS;
+               if (put_user(dev->rep[REP_DELAY], ip))
+                       return -EFAULT;
+               if (put_user(dev->rep[REP_PERIOD], ip + 1))
+                       return -EFAULT;
+               return 0;
 
-               case EVIOCSREP:
-                       if (!test_bit(EV_REP, dev->evbit))
-                               return -ENOSYS;
-                       if (get_user(u, ip))
-                               return -EFAULT;
-                       if (get_user(v, ip + 1))
-                               return -EFAULT;
+       case EVIOCSREP:
+               if (!test_bit(EV_REP, dev->evbit))
+                       return -ENOSYS;
+               if (get_user(u, ip))
+                       return -EFAULT;
+               if (get_user(v, ip + 1))
+                       return -EFAULT;
 
-                       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_DELAY, u);
+               input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v);
 
-                       return 0;
+               return 0;
 
-               case EVIOCGKEYCODE:
-                       if (get_user(t, ip))
-                               return -EFAULT;
+       case EVIOCGKEYCODE:
+               if (get_user(t, ip))
+                       return -EFAULT;
 
-                       error = dev->getkeycode(dev, t, &v);
-                       if (error)
-                               return error;
+               error = dev->getkeycode(dev, t, &v);
+               if (error)
+                       return error;
 
-                       if (put_user(v, ip + 1))
-                               return -EFAULT;
+               if (put_user(v, ip + 1))
+                       return -EFAULT;
 
-                       return 0;
+               return 0;
 
-               case EVIOCSKEYCODE:
-                       if (get_user(t, ip) || get_user(v, ip + 1))
-                               return -EFAULT;
+       case EVIOCSKEYCODE:
+               if (get_user(t, ip) || get_user(v, ip + 1))
+                       return -EFAULT;
 
-                       return dev->setkeycode(dev, t, v);
+               return dev->setkeycode(dev, t, v);
 
-               case EVIOCSFF:
-                       if (copy_from_user(&effect, p, sizeof(effect)))
-                               return -EFAULT;
+       case EVIOCSFF:
+               if (copy_from_user(&effect, p, sizeof(effect)))
+                       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)))
-                               return -EFAULT;
+               if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
+                       return -EFAULT;
 
-                       return error;
+               return error;
 
-               case EVIOCRMFF:
-                       return input_ff_erase(dev, (int)(unsigned long) p, file);
+       case EVIOCRMFF:
+               return input_ff_erase(dev, (int)(unsigned long) p, file);
 
-               case EVIOCGEFFECTS:
-                       i = test_bit(EV_FF, dev->evbit) ? dev->ff->max_effects : 0;
-                       if (put_user(i, ip))
-                               return -EFAULT;
-                       return 0;
-
-               case EVIOCGRAB:
-                       if (p) {
-                               if (evdev->grab)
-                                       return -EBUSY;
-                               if (input_grab_device(&evdev->handle))
-                                       return -EBUSY;
-                               evdev->grab = client;
-                               return 0;
-                       } else {
-                               if (evdev->grab != client)
-                                       return -EINVAL;
-                               input_release_device(&evdev->handle);
-                               evdev->grab = NULL;
-                               return 0;
-                       }
+       case EVIOCGEFFECTS:
+               i = test_bit(EV_FF, dev->evbit) ?
+                               dev->ff->max_effects : 0;
+               if (put_user(i, ip))
+                       return -EFAULT;
+               return 0;
+
+       case EVIOCGRAB:
+               if (p)
+                       return evdev_grab(evdev, client);
+               else
+                       return evdev_ungrab(evdev, client);
 
-               default:
+       default:
 
-                       if (_IOC_TYPE(cmd) != 'E')
-                               return -EINVAL;
+               if (_IOC_TYPE(cmd) != 'E')
+                       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;
-                                       int len;
+                               unsigned long *bits;
+                               int len;
 
-                                       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);
-                               }
+                               switch (_IOC_NR(cmd) & EV_MAX) {
 
-                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0)))
-                                       return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd),
-                                                           p, compat_mode);
+                               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(EVIOCGLED(0)))
-                                       return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd),
-                                                           p, compat_mode);
+                       if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0)))
+                               return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd),
+                                                   p, compat_mode);
 
-                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
-                                       return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd),
-                                                           p, compat_mode);
+                       if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0)))
+                               return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd),
+                                                   p, compat_mode);
 
-                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
-                                       return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd),
-                                                           p, compat_mode);
+                       if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
+                               return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd),
+                                                   p, compat_mode);
 
-                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0)))
-                                       return str_to_user(dev->name, _IOC_SIZE(cmd), p);
+                       if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
+                               return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd),
+                                                   p, compat_mode);
 
-                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
-                                       return str_to_user(dev->phys, _IOC_SIZE(cmd), p);
+                       if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0)))
+                               return str_to_user(dev->name, _IOC_SIZE(cmd), p);
 
-                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0)))
-                                       return str_to_user(dev->uniq, _IOC_SIZE(cmd), p);
+                       if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
+                               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];
-                                       abs.minimum = dev->absmin[t];
-                                       abs.maximum = dev->absmax[t];
-                                       abs.fuzz = dev->absfuzz[t];
-                                       abs.flat = dev->absflat[t];
+                               t = _IOC_NR(cmd) & ABS_MAX;
 
-                                       if (copy_to_user(p, &abs, sizeof(struct input_absinfo)))
-                                               return -EFAULT;
+                               abs.value = dev->abs[t];
+                               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)))
-                                               return -EFAULT;
+                               if (copy_from_user(&abs, p,
+                                               sizeof(struct input_absinfo)))
+                                       return -EFAULT;
 
-                                       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;
+                               /*
+                                * Take event lock to ensure that we are not
+                                * changing device parameters in the middle
+                                * of event.
+                                */
+                               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;
 }
 
+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)
 {
        return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);
 }
 
 #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);
 }
 #endif
 
 static const struct file_operations evdev_fops = {
-       .owner =        THIS_MODULE,
-       .read =         evdev_read,
-       .write =        evdev_write,
-       .poll =         evdev_poll,
-       .open =         evdev_open,
-       .release =      evdev_release,
-       .unlocked_ioctl = evdev_ioctl,
+       .owner          = THIS_MODULE,
+       .read           = evdev_read,
+       .write          = evdev_write,
+       .poll           = evdev_poll,
+       .open           = evdev_open,
+       .release        = evdev_release,
+       .unlocked_ioctl = evdev_ioctl,
 #ifdef CONFIG_COMPAT
-       .compat_ioctl = evdev_ioctl_compat,
+       .compat_ioctl   = evdev_ioctl_compat,
 #endif
-       .fasync =       evdev_fasync,
-       .flush =        evdev_flush
+       .fasync         = evdev_fasync,
+       .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,
                         const struct input_device_id *id)
 {
@@ -637,7 +871,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
        int minor;
        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) {
                printk(KERN_ERR "evdev: no more free evdev devices\n");
                return -ENFILE;
@@ -648,38 +885,44 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
                return -ENOMEM;
 
        INIT_LIST_HEAD(&evdev->client_list);
+       spin_lock_init(&evdev->client_lock);
+       mutex_init(&evdev->mutex);
        init_waitqueue_head(&evdev->wait);
 
+       snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
        evdev->exist = 1;
        evdev->minor = minor;
+
        evdev->handle.dev = dev;
        evdev->handle.name = evdev->name;
        evdev->handle.handler = handler;
        evdev->handle.private = evdev;
-       snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
 
-       snprintf(evdev->dev.bus_id, sizeof(evdev->dev.bus_id),
-                "event%d", minor);
+       strlcpy(evdev->dev.bus_id, evdev->name, sizeof(evdev->dev.bus_id));
+       evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
        evdev->dev.class = &input_class;
        evdev->dev.parent = &dev->dev;
-       evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
        evdev->dev.release = evdev_free;
        device_initialize(&evdev->dev);
 
-       evdev_table[minor] = evdev;
-
-       error = device_add(&evdev->dev);
+       error = input_register_handle(&evdev->handle);
        if (error)
                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)
-               goto err_delete_evdev;
+               goto err_cleanup_evdev;
 
        return 0;
 
- err_delete_evdev:
-       device_del(&evdev->dev);
+ err_cleanup_evdev:
+       evdev_cleanup(evdev);
+ err_unregister_handle:
+       input_unregister_handle(&evdev->handle);
  err_free_evdev:
        put_device(&evdev->dev);
        return error;
@@ -688,21 +931,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
 static void evdev_disconnect(struct input_handle *handle)
 {
        struct evdev *evdev = handle->private;
-       struct evdev_client *client;
 
-       input_unregister_handle(handle);
        device_del(&evdev->dev);
-
-       evdev->exist = 0;
-
-       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);
-       }
-
+       evdev_cleanup(evdev);
+       input_unregister_handle(handle);
        put_device(&evdev->dev);
 }
 
@@ -714,13 +946,13 @@ static const struct input_device_id evdev_ids[] = {
 MODULE_DEVICE_TABLE(input, evdev_ids);
 
 static struct input_handler evdev_handler = {
-       .event =        evdev_event,
-       .connect =      evdev_connect,
-       .disconnect =   evdev_disconnect,
-       .fops =         &evdev_fops,
-       .minor =        EVDEV_MINOR_BASE,
-       .name =         "evdev",
-       .id_table =     evdev_ids,
+       .event          = evdev_event,
+       .connect        = evdev_connect,
+       .disconnect     = evdev_disconnect,
+       .fops           = &evdev_fops,
+       .minor          = EVDEV_MINOR_BASE,
+       .name           = "evdev",
+       .id_table       = evdev_ids,
 };
 
 static int __init evdev_init(void)
index b773d4c756a61333d1e5d65ae5cc282cbe01815b..92b359894e81ed89d7608c90e4f52a2173daa8c3 100644 (file)
@@ -70,6 +70,7 @@ static int input_open_polled_device(struct input_dev *input)
 {
        struct input_polled_dev *dev = input->private;
        int error;
+       unsigned long ticks;
 
        error = input_polldev_start_workqueue();
        if (error)
@@ -78,8 +79,10 @@ static int input_open_polled_device(struct input_dev *input)
        if (dev->flush)
                dev->flush(dev);
 
-       queue_delayed_work(polldev_wq, &dev->work,
-                          msecs_to_jiffies(dev->poll_interval));
+       ticks = msecs_to_jiffies(dev->poll_interval);
+       if (ticks >= HZ)
+               ticks = round_jiffies(ticks);
+       queue_delayed_work(polldev_wq, &dev->work, ticks);
 
        return 0;
 }
index 5dc361c954e2aa92a3632117bdd4575bc9f23382..2f2b020cd6294109da6e757e2968c12bca97aaef 100644 (file)
 #include <linux/major.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
-#include <linux/interrupt.h>
 #include <linux/poll.h>
 #include <linux/device.h>
 #include <linux/mutex.h>
+#include <linux/rcupdate.h>
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
 MODULE_DESCRIPTION("Input core");
@@ -31,167 +31,245 @@ MODULE_LICENSE("GPL");
 static LIST_HEAD(input_dev_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];
 
-/**
- * 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()
- */
-void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+static inline int is_event_supported(unsigned int code,
+                                    unsigned long *bm, unsigned int max)
 {
-       struct input_handle *handle;
+       return code <= max && test_bit(code, bm);
+}
 
-       if (type > EV_MAX || !test_bit(type, dev->evbit))
-               return;
+static int input_defuzz_abs_event(int value, int old_val, int fuzz)
+{
+       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) {
-
-               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;
+               if (value > old_val - fuzz * 2 && value < old_val + fuzz * 2)
+                       return (old_val + value) / 2;
+       }
 
-               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)
-                               break;
+       rcu_read_lock();
 
-                       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;
-                               mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
-                       }
+/*
+ * Generate software autorepeat event. Note that we take
+ * 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)
-                               return;
+               input_pass_event(dev, EV_KEY, dev->repeat_key, 2);
 
-                       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))
-                               return;
+static void input_start_autorepeat(struct input_dev *dev, int code)
+{
+       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]) {
-                               if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) &&
-                                   (value < dev->abs[code] + (dev->absfuzz[code] >> 1)))
-                                       return;
+#define INPUT_IGNORE_EVENT     0
+#define INPUT_PASS_TO_HANDLERS 1
+#define INPUT_PASS_TO_DEVICE   2
+#define INPUT_PASS_TO_ALL      (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
 
-                               if ((value > dev->abs[code] - dev->absfuzz[code]) &&
-                                   (value < dev->abs[code] + dev->absfuzz[code]))
-                                       value = (dev->abs[code] * 3 + value) >> 2;
+static void input_handle_event(struct input_dev *dev,
+                              unsigned int type, unsigned int code, int value)
+{
+       int disposition = INPUT_IGNORE_EVENT;
 
-                               if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) &&
-                                   (value < dev->abs[code] + (dev->absfuzz[code] << 1)))
-                                       value = (dev->abs[code] + value) >> 1;
-                       }
+       switch (type) {
 
-                       if (dev->abs[code] == value)
-                               return;
+       case EV_SYN:
+               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;
 
-               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))
-                               return;
+                       if (value != 2) {
+                               __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))
-                               return;
+                       __change_bit(code, dev->sw);
+                       disposition = INPUT_PASS_TO_HANDLERS;
+               }
+               break;
 
-                       if (dev->event)
-                               dev->event(dev, type, code, value);
+       case EV_ABS:
+               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)
-                               return;
+               break;
 
-                       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)
-                               dev->event(dev, type, code, value);
+               break;
 
-                       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))
-                               return;
+       case EV_SND:
+               if (is_event_supported(code, dev->sndbit, SND_MAX)) {
 
                        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)
-                               dev->event(dev, type, code, value);
+       case EV_REP:
+               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)
-                               return;
+       if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
+               dev->event(dev, type, code, value);
 
-                       dev->rep[code] = value;
-                       if (dev->event)
-                               dev->event(dev, type, code, value);
+       if (disposition & INPUT_PASS_TO_HANDLERS)
+               input_pass_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)
-                               return;
+       if (is_event_supported(type, dev->evbit, EV_MAX)) {
 
-                       if (dev->event)
-                               dev->event(dev, type, code, value);
-                       break;
+               spin_lock_irqsave(&dev->event_lock, flags);
+               add_input_randomness(type, code, value);
+               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);
 
@@ -202,102 +280,228 @@ EXPORT_SYMBOL(input_event);
  * @code: event code
  * @value: value of the event
  *
- * Similar to input_event() but will ignore event if device is "grabbed" and handle
- * injecting event is not the one that owns the device.
+ * Similar to input_event() but will ignore event if device is
+ * "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)
-{
-       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)
+void input_inject_event(struct input_handle *handle,
+                       unsigned int type, unsigned int code, int value)
 {
-       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))
-               return;
+       if (is_event_supported(type, dev->evbit, EV_MAX)) {
+               spin_lock_irqsave(&dev->event_lock, flags);
 
-       input_event(dev, EV_KEY, dev->repeat_key, 2);
-       input_sync(dev);
+               rcu_read_lock();
+               grab = rcu_dereference(dev->grab);
+               if (!grab || grab == handle)
+                       input_handle_event(dev, type, code, value);
+               rcu_read_unlock();
 
-       if (dev->rep[REP_PERIOD])
-               mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD]));
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+       }
 }
+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)
 {
-       if (handle->dev->grab)
-               return -EBUSY;
+       struct input_dev *dev = handle->dev;
+       int retval;
 
-       handle->dev->grab = handle;
-       return 0;
+       retval = mutex_lock_interruptible(&dev->mutex);
+       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);
 
-void input_release_device(struct input_handle *handle)
+static void __input_release_device(struct input_handle *handle)
 {
        struct input_dev *dev = handle->dev;
 
        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)
-                       if (handle->handler->start)
+                       if (handle->open && handle->handler->start)
                                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);
 
+/**
+ * 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)
 {
        struct input_dev *dev = handle->dev;
-       int err;
+       int retval;
 
-       err = mutex_lock_interruptible(&dev->mutex);
-       if (err)
-               return err;
+       retval = mutex_lock_interruptible(&dev->mutex);
+       if (retval)
+               return retval;
+
+       if (dev->going_away) {
+               retval = -ENODEV;
+               goto out;
+       }
 
        handle->open++;
 
        if (!dev->users++ && dev->open)
-               err = dev->open(dev);
-
-       if (err)
-               handle->open--;
+               retval = dev->open(dev);
+
+       if (retval) {
+               dev->users--;
+               if (!--handle->open) {
+                       /*
+                        * Make sure we are not delivering any more events
+                        * through this handle
+                        */
+                       synchronize_rcu();
+               }
+       }
 
+ out:
        mutex_unlock(&dev->mutex);
-
-       return err;
+       return retval;
 }
 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)
-               return handle->dev->flush(handle->dev, file);
+       struct input_dev *dev = handle->dev;
+       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);
 
+/**
+ * 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)
 {
        struct input_dev *dev = handle->dev;
 
-       input_release_device(handle);
-
        mutex_lock(&dev->mutex);
 
+       __input_release_device(handle);
+
        if (!--dev->users && dev->close)
                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);
 }
 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)
 {
        switch (dev->keycodesize) {
@@ -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)
 {
-       /* 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);
 }
@@ -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)
 {
-       /* release lock here */
+       mutex_unlock(&input_mutex);
 }
 
 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 = {
 
 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;
        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)
 
 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)
@@ -983,6 +1190,7 @@ struct input_dev *input_allocate_device(void)
                dev->dev.class = &input_class;
                device_initialize(&dev->dev);
                mutex_init(&dev->mutex);
+               spin_lock_init(&dev->event_lock);
                INIT_LIST_HEAD(&dev->h_list);
                INIT_LIST_HEAD(&dev->node);
 
@@ -1000,7 +1208,7 @@ EXPORT_SYMBOL(input_allocate_device);
  * This function should only be used if input_register_device()
  * was not called yet or if it failed. Once device was registered
  * 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().
  *
@@ -1070,6 +1278,18 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int
 }
 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)
 {
        static atomic_t input_no = ATOMIC_INIT(0);
@@ -1077,7 +1297,7 @@ int input_register_device(struct input_dev *dev)
        const char *path;
        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
@@ -1098,8 +1318,6 @@ int input_register_device(struct input_dev *dev)
        if (!dev->setkeycode)
                dev->setkeycode = input_default_setkeycode;
 
-       list_add_tail(&dev->node, &input_dev_list);
-
        snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
                 "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
 
@@ -1115,49 +1333,79 @@ int input_register_device(struct input_dev *dev)
                dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
        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)
                input_attach_handler(dev, handler);
 
        input_wakeup_procfs_readers();
 
+       mutex_unlock(&input_mutex);
+
        return 0;
 }
 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)
 {
        struct input_handle *handle, *next;
-       int code;
 
-       for (code = 0; code <= KEY_MAX; code++)
-               if (test_bit(code, dev->key))
-                       input_report_key(dev, code, 0);
-       input_sync(dev);
+       input_disconnect_device(dev);
 
-       del_timer_sync(&dev->timer);
+       mutex_lock(&input_mutex);
 
        list_for_each_entry_safe(handle, next, &dev->h_list, d_node)
                handle->handler->disconnect(handle);
        WARN_ON(!list_empty(&dev->h_list));
 
+       del_timer_sync(&dev->timer);
        list_del_init(&dev->node);
 
-       device_unregister(&dev->dev);
-
        input_wakeup_procfs_readers();
+
+       mutex_unlock(&input_mutex);
+
+       device_unregister(&dev->dev);
 }
 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)
 {
        struct input_dev *dev;
+       int retval;
+
+       retval = mutex_lock_interruptible(&input_mutex);
+       if (retval)
+               return retval;
 
        INIT_LIST_HEAD(&handler->h_list);
 
        if (handler->fops != NULL) {
-               if (input_table[handler->minor >> 5])
-                       return -EBUSY;
-
+               if (input_table[handler->minor >> 5]) {
+                       retval = -EBUSY;
+                       goto out;
+               }
                input_table[handler->minor >> 5] = handler;
        }
 
@@ -1167,14 +1415,26 @@ int input_register_handler(struct input_handler *handler)
                input_attach_handler(dev, handler);
 
        input_wakeup_procfs_readers();
-       return 0;
+
+ out:
+       mutex_unlock(&input_mutex);
+       return retval;
 }
 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)
 {
        struct input_handle *handle, *next;
 
+       mutex_lock(&input_mutex);
+
        list_for_each_entry_safe(handle, next, &handler->h_list, h_node)
                handler->disconnect(handle);
        WARN_ON(!list_empty(&handler->h_list));
@@ -1185,14 +1445,45 @@ void input_unregister_handler(struct input_handler *handler)
                input_table[handler->minor >> 5] = NULL;
 
        input_wakeup_procfs_readers();
+
+       mutex_unlock(&input_mutex);
 }
 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)
 {
        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);
 
        if (handler->start)
@@ -1202,10 +1493,29 @@ int input_register_handle(struct input_handle *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)
 {
+       struct input_dev *dev = handle->dev;
+
        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);
 
index a9a0180bfd462f1c31895641a75448e596630bcc..2b201f9aa02405fce891b9aee53c757307609a28 100644 (file)
@@ -43,6 +43,8 @@ struct joydev {
        struct input_handle handle;
        wait_queue_head_t wait;
        struct list_head client_list;
+       spinlock_t client_lock; /* protects client_list */
+       struct mutex mutex;
        struct device dev;
 
        struct js_corr corr[ABS_MAX + 1];
@@ -61,31 +63,61 @@ struct joydev_client {
        int head;
        int tail;
        int startup;
+       spinlock_t buffer_lock; /* protects access to buffer, head and tail */
        struct fasync_struct *fasync;
        struct joydev *joydev;
        struct list_head node;
 };
 
 static struct joydev *joydev_table[JOYDEV_MINORS];
+static DEFINE_MUTEX(joydev_table_mutex);
 
 static int joydev_correct(int value, struct js_corr *corr)
 {
        switch (corr->type) {
-               case JS_CORR_NONE:
-                       break;
-               case JS_CORR_BROKEN:
-                       value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 :
-                               ((corr->coef[3] * (value - corr->coef[1])) >> 14)) :
-                               ((corr->coef[2] * (value - corr->coef[0])) >> 14);
-                       break;
-               default:
-                       return 0;
+
+       case JS_CORR_NONE:
+               break;
+
+       case JS_CORR_BROKEN:
+               value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 :
+                       ((corr->coef[3] * (value - corr->coef[1])) >> 14)) :
+                       ((corr->coef[2] * (value - corr->coef[0])) >> 14);
+               break;
+
+       default:
+               return 0;
        }
 
        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_client *client;
@@ -93,39 +125,34 @@ static void joydev_event(struct input_handle *handle, unsigned int type, unsigne
 
        switch (type) {
 
-               case EV_KEY:
-                       if (code < BTN_MISC || value == 2)
-                               return;
-                       event.type = JS_EVENT_BUTTON;
-                       event.number = joydev->keymap[code - BTN_MISC];
-                       event.value = value;
-                       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;
+       case EV_KEY:
+               if (code < BTN_MISC || value == 2)
+                       return;
+               event.type = JS_EVENT_BUTTON;
+               event.number = joydev->keymap[code - BTN_MISC];
+               event.value = 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;
+               joydev->abs[event.number] = event.value;
+               break;
+
+       default:
+               return;
        }
 
        event.time = jiffies_to_msecs(jiffies);
 
-       list_for_each_entry(client, &joydev->client_list, node) {
-
-               memcpy(client->buffer + client->head, &event, sizeof(struct js_event));
-
-               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);
-       }
+       rcu_read_lock();
+       list_for_each_entry_rcu(client, &joydev->client_list, node)
+               joydev_pass_event(client, &event);
+       rcu_read_unlock();
 
        wake_up_interruptible(&joydev->wait);
 }
@@ -144,23 +171,83 @@ static void joydev_free(struct device *dev)
 {
        struct joydev *joydev = container_of(dev, struct joydev, dev);
 
-       joydev_table[joydev->minor] = NULL;
        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)
 {
        struct joydev_client *client = file->private_data;
        struct joydev *joydev = client->joydev;
 
        joydev_fasync(-1, file, 0);
-
-       list_del(&client->node);
+       joydev_detach_client(joydev, client);
        kfree(client);
 
-       if (!--joydev->open && joydev->exist)
-               input_close_device(&joydev->handle);
-
+       joydev_close_device(joydev);
        put_device(&joydev->dev);
 
        return 0;
@@ -176,11 +263,16 @@ static int joydev_open(struct inode *inode, struct file *file)
        if (i >= JOYDEV_MINORS)
                return -ENODEV;
 
+       error = mutex_lock_interruptible(&joydev_table_mutex);
+       if (error)
+               return error;
        joydev = joydev_table[i];
-       if (!joydev || !joydev->exist)
-               return -ENODEV;
+       if (joydev)
+               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);
        if (!client) {
@@ -188,37 +280,129 @@ static int joydev_open(struct inode *inode, struct file *file)
                goto err_put_joydev;
        }
 
+       spin_lock_init(&client->buffer_lock);
        client->joydev = joydev;
-       list_add_tail(&client->node, &joydev->client_list);
+       joydev_attach_client(joydev, client);
 
-       if (!joydev->open++ && joydev->exist) {
-               error = input_open_device(&joydev->handle);
-               if (error)
-                       goto err_free_client;
-       }
+       error = joydev_open_device(joydev);
+       if (error)
+               goto err_free_client;
 
        file->private_data = client;
        return 0;
 
  err_free_client:
-       list_del(&client->node);
+       joydev_detach_client(joydev, client);
        kfree(client);
  err_put_joydev:
        put_device(&joydev->dev);
        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 *joydev = client->joydev;
        struct input_dev *input = joydev->handle.dev;
-       int retval = 0;
+       struct js_event event;
+       int retval;
 
        if (!joydev->exist)
                return -ENODEV;
@@ -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))
                return -EINVAL;
 
-       if (count == sizeof(struct JS_DATA_TYPE)) {
-
-               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;
+       if (count == sizeof(struct JS_DATA_TYPE))
+               return joydev_0x_read(client, input, buf);
 
-               return sizeof(struct JS_DATA_TYPE);
-       }
-
-       if (client->startup == joydev->nabs + joydev->nkey &&
-           client->head == client->tail && (file->f_flags & O_NONBLOCK))
+       if (!joydev_data_pending(client) && (file->f_flags & O_NONBLOCK))
                return -EAGAIN;
 
        retval = wait_event_interruptible(joydev->wait,
-                                         !joydev->exist ||
-                                         client->startup < joydev->nabs + joydev->nkey ||
-                                         client->head != client->tail);
+                       !joydev->exist || joydev_data_pending(client));
        if (retval)
                return retval;
 
        if (!joydev->exist)
                return -ENODEV;
 
-       while (client->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) {
-
-               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];
-               }
+       while (retval + sizeof(struct js_event) <= count &&
+              joydev_generate_startup_event(client, input, &event)) {
 
                if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))
                        return -EFAULT;
 
-               client->startup++;
                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;
 
-               client->tail = (client->tail + 1) & (JOYDEV_BUFFER_SIZE - 1);
                retval += sizeof(struct js_event);
        }
 
@@ -301,126 +452,144 @@ static unsigned int joydev_poll(struct file *file, poll_table *wait)
        struct joydev *joydev = client->joydev;
 
        poll_wait(file, &joydev->wait, wait);
-       return ((client->head != client->tail || client->startup < joydev->nabs + joydev->nkey) ?
-               (POLLIN | POLLRDNORM) : 0) | (joydev->exist ? 0 : (POLLHUP | POLLERR));
+       return (joydev_data_pending(client) ? (POLLIN | POLLRDNORM) : 0) |
+               (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;
        int i, j;
 
        switch (cmd) {
 
-               case JS_SET_CAL:
-                       return copy_from_user(&joydev->glue.JS_CORR, argp,
+       case JS_SET_CAL:
+               return copy_from_user(&joydev->glue.JS_CORR, argp,
                                sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0;
 
-               case JS_GET_CAL:
-                       return copy_to_user(argp, &joydev->glue.JS_CORR,
+       case JS_GET_CAL:
+               return copy_to_user(argp, &joydev->glue.JS_CORR,
                                sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0;
 
-               case JS_SET_TIMEOUT:
-                       return get_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp);
+       case JS_SET_TIMEOUT:
+               return get_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp);
 
-               case JS_GET_TIMEOUT:
-                       return put_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp);
+       case JS_GET_TIMEOUT:
+               return put_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp);
 
-               case JSIOCGVERSION:
-                       return put_user(JS_VERSION, (__u32 __user *) argp);
+       case JSIOCGVERSION:
+               return put_user(JS_VERSION, (__u32 __user *) argp);
 
-               case JSIOCGAXES:
-                       return put_user(joydev->nabs, (__u8 __user *) argp);
+       case JSIOCGAXES:
+               return put_user(joydev->nabs, (__u8 __user *) argp);
 
-               case JSIOCGBUTTONS:
-                       return put_user(joydev->nkey, (__u8 __user *) argp);
+       case JSIOCGBUTTONS:
+               return put_user(joydev->nkey, (__u8 __user *) argp);
 
-               case JSIOCSCORR:
-                       if (copy_from_user(joydev->corr, argp,
-                                     sizeof(joydev->corr[0]) * joydev->nabs))
-                           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 JSIOCSCORR:
+               if (copy_from_user(joydev->corr, argp,
+                             sizeof(joydev->corr[0]) * joydev->nabs))
+                   return -EFAULT;
 
-               case JSIOCGCORR:
-                       return copy_to_user(argp, joydev->corr,
-                                               sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0;
+               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 JSIOCSAXMAP:
-                       if (copy_from_user(joydev->abspam, argp, sizeof(__u8) * (ABS_MAX + 1)))
-                               return -EFAULT;
-                       for (i = 0; i < joydev->nabs; i++) {
-                               if (joydev->abspam[i] > ABS_MAX)
-                                       return -EINVAL;
-                               joydev->absmap[joydev->abspam[i]] = i;
-                       }
-                       return 0;
-
-               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)))
+       case JSIOCGCORR:
+               return copy_to_user(argp, joydev->corr,
+                       sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0;
+
+       case JSIOCSAXMAP:
+               if (copy_from_user(joydev->abspam, argp,
+                                  sizeof(__u8) * (ABS_MAX + 1)))
+                       return -EFAULT;
+
+               for (i = 0; i < joydev->nabs; i++) {
+                       if (joydev->abspam[i] > ABS_MAX)
+                               return -EINVAL;
+                       joydev->absmap[joydev->abspam[i]] = i;
+               }
+               return 0;
+
+       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;
-                       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 & ~(_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 len;
+               }
        }
        return -EINVAL;
 }
 
 #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 *joydev = client->joydev;
        void __user *argp = (void __user *)arg;
        s32 tmp32;
        struct JS_DATA_SAVE_TYPE_32 ds32;
-       int err;
+       int retval;
 
-       if (!joydev->exist)
-               return -ENODEV;
+       retval = mutex_lock_interruptible(&joydev->mutex);
+       if (retval)
+               return retval;
+
+       if (!joydev->exist) {
+               retval = -ENODEV;
+               goto out;
+       }
+
+       switch (cmd) {
 
-       switch(cmd) {
        case JS_SET_TIMELIMIT:
-               err = get_user(tmp32, (s32 __user *) arg);
-               if (err == 0)
+               retval = get_user(tmp32, (s32 __user *) arg);
+               if (retval == 0)
                        joydev->glue.JS_TIMELIMIT = tmp32;
                break;
+
        case JS_GET_TIMELIMIT:
                tmp32 = joydev->glue.JS_TIMELIMIT;
-               err = put_user(tmp32, (s32 __user *) arg);
+               retval = put_user(tmp32, (s32 __user *) arg);
                break;
 
        case JS_SET_ALL:
-               err = copy_from_user(&ds32, argp,
-                                    sizeof(ds32)) ? -EFAULT : 0;
-               if (err == 0) {
+               retval = copy_from_user(&ds32, argp,
+                                       sizeof(ds32)) ? -EFAULT : 0;
+               if (retval == 0) {
                        joydev->glue.JS_TIMEOUT    = ds32.JS_TIMEOUT;
                        joydev->glue.BUSY          = ds32.BUSY;
                        joydev->glue.JS_EXPIRETIME = ds32.JS_EXPIRETIME;
@@ -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_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;
 
        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 */
 
-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 *joydev = client->joydev;
        void __user *argp = (void __user *)arg;
+       int retval;
 
-       if (!joydev->exist)
-               return -ENODEV;
+       retval = mutex_lock_interruptible(&joydev->mutex);
+       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) {
-               case JS_SET_TIMELIMIT:
-                       return get_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg);
-               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);
+       default:
+               retval = joydev_ioctl_common(joydev, cmd, argp);
+               break;
        }
+ out:
+       mutex_unlock(&joydev->mutex);
+       return retval;
 }
 
 static const struct file_operations joydev_fops = {
-       .owner =        THIS_MODULE,
-       .read =         joydev_read,
-       .write =        joydev_write,
-       .poll =         joydev_poll,
-       .open =         joydev_open,
-       .release =      joydev_release,
-       .ioctl =        joydev_ioctl,
+       .owner          = THIS_MODULE,
+       .read           = joydev_read,
+       .poll           = joydev_poll,
+       .open           = joydev_open,
+       .release        = joydev_release,
+       .unlocked_ioctl = joydev_ioctl,
 #ifdef CONFIG_COMPAT
-       .compat_ioctl = joydev_compat_ioctl,
+       .compat_ioctl   = joydev_compat_ioctl,
 #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,
                          const struct input_device_id *id)
 {
@@ -494,7 +727,10 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
        int i, j, t, minor;
        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) {
                printk(KERN_ERR "joydev: no more free joydev devices\n");
                return -ENFILE;
@@ -505,15 +741,19 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
                return -ENOMEM;
 
        INIT_LIST_HEAD(&joydev->client_list);
+       spin_lock_init(&joydev->client_lock);
+       mutex_init(&joydev->mutex);
        init_waitqueue_head(&joydev->wait);
 
+       snprintf(joydev->name, sizeof(joydev->name), "js%d", minor);
+       joydev->exist = 1;
        joydev->minor = minor;
+
        joydev->exist = 1;
        joydev->handle.dev = dev;
        joydev->handle.name = joydev->name;
        joydev->handle.handler = handler;
        joydev->handle.private = joydev;
-       snprintf(joydev->name, sizeof(joydev->name), "js%d", minor);
 
        for (i = 0; i < ABS_MAX + 1; i++)
                if (test_bit(i, dev->absbit)) {
@@ -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].prec = dev->absfuzz[j];
-               joydev->corr[i].coef[0] = (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j];
-               joydev->corr[i].coef[1] = (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j];
-               if (!(t = ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j])))
-                       continue;
-               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);
+               joydev->corr[i].coef[0] =
+                       (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j];
+               joydev->corr[i].coef[1] =
+                       (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j];
+
+               t = (dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j];
+               if (t) {
+                       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),
-                "js%d", minor);
+       strlcpy(joydev->dev.bus_id, joydev->name, sizeof(joydev->dev.bus_id));
+       joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor);
        joydev->dev.class = &input_class;
        joydev->dev.parent = &dev->dev;
-       joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor);
        joydev->dev.release = joydev_free;
        device_initialize(&joydev->dev);
 
-       joydev_table[minor] = joydev;
-
-       error = device_add(&joydev->dev);
+       error = input_register_handle(&joydev->handle);
        if (error)
                goto err_free_joydev;
 
-       error = input_register_handle(&joydev->handle);
+       error = joydev_install_chrdev(joydev);
        if (error)
-               goto err_delete_joydev;
+               goto err_unregister_handle;
+
+       error = device_add(&joydev->dev);
+       if (error)
+               goto err_cleanup_joydev;
 
        return 0;
 
- err_delete_joydev:
-       device_del(&joydev->dev);
+ err_cleanup_joydev:
+       joydev_cleanup(joydev);
+ err_unregister_handle:
+       input_unregister_handle(&joydev->handle);
  err_free_joydev:
        put_device(&joydev->dev);
        return error;
 }
 
-
 static void joydev_disconnect(struct input_handle *handle)
 {
        struct joydev *joydev = handle->private;
-       struct joydev_client *client;
 
-       input_unregister_handle(handle);
        device_del(&joydev->dev);
-
-       joydev->exist = 0;
-
-       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);
-       }
-
+       joydev_cleanup(joydev);
+       input_unregister_handle(handle);
        put_device(&joydev->dev);
 }
 
 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) },
                .keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
        },      /* Avoid itouchpads, touchscreens and tablets */
@@ -614,17 +852,20 @@ static const struct input_device_id joydev_blacklist[] = {
 
 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) },
                .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) },
                .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) },
                .absbit = { BIT(ABS_THROTTLE) },
        },
@@ -634,14 +875,14 @@ static const struct input_device_id joydev_ids[] = {
 MODULE_DEVICE_TABLE(input, joydev_ids);
 
 static struct input_handler joydev_handler = {
-       .event =        joydev_event,
-       .connect =      joydev_connect,
-       .disconnect =   joydev_disconnect,
-       .fops =         &joydev_fops,
-       .minor =        JOYDEV_MINOR_BASE,
-       .name =         "joydev",
-       .id_table =     joydev_ids,
-       .blacklist =    joydev_blacklist,
+       .event          = joydev_event,
+       .connect        = joydev_connect,
+       .disconnect     = joydev_disconnect,
+       .fops           = &joydev_fops,
+       .minor          = JOYDEV_MINOR_BASE,
+       .name           = "joydev",
+       .id_table       = joydev_ids,
+       .blacklist      = joydev_blacklist,
 };
 
 static int __init joydev_init(void)
index 28080395899c21aacbc08accda1631c98637a286..623629a69b03e9be1b2a000a98cff8ccede174ef 100644 (file)
@@ -223,12 +223,16 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
        struct input_dev *dev = xpad->dev;
 
        /* left stick */
-       input_report_abs(dev, ABS_X, (__s16) (((__s16)data[13] << 8) | data[12]));
-       input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[15] << 8) | data[14]));
+       input_report_abs(dev, ABS_X,
+                        (__s16) le16_to_cpup((__le16 *)(data + 12)));
+       input_report_abs(dev, ABS_Y,
+                        (__s16) le16_to_cpup((__le16 *)(data + 14)));
 
        /* right stick */
-       input_report_abs(dev, ABS_RX, (__s16) (((__s16)data[17] << 8) | data[16]));
-       input_report_abs(dev, ABS_RY, (__s16) (((__s16)data[19] << 8) | data[18]));
+       input_report_abs(dev, ABS_RX,
+                        (__s16) le16_to_cpup((__le16 *)(data + 16)));
+       input_report_abs(dev, ABS_RY,
+                        (__s16) le16_to_cpup((__le16 *)(data + 18)));
 
        /* triggers left/right */
        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
 
        /* digital pad */
        if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) {
-               input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04));
-               input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01));
+               input_report_abs(dev, ABS_HAT0X,
+                                !!(data[2] & 0x08) - !!(data[2] & 0x04));
+               input_report_abs(dev, ABS_HAT0Y,
+                                !!(data[2] & 0x02) - !!(data[2] & 0x01));
        } else /* xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS */ {
                input_report_key(dev, BTN_LEFT,  data[2] & 0x04);
                input_report_key(dev, BTN_RIGHT, data[2] & 0x08);
@@ -274,14 +280,17 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
  *             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;
 
        /* digital pad */
        if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) {
-               input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04));
-               input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01));
+               input_report_abs(dev, ABS_HAT0X,
+                                !!(data[2] & 0x08) - !!(data[2] & 0x04));
+               input_report_abs(dev, ABS_HAT0Y,
+                                !!(data[2] & 0x02) - !!(data[2] & 0x01));
        } else if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS) {
                /* dpad as buttons (right, left, down, up) */
                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
        input_report_key(dev, BTN_MODE, data[3] & 0x04);
 
        /* left stick */
-       input_report_abs(dev, ABS_X, (__s16) (((__s16)data[7] << 8) | (__s16)data[6]));
-       input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[9] << 8) | (__s16)data[8]));
+       input_report_abs(dev, ABS_X,
+                        (__s16) le16_to_cpup((__le16 *)(data + 6)));
+       input_report_abs(dev, ABS_Y,
+                        (__s16) le16_to_cpup((__le16 *)(data + 8)));
 
        /* right stick */
-       input_report_abs(dev, ABS_RX, (__s16) (((__s16)data[11] << 8) | (__s16)data[10]));
-       input_report_abs(dev, ABS_RY, (__s16) (((__s16)data[13] << 8) | (__s16)data[12]));
+       input_report_abs(dev, ABS_RX,
+                        (__s16) le16_to_cpup((__le16 *)(data + 10)));
+       input_report_abs(dev, ABS_RY,
+                        (__s16) le16_to_cpup((__le16 *)(data + 12)));
 
        /* triggers left/right */
        input_report_abs(dev, ABS_Z, data[4]);
@@ -335,10 +348,12 @@ static void xpad_irq_in(struct urb *urb)
        case -ENOENT:
        case -ESHUTDOWN:
                /* 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;
        default:
-               dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+               dbg("%s - nonzero urb status received: %d",
+                       __FUNCTION__, urb->status);
                goto exit;
        }
 
@@ -367,10 +382,12 @@ static void xpad_irq_out(struct urb *urb)
                case -ENOENT:
                case -ESHUTDOWN:
                        /* 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;
                default:
-                       dbg("%s - nonzero urb status received: %d",  __FUNCTION__, urb->status);
+                       dbg("%s - nonzero urb status received: %d",
+                               __FUNCTION__, urb->status);
                        goto exit;
        }
 
@@ -378,7 +395,7 @@ exit:
        retval = usb_submit_urb(urb, GFP_ATOMIC);
        if (retval)
                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)
@@ -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)
 {
-       struct usb_device *udev = interface_to_usbdev (intf);
+       struct usb_device *udev = interface_to_usbdev(intf);
        struct usb_xpad *xpad;
        struct input_dev *input_dev;
        struct usb_endpoint_descriptor *ep_irq_in;
index c97d5eb0075df2954c3eb741a6d91f9e78e7e182..2316a018fae64212623ec6b7d24003aea6966e8c 100644 (file)
@@ -208,6 +208,27 @@ config KEYBOARD_HIL
          This driver implements support for HIL-keyboards attached
          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
        tristate "TI OMAP keypad support"
        depends on (ARCH_OMAP1 || ARCH_OMAP2)
@@ -253,4 +274,23 @@ config KEYBOARD_GPIO
          To compile this driver as a module, choose M here: the
          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
index 28d211b87b1486ea95ce661910c77e197f7d4a77..e97455fdcc83af75a458abcfeb74ee22856e8f9d 100644 (file)
@@ -21,4 +21,7 @@ obj-$(CONFIG_KEYBOARD_OMAP)           += omap-keypad.o
 obj-$(CONFIG_KEYBOARD_PXA27x)          += pxa27x_keyboard.o
 obj-$(CONFIG_KEYBOARD_AAED2000)                += aaed2000_kbd.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
diff --git a/drivers/input/keyboard/bf54x-keys.c b/drivers/input/keyboard/bf54x-keys.c
new file mode 100644 (file)
index 0000000..a67b29b
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * 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");
index f0b22b8b2769812a7d46e96b95be5129a0e8a3e7..e2a3293bc67ed37b66949747de99d0e3d4d1dfd9 100644 (file)
@@ -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 input_dev *input;
        int i, error;
+       int wakeup = 0;
 
        input = input_allocate_device();
        if (!input)
@@ -77,31 +78,51 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
                int irq = gpio_to_irq(button->gpio);
                unsigned int type = button->type ?: EV_KEY;
 
-               set_irq_type(irq, IRQ_TYPE_EDGE_BOTH);
-               error = request_irq(irq, gpio_keys_isr, IRQF_SAMPLE_RANDOM,
-                                    button->desc ? button->desc : "gpio_keys",
-                                    pdev);
+               if (irq < 0) {
+                       error = irq;
+                       printk(KERN_ERR
+                               "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) {
-                       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);
                        goto fail;
                }
 
+               if (button->wakeup)
+                       wakeup = 1;
+
                input_set_capability(input, type, button->code);
        }
 
        error = input_register_device(input);
        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;
        }
 
+       device_init_wakeup(&pdev->dev, wakeup);
+
        return 0;
 
  fail:
-       for (i = i - 1; i >= 0; i--)
+       while (--i >= 0)
                free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev);
 
+       platform_set_drvdata(pdev, NULL);
        input_free_device(input);
 
        return error;
@@ -113,6 +134,8 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
        struct input_dev *input = platform_get_drvdata(pdev);
        int i;
 
+       device_init_wakeup(&pdev->dev, 0);
+
        for (i = 0; i < pdata->nbuttons; i++) {
                int irq = gpio_to_irq(pdata->buttons[i].gpio);
                free_irq(irq, pdev);
@@ -123,9 +146,53 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
        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 = {
        .probe          = gpio_keys_probe,
        .remove         = __devexit_p(gpio_keys_remove),
+       .suspend        = gpio_keys_suspend,
+       .resume         = gpio_keys_resume,
        .driver         = {
                .name   = "gpio-keys",
        }
diff --git a/drivers/input/keyboard/jornada680_kbd.c b/drivers/input/keyboard/jornada680_kbd.c
new file mode 100644 (file)
index 0000000..bec1cf4
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * 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");
diff --git a/drivers/input/keyboard/jornada720_kbd.c b/drivers/input/keyboard/jornada720_kbd.c
new file mode 100644 (file)
index 0000000..e6696b3
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * 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);
diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c
new file mode 100644 (file)
index 0000000..2b40428
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * 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);
index 3a228634f1015239aae5f669c035b401613f90b7..76f1969552c5e453c93a3db263e1d7d834c66061 100644 (file)
@@ -233,7 +233,7 @@ static void omap_kp_tasklet(unsigned long data)
                        omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
                        kp_cur_group = -1;
                }
-       }
+       }
 }
 
 static ssize_t omap_kp_enable_show(struct device *dev,
@@ -318,7 +318,7 @@ static int __init omap_kp_probe(struct platform_device *pdev)
        keymap = pdata->keymap;
 
        if (pdata->rep)
-               set_bit(EV_REP, input_dev->evbit);
+               __set_bit(EV_REP, input_dev->evbit);
 
        if (pdata->delay)
                omap_kp->delay = pdata->delay;
@@ -365,9 +365,9 @@ static int __init omap_kp_probe(struct platform_device *pdev)
                goto err2;
 
        /* setup input device */
-       set_bit(EV_KEY, input_dev->evbit);
+       __set_bit(EV_KEY, input_dev->evbit);
        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->phys = "omap-keypad/input0";
        input_dev->dev.parent = &pdev->dev;
@@ -377,10 +377,6 @@ static int __init omap_kp_probe(struct platform_device *pdev)
        input_dev->id.product = 0x0001;
        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);
        if (ret < 0) {
                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)
        } else {
                for (irq_idx = 0; irq_idx < omap_kp->rows; irq_idx++) {
                        if (request_irq(OMAP_GPIO_IRQ(row_gpios[irq_idx]),
-                                       omap_kp_interrupt,
+                                       omap_kp_interrupt,
                                        IRQF_TRIGGER_FALLING,
-                                       "omap-keypad", omap_kp) < 0)
+                                       "omap-keypad", omap_kp) < 0)
                                goto err5;
                }
        }
        return 0;
 err5:
-       for (i = irq_idx-1; i >=0; i--)
+       for (i = irq_idx - 1; i >=0; i--)
                free_irq(row_gpios[i], 0);
 err4:
        input_unregister_device(omap_kp->input);
@@ -440,9 +436,9 @@ static int omap_kp_remove(struct platform_device *pdev)
        if (cpu_is_omap24xx()) {
                int 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++) {
-                       omap_free_gpio(row_gpios[i]);
+                       omap_free_gpio(row_gpios[i]);
                        free_irq(OMAP_GPIO_IRQ(row_gpios[i]), 0);
                }
        } else {
index 2c5f11a4f6b410f1c2fbff3f50b42ca0bcfc7b1e..64d70a9b714c9d84572f5c39d5f98b7a60f9d5b9 100644 (file)
@@ -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, 0x64 }, 0xf8, 0xf8, 0 },
        { { 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, 0x14 }, 0xf8, 0xf8, ALPS_FW_BK_2 },             /* Ahtec Laptop */
        { { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */
        { { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },
        { { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */
+       { { 0x73, 0x02, 0x50 }, 0xcf, 0xff, ALPS_FW_BK_1 } /* Dell Vostro 1400 */
 };
 
 /*
index a1804bfdbb8ce38510f783c233682e12e5dabac8..0117817bf538c5ce36511965b08134b550aa5c3a 100644 (file)
@@ -502,18 +502,23 @@ static void atp_complete(struct urb* urb)
 
                /* reset the accumulator on release */
                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
-                  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 */
-               if (!key && atp_is_geyser_3(dev)) {
+       if (atp_is_geyser_3(dev)) {
+               if (!x && !y && !key) {
                        dev->idlecount++;
                        if (dev->idlecount == 10) {
                                dev->valid = 0;
                                schedule_work(&dev->work);
                        }
                }
+               else
+                       dev->idlecount = 0;
        }
 
        input_report_key(dev->input, BTN_LEFT, key);
index 608674d0be8b25e95e5e9b165f0516bed34ff319..d7de4c53b3d8cb8cd853c14ae382f5f47f2fda83 100644 (file)
@@ -96,6 +96,14 @@ static const struct dmi_system_id lifebook_dmi_table[] = {
                },
                .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",
                .matches = {
@@ -282,7 +290,7 @@ static int lifebook_create_relative_device(struct psmouse *psmouse)
 int lifebook_init(struct psmouse *psmouse)
 {
        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))
                return -1;
index b9f0fb2530e2357cb1f39ff1e52d48cf764ccc1b..0735257565329aa590096f1f9bc8fd0ab49e665b 100644 (file)
@@ -648,9 +648,10 @@ static int psmouse_extensions(struct psmouse *psmouse,
 
 /*
  * Reset to defaults in case the device got confused by extended
- * protocol probes. Note that we do full reset becuase some mice
- * put themselves to sleep when see PSMOUSE_RESET_DIS.
+ * protocol probes. Note that we follow up with full reset because
+ * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS.
  */
+       ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
        psmouse_reset(psmouse);
 
        if (max_proto >= PSMOUSE_IMEX && im_explorer_detect(psmouse, set_properties) == 0)
index 9173916b8be5f34b84a02d1697ad8bdcc0483676..79146d6ed2ab2d392988d196d64d4f9d6c0e92bf 100644 (file)
@@ -61,9 +61,11 @@ struct mousedev {
        int open;
        int minor;
        char name[16];
+       struct input_handle handle;
        wait_queue_head_t wait;
        struct list_head client_list;
-       struct input_handle handle;
+       spinlock_t client_lock; /* protects client_list */
+       struct mutex mutex;
        struct device dev;
 
        struct list_head mixdev_node;
@@ -113,108 +115,137 @@ static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 };
 static struct input_handler mousedev_handler;
 
 static struct mousedev *mousedev_table[MOUSEDEV_MINORS];
+static DEFINE_MUTEX(mousedev_table_mutex);
 static struct mousedev *mousedev_mix;
 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 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;
        enum { FRACTION_DENOM = 128 };
 
        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:
-                       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;
+       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:
+               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;
 
        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:
-                       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;
+       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:
+               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) {
-               case REL_X:     mousedev->packet.dx += value; break;
-               case REL_Y:     mousedev->packet.dy -= value; break;
-               case REL_WHEEL: mousedev->packet.dz -= value; break;
+       case REL_X:
+               mousedev->packet.dx += value;
+               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;
 
        switch (code) {
-               case BTN_TOUCH:
-               case BTN_0:
-               case BTN_LEFT:          index = 0; break;
-               case BTN_STYLUS:
-               case BTN_1:
-               case BTN_RIGHT:         index = 1; break;
-               case BTN_2:
-               case BTN_FORWARD:
-               case BTN_STYLUS2:
-               case BTN_MIDDLE:        index = 2; break;
-               case BTN_3:
-               case BTN_BACK:
-               case BTN_SIDE:          index = 3; break;
-               case BTN_4:
-               case BTN_EXTRA:         index = 4; break;
-               default:                return;
+
+       case BTN_TOUCH:
+       case BTN_0:
+       case BTN_LEFT:          index = 0; break;
+
+       case BTN_STYLUS:
+       case BTN_1:
+       case BTN_RIGHT:         index = 1; break;
+
+       case BTN_2:
+       case BTN_FORWARD:
+       case BTN_STYLUS2:
+       case BTN_MIDDLE:        index = 2; break;
+
+       case BTN_3:
+       case BTN_BACK:
+       case BTN_SIDE:          index = 3; break;
+
+       case BTN_4:
+       case BTN_EXTRA:         index = 4; break;
+
+       default:                return;
        }
 
        if (value) {
@@ -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_motion *p;
-       unsigned long flags;
+       unsigned int new_head;
        int wake_readers = 0;
 
-       list_for_each_entry(client, &mousedev->client_list, node) {
-               spin_lock_irqsave(&client->packet_lock, flags);
+       rcu_read_lock();
+       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];
                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) {
                                p = &client->packets[client->head = new_head];
                                memset(p, 0, sizeof(struct mousedev_motion));
@@ -253,25 +288,29 @@ static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_h
                }
 
                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 = 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->dy += packet->dy;
                p->dz += packet->dz;
                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;
 
-               spin_unlock_irqrestore(&client->packet_lock, flags);
+               spin_unlock(&client->packet_lock);
 
                if (client->ready) {
                        kill_fasync(&client->fasync, SIGIO, POLL_IN);
                        wake_readers = 1;
                }
        }
+       rcu_read_unlock();
 
        if (wake_readers)
                wake_up_interruptible(&mousedev->wait);
@@ -281,7 +320,8 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value)
 {
        if (!value) {
                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.
                         * 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)
                        set_bit(0, &mousedev->packet.buttons);
                        set_bit(0, &mousedev_mix->packet.buttons);
                        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_mix->packet.buttons);
                }
@@ -302,54 +343,61 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value)
                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;
 
        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))
-                               mousedev_touchpad_event(handle->dev, mousedev, code, value);
-                       else
-                               mousedev_abs_event(handle->dev, mousedev, code, value);
+       case EV_ABS:
+               /* Ignore joysticks */
+               if (test_bit(BTN_TRIGGER, handle->dev->keybit))
+                       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:
-                       mousedev_rel_event(mousedev, code, value);
-                       break;
+               break;
 
-               case EV_KEY:
-                       if (value != 2) {
-                               if (code == BTN_TOUCH && test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
-                                       mousedev_touchpad_touch(mousedev, value);
-                               else
-                                       mousedev_key_event(mousedev, code, value);
-                       }
-                       break;
+       case EV_REL:
+               mousedev_rel_event(mousedev, code, value);
+               break;
 
-               case EV_SYN:
-                       if (code == SYN_REPORT) {
-                               if (mousedev->touch) {
-                                       mousedev->pkt_count++;
-                                       /* 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);
-                               }
-
-                               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;
+       case EV_KEY:
+               if (value != 2) {
+                       if (code == BTN_TOUCH &&
+                           test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
+                               mousedev_touchpad_touch(mousedev, value);
+                       else
+                               mousedev_key_event(mousedev, code, value);
+               }
+               break;
+
+       case EV_SYN:
+               if (code == SYN_REPORT) {
+                       if (mousedev->touch) {
+                               mousedev->pkt_count++;
+                               /*
+                                * 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)
 {
        struct mousedev *mousedev = container_of(dev, struct mousedev, dev);
 
-       mousedev_table[mousedev->minor] = NULL;
        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) {
-               error = input_open_device(&mousedev->handle);
-               if (error)
-                       return error;
+       retval = mutex_lock_interruptible(&mousedev->mutex);
+       if (retval)
+               return retval;
 
-               mousedev->open++;
-               mousedev->mixdev_open = 1;
+       if (mousedev->minor == MOUSEDEV_MIX)
+               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);
-       list_add_tail(&mousedev->mixdev_node, &mousedev_mix_list);
-
-       return 0;
+       mutex_unlock(&mousedev->mutex);
+       return retval;
 }
 
-static void mixdev_remove_device(struct mousedev *mousedev)
+static void mousedev_close_device(struct mousedev *mousedev)
 {
-       if (mousedev->mixdev_open) {
-               mousedev->mixdev_open = 0;
-               if (!--mousedev->open && mousedev->exist)
-                       input_close_device(&mousedev->handle);
-       }
+       mutex_lock(&mousedev->mutex);
 
-       list_del_init(&mousedev->mixdev_node);
-       put_device(&mousedev->dev);
+       if (mousedev->minor == MOUSEDEV_MIX)
+               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)
 {
        struct mousedev *mousedev;
@@ -411,16 +466,19 @@ static void mixdev_open_devices(void)
 
        list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
                if (!mousedev->mixdev_open) {
-                       if (!mousedev->open && mousedev->exist)
-                               if (input_open_device(&mousedev->handle))
-                                       continue;
+                       if (mousedev_open_device(mousedev))
+                               continue;
 
-                       mousedev->open++;
                        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)
 {
        struct mousedev *mousedev;
@@ -431,33 +489,45 @@ static void mixdev_close_devices(void)
        list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
                if (mousedev->mixdev_open) {
                        mousedev->mixdev_open = 0;
-                       if (!--mousedev->open && mousedev->exist)
-                               input_close_device(&mousedev->handle);
+                       mousedev_close_device(mousedev);
                }
        }
 }
 
+
+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)
 {
        struct mousedev_client *client = file->private_data;
        struct mousedev *mousedev = client->mousedev;
 
        mousedev_fasync(-1, file, 0);
-
-       list_del(&client->node);
+       mousedev_detach_client(mousedev, client);
        kfree(client);
 
-       if (mousedev->minor == MOUSEDEV_MIX)
-               mixdev_close_devices();
-       else if (!--mousedev->open && mousedev->exist)
-               input_close_device(&mousedev->handle);
-
+       mousedev_close_device(mousedev);
        put_device(&mousedev->dev);
 
        return 0;
 }
 
-
 static int mousedev_open(struct inode *inode, struct file *file)
 {
        struct mousedev_client *client;
@@ -475,12 +545,17 @@ static int mousedev_open(struct inode *inode, struct file *file)
        if (i >= MOUSEDEV_MINORS)
                return -ENODEV;
 
+       error = mutex_lock_interruptible(&mousedev_table_mutex);
+       if (error)
+               return error;
        mousedev = mousedev_table[i];
+       if (mousedev)
+               get_device(&mousedev->dev);
+       mutex_unlock(&mousedev_table_mutex);
+
        if (!mousedev)
                return -ENODEV;
 
-       get_device(&mousedev->dev);
-
        client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL);
        if (!client) {
                error = -ENOMEM;
@@ -491,21 +566,17 @@ static int mousedev_open(struct inode *inode, struct file *file)
        client->pos_x = xres / 2;
        client->pos_y = yres / 2;
        client->mousedev = mousedev;
-       list_add_tail(&client->node, &mousedev->client_list);
+       mousedev_attach_client(mousedev, client);
 
-       if (mousedev->minor == MOUSEDEV_MIX)
-               mixdev_open_devices();
-       else if (!mousedev->open++ && mousedev->exist) {
-               error = input_open_device(&mousedev->handle);
-               if (error)
-                       goto err_free_client;
-       }
+       error = mousedev_open_device(mousedev);
+       if (error)
+               goto err_free_client;
 
        file->private_data = client;
        return 0;
 
  err_free_client:
-       list_del(&client->node);
+       mousedev_detach_client(mousedev, client);
        kfree(client);
  err_put_mousedev:
        put_device(&mousedev->dev);
@@ -517,41 +588,41 @@ static inline int mousedev_limit_delta(int delta, int limit)
        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;
-       unsigned long flags;
-
-       spin_lock_irqsave(&client->packet_lock, flags);
-       p = &client->packets[client->tail];
+       struct mousedev_motion *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[2] = mousedev_limit_delta(p->dy, 127);
        p->dx -= ps2_data[1];
        p->dy -= ps2_data[2];
 
        switch (client->mode) {
-               case MOUSEDEV_EMUL_EXPS:
-                       ps2_data[3] = mousedev_limit_delta(p->dz, 7);
-                       p->dz -= ps2_data[3];
-                       ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1);
-                       client->bufsiz = 4;
-                       break;
-
-               case MOUSEDEV_EMUL_IMPS:
-                       ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
-                       ps2_data[3] = mousedev_limit_delta(p->dz, 127);
-                       p->dz -= ps2_data[3];
-                       client->bufsiz = 4;
-                       break;
-
-               case MOUSEDEV_EMUL_PS2:
-               default:
-                       ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
-                       p->dz = 0;
-                       client->bufsiz = 3;
-                       break;
+       case MOUSEDEV_EMUL_EXPS:
+               ps2_data[3] = mousedev_limit_delta(p->dz, 7);
+               p->dz -= ps2_data[3];
+               ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1);
+               client->bufsiz = 4;
+               break;
+
+       case MOUSEDEV_EMUL_IMPS:
+               ps2_data[0] |=
+                       ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
+               ps2_data[3] = mousedev_limit_delta(p->dz, 127);
+               p->dz -= ps2_data[3];
+               client->bufsiz = 4;
+               break;
+
+       case MOUSEDEV_EMUL_PS2:
+       default:
+               ps2_data[0] |=
+                       ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
+               p->dz = 0;
+               client->bufsiz = 3;
+               break;
        }
 
        if (!p->dx && !p->dy && !p->dz) {
@@ -561,12 +632,56 @@ static void mousedev_packet(struct mousedev_client *client, signed char *ps2_dat
                } else
                        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;
        unsigned char c;
@@ -577,6 +692,8 @@ static ssize_t mousedev_write(struct file *file, const char __user *buffer, size
                if (get_user(c, buffer + i))
                        return -EFAULT;
 
+               spin_lock_irq(&client->packet_lock);
+
                if (c == mousedev_imex_seq[client->imexseq]) {
                        if (++client->imexseq == MOUSEDEV_SEQ_LEN) {
                                client->imexseq = 0;
@@ -593,68 +710,39 @@ static ssize_t mousedev_write(struct file *file, const char __user *buffer, size
                } else
                        client->impsseq = 0;
 
-               client->ps2[0] = 0xfa;
-
-               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;
-               }
+               mousedev_generate_response(client, c);
 
-               client->buffer = client->bufsiz;
+               spin_unlock_irq(&client->packet_lock);
        }
 
        kill_fasync(&client->fasync, SIGIO, POLL_IN);
-
        wake_up_interruptible(&client->mousedev->wait);
 
        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 *mousedev = client->mousedev;
+       signed char data[sizeof(client->ps2)];
        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;
 
-       retval = wait_event_interruptible(client->mousedev->wait,
-                                         !client->mousedev->exist || client->ready || client->buffer);
-
+       retval = wait_event_interruptible(mousedev->wait,
+                       !mousedev->exist || client->ready || client->buffer);
        if (retval)
                return retval;
 
-       if (!client->mousedev->exist)
+       if (!mousedev->exist)
                return -ENODEV;
 
+       spin_lock_irq(&client->packet_lock);
+
        if (!client->buffer && client->ready) {
                mousedev_packet(client, client->ps2);
                client->buffer = client->bufsiz;
@@ -663,9 +751,12 @@ static ssize_t mousedev_read(struct file *file, char __user *buffer, size_t coun
        if (count > client->buffer)
                count = client->buffer;
 
+       memcpy(data, client->ps2 + client->bufsiz - 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 count;
@@ -692,6 +783,60 @@ static const struct file_operations mousedev_fops = {
        .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,
                                        struct input_handler *handler,
                                        int minor)
@@ -707,6 +852,10 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
 
        INIT_LIST_HEAD(&mousedev->client_list);
        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);
 
        if (minor == MOUSEDEV_MIX)
@@ -731,14 +880,27 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
        mousedev->dev.release = mousedev_free;
        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);
        if (error)
-               goto err_free_mousedev;
+               goto err_cleanup_mousedev;
 
        return mousedev;
 
+ err_cleanup_mousedev:
+       mousedev_cleanup(mousedev);
+ err_unregister_handle:
+       if (minor != MOUSEDEV_MIX)
+               input_unregister_handle(&mousedev->handle);
  err_free_mousedev:
        put_device(&mousedev->dev);
  err_out:
@@ -747,29 +909,64 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
 
 static void mousedev_destroy(struct mousedev *mousedev)
 {
-       struct mousedev_client *client;
-
        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) {
-               input_close_device(&mousedev->handle);
-               list_for_each_entry(client, &mousedev->client_list, node)
-                       kill_fasync(&client->fasync, SIGIO, POLL_HUP);
-               wake_up_interruptible(&mousedev->wait);
+static int mixdev_add_device(struct mousedev *mousedev)
+{
+       int retval;
+
+       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);
 }
 
-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)
 {
        struct mousedev *mousedev;
        int minor;
        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) {
                printk(KERN_ERR "mousedev: no more free mousedev devices\n");
                return -ENFILE;
@@ -779,21 +976,13 @@ static int mousedev_connect(struct input_handler *handler, struct input_dev *dev
        if (IS_ERR(mousedev))
                return PTR_ERR(mousedev);
 
-       error = input_register_handle(&mousedev->handle);
-       if (error)
-               goto err_delete_mousedev;
-
        error = mixdev_add_device(mousedev);
-       if (error)
-               goto err_unregister_handle;
+       if (error) {
+               mousedev_destroy(mousedev);
+               return error;
+       }
 
        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)
@@ -801,33 +990,42 @@ static void mousedev_disconnect(struct input_handle *handle)
        struct mousedev *mousedev = handle->private;
 
        mixdev_remove_device(mousedev);
-       input_unregister_handle(handle);
        mousedev_destroy(mousedev);
 }
 
 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) },
                .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 */
+       },      /* 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) },
                .relbit = { BIT(REL_WHEEL) },
        },      /* 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) },
                .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 */
+       },      /* 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) },
                .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 */
 
        { },    /* Terminating entry */
index c2eea2767e1096ed9d1e5b16e6e09593df8d06f0..11dafc0ee9942145b937a85bbebe27578ed073c6 100644 (file)
@@ -385,6 +385,8 @@ static int i8042_enable_kbd_port(void)
        i8042_ctr |= I8042_CTR_KBDINT;
 
        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");
                return -EIO;
        }
@@ -402,6 +404,8 @@ static int i8042_enable_aux_port(void)
        i8042_ctr |= I8042_CTR_AUXINT;
 
        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");
                return -EIO;
        }
index f929fcdbae2e75cb640dded83d2e33c33b1d8d24..e3e0baa1a158b4e907a5cb5e2f3aedc4654722e3 100644 (file)
@@ -126,6 +126,16 @@ config TOUCHSCREEN_HP600
          To compile this driver as a module, choose M here: the
          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
        tristate "Penmount serial touchscreen"
        select SERIO
@@ -191,6 +201,7 @@ config TOUCHSCREEN_USB_COMPOSITE
          - Gunze AHL61
          - DMC TSC-10/25
          - IRTOUCHSYSTEMS/UNITOP
+         - IdealTEK URTC1000
 
          Have a look at <http://linux.chapter7.ch/touchkit/> for
          a usage description and the required user-space stuff.
@@ -238,4 +249,14 @@ config TOUCHSCREEN_USB_IRTOUCH
        bool "IRTOUCHSYSTEMS/UNITOP device support" if EMBEDDED
        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
index 5de8933c49936ecbda73bb2da6e05c5dcb62cd41..35d4097df35a64ddbe988376340f51c27199acf8 100644 (file)
@@ -13,6 +13,7 @@ obj-$(CONFIG_TOUCHSCREEN_FUJITSU)     += fujitsu_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MTOUCH)       += mtouch.o
 obj-$(CONFIG_TOUCHSCREEN_MK712)                += mk712.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_PENMOUNT)     += penmount.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)   += touchright.o
diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c
new file mode 100644 (file)
index 0000000..42a1c9a
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * 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);
index 36f944019158af753cda9f4105fa9a85176c4c50..86aed64ec0fb9dd54c5ed3ec1b134f62a2615d6e 100644 (file)
@@ -130,8 +130,7 @@ static unsigned int ucb1400_adc_read(struct ucb1400 *ucb, u16 adc_channel)
                if (val & UCB_ADC_DAT_VALID)
                        break;
                /* yield to other processes */
-               set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(1);
+               schedule_timeout_uninterruptible(1);
        }
 
        return UCB_ADC_DAT_VALUE(val);
index 741f6c6f1e506f621e55ec8a580db0154db416cc..9fb3d5c309998162c5f14d40c6587e95c95395a5 100644 (file)
@@ -10,6 +10,7 @@
  *  - Gunze AHL61
  *  - DMC TSC-10/25
  *  - IRTOUCHSYSTEMS/UNITOP
+ *  - IdealTEK URTC1000
  *
  * Copyright (C) 2004-2006 by Daniel Ritz <daniel.ritz@gmx.ch>
  * Copyright (C) by Todd E. Johnson (mtouchusb.c)
@@ -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
 #endif
 
@@ -112,6 +113,8 @@ enum {
        DEVTYPE_GUNZE,
        DEVTYPE_DMC_TSC10,
        DEVTYPE_IRTOUCH,
+       DEVTYPE_IDEALTEK,
+       DEVTYPE_GENERAL_TOUCH,
 };
 
 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},
 #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)
                              TSC10_RATE_150, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
        if (ret < 0)
                return ret;
-       if (buf[0] != 0x06 || buf[1] != 0x00)
+       if ((buf[0] != 0x06 || buf[1] != 0x00) &&
+           (buf[0] != 0x15 || buf[1] != 0x01))
                return -ENODEV;
 
        /* start sending data */
@@ -437,6 +449,57 @@ static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
 #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
  */
@@ -537,6 +600,32 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
                .read_data      = irtouch_read_data,
        },
 #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
+
 };
 
 
diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c
deleted file mode 100644 (file)
index d2f882e..0000000
+++ /dev/null
@@ -1,533 +0,0 @@
-/*
- * $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;
-   &nbs