Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Input: atkbd - fix multi-byte scancode handling on reconnect

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>

authored by

Shawn Nematbakhsh and committed by
Dmitry Torokhov
be2d7e42 4a31ba37

+51 -21
+51 -21
drivers/input/keyboard/atkbd.c
··· 676 676 serio_continue_rx(atkbd->ps2dev.serio); 677 677 } 678 678 679 + static int atkbd_activate(struct atkbd *atkbd) 680 + { 681 + struct ps2dev *ps2dev = &atkbd->ps2dev; 682 + 683 + /* 684 + * Enable the keyboard to receive keystrokes. 685 + */ 686 + 687 + if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) { 688 + dev_err(&ps2dev->serio->dev, 689 + "Failed to enable keyboard on %s\n", 690 + ps2dev->serio->phys); 691 + return -1; 692 + } 693 + 694 + return 0; 695 + } 696 + 697 + /* 698 + * atkbd_deactivate() resets and disables the keyboard from sending 699 + * keystrokes. 700 + */ 701 + 702 + static void atkbd_deactivate(struct atkbd *atkbd) 703 + { 704 + struct ps2dev *ps2dev = &atkbd->ps2dev; 705 + 706 + if (ps2_command(ps2dev, NULL, ATKBD_CMD_RESET_DIS)) 707 + dev_err(&ps2dev->serio->dev, 708 + "Failed to deactivate keyboard on %s\n", 709 + ps2dev->serio->phys); 710 + } 711 + 679 712 /* 680 713 * atkbd_probe() probes for an AT keyboard on a serio port. 681 714 */ ··· 763 730 "Use i8042.direct=1 to disable translation.\n"); 764 731 return -1; 765 732 } 733 + 734 + /* 735 + * Make sure nothing is coming from the keyboard and disturbs our 736 + * internal state. 737 + */ 738 + atkbd_deactivate(atkbd); 766 739 767 740 return 0; 768 741 } ··· 860 821 param[0] = 0; 861 822 if (ps2_command(ps2dev, param, ATKBD_CMD_SETREP)) 862 823 return -1; 863 - 864 - return 0; 865 - } 866 - 867 - static int atkbd_activate(struct atkbd *atkbd) 868 - { 869 - struct ps2dev *ps2dev = &atkbd->ps2dev; 870 - 871 - /* 872 - * Enable the keyboard to receive keystrokes. 873 - */ 874 - 875 - if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) { 876 - dev_err(&ps2dev->serio->dev, 877 - "Failed to enable keyboard on %s\n", 878 - ps2dev->serio->phys); 879 - return -1; 880 - } 881 824 882 825 return 0; 883 826 } ··· 1171 1150 1172 1151 atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra); 1173 1152 atkbd_reset_state(atkbd); 1174 - atkbd_activate(atkbd); 1175 1153 1176 1154 } else { 1177 1155 atkbd->set = 2; ··· 1185 1165 goto fail3; 1186 1166 1187 1167 atkbd_enable(atkbd); 1168 + if (serio->write) 1169 + atkbd_activate(atkbd); 1188 1170 1189 1171 err = input_register_device(atkbd->dev); 1190 1172 if (err) ··· 1230 1208 if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra)) 1231 1209 goto out; 1232 1210 1233 - atkbd_activate(atkbd); 1234 - 1235 1211 /* 1236 1212 * Restore LED state and repeat rate. While input core 1237 1213 * will do this for us at resume time reconnect may happen ··· 1243 1223 1244 1224 } 1245 1225 1226 + /* 1227 + * Reset our state machine in case reconnect happened in the middle 1228 + * of multi-byte scancode. 1229 + */ 1230 + atkbd->xl_bit = 0; 1231 + atkbd->emul = 0; 1232 + 1246 1233 atkbd_enable(atkbd); 1234 + if (atkbd->write) 1235 + atkbd_activate(atkbd); 1236 + 1247 1237 retval = 0; 1248 1238 1249 1239 out: