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