logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

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. #include "protocol.h"
  15. #include "board.h"
  16. #include "ch.h"
  17. #include "hal.h"
  18. /* UART communication protocol state */
  19. protocol_t proto;
  20. void proto_init(protocol_t *proto, void (*callback)(const message_t *)) {
  21. proto->previous_id = 0;
  22. proto->callback = callback;
  23. proto->state = STATE_SYNC_1;
  24. proto->errors = 0;
  25. }
  26. static uint8_t msg_id = 0;
  27. void proto_tx(uint8_t cmd, const unsigned char *buf, int payload_size, int retries) {
  28. chDbgCheck(payload_size <= MAX_PAYLOAD_SIZE);
  29. const uint8_t header[5] = {
  30. 0x7A, 0x1D, cmd, ++msg_id, payload_size,
  31. };
  32. /* We don't implement ACKs, yet some messages should not be lost. */
  33. for (int i = 0; i < retries; i++) {
  34. sdWrite(&PROTOCOL_SD, header, sizeof(header));
  35. if (payload_size) sdWrite(&PROTOCOL_SD, buf, payload_size);
  36. }
  37. }
  38. static inline void messageReceived(protocol_t *proto) {
  39. if (proto->buffer.msg_id != proto->previous_id) {
  40. /* It's not a resend / duplicate */
  41. proto->callback(&proto->buffer);
  42. proto->previous_id = proto->buffer.msg_id;
  43. }
  44. proto->state = STATE_SYNC_1;
  45. }
  46. void proto_consume(protocol_t *proto, uint8_t byte) {
  47. switch (proto->state) {
  48. case STATE_SYNC_1:
  49. if (byte == 0x7A) {
  50. proto->state = STATE_SYNC_2;
  51. } else {
  52. proto->errors++;
  53. }
  54. return;
  55. case STATE_SYNC_2:
  56. if (byte == 0x1D) {
  57. proto->state = STATE_CMD;
  58. } else {
  59. proto->state = STATE_SYNC_1;
  60. proto->errors++;
  61. }
  62. return;
  63. case STATE_CMD:
  64. proto->buffer.command = byte;
  65. proto->state = STATE_ID;
  66. return;
  67. case STATE_ID:
  68. proto->buffer.msg_id = byte;
  69. proto->state = STATE_PAYLOAD_SIZE;
  70. return;
  71. case STATE_PAYLOAD_SIZE:
  72. proto->buffer.payload_size = byte;
  73. if (proto->buffer.payload_size > MAX_PAYLOAD_SIZE) {
  74. proto->buffer.payload_size = MAX_PAYLOAD_SIZE;
  75. proto->errors++;
  76. }
  77. proto->payload_position = 0;
  78. if (proto->buffer.payload_size == 0) {
  79. /* No payload - whole message received */
  80. messageReceived(proto);
  81. } else {
  82. proto->state = STATE_PAYLOAD;
  83. }
  84. return;
  85. case STATE_PAYLOAD:
  86. /* NOTE: This could be read with sdReadTimeout probably, but that breaks
  87. * abstraction */
  88. proto->buffer.payload[proto->payload_position] = byte;
  89. proto->payload_position++;
  90. if (proto->payload_position == proto->buffer.payload_size) {
  91. /* Payload read - message received */
  92. messageReceived(proto);
  93. }
  94. return;
  95. }
  96. }
  97. void proto_silence(protocol_t *proto) {
  98. if (proto->state != STATE_SYNC_1) {
  99. proto->state = STATE_SYNC_1;
  100. proto->errors++;
  101. }
  102. }