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