aboutsummaryrefslogtreecommitdiff
path: root/arduino/ace/arq.c
diff options
context:
space:
mode:
Diffstat (limited to 'arduino/ace/arq.c')
-rw-r--r--arduino/ace/arq.c122
1 files changed, 122 insertions, 0 deletions
diff --git a/arduino/ace/arq.c b/arduino/ace/arq.c
new file mode 100644
index 0000000..9d4a08c
--- /dev/null
+++ b/arduino/ace/arq.c
@@ -0,0 +1,122 @@
+#include "arq.h"
+
+#include <stdlib.h>
+/* Expected frame data for ARQ
+ *
+ * +-----+-----+------+
+ * | seq | cmd | data |
+ * +-----+-----+------+
+ *
+ * seq: sequence number of frame (1 byte)
+ * cmd: command id of frame (1 byte), currently either ACK or DATA
+ * data: actual data of frame (arbitraty length, respecting frame limitations)
+ *
+ * Note: frame checking, headers and footers are not handled by arq, see 'framing' instead
+ *
+*/
+
+#define MAX_RESENDS 5 //number of resends before a timeout is generated
+#define MAX_SEQ 255 //maximum sequence number of frames
+#define DATA 0x05
+#define ACK 0x06
+
+#define SEQ_INDEX 0
+#define CMD_INDEX 1
+#define MESSAGE_OFFSET 2
+#define MAX_MESSAGE_SIZE MAX_FRAME_SIZE - MESSAGE_OFFSET
+
+
+typedef enum {
+ RECEIVED_FRAME,
+ TIMEOUT
+} arq_event;
+
+static void send_ack(arq* a, uint8_t seq) {
+ uint8_t data[] = {seq, ACK};
+ a->sender(2, data);
+}
+
+static void process_event(arq* a, arq_event event, int16_t data_size, uint8_t* data) {
+ if (event == RECEIVED_FRAME) {
+ uint8_t seq = data[SEQ_INDEX];
+ uint8_t cmd = data[CMD_INDEX];
+ uint8_t* message = &(data[MESSAGE_OFFSET]);
+ int16_t message_size = data_size - MESSAGE_OFFSET;
+
+ if (!a->awaiting_ack) { //ready to receive
+ if (cmd == DATA) {
+ if (a->last_received_seq != seq) {
+ a->last_received_seq = seq;
+ a->event_handler(RECEIVED, message_size, message);
+ }
+ send_ack(a, seq);
+ }
+ //ignore case in which an ack is received even though none is awaited
+ } else { //awaiting ack
+ a->awaiting_ack = false;
+ a->stop_timer();
+
+ if (cmd == ACK && seq == a->last_sent_buffer[SEQ_INDEX]) {
+ a->event_handler(SEND_SUCCESS, a->last_sent_size - MESSAGE_OFFSET, &(a->last_sent_buffer[MESSAGE_OFFSET]));
+ } else {
+ a->event_handler(BAD_ACK, a->last_sent_size - MESSAGE_OFFSET, &(a->last_sent_buffer[MESSAGE_OFFSET]));
+ }
+ }
+
+ } else if (event == TIMEOUT) {
+ if (a->resends > MAX_RESENDS) {
+ a->awaiting_ack = false;
+ a->stop_timer();
+ a->event_handler(NO_ACK, a->last_sent_size - MESSAGE_OFFSET, &(a->last_sent_buffer[MESSAGE_OFFSET]));
+ } else {
+ a->resends += 1;
+ a->sender(a->last_sent_size, a->last_sent_buffer);
+ a->awaiting_ack = true;
+ a->start_timer();
+ }
+ }
+}
+
+void receive_frame(arq* a, int16_t size, uint8_t* data) {
+ process_event(a, RECEIVED_FRAME, size, data);
+}
+
+void timeout(arq* a) {
+ process_event(a, TIMEOUT, 0, NULL);
+}
+
+void send_message(arq* a, int16_t size, const uint8_t * const message) {
+ if (a->awaiting_ack) {
+ a->event_handler(BUSY, size, message);
+ return;
+ }
+
+ if (size > MAX_MESSAGE_SIZE) {
+ a->event_handler(SIZE_ERROR, size, message);
+ return;
+ }
+
+
+ a->last_sent_buffer[SEQ_INDEX] += 1; //increment seq
+ a->last_sent_buffer[CMD_INDEX] = DATA;
+ int i;
+ for (i = 0; i < size; ++i) {
+ a->last_sent_buffer[i + MESSAGE_OFFSET] = message[i];
+ }
+
+ a->last_sent_size = MESSAGE_OFFSET + size;
+
+ a->sender(a->last_sent_size, a->last_sent_buffer);
+ a->awaiting_ack = true;
+ a->resends = 0;
+ a->start_timer();
+}
+
+void init_arq(arq* a) {
+ a->last_sent_size = 0;
+ a->last_received_seq = 0;
+ a->resends = 0;
+ a->awaiting_ack = false;
+}
+
+