aboutsummaryrefslogtreecommitdiff
path: root/testing/yup-comm/protocol/frame.c
blob: 42c697ae507337068a4d9fc6415285310b2ca870 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
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);
}