xref: /netbsd/sys/arch/mac68k/dev/aed.c (revision c4a72b64)
1 /*	$NetBSD: aed.c,v 1.16 2002/11/26 19:50:26 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 1994	Bradley A. Grantham
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by Bradley A. Grantham.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include "opt_adb.h"
34 
35 #include <sys/param.h>
36 #include <sys/device.h>
37 #include <sys/fcntl.h>
38 #include <sys/poll.h>
39 #include <sys/select.h>
40 #include <sys/proc.h>
41 #include <sys/signalvar.h>
42 #include <sys/systm.h>
43 #include <sys/conf.h>
44 
45 #include <machine/autoconf.h>
46 #include <machine/cpu.h>
47 #include <machine/keyboard.h>
48 
49 #include <mac68k/mac68k/macrom.h>
50 #include <mac68k/dev/adbvar.h>
51 #include <mac68k/dev/aedvar.h>
52 #include <mac68k/dev/akbdvar.h>
53 
54 /*
55  * Function declarations.
56  */
57 static int	aedmatch __P((struct device *, struct cfdata *, void *));
58 static void	aedattach __P((struct device *, struct device *, void *));
59 static void	aed_emulate_mouse __P((adb_event_t *event));
60 static void	aed_kbdrpt __P((void *kstate));
61 static void	aed_dokeyupdown __P((adb_event_t *event));
62 static void	aed_handoff __P((adb_event_t *event));
63 static void	aed_enqevent __P((adb_event_t *event));
64 
65 /*
66  * Local variables.
67  */
68 static struct aed_softc *aed_sc = NULL;
69 static int aed_options = 0 | AED_MSEMUL;
70 
71 /* Driver definition */
72 CFATTACH_DECL(aed, sizeof(struct aed_softc),
73     aedmatch, aedattach, NULL, NULL);
74 
75 extern struct cfdriver aed_cd;
76 
77 dev_type_open(aedopen);
78 dev_type_close(aedclose);
79 dev_type_read(aedread);
80 dev_type_ioctl(aedioctl);
81 dev_type_poll(aedpoll);
82 dev_type_kqfilter(aedkqfilter);
83 
84 const struct cdevsw aed_cdevsw = {
85 	aedopen, aedclose, aedread, nullwrite, aedioctl,
86 	nostop, notty, aedpoll, nommap, aedkqfilter,
87 };
88 
89 static int
90 aedmatch(parent, cf, aux)
91 	struct device *parent;
92 	struct cfdata *cf;
93 	void *aux;
94 {
95 	struct adb_attach_args *aa_args = (struct adb_attach_args *)aux;
96 	static int aed_matched = 0;
97 
98 	/* Allow only one instance. */
99         if ((aa_args->origaddr == 0) && (!aed_matched)) {
100 		aed_matched = 1;
101                 return (1);
102         } else
103                 return (0);
104 }
105 
106 static void
107 aedattach(parent, self, aux)
108 	struct device *parent, *self;
109 	void   *aux;
110 {
111 	struct adb_attach_args *aa_args = (struct adb_attach_args *)aux;
112 	struct aed_softc *sc = (struct aed_softc *)self;
113 
114 	callout_init(&sc->sc_repeat_ch);
115 
116 	sc->origaddr = aa_args->origaddr;
117 	sc->adbaddr = aa_args->adbaddr;
118 	sc->handler_id = aa_args->handler_id;
119 
120 	sc->sc_evq_tail = 0;
121 	sc->sc_evq_len = 0;
122 
123 	sc->sc_rptdelay = 20;
124 	sc->sc_rptinterval = 6;
125 	sc->sc_repeating = -1;          /* not repeating */
126 
127 	/* Pull in the options flags. */
128 	sc->sc_options = (sc->sc_dev.dv_cfdata->cf_flags | aed_options);
129 
130 	sc->sc_ioproc = NULL;
131 
132 	sc->sc_buttons = 0;
133 
134 	sc->sc_open = 0;
135 
136 	aed_sc = sc;
137 
138 	printf("ADB Event device\n");
139 
140 	return;
141 }
142 
143 /*
144  * Given a keyboard ADB event, record the keycode and call the key
145  * repeat handler, optionally passing the event through the mouse
146  * button emulation handler first.  Pass mouse events directly to
147  * the handoff function.
148  */
149 int
150 aed_input(event)
151         adb_event_t *event;
152 {
153         adb_event_t new_event = *event;
154 	int rv = aed_sc->sc_open;
155 
156 	switch (event->def_addr) {
157 	case ADBADDR_KBD:
158 		if (aed_sc->sc_options & AED_MSEMUL)
159 			aed_emulate_mouse(&new_event);
160 		else
161 			aed_dokeyupdown(&new_event);
162 		break;
163 	case ADBADDR_MS:
164 		new_event.u.m.buttons |= aed_sc->sc_buttons;
165 		aed_handoff(&new_event);
166 		break;
167 	default:                /* God only knows. */
168 #ifdef DIAGNOSTIC
169 		panic("aed: received event from unsupported device!");
170 #endif
171 		rv = 0;
172 		break;
173 	}
174 
175 	return (rv);
176 }
177 
178 /*
179  * Handles mouse button emulation via the keyboard.  If the emulation
180  * modifier key is down, left and right arrows will generate 2nd and
181  * 3rd mouse button events while the 1, 2, and 3 keys will generate
182  * the corresponding mouse button event.
183  */
184 static void
185 aed_emulate_mouse(event)
186 	adb_event_t *event;
187 {
188 	static int emulmodkey_down = 0;
189 	adb_event_t new_event;
190 
191 	if (event->u.k.key == ADBK_KEYDOWN(ADBK_OPTION)) {
192 		emulmodkey_down = 1;
193 	} else if (event->u.k.key == ADBK_KEYUP(ADBK_OPTION)) {
194 		/* key up */
195 		emulmodkey_down = 0;
196 		if (aed_sc->sc_buttons & 0xfe) {
197 			aed_sc->sc_buttons &= 1;
198 			new_event.def_addr = ADBADDR_MS;
199 			new_event.u.m.buttons = aed_sc->sc_buttons;
200 			new_event.u.m.dx = new_event.u.m.dy = 0;
201 			microtime(&new_event.timestamp);
202 			aed_handoff(&new_event);
203 		}
204 	} else if (emulmodkey_down) {
205 		switch(event->u.k.key) {
206 #ifdef ALTXBUTTONS
207 		case ADBK_KEYDOWN(ADBK_1):
208 			aed_sc->sc_buttons |= 1;	/* left down */
209 			new_event.def_addr = ADBADDR_MS;
210 			new_event.u.m.buttons = aed_sc->sc_buttons;
211 			new_event.u.m.dx = new_event.u.m.dy = 0;
212 			microtime(&new_event.timestamp);
213 			aed_handoff(&new_event);
214 			break;
215 		case ADBK_KEYUP(ADBK_1):
216 			aed_sc->sc_buttons &= ~1;	/* left up */
217 			new_event.def_addr = ADBADDR_MS;
218 			new_event.u.m.buttons = aed_sc->sc_buttons;
219 			new_event.u.m.dx = new_event.u.m.dy = 0;
220 			microtime(&new_event.timestamp);
221 			aed_handoff(&new_event);
222 			break;
223 #endif
224 		case ADBK_KEYDOWN(ADBK_LEFT):
225 #ifdef ALTXBUTTONS
226 		case ADBK_KEYDOWN(ADBK_2):
227 #endif
228 			aed_sc->sc_buttons |= 2;	/* middle down */
229 			new_event.def_addr = ADBADDR_MS;
230 			new_event.u.m.buttons = aed_sc->sc_buttons;
231 			new_event.u.m.dx = new_event.u.m.dy = 0;
232 			microtime(&new_event.timestamp);
233 			aed_handoff(&new_event);
234 			break;
235 		case ADBK_KEYUP(ADBK_LEFT):
236 #ifdef ALTXBUTTONS
237 		case ADBK_KEYUP(ADBK_2):
238 #endif
239 			aed_sc->sc_buttons &= ~2;	/* middle up */
240 			new_event.def_addr = ADBADDR_MS;
241 			new_event.u.m.buttons = aed_sc->sc_buttons;
242 			new_event.u.m.dx = new_event.u.m.dy = 0;
243 			microtime(&new_event.timestamp);
244 			aed_handoff(&new_event);
245 			break;
246 		case ADBK_KEYDOWN(ADBK_RIGHT):
247 #ifdef ALTXBUTTONS
248 		case ADBK_KEYDOWN(ADBK_3):
249 #endif
250 			aed_sc->sc_buttons |= 4;	/* right down */
251 			new_event.def_addr = ADBADDR_MS;
252 			new_event.u.m.buttons = aed_sc->sc_buttons;
253 			new_event.u.m.dx = new_event.u.m.dy = 0;
254 			microtime(&new_event.timestamp);
255 			aed_handoff(&new_event);
256 			break;
257 		case ADBK_KEYUP(ADBK_RIGHT):
258 #ifdef ALTXBUTTONS
259 		case ADBK_KEYUP(ADBK_3):
260 #endif
261 			aed_sc->sc_buttons &= ~4;	/* right up */
262 			new_event.def_addr = ADBADDR_MS;
263 			new_event.u.m.buttons = aed_sc->sc_buttons;
264 			new_event.u.m.dx = new_event.u.m.dy = 0;
265 			microtime(&new_event.timestamp);
266 			aed_handoff(&new_event);
267 			break;
268 		case ADBK_KEYUP(ADBK_SHIFT):
269 		case ADBK_KEYDOWN(ADBK_SHIFT):
270 		case ADBK_KEYUP(ADBK_CONTROL):
271 		case ADBK_KEYDOWN(ADBK_CONTROL):
272 		case ADBK_KEYUP(ADBK_FLOWER):
273 		case ADBK_KEYDOWN(ADBK_FLOWER):
274 			/* ctrl, shift, cmd */
275 			aed_dokeyupdown(event);
276 			break;
277 		default:
278 			if (event->u.k.key & 0x80)
279 				/* ignore keyup */
280 				break;
281 
282 			/* key down */
283 			new_event = *event;
284 
285 			/* send option-down */
286 			new_event.u.k.key = ADBK_KEYDOWN(ADBK_OPTION);
287 			new_event.bytes[0] = new_event.u.k.key;
288 			microtime(&new_event.timestamp);
289 			aed_dokeyupdown(&new_event);
290 
291 			/* send key-down */
292 			new_event.u.k.key = event->bytes[0];
293 			new_event.bytes[0] = new_event.u.k.key;
294 			microtime(&new_event.timestamp);
295 			aed_dokeyupdown(&new_event);
296 
297 			/* send key-up */
298 			new_event.u.k.key =
299 				ADBK_KEYUP(ADBK_KEYVAL(event->bytes[0]));
300 			microtime(&new_event.timestamp);
301 			new_event.bytes[0] = new_event.u.k.key;
302 			aed_dokeyupdown(&new_event);
303 
304 			/* send option-up */
305 			new_event.u.k.key = ADBK_KEYUP(ADBK_OPTION);
306 			new_event.bytes[0] = new_event.u.k.key;
307 			microtime(&new_event.timestamp);
308 			aed_dokeyupdown(&new_event);
309 			break;
310 		}
311 	} else {
312 		aed_dokeyupdown(event);
313 	}
314 }
315 
316 /*
317  * Keyboard autorepeat timeout function.  Sends key up/down events
318  * for the repeating key and schedules the next call at sc_rptinterval
319  * ticks in the future.
320  */
321 static void
322 aed_kbdrpt(kstate)
323 	void *kstate;
324 {
325 	struct aed_softc *aed_sc = (struct aed_softc *)kstate;
326 
327 	aed_sc->sc_rptevent.bytes[0] |= 0x80;
328 	microtime(&aed_sc->sc_rptevent.timestamp);
329 	aed_handoff(&aed_sc->sc_rptevent);	/* do key up */
330 
331 	aed_sc->sc_rptevent.bytes[0] &= 0x7f;
332 	microtime(&aed_sc->sc_rptevent.timestamp);
333 	aed_handoff(&aed_sc->sc_rptevent);	/* do key down */
334 
335 	if (aed_sc->sc_repeating == aed_sc->sc_rptevent.u.k.key) {
336 		callout_reset(&aed_sc->sc_repeat_ch, aed_sc->sc_rptinterval,
337 		    aed_kbdrpt, kstate);
338 	}
339 }
340 
341 
342 /*
343  * Cancels the currently repeating key event if there is one, schedules
344  * a new repeating key event if needed, and hands the event off to the
345  * appropriate subsystem.
346  */
347 static void
348 aed_dokeyupdown(event)
349 	adb_event_t *event;
350 {
351 	int     kbd_key;
352 
353 	kbd_key = ADBK_KEYVAL(event->u.k.key);
354 	if (ADBK_PRESS(event->u.k.key) && keyboard[kbd_key][0] != 0) {
355 		/* ignore shift & control */
356 		if (aed_sc->sc_repeating != -1) {
357 			callout_stop(&aed_sc->sc_repeat_ch);
358 		}
359 		aed_sc->sc_rptevent = *event;
360 		aed_sc->sc_repeating = kbd_key;
361 		callout_reset(&aed_sc->sc_repeat_ch, aed_sc->sc_rptdelay,
362 		    aed_kbdrpt, (void *)aed_sc);
363 	} else {
364 		if (aed_sc->sc_repeating != -1) {
365 			aed_sc->sc_repeating = -1;
366 			callout_stop(&aed_sc->sc_repeat_ch);
367 		}
368 		aed_sc->sc_rptevent = *event;
369 	}
370 	aed_handoff(event);
371 }
372 
373 /*
374  * Place the event in the event queue if a requesting device is open
375  * and we are not polling, otherwise, pass it up to the console driver.
376  */
377 static void
378 aed_handoff(event)
379 	adb_event_t *event;
380 {
381 	if (aed_sc->sc_open && !adb_polling)
382 		aed_enqevent(event);
383 }
384 
385 /*
386  * Place the event in the event queue and wakeup any waiting processes.
387  */
388 static void
389 aed_enqevent(event)
390     adb_event_t *event;
391 {
392 	int     s;
393 
394 	s = spladb();
395 
396 #ifdef DIAGNOSTIC
397 	if (aed_sc->sc_evq_tail < 0 || aed_sc->sc_evq_tail >= AED_MAX_EVENTS)
398 		panic("adb: event queue tail is out of bounds");
399 
400 	if (aed_sc->sc_evq_len < 0 || aed_sc->sc_evq_len > AED_MAX_EVENTS)
401 		panic("adb: event queue len is out of bounds");
402 #endif
403 
404 	if (aed_sc->sc_evq_len == AED_MAX_EVENTS) {
405 		splx(s);
406 		return;		/* Oh, well... */
407 	}
408 	aed_sc->sc_evq[(aed_sc->sc_evq_len + aed_sc->sc_evq_tail) %
409 	    AED_MAX_EVENTS] = *event;
410 	aed_sc->sc_evq_len++;
411 
412 	selnotify(&aed_sc->sc_selinfo, 0);
413 	if (aed_sc->sc_ioproc)
414 		psignal(aed_sc->sc_ioproc, SIGIO);
415 
416 	splx(s);
417 }
418 
419 int
420 aedopen(dev, flag, mode, p)
421     dev_t dev;
422     int flag, mode;
423     struct proc *p;
424 {
425 	int unit;
426 	int error = 0;
427 	int s;
428 
429 	unit = minor(dev);
430 
431 	if (unit != 0)
432 		return (ENXIO);
433 
434 	s = spladb();
435 	if (aed_sc->sc_open) {
436 		splx(s);
437 		return (EBUSY);
438 	}
439 	aed_sc->sc_evq_tail = 0;
440 	aed_sc->sc_evq_len = 0;
441 	aed_sc->sc_open = 1;
442 	aed_sc->sc_ioproc = p;
443 	splx(s);
444 
445 	return (error);
446 }
447 
448 
449 int
450 aedclose(dev, flag, mode, p)
451     dev_t dev;
452     int flag, mode;
453     struct proc *p;
454 {
455 	int s = spladb();
456 
457 	aed_sc->sc_open = 0;
458 	aed_sc->sc_ioproc = NULL;
459 	splx(s);
460 
461 	return (0);
462 }
463 
464 
465 int
466 aedread(dev, uio, flag)
467     dev_t dev;
468     struct uio *uio;
469     int flag;
470 {
471 	int s, error;
472 	int willfit;
473 	int total;
474 	int firstmove;
475 	int moremove;
476 
477 	if (uio->uio_resid < sizeof(adb_event_t))
478 		return (EMSGSIZE);	/* close enough. */
479 
480 	s = spladb();
481 	if (aed_sc->sc_evq_len == 0) {
482 		splx(s);
483 		return (0);
484 	}
485 	willfit = howmany(uio->uio_resid, sizeof(adb_event_t));
486 	total = (aed_sc->sc_evq_len < willfit) ? aed_sc->sc_evq_len : willfit;
487 
488 	firstmove = (aed_sc->sc_evq_tail + total > AED_MAX_EVENTS)
489 	    ? (AED_MAX_EVENTS - aed_sc->sc_evq_tail) : total;
490 
491 	error = uiomove((caddr_t) & aed_sc->sc_evq[aed_sc->sc_evq_tail],
492 	    firstmove * sizeof(adb_event_t), uio);
493 	if (error) {
494 		splx(s);
495 		return (error);
496 	}
497 	moremove = total - firstmove;
498 
499 	if (moremove > 0) {
500 		error = uiomove((caddr_t) & aed_sc->sc_evq[0],
501 		    moremove * sizeof(adb_event_t), uio);
502 		if (error) {
503 			splx(s);
504 			return (error);
505 		}
506 	}
507 	aed_sc->sc_evq_tail = (aed_sc->sc_evq_tail + total) % AED_MAX_EVENTS;
508 	aed_sc->sc_evq_len -= total;
509 	splx(s);
510 	return (0);
511 }
512 
513 int
514 aedioctl(dev, cmd, data, flag, p)
515     dev_t dev;
516     u_long cmd;
517     caddr_t data;
518     int flag;
519     struct proc *p;
520 {
521 	switch (cmd) {
522 	case ADBIOC_DEVSINFO: {
523 		adb_devinfo_t *di;
524 		ADBDataBlock adbdata;
525 		int totaldevs;
526 		int adbaddr;
527 		int i;
528 
529 		di = (void *)data;
530 
531 		/* Initialize to no devices */
532 		for (i = 0; i < 16; i++)
533 			di->dev[i].addr = -1;
534 
535 		totaldevs = CountADBs();
536 		for (i = 1; i <= totaldevs; i++) {
537 			adbaddr = GetIndADB(&adbdata, i);
538 			di->dev[adbaddr].addr = adbaddr;
539 			di->dev[adbaddr].default_addr = (int)(adbdata.origADBAddr);
540 			di->dev[adbaddr].handler_id = (int)(adbdata.devType);
541 			}
542 
543 		/* Must call ADB Manager to get devices now */
544 		break;
545 	}
546 
547 	case ADBIOC_GETREPEAT:{
548 		adb_rptinfo_t *ri;
549 
550 		ri = (void *)data;
551 		ri->delay_ticks = aed_sc->sc_rptdelay;
552 		ri->interval_ticks = aed_sc->sc_rptinterval;
553 		break;
554 	}
555 
556 	case ADBIOC_SETREPEAT:{
557 		adb_rptinfo_t *ri;
558 
559 		ri = (void *) data;
560 		aed_sc->sc_rptdelay = ri->delay_ticks;
561 		aed_sc->sc_rptinterval = ri->interval_ticks;
562 		break;
563 	}
564 
565 	case ADBIOC_RESET:
566 		/* Do nothing for now */
567 		break;
568 
569 	case ADBIOC_LISTENCMD:{
570 		adb_listencmd_t *lc;
571 
572 		lc = (void *)data;
573 	}
574 
575 	default:
576 		return (EINVAL);
577 	}
578 	return (0);
579 }
580 
581 
582 int
583 aedpoll(dev, events, p)
584 	dev_t dev;
585 	int events;
586 	struct proc *p;
587 {
588 	int s, revents;
589 
590 	revents = events & (POLLOUT | POLLWRNORM);
591 
592 	if ((events & (POLLIN | POLLRDNORM)) == 0)
593 		return (revents);
594 
595 	s = spladb();
596 	if (aed_sc->sc_evq_len > 0)
597 		revents |= events & (POLLIN | POLLRDNORM);
598 	else
599 		selrecord(p, &aed_sc->sc_selinfo);
600 	splx(s);
601 
602 	return (revents);
603 }
604 
605 static void
606 filt_aedrdetach(struct knote *kn)
607 {
608 	int s;
609 
610 	s = spladb();
611 	SLIST_REMOVE(&aed_sc->sc_selinfo.sel_klist, kn, knote, kn_selnext);
612 	splx(s);
613 }
614 
615 static int
616 filt_aedread(struct knote *kn, long hint)
617 {
618 
619 	kn->kn_data = aed_sc->sc_evq_len * sizeof(adb_event_t);
620 	return (kn->kn_data > 0);
621 }
622 
623 static const struct filterops aedread_filtops =
624 	{ 1, NULL, filt_aedrdetach, filt_aedread };
625 
626 static const struct filterops aed_seltrue_filtops =
627 	{ 1, NULL, filt_aedrdetach, filt_seltrue };
628 
629 int
630 aedkqfilter(dev_t dev, struct knote *kn)
631 {
632 	struct klist *klist;
633 	int s;
634 
635 	switch (kn->kn_filter) {
636 	case EVFILT_READ:
637 		klist = &aed_sc->sc_selinfo.sel_klist;
638 		kn->kn_fop = &aedread_filtops;
639 		break;
640 
641 	case EVFILT_WRITE:
642 		klist = &aed_sc->sc_selinfo.sel_klist;
643 		kn->kn_fop = &aed_seltrue_filtops;
644 		break;
645 
646 	default:
647 		return (1);
648 	}
649 
650 	kn->kn_hook = NULL;
651 
652 	s = spladb();
653 	SLIST_INSERT_HEAD(klist, kn, kn_selnext);
654 	splx(s);
655 
656 	return (0);
657 }
658