summaryrefslogtreecommitdiff
path: root/drivers/input
diff options
context:
space:
mode:
authorVojtech Pavlik <vojtech@suse.cz>2004-09-16 19:10:08 +0200
committerVojtech Pavlik <vojtech@suse.cz>2004-09-16 19:10:08 +0200
commita21165ce22bea18419fc20f2b58b392680453ff4 (patch)
treee35a63a15ae5861cd93175f9bee84722c1a86c1e /drivers/input
parent5c5b9c24188adf7a37ad51844910f2dea4d0dfab (diff)
parent8718a604d60d615845e9d6bacd7107f795560694 (diff)
Merge suse.cz:/data2/bk/linus into suse.cz:/data2/bk/input
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/gameport/emu10k1-gp.c3
-rw-r--r--drivers/input/joydev.c6
-rw-r--r--drivers/input/joystick/Kconfig2
-rw-r--r--drivers/input/joystick/gamecon.c194
-rw-r--r--drivers/input/joystick/iforce/iforce-main.c4
-rw-r--r--drivers/input/joystick/iforce/iforce-serio.c18
-rw-r--r--drivers/input/joystick/iforce/iforce.h2
-rw-r--r--drivers/input/joystick/magellan.c24
-rw-r--r--drivers/input/joystick/spaceball.c24
-rw-r--r--drivers/input/joystick/spaceorb.c24
-rw-r--r--drivers/input/joystick/stinger.c24
-rw-r--r--drivers/input/joystick/tmdc.c2
-rw-r--r--drivers/input/joystick/twidjoy.c20
-rw-r--r--drivers/input/joystick/warrior.c24
-rw-r--r--drivers/input/keyboard/atkbd.c285
-rw-r--r--drivers/input/keyboard/lkkbd.c24
-rw-r--r--drivers/input/keyboard/newtonkbd.c24
-rw-r--r--drivers/input/keyboard/sunkbd.c24
-rw-r--r--drivers/input/keyboard/xtkbd.c24
-rw-r--r--drivers/input/misc/Kconfig2
-rw-r--r--drivers/input/misc/uinput.c3
-rw-r--r--drivers/input/mouse/Kconfig2
-rw-r--r--drivers/input/mouse/logips2pp.c2
-rw-r--r--drivers/input/mouse/psmouse-base.c312
-rw-r--r--drivers/input/mouse/psmouse.h40
-rw-r--r--drivers/input/mouse/sermouse.c24
-rw-r--r--drivers/input/mouse/synaptics.c54
-rw-r--r--drivers/input/mouse/vsxxxaa.c24
-rw-r--r--drivers/input/mousedev.c237
-rw-r--r--drivers/input/serio/Kconfig16
-rw-r--r--drivers/input/serio/Makefile1
-rw-r--r--drivers/input/serio/ambakmi.c44
-rw-r--r--drivers/input/serio/ct82c710.c104
-rw-r--r--drivers/input/serio/gscps2.c62
-rw-r--r--drivers/input/serio/i8042-io.h31
-rw-r--r--drivers/input/serio/i8042.c353
-rw-r--r--drivers/input/serio/i8042.h7
-rw-r--r--drivers/input/serio/maceps2.c86
-rw-r--r--drivers/input/serio/parkbd.c47
-rw-r--r--drivers/input/serio/pcips2.c54
-rw-r--r--drivers/input/serio/q40kbd.c117
-rw-r--r--drivers/input/serio/rpckbd.c48
-rw-r--r--drivers/input/serio/sa1111ps2.c41
-rw-r--r--drivers/input/serio/serio.c576
-rw-r--r--drivers/input/serio/serio_raw.c390
-rw-r--r--drivers/input/serio/serport.c49
-rw-r--r--drivers/input/touchscreen/gunze.c24
-rw-r--r--drivers/input/touchscreen/h3600_ts_input.c24
-rw-r--r--drivers/input/tsdev.c301
49 files changed, 2694 insertions, 1133 deletions
diff --git a/drivers/input/gameport/emu10k1-gp.c b/drivers/input/gameport/emu10k1-gp.c
index b8badfc110f9..8f1461ad2984 100644
--- a/drivers/input/gameport/emu10k1-gp.c
+++ b/drivers/input/gameport/emu10k1-gp.c
@@ -50,8 +50,11 @@ struct emu {
};
static struct pci_device_id emu_tbl[] = {
+
{ 0x1102, 0x7002, PCI_ANY_ID, PCI_ANY_ID }, /* SB Live gameport */
{ 0x1102, 0x7003, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy gameport */
+ { 0x1102, 0x7004, PCI_ANY_ID, PCI_ANY_ID }, /* Dell SB Live */
+ { 0x1102, 0x7005, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy LS gameport */
{ 0, }
};
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index 5eb884656db8..3e47218e0d6a 100644
--- a/drivers/input/joydev.c
+++ b/drivers/input/joydev.c
@@ -232,8 +232,10 @@ static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, lo
&& list->head == list->tail && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
- retval = wait_event_interruptible(list->joydev->wait, list->joydev->exist
- && (list->startup < joydev->nabs + joydev->nkey || list->head != list->tail));
+ retval = wait_event_interruptible(list->joydev->wait,
+ !list->joydev->exist ||
+ list->startup < joydev->nabs + joydev->nkey ||
+ list->head != list->tail);
if (retval)
return retval;
diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
index 7d506f1f742f..d2903255ef7f 100644
--- a/drivers/input/joystick/Kconfig
+++ b/drivers/input/joystick/Kconfig
@@ -247,7 +247,7 @@ config JOYSTICK_AMIGA
To compile this driver as a module, choose M here: the
module will be called amijoy.
-config INPUT_JOYDUMP
+config JOYSTICK_JOYDUMP
tristate "Gameport data dumper"
depends on INPUT && INPUT_JOYSTICK
help
diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c
index 555fb362ba31..46326b701743 100644
--- a/drivers/input/joystick/gamecon.c
+++ b/drivers/input/joystick/gamecon.c
@@ -1,7 +1,8 @@
/*
- * $Id: gamecon.c,v 1.22 2002/07/01 15:42:25 vojtech Exp $
+ * NES, SNES, N64, MultiSystem, PSX gamepad driver for Linux
*
- * Copyright (c) 1999-2001 Vojtech Pavlik
+ * Copyright (c) 1999-2004 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2004 Peter Nelson <rufus-kernel@hackish.org>
*
* Based on the work of:
* Andree Borrmann John Dahlstrom
@@ -9,10 +10,6 @@
*/
/*
- * NES, SNES, N64, MultiSystem, PSX gamepad driver for Linux
- */
-
-/*
* 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
@@ -72,8 +69,9 @@ __obsolete_setup("gc_3=");
#define GC_MULTI2 5
#define GC_N64 6
#define GC_PSX 7
+#define GC_DDR 8
-#define GC_MAX 7
+#define GC_MAX 8
#define GC_REFRESH_TIME HZ/100
@@ -91,7 +89,8 @@ static struct gc *gc_base[3];
static int gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 };
static char *gc_names[] = { NULL, "SNES pad", "NES pad", "NES FourPort", "Multisystem joystick",
- "Multisystem 2-button joystick", "N64 controller", "PSX controller" };
+ "Multisystem 2-button joystick", "N64 controller", "PSX controller"
+ "PSX DDR controller" };
/*
* N64 support.
*/
@@ -237,7 +236,7 @@ static void gc_multi_read_packet(struct gc *gc, int length, unsigned char *data)
#define GC_PSX_RUMBLE 7 /* Rumble in Red mode */
#define GC_PSX_CLOCK 0x04 /* Pin 4 */
-#define GC_PSX_COMMAND 0x01 /* Pin 1 */
+#define GC_PSX_COMMAND 0x01 /* Pin 2 */
#define GC_PSX_POWER 0xf8 /* Pins 5-9 */
#define GC_PSX_SELECT 0x02 /* Pin 3 */
@@ -253,25 +252,29 @@ __obsolete_setup("gc_psx_delay=");
static short gc_psx_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_HAT0X, ABS_HAT0Y };
static short gc_psx_btn[] = { BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_A, BTN_B, BTN_X, BTN_Y,
BTN_START, BTN_SELECT, BTN_THUMBL, BTN_THUMBR };
+static short gc_psx_ddr_btn[] = { BTN_0, BTN_1, BTN_2, BTN_3 };
/*
* gc_psx_command() writes 8bit command and reads 8bit data from
* the psx pad.
*/
-static int gc_psx_command(struct gc *gc, int b)
+static void gc_psx_command(struct gc *gc, int b, unsigned char data[GC_PSX_LENGTH])
{
- int i, cmd, data = 0;
+ int i, j, cmd, read;
+ for (i = 0; i < 5; i++)
+ data[i] = 0;
for (i = 0; i < 8; i++, b >>= 1) {
cmd = (b & 1) ? GC_PSX_COMMAND : 0;
parport_write_data(gc->pd->port, cmd | GC_PSX_POWER);
udelay(gc_psx_delay);
- data |= ((parport_read_status(gc->pd->port) ^ 0x80) & gc->pads[GC_PSX]) ? (1 << i) : 0;
+ read = parport_read_status(gc->pd->port) ^ 0x80;
+ for (j = 0; j < 5; j++)
+ data[j] |= (read & gc_status_bit[j] & gc->pads[GC_PSX]) ? (1 << i) : 0;
parport_write_data(gc->pd->port, cmd | GC_PSX_CLOCK | GC_PSX_POWER);
udelay(gc_psx_delay);
}
- return data;
}
/*
@@ -279,30 +282,39 @@ static int gc_psx_command(struct gc *gc, int b)
* device identifier code.
*/
-static int gc_psx_read_packet(struct gc *gc, unsigned char *data)
+static void gc_psx_read_packet(struct gc *gc, unsigned char data[5][GC_PSX_LENGTH], unsigned char id[5])
{
- int i, id;
+ int i, j, max_len = 0;
unsigned long flags;
+ unsigned char data2[5];
parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER); /* Select pad */
- udelay(gc_psx_delay * 2);
+ udelay(gc_psx_delay);
parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_POWER); /* Deselect, begin command */
- udelay(gc_psx_delay * 2);
+ udelay(gc_psx_delay);
local_irq_save(flags);
- gc_psx_command(gc, 0x01); /* Access pad */
- id = gc_psx_command(gc, 0x42); /* Get device id */
- if (gc_psx_command(gc, 0) == 0x5a) { /* Okay? */
- for (i = 0; i < GC_PSX_LEN(id) * 2; i++)
- data[i] = gc_psx_command(gc, 0);
- } else id = 0;
+ gc_psx_command(gc, 0x01, data2); /* Access pad */
+ gc_psx_command(gc, 0x42, id); /* Get device ids */
+ gc_psx_command(gc, 0, data2); /* Dump status */
+
+ for (i =0; i < 5; i++) /* Find the longest pad */
+ if((gc_status_bit[i] & gc->pads[GC_PSX]) && (GC_PSX_LEN(id[i]) > max_len))
+ max_len = GC_PSX_LEN(id[i]);
+
+ for (i = 0; i < max_len * 2; i++) { /* Read in all the data */
+ gc_psx_command(gc, 0, data2);
+ for (j = 0; j < 5; j++)
+ data[j][i] = data2[j];
+ }
local_irq_restore(flags);
parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER);
- return GC_PSX_ID(id);
+ for(i = 0; i < 5; i++) /* Set id's to the real value */
+ id[i] = GC_PSX_ID(id[i]);
}
/*
@@ -316,6 +328,7 @@ static void gc_timer(unsigned long private)
struct gc *gc = (void *) private;
struct input_dev *dev = gc->dev;
unsigned char data[GC_MAX_LENGTH];
+ unsigned char data_psx[5][GC_PSX_LENGTH];
int i, j, s;
/*
@@ -412,53 +425,72 @@ static void gc_timer(unsigned long private)
* PSX controllers
*/
- if (gc->pads[GC_PSX]) {
+ if (gc->pads[GC_PSX] || gc->pads[GC_DDR]) {
- for (i = 0; i < 5; i++)
- if (gc->pads[GC_PSX] & gc_status_bit[i])
- break;
+ gc_psx_read_packet(gc, data_psx, data);
- switch (gc_psx_read_packet(gc, data)) {
+ for (i = 0; i < 5; i++) {
+ switch (data[i]) {
- case GC_PSX_RUMBLE:
+ case GC_PSX_RUMBLE:
- input_report_key(dev + i, BTN_THUMBL, ~data[0] & 0x04);
- input_report_key(dev + i, BTN_THUMBR, ~data[0] & 0x02);
- input_sync(dev + i);
+ input_report_key(dev + i, BTN_THUMBL, ~data_psx[i][0] & 0x04);
+ input_report_key(dev + i, BTN_THUMBR, ~data_psx[i][0] & 0x02);
- case GC_PSX_NEGCON:
- case GC_PSX_ANALOG:
+ case GC_PSX_NEGCON:
+ case GC_PSX_ANALOG:
- for (j = 0; j < 4; j++)
- input_report_abs(dev + i, gc_psx_abs[j], data[j + 2]);
+ if(gc->pads[GC_DDR] & gc_status_bit[i]) {
+ for(j = 0; j < 4; j++)
+ input_report_key(dev + i, gc_psx_ddr_btn[j], ~data_psx[i][0] & (0x10 << j));
+ } else {
+ for (j = 0; j < 4; j++)
+ input_report_abs(dev + i, gc_psx_abs[j+2], data_psx[i][j + 2]);
- input_report_abs(dev + i, ABS_HAT0X, !(data[0] & 0x20) - !(data[0] & 0x80));
- input_report_abs(dev + i, ABS_HAT0Y, !(data[0] & 0x40) - !(data[0] & 0x10));
+ input_report_abs(dev + i, ABS_X, 128 + !(data_psx[i][0] & 0x20) * 127 - !(data_psx[i][0] & 0x80) * 128);
+ input_report_abs(dev + i, ABS_Y, 128 + !(data_psx[i][0] & 0x40) * 127 - !(data_psx[i][0] & 0x10) * 128);
+ }
- for (j = 0; j < 8; j++)
- input_report_key(dev + i, gc_psx_btn[j], ~data[1] & (1 << j));
+ for (j = 0; j < 8; j++)
+ input_report_key(dev + i, gc_psx_btn[j], ~data_psx[i][1] & (1 << j));
- input_report_key(dev + i, BTN_START, ~data[0] & 0x08);
- input_report_key(dev + i, BTN_SELECT, ~data[0] & 0x01);
+ input_report_key(dev + i, BTN_START, ~data_psx[i][0] & 0x08);
+ input_report_key(dev + i, BTN_SELECT, ~data_psx[i][0] & 0x01);
- input_sync(dev + i);
+ input_sync(dev + i);
- break;
+ break;
- case GC_PSX_NORMAL:
+ case GC_PSX_NORMAL:
+ if(gc->pads[GC_DDR] & gc_status_bit[i]) {
+ for(j = 0; j < 4; j++)
+ input_report_key(dev + i, gc_psx_ddr_btn[j], ~data_psx[i][0] & (0x10 << j));
+ } else {
+ input_report_abs(dev + i, ABS_X, 128 + !(data_psx[i][0] & 0x20) * 127 - !(data_psx[i][0] & 0x80) * 128);
+ input_report_abs(dev + i, ABS_Y, 128 + !(data_psx[i][0] & 0x40) * 127 - !(data_psx[i][0] & 0x10) * 128);
- input_report_abs(dev + i, ABS_X, 128 + !(data[0] & 0x20) * 127 - !(data[0] & 0x80) * 128);
- input_report_abs(dev + i, ABS_Y, 128 + !(data[0] & 0x40) * 127 - !(data[0] & 0x10) * 128);
+ /* for some reason if the extra axes are left unset they drift */
+ /* for (j = 0; j < 4; j++)
+ input_report_abs(dev + i, gc_psx_abs[j+2], 128);
+ * This needs to be debugged properly,
+ * maybe fuzz processing needs to be done in input_sync()
+ * --vojtech
+ */
+ }
- for (j = 0; j < 8; j++)
- input_report_key(dev + i, gc_psx_btn[j], ~data[1] & (1 << j));
+ for (j = 0; j < 8; j++)
+ input_report_key(dev + i, gc_psx_btn[j], ~data_psx[i][1] & (1 << j));
- input_report_key(dev + i, BTN_START, ~data[0] & 0x08);
- input_report_key(dev + i, BTN_SELECT, ~data[0] & 0x01);
+ input_report_key(dev + i, BTN_START, ~data_psx[i][0] & 0x08);
+ input_report_key(dev + i, BTN_SELECT, ~data_psx[i][0] & 0x01);
- input_sync(dev + i);
+ input_sync(dev + i);
- break;
+ break;
+
+ case 0: /* not a pad, ignore */
+ break;
+ }
}
}
@@ -490,8 +522,7 @@ static struct gc __init *gc_probe(int *config, int nargs)
{
struct gc *gc;
struct parport *pp;
- int i, j, psx;
- unsigned char data[32];
+ int i, j;
if (config[0] < 0)
return NULL;
@@ -588,43 +619,22 @@ static struct gc __init *gc_probe(int *config, int nargs)
break;
case GC_PSX:
-
- psx = gc_psx_read_packet(gc, data);
-
- switch(psx) {
- case GC_PSX_NEGCON:
- case GC_PSX_NORMAL:
- case GC_PSX_ANALOG:
- case GC_PSX_RUMBLE:
-
- for (j = 0; j < 6; j++) {
- psx = gc_psx_abs[j];
- set_bit(psx, gc->dev[i].absbit);
- if (j < 4) {
- gc->dev[i].absmin[psx] = 4;
- gc->dev[i].absmax[psx] = 252;
- gc->dev[i].absflat[psx] = 2;
- } else {
- gc->dev[i].absmin[psx] = -1;
- gc->dev[i].absmax[psx] = 1;
- }
- }
-
- for (j = 0; j < 12; j++)
- set_bit(gc_psx_btn[j], gc->dev[i].keybit);
-
- break;
-
- case 0:
- gc->pads[GC_PSX] &= ~gc_status_bit[i];
- printk(KERN_ERR "gamecon.c: No PSX controller found.\n");
- break;
-
- default:
- gc->pads[GC_PSX] &= ~gc_status_bit[i];
- printk(KERN_WARNING "gamecon.c: Unsupported PSX controller %#x,"
- " please report to <vojtech@ucw.cz>.\n", psx);
+ case GC_DDR:
+ if(config[i + 1] == GC_DDR) {
+ for (j = 0; j < 4; j++)
+ set_bit(gc_psx_ddr_btn[j], gc->dev[i].keybit);
+ } else {
+ for (j = 0; j < 6; j++) {
+ set_bit(gc_psx_abs[j], gc->dev[i].absbit);
+ gc->dev[i].absmin[gc_psx_abs[j]] = 4;
+ gc->dev[i].absmax[gc_psx_abs[j]] = 252;
+ gc->dev[i].absflat[gc_psx_abs[j]] = 2;
+ }
}
+
+ for (j = 0; j < 12; j++)
+ set_bit(gc_psx_btn[j], gc->dev[i].keybit);
+
break;
}
diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c
index 71cb7db75a63..cf815c7ee68d 100644
--- a/drivers/input/joystick/iforce/iforce-main.c
+++ b/drivers/input/joystick/iforce/iforce-main.c
@@ -524,7 +524,7 @@ static int __init iforce_init(void)
usb_register(&iforce_usb_driver);
#endif
#ifdef CONFIG_JOYSTICK_IFORCE_232
- serio_register_device(&iforce_serio_dev);
+ serio_register_driver(&iforce_serio_drv);
#endif
return 0;
}
@@ -535,7 +535,7 @@ static void __exit iforce_exit(void)
usb_deregister(&iforce_usb_driver);
#endif
#ifdef CONFIG_JOYSTICK_IFORCE_232
- serio_unregister_device(&iforce_serio_dev);
+ serio_unregister_driver(&iforce_serio_drv);
#endif
}
diff --git a/drivers/input/joystick/iforce/iforce-serio.c b/drivers/input/joystick/iforce/iforce-serio.c
index 0665b5460050..9422407c2acb 100644
--- a/drivers/input/joystick/iforce/iforce-serio.c
+++ b/drivers/input/joystick/iforce/iforce-serio.c
@@ -124,7 +124,7 @@ out:
return IRQ_HANDLED;
}
-static void iforce_serio_connect(struct serio *serio, struct serio_dev *dev)
+static void iforce_serio_connect(struct serio *serio, struct serio_driver *drv)
{
struct iforce *iforce;
if (serio->type != (SERIO_RS232 | SERIO_IFORCE))
@@ -137,7 +137,7 @@ static void iforce_serio_connect(struct serio *serio, struct serio_dev *dev)
iforce->serio = serio;
serio->private = iforce;
- if (serio_open(serio, dev)) {
+ if (serio_open(serio, drv)) {
kfree(iforce);
return;
}
@@ -158,9 +158,13 @@ static void iforce_serio_disconnect(struct serio *serio)
kfree(iforce);
}
-struct serio_dev iforce_serio_dev = {
- .write_wakeup = iforce_serio_write_wakeup,
- .interrupt = iforce_serio_irq,
- .connect = iforce_serio_connect,
- .disconnect = iforce_serio_disconnect,
+struct serio_driver iforce_serio_drv = {
+ .driver = {
+ .name = "iforce",
+ },
+ .description = "RS232 I-Force joysticks and wheels driver",
+ .write_wakeup = iforce_serio_write_wakeup,
+ .interrupt = iforce_serio_irq,
+ .connect = iforce_serio_connect,
+ .disconnect = iforce_serio_disconnect,
};
diff --git a/drivers/input/joystick/iforce/iforce.h b/drivers/input/joystick/iforce/iforce.h
index a8586abf530e..bce247bc300b 100644
--- a/drivers/input/joystick/iforce/iforce.h
+++ b/drivers/input/joystick/iforce/iforce.h
@@ -187,5 +187,5 @@ int iforce_upload_constant(struct iforce*, struct ff_effect*, int is_update);
int iforce_upload_condition(struct iforce*, struct ff_effect*, int is_update);
/* Public variables */
-extern struct serio_dev iforce_serio_dev;
+extern struct serio_driver iforce_serio_drv;
extern struct usb_driver iforce_usb_driver;
diff --git a/drivers/input/joystick/magellan.c b/drivers/input/joystick/magellan.c
index f326299b4193..2b303bac57ae 100644
--- a/drivers/input/joystick/magellan.c
+++ b/drivers/input/joystick/magellan.c
@@ -35,8 +35,10 @@
#include <linux/serio.h>
#include <linux/init.h>
+#define DRIVER_DESC "Magellan and SpaceMouse 6dof controller driver"
+
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
-MODULE_DESCRIPTION("Magellan and SpaceMouse 6dof controller driver");
+MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/*
@@ -146,7 +148,7 @@ static void magellan_disconnect(struct serio *serio)
* it as an input device.
*/
-static void magellan_connect(struct serio *serio, struct serio_dev *dev)
+static void magellan_connect(struct serio *serio, struct serio_driver *drv)
{
struct magellan *magellan;
int i, t;
@@ -184,7 +186,7 @@ static void magellan_connect(struct serio *serio, struct serio_dev *dev)
serio->private = magellan;
- if (serio_open(serio, dev)) {
+ if (serio_open(serio, drv)) {
kfree(magellan);
return;
}
@@ -199,10 +201,14 @@ static void magellan_connect(struct serio *serio, struct serio_dev *dev)
* The serio device structure.
*/
-static struct serio_dev magellan_dev = {
- .interrupt = magellan_interrupt,
- .connect = magellan_connect,
- .disconnect = magellan_disconnect,
+static struct serio_driver magellan_drv = {
+ .driver = {
+ .name = "magellan",
+ },
+ .description = DRIVER_DESC,
+ .interrupt = magellan_interrupt,
+ .connect = magellan_connect,
+ .disconnect = magellan_disconnect,
};
/*
@@ -211,13 +217,13 @@ static struct serio_dev magellan_dev = {
int __init magellan_init(void)
{
- serio_register_device(&magellan_dev);
+ serio_register_driver(&magellan_drv);
return 0;
}
void __exit magellan_exit(void)
{
- serio_unregister_device(&magellan_dev);
+ serio_unregister_driver(&magellan_drv);
}
module_init(magellan_init);
diff --git a/drivers/input/joystick/spaceball.c b/drivers/input/joystick/spaceball.c
index eb5b6f76c1f3..40412528ef9d 100644
--- a/drivers/input/joystick/spaceball.c
+++ b/drivers/input/joystick/spaceball.c
@@ -39,8 +39,10 @@
#include <linux/input.h>
#include <linux/serio.h>
+#define DRIVER_DESC "SpaceTec SpaceBall 2003/3003/4000 FLX driver"
+
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
-MODULE_DESCRIPTION("SpaceTec SpaceBall 2003/3003/4000 FLX driver");
+MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/*
@@ -201,7 +203,7 @@ static void spaceball_disconnect(struct serio *serio)
* it as an input device.
*/
-static void spaceball_connect(struct serio *serio, struct serio_dev *dev)
+static void spaceball_connect(struct serio *serio, struct serio_driver *drv)
{
struct spaceball *spaceball;
int i, t, id;
@@ -254,7 +256,7 @@ static void spaceball_connect(struct serio *serio, struct serio_dev *dev)
serio->private = spaceball;
- if (serio_open(serio, dev)) {
+ if (serio_open(serio, drv)) {
kfree(spaceball);
return;
}
@@ -269,10 +271,14 @@ static void spaceball_connect(struct serio *serio, struct serio_dev *dev)
* The serio device structure.
*/
-static struct serio_dev spaceball_dev = {
- .interrupt = spaceball_interrupt,
- .connect = spaceball_connect,
- .disconnect = spaceball_disconnect,
+static struct serio_driver spaceball_drv = {
+ .driver = {
+ .name = "spaceball",
+ },
+ .description = DRIVER_DESC,
+ .interrupt = spaceball_interrupt,
+ .connect = spaceball_connect,
+ .disconnect = spaceball_disconnect,
};
/*
@@ -281,13 +287,13 @@ static struct serio_dev spaceball_dev = {
int __init spaceball_init(void)
{
- serio_register_device(&spaceball_dev);
+ serio_register_driver(&spaceball_drv);
return 0;
}
void __exit spaceball_exit(void)
{
- serio_unregister_device(&spaceball_dev);
+ serio_unregister_driver(&spaceball_drv);
}
module_init(spaceball_init);
diff --git a/drivers/input/joystick/spaceorb.c b/drivers/input/joystick/spaceorb.c
index 00da66c4b489..c20091a6ecb5 100644
--- a/drivers/input/joystick/spaceorb.c
+++ b/drivers/input/joystick/spaceorb.c
@@ -38,8 +38,10 @@
#include <linux/input.h>
#include <linux/serio.h>
+#define DRIVER_DESC "SpaceTec SpaceOrb 360 and Avenger 6dof controller driver"
+
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
-MODULE_DESCRIPTION("SpaceTec SpaceOrb 360 and Avenger 6dof controller driver");
+MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/*
@@ -162,7 +164,7 @@ static void spaceorb_disconnect(struct serio *serio)
* it as an input device.
*/
-static void spaceorb_connect(struct serio *serio, struct serio_dev *dev)
+static void spaceorb_connect(struct serio *serio, struct serio_driver *drv)
{
struct spaceorb *spaceorb;
int i, t;
@@ -201,7 +203,7 @@ static void spaceorb_connect(struct serio *serio, struct serio_dev *dev)
serio->private = spaceorb;
- if (serio_open(serio, dev)) {
+ if (serio_open(serio, drv)) {
kfree(spaceorb);
return;
}
@@ -213,10 +215,14 @@ static void spaceorb_connect(struct serio *serio, struct serio_dev *dev)
* The serio device structure.
*/
-static struct serio_dev spaceorb_dev = {
- .interrupt = spaceorb_interrupt,
- .connect = spaceorb_connect,
- .disconnect = spaceorb_disconnect,
+static struct serio_driver spaceorb_drv = {
+ .driver = {
+ .name = "spaceorb",
+ },
+ .description = DRIVER_DESC,
+ .interrupt = spaceorb_interrupt,
+ .connect = spaceorb_connect,
+ .disconnect = spaceorb_disconnect,
};
/*
@@ -225,13 +231,13 @@ static struct serio_dev spaceorb_dev = {
int __init spaceorb_init(void)
{
- serio_register_device(&spaceorb_dev);
+ serio_register_driver(&spaceorb_drv);
return 0;
}
void __exit spaceorb_exit(void)
{
- serio_unregister_device(&spaceorb_dev);
+ serio_unregister_driver(&spaceorb_drv);
}
module_init(spaceorb_init);
diff --git a/drivers/input/joystick/stinger.c b/drivers/input/joystick/stinger.c
index ce9be4563135..580e32375f30 100644
--- a/drivers/input/joystick/stinger.c
+++ b/drivers/input/joystick/stinger.c
@@ -36,8 +36,10 @@
#include <linux/serio.h>
#include <linux/init.h>
+#define DRIVER_DESC "Gravis Stinger gamepad driver"
+
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
-MODULE_DESCRIPTION("Gravis Stinger gamepad driver");
+MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/*
@@ -134,7 +136,7 @@ static void stinger_disconnect(struct serio *serio)
* it as an input device.
*/
-static void stinger_connect(struct serio *serio, struct serio_dev *dev)
+static void stinger_connect(struct serio *serio, struct serio_driver *drv)
{
struct stinger *stinger;
int i;
@@ -172,7 +174,7 @@ static void stinger_connect(struct serio *serio, struct serio_dev *dev)
stinger->dev.private = stinger;
serio->private = stinger;
- if (serio_open(serio, dev)) {
+ if (serio_open(serio, drv)) {
kfree(stinger);
return;
}
@@ -187,10 +189,14 @@ static void stinger_connect(struct serio *serio, struct serio_dev *dev)
* The serio device structure.
*/
-static struct serio_dev stinger_dev = {
- .interrupt = stinger_interrupt,
- .connect = stinger_connect,
- .disconnect = stinger_disconnect,
+static struct serio_driver stinger_drv = {
+ .driver = {
+ .name = "stinger",
+ },
+ .description = DRIVER_DESC,
+ .interrupt = stinger_interrupt,
+ .connect = stinger_connect,
+ .disconnect = stinger_disconnect,
};
/*
@@ -199,13 +205,13 @@ static struct serio_dev stinger_dev = {
int __init stinger_init(void)
{
- serio_register_device(&stinger_dev);
+ serio_register_driver(&stinger_drv);
return 0;
}
void __exit stinger_exit(void)
{
- serio_unregister_device(&stinger_dev);
+ serio_unregister_driver(&stinger_drv);
}
module_init(stinger_init);
diff --git a/drivers/input/joystick/tmdc.c b/drivers/input/joystick/tmdc.c
index 6ed46ec46570..e11d2914826d 100644
--- a/drivers/input/joystick/tmdc.c
+++ b/drivers/input/joystick/tmdc.c
@@ -322,7 +322,7 @@ static void tmdc_connect(struct gameport *gameport, struct gameport_dev *dev)
tmdc->dev[j].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (i = 0; i < models[m].abs && i < TMDC_ABS; i++) {
- if (tmdc->abs[i] < 0) continue;
+ if (tmdc->abs[j][i] < 0) continue;
set_bit(tmdc->abs[j][i], tmdc->dev[j].absbit);
tmdc->dev[j].absmin[tmdc->abs[j][i]] = 8;
tmdc->dev[j].absmax[tmdc->abs[j][i]] = 248;
diff --git a/drivers/input/joystick/twidjoy.c b/drivers/input/joystick/twidjoy.c
index eb5c935a678e..909b6b54aae5 100644
--- a/drivers/input/joystick/twidjoy.c
+++ b/drivers/input/joystick/twidjoy.c
@@ -187,7 +187,7 @@ static void twidjoy_disconnect(struct serio *serio)
* it as an input device.
*/
-static void twidjoy_connect(struct serio *serio, struct serio_dev *dev)
+static void twidjoy_connect(struct serio *serio, struct serio_driver *drv)
{
struct twidjoy_button_spec *bp;
struct twidjoy *twidjoy;
@@ -232,7 +232,7 @@ static void twidjoy_connect(struct serio *serio, struct serio_dev *dev)
twidjoy->dev.private = twidjoy;
serio->private = twidjoy;
- if (serio_open(serio, dev)) {
+ if (serio_open(serio, drv)) {
kfree(twidjoy);
return;
}
@@ -246,10 +246,14 @@ static void twidjoy_connect(struct serio *serio, struct serio_dev *dev)
* The serio device structure.
*/
-static struct serio_dev twidjoy_dev = {
- .interrupt = twidjoy_interrupt,
- .connect = twidjoy_connect,
- .disconnect = twidjoy_disconnect,
+static struct serio_driver twidjoy_drv = {
+ .driver = {
+ .name = "twidjoy",
+ },
+ .description = DRIVER_DESC,
+ .interrupt = twidjoy_interrupt,
+ .connect = twidjoy_connect,
+ .disconnect = twidjoy_disconnect,
};
/*
@@ -258,13 +262,13 @@ static struct serio_dev twidjoy_dev = {
int __init twidjoy_init(void)
{
- serio_register_device(&twidjoy_dev);
+ serio_register_driver(&twidjoy_drv);
return 0;
}
void __exit twidjoy_exit(void)
{
- serio_unregister_device(&twidjoy_dev);
+ serio_unregister_driver(&twidjoy_drv);
}
module_init(twidjoy_init);
diff --git a/drivers/input/joystick/warrior.c b/drivers/input/joystick/warrior.c
index 32ec90e81d4a..5f19b2a51f27 100644
--- a/drivers/input/joystick/warrior.c
+++ b/drivers/input/joystick/warrior.c
@@ -35,8 +35,10 @@
#include <linux/serio.h>
#include <linux/init.h>
+#define DRIVER_DESC "Logitech WingMan Warrior joystick driver"
+
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
-MODULE_DESCRIPTION("Logitech WingMan Warrior joystick driver");
+MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/*
@@ -139,7 +141,7 @@ static void warrior_disconnect(struct serio *serio)
* it as an input device.
*/
-static void warrior_connect(struct serio *serio, struct serio_dev *dev)
+static void warrior_connect(struct serio *serio, struct serio_driver *drv)
{
struct warrior *warrior;
int i;
@@ -185,7 +187,7 @@ static void warrior_connect(struct serio *serio, struct serio_dev *dev)
serio->private = warrior;
- if (serio_open(serio, dev)) {
+ if (serio_open(serio, drv)) {
kfree(warrior);
return;
}
@@ -199,10 +201,14 @@ static void warrior_connect(struct serio *serio, struct serio_dev *dev)
* The serio device structure.
*/
-static struct serio_dev warrior_dev = {
- .interrupt = warrior_interrupt,
- .connect = warrior_connect,
- .disconnect = warrior_disconnect,
+static struct serio_driver warrior_drv = {
+ .driver = {
+ .name = "warrior",
+ },
+ .description = DRIVER_DESC,
+ .interrupt = warrior_interrupt,
+ .connect = warrior_connect,
+ .disconnect = warrior_disconnect,
};
/*
@@ -211,13 +217,13 @@ static struct serio_dev warrior_dev = {
int __init warrior_init(void)
{
- serio_register_device(&warrior_dev);
+ serio_register_driver(&warrior_drv);
return 0;
}
void __exit warrior_exit(void)
{
- serio_unregister_device(&warrior_dev);
+ serio_unregister_driver(&warrior_drv);
}
module_init(warrior_init);
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index 00ec1f86ad0f..ec97ae7d5052 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -27,8 +27,10 @@
#include <linux/serio.h>
#include <linux/workqueue.h>
+#define DRIVER_DESC "AT and PS/2 keyboard driver"
+
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
-MODULE_DESCRIPTION("AT and PS/2 keyboard driver");
+MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
static int atkbd_set = 2;
@@ -47,6 +49,10 @@ static int atkbd_softrepeat;
module_param_named(softrepeat, atkbd_softrepeat, bool, 0);
MODULE_PARM_DESC(softrepeat, "Use software keyboard repeat");
+static int atkbd_softraw = 1;
+module_param_named(softraw, atkbd_softraw, bool, 0);
+MODULE_PARM_DESC(softraw, "Use software generated rawmode");
+
static int atkbd_scroll;
module_param_named(scroll, atkbd_scroll, bool, 0);
MODULE_PARM_DESC(scroll, "Enable scroll-wheel on MS Office and similar keyboards");
@@ -164,36 +170,64 @@ static unsigned char atkbd_scroll_keys[5][2] = {
{ ATKBD_SCR_CLICK, 0x60 },
};
+#define ATKBD_FLAG_ACK 0 /* Waiting for ACK/NAK */
+#define ATKBD_FLAG_CMD 1 /* Waiting for command to finish */
+#define ATKBD_FLAG_CMD1 2 /* First byte of command response */
+#define ATKBD_FLAG_ENABLED 3 /* Waining for init to finish */
+
/*
* The atkbd control structure
*/
struct atkbd {
- unsigned char keycode[512];
- struct input_dev dev;
- struct serio *serio;
+ /* Written only during init */
char name[64];
char phys[32];
- unsigned short id;
+ struct serio *serio;
+ struct input_dev dev;
+
unsigned char set;
- unsigned int translated:1;
- unsigned int extra:1;
- unsigned int write:1;
+ unsigned short id;
+ unsigned char keycode[512];
+ unsigned char translated;
+ unsigned char extra;
+ unsigned char write;
+
+ /* Protected by FLAG_ACK */
+ unsigned char nak;
+ /* Protected by FLAG_CMD */
unsigned char cmdbuf[4];
unsigned char cmdcnt;
- volatile signed char ack;
- unsigned char emul;
- unsigned int resend:1;
- unsigned int release:1;
- unsigned int bat_xl:1;
- unsigned int enabled:1;
+ /* Accessed only from interrupt */
+ unsigned char emul;
+ unsigned char resend;
+ unsigned char release;
+ unsigned char bat_xl;
unsigned int last;
unsigned long time;
+
+ /* Ensures that only one command is executing at a time */
+ struct semaphore cmd_sem;
+
+ /* Used to signal completion from interrupt handler */
+ wait_queue_head_t wait;
+
+ /* Flags */
+ unsigned long flags;
+};
+
+/* Work structure to schedule execution of a command */
+struct atkbd_work {
+ struct work_struct work;
+ struct atkbd *atkbd;
+ int command;
+ unsigned char param[0];
};
+
static void atkbd_report_key(struct input_dev *dev, struct pt_regs *regs, int code, int value)
{
input_regs(dev, regs);
@@ -224,7 +258,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
#if !defined(__i386__) && !defined (__x86_64__)
if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && !atkbd->resend && atkbd->write) {
- printk("atkbd.c: frame/parity error: %02x\n", flags);
+ printk(KERN_WARNING "atkbd.c: frame/parity error: %02x\n", flags);
serio_write(serio, ATKBD_CMD_RESEND);
atkbd->resend = 1;
goto out;
@@ -234,24 +268,46 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
atkbd->resend = 0;
#endif
- if (!atkbd->ack)
+ if (test_bit(ATKBD_FLAG_ACK, &atkbd->flags)) {
switch (code) {
case ATKBD_RET_ACK:
- atkbd->ack = 1;
- goto out;
+ atkbd->nak = 0;
+ if (atkbd->cmdcnt) {
+ set_bit(ATKBD_FLAG_CMD, &atkbd->flags);
+ set_bit(ATKBD_FLAG_CMD1, &atkbd->flags);
+ }
+ clear_bit(ATKBD_FLAG_ACK, &atkbd->flags);
+ wake_up_interruptible(&atkbd->wait);
+ break;
case ATKBD_RET_NAK:
- atkbd->ack = -1;
- goto out;
+ atkbd->nak = 1;
+ clear_bit(ATKBD_FLAG_ACK, &atkbd->flags);
+ wake_up_interruptible(&atkbd->wait);
+ break;
}
+ goto out;
+ }
+
+ if (test_bit(ATKBD_FLAG_CMD, &atkbd->flags)) {
+
+ if (atkbd->cmdcnt)
+ atkbd->cmdbuf[--atkbd->cmdcnt] = code;
- if (atkbd->cmdcnt) {
- atkbd->cmdbuf[--atkbd->cmdcnt] = code;
+ if (test_and_clear_bit(ATKBD_FLAG_CMD1, &atkbd->flags) && atkbd->cmdcnt)
+ wake_up_interruptible(&atkbd->wait);
+
+ if (!atkbd->cmdcnt) {
+ clear_bit(ATKBD_FLAG_CMD, &atkbd->flags);
+ wake_up_interruptible(&atkbd->wait);
+ }
goto out;
}
- if (!atkbd->enabled)
+ if (!test_bit(ATKBD_FLAG_ENABLED, &atkbd->flags))
goto out;
+ input_event(&atkbd->dev, EV_MSC, MSC_RAW, code);
+
if (atkbd->translated) {
if (atkbd->emul ||
@@ -270,6 +326,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
switch (code) {
case ATKBD_RET_BAT:
+ clear_bit(ATKBD_FLAG_ENABLED, &atkbd->flags);
serio_rescan(atkbd->serio);
goto out;
case ATKBD_RET_EMUL0:
@@ -300,6 +357,9 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
code |= (atkbd->set != 3) ? 0x80 : 0x100;
}
+ if (atkbd->keycode[code] != ATKBD_KEY_NULL)
+ input_event(&atkbd->dev, EV_MSC, MSC_SCAN, code);
+
switch (atkbd->keycode[code]) {
case ATKBD_KEY_NULL:
break;
@@ -372,83 +432,147 @@ out:
* acknowledge. It doesn't handle resends according to the keyboard
* protocol specs, because if these are needed, the keyboard needs
* replacement anyway, and they only make a mess in the protocol.
+ *
+ * atkbd_sendbyte() can only be called from a process context
*/
static int atkbd_sendbyte(struct atkbd *atkbd, unsigned char byte)
{
- int timeout = 20000; /* 200 msec */
- atkbd->ack = 0;
-
#ifdef ATKBD_DEBUG
printk(KERN_DEBUG "atkbd.c: Sent: %02x\n", byte);
#endif
- if (serio_write(atkbd->serio, byte))
- return -1;
+ atkbd->nak = 1;
+ set_bit(ATKBD_FLAG_ACK, &atkbd->flags);
- while (!atkbd->ack && timeout--) udelay(10);
+ if (serio_write(atkbd->serio, byte) == 0)
+ wait_event_interruptible_timeout(atkbd->wait,
+ !test_bit(ATKBD_FLAG_ACK, &atkbd->flags),
+ msecs_to_jiffies(200));
- return -(atkbd->ack <= 0);
+ clear_bit(ATKBD_FLAG_ACK, &atkbd->flags);
+ return -atkbd->nak;
}
/*
* atkbd_command() sends a command, and its parameters to the keyboard,
* then waits for the response and puts it in the param array.
+ *
+ * atkbd_command() can only be called from a process context
*/
static int atkbd_command(struct atkbd *atkbd, unsigned char *param, int command)
{
- int timeout = 500000; /* 500 msec */
+ int timeout;
int send = (command >> 12) & 0xf;
int receive = (command >> 8) & 0xf;
+ int rc = -1;
int i;
- atkbd->cmdcnt = receive;
+ timeout = msecs_to_jiffies(command == ATKBD_CMD_RESET_BAT ? 4000 : 500);
- if (command == ATKBD_CMD_RESET_BAT)
- timeout = 2000000; /* 2 sec */
+ down(&atkbd->cmd_sem);
+ clear_bit(ATKBD_FLAG_CMD, &atkbd->flags);
if (receive && param)
for (i = 0; i < receive; i++)
atkbd->cmdbuf[(receive - 1) - i] = param[i];
+ atkbd->cmdcnt = receive;
+
if (command & 0xff)
if (atkbd_sendbyte(atkbd, command & 0xff))
- return (atkbd->cmdcnt = 0) - 1;
+ goto out;
for (i = 0; i < send; i++)
if (atkbd_sendbyte(atkbd, param[i]))
- return (atkbd->cmdcnt = 0) - 1;
-
- while (atkbd->cmdcnt && timeout--) {
-
- if (atkbd->cmdcnt == 1 &&
- command == ATKBD_CMD_RESET_BAT && timeout > 100000)
- timeout = 100000;
+ goto out;
- if (atkbd->cmdcnt == 1 && command == ATKBD_CMD_GETID &&
- atkbd->cmdbuf[1] != 0xab && atkbd->cmdbuf[1] != 0xac) {
+ timeout = wait_event_interruptible_timeout(atkbd->wait,
+ !test_bit(ATKBD_FLAG_CMD1, &atkbd->flags), timeout);
+
+ if (atkbd->cmdcnt && timeout > 0) {
+ if (command == ATKBD_CMD_RESET_BAT && jiffies_to_msecs(timeout) > 100)
+ timeout = msecs_to_jiffies(100);
+
+ if (command == ATKBD_CMD_GETID &&
+ atkbd->cmdbuf[receive - 1] != 0xab && atkbd->cmdbuf[receive - 1] != 0xac) {
+ /*
+ * Device behind the port is not a keyboard
+ * so we don't need to wait for the 2nd byte
+ * of ID response.
+ */
+ clear_bit(ATKBD_FLAG_CMD, &atkbd->flags);
atkbd->cmdcnt = 0;
- break;
}
- udelay(1);
+ wait_event_interruptible_timeout(atkbd->wait,
+ !test_bit(ATKBD_FLAG_CMD, &atkbd->flags), timeout);
}
if (param)
for (i = 0; i < receive; i++)
param[i] = atkbd->cmdbuf[(receive - 1) - i];
- if (command == ATKBD_CMD_RESET_BAT && atkbd->cmdcnt == 1)
- atkbd->cmdcnt = 0;
+ if (atkbd->cmdcnt && (command != ATKBD_CMD_RESET_BAT || atkbd->cmdcnt != 1))
+ goto out;
+
+ rc = 0;
+
+out:
+ clear_bit(ATKBD_FLAG_CMD, &atkbd->flags);
+ clear_bit(ATKBD_FLAG_CMD1, &atkbd->flags);
+ up(&atkbd->cmd_sem);
+
+ return rc;
+}
+
+/*
+ * atkbd_execute_scheduled_command() sends a command, previously scheduled by
+ * atkbd_schedule_command(), to the keyboard.
+ */
+
+static void atkbd_execute_scheduled_command(void *data)
+{
+ struct atkbd_work *atkbd_work = data;
+
+ atkbd_command(atkbd_work->atkbd, atkbd_work->param, atkbd_work->command);
+
+ kfree(atkbd_work);
+}
+
+/*
+ * atkbd_schedule_command() allows to schedule delayed execution of a keyboard
+ * command and can be used to issue a command from an interrupt or softirq
+ * context.
+ */
+
+static int atkbd_schedule_command(struct atkbd *atkbd, unsigned char *param, int command)
+{
+ struct atkbd_work *atkbd_work;
+ int send = (command >> 12) & 0xf;
+ int receive = (command >> 8) & 0xf;
+
+ if (!test_bit(ATKBD_FLAG_ENABLED, &atkbd->flags))
+ return -1;
+
+ if (!(atkbd_work = kmalloc(sizeof(struct atkbd_work) + max(send, receive), GFP_ATOMIC)))
+ return -1;
+
+ memset(atkbd_work, 0, sizeof(struct atkbd_work));
+ atkbd_work->atkbd = atkbd;
+ atkbd_work->command = command;
+ memcpy(atkbd_work->param, param, send);
+ INIT_WORK(&atkbd_work->work, atkbd_execute_scheduled_command, atkbd_work);
- if (atkbd->cmdcnt) {
- atkbd->cmdcnt = 0;
+ if (!schedule_work(&atkbd_work->work)) {
+ kfree(atkbd_work);
return -1;
}
return 0;
}
+
/*
* Event callback from the input module. Events that change the state of
* the hardware are processed here.
@@ -475,7 +599,7 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co
param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0)
| (test_bit(LED_NUML, dev->led) ? 2 : 0)
| (test_bit(LED_CAPSL, dev->led) ? 4 : 0);
- atkbd_command(atkbd, param, ATKBD_CMD_SETLEDS);
+ atkbd_schedule_command(atkbd, param, ATKBD_CMD_SETLEDS);
if (atkbd->extra) {
param[0] = 0;
@@ -484,7 +608,7 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co
| (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0)
| (test_bit(LED_MISC, dev->led) ? 0x10 : 0)
| (test_bit(LED_MUTE, dev->led) ? 0x20 : 0);
- atkbd_command(atkbd, param, ATKBD_CMD_EX_SETLEDS);
+ atkbd_schedule_command(atkbd, param, ATKBD_CMD_EX_SETLEDS);
}
return 0;
@@ -500,7 +624,7 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co
dev->rep[REP_PERIOD] = period[i];
dev->rep[REP_DELAY] = delay[j];
param[0] = i | (j << 5);
- atkbd_command(atkbd, param, ATKBD_CMD_SETREP);
+ atkbd_schedule_command(atkbd, param, ATKBD_CMD_SETREP);
return 0;
}
@@ -672,6 +796,11 @@ static void atkbd_cleanup(struct serio *serio)
static void atkbd_disconnect(struct serio *serio)
{
struct atkbd *atkbd = serio->private;
+
+ clear_bit(ATKBD_FLAG_ENABLED, &atkbd->flags);
+ synchronize_kernel();
+ flush_scheduled_work();
+
input_unregister_device(&atkbd->dev);
serio_close(serio);
kfree(atkbd);
@@ -684,7 +813,7 @@ static void atkbd_disconnect(struct serio *serio)
* to the input module.
*/
-static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
+static void atkbd_connect(struct serio *serio, struct serio_driver *drv)
{
struct atkbd *atkbd;
int i;
@@ -693,6 +822,9 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
return;
memset(atkbd, 0, sizeof(struct atkbd));
+ init_MUTEX(&atkbd->cmd_sem);
+ init_waitqueue_head(&atkbd->wait);
+
switch (serio->type & SERIO_TYPE) {
case SERIO_8042_XL:
@@ -709,17 +841,22 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
return;
}
+ if (!atkbd->write)
+ atkbd_softrepeat = 1;
+ if (atkbd_softrepeat)
+ atkbd_softraw = 1;
+
if (atkbd->write) {
- atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
+ atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP) | BIT(EV_MSC);
atkbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
- } else atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ } else atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_MSC);
+ atkbd->dev.mscbit[0] = atkbd_softraw ? BIT(MSC_SCAN) : BIT(MSC_RAW) | BIT(MSC_SCAN);
if (!atkbd_softrepeat) {
atkbd->dev.rep[REP_DELAY] = 250;
atkbd->dev.rep[REP_PERIOD] = 33;
- }
+ } else atkbd_softraw = 1;
- atkbd->ack = 1;
atkbd->serio = serio;
init_input_dev(&atkbd->dev);
@@ -732,7 +869,7 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
serio->private = atkbd;
- if (serio_open(serio, dev)) {
+ if (serio_open(serio, drv)) {
kfree(atkbd);
return;
}
@@ -754,8 +891,6 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
atkbd->id = 0xab00;
}
- atkbd->enabled = 1;
-
if (atkbd->extra) {
atkbd->dev.ledbit[0] |= BIT(LED_COMPOSE) | BIT(LED_SUSPEND) | BIT(LED_SLEEP) | BIT(LED_MUTE) | BIT(LED_MISC);
sprintf(atkbd->name, "AT Set 2 Extra keyboard");
@@ -797,6 +932,8 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
input_register_device(&atkbd->dev);
+ set_bit(ATKBD_FLAG_ENABLED, &atkbd->flags);
+
printk(KERN_INFO "input: %s on %s\n", atkbd->name, serio->phys);
}
@@ -808,10 +945,10 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
static int atkbd_reconnect(struct serio *serio)
{
struct atkbd *atkbd = serio->private;
- struct serio_dev *dev = serio->dev;
+ struct serio_driver *drv = serio->drv;
unsigned char param[1];
- if (!dev) {
+ if (!drv) {
printk(KERN_DEBUG "atkbd: reconnect request, but serio is disconnected, ignoring...\n");
return -1;
}
@@ -832,26 +969,32 @@ static int atkbd_reconnect(struct serio *serio)
return -1;
}
+ set_bit(ATKBD_FLAG_ENABLED, &atkbd->flags);
+
return 0;
}
-static struct serio_dev atkbd_dev = {
- .interrupt = atkbd_interrupt,
- .connect = atkbd_connect,
- .reconnect = atkbd_reconnect,
- .disconnect = atkbd_disconnect,
- .cleanup = atkbd_cleanup,
+static struct serio_driver atkbd_drv = {
+ .driver = {
+ .name = "atkbd",
+ },
+ .description = DRIVER_DESC,
+ .interrupt = atkbd_interrupt,
+ .connect = atkbd_connect,
+ .reconnect = atkbd_reconnect,
+ .disconnect = atkbd_disconnect,
+ .cleanup = atkbd_cleanup,
};
int __init atkbd_init(void)
{
- serio_register_device(&atkbd_dev);
+ serio_register_driver(&atkbd_drv);
return 0;
}
void __exit atkbd_exit(void)
{
- serio_unregister_device(&atkbd_dev);
+ serio_unregister_driver(&atkbd_drv);
}
module_init(atkbd_init);
diff --git a/drivers/input/keyboard/lkkbd.c b/drivers/input/keyboard/lkkbd.c
index 8dc51e2966e4..a04cf3efd3c3 100644
--- a/drivers/input/keyboard/lkkbd.c
+++ b/drivers/input/keyboard/lkkbd.c
@@ -76,8 +76,10 @@
#include <linux/serio.h>
#include <linux/workqueue.h>
+#define DRIVER_DESC "LK keyboard driver"
+
MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
-MODULE_DESCRIPTION ("LK keyboard driver");
+MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_LICENSE ("GPL");
/*
@@ -622,7 +624,7 @@ lkkbd_reinit (void *data)
* lkkbd_connect() probes for a LK keyboard and fills the necessary structures.
*/
static void
-lkkbd_connect (struct serio *serio, struct serio_dev *dev)
+lkkbd_connect (struct serio *serio, struct serio_driver *drv)
{
struct lkkbd *lk;
int i;
@@ -665,7 +667,7 @@ lkkbd_connect (struct serio *serio, struct serio_dev *dev)
serio->private = lk;
- if (serio_open (serio, dev)) {
+ if (serio_open (serio, drv)) {
kfree (lk);
return;
}
@@ -703,10 +705,14 @@ lkkbd_disconnect (struct serio *serio)
kfree (lk);
}
-static struct serio_dev lkkbd_dev = {
- .connect = lkkbd_connect,
- .disconnect = lkkbd_disconnect,
- .interrupt = lkkbd_interrupt,
+static struct serio_driver lkkbd_drv = {
+ .driver = {
+ .name = "lkkbd",
+ },
+ .description = DRIVER_DESC,
+ .connect = lkkbd_connect,
+ .disconnect = lkkbd_disconnect,
+ .interrupt = lkkbd_interrupt,
};
/*
@@ -715,14 +721,14 @@ static struct serio_dev lkkbd_dev = {
int __init
lkkbd_init (void)
{
- serio_register_device (&lkkbd_dev);
+ serio_register_driver(&lkkbd_drv);
return 0;
}
void __exit
lkkbd_exit (void)
{
- serio_unregister_device (&lkkbd_dev);
+ serio_unregister_driver(&lkkbd_drv);
}
module_init (lkkbd_init);
diff --git a/drivers/input/keyboard/newtonkbd.c b/drivers/input/keyboard/newtonkbd.c
index dc47acb0a4f7..caa92dd8bbe4 100644
--- a/drivers/input/keyboard/newtonkbd.c
+++ b/drivers/input/keyboard/newtonkbd.c
@@ -32,8 +32,10 @@
#include <linux/init.h>
#include <linux/serio.h>
+#define DRIVER_DESC "Newton keyboard driver"
+
MODULE_AUTHOR("Justin Cormack <j.cormack@doc.ic.ac.uk>");
-MODULE_DESCRIPTION("Newton keyboard driver");
+MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
#define NKBD_KEY 0x7f
@@ -82,7 +84,7 @@ irqreturn_t nkbd_interrupt(struct serio *serio,
}
-void nkbd_connect(struct serio *serio, struct serio_dev *dev)
+void nkbd_connect(struct serio *serio, struct serio_driver *drv)
{
struct nkbd *nkbd;
int i;
@@ -106,7 +108,7 @@ void nkbd_connect(struct serio *serio, struct serio_dev *dev)
nkbd->dev.private = nkbd;
serio->private = nkbd;
- if (serio_open(serio, dev)) {
+ if (serio_open(serio, drv)) {
kfree(nkbd);
return;
}
@@ -138,21 +140,25 @@ void nkbd_disconnect(struct serio *serio)
kfree(nkbd);
}
-struct serio_dev nkbd_dev = {
- .interrupt = nkbd_interrupt,
- .connect = nkbd_connect,
- .disconnect = nkbd_disconnect
+struct serio_driver nkbd_drv = {
+ .driver = {
+ .name = "newtonkbd",
+ },
+ .description = DRIVER_DESC,
+ .interrupt = nkbd_interrupt,
+ .connect = nkbd_connect,
+ .disconnect = nkbd_disconnect,
};
int __init nkbd_init(void)
{
- serio_register_device(&nkbd_dev);
+ serio_register_driver(&nkbd_drv);
return 0;
}
void __exit nkbd_exit(void)
{
- serio_unregister_device(&nkbd_dev);
+ serio_unregister_driver(&nkbd_drv);
}
module_init(nkbd_init);
diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c
index c281e726b6bb..7f0342c56a23 100644
--- a/drivers/input/keyboard/sunkbd.c
+++ b/drivers/input/keyboard/sunkbd.c
@@ -37,8 +37,10 @@
#include <linux/serio.h>
#include <linux/workqueue.h>
+#define DRIVER_DESC "Sun keyboard driver"
+
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
-MODULE_DESCRIPTION("Sun keyboard driver");
+MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
static unsigned char sunkbd_keycode[128] = {
@@ -221,7 +223,7 @@ static void sunkbd_reinit(void *data)
* sunkbd_connect() probes for a Sun keyboard and fills the necessary structures.
*/
-static void sunkbd_connect(struct serio *serio, struct serio_dev *dev)
+static void sunkbd_connect(struct serio *serio, struct serio_driver *drv)
{
struct sunkbd *sunkbd;
int i;
@@ -257,7 +259,7 @@ static void sunkbd_connect(struct serio *serio, struct serio_dev *dev)
serio->private = sunkbd;
- if (serio_open(serio, dev)) {
+ if (serio_open(serio, drv)) {
kfree(sunkbd);
return;
}
@@ -301,10 +303,14 @@ static void sunkbd_disconnect(struct serio *serio)
kfree(sunkbd);
}
-static struct serio_dev sunkbd_dev = {
- .interrupt = sunkbd_interrupt,
- .connect = sunkbd_connect,
- .disconnect = sunkbd_disconnect
+static struct serio_driver sunkbd_drv = {
+ .driver = {
+ .name = "sunkbd",
+ },
+ .description = DRIVER_DESC,
+ .interrupt = sunkbd_interrupt,
+ .connect = sunkbd_connect,
+ .disconnect = sunkbd_disconnect,
};
/*
@@ -313,13 +319,13 @@ static struct serio_dev sunkbd_dev = {
int __init sunkbd_init(void)
{
- serio_register_device(&sunkbd_dev);
+ serio_register_driver(&sunkbd_drv);
return 0;
}
void __exit sunkbd_exit(void)
{
- serio_unregister_device(&sunkbd_dev);
+ serio_unregister_driver(&sunkbd_drv);
}
module_init(sunkbd_init);
diff --git a/drivers/input/keyboard/xtkbd.c b/drivers/input/keyboard/xtkbd.c
index 56adfa1cceb8..eb8a9b72be0b 100644
--- a/drivers/input/keyboard/xtkbd.c
+++ b/drivers/input/keyboard/xtkbd.c
@@ -34,8 +34,10 @@
#include <linux/init.h>
#include <linux/serio.h>
+#define DRIVER_DESC "XT keyboard driver"
+
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
-MODULE_DESCRIPTION("XT keyboard driver");
+MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
#define XTKBD_EMUL0 0xe0
@@ -86,7 +88,7 @@ irqreturn_t xtkbd_interrupt(struct serio *serio,
return IRQ_HANDLED;
}
-void xtkbd_connect(struct serio *serio, struct serio_dev *dev)
+void xtkbd_connect(struct serio *serio, struct serio_driver *drv)
{
struct xtkbd *xtkbd;
int i;
@@ -111,7 +113,7 @@ void xtkbd_connect(struct serio *serio, struct serio_dev *dev)
serio->private = xtkbd;
- if (serio_open(serio, dev)) {
+ if (serio_open(serio, drv)) {
kfree(xtkbd);
return;
}
@@ -143,21 +145,25 @@ void xtkbd_disconnect(struct serio *serio)
kfree(xtkbd);
}
-struct serio_dev xtkbd_dev = {
- .interrupt = xtkbd_interrupt,
- .connect = xtkbd_connect,
- .disconnect = xtkbd_disconnect
+struct serio_driver xtkbd_drv = {
+ .driver = {
+ .name = "xtkbd",
+ },
+ .description = DRIVER_DESC,
+ .interrupt = xtkbd_interrupt,
+ .connect = xtkbd_connect,
+ .disconnect = xtkbd_disconnect,
};
int __init xtkbd_init(void)
{
- serio_register_device(&xtkbd_dev);
+ serio_register_driver(&xtkbd_drv);
return 0;
}
void __exit xtkbd_exit(void)
{
- serio_unregister_device(&xtkbd_dev);
+ serio_unregister_driver(&xtkbd_drv);
}
module_init(xtkbd_init);
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index d3e5d841ee69..a89050e12bbf 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -14,7 +14,7 @@ config INPUT_MISC
config INPUT_PCSPKR
tristate "PC Speaker support"
- depends on (ALPHA || X86 || X86_64 || MIPS) && INPUT && INPUT_MISC
+ depends on (ALPHA || X86 || X86_64 || MIPS || PPC_PREP || PPC_CHRP || PPC_PSERIES) && INPUT && INPUT_MISC
help
Say Y here if you want the standard PC Speaker to be used for
bells and whistles.
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index b86acf04d9e5..477579b9ee51 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -279,6 +279,9 @@ static unsigned int uinput_poll(struct file *file, poll_table *wait)
{
struct uinput_device *udev = file->private_data;
+ if (!test_bit(UIST_CREATED, &(udev->state)))
+ return 0;
+
poll_wait(file, &udev->waitq, wait);
if (udev->head != udev->tail)
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 1274cb68b754..bd2744fa2962 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -30,8 +30,6 @@ config MOUSE_PS2
and a new verion of GPM at:
http://www.geocities.com/dt_or/gpm/gpm.html
to take advantage of the advanced features of the touchpad.
- If you do not want install specialized drivers but want tapping
- working please use option psmouse.proto=imps.
If unsure, say Y.
diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c
index 0063af0379d6..6af1455fd97b 100644
--- a/drivers/input/mouse/logips2pp.c
+++ b/drivers/input/mouse/logips2pp.c
@@ -277,7 +277,7 @@ int ps2pp_init(struct psmouse *psmouse, int set_properties)
protocol = PSMOUSE_PS2TPP;
}
- } else if (get_model_info(model) != NULL) {
+ } else if (model_info != NULL) {
param[0] = param[1] = param[2] = 0;
ps2pp_cmd(psmouse, param, 0x39); /* Magic knock */
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 0b382672407b..7fbd926ddae7 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -22,8 +22,10 @@
#include "synaptics.h"
#include "logips2pp.h"
+#define DRIVER_DESC "PS/2 mouse driver"
+
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
-MODULE_DESCRIPTION("PS/2 mouse driver");
+MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
static char *psmouse_proto;
@@ -142,37 +144,67 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
printk(KERN_WARNING "psmouse.c: bad data from KBC -%s%s\n",
flags & SERIO_TIMEOUT ? " timeout" : "",
flags & SERIO_PARITY ? " bad parity" : "");
- if (psmouse->acking) {
- psmouse->ack = -1;
- psmouse->acking = 0;
- }
- psmouse->pktcnt = 0;
+ psmouse->nak = 1;
+ clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags);
+ clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
+ wake_up_interruptible(&psmouse->wait);
goto out;
}
- if (psmouse->acking) {
+ if (test_bit(PSMOUSE_FLAG_ACK, &psmouse->flags)) {
switch (data) {
case PSMOUSE_RET_ACK:
- psmouse->ack = 1;
+ psmouse->nak = 0;
break;
+
case PSMOUSE_RET_NAK:
- psmouse->ack = -1;
+ psmouse->nak = 1;
break;
+
+ /*
+ * Workaround for mice which don't ACK the Get ID command.
+ * These are valid mouse IDs that we recognize.
+ */
+ case 0x00:
+ case 0x03:
+ case 0x04:
+ if (test_bit(PSMOUSE_FLAG_WAITID, &psmouse->flags)) {
+ psmouse->nak = 0;
+ break;
+ }
+ /* Fall through */
default:
- psmouse->ack = 1; /* Workaround for mice which don't ACK the Get ID command */
- if (psmouse->cmdcnt)
- psmouse->cmdbuf[--psmouse->cmdcnt] = data;
- break;
+ goto out;
}
- psmouse->acking = 0;
- goto out;
+
+ if (!psmouse->nak && psmouse->cmdcnt) {
+ set_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
+ set_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags);
+ }
+ clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags);
+ wake_up_interruptible(&psmouse->wait);
+
+ if (data == PSMOUSE_RET_ACK || data == PSMOUSE_RET_NAK)
+ goto out;
}
- if (psmouse->cmdcnt) {
- psmouse->cmdbuf[--psmouse->cmdcnt] = data;
+ if (test_bit(PSMOUSE_FLAG_CMD, &psmouse->flags)) {
+ if (psmouse->cmdcnt)
+ psmouse->cmdbuf[--psmouse->cmdcnt] = data;
+
+ if (test_and_clear_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags) && psmouse->cmdcnt)
+ wake_up_interruptible(&psmouse->wait);
+
+ if (!psmouse->cmdcnt) {
+ clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
+ wake_up_interruptible(&psmouse->wait);
+ }
goto out;
}
+ if (psmouse->state == PSMOUSE_INITIALIZING)
+ goto out;
+
if (psmouse->state == PSMOUSE_ACTIVATED &&
psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) {
printk(KERN_WARNING "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n",
@@ -238,78 +270,96 @@ out:
* psmouse_sendbyte() sends a byte to the mouse, and waits for acknowledge.
* It doesn't handle retransmission, though it could - because when there would
* be need for retransmissions, the mouse has to be replaced anyway.
+ *
+ * psmouse_sendbyte() can only be called from a process context
*/
static int psmouse_sendbyte(struct psmouse *psmouse, unsigned char byte)
{
- int timeout = 10000; /* 100 msec */
- psmouse->ack = 0;
- psmouse->acking = 1;
-
- if (serio_write(psmouse->serio, byte)) {
- psmouse->acking = 0;
- return -1;
- }
+ psmouse->nak = 1;
+ set_bit(PSMOUSE_FLAG_ACK, &psmouse->flags);
- while (!psmouse->ack && timeout--) udelay(10);
+ if (serio_write(psmouse->serio, byte) == 0)
+ wait_event_interruptible_timeout(psmouse->wait,
+ !test_bit(PSMOUSE_FLAG_ACK, &psmouse->flags),
+ msecs_to_jiffies(200));
- return -(psmouse->ack <= 0);
+ clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags);
+ return -psmouse->nak;
}
/*
* psmouse_command() sends a command and its parameters to the mouse,
* then waits for the response and puts it in the param array.
+ *
+ * psmouse_command() can only be called from a process context
*/
int psmouse_command(struct psmouse *psmouse, unsigned char *param, int command)
{
- int timeout = 500000; /* 500 msec */
+ int timeout;
int send = (command >> 12) & 0xf;
int receive = (command >> 8) & 0xf;
+ int rc = -1;
int i;
- psmouse->cmdcnt = receive;
+ timeout = msecs_to_jiffies(command == PSMOUSE_CMD_RESET_BAT ? 4000 : 500);
- if (command == PSMOUSE_CMD_RESET_BAT)
- timeout = 4000000; /* 4 sec */
+ clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
+ if (command == PSMOUSE_CMD_GETID)
+ set_bit(PSMOUSE_FLAG_WAITID, &psmouse->flags);
- /* initialize cmdbuf with preset values from param */
- if (receive)
- for (i = 0; i < receive; i++)
- psmouse->cmdbuf[(receive - 1) - i] = param[i];
+ if (receive && param)
+ for (i = 0; i < receive; i++)
+ psmouse->cmdbuf[(receive - 1) - i] = param[i];
+
+ psmouse->cmdcnt = receive;
if (command & 0xff)
if (psmouse_sendbyte(psmouse, command & 0xff))
- return (psmouse->cmdcnt = 0) - 1;
+ goto out;
for (i = 0; i < send; i++)
if (psmouse_sendbyte(psmouse, param[i]))
- return (psmouse->cmdcnt = 0) - 1;
-
- while (psmouse->cmdcnt && timeout--) {
-
- if (psmouse->cmdcnt == 1 && command == PSMOUSE_CMD_RESET_BAT &&
- timeout > 100000) /* do not run in a endless loop */
- timeout = 100000; /* 1 sec */
+ goto out;
- if (psmouse->cmdcnt == 1 && command == PSMOUSE_CMD_GETID &&
- psmouse->cmdbuf[1] != 0xab && psmouse->cmdbuf[1] != 0xac) {
+ timeout = wait_event_interruptible_timeout(psmouse->wait,
+ !test_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags), timeout);
+
+ if (psmouse->cmdcnt && timeout > 0) {
+ if (command == PSMOUSE_CMD_RESET_BAT && jiffies_to_msecs(timeout) > 100)
+ timeout = msecs_to_jiffies(100);
+
+ if (command == PSMOUSE_CMD_GETID &&
+ psmouse->cmdbuf[receive - 1] != 0xab && psmouse->cmdbuf[receive - 1] != 0xac) {
+ /*
+ * Device behind the port is not a keyboard
+ * so we don't need to wait for the 2nd byte
+ * of ID response.
+ */
+ clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
psmouse->cmdcnt = 0;
- break;
}
- udelay(1);
+ wait_event_interruptible_timeout(psmouse->wait,
+ !test_bit(PSMOUSE_FLAG_CMD, &psmouse->flags), timeout);
}
- for (i = 0; i < receive; i++)
- param[i] = psmouse->cmdbuf[(receive - 1) - i];
+ if (param)
+ for (i = 0; i < receive; i++)
+ param[i] = psmouse->cmdbuf[(receive - 1) - i];
- if (psmouse->cmdcnt)
- return (psmouse->cmdcnt = 0) - 1;
+ if (psmouse->cmdcnt && (command != PSMOUSE_CMD_RESET_BAT || psmouse->cmdcnt != 1))
+ goto out;
- return 0;
-}
+ rc = 0;
+out:
+ clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
+ clear_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags);
+ clear_bit(PSMOUSE_FLAG_WAITID, &psmouse->flags);
+ return rc;
+}
/*
* psmouse_sliced_command() sends an extended PS/2 command to the mouse
@@ -394,6 +444,8 @@ static int im_explorer_detect(struct psmouse *psmouse)
{
unsigned char param[2];
+ intellimouse_detect(psmouse);
+
param[0] = 200;
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE);
param[0] = 200;
@@ -598,6 +650,21 @@ static void psmouse_initialize(struct psmouse *psmouse)
}
/*
+ * psmouse_set_state() sets new psmouse state and resets all flags and
+ * counters while holding serio lock so fighting with interrupt handler
+ * is not a concern.
+ */
+
+static void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
+{
+ serio_pause_rx(psmouse->serio);
+ psmouse->state = new_state;
+ psmouse->pktcnt = psmouse->cmdcnt = psmouse->out_of_sync = 0;
+ psmouse->flags = 0;
+ serio_continue_rx(psmouse->serio);
+}
+
+/*
* psmouse_activate() enables the mouse so that we get motion reports from it.
*/
@@ -606,9 +673,24 @@ static void psmouse_activate(struct psmouse *psmouse)
if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE))
printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", psmouse->serio->phys);
- psmouse->state = PSMOUSE_ACTIVATED;
+ psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
+}
+
+
+/*
+ * psmouse_deactivate() puts the mouse into poll mode so that we don't get motion
+ * reports from it unless we explicitely request it.
+ */
+
+static void psmouse_deactivate(struct psmouse *psmouse)
+{
+ if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_DISABLE))
+ printk(KERN_WARNING "psmouse.c: Failed to deactivate mouse on %s\n", psmouse->serio->phys);
+
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
}
+
/*
* psmouse_cleanup() resets the mouse into power-on state.
*/
@@ -626,22 +708,21 @@ static void psmouse_cleanup(struct serio *serio)
static void psmouse_disconnect(struct serio *serio)
{
- struct psmouse *psmouse = serio->private;
+ struct psmouse *psmouse, *parent;
- psmouse->state = PSMOUSE_CMD_MODE;
+ psmouse = serio->private;
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
- if (psmouse->ptport) {
- if (psmouse->ptport->deactivate)
- psmouse->ptport->deactivate(psmouse);
- __serio_unregister_port(&psmouse->ptport->serio); /* we have serio_sem */
- kfree(psmouse->ptport);
- psmouse->ptport = NULL;
+ if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) {
+ parent = serio->parent->private;
+ if (parent->pt_deactivate)
+ parent->pt_deactivate(parent);
}
if (psmouse->disconnect)
psmouse->disconnect(psmouse);
- psmouse->state = PSMOUSE_IGNORE;
+ psmouse_set_state(psmouse, PSMOUSE_IGNORE);
input_unregister_device(&psmouse->dev);
serio_close(serio);
@@ -652,39 +733,49 @@ static void psmouse_disconnect(struct serio *serio)
* psmouse_connect() is a callback from the serio module when
* an unhandled serio port is found.
*/
-static void psmouse_connect(struct serio *serio, struct serio_dev *dev)
+static void psmouse_connect(struct serio *serio, struct serio_driver *drv)
{
- struct psmouse *psmouse;
+ struct psmouse *psmouse, *parent = NULL;
if ((serio->type & SERIO_TYPE) != SERIO_8042 &&
(serio->type & SERIO_TYPE) != SERIO_PS_PSTHRU)
return;
+ /*
+ * If this is a pass-through port deactivate parent so the device
+ * connected to this port can be successfully identified
+ */
+ if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) {
+ parent = serio->parent->private;
+ psmouse_deactivate(parent);
+ }
+
if (!(psmouse = kmalloc(sizeof(struct psmouse), GFP_KERNEL)))
- return;
+ goto out;
memset(psmouse, 0, sizeof(struct psmouse));
+ init_waitqueue_head(&psmouse->wait);
init_input_dev(&psmouse->dev);
psmouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
psmouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
- psmouse->state = PSMOUSE_CMD_MODE;
psmouse->serio = serio;
psmouse->dev.private = psmouse;
+ psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
serio->private = psmouse;
- if (serio_open(serio, dev)) {
+ if (serio_open(serio, drv)) {
kfree(psmouse);
serio->private = NULL;
- return;
+ goto out;
}
if (psmouse_probe(psmouse) < 0) {
serio_close(serio);
kfree(psmouse);
serio->private = NULL;
- return;
+ goto out;
}
psmouse->type = psmouse_extensions(psmouse, psmouse_max_proto, 1);
@@ -711,63 +802,88 @@ static void psmouse_connect(struct serio *serio, struct serio_dev *dev)
printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys);
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+
psmouse_initialize(psmouse);
- if (psmouse->ptport) {
- printk(KERN_INFO "serio: %s port at %s\n", psmouse->ptport->serio.name, psmouse->phys);
- __serio_register_port(&psmouse->ptport->serio); /* we have serio_sem */
- if (psmouse->ptport->activate)
- psmouse->ptport->activate(psmouse);
+ if (parent && parent->pt_activate)
+ parent->pt_activate(parent);
+
+ if (serio->child) {
+ /*
+ * Nothing to be done here, serio core will detect that
+ * the driver set serio->child and will register it for us.
+ */
+ printk(KERN_INFO "serio: %s port at %s\n", serio->child->name, psmouse->phys);
}
psmouse_activate(psmouse);
+
+out:
+ /* If this is a pass-through port the parent awaits to be activated */
+ if (parent)
+ psmouse_activate(parent);
}
static int psmouse_reconnect(struct serio *serio)
{
struct psmouse *psmouse = serio->private;
- struct serio_dev *dev = serio->dev;
+ struct psmouse *parent = NULL;
+ struct serio_driver *drv = serio->drv;
+ int rc = -1;
- if (!dev || !psmouse) {
+ if (!drv || !psmouse) {
printk(KERN_DEBUG "psmouse: reconnect request, but serio is disconnected, ignoring...\n");
return -1;
}
- psmouse->state = PSMOUSE_CMD_MODE;
- psmouse->acking = psmouse->cmdcnt = psmouse->pktcnt = psmouse->out_of_sync = 0;
+ if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) {
+ parent = serio->parent->private;
+ psmouse_deactivate(parent);
+ }
+
+ psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+
if (psmouse->reconnect) {
if (psmouse->reconnect(psmouse))
- return -1;
+ goto out;
} else if (psmouse_probe(psmouse) < 0 ||
psmouse->type != psmouse_extensions(psmouse, psmouse_max_proto, 0))
- return -1;
+ goto out;
/* ok, the device type (and capabilities) match the old one,
* we can continue using it, complete intialization
*/
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+
psmouse_initialize(psmouse);
- if (psmouse->ptport) {
- if (psmouse_reconnect(&psmouse->ptport->serio)) {
- __serio_unregister_port(&psmouse->ptport->serio);
- __serio_register_port(&psmouse->ptport->serio);
- if (psmouse->ptport->activate)
- psmouse->ptport->activate(psmouse);
- }
- }
+ if (parent && parent->pt_activate)
+ parent->pt_activate(parent);
psmouse_activate(psmouse);
- return 0;
+ rc = 0;
+
+out:
+ /* If this is a pass-through port the parent waits to be activated */
+ if (parent)
+ psmouse_activate(parent);
+
+ return rc;
}
-static struct serio_dev psmouse_dev = {
- .interrupt = psmouse_interrupt,
- .connect = psmouse_connect,
- .reconnect = psmouse_reconnect,
- .disconnect = psmouse_disconnect,
- .cleanup = psmouse_cleanup,
+static struct serio_driver psmouse_drv = {
+ .driver = {
+ .name = "psmouse",
+ },
+ .description = DRIVER_DESC,
+ .interrupt = psmouse_interrupt,
+ .connect = psmouse_connect,
+ .reconnect = psmouse_reconnect,
+ .disconnect = psmouse_disconnect,
+ .cleanup = psmouse_cleanup,
};
static inline void psmouse_parse_proto(void)
@@ -787,13 +903,13 @@ static inline void psmouse_parse_proto(void)
int __init psmouse_init(void)
{
psmouse_parse_proto();
- serio_register_device(&psmouse_dev);
+ serio_register_driver(&psmouse_drv);
return 0;
}
void __exit psmouse_exit(void)
{
- serio_unregister_device(&psmouse_dev);
+ serio_unregister_driver(&psmouse_drv);
}
module_init(psmouse_init);
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index 47fbc48f66e5..20bbab42d591 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -9,6 +9,7 @@
#define PSMOUSE_CMD_GETID 0x02f2
#define PSMOUSE_CMD_SETRATE 0x10f3
#define PSMOUSE_CMD_ENABLE 0x00f4
+#define PSMOUSE_CMD_DISABLE 0x00f5
#define PSMOUSE_CMD_RESET_DIS 0x00f6
#define PSMOUSE_CMD_RESET_BAT 0x02ff
@@ -17,10 +18,17 @@
#define PSMOUSE_RET_ACK 0xfa
#define PSMOUSE_RET_NAK 0xfe
-/* psmouse states */
-#define PSMOUSE_CMD_MODE 0
-#define PSMOUSE_ACTIVATED 1
-#define PSMOUSE_IGNORE 2
+#define PSMOUSE_FLAG_ACK 0 /* Waiting for ACK/NAK */
+#define PSMOUSE_FLAG_CMD 1 /* Waiting for command to finish */
+#define PSMOUSE_FLAG_CMD1 2 /* Waiting for the first byte of command response */
+#define PSMOUSE_FLAG_WAITID 3 /* Command execiting is GET ID */
+
+enum psmouse_state {
+ PSMOUSE_IGNORE,
+ PSMOUSE_INITIALIZING,
+ PSMOUSE_CMD_MODE,
+ PSMOUSE_ACTIVATED,
+};
/* psmouse protocol handler return codes */
typedef enum {
@@ -29,20 +37,10 @@ typedef enum {
PSMOUSE_FULL_PACKET
} psmouse_ret_t;
-struct psmouse;
-
-struct psmouse_ptport {
- struct serio serio;
-
- void (*activate)(struct psmouse *parent);
- void (*deactivate)(struct psmouse *parent);
-};
-
struct psmouse {
void *private;
struct input_dev dev;
struct serio *serio;
- struct psmouse_ptport *ptport;
char *vendor;
char *name;
unsigned char cmdbuf[8];
@@ -53,16 +51,22 @@ struct psmouse {
unsigned char model;
unsigned long last;
unsigned long out_of_sync;
- unsigned char state;
- char acking;
- volatile char ack;
+ enum psmouse_state state;
+ unsigned char nak;
char error;
char devname[64];
char phys[32];
+ unsigned long flags;
- psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse, struct pt_regs *regs);
+ /* Used to signal completion from interrupt handler */
+ wait_queue_head_t wait;
+
+ psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse, struct pt_regs *regs);
int (*reconnect)(struct psmouse *psmouse);
void (*disconnect)(struct psmouse *psmouse);
+
+ void (*pt_activate)(struct psmouse *psmouse);
+ void (*pt_deactivate)(struct psmouse *psmouse);
};
#define PSMOUSE_PS2 1
diff --git a/drivers/input/mouse/sermouse.c b/drivers/input/mouse/sermouse.c
index 9c97a0a40c4d..e1db6e078c0b 100644
--- a/drivers/input/mouse/sermouse.c
+++ b/drivers/input/mouse/sermouse.c
@@ -37,8 +37,10 @@
#include <linux/serio.h>
#include <linux/init.h>
+#define DRIVER_DESC "Serial mouse driver"
+
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
-MODULE_DESCRIPTION("Serial mouse driver");
+MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
static char *sermouse_protocols[] = { "None", "Mouse Systems Mouse", "Sun Mouse", "Microsoft Mouse",
@@ -237,7 +239,7 @@ static void sermouse_disconnect(struct serio *serio)
* an unhandled serio port is found.
*/
-static void sermouse_connect(struct serio *serio, struct serio_dev *dev)
+static void sermouse_connect(struct serio *serio, struct serio_driver *drv)
{
struct sermouse *sermouse;
unsigned char c;
@@ -279,7 +281,7 @@ static void sermouse_connect(struct serio *serio, struct serio_dev *dev)
sermouse->dev.id.product = c;
sermouse->dev.id.version = 0x0100;
- if (serio_open(serio, dev)) {
+ if (serio_open(serio, drv)) {
kfree(sermouse);
return;
}
@@ -289,21 +291,25 @@ static void sermouse_connect(struct serio *serio, struct serio_dev *dev)
printk(KERN_INFO "input: %s on %s\n", sermouse_protocols[sermouse->type], serio->phys);
}
-static struct serio_dev sermouse_dev = {
- .interrupt = sermouse_interrupt,
- .connect = sermouse_connect,
- .disconnect = sermouse_disconnect
+static struct serio_driver sermouse_drv = {
+ .driver = {
+ .name = "sermouse",
+ },
+ .description = DRIVER_DESC,
+ .interrupt = sermouse_interrupt,
+ .connect = sermouse_connect,
+ .disconnect = sermouse_disconnect,
};
int __init sermouse_init(void)
{
- serio_register_device(&sermouse_dev);
+ serio_register_driver(&sermouse_drv);
return 0;
}
void __exit sermouse_exit(void)
{
- serio_unregister_device(&sermouse_dev);
+ serio_unregister_driver(&sermouse_drv);
}
module_init(sermouse_init);
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 2bd152fe02bc..101b8094720a 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -212,9 +212,9 @@ static int synaptics_set_mode(struct psmouse *psmouse, int mode)
/*****************************************************************************
* Synaptics pass-through PS/2 port support
****************************************************************************/
-static int synaptics_pt_write(struct serio *port, unsigned char c)
+static int synaptics_pt_write(struct serio *serio, unsigned char c)
{
- struct psmouse *parent = port->driver;
+ struct psmouse *parent = serio->parent->private;
char rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */
if (psmouse_sliced_command(parent, c))
@@ -233,22 +233,19 @@ static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet
{
struct psmouse *child = ptport->private;
- if (child) {
- if (child->state == PSMOUSE_ACTIVATED) {
- serio_interrupt(ptport, packet[1], 0, NULL);
- serio_interrupt(ptport, packet[4], 0, NULL);
- serio_interrupt(ptport, packet[5], 0, NULL);
- if (child->type >= PSMOUSE_GENPS)
- serio_interrupt(ptport, packet[2], 0, NULL);
- } else if (child->state != PSMOUSE_IGNORE) {
- serio_interrupt(ptport, packet[1], 0, NULL);
- }
- }
+ if (child && child->state == PSMOUSE_ACTIVATED) {
+ serio_interrupt(ptport, packet[1], 0, NULL);
+ serio_interrupt(ptport, packet[4], 0, NULL);
+ serio_interrupt(ptport, packet[5], 0, NULL);
+ if (child->type >= PSMOUSE_GENPS)
+ serio_interrupt(ptport, packet[2], 0, NULL);
+ } else
+ serio_interrupt(ptport, packet[1], 0, NULL);
}
static void synaptics_pt_activate(struct psmouse *psmouse)
{
- struct psmouse *child = psmouse->ptport->serio.private;
+ struct psmouse *child = psmouse->serio->child->private;
/* adjust the touchpad to child's choice of protocol */
if (child && child->type >= PSMOUSE_GENPS) {
@@ -259,23 +256,25 @@ static void synaptics_pt_activate(struct psmouse *psmouse)
static void synaptics_pt_create(struct psmouse *psmouse)
{
- struct psmouse_ptport *port;
+ struct serio *serio;
- psmouse->ptport = port = kmalloc(sizeof(struct psmouse_ptport), GFP_KERNEL);
- if (!port) {
+ serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!serio) {
printk(KERN_ERR "synaptics: not enough memory to allocate pass-through port\n");
return;
}
- memset(port, 0, sizeof(struct psmouse_ptport));
+ memset(serio, 0, sizeof(struct serio));
+
+ serio->type = SERIO_PS_PSTHRU;
+ strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
+ strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name));
+ serio->write = synaptics_pt_write;
+ serio->parent = psmouse->serio;
- port->serio.type = SERIO_PS_PSTHRU;
- port->serio.name = "Synaptics pass-through";
- port->serio.phys = "synaptics-pt/serio0";
- port->serio.write = synaptics_pt_write;
- port->serio.driver = psmouse;
+ psmouse->pt_activate = synaptics_pt_activate;
- port->activate = synaptics_pt_activate;
+ psmouse->serio->child = serio;
}
/*****************************************************************************
@@ -470,9 +469,10 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse, struct pt_r
if (unlikely(priv->pkt_type == SYN_NEWABS))
priv->pkt_type = synaptics_detect_pkt_type(psmouse);
- if (psmouse->ptport && psmouse->ptport->serio.dev && synaptics_is_pt_packet(psmouse->packet))
- synaptics_pass_pt_packet(&psmouse->ptport->serio, psmouse->packet);
- else
+ if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) {
+ if (psmouse->serio->child)
+ synaptics_pass_pt_packet(psmouse->serio->child, psmouse->packet);
+ } else
synaptics_process_packet(psmouse);
return PSMOUSE_FULL_PACKET;
diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c
index c26d907e8bb9..3845ca458f80 100644
--- a/drivers/input/mouse/vsxxxaa.c
+++ b/drivers/input/mouse/vsxxxaa.c
@@ -82,8 +82,10 @@
#include <linux/serio.h>
#include <linux/init.h>
+#define DRIVER_DESC "Serial DEC VSXXX-AA/GA mouse / DEC tablet driver"
+
MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
-MODULE_DESCRIPTION ("Serial DEC VSXXX-AA/GA mouse / DEC tablet driver");
+MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_LICENSE ("GPL");
#undef VSXXXAA_DEBUG
@@ -482,7 +484,7 @@ vsxxxaa_disconnect (struct serio *serio)
}
static void
-vsxxxaa_connect (struct serio *serio, struct serio_dev *dev)
+vsxxxaa_connect (struct serio *serio, struct serio_driver *drv)
{
struct vsxxxaa *mouse;
@@ -524,7 +526,7 @@ vsxxxaa_connect (struct serio *serio, struct serio_dev *dev)
mouse->dev.id.bustype = BUS_RS232;
mouse->serio = serio;
- if (serio_open (serio, dev)) {
+ if (serio_open (serio, drv)) {
kfree (mouse);
return;
}
@@ -540,23 +542,27 @@ vsxxxaa_connect (struct serio *serio, struct serio_dev *dev)
printk (KERN_INFO "input: %s on %s\n", mouse->name, mouse->phys);
}
-static struct serio_dev vsxxxaa_dev = {
- .connect = vsxxxaa_connect,
- .interrupt = vsxxxaa_interrupt,
- .disconnect = vsxxxaa_disconnect,
+static struct serio_driver vsxxxaa_drv = {
+ .driver = {
+ .name = "vsxxxaa",
+ },
+ .description = DRIVER_DESC,
+ .connect = vsxxxaa_connect,
+ .interrupt = vsxxxaa_interrupt,
+ .disconnect = vsxxxaa_disconnect,
};
int __init
vsxxxaa_init (void)
{
- serio_register_device (&vsxxxaa_dev);
+ serio_register_driver(&vsxxxaa_drv);
return 0;
}
void __exit
vsxxxaa_exit (void)
{
- serio_unregister_device (&vsxxxaa_dev);
+ serio_unregister_driver(&vsxxxaa_drv);
}
module_init (vsxxxaa_init);
diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c
index f266b7ef5513..bd1a6d98bde5 100644
--- a/drivers/input/mousedev.c
+++ b/drivers/input/mousedev.c
@@ -48,8 +48,15 @@ static int yres = CONFIG_INPUT_MOUSEDEV_SCREEN_Y;
module_param(yres, uint, 0);
MODULE_PARM_DESC(yres, "Vertical screen resolution");
-struct mousedev_motion {
+static unsigned tap_time = 200;
+module_param(tap_time, uint, 0);
+MODULE_PARM_DESC(tap_time, "Tap time for touchpads in absolute mode (msecs)");
+
+struct mousedev_hw_data {
int dx, dy, dz;
+ int x, y;
+ int abs_event;
+ unsigned long buttons;
};
struct mousedev {
@@ -61,22 +68,38 @@ struct mousedev {
struct list_head list;
struct input_handle handle;
- struct mousedev_motion packet;
- unsigned long buttons;
+ struct mousedev_hw_data packet;
unsigned int pkt_count;
int old_x[4], old_y[4];
- unsigned int touch;
+ unsigned long touch;
+};
+
+enum mousedev_emul {
+ MOUSEDEV_EMUL_PS2,
+ MOUSEDEV_EMUL_IMPS,
+ MOUSEDEV_EMUL_EXPS
};
+struct mousedev_motion {
+ int dx, dy, dz;
+ unsigned long buttons;
+};
+
+#define PACKET_QUEUE_LEN 16
struct mousedev_list {
struct fasync_struct *fasync;
struct mousedev *mousedev;
struct list_head node;
- int dx, dy, dz;
- unsigned long buttons;
+
+ struct mousedev_motion packets[PACKET_QUEUE_LEN];
+ unsigned int head, tail;
+ spinlock_t packet_lock;
+ int pos_x, pos_y;
+
signed char ps2[6];
unsigned char ready, buffer, bufsiz;
- unsigned char mode, imexseq, impsseq;
+ unsigned char imexseq, impsseq;
+ enum mousedev_emul mode;
};
#define MOUSEDEV_SEQ_LEN 6
@@ -119,15 +142,19 @@ static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev,
case ABS_X:
size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
if (size == 0) size = xres;
- mousedev->packet.dx = (value * xres - mousedev->old_x[0]) / size;
- mousedev->old_x[0] = mousedev->packet.dx * size;
+ if (value > dev->absmax[ABS_X]) value = dev->absmax[ABS_X];
+ if (value < dev->absmin[ABS_X]) value = dev->absmin[ABS_X];
+ mousedev->packet.x = ((value - dev->absmin[ABS_X]) * xres) / size;
+ mousedev->packet.abs_event = 1;
break;
case ABS_Y:
size = dev->absmax[ABS_Y] - dev->absmin[ABS_Y];
if (size == 0) size = yres;
- mousedev->packet.dy = (value * yres - mousedev->old_y[0]) / size;
- mousedev->old_y[0] = mousedev->packet.dy * size;
+ if (value > dev->absmax[ABS_Y]) value = dev->absmax[ABS_Y];
+ if (value < dev->absmin[ABS_Y]) value = dev->absmin[ABS_Y];
+ mousedev->packet.y = yres - ((value - dev->absmin[ABS_Y]) * yres) / size;
+ mousedev->packet.abs_event = 1;
break;
}
}
@@ -165,30 +192,82 @@ static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int
}
if (value) {
- set_bit(index, &mousedev->buttons);
- set_bit(index, &mousedev_mix.buttons);
+ set_bit(index, &mousedev->packet.buttons);
+ set_bit(index, &mousedev_mix.packet.buttons);
} else {
- clear_bit(index, &mousedev->buttons);
- clear_bit(index, &mousedev_mix.buttons);
+ clear_bit(index, &mousedev->packet.buttons);
+ clear_bit(index, &mousedev_mix.packet.buttons);
}
}
-static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_motion *packet)
+static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_hw_data *packet)
{
struct mousedev_list *list;
+ struct mousedev_motion *p;
+ unsigned long flags;
list_for_each_entry(list, &mousedev->list, node) {
- list->dx += packet->dx;
- list->dy += packet->dy;
- list->dz += packet->dz;
- list->buttons = mousedev->buttons;
+ spin_lock_irqsave(&list->packet_lock, flags);
+
+ p = &list->packets[list->head];
+ if (list->ready && p->buttons != packet->buttons) {
+ unsigned int new_head = (list->head + 1) % PACKET_QUEUE_LEN;
+ if (new_head != list->tail) {
+ p = &list->packets[list->head = new_head];
+ memset(p, 0, sizeof(struct mousedev_motion));
+ }
+ }
+
+ if (packet->abs_event) {
+ p->dx += packet->x - list->pos_x;
+ p->dy += packet->y - list->pos_y;
+ list->pos_x = packet->x;
+ list->pos_y = packet->y;
+ }
+
+ list->pos_x += packet->dx;
+ list->pos_x = list->pos_x < 0 ? 0 : (list->pos_x >= xres ? xres : list->pos_x);
+ list->pos_y += packet->dy;
+ list->pos_y = list->pos_y < 0 ? 0 : (list->pos_y >= yres ? yres : list->pos_y);
+
+ p->dx += packet->dx;
+ p->dy += packet->dy;
+ p->dz += packet->dz;
+ p->buttons = mousedev->packet.buttons;
+
list->ready = 1;
+
+ spin_unlock_irqrestore(&list->packet_lock, flags);
kill_fasync(&list->fasync, SIGIO, POLL_IN);
}
wake_up_interruptible(&mousedev->wait);
}
+static void mousedev_touchpad_touch(struct mousedev *mousedev, int value)
+{
+ if (!value) {
+ if (mousedev->touch &&
+ time_before(jiffies, mousedev->touch + msecs_to_jiffies(tap_time))) {
+ /*
+ * Toggle left button to emulate tap.
+ * We rely on the fact that mousedev_mix always has 0
+ * motion packet so we won't mess current position.
+ */
+ set_bit(0, &mousedev->packet.buttons);
+ set_bit(0, &mousedev_mix.packet.buttons);
+ mousedev_notify_readers(mousedev, &mousedev_mix.packet);
+ mousedev_notify_readers(&mousedev_mix, &mousedev_mix.packet);
+ clear_bit(0, &mousedev->packet.buttons);
+ clear_bit(0, &mousedev_mix.packet.buttons);
+ }
+ mousedev->touch = mousedev->pkt_count = 0;
+ }
+ else
+ if (!mousedev->touch)
+ mousedev->touch = jiffies;
+}
+
static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
struct mousedev *mousedev = handle->private;
@@ -212,12 +291,8 @@ static void mousedev_event(struct input_handle *handle, unsigned int type, unsig
case EV_KEY:
if (value != 2) {
- if (code == BTN_TOUCH && test_bit(BTN_TOOL_FINGER, handle->dev->keybit)) {
- /* Handle touchpad data */
- mousedev->touch = value;
- if (!mousedev->touch)
- mousedev->pkt_count = 0;
- }
+ if (code == BTN_TOUCH && test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
+ mousedev_touchpad_touch(mousedev, value);
else
mousedev_key_event(mousedev, code, value);
}
@@ -237,7 +312,8 @@ static void mousedev_event(struct input_handle *handle, unsigned int type, unsig
mousedev_notify_readers(mousedev, &mousedev->packet);
mousedev_notify_readers(&mousedev_mix, &mousedev->packet);
- memset(&mousedev->packet, 0, sizeof(struct mousedev_motion));
+ mousedev->packet.dx = mousedev->packet.dy = mousedev->packet.dz = 0;
+ mousedev->packet.abs_event = 0;
}
break;
}
@@ -322,6 +398,9 @@ static int mousedev_open(struct inode * inode, struct file * file)
return -ENOMEM;
memset(list, 0, sizeof(struct mousedev_list));
+ spin_lock_init(&list->packet_lock);
+ list->pos_x = xres / 2;
+ list->pos_y = yres / 2;
list->mousedev = mousedev_table[i];
list_add_tail(&list->node, &mousedev_table[i]->list);
file->private_data = list;
@@ -341,32 +420,56 @@ static int mousedev_open(struct inode * inode, struct file * file)
return 0;
}
-static void mousedev_packet(struct mousedev_list *list, unsigned char off)
+static inline int mousedev_limit_delta(int delta, int limit)
{
- list->ps2[off] = 0x08 | ((list->dx < 0) << 4) | ((list->dy < 0) << 5) | (list->buttons & 0x07);
- list->ps2[off + 1] = (list->dx > 127 ? 127 : (list->dx < -127 ? -127 : list->dx));
- list->ps2[off + 2] = (list->dy > 127 ? 127 : (list->dy < -127 ? -127 : list->dy));
- list->dx -= list->ps2[off + 1];
- list->dy -= list->ps2[off + 2];
- list->bufsiz = off + 3;
-
- if (list->mode == 2) {
- list->ps2[off + 3] = (list->dz > 7 ? 7 : (list->dz < -7 ? -7 : list->dz));
- list->dz -= list->ps2[off + 3];
- list->ps2[off + 3] = (list->ps2[off + 3] & 0x0f) | ((list->buttons & 0x18) << 1);
- list->bufsiz++;
- } else {
- list->ps2[off] |= ((list->buttons & 0x10) >> 3) | ((list->buttons & 0x08) >> 1);
+ return delta > limit ? limit : (delta < -limit ? -limit : delta);
+}
+
+static void mousedev_packet(struct mousedev_list *list, signed char *ps2_data)
+{
+ struct mousedev_motion *p;
+ unsigned long flags;
+
+ spin_lock_irqsave(&list->packet_lock, flags);
+ p = &list->packets[list->tail];
+
+ ps2_data[0] = 0x08 | ((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07);
+ ps2_data[1] = mousedev_limit_delta(p->dx, 127);
+ ps2_data[2] = mousedev_limit_delta(p->dy, 127);
+ p->dx -= ps2_data[1];
+ p->dy -= ps2_data[2];
+
+ switch (list->mode) {
+ case MOUSEDEV_EMUL_EXPS:
+ ps2_data[3] = mousedev_limit_delta(p->dz, 127);
+ p->dz -= ps2_data[3];
+ ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1);
+ list->bufsiz = 4;
+ break;
+
+ case MOUSEDEV_EMUL_IMPS:
+ ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
+ ps2_data[3] = mousedev_limit_delta(p->dz, 127);
+ p->dz -= ps2_data[3];
+ list->bufsiz = 4;
+ break;
+
+ case MOUSEDEV_EMUL_PS2:
+ default:
+ ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
+ p->dz = 0;
+ list->bufsiz = 3;
+ break;
}
- if (list->mode == 1) {
- list->ps2[off + 3] = (list->dz > 127 ? 127 : (list->dz < -127 ? -127 : list->dz));
- list->dz -= list->ps2[off + 3];
- list->bufsiz++;
+ if (!p->dx && !p->dy && !p->dz) {
+ if (list->tail != list->head)
+ list->tail = (list->tail + 1) % PACKET_QUEUE_LEN;
+ if (list->tail == list->head)
+ list->ready = 0;
}
- if (!list->dx && !list->dy && (!list->mode || !list->dz)) list->ready = 0;
- list->buffer = list->bufsiz;
+ spin_unlock_irqrestore(&list->packet_lock, flags);
}
@@ -384,31 +487,31 @@ static ssize_t mousedev_write(struct file * file, const char __user * buffer, si
if (c == mousedev_imex_seq[list->imexseq]) {
if (++list->imexseq == MOUSEDEV_SEQ_LEN) {
list->imexseq = 0;
- list->mode = 2;
+ list->mode = MOUSEDEV_EMUL_EXPS;
}
} else list->imexseq = 0;
if (c == mousedev_imps_seq[list->impsseq]) {
if (++list->impsseq == MOUSEDEV_SEQ_LEN) {
list->impsseq = 0;
- list->mode = 1;
+ list->mode = MOUSEDEV_EMUL_IMPS;
}
} else list->impsseq = 0;
list->ps2[0] = 0xfa;
- list->bufsiz = 1;
switch (c) {
case 0xeb: /* Poll */
- mousedev_packet(list, 1);
+ mousedev_packet(list, &list->ps2[1]);
+ list->bufsiz++; /* account for leading ACK */
break;
case 0xf2: /* Get ID */
switch (list->mode) {
- case 0: list->ps2[1] = 0; break;
- case 1: list->ps2[1] = 3; break;
- case 2: list->ps2[1] = 4; break;
+ case MOUSEDEV_EMUL_PS2: list->ps2[1] = 0; break;
+ case MOUSEDEV_EMUL_IMPS: list->ps2[1] = 3; break;
+ case MOUSEDEV_EMUL_EXPS: list->ps2[1] = 4; break;
}
list->bufsiz = 2;
break;
@@ -419,13 +522,15 @@ static ssize_t mousedev_write(struct file * file, const char __user * buffer, si
break;
case 0xff: /* Reset */
- list->impsseq = 0;
- list->imexseq = 0;
- list->mode = 0;
- list->ps2[1] = 0xaa;
- list->ps2[2] = 0x00;
+ list->impsseq = list->imexseq = 0;
+ list->mode = MOUSEDEV_EMUL_PS2;
+ list->ps2[1] = 0xaa; list->ps2[2] = 0x00;
list->bufsiz = 3;
break;
+
+ default:
+ list->bufsiz = 1;
+ break;
}
list->buffer = list->bufsiz;
@@ -446,13 +551,19 @@ static ssize_t mousedev_read(struct file * file, char __user * buffer, size_t co
if (!list->ready && !list->buffer && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
- retval = wait_event_interruptible(list->mousedev->wait, list->ready || list->buffer);
+ retval = wait_event_interruptible(list->mousedev->wait,
+ !list->mousedev->exist || list->ready || list->buffer);
if (retval)
return retval;
- if (!list->buffer && list->ready)
- mousedev_packet(list, 0);
+ if (!list->mousedev->exist)
+ return -ENODEV;
+
+ if (!list->buffer && list->ready) {
+ mousedev_packet(list, list->ps2);
+ list->buffer = list->bufsiz;
+ }
if (count > list->buffer)
count = list->buffer;
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 8338e6dd6711..93761f817d0e 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -130,3 +130,19 @@ config SERIO_MACEPS2
To compile this driver as a module, choose M here: the
module will be called maceps2.
+
+config SERIO_RAW
+ tristate "Raw access to serio ports"
+ depends on SERIO
+ help
+ Say Y here if you want to have raw access to serio ports, such as
+ AUX ports on i8042 keyboard controller. Each serio port that is
+ bound to this driver will be accessible via a char device with
+ major 10 and dynamically allocated minor. The driver will try
+ allocating minor 1 (that historically corresponds to /dev/psaux)
+ first. To bind this driver to a serio port use sysfs interface:
+
+ echo -n "serio_raw" > /sys/bus/serio/devices/serioX/driver
+
+ To compile this driver as a module, choose M here: the
+ module will be called serio_raw.
diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
index a47dec2556e8..cf52b6141363 100644
--- a/drivers/input/serio/Makefile
+++ b/drivers/input/serio/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_SERIO_98KBD) += 98kbd-io.o
obj-$(CONFIG_SERIO_GSCPS2) += gscps2.o
obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o
obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o
+obj-$(CONFIG_SERIO_RAW) += serio_raw.o
diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c
index 6a780c10fc73..4b3b5135f5c1 100644
--- a/drivers/input/serio/ambakmi.c
+++ b/drivers/input/serio/ambakmi.c
@@ -29,7 +29,7 @@
#define KMI_BASE (kmi->base)
struct amba_kmi_port {
- struct serio io;
+ struct serio *io;
struct clk *clk;
unsigned char *base;
unsigned int irq;
@@ -44,7 +44,7 @@ static irqreturn_t amba_kmi_int(int irq, void *dev_id, struct pt_regs *regs)
int handled = IRQ_NONE;
while (status & KMIIR_RXINTR) {
- serio_interrupt(&kmi->io, readb(KMIDATA), 0, regs);
+ serio_interrupt(kmi->io, readb(KMIDATA), 0, regs);
status = readb(KMIIR);
handled = IRQ_HANDLED;
}
@@ -54,7 +54,7 @@ static irqreturn_t amba_kmi_int(int irq, void *dev_id, struct pt_regs *regs)
static int amba_kmi_write(struct serio *io, unsigned char val)
{
- struct amba_kmi_port *kmi = io->driver;
+ struct amba_kmi_port *kmi = io->port_data;
unsigned int timeleft = 10000; /* timeout in 100ms */
while ((readb(KMISTAT) & KMISTAT_TXEMPTY) == 0 && timeleft--)
@@ -68,7 +68,7 @@ static int amba_kmi_write(struct serio *io, unsigned char val)
static int amba_kmi_open(struct serio *io)
{
- struct amba_kmi_port *kmi = io->driver;
+ struct amba_kmi_port *kmi = io->port_data;
unsigned int divisor;
int ret;
@@ -105,7 +105,7 @@ static int amba_kmi_open(struct serio *io)
static void amba_kmi_close(struct serio *io)
{
- struct amba_kmi_port *kmi = io->driver;
+ struct amba_kmi_port *kmi = io->port_data;
writeb(0, KMICR);
@@ -117,6 +117,7 @@ static void amba_kmi_close(struct serio *io)
static int amba_kmi_probe(struct amba_device *dev, void *id)
{
struct amba_kmi_port *kmi;
+ struct serio *io;
int ret;
ret = amba_request_regions(dev, NULL);
@@ -124,21 +125,25 @@ static int amba_kmi_probe(struct amba_device *dev, void *id)
return ret;
kmi = kmalloc(sizeof(struct amba_kmi_port), GFP_KERNEL);
- if (!kmi) {
+ io = kmalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!kmi || !io) {
ret = -ENOMEM;
goto out;
}
memset(kmi, 0, sizeof(struct amba_kmi_port));
-
- kmi->io.type = SERIO_8042;
- kmi->io.write = amba_kmi_write;
- kmi->io.open = amba_kmi_open;
- kmi->io.close = amba_kmi_close;
- kmi->io.name = dev->dev.bus_id;
- kmi->io.phys = dev->dev.bus_id;
- kmi->io.driver = kmi;
-
+ memset(io, 0, sizeof(struct serio));
+
+ io->type = SERIO_8042;
+ io->write = amba_kmi_write;
+ io->open = amba_kmi_open;
+ io->close = amba_kmi_close;
+ strlcpy(io->name, dev->dev.bus_id, sizeof(io->name));
+ strlcpy(io->phys, dev->dev.bus_id, sizeof(io->phys));
+ io->port_data = kmi;
+ io->dev.parent = &dev->dev;
+
+ kmi->io = io;
kmi->base = ioremap(dev->res.start, KMI_SIZE);
if (!kmi->base) {
ret = -ENOMEM;
@@ -154,13 +159,14 @@ static int amba_kmi_probe(struct amba_device *dev, void *id)
kmi->irq = dev->irq[0];
amba_set_drvdata(dev, kmi);
- serio_register_port(&kmi->io);
+ serio_register_port(kmi->io);
return 0;
unmap:
iounmap(kmi->base);
out:
kfree(kmi);
+ kfree(io);
amba_release_regions(dev);
return ret;
}
@@ -171,7 +177,7 @@ static int amba_kmi_remove(struct amba_device *dev)
amba_set_drvdata(dev, NULL);
- serio_unregister_port(&kmi->io);
+ serio_unregister_port(kmi->io);
clk_put(kmi->clk);
iounmap(kmi->base);
kfree(kmi);
@@ -184,7 +190,7 @@ static int amba_kmi_resume(struct amba_device *dev)
struct amba_kmi_port *kmi = amba_get_drvdata(dev);
/* kick the serio layer to rescan this port */
- serio_rescan(&kmi->io);
+ serio_reconnect(kmi->io);
return 0;
}
@@ -214,7 +220,7 @@ static int __init amba_kmi_init(void)
static void __exit amba_kmi_exit(void)
{
- return amba_driver_unregister(&ambakmi_driver);
+ amba_driver_unregister(&ambakmi_driver);
}
module_init(amba_kmi_init);
diff --git a/drivers/input/serio/ct82c710.c b/drivers/input/serio/ct82c710.c
index 2234ff39dca1..ee785460b78f 100644
--- a/drivers/input/serio/ct82c710.c
+++ b/drivers/input/serio/ct82c710.c
@@ -36,6 +36,7 @@
#include <linux/interrupt.h>
#include <linux/serio.h>
#include <linux/errno.h>
+#include <linux/err.h>
#include <asm/io.h>
@@ -43,9 +44,6 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("82C710 C&T mouse port chip driver");
MODULE_LICENSE("GPL");
-static char ct82c710_name[] = "C&T 82c710 mouse port";
-static char ct82c710_phys[16];
-
/*
* ct82c710 interface
*/
@@ -61,10 +59,22 @@ static char ct82c710_phys[16];
#define CT82C710_IRQ 12
-static int ct82c710_data;
-static int ct82c710_status;
+#define CT82C710_DATA ct82c710_iores.start
+#define CT82C710_STATUS (ct82c710_iores.start + 1)
+
+static struct serio *ct82c710_port;
+static struct platform_device *ct82c710_device;
+static struct resource ct82c710_iores;
+
+/*
+ * Interrupt handler for the 82C710 mouse port. A character
+ * is waiting in the 82C710.
+ */
-static irqreturn_t ct82c710_interrupt(int cpl, void *dev_id, struct pt_regs * regs);
+static irqreturn_t ct82c710_interrupt(int cpl, void *dev_id, struct pt_regs * regs)
+{
+ return serio_interrupt(ct82c710_port, inb(CT82C710_DATA), 0, regs);
+}
/*
* Wait for device to send output char and flush any input char.
@@ -74,10 +84,10 @@ static int ct82c170_wait(void)
{
int timeout = 60000;
- while ((inb(ct82c710_status) & (CT82C710_RX_FULL | CT82C710_TX_IDLE | CT82C710_DEV_IDLE))
+ while ((inb(CT82C710_STATUS) & (CT82C710_RX_FULL | CT82C710_TX_IDLE | CT82C710_DEV_IDLE))
!= (CT82C710_DEV_IDLE | CT82C710_TX_IDLE) && timeout) {
- if (inb_p(ct82c710_status) & CT82C710_RX_FULL) inb_p(ct82c710_data);
+ if (inb_p(CT82C710_STATUS) & CT82C710_RX_FULL) inb_p(CT82C710_DATA);
udelay(1);
timeout--;
@@ -91,7 +101,7 @@ static void ct82c710_close(struct serio *serio)
if (ct82c170_wait())
printk(KERN_WARNING "ct82c710.c: Device busy in close()\n");
- outb_p(inb_p(ct82c710_status) & ~(CT82C710_ENABLE | CT82C710_INTS_ON), ct82c710_status);
+ outb_p(inb_p(CT82C710_STATUS) & ~(CT82C710_ENABLE | CT82C710_INTS_ON), CT82C710_STATUS);
if (ct82c170_wait())
printk(KERN_WARNING "ct82c710.c: Device busy in close()\n");
@@ -106,21 +116,21 @@ static int ct82c710_open(struct serio *serio)
if (request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL))
return -1;
- status = inb_p(ct82c710_status);
+ status = inb_p(CT82C710_STATUS);
status |= (CT82C710_ENABLE | CT82C710_RESET);
- outb_p(status, ct82c710_status);
+ outb_p(status, CT82C710_STATUS);
status &= ~(CT82C710_RESET);
- outb_p(status, ct82c710_status);
+ outb_p(status, CT82C710_STATUS);
status |= CT82C710_INTS_ON;
- outb_p(status, ct82c710_status); /* Enable interrupts */
+ outb_p(status, CT82C710_STATUS); /* Enable interrupts */
while (ct82c170_wait()) {
printk(KERN_ERR "ct82c710: Device busy in open()\n");
status &= ~(CT82C710_ENABLE | CT82C710_INTS_ON);
- outb_p(status, ct82c710_status);
+ outb_p(status, CT82C710_STATUS);
free_irq(CT82C710_IRQ, NULL);
return -1;
}
@@ -135,30 +145,10 @@ static int ct82c710_open(struct serio *serio)
static int ct82c710_write(struct serio *port, unsigned char c)
{
if (ct82c170_wait()) return -1;
- outb_p(c, ct82c710_data);
+ outb_p(c, CT82C710_DATA);
return 0;
}
-static struct serio ct82c710_port =
-{
- .type = SERIO_8042,
- .name = ct82c710_name,
- .phys = ct82c710_phys,
- .write = ct82c710_write,
- .open = ct82c710_open,
- .close = ct82c710_close,
-};
-
-/*
- * Interrupt handler for the 82C710 mouse port. A character
- * is waiting in the 82C710.
- */
-
-static irqreturn_t ct82c710_interrupt(int cpl, void *dev_id, struct pt_regs * regs)
-{
- return serio_interrupt(&ct82c710_port, inb(ct82c710_data), 0, regs);
-}
-
/*
* See if we can find a 82C710 device. Read mouse address.
*/
@@ -175,36 +165,60 @@ static int __init ct82c710_probe(void)
return -1; /* No: no 82C710 here */
outb_p(0x0d, 0x390); /* Write index */
- ct82c710_data = inb_p(0x391) << 2; /* Get mouse I/O address */
- ct82c710_status = ct82c710_data + 1;
+ ct82c710_iores.start = inb_p(0x391) << 2; /* Get mouse I/O address */
+ ct82c710_iores.end = ct82c710_iores.start + 1;
+ ct82c710_iores.flags = IORESOURCE_IO;
outb_p(0x0f, 0x390);
outb_p(0x0f, 0x391); /* Close config mode */
return 0;
}
+static struct serio * __init ct82c710_allocate_port(void)
+{
+ struct serio *serio;
+
+ serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+ if (serio) {
+ memset(serio, 0, sizeof(struct serio));
+ serio->type = SERIO_8042;
+ serio->open = ct82c710_open;
+ serio->close = ct82c710_close;
+ serio->write = ct82c710_write;
+ serio->dev.parent = &ct82c710_device->dev;
+ strlcpy(serio->name, "C&T 82c710 mouse port", sizeof(serio->name));
+ snprintf(serio->phys, sizeof(serio->phys), "isa%04lx/serio0", CT82C710_DATA);
+ }
+
+ return serio;
+}
+
int __init ct82c710_init(void)
{
if (ct82c710_probe())
return -ENODEV;
- if (request_region(ct82c710_data, 2, "ct82c710"))
- return -EBUSY;
+ ct82c710_device = platform_device_register_simple("ct82c710", -1, &ct82c710_iores, 1);
+ if (IS_ERR(ct82c710_device))
+ return PTR_ERR(ct82c710_device);
- sprintf(ct82c710_phys, "isa%04x/serio0", ct82c710_data);
+ if (!(ct82c710_port = ct82c710_allocate_port())) {
+ platform_device_unregister(ct82c710_device);
+ return -ENOMEM;
+ }
- serio_register_port(&ct82c710_port);
+ serio_register_port(ct82c710_port);
- printk(KERN_INFO "serio: C&T 82c710 mouse port at %#x irq %d\n",
- ct82c710_data, CT82C710_IRQ);
+ printk(KERN_INFO "serio: C&T 82c710 mouse port at %#lx irq %d\n",
+ CT82C710_DATA, CT82C710_IRQ);
return 0;
}
void __exit ct82c710_exit(void)
{
- serio_unregister_port(&ct82c710_port);
- release_region(ct82c710_data, 2);
+ serio_unregister_port(ct82c710_port);
+ platform_device_unregister(ct82c710_device);
}
module_init(ct82c710_init);
diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c
index 58a7dec8faeb..cd9f987fb83d 100644
--- a/drivers/input/serio/gscps2.c
+++ b/drivers/input/serio/gscps2.c
@@ -91,7 +91,7 @@ static irqreturn_t gscps2_interrupt(int irq, void *dev, struct pt_regs *regs);
struct gscps2port {
struct list_head node;
struct parisc_device *padev;
- struct serio port;
+ struct serio *port;
spinlock_t lock;
char *addr;
u8 act, append; /* position in buffer[] */
@@ -100,7 +100,6 @@ struct gscps2port {
u8 str;
} buffer[BUFFER_SIZE+1];
int id;
- char name[32];
};
/*
@@ -272,7 +271,7 @@ static irqreturn_t gscps2_interrupt(int irq, void *dev, struct pt_regs *regs)
rxflags = ((status & GSC_STAT_TERR) ? SERIO_TIMEOUT : 0 ) |
((status & GSC_STAT_PERR) ? SERIO_PARITY : 0 );
- serio_interrupt(&ps2port->port, data, rxflags, regs);
+ serio_interrupt(ps2port->port, data, rxflags, regs);
} /* while() */
@@ -288,7 +287,7 @@ static irqreturn_t gscps2_interrupt(int irq, void *dev, struct pt_regs *regs)
static int gscps2_write(struct serio *port, unsigned char data)
{
- struct gscps2port *ps2port = port->driver;
+ struct gscps2port *ps2port = port->port_data;
if (!gscps2_writeb_output(ps2port, data)) {
printk(KERN_DEBUG PFX "sending byte %#x failed.\n", data);
@@ -304,7 +303,7 @@ static int gscps2_write(struct serio *port, unsigned char data)
static int gscps2_open(struct serio *port)
{
- struct gscps2port *ps2port = port->driver;
+ struct gscps2port *ps2port = port->port_data;
gscps2_reset(ps2port);
@@ -319,7 +318,7 @@ static int gscps2_open(struct serio *port)
static void gscps2_close(struct serio *port)
{
- struct gscps2port *ps2port = port->driver;
+ struct gscps2port *ps2port = port->port_data;
gscps2_enable(ps2port, DISABLE);
}
@@ -343,7 +342,8 @@ static struct serio gscps2_serio_port =
static int __init gscps2_probe(struct parisc_device *dev)
{
- struct gscps2port *ps2port;
+ struct gscps2port *ps2port;
+ struct serio *serio;
unsigned long hpa = dev->hpa;
int ret;
@@ -355,34 +355,45 @@ static int __init gscps2_probe(struct parisc_device *dev)
hpa += GSC_DINO_OFFSET;
ps2port = kmalloc(sizeof(struct gscps2port), GFP_KERNEL);
- if (!ps2port)
- return -ENOMEM;
+ serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!ps2port || !serio) {
+ ret = -ENOMEM;
+ goto fail_nomem;
+ }
dev_set_drvdata(&dev->dev, ps2port);
memset(ps2port, 0, sizeof(struct gscps2port));
+ memset(serio, 0, sizeof(struct serio));
+ ps2port->port = serio;
ps2port->padev = dev;
ps2port->addr = ioremap(hpa, GSC_STATUS + 4);
spin_lock_init(&ps2port->lock);
gscps2_reset(ps2port);
- ps2port->id = readb(ps2port->addr+GSC_ID) & 0x0f;
- snprintf(ps2port->name, sizeof(ps2port->name)-1, "%s %s",
- gscps2_serio_port.name,
- (ps2port->id == GSC_ID_KEYBOARD) ? "keyboard" : "mouse" );
-
- memcpy(&ps2port->port, &gscps2_serio_port, sizeof(gscps2_serio_port));
- ps2port->port.driver = ps2port;
- ps2port->port.name = ps2port->name;
- ps2port->port.phys = dev->dev.bus_id;
+ ps2port->id = readb(ps2port->addr + GSC_ID) & 0x0f;
+
+ snprintf(serio->name, sizeof(serio->name), "GSC PS/2 %s",
+ (ps2port->id == GSC_ID_KEYBOARD) ? "keyboard" : "mouse");
+ strlcpy(serio->phys, dev->dev.bus_id, sizeof(serio->phys));
+ serio->idbus = BUS_GSC;
+ serio->idvendor = PCI_VENDOR_ID_HP;
+ serio->idproduct = 0x0001;
+ serio->idversion = 0x0010;
+ serio->type = SERIO_8042;
+ serio->write = gscps2_write;
+ serio->open = gscps2_open;
+ serio->close = gscps2_close;
+ serio->port_data = ps2port;
+ serio->dev.parent = &dev->dev;
list_add_tail(&ps2port->node, &ps2port_list);
ret = -EBUSY;
- if (request_irq(dev->irq, gscps2_interrupt, SA_SHIRQ, ps2port->name, ps2port))
+ if (request_irq(dev->irq, gscps2_interrupt, SA_SHIRQ, ps2port->port->name, ps2port))
goto fail_miserably;
- if ( (ps2port->id != GSC_ID_KEYBOARD) && (ps2port->id != GSC_ID_MOUSE) ) {
+ if (ps2port->id != GSC_ID_KEYBOARD && ps2port->id != GSC_ID_MOUSE) {
printk(KERN_WARNING PFX "Unsupported PS/2 port at 0x%08lx (id=%d) ignored\n",
hpa, ps2port->id);
ret = -ENODEV;
@@ -395,12 +406,12 @@ static int __init gscps2_probe(struct parisc_device *dev)
#endif
printk(KERN_INFO "serio: %s port at 0x%p irq %d @ %s\n",
- ps2port->name,
+ ps2port->port->name,
ps2port->addr,
ps2port->padev->irq,
- ps2port->port.phys);
+ ps2port->port->phys);
- serio_register_port(&ps2port->port);
+ serio_register_port(ps2port->port);
return 0;
@@ -411,7 +422,10 @@ fail_miserably:
list_del(&ps2port->node);
iounmap(ps2port->addr);
release_mem_region(dev->hpa, GSC_STATUS + 4);
+
+fail_nomem:
kfree(ps2port);
+ kfree(serio);
return ret;
}
@@ -424,7 +438,7 @@ static int __devexit gscps2_remove(struct parisc_device *dev)
{
struct gscps2port *ps2port = dev_get_drvdata(&dev->dev);
- serio_unregister_port(&ps2port->port);
+ serio_unregister_port(ps2port->port);
free_irq(dev->irq, ps2port);
gscps2_flush(ps2port);
list_del(&ps2port->node);
diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h
index 2cba0884059c..b567c116a4ee 100644
--- a/drivers/input/serio/i8042-io.h
+++ b/drivers/input/serio/i8042-io.h
@@ -65,6 +65,31 @@ static inline void i8042_write_command(int val)
return;
}
+#if defined(__i386__)
+
+#include <linux/dmi.h>
+
+static struct dmi_system_id __initdata i8042_dmi_table[] = {
+ {
+ .ident = "Compaq Proliant 8500",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
+ DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "8500"),
+ },
+ },
+ {
+ .ident = "Compaq Proliant DL760",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
+ DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "DL760"),
+ },
+ },
+ { }
+};
+#endif
+
static inline int i8042_platform_init(void)
{
/*
@@ -79,6 +104,12 @@ static inline int i8042_platform_init(void)
#if !defined(__i386__) && !defined(__x86_64__)
i8042_reset = 1;
#endif
+
+#if defined(__i386__)
+ if (dmi_check_system(i8042_dmi_table))
+ i8042_noloop = 1;
+#endif
+
return 0;
}
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index e9d8dabd1da7..151c29ddb635 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -1,7 +1,7 @@
/*
* i8042 keyboard and mouse controller driver for Linux
*
- * Copyright (c) 1999-2002 Vojtech Pavlik
+ * Copyright (c) 1999-2004 Vojtech Pavlik
*/
/*
@@ -21,6 +21,7 @@
#include <linux/sysdev.h>
#include <linux/pm.h>
#include <linux/serio.h>
+#include <linux/err.h>
#include <asm/io.h>
@@ -52,6 +53,10 @@ static unsigned int i8042_dumbkbd;
module_param_named(dumbkbd, i8042_dumbkbd, bool, 0);
MODULE_PARM_DESC(dumbkbd, "Pretend that controller can only read data from keyboard");
+static unsigned int i8042_noloop;
+module_param_named(noloop, i8042_noloop, bool, 0);
+MODULE_PARM_DESC(dumbkbd, "Disable the AUX Loopback command while probing for the AUX port");
+
__obsolete_setup("i8042_noaux");
__obsolete_setup("i8042_nomux");
__obsolete_setup("i8042_unlock");
@@ -70,19 +75,35 @@ struct i8042_values {
unsigned char irqen;
unsigned char exists;
signed char mux;
- unsigned char *name;
- unsigned char *phys;
+ char name[8];
+};
+
+static struct i8042_values i8042_kbd_values = {
+ .disable = I8042_CTR_KBDDIS,
+ .irqen = I8042_CTR_KBDINT,
+ .mux = -1,
+ .name = "KBD",
+};
+
+static struct i8042_values i8042_aux_values = {
+ .disable = I8042_CTR_AUXDIS,
+ .irqen = I8042_CTR_AUXINT,
+ .mux = -1,
+ .name = "AUX",
};
-static struct serio i8042_kbd_port;
-static struct serio i8042_aux_port;
+static struct i8042_values i8042_mux_values[I8042_NUM_MUX_PORTS];
+
+static struct serio *i8042_kbd_port;
+static struct serio *i8042_aux_port;
+static struct serio *i8042_mux_port[I8042_NUM_MUX_PORTS];
static unsigned char i8042_initial_ctr;
static unsigned char i8042_ctr;
static unsigned char i8042_mux_open;
static unsigned char i8042_mux_present;
-static unsigned char i8042_sysdev_initialized;
static struct pm_dev *i8042_pm_dev;
-struct timer_list i8042_timer;
+static struct timer_list i8042_timer;
+static struct platform_device *i8042_platform_device;
/*
* Shared IRQ's require a device pointer, but this driver doesn't support
@@ -95,6 +116,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs);
/*
* The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to
* be ready for reading values from it / writing values to it.
+ * Called always with i8042_lock held.
*/
static int i8042_wait_read(void)
@@ -131,6 +153,7 @@ static int i8042_flush(void)
spin_lock_irqsave(&i8042_lock, flags);
while ((i8042_read_status() & I8042_STR_OBF) && (i++ < I8042_BUFFER_SIZE)) {
+ udelay(50);
data = i8042_read_data();
dbg("%02x <- i8042 (flush, %s)", data,
i8042_read_status() & I8042_STR_AUXDATA ? "aux" : "kbd");
@@ -154,6 +177,9 @@ static int i8042_command(unsigned char *param, int command)
unsigned long flags;
int retval = 0, i = 0;
+ if (i8042_noloop && command == I8042_CMD_AUX_LOOP)
+ return -1;
+
spin_lock_irqsave(&i8042_lock, flags);
retval = i8042_wait_write();
@@ -214,7 +240,7 @@ static int i8042_kbd_write(struct serio *port, unsigned char c)
static int i8042_aux_write(struct serio *port, unsigned char c)
{
- struct i8042_values *values = port->driver;
+ struct i8042_values *values = port->port_data;
int retval;
/*
@@ -242,7 +268,7 @@ static int i8042_aux_write(struct serio *port, unsigned char c)
static int i8042_activate_port(struct serio *port)
{
- struct i8042_values *values = port->driver;
+ struct i8042_values *values = port->port_data;
i8042_flush();
@@ -270,7 +296,7 @@ static int i8042_activate_port(struct serio *port)
static int i8042_open(struct serio *port)
{
- struct i8042_values *values = port->driver;
+ struct i8042_values *values = port->port_data;
if (values->mux != -1)
if (i8042_mux_open++)
@@ -309,7 +335,7 @@ irq_fail:
static void i8042_close(struct serio *port)
{
- struct i8042_values *values = port->driver;
+ struct i8042_values *values = port->port_data;
if (values->mux != -1)
if (--i8042_mux_open)
@@ -328,52 +354,6 @@ static void i8042_close(struct serio *port)
}
/*
- * Structures for registering the devices in the serio.c module.
- */
-
-static struct i8042_values i8042_kbd_values = {
- .irqen = I8042_CTR_KBDINT,
- .disable = I8042_CTR_KBDDIS,
- .name = "KBD",
- .mux = -1,
-};
-
-static struct serio i8042_kbd_port =
-{
- .type = SERIO_8042_XL,
- .write = i8042_kbd_write,
- .open = i8042_open,
- .close = i8042_close,
- .driver = &i8042_kbd_values,
- .name = "i8042 Kbd Port",
- .phys = I8042_KBD_PHYS_DESC,
-};
-
-static struct i8042_values i8042_aux_values = {
- .irqen = I8042_CTR_AUXINT,
- .disable = I8042_CTR_AUXDIS,
- .name = "AUX",
- .mux = -1,
-};
-
-static struct serio i8042_aux_port =
-{
- .type = SERIO_8042,
- .write = i8042_aux_write,
- .open = i8042_open,
- .close = i8042_close,
- .driver = &i8042_aux_values,
- .name = "i8042 Aux Port",
- .phys = I8042_AUX_PHYS_DESC,
-};
-
-static struct i8042_values i8042_mux_values[4];
-static struct serio i8042_mux_port[4];
-static char i8042_mux_names[4][32];
-static char i8042_mux_short[4][16];
-static char i8042_mux_phys[4][32];
-
-/*
* i8042_interrupt() is the most important function in this driver -
* it handles the interrupts from the i8042, and sends incoming bytes
* to the upper layers.
@@ -384,6 +364,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
unsigned long flags;
unsigned char str, data = 0;
unsigned int dfl;
+ unsigned int aux_idx;
int ret;
mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
@@ -400,44 +381,67 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
goto out;
}
- dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) |
- ((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0);
-
- if (i8042_mux_values[0].exists && (str & I8042_STR_AUXDATA)) {
+ if (i8042_mux_present && (str & I8042_STR_AUXDATA)) {
+ static unsigned long last_transmit;
+ static unsigned char last_str;
+ dfl = 0;
if (str & I8042_STR_MUXERR) {
+ dbg("MUX error, status is %02x, data is %02x", str, data);
switch (data) {
+ default:
+/*
+ * When MUXERR condition is signalled the data register can only contain
+ * 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately
+ * it is not always the case. Some KBC just get confused which port the
+ * data came from and signal error leaving the data intact. They _do not_
+ * revert to legacy mode (actually I've never seen KBC reverting to legacy
+ * mode yet, when we see one we'll add proper handling).
+ * Anyway, we will assume that the data came from the same serio last byte
+ * was transmitted (if transmission happened not too long ago).
+ */
+ if (time_before(jiffies, last_transmit + HZ/10)) {
+ str = last_str;
+ break;
+ }
+ /* fall through - report timeout */
case 0xfd:
- case 0xfe: dfl = SERIO_TIMEOUT; break;
- case 0xff: dfl = SERIO_PARITY; break;
+ case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break;
+ case 0xff: dfl = SERIO_PARITY; data = 0xfe; break;
}
- data = 0xfe;
- } else dfl = 0;
+ }
+
+ aux_idx = (str >> 6) & 3;
dbg("%02x <- i8042 (interrupt, aux%d, %d%s%s)",
- data, (str >> 6), irq,
+ data, aux_idx, irq,
dfl & SERIO_PARITY ? ", bad parity" : "",
dfl & SERIO_TIMEOUT ? ", timeout" : "");
- serio_interrupt(i8042_mux_port + ((str >> 6) & 3), data, dfl, regs);
+ if (likely(i8042_mux_values[aux_idx].exists))
+ serio_interrupt(i8042_mux_port[aux_idx], data, dfl, regs);
+ last_str = str;
+ last_transmit = jiffies;
goto irq_ret;
}
+ dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) |
+ ((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0);
+
dbg("%02x <- i8042 (interrupt, %s, %d%s%s)",
data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", irq,
dfl & SERIO_PARITY ? ", bad parity" : "",
dfl & SERIO_TIMEOUT ? ", timeout" : "");
- if (i8042_aux_values.exists && (str & I8042_STR_AUXDATA)) {
- serio_interrupt(&i8042_aux_port, data, dfl, regs);
- goto irq_ret;
- }
-
- if (!i8042_kbd_values.exists)
- goto irq_ret;
- serio_interrupt(&i8042_kbd_port, data, dfl, regs);
+ if (str & I8042_STR_AUXDATA) {
+ if (likely(i8042_aux_values.exists))
+ serio_interrupt(i8042_aux_port, data, dfl, regs);
+ } else {
+ if (likely(i8042_kbd_values.exists))
+ serio_interrupt(i8042_kbd_port, data, dfl, regs);
+ }
irq_ret:
ret = 1;
@@ -474,17 +478,8 @@ static int i8042_enable_mux_mode(struct i8042_values *values, unsigned char *mux
if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != 0xa9)
return -1;
param = 0xa4;
- if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param == 0x5b) {
-
-/*
- * Do another loop test with the 0x5a value. Doing anything else upsets
- * Profusion/ServerWorks OSB4 chipsets.
- */
-
- param = 0x5a;
- i8042_command(&param, I8042_CMD_AUX_LOOP);
+ if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param == 0x5b)
return -1;
- }
if (mux_version)
*mux_version = ~param;
@@ -639,8 +634,10 @@ static int __init i8042_check_aux(struct i8042_values *values)
* registers it, and reports to the user.
*/
-static int __init i8042_port_register(struct i8042_values *values, struct serio *port)
+static int __init i8042_port_register(struct serio *port)
{
+ struct i8042_values *values = port->port_data;
+
values->exists = 1;
i8042_ctr &= ~values->disable;
@@ -677,6 +674,7 @@ static void i8042_timer_func(unsigned long data)
static int i8042_controller_init(void)
{
+ unsigned long flags;
/*
* Test the i8042. We need to know if it thinks it's working correctly
@@ -723,12 +721,14 @@ static int i8042_controller_init(void)
* Handle keylock.
*/
+ spin_lock_irqsave(&i8042_lock, flags);
if (~i8042_read_status() & I8042_STR_KEYLOCK) {
if (i8042_unlock)
i8042_ctr |= I8042_CTR_IGNKEYLOCK;
else
printk(KERN_WARNING "i8042.c: Warning: Keylock active.\n");
}
+ spin_unlock_irqrestore(&i8042_lock, flags);
/*
* If the chip is configured into nontranslated mode by the BIOS, don't
@@ -745,10 +745,8 @@ static int i8042_controller_init(void)
* BIOSes.
*/
- if (i8042_direct) {
+ if (i8042_direct)
i8042_ctr &= ~I8042_CTR_XLATE;
- i8042_kbd_port.type = SERIO_8042;
- }
/*
* Write CTR back.
@@ -802,14 +800,14 @@ void i8042_controller_cleanup(void)
*/
if (i8042_kbd_values.exists)
- serio_cleanup(&i8042_kbd_port);
+ serio_cleanup(i8042_kbd_port);
if (i8042_aux_values.exists)
- serio_cleanup(&i8042_aux_port);
+ serio_cleanup(i8042_aux_port);
- for (i = 0; i < 4; i++)
+ for (i = 0; i < I8042_NUM_MUX_PORTS; i++)
if (i8042_mux_values[i].exists)
- serio_cleanup(i8042_mux_port + i);
+ serio_cleanup(i8042_mux_port[i]);
i8042_controller_reset();
}
@@ -851,15 +849,15 @@ static int i8042_controller_resume(void)
* Reconnect anything that was connected to the ports.
*/
- if (i8042_kbd_values.exists && i8042_activate_port(&i8042_kbd_port) == 0)
- serio_reconnect(&i8042_kbd_port);
+ if (i8042_kbd_values.exists && i8042_activate_port(i8042_kbd_port) == 0)
+ serio_reconnect(i8042_kbd_port);
- if (i8042_aux_values.exists && i8042_activate_port(&i8042_aux_port) == 0)
- serio_reconnect(&i8042_aux_port);
+ if (i8042_aux_values.exists && i8042_activate_port(i8042_aux_port) == 0)
+ serio_reconnect(i8042_aux_port);
- for (i = 0; i < 4; i++)
- if (i8042_mux_values[i].exists && i8042_activate_port(i8042_mux_port + i) == 0)
- serio_reconnect(i8042_mux_port + i);
+ for (i = 0; i < I8042_NUM_MUX_PORTS; i++)
+ if (i8042_mux_values[i].exists && i8042_activate_port(i8042_mux_port[i]) == 0)
+ serio_reconnect(i8042_mux_port[i]);
/*
* Restart timer (for polling "stuck" data)
*/
@@ -882,7 +880,7 @@ static int i8042_notify_sys(struct notifier_block *this, unsigned long code,
return NOTIFY_DONE;
}
-static struct notifier_block i8042_notifier=
+static struct notifier_block i8042_notifier =
{
i8042_notify_sys,
NULL,
@@ -892,25 +890,27 @@ static struct notifier_block i8042_notifier=
/*
* Suspend/resume handlers for the new PM scheme (driver model)
*/
-static int i8042_suspend(struct sys_device *dev, u32 state)
+static int i8042_suspend(struct device *dev, u32 state, u32 level)
{
- return i8042_controller_suspend();
+ return level == SUSPEND_DISABLE ? i8042_controller_suspend() : 0;
}
-static int i8042_resume(struct sys_device *dev)
+static int i8042_resume(struct device *dev, u32 level)
{
- return i8042_controller_resume();
+ return level == RESUME_ENABLE ? i8042_controller_resume() : 0;
}
-static struct sysdev_class kbc_sysclass = {
- set_kset_name("i8042"),
- .suspend = i8042_suspend,
- .resume = i8042_resume,
-};
+static void i8042_shutdown(struct device *dev)
+{
+ i8042_controller_cleanup();
+}
-static struct sys_device device_i8042 = {
- .id = 0,
- .cls = &kbc_sysclass,
+static struct device_driver i8042_driver = {
+ .name = "i8042",
+ .bus = &platform_bus_type,
+ .suspend = i8042_suspend,
+ .resume = i8042_resume,
+ .shutdown = i8042_shutdown,
};
/*
@@ -929,23 +929,75 @@ static int i8042_pm_callback(struct pm_dev *dev, pm_request_t request, void *dum
return 0;
}
-static void __init i8042_init_mux_values(struct i8042_values *values, struct serio *port, int index)
+static struct serio * __init i8042_allocate_kbd_port(void)
+{
+ struct serio *serio;
+
+ serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+ if (serio) {
+ memset(serio, 0, sizeof(struct serio));
+ serio->type = i8042_direct ? SERIO_8042 : SERIO_8042_XL,
+ serio->write = i8042_dumbkbd ? NULL : i8042_kbd_write,
+ serio->open = i8042_open,
+ serio->close = i8042_close,
+ serio->port_data = &i8042_kbd_values,
+ serio->dev.parent = &i8042_platform_device->dev;
+ strlcpy(serio->name, "i8042 Kbd Port", sizeof(serio->name));
+ strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys));
+ }
+
+ return serio;
+}
+
+static struct serio * __init i8042_allocate_aux_port(void)
{
- memcpy(port, &i8042_aux_port, sizeof(struct serio));
- memcpy(values, &i8042_aux_values, sizeof(struct i8042_values));
- sprintf(i8042_mux_names[index], "i8042 Aux-%d Port", index);
- sprintf(i8042_mux_phys[index], I8042_MUX_PHYS_DESC, index + 1);
- sprintf(i8042_mux_short[index], "AUX%d", index);
- port->name = i8042_mux_names[index];
- port->phys = i8042_mux_phys[index];
- port->driver = values;
- values->name = i8042_mux_short[index];
- values->mux = index;
+ struct serio *serio;
+
+ serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+ if (serio) {
+ memset(serio, 0, sizeof(struct serio));
+ serio->type = SERIO_8042;
+ serio->write = i8042_aux_write;
+ serio->open = i8042_open;
+ serio->close = i8042_close;
+ serio->port_data = &i8042_aux_values,
+ serio->dev.parent = &i8042_platform_device->dev;
+ strlcpy(serio->name, "i8042 Aux Port", sizeof(serio->name));
+ strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys));
+ }
+
+ return serio;
+}
+
+static struct serio * __init i8042_allocate_mux_port(int index)
+{
+ struct serio *serio;
+ struct i8042_values *values = &i8042_mux_values[index];
+
+ serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+ if (serio) {
+ *values = i8042_aux_values;
+ snprintf(values->name, sizeof(values->name), "AUX%d", index);
+ values->mux = index;
+
+ memset(serio, 0, sizeof(struct serio));
+ serio->type = SERIO_8042;
+ serio->write = i8042_aux_write;
+ serio->open = i8042_open;
+ serio->close = i8042_close;
+ serio->port_data = values;
+ serio->dev.parent = &i8042_platform_device->dev;
+ snprintf(serio->name, sizeof(serio->name), "i8042 Aux-%d Port", index);
+ snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, index + 1);
+ }
+
+ return serio;
}
int __init i8042_init(void)
{
int i;
+ int err;
dbg_init();
@@ -961,30 +1013,36 @@ int __init i8042_init(void)
if (i8042_controller_init())
return -ENODEV;
- if (i8042_dumbkbd)
- i8042_kbd_port.write = NULL;
+ err = driver_register(&i8042_driver);
+ if (err)
+ return err;
+
+ i8042_platform_device = platform_device_register_simple("i8042", -1, NULL, 0);
+ if (IS_ERR(i8042_platform_device)) {
+ driver_unregister(&i8042_driver);
+ return PTR_ERR(i8042_platform_device);
+ }
if (!i8042_noaux && !i8042_check_aux(&i8042_aux_values)) {
if (!i8042_nomux && !i8042_check_mux(&i8042_aux_values))
- for (i = 0; i < 4; i++) {
- i8042_init_mux_values(i8042_mux_values + i, i8042_mux_port + i, i);
- i8042_port_register(i8042_mux_values + i, i8042_mux_port + i);
+ for (i = 0; i < I8042_NUM_MUX_PORTS; i++) {
+ i8042_mux_port[i] = i8042_allocate_mux_port(i);
+ if (i8042_mux_port[i])
+ i8042_port_register(i8042_mux_port[i]);
}
- else
- i8042_port_register(&i8042_aux_values, &i8042_aux_port);
+ else {
+ i8042_aux_port = i8042_allocate_aux_port();
+ if (i8042_aux_port)
+ i8042_port_register(i8042_aux_port);
+ }
}
- i8042_port_register(&i8042_kbd_values, &i8042_kbd_port);
+ i8042_kbd_port = i8042_allocate_kbd_port();
+ if (i8042_kbd_port)
+ i8042_port_register(i8042_kbd_port);
mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
- if (sysdev_class_register(&kbc_sysclass) == 0) {
- if (sysdev_register(&device_i8042) == 0)
- i8042_sysdev_initialized = 1;
- else
- sysdev_class_unregister(&kbc_sysclass);
- }
-
i8042_pm_dev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, i8042_pm_callback);
register_reboot_notifier(&i8042_notifier);
@@ -1001,24 +1059,23 @@ void __exit i8042_exit(void)
if (i8042_pm_dev)
pm_unregister(i8042_pm_dev);
- if (i8042_sysdev_initialized) {
- sysdev_unregister(&device_i8042);
- sysdev_class_unregister(&kbc_sysclass);
- }
-
i8042_controller_cleanup();
if (i8042_kbd_values.exists)
- serio_unregister_port(&i8042_kbd_port);
+ serio_unregister_port(i8042_kbd_port);
if (i8042_aux_values.exists)
- serio_unregister_port(&i8042_aux_port);
+ serio_unregister_port(i8042_aux_port);
- for (i = 0; i < 4; i++)
+ for (i = 0; i < I8042_NUM_MUX_PORTS; i++)
if (i8042_mux_values[i].exists)
- serio_unregister_port(i8042_mux_port + i);
+ serio_unregister_port(i8042_mux_port[i]);
+
del_timer_sync(&i8042_timer);
+ platform_device_unregister(i8042_platform_device);
+ driver_unregister(&i8042_driver);
+
i8042_platform_exit();
}
diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h
index f0f637483233..cea24034d5be 100644
--- a/drivers/input/serio/i8042.h
+++ b/drivers/input/serio/i8042.h
@@ -104,6 +104,13 @@
#define I8042_BUFFER_SIZE 32
/*
+ * Number of AUX ports on controllers supporting active multiplexing
+ * specification
+ */
+
+#define I8042_NUM_MUX_PORTS 4
+
+/*
* Debug.
*/
diff --git a/drivers/input/serio/maceps2.c b/drivers/input/serio/maceps2.c
index c7db1de49cfc..78f4d9b5a5d9 100644
--- a/drivers/input/serio/maceps2.c
+++ b/drivers/input/serio/maceps2.c
@@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/err.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -46,15 +47,18 @@ MODULE_LICENSE("GPL");
#define PS2_CONTROL_RX_CLOCK_ENABLE BIT(4) /* pause reception if set to 0 */
#define PS2_CONTROL_RESET BIT(5) /* reset */
-
struct maceps2_data {
struct mace_ps2port *port;
int irq;
};
+static struct maceps2_data port_data[2];
+static struct serio *maceps2_port[2];
+static struct platform_device *maceps2_device;
+
static int maceps2_write(struct serio *dev, unsigned char val)
{
- struct mace_ps2port *port = ((struct maceps2_data *)dev->driver)->port;
+ struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port;
unsigned int timeout = MACE_PS2_TIMEOUT;
do {
@@ -68,11 +72,10 @@ static int maceps2_write(struct serio *dev, unsigned char val)
return -1;
}
-static irqreturn_t maceps2_interrupt(int irq, void *dev_id,
- struct pt_regs *regs)
+static irqreturn_t maceps2_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct serio *dev = dev_id;
- struct mace_ps2port *port = ((struct maceps2_data *)dev->driver)->port;
+ struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port;
unsigned int byte;
if (mace_read(port->status) & PS2_STATUS_RX_FULL) {
@@ -85,7 +88,7 @@ static irqreturn_t maceps2_interrupt(int irq, void *dev_id,
static int maceps2_open(struct serio *dev)
{
- struct maceps2_data *data = (struct maceps2_data *)dev->driver;
+ struct maceps2_data *data = (struct maceps2_data *)dev->port_data;
if (request_irq(data->irq, maceps2_interrupt, 0, "PS/2 port", dev)) {
printk(KERN_ERR "Could not allocate PS/2 IRQ\n");
@@ -106,7 +109,7 @@ static int maceps2_open(struct serio *dev)
static void maceps2_close(struct serio *dev)
{
- struct maceps2_data *data = (struct maceps2_data *)dev->driver;
+ struct maceps2_data *data = (struct maceps2_data *)dev->port_data;
mace_write(PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET,
data->port->control);
@@ -114,46 +117,59 @@ static void maceps2_close(struct serio *dev)
free_irq(data->irq, dev);
}
-static struct maceps2_data port0_data, port1_data;
-static struct serio maceps2_port0 =
+static struct serio * __init maceps2_allocate_port(int idx)
{
- .type = SERIO_8042,
- .open = maceps2_open,
- .close = maceps2_close,
- .write = maceps2_write,
- .name = "MACE PS/2 port0",
- .phys = "mace/serio0",
- .driver = &port0_data,
-};
+ struct serio *serio;
+
+ serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+ if (serio) {
+ memset(serio, 0, sizeof(struct serio));
+ serio->type = SERIO_8042;
+ serio->write = maceps2_write;
+ serio->open = maceps2_open;
+ serio->close = maceps2_close;
+ snprintf(serio->name, sizeof(serio->name), "MACE PS/2 port%d", idx);
+ snprintf(serio->phys, sizeof(serio->phys), "mace/serio%d", idx);
+ serio->port_data = &port_data[idx];
+ serio->dev.parent = &maceps2_device->dev;
+ }
+
+ return serio;
+}
-static struct serio maceps2_port1 =
-{
- .type = SERIO_8042,
- .open = maceps2_open,
- .close = maceps2_close,
- .write = maceps2_write,
- .name = "MACE PS/2 port1",
- .phys = "mace/serio1",
- .driver = &port1_data,
-};
static int __init maceps2_init(void)
{
- port0_data.port = &mace->perif.ps2.keyb;
- port0_data.irq = MACEISA_KEYB_IRQ;
- port1_data.port = &mace->perif.ps2.mouse;
- port1_data.irq = MACEISA_MOUSE_IRQ;
- serio_register_port(&maceps2_port0);
- serio_register_port(&maceps2_port1);
+ maceps2_device = platform_device_register_simple("maceps2", -1, NULL, 0);
+ if (IS_ERR(maceps2_device))
+ return PTR_ERR(maceps2_device);
+
+ port_data[0].port = &mace->perif.ps2.keyb;
+ port_data[0].irq = MACEISA_KEYB_IRQ;
+ port_data[1].port = &mace->perif.ps2.mouse;
+ port_data[1].irq = MACEISA_MOUSE_IRQ;
+
+ maceps2_port[0] = maceps2_allocate_port(0);
+ maceps2_port[1] = maceps2_allocate_port(1);
+ if (!maceps2_port[0] || !maceps2_port[1]) {
+ kfree(maceps2_port[0]);
+ kfree(maceps2_port[1]);
+ platform_device_unregister(maceps2_device);
+ return -ENOMEM;
+ }
+
+ serio_register_port(maceps2_port[0]);
+ serio_register_port(maceps2_port[1]);
return 0;
}
static void __exit maceps2_exit(void)
{
- serio_unregister_port(&maceps2_port0);
- serio_unregister_port(&maceps2_port1);
+ serio_unregister_port(maceps2_port[0]);
+ serio_unregister_port(maceps2_port[1]);
+ platform_device_unregister(maceps2_device);
}
module_init(maceps2_init);
diff --git a/drivers/input/serio/parkbd.c b/drivers/input/serio/parkbd.c
index 5f25b21bed21..07cc9c3b05fe 100644
--- a/drivers/input/serio/parkbd.c
+++ b/drivers/input/serio/parkbd.c
@@ -53,9 +53,7 @@ static int parkbd_writing;
static unsigned long parkbd_start;
static struct pardevice *parkbd_dev;
-
-static char parkbd_name[] = "PARKBD AT/XT keyboard adapter";
-static char parkbd_phys[32];
+static struct serio *parkbd_port;
static int parkbd_readlines(void)
{
@@ -86,13 +84,6 @@ static int parkbd_write(struct serio *port, unsigned char c)
return 0;
}
-static struct serio parkbd_port =
-{
- .write = parkbd_write,
- .name = parkbd_name,
- .phys = parkbd_phys,
-};
-
static void parkbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
@@ -125,7 +116,7 @@ static void parkbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
parkbd_buffer |= (parkbd_readlines() >> 1) << parkbd_counter++;
if (parkbd_counter == parkbd_mode + 10)
- serio_interrupt(&parkbd_port, (parkbd_buffer >> (2 - parkbd_mode)) & 0xff, 0, regs);
+ serio_interrupt(parkbd_port, (parkbd_buffer >> (2 - parkbd_mode)) & 0xff, 0, regs);
}
parkbd_last = jiffies;
@@ -163,16 +154,38 @@ static int parkbd_getport(void)
return 0;
}
+static struct serio * __init parkbd_allocate_serio(void)
+{
+ struct serio *serio;
+
+ serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+ if (serio) {
+ serio->type = parkbd_mode;
+ serio->write = parkbd_write,
+ strlcpy(serio->name, "PARKBD AT/XT keyboard adapter", sizeof(serio->name));
+ snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", parkbd_dev->port->name);
+ }
+
+ return serio;
+}
int __init parkbd_init(void)
{
- if (parkbd_getport()) return -1;
- parkbd_writelines(3);
- parkbd_port.type = parkbd_mode;
+ int err;
+
+ err = parkbd_getport();
+ if (err)
+ return err;
- sprintf(parkbd_phys, "%s/serio0", parkbd_dev->port->name);
+ parkbd_port = parkbd_allocate_serio();
+ if (!parkbd_port) {
+ parport_release(parkbd_dev);
+ return -ENOMEM;
+ }
+
+ parkbd_writelines(3);
- serio_register_port(&parkbd_port);
+ serio_register_port(parkbd_port);
printk(KERN_INFO "serio: PARKBD %s adapter on %s\n",
parkbd_mode ? "AT" : "XT", parkbd_dev->port->name);
@@ -183,7 +196,7 @@ int __init parkbd_init(void)
void __exit parkbd_exit(void)
{
parport_release(parkbd_dev);
- serio_unregister_port(&parkbd_port);
+ serio_unregister_port(parkbd_port);
parport_unregister_device(parkbd_dev);
}
diff --git a/drivers/input/serio/pcips2.c b/drivers/input/serio/pcips2.c
index 2b4acac9439c..489749e7c9d0 100644
--- a/drivers/input/serio/pcips2.c
+++ b/drivers/input/serio/pcips2.c
@@ -38,14 +38,14 @@
#define PS2_STAT_TXEMPTY (1<<7)
struct pcips2_data {
- struct serio io;
+ struct serio *io;
unsigned int base;
struct pci_dev *dev;
};
static int pcips2_write(struct serio *io, unsigned char val)
{
- struct pcips2_data *ps2if = io->driver;
+ struct pcips2_data *ps2if = io->port_data;
unsigned int stat;
do {
@@ -80,7 +80,7 @@ static irqreturn_t pcips2_interrupt(int irq, void *devid, struct pt_regs *regs)
if (hweight8(scancode) & 1)
flag ^= SERIO_PARITY;
- serio_interrupt(&ps2if->io, scancode, flag, regs);
+ serio_interrupt(ps2if->io, scancode, flag, regs);
} while (1);
return IRQ_RETVAL(handled);
}
@@ -101,7 +101,7 @@ static void pcips2_flush_input(struct pcips2_data *ps2if)
static int pcips2_open(struct serio *io)
{
- struct pcips2_data *ps2if = io->driver;
+ struct pcips2_data *ps2if = io->port_data;
int ret, val = 0;
outb(PS2_CTRL_ENABLE, ps2if->base);
@@ -119,7 +119,7 @@ static int pcips2_open(struct serio *io)
static void pcips2_close(struct serio *io)
{
- struct pcips2_data *ps2if = io->driver;
+ struct pcips2_data *ps2if = io->port_data;
outb(0, ps2if->base);
@@ -129,46 +129,51 @@ static void pcips2_close(struct serio *io)
static int __devinit pcips2_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct pcips2_data *ps2if;
+ struct serio *serio;
int ret;
ret = pci_enable_device(dev);
if (ret)
- return ret;
+ goto out;
- if (!request_region(pci_resource_start(dev, 0),
- pci_resource_len(dev, 0), "pcips2")) {
- ret = -EBUSY;
+ ret = pci_request_regions(dev, "pcips2");
+ if (ret)
goto disable;
- }
ps2if = kmalloc(sizeof(struct pcips2_data), GFP_KERNEL);
- if (!ps2if) {
+ serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!ps2if || !serio) {
ret = -ENOMEM;
goto release;
}
memset(ps2if, 0, sizeof(struct pcips2_data));
-
- ps2if->io.type = SERIO_8042;
- ps2if->io.write = pcips2_write;
- ps2if->io.open = pcips2_open;
- ps2if->io.close = pcips2_close;
- ps2if->io.name = pci_name(dev);
- ps2if->io.phys = dev->dev.bus_id;
- ps2if->io.driver = ps2if;
+ memset(serio, 0, sizeof(struct serio));
+
+ serio->type = SERIO_8042;
+ serio->write = pcips2_write;
+ serio->open = pcips2_open;
+ serio->close = pcips2_close;
+ strlcpy(serio->name, pci_name(dev), sizeof(serio->name));
+ strlcpy(serio->phys, dev->dev.bus_id, sizeof(serio->phys));
+ serio->port_data = ps2if;
+ serio->dev.parent = &dev->dev;
+ ps2if->io = serio;
ps2if->dev = dev;
ps2if->base = pci_resource_start(dev, 0);
pci_set_drvdata(dev, ps2if);
- serio_register_port(&ps2if->io);
+ serio_register_port(ps2if->io);
return 0;
release:
- release_region(pci_resource_start(dev, 0),
- pci_resource_len(dev, 0));
+ kfree(ps2if);
+ kfree(serio);
+ pci_release_regions(dev);
disable:
pci_disable_device(dev);
+ out:
return ret;
}
@@ -176,11 +181,10 @@ static void __devexit pcips2_remove(struct pci_dev *dev)
{
struct pcips2_data *ps2if = pci_get_drvdata(dev);
- serio_unregister_port(&ps2if->io);
- release_region(pci_resource_start(dev, 0),
- pci_resource_len(dev, 0));
+ serio_unregister_port(ps2if->io);
pci_set_drvdata(dev, NULL);
kfree(ps2if);
+ pci_release_regions(dev);
pci_disable_device(dev);
}
diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c
index 7f4a2628726a..7d5ecce92346 100644
--- a/drivers/input/serio/q40kbd.c
+++ b/drivers/input/serio/q40kbd.c
@@ -35,6 +35,7 @@
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/interrupt.h>
+#include <linux/err.h>
#include <asm/bitops.h>
#include <asm/io.h>
@@ -47,43 +48,106 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Q40 PS/2 keyboard controller driver");
MODULE_LICENSE("GPL");
-static struct serio q40kbd_port =
-{
- .type = SERIO_8042,
- .name = "Q40 kbd port",
- .phys = "Q40",
- .write = NULL,
-};
-
-static irqreturn_t q40kbd_interrupt(int irq, void *dev_id,
- struct pt_regs *regs)
+spinlock_t q40kbd_lock = SPIN_LOCK_UNLOCKED;
+static struct serio *q40kbd_port;
+static struct platform_device *q40kbd_device;
+
+static irqreturn_t q40kbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&q40kbd_lock, flags);
+
if (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG))
- serio_interrupt(&q40kbd_port, master_inb(KEYCODE_REG), 0, regs);
+ serio_interrupt(q40kbd_port, master_inb(KEYCODE_REG), 0, regs);
master_outb(-1, KEYBOARD_UNLOCK_REG);
+
+ spin_unlock_irqrestore(&q40kbd_lock, flags);
+
return IRQ_HANDLED;
}
-static int __init q40kbd_init(void)
+/*
+ * q40kbd_flush() flushes all data that may be in the keyboard buffers
+ */
+
+static void q40kbd_flush(void)
+{
+ int maxread = 100;
+ unsigned long flags;
+
+ spin_lock_irqsave(&q40kbd_lock, flags);
+
+ while (maxread-- && (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG)))
+ master_inb(KEYCODE_REG);
+
+ spin_unlock_irqrestore(&q40kbd_lock, flags);
+}
+
+/*
+ * q40kbd_open() is called when a port is open by the higher layer.
+ * It allocates the interrupt and enables in in the chip.
+ */
+
+static int q40kbd_open(struct serio *port)
{
- int maxread = 100;
+ q40kbd_flush();
+ if (request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0, "q40kbd", NULL)) {
+ printk(KERN_ERR "q40kbd.c: Can't get irq %d.\n", Q40_IRQ_KEYBOARD);
+ return -1;
+ }
+
+ /* off we go */
+ master_outb(-1, KEYBOARD_UNLOCK_REG);
+ master_outb(1, KEY_IRQ_ENABLE_REG);
+
+ return 0;
+}
+
+static void q40kbd_close(struct serio *port)
+{
+ master_outb(0, KEY_IRQ_ENABLE_REG);
+ master_outb(-1, KEYBOARD_UNLOCK_REG);
+ free_irq(Q40_IRQ_KEYBOARD, NULL);
+
+ q40kbd_flush();
+}
+
+static struct serio * __init q40kbd_allocate_port(void)
+{
+ struct serio *serio;
+
+ serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+ if (serio) {
+ memset(serio, 0, sizeof(struct serio));
+ serio->type = SERIO_8042;
+ serio->open = q40kbd_open;
+ serio->close = q40kbd_close;
+ serio->dev.parent = &q40kbd_device->dev;
+ strlcpy(serio->name, "Q40 Kbd Port", sizeof(serio->name));
+ strlcpy(serio->phys, "Q40", sizeof(serio->phys));
+ }
+
+ return serio;
+}
+
+static int __init q40kbd_init(void)
+{
if (!MACH_IS_Q40)
return -EIO;
- /* allocate the IRQ */
- request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0, "q40kbd", NULL);
-
- /* flush any pending input */
- while (maxread-- && (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG)))
- master_inb(KEYCODE_REG);
+ q40kbd_device = platform_device_register_simple("q40kbd", -1, NULL, 0);
+ if (IS_ERR(q40kbd_device))
+ return PTR_ERR(q40kbd_device);
- /* off we go */
- master_outb(-1,KEYBOARD_UNLOCK_REG);
- master_outb(1,KEY_IRQ_ENABLE_REG);
+ if (!(q40kbd_port = q40kbd_allocate_port())) {
+ platform_device_unregister(q40kbd_device);
+ return -ENOMEM;
+ }
- serio_register_port(&q40kbd_port);
+ serio_register_port(q40kbd_port);
printk(KERN_INFO "serio: Q40 kbd registered\n");
return 0;
@@ -91,11 +155,8 @@ static int __init q40kbd_init(void)
static void __exit q40kbd_exit(void)
{
- master_outb(0,KEY_IRQ_ENABLE_REG);
- master_outb(-1,KEYBOARD_UNLOCK_REG);
-
- serio_unregister_port(&q40kbd_port);
- free_irq(Q40_IRQ_KEYBOARD, NULL);
+ serio_unregister_port(q40kbd_port);
+ platform_device_unregister(q40kbd_device);
}
module_init(q40kbd_init);
diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c
index a2e4be7ae74c..b25d2e3b06ba 100644
--- a/drivers/input/serio/rpckbd.c
+++ b/drivers/input/serio/rpckbd.c
@@ -33,6 +33,7 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/serio.h>
+#include <linux/err.h>
#include <asm/irq.h>
#include <asm/hardware.h>
@@ -44,6 +45,9 @@ MODULE_AUTHOR("Vojtech Pavlik, Russell King");
MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver");
MODULE_LICENSE("GPL");
+static struct serio *rpckbd_port;
+static struct platform_device *rpckbd_device;
+
static int rpckbd_write(struct serio *port, unsigned char val)
{
while (!(iomd_readb(IOMD_KCTRL) & (1 << 7)))
@@ -101,25 +105,49 @@ static void rpckbd_close(struct serio *port)
free_irq(IRQ_KEYBOARDTX, port);
}
-static struct serio rpckbd_port =
+/*
+ * Allocate and initialize serio structure for subsequent registration
+ * with serio core.
+ */
+
+static struct serio * __init rpckbd_allocate_port(void)
{
- .type = SERIO_8042,
- .open = rpckbd_open,
- .close = rpckbd_close,
- .write = rpckbd_write,
- .name = "RiscPC PS/2 kbd port",
- .phys = "rpckbd/serio0",
-};
+ struct serio *serio;
+
+ serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+ if (serio) {
+ memset(serio, 0, sizeof(struct serio));
+ serio->type = SERIO_8042;
+ serio->write = rpckbd_write;
+ serio->open = rpckbd_open;
+ serio->close = rpckbd_close;
+ serio->dev.parent = &rpckbd_device->dev;
+ strlcpy(serio->name, "RiscPC PS/2 kbd port", sizeof(serio->name));
+ strlcpy(serio->phys, "rpckbd/serio0", sizeof(serio->phys));
+ }
+
+ return serio;
+}
static int __init rpckbd_init(void)
{
- serio_register_port(&rpckbd_port);
+ rpckbd_device = platform_device_register_simple("rpckbd", -1, NULL, 0);
+ if (IS_ERR(rpckbd_device))
+ return PTR_ERR(rpckbd_device);
+
+ if (!(rpckbd_port = rpckbd_allocate_port())) {
+ platform_device_unregister(rpckbd_device);
+ return -ENOMEM;
+ }
+
+ serio_register_port(rpckbd_port);
return 0;
}
static void __exit rpckbd_exit(void)
{
- serio_unregister_port(&rpckbd_port);
+ serio_unregister_port(rpckbd_port);
+ platform_device_unregister(rpckbd_device);
}
module_init(rpckbd_init);
diff --git a/drivers/input/serio/sa1111ps2.c b/drivers/input/serio/sa1111ps2.c
index 9beb7575c365..62937f5b3e57 100644
--- a/drivers/input/serio/sa1111ps2.c
+++ b/drivers/input/serio/sa1111ps2.c
@@ -26,7 +26,7 @@
#include <asm/hardware/sa1111.h>
struct ps2if {
- struct serio io;
+ struct serio *io;
struct sa1111_dev *dev;
unsigned long base;
unsigned int open;
@@ -59,7 +59,7 @@ static irqreturn_t ps2_rxint(int irq, void *dev_id, struct pt_regs *regs)
if (hweight8(scancode) & 1)
flag ^= SERIO_PARITY;
- serio_interrupt(&ps2if->io, scancode, flag, regs);
+ serio_interrupt(ps2if->io, scancode, flag, regs);
status = sa1111_readl(ps2if->base + SA1111_PS2STAT);
}
@@ -95,7 +95,7 @@ static irqreturn_t ps2_txint(int irq, void *dev_id, struct pt_regs *regs)
*/
static int ps2_write(struct serio *io, unsigned char val)
{
- struct ps2if *ps2if = io->driver;
+ struct ps2if *ps2if = io->port_data;
unsigned long flags;
unsigned int head;
@@ -122,7 +122,7 @@ static int ps2_write(struct serio *io, unsigned char val)
static int ps2_open(struct serio *io)
{
- struct ps2if *ps2if = io->driver;
+ struct ps2if *ps2if = io->port_data;
int ret;
sa1111_enable_device(ps2if->dev);
@@ -154,7 +154,7 @@ static int ps2_open(struct serio *io)
static void ps2_close(struct serio *io)
{
- struct ps2if *ps2if = io->driver;
+ struct ps2if *ps2if = io->port_data;
sa1111_writel(0, ps2if->base + SA1111_PS2CR);
@@ -232,22 +232,28 @@ static int __init ps2_test(struct ps2if *ps2if)
static int ps2_probe(struct sa1111_dev *dev)
{
struct ps2if *ps2if;
+ struct serio *serio;
int ret;
ps2if = kmalloc(sizeof(struct ps2if), GFP_KERNEL);
- if (!ps2if) {
- return -ENOMEM;
+ serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!ps2if || !serio) {
+ ret = -ENOMEM;
+ goto free;
}
memset(ps2if, 0, sizeof(struct ps2if));
-
- ps2if->io.type = SERIO_8042;
- ps2if->io.write = ps2_write;
- ps2if->io.open = ps2_open;
- ps2if->io.close = ps2_close;
- ps2if->io.name = dev->dev.bus_id;
- ps2if->io.phys = dev->dev.bus_id;
- ps2if->io.driver = ps2if;
+ memset(serio, 0, sizeof(struct serio));
+
+ serio->type = SERIO_8042;
+ serio->write = ps2_write;
+ serio->open = ps2_open;
+ serio->close = ps2_close;
+ strlcpy(serio->name, dev->dev.bus_id, sizeof(serio->name));
+ strlcpy(serio->phys, dev->dev.bus_id, sizeof(serio->phys));
+ serio->port_data = ps2if;
+ serio->dev.parent = &dev->dev;
+ ps2if->io = serio;
ps2if->dev = dev;
sa1111_set_drvdata(dev, ps2if);
@@ -292,7 +298,7 @@ static int ps2_probe(struct sa1111_dev *dev)
ps2_clear_input(ps2if);
sa1111_disable_device(ps2if->dev);
- serio_register_port(&ps2if->io);
+ serio_register_port(ps2if->io);
return 0;
out:
@@ -302,6 +308,7 @@ static int ps2_probe(struct sa1111_dev *dev)
free:
sa1111_set_drvdata(dev, NULL);
kfree(ps2if);
+ kfree(serio);
return ret;
}
@@ -312,7 +319,7 @@ static int ps2_remove(struct sa1111_dev *dev)
{
struct ps2if *ps2if = sa1111_get_drvdata(dev);
- serio_unregister_port(&ps2if->io);
+ serio_unregister_port(ps2if->io);
release_mem_region(dev->res.start,
dev->res.end - dev->res.start + 1);
sa1111_set_drvdata(dev, NULL);
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
index 6f003197b62c..268ca1d63805 100644
--- a/drivers/input/serio/serio.c
+++ b/drivers/input/serio/serio.c
@@ -1,11 +1,9 @@
/*
- * $Id: serio.c,v 1.15 2002/01/22 21:12:03 vojtech Exp $
- *
- * Copyright (c) 1999-2001 Vojtech Pavlik
- */
-
-/*
* The Serio abstraction module
+ *
+ * Copyright (c) 1999-2004 Vojtech Pavlik
+ * Copyright (c) 2004 Dmitry Torokhov
+ * Copyright (c) 2003 Daniele Bellucci
*/
/*
@@ -26,10 +24,6 @@
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
- *
- * Changes:
- * 20 Jul. 2003 Daniele Bellucci <bellucda@tiscali.it>
- * Minor cleanups.
*/
#include <linux/stddef.h>
@@ -50,100 +44,178 @@ MODULE_LICENSE("GPL");
EXPORT_SYMBOL(serio_interrupt);
EXPORT_SYMBOL(serio_register_port);
EXPORT_SYMBOL(serio_register_port_delayed);
-EXPORT_SYMBOL(__serio_register_port);
EXPORT_SYMBOL(serio_unregister_port);
EXPORT_SYMBOL(serio_unregister_port_delayed);
-EXPORT_SYMBOL(__serio_unregister_port);
-EXPORT_SYMBOL(serio_register_device);
-EXPORT_SYMBOL(serio_unregister_device);
+EXPORT_SYMBOL(serio_register_driver);
+EXPORT_SYMBOL(serio_unregister_driver);
EXPORT_SYMBOL(serio_open);
EXPORT_SYMBOL(serio_close);
EXPORT_SYMBOL(serio_rescan);
EXPORT_SYMBOL(serio_reconnect);
+static DECLARE_MUTEX(serio_sem); /* protects serio_list and serio_diriver_list */
+static LIST_HEAD(serio_list);
+static LIST_HEAD(serio_driver_list);
+static unsigned int serio_no;
+
+struct bus_type serio_bus = {
+ .name = "serio",
+};
+
+static void serio_find_driver(struct serio *serio);
+static void serio_create_port(struct serio *serio);
+static void serio_destroy_port(struct serio *serio);
+static void serio_connect_port(struct serio *serio, struct serio_driver *drv);
+static void serio_reconnect_port(struct serio *serio);
+static void serio_disconnect_port(struct serio *serio);
+
+static int serio_bind_driver(struct serio *serio, struct serio_driver *drv)
+{
+ get_driver(&drv->driver);
+
+ drv->connect(serio, drv);
+ if (serio->drv) {
+ down_write(&serio_bus.subsys.rwsem);
+ serio->dev.driver = &drv->driver;
+ device_bind_driver(&serio->dev);
+ up_write(&serio_bus.subsys.rwsem);
+ return 1;
+ }
+
+ put_driver(&drv->driver);
+ return 0;
+}
+
+/* serio_find_driver() must be called with serio_sem down. */
+static void serio_find_driver(struct serio *serio)
+{
+ struct serio_driver *drv;
+
+ list_for_each_entry(drv, &serio_driver_list, node)
+ if (!drv->manual_bind)
+ if (serio_bind_driver(serio, drv))
+ break;
+}
+
+/*
+ * Serio event processing.
+ */
+
struct serio_event {
int type;
struct serio *serio;
struct list_head node;
};
-static DECLARE_MUTEX(serio_sem);
-static LIST_HEAD(serio_list);
-static LIST_HEAD(serio_dev_list);
+enum serio_event_type {
+ SERIO_RESCAN,
+ SERIO_RECONNECT,
+ SERIO_REGISTER_PORT,
+ SERIO_UNREGISTER_PORT,
+};
+
+static spinlock_t serio_event_lock = SPIN_LOCK_UNLOCKED; /* protects serio_event_list */
static LIST_HEAD(serio_event_list);
+static DECLARE_WAIT_QUEUE_HEAD(serio_wait);
+static DECLARE_COMPLETION(serio_exited);
static int serio_pid;
-static void serio_find_dev(struct serio *serio)
+static void serio_queue_event(struct serio *serio, int event_type)
{
- struct serio_dev *dev;
+ unsigned long flags;
+ struct serio_event *event;
- list_for_each_entry(dev, &serio_dev_list, node) {
- if (serio->dev)
- break;
- if (dev->connect)
- dev->connect(serio, dev);
- }
-}
+ spin_lock_irqsave(&serio_event_lock, flags);
-#define SERIO_RESCAN 1
-#define SERIO_RECONNECT 2
-#define SERIO_REGISTER_PORT 3
-#define SERIO_UNREGISTER_PORT 4
+ if ((event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC))) {
+ event->type = event_type;
+ event->serio = serio;
-static DECLARE_WAIT_QUEUE_HEAD(serio_wait);
-static DECLARE_COMPLETION(serio_exited);
+ list_add_tail(&event->node, &serio_event_list);
+ wake_up(&serio_wait);
+ }
+
+ spin_unlock_irqrestore(&serio_event_lock, flags);
+}
-static void serio_invalidate_pending_events(struct serio *serio)
+static struct serio_event *serio_get_event(void)
{
struct serio_event *event;
+ struct list_head *node;
+ unsigned long flags;
+
+ spin_lock_irqsave(&serio_event_lock, flags);
+
+ if (list_empty(&serio_event_list)) {
+ spin_unlock_irqrestore(&serio_event_lock, flags);
+ return NULL;
+ }
+
+ node = serio_event_list.next;
+ event = container_of(node, struct serio_event, node);
+ list_del_init(node);
+
+ spin_unlock_irqrestore(&serio_event_lock, flags);
- list_for_each_entry(event, &serio_event_list, node)
- if (event->serio == serio)
- event->serio = NULL;
+ return event;
}
-void serio_handle_events(void)
+static void serio_handle_events(void)
{
- struct list_head *node, *next;
struct serio_event *event;
- list_for_each_safe(node, next, &serio_event_list) {
- event = container_of(node, struct serio_event, node);
+ while ((event = serio_get_event())) {
down(&serio_sem);
- if (event->serio == NULL)
- goto event_done;
switch (event->type) {
case SERIO_REGISTER_PORT :
- __serio_register_port(event->serio);
+ serio_create_port(event->serio);
+ serio_connect_port(event->serio, NULL);
break;
case SERIO_UNREGISTER_PORT :
- __serio_unregister_port(event->serio);
+ serio_disconnect_port(event->serio);
+ serio_destroy_port(event->serio);
break;
case SERIO_RECONNECT :
- if (event->serio->dev && event->serio->dev->reconnect)
- if (event->serio->dev->reconnect(event->serio) == 0)
- break;
- /* reconnect failed - fall through to rescan */
+ serio_reconnect_port(event->serio);
+ break;
case SERIO_RESCAN :
- if (event->serio->dev && event->serio->dev->disconnect)
- event->serio->dev->disconnect(event->serio);
- serio_find_dev(event->serio);
+ serio_disconnect_port(event->serio);
+ serio_connect_port(event->serio, NULL);
break;
default:
break;
}
-event_done:
+
up(&serio_sem);
- list_del_init(node);
kfree(event);
}
}
+static void serio_remove_pending_events(struct serio *serio)
+{
+ struct list_head *node, *next;
+ struct serio_event *event;
+ unsigned long flags;
+
+ spin_lock_irqsave(&serio_event_lock, flags);
+
+ list_for_each_safe(node, next, &serio_event_list) {
+ event = container_of(node, struct serio_event, node);
+ if (event->serio == serio) {
+ list_del_init(node);
+ kfree(event);
+ }
+ }
+
+ spin_unlock_irqrestore(&serio_event_lock, flags);
+}
+
+
static int serio_thread(void *nothing)
{
lock_kernel();
@@ -163,52 +235,239 @@ static int serio_thread(void *nothing)
complete_and_exit(&serio_exited, 0);
}
-static void serio_queue_event(struct serio *serio, int event_type)
+
+/*
+ * Serio port operations
+ */
+
+static ssize_t serio_show_description(struct device *dev, char *buf)
{
- struct serio_event *event;
+ struct serio *serio = to_serio_port(dev);
+ return sprintf(buf, "%s\n", serio->name);
+}
- if ((event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC))) {
- event->type = event_type;
- event->serio = serio;
+static ssize_t serio_show_driver(struct device *dev, char *buf)
+{
+ return sprintf(buf, "%s\n", dev->driver ? dev->driver->name : "(none)");
+}
- list_add_tail(&event->node, &serio_event_list);
- wake_up(&serio_wait);
+static ssize_t serio_rebind_driver(struct device *dev, const char *buf, size_t count)
+{
+ struct serio *serio = to_serio_port(dev);
+ struct device_driver *drv;
+ int retval;
+
+ retval = down_interruptible(&serio_sem);
+ if (retval)
+ return retval;
+
+ retval = count;
+ if (!strncmp(buf, "none", count)) {
+ serio_disconnect_port(serio);
+ } else if (!strncmp(buf, "reconnect", count)) {
+ serio_reconnect_port(serio);
+ } else if (!strncmp(buf, "rescan", count)) {
+ serio_disconnect_port(serio);
+ serio_connect_port(serio, NULL);
+ } else if ((drv = driver_find(buf, &serio_bus)) != NULL) {
+ serio_disconnect_port(serio);
+ serio_connect_port(serio, to_serio_driver(drv));
+ put_driver(drv);
+ } else {
+ retval = -EINVAL;
}
+
+ up(&serio_sem);
+
+ return retval;
}
-void serio_rescan(struct serio *serio)
+static ssize_t serio_show_bind_mode(struct device *dev, char *buf)
{
- serio_queue_event(serio, SERIO_RESCAN);
+ struct serio *serio = to_serio_port(dev);
+ return sprintf(buf, "%s\n", serio->manual_bind ? "manual" : "auto");
}
-void serio_reconnect(struct serio *serio)
+static ssize_t serio_set_bind_mode(struct device *dev, const char *buf, size_t count)
{
- serio_queue_event(serio, SERIO_RECONNECT);
+ struct serio *serio = to_serio_port(dev);
+ int retval;
+
+ retval = count;
+ if (!strncmp(buf, "manual", count)) {
+ serio->manual_bind = 1;
+ } else if (!strncmp(buf, "auto", count)) {
+ serio->manual_bind = 0;
+ } else {
+ retval = -EINVAL;
+ }
+
+ return retval;
}
-irqreturn_t serio_interrupt(struct serio *serio,
- unsigned char data, unsigned int flags, struct pt_regs *regs)
+static struct device_attribute serio_device_attrs[] = {
+ __ATTR(description, S_IRUGO, serio_show_description, NULL),
+ __ATTR(driver, S_IWUSR | S_IRUGO, serio_show_driver, serio_rebind_driver),
+ __ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode),
+ __ATTR_NULL
+};
+
+
+static void serio_release_port(struct device *dev)
{
- irqreturn_t ret = IRQ_NONE;
+ struct serio *serio = to_serio_port(dev);
- if (serio->dev && serio->dev->interrupt) {
- ret = serio->dev->interrupt(serio, data, flags, regs);
- } else {
- if (!flags) {
- if ((serio->type == SERIO_8042 ||
- serio->type == SERIO_8042_XL) && (data != 0xaa))
- return ret;
- serio_rescan(serio);
- ret = IRQ_HANDLED;
+ kfree(serio);
+ module_put(THIS_MODULE);
+}
+
+static void serio_create_port(struct serio *serio)
+{
+ try_module_get(THIS_MODULE);
+
+ spin_lock_init(&serio->lock);
+ list_add_tail(&serio->node, &serio_list);
+ snprintf(serio->dev.bus_id, sizeof(serio->dev.bus_id), "serio%d", serio_no++);
+ serio->dev.bus = &serio_bus;
+ serio->dev.release = serio_release_port;
+ if (serio->parent)
+ serio->dev.parent = &serio->parent->dev;
+ device_register(&serio->dev);
+}
+
+/*
+ * serio_destroy_port() completes deregistration process and removes
+ * port from the system
+ */
+static void serio_destroy_port(struct serio *serio)
+{
+ struct serio_driver *drv = serio->drv;
+ unsigned long flags;
+
+ serio_remove_pending_events(serio);
+ list_del_init(&serio->node);
+
+ if (drv) {
+ drv->disconnect(serio);
+ down_write(&serio_bus.subsys.rwsem);
+ device_release_driver(&serio->dev);
+ up_write(&serio_bus.subsys.rwsem);
+ put_driver(&drv->driver);
+ }
+
+ if (serio->parent) {
+ spin_lock_irqsave(&serio->parent->lock, flags);
+ serio->parent->child = NULL;
+ spin_unlock_irqrestore(&serio->parent->lock, flags);
+ }
+
+ device_unregister(&serio->dev);
+}
+
+/*
+ * serio_connect_port() tries to bind the port and possible all its
+ * children to appropriate drivers. If driver passed in the function will not
+ * try otehr drivers when binding parent port.
+ */
+static void serio_connect_port(struct serio *serio, struct serio_driver *drv)
+{
+ WARN_ON(serio->drv);
+ WARN_ON(serio->child);
+
+ if (drv)
+ serio_bind_driver(serio, drv);
+ else if (!serio->manual_bind)
+ serio_find_driver(serio);
+
+ /* Ok, now bind children, if any */
+ while (serio->child) {
+ serio = serio->child;
+
+ WARN_ON(serio->drv);
+ WARN_ON(serio->child);
+
+ serio_create_port(serio);
+
+ if (!serio->manual_bind) {
+ /*
+ * With children we just _prefer_ passed in driver,
+ * but we will try other options in case preferred
+ * is not the one
+ */
+ if (!drv || !serio_bind_driver(serio, drv))
+ serio_find_driver(serio);
}
}
- return ret;
+}
+
+/*
+ *
+ */
+static void serio_reconnect_port(struct serio *serio)
+{
+ do {
+ if (!serio->drv || !serio->drv->reconnect || serio->drv->reconnect(serio)) {
+ serio_disconnect_port(serio);
+ serio_connect_port(serio, NULL);
+ /* Ok, old children are now gone, we are done */
+ break;
+ }
+ serio = serio->child;
+ } while (serio);
+}
+
+/*
+ * serio_disconnect_port() unbinds a port from its driver. As a side effect
+ * all child ports are unbound and destroyed.
+ */
+static void serio_disconnect_port(struct serio *serio)
+{
+ struct serio_driver *drv = serio->drv;
+ struct serio *s;
+
+ if (serio->child) {
+ /*
+ * Children ports should be disconnected and destroyed
+ * first, staring with the leaf one, since we don't want
+ * to do recursion
+ */
+ do {
+ s = serio->child;
+ } while (s->child);
+
+ while (s != serio) {
+ s = s->parent;
+ serio_destroy_port(s->child);
+ }
+ }
+
+ /*
+ * Ok, no children left, now disconnect this port
+ */
+ if (drv) {
+ drv->disconnect(serio);
+ down_write(&serio_bus.subsys.rwsem);
+ device_release_driver(&serio->dev);
+ up_write(&serio_bus.subsys.rwsem);
+ put_driver(&drv->driver);
+ }
+}
+
+void serio_rescan(struct serio *serio)
+{
+ serio_queue_event(serio, SERIO_RESCAN);
+}
+
+void serio_reconnect(struct serio *serio)
+{
+ serio_queue_event(serio, SERIO_RECONNECT);
}
void serio_register_port(struct serio *serio)
{
down(&serio_sem);
- __serio_register_port(serio);
+ serio_create_port(serio);
+ serio_connect_port(serio, NULL);
up(&serio_sem);
}
@@ -222,21 +481,11 @@ void serio_register_port_delayed(struct serio *serio)
serio_queue_event(serio, SERIO_REGISTER_PORT);
}
-/*
- * Should only be called directly if serio_sem has already been taken,
- * for example when unregistering a serio from other input device's
- * connect() function.
- */
-void __serio_register_port(struct serio *serio)
-{
- list_add_tail(&serio->node, &serio_list);
- serio_find_dev(serio);
-}
-
void serio_unregister_port(struct serio *serio)
{
down(&serio_sem);
- __serio_unregister_port(serio);
+ serio_disconnect_port(serio);
+ serio_destroy_port(serio);
up(&serio_sem);
}
@@ -250,82 +499,171 @@ void serio_unregister_port_delayed(struct serio *serio)
serio_queue_event(serio, SERIO_UNREGISTER_PORT);
}
+
/*
- * Should only be called directly if serio_sem has already been taken,
- * for example when unregistering a serio from other input device's
- * disconnect() function.
+ * Serio driver operations
*/
-void __serio_unregister_port(struct serio *serio)
+
+static ssize_t serio_driver_show_description(struct device_driver *drv, char *buf)
{
- serio_invalidate_pending_events(serio);
- list_del_init(&serio->node);
- if (serio->dev && serio->dev->disconnect)
- serio->dev->disconnect(serio);
+ struct serio_driver *driver = to_serio_driver(drv);
+ return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)");
+}
+
+static ssize_t serio_driver_show_bind_mode(struct device_driver *drv, char *buf)
+{
+ struct serio_driver *serio_drv = to_serio_driver(drv);
+ return sprintf(buf, "%s\n", serio_drv->manual_bind ? "manual" : "auto");
}
-void serio_register_device(struct serio_dev *dev)
+static ssize_t serio_driver_set_bind_mode(struct device_driver *drv, const char *buf, size_t count)
+{
+ struct serio_driver *serio_drv = to_serio_driver(drv);
+ int retval;
+
+ retval = count;
+ if (!strncmp(buf, "manual", count)) {
+ serio_drv->manual_bind = 1;
+ } else if (!strncmp(buf, "auto", count)) {
+ serio_drv->manual_bind = 0;
+ } else {
+ retval = -EINVAL;
+ }
+
+ return retval;
+}
+
+
+static struct driver_attribute serio_driver_attrs[] = {
+ __ATTR(description, S_IRUGO, serio_driver_show_description, NULL),
+ __ATTR(bind_mode, S_IWUSR | S_IRUGO,
+ serio_driver_show_bind_mode, serio_driver_set_bind_mode),
+ __ATTR_NULL
+};
+
+void serio_register_driver(struct serio_driver *drv)
{
struct serio *serio;
+
down(&serio_sem);
- list_add_tail(&dev->node, &serio_dev_list);
- list_for_each_entry(serio, &serio_list, node)
- if (!serio->dev && dev->connect)
- dev->connect(serio, dev);
+
+ list_add_tail(&drv->node, &serio_driver_list);
+
+ drv->driver.bus = &serio_bus;
+ driver_register(&drv->driver);
+
+ if (drv->manual_bind)
+ goto out;
+
+start_over:
+ list_for_each_entry(serio, &serio_list, node) {
+ if (!serio->drv) {
+ serio_connect_port(serio, drv);
+ /*
+ * if new child appeared then the list is changed,
+ * we need to start over
+ */
+ if (serio->child)
+ goto start_over;
+ }
+ }
+
+out:
up(&serio_sem);
}
-void serio_unregister_device(struct serio_dev *dev)
+void serio_unregister_driver(struct serio_driver *drv)
{
struct serio *serio;
down(&serio_sem);
- list_del_init(&dev->node);
+ list_del_init(&drv->node);
+
+start_over:
list_for_each_entry(serio, &serio_list, node) {
- if (serio->dev == dev && dev->disconnect)
- dev->disconnect(serio);
- serio_find_dev(serio);
+ if (serio->drv == drv) {
+ serio_disconnect_port(serio);
+ serio_connect_port(serio, NULL);
+ /* we could've deleted some ports, restart */
+ goto start_over;
+ }
}
+
+ driver_unregister(&drv->driver);
+
up(&serio_sem);
}
-/* called from serio_dev->connect/disconnect methods under serio_sem */
-int serio_open(struct serio *serio, struct serio_dev *dev)
+/* called from serio_driver->connect/disconnect methods under serio_sem */
+int serio_open(struct serio *serio, struct serio_driver *drv)
{
- serio->dev = dev;
+ serio_pause_rx(serio);
+ serio->drv = drv;
+ serio_continue_rx(serio);
+
if (serio->open && serio->open(serio)) {
- serio->dev = NULL;
+ serio_pause_rx(serio);
+ serio->drv = NULL;
+ serio_continue_rx(serio);
return -1;
}
return 0;
}
-/* called from serio_dev->connect/disconnect methods under serio_sem */
+/* called from serio_driver->connect/disconnect methods under serio_sem */
void serio_close(struct serio *serio)
{
if (serio->close)
serio->close(serio);
- serio->dev = NULL;
+
+ serio_pause_rx(serio);
+ serio->drv = NULL;
+ serio_continue_rx(serio);
}
-static int __init serio_init(void)
+irqreturn_t serio_interrupt(struct serio *serio,
+ unsigned char data, unsigned int dfl, struct pt_regs *regs)
{
- int pid;
+ unsigned long flags;
+ irqreturn_t ret = IRQ_NONE;
+
+ spin_lock_irqsave(&serio->lock, flags);
+
+ if (likely(serio->drv)) {
+ ret = serio->drv->interrupt(serio, data, dfl, regs);
+ } else {
+ if (!dfl) {
+ if ((serio->type != SERIO_8042 &&
+ serio->type != SERIO_8042_XL) || (data == 0xaa)) {
+ serio_rescan(serio);
+ ret = IRQ_HANDLED;
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&serio->lock, flags);
- pid = kernel_thread(serio_thread, NULL, CLONE_KERNEL);
+ return ret;
+}
- if (!pid) {
+static int __init serio_init(void)
+{
+ if (!(serio_pid = kernel_thread(serio_thread, NULL, CLONE_KERNEL))) {
printk(KERN_WARNING "serio: Failed to start kseriod\n");
return -1;
}
- serio_pid = pid;
+ serio_bus.dev_attrs = serio_device_attrs;
+ serio_bus.drv_attrs = serio_driver_attrs;
+ bus_register(&serio_bus);
return 0;
}
static void __exit serio_exit(void)
{
+ bus_unregister(&serio_bus);
kill_proc(serio_pid, SIGTERM, 1);
wait_for_completion(&serio_exited);
}
diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c
new file mode 100644
index 000000000000..f4b78bbd5cfe
--- /dev/null
+++ b/drivers/input/serio/serio_raw.c
@@ -0,0 +1,390 @@
+/*
+ * Raw serio device providing access to a raw byte stream from underlying
+ * serio port. Closely emulates behavior of pre-2.6 /dev/psaux device
+ *
+ * Copyright (c) 2004 Dmitry Torokhov
+ *
+ * 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/slab.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+#include <linux/major.h>
+#include <linux/device.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/wait.h>
+
+#define DRIVER_DESC "Raw serio driver"
+
+MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define SERIO_RAW_QUEUE_LEN 64
+struct serio_raw {
+ unsigned char queue[SERIO_RAW_QUEUE_LEN];
+ unsigned int tail, head;
+
+ char name[16];
+ unsigned int refcnt;
+ struct serio *serio;
+ struct miscdevice dev;
+ wait_queue_head_t wait;
+ struct list_head list;
+ struct list_head node;
+};
+
+struct serio_raw_list {
+ struct fasync_struct *fasync;
+ struct serio_raw *serio_raw;
+ struct list_head node;
+};
+
+static DECLARE_MUTEX(serio_raw_sem);
+static LIST_HEAD(serio_raw_list);
+static unsigned int serio_raw_no;
+
+/*********************************************************************
+ * Interface with userspace (file operations) *
+ *********************************************************************/
+
+static int serio_raw_fasync(int fd, struct file *file, int on)
+{
+ struct serio_raw_list *list = file->private_data;
+ int retval;
+
+ retval = fasync_helper(fd, file, on, &list->fasync);
+ return retval < 0 ? retval : 0;
+}
+
+static struct serio_raw *serio_raw_locate(int minor)
+{
+ struct serio_raw *serio_raw;
+
+ list_for_each_entry(serio_raw, &serio_raw_list, node) {
+ if (serio_raw->dev.minor == minor)
+ return serio_raw;
+ }
+
+ return NULL;
+}
+
+static int serio_raw_open(struct inode *inode, struct file *file)
+{
+ struct serio_raw *serio_raw;
+ struct serio_raw_list *list;
+ int retval = 0;
+
+ retval = down_interruptible(&serio_raw_sem);
+ if (retval)
+ return retval;
+
+ if (!(serio_raw = serio_raw_locate(iminor(inode)))) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ if (!serio_raw->serio) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ if (!(list = kmalloc(sizeof(struct serio_raw_list), GFP_KERNEL))) {
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ memset(list, 0, sizeof(struct serio_raw_list));
+ list->serio_raw = serio_raw;
+ file->private_data = list;
+
+ serio_raw->refcnt++;
+ list_add_tail(&list->node, &serio_raw->list);
+
+out:
+ up(&serio_raw_sem);
+ return retval;
+}
+
+static int serio_raw_cleanup(struct serio_raw *serio_raw)
+{
+ if (--serio_raw->refcnt == 0) {
+ misc_deregister(&serio_raw->dev);
+ list_del_init(&serio_raw->node);
+ kfree(serio_raw);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int serio_raw_release(struct inode *inode, struct file *file)
+{
+ struct serio_raw_list *list = file->private_data;
+ struct serio_raw *serio_raw = list->serio_raw;
+
+ down(&serio_raw_sem);
+
+ serio_raw_fasync(-1, file, 0);
+ serio_raw_cleanup(serio_raw);
+
+ up(&serio_raw_sem);
+ return 0;
+}
+
+static int serio_raw_fetch_byte(struct serio_raw *serio_raw, char *c)
+{
+ unsigned long flags;
+ int empty;
+
+ spin_lock_irqsave(&serio_raw->serio->lock, flags);
+
+ empty = serio_raw->head == serio_raw->tail;
+ if (!empty) {
+ *c = serio_raw->queue[serio_raw->tail];
+ serio_raw->tail = (serio_raw->tail + 1) % SERIO_RAW_QUEUE_LEN;
+ }
+
+ spin_unlock_irqrestore(&serio_raw->serio->lock, flags);
+
+ return !empty;
+}
+
+static ssize_t serio_raw_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+ struct serio_raw_list *list = file->private_data;
+ struct serio_raw *serio_raw = list->serio_raw;
+ char c;
+ ssize_t retval = 0;
+
+ if (!serio_raw->serio)
+ return -ENODEV;
+
+ if (serio_raw->head == serio_raw->tail && (file->f_flags & O_NONBLOCK))
+ return -EAGAIN;
+
+ retval = wait_event_interruptible(list->serio_raw->wait,
+ serio_raw->head != serio_raw->tail || !serio_raw->serio);
+ if (retval)
+ return retval;
+
+ if (!serio_raw->serio)
+ return -ENODEV;
+
+ while (retval < count && serio_raw_fetch_byte(serio_raw, &c)) {
+ if (put_user(c, buffer++))
+ return -EFAULT;
+ retval++;
+ }
+
+ return retval;
+}
+
+static ssize_t serio_raw_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+ struct serio_raw_list *list = file->private_data;
+ ssize_t written = 0;
+ int retval;
+ unsigned char c;
+
+ retval = down_interruptible(&serio_raw_sem);
+ if (retval)
+ return retval;
+
+ if (!list->serio_raw->serio) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ if (count > 32)
+ count = 32;
+
+ while (count--) {
+ if (get_user(c, buffer++)) {
+ retval = -EFAULT;
+ goto out;
+ }
+ if (serio_write(list->serio_raw->serio, c)) {
+ retval = -EIO;
+ goto out;
+ }
+ written++;
+ };
+
+out:
+ up(&serio_raw_sem);
+ return written;
+}
+
+static unsigned int serio_raw_poll(struct file *file, poll_table *wait)
+{
+ struct serio_raw_list *list = file->private_data;
+
+ poll_wait(file, &list->serio_raw->wait, wait);
+
+ if (list->serio_raw->head != list->serio_raw->tail)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+struct file_operations serio_raw_fops = {
+ .owner = THIS_MODULE,
+ .open = serio_raw_open,
+ .release = serio_raw_release,
+ .read = serio_raw_read,
+ .write = serio_raw_write,
+ .poll = serio_raw_poll,
+ .fasync = serio_raw_fasync,
+};
+
+
+/*********************************************************************
+ * Interface with serio port *
+ *********************************************************************/
+
+static irqreturn_t serio_raw_interrupt(struct serio *serio, unsigned char data,
+ unsigned int dfl, struct pt_regs *regs)
+{
+ struct serio_raw *serio_raw = serio->private;
+ struct serio_raw_list *list;
+ unsigned int head = serio_raw->head;
+
+ /* we are holding serio->lock here so we are prootected */
+ serio_raw->queue[head] = data;
+ head = (head + 1) % SERIO_RAW_QUEUE_LEN;
+ if (likely(head != serio_raw->tail)) {
+ serio_raw->head = head;
+ list_for_each_entry(list, &serio_raw->list, node)
+ kill_fasync(&list->fasync, SIGIO, POLL_IN);
+ wake_up_interruptible(&serio_raw->wait);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void serio_raw_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct serio_raw *serio_raw;
+ int err;
+
+ if ((serio->type & SERIO_TYPE) != SERIO_8042)
+ return;
+
+ if (!(serio_raw = kmalloc(sizeof(struct serio_raw), GFP_KERNEL))) {
+ printk(KERN_ERR "serio_raw.c: can't allocate memory for a device\n");
+ return;
+ }
+
+ down(&serio_raw_sem);
+
+ memset(serio_raw, 0, sizeof(struct serio_raw));
+ snprintf(serio_raw->name, sizeof(serio_raw->name), "serio_raw%d", serio_raw_no++);
+ serio_raw->refcnt = 1;
+ serio_raw->serio = serio;
+ INIT_LIST_HEAD(&serio_raw->list);
+ init_waitqueue_head(&serio_raw->wait);
+
+ serio->private = serio_raw;
+ if (serio_open(serio, drv))
+ goto out_free;
+
+ list_add_tail(&serio_raw->node, &serio_raw_list);
+
+ serio_raw->dev.minor = PSMOUSE_MINOR;
+ serio_raw->dev.name = serio_raw->name;
+ serio_raw->dev.fops = &serio_raw_fops;
+
+ err = misc_register(&serio_raw->dev);
+ if (err) {
+ serio_raw->dev.minor = MISC_DYNAMIC_MINOR;
+ err = misc_register(&serio_raw->dev);
+ }
+
+ if (err) {
+ printk(KERN_INFO "serio_raw: failed to register raw access device for %s\n",
+ serio->phys);
+ goto out_close;
+ }
+
+ printk(KERN_INFO "serio_raw: raw access enabled on %s (%s, minor %d)\n",
+ serio->phys, serio_raw->name, serio_raw->dev.minor);
+ goto out;
+
+out_close:
+ serio_close(serio);
+ list_del_init(&serio_raw->node);
+out_free:
+ serio->private = NULL;
+ kfree(serio_raw);
+out:
+ up(&serio_raw_sem);
+}
+
+static int serio_raw_reconnect(struct serio *serio)
+{
+ struct serio_raw *serio_raw = serio->private;
+ struct serio_driver *drv = serio->drv;
+
+ if (!drv || !serio_raw) {
+ printk(KERN_DEBUG "serio_raw: reconnect request, but serio is disconnected, ignoring...\n");
+ return -1;
+ }
+
+ /*
+ * Nothing needs to be done here, we just need this method to
+ * keep the same device.
+ */
+ return 0;
+}
+
+static void serio_raw_disconnect(struct serio *serio)
+{
+ struct serio_raw *serio_raw;
+
+ down(&serio_raw_sem);
+
+ serio_raw = serio->private;
+
+ serio_close(serio);
+ serio->private = NULL;
+
+ serio_raw->serio = NULL;
+ if (!serio_raw_cleanup(serio_raw))
+ wake_up_interruptible(&serio_raw->wait);
+
+ up(&serio_raw_sem);
+}
+
+static struct serio_driver serio_raw_drv = {
+ .driver = {
+ .name = "serio_raw",
+ },
+ .description = DRIVER_DESC,
+ .interrupt = serio_raw_interrupt,
+ .connect = serio_raw_connect,
+ .reconnect = serio_raw_reconnect,
+ .disconnect = serio_raw_disconnect,
+ .manual_bind = 1,
+};
+
+int __init serio_raw_init(void)
+{
+ serio_register_driver(&serio_raw_drv);
+ return 0;
+}
+
+void __exit serio_raw_exit(void)
+{
+ serio_unregister_driver(&serio_raw_drv);
+}
+
+module_init(serio_raw_init);
+module_exit(serio_raw_exit);
diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c
index 19e269249a29..5582ee8227d5 100644
--- a/drivers/input/serio/serport.c
+++ b/drivers/input/serio/serport.c
@@ -31,28 +31,25 @@ MODULE_ALIAS_LDISC(N_MOUSE);
struct serport {
struct tty_struct *tty;
wait_queue_head_t wait;
- struct serio serio;
+ struct serio *serio;
unsigned long flags;
- char phys[32];
};
-char serport_name[] = "Serial port";
-
/*
* Callback functions from the serio code.
*/
static int serport_serio_write(struct serio *serio, unsigned char data)
{
- struct serport *serport = serio->driver;
+ struct serport *serport = serio->port_data;
return -(serport->tty->driver->write(serport->tty, 0, &data, 1) != 1);
}
static void serport_serio_close(struct serio *serio)
{
- struct serport *serport = serio->driver;
+ struct serport *serport = serio->port_data;
- serport->serio.type = 0;
+ serport->serio->type = 0;
wake_up_interruptible(&serport->wait);
}
@@ -64,26 +61,30 @@ static void serport_serio_close(struct serio *serio)
static int serport_ldisc_open(struct tty_struct *tty)
{
struct serport *serport;
+ struct serio *serio;
char name[64];
serport = kmalloc(sizeof(struct serport), GFP_KERNEL);
- if (unlikely(!serport))
+ serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+ if (unlikely(!serport || !serio)) {
+ kfree(serport);
+ kfree(serio);
return -ENOMEM;
- memset(serport, 0, sizeof(struct serport));
+ }
+ memset(serport, 0, sizeof(struct serport));
+ serport->serio = serio;
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
serport->tty = tty;
tty->disc_data = serport;
- snprintf(serport->phys, sizeof(serport->phys), "%s/serio0", tty_name(tty, name));
-
- serport->serio.name = serport_name;
- serport->serio.phys = serport->phys;
-
- serport->serio.type = SERIO_RS232;
- serport->serio.write = serport_serio_write;
- serport->serio.close = serport_serio_close;
- serport->serio.driver = serport;
+ memset(serio, 0, sizeof(struct serio));
+ strlcpy(serio->name, "Serial port", sizeof(serio->name));
+ snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty, name));
+ serio->type = SERIO_RS232;
+ serio->write = serport_serio_write;
+ serio->close = serport_serio_close;
+ serio->port_data = serport;
init_waitqueue_head(&serport->wait);
@@ -114,7 +115,7 @@ static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *c
struct serport *serport = (struct serport*) tty->disc_data;
int i;
for (i = 0; i < count; i++)
- serio_interrupt(&serport->serio, cp[i], 0, NULL);
+ serio_interrupt(serport->serio, cp[i], 0, NULL);
}
/*
@@ -142,10 +143,10 @@ static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, u
if (test_and_set_bit(SERPORT_BUSY, &serport->flags))
return -EBUSY;
- serio_register_port(&serport->serio);
+ serio_register_port(serport->serio);
printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty, name));
- wait_event_interruptible(serport->wait, !serport->serio.type);
- serio_unregister_port(&serport->serio);
+ wait_event_interruptible(serport->wait, !serport->serio->type);
+ serio_unregister_port(serport->serio);
clear_bit(SERPORT_BUSY, &serport->flags);
@@ -161,7 +162,7 @@ static int serport_ldisc_ioctl(struct tty_struct * tty, struct file * file, unsi
struct serport *serport = (struct serport*) tty->disc_data;
if (cmd == SPIOCSTYPE)
- return get_user(serport->serio.type, (unsigned long __user *) arg);
+ return get_user(serport->serio->type, (unsigned long __user *) arg);
return -EINVAL;
}
@@ -170,7 +171,7 @@ static void serport_ldisc_write_wakeup(struct tty_struct * tty)
{
struct serport *sp = (struct serport *) tty->disc_data;
- serio_dev_write_wakeup(&sp->serio);
+ serio_drv_write_wakeup(sp->serio);
}
/*
diff --git a/drivers/input/touchscreen/gunze.c b/drivers/input/touchscreen/gunze.c
index 77591498016a..e5e38dd4a191 100644
--- a/drivers/input/touchscreen/gunze.c
+++ b/drivers/input/touchscreen/gunze.c
@@ -36,8 +36,10 @@
#include <linux/serio.h>
#include <linux/init.h>
+#define DRIVER_DESC "Gunze AHL-51S touchscreen driver"
+
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
-MODULE_DESCRIPTION("Gunze AHL-51S touchscreen driver");
+MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/*
@@ -111,7 +113,7 @@ static void gunze_disconnect(struct serio *serio)
* and if yes, registers it as an input device.
*/
-static void gunze_connect(struct serio *serio, struct serio_dev *dev)
+static void gunze_connect(struct serio *serio, struct serio_driver *drv)
{
struct gunze *gunze;
@@ -142,7 +144,7 @@ static void gunze_connect(struct serio *serio, struct serio_dev *dev)
gunze->dev.id.product = 0x0051;
gunze->dev.id.version = 0x0100;
- if (serio_open(serio, dev)) {
+ if (serio_open(serio, drv)) {
kfree(gunze);
return;
}
@@ -156,10 +158,14 @@ static void gunze_connect(struct serio *serio, struct serio_dev *dev)
* The serio device structure.
*/
-static struct serio_dev gunze_dev = {
- .interrupt = gunze_interrupt,
- .connect = gunze_connect,
- .disconnect = gunze_disconnect,
+static struct serio_driver gunze_drv = {
+ .driver = {
+ .name = "gunze",
+ },
+ .description = DRIVER_DESC,
+ .interrupt = gunze_interrupt,
+ .connect = gunze_connect,
+ .disconnect = gunze_disconnect,
};
/*
@@ -168,13 +174,13 @@ static struct serio_dev gunze_dev = {
int __init gunze_init(void)
{
- serio_register_device(&gunze_dev);
+ serio_register_driver(&gunze_drv);
return 0;
}
void __exit gunze_exit(void)
{
- serio_unregister_device(&gunze_dev);
+ serio_unregister_driver(&gunze_drv);
}
module_init(gunze_init);
diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c
index 7b4c16d6fb54..5d719671cda2 100644
--- a/drivers/input/touchscreen/h3600_ts_input.c
+++ b/drivers/input/touchscreen/h3600_ts_input.c
@@ -45,8 +45,10 @@
#include <asm/arch/hardware.h>
#include <asm/arch/irqs.h>
+#define DRIVER_DESC "H3600 touchscreen driver"
+
MODULE_AUTHOR("James Simmons <jsimmons@transvirtual.com>");
-MODULE_DESCRIPTION("H3600 touchscreen driver");
+MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/*
@@ -373,7 +375,7 @@ static irqreturn_t h3600ts_interrupt(struct serio *serio, unsigned char data,
* new serio device. It looks whether it was registered as a H3600 touchscreen
* and if yes, registers it as an input device.
*/
-static void h3600ts_connect(struct serio *serio, struct serio_dev *dev)
+static void h3600ts_connect(struct serio *serio, struct serio_driver *drv)
{
struct h3600_dev *ts;
@@ -441,7 +443,7 @@ static void h3600ts_connect(struct serio *serio, struct serio_dev *dev)
ts->dev.id.product = 0x0666; /* FIXME !!! We can ask the hardware */
ts->dev.id.version = 0x0100;
- if (serio_open(serio, dev)) {
+ if (serio_open(serio, drv)) {
free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts);
free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts);
kfree(ts);
@@ -478,10 +480,14 @@ static void h3600ts_disconnect(struct serio *serio)
* The serio device structure.
*/
-static struct serio_dev h3600ts_dev = {
- .interrupt = h3600ts_interrupt,
- .connect = h3600ts_connect,
- .disconnect = h3600ts_disconnect,
+static struct serio_driver h3600ts_drv = {
+ .driver = {
+ .name = "h3600ts",
+ },
+ .description = DRIVER_DESC,
+ .interrupt = h3600ts_interrupt,
+ .connect = h3600ts_connect,
+ .disconnect = h3600ts_disconnect,
};
/*
@@ -490,13 +496,13 @@ static struct serio_dev h3600ts_dev = {
static int __init h3600ts_init(void)
{
- serio_register_device(&h3600ts_dev);
+ serio_register_driver(&h3600ts_drv);
return 0;
}
static void __exit h3600ts_exit(void)
{
- serio_unregister_device(&h3600ts_dev);
+ serio_unregister_driver(&h3600ts_drv);
}
module_init(h3600ts_init);
diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c
index ac072d37c477..7acacb3c6894 100644
--- a/drivers/input/tsdev.c
+++ b/drivers/input/tsdev.c
@@ -3,32 +3,42 @@
*
* Copyright (c) 2001 "Crazy" james Simmons
*
- * Input driver to Touchscreen device driver module.
+ * 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.
*
- * Sponsored by Transvirtual Technology
+ * 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
+ * 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@transvirtual.com>.
+ * 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>
@@ -52,48 +62,84 @@
#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");
+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");
+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[16];
+ char name[8];
wait_queue_head_t wait;
struct list_head list;
struct input_handle handle;
+ int x, y, pressure;
+ struct ts_calibration cal;
};
-/* From Compaq's Touch Screen Specification version 0.2 (draft) */
-typedef struct {
- short pressure;
- short x;
- short y;
- short millisecs;
-} TS_EVENT;
-
struct tsdev_list {
struct fasync_struct *fasync;
struct list_head node;
struct tsdev *tsdev;
int head, tail;
- int oldx, oldy, pendown;
- TS_EVENT event[TSDEV_BUFFER_SIZE];
+ 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 input_handler tsdev_handler;
-static struct tsdev *tsdev_table[TSDEV_MINORS];
+static struct tsdev *tsdev_table[TSDEV_MINORS/2];
static int tsdev_fasync(int fd, struct file *file, int on)
{
@@ -109,13 +155,16 @@ static int tsdev_open(struct inode *inode, struct file *file)
int i = iminor(inode) - TSDEV_MINOR_BASE;
struct tsdev_list *list;
- if (i >= TSDEV_MINORS || !tsdev_table[i])
+ if (i >= TSDEV_MINORS || !tsdev_table[i & TSDEV_MINOR_MASK])
return -ENODEV;
if (!(list = kmalloc(sizeof(struct tsdev_list), GFP_KERNEL)))
return -ENOMEM;
memset(list, 0, sizeof(struct tsdev_list));
+ list->raw = (i >= TSDEV_MINORS/2) ? 1 : 0;
+
+ i &= TSDEV_MINOR_MASK;
list->tsdev = tsdev_table[i];
list_add_tail(&list->node, &tsdev_table[i]->list);
file->private_data = list;
@@ -161,7 +210,7 @@ static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count,
return -EAGAIN;
retval = wait_event_interruptible(list->tsdev->wait,
- (list->head != list->tail) && list->tsdev->exist);
+ list->head != list->tail || !list->tsdev->exist);
if (retval)
return retval;
@@ -169,11 +218,13 @@ static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count,
if (!list->tsdev->exist)
return -ENODEV;
- while (list->head != list->tail && retval + sizeof(TS_EVENT) <= count) {
- if (copy_to_user (buffer + retval, list->event + list->tail, sizeof(TS_EVENT)))
+ while (list->head != list->tail &&
+ retval + sizeof (struct ts_event) <= count) {
+ if (copy_to_user (buffer + retval, list->event + list->tail,
+ sizeof (struct ts_event)))
return -EFAULT;
list->tail = (list->tail + 1) & (TSDEV_BUFFER_SIZE - 1);
- retval += sizeof(TS_EVENT);
+ retval += sizeof (struct ts_event);
}
return retval;
@@ -193,22 +244,27 @@ static unsigned int tsdev_poll(struct file *file, poll_table * wait)
static int tsdev_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
-/*
struct tsdev_list *list = file->private_data;
- struct tsdev *evdev = list->tsdev;
- struct input_dev *dev = tsdev->handle.dev;
- int retval;
-
+ struct tsdev *tsdev = list->tsdev;
+ int retval = 0;
+
switch (cmd) {
- case HHEHE:
- return 0;
- case hjff:
- return 0;
- default:
- return 0;
+ case TS_GET_CAL:
+ if (copy_to_user ((void *)arg, &tsdev->cal,
+ sizeof (struct ts_calibration)))
+ retval = -EFAULT;
+ break;
+ case TS_SET_CAL:
+ if (copy_from_user (&tsdev->cal, (void *)arg,
+ sizeof (struct ts_calibration)))
+ retval = -EFAULT;
+ break;
+ default:
+ retval = -EINVAL;
+ break;
}
-*/
- return -EINVAL;
+
+ return retval;
}
struct file_operations tsdev_fops = {
@@ -227,82 +283,85 @@ static void tsdev_event(struct input_handle *handle, unsigned int type,
struct tsdev *tsdev = handle->private;
struct tsdev_list *list;
struct timeval time;
- int size;
- list_for_each_entry(list, &tsdev->list, node) {
- switch (type) {
- case EV_ABS:
- switch (code) {
- case ABS_X:
- if (!list->pendown)
- return;
- size = handle->dev->absmax[ABS_X] - handle->dev->absmin[ABS_X];
- if (size > 0)
- list->oldx = ((value - handle->dev->absmin[ABS_X]) * xres / size);
- else
- list->oldx = ((value - handle->dev->absmin[ABS_X]));
- break;
- case ABS_Y:
- if (!list->pendown)
- return;
- size = handle->dev->absmax[ABS_Y] - handle->dev->absmin[ABS_Y];
- if (size > 0)
- list->oldy = ((value - handle->dev->absmin[ABS_Y]) * yres / size);
- else
- list->oldy = ((value - handle->dev->absmin[ABS_Y]));
- break;
- case ABS_PRESSURE:
- list->pendown = ((value > handle->dev-> absmin[ABS_PRESSURE])) ?
- value - handle->dev->absmin[ABS_PRESSURE] : 0;
- break;
- }
+ 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_REL:
- switch (code) {
- case REL_X:
- if (!list->pendown)
- return;
- list->oldx += value;
- if (list->oldx < 0)
- list->oldx = 0;
- else if (list->oldx > xres)
- list->oldx = xres;
+ case EV_KEY:
+ if (code == BTN_TOUCH || code == BTN_MOUSE) {
+ switch (value) {
+ case 0:
+ tsdev->pressure = 0;
break;
- case REL_Y:
- if (!list->pendown)
- return;
- list->oldy += value;
- if (list->oldy < 0)
- list->oldy = 0;
- else if (list->oldy > xres)
- list->oldy = xres;
+ case 1:
+ if (!tsdev->pressure)
+ tsdev->pressure = 1;
break;
}
- break;
-
- case EV_KEY:
- if (code == BTN_TOUCH || code == BTN_MOUSE) {
- switch (value) {
- case 0:
- list->pendown = 0;
- break;
- case 1:
- if (!list->pendown)
- list->pendown = 1;
- break;
- case 2:
- return;
- }
- } else
- return;
- break;
}
+ break;
+ }
+
+ if (type != EV_SYN || code != SYN_REPORT)
+ return;
+
+ list_for_each_entry(list, &tsdev->list, node) {
+ int x, y, tmp;
+
do_gettimeofday(&time);
list->event[list->head].millisecs = time.tv_usec / 100;
- list->event[list->head].pressure = list->pendown;
- list->event[list->head].x = list->oldx;
- list->event[list->head].y = list->oldy;
+ list->event[list->head].pressure = tsdev->pressure;
+
+ x = tsdev->x;
+ y = tsdev->y;
+
+ /* Calibration */
+ if (!list->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;
+ }
+ }
+
+ list->event[list->head].x = x;
+ list->event[list->head].y = y;
list->head = (list->head + 1) & (TSDEV_BUFFER_SIZE - 1);
kill_fasync(&list->fasync, SIGIO, POLL_IN);
}
@@ -314,11 +373,11 @@ static struct input_handle *tsdev_connect(struct input_handler *handler,
struct input_device_id *id)
{
struct tsdev *tsdev;
- int minor;
+ int minor, delta;
- for (minor = 0; minor < TSDEV_MINORS && tsdev_table[minor];
+ for (minor = 0; minor < TSDEV_MINORS/2 && tsdev_table[minor];
minor++);
- if (minor == TSDEV_MINORS) {
+ if (minor >= TSDEV_MINORS/2) {
printk(KERN_ERR
"tsdev: You have way too many touchscreens\n");
return NULL;
@@ -340,10 +399,25 @@ static struct input_handle *tsdev_connect(struct input_handler *handler,
tsdev->handle.handler = handler;
tsdev->handle.private = tsdev;
+ /* 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);
+
tsdev_table[minor] = tsdev;
-
+
devfs_mk_cdev(MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor),
S_IFCHR|S_IRUGO|S_IWUSR, "input/ts%d", minor);
+ devfs_mk_cdev(MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor + TSDEV_MINORS/2),
+ S_IFCHR|S_IRUGO|S_IWUSR, "input/tsraw%d", minor);
class_simple_device_add(input_class,
MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor),
dev->dev, "ts%d", minor);
@@ -362,6 +436,7 @@ static void tsdev_disconnect(struct input_handle *handle)
wake_up_interruptible(&tsdev->wait);
} else
tsdev_free(tsdev);
+ devfs_remove("input/tsraw%d", tsdev->minor);
}
static struct input_device_id tsdev_ids[] = {
@@ -379,6 +454,12 @@ static struct input_device_id tsdev_ids[] = {
.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 */
};