pms.c revision 1.25
1/* $OpenBSD: pms.c,v 1.25 2011/12/03 19:43:00 mpi Exp $ */ 2/* $NetBSD: psm.c,v 1.11 2000/06/05 22:20:57 sommerfeld Exp $ */ 3 4/*- 5 * Copyright (c) 1994 Charles M. Hannum. 6 * Copyright (c) 1992, 1993 Erik Forsberg. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED 16 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 17 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 18 * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 22 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <sys/param.h> 28#include <sys/systm.h> 29#include <sys/device.h> 30#include <sys/ioctl.h> 31#include <sys/malloc.h> 32 33#include <machine/bus.h> 34 35#include <dev/ic/pckbcvar.h> 36 37#include <dev/pckbc/pmsreg.h> 38 39#include <dev/wscons/wsconsio.h> 40#include <dev/wscons/wsmousevar.h> 41 42#ifdef DEBUG 43#define DPRINTF(x...) do { printf(x); } while (0); 44#else 45#define DPRINTF(x...) 46#endif 47 48#define DEVNAME(sc) ((sc)->sc_dev.dv_xname) 49 50#define WSMOUSE_BUTTON(x) (1 << ((x) - 1)) 51 52struct pms_softc; 53 54struct pms_protocol { 55 int type; 56#define PMS_STANDARD 0 57#define PMS_INTELLI 1 58#define PMS_SYNAPTICS 2 59#define PMS_ALPS 3 60 u_int packetsize; 61 int (*enable)(struct pms_softc *); 62 int (*ioctl)(struct pms_softc *, u_long, caddr_t, int, struct proc *); 63 int (*sync)(struct pms_softc *, int); 64 void (*proc)(struct pms_softc *); 65 void (*disable)(struct pms_softc *); 66}; 67 68struct synaptics_softc { 69 int identify; 70 int capabilities, ext_capabilities; 71 int model, ext_model; 72 int resolution, dimension; 73 74 int mode; 75 76 int res_x, res_y; 77 int min_x, min_y; 78 int max_x, max_y; 79 80 /* Compat mode */ 81 int wsmode; 82 int old_x, old_y; 83 u_int old_buttons; 84#define SYNAPTICS_SCALE 4 85#define SYNAPTICS_PRESSURE 30 86}; 87 88struct alps_softc { 89 int model; 90 int mask; 91 int version; 92 93 int min_x, min_y; 94 int max_x, max_y; 95 int old_fin; 96 97 /* Compat mode */ 98 int wsmode; 99 int old_x, old_y; 100 u_int old_buttons; 101#define ALPS_PRESSURE 40 102}; 103 104struct pms_softc { /* driver status information */ 105 struct device sc_dev; 106 107 pckbc_tag_t sc_kbctag; 108 109 int sc_state; 110#define PMS_STATE_DISABLED 0 111#define PMS_STATE_ENABLED 1 112#define PMS_STATE_SUSPENDED 2 113 114 int sc_dev_enable; 115#define PMS_DEV_IGNORE 0x00 116#define PMS_DEV_PRIMARY 0x01 117#define PMS_DEV_SECONDARY 0x02 118 119 int poll; 120 int inputstate; 121 122 const struct pms_protocol *protocol; 123 struct synaptics_softc *synaptics; 124 struct alps_softc *alps; 125 126 u_char packet[8]; 127 128 struct device *sc_wsmousedev; 129 struct device *sc_pt_wsmousedev; 130}; 131 132static const u_int butmap[8] = { 133 0, 134 WSMOUSE_BUTTON(1), 135 WSMOUSE_BUTTON(3), 136 WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(3), 137 WSMOUSE_BUTTON(2), 138 WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(2), 139 WSMOUSE_BUTTON(2) | WSMOUSE_BUTTON(3), 140 WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(2) | WSMOUSE_BUTTON(3) 141}; 142 143static const struct alps_model { 144 int version; 145 int mask; 146 int model; 147} alps_models[] = { 148#if 0 149 /* FIXME some clipads are not working yet */ 150 { 0x5212, 0xff, ALPS_DUALPOINT | ALPS_PASSTHROUGH }, 151 { 0x6222, 0xcf, ALPS_DUALPOINT | ALPS_PASSTHROUGH }, 152#endif 153 { 0x2021, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH }, 154 { 0x2221, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH }, 155 { 0x2222, 0xff, ALPS_DUALPOINT | ALPS_PASSTHROUGH }, 156 { 0x3222, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH }, 157 { 0x5321, 0xf8, ALPS_GLIDEPOINT }, 158 { 0x5322, 0xf8, ALPS_GLIDEPOINT }, 159 { 0x603b, 0xf8, ALPS_GLIDEPOINT }, 160 { 0x6321, 0xf8, ALPS_GLIDEPOINT }, 161 { 0x6322, 0xf8, ALPS_GLIDEPOINT }, 162 { 0x6323, 0xf8, ALPS_GLIDEPOINT }, 163 { 0x6324, 0x8f, ALPS_GLIDEPOINT }, 164 { 0x6325, 0xef, ALPS_GLIDEPOINT }, 165 { 0x6326, 0xf8, ALPS_GLIDEPOINT }, 166 { 0x633b, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH }, 167 { 0x7301, 0xf8, ALPS_DUALPOINT }, 168 { 0x7321, 0xf8, ALPS_GLIDEPOINT }, 169 { 0x7322, 0xf8, ALPS_GLIDEPOINT }, 170 { 0x7325, 0xcf, ALPS_GLIDEPOINT }, 171#if 0 172 { 0x7326, 0, 0 }, /* XXX Uses unknown v3 protocol */ 173#endif 174}; 175 176int pmsprobe(struct device *, void *, void *); 177void pmsattach(struct device *, struct device *, void *); 178int pmsactivate(struct device *, int); 179 180void pmsinput(void *, int); 181 182int pms_change_state(struct pms_softc *, int, int); 183int pms_ioctl(void *, u_long, caddr_t, int, struct proc *); 184int pms_enable(void *); 185void pms_disable(void *); 186 187int pms_cmd(struct pms_softc *, u_char *, int, u_char *, int); 188int pms_spec_cmd(struct pms_softc *, int); 189int pms_get_devid(struct pms_softc *, u_char *); 190int pms_get_status(struct pms_softc *, u_char *); 191int pms_set_rate(struct pms_softc *, int); 192int pms_set_resolution(struct pms_softc *, int); 193int pms_set_scaling(struct pms_softc *, int); 194int pms_reset(struct pms_softc *); 195int pms_dev_enable(struct pms_softc *); 196int pms_dev_disable(struct pms_softc *); 197 198int pms_enable_intelli(struct pms_softc *); 199 200int pms_ioctl_mouse(struct pms_softc *, u_long, caddr_t, int, struct proc *); 201int pms_sync_mouse(struct pms_softc *, int); 202void pms_proc_mouse(struct pms_softc *); 203 204int pms_enable_synaptics(struct pms_softc *); 205int pms_ioctl_synaptics(struct pms_softc *, u_long, caddr_t, int, struct proc *); 206int pms_sync_synaptics(struct pms_softc *, int); 207void pms_proc_synaptics(struct pms_softc *); 208void pms_disable_synaptics(struct pms_softc *); 209 210int pms_enable_alps(struct pms_softc *); 211int pms_ioctl_alps(struct pms_softc *, u_long, caddr_t, int, struct proc *); 212int pms_sync_alps(struct pms_softc *, int); 213void pms_proc_alps(struct pms_softc *); 214 215int synaptics_set_mode(struct pms_softc *, int); 216int synaptics_query(struct pms_softc *, int, int *); 217int synaptics_get_hwinfo(struct pms_softc *); 218 219void synaptics_pt_proc(struct pms_softc *); 220 221int synaptics_pt_ioctl(void *, u_long, caddr_t, int, struct proc *); 222int synaptics_pt_enable(void *); 223void synaptics_pt_disable(void *); 224 225int alps_get_hwinfo(struct pms_softc *); 226 227struct cfattach pms_ca = { 228 sizeof(struct pms_softc), pmsprobe, pmsattach, NULL, 229 pmsactivate 230}; 231 232struct cfdriver pms_cd = { 233 NULL, "pms", DV_DULL 234}; 235 236const struct wsmouse_accessops pms_accessops = { 237 pms_enable, 238 pms_ioctl, 239 pms_disable, 240}; 241 242const struct wsmouse_accessops synaptics_pt_accessops = { 243 synaptics_pt_enable, 244 synaptics_pt_ioctl, 245 synaptics_pt_disable, 246}; 247 248const struct pms_protocol pms_protocols[] = { 249 /* Generic PS/2 mouse */ 250 { 251 PMS_STANDARD, 3, 252 NULL, 253 pms_ioctl_mouse, 254 pms_sync_mouse, 255 pms_proc_mouse, 256 NULL 257 }, 258 /* Microsoft IntelliMouse */ 259 { 260 PMS_INTELLI, 4, 261 pms_enable_intelli, 262 pms_ioctl_mouse, 263 pms_sync_mouse, 264 pms_proc_mouse, 265 NULL 266 }, 267 /* Synaptics touchpad */ 268 { 269 PMS_SYNAPTICS, 6, 270 pms_enable_synaptics, 271 pms_ioctl_synaptics, 272 pms_sync_synaptics, 273 pms_proc_synaptics, 274 pms_disable_synaptics 275 }, 276 /* ALPS touchpad */ 277 { 278 PMS_ALPS, 6, 279 pms_enable_alps, 280 pms_ioctl_alps, 281 pms_sync_alps, 282 pms_proc_alps, 283 NULL 284 }, 285}; 286 287int 288pms_cmd(struct pms_softc *sc, u_char *cmd, int len, u_char *resp, int resplen) 289{ 290 if (sc->poll) { 291 return pckbc_poll_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT, 292 cmd, len, resplen, resp, 1); 293 } else { 294 return pckbc_enqueue_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT, 295 cmd, len, resplen, 1, resp); 296 } 297} 298 299int 300pms_spec_cmd(struct pms_softc *sc, int cmd) 301{ 302 if (pms_set_scaling(sc, 1) || 303 pms_set_resolution(sc, (cmd >> 6) & 0x03) || 304 pms_set_resolution(sc, (cmd >> 4) & 0x03) || 305 pms_set_resolution(sc, (cmd >> 2) & 0x03) || 306 pms_set_resolution(sc, (cmd >> 0) & 0x03)) 307 return (-1); 308 return (0); 309} 310 311int 312pms_get_devid(struct pms_softc *sc, u_char *resp) 313{ 314 u_char cmd[1]; 315 316 cmd[0] = PMS_SEND_DEV_ID; 317 return (pms_cmd(sc, cmd, 1, resp, 1)); 318} 319 320int 321pms_get_status(struct pms_softc *sc, u_char *resp) 322{ 323 u_char cmd[1]; 324 325 cmd[0] = PMS_SEND_DEV_STATUS; 326 return (pms_cmd(sc, cmd, 1, resp, 3)); 327} 328 329int 330pms_set_rate(struct pms_softc *sc, int value) 331{ 332 u_char cmd[2]; 333 334 cmd[0] = PMS_SET_SAMPLE; 335 cmd[1] = value; 336 return (pms_cmd(sc, cmd, 2, NULL, 0)); 337} 338 339int 340pms_set_resolution(struct pms_softc *sc, int value) 341{ 342 u_char cmd[2]; 343 344 cmd[0] = PMS_SET_RES; 345 cmd[1] = value; 346 return (pms_cmd(sc, cmd, 2, NULL, 0)); 347} 348 349int 350pms_set_scaling(struct pms_softc *sc, int scale) 351{ 352 u_char cmd[1]; 353 354 switch (scale) { 355 case 1: 356 default: 357 cmd[0] = PMS_SET_SCALE11; 358 break; 359 case 2: 360 cmd[0] = PMS_SET_SCALE21; 361 break; 362 } 363 return (pms_cmd(sc, cmd, 1, NULL, 0)); 364} 365 366int 367pms_reset(struct pms_softc *sc) 368{ 369 u_char cmd[1], resp[2]; 370 int res; 371 372 cmd[0] = PMS_RESET; 373 res = pms_cmd(sc, cmd, 1, resp, 2); 374#ifdef DEBUG 375 if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) 376 printf("%s: reset error %d (response 0x%02x, type 0x%02x)\n", 377 DEVNAME(sc), res, resp[0], resp[1]); 378#endif 379 return (res); 380} 381 382int 383pms_dev_enable(struct pms_softc *sc) 384{ 385 u_char cmd[1]; 386 int res; 387 388 cmd[0] = PMS_DEV_ENABLE; 389 res = pms_cmd(sc, cmd, 1, NULL, 0); 390 if (res) 391 printf("%s: enable error\n", DEVNAME(sc)); 392 return (res); 393} 394 395int 396pms_dev_disable(struct pms_softc *sc) 397{ 398 u_char cmd[1]; 399 int res; 400 401 cmd[0] = PMS_DEV_DISABLE; 402 res = pms_cmd(sc, cmd, 1, NULL, 0); 403 if (res) 404 printf("%s: disable error\n", DEVNAME(sc)); 405 return (res); 406} 407 408int 409pms_enable_intelli(struct pms_softc *sc) 410{ 411 u_char resp; 412 413 /* the special sequence to enable the third button and the roller */ 414 if (pms_set_rate(sc, PMS_INTELLI_MAGIC1) || 415 pms_set_rate(sc, PMS_INTELLI_MAGIC2) || 416 pms_set_rate(sc, PMS_INTELLI_MAGIC3) || 417 pms_get_devid(sc, &resp) || 418 resp != PMS_INTELLI_ID) 419 return (0); 420 421 return (1); 422} 423 424int 425pms_ioctl_mouse(struct pms_softc *sc, u_long cmd, caddr_t data, int flag, 426 struct proc *p) 427{ 428 int i; 429 430 switch (cmd) { 431 case WSMOUSEIO_GTYPE: 432 *(u_int *)data = WSMOUSE_TYPE_PS2; 433 break; 434 case WSMOUSEIO_SRES: 435 i = ((int) *(u_int *)data - 12) / 25; 436 /* valid values are {0,1,2,3} */ 437 if (i < 0) 438 i = 0; 439 if (i > 3) 440 i = 3; 441 442 if (pms_set_resolution(sc, i)) 443 printf("%s: SET_RES command error\n", DEVNAME(sc)); 444 break; 445 default: 446 return (-1); 447 } 448 return (0); 449} 450 451int 452pms_sync_mouse(struct pms_softc *sc, int data) 453{ 454 if (sc->inputstate != 0) 455 return (0); 456 457 switch (sc->protocol->type) { 458 case PMS_STANDARD: 459 if ((data & 0xc0) != 0) 460 return (-1); 461 break; 462 case PMS_INTELLI: 463 if ((data & 0x08) != 0x08) 464 return (-1); 465 break; 466 } 467 468 return (0); 469} 470 471void 472pms_proc_mouse(struct pms_softc *sc) 473{ 474 u_int buttons; 475 int dx, dy, dz; 476 477 buttons = butmap[sc->packet[0] & PMS_PS2_BUTTONSMASK]; 478 dx = (sc->packet[0] & PMS_PS2_XNEG) ? 479 (int)sc->packet[1] - 256 : sc->packet[1]; 480 dy = (sc->packet[0] & PMS_PS2_YNEG) ? 481 (int)sc->packet[2] - 256 : sc->packet[2]; 482 483 switch (sc->protocol->type) { 484 case PMS_STANDARD: 485 dz = 0; 486 break; 487 case PMS_INTELLI: 488 dz = (signed char)sc->packet[3]; 489 break; 490 } 491 492 wsmouse_input(sc->sc_wsmousedev, 493 buttons, dx, dy, dz, 0, WSMOUSE_INPUT_DELTA); 494} 495 496int 497pmsprobe(struct device *parent, void *match, void *aux) 498{ 499 struct pckbc_attach_args *pa = aux; 500 u_char cmd[1], resp[2]; 501 int res; 502 503 if (pa->pa_slot != PCKBC_AUX_SLOT) 504 return (0); 505 506 /* Flush any garbage. */ 507 pckbc_flush(pa->pa_tag, pa->pa_slot); 508 509 /* reset the device */ 510 cmd[0] = PMS_RESET; 511 res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1); 512 if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) { 513#ifdef DEBUG 514 printf("pms: reset error %d (response 0x%02x, type 0x%02x)\n", 515 res, resp[0], resp[1]); 516#endif 517 return (0); 518 } 519 520 return (1); 521} 522 523void 524pmsattach(struct device *parent, struct device *self, void *aux) 525{ 526 struct pms_softc *sc = (void *)self; 527 struct pckbc_attach_args *pa = aux; 528 struct wsmousedev_attach_args a; 529 int i; 530 531 sc->sc_kbctag = pa->pa_tag; 532 533 printf("\n"); 534 535 pckbc_set_inputhandler(sc->sc_kbctag, PCKBC_AUX_SLOT, 536 pmsinput, sc, DEVNAME(sc)); 537 538 a.accessops = &pms_accessops; 539 a.accesscookie = sc; 540 541 /* 542 * Attach the wsmouse, saving a handle to it. 543 * Note that we don't need to check this pointer against NULL 544 * here or in pmsintr, because if this fails pms_enable() will 545 * never be called, so pmsinput() will never be called. 546 */ 547 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); 548 549 sc->poll = 1; 550 sc->sc_dev_enable = 0; 551 552 sc->protocol = &pms_protocols[0]; 553 for (i = 1; i < nitems(pms_protocols); i++) { 554 pms_reset(sc); 555 if (pms_protocols[i].enable(sc)) 556 sc->protocol = &pms_protocols[i]; 557 } 558 559 DPRINTF("%s: protocol type %d\n", DEVNAME(sc), sc->protocol->type); 560 561 /* no interrupts until enabled */ 562 pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_IGNORE); 563} 564 565int 566pmsactivate(struct device *self, int act) 567{ 568 struct pms_softc *sc = (struct pms_softc *)self; 569 570 switch (act) { 571 case DVACT_SUSPEND: 572 if (sc->sc_state == PMS_STATE_ENABLED) 573 pms_change_state(sc, PMS_STATE_SUSPENDED, 574 PMS_DEV_IGNORE); 575 break; 576 case DVACT_RESUME: 577 if (sc->sc_state == PMS_STATE_SUSPENDED) 578 pms_change_state(sc, PMS_STATE_ENABLED, 579 PMS_DEV_IGNORE); 580 break; 581 } 582 return (0); 583} 584 585int 586pms_change_state(struct pms_softc *sc, int newstate, int dev) 587{ 588 int i; 589 590 if (dev != PMS_DEV_IGNORE) { 591 switch (newstate) { 592 case PMS_STATE_ENABLED: 593 if (sc->sc_dev_enable & dev) 594 return (EBUSY); 595 596 sc->sc_dev_enable |= dev; 597 598 if (sc->sc_state == PMS_STATE_ENABLED) 599 return (0); 600 601 break; 602 case PMS_STATE_DISABLED: 603 sc->sc_dev_enable &= ~dev; 604 605 if (sc->sc_dev_enable) 606 return (0); 607 608 break; 609 } 610 } 611 612 switch (newstate) { 613 case PMS_STATE_ENABLED: 614 sc->inputstate = 0; 615 616 pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 1); 617 618 if (sc->poll) 619 pckbc_flush(sc->sc_kbctag, PCKBC_AUX_SLOT); 620 621 pms_reset(sc); 622 623 if (sc->protocol->type != PMS_STANDARD && 624 sc->protocol->enable(sc) == 0) 625 sc->protocol = &pms_protocols[0]; 626 627 if (sc->protocol->type == PMS_STANDARD) 628 for (i = 1; i < nitems(pms_protocols); i++) { 629 pms_reset(sc); 630 if (pms_protocols[i].enable(sc)) 631 sc->protocol = &pms_protocols[i]; 632 } 633 634#ifdef DEBUG 635 printf("%s: protocol type %d\n", DEVNAME(sc), sc->protocol->type); 636#endif 637 638 pms_dev_enable(sc); 639 break; 640 case PMS_STATE_DISABLED: 641 case PMS_STATE_SUSPENDED: 642 pms_dev_disable(sc); 643 644 if (sc->protocol && sc->protocol->disable) 645 sc->protocol->disable(sc); 646 647 pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 0); 648 break; 649 } 650 651 sc->sc_state = newstate; 652 sc->poll = (newstate == PMS_STATE_SUSPENDED) ? 1 : 0; 653 654 return (0); 655} 656 657int 658pms_enable(void *v) 659{ 660 struct pms_softc *sc = v; 661 662 return pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_PRIMARY); 663} 664 665void 666pms_disable(void *v) 667{ 668 struct pms_softc *sc = v; 669 670 pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_PRIMARY); 671} 672 673int 674pms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 675{ 676 struct pms_softc *sc = v; 677 678 if (sc->protocol && sc->protocol->ioctl) 679 return (sc->protocol->ioctl(sc, cmd, data, flag, p)); 680 else 681 return (-1); 682} 683 684void 685pmsinput(void *vsc, int data) 686{ 687 struct pms_softc *sc = vsc; 688 689 if (sc->sc_state != PMS_STATE_ENABLED) { 690 /* Interrupts are not expected. Discard the byte. */ 691 return; 692 } 693 694 if (sc->protocol->sync(sc, data)) { 695#ifdef DIAGNOSTIC 696 printf("%s: not in sync yet, discard input\n", DEVNAME(sc)); 697#endif 698 sc->inputstate = 0; 699 return; 700 } 701 702 sc->packet[sc->inputstate++] = data; 703 if (sc->inputstate != sc->protocol->packetsize) 704 return; 705 706 sc->protocol->proc(sc); 707 sc->inputstate = 0; 708} 709 710int 711synaptics_set_mode(struct pms_softc *sc, int mode) 712{ 713 struct synaptics_softc *syn = sc->synaptics; 714 715 if (pms_spec_cmd(sc, mode) || 716 pms_set_rate(sc, SYNAPTICS_CMD_SET_MODE)) 717 return (-1); 718 719 syn->mode = mode; 720 721 return (0); 722} 723 724int 725synaptics_query(struct pms_softc *sc, int query, int *val) 726{ 727 u_char resp[3]; 728 729 if (pms_spec_cmd(sc, query) || 730 pms_get_status(sc, resp)) 731 return (-1); 732 733 if (val) 734 *val = (resp[0] << 16) | (resp[1] << 8) | resp[2]; 735 736 return (0); 737} 738 739int 740synaptics_get_hwinfo(struct pms_softc *sc) 741{ 742 struct synaptics_softc *syn = sc->synaptics; 743 744 if (synaptics_query(sc, SYNAPTICS_QUE_IDENTIFY, &syn->identify)) 745 return (-1); 746 if (synaptics_query(sc, SYNAPTICS_QUE_CAPABILITIES, 747 &syn->capabilities)) 748 return (-1); 749 if (synaptics_query(sc, SYNAPTICS_QUE_MODEL, &syn->model)) 750 return (-1); 751 if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 1) && 752 synaptics_query(sc, SYNAPTICS_QUE_EXT_MODEL, &syn->ext_model)) 753 return (-1); 754 if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 4) && 755 synaptics_query(sc, SYNAPTICS_QUE_EXT_CAPABILITIES, 756 &syn->ext_capabilities)) 757 return (-1); 758 if ((SYNAPTICS_ID_MAJOR(syn->identify) >= 4) && 759 synaptics_query(sc, SYNAPTICS_QUE_RESOLUTION, &syn->resolution)) 760 return (-1); 761 if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 5) && 762 (syn->ext_capabilities & SYNAPTICS_EXT_CAP_MAX_DIMENSIONS) && 763 synaptics_query(sc, SYNAPTICS_QUE_EXT_DIMENSIONS, &syn->dimension)) 764 return (-1); 765 766 syn->res_x = SYNAPTICS_RESOLUTION_X(syn->resolution); 767 syn->res_y = SYNAPTICS_RESOLUTION_Y(syn->resolution); 768 syn->min_x = SYNAPTICS_XMIN_BEZEL; 769 syn->min_y = SYNAPTICS_YMIN_BEZEL; 770 syn->max_x = (syn->dimension) ? 771 SYNAPTICS_DIM_X(syn->dimension) : SYNAPTICS_XMAX_BEZEL; 772 syn->max_y = (syn->dimension) ? 773 SYNAPTICS_DIM_Y(syn->dimension) : SYNAPTICS_YMAX_BEZEL; 774 775 if (SYNAPTICS_EXT_MODEL_BUTTONS(syn->ext_model) > 8) 776 syn->ext_model &= ~0xf000; 777 778 return (0); 779} 780 781void 782synaptics_pt_proc(struct pms_softc *sc) 783{ 784 u_int buttons; 785 int dx, dy; 786 787 if ((sc->sc_dev_enable & PMS_DEV_SECONDARY) == 0) 788 return; 789 790 buttons = butmap[sc->packet[1] & PMS_PS2_BUTTONSMASK]; 791 dx = (sc->packet[1] & PMS_PS2_XNEG) ? 792 (int)sc->packet[4] - 256 : sc->packet[4]; 793 dy = (sc->packet[1] & PMS_PS2_YNEG) ? 794 (int)sc->packet[5] - 256 : sc->packet[5]; 795 796 wsmouse_input(sc->sc_pt_wsmousedev, 797 buttons, dx, dy, 0, 0, WSMOUSE_INPUT_DELTA); 798} 799 800int 801synaptics_pt_enable(void *v) 802{ 803 struct pms_softc *sc = v; 804 805 return (pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_SECONDARY)); 806} 807 808void 809synaptics_pt_disable(void *v) 810{ 811 struct pms_softc *sc = v; 812 813 pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_SECONDARY); 814} 815 816int 817synaptics_pt_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 818{ 819 switch (cmd) { 820 case WSMOUSEIO_GTYPE: 821 *(u_int *)data = WSMOUSE_TYPE_PS2; 822 break; 823 default: 824 return (-1); 825 } 826 return (0); 827} 828 829int 830pms_enable_synaptics(struct pms_softc *sc) 831{ 832 struct synaptics_softc *syn = sc->synaptics; 833 struct wsmousedev_attach_args a; 834 u_char resp[3]; 835 int mode; 836 837 if (pms_set_resolution(sc, 0) || 838 pms_set_resolution(sc, 0) || 839 pms_set_resolution(sc, 0) || 840 pms_set_resolution(sc, 0) || 841 pms_get_status(sc, resp) || 842 resp[1] != SYNAPTICS_ID_MAGIC) 843 return (0); 844 845 if (sc->synaptics == NULL) { 846 sc->synaptics = syn = malloc(sizeof(struct synaptics_softc), 847 M_DEVBUF, M_WAITOK | M_ZERO); 848 if (syn == NULL) { 849 printf("%s: synaptics: not enough memory\n", 850 DEVNAME(sc)); 851 return (0); 852 } 853 854 if (synaptics_get_hwinfo(sc)) 855 return (0); 856 857 if ((syn->model & SYNAPTICS_MODEL_NEWABS) == 0) { 858 printf("%s: don't support Synaptics OLDABS\n", 859 DEVNAME(sc)); 860 return (0); 861 } 862 863 /* enable pass-through PS/2 port if supported */ 864 if (syn->capabilities & SYNAPTICS_CAP_PASSTHROUGH) { 865 a.accessops = &synaptics_pt_accessops; 866 a.accesscookie = sc; 867 sc->sc_pt_wsmousedev = config_found((void *)sc, &a, 868 wsmousedevprint); 869 } 870 871 syn->wsmode = WSMOUSE_COMPAT; 872 873 printf("%s: Synaptics %s, firmware %d.%d\n", DEVNAME(sc), 874 (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD ? 875 "clickpad" : "touchpad"), 876 SYNAPTICS_ID_MAJOR(syn->identify), 877 SYNAPTICS_ID_MINOR(syn->identify)); 878 } 879 880 mode = SYNAPTICS_ABSOLUTE_MODE | SYNAPTICS_HIGH_RATE; 881 if (SYNAPTICS_ID_MAJOR(syn->identify) >= 4) 882 mode |= SYNAPTICS_DISABLE_GESTURE; 883 if (syn->capabilities & SYNAPTICS_CAP_EXTENDED) 884 mode |= SYNAPTICS_W_MODE; 885 if (synaptics_set_mode(sc, mode)) 886 return (0); 887 888 /* enable advanced gesture mode if supported */ 889 if ((syn->ext_capabilities & SYNAPTICS_EXT_CAP_ADV_GESTURE) && 890 (pms_spec_cmd(sc, SYNAPTICS_QUE_MODEL) || 891 pms_set_rate(sc, SYNAPTICS_CMD_SET_ADV_GESTURE_MODE))) 892 return (0); 893 894 return (1); 895} 896 897int 898pms_ioctl_synaptics(struct pms_softc *sc, u_long cmd, caddr_t data, int flag, 899 struct proc *p) 900{ 901 struct synaptics_softc *syn = sc->synaptics; 902 struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data; 903 int wsmode; 904 905 switch (cmd) { 906 case WSMOUSEIO_GTYPE: 907 *(u_int *)data = WSMOUSE_TYPE_SYNAPTICS; 908 break; 909 case WSMOUSEIO_GCALIBCOORDS: 910 wsmc->minx = syn->min_x; 911 wsmc->maxx = syn->max_x; 912 wsmc->miny = syn->min_y; 913 wsmc->maxy = syn->max_y; 914 wsmc->swapxy = 0; 915 wsmc->resx = syn->res_x; 916 wsmc->resy = syn->res_y; 917 break; 918 case WSMOUSEIO_SETMODE: 919 wsmode = *(u_int *)data; 920 if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE) 921 return (EINVAL); 922 syn->wsmode = wsmode; 923 break; 924 default: 925 return (-1); 926 } 927 return (0); 928} 929 930int 931pms_sync_synaptics(struct pms_softc *sc, int data) 932{ 933 switch (sc->inputstate) { 934 case 0: 935 if ((data & 0xc8) != 0x80) 936 return (-1); 937 break; 938 case 3: 939 if ((data & 0xc8) != 0xc0) 940 return (-1); 941 break; 942 } 943 944 return (0); 945} 946 947void 948pms_proc_synaptics(struct pms_softc *sc) 949{ 950 struct synaptics_softc *syn = sc->synaptics; 951 u_int buttons; 952 int x, y, z, w, dx, dy; 953 954 w = ((sc->packet[0] & 0x30) >> 2) | ((sc->packet[0] & 0x04) >> 1) | 955 ((sc->packet[3] & 0x04) >> 2); 956 957 if ((syn->capabilities & SYNAPTICS_CAP_PASSTHROUGH) && w == 3) { 958 synaptics_pt_proc(sc); 959 return; 960 } 961 962 if ((sc->sc_dev_enable & PMS_DEV_PRIMARY) == 0) 963 return; 964 965 /* XXX ignore advanced gesture packet, not yet supported */ 966 if ((syn->ext_capabilities & SYNAPTICS_EXT_CAP_ADV_GESTURE) && w == 2) 967 return; 968 969 x = ((sc->packet[3] & 0x10) << 8) | ((sc->packet[1] & 0x0f) << 8) | 970 sc->packet[4]; 971 y = ((sc->packet[3] & 0x20) << 7) | ((sc->packet[1] & 0xf0) << 4) | 972 sc->packet[5]; 973 z = sc->packet[2]; 974 975 buttons = ((sc->packet[0] & sc->packet[3]) & 0x01) ? 976 WSMOUSE_BUTTON(1) : 0; 977 buttons |= ((sc->packet[0] & sc->packet[3]) & 0x02) ? 978 WSMOUSE_BUTTON(3) : 0; 979 980 if (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD) { 981 buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ? 982 WSMOUSE_BUTTON(1) : 0; 983 } else if (syn->capabilities & SYNAPTICS_CAP_MIDDLE_BUTTON) { 984 buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ? 985 WSMOUSE_BUTTON(2) : 0; 986 } 987 988 if (syn->capabilities & SYNAPTICS_CAP_FOUR_BUTTON) { 989 buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ? 990 WSMOUSE_BUTTON(4) : 0; 991 buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x02) ? 992 WSMOUSE_BUTTON(5) : 0; 993 } else if (SYNAPTICS_EXT_MODEL_BUTTONS(syn->ext_model) && 994 ((sc->packet[0] ^ sc->packet[3]) & 0x02)) { 995 buttons |= (sc->packet[4] & 0x01) ? WSMOUSE_BUTTON(6) : 0; 996 buttons |= (sc->packet[5] & 0x01) ? WSMOUSE_BUTTON(7) : 0; 997 buttons |= (sc->packet[4] & 0x02) ? WSMOUSE_BUTTON(8) : 0; 998 buttons |= (sc->packet[5] & 0x02) ? WSMOUSE_BUTTON(9) : 0; 999 buttons |= (sc->packet[4] & 0x04) ? WSMOUSE_BUTTON(10) : 0; 1000 buttons |= (sc->packet[5] & 0x04) ? WSMOUSE_BUTTON(11) : 0; 1001 buttons |= (sc->packet[4] & 0x08) ? WSMOUSE_BUTTON(12) : 0; 1002 buttons |= (sc->packet[5] & 0x08) ? WSMOUSE_BUTTON(13) : 0; 1003 x &= ~0x0f; 1004 y &= ~0x0f; 1005 } 1006 1007 /* ignore final events that happen when removing all fingers */ 1008 if (x <= 1 || y <= 1) { 1009 x = syn->old_x; 1010 y = syn->old_y; 1011 } 1012 1013 if (syn->wsmode == WSMOUSE_NATIVE) { 1014 wsmouse_input(sc->sc_wsmousedev, buttons, x, y, z, w, 1015 WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y | 1016 WSMOUSE_INPUT_ABSOLUTE_Z | WSMOUSE_INPUT_ABSOLUTE_W); 1017 } else { 1018 dx = dy = 0; 1019 if (z > SYNAPTICS_PRESSURE) { 1020 dx = x - syn->old_x; 1021 dy = y - syn->old_y; 1022 dx /= SYNAPTICS_SCALE; 1023 dy /= SYNAPTICS_SCALE; 1024 } 1025 if (dx || dy || buttons != syn->old_buttons) 1026 wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, 0, 0, 1027 WSMOUSE_INPUT_DELTA); 1028 syn->old_buttons = buttons; 1029 } 1030 1031 syn->old_x = x; 1032 syn->old_y = y; 1033} 1034 1035void 1036pms_disable_synaptics(struct pms_softc *sc) 1037{ 1038 struct synaptics_softc *syn = sc->synaptics; 1039 1040 if (syn->capabilities & SYNAPTICS_CAP_SLEEP) 1041 synaptics_set_mode(sc, SYNAPTICS_SLEEP_MODE | 1042 SYNAPTICS_DISABLE_GESTURE); 1043} 1044 1045int 1046alps_get_hwinfo(struct pms_softc *sc) 1047{ 1048 struct alps_softc *alps = sc->alps; 1049 u_char resp[3]; 1050 int i; 1051 1052 if (pms_set_resolution(sc, 0) || 1053 pms_set_scaling(sc, 2) || 1054 pms_set_scaling(sc, 2) || 1055 pms_set_scaling(sc, 2) || 1056 pms_get_status(sc, resp)) { 1057 DPRINTF("%s: alps: model query error\n", DEVNAME(sc)); 1058 return (-1); 1059 } 1060 1061 alps->version = (resp[0] << 8) | (resp[1] << 4) | (resp[2] / 20 + 1); 1062 1063 for (i = 0; i < nitems(alps_models); i++) 1064 if (alps->version == alps_models[i].version) { 1065 alps->model = alps_models[i].model; 1066 alps->mask = alps_models[i].mask; 1067 return (0); 1068 } 1069 1070 return (-1); 1071 1072} 1073 1074int 1075pms_enable_alps(struct pms_softc *sc) 1076{ 1077 struct alps_softc *alps = sc->alps; 1078 struct wsmousedev_attach_args a; 1079 u_char resp[3]; 1080 1081 if (pms_set_resolution(sc, 0) || 1082 pms_set_scaling(sc, 1) || 1083 pms_set_scaling(sc, 1) || 1084 pms_set_scaling(sc, 1) || 1085 pms_get_status(sc, resp) || 1086 resp[0] != PMS_ALPS_MAGIC1 || 1087 resp[1] != PMS_ALPS_MAGIC2 || 1088 (resp[2] != PMS_ALPS_MAGIC3_1 && resp[2] != PMS_ALPS_MAGIC3_2)) 1089 return (0); 1090 1091 if (sc->alps == NULL) { 1092 sc->alps = alps = malloc(sizeof(struct alps_softc), 1093 M_DEVBUF, M_WAITOK | M_ZERO); 1094 if (alps == NULL) { 1095 printf("%s: alps: not enough memory\n", DEVNAME(sc)); 1096 goto err; 1097 } 1098 1099 if (alps_get_hwinfo(sc)) 1100 goto err; 1101 1102 printf("%s: ALPS %s, version 0x%04x\n", DEVNAME(sc), 1103 (alps->model & ALPS_DUALPOINT ? "Dualpoint" : "Glidepoint"), 1104 alps->version); 1105 1106 alps->min_x = ALPS_XMIN_BEZEL; 1107 alps->min_y = ALPS_YMIN_BEZEL; 1108 alps->max_x = ALPS_XMAX_BEZEL; 1109 alps->max_y = ALPS_YMAX_BEZEL; 1110 1111 alps->wsmode = WSMOUSE_COMPAT; 1112 1113 if (alps->model & ALPS_DUALPOINT) { 1114 a.accessops = &synaptics_pt_accessops; 1115 a.accesscookie = sc; 1116 sc->sc_pt_wsmousedev = config_found((void *)sc, &a, 1117 wsmousedevprint); 1118 } 1119 } 1120 1121 if (alps->model == 0) 1122 goto err; 1123 1124 if ((alps->model & ALPS_PASSTHROUGH) && 1125 (pms_set_scaling(sc, 2) || 1126 pms_set_scaling(sc, 2) || 1127 pms_set_scaling(sc, 2) || 1128 pms_dev_disable(sc))) { 1129 DPRINTF("%s: alps: passthrough on error\n", DEVNAME(sc)); 1130 goto err; 1131 } 1132 1133 if (pms_dev_disable(sc) || 1134 pms_dev_disable(sc) || 1135 pms_set_rate(sc, 0x0a)) { 1136 DPRINTF("%s: alps: tapping error\n", DEVNAME(sc)); 1137 goto err; 1138 } 1139 1140 if (pms_dev_disable(sc) || 1141 pms_dev_disable(sc) || 1142 pms_dev_disable(sc) || 1143 pms_dev_disable(sc) || 1144 pms_dev_enable(sc)) { 1145 DPRINTF("%s: alps: absolute mode error\n", DEVNAME(sc)); 1146 goto err; 1147 } 1148 1149 if ((alps->model & ALPS_PASSTHROUGH) && 1150 (pms_set_scaling(sc, 1) || 1151 pms_set_scaling(sc, 1) || 1152 pms_set_scaling(sc, 1) || 1153 pms_dev_disable(sc))) { 1154 DPRINTF("%s: alps: passthrough off error\n", DEVNAME(sc)); 1155 goto err; 1156 } 1157 1158 return (1); 1159 1160err: 1161 pms_reset(sc); 1162 1163 return (0); 1164} 1165 1166int 1167pms_ioctl_alps(struct pms_softc *sc, u_long cmd, caddr_t data, int flag, 1168 struct proc *p) 1169{ 1170 struct alps_softc *alps = sc->alps; 1171 struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data; 1172 int wsmode; 1173 1174 switch (cmd) { 1175 case WSMOUSEIO_GTYPE: 1176 *(u_int *)data = WSMOUSE_TYPE_ALPS; 1177 break; 1178 case WSMOUSEIO_GCALIBCOORDS: 1179 wsmc->minx = alps->min_x; 1180 wsmc->maxx = alps->max_x; 1181 wsmc->miny = alps->min_y; 1182 wsmc->maxy = alps->max_y; 1183 wsmc->swapxy = 0; 1184 break; 1185 case WSMOUSEIO_SETMODE: 1186 wsmode = *(u_int *)data; 1187 if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE) 1188 return (EINVAL); 1189 alps->wsmode = wsmode; 1190 break; 1191 default: 1192 return (-1); 1193 } 1194 return (0); 1195} 1196 1197int 1198pms_sync_alps(struct pms_softc *sc, int data) 1199{ 1200 struct alps_softc *alps = sc->alps; 1201 1202 switch (sc->inputstate) { 1203 case 0: 1204 if ((data & alps->mask) != alps->mask) 1205 return (-1); 1206 break; 1207 case 1: 1208 case 2: 1209 case 3: 1210 case 4: 1211 case 5: 1212 if ((data & 0x80) != 0) 1213 return (-1); 1214 break; 1215 } 1216 1217 return (0); 1218} 1219 1220void 1221pms_proc_alps(struct pms_softc *sc) 1222{ 1223 struct alps_softc *alps = sc->alps; 1224 int x, y, z, dx, dy; 1225 u_int buttons; 1226 int fin, ges; 1227 1228 x = sc->packet[1] | ((sc->packet[2] & 0x78) << 4); 1229 y = sc->packet[4] | ((sc->packet[3] & 0x70) << 3); 1230 z = sc->packet[5]; 1231 1232 buttons = ((sc->packet[3] & 1) ? WSMOUSE_BUTTON(1) : 0) | 1233 ((sc->packet[3] & 2) ? WSMOUSE_BUTTON(3) : 0) | 1234 ((sc->packet[3] & 4) ? WSMOUSE_BUTTON(2) : 0); 1235 1236 if ((sc->sc_dev_enable & PMS_DEV_SECONDARY) && z == ALPS_Z_MAGIC) { 1237 dx = (x > ALPS_XSEC_BEZEL / 2) ? (x - ALPS_XSEC_BEZEL) : x; 1238 dy = (y > ALPS_YSEC_BEZEL / 2) ? (y - ALPS_YSEC_BEZEL) : y; 1239 1240 wsmouse_input(sc->sc_pt_wsmousedev, buttons, dx, dy, 0, 0, 1241 WSMOUSE_INPUT_DELTA); 1242 1243 return; 1244 } 1245 1246 if ((sc->sc_dev_enable & PMS_DEV_PRIMARY) == 0) 1247 return; 1248 1249 /* 1250 * XXX The Y-axis is in the oposit direction compared to 1251 * Synaptics touchpads and PS/2 mouses. 1252 * It's why we need to translate the y value here for both 1253 * NATIVE and COMPAT modes. 1254 */ 1255 y = ALPS_YMAX_BEZEL - y + ALPS_YMIN_BEZEL; 1256 1257 if (alps->wsmode == WSMOUSE_NATIVE) { 1258 ges = sc->packet[2] & 0x01; 1259 fin = sc->packet[2] & 0x02; 1260 1261 /* Simulate click (tap) */ 1262 if (ges && !fin) 1263 z = 35; 1264 1265 /* Generate a null pressure event (needed for tap & drag) */ 1266 if (ges && fin && !alps->old_fin) 1267 z = 0; 1268 1269 wsmouse_input(sc->sc_wsmousedev, buttons, x, y, z, 0, 1270 WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y | 1271 WSMOUSE_INPUT_ABSOLUTE_Z); 1272 1273 alps->old_fin = fin; 1274 } else { 1275 dx = dy = 0; 1276 if (z > ALPS_PRESSURE) { 1277 dx = x - alps->old_x; 1278 dy = y - alps->old_y; 1279 1280 /* Prevent jump */ 1281 dx = abs(dx) > 50 ? 0 : dx; 1282 dy = abs(dy) > 50 ? 0 : dy; 1283 } 1284 1285 if (dx || dy || buttons != alps->old_buttons) 1286 wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, 0, 0, 1287 WSMOUSE_INPUT_DELTA); 1288 1289 alps->old_x = x; 1290 alps->old_y = y; 1291 alps->old_buttons = buttons; 1292 } 1293} 1294