xref: /illumos-gate/usr/src/cmd/bhyve/ps2kbd.c (revision b0bb0d63)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
5  * Copyright (c) 2015 Nahanni Systems Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
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  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 
36 #include <assert.h>
37 #include <stdbool.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <strings.h>
41 #include <pthread.h>
42 #include <pthread_np.h>
43 #include <unistd.h>
44 #include <fcntl.h>
45 
46 #include "atkbdc.h"
47 #include "bhyverun.h"
48 #include "config.h"
49 #include "console.h"
50 #include "debug.h"
51 #include "ps2kbd.h"
52 
53 /* keyboard device commands */
54 #define	PS2KC_RESET_DEV		0xff
55 #define	PS2KC_DISABLE		0xf5
56 #define	PS2KC_ENABLE		0xf4
57 #define	PS2KC_SET_TYPEMATIC	0xf3
58 #define	PS2KC_SEND_DEV_ID	0xf2
59 #define	PS2KC_SET_SCANCODE_SET	0xf0
60 #define	PS2KC_ECHO		0xee
61 #define	PS2KC_SET_LEDS		0xed
62 
63 #define	PS2KC_BAT_SUCCESS	0xaa
64 #define	PS2KC_ACK		0xfa
65 
66 #define	PS2KBD_FIFOSZ		16
67 
68 #define	PS2KBD_LAYOUT_BASEDIR	"/usr/share/bhyve/kbdlayout/"
69 
70 #define	MAX_PATHNAME		256
71 
72 struct fifo {
73 	uint8_t	buf[PS2KBD_FIFOSZ];
74 	int	rindex;		/* index to read from */
75 	int	windex;		/* index to write to */
76 	int	num;		/* number of bytes in the fifo */
77 	int	size;		/* size of the fifo */
78 };
79 
80 struct ps2kbd_softc {
81 	struct atkbdc_softc	*atkbdc_sc;
82 	pthread_mutex_t		mtx;
83 
84 	bool			enabled;
85 	struct fifo		fifo;
86 
87 	uint8_t			curcmd;	/* current command for next byte */
88 };
89 
90 #define SCANCODE_E0_PREFIX 1
91 struct extended_translation {
92 	uint32_t keysym;
93 	uint8_t scancode;
94 	int flags;
95 };
96 
97 /*
98  * FIXME: Pause/break and Print Screen/SysRq require special handling.
99  */
100 static struct extended_translation extended_translations[128] = {
101 		{0xff08, 0x66, 0},		/* Back space */
102 		{0xff09, 0x0d, 0},		/* Tab */
103 		{0xff0d, 0x5a, 0},		/* Return */
104 		{0xff1b, 0x76, 0},		/* Escape */
105 		{0xff50, 0x6c, SCANCODE_E0_PREFIX}, 	/* Home */
106 		{0xff51, 0x6b, SCANCODE_E0_PREFIX}, 	/* Left arrow */
107 		{0xff52, 0x75, SCANCODE_E0_PREFIX}, 	/* Up arrow */
108 		{0xff53, 0x74, SCANCODE_E0_PREFIX}, 	/* Right arrow */
109 		{0xff54, 0x72, SCANCODE_E0_PREFIX}, 	/* Down arrow */
110 		{0xff55, 0x7d, SCANCODE_E0_PREFIX}, 	/* PgUp */
111 		{0xff56, 0x7a, SCANCODE_E0_PREFIX}, 	/* PgDown */
112 		{0xff57, 0x69, SCANCODE_E0_PREFIX}, 	/* End */
113 		{0xff63, 0x70, SCANCODE_E0_PREFIX}, 	/* Ins */
114 		{0xff8d, 0x5a, SCANCODE_E0_PREFIX}, 	/* Keypad Enter */
115 		{0xffe1, 0x12, 0},		/* Left shift */
116 		{0xffe2, 0x59, 0},		/* Right shift */
117 		{0xffe3, 0x14, 0},		/* Left control */
118 		{0xffe4, 0x14, SCANCODE_E0_PREFIX}, 	/* Right control */
119 		/* {0xffe7, XXX}, Left meta */
120 		/* {0xffe8, XXX}, Right meta */
121 		{0xffe9, 0x11, 0},		/* Left alt */
122 		{0xfe03, 0x11, SCANCODE_E0_PREFIX}, 	/* AltGr */
123 		{0xffea, 0x11, SCANCODE_E0_PREFIX}, 	/* Right alt */
124 		{0xffeb, 0x1f, SCANCODE_E0_PREFIX}, 	/* Left Windows */
125 		{0xffec, 0x27, SCANCODE_E0_PREFIX}, 	/* Right Windows */
126 		{0xffbe, 0x05, 0},		/* F1 */
127 		{0xffbf, 0x06, 0},		/* F2 */
128 		{0xffc0, 0x04, 0},		/* F3 */
129 		{0xffc1, 0x0c, 0},		/* F4 */
130 		{0xffc2, 0x03, 0},		/* F5 */
131 		{0xffc3, 0x0b, 0},		/* F6 */
132 		{0xffc4, 0x83, 0},		/* F7 */
133 		{0xffc5, 0x0a, 0},		/* F8 */
134 		{0xffc6, 0x01, 0},		/* F9 */
135 		{0xffc7, 0x09, 0},		/* F10 */
136 		{0xffc8, 0x78, 0},		/* F11 */
137 		{0xffc9, 0x07, 0},		/* F12 */
138 		{0xffff, 0x71, SCANCODE_E0_PREFIX},	/* Del */
139 		{0xff14, 0x7e, 0},		/* ScrollLock */
140 		/* NumLock and Keypads*/
141 		{0xff7f, 0x77, 0}, 	/* NumLock */
142 		{0xffaf, 0x4a, SCANCODE_E0_PREFIX}, 	/* Keypad slash */
143 		{0xffaa, 0x7c, 0}, 	/* Keypad asterisk */
144 		{0xffad, 0x7b, 0}, 	/* Keypad minus */
145 		{0xffab, 0x79, 0}, 	/* Keypad plus */
146 		{0xffb7, 0x6c, 0}, 	/* Keypad 7 */
147 		{0xff95, 0x6c, 0}, 	/* Keypad home */
148 		{0xffb8, 0x75, 0}, 	/* Keypad 8 */
149 		{0xff97, 0x75, 0}, 	/* Keypad up arrow */
150 		{0xffb9, 0x7d, 0}, 	/* Keypad 9 */
151 		{0xff9a, 0x7d, 0}, 	/* Keypad PgUp */
152 		{0xffb4, 0x6b, 0}, 	/* Keypad 4 */
153 		{0xff96, 0x6b, 0}, 	/* Keypad left arrow */
154 		{0xffb5, 0x73, 0}, 	/* Keypad 5 */
155 		{0xff9d, 0x73, 0}, 	/* Keypad empty */
156 		{0xffb6, 0x74, 0}, 	/* Keypad 6 */
157 		{0xff98, 0x74, 0}, 	/* Keypad right arrow */
158 		{0xffb1, 0x69, 0}, 	/* Keypad 1 */
159 		{0xff9c, 0x69, 0}, 	/* Keypad end */
160 		{0xffb2, 0x72, 0}, 	/* Keypad 2 */
161 		{0xff99, 0x72, 0}, 	/* Keypad down arrow */
162 		{0xffb3, 0x7a, 0}, 	/* Keypad 3 */
163 		{0xff9b, 0x7a, 0}, 	/* Keypad PgDown */
164 		{0xffb0, 0x70, 0}, 	/* Keypad 0 */
165 		{0xff9e, 0x70, 0}, 	/* Keypad ins */
166 		{0xffae, 0x71, 0}, 	/* Keypad . */
167 		{0xff9f, 0x71, 0}, 	/* Keypad del */
168 		{0, 0, 0} 		/* Terminator */
169 };
170 
171 /* ASCII to type 2 scancode lookup table */
172 static uint8_t ascii_translations[128] = {
173 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
174 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
176 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
177 		0x29, 0x16, 0x52, 0x26, 0x25, 0x2e, 0x3d, 0x52,
178 		0x46, 0x45, 0x3e, 0x55, 0x41, 0x4e, 0x49, 0x4a,
179 		0x45, 0x16, 0x1e, 0x26, 0x25, 0x2e, 0x36, 0x3d,
180 		0x3e, 0x46, 0x4c, 0x4c, 0x41, 0x55, 0x49, 0x4a,
181 		0x1e, 0x1c, 0x32, 0x21, 0x23, 0x24, 0x2b, 0x34,
182 		0x33, 0x43, 0x3b, 0x42, 0x4b, 0x3a, 0x31, 0x44,
183 		0x4d, 0x15, 0x2d, 0x1b, 0x2c, 0x3c, 0x2a, 0x1d,
184 		0x22, 0x35, 0x1a, 0x54, 0x5d, 0x5b, 0x36, 0x4e,
185 		0x0e, 0x1c, 0x32, 0x21, 0x23, 0x24, 0x2b, 0x34,
186 		0x33, 0x43, 0x3b, 0x42, 0x4b, 0x3a, 0x31, 0x44,
187 		0x4d, 0x15, 0x2d, 0x1b, 0x2c, 0x3c, 0x2a, 0x1d,
188 		0x22, 0x35, 0x1a, 0x54, 0x5d, 0x5b, 0x0e, 0x00,
189 };
190 
191 /* ScanCode set1 to set2 lookup table */
192 static const uint8_t keyset1to2_translations[128] = {
193 		   0, 0x76, 0x16, 0x1E, 0x26, 0x25, 0x2e, 0x36,
194 		0x3d, 0x3e, 0x46, 0x45, 0x4e, 0x55, 0x66, 0x0d,
195 		0x15, 0x1d, 0x24, 0x2d, 0x2c, 0x35, 0x3c, 0x43,
196 		0x44, 0x4d, 0x54, 0x5b, 0x5a, 0x14, 0x1c, 0x1b,
197 		0x23, 0x2b, 0x34, 0x33, 0x3b, 0x42, 0x4b, 0x4c,
198 		0x52, 0x0e, 0x12, 0x5d, 0x1a, 0x22, 0x21, 0x2a,
199 		0x32, 0x31, 0x3a, 0x41, 0x49, 0x4a, 0x59, 0x7c,
200 		0x11, 0x29, 0x58, 0x05, 0x06, 0x04, 0x0c, 0x03,
201 		0x0b, 0x83, 0x0a, 0x01, 0x09, 0x77, 0x7e, 0x6c,
202 		0x75, 0x7d, 0x7b, 0x6b, 0x73, 0x74, 0x79, 0x69,
203 		0x72, 0x7a, 0x70, 0x71, 0x84, 0x60, 0x61, 0x78,
204 		0x07, 0x0f, 0x17, 0x1f, 0x27, 0x2f, 0x37, 0x3f,
205 		0x47, 0x4f, 0x56, 0x5e, 0x08, 0x10, 0x18, 0x20,
206 		0x28, 0x30, 0x38, 0x40, 0x48, 0x50, 0x57, 0x6f,
207 		0x13, 0x19, 0x39, 0x51, 0x53, 0x5c, 0x5f, 0x62,
208 		0x63, 0x64, 0x65, 0x67, 0x68, 0x6a, 0x6d, 0x6e,
209 };
210 
211 static void
212 fifo_init(struct ps2kbd_softc *sc)
213 {
214 	struct fifo *fifo;
215 
216 	fifo = &sc->fifo;
217 	fifo->size = sizeof(((struct fifo *)0)->buf);
218 }
219 
220 static void
221 fifo_reset(struct ps2kbd_softc *sc)
222 {
223 	struct fifo *fifo;
224 
225 	fifo = &sc->fifo;
226 	bzero(fifo, sizeof(struct fifo));
227 	fifo->size = sizeof(((struct fifo *)0)->buf);
228 }
229 
230 static void
231 fifo_put(struct ps2kbd_softc *sc, uint8_t val)
232 {
233 	struct fifo *fifo;
234 
235 	fifo = &sc->fifo;
236 	if (fifo->num < fifo->size) {
237 		fifo->buf[fifo->windex] = val;
238 		fifo->windex = (fifo->windex + 1) % fifo->size;
239 		fifo->num++;
240 	}
241 }
242 
243 static int
244 fifo_get(struct ps2kbd_softc *sc, uint8_t *val)
245 {
246 	struct fifo *fifo;
247 
248 	fifo = &sc->fifo;
249 	if (fifo->num > 0) {
250 		*val = fifo->buf[fifo->rindex];
251 		fifo->rindex = (fifo->rindex + 1) % fifo->size;
252 		fifo->num--;
253 		return (0);
254 	}
255 
256 	return (-1);
257 }
258 
259 int
260 ps2kbd_read(struct ps2kbd_softc *sc, uint8_t *val)
261 {
262 	int retval;
263 
264 	pthread_mutex_lock(&sc->mtx);
265 	retval = fifo_get(sc, val);
266 	pthread_mutex_unlock(&sc->mtx);
267 
268 	return (retval);
269 }
270 
271 void
272 ps2kbd_write(struct ps2kbd_softc *sc, uint8_t val)
273 {
274 	pthread_mutex_lock(&sc->mtx);
275 	if (sc->curcmd) {
276 		switch (sc->curcmd) {
277 		case PS2KC_SET_TYPEMATIC:
278 			fifo_put(sc, PS2KC_ACK);
279 			break;
280 		case PS2KC_SET_SCANCODE_SET:
281 			fifo_put(sc, PS2KC_ACK);
282 			break;
283 		case PS2KC_SET_LEDS:
284 			fifo_put(sc, PS2KC_ACK);
285 			break;
286 		default:
287 			EPRINTLN("Unhandled ps2 keyboard current "
288 			    "command byte 0x%02x", val);
289 			break;
290 		}
291 		sc->curcmd = 0;
292 	} else {
293 		switch (val) {
294 		case 0x00:
295 			fifo_put(sc, PS2KC_ACK);
296 			break;
297 		case PS2KC_RESET_DEV:
298 			fifo_reset(sc);
299 			fifo_put(sc, PS2KC_ACK);
300 			fifo_put(sc, PS2KC_BAT_SUCCESS);
301 			break;
302 		case PS2KC_DISABLE:
303 			sc->enabled = false;
304 			fifo_put(sc, PS2KC_ACK);
305 			break;
306 		case PS2KC_ENABLE:
307 			sc->enabled = true;
308 			fifo_reset(sc);
309 			fifo_put(sc, PS2KC_ACK);
310 			break;
311 		case PS2KC_SET_TYPEMATIC:
312 			sc->curcmd = val;
313 			fifo_put(sc, PS2KC_ACK);
314 			break;
315 		case PS2KC_SEND_DEV_ID:
316 			fifo_put(sc, PS2KC_ACK);
317 			fifo_put(sc, 0xab);
318 			fifo_put(sc, 0x83);
319 			break;
320 		case PS2KC_SET_SCANCODE_SET:
321 			sc->curcmd = val;
322 			fifo_put(sc, PS2KC_ACK);
323 			break;
324 		case PS2KC_ECHO:
325 			fifo_put(sc, PS2KC_ECHO);
326 			break;
327 		case PS2KC_SET_LEDS:
328 			sc->curcmd = val;
329 			fifo_put(sc, PS2KC_ACK);
330 			break;
331 		default:
332 			EPRINTLN("Unhandled ps2 keyboard command "
333 			    "0x%02x", val);
334 			break;
335 		}
336 	}
337 	pthread_mutex_unlock(&sc->mtx);
338 }
339 
340 /*
341  * Translate keysym to type 2 scancode and insert into keyboard buffer.
342  */
343 static void
344 ps2kbd_keysym_queue(struct ps2kbd_softc *sc,
345     int down, uint32_t keysym, uint32_t keycode)
346 {
347 	const struct extended_translation *trans;
348 	int e0_prefix, found;
349 	uint8_t code;
350 
351 	assert(pthread_mutex_isowned_np(&sc->mtx));
352 
353 	if (keycode) {
354 		code =  keyset1to2_translations[(uint8_t)(keycode & 0x7f)];
355 		e0_prefix = ((keycode & 0x80) ?  SCANCODE_E0_PREFIX : 0);
356 		found = 1;
357 	} else {
358 		found = 0;
359 		if (keysym < 0x80) {
360 			code = ascii_translations[keysym];
361 			e0_prefix = 0;
362 			found = 1;
363 		} else {
364 			for (trans = &extended_translations[0];
365 			    trans->keysym != 0; trans++) {
366 				if (keysym == trans->keysym) {
367 					code = trans->scancode;
368 					e0_prefix = trans->flags & SCANCODE_E0_PREFIX;
369 					found = 1;
370 					break;
371 				}
372 			}
373 		}
374 	}
375 
376 	if (!found) {
377 		EPRINTLN("Unhandled ps2 keyboard keysym 0x%x", keysym);
378 		return;
379 	}
380 
381 	if (e0_prefix)
382 		fifo_put(sc, 0xe0);
383 	if (!down)
384 		fifo_put(sc, 0xf0);
385 	fifo_put(sc, code);
386 }
387 
388 static void
389 ps2kbd_event(int down, uint32_t keysym, uint32_t keycode, void *arg)
390 {
391 	struct ps2kbd_softc *sc = arg;
392 	int fifo_full;
393 
394 	pthread_mutex_lock(&sc->mtx);
395 	if (!sc->enabled) {
396 		pthread_mutex_unlock(&sc->mtx);
397 		return;
398 	}
399 	fifo_full = sc->fifo.num == PS2KBD_FIFOSZ;
400 	ps2kbd_keysym_queue(sc, down, keysym, keycode);
401 	pthread_mutex_unlock(&sc->mtx);
402 
403 	if (!fifo_full)
404 		atkbdc_event(sc->atkbdc_sc, 1);
405 }
406 
407 static void
408 ps2kbd_update_extended_translation(uint32_t keycode, uint32_t scancode, uint32_t prefix)
409 {
410 	int i = 0;
411 
412 	do {
413 		if (extended_translations[i].keysym == keycode)
414 			break;
415 	} while (extended_translations[++i].keysym);
416 
417 	if (i == (sizeof(extended_translations) / sizeof(struct extended_translation) - 1))
418 		return;
419 
420 	if (!extended_translations[i].keysym)	{
421 		extended_translations[i].keysym = keycode;
422 
423 		extended_translations[i+1].keysym = 0;
424 		extended_translations[i+1].scancode = 0;
425 		extended_translations[i+1].flags = 0;
426 	}
427 
428 	extended_translations[i].scancode = (uint8_t)(scancode & 0xff);
429 	extended_translations[i].flags = (prefix ? SCANCODE_E0_PREFIX : 0);
430 }
431 
432 static void
433 ps2kbd_setkbdlayout(void)
434 {
435 	int err;
436 	int fd;
437 	char path[MAX_PATHNAME];
438 	char *buf, *next, *line;
439 	struct stat sb;
440 	ssize_t sz;
441 	uint8_t ascii;
442 	uint32_t keycode, scancode, prefix;
443 
444 	snprintf(path, MAX_PATHNAME, PS2KBD_LAYOUT_BASEDIR"%s", get_config_value("keyboard.layout") );
445 
446 	err = stat(path, &sb);
447 	if (err)
448 		return;
449 
450 	buf = (char *)malloc(sizeof(char) * sb.st_size);
451 	if (buf == NULL)
452 		return;
453 
454 	fd = open(path, O_RDONLY);
455 	if (fd == -1)
456 		goto out;
457 
458 	sz = read(fd, buf, sb.st_size);
459 
460 	close(fd);
461 
462 	if (sz < 0 || sz != sb.st_size)
463 		goto out;
464 
465 	next = buf;
466 	while ((line = strsep(&next, "\n")) != NULL)	{
467 		if (sscanf(line, "'%c',%x;", &ascii, &scancode) == 2)	{
468 			if (ascii < 0x80)
469 				ascii_translations[ascii] = (uint8_t)(scancode & 0xff);
470 		} else if (sscanf(line, "%x,%x,%x;", &keycode, &scancode, &prefix) == 3 )	{
471 			ps2kbd_update_extended_translation(keycode, scancode, prefix);
472 		} else if (sscanf(line, "%x,%x;", &keycode, &scancode) == 2)	{
473 			if (keycode < 0x80)
474 				ascii_translations[(uint8_t)(keycode & 0xff)] = (uint8_t)(scancode & 0xff);
475 			else
476 				ps2kbd_update_extended_translation(keycode, scancode, 0);
477 		}
478 	}
479 
480 out:
481 	free(buf);
482 }
483 
484 struct ps2kbd_softc *
485 ps2kbd_init(struct atkbdc_softc *atkbdc_sc)
486 {
487 	struct ps2kbd_softc *sc;
488 
489 	if (get_config_value("keyboard.layout") != NULL)
490 		ps2kbd_setkbdlayout();
491 
492 	sc = calloc(1, sizeof (struct ps2kbd_softc));
493 	pthread_mutex_init(&sc->mtx, NULL);
494 	fifo_init(sc);
495 	sc->atkbdc_sc = atkbdc_sc;
496 
497 	console_kbd_register(ps2kbd_event, sc, 1);
498 
499 	return (sc);
500 }
501 
502