1/*	$OpenBSD: kbd_wscons.c,v 1.36 2022/05/05 16:12:42 bluhm Exp $ */
2
3/*
4 * Copyright (c) 2001 Mats O Jansson.  All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/ioctl.h>
28#include <sys/time.h>
29#include <dev/wscons/wsconsio.h>
30#include <dev/wscons/wsksymdef.h>
31
32#include <err.h>
33#include <errno.h>
34#include <fcntl.h>
35#include <limits.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40
41#define	NUM_KBD	10
42
43char *kbtype_tab[] = {
44	"pc-xt/pc-at",
45	"usb",
46	"adb",
47	"lk201",
48	"sun",
49	"sun5",
50	"hil",
51	"gsc",
52	"sgi"
53};
54enum {	SA_PCKBD,
55	SA_UKBD,
56	SA_AKBD,
57	SA_LKKBD,
58	SA_SUNKBD,
59	SA_SUN5KBD,
60	SA_HILKBD,
61	SA_GSCKBD,
62	SA_SGIKBD,
63
64	SA_MAX
65};
66
67struct nameint {
68	int value;
69	char *name;
70};
71
72struct nameint kbdenc_tab[] = {
73	KB_ENCTAB
74	,
75	{ 0, NULL }
76};
77
78struct nameint kbdvar_tab[] = {
79	KB_VARTAB
80	,
81	{ 0, NULL }
82};
83
84extern char *__progname;
85
86void	kbd_show_enc(struct wskbd_encoding_data *encs, int idx);
87void	kbd_get_encs(int fd, struct wskbd_encoding_data *encs);
88void	kbd_list(void);
89void	kbd_set(char *name, int verbose);
90
91void
92kbd_show_enc(struct wskbd_encoding_data *encs, int idx)
93{
94	int found;
95	kbd_t encoding, variant;
96	struct nameint *n;
97	int i;
98
99	printf("tables available for %s keyboard:\nencoding\n\n",
100	    kbtype_tab[idx]);
101
102	for (i = 0; i < encs->nencodings; i++) {
103		found = 0;
104		encoding = encs->encodings[i];
105		for (n = &kbdenc_tab[0]; n->value; n++) {
106			if (n->value == KB_ENCODING(encoding)) {
107				printf("%s", n->name);
108				found++;
109			}
110		}
111		if (found == 0)
112			printf("<encoding 0x%04x>", KB_ENCODING(encoding));
113		found = 0;
114		variant = KB_VARIANT(encoding);
115		for (n = &kbdvar_tab[0]; n->value; n++) {
116			if ((n->value & KB_VARIANT(encoding)) == n->value) {
117				printf(".%s", n->name);
118				variant &= ~n->value;
119			}
120		}
121		if (variant != 0)
122			printf(".<variant 0x%08x>", variant);
123		printf("\n");
124	}
125	printf("\n");
126}
127
128void
129kbd_get_encs(int fd, struct wskbd_encoding_data *encs)
130{
131	int nencodings = 64;
132
133	encs->nencodings = nencodings;
134	while (encs->nencodings == nencodings) {
135		encs->encodings = reallocarray(encs->encodings,
136		    encs->nencodings, sizeof(kbd_t));
137		if (encs->encodings == NULL)
138			err(1, NULL);
139		if (ioctl(fd, WSKBDIO_GETENCODINGS, encs) == -1)
140			err(1, "WSKBDIO_GETENCODINGS");
141		if (encs->nencodings == nencodings) {
142			nencodings *= 2;
143			encs->nencodings = nencodings;
144		}
145	}
146}
147
148void
149kbd_list(void)
150{
151	int	kbds[SA_MAX];
152	struct wskbd_encoding_data encs[SA_MAX];
153	int	fd, i, kbtype, t, error = 0;
154	char	device[PATH_MAX];
155
156	memset(kbds, 0, sizeof(kbds));
157	memset(encs, 0, sizeof(encs));
158
159	/* Go through all keyboards. */
160	for (i = 0; i < NUM_KBD; i++) {
161		(void) snprintf(device, sizeof device, "/dev/wskbd%d", i);
162		fd = open(device, O_WRONLY);
163		if (fd == -1)
164			fd = open(device, O_RDONLY);
165		if (fd == -1) {
166			/* remember the first error number */
167			if (error == 0)
168				error = errno;
169		} else {
170			/* at least one success, do not print error */
171			error = -1;
172
173			if (ioctl(fd, WSKBDIO_GTYPE, &kbtype) == -1)
174				err(1, "WSKBDIO_GTYPE %s", device);
175			switch (kbtype) {
176			case WSKBD_TYPE_PC_XT:
177			case WSKBD_TYPE_PC_AT:
178				t = SA_PCKBD;
179				break;
180			case WSKBD_TYPE_USB:
181				t = SA_UKBD;
182				break;
183			case WSKBD_TYPE_ADB:
184				t = SA_AKBD;
185				break;
186			case WSKBD_TYPE_LK201:
187			case WSKBD_TYPE_LK401:
188				t = SA_LKKBD;
189				break;
190			case WSKBD_TYPE_SUN:
191				t = SA_SUNKBD;
192				break;
193			case WSKBD_TYPE_SUN5:
194				t = SA_SUN5KBD;
195				break;
196			case WSKBD_TYPE_HIL:
197				t = SA_HILKBD;
198				break;
199			case WSKBD_TYPE_GSC:
200				t = SA_GSCKBD;
201				break;
202			case WSKBD_TYPE_SGI:
203				t = SA_SGIKBD;
204				break;
205			default:
206				t = SA_MAX;
207				break;
208			}
209
210			if (t != SA_MAX) {
211				kbds[t]++;
212				if (encs[t].encodings == NULL)
213					kbd_get_encs(fd, &encs[t]);
214			}
215			close(fd);
216		}
217	}
218	if (error > 0) {
219		errno = error;
220		err(1, "/dev/wskbd0");
221	}
222
223	for (i = 0; i < SA_MAX; i++)
224		if (kbds[i] != 0)
225			kbd_show_enc(&encs[i], i);
226
227	for (i = 0; i < SA_MAX; i++)
228		free(encs[i].encodings);
229}
230
231void
232kbd_set(char *name, int verbose)
233{
234	char	buf[LINE_MAX], *c, *b, device[sizeof "/dev/wskbd00"];
235	int	map = 0, v, i, fd, error = 0;
236	struct nameint *n;
237
238	c = name;
239	b = buf;
240	while (*c != '.' && *c != '\0' && b < buf + sizeof(buf) - 1)
241		*b++ = *c++;
242	*b = '\0';
243	n = &kbdenc_tab[0];
244	while (n->value) {
245		if (strcmp(n->name, buf) == 0)
246			map = n->value;
247		n++;
248	}
249	if (map == 0)
250		errx(1, "unknown encoding %s", buf);
251	while (*c == '.') {
252		b = buf;
253		c++;
254		while (*c != '.' && *c != '\0' && b < buf + sizeof(buf) - 1)
255			*b++ = *c++;
256		*b = '\0';
257		v = 0;
258		for (n = &kbdvar_tab[0]; n->value; n++) {
259			if (strcmp(n->name, buf) == 0)
260				v = n->value;
261		}
262		if (v == 0)
263			errx(1, "unknown variant %s", buf);
264		map |= v;
265	}
266
267	/* Go through all keyboards. */
268	v = 0;
269	for (i = 0; i < NUM_KBD; i++) {
270		(void) snprintf(device, sizeof device, "/dev/wskbd%d", i);
271		fd = open(device, O_WRONLY);
272		if (fd == -1)
273			fd = open(device, O_RDONLY);
274		if (fd == -1) {
275			/* remember the first error number */
276			if (error == 0)
277				error = errno;
278		} else {
279			/* at least one success, do not print error */
280			error = -1;
281
282			if (ioctl(fd, WSKBDIO_SETENCODING, &map) == -1) {
283				if (errno != EINVAL)
284					err(1, "WSKBDIO_SETENCODING %s",
285					    device);
286				fprintf(stderr,
287				    "%s: unsupported encoding %s on %s\n",
288				    __progname, name, device);
289			} else
290				v++;
291			close(fd);
292		}
293	}
294	if (error > 0) {
295		errno = error;
296		err(1, "/dev/wskbd0");
297	}
298
299	if (verbose && v > 0)
300		fprintf(stderr, "kbd: keyboard mapping set to %s\n", name);
301}
302