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