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