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