Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3d0f0fa0 authored by Dmitry Torokhov's avatar Dmitry Torokhov
Browse files

Input: atkbd - restore repeat rate when resuming



Make the AT keyboard driver restore previously set repeat rate
when resuming. Noticed by Linus Torvalds.

Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent 184dd275
Loading
Loading
Loading
Loading
+60 −43
Original line number Diff line number Diff line
@@ -482,13 +482,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
	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,
@@ -496,18 +490,32 @@ 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[2];
	int i, j;
	unsigned char param;
	int i = 0, j = 0;

	mutex_lock(&atkbd->event_mutex);
	while (i < ARRAY_SIZE(period) - 1 && period[i] < dev->rep[REP_PERIOD])
		i++;
	dev->rep[REP_PERIOD] = period[i];

	while (j < ARRAY_SIZE(period) - 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];

	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 (ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_SETLEDS))
		return -1;

	if (atkbd->extra) {
		param[0] = 0;
@@ -516,22 +524,31 @@ static void atkbd_event_work(void *data)
			 | (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 (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);
}

@@ -975,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");
@@ -985,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))
@@ -996,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);