1 /* $NetBSD: mms.c,v 1.16 1994/11/18 22:03:32 mycroft Exp $ */ 2 3 /*- 4 * Copyright (c) 1993, 1994 Charles Hannum. 5 * Copyright (c) 1992, 1993 Erik Forsberg. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 16 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 17 * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 21 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 22 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include <sys/param.h> 27 #include <sys/kernel.h> 28 #include <sys/systm.h> 29 #include <sys/buf.h> 30 #include <sys/malloc.h> 31 #include <sys/ioctl.h> 32 #include <sys/tty.h> 33 #include <sys/file.h> 34 #include <sys/select.h> 35 #include <sys/proc.h> 36 #include <sys/vnode.h> 37 #include <sys/device.h> 38 39 #include <machine/cpu.h> 40 #include <machine/pio.h> 41 #include <machine/mouse.h> 42 43 #include <i386/isa/isavar.h> 44 45 #define MMS_ADDR 0 /* offset for register select */ 46 #define MMS_DATA 1 /* offset for InPort data */ 47 #define MMS_IDENT 2 /* offset for identification register */ 48 #define MMS_NPORTS 4 49 50 #define MMS_CHUNK 128 /* chunk size for read */ 51 #define MMS_BSIZE 1020 /* buffer size */ 52 53 struct mms_softc { /* driver status information */ 54 struct device sc_dev; 55 struct intrhand sc_ih; 56 57 struct clist sc_q; 58 struct selinfo sc_rsel; 59 int sc_iobase; /* I/O port base */ 60 u_char sc_state; /* mouse driver state */ 61 #define MMS_OPEN 0x01 /* device is open */ 62 #define MMS_ASLP 0x02 /* waiting for mouse data */ 63 u_char sc_status; /* mouse button status */ 64 int sc_x, sc_y; /* accumulated motion in the X,Y axis */ 65 }; 66 67 int mmsprobe __P((struct device *, void *, void *)); 68 void mmsattach __P((struct device *, struct device *, void *)); 69 int mmsintr __P((struct mms_softc *)); 70 71 struct cfdriver mmscd = { 72 NULL, "mms", mmsprobe, mmsattach, DV_TTY, sizeof(struct mms_softc) 73 }; 74 75 #define MMSUNIT(dev) (minor(dev)) 76 77 int 78 mmsprobe(parent, match, aux) 79 struct device *parent; 80 void *match, *aux; 81 { 82 struct isa_attach_args *ia = aux; 83 int iobase = ia->ia_iobase; 84 85 /* Read identification register to see if present */ 86 if (inb(iobase + MMS_IDENT) != 0xde) 87 return 0; 88 89 /* Seems it was there; reset. */ 90 outb(iobase + MMS_ADDR, 0x87); 91 92 ia->ia_iosize = MMS_NPORTS; 93 ia->ia_msize = 0; 94 return 1; 95 } 96 97 void 98 mmsattach(parent, self, aux) 99 struct device *parent, *self; 100 void *aux; 101 { 102 struct mms_softc *sc = (void *)self; 103 struct isa_attach_args *ia = aux; 104 int iobase = ia->ia_iobase; 105 106 printf("\n"); 107 108 /* Other initialization was done by mmsprobe. */ 109 sc->sc_iobase = iobase; 110 sc->sc_state = 0; 111 112 sc->sc_ih.ih_fun = mmsintr; 113 sc->sc_ih.ih_arg = sc; 114 sc->sc_ih.ih_level = IPL_NONE; 115 intr_establish(ia->ia_irq, &sc->sc_ih); 116 } 117 118 int 119 mmsopen(dev, flag) 120 dev_t dev; 121 int flag; 122 { 123 int unit = MMSUNIT(dev); 124 struct mms_softc *sc; 125 126 if (unit >= mmscd.cd_ndevs) 127 return ENXIO; 128 sc = mmscd.cd_devs[unit]; 129 if (!sc) 130 return ENXIO; 131 132 if (sc->sc_state & MMS_OPEN) 133 return EBUSY; 134 135 if (clalloc(&sc->sc_q, MMS_BSIZE, 0) == -1) 136 return ENOMEM; 137 138 sc->sc_state |= MMS_OPEN; 139 sc->sc_status = 0; 140 sc->sc_x = sc->sc_y = 0; 141 142 /* Enable interrupts. */ 143 outb(sc->sc_iobase + MMS_ADDR, 0x07); 144 outb(sc->sc_iobase + MMS_DATA, 0x09); 145 146 return 0; 147 } 148 149 int 150 mmsclose(dev, flag) 151 dev_t dev; 152 int flag; 153 { 154 struct mms_softc *sc = mmscd.cd_devs[MMSUNIT(dev)]; 155 156 /* Disable interrupts. */ 157 outb(sc->sc_iobase + MMS_ADDR, 0x87); 158 159 sc->sc_state &= ~MMS_OPEN; 160 161 clfree(&sc->sc_q); 162 163 return 0; 164 } 165 166 int 167 mmsread(dev, uio, flag) 168 dev_t dev; 169 struct uio *uio; 170 int flag; 171 { 172 struct mms_softc *sc = mmscd.cd_devs[MMSUNIT(dev)]; 173 int s; 174 int error; 175 size_t length; 176 u_char buffer[MMS_CHUNK]; 177 178 /* Block until mouse activity occured. */ 179 180 s = spltty(); 181 while (sc->sc_q.c_cc == 0) { 182 if (flag & IO_NDELAY) { 183 splx(s); 184 return EWOULDBLOCK; 185 } 186 sc->sc_state |= MMS_ASLP; 187 if (error = tsleep((caddr_t)sc, PZERO | PCATCH, "mmsrea", 0)) { 188 sc->sc_state &= ~MMS_ASLP; 189 splx(s); 190 return error; 191 } 192 } 193 splx(s); 194 195 /* Transfer as many chunks as possible. */ 196 197 while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) { 198 length = min(sc->sc_q.c_cc, uio->uio_resid); 199 if (length > sizeof(buffer)) 200 length = sizeof(buffer); 201 202 /* Remove a small chunk from the input queue. */ 203 (void) q_to_b(&sc->sc_q, buffer, length); 204 205 /* Copy the data to the user process. */ 206 if (error = uiomove(buffer, length, uio)) 207 break; 208 } 209 210 return error; 211 } 212 213 int 214 mmsioctl(dev, cmd, addr, flag) 215 dev_t dev; 216 u_long cmd; 217 caddr_t addr; 218 int flag; 219 { 220 struct mms_softc *sc = mmscd.cd_devs[MMSUNIT(dev)]; 221 struct mouseinfo info; 222 int s; 223 int error; 224 225 switch (cmd) { 226 case MOUSEIOCREAD: 227 s = spltty(); 228 229 info.status = sc->sc_status; 230 if (sc->sc_x || sc->sc_y) 231 info.status |= MOVEMENT; 232 233 if (sc->sc_x > 127) 234 info.xmotion = 127; 235 else if (sc->sc_x < -127) 236 /* Bounding at -127 avoids a bug in XFree86. */ 237 info.xmotion = -127; 238 else 239 info.xmotion = sc->sc_x; 240 241 if (sc->sc_y > 127) 242 info.ymotion = 127; 243 else if (sc->sc_y < -127) 244 info.ymotion = -127; 245 else 246 info.ymotion = sc->sc_y; 247 248 /* Reset historical information. */ 249 sc->sc_x = sc->sc_y = 0; 250 sc->sc_status &= ~BUTCHNGMASK; 251 ndflush(&sc->sc_q, sc->sc_q.c_cc); 252 253 splx(s); 254 error = copyout(&info, addr, sizeof(struct mouseinfo)); 255 break; 256 257 default: 258 error = EINVAL; 259 break; 260 } 261 262 return error; 263 } 264 265 int 266 mmsintr(sc) 267 struct mms_softc *sc; 268 { 269 int iobase = sc->sc_iobase; 270 u_char buttons, changed, status; 271 char dx, dy; 272 u_char buffer[5]; 273 274 if ((sc->sc_state & MMS_OPEN) == 0) 275 /* Interrupts are not expected. */ 276 return 0; 277 278 /* Freeze InPort registers (disabling interrupts). */ 279 outb(iobase + MMS_ADDR, 0x07); 280 outb(iobase + MMS_DATA, 0x29); 281 282 outb(iobase + MMS_ADDR, 0x00); 283 status = inb(iobase + MMS_DATA); 284 285 if (status & 0x40) { 286 outb(iobase + MMS_ADDR, 1); 287 dx = inb(iobase + MMS_DATA); 288 dx = (dx == -128) ? -127 : dx; 289 outb(iobase + MMS_ADDR, 2); 290 dy = inb(iobase + MMS_DATA); 291 dy = (dy == -128) ? 127 : -dy; 292 } else 293 dx = dy = 0; 294 295 /* Unfreeze InPort registers (reenabling interrupts). */ 296 outb(iobase + MMS_ADDR, 0x07); 297 outb(iobase + MMS_DATA, 0x09); 298 299 buttons = status & BUTSTATMASK; 300 changed = status & BUTCHNGMASK; 301 sc->sc_status = buttons | (sc->sc_status & ~BUTSTATMASK) | changed; 302 303 if (dx || dy || changed) { 304 /* Update accumulated movements. */ 305 sc->sc_x += dx; 306 sc->sc_y += dy; 307 308 /* Add this event to the queue. */ 309 buffer[0] = 0x80 | (buttons ^ BUTSTATMASK); 310 buffer[1] = dx; 311 buffer[2] = dy; 312 buffer[3] = buffer[4] = 0; 313 (void) b_to_q(buffer, sizeof buffer, &sc->sc_q); 314 315 if (sc->sc_state & MMS_ASLP) { 316 sc->sc_state &= ~MMS_ASLP; 317 wakeup((caddr_t)sc); 318 } 319 selwakeup(&sc->sc_rsel); 320 } 321 322 return -1; 323 } 324 325 int 326 mmsselect(dev, rw, p) 327 dev_t dev; 328 int rw; 329 struct proc *p; 330 { 331 struct mms_softc *sc = mmscd.cd_devs[MMSUNIT(dev)]; 332 int s; 333 int ret; 334 335 if (rw == FWRITE) 336 return 0; 337 338 s = spltty(); 339 if (!sc->sc_q.c_cc) { 340 selrecord(p, &sc->sc_rsel); 341 ret = 0; 342 } else 343 ret = 1; 344 splx(s); 345 346 return ret; 347 } 348