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