1 /* $NetBSD: opms.c,v 1.4 2002/09/06 13:18:43 gehenna 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 102 const struct cdevsw opms_cdevsw = { 103 opmsopen, opmsclose, opmsread, nowrite, opmsioctl, 104 nostop, notty, opmspoll, nommap, 105 }; 106 107 static __inline void pms_dev_cmd __P((u_char)); 108 static __inline void pms_aux_cmd __P((u_char)); 109 static __inline void pms_pit_cmd __P((u_char)); 110 111 static __inline void 112 pms_dev_cmd(value) 113 u_char value; 114 { 115 kbd_flush_input(); 116 kbd_cmd_write_1(0xd4); 117 kbd_flush_input(); 118 kbd_data_write_1(value); 119 } 120 121 static __inline void 122 pms_aux_cmd(value) 123 u_char value; 124 { 125 kbd_flush_input(); 126 kbd_cmd_write_1(value); 127 } 128 129 static __inline void 130 pms_pit_cmd(value) 131 u_char value; 132 { 133 kbd_flush_input(); 134 kbd_cmd_write_1(0x60); 135 kbd_flush_input(); 136 kbd_data_write_1(value); 137 } 138 139 int opms_common_match(kbd_iot, config) 140 bus_space_tag_t kbd_iot; 141 struct pccons_config *config; 142 { 143 u_char x; 144 145 kbd_context_init(kbd_iot, config); 146 147 pms_dev_cmd(KBC_RESET); 148 pms_aux_cmd(PMS_MAGIC_1); 149 delay(10000); 150 x = kbd_data_read_1(); 151 pms_pit_cmd(PMS_INT_DISABLE); 152 if (x & 0x04) 153 return 0; 154 155 return 1; 156 } 157 158 void 159 opms_common_attach(sc, opms_iot, config) 160 struct opms_softc *sc; 161 bus_space_tag_t opms_iot; 162 struct pccons_config *config; 163 { 164 kbd_context_init(opms_iot, config); 165 166 /* Other initialization was done by opmsprobe. */ 167 sc->sc_state = 0; 168 } 169 170 int 171 opmsopen(dev, flag, mode, p) 172 dev_t dev; 173 int flag, mode; 174 struct proc *p; 175 { 176 int unit = PMSUNIT(dev); 177 struct opms_softc *sc; 178 179 if (unit >= opms_cd.cd_ndevs) 180 return ENXIO; 181 sc = opms_cd.cd_devs[unit]; 182 if (!sc) 183 return ENXIO; 184 185 if (sc->sc_state & PMS_OPEN) 186 return EBUSY; 187 188 if (clalloc(&sc->sc_q, PMS_BSIZE, 0) == -1) 189 return ENOMEM; 190 191 sc->sc_state |= PMS_OPEN; 192 sc->sc_status = 0; 193 sc->sc_x = sc->sc_y = 0; 194 195 /* Enable interrupts. */ 196 pms_dev_cmd(PMS_DEV_ENABLE); 197 pms_aux_cmd(PMS_AUX_ENABLE); 198 pms_dev_cmd(PMS_SET_RES); 199 pms_dev_cmd(3); /* 8 counts/mm */ 200 pms_dev_cmd(PMS_SET_SCALE21); 201 #if 0 202 pms_dev_cmd(PMS_SET_SAMPLE); 203 pms_dev_cmd(100); /* 100 samples/sec */ 204 pms_dev_cmd(PMS_SET_STREAM); 205 #endif 206 pms_pit_cmd(PMS_INT_ENABLE); 207 208 return 0; 209 } 210 211 int 212 opmsclose(dev, flag, mode, p) 213 dev_t dev; 214 int flag, mode; 215 struct proc *p; 216 { 217 struct opms_softc *sc = opms_cd.cd_devs[PMSUNIT(dev)]; 218 219 /* Disable interrupts. */ 220 pms_dev_cmd(PMS_DEV_DISABLE); 221 pms_pit_cmd(PMS_INT_DISABLE); 222 pms_aux_cmd(PMS_AUX_DISABLE); 223 224 sc->sc_state &= ~PMS_OPEN; 225 226 clfree(&sc->sc_q); 227 228 return 0; 229 } 230 231 int 232 opmsread(dev, uio, flag) 233 dev_t dev; 234 struct uio *uio; 235 int flag; 236 { 237 struct opms_softc *sc = opms_cd.cd_devs[PMSUNIT(dev)]; 238 int s; 239 int error = 0; 240 size_t length; 241 u_char buffer[PMS_CHUNK]; 242 243 /* Block until mouse activity occurred. */ 244 245 s = spltty(); 246 while (sc->sc_q.c_cc == 0) { 247 if (flag & IO_NDELAY) { 248 splx(s); 249 return EWOULDBLOCK; 250 } 251 sc->sc_state |= PMS_ASLP; 252 error = tsleep((caddr_t)sc, PZERO | PCATCH, "pmsrea", 0); 253 if (error) { 254 sc->sc_state &= ~PMS_ASLP; 255 splx(s); 256 return error; 257 } 258 } 259 splx(s); 260 261 /* Transfer as many chunks as possible. */ 262 263 while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) { 264 length = min(sc->sc_q.c_cc, uio->uio_resid); 265 if (length > sizeof(buffer)) 266 length = sizeof(buffer); 267 268 /* Remove a small chunk from the input queue. */ 269 (void) q_to_b(&sc->sc_q, buffer, length); 270 271 /* Copy the data to the user process. */ 272 error = uiomove(buffer, length, uio); 273 if (error) 274 break; 275 } 276 277 return error; 278 } 279 280 int 281 opmsioctl(dev, cmd, addr, flag, p) 282 dev_t dev; 283 u_long cmd; 284 caddr_t addr; 285 int flag; 286 struct proc *p; 287 { 288 struct opms_softc *sc = opms_cd.cd_devs[PMSUNIT(dev)]; 289 struct mouseinfo info; 290 int s; 291 int error; 292 293 switch (cmd) { 294 case MOUSEIOCREAD: 295 s = spltty(); 296 297 info.status = sc->sc_status; 298 if (sc->sc_x || sc->sc_y) 299 info.status |= MOVEMENT; 300 301 if (sc->sc_x > 127) 302 info.xmotion = 127; 303 else if (sc->sc_x < -127) 304 /* Bounding at -127 avoids a bug in XFree86. */ 305 info.xmotion = -127; 306 else 307 info.xmotion = sc->sc_x; 308 309 if (sc->sc_y > 127) 310 info.ymotion = 127; 311 else if (sc->sc_y < -127) 312 info.ymotion = -127; 313 else 314 info.ymotion = sc->sc_y; 315 316 /* Reset historical information. */ 317 sc->sc_x = sc->sc_y = 0; 318 sc->sc_status &= ~BUTCHNGMASK; 319 ndflush(&sc->sc_q, sc->sc_q.c_cc); 320 321 splx(s); 322 error = copyout(&info, addr, sizeof(struct mouseinfo)); 323 break; 324 default: 325 error = EINVAL; 326 break; 327 } 328 329 return error; 330 } 331 332 /* Masks for the first byte of a packet */ 333 #define PS2LBUTMASK 0x01 334 #define PS2RBUTMASK 0x02 335 #define PS2MBUTMASK 0x04 336 337 int 338 opmsintr(arg) 339 void *arg; 340 { 341 struct opms_softc *sc = arg; 342 static int state = 0; 343 static u_char buttons; 344 u_char changed; 345 static char dx, dy; 346 u_char buffer[5]; 347 348 if ((sc->sc_state & PMS_OPEN) == 0) { 349 /* Interrupts are not expected. Discard the byte. */ 350 kbd_flush_input(); 351 return 0; 352 } 353 354 switch (state) { 355 356 case 0: 357 buttons = kbd_data_read_1(); 358 if ((buttons & 0xc0) == 0) 359 ++state; 360 break; 361 362 case 1: 363 dx = kbd_data_read_1(); 364 /* Bounding at -127 avoids a bug in XFree86. */ 365 dx = (dx == -128) ? -127 : dx; 366 ++state; 367 break; 368 369 case 2: 370 dy = kbd_data_read_1(); 371 dy = (dy == -128) ? -127 : dy; 372 state = 0; 373 374 buttons = ((buttons & PS2LBUTMASK) << 2) | 375 ((buttons & (PS2RBUTMASK | PS2MBUTMASK)) >> 1); 376 changed = ((buttons ^ sc->sc_status) & BUTSTATMASK) << 3; 377 sc->sc_status = buttons | (sc->sc_status & ~BUTSTATMASK) | changed; 378 379 if (dx || dy || changed) { 380 /* Update accumulated movements. */ 381 sc->sc_x += dx; 382 sc->sc_y += dy; 383 384 /* Add this event to the queue. */ 385 buffer[0] = 0x80 | (buttons & BUTSTATMASK); 386 if(dx < 0) 387 buffer[0] |= 0x10; 388 buffer[1] = dx & 0x7f; 389 if(dy < 0) 390 buffer[0] |= 0x20; 391 buffer[2] = dy & 0x7f; 392 buffer[3] = buffer[4] = 0; 393 (void) b_to_q(buffer, sizeof buffer, &sc->sc_q); 394 395 if (sc->sc_state & PMS_ASLP) { 396 sc->sc_state &= ~PMS_ASLP; 397 wakeup((caddr_t)sc); 398 } 399 selwakeup(&sc->sc_rsel); 400 } 401 402 break; 403 } 404 return -1; 405 } 406 407 int 408 opmspoll(dev, events, p) 409 dev_t dev; 410 int events; 411 struct proc *p; 412 { 413 struct opms_softc *sc = opms_cd.cd_devs[PMSUNIT(dev)]; 414 int revents = 0; 415 int s = spltty(); 416 417 if (events & (POLLIN | POLLRDNORM)) { 418 if (sc->sc_q.c_cc > 0) 419 revents |= events & (POLLIN | POLLRDNORM); 420 else 421 selrecord(p, &sc->sc_rsel); 422 } 423 424 splx(s); 425 return (revents); 426 } 427