vserver 1.9.3
[linux-2.6.git] / drivers / input / keyboard / atkbd.c
index 00ec1f8..ec97ae7 100644 (file)
 #include <linux/serio.h>
 #include <linux/workqueue.h>
 
+#define DRIVER_DESC    "AT and PS/2 keyboard driver"
+
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
-MODULE_DESCRIPTION("AT and PS/2 keyboard driver");
+MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 
 static int atkbd_set = 2;
@@ -47,6 +49,10 @@ static int atkbd_softrepeat;
 module_param_named(softrepeat, atkbd_softrepeat, bool, 0);
 MODULE_PARM_DESC(softrepeat, "Use software keyboard repeat");
 
+static int atkbd_softraw = 1;
+module_param_named(softraw, atkbd_softraw, bool, 0);
+MODULE_PARM_DESC(softraw, "Use software generated rawmode");
+
 static int atkbd_scroll;
 module_param_named(scroll, atkbd_scroll, bool, 0);
 MODULE_PARM_DESC(scroll, "Enable scroll-wheel on MS Office and similar keyboards");
@@ -164,36 +170,64 @@ static unsigned char atkbd_scroll_keys[5][2] = {
        { ATKBD_SCR_CLICK, 0x60 },
 };
 
+#define ATKBD_FLAG_ACK         0       /* Waiting for ACK/NAK */
+#define ATKBD_FLAG_CMD         1       /* Waiting for command to finish */
+#define ATKBD_FLAG_CMD1                2       /* First byte of command response */
+#define ATKBD_FLAG_ENABLED     3       /* Waining for init to finish */
+
 /*
  * The atkbd control structure
  */
 
 struct atkbd {
-       unsigned char keycode[512];
-       struct input_dev dev;
-       struct serio *serio;
 
+       /* Written only during init */
        char name[64];
        char phys[32];
-       unsigned short id;
+       struct serio *serio;
+       struct input_dev dev;
+
        unsigned char set;
-       unsigned int translated:1;
-       unsigned int extra:1;
-       unsigned int write:1;
+       unsigned short id;
+       unsigned char keycode[512];
+       unsigned char translated;
+       unsigned char extra;
+       unsigned char write;
+
+       /* Protected by FLAG_ACK */
+       unsigned char nak;
 
+       /* Protected by FLAG_CMD */
        unsigned char cmdbuf[4];
        unsigned char cmdcnt;
-       volatile signed char ack;
-       unsigned char emul;
-       unsigned int resend:1;
-       unsigned int release:1;
-       unsigned int bat_xl:1;
-       unsigned int enabled:1;
 
+       /* Accessed only from interrupt */
+       unsigned char emul;
+       unsigned char resend;
+       unsigned char release;
+       unsigned char bat_xl;
        unsigned int last;
        unsigned long time;
+
+       /* Ensures that only one command is executing at a time */
+       struct semaphore cmd_sem;
+
+       /* Used to signal completion from interrupt handler */
+       wait_queue_head_t wait;
+
+       /* Flags */
+       unsigned long flags;
+};
+
+/* Work structure to schedule execution of a command */
+struct atkbd_work {
+       struct work_struct work;
+       struct atkbd *atkbd;
+       int command;
+       unsigned char param[0];
 };
 
