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