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 @@
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("AT and PS/2 keyboard driver");
MODULE_PARM(atkbd_set, "1i");
MODULE_PARM(atkbd_reset, "1i");
MODULE_LICENSE("GPL");
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
......@@ -89,6 +95,7 @@ static unsigned char atkbd_set3_keycode[512] = {
#define ATKBD_CMD_RESEND 0x00fe
#define ATKBD_CMD_EX_ENABLE 0x10ea
#define ATKBD_CMD_EX_SETLEDS 0x20eb
#define ATKBD_CMD_OK_GETID 0x02e8
#define ATKBD_RET_ACK 0xfa
#define ATKBD_RET_NAK 0xfe
......@@ -113,6 +120,7 @@ struct atkbd {
unsigned char cmdbuf[4];
unsigned char cmdcnt;
unsigned char set;
unsigned char oldset;
unsigned char release;
signed char ack;
unsigned char emul;
......@@ -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);
#endif
/* Interface error. Request that the keyboard resend. */
if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && atkbd->write) {
printk("atkbd.c: frame/parity error: %02x\n", flags);
serio_write(serio, ATKBD_CMD_RESEND);
......@@ -205,7 +212,9 @@ static int atkbd_sendbyte(struct atkbd *atkbd, unsigned char byte)
#ifdef ATKBD_DEBUG
printk(KERN_DEBUG "atkbd.c: Sent: %02x\n", byte);
#endif
serio_write(atkbd->serio, byte);
if (serio_write(atkbd->serio, byte))
return -1;
while (!atkbd->ack && timeout--) udelay(10);
return -(atkbd->ack <= 0);
......@@ -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)
{
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.
* We check for NCD PS/2 Sun, NorthGate OmniKey 101 and IBM RapidAccess
* keyboards.
*/
if (atkbd->id == 0xaca1) {
param = 3;
atkbd_command(atkbd, &param, ATKBD_CMD_SSCANSET);
param[0] = 3;
atkbd_command(atkbd, param, ATKBD_CMD_SSCANSET);
return 3;
}
/*
* We check for the extra keys on an some keyboards that need extra
* command to get enabled. This shouldn't harm any keyboards not
* knowing the command.
*/
if (!atkbd_command(atkbd, param, ATKBD_CMD_OK_GETID)) {
atkbd->id = param[0] << 8 | param[1];
return 2;
}
param = 0x71;
if (!atkbd_command(atkbd, &param, ATKBD_CMD_EX_ENABLE))
param[0] = 0x71;
if (!atkbd_command(atkbd, param, ATKBD_CMD_EX_ENABLE))
return 4;
/*
* Try to set the set we want.
*/
param = atkbd_set;
if (atkbd_command(atkbd, &param, ATKBD_CMD_SSCANSET))
if (atkbd_command(atkbd, &atkbd_set, ATKBD_CMD_SSCANSET))
return 2;
/*
......@@ -327,8 +343,8 @@ static int atkbd_set_3(struct atkbd *atkbd)
* In that case we time out, and return 2.
*/
param = 0;
if (atkbd_command(atkbd, &param, ATKBD_CMD_GSCANSET))
param[0] = 0;
if (atkbd_command(atkbd, param, ATKBD_CMD_GSCANSET))
return 2;
/*
......@@ -336,7 +352,7 @@ static int atkbd_set_3(struct atkbd *atkbd)
* itself.
*/
return (param == 3) ? 3 : 2;
return (param[0] == 3) ? 3 : 2;
}
/*
......@@ -353,10 +369,9 @@ static int atkbd_probe(struct atkbd *atkbd)
* 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))
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
......@@ -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)
......@@ -508,18 +534,28 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
static struct serio_dev atkbd_dev = {
.interrupt = atkbd_interrupt,
.connect = atkbd_connect,
.disconnect = atkbd_disconnect
.disconnect = atkbd_disconnect,
.cleanup = atkbd_cleanup,
};
#ifndef MODULE
static int __init atkbd_setup(char *str)
static int __init atkbd_setup_set(char *str)
{
int ints[4];
str = get_options(str, ARRAY_SIZE(ints), ints);
if (ints[0] > 0) atkbd_set = ints[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
int __init atkbd_init(void)
......
......@@ -21,8 +21,11 @@
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("PS/2 mouse driver");
MODULE_PARM(psmouse_noext, "1i");
MODULE_LICENSE("GPL");
static int psmouse_noext;
#define PSMOUSE_CMD_SETSCALE11 0x00e6
#define PSMOUSE_CMD_SETRES 0x10e8
#define PSMOUSE_CMD_GETINFO 0x03e9
......@@ -33,6 +36,7 @@ MODULE_LICENSE("GPL");
#define PSMOUSE_CMD_SETRATE 0x10f3
#define PSMOUSE_CMD_ENABLE 0x00f4
#define PSMOUSE_CMD_RESET_DIS 0x00f6
#define PSMOUSE_CMD_RESET_BAT 0x02ff
#define PSMOUSE_RET_BAT 0xaa
#define PSMOUSE_RET_ACK 0xfa
......@@ -222,7 +226,11 @@ static int psmouse_sendbyte(struct psmouse *psmouse, unsigned char byte)
psmouse->ack = 0;
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);
return -(psmouse->ack <= 0);
......@@ -302,6 +310,9 @@ static int psmouse_extensions(struct psmouse *psmouse)
psmouse->name = "Mouse";
psmouse->model = 0;
if (psmouse_noext)
return PSMOUSE_PS2;
/*
* Try Genius NetMouse magic init.
*/
......@@ -529,15 +540,24 @@ static void psmouse_initialize(struct psmouse *psmouse)
* 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);
}
}
/*
* psmouse_disconnect() cleans up after we don't want talk
* to the mouse anymore.
* psmouse_cleanup() resets the mouse into power-on state.
*/
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)
......@@ -607,9 +627,19 @@ static void psmouse_connect(struct serio *serio, struct serio_dev *dev)
static struct serio_dev psmouse_dev = {
.interrupt = psmouse_interrupt,
.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)
{
serio_register_device(&psmouse_dev);
......
......@@ -13,6 +13,7 @@
#define I8042_KBD_PHYS_DESC "isa0060/serio0"
#define I8042_AUX_PHYS_DESC "isa0060/serio1"
#define I8042_MUX_PHYS_DESC "isa0060/serio%d"
/*
* IRQs.
......@@ -64,9 +65,13 @@ static inline int i8042_platform_init(void)
*/
#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__) && !defined(__x86_64__)
if (!request_region(I8042_DATA_REG, 16, "i8042"))
return 0;
return -1;
#endif
#if !defined(__i386__) && !defined(__x86_64__)
i8042_reset = 1;
#endif
return 1;
return 0;
}
static inline void i8042_platform_exit(void)
......
......@@ -14,6 +14,7 @@
#define I8042_KBD_PHYS_DESC "walnutps2/serio0"
#define I8042_AUX_PHYS_DESC "walnutps2/serio1"
#define I8042_MUX_PHYS_DESC "walnutps2/serio%d"
extern void *kb_cs;
extern void *kb_data;
......@@ -34,18 +35,17 @@ static inline int i8042_read_status(void)
static inline void i8042_write_data(int val)
{
writeb(val, kb_data);
return;
}
static inline void i8042_write_command(int val)
{
writeb(val, kb_cs);
return;
}
static inline int i8042_platform_init(void)
{
return 1;
i8042_reset = 1;
return 0;
}
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_AUX_PHYS_DESC "spruceps2/serio1"
#define I8042_MUX_PHYS_DESC "spruceps2/serio%d"
#define I8042_COMMAND_REG 0xff810000
#define I8042_DATA_REG 0xff810001
......@@ -109,18 +110,17 @@ static inline int i8042_read_status(void)
static inline void i8042_write_data(int val)
{
*((unsigned char *)0xff810000) = (char)val;
return;
}
static inline void i8042_write_command(int val)
{
*((unsigned char *)0xff810001) = (char)val;
return;
}
static inline int i8042_platform_init(void)
{
return 1;
i8042_reset = 1;
return 0;
}
static inline void i8042_platform_exit(void)
......
......@@ -11,6 +11,7 @@ static int i8042_aux_irq = -1;
#define I8042_KBD_PHYS_DESC "sparcps2/serio0"
#define I8042_AUX_PHYS_DESC "sparcps2/serio1"
#define I8042_MUX_PHYS_DESC "sparcps2/serio%d"
static unsigned long kbd_iobase;
......@@ -50,14 +51,14 @@ static int i8042_platform_init(void)
len = prom_getproperty(prom_root_node, "name", prop, sizeof(prop));
if (len < 0) {
printk("i8042: Cannot get name property of root OBP node.\n");
return 0;
return -1;
}
if (strncmp(prop, "SUNW,JavaStation-1", len) == 0) {
/* Hardcoded values for MrCoffee. */
i8042_kbd_irq = i8042_aux_irq = 13 | 0x20;
kbd_iobase = (unsigned long) ioremap(0x71300060, 8);
if (!kbd_iobase)
return 0;
return -1;
} else {
struct linux_ebus *ebus;
struct linux_ebus_device *edev;
......@@ -69,7 +70,7 @@ static int i8042_platform_init(void)
goto edev_found;
}
}
return 0;
return -1;
edev_found:
for_each_edevchild(edev, child) {
......@@ -87,11 +88,13 @@ static int i8042_platform_init(void)
i8042_aux_irq == -1) {
printk("i8042: Error, 8042 device lacks both kbd and "
"mouse nodes.\n");
return 0;
return -1;
}
}
return 1;
i8042_reset = 1;
return 0;
}
static inline void i8042_platform_exit(void)
......
......@@ -19,7 +19,9 @@
#include <linux/reboot.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/sched.h> /* request/free_irq */
#include <linux/sched.h>
#undef DEBUG
#include "i8042.h"
......@@ -31,13 +33,13 @@ MODULE_PARM(i8042_noaux, "1i");
MODULE_PARM(i8042_unlock, "1i");
MODULE_PARM(i8042_reset, "1i");
MODULE_PARM(i8042_direct, "1i");
MODULE_PARM(i8042_restore_ctr, "1i");
MODULE_PARM(i8042_dumbkbd, "1i");
static int i8042_noaux;
static int i8042_unlock;
static int i8042_reset;
static int i8042_direct;
static int i8042_restore_ctr;
static int i8042_dumbkbd;
spinlock_t i8042_lock = SPIN_LOCK_UNLOCKED;
......@@ -46,6 +48,7 @@ struct i8042_values {
unsigned char disable;
unsigned char irqen;
unsigned char exists;
signed char mux;
unsigned char *name;
unsigned char *phys;
};
......@@ -55,12 +58,9 @@ static struct serio i8042_aux_port;
static unsigned char i8042_initial_ctr;
static unsigned char i8042_ctr;
static unsigned char i8042_last_e0;
static unsigned char i8042_mux_open;
struct timer_list i8042_timer;
#ifdef I8042_DEBUG_IO
static unsigned long i8042_start;
#endif
extern struct pt_regs *kbd_pt_regs;
static unsigned long i8042_unxlate_seen[128 / BITS_PER_LONG];
......@@ -110,18 +110,16 @@ static int i8042_wait_write(void)
static int i8042_flush(void)
{
unsigned long flags;
unsigned char data;
int i = 0;
spin_lock_irqsave(&i8042_lock, flags);
while ((i8042_read_status() & I8042_STR_OBF) && (i++ < I8042_BUFFER_SIZE))
#ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x <- i8042 (flush, %s) [%d]\n",
i8042_read_data(), i8042_read_status() & I8042_STR_AUXDATA ? "aux" : "kbd",
(int) (jiffies - i8042_start));
#else
i8042_read_data();
#endif
while ((i8042_read_status() & I8042_STR_OBF) && (i++ < I8042_BUFFER_SIZE)) {
data = i8042_read_data();
dbg("%02x <- i8042 (flush, %s)", data,
i8042_read_status() & I8042_STR_AUXDATA ? "aux" : "kbd");
}
spin_unlock_irqrestore(&i8042_lock, flags);
......@@ -145,20 +143,14 @@ static int i8042_command(unsigned char *param, int command)
retval = i8042_wait_write();
if (!retval) {
#ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x -> i8042 (command) [%d]\n",
command & 0xff, (int) (jiffies - i8042_start));
#endif
dbg("%02x -> i8042 (command)", command & 0xff);
i8042_write_command(command & 0xff);
}
if (!retval)
for (i = 0; i < ((command >> 12) & 0xf); i++) {
if ((retval = i8042_wait_write())) break;
#ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x -> i8042 (parameter) [%d]\n",
param[i], (int) (jiffies - i8042_start));
#endif
dbg("%02x -> i8042 (parameter)", param[i]);
i8042_write_data(param[i]);
}
......@@ -169,19 +161,13 @@ static int i8042_command(unsigned char *param, int command)
param[i] = ~i8042_read_data();
else
param[i] = i8042_read_data();
#ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x <- i8042 (return) [%d]\n",
param[i], (int) (jiffies - i8042_start));
#endif
dbg("%02x <- i8042 (return)\n", param[i]);
}
spin_unlock_irqrestore(&i8042_lock, flags);
#ifdef I8042_DEBUG_IO
if (retval)
printk(KERN_DEBUG "i8042.c: -- i8042 (timeout) [%d]\n",
(int) (jiffies - i8042_start));
#endif
dbg(" -- i8042 (timeout)");
return retval;
}
......@@ -198,10 +184,7 @@ static int i8042_kbd_write(struct serio *port, unsigned char c)
spin_lock_irqsave(&i8042_lock, flags);
if(!(retval = i8042_wait_write())) {
#ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x -> i8042 (kbd-data) [%d]\n",
c, (int) (jiffies - i8042_start));
#endif
dbg("%02x -> i8042 (kbd-data)", c);
i8042_write_data(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)
{
struct i8042_values *values = port->driver;
int retval;
/*
* Send the byte out.
*/
if (values->mux == -1)
retval = i8042_command(&c, I8042_CMD_AUX_SEND);
/*
* Here we restore the CTR value if requested. I don't know why, but i8042's in
* 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);
else
retval = i8042_command(&c, I8042_CMD_MUX_SEND + values->mux);
/*
* 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)
return retval;
}
/*
* i8042_open() is called when a port is open by the higher layer.
* It allocates the interrupt and enables in in the chip.
......@@ -253,6 +233,10 @@ static int i8042_open(struct serio *port)
i8042_flush();
if (values->mux != -1)
if (i8042_mux_open++)
return 0;
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);
values->exists = 0;
......@@ -282,6 +266,10 @@ static void i8042_close(struct serio *port)
{
struct i8042_values *values = port->driver;
if (values->mux != -1)
if (--i8042_mux_open)
return;
i8042_ctr &= ~values->irqen;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
......@@ -299,11 +287,11 @@ static void i8042_close(struct serio *port)
*/
static struct i8042_values i8042_kbd_values = {
.irq = 0,
.irq = I8042_KBD_IRQ,
.irqen = I8042_CTR_KBDINT,
.disable = I8042_CTR_KBDDIS,
.name = "KBD",
.exists = 0,
.mux = -1,
};
static struct serio i8042_kbd_port =
......@@ -318,11 +306,11 @@ static struct serio i8042_kbd_port =
};
static struct i8042_values i8042_aux_values = {
.irq = 0,
.irq = I8042_AUX_IRQ,
.irqen = I8042_CTR_AUXINT,
.disable = I8042_CTR_AUXDIS,
.name = "AUX",
.exists = 0,
.mux = -1,
};
static struct serio i8042_aux_port =
......@@ -336,6 +324,12 @@ static struct serio 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][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 -
* it handles the interrupts from the i8042, and sends incoming bytes
......@@ -373,19 +367,45 @@ static void i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) |
((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0);
#ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x <- i8042 (interrupt, %s, %d%s%s) [%d]\n",
if (i8042_mux_values[0].exists && (buffer[i].str & I8042_STR_AUXDATA)) {
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,
dfl & SERIO_PARITY ? ", bad parity" : "",
dfl & SERIO_TIMEOUT ? ", timeout" : "",
(int) (jiffies - i8042_start));
#endif
dfl & SERIO_TIMEOUT ? ", timeout" : "");
if (i8042_aux_values.exists && (buffer[i].str & I8042_STR_AUXDATA)) {
serio_interrupt(&i8042_aux_port, buffer[i].data, dfl);
} else
if (i8042_kbd_values.exists) {
if (!i8042_direct) {
continue;
}
if (!i8042_kbd_values.exists)
continue;
if (i8042_direct) {
serio_interrupt(&i8042_kbd_port, data, dfl);
continue;
}
if (data > 0x7f) {
if (test_and_clear_bit(data & 0x7f, i8042_unxlate_seen)) {
serio_interrupt(&i8042_kbd_port, 0xf0, dfl);
......@@ -397,11 +417,11 @@ static void i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
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)
*/
if (~i8042_read_status() & I8042_STR_KEYLOCK) {
if (i8042_unlock) {
if (i8042_unlock)
i8042_ctr |= I8042_CTR_IGNKEYLOCK;
} else {
else
printk(KERN_WARNING "i8042.c: Warning: Keylock active.\n");
}
}
/*
* 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)
......@@ -478,10 +496,9 @@ static int __init i8042_controller_init(void)
/*
* 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
* interface becomes a simple serial in/out, like the aux interface is. If
* the user doesn't wish this, the driver tries to untranslate the values
* after the i8042 translates them.
* After this the kbd interface becomes a simple serial in/out, like the aux
* interface is. We don't do this by default, since it can confuse notebook
* BIOSes.
*/
if (i8042_direct)
......@@ -506,9 +523,24 @@ static int __init i8042_controller_init(void)
void i8042_controller_cleanup(void)
{
int i;
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.
*/
......@@ -529,16 +561,72 @@ void i8042_controller_cleanup(void)
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
printk(KERN_WARNING "i8042.c: Can't restore CTR.\n");
}
/*
* Reset anything that is connected to the ports if the ports
* are enabled in the original config.
* i8042_check_mux() checks whether the controller supports the PS/2 Active
* Multiplexing specification by Synaptics, Phoenix, Insyde and
* LCS/Telegraphics.
*/
if (i8042_kbd_values.exists)
i8042_kbd_write(&i8042_kbd_port, 0xff);
static int __init i8042_check_mux(struct i8042_values *values)
{
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)
* 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;
......@@ -570,7 +658,6 @@ static int __init i8042_check_aux(struct i8042_values *values, struct serio *por
*/
param = 0x5a;
if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != 0xa5)
return -1;
......@@ -665,9 +752,9 @@ static int __init i8042_setup_direct(char *str)
i8042_direct = 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;
}
......@@ -675,7 +762,7 @@ __setup("i8042_reset", i8042_setup_reset);
__setup("i8042_noaux", i8042_setup_noaux);
__setup("i8042_unlock", i8042_setup_unlock);
__setup("i8042_direct", i8042_setup_direct);
__setup("i8042_restore_ctr", i8042_setup_restore_ctr);
__setup("i8042_dumbkbd", i8042_setup_dumbkbd);
#endif
/*
......@@ -698,26 +785,42 @@ static struct notifier_block i8042_notifier=
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)
{
#ifdef I8042_DEBUG_IO
i8042_start = jiffies;
#endif
int i;
#if !defined(__i386__) && !defined(__x86_64__)
i8042_reset = 1;
#endif
dbg_init();
if (!i8042_platform_init())
if (i8042_platform_init())
return -EBUSY;
i8042_kbd_values.irq = I8042_KBD_IRQ;
i8042_aux_values.irq = I8042_AUX_IRQ;
if (i8042_controller_init())
return -ENODEV;
if (!i8042_noaux && !i8042_check_aux(&i8042_aux_values, &i8042_aux_port))
if (i8042_dumbkbd)
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);
......@@ -732,22 +835,26 @@ int __init i8042_init(void)
void __exit i8042_exit(void)
{
int i;
unregister_reboot_notifier(&i8042_notifier);
del_timer(&i8042_timer);
i8042_controller_cleanup();
if (i8042_kbd_values.exists)
serio_unregister_port(&i8042_kbd_port);
if (i8042_aux_values.exists)
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();
}
module_init(i8042_init);
module_exit(i8042_exit);
......@@ -21,13 +21,6 @@
#include "i8042-io.h"
#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
* has to be long enough for the i8042 itself to timeout on sending a byte
......@@ -54,6 +47,7 @@
#define I8042_STR_AUXDATA 0x20
#define I8042_STR_KEYLOCK 0x10
#define I8042_STR_CMDDAT 0x08
#define I8042_STR_MUXERR 0x04
#define I8042_STR_IBF 0x02
#define I8042_STR_OBF 0x01
......@@ -87,6 +81,9 @@
#define I8042_CMD_AUX_SEND 0x10d4
#define I8042_CMD_AUX_LOOP 0x11d3
#define I8042_CMD_MUX_PFX 0x0090
#define I8042_CMD_MUX_SEND 0x1090
/*
* Return codes.
*/
......@@ -100,4 +97,18 @@
#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 */
......@@ -69,6 +69,7 @@ struct serio_dev {
void (*interrupt)(struct serio *, unsigned char, unsigned int);
void (*connect)(struct serio *, struct serio_dev *dev);
void (*disconnect)(struct serio *);
void (*cleanup)(struct serio *);
struct serio_dev *next;
};
......@@ -85,7 +86,10 @@ void serio_unregister_device(struct serio_dev *dev);
static __inline__ int serio_write(struct serio *serio, unsigned char data)
{
if (serio->write)
return serio->write(serio, data);
else
return -1;
}
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);
}
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)
*/
......
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