+
 static void atkbd_report_key(struct input_dev *dev, struct pt_regs *regs, int code, int value)
 {
        input_regs(dev, regs);
@@ -224,7 +258,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
 
 #if !defined(__i386__) && !defined (__x86_64__)
        if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && !atkbd->resend && atkbd->write) {
-               printk("atkbd.c: frame/parity error: %02x\n", flags);
+               printk(KERN_WARNING "atkbd.c: frame/parity error: %02x\n", flags);
                serio_write(serio, ATKBD_CMD_RESEND);
                atkbd->resend = 1;
                goto out;
@@ -234,24 +268,46 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
                atkbd->resend = 0;
 #endif
 
-       if (!atkbd->ack)
+       if (test_bit(ATKBD_FLAG_ACK, &atkbd->flags)) {
                switch (code) {
                        case ATKBD_RET_ACK:
-                               atkbd->ack = 1;
-                               goto out;
+                               atkbd->nak = 0;
+                               if (atkbd->cmdcnt) {
+                                       set_bit(ATKBD_FLAG_CMD, &atkbd->flags);
+                                       set_bit(ATKBD_FLAG_CMD1, &atkbd->flags);
+                               }
+                               clear_bit(ATKBD_FLAG_ACK, &atkbd->flags);
+                               wake_up_interruptible(&atkbd->wait);
+                               break;
                        case ATKBD_RET_NAK:
-                               atkbd->ack = -1;
-                               goto out;
+                               atkbd->nak = 1;
+                               clear_bit(ATKBD_FLAG_ACK, &atkbd->flags);
+                               wake_up_interruptible(&atkbd->wait);
+                               break;
                }
+               goto out;
+       }
+
+       if (test_bit(ATKBD_FLAG_CMD, &atkbd->flags)) {
+
+               if (atkbd->cmdcnt)
+                       atkbd->cmdbuf[--atkbd->cmdcnt] = code;
 
-       if (atkbd->cmdcnt) {
-               atkbd->cmdbuf[--atkbd->cmdcnt] = code;
+               if (test_and_clear_bit(ATKBD_FLAG_CMD1, &atkbd->flags) && atkbd->cmdcnt)
+                       wake_up_interruptible(&atkbd->wait);
+
+               if (!atkbd->cmdcnt) {
+                       clear_bit(ATKBD_FLAG_CMD, &atkbd->flags);
+                       wake_up_interruptible(&atkbd->wait);
+               }
                goto out;
        }
 
-       if (!atkbd->enabled)
+       if (!test_bit(ATKBD_FLAG_ENABLED, &atkbd->flags))
                goto out;
 
+       input_event(&atkbd->dev, EV_MSC, MSC_RAW, code);
+
        if (atkbd->translated) {
 
                if (atkbd->emul ||
@@ -270,6 +326,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
 
        switch (code) {
                case ATKBD_RET_BAT:
+                       clear_bit(ATKBD_FLAG_ENABLED, &atkbd->flags);
                        serio_rescan(atkbd->serio);
                        goto out;
                case ATKBD_RET_EMUL0:
@@ -300,6 +357,9 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
                code |= (atkbd->set != 3) ? 0x80 : 0x100;
        }
 
+       if (atkbd->keycode[code] != ATKBD_KEY_NULL)
+               input_event(&atkbd->dev, EV_MSC, MSC_SCAN, code);
+
        switch (atkbd->keycode[code]) {
                case ATKBD_KEY_NULL:
                        break;
@@ -372,83 +432,147 @@ out:
  * acknowledge. It doesn't handle resends according to the keyboard
  * protocol specs, because if these are needed, the keyboard needs
  * replacement anyway, and they only make a mess in the protocol.
+ *
+ * atkbd_sendbyte() can only be called from a process context
  */
 
 static int atkbd_sendbyte(struct atkbd *atkbd, unsigned char byte)
 {
-       int timeout = 20000; /* 200 msec */
-       atkbd->ack = 0;
-
 #ifdef ATKBD_DEBUG
        printk(KERN_DEBUG "atkbd.c: Sent: %02x\n", byte);
 #endif
-       if (serio_write(atkbd->serio, byte))
-               return -1;
+       atkbd->nak = 1;
+       set_bit(ATKBD_FLAG_ACK, &atkbd->flags);
 
-       while (!atkbd->ack && timeout--) udelay(10);
+       if (serio_write(atkbd->serio, byte) == 0)
+               wait_event_interruptible_timeout(atkbd->wait,
+                               !test_bit(ATKBD_FLAG_ACK, &atkbd->flags),
+                               msecs_to_jiffies(200));
 
-       return -(atkbd->ack <= 0);
+       clear_bit(ATKBD_FLAG_ACK, &atkbd->flags);
+       return -atkbd->nak;
 }
 
 /*
  * atkbd_command() sends a command, and its parameters to the keyboard,
  * then waits for the response and puts it in the param array.
+ *
+ * atkbd_command() can only be called from a process context
  */
 
 static int atkbd_command(struct atkbd *atkbd, unsigned char *param, int command)
 {
-       int timeout = 500000; /* 500 msec */
+       int timeout;
        int send = (command >> 12) & 0xf;
        int receive = (command >> 8) & 0xf;
+       int rc = -1;
        int i;
 
-       atkbd->cmdcnt = receive;
+       timeout = msecs_to_jiffies(command == ATKBD_CMD_RESET_BAT ? 4000 : 500);
 
-       if (command == ATKBD_CMD_RESET_BAT)
-               timeout = 2000000; /* 2 sec */
+       down(&atkbd->cmd_sem);
+       clear_bit(ATKBD_FLAG_CMD, &atkbd->flags);
 
        if (receive && param)
                for (i = 0; i < receive; i++)
                        atkbd->cmdbuf[(receive - 1) - i] = param[i];
 
+       atkbd->cmdcnt = receive;
+
        if (command & 0xff)
                if (atkbd_sendbyte(atkbd, command & 0xff))
-                       return (atkbd->cmdcnt = 0) - 1;
+                       goto out;
 
        for (i = 0; i < send; i++)
                if (atkbd_sendbyte(atkbd, param[i]))
-                       return (atkbd->cmdcnt = 0) - 1;
-
-       while (atkbd->cmdcnt && timeout--) {
-
-               if (atkbd->cmdcnt == 1 &&
-                   command == ATKBD_CMD_RESET_BAT && timeout > 100000)
-                       timeout = 100000;
+                       goto out;
 
-               if (atkbd->cmdcnt == 1 && command == ATKBD_CMD_GETID &&
-                   atkbd->cmdbuf[1] != 0xab && atkbd->cmdbuf[1] != 0xac) {
+       timeout = wait_event_interruptible_timeout(atkbd->wait,
+                               !test_bit(ATKBD_FLAG_CMD1, &atkbd->flags), timeout);
+
+       if (atkbd->cmdcnt && timeout > 0) {
+               if (command == ATKBD_CMD_RESET_BAT && jiffies_to_msecs(timeout) > 100)
+                       timeout = msecs_to_jiffies(100);
+
+               if (command == ATKBD_CMD_GETID &&
+                   atkbd->cmdbuf[receive - 1] != 0xab && atkbd->cmdbuf[receive - 1] != 0xac) {
+                       /*
+                        * Device behind the port is not a keyboard
+                        * so we don't need to wait for the 2nd byte
+                        * of ID response.
+                        */
+                       clear_bit(ATKBD_FLAG_CMD, &atkbd->flags);
                        atkbd->cmdcnt = 0;
-                       break;
                }
 
-               udelay(1);
+               wait_event_interruptible_timeout(atkbd->wait,
+                               !test_bit(ATKBD_FLAG_CMD, &atkbd->flags), timeout);
        }
 
        if (param)
                for (i = 0; i < receive; i++)
                        param[i] = atkbd->cmdbuf[(receive - 1) - i];
 
-       if (command == ATKBD_CMD_RESET_BAT && atkbd->cmdcnt == 1)
-               atkbd->cmdcnt = 0;
+       if (atkbd->cmdcnt && (command != ATKBD_CMD_RESET_BAT || atkbd->cmdcnt != 1))
+               goto out;
+
+       rc = 0;
+
+out:
+       clear_bit(ATKBD_FLAG_CMD, &atkbd->flags);
+       clear_bit(ATKBD_FLAG_CMD1, &atkbd->flags);
+       up(&atkbd->cmd_sem);
+
+       return rc;
+}
+
+/*
+ * atkbd_execute_scheduled_command() sends a command, previously scheduled by
+ * atkbd_schedule_command(), to the keyboard.
+ */
+
+static void atkbd_execute_scheduled_command(void *data)
+{
+       struct atkbd_work *atkbd_work = data;
+
+       atkbd_command(atkbd_work->atkbd, atkbd_work->param, atkbd_work->command);
+
+       kfree(atkbd_work);
+}
+
+/*
+ * atkbd_schedule_command() allows to schedule delayed execution of a keyboard
+ * command and can be used to issue a command from an interrupt or softirq
+ * context.
+ */
+
+static int atkbd_schedule_command(struct atkbd *atkbd, unsigned char *param, int command)
+{
+       struct atkbd_work *atkbd_work;
+       int send = (command >> 12) & 0xf;
+       int receive = (command >> 8) & 0xf;
+
+       if (!test_bit(ATKBD_FLAG_ENABLED, &atkbd->flags))
+               return -1;
+
+       if (!(atkbd_work = kmalloc(sizeof(struct atkbd_work) + max(send, receive), GFP_ATOMIC)))
+               return -1;
+
+       memset(atkbd_work, 0, sizeof(struct atkbd_work));
+       atkbd_work->atkbd = atkbd;
+       atkbd_work->command = command;
+       memcpy(atkbd_work->param, param, send);
+       INIT_WORK(&atkbd_work->work, atkbd_execute_scheduled_command, atkbd_work);
 
-       if (atkbd->cmdcnt) {
-               atkbd->cmdcnt = 0;
+       if (!schedule_work(&atkbd_work->work)) {
+               kfree(atkbd_work);
                return -1;
        }
 
        return 0;
 }
 
+
 /*
  * Event callback from the input module. Events that change the state of
  * the hardware are processed here.
@@ -475,7 +599,7 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co
                        param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0)
                                 | (test_bit(LED_NUML,    dev->led) ? 2 : 0)
                                 | (test_bit(LED_CAPSL,   dev->led) ? 4 : 0);
-                       atkbd_command(atkbd, param, ATKBD_CMD_SETLEDS);
+                       atkbd_schedule_command(atkbd, param, ATKBD_CMD_SETLEDS);
 
                        if (atkbd->extra) {
                                param[0] = 0;
@@ -484,7 +608,7 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co
                                         | (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0)
                                         | (test_bit(LED_MISC,    dev->led) ? 0x10 : 0)
                                         | (test_bit(LED_MUTE,    dev->led) ? 0x20 : 0);
-                               atkbd_command(atkbd, param, ATKBD_CMD_EX_SETLEDS);
+                               atkbd_schedule_command(atkbd, param, ATKBD_CMD_EX_SETLEDS);
                        }
 
                        return 0;
@@ -500,7 +624,7 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co
                        dev->rep[REP_PERIOD] = period[i];
                        dev->rep[REP_DELAY] = delay[j];
                        param[0] = i | (j << 5);
-                       atkbd_command(atkbd, param, ATKBD_CMD_SETREP);
+                       atkbd_schedule_command(atkbd, param, ATKBD_CMD_SETREP);
 
                        return 0;
        }
@@ -672,6 +796,11 @@ static void atkbd_cleanup(struct serio *serio)
 static void atkbd_disconnect(struct serio *serio)
 {
        struct atkbd *atkbd = serio->private;
+
+       clear_bit(ATKBD_FLAG_ENABLED, &atkbd->flags);
+       synchronize_kernel();
+       flush_scheduled_work();
+
        input_unregister_device(&atkbd->dev);
        serio_close(serio);
        kfree(atkbd);
@@ -684,7 +813,7 @@ static void atkbd_disconnect(struct serio *serio)
  * to the input module.
  */
 
-static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
+static void atkbd_connect(struct serio *serio, struct serio_driver *drv)
 {
        struct atkbd *atkbd;
        int i;
@@ -693,6 +822,9 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
                return;
        memset(atkbd, 0, sizeof(struct atkbd));
 
+       init_MUTEX(&atkbd->cmd_sem);
+       init_waitqueue_head(&atkbd->wait);
+
        switch (serio->type & SERIO_TYPE) {
 
                case SERIO_8042_XL:
@@ -709,17 +841,22 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
                        return;
        }
 
+       if (!atkbd->write)
+               atkbd_softrepeat = 1;
+       if (atkbd_softrepeat)
+               atkbd_softraw = 1;
+
        if (atkbd->write) {
-               atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
+               atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP) | BIT(EV_MSC);
                atkbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
-       } else  atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+       } else  atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_MSC);
+       atkbd->dev.mscbit[0] = atkbd_softraw ? BIT(MSC_SCAN) : BIT(MSC_RAW) | BIT(MSC_SCAN);
 
        if (!atkbd_softrepeat) {
                atkbd->dev.rep[REP_DELAY] = 250;
                atkbd->dev.rep[REP_PERIOD] = 33;
-       }
+       } else atkbd_softraw = 1;
 
-       atkbd->ack = 1;
        atkbd->serio = serio;
 
        init_input_dev(&atkbd->dev);
@@ -732,7 +869,7 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
 
        serio->private = atkbd;
 
-       if (serio_open(serio, dev)) {
+       if (serio_open(serio, drv)) {
                kfree(atkbd);
                return;
        }
@@ -754,8 +891,6 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
                atkbd->id = 0xab00;
        }
 
-       atkbd->enabled = 1;
-
        if (atkbd->extra) {
                atkbd->dev.ledbit[0] |= BIT(LED_COMPOSE) | BIT(LED_SUSPEND) | BIT(LED_SLEEP) | BIT(LED_MUTE) | BIT(LED_MISC);
                sprintf(atkbd->name, "AT Set 2 Extra keyboard");
@@ -797,6 +932,8 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
 
        input_register_device(&atkbd->dev);
 
+       set_bit(ATKBD_FLAG_ENABLED, &atkbd->flags);
+
        printk(KERN_INFO "input: %s on %s\n", atkbd->name, serio->phys);
 }
 
@@ -808,10 +945,10 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
 static int atkbd_reconnect(struct serio *serio)
 {
        struct atkbd *atkbd = serio->private;
-       struct serio_dev *dev = serio->dev;
+       struct serio_driver *drv = serio->drv;
        unsigned char param[1];
 
-       if (!dev) {
+       if (!drv) {
                printk(KERN_DEBUG "atkbd: reconnect request, but serio is disconnected, ignoring...\n");
                return -1;
        }
@@ -832,26 +969,32 @@ static int atkbd_reconnect(struct serio *serio)
                        return -1;
        }
 
+       set_bit(ATKBD_FLAG_ENABLED, &atkbd->flags);
+
        return 0;
 }
 
-static struct serio_dev atkbd_dev = {
-       .interrupt =    atkbd_interrupt,
-       .connect =      atkbd_connect,
-       .reconnect =    atkbd_reconnect,
-       .disconnect =   atkbd_disconnect,
-       .cleanup =      atkbd_cleanup,
+static struct serio_driver atkbd_drv = {
+       .driver         = {
+               .name   = "atkbd",
+       },
+       .description    = DRIVER_DESC,
+       .interrupt      = atkbd_interrupt,
+       .connect        = atkbd_connect,
+       .reconnect      = atkbd_reconnect,
+       .disconnect     = atkbd_disconnect,
+       .cleanup        = atkbd_cleanup,
 };
 
 int __init atkbd_init(void)
 {
-       serio_register_device(&atkbd_dev);
+       serio_register_driver(&atkbd_drv);
        return 0;
 }
 
 void __exit atkbd_exit(void)
 {
-       serio_unregister_device(&atkbd_dev);
+       serio_unregister_driver(&atkbd_drv);
 }
 
 module_init(atkbd_init);