Skip to content

Commit

Permalink
Input: atkbd - fix multi-byte scancode handling on reconnect
Browse files Browse the repository at this point in the history
On resume from suspend there is a possibility for multi-byte scancodes
to be handled incorrectly. atkbd_reconnect disables the processing of
scancodes in software by calling atkbd_disable, but the keyboard may
still be active because no disconnect command was sent. Later, software
handling is re-enabled. If a multi-byte scancode sent from the keyboard
straddles the re-enable, only the latter byte(s) will be handled.

In practice, this leads to cases where multi-byte break codes (ex. "e0
4d" - break code for right-arrow) are misread as make codes ("4d" - make
code for numeric 6), leading to one or more unwanted, untyped characters
being interpreted.

The solution implemented here involves sending command f5 (reset
disable) to the keyboard prior to disabling software handling of codes.
Later, the command to re-enable the keyboard is sent only after we are
prepared to handle scancodes.

Signed-off-by: Shawn Nematbakhsh <shawnn@chromium.org>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
  • Loading branch information
Shawn Nematbakhsh authored and Dmitry Torokhov committed Jan 22, 2013
1 parent 4a31ba3 commit be2d7e4
Showing 1 changed file with 51 additions and 21 deletions.
72 changes: 51 additions & 21 deletions drivers/input/keyboard/atkbd.c
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,39 @@ static inline void atkbd_disable(struct atkbd *atkbd)
serio_continue_rx(atkbd->ps2dev.serio);
}

static int atkbd_activate(struct atkbd *atkbd)
{
struct ps2dev *ps2dev = &atkbd->ps2dev;

/*
* Enable the keyboard to receive keystrokes.
*/

if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) {
dev_err(&ps2dev->serio->dev,
"Failed to enable keyboard on %s\n",
ps2dev->serio->phys);
return -1;
}

return 0;
}

/*
* atkbd_deactivate() resets and disables the keyboard from sending
* keystrokes.
*/

static void atkbd_deactivate(struct atkbd *atkbd)
{
struct ps2dev *ps2dev = &atkbd->ps2dev;

if (ps2_command(ps2dev, NULL, ATKBD_CMD_RESET_DIS))
dev_err(&ps2dev->serio->dev,
"Failed to deactivate keyboard on %s\n",
ps2dev->serio->phys);
}

/*
* atkbd_probe() probes for an AT keyboard on a serio port.
*/
Expand Down Expand Up @@ -731,6 +764,12 @@ static int atkbd_probe(struct atkbd *atkbd)
return -1;
}

/*
* Make sure nothing is coming from the keyboard and disturbs our
* internal state.
*/
atkbd_deactivate(atkbd);

return 0;
}

Expand Down Expand Up @@ -825,24 +864,6 @@ static int atkbd_reset_state(struct atkbd *atkbd)
return 0;
}

static int atkbd_activate(struct atkbd *atkbd)
{
struct ps2dev *ps2dev = &atkbd->ps2dev;

/*
* Enable the keyboard to receive keystrokes.
*/

if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) {
dev_err(&ps2dev->serio->dev,
"Failed to enable keyboard on %s\n",
ps2dev->serio->phys);
return -1;
}

return 0;
}

/*
* atkbd_cleanup() restores the keyboard state so that BIOS is happy after a
* reboot.
Expand Down Expand Up @@ -1150,7 +1171,6 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)

atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra);
atkbd_reset_state(atkbd);
atkbd_activate(atkbd);

} else {
atkbd->set = 2;
Expand All @@ -1165,6 +1185,8 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
goto fail3;

atkbd_enable(atkbd);
if (serio->write)
atkbd_activate(atkbd);

err = input_register_device(atkbd->dev);
if (err)
Expand Down Expand Up @@ -1208,8 +1230,6 @@ static int atkbd_reconnect(struct serio *serio)
if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra))
goto out;

atkbd_activate(atkbd);

/*
* Restore LED state and repeat rate. While input core
* will do this for us at resume time reconnect may happen
Expand All @@ -1223,7 +1243,17 @@ static int atkbd_reconnect(struct serio *serio)

}

/*
* Reset our state machine in case reconnect happened in the middle
* of multi-byte scancode.
*/
atkbd->xl_bit = 0;
atkbd->emul = 0;

atkbd_enable(atkbd);
if (atkbd->write)
atkbd_activate(atkbd);

retval = 0;

out:
Expand Down

0 comments on commit be2d7e4

Please sign in to comment.