Commit 25b78b8f authored by Dmitry Torokhov's avatar Dmitry Torokhov

[PATCH] Input: implement resume methods

- Implement resume methods using serio_reconnect facility
- Register i8042 with sysfs
- Register i8042 with older PM scheme to restore keyboard
  and mouse for APM users
- Convert parameter handling to the new style
- Unregister port not only when there is no free IRQ but
  also if the port fails to activate.
parent c0fbf5b6
...@@ -12,11 +12,14 @@ ...@@ -12,11 +12,14 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/config.h> #include <linux/config.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/sysdev.h>
#include <linux/pm.h>
#include <linux/serio.h> #include <linux/serio.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -25,19 +28,23 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); ...@@ -25,19 +28,23 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("i8042 keyboard and mouse controller driver"); MODULE_DESCRIPTION("i8042 keyboard and mouse controller driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_PARM(i8042_noaux, "1i"); static unsigned int i8042_noaux;
MODULE_PARM(i8042_nomux, "1i"); module_param(i8042_noaux, bool, 0);
MODULE_PARM(i8042_unlock, "1i");
MODULE_PARM(i8042_reset, "1i");
MODULE_PARM(i8042_direct, "1i");
MODULE_PARM(i8042_dumbkbd, "1i");
static int i8042_reset; static unsigned int i8042_nomux;
static int i8042_noaux; module_param(i8042_nomux, bool, 0);
static int i8042_nomux;
static int i8042_unlock; static unsigned int i8042_unlock;
static int i8042_direct; module_param(i8042_unlock, bool, 0);
static int i8042_dumbkbd;
static unsigned int i8042_reset;
module_param(i8042_reset, bool, 0);
static unsigned int i8042_direct;
module_param(i8042_direct, bool, 0);
static unsigned int i8042_dumbkbd;
module_param(i8042_dumbkbd, bool, 0);
#undef DEBUG #undef DEBUG
#include "i8042.h" #include "i8042.h"
...@@ -59,6 +66,9 @@ static struct serio i8042_aux_port; ...@@ -59,6 +66,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_mux_open; 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; struct timer_list i8042_timer;
/* /*
...@@ -214,16 +224,41 @@ static int i8042_aux_write(struct serio *port, unsigned char c) ...@@ -214,16 +224,41 @@ static int i8042_aux_write(struct serio *port, unsigned char c)
} }
/* /*
* i8042_open() is called when a port is open by the higher layer. * i8042_activate_port() enables port on a chip.
* It allocates the interrupt and enables it in the chip.
*/ */
static int i8042_open(struct serio *port) static int i8042_activate_port(struct serio *port)
{ {
struct i8042_values *values = port->driver; struct i8042_values *values = port->driver;
i8042_flush(); i8042_flush();
/*
* Enable port again here because it is disabled if we are
* resuming (normally it is enabled already).
*/
i8042_ctr &= ~values->disable;
i8042_ctr |= values->irqen;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
i8042_ctr &= ~values->irqen;
return -1;
}
return 0;
}
/*
* i8042_open() is called when a port is open by the higher layer.
* It allocates the interrupt and calls i8042_enable_port.
*/
static int i8042_open(struct serio *port)
{
struct i8042_values *values = port->driver;
if (values->mux != -1) if (values->mux != -1)
if (i8042_mux_open++) if (i8042_mux_open++)
return 0; return 0;
...@@ -234,19 +269,16 @@ static int i8042_open(struct serio *port) ...@@ -234,19 +269,16 @@ static int i8042_open(struct serio *port)
goto irq_fail; goto irq_fail;
} }
i8042_ctr |= values->irqen; if (i8042_activate_port(port)) {
printk(KERN_ERR "i8042.c: Can't activate %s, unregistering the port\n", values->name);
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { goto activate_fail;
printk(KERN_ERR "i8042.c: Can't write CTR while opening %s, unregistering the port\n", values->name);
goto ctr_fail;
} }
i8042_interrupt(0, NULL, NULL); i8042_interrupt(0, NULL, NULL);
return 0; return 0;
ctr_fail: activate_fail:
i8042_ctr &= ~values->irqen;
free_irq(values->irq, i8042_request_irq_cookie); free_irq(values->irq, i8042_request_irq_cookie);
irq_fail: irq_fail:
...@@ -401,145 +433,78 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -401,145 +433,78 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
} }
/* /*
* i8042_controller init initializes the i8042 controller, and, * i8042_enable_mux_mode checks whether the controller has an active
* most importantly, sets it into non-xlated mode if that's * multiplexor and puts the chip into Multiplexed (as opposed to
* desired. * Legacy) mode.
*/ */
static int __init i8042_controller_init(void) static int i8042_enable_mux_mode(struct i8042_values *values, unsigned char *mux_version)
{ {
unsigned char param;
/* /*
* Test the i8042. We need to know if it thinks it's working correctly * Get rid of bytes in the queue.
* before doing anything else.
*/ */
i8042_flush(); i8042_flush();
if (i8042_reset) {
unsigned char param;
if (i8042_command(&param, I8042_CMD_CTL_TEST)) {
printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n");
return -1;
}
if (param != I8042_RET_CTL_TEST) {
printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n",
param, I8042_RET_CTL_TEST);
return -1;
}
}
/* /*
* Save the CTR for restoral on unload / reboot. * 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.
*/ */
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) { param = 0xf0;
printk(KERN_ERR "i8042.c: Can't read CTR while initializing i8042.\n"); if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != 0x0f)
return -1; return -1;
} param = 0x56;
if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != 0xa9)
i8042_initial_ctr = i8042_ctr;
/*
* Disable the keyboard interface and interrupt.
*/
i8042_ctr |= I8042_CTR_KBDDIS;
i8042_ctr &= ~I8042_CTR_KBDINT;
/*
* Handle keylock.
*/
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");
}
/*
* If the chip is configured into nontranslated mode by the BIOS, don't
* bother enabling translating and be happy.
*/
if (~i8042_ctr & I8042_CTR_XLATE)
i8042_direct = 1;
/*
* Set nontranslated mode for the kbd interface if requested by an option.
* 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) {
i8042_ctr &= ~I8042_CTR_XLATE;
i8042_kbd_port.type = SERIO_8042;
}
/*
* Write CTR back.
*/
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
printk(KERN_ERR "i8042.c: Can't write CTR while initializing i8042.\n");
return -1; return -1;
} param = 0xa4;
if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param == 0x5b)
return -1;
if (mux_version)
*mux_version = ~param;
return 0; return 0;
} }
/* /*
* Here we try to reset everything back to a state in which the BIOS will be * i8042_enable_mux_ports enables 4 individual AUX ports after
* able to talk to the hardware when rebooting. * the controller has been switched into Multiplexed mode
*/ */
void i8042_controller_cleanup(void) static int i8042_enable_mux_ports(struct i8042_values *values)
{ {
unsigned char param;
int i; 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. * Disable all muxed ports by disabling AUX.
*/ */
if (i8042_reset) { i8042_ctr |= I8042_CTR_AUXDIS;
unsigned char param; i8042_ctr &= ~I8042_CTR_AUXINT;
if (i8042_command(&param, I8042_CMD_CTL_TEST)) if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
printk(KERN_ERR "i8042.c: i8042 controller reset timeout.\n"); printk(KERN_ERR "i8042.c: Failed to disable AUX port, can't use MUX.\n");
return -1;
} }
/* /*
* Restore the original control register setting. * Enable all muxed ports.
*/ */
i8042_ctr = i8042_initial_ctr; for (i = 0; i < 4; i++) {
i8042_command(&param, I8042_CMD_MUX_PFX + i);
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) i8042_command(&param, I8042_CMD_AUX_ENABLE);
printk(KERN_WARNING "i8042.c: Can't restore CTR.\n"); }
return 0;
} }
/* /*
* i8042_check_mux() checks whether the controller supports the PS/2 Active * i8042_check_mux() checks whether the controller supports the PS/2 Active
* Multiplexing specification by Synaptics, Phoenix, Insyde and * Multiplexing specification by Synaptics, Phoenix, Insyde and
...@@ -548,66 +513,31 @@ void i8042_controller_cleanup(void) ...@@ -548,66 +513,31 @@ void i8042_controller_cleanup(void)
static int __init i8042_check_mux(struct i8042_values *values) static int __init i8042_check_mux(struct i8042_values *values)
{ {
unsigned char param;
static int i8042_check_mux_cookie; static int i8042_check_mux_cookie;
int i; unsigned char mux_version;
/* /*
* Check if AUX irq is available. * Check if AUX irq is available.
*/ */
if (request_irq(values->irq, i8042_interrupt, SA_SHIRQ, if (request_irq(values->irq, i8042_interrupt, SA_SHIRQ,
"i8042", &i8042_check_mux_cookie)) "i8042", &i8042_check_mux_cookie))
return -1; return -1;
free_irq(values->irq, &i8042_check_mux_cookie); free_irq(values->irq, &i8042_check_mux_cookie);
/* if (i8042_enable_mux_mode(values, &mux_version))
* 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; return -1;
printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n", printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n",
(~param >> 4) & 0xf, ~param & 0xf); (mux_version >> 4) & 0xf, mux_version & 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)) if (i8042_enable_mux_ports(values))
return -1; return -1;
/* i8042_mux_present = 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; return 0;
} }
/* /*
* i8042_check_aux() applies as much paranoia as it can at detecting * i8042_check_aux() applies as much paranoia as it can at detecting
* the presence of an AUX interface. * the presence of an AUX interface.
...@@ -683,6 +613,7 @@ static int __init i8042_check_aux(struct i8042_values *values) ...@@ -683,6 +613,7 @@ static int __init i8042_check_aux(struct i8042_values *values)
return 0; return 0;
} }
/* /*
* i8042_port_register() marks the device as existing, * i8042_port_register() marks the device as existing,
* registers it, and reports to the user. * registers it, and reports to the user.
...@@ -710,52 +641,194 @@ static int __init i8042_port_register(struct i8042_values *values, struct serio ...@@ -710,52 +641,194 @@ static int __init i8042_port_register(struct i8042_values *values, struct serio
return 0; return 0;
} }
static void i8042_timer_func(unsigned long data) static void i8042_timer_func(unsigned long data)
{ {
i8042_interrupt(0, NULL, NULL); i8042_interrupt(0, NULL, NULL);
mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD); mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
} }
#ifndef MODULE
static int __init i8042_setup_reset(char *str) /*
{ * i8042_controller init initializes the i8042 controller, and,
i8042_reset = 1; * most importantly, sets it into non-xlated mode if that's
return 1; * desired.
} */
static int __init i8042_setup_noaux(char *str)
{ static int i8042_controller_init(void)
i8042_noaux = 1;
i8042_nomux = 1;
return 1;
}
static int __init i8042_setup_nomux(char *str)
{
i8042_nomux = 1;
return 1;
}
static int __init i8042_setup_unlock(char *str)
{ {
i8042_unlock = 1;
return 1; if (i8042_noaux)
i8042_nomux = 1;
/*
* Test the i8042. We need to know if it thinks it's working correctly
* before doing anything else.
*/
i8042_flush();
if (i8042_reset) {
unsigned char param;
if (i8042_command(&param, I8042_CMD_CTL_TEST)) {
printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n");
return -1;
}
if (param != I8042_RET_CTL_TEST) {
printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n",
param, I8042_RET_CTL_TEST);
return -1;
}
}
/*
* Save the CTR for restoral on unload / reboot.
*/
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) {
printk(KERN_ERR "i8042.c: Can't read CTR while initializing i8042.\n");
return -1;
}
i8042_initial_ctr = i8042_ctr;
/*
* Disable the keyboard interface and interrupt.
*/
i8042_ctr |= I8042_CTR_KBDDIS;
i8042_ctr &= ~I8042_CTR_KBDINT;
/*
* Handle keylock.
*/
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");
}
/*
* If the chip is configured into nontranslated mode by the BIOS, don't
* bother enabling translating and be happy.
*/
if (~i8042_ctr & I8042_CTR_XLATE)
i8042_direct = 1;
/*
* Set nontranslated mode for the kbd interface if requested by an option.
* 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) {
i8042_ctr &= ~I8042_CTR_XLATE;
i8042_kbd_port.type = SERIO_8042;
}
/*
* Write CTR back.
*/
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
printk(KERN_ERR "i8042.c: Can't write CTR while initializing i8042.\n");
return -1;
}
return 0;
} }
static int __init i8042_setup_direct(char *str)
/*
* Here we try to reset everything back to a state in which the BIOS will be
* able to talk to the hardware when rebooting.
*/
void i8042_controller_cleanup(void)
{ {
i8042_direct = 1; int i;
return 1;
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.
*/
if (i8042_reset) {
unsigned char param;
if (i8042_command(&param, I8042_CMD_CTL_TEST))
printk(KERN_ERR "i8042.c: i8042 controller reset timeout.\n");
}
/*
* Restore the original control register setting.
*/
i8042_ctr = i8042_initial_ctr;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
printk(KERN_WARNING "i8042.c: Can't restore CTR.\n");
} }
static int __init i8042_setup_dumbkbd(char *str)
/*
* Here we try to reset everything back to a state in which suspended
*/
static int i8042_controller_resume(void)
{ {
i8042_dumbkbd = 1; int i;
return 1;
if (i8042_controller_init()) {
printk(KERN_ERR "i8042: resume failed\n");
return -1;
}
if (i8042_mux_present)
if (i8042_enable_mux_mode(&i8042_aux_values, NULL) ||
i8042_enable_mux_ports(&i8042_aux_values)) {
printk(KERN_WARNING "i8042: failed to resume active multiplexor, mouse won't wotk.\n");
}
/*
* 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_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);
return 0;
} }
__setup("i8042_reset", i8042_setup_reset);
__setup("i8042_noaux", i8042_setup_noaux);
__setup("i8042_nomux", i8042_setup_nomux);
__setup("i8042_unlock", i8042_setup_unlock);
__setup("i8042_direct", i8042_setup_direct);
__setup("i8042_dumbkbd", i8042_setup_dumbkbd);
#endif
/* /*
* We need to reset the 8042 back to original mode on system shutdown, * We need to reset the 8042 back to original mode on system shutdown,
...@@ -777,6 +850,35 @@ static struct notifier_block i8042_notifier= ...@@ -777,6 +850,35 @@ static struct notifier_block i8042_notifier=
0 0
}; };
/*
* Resume handler for the new PM scheme (driver model)
*/
static int i8042_resume(struct sys_device *dev)
{
return i8042_controller_resume();
}
static struct sysdev_class kbc_sysclass = {
set_kset_name("i8042"),
.resume = i8042_resume,
};
static struct sys_device device_i8042 = {
.id = 0,
.cls = &kbc_sysclass,
};
/*
* Resume handler for the old PM scheme (APM)
*/
static int i8042_pm_callback(struct pm_dev *dev, pm_request_t request, void *dummy)
{
if (request == PM_RESUME)
return i8042_controller_resume();
return 0;
}
static void __init i8042_init_mux_values(struct i8042_values *values, struct serio *port, int index) 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(port, &i8042_aux_port, sizeof(struct serio));
...@@ -825,6 +927,15 @@ int __init i8042_init(void) ...@@ -825,6 +927,15 @@ int __init i8042_init(void)
i8042_timer.function = i8042_timer_func; i8042_timer.function = i8042_timer_func;
mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD); mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
if (sysdev_class_register(&kbc_sysclass) == 0) {
if (sys_device_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); register_reboot_notifier(&i8042_notifier);
return 0; return 0;
...@@ -836,6 +947,14 @@ void __exit i8042_exit(void) ...@@ -836,6 +947,14 @@ void __exit i8042_exit(void)
unregister_reboot_notifier(&i8042_notifier); unregister_reboot_notifier(&i8042_notifier);
if (i8042_pm_dev)
pm_unregister(i8042_pm_dev);
if (i8042_sysdev_initialized) {
sys_device_unregister(&device_i8042);
sysdev_class_unregister(&kbc_sysclass);
}
del_timer(&i8042_timer); del_timer(&i8042_timer);
i8042_controller_cleanup(); i8042_controller_cleanup();
......
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