summary refs log tree commit diff
path: root/protocol
diff options
context:
space:
mode:
authortmk <nobody@nowhere>2013-11-28 16:02:29 +0900
committertmk <nobody@nowhere>2013-11-28 16:02:29 +0900
commitbc5b64d83259715667356770965a33aa620a8030 (patch)
tree074377caef4d838ac3890f39e9ff67f346aab506 /protocol
parent4e36159be226e544dfebbe06b1955261951209a4 (diff)
parent0b9c0f6012a90eeb8cced9feccf3a65d8b4e5535 (diff)
Merge branch 'ps2_keyboard_fix'
Diffstat (limited to 'protocol')
-rw-r--r--protocol/lufa/lufa.c4
-rw-r--r--protocol/pjrc/usb.c3
-rw-r--r--protocol/ps2.h146
-rw-r--r--protocol/ps2_busywait.c185
-rw-r--r--protocol/ps2_interrupt.c (renamed from protocol/ps2.c)334
-rw-r--r--protocol/ps2_usart.c174
6 files changed, 431 insertions, 415 deletions
diff --git a/protocol/lufa/lufa.c b/protocol/lufa/lufa.c
index 04e8e78f38..eca51c8784 100644
--- a/protocol/lufa/lufa.c
+++ b/protocol/lufa/lufa.c
@@ -148,7 +148,6 @@ static void Console_Task(void)
 */
 void EVENT_USB_Device_Connect(void)
 {
-    led_set(0x1f);  // all on
 }
 
 void EVENT_USB_Device_Disconnect(void)
@@ -172,8 +171,9 @@ void EVENT_USB_Device_WakeUp()
 
 #ifdef SLEEP_LED_ENABLE
     sleep_led_disable();
-#endif
+    // NOTE: converters may not accept this
     led_set(host_keyboard_leds());
+#endif
 }
 
 void EVENT_USB_Device_StartOfFrame(void)
diff --git a/protocol/pjrc/usb.c b/protocol/pjrc/usb.c
index 84c99972f2..393b36f78e 100644
--- a/protocol/pjrc/usb.c
+++ b/protocol/pjrc/usb.c
@@ -662,8 +662,9 @@ ISR(USB_GEN_vect)
             suspend_wakeup_init();
 #ifdef SLEEP_LED_ENABLE
             sleep_led_disable();
-#endif
+            // NOTE: converters may not accept this
             led_set(host_keyboard_leds());
+#endif
 
             UDIEN |= (1<<SUSPE);
             UDIEN &= ~(1<<WAKEUPE);
diff --git a/protocol/ps2.h b/protocol/ps2.h
index 834165356c..483eea7209 100644
--- a/protocol/ps2.h
+++ b/protocol/ps2.h
@@ -1,5 +1,5 @@
 /*
-Copyright 2010,2011 Jun WAKO <wakojun@gmail.com>
+Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>
 
 This software is licensed with a Modified BSD License.
 All of this is supposed to be Free Software, Open Source, DFSG-free,
@@ -37,32 +37,46 @@ POSSIBILITY OF SUCH DAMAGE.
 
 #ifndef PS2_H
 #define PS2_H
+
+#include <stdbool.h>
+#include <util/delay.h>
+#include <avr/io.h>
+
 /*
  * Primitive PS/2 Library for AVR
+ *
+ * PS/2 Resources
+ * --------------
+ * [1] The PS/2 Mouse/Keyboard Protocol
+ * http://www.computer-engineering.org/ps2protocol/
+ * Concise and thorough primer of PS/2 protocol.
+ *
+ * [2] Keyboard and Auxiliary Device Controller
+ * http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
+ * Signal Timing and Format
+ *
+ * [3] Keyboards(101- and 102-key)
+ * http://www.mcamafia.de/pdf/ibm_hitrc11.pdf
+ * Keyboard Layout, Scan Code Set, POR, and Commands.
+ *
+ * [4] PS/2 Reference Manuals
+ * http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
+ * Collection of IBM Personal System/2 documents.
+ *
+ * [5] TrackPoint Engineering Specifications for version 3E
+ * https://web.archive.org/web/20100526161812/http://wwwcssrv.almaden.ibm.com/trackpoint/download.html
  */
-
-
-/* port settings for clock and data line */
-#if !(defined(PS2_CLOCK_PORT) && \
-      defined(PS2_CLOCK_PIN) && \
-      defined(PS2_CLOCK_DDR) && \
-      defined(PS2_CLOCK_BIT))
-#   error "PS/2 clock port setting is required in config.h"
-#endif
-
-#if !(defined(PS2_DATA_PORT) && \
-      defined(PS2_DATA_PIN) && \
-      defined(PS2_DATA_DDR) && \
-      defined(PS2_DATA_BIT))
-#   error "PS/2 data port setting is required in config.h"
-#endif
-
 #define PS2_ACK         0xFA
 #define PS2_RESEND      0xFE
 #define PS2_SET_LED     0xED
 
