Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6
[pandora-kernel.git] / drivers / input / keyboard / atkbd.c
index fad04b6..a86afd0 100644 (file)
@@ -55,7 +55,7 @@ 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 = 0;
+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");
 
@@ -150,8 +150,8 @@ static unsigned char atkbd_unxlate_table[128] = {
 #define ATKBD_RET_EMUL0                0xe0
 #define ATKBD_RET_EMUL1                0xe1
 #define ATKBD_RET_RELEASE      0xf0
-#define ATKBD_RET_HANGUEL      0xf1
-#define ATKBD_RET_HANJA                0xf2
+#define ATKBD_RET_HANJA                0xf1
+#define ATKBD_RET_HANGEUL      0xf2
 #define ATKBD_RET_ERR          0xff
 
 #define ATKBD_KEY_UNKNOWN        0
@@ -170,6 +170,13 @@ static unsigned char atkbd_unxlate_table[128] = {
 #define ATKBD_LED_EVENT_BIT    0
 #define ATKBD_REP_EVENT_BIT    1
 
+#define ATKBD_XL_ERR           0x01
+#define ATKBD_XL_BAT           0x02
+#define ATKBD_XL_ACK           0x04
+#define ATKBD_XL_NAK           0x08
+#define ATKBD_XL_HANGEUL       0x10
+#define ATKBD_XL_HANJA         0x20
+
 static struct {
        unsigned char keycode;
        unsigned char set2;
@@ -211,8 +218,7 @@ struct atkbd {
        unsigned char emul;
        unsigned char resend;
        unsigned char release;
-       unsigned char bat_xl;
-       unsigned char err_xl;
+       unsigned long xl_bit;
        unsigned int last;
        unsigned long time;
 
@@ -245,17 +251,65 @@ ATKBD_DEFINE_ATTR(set);
 ATKBD_DEFINE_ATTR(softrepeat);
 ATKBD_DEFINE_ATTR(softraw);
 
+static const unsigned int xl_table[] = {
+       ATKBD_RET_BAT, ATKBD_RET_ERR, ATKBD_RET_ACK,
+       ATKBD_RET_NAK, ATKBD_RET_HANJA, ATKBD_RET_HANGEUL,
+};
 
-static void atkbd_report_key(struct input_dev *dev, struct pt_regs *regs, int code, int value)
+/*
+ * Checks if we should mangle the scancode to extract 'release' bit
+ * in translated mode.
+ */
+static int atkbd_need_xlate(unsigned long xl_bit, unsigned char code)
 {
-       input_regs(dev, regs);
-       if (value == 3) {
-               input_report_key(dev, code, 1);
-               input_sync(dev);
-               input_report_key(dev, code, 0);
-       } else
-               input_event(dev, EV_KEY, code, value);
-       input_sync(dev);
+       int i;
+
+       if (code == ATKBD_RET_EMUL0 || code == ATKBD_RET_EMUL1)
+               return 0;
+
+       for (i = 0; i < ARRAY_SIZE(xl_table); i++)
+               if (code == xl_table[i])
+                       return test_bit(i, &xl_bit);
+
+       return 1;
+}
+
+/*
+ * Calculates new value of xl_bit so the driver can distinguish
+ * between make/break pair of scancodes for select keys and PS/2
+ * protocol responses.
+ */
+static void atkbd_calculate_xl_bit(struct atkbd *atkbd, unsigned char code)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(xl_table); i++) {
+               if (!((code ^ xl_table[i]) & 0x7f)) {
+                       if (code & 0x80)
+                               __clear_bit(i, &atkbd->xl_bit);
+                       else
+                               __set_bit(i, &atkbd->xl_bit);
+                       break;
+               }
+       }
+}
+
+/*
+ * Encode the scancode, 0xe0 prefix, and high bit into a single integer,
+ * keeping kernel 2.4 compatibility for set 2
+ */
+static unsigned int atkbd_compat_scancode(struct atkbd *atkbd, unsigned int code)
+{
+       if (atkbd->set == 3) {
+               if (atkbd->emul == 1)
+                       code |= 0x100;
+        } else {
+               code = (code & 0x7f) | ((code & 0x80) << 1);
+               if (atkbd->emul == 1)
+                       code |= 0x80;
+       }
+
+       return code;
 }
 
 /*
@@ -267,9 +321,11 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
                        unsigned int flags, struct pt_regs *regs)
 {
        struct atkbd *atkbd = serio_get_drvdata(serio);
+       struct input_dev *dev = atkbd->dev;
        unsigned int code = data;
-       int scroll = 0, hscroll = 0, click = -1;
+       int scroll = 0, hscroll = 0, click = -1, add_release_event = 0;
        int value;
+       unsigned char keycode;
 
 #ifdef ATKBD_DEBUG
        printk(KERN_DEBUG "atkbd.c: Received %02x flags %02x\n", data, flags);
@@ -298,25 +354,17 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
        if (!atkbd->enabled)
                goto out;
 
-       input_event(atkbd->dev, EV_MSC, MSC_RAW, code);
+       input_event(dev, EV_MSC, MSC_RAW, code);
 
        if (atkbd->translated) {
 
-               if (atkbd->emul ||
-                   (code != ATKBD_RET_EMUL0 && code != ATKBD_RET_EMUL1 &&
-                    code != ATKBD_RET_HANGUEL && code != ATKBD_RET_HANJA &&
-                    (code != ATKBD_RET_ERR || atkbd->err_xl) &&
-                    (code != ATKBD_RET_BAT || atkbd->bat_xl))) {
+               if (atkbd->emul || atkbd_need_xlate(atkbd->xl_bit, code)) {
                        atkbd->release = code >> 7;
                        code &= 0x7f;
                }
 
-               if (!atkbd->emul) {
-                    if ((code & 0x7f) == (ATKBD_RET_BAT & 0x7f))
-                       atkbd->bat_xl = !(data >> 7);
-                    if ((code & 0x7f) == (ATKBD_RET_ERR & 0x7f))
-                       atkbd->err_xl = !(data >> 7);
-               }
+               if (!atkbd->emul)
+                       atkbd_calculate_xl_bit(atkbd, data);
        }
 
        switch (code) {
@@ -333,47 +381,48 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
                case ATKBD_RET_RELEASE:
                        atkbd->release = 1;
                        goto out;
-               case ATKBD_RET_HANGUEL:
-                       atkbd_report_key(atkbd->dev, regs, KEY_HANGUEL, 3);
+               case ATKBD_RET_ACK:
+               case ATKBD_RET_NAK:
+                       printk(KERN_WARNING "atkbd.c: Spurious %s on %s. "
+                              "Some program might be trying access hardware directly.\n",
+                              data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys);
                        goto out;
+               case ATKBD_RET_HANGEUL:
                case ATKBD_RET_HANJA:
-                       atkbd_report_key(atkbd->dev, regs, KEY_HANJA, 3);
-                       goto out;
+                       /*
+                        * These keys do not report release and thus need to be
+                        * flagged properly
+                        */
+                       add_release_event = 1;
+                       break;
                case ATKBD_RET_ERR:
                        printk(KERN_DEBUG "atkbd.c: Keyboard on %s reports too many keys pressed.\n", serio->phys);
                        goto out;
        }
 
-       if (atkbd->set != 3)
-               code = (code & 0x7f) | ((code & 0x80) << 1);
-       if (atkbd->emul) {
-               if (--atkbd->emul)
-                       goto out;
-               code |= (atkbd->set != 3) ? 0x80 : 0x100;
-       }
+       code = atkbd_compat_scancode(atkbd, code);
+
+       if (atkbd->emul && --atkbd->emul)
+               goto out;
+
+       keycode = atkbd->keycode[code];
 
-       if (atkbd->keycode[code] != ATKBD_KEY_NULL)
-               input_event(atkbd->dev, EV_MSC, MSC_SCAN, code);
+       if (keycode != ATKBD_KEY_NULL)
+               input_event(dev, EV_MSC, MSC_SCAN, code);
 
-       switch (atkbd->keycode[code]) {
+       switch (keycode) {
                case ATKBD_KEY_NULL:
                        break;
                case ATKBD_KEY_UNKNOWN:
-                       if (data == ATKBD_RET_ACK || data == ATKBD_RET_NAK) {
-                               printk(KERN_WARNING "atkbd.c: Spurious %s on %s. Some program, "
-                                      "like XFree86, might be trying access hardware directly.\n",
-                                      data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys);
-                       } else {
-                               printk(KERN_WARNING "atkbd.c: Unknown key %s "
-                                      "(%s set %d, code %#x on %s).\n",
-                                      atkbd->release ? "released" : "pressed",
-                                      atkbd->translated ? "translated" : "raw",
-                                      atkbd->set, code, serio->phys);
-                               printk(KERN_WARNING "atkbd.c: Use 'setkeycodes %s%02x <keycode>' "
-                                      "to make it known.\n",
-                                      code & 0x80 ? "e0" : "", code & 0x7f);
-                       }
-                       input_sync(atkbd->dev);
+                       printk(KERN_WARNING
+                              "atkbd.c: Unknown key %s (%s set %d, code %#x on %s).\n",
+                              atkbd->release ? "released" : "pressed",
+                              atkbd->translated ? "translated" : "raw",
+                              atkbd->set, code, serio->phys);
+                       printk(KERN_WARNING
+                              "atkbd.c: Use 'setkeycodes %s%02x <keycode>' to make it known.\n",
+                              code & 0x80 ? "e0" : "", code & 0x7f);
+                       input_sync(dev);
                        break;
                case ATKBD_SCR_1:
                        scroll = 1 - atkbd->release * 2;
@@ -397,33 +446,35 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
                        hscroll = 1;
                        break;
                default:
-                       value = atkbd->release ? 0 :
-                               (1 + (!atkbd->softrepeat && test_bit(atkbd->keycode[code], atkbd->dev->key)));
-
-                       switch (value) {        /* Workaround Toshiba laptop multiple keypress */
-                               case 0:
-                                       atkbd->last = 0;
-                                       break;
-                               case 1:
-                                       atkbd->last = code;
-                                       atkbd->time = jiffies + msecs_to_jiffies(atkbd->dev->rep[REP_DELAY]) / 2;
-                                       break;
-                               case 2:
-                                       if (!time_after(jiffies, atkbd->time) && atkbd->last == code)
-                                               value = 1;
-                                       break;
+                       if (atkbd->release) {
+                               value = 0;
+                               atkbd->last = 0;
+                       } else if (!atkbd->softrepeat && test_bit(keycode, dev->key)) {
+                               /* Workaround Toshiba laptop multiple keypress */
+                               value = time_before(jiffies, atkbd->time) && atkbd->last == code ? 1 : 2;
+                       } else {
+                               value = 1;
+                               atkbd->last = code;
+                               atkbd->time = jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]) / 2;
                        }
 
-                       atkbd_report_key(atkbd->dev, regs, atkbd->keycode[code], value);
+                       input_regs(dev, regs);
+                       input_event(dev, EV_KEY, keycode, value);
+                       input_sync(dev);
+
+                       if (value && add_release_event) {
+                               input_report_key(dev, keycode, 0);
+                               input_sync(dev);
+                       }
        }
 
        if (atkbd->scroll) {
-               input_regs(atkbd->dev, regs);
+               input_regs(dev, regs);
                if (click != -1)
-                       input_report_key(atkbd->dev, BTN_MIDDLE, click);
-               input_report_rel(atkbd->dev, REL_WHEEL, scroll);
-               input_report_rel(atkbd->dev, REL_HWHEEL, hscroll);
-               input_sync(atkbd->dev);
+                       input_report_key(dev, BTN_MIDDLE, click);
+               input_report_rel(dev, REL_WHEEL, scroll);
+               input_report_rel(dev, REL_HWHEEL, hscroll);
+               input_sync(dev);
        }
 
        atkbd->release = 0;
@@ -431,13 +482,7 @@ out:
        return IRQ_HANDLED;
 }
 
