xref: /haiku/src/add-ons/kernel/bus_managers/ps2/ps2_dev.cpp (revision b41678294beaafee3f90d30b132088d890eac969)
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 #ifdef TRACE_PS2
372 	bigtime_t start;
373 #endif
374 	int32 sem_count;
375 	int i;
376 
377 	TRACE("ps2: ps2_dev_command cmd 0x%02x, out-count %d, in-count %d, "
378 		"dev %s\n", cmd, out_count, in_count, dev->name);
379 	for (i = 0; i < out_count; i++)
380 		TRACE("ps2: ps2_dev_command tx: 0x%02x\n", out[i]);
381 
382 	res = get_sem_count(dev->result_sem, &sem_count);
383 	if (res == B_OK && sem_count != 0) {
384 		TRACE("ps2: ps2_dev_command: sem_count %" B_PRId32 ", fixing!\n",
385 			sem_count);
386 		if (sem_count > 0)
387 			acquire_sem_etc(dev->result_sem, sem_count, 0, 0);
388 		else
389 			release_sem_etc(dev->result_sem, -sem_count, 0);
390 	}
391 
392 	dev->result_buf_cnt = in_count;
393 	dev->result_buf_idx = 0;
394 	dev->result_buf = in;
395 
396 	res = B_OK;
397 	for (i = -1; res == B_OK && i < out_count; i++) {
398 
399 		atomic_and(&dev->flags,
400 			~(PS2_FLAG_ACK | PS2_FLAG_NACK | PS2_FLAG_GETID | PS2_FLAG_RESEND));
401 
402 		acquire_sem(gControllerSem);
403 
404 		if (!(atomic_get(&dev->flags) & PS2_FLAG_KEYB)) {
405 			uint8 prefix_cmd;
406 			if (gActiveMultiplexingEnabled)
407 				prefix_cmd = 0x90 + dev->idx;
408 			else
409 				prefix_cmd = 0xd4;
410 			res = ps2_wait_write();
411 			if (res == B_OK)
412 				ps2_write_ctrl(prefix_cmd);
413 		}
414 
415 		res = ps2_wait_write();
416 		if (res == B_OK) {
417 			if (i == -1) {
418 				if (cmd == PS2_CMD_GET_DEVICE_ID)
419 					atomic_or(&dev->flags, PS2_FLAG_CMD | PS2_FLAG_GETID);
420 				else if (cmd == PS2_CMD_RESEND)
421 					atomic_or(&dev->flags, PS2_FLAG_CMD | PS2_FLAG_RESEND);
422 				else
423 					atomic_or(&dev->flags, PS2_FLAG_CMD);
424 				ps2_write_data(cmd);
425 			} else {
426 				ps2_write_data(out[i]);
427 			}
428 		}
429 
430 		release_sem(gControllerSem);
431 #ifdef TRACE_PS2
432 		start = system_time();
433 #endif
434 		res = acquire_sem_etc(dev->result_sem, 1, B_RELATIVE_TIMEOUT, timeout);
435 
436 		if (res != B_OK)
437 			atomic_and(&dev->flags, ~PS2_FLAG_CMD);
438 
439 #ifdef TRACE_PS2
440 		TRACE("ps2: ps2_dev_command wait for ack res 0x%08" B_PRIx32 ", "
441 			"wait-time %" B_PRId64 "\n", res, system_time() - start);
442 #endif
443 		if (atomic_get(&dev->flags) & PS2_FLAG_ACK) {
444 			TRACE("ps2: ps2_dev_command got ACK\n");
445 		}
446 
447 		if (atomic_get(&dev->flags) & PS2_FLAG_NACK) {
448 			atomic_and(&dev->flags, ~PS2_FLAG_CMD);
449 			res = B_IO_ERROR;
450 			TRACE("ps2: ps2_dev_command got NACK\n");
451 		}
452 
453 		if (res != B_OK)
454 			break;
455 	}
456 
457 	if (res == B_OK) {
458 		if (in_count == 0) {
459 			atomic_and(&dev->flags, ~PS2_FLAG_CMD);
460 		} else {
461 #ifdef TRACE_PS2
462 			start = system_time();
463 #endif
464 			res = acquire_sem_etc(dev->result_sem, 1, B_RELATIVE_TIMEOUT,
465 				timeout);
466 
467 			atomic_and(&dev->flags, ~PS2_FLAG_CMD);
468 
469 			if (dev->result_buf_cnt != 0) {
470 				TRACE("ps2: ps2_dev_command error: %d rx bytes not received\n",
471 					dev->result_buf_cnt);
472 				in_count -= dev->result_buf_cnt;
473 				dev->result_buf_cnt = 0;
474 				res = B_IO_ERROR;
475 			}
476 
477 #ifdef TRACE_PS2
478 			TRACE("ps2: ps2_dev_command wait for input res 0x%08" B_PRIx32 ", "
479 				"wait-time %" B_PRId64 "\n", res, system_time() - start);
480 			for (i = 0; i < in_count; i++)
481 				TRACE("ps2: ps2_dev_command rx: 0x%02x\n", in[i]);
482 #endif
483 		}
484 	}
485 
486 	TRACE("ps2: ps2_dev_command result 0x%08" B_PRIx32 "\n", res);
487 
488 	return res;
489 }
490 
491 
492 status_t
493 ps2_dev_command(ps2_dev* dev, uint8 cmd, const uint8* out, int out_count,
494 	uint8* in, int in_count)
495 {
496 	return ps2_dev_command_timeout(dev, cmd, out, out_count, in, in_count,
497 		4000000);
498 }
499 
500 
501 status_t
502 ps2_dev_command_timeout(ps2_dev* dev, uint8 cmd, const uint8* out,
503 	int out_count, uint8* in, int in_count, bigtime_t timeout)
504 {
505 	return dev->command(dev, cmd, out, out_count, in, in_count, timeout);
506 }
507 
508 
509 status_t
510 ps2_dev_sliced_command(ps2_dev* dev, uint8 cmd)
511 {
512 	uint8 val;
513 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK)
514 		return B_ERROR;
515 
516 	val = (cmd >> 6) & 3;
517 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
518 		return B_ERROR;
519 
520 	val = (cmd >> 4) & 3;
521 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
522 		return B_ERROR;
523 
524 	val = (cmd >> 2) & 3;
525 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
526 		return B_ERROR;
527 
528 	val = cmd & 3;
529 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
530 		return B_ERROR;
531 
532 	return B_OK;
533 }
534