-#define PS2_ERR_NONE    0
-#define PS2_ERR_PARITY  0x10
+// TODO: error numbers
+#define PS2_ERR_NONE        0
+#define PS2_ERR_STARTBIT1   1
+#define PS2_ERR_STARTBIT2   2
+#define PS2_ERR_STARTBIT3   3
+#define PS2_ERR_PARITY      0x10
+#define PS2_ERR_NODATA      0x20
 
 #define PS2_LED_SCROLL_LOCK 0
 #define PS2_LED_NUM_LOCK    1
@@ -71,13 +85,101 @@ POSSIBILITY OF SUCH DAMAGE.
 
 extern uint8_t ps2_error;
 
-/* host role */
 void ps2_host_init(void);
 uint8_t ps2_host_send(uint8_t data);
 uint8_t ps2_host_recv_response(void);
 uint8_t ps2_host_recv(void);
 void ps2_host_set_led(uint8_t usb_led);
 
-/* device role */
+
+/* Check port settings for clock and data line */
+#if !(defined(PS2_CLOCK_PORT) && \
+      defined(PS2_CLOCK_PIN) && \
+      defined(PS2_CLOCK_DDR) && \
+      defined(PS2_CLOCK_BIT))
+#   error "PS/2 clock port setting is required in config.h"
+#endif
+
+#if !(defined(PS2_DATA_PORT) && \
+      defined(PS2_DATA_PIN) && \
+      defined(PS2_DATA_DDR) && \
+      defined(PS2_DATA_BIT))
+#   error "PS/2 data port setting is required in config.h"
+#endif
+
+/*--------------------------------------------------------------------
+ * static functions
+ *------------------------------------------------------------------*/
+static inline void clock_lo(void)
+{
+    PS2_CLOCK_PORT &= ~(1<<PS2_CLOCK_BIT);
+    PS2_CLOCK_DDR  |=  (1<<PS2_CLOCK_BIT);
+}
+static inline void clock_hi(void)
+{
+    /* input with pull up */
+    PS2_CLOCK_DDR  &= ~(1<<PS2_CLOCK_BIT);
+    PS2_CLOCK_PORT |=  (1<<PS2_CLOCK_BIT);
+}
+static inline bool clock_in(void)
+{
+    PS2_CLOCK_DDR  &= ~(1<<PS2_CLOCK_BIT);
+    PS2_CLOCK_PORT |=  (1<<PS2_CLOCK_BIT);
+    _delay_us(1);
+    return PS2_CLOCK_PIN&(1<<PS2_CLOCK_BIT);
+}
+static inline void data_lo(void)
+{
+    PS2_DATA_PORT &= ~(1<<PS2_DATA_BIT);
+    PS2_DATA_DDR  |=  (1<<PS2_DATA_BIT);
+}
+static inline void data_hi(void)
+{
+    /* input with pull up */
+    PS2_DATA_DDR  &= ~(1<<PS2_DATA_BIT);
+    PS2_DATA_PORT |=  (1<<PS2_DATA_BIT);
+}
+static inline bool data_in(void)
+{
+    PS2_DATA_DDR  &= ~(1<<PS2_DATA_BIT);
+    PS2_DATA_PORT |=  (1<<PS2_DATA_BIT);
+    _delay_us(1);
+    return PS2_DATA_PIN&(1<<PS2_DATA_BIT);
+}
+
+static inline uint16_t wait_clock_lo(uint16_t us)
+{
+    while (clock_in()  && us) { asm(""); _delay_us(1); us--; }
+    return us;
+}
+static inline uint16_t wait_clock_hi(uint16_t us)
+{
+    while (!clock_in() && us) { asm(""); _delay_us(1); us--; }
+    return us;
+}
+static inline uint16_t wait_data_lo(uint16_t us)
+{
+    while (data_in() && us)  { asm(""); _delay_us(1); us--; }
+    return us;
+}
+static inline uint16_t wait_data_hi(uint16_t us)
+{
+    while (!data_in() && us)  { asm(""); _delay_us(1); us--; }
+    return us;
+}
+
+/* idle state that device can send */
+static inline void idle(void)
+{
+    clock_hi();
+    data_hi();
+}
+
+/* inhibit device to send */
+static inline void inhibit(void)
+{
+    clock_lo();
+    data_hi();
+}
 
 #endif
