diff options
| author | Jan Sucan <sucanjan@fit.cvut.cz> | 2019-06-04 14:34:27 +0200 |
|---|---|---|
| committer | Jan Sucan <sucanjan@fit.cvut.cz> | 2019-06-04 14:34:27 +0200 |
| commit | dc8703206e3f0f69605c56d0e1127f7e17f3476a (patch) | |
| tree | 166823a741dc420c10d54250cb53d1e3a6b74faf /testing/yup-comm | |
Initial commit
Diffstat (limited to 'testing/yup-comm')
| -rw-r--r-- | testing/yup-comm/Makefile | 27 | ||||
| -rw-r--r-- | testing/yup-comm/comm/bt.c | 176 | ||||
| -rw-r--r-- | testing/yup-comm/comm/bt.h | 30 | ||||
| -rw-r--r-- | testing/yup-comm/comm/serial_port.c | 144 | ||||
| -rw-r--r-- | testing/yup-comm/comm/serial_port.h | 16 | ||||
| -rw-r--r-- | testing/yup-comm/mailslot.c | 43 | ||||
| -rw-r--r-- | testing/yup-comm/mailslot.h | 9 | ||||
| -rw-r--r-- | testing/yup-comm/protocol/data.c | 312 | ||||
| -rw-r--r-- | testing/yup-comm/protocol/data.h | 16 | ||||
| -rw-r--r-- | testing/yup-comm/protocol/frame.c | 378 | ||||
| -rw-r--r-- | testing/yup-comm/protocol/frame.h | 56 | ||||
| -rw-r--r-- | testing/yup-comm/protocol/return_codes.h | 38 | ||||
| -rw-r--r-- | testing/yup-comm/utils/byte_buffer.h | 31 | ||||
| -rw-r--r-- | testing/yup-comm/utils/crc32q.c | 64 | ||||
| -rw-r--r-- | testing/yup-comm/utils/crc32q.h | 12 | ||||
| -rw-r--r-- | testing/yup-comm/yup-comm-client.c | 80 | ||||
| -rw-r--r-- | testing/yup-comm/yup-comm-client.exe | bin | 0 -> 148529 bytes | |||
| -rw-r--r-- | testing/yup-comm/yup-comm-server.c | 198 | ||||
| -rw-r--r-- | testing/yup-comm/yup-comm-server.exe | bin | 0 -> 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 Binary files differnew file mode 100644 index 0000000..99ace18 --- /dev/null +++ b/testing/yup-comm/yup-comm-client.exe 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 Binary files differnew file mode 100644 index 0000000..bc42c55 --- /dev/null +++ b/testing/yup-comm/yup-comm-server.exe |
