Fix #8729.
[haiku.git] / src / add-ons / kernel / bus_managers / ps2 / ps2_synaptics.cpp
blob06232fef04bb1cc756ebab742a3335f10f2e403a
1 /*
2 * Copyright 2008-2010, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
5 * Authors (in chronological order):
6 * Clemens Zeidler (haiku@Clemens-Zeidler.de)
7 * Axel Dörfler, axeld@pinc-software.de
8 */
11 //! PS/2 synaptics touchpad
14 #include "ps2_synaptics.h"
16 #include <string.h>
17 #include <stdlib.h>
19 #include <keyboard_mouse_driver.h>
21 #include "ps2_service.h"
24 // synaptics touchpad proportions
25 #define SYN_EDGE_MOTION_WIDTH 50
26 #define SYN_AREA_OFFSET 40
28 #define MIN_PRESSURE 30
29 #define REAL_MAX_PRESSURE 100
30 #define MAX_PRESSURE 200
33 static hardware_specs gHardwareSpecs;
36 const char* kSynapticsPath[4] = {
37 "input/touchpad/ps2/synaptics_0",
38 "input/touchpad/ps2/synaptics_1",
39 "input/touchpad/ps2/synaptics_2",
40 "input/touchpad/ps2/synaptics_3"
44 static touchpad_info sTouchpadInfo;
45 static ps2_dev *sPassthroughDevice = &ps2_device[PS2_DEVICE_SYN_PASSTHROUGH];
48 static void
49 default_settings(touchpad_settings *set)
51 memcpy(set, &kDefaultTouchpadSettings, sizeof(touchpad_settings));
55 static status_t
56 send_touchpad_arg_timeout(ps2_dev *dev, uint8 arg, bigtime_t timeout)
58 int8 i;
59 uint8 val[8];
60 for (i = 0; i < 4; i++) {
61 val[2 * i] = (arg >> (6 - 2 * i)) & 3;
62 val[2 * i + 1] = 0xE8;
64 return ps2_dev_command_timeout(dev, 0xE8, val, 7, NULL, 0, timeout);
68 static status_t
69 send_touchpad_arg(ps2_dev *dev, uint8 arg)
71 return send_touchpad_arg_timeout(dev, arg, 4000000);
75 static status_t
76 set_touchpad_mode(ps2_dev *dev, uint8 mode)
78 uint8 sample_rate = SYN_CHANGE_MODE;
79 send_touchpad_arg(dev, mode);
80 return ps2_dev_command(dev, PS2_CMD_SET_SAMPLE_RATE, &sample_rate, 1,
81 NULL, 0);
85 static status_t
86 get_synaptics_movment(synaptics_cookie *cookie, mouse_movement *movement)
88 status_t status;
89 touch_event event;
90 uint8 event_buffer[PS2_MAX_PACKET_SIZE];
91 uint8 wValue0, wValue1, wValue2, wValue3, wValue;
92 uint32 val32;
93 uint32 xTwelfBit, yTwelfBit;
95 status = acquire_sem_etc(cookie->synaptics_sem, 1, B_CAN_INTERRUPT, 0);
96 if (status < B_OK)
97 return status;
99 if (!cookie->dev->active) {
100 TRACE("SYNAPTICS: read_event: Error device no longer active\n");
101 return B_ERROR;
104 if (packet_buffer_read(cookie->synaptics_ring_buffer, event_buffer,
105 cookie->dev->packet_size) != cookie->dev->packet_size) {
106 TRACE("SYNAPTICS: error copying buffer\n");
107 return B_ERROR;
110 event.buttons = event_buffer[0] & 3;
111 event.zPressure = event_buffer[2];
113 if (sTouchpadInfo.capExtended) {
114 wValue0 = event_buffer[3] >> 2 & 1;
115 wValue1 = event_buffer[0] >> 2 & 1;
116 wValue2 = event_buffer[0] >> 4 & 1;
117 wValue3 = event_buffer[0] >> 5 & 1;
119 wValue = wValue0;
120 wValue = wValue | (wValue1 << 1);
121 wValue = wValue | (wValue2 << 2);
122 wValue = wValue | (wValue3 << 3);
124 event.wValue = wValue;
125 event.gesture = false;
127 if (sTouchpadInfo.capMiddleButton || sTouchpadInfo.capFourButtons)
128 event.buttons |= ((event_buffer[0] ^ event_buffer[3]) & 0x01) << 2;
129 } else {
130 bool finger = event_buffer[0] >> 5 & 1;
131 if (finger) {
132 // finger with normal width
133 event.wValue = 4;
135 event.gesture = event_buffer[0] >> 2 & 1;
138 event.xPosition = event_buffer[4];
139 event.yPosition = event_buffer[5];
141 val32 = event_buffer[1] & 0x0F;
142 event.xPosition += val32 << 8;
143 val32 = event_buffer[1] >> 4 & 0x0F;
144 event.yPosition += val32 << 8;
146 xTwelfBit = event_buffer[3] >> 4 & 1;
147 event.xPosition += xTwelfBit << 12;
148 yTwelfBit = event_buffer[3] >> 5 & 1;
149 event.yPosition += yTwelfBit << 12;
151 status = cookie->movementMaker.EventToMovement(&event, movement);
153 return status;
157 static void
158 query_capability(ps2_dev *dev)
160 uint8 val[3];
161 send_touchpad_arg(dev, 0x02);
162 ps2_dev_command(dev, 0xE9, NULL, 0, val, 3);
164 sTouchpadInfo.capExtended = val[0] >> 7 & 1;
165 TRACE("SYNAPTICS: extended mode %2x\n", val[0] >> 7 & 1);
166 TRACE("SYNAPTICS: middle button %2x\n", val[0] >> 2 & 1);
167 sTouchpadInfo.capMiddleButton = val[0] >> 2 & 1;
168 TRACE("SYNAPTICS: sleep mode %2x\n", val[2] >> 4 & 1);
169 sTouchpadInfo.capSleep = val[2] >> 4 & 1;
170 TRACE("SYNAPTICS: four buttons %2x\n", val[2] >> 3 & 1);
171 sTouchpadInfo.capFourButtons = val[2] >> 3 & 1;
172 TRACE("SYNAPTICS: multi finger %2x\n", val[2] >> 1 & 1);
173 sTouchpadInfo.capMultiFinger = val[2] >> 1 & 1;
174 TRACE("SYNAPTICS: palm detection %2x\n", val[2] & 1);
175 sTouchpadInfo.capPalmDetection = val[2] & 1;
176 TRACE("SYNAPTICS: pass through %2x\n", val[2] >> 7 & 1);
177 sTouchpadInfo.capPassThrough = val[2] >> 7 & 1;
181 // #pragma mark - exported functions
184 status_t
185 synaptics_pass_through_set_packet_size(ps2_dev *dev, uint8 size)
187 synaptics_cookie *synapticsCookie
188 = (synaptics_cookie*)dev->parent_dev->cookie;
190 status_t status = ps2_dev_command(dev->parent_dev, PS2_CMD_DISABLE, NULL,
191 0, NULL, 0);
192 if (status < B_OK) {
193 INFO("SYNAPTICS: cannot disable touchpad %s\n", dev->parent_dev->name);
194 return B_ERROR;
197 synapticsCookie->packet_index = 0;
199 if (size == 4)
200 synapticsCookie->mode |= SYN_FOUR_BYTE_CHILD;
201 else
202 synapticsCookie->mode &= ~SYN_FOUR_BYTE_CHILD;
204 set_touchpad_mode(dev->parent_dev, synapticsCookie->mode);
206 status = ps2_dev_command(dev->parent_dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0);
207 if (status < B_OK) {
208 INFO("SYNAPTICS: cannot enable touchpad %s\n", dev->parent_dev->name);
209 return B_ERROR;
211 return status;
215 status_t
216 passthrough_command(ps2_dev *dev, uint8 cmd, const uint8 *out, int outCount,
217 uint8 *in, int inCount, bigtime_t timeout)
219 status_t status;
220 uint8 passThroughCmd = SYN_PASSTHROUGH_CMD;
221 uint8 val;
222 uint32 passThroughInCount = (inCount + 1) * 6;
223 uint8 passThroughIn[passThroughInCount];
224 int8 i;
226 TRACE("SYNAPTICS: passthrough command 0x%x\n", cmd);
228 status = ps2_dev_command(dev->parent_dev, PS2_CMD_DISABLE, NULL, 0,
229 NULL, 0);
230 if (status != B_OK)
231 return status;
233 for (i = -1; i < outCount; i++) {
234 if (i == -1)
235 val = cmd;
236 else
237 val = out[i];
238 status = send_touchpad_arg_timeout(dev->parent_dev, val, timeout);
239 if (status != B_OK)
240 goto finalize;
241 if (i != outCount -1) {
242 status = ps2_dev_command_timeout(dev->parent_dev,
243 PS2_CMD_SET_SAMPLE_RATE, &passThroughCmd, 1, NULL, 0, timeout);
244 if (status != B_OK)
245 goto finalize;
248 status = ps2_dev_command_timeout(dev->parent_dev, PS2_CMD_SET_SAMPLE_RATE,
249 &passThroughCmd, 1, passThroughIn, passThroughInCount, timeout);
250 if (status != B_OK)
251 goto finalize;
253 for (i = 0; i < inCount + 1; i++) {
254 uint8 *inPointer = &passThroughIn[i * 6];
255 if (!IS_SYN_PT_PACKAGE(inPointer)) {
256 TRACE("SYNAPTICS: not a pass throught package\n");
257 goto finalize;
259 if (i == 0)
260 continue;
262 in[i - 1] = passThroughIn[i * 6 + 1];
265 finalize:
266 status_t statusOfEnable = ps2_dev_command(dev->parent_dev, PS2_CMD_ENABLE,
267 NULL, 0, NULL, 0);
268 if (statusOfEnable != B_OK)
269 TRACE("SYNAPTICS: enabling of parent failed: 0x%lx.\n", statusOfEnable);
271 return status != B_OK ? status : statusOfEnable;
275 status_t
276 probe_synaptics(ps2_dev *dev)
278 uint8 val[3];
279 uint8 deviceId;
280 status_t status;
281 TRACE("SYNAPTICS: probe\n");
283 status = send_touchpad_arg(dev, 0x00);
284 if (status != B_OK)
285 return status;
286 status = ps2_dev_command(dev, 0xE9, NULL, 0, val, 3);
287 if (status != B_OK)
288 return status;
290 sTouchpadInfo.minorVersion = val[0];
291 deviceId = val[1];
292 if (deviceId != SYN_TOUCHPAD) {
293 TRACE("SYNAPTICS: not found\n");
294 return B_ERROR;
297 TRACE("SYNAPTICS: Touchpad found id:l %2x\n", deviceId);
298 sTouchpadInfo.majorVersion = val[2] & 0x0F;
299 TRACE("SYNAPTICS: version %d.%d\n", sTouchpadInfo.majorVersion,
300 sTouchpadInfo.minorVersion);
301 // version >= 4.0?
302 if (sTouchpadInfo.minorVersion <= 2
303 && sTouchpadInfo.majorVersion <= 3) {
304 TRACE("SYNAPTICS: too old touchpad not supported\n");
305 return B_ERROR;
307 dev->name = kSynapticsPath[dev->idx];
308 return B_OK;
312 // #pragma mark - Device functions
315 status_t
316 synaptics_open(const char *name, uint32 flags, void **_cookie)
318 status_t status;
319 synaptics_cookie *cookie;
320 ps2_dev *dev;
321 int i;
323 for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) {
324 if (0 == strcmp(ps2_device[i].name, name)) {
325 dev = &ps2_device[i];
326 break;
330 if (dev == NULL) {
331 TRACE("ps2: dev = NULL\n");
332 return B_ERROR;
335 if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN)
336 return B_BUSY;
338 cookie = (synaptics_cookie*)malloc(sizeof(synaptics_cookie));
339 if (cookie == NULL)
340 goto err1;
341 memset(cookie, 0, sizeof(*cookie));
343 cookie->movementMaker.Init();
344 *_cookie = cookie;
346 cookie->dev = dev;
347 dev->cookie = cookie;
348 dev->disconnect = &synaptics_disconnect;
349 dev->handle_int = &synaptics_handle_int;
351 default_settings(&cookie->settings);
353 gHardwareSpecs.edgeMotionWidth = SYN_EDGE_MOTION_WIDTH;
355 gHardwareSpecs.areaStartX = SYN_AREA_START_X;
356 gHardwareSpecs.areaEndX = SYN_AREA_END_X;
357 gHardwareSpecs.areaStartY = SYN_AREA_START_Y;
358 gHardwareSpecs.areaEndY = SYN_AREA_END_Y;
360 gHardwareSpecs.minPressure = MIN_PRESSURE;
361 gHardwareSpecs.realMaxPressure = REAL_MAX_PRESSURE;
362 gHardwareSpecs.maxPressure = MAX_PRESSURE;
364 cookie->movementMaker.SetSettings(&cookie->settings);
365 cookie->movementMaker.SetSpecs(&gHardwareSpecs);
367 dev->packet_size = PS2_PACKET_SYNAPTICS;
369 cookie->synaptics_ring_buffer
370 = create_packet_buffer(SYNAPTICS_HISTORY_SIZE * dev->packet_size);
371 if (cookie->synaptics_ring_buffer == NULL) {
372 TRACE("ps2: can't allocate mouse actions buffer\n");
373 goto err2;
376 // create the mouse semaphore, used for synchronization between
377 // the interrupt handler and the read operation
378 cookie->synaptics_sem = create_sem(0, "ps2_synaptics_sem");
379 if (cookie->synaptics_sem < 0) {
380 TRACE("SYNAPTICS: failed creating semaphore!\n");
381 goto err3;
383 query_capability(dev);
385 // create pass through dev
386 if (sTouchpadInfo.capPassThrough) {
387 TRACE("SYNAPTICS: pass through detected\n");
388 sPassthroughDevice->parent_dev = dev;
389 sPassthroughDevice->idx = dev->idx;
390 ps2_service_notify_device_added(sPassthroughDevice);
393 // Set Mode
394 if (sTouchpadInfo.capExtended)
395 cookie->mode = SYN_ABSOLUTE_W_MODE;
396 else
397 cookie->mode = SYN_ABSOLUTE_MODE;
399 status = set_touchpad_mode(dev, cookie->mode);
400 if (status < B_OK) {
401 INFO("SYNAPTICS: cannot set mode %s\n", name);
402 goto err4;
405 status = ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0);
406 if (status < B_OK) {
407 INFO("SYNAPTICS: cannot enable touchpad %s\n", name);
408 goto err4;
411 atomic_or(&dev->flags, PS2_FLAG_ENABLED);
413 TRACE("SYNAPTICS: open %s success\n", name);
414 return B_OK;
416 err4:
417 delete_sem(cookie->synaptics_sem);
418 err3:
419 delete_packet_buffer(cookie->synaptics_ring_buffer);
420 err2:
421 free(cookie);
422 err1:
423 atomic_and(&dev->flags, ~PS2_FLAG_OPEN);
425 TRACE("SYNAPTICS: synaptics_open %s failed\n", name);
426 return B_ERROR;
430 status_t
431 synaptics_close(void *_cookie)
433 status_t status;
434 synaptics_cookie *cookie = (synaptics_cookie*)_cookie;
436 ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0,
437 150000);
439 delete_packet_buffer(cookie->synaptics_ring_buffer);
440 delete_sem(cookie->synaptics_sem);
442 atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN);
443 atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED);
445 // Reset the touchpad so it generate standard ps2 packets instead of
446 // extended ones. If not, BeOS is confused with such packets when rebooting
447 // without a complete shutdown.
448 status = ps2_reset_mouse(cookie->dev);
449 if (status != B_OK) {
450 INFO("ps2: reset failed\n");
451 return B_ERROR;
454 if (sTouchpadInfo.capPassThrough)
455 ps2_service_notify_device_removed(sPassthroughDevice);
457 TRACE("SYNAPTICS: close %s done\n", cookie->dev->name);
458 return B_OK;
462 status_t
463 synaptics_freecookie(void *_cookie)
465 free(_cookie);
466 return B_OK;
470 static status_t
471 synaptics_read(void *cookie, off_t pos, void *buffer, size_t *_length)
473 *_length = 0;
474 return B_NOT_ALLOWED;
478 static status_t
479 synaptics_write(void *cookie, off_t pos, const void *buffer, size_t *_length)
481 *_length = 0;
482 return B_NOT_ALLOWED;
486 status_t
487 synaptics_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
489 synaptics_cookie *cookie = (synaptics_cookie*)_cookie;
490 mouse_movement movement;
491 status_t status;
493 switch (op) {
494 case MS_READ:
495 TRACE("SYNAPTICS: MS_READ get event\n");
496 if ((status = get_synaptics_movment(cookie, &movement)) != B_OK)
497 return status;
498 return user_memcpy(buffer, &movement, sizeof(movement));
500 case MS_IS_TOUCHPAD:
501 TRACE("SYNAPTICS: MS_IS_TOUCHPAD\n");
502 return B_OK;
504 case MS_SET_TOUCHPAD_SETTINGS:
505 TRACE("SYNAPTICS: MS_SET_TOUCHPAD_SETTINGS");
506 user_memcpy(&cookie->settings, buffer, sizeof(touchpad_settings));
507 return B_OK;
509 case MS_SET_CLICKSPEED:
510 TRACE("SYNAPTICS: ioctl MS_SETCLICK (set click speed)\n");
511 return user_memcpy(&cookie->movementMaker.click_speed, buffer,
512 sizeof(bigtime_t));
514 default:
515 TRACE("SYNAPTICS: unknown opcode: %ld\n", op);
516 return B_DEV_INVALID_IOCTL;
521 int32
522 synaptics_handle_int(ps2_dev *dev)
524 synaptics_cookie *cookie = (synaptics_cookie*)dev->cookie;
525 uint8 val;
527 val = cookie->dev->history[0].data;
529 if ((cookie->packet_index == 0 || cookie->packet_index == 3)
530 && (val & 8) != 0) {
531 INFO("SYNAPTICS: bad mouse data, trying resync\n");
532 cookie->packet_index = 0;
533 return B_UNHANDLED_INTERRUPT;
535 if (cookie->packet_index == 0 && val >> 6 != 0x02) {
536 TRACE("SYNAPTICS: first package begins not with bit 1, 0\n");
537 return B_UNHANDLED_INTERRUPT;
539 if (cookie->packet_index == 3 && val >> 6 != 0x03) {
540 TRACE("SYNAPTICS: third package begins not with bit 1, 1\n");
541 cookie->packet_index = 0;
542 return B_UNHANDLED_INTERRUPT;
544 cookie->buffer[cookie->packet_index] = val;
546 cookie->packet_index++;
547 if (cookie->packet_index >= 6) {
548 cookie->packet_index = 0;
550 // check if package is a pass through package if true pass it
551 // too the pass through interrupt handle
552 if (sPassthroughDevice->active
553 && sPassthroughDevice->handle_int != NULL
554 && IS_SYN_PT_PACKAGE(cookie->buffer)) {
555 status_t status;
557 sPassthroughDevice->history[0].data = cookie->buffer[1];
558 sPassthroughDevice->handle_int(sPassthroughDevice);
559 sPassthroughDevice->history[0].data = cookie->buffer[4];
560 sPassthroughDevice->handle_int(sPassthroughDevice);
561 sPassthroughDevice->history[0].data = cookie->buffer[5];
562 status = sPassthroughDevice->handle_int(sPassthroughDevice);
564 if (cookie->dev->packet_size == 4) {
565 sPassthroughDevice->history[0].data = cookie->buffer[2];
566 status = sPassthroughDevice->handle_int(sPassthroughDevice);
568 return status;
571 if (packet_buffer_write(cookie->synaptics_ring_buffer,
572 cookie->buffer, cookie->dev->packet_size)
573 != cookie->dev->packet_size) {
574 // buffer is full, drop new data
575 return B_HANDLED_INTERRUPT;
577 release_sem_etc(cookie->synaptics_sem, 1, B_DO_NOT_RESCHEDULE);
579 return B_INVOKE_SCHEDULER;
582 return B_HANDLED_INTERRUPT;
586 void
587 synaptics_disconnect(ps2_dev *dev)
589 synaptics_cookie *cookie = (synaptics_cookie*)dev->cookie;
590 // the mouse device might not be opened at this point
591 INFO("SYNAPTICS: synaptics_disconnect %s\n", dev->name);
592 if ((dev->flags & PS2_FLAG_OPEN) != 0)
593 release_sem(cookie->synaptics_sem);
597 device_hooks gSynapticsDeviceHooks = {
598 synaptics_open,
599 synaptics_close,
600 synaptics_freecookie,
601 synaptics_ioctl,
602 synaptics_read,
603 synaptics_write,