-/*
- * atkbd_event_work() is used to complete processing of events that
- * can not be processed by input_event() which is often called from
- * interrupt context.
- */
-
-static void atkbd_event_work(void *data)
+static int atkbd_set_repeat_rate(struct atkbd *atkbd)
 {
        const short period[32] =
                { 33,  37,  42,  46,  50,  54,  58,  63,  67,  75,  83,  92, 100, 109, 116, 125,
@@ -445,41 +490,64 @@ static void atkbd_event_work(void *data)
        const short delay[4] =
                { 250, 500, 750, 1000 };
 
-       struct atkbd *atkbd = data;
+       struct input_dev *dev = atkbd->dev;
+       unsigned char param;
+       int i = 0, j = 0;
+
+       while (i < ARRAY_SIZE(period) - 1 && period[i] < dev->rep[REP_PERIOD])
+               i++;
+       dev->rep[REP_PERIOD] = period[i];
+
+       while (j < ARRAY_SIZE(delay) - 1 && delay[j] < dev->rep[REP_DELAY])
+               j++;
+       dev->rep[REP_DELAY] = delay[j];
+
+       param = i | (j << 5);
+       return ps2_command(&atkbd->ps2dev, &param, ATKBD_CMD_SETREP);
+}
+
+static int atkbd_set_leds(struct atkbd *atkbd)
+{
        struct input_dev *dev = atkbd->dev;
        unsigned char param[2];
-       int i, j;
 
-       mutex_lock(&atkbd->event_mutex);
+       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);
+       if (ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_SETLEDS))
+               return -1;
 
