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