summaryrefslogtreecommitdiff
path: root/kbc.c
diff options
context:
space:
mode:
Diffstat (limited to 'kbc.c')
-rw-r--r--kbc.c246
1 files changed, 246 insertions, 0 deletions
diff --git a/kbc.c b/kbc.c
new file mode 100644
index 0000000..6a161c3
--- /dev/null
+++ b/kbc.c
@@ -0,0 +1,246 @@
+#include <stdint.h>
+#include <string.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/sleep.h>
+
+#ifndef PIN
+#error need to define a PIN
+#endif
+
+#define B_RED (1 << 0)
+#define B_YLW (1 << 1)
+
+static void usart_rx()
+{
+ UCSR0B = (1 << RXCIE0) /* recv int en */
+ | (1 << RXEN0); /* do RX */
+}
+
+static void usart_rxpoll()
+{
+ UCSR0B = (1 << RXEN0); /* do RX*/
+}
+
+static void usart_dis()
+{
+ UCSR0B = 0;
+}
+
+#define CLK (1 << 3)
+#define DATA (1 << 1)
+
+static uint8_t send_byte(uint8_t byte)
+{
+ uint8_t cntr, bit, parity = 1, rv;
+
+ usart_dis();
+ rv = UDR0;
+
+ /* 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;
+
+ delay(10);
+
+ DDRD = DATA;
+ PORTD = CLK;
+
+#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)
+
+ for (bit = 0; bit < 8; bit++) {
+ wait_CLK;
+ if (byte & 1) {
+ PORTD = CLK | DATA;
+ parity++;
+ } else {
+ PORTD = CLK;
+ }
+ byte >>= 1;
+ }
+ wait_CLK;
+ PORTD = (parity & 1) ? (CLK | DATA) : CLK;
+ wait_CLK;
+
+ PORTD = CLK | DATA;
+ wait_CLKhi;
+
+ DDRD = 0;
+
+ wait_CLKlo;
+ wait_CLKhi;
+
+ usart_rxpoll();
+
+ while (!(UCSR0A & (1 << RXC0)))
+ __asm__ volatile("nop\n");
+
+ rv = UDR0;
+ usart_rx();
+
+ return rv;
+}
+
+uint8_t ext, brk, pressed, state;
+const char passwd[4] = PIN;
+char code[sizeof(passwd)];
+
+const char kbc_60[32] = {
+ '_', '_', '_', '_', '_', '_', '_', '_', /* 60 - 67 */
+ '_', '1', '_', '4', '7', '_', '_', '_', /* 68 - 6f */
+ '0', '.', '2', '5', '6', '8', '_', '_', /* 70 - 77 */
+ '_', '+', '3', '-', '*', '9', '_', '_' /* 78 - 7f */
+};
+
+#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 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 */
+
+static uint8_t cntr = 0;
+
+ISR(SIG_OVERFLOW0)
+{
+ 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;
+ break;
+ case CNTROK_BEEPON2:
+ if (state > 1)
+ DDRD |= DDRD_BEEP;
+ break;
+ case CNTRKEYBEEPOFF:
+ DDRD = 0;
+ break;
+ case 0x00:
+ if (state <= 1) {
+ send_byte(0xed);
+ send_byte(state ? KBLED_STATE : 0);
+ }
+ break;
+ }
+ cntr++;
+}
+
+ISR(SIG_USART_RECV)
+{
+ uint8_t data = UDR0, now;
+
+ if (data == 0xe0) {
+ ext = 0x80;
+ return;
+ }
+ if (data == 0xf0) {
+ brk = 1;
+ return;
+ }
+ if (data < 0x80) {
+ if (brk) {
+ pressed = 0;
+ brk = 0;
+ ext = 0;
+ return;
+ } 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);
+ }
+ }
+ return;
+ }
+}
+
+int main()
+{
+ DDRB = (1 << 4) | B_RED | B_YLW;
+ PORTB = (1 << 4) | B_RED | B_YLW;
+
+ DDRD = 0;
+ PORTD = 0x3f;
+
+ 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 */
+
+ usart_rx();
+
+ sei();
+ while (1)
+ ;
+}
+