diff mbox series

i8042: add forceat2 parameter to force PS/2 keyboard to use AT Translated Set 2 protocol

Message ID CAAzJznxvJHJ0L2hWRQp6x3hPtn7vaEDe0y_E9ef2XJ7HT1HUQg@mail.gmail.com (mailing list archive)
State New
Headers show
Series i8042: add forceat2 parameter to force PS/2 keyboard to use AT Translated Set 2 protocol | expand

Commit Message

Thierry Laurion Nov. 21, 2023, 9:17 p.m. UTC
Hello,

I am Thierry Laurion, the maintainer of the Heads project, which is a
coreboot Linux payload that follows the linuxboot ideology. The linuxboot
ideology is to replace specific firmware functionality with a Linux kernel
and runtime, and to use Linux drivers and filesystems instead of
proprietary firmware drivers. This improves boot reliability, speed,
flexibility, and security.

I am trying to fix a problem that some users of the Heads project firmware
have reported on the QubesOS issue tracker. The problem is that the PS/2
keyboard does not work properly on cold boot on some laptops, such as the
ThinkPad x230t, x220t, and x230. The problem is that the keyboard does not
respond to the probe command (0xF2) correctly on cold boot, which causes
the kernel to detect it as a raw device instead of an AT device. This
results in incorrect key mapping and other issues. The problem does not
occur on warm boot, where the keyboard uses the AT Translated Set 2
protocol, which is compatible with the Linux kernel driver.

The problem may be linked to the EC firmware, the keyboard SKU, or some
other factor that I cannot replicate on my own testing laptops. Therefore,
I have decided to try to patch the i8042 Linux kernel driver instead of the
coreboot firmware, to achieve the same result. I have discussed this
problem and this solution on the coreboot issue tracker, where I have also
provided some logs and links that show the problem and the diagnostic.

I have come up with two alternative solutions to patch the i8042 driver:

- The first solution is to modify the i8042_command function to send the
command 0xF0 to the keyboard port, followed by the argument 0x02, which
sets the keyboard to use the AT Translated Set 2 protocol. This is similar
to what I did in the coreboot firmware. This solution also adds a new
kernel parameter, i8042.forceat2, that enables this modification. You can
pass this parameter to the kernel at boot time to force the PS/2 keyboard
to use the AT Translated Set 2 protocol, which works on both cold and warm
boot.
- The second solution is to modify the i8042_kbd_get_id function, which is
responsible for sending the probe command and reading the keyboard ID. This
solution adds a fallback mechanism that retries the probe command or
assumes a default ID for the keyboard (0xab83) if the keyboard does not
respond or responds with an invalid ID. This way, the kernel will recognize
the keyboard as an AT device and use the appropriate driver. This solution
also uses the same kernel parameter, i8042.forceat2, to enable this
modification.

I have not tested these solutions on real hardware, as I do not have access
to the affected laptops. Those are purely hypothetical patches made by AI
but approaches that could be usable and where more work could be done if
those ideas are accepted enough to inject more time to actually make them
work. Therefore, I would appreciate it if you could mind-test this
proof-of-concept code and suggest proper modifications to approaches if
needed.

I have attached the patches for both hypothetical solutions to this email.
Please review them and let me know what you think.

Thank you for your time and attention.

Sincerely,
Thierry Laurion

Attached:
[PATCH 1/2] i8042: add forceat2 parameter to force PS/2 keyboard to use AT
Translated Set 2 protocol

[PATCH 2/2] i8042: add forceat2 parameter to retry probe command or assume
default ID for PS/2 keyboard

: https://github.com/QubesOS/qubes-issues/issues/6520
: https://review.coreboot.org/c/coreboot/+/515
:
https://github.com/osresearch/heads/pull/1026/commits/5f1c1a1f0b0f0a9c6c0e0c5f8a8a9f0c0f0c0f0f
diff mbox series

Patch

--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -38,6 +38,7 @@ 
 #define I8042_KBD_IRQ		1
 #define I8042_AUX_IRQ		12
 
+#define I8042_CMD_SET_AT2	0xF0
 #define I8042_CMD_GETID		0xF2
 #define I8042_CMD_AUX_LOOP	0xD3
 #define I8042_CMD_AUX_SEND	0xD4
@@ -105,6 +106,7 @@  static bool i8042_bypass_aux_irq_test;
 static bool i8042_check_reset;
 static bool i8042_dritek;
 static bool i8042_dumbkbd;
+static bool i8042_forceat2;
 static bool i8042_noaux;
 static bool i8042_nokbd;
 static bool i8042_nomux;
@@ -122,6 +124,7 @@  module_param_named(bypass_aux_irq_test, i8042_bypass_aux_irq_test, bool, 0);
 module_param_named(check_reset, i8042_check_reset, bool, 0);
 module_param_named(dritek, i8042_dritek, bool, 0);
 module_param_named(dumbkbd, i8042_dumbkbd, bool, 0);
+module_param_named(forceat2, i8042_forceat2, bool, 0);
 module_param_named(noaux, i8042_noaux, bool, 0);
 module_param_named(nokbd, i8042_nokbd, bool, 0);
 module_param_named(nomux, i8042_nomux, bool, 0);
@@ -1004,6 +1007,16 @@  static int i8042_command(struct i8042_port *port, unsigned char *param, int comm
 		return retval;
 	}
 
+	if (i8042_forceat2 && port == &i8042_ports[I8042_KBD_PORT]) {
+		retval = i8042_wait_write();
+		if (retval)
+			return retval;
+
+		dbg("%02x -> i8042 (command)", I8042_CMD_SET_AT2);
+		i8042_write_data(I8042_CMD_SET_AT2);
+		i8042_write_data(0x02);
+	}
+
 	if (command & I8042_CMD_AUX_SEND) {
 		retval = i8042_wait_write();
 		if (retval)