summary refs log tree commit diff
path: root/protocol
diff options
context:
space:
mode:
authortmk <nobody@nowhere>2013-11-25 17:43:26 +0900
committertmk <nobody@nowhere>2013-11-25 17:43:26 +0900
commit9ae9742ac59b18cf989370f53d669daeb75bd7a3 (patch)
tree2a661eaef74bb7817ff5d03ff115ec00249e188a /protocol
parentd5ecbb83daa13b806ee4879692aaf807b6b1b69f (diff)
Add ps2_busywait.c and recfactor PS/2 protocol
Diffstat (limited to 'protocol')
-rw-r--r--protocol/ps2.c59
-rw-r--r--protocol/ps2.h144
-rw-r--r--protocol/ps2_busywait.c179
3 files changed, 339 insertions, 43 deletions
diff --git a/protocol/ps2.c b/protocol/ps2.c
index e5873a9bfa..4886b16d03 100644
--- a/protocol/ps2.c
+++ b/protocol/ps2.c
@@ -91,6 +91,9 @@ 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);
+
 #ifdef PS2_USE_INT
     PS2_INT_INIT();
     PS2_INT_ON();
@@ -163,32 +166,30 @@ ERROR:
 /* receive data when host want else inhibit communication */
 uint8_t ps2_host_recv_response(void)
 {
+    // Command might take 20ms to response([3]p.21)
+    // TrackPoint might take 25ms ([5]2.7)
     uint8_t data = 0;
-
-#ifdef PS2_USE_INT
-    PS2_INT_OFF();
+    uint8_t try = 200;
+    while (try-- && (data = ps2_host_recv())) ;
+    return data;
+}
 #endif
-    /* terminate a transmission if we have */
-    inhibit();
-    _delay_us(100);
+
+#ifndef PS2_USE_INT
+uint8_t ps2_host_recv(void)
+{
+    uint8_t data = 0;
 
     /* release lines(idle state) */
     idle();
 
     /* wait start bit */
-    wait_clock_lo(25000);    // command response may take 20 ms at most
+    wait_clock_lo(100); // TODO: this is enough?
     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
@@ -241,13 +242,6 @@ static inline void pbuf_clear(void)
 /* 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
-        ps2_error = PS2_ERR_NONE;
-    }
-    idle();
     return pbuf_dequeue();
 }
 
@@ -450,3 +444,26 @@ static inline void inhibit(void)
     clock_lo();
     data_hi();
 }
+
+
+/* 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
+ */
diff --git a/protocol/ps2.h b/protocol/ps2.h
index 834165356c..b780d01a29 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,44 @@ 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
+#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_LED_SCROLL_LOCK 0
 #define PS2_LED_NUM_LOCK    1
@@ -71,13 +83,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..1e29258895
--- /dev/null
+++ b/protocol/ps2_busywait.c
@@ -0,0 +1,179 @@
+/*
+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.
+*/
+
+#include <stdbool.h>
+#include <util/delay.h>
+#include "ps2.h"
+#include "debug.h"
+
+
+/*
+ * PS/2 protocol busywait version
+ */
+
+#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)
+{
+    uint8_t res = 0;
+    bool parity = true;
+    ps2_error = PS2_ERR_NONE;
+    /* terminate a transmission if we have */
+    inhibit();
+    _delay_us(200); // at least 100us
+
+    /* start bit [1] */
+    data_lo();
+    clock_hi();
+    WAIT(clock_lo, 20000, 10);   // may take 15ms at most until device starts clocking
+    /* data [2-9] */
+    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 [10] */
+    _delay_us(15);
+    if (parity) { data_hi(); } else { data_lo(); }
+    WAIT(clock_hi, 50, 4);
+    WAIT(clock_lo, 50, 5);
+    /* stop bit [11] */
+    _delay_us(15);
+    data_hi();
+    /* ack [12] */
+    WAIT(data_lo, 50, 6);
+    WAIT(clock_lo, 50, 7);
+
+    /* wait for idle state */
+    WAIT(clock_hi, 50, 8);
+    WAIT(data_hi, 50, 9);
+
+    res = ps2_host_recv_response();
+ERROR:
+    inhibit();
+    return res;
+}
+
+/* receive data when host want else inhibit communication */
+uint8_t ps2_host_recv_response(void)
+{
+    // TODO:
+    // Command might take 20ms to response([3]p.21)
+    // TrackPoint might take 25ms ([5]2.7)
+    uint8_t data = 0;
+    uint8_t try = 200;
+    while (try-- && (data = ps2_host_recv())) ;
+    return data;
+}
+
+/* send LED state to keyboard */
+void ps2_host_set_led(uint8_t led)
+{
+    ps2_host_send(0xED);
+    ps2_host_send(led);
+}
+
+
+/* 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) {
+        printf("x%02X\n", ps2_error);
+    }
+    inhibit();
+    return 0;
+}