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
)
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
);
69 send_touchpad_arg(ps2_dev
*dev
, uint8 arg
)
71 return send_touchpad_arg_timeout(dev
, arg
, 4000000);
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,
86 get_synaptics_movment(synaptics_cookie
*cookie
, mouse_movement
*movement
)
90 uint8 event_buffer
[PS2_MAX_PACKET_SIZE
];
91 uint8 wValue0
, wValue1
, wValue2
, wValue3
, wValue
;
93 uint32 xTwelfBit
, yTwelfBit
;
95 status
= acquire_sem_etc(cookie
->synaptics_sem
, 1, B_CAN_INTERRUPT
, 0);
99 if (!cookie
->dev
->active
) {
100 TRACE("SYNAPTICS: read_event: Error device no longer active\n");
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");
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;
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;
130 bool finger
= event_buffer
[0] >> 5 & 1;
132 // finger with normal width
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
);
158 query_capability(ps2_dev
*dev
)
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
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
,
193 INFO("SYNAPTICS: cannot disable touchpad %s\n", dev
->parent_dev
->name
);
197 synapticsCookie
->packet_index
= 0;
200 synapticsCookie
->mode
|= SYN_FOUR_BYTE_CHILD
;
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);
208 INFO("SYNAPTICS: cannot enable touchpad %s\n", dev
->parent_dev
->name
);
216 passthrough_command(ps2_dev
*dev
, uint8 cmd
, const uint8
*out
, int outCount
,
217 uint8
*in
, int inCount
, bigtime_t timeout
)
220 uint8 passThroughCmd
= SYN_PASSTHROUGH_CMD
;
222 uint32 passThroughInCount
= (inCount
+ 1) * 6;
223 uint8 passThroughIn
[passThroughInCount
];
226 TRACE("SYNAPTICS: passthrough command 0x%x\n", cmd
);
228 status
= ps2_dev_command(dev
->parent_dev
, PS2_CMD_DISABLE
, NULL
, 0,
233 for (i
= -1; i
< outCount
; i
++) {
238 status
= send_touchpad_arg_timeout(dev
->parent_dev
, val
, timeout
);
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
);
248 status
= ps2_dev_command_timeout(dev
->parent_dev
, PS2_CMD_SET_SAMPLE_RATE
,
249 &passThroughCmd
, 1, passThroughIn
, passThroughInCount
, timeout
);
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");
262 in
[i
- 1] = passThroughIn
[i
* 6 + 1];
266 status_t statusOfEnable
= ps2_dev_command(dev
->parent_dev
, PS2_CMD_ENABLE
,
268 if (statusOfEnable
!= B_OK
)
269 TRACE("SYNAPTICS: enabling of parent failed: 0x%lx.\n", statusOfEnable
);
271 return status
!= B_OK
? status
: statusOfEnable
;
276 probe_synaptics(ps2_dev
*dev
)
281 TRACE("SYNAPTICS: probe\n");
283 status
= send_touchpad_arg(dev
, 0x00);
286 status
= ps2_dev_command(dev
, 0xE9, NULL
, 0, val
, 3);
290 sTouchpadInfo
.minorVersion
= val
[0];
292 if (deviceId
!= SYN_TOUCHPAD
) {
293 TRACE("SYNAPTICS: not found\n");
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
);
302 if (sTouchpadInfo
.minorVersion
<= 2
303 && sTouchpadInfo
.majorVersion
<= 3) {
304 TRACE("SYNAPTICS: too old touchpad not supported\n");
307 dev
->name
= kSynapticsPath
[dev
->idx
];
312 // #pragma mark - Device functions
316 synaptics_open(const char *name
, uint32 flags
, void **_cookie
)
319 synaptics_cookie
*cookie
;
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
];
331 TRACE("ps2: dev = NULL\n");
335 if (atomic_or(&dev
->flags
, PS2_FLAG_OPEN
) & PS2_FLAG_OPEN
)
338 cookie
= (synaptics_cookie
*)malloc(sizeof(synaptics_cookie
));
341 memset(cookie
, 0, sizeof(*cookie
));
343 cookie
->movementMaker
.Init();
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");
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");
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
);
394 if (sTouchpadInfo
.capExtended
)
395 cookie
->mode
= SYN_ABSOLUTE_W_MODE
;
397 cookie
->mode
= SYN_ABSOLUTE_MODE
;
399 status
= set_touchpad_mode(dev
, cookie
->mode
);
401 INFO("SYNAPTICS: cannot set mode %s\n", name
);
405 status
= ps2_dev_command(dev
, PS2_CMD_ENABLE
, NULL
, 0, NULL
, 0);
407 INFO("SYNAPTICS: cannot enable touchpad %s\n", name
);
411 atomic_or(&dev
->flags
, PS2_FLAG_ENABLED
);
413 TRACE("SYNAPTICS: open %s success\n", name
);
417 delete_sem(cookie
->synaptics_sem
);
419 delete_packet_buffer(cookie
->synaptics_ring_buffer
);
423 atomic_and(&dev
->flags
, ~PS2_FLAG_OPEN
);
425 TRACE("SYNAPTICS: synaptics_open %s failed\n", name
);
431 synaptics_close(void *_cookie
)
434 synaptics_cookie
*cookie
= (synaptics_cookie
*)_cookie
;
436 ps2_dev_command_timeout(cookie
->dev
, PS2_CMD_DISABLE
, NULL
, 0, NULL
, 0,
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");
454 if (sTouchpadInfo
.capPassThrough
)
455 ps2_service_notify_device_removed(sPassthroughDevice
);
457 TRACE("SYNAPTICS: close %s done\n", cookie
->dev
->name
);
463 synaptics_freecookie(void *_cookie
)
471 synaptics_read(void *cookie
, off_t pos
, void *buffer
, size_t *_length
)
474 return B_NOT_ALLOWED
;
479 synaptics_write(void *cookie
, off_t pos
, const void *buffer
, size_t *_length
)
482 return B_NOT_ALLOWED
;
487 synaptics_ioctl(void *_cookie
, uint32 op
, void *buffer
, size_t length
)
489 synaptics_cookie
*cookie
= (synaptics_cookie
*)_cookie
;
490 mouse_movement movement
;
495 TRACE("SYNAPTICS: MS_READ get event\n");
496 if ((status
= get_synaptics_movment(cookie
, &movement
)) != B_OK
)
498 return user_memcpy(buffer
, &movement
, sizeof(movement
));
501 TRACE("SYNAPTICS: MS_IS_TOUCHPAD\n");
504 case MS_SET_TOUCHPAD_SETTINGS
:
505 TRACE("SYNAPTICS: MS_SET_TOUCHPAD_SETTINGS");
506 user_memcpy(&cookie
->settings
, buffer
, sizeof(touchpad_settings
));
509 case MS_SET_CLICKSPEED
:
510 TRACE("SYNAPTICS: ioctl MS_SETCLICK (set click speed)\n");
511 return user_memcpy(&cookie
->movementMaker
.click_speed
, buffer
,
515 TRACE("SYNAPTICS: unknown opcode: %ld\n", op
);
516 return B_DEV_INVALID_IOCTL
;
522 synaptics_handle_int(ps2_dev
*dev
)
524 synaptics_cookie
*cookie
= (synaptics_cookie
*)dev
->cookie
;
527 val
= cookie
->dev
->history
[0].data
;
529 if ((cookie
->packet_index
== 0 || cookie
->packet_index
== 3)
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
)) {
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
);
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
;
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
= {
600 synaptics_freecookie
,