#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"
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");
#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
#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;
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;
};
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);
}
/*
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);
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) {
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;
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;
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,
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, ¶m, 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);
}
/*
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;
}
/*
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;
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) {
atkbd->softrepeat = atkbd_softrepeat;
atkbd->scroll = atkbd_scroll;
+ if (!atkbd->write)
+ atkbd->softrepeat = 1;
+
if (atkbd->softrepeat)
atkbd->softraw = 1;
{
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");
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))
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);