diff --git a/protocol/ps2_busywait.c b/protocol/ps2_busywait.c
new file mode 100644
index 0000000000..05dd7b27e6
--- /dev/null
+++ b/protocol/ps2_busywait.c
@@ -0,0 +1,185 @@
+/*
+Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>
+
+This software is licensed with a Modified BSD License.
+All of this is supposed to be Free Software, Open Source, DFSG-free,
+GPL-compatible, and OK to use in both free and proprietary applications.
+Additions and corrections to this file are welcome.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in
+  the documentation and/or other materials provided with the
+  distribution.
+
+* Neither the name of the copyright holders nor the names of
+  contributors may be used to endorse or promote products derived
+  from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ * PS/2 protocol busywait version
+ */
+
+#include <stdbool.h>
+#include <util/delay.h>
+#include "ps2.h"
+#include "debug.h"
+
+
+#define WAIT(stat, us, err) do { \
+    if (!wait_##stat(us)) { \
+        ps2_error = err; \
+        goto ERROR; \
+    } \
+} while (0)
+
+
+uint8_t ps2_error = PS2_ERR_NONE;
+
+
+void ps2_host_init(void)
+{
+    // POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
+    _delay_ms(2500);
+
+    inhibit();
+}
+
+uint8_t ps2_host_send(uint8_t data)
+{
+    bool parity = true;
+    ps2_error = PS2_ERR_NONE;
+
+    /* terminate a transmission if we have */
+    inhibit();
+    _delay_us(100); // 100us [4]p.13, [5]p.50
+
+    /* 'Request to Send' and Start bit */
+    data_lo();
+    clock_hi();
+    WAIT(clock_lo, 10000, 10);   // 10ms [5]p.50
+
+    /* Data bit */
+    for (uint8_t i = 0; i < 8; i++) {
+        _delay_us(15);
+        if (data&(1<<i)) {
+            parity = !parity;
+            data_hi();
+        } else {
+            data_lo();
+        }
+        WAIT(clock_hi, 50, 2);
+        WAIT(clock_lo, 50, 3);
+    }
+
+    /* Parity bit */
+    _delay_us(15);
+    if (parity) { data_hi(); } else { data_lo(); }
+    WAIT(clock_hi, 50, 4);
+    WAIT(clock_lo, 50, 5);
+
+    /* Stop bit */
+    _delay_us(15);
+    data_hi();
+
+    /* Ack */
+    WAIT(data_lo, 50, 6);
+    WAIT(clock_lo, 50, 7);
+
+    /* wait for idle state */
+    WAIT(clock_hi, 50, 8);
+    WAIT(data_hi, 50, 9);
+
+    inhibit();
+    return ps2_host_recv_response();
+ERROR:
+    inhibit();
+    return 0;
+}
+
+/* receive data when host want else inhibit communication */
+uint8_t ps2_host_recv_response(void)
+{
+    // Command may take 25ms/20ms at most([5]p.46, [3]p.21)
+    // 250 * 100us(wait for start bit in ps2_host_recv)
+    uint8_t data = 0;
+    uint8_t try = 250;
+    do {
+        data = ps2_host_recv();
+    } while (try-- && ps2_error);
+    return data;
+}
+
+/* called after start bit comes */
+uint8_t ps2_host_recv(void)
+{
+    uint8_t data = 0;
+    bool parity = true;
+    ps2_error = PS2_ERR_NONE;
+
+    /* release lines(idle state) */
+    idle();
+
+    /* start bit [1] */
+    WAIT(clock_lo, 100, 1); // TODO: this is enough?
+    WAIT(data_lo, 1, 2);
+    WAIT(clock_hi, 50, 3);
+
+    /* data [2-9] */
+    for (uint8_t i = 0; i < 8; i++) {
+        WAIT(clock_lo, 50, 4);
+        if (data_in()) {
+            parity = !parity;
+            data |= (1<<i);
+        }
+        WAIT(clock_hi, 50, 5);
+    }
+
+    /* parity [10] */
+    WAIT(clock_lo, 50, 6);
+    if (data_in() != parity) {
+        ps2_error = PS2_ERR_PARITY;
+        goto ERROR;
+    }
+    WAIT(clock_hi, 50, 7);
+
+    /* stop bit [11] */
+    WAIT(clock_lo, 50, 8);
+    WAIT(data_hi, 1, 9);
+    WAIT(clock_hi, 50, 10);
+
+    inhibit();
+    return data;
+ERROR:
+    if (ps2_error > PS2_ERR_STARTBIT3) {
+        xprintf("x%02X\n", ps2_error);
+    }
+    inhibit();
+    return 0;
+}
+
+/* send LED state to keyboard */
+void ps2_host_set_led(uint8_t led)
+{
+    ps2_host_send(0xED);
+    ps2_host_send(led);
+}
diff --git a/protocol/ps2.c b/protocol/ps2_interrupt.c
index e5873a9bfa..259d254007 100644
--- a/protocol/ps2.c
+++ b/protocol/ps2_interrupt.c
@@ -35,47 +35,15 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
 */
 
