xref: /haiku/src/add-ons/kernel/bus_managers/ps2/ps2_synaptics.cpp (revision f60531661beee9c2bcde5df0e6b821dfc0bdfc24)
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 enum {
33 	kIdentify = 0x00,
34 	kReadModes = 0x01,
35 	kReadCapabilities = 0x02,
36 	kReadModelId = 0x03,
37 	kReadSerialNumberPrefix = 0x06,
38 	kReadSerialModelSuffix = 0x07,
39 	kReadResolutions = 0x08,
40 	kExtendedModelId = 0x09,
41 	kContinuedCapabilities = 0x0C,
42 	kMaximumCoordinates = 0x0D,
43 	kDeluxeLedInfo = 0x0E,
44 	kMinimumCoordinates = 0x0F,
45 	kTrackpointQuirk = 0x10
46 };
47 
48 static hardware_specs gHardwareSpecs;
49 
50 
51 const char* kSynapticsPath[4] = {
52 	"input/touchpad/ps2/synaptics_0",
53 	"input/touchpad/ps2/synaptics_1",
54 	"input/touchpad/ps2/synaptics_2",
55 	"input/touchpad/ps2/synaptics_3"
56 };
57 
58 
59 static touchpad_info sTouchpadInfo;
60 static ps2_dev *sPassthroughDevice = &ps2_device[PS2_DEVICE_SYN_PASSTHROUGH];
61 
62 
63 static void
64 default_settings(touchpad_settings *set)
65 {
66 	memcpy(set, &kDefaultTouchpadSettings, sizeof(touchpad_settings));
67 }
68 
69 
70 static status_t
71 send_touchpad_arg_timeout(ps2_dev *dev, uint8 arg, bigtime_t timeout)
72 {
73 	int8 i;
74 	uint8 val[8];
75 
76 	for (i = 0; i < 4; i++) {
77 		val[2 * i] = (arg >> (6 - 2 * i)) & 3;
78 		val[2 * i + 1] = 0xE8;
79 	}
80 	return ps2_dev_command_timeout(dev, 0xE8, val, 7, NULL, 0, timeout);
81 }
82 
83 
84 static status_t
85 send_touchpad_arg(ps2_dev *dev, uint8 arg)
86 {
87 	return send_touchpad_arg_timeout(dev, arg, 4000000);
88 }
89 
90 
91 static status_t
92 set_touchpad_mode(ps2_dev *dev, uint8 mode)
93 {
94 	uint8 sample_rate = SYN_CHANGE_MODE;
95 	send_touchpad_arg(dev, mode);
96 	return ps2_dev_command(dev, PS2_CMD_SET_SAMPLE_RATE, &sample_rate, 1,
97 		NULL, 0);
98 }
99 
100 
101 static status_t
102 get_information_query(ps2_dev *dev, uint8 extendedQueries, uint8 query,
103 	uint8 val[3])
104 {
105 	if (query == kTrackpointQuirk) {
106 		// Special case: this information query is not reported in the
107 		// "extended queries", but is still supported when the touchpad has
108 		// a pass-through port.
109 		if (!sTouchpadInfo.capPassThrough)
110 			return B_NOT_SUPPORTED;
111 	} else if (query > extendedQueries + 8)
112 		return B_NOT_SUPPORTED;
113 
114 	status_t error = send_touchpad_arg(dev, query);
115 	if (error != B_OK)
116 		return error;
117 	return ps2_dev_command(dev, 0xE9, NULL, 0, val, 3);
118 }
119 
120 
121 static status_t
122 get_synaptics_movment(synaptics_cookie *cookie, mouse_movement *movement)
123 {
124 	status_t status;
125 	touch_event event;
126 	uint8 event_buffer[PS2_MAX_PACKET_SIZE];
127 	uint8 wValue0, wValue1, wValue2, wValue3, wValue;
128 	uint32 val32;
129 	uint32 xTwelfBit, yTwelfBit;
130 
131 	status = acquire_sem_etc(cookie->synaptics_sem, 1, B_CAN_INTERRUPT, 0);
132 	if (status < B_OK)
133 		return status;
134 
135 	if (!cookie->dev->active) {
136 		TRACE("SYNAPTICS: read_event: Error device no longer active\n");
137 		return B_ERROR;
138 	}
139 
140 	if (packet_buffer_read(cookie->synaptics_ring_buffer, event_buffer,
141 			cookie->dev->packet_size) != cookie->dev->packet_size) {
142 		TRACE("SYNAPTICS: error copying buffer\n");
143 		return B_ERROR;
144 	}
145 
146 	event.buttons = event_buffer[0] & 3;
147  	event.zPressure = event_buffer[2];
148 
149  	if (sTouchpadInfo.capExtended) {
150  		wValue0 = event_buffer[3] >> 2 & 1;
151 	 	wValue1 = event_buffer[0] >> 2 & 1;
152  		wValue2 = event_buffer[0] >> 4 & 1;
153 	 	wValue3 = event_buffer[0] >> 5 & 1;
154 
155  		wValue = wValue0;
156  		wValue = wValue | (wValue1 << 1);
157  		wValue = wValue | (wValue2 << 2);
158  		wValue = wValue | (wValue3 << 3);
159 
160 	 	event.wValue = wValue;
161 	 	event.gesture = false;
162 
163 		// Clickpad pretends that all clicks on the touchpad are middle clicks.
164 		// Pass them to userspace as left clicks instead.
165 		if (sTouchpadInfo.capClickPad)
166 			event.buttons |= ((event_buffer[0] ^ event_buffer[3]) & 0x01);
167 
168 		if (sTouchpadInfo.capMiddleButton || sTouchpadInfo.capFourButtons)
169 			event.buttons |= ((event_buffer[0] ^ event_buffer[3]) & 0x01) << 2;
170 
171 		if (sTouchpadInfo.nExtendedButtons > 0) {
172 			if (((event_buffer[0] ^ event_buffer[3]) & 0x02) != 0) {
173 				// This packet includes extended buttons state. The state is
174 				// only reported once when one of the buttons is pressed or
175 				// released, so we must keep track of the buttons state.
176 
177 				// The values replace the lowest bits of the X and Y coordinates
178 				// in the packet, we need to extract them from there.
179 
180 				bool pressed;
181 				for (int button = 0; button < sTouchpadInfo.nExtendedButtons;
182 						button++) {
183 					// Even buttons are in the X byte
184 					pressed = event_buffer[4 + button % 2] >> button / 2 & 0x1;
185 					if (pressed) {
186 						sTouchpadInfo.extendedButtonsState |= 1 << button;
187 					} else {
188 						sTouchpadInfo.extendedButtonsState &= ~(1 << button);
189 					}
190 				}
191 			}
192 
193 			event.buttons |= sTouchpadInfo.extendedButtonsState
194 				<< sTouchpadInfo.firstExtendedButton;
195 		}
196  	} else {
197  		bool finger = event_buffer[0] >> 5 & 1;
198  		if (finger) {
199  			// finger with normal width
200  			event.wValue = 4;
201  		}
202  		event.gesture = event_buffer[0] >> 2 & 1;
203  	}
204 
205  	event.xPosition = event_buffer[4];
206  	event.yPosition = event_buffer[5];
207 
208  	val32 = event_buffer[1] & 0x0F;
209  	event.xPosition += val32 << 8;
210  	val32 = event_buffer[1] >> 4 & 0x0F;
211  	event.yPosition += val32 << 8;
212 
213  	xTwelfBit = event_buffer[3] >> 4 & 1;
214  	event.xPosition += xTwelfBit << 12;
215  	yTwelfBit = event_buffer[3] >> 5 & 1;
216  	event.yPosition += yTwelfBit << 12;
217 
218  	status = cookie->movementMaker.EventToMovement(&event, movement);
219 
220 	return status;
221 }
222 
223 
224 static void
225 query_capability(ps2_dev *dev)
226 {
227 	uint8 val[3];
228 	uint8 nExtendedQueries = 0;
229 
230 	get_information_query(dev, nExtendedQueries, kReadCapabilities, val);
231 
232 	TRACE("SYNAPTICS: extended mode %2x\n", val[0] >> 7 & 1);
233 	sTouchpadInfo.capExtended = val[0] >> 7 & 1;
234 	TRACE("SYNAPTICS: extended queries %2x\n", val[0] >> 4 & 7);
235 	nExtendedQueries = val[0] >> 4 & 7;
236 	TRACE("SYNAPTICS: middle button %2x\n", val[0] >> 2 & 1);
237 	sTouchpadInfo.capMiddleButton = val[0] >> 2 & 1;
238 
239 	TRACE("SYNAPTICS: sleep mode %2x\n", val[2] >> 4 & 1);
240 	sTouchpadInfo.capSleep = val[2] >> 4 & 1;
241 	TRACE("SYNAPTICS: four buttons %2x\n", val[2] >> 3 & 1);
242 	sTouchpadInfo.capFourButtons = val[2] >> 3 & 1;
243 	TRACE("SYNAPTICS: multi finger %2x\n", val[2] >> 1 & 1);
244 	sTouchpadInfo.capMultiFinger = val[2] >> 1 & 1;
245 	TRACE("SYNAPTICS: palm detection %2x\n", val[2] & 1);
246 	sTouchpadInfo.capPalmDetection = val[2] & 1;
247 	TRACE("SYNAPTICS: pass through %2x\n", val[2] >> 7 & 1);
248 	sTouchpadInfo.capPassThrough = val[2] >> 7 & 1;
249 
250 	if (get_information_query(dev, nExtendedQueries, kExtendedModelId, val)
251 			!= B_OK) {
252 		// "Extended Model ID" is not supported, so there cannot be extra
253 		// buttons.
254 		sTouchpadInfo.nExtendedButtons = 0;
255 		sTouchpadInfo.firstExtendedButton = 0;
256 		sTouchpadInfo.capClickPad = false;
257 		return;
258 	}
259 
260 	sTouchpadInfo.capClickPad = (val[0] >> 5 & 1) | (val[1] >> 0 & 1);
261 	TRACE("SYNAPTICS: clickpad %x\n", sTouchpadInfo.capClickPad);
262 
263 	TRACE("SYNAPTICS: extended buttons %2x\n", val[1] >> 4 & 15);
264 	sTouchpadInfo.nExtendedButtons = val[1] >> 4 & 15;
265 	sTouchpadInfo.extendedButtonsState = 0;
266 
267 	if (sTouchpadInfo.capMiddleButton)
268 		sTouchpadInfo.firstExtendedButton = 3;
269 	else
270 		sTouchpadInfo.firstExtendedButton = 2;
271 
272 	// Capability 0x10 is not documented in the Synaptics Touchpad interfacing
273 	// guide (at least the versions I could find), but we got the information
274 	// from Linux patches: https://lkml.org/lkml/2015/2/6/621
275 	if (get_information_query(dev, nExtendedQueries, kTrackpointQuirk, val)
276 			!= B_OK)
277 		return;
278 
279 	// Workaround for Thinkpad use of the extended buttons: they are
280 	// used as buttons for the trackpoint, so they should be reported
281 	// as buttons 0, 1, 2 rather than 3, 4, 5.
282 	TRACE("SYNAPTICS: alternate buttons %2x\n", val[0] >> 0 & 1);
283 	if (val[0] >> 0 & 1)
284 		sTouchpadInfo.firstExtendedButton = 0;
285 }
286 
287 
288 //	#pragma mark - exported functions
289 
290 
291 status_t
292 synaptics_pass_through_set_packet_size(ps2_dev *dev, uint8 size)
293 {
294 	synaptics_cookie *synapticsCookie
295 		= (synaptics_cookie*)dev->parent_dev->cookie;
296 
297 	status_t status = ps2_dev_command(dev->parent_dev, PS2_CMD_DISABLE, NULL,
298 		0, NULL, 0);
299 	if (status < B_OK) {
300 		INFO("SYNAPTICS: cannot disable touchpad %s\n", dev->parent_dev->name);
301 		return B_ERROR;
302 	}
303 
304 	synapticsCookie->packet_index = 0;
305 
306 	if (size == 4)
307 		synapticsCookie->mode |= SYN_FOUR_BYTE_CHILD;
308 	else
309 		synapticsCookie->mode &= ~SYN_FOUR_BYTE_CHILD;
310 
311 	set_touchpad_mode(dev->parent_dev, synapticsCookie->mode);
312 
313 	status = ps2_dev_command(dev->parent_dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0);
314 	if (status < B_OK) {
315 		INFO("SYNAPTICS: cannot enable touchpad %s\n", dev->parent_dev->name);
316 		return B_ERROR;
317 	}
318 	return status;
319 }
320 
321 
322 status_t
323 passthrough_command(ps2_dev *dev, uint8 cmd, const uint8 *out, int outCount,
324 	uint8 *in, int inCount, bigtime_t timeout)
325 {
326 	status_t status;
327 	uint8 passThroughCmd = SYN_PASSTHROUGH_CMD;
328 	uint8 val;
329 	uint32 passThroughInCount = (inCount + 1) * 6;
330 	uint8 passThroughIn[passThroughInCount];
331 	int8 i;
332 
333 	TRACE("SYNAPTICS: passthrough command 0x%x\n", cmd);
334 
335 	status = ps2_dev_command(dev->parent_dev, PS2_CMD_DISABLE, NULL, 0,
336 		NULL, 0);
337 	if (status != B_OK)
338 		return status;
339 
340 	for (i = -1; i < outCount; i++) {
341 		if (i == -1)
342 			val = cmd;
343 		else
344 			val = out[i];
345 		status = send_touchpad_arg_timeout(dev->parent_dev, val, timeout);
346 		if (status != B_OK)
347 			goto finalize;
348 		if (i != outCount -1) {
349 			status = ps2_dev_command_timeout(dev->parent_dev,
350 				PS2_CMD_SET_SAMPLE_RATE, &passThroughCmd, 1, NULL, 0, timeout);
351 			if (status != B_OK)
352 				goto finalize;
353 		}
354 	}
355 	status = ps2_dev_command_timeout(dev->parent_dev, PS2_CMD_SET_SAMPLE_RATE,
356 		&passThroughCmd, 1, passThroughIn, passThroughInCount, timeout);
357 	if (status != B_OK)
358 		goto finalize;
359 
360 	for (i = 0; i < inCount + 1; i++) {
361 		uint8 *inPointer = &passThroughIn[i * 6];
362 		if (!IS_SYN_PT_PACKAGE(inPointer)) {
363 			TRACE("SYNAPTICS: not a pass throught package\n");
364 			goto finalize;
365 		}
366 		if (i == 0)
367 			continue;
368 
369 		in[i - 1] = passThroughIn[i * 6 + 1];
370 	}
371 
372 finalize:
373 	status_t statusOfEnable = ps2_dev_command(dev->parent_dev, PS2_CMD_ENABLE,
374 			NULL, 0, NULL, 0);
375 	if (statusOfEnable != B_OK) {
376 		TRACE("SYNAPTICS: enabling of parent failed: 0x%" B_PRIx32 ".\n",
377 			statusOfEnable);
378 	}
379 
380 	return status != B_OK ? status : statusOfEnable;
381 }
382 
383 
384 status_t
385 probe_synaptics(ps2_dev *dev)
386 {
387 	uint8 val[3];
388 	uint8 deviceId;
389 	status_t status;
390 	TRACE("SYNAPTICS: probe\n");
391 
392 	// We reset the device here because it may have been left in a confused
393 	// state by a previous probing attempt. Some synaptics touchpads are known
394 	// to lockup when we attempt to detect them as IBM trackpoints.
395 	ps2_reset_mouse(dev);
396 
397 	// Request "Identify touchpad"
398 	// The touchpad will delay this, until it's ready and calibrated.
399 	status = get_information_query(dev, 0, kIdentify, val);
400 	if (status != B_OK)
401 		return status;
402 
403 	sTouchpadInfo.minorVersion = val[0];
404 	deviceId = val[1];
405 	if (deviceId != SYN_TOUCHPAD) {
406 		TRACE("SYNAPTICS: not found\n");
407 		return B_ERROR;
408 	}
409 
410 	TRACE("SYNAPTICS: Touchpad found id:l %2x\n", deviceId);
411 	sTouchpadInfo.majorVersion = val[2] & 0x0F;
412 	TRACE("SYNAPTICS: version %d.%d\n", sTouchpadInfo.majorVersion,
413 		sTouchpadInfo.minorVersion);
414 	// version >= 4.0?
415 	if (sTouchpadInfo.minorVersion <= 2
416 		&& sTouchpadInfo.majorVersion <= 3) {
417 		TRACE("SYNAPTICS: too old touchpad not supported\n");
418 		return B_ERROR;
419 	}
420 	dev->name = kSynapticsPath[dev->idx];
421 	return B_OK;
422 }
423 
424 
425 //	#pragma mark - Device functions
426 
427 
428 status_t
429 synaptics_open(const char *name, uint32 flags, void **_cookie)
430 {
431 	status_t status;
432 	synaptics_cookie *cookie;
433 	ps2_dev *dev;
434 	int i;
435 
436 	for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) {
437 		if (0 == strcmp(ps2_device[i].name, name)) {
438 			dev = &ps2_device[i];
439 			break;
440 		}
441 	}
442 
443 	if (dev == NULL) {
444 		TRACE("ps2: dev = NULL\n");
445 		return B_ERROR;
446 	}
447 
448 	if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN)
449 		return B_BUSY;
450 
451 	cookie = (synaptics_cookie*)malloc(sizeof(synaptics_cookie));
452 	if (cookie == NULL)
453 		goto err1;
454 	memset(cookie, 0, sizeof(*cookie));
455 
456 	cookie->movementMaker.Init();
457 	*_cookie = cookie;
458 
459 	cookie->dev = dev;
460 	dev->cookie = cookie;
461 	dev->disconnect = &synaptics_disconnect;
462 	dev->handle_int = &synaptics_handle_int;
463 
464 	default_settings(&cookie->settings);
465 
466 	gHardwareSpecs.edgeMotionWidth = SYN_EDGE_MOTION_WIDTH;
467 
468 	gHardwareSpecs.areaStartX = SYN_AREA_START_X;
469 	gHardwareSpecs.areaEndX = SYN_AREA_END_X;
470 	gHardwareSpecs.areaStartY = SYN_AREA_START_Y;
471 	gHardwareSpecs.areaEndY = SYN_AREA_END_Y;
472 
473 	gHardwareSpecs.minPressure = MIN_PRESSURE;
474 	gHardwareSpecs.realMaxPressure = REAL_MAX_PRESSURE;
475 	gHardwareSpecs.maxPressure = MAX_PRESSURE;
476 
477 	cookie->movementMaker.SetSettings(&cookie->settings);
478 	cookie->movementMaker.SetSpecs(&gHardwareSpecs);
479 
480 	dev->packet_size = PS2_PACKET_SYNAPTICS;
481 
482 	cookie->synaptics_ring_buffer
483 		= create_packet_buffer(SYNAPTICS_HISTORY_SIZE * dev->packet_size);
484 	if (cookie->synaptics_ring_buffer == NULL) {
485 		TRACE("ps2: can't allocate mouse actions buffer\n");
486 		goto err2;
487 	}
488 
489 	// create the mouse semaphore, used for synchronization between
490 	// the interrupt handler and the read operation
491 	cookie->synaptics_sem = create_sem(0, "ps2_synaptics_sem");
492 	if (cookie->synaptics_sem < 0) {
493 		TRACE("SYNAPTICS: failed creating semaphore!\n");
494 		goto err3;
495 	}
496 	query_capability(dev);
497 
498 	// create pass through dev
499 	if (sTouchpadInfo.capPassThrough) {
500 		TRACE("SYNAPTICS: pass through detected\n");
501 		sPassthroughDevice->parent_dev = dev;
502 		sPassthroughDevice->idx = dev->idx;
503 		ps2_service_notify_device_added(sPassthroughDevice);
504 	}
505 
506 	// Set Mode
507 	if (sTouchpadInfo.capExtended)
508 		cookie->mode = SYN_ABSOLUTE_W_MODE;
509 	else
510 		cookie->mode = SYN_ABSOLUTE_MODE;
511 
512 	status = set_touchpad_mode(dev, cookie->mode);
513 	if (status < B_OK) {
514 		INFO("SYNAPTICS: cannot set mode %s\n", name);
515 		goto err4;
516 	}
517 
518 	status = ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0);
519 	if (status < B_OK) {
520 		INFO("SYNAPTICS: cannot enable touchpad %s\n", name);
521 		goto err4;
522 	}
523 
524 	atomic_or(&dev->flags, PS2_FLAG_ENABLED);
525 
526 	TRACE("SYNAPTICS: open %s success\n", name);
527 	return B_OK;
528 
529 err4:
530 	delete_sem(cookie->synaptics_sem);
531 err3:
532 	delete_packet_buffer(cookie->synaptics_ring_buffer);
533 err2:
534 	free(cookie);
535 err1:
536 	atomic_and(&dev->flags, ~PS2_FLAG_OPEN);
537 
538 	TRACE("SYNAPTICS: synaptics_open %s failed\n", name);
539 	return B_ERROR;
540 }
541 
542 
543 status_t
544 synaptics_close(void *_cookie)
545 {
546 	status_t status;
547 	synaptics_cookie *cookie = (synaptics_cookie*)_cookie;
548 
549 	ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0,
550 		150000);
551 
552 	delete_packet_buffer(cookie->synaptics_ring_buffer);
553 	delete_sem(cookie->synaptics_sem);
554 
555 	atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN);
556 	atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED);
557 
558 	// Reset the touchpad so it generate standard ps2 packets instead of
559 	// extended ones. If not, BeOS is confused with such packets when rebooting
560 	// without a complete shutdown.
561 	status = ps2_reset_mouse(cookie->dev);
562 	if (status != B_OK) {
563 		INFO("ps2: reset failed\n");
564 		return B_ERROR;
565 	}
566 
567 	if (sTouchpadInfo.capPassThrough)
568 		ps2_service_notify_device_removed(sPassthroughDevice);
569 
570 	TRACE("SYNAPTICS: close %s done\n", cookie->dev->name);
571 	return B_OK;
572 }
573 
574 
575 status_t
576 synaptics_freecookie(void *_cookie)
577 {
578 	free(_cookie);
579 	return B_OK;
580 }
581 
582 
583 static status_t
584 synaptics_read(void *cookie, off_t pos, void *buffer, size_t *_length)
585 {
586 	*_length = 0;
587 	return B_NOT_ALLOWED;
588 }
589 
590 
591 static status_t
592 synaptics_write(void *cookie, off_t pos, const void *buffer, size_t *_length)
593 {
594 	*_length = 0;
595 	return B_NOT_ALLOWED;
596 }
597 
598 
599 status_t
600 synaptics_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
601 {
602 	synaptics_cookie *cookie = (synaptics_cookie*)_cookie;
603 	mouse_movement movement;
604 	status_t status;
605 
606 	switch (op) {
607 		case MS_READ:
608 			TRACE("SYNAPTICS: MS_READ get event\n");
609 			if ((status = get_synaptics_movment(cookie, &movement)) != B_OK)
610 				return status;
611 			return user_memcpy(buffer, &movement, sizeof(movement));
612 
613 		case MS_IS_TOUCHPAD:
614 			TRACE("SYNAPTICS: MS_IS_TOUCHPAD\n");
615 			return B_OK;
616 
617 		case MS_SET_TOUCHPAD_SETTINGS:
618 			TRACE("SYNAPTICS: MS_SET_TOUCHPAD_SETTINGS");
619 			user_memcpy(&cookie->settings, buffer, sizeof(touchpad_settings));
620 			return B_OK;
621 
622 		case MS_SET_CLICKSPEED:
623 			TRACE("SYNAPTICS: ioctl MS_SETCLICK (set click speed)\n");
624 			return user_memcpy(&cookie->movementMaker.click_speed, buffer,
625 				sizeof(bigtime_t));
626 
627 		default:
628 			TRACE("SYNAPTICS: unknown opcode: %" B_PRIu32 "\n", op);
629 			return B_DEV_INVALID_IOCTL;
630 	}
631 }
632 
633 
634 int32
635 synaptics_handle_int(ps2_dev *dev)
636 {
637 	synaptics_cookie *cookie = (synaptics_cookie*)dev->cookie;
638 	uint8 val;
639 
640 	val = cookie->dev->history[0].data;
641 
642 	if ((cookie->packet_index == 0 || cookie->packet_index == 3)
643 		&& (val & 8) != 0) {
644 		INFO("SYNAPTICS: bad mouse data, trying resync\n");
645 		cookie->packet_index = 0;
646 		return B_UNHANDLED_INTERRUPT;
647 	}
648 	if (cookie->packet_index == 0 && val >> 6 != 0x02) {
649 	 	TRACE("SYNAPTICS: first package begins not with bit 1, 0\n");
650 		return B_UNHANDLED_INTERRUPT;
651  	}
652  	if (cookie->packet_index == 3 && val >> 6 != 0x03) {
653 	 	TRACE("SYNAPTICS: third package begins not with bit 1, 1\n");
654 	 	cookie->packet_index = 0;
655 		return B_UNHANDLED_INTERRUPT;
656  	}
657  	cookie->buffer[cookie->packet_index] = val;
658 
659 	cookie->packet_index++;
660 	if (cookie->packet_index >= 6) {
661 		cookie->packet_index = 0;
662 
663 		// check if package is a pass through package if true pass it
664 		// too the pass through interrupt handle
665 		if (sPassthroughDevice->active
666 			&& sPassthroughDevice->handle_int != NULL
667 			&& IS_SYN_PT_PACKAGE(cookie->buffer)) {
668 			status_t status;
669 
670 			sPassthroughDevice->history[0].data = cookie->buffer[1];
671 			sPassthroughDevice->handle_int(sPassthroughDevice);
672 			sPassthroughDevice->history[0].data = cookie->buffer[4];
673 			sPassthroughDevice->handle_int(sPassthroughDevice);
674 			sPassthroughDevice->history[0].data = cookie->buffer[5];
675 			status = sPassthroughDevice->handle_int(sPassthroughDevice);
676 
677 			if (cookie->dev->packet_size == 4) {
678 				sPassthroughDevice->history[0].data = cookie->buffer[2];
679 				status = sPassthroughDevice->handle_int(sPassthroughDevice);
680 			}
681 			return status;
682 		}
683 
684 		if (packet_buffer_write(cookie->synaptics_ring_buffer,
685 				cookie->buffer, cookie->dev->packet_size)
686 			!= cookie->dev->packet_size) {
687 			// buffer is full, drop new data
688 			return B_HANDLED_INTERRUPT;
689 		}
690 		release_sem_etc(cookie->synaptics_sem, 1, B_DO_NOT_RESCHEDULE);
691 
692 		return B_INVOKE_SCHEDULER;
693 	}
694 
695 	return B_HANDLED_INTERRUPT;
696 }
697 
698 
699 void
700 synaptics_disconnect(ps2_dev *dev)
701 {
702 	synaptics_cookie *cookie = (synaptics_cookie*)dev->cookie;
703 	// the mouse device might not be opened at this point
704 	INFO("SYNAPTICS: synaptics_disconnect %s\n", dev->name);
705 	if ((dev->flags & PS2_FLAG_OPEN) != 0)
706 		release_sem(cookie->synaptics_sem);
707 }
708 
709 
710 device_hooks gSynapticsDeviceHooks = {
711 	synaptics_open,
712 	synaptics_close,
713 	synaptics_freecookie,
714 	synaptics_ioctl,
715 	synaptics_read,
716 	synaptics_write,
717 };
718