2 * Copyright 2011, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
5 * The alps_model_info struct and all the hardware specs are taken from the
6 * linux driver, thanks a lot!
9 * Clemens Zeidler (haiku@Clemens-Zeidler.de)
18 #include "ps2_service.h"
21 static int32
generate_event(timer
* timer
);
24 const bigtime_t kEventInterval
= 1000 * 50;
35 FireEvent(alps_cookie
* cookie
, uint8
* package
)
38 memcpy(fLastPackage
, package
, sizeof(uint8
) * PS2_PACKET_ALPS
);
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_ALPS
) != PS2_PACKET_ALPS
) {
61 // buffer is full, drop new data
64 release_sem_etc(fCookie
->sem
, 1, B_DO_NOT_RESCHEDULE
);
69 uint8 fLastPackage
[PS2_PACKET_ALPS
];
75 static EventProducer gEventProducer
;
79 generate_event(timer
* timer
)
81 gEventProducer
.InjectEvent();
86 const char* kALPSPath
[4] = {
87 "input/touchpad/ps2/alps_0",
88 "input/touchpad/ps2/alps_1",
89 "input/touchpad/ps2/alps_2",
90 "input/touchpad/ps2/alps_3"
94 typedef struct alps_model_info
{
102 #define ALPS_OLDPROTO 0x01 // old style input
103 #define ALPS_DUALPOINT 0x02 // touchpad has trackstick
104 #define ALPS_PASS 0x04 // device has a pass-through port
106 #define ALPS_WHEEL 0x08 // hardware wheel present
107 #define ALPS_FW_BK_1 0x10 // front & back buttons present
108 #define ALPS_FW_BK_2 0x20 // front & back buttons present
109 #define ALPS_FOUR_BUTTONS 0x40 // 4 direction button present
110 #define ALPS_PS2_INTERLEAVED 0x80 // 3-byte PS/2 packet interleaved with
111 // 6-byte ALPS packet
113 static const struct alps_model_info gALPSModelInfos
[] = {
114 {{0x32, 0x02, 0x14}, 0xf8, 0xf8, ALPS_PASS
| ALPS_DUALPOINT
},
115 // Toshiba Salellite Pro M10
116 // {{0x33, 0x02, 0x0a}, 0x88, 0xf8, ALPS_OLDPROTO},
118 {{0x53, 0x02, 0x0a}, 0xf8, 0xf8, 0},
119 {{0x53, 0x02, 0x14}, 0xf8, 0xf8, 0},
120 {{0x60, 0x03, 0xc8}, 0xf8, 0xf8, 0},
122 {{0x63, 0x02, 0x0a}, 0xf8, 0xf8, 0},
123 {{0x63, 0x02, 0x14}, 0xf8, 0xf8, 0},
124 {{0x63, 0x02, 0x28}, 0xf8, 0xf8, ALPS_FW_BK_2
},
125 // Fujitsu Siemens S6010
126 // {{0x63, 0x02, 0x3c}, 0x8f, 0x8f, ALPS_WHEEL},
127 // Toshiba Satellite S2400-103
128 {{0x63, 0x02, 0x50}, 0xef, 0xef, ALPS_FW_BK_1
},
130 {{0x63, 0x02, 0x64}, 0xf8, 0xf8, 0},
131 {{0x63, 0x03, 0xc8}, 0xf8, 0xf8, ALPS_PASS
| ALPS_DUALPOINT
},
132 // Dell Latitude D800
133 {{0x73, 0x00, 0x0a}, 0xf8, 0xf8, ALPS_DUALPOINT
},
134 // ThinkPad R61 8918-5QG, x301
135 {{0x73, 0x02, 0x0a}, 0xf8, 0xf8, 0},
136 {{0x73, 0x02, 0x14}, 0xf8, 0xf8, ALPS_FW_BK_2
},
138 {{0x20, 0x02, 0x0e}, 0xf8, 0xf8, ALPS_PASS
| ALPS_DUALPOINT
},
140 {{0x22, 0x02, 0x0a}, 0xf8, 0xf8, ALPS_PASS
| ALPS_DUALPOINT
},
141 {{0x22, 0x02, 0x14}, 0xff, 0xff, ALPS_PASS
| ALPS_DUALPOINT
},
142 // Dell Latitude D600
143 // {{0x62, 0x02, 0x14}, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT
144 // | ALPS_PS2_INTERLEAVED},
145 // Dell Latitude E5500, E6400, E6500, Precision M4400
146 {{0x73, 0x02, 0x50}, 0xcf, 0xcf, ALPS_FOUR_BUTTONS
},
148 // {{0x52, 0x01, 0x14}, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT
149 // | ALPS_PS2_INTERLEAVED},
150 // Toshiba Tecra A11-11L
155 static alps_model_info
* sFoundModel
= NULL
;
158 // touchpad proportions
159 #define EDGE_MOTION_WIDTH 55
160 // increase the touchpad size a little bit
161 #define AREA_START_X 40
162 #define AREA_END_X 987
163 #define AREA_START_Y 40
164 #define AREA_END_Y 734
166 #define MIN_PRESSURE 15
167 #define REAL_MAX_PRESSURE 70
168 #define MAX_PRESSURE 115
171 #define ALPS_HISTORY_SIZE 256
174 static hardware_specs gHardwareSpecs
;
177 /* Data taken from linux driver:
178 ALPS absolute Mode - new format
179 byte 0: 1 ? ? ? 1 ? ? ?
180 byte 1: 0 x6 x5 x4 x3 x2 x1 x0
181 byte 2: 0 x10 x9 x8 x7 ? fin ges
182 byte 3: 0 y9 y8 y7 1 M R L
183 byte 4: 0 y6 y5 y4 y3 y2 y1 y0
184 byte 5: 0 z6 z5 z4 z3 z2 z1 z0
187 get_alps_movment(alps_cookie
*cookie
, mouse_movement
*movement
)
191 uint8 event_buffer
[PS2_PACKET_ALPS
];
193 status
= acquire_sem_etc(cookie
->sem
, 1, B_CAN_INTERRUPT
, 0);
197 if (!cookie
->dev
->active
) {
198 TRACE("ALPS: read_event: Error device no longer active\n");
202 if (packet_buffer_read(cookie
->ring_buffer
, event_buffer
,
203 cookie
->dev
->packet_size
) != cookie
->dev
->packet_size
) {
204 TRACE("ALPS: error copying buffer\n");
208 event
.buttons
= event_buffer
[3] & 7;
209 event
.zPressure
= event_buffer
[5];
211 // finger on touchpad
212 if (event_buffer
[2] & 0x2) {
213 // finger with normal width
220 if (event_buffer
[2] & 0x1) {
221 event
.zPressure
= 60;
224 // if hardware tab gesture is off a z pressure of 16 is reported
225 if (cookie
->previousZ
== 0 && event
.wValue
== 4 && event
.zPressure
== 16)
226 event
.zPressure
= 60;
228 cookie
->previousZ
= event
.zPressure
;
230 event
.xPosition
= event_buffer
[1] | ((event_buffer
[2] & 0x78) << 4);
231 event
.yPosition
= event_buffer
[4] | ((event_buffer
[3] & 0x70) << 3);
233 // check for trackpoint even (z pressure 127)
234 if (sFoundModel
->flags
& ALPS_DUALPOINT
&& event
.zPressure
== 127) {
235 movement
->xdelta
= event
.xPosition
> 383 ? event
.xPosition
- 768
237 movement
->ydelta
= event
.yPosition
> 255
238 ? event
.yPosition
- 512 : event
.yPosition
;
239 movement
->wheel_xdelta
= 0;
240 movement
->wheel_ydelta
= 0;
241 movement
->buttons
= event
.buttons
;
242 movement
->timestamp
= system_time();
243 cookie
->movementMaker
.UpdateButtons(movement
);
245 event
.yPosition
= AREA_END_Y
- (event
.yPosition
- AREA_START_Y
);
246 status
= cookie
->movementMaker
.EventToMovement(&event
, movement
);
249 if (cookie
->movementMaker
.WasEdgeMotion()
250 || cookie
->movementMaker
.TapDragStarted()) {
251 gEventProducer
.FireEvent(cookie
, event_buffer
);
259 default_settings(touchpad_settings
*set
)
261 memcpy(set
, &kDefaultTouchpadSettings
, sizeof(touchpad_settings
));
266 probe_alps(ps2_dev
* dev
)
270 TRACE("ALPS: probe\n");
273 if (ps2_dev_command(dev
, PS2_CMD_MOUSE_SET_RES
, val
, 1, NULL
, 0) != B_OK
274 || ps2_dev_command(dev
, PS2_CMD_MOUSE_SET_SCALE11
, NULL
, 0, NULL
, 0)
276 || ps2_dev_command(dev
, PS2_CMD_MOUSE_SET_SCALE11
, NULL
, 0, NULL
, 0)
278 || ps2_dev_command(dev
, PS2_CMD_MOUSE_SET_SCALE11
, NULL
, 0, NULL
, 0)
282 if (ps2_dev_command(dev
, PS2_CMD_MOUSE_GET_INFO
, NULL
, 0, val
, 3)
286 if (val
[0] != 0 || val
[1] != 0 || (val
[2] != 10 && val
[2] != 100))
290 if (ps2_dev_command(dev
, PS2_CMD_MOUSE_SET_RES
, val
, 1, NULL
, 0) != B_OK
291 || ps2_dev_command(dev
, PS2_CMD_MOUSE_SET_SCALE21
, NULL
, 0, NULL
, 0)
293 || ps2_dev_command(dev
, PS2_CMD_MOUSE_SET_SCALE21
, NULL
, 0, NULL
, 0)
295 || ps2_dev_command(dev
, PS2_CMD_MOUSE_SET_SCALE21
, NULL
, 0, NULL
, 0)
299 if (ps2_dev_command(dev
, PS2_CMD_MOUSE_GET_INFO
, NULL
, 0, val
, 3)
304 const alps_model_info
* info
= &gALPSModelInfos
[i
];
305 if (info
->id
[0] == 0) {
306 INFO("ALPS not supported: %2.2x %2.2x %2.2x\n", val
[0], val
[1],
311 if (info
->id
[0] == val
[0] && info
->id
[1] == val
[1]
312 && info
->id
[2] == val
[2]) {
313 sFoundModel
= (alps_model_info
*)info
;
314 INFO("ALPS found: %2.2x %2.2x %2.2x\n", val
[0], val
[1], val
[2]);
319 dev
->name
= kALPSPath
[dev
->idx
];
320 dev
->packet_size
= PS2_PACKET_ALPS
;
327 switch_hardware_tab(ps2_dev
* dev
, bool on
)
331 uint8 command
= PS2_CMD_MOUSE_SET_RES
;
334 command
= PS2_CMD_SET_TYPEMATIC
;
336 if (ps2_dev_command(dev
, PS2_CMD_MOUSE_GET_INFO
, NULL
, 0, val
, 3) != B_OK
337 || ps2_dev_command(dev
, PS2_CMD_DISABLE
, NULL
, 0, NULL
, 0) != B_OK
338 || ps2_dev_command(dev
, PS2_CMD_DISABLE
, NULL
, 0, NULL
, 0) != B_OK
339 || ps2_dev_command(dev
, command
, &arg
, 1, NULL
, 0) != B_OK
)
347 enable_passthrough(ps2_dev
* dev
, bool on
)
349 uint8 command
= PS2_CMD_MOUSE_SET_SCALE11
;
351 command
= PS2_CMD_MOUSE_SET_SCALE21
;
353 if (ps2_dev_command(dev
, command
, NULL
, 0, NULL
, 0) != B_OK
354 || ps2_dev_command(dev
, command
, NULL
, 0, NULL
, 0) != B_OK
355 || ps2_dev_command(dev
, command
, NULL
, 0, NULL
, 0) != B_OK
356 || ps2_dev_command(dev
, PS2_CMD_DISABLE
, NULL
, 0, NULL
, 0) != B_OK
)
364 alps_open(const char *name
, uint32 flags
, void **_cookie
)
368 for (dev
= NULL
, i
= 0; i
< PS2_DEVICE_COUNT
; i
++) {
369 if (0 == strcmp(ps2_device
[i
].name
, name
)) {
370 dev
= &ps2_device
[i
];
376 TRACE("ps2: dev = NULL\n");
380 if (atomic_or(&dev
->flags
, PS2_FLAG_OPEN
) & PS2_FLAG_OPEN
)
383 alps_cookie
* cookie
= (alps_cookie
*)malloc(sizeof(alps_cookie
));
386 memset(cookie
, 0, sizeof(*cookie
));
388 cookie
->movementMaker
.Init();
389 cookie
->previousZ
= 0;
393 dev
->cookie
= cookie
;
394 dev
->disconnect
= &alps_disconnect
;
395 dev
->handle_int
= &alps_handle_int
;
397 default_settings(&cookie
->settings
);
399 gHardwareSpecs
.edgeMotionWidth
= EDGE_MOTION_WIDTH
;
401 gHardwareSpecs
.areaStartX
= AREA_START_X
;
402 gHardwareSpecs
.areaEndX
= AREA_END_X
;
403 gHardwareSpecs
.areaStartY
= AREA_START_Y
;
404 gHardwareSpecs
.areaEndY
= AREA_END_Y
;
406 gHardwareSpecs
.minPressure
= MIN_PRESSURE
;
407 gHardwareSpecs
.realMaxPressure
= REAL_MAX_PRESSURE
;
408 gHardwareSpecs
.maxPressure
= MAX_PRESSURE
;
410 cookie
->movementMaker
.SetSettings(&cookie
->settings
);
411 cookie
->movementMaker
.SetSpecs(&gHardwareSpecs
);
413 dev
->packet_size
= PS2_PACKET_ALPS
;
415 cookie
->ring_buffer
= create_packet_buffer(
416 ALPS_HISTORY_SIZE
* dev
->packet_size
);
417 if (cookie
->ring_buffer
== NULL
) {
418 TRACE("ALPS: can't allocate mouse actions buffer\n");
421 // create the mouse semaphore, used for synchronization between
422 // the interrupt handler and the read operation
423 cookie
->sem
= create_sem(0, "ps2_alps_sem");
424 if (cookie
->sem
< 0) {
425 TRACE("ALPS: failed creating semaphore!\n");
429 if ((sFoundModel
->flags
& ALPS_PASS
) != 0
430 && enable_passthrough(dev
, true) != B_OK
)
433 // switch tap mode off
434 if (switch_hardware_tab(dev
, false) != B_OK
)
437 // init the alps device to absolut mode
438 if (ps2_dev_command(dev
, PS2_CMD_DISABLE
, NULL
, 0, NULL
, 0) != B_OK
439 || ps2_dev_command(dev
, PS2_CMD_DISABLE
, NULL
, 0, NULL
, 0) != B_OK
440 || ps2_dev_command(dev
, PS2_CMD_DISABLE
, NULL
, 0, NULL
, 0) != B_OK
441 || ps2_dev_command(dev
, PS2_CMD_DISABLE
, NULL
, 0, NULL
, 0) != B_OK
442 || ps2_dev_command(dev
, PS2_CMD_ENABLE
, NULL
, 0, NULL
, 0) != B_OK
)
445 if ((sFoundModel
->flags
& ALPS_PASS
) != 0
446 && enable_passthrough(dev
, false) != B_OK
)
449 if (ps2_dev_command(dev
, PS2_CMD_MOUSE_SET_STREAM
, NULL
, 0, NULL
, 0) != B_OK
)
452 if (ps2_dev_command(dev
, PS2_CMD_ENABLE
, NULL
, 0, NULL
, 0) != B_OK
)
455 atomic_or(&dev
->flags
, PS2_FLAG_ENABLED
);
457 TRACE("ALPS: open %s success\n", name
);
461 delete_sem(cookie
->sem
);
463 delete_packet_buffer(cookie
->ring_buffer
);
467 atomic_and(&dev
->flags
, ~PS2_FLAG_OPEN
);
469 TRACE("ALPS: open %s failed\n", name
);
475 alps_close(void *_cookie
)
477 gEventProducer
.CancelEvent();
479 alps_cookie
*cookie
= (alps_cookie
*)_cookie
;
481 ps2_dev_command_timeout(cookie
->dev
, PS2_CMD_DISABLE
, NULL
, 0, NULL
, 0,
484 delete_packet_buffer(cookie
->ring_buffer
);
485 delete_sem(cookie
->sem
);
487 atomic_and(&cookie
->dev
->flags
, ~PS2_FLAG_OPEN
);
488 atomic_and(&cookie
->dev
->flags
, ~PS2_FLAG_ENABLED
);
490 // Reset the touchpad so it generate standard ps2 packets instead of
491 // extended ones. If not, BeOS is confused with such packets when rebooting
492 // without a complete shutdown.
493 status_t status
= ps2_reset_mouse(cookie
->dev
);
494 if (status
!= B_OK
) {
495 INFO("ps2: reset failed\n");
499 TRACE("ALPS: close %s done\n", cookie
->dev
->name
);
505 alps_freecookie(void *_cookie
)
513 alps_ioctl(void *_cookie
, uint32 op
, void *buffer
, size_t length
)
515 alps_cookie
*cookie
= (alps_cookie
*)_cookie
;
516 mouse_movement movement
;
521 TRACE("ALPS: MS_READ get event\n");
522 if ((status
= get_alps_movment(cookie
, &movement
)) != B_OK
)
524 return user_memcpy(buffer
, &movement
, sizeof(movement
));
527 TRACE("ALPS: MS_IS_TOUCHPAD\n");
530 case MS_SET_TOUCHPAD_SETTINGS
:
531 TRACE("ALPS: MS_SET_TOUCHPAD_SETTINGS");
532 user_memcpy(&cookie
->settings
, buffer
, sizeof(touchpad_settings
));
535 case MS_SET_CLICKSPEED
:
536 TRACE("ALPS: ioctl MS_SETCLICK (set click speed)\n");
537 return user_memcpy(&cookie
->movementMaker
.click_speed
, buffer
,
541 TRACE("ALPS: unknown opcode: %ld\n", op
);
548 alps_read(void* cookie
, off_t pos
, void* buffer
, size_t* _length
)
551 return B_NOT_ALLOWED
;
556 alps_write(void* cookie
, off_t pos
, const void* buffer
, size_t* _length
)
559 return B_NOT_ALLOWED
;
564 alps_handle_int(ps2_dev
* dev
)
566 alps_cookie
* cookie
= (alps_cookie
*)dev
->cookie
;
568 // we got a real event cancel the fake event
569 gEventProducer
.CancelEvent();
572 val
= cookie
->dev
->history
[0].data
;
573 if (cookie
->packet_index
== 0
574 && (val
& sFoundModel
->maskFirstByte
) != sFoundModel
->firstByte
) {
575 INFO("ALPS: bad header, trying resync\n");
576 cookie
->packet_index
= 0;
577 return B_UNHANDLED_INTERRUPT
;
580 // data packages starting with a 0
581 if (cookie
->packet_index
> 1 && (val
& 0x80)) {
582 INFO("ALPS: bad package data, trying resync\n");
583 cookie
->packet_index
= 0;
584 return B_UNHANDLED_INTERRUPT
;
587 cookie
->buffer
[cookie
->packet_index
] = val
;
589 cookie
->packet_index
++;
590 if (cookie
->packet_index
>= 6) {
591 cookie
->packet_index
= 0;
593 if (packet_buffer_write(cookie
->ring_buffer
,
594 cookie
->buffer
, cookie
->dev
->packet_size
)
595 != cookie
->dev
->packet_size
) {
596 // buffer is full, drop new data
597 return B_HANDLED_INTERRUPT
;
599 release_sem_etc(cookie
->sem
, 1, B_DO_NOT_RESCHEDULE
);
601 return B_INVOKE_SCHEDULER
;
604 return B_HANDLED_INTERRUPT
;
609 alps_disconnect(ps2_dev
*dev
)
611 alps_cookie
*cookie
= (alps_cookie
*)dev
->cookie
;
612 // the mouse device might not be opened at this point
613 INFO("ALPS: alps_disconnect %s\n", dev
->name
);
614 if ((dev
->flags
& PS2_FLAG_OPEN
) != 0)
615 release_sem(cookie
->sem
);
619 device_hooks gALPSDeviceHooks
= {