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
48enum {
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
64static touchpad_specs gHardwareSpecs;
65
66
67typedef 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
90const 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
98static touchpad_info sTouchpadInfo;
99static ps2_dev *sPassthroughDevice = &ps2_device[PS2_DEVICE_SYN_PASSTHROUGH];
100
101
102static status_t
103send_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
116static status_t
117send_touchpad_arg(ps2_dev *dev, uint8 arg)
118{
119	return send_touchpad_arg_timeout(dev, arg, 4000000);
120}
121
122
123static status_t
124set_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
133static status_t
134get_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
153static status_t
154get_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
345static void
346query_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
443status_t
444synaptics_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
474status_t
475passthrough_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
524finalize:
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
536status_t
537probe_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
580status_t
581synaptics_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
677err4:
678	delete_sem(cookie->synaptics_sem);
679err3:
680	delete_packet_buffer(cookie->synaptics_ring_buffer);
681err2:
682	free(cookie);
683err1:
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
691status_t
692synaptics_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
723status_t
724synaptics_freecookie(void *_cookie)
725{
726	free(_cookie);
727	return B_OK;
728}
729
730
731static status_t
732synaptics_read(void *cookie, off_t pos, void *buffer, size_t *_length)
733{
734	*_length = 0;
735	return B_NOT_ALLOWED;
736}
737
738
739static status_t
740synaptics_write(void *cookie, off_t pos, const void *buffer, size_t *_length)
741{
742	*_length = 0;
743	return B_NOT_ALLOWED;
744}
745
746
747status_t
748synaptics_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
778int32
779synaptics_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
843void
844synaptics_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
854device_hooks gSynapticsDeviceHooks = {
855	synaptics_open,
856	synaptics_close,
857	synaptics_freecookie,
858	synaptics_ioctl,
859	synaptics_read,
860	synaptics_write,
861};
862