Made directories for keyboard and converter projects.
[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 <avr/io.h>
21 #include <util/delay.h>
22 #include "print.h"
23 #include "util.h"
24 #include "debug.h"
25 #include "ps2.h"
26 #include "matrix.h"
27
28
29 static void matrix_make(uint8_t code);
30 static void matrix_break(uint8_t code);
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 ps2_host_init();
87
88 // initialize matrix state: all keys off
89 for (uint8_t i=0; i < MATRIX_ROWS; i++) matrix[i] = 0x00;
90
91 return;
92 }
93
94 /*
95 * PS/2 Scan Code Set 2: Exceptional Handling
96 *
97 * There are several keys to be handled exceptionally.
98 * The scan code for these keys are varied or prefix/postfix'd
99 * depending on modifier key state.
100 *
101 * Keyboard Scan Code Specification:
102 * http://www.microsoft.com/whdc/archive/scancode.mspx
103 * http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/scancode.doc
104 *
105 *
106 * 1) Insert, Delete, Home, End, PageUp, PageDown, Up, Down, Right, Left
107 * a) when Num Lock is off
108 * modifiers | make | break
109 * ----------+---------------------------+----------------------
110 * Ohter | <make> | <break>
111 * LShift | E0 F0 12 <make> | <break> E0 12
112 * RShift | E0 F0 59 <make> | <break> E0 59
113 * L+RShift | E0 F0 12 E0 F0 59 <make> | <break> E0 59 E0 12
114 *
115 * b) when Num Lock is on
116 * modifiers | make | break
117 * ----------+---------------------------+----------------------
118 * Other | E0 12 <make> | <break> E0 F0 12
119 * Shift'd | <make> | <break>
120 *
121 * Handling: These prefix/postfix codes are ignored.
122 *
123 *
124 * 2) Keypad /
125 * modifiers | make | break
126 * ----------+---------------------------+----------------------
127 * Ohter | <make> | <break>
128 * LShift | E0 F0 12 <make> | <break> E0 12
129 * RShift | E0 F0 59 <make> | <break> E0 59
130 * L+RShift | E0 F0 12 E0 F0 59 <make> | <break> E0 59 E0 12
131 *
132 * Handling: These prefix/postfix codes are ignored.
133 *
134 *
135 * 3) PrintScreen
136 * modifiers | make | break
137 * ----------+--------------+-----------------------------------
138 * Other | E0 12 E0 7C | E0 F0 7C E0 F0 12
139 * Shift'd | E0 7C | E0 F0 7C
140 * Control'd | E0 7C | E0 F0 7C
141 * Alt'd | 84 | F0 84
142 *
143 * Handling: These prefix/postfix codes are ignored, and both scan codes
144 * 'E0 7C' and 84 are seen as PrintScreen.
145 *
146 * 4) Pause
147 * modifiers | make(no break code)
148 * ----------+--------------------------------------------------
149 * Other | E1 14 77 E1 F0 14 F0 77
150 * Control'd | E0 7E E0 F0 7E
151 *
152 * Handling: Both code sequences are treated as a whole.
153 * And we need a ad hoc 'pseudo break code' hack to get the key off
154 * because it has no break code.
155 *
156 */
157 uint8_t matrix_scan(void)
158 {
159
160 // scan code reading states
161 static enum {
162 INIT,
163 F0,
164 E0,
165 E0_F0,
166 // Pause
167 E1,
168 E1_14,
169 E1_14_77,
170 E1_14_77_E1,
171 E1_14_77_E1_F0,
172 E1_14_77_E1_F0_14,
173 E1_14_77_E1_F0_14_F0,
174 // Control'd Pause
175 E0_7E,
176 E0_7E_E0,
177 E0_7E_E0_F0,
178 } state = INIT;
179
180
181 is_modified = false;
182
183 // 'pseudo break code' hack
184 if (matrix_is_on(ROW(PAUSE), COL(PAUSE))) {
185 matrix_break(PAUSE);
186 }
187
188 uint8_t code;
189 while ((code = ps2_host_recv())) {
190 switch (state) {
191 case INIT:
192 switch (code) {
193 case 0xE0:
194 state = E0;
195 break;
196 case 0xF0:
197 state = F0;
198 break;
199 case 0xE1:
200 state = E1;
201 break;
202 case 0x83: // F7
203 matrix_make(F7);
204 state = INIT;
205 break;
206 case 0x84: // Alt'd PrintScreen
207 matrix_make(PRINT_SCREEN);
208 state = INIT;
209 break;
210 default: // normal key make
211 if (code < 0x80) {
212 matrix_make(code);
213 } else {
214 debug("unexpected scan code at INIT: "); debug_hex(code); debug("\n");
215 }
216 state = INIT;
217 }
218 break;
219 case E0: // E0-Prefixed
220 switch (code) {
221 case 0x12: // to be ignored
222 case 0x59: // to be ignored
223 state = INIT;
224 break;
225 case 0x7E: // Control'd Pause
226 state = E0_7E;
227 break;
228 case 0xF0:
229 state = E0_F0;
230 break;
231 default:
232 if (code < 0x80) {
233 matrix_make(code|0x80);
234 } else {
235 debug("unexpected scan code at E0: "); debug_hex(code); debug("\n");
236 }
237 state = INIT;
238 }
239 break;
240 case F0: // Break code
241 switch (code) {
242 case 0x83: // F7
243 matrix_break(F7);
244 state = INIT;
245 break;
246 case 0x84: // Alt'd PrintScreen
247 matrix_break(PRINT_SCREEN);
248 state = INIT;
249 break;
250 default:
251 if (code < 0x80) {
252 matrix_break(code);
253 } else {
254 debug("unexpected scan code at F0: "); debug_hex(code); debug("\n");
255 }
256 state = INIT;
257 }
258 break;
259 case E0_F0: // Break code of E0-prefixed
260 switch (code) {
261 case 0x12: // to be ignored
262 case 0x59: // to be ignored
263 state = INIT;
264 break;
265 default:
266 if (code < 0x80) {
267 matrix_break(code|0x80);
268 } else {
269 debug("unexpected scan code at E0_F0: "); debug_hex(code); debug("\n");
270 }
271 state = INIT;
272 }
273 break;
274 // following are states of Pause
275 case E1:
276 switch (code) {
277 case 0x14:
278 state = E1_14;
279 break;
280 default:
281 state = INIT;
282 }
283 break;
284 case E1_14:
285 switch (code) {
286 case 0x77:
287 state = E1_14_77;
288 break;
289 default:
290 state = INIT;
291 }
292 break;
293 case E1_14_77:
294 switch (code) {
295 case 0xE1:
296 state = E1_14_77_E1;
297 break;
298 default:
299 state = INIT;
300 }
301 break;
302 case E1_14_77_E1:
303 switch (code) {
304 case 0xF0:
305 state = E1_14_77_E1_F0;
306 break;
307 default:
308 state = INIT;
309 }
310 break;
311 case E1_14_77_E1_F0:
312 switch (code) {
313 case 0x14:
314 state = E1_14_77_E1_F0_14;
315 break;
316 default:
317 state = INIT;
318 }
319 break;
320 case E1_14_77_E1_F0_14:
321 switch (code) {
322 case 0xF0:
323 state = E1_14_77_E1_F0_14_F0;
324 break;
325 default:
326 state = INIT;
327 }
328 break;
329 case E1_14_77_E1_F0_14_F0:
330 switch (code) {
331 case 0x77:
332 matrix_make(PAUSE);
333 state = INIT;
334 break;
335 default:
336 state = INIT;
337 }
338 break;
339 // Following are states of Control'd Pause
340 case E0_7E:
341 if (code == 0xE0)
342 state = E0_7E_E0;
343 else
344 state = INIT;
345 break;
346 case E0_7E_E0:
347 if (code == 0xF0)
348 state = E0_7E_E0_F0;
349 else
350 state = INIT;
351 break;
352 case E0_7E_E0_F0:
353 if (code == 0x7E)
354 matrix_make(PAUSE);
355 state = INIT;
356 break;
357 default:
358 state = INIT;
359 }
360 phex(code);
361 }
362 return 1;
363 }
364
365 bool matrix_is_modified(void)
366 {
367 return is_modified;
368 }
369
370 inline
371 bool matrix_has_ghost(void)
372 {
373 #ifdef MATRIX_HAS_GHOST
374 for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
375 if (matrix_has_ghost_in_row(i))
376 return true;
377 }
378 #endif
379 return false;
380 }
381
382 inline
383 bool matrix_is_on(uint8_t row, uint8_t col)
384 {
385 return (matrix[row] & (1<<col));
386 }
387
388 inline
389 uint8_t matrix_get_row(uint8_t row)
390 {
391 return matrix[row];
392 }
393
394 void matrix_print(void)
395 {
396 print("\nr/c 01234567\n");
397 for (uint8_t row = 0; row < matrix_rows(); row++) {
398 phex(row); print(": ");
399 pbin_reverse(matrix_get_row(row));
400 #ifdef MATRIX_HAS_GHOST
401 if (matrix_has_ghost_in_row(row)) {
402 print(" <ghost");
403 }
404 #endif
405 print("\n");
406 }
407 }
408
409 uint8_t matrix_key_count(void)
410 {
411 uint8_t count = 0;
412 for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
413 count += bitpop(matrix[i]);
414 }
415 return count;
416 }
417
418 #ifdef MATRIX_HAS_GHOST
419 inline
420 static bool matrix_has_ghost_in_row(uint8_t row)
421 {
422 // no ghost exists in case less than 2 keys on
423 if (((matrix[row] - 1) & matrix[row]) == 0)
424 return false;
425
426 // ghost exists in case same state as other row
427 for (uint8_t i=0; i < MATRIX_ROWS; i++) {
428 if (i != row && (matrix[i] & matrix[row]) == matrix[row])
429 return true;
430 }
431 return false;
432 }
433 #endif
434
435
436 inline
437 static void matrix_make(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 }
443 }
444
445 inline
446 static void matrix_break(uint8_t code)
447 {
448 if (matrix_is_on(ROW(code), COL(code))) {
449 matrix[ROW(code)] &= ~(1<<COL(code));
450 is_modified = true;
451 }
452 }
Imprint / Impressum