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