xref: /illumos-gate/usr/src/cmd/bhyve/ps2kbd.c (revision 0d1087e85d1cd423a6cbe5358a51a160350e956e)
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 static void
182 fifo_init(struct ps2kbd_softc *sc)
183 {
184 	struct fifo *fifo;
185 
186 	fifo = &sc->fifo;
187 	fifo->size = sizeof(((struct fifo *)0)->buf);
188 }
189 
190 static void
191 fifo_reset(struct ps2kbd_softc *sc)
192 {
193 	struct fifo *fifo;
194 
195 	fifo = &sc->fifo;
196 	bzero(fifo, sizeof(struct fifo));
197 	fifo->size = sizeof(((struct fifo *)0)->buf);
198 }
199 
200 static void
201 fifo_put(struct ps2kbd_softc *sc, uint8_t val)
202 {
203 	struct fifo *fifo;
204 
205 	fifo = &sc->fifo;
206 	if (fifo->num < fifo->size) {
207 		fifo->buf[fifo->windex] = val;
208 		fifo->windex = (fifo->windex + 1) % fifo->size;
209 		fifo->num++;
210 	}
211 }
212 
213 static int
214 fifo_get(struct ps2kbd_softc *sc, uint8_t *val)
215 {
216 	struct fifo *fifo;
217 
218 	fifo = &sc->fifo;
219 	if (fifo->num > 0) {
220 		*val = fifo->buf[fifo->rindex];
221 		fifo->rindex = (fifo->rindex + 1) % fifo->size;
222 		fifo->num--;
223 		return (0);
224 	}
225 
226 	return (-1);
227 }
228 
229 int
230 ps2kbd_read(struct ps2kbd_softc *sc, uint8_t *val)
231 {
232 	int retval;
233 
234 	pthread_mutex_lock(&sc->mtx);
235 	retval = fifo_get(sc, val);
236 	pthread_mutex_unlock(&sc->mtx);
237 
238 	return (retval);
239 }
240 
241 void
242 ps2kbd_write(struct ps2kbd_softc *sc, uint8_t val)
243 {
244 	pthread_mutex_lock(&sc->mtx);
245 	if (sc->curcmd) {
246 		switch (sc->curcmd) {
247 		case PS2KC_SET_TYPEMATIC:
248 			fifo_put(sc, PS2KC_ACK);
249 			break;
250 		case PS2KC_SET_SCANCODE_SET:
251 			fifo_put(sc, PS2KC_ACK);
252 			break;
253 		case PS2KC_SET_LEDS:
254 			fifo_put(sc, PS2KC_ACK);
255 			break;
256 		default:
257 			EPRINTLN("Unhandled ps2 keyboard current "
258 			    "command byte 0x%02x", val);
259 			break;
260 		}
261 		sc->curcmd = 0;
262 	} else {
263 		switch (val) {
264 		case 0x00:
265 			fifo_put(sc, PS2KC_ACK);
266 			break;
267 		case PS2KC_RESET_DEV:
268 			fifo_reset(sc);
269 			fifo_put(sc, PS2KC_ACK);
270 			fifo_put(sc, PS2KC_BAT_SUCCESS);
271 			break;
272 		case PS2KC_DISABLE:
273 			sc->enabled = false;
274 			fifo_put(sc, PS2KC_ACK);
275 			break;
276 		case PS2KC_ENABLE:
277 			sc->enabled = true;
278 			fifo_reset(sc);
279 			fifo_put(sc, PS2KC_ACK);
280 			break;
281 		case PS2KC_SET_TYPEMATIC:
282 			sc->curcmd = val;
283 			fifo_put(sc, PS2KC_ACK);
284 			break;
285 		case PS2KC_SEND_DEV_ID:
286 			fifo_put(sc, PS2KC_ACK);
287 			fifo_put(sc, 0xab);
288 			fifo_put(sc, 0x83);
289 			break;
290 		case PS2KC_SET_SCANCODE_SET:
291 			sc->curcmd = val;
292 			fifo_put(sc, PS2KC_ACK);
293 			break;
294 		case PS2KC_ECHO:
295 			fifo_put(sc, PS2KC_ECHO);
296 			break;
297 		case PS2KC_SET_LEDS:
298 			sc->curcmd = val;
299 			fifo_put(sc, PS2KC_ACK);
300 			break;
301 		default:
302 			EPRINTLN("Unhandled ps2 keyboard command "
303 			    "0x%02x", val);
304 			break;
305 		}
306 	}
307 	pthread_mutex_unlock(&sc->mtx);
308 }
309 
310 /*
311  * Translate keysym to type 2 scancode and insert into keyboard buffer.
312  */
313 static void
314 ps2kbd_keysym_queue(struct ps2kbd_softc *sc,
315     int down, uint32_t keysym)
316 {
317 	assert(pthread_mutex_isowned_np(&sc->mtx));
318 	int e0_prefix, found;
319 	uint8_t code;
320 	const struct extended_translation *trans;
321 
322 	found = 0;
323 	if (keysym < 0x80) {
324 		code = ascii_translations[keysym];
325 		e0_prefix = 0;
326 		found = 1;
327 	} else {
328 		for (trans = &(extended_translations[0]); trans->keysym != 0;
329 		    trans++) {
330 			if (keysym == trans->keysym) {
331 				code = trans->scancode;
332 				e0_prefix = trans->flags & SCANCODE_E0_PREFIX;
333 				found = 1;
334 				break;
335 			}
336 		}
337 	}
338 
339 	if (!found) {
340 		EPRINTLN("Unhandled ps2 keyboard keysym 0x%x", keysym);
341 		return;
342 	}
343 
344 	if (e0_prefix)
345 		fifo_put(sc, 0xe0);
346 	if (!down)
347 		fifo_put(sc, 0xf0);
348 	fifo_put(sc, code);
349 }
350 
351 static void
352 ps2kbd_event(int down, uint32_t keysym, void *arg)
353 {
354 	struct ps2kbd_softc *sc = arg;
355 	int fifo_full;
356 
357 	pthread_mutex_lock(&sc->mtx);
358 	if (!sc->enabled) {
359 		pthread_mutex_unlock(&sc->mtx);
360 		return;
361 	}
362 	fifo_full = sc->fifo.num == PS2KBD_FIFOSZ;
363 	ps2kbd_keysym_queue(sc, down, keysym);
364 	pthread_mutex_unlock(&sc->mtx);
365 
366 	if (!fifo_full)
367 		atkbdc_event(sc->atkbdc_sc, 1);
368 }
369 
370 struct ps2kbd_softc *
371 ps2kbd_init(struct atkbdc_softc *atkbdc_sc)
372 {
373 	struct ps2kbd_softc *sc;
374 
375 	sc = calloc(1, sizeof (struct ps2kbd_softc));
376 	pthread_mutex_init(&sc->mtx, NULL);
377 	fifo_init(sc);
378 	sc->atkbdc_sc = atkbdc_sc;
379 
380 	console_kbd_register(ps2kbd_event, sc, 1);
381 
382 	return (sc);
383 }
384 
385