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