diff options
Diffstat (limited to 'protocol/ps2_usart.c')
-rw-r--r-- | protocol/ps2_usart.c | 224 |
1 files changed, 61 insertions, 163 deletions
diff --git a/protocol/ps2_usart.c b/protocol/ps2_usart.c index 9ea6b7786..c2d9d0a20 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; +} |