+/*
+ * PS/2 protocol Pin interrupt version
+ */
+
 #include <stdbool.h>
-#include <avr/io.h>
 #include <avr/interrupt.h>
 #include <util/delay.h>
 #include "ps2.h"
-#include "debug.h"
-
-
-#ifndef PS2_USE_INT
-static uint8_t recv_data(void);
-#endif
-static inline void clock_lo(void);
-static inline void clock_hi(void);
-static inline bool clock_in(void);
-static inline void data_lo(void);
-static inline void data_hi(void);
-static inline bool data_in(void);
-static inline uint16_t wait_clock_lo(uint16_t us);
-static inline uint16_t wait_clock_hi(uint16_t us);
-static inline uint16_t wait_data_lo(uint16_t us);
-static inline uint16_t wait_data_hi(uint16_t us);
-static inline void idle(void);
-static inline void inhibit(void);
-
-
-/*
-Primitive PS/2 Library for AVR
-==============================
-Host side is only supported now.
-
-
-I/O control
------------
-High state is asserted by input with pull up.
-
-
-PS/2 References
----------------
-http://www.computer-engineering.org/ps2protocol/
-http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
-*/
+#include "print.h"
 
 
 #define WAIT(stat, us, err) do { \
@@ -89,35 +57,38 @@ http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
 uint8_t ps2_error = PS2_ERR_NONE;
 
 
+static inline uint8_t pbuf_dequeue(void);
+static inline void pbuf_enqueue(uint8_t data);
+static inline bool pbuf_has_data(void);
+static inline void pbuf_clear(void);
+
+
 void ps2_host_init(void)
 {
-#ifdef PS2_USE_INT
+    idle();
     PS2_INT_INIT();
     PS2_INT_ON();
-    idle();
-#else
-    inhibit();
-#endif
+    // POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
+    //_delay_ms(2500);
 }
 
