adb_kbd.c revision 1.1 1 /* $NetBSD: adb_kbd.c,v 1.1 2007/01/17 23:20:16 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: adb_kbd.c,v 1.1 2007/01/17 23:20:16 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/systm.h>
43 #include <sys/kernel.h>
44 #include <sys/sysctl.h>
45
46 #include <dev/wscons/wsconsio.h>
47 #include <dev/wscons/wskbdvar.h>
48 #include <dev/wscons/wsksymdef.h>
49 #include <dev/wscons/wsksymvar.h>
50 #include <dev/wscons/wsmousevar.h>
51
52 #include <machine/autoconf.h>
53 #define KEYBOARD_ARRAY
54 #include <machine/keyboard.h>
55 #include <machine/adbsys.h>
56
57 #include <dev/adb/adbvar.h>
58 #include <dev/adb/adb_keymap.h>
59
60 #include "adbdebug.h"
61
62 struct adbkbd_softc {
63 struct device sc_dev;
64 struct adb_device *sc_adbdev;
65 struct adb_bus_accessops *sc_ops;
66 struct device *sc_wskbddev;
67 struct device *sc_wsmousedev;
68 int sc_leds;
69 int sc_have_led_control;
70 int sc_msg_len;
71 int sc_event;
72 int sc_poll;
73 int sc_polled_chars;
74 int sc_trans[3];
75 int sc_capslock;
76 #ifdef WSDISPLAY_COMPAT_RAWKBD
77 int sc_rawkbd;
78 #endif
79 uint8_t sc_buffer[16];
80 uint8_t sc_pollbuf[16];
81 uint8_t sc_us;
82 };
83
84 /*
85 * Function declarations.
86 */
87 static int adbkbd_match(struct device *, struct cfdata *, void *);
88 static void adbkbd_attach(struct device *, struct device *, void *);
89
90 static void adbkbd_initleds(struct adbkbd_softc *);
91 static void adbkbd_keys(struct adbkbd_softc *, uint8_t, uint8_t);
92 static inline void adbkbd_key(struct adbkbd_softc *, uint8_t);
93 static int adbkbd_wait(struct adbkbd_softc *, int);
94
95 /* Driver definition. */
96 CFATTACH_DECL(adbkbd, sizeof(struct adbkbd_softc),
97 adbkbd_match, adbkbd_attach, NULL, NULL);
98
99 extern struct cfdriver akbd_cd;
100
101 static int adbkbd_enable(void *, int);
102 static int adbkbd_ioctl(void *, u_long, caddr_t, int, struct lwp *);
103 static void adbkbd_set_leds(void *, int);
104 static void adbkbd_handler(void *, int, uint8_t *);
105
106 struct wskbd_accessops adbkbd_accessops = {
107 adbkbd_enable,
108 adbkbd_set_leds,
109 adbkbd_ioctl,
110 };
111
112 static void adbkbd_cngetc(void *, u_int *, int *);
113 static void adbkbd_cnpollc(void *, int);
114
115 struct wskbd_consops adbkbd_consops = {
116 adbkbd_cngetc,
117 adbkbd_cnpollc,
118 };
119
120 struct wskbd_mapdata adbkbd_keymapdata = {
121 akbd_keydesctab,
122 #ifdef AKBD_LAYOUT
123 AKBD_LAYOUT,
124 #else
125 KB_US,
126 #endif
127 };
128
129 static int adbkms_enable(void *);
130 static int adbkms_ioctl(void *, u_long, caddr_t, int, struct lwp *);
131 static void adbkms_disable(void *);
132
133 const struct wsmouse_accessops adbkms_accessops = {
134 adbkms_enable,
135 adbkms_ioctl,
136 adbkms_disable,
137 };
138
139 static int adbkbd_sysctl_button(SYSCTLFN_ARGS);
140 static void adbkbd_setup_sysctl(struct adbkbd_softc *);
141
142 #ifdef ADBKBD_DEBUG
143 #define DPRINTF printf
144 #else
145 #define DPRINTF while (0) printf
146 #endif
147
148 static int adbkbd_is_console = 0;
149 static int adbkbd_console_attached = 0;
150
151 static int
152 adbkbd_match(parent, cf, aux)
153 struct device *parent;
154 struct cfdata *cf;
155 void *aux;
156 {
157 struct adb_attach_args *aaa = aux;
158
159 if (aaa->dev->original_addr == ADBADDR_KBD)
160 return 1;
161 else
162 return 0;
163 }
164
165 static void
166 adbkbd_attach(struct device *parent, struct device *self, void *aux)
167 {
168 struct adbkbd_softc *sc = (struct adbkbd_softc *)self;
169 struct adb_attach_args *aaa = aux;
170 short cmd;
171 struct wskbddev_attach_args a;
172 struct wsmousedev_attach_args am;
173
174 sc->sc_ops = aaa->ops;
175 sc->sc_adbdev = aaa->dev;
176 sc->sc_adbdev->cookie = sc;
177 sc->sc_adbdev->handler = adbkbd_handler;
178 sc->sc_us = ADBTALK(sc->sc_adbdev->current_addr, 0);
179
180 sc->sc_leds = 0; /* initially off */
181 sc->sc_have_led_control = 0;
182 sc->sc_msg_len = 0;
183 sc->sc_poll = 0;
184 sc->sc_capslock = 0;
185 sc->sc_trans[1] = 103; /* F11 */
186 sc->sc_trans[2] = 111; /* F12 */
187
188 printf(" addr %d ", sc->sc_adbdev->current_addr);
189
190 switch (sc->sc_adbdev->handler_id) {
191 case ADB_STDKBD:
192 printf("standard keyboard\n");
193 break;
194 case ADB_ISOKBD:
195 printf("standard keyboard (ISO layout)\n");
196 break;
197 case ADB_EXTKBD:
198 cmd = ADBTALK(sc->sc_adbdev->current_addr, 1);
199 sc->sc_msg_len = 0;
200 sc->sc_ops->send(sc->sc_ops->cookie, sc->sc_poll, cmd, 0, NULL);
201 adbkbd_wait(sc, 10);
202
203 /* Ignore Logitech MouseMan/Trackman pseudo keyboard */
204 /* XXX needs testing */
205 if (sc->sc_buffer[2] == 0x9a && sc->sc_buffer[3] == 0x20) {
206 printf("Mouseman (non-EMP) pseudo keyboard\n");
207 return;
208 } else if (sc->sc_buffer[2] == 0x9a &&
209 sc->sc_buffer[3] == 0x21) {
210 printf("Trackman (non-EMP) pseudo keyboard\n");
211 return;
212 } else {
213 printf("extended keyboard\n");
214 adbkbd_initleds(sc);
215 }
216 break;
217 case ADB_EXTISOKBD:
218 printf("extended keyboard (ISO layout)\n");
219 adbkbd_initleds(sc);
220 break;
221 case ADB_KBDII:
222 printf("keyboard II\n");
223 break;
224 case ADB_ISOKBDII:
225 printf("keyboard II (ISO layout)\n");
226 break;
227 case ADB_PBKBD:
228 printf("PowerBook keyboard\n");
229 break;
230 case ADB_PBISOKBD:
231 printf("PowerBook keyboard (ISO layout)\n");
232 break;
233 case ADB_ADJKPD:
234 printf("adjustable keypad\n");
235 break;
236 case ADB_ADJKBD:
237 printf("adjustable keyboard\n");
238 break;
239 case ADB_ADJISOKBD:
240 printf("adjustable keyboard (ISO layout)\n");
241 break;
242 case ADB_ADJJAPKBD:
243 printf("adjustable keyboard (Japanese layout)\n");
244 break;
245 case ADB_PBEXTISOKBD:
246 printf("PowerBook extended keyboard (ISO layout)\n");
247 break;
248 case ADB_PBEXTJAPKBD:
249 printf("PowerBook extended keyboard (Japanese layout)\n");
250 break;
251 case ADB_JPKBDII:
252 printf("keyboard II (Japanese layout)\n");
253 break;
254 case ADB_PBEXTKBD:
255 printf("PowerBook extended keyboard\n");
256 break;
257 case ADB_DESIGNKBD:
258 printf("extended keyboard\n");
259 adbkbd_initleds(sc);
260 break;
261 case ADB_PBJPKBD:
262 printf("PowerBook keyboard (Japanese layout)\n");
263 break;
264 case ADB_PBG3KBD:
265 printf("PowerBook G3 keyboard\n");
266 break;
267 case ADB_PBG3JPKBD:
268 printf("PowerBook G3 keyboard (Japanese layout)\n");
269 break;
270 case ADB_IBOOKKBD:
271 printf("iBook keyboard\n");
272 break;
273 default:
274 printf("mapped device (%d)\n", sc->sc_adbdev->handler_id);
275 break;
276 }
277
278 if (adbkbd_is_console && (adbkbd_console_attached == 0)) {
279 wskbd_cnattach(&adbkbd_consops, sc, &adbkbd_keymapdata);
280 adbkbd_console_attached = 1;
281 a.console = 1;
282 } else {
283 a.console = 0;
284 }
285 a.keymap = &adbkbd_keymapdata;
286 a.accessops = &adbkbd_accessops;
287 a.accesscookie = sc;
288
289 sc->sc_wskbddev = config_found_ia(self, "wskbddev", &a, wskbddevprint);
290
291 /* attach the mouse device */
292 am.accessops = &adbkms_accessops;
293 am.accesscookie = sc;
294 sc->sc_wsmousedev = config_found_ia(self, "wsmousedev", &am, wsmousedevprint);
295
296 if (sc->sc_wsmousedev != NULL)
297 adbkbd_setup_sysctl(sc);
298 }
299
300 static void
301 adbkbd_handler(void *cookie, int len, uint8_t *data)
302 {
303 struct adbkbd_softc *sc = cookie;
304
305 #ifdef ADBKBD_DEBUG
306 int i;
307 printf("%s: %02x - ", sc->sc_dev.dv_xname, sc->sc_us);
308 for (i = 0; i < len; i++) {
309 printf(" %02x", data[i]);
310 }
311 printf("\n");
312 #endif
313 if (len >= 2) {
314 if (data[1] == sc->sc_us) {
315 adbkbd_keys(sc, data[2], data[3]);
316 return;
317 } else {
318 memcpy(sc->sc_buffer, data, len);
319 }
320 sc->sc_msg_len = len;
321 wakeup(&sc->sc_event);
322 } else {
323 DPRINTF("bogus message\n");
324 }
325 }
326
327 static int
328 adbkbd_wait(struct adbkbd_softc *sc, int timeout)
329 {
330 int cnt = 0;
331
332 if (sc->sc_poll) {
333 while (sc->sc_msg_len == 0) {
334 sc->sc_ops->poll(sc->sc_ops->cookie);
335 }
336 } else {
337 while ((sc->sc_msg_len == 0) && (cnt < timeout)) {
338 tsleep(&sc->sc_event, 0, "adbkbdio", hz);
339 cnt++;
340 }
341 }
342 return (sc->sc_msg_len > 0);
343 }
344
345 static void
346 adbkbd_keys(struct adbkbd_softc *sc, uint8_t k1, uint8_t k2)
347 {
348 /* keyboard event processing */
349 DPRINTF("[%02x %02x]", k1, k2);
350 if (((k1 == k2) && (k1 == 0x7f)) || (k1 == 0x7e)) {
351 /* power button, handle separately */
352 #ifdef ADBKBD_POWER_PANIC
353 panic("power button pressed");
354 #endif
355 } else {
356 adbkbd_key(sc, k1);
357 if (k2 != 0xff)
358 adbkbd_key(sc, k2);
359 }
360 }
361
362 static inline void
363 adbkbd_key(struct adbkbd_softc *sc, uint8_t k)
364 {
365
366 if (sc->sc_poll) {
367 if (sc->sc_polled_chars >= 16) {
368 printf("%s: polling buffer is full\n",
369 sc->sc_dev.dv_xname);
370 }
371 sc->sc_pollbuf[sc->sc_polled_chars] = k;
372 sc->sc_polled_chars++;
373 return;
374 }
375
376 /* translate some keys to mouse events */
377 if (sc->sc_wsmousedev != NULL) {
378 if (ADBK_KEYVAL(k) == sc->sc_trans[1]) {
379 wsmouse_input(sc->sc_wsmousedev, ADBK_PRESS(k) ? 2 : 0,
380 0, 0, 0, 0,
381 WSMOUSE_INPUT_DELTA);
382 return;
383 }
384 if (ADBK_KEYVAL(k) == sc->sc_trans[2]) {
385 wsmouse_input(sc->sc_wsmousedev, ADBK_PRESS(k) ? 4 : 0,
386 0, 0, 0, 0,
387 WSMOUSE_INPUT_DELTA);
388 return;
389 }
390 }
391 #ifdef WSDISPLAY_COMPAT_RAWKBD
392 if (sc->sc_rawkbd) {
393 char cbuf[2];
394 int s;
395 int j = 0;
396 int c = keyboard[ADBK_KEYVAL(k)][3]
397
398 if (k & 0x80)
399 cbuf[j++] = 0xe0;
400
401 cbuf[j++] = (c & 0x7f) | (ADBK_PRESS(k)? 0 : 0x80);
402
403 s = spltty();
404 wskbd_rawinput(sc->sc_wskbddev, cbuf, j);
405 splx(s);
406 } else {
407 #endif
408
409 if (ADBK_KEYVAL(k) == 0x39) {
410 /* caps lock - send up and down */
411 if (ADBK_PRESS(k) != sc->sc_capslock) {
412 sc->sc_capslock = ADBK_PRESS(k);
413 wskbd_input(sc->sc_wskbddev,
414 WSCONS_EVENT_KEY_DOWN, 0x39);
415 wskbd_input(sc->sc_wskbddev,
416 WSCONS_EVENT_KEY_UP, 0x39);
417 }
418 } else {
419 /* normal event */
420 int type;
421
422 type = ADBK_PRESS(k) ?
423 WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP;
424 wskbd_input(sc->sc_wskbddev, type, ADBK_KEYVAL(k));
425 }
426 #ifdef WSDISPLAY_COMPAT_RAWKBD
427 }
428 #endif
429 }
430
431 /*
432 * Set the keyboard LED's.
433 *
434 * Automatically translates from ioctl/softc format to the
435 * actual keyboard register format
436 */
437 static void
438 adbkbd_set_leds(void *cookie, int leds)
439 {
440 struct adbkbd_softc *sc = cookie;
441 int aleds;
442 short cmd;
443 uint8_t buffer[2];
444
445 DPRINTF("adbkbd_set_leds: %02x\n", leds);
446 if ((leds & 0x07) == (sc->sc_leds & 0x07))
447 return;
448
449 if (sc->sc_have_led_control) {
450
451 aleds = (~leds & 0x04) | 3;
452 if (leds & 1)
453 aleds &= ~2;
454 if (leds & 2)
455 aleds &= ~1;
456
457 buffer[0] = 0xff;
458 buffer[1] = aleds | 0xf8;
459
460 cmd = ADBLISTEN(sc->sc_adbdev->current_addr, 2);
461 sc->sc_ops->send(sc->sc_ops->cookie, sc->sc_poll, cmd, 2, buffer);
462 }
463
464 sc->sc_leds = leds & 7;
465 }
466
467 static void
468 adbkbd_initleds(struct adbkbd_softc *sc)
469 {
470 short cmd;
471
472 /* talk R2 */
473 cmd = ADBTALK(sc->sc_adbdev->current_addr, 2);
474 sc->sc_msg_len = 0;
475 sc->sc_ops->send(sc->sc_ops->cookie, sc->sc_poll, cmd, 0, NULL);
476 if (!adbkbd_wait(sc, 10)) {
477 printf("unable to read LED state\n");
478 return;
479 }
480 sc->sc_have_led_control = 1;
481 DPRINTF("have LED control\n");
482 return;
483 }
484
485 static int
486 adbkbd_enable(void *v, int on)
487 {
488 return 0;
489 }
490
491 static int
492 adbkbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct lwp *l)
493 {
494 struct adbkbd_softc *sc = (struct adbkbd_softc *) v;
495
496 switch (cmd) {
497
498 case WSKBDIO_GTYPE:
499 *(int *)data = WSKBD_TYPE_ADB;
500 return 0;
501 case WSKBDIO_SETLEDS:
502 adbkbd_set_leds(sc, *(int *)data);
503 return 0;
504 case WSKBDIO_GETLEDS:
505 *(int *)data = sc->sc_leds;
506 return 0;
507 #ifdef WSDISPLAY_COMPAT_RAWKBD
508 case WSKBDIO_SETMODE:
509 sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
510 return 0;
511 #endif
512 }
513
514 return EPASSTHROUGH;
515 }
516
517 int
518 adbkbd_cnattach()
519 {
520
521 adbkbd_is_console = 1;
522 return 0;
523 }
524
525 static void
526 adbkbd_cngetc(void *v, u_int *type, int *data)
527 {
528 struct adbkbd_softc *sc = v;
529 int key, press, val;
530 int s;
531
532 s = splhigh();
533
534 KASSERT(sc->sc_poll);
535
536 DPRINTF("polling...");
537 while (sc->sc_polled_chars == 0) {
538 sc->sc_ops->poll(sc->sc_ops->cookie);
539 }
540 DPRINTF(" got one\n");
541 splx(s);
542
543 key = sc->sc_pollbuf[0];
544 sc->sc_polled_chars--;
545 memmove(sc->sc_pollbuf, sc->sc_pollbuf + 1,
546 sc->sc_polled_chars);
547
548 press = ADBK_PRESS(key);
549 val = ADBK_KEYVAL(key);
550
551 *data = val;
552 *type = press ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP;
553 }
554
555 static void
556 adbkbd_cnpollc(void *v, int on)
557 {
558 struct adbkbd_softc *sc = v;
559
560 sc->sc_poll = on;
561 if (!on) {
562 int i;
563
564 /* feed the poll buffer's content to wskbd */
565 for (i = 0; i < sc->sc_polled_chars; i++) {
566 adbkbd_key(sc, sc->sc_pollbuf[i]);
567 }
568 sc->sc_polled_chars = 0;
569 }
570 }
571
572 /* stuff for the pseudo mouse */
573 static int
574 adbkms_enable(void *v)
575 {
576 return 0;
577 }
578
579 static int
580 adbkms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct lwp *l)
581 {
582
583 switch (cmd) {
584 case WSMOUSEIO_GTYPE:
585 *(u_int *)data = WSMOUSE_TYPE_PSEUDO;
586 break;
587
588 default:
589 return (EPASSTHROUGH);
590 }
591 return (0);
592 }
593
594 static void
595 adbkms_disable(void *v)
596 {
597 }
598
599 static void
600 adbkbd_setup_sysctl(struct adbkbd_softc *sc)
601 {
602 struct sysctlnode *node, *me;
603 int ret;
604
605 DPRINTF("%s: sysctl setup\n", sc->sc_dev.dv_xname);
606 ret = sysctl_createv(NULL, 0, NULL, (const struct sysctlnode **)&me,
607 CTLFLAG_READWRITE,
608 CTLTYPE_NODE, sc->sc_dev.dv_xname, NULL,
609 NULL, 0, NULL, 0,
610 CTL_MACHDEP, CTL_CREATE, CTL_EOL);
611
612 ret = sysctl_createv(NULL, 0, NULL,
613 (const struct sysctlnode **)&node,
614 CTLFLAG_READWRITE | CTLFLAG_OWNDESC | CTLFLAG_IMMEDIATE,
615 CTLTYPE_INT, "middle", "middle mouse button", adbkbd_sysctl_button,
616 1, NULL, 0, CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL);
617 node->sysctl_data = sc;
618
619 ret = sysctl_createv(NULL, 0, NULL,
620 (const struct sysctlnode **)&node,
621 CTLFLAG_READWRITE | CTLFLAG_OWNDESC | CTLFLAG_IMMEDIATE,
622 CTLTYPE_INT, "right", "right mouse button", adbkbd_sysctl_button,
623 2, NULL, 0, CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL);
624 node->sysctl_data = sc;
625 }
626
627 static int
628 adbkbd_sysctl_button(SYSCTLFN_ARGS)
629 {
630 struct sysctlnode node = *rnode;
631 struct adbkbd_softc *sc=(struct adbkbd_softc *)node.sysctl_data;
632 const int *np = newp;
633 int btn = node.sysctl_idata, reg;
634
635 DPRINTF("adbkbd_sysctl_button %d\n", btn);
636 node.sysctl_idata = sc->sc_trans[btn];
637 reg = sc->sc_trans[btn];
638 if (np) {
639 /* we're asked to write */
640 node.sysctl_data = ®
641 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
642
643 sc->sc_trans[btn] = node.sysctl_idata;
644 return 0;
645 }
646 return EINVAL;
647 } else {
648 node.sysctl_size = 4;
649 return (sysctl_lookup(SYSCTLFN_CALL(&node)));
650 }
651 }
652
653 SYSCTL_SETUP(sysctl_adbkbdtrans_setup, "adbkbd translator setup")
654 {
655
656 sysctl_createv(NULL, 0, NULL, NULL,
657 CTLFLAG_PERMANENT,
658 CTLTYPE_NODE, "machdep", NULL,
659 NULL, 0, NULL, 0,
660 CTL_MACHDEP, CTL_EOL);
661 }
662