-       if (test_and_clear_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask)) {
-               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);
-               ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_SETLEDS);
-
-               if (atkbd->extra) {
-                       param[0] = 0;
-                       param[1] = (test_bit(LED_COMPOSE, dev->led) ? 0x01 : 0)
-                                | (test_bit(LED_SLEEP,   dev->led) ? 0x02 : 0)
-                                | (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0)
-                                | (test_bit(LED_MISC,    dev->led) ? 0x10 : 0)
-                                | (test_bit(LED_MUTE,    dev->led) ? 0x20 : 0);
-                       ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_EX_SETLEDS);
-               }
+       if (atkbd->extra) {
+               param[0] = 0;
+               param[1] = (test_bit(LED_COMPOSE, dev->led) ? 0x01 : 0)
+                        | (test_bit(LED_SLEEP,   dev->led) ? 0x02 : 0)
+                        | (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0)
+                        | (test_bit(LED_MISC,    dev->led) ? 0x10 : 0)
+                        | (test_bit(LED_MUTE,    dev->led) ? 0x20 : 0);
+               if (ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_EX_SETLEDS))
+                       return -1;
        }
 
-       if (test_and_clear_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask)) {
-               i = j = 0;
-               while (i < 31 && period[i] < dev->rep[REP_PERIOD])
-                       i++;
-               while (j < 3 && delay[j] < dev->rep[REP_DELAY])
-                       j++;
-               dev->rep[REP_PERIOD] = period[i];
-               dev->rep[REP_DELAY] = delay[j];
-               param[0] = i | (j << 5);
-               ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_SETREP);
-       }
+       return 0;
+}
+
+/*
+ * atkbd_event_work() is used to complete processing of events that
+ * can not be processed by input_event() which is often called from
+ * interrupt context.
+ */
+
+static void atkbd_event_work(void *data)
+{
+       struct atkbd *atkbd = data;
+
+       mutex_lock(&atkbd->event_mutex);
+
+       if (test_and_clear_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask))
+               atkbd_set_leds(atkbd);
+
+       if (test_and_clear_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask))
+               atkbd_set_repeat_rate(atkbd);
 
        mutex_unlock(&atkbd->event_mutex);
 }
