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