x86/ps2: disable devices during commands

Especially the initial reset ended up with an unusable keyboard in rare
cases before. This commit disables keyboard and mouse handling of i8042
during command sequences according to a note from

  https://www.win.tue.nl/~aeb/linux/kbd/scancodes-12.html
This commit is contained in:
Alexander Boettcher 2020-01-21 11:15:53 +01:00 committed by Christian Helmuth
parent a2b303e95a
commit a036d2373a
4 changed files with 88 additions and 58 deletions

View File

@ -382,17 +382,19 @@ class Ps2::Keyboard : public Input_driver
void _set_leds(bool capslock, bool numlock, bool scrlock)
{
_kbd.write(0xed);
if (_kbd.read() != ACK) {
Genode::warning("setting of mode indicators failed (0xed)");
return;
}
_kbd.apply_commands([&]() {
_kbd.write(0xed);
if (_kbd.read() != ACK) {
Genode::warning("setting of mode indicators failed (0xed)");
return;
}
_kbd.write((capslock ? 4:0) | (numlock ? 2:0) | (scrlock ? 1:0));
if (_kbd.read() != ACK) {
Genode::warning("setting of mode indicators failed");
return;
}
_kbd.write((capslock ? 4:0) | (numlock ? 2:0) | (scrlock ? 1:0));
if (_kbd.read() != ACK) {
Genode::warning("setting of mode indicators failed");
return;
}
});
}
void _update_leds()
@ -468,17 +470,19 @@ class Ps2::Keyboard : public Input_driver
if (_xlate_mode)
return;
/* try to enable scan-code set 2 */
_kbd.write(0xf0);
if (_kbd.read() != ACK) {
Genode::warning("scan code setting not supported");
return;
}
_kbd.write(SCAN_CODE_SET_2);
if (_kbd.read() != ACK) {
Genode::warning("scan code 2 not supported");
return;
}
_kbd.apply_commands([&]() {
/* try to enable scan-code set 2 */
_kbd.write(0xf0);
if (_kbd.read() != ACK) {
Genode::warning("scan code setting not supported");
return;
}
_kbd.write(SCAN_CODE_SET_2);
if (_kbd.read() != ACK) {
Genode::warning("scan code 2 not supported");
return;
}
});
/*
* If configuration of scan-code set 2 was successful, select

View File

@ -27,6 +27,24 @@ struct Serial_interface : Genode::Interface
* (Re-)enable device interrupt
*/
virtual void enable_irq() { }
protected:
virtual void _begin_commands() { }
virtual void _end_commands() { }
public:
/**
* Don't interfere with incoming events on command sequence
*/
template <typename FN>
void apply_commands(FN const &fn)
{
_begin_commands();
fn();
_end_commands();
}
};
#endif /* _SERIAL_INTERFACE_H_ */

View File

@ -156,6 +156,23 @@ class I8042
flush_read();
return !empty();
}
void _begin_commands() override
{
/* disable keyboard and mouse */
_i8042._command(CMD_KBD_DISABLE);
_i8042._command(CMD_AUX_DISABLE);
/* flush remaining data in controller */
while (_i8042._output_buffer_full()) _i8042._data();
}
void _end_commands() override
{
/* enable keyboard and mouse */
_i8042._command(CMD_KBD_ENABLE);
_i8042._command(CMD_AUX_ENABLE);
}
};
private:
@ -266,46 +283,37 @@ class I8042
unsigned char configuration;
unsigned char ret;
/* disable keyboard and mouse */
_command(CMD_KBD_DISABLE);
_command(CMD_AUX_DISABLE);
_kbd_interface.apply_commands([&]() {
/* get configuration (can change during the self tests) */
_command(CMD_READ);
configuration = _wait_data();
/* query xlate bit */
_kbd_xlate = !!(configuration & CTRL_XLATE);
/* read remaining data in controller */
while (_output_buffer_full()) _data();
/* run self tests */
_command(CMD_TEST);
if ((ret = _wait_data()) != RET_TEST_OK) {
Genode::log("i8042: self test failed (", Genode::Hex(ret), ")");
return;
}
/* get configuration (can change during the self tests) */
_command(CMD_READ);
configuration = _wait_data();
/* query xlate bit */
_kbd_xlate = !!(configuration & CTRL_XLATE);
_command(CMD_KBD_TEST);
if ((ret = _wait_data()) != RET_KBD_TEST_OK) {
Genode::log("i8042: kbd test failed (", Genode::Hex(ret), ")");
return;
}
/* run self tests */
_command(CMD_TEST);
if ((ret = _wait_data()) != RET_TEST_OK) {
Genode::log("i8042: self test failed (", Genode::Hex(ret), ")");
return;
}
_command(CMD_AUX_TEST);
if ((ret = _wait_data()) != RET_AUX_TEST_OK) {
Genode::log("I8042: aux test failed (", Genode::Hex(ret), ")");
return;
}
_command(CMD_KBD_TEST);
if ((ret = _wait_data()) != RET_KBD_TEST_OK) {
Genode::log("i8042: kbd test failed (", Genode::Hex(ret), ")");
return;
}
_command(CMD_AUX_TEST);
if ((ret = _wait_data()) != RET_AUX_TEST_OK) {
Genode::log("I8042: aux test failed (", Genode::Hex(ret), ")");
return;
}
/* enable interrupts for keyboard and mouse at the controller */
configuration |= CTRL_KBD_INT | CTRL_AUX_INT;
_command(CMD_WRITE);
_data(configuration);
/* enable keyboard and mouse */
_command(CMD_KBD_ENABLE);
_command(CMD_AUX_ENABLE);
/* enable interrupts for keyboard and mouse at the controller */
configuration |= CTRL_KBD_INT | CTRL_AUX_INT;
_command(CMD_WRITE);
_data(configuration);
});
}
/**

View File

@ -52,7 +52,7 @@ struct Ps2::Main
Platform::Device_client _device_ps2 { _ps2_device_cap() };
enum { REG_IOPORT_DATA = 0, REG_IOPORT_STATUS };
enum { REG_IOPORT_DATA = 0, REG_IOPORT_STATUS = 1 };
I8042 _i8042 { _device_ps2.io_port(REG_IOPORT_DATA),
_device_ps2.io_port(REG_IOPORT_STATUS) };