xref: /haiku/src/add-ons/kernel/bus_managers/ps2/ps2_dev.cpp (revision 71452e98334eaac603bf542d159e24788a46bebb)
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 #if 0
93 	status = probe_elantech(dev);
94 	if (status == B_OK) {
95 		*hooks = &gElantechDeviceHooks;
96 		goto dev_found;
97 	}
98 #endif
99 
100 	// reset the mouse for the case that the previous probes leaf the mouse in
101 	// a undefined state
102 	status = ps2_reset_mouse(dev);
103 	if (status != B_OK) {
104 		INFO("ps2: reset after probe failed\n");
105 		return B_ERROR;
106 	}
107 
108 	status = probe_standard_mouse(dev);
109 	if (status == B_OK) {
110 		*hooks = &gStandardMouseDeviceHooks;
111 		goto dev_found;
112 	}
113 
114 	return B_ERROR;
115 
116 dev_found:
117 	if (dev == &(ps2_device[PS2_DEVICE_SYN_PASSTHROUGH]))
118 		synaptics_pass_through_set_packet_size(dev, dev->packet_size);
119 
120 	return B_OK;
121 }
122 
123 
124 status_t
125 ps2_dev_init(void)
126 {
127 	ps2_device[0].name = "input/mouse/ps2/0";
128 	ps2_device[0].active = false;
129 	ps2_device[0].idx = 0;
130 	ps2_device[0].result_sem = -1;
131 	ps2_device[0].command = standard_command_timeout;
132 
133 	ps2_device[1].name = "input/mouse/ps2/1";
134 	ps2_device[1].active = false;
135 	ps2_device[1].idx = 1;
136 	ps2_device[1].result_sem = -1;
137 	ps2_device[1].command = standard_command_timeout;
138 
139 	ps2_device[2].name = "input/mouse/ps2/2";
140 	ps2_device[2].active = false;
141 	ps2_device[2].idx = 2;
142 	ps2_device[2].result_sem = -1;
143 	ps2_device[2].command = standard_command_timeout;
144 
145 	ps2_device[3].name = "input/mouse/ps2/3";
146 	ps2_device[3].active = false;
147 	ps2_device[3].idx = 3;
148 	ps2_device[3].result_sem = -1;
149 	ps2_device[3].command = standard_command_timeout;
150 
151 	ps2_device[4].name = "input/mouse/ps2/synaptics_passthrough";
152 	ps2_device[4].active = false;
153 	ps2_device[4].result_sem = -1;
154 	ps2_device[4].command = passthrough_command;
155 
156 	ps2_device[5].name = "input/keyboard/at/0";
157 	ps2_device[5].active = false;
158 	ps2_device[5].result_sem = -1;
159 	ps2_device[5].flags = PS2_FLAG_KEYB;
160 	ps2_device[5].command = standard_command_timeout;
161 
162 	int i;
163 	for (i = 0; i < PS2_DEVICE_COUNT; i++) {
164 		ps2_dev* dev = &ps2_device[i];
165 		dev->result_sem = create_sem(0, "ps2 result");
166 		if (dev->result_sem < 0)
167 			goto err;
168 	}
169 	return B_OK;
170 
171 err:
172 	ps2_dev_exit();
173 
174 	return B_ERROR;
175 }
176 
177 
178 void
179 ps2_dev_exit(void)
180 {
181 	for (int i = 0; i < PS2_DEVICE_COUNT; i++) {
182 		ps2_dev* dev = &ps2_device[i];
183 		if (dev->result_sem >= 0) {
184 			delete_sem(dev->result_sem);
185 			dev->result_sem = -1;
186 		}
187 	}
188 }
189 
190 
191 void
192 ps2_dev_publish(ps2_dev* dev)
193 {
194 	status_t status = B_OK;
195 	TRACE("ps2: ps2_dev_publish %s\n", dev->name);
196 
197 	if (dev->active)
198 		return;
199 
200 	if (atomic_get(&dev->flags) & PS2_FLAG_KEYB) {
201 		status = devfs_publish_device(dev->name, &gKeyboardDeviceHooks);
202 	} else {
203 		// Check if this is the "pass-through" device and wait until
204 		// the parent_dev goes to enabled state. It is required to prevent
205 		// from messing up the Synaptics command sequences in synaptics_open.
206 		if (dev->parent_dev) {
207 			const bigtime_t timeout = 2000000;
208 			bigtime_t start = system_time();
209 			while (!(atomic_get(&dev->parent_dev->flags) & PS2_FLAG_ENABLED)) {
210 				if ((system_time() - start) > timeout) {
211 					status = B_BUSY;
212 					break;
213 				}
214 				snooze(timeout / 20);
215 			}
216 			TRACE("ps2: publishing %s: parent %s is %s; wait time %" B_PRId64
217 				"\n", dev->name, dev->parent_dev->name,
218 				status == B_OK ? "enabled" : "busy", system_time() - start);
219 		}
220 
221 		if (status == B_OK) {
222 			device_hooks* hooks;
223 			status = ps2_dev_detect_pointing(dev, &hooks);
224 			if (status == B_OK) {
225 				status = devfs_publish_device(dev->name, hooks);
226 			}
227 		}
228 	}
229 
230 	dev->active = true;
231 
232 	INFO("ps2: devfs_publish_device %s, status = 0x%08" B_PRIx32 "\n",
233 		dev->name, status);
234 }
235 
236 
237 void
238 ps2_dev_unpublish(ps2_dev* dev)
239 {
240 	status_t status;
241 	TRACE("ps2: ps2_dev_unpublish %s\n", dev->name);
242 
243 	if (!dev->active)
244 		return;
245 
246 	dev->active = false;
247 
248 	status = devfs_unpublish_device(dev->name, true);
249 
250 	if ((dev->flags & PS2_FLAG_ENABLED) && dev->disconnect)
251 		dev->disconnect(dev);
252 
253 	INFO("ps2: devfs_unpublish_device %s, status = 0x%08" B_PRIx32 "\n",
254 		dev->name, status);
255 }
256 
257 
258 int32
259 ps2_dev_handle_int(ps2_dev* dev)
260 {
261 	const uint8 data = dev->history[0].data;
262 	uint32 flags;
263 
264 	flags = atomic_get(&dev->flags);
265 
266 	if (flags & PS2_FLAG_CMD) {
267 		if ((flags & (PS2_FLAG_ACK | PS2_FLAG_NACK)) == 0) {
268 			int cnt = 1;
269 			if (data == PS2_REPLY_ACK) {
270 				atomic_or(&dev->flags, PS2_FLAG_ACK);
271 			} else if (data == PS2_REPLY_RESEND || data == PS2_REPLY_ERROR) {
272 				atomic_or(&dev->flags, PS2_FLAG_NACK);
273 			} else if ((flags & PS2_FLAG_GETID)
274 				&& (data == 0 || data == 3 || data == 4)) {
275 				// workaround for broken mice that don't ack the "get id"
276 				// command
277 				TRACE("ps2: ps2_dev_handle_int: mouse didn't ack the 'get id' "
278 					"command\n");
279 				atomic_or(&dev->flags, PS2_FLAG_ACK);
280 				if (dev->result_buf_cnt) {
281 					dev->result_buf[dev->result_buf_idx] = data;
282 					dev->result_buf_idx++;
283 					dev->result_buf_cnt--;
284 					if (dev->result_buf_cnt == 0) {
285 						atomic_and(&dev->flags, ~PS2_FLAG_CMD);
286 						cnt++;
287 					}
288 				}
289 			} else if ((flags & PS2_FLAG_RESEND)) {
290 				TRACE("ps2: ps2_dev_handle_int: processing RESEND request\n");
291 				atomic_or(&dev->flags, PS2_FLAG_ACK);
292 				if (dev->result_buf_cnt) {
293 					dev->result_buf[dev->result_buf_idx] = data;
294 					dev->result_buf_idx++;
295 					dev->result_buf_cnt--;
296 					if (dev->result_buf_cnt == 0) {
297 						atomic_and(&dev->flags, ~PS2_FLAG_CMD);
298 						cnt++;
299 					}
300 				}
301 			} else {
302 //				TRACE("ps2: ps2_dev_handle_int unexpected data 0x%02x while "
303 //					"waiting for ack\n", data);
304 				TRACE("ps2: int1 %02x\n", data);
305 				goto pass_to_handler;
306 			}
307 			release_sem_etc(dev->result_sem, cnt, B_DO_NOT_RESCHEDULE);
308 			return B_INVOKE_SCHEDULER;
309 		} else if (dev->result_buf_cnt) {
310 			dev->result_buf[dev->result_buf_idx] = data;
311 			dev->result_buf_idx++;
312 			dev->result_buf_cnt--;
313 			if (dev->result_buf_cnt == 0) {
314 				atomic_and(&dev->flags, ~PS2_FLAG_CMD);
315 				release_sem_etc(dev->result_sem, 1, B_DO_NOT_RESCHEDULE);
316 				return B_INVOKE_SCHEDULER;
317 			}
318 		} else {
319 //			TRACE("ps2: ps2_dev_handle_int unexpected data 0x%02x during "
320 //				"command processing\n", data);
321 			TRACE("ps2: int2 %02x\n", data);
322 			goto pass_to_handler;
323 		}
324 		return B_HANDLED_INTERRUPT;
325 	}
326 
327 pass_to_handler:
328 
329 	if ((flags & PS2_FLAG_KEYB) == 0) {
330 		if (dev->history[0].error && data == 0xfd) {
331 			INFO("ps2: hot removal of %s\n", dev->name);
332 			ps2_service_notify_device_removed(dev);
333 			return B_INVOKE_SCHEDULER;
334 		}
335 		if (data == 0x00 && dev->history[1].data == 0xaa
336 			&& (dev->history[0].time - dev->history[1].time) < 50000) {
337 			INFO("ps2: hot plugin of %s\n", dev->name);
338 			if (dev->active) {
339 				INFO("ps2: device %s still active, republishing...\n",
340 					dev->name);
341 				ps2_service_notify_device_republish(dev);
342 			} else {
343 				ps2_service_notify_device_added(dev);
344 			}
345 			return B_INVOKE_SCHEDULER;
346 		}
347 	}
348 
349 	if (!dev->active) {
350 		TRACE("ps2: %s not active, data 0x%02x dropped\n", dev->name, data);
351 		if (data != 0x00 && data != 0xaa) {
352 			INFO("ps2: possibly a hot plugin of %s\n", dev->name);
353 			ps2_service_notify_device_added(dev);
354 			return B_INVOKE_SCHEDULER;
355 		}
356 		return B_HANDLED_INTERRUPT;
357 	}
358 
359 	if ((flags & PS2_FLAG_ENABLED) == 0) {
360 		TRACE("ps2: %s not enabled, data 0x%02x dropped\n", dev->name, data);
361 		return B_HANDLED_INTERRUPT;
362 	}
363 
364 	return dev->handle_int(dev);
365 }
366 
367 
368 status_t
369 standard_command_timeout(ps2_dev* dev, uint8 cmd, const uint8* out,
370 	int out_count, uint8* in, int in_count, bigtime_t timeout)
371 {
372 	status_t res;
373 	bigtime_t start;
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 
432 		start = system_time();
433 		res = acquire_sem_etc(dev->result_sem, 1, B_RELATIVE_TIMEOUT, timeout);
434 
435 		if (res != B_OK)
436 			atomic_and(&dev->flags, ~PS2_FLAG_CMD);
437 
438 		TRACE("ps2: ps2_dev_command wait for ack res 0x%08" B_PRIx32 ", "
439 			"wait-time %" B_PRId64 "\n", res, system_time() - start);
440 
441 		if (atomic_get(&dev->flags) & PS2_FLAG_ACK) {
442 			TRACE("ps2: ps2_dev_command got ACK\n");
443 		}
444 
445 		if (atomic_get(&dev->flags) & PS2_FLAG_NACK) {
446 			atomic_and(&dev->flags, ~PS2_FLAG_CMD);
447 			res = B_IO_ERROR;
448 			TRACE("ps2: ps2_dev_command got NACK\n");
449 		}
450 
451 		if (res != B_OK)
452 			break;
453 	}
454 
455 	if (res == B_OK) {
456 		if (in_count == 0) {
457 			atomic_and(&dev->flags, ~PS2_FLAG_CMD);
458 		} else {
459 			start = system_time();
460 			res = acquire_sem_etc(dev->result_sem, 1, B_RELATIVE_TIMEOUT,
461 				timeout);
462 
463 			atomic_and(&dev->flags, ~PS2_FLAG_CMD);
464 
465 			if (dev->result_buf_cnt != 0) {
466 				TRACE("ps2: ps2_dev_command error: %d rx bytes not received\n",
467 					dev->result_buf_cnt);
468 				in_count -= dev->result_buf_cnt;
469 				dev->result_buf_cnt = 0;
470 				res = B_IO_ERROR;
471 			}
472 
473 			TRACE("ps2: ps2_dev_command wait for input res 0x%08" B_PRIx32 ", "
474 				"wait-time %" B_PRId64 "\n", res, system_time() - start);
475 
476 			for (i = 0; i < in_count; i++)
477 				TRACE("ps2: ps2_dev_command rx: 0x%02x\n", in[i]);
478 		}
479 	}
480 
481 	TRACE("ps2: ps2_dev_command result 0x%08" B_PRIx32 "\n", res);
482 
483 	return res;
484 }
485 
486 
487 status_t
488 ps2_dev_command(ps2_dev* dev, uint8 cmd, const uint8* out, int out_count,
489 	uint8* in, int in_count)
490 {
491 	return ps2_dev_command_timeout(dev, cmd, out, out_count, in, in_count,
492 		4000000);
493 }
494 
495 
496 status_t
497 ps2_dev_command_timeout(ps2_dev* dev, uint8 cmd, const uint8* out,
498 	int out_count, uint8* in, int in_count, bigtime_t timeout)
499 {
500 	return dev->command(dev, cmd, out, out_count, in, in_count, timeout);
501 }
502 
503 
504 status_t
505 ps2_dev_sliced_command(ps2_dev* dev, uint8 cmd)
506 {
507 	uint8 val;
508 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK)
509 		return B_ERROR;
510 
511 	val = (cmd >> 6) & 3;
512 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
513 		return B_ERROR;
514 
515 	val = (cmd >> 4) & 3;
516 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
517 		return B_ERROR;
518 
519 	val = (cmd >> 2) & 3;
520 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
521 		return B_ERROR;
522 
523 	val = cmd & 3;
524 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
525 		return B_ERROR;
526 
527 	return B_OK;
528 }
529