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