diff options
Diffstat (limited to 'protocol')
| -rw-r--r-- | protocol/lufa/lufa.c | 4 | ||||
| -rw-r--r-- | protocol/pjrc/usb.c | 3 | ||||
| -rw-r--r-- | protocol/ps2.h | 146 | ||||
| -rw-r--r-- | protocol/ps2_busywait.c | 185 | ||||
| -rw-r--r-- | protocol/ps2_interrupt.c (renamed from protocol/ps2.c) | 334 | ||||
| -rw-r--r-- | protocol/ps2_usart.c | 174 | 
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; | 
