1 /* $NetBSD: adb_kbd.c,v 1.12 2008/03/26 18:04:15 matt Exp $ */
4 * Copyright (C) 1998 Colin Wood
5 * Copyright (C) 2006, 2007 Michael Lorenz
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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.
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: adb_kbd.c,v 1.12 2008/03/26 18:04:15 matt Exp $");
37 #include <sys/param.h>
38 #include <sys/device.h>
39 #include <sys/fcntl.h>
41 #include <sys/select.h>
43 #include <sys/systm.h>
44 #include <sys/kernel.h>
45 #include <sys/sysctl.h>
47 #include <dev/wscons/wsconsio.h>
48 #include <dev/wscons/wskbdvar.h>
49 #include <dev/wscons/wsksymdef.h>
50 #include <dev/wscons/wsksymvar.h>
51 #include <dev/wscons/wsmousevar.h>
53 #include <dev/sysmon/sysmonvar.h>
54 #include <dev/sysmon/sysmon_taskq.h>
56 #include <machine/autoconf.h>
57 #include <machine/keyboard.h>
58 #include <machine/adbsys.h>
60 #include <dev/adb/adbvar.h>
61 #include <dev/adb/adb_keymap.h>
63 #include "opt_wsdisplay_compat.h"
69 struct adb_device
*sc_adbdev
;
70 struct adb_bus_accessops
*sc_ops
;
73 device_t sc_wsmousedev
;
75 struct sysmon_pswitch sc_sm_pbutton
;
77 int sc_have_led_control
;
84 uint32_t sc_timestamp
;
85 #ifdef WSDISPLAY_COMPAT_RAWKBD
88 uint8_t sc_buffer
[16];
89 uint8_t sc_pollbuf
[16];
91 uint8_t sc_power
, sc_pe
;
95 * Function declarations.
97 static int adbkbd_match(device_t
, cfdata_t
, void *);
98 static void adbkbd_attach(device_t
, device_t
, void *);
100 static void adbkbd_initleds(struct adbkbd_softc
*);
101 static void adbkbd_keys(struct adbkbd_softc
*, uint8_t, uint8_t);
102 static inline void adbkbd_key(struct adbkbd_softc
*, uint8_t);
103 static int adbkbd_wait(struct adbkbd_softc
*, int);
105 /* Driver definition. */
106 CFATTACH_DECL_NEW(adbkbd
, sizeof(struct adbkbd_softc
),
107 adbkbd_match
, adbkbd_attach
, NULL
, NULL
);
109 extern struct cfdriver adbkbd_cd
;
111 static int adbkbd_enable(void *, int);
112 static int adbkbd_ioctl(void *, u_long
, void *, int, struct lwp
*);
113 static void adbkbd_set_leds(void *, int);
114 static void adbkbd_handler(void *, int, uint8_t *);
115 static void adbkbd_powerbutton(void *);
117 struct wskbd_accessops adbkbd_accessops
= {
123 static void adbkbd_cngetc(void *, u_int
*, int *);
124 static void adbkbd_cnpollc(void *, int);
126 struct wskbd_consops adbkbd_consops
= {
131 struct wskbd_mapdata adbkbd_keymapdata
= {
141 static int adbkms_enable(void *);
142 static int adbkms_ioctl(void *, u_long
, void *, int, struct lwp
*);
143 static void adbkms_disable(void *);
145 const struct wsmouse_accessops adbkms_accessops
= {
151 static int adbkbd_sysctl_button(SYSCTLFN_ARGS
);
152 static void adbkbd_setup_sysctl(struct adbkbd_softc
*);
154 #endif /* NWSMOUSE > 0 */
157 #define DPRINTF printf
159 #define DPRINTF while (0) printf
162 static int adbkbd_is_console
= 0;
163 static int adbkbd_console_attached
= 0;
166 adbkbd_match(device_t parent
, cfdata_t cf
, void *aux
)
168 struct adb_attach_args
*aaa
= aux
;
170 if (aaa
->dev
->original_addr
== ADBADDR_KBD
)
177 adbkbd_attach(device_t parent
, device_t self
, void *aux
)
179 struct adbkbd_softc
*sc
= device_private(self
);
180 struct adb_attach_args
*aaa
= aux
;
182 struct wskbddev_attach_args a
;
184 struct wsmousedev_attach_args am
;
188 sc
->sc_ops
= aaa
->ops
;
189 sc
->sc_adbdev
= aaa
->dev
;
190 sc
->sc_adbdev
->cookie
= sc
;
191 sc
->sc_adbdev
->handler
= adbkbd_handler
;
192 sc
->sc_us
= ADBTALK(sc
->sc_adbdev
->current_addr
, 0);
194 sc
->sc_leds
= 0; /* initially off */
195 sc
->sc_have_led_control
= 0;
199 sc
->sc_trans
[1] = 103; /* F11 */
200 sc
->sc_trans
[2] = 111; /* F12 */
202 sc
->sc_timestamp
= 0;
204 printf(" addr %d: ", sc
->sc_adbdev
->current_addr
);
206 switch (sc
->sc_adbdev
->handler_id
) {
208 printf("standard keyboard\n");
211 printf("standard keyboard (ISO layout)\n");
214 cmd
= ADBTALK(sc
->sc_adbdev
->current_addr
, 1);
216 sc
->sc_ops
->send(sc
->sc_ops
->cookie
, sc
->sc_poll
, cmd
, 0, NULL
);
219 /* Ignore Logitech MouseMan/Trackman pseudo keyboard */
220 /* XXX needs testing */
221 if (sc
->sc_buffer
[2] == 0x9a && sc
->sc_buffer
[3] == 0x20) {
222 printf("Mouseman (non-EMP) pseudo keyboard\n");
224 } else if (sc
->sc_buffer
[2] == 0x9a &&
225 sc
->sc_buffer
[3] == 0x21) {
226 printf("Trackman (non-EMP) pseudo keyboard\n");
229 printf("extended keyboard\n");
234 printf("extended keyboard (ISO layout)\n");
238 printf("keyboard II\n");
241 printf("keyboard II (ISO layout)\n");
244 printf("PowerBook keyboard\n");
248 printf("PowerBook keyboard (ISO layout)\n");
252 printf("adjustable keypad\n");
255 printf("adjustable keyboard\n");
258 printf("adjustable keyboard (ISO layout)\n");
261 printf("adjustable keyboard (Japanese layout)\n");
263 case ADB_PBEXTISOKBD
:
264 printf("PowerBook extended keyboard (ISO layout)\n");
267 case ADB_PBEXTJAPKBD
:
268 printf("PowerBook extended keyboard (Japanese layout)\n");
272 printf("keyboard II (Japanese layout)\n");
275 printf("PowerBook extended keyboard\n");
279 printf("extended keyboard\n");
283 printf("PowerBook keyboard (Japanese layout)\n");
287 printf("PowerBook G3 keyboard\n");
291 printf("PowerBook G3 keyboard (Japanese layout)\n");
295 printf("iBook keyboard\n");
298 printf("mapped device (%d)\n", sc
->sc_adbdev
->handler_id
);
302 if (adbkbd_is_console
&& (adbkbd_console_attached
== 0)) {
303 wskbd_cnattach(&adbkbd_consops
, sc
, &adbkbd_keymapdata
);
304 adbkbd_console_attached
= 1;
309 a
.keymap
= &adbkbd_keymapdata
;
310 a
.accessops
= &adbkbd_accessops
;
313 sc
->sc_wskbddev
= config_found_ia(self
, "wskbddev", &a
, wskbddevprint
);
316 /* attach the mouse device */
317 am
.accessops
= &adbkms_accessops
;
318 am
.accesscookie
= sc
;
319 sc
->sc_wsmousedev
= config_found_ia(self
, "wsmousedev", &am
,
322 if (sc
->sc_wsmousedev
!= NULL
)
323 adbkbd_setup_sysctl(sc
);
326 /* finally register the power button */
327 sysmon_task_queue_init();
328 memset(&sc
->sc_sm_pbutton
, 0, sizeof(struct sysmon_pswitch
));
329 sc
->sc_sm_pbutton
.smpsw_name
= device_xname(sc
->sc_dev
);
330 sc
->sc_sm_pbutton
.smpsw_type
= PSWITCH_TYPE_POWER
;
331 if (sysmon_pswitch_register(&sc
->sc_sm_pbutton
) != 0)
332 aprint_error_dev(sc
->sc_dev
,
333 "unable to register power button with sysmon\n");
337 adbkbd_handler(void *cookie
, int len
, uint8_t *data
)
339 struct adbkbd_softc
*sc
= cookie
;
343 printf("%s: %02x - ", device_xname(sc
->sc_dev
), sc
->sc_us
);
344 for (i
= 0; i
< len
; i
++) {
345 printf(" %02x", data
[i
]);
350 if (data
[1] == sc
->sc_us
) {
351 adbkbd_keys(sc
, data
[2], data
[3]);
354 memcpy(sc
->sc_buffer
, data
, len
);
356 sc
->sc_msg_len
= len
;
357 wakeup(&sc
->sc_event
);
359 DPRINTF("bogus message\n");
364 adbkbd_wait(struct adbkbd_softc
*sc
, int timeout
)
369 while (sc
->sc_msg_len
== 0) {
370 sc
->sc_ops
->poll(sc
->sc_ops
->cookie
);
373 while ((sc
->sc_msg_len
== 0) && (cnt
< timeout
)) {
374 tsleep(&sc
->sc_event
, 0, "adbkbdio", hz
);
378 return (sc
->sc_msg_len
> 0);
382 adbkbd_keys(struct adbkbd_softc
*sc
, uint8_t k1
, uint8_t k2
)
385 /* keyboard event processing */
387 DPRINTF("[%02x %02x]", k1
, k2
);
389 if (((k1
== k2
) && (k1
== 0x7f)) || (k1
== sc
->sc_power
)) {
390 uint32_t now
= time_second
;
391 uint32_t diff
= now
- sc
->sc_timestamp
;
393 sc
->sc_timestamp
= now
;
394 if ((diff
> 1) && (diff
< 5)) {
396 /* power button, report to sysmon */
399 sysmon_task_queue_sched(0, adbkbd_powerbutton
, sc
);
410 adbkbd_powerbutton(void *cookie
)
412 struct adbkbd_softc
*sc
= cookie
;
414 sysmon_pswitch_event(&sc
->sc_sm_pbutton
,
415 ADBK_PRESS(sc
->sc_pe
) ? PSWITCH_EVENT_PRESSED
:
416 PSWITCH_EVENT_RELEASED
);
420 adbkbd_key(struct adbkbd_softc
*sc
, uint8_t k
)
424 if (sc
->sc_polled_chars
>= 16) {
425 aprint_error_dev(sc
->sc_dev
,"polling buffer is full\n");
427 sc
->sc_pollbuf
[sc
->sc_polled_chars
] = k
;
428 sc
->sc_polled_chars
++;
433 /* translate some keys to mouse events */
434 if (sc
->sc_wsmousedev
!= NULL
) {
435 if (ADBK_KEYVAL(k
) == sc
->sc_trans
[1]) {
436 wsmouse_input(sc
->sc_wsmousedev
, ADBK_PRESS(k
) ? 2 : 0,
438 WSMOUSE_INPUT_DELTA
);
441 if (ADBK_KEYVAL(k
) == sc
->sc_trans
[2]) {
442 wsmouse_input(sc
->sc_wsmousedev
, ADBK_PRESS(k
) ? 4 : 0,
444 WSMOUSE_INPUT_DELTA
);
450 #ifdef WSDISPLAY_COMPAT_RAWKBD
458 wskbd_rawinput(sc
->sc_wskbddev
, cbuf
, 1);
463 if (ADBK_KEYVAL(k
) == 0x39) {
464 /* caps lock - send up and down */
465 if (ADBK_PRESS(k
) != sc
->sc_capslock
) {
466 sc
->sc_capslock
= ADBK_PRESS(k
);
467 wskbd_input(sc
->sc_wskbddev
,
468 WSCONS_EVENT_KEY_DOWN
, 0x39);
469 wskbd_input(sc
->sc_wskbddev
,
470 WSCONS_EVENT_KEY_UP
, 0x39);
476 type
= ADBK_PRESS(k
) ?
477 WSCONS_EVENT_KEY_DOWN
: WSCONS_EVENT_KEY_UP
;
478 wskbd_input(sc
->sc_wskbddev
, type
, ADBK_KEYVAL(k
));
480 #ifdef WSDISPLAY_COMPAT_RAWKBD
486 * Set the keyboard LED's.
488 * Automatically translates from ioctl/softc format to the
489 * actual keyboard register format
492 adbkbd_set_leds(void *cookie
, int leds
)
494 struct adbkbd_softc
*sc
= cookie
;
499 DPRINTF("adbkbd_set_leds: %02x\n", leds
);
500 if ((leds
& 0x07) == (sc
->sc_leds
& 0x07))
503 if (sc
->sc_have_led_control
) {
505 aleds
= (~leds
& 0x04) | 3;
512 buffer
[1] = aleds
| 0xf8;
514 cmd
= ADBLISTEN(sc
->sc_adbdev
->current_addr
, 2);
515 sc
->sc_ops
->send(sc
->sc_ops
->cookie
, sc
->sc_poll
, cmd
, 2,
519 sc
->sc_leds
= leds
& 7;
523 adbkbd_initleds(struct adbkbd_softc
*sc
)
528 cmd
= ADBTALK(sc
->sc_adbdev
->current_addr
, 2);
530 sc
->sc_ops
->send(sc
->sc_ops
->cookie
, sc
->sc_poll
, cmd
, 0, NULL
);
531 if (!adbkbd_wait(sc
, 10)) {
532 printf("unable to read LED state\n");
535 sc
->sc_have_led_control
= 1;
536 DPRINTF("have LED control\n");
541 adbkbd_enable(void *v
, int on
)
547 adbkbd_ioctl(void *v
, u_long cmd
, void *data
, int flag
, struct lwp
*l
)
549 struct adbkbd_softc
*sc
= (struct adbkbd_softc
*) v
;
554 *(int *)data
= WSKBD_TYPE_ADB
;
556 case WSKBDIO_SETLEDS
:
557 adbkbd_set_leds(sc
, *(int *)data
);
559 case WSKBDIO_GETLEDS
:
560 *(int *)data
= sc
->sc_leds
;
562 #ifdef WSDISPLAY_COMPAT_RAWKBD
563 case WSKBDIO_SETMODE
:
564 sc
->sc_rawkbd
= *(int *)data
== WSKBD_RAW
;
573 adbkbd_cnattach(void)
576 adbkbd_is_console
= 1;
581 adbkbd_cngetc(void *v
, u_int
*type
, int *data
)
583 struct adbkbd_softc
*sc
= v
;
589 KASSERT(sc
->sc_poll
);
591 DPRINTF("polling...");
592 while (sc
->sc_polled_chars
== 0) {
593 sc
->sc_ops
->poll(sc
->sc_ops
->cookie
);
595 DPRINTF(" got one\n");
598 key
= sc
->sc_pollbuf
[0];
599 sc
->sc_polled_chars
--;
600 memmove(sc
->sc_pollbuf
, sc
->sc_pollbuf
+ 1,
601 sc
->sc_polled_chars
);
603 press
= ADBK_PRESS(key
);
604 val
= ADBK_KEYVAL(key
);
607 *type
= press
? WSCONS_EVENT_KEY_DOWN
: WSCONS_EVENT_KEY_UP
;
611 adbkbd_cnpollc(void *v
, int on
)
613 struct adbkbd_softc
*sc
= v
;
619 /* feed the poll buffer's content to wskbd */
620 for (i
= 0; i
< sc
->sc_polled_chars
; i
++) {
621 adbkbd_key(sc
, sc
->sc_pollbuf
[i
]);
623 sc
->sc_polled_chars
= 0;
628 /* stuff for the pseudo mouse */
630 adbkms_enable(void *v
)
636 adbkms_ioctl(void *v
, u_long cmd
, void *data
, int flag
, struct lwp
*l
)
640 case WSMOUSEIO_GTYPE
:
641 *(u_int
*)data
= WSMOUSE_TYPE_PSEUDO
;
645 return (EPASSTHROUGH
);
651 adbkms_disable(void *v
)
656 adbkbd_setup_sysctl(struct adbkbd_softc
*sc
)
658 struct sysctlnode
*node
, *me
;
661 DPRINTF("%s: sysctl setup\n", device_xname(sc
->sc_dev
));
662 ret
= sysctl_createv(NULL
, 0, NULL
, (const struct sysctlnode
**)&me
,
664 CTLTYPE_NODE
, device_xname(sc
->sc_dev
), NULL
,
666 CTL_MACHDEP
, CTL_CREATE
, CTL_EOL
);
668 ret
= sysctl_createv(NULL
, 0, NULL
,
669 (const struct sysctlnode
**)&node
,
670 CTLFLAG_READWRITE
| CTLFLAG_OWNDESC
| CTLFLAG_IMMEDIATE
,
671 CTLTYPE_INT
, "middle", "middle mouse button", adbkbd_sysctl_button
,
672 1, NULL
, 0, CTL_MACHDEP
, me
->sysctl_num
, CTL_CREATE
,
674 node
->sysctl_data
= sc
;
676 ret
= sysctl_createv(NULL
, 0, NULL
,
677 (const struct sysctlnode
**)&node
,
678 CTLFLAG_READWRITE
| CTLFLAG_OWNDESC
| CTLFLAG_IMMEDIATE
,
679 CTLTYPE_INT
, "right", "right mouse button", adbkbd_sysctl_button
,
680 2, NULL
, 0, CTL_MACHDEP
, me
->sysctl_num
, CTL_CREATE
,
682 node
->sysctl_data
= sc
;
686 adbkbd_sysctl_button(SYSCTLFN_ARGS
)
688 struct sysctlnode node
= *rnode
;
689 struct adbkbd_softc
*sc
=(struct adbkbd_softc
*)node
.sysctl_data
;
690 const int *np
= newp
;
691 int btn
= node
.sysctl_idata
, reg
;
693 DPRINTF("adbkbd_sysctl_button %d\n", btn
);
694 node
.sysctl_idata
= sc
->sc_trans
[btn
];
695 reg
= sc
->sc_trans
[btn
];
697 /* we're asked to write */
698 node
.sysctl_data
= ®
;
699 if (sysctl_lookup(SYSCTLFN_CALL(&node
)) == 0) {
701 sc
->sc_trans
[btn
] = node
.sysctl_idata
;
706 node
.sysctl_size
= 4;
707 return (sysctl_lookup(SYSCTLFN_CALL(&node
)));
711 SYSCTL_SETUP(sysctl_adbkbdtrans_setup
, "adbkbd translator setup")
714 sysctl_createv(NULL
, 0, NULL
, NULL
,
716 CTLTYPE_NODE
, "machdep", NULL
,
718 CTL_MACHDEP
, CTL_EOL
);
720 #endif /* NWSMOUSE > 0 */