浏览代码

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

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

Resolved conflicts manually in drivers/hwmon/applesmc.c: converting from
a class device to a device and converting to use input-polldev created a
few apparently trivial clashes..
Linus Torvalds 17 年之前
父节点
当前提交
f2e1d89f9b
共有 42 个文件被更改,包括 3753 次插入2154 次删除
  1. 0 14
      Documentation/feature-removal-schedule.txt
  2. 0 3
      Documentation/kernel-parameters.txt
  3. 3 3
      arch/blackfin/mach-bf548/boards/ezkit.c
  4. 0 457
      drivers/char/ec3104_keyb.c
  5. 3 0
      drivers/hwmon/Kconfig
  6. 32 44
      drivers/hwmon/ams/ams-input.c
  7. 2 3
      drivers/hwmon/ams/ams.h
  8. 30 53
      drivers/hwmon/applesmc.c
  9. 24 31
      drivers/hwmon/hdaps.c
  10. 0 22
      drivers/input/Kconfig
  11. 0 1
      drivers/input/Makefile
  12. 470 238
      drivers/input/evdev.c
  13. 5 2
      drivers/input/input-polldev.c
  14. 485 175
      drivers/input/input.c
  15. 492 251
      drivers/input/joydev.c
  16. 36 19
      drivers/input/joystick/xpad.c
  17. 40 0
      drivers/input/keyboard/Kconfig
  18. 4 1
      drivers/input/keyboard/Makefile
  19. 382 0
      drivers/input/keyboard/bf54x-keys.c
  20. 74 7
      drivers/input/keyboard/gpio_keys.c
  21. 277 0
      drivers/input/keyboard/jornada680_kbd.c
  22. 185 0
      drivers/input/keyboard/jornada720_kbd.c
  23. 252 0
      drivers/input/keyboard/maple_keyb.c
  24. 9 13
      drivers/input/keyboard/omap-keypad.c
  25. 2 0
      drivers/input/mouse/alps.c
  26. 10 5
      drivers/input/mouse/appletouch.c
  27. 9 1
      drivers/input/mouse/lifebook.c
  28. 3 2
      drivers/input/mouse/psmouse-base.c
  29. 448 251
      drivers/input/mousedev.c
  30. 4 0
      drivers/input/serio/i8042.c
  31. 21 0
      drivers/input/touchscreen/Kconfig
  32. 1 0
      drivers/input/touchscreen/Makefile
  33. 182 0
      drivers/input/touchscreen/jornada720_ts.c
  34. 1 2
      drivers/input/touchscreen/ucb1400_ts.c
  35. 91 2
      drivers/input/touchscreen/usbtouchscreen.c
  36. 0 533
      drivers/input/tsdev.c
  37. 1 0
      drivers/macintosh/Kconfig
  38. 48 10
      drivers/macintosh/adbhid.c
  39. 17 0
      include/asm-blackfin/mach-bf548/bf54x_keys.h
  40. 1 0
      include/linux/gpio_keys.h
  41. 106 10
      include/linux/input.h
  42. 3 1
      include/linux/keyboard.h

+ 0 - 14
Documentation/feature-removal-schedule.txt

@@ -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

+ 0 - 3
Documentation/kernel-parameters.txt

@@ -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:

+ 3 - 3
arch/blackfin/mach-bf548/boards/ezkit.c

@@ -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) */

+ 0 - 457
drivers/char/ec3104_keyb.c

@@ -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);
-}

+ 3 - 0
drivers/hwmon/Kconfig

@@ -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

+ 32 - 44
drivers/hwmon/ams/ams-input.c

@@ -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;
 	}
 }

+ 2 - 3
drivers/hwmon/ams/ams.h

@@ -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 */

+ 30 - 53
drivers/hwmon/applesmc.c

@@ -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;

+ 24 - 31
drivers/hwmon/hdaps.c

@@ -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>
@@ -61,13 +61,12 @@
 #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);

+ 0 - 22
drivers/input/Kconfig

@@ -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

+ 0 - 1
drivers/input/Makefile

@@ -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/

+ 470 - 238
drivers/input/evdev.c

