summaryrefslogtreecommitdiff
path: root/protocol
diff options
context:
space:
mode:
Diffstat (limited to 'protocol')
-rw-r--r--protocol/adb.c18
-rw-r--r--protocol/lufa/lufa.c13
-rw-r--r--protocol/pjrc.mk6
-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)321
-rw-r--r--protocol/ps2_mouse.c303
-rw-r--r--protocol/ps2_mouse.h40
-rw-r--r--protocol/ps2_usart.c224
10 files changed, 657 insertions, 602 deletions
diff --git a/protocol/adb.c b/protocol/adb.c
index 750f4b9650..a4783f36e5 100644
--- a/protocol/adb.c
+++ b/protocol/adb.c
@@ -41,7 +41,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include <avr/io.h>
#include <avr/interrupt.h>
#include "adb.h"
-#include "debug.h"
// GCC doesn't inline functions normally
@@ -125,16 +124,15 @@ bool adb_host_psw(void)
uint16_t adb_host_kbd_recv(void)
{
uint16_t data = 0;
+ cli();
attention();
send_byte(0x2C); // Addr:Keyboard(0010), Cmd:Talk(11), Register0(00)
place_bit0(); // Stopbit(0)
if (!wait_data_lo(500)) { // Tlt/Stop to Start(140-260us)
+ sei();
return 0; // No data to send
}
- // ad hoc fix: without block inerrupt read wrong bit occasionally and get keys stuck
- // TODO: is this needed anymore with improved timing?
- //cli();
uint8_t n = 17; // start bit + 16 data bits
do {
uint8_t lo = (uint8_t) wait_data_hi(130);
@@ -153,10 +151,8 @@ uint16_t adb_host_kbd_recv(void)
data |= 1;
}
else if (n == 17) {
- // Service Request
- dprintf("Startbit ERROR\n");
sei();
- return -2;
+ return -20;
}
}
while ( --n );
@@ -164,21 +160,20 @@ uint16_t adb_host_kbd_recv(void)
// Stop bit can't be checked normally since it could have service request lenghtening
// and its high state never goes low.
if (!wait_data_hi(351) || wait_data_lo(91)) {
- dprintf("Stopbit ERROR\n");
sei();
- return -3;
+ return -21;
}
sei();
return data;
error:
- dprintf("Bit ERROR\n");
sei();
- return -4;
+ return -n;
}
void adb_host_listen(uint8_t cmd, uint8_t data_h, uint8_t data_l)
{
+ cli();
attention();
send_byte(cmd);
place_bit0(); // Stopbit(0)
@@ -187,6 +182,7 @@ void adb_host_listen(uint8_t cmd, uint8_t data_h, uint8_t data_l)
send_byte(data_h);
send_byte(data_l);
place_bit0(); // Stopbit(0);
+ sei();
}
// send state of LEDs
diff --git a/protocol/lufa/lufa.c b/protocol/lufa/lufa.c
index a230d5ba20..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)
@@ -539,11 +539,18 @@ int main(void)
{
SetupHardware();
sei();
+
+ /* wait for USB startup & debug output */
+ while (USB_DeviceState != DEVICE_STATE_Configured) {
#if defined(INTERRUPT_CONTROL_ENDPOINT)
- while (USB_DeviceState != DEVICE_STATE_Configured) ;
+ ;
+#else
+ USB_USBTask();
#endif
+ }
print("USB configured.\n");
+ /* init modules */
keyboard_init();
host_set_driver(&lufa_driver);
#ifdef SLEEP_LED_ENABLE
diff --git a/protocol/pjrc.mk b/protocol/pjrc.mk
index 27f908b1c2..5a44613820 100644
--- a/protocol/pjrc.mk
+++ b/protocol/pjrc.mk
@@ -7,7 +7,11 @@ SRC += $(PJRC_DIR)/main.c \
$(PJRC_DIR)/usb.c
# Option modules
-ifdef $(or MOUSEKEY_ENABLE, PS2_MOUSE_ENABLE)
+ifdef MOUSEKEY_ENABLE
+ SRC += $(PJRC_DIR)/usb_mouse.c
+endif
+
+ifdef PS2_MOUSE_ENABLE
SRC += $(PJRC_DIR)/usb_mouse.c
endif
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 ed45609104..259d254007 100644
--- a/protocol/ps2.c
+++ b/protocol/ps2_interrupt.c
@@ -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,
@@ -35,45 +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"
-
-
-static uint8_t recv_data(void);
-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 { \
@@ -87,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(100);
+ _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, 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)) {
@@ -127,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);
@@ -143,99 +119,37 @@ uint8_t ps2_host_send(uint8_t data)
WAIT(clock_hi, 50, 8);
WAIT(data_hi, 50, 9);
- res = ps2_host_recv_response();
-ERROR:
-#ifdef PS2_USE_INT
+ idle();
PS2_INT_ON();
+ return ps2_host_recv_response();
+ERROR:
idle();
-#else
- inhibit();
-#endif
- return res;
+ PS2_INT_ON();
+ return 0;
}
-/* receive data when host want else inhibit communication */
uint8_t ps2_host_recv_response(void)
{
- uint8_t data = 0;
-
- /* terminate a transmission if we have */
- inhibit();
- _delay_us(100);
-
- /* release lines(idle state) */
- idle();
-
- /* wait start bit */
- wait_clock_lo(2000);
- data = recv_data();
-
- inhibit();
- return data;
-}
-
-#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)
-{
- if (!data)
- return;
-
- 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;
+ 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();
}
-#if 0
-#define DEBUGP_INIT() do { DDRC = 0xFF; } while (0)
-#define DEBUGP(x) do { PORTC = x; } while (0)
-#else
-#define DEBUGP_INIT()
-#define DEBUGP(x)
-#endif
ISR(PS2_INT_VECT)
{
static enum {
@@ -256,7 +170,6 @@ ISR(PS2_INT_VECT)
}
state++;
- DEBUGP(state);
switch (state) {
case START:
if (data_in())
@@ -296,8 +209,6 @@ ISR(PS2_INT_VECT)
}
goto RETURN;
ERROR:
- DEBUGP(0x0F);
- inhibit();
ps2_error = state;
DONE:
state = INIT;
@@ -306,13 +217,6 @@ DONE:
RETURN:
return;
}
-#endif
-
-
-static void ps2_reset(void)
-{
- ps2_host_send(0xFF);
-}
/* send LED state to keyboard */
void ps2_host_set_led(uint8_t led)
@@ -322,114 +226,53 @@ void ps2_host_set_led(uint8_t led)
}
-/* 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;
-}
-
-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_mouse.c b/protocol/ps2_mouse.c
index f796b2b4de..4702f12c40 100644
--- a/protocol/ps2_mouse.c
+++ b/protocol/ps2_mouse.c
@@ -1,5 +1,5 @@
/*
-Copyright 2011 Jun Wako <wakojun@gmail.com>
+Copyright 2011,2013 Jun Wako <wakojun@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,199 +20,196 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include<util/delay.h>
#include "ps2.h"
#include "ps2_mouse.h"
-#include "usb_mouse.h"
+#include "report.h"
+#include "host.h"
+#include "timer.h"
+#include "print.h"
+#include "debug.h"
-#define PS2_MOUSE_DEBUG
-#ifdef PS2_MOUSE_DEBUG
-# include "print.h"
-# include "debug.h"
-#else
-# define print(s)
-# define phex(h)
-# define phex16(h)
-#endif
-// disable when errors occur 255 times.
-#define ERROR_RETURN() do { \
- if (ps2_error) { \
- if (ps2_mouse_error_count < 255) { \
- ps2_mouse_error_count++; \
- } else { \
- ps2_mouse_error_count = 0; \
- ps2_mouse_enable = false; \
- } \
- return ps2_error; \
- } \
-} while (0)
+static report_mouse_t mouse_report = {};
-/*
-TODO
-----
-- Stream mode
-- Tracpoint command support: needed
-- Middle button + move = Wheel traslation
-*/
-bool ps2_mouse_enable = true;
-uint8_t ps2_mouse_x = 0;
-uint8_t ps2_mouse_y = 0;
-uint8_t ps2_mouse_btn = 0;
-uint8_t ps2_mouse_error_count = 0;
-
-static uint8_t ps2_mouse_btn_prev = 0;
+static void print_usb_data(void);
+/* supports only 3 button mouse at this time */
uint8_t ps2_mouse_init(void) {
uint8_t rcv;
- if (!ps2_mouse_enable) return 1;
-
ps2_host_init();
- // Reset
- rcv = ps2_host_send(0xFF);
- print("ps2_mouse_init: send 0xFF: ");
- phex(ps2_error); print("\n");
- ERROR_RETURN();
+ _delay_ms(1000); // wait for powering up
- // ACK
- rcv = ps2_host_recv();
- print("ps2_mouse_init: read ACK: ");
+ // send Reset
+ rcv = ps2_host_send(0xFF);
+ print("ps2_mouse_init: send Reset: ");
phex(rcv); phex(ps2_error); print("\n");
- ERROR_RETURN();
- // BAT takes some time
- _delay_ms(100);
- rcv = ps2_host_recv();
+ // read completion code of BAT
+ rcv = ps2_host_recv_response();
print("ps2_mouse_init: read BAT: ");
phex(rcv); phex(ps2_error); print("\n");
- ERROR_RETURN();
- // Device ID
- rcv = ps2_host_recv();
+ // read Device ID
+ rcv = ps2_host_recv_response();
print("ps2_mouse_init: read DevID: ");
phex(rcv); phex(ps2_error); print("\n");
- ERROR_RETURN();
- // Enable data reporting
- ps2_host_send(0xF4);
- print("ps2_mouse_init: send 0xF4: ");
- phex(ps2_error); print("\n");
- ERROR_RETURN();
-
- // ACK
- rcv = ps2_host_recv();
- print("ps2_mouse_init: read ACK: ");
- phex(rcv); phex(ps2_error); print("\n");
- ERROR_RETURN();
-
- // Set Remote mode
- ps2_host_send(0xF0);
+ // send Set Remote mode
+ rcv = ps2_host_send(0xF0);
print("ps2_mouse_init: send 0xF0: ");
- phex(ps2_error); print("\n");
- ERROR_RETURN();
-
- // ACK
- rcv = ps2_host_recv();
- print("ps2_mouse_init: read ACK: ");
phex(rcv); phex(ps2_error); print("\n");
- ERROR_RETURN();
return 0;
}
-/*
-Data format:
- bit: 7 6 5 4 3 2 1 0
------------------------------------------------------------------------
-0 btn: Yovflw Xovflw Ysign Xsign 1 Middle Right Left
-1 x: X movement(0-255)
-2 y: Y movement(0-255)
-*/
-uint8_t ps2_mouse_read(void)
+#define X_IS_NEG (mouse_report.buttons & (1<<PS2_MOUSE_X_SIGN))
+#define Y_IS_NEG (mouse_report.buttons & (1<<PS2_MOUSE_Y_SIGN))
+#define X_IS_OVF (mouse_report.buttons & (1<<PS2_MOUSE_X_OVFLW))
+#define Y_IS_OVF (mouse_report.buttons & (1<<PS2_MOUSE_Y_OVFLW))
+void ps2_mouse_task(void)
{
+ enum { SCROLL_NONE, SCROLL_BTN, SCROLL_SENT };
+ static uint8_t scroll_state = SCROLL_NONE;
+ static uint8_t buttons_prev = 0;
+
+ /* receives packet from mouse */
uint8_t rcv;
+ rcv = ps2_host_send(PS2_MOUSE_READ_DATA);
+ if (rcv == PS2_ACK) {
+ mouse_report.buttons = ps2_host_recv_response();
+ mouse_report.x = ps2_host_recv_response();
+ mouse_report.y = ps2_host_recv_response();
+ } else {
+ if (!debug_mouse) print("ps2_mouse: fail to get mouse packet\n");
+ return;
+ }
- if (!ps2_mouse_enable) return 1;
+ /* if mouse moves or buttons state changes */
+ if (mouse_report.x || mouse_report.y ||
+ ((mouse_report.buttons ^ buttons_prev) & PS2_MOUSE_BTN_MASK)) {
- ps2_host_send(0xEB);
- ERROR_RETURN();
+#ifdef PS2_MOUSE_DEBUG
+ print("ps2_mouse raw: [");
+ phex(mouse_report.buttons); print("|");
+ print_hex8((uint8_t)mouse_report.x); print(" ");
+ print_hex8((uint8_t)mouse_report.y); print("]\n");
+#endif
- rcv=ps2_host_recv();
- ERROR_RETURN();
+ buttons_prev = mouse_report.buttons;
+
+ // PS/2 mouse data is '9-bit integer'(-256 to 255) which is comprised of sign-bit and 8-bit value.
+ // bit: 8 7 ... 0
+ // sign \8-bit/
+ //
+ // Meanwhile USB HID mouse indicates 8bit data(-127 to 127), note that -128 is not used.
+ //
+ // This converts PS/2 data into HID value. Use only -127-127 out of PS/2 9-bit.
+ mouse_report.x = X_IS_NEG ?
+ ((!X_IS_OVF && -127 <= mouse_report.x && mouse_report.x <= -1) ? mouse_report.x : -127) :
+ ((!X_IS_OVF && 0 <= mouse_report.x && mouse_report.x <= 127) ? mouse_report.x : 127);
+ mouse_report.y = Y_IS_NEG ?
+ ((!Y_IS_OVF && -127 <= mouse_report.y && mouse_report.y <= -1) ? mouse_report.y : -127) :
+ ((!Y_IS_OVF && 0 <= mouse_report.y && mouse_report.y <= 127) ? mouse_report.y : 127);
+
+ // remove sign and overflow flags
+ mouse_report.buttons &= PS2_MOUSE_BTN_MASK;
+
+ // invert coordinate of y to conform to USB HID mouse
+ mouse_report.y = -mouse_report.y;
+
+
+#if PS2_MOUSE_SCROLL_BTN_MASK
+ static uint16_t scroll_button_time = 0;
+ if ((mouse_report.buttons & (PS2_MOUSE_SCROLL_BTN_MASK)) == (PS2_MOUSE_SCROLL_BTN_MASK)) {
+ if (scroll_state == SCROLL_NONE) {
+ scroll_button_time = timer_read();
+ scroll_state = SCROLL_BTN;
+ }
- if(rcv==0xFA) {
- ps2_mouse_btn = ps2_host_recv();
- ERROR_RETURN();
- ps2_mouse_x = ps2_host_recv();
- ERROR_RETURN();
- ps2_mouse_y = ps2_host_recv();
- ERROR_RETURN();
- }
- return 0;
-}
+ // doesn't send Scroll Button
+ //mouse_report.buttons &= ~(PS2_MOUSE_SCROLL_BTN_MASK);
-bool ps2_mouse_changed(void)
-{
- return (ps2_mouse_x || ps2_mouse_y || (ps2_mouse_btn & PS2_MOUSE_BTN_MASK) != ps2_mouse_btn_prev);
-}
+ if (mouse_report.x || mouse_report.y) {
+ scroll_state = SCROLL_SENT;
-#define PS2_MOUSE_SCROLL_BUTTON 0x04
-void ps2_mouse_usb_send(void)
-{
- static bool scrolled = false;
-
- if (!ps2_mouse_enable) return;
-
- if (ps2_mouse_changed()) {
- int8_t x, y, v, h;
- x = y = v = h = 0;
-
- // convert scale of X, Y: PS/2(-256/255) -> USB(-127/127)
- if (ps2_mouse_btn & (1<<PS2_MOUSE_X_SIGN))
- x = ps2_mouse_x > 128 ? (int8_t)ps2_mouse_x : -127;
- else
- x = ps2_mouse_x < 128 ? (int8_t)ps2_mouse_x : 127;
-
- if (ps2_mouse_btn & (1<<PS2_MOUSE_Y_SIGN))
- y = ps2_mouse_y > 128 ? (int8_t)ps2_mouse_y : -127;
- else
- y = ps2_mouse_y < 128 ? (int8_t)ps2_mouse_y : 127;
-
- // Y is needed to reverse
- y = -y;
-
- if (ps2_mouse_btn & PS2_MOUSE_SCROLL_BUTTON) {
- // scroll
- if (x > 0 || x < 0) h = (x > 64 ? 64 : (x < -64 ? -64 :x));
- if (y > 0 || y < 0) v = (y > 64 ? 64 : (y < -64 ? -64 :y));
- if (h || v) {
- scrolled = true;
- usb_mouse_send(0,0, -v/16, h/16, 0);
+ mouse_report.v = -mouse_report.y/(PS2_MOUSE_SCROLL_DIVISOR_V);
+ mouse_report.h = mouse_report.x/(PS2_MOUSE_SCROLL_DIVISOR_H);
+ mouse_report.x = 0;
+ mouse_report.y = 0;
+ //host_mouse_send(&mouse_report);
+ }
+ }
+ else if ((mouse_report.buttons & (PS2_MOUSE_SCROLL_BTN_MASK)) == 0) {
+#if PS2_MOUSE_SCROLL_BTN_SEND
+ if (scroll_state == SCROLL_BTN &&
+ TIMER_DIFF_16(timer_read(), scroll_button_time) < PS2_MOUSE_SCROLL_BTN_SEND) {
+ // send Scroll Button(down and up at once) when not scrolled
+ mouse_report.buttons |= (PS2_MOUSE_SCROLL_BTN_MASK);
+ host_mouse_send(&mouse_report);
_delay_ms(100);
+ mouse_report.buttons &= ~(PS2_MOUSE_SCROLL_BTN_MASK);
}
- } else if (!scrolled && (ps2_mouse_btn_prev & PS2_MOUSE_SCROLL_BUTTON)) {
- usb_mouse_send(0,0,0,0, PS2_MOUSE_SCROLL_BUTTON);
- _delay_ms(100);
- usb_mouse_send(0,0,0,0, 0);
- } else {
- scrolled = false;
- usb_mouse_send(x, y, 0, 0, ps2_mouse_btn & PS2_MOUSE_BTN_MASK);
+#endif
+ scroll_state = SCROLL_NONE;
}
+ // doesn't send Scroll Button
+ mouse_report.buttons &= ~(PS2_MOUSE_SCROLL_BTN_MASK);
+#endif
+
- ps2_mouse_btn_prev = (ps2_mouse_btn & PS2_MOUSE_BTN_MASK);
- ps2_mouse_print();
+ host_mouse_send(&mouse_report);
+ print_usb_data();
}
- ps2_mouse_x = 0;
- ps2_mouse_y = 0;
- ps2_mouse_btn = 0;
+ // clear report
+ mouse_report.x = 0;
+ mouse_report.y = 0;
+ mouse_report.v = 0;
+ mouse_report.h = 0;
+ mouse_report.buttons = 0;
}
-void ps2_mouse_print(void)
+static void print_usb_data(void)
{
if (!debug_mouse) return;
- print("ps2_mouse[btn|x y]: ");
- phex(ps2_mouse_btn); print("|");
- phex(ps2_mouse_x); print(" ");
- phex(ps2_mouse_y); print("\n");
+ print("ps2_mouse usb: [");
+ phex(mouse_report.buttons); print("|");
+ print_hex8((uint8_t)mouse_report.x); print(" ");
+ print_hex8((uint8_t)mouse_report.y); print(" ");
+ print_hex8((uint8_t)mouse_report.v); print(" ");
+ print_hex8((uint8_t)mouse_report.h); print("]\n");
}
+
+
+/* PS/2 Mouse Synopsis
+ * http://www.computer-engineering.org/ps2mouse/
+ *
+ * Command:
+ * 0xFF: Reset
+ * 0xF6: Set Defaults Sampling; rate=100, resolution=4cnt/mm, scaling=1:1, reporting=disabled
+ * 0xF5: Disable Data Reporting
+ * 0xF4: Enable Data Reporting
+ * 0xF3: Set Sample Rate
+ * 0xF2: Get Device ID
+ * 0xF0: Set Remote Mode
+ * 0xEB: Read Data
+ * 0xEA: Set Stream Mode
+ * 0xE9: Status Request
+ * 0xE8: Set Resolution
+ * 0xE7: Set Scaling 2:1
+ * 0xE6: Set Scaling 1:1
+ *
+ * Mode:
+ * Stream Mode: devices sends the data when it changs its state
+ * Remote Mode: host polls the data periodically
+ *
+ * This code uses Remote Mode and polls the data with Read Data(0xEB).
+ *
+ * Data format:
+ * byte|7 6 5 4 3 2 1 0
+ * ----+--------------------------------------------------------------
+ * 0|Yovflw Xovflw Ysign Xsign 1 Middle Right Left
+ * 1| X movement
+ * 2| Y movement
+ */
diff --git a/protocol/ps2_mouse.h b/protocol/ps2_mouse.h
index 4529ce1132..27d9790d43 100644
--- a/protocol/ps2_mouse.h
+++ b/protocol/ps2_mouse.h
@@ -20,6 +20,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <stdbool.h>
+#define PS2_MOUSE_READ_DATA 0xEB
+
+/*
+ * Data format:
+ * byte|7 6 5 4 3 2 1 0
+ * ----+--------------------------------------------------------------
+ * 0|Yovflw Xovflw Ysign Xsign 1 Middle Right Left
+ * 1| X movement(0-255)
+ * 2| Y movement(0-255)
+ */
#define PS2_MOUSE_BTN_MASK 0x07
#define PS2_MOUSE_BTN_LEFT 0
#define PS2_MOUSE_BTN_RIGHT 1
@@ -29,16 +39,28 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define PS2_MOUSE_X_OVFLW 6
#define PS2_MOUSE_Y_OVFLW 7
-bool ps2_mouse_enable;
-extern uint8_t ps2_mouse_x;
-extern uint8_t ps2_mouse_y;
-extern uint8_t ps2_mouse_btn;
-extern uint8_t ps2_mouse_error_count;
+
+/*
+ * Scroll by mouse move with pressing button
+ */
+/* mouse button to start scrolling; set 0 to disable scroll */
+#ifndef PS2_MOUSE_SCROLL_BTN_MASK
+#define PS2_MOUSE_SCROLL_BTN_MASK (1<<PS2_MOUSE_BTN_MIDDLE)
+#endif
+/* send button event when button is released within this value(ms); set 0 to disable */
+#ifndef PS2_MOUSE_SCROLL_BTN_SEND
+#define PS2_MOUSE_SCROLL_BTN_SEND 300
+#endif
+/* divide virtical and horizontal mouse move by this to convert to scroll move */
+#ifndef PS2_MOUSE_SCROLL_DIVISOR_V
+#define PS2_MOUSE_SCROLL_DIVISOR_V 2
+#endif
+#ifndef PS2_MOUSE_SCROLL_DIVISOR_H
+#define PS2_MOUSE_SCROLL_DIVISOR_H 2
+#endif
+
uint8_t ps2_mouse_init(void);
-uint8_t ps2_mouse_read(void);
-bool ps2_mouse_changed(void);
-void ps2_mouse_usb_send(void);
-void ps2_mouse_print(void);
+void ps2_mouse_task(void);
#endif
diff --git a/protocol/ps2_usart.c b/protocol/ps2_usart.c
index 9ea6b77868..c2d9d0a208 100644
--- a/protocol/ps2_usart.c
+++ b/protocol/ps2_usart.c
@@ -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,
@@ -36,41 +36,15 @@ 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"
-#if 0
-#define DEBUGP_INIT() do { DDRC = 0xFF; } while (0)
-#define DEBUGP(x) do { PORTC = x; } while (0)
-#else
-#define DEBUGP_INIT()
-#define DEBUGP(x)
-#endif
#define WAIT(stat, us, err) do { \
if (!wait_##stat(us)) { \
@@ -83,49 +57,38 @@ 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);
+static inline void pbuf_clear(void);
void ps2_host_init(void)
{
- DEBUGP_INIT();
- DEBUGP(0x1);
- 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;
- DEBUGP(0x6);
PS2_USART_OFF();
/* 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)) {
@@ -137,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);
@@ -153,134 +119,55 @@ uint8_t ps2_host_send(uint8_t data)
WAIT(clock_hi, 50, 8);
WAIT(data_hi, 50, 9);
- res = ps2_host_recv_response();
+ idle();
+ PS2_USART_INIT();
+ PS2_USART_RX_INT_ON();
+ 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)
{
- uint8_t data = 0;
- PS2_USART_INIT();
- PS2_USART_RX_POLL_ON();
- while (!PS2_USART_RX_READY)
- ;
- data = PS2_USART_RX_DATA;
- PS2_USART_OFF();
- DEBUGP(0x9);
- return data;
+ // 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)
{
- DEBUGP(0x7);
- 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) {
- DEBUGP(error>>2);
- } else {
+ if (!error) {
pbuf_enqueue(data);
+ } else {
+ xprintf("PS2 USART error: %02X data: %02X\n", error, data);
}
- DEBUGP(0x8);
}
/* 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);
}
@@ -293,9 +180,6 @@ static uint8_t pbuf_head = 0;
static uint8_t pbuf_tail = 0;
static inline void pbuf_enqueue(uint8_t data)
{
- if (!data)
- return;
-
uint8_t sreg = SREG;
cli();
uint8_t next = (pbuf_head + 1) % PBUF_SIZE;
@@ -303,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;
@@ -322,3 +205,18 @@ static inline uint8_t pbuf_dequeue(void)
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;
+}