1 /* $OpenBSD: pms.c,v 1.18 2011/01/03 19:46:34 shadchin 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 32 #include <machine/bus.h> 33 34 #include <dev/ic/pckbcvar.h> 35 36 #include <dev/pckbc/pmsreg.h> 37 38 #include <dev/wscons/wsconsio.h> 39 #include <dev/wscons/wsmousevar.h> 40 41 #define DEVNAME(sc) ((sc)->sc_dev.dv_xname) 42 43 struct pms_softc; 44 45 struct pms_protocol { 46 int type; 47 #define PMS_STANDARD 0 48 #define PMS_INTELLI 1 49 u_int packetsize; 50 int (*enable)(struct pms_softc *); 51 int (*ioctl)(struct pms_softc *, u_long, caddr_t, int, struct proc *); 52 int (*sync)(struct pms_softc *, int); 53 void (*proc)(struct pms_softc *); 54 void (*disable)(struct pms_softc *); 55 }; 56 57 struct pms_softc { /* driver status information */ 58 struct device sc_dev; 59 60 pckbc_tag_t sc_kbctag; 61 62 int sc_state; 63 #define PMS_STATE_DISABLED 0 64 #define PMS_STATE_ENABLED 1 65 #define PMS_STATE_SUSPENDED 2 66 67 int poll; 68 int inputstate; 69 70 const struct pms_protocol *protocol; 71 72 u_char packet[8]; 73 74 struct device *sc_wsmousedev; 75 }; 76 77 #define PMS_BUTTON1DOWN 0x0001 /* left */ 78 #define PMS_BUTTON2DOWN 0x0002 /* middle */ 79 #define PMS_BUTTON3DOWN 0x0004 /* right */ 80 81 static const u_int butmap[8] = { 82 0, 83 PMS_BUTTON1DOWN, 84 PMS_BUTTON3DOWN, 85 PMS_BUTTON1DOWN | PMS_BUTTON3DOWN, 86 PMS_BUTTON2DOWN, 87 PMS_BUTTON1DOWN | PMS_BUTTON2DOWN, 88 PMS_BUTTON2DOWN | PMS_BUTTON3DOWN, 89 PMS_BUTTON1DOWN | PMS_BUTTON2DOWN | PMS_BUTTON3DOWN 90 }; 91 92 /* PS/2 mouse data packet */ 93 #define PMS_PS2_BUTTONSMASK 0x07 94 #define PMS_PS2_BUTTON1 0x01 /* left */ 95 #define PMS_PS2_BUTTON2 0x04 /* middle */ 96 #define PMS_PS2_BUTTON3 0x02 /* right */ 97 #define PMS_PS2_XNEG 0x10 98 #define PMS_PS2_YNEG 0x20 99 100 #define PMS_INTELLI_MAGIC1 200 101 #define PMS_INTELLI_MAGIC2 100 102 #define PMS_INTELLI_MAGIC3 80 103 #define PMS_INTELLI_ID 0x03 104 105 int pmsprobe(struct device *, void *, void *); 106 void pmsattach(struct device *, struct device *, void *); 107 int pmsactivate(struct device *, int); 108 109 void pmsinput(void *, int); 110 111 int pms_change_state(struct pms_softc *, int); 112 int pms_ioctl(void *, u_long, caddr_t, int, struct proc *); 113 int pms_enable(void *); 114 void pms_disable(void *); 115 116 int pms_cmd(struct pms_softc *, u_char *, int, u_char *, int); 117 int pms_get_devid(struct pms_softc *, u_char *); 118 int pms_get_status(struct pms_softc *, u_char *); 119 int pms_set_rate(struct pms_softc *, int); 120 int pms_set_resolution(struct pms_softc *, int); 121 int pms_set_scaling(struct pms_softc *, int); 122 int pms_reset(struct pms_softc *); 123 int pms_dev_enable(struct pms_softc *); 124 int pms_dev_disable(struct pms_softc *); 125 126 int pms_enable_intelli(struct pms_softc *); 127 128 int pms_ioctl_mouse(struct pms_softc *, u_long, caddr_t, int, struct proc *); 129 int pms_sync_mouse(struct pms_softc *, int); 130 void pms_proc_mouse(struct pms_softc *); 131 132 struct cfattach pms_ca = { 133 sizeof(struct pms_softc), pmsprobe, pmsattach, NULL, 134 pmsactivate 135 }; 136 137 struct cfdriver pms_cd = { 138 NULL, "pms", DV_DULL 139 }; 140 141 const struct wsmouse_accessops pms_accessops = { 142 pms_enable, 143 pms_ioctl, 144 pms_disable, 145 }; 146 147 const struct pms_protocol pms_mouse[] = { 148 /* Generic PS/2 mouse */ 149 { 150 PMS_STANDARD, 3, 151 NULL, 152 pms_ioctl_mouse, 153 pms_sync_mouse, 154 pms_proc_mouse, 155 NULL 156 }, 157 /* Microsoft IntelliMouse */ 158 { 159 PMS_INTELLI, 4, 160 pms_enable_intelli, 161 pms_ioctl_mouse, 162 pms_sync_mouse, 163 pms_proc_mouse, 164 NULL 165 } 166 }; 167 168 int 169 pms_cmd(struct pms_softc *sc, u_char *cmd, int len, u_char *resp, int resplen) 170 { 171 if (sc->poll) { 172 return pckbc_poll_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT, 173 cmd, len, resplen, resp, 1); 174 } else { 175 return pckbc_enqueue_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT, 176 cmd, len, resplen, 1, resp); 177 } 178 } 179 180 int 181 pms_get_devid(struct pms_softc *sc, u_char *resp) 182 { 183 u_char cmd[1]; 184 185 cmd[0] = PMS_SEND_DEV_ID; 186 return (pms_cmd(sc, cmd, 1, resp, 1)); 187 } 188 189 int 190 pms_get_status(struct pms_softc *sc, u_char *resp) 191 { 192 u_char cmd[1]; 193 194 cmd[0] = PMS_SEND_DEV_STATUS; 195 return (pms_cmd(sc, cmd, 1, resp, 3)); 196 } 197 198 int 199 pms_set_rate(struct pms_softc *sc, int value) 200 { 201 u_char cmd[2]; 202 203 cmd[0] = PMS_SET_SAMPLE; 204 cmd[1] = value; 205 return (pms_cmd(sc, cmd, 2, NULL, 0)); 206 } 207 208 int 209 pms_set_resolution(struct pms_softc *sc, int value) 210 { 211 u_char cmd[2]; 212 213 cmd[0] = PMS_SET_RES; 214 cmd[1] = value; 215 return (pms_cmd(sc, cmd, 2, NULL, 0)); 216 } 217 218 int 219 pms_set_scaling(struct pms_softc *sc, int scale) 220 { 221 u_char cmd[1]; 222 223 switch (scale) { 224 case 1: 225 default: 226 cmd[0] = PMS_SET_SCALE11; 227 break; 228 case 2: 229 cmd[0] = PMS_SET_SCALE21; 230 break; 231 } 232 return (pms_cmd(sc, cmd, 1, NULL, 0)); 233 } 234 235 int 236 pms_reset(struct pms_softc *sc) 237 { 238 u_char cmd[1], resp[2]; 239 int res; 240 241 cmd[0] = PMS_RESET; 242 res = pms_cmd(sc, cmd, 1, resp, 2); 243 #ifdef DEBUG 244 if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) 245 printf("%s: reset error %d (response 0x%02x, type 0x%02x)\n", 246 DEVNAME(sc), res, resp[0], resp[1]); 247 #endif 248 return (res); 249 } 250 251 int 252 pms_dev_enable(struct pms_softc *sc) 253 { 254 u_char cmd[1]; 255 int res; 256 257 cmd[0] = PMS_DEV_ENABLE; 258 res = pms_cmd(sc, cmd, 1, NULL, 0); 259 if (res) 260 printf("%s: enable error\n", DEVNAME(sc)); 261 return (res); 262 } 263 264 int 265 pms_dev_disable(struct pms_softc *sc) 266 { 267 u_char cmd[1]; 268 int res; 269 270 cmd[0] = PMS_DEV_DISABLE; 271 res = pms_cmd(sc, cmd, 1, NULL, 0); 272 if (res) 273 printf("%s: disable error\n", DEVNAME(sc)); 274 return (res); 275 } 276 277 int 278 pms_enable_intelli(struct pms_softc *sc) 279 { 280 u_char resp; 281 282 /* the special sequence to enable the third button and the roller */ 283 if (pms_set_rate(sc, PMS_INTELLI_MAGIC1) || 284 pms_set_rate(sc, PMS_INTELLI_MAGIC2) || 285 pms_set_rate(sc, PMS_INTELLI_MAGIC3) || 286 pms_get_devid(sc, &resp) || 287 resp != PMS_INTELLI_ID) 288 return (0); 289 290 return (1); 291 } 292 293 int 294 pms_ioctl_mouse(struct pms_softc *sc, u_long cmd, caddr_t data, int flag, 295 struct proc *p) 296 { 297 int i; 298 299 switch (cmd) { 300 case WSMOUSEIO_GTYPE: 301 *(u_int *)data = WSMOUSE_TYPE_PS2; 302 break; 303 case WSMOUSEIO_SRES: 304 i = ((int) *(u_int *)data - 12) / 25; 305 /* valid values are {0,1,2,3} */ 306 if (i < 0) 307 i = 0; 308 if (i > 3) 309 i = 3; 310 311 if (pms_set_resolution(sc, i)) 312 printf("%s: SET_RES command error\n", DEVNAME(sc)); 313 break; 314 default: 315 return (-1); 316 } 317 return (0); 318 } 319 320 int 321 pms_sync_mouse(struct pms_softc *sc, int data) 322 { 323 if (sc->inputstate != 0) 324 return (0); 325 326 switch (sc->protocol->type) { 327 case PMS_STANDARD: 328 if ((data & 0xc0) != 0) 329 return (-1); 330 break; 331 case PMS_INTELLI: 332 if ((data & 0x08) != 0x08) 333 return (-1); 334 break; 335 } 336 337 return (0); 338 } 339 340 void 341 pms_proc_mouse(struct pms_softc *sc) 342 { 343 u_int buttons; 344 int dx, dy, dz; 345 346 buttons = butmap[sc->packet[0] & PMS_PS2_BUTTONSMASK]; 347 dx = (sc->packet[0] & PMS_PS2_XNEG) ? 348 (int)sc->packet[1] - 256 : sc->packet[1]; 349 dy = (sc->packet[0] & PMS_PS2_YNEG) ? 350 (int)sc->packet[2] - 256 : sc->packet[2]; 351 352 switch (sc->protocol->type) { 353 case PMS_STANDARD: 354 dz = 0; 355 break; 356 case PMS_INTELLI: 357 dz = (signed char)sc->packet[3]; 358 break; 359 } 360 361 wsmouse_input(sc->sc_wsmousedev, 362 buttons, dx, dy, dz, 0, WSMOUSE_INPUT_DELTA); 363 } 364 365 int 366 pmsprobe(struct device *parent, void *match, void *aux) 367 { 368 struct pckbc_attach_args *pa = aux; 369 u_char cmd[1], resp[2]; 370 int res; 371 372 if (pa->pa_slot != PCKBC_AUX_SLOT) 373 return (0); 374 375 /* Flush any garbage. */ 376 pckbc_flush(pa->pa_tag, pa->pa_slot); 377 378 /* reset the device */ 379 cmd[0] = PMS_RESET; 380 res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1); 381 if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) { 382 #ifdef DEBUG 383 printf("pms: reset error %d (response 0x%02x, type 0x%02x)\n", 384 res, resp[0], resp[1]); 385 #endif 386 return (0); 387 } 388 389 return (1); 390 } 391 392 void 393 pmsattach(struct device *parent, struct device *self, void *aux) 394 { 395 struct pms_softc *sc = (void *)self; 396 struct pckbc_attach_args *pa = aux; 397 struct wsmousedev_attach_args a; 398 399 sc->sc_kbctag = pa->pa_tag; 400 401 printf("\n"); 402 403 pckbc_set_inputhandler(sc->sc_kbctag, PCKBC_AUX_SLOT, 404 pmsinput, sc, DEVNAME(sc)); 405 406 a.accessops = &pms_accessops; 407 a.accesscookie = sc; 408 409 /* 410 * Attach the wsmouse, saving a handle to it. 411 * Note that we don't need to check this pointer against NULL 412 * here or in pmsintr, because if this fails pms_enable() will 413 * never be called, so pmsinput() will never be called. 414 */ 415 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); 416 417 /* no interrupts until enabled */ 418 sc->poll = 1; 419 pms_change_state(sc, PMS_STATE_ENABLED); 420 sc->poll = 1; /* XXX */ 421 pms_change_state(sc, PMS_STATE_DISABLED); 422 } 423 424 int 425 pmsactivate(struct device *self, int act) 426 { 427 struct pms_softc *sc = (struct pms_softc *)self; 428 429 switch (act) { 430 case DVACT_SUSPEND: 431 if (sc->sc_state == PMS_STATE_ENABLED) 432 pms_change_state(sc, PMS_STATE_SUSPENDED); 433 break; 434 case DVACT_RESUME: 435 if (sc->sc_state == PMS_STATE_SUSPENDED) 436 pms_change_state(sc, PMS_STATE_ENABLED); 437 break; 438 } 439 return (0); 440 } 441 442 int 443 pms_change_state(struct pms_softc *sc, int newstate) 444 { 445 int i; 446 447 switch (newstate) { 448 case PMS_STATE_ENABLED: 449 if (sc->sc_state == PMS_STATE_ENABLED) 450 return (EBUSY); 451 452 sc->inputstate = 0; 453 454 pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 1); 455 456 if (sc->poll) 457 pckbc_flush(sc->sc_kbctag, PCKBC_AUX_SLOT); 458 459 pms_reset(sc); 460 461 sc->protocol = &pms_mouse[0]; 462 for (i = 1; i < nitems(pms_mouse); i++) 463 if (pms_mouse[i].enable(sc)) 464 sc->protocol = &pms_mouse[i]; 465 466 #ifdef DEBUG 467 printf("%s: protocol type %d\n", DEVNAME(sc), sc->protocol->type); 468 #endif 469 470 pms_dev_enable(sc); 471 break; 472 case PMS_STATE_DISABLED: 473 case PMS_STATE_SUSPENDED: 474 pms_dev_disable(sc); 475 476 if (sc->protocol && sc->protocol->disable) 477 sc->protocol->disable(sc); 478 479 pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 0); 480 break; 481 } 482 483 sc->sc_state = newstate; 484 sc->poll = (newstate == PMS_STATE_SUSPENDED) ? 1 : 0; 485 486 return (0); 487 } 488 489 int 490 pms_enable(void *v) 491 { 492 struct pms_softc *sc = v; 493 494 return pms_change_state(sc, PMS_STATE_ENABLED); 495 } 496 497 void 498 pms_disable(void *v) 499 { 500 struct pms_softc *sc = v; 501 502 pms_change_state(sc, PMS_STATE_DISABLED); 503 } 504 505 int 506 pms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 507 { 508 struct pms_softc *sc = v; 509 510 if (sc->protocol && sc->protocol->ioctl) 511 return (sc->protocol->ioctl(sc, cmd, data, flag, p)); 512 else 513 return (-1); 514 } 515 516 void 517 pmsinput(void *vsc, int data) 518 { 519 struct pms_softc *sc = vsc; 520 521 if (sc->sc_state != PMS_STATE_ENABLED) { 522 /* Interrupts are not expected. Discard the byte. */ 523 return; 524 } 525 526 if (sc->protocol->sync(sc, data)) { 527 #ifdef DEBUG 528 printf("%s: not in sync yet, discard input\n", DEVNAME(sc)); 529 #endif 530 sc->inputstate = 0; 531 return; 532 } 533 534 sc->packet[sc->inputstate++] = data; 535 if (sc->inputstate != sc->protocol->packetsize) 536 return; 537 538 sc->protocol->proc(sc); 539 sc->inputstate = 0; 540 } 541