mms.c revision 1.6.2.4 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.4 1993/10/01 00:38:51 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/isa.h"
42 #include "i386/isa/icu.h"
43 #include "i386/include/mouse.h"
44 #include "i386/include/pio.h"
45
46 #define MMS_ADDR 0 /* Offset for register select */
47 #define MMS_DATA 1 /* Offset for InPort data */
48 #define MMS_IDENT 2 /* Offset for identification register */
49 #define MMS_NPORTS 4
50
51 #define MMS_CHUNK 128 /* chunk size for read */
52 #define MMS_BSIZE 1024 /* buffer size */
53
54 struct mms_softc { /* Driver status information */
55 struct device sc_dev;
56 struct isadev sc_id;
57 struct intrhand sc_ih;
58
59 struct ringbuf { /* Input queue */
60 int rb_count, rb_first, rb_last;
61 char rb_data[MMS_BSIZE];
62 } sc_q;
63 struct selinfo sc_rsel;
64 u_short sc_iobase; /* I/O port base */
65 u_char sc_flags; /* Driver flags */
66 #define MMS_NOBLOCK 0x01
67 u_char sc_state; /* Mouse driver state */
68 #define MMS_OPEN 0x01 /* Device is open */
69 #define MMS_ASLP 0x02 /* Waiting for mouse data */
70 u_char sc_status; /* Mouse button status */
71 int sc_x, sc_y; /* accumulated motion in the X,Y axis */
72 };
73
74 static int mmsprobe __P((struct device *, struct cfdata *, void *));
75 static void mmsforceintr __P((void *));
76 static void mmsattach __P((struct device *, struct device *, void *));
77 static int mmsintr __P((void *));
78
79 struct cfdriver mmscd =
80 { NULL, "mms", mmsprobe, mmsattach, sizeof (struct mms_softc) };
81
82 #define MMSUNIT(dev) (minor(dev) >> 1)
83 #define MMSFLAGS(dev) (minor(dev) & 0x01)
84
85 static int
86 mmsprobe(parent, cf, aux)
87 struct device *parent;
88 struct cfdata *cf;
89 void *aux;
90 {
91 struct isa_attach_args *ia = aux;
92 u_short iobase = ia->ia_iobase;
93
94 if (iobase == IOBASEUNK)
95 return 0;
96
97 /* Read identification register to see if present */
98 if (inb(iobase + MMS_IDENT) != 0xde)
99 return 0;
100
101 /* Seems it was there; reset */
102 outb(iobase + MMS_ADDR, 0x87);
103
104 if (ia->ia_irq == IRQUNK) {
105 ia->ia_irq = isa_discoverintr(mmsforceintr, aux);
106 if (ia->ia_irq == IRQNONE)
107 return 0;
108 }
109
110 /* reset again to disable interrupts */
111 outb(iobase + MMS_ADDR, 0x87);
112
113 ia->ia_iosize = MMS_NPORTS;
114 ia->ia_drq = DRQUNK;
115 ia->ia_msize = 0;
116 return 1;
117 }
118
119 static void
120 mmsforceintr(aux)
121 void *aux;
122 {
123 struct isa_attach_args *ia = aux;
124 u_short iobase = ia->ia_iobase;
125
126 /* enable interrupts; expect to get one in 1/60 second */
127 outb(iobase + MMS_ADDR, 0x07);
128 outb(iobase + MMS_DATA, 0x09);
129 }
130
131 static void
132 mmsattach(parent, self, aux)
133 struct device *parent, *self;
134 void *aux;
135 {
136 struct mms_softc *sc = (struct mms_softc *)self;
137 struct isa_attach_args *ia = aux;
138 u_short iobase = ia->ia_iobase;
139
140 /* other initialization done by mmsprobe */
141 sc->sc_iobase = iobase;
142 sc->sc_state = 0;
143
144 printf(": Microsoft mouse\n");
145 isa_establish(&sc->sc_id, &sc->sc_dev);
146
147 sc->sc_ih.ih_fun = mmsintr;
148 sc->sc_ih.ih_arg = sc;
149 intr_establish(ia->ia_irq, &sc->sc_ih, DV_TTY);
150 }
151
152 int
153 mmsopen(dev, flag)
154 dev_t dev;
155 int flag;
156 {
157 int unit = MMSUNIT(dev);
158 struct mms_softc *sc;
159 u_short iobase;
160
161 if (unit >= mmscd.cd_ndevs)
162 return ENXIO;
163 sc = mmscd.cd_devs[unit];
164 if (!sc)
165 return ENXIO;
166
167 if (sc->sc_state & MMS_OPEN)
168 return EBUSY;
169
170 sc->sc_state |= MMS_OPEN;
171 sc->sc_status = 0;
172 sc->sc_x = sc->sc_y = 0;
173 sc->sc_q.rb_count = sc->sc_q.rb_first = sc->sc_q.rb_last = 0;
174
175 /* enable interrupts */
176 iobase = sc->sc_iobase;
177 outb(iobase + MMS_ADDR, 0x07);
178 outb(iobase + MMS_DATA, 0x09);
179
180 return 0;
181 }
182
183 int
184 mmsclose(dev, flag)
185 dev_t dev;
186 int flag;
187 {
188 int unit = MMSUNIT(dev);
189 struct mms_softc *sc = mmscd.cd_devs[unit];
190
191 /* disable interrupts */
192 outb(sc->sc_iobase + MMS_ADDR, 0x87);
193
194 sc->sc_state &= ~MMS_OPEN;
195
196 return 0;
197 }
198
199 int
200 mmsread(dev, uio, flag)
201 dev_t dev;
202 struct uio *uio;
203 int flag;
204 {
205 int unit = MMSUNIT(dev);
206 struct mms_softc *sc = mmscd.cd_devs[unit];
207 int s;
208 int error;
209 size_t length;
210 u_char buffer[MMS_CHUNK];
211
212 /* Block until mouse activity occured */
213
214 s = spltty();
215 while (sc->sc_q.rb_count == 0) {
216 if (sc->sc_flags & MMS_NOBLOCK) {
217 splx(s);
218 return EWOULDBLOCK;
219 }
220 sc->sc_state |= MMS_ASLP;
221 if (error = tsleep((caddr_t)sc, PZERO | PCATCH, "mmsrea", 0)) {
222 sc->sc_state &= ~MMS_ASLP;
223 splx(s);
224 return error;
225 }
226 }
227
228 /* Transfer as many chunks as possible */
229
230 while (sc->sc_q.rb_count > 0 && uio->uio_resid > 0) {
231 length = min(sc->sc_q.rb_count, uio->uio_resid);
232 if (length > sizeof(buffer))
233 length = sizeof(buffer);
234
235 /* Remove a small chunk from input queue */
236
237 if (sc->sc_q.rb_first + length >= MMS_BSIZE) {
238 size_t left = MMS_BSIZE - sc->sc_q.rb_first;
239 bcopy(&sc->sc_q.rb_data[sc->sc_q.rb_first], buffer,
240 left);
241 bcopy(sc->sc_q.rb_data, &buffer[left], length - left);
242 } else
243 bcopy(&sc->sc_q.rb_data[sc->sc_q.rb_first], buffer,
244 length);
245
246 sc->sc_q.rb_first = (sc->sc_q.rb_first + length) % MMS_BSIZE;
247 sc->sc_q.rb_count -= length;
248
249 /* Copy data to user process */
250
251 if (error = uiomove(buffer, length, uio))
252 break;
253 }
254
255 /* reset counters */
256 sc->sc_x = sc->sc_y = 0;
257
258 splx(s);
259 return error;
260 }
261
262 int
263 mmsioctl(dev, cmd, addr, flag)
264 dev_t dev;
265 int cmd;
266 caddr_t addr;
267 int flag;
268 {
269 int unit = MMSUNIT(dev);
270 struct mms_softc *sc = mmscd.cd_devs[unit];
271 struct mouseinfo info;
272 int s;
273 int error;
274
275 switch (cmd) {
276 case MOUSEIOCREAD:
277 s = spltty();
278
279 info.status = sc->sc_status;
280 if (sc->sc_x || sc->sc_y)
281 info.status |= MOVEMENT;
282
283 if (sc->sc_x > 127)
284 info.xmotion = 127;
285 else if (sc->sc_x < -127)
286 /* bounding at -127 avoids a bug in XFree86 */
287 info.xmotion = -127;
288 else
289 info.xmotion = sc->sc_x;
290
291 if (sc->sc_y > 127)
292 info.ymotion = 127;
293 else if (sc->sc_y < -127)
294 info.ymotion = -127;
295 else
296 info.ymotion = sc->sc_y;
297
298 sc->sc_x = 0;
299 sc->sc_y = 0;
300 sc->sc_status &= ~BUTCHNGMASK;
301
302 splx(s);
303 error = copyout(&info, addr, sizeof(struct mouseinfo));
304 break;
305
306 default:
307 error = EINVAL;
308 break;
309 }
310
311 return error;
312 }
313
314 int
315 mmsintr(arg)
316 void *arg;
317 {
318 struct mms_softc *sc = (struct mms_softc *)arg;
319 u_short iobase = sc->sc_iobase;
320 u_char buttons, changed, status;
321 char dx, dy;
322
323 if ((sc->sc_state & MMS_OPEN) == 0)
324 /* interrupts not expected */
325 return 0;
326
327 /* Freeze InPort registers (disabling interrupts) */
328 outb(iobase + MMS_ADDR, 0x07);
329 outb(iobase + MMS_DATA, 0x29);
330
331 outb(iobase + MMS_ADDR, 0);
332 status = inb(iobase + MMS_DATA);
333
334 if (status & 0x40) {
335 outb(iobase + MMS_ADDR, 1);
336 dx = inb(iobase + MMS_DATA);
337 dx = (dx == -128) ? -127 : dx;
338 outb(iobase + MMS_ADDR, 2);
339 dy = inb(iobase + MMS_DATA);
340 dy = (dy == -128) ? 127 : -dy;
341 } else
342 dx = dy = 0;
343
344 /* Unfreeze InPort Registers (re-enables interrupts) */
345 outb(iobase + MMS_ADDR, 0x07);
346 outb(iobase + MMS_DATA, 0x09);
347
348 buttons = status & BUTSTATMASK;
349 changed = status & BUTCHNGMASK;
350 sc->sc_status = buttons | (sc->sc_status & ~BUTSTATMASK) | changed;
351
352 if (dx || dy || changed) {
353 int last = sc->sc_q.rb_last;
354 char *cp = &sc->sc_q.rb_data[last];
355 int count = sc->sc_q.rb_count;
356
357 /* Update accumulated movements */
358 sc->sc_x += dx;
359 sc->sc_y += dy;
360
361 if ((count += 5) > MMS_BSIZE)
362 return 1;
363 sc->sc_q.rb_count = count;
364
365 #define next() \
366 if (++last >= MMS_BSIZE) { \
367 last = 0; \
368 cp = sc->sc_q.rb_data; \
369 } else \
370 cp++;
371 *cp = 0x80 | (~status & BUTSTATMASK);
372 next();
373 *cp = dx;
374 next();
375 *cp = dy;
376 next();
377 *cp = 0;
378 next();
379 *cp = 0;
380 next();
381 sc->sc_q.rb_last = last;
382
383 if (sc->sc_state & MMS_ASLP) {
384 sc->sc_state &= ~MMS_ASLP;
385 wakeup((caddr_t)sc);
386 }
387 selwakeup(&sc->sc_rsel);
388 }
389
390 return 1;
391 }
392
393 int
394 mmsselect(dev, rw, p)
395 dev_t dev;
396 int rw;
397 struct proc *p;
398 {
399 int unit = MMSUNIT(dev);
400 struct mms_softc *sc = mmscd.cd_devs[unit];
401 int s;
402 int ret;
403
404 if (rw == FWRITE)
405 return 0;
406
407 s = spltty();
408 if (sc->sc_q.rb_count)
409 ret = 1;
410 else {
411 selrecord(p, &sc->sc_rsel);
412 ret = 0;
413 }
414 splx(s);
415
416 return ret;
417 }
418