1 /* $NetBSD: lms.c,v 1.19 1995/04/17 12:07:17 cgd 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 <dev/isa/isavar.h> 44 45 #define LMS_DATA 0 /* offset for data port, read-only */ 46 #define LMS_SIGN 1 /* offset for signature port, read-write */ 47 #define LMS_INTR 2 /* offset for interrupt port, read-only */ 48 #define LMS_CNTRL 2 /* offset for control port, write-only */ 49 #define LMS_CONFIG 3 /* for configuration port, read-write */ 50 #define LMS_NPORTS 4 51 52 #define LMS_CHUNK 128 /* chunk size for read */ 53 #define LMS_BSIZE 1020 /* buffer size */ 54 55 struct lms_softc { /* driver status information */ 56 struct device sc_dev; 57 void *sc_ih; 58 59 struct clist sc_q; 60 struct selinfo sc_rsel; 61 int sc_iobase; /* I/O port base */ 62 u_char sc_state; /* mouse driver state */ 63 #define LMS_OPEN 0x01 /* device is open */ 64 #define LMS_ASLP 0x02 /* waiting for mouse data */ 65 u_char sc_status; /* mouse button status */ 66 int sc_x, sc_y; /* accumulated motion in the X,Y axis */ 67 }; 68 69 int lmsprobe __P((struct device *, void *, void *)); 70 void lmsattach __P((struct device *, struct device *, void *)); 71 int lmsintr __P((void *)); 72 73 struct cfdriver lmscd = { 74 NULL, "lms", lmsprobe, lmsattach, DV_TTY, sizeof(struct lms_softc) 75 }; 76 77 #define LMSUNIT(dev) (minor(dev)) 78 79 int 80 lmsprobe(parent, match, aux) 81 struct device *parent; 82 void *match, *aux; 83 { 84 struct isa_attach_args *ia = aux; 85 int iobase = ia->ia_iobase; 86 87 /* Configure and check for port present. */ 88 outb(iobase + LMS_CONFIG, 0x91); 89 delay(10); 90 outb(iobase + LMS_SIGN, 0x0c); 91 delay(10); 92 if (inb(iobase + LMS_SIGN) != 0x0c) 93 return 0; 94 outb(iobase + LMS_SIGN, 0x50); 95 delay(10); 96 if (inb(iobase + LMS_SIGN) != 0x50) 97 return 0; 98 99 /* Disable interrupts. */ 100 outb(iobase + LMS_CNTRL, 0x10); 101 102 ia->ia_iosize = LMS_NPORTS; 103 ia->ia_msize = 0; 104 return 1; 105 } 106 107 void 108 lmsattach(parent, self, aux) 109 struct device *parent, *self; 110 void *aux; 111 { 112 struct lms_softc *sc = (void *)self; 113 struct isa_attach_args *ia = aux; 114 int iobase = ia->ia_iobase; 115 116 printf("\n"); 117 118 /* Other initialization was done by lmsprobe. */ 119 sc->sc_iobase = iobase; 120 sc->sc_state = 0; 121 122 sc->sc_ih = isa_intr_establish(ia->ia_irq, ISA_IST_PULSE, ISA_IPL_NONE, 123 lmsintr, sc); 124 } 125 126 int 127 lmsopen(dev, flag) 128 dev_t dev; 129 int flag; 130 { 131 int unit = LMSUNIT(dev); 132 struct lms_softc *sc; 133 134 if (unit >= lmscd.cd_ndevs) 135 return ENXIO; 136 sc = lmscd.cd_devs[unit]; 137 if (!sc) 138 return ENXIO; 139 140 if (sc->sc_state & LMS_OPEN) 141 return EBUSY; 142 143 if (clalloc(&sc->sc_q, LMS_BSIZE, 0) == -1) 144 return ENOMEM; 145 146 sc->sc_state |= LMS_OPEN; 147 sc->sc_status = 0; 148 sc->sc_x = sc->sc_y = 0; 149 150 /* Enable interrupts. */ 151 outb(sc->sc_iobase + LMS_CNTRL, 0); 152 153 return 0; 154 } 155 156 int 157 lmsclose(dev, flag) 158 dev_t dev; 159 int flag; 160 { 161 struct lms_softc *sc = lmscd.cd_devs[LMSUNIT(dev)]; 162 163 /* Disable interrupts. */ 164 outb(sc->sc_iobase + LMS_CNTRL, 0x10); 165 166 sc->sc_state &= ~LMS_OPEN; 167 168 clfree(&sc->sc_q); 169 170 return 0; 171 } 172 173 int 174 lmsread(dev, uio, flag) 175 dev_t dev; 176 struct uio *uio; 177 int flag; 178 { 179 struct lms_softc *sc = lmscd.cd_devs[LMSUNIT(dev)]; 180 int s; 181 int error; 182 size_t length; 183 u_char buffer[LMS_CHUNK]; 184 185 /* Block until mouse activity occured. */ 186 187 s = spltty(); 188 while (sc->sc_q.c_cc == 0) { 189 if (flag & IO_NDELAY) { 190 splx(s); 191 return EWOULDBLOCK; 192 } 193 sc->sc_state |= LMS_ASLP; 194 if (error = tsleep((caddr_t)sc, PZERO | PCATCH, "lmsrea", 0)) { 195 sc->sc_state &= ~LMS_ASLP; 196 splx(s); 197 return error; 198 } 199 } 200 splx(s); 201 202 /* Transfer as many chunks as possible. */ 203 204 while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) { 205 length = min(sc->sc_q.c_cc, uio->uio_resid); 206 if (length > sizeof(buffer)) 207 length = sizeof(buffer); 208 209 /* Remove a small chunk from the input queue. */ 210 (void) q_to_b(&sc->sc_q, buffer, length); 211 212 /* Copy the data to the user process. */ 213 if (error = uiomove(buffer, length, uio)) 214 break; 215 } 216 217 return error; 218 } 219 220 int 221 lmsioctl(dev, cmd, addr, flag) 222 dev_t dev; 223 u_long cmd; 224 caddr_t addr; 225 int flag; 226 { 227 struct lms_softc *sc = lmscd.cd_devs[LMSUNIT(dev)]; 228 struct mouseinfo info; 229 int s; 230 int error; 231 232 switch (cmd) { 233 case MOUSEIOCREAD: 234 s = spltty(); 235 236 info.status = sc->sc_status; 237 if (sc->sc_x || sc->sc_y) 238 info.status |= MOVEMENT; 239 240 if (sc->sc_x > 127) 241 info.xmotion = 127; 242 else if (sc->sc_x < -127) 243 /* Bounding at -127 avoids a bug in XFree86. */ 244 info.xmotion = -127; 245 else 246 info.xmotion = sc->sc_x; 247 248 if (sc->sc_y > 127) 249 info.ymotion = 127; 250 else if (sc->sc_y < -127) 251 info.ymotion = -127; 252 else 253 info.ymotion = sc->sc_y; 254 255 /* Reset historical information. */ 256 sc->sc_x = sc->sc_y = 0; 257 sc->sc_status &= ~BUTCHNGMASK; 258 ndflush(&sc->sc_q, sc->sc_q.c_cc); 259 260 splx(s); 261 error = copyout(&info, addr, sizeof(struct mouseinfo)); 262 break; 263 264 default: 265 error = EINVAL; 266 break; 267 } 268 269 return error; 270 } 271 272 int 273 lmsintr(arg) 274 void *arg; 275 { 276 struct lms_softc *sc = arg; 277 int iobase = sc->sc_iobase; 278 u_char hi, lo, buttons, changed; 279 char dx, dy; 280 u_char buffer[5]; 281 282 if ((sc->sc_state & LMS_OPEN) == 0) 283 /* Interrupts are not expected. */ 284 return 0; 285 286 outb(iobase + LMS_CNTRL, 0xab); 287 hi = inb(iobase + LMS_DATA); 288 outb(iobase + LMS_CNTRL, 0x90); 289 lo = inb(iobase + LMS_DATA); 290 dx = ((hi & 0x0f) << 4) | (lo & 0x0f); 291 /* Bounding at -127 avoids a bug in XFree86. */ 292 dx = (dx == -128) ? -127 : dx; 293 294 outb(iobase + LMS_CNTRL, 0xf0); 295 hi = inb(iobase + LMS_DATA); 296 outb(iobase + LMS_CNTRL, 0xd0); 297 lo = inb(iobase + LMS_DATA); 298 dy = ((hi & 0x0f) << 4) | (lo & 0x0f); 299 dy = (dy == -128) ? 127 : -dy; 300 301 outb(iobase + LMS_CNTRL, 0); 302 303 buttons = (~hi >> 5) & 0x07; 304 changed = ((buttons ^ sc->sc_status) & 0x07) << 3; 305 sc->sc_status = buttons | (sc->sc_status & ~BUTSTATMASK) | changed; 306 307 if (dx || dy || changed) { 308 /* Update accumulated movements. */ 309 sc->sc_x += dx; 310 sc->sc_y += dy; 311 312 /* Add this event to the queue. */ 313 buffer[0] = 0x80 | (buttons ^ BUTSTATMASK); 314 buffer[1] = dx; 315 buffer[2] = dy; 316 buffer[3] = buffer[4] = 0; 317 (void) b_to_q(buffer, sizeof buffer, &sc->sc_q); 318 319 if (sc->sc_state & LMS_ASLP) { 320 sc->sc_state &= ~LMS_ASLP; 321 wakeup((caddr_t)sc); 322 } 323 selwakeup(&sc->sc_rsel); 324 } 325 326 return -1; 327 } 328 329 int 330 lmsselect(dev, rw, p) 331 dev_t dev; 332 int rw; 333 struct proc *p; 334 { 335 struct lms_softc *sc = lmscd.cd_devs[LMSUNIT(dev)]; 336 int s; 337 int ret; 338 339 if (rw == FWRITE) 340 return 0; 341 342 s = spltty(); 343 if (!sc->sc_q.c_cc) { 344 selrecord(p, &sc->sc_rsel); 345 ret = 0; 346 } else 347 ret = 1; 348 splx(s); 349 350 return ret; 351 } 352