summaryrefslogtreecommitdiffstats
path: root/app/atkbd.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/atkbd.c')
-rw-r--r--app/atkbd.c325
1 files changed, 325 insertions, 0 deletions
diff --git a/app/atkbd.c b/app/atkbd.c
new file mode 100644
index 0000000..0dfc9c8
--- /dev/null
+++ b/app/atkbd.c
@@ -0,0 +1,325 @@
+#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 */
+
+
+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 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_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 uint32_t cycle_diff(uint32_t a,uint32_t b)
+{
+return b-a;
+}
+
+
+static void atkbd_dispatch(int emul,int key,int updown)
+{
+
+printf("KEY> %x %x %x\r\n",emul,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:
+case ATKBD_RET_HANJA: /*Don't handle japanese or korean for the moment*/
+case ATKBD_RET_HANGEUL: /*Don't handle japanese or korean for the moment*/
+ /*All these need no action */
+ break;
+case ATKBD_RET_EMUL0:
+ emul=1;
+ break;
+case ATKBD_RET_EMUL1:
+ emul=2;
+ 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;
+
+
+nvic_disable_irq(KBCLK_IRQ);
+
+#define PS2_CLOCK_SEIZE_DELAY 200
+#define PS2_BIT_DELAY 10
+
+atkbd_set(0,1);
+delay_us(PS2_CLOCK_SEIZE_DELAY);
+atkbd_set(0,0);
+delay_us(PS2_BIT_DELAY);
+atkbd_set(1,0);
+delay_us(PS2_BIT_DELAY);
+
+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;
+}
+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;
+
+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(KBDAT)) if (timed_out(then,ATKBD_TIMEOUT)) goto err;
+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;
+
+exti_reset_request(KBCLK);
+nvic_enable_irq(KBCLK_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;
+
+exti_reset_request(KBCLK);
+nvic_enable_irq(KBCLK_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;
+}
+
+
+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_leds(0x07);
+
+
+}
+
+