-// TODO: send using interrupt if available
 uint8_t ps2_host_send(uint8_t data)
 {
-    uint8_t res = 0;
     bool parity = true;
     ps2_error = PS2_ERR_NONE;
-#ifdef PS2_USE_INT
+
     PS2_INT_OFF();
-#endif
+
     /* terminate a transmission if we have */
     inhibit();
-    _delay_us(200); // at least 100us
+    _delay_us(100); // 100us [4]p.13, [5]p.50
 
-    /* start bit [1] */
+    /* 'Request to Send' and Start bit */
     data_lo();
     clock_hi();
-    WAIT(clock_lo, 20000, 10);   // may take 15ms at most until device starts clocking
-    /* data [2-9] */
+    WAIT(clock_lo, 10000, 10);   // 10ms [5]p.50
+
+    /* Data bit[2-9] */
     for (uint8_t i = 0; i < 8; i++) {
         _delay_us(15);
         if (data&(1<<i)) {
@@ -129,15 +100,18 @@ uint8_t ps2_host_send(uint8_t data)
         WAIT(clock_hi, 50, 2);
         WAIT(clock_lo, 50, 3);
     }
-    /* parity [10] */
+
+    /* Parity bit */
     _delay_us(15);
     if (parity) { data_hi(); } else { data_lo(); }
     WAIT(clock_hi, 50, 4);
     WAIT(clock_lo, 50, 5);
-    /* stop bit [11] */
+
+    /* Stop bit */
     _delay_us(15);
     data_hi();
-    /* ack [12] */
+
+    /* Ack */
     WAIT(data_lo, 50, 6);
     WAIT(clock_lo, 50, 7);
 
@@ -145,116 +119,35 @@ uint8_t ps2_host_send(uint8_t data)
     WAIT(clock_hi, 50, 8);
     WAIT(data_hi, 50, 9);
 
-#ifdef PS2_USE_INT
+    idle();
     PS2_INT_ON();
-#endif
-    res = ps2_host_recv_response();
+    return ps2_host_recv_response();
 ERROR:
-#ifdef PS2_USE_INT
-    PS2_INT_ON();
     idle();
-#else
-    inhibit();
-#endif
-    return res;
+    PS2_INT_ON();
+    return 0;
 }
 
-#ifndef PS2_USE_INT
-/* receive data when host want else inhibit communication */
 uint8_t ps2_host_recv_response(void)
 {
-    uint8_t data = 0;
-
-#ifdef PS2_USE_INT
-    PS2_INT_OFF();
-#endif
-    /* terminate a transmission if we have */
-    inhibit();
-    _delay_us(100);
-
-    /* release lines(idle state) */
-    idle();
-
-    /* wait start bit */
-    wait_clock_lo(25000);    // command response may take 20 ms at most
-    data = recv_data();
-
-    inhibit();
-    return data;
-}
-#endif
-
-#ifndef PS2_USE_INT
-uint8_t ps2_host_recv(void)
-{
-    return ps2_host_recv_response();
-}
-#else
-/* ring buffer to store ps/2 key data */
-#define PBUF_SIZE 32
-static uint8_t pbuf[PBUF_SIZE];
-static uint8_t pbuf_head = 0;
-static uint8_t pbuf_tail = 0;
-static inline void pbuf_enqueue(uint8_t data)
-{
-    uint8_t sreg = SREG;
-    cli();
-    uint8_t next = (pbuf_head + 1) % PBUF_SIZE;
-    if (next != pbuf_tail) {
-        pbuf[pbuf_head] = data;
-        pbuf_head = next;
-    } else {
-        debug("pbuf: full\n");
-    }
-    SREG = sreg;
-}
-static inline uint8_t pbuf_dequeue(void)
-{
-    uint8_t val = 0;
-
-    uint8_t sreg = SREG;
-    cli();
-    if (pbuf_head != pbuf_tail) {
-        val = pbuf[pbuf_tail];
-        pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE;
+    // Command may take 25ms/20ms at most([5]p.46, [3]p.21)
+    uint8_t retry = 25;
+    while (retry-- && !pbuf_has_data()) {
+        _delay_ms(1);
     }
-    SREG = sreg;
-
-    return val;
-}
-static inline bool pbuf_has_data(void)
-{
-    uint8_t sreg = SREG;
-    cli();
-    bool has_data = (pbuf_head != pbuf_tail);
-    SREG = sreg;
-    return has_data;
-}
-static inline void pbuf_clear(void)
-{
-    uint8_t sreg = SREG;
-    cli();
-    pbuf_head = pbuf_tail = 0;
-    SREG = sreg;
+    return pbuf_dequeue();
 }
 
 /* get data received by interrupt */
 uint8_t ps2_host_recv(void)
 {
-    if (ps2_error) {
-        print("x");
-        phex(ps2_error);
-        ps2_host_send(0xFE);    // request to resend
+    if (pbuf_has_data()) {
         ps2_error = PS2_ERR_NONE;
+        return pbuf_dequeue();
+    } else {
+        ps2_error = PS2_ERR_NODATA;
+        return 0;
     }
-    idle();
-    return pbuf_dequeue();
-}
-
-uint8_t ps2_host_recv_response(void)
-{
-    while (!pbuf_has_data()) ;
-    return pbuf_dequeue();
 }
 
 ISR(PS2_INT_VECT)
@@ -309,7 +202,6 @@ ISR(PS2_INT_VECT)
             if (!data_in())
                 goto ERROR;
             pbuf_enqueue(data);
-//phex(data);
             goto DONE;
             break;
         default:
@@ -317,7 +209,6 @@ ISR(PS2_INT_VECT)
     }
     goto RETURN;
 ERROR:
-    inhibit();
     ps2_error = state;
 DONE:
     state = INIT;
@@ -326,8 +217,6 @@ DONE:
 RETURN:
     return;
 }
-#endif
-
 
 /* send LED state to keyboard */
 void ps2_host_set_led(uint8_t led)
@@ -337,116 +226,53 @@ void ps2_host_set_led(uint8_t led)
 }
 
 
