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 <siro@das-labor.org> Reviewed-on: https://review.coreboot.org/18597 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Patrick Georgi <pgeorgi@google.com> Reviewed-by: Philipp Deppenwiese <zaolin.daisuki@gmail.com>
		
			
				
	
	
		
			293 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			293 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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();
 | 
						|
}
 |