opms.c revision 1.9 1 /* $NetBSD: opms.c,v 1.9 2003/07/15 00:04:45 lukem Exp $ */
2 /* $OpenBSD: pccons.c,v 1.22 1999/01/30 22:39:37 imp Exp $ */
3 /* NetBSD: pms.c,v 1.21 1995/04/18 02:25:18 mycroft Exp */
4
5 /*-
6 * Copyright (c) 1993, 1994, 1995 Charles M. Hannum. All rights reserved.
7 * Copyright (c) 1990 The Regents of the University of California.
8 * All rights reserved.
9 *
10 * This code is derived from software contributed to Berkeley by
11 * William Jolitz and Don Ahn.
12 *
13 * Copyright (c) 1994 Charles M. Hannum.
14 * Copyright (c) 1992, 1993 Erik Forsberg.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 * 3. All advertising materials mentioning features or use of this software
25 * must display the following acknowledgement:
26 * This product includes software developed by the University of
27 * California, Berkeley and its contributors.
28 * 4. Neither the name of the University nor the names of its contributors
29 * may be used to endorse or promote products derived from this software
30 * without specific prior written permission.
31 *
32 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * SUCH DAMAGE.
43 *
44 * @(#)pccons.c 5.11 (Berkeley) 5/21/91
45 */
46
47 #include <sys/cdefs.h>
48 __KERNEL_RCSID(0, "$NetBSD: opms.c,v 1.9 2003/07/15 00:04:45 lukem Exp $");
49
50 #include <sys/param.h>
51 #include <sys/systm.h>
52 #include <sys/vnode.h>
53 #include <sys/poll.h>
54 #include <sys/tty.h>
55 #include <sys/device.h>
56 #include <sys/proc.h>
57 #include <sys/conf.h>
58
59 #include <machine/bus.h>
60 #include <machine/kbdreg.h>
61 #include <machine/mouse.h>
62
63 #include <arc/dev/pcconsvar.h>
64 #include <arc/dev/opmsvar.h>
65
66 #define PMSUNIT(dev) (minor(dev))
67
68 /* status bits */
69 #define PMS_OBUF_FULL 0x01
70 #define PMS_IBUF_FULL 0x02
71
72 /* controller commands */
73 #define PMS_INT_ENABLE 0x47 /* enable controller interrupts */
74 #define PMS_INT_DISABLE 0x65 /* disable controller interrupts */
75 #define PMS_AUX_ENABLE 0xa7 /* enable auxiliary port */
76 #define PMS_AUX_DISABLE 0xa8 /* disable auxiliary port */
77 #define PMS_MAGIC_1 0xa9 /* XXX */
78
79 #define PMS_8042_CMD 0x65
80
81 /* mouse commands */
82 #define PMS_SET_SCALE11 0xe6 /* set scaling 1:1 */
83 #define PMS_SET_SCALE21 0xe7 /* set scaling 2:1 */
84 #define PMS_SET_RES 0xe8 /* set resolution */
85 #define PMS_GET_SCALE 0xe9 /* get scaling factor */
86 #define PMS_SET_STREAM 0xea /* set streaming mode */
87 #define PMS_SET_SAMPLE 0xf3 /* set sampling rate */
88 #define PMS_DEV_ENABLE 0xf4 /* mouse on */
89 #define PMS_DEV_DISABLE 0xf5 /* mouse off */
90 #define PMS_RESET 0xff /* reset */
91
92 #define PMS_CHUNK 128 /* chunk size for read */
93 #define PMS_BSIZE 1020 /* buffer size */
94
95 #define FLUSHQ(q) { if((q)->c_cc) ndflush(q, (q)->c_cc); }
96
97 extern struct cfdriver opms_cd;
98
99 dev_type_open(opmsopen);
100 dev_type_close(opmsclose);
101 dev_type_read(opmsread);
102 dev_type_ioctl(opmsioctl);
103 dev_type_poll(opmspoll);
104 dev_type_kqfilter(opmskqfilter);
105
106 const struct cdevsw opms_cdevsw = {
107 opmsopen, opmsclose, opmsread, nowrite, opmsioctl,
108 nostop, notty, opmspoll, nommap, opmskqfilter,
109 };
110
111 static __inline void pms_dev_cmd __P((u_char));
112 static __inline void pms_aux_cmd __P((u_char));
113 static __inline void pms_pit_cmd __P((u_char));
114
115 static __inline void
116 pms_dev_cmd(value)
117 u_char value;
118 {
119 kbd_flush_input();
120 kbd_cmd_write_1(0xd4);
121 kbd_flush_input();
122 kbd_data_write_1(value);
123 }
124
125 static __inline void
126 pms_aux_cmd(value)
127 u_char value;
128 {
129 kbd_flush_input();
130 kbd_cmd_write_1(value);
131 }
132
133 static __inline void
134 pms_pit_cmd(value)
135 u_char value;
136 {
137 kbd_flush_input();
138 kbd_cmd_write_1(0x60);
139 kbd_flush_input();
140 kbd_data_write_1(value);
141 }
142
143 int opms_common_match(kbd_iot, config)
144 bus_space_tag_t kbd_iot;
145 struct pccons_config *config;
146 {
147 u_char x;
148
149 kbd_context_init(kbd_iot, config);
150
151 pms_dev_cmd(KBC_RESET);
152 pms_aux_cmd(PMS_MAGIC_1);
153 delay(10000);
154 x = kbd_data_read_1();
155 pms_pit_cmd(PMS_INT_DISABLE);
156 if (x & 0x04)
157 return 0;
158
159 return 1;
160 }
161
162 void
163 opms_common_attach(sc, opms_iot, config)
164 struct opms_softc *sc;
165 bus_space_tag_t opms_iot;
166 struct pccons_config *config;
167 {
168 kbd_context_init(opms_iot, config);
169
170 /* Other initialization was done by opmsprobe. */
171 sc->sc_state = 0;
172 }
173
174 int
175 opmsopen(dev, flag, mode, p)
176 dev_t dev;
177 int flag, mode;
178 struct proc *p;
179 {
180 int unit = PMSUNIT(dev);
181 struct opms_softc *sc;
182
183 if (unit >= opms_cd.cd_ndevs)
184 return ENXIO;
185 sc = opms_cd.cd_devs[unit];
186 if (!sc)
187 return ENXIO;
188
189 if (sc->sc_state & PMS_OPEN)
190 return EBUSY;
191
192 if (clalloc(&sc->sc_q, PMS_BSIZE, 0) == -1)
193 return ENOMEM;
194
195 sc->sc_state |= PMS_OPEN;
196 sc->sc_status = 0;
197 sc->sc_x = sc->sc_y = 0;
198
199 /* Enable interrupts. */
200 pms_dev_cmd(PMS_DEV_ENABLE);
201 pms_aux_cmd(PMS_AUX_ENABLE);
202 pms_dev_cmd(PMS_SET_RES);
203 pms_dev_cmd(3); /* 8 counts/mm */
204 pms_dev_cmd(PMS_SET_SCALE21);
205 #if 0
206 pms_dev_cmd(PMS_SET_SAMPLE);
207 pms_dev_cmd(100); /* 100 samples/sec */
208 pms_dev_cmd(PMS_SET_STREAM);
209 #endif
210 pms_pit_cmd(PMS_INT_ENABLE);
211
212 return 0;
213 }
214
215 int
216 opmsclose(dev, flag, mode, p)
217 dev_t dev;
218 int flag, mode;
219 struct proc *p;
220 {
221 struct opms_softc *sc = opms_cd.cd_devs[PMSUNIT(dev)];
222
223 /* Disable interrupts. */
224 pms_dev_cmd(PMS_DEV_DISABLE);
225 pms_pit_cmd(PMS_INT_DISABLE);
226 pms_aux_cmd(PMS_AUX_DISABLE);
227
228 sc->sc_state &= ~PMS_OPEN;
229
230 clfree(&sc->sc_q);
231
232 return 0;
233 }
234
235 int
236 opmsread(dev, uio, flag)
237 dev_t dev;
238 struct uio *uio;
239 int flag;
240 {
241 struct opms_softc *sc = opms_cd.cd_devs[PMSUNIT(dev)];
242 int s;
243 int error = 0;
244 size_t length;
245 u_char buffer[PMS_CHUNK];
246
247 /* Block until mouse activity occurred. */
248
249 s = spltty();
250 while (sc->sc_q.c_cc == 0) {
251 if (flag & IO_NDELAY) {
252 splx(s);
253 return EWOULDBLOCK;
254 }
255 sc->sc_state |= PMS_ASLP;
256 error = tsleep((caddr_t)sc, PZERO | PCATCH, "pmsrea", 0);
257 if (error) {
258 sc->sc_state &= ~PMS_ASLP;
259 splx(s);
260 return error;
261 }
262 }
263 splx(s);
264
265 /* Transfer as many chunks as possible. */
266
267 while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) {
268 length = min(sc->sc_q.c_cc, uio->uio_resid);
269 if (length > sizeof(buffer))
270 length = sizeof(buffer);
271
272 /* Remove a small chunk from the input queue. */
273 (void) q_to_b(&sc->sc_q, buffer, length);
274
275 /* Copy the data to the user process. */
276 error = uiomove(buffer, length, uio);
277 if (error)
278 break;
279 }
280
281 return error;
282 }
283
284 int
285 opmsioctl(dev, cmd, addr, flag, p)
286 dev_t dev;
287 u_long cmd;
288 caddr_t addr;
289 int flag;
290 struct proc *p;
291 {
292 struct opms_softc *sc = opms_cd.cd_devs[PMSUNIT(dev)];
293 struct mouseinfo info;
294 int s;
295 int error;
296
297 switch (cmd) {
298 case MOUSEIOCREAD:
299 s = spltty();
300
301 info.status = sc->sc_status;
302 if (sc->sc_x || sc->sc_y)
303 info.status |= MOVEMENT;
304
305 if (sc->sc_x > 127)
306 info.xmotion = 127;
307 else if (sc->sc_x < -127)
308 /* Bounding at -127 avoids a bug in XFree86. */
309 info.xmotion = -127;
310 else
311 info.xmotion = sc->sc_x;
312
313 if (sc->sc_y > 127)
314 info.ymotion = 127;
315 else if (sc->sc_y < -127)
316 info.ymotion = -127;
317 else
318 info.ymotion = sc->sc_y;
319
320 /* Reset historical information. */
321 sc->sc_x = sc->sc_y = 0;
322 sc->sc_status &= ~BUTCHNGMASK;
323 ndflush(&sc->sc_q, sc->sc_q.c_cc);
324
325 splx(s);
326 error = copyout(&info, addr, sizeof(struct mouseinfo));
327 break;
328 default:
329 error = EINVAL;
330 break;
331 }
332
333 return error;
334 }
335
336 /* Masks for the first byte of a packet */
337 #define PS2LBUTMASK 0x01
338 #define PS2RBUTMASK 0x02
339 #define PS2MBUTMASK 0x04
340
341 int
342 opmsintr(arg)
343 void *arg;
344 {
345 struct opms_softc *sc = arg;
346 static int state = 0;
347 static u_char buttons;
348 u_char changed;
349 static char dx, dy;
350 u_char buffer[5];
351
352 if ((sc->sc_state & PMS_OPEN) == 0) {
353 /* Interrupts are not expected. Discard the byte. */
354 kbd_flush_input();
355 return 0;
356 }
357
358 switch (state) {
359
360 case 0:
361 buttons = kbd_data_read_1();
362 if ((buttons & 0xc0) == 0)
363 ++state;
364 break;
365
366 case 1:
367 dx = kbd_data_read_1();
368 /* Bounding at -127 avoids a bug in XFree86. */
369 dx = (dx == -128) ? -127 : dx;
370 ++state;
371 break;
372
373 case 2:
374 dy = kbd_data_read_1();
375 dy = (dy == -128) ? -127 : dy;
376 state = 0;
377
378 buttons = ((buttons & PS2LBUTMASK) << 2) |
379 ((buttons & (PS2RBUTMASK | PS2MBUTMASK)) >> 1);
380 changed = ((buttons ^ sc->sc_status) & BUTSTATMASK) << 3;
381 sc->sc_status = buttons | (sc->sc_status & ~BUTSTATMASK) | changed;
382
383 if (dx || dy || changed) {
384 /* Update accumulated movements. */
385 sc->sc_x += dx;
386 sc->sc_y += dy;
387
388 /* Add this event to the queue. */
389 buffer[0] = 0x80 | (buttons & BUTSTATMASK);
390 if(dx < 0)
391 buffer[0] |= 0x10;
392 buffer[1] = dx & 0x7f;
393 if(dy < 0)
394 buffer[0] |= 0x20;
395 buffer[2] = dy & 0x7f;
396 buffer[3] = buffer[4] = 0;
397 (void) b_to_q(buffer, sizeof buffer, &sc->sc_q);
398
399 if (sc->sc_state & PMS_ASLP) {
400 sc->sc_state &= ~PMS_ASLP;
401 wakeup((caddr_t)sc);
402 }
403 selnotify(&sc->sc_rsel, 0);
404 }
405
406 break;
407 }
408 return -1;
409 }
410
411 int
412 opmspoll(dev, events, p)
413 dev_t dev;
414 int events;
415 struct proc *p;
416 {
417 struct opms_softc *sc = opms_cd.cd_devs[PMSUNIT(dev)];
418 int revents = 0;
419 int s = spltty();
420
421 if (events & (POLLIN | POLLRDNORM)) {
422 if (sc->sc_q.c_cc > 0)
423 revents |= events & (POLLIN | POLLRDNORM);
424 else
425 selrecord(p, &sc->sc_rsel);
426 }
427
428 splx(s);
429 return (revents);
430 }
431
432 static void
433 filt_opmsrdetach(struct knote *kn)
434 {
435 struct opms_softc *sc = kn->kn_hook;
436 int s;
437
438 s = spltty();
439 SLIST_REMOVE(&sc->sc_rsel.sel_klist, kn, knote, kn_selnext);
440 splx(s);
441 }
442
443 static int
444 filt_opmsread(struct knote *kn, long hint)
445 {
446 struct opms_softc *sc = kn->kn_hook;
447
448 kn->kn_data = sc->sc_q.c_cc;
449 return (kn->kn_data > 0);
450 }
451
452 static const struct filterops opmsread_filtops =
453 { 1, NULL, filt_opmsrdetach, filt_opmsread };
454
455 int
456 opmskqfilter(dev_t dev, struct knote *kn)
457 {
458 struct opms_softc *sc = opms_cd.cd_devs[PMSUNIT(dev)];
459 struct klist *klist;
460 int s;
461
462 switch (kn->kn_filter) {
463 case EVFILT_READ:
464 klist = &sc->sc_rsel.sel_klist;
465 kn->kn_fop = &opmsread_filtops;
466 break;
467
468 default:
469 return (1);
470 }
471
472 kn->kn_hook = sc;
473
474 s = spltty();
475 SLIST_INSERT_HEAD(klist, kn, kn_selnext);
476 splx(s);
477
478 return (0);
479 }
480