diff options
-rw-r--r-- | kbc.c | 550 |
1 files changed, 407 insertions, 143 deletions
@@ -1,246 +1,510 @@ +#define F_CPU 8*1000000UL #include <stdint.h> #include <string.h> #include <avr/io.h> #include <avr/interrupt.h> #include <avr/sleep.h> +#include <util/delay.h> #ifndef PIN #error need to define a PIN #endif -#define B_RED (1 << 0) -#define B_YLW (1 << 1) +/* port B RJ45 + * 0 + * 1 BEEP (OC1A) + * 2 SS up:8 + * 3 MOSI up:2 + * 4 MISO up:4 + * 5 SCK up:7 + * port C + * 0-3 + * 4 (SDA) + * 5 (SCL) + * port D + * 0 PS2_DATA (RXD) kb:3 + * 1 PS2_DATA_O (TXD) + * 2 + * 3 PS2_CLK_O (INT1) + * 4 PS2_CLK (XCK) kb:2 + * 5 PS2_PWREN + * 6-7 + */ + +#define D_DATA (1 << 0) +#define D_CLK (1 << 4) +#define D_PWR (1 << 5) + +#define B_BEEP (1 << 1) +#define B_OPEN 0 +#define B_CLOSE 0 +#define B_MISO (1 << 4) + +enum state { + STATE_NONE = 0, + STATE_FAILURE, + STATE_POWERUP, + STATE_INITRESET, + STATE_CONFIG, + STATE_IDLE, + STATE_IDLEBLINK, +}; +static volatile enum state state, nextstate; + +static void power_up() +{ + DDRD = D_PWR; + PORTD = D_DATA | D_CLK; +} + +static void power_down() +{ + DDRD = D_PWR; + PORTD = D_PWR; +} static void usart_rx() { + UCSR0A = 0; UCSR0B = (1 << RXCIE0) /* recv int en */ | (1 << RXEN0); /* do RX */ } static void usart_rxpoll() { + UCSR0A = 0; UCSR0B = (1 << RXEN0); /* do RX*/ } static void usart_dis() { UCSR0B = 0; + (void)UDR0; } -#define CLK (1 << 3) -#define DATA (1 << 1) +static uint8_t dbgtx[64]; +static uint8_t dbgpos; -static uint8_t send_byte(uint8_t byte) +static void dbg_wr(uint8_t what) { - uint8_t cntr, bit, parity = 1, rv; + cli(); + if (dbgpos < sizeof(dbgtx)) + dbgtx[dbgpos++] = what; + SPCR = (1 << SPIE) | (1 << SPE); + sei(); +} - usart_dis(); - rv = UDR0; +ISR(SIG_SPI) +{ + if (dbgpos) { + SPDR = dbgtx[0]; + for (uint8_t c = 0; c < sizeof(dbgtx); c++) + dbgtx[c] = dbgtx[c + 1]; + dbgpos--; + } else { + SPDR = 0xff; + SPCR = (1 << SPE); + } +} + +static void dbg_init(void) +{ + DDRB |= B_MISO; + /* MSB first, cpol = rise,fall, cpha = sample(r),setup(f) */ + SPCR = (1 << SPIE) | (1 << SPE); +} - /* make clock low, request clock from keyboard */ - PORTD = 0; - DDRD = CLK; -#define delay(x) for (cntr = 0; cntr < x; cntr++) __asm__ volatile ("nop\n") - delay(200); - DDRD = CLK | DATA; +static volatile uint8_t statecntr = 0; + +#define mayabort if (TIFR0 & (1 << TOV0)) return 0; +#define wait_CLKlo while (PIND & D_CLK) { __asm__ volatile ("nop\n"); mayabort; } +#define wait_CLKhi while (!(PIND & D_CLK)) { __asm__ volatile ("nop\n"); mayabort; } +#define wait_CLK wait_CLKhi; wait_CLKlo; _delay_us(5) + +static void timer_return(void) +{ + TCNT0 = 0; + TIFR0 |= (1 << TOV0); + __asm__ volatile("nop\n"); + TIMSK0 = (1 << TOIE0); +} + +static uint8_t do_send_byte(uint8_t byte) +{ + uint8_t bit = 0, parity = 1, rv, usarts; + + usart_dis(); + + /* make clock low, request clock from keyboard */ + PORTD &= ~D_CLK; + DDRD |= D_CLK; - delay(10); + _delay_us(250); - DDRD = DATA; - PORTD = CLK; + PORTD &= ~D_DATA; + DDRD |= D_DATA; -#define wait_CLKlo while (PIND & CLK) __asm__ volatile ("nop\n") -#define wait_CLKhi while (!(PIND & CLK)) __asm__ volatile ("nop\n") -#define wait_CLK wait_CLKhi; wait_CLKlo; delay(3) + _delay_us(25); + DDRD &= ~D_CLK; + PORTD |= D_CLK; + + /* data bits */ for (bit = 0; bit < 8; bit++) { wait_CLK; if (byte & 1) { - PORTD = CLK | DATA; - parity++; + PORTD |= D_DATA; + parity ^= 1; } else { - PORTD = CLK; + PORTD &= ~D_DATA; } byte >>= 1; } + /* parity */ wait_CLK; - PORTD = (parity & 1) ? (CLK | DATA) : CLK; + if (parity) + PORTD |= D_DATA; + else + PORTD &= ~D_DATA; + + /* stop bit */ wait_CLK; + PORTD |= D_DATA; - PORTD = CLK | DATA; + /* ACK from keyboard */ wait_CLKhi; - - DDRD = 0; + DDRD &= ~D_DATA; wait_CLKlo; wait_CLKhi; + while (!(PIND & D_DATA)) { __asm__ volatile ("nop\n"); mayabort; } + + /* response from keyboard */ usart_rxpoll(); - while (!(UCSR0A & (1 << RXC0))) - __asm__ volatile("nop\n"); + usarts = 0; + while (!(usarts & ((1 << RXC0) | (1 << UPE0) | (1 << FE0)))) { + __asm__ volatile("nop\nnop\n"); + usarts = UCSR0A; + mayabort; + } rv = UDR0; - usart_rx(); + return rv; +} + +static uint8_t send_byte(uint8_t byte) +{ + uint8_t rv, attempts; + + /* stop timer, use for ourselves to enforce 8 ms timeout */ + TIMSK0 = 0; + + for (attempts = 5; attempts; attempts--) { + TCNT0 = 0; + TIFR0 |= (1 << TOV0); + + if ((rv = do_send_byte(byte)) == 0xfa) { + dbg_wr(0x14); + dbg_wr(0x80 | (byte & 0x0f)); + dbg_wr(0x80 | (byte >> 4)); + dbg_wr(0x80 | attempts); + goto out; + } + /* clear state, wait, retry */ + DDRD &= ~(D_DATA | D_CLK); + PORTD |= D_DATA | D_CLK; + _delay_us(50); + } + + dbg_wr(0x01); + dbg_wr(0x80 | (byte & 0x0f)); + dbg_wr(0x80 | (byte >> 4)); + +out: + /* hand back timer to state machine */ + timer_return(); + usart_rx(); return rv; } -uint8_t ext, brk, pressed, state; -const char passwd[4] = PIN; -char code[sizeof(passwd)]; +static uint8_t wait_byte(void) +{ + uint8_t data, usarts; -const char kbc_60[32] = { - '_', '_', '_', '_', '_', '_', '_', '_', /* 60 - 67 */ - '_', '1', '_', '4', '7', '_', '_', '_', /* 68 - 6f */ - '0', '.', '2', '5', '6', '8', '_', '_', /* 70 - 77 */ - '_', '+', '3', '-', '*', '9', '_', '_' /* 78 - 7f */ -}; + usart_rxpoll(); + /* stop timer, use for ourselves to enforce 8 ms timeout */ + TIMSK0 = 0; + TCNT0 = 0; + TIFR0 |= (1 << TOV0); + + usarts = 0; + while (!(usarts & ((1 << RXC0) | (1 << UPE0) | (1 << FE0)))) { + if (TIFR0 & (1 << TOV0)) { + (void)UDR0; + timer_return(); + usart_rx(); + + dbg_wr(0x02); + return 0; + } + + __asm__ volatile("nop\nnop\n"); + usarts = UCSR0A; + } + + data = UDR0; + timer_return(); + usart_rx(); + + dbg_wr(0x19); + dbg_wr(0x80 | (state)); + dbg_wr(0x80 | (usarts & 0x0f)); + dbg_wr(0x80 | (usarts >> 4)); + dbg_wr(0x80 | (data & 0x0f)); + dbg_wr(0x80 | (data >> 4)); + + return data; +} #define KBLED_STATE 0x02 #define KBLED_ERROR 0x04 #define KBLED_OK 0x01 -#define DDRD_BEEP 0x20 -#define DDRD_OPEN 0x40 -#define DDRD_CLOSE 0x80 +#define CNTRTOP 122 + +#define ENTER_FAIL 1 +#define WAIT_FAIL 5 +#define WAIT_POWERUP 3 +#define WAIT_INIT 3 +#define WAIT_IDLE 3 -#define CNTRTOP 96 -#define CNTRERR 90 -#define CNTRKEYBEEPOFF 4 -#define CNTROK 88 /* regulates length of keymatic button press */ -#define CNTROK_BEEPOFF 90 -#define CNTROK_BEEPON2 94 -#define OK_WAIT 3 /* mult CNTRTOP */ +#define WAIT_IDLEBLINK 1 /* no full cycle */ +#define CNTR_BLINK 6 + +#define WAIT_ENTRY 15 static uint8_t cntr = 0; -ISR(SIG_OVERFLOW0) +static const char passwd[sizeof(PIN)] = PIN; +static char code[sizeof(PIN)]; + +static void state_enter(void) { - switch (cntr) { - case CNTRTOP: - DDRD = 0; - if (state > 1) { - state--; - } else if (state == 1) { - memset(&code, 0, sizeof(code)); - state = 0; - } else { - send_byte(0xed); - send_byte(KBLED_STATE); - } - cntr = 0; - return; - case CNTROK_BEEPOFF: - if (state > 1) - DDRD &= ~DDRD_BEEP; + dbg_wr(0x11); + dbg_wr(state); + + switch (state) { + case STATE_NONE: + case STATE_FAILURE: + statecntr = WAIT_FAIL; + usart_dis(); + power_down(); break; - case CNTROK_BEEPON2: - if (state > 1) - DDRD |= DDRD_BEEP; + case STATE_POWERUP: + statecntr = WAIT_POWERUP; + power_up(); + usart_rx(); break; - case CNTRKEYBEEPOFF: - DDRD = 0; + case STATE_INITRESET: + statecntr = WAIT_INIT; + send_byte(0xff); break; - case 0x00: - if (state <= 1) { - send_byte(0xed); - send_byte(state ? KBLED_STATE : 0); - } + case STATE_CONFIG: + /* statecntr not used */ + nextstate = STATE_FAILURE; + if (send_byte(0xed) != 0xfa) + break; + if (send_byte(0x00) != 0xfa) + break; + /* identify */ + if (send_byte(0xf2) != 0xfa) + break; + if (wait_byte() != 0xab) + break; + if (wait_byte() != 0x83) + break; + /* scan code set 3 */ + if (send_byte(0xf0) != 0xfa) + break; + if (send_byte(0x03) != 0xfa) + break; + /* make codes only */ + if (send_byte(0xf9) != 0xfa) + break; + nextstate = STATE_IDLE; + break; + case STATE_IDLE: + statecntr = WAIT_IDLE; + nextstate = STATE_FAILURE; + if (send_byte(0xed) != 0xfa) + break; + if (send_byte(0x00) != 0xfa) + break; + nextstate = STATE_NONE; + break; + case STATE_IDLEBLINK: + statecntr = WAIT_IDLEBLINK; + nextstate = STATE_FAILURE; + if (send_byte(0xed) != 0xfa) + break; + if (send_byte(0x01) != 0xfa) + break; + nextstate = STATE_NONE; + cntr = CNTR_BLINK; break; } - cntr++; } -ISR(SIG_USART_RECV) +static void state_timeout(void) { - uint8_t data = UDR0, now; - - if (data == 0xe0) { - ext = 0x80; - return; + switch (state) { + case STATE_NONE: + case STATE_FAILURE: + state = STATE_POWERUP; + break; + case STATE_POWERUP: + state = STATE_INITRESET; + break; + case STATE_INITRESET: + case STATE_CONFIG: + state = STATE_FAILURE; + break; + case STATE_IDLE: + state = STATE_IDLEBLINK; + break; + case STATE_IDLEBLINK: + state = STATE_IDLE; + break; } - if (data == 0xf0) { - brk = 1; - return; + state_enter(); +} + +ISR(SIG_OVERFLOW0) +{ + if (!--cntr) { + cntr = CNTRTOP; + if (statecntr) + statecntr--; } - if (data < 0x80) { - if (brk) { - pressed = 0; - brk = 0; - ext = 0; - return; +} + +static const char kbc_60[48] = { + '_', '_', '_', '_', '_', '_', '_', '_', /* 60 - 67 */ + '_', '1', '_', '4', '7', '_', '_', '_', /* 68 - 6f */ + '0', '.', '2', '5', '6', '8', '_', '/', /* 70 - 77 */ + '_', '_', '3', '_', '+', '9', '*', '_' /* 78 - 7f */ +}; +#define KC_ESC 0x08 +#define KC_ENTER 0x5a +#define KC_NUM 0x76 +#define KC_NP_ENTER 0x79 + +static void handle_keypress(uint8_t data) +{ + uint8_t unlock, lock; + uint8_t ascii = '_'; + uint8_t c; + + lock = data == KC_ESC || data == KC_NUM; + unlock = data == KC_ENTER || data == KC_NP_ENTER; + + if (lock || unlock) { + /* passwd: a b c d \0 + * code: \0 a b c d + */ + if (code[0] || memcmp(passwd, code + 1, sizeof(passwd) - 1)) { + + dbg_wr(0x20); + /* state = BLOCK; */ } else { - now = (data | ext); - ext = 0; - if (pressed == now) - return; - pressed = now; - - if (pressed > 0x60 && pressed < 0x80 && pressed != 0x76 && pressed != 0x77) { - uint8_t c; - for (c = 1; c < sizeof(code); c++) - code[c - 1] = code[c]; - code[sizeof(code) - 1] = kbc_60[pressed - 0x60]; - - TCNT0 = 0; - state = 1; - send_byte(0xed); - send_byte(0x00); - DDRD = DDRD_BEEP; - cntr = 0; - TIFR0 = (1 << TOV0); - }; - if (pressed == 0xda || pressed == 0x76 || pressed == 0x77) { - if (memcmp(passwd, code, sizeof(passwd))) { - TCNT0 = 0; - memset(&code, 0, sizeof(code)); - state = 0; - send_byte(0xed); - send_byte(KBLED_ERROR); - DDRD = DDRD_BEEP; - cntr = CNTRERR; - TIFR0 = (1 << TOV0); - return; - } - TCNT0 = 0; - send_byte(0xed); - send_byte(KBLED_OK); - state = OK_WAIT; - cntr = CNTROK; - TIFR0 = (1 << TOV0); - - DDRD = DDRD_BEEP | ((pressed == 0xda) ? DDRD_OPEN : DDRD_CLOSE); - } + dbg_wr(lock ? 0x21 : 0x22); + /* state = OPENING; */ } + memset(&code, 0, sizeof(code)); return; } + + if (data >= 0x60 && data < 0x80) + ascii = kbc_60[data - 0x60]; + + for (c = 0; c < sizeof(code) - 1; c++) + code[c] = code[c + 1]; + code[c] = ascii; + + /* statecntr = WAIT_ENTRY; */ + +#if 0 + TCNT0 = 0; + state = 1; + send_byte(0xed); + send_byte(0x00); + DDRD = DDRD_BEEP; + cntr = 0; + TIFR0 = (1 << TOV0); +#endif } -int main() +ISR(SIG_USART_RECV) { - DDRB = (1 << 4) | B_RED | B_YLW; - PORTB = (1 << 4) | B_RED | B_YLW; + uint8_t data = UDR0; + + dbg_wr(0x80 | (data & 0x3f)); + dbg_wr(0x84 | (data >> 6)); + + switch (state) { + case STATE_POWERUP: + case STATE_INITRESET: + if (data == 0xaa) + nextstate = STATE_CONFIG; + break; + case STATE_IDLE: + case STATE_IDLEBLINK: + if (data < 0x90) + handle_keypress(data); + default: + break; + } +} - DDRD = 0; - PORTD = 0x3f; +int main() +{ + dbg_init(); + /* /256 = 31'250 Hz = 32 µs per 1 unit + * /256 = 122 Hz = 8'192 µs per for overflow + * /122 = 1,0006 Hz = 1 s per CNTRTOP */ TCCR0A = 0; TIMSK0 = (1 << TOIE0); - TCCR0B = (1 << CS02); + TCCR0B = (1 << CS02); UCSR0C = (1 << UMSEL00) /* synch mode */ | (1 << UPM01) | (1 << UPM00) /* odd parity */ | (1 << UCSZ01) | (1 << UCSZ00) /* 8-bit chars */ | (0 << UCPOL0); /* polarity: sample on falling */ - usart_rx(); + state = STATE_FAILURE; + nextstate = STATE_NONE; + state_enter(); sei(); - while (1) - ; + while (1) { + if (nextstate != STATE_NONE) { + state = nextstate; + nextstate = STATE_NONE; + state_enter(); + + dbg_wr(0x0e); + } else if (!statecntr) { + state_timeout(); + + dbg_wr(0x0e); + } + } } |