1 /* $NetBSD: opms.c,v 1.10 2003/08/07 16:26:48 agc Exp $ */ 2 /* $OpenBSD: pccons.c,v 1.22 1999/01/30 22:39:37 imp Exp $ */ 3 /* NetBSD: pms.c,v 1.21 1995/04/18 02:25:18 mycroft Exp */ 4 5 /*- 6 * Copyright (c) 1990 The Regents of the University of California. 7 * All rights reserved. 8 * 9 * This code is derived from software contributed to Berkeley by 10 * William Jolitz and Don Ahn. 11 * 12 * Copyright (c) 1994 Charles M. Hannum. 13 * Copyright (c) 1992, 1993 Erik Forsberg. 14 * 15 * Redistribution and use in source and binary forms, with or without 16 * modification, are permitted provided that the following conditions 17 * are met: 18 * 1. Redistributions of source code must retain the above copyright 19 * notice, this list of conditions and the following disclaimer. 20 * 2. Redistributions in binary form must reproduce the above copyright 21 * notice, this list of conditions and the following disclaimer in the 22 * documentation and/or other materials provided with the distribution. 23 * 3. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 * 39 * @(#)pccons.c 5.11 (Berkeley) 5/21/91 40 */ 41 42 /*- 43 * Copyright (c) 1993, 1994, 1995 Charles M. Hannum. All rights reserved. 44 * 45 * This code is derived from software contributed to Berkeley by 46 * William Jolitz and Don Ahn. 47 * 48 * Copyright (c) 1994 Charles M. Hannum. 49 * Copyright (c) 1992, 1993 Erik Forsberg. 50 * 51 * Redistribution and use in source and binary forms, with or without 52 * modification, are permitted provided that the following conditions 53 * are met: 54 * 1. Redistributions of source code must retain the above copyright 55 * notice, this list of conditions and the following disclaimer. 56 * 2. Redistributions in binary form must reproduce the above copyright 57 * notice, this list of conditions and the following disclaimer in the 58 * documentation and/or other materials provided with the distribution. 59 * 3. All advertising materials mentioning features or use of this software 60 * must display the following acknowledgement: 61 * This product includes software developed by the University of 62 * California, Berkeley and its contributors. 63 * 4. Neither the name of the University nor the names of its contributors 64 * may be used to endorse or promote products derived from this software 65 * without specific prior written permission. 66 * 67 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 68 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 69 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 70 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 71 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 72 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 73 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 74 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 75 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 76 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 77 * SUCH DAMAGE. 78 * 79 * @(#)pccons.c 5.11 (Berkeley) 5/21/91 80 */ 81 82 #include <sys/cdefs.h> 83 __KERNEL_RCSID(0, "$NetBSD: opms.c,v 1.10 2003/08/07 16:26:48 agc Exp $"); 84 85 #include <sys/param.h> 86 #include <sys/systm.h> 87 #include <sys/vnode.h> 88 #include <sys/poll.h> 89 #include <sys/tty.h> 90 #include <sys/device.h> 91 #include <sys/proc.h> 92 #include <sys/conf.h> 93 94 #include <machine/bus.h> 95 #include <machine/kbdreg.h> 96 #include <machine/mouse.h> 97 98 #include <arc/dev/pcconsvar.h> 99 #include <arc/dev/opmsvar.h> 100 101 #define PMSUNIT(dev) (minor(dev)) 102 103 /* status bits */ 104 #define PMS_OBUF_FULL 0x01 105 #define PMS_IBUF_FULL 0x02 106 107 /* controller commands */ 108 #define PMS_INT_ENABLE 0x47 /* enable controller interrupts */ 109 #define PMS_INT_DISABLE 0x65 /* disable controller interrupts */ 110 #define PMS_AUX_ENABLE 0xa7 /* enable auxiliary port */ 111 #define PMS_AUX_DISABLE 0xa8 /* disable auxiliary port */ 112 #define PMS_MAGIC_1 0xa9 /* XXX */ 113 114 #define PMS_8042_CMD 0x65 115 116 /* mouse commands */ 117 #define PMS_SET_SCALE11 0xe6 /* set scaling 1:1 */ 118 #define PMS_SET_SCALE21 0xe7 /* set scaling 2:1 */ 119 #define PMS_SET_RES 0xe8 /* set resolution */ 120 #define PMS_GET_SCALE 0xe9 /* get scaling factor */ 121 #define PMS_SET_STREAM 0xea /* set streaming mode */ 122 #define PMS_SET_SAMPLE 0xf3 /* set sampling rate */ 123 #define PMS_DEV_ENABLE 0xf4 /* mouse on */ 124 #define PMS_DEV_DISABLE 0xf5 /* mouse off */ 125 #define PMS_RESET 0xff /* reset */ 126 127 #define PMS_CHUNK 128 /* chunk size for read */ 128 #define PMS_BSIZE 1020 /* buffer size */ 129 130 #define FLUSHQ(q) { if((q)->c_cc) ndflush(q, (q)->c_cc); } 131 132 extern struct cfdriver opms_cd; 133 134 dev_type_open(opmsopen); 135 dev_type_close(opmsclose); 136 dev_type_read(opmsread); 137 dev_type_ioctl(opmsioctl); 138 dev_type_poll(opmspoll); 139 dev_type_kqfilter(opmskqfilter); 140 141 const struct cdevsw opms_cdevsw = { 142 opmsopen, opmsclose, opmsread, nowrite, opmsioctl, 143 nostop, notty, opmspoll, nommap, opmskqfilter, 144 }; 145 146 static __inline void pms_dev_cmd __P((u_char)); 147 static __inline void pms_aux_cmd __P((u_char)); 148 static __inline void pms_pit_cmd __P((u_char)); 149 150 static __inline void 151 pms_dev_cmd(value) 152 u_char value; 153 { 154 kbd_flush_input(); 155 kbd_cmd_write_1(0xd4); 156 kbd_flush_input(); 157 kbd_data_write_1(value); 158 } 159 160 static __inline void 161 pms_aux_cmd(value) 162 u_char value; 163 { 164 kbd_flush_input(); 165 kbd_cmd_write_1(value); 166 } 167 168 static __inline void 169 pms_pit_cmd(value) 170 u_char value; 171 { 172 kbd_flush_input(); 173 kbd_cmd_write_1(0x60); 174 kbd_flush_input(); 175 kbd_data_write_1(value); 176 } 177 178 int opms_common_match(kbd_iot, config) 179 bus_space_tag_t kbd_iot; 180 struct pccons_config *config; 181 { 182 u_char x; 183 184 kbd_context_init(kbd_iot, config); 185 186 pms_dev_cmd(KBC_RESET); 187 pms_aux_cmd(PMS_MAGIC_1); 188 delay(10000); 189 x = kbd_data_read_1(); 190 pms_pit_cmd(PMS_INT_DISABLE); 191 if (x & 0x04) 192 return 0; 193 194 return 1; 195 } 196 197 void 198 opms_common_attach(sc, opms_iot, config) 199 struct opms_softc *sc; 200 bus_space_tag_t opms_iot; 201 struct pccons_config *config; 202 { 203 kbd_context_init(opms_iot, config); 204 205 /* Other initialization was done by opmsprobe. */ 206 sc->sc_state = 0; 207 } 208 209 int 210 opmsopen(dev, flag, mode, p) 211 dev_t dev; 212 int flag, mode; 213 struct proc *p; 214 { 215 int unit = PMSUNIT(dev); 216 struct opms_softc *sc; 217 218 if (unit >= opms_cd.cd_ndevs) 219 return ENXIO; 220 sc = opms_cd.cd_devs[unit]; 221 if (!sc) 222 return ENXIO; 223 224 if (sc->sc_state & PMS_OPEN) 225 return EBUSY; 226 227 if (clalloc(&sc->sc_q, PMS_BSIZE, 0) == -1) 228 return ENOMEM; 229 230 sc->sc_state |= PMS_OPEN; 231 sc->sc_status = 0; 232 sc->sc_x = sc->sc_y = 0; 233 234 /* Enable interrupts. */ 235 pms_dev_cmd(PMS_DEV_ENABLE); 236 pms_aux_cmd(PMS_AUX_ENABLE); 237 pms_dev_cmd(PMS_SET_RES); 238 pms_dev_cmd(3); /* 8 counts/mm */ 239 pms_dev_cmd(PMS_SET_SCALE21); 240 #if 0 241 pms_dev_cmd(PMS_SET_SAMPLE); 242 pms_dev_cmd(100); /* 100 samples/sec */ 243 pms_dev_cmd(PMS_SET_STREAM); 244 #endif 245 pms_pit_cmd(PMS_INT_ENABLE); 246 247 return 0; 248 } 249 250 int 251 opmsclose(dev, flag, mode, p) 252 dev_t dev; 253 int flag, mode; 254 struct proc *p; 255 { 256 struct opms_softc *sc = opms_cd.cd_devs[PMSUNIT(dev)]; 257 258 /* Disable interrupts. */ 259 pms_dev_cmd(PMS_DEV_DISABLE); 260 pms_pit_cmd(PMS_INT_DISABLE); 261 pms_aux_cmd(PMS_AUX_DISABLE); 262 263 sc->sc_state &= ~PMS_OPEN; 264 265 clfree(&sc->sc_q); 266 267 return 0; 268 } 269 270 int 271 opmsread(dev, uio, flag) 272 dev_t dev; 273 struct uio *uio; 274 int flag; 275 { 276 struct opms_softc *sc = opms_cd.cd_devs[PMSUNIT(dev)]; 277 int s; 278 int error = 0; 279 size_t length; 280 u_char buffer[PMS_CHUNK]; 281 282 /* Block until mouse activity occurred. */ 283 284 s = spltty(); 285 while (sc->sc_q.c_cc == 0) { 286 if (flag & IO_NDELAY) { 287 splx(s); 288 return EWOULDBLOCK; 289 } 290 sc->sc_state |= PMS_ASLP; 291 error = tsleep((caddr_t)sc, PZERO | PCATCH, "pmsrea", 0); 292 if (error) { 293 sc->sc_state &= ~PMS_ASLP; 294 splx(s); 295 return error; 296 } 297 } 298 splx(s); 299 300 /* Transfer as many chunks as possible. */ 301 302 while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) { 303 length = min(sc->sc_q.c_cc, uio->uio_resid); 304 if (length > sizeof(buffer)) 305 length = sizeof(buffer); 306 307 /* Remove a small chunk from the input queue. */ 308 (void) q_to_b(&sc->sc_q, buffer, length); 309 310 /* Copy the data to the user process. */ 311 error = uiomove(buffer, length, uio); 312 if (error) 313 break; 314 } 315 316 return error; 317 } 318 319 int 320 opmsioctl(dev, cmd, addr, flag, p) 321 dev_t dev; 322 u_long cmd; 323 caddr_t addr; 324 int flag; 325 struct proc *p; 326 { 327 struct opms_softc *sc = opms_cd.cd_devs[PMSUNIT(dev)]; 328 struct mouseinfo info; 329 int s; 330 int error; 331 332 switch (cmd) { 333 case MOUSEIOCREAD: 334 s = spltty(); 335 336 info.status = sc->sc_status; 337 if (sc->sc_x || sc->sc_y) 338 info.status |= MOVEMENT; 339 340 if (sc->sc_x > 127) 341 info.xmotion = 127; 342 else if (sc->sc_x < -127) 343 /* Bounding at -127 avoids a bug in XFree86. */ 344 info.xmotion = -127; 345 else 346 info.xmotion = sc->sc_x; 347 348 if (sc->sc_y > 127) 349 info.ymotion = 127; 350 else if (sc->sc_y < -127) 351 info.ymotion = -127; 352 else 353 info.ymotion = sc->sc_y; 354 355 /* Reset historical information. */ 356 sc->sc_x = sc->sc_y = 0; 357 sc->sc_status &= ~BUTCHNGMASK; 358 ndflush(&sc->sc_q, sc->sc_q.c_cc); 359 360 splx(s); 361 error = copyout(&info, addr, sizeof(struct mouseinfo)); 362 break; 363 default: 364 error = EINVAL; 365 break; 366 } 367 368 return error; 369 } 370 371 /* Masks for the first byte of a packet */ 372 #define PS2LBUTMASK 0x01 373 #define PS2RBUTMASK 0x02 374 #define PS2MBUTMASK 0x04 375 376 int 377 opmsintr(arg) 378 void *arg; 379 { 380 struct opms_softc *sc = arg; 381 static int state = 0; 382 static u_char buttons; 383 u_char changed; 384 static char dx, dy; 385 u_char buffer[5]; 386 387 if ((sc->sc_state & PMS_OPEN) == 0) { 388 /* Interrupts are not expected. Discard the byte. */ 389 kbd_flush_input(); 390 return 0; 391 } 392 393 switch (state) { 394 395 case 0: 396 buttons = kbd_data_read_1(); 397 if ((buttons & 0xc0) == 0) 398 ++state; 399 break; 400 401 case 1: 402 dx = kbd_data_read_1(); 403 /* Bounding at -127 avoids a bug in XFree86. */ 404 dx = (dx == -128) ? -127 : dx; 405 ++state; 406 break; 407 408 case 2: 409 dy = kbd_data_read_1(); 410 dy = (dy == -128) ? -127 : dy; 411 state = 0; 412 413 buttons = ((buttons & PS2LBUTMASK) << 2) | 414 ((buttons & (PS2RBUTMASK | PS2MBUTMASK)) >> 1); 415 changed = ((buttons ^ sc->sc_status) & BUTSTATMASK) << 3; 416 sc->sc_status = buttons | (sc->sc_status & ~BUTSTATMASK) | changed; 417 418 if (dx || dy || changed) { 419 /* Update accumulated movements. */ 420 sc->sc_x += dx; 421 sc->sc_y += dy; 422 423 /* Add this event to the queue. */ 424 buffer[0] = 0x80 | (buttons & BUTSTATMASK); 425 if(dx < 0) 426 buffer[0] |= 0x10; 427 buffer[1] = dx & 0x7f; 428 if(dy < 0) 429 buffer[0] |= 0x20; 430 buffer[2] = dy & 0x7f; 431 buffer[3] = buffer[4] = 0; 432 (void) b_to_q(buffer, sizeof buffer, &sc->sc_q); 433 434 if (sc->sc_state & PMS_ASLP) { 435 sc->sc_state &= ~PMS_ASLP; 436 wakeup((caddr_t)sc); 437 } 438 selnotify(&sc->sc_rsel, 0); 439 } 440 441 break; 442 } 443 return -1; 444 } 445 446 int 447 opmspoll(dev, events, p) 448 dev_t dev; 449 int events; 450 struct proc *p; 451 { 452 struct opms_softc *sc = opms_cd.cd_devs[PMSUNIT(dev)]; 453 int revents = 0; 454 int s = spltty(); 455 456 if (events & (POLLIN | POLLRDNORM)) { 457 if (sc->sc_q.c_cc > 0) 458 revents |= events & (POLLIN | POLLRDNORM); 459 else 460 selrecord(p, &sc->sc_rsel); 461 } 462 463 splx(s); 464 return (revents); 465 } 466 467 static void 468 filt_opmsrdetach(struct knote *kn) 469 { 470 struct opms_softc *sc = kn->kn_hook; 471 int s; 472 473 s = spltty(); 474 SLIST_REMOVE(&sc->sc_rsel.sel_klist, kn, knote, kn_selnext); 475 splx(s); 476 } 477 478 static int 479 filt_opmsread(struct knote *kn, long hint) 480 { 481 struct opms_softc *sc = kn->kn_hook; 482 483 kn->kn_data = sc->sc_q.c_cc; 484 return (kn->kn_data > 0); 485 } 486 487 static const struct filterops opmsread_filtops = 488 { 1, NULL, filt_opmsrdetach, filt_opmsread }; 489 490 int 491 opmskqfilter(dev_t dev, struct knote *kn) 492 { 493 struct opms_softc *sc = opms_cd.cd_devs[PMSUNIT(dev)]; 494 struct klist *klist; 495 int s; 496 497 switch (kn->kn_filter) { 498 case EVFILT_READ: 499 klist = &sc->sc_rsel.sel_klist; 500 kn->kn_fop = &opmsread_filtops; 501 break; 502 503 default: 504 return (1); 505 } 506 507 kn->kn_hook = sc; 508 509 s = spltty(); 510 SLIST_INSERT_HEAD(klist, kn, kn_selnext); 511 splx(s); 512 513 return (0); 514 } 515