1 /* $NetBSD: opms.c,v 1.20 2014/03/16 05:20:22 dholland 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.20 2014/03/16 05:20:22 dholland 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 <sys/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 #include "ioconf.h" 102 103 #define PMSUNIT(dev) (minor(dev)) 104 105 /* status bits */ 106 #define PMS_OBUF_FULL 0x01 107 #define PMS_IBUF_FULL 0x02 108 109 /* controller commands */ 110 #define PMS_INT_ENABLE 0x47 /* enable controller interrupts */ 111 #define PMS_INT_DISABLE 0x65 /* disable controller interrupts */ 112 #define PMS_AUX_ENABLE 0xa7 /* enable auxiliary port */ 113 #define PMS_AUX_DISABLE 0xa8 /* disable auxiliary port */ 114 #define PMS_MAGIC_1 0xa9 /* XXX */ 115 116 #define PMS_8042_CMD 0x65 117 118 /* mouse commands */ 119 #define PMS_SET_SCALE11 0xe6 /* set scaling 1:1 */ 120 #define PMS_SET_SCALE21 0xe7 /* set scaling 2:1 */ 121 #define PMS_SET_RES 0xe8 /* set resolution */ 122 #define PMS_GET_SCALE 0xe9 /* get scaling factor */ 123 #define PMS_SET_STREAM 0xea /* set streaming mode */ 124 #define PMS_SET_SAMPLE 0xf3 /* set sampling rate */ 125 #define PMS_DEV_ENABLE 0xf4 /* mouse on */ 126 #define PMS_DEV_DISABLE 0xf5 /* mouse off */ 127 #define PMS_RESET 0xff /* reset */ 128 129 #define PMS_CHUNK 128 /* chunk size for read */ 130 #define PMS_BSIZE 1020 /* buffer size */ 131 132 #define FLUSHQ(q) { if((q)->c_cc) ndflush(q, (q)->c_cc); } 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 .d_open = opmsopen, 143 .d_close = opmsclose, 144 .d_read = opmsread, 145 .d_write = nowrite, 146 .d_ioctl = opmsioctl, 147 .d_stop = nostop, 148 .d_tty = notty, 149 .d_poll = opmspoll, 150 .d_mmap = nommap, 151 .d_kqfilter = opmskqfilter, 152 .d_flag = 0 153 }; 154 155 static inline void pms_dev_cmd(uint8_t); 156 static inline void pms_aux_cmd(uint8_t); 157 static inline void pms_pit_cmd(uint8_t); 158 159 static inline void 160 pms_dev_cmd(uint8_t value) 161 { 162 163 kbd_flush_input(); 164 kbd_cmd_write_1(0xd4); 165 kbd_flush_input(); 166 kbd_data_write_1(value); 167 } 168 169 static inline void 170 pms_aux_cmd(uint8_t value) 171 { 172 173 kbd_flush_input(); 174 kbd_cmd_write_1(value); 175 } 176 177 static inline void 178 pms_pit_cmd(uint8_t value) 179 { 180 181 kbd_flush_input(); 182 kbd_cmd_write_1(0x60); 183 kbd_flush_input(); 184 kbd_data_write_1(value); 185 } 186 187 int opms_common_match(bus_space_tag_t kbd_iot, struct pccons_config *config) 188 { 189 uint8_t x; 190 191 kbd_context_init(kbd_iot, config); 192 193 pms_dev_cmd(KBC_RESET); 194 pms_aux_cmd(PMS_MAGIC_1); 195 delay(10000); 196 x = kbd_data_read_1(); 197 pms_pit_cmd(PMS_INT_DISABLE); 198 if (x & 0x04) 199 return 0; 200 201 return 1; 202 } 203 204 void 205 opms_common_attach(struct opms_softc *sc, bus_space_tag_t opms_iot, 206 struct pccons_config *config) 207 { 208 209 kbd_context_init(opms_iot, config); 210 selinit(&sc->sc_rsel); 211 212 /* Other initialization was done by opmsprobe. */ 213 sc->sc_state = 0; 214 } 215 216 int 217 opmsopen(dev_t dev, int flag, int mode, struct lwp *l) 218 { 219 struct opms_softc *sc; 220 221 sc = device_lookup_private(&opms_cd, PMSUNIT(dev)); 222 if (!sc) 223 return ENXIO; 224 225 if (sc->sc_state & PMS_OPEN) 226 return EBUSY; 227 228 if (clalloc(&sc->sc_q, PMS_BSIZE, 0) == -1) 229 return ENOMEM; 230 231 sc->sc_state |= PMS_OPEN; 232 sc->sc_status = 0; 233 sc->sc_x = sc->sc_y = 0; 234 235 /* Enable interrupts. */ 236 pms_dev_cmd(PMS_DEV_ENABLE); 237 pms_aux_cmd(PMS_AUX_ENABLE); 238 pms_dev_cmd(PMS_SET_RES); 239 pms_dev_cmd(3); /* 8 counts/mm */ 240 pms_dev_cmd(PMS_SET_SCALE21); 241 #if 0 242 pms_dev_cmd(PMS_SET_SAMPLE); 243 pms_dev_cmd(100); /* 100 samples/sec */ 244 pms_dev_cmd(PMS_SET_STREAM); 245 #endif 246 pms_pit_cmd(PMS_INT_ENABLE); 247 248 return 0; 249 } 250 251 int 252 opmsclose(dev_t dev, int flag, int mode, struct lwp *l) 253 { 254 struct opms_softc *sc = device_lookup_private(&opms_cd, PMSUNIT(dev)); 255 256 /* Disable interrupts. */ 257 pms_dev_cmd(PMS_DEV_DISABLE); 258 pms_pit_cmd(PMS_INT_DISABLE); 259 pms_aux_cmd(PMS_AUX_DISABLE); 260 261 sc->sc_state &= ~PMS_OPEN; 262 263 clfree(&sc->sc_q); 264 265 return 0; 266 } 267 268 int 269 opmsread(dev_t dev, struct uio *uio, int flag) 270 { 271 struct opms_softc *sc = device_lookup_private(&opms_cd, PMSUNIT(dev)); 272 int s; 273 int error = 0; 274 size_t length; 275 u_char buffer[PMS_CHUNK]; 276 277 /* Block until mouse activity occurred. */ 278 279 s = spltty(); 280 while (sc->sc_q.c_cc == 0) { 281 if (flag & IO_NDELAY) { 282 splx(s); 283 return EWOULDBLOCK; 284 } 285 sc->sc_state |= PMS_ASLP; 286 error = tsleep((void *)sc, PZERO | PCATCH, "pmsrea", 0); 287 if (error) { 288 sc->sc_state &= ~PMS_ASLP; 289 splx(s); 290 return error; 291 } 292 } 293 splx(s); 294 295 /* Transfer as many chunks as possible. */ 296 297 while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) { 298 length = min(sc->sc_q.c_cc, uio->uio_resid); 299 if (length > sizeof(buffer)) 300 length = sizeof(buffer); 301 302 /* Remove a small chunk from the input queue. */ 303 (void) q_to_b(&sc->sc_q, buffer, length); 304 305 /* Copy the data to the user process. */ 306 error = uiomove(buffer, length, uio); 307 if (error) 308 break; 309 } 310 311 return error; 312 } 313 314 int 315 opmsioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l) 316 { 317 struct opms_softc *sc = device_lookup_private(&opms_cd, PMSUNIT(dev)); 318 struct mouseinfo info; 319 int s; 320 int error; 321 322 switch (cmd) { 323 case MOUSEIOCREAD: 324 s = spltty(); 325 326 info.status = sc->sc_status; 327 if (sc->sc_x || sc->sc_y) 328 info.status |= MOVEMENT; 329 330 if (sc->sc_x > 127) 331 info.xmotion = 127; 332 else if (sc->sc_x < -127) 333 /* Bounding at -127 avoids a bug in XFree86. */ 334 info.xmotion = -127; 335 else 336 info.xmotion = sc->sc_x; 337 338 if (sc->sc_y > 127) 339 info.ymotion = 127; 340 else if (sc->sc_y < -127) 341 info.ymotion = -127; 342 else 343 info.ymotion = sc->sc_y; 344 345 /* Reset historical information. */ 346 sc->sc_x = sc->sc_y = 0; 347 sc->sc_status &= ~BUTCHNGMASK; 348 ndflush(&sc->sc_q, sc->sc_q.c_cc); 349 350 splx(s); 351 error = copyout(&info, addr, sizeof(struct mouseinfo)); 352 break; 353 default: 354 error = EINVAL; 355 break; 356 } 357 358 return error; 359 } 360 361 /* Masks for the first byte of a packet */ 362 #define PS2LBUTMASK 0x01 363 #define PS2RBUTMASK 0x02 364 #define PS2MBUTMASK 0x04 365 366 int 367 opmsintr(void *arg) 368 { 369 struct opms_softc *sc = arg; 370 static int state = 0; 371 static u_char buttons; 372 u_char changed; 373 static char dx, dy; 374 u_char buffer[5]; 375 376 if ((sc->sc_state & PMS_OPEN) == 0) { 377 /* Interrupts are not expected. Discard the byte. */ 378 kbd_flush_input(); 379 return 0; 380 } 381 382 switch (state) { 383 384 case 0: 385 buttons = kbd_data_read_1(); 386 if ((buttons & 0xc0) == 0) 387 ++state; 388 break; 389 390 case 1: 391 dx = kbd_data_read_1(); 392 /* Bounding at -127 avoids a bug in XFree86. */ 393 dx = (dx == -128) ? -127 : dx; 394 ++state; 395 break; 396 397 case 2: 398 dy = kbd_data_read_1(); 399 dy = (dy == -128) ? -127 : dy; 400 state = 0; 401 402 buttons = ((buttons & PS2LBUTMASK) << 2) | 403 ((buttons & (PS2RBUTMASK | PS2MBUTMASK)) >> 1); 404 changed = ((buttons ^ sc->sc_status) & BUTSTATMASK) << 3; 405 sc->sc_status = buttons | (sc->sc_status & ~BUTSTATMASK) | 406 changed; 407 408 if (dx || dy || changed) { 409 /* Update accumulated movements. */ 410 sc->sc_x += dx; 411 sc->sc_y += dy; 412 413 /* Add this event to the queue. */ 414 buffer[0] = 0x80 | (buttons & BUTSTATMASK); 415 if(dx < 0) 416 buffer[0] |= 0x10; 417 buffer[1] = dx & 0x7f; 418 if(dy < 0) 419 buffer[0] |= 0x20; 420 buffer[2] = dy & 0x7f; 421 buffer[3] = buffer[4] = 0; 422 (void) b_to_q(buffer, sizeof buffer, &sc->sc_q); 423 424 if (sc->sc_state & PMS_ASLP) { 425 sc->sc_state &= ~PMS_ASLP; 426 wakeup((void *)sc); 427 } 428 selnotify(&sc->sc_rsel, 0, 0); 429 } 430 431 break; 432 } 433 return -1; 434 } 435 436 int 437 opmspoll(dev_t dev, int events, struct lwp *l) 438 { 439 struct opms_softc *sc = device_lookup_private(&opms_cd, PMSUNIT(dev)); 440 int revents = 0; 441 int s = spltty(); 442 443 if (events & (POLLIN | POLLRDNORM)) { 444 if (sc->sc_q.c_cc > 0) 445 revents |= events & (POLLIN | POLLRDNORM); 446 else 447 selrecord(l, &sc->sc_rsel); 448 } 449 450 splx(s); 451 return revents; 452 } 453 454 static void 455 filt_opmsrdetach(struct knote *kn) 456 { 457 struct opms_softc *sc = kn->kn_hook; 458 int s; 459 460 s = spltty(); 461 SLIST_REMOVE(&sc->sc_rsel.sel_klist, kn, knote, kn_selnext); 462 splx(s); 463 } 464 465 static int 466 filt_opmsread(struct knote *kn, long hint) 467 { 468 struct opms_softc *sc = kn->kn_hook; 469 470 kn->kn_data = sc->sc_q.c_cc; 471 return kn->kn_data > 0; 472 } 473 474 static const struct filterops opmsread_filtops = 475 { 1, NULL, filt_opmsrdetach, filt_opmsread }; 476 477 int 478 opmskqfilter(dev_t dev, struct knote *kn) 479 { 480 struct opms_softc *sc = device_lookup_private(&opms_cd, PMSUNIT(dev)); 481 struct klist *klist; 482 int s; 483 484 switch (kn->kn_filter) { 485 case EVFILT_READ: 486 klist = &sc->sc_rsel.sel_klist; 487 kn->kn_fop = &opmsread_filtops; 488 break; 489 490 default: 491 return 1; 492 } 493 494 kn->kn_hook = sc; 495 496 s = spltty(); 497 SLIST_INSERT_HEAD(klist, kn, kn_selnext); 498 splx(s); 499 500 return 0; 501 } 502