xref: /freebsd/usr.sbin/bhyve/amd64/ps2kbd.c (revision 5e3190f700637fcfc1a52daeaa4a031fdd2557c7)
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/cdefs.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 
34 #include <machine/vmm_snapshot.h>
35 
36 #include <assert.h>
37 #include <stdbool.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <strings.h>
42 #include <pthread.h>
43 #include <pthread_np.h>
44 #include <unistd.h>
45 #include <fcntl.h>
46 
47 #include "atkbdc.h"
48 #include "bhyverun.h"
49 #include "config.h"
50 #include "console.h"
51 #include "debug.h"
52 #include "ps2kbd.h"
53 
54 /* keyboard device commands */
55 #define	PS2KC_RESET_DEV		0xff
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_DISABLE:
304 			sc->enabled = false;
305 			fifo_put(sc, PS2KC_ACK);
306 			break;
307 		case PS2KC_ENABLE:
308 			sc->enabled = true;
309 			fifo_reset(sc);
310 			fifo_put(sc, PS2KC_ACK);
311 			break;
312 		case PS2KC_SET_TYPEMATIC:
313 			sc->curcmd = val;
314 			fifo_put(sc, PS2KC_ACK);
315 			break;
316 		case PS2KC_SEND_DEV_ID:
317 			fifo_put(sc, PS2KC_ACK);
318 			fifo_put(sc, 0xab);
319 			fifo_put(sc, 0x83);
320 			break;
321 		case PS2KC_SET_SCANCODE_SET:
322 			sc->curcmd = val;
323 			fifo_put(sc, PS2KC_ACK);
324 			break;
325 		case PS2KC_ECHO:
326 			fifo_put(sc, PS2KC_ECHO);
327 			break;
328 		case PS2KC_SET_LEDS:
329 			sc->curcmd = val;
330 			fifo_put(sc, PS2KC_ACK);
331 			break;
332 		default:
333 			EPRINTLN("Unhandled ps2 keyboard command "
334 			    "0x%02x", val);
335 			break;
336 		}
337 	}
338 	pthread_mutex_unlock(&sc->mtx);
339 }
340 
341 /*
342  * Translate keysym to type 2 scancode and insert into keyboard buffer.
343  */
344 static void
345 ps2kbd_keysym_queue(struct ps2kbd_softc *sc,
346     int down, uint32_t keysym, uint32_t keycode)
347 {
348 	const struct extended_translation *trans;
349 	int e0_prefix, found;
350 	uint8_t code;
351 
352 	assert(pthread_mutex_isowned_np(&sc->mtx));
353 
354 	if (keycode) {
355 		code =  keyset1to2_translations[(uint8_t)(keycode & 0x7f)];
356 		e0_prefix = ((keycode & 0x80) ?  SCANCODE_E0_PREFIX : 0);
357 		found = 1;
358 	} else {
359 		found = 0;
360 		if (keysym < 0x80) {
361 			code = ascii_translations[keysym];
362 			e0_prefix = 0;
363 			found = 1;
364 		} else {
365 			for (trans = &extended_translations[0];
366 			    trans->keysym != 0; trans++) {
367 				if (keysym == trans->keysym) {
368 					code = trans->scancode;
369 					e0_prefix = trans->flags & SCANCODE_E0_PREFIX;
370 					found = 1;
371 					break;
372 				}
373 			}
374 		}
375 	}
376 
377 	if (!found) {
378 		EPRINTLN("Unhandled ps2 keyboard keysym 0x%x", keysym);
379 		return;
380 	}
381 
382 	if (e0_prefix)
383 		fifo_put(sc, 0xe0);
384 	if (!down)
385 		fifo_put(sc, 0xf0);
386 	fifo_put(sc, code);
387 }
388 
389 static void
390 ps2kbd_event(int down, uint32_t keysym, uint32_t keycode, void *arg)
391 {
392 	struct ps2kbd_softc *sc = arg;
393 	int fifo_full;
394 
395 	pthread_mutex_lock(&sc->mtx);
396 	if (!sc->enabled) {
397 		pthread_mutex_unlock(&sc->mtx);
398 		return;
399 	}
400 	fifo_full = sc->fifo.num == PS2KBD_FIFOSZ;
401 	ps2kbd_keysym_queue(sc, down, keysym, keycode);
402 	pthread_mutex_unlock(&sc->mtx);
403 
404 	if (!fifo_full)
405 		atkbdc_event(sc->atkbdc_sc, 1);
406 }
407 
408 static void
409 ps2kbd_update_extended_translation(uint32_t keycode, uint32_t scancode, uint32_t prefix)
410 {
411 	int i = 0;
412 
413 	do {
414 		if (extended_translations[i].keysym == keycode)
415 			break;
416 	} while (extended_translations[++i].keysym);
417 
418 	if (i == (sizeof(extended_translations) / sizeof(struct extended_translation) - 1))
419 		return;
420 
421 	if (!extended_translations[i].keysym)	{
422 		extended_translations[i].keysym = keycode;
423 
424 		extended_translations[i+1].keysym = 0;
425 		extended_translations[i+1].scancode = 0;
426 		extended_translations[i+1].flags = 0;
427 	}
428 
429 	extended_translations[i].scancode = (uint8_t)(scancode & 0xff);
430 	extended_translations[i].flags = (prefix ? SCANCODE_E0_PREFIX : 0);
431 }
432 
433 static void
434 ps2kbd_setkbdlayout(void)
435 {
436 	int err;
437 	int fd;
438 	char path[MAX_PATHNAME];
439 	char *buf, *next, *line;
440 	struct stat sb;
441 	ssize_t sz;
442 	uint8_t ascii;
443 	uint32_t keycode, scancode, prefix;
444 
445 	snprintf(path, MAX_PATHNAME, PS2KBD_LAYOUT_BASEDIR"%s", get_config_value("keyboard.layout") );
446 
447 	err = stat(path, &sb);
448 	if (err)
449 		return;
450 
451 	buf = (char *)malloc(sizeof(char) * sb.st_size);
452 	if (buf == NULL)
453 		return;
454 
455 	fd = open(path, O_RDONLY);
456 	if (fd == -1)
457 		goto out;
458 
459 	sz = read(fd, buf, sb.st_size);
460 
461 	close(fd);
462 
463 	if (sz < 0 || sz != sb.st_size)
464 		goto out;
465 
466 	next = buf;
467 	while ((line = strsep(&next, "\n")) != NULL)	{
468 		if (sscanf(line, "'%c',%x;", &ascii, &scancode) == 2)	{
469 			if (ascii < 0x80)
470 				ascii_translations[ascii] = (uint8_t)(scancode & 0xff);
471 		} else if (sscanf(line, "%x,%x,%x;", &keycode, &scancode, &prefix) == 3 )	{
472 			ps2kbd_update_extended_translation(keycode, scancode, prefix);
473 		} else if (sscanf(line, "%x,%x;", &keycode, &scancode) == 2)	{
474 			if (keycode < 0x80)
475 				ascii_translations[(uint8_t)(keycode & 0xff)] = (uint8_t)(scancode & 0xff);
476 			else
477 				ps2kbd_update_extended_translation(keycode, scancode, 0);
478 		}
479 	}
480 
481 out:
482 	free(buf);
483 }
484 
485 struct ps2kbd_softc *
486 ps2kbd_init(struct atkbdc_softc *atkbdc_sc)
487 {
488 	struct ps2kbd_softc *sc;
489 
490 	if (get_config_value("keyboard.layout") != NULL)
491 		ps2kbd_setkbdlayout();
492 
493 	sc = calloc(1, sizeof (struct ps2kbd_softc));
494 	pthread_mutex_init(&sc->mtx, NULL);
495 	fifo_init(sc);
496 	sc->atkbdc_sc = atkbdc_sc;
497 
498 	console_kbd_register(ps2kbd_event, sc, 1);
499 
500 	return (sc);
501 }
502 
503 #ifdef BHYVE_SNAPSHOT
504 int
505 ps2kbd_snapshot(struct ps2kbd_softc *sc, struct vm_snapshot_meta *meta)
506 {
507 	int ret;
508 
509 	SNAPSHOT_VAR_OR_LEAVE(sc->enabled, meta, ret, done);
510 	SNAPSHOT_VAR_OR_LEAVE(sc->curcmd, meta, ret, done);
511 
512 done:
513 	return (ret);
514 }
515 #endif
516 
517