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