aboutsummaryrefslogtreecommitdiff
path: root/testing/yup-comm/protocol
diff options
context:
space:
mode:
authorJan Sucan <sucanjan@fit.cvut.cz>2019-06-04 14:34:27 +0200
committerJan Sucan <sucanjan@fit.cvut.cz>2019-06-04 14:34:27 +0200
commitdc8703206e3f0f69605c56d0e1127f7e17f3476a (patch)
tree166823a741dc420c10d54250cb53d1e3a6b74faf /testing/yup-comm/protocol
Initial commit
Diffstat (limited to 'testing/yup-comm/protocol')
-rw-r--r--testing/yup-comm/protocol/data.c312
-rw-r--r--testing/yup-comm/protocol/data.h16
-rw-r--r--testing/yup-comm/protocol/frame.c378
-rw-r--r--testing/yup-comm/protocol/frame.h56
-rw-r--r--testing/yup-comm/protocol/return_codes.h38
5 files changed, 800 insertions, 0 deletions
diff --git a/testing/yup-comm/protocol/data.c b/testing/yup-comm/protocol/data.c
new file mode 100644
index 0000000..c93ef6d
--- /dev/null
+++ b/testing/yup-comm/protocol/data.c
@@ -0,0 +1,312 @@
+/* Author: Jan Sucan */
+
+#include "data.h"
+#include "frame.h"
+
+#include <stdbool.h>
+
+#define DATA_PROTOCOL_VERSION 0x0
+
+#define DATA_ACK_RETRY_COUNT 4
+
+
+yup_retcode_t data_is_data_frame (const frame_t * const frame);
+yup_retcode_t data_is_starting_frame (const frame_t * const frame);
+yup_retcode_t data_is_ack_for_frame (const frame_t * const ack, const frame_t * const frame);
+void data_send_ack (uint16_t relation_id, uint16_t seq_number);
+void data_send_rej_for_frame(const frame_t * const frame, uint8_t err_code);
+
+
+yup_retcode_t
+data_is_data_frame(const frame_t * const frame)
+{
+ yup_retcode_t yr = DATA_OK;
+
+ if (frame_get_data_length(frame) == 0) {
+ yr = DATA_DATA_HAS_NO_PAYLOAD;
+ } else if (frame_is_flag_ack_set(frame)) {
+ yr = DATA_DATA_HAS_REJ_FLAG;
+ } else if (frame_is_flag_rej_set(frame)) {
+ yr = DATA_DATA_HAS_REJ_FLAG;
+ }
+
+ return yr;
+}
+
+yup_retcode_t
+data_is_starting_frame(const frame_t * const frame)
+{
+ yup_retcode_t yr = DATA_OK;
+
+ if ((yr = data_is_data_frame(frame)) != DATA_OK) {
+ ;
+ } else if (frame_get_seq_number(frame) != 0) {
+ yr = DATA_STARTING_HAS_NONZERO_SEQ_NUMBER;
+ }
+
+ return yr;
+}
+
+yup_retcode_t
+data_is_ack_for_frame(const frame_t * const ack, const frame_t * const frame)
+{
+ yup_retcode_t yr = DATA_OK;
+
+ if (frame_get_data_length(ack) != 0) {
+ yr = DATA_ACK_HAS_PAYLOAD;
+ } else if (!frame_is_flag_ack_set(ack)) {
+ yr = DATA_ACK_HAS_NOT_ACK_FLAG;
+ } else if (frame_is_flag_lor_set(ack)) {
+ yr = DATA_ACK_HAS_LOR_FLAG;
+ } else if (frame_is_flag_rej_set(ack)) {
+ yr = DATA_ACK_HAS_REJ_FLAG;
+ } else if (frame_get_relation_id(ack) != frame_get_relation_id(frame)) {
+ yr = DATA_ACK_RELATION_ID_MISMATCH;
+ } else if (frame_get_seq_number(ack) != frame_get_seq_number(frame)) {
+ yr = DATA_ACK_SEQ_NUMBER_MISMATCH;
+ }
+
+ return yr;
+}
+
+void
+data_send_ack(uint16_t relation_id, uint16_t seq_number)
+{
+ frame_t ack;
+
+ frame_init(&ack, DATA_PROTOCOL_VERSION);
+ frame_set_flag_ack(&ack);
+ frame_set_relation_id(&ack, relation_id);
+ frame_set_seq_number(&ack, seq_number);
+
+ frame_send(&ack);
+}
+
+void
+data_send_rej_for_frame(const frame_t * const frame, uint8_t err_code)
+{
+ frame_t rej;
+
+ frame_init(&rej, frame_get_protocol_version(frame));
+ frame_set_flag_rej(&rej);
+ frame_set_relation_id(&rej, frame_get_relation_id(frame));
+ frame_set_seq_number(&rej, frame_get_seq_number(frame));
+ frame_set_data(&rej, &err_code, sizeof(err_code));
+
+ frame_send(&rej);
+}
+
+
+typedef enum data_receive_automaton_states {
+ DATA_R_WAITING_FOR_STARTING_FRAME,
+ DATA_R_START_RELATION,
+ DATA_R_SAVE,
+ DATA_R_ACK_LAST_FRAME,
+ DATA_R_WAITING_NEXT_DATA_FRAME,
+ DATA_R_END
+} data_receive_automaton_state_t;
+
+typedef enum data_send_automaton_states {
+ DATA_S_START_RELATION,
+ DATA_S_PEPARE_DATA_FRAME,
+ DATA_S_SEND_DATA_FRAME,
+ DATA_S_WAIT_ACK,
+ DATA_S_END
+} data_send_automaton_state_t;
+
+yup_retcode_t
+data_receive(uint8_t * const buf, size_t bufsize, size_t * const bytes_read)
+{
+ uint16_t relation_id = 0;
+ uint16_t sequence_num = 0;
+ size_t data_index = 0;
+ data_receive_automaton_state_t state = DATA_R_WAITING_FOR_STARTING_FRAME;
+
+ // Kontrola argumentov
+ if ((buf == NULL) || (bufsize == 0)) {
+ return DATA_ERROR;
+ }
+
+ frame_t f;
+
+ while (state != DATA_R_END) {
+
+ yup_retcode_t r;
+ uint8_t l;
+
+ switch (state) {
+ case DATA_R_WAITING_FOR_STARTING_FRAME:
+ // Na prijem startovacieho ramca sa bude cakat donekonecna
+ r = frame_receive(&f, DATA_PROTOCOL_VERSION);
+ if (r != FRAME_OK) {
+ // Chyba pri prijme ramca, posle sa naspat REJ so spravou o chybe
+ data_send_rej_for_frame(&f, r);
+ } else if ((r = data_is_starting_frame(&f)) == DATA_OK) {
+ // Mame platny startovaci ramec, zahajime relaciu
+ state = DATA_R_START_RELATION;
+ }
+ break;
+
+ case DATA_R_START_RELATION:
+ // Od startovacieho ramca zacne plynut cas pre timeout relacie
+ relation_id = frame_get_relation_id(&f);
+ sequence_num = frame_get_seq_number(&f);
+ data_index = 0;
+ state = DATA_R_SAVE;
+ break;
+
+ case DATA_R_SAVE:
+ if ((bufsize - data_index) < frame_get_data_length(&f)) {
+ // Data sa nezmestia do bufferu
+ // Pocka sa na dalsi paket, co sa bude hodit
+ state = DATA_R_WAITING_NEXT_DATA_FRAME;
+ } else {
+ // Data sa ulozia
+ frame_get_data(&f, buf + data_index, &l);
+ // Ulozia sa informacie o naposledy prijatom ramci
+ sequence_num = frame_get_seq_number(&f);
+ data_index += frame_get_data_length(&f);
+ // Ramec sa potvrdi
+ state = DATA_R_ACK_LAST_FRAME;
+ }
+ break;
+
+ case DATA_R_ACK_LAST_FRAME:
+ // Potvrdime naposledy prijaty ramec
+ data_send_ack(relation_id, sequence_num);
+ // Na posledny ramec relacie sa uz neocakava odpoved
+ // Je bezpecne pristupovat k ramcu f, ak ma nastaveny LOR nemoze byt prepisany dalsim ramcom
+ if (frame_is_flag_lor_set(&f)) {
+ state = DATA_R_END;
+ } else {
+ // Pockame na prijem dalsieho datoveho ramca
+ state = DATA_R_WAITING_NEXT_DATA_FRAME;
+ }
+ break;
+
+ case DATA_R_WAITING_NEXT_DATA_FRAME:
+ r = frame_receive(&f, DATA_PROTOCOL_VERSION);
+ if (r == FRAME_TIMEOUT) {
+ // Pri timeoute prijmu prijmu sa nebude opakovane posielat ACK
+ // Timeout relacie vyprsal
+ state = DATA_R_END;
+ } else if (r != FRAME_OK) {
+ // Chyba pri prijme datoveho ramca
+ data_send_rej_for_frame(&f, r);
+ } else if ((r = data_is_data_frame(&f)) != DATA_OK) {
+ // Datovy ramec prijaty v poriadku, ale s nespavnym obsahom
+ data_send_rej_for_frame(&f, r);
+ } else if ((frame_get_seq_number(&f) == 0) && (frame_get_relation_id(&f) != relation_id)) {
+ // Jedna sa o zaciatok novej relacie
+ state = DATA_R_START_RELATION;
+ } else if (frame_get_relation_id(&f) != relation_id) {
+ // Obycajny datovy ramec, ale v odlisnej relacii
+ data_send_rej_for_frame(&f, (uint8_t) DATA_DATA_RELATION_ID_MISMATCH);
+ } else if (frame_get_seq_number(&f) != (sequence_num + 1)) {
+ // Obycajny datovy ramec v spravnej relacii, ale nenavazujuci do sekvencie
+ data_send_rej_for_frame(&f, (uint8_t) DATA_DATA_SEQ_NUMBER_MISMATCH);
+ } else {
+ // Spravny navazujuci datovy ramac, ulozia sa jeho data
+ state = DATA_R_SAVE;
+ }
+ break;
+
+ case DATA_R_END:
+ // Sem sa nedostaneme, len sa umlci prekladac, ze stav nie je osetreny vo switch
+ break;
+ }
+
+ }
+
+ // Pocet skutocne prijatych bajtov
+ *bytes_read = data_index;
+
+ return DATA_OK;
+}
+
+yup_retcode_t
+data_send(const uint8_t * const buf, size_t bufsize)
+{
+ static uint16_t relation_id = 0;
+
+ uint16_t sequence_num = 0;
+ size_t data_index = 0;
+ size_t ack_retry_count = DATA_ACK_RETRY_COUNT;
+
+ data_send_automaton_state_t state = DATA_S_START_RELATION;
+
+ // Kontrola argumentov
+ if ((buf == NULL) || (bufsize == 0)) {
+ return DATA_ERROR;
+ }
+
+ frame_t f, a;
+
+ while (state != DATA_S_END) {
+
+ yup_retcode_t r;
+
+ switch (state) {
+ case DATA_S_START_RELATION:
+ ++relation_id;
+ sequence_num = 0;
+ data_index = 0;
+ ack_retry_count = DATA_ACK_RETRY_COUNT;
+ state = DATA_S_PEPARE_DATA_FRAME;
+ break;
+
+ case DATA_S_PEPARE_DATA_FRAME:
+ // Pripravi sa ramec s datami
+ frame_init(&f, DATA_PROTOCOL_VERSION);
+ frame_set_relation_id(&f, relation_id);
+ frame_set_seq_number(&f, sequence_num);
+ size_t len = ((bufsize - data_index) >= FRAME_MAX_DATA_BYTES) ? FRAME_MAX_DATA_BYTES : (bufsize - data_index);
+ frame_set_data(&f, buf + data_index, len);
+ data_index += len;
+ // Je to posledny ramec?
+ if (data_index >= bufsize) {
+ frame_set_flag_lor(&f);
+ }
+ state = DATA_S_SEND_DATA_FRAME;
+ break;
+
+ case DATA_S_SEND_DATA_FRAME:
+ // Odosle sa pripraveny ramec
+ frame_send(&f);
+ state = DATA_S_WAIT_ACK;
+ break;
+
+ case DATA_S_WAIT_ACK:
+ r = frame_receive(&a, DATA_PROTOCOL_VERSION);
+ if (r == FRAME_TIMEOUT) {
+ // Neprijal sa ACK, znovu sa odoslu pipravene data
+ if (ack_retry_count-- > 0) {
+ state = DATA_S_SEND_DATA_FRAME;
+ } else {
+ // Bol dosiahnuty maximalny pocet pokusov o znozvu odoslanie dat
+ return DATA_ERROR;
+ }
+ } else if (r != FRAME_OK) {
+ // Chyba pri prijme ACK ramca
+ data_send_rej_for_frame(&a, r);
+ } else if ((r = data_is_ack_for_frame(&a, &f)) != DATA_OK) {
+ // ACK ramec prijaty v poriadku, ale s nespravnym obshom
+ data_send_rej_for_frame(&a, r);
+ } else if (frame_is_flag_lor_set(&f)) {
+ // Datovy ramec, ktory som odoslal, bol posledny
+ state = DATA_S_END;
+ } else {
+ // Naposledy vyslany datovy ramec bol potvrdeny protistranou, odosle sa dalsi
+ sequence_num++;
+ state = DATA_S_PEPARE_DATA_FRAME;
+ }
+ break;
+
+ case DATA_S_END:
+ // Sem sa nedostaneme, len sa umlci prekladac, ze stav nie je osetreny vo switch
+ break;
+ }
+ }
+
+ return DATA_OK;
+}
diff --git a/testing/yup-comm/protocol/data.h b/testing/yup-comm/protocol/data.h
new file mode 100644
index 0000000..a0b29db
--- /dev/null
+++ b/testing/yup-comm/protocol/data.h
@@ -0,0 +1,16 @@
+/* Author: Jan Sucan */
+
+#ifndef DATA_H_
+#define DATA_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "return_codes.h"
+
+yup_retcode_t data_receive(uint8_t * const buf, size_t bufsize, size_t * const bytes_read);
+yup_retcode_t data_send (const uint8_t * const buf, size_t bufsize);
+
+#endif /* DATA_H_ */
+
diff --git a/testing/yup-comm/protocol/frame.c b/testing/yup-comm/protocol/frame.c
new file mode 100644
index 0000000..42c697a
--- /dev/null
+++ b/testing/yup-comm/protocol/frame.c
@@ -0,0 +1,378 @@
+/* Author: Jan Sucan */
+
+#include "frame.h"
+#include "../comm/bt.h"
+#include "../utils/byte_buffer.h"
+#include "../utils/crc32q.h"
+
+#include <string.h>
+
+// Bitove masky priznakov ramca
+#define FRAME_FLAG_ACK 0x1 // Prijatie paketu
+#define FRAME_FLAG_REJ 0x2 // Odmietnutie paketou
+#define FRAME_FLAG_LOR 0x8 // Oznacenie posledneho paketu relacie
+
+// Offsety hodnot v surovych datach ramca
+#define FRAME_OFFSET_PROTOCOL_VERSION 0x00
+#define FRAME_OFFSET_FLAGS 0x00
+#define FRAME_OFFSET_DATA_LENGTH 0x00
+#define FRAME_OFFSET_RELATION_ID 0x02
+#define FRAME_OFFSET_SEQ_NUMBER 0x04
+#define FRAME_OFFSET_HEADER_CHECKSUM 0x06
+#define FRAME_OFFSET_DATA 0x0A
+
+// Bitove masky pre 16-bitovu hodnotu obsahujucu verziu protokolu YUP, priznaky ramca a dlzku pripojenych dat
+#define FRAME_MASK_PROTOCOL_VERSION 0xF000
+#define FRAME_MASK_FLAGS 0x0F00
+#define FRAME_MASK_DATA_LENGTH 0x00FF
+
+// Bitove offsety v 16-bitovej hodnote, v ktorej je obsiahnuta verzia YUP a priznaky
+#define FRAME_BITOFF_PROTOCOL_VERSION 12
+#define FRAME_BITOFF_FLAGS 8
+
+// Velkost hlavicky v bajtoch
+#define FRAME_HEADER_BYTES FRAME_OFFSET_DATA
+
+// Velkost pola kontrolneho suctu
+#define FRAME_CHECKSUM_BYTES 4
+
+// Interne pouzivany navratovy kod
+#define FRAME_PVT_ERR_DATA_INTERRUPTED 1
+
+
+static yup_retcode_t frame_receive_header (frame_t * const frame);
+static yup_retcode_t frame_verify_header_checksum (const frame_t * const frame);
+static yup_retcode_t frame_receive_data (frame_t * const frame);
+static yup_retcode_t frame_verify_data_checksum (const frame_t * const frame);
+static uint8_t frame_get_flags (const frame_t * const frame);
+static void frame_set_flags (frame_t * const frame, uint8_t f);
+static yup_retcode_t yup_retcode_from_bt_retcode (bt_retcode_t btr);
+
+void
+frame_init(frame_t * const frame, uint8_t protocol_version)
+{
+ frame_set_protocol_version(frame, protocol_version);
+ frame_set_flags(frame, 0);
+ frame_set_data(frame, NULL, 0);
+ frame_set_relation_id(frame, 0);
+ frame_set_seq_number(frame, 0);
+}
+
+void
+frame_send(frame_t * const frame)
+{
+ // Vyplni sa kontrolny sucet hlavicky
+ frame_set_header_checksum(frame);
+
+ // Vyplni sa kontrolny sucet dat, ak nejake su
+ frame_set_data_checksum(frame);
+
+ // Odosle sa ramec
+ bt_send_cobs_data_block(frame->raw, frame_get_length(frame));
+}
+
+yup_retcode_t
+frame_receive(frame_t * const frame, uint8_t protocol_version)
+{
+ yup_retcode_t retcode = FRAME_OK;
+
+ while (1) {
+ // Hlavicka sa prijme a skontroluje
+ if ((retcode = frame_receive_header(frame))) {
+ break;
+ }
+ if ((retcode = frame_verify_header_checksum(frame))) {
+ break;
+ }
+ // Hlavicka ramca je OK, odteraz sa moze getterom pristupovat k informacii o verzii protokolu a dlzke dat
+ // Skontroluje sa pozadovana verzia protokolu
+ if (frame_get_protocol_version(frame) != protocol_version) {
+ retcode = FRAME_PROTOCOL_VERSION_MISMATCH;
+ break;
+ }
+ // Prijmu sa data
+ yup_retcode_t r = frame_receive_data(frame);
+
+ if (r == FRAME_OK) {
+ break;
+ } else if (r == FRAME_PVT_ERR_DATA_INTERRUPTED) {
+ // Prijal sa delimiter, novy ramec zacal skor nez skoncil aktualny, zacne sa s prijmom noveho ramca
+ continue;
+ } else {
+ retcode = r;
+ break;
+ }
+ }
+
+ if (retcode == FRAME_OK) {
+ retcode = frame_verify_data_checksum(frame);
+ }
+
+ return retcode;
+}
+
+yup_retcode_t
+frame_receive_header(frame_t * const frame)
+{
+ return yup_retcode_from_bt_retcode(bt_receive_cobs_data(frame->raw, FRAME_HEADER_BYTES, BT_RECEIVE_COBS_START));
+}
+
+yup_retcode_t
+frame_verify_header_checksum (const frame_t * const frame)
+{
+ yup_retcode_t retcode = FRAME_OK;
+
+ // Vypocet kontrolneho suctu prijatych dat
+ // Preskoci sa COBS delimiter (1B) a COBS header (1B)
+ // Do vypoctu nebudu zahrnute 4 bajty kontrolneho suctu
+ const uint32_t sum = crc32q(frame->raw, FRAME_HEADER_BYTES - FRAME_CHECKSUM_BYTES);
+
+ // Vypocitany kontrolny sucet sa porovna so suctom v ramci
+ if (sum != frame_get_header_checksum(frame)) {
+ retcode = FRAME_BAD_HEADER_CHECKSUM;
+ }
+
+ return retcode;
+}
+
+yup_retcode_t
+frame_receive_data (frame_t * const frame)
+{
+ const uint8_t data_length = frame_get_data_length(frame);
+ yup_retcode_t retcode = FRAME_OK;
+
+ // Pri nulovych data sa nic neprijme
+ if (data_length > 0) {
+ // Treba prijat naviac 4B kontrolneho suctu
+ retcode = yup_retcode_from_bt_retcode(bt_receive_cobs_data(frame->raw + FRAME_HEADER_BYTES,
+ data_length + FRAME_CHECKSUM_BYTES,
+ BT_RECEIVE_COBS_CONTINUE));
+ }
+
+ return retcode;
+}
+
+yup_retcode_t
+frame_verify_data_checksum (const frame_t * const frame)
+{
+ yup_retcode_t retcode = FRAME_OK;
+ const uint8_t data_length = frame_get_data_length(frame);
+
+ // Ked sme sa dostali az sem, kontrolny sucet hlavicky je OK
+ // Ak nemame ziadne data, nic sa uz nemusi kontrolovat
+ if (data_length > 0) {
+ // Kontrola suctu dat
+ // Do vypoctu nebudu zahrnute 4 bajty kontrolneho suctu
+ const uint32_t sum = crc32q(frame->raw + FRAME_HEADER_BYTES, data_length);
+
+ // Vypocitany kontrolny sucet sa porovna so suctom v ramci
+ if (sum != frame_get_data_checksum(frame)) {
+ retcode = FRAME_BAD_DATA_CHECKSUM;
+ }
+ }
+
+ return retcode;
+}
+
+yup_retcode_t
+yup_retcode_from_bt_retcode(bt_retcode_t btr)
+{
+ yup_retcode_t r = FRAME_RECEIVE_ERROR;
+
+ switch (btr) {
+ case BT_RECEIVE_TIMEOUT:
+ r = FRAME_TIMEOUT;
+ break;
+
+ case BT_RECEIVE_COBS_INTERRUPTED:
+ r = FRAME_PVT_ERR_DATA_INTERRUPTED;
+ break;
+
+ case BT_RECEIVE_BAD_ARGUMENT:
+ case BT_RECEIVE_ERROR:
+ case BT_RECEIVE_COBS_UNKNOWN_STATE:
+ r = FRAME_RECEIVE_ERROR;
+ break;
+
+ case BT_OK:
+ r = FRAME_OK;
+ break;
+ }
+
+ return r;
+}
+
+
+// Getter/Setter funkcie
+uint8_t
+frame_get_protocol_version(const frame_t * const frame)
+{
+ const uint16_t x = GET_UINT16_T_FROM_BYTES(frame->raw, FRAME_OFFSET_PROTOCOL_VERSION);
+ return (x & FRAME_MASK_PROTOCOL_VERSION) >> FRAME_BITOFF_PROTOCOL_VERSION;
+}
+
+void
+frame_set_protocol_version(frame_t * const frame, uint8_t v)
+{
+ uint16_t x = GET_UINT16_T_FROM_BYTES(frame->raw, FRAME_OFFSET_PROTOCOL_VERSION);
+ x &= ~FRAME_MASK_PROTOCOL_VERSION;
+ x |= (v & 0x0F) << FRAME_BITOFF_PROTOCOL_VERSION;
+ SET_BYTES_FROM_UINT16_T(frame->raw, FRAME_OFFSET_PROTOCOL_VERSION, x);
+}
+
+void
+frame_set_flag_ack(frame_t * const frame)
+{
+ frame_set_flags(frame, frame_get_flags(frame) | FRAME_FLAG_ACK);
+}
+
+bool
+frame_is_flag_ack_set(const frame_t * const frame)
+{
+ return frame_get_flags(frame) & FRAME_FLAG_ACK;
+}
+
+void
+frame_set_flag_rej(frame_t * const frame)
+{
+ frame_set_flags(frame, frame_get_flags(frame) | FRAME_FLAG_REJ);
+}
+
+bool
+frame_is_flag_rej_set(const frame_t * const frame)
+{
+ return frame_get_flags(frame) & FRAME_FLAG_REJ;
+}
+
+void
+frame_set_flag_lor(frame_t * const frame)
+{
+ frame_set_flags(frame, frame_get_flags(frame) | FRAME_FLAG_LOR);
+}
+
+bool
+frame_is_flag_lor_set(const frame_t * const frame)
+{
+ return frame_get_flags(frame) & FRAME_FLAG_LOR;
+}
+
+uint8_t
+frame_get_flags(const frame_t * const frame)
+{
+ const uint16_t x = GET_UINT16_T_FROM_BYTES(frame->raw, FRAME_OFFSET_FLAGS);
+ return (x & FRAME_MASK_FLAGS) >> FRAME_BITOFF_FLAGS;
+}
+
+void
+frame_set_flags(frame_t * const frame, uint8_t f)
+{
+ uint16_t x = GET_UINT16_T_FROM_BYTES(frame->raw, FRAME_OFFSET_FLAGS);
+ x &= ~FRAME_MASK_FLAGS;
+ x |= (f & 0x0F) << FRAME_BITOFF_FLAGS;
+ SET_BYTES_FROM_UINT16_T(frame->raw, FRAME_OFFSET_FLAGS, x);
+}
+
+uint8_t
+frame_get_data_length(const frame_t * const frame)
+{
+ const uint16_t x = GET_UINT16_T_FROM_BYTES(frame->raw, FRAME_OFFSET_DATA_LENGTH);
+ return (x & FRAME_MASK_DATA_LENGTH);
+}
+
+void
+frame_set_data_length(frame_t * const frame, uint8_t v)
+{
+ uint16_t x = GET_UINT16_T_FROM_BYTES(frame->raw, FRAME_OFFSET_DATA_LENGTH);
+ x &= ~FRAME_MASK_DATA_LENGTH;
+ x |= v;
+ SET_BYTES_FROM_UINT16_T(frame->raw, FRAME_OFFSET_DATA_LENGTH, x);
+}
+
+uint16_t
+frame_get_relation_id(const frame_t * const frame)
+{
+ return GET_UINT16_T_FROM_BYTES(frame->raw, FRAME_OFFSET_RELATION_ID);
+}
+
+void
+frame_set_relation_id(frame_t * const frame, uint16_t v)
+{
+ SET_BYTES_FROM_UINT16_T(frame->raw, FRAME_OFFSET_RELATION_ID, v);
+}
+
+uint16_t
+frame_get_seq_number(const frame_t * const frame)
+{
+ return GET_UINT16_T_FROM_BYTES(frame->raw, FRAME_OFFSET_SEQ_NUMBER);
+}
+
+void
+frame_set_seq_number(frame_t * const frame, uint16_t v)
+{
+ SET_BYTES_FROM_UINT16_T(frame->raw, FRAME_OFFSET_SEQ_NUMBER, v);
+}
+
+uint32_t
+frame_get_header_checksum(const frame_t * const frame)
+{
+ return GET_UINT32_T_FROM_BYTES(frame->raw, FRAME_OFFSET_HEADER_CHECKSUM);
+}
+
+void
+frame_set_header_checksum(frame_t * const frame)
+{
+ // Nezapocitavaju sa prve dva bajty COBSu a posledne 4 bajty pre ulozenie kontrolneho suctu
+ const uint32_t cs = crc32q(frame->raw, FRAME_HEADER_BYTES - FRAME_CHECKSUM_BYTES);
+ SET_BYTES_FROM_UINT32_T(frame->raw, FRAME_OFFSET_HEADER_CHECKSUM, cs);
+}
+
+void
+frame_get_data(const frame_t * const frame, uint8_t * const data, uint8_t * const data_length)
+{
+ *data_length = frame_get_data_length(frame);
+ memcpy(data, frame->raw + FRAME_OFFSET_DATA, *data_length);
+}
+
+void
+frame_set_data(frame_t * const frame, const uint8_t * const data, uint8_t num)
+{
+ uint8_t n = (num > FRAME_MAX_DATA_BYTES) ? FRAME_MAX_DATA_BYTES : num;
+
+ frame_set_data_length(frame, n);
+
+ if ((data != NULL) && (n > 0)) {
+ memcpy(frame->raw + FRAME_OFFSET_DATA, data, n);
+ }
+}
+
+uint32_t
+frame_get_data_checksum(const frame_t * const frame)
+{
+ uint8_t data_length = frame_get_data_length(frame);
+ uint32_t x = 0;
+ // Pole checksum je pripojene len vtedy, ked su pripojene nejake data
+ if (data_length > 0) {
+ x = GET_UINT32_T_FROM_BYTES(frame->raw, FRAME_OFFSET_DATA + data_length);
+ }
+ return x;
+}
+
+void
+frame_set_data_checksum(frame_t * const frame)
+{
+ const uint8_t dl = frame_get_data_length(frame);
+ if (dl == 0) {
+ // Zadne data, nie je k comu pocitat kontrolny sucet
+ return;
+ }
+
+ const uint32_t cs = crc32q(frame->raw + FRAME_OFFSET_DATA, dl);
+ SET_BYTES_FROM_UINT32_T(frame->raw, FRAME_OFFSET_DATA + dl, cs);
+}
+
+uint8_t
+frame_get_length(const frame_t * const frame)
+{
+ const uint8_t dl = frame_get_data_length(frame);
+ return FRAME_OFFSET_DATA + dl + ((dl > 0) ? FRAME_CHECKSUM_BYTES : 0);
+}
diff --git a/testing/yup-comm/protocol/frame.h b/testing/yup-comm/protocol/frame.h
new file mode 100644
index 0000000..d101968
--- /dev/null
+++ b/testing/yup-comm/protocol/frame.h
@@ -0,0 +1,56 @@
+/* Author: Jan Sucan */
+
+#ifndef FRAME_H_
+#define FRAME_H_
+
+#include "return_codes.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#define FRAME_MAX_BYTES 254
+
+#define FRAME_MAX_DATA_BYTES 240
+
+typedef struct {
+ uint8_t raw[FRAME_MAX_BYTES];
+} frame_t;
+
+void frame_init (frame_t * const frame, uint8_t protocol_version);
+yup_retcode_t frame_receive(frame_t * const frame, uint8_t protocol_version);
+void frame_send (frame_t * const frame);
+
+// Protocol version
+uint8_t frame_get_protocol_version(const frame_t * const frame);
+void frame_set_protocol_version(frame_t * const frame, uint8_t v);
+// Flags
+void frame_set_flag_ack(frame_t * const frame);
+bool frame_is_flag_ack_set(const frame_t * const frame);
+void frame_set_flag_rej(frame_t * const frame);
+bool frame_is_flag_rej_set(const frame_t * const frame);
+void frame_set_flag_lor(frame_t * const frame);
+bool frame_is_flag_lor_set(const frame_t * const frame);
+// Data length
+uint8_t frame_get_data_length(const frame_t * const frame);
+void frame_set_data_length(frame_t * const frame, uint8_t v);
+// Relation ID
+uint16_t frame_get_relation_id(const frame_t * const frame);
+void frame_set_relation_id(frame_t * const frame, uint16_t v);
+// Seq number
+uint16_t frame_get_seq_number(const frame_t * const frame);
+void frame_set_seq_number(frame_t * const frame, uint16_t v);
+// Header checksum
+uint32_t frame_get_header_checksum(const frame_t * const frame);
+void frame_set_header_checksum(frame_t * const frame);
+// Data
+void frame_get_data(const frame_t * const frame, uint8_t * const data, uint8_t * const data_length);
+void frame_set_data(frame_t * const frame, const uint8_t * const data, uint8_t num);
+// Data checksum
+uint32_t frame_get_data_checksum(const frame_t * const frame);
+void frame_set_data_checksum(frame_t * const frame);
+
+// Ostatne
+uint8_t frame_get_length(const frame_t * const frame);
+
+#endif /* FRAME_H_ */
diff --git a/testing/yup-comm/protocol/return_codes.h b/testing/yup-comm/protocol/return_codes.h
new file mode 100644
index 0000000..b7858a5
--- /dev/null
+++ b/testing/yup-comm/protocol/return_codes.h
@@ -0,0 +1,38 @@
+/* Author: Jan Sucan */
+
+#ifndef RETURN_CODES_H_
+#define RETURN_CODES_H_
+
+#include <limits.h>
+
+typedef enum {
+ // Privatne kody nesirene v REJ ramcoch
+ FRAME_OK = 0x00,
+ DATA_OK = 0x00,
+ DATA_ERROR,
+ FRAME_TIMEOUT,
+
+ // Kody sirene v REJ ramcoch
+ DATA_STARTING_HAS_NONZERO_SEQ_NUMBER = 0x10,
+ DATA_RELATION_TIMEOUT,
+
+ DATA_DATA_HAS_NO_PAYLOAD = 0x30,
+ DATA_DATA_HAS_ACK_FLAG,
+ DATA_DATA_HAS_REJ_FLAG,
+ DATA_DATA_RELATION_ID_MISMATCH,
+ DATA_DATA_SEQ_NUMBER_MISMATCH,
+
+ DATA_ACK_HAS_PAYLOAD = 0x50,
+ DATA_ACK_HAS_NOT_ACK_FLAG,
+ DATA_ACK_HAS_LOR_FLAG,
+ DATA_ACK_HAS_REJ_FLAG,
+ DATA_ACK_RELATION_ID_MISMATCH,
+ DATA_ACK_SEQ_NUMBER_MISMATCH,
+
+ FRAME_RECEIVE_ERROR = 0x70,
+ FRAME_BAD_HEADER_CHECKSUM,
+ FRAME_BAD_DATA_CHECKSUM,
+ FRAME_PROTOCOL_VERSION_MISMATCH
+} yup_retcode_t;
+
+#endif /* RETURN_CODES_H_ */ \ No newline at end of file