linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / input / keyboard / atkbd.c
index dd99588..ffacf6e 100644 (file)
@@ -27,7 +27,6 @@
 #include <linux/serio.h>
 #include <linux/workqueue.h>
 #include <linux/libps2.h>
-#include <linux/mutex.h>
 
 #define DRIVER_DESC    "AT and PS/2 keyboard driver"
 
@@ -55,7 +54,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;
+static int atkbd_scroll = 0;
 module_param_named(scroll, atkbd_scroll, bool, 0);
 MODULE_PARM_DESC(scroll, "Enable scroll-wheel on MS Office and similar keyboards");
 
@@ -150,8 +149,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_HANJA                0xf1
-#define ATKBD_RET_HANGEUL      0xf2
+#define ATKBD_RET_HANGUEL      0xf1
+#define ATKBD_RET_HANJA                0xf2
 #define ATKBD_RET_ERR          0xff
 
 #define ATKBD_KEY_UNKNOWN        0
@@ -170,13 +169,6 @@ 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;
@@ -218,12 +210,13 @@ struct atkbd {
        unsigned char emul;
        unsigned char resend;
        unsigned char release;
-       unsigned long xl_bit;
+       unsigned char bat_xl;
+       unsigned char err_xl;
        unsigned int last;
        unsigned long time;
 
        struct work_struct event_work;
-       struct mutex event_mutex;
+       struct semaphore event_sem;
        unsigned long event_mask;
 };
 
@@ -251,65 +244,17 @@ 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,
-};
-
-/*
- * 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)
-{
-       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)
+static void atkbd_report_key(struct input_dev *dev, struct pt_regs *regs, int code, int value)
 {
-       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;
+       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);
 }
 
 /*
@@ -321,11 +266,9 @@ 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, add_release_event = 0;
+       int scroll = 0, hscroll = 0, click = -1;
        int value;
-       unsigned char keycode;
 
 #ifdef ATKBD_DEBUG
        printk(KERN_DEBUG "atkbd.c: Received %02x flags %02x\n", data, flags);
@@ -354,17 +297,25 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
        if (!atkbd->enabled)
                goto out;
 
-       input_event(dev, EV_MSC, MSC_RAW, code);
+       input_event(atkbd->dev, EV_MSC, MSC_RAW, code);
 
        if (atkbd->translated) {
 
-               if (atkbd->emul || atkbd_need_xlate(atkbd->xl_bit, code)) {
+               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))) {
                        atkbd->release = code >> 7;
                        code &= 0x7f;
                }
 
-               if (!atkbd->emul)
-                       atkbd_calculate_xl_bit(atkbd, data);
+               if (!atkbd->emul) {
+                    if ((code & 0x7f) == (ATKBD_RET_BAT & 0x7f))
+                       atkbd->bat_xl = !atkbd->release;
+                    if ((code & 0x7f) == (ATKBD_RET_ERR & 0x7f))
+                       atkbd->err_xl = !atkbd->release;
+               }
        }
 
        switch (code) {
@@ -381,52 +332,47 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
                case ATKBD_RET_RELEASE:
                        atkbd->release = 1;
                        goto out;
-               case ATKBD_RET_ACK:
-               case ATKBD_RET_NAK:
-#if 0
-                       /* Quite a few key switchers and other tools trigger this and it confuses
-                          people who can do nothing about it */
-                       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);
-#endif
+               case ATKBD_RET_HANGUEL:
+                       atkbd_report_key(atkbd->dev, regs, KEY_HANGUEL, 3);
                        goto out;
-               case ATKBD_RET_HANGEUL:
                case ATKBD_RET_HANJA:
-                       /*
-                        * These keys do not report release and thus need to be
-                        * flagged properly
-                        */
-                       add_release_event = 1;
-                       break;
+                       atkbd_report_key(atkbd->dev, regs, KEY_HANJA, 3);
+                       goto out;
                case ATKBD_RET_ERR:
-//                     printk(KERN_DEBUG "atkbd.c: Keyboard on %s reports too many keys pressed.\n", serio->phys);
+                       printk(KERN_DEBUG "atkbd.c: Keyboard on %s reports too many keys pressed.\n", serio->phys);
                        goto out;
        }
 
-       code = atkbd_compat_scancode(atkbd, code);
-
-       if (atkbd->emul && --atkbd->emul)
-               goto out;
-
-       keycode = atkbd->keycode[code];
+       if (atkbd->set != 3)
+               code = (code & 0x7f) | ((code & 0x80) << 1);
+       if (atkbd->emul) {
+               if (--atkbd->emul)
+                       goto out;
+               code |= (atkbd->set != 3) ? 0x80 : 0x100;
+       }
 
