Commit 33124340 authored by Vojtech Pavlik's avatar Vojtech Pavlik

Add support for PS/2 Active Multiplexing Spec, updates for PS/2 mouse

and keyboard handling - proper cleanup on reboot, allow USB-emulated
AT keyboards, option to restrict PS/2 mouse to generic mode.
parent e0fcd3bc
...@@ -22,9 +22,15 @@ ...@@ -22,9 +22,15 @@
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("AT and PS/2 keyboard driver"); MODULE_DESCRIPTION("AT and PS/2 keyboard driver");
MODULE_PARM(atkbd_set, "1i"); MODULE_PARM(atkbd_set, "1i");
MODULE_PARM(atkbd_reset, "1i");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
static int atkbd_set = 2; static int atkbd_set = 2;
#if defined(__i386__) || defined (__x86_64__)
static int atkbd_reset;
#else
static int atkbd_reset = 1;
#endif
/* /*
* Scancode to keycode tables. These are just the default setting, and * Scancode to keycode tables. These are just the default setting, and
...@@ -89,6 +95,7 @@ static unsigned char atkbd_set3_keycode[512] = { ...@@ -89,6 +95,7 @@ static unsigned char atkbd_set3_keycode[512] = {
#define ATKBD_CMD_RESEND 0x00fe #define ATKBD_CMD_RESEND 0x00fe
#define ATKBD_CMD_EX_ENABLE 0x10ea #define ATKBD_CMD_EX_ENABLE 0x10ea
#define ATKBD_CMD_EX_SETLEDS 0x20eb #define ATKBD_CMD_EX_SETLEDS 0x20eb
#define ATKBD_CMD_OK_GETID 0x02e8
#define ATKBD_RET_ACK 0xfa #define ATKBD_RET_ACK 0xfa
#define ATKBD_RET_NAK 0xfe #define ATKBD_RET_NAK 0xfe
...@@ -113,6 +120,7 @@ struct atkbd { ...@@ -113,6 +120,7 @@ struct atkbd {
unsigned char cmdbuf[4]; unsigned char cmdbuf[4];
unsigned char cmdcnt; unsigned char cmdcnt;
unsigned char set; unsigned char set;
unsigned char oldset;
unsigned char release; unsigned char release;
signed char ack; signed char ack;
unsigned char emul; unsigned char emul;
...@@ -134,7 +142,6 @@ static void atkbd_interrupt(struct serio *serio, unsigned char data, unsigned in ...@@ -134,7 +142,6 @@ static void atkbd_interrupt(struct serio *serio, unsigned char data, unsigned in
printk(KERN_DEBUG "atkbd.c: Received %02x flags %02x\n", data, flags); printk(KERN_DEBUG "atkbd.c: Received %02x flags %02x\n", data, flags);
#endif #endif
/* Interface error. Request that the keyboard resend. */
if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && atkbd->write) { if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && atkbd->write) {
printk("atkbd.c: frame/parity error: %02x\n", flags); printk("atkbd.c: frame/parity error: %02x\n", flags);
serio_write(serio, ATKBD_CMD_RESEND); serio_write(serio, ATKBD_CMD_RESEND);
...@@ -205,7 +212,9 @@ static int atkbd_sendbyte(struct atkbd *atkbd, unsigned char byte) ...@@ -205,7 +212,9 @@ static int atkbd_sendbyte(struct atkbd *atkbd, unsigned char byte)
#ifdef ATKBD_DEBUG #ifdef ATKBD_DEBUG
printk(KERN_DEBUG "atkbd.c: Sent: %02x\n", byte); printk(KERN_DEBUG "atkbd.c: Sent: %02x\n", byte);
#endif #endif
serio_write(atkbd->serio, byte); if (serio_write(atkbd->serio, byte))
return -1;
while (!atkbd->ack && timeout--) udelay(10); while (!atkbd->ack && timeout--) udelay(10);
return -(atkbd->ack <= 0); return -(atkbd->ack <= 0);
...@@ -289,34 +298,41 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co ...@@ -289,34 +298,41 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co
static int atkbd_set_3(struct atkbd *atkbd) static int atkbd_set_3(struct atkbd *atkbd)
{ {
unsigned char param; unsigned char param[2];
/*
* Remember original scancode set value, so that we can restore it on exit.
*/
if (atkbd_command(atkbd, &atkbd->oldset, ATKBD_CMD_GSCANSET))
atkbd->oldset = 2;
/* /*
* For known special keyboards we can go ahead and set the correct set. * For known special keyboards we can go ahead and set the correct set.
* We check for NCD PS/2 Sun, NorthGate OmniKey 101 and IBM RapidAccess
* keyboards.
*/ */
if (atkbd->id == 0xaca1) { if (atkbd->id == 0xaca1) {
param = 3; param[0] = 3;
atkbd_command(atkbd, &param, ATKBD_CMD_SSCANSET); atkbd_command(atkbd, param, ATKBD_CMD_SSCANSET);
return 3; return 3;
} }
/* if (!atkbd_command(atkbd, param, ATKBD_CMD_OK_GETID)) {
* We check for the extra keys on an some keyboards that need extra atkbd->id = param[0] << 8 | param[1];
* command to get enabled. This shouldn't harm any keyboards not return 2;
* knowing the command. }
*/
param = 0x71; param[0] = 0x71;
if (!atkbd_command(atkbd, &param, ATKBD_CMD_EX_ENABLE)) if (!atkbd_command(atkbd, param, ATKBD_CMD_EX_ENABLE))
return 4; return 4;
/* /*
* Try to set the set we want. * Try to set the set we want.
*/ */
param = atkbd_set; if (atkbd_command(atkbd, &atkbd_set, ATKBD_CMD_SSCANSET))
if (atkbd_command(atkbd, &param, ATKBD_CMD_SSCANSET))
return 2; return 2;
/* /*
...@@ -327,8 +343,8 @@ static int atkbd_set_3(struct atkbd *atkbd) ...@@ -327,8 +343,8 @@ static int atkbd_set_3(struct atkbd *atkbd)
* In that case we time out, and return 2. * In that case we time out, and return 2.
*/ */
param = 0; param[0] = 0;
if (atkbd_command(atkbd, &param, ATKBD_CMD_GSCANSET)) if (atkbd_command(atkbd, param, ATKBD_CMD_GSCANSET))
return 2; return 2;
/* /*
...@@ -336,7 +352,7 @@ static int atkbd_set_3(struct atkbd *atkbd) ...@@ -336,7 +352,7 @@ static int atkbd_set_3(struct atkbd *atkbd)
* itself. * itself.
*/ */
return (param == 3) ? 3 : 2; return (param[0] == 3) ? 3 : 2;
} }
/* /*
...@@ -353,10 +369,9 @@ static int atkbd_probe(struct atkbd *atkbd) ...@@ -353,10 +369,9 @@ static int atkbd_probe(struct atkbd *atkbd)
* these systems the BIOS also usually doesn't do it for us. * these systems the BIOS also usually doesn't do it for us.
*/ */
#ifdef CONFIG_KEYBOARD_ATKBD_RESET if (atkbd_reset)
if (atkbd_command(atkbd, NULL, ATKBD_CMD_RESET_BAT)) if (atkbd_command(atkbd, NULL, ATKBD_CMD_RESET_BAT))
printk(KERN_WARNING "atkbd.c: keyboard reset failed\n"); printk(KERN_WARNING "atkbd.c: keyboard reset failed\n");
#endif
/* /*
* Next we check we can set LEDs on the keyboard. This should work on every * Next we check we can set LEDs on the keyboard. This should work on every
...@@ -405,7 +420,18 @@ static int atkbd_probe(struct atkbd *atkbd) ...@@ -405,7 +420,18 @@ static int atkbd_probe(struct atkbd *atkbd)
} }
/* /*
* atkbd_disconnect() cleans up behind us ... * atkbd_cleanup() restores the keyboard state so that BIOS is happy after a
* reboot.
*/
static void atkbd_cleanup(struct serio *serio)
{
struct atkbd *atkbd = serio->private;
atkbd_command(atkbd, &atkbd->oldset, ATKBD_CMD_SSCANSET);
}
/*
* atkbd_disconnect() closes and frees.
*/ */
static void atkbd_disconnect(struct serio *serio) static void atkbd_disconnect(struct serio *serio)
...@@ -508,18 +534,28 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev) ...@@ -508,18 +534,28 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
static struct serio_dev atkbd_dev = { static struct serio_dev atkbd_dev = {
.interrupt = atkbd_interrupt, .interrupt = atkbd_interrupt,
.connect = atkbd_connect, .connect = atkbd_connect,
.disconnect = atkbd_disconnect .disconnect = atkbd_disconnect,
.cleanup = atkbd_cleanup,
}; };
#ifndef MODULE #ifndef MODULE
static int __init atkbd_setup(char *str) static int __init atkbd_setup_set(char *str)
{ {
int ints[4]; int ints[4];
str = get_options(str, ARRAY_SIZE(ints), ints); str = get_options(str, ARRAY_SIZE(ints), ints);
if (ints[0] > 0) atkbd_set = ints[1]; if (ints[0] > 0) atkbd_set = ints[1];
return 1; return 1;
} }
__setup("atkbd_set=", atkbd_setup); static int __init atkbd_setup_reset(char *str)
{
int ints[4];
str = get_options(str, ARRAY_SIZE(ints), ints);
if (ints[0] > 0) atkbd_reset = ints[1];
return 1;
}
__setup("atkbd_set=", atkbd_setup_set);
__setup("atkbd_reset", atkbd_setup_reset);
#endif #endif
int __init atkbd_init(void) int __init atkbd_init(void)
......
...@@ -21,8 +21,11 @@ ...@@ -21,8 +21,11 @@
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("PS/2 mouse driver"); MODULE_DESCRIPTION("PS/2 mouse driver");
MODULE_PARM(psmouse_noext, "1i");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
static int psmouse_noext;
#define PSMOUSE_CMD_SETSCALE11 0x00e6 #define PSMOUSE_CMD_SETSCALE11 0x00e6
#define PSMOUSE_CMD_SETRES 0x10e8 #define PSMOUSE_CMD_SETRES 0x10e8
#define PSMOUSE_CMD_GETINFO 0x03e9 #define PSMOUSE_CMD_GETINFO 0x03e9
...@@ -33,6 +36,7 @@ MODULE_LICENSE("GPL"); ...@@ -33,6 +36,7 @@ MODULE_LICENSE("GPL");
#define PSMOUSE_CMD_SETRATE 0x10f3 #define PSMOUSE_CMD_SETRATE 0x10f3
#define PSMOUSE_CMD_ENABLE 0x00f4 #define PSMOUSE_CMD_ENABLE 0x00f4
#define PSMOUSE_CMD_RESET_DIS 0x00f6 #define PSMOUSE_CMD_RESET_DIS 0x00f6
#define PSMOUSE_CMD_RESET_BAT 0x02ff
#define PSMOUSE_RET_BAT 0xaa #define PSMOUSE_RET_BAT 0xaa
#define PSMOUSE_RET_ACK 0xfa #define PSMOUSE_RET_ACK 0xfa
...@@ -222,7 +226,11 @@ static int psmouse_sendbyte(struct psmouse *psmouse, unsigned char byte) ...@@ -222,7 +226,11 @@ static int psmouse_sendbyte(struct psmouse *psmouse, unsigned char byte)
psmouse->ack = 0; psmouse->ack = 0;
psmouse->acking = 1; psmouse->acking = 1;
serio_write(psmouse->serio, byte); if (serio_write(psmouse->serio, byte)) {
psmouse->acking = 0;
return -1;
}
while (!psmouse->ack && timeout--) udelay(10); while (!psmouse->ack && timeout--) udelay(10);
return -(psmouse->ack <= 0); return -(psmouse->ack <= 0);
...@@ -302,6 +310,9 @@ static int psmouse_extensions(struct psmouse *psmouse) ...@@ -302,6 +310,9 @@ static int psmouse_extensions(struct psmouse *psmouse)
psmouse->name = "Mouse"; psmouse->name = "Mouse";
psmouse->model = 0; psmouse->model = 0;
if (psmouse_noext)
return PSMOUSE_PS2;
/* /*
* Try Genius NetMouse magic init. * Try Genius NetMouse magic init.
*/ */
...@@ -529,15 +540,24 @@ static void psmouse_initialize(struct psmouse *psmouse) ...@@ -529,15 +540,24 @@ static void psmouse_initialize(struct psmouse *psmouse)
* Last, we enable the mouse so that we get reports from it. * Last, we enable the mouse so that we get reports from it.
*/ */
if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE)) { if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE))
printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", psmouse->serio->phys); printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", psmouse->serio->phys);
}
} }
/* /*
* psmouse_disconnect() cleans up after we don't want talk * psmouse_cleanup() resets the mouse into power-on state.
* to the mouse anymore. */
static void psmouse_cleanup(struct serio *serio)
{
struct psmouse *psmouse = serio->private;
unsigned char param[2];
psmouse_command(psmouse, param, PSMOUSE_CMD_RESET_BAT);
}
/*
* psmouse_disconnect() closes and frees.
*/ */
static void psmouse_disconnect(struct serio *serio) static void psmouse_disconnect(struct serio *serio)
...@@ -607,9 +627,19 @@ static void psmouse_connect(struct serio *serio, struct serio_dev *dev) ...@@ -607,9 +627,19 @@ static void psmouse_connect(struct serio *serio, struct serio_dev *dev)
static struct serio_dev psmouse_dev = { static struct serio_dev psmouse_dev = {
.interrupt = psmouse_interrupt, .interrupt = psmouse_interrupt,
.connect = psmouse_connect, .connect = psmouse_connect,
.disconnect = psmouse_disconnect .disconnect = psmouse_disconnect,
.cleanup = psmouse_cleanup,
}; };
#ifndef MODULE
static int __init psmouse_setup(char *str)
{
psmouse_noext = 1;
return 1;
}
__setup("psmouse_noext", psmouse_setup);
#endif
int __init psmouse_init(void) int __init psmouse_init(void)
{ {
serio_register_device(&psmouse_dev); serio_register_device(&psmouse_dev);
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#define I8042_KBD_PHYS_DESC "isa0060/serio0" #define I8042_KBD_PHYS_DESC "isa0060/serio0"
#define I8042_AUX_PHYS_DESC "isa0060/serio1" #define I8042_AUX_PHYS_DESC "isa0060/serio1"
#define I8042_MUX_PHYS_DESC "isa0060/serio%d"
/* /*
* IRQs. * IRQs.
...@@ -64,9 +65,13 @@ static inline int i8042_platform_init(void) ...@@ -64,9 +65,13 @@ static inline int i8042_platform_init(void)
*/ */
#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__) && !defined(__x86_64__) #if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__) && !defined(__x86_64__)
if (!request_region(I8042_DATA_REG, 16, "i8042")) if (!request_region(I8042_DATA_REG, 16, "i8042"))
return 0; return -1;
#endif #endif
return 1;
#if !defined(__i386__) && !defined(__x86_64__)
i8042_reset = 1;
#endif
return 0;
} }
static inline void i8042_platform_exit(void) static inline void i8042_platform_exit(void)
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#define I8042_KBD_PHYS_DESC "walnutps2/serio0" #define I8042_KBD_PHYS_DESC "walnutps2/serio0"
#define I8042_AUX_PHYS_DESC "walnutps2/serio1" #define I8042_AUX_PHYS_DESC "walnutps2/serio1"
#define I8042_MUX_PHYS_DESC "walnutps2/serio%d"
extern void *kb_cs; extern void *kb_cs;
extern void *kb_data; extern void *kb_data;
...@@ -34,18 +35,17 @@ static inline int i8042_read_status(void) ...@@ -34,18 +35,17 @@ static inline int i8042_read_status(void)
static inline void i8042_write_data(int val) static inline void i8042_write_data(int val)
{ {
writeb(val, kb_data); writeb(val, kb_data);
return;
} }
static inline void i8042_write_command(int val) static inline void i8042_write_command(int val)
{ {
writeb(val, kb_cs); writeb(val, kb_cs);
return;
} }
static inline int i8042_platform_init(void) static inline int i8042_platform_init(void)
{ {
return 1; i8042_reset = 1;
return 0;
} }
static inline void i8042_platform_exit(void) static inline void i8042_platform_exit(void)
...@@ -59,6 +59,7 @@ static inline void i8042_platform_exit(void) ...@@ -59,6 +59,7 @@ static inline void i8042_platform_exit(void)
#define I8042_KBD_PHYS_DESC "spruceps2/serio0" #define I8042_KBD_PHYS_DESC "spruceps2/serio0"
#define I8042_AUX_PHYS_DESC "spruceps2/serio1" #define I8042_AUX_PHYS_DESC "spruceps2/serio1"
#define I8042_MUX_PHYS_DESC "spruceps2/serio%d"
#define I8042_COMMAND_REG 0xff810000 #define I8042_COMMAND_REG 0xff810000
#define I8042_DATA_REG 0xff810001 #define I8042_DATA_REG 0xff810001
...@@ -109,18 +110,17 @@ static inline int i8042_read_status(void) ...@@ -109,18 +110,17 @@ static inline int i8042_read_status(void)
static inline void i8042_write_data(int val) static inline void i8042_write_data(int val)
{ {
*((unsigned char *)0xff810000) = (char)val; *((unsigned char *)0xff810000) = (char)val;
return;
} }
static inline void i8042_write_command(int val) static inline void i8042_write_command(int val)
{ {
*((unsigned char *)0xff810001) = (char)val; *((unsigned char *)0xff810001) = (char)val;
return;
} }
static inline int i8042_platform_init(void) static inline int i8042_platform_init(void)
{ {
return 1; i8042_reset = 1;
return 0;
} }
static inline void i8042_platform_exit(void) static inline void i8042_platform_exit(void)
......
...@@ -11,6 +11,7 @@ static int i8042_aux_irq = -1; ...@@ -11,6 +11,7 @@ static int i8042_aux_irq = -1;
#define I8042_KBD_PHYS_DESC "sparcps2/serio0" #define I8042_KBD_PHYS_DESC "sparcps2/serio0"
#define I8042_AUX_PHYS_DESC "sparcps2/serio1" #define I8042_AUX_PHYS_DESC "sparcps2/serio1"
#define I8042_MUX_PHYS_DESC "sparcps2/serio%d"
static unsigned long kbd_iobase; static unsigned long kbd_iobase;
...@@ -50,14 +51,14 @@ static int i8042_platform_init(void) ...@@ -50,14 +51,14 @@ static int i8042_platform_init(void)
len = prom_getproperty(prom_root_node, "name", prop, sizeof(prop)); len = prom_getproperty(prom_root_node, "name", prop, sizeof(prop));
if (len < 0) { if (len < 0) {
printk("i8042: Cannot get name property of root OBP node.\n"); printk("i8042: Cannot get name property of root OBP node.\n");
return 0; return -1;
} }
if (strncmp(prop, "SUNW,JavaStation-1", len) == 0) { if (strncmp(prop, "SUNW,JavaStation-1", len) == 0) {
/* Hardcoded values for MrCoffee. */ /* Hardcoded values for MrCoffee. */
i8042_kbd_irq = i8042_aux_irq = 13 | 0x20; i8042_kbd_irq = i8042_aux_irq = 13 | 0x20;
kbd_iobase = (unsigned long) ioremap(0x71300060, 8); kbd_iobase = (unsigned long) ioremap(0x71300060, 8);
if (!kbd_iobase) if (!kbd_iobase)
return 0; return -1;
} else { } else {
struct linux_ebus *ebus; struct linux_ebus *ebus;
struct linux_ebus_device *edev; struct linux_ebus_device *edev;
...@@ -69,7 +70,7 @@ static int i8042_platform_init(void) ...@@ -69,7 +70,7 @@ static int i8042_platform_init(void)
goto edev_found; goto edev_found;
} }
} }
return 0; return -1;
edev_found: edev_found:
for_each_edevchild(edev, child) { for_each_edevchild(edev, child) {
...@@ -87,11 +88,13 @@ static int i8042_platform_init(void) ...@@ -87,11 +88,13 @@ static int i8042_platform_init(void)
i8042_aux_irq == -1) { i8042_aux_irq == -1) {
printk("i8042: Error, 8042 device lacks both kbd and " printk("i8042: Error, 8042 device lacks both kbd and "
"mouse nodes.\n"); "mouse nodes.\n");
return 0; return -1;
} }
} }
return 1; i8042_reset = 1;
return 0;
} }
static inline void i8042_platform_exit(void) static inline void i8042_platform_exit(void)
......
...@@ -19,7 +19,9 @@ ...@@ -19,7 +19,9 @@
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/serio.h> #include <linux/serio.h>
#include <linux/sched.h> /* request/free_irq */ #include <linux/sched.h>
#undef DEBUG
#include "i8042.h" #include "i8042.h"
...@@ -31,13 +33,13 @@ MODULE_PARM(i8042_noaux, "1i"); ...@@ -31,13 +33,13 @@ MODULE_PARM(i8042_noaux, "1i");
MODULE_PARM(i8042_unlock, "1i"); MODULE_PARM(i8042_unlock, "1i");
MODULE_PARM(i8042_reset, "1i"); MODULE_PARM(i8042_reset, "1i");
MODULE_PARM(i8042_direct, "1i"); MODULE_PARM(i8042_direct, "1i");
MODULE_PARM(i8042_restore_ctr, "1i"); MODULE_PARM(i8042_dumbkbd, "1i");
static int i8042_noaux; static int i8042_noaux;
static int i8042_unlock; static int i8042_unlock;
static int i8042_reset; static int i8042_reset;
static int i8042_direct; static int i8042_direct;
static int i8042_restore_ctr; static int i8042_dumbkbd;
spinlock_t i8042_lock = SPIN_LOCK_UNLOCKED; spinlock_t i8042_lock = SPIN_LOCK_UNLOCKED;
...@@ -46,6 +48,7 @@ struct i8042_values { ...@@ -46,6 +48,7 @@ struct i8042_values {
unsigned char disable; unsigned char disable;
unsigned char irqen; unsigned char irqen;
unsigned char exists; unsigned char exists;
signed char mux;
unsigned char *name; unsigned char *name;
unsigned char *phys; unsigned char *phys;
}; };
...@@ -55,12 +58,9 @@ static struct serio i8042_aux_port; ...@@ -55,12 +58,9 @@ static struct serio i8042_aux_port;
static unsigned char i8042_initial_ctr; static unsigned char i8042_initial_ctr;
static unsigned char i8042_ctr; static unsigned char i8042_ctr;
static unsigned char i8042_last_e0; static unsigned char i8042_last_e0;
static unsigned char i8042_mux_open;
struct timer_list i8042_timer; struct timer_list i8042_timer;
#ifdef I8042_DEBUG_IO
static unsigned long i8042_start;
#endif
extern struct pt_regs *kbd_pt_regs; extern struct pt_regs *kbd_pt_regs;
static unsigned long i8042_unxlate_seen[128 / BITS_PER_LONG]; static unsigned long i8042_unxlate_seen[128 / BITS_PER_LONG];
...@@ -110,18 +110,16 @@ static int i8042_wait_write(void) ...@@ -110,18 +110,16 @@ static int i8042_wait_write(void)
static int i8042_flush(void) static int i8042_flush(void)
{ {
unsigned long flags; unsigned long flags;
unsigned char data;
int i = 0; int i = 0;
spin_lock_irqsave(&i8042_lock, flags); spin_lock_irqsave(&i8042_lock, flags);
while ((i8042_read_status() & I8042_STR_OBF) && (i++ < I8042_BUFFER_SIZE)) while ((i8042_read_status() & I8042_STR_OBF) && (i++ < I8042_BUFFER_SIZE)) {
#ifdef I8042_DEBUG_IO data = i8042_read_data();
printk(KERN_DEBUG "i8042.c: %02x <- i8042 (flush, %s) [%d]\n", dbg("%02x <- i8042 (flush, %s)", data,
i8042_read_data(), i8042_read_status() & I8042_STR_AUXDATA ? "aux" : "kbd", i8042_read_status() & I8042_STR_AUXDATA ? "aux" : "kbd");
(int) (jiffies - i8042_start)); }
#else
i8042_read_data();
#endif
spin_unlock_irqrestore(&i8042_lock, flags); spin_unlock_irqrestore(&i8042_lock, flags);
...@@ -145,20 +143,14 @@ static int i8042_command(unsigned char *param, int command) ...@@ -145,20 +143,14 @@ static int i8042_command(unsigned char *param, int command)
retval = i8042_wait_write(); retval = i8042_wait_write();
if (!retval) { if (!retval) {
#ifdef I8042_DEBUG_IO dbg("%02x -> i8042 (command)", command & 0xff);
printk(KERN_DEBUG "i8042.c: %02x -> i8042 (command) [%d]\n",
command & 0xff, (int) (jiffies - i8042_start));
#endif
i8042_write_command(command & 0xff); i8042_write_command(command & 0xff);
} }
if (!retval) if (!retval)
for (i = 0; i < ((command >> 12) & 0xf); i++) { for (i = 0; i < ((command >> 12) & 0xf); i++) {
if ((retval = i8042_wait_write())) break; if ((retval = i8042_wait_write())) break;
#ifdef I8042_DEBUG_IO dbg("%02x -> i8042 (parameter)", param[i]);
printk(KERN_DEBUG "i8042.c: %02x -> i8042 (parameter) [%d]\n",
param[i], (int) (jiffies - i8042_start));
#endif
i8042_write_data(param[i]); i8042_write_data(param[i]);
} }
...@@ -169,19 +161,13 @@ static int i8042_command(unsigned char *param, int command) ...@@ -169,19 +161,13 @@ static int i8042_command(unsigned char *param, int command)
param[i] = ~i8042_read_data(); param[i] = ~i8042_read_data();
else else
param[i] = i8042_read_data(); param[i] = i8042_read_data();
#ifdef I8042_DEBUG_IO dbg("%02x <- i8042 (return)\n", param[i]);
printk(KERN_DEBUG "i8042.c: %02x <- i8042 (return) [%d]\n",
param[i], (int) (jiffies - i8042_start));
#endif
} }
spin_unlock_irqrestore(&i8042_lock, flags); spin_unlock_irqrestore(&i8042_lock, flags);
#ifdef I8042_DEBUG_IO
if (retval) if (retval)
printk(KERN_DEBUG "i8042.c: -- i8042 (timeout) [%d]\n", dbg(" -- i8042 (timeout)");
(int) (jiffies - i8042_start));
#endif
return retval; return retval;
} }
...@@ -198,10 +184,7 @@ static int i8042_kbd_write(struct serio *port, unsigned char c) ...@@ -198,10 +184,7 @@ static int i8042_kbd_write(struct serio *port, unsigned char c)
spin_lock_irqsave(&i8042_lock, flags); spin_lock_irqsave(&i8042_lock, flags);
if(!(retval = i8042_wait_write())) { if(!(retval = i8042_wait_write())) {
#ifdef I8042_DEBUG_IO dbg("%02x -> i8042 (kbd-data)", c);
printk(KERN_DEBUG "i8042.c: %02x -> i8042 (kbd-data) [%d]\n",
c, (int) (jiffies - i8042_start));
#endif
i8042_write_data(c); i8042_write_data(c);
} }
...@@ -216,21 +199,17 @@ static int i8042_kbd_write(struct serio *port, unsigned char c) ...@@ -216,21 +199,17 @@ static int i8042_kbd_write(struct serio *port, unsigned char c)
static int i8042_aux_write(struct serio *port, unsigned char c) static int i8042_aux_write(struct serio *port, unsigned char c)
{ {
struct i8042_values *values = port->driver;
int retval; int retval;
/* /*
* Send the byte out. * Send the byte out.
*/ */
retval = i8042_command(&c, I8042_CMD_AUX_SEND); if (values->mux == -1)
retval = i8042_command(&c, I8042_CMD_AUX_SEND);
/* else
* Here we restore the CTR value if requested. I don't know why, but i8042's in retval = i8042_command(&c, I8042_CMD_MUX_SEND + values->mux);
* half-AT mode tend to trash their CTR when doing the AUX_SEND command.
*/
if (i8042_restore_ctr)
retval |= i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR);
/* /*
* Make sure the interrupt happens and the character is received even * Make sure the interrupt happens and the character is received even
...@@ -242,6 +221,7 @@ static int i8042_aux_write(struct serio *port, unsigned char c) ...@@ -242,6 +221,7 @@ static int i8042_aux_write(struct serio *port, unsigned char c)
return retval; return retval;
} }
/* /*
* i8042_open() is called when a port is open by the higher layer. * i8042_open() is called when a port is open by the higher layer.
* It allocates the interrupt and enables in in the chip. * It allocates the interrupt and enables in in the chip.
...@@ -253,6 +233,10 @@ static int i8042_open(struct serio *port) ...@@ -253,6 +233,10 @@ static int i8042_open(struct serio *port)
i8042_flush(); i8042_flush();
if (values->mux != -1)
if (i8042_mux_open++)
return 0;
if (request_irq(values->irq, i8042_interrupt, 0, "i8042", NULL)) { if (request_irq(values->irq, i8042_interrupt, 0, "i8042", NULL)) {
printk(KERN_ERR "i8042.c: Can't get irq %d for %s, unregistering the port.\n", values->irq, values->name); printk(KERN_ERR "i8042.c: Can't get irq %d for %s, unregistering the port.\n", values->irq, values->name);
values->exists = 0; values->exists = 0;
...@@ -282,6 +266,10 @@ static void i8042_close(struct serio *port) ...@@ -282,6 +266,10 @@ static void i8042_close(struct serio *port)
{ {
struct i8042_values *values = port->driver; struct i8042_values *values = port->driver;
if (values->mux != -1)
if (--i8042_mux_open)
return;
i8042_ctr &= ~values->irqen; i8042_ctr &= ~values->irqen;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
...@@ -299,11 +287,11 @@ static void i8042_close(struct serio *port) ...@@ -299,11 +287,11 @@ static void i8042_close(struct serio *port)
*/ */
static struct i8042_values i8042_kbd_values = { static struct i8042_values i8042_kbd_values = {
.irq = 0, .irq = I8042_KBD_IRQ,
.irqen = I8042_CTR_KBDINT, .irqen = I8042_CTR_KBDINT,
.disable = I8042_CTR_KBDDIS, .disable = I8042_CTR_KBDDIS,
.name = "KBD", .name = "KBD",
.exists = 0, .mux = -1,
}; };
static struct serio i8042_kbd_port = static struct serio i8042_kbd_port =
...@@ -318,11 +306,11 @@ static struct serio i8042_kbd_port = ...@@ -318,11 +306,11 @@ static struct serio i8042_kbd_port =
}; };
static struct i8042_values i8042_aux_values = { static struct i8042_values i8042_aux_values = {
.irq = 0, .irq = I8042_AUX_IRQ,
.irqen = I8042_CTR_AUXINT, .irqen = I8042_CTR_AUXINT,
.disable = I8042_CTR_AUXDIS, .disable = I8042_CTR_AUXDIS,
.name = "AUX", .name = "AUX",
.exists = 0, .mux = -1,
}; };
static struct serio i8042_aux_port = static struct serio i8042_aux_port =
...@@ -336,6 +324,12 @@ static struct serio i8042_aux_port = ...@@ -336,6 +324,12 @@ static struct serio i8042_aux_port =
.phys = I8042_AUX_PHYS_DESC, .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][16];
static char i8042_mux_short[4][8];
static char i8042_mux_phys[4][32];
/* /*
* i8042_interrupt() is the most important function in this driver - * i8042_interrupt() is the most important function in this driver -
* it handles the interrupts from the i8042, and sends incoming bytes * it handles the interrupts from the i8042, and sends incoming bytes
...@@ -373,34 +367,60 @@ static void i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -373,34 +367,60 @@ static void i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) | dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) |
((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0); ((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0);
#ifdef I8042_DEBUG_IO if (i8042_mux_values[0].exists && (buffer[i].str & I8042_STR_AUXDATA)) {
printk(KERN_DEBUG "i8042.c: %02x <- i8042 (interrupt, %s, %d%s%s) [%d]\n",
if (buffer[i].str & I8042_STR_MUXERR) {
switch (buffer[i].data) {
case 0xfd:
case 0xfe: dfl = SERIO_TIMEOUT; break;
case 0xff: dfl = SERIO_PARITY; break;
}
buffer[i].data = 0xfe;
} else dfl = 0;
dbg("%02x <- i8042 (interrupt, aux%d, %d%s%s)",
data, (str >> 6), irq,
dfl & SERIO_PARITY ? ", bad parity" : "",
dfl & SERIO_TIMEOUT ? ", timeout" : "");
if (i8042_mux_values[(str >> 6)].exists)
serio_interrupt(i8042_mux_port + (str >> 6), buffer[i].data, dfl);
continue;
}
dbg("%02x <- i8042 (interrupt, %s, %d%s%s)",
data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", irq, data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", irq,
dfl & SERIO_PARITY ? ", bad parity" : "", dfl & SERIO_PARITY ? ", bad parity" : "",
dfl & SERIO_TIMEOUT ? ", timeout" : "", dfl & SERIO_TIMEOUT ? ", timeout" : "");
(int) (jiffies - i8042_start));
#endif
if (i8042_aux_values.exists && (buffer[i].str & I8042_STR_AUXDATA)) { if (i8042_aux_values.exists && (buffer[i].str & I8042_STR_AUXDATA)) {
serio_interrupt(&i8042_aux_port, buffer[i].data, dfl); serio_interrupt(&i8042_aux_port, buffer[i].data, dfl);
} else continue;
if (i8042_kbd_values.exists) { }
if (!i8042_direct) {
if (data > 0x7f) { if (!i8042_kbd_values.exists)
if (test_and_clear_bit(data & 0x7f, i8042_unxlate_seen)) { continue;
serio_interrupt(&i8042_kbd_port, 0xf0, dfl);
if (i8042_last_e0 && (data == 0xaa || data == 0xb6)) if (i8042_direct) {
set_bit(data & 0x7f, i8042_unxlate_seen); serio_interrupt(&i8042_kbd_port, data, dfl);
data = i8042_unxlate_table[data & 0x7f]; continue;
} }
} else {
set_bit(data, i8042_unxlate_seen); if (data > 0x7f) {
data = i8042_unxlate_table[data]; if (test_and_clear_bit(data & 0x7f, i8042_unxlate_seen)) {
} serio_interrupt(&i8042_kbd_port, 0xf0, dfl);
i8042_last_e0 = (data == 0xe0); if (i8042_last_e0 && (data == 0xaa || data == 0xb6))
} set_bit(data & 0x7f, i8042_unxlate_seen);
serio_interrupt(&i8042_kbd_port, data, dfl); data = i8042_unxlate_table[data & 0x7f];
} }
} else {
set_bit(data, i8042_unxlate_seen);
data = i8042_unxlate_table[data];
}
i8042_last_e0 = (data == 0xe0);
serio_interrupt(&i8042_kbd_port, data, dfl);
} }
} }
...@@ -460,17 +480,15 @@ static int __init i8042_controller_init(void) ...@@ -460,17 +480,15 @@ static int __init i8042_controller_init(void)
*/ */
if (~i8042_read_status() & I8042_STR_KEYLOCK) { if (~i8042_read_status() & I8042_STR_KEYLOCK) {
if (i8042_unlock)
if (i8042_unlock) {
i8042_ctr |= I8042_CTR_IGNKEYLOCK; i8042_ctr |= I8042_CTR_IGNKEYLOCK;
} else { else
printk(KERN_WARNING "i8042.c: Warning: Keylock active.\n"); printk(KERN_WARNING "i8042.c: Warning: Keylock active.\n");
}
} }
/* /*
* If the chip is configured into nontranslated mode by the BIOS, don't * If the chip is configured into nontranslated mode by the BIOS, don't
* bother enabling translating and just use that happily. * bother enabling translating and be happy.
*/ */
if (~i8042_ctr & I8042_CTR_XLATE) if (~i8042_ctr & I8042_CTR_XLATE)
...@@ -478,10 +496,9 @@ static int __init i8042_controller_init(void) ...@@ -478,10 +496,9 @@ static int __init i8042_controller_init(void)
/* /*
* Set nontranslated mode for the kbd interface if requested by an option. * Set nontranslated mode for the kbd interface if requested by an option.
* This is vital for a working scancode set 3 support. After this the kbd * After this the kbd interface becomes a simple serial in/out, like the aux
* interface becomes a simple serial in/out, like the aux interface is. If * interface is. We don't do this by default, since it can confuse notebook
* the user doesn't wish this, the driver tries to untranslate the values * BIOSes.
* after the i8042 translates them.
*/ */
if (i8042_direct) if (i8042_direct)
...@@ -506,9 +523,24 @@ static int __init i8042_controller_init(void) ...@@ -506,9 +523,24 @@ static int __init i8042_controller_init(void)
void i8042_controller_cleanup(void) void i8042_controller_cleanup(void)
{ {
int i;
i8042_flush(); i8042_flush();
/*
* Reset anything that is connected to the ports.
*/
if (i8042_kbd_values.exists)
serio_cleanup(&i8042_kbd_port);
if (i8042_aux_values.exists)
serio_cleanup(&i8042_aux_port);
for (i = 0; i < 4; i++)
if (i8042_mux_values[i].exists)
serio_cleanup(i8042_mux_port + i);
/* /*
* Reset the controller. * Reset the controller.
*/ */
...@@ -529,16 +561,72 @@ void i8042_controller_cleanup(void) ...@@ -529,16 +561,72 @@ void i8042_controller_cleanup(void)
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
printk(KERN_WARNING "i8042.c: Can't restore CTR.\n"); printk(KERN_WARNING "i8042.c: Can't restore CTR.\n");
}
/* /*
* Reset anything that is connected to the ports if the ports * i8042_check_mux() checks whether the controller supports the PS/2 Active
* are enabled in the original config. * Multiplexing specification by Synaptics, Phoenix, Insyde and
* LCS/Telegraphics.
*/ */
if (i8042_kbd_values.exists) static int __init i8042_check_mux(struct i8042_values *values)
i8042_kbd_write(&i8042_kbd_port, 0xff); {
unsigned char param;
int i;
if (i8042_aux_values.exists) /*
i8042_aux_write(&i8042_aux_port, 0xff); * Check if AUX irq is available.
*/
if (request_irq(values->irq, i8042_interrupt, 0, "i8042", NULL))
return -1;
free_irq(values->irq, NULL);
/*
* Get rid of bytes in the queue.
*/
i8042_flush();
/*
* Internal loopback test - send three bytes, they should come back from the
* mouse interface, the last should be version. Note that we negate mouseport
* command responses for the i8042_check_aux() routine.
*/
param = 0xf0;
if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != 0x0f)
return -1;
param = 0x56;
if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != 0xa9)
return -1;
param = 0xa4;
if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param == 0x5b)
return -1;
printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev%d.%d.\n",
~param >> 4, ~param & 0xf);
/*
* Disable all muxed ports by disabling AUX.
*/
i8042_ctr &= I8042_CTR_AUXDIS;
i8042_ctr &= ~I8042_CTR_AUXINT;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
return -1;
/*
* Enable all muxed ports.
*/
for (i = 0; i < 4; i++) {
i8042_command(&param, I8042_CMD_MUX_PFX + i);
i8042_command(&param, I8042_CMD_AUX_ENABLE);
}
return 0;
} }
/* /*
...@@ -546,7 +634,7 @@ void i8042_controller_cleanup(void) ...@@ -546,7 +634,7 @@ void i8042_controller_cleanup(void)
* the presence of an AUX interface. * the presence of an AUX interface.
*/ */
static int __init i8042_check_aux(struct i8042_values *values, struct serio *port) static int __init i8042_check_aux(struct i8042_values *values)
{ {
unsigned char param; unsigned char param;
...@@ -570,7 +658,6 @@ static int __init i8042_check_aux(struct i8042_values *values, struct serio *por ...@@ -570,7 +658,6 @@ static int __init i8042_check_aux(struct i8042_values *values, struct serio *por
*/ */
param = 0x5a; param = 0x5a;
if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != 0xa5) if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != 0xa5)
return -1; return -1;
...@@ -665,9 +752,9 @@ static int __init i8042_setup_direct(char *str) ...@@ -665,9 +752,9 @@ static int __init i8042_setup_direct(char *str)
i8042_direct = 1; i8042_direct = 1;
return 1; return 1;
} }
static int __init i8042_setup_restore_ctr(char *str) static int __init i8042_setup_dumbkbd(char *str)
{ {
i8042_restore_ctr = 1; i8042_dumbkbd = 1;
return 1; return 1;
} }
...@@ -675,7 +762,7 @@ __setup("i8042_reset", i8042_setup_reset); ...@@ -675,7 +762,7 @@ __setup("i8042_reset", i8042_setup_reset);
__setup("i8042_noaux", i8042_setup_noaux); __setup("i8042_noaux", i8042_setup_noaux);
__setup("i8042_unlock", i8042_setup_unlock); __setup("i8042_unlock", i8042_setup_unlock);
__setup("i8042_direct", i8042_setup_direct); __setup("i8042_direct", i8042_setup_direct);
__setup("i8042_restore_ctr", i8042_setup_restore_ctr); __setup("i8042_dumbkbd", i8042_setup_dumbkbd);
#endif #endif
/* /*
...@@ -698,27 +785,43 @@ static struct notifier_block i8042_notifier= ...@@ -698,27 +785,43 @@ static struct notifier_block i8042_notifier=
0 0
}; };
static void __init i8042_init_mux_values(struct i8042_values *values, struct serio *port, int index)
{
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];
values->name = i8042_mux_short[index];
values->mux = index;
}
int __init i8042_init(void) int __init i8042_init(void)
{ {
#ifdef I8042_DEBUG_IO int i;
i8042_start = jiffies;
#endif
#if !defined(__i386__) && !defined(__x86_64__) dbg_init();
i8042_reset = 1;
#endif
if (!i8042_platform_init()) if (i8042_platform_init())
return -EBUSY; return -EBUSY;
i8042_kbd_values.irq = I8042_KBD_IRQ;
i8042_aux_values.irq = I8042_AUX_IRQ;
if (i8042_controller_init()) if (i8042_controller_init())
return -ENODEV; return -ENODEV;
if (!i8042_noaux && !i8042_check_aux(&i8042_aux_values, &i8042_aux_port)) if (i8042_dumbkbd)
i8042_port_register(&i8042_aux_values, &i8042_aux_port); i8042_kbd_port.write = NULL;
for (i = 0; i < 4; i++)
i8042_init_mux_values(i8042_mux_values + i, i8042_mux_port + i, i);
if (!i8042_noaux && !i8042_check_mux(&i8042_aux_values))
for (i = 0; i < 4; i++)
i8042_port_register(i8042_mux_values + i, i8042_mux_port + i);
else
if (!i8042_noaux && !i8042_check_aux(&i8042_aux_values))
i8042_port_register(&i8042_aux_values, &i8042_aux_port);
i8042_port_register(&i8042_kbd_values, &i8042_kbd_port); i8042_port_register(&i8042_kbd_values, &i8042_kbd_port);
...@@ -732,22 +835,26 @@ int __init i8042_init(void) ...@@ -732,22 +835,26 @@ int __init i8042_init(void)
void __exit i8042_exit(void) void __exit i8042_exit(void)
{ {
int i;
unregister_reboot_notifier(&i8042_notifier); unregister_reboot_notifier(&i8042_notifier);
del_timer(&i8042_timer); del_timer(&i8042_timer);
i8042_controller_cleanup();
if (i8042_kbd_values.exists) if (i8042_kbd_values.exists)
serio_unregister_port(&i8042_kbd_port); serio_unregister_port(&i8042_kbd_port);
if (i8042_aux_values.exists) if (i8042_aux_values.exists)
serio_unregister_port(&i8042_aux_port); serio_unregister_port(&i8042_aux_port);
i8042_controller_cleanup(); for (i = 0; i < 4; i++)
if (i8042_mux_values[i].exists)
serio_unregister_port(i8042_mux_port + i);
i8042_platform_exit(); i8042_platform_exit();
} }
module_init(i8042_init); module_init(i8042_init);
module_exit(i8042_exit); module_exit(i8042_exit);
...@@ -21,13 +21,6 @@ ...@@ -21,13 +21,6 @@
#include "i8042-io.h" #include "i8042-io.h"
#endif #endif
/*
* If you want to trace all the i/o the i8042 module does for
* debugging purposes, define this.
*/
#undef I8042_DEBUG_IO
/* /*
* This is in 50us units, the time we wait for the i8042 to react. This * This is in 50us units, the time we wait for the i8042 to react. This
* has to be long enough for the i8042 itself to timeout on sending a byte * has to be long enough for the i8042 itself to timeout on sending a byte
...@@ -54,6 +47,7 @@ ...@@ -54,6 +47,7 @@
#define I8042_STR_AUXDATA 0x20 #define I8042_STR_AUXDATA 0x20
#define I8042_STR_KEYLOCK 0x10 #define I8042_STR_KEYLOCK 0x10
#define I8042_STR_CMDDAT 0x08 #define I8042_STR_CMDDAT 0x08
#define I8042_STR_MUXERR 0x04
#define I8042_STR_IBF 0x02 #define I8042_STR_IBF 0x02
#define I8042_STR_OBF 0x01 #define I8042_STR_OBF 0x01
...@@ -87,6 +81,9 @@ ...@@ -87,6 +81,9 @@
#define I8042_CMD_AUX_SEND 0x10d4 #define I8042_CMD_AUX_SEND 0x10d4
#define I8042_CMD_AUX_LOOP 0x11d3 #define I8042_CMD_AUX_LOOP 0x11d3
#define I8042_CMD_MUX_PFX 0x0090
#define I8042_CMD_MUX_SEND 0x1090
/* /*
* Return codes. * Return codes.
*/ */
...@@ -100,4 +97,18 @@ ...@@ -100,4 +97,18 @@
#define I8042_BUFFER_SIZE 32 #define I8042_BUFFER_SIZE 32
/*
* Debug.
*/
#ifdef DEBUG
static unsigned long i8042_start;
#define dbg_init() do { i8042_start = jiffies; } while (0);
#define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "[%d]\n" ,\
## arg, (int) (jiffies - i8042_start))
#else
#define dbg_init() do { } while (0);
#define dbg(format, arg...) do {} while (0)
#endif
#endif /* _I8042_H */ #endif /* _I8042_H */
...@@ -69,6 +69,7 @@ struct serio_dev { ...@@ -69,6 +69,7 @@ struct serio_dev {
void (*interrupt)(struct serio *, unsigned char, unsigned int); void (*interrupt)(struct serio *, unsigned char, unsigned int);
void (*connect)(struct serio *, struct serio_dev *dev); void (*connect)(struct serio *, struct serio_dev *dev);
void (*disconnect)(struct serio *); void (*disconnect)(struct serio *);
void (*cleanup)(struct serio *);
struct serio_dev *next; struct serio_dev *next;
}; };
...@@ -85,7 +86,10 @@ void serio_unregister_device(struct serio_dev *dev); ...@@ -85,7 +86,10 @@ void serio_unregister_device(struct serio_dev *dev);
static __inline__ int serio_write(struct serio *serio, unsigned char data) static __inline__ int serio_write(struct serio *serio, unsigned char data)
{ {
return serio->write(serio, data); if (serio->write)
return serio->write(serio, data);
else
return -1;
} }
static __inline__ void serio_dev_write_wakeup(struct serio *serio) static __inline__ void serio_dev_write_wakeup(struct serio *serio)
...@@ -94,6 +98,12 @@ static __inline__ void serio_dev_write_wakeup(struct serio *serio) ...@@ -94,6 +98,12 @@ static __inline__ void serio_dev_write_wakeup(struct serio *serio)
serio->dev->write_wakeup(serio); serio->dev->write_wakeup(serio);
} }
static __inline__ void serio_cleanup(struct serio *serio)
{
if (serio->dev && serio->dev->cleanup)
serio->dev->cleanup(serio);
}
/* /*
* bit masks for use in "interrupt" flags (3rd argument) * bit masks for use in "interrupt" flags (3rd argument)
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment