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
11 //! PS/2 synaptics touchpad
14 #include "ps2_synaptics.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
];
49 default_settings(touchpad_settings
*set
)
51 memcpy(set
, &kDefaultTouchpadSettings
, sizeof(touchpad_settings
));
56 send_touchpad_arg_timeout(ps2_dev
*dev
, uint8 arg
, bigtime_t timeout
)
61 for (i
= 0; i
< 4; i
++) {
62 val
[2 * i
] = (arg
>> (6 - 2 * i
)) & 3;
63 val
[2 * i
+ 1] = 0xE8;
65 return ps2_dev_command_timeout(dev
, 0xE8, val
, 7, NULL
, 0, timeout
);
70 send_touchpad_arg(ps2_dev
*dev
, uint8 arg
)
72 return send_touchpad_arg_timeout(dev
, arg
, 4000000);
77 set_touchpad_mode(ps2_dev
*dev
, uint8 mode
)
79 uint8 sample_rate
= SYN_CHANGE_MODE
;
80 send_touchpad_arg(dev
, mode
);
81 return ps2_dev_command(dev
, PS2_CMD_SET_SAMPLE_RATE
, &sample_rate
, 1,
87 get_synaptics_movment(synaptics_cookie
*cookie
, mouse_movement
*movement
)
91 uint8 event_buffer
[PS2_MAX_PACKET_SIZE
];
92 uint8 wValue0
, wValue1
, wValue2
, wValue3
, wValue
;
94 uint32 xTwelfBit
, yTwelfBit
;
96 status
= acquire_sem_etc(cookie
->synaptics_sem
, 1, B_CAN_INTERRUPT
, 0);
100 if (!cookie
->dev
->active
) {
101 TRACE("SYNAPTICS: read_event: Error device no longer active\n");
105 if (packet_buffer_read(cookie
->synaptics_ring_buffer
, event_buffer
,
106 cookie
->dev
->packet_size
) != cookie
->dev
->packet_size
) {
107 TRACE("SYNAPTICS: error copying buffer\n");
111 event
.buttons
= event_buffer
[0] & 3;
112 event
.zPressure
= event_buffer
[2];
114 if (sTouchpadInfo
.capExtended
) {
115 wValue0
= event_buffer
[3] >> 2 & 1;
116 wValue1
= event_buffer
[0] >> 2 & 1;
117 wValue2
= event_buffer
[0] >> 4 & 1;
118 wValue3
= event_buffer
[0] >> 5 & 1;
121 wValue
= wValue
| (wValue1
<< 1);
122 wValue
= wValue
| (wValue2
<< 2);
123 wValue
= wValue
| (wValue3
<< 3);
125 event
.wValue
= wValue
;
126 event
.gesture
= false;
128 if (sTouchpadInfo
.capMiddleButton
|| sTouchpadInfo
.capFourButtons
)
129 event
.buttons
|= ((event_buffer
[0] ^ event_buffer
[3]) & 0x01) << 2;
131 bool finger
= event_buffer
[0] >> 5 & 1;
133 // finger with normal width
136 event
.gesture
= event_buffer
[0] >> 2 & 1;
139 event
.xPosition
= event_buffer
[4];
140 event
.yPosition
= event_buffer
[5];
142 val32
= event_buffer
[1] & 0x0F;
143 event
.xPosition
+= val32
<< 8;
144 val32
= event_buffer
[1] >> 4 & 0x0F;
145 event
.yPosition
+= val32
<< 8;
147 xTwelfBit
= event_buffer
[3] >> 4 & 1;
148 event
.xPosition
+= xTwelfBit
<< 12;
149 yTwelfBit
= event_buffer
[3] >> 5 & 1;
150 event
.yPosition
+= yTwelfBit
<< 12;
152 status
= cookie
->movementMaker
.EventToMovement(&event
, movement
);
159 query_capability(ps2_dev
*dev
)
162 send_touchpad_arg(dev
, 0x02);
163 ps2_dev_command(dev
, 0xE9, NULL
, 0, val
, 3);
165 sTouchpadInfo
.capExtended
= val
[0] >> 7 & 1;
166 TRACE("SYNAPTICS: extended mode %2x\n", val
[0] >> 7 & 1);
167 TRACE("SYNAPTICS: middle button %2x\n", val
[0] >> 2 & 1);
168 sTouchpadInfo
.capMiddleButton
= val
[0] >> 2 & 1;
169 TRACE("SYNAPTICS: sleep mode %2x\n", val
[2] >> 4 & 1);
170 sTouchpadInfo
.capSleep
= val
[2] >> 4 & 1;
171 TRACE("SYNAPTICS: four buttons %2x\n", val
[2] >> 3 & 1);
172 sTouchpadInfo
.capFourButtons
= val
[2] >> 3 & 1;
173 TRACE("SYNAPTICS: multi finger %2x\n", val
[2] >> 1 & 1);
174 sTouchpadInfo
.capMultiFinger
= val
[2] >> 1 & 1;
175 TRACE("SYNAPTICS: palm detection %2x\n", val
[2] & 1);
176 sTouchpadInfo
.capPalmDetection
= val
[2] & 1;
177 TRACE("SYNAPTICS: pass through %2x\n", val
[2] >> 7 & 1);
178 sTouchpadInfo
.capPassThrough
= val
[2] >> 7 & 1;
182 // #pragma mark - exported functions
186 synaptics_pass_through_set_packet_size(ps2_dev
*dev
, uint8 size
)
188 synaptics_cookie
*synapticsCookie
189 = (synaptics_cookie
*)dev
->parent_dev
->cookie
;
191 status_t status
= ps2_dev_command(dev
->parent_dev
, PS2_CMD_DISABLE
, NULL
,
194 INFO("SYNAPTICS: cannot disable touchpad %s\n", dev
->parent_dev
->name
);
198 synapticsCookie
->packet_index
= 0;
201 synapticsCookie
->mode
|= SYN_FOUR_BYTE_CHILD
;
203 synapticsCookie
->mode
&= ~SYN_FOUR_BYTE_CHILD
;
205 set_touchpad_mode(dev
->parent_dev
, synapticsCookie
->mode
);
207 status
= ps2_dev_command(dev
->parent_dev
, PS2_CMD_ENABLE
, NULL
, 0, NULL
, 0);
209 INFO("SYNAPTICS: cannot enable touchpad %s\n", dev
->parent_dev
->name
);
217 passthrough_command(ps2_dev
*dev
, uint8 cmd
, const uint8
*out
, int outCount
,
218 uint8
*in
, int inCount
, bigtime_t timeout
)
221 uint8 passThroughCmd
= SYN_PASSTHROUGH_CMD
;
223 uint32 passThroughInCount
= (inCount
+ 1) * 6;
224 uint8 passThroughIn
[passThroughInCount
];
227 TRACE("SYNAPTICS: passthrough command 0x%x\n", cmd
);
229 status
= ps2_dev_command(dev
->parent_dev
, PS2_CMD_DISABLE
, NULL
, 0,
234 for (i
= -1; i
< outCount
; i
++) {
239 status
= send_touchpad_arg_timeout(dev
->parent_dev
, val
, timeout
);
242 if (i
!= outCount
-1) {
243 status
= ps2_dev_command_timeout(dev
->parent_dev
,
244 PS2_CMD_SET_SAMPLE_RATE
, &passThroughCmd
, 1, NULL
, 0, timeout
);
249 status
= ps2_dev_command_timeout(dev
->parent_dev
, PS2_CMD_SET_SAMPLE_RATE
,
250 &passThroughCmd
, 1, passThroughIn
, passThroughInCount
, timeout
);
254 for (i
= 0; i
< inCount
+ 1; i
++) {
255 uint8
*inPointer
= &passThroughIn
[i
* 6];
256 if (!IS_SYN_PT_PACKAGE(inPointer
)) {
257 TRACE("SYNAPTICS: not a pass throught package\n");
263 in
[i
- 1] = passThroughIn
[i
* 6 + 1];
267 status_t statusOfEnable
= ps2_dev_command(dev
->parent_dev
, PS2_CMD_ENABLE
,
269 if (statusOfEnable
!= B_OK
)
270 TRACE("SYNAPTICS: enabling of parent failed: 0x%lx.\n", statusOfEnable
);
272 return status
!= B_OK
? status
: statusOfEnable
;
277 probe_synaptics(ps2_dev
*dev
)
282 TRACE("SYNAPTICS: probe\n");
284 // We reset the device here because it may have been left in a confused
285 // state by a previous probing attempt. Some synaptics touchpads are known
286 // to lockup when we attempt to detect them as IBM trackpoints.
287 ps2_reset_mouse(dev
);
289 // Request "Identify touchpad"
290 // The touchpad will delay this, until it's ready and calibrated.
291 status
= send_touchpad_arg(dev
, 0x00);
295 // "Status request" (executes "Identify touchpad")
296 status
= ps2_dev_command(dev
, 0xE9, NULL
, 0, val
, 3);
300 sTouchpadInfo
.minorVersion
= val
[0];
302 if (deviceId
!= SYN_TOUCHPAD
) {
303 TRACE("SYNAPTICS: not found\n");
307 TRACE("SYNAPTICS: Touchpad found id:l %2x\n", deviceId
);
308 sTouchpadInfo
.majorVersion
= val
[2] & 0x0F;
309 TRACE("SYNAPTICS: version %d.%d\n", sTouchpadInfo
.majorVersion
,
310 sTouchpadInfo
.minorVersion
);
312 if (sTouchpadInfo
.minorVersion
<= 2
313 && sTouchpadInfo
.majorVersion
<= 3) {
314 TRACE("SYNAPTICS: too old touchpad not supported\n");
317 dev
->name
= kSynapticsPath
[dev
->idx
];
322 // #pragma mark - Device functions
326 synaptics_open(const char *name
, uint32 flags
, void **_cookie
)
329 synaptics_cookie
*cookie
;
333 for (dev
= NULL
, i
= 0; i
< PS2_DEVICE_COUNT
; i
++) {
334 if (0 == strcmp(ps2_device
[i
].name
, name
)) {
335 dev
= &ps2_device
[i
];
341 TRACE("ps2: dev = NULL\n");
345 if (atomic_or(&dev
->flags
, PS2_FLAG_OPEN
) & PS2_FLAG_OPEN
)
348 cookie
= (synaptics_cookie
*)malloc(sizeof(synaptics_cookie
));
351 memset(cookie
, 0, sizeof(*cookie
));
353 cookie
->movementMaker
.Init();
357 dev
->cookie
= cookie
;
358 dev
->disconnect
= &synaptics_disconnect
;
359 dev
->handle_int
= &synaptics_handle_int
;
361 default_settings(&cookie
->settings
);
363 gHardwareSpecs
.edgeMotionWidth
= SYN_EDGE_MOTION_WIDTH
;
365 gHardwareSpecs
.areaStartX
= SYN_AREA_START_X
;
366 gHardwareSpecs
.areaEndX
= SYN_AREA_END_X
;
367 gHardwareSpecs
.areaStartY
= SYN_AREA_START_Y
;
368 gHardwareSpecs
.areaEndY
= SYN_AREA_END_Y
;
370 gHardwareSpecs
.minPressure
= MIN_PRESSURE
;
371 gHardwareSpecs
.realMaxPressure
= REAL_MAX_PRESSURE
;
372 gHardwareSpecs
.maxPressure
= MAX_PRESSURE
;
374 cookie
->movementMaker
.SetSettings(&cookie
->settings
);
375 cookie
->movementMaker
.SetSpecs(&gHardwareSpecs
);
377 dev
->packet_size
= PS2_PACKET_SYNAPTICS
;
379 cookie
->synaptics_ring_buffer
380 = create_packet_buffer(SYNAPTICS_HISTORY_SIZE
* dev
->packet_size
);
381 if (cookie
->synaptics_ring_buffer
== NULL
) {
382 TRACE("ps2: can't allocate mouse actions buffer\n");
386 // create the mouse semaphore, used for synchronization between
387 // the interrupt handler and the read operation
388 cookie
->synaptics_sem
= create_sem(0, "ps2_synaptics_sem");
389 if (cookie
->synaptics_sem
< 0) {
390 TRACE("SYNAPTICS: failed creating semaphore!\n");
393 query_capability(dev
);
395 // create pass through dev
396 if (sTouchpadInfo
.capPassThrough
) {
397 TRACE("SYNAPTICS: pass through detected\n");
398 sPassthroughDevice
->parent_dev
= dev
;
399 sPassthroughDevice
->idx
= dev
->idx
;
400 ps2_service_notify_device_added(sPassthroughDevice
);
404 if (sTouchpadInfo
.capExtended
)
405 cookie
->mode
= SYN_ABSOLUTE_W_MODE
;
407 cookie
->mode
= SYN_ABSOLUTE_MODE
;
409 status
= set_touchpad_mode(dev
, cookie
->mode
);
411 INFO("SYNAPTICS: cannot set mode %s\n", name
);
415 status
= ps2_dev_command(dev
, PS2_CMD_ENABLE
, NULL
, 0, NULL
, 0);
417 INFO("SYNAPTICS: cannot enable touchpad %s\n", name
);
421 atomic_or(&dev
->flags
, PS2_FLAG_ENABLED
);
423 TRACE("SYNAPTICS: open %s success\n", name
);
427 delete_sem(cookie
->synaptics_sem
);
429 delete_packet_buffer(cookie
->synaptics_ring_buffer
);
433 atomic_and(&dev
->flags
, ~PS2_FLAG_OPEN
);
435 TRACE("SYNAPTICS: synaptics_open %s failed\n", name
);
441 synaptics_close(void *_cookie
)
444 synaptics_cookie
*cookie
= (synaptics_cookie
*)_cookie
;
446 ps2_dev_command_timeout(cookie
->dev
, PS2_CMD_DISABLE
, NULL
, 0, NULL
, 0,
449 delete_packet_buffer(cookie
->synaptics_ring_buffer
);
450 delete_sem(cookie
->synaptics_sem
);
452 atomic_and(&cookie
->dev
->flags
, ~PS2_FLAG_OPEN
);
453 atomic_and(&cookie
->dev
->flags
, ~PS2_FLAG_ENABLED
);
455 // Reset the touchpad so it generate standard ps2 packets instead of
456 // extended ones. If not, BeOS is confused with such packets when rebooting
457 // without a complete shutdown.
458 status
= ps2_reset_mouse(cookie
->dev
);
459 if (status
!= B_OK
) {
460 INFO("ps2: reset failed\n");
464 if (sTouchpadInfo
.capPassThrough
)
465 ps2_service_notify_device_removed(sPassthroughDevice
);
467 TRACE("SYNAPTICS: close %s done\n", cookie
->dev
->name
);
473 synaptics_freecookie(void *_cookie
)
481 synaptics_read(void *cookie
, off_t pos
, void *buffer
, size_t *_length
)
484 return B_NOT_ALLOWED
;
489 synaptics_write(void *cookie
, off_t pos
, const void *buffer
, size_t *_length
)
492 return B_NOT_ALLOWED
;
497 synaptics_ioctl(void *_cookie
, uint32 op
, void *buffer
, size_t length
)
499 synaptics_cookie
*cookie
= (synaptics_cookie
*)_cookie
;
500 mouse_movement movement
;
505 TRACE("SYNAPTICS: MS_READ get event\n");
506 if ((status
= get_synaptics_movment(cookie
, &movement
)) != B_OK
)
508 return user_memcpy(buffer
, &movement
, sizeof(movement
));
511 TRACE("SYNAPTICS: MS_IS_TOUCHPAD\n");
514 case MS_SET_TOUCHPAD_SETTINGS
:
515 TRACE("SYNAPTICS: MS_SET_TOUCHPAD_SETTINGS");
516 user_memcpy(&cookie
->settings
, buffer
, sizeof(touchpad_settings
));
519 case MS_SET_CLICKSPEED
:
520 TRACE("SYNAPTICS: ioctl MS_SETCLICK (set click speed)\n");
521 return user_memcpy(&cookie
->movementMaker
.click_speed
, buffer
,
525 TRACE("SYNAPTICS: unknown opcode: %ld\n", op
);
526 return B_DEV_INVALID_IOCTL
;
532 synaptics_handle_int(ps2_dev
*dev
)
534 synaptics_cookie
*cookie
= (synaptics_cookie
*)dev
->cookie
;
537 val
= cookie
->dev
->history
[0].data
;
539 if ((cookie
->packet_index
== 0 || cookie
->packet_index
== 3)
541 INFO("SYNAPTICS: bad mouse data, trying resync\n");
542 cookie
->packet_index
= 0;
543 return B_UNHANDLED_INTERRUPT
;
545 if (cookie
->packet_index
== 0 && val
>> 6 != 0x02) {
546 TRACE("SYNAPTICS: first package begins not with bit 1, 0\n");
547 return B_UNHANDLED_INTERRUPT
;
549 if (cookie
->packet_index
== 3 && val
>> 6 != 0x03) {
550 TRACE("SYNAPTICS: third package begins not with bit 1, 1\n");
551 cookie
->packet_index
= 0;
552 return B_UNHANDLED_INTERRUPT
;
554 cookie
->buffer
[cookie
->packet_index
] = val
;
556 cookie
->packet_index
++;
557 if (cookie
->packet_index
>= 6) {
558 cookie
->packet_index
= 0;
560 // check if package is a pass through package if true pass it
561 // too the pass through interrupt handle
562 if (sPassthroughDevice
->active
563 && sPassthroughDevice
->handle_int
!= NULL
564 && IS_SYN_PT_PACKAGE(cookie
->buffer
)) {
567 sPassthroughDevice
->history
[0].data
= cookie
->buffer
[1];
568 sPassthroughDevice
->handle_int(sPassthroughDevice
);
569 sPassthroughDevice
->history
[0].data
= cookie
->buffer
[4];
570 sPassthroughDevice
->handle_int(sPassthroughDevice
);
571 sPassthroughDevice
->history
[0].data
= cookie
->buffer
[5];
572 status
= sPassthroughDevice
->handle_int(sPassthroughDevice
);
574 if (cookie
->dev
->packet_size
== 4) {
575 sPassthroughDevice
->history
[0].data
= cookie
->buffer
[2];
576 status
= sPassthroughDevice
->handle_int(sPassthroughDevice
);
581 if (packet_buffer_write(cookie
->synaptics_ring_buffer
,
582 cookie
->buffer
, cookie
->dev
->packet_size
)
583 != cookie
->dev
->packet_size
) {
584 // buffer is full, drop new data
585 return B_HANDLED_INTERRUPT
;
587 release_sem_etc(cookie
->synaptics_sem
, 1, B_DO_NOT_RESCHEDULE
);
589 return B_INVOKE_SCHEDULER
;
592 return B_HANDLED_INTERRUPT
;
597 synaptics_disconnect(ps2_dev
*dev
)
599 synaptics_cookie
*cookie
= (synaptics_cookie
*)dev
->cookie
;
600 // the mouse device might not be opened at this point
601 INFO("SYNAPTICS: synaptics_disconnect %s\n", dev
->name
);
602 if ((dev
->flags
& PS2_FLAG_OPEN
) != 0)
603 release_sem(cookie
->synaptics_sem
);
607 device_hooks gSynapticsDeviceHooks
= {
610 synaptics_freecookie
,