-       if (keycode != ATKBD_KEY_NULL)
-               input_event(dev, EV_MSC, MSC_SCAN, code);
+       if (atkbd->keycode[code] != ATKBD_KEY_NULL)
+               input_event(atkbd->dev, EV_MSC, MSC_SCAN, code);
 
-       switch (keycode) {
+       switch (atkbd->keycode[code]) {
                case ATKBD_KEY_NULL:
                        break;
                case ATKBD_KEY_UNKNOWN:
-                       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);
+                       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);
                        break;
                case ATKBD_SCR_1:
                        scroll = 1 - atkbd->release * 2;
@@ -450,35 +396,33 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
                        hscroll = 1;
                        break;
                default:
-                       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;
+                       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;
                        }
 
-                       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);
-                       }
+                       atkbd_report_key(atkbd->dev, regs, atkbd->keycode[code], value);
        }
 
        if (atkbd->scroll) {
-               input_regs(dev, regs);
+               input_regs(atkbd->dev, regs);
                if (click != -1)
-                       input_report_key(dev, BTN_MIDDLE, click);
-               input_report_rel(dev, REL_WHEEL, scroll);
-               input_report_rel(dev, REL_HWHEEL, hscroll);
-               input_sync(dev);
+                       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);
        }
 
        atkbd->release = 0;
@@ -486,7 +430,13 @@ out:
        return IRQ_HANDLED;
 }
 
-static int atkbd_set_repeat_rate(struct atkbd *atkbd)
+/*
+ * 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)
 {
        const short period[32] =
                { 33,  37,  42,  46,  50,  54,  58,  63,  67,  75,  83,  92, 100, 109, 116, 125,
@@ -494,66 +444,43 @@ static int atkbd_set_repeat_rate(struct atkbd *atkbd)
        const short delay[4] =
                { 250, 500, 750, 1000 };
 
-       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 atkbd *atkbd = data;
        struct input_dev *dev = atkbd->dev;
        unsigned char param[2];
+       int i, j;
 
-       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 (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;
+       down(&atkbd->event_sem);
+
+       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);
+               }
        }
 
-       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);
+       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);
+       }
 
-       mutex_unlock(&atkbd->event_mutex);
+       up(&atkbd->event_sem);
 }
 
 /*
@@ -836,9 +763,6 @@ 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;
 }
 
 /*
@@ -851,15 +775,12 @@ static void atkbd_set_device_attrs(struct atkbd *atkbd)
        int i;
 
        if (atkbd->extra)
-               snprintf(atkbd->name, sizeof(atkbd->name),
-                        "AT Set 2 Extra keyboard");
+               sprintf(atkbd->name, "AT Set 2 Extra keyboard");
        else
-               snprintf(atkbd->name, sizeof(atkbd->name),
-                        "AT %s Set %d keyboard",
-                        atkbd->translated ? "Translated" : "Raw", atkbd->set);
+               sprintf(atkbd->name, "AT %s Set %d keyboard",
+                       atkbd->translated ? "Translated" : "Raw", atkbd->set);
 
-       snprintf(atkbd->phys, sizeof(atkbd->phys),
-                "%s/input0", atkbd->ps2dev.serio->phys);
+       sprintf(atkbd->phys, "%s/input0", atkbd->ps2dev.serio->phys);
 
        input_dev->name = atkbd->name;
        input_dev->phys = atkbd->phys;
@@ -925,7 +846,7 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
        atkbd->dev = dev;
        ps2_init(&atkbd->ps2dev, serio);
        INIT_WORK(&atkbd->event_work, atkbd_event_work, atkbd);
-       mutex_init(&atkbd->event_mutex);
+       init_MUTEX(&atkbd->event_sem);
 
        switch (serio->id.type) {
 
@@ -941,6 +862,9 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
        atkbd->softrepeat = atkbd_softrepeat;
        atkbd->scroll = atkbd_scroll;
 
+       if (!atkbd->write)
+               atkbd->softrepeat = 1;
+
        if (atkbd->softrepeat)
                atkbd->softraw = 1;
 
@@ -996,6 +920,7 @@ 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");
@@ -1005,6 +930,10 @@ 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))
@@ -1012,13 +941,8 @@ static int atkbd_reconnect(struct serio *serio)
 
                atkbd_activate(atkbd);
 
-/*
- * 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);
+               if (ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_SETLEDS))
+                       return -1;
        }
 
        atkbd_enable(atkbd);