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