#include <linux/serio.h>
#include <linux/err.h>
#include <linux/rcupdate.h>
+#include <linux/platform_device.h>
#include <asm/io.h>
MODULE_DESCRIPTION("i8042 keyboard and mouse controller driver");
MODULE_LICENSE("GPL");
+static unsigned int i8042_nokbd;
+module_param_named(nokbd, i8042_nokbd, bool, 0);
+MODULE_PARM_DESC(nokbd, "Do not probe or use KBD port.");
+
static unsigned int i8042_noaux;
module_param_named(noaux, i8042_noaux, bool, 0);
MODULE_PARM_DESC(noaux, "Do not probe or use AUX (mouse) port.");
static struct i8042_port i8042_ports[I8042_NUM_PORTS] = {
{
.disable = I8042_CTR_KBDDIS,
- .irqen = I8042_CTR_KBDINT,
+ .irqen = I8042_CTR_KBDINT,
.mux = -1,
.name = "KBD",
},
static int i8042_command(unsigned char *param, int command)
{
unsigned long flags;
- int retval = 0, i = 0;
+ int i, retval, auxerr = 0;
if (i8042_noloop && command == I8042_CMD_AUX_LOOP)
return -1;
spin_lock_irqsave(&i8042_lock, flags);
- retval = i8042_wait_write();
- if (!retval) {
- dbg("%02x -> i8042 (command)", command & 0xff);
- i8042_write_command(command & 0xff);
+ if ((retval = i8042_wait_write()))
+ goto out;
+
+ dbg("%02x -> i8042 (command)", command & 0xff);
+ i8042_write_command(command & 0xff);
+
+ for (i = 0; i < ((command >> 12) & 0xf); i++) {
+ if ((retval = i8042_wait_write()))
+ goto out;
+ dbg("%02x -> i8042 (parameter)", param[i]);
+ i8042_write_data(param[i]);
}
- if (!retval)
- for (i = 0; i < ((command >> 12) & 0xf); i++) {
- if ((retval = i8042_wait_write())) break;
- dbg("%02x -> i8042 (parameter)", param[i]);
- i8042_write_data(param[i]);
- }
+ for (i = 0; i < ((command >> 8) & 0xf); i++) {
+ if ((retval = i8042_wait_read()))
+ goto out;
- if (!retval)
- for (i = 0; i < ((command >> 8) & 0xf); i++) {
- if ((retval = i8042_wait_read())) break;
- if (i8042_read_status() & I8042_STR_AUXDATA)
- param[i] = ~i8042_read_data();
- else
- param[i] = i8042_read_data();
- dbg("%02x <- i8042 (return)", param[i]);
+ if (command == I8042_CMD_AUX_LOOP &&
+ !(i8042_read_status() & I8042_STR_AUXDATA)) {
+ retval = auxerr = -1;
+ goto out;
}
- spin_unlock_irqrestore(&i8042_lock, flags);
+ param[i] = i8042_read_data();
+ dbg("%02x <- i8042 (return)", param[i]);
+ }
if (retval)
- dbg(" -- i8042 (timeout)");
+ dbg(" -- i8042 (%s)", auxerr ? "auxerr" : "timeout");
+ out:
+ spin_unlock_irqrestore(&i8042_lock, flags);
return retval;
}
return 0;
-activate_fail:
+ activate_fail:
free_irq(port->irq, i8042_request_irq_cookie);
-irq_fail:
+ irq_fail:
serio_unregister_port_delayed(serio);
return -1;
struct i8042_port *port = serio->port_data;
port->exists = 0;
- synchronize_kernel();
+ synchronize_sched();
port->serio = NULL;
}
serio_interrupt(port->serio, data, dfl, regs);
ret = 1;
-out:
+ out:
return IRQ_RETVAL(ret);
}
*/
param = 0xf0;
- if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != 0x0f)
+ if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != 0xf0)
return -1;
param = mode ? 0x56 : 0xf6;
- if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != (mode ? 0xa9 : 0x09))
+ if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != (mode ? 0x56 : 0xf6))
return -1;
param = mode ? 0xa4 : 0xa5;
- if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param == (mode ? 0x5b : 0x5a))
+ if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param == (mode ? 0xa4 : 0xa5))
return -1;
if (mux_version)
- *mux_version = ~param;
+ *mux_version = param;
return 0;
}
* Enable all muxed ports.
*/
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < I8042_NUM_MUX_PORTS; i++) {
i8042_command(¶m, I8042_CMD_MUX_PFX + i);
i8042_command(¶m, I8042_CMD_AUX_ENABLE);
}
* LCS/Telegraphics.
*/
-static int __init i8042_check_mux(void)
+static int __devinit i8042_check_mux(void)
{
unsigned char mux_version;
* the presence of an AUX interface.
*/
-static int __init i8042_check_aux(void)
+static int __devinit i8042_check_aux(void)
{
unsigned char param;
static int i8042_check_aux_cookie;
*/
param = 0x5a;
- if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != 0xa5) {
+ if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != 0x5a) {
/*
* External connection test - filters out AT-soldered PS/2 i8042's
*/
if (i8042_command(¶m, I8042_CMD_AUX_TEST)
- || (param && param != 0xfa && param != 0xff))
+ || (param && param != 0xfa && param != 0xff))
return -1;
}
* registers it, and reports to the user.
*/
-static int __init i8042_port_register(struct i8042_port *port)
+static int __devinit i8042_port_register(struct i8042_port *port)
{
i8042_ctr &= ~port->disable;
kfree(port->serio);
port->serio = NULL;
i8042_ctr |= port->disable;
- return -1;
+ return -EIO;
}
printk(KERN_INFO "serio: i8042 %s port at %#lx,%#lx irq %d\n",
* Here we try to restore the original BIOS settings
*/
-static int i8042_suspend(struct device *dev, pm_message_t state, u32 level)
+static int i8042_suspend(struct platform_device *dev, pm_message_t state)
{
- if (level == SUSPEND_DISABLE) {
- del_timer_sync(&i8042_timer);
- i8042_controller_reset();
- }
+ del_timer_sync(&i8042_timer);
+ i8042_controller_reset();
return 0;
}
* Here we try to reset everything back to a state in which suspended
*/
-static int i8042_resume(struct device *dev, u32 level)
+static int i8042_resume(struct platform_device *dev)
{
int i;
- if (level != RESUME_ENABLE)
- return 0;
-
if (i8042_ctl_test())
return -1;
panic_blink = i8042_panic_blink;
return 0;
-
}
/*
* because otherwise BIOSes will be confused.
*/
-static void i8042_shutdown(struct device *dev)
+static void i8042_shutdown(struct platform_device *dev)
{
i8042_controller_cleanup();
}
-static struct device_driver i8042_driver = {
- .name = "i8042",
- .bus = &platform_bus_type,
- .suspend = i8042_suspend,
- .resume = i8042_resume,
- .shutdown = i8042_shutdown,
-};
-
-static void __init i8042_create_kbd_port(void)
+static int __devinit i8042_create_kbd_port(void)
{
struct serio *serio;
struct i8042_port *port = &i8042_ports[I8042_KBD_PORT_NO];
- serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
- if (serio) {
- memset(serio, 0, sizeof(struct serio));
- serio->id.type = i8042_direct ? SERIO_8042 : SERIO_8042_XL;
- serio->write = i8042_dumbkbd ? NULL : i8042_kbd_write;
- serio->open = i8042_open;
- serio->close = i8042_close;
- serio->start = i8042_start;
- serio->stop = i8042_stop;
- serio->port_data = port;
- serio->dev.parent = &i8042_platform_device->dev;
- strlcpy(serio->name, "i8042 Kbd Port", sizeof(serio->name));
- strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys));
-
- port->serio = serio;
- i8042_port_register(port);
- }
+ serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!serio)
+ return -ENOMEM;
+
+ serio->id.type = i8042_direct ? SERIO_8042 : SERIO_8042_XL;
+ serio->write = i8042_dumbkbd ? NULL : i8042_kbd_write;
+ serio->open = i8042_open;
+ serio->close = i8042_close;
+ serio->start = i8042_start;
+ serio->stop = i8042_stop;
+ serio->port_data = port;
+ serio->dev.parent = &i8042_platform_device->dev;
+ strlcpy(serio->name, "i8042 Kbd Port", sizeof(serio->name));
+ strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys));
+
+ port->serio = serio;
+
+ return i8042_port_register(port);
}
-static void __init i8042_create_aux_port(void)
+static int __devinit i8042_create_aux_port(void)
{
struct serio *serio;
struct i8042_port *port = &i8042_ports[I8042_AUX_PORT_NO];
- serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
- if (serio) {
- memset(serio, 0, sizeof(struct serio));
- serio->id.type = SERIO_8042;
- serio->write = i8042_aux_write;
- serio->open = i8042_open;
- serio->close = i8042_close;
- serio->start = i8042_start;
- serio->stop = i8042_stop;
- serio->port_data = port;
- serio->dev.parent = &i8042_platform_device->dev;
- strlcpy(serio->name, "i8042 Aux Port", sizeof(serio->name));
- strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys));
-
- port->serio = serio;
- i8042_port_register(port);
- }
+ serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!serio)
+ return -ENOMEM;
+
+ serio->id.type = SERIO_8042;
+ serio->write = i8042_aux_write;
+ serio->open = i8042_open;
+ serio->close = i8042_close;
+ serio->start = i8042_start;
+ serio->stop = i8042_stop;
+ serio->port_data = port;
+ serio->dev.parent = &i8042_platform_device->dev;
+ strlcpy(serio->name, "i8042 Aux Port", sizeof(serio->name));
+ strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys));
+
+ port->serio = serio;
+
+ return i8042_port_register(port);
}
-static void __init i8042_create_mux_port(int index)
+static int __devinit i8042_create_mux_port(int index)
{
struct serio *serio;
struct i8042_port *port = &i8042_ports[I8042_MUX_PORT_NO + index];
- serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
- if (serio) {
- memset(serio, 0, sizeof(struct serio));
- serio->id.type = SERIO_8042;
- serio->write = i8042_aux_write;
- serio->open = i8042_open;
- serio->close = i8042_close;
- serio->start = i8042_start;
- serio->stop = i8042_stop;
- serio->port_data = port;
- serio->dev.parent = &i8042_platform_device->dev;
- snprintf(serio->name, sizeof(serio->name), "i8042 Aux-%d Port", index);
- snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, index + 1);
-
- *port = i8042_ports[I8042_AUX_PORT_NO];
- port->exists = 0;
- snprintf(port->name, sizeof(port->name), "AUX%d", index);
- port->mux = index;
- port->serio = serio;
- i8042_port_register(port);
- }
+ serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!serio)
+ return -ENOMEM;
+
+ serio->id.type = SERIO_8042;
+ serio->write = i8042_aux_write;
+ serio->open = i8042_open;
+ serio->close = i8042_close;
+ serio->start = i8042_start;
+ serio->stop = i8042_stop;
+ serio->port_data = port;
+ serio->dev.parent = &i8042_platform_device->dev;
+ snprintf(serio->name, sizeof(serio->name), "i8042 Aux-%d Port", index);
+ snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, index + 1);
+
+ *port = i8042_ports[I8042_AUX_PORT_NO];
+ port->exists = 0;
+ snprintf(port->name, sizeof(port->name), "AUX%d", index);
+ port->mux = index;
+ port->serio = serio;
+
+ return i8042_port_register(port);
}
-static int __init i8042_init(void)
+static int __devinit i8042_probe(struct platform_device *dev)
{
- int i;
+ int i, have_ports = 0;
int err;
- dbg_init();
-
init_timer(&i8042_timer);
i8042_timer.function = i8042_timer_func;
- if (i8042_platform_init())
- return -EBUSY;
-
- i8042_ports[I8042_AUX_PORT_NO].irq = I8042_AUX_IRQ;
- i8042_ports[I8042_KBD_PORT_NO].irq = I8042_KBD_IRQ;
-
- if (i8042_controller_init()) {
- i8042_platform_exit();
+ if (i8042_controller_init())
return -ENODEV;
- }
- err = driver_register(&i8042_driver);
- if (err) {
- i8042_platform_exit();
- return err;
+ if (!i8042_noaux && !i8042_check_aux()) {
+ if (!i8042_nomux && !i8042_check_mux()) {
+ for (i = 0; i < I8042_NUM_MUX_PORTS; i++) {
+ err = i8042_create_mux_port(i);
+ if (err)
+ goto err_unregister_ports;
+ }
+ } else {
+ err = i8042_create_aux_port();
+ if (err)
+ goto err_unregister_ports;
+ }
+ have_ports = 1;
}
- i8042_platform_device = platform_device_register_simple("i8042", -1, NULL, 0);
- if (IS_ERR(i8042_platform_device)) {
- driver_unregister(&i8042_driver);
- i8042_platform_exit();
- return PTR_ERR(i8042_platform_device);
+ if (!i8042_nokbd) {
+ err = i8042_create_kbd_port();
+ if (err)
+ goto err_unregister_ports;
+ have_ports = 1;
}
- if (!i8042_noaux && !i8042_check_aux()) {
- if (!i8042_nomux && !i8042_check_mux())
- for (i = 0; i < I8042_NUM_MUX_PORTS; i++)
- i8042_create_mux_port(i);
- else
- i8042_create_aux_port();
+ if (!have_ports) {
+ err = -ENODEV;
+ goto err_controller_cleanup;
}
- i8042_create_kbd_port();
-
mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
-
return 0;
+
+ err_unregister_ports:
+ for (i = 0; i < I8042_NUM_PORTS; i++)
+ if (i8042_ports[i].serio)
+ serio_unregister_port(i8042_ports[i].serio);
+ err_controller_cleanup:
+ i8042_controller_cleanup();
+
+ return err;
}
-static void __exit i8042_exit(void)
+static int __devexit i8042_remove(struct platform_device *dev)
{
int i;
del_timer_sync(&i8042_timer);
+ return 0;
+}
+
+static struct platform_driver i8042_driver = {
+ .driver = {
+ .name = "i8042",
+ .owner = THIS_MODULE,
+ },
+ .probe = i8042_probe,
+ .remove = __devexit_p(i8042_remove),
+ .suspend = i8042_suspend,
+ .resume = i8042_resume,
+ .shutdown = i8042_shutdown,
+};
+
+static int __init i8042_init(void)
+{
+ int err;
+
+ dbg_init();
+
+ err = i8042_platform_init();
+ if (err)
+ return err;
+
+ i8042_ports[I8042_AUX_PORT_NO].irq = I8042_AUX_IRQ;
+ i8042_ports[I8042_KBD_PORT_NO].irq = I8042_KBD_IRQ;
+
+ err = platform_driver_register(&i8042_driver);
+ if (err)
+ goto err_platform_exit;
+
+ i8042_platform_device = platform_device_alloc("i8042", -1);
+ if (!i8042_platform_device) {
+ err = -ENOMEM;
+ goto err_unregister_driver;
+ }
+
+ err = platform_device_add(i8042_platform_device);
+ if (err)
+ goto err_free_device;
+
+ return 0;
+
+ err_free_device:
+ platform_device_put(i8042_platform_device);
+ err_unregister_driver:
+ platform_driver_unregister(&i8042_driver);
+ err_platform_exit:
+ i8042_platform_exit();
+
+ return err;
+}
+
+static void __exit i8042_exit(void)
+{
platform_device_unregister(i8042_platform_device);
- driver_unregister(&i8042_driver);
+ platform_driver_unregister(&i8042_driver);
i8042_platform_exit();