xref: /haiku/src/add-ons/kernel/bus_managers/ps2/ps2_dev.cpp (revision 7457ccb4b2f4786525d3b7bda42598487d57ab7d)
1 /*
2  * Copyright 2005-2014 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors (in chronological order):
6  *		Marcus Overhagen (marcus@overhagen.de)
7  *		Clemens Zeidler (haiku@clemens-zeidler.de)
8  */
9 
10 
11 /*!	PS/2 bus manager */
12 
13 #include "ps2_dev.h"
14 #include "ps2_service.h"
15 
16 #include "ps2_alps.h"
17 #include "ps2_elantech.h"
18 #include "ps2_standard_mouse.h"
19 #include "ps2_synaptics.h"
20 #include "ps2_trackpoint.h"
21 
22 #include <fs/devfs.h>
23 
24 #include <string.h>
25 
26 
27 ps2_dev ps2_device[PS2_DEVICE_COUNT];
28 
29 
30 status_t
31 ps2_reset_mouse(ps2_dev* dev)
32 {
33 	uint8 data[2];
34 	status_t status;
35 
36 	TRACE("ps2: ps2_reset_mouse\n");
37 
38 	status = ps2_dev_command(dev, PS2_CMD_RESET, NULL, 0, data, 2);
39 
40 	if (status == B_OK && data[0] == 0xFE && data[1] == 0xAA) {
41 		// workaround for HP/Compaq KBCs timeout condition. #2867 #3594 #4315
42 		TRACE("ps2: KBC has timed out the mouse reset request. "
43 			"Response was: 0x%02x 0x%02x. Requesting the answer data.\n",
44 			data[0], data[1]);
45 		status = ps2_dev_command(dev, PS2_CMD_RESEND, NULL, 0, data, 2);
46 	}
47 
48 	if (status == B_OK && data[0] != 0xAA && data[1] != 0x00) {
49 		TRACE("ps2: reset mouse failed, response was: 0x%02x 0x%02x\n",
50 			data[0], data[1]);
51 		status = B_ERROR;
52 	} else if (status != B_OK) {
53 		TRACE("ps2: reset mouse failed\n");
54 	} else {
55 		TRACE("ps2: reset mouse success\n");
56 	}
57 
58 	return status;
59 }
60 
61 
62 status_t
63 ps2_dev_detect_pointing(ps2_dev* dev, device_hooks** hooks)
64 {
65 	status_t status = ps2_reset_mouse(dev);
66 	if (status != B_OK) {
67 		INFO("ps2: reset failed\n");
68 		return B_ERROR;
69 	}
70 
71 	// probe devices
72 	// the probe function has to set the dev name and the dev packet size
73 
74 	status = probe_trackpoint(dev);
75 	if (status == B_OK) {
76 		*hooks = &gStandardMouseDeviceHooks;
77 		goto dev_found;
78 	}
79 
80 	status = probe_synaptics(dev);
81 	if (status == B_OK) {
82 		*hooks = &gSynapticsDeviceHooks;
83 		goto dev_found;
84 	}
85 
86 	status = probe_alps(dev);
87 	if (status == B_OK) {
88 		*hooks = &gALPSDeviceHooks;
89 		goto dev_found;
90 	}
91 
92 	status = probe_elantech(dev);
93 	if (status == B_OK) {
94 		*hooks = &gElantechDeviceHooks;
95 		goto dev_found;
96 	}
97 
98 	// reset the mouse for the case that the previous probes left the mouse in
99 	// a undefined state
100 	status = ps2_reset_mouse(dev);
101 	if (status != B_OK) {
102 		INFO("ps2: reset after probe failed\n");
103 		return B_ERROR;
104 	}
105 
106 	status = probe_standard_mouse(dev);
107 	if (status == B_OK) {
108 		*hooks = &gStandardMouseDeviceHooks;
109 		goto dev_found;
110 	}
111 
112 	return B_ERROR;
113 
114 dev_found:
115 	if (dev == &(ps2_device[PS2_DEVICE_SYN_PASSTHROUGH]))
116 		synaptics_pass_through_set_packet_size(dev, dev->packet_size);
117 
118 	return B_OK;
119 }
120 
121 
122 status_t
123 ps2_dev_init(void)
124 {
125 	ps2_device[0].name = "input/mouse/ps2/0";
126 	ps2_device[0].active = false;
127 	ps2_device[0].idx = 0;
128 	ps2_device[0].result_sem = -1;
129 	ps2_device[0].command = standard_command_timeout;
130 
131 	ps2_device[1].name = "input/mouse/ps2/1";
132 	ps2_device[1].active = false;
133 	ps2_device[1].idx = 1;
134 	ps2_device[1].result_sem = -1;
135 	ps2_device[1].command = standard_command_timeout;
136 
137 	ps2_device[2].name = "input/mouse/ps2/2";
138 	ps2_device[2].active = false;
139 	ps2_device[2].idx = 2;
140 	ps2_device[2].result_sem = -1;
141 	ps2_device[2].command = standard_command_timeout;
142 
143 	ps2_device[3].name = "input/mouse/ps2/3";
144 	ps2_device[3].active = false;
145 	ps2_device[3].idx = 3;
146 	ps2_device[3].result_sem = -1;
147 	ps2_device[3].command = standard_command_timeout;
148 
149 	ps2_device[4].name = "input/mouse/ps2/synaptics_passthrough";
150 	ps2_device[4].active = false;
151 	ps2_device[4].result_sem = -1;
152 	ps2_device[4].command = passthrough_command;
153 
154 	ps2_device[5].name = "input/keyboard/at/0";
155 	ps2_device[5].active = false;
156 	ps2_device[5].result_sem = -1;
157 	ps2_device[5].flags = PS2_FLAG_KEYB;
158 	ps2_device[5].command = standard_command_timeout;
159 
160 	int i;
161 	for (i = 0; i < PS2_DEVICE_COUNT; i++) {
162 		ps2_dev* dev = &ps2_device[i];
163 		dev->result_sem = create_sem(0, "ps2 result");
164 		if (dev->result_sem < 0)
165 			goto err;
166 	}
167 	return B_OK;
168 
169 err:
170 	ps2_dev_exit();
171 
172 	return B_ERROR;
173 }
174 
175 
176 void
177 ps2_dev_exit(void)
178 {
179 	for (int i = 0; i < PS2_DEVICE_COUNT; i++) {
180 		ps2_dev* dev = &ps2_device[i];
181 		if (dev->result_sem >= 0) {
182 			delete_sem(dev->result_sem);
183 			dev->result_sem = -1;
184 		}
185 	}
186 }
187 
188 
189 void
190 ps2_dev_publish(ps2_dev* dev)
191 {
192 	status_t status = B_OK;
193 	TRACE("ps2: ps2_dev_publish %s\n", dev->name);
194 
195 	if (dev->active)
196 		return;
197 
198 	if (atomic_get(&dev->flags) & PS2_FLAG_KEYB) {
199 		status = devfs_publish_device(dev->name, &gKeyboardDeviceHooks);
200 	} else {
201 		// Check if this is the "pass-through" device and wait until
202 		// the parent_dev goes to enabled state. It is required to prevent
203 		// from messing up the Synaptics command sequences in synaptics_open.
204 		if (dev->parent_dev) {
205 			const bigtime_t timeout = 2000000;
206 			bigtime_t start = system_time();
207 			while (!(atomic_get(&dev->parent_dev->flags) & PS2_FLAG_ENABLED)) {
208 				if ((system_time() - start) > timeout) {
209 					status = B_BUSY;
210 					break;
211 				}
212 				snooze(timeout / 20);
213 			}
214 			TRACE("ps2: publishing %s: parent %s is %s; wait time %" B_PRId64
215 				"\n", dev->name, dev->parent_dev->name,
216 				status == B_OK ? "enabled" : "busy", system_time() - start);
217 		}
218 
219 		if (status == B_OK) {
220 			device_hooks* hooks;
221 			status = ps2_dev_detect_pointing(dev, &hooks);
222 			if (status == B_OK) {
223 				status = devfs_publish_device(dev->name, hooks);
224 			}
225 		}
226 	}
227 
228 	dev->active = true;
229 
230 	INFO("ps2: devfs_publish_device %s, status = 0x%08" B_PRIx32 "\n",
231 		dev->name, status);
232 }
233 
234 
235 void
236 ps2_dev_unpublish(ps2_dev* dev)
237 {
238 	status_t status;
239 	TRACE("ps2: ps2_dev_unpublish %s\n", dev->name);
240 
241 	if (!dev->active)
242 		return;
243 
244 	dev->active = false;
245 
246 	status = devfs_unpublish_device(dev->name, true);
247 
248 	if ((dev->flags & PS2_FLAG_ENABLED) && dev->disconnect)
249 		dev->disconnect(dev);
250 
251 	INFO("ps2: devfs_unpublish_device %s, status = 0x%08" B_PRIx32 "\n",
252 		dev->name, status);
253 }
254 
255 
256 int32
257 ps2_dev_handle_int(ps2_dev* dev)
258 {
259 	const uint8 data = dev->history[0].data;
260 	uint32 flags;
261 
262 	flags = atomic_get(&dev->flags);
263 
264 	if (flags & PS2_FLAG_CMD) {
265 		if ((flags & (PS2_FLAG_ACK | PS2_FLAG_NACK)) == 0) {
266 			int cnt = 1;
267 			if (data == PS2_REPLY_ACK) {
268 				atomic_or(&dev->flags, PS2_FLAG_ACK);
269 			} else if (data == PS2_REPLY_RESEND || data == PS2_REPLY_ERROR) {
270 				atomic_or(&dev->flags, PS2_FLAG_NACK);
271 			} else if ((flags & PS2_FLAG_GETID)
272 				&& (data == 0 || data == 3 || data == 4)) {
273 				// workaround for broken mice that don't ack the "get id"
274 				// command
275 				TRACE("ps2: ps2_dev_handle_int: mouse didn't ack the 'get id' "
276 					"command\n");
277 				atomic_or(&dev->flags, PS2_FLAG_ACK);
278 				if (dev->result_buf_cnt) {
279 					dev->result_buf[dev->result_buf_idx] = data;
280 					dev->result_buf_idx++;
281 					dev->result_buf_cnt--;
282 					if (dev->result_buf_cnt == 0) {
283 						atomic_and(&dev->flags, ~PS2_FLAG_CMD);
284 						cnt++;
285 					}
286 				}
287 			} else if ((flags & PS2_FLAG_RESEND)) {
288 				TRACE("ps2: ps2_dev_handle_int: processing RESEND request\n");
289 				atomic_or(&dev->flags, PS2_FLAG_ACK);
290 				if (dev->result_buf_cnt) {
291 					dev->result_buf[dev->result_buf_idx] = data;
292 					dev->result_buf_idx++;
293 					dev->result_buf_cnt--;
294 					if (dev->result_buf_cnt == 0) {
295 						atomic_and(&dev->flags, ~PS2_FLAG_CMD);
296 						cnt++;
297 					}
298 				}
299 			} else {
300 //				TRACE("ps2: ps2_dev_handle_int unexpected data 0x%02x while "
301 //					"waiting for ack\n", data);
302 				TRACE("ps2: int1 %02x\n", data);
303 				goto pass_to_handler;
304 			}
305 			release_sem_etc(dev->result_sem, cnt, B_DO_NOT_RESCHEDULE);
306 			return B_INVOKE_SCHEDULER;
307 		} else if (dev->result_buf_cnt) {
308 			dev->result_buf[dev->result_buf_idx] = data;
309 			dev->result_buf_idx++;
310 			dev->result_buf_cnt--;
311 			if (dev->result_buf_cnt == 0) {
312 				atomic_and(&dev->flags, ~PS2_FLAG_CMD);
313 				release_sem_etc(dev->result_sem, 1, B_DO_NOT_RESCHEDULE);
314 				return B_INVOKE_SCHEDULER;
315 			}
316 		} else {
317 //			TRACE("ps2: ps2_dev_handle_int unexpected data 0x%02x during "
318 //				"command processing\n", data);
319 			TRACE("ps2: int2 %02x\n", data);
320 			goto pass_to_handler;
321 		}
322 		return B_HANDLED_INTERRUPT;
323 	}
324 
325 pass_to_handler:
326 
327 	if ((flags & PS2_FLAG_KEYB) == 0) {
328 		if (dev->history[0].error && data == 0xfd) {
329 			INFO("ps2: hot removal of %s\n", dev->name);
330 			ps2_service_notify_device_removed(dev);
331 			return B_INVOKE_SCHEDULER;
332 		}
333 		if (data == 0x00 && dev->history[1].data == 0xaa
334 			&& (dev->history[0].time - dev->history[1].time) < 50000) {
335 			INFO("ps2: hot plugin of %s\n", dev->name);
336 			if (dev->active) {
337 				INFO("ps2: device %s still active, republishing...\n",
338 					dev->name);
339 				ps2_service_notify_device_republish(dev);
340 			} else {
341 				ps2_service_notify_device_added(dev);
342 			}
343 			return B_INVOKE_SCHEDULER;
344 		}
345 	}
346 
347 	if (!dev->active) {
348 		TRACE("ps2: %s not active, data 0x%02x dropped\n", dev->name, data);
349 		if (data != 0x00 && data != 0xaa) {
350 			INFO("ps2: possibly a hot plugin of %s\n", dev->name);
351 			ps2_service_notify_device_added(dev);
352 			return B_INVOKE_SCHEDULER;
353 		}
354 		return B_HANDLED_INTERRUPT;
355 	}
356 
357 	if ((flags & PS2_FLAG_ENABLED) == 0) {
358 		TRACE("ps2: %s not enabled, data 0x%02x dropped\n", dev->name, data);
359 		return B_HANDLED_INTERRUPT;
360 	}
361 
362 	return dev->handle_int(dev);
363 }
364 
365 
366 status_t
367 standard_command_timeout(ps2_dev* dev, uint8 cmd, const uint8* out,
368 	int out_count, uint8* in, int in_count, bigtime_t timeout)
369 {
370 	status_t res;
371 	bigtime_t start;
372 	int32 sem_count;
373 	int i;
374 
375 	TRACE("ps2: ps2_dev_command cmd 0x%02x, out-count %d, in-count %d, "
376 		"dev %s\n", cmd, out_count, in_count, dev->name);
377 	for (i = 0; i < out_count; i++)
378 		TRACE("ps2: ps2_dev_command tx: 0x%02x\n", out[i]);
379 
380 	res = get_sem_count(dev->result_sem, &sem_count);
381 	if (res == B_OK && sem_count != 0) {
382 		TRACE("ps2: ps2_dev_command: sem_count %" B_PRId32 ", fixing!\n",
383 			sem_count);
384 		if (sem_count > 0)
385 			acquire_sem_etc(dev->result_sem, sem_count, 0, 0);
386 		else
387 			release_sem_etc(dev->result_sem, -sem_count, 0);
388 	}
389 
390 	dev->result_buf_cnt = in_count;
391 	dev->result_buf_idx = 0;
392 	dev->result_buf = in;
393 
394 	res = B_OK;
395 	for (i = -1; res == B_OK && i < out_count; i++) {
396 
397 		atomic_and(&dev->flags,
398 			~(PS2_FLAG_ACK | PS2_FLAG_NACK | PS2_FLAG_GETID | PS2_FLAG_RESEND));
399 
400 		acquire_sem(gControllerSem);
401 
402 		if (!(atomic_get(&dev->flags) & PS2_FLAG_KEYB)) {
403 			uint8 prefix_cmd;
404 			if (gActiveMultiplexingEnabled)
405 				prefix_cmd = 0x90 + dev->idx;
406 			else
407 				prefix_cmd = 0xd4;
408 			res = ps2_wait_write();
409 			if (res == B_OK)
410 				ps2_write_ctrl(prefix_cmd);
411 		}
412 
413 		res = ps2_wait_write();
414 		if (res == B_OK) {
415 			if (i == -1) {
416 				if (cmd == PS2_CMD_GET_DEVICE_ID)
417 					atomic_or(&dev->flags, PS2_FLAG_CMD | PS2_FLAG_GETID);
418 				else if (cmd == PS2_CMD_RESEND)
419 					atomic_or(&dev->flags, PS2_FLAG_CMD | PS2_FLAG_RESEND);
420 				else
421 					atomic_or(&dev->flags, PS2_FLAG_CMD);
422 				ps2_write_data(cmd);
423 			} else {
424 				ps2_write_data(out[i]);
425 			}
426 		}
427 
428 		release_sem(gControllerSem);
429 
430 		start = system_time();
431 		res = acquire_sem_etc(dev->result_sem, 1, B_RELATIVE_TIMEOUT, timeout);
432 
433 		if (res != B_OK)
434 			atomic_and(&dev->flags, ~PS2_FLAG_CMD);
435 
436 		TRACE("ps2: ps2_dev_command wait for ack res 0x%08" B_PRIx32 ", "
437 			"wait-time %" B_PRId64 "\n", res, system_time() - start);
438 
439 		if (atomic_get(&dev->flags) & PS2_FLAG_ACK) {
440 			TRACE("ps2: ps2_dev_command got ACK\n");
441 		}
442 
443 		if (atomic_get(&dev->flags) & PS2_FLAG_NACK) {
444 			atomic_and(&dev->flags, ~PS2_FLAG_CMD);
445 			res = B_IO_ERROR;
446 			TRACE("ps2: ps2_dev_command got NACK\n");
447 		}
448 
449 		if (res != B_OK)
450 			break;
451 	}
452 
453 	if (res == B_OK) {
454 		if (in_count == 0) {
455 			atomic_and(&dev->flags, ~PS2_FLAG_CMD);
456 		} else {
457 			start = system_time();
458 			res = acquire_sem_etc(dev->result_sem, 1, B_RELATIVE_TIMEOUT,
459 				timeout);
460 
461 			atomic_and(&dev->flags, ~PS2_FLAG_CMD);
462 
463 			if (dev->result_buf_cnt != 0) {
464 				TRACE("ps2: ps2_dev_command error: %d rx bytes not received\n",
465 					dev->result_buf_cnt);
466 				in_count -= dev->result_buf_cnt;
467 				dev->result_buf_cnt = 0;
468 				res = B_IO_ERROR;
469 			}
470 
471 			TRACE("ps2: ps2_dev_command wait for input res 0x%08" B_PRIx32 ", "
472 				"wait-time %" B_PRId64 "\n", res, system_time() - start);
473 
474 			for (i = 0; i < in_count; i++)
475 				TRACE("ps2: ps2_dev_command rx: 0x%02x\n", in[i]);
476 		}
477 	}
478 
479 	TRACE("ps2: ps2_dev_command result 0x%08" B_PRIx32 "\n", res);
480 
481 	return res;
482 }
483 
484 
485 status_t
486 ps2_dev_command(ps2_dev* dev, uint8 cmd, const uint8* out, int out_count,
487 	uint8* in, int in_count)
488 {
489 	return ps2_dev_command_timeout(dev, cmd, out, out_count, in, in_count,
490 		4000000);
491 }
492 
493 
494 status_t
495 ps2_dev_command_timeout(ps2_dev* dev, uint8 cmd, const uint8* out,
496 	int out_count, uint8* in, int in_count, bigtime_t timeout)
497 {
498 	return dev->command(dev, cmd, out, out_count, in, in_count, timeout);
499 }
500 
501 
502 status_t
503 ps2_dev_sliced_command(ps2_dev* dev, uint8 cmd)
504 {
505 	uint8 val;
506 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK)
507 		return B_ERROR;
508 
509 	val = (cmd >> 6) & 3;
510 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
511 		return B_ERROR;
512 
513 	val = (cmd >> 4) & 3;
514 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
515 		return B_ERROR;
516 
517 	val = (cmd >> 2) & 3;
518 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
519 		return B_ERROR;
520 
521 	val = cmd & 3;
522 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
523 		return B_ERROR;
524 
525 	return B_OK;
526 }
527