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