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
get_elantech_movement(elantech_cookie * cookie,touchpad_movement * _event,bigtime_t timeout)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
synaptics_dev_send_command(ps2_dev * dev,uint8 cmd,uint8 * in,int in_count)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
elantech_dev_send_command(ps2_dev * dev,uint8 cmd,uint8 * in,int in_count)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
probe_elantech(ps2_dev * dev)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
elantech_write_reg(elantech_cookie * cookie,uint8 reg,uint8 value)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
elantech_read_reg(elantech_cookie * cookie,uint8 reg,uint8 * value)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
get_resolution_v4(elantech_cookie * cookie,uint32 * x,uint32 * y)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
get_range(elantech_cookie * cookie,uint32 * x_min,uint32 * y_min,uint32 * x_max,uint32 * y_max,uint32 * width)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
enable_absolute_mode(elantech_cookie * cookie)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
elantech_open(const char * name,uint32 flags,void ** _cookie)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
elantech_close(void * _cookie)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
elantech_freecookie(void * _cookie)614 elantech_freecookie(void *_cookie)
615 {
616 free(_cookie);
617 return B_OK;
618 }
619
620
621 status_t
elantech_ioctl(void * _cookie,uint32 op,void * buffer,size_t length)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
elantech_read(void * cookie,off_t pos,void * buffer,size_t * _length)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
elantech_write(void * cookie,off_t pos,const void * buffer,size_t * _length)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
elantech_handle_int(ps2_dev * dev)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
elantech_disconnect(ps2_dev * dev)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