xref: /openbsd/sys/dev/adb/akbd.c (revision d89ec533)
1 /*	$OpenBSD: akbd.c,v 1.14 2014/01/26 17:48:08 miod 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 #include <dev/adb/keyboard.h>
52 
53 /*
54  * Function declarations.
55  */
56 int	akbdmatch(struct device *, void *, void *);
57 void	akbdattach(struct device *, struct device *, void *);
58 
59 /* Driver definition. */
60 struct cfattach akbd_ca = {
61 	sizeof(struct akbd_softc), akbdmatch, akbdattach
62 };
63 struct cfdriver akbd_cd = {
64 	NULL, "akbd", DV_DULL
65 };
66 
67 int	akbd_enable(void *, int);
68 void	akbd_set_leds(void *, int);
69 int	akbd_ioctl(void *, u_long, caddr_t, int, struct proc *);
70 
71 
72 struct wskbd_accessops akbd_accessops = {
73 	akbd_enable,
74 	akbd_set_leds,
75 	akbd_ioctl,
76 };
77 
78 struct wskbd_mapdata akbd_keymapdata = {
79 	akbd_keydesctab,
80 #ifdef AKBD_LAYOUT
81 	AKBD_LAYOUT,
82 #else
83 	KB_US | KB_DEFAULT,
84 #endif
85 };
86 
87 void	akbd_adbcomplete(caddr_t, caddr_t, int);
88 void	akbd_capslockwrapper(struct akbd_softc *, int);
89 void	akbd_input(struct akbd_softc *, int);
90 void	akbd_processevent(struct akbd_softc *, adb_event_t *);
91 #ifdef notyet
92 u_char	getleds(int);
93 int	setleds(struct akbd_softc *, u_char);
94 void	blinkleds(struct akbd_softc *);
95 #endif
96 
97 int
98 akbdmatch(struct device *parent, void *vcf, void *aux)
99 {
100 	struct adb_attach_args *aa_args = (struct adb_attach_args *)aux;
101 
102 	if (strcmp(aa_args->name, adb_device_name) != 0)
103 		return (0);
104 
105 	if (aa_args->origaddr == ADBADDR_KBD)
106 		return (1);
107 	else
108 		return (0);
109 }
110 
111 void
112 akbdattach(struct device *parent, struct device *self, void *aux)
113 {
114 	ADBSetInfoBlock adbinfo;
115 	struct akbd_softc *sc = (struct akbd_softc *)self;
116 	struct adb_attach_args *aa_args = (struct adb_attach_args *)aux;
117 	int error, kbd_done;
118 	short cmd;
119 	u_char buffer[9];
120 	struct wskbddev_attach_args a;
121 	static int akbd_console_initted;
122 	int wskbd_eligible = 1;
123 
124 	sc->origaddr = aa_args->origaddr;
125 	sc->adbaddr = aa_args->adbaddr;
126 	sc->handler_id = aa_args->handler_id;
127 
128 	sc->sc_leds = (u_int8_t)0x00;	/* initially off */
129 	sc->sc_caps = 0;
130 	sc->sc_iso = 0;
131 
132 	adbinfo.siServiceRtPtr = (Ptr)akbd_adbcomplete;
133 	adbinfo.siDataAreaAddr = (caddr_t)sc;
134 
135 	printf(": ");
136 	switch (sc->handler_id) {
137 	case ADB_STDKBD:
138 		printf("standard keyboard\n");
139 		break;
140 	case ADB_ISOKBD:
141 		printf("standard keyboard (ISO layout)\n");
142 		sc->sc_iso = 1;
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 		sc->sc_iso = 1;
170 #ifdef notyet
171 		blinkleds(sc);
172 #endif
173 		break;
174 	case ADB_KBDII:
175 		printf("keyboard II\n");
176 		break;
177 	case ADB_ISOKBDII:
178 		printf("keyboard II (ISO layout)\n");
179 		sc->sc_iso = 1;
180 		break;
181 	case ADB_PBKBD:
182 		printf("PowerBook keyboard\n");
183 		break;
184 	case ADB_PBISOKBD:
185 		printf("PowerBook keyboard (ISO layout)\n");
186 		sc->sc_iso = 1;
187 		break;
188 	case ADB_ADJKPD:
189 		printf("adjustable keypad\n");
190 		wskbd_eligible = 0;
191 		break;
192 	case ADB_ADJKBD:
193 		printf("adjustable keyboard\n");
194 		break;
195 	case ADB_ADJISOKBD:
196 		printf("adjustable keyboard (ISO layout)\n");
197 		sc->sc_iso = 1;
198 		break;
199 	case ADB_ADJJAPKBD:
200 		printf("adjustable keyboard (Japanese layout)\n");
201 		break;
202 	case ADB_PBEXTISOKBD:
203 		printf("PowerBook extended keyboard (ISO layout)\n");
204 		sc->sc_iso = 1;
205 		break;
206 	case ADB_PBEXTJAPKBD:
207 		printf("PowerBook extended keyboard (Japanese layout)\n");
208 		break;
209 	case ADB_JPKBDII:
210 		printf("keyboard II (Japanese layout)\n");
211 		break;
212 	case ADB_PBEXTKBD:
213 		printf("PowerBook extended keyboard\n");
214 		break;
215 	case ADB_DESIGNKBD:
216 		printf("extended keyboard\n");
217 #ifdef notyet
218 		blinkleds(sc);
219 #endif
220 		break;
221 	case ADB_PBJPKBD:
222 		printf("PowerBook keyboard (Japanese layout)\n");
223 		break;
224 	case ADB_PBG3JPKBD:
225 		printf("PowerBook G3 keyboard (Japanese layout)\n");
226 		break;
227 	case ADB_PBG4KBD:
228 		printf("PowerBook G4 keyboard (Inverted T)\n");
229 		break;
230 	case ADB_IBITISOKBD:
231 		printf("iBook keyboard with inverted T (ISO layout)\n");
232 		sc->sc_iso = 1;
233 		break;
234 	default:
235 		printf("mapped device (%d)\n", sc->handler_id);
236 #if 0
237 		wskbd_eligible = 0;
238 #endif
239 		break;
240 	}
241 	error = set_adb_info(&adbinfo, sc->adbaddr);
242 #ifdef ADB_DEBUG
243 	if (adb_debug)
244 		printf("akbd: returned %d from set_adb_info\n", error);
245 #endif
246 
247 	if (akbd_is_console() && wskbd_eligible)
248 		a.console = (++akbd_console_initted == 1);
249 	else
250 		a.console = 0;
251 	a.keymap = &akbd_keymapdata;
252 	a.accessops = &akbd_accessops;
253 	a.accesscookie = sc;
254 
255 	sc->sc_wskbddev = config_found(self, &a, wskbddevprint);
256 }
257 
258 
259 /*
260  * Handle putting the keyboard data received from the ADB into
261  * an ADB event record.
262  */
263 void
264 akbd_adbcomplete(caddr_t buffer, caddr_t data_area, int adb_command)
265 {
266 	adb_event_t event;
267 	struct akbd_softc *sc;
268 	int adbaddr;
269 #ifdef ADB_DEBUG
270 	int i;
271 
272 	if (adb_debug)
273 		printf("adb: transaction completion\n");
274 #endif
275 
276 	adbaddr = ADB_CMDADDR(adb_command);
277 	sc = (struct akbd_softc *)data_area;
278 
279 	event.byte_count = buffer[0];
280 	memcpy(event.bytes, buffer + 1, event.byte_count);
281 
282 #ifdef ADB_DEBUG
283 	if (adb_debug) {
284 		printf("akbd: from %d at %d (org %d) %d:", adbaddr,
285 		    sc->handler_id, sc->origaddr, buffer[0]);
286 		for (i = 1; i <= buffer[0]; i++)
287 			printf(" %x", buffer[i]);
288 		printf("\n");
289 	}
290 #endif
291 
292 	if (sc->sc_wskbddev != NULL)
293 		akbd_processevent(sc, &event);
294 }
295 
296 #ifdef notyet
297 /*
298  * Get the actual hardware LED state and convert it to softc format.
299  */
300 u_char
301 getleds(int addr)
302 {
303 	short cmd;
304 	u_char buffer[9], leds;
305 
306 	leds = 0x00;	/* all off */
307 	buffer[0] = 0;
308 
309 	cmd = ADBTALK(addr, 2);
310 	if (adb_op_sync((Ptr)buffer, cmd) == 0 &&
311 	    buffer[0] > 0)
312 		leds = ~(buffer[2]) & 0x07;
313 
314 	return (leds);
315 }
316 
317 /*
318  * Set the keyboard LED's.
319  *
320  * Automatically translates from ioctl/softc format to the
321  * actual keyboard register format
322  */
323 int
324 setleds(struct akbd_softc *sc, u_char leds)
325 {
326 	int addr;
327 	short cmd;
328 	u_char buffer[9];
329 
330 	addr = sc->adbaddr;
331 	buffer[0] = 0;
332 
333 	cmd = ADBTALK(addr, 2);
334 	if (adb_op_sync((Ptr)buffer, cmd) || buffer[0] == 0)
335 		return (EIO);
336 
337 	leds = ~leds & 0x07;
338 	buffer[2] &= 0xf8;
339 	buffer[2] |= leds;
340 
341 	cmd = ADBLISTEN(addr, 2);
342 	adb_op_sync((Ptr)buffer, cmd);
343 
344 	/* talk R2 */
345 	cmd = ADBTALK(addr, 2);
346 	if (adb_op_sync((Ptr)buffer, cmd) || buffer[0] == 0)
347 		return (EIO);
348 
349 	if ((buffer[2] & 0xf8) != leds)
350 		return (EIO);
351 	else
352 		return (0);
353 }
354 
355 /*
356  * Toggle all of the LED's on and off, just for show.
357  */
358 void
359 blinkleds(struct akbd_softc *sc)
360 {
361 	u_char origleds;
362 
363 	origleds = getleds(sc->adbaddr);
364 	setleds(sc, LED_NUMLOCK | LED_CAPSLOCK | LED_SCROLL_LOCK);
365 	delay(400000);
366 	setleds(sc, origleds);
367 
368 	if (origleds & LED_NUMLOCK)
369 		sc->sc_leds |= WSKBD_LED_NUM;
370 	if (origleds & LED_CAPSLOCK)
371 		sc->sc_leds |= WSKBD_LED_CAPS;
372 	if (origleds & LED_SCROLL_LOCK)
373 		sc->sc_leds |= WSKBD_LED_SCROLL;
374 }
375 #endif
376 
377 int
378 akbd_enable(void *v, int on)
379 {
380 	return 0;
381 }
382 
383 void
384 akbd_set_leds(void *v, int on)
385 {
386 #ifdef notyet
387 	struct akbd_softc *sc = v;
388 	int leds;
389 
390 	if (sc->sc_extended) {
391 		if (sc->sc_leds == on)
392 			return;
393 
394 		leds = 0;
395 		if (on & WSKBD_LED_NUM)
396 			leds |= LED_NUMLOCK;
397 		if (on & WSKBD_LED_CAPS)
398 			leds |= LED_CAPSLOCK;
399 		if (on & WSKBD_LED_SCROLL)
400 			leds |= LED_SCROLL_LOCK;
401 
402 		setleds(sc, leds);
403 	}
404 #endif
405 }
406 
407 int
408 akbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
409 {
410 	struct akbd_softc *sc = v;
411 
412 	switch (cmd) {
413 
414 	case WSKBDIO_GTYPE:
415 		*(int *)data = WSKBD_TYPE_ADB;
416 		return 0;
417 	case WSKBDIO_SETLEDS:
418 		akbd_set_leds(v, *(int *)data);
419 		return 0;
420 	case WSKBDIO_GETLEDS:
421 		*(int *)data = sc->sc_leds;
422 		return 0;
423 #ifdef WSDISPLAY_COMPAT_RAWKBD
424 	case WSKBDIO_SETMODE:
425 		sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
426 		return (0);
427 #endif
428 	default:
429 		return (-1);
430 	}
431 }
432 
433 /*
434  * The ``caps lock'' key is special: since on earlier keyboards, the physical
435  * key stays down when pressed, we will get a notification of the key press,
436  * but not of the key release. Then, when it is pressed again, we will not get
437  * a notification of the key press, but will see the key release.
438  *
439  * This is not exactly true. We see the missing release and press events both
440  * as the release of the power (reset) key.
441  *
442  * To avoid confusing them with real power key presses, we maintain two
443  * states for the caps lock key: logically down (from wscons' point of view),
444  * and ``physically'' down (from the adb messages point of view), to ignore
445  * the power key. But since one may press the power key while the caps lock
446  * is held down, we also have to remember the state of the power key... this
447  * is quite messy.
448  */
449 
450 /*
451  * Values for caps lock state machine
452  */
453 #define	CL_DOWN_ADB	0x01
454 #define	CL_DOWN_LOGICAL	0x02
455 #define	CL_DOWN_RESET	0x04
456 
457 /*
458  * Given a keyboard ADB event, decode the keycodes and pass them to wskbd.
459  */
460 void
461 akbd_processevent(struct akbd_softc *sc, adb_event_t *event)
462 {
463 	switch (event->byte_count) {
464 	case 1:
465 		akbd_capslockwrapper(sc, event->bytes[0]);
466 		break;
467 	case 2:
468 		/*
469 		 * The reset (or power) key sends 0x7f7f on press and
470 		 * 0xffff on release, and we ignore it.
471 		 */
472 		if (event->bytes[0] == event->bytes[1] &&
473 		    ADBK_KEYVAL(event->bytes[0]) == ADBK_RESET) {
474 			if (event->bytes[0] == ADBK_KEYDOWN(ADBK_RESET))
475 				SET(sc->sc_caps, CL_DOWN_RESET);
476 			else {
477 				if (ISSET(sc->sc_caps, CL_DOWN_RESET))
478 					CLR(sc->sc_caps, CL_DOWN_RESET);
479 				else if (ISSET(sc->sc_caps, CL_DOWN_ADB)) {
480 					akbd_input(sc, ISSET(sc->sc_caps,
481 					    CL_DOWN_LOGICAL) ?
482 					      ADBK_KEYDOWN(ADBK_CAPSLOCK) :
483 					      ADBK_KEYUP(ADBK_CAPSLOCK));
484 					sc->sc_caps ^= CL_DOWN_LOGICAL;
485 				}
486 			}
487 		} else {
488 			akbd_capslockwrapper(sc, event->bytes[0]);
489 			akbd_capslockwrapper(sc, event->bytes[1]);
490 		}
491 		break;
492 	default:
493 #ifdef DIAGNOSTIC
494 		printf("%s: unexpected message length %d\n",
495 		    sc->sc_dev.dv_xname, event->byte_count);
496 #endif
497 		break;
498 	}
499 
500 }
501 
502 void
503 akbd_capslockwrapper(struct akbd_softc *sc, int key)
504 {
505 	if (ADBK_KEYVAL(key) == ADBK_CAPSLOCK)
506 		sc->sc_caps ^= CL_DOWN_ADB;
507 
508 	if (key != 0xff)
509 		akbd_input(sc, key);
510 }
511 
512 static inline int
513 akbd_iso_swap(int keycode)
514 {
515 	switch (keycode) {
516 	case 10:
517 		return (50);
518 	case 50:
519 		return (10);
520 	default:
521 		return (keycode);
522 	}
523 }
524 
525 int adb_polledkey;
526 void
527 akbd_input(struct akbd_softc *sc, int key)
528 {
529 	int press, val;
530 	int type;
531 
532 	press = ADBK_PRESS(key);
533 	val = ADBK_KEYVAL(key);
534 
535 	if (sc->sc_iso)
536 		val = akbd_iso_swap(val);
537 
538 	type = press ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP;
539 
540 	if (adb_polling) {
541 		adb_polledkey = key;
542 #ifdef WSDISPLAY_COMPAT_RAWKBD
543 	} else if (sc->sc_rawkbd) {
544 		char cbuf[2];
545 		int c, j, s;
546 
547 		j = 0;
548 
549 		c = keyboard[val];
550 		if (c == 0) {
551 			return; /* XXX */
552 		}
553 		if (c & 0x80)
554 			cbuf[j++] = 0xe0;
555 		cbuf[j] = c & 0x7f;
556 		if (type == WSCONS_EVENT_KEY_UP)
557 			cbuf[j] |= 0x80;
558 		j++;
559 		s = spltty();
560 		wskbd_rawinput(sc->sc_wskbddev, cbuf, j);
561 		splx(s);
562 #endif
563 	} else {
564 		wskbd_input(sc->sc_wskbddev, type, val);
565 	}
566 }
567