xref: /aosp_15_r20/external/coreboot/payloads/libpayload/drivers/i8042/mouse.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /*
2  *
3  * Copyright (C) 2017 Patrick Rudolph <[email protected]>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <libpayload-config.h>
30 #include <libpayload.h>
31 
32 static int x_axis;
33 static int y_axis;
34 static int z_axis;
35 static u32 buttons;
36 static u8 is_intellimouse;
37 static u8 is_explorer_intellimouse;
38 static u8 initialized;
39 static unsigned char mouse_buf[4];
40 static unsigned char mouse_buf_idx;
41 
mouse_cmd(unsigned char cmd)42 static u8 mouse_cmd(unsigned char cmd)
43 {
44 	i8042_cmd(0xd4);
45 
46 	i8042_write_data(cmd);
47 
48 	return i8042_wait_read_aux() == 0xfa;
49 }
50 
mouse_cmd_data(u8 cmd,u8 val)51 static u8 mouse_cmd_data(u8 cmd, u8 val)
52 {
53 	if (!mouse_cmd(cmd))
54 		return 0;
55 
56 	return mouse_cmd(val);
57 }
58 
59 /** Try to detect Microsoft Intelli mouse */
mouse_is_intellimouse(void)60 static u8 mouse_is_intellimouse(void)
61 {
62 	/* Silence mouse. */
63 	if (!mouse_cmd(0xf5))
64 		return 0;
65 
66 	/* Set standard. */
67 	if (!mouse_cmd(0xf6))
68 		return 0;
69 
70 	/* Magic sequence. */
71 	if (!mouse_cmd_data(0xf3, 0xc8))
72 		return 0;
73 	if (!mouse_cmd_data(0xf3, 0x64))
74 		return 0;
75 	if (!mouse_cmd_data(0xf3, 0x50))
76 		return 0;
77 
78 	/* Get mouse id */
79 	if (!mouse_cmd(0xf2))
80 		return 0;
81 
82 	if (i8042_wait_read_aux() != 0x03)
83 		return 0;
84 
85 	return 1;
86 }
87 
88 /** Try to detect Microsoft Explorer mouse */
mouse_is_intellimouse_explorer(void)89 static u8 mouse_is_intellimouse_explorer(void)
90 {
91 	/* Silence mouse. */
92 	if (!mouse_cmd(0xf5))
93 		return 0;
94 
95 	/* Set standard. */
96 	if (!mouse_cmd(0xf6))
97 		return 0;
98 
99 	/* Magic sequence. */
100 	if (!mouse_cmd_data(0xf3, 0xc8))
101 		return 0;
102 	if (!mouse_cmd_data(0xf3, 0xc8))
103 		return 0;
104 	if (!mouse_cmd_data(0xf3, 0x50))
105 		return 0;
106 
107 	/* Get mouse id */
108 	if (!mouse_cmd(0xf2))
109 		return 0;
110 
111 	if (i8042_wait_read_aux() != 4)
112 		return 0;
113 
114 	return 1;
115 }
116 
117 /** Decode temporary buffer
118  * Sanity check to prevent out of order decode.
119  * Decode PS/2 data.
120  * Supported devices:
121  *  Generic 3 button mouse
122  *  Microsoft Intelli mouse
123  *  Microsoft Explorer mouse
124  */
mouse_decode(void)125 static void mouse_decode(void)
126 {
127 	/* Buffer full check and sanity check */
128 	if (is_intellimouse) {
129 		if (mouse_buf_idx < 4)
130 			return;
131 		if ((mouse_buf[3] & 0x10) != (mouse_buf[3] & 0x08)) {
132 			mouse_buf_idx = 0;
133 			return;
134 		}
135 	} else if (is_explorer_intellimouse) {
136 		if (mouse_buf_idx < 4)
137 			return;
138 		if (mouse_buf[3] & 0xc0) {
139 			mouse_buf_idx = 0;
140 			return;
141 		}
142 	} else {
143 		if (mouse_buf_idx < 3)
144 			return;
145 	}
146 
147 	/* Common protocol */
148 	x_axis += mouse_buf[1] ? mouse_buf[1] - ((mouse_buf[0] << 4) & 0x100) : 0;
149 	y_axis += mouse_buf[2] ? ((mouse_buf[0] << 3) & 0x100) - mouse_buf[2] : 0;
150 	buttons = mouse_buf[0] & 0x7;
151 
152 	/* Extended protocol */
153 	if (is_intellimouse) {
154 		z_axis += (mouse_buf[3] & 0x7) - (mouse_buf[3] & 0x08) ? 8 : 0;
155 	} else if (is_explorer_intellimouse) {
156 		z_axis += (mouse_buf[3] & 0x7) - (mouse_buf[3] & 0x08) ? 8 : 0;
157 		buttons = (mouse_buf[0] & 0x7) | (mouse_buf[3] & 0x30) >> 1;
158 	}
159 
160 	mouse_buf_idx = 0;
161 }
162 
163 /** Insert data into internal temporary buffer. */
insert_buf(unsigned char c)164 static void insert_buf(unsigned char c)
165 {
166 	/* Validate input:
167 	 * First byte shall have bit 3 set ! */
168 	if (!mouse_buf_idx && !(c & 8))
169 		return;
170 
171 	mouse_buf[mouse_buf_idx++] = c;
172 }
173 
174 /** Probe i8042 for new aux data and try to decode it. */
mouse_sample(void)175 static void mouse_sample(void)
176 {
177 	if (!initialized)
178 		return;
179 
180 	while (i8042_data_ready_aux()) {
181 		insert_buf(i8042_read_data_aux());
182 		mouse_decode();
183 	}
184 }
185 
186 /** Mouse cursor interface method
187  * Return and reset internal state.
188  */
mouse_state(int * x,int * y,int * z,u32 * b)189 static void mouse_state(int *x, int *y, int *z, u32 *b)
190 {
191 	if (!initialized)
192 		return;
193 
194 	mouse_sample();
195 
196 	if (x) {
197 		*x = x_axis;
198 		x_axis = 0;
199 	}
200 	if (y) {
201 		*y = y_axis;
202 		y_axis = 0;
203 	}
204 	if (z) {
205 		*z = z_axis;
206 		z_axis = 0;
207 	}
208 	if (b)
209 		*b = buttons;
210 }
211 
212 static struct mouse_cursor_input_driver curs = {
213 	.get_state = mouse_state,
214 	.input_type = CURSOR_INPUT_TYPE_PS2,
215 };
216 
217 /** Probe for PS/2 mouse */
i8042_mouse_init(void)218 void i8042_mouse_init(void)
219 {
220 	int ret;
221 
222 	/**
223 	 * Initialize keyboard controller.
224 	 * Might fail in case no AUX port or firmware disabled the AUX port.
225 	 */
226 	if (!i8042_probe() || !i8042_has_aux())
227 		return;
228 
229 	/* Empty mouse buffer. */
230 	while (i8042_data_ready_aux())
231 		i8042_read_data_aux();
232 
233 	/* Enable mouse.
234 	 * Documentation is unclear at this point.
235 	 * Some recommend to wait for response, some claim there's none.
236 	 * No response on Lenovo H8 EC.
237 	 * Ignore it ... */
238 	ret = i8042_cmd(0xa8);
239 	if (ret == -1)
240 		return;
241 
242 	/* Silence mouse. */
243 	if (!mouse_cmd(0xf5))
244 		return;
245 
246 	/* Read mouse id. */
247 	if (!mouse_cmd(0xf2))
248 		return;
249 
250 	ret = i8042_wait_read_aux();
251 	if (ret)
252 		return;
253 
254 	/* Get and enable features (scroll wheel and 5 buttons) */
255 	is_intellimouse = mouse_is_intellimouse();
256 	is_explorer_intellimouse = mouse_is_intellimouse_explorer();
257 
258 	/* Set defaults. */
259 	if (!mouse_cmd(0xf6))
260 		return;
261 
262 	/* Enable data transmission. */
263 	if (!mouse_cmd(0xf4))
264 		return;
265 
266 	initialized = 1;
267 
268 	/* Register mouse cursor driver */
269 	mouse_cursor_add_input_driver(&curs);
270 }
271 
272 /* Disable PS/2 mouse. */
i8042_mouse_disconnect(void)273 void i8042_mouse_disconnect(void)
274 {
275 	/* If 0x64 returns 0xff, then we have no keyboard
276 	 * controller */
277 	if (inb(0x64) == 0xFF || !initialized)
278 		return;
279 
280 	/* Empty keyboard buffer */
281 	while (i8042_data_ready_aux())
282 		i8042_read_data_aux();
283 
284 	/* Disable mouse. */
285 	i8042_cmd(0xa7);
286 
287 	initialized = 0;
288 
289 	/* Release keyboard controller driver */
290 	i8042_close();
291 }
292