xref: /haiku/src/add-ons/kernel/bus_managers/ps2/ps2_synaptics.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
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 typedef struct {
68 	uint8 majorVersion;
69 	uint8 minorVersion;
70 
71 	uint8 nExtendedButtons;
72 	uint8 firstExtendedButton;
73 	uint8 extendedButtonsState;
74 
75 	bool capExtended : 1;
76 	bool capMiddleButton : 1;
77 	bool capSleep : 1;
78 	bool capFourButtons : 1;
79 	bool capMultiFinger : 1;
80 	bool capMultiFingerReport : 1;
81 	bool capPalmDetection : 1;
82 	bool capPassThrough : 1;
83 	bool capAdvancedGestures : 1;
84 	bool capExtendedWMode : 1;
85 	bool capClickPadUniform : 1;
86 	int  capClickPadButtonCount : 2;
87 } touchpad_info;
88 
89 
90 const char* kSynapticsPath[4] = {
91 	"input/touchpad/ps2/synaptics_0",
92 	"input/touchpad/ps2/synaptics_1",
93 	"input/touchpad/ps2/synaptics_2",
94 	"input/touchpad/ps2/synaptics_3"
95 };
96 
97 
98 static touchpad_info sTouchpadInfo;
99 static ps2_dev *sPassthroughDevice = &ps2_device[PS2_DEVICE_SYN_PASSTHROUGH];
100 
101 
102 static status_t
103 send_touchpad_arg_timeout(ps2_dev *dev, uint8 arg, bigtime_t timeout)
104 {
105 	int8 i;
106 	uint8 val[8];
107 
108 	for (i = 0; i < 4; i++) {
109 		val[2 * i] = (arg >> (6 - 2 * i)) & 3;
110 		val[2 * i + 1] = 0xE8;
111 	}
112 	return ps2_dev_command_timeout(dev, 0xE8, val, 7, NULL, 0, timeout);
113 }
114 
115 
116 static status_t
117 send_touchpad_arg(ps2_dev *dev, uint8 arg)
118 {
119 	return send_touchpad_arg_timeout(dev, arg, 4000000);
120 }
121 
122 
123 static status_t
124 set_touchpad_mode(ps2_dev *dev, uint8 mode)
125 {
126 	uint8 sample_rate = SYN_CHANGE_MODE;
127 	send_touchpad_arg(dev, mode);
128 	return ps2_dev_command(dev, PS2_CMD_SET_SAMPLE_RATE, &sample_rate, 1,
129 		NULL, 0);
130 }
131 
132 
133 static status_t
134 get_information_query(ps2_dev *dev, uint8 extendedQueries, uint8 query,
135 	uint8 val[3])
136 {
137 	if (query == kTrackpointQuirk) {
138 		// Special case: this information query is not reported in the
139 		// "extended queries", but is still supported when the touchpad has
140 		// a pass-through port.
141 		if (!sTouchpadInfo.capPassThrough)
142 			return B_NOT_SUPPORTED;
143 	} else if (query > extendedQueries + 8)
144 		return B_NOT_SUPPORTED;
145 
146 	status_t error = send_touchpad_arg(dev, query);
147 	if (error != B_OK)
148 		return error;
149 	return ps2_dev_command(dev, 0xE9, NULL, 0, val, 3);
150 }
151 
152 
153 static status_t
154 get_synaptics_movment(synaptics_cookie *cookie, touchpad_movement *_event, bigtime_t timeout)
155 {
156 	status_t status;
157 	touchpad_movement event = {};
158 	uint8 event_buffer[PS2_MAX_PACKET_SIZE];
159 	uint8 wValue0, wValue1, wValue2, wValue3, wValue;
160 	uint32 val32;
161 	uint32 xTwelfBit, yTwelfBit;
162 
163 	status = acquire_sem_etc(cookie->synaptics_sem, 1, B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT,
164 		timeout);
165 	if (status < B_OK)
166 		return status;
167 
168 	if (!cookie->dev->active) {
169 		TRACE("SYNAPTICS: read_event: Error device no longer active\n");
170 		return B_ERROR;
171 	}
172 
173 	if (packet_buffer_read(cookie->synaptics_ring_buffer, event_buffer,
174 			cookie->dev->packet_size) != cookie->dev->packet_size) {
175 		TRACE("SYNAPTICS: error copying buffer\n");
176 		return B_ERROR;
177 	}
178 
179 	event.buttons = event_buffer[0] & 3;
180  	event.zPressure = event_buffer[2];
181 	bool isExtended = false;
182 
183  	if (sTouchpadInfo.capExtended) {
184  		wValue0 = event_buffer[3] >> 2 & 1;
185 	 	wValue1 = event_buffer[0] >> 2 & 1;
186  		wValue2 = event_buffer[0] >> 4 & 1;
187 	 	wValue3 = event_buffer[0] >> 5 & 1;
188 
189  		wValue = wValue0;
190  		wValue = wValue | (wValue1 << 1);
191  		wValue = wValue | (wValue2 << 2);
192  		wValue = wValue | (wValue3 << 3);
193 
194 
195 		TRACE("SYNAPTICS: received packet %02x %02x %02x %02x %02x %02x -> W = %d\n",
196 			event_buffer[0], event_buffer[1], event_buffer[2], event_buffer[3], event_buffer[4],
197 			event_buffer[5], wValue);
198 
199 		if (wValue <= 1) {
200 			// Multiple fingers detected, report the finger count.
201 			event.fingers = 2 + wValue;
202 
203 			// There is a separate "v" value indicating the finger width
204 			wValue0 = event_buffer[3] >> 1 & 1;
205 			wValue1 = event_buffer[4] >> 1 & 1;
206 			wValue2 = event_buffer[2] >> 0 & 1;
207 
208 			wValue = wValue0;
209 			wValue = wValue | (wValue1 << 1);
210 			wValue = wValue | (wValue2 << 2);
211 
212 			event.fingerWidth = wValue * 2;
213 		} else if (wValue >= 4) {
214 			// wValue = 4 to 15 corresponds to a finger width report for a single finger
215 			event.fingerWidth = wValue;
216 			event.fingers = 1;
217 		}
218 		// wValue = 2 - Extended W mode
219 		// wValue = 3 - Packet from pass-through device
220 	 	event.gesture = false;
221 
222 		if (sTouchpadInfo.capExtendedWMode && wValue == 2) {
223 			// The extended W can be encoded on 4 bits (values 0-7) or 8 bits (80-FF)
224 			uint8_t extendedWValue = event_buffer[5] >> 4;
225 			if (extendedWValue >= 8)
226 				extendedWValue = event_buffer[5];
227 			isExtended = true;
228 
229 			// Extended W = 0 - Scroll wheels
230 			if (extendedWValue == 0) {
231 				event.wheel_ydelta = event_buffer[2];
232 				event.wheel_xdelta = event_buffer[3];
233 				event.wheel_zdelta = event_buffer[4];
234 				event.wheel_wdelta = (event_buffer[5] & 0x0f) | (event_buffer[3] & 0x30);
235 				// Sign extend wdelta if needed
236 				if (event.wheel_wdelta & 0x20)
237 					event.wheel_wdelta |= 0xffffffc0;
238 			}
239 
240 			// Extended W = 1 - Secondary finger
241 			if (extendedWValue == 1) {
242 				event.xPosition = event_buffer[1];
243 				event.yPosition = event_buffer[2];
244 
245 				val32 = event_buffer[4] & 0x0F;
246 				event.xPosition += val32 << 8;
247 				val32 = event_buffer[4] & 0xF0;
248 				event.yPosition += val32 << 4;
249 
250 				event.xPosition *= 2;
251 				event.yPosition *= 2;
252 
253 				event.zPressure = event_buffer[5] & 0x0F;
254 				event.zPressure += event_buffer[3] & 0x30;
255 
256 				// There is a separate "v" value indicating the finger width
257 				wValue0 = event_buffer[1] >> 0 & 1;
258 				wValue1 = event_buffer[2] >> 0 & 1;
259 				wValue2 = event_buffer[5] >> 0 & 1;
260 
261 				wValue = wValue0;
262 				wValue = wValue | (wValue1 << 1);
263 				wValue = wValue | (wValue2 << 2);
264 
265 				event.fingerWidth = wValue * 2;
266 			}
267 
268 			// Extended W = 2 - Finger state info
269 			if (extendedWValue == 2) {
270 				event.fingers = event_buffer[1] & 0x0F;
271 				// TODO this event also provides primary and secondary finger indexes, what do
272 				// these mean?
273 			}
274 
275 			// Other values are reserved for other uses (usually enabled with special commands)
276 
277 
278 			*_event = event;
279 
280 			return status;
281 		}
282 
283 		// Clickpad pretends that all clicks on the touchpad are middle clicks.
284 		// Pass them to userspace as left clicks instead.
285 		if (sTouchpadInfo.capClickPadButtonCount == 1)
286 			event.buttons |= ((event_buffer[0] ^ event_buffer[3]) & 0x01);
287 
288 		if (sTouchpadInfo.capMiddleButton || sTouchpadInfo.capFourButtons)
289 			event.buttons |= ((event_buffer[0] ^ event_buffer[3]) & 0x01) << 2;
290 
291 		if (sTouchpadInfo.nExtendedButtons > 0) {
292 			if (((event_buffer[0] ^ event_buffer[3]) & 0x02) != 0) {
293 				// This packet includes extended buttons state. The state is
294 				// only reported once when one of the buttons is pressed or
295 				// released, so we must keep track of the buttons state.
296 
297 				// The values replace the lowest bits of the X and Y coordinates
298 				// in the packet, we need to extract them from there.
299 
300 				bool pressed;
301 				for (int button = 0; button < sTouchpadInfo.nExtendedButtons;
302 						button++) {
303 					// Even buttons are in the X byte
304 					pressed = event_buffer[4 + button % 2] >> button / 2 & 0x1;
305 					if (pressed) {
306 						sTouchpadInfo.extendedButtonsState |= 1 << button;
307 					} else {
308 						sTouchpadInfo.extendedButtonsState &= ~(1 << button);
309 					}
310 				}
311 			}
312 
313 			event.buttons |= sTouchpadInfo.extendedButtonsState
314 				<< sTouchpadInfo.firstExtendedButton;
315 		}
316  	} else {
317  		bool finger = event_buffer[0] >> 5 & 1;
318  		if (finger) {
319  			// finger with normal width
320 			event.fingerWidth = 4;
321  		}
322  		event.gesture = event_buffer[0] >> 2 & 1;
323  	}
324 
325 	if (!isExtended) {
326 		event.xPosition = event_buffer[4];
327 		event.yPosition = event_buffer[5];
328 
329 		val32 = event_buffer[1] & 0x0F;
330 		event.xPosition += val32 << 8;
331 		val32 = event_buffer[1] >> 4 & 0x0F;
332 		event.yPosition += val32 << 8;
333 
334 		xTwelfBit = event_buffer[3] >> 4 & 1;
335 		event.xPosition += xTwelfBit << 12;
336 		yTwelfBit = event_buffer[3] >> 5 & 1;
337 		event.yPosition += yTwelfBit << 12;
338 	}
339 
340 	*_event = event;
341 	return B_OK;
342 }
343 
344 
345 static void
346 query_capability(ps2_dev *dev)
347 {
348 	uint8 val[3];
349 	uint8 nExtendedQueries = 0;
350 
351 	get_information_query(dev, nExtendedQueries, kReadCapabilities, val);
352 
353 	TRACE("SYNAPTICS: extended mode %2x\n", val[0] >> 7 & 1);
354 	sTouchpadInfo.capExtended = val[0] >> 7 & 1;
355 	TRACE("SYNAPTICS: extended queries %2x\n", val[0] >> 4 & 7);
356 	nExtendedQueries = val[0] >> 4 & 7;
357 	TRACE("SYNAPTICS: middle button %2x\n", val[0] >> 2 & 1);
358 	sTouchpadInfo.capMiddleButton = val[0] >> 2 & 1;
359 
360 	TRACE("SYNAPTICS: pass through %2x\n", val[2] >> 7 & 1);
361 	sTouchpadInfo.capPassThrough = val[2] >> 7 & 1;
362 
363 	// bit 6, low power, is only informative (touchpad has an automatic powersave mode)
364 
365 	TRACE("SYNAPTICS: multi finger report %2x\n", val[2] >> 5 & 1);
366 	sTouchpadInfo.capMultiFingerReport = val[2] >> 5 & 1;
367 
368 	TRACE("SYNAPTICS: sleep mode %2x\n", val[2] >> 4 & 1);
369 	sTouchpadInfo.capSleep = val[2] >> 4 & 1;
370 
371 	TRACE("SYNAPTICS: four buttons %2x\n", val[2] >> 3 & 1);
372 	sTouchpadInfo.capFourButtons = val[2] >> 3 & 1;
373 
374 	// bit 2, TouchStyk ballistics, not further documented in the documents I have
375 
376 	TRACE("SYNAPTICS: multi finger %2x\n", val[2] >> 1 & 1);
377 	sTouchpadInfo.capMultiFinger = val[2] >> 1 & 1;
378 
379 	TRACE("SYNAPTICS: palm detection %2x\n", val[2] & 1);
380 	sTouchpadInfo.capPalmDetection = val[2] & 1;
381 
382 	if (get_information_query(dev, nExtendedQueries, kContinuedCapabilities,
383 			val) == B_OK) {
384 		sTouchpadInfo.capAdvancedGestures = val[0] >> 3 & 1;
385 		TRACE("SYNAPTICS: advanced gestures %x\n", sTouchpadInfo.capAdvancedGestures);
386 
387 		sTouchpadInfo.capClickPadButtonCount = (val[0] >> 4 & 1) | ((val[1] >> 0 & 1) << 1);
388 		sTouchpadInfo.capClickPadUniform = val[1] >> 4 & 1;
389 		TRACE("SYNAPTICS: clickpad buttons: %x\n", sTouchpadInfo.capClickPadButtonCount);
390 		TRACE("SYNAPTICS: clickpad type: %s\n",
391 			sTouchpadInfo.capClickPadUniform ? "uniform" : "hinged");
392 
393 	} else {
394 		sTouchpadInfo.capAdvancedGestures = 0;
395 		sTouchpadInfo.capClickPadButtonCount = 0;
396 		sTouchpadInfo.capClickPadUniform = 0;
397 		sTouchpadInfo.capAdvancedGestures = 0;
398 	}
399 
400 	if (get_information_query(dev, nExtendedQueries, kExtendedModelId, val)
401 			!= B_OK) {
402 		// "Extended Model ID" is not supported, so there cannot be extra
403 		// buttons.
404 		sTouchpadInfo.nExtendedButtons = 0;
405 		sTouchpadInfo.firstExtendedButton = 0;
406 		sTouchpadInfo.capExtendedWMode = 0;
407 		return;
408 	}
409 
410 	TRACE("SYNAPTICS: extended buttons %2x\n", val[1] >> 4 & 15);
411 	sTouchpadInfo.nExtendedButtons = val[1] >> 4 & 15;
412 	sTouchpadInfo.extendedButtonsState = 0;
413 
414 	sTouchpadInfo.capExtendedWMode = val[0] >> 2 & 1;
415 	TRACE("SYNAPTICS: extended wmode %2x\n", sTouchpadInfo.capExtendedWMode);
416 
417 	if (sTouchpadInfo.capFourButtons)
418 		sTouchpadInfo.firstExtendedButton = 4;
419 	else if (sTouchpadInfo.capMiddleButton)
420 		sTouchpadInfo.firstExtendedButton = 3;
421 	else
422 		sTouchpadInfo.firstExtendedButton = 2;
423 
424 	// Capability 0x10 is not documented in the Synaptics Touchpad interfacing
425 	// guide (at least the versions I could find), but we got the information
426 	// from Linux patches: https://lkml.org/lkml/2015/2/6/621
427 	if (get_information_query(dev, nExtendedQueries, kTrackpointQuirk, val)
428 			!= B_OK)
429 		return;
430 
431 	// Workaround for Thinkpad use of the extended buttons: they are
432 	// used as buttons for the trackpoint, so they should be reported
433 	// as buttons 0, 1, 2 rather than 3, 4, 5.
434 	TRACE("SYNAPTICS: alternate buttons %2x\n", val[0] >> 0 & 1);
435 	if (val[0] >> 0 & 1)
436 		sTouchpadInfo.firstExtendedButton = 0;
437 }
438 
439 
440 //	#pragma mark - exported functions
441 
442 
443 status_t
444 synaptics_pass_through_set_packet_size(ps2_dev *dev, uint8 size)
445 {
446 	synaptics_cookie *synapticsCookie
447 		= (synaptics_cookie*)dev->parent_dev->cookie;
448 
449 	status_t status = ps2_dev_command(dev->parent_dev, PS2_CMD_DISABLE, NULL,
450 		0, NULL, 0);
451 	if (status < B_OK) {
452 		INFO("SYNAPTICS: cannot disable touchpad %s\n", dev->parent_dev->name);
453 		return B_ERROR;
454 	}
455 
456 	synapticsCookie->packet_index = 0;
457 
458 	if (size == 4)
459 		synapticsCookie->mode |= SYN_FOUR_BYTE_CHILD;
460 	else
461 		synapticsCookie->mode &= ~SYN_FOUR_BYTE_CHILD;
462 
463 	set_touchpad_mode(dev->parent_dev, synapticsCookie->mode);
464 
465 	status = ps2_dev_command(dev->parent_dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0);
466 	if (status < B_OK) {
467 		INFO("SYNAPTICS: cannot enable touchpad %s\n", dev->parent_dev->name);
468 		return B_ERROR;
469 	}
470 	return status;
471 }
472 
473 
474 status_t
475 passthrough_command(ps2_dev *dev, uint8 cmd, const uint8 *out, int outCount,
476 	uint8 *in, int inCount, bigtime_t timeout)
477 {
478 	status_t status;
479 	uint8 passThroughCmd = SYN_PASSTHROUGH_CMD;
480 	uint8 val;
481 	uint32 passThroughInCount = (inCount + 1) * 6;
482 	uint8 passThroughIn[passThroughInCount];
483 	int8 i;
484 
485 	TRACE("SYNAPTICS: passthrough command 0x%x\n", cmd);
486 
487 	status = ps2_dev_command(dev->parent_dev, PS2_CMD_DISABLE, NULL, 0,
488 		NULL, 0);
489 	if (status != B_OK)
490 		return status;
491 
492 	for (i = -1; i < outCount; i++) {
493 		if (i == -1)
494 			val = cmd;
495 		else
496 			val = out[i];
497 		status = send_touchpad_arg_timeout(dev->parent_dev, val, timeout);
498 		if (status != B_OK)
499 			goto finalize;
500 		if (i != outCount -1) {
501 			status = ps2_dev_command_timeout(dev->parent_dev,
502 				PS2_CMD_SET_SAMPLE_RATE, &passThroughCmd, 1, NULL, 0, timeout);
503 			if (status != B_OK)
504 				goto finalize;
505 		}
506 	}
507 	status = ps2_dev_command_timeout(dev->parent_dev, PS2_CMD_SET_SAMPLE_RATE,
508 		&passThroughCmd, 1, passThroughIn, passThroughInCount, timeout);
509 	if (status != B_OK)
510 		goto finalize;
511 
512 	for (i = 0; i < inCount + 1; i++) {
513 		uint8 *inPointer = &passThroughIn[i * 6];
514 		if (!IS_SYN_PT_PACKAGE(inPointer)) {
515 			TRACE("SYNAPTICS: not a pass throught package\n");
516 			goto finalize;
517 		}
518 		if (i == 0)
519 			continue;
520 
521 		in[i - 1] = passThroughIn[i * 6 + 1];
522 	}
523 
524 finalize:
525 	status_t statusOfEnable = ps2_dev_command(dev->parent_dev, PS2_CMD_ENABLE,
526 			NULL, 0, NULL, 0);
527 	if (statusOfEnable != B_OK) {
528 		TRACE("SYNAPTICS: enabling of parent failed: 0x%" B_PRIx32 ".\n",
529 			statusOfEnable);
530 	}
531 
532 	return status != B_OK ? status : statusOfEnable;
533 }
534 
535 
536 status_t
537 probe_synaptics(ps2_dev *dev)
538 {
539 	uint8 val[3];
540 	uint8 deviceId;
541 	status_t status;
542 	TRACE("SYNAPTICS: probe\n");
543 
544 	// We reset the device here because it may have been left in a confused
545 	// state by a previous probing attempt. Some synaptics touchpads are known
546 	// to lockup when we attempt to detect them as IBM trackpoints.
547 	ps2_reset_mouse(dev);
548 
549 	// Request "Identify touchpad"
550 	// The touchpad will delay this, until it's ready and calibrated.
551 	status = get_information_query(dev, 0, kIdentify, val);
552 	if (status != B_OK)
553 		return status;
554 
555 	sTouchpadInfo.minorVersion = val[0];
556 	deviceId = val[1];
557 	if (deviceId != SYN_TOUCHPAD) {
558 		TRACE("SYNAPTICS: not found\n");
559 		return B_ERROR;
560 	}
561 
562 	TRACE("SYNAPTICS: Touchpad found id:l %2x\n", deviceId);
563 	sTouchpadInfo.majorVersion = val[2] & 0x0F;
564 	TRACE("SYNAPTICS: version %d.%d\n", sTouchpadInfo.majorVersion,
565 		sTouchpadInfo.minorVersion);
566 	// version >= 4.0?
567 	if (sTouchpadInfo.minorVersion <= 2
568 		&& sTouchpadInfo.majorVersion <= 3) {
569 		TRACE("SYNAPTICS: too old touchpad not supported\n");
570 		return B_ERROR;
571 	}
572 	dev->name = kSynapticsPath[dev->idx];
573 	return B_OK;
574 }
575 
576 
577 //	#pragma mark - Device functions
578 
579 
580 status_t
581 synaptics_open(const char *name, uint32 flags, void **_cookie)
582 {
583 	status_t status;
584 	synaptics_cookie *cookie;
585 	ps2_dev *dev;
586 	int i;
587 
588 	for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) {
589 		if (0 == strcmp(ps2_device[i].name, name)) {
590 			dev = &ps2_device[i];
591 			break;
592 		}
593 	}
594 
595 	if (dev == NULL) {
596 		TRACE("ps2: dev = NULL\n");
597 		return B_ERROR;
598 	}
599 
600 	if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN)
601 		return B_BUSY;
602 
603 	cookie = (synaptics_cookie*)malloc(sizeof(synaptics_cookie));
604 	if (cookie == NULL)
605 		goto err1;
606 	memset(cookie, 0, sizeof(*cookie));
607 
608 	*_cookie = cookie;
609 
610 	cookie->dev = dev;
611 	dev->cookie = cookie;
612 	dev->disconnect = &synaptics_disconnect;
613 	dev->handle_int = &synaptics_handle_int;
614 
615 	gHardwareSpecs.edgeMotionWidth = SYN_EDGE_MOTION_WIDTH;
616 
617 	gHardwareSpecs.areaStartX = SYN_AREA_START_X;
618 	gHardwareSpecs.areaEndX = SYN_AREA_END_X;
619 	gHardwareSpecs.areaStartY = SYN_AREA_START_Y;
620 	gHardwareSpecs.areaEndY = SYN_AREA_END_Y;
621 
622 	gHardwareSpecs.minPressure = MIN_PRESSURE;
623 	gHardwareSpecs.realMaxPressure = REAL_MAX_PRESSURE;
624 	gHardwareSpecs.maxPressure = MAX_PRESSURE;
625 
626 	dev->packet_size = PS2_PACKET_SYNAPTICS;
627 
628 	cookie->synaptics_ring_buffer
629 		= create_packet_buffer(SYNAPTICS_HISTORY_SIZE * dev->packet_size);
630 	if (cookie->synaptics_ring_buffer == NULL) {
631 		TRACE("ps2: can't allocate mouse actions buffer\n");
632 		goto err2;
633 	}
634 
635 	// create the mouse semaphore, used for synchronization between
636 	// the interrupt handler and the read operation
637 	cookie->synaptics_sem = create_sem(0, "ps2_synaptics_sem");
638 	if (cookie->synaptics_sem < 0) {
639 		TRACE("SYNAPTICS: failed creating semaphore!\n");
640 		goto err3;
641 	}
642 	query_capability(dev);
643 
644 	// create pass through dev
645 	if (sTouchpadInfo.capPassThrough) {
646 		TRACE("SYNAPTICS: pass through detected\n");
647 		sPassthroughDevice->parent_dev = dev;
648 		sPassthroughDevice->idx = dev->idx;
649 		ps2_service_notify_device_added(sPassthroughDevice);
650 	}
651 
652 	// Set Mode
653 	if (sTouchpadInfo.capExtendedWMode)
654 		cookie->mode = SYN_MODE_ABSOLUTE | SYN_MODE_W | SYN_MODE_EXTENDED_W;
655 	else if (sTouchpadInfo.capExtended)
656 		cookie->mode = SYN_MODE_ABSOLUTE | SYN_MODE_W;
657 	else
658 		cookie->mode = SYN_MODE_ABSOLUTE;
659 
660 	status = set_touchpad_mode(dev, cookie->mode);
661 	if (status < B_OK) {
662 		INFO("SYNAPTICS: cannot set mode %s\n", name);
663 		goto err4;
664 	}
665 
666 	status = ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0);
667 	if (status < B_OK) {
668 		INFO("SYNAPTICS: cannot enable touchpad %s\n", name);
669 		goto err4;
670 	}
671 
672 	atomic_or(&dev->flags, PS2_FLAG_ENABLED);
673 
674 	TRACE("SYNAPTICS: open %s success\n", name);
675 	return B_OK;
676 
677 err4:
678 	delete_sem(cookie->synaptics_sem);
679 err3:
680 	delete_packet_buffer(cookie->synaptics_ring_buffer);
681 err2:
682 	free(cookie);
683 err1:
684 	atomic_and(&dev->flags, ~PS2_FLAG_OPEN);
685 
686 	TRACE("SYNAPTICS: synaptics_open %s failed\n", name);
687 	return B_ERROR;
688 }
689 
690 
691 status_t
692 synaptics_close(void *_cookie)
693 {
694 	status_t status;
695 	synaptics_cookie *cookie = (synaptics_cookie*)_cookie;
696 
697 	ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0,
698 		150000);
699 
700 	delete_packet_buffer(cookie->synaptics_ring_buffer);
701 	delete_sem(cookie->synaptics_sem);
702 
703 	atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN);
704 	atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED);
705 
706 	// Reset the touchpad so it generate standard ps2 packets instead of
707 	// extended ones. If not, BeOS is confused with such packets when rebooting
708 	// without a complete shutdown.
709 	status = ps2_reset_mouse(cookie->dev);
710 	if (status != B_OK) {
711 		INFO("ps2_synaptics: reset failed\n");
712 		return B_ERROR;
713 	}
714 
715 	if (sTouchpadInfo.capPassThrough)
716 		ps2_service_notify_device_removed(sPassthroughDevice);
717 
718 	TRACE("SYNAPTICS: close %s done\n", cookie->dev->name);
719 	return B_OK;
720 }
721 
722 
723 status_t
724 synaptics_freecookie(void *_cookie)
725 {
726 	free(_cookie);
727 	return B_OK;
728 }
729 
730 
731 static status_t
732 synaptics_read(void *cookie, off_t pos, void *buffer, size_t *_length)
733 {
734 	*_length = 0;
735 	return B_NOT_ALLOWED;
736 }
737 
738 
739 static status_t
740 synaptics_write(void *cookie, off_t pos, const void *buffer, size_t *_length)
741 {
742 	*_length = 0;
743 	return B_NOT_ALLOWED;
744 }
745 
746 
747 status_t
748 synaptics_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
749 {
750 	synaptics_cookie *cookie = (synaptics_cookie*)_cookie;
751 	touchpad_read read;
752 	status_t status;
753 
754 	switch (op) {
755 		case MS_IS_TOUCHPAD:
756 			TRACE("SYNAPTICS: MS_IS_TOUCHPAD\n");
757 			if (buffer == NULL)
758 				return B_OK;
759 			return user_memcpy(buffer, &gHardwareSpecs, sizeof(gHardwareSpecs));
760 
761 		case MS_READ_TOUCHPAD:
762 			TRACE("SYNAPTICS: MS_READ get event\n");
763 			if (user_memcpy(&read.timeout, &(((touchpad_read*)buffer)->timeout),
764 					sizeof(bigtime_t)) != B_OK)
765 				return B_BAD_ADDRESS;
766 			if ((status = get_synaptics_movment(cookie, &read.u.touchpad, read.timeout)) != B_OK)
767 				return status;
768 			read.event = MS_READ_TOUCHPAD;
769 			return user_memcpy(buffer, &read, sizeof(read));
770 
771 		default:
772 			TRACE("SYNAPTICS: unknown opcode: %" B_PRIu32 "\n", op);
773 			return B_DEV_INVALID_IOCTL;
774 	}
775 }
776 
777 
778 int32
779 synaptics_handle_int(ps2_dev *dev)
780 {
781 	synaptics_cookie *cookie = (synaptics_cookie*)dev->cookie;
782 	uint8 val;
783 
784 	val = cookie->dev->history[0].data;
785 
786 	if ((cookie->packet_index == 0 || cookie->packet_index == 3) && (val & 8) != 0) {
787 		INFO("SYNAPTICS: bad mouse data %#02x, trying resync\n", val);
788 		cookie->packet_index = 0;
789 		return B_UNHANDLED_INTERRUPT;
790 	}
791 	if (cookie->packet_index == 0 && val >> 6 != 0x02) {
792 		TRACE("SYNAPTICS: first package %#02x begins not with bit 1, 0\n", val);
793 		return B_UNHANDLED_INTERRUPT;
794 	}
795 	if (cookie->packet_index == 3 && val >> 6 != 0x03) {
796 		TRACE("SYNAPTICS: third package %#02x begins not with bit 1, 1\n", val);
797 		cookie->packet_index = 0;
798 		return B_UNHANDLED_INTERRUPT;
799 	}
800 	cookie->buffer[cookie->packet_index] = val;
801 
802 	cookie->packet_index++;
803 	if (cookie->packet_index >= 6) {
804 		cookie->packet_index = 0;
805 
806 		// check if package is a pass through package if true pass it
807 		// too the pass through interrupt handle
808 		if (sPassthroughDevice->active
809 			&& sPassthroughDevice->handle_int != NULL
810 			&& IS_SYN_PT_PACKAGE(cookie->buffer)) {
811 			TRACE("SYNAPTICS: forward packet to passthrough device\n");
812 			status_t status;
813 
814 			sPassthroughDevice->history[0].data = cookie->buffer[1];
815 			sPassthroughDevice->handle_int(sPassthroughDevice);
816 			sPassthroughDevice->history[0].data = cookie->buffer[4];
817 			sPassthroughDevice->handle_int(sPassthroughDevice);
818 			sPassthroughDevice->history[0].data = cookie->buffer[5];
819 			status = sPassthroughDevice->handle_int(sPassthroughDevice);
820 
821 			if (cookie->dev->packet_size == 4) {
822 				sPassthroughDevice->history[0].data = cookie->buffer[2];
823 				status = sPassthroughDevice->handle_int(sPassthroughDevice);
824 			}
825 			return status;
826 		}
827 
828 		if (packet_buffer_write(cookie->synaptics_ring_buffer,
829 				cookie->buffer, cookie->dev->packet_size)
830 			!= cookie->dev->packet_size) {
831 			// buffer is full, drop new data
832 			return B_HANDLED_INTERRUPT;
833 		}
834 		release_sem_etc(cookie->synaptics_sem, 1, B_DO_NOT_RESCHEDULE);
835 
836 		return B_INVOKE_SCHEDULER;
837 	}
838 
839 	return B_HANDLED_INTERRUPT;
840 }
841 
842 
843 void
844 synaptics_disconnect(ps2_dev *dev)
845 {
846 	synaptics_cookie *cookie = (synaptics_cookie*)dev->cookie;
847 	// the mouse device might not be opened at this point
848 	INFO("SYNAPTICS: synaptics_disconnect %s\n", dev->name);
849 	if ((dev->flags & PS2_FLAG_OPEN) != 0)
850 		release_sem(cookie->synaptics_sem);
851 }
852 
853 
854 device_hooks gSynapticsDeviceHooks = {
855 	synaptics_open,
856 	synaptics_close,
857 	synaptics_freecookie,
858 	synaptics_ioctl,
859 	synaptics_read,
860 	synaptics_write,
861 };
862