xref: /haiku/src/add-ons/kernel/bus_managers/ps2/ps2_elantech.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
1 /*
2  * Copyright 2013, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Hardware specs taken from the linux driver, thanks a lot!
6  * Based on ps2_alps.c
7  *
8  * Authors:
9  *		Jérôme Duval <korli@users.berlios.de>
10  */
11 
12 
13 #include "ps2_elantech.h"
14 
15 #include <stdlib.h>
16 #include <string.h>
17 
18 #include "ps2_service.h"
19 
20 
21 //#define TRACE_PS2_ELANTECH
22 #ifdef TRACE_PS2_ELANTECH
23 #	define TRACE(x...) dprintf(x)
24 #else
25 #	define TRACE(x...)
26 #endif
27 
28 
29 static int32 generate_event(timer* timer);
30 
31 
32 const bigtime_t kEventInterval = 1000 * 50;
33 
34 
35 class EventProducer {
36 public:
37 	EventProducer()
38 	{
39 		fFired = false;
40 	}
41 
42 	status_t
43 	FireEvent(elantech_cookie* cookie, uint8* package)
44 	{
45 		fCookie = cookie;
46 		memcpy(fLastPackage, package, sizeof(uint8) * PS2_PACKET_ELANTECH);
47 
48 		status_t status = add_timer(&fEventTimer, &generate_event,
49 			kEventInterval, B_ONE_SHOT_RELATIVE_TIMER);
50 		if (status == B_OK)
51 			fFired  = true;
52 		return status;
53 	}
54 
55 	bool
56 	CancelEvent()
57 	{
58 		if (!fFired)
59 			return false;
60 		fFired = false;
61 		return cancel_timer(&fEventTimer);
62 	}
63 
64 	void
65 	InjectEvent()
66 	{
67 		if (packet_buffer_write(fCookie->ring_buffer, fLastPackage,
68 			PS2_PACKET_ELANTECH) != PS2_PACKET_ELANTECH) {
69 			// buffer is full, drop new data
70 			return;
71 		}
72 		release_sem_etc(fCookie->sem, 1, B_DO_NOT_RESCHEDULE);
73 	}
74 
75 private:
76 	bool				fFired;
77 	uint8				fLastPackage[PS2_PACKET_ELANTECH];
78 	timer				fEventTimer;
79 	elantech_cookie*		fCookie;
80 };
81 
82 
83 static EventProducer gEventProducer;
84 
85 
86 static int32
87 generate_event(timer* timer)
88 {
89 	gEventProducer.InjectEvent();
90 	return B_HANDLED_INTERRUPT;
91 }
92 
93 
94 const char* kElantechPath[4] = {
95 	"input/touchpad/ps2/elantech_0",
96 	"input/touchpad/ps2/elantech_1",
97 	"input/touchpad/ps2/elantech_2",
98 	"input/touchpad/ps2/elantech_3"
99 };
100 
101 
102 #define ELANTECH_CMD_GET_ID				0x00
103 #define ELANTECH_CMD_GET_VERSION		0x01
104 #define ELANTECH_CMD_GET_CAPABILITIES	0x02
105 #define ELANTECH_CMD_GET_SAMPLE			0x03
106 #define ELANTECH_CMD_GET_RESOLUTION		0x04
107 
108 #define ELANTECH_CMD_REGISTER_READ		0x10
109 #define ELANTECH_CMD_REGISTER_WRITE		0x11
110 #define ELANTECH_CMD_REGISTER_READWRITE	0x00
111 #define ELANTECH_CMD_PS2_CUSTOM_CMD		0xf8
112 
113 
114 // touchpad proportions
115 #define EDGE_MOTION_WIDTH	55
116 
117 #define MIN_PRESSURE		0
118 #define REAL_MAX_PRESSURE	50
119 #define MAX_PRESSURE		255
120 
121 #define ELANTECH_HISTORY_SIZE	256
122 
123 #define STATUS_PACKET	0x0
124 #define HEAD_PACKET		0x1
125 #define MOTION_PACKET	0x2
126 
127 static hardware_specs gHardwareSpecs;
128 
129 
130 static status_t
131 get_elantech_movement(elantech_cookie *cookie, mouse_movement *movement)
132 {
133 	touch_event event;
134 	uint8 packet[PS2_PACKET_ELANTECH];
135 
136 	status_t status = acquire_sem_etc(cookie->sem, 1, B_CAN_INTERRUPT, 0);
137 	if (status < B_OK)
138 		return status;
139 
140 	if (!cookie->dev->active) {
141 		TRACE("ELANTECH: read_event: Error device no longer active\n");
142 		return B_ERROR;
143 	}
144 
145 	if (packet_buffer_read(cookie->ring_buffer, packet,
146 			cookie->dev->packet_size) != cookie->dev->packet_size) {
147 		TRACE("ELANTECH: error copying buffer\n");
148 		return B_ERROR;
149 	}
150 
151 	if (cookie->crcEnabled && (packet[3] & 0x08) != 0) {
152 		TRACE("ELANTECH: bad crc buffer\n");
153 		return B_ERROR;
154 	} else if (!cookie->crcEnabled && ((packet[0] & 0x0c) != 0x04
155 		|| (packet[3] & 0x1c) != 0x10)) {
156 		TRACE("ELANTECH: bad crc buffer\n");
157 		return B_ERROR;
158 	}
159 		// https://www.kernel.org/doc/html/v4.16/input/devices/elantech.html
160 	uint8 packet_type = packet[3] & 3;
161 	TRACE("ELANTECH: packet type %d\n", packet_type);
162 	TRACE("ELANTECH: packet content 0x%02x%02x%02x%02x%02x%02x\n",
163 		packet[0], packet[1], packet[2], packet[3],
164 		packet[4], packet[5]);
165 	switch (packet_type) {
166 		case STATUS_PACKET:
167 			//fingers, no palm
168 			cookie->fingers = (packet[4] & 0x80) == 0 ? packet[1] & 0x1f: 0;
169 			dprintf("ELANTECH: Fingers %" B_PRId32 ", raw %x (STATUS)\n",
170 				cookie->fingers, packet[1]);
171 			break;
172 		case HEAD_PACKET:
173 			dprintf("ELANTECH: Fingers %d, raw %x (HEAD)\n", (packet[3] & 0xe0) >>5, packet[3]);
174 			// only process first finger
175 			if ((packet[3] & 0xe0) != 0x20)
176 				return B_OK;
177 
178 			event.zPressure = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4);
179 
180 			cookie->previousZ = event.zPressure;
181 
182 			cookie->x = event.xPosition = ((packet[1] & 0xf) << 8) | packet[2];
183 			cookie->y = event.yPosition = ((packet[4] & 0xf) << 8) | packet[5];
184 			dprintf("ELANTECH: Pos: %" B_PRId32 ":%" B_PRId32 "\n (HEAD)",
185 				cookie->x, cookie->y);
186 			TRACE("ELANTECH: buttons 0x%x x %" B_PRIu32 " y %" B_PRIu32
187 				" z %d\n", event.buttons, event.xPosition, event.yPosition,
188 				event.zPressure);
189 			break;
190 		case MOTION_PACKET:
191 			dprintf("ELANTECH: Fingers %d, raw %x (MOTION)\n", (packet[3] & 0xe0) >>5, packet[3]);			//Most likely palm
192 			if (cookie->fingers == 0) return B_OK;
193 			//handle overflow and delta values
194 			if ((packet[0] & 0x10) != 0) {
195 				event.xPosition = cookie->x += 5 * (int8)packet[1];
196 				event.yPosition = cookie->y += 5 * (int8)packet[2];
197 			} else {
198 				event.xPosition = cookie->x += (int8)packet[1];
199 				event.yPosition = cookie->y += (int8)packet[2];
200 			}
201 			dprintf("ELANTECH: Pos: %" B_PRId32 ":%" B_PRId32 " (Motion)\n",
202 				cookie->x, cookie->y);
203 
204 			break;
205 		default:
206 			dprintf("ELANTECH: unknown packet type %d\n", packet_type);
207 			return B_ERROR;
208 	}
209 
210 	event.buttons = 0;
211 	event.wValue = cookie->fingers == 1 ? 4 :0;
212 	status = cookie->movementMaker.EventToMovement(&event, movement);
213 
214 	if (cookie->movementMaker.WasEdgeMotion()
215 		|| cookie->movementMaker.TapDragStarted()) {
216 		gEventProducer.FireEvent(cookie, packet);
217 	}
218 
219 	return status;
220 }
221 
222 
223 static void
224 default_settings(touchpad_settings *set)
225 {
226 	memcpy(set, &kDefaultTouchpadSettings, sizeof(touchpad_settings));
227 }
228 
229 
230 static status_t
231 synaptics_dev_send_command(ps2_dev* dev, uint8 cmd, uint8 *in, int in_count)
232 {
233 	if (ps2_dev_sliced_command(dev, cmd) != B_OK
234 		|| ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, in, in_count)
235 		!= B_OK) {
236 		TRACE("ELANTECH: synaptics_dev_send_command failed\n");
237 		return B_ERROR;
238 	}
239 	return B_OK;
240 }
241 
242 
243 static status_t
244 elantech_dev_send_command(ps2_dev* dev, uint8 cmd, uint8 *in, int in_count)
245 {
246 	if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
247 		|| ps2_dev_command(dev, cmd) != B_OK
248 		|| ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, in, in_count)
249 		!= B_OK) {
250 		TRACE("ELANTECH: elantech_dev_send_command failed\n");
251 		return B_ERROR;
252 	}
253 	return B_OK;
254 }
255 
256 
257 status_t
258 probe_elantech(ps2_dev* dev)
259 {
260 	uint8 val[3];
261 	TRACE("ELANTECH: probe\n");
262 
263 	ps2_dev_command(dev, PS2_CMD_MOUSE_RESET_DIS);
264 
265 	if (ps2_dev_command(dev, PS2_CMD_DISABLE) != B_OK
266 		|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK
267 		|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK
268 		|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK) {
269 		TRACE("ELANTECH: not found (1)\n");
270 		return B_ERROR;
271 	}
272 
273 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 3)
274 		!= B_OK) {
275 		TRACE("ELANTECH: not found (2)\n");
276 		return B_ERROR;
277 	}
278 
279 	if (val[0] != 0x3c || val[1] != 0x3 || (val[2] != 0xc8 && val[2] != 0x0)) {
280 		TRACE("ELANTECH: not found (3)\n");
281 		return B_ERROR;
282 	}
283 
284 	val[0] = 0;
285 	if (synaptics_dev_send_command(dev, ELANTECH_CMD_GET_VERSION, val, 3)
286 		!= B_OK) {
287 		TRACE("ELANTECH: not found (4)\n");
288 		return B_ERROR;
289 	}
290 
291 	if (val[0] == 0x0 || val[2] == 10 || val[2] == 20 || val[2] == 40
292 		|| val[2] == 60 || val[2] == 80 || val[2] == 100 || val[2] == 200) {
293 		TRACE("ELANTECH: not found (5)\n");
294 		return B_ERROR;
295 	}
296 
297 	INFO("Elantech version %02X%02X%02X, under developement! Using fallback.\n",
298 		val[0], val[1], val[2]);
299 
300 	dev->name = kElantechPath[dev->idx];
301 	dev->packet_size = PS2_PACKET_ELANTECH;
302 
303 	return B_ERROR;
304 }
305 
306 
307 static status_t
308 elantech_write_reg(elantech_cookie* cookie, uint8 reg, uint8 value)
309 {
310 	if (reg < 0x7 || reg > 0x26)
311 		return B_BAD_VALUE;
312 	if (reg > 0x11 && reg < 0x20)
313 		return B_BAD_VALUE;
314 
315 	ps2_dev* dev = cookie->dev;
316 	switch (cookie->version) {
317 		case 1:
318 			// TODO
319 			return B_ERROR;
320 			break;
321 		case 2:
322 			if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
323 				|| ps2_dev_command(dev, ELANTECH_CMD_REGISTER_WRITE) != B_OK
324 				|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
325 				|| ps2_dev_command(dev, reg) != B_OK
326 				|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
327 				|| ps2_dev_command(dev, value) != B_OK
328 				|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK)
329 				return B_ERROR;
330 			break;
331 		case 3:
332 			if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
333 				|| ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE) != B_OK
334 				|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
335 				|| ps2_dev_command(dev, reg) != B_OK
336 				|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
337 				|| ps2_dev_command(dev, value) != B_OK
338 				|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK)
339 				return B_ERROR;
340 			break;
341 		case 4:
342 			if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
343 				|| ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE) != B_OK
344 				|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
345 				|| ps2_dev_command(dev, reg) != B_OK
346 				|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
347 				|| ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE) != B_OK
348 				|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
349 				|| ps2_dev_command(dev, value) != B_OK
350 				|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK)
351 				return B_ERROR;
352 			break;
353 		default:
354 			TRACE("ELANTECH: read_write_reg: unknown version\n");
355 			return B_ERROR;
356 	}
357 	return B_OK;
358 }
359 
360 
361 static status_t
362 elantech_read_reg(elantech_cookie* cookie, uint8 reg, uint8 *value)
363 {
364 	if (reg < 0x7 || reg > 0x26)
365 		return B_BAD_VALUE;
366 	if (reg > 0x11 && reg < 0x20)
367 		return B_BAD_VALUE;
368 
369 	ps2_dev* dev = cookie->dev;
370 	uint8 val[3];
371 	switch (cookie->version) {
372 		case 1:
373 			// TODO
374 			return B_ERROR;
375 			break;
376 		case 2:
377 			if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
378 				|| ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READ) != B_OK
379 				|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
380 				|| ps2_dev_command(dev, reg) != B_OK
381 				|| ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val,
382 					3) != B_OK)
383 				return B_ERROR;
384 			break;
385 		case 3:
386 		case 4:
387 			if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
388 				|| ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE)
389 					!= B_OK
390 				|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
391 				|| ps2_dev_command(dev, reg) != B_OK
392 				|| ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val,
393 					3) != B_OK)
394 				return B_ERROR;
395 			break;
396 		default:
397 			TRACE("ELANTECH: read_write_reg: unknown version\n");
398 			return B_ERROR;
399 	}
400 	if (cookie->version == 4)
401 		*value = val[1];
402 	else
403 		*value = val[0];
404 
405 	return B_OK;
406 }
407 
408 
409 static status_t
410 get_resolution_v4(elantech_cookie* cookie, uint32* x, uint32* y)
411 {
412 	uint8 val[3];
413 	if (elantech_dev_send_command(cookie->dev, ELANTECH_CMD_GET_RESOLUTION,
414 		val, 3) != B_OK)
415 		return B_ERROR;
416 	*x = (val[1] & 0xf) * 10 + 790;
417 	*y = ((val[1] & 0xf) >> 4) * 10 + 790;
418 	return B_OK;
419 }
420 
421 
422 static status_t
423 get_range(elantech_cookie* cookie, uint32* x_min, uint32* y_min, uint32* x_max,
424 	uint32* y_max, uint32 *width)
425 {
426 	uint8 val[3];
427 	switch (cookie->version) {
428 		case 1:
429 			*x_min = 32;
430 			*y_min = 32;
431 			*x_max = 544;
432 			*y_max = 344;
433 			*width = 0;
434 			break;
435 		case 2:
436 			// TODO
437 			break;
438 		case 3:
439 			if ((cookie->send_command)(cookie->dev, ELANTECH_CMD_GET_ID, val, 3)
440 				!= B_OK) {
441 				return B_ERROR;
442 			}
443 			*x_min = 0;
444 			*y_min = 0;
445 			*x_max = ((val[0] & 0xf) << 8) | val[1];
446 			*y_max = ((val[0] & 0xf0) << 4) | val[2];
447 			*width = 0;
448 			break;
449 		case 4:
450 			if ((cookie->send_command)(cookie->dev, ELANTECH_CMD_GET_ID, val, 3)
451 				!= B_OK) {
452 				return B_ERROR;
453 			}
454 			*x_min = 0;
455 			*y_min = 0;
456 			*x_max = ((val[0] & 0xf) << 8) | val[1];
457 			*y_max = ((val[0] & 0xf0) << 4) | val[2];
458 			if (cookie->capabilities[1] < 2 || cookie->capabilities[1] > *x_max)
459 				return B_ERROR;
460 			*width = *x_max / (cookie->capabilities[1] - 1);
461 			break;
462 	}
463 	return B_OK;
464 }
465 
466 
467 static status_t
468 enable_absolute_mode(elantech_cookie* cookie)
469 {
470 	status_t status = B_OK;
471 	switch (cookie->version) {
472 		case 1:
473 			status = elantech_write_reg(cookie, 0x10, 0x16);
474 			if (status == B_OK)
475 				status = elantech_write_reg(cookie, 0x11, 0x8f);
476 			break;
477 		case 2:
478 			status = elantech_write_reg(cookie, 0x10, 0x54);
479 			if (status == B_OK)
480 				status = elantech_write_reg(cookie, 0x11, 0x88);
481 			if (status == B_OK)
482 				status = elantech_write_reg(cookie, 0x12, 0x60);
483 			break;
484 		case 3:
485 			status = elantech_write_reg(cookie, 0x10, 0xb);
486 			break;
487 		case 4:
488 			status = elantech_write_reg(cookie, 0x7, 0x1);
489 			break;
490 
491 	}
492 
493 	if (cookie->version < 4) {
494 		uint8 val;
495 
496 		for (uint8 retry = 0; retry < 5; retry++) {
497 			status = elantech_read_reg(cookie, 0x10, &val);
498 			if (status != B_OK)
499 				break;
500 			snooze(100);
501 		}
502 	}
503 
504 	return status;
505 }
506 
507 
508 status_t
509 elantech_open(const char *name, uint32 flags, void **_cookie)
510 {
511 	TRACE("ELANTECH: open %s\n", name);
512 	ps2_dev* dev;
513 	int i;
514 	for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) {
515 		if (0 == strcmp(ps2_device[i].name, name)) {
516 			dev = &ps2_device[i];
517 			break;
518 		}
519 	}
520 
521 	if (dev == NULL) {
522 		TRACE("ps2: dev = NULL\n");
523 		return B_ERROR;
524 	}
525 
526 	if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN)
527 		return B_BUSY;
528 
529 	uint32 x_min = 0, x_max = 0, y_min = 0, y_max = 0, width = 0;
530 
531 	elantech_cookie* cookie = (elantech_cookie*)malloc(
532 		sizeof(elantech_cookie));
533 	if (cookie == NULL)
534 		goto err1;
535 	memset(cookie, 0, sizeof(*cookie));
536 
537 	cookie->movementMaker.Init();
538 	cookie->previousZ = 0;
539 	*_cookie = cookie;
540 
541 	cookie->dev = dev;
542 	dev->cookie = cookie;
543 	dev->disconnect = &elantech_disconnect;
544 	dev->handle_int = &elantech_handle_int;
545 
546 	default_settings(&cookie->settings);
547 
548 	dev->packet_size = PS2_PACKET_ELANTECH;
549 
550 	cookie->ring_buffer = create_packet_buffer(
551 		ELANTECH_HISTORY_SIZE * dev->packet_size);
552 	if (cookie->ring_buffer == NULL) {
553 		TRACE("ELANTECH: can't allocate mouse actions buffer\n");
554 		goto err2;
555 	}
556 	// create the mouse semaphore, used for synchronization between
557 	// the interrupt handler and the read operation
558 	cookie->sem = create_sem(0, "ps2_elantech_sem");
559 	if (cookie->sem < 0) {
560 		TRACE("ELANTECH: failed creating semaphore!\n");
561 		goto err3;
562 	}
563 
564 	uint8 val[3];
565 	if (synaptics_dev_send_command(dev, ELANTECH_CMD_GET_VERSION, val, 3)
566 		!= B_OK) {
567 		TRACE("ELANTECH: get version failed!\n");
568 		goto err4;
569 	}
570 	cookie->fwVersion = (val[0] << 16) | (val[1] << 8) | val[2];
571 	if (cookie->fwVersion < 0x020030 || cookie->fwVersion == 0x020600)
572 		cookie->version = 1;
573 	else {
574 		switch (val[0] & 0xf) {
575 			case 2:
576 			case 4:
577 				cookie->version = 2;
578 				break;
579 			case 5:
580 				cookie->version = 3;
581 				break;
582 			case 6:
583 			case 7:
584 				cookie->version = 4;
585 				break;
586 			default:
587 				TRACE("ELANTECH: unknown version!\n");
588 				goto err4;
589 		}
590 	}
591 	INFO("ELANTECH: version 0x%" B_PRIu32 " (0x%" B_PRIu32 ")\n",
592 		cookie->version, cookie->fwVersion);
593 
594 	if (cookie->version >= 3)
595 		cookie->send_command = &elantech_dev_send_command;
596 	else
597 		cookie->send_command = &synaptics_dev_send_command;
598 	cookie->crcEnabled = (cookie->fwVersion & 0x4000) == 0x4000;
599 
600 	if ((cookie->send_command)(cookie->dev, ELANTECH_CMD_GET_CAPABILITIES,
601 		cookie->capabilities, 3) != B_OK) {
602 		TRACE("ELANTECH: get capabilities failed!\n");
603 		return B_ERROR;
604 	}
605 
606 	if (enable_absolute_mode(cookie) != B_OK) {
607 		TRACE("ELANTECH: failed enabling absolute mode!\n");
608 		goto err4;
609 	}
610 	TRACE("ELANTECH: enabled absolute mode!\n");
611 
612 	if (get_range(cookie, &x_min, &y_min, &x_max, &y_max, &width) != B_OK) {
613 		TRACE("ELANTECH: get range failed!\n");
614 		goto err4;
615 	}
616 
617 	TRACE("ELANTECH: range x %" B_PRIu32 "-%" B_PRIu32 " y %" B_PRIu32
618 		"-%" B_PRIu32 " (%" B_PRIu32 ")\n", x_min, x_max, y_min, y_max, width);
619 
620 	uint32 x_res, y_res;
621 	if (get_resolution_v4(cookie, &x_res, &y_res) != B_OK) {
622 		TRACE("ELANTECH: get resolution failed!\n");
623 		goto err4;
624 	}
625 
626 	TRACE("ELANTECH: resolution x %" B_PRIu32 " y %" B_PRIu32 " (dpi)\n",
627 		x_res, y_res);
628 
629 	gHardwareSpecs.edgeMotionWidth = EDGE_MOTION_WIDTH;
630 
631 	gHardwareSpecs.areaStartX = x_min;
632 	gHardwareSpecs.areaEndX = x_max;
633 	gHardwareSpecs.areaStartY = y_min;
634 	gHardwareSpecs.areaEndY = y_max;
635 
636 	gHardwareSpecs.minPressure = MIN_PRESSURE;
637 	gHardwareSpecs.realMaxPressure = REAL_MAX_PRESSURE;
638 	gHardwareSpecs.maxPressure = MAX_PRESSURE;
639 
640 	cookie->movementMaker.SetSettings(&cookie->settings);
641 	cookie->movementMaker.SetSpecs(&gHardwareSpecs);
642 
643 	if (ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0) != B_OK)
644 		goto err4;
645 
646 	atomic_or(&dev->flags, PS2_FLAG_ENABLED);
647 
648 	TRACE("ELANTECH: open %s success\n", name);
649 	return B_OK;
650 
651 err4:
652 	delete_sem(cookie->sem);
653 err3:
654 	delete_packet_buffer(cookie->ring_buffer);
655 err2:
656 	free(cookie);
657 err1:
658 	atomic_and(&dev->flags, ~PS2_FLAG_OPEN);
659 
660 	TRACE("ELANTECH: open %s failed\n", name);
661 	return B_ERROR;
662 }
663 
664 
665 status_t
666 elantech_close(void *_cookie)
667 {
668 	gEventProducer.CancelEvent();
669 
670 	elantech_cookie *cookie = (elantech_cookie*)_cookie;
671 
672 	ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0,
673 		150000);
674 
675 	delete_packet_buffer(cookie->ring_buffer);
676 	delete_sem(cookie->sem);
677 
678 	atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN);
679 	atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED);
680 
681 	// Reset the touchpad so it generate standard ps2 packets instead of
682 	// extended ones. If not, BeOS is confused with such packets when rebooting
683 	// without a complete shutdown.
684 	status_t status = ps2_reset_mouse(cookie->dev);
685 	if (status != B_OK) {
686 		INFO("ps2: reset failed\n");
687 		return B_ERROR;
688 	}
689 
690 	TRACE("ELANTECH: close %s done\n", cookie->dev->name);
691 	return B_OK;
692 }
693 
694 
695 status_t
696 elantech_freecookie(void *_cookie)
697 {
698 	free(_cookie);
699 	return B_OK;
700 }
701 
702 
703 status_t
704 elantech_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
705 {
706 	elantech_cookie *cookie = (elantech_cookie*)_cookie;
707 	mouse_movement movement;
708 	status_t status;
709 
710 	switch (op) {
711 		case MS_READ:
712 			TRACE("ELANTECH: MS_READ get event\n");
713 			if ((status = get_elantech_movement(cookie, &movement)) != B_OK)
714 				return status;
715 			return user_memcpy(buffer, &movement, sizeof(movement));
716 
717 		case MS_IS_TOUCHPAD:
718 			TRACE("ELANTECH: MS_IS_TOUCHPAD\n");
719 			return B_OK;
720 
721 		case MS_SET_TOUCHPAD_SETTINGS:
722 			TRACE("ELANTECH: MS_SET_TOUCHPAD_SETTINGS\n");
723 			user_memcpy(&cookie->settings, buffer, sizeof(touchpad_settings));
724 			return B_OK;
725 
726 		case MS_SET_CLICKSPEED:
727 			TRACE("ELANTECH: ioctl MS_SETCLICK (set click speed)\n");
728 			return user_memcpy(&cookie->movementMaker.click_speed, buffer,
729 				sizeof(bigtime_t));
730 
731 		default:
732 			INFO("ELANTECH: unknown opcode: 0x%" B_PRIx32 "\n", op);
733 			return B_BAD_VALUE;
734 	}
735 }
736 
737 
738 static status_t
739 elantech_read(void* cookie, off_t pos, void* buffer, size_t* _length)
740 {
741 	*_length = 0;
742 	return B_NOT_ALLOWED;
743 }
744 
745 
746 static status_t
747 elantech_write(void* cookie, off_t pos, const void* buffer, size_t* _length)
748 {
749 	*_length = 0;
750 	return B_NOT_ALLOWED;
751 }
752 
753 
754 int32
755 elantech_handle_int(ps2_dev* dev)
756 {
757 	elantech_cookie* cookie = (elantech_cookie*)dev->cookie;
758 
759 	// we got a real event cancel the fake event
760 	gEventProducer.CancelEvent();
761 
762 	uint8 val;
763 	val = cookie->dev->history[0].data;
764  	cookie->buffer[cookie->packet_index] = val;
765 	cookie->packet_index++;
766 
767 	if (cookie->packet_index < PS2_PACKET_ELANTECH)
768 		return B_HANDLED_INTERRUPT;
769 
770 	cookie->packet_index = 0;
771 	if (packet_buffer_write(cookie->ring_buffer,
772 				cookie->buffer, cookie->dev->packet_size)
773 			!= cookie->dev->packet_size) {
774 			// buffer is full, drop new data
775 			return B_HANDLED_INTERRUPT;
776 		}
777 	release_sem_etc(cookie->sem, 1, B_DO_NOT_RESCHEDULE);
778 	return B_INVOKE_SCHEDULER;
779 }
780 
781 
782 void
783 elantech_disconnect(ps2_dev *dev)
784 {
785 	elantech_cookie *cookie = (elantech_cookie*)dev->cookie;
786 	// the mouse device might not be opened at this point
787 	INFO("ELANTECH: elantech_disconnect %s\n", dev->name);
788 	if ((dev->flags & PS2_FLAG_OPEN) != 0)
789 		release_sem(cookie->sem);
790 }
791 
792 
793 device_hooks gElantechDeviceHooks = {
794 	elantech_open,
795 	elantech_close,
796 	elantech_freecookie,
797 	elantech_ioctl,
798 	elantech_read,
799 	elantech_write,
800 };
801