2 * Copyright 2013, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
5 * Hardware specs taken from the linux driver, thanks a lot!
9 * Jérôme Duval <korli@users.berlios.de>
13 #include "ps2_elantech.h"
18 #include "ps2_service.h"
21 static int32
generate_event(timer
* timer
);
24 const bigtime_t kEventInterval
= 1000 * 50;
35 FireEvent(elantech_cookie
* cookie
, uint8
* package
)
38 memcpy(fLastPackage
, package
, sizeof(uint8
) * PS2_PACKET_ELANTECH
);
40 status_t status
= add_timer(&fEventTimer
, &generate_event
,
41 kEventInterval
, B_ONE_SHOT_RELATIVE_TIMER
);
53 return cancel_timer(&fEventTimer
);
59 if (packet_buffer_write(fCookie
->ring_buffer
, fLastPackage
,
60 PS2_PACKET_ELANTECH
) != PS2_PACKET_ELANTECH
) {
61 // buffer is full, drop new data
64 release_sem_etc(fCookie
->sem
, 1, B_DO_NOT_RESCHEDULE
);
69 uint8 fLastPackage
[PS2_PACKET_ELANTECH
];
71 elantech_cookie
* fCookie
;
75 static EventProducer gEventProducer
;
79 generate_event(timer
* timer
)
81 gEventProducer
.InjectEvent();
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"
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
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
106 // touchpad proportions
107 #define EDGE_MOTION_WIDTH 55
109 #define MIN_PRESSURE 0
110 #define REAL_MAX_PRESSURE 50
111 #define MAX_PRESSURE 255
113 #define ELANTECH_HISTORY_SIZE 256
116 static hardware_specs gHardwareSpecs
;
120 get_elantech_movement(elantech_cookie
*cookie
, mouse_movement
*movement
)
123 uint8 event_buffer
[PS2_PACKET_ELANTECH
];
125 status_t status
= acquire_sem_etc(cookie
->sem
, 1, B_CAN_INTERRUPT
, 0);
129 if (!cookie
->dev
->active
) {
130 TRACE("ELANTECH: read_event: Error device no longer active\n");
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");
140 if (cookie
->crcEnabled
&& (event_buffer
[3] & 0x08) != 0) {
141 TRACE("ELANTECH: bad crc buffer\n");
143 } else if (!cookie
->crcEnabled
&& ((event_buffer
[0] & 0x0c) != 0x04
144 || (event_buffer
[3] & 0x1c) != 0x10)) {
145 TRACE("ELANTECH: bad crc buffer\n");
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]);
156 cookie
->fingers
= event_buffer
[1] & 0x1f;
159 if ((((event_buffer
[3] & 0xe0) >> 5) - 1) != 0) {
160 // only process first finger
163 event
.zPressure
= (event_buffer
[1] & 0xf0)
164 | ((event_buffer
[4] & 0xf0) >> 4);
166 cookie
->previousZ
= event
.zPressure
;
168 event
.xPosition
= ((event_buffer
[1] & 0xf) << 8) | event_buffer
[2];
169 event
.yPosition
= (((event_buffer
[4] & 0xf) << 8)
171 TRACE("ELANTECH: buttons 0x%x x %ld y %ld z %d\n",
172 event
.buttons
, event
.xPosition
, event
.yPosition
,
176 TRACE("ELANTECH: packet type motion\n");
180 TRACE("ELANTECH: unknown packet type %d\n", type
);
185 // finger on touchpad
186 if ((cookie
->fingers
& 1) != 0) {
187 // finger with normal width
192 status
= cookie
->movementMaker
.EventToMovement(&event
, movement
);
194 if (cookie
->movementMaker
.WasEdgeMotion()
195 || cookie
->movementMaker
.TapDragStarted()) {
196 gEventProducer
.FireEvent(cookie
, event_buffer
);
204 default_settings(touchpad_settings
*set
)
206 memcpy(set
, &kDefaultTouchpadSettings
, sizeof(touchpad_settings
));
211 synaptics_dev_send_command(ps2_dev
* dev
, uint8 cmd
, uint8
*in
, int in_count
)
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
)
216 TRACE("ELANTECH: synaptics_dev_send_command failed\n");
224 elantech_dev_send_command(ps2_dev
* dev
, uint8 cmd
, uint8
*in
, int in_count
)
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
)
230 TRACE("ELANTECH: elantech_dev_send_command failed\n");
238 probe_elantech(ps2_dev
* dev
)
241 TRACE("ELANTECH: probe\n");
243 ps2_dev_command(dev
, PS2_CMD_MOUSE_RESET_DIS
);
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");
253 if (ps2_dev_command(dev
, PS2_CMD_MOUSE_GET_INFO
, NULL
, 0, val
, 3)
255 TRACE("ELANTECH: not found (2)\n");
259 if (val
[0] != 0x3c || val
[1] != 0x3 || (val
[2] != 0xc8 && val
[2] != 0x0)) {
260 TRACE("ELANTECH: not found (3)\n");
265 if (synaptics_dev_send_command(dev
, ELANTECH_CMD_GET_VERSION
, val
, 3)
267 TRACE("ELANTECH: not found (4)\n");
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");
277 INFO("Elantech version %02X%02X%02X, under developement! Using fallback.\n",
278 val
[0], val
[1], val
[2]);
280 dev
->name
= kElantechPath
[dev
->idx
];
281 dev
->packet_size
= PS2_PACKET_ELANTECH
;
288 elantech_write_reg(elantech_cookie
* cookie
, uint8 reg
, uint8 value
)
290 if (reg
< 0x7 || reg
> 0x26)
292 if (reg
> 0x11 && reg
< 0x20)
295 ps2_dev
* dev
= cookie
->dev
;
296 switch (cookie
->version
) {
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
)
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
)
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
)
334 TRACE("ELANTECH: read_write_reg: unknown version\n");
342 elantech_read_reg(elantech_cookie
* cookie
, uint8 reg
, uint8
*value
)
344 if (reg
< 0x7 || reg
> 0x26)
346 if (reg
> 0x11 && reg
< 0x20)
349 ps2_dev
* dev
= cookie
->dev
;
351 switch (cookie
->version
) {
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
,
367 if (ps2_dev_command(dev
, ELANTECH_CMD_PS2_CUSTOM_CMD
) != B_OK
368 || ps2_dev_command(dev
, ELANTECH_CMD_REGISTER_READWRITE
)
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
,
377 TRACE("ELANTECH: read_write_reg: unknown version\n");
380 if (cookie
->version
== 4)
390 get_resolution_v4(elantech_cookie
* cookie
, uint32
* x
, uint32
* y
)
393 if (elantech_dev_send_command(cookie
->dev
, ELANTECH_CMD_GET_RESOLUTION
,
396 *x
= (val
[1] & 0xf) * 10 + 790;
397 *y
= ((val
[1] & 0xf) >> 4) * 10 + 790;
403 get_range(elantech_cookie
* cookie
, uint32
* x_min
, uint32
* y_min
, uint32
* x_max
,
404 uint32
* y_max
, uint32
*width
)
407 switch (cookie
->version
) {
419 if ((cookie
->send_command
)(cookie
->dev
, ELANTECH_CMD_GET_ID
, val
, 3)
425 *x_max
= ((val
[0] & 0xf) << 8) | val
[1];
426 *y_max
= ((val
[0] & 0xf0) << 4) | val
[2];
430 if ((cookie
->send_command
)(cookie
->dev
, ELANTECH_CMD_GET_ID
, val
, 3)
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
)
440 *width
= *x_max
/ (cookie
->capabilities
[1] - 1);
448 enable_absolute_mode(elantech_cookie
* cookie
)
450 status_t status
= B_OK
;
451 switch (cookie
->version
) {
453 status
= elantech_write_reg(cookie
, 0x10, 0x16);
455 status
= elantech_write_reg(cookie
, 0x11, 0x8f);
458 status
= elantech_write_reg(cookie
, 0x10, 0x54);
460 status
= elantech_write_reg(cookie
, 0x11, 0x88);
462 status
= elantech_write_reg(cookie
, 0x12, 0x60);
465 status
= elantech_write_reg(cookie
, 0x10, 0xb);
468 status
= elantech_write_reg(cookie
, 0x7, 0x1);
473 if (cookie
->version
< 4) {
476 for (uint8 retry
= 0; retry
< 5; retry
++) {
477 status
= elantech_read_reg(cookie
, 0x10, &val
);
489 elantech_open(const char *name
, uint32 flags
, void **_cookie
)
491 TRACE("ELANTECH: open %s\n", name
);
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
];
502 TRACE("ps2: dev = NULL\n");
506 if (atomic_or(&dev
->flags
, PS2_FLAG_OPEN
) & PS2_FLAG_OPEN
)
509 elantech_cookie
* cookie
= (elantech_cookie
*)malloc(
510 sizeof(elantech_cookie
));
513 memset(cookie
, 0, sizeof(*cookie
));
515 cookie
->movementMaker
.Init();
516 cookie
->previousZ
= 0;
520 dev
->cookie
= cookie
;
521 dev
->disconnect
= &elantech_disconnect
;
522 dev
->handle_int
= &elantech_handle_int
;
524 default_settings(&cookie
->settings
);
526 dev
->packet_size
= PS2_PACKET_ELANTECH
;
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");
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");
543 if (synaptics_dev_send_command(dev
, ELANTECH_CMD_GET_VERSION
, val
, 3)
545 TRACE("ELANTECH: get version failed!\n");
548 cookie
->fwVersion
= (val
[0] << 16) | (val
[1] << 8) | val
[2];
549 if (cookie
->fwVersion
< 0x020030 || cookie
->fwVersion
== 0x020600)
552 switch (val
[0] & 0xf) {
565 TRACE("ELANTECH: unknown version!\n");
569 TRACE("ELANTECH: version 0x%lx (0x%lx)\n", cookie
->version
,
572 if (cookie
->version
>= 3)
573 cookie
->send_command
= &elantech_dev_send_command
;
575 cookie
->send_command
= &synaptics_dev_send_command
;
576 cookie
->crcEnabled
= (cookie
->fwVersion
& 0x4000) == 0x4000;
578 if ((cookie
->send_command
)(cookie
->dev
, ELANTECH_CMD_GET_CAPABILITIES
,
579 cookie
->capabilities
, 3) != B_OK
) {
580 TRACE("ELANTECH: get capabilities failed!\n");
584 if (enable_absolute_mode(cookie
) != B_OK
) {
585 TRACE("ELANTECH: failed enabling absolute mode!\n");
588 TRACE("ELANTECH: enabled absolute mode!\n");
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");
596 TRACE("ELANTECH: range x %ld-%ld y %ld-%ld (%ld)\n", x_min
, x_max
,
597 y_min
, y_max
, width
);
600 if (get_resolution_v4(cookie
, &x_res
, &y_res
) != B_OK
) {
601 TRACE("ELANTECH: get resolution failed!\n");
605 TRACE("ELANTECH: resolution x %ld y %ld (dpi)\n", x_res
, y_res
);
607 gHardwareSpecs
.edgeMotionWidth
= EDGE_MOTION_WIDTH
;
609 gHardwareSpecs
.areaStartX
= x_min
;
610 gHardwareSpecs
.areaEndX
= x_max
;
611 gHardwareSpecs
.areaStartY
= y_min
;
612 gHardwareSpecs
.areaEndY
= y_max
;
614 gHardwareSpecs
.minPressure
= MIN_PRESSURE
;
615 gHardwareSpecs
.realMaxPressure
= REAL_MAX_PRESSURE
;
616 gHardwareSpecs
.maxPressure
= MAX_PRESSURE
;
618 cookie
->movementMaker
.SetSettings(&cookie
->settings
);
619 cookie
->movementMaker
.SetSpecs(&gHardwareSpecs
);
621 if (ps2_dev_command(dev
, PS2_CMD_ENABLE
, NULL
, 0, NULL
, 0) != B_OK
)
624 atomic_or(&dev
->flags
, PS2_FLAG_ENABLED
);
626 TRACE("ELANTECH: open %s success\n", name
);
630 delete_sem(cookie
->sem
);
632 delete_packet_buffer(cookie
->ring_buffer
);
636 atomic_and(&dev
->flags
, ~PS2_FLAG_OPEN
);
638 TRACE("ELANTECH: open %s failed\n", name
);
644 elantech_close(void *_cookie
)
646 gEventProducer
.CancelEvent();
648 elantech_cookie
*cookie
= (elantech_cookie
*)_cookie
;
650 ps2_dev_command_timeout(cookie
->dev
, PS2_CMD_DISABLE
, NULL
, 0, NULL
, 0,
653 delete_packet_buffer(cookie
->ring_buffer
);
654 delete_sem(cookie
->sem
);
656 atomic_and(&cookie
->dev
->flags
, ~PS2_FLAG_OPEN
);
657 atomic_and(&cookie
->dev
->flags
, ~PS2_FLAG_ENABLED
);
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");
668 TRACE("ELANTECH: close %s done\n", cookie
->dev
->name
);
674 elantech_freecookie(void *_cookie
)
682 elantech_ioctl(void *_cookie
, uint32 op
, void *buffer
, size_t length
)
684 elantech_cookie
*cookie
= (elantech_cookie
*)_cookie
;
685 mouse_movement movement
;
690 TRACE("ELANTECH: MS_READ get event\n");
691 if ((status
= get_elantech_movement(cookie
, &movement
)) != B_OK
)
693 return user_memcpy(buffer
, &movement
, sizeof(movement
));
696 TRACE("ELANTECH: MS_IS_TOUCHPAD\n");
699 case MS_SET_TOUCHPAD_SETTINGS
:
700 TRACE("ELANTECH: MS_SET_TOUCHPAD_SETTINGS");
701 user_memcpy(&cookie
->settings
, buffer
, sizeof(touchpad_settings
));
704 case MS_SET_CLICKSPEED
:
705 TRACE("ELANTECH: ioctl MS_SETCLICK (set click speed)\n");
706 return user_memcpy(&cookie
->movementMaker
.click_speed
, buffer
,
710 TRACE("ELANTECH: unknown opcode: %ld\n", op
);
717 elantech_read(void* cookie
, off_t pos
, void* buffer
, size_t* _length
)
720 return B_NOT_ALLOWED
;
725 elantech_write(void* cookie
, off_t pos
, const void* buffer
, size_t* _length
)
728 return B_NOT_ALLOWED
;
733 elantech_handle_int(ps2_dev
* dev
)
735 elantech_cookie
* cookie
= (elantech_cookie
*)dev
->cookie
;
737 // we got a real event cancel the fake event
738 gEventProducer
.CancelEvent();
741 val
= cookie
->dev
->history
[0].data
;
742 cookie
->buffer
[cookie
->packet_index
] = val
;
743 cookie
->packet_index
++;
745 if (cookie
->packet_index
< PS2_PACKET_ELANTECH
)
746 return B_HANDLED_INTERRUPT
;
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
;
755 release_sem_etc(cookie
->sem
, 1, B_DO_NOT_RESCHEDULE
);
756 return B_INVOKE_SCHEDULER
;
761 elantech_disconnect(ps2_dev
*dev
)
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
);
771 device_hooks gElantechDeviceHooks
= {