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