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