qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

ps2_interrupt.c (8511B)


      1 /*
      2 Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>
      3 
      4 This software is licensed with a Modified BSD License.
      5 All of this is supposed to be Free Software, Open Source, DFSG-free,
      6 GPL-compatible, and OK to use in both free and proprietary applications.
      7 Additions and corrections to this file are welcome.
      8 
      9 
     10 Redistribution and use in source and binary forms, with or without
     11 modification, are permitted provided that the following conditions are met:
     12 
     13 * Redistributions of source code must retain the above copyright
     14   notice, this list of conditions and the following disclaimer.
     15 
     16 * Redistributions in binary form must reproduce the above copyright
     17   notice, this list of conditions and the following disclaimer in
     18   the documentation and/or other materials provided with the
     19   distribution.
     20 
     21 * Neither the name of the copyright holders nor the names of
     22   contributors may be used to endorse or promote products derived
     23   from this software without specific prior written permission.
     24 
     25 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     26 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     27 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     28 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
     29 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     30 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     31 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     32 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     33 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     34 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     35 POSSIBILITY OF SUCH DAMAGE.
     36 */
     37 
     38 /*
     39  * PS/2 protocol Pin interrupt version
     40  */
     41 
     42 #include <stdbool.h>
     43 
     44 #if defined(__AVR__)
     45 #    include <avr/interrupt.h>
     46 #elif defined(PROTOCOL_CHIBIOS) // TODO: or STM32 ?
     47 // chibiOS headers
     48 #    include "ch.h"
     49 #    include "hal.h"
     50 #    include "gpio.h"
     51 #endif
     52 
     53 #include "ps2.h"
     54 #include "ps2_io.h"
     55 #include "print.h"
     56 #include "wait.h"
     57 
     58 #define WAIT(stat, us, err)     \
     59     do {                        \
     60         if (!wait_##stat(us)) { \
     61             ps2_error = err;    \
     62             goto ERROR;         \
     63         }                       \
     64     } while (0)
     65 
     66 uint8_t ps2_error = PS2_ERR_NONE;
     67 
     68 static inline uint8_t pbuf_dequeue(void);
     69 static inline void    pbuf_enqueue(uint8_t data);
     70 static inline void    pbuf_clear(void);
     71 bool                  pbuf_has_data(void);
     72 
     73 #if defined(PROTOCOL_CHIBIOS)
     74 void ps2_interrupt_service_routine(void);
     75 void palCallback(void *arg) {
     76     ps2_interrupt_service_routine();
     77 }
     78 
     79 #    define PS2_INT_INIT()                                 \
     80         do {                                               \
     81             palSetLineMode(PS2_CLOCK_PIN, PAL_MODE_INPUT); \
     82         } while (0)
     83 #    define PS2_INT_ON()                                                    \
     84         do {                                                                \
     85             palEnableLineEvent(PS2_CLOCK_PIN, PAL_EVENT_MODE_FALLING_EDGE); \
     86             palSetLineCallback(PS2_CLOCK_PIN, palCallback, NULL);           \
     87         } while (0)
     88 #    define PS2_INT_OFF()                       \
     89         do {                                    \
     90             palDisableLineEvent(PS2_CLOCK_PIN); \
     91         } while (0)
     92 #endif // PROTOCOL_CHIBIOS
     93 
     94 void ps2_host_init(void) {
     95     idle();
     96     PS2_INT_INIT();
     97     PS2_INT_ON();
     98     // POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
     99     // wait_ms(2500);
    100 }
    101 
    102 uint8_t ps2_host_send(uint8_t data) {
    103     bool parity = true;
    104     ps2_error   = PS2_ERR_NONE;
    105 
    106     PS2_INT_OFF();
    107 
    108     /* terminate a transmission if we have */
    109     inhibit();
    110     wait_us(100); // 100us [4]p.13, [5]p.50
    111 
    112     /* 'Request to Send' and Start bit */
    113     data_lo();
    114     clock_hi();
    115     WAIT(clock_lo, 10000, 10); // 10ms [5]p.50
    116 
    117     /* Data bit[2-9] */
    118     for (uint8_t i = 0; i < 8; i++) {
    119         if (data & (1 << i)) {
    120             parity = !parity;
    121             data_hi();
    122         } else {
    123             data_lo();
    124         }
    125         WAIT(clock_hi, 50, 2);
    126         WAIT(clock_lo, 50, 3);
    127     }
    128 
    129     /* Parity bit */
    130     wait_us(15);
    131     if (parity) {
    132         data_hi();
    133     } else {
    134         data_lo();
    135     }
    136     WAIT(clock_hi, 50, 4);
    137     WAIT(clock_lo, 50, 5);
    138 
    139     /* Stop bit */
    140     wait_us(15);
    141     data_hi();
    142 
    143     /* Ack */
    144     WAIT(data_lo, 50, 6);
    145     WAIT(clock_lo, 50, 7);
    146 
    147     /* wait for idle state */
    148     WAIT(clock_hi, 50, 8);
    149     WAIT(data_hi, 50, 9);
    150 
    151     idle();
    152     PS2_INT_ON();
    153     return ps2_host_recv_response();
    154 ERROR:
    155     idle();
    156     PS2_INT_ON();
    157     return 0;
    158 }
    159 
    160 uint8_t ps2_host_recv_response(void) {
    161     // Command may take 25ms/20ms at most([5]p.46, [3]p.21)
    162     uint8_t retry = 25;
    163     while (retry-- && !pbuf_has_data()) {
    164         wait_ms(1);
    165     }
    166     return pbuf_dequeue();
    167 }
    168 
    169 /* get data received by interrupt */
    170 uint8_t ps2_host_recv(void) {
    171     if (pbuf_has_data()) {
    172         ps2_error = PS2_ERR_NONE;
    173         return pbuf_dequeue();
    174     } else {
    175         ps2_error = PS2_ERR_NODATA;
    176         return 0;
    177     }
    178 }
    179 
    180 void ps2_interrupt_service_routine(void) {
    181     static enum {
    182         INIT,
    183         START,
    184         BIT0,
    185         BIT1,
    186         BIT2,
    187         BIT3,
    188         BIT4,
    189         BIT5,
    190         BIT6,
    191         BIT7,
    192         PARITY,
    193         STOP,
    194     } state               = INIT;
    195     static uint8_t data   = 0;
    196     static uint8_t parity = 1;
    197 
    198     // TODO: abort if elapse 100us from previous interrupt
    199 
    200     // return unless falling edge
    201     if (clock_in()) {
    202         goto RETURN;
    203     }
    204 
    205     state++;
    206     switch (state) {
    207         case START:
    208             if (data_in()) goto ERROR;
    209             break;
    210         case BIT0:
    211         case BIT1:
    212         case BIT2:
    213         case BIT3:
    214         case BIT4:
    215         case BIT5:
    216         case BIT6:
    217         case BIT7:
    218             data >>= 1;
    219             if (data_in()) {
    220                 data |= 0x80;
    221                 parity++;
    222             }
    223             break;
    224         case PARITY:
    225             if (data_in()) {
    226                 if (!(parity & 0x01)) goto ERROR;
    227             } else {
    228                 if (parity & 0x01) goto ERROR;
    229             }
    230             break;
    231         case STOP:
    232             if (!data_in()) goto ERROR;
    233             pbuf_enqueue(data);
    234             goto DONE;
    235             break;
    236         default:
    237             goto ERROR;
    238     }
    239     goto RETURN;
    240 ERROR:
    241     ps2_error = state;
    242 DONE:
    243     state  = INIT;
    244     data   = 0;
    245     parity = 1;
    246 RETURN:
    247     return;
    248 }
    249 
    250 #if defined(__AVR__)
    251 ISR(PS2_INT_VECT) {
    252     ps2_interrupt_service_routine();
    253 }
    254 #endif
    255 
    256 /* send LED state to keyboard */
    257 void ps2_host_set_led(uint8_t led) {
    258     ps2_host_send(0xED);
    259     ps2_host_send(led);
    260 }
    261 
    262 /*--------------------------------------------------------------------
    263  * Ring buffer to store scan codes from keyboard
    264  *------------------------------------------------------------------*/
    265 #define PBUF_SIZE 32
    266 static uint8_t     pbuf[PBUF_SIZE];
    267 static uint8_t     pbuf_head = 0;
    268 static uint8_t     pbuf_tail = 0;
    269 static inline void pbuf_enqueue(uint8_t data) {
    270 #if defined(__AVR__)
    271     uint8_t sreg = SREG;
    272     cli();
    273 #elif defined(PROTOCOL_CHIBIOS)
    274     chSysLockFromISR();
    275 #endif
    276 
    277     uint8_t next = (pbuf_head + 1) % PBUF_SIZE;
    278     if (next != pbuf_tail) {
    279         pbuf[pbuf_head] = data;
    280         pbuf_head       = next;
    281     } else {
    282         print("pbuf: full\n");
    283     }
    284 
    285 #if defined(__AVR__)
    286     SREG = sreg;
    287 #elif defined(PROTOCOL_CHIBIOS)
    288     chSysUnlockFromISR();
    289 #endif
    290 }
    291 static inline uint8_t pbuf_dequeue(void) {
    292     uint8_t val = 0;
    293 
    294 #if defined(__AVR__)
    295     uint8_t sreg = SREG;
    296     cli();
    297 #elif defined(PROTOCOL_CHIBIOS)
    298     chSysLock();
    299 #endif
    300 
    301     if (pbuf_head != pbuf_tail) {
    302         val       = pbuf[pbuf_tail];
    303         pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE;
    304     }
    305 
    306 #if defined(__AVR__)
    307     SREG = sreg;
    308 #elif defined(PROTOCOL_CHIBIOS)
    309     chSysUnlock();
    310 #endif
    311 
    312     return val;
    313 }
    314 bool pbuf_has_data(void) {
    315 #if defined(__AVR__)
    316     uint8_t sreg = SREG;
    317     cli();
    318 #elif defined(PROTOCOL_CHIBIOS)
    319     chSysLock();
    320 #endif
    321 
    322     bool has_data = (pbuf_head != pbuf_tail);
    323 
    324 #if defined(__AVR__)
    325     SREG = sreg;
    326 #elif defined(PROTOCOL_CHIBIOS)
    327     chSysUnlock();
    328 #endif
    329     return has_data;
    330 }
    331 static inline void pbuf_clear(void) {
    332 #if defined(__AVR__)
    333     uint8_t sreg = SREG;
    334     cli();
    335 #elif defined(PROTOCOL_CHIBIOS)
    336     chSysLock();
    337 #endif
    338 
    339     pbuf_head = pbuf_tail = 0;
    340 
    341 #if defined(__AVR__)
    342     SREG = sreg;
    343 #elif defined(PROTOCOL_CHIBIOS)
    344     chSysUnlock();
    345 #endif
    346 }