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 * $Id: lms.c,v 1.6 1993/08/02 17:52:34 mycroft Exp $ 23 */ 24 25 #include "lms.h" 26 27 #if NLMS > 0 28 29 #include "param.h" 30 #include "kernel.h" 31 #include "systm.h" 32 #include "buf.h" 33 #include "malloc.h" 34 #include "ioctl.h" 35 #include "tty.h" 36 #include "file.h" 37 #ifdef NetBSD 38 #include "select.h" 39 #endif 40 #include "proc.h" 41 #include "vnode.h" 42 43 #include "i386/include/mouse.h" 44 #include "i386/include/pio.h" /* Julian's fast IO macros */ 45 #include "i386/isa/isa_device.h" 46 47 #define DATA 0 /* Offset for data port, read-only */ 48 #define SIGN 1 /* Offset for signature port, read-write */ 49 #define INTR 2 /* Offset for interrupt port, read-only */ 50 #define CNTRL 2 /* Offset for control port, write-only */ 51 #define CONFIG 3 /* for configuration port, read-write */ 52 53 #define LMSUNIT(dev) (minor(dev) >> 1) 54 55 #ifndef min 56 #define min(x,y) (x < y ? x : y) 57 #endif min 58 59 int lmsprobe (struct isa_device *); 60 int lmsattach (struct isa_device *); 61 62 static int lmsaddr[NLMS]; /* Base I/O port addresses per unit */ 63 64 #define MSBSZ 1024 /* Output queue size (pwr of 2 is best) */ 65 66 struct ringbuf { 67 int count, first, last; 68 char queue[MSBSZ]; 69 }; 70 71 static struct lms_softc { /* Driver status information */ 72 struct ringbuf inq; /* Input queue */ 73 #ifdef NetBSD 74 struct selinfo rsel; 75 #else 76 pid_t rsel; /* Process selecting for Input */ 77 #endif 78 unsigned char state; /* Mouse driver state */ 79 unsigned char status; /* Mouse button status */ 80 unsigned char button; /* Previous mouse button status bits */ 81 int x, y; /* accumulated motion in the X,Y axis */ 82 } lms_softc[NLMS]; 83 84 #define OPEN 1 /* Device is open */ 85 #define ASLP 2 /* Waiting for mouse data */ 86 87 struct isa_driver lmsdriver = { lmsprobe, lmsattach, "lms" }; 88 89 int lmsprobe(struct isa_device *dvp) 90 { 91 int ioport = dvp->id_iobase; 92 int val; 93 94 /* Configure and check for port present */ 95 96 outb(ioport+CONFIG, 0x91); 97 DELAY(10); 98 outb(ioport+SIGN, 0x0C); 99 DELAY(10); 100 val = inb(ioport+SIGN); 101 DELAY(10); 102 outb(ioport+SIGN, 0x50); 103 104 /* Check if something is out there */ 105 106 if (val == 0x0C && inb(ioport+SIGN) == 0x50) 107 return(4); 108 109 /* Not present */ 110 111 return(0); 112 } 113 114 int lmsattach(struct isa_device *dvp) 115 { 116 int unit = dvp->id_unit; 117 int ioport = dvp->id_iobase; 118 struct lms_softc *sc = &lms_softc[unit]; 119 120 /* Save I/O base address */ 121 122 lmsaddr[unit] = ioport; 123 124 /* Disable mouse interrupts */ 125 126 outb(ioport+CNTRL, 0x10); 127 128 /* Setup initial state */ 129 130 sc->state = 0; 131 132 /* Done */ 133 134 return(0); 135 } 136 137 int lmsopen(dev_t dev, int flag, int fmt, struct proc *p) 138 { 139 int unit = LMSUNIT(dev); 140 struct lms_softc *sc; 141 int ioport; 142 143 /* Validate unit number */ 144 145 if (unit >= NLMS) 146 return(ENXIO); 147 148 /* Get device data */ 149 150 sc = &lms_softc[unit]; 151 ioport = lmsaddr[unit]; 152 153 /* If device does not exist */ 154 155 if (ioport == 0) 156 return(ENXIO); 157 158 /* Disallow multiple opens */ 159 160 if (sc->state & OPEN) 161 return(EBUSY); 162 163 /* Initialize state */ 164 165 sc->state |= OPEN; 166 #ifdef NetBSD 167 sc->rsel.si_pid = 0; 168 sc->rsel.si_coll = 0; 169 #else 170 sc->rsel = 0; 171 #endif 172 sc->status = 0; 173 sc->button = 0; 174 sc->x = 0; 175 sc->y = 0; 176 177 /* Allocate and initialize a ring buffer */ 178 179 sc->inq.count = sc->inq.first = sc->inq.last = 0; 180 181 /* Enable Bus Mouse interrupts */ 182 183 outb(ioport+CNTRL, 0); 184 185 /* Successful open */ 186 187 return(0); 188 } 189 190 int lmsclose(dev_t dev, int flag, int fmt, struct proc *p) 191 { 192 int unit, ioport; 193 struct lms_softc *sc; 194 195 /* Get unit and associated info */ 196 197 unit = LMSUNIT(dev); 198 sc = &lms_softc[unit]; 199 ioport = lmsaddr[unit]; 200 201 /* Disable further mouse interrupts */ 202 203 outb(ioport+CNTRL, 0x10); 204 205 /* Complete the close */ 206 207 sc->state &= ~OPEN; 208 209 /* close is almost always successful */ 210 211 return(0); 212 } 213 214 int lmsread(dev_t dev, struct uio *uio, int flag) 215 { 216 int s; 217 int error = 0; /* keep compiler quiet, even though initialisation 218 is unnecessary */ 219 unsigned length; 220 struct lms_softc *sc; 221 unsigned char buffer[100]; 222 223 /* Get device information */ 224 225 sc = &lms_softc[LMSUNIT(dev)]; 226 227 /* Block until mouse activity occured */ 228 229 s = spltty(); 230 while (sc->inq.count == 0) { 231 if (minor(dev) & 0x1) { 232 splx(s); 233 return(EWOULDBLOCK); 234 } 235 sc->state |= ASLP; 236 error = tsleep((caddr_t)sc, PZERO | PCATCH, "lmsrea", 0); 237 if (error != 0) { 238 splx(s); 239 return(error); 240 } 241 } 242 243 /* Transfer as many chunks as possible */ 244 245 while (sc->inq.count > 0 && uio->uio_resid > 0) { 246 length = min(sc->inq.count, uio->uio_resid); 247 if (length > sizeof(buffer)) 248 length = sizeof(buffer); 249 250 /* Remove a small chunk from input queue */ 251 252 if (sc->inq.first + length >= MSBSZ) { 253 bcopy(&sc->inq.queue[sc->inq.first], 254 buffer, MSBSZ - sc->inq.first); 255 bcopy(sc->inq.queue, &buffer[MSBSZ-sc->inq.first], 256 length - (MSBSZ - sc->inq.first)); 257 } 258 else 259 bcopy(&sc->inq.queue[sc->inq.first], buffer, length); 260 261 sc->inq.first = (sc->inq.first + length) % MSBSZ; 262 sc->inq.count -= length; 263 264 /* Copy data to user process */ 265 266 error = uiomove(buffer, length, uio); 267 if (error) 268 break; 269 } 270 271 sc->x = sc->y = 0; 272 273 /* Allow interrupts again */ 274 275 splx(s); 276 return(error); 277 } 278 279 int lmsioctl(dev_t dev, caddr_t addr, int cmd, int flag, struct proc *p) 280 { 281 struct lms_softc *sc; 282 struct mouseinfo info; 283 int s, error; 284 285 /* Get device information */ 286 287 sc = &lms_softc[LMSUNIT(dev)]; 288 289 /* Perform IOCTL command */ 290 291 switch (cmd) { 292 293 case MOUSEIOCREAD: 294 295 /* Don't modify info while calculating */ 296 297 s = spltty(); 298 299 /* Build mouse status octet */ 300 301 info.status = sc->status; 302 if (sc->x || sc->y) 303 info.status |= MOVEMENT; 304 305 /* Encode X and Y motion as good as we can */ 306 307 if (sc->x > 127) 308 info.xmotion = 127; 309 else if (sc->x < -127) 310 info.xmotion = -127; 311 else 312 info.xmotion = sc->x; 313 314 if (sc->y > 127) 315 info.ymotion = 127; 316 else if (sc->y < -127) 317 info.ymotion = -127; 318 else 319 info.ymotion = sc->y; 320 321 /* Reset historical information */ 322 323 sc->x = 0; 324 sc->y = 0; 325 sc->status &= ~BUTCHNGMASK; 326 327 /* Allow interrupts and copy result buffer */ 328 329 splx(s); 330 error = copyout(&info, addr, sizeof(struct mouseinfo)); 331 break; 332 333 default: 334 error = EINVAL; 335 break; 336 } 337 338 /* Return error code */ 339 340 return(error); 341 } 342 343 void lmsintr(unit) 344 int unit; 345 { 346 struct lms_softc *sc = &lms_softc[unit]; 347 int ioport = lmsaddr[unit]; 348 char hi, lo, dx, dy, buttons, changed; 349 350 outb(ioport+CNTRL, 0xAB); 351 hi = inb(ioport+DATA) & 15; 352 outb(ioport+CNTRL, 0x90); 353 lo = inb(ioport+DATA) & 15; 354 dx = (hi << 4) | lo; 355 dx = (dx == -128) ? -127 : dx; 356 357 outb(ioport+CNTRL, 0xF0); 358 hi = inb(ioport+DATA); 359 outb(ioport+CNTRL, 0xD0); 360 lo = inb(ioport+DATA); 361 outb(ioport+CNTRL, 0); 362 dy = ((hi & 15) << 4) | (lo & 15); 363 dy = (dy == -128) ? 127 : -dy; 364 365 buttons = (~hi >> 5) & 7; 366 changed = buttons ^ sc->button; 367 sc->button = buttons; 368 sc->status = buttons | (sc->status & ~BUTSTATMASK) | (changed << 3); 369 370 /* Update accumulated movements */ 371 372 sc->x += dx; 373 sc->y += dy; 374 375 /* If device in use and a change occurred... */ 376 377 if (sc->state & OPEN && (dx || dy || changed)) { 378 sc->inq.queue[sc->inq.last++] = 0x80 | (buttons ^ BUTSTATMASK); 379 sc->inq.queue[sc->inq.last++ % MSBSZ] = dx; 380 sc->inq.queue[sc->inq.last++ % MSBSZ] = dy; 381 sc->inq.queue[sc->inq.last++ % MSBSZ] = 0; 382 sc->inq.queue[sc->inq.last++ % MSBSZ] = 0; 383 sc->inq.last = sc->inq.last % MSBSZ; 384 sc->inq.count += 5; 385 386 if (sc->state & ASLP) { 387 sc->state &= ~ASLP; 388 wakeup((caddr_t)sc); 389 } 390 #ifdef NetBSD 391 selwakeup(&sc->rsel); 392 #else 393 if (sc->rsel) { 394 selwakeup(sc->rsel, 0); 395 sc->rsel = 0; 396 } 397 #endif 398 } 399 } 400 401 int lmsselect(dev_t dev, int rw, struct proc *p) 402 { 403 int s, ret; 404 struct lms_softc *sc = &lms_softc[LMSUNIT(dev)]; 405 406 /* Silly to select for output */ 407 408 if (rw == FWRITE) 409 return(0); 410 411 /* Return true if a mouse event available */ 412 413 s = spltty(); 414 if (sc->inq.count) 415 ret = 1; 416 else { 417 #ifdef NetBSD 418 selrecord(p, &sc->rsel); 419 #else 420 sc->rsel = p->p_pid; 421 #endif 422 ret = 0; 423 } 424 splx(s); 425 426 return(ret); 427 } 428 #endif 429