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