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