]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/input/keyboard/atkbd.c
Input: fix misspelling of Hangeul key
[mv-sheeva.git] / drivers / input / keyboard / atkbd.c
index 820c7fd9a6049c7f0e732de29b558a18ce503711..1bf61f00cbd26802223657b8fc8e7d832ad31c92 100644 (file)
@@ -27,6 +27,7 @@
 #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"
 
@@ -149,7 +150,7 @@ 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_HANGEUL      0xf1
 #define ATKBD_RET_HANJA                0xf2
 #define ATKBD_RET_ERR          0xff
 
@@ -166,6 +167,9 @@ static unsigned char atkbd_unxlate_table[128] = {
 
 #define ATKBD_SPECIAL          248
 
+#define ATKBD_LED_EVENT_BIT    0
+#define ATKBD_REP_EVENT_BIT    1
+
 static struct {
        unsigned char keycode;
        unsigned char set2;
@@ -211,6 +215,10 @@ struct atkbd {
        unsigned char err_xl;
        unsigned int last;
        unsigned long time;
+
+       struct work_struct event_work;
+       struct mutex event_mutex;
+       unsigned long event_mask;
 };
 
 static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf,
@@ -295,26 +303,26 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
        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))) {
+                   (code != ATKBD_RET_EMUL0 && code != ATKBD_RET_EMUL1 &&
+                    code != ATKBD_RET_HANGEUL && 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) {
                     if ((code & 0x7f) == (ATKBD_RET_BAT & 0x7f))
-                       atkbd->bat_xl = !atkbd->release;
+                       atkbd->bat_xl = !(data >> 7);
                     if ((code & 0x7f) == (ATKBD_RET_ERR & 0x7f))
-                       atkbd->err_xl = !atkbd->release;
+                       atkbd->err_xl = !(data >> 7);
                }
        }
 
        switch (code) {
                case ATKBD_RET_BAT:
                        atkbd->enabled = 0;
-                       serio_rescan(atkbd->ps2dev.serio);
+                       serio_reconnect(atkbd->ps2dev.serio);
                        goto out;
                case ATKBD_RET_EMUL0:
                        atkbd->emul = 1;
@@ -325,8 +333,8 @@ 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_HANGEUL:
+                       atkbd_report_key(atkbd->dev, regs, KEY_HANGEUL, 3);
                        goto out;
                case ATKBD_RET_HANJA:
                        atkbd_report_key(atkbd->dev, regs, KEY_HANJA, 3);
@@ -424,58 +432,86 @@ out:
 }
 
 /*
- * Event callback from the input module. Events that change the state of
- * the hardware are processed here.
+ * 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 int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+static void atkbd_event_work(void *data)
 {
-       struct atkbd *atkbd = dev->private;
        const short period[32] =
                { 33,  37,  42,  46,  50,  54,  58,  63,  67,  75,  83,  92, 100, 109, 116, 125,
                 133, 149, 167, 182, 200, 217, 232, 250, 270, 303, 333, 370, 400, 435, 470, 500 };
        const short delay[4] =
                { 250, 500, 750, 1000 };
+
+       struct atkbd *atkbd = data;
+       struct input_dev *dev = atkbd->dev;
        unsigned char param[2];
        int i, j;
 
+       mutex_lock(&atkbd->event_mutex);
+
+       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 (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);
+}
+
+/*
+ * Event callback from the input module. Events that change the state of
+ * the hardware are processed here. If action can not be performed in
+ * interrupt context it is offloaded to atkbd_event_work.
+ */
+
+static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+       struct atkbd *atkbd = dev->private;
+
        if (!atkbd->write)
                return -1;
 
        switch (type) {
 
                case EV_LED:
-
-                       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_schedule_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_schedule_command(&atkbd->ps2dev, param, ATKBD_CMD_EX_SETLEDS);
-                       }
-
+                       set_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask);
+                       wmb();
+                       schedule_work(&atkbd->event_work);
                        return 0;
 
                case EV_REP:
 
-                       if (atkbd->softrepeat) return 0;
-
-                       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_schedule_command(&atkbd->ps2dev, param, ATKBD_CMD_SETREP);
+                       if (!atkbd->softrepeat) {
+                               set_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask);
+                               wmb();
+                               schedule_work(&atkbd->event_work);
+                       }
 
                        return 0;
        }
@@ -740,12 +776,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;
@@ -810,6 +849,8 @@ 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);
 
        switch (serio->id.type) {
 
@@ -825,9 +866,6 @@ 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;