@@ -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)

+ 5 - 2
drivers/input/input-polldev.c

@@ -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;
 }

+ 485 - 175
drivers/input/input.c

@@ -17,10 +17,10 @@
 #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);
 

+ 492 - 251
drivers/input/joydev.c

@@ -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)

+ 36 - 19
drivers/input/joystick/xpad.c

@@ -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;

+ 40 - 0
drivers/input/keyboard/Kconfig

@@ -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

+ 4 - 1
drivers/input/keyboard/Makefile

@@ -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

+ 382 - 0
drivers/input/keyboard/bf54x-keys.c

@@ -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");

+ 74 - 7
drivers/input/keyboard/gpio_keys.c

@@ -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",
 	}

+ 277 - 0
drivers/input/keyboard/jornada680_kbd.c

@@ -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");

+ 185 - 0
drivers/input/keyboard/jornada720_kbd.c

@@ -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);

+ 252 - 0
drivers/input/keyboard/maple_keyb.c

@@ -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);

+ 9 - 13
drivers/input/keyboard/omap-keypad.c

@@ -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 {

+ 2 - 0
drivers/input/mouse/alps.c

@@ -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 */
 };
 
 /*

+ 10 - 5
drivers/input/mouse/appletouch.c

@@ -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);

+ 9 - 1
drivers/input/mouse/lifebook.c

@@ -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;

+ 3 - 2
drivers/input/mouse/psmouse-base.c

@@ -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)

文件差异内容过多而无法显示
+ 448 - 251
drivers/input/mousedev.c


+ 4 - 0
drivers/input/serio/i8042.c

@@ -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;
 	}

+ 21 - 0
drivers/input/touchscreen/Kconfig

@@ -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

+ 1 - 0
drivers/input/touchscreen/Makefile

@@ -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

+ 182 - 0
drivers/input/touchscreen/jornada720_ts.c

@@ -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);

+ 1 - 2
drivers/input/touchscreen/ucb1400_ts.c

@@ -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);

+ 91 - 2
drivers/input/touchscreen/usbtouchscreen.c

@@ -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
+
 };
 
 

+ 0 - 533
drivers/input/tsdev.c

@@ -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;
-			break;
-
-		case ABS_Y:
-			tsdev->y = value;
-			break;
-
-		case ABS_PRESSURE:
-			if (value > handle->dev->absmax[ABS_PRESSURE])
-				value = handle->dev->absmax[ABS_PRESSURE];
-			value -= handle->dev->absmin[ABS_PRESSURE];
-			if (value < 0)
-				value = 0;
-			tsdev->pressure = value;
-			break;
-		}
-		break;
-
-	case EV_REL:
-		switch (code) {
-		case REL_X:
-			tsdev->x += value;
-			if (tsdev->x < 0)
-				tsdev->x = 0;
-			else if (tsdev->x > xres)
-				tsdev->x = xres;
-			break;
-
-		case REL_Y:
-			tsdev->y += value;
-			if (tsdev->y < 0)
-				tsdev->y = 0;
-			else if (tsdev->y > yres)
-				tsdev->y = yres;
-			break;
-		}
-		break;
-
-	case EV_KEY:
-		if (code == BTN_TOUCH || code == BTN_MOUSE) {
-			switch (value) {
-			case 0:
-				tsdev->pressure = 0;
-				break;
-
-			case 1:
-				if (!tsdev->pressure)
-					tsdev->pressure = 1;
-				break;
-			}
-		}
-		break;
-	}
-
-	if (type != EV_SYN || code != SYN_REPORT)
-		return;
-
-	list_for_each_entry(client, &tsdev->client_list, node) {
-		int x, y, tmp;
-
-		do_gettimeofday(&time);
-		client->event[client->head].millisecs = time.tv_usec / 1000;
-		client->event[client->head].pressure = tsdev->pressure;
-
-		x = tsdev->x;
-		y = tsdev->y;
-
-		/* Calibration */
-		if (!client->raw) {
-			x = ((x * tsdev->cal.xscale) >> 8) + tsdev->cal.xtrans;
-			y = ((y * tsdev->cal.yscale) >> 8) + tsdev->cal.ytrans;
-			if (tsdev->cal.xyswap) {
-				tmp = x; x = y; y = tmp;
-			}
-		}
-
-		client->event[client->head].x = x;
-		client->event[client->head].y = y;
-		client->head = (client->head + 1) & (TSDEV_BUFFER_SIZE - 1);
-		kill_fasync(&client->fasync, SIGIO, POLL_IN);
-	}
-	wake_up_interruptible(&tsdev->wait);
-}
-
-static int tsdev_connect(struct input_handler *handler, struct input_dev *dev,
-			 const struct input_device_id *id)
-{
-	struct tsdev *tsdev;
-	int minor, delta;
-	int error;
-
-	for (minor = 0; minor < TSDEV_MINORS / 2 && tsdev_table[minor]; minor++);
-	if (minor >= TSDEV_MINORS / 2) {
-		printk(KERN_ERR
-		       "tsdev: You have way too many touchscreens\n");
-		return -ENFILE;
-	}
-
-	tsdev = kzalloc(sizeof(struct tsdev), GFP_KERNEL);
-	if (!tsdev)
-		return -ENOMEM;
-
-	INIT_LIST_HEAD(&tsdev->client_list);
-	init_waitqueue_head(&tsdev->wait);
-
-	tsdev->exist = 1;
-	tsdev->minor = minor;
-	tsdev->handle.dev = dev;
-	tsdev->handle.name = tsdev->name;
-	tsdev->handle.handler = handler;
-	tsdev->handle.private = tsdev;
-	snprintf(tsdev->name, sizeof(tsdev->name), "ts%d", minor);
-
-	/* Precompute the rough calibration matrix */
-	delta = dev->absmax [ABS_X] - dev->absmin [ABS_X] + 1;
-	if (delta == 0)
-		delta = 1;
-	tsdev->cal.xscale = (xres << 8) / delta;
-	tsdev->cal.xtrans = - ((dev->absmin [ABS_X] * tsdev->cal.xscale) >> 8);
-
-	delta = dev->absmax [ABS_Y] - dev->absmin [ABS_Y] + 1;
-	if (delta == 0)
-		delta = 1;
-	tsdev->cal.yscale = (yres << 8) / delta;
-	tsdev->cal.ytrans = - ((dev->absmin [ABS_Y] * tsdev->cal.yscale) >> 8);
-
-	snprintf(tsdev->dev.bus_id, sizeof(tsdev->dev.bus_id),
-		 "ts%d", minor);
-	tsdev->dev.class = &input_class;
-	tsdev->dev.parent = &dev->dev;
-	tsdev->dev.devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor);
-	tsdev->dev.release = tsdev_free;
-	device_initialize(&tsdev->dev);
-
-	tsdev_table[minor] = tsdev;
-
-	error = device_add(&tsdev->dev);
-	if (error)
-		goto err_free_tsdev;
-
-	error = input_register_handle(&tsdev->handle);
-	if (error)
-		goto err_delete_tsdev;
-
-	return 0;
-
- err_delete_tsdev:
-	device_del(&tsdev->dev);
- err_free_tsdev:
-	put_device(&tsdev->dev);
-	return error;
-}
-
-static void tsdev_disconnect(struct input_handle *handle)
-{
-	struct tsdev *tsdev = handle->private;
-	struct tsdev_client *client;
-
-	input_unregister_handle(handle);
-	device_del(&tsdev->dev);
-
-	tsdev->exist = 0;
-
-	if (tsdev->open) {
-		input_close_device(handle);
-		list_for_each_entry(client, &tsdev->client_list, node)
-			kill_fasync(&client->fasync, SIGIO, POLL_HUP);
-		wake_up_interruptible(&tsdev->wait);
-	}
-
-	put_device(&tsdev->dev);
-}
-
-static const struct input_device_id tsdev_ids[] = {
-	{
-	      .flags	= INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
-	      .evbit	= { BIT(EV_KEY) | BIT(EV_REL) },
-	      .keybit	= { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) },
-	      .relbit	= { BIT(REL_X) | BIT(REL_Y) },
-	}, /* A mouse like device, at least one button, two relative axes */
-
-	{
-	      .flags	= INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
-	      .evbit	= { BIT(EV_KEY) | BIT(EV_ABS) },
-	      .keybit	= { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
-	      .absbit	= { BIT(ABS_X) | BIT(ABS_Y) },
-	}, /* A tablet like device, at least touch detection, two absolute axes */
-
-	{
-	      .flags	= INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
-	      .evbit	= { BIT(EV_ABS) },
-	      .absbit	= { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) },
-	}, /* A tablet like device with several gradations of pressure */
-
-	{} /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(input, tsdev_ids);
-
-static struct input_handler tsdev_handler = {
-	.event =	tsdev_event,
-	.connect =	tsdev_connect,
-	.disconnect =	tsdev_disconnect,
-	.fops =		&tsdev_fops,
-	.minor =	TSDEV_MINOR_BASE,
-	.name =		"tsdev",
-	.id_table =	tsdev_ids,
-};
-
-static int __init tsdev_init(void)
-{
-	return input_register_handler(&tsdev_handler);
-}
-
-static void __exit tsdev_exit(void)
-{
-	input_unregister_handler(&tsdev_handler);
-}
-
-module_init(tsdev_init);
-module_exit(tsdev_exit);

