xref: /haiku/src/add-ons/kernel/bus_managers/ps2/ps2_synaptics.cpp (revision 2b76973fa2401f7a5edf68e6470f3d3210cbcff3)
1 /*
2  * Copyright 2008-2010, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors (in chronological order):
6  *		Clemens Zeidler (haiku@Clemens-Zeidler.de)
7  *		Axel Dörfler, axeld@pinc-software.de
8  */
9 
10 
11 //!	PS/2 synaptics touchpad
12 
13 
14 #include "ps2_synaptics.h"
15 
16 #include <string.h>
17 #include <stdlib.h>
18 
19 #include <keyboard_mouse_driver.h>
20 
21 #include "ps2_service.h"
22 
23 
24 // synaptics touchpad proportions
25 #define SYN_EDGE_MOTION_WIDTH	50
26 #define SYN_AREA_OFFSET			40
27 
28 #define MIN_PRESSURE			30
29 #define REAL_MAX_PRESSURE		100
30 #define MAX_PRESSURE			200
31 
32 
33 static hardware_specs gHardwareSpecs;
34 
35 
36 const char* kSynapticsPath[4] = {
37 	"input/touchpad/ps2/synaptics_0",
38 	"input/touchpad/ps2/synaptics_1",
39 	"input/touchpad/ps2/synaptics_2",
40 	"input/touchpad/ps2/synaptics_3"
41 };
42 
43 
44 static touchpad_info sTouchpadInfo;
45 static ps2_dev *sPassthroughDevice = &ps2_device[PS2_DEVICE_SYN_PASSTHROUGH];
46 
47 
48 static void
49 default_settings(touchpad_settings *set)
50 {
51 	memcpy(set, &kDefaultTouchpadSettings, sizeof(touchpad_settings));
52 }
53 
54 
55 static status_t
56 send_touchpad_arg_timeout(ps2_dev *dev, uint8 arg, bigtime_t timeout)
57 {
58 	int8 i;
59 	uint8 val[8];
60 	for (i = 0; i < 4; i++) {
61 		val[2 * i] = (arg >> (6 - 2 * i)) & 3;
62 		val[2 * i + 1] = 0xE8;
63 	}
64 	return ps2_dev_command_timeout(dev, 0xE8, val, 7, NULL, 0, timeout);
65 }
66 
67 
68 static status_t
69 send_touchpad_arg(ps2_dev *dev, uint8 arg)
70 {
71 	return send_touchpad_arg_timeout(dev, arg, 4000000);
72 }
73 
74 
75 static status_t
76 set_touchpad_mode(ps2_dev *dev, uint8 mode)
77 {
78 	uint8 sample_rate = SYN_CHANGE_MODE;
79 	send_touchpad_arg(dev, mode);
80 	return ps2_dev_command(dev, PS2_CMD_SET_SAMPLE_RATE, &sample_rate, 1,
81 		NULL, 0);
82 }
83 
84 
85 static status_t
86 get_synaptics_movment(synaptics_cookie *cookie, mouse_movement *movement)
87 {
88 	status_t status;
89 	touch_event event;
90 	uint8 event_buffer[PS2_MAX_PACKET_SIZE];
91 	uint8 wValue0, wValue1, wValue2, wValue3, wValue;
92 	uint32 val32;
93 	uint32 xTwelfBit, yTwelfBit;
94 
95 	status = acquire_sem_etc(cookie->synaptics_sem, 1, B_CAN_INTERRUPT, 0);
96 	if (status < B_OK)
97 		return status;
98 
99 	if (!cookie->dev->active) {
100 		TRACE("SYNAPTICS: read_event: Error device no longer active\n");
101 		return B_ERROR;
102 	}
103 
104 	if (packet_buffer_read(cookie->synaptics_ring_buffer, event_buffer,
105 			cookie->dev->packet_size) != cookie->dev->packet_size) {
106 		TRACE("SYNAPTICS: error copying buffer\n");
107 		return B_ERROR;
108 	}
109 
110 	event.buttons = event_buffer[0] & 3;
111  	event.zPressure = event_buffer[2];
112 
113  	if (sTouchpadInfo.capExtended) {
114  		wValue0 = event_buffer[3] >> 2 & 1;
115 	 	wValue1 = event_buffer[0] >> 2 & 1;
116  		wValue2 = event_buffer[0] >> 4 & 1;
117 	 	wValue3 = event_buffer[0] >> 5 & 1;
118 
119  		wValue = wValue0;
120  		wValue = wValue | (wValue1 << 1);
121  		wValue = wValue | (wValue2 << 2);
122  		wValue = wValue | (wValue3 << 3);
123 
124 	 	event.wValue = wValue;
125 	 	event.gesture = false;
126 
127 		if (sTouchpadInfo.capMiddleButton || sTouchpadInfo.capFourButtons)
128 			event.buttons |= ((event_buffer[0] ^ event_buffer[3]) & 0x01) << 2;
129  	} else {
130  		bool finger = event_buffer[0] >> 5 & 1;
131  		if (finger) {
132  			// finger with normal width
133  			event.wValue = 4;
134  		}
135  		event.gesture = event_buffer[0] >> 2 & 1;
136  	}
137 
138  	event.xPosition = event_buffer[4];
139  	event.yPosition = event_buffer[5];
140 
141  	val32 = event_buffer[1] & 0x0F;
142  	event.xPosition += val32 << 8;
143  	val32 = event_buffer[1] >> 4 & 0x0F;
144  	event.yPosition += val32 << 8;
145 
146  	xTwelfBit = event_buffer[3] >> 4 & 1;
147  	event.xPosition += xTwelfBit << 12;
148  	yTwelfBit = event_buffer[3] >> 5 & 1;
149  	event.yPosition += yTwelfBit << 12;
150 
151  	status = cookie->movementMaker.EventToMovement(&event, movement);
152 
153 	return status;
154 }
155 
156 
157 static void
158 query_capability(ps2_dev *dev)
159 {
160 	uint8 val[3];
161 	send_touchpad_arg(dev, 0x02);
162 	ps2_dev_command(dev, 0xE9, NULL, 0, val, 3);
163 
164 	sTouchpadInfo.capExtended = val[0] >> 7 & 1;
165 	TRACE("SYNAPTICS: extended mode %2x\n", val[0] >> 7 & 1);
166 	TRACE("SYNAPTICS: middle button %2x\n", val[0] >> 2 & 1);
167 	sTouchpadInfo.capMiddleButton = val[0] >> 2 & 1;
168 	TRACE("SYNAPTICS: sleep mode %2x\n", val[2] >> 4 & 1);
169 	sTouchpadInfo.capSleep = val[2] >> 4 & 1;
170 	TRACE("SYNAPTICS: four buttons %2x\n", val[2] >> 3 & 1);
171 	sTouchpadInfo.capFourButtons = val[2] >> 3 & 1;
172 	TRACE("SYNAPTICS: multi finger %2x\n", val[2] >> 1 & 1);
173 	sTouchpadInfo.capMultiFinger = val[2] >> 1 & 1;
174 	TRACE("SYNAPTICS: palm detection %2x\n", val[2] & 1);
175 	sTouchpadInfo.capPalmDetection = val[2] & 1;
176 	TRACE("SYNAPTICS: pass through %2x\n", val[2] >> 7 & 1);
177 	sTouchpadInfo.capPassThrough = val[2] >> 7 & 1;
178 }
179 
180 
181 //	#pragma mark - exported functions
182 
183 
184 status_t
185 synaptics_pass_through_set_packet_size(ps2_dev *dev, uint8 size)
186 {
187 	synaptics_cookie *synapticsCookie
188 		= (synaptics_cookie*)dev->parent_dev->cookie;
189 
190 	status_t status = ps2_dev_command(dev->parent_dev, PS2_CMD_DISABLE, NULL,
191 		0, NULL, 0);
192 	if (status < B_OK) {
193 		INFO("SYNAPTICS: cannot disable touchpad %s\n", dev->parent_dev->name);
194 		return B_ERROR;
195 	}
196 
197 	synapticsCookie->packet_index = 0;
198 
199 	if (size == 4)
200 		synapticsCookie->mode |= SYN_FOUR_BYTE_CHILD;
201 	else
202 		synapticsCookie->mode &= ~SYN_FOUR_BYTE_CHILD;
203 
204 	set_touchpad_mode(dev->parent_dev, synapticsCookie->mode);
205 
206 	status = ps2_dev_command(dev->parent_dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0);
207 	if (status < B_OK) {
208 		INFO("SYNAPTICS: cannot enable touchpad %s\n", dev->parent_dev->name);
209 		return B_ERROR;
210 	}
211 	return status;
212 }
213 
214 
215 status_t
216 passthrough_command(ps2_dev *dev, uint8 cmd, const uint8 *out, int outCount,
217 	uint8 *in, int inCount, bigtime_t timeout)
218 {
219 	status_t status;
220 	uint8 passThroughCmd = SYN_PASSTHROUGH_CMD;
221 	uint8 val;
222 	uint32 passThroughInCount = (inCount + 1) * 6;
223 	uint8 passThroughIn[passThroughInCount];
224 	int8 i;
225 
226 	TRACE("SYNAPTICS: passthrough command 0x%x\n", cmd);
227 
228 	status = ps2_dev_command(dev->parent_dev, PS2_CMD_DISABLE, NULL, 0,
229 		NULL, 0);
230 	if (status != B_OK)
231 		return status;
232 
233 	for (i = -1; i < outCount; i++) {
234 		if (i == -1)
235 			val = cmd;
236 		else
237 			val = out[i];
238 		status = send_touchpad_arg_timeout(dev->parent_dev, val, timeout);
239 		if (status != B_OK)
240 			goto finalize;
241 		if (i != outCount -1) {
242 			status = ps2_dev_command_timeout(dev->parent_dev,
243 				PS2_CMD_SET_SAMPLE_RATE, &passThroughCmd, 1, NULL, 0, timeout);
244 			if (status != B_OK)
245 				goto finalize;
246 		}
247 	}
248 	status = ps2_dev_command_timeout(dev->parent_dev, PS2_CMD_SET_SAMPLE_RATE,
249 		&passThroughCmd, 1, passThroughIn, passThroughInCount, timeout);
250 	if (status != B_OK)
251 		goto finalize;
252 
253 	for (i = 0; i < inCount + 1; i++) {
254 		uint8 *inPointer = &passThroughIn[i * 6];
255 		if (!IS_SYN_PT_PACKAGE(inPointer)) {
256 			TRACE("SYNAPTICS: not a pass throught package\n");
257 			goto finalize;
258 		}
259 		if (i == 0)
260 			continue;
261 
262 		in[i - 1] = passThroughIn[i * 6 + 1];
263 	}
264 
265 finalize:
266 	status_t statusOfEnable = ps2_dev_command(dev->parent_dev, PS2_CMD_ENABLE,
267 			NULL, 0, NULL, 0);
268 	if (statusOfEnable != B_OK)
269 		TRACE("SYNAPTICS: enabling of parent failed: 0x%lx.\n", statusOfEnable);
270 
271 	return status != B_OK ? status : statusOfEnable;
272 }
273 
274 
275 status_t
276 probe_synaptics(ps2_dev *dev)
277 {
278 	uint8 val[3];
279 	uint8 deviceId;
280 	status_t status;
281 	TRACE("SYNAPTICS: probe\n");
282 
283 	status = send_touchpad_arg(dev, 0x00);
284 	if (status != B_OK)
285 		return status;
286 	status = ps2_dev_command(dev, 0xE9, NULL, 0, val, 3);
287 	if (status != B_OK)
288 		return status;
289 
290 	sTouchpadInfo.minorVersion = val[0];
291 	deviceId = val[1];
292 	if (deviceId != SYN_TOUCHPAD) {
293 		TRACE("SYNAPTICS: not found\n");
294 		return B_ERROR;
295 	}
296 
297 	TRACE("SYNAPTICS: Touchpad found id:l %2x\n", deviceId);
298 	sTouchpadInfo.majorVersion = val[2] & 0x0F;
299 	TRACE("SYNAPTICS: version %d.%d\n", sTouchpadInfo.majorVersion,
300 		sTouchpadInfo.minorVersion);
301 	// version >= 4.0?
302 	if (sTouchpadInfo.minorVersion <= 2
303 		&& sTouchpadInfo.majorVersion <= 3) {
304 		TRACE("SYNAPTICS: too old touchpad not supported\n");
305 		return B_ERROR;
306 	}
307 	dev->name = kSynapticsPath[dev->idx];
308 	return B_OK;
309 }
310 
311 
312 //	#pragma mark - Device functions
313 
314 
315 status_t
316 synaptics_open(const char *name, uint32 flags, void **_cookie)
317 {
318 	status_t status;
319 	synaptics_cookie *cookie;
320 	ps2_dev *dev;
321 	int i;
322 
323 	for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) {
324 		if (0 == strcmp(ps2_device[i].name, name)) {
325 			dev = &ps2_device[i];
326 			break;
327 		}
328 	}
329 
330 	if (dev == NULL) {
331 		TRACE("ps2: dev = NULL\n");
332 		return B_ERROR;
333 	}
334 
335 	if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN)
336 		return B_BUSY;
337 
338 	cookie = (synaptics_cookie*)malloc(sizeof(synaptics_cookie));
339 	if (cookie == NULL)
340 		goto err1;
341 	memset(cookie, 0, sizeof(*cookie));
342 
343 	cookie->movementMaker.Init();
344 	*_cookie = cookie;
345 
346 	cookie->dev = dev;
347 	dev->cookie = cookie;
348 	dev->disconnect = &synaptics_disconnect;
349 	dev->handle_int = &synaptics_handle_int;
350 
351 	default_settings(&cookie->settings);
352 
353 	gHardwareSpecs.edgeMotionWidth = SYN_EDGE_MOTION_WIDTH;
354 
355 	gHardwareSpecs.areaStartX = SYN_AREA_START_X;
356 	gHardwareSpecs.areaEndX = SYN_AREA_END_X;
357 	gHardwareSpecs.areaStartY = SYN_AREA_START_Y;
358 	gHardwareSpecs.areaEndY = SYN_AREA_END_Y;
359 
360 	gHardwareSpecs.minPressure = MIN_PRESSURE;
361 	gHardwareSpecs.realMaxPressure = REAL_MAX_PRESSURE;
362 	gHardwareSpecs.maxPressure = MAX_PRESSURE;
363 
364 	cookie->movementMaker.SetSettings(&cookie->settings);
365 	cookie->movementMaker.SetSpecs(&gHardwareSpecs);
366 
367 	dev->packet_size = PS2_PACKET_SYNAPTICS;
368 
369 	cookie->synaptics_ring_buffer
370 		= create_packet_buffer(SYNAPTICS_HISTORY_SIZE * dev->packet_size);
371 	if (cookie->synaptics_ring_buffer == NULL) {
372 		TRACE("ps2: can't allocate mouse actions buffer\n");
373 		goto err2;
374 	}
375 
376 	// create the mouse semaphore, used for synchronization between
377 	// the interrupt handler and the read operation
378 	cookie->synaptics_sem = create_sem(0, "ps2_synaptics_sem");
379 	if (cookie->synaptics_sem < 0) {
380 		TRACE("SYNAPTICS: failed creating semaphore!\n");
381 		goto err3;
382 	}
383 	query_capability(dev);
384 
385 	// create pass through dev
386 	if (sTouchpadInfo.capPassThrough) {
387 		TRACE("SYNAPTICS: pass through detected\n");
388 		sPassthroughDevice->parent_dev = dev;
389 		sPassthroughDevice->idx = dev->idx;
390 		ps2_service_notify_device_added(sPassthroughDevice);
391 	}
392 
393 	// Set Mode
394 	if (sTouchpadInfo.capExtended)
395 		cookie->mode = SYN_ABSOLUTE_W_MODE;
396 	else
397 		cookie->mode = SYN_ABSOLUTE_MODE;
398 
399 	status = set_touchpad_mode(dev, cookie->mode);
400 	if (status < B_OK) {
401 		INFO("SYNAPTICS: cannot set mode %s\n", name);
402 		goto err4;
403 	}
404 
405 	status = ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0);
406 	if (status < B_OK) {
407 		INFO("SYNAPTICS: cannot enable touchpad %s\n", name);
408 		goto err4;
409 	}
410 
411 	atomic_or(&dev->flags, PS2_FLAG_ENABLED);
412 
413 	TRACE("SYNAPTICS: open %s success\n", name);
414 	return B_OK;
415 
416 err4:
417 	delete_sem(cookie->synaptics_sem);
418 err3:
419 	delete_packet_buffer(cookie->synaptics_ring_buffer);
420 err2:
421 	free(cookie);
422 err1:
423 	atomic_and(&dev->flags, ~PS2_FLAG_OPEN);
424 
425 	TRACE("SYNAPTICS: synaptics_open %s failed\n", name);
426 	return B_ERROR;
427 }
428 
429 
430 status_t
431 synaptics_close(void *_cookie)
432 {
433 	status_t status;
434 	synaptics_cookie *cookie = (synaptics_cookie*)_cookie;
435 
436 	ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0,
437 		150000);
438 
439 	delete_packet_buffer(cookie->synaptics_ring_buffer);
440 	delete_sem(cookie->synaptics_sem);
441 
442 	atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN);
443 	atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED);
444 
445 	// Reset the touchpad so it generate standard ps2 packets instead of
446 	// extended ones. If not, BeOS is confused with such packets when rebooting
447 	// without a complete shutdown.
448 	status = ps2_reset_mouse(cookie->dev);
449 	if (status != B_OK) {
450 		INFO("ps2: reset failed\n");
451 		return B_ERROR;
452 	}
453 
454 	if (sTouchpadInfo.capPassThrough)
455 		ps2_service_notify_device_removed(sPassthroughDevice);
456 
457 	TRACE("SYNAPTICS: close %s done\n", cookie->dev->name);
458 	return B_OK;
459 }
460 
461 
462 status_t
463 synaptics_freecookie(void *_cookie)
464 {
465 	free(_cookie);
466 	return B_OK;
467 }
468 
469 
470 static status_t
471 synaptics_read(void *cookie, off_t pos, void *buffer, size_t *_length)
472 {
473 	*_length = 0;
474 	return B_NOT_ALLOWED;
475 }
476 
477 
478 static status_t
479 synaptics_write(void *cookie, off_t pos, const void *buffer, size_t *_length)
480 {
481 	*_length = 0;
482 	return B_NOT_ALLOWED;
483 }
484 
485 
486 status_t
487 synaptics_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
488 {
489 	synaptics_cookie *cookie = (synaptics_cookie*)_cookie;
490 	mouse_movement movement;
491 	status_t status;
492 
493 	switch (op) {
494 		case MS_READ:
495 			TRACE("SYNAPTICS: MS_READ get event\n");
496 			if ((status = get_synaptics_movment(cookie, &movement)) != B_OK)
497 				return status;
498 			return user_memcpy(buffer, &movement, sizeof(movement));
499 
500 		case MS_IS_TOUCHPAD:
501 			TRACE("SYNAPTICS: MS_IS_TOUCHPAD\n");
502 			return B_OK;
503 
504 		case MS_SET_TOUCHPAD_SETTINGS:
505 			TRACE("SYNAPTICS: MS_SET_TOUCHPAD_SETTINGS");
506 			user_memcpy(&cookie->settings, buffer, sizeof(touchpad_settings));
507 			return B_OK;
508 
509 		case MS_SET_CLICKSPEED:
510 			TRACE("SYNAPTICS: ioctl MS_SETCLICK (set click speed)\n");
511 			return user_memcpy(&cookie->movementMaker.click_speed, buffer,
512 				sizeof(bigtime_t));
513 
514 		default:
515 			TRACE("SYNAPTICS: unknown opcode: %ld\n", op);
516 			return B_DEV_INVALID_IOCTL;
517 	}
518 }
519 
520 
521 int32
522 synaptics_handle_int(ps2_dev *dev)
523 {
524 	synaptics_cookie *cookie = (synaptics_cookie*)dev->cookie;
525 	uint8 val;
526 
527 	val = cookie->dev->history[0].data;
528 
529 	if ((cookie->packet_index == 0 || cookie->packet_index == 3)
530 		&& (val & 8) != 0) {
531 		INFO("SYNAPTICS: bad mouse data, trying resync\n");
532 		cookie->packet_index = 0;
533 		return B_UNHANDLED_INTERRUPT;
534 	}
535 	if (cookie->packet_index == 0 && val >> 6 != 0x02) {
536 	 	TRACE("SYNAPTICS: first package begins not with bit 1, 0\n");
537 		return B_UNHANDLED_INTERRUPT;
538  	}
539  	if (cookie->packet_index == 3 && val >> 6 != 0x03) {
540 	 	TRACE("SYNAPTICS: third package begins not with bit 1, 1\n");
541 	 	cookie->packet_index = 0;
542 		return B_UNHANDLED_INTERRUPT;
543  	}
544  	cookie->buffer[cookie->packet_index] = val;
545 
546 	cookie->packet_index++;
547 	if (cookie->packet_index >= 6) {
548 		cookie->packet_index = 0;
549 
550 		// check if package is a pass through package if true pass it
551 		// too the pass through interrupt handle
552 		if (sPassthroughDevice->active
553 			&& sPassthroughDevice->handle_int != NULL
554 			&& IS_SYN_PT_PACKAGE(cookie->buffer)) {
555 			status_t status;
556 
557 			sPassthroughDevice->history[0].data = cookie->buffer[1];
558 			sPassthroughDevice->handle_int(sPassthroughDevice);
559 			sPassthroughDevice->history[0].data = cookie->buffer[4];
560 			sPassthroughDevice->handle_int(sPassthroughDevice);
561 			sPassthroughDevice->history[0].data = cookie->buffer[5];
562 			status = sPassthroughDevice->handle_int(sPassthroughDevice);
563 
564 			if (cookie->dev->packet_size == 4) {
565 				sPassthroughDevice->history[0].data = cookie->buffer[2];
566 				status = sPassthroughDevice->handle_int(sPassthroughDevice);
567 			}
568 			return status;
569 		}
570 
571 		if (packet_buffer_write(cookie->synaptics_ring_buffer,
572 				cookie->buffer, cookie->dev->packet_size)
573 			!= cookie->dev->packet_size) {
574 			// buffer is full, drop new data
575 			return B_HANDLED_INTERRUPT;
576 		}
577 		release_sem_etc(cookie->synaptics_sem, 1, B_DO_NOT_RESCHEDULE);
578 
579 		return B_INVOKE_SCHEDULER;
580 	}
581 
582 	return B_HANDLED_INTERRUPT;
583 }
584 
585 
586 void
587 synaptics_disconnect(ps2_dev *dev)
588 {
589 	synaptics_cookie *cookie = (synaptics_cookie*)dev->cookie;
590 	// the mouse device might not be opened at this point
591 	INFO("SYNAPTICS: synaptics_disconnect %s\n", dev->name);
592 	if ((dev->flags & PS2_FLAG_OPEN) != 0)
593 		release_sem(cookie->synaptics_sem);
594 }
595 
596 
597 device_hooks gSynapticsDeviceHooks = {
598 	synaptics_open,
599 	synaptics_close,
600 	synaptics_freecookie,
601 	synaptics_ioctl,
602 	synaptics_read,
603 	synaptics_write,
604 };
605