xref: /haiku/src/add-ons/kernel/bus_managers/ps2/ps2_standard_mouse.cpp (revision d64b771b96050fca8ff1859daa5ec44ff3493af7)
1 /*
2  * Copyright 2001-2014 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors (in chronological order):
6  *		Elad Lahav, elad@eldarshany.com
7  *		Stefano Ceccherini, burton666@libero.it
8  *		Axel Dörfler, axeld@pinc-software.de
9  *		Marcus Overhagen, marcus@overhagen.de
10  *		Clemens Zeidler, czeidler@gmx.de
11  *		John Scipione, jscipione@gmail.com
12  */
13 
14 
15 /*!	PS/2 mouse device driver
16 
17 	A PS/2 mouse is connected to the IBM 8042 controller, and gets its
18 	name from the IBM PS/2 personal computer, which was the first to
19 	use this device. All resources are shared between the keyboard, and
20 	the mouse, referred to as the "Auxiliary Device".
21 
22 	I/O:
23 	~~~
24 	The controller has 3 I/O registers:
25 	1. Status (input), mapped to port 64h
26 	2. Control (output), mapped to port 64h
27 	3. Data (input/output), mapped to port 60h
28 
29 	Data:
30 	~~~~
31 	A packet read from the mouse data port is composed of
32 	three bytes:
33 		byte 0: status byte, where
34 			- bit 7: Y overflow (1 = true)
35 			- bit 6: X overflow (1 = true)
36 			- bit 5: MSB of Y offset
37 			- bit 4: MSB of X offset
38 			- bit 3: Syncronization bit (always 1)
39 			- bit 2: Middle button (1 = down)
40 			- bit 1: Right button (1 = down)
41 			- bit 0: Left button (1 = down)
42 		byte 1: X position change, since last probed (-127 to +127)
43 		byte 2: Y position change, since last probed (-127 to +127)
44 
45 	Intellimouse mice send a four byte packet, where the first three
46 	bytes are the same as standard mice, and the last one reports the
47 	Z position, which is, usually, the wheel movement.
48 
49 	Interrupts:
50 	~~~~~~~~~~
51 	The PS/2 mouse device is connected to interrupt 12.
52 	The controller uses 3 consecutive interrupts to inform the computer
53 	that it has new data. On the first the data register holds the status
54 	byte, on the second the X offset, and on the 3rd the Y offset.
55 */
56 
57 
58 #include <stdlib.h>
59 #include <string.h>
60 
61 #include <keyboard_mouse_driver.h>
62 
63 #include "ps2_service.h"
64 #include "ps2_standard_mouse.h"
65 
66 
67 const char* kStandardMousePath[4] = {
68 	"input/mouse/ps2/standard_0",
69 	"input/mouse/ps2/standard_1",
70 	"input/mouse/ps2/standard_2",
71 	"input/mouse/ps2/standard_3"
72 };
73 
74 const char* kIntelliMousePath[4] = {
75 	"input/mouse/ps2/intelli_0",
76 	"input/mouse/ps2/intelli_1",
77 	"input/mouse/ps2/intelli_2",
78 	"input/mouse/ps2/intelli_3"
79 };
80 
81 
82 //!	Set sampling rate of the ps2 port.
83 static inline status_t
84 ps2_set_sample_rate(ps2_dev* dev, uint8 rate)
85 {
86 	return ps2_dev_command(dev, PS2_CMD_SET_SAMPLE_RATE, &rate, 1, NULL, 0);
87 }
88 
89 
90 //!	Converts a packet received by the mouse to a "movement".
91 static void
92 ps2_packet_to_movement(standard_mouse_cookie* cookie, uint8 packet[],
93 	mouse_movement* pos)
94 {
95 	int buttons = packet[0] & 7;
96 	int xDelta = ((packet[0] & 0x10) ? ~0xff : 0) | packet[1];
97 	int yDelta = ((packet[0] & 0x20) ? ~0xff : 0) | packet[2];
98 	int xDeltaWheel = 0;
99 	int yDeltaWheel = 0;
100 	bigtime_t currentTime = system_time();
101 
102 	if (buttons != 0 && cookie->buttons_state == 0) {
103 		if (cookie->click_last_time + cookie->click_speed > currentTime)
104 			cookie->click_count++;
105 		else
106 			cookie->click_count = 1;
107 
108 		cookie->click_last_time = currentTime;
109 	}
110 
111 	cookie->buttons_state = buttons;
112 
113 	if (cookie->flags & F_MOUSE_TYPE_INTELLIMOUSE) {
114 		yDeltaWheel = packet[3] & 0x07;
115  		if (packet[3] & 0x08)
116 			yDeltaWheel |= ~0x07;
117 	}
118 #if 0
119 	if (cookie->flags & F_MOUSE_TYPE_2WHEELS) {
120 		switch (packet[3] & 0x0F) {
121 			case 0x01: yDeltaWheel = +1; break; // wheel 1 down
122 			case 0x0F: yDeltaWheel = -1; break; // wheel 1 up
123 			case 0x02: xDeltaWheel = +1; break; // wheel 2 down
124 			case 0x0E: xDeltaWheel = -1; break; // wheel 2 up
125 		}
126 	}
127 #endif
128 
129 #if 0
130 	TRACE("packet: %02x %02x %02x %02x: xd %d, yd %d, 0x%x (%d), w-xd %d, "
131 		"w-yd %d\n", packet[0], packet[1], packet[2], packet[3],
132 		xDelta, yDelta, buttons, cookie->click_count, xDeltaWheel,
133 		yDeltaWheel);
134 #endif
135 
136 	if (pos != NULL) {
137 		pos->xdelta = xDelta;
138 		pos->ydelta = yDelta;
139 		pos->buttons = buttons;
140 		pos->clicks = cookie->click_count;
141 		pos->modifiers = 0;
142 		pos->timestamp = currentTime;
143 		pos->wheel_ydelta = yDeltaWheel;
144 		pos->wheel_xdelta = xDeltaWheel;
145 
146 		TRACE("ps2: ps2_packet_to_movement xdelta: %d, ydelta: %d, buttons %x, "
147 			"clicks: %d, timestamp %Ld\n",
148 			xDelta, yDelta, buttons, cookie->click_count, currentTime);
149 	}
150 }
151 
152 
153 //!	Read a mouse event from the mouse events chain buffer.
154 static status_t
155 standard_mouse_read_event(standard_mouse_cookie* cookie,
156 	mouse_movement* movement)
157 {
158 	uint8 packet[PS2_MAX_PACKET_SIZE];
159 	status_t status;
160 
161 	TRACE("ps2: standard_mouse_read_event\n");
162 
163 	status = acquire_sem_etc(cookie->standard_mouse_sem, 1, B_CAN_INTERRUPT, 0);
164 	TRACE("ps2: standard_mouse_read_event acquired\n");
165 	if (status < B_OK)
166 		return status;
167 
168 	if (!cookie->dev->active) {
169 		TRACE("ps2: standard_mouse_read_event: Error device no longer "
170 			"active\n");
171 		return B_ERROR;
172 	}
173 
174 	if (packet_buffer_read(cookie->standard_mouse_buffer, packet,
175 		cookie->dev->packet_size) != cookie->dev->packet_size) {
176 		TRACE("ps2: error copying buffer\n");
177 		return B_ERROR;
178 	}
179 
180 	if (!(packet[0] & 8))
181 		panic("ps2: got broken data from packet_buffer_read\n");
182 
183 	ps2_packet_to_movement(cookie, packet, movement);
184 	return B_OK;
185 }
186 
187 
188 //	#pragma mark - Interrupt handler functions
189 
190 
191 void
192 standard_mouse_disconnect(ps2_dev* dev)
193 {
194 	// the mouse device might not be opened at this point
195 	INFO("ps2: ps2_standard_mouse_disconnect %s\n", dev->name);
196 	if (dev->flags & PS2_FLAG_OPEN)
197 		release_sem(((standard_mouse_cookie*)dev->cookie)->standard_mouse_sem);
198 }
199 
200 
201 /*!	Interrupt handler for the mouse device. Called whenever the I/O
202 	controller generates an interrupt for the PS/2 mouse. Reads mouse
203 	information from the data port, and stores it, so it can be accessed
204 	by read() operations. The full data is obtained using 3 consecutive
205 	calls to the handler, each holds a different byte on the data port.
206 */
207 int32
208 standard_mouse_handle_int(ps2_dev* dev)
209 {
210 	standard_mouse_cookie* cookie = (standard_mouse_cookie*)dev->cookie;
211 	const uint8 data = dev->history[0].data;
212 
213 	TRACE("ps2: standard mouse: %1x\t%1x\t%1x\t%1x\t%1x\t%1x\t%1x\t%1x\n",
214 		data >> 7 & 1, data >> 6 & 1, data >> 5 & 1,
215 		data >> 4 & 1, data >> 3 & 1, data >> 2 & 1,
216 		data >> 1 & 1, data >> 0 & 1);
217 
218 	if (cookie->packet_index == 0 && (data & 8) == 0) {
219 		INFO("ps2: bad mouse data, trying resync\n");
220 		return B_HANDLED_INTERRUPT;
221 	}
222 
223 	// Workarounds for active multiplexing keyboard controllers
224 	// that lose data or send them to the wrong port.
225 	if (cookie->packet_index == 0 && (data & 0xc0) != 0) {
226 		INFO("ps2: strange mouse data, x/y overflow, trying resync\n");
227 		return B_HANDLED_INTERRUPT;
228 	}
229 
230 	if (cookie->packet_index == 1) {
231 		int xDelta
232 			= ((cookie->buffer[0] & 0x10) ? 0xFFFFFF00 : 0) | data;
233 		if (xDelta < -100 || xDelta > 100) {
234 			INFO("ps2: strange mouse data, x-delta %d, trying resync\n",
235 				xDelta);
236 			cookie->packet_index = 0;
237 			return B_HANDLED_INTERRUPT;
238 		}
239 	}
240 	if (cookie->packet_index == 2) {
241 		int yDelta
242 			= ((cookie->buffer[0] & 0x20) ? 0xFFFFFF00 : 0) | data;
243 		if (yDelta < -100 || yDelta > 100) {
244 			INFO("ps2: strange mouse data, y-delta %d, trying resync\n",
245 				yDelta);
246 			cookie->packet_index = 0;
247 			return B_HANDLED_INTERRUPT;
248 		}
249 	}
250 
251 	cookie->buffer[cookie->packet_index++] = data;
252 
253 	if (cookie->packet_index != dev->packet_size) {
254 		// packet not yet complete
255 		return B_HANDLED_INTERRUPT;
256 	}
257 
258 	// complete packet is assembled
259 
260 	cookie->packet_index = 0;
261 	if (packet_buffer_write(cookie->standard_mouse_buffer,
262 		cookie->buffer, dev->packet_size) != dev->packet_size) {
263 		// buffer is full, drop new data
264 		return B_HANDLED_INTERRUPT;
265 	}
266 
267 	release_sem_etc(cookie->standard_mouse_sem, 1, B_DO_NOT_RESCHEDULE);
268 
269 	return B_INVOKE_SCHEDULER;
270 }
271 
272 
273 //	#pragma mark - probe_standard_mouse()
274 
275 
276 status_t
277 probe_standard_mouse(ps2_dev* dev)
278 {
279 	status_t status;
280 	uint8 deviceId = 0;
281 
282 	// get device id
283 	status = ps2_dev_command(dev, PS2_CMD_GET_DEVICE_ID, NULL, 0,
284 		&deviceId, 1);
285 	if (status != B_OK) {
286 		INFO("ps2: probe_mouse get device id failed\n");
287 		return B_ERROR;
288 	}
289 
290 	TRACE("ps2: probe_mouse device id: %2x\n", deviceId);
291 
292 	// check for MS Intellimouse
293 	if (deviceId == 0) {
294 		uint8 alternate_device_id;
295 		status  = ps2_set_sample_rate(dev, 200);
296 		status |= ps2_set_sample_rate(dev, 100);
297 		status |= ps2_set_sample_rate(dev, 80);
298 		status |= ps2_dev_command(dev, PS2_CMD_GET_DEVICE_ID, NULL, 0,
299 			&alternate_device_id, 1);
300 		if (status == 0) {
301 			TRACE("ps2: probe_mouse alternate device id: %2x\n",
302 				alternate_device_id);
303 			deviceId = alternate_device_id;
304 		}
305 	}
306 
307 	if (deviceId == PS2_DEV_ID_STANDARD
308 		|| deviceId == PS2_DEV_ID_TOUCHPAD_RICATECH) {
309 		INFO("ps2: probe_mouse Standard PS/2 mouse found\n");
310 		dev->name = kStandardMousePath[dev->idx];
311 		dev->packet_size = PS2_PACKET_STANDARD;
312 	} else if (deviceId == PS2_DEV_ID_INTELLIMOUSE) {
313 		dev->name = kIntelliMousePath[dev->idx];
314 		dev->packet_size = PS2_PACKET_INTELLIMOUSE;
315 		INFO("ps2: probe_mouse Extended PS/2 mouse found\n");
316 	} else {
317 		INFO("ps2: probe_mouse Error unknown device id.\n");
318 		return B_ERROR;
319 	}
320 
321 	return B_OK;
322 }
323 
324 
325 //	#pragma mark - Device functions
326 
327 
328 status_t
329 standard_mouse_open(const char* name, uint32 flags, void** _cookie)
330 {
331 	standard_mouse_cookie* cookie;
332 	status_t status;
333 	ps2_dev* dev = NULL;
334 	int i;
335 
336 	TRACE("ps2: standard_mouse_open %s\n", name);
337 
338 	for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) {
339 		if (0 == strcmp(ps2_device[i].name, name)) {
340 			dev = &ps2_device[i];
341 			break;
342 		}
343 #if 0
344 		if (0 == strcmp(g_passthrough_dev.name, name)) {
345 			dev = &g_passthrough_dev;
346 			isSynapticsPTDevice = true;
347 			break;
348 		}
349 #endif
350 	}
351 
352 	if (dev == NULL) {
353 		TRACE("ps2: dev = NULL\n");
354 		return B_ERROR;
355 	}
356 
357 	if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN)
358 		return B_BUSY;
359 
360 	cookie = (standard_mouse_cookie*)malloc(sizeof(standard_mouse_cookie));
361 	if (cookie == NULL)
362 		goto err1;
363 
364 	*_cookie = cookie;
365 	memset(cookie, 0, sizeof(*cookie));
366 
367 	cookie->dev = dev;
368 	dev->cookie = cookie;
369 	dev->disconnect = &standard_mouse_disconnect;
370 	dev->handle_int = &standard_mouse_handle_int;
371 
372 	if (strstr(dev->name, "standard") != NULL)
373 		cookie->flags = F_MOUSE_TYPE_STANDARD;
374 
375 	if (strstr(dev->name, "intelli") != NULL)
376 		cookie->flags = F_MOUSE_TYPE_INTELLIMOUSE;
377 
378 	cookie->standard_mouse_buffer
379 		= create_packet_buffer(MOUSE_HISTORY_SIZE * dev->packet_size);
380 	if (cookie->standard_mouse_buffer == NULL) {
381 		TRACE("ps2: can't allocate mouse actions buffer\n");
382 		goto err2;
383 	}
384 
385 	// create the mouse semaphore, used for synchronization between
386 	// the interrupt handler and the read operation
387 	cookie->standard_mouse_sem = create_sem(0, "ps2_standard_mouse_sem");
388 	if (cookie->standard_mouse_sem < 0) {
389 		TRACE("ps2: failed creating PS/2 mouse semaphore!\n");
390 		goto err3;
391 	}
392 
393 	status = ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0);
394 	if (status < B_OK) {
395 		INFO("ps2: cannot enable mouse %s\n", name);
396 		goto err4;
397 	}
398 
399 	atomic_or(&dev->flags, PS2_FLAG_ENABLED);
400 
401 
402 	TRACE("ps2: standard_mouse_open %s success\n", name);
403 	return B_OK;
404 
405 err4:
406 	delete_sem(cookie->standard_mouse_sem);
407 err3:
408 	delete_packet_buffer(cookie->standard_mouse_buffer);
409 err2:
410 	free(cookie);
411 err1:
412 	atomic_and(&dev->flags, ~PS2_FLAG_OPEN);
413 
414 	TRACE("ps2: standard_mouse_open %s failed\n", name);
415 	return B_ERROR;
416 }
417 
418 
419 status_t
420 standard_mouse_close(void* _cookie)
421 {
422 	standard_mouse_cookie* cookie = (standard_mouse_cookie*)_cookie;
423 
424 	TRACE("ps2: standard_mouse_close %s enter\n", cookie->dev->name);
425 
426 	ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0,
427 		150000);
428 
429 	delete_packet_buffer(cookie->standard_mouse_buffer);
430 	delete_sem(cookie->standard_mouse_sem);
431 
432 	atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN);
433 	atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED);
434 
435 	TRACE("ps2: standard_mouse_close %s done\n", cookie->dev->name);
436 	return B_OK;
437 }
438 
439 
440 status_t
441 standard_mouse_freecookie(void* _cookie)
442 {
443 	standard_mouse_cookie* cookie = (standard_mouse_cookie*)_cookie;
444 	free(cookie);
445 	return B_OK;
446 }
447 
448 
449 static status_t
450 standard_mouse_read(void* cookie, off_t pos, void* buffer, size_t* _length)
451 {
452 	*_length = 0;
453 	return B_NOT_ALLOWED;
454 }
455 
456 
457 static status_t
458 standard_mouse_write(void* cookie, off_t pos, const void* buffer,
459 	size_t* _length)
460 {
461 	*_length = 0;
462 	return B_NOT_ALLOWED;
463 }
464 
465 
466 status_t
467 standard_mouse_ioctl(void* _cookie, uint32 op, void* buffer, size_t length)
468 {
469 	standard_mouse_cookie* cookie = (standard_mouse_cookie*)_cookie;
470 
471 	switch (op) {
472 		case MS_NUM_EVENTS:
473 		{
474 			int32 count;
475 			TRACE("ps2: ioctl MS_NUM_EVENTS\n");
476 			get_sem_count(cookie->standard_mouse_sem, &count);
477 			return count;
478 		}
479 
480 		case MS_READ:
481 		{
482 			mouse_movement movement;
483 			status_t status;
484 			TRACE("ps2: ioctl MS_READ\n");
485 			if ((status = standard_mouse_read_event(cookie, &movement)) < B_OK)
486 				return status;
487 //			TRACE("%s %d %d %d %d\n", cookie->dev->name,
488 //				movement.xdelta, movement.ydelta, movement.buttons,
489 //				movement.clicks);
490 			return user_memcpy(buffer, &movement, sizeof(movement));
491 		}
492 
493 		case MS_SET_TYPE:
494 			TRACE("ps2: ioctl MS_SET_TYPE not implemented\n");
495 			return B_BAD_VALUE;
496 
497 		case MS_SET_MAP:
498 			TRACE("ps2: ioctl MS_SET_MAP (set mouse mapping) not "
499 				"implemented\n");
500 			return B_BAD_VALUE;
501 
502 		case MS_GET_ACCEL:
503 			TRACE("ps2: ioctl MS_GET_ACCEL (get mouse acceleration) not "
504 				"implemented\n");
505 			return B_BAD_VALUE;
506 
507 		case MS_SET_ACCEL:
508 			TRACE("ps2: ioctl MS_SET_ACCEL (set mouse acceleration) not "
509 				"implemented\n");
510 			return B_BAD_VALUE;
511 
512 		case MS_SET_CLICKSPEED:
513 			TRACE("ps2: ioctl MS_SETCLICK (set click speed)\n");
514 			return user_memcpy(&cookie->click_speed, buffer, sizeof(bigtime_t));
515 
516 		default:
517 			TRACE("ps2: ioctl unknown mouse opcode: %ld\n", op);
518 			return B_DEV_INVALID_IOCTL;
519 	}
520 }
521 
522 
523 device_hooks gStandardMouseDeviceHooks = {
524 	standard_mouse_open,
525 	standard_mouse_close,
526 	standard_mouse_freecookie,
527 	standard_mouse_ioctl,
528 	standard_mouse_read,
529 	standard_mouse_write,
530 };
531