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