lms.c revision 1.9 1 /*-
2 * Copyright (c) 1993, 1994 Charles Hannum.
3 * Copyright (c) 1992, 1993 Erik Forsberg.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
13 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
15 * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
16 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
17 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
18 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
19 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
20 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22 *
23 * $Id: lms.c,v 1.9 1994/03/06 17:19:10 mycroft Exp $
24 */
25
26 #include "lms.h"
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 #include <sys/select.h>
37 #include <sys/proc.h>
38 #include <sys/vnode.h>
39 #include <sys/device.h>
40
41 #include <machine/cpu.h>
42 #include <machine/pio.h>
43 #include <machine/mouse.h>
44
45 #include <i386/isa/isa_device.h>
46
47 #define LMS_DATA 0 /* offset for data port, read-only */
48 #define LMS_SIGN 1 /* offset for signature port, read-write */
49 #define LMS_INTR 2 /* offset for interrupt port, read-only */
50 #define LMS_CNTRL 2 /* offset for control port, write-only */
51 #define LMS_CONFIG 3 /* for configuration port, read-write */
52 #define LMS_NPORTS 4
53
54 #define LMS_CHUNK 128 /* chunk size for read */
55 #define LMS_BSIZE 1020 /* buffer size */
56
57 struct lms_softc { /* driver status information */
58 struct device sc_dev;
59
60 struct clist sc_q;
61 struct selinfo sc_rsel;
62 u_short sc_iobase; /* I/O port base */
63 u_char sc_state; /* mouse driver state */
64 #define LMS_OPEN 0x01 /* device is open */
65 #define LMS_ASLP 0x02 /* waiting for mouse data */
66 u_char sc_status; /* mouse button status */
67 int sc_x, sc_y; /* accumulated motion in the X,Y axis */
68 } lms_softc[NLMS];
69
70 int lmsprobe __P((struct isa_device *));
71 int lmsattach __P((struct isa_device *));
72 int lmsintr __P((int));
73
74 struct isa_driver lmsdriver = { lmsprobe, lmsattach, "lms" };
75
76 #define LMSUNIT(dev) (minor(dev))
77
78 int
79 lmsprobe(isa_dev)
80 struct isa_device *isa_dev;
81 {
82 u_short iobase = isa_dev->id_iobase;
83
84 /* Configure and check for port present. */
85 outb(iobase + LMS_CONFIG, 0x91);
86 delay(10);
87 outb(iobase + LMS_SIGN, 0x0c);
88 delay(10);
89 if (inb(iobase + LMS_SIGN) != 0x0c)
90 return 0;
91 outb(iobase + LMS_SIGN, 0x50);
92 delay(10);
93 if (inb(iobase + LMS_SIGN) != 0x50)
94 return 0;
95
96 /* Disable interrupts. */
97 outb(iobase + LMS_CNTRL, 0x10);
98
99 return LMS_NPORTS;
100 }
101
102 int
103 lmsattach(isa_dev)
104 struct isa_device *isa_dev;
105 {
106 struct lms_softc *sc = &lms_softc[isa_dev->id_unit];
107 u_short iobase = isa_dev->id_iobase;
108
109 /* Other initialization was done by lmsprobe. */
110 sc->sc_iobase = iobase;
111 sc->sc_state = 0;
112
113 /* XXX HACK */
114 sprintf(sc->sc_dev.dv_xname, "%s%d", lmsdriver.name, isa_dev->id_unit);
115 sc->sc_dev.dv_unit = isa_dev->id_unit;
116 }
117
118 int
119 lmsopen(dev, flag)
120 dev_t dev;
121 int flag;
122 {
123 int unit = LMSUNIT(dev);
124 struct lms_softc *sc;
125
126 if (unit >= NLMS)
127 return ENXIO;
128 sc = &lms_softc[unit];
129 if (!sc->sc_iobase)
130 return ENXIO;
131
132 if (sc->sc_state & LMS_OPEN)
133 return EBUSY;
134
135 if (clalloc(&sc->sc_q, LMS_BSIZE, 0) == -1)
136 return ENOMEM;
137
138 sc->sc_state |= LMS_OPEN;
139 sc->sc_status = 0;
140 sc->sc_x = sc->sc_y = 0;
141
142 /* Enable interrupts. */
143 outb(sc->sc_iobase + LMS_CNTRL, 0);
144
145 return 0;
146 }
147
148 int
149 lmsclose(dev, flag)
150 dev_t dev;
151 int flag;
152 {
153 struct lms_softc *sc = &lms_softc[LMSUNIT(dev)];
154
155 /* Disable interrupts. */
156 outb(sc->sc_iobase + LMS_CNTRL, 0x10);
157
158 sc->sc_state &= ~LMS_OPEN;
159
160 clfree(&sc->sc_q);
161
162 return 0;
163 }
164
165 int
166 lmsread(dev, uio, flag)
167 dev_t dev;
168 struct uio *uio;
169 int flag;
170 {
171 struct lms_softc *sc = &lms_softc[LMSUNIT(dev)];
172 int s;
173 int error;
174 size_t length;
175 u_char buffer[LMS_CHUNK];
176
177 /* Block until mouse activity occured. */
178
179 s = spltty();
180 while (sc->sc_q.c_cc == 0) {
181 if (flag & IO_NDELAY) {
182 splx(s);
183 return EWOULDBLOCK;
184 }
185 sc->sc_state |= LMS_ASLP;
186 if (error = tsleep((caddr_t)sc, PZERO | PCATCH, "lmsrea", 0)) {
187 sc->sc_state &= ~LMS_ASLP;
188 splx(s);
189 return error;
190 }
191 }
192 splx(s);
193
194 /* Transfer as many chunks as possible. */
195
196 while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) {
197 length = min(sc->sc_q.c_cc, uio->uio_resid);
198 if (length > sizeof(buffer))
199 length = sizeof(buffer);
200
201 /* Remove a small chunk from the input queue. */
202 (void) q_to_b(&sc->sc_q, buffer, length);
203
204 /* Copy the data to the user process. */
205 if (error = uiomove(buffer, length, uio))
206 break;
207 }
208
209 return error;
210 }
211
212 int
213 lmsioctl(dev, cmd, addr, flag)
214 dev_t dev;
215 int cmd;
216 caddr_t addr;
217 int flag;
218 {
219 struct lms_softc *sc = &lms_softc[LMSUNIT(dev)];
220 struct mouseinfo info;
221 int s;
222 int error;
223
224 switch (cmd) {
225 case MOUSEIOCREAD:
226 s = spltty();
227
228 info.status = sc->sc_status;
229 if (sc->sc_x || sc->sc_y)
230 info.status |= MOVEMENT;
231
232 if (sc->sc_x > 127)
233 info.xmotion = 127;
234 else if (sc->sc_x < -127)
235 /* Bounding at -127 avoids a bug in XFree86. */
236 info.xmotion = -127;
237 else
238 info.xmotion = sc->sc_x;
239
240 if (sc->sc_y > 127)
241 info.ymotion = 127;
242 else if (sc->sc_y < -127)
243 info.ymotion = -127;
244 else
245 info.ymotion = sc->sc_y;
246
247 /* Reset historical information. */
248 sc->sc_x = sc->sc_y = 0;
249 sc->sc_status &= ~BUTCHNGMASK;
250 flushq(&sc->sc_q);
251
252 splx(s);
253 error = copyout(&info, addr, sizeof(struct mouseinfo));
254 break;
255
256 default:
257 error = EINVAL;
258 break;
259 }
260
261 return error;
262 }
263
264 int
265 lmsintr(unit)
266 int unit;
267 {
268 struct lms_softc *sc = &lms_softc[unit];
269 u_short iobase = sc->sc_iobase;
270 u_char hi, lo, buttons, changed;
271 char dx, dy;
272 u_char buffer[5];
273
274 if ((sc->sc_state & LMS_OPEN) == 0)
275 /* Interrupts are not expected. */
276 return 0;
277
278 outb(iobase + LMS_CNTRL, 0xab);
279 hi = inb(iobase + LMS_DATA);
280 outb(iobase + LMS_CNTRL, 0x90);
281 lo = inb(iobase + LMS_DATA);
282 dx = ((hi & 0x0f) << 4) | (lo & 0x0f);
283 /* Bounding at -127 avoids a bug in XFree86. */
284 dx = (dx == -128) ? -127 : dx;
285
286 outb(iobase + LMS_CNTRL, 0xf0);
287 hi = inb(iobase + LMS_DATA);
288 outb(iobase + LMS_CNTRL, 0xd0);
289 lo = inb(iobase + LMS_DATA);
290 dy = ((hi & 0x0f) << 4) | (lo & 0x0f);
291 dy = (dy == -128) ? 127 : -dy;
292
293 outb(iobase + LMS_CNTRL, 0);
294
295 buttons = (~hi >> 5) & 0x07;
296 changed = ((buttons ^ sc->sc_status) & 0x07) << 3;
297 sc->sc_status = buttons | (sc->sc_status & ~BUTSTATMASK) | changed;
298
299 if (dx || dy || changed) {
300 /* Update accumulated movements. */
301 sc->sc_x += dx;
302 sc->sc_y += dy;
303
304 /* Add this event to the queue. */
305 buffer[0] = 0x80 | (buttons ^ BUTSTATMASK);
306 buffer[1] = dx;
307 buffer[2] = dy;
308 buffer[3] = buffer[4] = 0;
309 (void) b_to_q(buffer, sizeof buffer, &sc->sc_q);
310
311 if (sc->sc_state & LMS_ASLP) {
312 sc->sc_state &= ~LMS_ASLP;
313 wakeup((caddr_t)sc);
314 }
315 selwakeup(&sc->sc_rsel);
316 }
317
318 return 1;
319 }
320
321 int
322 lmsselect(dev, rw, p)
323 dev_t dev;
324 int rw;
325 struct proc *p;
326 {
327 struct lms_softc *sc = &lms_softc[LMSUNIT(dev)];
328 int s;
329 int ret;
330
331 if (rw == FWRITE)
332 return 0;
333
334 s = spltty();
335 if (!sc->sc_q.c_cc) {
336 selrecord(p, &sc->sc_rsel);
337 ret = 0;
338 } else
339 ret = 1;
340 splx(s);
341
342 return ret;
343 }
344