ps2kbd.c revision 336189
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 336189 2018-07-11 07:16:13Z 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
79static void
80fifo_init(struct ps2kbd_softc *sc)
81{
82	struct fifo *fifo;
83
84	fifo = &sc->fifo;
85	fifo->size = sizeof(((struct fifo *)0)->buf);
86}
87
88static void
89fifo_reset(struct ps2kbd_softc *sc)
90{
91	struct fifo *fifo;
92
93	fifo = &sc->fifo;
94	bzero(fifo, sizeof(struct fifo));
95	fifo->size = sizeof(((struct fifo *)0)->buf);
96}
97
98static void
99fifo_put(struct ps2kbd_softc *sc, uint8_t val)
100{
101	struct fifo *fifo;
102
103	fifo = &sc->fifo;
104	if (fifo->num < fifo->size) {
105		fifo->buf[fifo->windex] = val;
106		fifo->windex = (fifo->windex + 1) % fifo->size;
107		fifo->num++;
108	}
109}
110
111static int
112fifo_get(struct ps2kbd_softc *sc, uint8_t *val)
113{
114	struct fifo *fifo;
115
116	fifo = &sc->fifo;
117	if (fifo->num > 0) {
118		*val = fifo->buf[fifo->rindex];
119		fifo->rindex = (fifo->rindex + 1) % fifo->size;
120		fifo->num--;
121		return (0);
122	}
123
124	return (-1);
125}
126
127int
128ps2kbd_read(struct ps2kbd_softc *sc, uint8_t *val)
129{
130	int retval;
131
132	pthread_mutex_lock(&sc->mtx);
133	retval = fifo_get(sc, val);
134	pthread_mutex_unlock(&sc->mtx);
135
136	return (retval);
137}
138
139void
140ps2kbd_write(struct ps2kbd_softc *sc, uint8_t val)
141{
142	pthread_mutex_lock(&sc->mtx);
143	if (sc->curcmd) {
144		switch (sc->curcmd) {
145		case PS2KC_SET_TYPEMATIC:
146			fifo_put(sc, PS2KC_ACK);
147			break;
148		case PS2KC_SET_SCANCODE_SET:
149			fifo_put(sc, PS2KC_ACK);
150			break;
151		case PS2KC_SET_LEDS:
152			fifo_put(sc, PS2KC_ACK);
153			break;
154		default:
155			fprintf(stderr, "Unhandled ps2 keyboard current "
156			    "command byte 0x%02x\n", val);
157			break;
158		}
159		sc->curcmd = 0;
160	} else {
161		switch (val) {
162		case 0x00:
163			fifo_put(sc, PS2KC_ACK);
164			break;
165		case PS2KC_RESET_DEV:
166			fifo_reset(sc);
167			fifo_put(sc, PS2KC_ACK);
168			fifo_put(sc, PS2KC_BAT_SUCCESS);
169			break;
170		case PS2KC_DISABLE:
171			sc->enabled = false;
172			fifo_put(sc, PS2KC_ACK);
173			break;
174		case PS2KC_ENABLE:
175			sc->enabled = true;
176			fifo_reset(sc);
177			fifo_put(sc, PS2KC_ACK);
178			break;
179		case PS2KC_SET_TYPEMATIC:
180			sc->curcmd = val;
181			fifo_put(sc, PS2KC_ACK);
182			break;
183		case PS2KC_SEND_DEV_ID:
184			fifo_put(sc, PS2KC_ACK);
185			fifo_put(sc, 0xab);
186			fifo_put(sc, 0x83);
187			break;
188		case PS2KC_SET_SCANCODE_SET:
189			sc->curcmd = val;
190			fifo_put(sc, PS2KC_ACK);
191			break;
192		case PS2KC_ECHO:
193			fifo_put(sc, PS2KC_ECHO);
194			break;
195		case PS2KC_SET_LEDS:
196			sc->curcmd = val;
197			fifo_put(sc, PS2KC_ACK);
198			break;
199		default:
200			fprintf(stderr, "Unhandled ps2 keyboard command "
201			    "0x%02x\n", val);
202			break;
203		}
204	}
205	pthread_mutex_unlock(&sc->mtx);
206}
207
208/*
209 * Translate keysym to type 2 scancode and insert into keyboard buffer.
210 */
211static void
212ps2kbd_keysym_queue(struct ps2kbd_softc *sc,
213    int down, uint32_t keysym)
214{
215	/* ASCII to type 2 scancode lookup table */
216	const uint8_t translation[128] = {
217		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
218		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
219		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
220		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
221		0x29, 0x16, 0x52, 0x26, 0x25, 0x2e, 0x3d, 0x52,
222		0x46, 0x45, 0x3e, 0x55, 0x41, 0x4e, 0x49, 0x4a,
223		0x45, 0x16, 0x1e, 0x26, 0x25, 0x2e, 0x36, 0x3d,
224		0x3e, 0x46, 0x4c, 0x4c, 0x41, 0x55, 0x49, 0x4a,
225		0x1e, 0x1c, 0x32, 0x21, 0x23, 0x24, 0x2b, 0x34,
226		0x33, 0x43, 0x3b, 0x42, 0x4b, 0x3a, 0x31, 0x44,
227		0x4d, 0x15, 0x2d, 0x1b, 0x2c, 0x3c, 0x2a, 0x1d,
228		0x22, 0x35, 0x1a, 0x54, 0x5d, 0x5b, 0x36, 0x4e,
229		0x0e, 0x1c, 0x32, 0x21, 0x23, 0x24, 0x2b, 0x34,
230		0x33, 0x43, 0x3b, 0x42, 0x4b, 0x3a, 0x31, 0x44,
231		0x4d, 0x15, 0x2d, 0x1b, 0x2c, 0x3c, 0x2a, 0x1d,
232		0x22, 0x35, 0x1a, 0x54, 0x5d, 0x5b, 0x0e, 0x00,
233	};
234
235	assert(pthread_mutex_isowned_np(&sc->mtx));
236
237	switch (keysym) {
238	case 0x0 ... 0x7f:
239		if (!down)
240			fifo_put(sc, 0xf0);
241		fifo_put(sc, translation[keysym]);
242		break;
243	case 0xff08:	/* Back space */
244		if (!down)
245			fifo_put(sc, 0xf0);
246		fifo_put(sc, 0x66);
247		break;
248	case 0xff09:	/* Tab */
249		if (!down)
250			fifo_put(sc, 0xf0);
251		fifo_put(sc, 0x0d);
252		break;
253	case 0xff0d:	/* Return  */
254		if (!down)
255			fifo_put(sc, 0xf0);
256		fifo_put(sc, 0x5a);
257		break;
258	case 0xff1b:	/* Escape */
259		if (!down)
260			fifo_put(sc, 0xf0);
261		fifo_put(sc, 0x76);
262		break;
263	case 0xff50:	/* Home */
264		fifo_put(sc, 0xe0);
265		if (!down)
266			fifo_put(sc, 0xf0);
267		fifo_put(sc, 0x6c);
268		break;
269	case 0xff51:	/* Left arrow */
270		fifo_put(sc, 0xe0);
271		if (!down)
272			fifo_put(sc, 0xf0);
273		fifo_put(sc, 0x6b);
274		break;
275	case 0xff52:	/* Up arrow */
276		fifo_put(sc, 0xe0);
277		if (!down)
278			fifo_put(sc, 0xf0);
279		fifo_put(sc, 0x75);
280		break;
281	case 0xff53:	/* Right arrow */
282		fifo_put(sc, 0xe0);
283		if (!down)
284			fifo_put(sc, 0xf0);
285		fifo_put(sc, 0x74);
286		break;
287	case 0xff54:	/* Down arrow */
288		fifo_put(sc, 0xe0);
289		if (!down)
290			fifo_put(sc, 0xf0);
291		fifo_put(sc, 0x72);
292		break;
293	case 0xff55:	/* PgUp */
294		fifo_put(sc, 0xe0);
295		if (!down)
296			fifo_put(sc, 0xf0);
297		fifo_put(sc, 0x7d);
298		break;
299	case 0xff56:	/* PgDwn */
300		fifo_put(sc, 0xe0);
301		if (!down)
302			fifo_put(sc, 0xf0);
303		fifo_put(sc, 0x7a);
304		break;
305	case 0xff57:	/* End */
306		fifo_put(sc, 0xe0);
307		if (!down)
308			fifo_put(sc, 0xf0);
309		fifo_put(sc, 0x69);
310		break;
311	case 0xff63:	/* Ins */
312		fifo_put(sc, 0xe0);
313		if (!down)
314			fifo_put(sc, 0xf0);
315		fifo_put(sc, 0x70);
316		break;
317	case 0xff8d:	/* Keypad Enter */
318		fifo_put(sc, 0xe0);
319		if (!down)
320			fifo_put(sc, 0xf0);
321		fifo_put(sc, 0x5a);
322		break;
323	case 0xffe1:	/* Left shift */
324		if (!down)
325			fifo_put(sc, 0xf0);
326		fifo_put(sc, 0x12);
327		break;
328	case 0xffe2:	/* Right shift */
329		if (!down)
330			fifo_put(sc, 0xf0);
331		fifo_put(sc, 0x59);
332		break;
333	case 0xffe3:	/* Left control */
334		if (!down)
335			fifo_put(sc, 0xf0);
336		fifo_put(sc, 0x14);
337		break;
338	case 0xffe4:	/* Right control */
339		fifo_put(sc, 0xe0);
340		if (!down)
341			fifo_put(sc, 0xf0);
342		fifo_put(sc, 0x14);
343		break;
344	case 0xffe7:	/* Left meta */
345		/* XXX */
346		break;
347	case 0xffe8:	/* Right meta */
348		/* XXX */
349		break;
350	case 0xffe9:	/* Left alt */
351		if (!down)
352			fifo_put(sc, 0xf0);
353		fifo_put(sc, 0x11);
354		break;
355	case 0xfe03:	/* AltGr */
356	case 0xffea:	/* Right alt */
357		fifo_put(sc, 0xe0);
358		if (!down)
359			fifo_put(sc, 0xf0);
360		fifo_put(sc, 0x11);
361		break;
362	case 0xffeb:	/* Left Windows */
363		fifo_put(sc, 0xe0);
364		if (!down)
365			fifo_put(sc, 0xf0);
366		fifo_put(sc, 0x1f);
367		break;
368	case 0xffec:	/* Right Windows */
369		fifo_put(sc, 0xe0);
370		if (!down)
371			fifo_put(sc, 0xf0);
372		fifo_put(sc, 0x27);
373		break;
374	case 0xffbe:    /* F1 */
375		if (!down)
376			fifo_put(sc, 0xf0);
377		fifo_put(sc, 0x05);
378		break;
379	case 0xffbf:    /* F2 */
380		if (!down)
381			fifo_put(sc, 0xf0);
382		fifo_put(sc, 0x06);
383		break;
384	case 0xffc0:    /* F3 */
385		if (!down)
386			fifo_put(sc, 0xf0);
387		fifo_put(sc, 0x04);
388		break;
389	case 0xffc1:    /* F4 */
390		if (!down)
391			fifo_put(sc, 0xf0);
392		fifo_put(sc, 0x0C);
393		break;
394	case 0xffc2:    /* F5 */
395		if (!down)
396			fifo_put(sc, 0xf0);
397		fifo_put(sc, 0x03);
398		break;
399	case 0xffc3:    /* F6 */
400		if (!down)
401			fifo_put(sc, 0xf0);
402		fifo_put(sc, 0x0B);
403		break;
404	case 0xffc4:    /* F7 */
405		if (!down)
406			fifo_put(sc, 0xf0);
407		fifo_put(sc, 0x83);
408		break;
409	case 0xffc5:    /* F8 */
410		if (!down)
411			fifo_put(sc, 0xf0);
412		fifo_put(sc, 0x0A);
413		break;
414	case 0xffc6:    /* F9 */
415		if (!down)
416			fifo_put(sc, 0xf0);
417		fifo_put(sc, 0x01);
418		break;
419	case 0xffc7:    /* F10 */
420		if (!down)
421			fifo_put(sc, 0xf0);
422		fifo_put(sc, 0x09);
423		break;
424	case 0xffc8:    /* F11 */
425		if (!down)
426			fifo_put(sc, 0xf0);
427		fifo_put(sc, 0x78);
428		break;
429	case 0xffc9:    /* F12 */
430		if (!down)
431			fifo_put(sc, 0xf0);
432		fifo_put(sc, 0x07);
433		break;
434	case 0xffff:    /* Del */
435		fifo_put(sc, 0xe0);
436		if (!down)
437			fifo_put(sc, 0xf0);
438		fifo_put(sc, 0x71);
439		break;
440	default:
441		fprintf(stderr, "Unhandled ps2 keyboard keysym 0x%x\n",
442		     keysym);
443		break;
444	}
445}
446
447static void
448ps2kbd_event(int down, uint32_t keysym, void *arg)
449{
450	struct ps2kbd_softc *sc = arg;
451	int fifo_full;
452
453	pthread_mutex_lock(&sc->mtx);
454	if (!sc->enabled) {
455		pthread_mutex_unlock(&sc->mtx);
456		return;
457	}
458	fifo_full = sc->fifo.num == PS2KBD_FIFOSZ;
459	ps2kbd_keysym_queue(sc, down, keysym);
460	pthread_mutex_unlock(&sc->mtx);
461
462	if (!fifo_full)
463		atkbdc_event(sc->atkbdc_sc, 1);
464}
465
466struct ps2kbd_softc *
467ps2kbd_init(struct atkbdc_softc *atkbdc_sc)
468{
469	struct ps2kbd_softc *sc;
470
471	sc = calloc(1, sizeof (struct ps2kbd_softc));
472	pthread_mutex_init(&sc->mtx, NULL);
473	fifo_init(sc);
474	sc->atkbdc_sc = atkbdc_sc;
475
476	console_kbd_register(ps2kbd_event, sc, 1);
477
478	return (sc);
479}
480
481