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