xref: /netbsd-src/sys/arch/macppc/dev/akbd.c (revision 3b01aba77a7a698587faaae455bbfe740923c1f5)
1 /*	$NetBSD: akbd.c,v 1.17 2001/07/26 11:36:41 tsubai Exp $	*/
2 
3 /*
4  * Copyright (C) 1998	Colin Wood
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 Colin Wood.
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 <sys/param.h>
34 #include <sys/device.h>
35 #include <sys/fcntl.h>
36 #include <sys/poll.h>
37 #include <sys/select.h>
38 #include <sys/proc.h>
39 #include <sys/signalvar.h>
40 #include <sys/systm.h>
41 
42 #include <dev/wscons/wsconsio.h>
43 #include <dev/wscons/wskbdvar.h>
44 #include <dev/wscons/wsksymdef.h>
45 #include <dev/wscons/wsksymvar.h>
46 #include <dev/ofw/openfirm.h>
47 
48 #include <machine/autoconf.h>
49 #define KEYBOARD_ARRAY
50 #include <machine/keyboard.h>
51 
52 #include <macppc/dev/adbvar.h>
53 #include <macppc/dev/aedvar.h>
54 #include <macppc/dev/akbdmap.h>
55 #include <macppc/dev/akbdvar.h>
56 #include <macppc/dev/pm_direct.h>
57 
58 #include "aed.h"
59 
60 /*
61  * Function declarations.
62  */
63 static int	akbdmatch __P((struct device *, struct cfdata *, void *));
64 static void	akbdattach __P((struct device *, struct device *, void *));
65 void		kbd_adbcomplete __P((caddr_t buffer, caddr_t data_area, int adb_command));
66 static void	kbd_processevent __P((adb_event_t *event, struct akbd_softc *));
67 #ifdef notyet
68 static u_char	getleds __P((int));
69 static int	setleds __P((struct akbd_softc *, u_char));
70 static void	blinkleds __P((struct akbd_softc *));
71 #endif
72 
73 /* Driver definition. */
74 struct cfattach akbd_ca = {
75 	sizeof(struct akbd_softc), akbdmatch, akbdattach
76 };
77 
78 extern struct cfdriver akbd_cd;
79 
80 int akbd_enable __P((void *, int));
81 void akbd_set_leds __P((void *, int));
82 int akbd_ioctl __P((void *, u_long, caddr_t, int, struct proc *));
83 
84 struct wskbd_accessops akbd_accessops = {
85 	akbd_enable,
86 	akbd_set_leds,
87 	akbd_ioctl,
88 };
89 
90 void akbd_cngetc __P((void *, u_int *, int *));
91 void akbd_cnpollc __P((void *, int));
92 
93 struct wskbd_consops akbd_consops = {
94 	akbd_cngetc,
95 	akbd_cnpollc,
96 };
97 
98 struct wskbd_mapdata akbd_keymapdata = {
99 	akbd_keydesctab,
100 #ifdef AKBD_LAYOUT
101 	AKBD_LAYOUT,
102 #else
103 	KB_US,
104 #endif
105 };
106 
107 static int akbd_is_console;
108 static int pcmcia_soft_eject;
109 
110 static int
111 akbdmatch(parent, cf, aux)
112 	struct device *parent;
113 	struct cfdata *cf;
114 	void   *aux;
115 {
116 	struct adb_attach_args *aa_args = aux;
117 
118 	if (aa_args->origaddr == ADBADDR_KBD)
119 		return 1;
120 	else
121 		return 0;
122 }
123 
124 static void
125 akbdattach(parent, self, aux)
126 	struct device *parent, *self;
127 	void   *aux;
128 {
129 	ADBSetInfoBlock adbinfo;
130 	struct akbd_softc *sc = (struct akbd_softc *)self;
131 	struct adb_attach_args *aa_args = aux;
132 	int error, kbd_done;
133 	short cmd;
134 	u_char buffer[9];
135 	struct wskbddev_attach_args a;
136 
137 	/* ohare based models have soft ejectable card slot. */
138 	if (OF_finddevice("/bandit/ohare") != -1)
139 		pcmcia_soft_eject = 1;
140 
141 	sc->origaddr = aa_args->origaddr;
142 	sc->adbaddr = aa_args->adbaddr;
143 	sc->handler_id = aa_args->handler_id;
144 
145 	sc->sc_leds = (u_int8_t)0x00;	/* initially off */
146 
147 	adbinfo.siServiceRtPtr = (Ptr)kbd_adbcomplete;
148 	adbinfo.siDataAreaAddr = (caddr_t)sc;
149 
150 	switch (sc->handler_id) {
151 	case ADB_STDKBD:
152 		printf("standard keyboard\n");
153 		break;
154 	case ADB_ISOKBD:
155 		printf("standard keyboard (ISO layout)\n");
156 		break;
157 	case ADB_EXTKBD:
158 		cmd = ADBTALK(sc->adbaddr, 1);
159 		kbd_done =
160 		    (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd) == 0);
161 
162 		/* Ignore Logitech MouseMan/Trackman pseudo keyboard */
163 		if (kbd_done && buffer[1] == 0x9a && buffer[2] == 0x20) {
164 			printf("Mouseman (non-EMP) pseudo keyboard\n");
165 			adbinfo.siServiceRtPtr = (Ptr)0;
166 			adbinfo.siDataAreaAddr = (Ptr)0;
167 		} else if (kbd_done && buffer[1] == 0x9a && buffer[2] == 0x21) {
168 			printf("Trackman (non-EMP) pseudo keyboard\n");
169 			adbinfo.siServiceRtPtr = (Ptr)0;
170 			adbinfo.siDataAreaAddr = (Ptr)0;
171 		} else {
172 			printf("extended keyboard\n");
173 #ifdef notyet
174 			blinkleds(sc);
175 #endif
176 		}
177 		break;
178 	case ADB_EXTISOKBD:
179 		printf("extended keyboard (ISO layout)\n");
180 #ifdef notyet
181 		blinkleds(sc);
182 #endif
183 		break;
184 	case ADB_KBDII:
185 		printf("keyboard II\n");
186 		break;
187 	case ADB_ISOKBDII:
188 		printf("keyboard II (ISO layout)\n");
189 		break;
190 	case ADB_PBKBD:
191 		printf("PowerBook keyboard\n");
192 		break;
193 	case ADB_PBISOKBD:
194 		printf("PowerBook keyboard (ISO layout)\n");
195 		break;
196 	case ADB_ADJKPD:
197 		printf("adjustable keypad\n");
198 		break;
199 	case ADB_ADJKBD:
200 		printf("adjustable keyboard\n");
201 		break;
202 	case ADB_ADJISOKBD:
203 		printf("adjustable keyboard (ISO layout)\n");
204 		break;
205 	case ADB_ADJJAPKBD:
206 		printf("adjustable keyboard (Japanese layout)\n");
207 		break;
208 	case ADB_PBEXTISOKBD:
209 		printf("PowerBook extended keyboard (ISO layout)\n");
210 		break;
211 	case ADB_PBEXTJAPKBD:
212 		printf("PowerBook extended keyboard (Japanese layout)\n");
213 		break;
214 	case ADB_JPKBDII:
215 		printf("keyboard II (Japanese layout)\n");
216 		break;
217 	case ADB_PBEXTKBD:
218 		printf("PowerBook extended keyboard\n");
219 		break;
220 	case ADB_DESIGNKBD:
221 		printf("extended keyboard\n");
222 #ifdef notyet
223 		blinkleds(sc);
224 #endif
225 		break;
226 	case ADB_PBJPKBD:
227 		printf("PowerBook keyboard (Japanese layout)\n");
228 		break;
229 	case ADB_PBG3KBD:
230 		printf("PowerBook G3 keyboard\n");
231 		break;
232 	case ADB_PBG3JPKBD:
233 		printf("PowerBook G3 keyboard (Japanese layout)\n");
234 		break;
235 	default:
236 		printf("mapped device (%d)\n", sc->handler_id);
237 		break;
238 	}
239 	error = SetADBInfo(&adbinfo, sc->adbaddr);
240 #ifdef ADB_DEBUG
241 	if (adb_debug)
242 		printf("akbd: returned %d from SetADBInfo\n", error);
243 #endif
244 
245 	a.console = akbd_is_console;
246 	a.keymap = &akbd_keymapdata;
247 	a.accessops = &akbd_accessops;
248 	a.accesscookie = sc;
249 
250 	sc->sc_wskbddev = config_found(self, &a, wskbddevprint);
251 }
252 
253 
254 /*
255  * Handle putting the keyboard data received from the ADB into
256  * an ADB event record.
257  */
258 void
259 kbd_adbcomplete(buffer, data_area, adb_command)
260 	caddr_t buffer;
261 	caddr_t data_area;
262 	int adb_command;
263 {
264 	adb_event_t event;
265 	struct akbd_softc *ksc;
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 	ksc = (struct akbd_softc *)data_area;
276 
277 	event.addr = adbaddr;
278 	event.hand_id = ksc->handler_id;
279 	event.def_addr = ksc->origaddr;
280 	event.byte_count = buffer[0];
281 	memcpy(event.bytes, buffer + 1, event.byte_count);
282 
283 #ifdef ADB_DEBUG
284 	if (adb_debug) {
285 		printf("akbd: from %d at %d (org %d) %d:", event.addr,
286 		    event.hand_id, event.def_addr, buffer[0]);
287 		for (i = 1; i <= buffer[0]; i++)
288 			printf(" %x", buffer[i]);
289 		printf("\n");
290 	}
291 #endif
292 
293 	microtime(&event.timestamp);
294 
295 	kbd_processevent(&event, ksc);
296 }
297 
298 /*
299  * Given a keyboard ADB event, record the keycodes and call the key
300  * repeat handler, optionally passing the event through the mouse
301  * button emulation handler first.
302  */
303 static void
304 kbd_processevent(event, ksc)
305         adb_event_t *event;
306         struct akbd_softc *ksc;
307 {
308         adb_event_t new_event;
309 
310         new_event = *event;
311 	new_event.u.k.key = event->bytes[0];
312 	new_event.bytes[1] = 0xff;
313 	kbd_intr(&new_event);
314 #if NAED > 0
315 	aed_input(&new_event);
316 #endif
317 	if (event->bytes[1] != 0xff) {
318 		new_event.u.k.key = event->bytes[1];
319 		new_event.bytes[0] = event->bytes[1];
320 		new_event.bytes[1] = 0xff;
321 		kbd_intr(&new_event);
322 #if NAED > 0
323 		aed_input(&new_event);
324 #endif
325 	}
326 
327 }
328 
329 #ifdef notyet
330 /*
331  * Get the actual hardware LED state and convert it to softc format.
332  */
333 static u_char
334 getleds(addr)
335 	int	addr;
336 {
337 	short cmd;
338 	u_char buffer[9], leds;
339 
340 	leds = 0x00;	/* all off */
341 	buffer[0] = 0;
342 
343 	/* talk R2 */
344 	cmd = ADBTALK(addr, 2);
345 	if (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd) == 0 &&
346 	    buffer[0] > 0)
347 		leds = ~(buffer[2]) & 0x07;
348 
349 	return (leds);
350 }
351 
352 /*
353  * Set the keyboard LED's.
354  *
355  * Automatically translates from ioctl/softc format to the
356  * actual keyboard register format
357  */
358 static int
359 setleds(ksc, leds)
360 	struct akbd_softc *ksc;
361 	u_char	leds;
362 {
363 	int addr;
364 	short cmd;
365 	u_char buffer[9];
366 
367 	if ((leds & 0x07) == (ksc->sc_leds & 0x07))
368 		return (0);
369 
370 	addr = ksc->adbaddr;
371 	buffer[0] = 0;
372 
373 	cmd = ADBTALK(addr, 2);
374 	if (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd) || buffer[0] == 0)
375 		return (EIO);
376 
377 	leds = ~leds & 0x07;
378 	buffer[2] &= 0xf8;
379 	buffer[2] |= leds;
380 
381 	cmd = ADBLISTEN(addr, 2);
382 	adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd);
383 
384 	cmd = ADBTALK(addr, 2);
385 	if (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd) || buffer[0] == 0)
386 		return (EIO);
387 
388 	ksc->sc_leds = ~((u_int8_t)buffer[2]) & 0x07;
389 
390 	if ((buffer[2] & 0xf8) != leds)
391 		return (EIO);
392 	else
393 		return (0);
394 }
395 
396 /*
397  * Toggle all of the LED's on and off, just for show.
398  */
399 static void
400 blinkleds(ksc)
401 	struct akbd_softc *ksc;
402 {
403 	int addr, i;
404 	u_char blinkleds, origleds;
405 
406 	addr = ksc->adbaddr;
407 	origleds = getleds(addr);
408 	blinkleds = LED_NUMLOCK | LED_CAPSLOCK | LED_SCROLL_LOCK;
409 
410 	(void)setleds(ksc, blinkleds);
411 
412 	for (i = 0; i < 10000; i++)
413 		delay(50);
414 
415 	/* make sure that we restore the LED settings */
416 	i = 10;
417 	do {
418 		(void)setleds(ksc, (u_char)0x00);
419 	} while (setleds(ksc, (u_char)0x00) && (i-- > 0));
420 
421 	return;
422 }
423 #endif
424 
425 int
426 akbd_enable(v, on)
427 	void *v;
428 	int on;
429 {
430 	return 0;
431 }
432 
433 void
434 akbd_set_leds(v, on)
435 	void *v;
436 	int on;
437 {
438 }
439 
440 int
441 akbd_ioctl(v, cmd, data, flag, p)
442 	void *v;
443 	u_long cmd;
444 	caddr_t data;
445 	int flag;
446 	struct proc *p;
447 {
448 	switch (cmd) {
449 
450 	case WSKBDIO_GTYPE:
451 		*(int *)data = 0;		/* XXX */
452 		return 0;
453 	case WSKBDIO_SETLEDS:
454 		return 0;
455 	case WSKBDIO_GETLEDS:
456 		*(int *)data = 0;
457 		return 0;
458 	}
459 	/* kbdioctl(...); */
460 
461 	return -1;
462 }
463 
464 static int polledkey;
465 extern int adb_polling;
466 
467 int
468 kbd_intr(arg)
469 	void *arg;
470 {
471 	adb_event_t *event = arg;
472 	int key, press, val;
473 	int type;
474 
475 	struct akbd_softc *sc = akbd_cd.cd_devs[0];
476 
477 	key = event->u.k.key;
478 	press = ADBK_PRESS(key);
479 	val = ADBK_KEYVAL(key);
480 
481 	type = press ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP;
482 
483 	switch (key) {
484 	case 57:	/* Caps Lock pressed */
485 	case 185:	/* Caps Lock released */
486 		type = WSCONS_EVENT_KEY_DOWN;
487 		wskbd_input(sc->sc_wskbddev, type, val);
488 		type = WSCONS_EVENT_KEY_UP;
489 		break;
490 	case 245:
491 		if (pcmcia_soft_eject)
492 			pm_eject_pcmcia(0);
493 		break;
494 	case 244:
495 		if (pcmcia_soft_eject)
496 			pm_eject_pcmcia(1);
497 		break;
498 	}
499 
500 	if (adb_polling)
501 		polledkey = key;
502 	else
503 		wskbd_input(sc->sc_wskbddev, type, val);
504 
505 	return 0;
506 }
507 
508 int
509 akbd_cnattach()
510 {
511 
512 	akbd_is_console = 1;
513 	wskbd_cnattach(&akbd_consops, NULL, &akbd_keymapdata);
514 	return 0;
515 }
516 
517 void
518 akbd_cngetc(v, type, data)
519 	void *v;
520 	u_int *type;
521 	int *data;
522 {
523 	int key, press, val;
524 	int s;
525 
526 	s = splhigh();
527 
528 	polledkey = -1;
529 	adb_polling = 1;
530 
531 	while (polledkey == -1) {
532 		adb_intr();
533 		DELAY(10000);				/* XXX */
534 	}
535 
536 	adb_polling = 0;
537 	splx(s);
538 
539 	key = polledkey;
540 	press = ADBK_PRESS(key);
541 	val = ADBK_KEYVAL(key);
542 
543 	*data = val;
544 	*type = press ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP;
545 }
546 
547 void
548 akbd_cnpollc(v, on)
549 	void *v;
550 	int on;
551 {
552 }
553