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