+ 1 - 0
drivers/macintosh/Kconfig

@@ -172,6 +172,7 @@ config INPUT_ADBHID
 
 config MAC_EMUMOUSEBTN
 	bool "Support for mouse button 2+3 emulation"
+	select INPUT
 	help
 	  This provides generic support for emulating the 2nd and 3rd mouse
 	  button with keypresses.  If you say Y here, the emulation is still

+ 48 - 10
drivers/macintosh/adbhid.c

@@ -52,6 +52,11 @@
 
 MODULE_AUTHOR("Franz Sirl <Franz.Sirl-kernel@lauterbach.com>");
 
+static int restore_capslock_events;
+module_param(restore_capslock_events, int, 0644);
+MODULE_PARM_DESC(restore_capslock_events,
+	"Produce keypress events for capslock on both keyup and keydown.");
+
 #define KEYB_KEYREG	0	/* register # for key up/down data */
 #define KEYB_LEDREG	2	/* register # for leds on ADB keyboard */
 #define MOUSE_DATAREG	0	/* reg# for movement/button codes from mouse */
@@ -217,6 +222,8 @@ struct adbhid {
 #define FLAG_FN_KEY_PRESSED	0x00000001
 #define FLAG_POWER_FROM_FN	0x00000002
 #define FLAG_EMU_FWDEL_DOWN	0x00000004
+#define FLAG_CAPSLOCK_TRANSLATE	0x00000008
+#define FLAG_CAPSLOCK_DOWN	0x00000010
 
 static struct adbhid *adbhid[16];
 
@@ -272,19 +279,50 @@ adbhid_keyboard_input(unsigned char *data, int nb, int apoll)
 }
 
 static void
