xref: /haiku/src/add-ons/kernel/bus_managers/ps2/ps2_elantech.cpp (revision 6c2abee2f5e73c3fc81c33da51ac610f8bf1117a)
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 found\n");
278 
279 	dev->name = kElantechPath[dev->idx];
280 	dev->packet_size = PS2_PACKET_ELANTECH;
281 
282 	return B_OK;
283 }
284 
285 
286 static status_t
287 elantech_write_reg(elantech_cookie* cookie, uint8 reg, uint8 value)
288 {
289 	if (reg < 0x7 || reg > 0x26)
290 		return B_BAD_VALUE;
291 	if (reg > 0x11 && reg < 0x20)
292 		return B_BAD_VALUE;
293 
294 	ps2_dev* dev = cookie->dev;
295 	switch (cookie->version) {
296 		case 1:
297 			// TODO
298 			return B_ERROR;
299 			break;
300 		case 2:
301 			if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
302 				|| ps2_dev_command(dev, ELANTECH_CMD_REGISTER_WRITE) != B_OK
303 				|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
304 				|| ps2_dev_command(dev, reg) != B_OK
305 				|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
306 				|| ps2_dev_command(dev, value) != B_OK
307 				|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK)
308 				return B_ERROR;
309 			break;
310 		case 3:
311 			if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
312 				|| ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE) != B_OK
313 				|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
314 				|| ps2_dev_command(dev, reg) != B_OK
315 				|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
316 				|| ps2_dev_command(dev, value) != B_OK
317 				|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK)
318 				return B_ERROR;
319 			break;
320 		case 4:
321 			if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
322 				|| ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE) != B_OK
323 				|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
324 				|| ps2_dev_command(dev, reg) != B_OK
325 				|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
326 				|| ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE) != B_OK
327 				|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
328 				|| ps2_dev_command(dev, value) != B_OK
329 				|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK)
330 				return B_ERROR;
331 			break;
332 		default:
333 			TRACE("ELANTECH: read_write_reg: unknown version\n");
334 			return B_ERROR;
335 	}
336 	return B_OK;
337 }
338 
339 
340 static status_t
341 elantech_read_reg(elantech_cookie* cookie, uint8 reg, uint8 *value)
342 {
343 	if (reg < 0x7 || reg > 0x26)
344 		return B_BAD_VALUE;
345 	if (reg > 0x11 && reg < 0x20)
346 		return B_BAD_VALUE;
347 
348 	ps2_dev* dev = cookie->dev;
349 	uint8 val[3];
350 	switch (cookie->version) {
351 		case 1:
352 			// TODO
353 			return B_ERROR;
354 			break;
355 		case 2:
356 			if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
357 				|| ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READ) != B_OK
358 				|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
359 				|| ps2_dev_command(dev, reg) != B_OK
360 				|| ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val,
361 					3) != B_OK)
362 				return B_ERROR;
363 			break;
364 		case 3:
365 		case 4:
366 			if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
367 				|| ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE)
368 					!= B_OK
369 				|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
370 				|| ps2_dev_command(dev, reg) != B_OK
371 				|| ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val,
372 					3) != B_OK)
373 				return B_ERROR;
374 			break;
375 		default:
376 			TRACE("ELANTECH: read_write_reg: unknown version\n");
377 			return B_ERROR;
378 	}
379 	if (cookie->version == 4)
380 		*value = val[1];
381 	else
382 		*value = val[0];
383 
384 	return B_OK;
385 }
386 
387 
388 static status_t
389 get_resolution_v4(elantech_cookie* cookie, uint32* x, uint32* y)
390 {
391 	uint8 val[3];
392 	if (elantech_dev_send_command(cookie->dev, ELANTECH_CMD_GET_RESOLUTION,
393 		val, 3) != B_OK)
394 		return B_ERROR;
395 	*x = (val[1] & 0xf) * 10 + 790;
396 	*y = ((val[1] & 0xf) >> 4) * 10 + 790;
397 	return B_OK;
398 }
399 
400 
401 static status_t
402 get_range(elantech_cookie* cookie, uint32* x_min, uint32* y_min, uint32* x_max,
403 	uint32* y_max, uint32 *width)
404 {
405 	uint8 val[3];
406 	switch (cookie->version) {
407 		case 1:
408 			*x_min = 32;
409 			*y_min = 32;
410 			*x_max = 544;
411 			*y_max = 344;
412 			*width = 0;
413 			break;
414 		case 2:
415 			// TODO
416 			break;
417 		case 3:
418 			if ((cookie->send_command)(cookie->dev, ELANTECH_CMD_GET_ID, val, 3)
419 				!= B_OK) {
420 				return B_ERROR;
421 			}
422 			*x_min = 0;
423 			*y_min = 0;
424 			*x_max = ((val[0] & 0xf) << 8) | val[1];
425 			*y_max = ((val[0] & 0xf0) << 4) | val[2];
426 			*width = 0;
427 			break;
428 		case 4:
429 			if ((cookie->send_command)(cookie->dev, ELANTECH_CMD_GET_ID, val, 3)
430 				!= B_OK) {
431 				return B_ERROR;
432 			}
433 			*x_min = 0;
434 			*y_min = 0;
435 			*x_max = ((val[0] & 0xf) << 8) | val[1];
436 			*y_max = ((val[0] & 0xf0) << 4) | val[2];
437 			if (cookie->capabilities[1] < 2 || cookie->capabilities[1] > *x_max)
438 				return B_ERROR;
439 			*width = *x_max / (cookie->capabilities[1] - 1);
440 			break;
441 	}
442 	return B_OK;
443 }
444 
445 
446 static status_t
447 enable_absolute_mode(elantech_cookie* cookie)
448 {
449 	status_t status = B_OK;
450 	switch (cookie->version) {
451 		case 1:
452 			status = elantech_write_reg(cookie, 0x10, 0x16);
453 			if (status == B_OK)
454 				status = elantech_write_reg(cookie, 0x11, 0x8f);
455 			break;
456 		case 2:
457 			status = elantech_write_reg(cookie, 0x10, 0x54);
458 			if (status == B_OK)
459 				status = elantech_write_reg(cookie, 0x11, 0x88);
460 			if (status == B_OK)
461 				status = elantech_write_reg(cookie, 0x12, 0x60);
462 			break;
463 		case 3:
464 			status = elantech_write_reg(cookie, 0x10, 0xb);
465 			break;
466 		case 4:
467 			status = elantech_write_reg(cookie, 0x7, 0x1);
468 			break;
469 
470 	}
471 
472 	if (cookie->version < 4) {
473 		uint8 val;
474 
475 		for (uint8 retry = 0; retry < 5; retry++) {
476 			status = elantech_read_reg(cookie, 0x10, &val);
477 			if (status != B_OK)
478 				break;
479 			snooze(100);
480 		}
481 	}
482 
483 	return status;
484 }
485 
486 
487 status_t
488 elantech_open(const char *name, uint32 flags, void **_cookie)
489 {
490 	TRACE("ELANTECH: open %s\n", name);
491 	ps2_dev* dev;
492 	int i;
493 	for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) {
494 		if (0 == strcmp(ps2_device[i].name, name)) {
495 			dev = &ps2_device[i];
496 			break;
497 		}
498 	}
499 
500 	if (dev == NULL) {
501 		TRACE("ps2: dev = NULL\n");
502 		return B_ERROR;
503 	}
504 
505 	if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN)
506 		return B_BUSY;
507 
508 	elantech_cookie* cookie = (elantech_cookie*)malloc(
509 		sizeof(elantech_cookie));
510 	if (cookie == NULL)
511 		goto err1;
512 	memset(cookie, 0, sizeof(*cookie));
513 
514 	cookie->movementMaker.Init();
515 	cookie->previousZ = 0;
516 	*_cookie = cookie;
517 
518 	cookie->dev = dev;
519 	dev->cookie = cookie;
520 	dev->disconnect = &elantech_disconnect;
521 	dev->handle_int = &elantech_handle_int;
522 
523 	default_settings(&cookie->settings);
524 
525 	dev->packet_size = PS2_PACKET_ELANTECH;
526 
527 	cookie->ring_buffer = create_packet_buffer(
528 		ELANTECH_HISTORY_SIZE * dev->packet_size);
529 	if (cookie->ring_buffer == NULL) {
530 		TRACE("ELANTECH: can't allocate mouse actions buffer\n");
531 		goto err2;
532 	}
533 	// create the mouse semaphore, used for synchronization between
534 	// the interrupt handler and the read operation
535 	cookie->sem = create_sem(0, "ps2_elantech_sem");
536 	if (cookie->sem < 0) {
537 		TRACE("ELANTECH: failed creating semaphore!\n");
538 		goto err3;
539 	}
540 
541 	uint8 val[3];
542 	if (synaptics_dev_send_command(dev, ELANTECH_CMD_GET_VERSION, val, 3)
543 		!= B_OK) {
544 		TRACE("ELANTECH: get version failed!\n");
545 		goto err4;
546 	}
547 	cookie->fwVersion = (val[0] << 16) | (val[1] << 8) | val[2];
548 	if (cookie->fwVersion < 0x020030 || cookie->fwVersion == 0x020600)
549 		cookie->version = 1;
550 	else {
551 		switch (val[0] & 0xf) {
552 			case 2:
553 			case 4:
554 				cookie->version = 2;
555 				break;
556 			case 5:
557 				cookie->version = 3;
558 				break;
559 			case 6:
560 			case 7:
561 				cookie->version = 4;
562 				break;
563 			default:
564 				TRACE("ELANTECH: unknown version!\n");
565 				goto err4;
566 		}
567 	}
568 	TRACE("ELANTECH: version 0x%lx (0x%lx)\n", cookie->version,
569 		cookie->fwVersion);
570 
571 	if (cookie->version >= 3)
572 		cookie->send_command = &elantech_dev_send_command;
573 	else
574 		cookie->send_command = &synaptics_dev_send_command;
575 	cookie->crcEnabled = (cookie->fwVersion & 0x4000) == 0x4000;
576 
577 	if ((cookie->send_command)(cookie->dev, ELANTECH_CMD_GET_CAPABILITIES,
578 		cookie->capabilities, 3) != B_OK) {
579 		TRACE("ELANTECH: get capabilities failed!\n");
580 		return B_ERROR;
581 	}
582 
583 	if (enable_absolute_mode(cookie) != B_OK) {
584 		TRACE("ELANTECH: failed enabling absolute mode!\n");
585 		goto err4;
586 	}
587 	TRACE("ELANTECH: enabled absolute mode!\n");
588 
589 	uint32 x_min, x_max, y_min, y_max, width;
590 	if (get_range(cookie, &x_min, &y_min, &x_max, &y_max, &width) != B_OK) {
591 		TRACE("ELANTECH: get range failed!\n");
592 		goto err4;
593 	}
594 
595 	TRACE("ELANTECH: range x %ld-%ld y %ld-%ld (%ld)\n", x_min, x_max,
596 		y_min, y_max, width);
597 
598 	uint32 x_res, y_res;
599 	if (get_resolution_v4(cookie, &x_res, &y_res) != B_OK) {
600 		TRACE("ELANTECH: get resolution failed!\n");
601 		goto err4;
602 	}
603 
604 	TRACE("ELANTECH: resolution x %ld y %ld (dpi)\n", x_res, y_res);
605 
606 	gHardwareSpecs.edgeMotionWidth = EDGE_MOTION_WIDTH;
607 
608 	gHardwareSpecs.areaStartX = x_min;
609 	gHardwareSpecs.areaEndX = x_max;
610 	gHardwareSpecs.areaStartY = y_min;
611 	gHardwareSpecs.areaEndY = y_max;
612 
613 	gHardwareSpecs.minPressure = MIN_PRESSURE;
614 	gHardwareSpecs.realMaxPressure = REAL_MAX_PRESSURE;
615 	gHardwareSpecs.maxPressure = MAX_PRESSURE;
616 
617 	cookie->movementMaker.SetSettings(&cookie->settings);
618 	cookie->movementMaker.SetSpecs(&gHardwareSpecs);
619 
620 	if (ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0) != B_OK)
621 		goto err4;
622 
623 	atomic_or(&dev->flags, PS2_FLAG_ENABLED);
624 
625 	TRACE("ELANTECH: open %s success\n", name);
626 	return B_OK;
627 
628 err4:
629 	delete_sem(cookie->sem);
630 err3:
631 	delete_packet_buffer(cookie->ring_buffer);
632 err2:
633 	free(cookie);
634 err1:
635 	atomic_and(&dev->flags, ~PS2_FLAG_OPEN);
636 
637 	TRACE("ELANTECH: open %s failed\n", name);
638 	return B_ERROR;
639 }
640 
641 
642 status_t
643 elantech_close(void *_cookie)
644 {
645 	gEventProducer.CancelEvent();
646 
647 	elantech_cookie *cookie = (elantech_cookie*)_cookie;
648 
649 	ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0,
650 		150000);
651 
652 	delete_packet_buffer(cookie->ring_buffer);
653 	delete_sem(cookie->sem);
654 
655 	atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN);
656 	atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED);
657 
658 	// Reset the touchpad so it generate standard ps2 packets instead of
659 	// extended ones. If not, BeOS is confused with such packets when rebooting
660 	// without a complete shutdown.
661 	status_t status = ps2_reset_mouse(cookie->dev);
662 	if (status != B_OK) {
663 		INFO("ps2: reset failed\n");
664 		return B_ERROR;
665 	}
666 
667 	TRACE("ELANTECH: close %s done\n", cookie->dev->name);
668 	return B_OK;
669 }
670 
671 
672 status_t
673 elantech_freecookie(void *_cookie)
674 {
675 	free(_cookie);
676 	return B_OK;
677 }
678 
679 
680 status_t
681 elantech_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
682 {
683 	elantech_cookie *cookie = (elantech_cookie*)_cookie;
684 	mouse_movement movement;
685 	status_t status;
686 
687 	switch (op) {
688 		case MS_READ:
689 			TRACE("ELANTECH: MS_READ get event\n");
690 			if ((status = get_elantech_movement(cookie, &movement)) != B_OK)
691 				return status;
692 			return user_memcpy(buffer, &movement, sizeof(movement));
693 
694 		case MS_IS_TOUCHPAD:
695 			TRACE("ELANTECH: MS_IS_TOUCHPAD\n");
696 			return B_OK;
697 
698 		case MS_SET_TOUCHPAD_SETTINGS:
699 			TRACE("ELANTECH: MS_SET_TOUCHPAD_SETTINGS");
700 			user_memcpy(&cookie->settings, buffer, sizeof(touchpad_settings));
701 			return B_OK;
702 
703 		case MS_SET_CLICKSPEED:
704 			TRACE("ELANTECH: ioctl MS_SETCLICK (set click speed)\n");
705 			return user_memcpy(&cookie->movementMaker.click_speed, buffer,
706 				sizeof(bigtime_t));
707 
708 		default:
709 			TRACE("ELANTECH: unknown opcode: %ld\n", op);
710 			return B_BAD_VALUE;
711 	}
712 }
713 
714 
715 static status_t
716 elantech_read(void* cookie, off_t pos, void* buffer, size_t* _length)
717 {
718 	*_length = 0;
719 	return B_NOT_ALLOWED;
720 }
721 
722 
723 static status_t
724 elantech_write(void* cookie, off_t pos, const void* buffer, size_t* _length)
725 {
726 	*_length = 0;
727 	return B_NOT_ALLOWED;
728 }
729 
730 
731 int32
732 elantech_handle_int(ps2_dev* dev)
733 {
734 	elantech_cookie* cookie = (elantech_cookie*)dev->cookie;
735 
736 	// we got a real event cancel the fake event
737 	gEventProducer.CancelEvent();
738 
739 	uint8 val;
740 	val = cookie->dev->history[0].data;
741  	cookie->buffer[cookie->packet_index] = val;
742 	cookie->packet_index++;
743 
744 	if (cookie->packet_index < PS2_PACKET_ELANTECH)
745 		return B_HANDLED_INTERRUPT;
746 
747 	cookie->packet_index = 0;
748 	if (packet_buffer_write(cookie->ring_buffer,
749 				cookie->buffer, cookie->dev->packet_size)
750 			!= cookie->dev->packet_size) {
751 			// buffer is full, drop new data
752 			return B_HANDLED_INTERRUPT;
753 		}
754 	release_sem_etc(cookie->sem, 1, B_DO_NOT_RESCHEDULE);
755 	return B_INVOKE_SCHEDULER;
756 }
757 
758 
759 void
760 elantech_disconnect(ps2_dev *dev)
761 {
762 	elantech_cookie *cookie = (elantech_cookie*)dev->cookie;
763 	// the mouse device might not be opened at this point
764 	INFO("ELANTECH: elantech_disconnect %s\n", dev->name);
765 	if ((dev->flags & PS2_FLAG_OPEN) != 0)
766 		release_sem(cookie->sem);
767 }
768 
769 
770 device_hooks gElantechDeviceHooks = {
771 	elantech_open,
772 	elantech_close,
773 	elantech_freecookie,
774 	elantech_ioctl,
775 	elantech_read,
776 	elantech_write,
777 };
778