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