From 5afc2936b83c744e0a0d3ab92e7a98b2aa163df4 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Mon, 6 Feb 2017 15:26:58 +0100 Subject: [PATCH] libpayload-x86: Add PS2 mouse driver Make use of i8042 driver to add PS2 mouse driver support. Tested on Lenovot T500. The touchpad can be used to drive the mouse cursor. Change-Id: I4be9c74467596b94d64dfa510824d8722108fe9c Signed-off-by: Patrick Rudolph Reviewed-on: https://review.coreboot.org/18597 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi Reviewed-by: Philipp Deppenwiese --- payloads/libpayload/Kconfig | 10 +- payloads/libpayload/drivers/Makefile.inc | 1 + payloads/libpayload/drivers/i8042/mouse.c | 292 +++++++++++++++++++++ payloads/libpayload/drivers/mouse_cursor.c | 4 +- payloads/libpayload/include/libpayload.h | 9 + 5 files changed, 314 insertions(+), 2 deletions(-) create mode 100644 payloads/libpayload/drivers/i8042/mouse.c diff --git a/payloads/libpayload/Kconfig b/payloads/libpayload/Kconfig index d4d76bbdeb..e482e93b94 100644 --- a/payloads/libpayload/Kconfig +++ b/payloads/libpayload/Kconfig @@ -331,11 +331,18 @@ config FONT_SCALE_FACTOR config PC_I8042 bool "A common PC i8042 driver" - default y if PC_KEYBOARD + default y if PC_KEYBOARD || PC_MOUSE default n help To be used by PC_KEYBOARD and PC_MOUSE. +config PC_MOUSE + bool "Allow input from a PC mouse" + default y if ARCH_X86 # uses IO + default n + help + PS/2 mouse driver on top of PC_I8042. + config PC_KEYBOARD bool "Allow input from a PC keyboard" default y if ARCH_X86 # uses IO @@ -367,6 +374,7 @@ config NVRAM config MOUSE_CURSOR bool "Support for mouse cursor handling" + default y if PC_MOUSE default n help Provides a common interface for various mouse cursor drivers. diff --git a/payloads/libpayload/drivers/Makefile.inc b/payloads/libpayload/drivers/Makefile.inc index 6eb9f0c4fc..2d70856f0a 100644 --- a/payloads/libpayload/drivers/Makefile.inc +++ b/payloads/libpayload/drivers/Makefile.inc @@ -39,6 +39,7 @@ libc-$(CONFIG_LP_IPQ806X_SERIAL_CONSOLE) += serial/ipq806x.c serial/serial.c libc-$(CONFIG_LP_IPQ40XX_SERIAL_CONSOLE) += serial/ipq40xx.c serial/serial.c libc-$(CONFIG_LP_BG4CD_SERIAL_CONSOLE) += serial/bg4cd.c serial/serial.c libc-$(CONFIG_LP_PC_KEYBOARD) += i8042/keyboard.c +libc-$(CONFIG_LP_PC_MOUSE) += i8042/mouse.c libc-$(CONFIG_LP_PC_I8042) += i8042/i8042.c libc-$(CONFIG_LP_CBMEM_CONSOLE) += cbmem_console.c diff --git a/payloads/libpayload/drivers/i8042/mouse.c b/payloads/libpayload/drivers/i8042/mouse.c new file mode 100644 index 0000000000..21096d18c6 --- /dev/null +++ b/payloads/libpayload/drivers/i8042/mouse.c @@ -0,0 +1,292 @@ +/* + * This file is part of the libpayload project. + * + * Copyright (C) 2017 Patrick Rudolph + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +static int x_axis; +static int y_axis; +static int z_axis; +static u32 buttons; +static u8 is_intellimouse; +static u8 is_explorer_intellimouse; +static u8 initialized; +static unsigned char mouse_buf[4]; +static unsigned char mouse_buf_idx; + +static u8 mouse_cmd(unsigned char cmd) +{ + i8042_cmd(0xd4); + + i8042_write_data(cmd); + + return i8042_wait_read_aux() == 0xfa; +} + +static u8 mouse_cmd_data(u8 cmd, u8 val) +{ + if (!mouse_cmd(cmd)) + return 0; + + return mouse_cmd(val); +} + +/** Try to detect Microsoft Intelli mouse */ +static u8 mouse_is_intellimouse(void) +{ + /* Silence mouse. */ + if (!mouse_cmd(0xf5)) + return 0; + + /* Set standard. */ + if (!mouse_cmd(0xf6)) + return 0; + + /* Magic sequence. */ + if (!mouse_cmd_data(0xf3, 0xc8)) + return 0; + if (!mouse_cmd_data(0xf3, 0x64)) + return 0; + if (!mouse_cmd_data(0xf3, 0x50)) + return 0; + + /* Get mouse id */ + if (!mouse_cmd(0xf2)) + return 0; + + if (i8042_wait_read_aux() != 0x03) + return 0; + + return 1; +} + +/** Try to detect Microsoft Explorer mouse */ +static u8 mouse_is_intellimouse_explorer(void) +{ + /* Silence mouse. */ + if (!mouse_cmd(0xf5)) + return 0; + + /* Set standard. */ + if (!mouse_cmd(0xf6)) + return 0; + + /* Magic sequence. */ + if (!mouse_cmd_data(0xf3, 0xc8)) + return 0; + if (!mouse_cmd_data(0xf3, 0xc8)) + return 0; + if (!mouse_cmd_data(0xf3, 0x50)) + return 0; + + /* Get mouse id */ + if (!mouse_cmd(0xf2)) + return 0; + + if (i8042_wait_read_aux() != 4) + return 0; + + return 1; +} + +/** Decode temporary buffer + * Sanity check to prevent out of order decode. + * Decode PS/2 data. + * Supported devices: + * Generic 3 button mouse + * Microsoft Intelli mouse + * Microsoft Explorer mouse + */ +static void mouse_decode(void) +{ + /* Buffer full check and sanity check */ + if (is_intellimouse) { + if (mouse_buf_idx < 4) + return; + if ((mouse_buf[3] & 0x10) != (mouse_buf[3] & 0x08)) { + mouse_buf_idx = 0; + return; + } + } else if (is_explorer_intellimouse) { + if (mouse_buf_idx < 4) + return; + if (mouse_buf[3] & 0xc0) { + mouse_buf_idx = 0; + return; + } + } else { + if (mouse_buf_idx < 3) + return; + } + + /* Common protocol */ + x_axis += mouse_buf[1] ? mouse_buf[1] - ((mouse_buf[0] << 4) & 0x100) : 0; + y_axis += mouse_buf[2] ? ((mouse_buf[0] << 3) & 0x100) - mouse_buf[2] : 0; + buttons = mouse_buf[0] & 0x7; + + /* Extended protocol */ + if (is_intellimouse) { + z_axis += (mouse_buf[3] & 0x7) - (mouse_buf[3] & 0x08) ? 8 : 0; + } else if (is_explorer_intellimouse) { + z_axis += (mouse_buf[3] & 0x7) - (mouse_buf[3] & 0x08) ? 8 : 0; + buttons = (mouse_buf[0] & 0x7) | (mouse_buf[3] & 0x30) >> 1; + } + + mouse_buf_idx = 0; +} + +/** Insert data into internal temporary buffer. */ +static void insert_buf(unsigned char c) +{ + /* Validate input: + * First byte shall have bit 3 set ! */ + if (!mouse_buf_idx && !(c & 8)) + return; + + mouse_buf[mouse_buf_idx++] = c; +} + +/** Probe i8042 for new aux data and try to decode it. */ +static void mouse_sample(void) +{ + if (!initialized) + return; + + while (i8042_data_ready_aux()) { + insert_buf(i8042_read_data_aux()); + mouse_decode(); + } +} + +/** Mouse cursor interface method + * Return and reset internal state. + */ +static void mouse_state(int *x, int *y, int *z, u32 *b) +{ + if (!initialized) + return; + + mouse_sample(); + + if (x) { + *x = x_axis; + x_axis = 0; + } + if (y) { + *y = y_axis; + y_axis = 0; + } + if (z) { + *z = z_axis; + z_axis = 0; + } + if (b) + *b = buttons; +} + +static struct mouse_cursor_input_driver curs = { + .get_state = mouse_state, + .input_type = CURSOR_INPUT_TYPE_PS2, +}; + +/** Probe for PS/2 mouse */ +void i8042_mouse_init(void) +{ + int ret; + + /** + * Initialize keyboard controller. + * Might fail in case no AUX port or firmware disabled the AUX port. + */ + if (!i8042_probe() || !i8042_has_aux()) + return; + + /* Empty mouse buffer. */ + while (i8042_data_ready_aux()) + i8042_read_data_aux(); + + /* Enable mouse. + * Documentation is unclear at this point. + * Some recommend to wait for response, some claim there's none. + * No response on Lenovo H8 EC. + * Ignore it ... */ + ret = i8042_cmd(0xa8); + if (ret == -1) + return; + + /* Silence mouse. */ + if (!mouse_cmd(0xf5)) + return; + + /* Read mouse id. */ + if (!mouse_cmd(0xf2)) + return; + + ret = i8042_wait_read_aux(); + if (ret) + return; + + /* Get and enable features (scroll wheel and 5 buttons) */ + is_intellimouse = mouse_is_intellimouse(); + is_explorer_intellimouse = mouse_is_intellimouse_explorer(); + + /* Set defaults. */ + if (!mouse_cmd(0xf6)) + return; + + /* Enable data transmission. */ + if (!mouse_cmd(0xf4)) + return; + + initialized = 1; + + /* Register mouse cursor driver */ + mouse_cursor_add_input_driver(&curs); +} + +/* Disable PS/2 mouse. */ +void i8042_mouse_disconnect(void) +{ + /* If 0x64 returns 0xff, then we have no keyboard + * controller */ + if (inb(0x64) == 0xFF || !initialized) + return; + + /* Empty keyboard buffer */ + while (i8042_data_ready_aux()) + i8042_read_data_aux(); + + /* Disable mouse. */ + i8042_cmd(0xa7); + + initialized = 0; + + /* Release keyboard controller driver */ + i8042_close(); +} diff --git a/payloads/libpayload/drivers/mouse_cursor.c b/payloads/libpayload/drivers/mouse_cursor.c index 781845caaf..40c8934889 100644 --- a/payloads/libpayload/drivers/mouse_cursor.c +++ b/payloads/libpayload/drivers/mouse_cursor.c @@ -60,7 +60,9 @@ void mouse_cursor_add_input_driver(struct mouse_cursor_input_driver *const in) /** Init enabled mouse cursor drivers */ void mouse_cursor_init(void) { -/* FIXME */ +#if IS_ENABLED(CONFIG_LP_PC_MOUSE) + i8042_mouse_init(); +#endif } static u32 mouse_buttons; diff --git a/payloads/libpayload/include/libpayload.h b/payloads/libpayload/include/libpayload.h index 96cc5f8165..c6b9447dfb 100644 --- a/payloads/libpayload/include/libpayload.h +++ b/payloads/libpayload/include/libpayload.h @@ -203,6 +203,15 @@ int i8042_wait_read_aux(void); /** @} */ +/** + * @defgroup i8042 PS2 Mouse functions + * @ingroup input + * @{ + */ +void i8042_mouse_init(void); +void i8042_mouse_disconnect(void); +/** @} */ + /** * @defgroup serial Serial functions * @ingroup input