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