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
31const 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
64static touchpad_specs gHardwareSpecs;
65
66
67static status_t
68get_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
156static status_t
157synaptics_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
169static status_t
170elantech_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
183status_t
184probe_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
233static status_t
234elantech_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
287static status_t
288elantech_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
335static status_t
336get_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
348static status_t
349get_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
393static status_t
394enable_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
434status_t
435elantech_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
571err4:
572	delete_sem(cookie->sem);
573err3:
574	delete_packet_buffer(cookie->ring_buffer);
575err2:
576	free(cookie);
577err1:
578	atomic_and(&dev->flags, ~PS2_FLAG_OPEN);
579
580	TRACE("ELANTECH: open %s failed\n", name);
581	return B_ERROR;
582}
583
584
585status_t
586elantech_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
613status_t
614elantech_freecookie(void *_cookie)
615{
616	free(_cookie);
617	return B_OK;
618}
619
620
621status_t
622elantech_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
652static status_t
653elantech_read(void* cookie, off_t pos, void* buffer, size_t* _length)
654{
655	*_length = 0;
656	return B_NOT_ALLOWED;
657}
658
659
660static status_t
661elantech_write(void* cookie, off_t pos, const void* buffer, size_t* _length)
662{
663	*_length = 0;
664	return B_NOT_ALLOWED;
665}
666
667
668int32
669elantech_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
693void
694elantech_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
704device_hooks gElantechDeviceHooks = {
705	elantech_open,
706	elantech_close,
707	elantech_freecookie,
708	elantech_ioctl,
709	elantech_read,
710	elantech_write,
711};
712