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