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