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 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 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 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 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 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 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 436 alps_freecookie(void *_cookie) 437 { 438 free(_cookie); 439 return B_OK; 440 } 441 442 443 status_t 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 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 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 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 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