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