1 /* $NetBSD: pckbd.c,v 1.26 2009/03/08 15:06:56 ad Exp $ */
4 * Copyright (c) 1998, 2009 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Charles M. Hannum.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * Copyright (c) 1990 The Regents of the University of California.
34 * All rights reserved.
36 * This code is derived from software contributed to Berkeley by
37 * William Jolitz and Don Ahn.
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
42 * 1. Redistributions of source code must retain the above copyright
43 * notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in the
46 * documentation and/or other materials provided with the distribution.
47 * 3. Neither the name of the University nor the names of its contributors
48 * may be used to endorse or promote products derived from this software
49 * without specific prior written permission.
51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63 * @(#)pccons.c 5.11 (Berkeley) 5/21/91
67 * code to work keyboard for PC-style console
70 #include <sys/cdefs.h>
71 __KERNEL_RCSID(0, "$NetBSD: pckbd.c,v 1.26 2009/03/08 15:06:56 ad Exp $");
73 #include <sys/param.h>
74 #include <sys/systm.h>
75 #include <sys/device.h>
76 #include <sys/malloc.h>
77 #include <sys/ioctl.h>
81 #include <dev/pckbport/pckbportvar.h>
83 #include <dev/wscons/wsconsio.h>
84 #include <dev/wscons/wskbdvar.h>
85 #include <dev/wscons/wsksymdef.h>
86 #include <dev/wscons/wsksymvar.h>
88 #include <dev/pckbport/pckbdreg.h>
89 #include <dev/pckbport/pckbdvar.h>
90 #include <dev/pckbport/wskbdmap_mfii.h>
94 #include "opt_pckbd_layout.h"
95 #include "opt_pckbd_cnattach_may_fail.h"
96 #include "opt_wsdisplay_compat.h"
98 struct pckbd_internal
{
100 pckbport_tag_t t_kbctag
;
101 pckbport_slot_t t_kbcslot
;
107 struct pckbd_softc
*t_sc
; /* back pointer */
113 struct pckbd_internal
*id
;
118 device_t sc_wskbddev
;
119 #ifdef WSDISPLAY_COMPAT_RAWKBD
124 static int pckbd_is_console(pckbport_tag_t
, pckbport_slot_t
);
126 int pckbdprobe(device_t
, cfdata_t
, void *);
127 void pckbdattach(device_t
, device_t
, void *);
129 CFATTACH_DECL_NEW(pckbd
, sizeof(struct pckbd_softc
),
130 pckbdprobe
, pckbdattach
, NULL
, NULL
);
132 int pckbd_enable(void *, int);
133 void pckbd_set_leds(void *, int);
134 int pckbd_ioctl(void *, u_long
, void *, int, struct lwp
*);
136 const struct wskbd_accessops pckbd_accessops
= {
142 void pckbd_cngetc(void *, u_int
*, int *);
143 void pckbd_cnpollc(void *, int);
144 void pckbd_cnbell(void *, u_int
, u_int
, u_int
);
146 const struct wskbd_consops pckbd_consops
= {
152 const struct wskbd_mapdata pckbd_keymapdata
= {
162 * Hackish support for a bell on the PC Keyboard; when a suitable feeper
163 * is found, it attaches itself into the pckbd driver here.
165 void (*pckbd_bell_fn
)(void *, u_int
, u_int
, u_int
, int);
166 void *pckbd_bell_fn_arg
;
168 void pckbd_bell(u_int
, u_int
, u_int
, int);
170 int pckbd_set_xtscancode(pckbport_tag_t
, pckbport_slot_t
);
171 int pckbd_init(struct pckbd_internal
*, pckbport_tag_t
, pckbport_slot_t
,
173 void pckbd_input(void *, int);
175 static int pckbd_decode(struct pckbd_internal
*, int, u_int
*, int *);
176 static int pckbd_led_encode(int);
177 static int pckbd_led_decode(int);
179 struct pckbd_internal pckbd_consdata
;
182 pckbd_set_xtscancode(pckbport_tag_t kbctag
, pckbport_slot_t kbcslot
)
188 * Some keyboard/8042 combinations do not seem to work if the keyboard
189 * is set to table 1; in fact, it would appear that some keyboards just
190 * ignore the command altogether. So by default, we use the AT scan
191 * codes and have the 8042 translate them. Unfortunately, this is
192 * known to not work on some PS/2 machines. We try desperately to deal
193 * with this by checking the (lack of a) translate bit in the 8042 and
194 * attempting to set the keyboard to XT mode. If this all fails, well,
197 * XXX It would perhaps be a better choice to just use AT scan codes
198 * and not bother with this.
200 if (pckbport_xt_translation(kbctag
, kbcslot
, 1)) {
201 /* The 8042 is translating for us; use AT codes. */
202 cmd
[0] = KBC_SETTABLE
;
204 res
= pckbport_poll_cmd(kbctag
, kbcslot
, cmd
, 2, 0, 0, 0);
207 aprint_debug("pckbd: error setting scanset 2\n");
209 * XXX at least one keyboard is reported to lock up
210 * if a "set table" is attempted, thus the "reset".
211 * XXX ignore errors, scanset 2 should be
215 (void)pckbport_poll_cmd(kbctag
, kbcslot
, cmdb
, 1, 1, 0, 1);
216 pckbport_flush(kbctag
, kbcslot
);
220 /* Stupid 8042; set keyboard to XT codes. */
221 cmd
[0] = KBC_SETTABLE
;
223 res
= pckbport_poll_cmd(kbctag
, kbcslot
, cmd
, 2, 0, 0, 0);
225 aprint_debug("pckbd: error setting scanset 1\n");
231 pckbd_is_console(pckbport_tag_t tag
, pckbport_slot_t slot
)
234 return pckbd_consdata
.t_isconsole
&&
235 tag
== pckbd_consdata
.t_kbctag
&& slot
== pckbd_consdata
.t_kbcslot
;
239 pckbd_suspend(device_t dv PMF_FN_ARGS
)
241 struct pckbd_softc
*sc
= device_private(dv
);
245 /* XXX duped from pckbd_enable, but we want to disable
246 * it even if it's the console kbd
248 cmd
[0] = KBC_DISABLE
;
249 res
= pckbport_enqueue_cmd(sc
->id
->t_kbctag
,
250 sc
->id
->t_kbcslot
, cmd
, 1, 0, 1, 0);
254 pckbport_slot_enable(sc
->id
->t_kbctag
,
255 sc
->id
->t_kbcslot
, 0);
262 pckbd_resume(device_t dv PMF_FN_ARGS
)
264 struct pckbd_softc
*sc
= device_private(dv
);
265 u_char cmd
[1], resp
[1];
268 /* XXX jmcneill reset the keyboard */
269 pckbport_flush(sc
->id
->t_kbctag
, sc
->id
->t_kbcslot
);
272 res
= pckbport_poll_cmd(sc
->id
->t_kbctag
,
273 sc
->id
->t_kbcslot
, cmd
, 1, 1, resp
, 1);
275 aprint_debug("pckbdprobe: reset error %d\n", res
);
276 if (resp
[0] != KBR_RSTDONE
)
277 printf("pckbdprobe: reset response 0x%x\n",
280 pckbport_flush(sc
->id
->t_kbctag
, sc
->id
->t_kbcslot
);
288 * these are both bad jokes
291 pckbdprobe(device_t parent
, cfdata_t cf
, void *aux
)
293 struct pckbport_attach_args
*pa
= aux
;
295 u_char cmd
[1], resp
[1];
298 * XXX There are rumours that a keyboard can be connected
299 * to the aux port as well. For me, this didn't work.
300 * For further experiments, allow it if explicitly
301 * wired in the config file.
303 if ((pa
->pa_slot
!= PCKBPORT_KBD_SLOT
) &&
304 (cf
->cf_loc
[PCKBPORTCF_SLOT
] == PCKBPORTCF_SLOT_DEFAULT
))
307 /* Flush any garbage. */
308 pckbport_flush(pa
->pa_tag
, pa
->pa_slot
);
310 /* Reset the keyboard. */
312 res
= pckbport_poll_cmd(pa
->pa_tag
, pa
->pa_slot
, cmd
, 1, 1, resp
, 1);
314 aprint_debug("pckbdprobe: reset error %d\n", res
);
316 * There is probably no keyboard connected.
317 * Let the probe succeed if the keyboard is used
318 * as console input - it can be connected later.
320 return pckbd_is_console(pa
->pa_tag
, pa
->pa_slot
) ? 1 : 0;
322 if (resp
[0] != KBR_RSTDONE
) {
323 printf("pckbdprobe: reset response 0x%x\n", resp
[0]);
328 * Some keyboards seem to leave a second ack byte after the reset.
329 * This is kind of stupid, but we account for them anyway by just
330 * flushing the buffer.
332 pckbport_flush(pa
->pa_tag
, pa
->pa_slot
);
334 if (pckbd_set_xtscancode(pa
->pa_tag
, pa
->pa_slot
))
341 pckbdattach(device_t parent
, device_t self
, void *aux
)
343 struct pckbd_softc
*sc
= device_private(self
);
344 struct pckbport_attach_args
*pa
= aux
;
345 struct wskbddev_attach_args a
;
353 isconsole
= pckbd_is_console(pa
->pa_tag
, pa
->pa_slot
);
356 sc
->id
= &pckbd_consdata
;
359 * Some keyboards are not enabled after a reset,
360 * so make sure it is enabled now.
363 (void) pckbport_poll_cmd(sc
->id
->t_kbctag
, sc
->id
->t_kbcslot
,
367 sc
->id
= malloc(sizeof(struct pckbd_internal
),
369 (void) pckbd_init(sc
->id
, pa
->pa_tag
, pa
->pa_slot
, 0);
371 /* no interrupts until enabled */
372 cmd
[0] = KBC_DISABLE
;
373 (void) pckbport_poll_cmd(sc
->id
->t_kbctag
, sc
->id
->t_kbcslot
,
380 pckbport_set_inputhandler(sc
->id
->t_kbctag
, sc
->id
->t_kbcslot
,
381 pckbd_input
, sc
, device_xname(sc
->sc_dev
));
383 a
.console
= isconsole
;
385 a
.keymap
= &pckbd_keymapdata
;
387 a
.accessops
= &pckbd_accessops
;
390 if (!pmf_device_register(self
, pckbd_suspend
, pckbd_resume
))
391 aprint_error_dev(self
, "couldn't establish power handler\n");
394 * Attach the wskbd, saving a handle to it.
397 sc
->sc_wskbddev
= config_found_ia(self
, "wskbddev", &a
, wskbddevprint
);
401 pckbd_enable(void *v
, int on
)
403 struct pckbd_softc
*sc
= v
;
408 if (sc
->sc_enabled
) {
409 aprint_debug("pckbd_enable: bad enable\n");
413 pckbport_slot_enable(sc
->id
->t_kbctag
, sc
->id
->t_kbcslot
, 1);
416 res
= pckbport_poll_cmd(sc
->id
->t_kbctag
, sc
->id
->t_kbcslot
,
419 printf("pckbd_enable: command error\n");
423 res
= pckbd_set_xtscancode(sc
->id
->t_kbctag
,
430 if (sc
->id
->t_isconsole
)
433 cmd
[0] = KBC_DISABLE
;
434 res
= pckbport_enqueue_cmd(sc
->id
->t_kbctag
, sc
->id
->t_kbcslot
,
437 printf("pckbd_disable: command error\n");
441 pckbport_slot_enable(sc
->id
->t_kbctag
, sc
->id
->t_kbcslot
, 0);
450 pckbd_decode(struct pckbd_internal
*id
, int datain
, u_int
*type
, int *dataout
)
454 if (datain
== KBR_EXTENDED0
) {
457 } else if (datain
== KBR_EXTENDED1
) {
462 if (id
->t_extended0
== 1) {
463 switch (datain
& 0x7f) {
473 /* map extended keys to (unused) codes 128-254 */
474 key
= (datain
& 0x7f) | (id
->t_extended0
? 0x80 : 0);
478 * process PAUSE (also break) key (EXT1 1D 45 EXT1 9D C5):
479 * map to (unused) code 7F
481 if (id
->t_extended1
== 2 && (datain
== 0x1d || datain
== 0x9d)) {
484 } else if (id
->t_extended1
== 1 &&
485 (datain
== 0x45 || datain
== 0xc5)) {
488 } else if (id
->t_extended1
> 0) {
494 *type
= WSCONS_EVENT_KEY_UP
;
496 /* Always ignore typematic keys */
497 if (key
== id
->t_lastchar
)
499 id
->t_lastchar
= key
;
500 *type
= WSCONS_EVENT_KEY_DOWN
;
508 pckbd_init(struct pckbd_internal
*t
, pckbport_tag_t kbctag
,
509 pckbport_slot_t kbcslot
, int console
)
512 memset(t
, 0, sizeof(struct pckbd_internal
));
514 t
->t_isconsole
= console
;
515 t
->t_kbctag
= kbctag
;
516 t
->t_kbcslot
= kbcslot
;
518 return pckbd_set_xtscancode(kbctag
, kbcslot
);
522 pckbd_led_encode(int led
)
528 if (led
& WSKBD_LED_SCROLL
)
530 if (led
& WSKBD_LED_NUM
)
532 if (led
& WSKBD_LED_CAPS
)
538 pckbd_led_decode(int led
)
544 res
|= WSKBD_LED_SCROLL
;
546 res
|= WSKBD_LED_NUM
;
548 res
|= WSKBD_LED_CAPS
;
553 pckbd_set_leds(void *v
, int leds
)
555 struct pckbd_softc
*sc
= v
;
558 cmd
[0] = KBC_MODEIND
;
559 cmd
[1] = pckbd_led_encode(leds
);
560 sc
->sc_ledstate
= cmd
[1];
562 (void)pckbport_enqueue_cmd(sc
->id
->t_kbctag
, sc
->id
->t_kbcslot
,
567 * Got a console receive interrupt -
568 * the console processor wants to give us a character.
571 pckbd_input(void *vsc
, int data
)
573 struct pckbd_softc
*sc
= vsc
;
577 #ifdef WSDISPLAY_COMPAT_RAWKBD
580 wskbd_rawinput(sc
->sc_wskbddev
, &d
, 1);
584 if (pckbd_decode(sc
->id
, data
, &type
, &key
))
585 wskbd_input(sc
->sc_wskbddev
, type
, key
);
589 pckbd_ioctl(void *v
, u_long cmd
, void *data
, int flag
,
592 struct pckbd_softc
*sc
= v
;
596 *(int *)data
= WSKBD_TYPE_PC_XT
;
598 case WSKBDIO_SETLEDS
:
603 cmdb
[0] = KBC_MODEIND
;
604 cmdb
[1] = pckbd_led_encode(*(int *)data
);
605 sc
->sc_ledstate
= cmdb
[1];
606 res
= pckbport_enqueue_cmd(sc
->id
->t_kbctag
, sc
->id
->t_kbcslot
,
610 case WSKBDIO_GETLEDS
:
611 *(int *)data
= pckbd_led_decode(sc
->sc_ledstate
);
613 case WSKBDIO_COMPLEXBELL
:
614 #define d ((struct wskbd_bell_data *)data)
616 * Keyboard can't beep directly; we have an
617 * externally-provided global hook to do this.
619 pckbd_bell(d
->pitch
, d
->period
, d
->volume
, 0);
622 #ifdef WSDISPLAY_COMPAT_RAWKBD
623 case WSKBDIO_SETMODE
:
624 sc
->rawkbd
= (*(int *)data
== WSKBD_RAW
);
632 pckbd_bell(u_int pitch
, u_int period
, u_int volume
, int poll
)
635 if (pckbd_bell_fn
!= NULL
)
636 (*pckbd_bell_fn
)(pckbd_bell_fn_arg
, pitch
, period
,
641 pckbd_unhook_bell(void (*fn
)(void *, u_int
, u_int
, u_int
, int), void *arg
)
643 if (pckbd_bell_fn
!= fn
&& pckbd_bell_fn_arg
!= arg
)
645 pckbd_bell_fn
= NULL
;
646 pckbd_bell_fn_arg
= NULL
;
650 pckbd_hookup_bell(void (*fn
)(void *, u_int
, u_int
, u_int
, int), void *arg
)
653 if (pckbd_bell_fn
== NULL
) {
655 pckbd_bell_fn_arg
= arg
;
660 pckbd_cnattach(pckbport_tag_t kbctag
, int kbcslot
)
665 res
= pckbd_init(&pckbd_consdata
, kbctag
, kbcslot
, 1);
666 /* We may allow the console to be attached if no keyboard is present */
667 #if defined(PCKBD_CNATTACH_MAY_FAIL)
672 /* Just to be sure. */
674 res
= pckbport_poll_cmd(kbctag
, kbcslot
, cmd
, 1, 0, 0, 0);
676 #if defined(PCKBD_CNATTACH_MAY_FAIL)
681 wskbd_cnattach(&pckbd_consops
, &pckbd_consdata
, &pckbd_keymapdata
);
688 pckbd_cngetc(void *v
, u_int
*type
, int *data
)
690 struct pckbd_internal
*t
= v
;
694 val
= pckbport_poll_data(t
->t_kbctag
, t
->t_kbcslot
);
695 if ((val
!= -1) && pckbd_decode(t
, val
, type
, data
))
701 pckbd_cnpollc(void *v
, int on
)
703 struct pckbd_internal
*t
= v
;
705 pckbport_set_poll(t
->t_kbctag
, t
->t_kbcslot
, on
);
709 pckbd_cnbell(void *v
, u_int pitch
, u_int period
, u_int volume
)
712 pckbd_bell(pitch
, period
, volume
, 1);