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