aboutsummaryrefslogtreecommitdiff
path: root/arduino/ace_old/transport.c
blob: 986165e98a7c2713d565a0e41d559bf6bc243855 (plain) (blame)
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
#include "transport.h"
#include "link.h"

#include <stdlib.h>
#include <stdbool.h>
#include <avr/interrupt.h>

//ms
#define EXTRA_TIME 200
#define MAX_RESENDS 5
#define MAX_SEQ 255

#define next_seq(k) if (k < MAX_SEQ) k = k + 1; else k = 0

#define DATA 0x05
#define ACK 0x06


static void start_sending(message* msg, bool resend);
static void stop_sending();


typedef enum {
  RECEIVED_PACKET,
  TIMEOUT
} event_kind;

typedef struct {
  event_kind kind;
  packet* payload;
} event;

static void disassemble_packet(packet* p, uint8_t* seq, uint8_t* command, uint8_t** msg, uint16_t* message_length) {
  if (seq != NULL) *seq = p->data[0];
  if (command != NULL) *command = p->data[1];
  if (msg != NULL) *msg = &(p->data[2]);
  if (message_length != NULL) *message_length = p->length - 2;
}

static void assemble_packet(packet* p, uint8_t seq, uint8_t command, uint8_t* msg, uint16_t message_length) {
  p->data[0] = seq;
  p->data[1] = command;
  int i;
  for (i = 0; i < message_length; ++i) {
    p->data[i+2] = msg[i];
  }
  p->length = message_length + 2;
}

static packet* ack(uint8_t seq) {
  static uint8_t ack_buffer[] = {0, ACK};
  static packet ack_packet = {ack_buffer, 2};
  ack_buffer[0] = seq;
  return &ack_packet;
}

static volatile bool awaiting_ack = false;
static volatile uint8_t last_sent_seq = 0;
static volatile uint8_t resends = 0;
static volatile uint8_t last_sent_buffer[MAX_PACKET_SIZE]; 
static volatile packet last_sent = {last_sent_buffer, 0};

static void process_event(event* e) {
  static uint8_t last_received_seq = 0;
  
  switch(e->kind) {
    case RECEIVED_PACKET: {
      uint8_t seq;
      uint8_t cmd;
      static message msg;
      disassemble_packet(e->payload, &seq, &cmd, &msg.data, &msg.length);
      
      if (!awaiting_ack) {
        if (cmd == DATA) {
          if (last_received_seq != seq) {
            last_received_seq = seq;
            from_transport_layer(RECEIVED, &msg);
          }
          to_link_layer(ack(seq));
        }
        //ignore case in which an ack is received even though none is awaited
 
      } else { //waiting for an ack
        stop_sending();
        disassemble_packet(&last_sent, NULL, NULL, &msg.data, &msg.length);
        if (cmd == ACK && seq == last_sent_seq) {
          from_transport_layer(SEND_SUCCESS, &msg);
        } else {
          from_transport_layer(BAD_ACK, &msg);
        }
      }      
    } break;
    case TIMEOUT:
      if (resends > MAX_RESENDS) {
        message msg;
        disassemble_packet(&last_sent, NULL, NULL, &msg.data, &msg.length);
        stop_sending();
        from_transport_layer(NO_ACK, &msg);
      } else {
        start_sending(NULL, true);
      }
      break;
  }
}

static void start_sending(message* msg, bool resend) {  
  if (resend) {
    resends = resends + 1;
  } else {
    next_seq(last_sent_seq);
    assemble_packet(&last_sent, last_sent_seq, DATA, msg->data, msg->length);
    resends = 0;
    TIMSK1 |= (1 << OCIE1A);
  }
  
  awaiting_ack = true;
  to_link_layer(&last_sent);
}

static void stop_sending() {
  TIMSK1 &= ~(1 << OCIE1A);
  awaiting_ack = false;
}

static volatile uint32_t ticks;
static volatile uint32_t max_ticks;

void init_transport_layer(uint32_t timeout_millis) {
  cli();
  TCCR1A = 0; // set entire TCCR1A register to 0
  TCCR1B = 0; // same for TCCR1B
  // turn on CTC mode:
  TCCR1B |= (1 << WGM12);
  // set CS31 for 64 prescaler
  TCCR1B |= (1 << CS11);
  // set compare match register to desired timer count:
  OCR1A = F_CPU / 1000 / 64; // should be 250 for F_CPU=16Mhz and f = 1000Hz
  sei();
  
  //the timer should now interrupt every ms
  max_ticks = timeout_millis;
}

ISR(TIMER1_COMPA_vect) {
  static event e = {TIMEOUT, NULL};
  
  ticks = ticks + 1;
  if (ticks > max_ticks) {
    process_event(&e);
    ticks = 0;
  }
} 

void from_link_layer(packet* r) {
 static event e = {RECEIVED_PACKET, NULL};
 e.payload = r;
 process_event(&e);
}

void to_transport_layer(message* s) {
  if (!awaiting_ack)
    start_sending(s, false);
  else
    from_transport_layer(BUSY, s);
}