1/* $NetBSD: pckbd.c,v 1.39 2024/02/09 22:08:36 andvar Exp $ */
2
3/*-
4 * Copyright (c) 1998, 2009 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Charles M. Hannum.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
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.
18 *
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.
30 */
31
32/*-
33 * Copyright (c) 1990 The Regents of the University of California.
34 * All rights reserved.
35 *
36 * This code is derived from software contributed to Berkeley by
37 * William Jolitz and Don Ahn.
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
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.
50 *
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
61 * SUCH DAMAGE.
62 *
63 *	@(#)pccons.c	5.11 (Berkeley) 5/21/91
64 */
65
66/*
67 * code to work keyboard for PC-style console
68 */
69
70#include <sys/cdefs.h>
71__KERNEL_RCSID(0, "$NetBSD: pckbd.c,v 1.39 2024/02/09 22:08:36 andvar Exp $");
72
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>
78
79#include <sys/bus.h>
80
81#include <dev/pckbport/pckbportvar.h>
82
83#include <dev/wscons/wsconsio.h>
84#include <dev/wscons/wskbdvar.h>
85#include <dev/wscons/wsksymdef.h>
86#include <dev/wscons/wsksymvar.h>
87
88#include <dev/pckbport/pckbdreg.h>
89#include <dev/pckbport/pckbdvar.h>
90#include <dev/pckbport/wskbdmap_mfii.h>
91
92#include "locators.h"
93
94#include "opt_pckbd_layout.h"
95#include "opt_pckbd_cnattach_may_fail.h"
96#include "opt_wsdisplay_compat.h"
97
98struct pckbd_internal {
99	int t_isconsole;
100	pckbport_tag_t t_kbctag;
101	pckbport_slot_t t_kbcslot;
102
103	int t_translating;
104
105	int t_lastchar;
106	int t_extended0;
107	int t_extended1;
108	int t_releasing;
109
110	struct pckbd_softc *t_sc; /* back pointer */
111};
112
113struct pckbd_softc {
114        device_t sc_dev;
115
116	struct pckbd_internal *id;
117	int sc_enabled;
118
119	int sc_ledstate;
120
121	device_t sc_wskbddev;
122#ifdef WSDISPLAY_COMPAT_RAWKBD
123	int rawkbd;
124#endif
125};
126
127int pckbdprobe(device_t, cfdata_t, void *);
128void pckbdattach(device_t, device_t, void *);
129
130CFATTACH_DECL_NEW(pckbd, sizeof(struct pckbd_softc),
131    pckbdprobe, pckbdattach, NULL, NULL);
132
133int	pckbd_enable(void *, int);
134void	pckbd_set_leds(void *, int);
135int	pckbd_ioctl(void *, u_long, void *, int, struct lwp *);
136
137const struct wskbd_accessops pckbd_accessops = {
138	.enable = pckbd_enable,
139	.set_leds = pckbd_set_leds,
140	.ioctl = pckbd_ioctl,
141};
142
143void	pckbd_cngetc(void *, u_int *, int *);
144void	pckbd_cnpollc(void *, int);
145void	pckbd_cnbell(void *, u_int, u_int, u_int);
146
147const struct wskbd_consops pckbd_consops = {
148	.getc = pckbd_cngetc,
149	.pollc = pckbd_cnpollc,
150	.bell = pckbd_cnbell,
151};
152
153const struct wskbd_mapdata pckbd_keymapdata = {
154	.keydesc = pckbd_keydesctab,
155#ifdef PCKBD_LAYOUT
156	.layout = PCKBD_LAYOUT,
157#else
158	.layout = KB_US,
159#endif
160};
161
162/*
163 * Hackish support for a bell on the PC Keyboard; when a suitable beeper
164 * is found, it attaches itself into the pckbd driver here.
165 */
166void	(*pckbd_bell_fn)(void *, u_int, u_int, u_int, int);
167void	*pckbd_bell_fn_arg;
168
169void	pckbd_bell(u_int, u_int, u_int, int);
170
171int	pckbd_scancode_translate(struct pckbd_internal *, int);
172int	pckbd_set_xtscancode(pckbport_tag_t, pckbport_slot_t,
173	    struct pckbd_internal *);
174int	pckbd_init(struct pckbd_internal *, pckbport_tag_t, pckbport_slot_t,
175			int);
176void	pckbd_input(void *, int);
177
178struct pckbd_internal pckbd_consdata;
179
180int
181pckbd_set_xtscancode(pckbport_tag_t kbctag, pckbport_slot_t kbcslot,
182    struct pckbd_internal *id)
183{
184	int xt, res = 0;
185	u_char cmd[2];
186
187	/*
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,
195	 * tough luck.  If the PCKBC_CANT_TRANSLATE pckbc flag was set, we
196	 * enable software translation.
197	 *
198	 * XXX It would perhaps be a better choice to just use AT scan codes
199	 * and not bother with this.
200	 */
201	xt = pckbport_xt_translation(kbctag, kbcslot, 1);
202	if (xt == 1) {
203		/* The 8042 is translating for us; use AT codes. */
204		cmd[0] = KBC_SETTABLE;
205		cmd[1] = 2;
206		res = pckbport_poll_cmd(kbctag, kbcslot, cmd, 2, 0, 0, 0);
207		if (res) {
208			u_char cmdb[1];
209			aprint_debug("%s: error setting scanset 2\n", __func__);
210			/*
211			 * XXX at least one keyboard is reported to lock up
212			 * if a "set table" is attempted, thus the "reset".
213			 * XXX ignore errors, scanset 2 should be
214			 * default anyway.
215			 */
216			cmdb[0] = KBC_RESET;
217			(void)pckbport_poll_cmd(kbctag, kbcslot, cmdb, 1, 1,
218			    0, 1);
219			pckbport_flush(kbctag, kbcslot);
220			res = 0;
221		}
222		if (id != NULL)
223			id->t_translating = 1;
224	} else if (xt == -1) {
225		/* Software translation required */
226		if (id != NULL)
227			id->t_translating = 0;
228	} else {
229		/* Stupid 8042; set keyboard to XT codes. */
230		cmd[0] = KBC_SETTABLE;
231		cmd[1] = 1;
232		res = pckbport_poll_cmd(kbctag, kbcslot, cmd, 2, 0, 0, 0);
233		if (res)
234			aprint_debug("%s: error setting scanset 1\n", __func__);
235		if (id != NULL)
236			id->t_translating = 1;
237	}
238	return res;
239}
240
241static int
242pckbd_is_console(pckbport_tag_t tag, pckbport_slot_t slot)
243{
244
245	return pckbd_consdata.t_isconsole &&
246	    tag == pckbd_consdata.t_kbctag && slot == pckbd_consdata.t_kbcslot;
247}
248
249static bool
250pckbd_suspend(device_t dv, const pmf_qual_t *qual)
251{
252	struct pckbd_softc *sc = device_private(dv);
253	u_char cmd[1];
254	int res;
255
256	/* XXX duped from pckbd_enable, but we want to disable
257	 *     it even if it's the console kbd
258	 */
259	cmd[0] = KBC_DISABLE;
260	res = pckbport_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot,
261	    cmd, 1, 0, 1, 0);
262	if (res)
263		return false;
264
265	pckbport_slot_enable(sc->id->t_kbctag,
266	    sc->id->t_kbcslot, 0);
267
268	sc->sc_enabled = 0;
269	return true;
270}
271
272static bool
273pckbd_resume(device_t dv, const pmf_qual_t *qual)
274{
275	struct pckbd_softc *sc = device_private(dv);
276	u_char cmd[1], resp[1];
277	int res;
278
279	/* XXX jmcneill reset the keyboard */
280	pckbport_flush(sc->id->t_kbctag, sc->id->t_kbcslot);
281
282	cmd[0] = KBC_RESET;
283	res = pckbport_poll_cmd(sc->id->t_kbctag,
284	    sc->id->t_kbcslot, cmd, 1, 1, resp, 1);
285	if (res)
286		aprint_debug("%s: reset error %d\n", __func__, res);
287	if (resp[0] != KBR_RSTDONE)
288		printf("%s: reset response 0x%x\n", __func__, resp[0]);
289
290	pckbport_flush(sc->id->t_kbctag, sc->id->t_kbcslot);
291
292	pckbd_enable(sc, 1);
293
294	return true;
295}
296
297/*
298 * these are three bad jokes
299 */
300static bool
301check_keyboard_by_id(struct pckbport_attach_args *pa)
302{
303	u_char cmd[1], resp[2];
304	int res;
305
306	cmd[0] = KBC_GETID;
307	res = pckbport_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 0);
308	if (res) {
309		aprint_debug("%s: getid failed with %d\n", __func__, res);
310		return false;
311	}
312
313	switch (resp[0]) {
314	case 0xab: case 0xac:	/* Regular and NCD Sun keyboards */
315	case 0x2b: case 0x5d:	/* Trust keyboard, raw and translated */
316	case 0x60: case 0x47:	/* NMB SGI keyboard, raw and translated */
317		return true;
318	default:
319		aprint_debug("%s: getid returned %#x\n", __func__, resp[0]);
320		return false;
321	}
322
323}
324
325int
326pckbdprobe(device_t parent, cfdata_t cf, void *aux)
327{
328	struct pckbport_attach_args *pa = aux;
329	int res;
330	u_char cmd[1], resp[1];
331
332	/*
333	 * XXX There are rumours that a keyboard can be connected
334	 * to the aux port as well. For me, this didn't work.
335	 * For further experiments, allow it if explicitly
336	 * wired in the config file.
337	 */
338	if ((pa->pa_slot != PCKBPORT_KBD_SLOT) &&
339	    (cf->cf_loc[PCKBPORTCF_SLOT] == PCKBPORTCF_SLOT_DEFAULT))
340		return 0;
341
342	/* Flush any garbage. */
343	pckbport_flush(pa->pa_tag, pa->pa_slot);
344
345	/* Reset the keyboard. */
346	cmd[0] = KBC_RESET;
347	res = pckbport_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 1, resp, 1);
348	if (res) {
349		aprint_debug("%s: reset error %d\n", __func__, res);
350
351		/*
352		 * On Chromebooks reset fails but otherwise the controller
353		 * works fine.
354		 * Check keyboard IDs similar to Linux and Haiku.
355		 * FreeBSD's approach here is to skip probing if
356		 * coreboot is detected which is suboptimal as coreboot
357		 * also supports some mac models which have no PC controller
358		 */
359		if (check_keyboard_by_id(pa)) {
360			if (pckbd_set_xtscancode(pa->pa_tag, pa->pa_slot, NULL))
361				return 0;
362			return 2;
363		}
364
365		/*
366		 * There is probably no keyboard connected.
367		 * Let the probe succeed if the keyboard is used
368		 * as console input - it can be connected later.
369		 */
370		return pckbd_is_console(pa->pa_tag, pa->pa_slot) ? 1 : 0;
371	}
372	if (resp[0] != KBR_RSTDONE) {
373		printf("%s: reset response %#x\n", __func__, resp[0]);
374
375		if (check_keyboard_by_id(pa)) {
376			if (pckbd_set_xtscancode(pa->pa_tag, pa->pa_slot, NULL))
377				return 0;
378			return 2;
379		}
380
381		return 0;
382	}
383
384	/*
385	 * Some keyboards seem to leave a second ack byte after the reset.
386	 * This is kind of stupid, but we account for them anyway by just
387	 * flushing the buffer.
388	 */
389	pckbport_flush(pa->pa_tag, pa->pa_slot);
390
391	if (pckbd_set_xtscancode(pa->pa_tag, pa->pa_slot, NULL))
392		return 0;
393
394	return 2;
395}
396
397void
398pckbdattach(device_t parent, device_t self, void *aux)
399{
400	struct pckbd_softc *sc = device_private(self);
401	struct pckbport_attach_args *pa = aux;
402	struct wskbddev_attach_args a;
403	int isconsole;
404	u_char cmd[1];
405
406	aprint_naive("\n");
407	aprint_normal("\n");
408
409	sc->sc_dev = self;
410	isconsole = pckbd_is_console(pa->pa_tag, pa->pa_slot);
411
412	if (isconsole) {
413		sc->id = &pckbd_consdata;
414
415		/*
416		 * Some keyboards are not enabled after a reset,
417		 * so make sure it is enabled now.
418		 */
419		cmd[0] = KBC_ENABLE;
420		(void) pckbport_poll_cmd(sc->id->t_kbctag, sc->id->t_kbcslot,
421		    cmd, 1, 0, 0, 0);
422		sc->sc_enabled = 1;
423	} else {
424		sc->id = malloc(sizeof(*sc->id), M_DEVBUF, M_WAITOK);
425		(void) pckbd_init(sc->id, pa->pa_tag, pa->pa_slot, 0);
426
427		/* no interrupts until enabled */
428		cmd[0] = KBC_DISABLE;
429		(void) pckbport_poll_cmd(sc->id->t_kbctag, sc->id->t_kbcslot,
430		    cmd, 1, 0, 0, 0);
431		sc->sc_enabled = 0;
432	}
433
434	sc->id->t_sc = sc;
435
436	pckbport_set_inputhandler(sc->id->t_kbctag, sc->id->t_kbcslot,
437			       pckbd_input, sc, device_xname(sc->sc_dev));
438
439	a.console = isconsole;
440
441	a.keymap = &pckbd_keymapdata;
442
443	a.accessops = &pckbd_accessops;
444	a.accesscookie = sc;
445
446	if (!pmf_device_register(self, pckbd_suspend, pckbd_resume))
447		aprint_error_dev(self, "couldn't establish power handler\n");
448
449	/*
450	 * Attach the wskbd, saving a handle to it.
451	 * XXX XXX XXX
452	 */
453	sc->sc_wskbddev = config_found(self, &a, wskbddevprint, CFARGS_NONE);
454}
455
456int
457pckbd_enable(void *v, int on)
458{
459	struct pckbd_softc *sc = v;
460	int res;
461	u_char cmd[1];
462
463	if (on) {
464		if (sc->sc_enabled) {
465			aprint_debug("%s: bad enable\n", __func__);
466			return EBUSY;
467		}
468
469		pckbport_slot_enable(sc->id->t_kbctag, sc->id->t_kbcslot, 1);
470
471		cmd[0] = KBC_ENABLE;
472		res = pckbport_poll_cmd(sc->id->t_kbctag, sc->id->t_kbcslot,
473		    cmd, 1, 0, NULL, 0);
474		if (res) {
475			printf("%s: command error\n", __func__);
476			return res;
477		}
478
479		res = pckbd_set_xtscancode(sc->id->t_kbctag,
480		    sc->id->t_kbcslot, sc->id);
481		if (res)
482			return res;
483
484		sc->sc_enabled = 1;
485	} else {
486		if (sc->id->t_isconsole)
487			return EBUSY;
488
489		cmd[0] = KBC_DISABLE;
490		res = pckbport_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot,
491		    cmd, 1, 0, 1, 0);
492		if (res) {
493			printf("%s: command error\n", __func__);
494			return res;
495		}
496
497		pckbport_slot_enable(sc->id->t_kbctag, sc->id->t_kbcslot, 0);
498
499		sc->sc_enabled = 0;
500	}
501
502	return 0;
503}
504
505const u_int8_t pckbd_xtbl[] = {
506/* 0x00 */
507	0,
508	0x43,		/* F9 */
509	0x89,		/* SunStop */
510	0x3f,		/* F5 */
511	0x3d,		/* F3 */
512	0x3b,		/* F1 */
513	0x3c,		/* F2 */
514	0x58,		/* F12 */
515	0,
516	0x44,		/* F10 */
517	0x42,		/* F8 */
518	0x40,		/* F6 */
519	0x3e,		/* F4 */
520	0x0f,		/* Tab */
521	0x29,		/* ` ~ */
522	0,
523/* 0x10 */
524	0,
525	0x38,		/* Left Alt */
526	0x2a,		/* Left Shift */
527	0,
528	0x1d,		/* Left Ctrl */
529	0x10,		/* q */
530	0x02,		/* 1 ! */
531	0,
532	0,
533	0,
534	0x2c,		/* z */
535	0x1f,		/* s */
536	0x1e,		/* a */
537	0x11,		/* w */
538	0x03,		/* 2 @ */
539	0,
540/* 0x20 */
541	0,
542	0x2e,		/* c */
543	0x2d,		/* x */
544	0x20,		/* d */
545	0x12,		/* e */
546	0x05,		/* 4 $ */
547	0x04,		/* 3 # */
548	0,
549	0,
550	0x39,		/* Space */
551	0x2f,		/* v */
552	0x21,		/* f */
553	0x14,		/* t */
554	0x13,		/* r */
555	0x06,		/* 5 % */
556	0,
557/* 0x30 */
558	0,
559	0x31,		/* n */
560	0x30,		/* b */
561	0x23,		/* h */
562	0x22,		/* g */
563	0x15,		/* y */
564	0x07,		/* 6 ^ */
565	0,
566	0,
567	0,
568	0x32,		/* m */
569	0x24,		/* j */
570	0x16,		/* u */
571	0x08,		/* 7 & */
572	0x09,		/* 8 * */
573	0,
574/* 0x40 */
575	0,
576	0x33,		/* , < */
577	0x25,		/* k */
578	0x17,		/* i */
579	0x18,		/* o */
580	0x0b,		/* 0 ) */
581	0x0a,		/* 9 ( */
582	0,
583	0,
584	0x34,		/* . > */
585	0x35,		/* / ? */
586	0x26,		/* l */
587	0x27,		/* ; : */
588	0x19,		/* p */
589	0x0c,		/* - _ */
590	0,
591/* 0x50 */
592	0,
593	0,
594	0x28,		/* ' " */
595	0,
596	0x1a,		/* [ { */
597	0x0d,		/* = + */
598	0,
599	0,
600	0x3a,		/* Caps Lock */
601	0x36,		/* Right Shift */
602	0x1c,		/* Return */
603	0x1b,		/* ] } */
604	0,
605	0x2b,		/* \ | */
606	0,
607	0,
608/* 0x60 */
609	0,
610	0,
611	0,
612	0,
613	0,
614	0,
615	0x0e,		/* Back Space */
616	0,
617	0,
618	0x4f,		/* KP 1 */
619	0,
620	0x4b,		/* KP 4 */
621	0x47,		/* KP 7 */
622	0,
623	0,
624	0,
625/* 0x70 */
626	0x52,		/* KP 0 */
627	0x53,		/* KP . */
628	0x50,		/* KP 2 */
629	0x4c,		/* KP 5 */
630	0x4d,		/* KP 6 */
631	0x48,		/* KP 8 */
632	0x01,		/* Escape */
633	0x45,		/* Num Lock */
634	0x57,		/* F11 */
635	0x4e,		/* KP + */
636	0x51,		/* KP 3 */
637	0x4a,		/* KP - */
638	0x37,		/* KP * */
639	0x49,		/* KP 9 */
640	0x46,		/* Scroll Lock */
641	0,
642/* 0x80 */
643	0,
644	0,
645	0,
646	0x41,		/* F7 (produced as an actual 8 bit code) */
647	0,		/* Alt-Print Screen */
648	0,
649	0,
650	0,
651	0,
652	0,
653	0,
654	0,
655	0,
656	0,
657	0,
658	0,
659/* 0x90 */
660	0xdb,		/* Left Meta */
661	0x88,		/* SunHelp */
662	0x8a,		/* SunAgain */
663	0x8c,		/* SunUndo */
664	0x8e,		/* SunCopy */
665	0x90,		/* SunPaste */
666	0x92,		/* SunCut */
667	0x8b,		/* SunProps */
668	0x8d,		/* SunFront */
669	0x8f,		/* SunOpen */
670	0x91		/* SunFind */
671};
672
673const u_int8_t pckbd_xtbl_ext[] = {
674/* 0x00 */
675	0,
676	0,
677	0,
678	0,
679	0,
680	0,
681	0,
682	0,
683	0,
684	0,
685	0,
686	0,
687	0,
688	0,
689	0,
690	0,
691/* 0x10 */
692	0,
693	0x38,		/* Right Alt */
694	0,		/* E0 12, to be ignored */
695	0,
696	0x1d,		/* Right Ctrl */
697	0,
698	0,
699	0,
700	0,
701	0,
702	0,
703	0,
704	0,
705	0,
706	0,
707	0,
708/* 0x20 */
709	0,
710	0,
711	0,
712	0,
713	0,
714	0,
715	0,
716	0,
717	0,
718	0,
719	0,
720	0,
721	0,
722	0,
723	0,
724	0xdd,		/* Compose */
725/* 0x30 */
726	0,
727	0,
728	0,
729	0,
730	0,
731	0,
732	0,
733	0,
734	0,
735	0,
736	0,
737	0,
738	0,
739	0,
740	0,
741	0,
742/* 0x40 */
743	0,
744	0,
745	0,
746	0,
747	0,
748	0,
749	0,
750	0,
751	0,
752	0,
753	0xb5,		/* KP / */
754	0,
755	0,
756	0,
757	0,
758	0,
759/* 0x50 */
760	0,
761	0,
762	0,
763	0,
764	0,
765	0,
766	0,
767	0,
768	0,
769	0,
770	0x1c,		/* KP Return */
771	0,
772	0,
773	0,
774	0,
775	0,
776/* 0x60 */
777	0,
778	0,
779	0,
780	0,
781	0,
782	0,
783	0,
784	0,
785	0,
786	0x4f,		/* End */
787	0,
788	0x4b,		/* Left */
789	0x47,		/* Home */
790	0,
791	0,
792	0,
793/* 0x70 */
794	0x52,		/* Insert */
795	0x53,		/* Delete */
796	0x50,		/* Down */
797	0,
798	0x4d,		/* Right */
799	0x48,		/* Up */
800	0,
801	0,
802	0,
803	0,
804	0x51,		/* Page Down */
805	0,
806	0x37,		/* Print Screen */
807	0x49,		/* Page Up */
808	0x46,		/* Ctrl-Break */
809	0
810};
811
812/*
813 * Translate scan codes from set 2 to set 1
814 */
815int
816pckbd_scancode_translate(struct pckbd_internal *id, int datain)
817{
818	if (id->t_translating != 0)
819		return datain;
820
821	if (datain == KBR_BREAK) {
822		id->t_releasing = 0x80;	/* next keycode is a release */
823		return 0;	/* consume scancode */
824	}
825
826	/*
827	 * Handle extended sequences
828	 */
829	if (datain == KBR_EXTENDED0 || datain == KBR_EXTENDED1)
830		return datain;
831
832	/*
833	 * Convert BREAK sequence (14 77 -> 1D 45)
834	 */
835	if (id->t_extended1 == 2 && datain == 0x14)
836		return 0x1d | id->t_releasing;
837	else if (id->t_extended1 == 1 && datain == 0x77)
838		return 0x45 | id->t_releasing;
839
840	if (id->t_extended0 != 0) {
841		if (datain >= sizeof pckbd_xtbl_ext)
842			datain = 0;
843		else
844			datain = pckbd_xtbl_ext[datain];
845	} else {
846		if (datain >= sizeof pckbd_xtbl)
847			datain = 0;
848		else
849			datain = pckbd_xtbl[datain];
850	}
851
852	/*
853	 * If we are mapping in the range 128-254, then make this
854	 * an extended keycode, as table 1 codes are limited to
855	 * the range 0-127 (the top bit is used for key up/break).
856	 */
857	if (datain > 0x7f) {
858		datain &= 0x7f;
859		id->t_extended0 = 0x80;
860	}
861
862	if (datain == 0) {
863		/*
864		 * We don't know how to translate this scan code, but
865		 * we can't silently eat it either (because there might
866		 * have been an extended byte transmitted already).
867		 * Hopefully this value will be harmless to the upper
868		 * layers.
869		 */
870		return 0xff;
871	}
872	return datain | id->t_releasing;
873}
874
875static bool
876pckbd_decode(struct pckbd_internal *id, int datain, u_int *type, int *dataout)
877{
878	int key;
879	int releasing;
880
881	if (datain == KBR_EXTENDED0) {
882		id->t_extended0 = 0x80;
883		return false;
884	} else if (datain == KBR_EXTENDED1) {
885		id->t_extended1 = 2;
886		return false;
887	}
888
889	releasing = datain & 0x80;
890	datain &= 0x7f;
891
892	if (id->t_extended0 == 0x80) {
893		switch (datain) {
894		case 0x2a:
895		case 0x36:
896			id->t_extended0 = 0;
897			return false;
898		default:
899			break;
900		}
901	}
902
903	/* map extended keys to (unused) codes 128-254 */
904	key = datain | id->t_extended0;
905	id->t_extended0 = 0;
906
907	/*
908	 * process PAUSE (also break) key (EXT1 1D 45  EXT1 9D C5):
909	 * map to (unused) code 7F
910	 */
911	if (id->t_extended1 == 2 && (datain == 0x1d || datain == 0x9d)) {
912		id->t_extended1 = 1;
913		return false;
914	} else if (id->t_extended1 == 1 &&
915		   (datain == 0x45 || datain == 0xc5)) {
916		id->t_extended1 = 0;
917		key = 0x7f;
918	} else if (id->t_extended1 > 0) {
919		id->t_extended1 = 0;
920	}
921
922	if (id->t_translating != 0) {
923		id->t_releasing = releasing;
924	} else {
925		/* id->t_releasing computed in pckbd_scancode_translate() */
926	}
927
928	if (id->t_releasing) {
929		id->t_releasing = 0;
930		id->t_lastchar = 0;
931		*type = WSCONS_EVENT_KEY_UP;
932	} else {
933		/* Always ignore typematic keys */
934		if (key == id->t_lastchar)
935			return false;
936		id->t_lastchar = key;
937		*type = WSCONS_EVENT_KEY_DOWN;
938	}
939
940	*dataout = key;
941	return true;
942}
943
944int
945pckbd_init(struct pckbd_internal *t, pckbport_tag_t kbctag,
946    pckbport_slot_t kbcslot, int console)
947{
948
949	memset(t, 0, sizeof(struct pckbd_internal));
950
951	t->t_isconsole = console;
952	t->t_kbctag = kbctag;
953	t->t_kbcslot = kbcslot;
954
955	return pckbd_set_xtscancode(kbctag, kbcslot, t);
956}
957
958static int
959pckbd_led_encode(int led)
960{
961	int res;
962
963	res = 0;
964
965	if (led & WSKBD_LED_SCROLL)
966		res |= 0x01;
967	if (led & WSKBD_LED_NUM)
968		res |= 0x02;
969	if (led & WSKBD_LED_CAPS)
970		res |= 0x04;
971	return res;
972}
973
974static int
975pckbd_led_decode(int led)
976{
977	int res;
978
979	res = 0;
980	if (led & 0x01)
981		res |= WSKBD_LED_SCROLL;
982	if (led & 0x02)
983		res |= WSKBD_LED_NUM;
984	if (led & 0x04)
985		res |= WSKBD_LED_CAPS;
986	return res;
987}
988
989void
990pckbd_set_leds(void *v, int leds)
991{
992	struct pckbd_softc *sc = v;
993	u_char cmd[2];
994
995	cmd[0] = KBC_MODEIND;
996	cmd[1] = pckbd_led_encode(leds);
997	sc->sc_ledstate = cmd[1];
998
999	(void)pckbport_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot,
1000	    cmd, 2, 0, 0, 0);
1001}
1002
1003/*
1004 * Got a console receive interrupt -
1005 * the console processor wants to give us a character.
1006 */
1007void
1008pckbd_input(void *vsc, int data)
1009{
1010	struct pckbd_softc *sc = vsc;
1011	int key;
1012	u_int type;
1013
1014	data = pckbd_scancode_translate(sc->id, data);
1015	if (data == 0)
1016		return;
1017
1018#ifdef WSDISPLAY_COMPAT_RAWKBD
1019	if (sc->rawkbd) {
1020		u_char d = data;
1021		wskbd_rawinput(sc->sc_wskbddev, &d, 1);
1022		return;
1023	}
1024#endif
1025	if (pckbd_decode(sc->id, data, &type, &key))
1026		wskbd_input(sc->sc_wskbddev, type, key);
1027}
1028
1029int
1030pckbd_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
1031{
1032	struct pckbd_softc *sc = v;
1033	u_char cmdb[2];
1034
1035	switch (cmd) {
1036	case WSKBDIO_GTYPE:
1037		*(int *)data = WSKBD_TYPE_PC_XT;
1038		return 0;
1039	case WSKBDIO_SETLEDS:
1040		cmdb[0] = KBC_MODEIND;
1041		cmdb[1] = pckbd_led_encode(*(int *)data);
1042		sc->sc_ledstate = cmdb[1];
1043		return pckbport_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot,
1044		    cmdb, 2, 0, 1, 0);
1045	case WSKBDIO_GETLEDS:
1046		*(int *)data = pckbd_led_decode(sc->sc_ledstate);
1047		return 0;
1048#if 0
1049	case WSKBDIO_COMPLEXBELL:
1050#define d ((struct wskbd_bell_data *)data)
1051		/*
1052		 * Keyboard can't beep directly; we have an
1053		 * externally-provided global hook to do this.
1054		 */
1055		pckbd_bell(d->pitch, d->period, d->volume, 0);
1056#undef d
1057		return 0;
1058#endif
1059#ifdef WSDISPLAY_COMPAT_RAWKBD
1060	case WSKBDIO_SETMODE:
1061		sc->rawkbd = (*(int *)data == WSKBD_RAW);
1062		return 0;
1063#endif
1064	}
1065	return EPASSTHROUGH;
1066}
1067
1068void
1069pckbd_bell(u_int pitch, u_int period, u_int volume, int poll)
1070{
1071
1072	if (pckbd_bell_fn != NULL)
1073		(*pckbd_bell_fn)(pckbd_bell_fn_arg, pitch, period,
1074		    volume, poll);
1075}
1076
1077void
1078pckbd_unhook_bell(void (*fn)(void *, u_int, u_int, u_int, int), void *arg)
1079{
1080	if (pckbd_bell_fn != fn && pckbd_bell_fn_arg != arg)
1081		return;
1082	pckbd_bell_fn = NULL;
1083	pckbd_bell_fn_arg = NULL;
1084}
1085
1086void
1087pckbd_hookup_bell(void (*fn)(void *, u_int, u_int, u_int, int), void *arg)
1088{
1089
1090	if (pckbd_bell_fn == NULL) {
1091		pckbd_bell_fn = fn;
1092		pckbd_bell_fn_arg = arg;
1093	}
1094}
1095
1096int
1097pckbd_cnattach(pckbport_tag_t kbctag, int kbcslot)
1098{
1099	int res;
1100	u_char cmd[1];
1101
1102	res = pckbd_init(&pckbd_consdata, kbctag, kbcslot, 1);
1103	/* We may allow the console to be attached if no keyboard is present */
1104#if defined(PCKBD_CNATTACH_MAY_FAIL)
1105	if (res)
1106		return res;
1107#endif
1108
1109	/* Just to be sure. */
1110	cmd[0] = KBC_ENABLE;
1111	res = pckbport_poll_cmd(kbctag, kbcslot, cmd, 1, 0, 0, 0);
1112
1113#if defined(PCKBD_CNATTACH_MAY_FAIL)
1114	if (res)
1115		return res;
1116#endif
1117
1118	wskbd_cnattach(&pckbd_consops, &pckbd_consdata, &pckbd_keymapdata);
1119
1120	return res;
1121}
1122
1123/* ARGSUSED */
1124void
1125pckbd_cngetc(void *v, u_int *type, int *data)
1126{
1127        struct pckbd_internal *t = v;
1128	int val;
1129
1130	for (;;) {
1131		val = pckbport_poll_data(t->t_kbctag, t->t_kbcslot);
1132		if (val == -1) {
1133			*type = 0;
1134			*data = 0;
1135			return;
1136		}
1137
1138		val = pckbd_scancode_translate(t, val);
1139		if (val == 0)
1140			continue;
1141
1142		if (pckbd_decode(t, val, type, data))
1143			return;
1144	}
1145}
1146
1147void
1148pckbd_cnpollc(void *v, int on)
1149{
1150	struct pckbd_internal *t = v;
1151
1152	pckbport_set_poll(t->t_kbctag, t->t_kbcslot, on);
1153}
1154
1155void
1156pckbd_cnbell(void *v, u_int pitch, u_int period, u_int volume)
1157{
1158
1159	pckbd_bell(pitch, period, volume, 1);
1160}
1161