]> git.friedersdorff.com Git - max/tmk_keyboard.git/blob - converter/ibmpc_usb/ibmpc_usb.c
ibmpc_usb: Fix scan code set change detection
[max/tmk_keyboard.git] / converter / ibmpc_usb / ibmpc_usb.c
1 /*
2 Copyright 2019,2020 Jun Wako <wakojun@gmail.com>
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <stdint.h>
19 #include <stdbool.h>
20 #include "print.h"
21 #include "util.h"
22 #include "debug.h"
23 #include "ibmpc.h"
24 #include "host.h"
25 #include "led.h"
26 #include "matrix.h"
27 #include "timer.h"
28 #include "action.h"
29 #include "ibmpc_usb.h"
30 #include "ibmpc.h"
31
32
33 static void matrix_make(uint8_t code);
34 static void matrix_break(uint8_t code);
35
36 static int8_t process_cs1(uint8_t code);
37 static int8_t process_cs2(uint8_t code);
38 static int8_t process_cs3(uint8_t code);
39
40
41 static uint8_t matrix[MATRIX_ROWS];
42 #define ROW(code)      ((code>>3)&0x0F)
43 #define COL(code)      (code&0x07)
44
45 static int16_t read_wait(uint16_t wait_ms)
46 {
47     uint16_t start = timer_read();
48     int16_t code;
49     while ((code = ibmpc_host_recv()) == -1 && timer_elapsed(start) < wait_ms);
50     return code;
51 }
52
53 static uint16_t read_keyboard_id(void)
54 {
55     uint16_t id = 0;
56     int16_t  code = 0;
57
58     // Disable
59     //code = ibmpc_host_send(0xF5);
60
61     // Read ID
62     code = ibmpc_host_send(0xF2);
63     if (code == -1) { id = 0xFFFF; goto DONE; }     // XT or No keyboard
64     if (code != 0xFA) { id = 0xFFFE; goto DONE; }   // Broken PS/2?
65
66     // ID takes 500ms max TechRef [8] 4-41
67     code = read_wait(500);
68     if (code == -1) { id = 0x0000; goto DONE; }     // AT
69     id = (code & 0xFF)<<8;
70
71     // Mouse responds with one-byte 00, this returns 00FF [y] p.14
72     code = read_wait(500);
73     id |= code & 0xFF;
74
75 DONE:
76     // Enable
77     //code = ibmpc_host_send(0xF4);
78
79     return id;
80 }
81
82 void hook_early_init(void)
83 {
84     ibmpc_host_init();
85     ibmpc_host_enable();
86 }
87
88 void matrix_init(void)
89 {
90     debug_enable = true;
91
92     // initialize matrix state: all keys off
93     for (uint8_t i=0; i < MATRIX_ROWS; i++) matrix[i] = 0x00;
94
95     return;
96 }
97
98 /*
99  * keyboard recognition
100  *
101  * 1. Send F2 to get keyboard ID
102  *      a. no ACK(FA): XT keyobard
103  *      b. ACK but no ID: 84-key AT keyboard CodeSet2
104  *      c. ID is AB 83: PS/2 keyboard CodeSet2
105  *      d. ID is BF BF: Terminal keyboard CodeSet3
106  *      e. error on recv: maybe broken PS/2
107  */
108 uint8_t current_protocol = 0;
109 uint16_t keyboard_id = 0x0000;
110 keyboard_kind_t keyboard_kind = NONE;
111 uint8_t matrix_scan(void)
112 {
113     // scan code reading states
114     static enum {
115         INIT,
116         WAIT_SETTLE,
117         AT_RESET,
118         XT_RESET,
119         XT_RESET_WAIT,
120         XT_RESET_DONE,
121         WAIT_AA,
122         WAIT_AABF,
123         WAIT_AABFBF,
124         READ_ID,
125         SETUP,
126         LOOP,
127     } state = INIT;
128     static uint16_t init_time;
129
130
131     if (ibmpc_error) {
132         xprintf("\nERR:%02X ISR:%04X ", ibmpc_error, ibmpc_isr_debug);
133
134         // when recv error, neither send error nor buffer full
135         if (!(ibmpc_error & (IBMPC_ERR_SEND | IBMPC_ERR_FULL))) {
136             // keyboard init again
137             if (state == LOOP) {
138                 state = INIT;
139             }
140         }
141
142         // clear or process error
143         ibmpc_error = IBMPC_ERR_NONE;
144         ibmpc_isr_debug = 0;
145     }
146
147     // check protocol AT/XT
148     if (ibmpc_protocol && ibmpc_protocol != current_protocol) {
149         xprintf("\nPROTO:%02X ISR:%04X ", ibmpc_protocol, ibmpc_isr_debug);
150         current_protocol = ibmpc_protocol;
151         ibmpc_isr_debug = 0;
152     }
153
154     switch (state) {
155         case INIT:
156             ibmpc_host_disable();
157
158             xprintf("I%u ", timer_read());
159             keyboard_kind = NONE;
160             keyboard_id = 0x0000;
161
162             matrix_clear();
163             clear_keyboard();
164
165             init_time = timer_read();
166             state = WAIT_SETTLE;
167             break;
168         case WAIT_SETTLE:
169             // wait for keyboard to settle after plugin
170             if (timer_elapsed(init_time) > 1000) {
171                 state = AT_RESET;
172             }
173             break;
174         case AT_RESET:
175             ibmpc_host_isr_clear();
176             ibmpc_host_enable();
177             wait_ms(1); // keyboard can't respond to command without this
178
179             // SKIDATA-2-DE(and some other keyboards?) stores 'Code Set' setting in nonvlatile memory
180             // and keeps it until receiving reset. Sending reset here may be useful to clear it, perhaps.
181             // https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#select-alternate-scan-codesf0
182
183             // reset command
184             if (0xFA == ibmpc_host_send(0xFF)) {
185                 state = WAIT_AA;
186             } else {
187                 state = XT_RESET;
188             }
189             xprintf("A%u ", timer_read());
190             break;
191         case XT_RESET:
192             // Reset XT-initialize keyboard
193             // XT: hard reset 500ms for IBM XT Type-1 keyboard and clones
194             // XT: soft reset 20ms min(clock Lo)
195             ibmpc_host_disable();   // soft reset: inihibit(clock Lo/Data Hi)
196             IBMPC_RST_LO();         // hard reset: reset pin Lo
197
198             init_time = timer_read();
199             state = XT_RESET_WAIT;
200             break;
201         case XT_RESET_WAIT:
202             if (timer_elapsed(init_time) > 500) {
203                 state = XT_RESET_DONE;
204             }
205             break;
206         case XT_RESET_DONE:
207             IBMPC_RST_HIZ();        // hard reset: reset pin HiZ
208             ibmpc_host_isr_clear();
209             ibmpc_host_enable();    // soft reset: idle(clock Hi/Data Hi)
210
211             xprintf("X%u ", timer_read());
212             init_time = timer_read();
213             state = WAIT_AA;
214             break;
215         case WAIT_AA:
216             // 1) Read BAT code and ID on keybaord power-up
217             // For example, XT/AT sends 'AA' and Terminal sends 'AA BF BF' after BAT
218             // AT 84-key: POR and BAT can take 900-9900ms according to AT TechRef [8] 4-7
219             // AT 101/102-key: POR and BAT can take 450-2500ms according to AT TechRef [8] 4-39
220             // 2) Read key typed by user or anything after error on protocol or scan code
221             // This can happen in case of keyboard hotswap, unstable hardware, signal integrity problem or bug
222
223             /* wait until keyboard sends any code without 10000ms timeout
224             if (timer_elapsed(init_time) > 10000) {
225                 state = READ_ID;
226             }
227             */
228             if (ibmpc_host_recv() != -1) {  // wait for AA
229                 xprintf("W%u ", timer_read());
230                 init_time = timer_read();
231                 state = WAIT_AABF;
232             }
233             break;
234         case WAIT_AABF:
235             // NOTE: we can omit to wait BF BF
236             // ID takes 500ms max? TechRef [8] 4-41, though 1ms is enough for 122-key Terminal 6110345
237             if (timer_elapsed(init_time) > 500) {
238                 state = READ_ID;
239             }
240             if (ibmpc_host_recv() != -1) {  // wait for BF
241                 xprintf("W%u ", timer_read());
242                 init_time = timer_read();
243                 state = WAIT_AABFBF;
244             }
245             break;
246         case WAIT_AABFBF:
247             if (timer_elapsed(init_time) > 500) {
248                 state = READ_ID;
249             }
250             if (ibmpc_host_recv() != -1) {  // wait for BF
251                 xprintf("W%u ", timer_read());
252                 state = READ_ID;
253             }
254             break;
255         case READ_ID:
256             keyboard_id = read_keyboard_id();
257             xprintf("R%u ", timer_read());
258
259             if (0x0000 == keyboard_id) {            // CodeSet2 AT(IBM PC AT 84-key)
260                 keyboard_kind = PC_AT;
261             } else if (0xFFFF == keyboard_id) {     // CodeSet1 XT
262                 keyboard_kind = PC_XT;
263             } else if (0xFFFE == keyboard_id) {     // CodeSet2 PS/2 fails to response?
264                 keyboard_kind = PC_AT;
265             } else if (0x00FF == keyboard_id) {     // Mouse is not supported
266                 xprintf("Mouse: not supported\n");
267                 keyboard_kind = NONE;
268 #ifdef G80_2551_SUPPORT
269             } else if (0xAB86 == keyboard_id ||
270                        0xAB85 == keyboard_id) {     // For G80-2551 and other 122-key terminal
271                 // https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#ab86
272                 // https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#ab85
273
274                 if ((0xFA == ibmpc_host_send(0xF0)) &&
275                     (0xFA == ibmpc_host_send(0x03))) {
276                     // switch to code set 3
277                     keyboard_kind = PC_TERMINAL;
278                 } else {
279                     keyboard_kind = PC_AT;
280                 }
281 #endif
282             } else if (0xBFB0 == keyboard_id) {     // IBM RT Keyboard
283                 // https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#bfb0
284                 // TODO: LED indicator fix
285                 //keyboard_kind = PC_TERMINAL_IBM_RT;
286                 keyboard_kind = PC_TERMINAL;
287             } else if (0xAB00 == (keyboard_id & 0xFF00)) {  // CodeSet2 PS/2
288                 keyboard_kind = PC_AT;
289             } else if (0xBF00 == (keyboard_id & 0xFF00)) {  // CodeSet3 Terminal
290                 keyboard_kind = PC_TERMINAL;
291             } else {
292                 keyboard_kind = PC_AT;
293             }
294
295             xprintf("\nID:%04X(%d) ", keyboard_id, keyboard_kind);
296
297             state = SETUP;
298             break;
299         case SETUP:
300             xprintf("S%u ", timer_read());
301             switch (keyboard_kind) {
302                 case PC_XT:
303                     break;
304                 case PC_AT:
305                     led_set(host_keyboard_leds());
306                     break;
307                 case PC_TERMINAL:
308                     // Set all keys to make/break type
309                     ibmpc_host_send(0xF8);
310                     // This should not be harmful
311                     led_set(host_keyboard_leds());
312                     break;
313                 default:
314                     break;
315             }
316             state = LOOP;
317             xprintf("L%u ", timer_read());
318         case LOOP:
319             {
320                 uint16_t code = ibmpc_host_recv();
321                 if (code == -1) {
322                     // no code
323                     break;
324                 }
325
326                 // Keyboard Error/Overrun([3]p.26) or Buffer full
327                 // Scan Code Set 1: 0xFF
328                 // Scan Code Set 2 and 3: 0x00
329                 // Buffer full(IBMPC_ERR_FULL): 0xFF
330                 if (code == 0x00 || code == 0xFF) {
331                     xprintf("\n!OVERRUN![");
332
333                     // read and ignore data
334                     do {
335                         wait_ms(10);
336                     } while ((code = ibmpc_host_recv()) != -1);
337                     xprintf("]\n");
338
339                     // clear stuck keys
340                     matrix_clear();
341                     clear_keyboard();
342                     break;
343                 }
344
345                 switch (keyboard_kind) {
346                     case PC_XT:
347                         if (process_cs1(code) == -1) state = INIT;
348                         break;
349                     case PC_AT:
350                         if (process_cs2(code) == -1) state = INIT;
351                         break;
352                     case PC_TERMINAL:
353                         if (process_cs3(code) == -1) state = INIT;
354                         break;
355                     default:
356                         break;
357                 }
358             }
359             break;
360         default:
361             break;
362     }
363     return 1;
364 }
365
366 inline
367 bool matrix_is_on(uint8_t row, uint8_t col)
368 {
369     return (matrix[row] & (1<<col));
370 }
371
372 inline
373 uint8_t matrix_get_row(uint8_t row)
374 {
375     return matrix[row];
376 }
377
378 uint8_t matrix_key_count(void)
379 {
380     uint8_t count = 0;
381     for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
382         count += bitpop(matrix[i]);
383     }
384     return count;
385 }
386
387
388 inline
389 static void matrix_make(uint8_t code)
390 {
391     if (!matrix_is_on(ROW(code), COL(code))) {
392         matrix[ROW(code)] |= 1<<COL(code);
393     }
394 }
395
396 inline
397 static void matrix_break(uint8_t code)
398 {
399     if (matrix_is_on(ROW(code), COL(code))) {
400         matrix[ROW(code)] &= ~(1<<COL(code));
401     }
402 }
403
404 void matrix_clear(void)
405 {
406     for (uint8_t i=0; i < MATRIX_ROWS; i++) matrix[i] = 0x00;
407 }
408
409 void led_set(uint8_t usb_led)
410 {
411     // Sending before keyboard recognition may be harmful for XT keyboard
412     if (keyboard_kind == NONE) return;
413
414     // XT keyobard doesn't support any command and it is harmful perhaps
415     // https://github.com/tmk/tmk_keyboard/issues/635#issuecomment-626993437
416     if (keyboard_kind == PC_XT) return;
417
418     // It should be safe to send the command to keyboards with AT protocol
419     // - IBM Terminal doesn't support the command and response with 0xFE but it is not harmful.
420     // - Some other Terminals like G80-2551 supports the command.
421     //   https://geekhack.org/index.php?topic=103648.msg2894921#msg2894921
422
423     // TODO: PC_TERMINAL_IBM_RT support
424     uint8_t ibmpc_led = 0;
425     if (usb_led &  (1<<USB_LED_SCROLL_LOCK))
426         ibmpc_led |= (1<<IBMPC_LED_SCROLL_LOCK);
427     if (usb_led &  (1<<USB_LED_NUM_LOCK))
428         ibmpc_led |= (1<<IBMPC_LED_NUM_LOCK);
429     if (usb_led &  (1<<USB_LED_CAPS_LOCK))
430         ibmpc_led |= (1<<IBMPC_LED_CAPS_LOCK);
431     ibmpc_host_set_led(ibmpc_led);
432 }
433
434
435 /*******************************************************************************
436  * XT: Scan Code Set 1
437  *
438  * See [3], [a]
439  *
440  * E0-escaped scan codes are translated into unused range of the matrix.(54-7F)
441  *
442  *     01-53: Normal codes used in original XT keyboard
443  *     54-7F: Not used in original XT keyboard
444  *
445  *         0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
446  *     50  -   -   -   -   *   *   x   x   x   x   *   *   *   *   *   *
447  *     60  *   *   *   *   x   x   x   x   x   x   x   x   x   x   x   *
448  *     70  x   *   *   x   *   *   x   *   *   x   *   x   *   x   x   *
449  *
450  * -: codes existed in original XT keyboard
451  * *: E0-escaped codes translated
452  * x: Non-espcaped codes(Some are not used in real keyboards probably)
453  *
454  * Codes assigned in range 54-7F:
455  *
456  *     50  -                60  Up*                 70  KANAx
457  *     51  -                61  Left*               71  Insert*
458  *     52  -                62  Down*               72  Delete*
459  *     53  -                63  Right*              73  ROx
460  *     54  PrintScr*        64  F13x                74  Home*
461  *     55  Pause*           65  F14x                75  End*
462  *     56  Euro2x           66  F15x                76  F24x
463  *     57  F11x             67  F16x                77  PageUp*
464  *     58  F12x             68  F17x                78  PageDown*
465  *     59  Keypad=x         69  F18x                79  HENKANx
466  *     5A  LGUI*            6A  F19x                7A  RCTL*
467  *     5B  RGUI*            6B  F20x                7B  MUHENKANx
468  *     5C  APP*             6C  F21x                7C  RALT*
469  *     5D  Mute*            6D  F22x                7D  JPYx
470  *     5E  Volume Down*     6E  F23x                7E  Keypad,x
471  *     5F  Volume Up*       6F  Keypad Enter*       7F  Keypad/ *
472  */
473 static uint8_t cs1_e0code(uint8_t code) {
474     switch(code) {
475         // Original IBM XT keyboard doesn't use E0-codes probably
476         // Some XT compatilble keyobards need these keys?
477         case 0x37: return 0x54; // Print Screen
478         case 0x46: return 0x55; // Ctrl + Pause
479         case 0x5B: return 0x5A; // Left  GUI
480         case 0x5C: return 0x5B; // Right GUI
481         case 0x5D: return 0x5C; // Application
482         case 0x20: return 0x5D; // Mute
483         case 0x2E: return 0x5E; // Volume Down
484         case 0x30: return 0x5F; // Volume Up
485         case 0x48: return 0x60; // Up
486         case 0x4B: return 0x61; // Left
487         case 0x50: return 0x62; // Down
488         case 0x4D: return 0x63; // Right
489         case 0x1C: return 0x6F; // Keypad Enter
490         case 0x52: return 0x71; // Insert
491         case 0x53: return 0x72; // Delete
492         case 0x47: return 0x74; // Home
493         case 0x4F: return 0x75; // End
494         case 0x49: return 0x77; // Page Up
495         case 0x51: return 0x78; // Page Down
496         case 0x1D: return 0x7A; // Right Ctrl
497         case 0x38: return 0x7C; // Right Alt
498         case 0x35: return 0x7F; // Keypad /
499
500         // Shared matrix cell with other keys
501         case 0x5E: return 0x70; // Power (KANA)
502         case 0x5F: return 0x79; // Sleep (HENKAN)
503         case 0x63: return 0x7B; // Wake  (MUHENKAN)
504
505         default:
506            xprintf("!CS1_E0_%02X!\n", code);
507            return code;
508     }
509     return 0x00;
510 }
511
512 static int8_t process_cs1(uint8_t code)
513 {
514     static enum {
515         INIT,
516         E0,
517         // Pause: E1 1D 45, E1 9D C5 [a] (TODO: test)
518         E1,
519         E1_1D,
520         E1_9D,
521     } state = INIT;
522
523     switch (state) {
524         case INIT:
525             switch (code) {
526                 case 0xE0:
527                     state = E0;
528                     break;
529                 case 0xE1:
530                     state = E1;
531                     break;
532                 default:
533                     if (code < 0x80)
534                         matrix_make(code);
535                     else
536                         matrix_break(code & 0x7F);
537                     break;
538             }
539             break;
540         case E0:
541             switch (code) {
542                 case 0x2A:
543                 case 0xAA:
544                 case 0x36:
545                 case 0xB6:
546                     //ignore fake shift
547                     state = INIT;
548                     break;
549                 default:
550                     if (code < 0x80)
551                         matrix_make(cs1_e0code(code));
552                     else
553                         matrix_break(cs1_e0code(code & 0x7F));
554                     state = INIT;
555                     break;
556             }
557             break;
558         case E1:
559             switch (code) {
560                 case 0x1D:
561                     state = E1_1D;
562                     break;
563                 case 0x9D:
564                     state = E1_9D;
565                     break;
566                 default:
567                     state = INIT;
568                     break;
569             }
570             break;
571         case E1_1D:
572             switch (code) {
573                 case 0x45:
574                     matrix_make(0x55);
575                     break;
576                 default:
577                     state = INIT;
578                     break;
579             }
580             break;
581         case E1_9D:
582             switch (code) {
583                 case 0x45:
584                     matrix_break(0x55);
585                     break;
586                 default:
587                     state = INIT;
588                     break;
589             }
590             break;
591         default:
592             state = INIT;
593     }
594     return 0;
595 }
596
597
598 /*******************************************************************************
599  * AT, PS/2: Scan Code Set 2
600  *
601  * Exceptional Handling
602  * --------------------
603  * Some keys should be handled exceptionally. See [b].
604  *
605  * Scan codes are varied or prefix/postfix'd depending on modifier key state.
606  *
607  * 1) Insert, Delete, Home, End, PageUp, PageDown, Up, Down, Right, Left
608  *     a) when Num Lock is off
609  *     modifiers | make                      | break
610  *     ----------+---------------------------+----------------------
611  *     Ohter     |                    <make> | <break>
612  *     LShift    | E0 F0 12           <make> | <break>  E0 12
613  *     RShift    | E0 F0 59           <make> | <break>  E0 59
614  *     L+RShift  | E0 F0 12  E0 F0 59 <make> | <break>  E0 59 E0 12
615  *
616  *     b) when Num Lock is on
617  *     modifiers | make                      | break
618  *     ----------+---------------------------+----------------------
619  *     Other     | E0 12              <make> | <break>  E0 F0 12
620  *     Shift'd   |                    <make> | <break>
621  *
622  *     Handling: These prefix/postfix codes are ignored.
623  *
624  *
625  * 2) Keypad /
626  *     modifiers | make                      | break
627  *     ----------+---------------------------+----------------------
628  *     Ohter     |                    <make> | <break>
629  *     LShift    | E0 F0 12           <make> | <break>  E0 12
630  *     RShift    | E0 F0 59           <make> | <break>  E0 59
631  *     L+RShift  | E0 F0 12  E0 F0 59 <make> | <break>  E0 59 E0 12
632  *
633  *     Handling: These prefix/postfix codes are ignored.
634  *
635  *
636  * 3) PrintScreen
637  *     modifiers | make         | break
638  *     ----------+--------------+-----------------------------------
639  *     Other     | E0 12  E0 7C | E0 F0 7C  E0 F0 12
640  *     Shift'd   |        E0 7C | E0 F0 7C
641  *     Control'd |        E0 7C | E0 F0 7C
642  *     Alt'd     |           84 | F0 84
643  *
644  *     Handling: These prefix/postfix codes are ignored, and both scan codes
645  *               'E0 7C' and 84 are seen as PrintScreen.
646  *
647  * 4) Pause
648  *     modifiers | make(no break code)
649  *     ----------+--------------------------------------------------
650  *     Other     | E1 14 77 E1 F0 14 F0 77
651  *     Control'd | E0 7E E0 F0 7E
652  *
653  *     Handling: Both code sequences are treated as a whole.
654  *               And we need a ad hoc 'pseudo break code' hack to get the key off
655  *               because it has no break code.
656  *
657  * Notes:
658  * 'Hanguel/English'(F1) and 'Hanja'(F2) have no break code. See [a].
659  * These two Korean keys need exceptional handling and are not supported for now.
660  *
661  */
662 static uint8_t cs2_e0code(uint8_t code) {
663     switch(code) {
664         // E0 prefixed codes translation See [a].
665         case 0x11: return 0x0F; // right alt
666         case 0x14: return 0x17; // right control
667         case 0x1F: return 0x19; // left GUI
668         case 0x27: return 0x1F; // right GUI
669         case 0x2F: return 0x5C; // apps
670         case 0x4A: return 0x60; // keypad /
671         case 0x5A: return 0x62; // keypad enter
672         case 0x69: return 0x27; // end
673         case 0x6B: return 0x53; // cursor left
674         case 0x6C: return 0x2F; // home
675         case 0x70: return 0x39; // insert
676         case 0x71: return 0x37; // delete
677         case 0x72: return 0x3F; // cursor down
678         case 0x74: return 0x47; // cursor right
679         case 0x75: return 0x4F; // cursor up
680         case 0x7A: return 0x56; // page down
681         case 0x7D: return 0x5E; // page up
682         case 0x7C: return 0x7F; // Print Screen
683         case 0x7E: return 0x00; // Control'd Pause
684
685         case 0x21: return 0x65; // volume down
686         case 0x32: return 0x6E; // volume up
687         case 0x23: return 0x6F; // mute
688         case 0x10: return 0x08; // (WWW search)     -> F13
689         case 0x18: return 0x10; // (WWW favourites) -> F14
690         case 0x20: return 0x18; // (WWW refresh)    -> F15
691         case 0x28: return 0x20; // (WWW stop)       -> F16
692         case 0x30: return 0x28; // (WWW forward)    -> F17
693         case 0x38: return 0x30; // (WWW back)       -> F18
694         case 0x3A: return 0x38; // (WWW home)       -> F19
695         case 0x40: return 0x40; // (my computer)    -> F20
696         case 0x48: return 0x48; // (email)          -> F21
697         case 0x2B: return 0x50; // (calculator)     -> F22
698         case 0x34: return 0x08; // (play/pause)     -> F13
699         case 0x3B: return 0x10; // (stop)           -> F14
700         case 0x15: return 0x18; // (previous track) -> F15
701         case 0x4D: return 0x20; // (next track)     -> F16
702         case 0x50: return 0x28; // (media select)   -> F17
703         case 0x5E: return 0x50; // (ACPI wake)      -> F22
704         case 0x3F: return 0x57; // (ACPI sleep)     -> F23
705         case 0x37: return 0x5F; // (ACPI power)     -> F24
706
707         // https://github.com/tmk/tmk_keyboard/pull/636
708         case 0x03: return 0x18; // Help        DEC LK411 -> F15
709         case 0x04: return 0x08; // F13         DEC LK411
710         case 0x0B: return 0x20; // Do          DEC LK411 -> F16
711         case 0x0C: return 0x10; // F14         DEC LK411
712         case 0x0D: return 0x19; // LCompose    DEC LK411 -> LGUI
713         case 0x79: return 0x6D; // KP-         DEC LK411 -> PCMM
714         case 0x83: return 0x28; // F17         DEC LK411
715         default: return (code & 0x7F);
716     }
717 }
718
719 static int8_t process_cs2(uint8_t code)
720 {
721     // scan code reading states
722     static enum {
723         INIT,
724         F0,
725         E0,
726         E0_F0,
727         // Pause
728         E1,
729         E1_14,
730         E1_F0,
731         E1_F0_14,
732         E1_F0_14_F0,
733     } state = INIT;
734
735     switch (state) {
736         case INIT:
737             switch (code) {
738                 case 0xE0:
739                     state = E0;
740                     break;
741                 case 0xF0:
742                     state = F0;
743                     break;
744                 case 0xE1:
745                     state = E1;
746                     break;
747                 case 0x83:  // F7
748                     matrix_make(0x02);
749                     state = INIT;
750                     break;
751                 case 0x84:  // Alt'd PrintScreen
752                     matrix_make(0x7F);
753                     state = INIT;
754                     break;
755                 case 0xAA:  // Self-test passed
756                 case 0xFC:  // Self-test failed
757                     // replug or unstable connection probably
758                 default:    // normal key make
759                     state = INIT;
760                     if (code < 0x80) {
761                         matrix_make(code);
762                     } else {
763                         matrix_clear();
764                         xprintf("!CS2_INIT!\n");
765                         return -1;
766                     }
767             }
768             break;
769         case E0:    // E0-Prefixed
770             switch (code) {
771                 case 0x12:  // to be ignored
772                 case 0x59:  // to be ignored
773                     state = INIT;
774                     break;
775                 case 0xF0:
776                     state = E0_F0;
777                     break;
778                 default:
779                     state = INIT;
780                     if (code < 0x80) {
781                         matrix_make(cs2_e0code(code));
782                     } else {
783                         matrix_clear();
784                         xprintf("!CS2_E0!\n");
785                         return -1;
786                     }
787             }
788             break;
789         case F0:    // Break code
790             switch (code) {
791                 case 0x83:  // F7
792                     matrix_break(0x02);
793                     state = INIT;
794                     break;
795                 case 0x84:  // Alt'd PrintScreen
796                     matrix_break(0x7F);
797                     state = INIT;
798                     break;
799                 default:
800                     state = INIT;
801                     if (code < 0x80) {
802                         matrix_break(code);
803                     } else {
804                         matrix_clear();
805                         xprintf("!CS2_F0!\n");
806                         return -1;
807                     }
808             }
809             break;
810         case E0_F0: // Break code of E0-prefixed
811             switch (code) {
812                 case 0x12:  // to be ignored
813                 case 0x59:  // to be ignored
814                     state = INIT;
815                     break;
816                 default:
817                     state = INIT;
818                     if (code < 0x80) {
819                         matrix_break(cs2_e0code(code));
820                     } else {
821                         matrix_clear();
822                         xprintf("!CS2_E0_F0!\n");
823                         return -1;
824                     }
825             }
826             break;
827         // Pause make: E1 14 77
828         case E1:
829             switch (code) {
830                 case 0x14:
831                     state = E1_14;
832                     break;
833                 case 0xF0:
834                     state = E1_F0;
835                     break;
836                 default:
837                     state = INIT;
838             }
839             break;
840         case E1_14:
841             switch (code) {
842                 case 0x77:
843                     matrix_make(0x00);
844                     state = INIT;
845                     break;
846                 default:
847                     state = INIT;
848             }
849             break;
850         // Pause break: E1 F0 14 F0 77
851         case E1_F0:
852             switch (code) {
853                 case 0x14:
854                     state = E1_F0_14;
855                     break;
856                 default:
857                     state = INIT;
858             }
859             break;
860         case E1_F0_14:
861             switch (code) {
862                 case 0xF0:
863                     state = E1_F0_14_F0;
864                     break;
865                 default:
866                     state = INIT;
867             }
868             break;
869         case E1_F0_14_F0:
870             switch (code) {
871                 case 0x77:
872                     matrix_break(0x00);
873                     state = INIT;
874                     break;
875                 default:
876                     state = INIT;
877             }
878             break;
879         default:
880             state = INIT;
881     }
882     return 0;
883 }
884
885 /*
886  * Terminal: Scan Code Set 3
887  *
888  * See [3], [7] and
889  * https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#scan-code-set-3
890  */
891 static int8_t process_cs3(uint8_t code)
892 {
893     static enum {
894         READY,
895         F0,
896 #ifdef G80_2551_SUPPORT
897         // G80-2551 four extra keys around cursor keys
898         G80,
899         G80_F0,
900 #endif
901     } state = READY;
902
903     switch (state) {
904         case READY:
905             switch (code) {
906                 case 0xF0:
907                     state = F0;
908                     break;
909                 case 0x83:  // PrintScreen
910                     matrix_make(0x02);
911                     break;
912                 case 0x84:  // Keypad *
913                     matrix_make(0x7F);
914                     break;
915                 case 0x85:  // Muhenkan
916                     matrix_make(0x0B);
917                     break;
918                 case 0x86:  // Henkan
919                     matrix_make(0x06);
920                     break;
921                 case 0x87:  // Hiragana
922                     matrix_make(0x00);
923                     break;
924                 case 0x8B:  // Left GUI
925                     matrix_make(0x01);
926                     break;
927                 case 0x8C:  // Right GUI
928                     matrix_make(0x09);
929                     break;
930                 case 0x8D:  // Application
931                     matrix_make(0x0A);
932                     break;
933 #ifdef G80_2551_SUPPORT
934                 case 0x80:  // G80-2551 four extra keys around cursor keys
935                     state = G80;
936                     break;
937 #endif
938                 default:    // normal key make
939                     if (code < 0x80) {
940                         matrix_make(code);
941                     } else {
942                         xprintf("!CS3_READY!\n");
943                         return -1;
944                     }
945             }
946             break;
947         case F0:    // Break code
948             switch (code) {
949                 case 0x83:  // PrintScreen
950                     matrix_break(0x02);
951                     state = READY;
952                     break;
953                 case 0x84:  // Keypad *
954                     matrix_break(0x7F);
955                     state = READY;
956                     break;
957                 case 0x85:  // Muhenkan
958                     matrix_break(0x0B);
959                     state = READY;
960                     break;
961                 case 0x86:  // Henkan
962                     matrix_break(0x06);
963                     state = READY;
964                     break;
965                 case 0x87:  // Hiragana
966                     matrix_break(0x00);
967                     state = READY;
968                     break;
969                 case 0x8B:  // Left GUI
970                     matrix_break(0x01);
971                     state = READY;
972                     break;
973                 case 0x8C:  // Right GUI
974                     matrix_break(0x09);
975                     state = READY;
976                     break;
977                 case 0x8D:  // Application
978                     matrix_break(0x0A);
979                     state = READY;
980                     break;
981                 default:
982                     state = READY;
983                     if (code < 0x80) {
984                         matrix_break(code);
985                     } else {
986                         xprintf("!CS3_F0!\n");
987                         return -1;
988                     }
989             }
990             break;
991 #ifdef G80_2551_SUPPORT
992         /*
993          * G80-2551 terminal keyboard support
994          * https://deskthority.net/wiki/Cherry_G80-2551
995          * https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#g80-2551-in-code-set-3
996          */
997         case G80:   // G80-2551 four extra keys around cursor keys
998             switch (code) {
999                 case (0x26):    // TD= -> JYEN
1000                     matrix_make(0x5D);
1001                     break;
1002                 case (0x25):    // page with edge -> NUHS
1003                     matrix_make(0x53);
1004                     break;
1005                 case (0x16):    // two pages -> RO
1006                     matrix_make(0x51);
1007                     break;
1008                 case (0x1E):    // calc -> KANA
1009                     matrix_make(0x00);
1010                     break;
1011                 case (0xF0):
1012                     state = G80_F0;
1013                     return 0;
1014                 default:
1015                     // Not supported
1016                     matrix_clear();
1017                     break;
1018             }
1019             state = READY;
1020             break;
1021         case G80_F0:
1022             switch (code) {
1023                 case (0x26):    // TD= -> JYEN
1024                     matrix_break(0x5D);
1025                     break;
1026                 case (0x25):    // page with edge -> NUHS
1027                     matrix_break(0x53);
1028                     break;
1029                 case (0x16):    // two pages -> RO
1030                     matrix_break(0x51);
1031                     break;
1032                 case (0x1E):    // calc -> KANA
1033                     matrix_break(0x00);
1034                     break;
1035                 default:
1036                     // Not supported
1037                     matrix_clear();
1038                     break;
1039             }
1040             state = READY;
1041             break;
1042 #endif
1043     }
1044     return 0;
1045 }
1046
1047 /*
1048  * IBM PC Keyboard Protocol Resources:
1049  *
1050  * [a] Microsoft USB HID to PS/2 Translation Table - Scan Code Set 1 and 2
1051  * http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf
1052  *
1053  * [b] Microsoft Keyboard Scan Code Specification - Special rules of Scan Code Set 1 and 2
1054  * http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/scancode.doc
1055  *
1056  * [1] PS/2 Reference Manuals - Collection of IBM Personal System/2 documents.
1057  * http://www.mcamafia.de/pdf/pdfref.htm
1058  *
1059  * [2] Keyboard and Auxiliary Device Controller - Signal Timing and Format
1060  * http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
1061  *
1062  * [3] Keyboards(101- and 102-key) - Keyboard Layout, Scan Code Set, POR, and Commands.
1063  * http://www.mcamafia.de/pdf/ibm_hitrc11.pdf
1064  *
1065  * [4] IBM PC XT Keyboard Protocol
1066  * https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol
1067  *
1068  * [5] IBM Keyboard Scan Code by John Elliott - 83-key, 84-key, 102-key and 122-key
1069  * https://www.seasip.info/VintagePC/index.html
1070  *
1071  * [6] IBM 1391406 Keyboard - Scan Code Set 2 of 102-key PS/2 keyboard
1072  * https://www.seasip.info/VintagePC/ibm_1391406.html
1073  *
1074  * [7] The IBM 6110344 Keyboard - Scan Code Set 3 of 122-key terminal keyboard
1075  * https://www.seasip.info/VintagePC/ibm_6110344.html
1076  *
1077  * [8] IBM PC AT Technical Reference 1986
1078  * http://bitsavers.org/pdf/ibm/pc/at/6183355_PC_AT_Technical_Reference_Mar86.pdf
1079  *
1080  * [y] TrackPoint Engineering Specifications for version 3E
1081  * https://web.archive.org/web/20100526161812/http://wwwcssrv.almaden.ibm.com/trackpoint/download.html
1082  *
1083  * [z] [Soarer's XT/AT/PS2/Terminal to USB converter]
1084  * https://geekhack.org/index.php?topic=17458.0
1085  *
1086  */