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