#define F_CPU 8*1000000UL #include #include #include #include #include #include #include #include #ifndef PIN #error need to define a PIN #endif #ifndef MASTER_PIN #error need to define a MASTER_PIN #endif /* port B RJ45 * 0 * 1 BEEP (OC1A) * 2 SS up:8 * 3 MOSI up:2 was: LOCK - output broken! * 4 MISO up:4 UNLOCK * 5 SCK up:7 LOCK * 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_FEEDBACK (1 << 2) #define B_OPEN (1 << 4) #define B_CLOSE (1 << 5) #define B_MISO (1 << 4) #define MAXTRIES 3 enum state { STATE_NONE = 0, STATE_FAILURE, STATE_POWERUP, STATE_INITRESET, STATE_CONFIG, STATE_IDLE, STATE_IDLEBLINK, STATE_PASSWORD, STATE_PASSWORD2, STATE_INPUT, STATE_ACCEPT, STATE_REJECT, STATE_KEYMATIC_RECHECK, STATE_KEYMATIC_RECLOSE, #ifdef KILLSWITCH STATE_ERROR, #endif }; /* state is the current system state */ static volatile enum state state; /* system will advance to nextstate on return to main, if nextstate is != STATE_NONE */ static volatile enum state nextstate; /* system will advance to toutstate on timeout. */ static volatile enum state toutstate; 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; } #if 0 static uint8_t dbgtx[64]; static uint8_t dbgpos; static void dbg_wr(uint8_t what) { cli(); if (dbgpos < sizeof(dbgtx)) dbgtx[dbgpos++] = what; SPCR = (1 << SPIE) | (1 << SPE); sei(); } ISR(SPI_STC_vect) { 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); } #else #define dbg_init() #define dbg_wr(x) #endif static volatile uint8_t statecntr = 0; #define mayabort if (TIFR0 & (1 << TOV0)) return bit; #define _wait_CLK(cond) while(cond) { mayabort; } #define wait_CLKlo _wait_CLK( PIND /* & p0 & p1 & p2 */ & D_CLK) #define wait_CLKhi _wait_CLK(!(PIND /* & p0 & p1 & p2 */ & D_CLK)) #define wait_CLK wait_CLKhi; wait_CLKlo; _delay_us(15) #define wait_CLKb #define wait_CLK_set wait_CLKlo #define wait_CLK_samp wait_CLKhi static void timer_return(void) { TCNT0 = 0; TIFR0 = (1 << TOV0); __asm__ volatile("nop\n"); TIMSK0 = (1 << TOIE0); } static void timer_clear(void) { TCNT0 = 0; __asm__ volatile("nop\n"); TIFR0 = (1 << TOV0); } static uint16_t do_send_byte(uint8_t byte) { uint8_t bit = 0, parity = 1, rv, usarts; usart_dis(); _delay_us(5); /* make clock low, request clock from keyboard */ PORTD &= ~D_CLK; DDRD |= D_CLK; _delay_us(166); PORTD &= ~D_DATA; DDRD |= D_DATA; // _delay_us(3); DDRD &= ~D_CLK; PORTD |= D_CLK; wait_CLK_samp; /* data bits */ for (bit = 0; bit < 8; bit++) { wait_CLK_set; if (byte & 1) { PORTD |= D_DATA; parity ^= 1; } else { PORTD &= ~D_DATA; } byte >>= 1; wait_CLK_samp; } timer_clear(); bit = 0x10; /* parity */ wait_CLK_set; if (parity) PORTD |= D_DATA; else PORTD &= ~D_DATA; wait_CLK_samp; bit = 0x11; /* stop bit */ wait_CLK_set; PORTD |= D_DATA; wait_CLK_samp; DDRD &= ~D_DATA; #if 0 bit = 0x12; /* ACK from keyboard */ wait_CLK_set; wait_CLK_samp; #endif bit = 0x14; while (PIND & D_DATA) { mayabort; } bit = 0x15; while (!(PIND & D_DATA)) { mayabort; } wait_CLKhi; /* response from keyboard */ timer_clear(); bit = 0x16; usart_rxpoll(); usarts = 0; while (!(usarts & ((1 << RXC0) | (1 << UPE0) | (1 << FE0)))) { __asm__ volatile("nop\nnop\n"); usarts = UCSR0A; if (TIFR0 & (1 << TOV0)) { TIFR0 = (1 << TOV0); if (++bit == 0x27) return bit; }; } rv = UDR0; return rv | 0x100; } static uint8_t send_byte(uint8_t byte) { uint16_t rv; uint8_t attempts; /* stop timer, use for ourselves to enforce 8 ms timeout */ TIMSK0 = 0; for (attempts = 5; attempts; attempts--) { TCNT0 = 0; __asm__ volatile("nop\n"); TIFR0 = (1 << TOV0); rv = do_send_byte(byte); if (rv == 0x1fa) goto out; /* clear state, wait, retry */ DDRD &= ~(D_DATA | D_CLK); PORTD |= D_DATA | D_CLK; if (rv == 0x1fe) _delay_ms(33); } 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; } static uint8_t wait_byte(void) { uint8_t data, usarts; 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(); return 0; } __asm__ volatile("nop\nnop\n"); usarts = UCSR0A; } data = UDR0; timer_return(); usart_rx(); return data; } #define KBLED_STATE 0x02 #define KBLED_ERROR 0x04 #define KBLED_OK 0x01 #define CNTRTOP 122 #define ENTER_FAIL 1 #define WAIT_FAIL 5 #define WAIT_POWERUP 3 #define WAIT_INIT 3 #define WAIT_IDLE 3 #define WAIT_IDLEBLINK 1 /* no full cycle */ #define CNTR_BLINK 6 #define CNTR_ERROR 61 #define CNTR_BEEP 10 #define CNTR_BEEP_ACK 30 #define CNTR_BEEP_REJ 92 #define WAIT_INPUT 6 #define WAIT_ACCEPT 8 #define WAIT_REJECT 8 #define WAIT_KEYMATIC_RETRY 9 #define CNTR_KEYMATIC 30 /* CNTR_BEEP_ACC == CNTR_KEYMATIC */ static uint8_t cntr = 0; static uint8_t close_try = 0; static uint8_t idle_looped = 0; #ifdef KILLSWITCH static uint8_t error; #endif static EEMEM uint8_t passwd[sizeof(PIN) - 1] = PIN; static EEMEM uint8_t mpasswd[sizeof(MASTER_PIN) - 1] = MASTER_PIN; static uint8_t code[sizeof(PIN)]; static uint8_t new_code[sizeof(PIN)]; static void state_enter(void) { switch (state) { case STATE_NONE: case STATE_FAILURE: toutstate = STATE_POWERUP; statecntr = WAIT_FAIL; usart_dis(); power_down(); break; case STATE_POWERUP: toutstate = STATE_INITRESET; statecntr = WAIT_POWERUP; power_up(); usart_rx(); break; case STATE_INITRESET: toutstate = STATE_FAILURE; statecntr = WAIT_INIT; send_byte(0xff); break; case STATE_CONFIG: /* statecntr not used */ toutstate = STATE_FAILURE; 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; #if 0 /* 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; #endif nextstate = STATE_IDLE; break; case STATE_IDLE: memset(code, 0, sizeof(code)); toutstate = STATE_IDLEBLINK; statecntr = WAIT_IDLE; nextstate = STATE_FAILURE; if (send_byte(0xed) != 0xfa) break; if (send_byte(0x00) != 0xfa) break; /* We have some issues with the keyboard failing every * now and then. Until we know more about the issue, * reset it every 256th time we enter STATE_IDLE :/ */ idle_looped += 1; if (idle_looped != 0xff) nextstate = STATE_NONE; break; case STATE_IDLEBLINK: toutstate = STATE_IDLE; statecntr = WAIT_IDLEBLINK; nextstate = STATE_FAILURE; if (send_byte(0xed) != 0xfa) break; if (send_byte(KBLED_STATE) != 0xfa) break; nextstate = STATE_NONE; cntr = CNTR_BLINK; break; case STATE_INPUT: toutstate = STATE_IDLE; idle_looped = 0; /* input still works, reset idle counter */ statecntr = WAIT_INPUT; if (send_byte(0xed) != 0xfa) break; if (send_byte(KBLED_STATE) != 0xfa) break; break; case STATE_PASSWORD: case STATE_PASSWORD2: toutstate = STATE_IDLE; statecntr = WAIT_INPUT; if (send_byte(0xed) != 0xfa) break; if (send_byte(KBLED_STATE | KBLED_OK) != 0xfa) break; break; case STATE_ACCEPT: toutstate = close_try ? STATE_KEYMATIC_RECHECK : STATE_IDLE; statecntr = WAIT_ACCEPT; if (send_byte(0xed) != 0xfa) break; if (send_byte(KBLED_OK) != 0xfa) break; break; case STATE_REJECT: toutstate = STATE_IDLE; statecntr = WAIT_REJECT; if (send_byte(0xed) != 0xfa) break; if (send_byte(KBLED_ERROR) != 0xfa) break; break; case STATE_KEYMATIC_RECHECK: if (!close_try || !(PINB & B_FEEDBACK)) { close_try = 0; nextstate = STATE_IDLE; return; } if (send_byte(0xed) != 0xfa) break; if (send_byte(KBLED_OK | KBLED_ERROR) != 0xfa) break; close_try--; cntr = CNTR_KEYMATIC; statecntr = WAIT_KEYMATIC_RETRY; toutstate = STATE_KEYMATIC_RECLOSE; PORTB &= ~(B_OPEN | B_BEEP); break; case STATE_KEYMATIC_RECLOSE: cntr = CNTR_KEYMATIC; statecntr = WAIT_KEYMATIC_RETRY; toutstate = STATE_KEYMATIC_RECHECK; PORTB &= ~(B_CLOSE | B_BEEP); break; #ifdef KILLSWITCH case STATE_ERROR: toutstate = STATE_ERROR; statecntr = 1; cntr = CNTR_ERROR; error ^= KBLED_ERROR; if (send_byte(0xed) != 0xfa) break; if (send_byte(error) != 0xfa) break; break; #endif } } ISR(TIMER0_OVF_vect) { if (!--cntr) { cntr = CNTRTOP; PORTB = B_OPEN | B_CLOSE | B_BEEP; if (statecntr) statecntr--; } } #define ESC 0x1b #define ENT 0x0d #define KBC_BASE 0x16 const PROGMEM uint8_t kbc[] = { /* 0 1 2 3 4 5 6 7 * 8 9 a b c d e f */ '1', '_', /* 10 - 17 */ '_', '_', '_', '_', '_', '_', '2', '_', /* 18 - 1f */ '_', '_', '_', '_', '_', '4', '3', '_', /* 20 - 27 */ '_', '_', '_', '_', '_', '_', '5', '_', /* 28 - 2f */ '_', '_', '_', '_', '_', '_', '6', '_', /* 30 - 37 */ '_', '_', '_', '_', '_', '7', '8', '_', /* 38 - 3f */ '_', '_', '_', '_', '_', '0', '9', '_', /* 40 - 47 */ '_', '_', '_', '_', '_', '_', '_', '_', /* 48 - 4f */ '_', '_', '_', '_', '_', '_', '_', '_', /* 50 - 57 */ '_', '_', ENT, '_', '_', '_', '_', '_', /* 58 - 5f */ '_', '_', '_', '_', '_', '_', '_', '_', /* 60 - 67 */ '_', '1', '_', '4', '7', '_', '_', '_', /* 68 - 6f */ '0', '_', '2', '5', '6', '8', ESC, ESC, /* 70 - 77 */ '_', '_', '3', '_', '_', '9' /* 78 - 7f */ }; #define NUMPAD_ENTER 0xda /* e0 5a */ #define F12 0x07 static uint8_t pressed = 0, e0 = 0, release = 0; static void handle_keypress(uint8_t data) { uint8_t unlock, lock, change_pass; uint8_t ascii = '_'; uint8_t c; if (release) { pressed = 0; release = 0; e0 = 0; return; } if (data == pressed) return; if (data == 0xe0) { e0 = 0x80; return; } if (data == 0xf0) { release = 1; return; } if (data >= 0x80) return; pressed = data; data |= e0; e0 = 0; if (data >= KBC_BASE && data < KBC_BASE + sizeof(kbc)) ascii = pgm_read_byte(kbc + data - KBC_BASE); dbg_wr(0x03); dbg_wr(0x80 | (ascii & 0xf)); dbg_wr(0x80 | (ascii >> 4)); if (state != STATE_PASSWORD && state != STATE_PASSWORD2) { lock = ascii == ESC; unlock = ascii == ENT || data == NUMPAD_ENTER; change_pass = data == F12; } else { lock = unlock = 0; change_pass = ascii == ENT || data == NUMPAD_ENTER; } if (change_pass) { uint8_t pos, ok = 1; switch(state) { default: /* Check master password, change state to password input */ if (code[0]) ok = 0; for (pos = 1; pos < sizeof(code); pos++) if(code[pos] != eeprom_read_byte(mpasswd + pos - 1)) ok = 0; if (ok) { cntr = CNTR_BEEP_ACK; nextstate = STATE_PASSWORD; } break; case STATE_PASSWORD: /* Check password and store it to buffer */ if (code[0]) ok = 0; if (ok) { memcpy(new_code, code, sizeof(code)); PORTB &= ~B_BEEP; cntr = CNTR_BEEP_ACK; nextstate = STATE_PASSWORD2; } break; case STATE_PASSWORD2: /* Compare password with buffer, write to eeprom */ if (memcmp(new_code, code, sizeof(code))) ok = 0; if (ok) { eeprom_update_block(new_code + 1, passwd, sizeof(new_code) - 1); PORTB &= ~B_BEEP; cntr = CNTR_BEEP_REJ; nextstate = STATE_ACCEPT; } break; } if (!ok) { PORTB &= ~B_BEEP; cntr = CNTR_BEEP_REJ; nextstate = STATE_REJECT; } memset(code, 0, sizeof(code)); return; } if (lock || unlock) { uint8_t pos, ok = 1; /* passwd: a b c d * code: \0 a b c d */ if (code[0]) ok = 0; #ifdef KILLSWITCH if (!memcmp("9164", code + 1, 4)) { error = 0; nextstate = STATE_ERROR; return; } #endif for (pos = 1; pos < sizeof(code); pos++) if (code[pos] != eeprom_read_byte(passwd + pos - 1)) ok = 0; if (!ok) { PORTB &= ~B_BEEP; cntr = CNTR_BEEP_REJ; nextstate = STATE_REJECT; } else { if (lock) { PORTB &= ~(B_CLOSE | B_BEEP); close_try = MAXTRIES; } else { PORTB &= ~(B_OPEN | B_BEEP); } cntr = CNTR_KEYMATIC; nextstate = STATE_ACCEPT; } memset(code, 0, sizeof(code)); return; } for (c = 0; c < sizeof(code) - 1; c++) code[c] = code[c + 1]; code[c] = ascii; PORTB &= ~B_BEEP; cntr = CNTR_BEEP; switch (state) { case STATE_PASSWORD: nextstate = STATE_PASSWORD; break; case STATE_PASSWORD2: nextstate = STATE_PASSWORD2; break; default: nextstate = STATE_INPUT; } } ISR(USART_RX_vect) { 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: case STATE_PASSWORD: case STATE_PASSWORD2: case STATE_INPUT: handle_keypress(data); default: break; } } int main() { dbg_init(); PORTB = B_OPEN | B_CLOSE | B_BEEP; DDRB = B_OPEN | B_CLOSE | B_BEEP; /* /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); UCSR0C = (1 << UMSEL00) /* synch mode */ | (1 << UPM01) | (1 << UPM00) /* odd parity */ | (1 << UCSZ01) | (1 << UCSZ00) /* 8-bit chars */ | (0 << UCPOL0); /* polarity: sample on falling */ state = STATE_FAILURE; nextstate = STATE_NONE; state_enter(); for (int i = 0; i < 5000; i++) { __asm__ volatile("nop\nnop\n"); } sei(); while (1) { if (nextstate != STATE_NONE) { state = nextstate; nextstate = STATE_NONE; state_enter(); } else if (!statecntr) { state = toutstate; nextstate = STATE_NONE; state_enter(); } } }