2 * Copyright © 2016 Keith Packard <keithp@keithp.com>
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.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
19 static struct ao_fifo ao_ps2_rx_fifo;
21 static uint16_t ao_ps2_tx;
22 static uint8_t ao_ps2_tx_count;
24 static AO_TICK_TYPE ao_ps2_tick;
25 static uint16_t ao_ps2_value;
26 static uint8_t ao_ps2_count;
30 uint8_t ao_ps2_scancode_set;
32 #define AO_PS2_CLOCK_MODE(pull) ((pull) | AO_EXTI_MODE_FALLING | AO_EXTI_PRIORITY_MED)
38 _ao_ps2_parity(uint8_t value)
43 for (b = 0; b < 8; b++) {
44 parity ^= (value & 1);
54 if (ao_fifo_empty(ao_ps2_rx_fifo)) {
57 ao_fifo_remove(ao_ps2_rx_fifo, u);
66 ao_arch_block_interrupts();
67 while ((c = _ao_ps2_poll()) == AO_READ_AGAIN)
68 ao_sleep(&ao_ps2_rx_fifo);
69 ao_arch_release_interrupts();
78 ao_arch_block_interrupts();
80 ao_arch_release_interrupts();
87 ao_arch_block_interrupts();
88 ao_ps2_tx = ((uint16_t) c) | (_ao_ps2_parity(c) << 8) | (3 << 9);
90 ao_exti_disable(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT);
91 ao_arch_release_interrupts();
93 /* pull the clock pin down */
94 ao_enable_output(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT, AO_PS2_CLOCK_PIN, 0);
97 /* pull the data pin down for the start bit */
98 ao_enable_output(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT, AO_PS2_DATA_PIN, 0);
101 /* switch back to input mode for the interrupt to work */
102 ao_exti_setup(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT,
103 AO_PS2_CLOCK_MODE(AO_EXTI_MODE_PULL_UP),
105 ao_exti_enable(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT);
107 /* wait for the bits to drain */
108 while (ao_ps2_tx_count)
109 ao_sleep(&ao_ps2_tx_count);
113 static uint8_t ao_ps2_down[128 / 8];
116 ao_ps2_set_down(uint8_t code, uint8_t value)
118 uint8_t shift = (code & 0x07);
119 uint8_t byte = code >> 3;
121 ao_ps2_down[byte] = (ao_ps2_down[byte] & ~(1 << shift)) | (value << shift);
125 ao_ps2_is_down(uint8_t code)
127 uint8_t shift = (code & 0x07);
128 uint8_t byte = code >> 3;
130 return (ao_ps2_down[byte] >> shift) & 1;
134 _ao_ps2_set_leds(void)
137 if (ao_ps2_is_down(AO_PS2_CAPS_LOCK))
138 led |= AO_PS2_SET_LEDS_CAPS;
139 if (ao_ps2_is_down(AO_PS2_NUM_LOCK))
140 led |= AO_PS2_SET_LEDS_NUM;
141 if (ao_ps2_is_down(AO_PS2_SCROLL_LOCK))
142 led |= AO_PS2_SET_LEDS_SCROLL;
143 ao_arch_release_interrupts();
144 ao_ps2_put(AO_PS2_SET_LEDS);
145 while (ao_ps2_get() != 0xfa);
147 ao_arch_block_interrupts();
151 ao_ps2_is_lock(uint8_t code) {
153 case AO_PS2_CAPS_LOCK:
154 case AO_PS2_NUM_LOCK:
155 case AO_PS2_SCROLL_LOCK:
162 _ao_ps2_set_scancode_set(uint8_t set)
164 ao_ps2_scancode_set = set;
165 ao_arch_release_interrupts();
166 ao_ps2_put(AO_PS2_SET_SCAN_CODE_SET);
167 while (ao_ps2_get() != 0xfa);
169 ao_ps2_put(AO_PS2_SET_KEY_TYPEMATIC_MAKE_BREAK);
170 while (ao_ps2_get() != 0xfa);
171 ao_arch_block_interrupts();
175 _ao_ps2_poll_key(void)
179 static uint8_t saw_break;
183 if (ao_ps2_scancode_set != 3) {
184 _ao_ps2_set_scancode_set(3);
189 if (c == AO_PS2_BREAK) {
191 return AO_READ_AGAIN;
194 return AO_READ_AGAIN;
196 if (ao_ps2_is_lock(c)) {
199 return AO_READ_AGAIN;
201 if (ao_ps2_is_down(c))
207 ao_ps2_set_down(c, 0);
210 ao_ps2_set_down(c, 1);
214 if (ao_ps2_scancode_set != 3)
215 _ao_ps2_set_scancode_set(3);
221 ao_ps2_poll_key(void)
224 ao_arch_block_interrupts();
225 c = _ao_ps2_poll_key();
226 ao_arch_release_interrupts();
234 ao_arch_block_interrupts();
235 while ((c = _ao_ps2_poll_key()) == AO_READ_AGAIN)
236 ao_sleep(&ao_ps2_rx_fifo);
237 ao_arch_release_interrupts();
241 static const uint8_t ao_ps2_asciimap[128][2] = {
242 [AO_PS2_A] = { 'a', 'A' },
243 [AO_PS2_B] = { 'b', 'B' },
244 [AO_PS2_C] = { 'c', 'C' },
245 [AO_PS2_D] = { 'd', 'D' },
246 [AO_PS2_E] = { 'e', 'E' },
247 [AO_PS2_F] = { 'f', 'F' },
248 [AO_PS2_G] = { 'g', 'G' },
249 [AO_PS2_H] = { 'h', 'H' },
250 [AO_PS2_I] = { 'i', 'I' },
251 [AO_PS2_J] = { 'j', 'J' },
252 [AO_PS2_K] = { 'k', 'K' },
253 [AO_PS2_L] = { 'l', 'L' },
254 [AO_PS2_M] = { 'm', 'M' },
255 [AO_PS2_N] = { 'n', 'N' },
256 [AO_PS2_O] = { 'o', 'O' },
257 [AO_PS2_P] = { 'p', 'P' },
258 [AO_PS2_Q] = { 'q', 'Q' },
259 [AO_PS2_R] = { 'r', 'R' },
260 [AO_PS2_S] = { 's', 'S' },
261 [AO_PS2_T] = { 't', 'T' },
262 [AO_PS2_U] = { 'u', 'U' },
263 [AO_PS2_V] = { 'v', 'V' },
264 [AO_PS2_W] = { 'w', 'W' },
265 [AO_PS2_X] = { 'x', 'X' },
266 [AO_PS2_Y] = { 'y', 'Y' },
267 [AO_PS2_Z] = { 'z', 'Z' },
269 [AO_PS2_0] = { '0', ')' },
270 [AO_PS2_1] = { '1', '!' },
271 [AO_PS2_2] = { '2', '@' },
272 [AO_PS2_3] = { '3', '#' },
273 [AO_PS2_4] = { '4', '$' },
274 [AO_PS2_5] = { '5', '%' },
275 [AO_PS2_6] = { '6', '^' },
276 [AO_PS2_7] = { '7', '&' },
277 [AO_PS2_8] = { '8', '*' },
278 [AO_PS2_9] = { '9', '(' },
280 [AO_PS2_GRAVE] = { '`', '~' },
281 [AO_PS2_HYPHEN] = { '-', '_' },
282 [AO_PS2_EQUAL] = { '=', '+' },
283 [AO_PS2_BACKSLASH] = { '\\', '|' },
284 [AO_PS2_BACKSPACE] = { '\010', '\010' },
285 [AO_PS2_SPACE] = { ' ', ' ' },
286 [AO_PS2_TAB] = { '\t', '\t' },
288 [AO_PS2_ENTER] = { '\r', '\r' },
289 [AO_PS2_ESC] = { '\033', '\033' },
291 [AO_PS2_OPEN_SQ] = { '[', '{' },
292 [AO_PS2_DELETE] = { '\177', '\177' },
294 [AO_PS2_KP_TIMES] = { '*', '*' },
295 [AO_PS2_KP_PLUS] = { '+', '+' },
296 [AO_PS2_KP_ENTER] = { '\r', '\r' },
297 [AO_PS2_KP_DECIMAL] = { '.', '.' },
298 [AO_PS2_KP_0] = { '0', '0' },
299 [AO_PS2_KP_1] = { '1', '1' },
300 [AO_PS2_KP_2] = { '2', '2' },
301 [AO_PS2_KP_3] = { '3', '3' },
302 [AO_PS2_KP_4] = { '4', '4' },
303 [AO_PS2_KP_5] = { '5', '5' },
304 [AO_PS2_KP_6] = { '6', '6' },
305 [AO_PS2_KP_7] = { '7', '7' },
306 [AO_PS2_KP_8] = { '8', '8' },
307 [AO_PS2_KP_9] = { '9', '9' },
308 [AO_PS2_CLOSE_SQ] = { ']', '}' },
309 [AO_PS2_SEMICOLON] = { ';', ':' },
310 [AO_PS2_ACUTE] = { '\'', '"' },
311 [AO_PS2_COMMA] = { ',', '<' },
312 [AO_PS2_PERIOD] = { '.', '>' },
313 [AO_PS2_SLASH] = { '/', '?' },
317 ao_ps2_ascii(uint8_t key)
322 /* Skip key releases */
324 return AO_READ_AGAIN;
327 if (ao_ps2_is_down(AO_PS2_L_SHIFT) || ao_ps2_is_down(AO_PS2_R_SHIFT))
331 a = ao_ps2_asciimap[key][0];
333 return AO_READ_AGAIN;
335 if ('a' <= a && a <= 'z')
336 if (ao_ps2_is_down(AO_PS2_CAPS_LOCK))
338 a = ao_ps2_asciimap[key][col];
339 if ('@' <= a && a <= 0x7f && (ao_ps2_is_down(AO_PS2_L_CTRL) || ao_ps2_is_down(AO_PS2_R_CTRL)))
345 _ao_ps2_pollchar(void)
349 key = _ao_ps2_poll_key();
352 return ao_ps2_ascii(key);
359 ao_arch_block_interrupts();
360 while ((c = _ao_ps2_pollchar()) == AO_READ_AGAIN)
361 ao_sleep(&ao_ps2_rx_fifo);
362 ao_arch_release_interrupts();
371 if (ao_ps2_tx_count) {
372 ao_gpio_set(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT, AO_PS2_DATA_PIN, ao_ps2_tx&1);
375 if (!ao_ps2_tx_count) {
376 ao_enable_input(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT, AO_EXTI_MODE_PULL_UP);
377 ao_wakeup(&ao_ps2_tx_count);
381 /* reset if its been a while */
382 if ((ao_tick_count - ao_ps2_tick) > AO_MS_TO_TICKS(100))
384 ao_ps2_tick = ao_tick_count;
386 bit = ao_gpio_get(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT, AO_PS2_DATA_PIN);
387 if (ao_ps2_count == 0) {
388 /* check for start bit, ignore if not zero */
392 } else if (ao_ps2_count < 9) {
393 ao_ps2_value |= (bit << (ao_ps2_count - 1));
394 } else if (ao_ps2_count == 10) {
395 ao_fifo_insert(ao_ps2_rx_fifo, ao_ps2_value);
396 ao_wakeup(&ao_ps2_rx_fifo);
398 ao_wakeup(&ao_stdin_ready);
408 ao_enable_input(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT,
409 AO_EXTI_MODE_PULL_UP);
411 ao_enable_port(AO_PS2_CLOCK_PORT);
413 ao_exti_setup(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT,
414 AO_PS2_CLOCK_MODE(AO_EXTI_MODE_PULL_UP),
416 ao_exti_enable(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT);
418 ao_ps2_scancode_set = 2;