1 /*- 2 * Copyright (c) 1992, 1993 Erik Forsberg. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED 12 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 14 * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 15 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 16 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 17 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 18 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 19 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 20 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 21 */ 22 23 /* 24 * Ported to 386bsd Oct 17, 1992 25 * Sandi Donno, Computer Science, University of Cape Town, South Africa 26 * Please send bug reports to sandi@cs.uct.ac.za 27 * 28 * Thanks are also due to Rick Macklem, rick@snowhite.cis.uoguelph.ca - 29 * although I was only partially successful in getting the alpha release 30 * of his "driver for the Logitech and ATI Inport Bus mice for use with 31 * 386bsd and the X386 port" to work with my Microsoft mouse, I nevertheless 32 * found his code to be an invaluable reference when porting this driver 33 * to 386bsd. 34 * 35 * Further modifications for latest 386BSD+patchkit and port to NetBSD, 36 * Andrew Herbert <andrew@werple.apana.org.au> - 8 June 1993 37 */ 38 39 #include "mms.h" 40 41 #if NMMS > 0 42 43 #include "param.h" 44 #include "kernel.h" 45 #include "systm.h" 46 #include "buf.h" 47 #include "malloc.h" 48 #include "ioctl.h" 49 #include "tty.h" 50 #include "file.h" 51 #ifdef NetBSD 52 #include "select.h" 53 #endif 54 #include "proc.h" 55 #include "vnode.h" 56 57 #include "i386/include/mouse.h" 58 #include "i386/include/pio.h" /* Julian's fast IO macros */ 59 #include "i386/isa/isa_device.h" 60 61 #define ADDR 0 /* Offset for register select */ 62 #define DATA 1 /* Offset for InPort data */ 63 #define IDENT 2 /* Offset for identification register */ 64 65 #define MMSUNIT(dev) (minor(dev) >> 1) 66 67 #ifndef min 68 #define min(x,y) (x < y ? x : y) 69 #endif min 70 71 int mmsprobe (struct isa_device *); 72 int mmsattach (struct isa_device *); 73 74 static int mmsaddr[NMMS]; /* Base I/O port addresses per unit */ 75 76 #define MSBSZ 1024 /* Output queue size (pwr of 2 is best) */ 77 78 struct ringbuf { 79 int count, first, last; 80 char queue[MSBSZ]; 81 }; 82 83 static struct mms_softc { /* Driver status information */ 84 struct ringbuf inq; /* Input queue */ 85 #ifdef NetBSD 86 struct selinfo rsel; 87 #else 88 pid_t rsel; /* Process selecting for Input */ 89 #endif 90 unsigned char state; /* Mouse driver state */ 91 unsigned char status; /* Mouse button status */ 92 int x, y; /* accumulated motion in the X,Y axis */ 93 } mms_softc[NMMS]; 94 95 #define OPEN 1 /* Device is open */ 96 #define ASLP 2 /* Waiting for mouse data */ 97 98 struct isa_driver mmsdriver = { mmsprobe, mmsattach, "mms" }; 99 100 int mmsprobe(struct isa_device *dvp) 101 { 102 int ioport = dvp->id_iobase; 103 104 /* Read identification register to see if present */ 105 106 if (inb(ioport+IDENT) != 0xDE) 107 return(0); 108 109 /* Seems it was there; reset */ 110 111 outb(ioport+ADDR, 0x87); 112 return(4); 113 } 114 115 int mmsattach(struct isa_device *dvp) 116 { 117 int unit = dvp->id_unit; 118 int ioport = dvp->id_iobase; 119 struct mms_softc *sc = &mms_softc[unit]; 120 121 /* Save I/O base address */ 122 123 mmsaddr[unit] = ioport; 124 125 /* Setup initial state */ 126 127 sc->state = 0; 128 129 /* Done */ 130 131 return(0); 132 } 133 134 int mmsopen(dev_t dev, int flag, int fmt, struct proc *p) 135 { 136 int unit = MMSUNIT(dev); 137 struct mms_softc *sc; 138 int ioport; 139 140 /* Validate unit number */ 141 142 if (unit >= NMMS) 143 return(ENXIO); 144 145 /* Get device data */ 146 147 sc = &mms_softc[unit]; 148 ioport = mmsaddr[unit]; 149 150 /* If device does not exist */ 151 152 if (ioport == 0) 153 return(ENXIO); 154 155 /* Disallow multiple opens */ 156 157 if (sc->state & OPEN) 158 return(EBUSY); 159 160 /* Initialize state */ 161 162 sc->state |= OPEN; 163 #ifdef NetBSD 164 sc->rsel.si_pid = 0; 165 sc->rsel.si_coll = 0; 166 #else 167 sc->rsel = 0; 168 #endif 169 sc->status = 0; 170 sc->x = 0; 171 sc->y = 0; 172 173 /* Allocate and initialize a ring buffer */ 174 175 sc->inq.count = sc->inq.first = sc->inq.last = 0; 176 177 /* Setup Bus Mouse */ 178 179 outb(ioport+ADDR, 7); 180 outb(ioport+DATA, 0x09); 181 182 /* Successful open */ 183 184 return(0); 185 } 186 187 int mmsclose(dev_t dev, int flag, int fmt, struct proc *p) 188 { 189 int unit, ioport; 190 struct mms_softc *sc; 191 192 /* Get unit and associated info */ 193 194 unit = MMSUNIT(dev); 195 sc = &mms_softc[unit]; 196 ioport = mmsaddr[unit]; 197 198 /* Reset Bus Mouse */ 199 200 outb(ioport+ADDR, 0x87); 201 202 /* Complete the close */ 203 204 sc->state &= ~OPEN; 205 206 /* close is almost always successful */ 207 208 return(0); 209 } 210 211 int mmsread(dev_t dev, struct uio *uio, int flag) 212 { 213 int s, error = 0; 214 unsigned length; 215 struct mms_softc *sc; 216 unsigned char buffer[100]; 217 218 /* Get device information */ 219 220 sc = &mms_softc[MMSUNIT(dev)]; 221 222 /* Block until mouse activity occured */ 223 224 s = spltty(); 225 while (sc->inq.count == 0) { 226 if (minor(dev) & 0x1) { 227 splx(s); 228 return(EWOULDBLOCK); 229 } 230 sc->state |= ASLP; 231 error = tsleep((caddr_t)sc, PZERO | PCATCH, "mmsrea", 0); 232 if (error != 0) { 233 splx(s); 234 return(error); 235 } 236 } 237 238 /* Transfer as many chunks as possible */ 239 240 while (sc->inq.count > 0 && uio->uio_resid > 0) { 241 length = min(sc->inq.count, uio->uio_resid); 242 if (length > sizeof(buffer)) 243 length = sizeof(buffer); 244 245 /* Remove a small chunk from input queue */ 246 247 if (sc->inq.first + length >= MSBSZ) { 248 bcopy(&sc->inq.queue[sc->inq.first], 249 buffer, MSBSZ - sc->inq.first); 250 bcopy(sc->inq.queue, &buffer[MSBSZ-sc->inq.first], 251 length - (MSBSZ - sc->inq.first)); 252 } 253 else 254 bcopy(&sc->inq.queue[sc->inq.first], buffer, length); 255 256 sc->inq.first = (sc->inq.first + length) % MSBSZ; 257 sc->inq.count -= length; 258 259 /* Copy data to user process */ 260 261 error = uiomove(buffer, length, uio); 262 if (error) 263 break; 264 } 265 266 sc->x = sc->y = 0; 267 268 /* Allow interrupts again */ 269 270 splx(s); 271 return(error); 272 } 273 274 int mmsioctl(dev_t dev, caddr_t addr, int cmd, int flag, struct proc *p) 275 { 276 struct mms_softc *sc; 277 struct mouseinfo info; 278 int s, error; 279 280 /* Get device information */ 281 282 sc = &mms_softc[MMSUNIT(dev)]; 283 284 /* Perform IOCTL command */ 285 286 switch (cmd) { 287 288 case MOUSEIOCREAD: 289 290 /* Don't modify info while calculating */ 291 292 s = spltty(); 293 294 /* Build mouse status octet */ 295 296 info.status = sc->status; 297 if (sc->x || sc->y) 298 info.status |= MOVEMENT; 299 300 /* Encode X and Y motion as good as we can */ 301 302 if (sc->x > 127) 303 info.xmotion = 127; 304 else if (sc->x < -128) 305 info.xmotion = -128; 306 else 307 info.xmotion = sc->x; 308 309 if (sc->y > 127) 310 info.ymotion = 127; 311 else if (sc->y < -128) 312 info.ymotion = -128; 313 else 314 info.ymotion = sc->y; 315 316 /* Reset historical information */ 317 318 sc->x = 0; 319 sc->y = 0; 320 sc->status &= ~BUTCHNGMASK; 321 322 /* Allow interrupts and copy result buffer */ 323 324 splx(s); 325 error = copyout(&info, addr, sizeof(struct mouseinfo)); 326 break; 327 328 default: 329 error = EINVAL; 330 break; 331 } 332 333 /* Return error code */ 334 335 return(error); 336 } 337 338 void mmsintr(unit) 339 int unit; 340 { 341 struct mms_softc *sc = &mms_softc[unit]; 342 int ioport = mmsaddr[unit]; 343 char dx, dy, status; 344 345 /* Freeze InPort registers (disabling interrupts) */ 346 347 outb(ioport+ADDR, 7); 348 outb(ioport+DATA, 0x29); 349 350 /* Read mouse status */ 351 352 outb(ioport+ADDR, 0); 353 status = inb(ioport+DATA); 354 355 /* Check if any movement detected */ 356 357 if (status & 0x40) { 358 outb(ioport+ADDR, 1); 359 dx = inb(ioport+DATA); 360 outb(ioport+ADDR, 2); 361 dy = inb(ioport+DATA); 362 dy = (dy == -128) ? 127 : -dy; 363 } 364 else 365 dx = dy = 0; 366 367 /* Unfreeze InPort Registers (re-enables interrupts) */ 368 369 outb(ioport+ADDR, 7); 370 outb(ioport+DATA, 0x09); 371 372 /* Update accumulated movements */ 373 374 sc->x += dx; 375 sc->y += dy; 376 377 /* Inclusive OR status changes, but always save only last state */ 378 379 sc->status |= status & BUTCHNGMASK; 380 sc->status = (sc->status & ~BUTSTATMASK) | (status & BUTSTATMASK); 381 382 /* If device in use and a change occurred... */ 383 384 if (sc->state & OPEN && status & 0x78 && sc->inq.count < (MSBSZ-5)) { 385 status &= BUTSTATMASK; 386 sc->inq.queue[sc->inq.last++] = 0x80 | (status ^ BUTSTATMASK); 387 sc->inq.queue[sc->inq.last++ % MSBSZ] = dx; 388 sc->inq.queue[sc->inq.last++ % MSBSZ] = dy; 389 sc->inq.queue[sc->inq.last++ % MSBSZ] = 0; 390 sc->inq.queue[sc->inq.last++ % MSBSZ] = 0; 391 sc->inq.last = sc->inq.last % MSBSZ; 392 sc->inq.count += 5; 393 394 if (sc->state & ASLP) { 395 sc->state &= ~ASLP; 396 wakeup((caddr_t)sc); 397 } 398 #ifdef NetBSD 399 selwakeup(&sc->rsel); 400 #else 401 if (sc->rsel) { 402 selwakeup(sc->rsel, 0); 403 sc->rsel = 0; 404 } 405 #endif 406 } 407 } 408 409 int mmsselect(dev_t dev, int rw, struct proc *p) 410 { 411 int s, ret; 412 struct mms_softc *sc = &mms_softc[MMSUNIT(dev)]; 413 414 /* Silly to select for output */ 415 416 if (rw == FWRITE) 417 return(0); 418 419 /* Return true if a mouse event available */ 420 421 s = spltty(); 422 if (sc->inq.count) 423 ret = 1; 424 else { 425 #ifdef NetBSD 426 selrecord(p, &sc->rsel); 427 #else 428 sc->rsel = p->p_pid; 429 #endif 430 ret = 0; 431 } 432 splx(s); 433 434 return(ret); 435 } 436 #endif 437