1/*-
2 * Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
3 * Copyright (c) 2015 Nahanni Systems Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: releng/11.0/usr.sbin/bhyve/ps2kbd.c 302408 2016-07-08 00:04:57Z gjb $");
30
31#include <sys/types.h>
32
33#include <assert.h>
34#include <stdbool.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <strings.h>
38#include <pthread.h>
39#include <pthread_np.h>
40
41#include "atkbdc.h"
42#include "console.h"
43
44/* keyboard device commands */
45#define	PS2KC_RESET_DEV		0xff
46#define	PS2KC_DISABLE		0xf5
47#define	PS2KC_ENABLE		0xf4
48#define	PS2KC_SET_TYPEMATIC	0xf3
49#define	PS2KC_SEND_DEV_ID	0xf2
50#define	PS2KC_SET_SCANCODE_SET	0xf0
51#define	PS2KC_ECHO		0xee
52#define	PS2KC_SET_LEDS		0xed
53
54#define	PS2KC_BAT_SUCCESS	0xaa
55#define	PS2KC_ACK		0xfa
56
57#define	PS2KBD_FIFOSZ		16
58
59struct fifo {
60	uint8_t	buf[PS2KBD_FIFOSZ];
61	int	rindex;		/* index to read from */
62	int	windex;		/* index to write to */
63	int	num;		/* number of bytes in the fifo */
64	int	size;		/* size of the fifo */
65};
66
67struct ps2kbd_softc {
68	struct atkbdc_softc	*atkbdc_sc;
69	pthread_mutex_t		mtx;
70
71	bool			enabled;
72	struct fifo		fifo;
73
74	uint8_t			curcmd;	/* current command for next byte */
75};
76
77static void
78fifo_init(struct ps2kbd_softc *sc)
79{
80	struct fifo *fifo;
81
82	fifo = &sc->fifo;
83	fifo->size = sizeof(((struct fifo *)0)->buf);
84}
85
86static void
87fifo_reset(struct ps2kbd_softc *sc)
88{
89	struct fifo *fifo;
90
91	fifo = &sc->fifo;
92	bzero(fifo, sizeof(struct fifo));
93	fifo->size = sizeof(((struct fifo *)0)->buf);
94}
95
96static void
97fifo_put(struct ps2kbd_softc *sc, uint8_t val)
98{
99	struct fifo *fifo;
100
101	fifo = &sc->fifo;
102	if (fifo->num < fifo->size) {
103		fifo->buf[fifo->windex] = val;
104		fifo->windex = (fifo->windex + 1) % fifo->size;
105		fifo->num++;
106	}
107}
108
109static int
110fifo_get(struct ps2kbd_softc *sc, uint8_t *val)
111{
112	struct fifo *fifo;
113
114	fifo = &sc->fifo;
115	if (fifo->num > 0) {
116		*val = fifo->buf[fifo->rindex];
117		fifo->rindex = (fifo->rindex + 1) % fifo->size;
118		fifo->num--;
119		return (0);
120	}
121
122	return (-1);
123}
124
125int
126ps2kbd_read(struct ps2kbd_softc *sc, uint8_t *val)
127{
128	int retval;
129
130	pthread_mutex_lock(&sc->mtx);
131	retval = fifo_get(sc, val);
132	pthread_mutex_unlock(&sc->mtx);
133
134	return (retval);
135}
136
137void
138ps2kbd_write(struct ps2kbd_softc *sc, uint8_t val)
139{
140	pthread_mutex_lock(&sc->mtx);
141	if (sc->curcmd) {
142		switch (sc->curcmd) {
143		case PS2KC_SET_TYPEMATIC:
144			fifo_put(sc, PS2KC_ACK);
145			break;
146		case PS2KC_SET_SCANCODE_SET:
147			fifo_put(sc, PS2KC_ACK);
148			break;
149		case PS2KC_SET_LEDS:
150			fifo_put(sc, PS2KC_ACK);
151			break;
152		default:
153			fprintf(stderr, "Unhandled ps2 keyboard current "
154			    "command byte 0x%02x\n", val);
155			break;
156		}
157		sc->curcmd = 0;
158	} else {
159		switch (val) {
160		case 0x00:
161			fifo_put(sc, PS2KC_ACK);
162			break;
163		case PS2KC_RESET_DEV:
164			fifo_reset(sc);
165			fifo_put(sc, PS2KC_ACK);
166			fifo_put(sc, PS2KC_BAT_SUCCESS);
167			break;
168		case PS2KC_DISABLE:
169			sc->enabled = false;
170			fifo_put(sc, PS2KC_ACK);
171			break;
172		case PS2KC_ENABLE:
173			sc->enabled = true;
174			fifo_reset(sc);
175			fifo_put(sc, PS2KC_ACK);
176			break;
177		case PS2KC_SET_TYPEMATIC:
178			sc->curcmd = val;
179			fifo_put(sc, PS2KC_ACK);
180			break;
181		case PS2KC_SEND_DEV_ID:
182			fifo_put(sc, PS2KC_ACK);
183			fifo_put(sc, 0xab);
184			fifo_put(sc, 0x83);
185			break;
186		case PS2KC_SET_SCANCODE_SET:
187			sc->curcmd = val;
188			fifo_put(sc, PS2KC_ACK);
189			break;
190		case PS2KC_ECHO:
191			fifo_put(sc, PS2KC_ECHO);
192			break;
193		case PS2KC_SET_LEDS:
194			sc->curcmd = val;
195			fifo_put(sc, PS2KC_ACK);
196			break;
197		default:
198			fprintf(stderr, "Unhandled ps2 keyboard command "
199			    "0x%02x\n", val);
200			break;
201		}
202	}
203	pthread_mutex_unlock(&sc->mtx);
204}
205
206/*
207 * Translate keysym to type 2 scancode and insert into keyboard buffer.
208 */
209static void
210ps2kbd_keysym_queue(struct ps2kbd_softc *sc,
211    int down, uint32_t keysym)
212{
213	/* ASCII to type 2 scancode lookup table */
214	const uint8_t translation[128] = {
215		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
216		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
217		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
218		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
219		0x29, 0x16, 0x52, 0x26, 0x25, 0x2e, 0x3d, 0x52,
220		0x46, 0x45, 0x3e, 0x55, 0x41, 0x4e, 0x49, 0x4a,
221		0x45, 0x16, 0x1e, 0x26, 0x25, 0x2e, 0x36, 0x3d,
222		0x3e, 0x46, 0x4c, 0x4c, 0x41, 0x55, 0x49, 0x4a,
223		0x1e, 0x1c, 0x32, 0x21, 0x23, 0x24, 0x2b, 0x34,
224		0x33, 0x43, 0x3b, 0x42, 0x4b, 0x3a, 0x31, 0x44,
225		0x4d, 0x15, 0x2d, 0x1b, 0x2c, 0x3c, 0x2a, 0x1d,
226		0x22, 0x35, 0x1a, 0x54, 0x5d, 0x5b, 0x36, 0x4e,
227		0x0e, 0x1c, 0x32, 0x21, 0x23, 0x24, 0x2b, 0x34,
228		0x33, 0x43, 0x3b, 0x42, 0x4b, 0x3a, 0x31, 0x44,
229		0x4d, 0x15, 0x2d, 0x1b, 0x2c, 0x3c, 0x2a, 0x1d,
230		0x22, 0x35, 0x1a, 0x54, 0x5d, 0x5b, 0x0e, 0x00,
231	};
232
233	assert(pthread_mutex_isowned_np(&sc->mtx));
234
235	switch (keysym) {
236	case 0x0 ... 0x7f:
237		if (!down)
238			fifo_put(sc, 0xf0);
239		fifo_put(sc, translation[keysym]);
240		break;
241	case 0xff08:	/* Back space */
242		if (!down)
243			fifo_put(sc, 0xf0);
244		fifo_put(sc, 0x66);
245		break;
246	case 0xff09:	/* Tab */
247		if (!down)
248			fifo_put(sc, 0xf0);
249		fifo_put(sc, 0x0d);
250		break;
251	case 0xff0d:	/* Return  */
252		if (!down)
253			fifo_put(sc, 0xf0);
254		fifo_put(sc, 0x5a);
255		break;
256	case 0xff1b:	/* Escape */
257		if (!down)
258			fifo_put(sc, 0xf0);
259		fifo_put(sc, 0x76);
260		break;
261	case 0xff50:	/* Home */
262		fifo_put(sc, 0xe0);
263		if (!down)
264			fifo_put(sc, 0xf0);
265		fifo_put(sc, 0x6c);
266		break;
267	case 0xff51:	/* Left arrow */
268		fifo_put(sc, 0xe0);
269		if (!down)
270			fifo_put(sc, 0xf0);
271		fifo_put(sc, 0x6b);
272		break;
273	case 0xff52:	/* Up arrow */
274		fifo_put(sc, 0xe0);
275		if (!down)
276			fifo_put(sc, 0xf0);
277		fifo_put(sc, 0x75);
278		break;
279	case 0xff53:	/* Right arrow */
280		fifo_put(sc, 0xe0);
281		if (!down)
282			fifo_put(sc, 0xf0);
283		fifo_put(sc, 0x74);
284		break;
285	case 0xff54:	/* Down arrow */
286		fifo_put(sc, 0xe0);
287		if (!down)
288			fifo_put(sc, 0xf0);
289		fifo_put(sc, 0x72);
290		break;
291	case 0xff55:	/* PgUp */
292		fifo_put(sc, 0xe0);
293		if (!down)
294			fifo_put(sc, 0xf0);
295		fifo_put(sc, 0x7d);
296		break;
297	case 0xff56:	/* PgDwn */
298		fifo_put(sc, 0xe0);
299		if (!down)
300			fifo_put(sc, 0xf0);
301		fifo_put(sc, 0x7a);
302		break;
303	case 0xff57:	/* End */
304		fifo_put(sc, 0xe0);
305		if (!down)
306			fifo_put(sc, 0xf0);
307		fifo_put(sc, 0x69);
308		break;
309	case 0xff63:	/* Ins */
310		fifo_put(sc, 0xe0);
311		if (!down)
312			fifo_put(sc, 0xf0);
313		fifo_put(sc, 0x70);
314		break;
315	case 0xff8d:	/* Keypad Enter */
316		fifo_put(sc, 0xe0);
317		if (!down)
318			fifo_put(sc, 0xf0);
319		fifo_put(sc, 0x5a);
320		break;
321	case 0xffe1:	/* Left shift */
322		if (!down)
323			fifo_put(sc, 0xf0);
324		fifo_put(sc, 0x12);
325		break;
326	case 0xffe2:	/* Right shift */
327		/* XXX */
328		break;
329	case 0xffe3:	/* Left control */
330		if (!down)
331			fifo_put(sc, 0xf0);
332		fifo_put(sc, 0x14);
333		break;
334	case 0xffe4:	/* Right control */
335		/* XXX */
336		break;
337	case 0xffe7:	/* Left meta */
338		/* XXX */
339		break;
340	case 0xffe8:	/* Right meta */
341		/* XXX */
342		break;
343	case 0xffe9:	/* Left alt */
344		if (!down)
345			fifo_put(sc, 0xf0);
346		fifo_put(sc, 0x11);
347		break;
348	case 0xffea:	/* Right alt */
349		fifo_put(sc, 0xe0);
350		if (!down)
351			fifo_put(sc, 0xf0);
352		fifo_put(sc, 0x11);
353		break;
354	case 0xffeb:	/* Left Windows */
355		fifo_put(sc, 0xe0);
356		if (!down)
357			fifo_put(sc, 0xf0);
358		fifo_put(sc, 0x1f);
359		break;
360	case 0xffec:	/* Right Windows */
361		fifo_put(sc, 0xe0);
362		if (!down)
363			fifo_put(sc, 0xf0);
364		fifo_put(sc, 0x27);
365		break;
366	case 0xffbe:    /* F1 */
367		if (!down)
368			fifo_put(sc, 0xf0);
369		fifo_put(sc, 0x05);
370		break;
371	case 0xffbf:    /* F2 */
372		if (!down)
373			fifo_put(sc, 0xf0);
374		fifo_put(sc, 0x06);
375		break;
376	case 0xffc0:    /* F3 */
377		if (!down)
378			fifo_put(sc, 0xf0);
379		fifo_put(sc, 0x04);
380		break;
381	case 0xffc1:    /* F4 */
382		if (!down)
383			fifo_put(sc, 0xf0);
384		fifo_put(sc, 0x0C);
385		break;
386	case 0xffc2:    /* F5 */
387		if (!down)
388			fifo_put(sc, 0xf0);
389		fifo_put(sc, 0x03);
390		break;
391	case 0xffc3:    /* F6 */
392		if (!down)
393			fifo_put(sc, 0xf0);
394		fifo_put(sc, 0x0B);
395		break;
396	case 0xffc4:    /* F7 */
397		if (!down)
398			fifo_put(sc, 0xf0);
399		fifo_put(sc, 0x83);
400		break;
401	case 0xffc5:    /* F8 */
402		if (!down)
403			fifo_put(sc, 0xf0);
404		fifo_put(sc, 0x0A);
405		break;
406	case 0xffc6:    /* F9 */
407		if (!down)
408			fifo_put(sc, 0xf0);
409		fifo_put(sc, 0x01);
410		break;
411	case 0xffc7:    /* F10 */
412		if (!down)
413			fifo_put(sc, 0xf0);
414		fifo_put(sc, 0x09);
415		break;
416	case 0xffc8:    /* F11 */
417		if (!down)
418			fifo_put(sc, 0xf0);
419		fifo_put(sc, 0x78);
420		break;
421	case 0xffc9:    /* F12 */
422		if (!down)
423			fifo_put(sc, 0xf0);
424		fifo_put(sc, 0x07);
425		break;
426	case 0xffff:    /* Del */
427		fifo_put(sc, 0xe0);
428		if (!down)
429			fifo_put(sc, 0xf0);
430		fifo_put(sc, 0x71);
431		break;
432	default:
433		fprintf(stderr, "Unhandled ps2 keyboard keysym 0x%x\n",
434		     keysym);
435		break;
436	}
437}
438
439static void
440ps2kbd_event(int down, uint32_t keysym, void *arg)
441{
442	struct ps2kbd_softc *sc = arg;
443	int fifo_full;
444
445	pthread_mutex_lock(&sc->mtx);
446	if (!sc->enabled) {
447		pthread_mutex_unlock(&sc->mtx);
448		return;
449	}
450	fifo_full = sc->fifo.num == PS2KBD_FIFOSZ;
451	ps2kbd_keysym_queue(sc, down, keysym);
452	pthread_mutex_unlock(&sc->mtx);
453
454	if (!fifo_full)
455		atkbdc_event(sc->atkbdc_sc, 1);
456}
457
458struct ps2kbd_softc *
459ps2kbd_init(struct atkbdc_softc *atkbdc_sc)
460{
461	struct ps2kbd_softc *sc;
462
463	sc = calloc(1, sizeof (struct ps2kbd_softc));
464	pthread_mutex_init(&sc->mtx, NULL);
465	fifo_init(sc);
466	sc->atkbdc_sc = atkbdc_sc;
467
468	console_kbd_register(ps2kbd_event, sc, 1);
469
470	return (sc);
471}
472
473