]> git.gir.st - tmk_keyboard.git/blob - ps2_usb/matrix.c
added copyright and license notice.
[tmk_keyboard.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 }
Imprint / Impressum