mms.c revision 1.19 1 /* $NetBSD: mms.c,v 1.19 1995/10/05 22:06:51 mycroft 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 cfdriver mmscd = {
72 NULL, "mms", mmsprobe, mmsattach, DV_TTY, sizeof(struct mms_softc)
73 };
74
75 #define MMSUNIT(dev) (minor(dev))
76
77 int
78 mmsprobe(parent, match, aux)
79 struct device *parent;
80 void *match, *aux;
81 {
82 struct isa_attach_args *ia = aux;
83 int iobase = ia->ia_iobase;
84
85 /* Read identification register to see if present */
86 if (inb(iobase + MMS_IDENT) != 0xde)
87 return 0;
88
89 /* Seems it was there; reset. */
90 outb(iobase + MMS_ADDR, 0x87);
91
92 ia->ia_iosize = MMS_NPORTS;
93 ia->ia_msize = 0;
94 return 1;
95 }
96
97 void
98 mmsattach(parent, self, aux)
99 struct device *parent, *self;
100 void *aux;
101 {
102 struct mms_softc *sc = (void *)self;
103 struct isa_attach_args *ia = aux;
104 int iobase = ia->ia_iobase;
105
106 printf("\n");
107
108 /* Other initialization was done by mmsprobe. */
109 sc->sc_iobase = iobase;
110 sc->sc_state = 0;
111
112 sc->sc_ih = isa_intr_establish(ia->ia_irq, ISA_IST_PULSE, ISA_IPL_TTY,
113 mmsintr, sc);
114 }
115
116 int
117 mmsopen(dev, flag)
118 dev_t dev;
119 int flag;
120 {
121 int unit = MMSUNIT(dev);
122 struct mms_softc *sc;
123
124 if (unit >= mmscd.cd_ndevs)
125 return ENXIO;
126 sc = mmscd.cd_devs[unit];
127 if (!sc)
128 return ENXIO;
129
130 if (sc->sc_state & MMS_OPEN)
131 return EBUSY;
132
133 if (clalloc(&sc->sc_q, MMS_BSIZE, 0) == -1)
134 return ENOMEM;
135
136 sc->sc_state |= MMS_OPEN;
137 sc->sc_status = 0;
138 sc->sc_x = sc->sc_y = 0;
139
140 /* Enable interrupts. */
141 outb(sc->sc_iobase + MMS_ADDR, 0x07);
142 outb(sc->sc_iobase + MMS_DATA, 0x09);
143
144 return 0;
145 }
146
147 int
148 mmsclose(dev, flag)
149 dev_t dev;
150 int flag;
151 {
152 struct mms_softc *sc = mmscd.cd_devs[MMSUNIT(dev)];
153
154 /* Disable interrupts. */
155 outb(sc->sc_iobase + MMS_ADDR, 0x87);
156
157 sc->sc_state &= ~MMS_OPEN;
158
159 clfree(&sc->sc_q);
160
161 return 0;
162 }
163
164 int
165 mmsread(dev, uio, flag)
166 dev_t dev;
167 struct uio *uio;
168 int flag;
169 {
170 struct mms_softc *sc = mmscd.cd_devs[MMSUNIT(dev)];
171 int s;
172 int error;
173 size_t length;
174 u_char buffer[MMS_CHUNK];
175
176 /* Block until mouse activity occured. */
177
178 s = spltty();
179 while (sc->sc_q.c_cc == 0) {
180 if (flag & IO_NDELAY) {
181 splx(s);
182 return EWOULDBLOCK;
183 }
184 sc->sc_state |= MMS_ASLP;
185 if (error = tsleep((caddr_t)sc, PZERO | PCATCH, "mmsrea", 0)) {
186 sc->sc_state &= ~MMS_ASLP;
187 splx(s);
188 return error;
189 }
190 }
191 splx(s);
192
193 /* Transfer as many chunks as possible. */
194
195 while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) {
196 length = min(sc->sc_q.c_cc, uio->uio_resid);
197 if (length > sizeof(buffer))
198 length = sizeof(buffer);
199
200 /* Remove a small chunk from the input queue. */
201 (void) q_to_b(&sc->sc_q, buffer, length);
202
203 /* Copy the data to the user process. */
204 if (error = uiomove(buffer, length, uio))
205 break;
206 }
207
208 return error;
209 }
210
211 int
212 mmsioctl(dev, cmd, addr, flag)
213 dev_t dev;
214 u_long cmd;
215 caddr_t addr;
216 int flag;
217 {
218 struct mms_softc *sc = mmscd.cd_devs[MMSUNIT(dev)];
219 struct mouseinfo info;
220 int s;
221 int error;
222
223 switch (cmd) {
224 case MOUSEIOCREAD:
225 s = spltty();
226
227 info.status = sc->sc_status;
228 if (sc->sc_x || sc->sc_y)
229 info.status |= MOVEMENT;
230
231 if (sc->sc_x > 127)
232 info.xmotion = 127;
233 else if (sc->sc_x < -127)
234 /* Bounding at -127 avoids a bug in XFree86. */
235 info.xmotion = -127;
236 else
237 info.xmotion = sc->sc_x;
238
239 if (sc->sc_y > 127)
240 info.ymotion = 127;
241 else if (sc->sc_y < -127)
242 info.ymotion = -127;
243 else
244 info.ymotion = sc->sc_y;
245
246 /* Reset historical information. */
247 sc->sc_x = sc->sc_y = 0;
248 sc->sc_status &= ~BUTCHNGMASK;
249 ndflush(&sc->sc_q, sc->sc_q.c_cc);
250
251 splx(s);
252 error = copyout(&info, addr, sizeof(struct mouseinfo));
253 break;
254
255 default:
256 error = EINVAL;
257 break;
258 }
259
260 return error;
261 }
262
263 int
264 mmsintr(arg)
265 void *arg;
266 {
267 struct mms_softc *sc = arg;
268 int iobase = sc->sc_iobase;
269 u_char buttons, changed, status;
270 char dx, dy;
271 u_char buffer[5];
272
273 if ((sc->sc_state & MMS_OPEN) == 0)
274 /* Interrupts are not expected. */
275 return 0;
276
277 /* Freeze InPort registers (disabling interrupts). */
278 outb(iobase + MMS_ADDR, 0x07);
279 outb(iobase + MMS_DATA, 0x29);
280
281 outb(iobase + MMS_ADDR, 0x00);
282 status = inb(iobase + MMS_DATA);
283
284 if (status & 0x40) {
285 outb(iobase + MMS_ADDR, 1);
286 dx = inb(iobase + MMS_DATA);
287 dx = (dx == -128) ? -127 : dx;
288 outb(iobase + MMS_ADDR, 2);
289 dy = inb(iobase + MMS_DATA);
290 dy = (dy == -128) ? 127 : -dy;
291 } else
292 dx = dy = 0;
293
294 /* Unfreeze InPort registers (reenabling interrupts). */
295 outb(iobase + MMS_ADDR, 0x07);
296 outb(iobase + MMS_DATA, 0x09);
297
298 buttons = status & BUTSTATMASK;
299 changed = status & BUTCHNGMASK;
300 sc->sc_status = buttons | (sc->sc_status & ~BUTSTATMASK) | changed;
301
302 if (dx || dy || changed) {
303 /* Update accumulated movements. */
304 sc->sc_x += dx;
305 sc->sc_y += dy;
306
307 /* Add this event to the queue. */
308 buffer[0] = 0x80 | (buttons ^ BUTSTATMASK);
309 buffer[1] = dx;
310 buffer[2] = dy;
311 buffer[3] = buffer[4] = 0;
312 (void) b_to_q(buffer, sizeof buffer, &sc->sc_q);
313
314 if (sc->sc_state & MMS_ASLP) {
315 sc->sc_state &= ~MMS_ASLP;
316 wakeup((caddr_t)sc);
317 }
318 selwakeup(&sc->sc_rsel);
319 }
320
321 return -1;
322 }
323
324 int
325 mmsselect(dev, rw, p)
326 dev_t dev;
327 int rw;
328 struct proc *p;
329 {
330 struct mms_softc *sc = mmscd.cd_devs[MMSUNIT(dev)];
331 int s;
332 int ret;
333
334 if (rw == FWRITE)
335 return 0;
336
337 s = spltty();
338 if (!sc->sc_q.c_cc) {
339 selrecord(p, &sc->sc_rsel);
340 ret = 0;
341 } else
342 ret = 1;
343 splx(s);
344
345 return ret;
346 }
347