-#ifndef PS2_USE_INT
-/* called after start bit comes */
-static uint8_t recv_data(void)
+/*--------------------------------------------------------------------
+ * Ring buffer to store scan codes from keyboard
+ *------------------------------------------------------------------*/
+#define PBUF_SIZE 32
+static uint8_t pbuf[PBUF_SIZE];
+static uint8_t pbuf_head = 0;
+static uint8_t pbuf_tail = 0;
+static inline void pbuf_enqueue(uint8_t data)
 {
-    uint8_t data = 0;
-    bool parity = true;
-    ps2_error = PS2_ERR_NONE;
-
-    /* start bit [1] */
-    WAIT(clock_lo, 1, 1);
-    WAIT(data_lo, 1, 2);
-    WAIT(clock_hi, 50, 3);
-
-    /* data [2-9] */
-    for (uint8_t i = 0; i < 8; i++) {
-        WAIT(clock_lo, 50, 4);
-        if (data_in()) {
-            parity = !parity;
-            data |= (1<<i);
-        }
-        WAIT(clock_hi, 50, 5);
-    }
-
-    /* parity [10] */
-    WAIT(clock_lo, 50, 6);
-    if (data_in() != parity) {
-        ps2_error = PS2_ERR_PARITY;
-        goto ERROR;
+    uint8_t sreg = SREG;
+    cli();
+    uint8_t next = (pbuf_head + 1) % PBUF_SIZE;
+    if (next != pbuf_tail) {
+        pbuf[pbuf_head] = data;
+        pbuf_head = next;
+    } else {
+        print("pbuf: full\n");
     }
-    WAIT(clock_hi, 50, 7);
-
-    /* stop bit [11] */
-    WAIT(clock_lo, 50, 8);
-    WAIT(data_hi, 1, 9);
-    WAIT(clock_hi, 50, 10);
-
-    return data;
-ERROR:
-    return 0;
-}
-#endif
-
-static inline void clock_lo()
-{
-    PS2_CLOCK_PORT &= ~(1<<PS2_CLOCK_BIT);
-    PS2_CLOCK_DDR  |=  (1<<PS2_CLOCK_BIT);
-}
-static inline void clock_hi()
-{
-    /* input with pull up */
-    PS2_CLOCK_DDR  &= ~(1<<PS2_CLOCK_BIT);
-    PS2_CLOCK_PORT |=  (1<<PS2_CLOCK_BIT);
-}
-static inline bool clock_in()
-{
-    PS2_CLOCK_DDR  &= ~(1<<PS2_CLOCK_BIT);
-    PS2_CLOCK_PORT |=  (1<<PS2_CLOCK_BIT);
-    _delay_us(1);
-    return PS2_CLOCK_PIN&(1<<PS2_CLOCK_BIT);
-}
-static inline void data_lo()
-{
-    PS2_DATA_PORT &= ~(1<<PS2_DATA_BIT);
-    PS2_DATA_DDR  |=  (1<<PS2_DATA_BIT);
-}
-static inline void data_hi()
-{
-    /* input with pull up */
-    PS2_DATA_DDR  &= ~(1<<PS2_DATA_BIT);
-    PS2_DATA_PORT |=  (1<<PS2_DATA_BIT);
+    SREG = sreg;
 }
-static inline bool data_in()
+static inline uint8_t pbuf_dequeue(void)
 {
-    PS2_DATA_DDR  &= ~(1<<PS2_DATA_BIT);
-    PS2_DATA_PORT |=  (1<<PS2_DATA_BIT);
-    _delay_us(1);
-    return PS2_DATA_PIN&(1<<PS2_DATA_BIT);
-}
+    uint8_t val = 0;
 
-static inline uint16_t wait_clock_lo(uint16_t us)
-{
-    while (clock_in()  && us) { asm(""); _delay_us(1); us--; }
-    return us;
-}
-static inline uint16_t wait_clock_hi(uint16_t us)
-{
-    while (!clock_in() && us) { asm(""); _delay_us(1); us--; }
-    return us;
-}
-static inline uint16_t wait_data_lo(uint16_t us)
-{
-    while (data_in() && us)  { asm(""); _delay_us(1); us--; }
-    return us;
+    uint8_t sreg = SREG;
+    cli();
+    if (pbuf_head != pbuf_tail) {
+        val = pbuf[pbuf_tail];
+        pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE;
+    }
+    SREG = sreg;
+
+    return val;
 }
-static inline uint16_t wait_data_hi(uint16_t us)
+static inline bool pbuf_has_data(void)
 {
-    while (!data_in() && us)  { asm(""); _delay_us(1); us--; }
-    return us;
+    uint8_t sreg = SREG;
+    cli();
+    bool has_data = (pbuf_head != pbuf_tail);
+    SREG = sreg;
+    return has_data;
 }
-
-/* idle state that device can send */
-static inline void idle(void)
+static inline void pbuf_clear(void)
 {
-    clock_hi();
-    data_hi();
+    uint8_t sreg = SREG;
+    cli();
+    pbuf_head = pbuf_tail = 0;
+    SREG = sreg;
 }
 
-/* inhibit device to send */
-static inline void inhibit(void)
-{
-    clock_lo();
-    data_hi();
-}
diff --git a/protocol/ps2_usart.c b/protocol/ps2_usart.c
index 40c46c497c..c2d9d0a208 100644
--- a/protocol/ps2_usart.c
+++ b/protocol/ps2_usart.c
@@ -36,32 +36,14 @@ POSSIBILITY OF SUCH DAMAGE.
 */
 
 /*
-Primitive PS/2 Library for AVR
-==============================
-Host side is only supported now.
-Synchronous USART is used to receive data by hardware process
-rather than interrupt. During V-USB interrupt runs, CLOCK interrupt
-cannot interpose. In the result it is prone to lost CLOCK edge.
+ * PS/2 protocol USART version
+ */
 
-
-I/O control
------------
-High state is asserted by internal pull-up.
-If you have a signaling problem, you may need to have
-external pull-up resisters on CLOCK and DATA line.
-
-
-PS/2 References
----------------
-http://www.computer-engineering.org/ps2protocol/
-http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
-*/
 #include <stdbool.h>
-#include <avr/io.h>
 #include <avr/interrupt.h>
 #include <util/delay.h>
 #include "ps2.h"
-#include "debug.h"
+#include "print.h"
 
 
 #define WAIT(stat, us, err) do { \
@@ -75,18 +57,6 @@ http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
 uint8_t ps2_error = PS2_ERR_NONE;
 
 
-static inline void clock_lo(void);
-static inline void clock_hi(void);
-static inline bool clock_in(void);
-static inline void data_lo(void);
-static inline void data_hi(void);
-static inline bool data_in(void);
-static inline uint16_t wait_clock_lo(uint16_t us);
-static inline uint16_t wait_clock_hi(uint16_t us);
-static inline uint16_t wait_data_lo(uint16_t us);
-static inline uint16_t wait_data_hi(uint16_t us);
-static inline void idle(void);
-static inline void inhibit(void);
 static inline uint8_t pbuf_dequeue(void);
 static inline void pbuf_enqueue(uint8_t data);
 static inline bool pbuf_has_data(void);
@@ -95,14 +65,15 @@ static inline void pbuf_clear(void);
 
 void ps2_host_init(void)
 {
-    idle();
+    idle(); // without this many USART errors occur when cable is disconnected
     PS2_USART_INIT();
     PS2_USART_RX_INT_ON();
+    // POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
+    //_delay_ms(2500);
 }
 
 uint8_t ps2_host_send(uint8_t data)
 {
-    uint8_t res = 0;
     bool parity = true;
     ps2_error = PS2_ERR_NONE;
 
@@ -110,13 +81,14 @@ uint8_t ps2_host_send(uint8_t data)
 
     /* terminate a transmission if we have */
     inhibit();
-    _delay_us(100);
+    _delay_us(100); // [4]p.13
 
-    /* start bit [1] */
+    /* 'Request to Send' and Start bit */
     data_lo();
     clock_hi();
-    WAIT(clock_lo, 15000, 1);
-    /* data [2-9] */
+    WAIT(clock_lo, 10000, 10);   // 10ms [5]p.50
+
+    /* Data bit[2-9] */
     for (uint8_t i = 0; i < 8; i++) {
         _delay_us(15);
         if (data&(1<<i)) {
@@ -128,15 +100,18 @@ uint8_t ps2_host_send(uint8_t data)
         WAIT(clock_hi, 50, 2);
         WAIT(clock_lo, 50, 3);
     }
-    /* parity [10] */
+
+    /* Parity bit */
     _delay_us(15);
     if (parity) { data_hi(); } else { data_lo(); }
     WAIT(clock_hi, 50, 4);
     WAIT(clock_lo, 50, 5);
-    /* stop bit [11] */
+
+    /* Stop bit */
     _delay_us(15);
     data_hi();
-    /* ack [12] */
+
+    /* Ack */
     WAIT(data_lo, 50, 6);
     WAIT(clock_lo, 50, 7);
 
@@ -144,127 +119,55 @@ uint8_t ps2_host_send(uint8_t data)
     WAIT(clock_hi, 50, 8);
     WAIT(data_hi, 50, 9);
 
+    idle();
     PS2_USART_INIT();
     PS2_USART_RX_INT_ON();
-    res = ps2_host_recv_response();
+    return ps2_host_recv_response();
 ERROR:
     idle();
     PS2_USART_INIT();
     PS2_USART_RX_INT_ON();
-    return res;
+    return 0;
 }
 
-// Do polling data from keyboard to get response to last command.
 uint8_t ps2_host_recv_response(void)
 {
-    while (!pbuf_has_data()) {
-        _delay_ms(1);   // without this delay it seems to fall into deadlock
+    // Command may take 25ms/20ms at most([5]p.46, [3]p.21)
+    uint8_t retry = 25;
+    while (retry-- && !pbuf_has_data()) {
+        _delay_ms(1);
     }
     return pbuf_dequeue();
 }
 
 uint8_t ps2_host_recv(void)
 {
-    return pbuf_dequeue();
+    if (pbuf_has_data()) {
+        ps2_error = PS2_ERR_NONE;
+        return pbuf_dequeue();
+    } else {
+        ps2_error = PS2_ERR_NODATA;
+        return 0;
+    }
 }
 
 ISR(PS2_USART_RX_VECT)
 {
-    uint8_t error = PS2_USART_ERROR;
+    // TODO: request RESEND when error occurs?
+    uint8_t error = PS2_USART_ERROR;    // USART error should be read before data
     uint8_t data = PS2_USART_RX_DATA;
     if (!error) {
         pbuf_enqueue(data);
+    } else {
+        xprintf("PS2 USART error: %02X data: %02X\n", error, data);
     }
 }
 
 /* send LED state to keyboard */
 void ps2_host_set_led(uint8_t led)
 {
-    // send 0xED then keyboard keeps waiting for next LED data
-    // and keyboard does not send any scan codes during waiting.
-    // If fail to send LED data keyboard looks like being freezed.
-    uint8_t retry = 3;
-    while (retry-- && ps2_host_send(PS2_SET_LED) != PS2_ACK)
-        ;
-    retry = 3;
-    while (retry-- && ps2_host_send(led) != PS2_ACK)
-        ;
-}
-
-
-/*--------------------------------------------------------------------
- * static functions
- *------------------------------------------------------------------*/
-static inline void clock_lo()
-{
-    PS2_CLOCK_PORT &= ~(1<<PS2_CLOCK_BIT);
-    PS2_CLOCK_DDR  |=  (1<<PS2_CLOCK_BIT);
-}
-static inline void clock_hi()
-{
-    /* input with pull up */
-    PS2_CLOCK_DDR  &= ~(1<<PS2_CLOCK_BIT);
-    PS2_CLOCK_PORT |=  (1<<PS2_CLOCK_BIT);
-}
-static inline bool clock_in()
-{
-    PS2_CLOCK_DDR  &= ~(1<<PS2_CLOCK_BIT);
-    PS2_CLOCK_PORT |=  (1<<PS2_CLOCK_BIT);
-    _delay_us(1);
-    return PS2_CLOCK_PIN&(1<<PS2_CLOCK_BIT);
-}
-static inline void data_lo()
-{
-    PS2_DATA_PORT &= ~(1<<PS2_DATA_BIT);
-    PS2_DATA_DDR  |=  (1<<PS2_DATA_BIT);
-}
-static inline void data_hi()
-{
-    /* input with pull up */
-    PS2_DATA_DDR  &= ~(1<<PS2_DATA_BIT);
-    PS2_DATA_PORT |=  (1<<PS2_DATA_BIT);
-}
-static inline bool data_in()
-{
-    PS2_DATA_DDR  &= ~(1<<PS2_DATA_BIT);
-    PS2_DATA_PORT |=  (1<<PS2_DATA_BIT);
-    _delay_us(1);
-    return PS2_DATA_PIN&(1<<PS2_DATA_BIT);
-}
-
-static inline uint16_t wait_clock_lo(uint16_t us)
-{
-    while (clock_in()  && us) { asm(""); _delay_us(1); us--; }
-    return us;
-}
-static inline uint16_t wait_clock_hi(uint16_t us)
-{
-    while (!clock_in() && us) { asm(""); _delay_us(1); us--; }
-    return us;
-}
-static inline uint16_t wait_data_lo(uint16_t us)
-{
-    while (data_in() && us)  { asm(""); _delay_us(1); us--; }
-    return us;
-}
-static inline uint16_t wait_data_hi(uint16_t us)
-{
-    while (!data_in() && us)  { asm(""); _delay_us(1); us--; }
-    return us;
-}
-
-/* idle state that device can send */
-static inline void idle(void)
-{
-    clock_hi();
-    data_hi();
-}
-
-/* inhibit device to send */
-static inline void inhibit(void)
-{
-    clock_lo();
-    data_hi();
+    ps2_host_send(0xED);
+    ps2_host_send(led);
 }
 
 
@@ -284,11 +187,10 @@ static inline void pbuf_enqueue(uint8_t data)
         pbuf[pbuf_head] = data;
         pbuf_head = next;
     } else {
-        debug("pbuf: full\n");
+        print("pbuf: full\n");
     }
     SREG = sreg;
 }
-
 static inline uint8_t pbuf_dequeue(void)
 {
     uint8_t val = 0;