From: Dmitry Torokhov <dtor_core@ameritech.net>

- 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.



 drivers/input/serio/i8042.c |  523 +++++++++++++++++++++++++++-----------------
 1 files changed, 321 insertions(+), 202 deletions(-)

diff -puN drivers/input/serio/i8042.c~input-03-resume-methods drivers/input/serio/i8042.c
--- 25/drivers/input/serio/i8042.c~input-03-resume-methods	2003-12-16 22:47:32.000000000 -0800
+++ 25-akpm/drivers/input/serio/i8042.c	2003-12-16 22:47:32.000000000 -0800
@@ -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_AUTHOR("Vojtech Pavlik <vojtech@s
 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 struct serio i8042_aux_port;
 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 @@ static int i8042_aux_write(struct serio 
 }
 
 /*
- * 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,19 +269,16 @@ static int i8042_open(struct serio *port
 		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);
-		goto ctr_fail;
+	if (i8042_activate_port(port)) {
+		printk(KERN_ERR "i8042.c: Can't activate %s, unregistering the port\n", values->name);
+		goto activate_fail;
 	}
 
 	i8042_interrupt(0, NULL, NULL);
 
 	return 0;
 
-ctr_fail:
-	i8042_ctr &= ~values->irqen;
+activate_fail:
 	free_irq(values->irq, i8042_request_irq_cookie);
 
 irq_fail:
@@ -401,145 +433,78 @@ static irqreturn_t i8042_interrupt(int i
 }
 
 /*
- * 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 +513,31 @@ void i8042_controller_cleanup(void)
 
 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 +613,7 @@ static int __init i8042_check_aux(struct
 	return 0;
 }
 
+
 /*
  * i8042_port_register() marks the device as existing,
  * registers it, and reports to the user.
@@ -710,52 +641,194 @@ static int __init i8042_port_register(st
 	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 +850,35 @@ static struct notifier_block i8042_notif
         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 +927,15 @@ int __init i8042_init(void)
 	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;
@@ -836,6 +947,14 @@ void __exit i8042_exit(void)
 
 	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);
 
 	i8042_controller_cleanup();

_