1 /*
2 * Copyright 2011, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
4 *
5 * The alps_model_info struct and all the hardware specs are taken from the
6 * linux driver, thanks a lot!
7 *
8 * Authors:
9 * Clemens Zeidler (haiku@Clemens-Zeidler.de)
10 */
11
12
13 #include "ps2_alps.h"
14
15 #include <stdlib.h>
16 #include <string.h>
17
18 #include <keyboard_mouse_driver.h>
19
20 #include "ps2_service.h"
21
22
23 //#define TRACE_PS2_ALPS
24 #ifdef TRACE_PS2_APLS
25 # define TRACE(x...) dprintf(x)
26 #else
27 # define TRACE(x...)
28 #endif
29
30
31 const char* kALPSPath[4] = {
32 "input/touchpad/ps2/alps_0",
33 "input/touchpad/ps2/alps_1",
34 "input/touchpad/ps2/alps_2",
35 "input/touchpad/ps2/alps_3"
36 };
37
38
39 typedef struct alps_model_info {
40 uint8 id[3];
41 uint8 firstByte;
42 uint8 maskFirstByte;
43 uint8 flags;
44 } alps_model_info;
45
46
47 #define ALPS_OLDPROTO 0x01 // old style input
48 #define ALPS_DUALPOINT 0x02 // touchpad has trackstick
49 #define ALPS_PASS 0x04 // device has a pass-through port
50
51 #define ALPS_WHEEL 0x08 // hardware wheel present
52 #define ALPS_FW_BK_1 0x10 // front & back buttons present
53 #define ALPS_FW_BK_2 0x20 // front & back buttons present
54 #define ALPS_FOUR_BUTTONS 0x40 // 4 direction button present
55 #define ALPS_PS2_INTERLEAVED 0x80 // 3-byte PS/2 packet interleaved with
56 // 6-byte ALPS packet
57
58 static const struct alps_model_info gALPSModelInfos[] = {
59 {{0x32, 0x02, 0x14}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT},
60 // Toshiba Salellite Pro M10
61 // {{0x33, 0x02, 0x0a}, 0x88, 0xf8, ALPS_OLDPROTO},
62 // UMAX-530T
63 {{0x53, 0x02, 0x0a}, 0xf8, 0xf8, 0},
64 {{0x53, 0x02, 0x14}, 0xf8, 0xf8, 0},
65 {{0x60, 0x03, 0xc8}, 0xf8, 0xf8, 0},
66 // HP ze1115
67 {{0x63, 0x02, 0x0a}, 0xf8, 0xf8, 0},
68 {{0x63, 0x02, 0x14}, 0xf8, 0xf8, 0},
69 {{0x63, 0x02, 0x28}, 0xf8, 0xf8, ALPS_FW_BK_2},
70 // Fujitsu Siemens S6010
71 // {{0x63, 0x02, 0x3c}, 0x8f, 0x8f, ALPS_WHEEL},
72 // Toshiba Satellite S2400-103
73 {{0x63, 0x02, 0x50}, 0xef, 0xef, ALPS_FW_BK_1},
74 // NEC Versa L320
75 {{0x63, 0x02, 0x64}, 0xf8, 0xf8, 0},
76 {{0x63, 0x03, 0xc8}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT},
77 // Dell Latitude D800
78 {{0x73, 0x00, 0x0a}, 0xf8, 0xf8, ALPS_DUALPOINT},
79 // ThinkPad R61 8918-5QG, x301
80 {{0x73, 0x02, 0x0a}, 0xf8, 0xf8, 0},
81 {{0x73, 0x02, 0x14}, 0xf8, 0xf8, ALPS_FW_BK_2},
82 // Ahtec Laptop
83 {{0x20, 0x02, 0x0e}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT},
84 // XXX
85 {{0x22, 0x02, 0x0a}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT},
86 {{0x22, 0x02, 0x14}, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT},
87 // Dell Latitude D600
88 // {{0x62, 0x02, 0x14}, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT
89 // | ALPS_PS2_INTERLEAVED},
90 // Dell Latitude E5500, E6400, E6500, Precision M4400
91 {{0x73, 0x02, 0x50}, 0xcf, 0xcf, ALPS_FOUR_BUTTONS},
92 // Dell Vostro 1400
93 // {{0x52, 0x01, 0x14}, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT
94 // | ALPS_PS2_INTERLEAVED},
95 // Toshiba Tecra A11-11L
96 {{0, 0, 0}, 0, 0, 0}
97 };
98
99
100 static alps_model_info* sFoundModel = NULL;
101
102
103 // touchpad proportions
104 #define EDGE_MOTION_WIDTH 55
105 // increase the touchpad size a little bit
106 #define AREA_START_X 40
107 #define AREA_END_X 987
108 #define AREA_START_Y 40
109 #define AREA_END_Y 734
110
111 #define MIN_PRESSURE 15
112 #define REAL_MAX_PRESSURE 70
113 #define MAX_PRESSURE 115
114
115
116 #define ALPS_HISTORY_SIZE 256
117
118
119 static touchpad_specs gHardwareSpecs;
120
121
122 /* Data taken from linux driver:
123 ALPS absolute Mode - new format
124 byte 0: 1 ? ? ? 1 ? ? ?
125 byte 1: 0 x6 x5 x4 x3 x2 x1 x0
126 byte 2: 0 x10 x9 x8 x7 ? fin ges
127 byte 3: 0 y9 y8 y7 1 M R L
128 byte 4: 0 y6 y5 y4 y3 y2 y1 y0
129 byte 5: 0 z6 z5 z4 z3 z2 z1 z0
130 */
131 static status_t
get_alps_movment(alps_cookie * cookie,touchpad_read * _read)132 get_alps_movment(alps_cookie *cookie, touchpad_read *_read)
133 {
134 status_t status;
135 touchpad_movement event;
136 uint8 event_buffer[PS2_PACKET_ALPS];
137
138 status = acquire_sem_etc(cookie->sem, 1, B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT,
139 _read->timeout);
140 if (status < B_OK)
141 return status;
142
143 if (!cookie->dev->active) {
144 TRACE("ALPS: read_event: Error device no longer active\n");
145 return B_ERROR;
146 }
147
148 if (packet_buffer_read(cookie->ring_buffer, event_buffer,
149 cookie->dev->packet_size) != cookie->dev->packet_size) {
150 TRACE("ALPS: error copying buffer\n");
151 return B_ERROR;
152 }
153
154 event.buttons = event_buffer[3] & 7;
155 event.zPressure = event_buffer[5];
156
157 // finger on touchpad
158 if (event_buffer[2] & 0x2) {
159 // finger with normal width
160 event.fingerWidth = 4;
161 } else {
162 event.fingerWidth = 3;
163 }
164
165 // tab gesture
166 if (event_buffer[2] & 0x1) {
167 event.zPressure = 60;
168 event.fingerWidth = 4;
169 }
170 // if hardware tab gesture is off a z pressure of 16 is reported
171 if (cookie->previousZ == 0 && event.fingerWidth == 4 && event.zPressure == 16)
172 event.zPressure = 60;
173
174 cookie->previousZ = event.zPressure;
175
176 event.xPosition = event_buffer[1] | ((event_buffer[2] & 0x78) << 4);
177 event.yPosition = event_buffer[4] | ((event_buffer[3] & 0x70) << 3);
178
179 // check for trackpoint even (z pressure 127)
180 if (sFoundModel->flags & ALPS_DUALPOINT && event.zPressure == 127) {
181 mouse_movement movement;
182 movement.xdelta = event.xPosition > 383 ? event.xPosition - 768
183 : event.xPosition;
184 movement.ydelta = event.yPosition > 255
185 ? event.yPosition - 512 : event.yPosition;
186 movement.wheel_xdelta = 0;
187 movement.wheel_ydelta = 0;
188 movement.buttons = event.buttons;
189 movement.timestamp = system_time();
190
191 _read->event = MS_READ;
192 _read->u.mouse = movement;
193 } else {
194 event.yPosition = AREA_END_Y - (event.yPosition - AREA_START_Y);
195
196 _read->event = MS_READ_TOUCHPAD;
197 _read->u.touchpad = event;
198 }
199
200 return status;
201 }
202
203
204 status_t
probe_alps(ps2_dev * dev)205 probe_alps(ps2_dev* dev)
206 {
207 int i;
208 uint8 val[3];
209 TRACE("ALPS: probe\n");
210
211 val[0] = 0;
212 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, val, 1, NULL, 0) != B_OK
213 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11, NULL, 0, NULL, 0)
214 != B_OK
215 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11, NULL, 0, NULL, 0)
216 != B_OK
217 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11, NULL, 0, NULL, 0)
218 != B_OK)
219 return B_ERROR;
220
221 if (ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 3)
222 != B_OK)
223 return B_ERROR;
224
225 if (val[0] != 0 || val[1] != 0 || (val[2] != 10 && val[2] != 100))
226 return B_ERROR;
227
228 val[0] = 0;
229 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, val, 1, NULL, 0) != B_OK
230 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE21, NULL, 0, NULL, 0)
231 != B_OK
232 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE21, NULL, 0, NULL, 0)
233 != B_OK
234 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE21, NULL, 0, NULL, 0)
235 != B_OK)
236 return B_ERROR;
237
238 if (ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 3)
239 != B_OK)
240 return B_ERROR;
241
242 for (i = 0; ; i++) {
243 const alps_model_info* info = &gALPSModelInfos[i];
244 if (info->id[0] == 0) {
245 INFO("ALPS not supported: %2.2x %2.2x %2.2x\n", val[0], val[1],
246 val[2]);
247 return B_ERROR;
248 }
249
250 if (info->id[0] == val[0] && info->id[1] == val[1]
251 && info->id[2] == val[2]) {
252 sFoundModel = (alps_model_info*)info;
253 INFO("ALPS found: %2.2x %2.2x %2.2x\n", val[0], val[1], val[2]);
254 break;
255 }
256 }
257
258 dev->name = kALPSPath[dev->idx];
259 dev->packet_size = PS2_PACKET_ALPS;
260
261 return B_OK;
262 }
263
264
265 status_t
switch_hardware_tab(ps2_dev * dev,bool on)266 switch_hardware_tab(ps2_dev* dev, bool on)
267 {
268 uint8 val[3];
269 uint8 arg = 0x00;
270 uint8 command = PS2_CMD_MOUSE_SET_RES;
271 if (on) {
272 arg = 0x0A;
273 command = PS2_CMD_SET_TYPEMATIC;
274 }
275 if (ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 3) != B_OK
276 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
277 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
278 || ps2_dev_command(dev, command, &arg, 1, NULL, 0) != B_OK)
279 return B_ERROR;
280
281 return B_OK;
282 }
283
284
285 status_t
enable_passthrough(ps2_dev * dev,bool on)286 enable_passthrough(ps2_dev* dev, bool on)
287 {
288 uint8 command = PS2_CMD_MOUSE_SET_SCALE11;
289 if (on)
290 command = PS2_CMD_MOUSE_SET_SCALE21;
291
292 if (ps2_dev_command(dev, command, NULL, 0, NULL, 0) != B_OK
293 || ps2_dev_command(dev, command, NULL, 0, NULL, 0) != B_OK
294 || ps2_dev_command(dev, command, NULL, 0, NULL, 0) != B_OK
295 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK)
296 return B_ERROR;
297
298 return B_OK;
299 }
300
301
302 status_t
alps_open(const char * name,uint32 flags,void ** _cookie)303 alps_open(const char *name, uint32 flags, void **_cookie)
304 {
305 ps2_dev* dev;
306 int i;
307 for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) {
308 if (0 == strcmp(ps2_device[i].name, name)) {
309 dev = &ps2_device[i];
310 break;
311 }
312 }
313
314 if (dev == NULL) {
315 TRACE("ps2: dev = NULL\n");
316 return B_ERROR;
317 }
318
319 if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN)
320 return B_BUSY;
321
322 alps_cookie* cookie = (alps_cookie*)malloc(sizeof(alps_cookie));
323 if (cookie == NULL)
324 goto err1;
325 memset(cookie, 0, sizeof(*cookie));
326
327 cookie->previousZ = 0;
328 *_cookie = cookie;
329
330 cookie->dev = dev;
331 dev->cookie = cookie;
332 dev->disconnect = &alps_disconnect;
333 dev->handle_int = &alps_handle_int;
334
335 gHardwareSpecs.edgeMotionWidth = EDGE_MOTION_WIDTH;
336
337 gHardwareSpecs.areaStartX = AREA_START_X;
338 gHardwareSpecs.areaEndX = AREA_END_X;
339 gHardwareSpecs.areaStartY = AREA_START_Y;
340 gHardwareSpecs.areaEndY = AREA_END_Y;
341
342 gHardwareSpecs.minPressure = MIN_PRESSURE;
343 gHardwareSpecs.realMaxPressure = REAL_MAX_PRESSURE;
344 gHardwareSpecs.maxPressure = MAX_PRESSURE;
345
346 dev->packet_size = PS2_PACKET_ALPS;
347
348 cookie->ring_buffer = create_packet_buffer(
349 ALPS_HISTORY_SIZE * dev->packet_size);
350 if (cookie->ring_buffer == NULL) {
351 TRACE("ALPS: can't allocate mouse actions buffer\n");
352 goto err2;
353 }
354 // create the mouse semaphore, used for synchronization between
355 // the interrupt handler and the read operation
356 cookie->sem = create_sem(0, "ps2_alps_sem");
357 if (cookie->sem < 0) {
358 TRACE("ALPS: failed creating semaphore!\n");
359 goto err3;
360 }
361
362 if ((sFoundModel->flags & ALPS_PASS) != 0
363 && enable_passthrough(dev, true) != B_OK)
364 goto err4;
365
366 // switch tap mode off
367 if (switch_hardware_tab(dev, false) != B_OK)
368 goto err4;
369
370 // init the alps device to absolut mode
371 if (ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
372 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
373 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
374 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
375 || ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0) != B_OK)
376 goto err4;
377
378 if ((sFoundModel->flags & ALPS_PASS) != 0
379 && enable_passthrough(dev, false) != B_OK)
380 goto err4;
381
382 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_STREAM, NULL, 0, NULL, 0) != B_OK)
383 goto err4;
384
385 if (ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0) != B_OK)
386 goto err4;
387
388 atomic_or(&dev->flags, PS2_FLAG_ENABLED);
389
390 TRACE("ALPS: open %s success\n", name);
391 return B_OK;
392
393 err4:
394 delete_sem(cookie->sem);
395 err3:
396 delete_packet_buffer(cookie->ring_buffer);
397 err2:
398 free(cookie);
399 err1:
400 atomic_and(&dev->flags, ~PS2_FLAG_OPEN);
401
402 TRACE("ALPS: open %s failed\n", name);
403 return B_ERROR;
404 }
405
406
407 status_t
alps_close(void * _cookie)408 alps_close(void *_cookie)
409 {
410 alps_cookie *cookie = (alps_cookie*)_cookie;
411
412 ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0,
413 150000);
414
415 delete_packet_buffer(cookie->ring_buffer);
416 delete_sem(cookie->sem);
417
418 atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN);
419 atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED);
420
421 // Reset the touchpad so it generate standard ps2 packets instead of
422 // extended ones. If not, BeOS is confused with such packets when rebooting
423 // without a complete shutdown.
424 status_t status = ps2_reset_mouse(cookie->dev);
425 if (status != B_OK) {
426 INFO("ps2_alps: reset failed\n");
427 return B_ERROR;
428 }
429
430 TRACE("ALPS: close %s done\n", cookie->dev->name);
431 return B_OK;
432 }
433
434
435 status_t
alps_freecookie(void * _cookie)436 alps_freecookie(void *_cookie)
437 {
438 free(_cookie);
439 return B_OK;
440 }
441
442
443 status_t
alps_ioctl(void * _cookie,uint32 op,void * buffer,size_t length)444 alps_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
445 {
446 alps_cookie *cookie = (alps_cookie*)_cookie;
447 touchpad_read read;
448 status_t status;
449
450 switch (op) {
451 case MS_IS_TOUCHPAD:
452 TRACE("ALPS: MS_IS_TOUCHPAD\n");
453 if (buffer == NULL)
454 return B_OK;
455 return user_memcpy(buffer, &gHardwareSpecs, sizeof(gHardwareSpecs));
456
457 case MS_READ_TOUCHPAD:
458 TRACE("ALPS: MS_READ get event\n");
459 if (user_memcpy(&read.timeout, &(((touchpad_read*)buffer)->timeout),
460 sizeof(bigtime_t)) != B_OK)
461 return B_BAD_ADDRESS;
462 if ((status = get_alps_movment(cookie, &read)) != B_OK)
463 return status;
464 return user_memcpy(buffer, &read, sizeof(read));
465
466 default:
467 TRACE("ALPS: unknown opcode: %" B_PRIu32 "\n", op);
468 return B_BAD_VALUE;
469 }
470 }
471
472
473 static status_t
alps_read(void * cookie,off_t pos,void * buffer,size_t * _length)474 alps_read(void* cookie, off_t pos, void* buffer, size_t* _length)
475 {
476 *_length = 0;
477 return B_NOT_ALLOWED;
478 }
479
480
481 static status_t
alps_write(void * cookie,off_t pos,const void * buffer,size_t * _length)482 alps_write(void* cookie, off_t pos, const void* buffer, size_t* _length)
483 {
484 *_length = 0;
485 return B_NOT_ALLOWED;
486 }
487
488
489 int32
alps_handle_int(ps2_dev * dev)490 alps_handle_int(ps2_dev* dev)
491 {
492 alps_cookie* cookie = (alps_cookie*)dev->cookie;
493
494 uint8 val;
495 val = cookie->dev->history[0].data;
496 if (cookie->packet_index == 0
497 && (val & sFoundModel->maskFirstByte) != sFoundModel->firstByte) {
498 INFO("ALPS: bad header, trying resync\n");
499 cookie->packet_index = 0;
500 return B_UNHANDLED_INTERRUPT;
501 }
502
503 // data packages starting with a 0
504 if (cookie->packet_index > 1 && (val & 0x80)) {
505 INFO("ALPS: bad package data, trying resync\n");
506 cookie->packet_index = 0;
507 return B_UNHANDLED_INTERRUPT;
508 }
509
510 cookie->buffer[cookie->packet_index] = val;
511
512 cookie->packet_index++;
513 if (cookie->packet_index >= 6) {
514 cookie->packet_index = 0;
515
516 if (packet_buffer_write(cookie->ring_buffer,
517 cookie->buffer, cookie->dev->packet_size)
518 != cookie->dev->packet_size) {
519 // buffer is full, drop new data
520 return B_HANDLED_INTERRUPT;
521 }
522 release_sem_etc(cookie->sem, 1, B_DO_NOT_RESCHEDULE);
523
524 return B_INVOKE_SCHEDULER;
525 }
526
527 return B_HANDLED_INTERRUPT;
528 }
529
530
531 void
alps_disconnect(ps2_dev * dev)532 alps_disconnect(ps2_dev *dev)
533 {
534 alps_cookie *cookie = (alps_cookie*)dev->cookie;
535 // the mouse device might not be opened at this point
536 INFO("ALPS: alps_disconnect %s\n", dev->name);
537 if ((dev->flags & PS2_FLAG_OPEN) != 0)
538 release_sem(cookie->sem);
539 }
540
541
542 device_hooks gALPSDeviceHooks = {
543 alps_open,
544 alps_close,
545 alps_freecookie,
546 alps_ioctl,
547 alps_read,
548 alps_write,
549 };
550