xref: /haiku/src/add-ons/kernel/bus_managers/ps2/ps2_dev.cpp (revision 805cd0be8c456c8c19aea06a1b12bcd93eada2f2)
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] != 0xAA && data[1] != 0x00) {
40 		TRACE("ps2: reset mouse failed, response was: 0x%02x 0x%02x\n",
41 			data[0], data[1]);
42 		status = B_ERROR;
43 	} else if (status != B_OK) {
44 		TRACE("ps2: reset mouse failed\n");
45 	} else {
46 		TRACE("ps2: reset mouse success\n");
47 	}
48 
49 	return status;
50 }
51 
52 
53 status_t
54 ps2_dev_detect_pointing(ps2_dev *dev, device_hooks **hooks)
55 {
56 	status_t status = ps2_reset_mouse(dev);
57 	if (status != B_OK) {
58 		INFO("ps2: reset failed\n");
59 		return B_ERROR;
60 	}
61 
62 	// probe devices
63 	// the probe function has to set the dev name and the dev packet size
64 
65 	status = probe_trackpoint(dev);
66 	if (status == B_OK) {
67 		*hooks = &gStandardMouseDeviceHooks;
68 		goto dev_found;
69 	}
70 
71 	status = probe_synaptics(dev);
72 	if (status == B_OK) {
73 		*hooks = &gSynapticsDeviceHooks;
74 		goto dev_found;
75 	}
76 
77 	status = probe_alps(dev);
78 	if (status == B_OK) {
79 		*hooks = &gALPSDeviceHooks;
80 		goto dev_found;
81 	}
82 
83 	// reset the mouse for the case that the previous probes leaf the mouse in
84 	// a undefined state
85 	status = ps2_reset_mouse(dev);
86 	if (status != B_OK) {
87 		INFO("ps2: reset after probe failed\n");
88 		return B_ERROR;
89 	}
90 
91 	status = probe_standard_mouse(dev);
92 	if (status == B_OK) {
93 		*hooks = &gStandardMouseDeviceHooks;
94 		goto dev_found;
95 	}
96 
97 	return B_ERROR;
98 
99 dev_found:
100 	if (dev == &(ps2_device[PS2_DEVICE_SYN_PASSTHROUGH]))
101 		synaptics_pass_through_set_packet_size(dev, dev->packet_size);
102 
103 	return B_OK;
104 }
105 
106 
107 status_t
108 ps2_dev_init(void)
109 {
110 	ps2_device[0].name = "input/mouse/ps2/0";
111 	ps2_device[0].active = false;
112 	ps2_device[0].idx = 0;
113 	ps2_device[0].result_sem = -1;
114 	ps2_device[0].command = standard_command_timeout;
115 
116 	ps2_device[1].name = "input/mouse/ps2/1";
117 	ps2_device[1].active = false;
118 	ps2_device[1].idx = 1;
119 	ps2_device[1].result_sem = -1;
120 	ps2_device[1].command = standard_command_timeout;
121 
122 	ps2_device[2].name = "input/mouse/ps2/2";
123 	ps2_device[2].active = false;
124 	ps2_device[2].idx = 2;
125 	ps2_device[2].result_sem = -1;
126 	ps2_device[2].command = standard_command_timeout;
127 
128 	ps2_device[3].name = "input/mouse/ps2/3";
129 	ps2_device[3].active = false;
130 	ps2_device[3].idx = 3;
131 	ps2_device[3].result_sem = -1;
132 	ps2_device[3].command = standard_command_timeout;
133 
134 	ps2_device[4].name = "input/mouse/ps2/synaptics_passthrough";
135 	ps2_device[4].active = false;
136 	ps2_device[4].result_sem = -1;
137 	ps2_device[4].command = passthrough_command;
138 
139 	ps2_device[5].name = "input/keyboard/at/0";
140 	ps2_device[5].active = false;
141 	ps2_device[5].result_sem = -1;
142 	ps2_device[5].flags = PS2_FLAG_KEYB;
143 	ps2_device[5].command = standard_command_timeout;
144 
145 	int i;
146 	for (i = 0; i < PS2_DEVICE_COUNT; i++) {
147 		ps2_dev *dev = &ps2_device[i];
148 		dev->result_sem = create_sem(0, "ps2 result");
149 		if (dev->result_sem < 0)
150 			goto err;
151 	}
152 	return B_OK;
153 err:
154 	ps2_dev_exit();
155 	return B_ERROR;
156 }
157 
158 
159 void
160 ps2_dev_exit(void)
161 {
162 	int i;
163 	for (i = 0; i < PS2_DEVICE_COUNT; i++) {
164 		ps2_dev *dev = &ps2_device[i];
165 		if (dev->result_sem >= 0) {
166 			delete_sem(dev->result_sem);
167 			dev->result_sem = -1;
168 		}
169 	}
170 }
171 
172 
173 void
174 ps2_dev_publish(ps2_dev *dev)
175 {
176 	status_t status;
177 	TRACE("ps2: ps2_dev_publish %s\n", dev->name);
178 
179 	if (dev->active)
180 		return;
181 
182 	if (atomic_get(&dev->flags) & PS2_FLAG_KEYB) {
183 		status = devfs_publish_device(dev->name, &gKeyboardDeviceHooks);
184 	} else {
185 		device_hooks *hooks;
186 		status = ps2_dev_detect_pointing(dev, &hooks);
187 		if (status == B_OK) {
188 			status = devfs_publish_device(dev->name, hooks);
189 		}
190 	}
191 
192 	dev->active = true;
193 
194 	INFO("ps2: devfs_publish_device %s, status = 0x%08lx\n", dev->name, status);
195 }
196 
197 
198 void
199 ps2_dev_unpublish(ps2_dev *dev)
200 {
201 	status_t status;
202 	TRACE("ps2: ps2_dev_unpublish %s\n", dev->name);
203 
204 	if (!dev->active)
205 		return;
206 
207 	dev->active = false;
208 
209 	status = devfs_unpublish_device(dev->name, true);
210 
211 	if ((dev->flags & PS2_FLAG_ENABLED) && dev->disconnect)
212 		dev->disconnect(dev);
213 
214 	INFO("ps2: devfs_unpublish_device %s, status = 0x%08lx\n", dev->name,
215 		status);
216 }
217 
218 
219 int32
220 ps2_dev_handle_int(ps2_dev *dev)
221 {
222 	const uint8 data = dev->history[0].data;
223 	uint32 flags;
224 
225 	flags = atomic_get(&dev->flags);
226 
227 	if (flags & PS2_FLAG_CMD) {
228 		if ((flags & (PS2_FLAG_ACK | PS2_FLAG_NACK)) == 0) {
229 			int cnt = 1;
230 			if (data == PS2_REPLY_ACK) {
231 				atomic_or(&dev->flags, PS2_FLAG_ACK);
232 			} else if (data == PS2_REPLY_RESEND || data == PS2_REPLY_ERROR) {
233 				atomic_or(&dev->flags, PS2_FLAG_NACK);
234 			} else if ((flags & PS2_FLAG_GETID)
235 				&& (data == 0 || data == 3 || data == 4)) {
236 				// workaround for broken mice that don't ack the "get id"
237 				// command
238 				TRACE("ps2: ps2_dev_handle_int: mouse didn't ack the 'get id' "
239 					"command\n");
240 				atomic_or(&dev->flags, PS2_FLAG_ACK);
241 				if (dev->result_buf_cnt) {
242 					dev->result_buf[dev->result_buf_idx] = data;
243 					dev->result_buf_idx++;
244 					dev->result_buf_cnt--;
245 					if (dev->result_buf_cnt == 0) {
246 						atomic_and(&dev->flags, ~PS2_FLAG_CMD);
247 						cnt++;
248 					}
249 				}
250 			} else {
251 //				TRACE("ps2: ps2_dev_handle_int unexpected data 0x%02x while "
252 //					"waiting for ack\n", data);
253 				TRACE("ps2: int1 %02x\n", data);
254 				goto pass_to_handler;
255 			}
256 			release_sem_etc(dev->result_sem, cnt, B_DO_NOT_RESCHEDULE);
257 			return B_INVOKE_SCHEDULER;
258 		} else if (dev->result_buf_cnt) {
259 			dev->result_buf[dev->result_buf_idx] = data;
260 			dev->result_buf_idx++;
261 			dev->result_buf_cnt--;
262 			if (dev->result_buf_cnt == 0) {
263 				atomic_and(&dev->flags, ~PS2_FLAG_CMD);
264 				release_sem_etc(dev->result_sem, 1, B_DO_NOT_RESCHEDULE);
265 				return B_INVOKE_SCHEDULER;
266 			}
267 		} else {
268 //			TRACE("ps2: ps2_dev_handle_int unexpected data 0x%02x during "
269 //				"command processing\n", data);
270 			TRACE("ps2: int2 %02x\n", data);
271 			goto pass_to_handler;
272 		}
273 		return B_HANDLED_INTERRUPT;
274 	}
275 
276 pass_to_handler:
277 
278 	if ((flags & PS2_FLAG_KEYB) == 0) {
279 		if (dev->history[0].error && data == 0xfd) {
280 			INFO("ps2: hot removal of %s\n", dev->name);
281 			ps2_service_notify_device_removed(dev);
282 			return B_INVOKE_SCHEDULER;
283 		}
284 		if (data == 0x00 && dev->history[1].data == 0xaa
285 			&& (dev->history[0].time - dev->history[1].time) < 50000) {
286 			INFO("ps2: hot plugin of %s\n", dev->name);
287 			if (dev->active) {
288 				INFO("ps2: device %s still active, republishing...\n",
289 					dev->name);
290 				ps2_service_notify_device_republish(dev);
291 			} else {
292 				ps2_service_notify_device_added(dev);
293 			}
294 			return B_INVOKE_SCHEDULER;
295 		}
296 	}
297 
298 	if (!dev->active) {
299 		TRACE("ps2: %s not active, data 0x%02x dropped\n", dev->name, data);
300 		if (data != 0x00 && data != 0xaa) {
301 			INFO("ps2: possibly a hot plugin of %s\n", dev->name);
302 			ps2_service_notify_device_added(dev);
303 			return B_INVOKE_SCHEDULER;
304 		}
305 		return B_HANDLED_INTERRUPT;
306 	}
307 
308 	if ((flags & PS2_FLAG_ENABLED) == 0) {
309 		TRACE("ps2: %s not enabled, data 0x%02x dropped\n", dev->name, data);
310 		return B_HANDLED_INTERRUPT;
311 	}
312 
313 	return dev->handle_int(dev);
314 }
315 
316 
317 status_t
318 standard_command_timeout(ps2_dev *dev, uint8 cmd, const uint8 *out,
319 	int out_count, uint8 *in, int in_count, bigtime_t timeout)
320 {
321 	status_t res;
322 	bigtime_t start;
323 	int32 sem_count;
324 	int i;
325 
326 	TRACE("ps2: ps2_dev_command cmd 0x%02x, out-count %d, in-count %d, "
327 		"dev %s\n", cmd, out_count, in_count, dev->name);
328 	for (i = 0; i < out_count; i++)
329 		TRACE("ps2: ps2_dev_command tx: 0x%02x\n", out[i]);
330 
331 	res = get_sem_count(dev->result_sem, &sem_count);
332 	if (res == B_OK && sem_count != 0) {
333 		TRACE("ps2: ps2_dev_command: sem_count %ld, fixing!\n", sem_count);
334 		if (sem_count > 0)
335 			acquire_sem_etc(dev->result_sem, sem_count, 0, 0);
336 		else
337 			release_sem_etc(dev->result_sem, -sem_count, 0);
338 	}
339 
340 	dev->result_buf_cnt = in_count;
341 	dev->result_buf_idx = 0;
342 	dev->result_buf = in;
343 
344 	res = B_OK;
345 	for (i = -1; res == B_OK && i < out_count; i++) {
346 
347 		atomic_and(&dev->flags,
348 			~(PS2_FLAG_ACK | PS2_FLAG_NACK | PS2_FLAG_GETID));
349 
350 		acquire_sem(gControllerSem);
351 
352 		if (!(atomic_get(&dev->flags) & PS2_FLAG_KEYB)) {
353 			uint8 prefix_cmd;
354 			if (gActiveMultiplexingEnabled)
355 				prefix_cmd = 0x90 + dev->idx;
356 			else
357 				prefix_cmd = 0xd4;
358 			res = ps2_wait_write();
359 			if (res == B_OK)
360 				ps2_write_ctrl(prefix_cmd);
361 		}
362 
363 		res = ps2_wait_write();
364 		if (res == B_OK) {
365 			if (i == -1) {
366 				if (cmd == PS2_CMD_GET_DEVICE_ID)
367 					atomic_or(&dev->flags, PS2_FLAG_CMD | PS2_FLAG_GETID);
368 				else
369 					atomic_or(&dev->flags, PS2_FLAG_CMD);
370 				ps2_write_data(cmd);
371 			} else {
372 				ps2_write_data(out[i]);
373 			}
374 		}
375 
376 		release_sem(gControllerSem);
377 
378 		start = system_time();
379 		res = acquire_sem_etc(dev->result_sem, 1, B_RELATIVE_TIMEOUT, timeout);
380 
381 		if (res != B_OK)
382 			atomic_and(&dev->flags, ~PS2_FLAG_CMD);
383 
384 		TRACE("ps2: ps2_dev_command wait for ack res 0x%08lx, wait-time %Ld\n",
385 			res, system_time() - start);
386 
387 		if (atomic_get(&dev->flags) & PS2_FLAG_ACK) {
388 			TRACE("ps2: ps2_dev_command got ACK\n");
389 		}
390 
391 		if (atomic_get(&dev->flags) & PS2_FLAG_NACK) {
392 			atomic_and(&dev->flags, ~PS2_FLAG_CMD);
393 			res = B_IO_ERROR;
394 			TRACE("ps2: ps2_dev_command got NACK\n");
395 		}
396 
397 		if (res != B_OK)
398 			break;
399 	}
400 
401 	if (res == B_OK) {
402 		if (in_count == 0) {
403 			atomic_and(&dev->flags, ~PS2_FLAG_CMD);
404 		} else {
405 			start = system_time();
406 			res = acquire_sem_etc(dev->result_sem, 1, B_RELATIVE_TIMEOUT,
407 				timeout);
408 
409 			atomic_and(&dev->flags, ~PS2_FLAG_CMD);
410 
411 			if (dev->result_buf_cnt != 0) {
412 				TRACE("ps2: ps2_dev_command error: %d rx bytes not received\n",
413 					dev->result_buf_cnt);
414 				in_count -= dev->result_buf_cnt;
415 				dev->result_buf_cnt = 0;
416 				res = B_IO_ERROR;
417 			}
418 
419 			TRACE("ps2: ps2_dev_command wait for input res 0x%08lx, "
420 				"wait-time %Ld\n", res, system_time() - start);
421 
422 			for (i = 0; i < in_count; i++)
423 				TRACE("ps2: ps2_dev_command rx: 0x%02x\n", in[i]);
424 		}
425 	}
426 
427 	TRACE("ps2: ps2_dev_command result 0x%08lx\n", res);
428 
429 	return res;
430 }
431 
432 
433 status_t
434 ps2_dev_command(ps2_dev *dev, uint8 cmd, const uint8 *out, int out_count,
435 	uint8 *in, int in_count)
436 {
437 	return ps2_dev_command_timeout(dev, cmd, out, out_count, in, in_count,
438 		4000000);
439 }
440 
441 
442 status_t
443 ps2_dev_command_timeout(ps2_dev *dev, uint8 cmd, const uint8 *out,
444 	int out_count, uint8 *in, int in_count, bigtime_t timeout)
445 {
446 	return dev->command(dev, cmd, out, out_count, in, in_count, timeout);
447 }
448 
449