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
62struct 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
70struct 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
81struct 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 */
90static 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 */
162static 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
181static void
182fifo_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
190static void
191fifo_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
200static void
201fifo_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
213static int
214fifo_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
229int
230ps2kbd_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
241void
242ps2kbd_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 */
313static void
314ps2kbd_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
351static void
352ps2kbd_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
370struct ps2kbd_softc *
371ps2kbd_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