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
67const 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
74const 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.
83static inline status_t
84ps2_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".
91static void
92ps2_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 %" B_PRIdBIGTIME "\n",
148			xDelta, yDelta, buttons, cookie->click_count, currentTime);
149	}
150}
151
152
153//!	Read a mouse event from the mouse events chain buffer.
154static status_t
155standard_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
191void
192standard_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*/
207int32
208standard_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	cookie->buffer[cookie->packet_index++] = data;
231
232	if (cookie->packet_index != dev->packet_size) {
233		// packet not yet complete
234		return B_HANDLED_INTERRUPT;
235	}
236
237	// complete packet is assembled
238
239	cookie->packet_index = 0;
240	if (packet_buffer_write(cookie->standard_mouse_buffer,
241		cookie->buffer, dev->packet_size) != dev->packet_size) {
242		// buffer is full, drop new data
243		return B_HANDLED_INTERRUPT;
244	}
245
246	release_sem_etc(cookie->standard_mouse_sem, 1, B_DO_NOT_RESCHEDULE);
247
248	return B_INVOKE_SCHEDULER;
249}
250
251
252//	#pragma mark - probe_standard_mouse()
253
254
255status_t
256probe_standard_mouse(ps2_dev* dev)
257{
258	status_t status;
259	uint8 deviceId = 0;
260
261	// get device id
262	status = ps2_dev_command(dev, PS2_CMD_GET_DEVICE_ID, NULL, 0,
263		&deviceId, 1);
264	if (status != B_OK) {
265		INFO("ps2: probe_mouse get device id failed\n");
266		return B_ERROR;
267	}
268
269	TRACE("ps2: probe_mouse device id: %2x\n", deviceId);
270
271	// check for MS Intellimouse
272	if (deviceId == 0) {
273		uint8 alternate_device_id;
274		status  = ps2_set_sample_rate(dev, 200);
275		status |= ps2_set_sample_rate(dev, 100);
276		status |= ps2_set_sample_rate(dev, 80);
277		status |= ps2_dev_command(dev, PS2_CMD_GET_DEVICE_ID, NULL, 0,
278			&alternate_device_id, 1);
279		if (status == 0) {
280			TRACE("ps2: probe_mouse alternate device id: %2x\n",
281				alternate_device_id);
282			deviceId = alternate_device_id;
283		}
284	}
285
286	if (deviceId == PS2_DEV_ID_STANDARD
287		|| deviceId == PS2_DEV_ID_TOUCHPAD_RICATECH) {
288		INFO("ps2: probe_mouse Standard PS/2 mouse found\n");
289		dev->name = kStandardMousePath[dev->idx];
290		dev->packet_size = PS2_PACKET_STANDARD;
291	} else if (deviceId == PS2_DEV_ID_INTELLIMOUSE) {
292		dev->name = kIntelliMousePath[dev->idx];
293		dev->packet_size = PS2_PACKET_INTELLIMOUSE;
294		INFO("ps2: probe_mouse Extended PS/2 mouse found\n");
295	} else {
296		INFO("ps2: probe_mouse Error unknown device id.\n");
297		return B_ERROR;
298	}
299
300	return B_OK;
301}
302
303
304//	#pragma mark - Device functions
305
306
307status_t
308standard_mouse_open(const char* name, uint32 flags, void** _cookie)
309{
310	standard_mouse_cookie* cookie;
311	status_t status;
312	ps2_dev* dev = NULL;
313	int i;
314
315	TRACE("ps2: standard_mouse_open %s\n", name);
316
317	for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) {
318		if (0 == strcmp(ps2_device[i].name, name)) {
319			dev = &ps2_device[i];
320			break;
321		}
322#if 0
323		if (0 == strcmp(g_passthrough_dev.name, name)) {
324			dev = &g_passthrough_dev;
325			isSynapticsPTDevice = true;
326			break;
327		}
328#endif
329	}
330
331	if (dev == NULL) {
332		TRACE("ps2: dev = NULL\n");
333		return B_ERROR;
334	}
335
336	if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN)
337		return B_BUSY;
338
339	cookie = (standard_mouse_cookie*)malloc(sizeof(standard_mouse_cookie));
340	if (cookie == NULL)
341		goto err1;
342
343	*_cookie = cookie;
344	memset(cookie, 0, sizeof(*cookie));
345
346	cookie->dev = dev;
347	dev->cookie = cookie;
348	dev->disconnect = &standard_mouse_disconnect;
349	dev->handle_int = &standard_mouse_handle_int;
350
351	if (strstr(dev->name, "standard") != NULL)
352		cookie->flags = F_MOUSE_TYPE_STANDARD;
353
354	if (strstr(dev->name, "intelli") != NULL)
355		cookie->flags = F_MOUSE_TYPE_INTELLIMOUSE;
356
357	cookie->standard_mouse_buffer
358		= create_packet_buffer(MOUSE_HISTORY_SIZE * dev->packet_size);
359	if (cookie->standard_mouse_buffer == NULL) {
360		TRACE("ps2: can't allocate mouse actions buffer\n");
361		goto err2;
362	}
363
364	// create the mouse semaphore, used for synchronization between
365	// the interrupt handler and the read operation
366	cookie->standard_mouse_sem = create_sem(0, "ps2_standard_mouse_sem");
367	if (cookie->standard_mouse_sem < 0) {
368		TRACE("ps2: failed creating PS/2 mouse semaphore!\n");
369		goto err3;
370	}
371
372	status = ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0);
373	if (status < B_OK) {
374		INFO("ps2: cannot enable mouse %s\n", name);
375		goto err4;
376	}
377
378	atomic_or(&dev->flags, PS2_FLAG_ENABLED);
379
380
381	TRACE("ps2: standard_mouse_open %s success\n", name);
382	return B_OK;
383
384err4:
385	delete_sem(cookie->standard_mouse_sem);
386err3:
387	delete_packet_buffer(cookie->standard_mouse_buffer);
388err2:
389	free(cookie);
390err1:
391	atomic_and(&dev->flags, ~PS2_FLAG_OPEN);
392
393	TRACE("ps2: standard_mouse_open %s failed\n", name);
394	return B_ERROR;
395}
396
397
398status_t
399standard_mouse_close(void* _cookie)
400{
401	standard_mouse_cookie* cookie = (standard_mouse_cookie*)_cookie;
402
403	TRACE("ps2: standard_mouse_close %s enter\n", cookie->dev->name);
404
405	ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0,
406		150000);
407
408	delete_packet_buffer(cookie->standard_mouse_buffer);
409	delete_sem(cookie->standard_mouse_sem);
410
411	atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN);
412	atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED);
413
414	TRACE("ps2: standard_mouse_close %s done\n", cookie->dev->name);
415	return B_OK;
416}
417
418
419status_t
420standard_mouse_freecookie(void* _cookie)
421{
422	standard_mouse_cookie* cookie = (standard_mouse_cookie*)_cookie;
423	free(cookie);
424	return B_OK;
425}
426
427
428static status_t
429standard_mouse_read(void* cookie, off_t pos, void* buffer, size_t* _length)
430{
431	*_length = 0;
432	return B_NOT_ALLOWED;
433}
434
435
436static status_t
437standard_mouse_write(void* cookie, off_t pos, const void* buffer,
438	size_t* _length)
439{
440	*_length = 0;
441	return B_NOT_ALLOWED;
442}
443
444
445status_t
446standard_mouse_ioctl(void* _cookie, uint32 op, void* buffer, size_t length)
447{
448	standard_mouse_cookie* cookie = (standard_mouse_cookie*)_cookie;
449
450	switch (op) {
451		case MS_NUM_EVENTS:
452		{
453			int32 count;
454			TRACE("ps2: ioctl MS_NUM_EVENTS\n");
455			get_sem_count(cookie->standard_mouse_sem, &count);
456			return count;
457		}
458
459		case MS_READ:
460		{
461			mouse_movement movement;
462			status_t status;
463			TRACE("ps2: ioctl MS_READ\n");
464			if ((status = standard_mouse_read_event(cookie, &movement)) < B_OK)
465				return status;
466//			TRACE("%s %d %d %d %d\n", cookie->dev->name,
467//				movement.xdelta, movement.ydelta, movement.buttons,
468//				movement.clicks);
469			return user_memcpy(buffer, &movement, sizeof(movement));
470		}
471
472		case MS_SET_TYPE:
473			TRACE("ps2: ioctl MS_SET_TYPE not implemented\n");
474			return B_BAD_VALUE;
475
476		case MS_SET_MAP:
477			TRACE("ps2: ioctl MS_SET_MAP (set mouse mapping) not "
478				"implemented\n");
479			return B_BAD_VALUE;
480
481		case MS_GET_ACCEL:
482			TRACE("ps2: ioctl MS_GET_ACCEL (get mouse acceleration) not "
483				"implemented\n");
484			return B_BAD_VALUE;
485
486		case MS_SET_ACCEL:
487			TRACE("ps2: ioctl MS_SET_ACCEL (set mouse acceleration) not "
488				"implemented\n");
489			return B_BAD_VALUE;
490
491		case MS_SET_CLICKSPEED:
492			TRACE("ps2: ioctl MS_SETCLICK (set click speed)\n");
493			return user_memcpy(&cookie->click_speed, buffer, sizeof(bigtime_t));
494
495		default:
496			TRACE("ps2: ioctl unknown mouse opcode: %" B_PRIu32 "\n", op);
497			return B_DEV_INVALID_IOCTL;
498	}
499}
500
501
502device_hooks gStandardMouseDeviceHooks = {
503	standard_mouse_open,
504	standard_mouse_close,
505	standard_mouse_freecookie,
506	standard_mouse_ioctl,
507	standard_mouse_read,
508	standard_mouse_write,
509};
510