#include "project.h" #define KBCLK (1UL << 0) #define KBCLK_PORT GPIOA #define KBCLK_IRQ NVIC_EXTI0_IRQ #define KBDAT (1UL << 1) #define KBDAT_PORT GPIOA /*Cope with lost sync */ /*If we've had no bits for this long, it's definately the start of a new word, 72MHz units */ #define RESYNC_GAP (7200000) /* 100ms */ #define ATKBD_TIMEOUT 1000 /*how long to wait for the keyboard to respond to a command in ms */ #define ATKBD_CLOCK_SEIZE_DELAY 200 /* how many us from pulling clock low to pulling data low, when we xmit */ #define ATKBD_BIT_DELAY 10 /* how many us we allow from the KBD to pull clock back up */ typedef enum { STATE_START = 0, STATE_BIT0, STATE_BIT1, STATE_BIT2, STATE_BIT3, STATE_BIT4, STATE_BIT5, STATE_BIT6, STATE_BIT7, STATE_PARITY, STATE_STOP, } atkbd_state; #define LED_CAPS 0x01 #define LED_NUMLOCK 0x02 #define LED_SCROLLOCK 0x04 #define ATKBD_CMD_SETLEDS 0xed #define ATKBD_CMD_GSCANSET 0xf0 #define ATKBD_CMD_SSCANSET 0xf0 #define ATKBD_CMD_GETID 0xf2 #define ATKBD_CMD_SETREP 0xf3 #define ATKBD_CMD_ENABLE 0xf4 #define ATKBD_CMD_RESET_DIS 0xf5 #define ATKBD_CMD_RESET_DEF 0xf6 #define ATKBD_CMD_SETALL_MB 0xf8 #define ATKBD_CMD_SETALL_MBR 0xfa #define ATKBD_CMD_RESET_BAT 0xff #define ATKBD_CMD_RESEND 0xfe #define ATKBD_CMD_EX_ENABLE 0xea #define ATKBD_CMD_EX_SETLEDS 0xeb #define ATKBD_CMD_OK_GETID 0xe8 #define ATKBD_CMD_ECHO 0xee #define ATKBD_RET_ECHO 0xee #define ATKBD_RET_ACK 0xfa #define ATKBD_RET_NAK 0xfe #define ATKBD_RET_BAT 0xaa #define ATKBD_RET_EMUL0 0xe0 #define ATKBD_RET_EMUL1 0xe1 #define ATKBD_RET_RELEASE 0xf0 #define ATKBD_RET_HANJA 0xf1 #define ATKBD_RET_HANGEUL 0xf2 #define ATKBD_RET_ERR 0xff #define ATKBD_KEY_UNKNOWN 0 #define ATKBD_KEY_NULL 255 static int atkbd_ack; static int atkbd_nack; static int atkbd_bat; static int atkbd_echo; static void atkbd_mask_irq (void) { nvic_disable_irq (KBCLK_IRQ); } static void atkbd_unmask_flush_irq (void) { exti_reset_request (KBCLK); nvic_enable_irq (KBCLK_IRQ); } static uint32_t cycle_diff (uint32_t a, uint32_t b) { return b - a; } static void atkbd_dispatch (int key, int updown) { /* the logic here is batshit, consult scancode.doc in the DOCS dir */ static int fake_ctrl, pause_down; switch (key) { case AT_SC_LEFTCTRL | AT_BS_EMUL1: fake_ctrl = updown; break; } if (key == 0x84) key = AT_SC_SYSRQ; if (key == 0x77) { /*Grr broken MS state machine in docs */ if ((!updown) && (pause_down)) { key = AT_SC_PAUSE; pause_down = 0; } if (fake_ctrl && updown) { key = AT_SC_PAUSE; pause_down = 1; } } /* Filter fakes */ switch (key) { case AT_SC_LEFTCTRL | AT_BS_EMUL1: case AT_SC_LEFTSHIFT | AT_BS_EMUL0: case AT_SC_RIGHTSHIFT | AT_BS_EMUL0: break; default: scancode_dispatch (key, updown); } } static void atkbd_data_dispatch (uint8_t byte) { static int release; static int emul; // printf ("ATKBD < 0x%02x\r\n", byte); switch (byte) { case ATKBD_RET_ACK: atkbd_ack++; break; case ATKBD_RET_NAK: atkbd_nack++; break; case ATKBD_RET_BAT: atkbd_bat++; break; case ATKBD_RET_ECHO: atkbd_echo++; break; case ATKBD_RET_ERR: case ATKBD_KEY_UNKNOWN: /*All these need no action */ break; case AT_SC_EMUL0: emul = AT_BS_EMUL0; break; case AT_SC_EMUL1: emul = AT_BS_EMUL1; break; case ATKBD_RET_RELEASE: release = 1; break; default: atkbd_dispatch (emul | byte, !release); emul = 0; release = 0; } } void exti0_isr (void) { static uint32_t last_interrupt = 0; static atkbd_state state = STATE_START; static uint8_t atkbd_byte; static int parity; uint32_t now, diff; int d; d = ! !GET (KBDAT); exti_reset_request (KBCLK); now = dwt_read_cycle_counter (); diff = cycle_diff (last_interrupt, now); last_interrupt = now; if (diff > RESYNC_GAP) state = STATE_START; switch (state) { case STATE_START: atkbd_byte = 0; parity = 0; if (!d) state++; break; case STATE_BIT0: case STATE_BIT1: case STATE_BIT2: case STATE_BIT3: case STATE_BIT4: case STATE_BIT5: case STATE_BIT6: case STATE_BIT7: atkbd_byte |= d << (state - STATE_BIT0); /* fall through */ case STATE_PARITY: parity ^= d; state++; break; case STATE_STOP: if (d && parity) atkbd_data_dispatch (atkbd_byte); state = STATE_START; break; } } void atkbd_set (int clk, int dat) { if (clk) { MAP_INPUT_PU (KBCLK); } else { CLEAR (KBCLK); MAP_OUTPUT_PP (KBCLK); } if (dat) { MAP_INPUT_PU (KBDAT); } else { CLEAR (KBDAT); MAP_OUTPUT_PP (KBDAT); } } int atkbd_send (uint8_t d) { uint32_t then = ticks; int parity = 1; uint32_t c; atkbd_mask_irq (); atkbd_set (0, 1); delay_us (ATKBD_CLOCK_SEIZE_DELAY); atkbd_set (0, 0); delay_us (ATKBD_BIT_DELAY); atkbd_set (1, 0); delay_us (ATKBD_BIT_DELAY); /* 8 data bits */ for (c = 1; c < 0x100; c <<= 1) { while (GET (KBCLK)) if (timed_out (then, ATKBD_TIMEOUT)) goto err; atkbd_set (1, c & d); parity ^= ! !(c & d); while (!GET (KBCLK)) if (timed_out (then, ATKBD_TIMEOUT)) goto err; } /* A parity bit */ while (GET (KBCLK)) if (timed_out (then, ATKBD_TIMEOUT)) goto err; atkbd_set (1, parity); while (!GET (KBCLK)) if (timed_out (then, ATKBD_TIMEOUT)) goto err; /* two stop bits */ while (GET (KBCLK)) if (timed_out (then, ATKBD_TIMEOUT)) goto err; atkbd_set (1, 1); while (!GET (KBCLK)) if (timed_out (then, ATKBD_TIMEOUT)) goto err; while (GET (KBCLK)) if (timed_out (then, ATKBD_TIMEOUT)) goto err; atkbd_set (1, 1); while (!GET (KBCLK)) if (timed_out (then, ATKBD_TIMEOUT)) goto err; while (!GET (KBDAT)) if (timed_out (then, ATKBD_TIMEOUT)) goto err; while (!GET (KBCLK)) if (timed_out (then, ATKBD_TIMEOUT)) goto err; atkbd_unmask_flush_irq (); // printf ("ATKBD > 0x%02x\r\n", d); return 0; err: atkbd_set (1, 1); while (!GET (KBDAT)) if (timed_out (then, ATKBD_TIMEOUT)) goto err; while (!GET (KBCLK)) if (timed_out (then, ATKBD_TIMEOUT)) goto err; atkbd_unmask_flush_irq (); // printf ("ATKBD >! 0x%02x\r\n", d); return -1; } static int atkbd_reset (void) { uint32_t then = ticks; atkbd_bat = 0; atkbd_send (ATKBD_CMD_RESET_BAT); while (!atkbd_bat) if (timed_out (then, ATKBD_TIMEOUT)) return -1; then = ticks; atkbd_ack = 0; atkbd_send (ATKBD_CMD_SETALL_MBR); while (!atkbd_ack) if (timed_out (then, ATKBD_TIMEOUT)) return -1; return 0; } int atkbd_request_echo (void) { uint32_t then = ticks; atkbd_ack = 0; atkbd_send (ATKBD_CMD_ECHO); while (!atkbd_echo) if (timed_out (then, ATKBD_TIMEOUT)) return -1; return 0; } int atkbd_set_leds (uint8_t leds) { uint32_t then = ticks; atkbd_ack = 0; atkbd_send (ATKBD_CMD_SETLEDS); while (!atkbd_ack) if (timed_out (then, ATKBD_TIMEOUT)) return -1; then = ticks; atkbd_ack = 0; atkbd_send (leds); while (!atkbd_ack) if (timed_out (then, ATKBD_TIMEOUT)) return -1; return 0; } int atkbd_set_scanset (uint8_t scanset) { uint32_t then = ticks; atkbd_ack = 0; atkbd_send (ATKBD_CMD_SSCANSET); while (!atkbd_ack) if (timed_out (then, ATKBD_TIMEOUT)) return -1; then = ticks; atkbd_ack = 0; atkbd_send (scanset); while (!atkbd_ack) if (timed_out (then, ATKBD_TIMEOUT)) return -1; return 0; } int atkbd_set_mb (void) { uint32_t then = ticks; atkbd_ack = 0; atkbd_send (ATKBD_CMD_SETALL_MB); while (!atkbd_ack) if (timed_out (then, ATKBD_TIMEOUT)) return -1; return 0; } int atkbd_set_mbr (void) { uint32_t then = ticks; atkbd_ack = 0; atkbd_send (ATKBD_CMD_SETALL_MBR); while (!atkbd_ack) if (timed_out (then, ATKBD_TIMEOUT)) return -1; return 0; } void atkbd_init (void) { atkbd_set (1, 1); delay_ms (200); nvic_enable_irq (NVIC_EXTI0_IRQ); exti_select_source (KBCLK, KBCLK_PORT); exti_set_trigger (KBCLK, EXTI_TRIGGER_FALLING); exti_enable_request (KBCLK); exti_reset_request (KBCLK); nvic_enable_irq (KBCLK_IRQ); atkbd_reset (); atkbd_request_echo (); atkbd_set_mbr (); atkbd_set_scanset (2); atkbd_set_leds (LED_CAPS); }