lkml.org 
[lkml]   [2003]   [Dec]   [7]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
Date
From
SubjectRe: [PATCH 2.6 1/3] Take 2: resume support for i8042 (atkbd & psmouse)
On Sun, Dec 07, 2003 at 02:27:49AM -0500, Dmitry Torokhov wrote:

Hi!

These look very good. I'll go over them later today and apply them to my
tree. Thanks!

> Hi,
>
> Here is the 2nd attempt to implement resume for i8042 using serio_reconnect
> facility that can be found in -mm kernels. Big thanks to everyone who tested
> the first version. The changes from the last patch:
>
> i8042: - now correctly enables keyboard port on resume
> - switches active multiplexing controller from Legacy mode back
> to Multiplexing mode (4 AUX ports) on resume
> - installs resume handlers for both old (pm_dev - APM) and new
> (driver model) suspend/resume schemes.
> - convert parameter handling to the new style
>
> psmouse: - psmouse_pm_callback removed as i8042 will request reconnect
> regardless of whether APM or new PM method is used.
>
> The patched depend on bunch of other changes in input subsystem all of which
> can be found here:
>
> http://www.geocities.com/dt_or/input
>
> Should apply cleanly to -test11. If patching -mm tree omit patches 1-6 and 8
> as they are already present there.
>
> Dmitry
>
>
> ===================================================================
>
>
> ChangeSet@1.1514, 2003-12-07 01:48:52-05:00, dtor_core@ameritech.net
> Input: - 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
>
>
> i8042.c | 520 +++++++++++++++++++++++++++++++++++++++-------------------------
> 1 files changed, 320 insertions(+), 200 deletions(-)
>
>
> ===================================================================
>
>
>
> diff -Nru a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
> --- a/drivers/input/serio/i8042.c Sun Dec 7 02:17:56 2003
> +++ b/drivers/input/serio/i8042.c Sun Dec 7 02:17:56 2003
> @@ -12,11 +12,14 @@
>
> #include <linux/delay.h>
> #include <linux/module.h>
> +#include <linux/moduleparam.h>
> #include <linux/interrupt.h>
> #include <linux/ioport.h>
> #include <linux/config.h>
> #include <linux/reboot.h>
> #include <linux/init.h>
> +#include <linux/sysdev.h>
> +#include <linux/pm.h>
> #include <linux/serio.h>
>
> #include <asm/io.h>
> @@ -25,19 +28,23 @@
> MODULE_DESCRIPTION("i8042 keyboard and mouse controller driver");
> MODULE_LICENSE("GPL");
>
> -MODULE_PARM(i8042_noaux, "1i");
> -MODULE_PARM(i8042_nomux, "1i");
> -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 int i8042_noaux;
> -static int i8042_nomux;
> -static int i8042_unlock;
> -static int i8042_direct;
> -static int i8042_dumbkbd;
> +static unsigned int i8042_noaux;
> +module_param(i8042_noaux, bool, 0);
> +
> +static unsigned int i8042_nomux;
> +module_param(i8042_nomux, bool, 0);
> +
> +static unsigned int i8042_unlock;
> +module_param(i8042_unlock, bool, 0);
> +
> +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
> #include "i8042.h"
> @@ -59,6 +66,9 @@
> 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;
>
> /*
> @@ -214,16 +224,41 @@
> }
>
> /*
> - * i8042_open() is called when a port is open by the higher layer.
> - * It allocates the interrupt and enables it in the chip.
> + * i8042_activate_port() enables port on a chip.
> */
>
> -static int i8042_open(struct serio *port)
> +static int i8042_activate_port(struct serio *port)
> {
> struct i8042_values *values = port->driver;
>
> 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 (i8042_mux_open++)
> return 0;
> @@ -234,10 +269,8 @@
> goto irq_fail;
> }
>
> - i8042_ctr |= values->irqen;
> -
> - if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
> - printk(KERN_ERR "i8042.c: Can't write CTR while opening %s, unregistering the port\n", values->name);
> + if (i8042_activate_port(port)) {
> + printk(KERN_ERR "i8042.c: Can't activate %s, unregistering the port\n", values->name);
> goto ctr_fail;
> }
>
> @@ -246,7 +279,6 @@
> return 0;
>
> ctr_fail:
> - i8042_ctr &= ~values->irqen;
> free_irq(values->irq, i8042_request_irq_cookie);
>
> irq_fail:
> @@ -400,146 +432,80 @@
> return IRQ_RETVAL(j);
> }
>
> +
> /*
> - * i8042_controller init initializes the i8042 controller, and,
> - * most importantly, sets it into non-xlated mode if that's
> - * desired.
> + * i8042_enable_mux_mode checks whether the controller has an active
> + * multiplexor and puts the chip into Multiplexed (as opposed to
> + * 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
> - * before doing anything else.
> + * Get rid of bytes in the queue.
> */
>
> 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)) {
> - printk(KERN_ERR "i8042.c: Can't read CTR while initializing i8042.\n");
> + 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;
> - }
> -
> - i8042_initial_ctr = i8042_ctr;
> -
> -/*
> - * Disable the keyboard interface and interrupt.
> - */
>
> - i8042_ctr |= I8042_CTR_KBDDIS;
> - i8042_ctr &= ~I8042_CTR_KBDINT;
> + if (mux_version)
> + *mux_version = ~param;
>
> -/*
> - * Handle keylock.
> - */
> + return 0;
> +}
>
> - 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.
> + * i8042_enable_mux_ports enables 4 individual AUX ports after
> + * the controller has been switched into Multiplexed mode
> */
>
> - if (~i8042_ctr & I8042_CTR_XLATE)
> - i8042_direct = 1;
> -
> +static int i8042_enable_mux_ports(struct i8042_values *values)
> +{
> + unsigned char param;
> + int i;
> /*
> - * 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.
> + * Disable all muxed ports by disabling AUX.
> */
>
> - if (i8042_direct) {
> - i8042_ctr &= ~I8042_CTR_XLATE;
> - i8042_kbd_port.type = SERIO_8042;
> - }
> -
> -/*
> - * Write CTR back.
> - */
> + i8042_ctr |= I8042_CTR_AUXDIS;
> + i8042_ctr &= ~I8042_CTR_AUXINT;
>
> if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
> - printk(KERN_ERR "i8042.c: Can't write CTR while initializing i8042.\n");
> + printk(KERN_ERR "i8042.c: Failed to disable AUX port, can't use MUX.\n");
> return -1;
> }
>
> - return 0;
> -}
> -
> /*
> - * 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)
> -{
> - 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.
> + * Enable all muxed ports.
> */
>
> - if (i8042_reset) {
> - unsigned char param;
> -
> - if (i8042_command(&param, I8042_CMD_CTL_TEST))
> - printk(KERN_ERR "i8042.c: i8042 controller reset timeout.\n");
> + for (i = 0; i < 4; i++) {
> + i8042_command(&param, I8042_CMD_MUX_PFX + i);
> + i8042_command(&param, I8042_CMD_AUX_ENABLE);
> }
>
> -/*
> - * 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");
> -
> + return 0;
> }
>
> +
> /*
> * i8042_check_mux() checks whether the controller supports the PS/2 Active
> * Multiplexing specification by Synaptics, Phoenix, Insyde and
> @@ -548,66 +514,31 @@
>
> static int __init i8042_check_mux(struct i8042_values *values)
> {
> - unsigned char param;
> static int i8042_check_mux_cookie;
> - int i;
> + unsigned char mux_version;
>
> /*
> * Check if AUX irq is available.
> */
> -
> if (request_irq(values->irq, i8042_interrupt, SA_SHIRQ,
> "i8042", &i8042_check_mux_cookie))
> return -1;
> free_irq(values->irq, &i8042_check_mux_cookie);
>
> -/*
> - * 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)
> + if (i8042_enable_mux_mode(values, &mux_version))
> return -1;
>
> printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n",
> - (~param >> 4) & 0xf, ~param & 0xf);
> -
> -/*
> - * Disable all muxed ports by disabling AUX.
> - */
> -
> - i8042_ctr |= I8042_CTR_AUXDIS;
> - i8042_ctr &= ~I8042_CTR_AUXINT;
> + (mux_version >> 4) & 0xf, mux_version & 0xf);
>
> - if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
> + if (i8042_enable_mux_ports(values))
> 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);
> - }
> -
> + i8042_mux_present = 1;
> return 0;
> }
>
> +
> /*
> * i8042_check_aux() applies as much paranoia as it can at detecting
> * the presence of an AUX interface.
> @@ -683,6 +614,7 @@
> return 0;
> }
>
> +
> /*
> * i8042_port_register() marks the device as existing,
> * registers it, and reports to the user.
> @@ -710,52 +642,194 @@
> return 0;
> }
>
> +
> static void i8042_timer_func(unsigned long data)
> {
> i8042_interrupt(0, NULL, NULL);
> mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
> }
>
> -#ifndef MODULE
> -static int __init i8042_setup_reset(char *str)
> -{
> - i8042_reset = 1;
> - return 1;
> -}
> -static int __init i8042_setup_noaux(char *str)
> -{
> - 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_controller init initializes the i8042 controller, and,
> + * most importantly, sets it into non-xlated mode if that's
> + * desired.
> + */
> +
> +static int i8042_controller_init(void)
> {
> - 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;
> - return 1;
> + 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.
> + */
> +
> + 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;
> - return 1;
> + int i;
> +
> + 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,
> @@ -777,6 +851,35 @@
> 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)
> {
> memcpy(port, &i8042_aux_port, sizeof(struct serio));
> @@ -825,6 +928,15 @@
> i8042_timer.function = i8042_timer_func;
> 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);
>
> return 0;
> @@ -835,6 +947,14 @@
> int i;
>
> 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);
>

--
Vojtech Pavlik
SuSE Labs, SuSE CR
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

\
 
 \ /
  Last update: 2005-03-22 13:59    [W:0.063 / U:0.124 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site