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