mms.c revision 1.21 1 /* $NetBSD: mms.c,v 1.21 1996/03/17 01:31:16 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 1993, 1994 Charles Hannum.
5 * Copyright (c) 1992, 1993 Erik Forsberg.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17 * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include <sys/param.h>
27 #include <sys/kernel.h>
28 #include <sys/systm.h>
29 #include <sys/buf.h>
30 #include <sys/malloc.h>
31 #include <sys/ioctl.h>
32 #include <sys/tty.h>
33 #include <sys/file.h>
34 #include <sys/select.h>
35 #include <sys/proc.h>
36 #include <sys/vnode.h>
37 #include <sys/device.h>
38
39 #include <machine/cpu.h>
40 #include <machine/pio.h>
41 #include <machine/mouse.h>
42
43 #include <dev/isa/isavar.h>
44
45 #define MMS_ADDR 0 /* offset for register select */
46 #define MMS_DATA 1 /* offset for InPort data */
47 #define MMS_IDENT 2 /* offset for identification register */
48 #define MMS_NPORTS 4
49
50 #define MMS_CHUNK 128 /* chunk size for read */
51 #define MMS_BSIZE 1020 /* buffer size */
52
53 struct mms_softc { /* driver status information */
54 struct device sc_dev;
55 void *sc_ih;
56
57 struct clist sc_q;
58 struct selinfo sc_rsel;
59 int sc_iobase; /* I/O port base */
60 u_char sc_state; /* mouse driver state */
61 #define MMS_OPEN 0x01 /* device is open */
62 #define MMS_ASLP 0x02 /* waiting for mouse data */
63 u_char sc_status; /* mouse button status */
64 int sc_x, sc_y; /* accumulated motion in the X,Y axis */
65 };
66
67 int mmsprobe __P((struct device *, void *, void *));
68 void mmsattach __P((struct device *, struct device *, void *));
69 int mmsintr __P((void *));
70
71 struct cfattach mms_ca = {
72 sizeof(struct mms_softc), mmsprobe, mmsattach
73 };
74
75 struct cfdriver mms_cd = {
76 NULL, "mms", DV_TTY
77 };
78
79 #define MMSUNIT(dev) (minor(dev))
80
81 int
82 mmsprobe(parent, match, aux)
83 struct device *parent;
84 void *match, *aux;
85 {
86 struct isa_attach_args *ia = aux;
87 int iobase = ia->ia_iobase;
88
89 /* Read identification register to see if present */
90 if (inb(iobase + MMS_IDENT) != 0xde)
91 return 0;
92
93 /* Seems it was there; reset. */
94 outb(iobase + MMS_ADDR, 0x87);
95
96 ia->ia_iosize = MMS_NPORTS;
97 ia->ia_msize = 0;
98 return 1;
99 }
100
101 void
102 mmsattach(parent, self, aux)
103 struct device *parent, *self;
104 void *aux;
105 {
106 struct mms_softc *sc = (void *)self;
107 struct isa_attach_args *ia = aux;
108 int iobase = ia->ia_iobase;
109
110 printf("\n");
111
112 /* Other initialization was done by mmsprobe. */
113 sc->sc_iobase = iobase;
114 sc->sc_state = 0;
115
116 sc->sc_ih = isa_intr_establish(ia->ia_irq, IST_PULSE, IPL_TTY, mmsintr,
117 sc);
118 }
119
120 int
121 mmsopen(dev, flag)
122 dev_t dev;
123 int flag;
124 {
125 int unit = MMSUNIT(dev);
126 struct mms_softc *sc;
127
128 if (unit >= mms_cd.cd_ndevs)
129 return ENXIO;
130 sc = mms_cd.cd_devs[unit];
131 if (!sc)
132 return ENXIO;
133
134 if (sc->sc_state & MMS_OPEN)
135 return EBUSY;
136
137 if (clalloc(&sc->sc_q, MMS_BSIZE, 0) == -1)
138 return ENOMEM;
139
140 sc->sc_state |= MMS_OPEN;
141 sc->sc_status = 0;
142 sc->sc_x = sc->sc_y = 0;
143
144 /* Enable interrupts. */
145 outb(sc->sc_iobase + MMS_ADDR, 0x07);
146 outb(sc->sc_iobase + MMS_DATA, 0x09);
147
148 return 0;
149 }
150
151 int
152 mmsclose(dev, flag)
153 dev_t dev;
154 int flag;
155 {
156 struct mms_softc *sc = mms_cd.cd_devs[MMSUNIT(dev)];
157
158 /* Disable interrupts. */
159 outb(sc->sc_iobase + MMS_ADDR, 0x87);
160
161 sc->sc_state &= ~MMS_OPEN;
162
163 clfree(&sc->sc_q);
164
165 return 0;
166 }
167
168 int
169 mmsread(dev, uio, flag)
170 dev_t dev;
171 struct uio *uio;
172 int flag;
173 {
174 struct mms_softc *sc = mms_cd.cd_devs[MMSUNIT(dev)];
175 int s;
176 int error;
177 size_t length;
178 u_char buffer[MMS_CHUNK];
179
180 /* Block until mouse activity occured. */
181
182 s = spltty();
183 while (sc->sc_q.c_cc == 0) {
184 if (flag & IO_NDELAY) {
185 splx(s);
186 return EWOULDBLOCK;
187 }
188 sc->sc_state |= MMS_ASLP;
189 if (error = tsleep((caddr_t)sc, PZERO | PCATCH, "mmsrea", 0)) {
190 sc->sc_state &= ~MMS_ASLP;
191 splx(s);
192 return error;
193 }
194 }
195 splx(s);
196
197 /* Transfer as many chunks as possible. */
198
199 while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) {
200 length = min(sc->sc_q.c_cc, uio->uio_resid);
201 if (length > sizeof(buffer))
202 length = sizeof(buffer);
203
204 /* Remove a small chunk from the input queue. */
205 (void) q_to_b(&sc->sc_q, buffer, length);
206
207 /* Copy the data to the user process. */
208 if (error = uiomove(buffer, length, uio))
209 break;
210 }
211
212 return error;
213 }
214
215 int
216 mmsioctl(dev, cmd, addr, flag)
217 dev_t dev;
218 u_long cmd;
219 caddr_t addr;
220 int flag;
221 {
222 struct mms_softc *sc = mms_cd.cd_devs[MMSUNIT(dev)];
223 struct mouseinfo info;
224 int s;
225 int error;
226
227 switch (cmd) {
228 case MOUSEIOCREAD:
229 s = spltty();
230
231 info.status = sc->sc_status;
232 if (sc->sc_x || sc->sc_y)
233 info.status |= MOVEMENT;
234
235 if (sc->sc_x > 127)
236 info.xmotion = 127;
237 else if (sc->sc_x < -127)
238 /* Bounding at -127 avoids a bug in XFree86. */
239 info.xmotion = -127;
240 else
241 info.xmotion = sc->sc_x;
242
243 if (sc->sc_y > 127)
244 info.ymotion = 127;
245 else if (sc->sc_y < -127)
246 info.ymotion = -127;
247 else
248 info.ymotion = sc->sc_y;
249
250 /* Reset historical information. */
251 sc->sc_x = sc->sc_y = 0;
252 sc->sc_status &= ~BUTCHNGMASK;
253 ndflush(&sc->sc_q, sc->sc_q.c_cc);
254
255 splx(s);
256 error = copyout(&info, addr, sizeof(struct mouseinfo));
257 break;
258
259 default:
260 error = EINVAL;
261 break;
262 }
263
264 return error;
265 }
266
267 int
268 mmsintr(arg)
269 void *arg;
270 {
271 struct mms_softc *sc = arg;
272 int iobase = sc->sc_iobase;
273 u_char buttons, changed, status;
274 char dx, dy;
275 u_char buffer[5];
276
277 if ((sc->sc_state & MMS_OPEN) == 0)
278 /* Interrupts are not expected. */
279 return 0;
280
281 /* Freeze InPort registers (disabling interrupts). */
282 outb(iobase + MMS_ADDR, 0x07);
283 outb(iobase + MMS_DATA, 0x29);
284
285 outb(iobase + MMS_ADDR, 0x00);
286 status = inb(iobase + MMS_DATA);
287
288 if (status & 0x40) {
289 outb(iobase + MMS_ADDR, 1);
290 dx = inb(iobase + MMS_DATA);
291 dx = (dx == -128) ? -127 : dx;
292 outb(iobase + MMS_ADDR, 2);
293 dy = inb(iobase + MMS_DATA);
294 dy = (dy == -128) ? 127 : -dy;
295 } else
296 dx = dy = 0;
297
298 /* Unfreeze InPort registers (reenabling interrupts). */
299 outb(iobase + MMS_ADDR, 0x07);
300 outb(iobase + MMS_DATA, 0x09);
301
302 buttons = status & BUTSTATMASK;
303 changed = status & BUTCHNGMASK;
304 sc->sc_status = buttons | (sc->sc_status & ~BUTSTATMASK) | changed;
305
306 if (dx || dy || changed) {
307 /* Update accumulated movements. */
308 sc->sc_x += dx;
309 sc->sc_y += dy;
310
311 /* Add this event to the queue. */
312 buffer[0] = 0x80 | (buttons ^ BUTSTATMASK);
313 buffer[1] = dx;
314 buffer[2] = dy;
315 buffer[3] = buffer[4] = 0;
316 (void) b_to_q(buffer, sizeof buffer, &sc->sc_q);
317
318 if (sc->sc_state & MMS_ASLP) {
319 sc->sc_state &= ~MMS_ASLP;
320 wakeup((caddr_t)sc);
321 }
322 selwakeup(&sc->sc_rsel);
323 }
324
325 return -1;
326 }
327
328 int
329 mmsselect(dev, rw, p)
330 dev_t dev;
331 int rw;
332 struct proc *p;
333 {
334 struct mms_softc *sc = mms_cd.cd_devs[MMSUNIT(dev)];
335 int s;
336 int ret;
337
338 if (rw == FWRITE)
339 return 0;
340
341 s = spltty();
342 if (!sc->sc_q.c_cc) {
343 selrecord(p, &sc->sc_rsel);
344 ret = 0;
345 } else
346 ret = 1;
347 splx(s);
348
349 return ret;
350 }
351