aboutsummaryrefslogtreecommitdiff
path: root/testing/yup-comm
diff options
context:
space:
mode:
Diffstat (limited to 'testing/yup-comm')
-rw-r--r--testing/yup-comm/Makefile27
-rw-r--r--testing/yup-comm/comm/bt.c176
-rw-r--r--testing/yup-comm/comm/bt.h30
-rw-r--r--testing/yup-comm/comm/serial_port.c144
-rw-r--r--testing/yup-comm/comm/serial_port.h16
-rw-r--r--testing/yup-comm/mailslot.c43
-rw-r--r--testing/yup-comm/mailslot.h9
-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
-rw-r--r--testing/yup-comm/utils/byte_buffer.h31
-rw-r--r--testing/yup-comm/utils/crc32q.c64
-rw-r--r--testing/yup-comm/utils/crc32q.h12
-rw-r--r--testing/yup-comm/yup-comm-client.c80
-rw-r--r--testing/yup-comm/yup-comm-client.exebin0 -> 148529 bytes
-rw-r--r--testing/yup-comm/yup-comm-server.c198
-rw-r--r--testing/yup-comm/yup-comm-server.exebin0 -> 166747 bytes
19 files changed, 1630 insertions, 0 deletions
diff --git a/testing/yup-comm/Makefile b/testing/yup-comm/Makefile
new file mode 100644
index 0000000..1d20ead
--- /dev/null
+++ b/testing/yup-comm/Makefile
@@ -0,0 +1,27 @@
+CC = gcc
+CFLAGS = -Wall
+
+SERVER_SRCS = yup-comm-server.c comm/serial_port.c comm/bt.c protocol/data.c protocol/frame.c
+SERVER_SRCS += utils/crc32q.c mailslot.c
+
+SERVER_HDRS = comm/bt.h comm/serial_port.h protocol/data.h protocol/frame.h
+SERVER_HDRS += protocol/return_codes.h utils/byte_buffer.h utils/crc32q.h
+SERVER_HDRS += mailslot.h
+
+CLIENT_SRCS = yup-comm-client.c mailslot.c
+
+CLIENT_HDRS = mailslot.h
+
+
+all: yup-comm-server yup-comm-client
+
+yup-comm-server: $(SERVER_SRCS) $(SERVER_HDRS)
+ $(CC) $(CFLAGS) $(SERVER_SRCS) -o yup-comm-server
+
+yup-comm-client: $(CLIENT_SRCS) $(CLIENT_HDRS)
+ $(CC) $(CFLAGS) $(CLIENT_SRCS) -o yup-comm-client
+
+.PHONY: clean
+
+clean:
+ rm -f *.o *~ */*.o */*~ yup-comm-server.exe yup-comm-client.exe
diff --git a/testing/yup-comm/comm/bt.c b/testing/yup-comm/comm/bt.c
new file mode 100644
index 0000000..a0eceb9
--- /dev/null
+++ b/testing/yup-comm/comm/bt.c
@@ -0,0 +1,176 @@
+/* Author: Jan Sucan */
+
+#include "serial_port.h"
+#include "bt.h"
+
+#define COMM_PARAMS_COBS_DELIMITER_CHAR '$'
+
+typedef enum {
+ COBS_WAITING_FOR_THE_FIRST_DELIMITER,
+ COBS_WAITING_FOR_STUFFED_BYTE,
+ COBS_WAITING_FOR_NON_STUFFED_BYTE
+} cobs_state_t;
+
+static HANDLE bt_serial_port;
+
+static bt_retcode_t bt_retcode_from_usart_retcode(int ur);
+
+void
+bt_init(HANDLE serial_port)
+{
+ bt_serial_port = serial_port;
+}
+
+bt_retcode_t
+bt_receive_cobs_automaton(uint8_t b, uint8_t * const buf, size_t * const i, bool cobs_continue)
+{
+ static cobs_state_t cobs_state = COBS_WAITING_FOR_THE_FIRST_DELIMITER;
+ static uint8_t next_non_stuffed = 0;
+ static bool interrupted = false;
+
+ bt_retcode_t r = BT_OK;
+
+ if (interrupted) {
+ // Delimiter bol prijaty v minulom volani tejto funkcie, 'b' je uz nasledujuci bajt
+ interrupted = false;
+ // Jeden prazdny priechod nadradenym for cyklom a pokracuje sa v aktualnej funkcii
+ ++(*i);
+ }
+
+ // Frame delimiter resetuje stavovy automat a prijem bajtov
+ if (b == COMM_PARAMS_COBS_DELIMITER_CHAR) {
+ if (!cobs_continue) {
+ cobs_state = COBS_WAITING_FOR_STUFFED_BYTE;
+ *i = 0;
+ } else {
+ interrupted = true;
+ r = BT_RECEIVE_COBS_INTERRUPTED;
+ }
+ } else {
+ switch (cobs_state) {
+ case COBS_WAITING_FOR_THE_FIRST_DELIMITER:
+ // Automat deaktivovany, ztial sa neprijal frame delimiter
+ break;
+
+ case COBS_WAITING_FOR_STUFFED_BYTE:
+ // Prijal sa stuffovany byte, ziskame pocet nasledujuci nestuffovanych bajtov
+ next_non_stuffed = (b <= COMM_PARAMS_COBS_DELIMITER_CHAR) ? b : (b - 1);
+ // COBS header sa nezapisuje do dat, vsetky dalsie stuffovane ano
+ if (!cobs_continue && (*i > 1)) {
+ buf[*i - 2] = COMM_PARAMS_COBS_DELIMITER_CHAR;
+ } else {
+ buf[*i] = COMM_PARAMS_COBS_DELIMITER_CHAR;
+ }
+ cobs_state = (next_non_stuffed == 0) ? COBS_WAITING_FOR_STUFFED_BYTE : COBS_WAITING_FOR_NON_STUFFED_BYTE;
+ break;
+
+ case COBS_WAITING_FOR_NON_STUFFED_BYTE:
+ // Prijal sa nestuffovany bajt, nic sa s nim nerobi
+ --next_non_stuffed;
+ // Len sa ulozi
+ buf[*i - ((cobs_continue) ? 0 : 2)] = b;
+ cobs_state = (next_non_stuffed == 0) ? COBS_WAITING_FOR_STUFFED_BYTE : COBS_WAITING_FOR_NON_STUFFED_BYTE;
+ break;
+
+ default:
+ r = BT_RECEIVE_COBS_UNKNOWN_STATE;
+ break;
+ }
+ }
+
+ return r;
+}
+
+bt_retcode_t
+bt_receive_cobs_data(uint8_t * const buf, size_t n, bool cobs_continue)
+{
+ bt_retcode_t r = BT_RECEIVE_BAD_ARGUMENT;
+
+ // Kontrola argumentov
+ if (buf != NULL) {
+ size_t i;
+ // Na zaciatku dat sa prijma navyse COBS frame delimiter (1B) a COBS header (1B)
+ if (!cobs_continue) {
+ n += 2;
+ }
+
+ for (i = 0; i < n; ++i) {
+ uint8_t b;
+ const int ur = serial_port_read_byte(bt_serial_port, &b);
+
+ if (ur) {
+ // Chyba pri prijme bajtu
+ r = bt_retcode_from_usart_retcode(ur);
+ break;
+ }
+
+ const bt_retcode_t ar = bt_receive_cobs_automaton(b, buf, &i, cobs_continue);
+
+ if (ar != BT_OK) {
+ r = ar;
+ break;
+ }
+ }
+
+ // Uspesny prijem a odsfuttovanie vsetkych bajtov
+ if (i >= n) {
+ r = BT_OK;
+ }
+ }
+
+ return r;
+}
+
+void
+bt_send_cobs_data_block(const uint8_t *const buf, size_t n)
+{
+ uint8_t comm[2048U];
+ unsigned ci = 0;
+
+ // Kontrola argumentov
+ if (buf == NULL) {
+ return;
+ }
+ // POZOR: neosetrujeme velkost dat, moze dojst k preteceniu hodnot na stuffovanych bajtoch
+
+ // Odosle sa delimiter
+ comm[ci++] = COMM_PARAMS_COBS_DELIMITER_CHAR;
+
+ uint8_t next_non_stuffed = 0;
+ size_t send_index = 0 - 1;
+
+ for (size_t i = 0; i <= n; ++i) {
+ if ((i == n) || (buf[i] == COMM_PARAMS_COBS_DELIMITER_CHAR)) {
+ comm[ci++] = (next_non_stuffed >= COMM_PARAMS_COBS_DELIMITER_CHAR) ? (next_non_stuffed + 1) : next_non_stuffed;
+ // Zacne sa odosielat az za virtualnym, alebo realnym stuffovanym bajtom
+ ++send_index;
+ // Odoslu sa napocitane bajty
+ while (next_non_stuffed > 0) {
+ comm[ci++] = buf[send_index];
+ ++send_index;
+ --next_non_stuffed;
+ }
+ } else {
+ // Pocitaju sa nestuffovane bajty, zatial sa nic neposiela
+ ++next_non_stuffed;
+ }
+ }
+
+ serial_port_write_byte(bt_serial_port, comm, ci);
+}
+
+bt_retcode_t
+bt_retcode_from_usart_retcode(int ur)
+{
+ bt_retcode_t r = BT_RECEIVE_BAD_ARGUMENT;
+
+ if (ur == -2) {
+ r = BT_RECEIVE_TIMEOUT;
+ } else if (ur != 0) {
+ r = BT_RECEIVE_ERROR;
+ } else {
+ r = BT_OK;
+ }
+
+ return r;
+}
diff --git a/testing/yup-comm/comm/bt.h b/testing/yup-comm/comm/bt.h
new file mode 100644
index 0000000..a9df2d5
--- /dev/null
+++ b/testing/yup-comm/comm/bt.h
@@ -0,0 +1,30 @@
+/* Author: Jan Sucan */
+
+#ifndef BT_H_
+#define BT_H_
+
+#include <windows.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <limits.h>
+
+#define BT_RECEIVE_COBS_START false
+
+#define BT_RECEIVE_COBS_CONTINUE true
+
+typedef enum {
+ BT_RECEIVE_BAD_ARGUMENT = INT_MIN,
+ BT_RECEIVE_TIMEOUT,
+ BT_RECEIVE_ERROR,
+ BT_RECEIVE_COBS_INTERRUPTED,
+ BT_RECEIVE_COBS_UNKNOWN_STATE,
+ BT_OK = 0
+} bt_retcode_t;
+
+void bt_init(HANDLE serial_port);
+void bt_close(void);
+bt_retcode_t bt_receive_cobs_data(uint8_t * const buf, size_t n, bool cobs_continue);
+void bt_send_cobs_data_block(const uint8_t *const buf, size_t n);
+
+#endif /* BT_H_ */
diff --git a/testing/yup-comm/comm/serial_port.c b/testing/yup-comm/comm/serial_port.c
new file mode 100644
index 0000000..da30fab
--- /dev/null
+++ b/testing/yup-comm/comm/serial_port.c
@@ -0,0 +1,144 @@
+/* Author: Jan Sucan */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "serial_port.h"
+
+static HANDLE serial_port_open_port(const char * const portName);
+static BOOL serial_port_set_timeouts(HANDLE serial_port, int ms);
+
+LPSTR
+serial_port_get_error(void)
+{
+ DWORD e = GetLastError();
+ if (e == 0) {
+ return NULL;
+ }
+
+ LPSTR msgBuff = NULL;
+ FormatMessageA(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ e,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPSTR) &msgBuff,
+ 0,
+ NULL);
+
+ return msgBuff;
+}
+
+void
+serial_port_free_error(LPSTR msg)
+{
+ LocalFree(msg);
+}
+
+HANDLE
+serial_port_open_port(const char * const portName)
+{
+ HANDLE sp = INVALID_HANDLE_VALUE;
+
+ // Construct Windows serial port name
+ char sn[16];
+ strcpy(sn, "\\\\.\\");
+ strcpy(sn + strlen(sn), portName);
+ // Open serial port
+ sp = CreateFile(sn, // Port name
+ GENERIC_READ | GENERIC_WRITE, // Read/Write
+ 0, // No Sharing
+ NULL, // No Security
+ OPEN_EXISTING,// Open existing port only
+ 0, // Non Overlapped I/O
+ NULL); // Null for Comm Devices
+
+ return sp;
+}
+
+HANDLE
+serial_port_open(const char * const portName)
+{
+ HANDLE serial_port = serial_port_open_port(portName);
+ if (serial_port == INVALID_HANDLE_VALUE) {
+ return INVALID_HANDLE_VALUE;
+ }
+
+ // Konfiguracia
+ DCB dcbSerialParams = { 0 };
+ dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
+
+ if (GetCommState(serial_port, &dcbSerialParams) == 0) {
+ serial_port_close(serial_port);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ dcbSerialParams.BaudRate = CBR_115200;
+ dcbSerialParams.ByteSize = 8; // Setting ByteSize = 8
+ dcbSerialParams.StopBits = ONESTOPBIT;// Setting StopBits = 1
+ dcbSerialParams.Parity = NOPARITY; // Setting Parity = None
+
+ if ((SetCommState(serial_port, &dcbSerialParams) == 0)
+ || (serial_port_set_timeouts(serial_port, 3000U) == 0)) {
+ serial_port_close(serial_port);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ return serial_port;
+}
+
+void
+serial_port_close(HANDLE serial_port)
+{
+ if (serial_port != INVALID_HANDLE_VALUE) {
+ CloseHandle(serial_port);
+ serial_port = INVALID_HANDLE_VALUE;
+ }
+}
+
+BOOL
+serial_port_set_timeouts(HANDLE serial_port, int ms)
+{
+ COMMTIMEOUTS timeouts;
+
+ if (!GetCommTimeouts(serial_port, &timeouts)) {
+ return FALSE;
+ }
+
+ timeouts.ReadIntervalTimeout = ms;
+ timeouts.ReadTotalTimeoutMultiplier = ms;
+ timeouts.ReadTotalTimeoutConstant = 0;
+
+ timeouts.WriteTotalTimeoutMultiplier = ms;
+ timeouts.WriteTotalTimeoutConstant = 0;
+
+ if (!SetCommTimeouts(serial_port, &timeouts)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+int
+serial_port_write_byte(HANDLE serial_port, uint8_t * const data, unsigned size)
+{
+ DWORD written = 0;
+
+ if (!WriteFile(serial_port, data, size, &written, NULL)) {
+ written = 0;
+ }
+
+ return (written != size) ? -1 : 0;
+}
+
+int
+serial_port_read_byte(HANDLE serial_port, uint8_t * const byte)
+{
+ DWORD read;
+
+ if (!ReadFile(serial_port, byte, sizeof(uint8_t), &read, NULL)) {
+ return -1;
+ }
+
+ return (read != 0) ? 0 : -2;
+}
diff --git a/testing/yup-comm/comm/serial_port.h b/testing/yup-comm/comm/serial_port.h
new file mode 100644
index 0000000..db9dfe9
--- /dev/null
+++ b/testing/yup-comm/comm/serial_port.h
@@ -0,0 +1,16 @@
+/* Author: Jan Sucan */
+
+#ifndef SERIAL_PORT_H
+#define SERIAL_PORT_H 1
+
+#include <windows.h>
+#include <stdint.h>
+
+HANDLE serial_port_open(const char * const portName);
+void serial_port_close(HANDLE serial_port);
+int serial_port_write_byte(HANDLE serial_port, uint8_t * const data, unsigned size);
+int serial_port_read_byte(HANDLE serial_port, uint8_t * const byte);
+LPSTR serial_port_get_error(void);
+void serial_port_free_error(LPSTR msg);
+
+#endif
diff --git a/testing/yup-comm/mailslot.c b/testing/yup-comm/mailslot.c
new file mode 100644
index 0000000..a18f21e
--- /dev/null
+++ b/testing/yup-comm/mailslot.c
@@ -0,0 +1,43 @@
+/* Author: Jan Sucan */
+
+#include "mailslot.h"
+
+HANDLE
+mailslot_create(const char * const name)
+{
+ return CreateMailslot(name, 0, MAILSLOT_WAIT_FOREVER, NULL);
+}
+
+unsigned
+mailslot_read(HANDLE slot, char * const buf, unsigned buf_size)
+{
+ unsigned bytes_read;
+
+ ReadFile(slot, buf, buf_size, &bytes_read, NULL);
+
+ return bytes_read;
+}
+
+HANDLE
+mailslot_connect(const char * const name)
+{
+ return CreateFile(name, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+}
+
+int
+mailslot_write(HANDLE slot, char * const buf, unsigned buf_size)
+{
+ unsigned bytes_written;
+
+ if (WriteFile(slot, buf, buf_size, &bytes_written, NULL) == 0) {
+ return -1;
+ }
+
+ return (bytes_written != buf_size) ? -1 : 0;
+}
+
+void
+mailslot_close(HANDLE slot)
+{
+ CloseHandle(slot);
+}
diff --git a/testing/yup-comm/mailslot.h b/testing/yup-comm/mailslot.h
new file mode 100644
index 0000000..78fc0d9
--- /dev/null
+++ b/testing/yup-comm/mailslot.h
@@ -0,0 +1,9 @@
+/* Author: Jan Sucan */
+
+#include <windows.h>
+
+HANDLE mailslot_create(const char * const name);
+unsigned mailslot_read(HANDLE slot, char * const buf, unsigned buf_size);
+HANDLE mailslot_connect(const char * const name);
+int mailslot_write(HANDLE slot, char * const buf, unsigned buf_size);
+void mailslot_close(HANDLE slot);
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
diff --git a/testing/yup-comm/utils/byte_buffer.h b/testing/yup-comm/utils/byte_buffer.h
new file mode 100644
index 0000000..30746f2
--- /dev/null
+++ b/testing/yup-comm/utils/byte_buffer.h
@@ -0,0 +1,31 @@
+/* Author: Jan Sucan */
+
+#ifndef BYTE_BUFFER_H_
+#define BYTE_BUFFER_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#define GET_UINT8_T_FROM_BYTES(b, i) ((uint8_t) b[i])
+
+#define GET_UINT16_T_FROM_BYTES(b, i) ((uint16_t) ((b[i + 1] << 8) | b[i]))
+
+#define GET_UINT32_T_FROM_BYTES(b, i) ((uint32_t) ((b[i + 3] << 24) | (b[i + 2] << 16) | (b[i + 1] << 8) | b[i]))
+
+#define SET_BYTES_FROM_UINT8_T(b, i, v) {\
+ b[i + 0] = v;\
+}
+
+#define SET_BYTES_FROM_UINT16_T(b, i, v) { \
+ b[i + 1] = (v & 0xFF00) >> 8;\
+ b[i + 0] = (v & 0x00FF);\
+}
+
+#define SET_BYTES_FROM_UINT32_T(b, i, v) {\
+ b[i + 3] = (v & 0xFF000000) >> 24;\
+ b[i + 2] = (v & 0x00FF0000) >> 16;\
+ b[i + 1] = (v & 0x0000FF00) >> 8;\
+ b[i + 0] = (v & 0x000000FF);\
+}
+
+#endif /* BYTE_BUFFER_H_ */
diff --git a/testing/yup-comm/utils/crc32q.c b/testing/yup-comm/utils/crc32q.c
new file mode 100644
index 0000000..a0f0c46
--- /dev/null
+++ b/testing/yup-comm/utils/crc32q.c
@@ -0,0 +1,64 @@
+/* Author: Jan Sucan */
+
+#include "crc32q.h"
+
+#include <stdbool.h>
+
+#define CRC32_TABLE_SIZE 256
+
+#define CRC32_GENERATING_POLYNOME 0x814141ab
+
+static void crc32q_create_table(uint32_t * const tab, uint32_t gen_polynome);
+
+void
+crc32q_create_table(uint32_t * const tab, uint32_t gen_polynome)
+{
+ int i, j;
+ uint32_t c;
+ uint32_t msbMask = 1U << ((sizeof(c) * 8) - 1);
+
+ for (i = 0; i < CRC32_TABLE_SIZE; i++) {
+ c = i << 24;
+ for (j = 0; j < 8; j++) {
+ if (c & msbMask) {
+ c = (c << 1) ^ gen_polynome;
+ } else {
+ c = c << 1;
+ }
+ }
+ tab[i] = c;
+ }
+}
+
+static uint32_t crc32_tab[CRC32_TABLE_SIZE];
+
+static bool crc32q_table_created = false;
+
+void
+crc32q_init(void)
+{
+ crc32q_create_table(crc32_tab, CRC32_GENERATING_POLYNOME);
+ crc32q_table_created = true;
+}
+
+uint32_t
+crc32q(const void * const buf, size_t size)
+{
+ // Vytvorenie tabulky pred prvym pouzitim
+ if (!crc32q_table_created) {
+ crc32q_create_table(crc32_tab, CRC32_GENERATING_POLYNOME);
+ crc32q_table_created = true;
+ }
+
+ // Vypocet CRC-32
+ const uint8_t *p;
+
+ p = buf;
+ uint32_t crc = 0U; // Inicializacna CRC hodnota
+
+ while (size--) {
+ crc = (crc << 8) ^ crc32_tab[(crc >> 24) ^ *p++];
+ }
+
+ return crc ^ 0U;
+}
diff --git a/testing/yup-comm/utils/crc32q.h b/testing/yup-comm/utils/crc32q.h
new file mode 100644
index 0000000..54ea16d
--- /dev/null
+++ b/testing/yup-comm/utils/crc32q.h
@@ -0,0 +1,12 @@
+/* Author: Jan Sucan */
+
+#ifndef CRC32Q_H_
+#define CRC32Q_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+
+void crc32q_init(void);
+uint32_t crc32q(const void * const buf, size_t size);
+
+#endif /* CRC32Q_H_ */ \ No newline at end of file
diff --git a/testing/yup-comm/yup-comm-client.c b/testing/yup-comm/yup-comm-client.c
new file mode 100644
index 0000000..d6cc8e4
--- /dev/null
+++ b/testing/yup-comm/yup-comm-client.c
@@ -0,0 +1,80 @@
+/* Author: Jan Sucan */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "mailslot.h"
+
+#define MAILSLOT_SERVER_NAME "\\\\.\\Mailslot\\yup-comm-server"
+#define MAILSLOT_CLIENT_NAME "\\\\.\\Mailslot\\yup-comm-client"
+#define MAILSLOT_MAX_MSG_SIZE 400
+
+HANDLE mailslot_server = INVALID_HANDLE_VALUE;
+HANDLE mailslot_client = INVALID_HANDLE_VALUE;
+
+void
+print_msg(FILE * stream, const char * format, ...)
+{
+ char buf[1024];
+
+ va_list args;
+ va_start(args, format);
+ vsnprintf(buf, 1024, format, args);
+ va_end(args);
+
+ fprintf(stream, " yup-comm-client: %s%s", (stream == stderr) ? "ERROR: " : "" , buf);
+ fflush(stream);
+}
+
+void
+clean_exit(int code)
+{
+ if (mailslot_server != INVALID_HANDLE_VALUE) {
+ CloseHandle(mailslot_server);
+ }
+
+ exit(code);
+}
+
+void
+usage(void)
+{
+ printf("Usage: yup-comm-client CMD\n");
+ printf(" CMD command for yup-comm-server\n");
+ clean_exit(1);
+}
+
+int
+main(int argc, char ** argv)
+{
+ if (argc != 2) {
+ print_msg(stderr, "Missing argument\n");
+ usage();
+ } if (strlen(argv[1]) > MAILSLOT_MAX_MSG_SIZE) {
+ print_msg(stderr, "The command is too long (> 400 characters)\n");
+ clean_exit(1);
+ }
+
+ if ((mailslot_client = mailslot_create(MAILSLOT_CLIENT_NAME)) == INVALID_HANDLE_VALUE) {
+ print_msg(stderr, "Cannot create mailslot for receiving reply from the server\n");
+ clean_exit(1);
+ }
+
+ if ((mailslot_server = mailslot_connect(MAILSLOT_SERVER_NAME)) == INVALID_HANDLE_VALUE) {
+ print_msg(stderr, "Cannot open server's mailslot\n");
+ clean_exit(1);
+ }
+
+ if (mailslot_write(mailslot_server, argv[1], strlen(argv[1]) + 1U) != 0) {
+ print_msg(stderr, "Cannot send command to the server\n");
+ clean_exit(1);
+ }
+
+ print_msg(stdout, "Waiting for reply from the server ...\n");
+
+ char c;
+ mailslot_read(mailslot_client, &c, sizeof(c));
+
+ clean_exit(0);
+}
diff --git a/testing/yup-comm/yup-comm-client.exe b/testing/yup-comm/yup-comm-client.exe
new file mode 100644
index 0000000..99ace18
--- /dev/null
+++ b/testing/yup-comm/yup-comm-client.exe
Binary files differ
diff --git a/testing/yup-comm/yup-comm-server.c b/testing/yup-comm/yup-comm-server.c
new file mode 100644
index 0000000..427ccbb
--- /dev/null
+++ b/testing/yup-comm/yup-comm-server.c
@@ -0,0 +1,198 @@
+/* Author: Jan Sucan */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include "comm/serial_port.h"
+#include "comm/bt.h"
+#include "protocol/data.h"
+#include "mailslot.h"
+
+#define COM_PORT_OPEN_TRIES 4U
+#define MAILSLOT_SERVER_NAME "\\\\.\\Mailslot\\yup-comm-server"
+#define MAILSLOT_CLIENT_NAME "\\\\.\\Mailslot\\yup-comm-client"
+#define MAILSLOT_MAX_MSG_SIZE 400
+
+HANDLE serial_port;
+HANDLE mailslot_server = INVALID_HANDLE_VALUE;
+HANDLE mailslot_client = INVALID_HANDLE_VALUE;
+
+int
+send_byte_to_client(char byte, bool check_errors)
+{
+ /* Command received, client is alive, open his mailslot */
+ if ((mailslot_client = mailslot_connect(MAILSLOT_CLIENT_NAME)) == INVALID_HANDLE_VALUE) {
+ return (check_errors) ? -1 : 0;
+ }
+
+ /* Inform possible client about return value */
+ if (mailslot_write(mailslot_client, &byte, sizeof(byte)) != 0) {
+ return (check_errors) ? -1 : 0;
+ }
+
+ CloseHandle(mailslot_client);
+ return 0;
+}
+
+void
+print_msg(FILE * stream, const char * format, ...)
+{
+ char buf[1024];
+
+ va_list args;
+ va_start(args, format);
+ vsnprintf(buf, 1024, format, args);
+ va_end(args);
+
+ fprintf(stream, " yup-comm-server: %s%s", (stream == stderr) ? "ERROR: " : "" , buf);
+ fflush(stream);
+}
+
+void
+clean_exit(int code)
+{
+ if (serial_port != INVALID_HANDLE_VALUE) {
+ serial_port_close(serial_port);
+ }
+ print_msg(stdout, "COM port closed\n");
+
+ send_byte_to_client(0, false);
+
+ exit(code);
+}
+
+void
+usage(void)
+{
+ printf("Usage: yup-comm-server COMPORT\n");
+ printf(" COMPORT name of the COM port to use (COM0, COM1, ...)\n");
+ clean_exit(1);
+}
+
+void
+execute_yup_cmd(FILE * const in_file, FILE * const out_file)
+{
+ char command[2048U];
+ const size_t command_size = fread(command, sizeof(char), sizeof(command), in_file);
+
+ data_send((uint8_t *) command, command_size);
+
+ char reply[2048U];
+ size_t reply_size;
+
+ data_receive((uint8_t *) reply, sizeof(reply), &reply_size);
+
+ fwrite(reply, sizeof(char), reply_size, out_file);
+}
+
+void
+process_execute_cmd(const char * const in_path, const char * const out_path)
+{
+ FILE * const in_file = fopen(in_path, "rb");
+
+ if (in_file == NULL) {
+ print_msg(stderr, "Cannot open file with the command data: %s: %s\n", in_path, strerror(errno));
+ clean_exit(1);
+ }
+
+ FILE * const out_file = fopen(out_path, "wb");
+
+ if (out_file == NULL) {
+ print_msg(stderr, "Cannot open file for data of reply: %s: %s\n", out_path, strerror(errno));
+ clean_exit(1);
+ }
+
+ print_msg(stdout, "%s --> ", in_path);
+
+ execute_yup_cmd(in_file, out_file);
+
+ printf("%s\n", out_path);
+ fflush(stdout);
+
+ fclose(in_file);
+ fclose(out_file);
+}
+
+int
+main(int argc, char ** argv)
+{
+ if (argc != 2) {
+ print_msg(stderr, "Missing argument\n");
+ usage();
+ }
+
+ /* Process commands from mailslot */
+ if ((mailslot_server = mailslot_create(MAILSLOT_SERVER_NAME)) == INVALID_HANDLE_VALUE) {
+ print_msg(stderr, "Cannot create mailslot for receiving commands\n");
+ clean_exit(1);
+ }
+
+ int retry_delay = 4;
+
+ for (int i = 0; i < COM_PORT_OPEN_TRIES; ++i) {
+ if (i == 0) {
+ print_msg(stdout, "opening port %s\n", argv[1]);
+ } else {
+ print_msg(stdout, "retrying to open port %s (try %d/%d)\n", argv[1], i + 1, COM_PORT_OPEN_TRIES);
+ sleep(retry_delay);
+ }
+ if ((serial_port = serial_port_open(argv[1])) != INVALID_HANDLE_VALUE) {
+ break;
+ }
+ retry_delay += 2;
+ }
+ if (serial_port == INVALID_HANDLE_VALUE) {
+ print_msg(stderr, "Cannot open %s port\n", argv[1]);
+ clean_exit(1);
+ } else {
+ print_msg(stdout, "%s port opened\n", argv[1]);
+ }
+
+ /* Initialize COBS layer over the COM port */
+ bt_init(serial_port);
+
+ int end = 0;
+
+ while (!end) {
+ char line[MAILSLOT_MAX_MSG_SIZE];
+
+ /* Receive command from mailslot */
+ size_t line_chars = mailslot_read(mailslot_server, line, MAILSLOT_MAX_MSG_SIZE);
+
+ /* Remove line delimiter */
+ if (line_chars > 0) {
+ line[--line_chars] = '\0';
+ }
+
+ if (line_chars == 0) {
+ /* The line is empty */
+ continue;
+ }
+
+ char * delim;
+
+ if (!strcasecmp(line,"EXIT")) {
+ end = 1;
+ } else if (!strcasecmp(line,"PING")) {
+ ;
+ } else if ((delim = strstr(line, ",")) != NULL) {
+ /* It is either Sleep or Execute command */
+ *delim = '\0';
+ process_execute_cmd(line, delim + 1U);
+ } else {
+ print_msg(stderr, "Unknown command '%s'\n", line);
+ clean_exit(1);
+ }
+
+ /* Command successful */
+ if (send_byte_to_client(0, true) != 0) {
+ print_msg(stderr, "Cannot send message to client's mailslot\n");
+ clean_exit(1);
+ }
+ }
+
+ clean_exit(0);
+}
diff --git a/testing/yup-comm/yup-comm-server.exe b/testing/yup-comm/yup-comm-server.exe
new file mode 100644
index 0000000..bc42c55
--- /dev/null
+++ b/testing/yup-comm/yup-comm-server.exe
Binary files differ