-adbhid_input_keycode(int id, int keycode, int repeat)
+adbhid_input_keycode(int id, int scancode, int repeat)
 {
 	struct adbhid *ahid = adbhid[id];
-	int up_flag, key;
-
-	up_flag = (keycode & 0x80);
-	keycode &= 0x7f;
+	int keycode, up_flag;
+
+	keycode = scancode & 0x7f;
+	up_flag = scancode & 0x80;
+
+	if (restore_capslock_events) {
+		if (keycode == ADB_KEY_CAPSLOCK && !up_flag) {
+			/* Key pressed, turning on the CapsLock LED.
+			 * The next 0xff will be interpreted as a release. */
+			ahid->flags |= FLAG_CAPSLOCK_TRANSLATE
+					| FLAG_CAPSLOCK_DOWN;
+		} else if (scancode == 0xff) {
+			/* Scancode 0xff usually signifies that the capslock
+			 * key was either pressed or released. */
+			if (ahid->flags & FLAG_CAPSLOCK_TRANSLATE) {
+				keycode = ADB_KEY_CAPSLOCK;
+				if (ahid->flags & FLAG_CAPSLOCK_DOWN) {
+					/* Key released */
+					up_flag = 1;
+					ahid->flags &= ~FLAG_CAPSLOCK_DOWN;
+				} else {
+					/* Key pressed */
+					up_flag = 0;
+					ahid->flags &= ~FLAG_CAPSLOCK_TRANSLATE;
+				}
+			} else {
+				printk(KERN_INFO "Spurious caps lock event "
+						"(scancode 0xff).");
+			}
+		}
+	}
 
 	switch (keycode) {
-	case ADB_KEY_CAPSLOCK: /* Generate down/up events for CapsLock everytime. */
-		input_report_key(ahid->input, KEY_CAPSLOCK, 1);
-		input_report_key(ahid->input, KEY_CAPSLOCK, 0);
-		input_sync(ahid->input);
+	case ADB_KEY_CAPSLOCK:
+		if (!restore_capslock_events) {
+			/* Generate down/up events for CapsLock everytime. */
+			input_report_key(ahid->input, KEY_CAPSLOCK, 1);
+			input_sync(ahid->input);
+			input_report_key(ahid->input, KEY_CAPSLOCK, 0);
+			input_sync(ahid->input);
+		}
 		return;
 #ifdef CONFIG_PPC_PMAC
 	case ADB_KEY_POWER_OLD: /* Power key on PBook 3400 needs remapping */
@@ -296,7 +334,7 @@ adbhid_input_keycode(int id, int keycode, int repeat)
 			keycode = ADB_KEY_POWER;
 		}
 		break;
-	case ADB_KEY_POWER: 
+	case ADB_KEY_POWER:
 		/* Fn + Command will produce a bogus "power" keycode */
 		if (ahid->flags & FLAG_FN_KEY_PRESSED) {
 			keycode = ADB_KEY_CMD;

+ 17 - 0
include/asm-blackfin/mach-bf548/bf54x_keys.h

@@ -0,0 +1,17 @@
+#ifndef _BFIN_KPAD_H
+#define _BFIN_KPAD_H
+
+struct bfin_kpad_platform_data {
+	int rows;
+	int cols;
+	const unsigned int *keymap;
+	unsigned short keymapsize;
+	unsigned short repeat;
+	u32 debounce_time;	/* in ns */
+	u32 coldrive_time;	/* in ns */
+	u32 keyup_test_interval; /* in ms */
+};
+
+#define KEYVAL(col, row, val) (((1 << col) << 24) | ((1 << row) << 16) | (val))
+
+#endif

+ 1 - 0
include/linux/gpio_keys.h

@@ -8,6 +8,7 @@ struct gpio_keys_button {
 	int active_low;
 	char *desc;
 	int type;		/* input event type (EV_KEY, EV_SW) */
+	int wakeup;		/* configure the button as a wake-up source */
 };
 
 struct gpio_keys_platform_data {

+ 106 - 10
include/linux/input.h

@@ -856,7 +856,7 @@ struct ff_rumble_effect {
  *	defining effect parameters
  *
  * This structure is sent through ioctl from the application to the driver.
- * To create a new effect aplication should set its @id to -1; the kernel
+ * To create a new effect application should set its @id to -1; the kernel
  * will return assigned @id which can later be used to update or delete
  * this effect.
  *
@@ -936,9 +936,82 @@ struct ff_effect {
 #define BIT(x)	(1UL<<((x)%BITS_PER_LONG))
 #define LONG(x) ((x)/BITS_PER_LONG)
 
+/**
+ * struct input_dev - represents an input device
+ * @name: name of the device
+ * @phys: physical path to the device in the system hierarchy
+ * @uniq: unique identification code for the device (if device has it)
+ * @id: id of the device (struct input_id)
+ * @evbit: bitmap of types of events supported by the device (EV_KEY,
+ *	EV_REL, etc.)
+ * @keybit: bitmap of keys/buttons this device has
+ * @relbit: bitmap of relative axes for the device
+ * @absbit: bitmap of absolute axes for the device
+ * @mscbit: bitmap of miscellaneous events supported by the device
+ * @ledbit: bitmap of leds present on the device
+ * @sndbit: bitmap of sound effects supported by the device
+ * @ffbit: bitmap of force feedback effects supported by the device
+ * @swbit: bitmap of switches present on the device
+ * @keycodemax: size of keycode table
+ * @keycodesize: size of elements in keycode table
+ * @keycode: map of scancodes to keycodes for this device
+ * @setkeycode: optional method to alter current keymap, used to implement
+ *	sparse keymaps. If not supplied default mechanism will be used
+ * @getkeycode: optional method to retrieve current keymap. If not supplied
+ *	default mechanism will be used
+ * @ff: force feedback structure associated with the device if device
+ *	supports force feedback effects
+ * @repeat_key: stores key code of the last key pressed; used to implement
+ *	software autorepeat
+ * @timer: timer for software autorepeat
+ * @sync: set to 1 when there were no new events since last EV_SYNC
+ * @abs: current values for reports from absolute axes
+ * @rep: current values for autorepeat parameters (delay, rate)
+ * @key: reflects current state of device's keys/buttons
+ * @led: reflects current state of device's LEDs
+ * @snd: reflects current state of sound effects
+ * @sw: reflects current state of device's switches
+ * @absmax: maximum values for events coming from absolute axes
+ * @absmin: minimum values for events coming from absolute axes
+ * @absfuzz: describes noisiness for axes
+ * @absflat: size of the center flat position (used by joydev)
+ * @open: this method is called when the very first user calls
+ *	input_open_device(). The driver must prepare the device
+ *	to start generating events (start polling thread,
+ *	request an IRQ, submit URB, etc.)
+ * @close: this method is called when the very last user calls
+ *	input_close_device().
+ * @flush: purges the device. Most commonly used to get rid of force
+ *	feedback effects loaded into the device when disconnecting
+ *	from it
+ * @event: event handler for events sent _to_ the device, like EV_LED
+ *	or EV_SND. The device is expected to carry out the requested
+ *	action (turn on a LED, play sound, etc.) The call is protected
+ *	by @event_lock and must not sleep
+ * @grab: input handle that currently has the device grabbed (via
+ *	EVIOCGRAB ioctl). When a handle grabs a device it becomes sole
+ *	recipient for all input events coming from the device
+ * @event_lock: this spinlock is is taken when input core receives
+ *	and processes a new event for the device (in input_event()).
+ *	Code that accesses and/or modifies parameters of a device
+ *	(such as keymap or absmin, absmax, absfuzz, etc.) after device
+ *	has been registered with input core must take this lock.
+ * @mutex: serializes calls to open(), close() and flush() methods
+ * @users: stores number of users (input handlers) that opened this
+ *	device. It is used by input_open_device() and input_close_device()
+ *	to make sure that dev->open() is only called when the first
+ *	user opens device and dev->close() is called when the very
+ *	last user closes the device
+ * @going_away: marks devices that are in a middle of unregistering and
+ *	causes input_open_device*() fail with -ENODEV.
+ * @dev: driver model's view of this device
+ * @h_list: list of input handles associated with the device. When
+ *	accessing the list dev->mutex must be held
+ * @node: used to place the device onto input_dev_list
+ */
 struct input_dev {
 
-	void *private;
+	void *private;	/* do not use */
 
 	const char *name;
 	const char *phys;
@@ -966,8 +1039,6 @@ struct input_dev {
 	unsigned int repeat_key;
 	struct timer_list timer;
 
-	int state;
-
 	int sync;
 
 	int abs[ABS_MAX + 1];
@@ -990,8 +1061,11 @@ struct input_dev {
 
 	struct input_handle *grab;
 
-	struct mutex mutex;	/* serializes open and close operations */
+	spinlock_t event_lock;
+	struct mutex mutex;
+
 	unsigned int users;
+	int going_away;
 
 	struct device dev;
 	union {			/* temporarily so while we switching to struct device */
@@ -1057,7 +1131,9 @@ struct input_handle;
 /**
  * struct input_handler - implements one of interfaces for input devices
  * @private: driver-specific data
- * @event: event handler
+ * @event: event handler. This method is being called by input core with
+ *	interrupts disabled and dev->event_lock spinlock held and so
+ *	it may not sleep
  * @connect: called when attaching a handler to an input device
  * @disconnect: disconnects a handler from input device
  * @start: starts handler for given handle. This function is called by
@@ -1069,10 +1145,18 @@ struct input_handle;
  * @name: name of the handler, to be shown in /proc/bus/input/handlers
  * @id_table: pointer to a table of input_device_ids this driver can
  *	handle
- * @blacklist: prointer to a table of input_device_ids this driver should
+ * @blacklist: pointer to a table of input_device_ids this driver should
  *	ignore even if they match @id_table
  * @h_list: list of input handles associated with the handler
  * @node: for placing the driver onto input_handler_list
+ *
+ * Input handlers attach to input devices and create input handles. There
+ * are likely several handlers attached to any given input device at the
+ * same time. All of them will get their copy of input event generated by
+ * the device.
+ *
+ * Note that input core serializes calls to connect() and disconnect()
+ * methods.
  */
 struct input_handler {
 
@@ -1094,6 +1178,18 @@ struct input_handler {
 	struct list_head	node;
 };
 
+/**
+ * struct input_handle - links input device with an input handler
+ * @private: handler-specific data
+ * @open: counter showing whether the handle is 'open', i.e. should deliver
+ *	events from its device
+ * @name: name given to the handle by handler that created it
+ * @dev: input device the handle is attached to
+ * @handler: handler that works with the device through this handle
+ * @d_node: used to put the handle on device's list of attached handles
+ * @h_node: used to put the handle on handler's list of handles from which
+ *	it gets events
+ */
 struct input_handle {
 
 	void *private;
@@ -1136,10 +1232,10 @@ static inline void input_set_drvdata(struct input_dev *dev, void *data)
 	dev->private = data;
 }
 
-int input_register_device(struct input_dev *);
+int __must_check input_register_device(struct input_dev *);
 void input_unregister_device(struct input_dev *);
 
-int input_register_handler(struct input_handler *);
+int __must_check input_register_handler(struct input_handler *);
 void input_unregister_handler(struct input_handler *);
 
 int input_register_handle(struct input_handle *);
@@ -1216,7 +1312,7 @@ extern struct class input_class;
  * @max_effects: maximum number of effects supported by device
  * @effects: pointer to an array of effects currently loaded into device
  * @effect_owners: array of effect owners; when file handle owning
- *	an effect gets closed the effcet is automatically erased
+ *	an effect gets closed the effect is automatically erased
  *
  * Every force-feedback device must implement upload() and playback()
  * methods; erase() is optional. set_gain() and set_autocenter() need

+ 3 - 1
include/linux/keyboard.h

@@ -416,6 +416,7 @@ extern unsigned short plain_map[NR_KEYS];
 #define K_SHIFTRLOCK	K(KT_LOCK,KG_SHIFTR)
 #define K_CTRLLLOCK	K(KT_LOCK,KG_CTRLL)
 #define K_CTRLRLOCK	K(KT_LOCK,KG_CTRLR)
+#define K_CAPSSHIFTLOCK	K(KT_LOCK,KG_CAPSSHIFT)
 
 #define K_SHIFT_SLOCK	K(KT_SLOCK,KG_SHIFT)
 #define K_CTRL_SLOCK	K(KT_SLOCK,KG_CTRL)
@@ -425,8 +426,9 @@ extern unsigned short plain_map[NR_KEYS];
 #define K_SHIFTR_SLOCK	K(KT_SLOCK,KG_SHIFTR)
 #define K_CTRLL_SLOCK	K(KT_SLOCK,KG_CTRLL)
 #define K_CTRLR_SLOCK	K(KT_SLOCK,KG_CTRLR)
+#define K_CAPSSHIFT_SLOCK	K(KT_SLOCK,KG_CAPSSHIFT)
 
-#define NR_LOCK		8
+#define NR_LOCK		9
 
 #define K_BRL_BLANK     K(KT_BRL, 0)
 #define K_BRL_DOT1      K(KT_BRL, 1)

部分文件因为文件数量过多而无法显示