pms.c revision 1.7
1/* $OpenBSD: pms.c,v 1.7 2010/10/02 00:28:57 krw 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 41struct pms_softc { /* driver status information */ 42 struct device sc_dev; 43 44 pckbc_tag_t sc_kbctag; 45 int sc_kbcslot; 46 47 int sc_state; 48#define PMS_STATE_DISABLED 0 49#define PMS_STATE_ENABLED 1 50#define PMS_STATE_SUSPENDED 2 51 52 int poll; 53 int intelli; 54 int inputstate; 55 u_int buttons, oldbuttons; /* mouse button status */ 56 signed char dx, dy; 57 58 struct device *sc_wsmousedev; 59}; 60 61int pmsprobe(struct device *, void *, void *); 62void pmsattach(struct device *, struct device *, void *); 63int pmsactivate(struct device *, int); 64void pmsinput(void *, int); 65 66struct cfattach pms_ca = { 67 sizeof(struct pms_softc), pmsprobe, pmsattach, NULL, 68 pmsactivate 69}; 70 71int pms_change_state(struct pms_softc *, int); 72int pms_ioctl(void *, u_long, caddr_t, int, struct proc *); 73int pms_enable(void *); 74void pms_disable(void *); 75 76int pms_cmd(struct pms_softc *, u_char *, int, u_char *, int); 77 78int pms_setintellimode(struct pms_softc *sc); 79 80const struct wsmouse_accessops pms_accessops = { 81 pms_enable, 82 pms_ioctl, 83 pms_disable, 84}; 85 86int 87pms_cmd(struct pms_softc *sc, u_char *cmd, int len, u_char *resp, int resplen) 88{ 89 if (sc->poll) { 90 return pckbc_poll_cmd(sc->sc_kbctag, sc->sc_kbcslot, 91 cmd, len, resplen, resp, 1); 92 } else { 93 return pckbc_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, 94 cmd, len, resplen, 1, resp); 95 } 96} 97 98int 99pms_setintellimode(struct pms_softc *sc) 100{ 101 u_char cmd[2], resp[1]; 102 int i, res; 103 static const u_char rates[] = {200, 100, 80}; 104 105 cmd[0] = PMS_SET_SAMPLE; 106 for (i = 0; i < 3; i++) { 107 cmd[1] = rates[i]; 108 res = pms_cmd(sc, cmd, 2, NULL, 0); 109 if (res) 110 return (0); 111 } 112 113 cmd[0] = PMS_SEND_DEV_ID; 114 res = pms_cmd(sc, cmd, 1, resp, 1); 115 if (res || resp[0] != 3) 116 return (0); 117 118 return (1); 119} 120 121int 122pmsprobe(parent, match, aux) 123 struct device *parent; 124 void *match; 125 void *aux; 126{ 127 struct pckbc_attach_args *pa = aux; 128 u_char cmd[1], resp[2]; 129 int res; 130 131 if (pa->pa_slot != PCKBC_AUX_SLOT) 132 return (0); 133 134 /* Flush any garbage. */ 135 pckbc_flush(pa->pa_tag, pa->pa_slot); 136 137 /* reset the device */ 138 cmd[0] = PMS_RESET; 139 res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1); 140 if (res) { 141#ifdef DEBUG 142 printf("pmsprobe: reset error %d\n", res); 143#endif 144 return (0); 145 } 146 if (resp[0] != PMS_RSTDONE) { 147 printf("pmsprobe: reset response 0x%x\n", resp[0]); 148 return (0); 149 } 150 151 /* get type number (0 = mouse) */ 152 if (resp[1] != 0) { 153#ifdef DEBUG 154 printf("pmsprobe: type 0x%x\n", resp[1]); 155#endif 156 return (0); 157 } 158 159 return (1); 160} 161 162void 163pmsattach(parent, self, aux) 164 struct device *parent, *self; 165 void *aux; 166{ 167 struct pms_softc *sc = (void *)self; 168 struct pckbc_attach_args *pa = aux; 169 struct wsmousedev_attach_args a; 170 u_char cmd[1], resp[2]; 171 int res; 172 173 sc->sc_kbctag = pa->pa_tag; 174 sc->sc_kbcslot = pa->pa_slot; 175 176 printf("\n"); 177 178 /* Flush any garbage. */ 179 pckbc_flush(pa->pa_tag, pa->pa_slot); 180 181 /* reset the device */ 182 cmd[0] = PMS_RESET; 183 res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1); 184#ifdef DEBUG 185 if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) { 186 printf("pmsattach: reset error\n"); 187 return; 188 } 189#endif 190 191 sc->inputstate = 0; 192 sc->oldbuttons = 0; 193 194 pckbc_set_inputhandler(sc->sc_kbctag, sc->sc_kbcslot, 195 pmsinput, sc, sc->sc_dev.dv_xname); 196 197 a.accessops = &pms_accessops; 198 a.accesscookie = sc; 199 200 /* 201 * Attach the wsmouse, saving a handle to it. 202 * Note that we don't need to check this pointer against NULL 203 * here or in pmsintr, because if this fails pms_enable() will 204 * never be called, so pmsinput() will never be called. 205 */ 206 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); 207 208 /* no interrupts until enabled */ 209 sc->poll = 1; 210 pms_change_state(sc, PMS_STATE_DISABLED); 211} 212 213int 214pmsactivate(struct device *self, int act) 215{ 216 struct pms_softc *sc = (struct pms_softc *)self; 217 218 switch (act) { 219 case DVACT_SUSPEND: 220 if (sc->sc_state == PMS_STATE_ENABLED) 221 pms_change_state(sc, PMS_STATE_SUSPENDED); 222 break; 223 case DVACT_RESUME: 224 if (sc->sc_state == PMS_STATE_SUSPENDED) 225 pms_change_state(sc, PMS_STATE_ENABLED); 226 break; 227 } 228 return (0); 229} 230 231int 232pms_change_state(struct pms_softc *sc, int newstate) 233{ 234 u_char cmd[1], resp[2]; 235 int res; 236 237 switch (newstate) { 238 case PMS_STATE_ENABLED: 239 if (sc->sc_state == PMS_STATE_ENABLED) 240 return EBUSY; 241 sc->inputstate = 0; 242 sc->oldbuttons = 0; 243 244 pckbc_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 1); 245 246 pckbc_flush(sc->sc_kbctag, sc->sc_kbcslot); 247 248 cmd[0] = PMS_RESET; 249 res = pms_cmd(sc, cmd, 1, resp, 2); 250 251 sc->intelli = pms_setintellimode(sc); 252 253 cmd[0] = PMS_DEV_ENABLE; 254 res = pms_cmd(sc, cmd, 1, NULL, 0); 255 if (res) 256 printf("pms_enable: command error\n"); 257#if 0 258 { 259 u_char scmd[2]; 260 261 scmd[0] = PMS_SET_RES; 262 scmd[1] = 3; /* 8 counts/mm */ 263 res = pckbc_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd, 264 2, 0, 1, 0); 265 if (res) 266 printf("pms_enable: setup error1 (%d)\n", res); 267 268 scmd[0] = PMS_SET_SCALE21; 269 res = pckbc_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd, 270 1, 0, 1, 0); 271 if (res) 272 printf("pms_enable: setup error2 (%d)\n", res); 273 274 scmd[0] = PMS_SET_SAMPLE; 275 scmd[1] = 100; /* 100 samples/sec */ 276 res = pckbc_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd, 277 2, 0, 1, 0); 278 if (res) 279 printf("pms_enable: setup error3 (%d)\n", res); 280 } 281#endif 282 sc->sc_state = newstate; 283 sc->poll = 0; 284 break; 285 case PMS_STATE_DISABLED: 286 287 /* FALLTHROUGH */ 288 case PMS_STATE_SUSPENDED: 289 cmd[0] = PMS_DEV_DISABLE; 290 res = pms_cmd(sc, cmd, 1, NULL, 0); 291 if (res) 292 printf("pms_disable: command error\n"); 293 pckbc_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0); 294 sc->sc_state = newstate; 295 sc->poll = (newstate == PMS_STATE_SUSPENDED) ? 1 : 0; 296 break; 297 } 298 return 0; 299} 300 301int 302pms_enable(v) 303 void *v; 304{ 305 struct pms_softc *sc = v; 306 307 return pms_change_state(sc, PMS_STATE_ENABLED); 308} 309 310void 311pms_disable(v) 312 void *v; 313{ 314 struct pms_softc *sc = v; 315 316 pms_change_state(sc, PMS_STATE_DISABLED); 317} 318 319int 320pms_ioctl(v, cmd, data, flag, p) 321 void *v; 322 u_long cmd; 323 caddr_t data; 324 int flag; 325 struct proc *p; 326{ 327 struct pms_softc *sc = v; 328 u_char kbcmd[2]; 329 int i; 330 331 switch (cmd) { 332 case WSMOUSEIO_GTYPE: 333 *(u_int *)data = WSMOUSE_TYPE_PS2; 334 break; 335 336 case WSMOUSEIO_SRES: 337 i = ((int) *(u_int *)data - 12) / 25; 338 /* valid values are {0,1,2,3} */ 339 if (i < 0) 340 i = 0; 341 if (i > 3) 342 i = 3; 343 344 kbcmd[0] = PMS_SET_RES; 345 kbcmd[1] = (unsigned char) i; 346 i = pckbc_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, kbcmd, 347 2, 0, 1, 0); 348 349 if (i) 350 printf("pms_ioctl: SET_RES command error\n"); 351 break; 352 353 default: 354 return (-1); 355 } 356 return (0); 357} 358 359/* Masks for the first byte of a packet */ 360#define PS2LBUTMASK 0x01 361#define PS2RBUTMASK 0x02 362#define PS2MBUTMASK 0x04 363 364void pmsinput(vsc, data) 365void *vsc; 366int data; 367{ 368 struct pms_softc *sc = vsc; 369 signed char dz = 0; 370 u_int changed; 371 372 if (sc->sc_state != PMS_STATE_ENABLED) { 373 /* Interrupts are not expected. Discard the byte. */ 374 return; 375 } 376 377 switch (sc->inputstate) { 378 379 case 0: 380 if ((data & 0xc0) == 0) { /* no ovfl, bit 3 == 1 too? */ 381 sc->buttons = ((data & PS2LBUTMASK) ? 0x1 : 0) | 382 ((data & PS2MBUTMASK) ? 0x2 : 0) | 383 ((data & PS2RBUTMASK) ? 0x4 : 0); 384 ++sc->inputstate; 385 } 386 break; 387 388 case 1: 389 sc->dx = data; 390 /* Bounding at -127 avoids a bug in XFree86. */ 391 sc->dx = (sc->dx == -128) ? -127 : sc->dx; 392 ++sc->inputstate; 393 break; 394 395 case 2: 396 sc->dy = data; 397 sc->dy = (sc->dy == -128) ? -127 : sc->dy; 398 ++sc->inputstate; 399 break; 400 401 case 3: 402 dz = data; 403 dz = (dz == -128) ? -127 : dz; 404 ++sc->inputstate; 405 break; 406 } 407 408 if ((sc->inputstate == 3 && sc->intelli == 0) || sc->inputstate == 4) { 409 sc->inputstate = 0; 410 411 changed = (sc->buttons ^ sc->oldbuttons); 412 sc->oldbuttons = sc->buttons; 413 414 if (sc->dx || sc->dy || dz || changed) 415 wsmouse_input(sc->sc_wsmousedev, 416 sc->buttons, sc->dx, sc->dy, dz, 0, 417 WSMOUSE_INPUT_DELTA); 418 } 419 420 return; 421} 422 423struct cfdriver pms_cd = { 424 NULL, "pms", DV_DULL 425}; 426