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