Commit 0ae051a1 authored by Dmitry Torokhov's avatar Dmitry Torokhov
Browse files

Input: atkbd - fix HANGEUL/HANJA keys



Make atkbd report HANGEUL/HANJA keys by default and use correct scan
codes for these keys (they were swapped). Also make sure their scancodes
reported as EV_MSC/MSC_SCAN events.

Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent b9ab58dd
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -1075,10 +1075,12 @@ static int emulate_raw(struct vc_data *vc, unsigned int keycode,
			put_queue(vc, 0x45 | up_flag);
			return 0;
		case KEY_HANGEUL:
			if (!up_flag) put_queue(vc, 0xf1);
			if (!up_flag)
				put_queue(vc, 0xf2);
			return 0;
		case KEY_HANJA:
			if (!up_flag) put_queue(vc, 0xf2);
			if (!up_flag)
				put_queue(vc, 0xf1);
			return 0;
	}

+131 −77
Original line number Diff line number Diff line
@@ -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_HANGEUL	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_HANGEUL && 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_HANGEUL:
			atkbd_report_key(atkbd->dev, regs, KEY_HANGEUL, 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)
	code = atkbd_compat_scancode(atkbd, code);

	if (atkbd->emul && --atkbd->emul)
		goto out;
		code |= (atkbd->set != 3) ? 0x80 : 0x100;
	}

	if (atkbd->keycode[code] != ATKBD_KEY_NULL)
		input_event(atkbd->dev, EV_MSC, MSC_SCAN, code);
	keycode = atkbd->keycode[code];

	switch (atkbd->keycode[code]) {
	if (keycode != ATKBD_KEY_NULL)
		input_event(dev, EV_MSC, MSC_SCAN, 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",
			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",
			printk(KERN_WARNING
			       "atkbd.c: Use 'setkeycodes %s%02x <keycode>' to make it known.\n",
			       code & 0x80 ? "e0" : "", code & 0x7f);
			}
			input_sync(atkbd->dev);
			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:
			if (atkbd->release) {
				value = 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)
			} 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;
					break;
				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_report_key(dev, 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;
@@ -764,6 +815,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;
}

/*