summaryrefslogtreecommitdiff
path: root/payloads/libpayload/drivers/i8042/mouse.c
diff options
context:
space:
mode:
Diffstat (limited to 'payloads/libpayload/drivers/i8042/mouse.c')
-rw-r--r--payloads/libpayload/drivers/i8042/mouse.c292
1 files changed, 292 insertions, 0 deletions
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 <siro@das-labor.org>
+ *
+ * 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 <libpayload-config.h>
+#include <libpayload.h>
+
+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();
+}