qmk_firmware

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

protocol.c (3455B)


      1 /*
      2  * (c) 2021 by Tomasz bla Fortuna
      3  * License: GPLv2
      4  *
      5  * This file is shared with the Shine firmware. Keep it in sync (and in the
      6  * shine's clang formatting).
      7  *
      8  * Implementation of a robust serial protocol which can handle single dropped
      9  * characters during transit without locking.
     10  *
     11  * At 115200, transmitting the shortest message takes 0.043ms, at 9600 - 0.52ms.
     12  *
     13  */
     14 
     15 #include "protocol.h"
     16 #include "board.h"
     17 #include "ch.h"
     18 #include "hal.h"
     19 
     20 /* UART communication protocol state */
     21 protocol_t proto;
     22 
     23 void proto_init(protocol_t *proto, void (*callback)(const message_t *)) {
     24     proto->previous_id = 0;
     25     proto->callback   = callback;
     26     proto->state      = STATE_SYNC_1;
     27     proto->errors     = 0;
     28 }
     29 
     30 static uint8_t msg_id = 0;
     31 void           proto_tx(uint8_t cmd, const unsigned char *buf, int payload_size, int retries) {
     32     chDbgCheck(payload_size <= MAX_PAYLOAD_SIZE);
     33 
     34     const uint8_t header[5] = {
     35         0x7A, 0x1D, cmd, ++msg_id, payload_size,
     36     };
     37 
     38     /* We don't implement ACKs, yet some messages should not be lost. */
     39     for (int i = 0; i < retries; i++) {
     40         sdWrite(&PROTOCOL_SD, header, sizeof(header));
     41         if (payload_size) sdWrite(&PROTOCOL_SD, buf, payload_size);
     42     }
     43 }
     44 
     45 static inline void messageReceived(protocol_t *proto) {
     46     if (proto->buffer.msg_id != proto->previous_id) {
     47         /* It's not a resend / duplicate */
     48         proto->callback(&proto->buffer);
     49         proto->previous_id = proto->buffer.msg_id;
     50     }
     51     proto->state = STATE_SYNC_1;
     52 }
     53 
     54 void proto_consume(protocol_t *proto, uint8_t byte) {
     55     switch (proto->state) {
     56         case STATE_SYNC_1:
     57             if (byte == 0x7A) {
     58                 proto->state = STATE_SYNC_2;
     59             } else {
     60                 proto->errors++;
     61             }
     62             return;
     63 
     64         case STATE_SYNC_2:
     65             if (byte == 0x1D) {
     66                 proto->state = STATE_CMD;
     67             } else {
     68                 proto->state = STATE_SYNC_1;
     69                 proto->errors++;
     70             }
     71             return;
     72 
     73         case STATE_CMD:
     74             proto->buffer.command = byte;
     75             proto->state          = STATE_ID;
     76             return;
     77 
     78         case STATE_ID:
     79             proto->buffer.msg_id = byte;
     80             proto->state        = STATE_PAYLOAD_SIZE;
     81             return;
     82 
     83         case STATE_PAYLOAD_SIZE:
     84             proto->buffer.payload_size = byte;
     85             if (proto->buffer.payload_size > MAX_PAYLOAD_SIZE) {
     86                 proto->buffer.payload_size = MAX_PAYLOAD_SIZE;
     87                 proto->errors++;
     88             }
     89             proto->payload_position = 0;
     90             if (proto->buffer.payload_size == 0) {
     91                 /* No payload - whole message received */
     92                 messageReceived(proto);
     93             } else {
     94                 proto->state = STATE_PAYLOAD;
     95             }
     96             return;
     97 
     98         case STATE_PAYLOAD:
     99             /* NOTE: This could be read with sdReadTimeout probably, but that breaks
    100              * abstraction */
    101             proto->buffer.payload[proto->payload_position] = byte;
    102             proto->payload_position++;
    103             if (proto->payload_position == proto->buffer.payload_size) {
    104                 /* Payload read - message received */
    105                 messageReceived(proto);
    106             }
    107             return;
    108     }
    109 }
    110 
    111 void proto_silence(protocol_t *proto) {
    112     if (proto->state != STATE_SYNC_1) {
    113         proto->state = STATE_SYNC_1;
    114         proto->errors++;
    115     }
    116 }