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