xref: /openbsd-src/sys/dev/adb/akbd.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: akbd.c,v 1.7 2009/01/21 21:53:59 grange Exp $	*/
2 /*	$NetBSD: akbd.c,v 1.17 2005/01/15 16:00:59 chs Exp $	*/
3 
4 /*
5  * Copyright (C) 1998	Colin Wood
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  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by Colin Wood.
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <sys/param.h>
35 #include <sys/timeout.h>
36 #include <sys/kernel.h>
37 #include <sys/device.h>
38 #include <sys/systm.h>
39 
40 #include <dev/wscons/wsconsio.h>
41 #include <dev/wscons/wskbdvar.h>
42 #include <dev/wscons/wsksymdef.h>
43 #include <dev/wscons/wsksymvar.h>
44 
45 #include <machine/autoconf.h>
46 #include <machine/cpu.h>
47 
48 #include <dev/adb/adb.h>
49 #include <dev/adb/akbdmap.h>
50 #include <dev/adb/akbdvar.h>
51 
52 #ifdef WSDISPLAY_COMPAT_RAWKBD
53 #define KEYBOARD_ARRAY
54 #endif
55 #include <dev/adb/keyboard.h>
56 
57 /*
58  * Function declarations.
59  */
60 int	akbdmatch(struct device *, void *, void *);
61 void	akbdattach(struct device *, struct device *, void *);
62 
63 /* Driver definition. */
64 struct cfattach akbd_ca = {
65 	sizeof(struct akbd_softc), akbdmatch, akbdattach
66 };
67 struct cfdriver akbd_cd = {
68 	NULL, "akbd", DV_DULL
69 };
70 
71 int	akbd_enable(void *, int);
72 void	akbd_set_leds(void *, int);
73 int	akbd_ioctl(void *, u_long, caddr_t, int, struct proc *);
74 
75 
76 struct wskbd_accessops akbd_accessops = {
77 	akbd_enable,
78 	akbd_set_leds,
79 	akbd_ioctl,
80 };
81 
82 struct wskbd_mapdata akbd_keymapdata = {
83 	akbd_keydesctab,
84 #ifdef AKBD_LAYOUT
85 	AKBD_LAYOUT,
86 #else
87 	KB_US,
88 #endif
89 };
90 
91 void	akbd_adbcomplete(caddr_t, caddr_t, int);
92 void	akbd_capslockwrapper(struct akbd_softc *, int);
93 void	akbd_input(struct akbd_softc *, int);
94 void	akbd_processevent(struct akbd_softc *, adb_event_t *);
95 void	akbd_rawrepeat(void *v);
96 #ifdef notyet
97 u_char	getleds(int);
98 int	setleds(struct akbd_softc *, u_char);
99 void	blinkleds(struct akbd_softc *);
100 #endif
101 
102 int
103 akbdmatch(struct device *parent, void *vcf, void *aux)
104 {
105 	struct adb_attach_args *aa_args = (struct adb_attach_args *)aux;
106 
107 	if (aa_args->origaddr == ADBADDR_KBD)
108 		return (1);
109 	else
110 		return (0);
111 }
112 
113 void
114 akbdattach(struct device *parent, struct device *self, void *aux)
115 {
116 	ADBSetInfoBlock adbinfo;
117 	struct akbd_softc *sc = (struct akbd_softc *)self;
118 	struct adb_attach_args *aa_args = (struct adb_attach_args *)aux;
119 	int error, kbd_done;
120 	short cmd;
121 	u_char buffer[9];
122 	struct wskbddev_attach_args a;
123 	static int akbd_console_initted;
124 	int wskbd_eligible = 1;
125 
126 	sc->origaddr = aa_args->origaddr;
127 	sc->adbaddr = aa_args->adbaddr;
128 	sc->handler_id = aa_args->handler_id;
129 
130 	sc->sc_leds = (u_int8_t)0x00;	/* initially off */
131 	sc->sc_caps = 0;
132 
133 	adbinfo.siServiceRtPtr = (Ptr)akbd_adbcomplete;
134 	adbinfo.siDataAreaAddr = (caddr_t)sc;
135 
136 	printf(": ");
137 	switch (sc->handler_id) {
138 	case ADB_STDKBD:
139 		printf("standard keyboard\n");
140 		break;
141 	case ADB_ISOKBD:
142 		printf("standard keyboard (ISO layout)\n");
143 		break;
144 	case ADB_EXTKBD:
145 		cmd = ADBTALK(sc->adbaddr, 1);
146 		kbd_done =
147 		    (adb_op_sync((Ptr)buffer, cmd) == 0);
148 
149 		/* Ignore Logitech MouseMan/Trackman pseudo keyboard */
150 		if (kbd_done && buffer[1] == 0x9a && buffer[2] == 0x20) {
151 			printf("Mouseman (non-EMP) pseudo keyboard\n");
152 			adbinfo.siServiceRtPtr = (Ptr)0;
153 			adbinfo.siDataAreaAddr = (Ptr)0;
154 			wskbd_eligible = 0;
155 		} else if (kbd_done && buffer[1] == 0x9a && buffer[2] == 0x21) {
156 			printf("Trackman (non-EMP) pseudo keyboard\n");
157 			adbinfo.siServiceRtPtr = (Ptr)0;
158 			adbinfo.siDataAreaAddr = (Ptr)0;
159 			wskbd_eligible = 0;
160 		} else {
161 			printf("extended keyboard\n");
162 #ifdef notyet
163 			blinkleds(sc);
164 #endif
165 		}
166 		break;
167 	case ADB_EXTISOKBD:
168 		printf("extended keyboard (ISO layout)\n");
169 #ifdef notyet
170 		blinkleds(sc);
171 #endif
172 		break;
173 	case ADB_KBDII:
174 		printf("keyboard II\n");
175 		break;
176 	case ADB_ISOKBDII:
177 		printf("keyboard II (ISO layout)\n");
178 		break;
179 	case ADB_PBKBD:
180 		printf("PowerBook keyboard\n");
181 		break;
182 	case ADB_PBISOKBD:
183 		printf("PowerBook keyboard (ISO layout)\n");
184 		break;
185 	case ADB_ADJKPD:
186 		printf("adjustable keypad\n");
187 		wskbd_eligible = 0;
188 		break;
189 	case ADB_ADJKBD:
190 		printf("adjustable keyboard\n");
191 		break;
192 	case ADB_ADJISOKBD:
193 		printf("adjustable keyboard (ISO layout)\n");
194 		break;
195 	case ADB_ADJJAPKBD:
196 		printf("adjustable keyboard (Japanese layout)\n");
197 		break;
198 	case ADB_PBEXTISOKBD:
199 		printf("PowerBook extended keyboard (ISO layout)\n");
200 		break;
201 	case ADB_PBEXTJAPKBD:
202 		printf("PowerBook extended keyboard (Japanese layout)\n");
203 		break;
204 	case ADB_JPKBDII:
205 		printf("keyboard II (Japanese layout)\n");
206 		break;
207 	case ADB_PBEXTKBD:
208 		printf("PowerBook extended keyboard\n");
209 		break;
210 	case ADB_DESIGNKBD:
211 		printf("extended keyboard\n");
212 #ifdef notyet
213 		blinkleds(sc);
214 #endif
215 		break;
216 	case ADB_PBJPKBD:
217 		printf("PowerBook keyboard (Japanese layout)\n");
218 		break;
219 	case ADB_PBG3JPKBD:
220 		printf("PowerBook G3 keyboard (Japanese layout)\n");
221 		break;
222 	case ADB_PBG4KBD:
223 		printf("PowerBook G4 keyboard (Inverted T)\n");
224 		break;
225 	case ADB_IBITISOKBD:
226 		printf("iBook keyboard with inverted T (ISO layout)\n");
227 		break;
228 	default:
229 		printf("mapped device (%d)\n", sc->handler_id);
230 #if 0
231 		wskbd_eligible = 0;
232 #endif
233 		break;
234 	}
235 	error = set_adb_info(&adbinfo, sc->adbaddr);
236 #ifdef ADB_DEBUG
237 	if (adb_debug)
238 		printf("akbd: returned %d from set_adb_info\n", error);
239 #endif
240 
241 #ifdef WSDISPLAY_COMPAT_RAWKBD
242 	timeout_set(&sc->sc_rawrepeat_ch, akbd_rawrepeat, sc);
243 #endif
244 
245 	if (akbd_is_console() && wskbd_eligible)
246 		a.console = (++akbd_console_initted == 1);
247 	else
248 		a.console = 0;
249 	a.keymap = &akbd_keymapdata;
250 	a.accessops = &akbd_accessops;
251 	a.accesscookie = sc;
252 
253 	sc->sc_wskbddev = config_found(self, &a, wskbddevprint);
254 }
255 
256 
257 /*
258  * Handle putting the keyboard data received from the ADB into
259  * an ADB event record.
260  */
261 void
262 akbd_adbcomplete(caddr_t buffer, caddr_t data_area, int adb_command)
263 {
264 	adb_event_t event;
265 	struct akbd_softc *sc;
266 	int adbaddr;
267 #ifdef ADB_DEBUG
268 	int i;
269 
270 	if (adb_debug)
271 		printf("adb: transaction completion\n");
272 #endif
273 
274 	adbaddr = ADB_CMDADDR(adb_command);
275 	sc = (struct akbd_softc *)data_area;
276 
277 	event.byte_count = buffer[0];
278 	memcpy(event.bytes, buffer + 1, event.byte_count);
279 
280 #ifdef ADB_DEBUG
281 	if (adb_debug) {
282 		printf("akbd: from %d at %d (org %d) %d:", adbaddr,
283 		    sc->handler_id, sc->origaddr, buffer[0]);
284 		for (i = 1; i <= buffer[0]; i++)
285 			printf(" %x", buffer[i]);
286 		printf("\n");
287 	}
288 #endif
289 
290 	if (sc->sc_wskbddev != NULL)
291 		akbd_processevent(sc, &event);
292 }
293 
294 #ifdef notyet
295 /*
296  * Get the actual hardware LED state and convert it to softc format.
297  */
298 u_char
299 getleds(int addr)
300 {
301 	short cmd;
302 	u_char buffer[9], leds;
303 
304 	leds = 0x00;	/* all off */
305 	buffer[0] = 0;
306 
307 	cmd = ADBTALK(addr, 2);
308 	if (adb_op_sync((Ptr)buffer, cmd) == 0 &&
309 	    buffer[0] > 0)
310 		leds = ~(buffer[2]) & 0x07;
311 
312 	return (leds);
313 }
314 
315 /*
316  * Set the keyboard LED's.
317  *
318  * Automatically translates from ioctl/softc format to the
319  * actual keyboard register format
320  */
321 int
322 setleds(struct akbd_softc *sc, u_char leds)
323 {
324 	int addr;
325 	short cmd;
326 	u_char buffer[9];
327 
328 	addr = sc->adbaddr;
329 	buffer[0] = 0;
330 
331 	cmd = ADBTALK(addr, 2);
332 	if (adb_op_sync((Ptr)buffer, cmd) || buffer[0] == 0)
333 		return (EIO);
334 
335 	leds = ~leds & 0x07;
336 	buffer[2] &= 0xf8;
337 	buffer[2] |= leds;
338 
339 	cmd = ADBLISTEN(addr, 2);
340 	adb_op_sync((Ptr)buffer, cmd);
341 
342 	/* talk R2 */
343 	cmd = ADBTALK(addr, 2);
344 	if (adb_op_sync((Ptr)buffer, cmd) || buffer[0] == 0)
345 		return (EIO);
346 
347 	if ((buffer[2] & 0xf8) != leds)
348 		return (EIO);
349 	else
350 		return (0);
351 }
352 
353 /*
354  * Toggle all of the LED's on and off, just for show.
355  */
356 void
357 blinkleds(struct akbd_softc *sc)
358 {
359 	u_char origleds;
360 
361 	origleds = getleds(sc->adbaddr);
362 	setleds(sc, LED_NUMLOCK | LED_CAPSLOCK | LED_SCROLL_LOCK);
363 	delay(400000);
364 	setleds(sc, origleds);
365 
366 	if (origleds & LED_NUMLOCK)
367 		sc->sc_leds |= WSKBD_LED_NUM;
368 	if (origleds & LED_CAPSLOCK)
369 		sc->sc_leds |= WSKBD_LED_CAPS;
370 	if (origleds & LED_SCROLL_LOCK)
371 		sc->sc_leds |= WSKBD_LED_SCROLL;
372 }
373 #endif
374 
375 int
376 akbd_enable(void *v, int on)
377 {
378 	return 0;
379 }
380 
381 void
382 akbd_set_leds(void *v, int on)
383 {
384 #ifdef notyet
385 	struct akbd_softc *sc = v;
386 	int leds;
387 
388 	if (sc->sc_extended) {
389 		if (sc->sc_leds == on)
390 			return;
391 
392 		leds = 0;
393 		if (on & WSKBD_LED_NUM)
394 			leds |= LED_NUMLOCK;
395 		if (on & WSKBD_LED_CAPS)
396 			leds |= LED_CAPSLOCK;
397 		if (on & WSKBD_LED_SCROLL)
398 			leds |= LED_SCROLL_LOCK;
399 
400 		setleds(sc, leds);
401 	}
402 #endif
403 }
404 
405 int
406 akbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
407 {
408 	struct akbd_softc *sc = v;
409 
410 	switch (cmd) {
411 
412 	case WSKBDIO_GTYPE:
413 		*(int *)data = WSKBD_TYPE_ADB;
414 		return 0;
415 	case WSKBDIO_SETLEDS:
416 		akbd_set_leds(v, *(int *)data);
417 		return 0;
418 	case WSKBDIO_GETLEDS:
419 		*(int *)data = sc->sc_leds;
420 		return 0;
421 #ifdef WSDISPLAY_COMPAT_RAWKBD
422 	case WSKBDIO_SETMODE:
423 		sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
424 		timeout_del(&sc->sc_rawrepeat_ch);
425 		return (0);
426 #endif
427 
428 #ifdef mac68k	/* XXX not worth creating akbd_machdep_ioctl() */
429 	case WSKBDIO_BELL:
430 	case WSKBDIO_COMPLEXBELL:
431 #define d ((struct wskbd_bell_data *)data)
432 		mac68k_ring_bell(d->pitch, d->period * hz / 1000, d->volume);
433 #undef d
434 		return (0);
435 #endif
436 
437 	default:
438 		return (-1);
439 	}
440 }
441 
442 #ifdef WSDISPLAY_COMPAT_RAWKBD
443 void
444 akbd_rawrepeat(void *v)
445 {
446 	struct akbd_softc *sc = v;
447 	int s;
448 
449 	s = spltty();
450 	wskbd_rawinput(sc->sc_wskbddev, sc->sc_rep, sc->sc_nrep);
451 	splx(s);
452 	timeout_add_msec(&sc->sc_rawrepeat_ch, REP_DELAYN);
453 }
454 #endif
455 
456 /*
457  * The ``caps lock'' key is special: since on earlier keyboards, the physical
458  * key stays down when pressed, we will get a notification of the key press,
459  * but not of the key release. Then, when it is pressed again, we will not get
460  * a notification of the key press, but will see the key release.
461  *
462  * This is not exactly true. We see the missing release and press events both
463  * as the release of the power (reset) key.
464  *
465  * To avoid confusing them with real power key presses, we maintain two
466  * states for the caps lock key: logically down (from wscons' point of view),
467  * and ``physically'' down (from the adb messages point of view), to ignore
468  * the power key. But since one may press the power key while the caps lock
469  * is held down, we also have to remember the state of the power key... this
470  * is quite messy.
471  */
472 
473 /*
474  * Values for caps lock state machine
475  */
476 #define	CL_DOWN_ADB	0x01
477 #define	CL_DOWN_LOGICAL	0x02
478 #define	CL_DOWN_RESET	0x04
479 
480 /*
481  * Given a keyboard ADB event, decode the keycodes and pass them to wskbd.
482  */
483 void
484 akbd_processevent(struct akbd_softc *sc, adb_event_t *event)
485 {
486 	switch (event->byte_count) {
487 	case 1:
488 		akbd_capslockwrapper(sc, event->bytes[0]);
489 		break;
490 	case 2:
491 		/*
492 		 * The reset (or power) key sends 0x7f7f on press and
493 		 * 0xffff on release, and we ignore it.
494 		 */
495 		if (event->bytes[0] == event->bytes[1] &&
496 		    ADBK_KEYVAL(event->bytes[0]) == ADBK_RESET) {
497 			if (event->bytes[0] == ADBK_KEYDOWN(ADBK_RESET))
498 				SET(sc->sc_caps, CL_DOWN_RESET);
499 			else {
500 				if (ISSET(sc->sc_caps, CL_DOWN_RESET))
501 					CLR(sc->sc_caps, CL_DOWN_RESET);
502 				else if (ISSET(sc->sc_caps, CL_DOWN_ADB)) {
503 					akbd_input(sc, ISSET(sc->sc_caps,
504 					    CL_DOWN_LOGICAL) ?
505 					      ADBK_KEYDOWN(ADBK_CAPSLOCK) :
506 					      ADBK_KEYUP(ADBK_CAPSLOCK));
507 					sc->sc_caps ^= CL_DOWN_LOGICAL;
508 				}
509 			}
510 		} else {
511 			akbd_capslockwrapper(sc, event->bytes[0]);
512 			akbd_capslockwrapper(sc, event->bytes[1]);
513 		}
514 		break;
515 	default:
516 #ifdef DIAGNOSTIC
517 		printf("%s: unexpected message length %d\n",
518 		    sc->sc_dev.dv_xname, event->byte_count);
519 #endif
520 		break;
521 	}
522 
523 }
524 
525 void
526 akbd_capslockwrapper(struct akbd_softc *sc, int key)
527 {
528 	if (ADBK_KEYVAL(key) == ADBK_CAPSLOCK)
529 		sc->sc_caps ^= CL_DOWN_ADB;
530 
531 	if (key != 0xff)
532 		akbd_input(sc, key);
533 }
534 
535 int adb_polledkey;
536 void
537 akbd_input(struct akbd_softc *sc, int key)
538 {
539 	int press, val;
540 	int type;
541 
542 	press = ADBK_PRESS(key);
543 	val = ADBK_KEYVAL(key);
544 
545 	type = press ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP;
546 
547 	if (adb_polling) {
548 		adb_polledkey = key;
549 #ifdef WSDISPLAY_COMPAT_RAWKBD
550 	} else if (sc->sc_rawkbd) {
551 		char cbuf[MAXKEYS *2];
552 		int c, j, s;
553 		int npress;
554 
555 		j = npress = 0;
556 
557 		c = keyboard[val];
558 		if (c == 0) {
559 			return; /* XXX */
560 		}
561 		if (c & 0x80)
562 			cbuf[j++] = 0xe0;
563 		cbuf[j] = c & 0x7f;
564 		if (type == WSCONS_EVENT_KEY_UP) {
565 			cbuf[j] |= 0x80;
566 		} else {
567 			/* this only records last key pressed */
568 			if (c & 0x80)
569 				sc->sc_rep[npress++] = 0xe0;
570 			sc->sc_rep[npress++] = c & 0x7f;
571 		}
572 		j++;
573 		s = spltty();
574 		wskbd_rawinput(sc->sc_wskbddev, cbuf, j);
575 		splx(s);
576 		timeout_del(&sc->sc_rawrepeat_ch);
577 		sc->sc_nrep = npress;
578 		if (npress != 0)
579 			timeout_add_msec(&sc->sc_rawrepeat_ch, REP_DELAY1);
580 #endif
581 	} else {
582 		wskbd_input(sc->sc_wskbddev, type, val);
583 	}
584 }
585