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
26ps2_dev ps2_device[PS2_DEVICE_COUNT];
27
28
29status_t
30ps2_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
61status_t
62ps2_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
107dev_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
115status_t
116ps2_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;
161err:
162	ps2_dev_exit();
163	return B_ERROR;
164}
165
166
167void
168ps2_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
181void
182ps2_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
227void
228ps2_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
248int32
249ps2_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
317pass_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
358status_t
359standard_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
477status_t
478ps2_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
486status_t
487ps2_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