]> git.donarmstrong.com Git - tmk_firmware.git/blob - converter/ps2_usb/matrix.c
ps2_usb: Fix README
[tmk_firmware.git] / converter / ps2_usb / matrix.c
1 /*
2 Copyright 2011 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 <avr/io.h>
21 #include <util/delay.h>
22 #include "action.h"
23 #include "print.h"
24 #include "util.h"
25 #include "debug.h"
26 #include "ps2.h"
27 #include "matrix.h"
28
29
30 static void matrix_make(uint8_t code);
31 static void matrix_break(uint8_t code);
32 static void matrix_clear(void);
33 #ifdef MATRIX_HAS_GHOST
34 static bool matrix_has_ghost_in_row(uint8_t row);
35 #endif
36
37
38 /*
39  * Matrix Array usage:
40  * 'Scan Code Set 2' is assigned into 256(32x8)cell matrix.
41  * Hmm, it is very sparse and not efficient :(
42  *
43  * Notes:
44  * Both 'Hanguel/English'(F1) and 'Hanja'(F2) collide with 'Delete'(E0 71) and 'Down'(E0 72).
45  * These two Korean keys need exceptional handling and are not supported for now. Sorry.
46  *
47  *    8bit wide
48  *   +---------+
49  *  0|         |
50  *  :|   XX    | 00-7F for normal codes(without E0-prefix)
51  *  f|_________|
52  * 10|         |
53  *  :|  E0 YY  | 80-FF for E0-prefixed codes
54  * 1f|         |     (<YY>|0x80) is used as matrix position.
55  *   +---------+
56  *
57  * Exceptions:
58  * 0x83:    F7(0x83) This is a normal code but beyond  0x7F.
59  * 0xFC:    PrintScreen
60  * 0xFE:    Pause
61  */
62 static uint8_t matrix[MATRIX_ROWS];
63 #define ROW(code)      (code>>3)
64 #define COL(code)      (code&0x07)
65
66 // matrix positions for exceptional keys
67 #define F7             (0x83)
68 #define PRINT_SCREEN   (0xFC)
69 #define PAUSE          (0xFE)
70
71 static bool is_modified = false;
72
73
74 inline
75 uint8_t matrix_rows(void)
76 {
77     return MATRIX_ROWS;
78 }
79
80 inline
81 uint8_t matrix_cols(void)
82 {
83     return MATRIX_COLS;
84 }
85
86 void matrix_init(void)
87 {
88     debug_enable = true;
89     ps2_host_init();
90
91     // initialize matrix state: all keys off
92     for (uint8_t i=0; i < MATRIX_ROWS; i++) matrix[i] = 0x00;
93
94     return;
95 }
96
97 /*
98  * PS/2 Scan Code Set 2: Exceptional Handling
99  *
100  * There are several keys to be handled exceptionally.
101  * The scan code for these keys are varied or prefix/postfix'd
102  * depending on modifier key state.
103  *
104  * Keyboard Scan Code Specification:
105  *     http://www.microsoft.com/whdc/archive/scancode.mspx
106  *     http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/scancode.doc
107  *
108  *
109  * 1) Insert, Delete, Home, End, PageUp, PageDown, Up, Down, Right, Left
110  *     a) when Num Lock is off
111  *     modifiers | make                      | break
112  *     ----------+---------------------------+----------------------
113  *     Ohter     |                    <make> | <break>
114  *     LShift    | E0 F0 12           <make> | <break>  E0 12
115  *     RShift    | E0 F0 59           <make> | <break>  E0 59
116  *     L+RShift  | E0 F0 12  E0 F0 59 <make> | <break>  E0 59 E0 12
117  *
118  *     b) when Num Lock is on
119  *     modifiers | make                      | break
120  *     ----------+---------------------------+----------------------
121  *     Other     | E0 12              <make> | <break>  E0 F0 12
122  *     Shift'd   |                    <make> | <break>
123  *
124  *     Handling: These prefix/postfix codes are ignored.
125  *
126  *
127  * 2) Keypad /
128  *     modifiers | make                      | break
129  *     ----------+---------------------------+----------------------
130  *     Ohter     |                    <make> | <break>
131  *     LShift    | E0 F0 12           <make> | <break>  E0 12
132  *     RShift    | E0 F0 59           <make> | <break>  E0 59
133  *     L+RShift  | E0 F0 12  E0 F0 59 <make> | <break>  E0 59 E0 12
134  *
135  *     Handling: These prefix/postfix codes are ignored.
136  *
137  *
138  * 3) PrintScreen
139  *     modifiers | make         | break
140  *     ----------+--------------+-----------------------------------
141  *     Other     | E0 12  E0 7C | E0 F0 7C  E0 F0 12
142  *     Shift'd   |        E0 7C | E0 F0 7C
143  *     Control'd |        E0 7C | E0 F0 7C
144  *     Alt'd     |           84 | F0 84
145  *
146  *     Handling: These prefix/postfix codes are ignored, and both scan codes
147  *               'E0 7C' and 84 are seen as PrintScreen.
148  *
149  * 4) Pause
150  *     modifiers | make(no break code)
151  *     ----------+--------------------------------------------------
152  *     Other     | E1 14 77 E1 F0 14 F0 77
153  *     Control'd | E0 7E E0 F0 7E
154  *
155  *     Handling: Both code sequences are treated as a whole.
156  *               And we need a ad hoc 'pseudo break code' hack to get the key off
157  *               because it has no break code.
158  *
159  */
160 uint8_t matrix_scan(void)
161 {
162
163     // scan code reading states
164     static enum {
165         INIT,
166         F0,
167         E0,
168         E0_F0,
169         // Pause
170         E1,
171         E1_14,
172         E1_14_77,
173         E1_14_77_E1,
174         E1_14_77_E1_F0,
175         E1_14_77_E1_F0_14,
176         E1_14_77_E1_F0_14_F0,
177         // Control'd Pause
178         E0_7E,
179         E0_7E_E0,
180         E0_7E_E0_F0,
181     } state = INIT;
182
183
184     is_modified = false;
185
186     // 'pseudo break code' hack
187     if (matrix_is_on(ROW(PAUSE), COL(PAUSE))) {
188         matrix_break(PAUSE);
189     }
190
191     uint8_t code = ps2_host_recv();
192     if (!ps2_error) {
193         switch (state) {
194             case INIT:
195                 switch (code) {
196                     case 0xE0:
197                         state = E0;
198                         break;
199                     case 0xF0:
200                         state = F0;
201                         break;
202                     case 0xE1:
203                         state = E1;
204                         break;
205                     case 0x83:  // F7
206                         matrix_make(F7);
207                         state = INIT;
208                         break;
209                     case 0x84:  // Alt'd PrintScreen
210                         matrix_make(PRINT_SCREEN);
211                         state = INIT;
212                         break;
213                     case 0x00:  // Overrun [3]p.25
214                         matrix_clear();
215                         clear_keyboard();
216                         print("Overrun\n");
217                         state = INIT;
218                         break;
219                     default:    // normal key make
220                         if (code < 0x80) {
221                             matrix_make(code);
222                         } else {
223                             matrix_clear();
224                             clear_keyboard();
225                             xprintf("unexpected scan code at INIT: %02X\n", code);
226                         }
227                         state = INIT;
228                 }
229                 break;
230             case E0:    // E0-Prefixed
231                 switch (code) {
232                     case 0x12:  // to be ignored
233                     case 0x59:  // to be ignored
234                         state = INIT;
235                         break;
236                     case 0x7E:  // Control'd Pause
237                         state = E0_7E;
238                         break;
239                     case 0xF0:
240                         state = E0_F0;
241                         break;
242                     default:
243                         if (code < 0x80) {
244                             matrix_make(code|0x80);
245                         } else {
246                             matrix_clear();
247                             clear_keyboard();
248                             xprintf("unexpected scan code at E0: %02X\n", code);
249                         }
250                         state = INIT;
251                 }
252                 break;
253             case F0:    // Break code
254                 switch (code) {
255                     case 0x83:  // F7
256                         matrix_break(F7);
257                         state = INIT;
258                         break;
259                     case 0x84:  // Alt'd PrintScreen
260                         matrix_break(PRINT_SCREEN);
261                         state = INIT;
262                         break;
263                     case 0xF0:
264                         matrix_clear();
265                         clear_keyboard();
266                         xprintf("unexpected scan code at F0: F0(clear and cont.)\n");
267                         break;
268                     default:
269                     if (code < 0x80) {
270                         matrix_break(code);
271                     } else {
272                         matrix_clear();
273                         clear_keyboard();
274                         xprintf("unexpected scan code at F0: %02X\n", code);
275                     }
276                     state = INIT;
277                 }
278                 break;
279             case E0_F0: // Break code of E0-prefixed
280                 switch (code) {
281                     case 0x12:  // to be ignored
282                     case 0x59:  // to be ignored
283                         state = INIT;
284                         break;
285                     default:
286                         if (code < 0x80) {
287                             matrix_break(code|0x80);
288                         } else {
289                             matrix_clear();
290                             clear_keyboard();
291                             xprintf("unexpected scan code at E0_F0: %02X\n", code);
292                         }
293                         state = INIT;
294                 }
295                 break;
296             // following are states of Pause
297             case E1:
298                 switch (code) {
299                     case 0x14:
300                         state = E1_14;
301                         break;
302                     default:
303                         state = INIT;
304                 }
305                 break;
306             case E1_14:
307                 switch (code) {
308                     case 0x77:
309                         state = E1_14_77;
310                         break;
311                     default:
312                         state = INIT;
313                 }
314                 break;
315             case E1_14_77:
316                 switch (code) {
317                     case 0xE1:
318                         state = E1_14_77_E1;
319                         break;
320                     default:
321                         state = INIT;
322                 }
323                 break;
324             case E1_14_77_E1:
325                 switch (code) {
326                     case 0xF0:
327                         state = E1_14_77_E1_F0;
328                         break;
329                     default:
330                         state = INIT;
331                 }
332                 break;
333             case E1_14_77_E1_F0:
334                 switch (code) {
335                     case 0x14:
336                         state = E1_14_77_E1_F0_14;
337                         break;
338                     default:
339                         state = INIT;
340                 }
341                 break;
342             case E1_14_77_E1_F0_14:
343                 switch (code) {
344                     case 0xF0:
345                         state = E1_14_77_E1_F0_14_F0;
346                         break;
347                     default:
348                         state = INIT;
349                 }
350                 break;
351             case E1_14_77_E1_F0_14_F0:
352                 switch (code) {
353                     case 0x77:
354                         matrix_make(PAUSE);
355                         state = INIT;
356                         break;
357                     default:
358                         state = INIT;
359                 }
360                 break;
361             // Following are states of Control'd Pause
362             case E0_7E:
363                 if (code == 0xE0)
364                     state = E0_7E_E0;
365                 else
366                     state = INIT;
367                 break;
368             case E0_7E_E0:
369                 if (code == 0xF0)
370                     state = E0_7E_E0_F0;
371                 else
372                     state = INIT;
373                 break;
374             case E0_7E_E0_F0:
375                 if (code == 0x7E)
376                     matrix_make(PAUSE);
377                 state = INIT;
378                 break;
379             default:
380                 state = INIT;
381         }
382     }
383
384     // TODO: request RESEND when error occurs?
385 /*
386     if (PS2_IS_FAILED(ps2_error)) {
387         uint8_t ret = ps2_host_send(PS2_RESEND);
388         xprintf("Resend: %02X\n", ret);
389     }
390 */
391     return 1;
392 }
393
394 bool matrix_is_modified(void)
395 {
396     return is_modified;
397 }
398
399 inline
400 bool matrix_has_ghost(void)
401 {
402 #ifdef MATRIX_HAS_GHOST
403     for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
404         if (matrix_has_ghost_in_row(i))
405             return true;
406     }
407 #endif
408     return false;
409 }
410
411 inline
412 bool matrix_is_on(uint8_t row, uint8_t col)
413 {
414     return (matrix[row] & (1<<col));
415 }
416
417 inline
418 uint8_t matrix_get_row(uint8_t row)
419 {
420     return matrix[row];
421 }
422
423 void matrix_print(void)
424 {
425     print("\nr/c 01234567\n");
426     for (uint8_t row = 0; row < matrix_rows(); row++) {
427         phex(row); print(": ");
428         pbin_reverse(matrix_get_row(row));
429 #ifdef MATRIX_HAS_GHOST
430         if (matrix_has_ghost_in_row(row)) {
431             print(" <ghost");
432         }
433 #endif
434         print("\n");
435     }
436 }
437
438 uint8_t matrix_key_count(void)
439 {
440     uint8_t count = 0;
441     for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
442         count += bitpop(matrix[i]);
443     }
444     return count;
445 }
446
447 #ifdef MATRIX_HAS_GHOST
448 inline
449 static bool matrix_has_ghost_in_row(uint8_t row)
450 {
451     // no ghost exists in case less than 2 keys on
452     if (((matrix[row] - 1) & matrix[row]) == 0)
453         return false;
454
455     // ghost exists in case same state as other row
456     for (uint8_t i=0; i < MATRIX_ROWS; i++) {
457         if (i != row && (matrix[i] & matrix[row]) == matrix[row])
458             return true;
459     }
460     return false;
461 }
462 #endif
463
464
465 inline
466 static void matrix_make(uint8_t code)
467 {
468     if (!matrix_is_on(ROW(code), COL(code))) {
469         matrix[ROW(code)] |= 1<<COL(code);
470         is_modified = true;
471     }
472 }
473
474 inline
475 static void matrix_break(uint8_t code)
476 {
477     if (matrix_is_on(ROW(code), COL(code))) {
478         matrix[ROW(code)] &= ~(1<<COL(code));
479         is_modified = true;
480     }
481 }
482
483 inline
484 static void matrix_clear(void)
485 {
486     for (uint8_t i=0; i < MATRIX_ROWS; i++) matrix[i] = 0x00;
487 }