@@ -764,6 +832,9 @@ static void atkbd_set_keycode_table(struct atkbd *atkbd)
                        for (i = 0; i < ARRAY_SIZE(atkbd_scroll_keys); i++)
                                atkbd->keycode[atkbd_scroll_keys[i].set2] = atkbd_scroll_keys[i].keycode;
        }
+
+       atkbd->keycode[atkbd_compat_scancode(atkbd, ATKBD_RET_HANGEUL)] = KEY_HANGUEL;
+       atkbd->keycode[atkbd_compat_scancode(atkbd, ATKBD_RET_HANJA)] = KEY_HANJA;
 }
 
 /*
@@ -776,12 +847,15 @@ static void atkbd_set_device_attrs(struct atkbd *atkbd)
        int i;
 
        if (atkbd->extra)
-               sprintf(atkbd->name, "AT Set 2 Extra keyboard");
+               snprintf(atkbd->name, sizeof(atkbd->name),
+                        "AT Set 2 Extra keyboard");
        else
-               sprintf(atkbd->name, "AT %s Set %d keyboard",
-                       atkbd->translated ? "Translated" : "Raw", atkbd->set);
+               snprintf(atkbd->name, sizeof(atkbd->name),
+                        "AT %s Set %d keyboard",
+                        atkbd->translated ? "Translated" : "Raw", atkbd->set);
 
-       sprintf(atkbd->phys, "%s/input0", atkbd->ps2dev.serio->phys);
+       snprintf(atkbd->phys, sizeof(atkbd->phys),
+                "%s/input0", atkbd->ps2dev.serio->phys);
 
        input_dev->name = atkbd->name;
        input_dev->phys = atkbd->phys;
@@ -918,7 +992,6 @@ static int atkbd_reconnect(struct serio *serio)
 {
        struct atkbd *atkbd = serio_get_drvdata(serio);
        struct serio_driver *drv = serio->drv;
-       unsigned char param[1];
 
        if (!atkbd || !drv) {
                printk(KERN_DEBUG "atkbd: reconnect request, but serio is disconnected, ignoring...\n");
@@ -928,10 +1001,6 @@ static int atkbd_reconnect(struct serio *serio)
        atkbd_disable(atkbd);
 
        if (atkbd->write) {
-               param[0] = (test_bit(LED_SCROLLL, atkbd->dev->led) ? 1 : 0)
-                        | (test_bit(LED_NUML,    atkbd->dev->led) ? 2 : 0)
-                        | (test_bit(LED_CAPSL,   atkbd->dev->led) ? 4 : 0);
-
                if (atkbd_probe(atkbd))
                        return -1;
                if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra))
@@ -939,8 +1008,13 @@ static int atkbd_reconnect(struct serio *serio)
 
                atkbd_activate(atkbd);
 
-               if (ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_SETLEDS))
-                       return -1;
+/*
+ * Restore repeat rate and LEDs (that were reset by atkbd_activate)
+ * to pre-resume state
+ */
+               if (!atkbd->softrepeat)
+                       atkbd_set_repeat_rate(atkbd);
+               atkbd_set_leds(atkbd);
        }
 
        atkbd_enable(atkbd);