Contiki 3.x
codeprop.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2005, Swedish Institute of Computer Science
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  *
31  */
32 
33 /**
34  *
35  * \file
36  * Code propagation and storage.
37  * \author
38  * Adam Dunkels <adam@sics.se>
39  *
40  * This file implements a simple form of code propagation, which
41  * allows a binary program to be downloaded and propagated throughout
42  * a network of devices.
43  *
44  * Features:
45  *
46  * Commands: load code, start code
47  * Point-to-point download over TCP
48  * Point-to-multipoint delivery over UDP broadcasts
49  * Versioning of code modules
50  *
51  * Procedure:
52  *
53  * 1. Receive code over TCP
54  * 2. Send code packets over UDP
55  *
56  * When a code packet is deemed to be missed, a NACK is sent. If a
57  * NACK is received, the sending restarts at the point in the
58  * binary where the NACK pointed to. (This is *not* very efficient,
59  * but simple to implement...)
60  *
61  * States:
62  *
63  * Receiving code header -> receiving code -> sending code
64  *
65  */
66 
67 #include "contiki.h"
68 #include "sys/clock.h"
69 
70 #include "loader/elfloader.h"
71 #include "net/ip/tcpip.h"
72 
73 #include "dev/eeprom.h"
74 #include "dev/leds.h"
75 
76 #include "lib/random.h"
77 
78 #include "codeprop.h"
79 
80 void codeprop_set_rate(clock_time_t time);
81 
82 /*static int random_rand(void) { return 1; }*/
83 
84 #if 0
85 #define PRINTF(x) printf x
86 #else
87 #define PRINTF(x)
88 #endif
89 
90 #define START_TIMEOUT 12 * CLOCK_SECOND
91 #define MISS_NACK_TIMEOUT (CLOCK_SECOND / 8) * (random_rand() % 8)
92 #define HIT_NACK_TIMEOUT (CLOCK_SECOND / 8) * (8 + random_rand() % 16)
93 #define NACK_REXMIT_TIMEOUT CLOCK_SECOND * (4 + random_rand() % 4)
94 
95 #define WAITING_TIME CLOCK_SECOND * 10
96 
97 #define NUM_SEND_DUPLICATES 2
98 
99 #define UDPHEADERSIZE 8
100 #define UDPDATASIZE 32
101 
102 struct codeprop_udphdr {
103  uint16_t id;
104  uint16_t type;
105 #define TYPE_DATA 0x0001
106 #define TYPE_NACK 0x0002
107  uint16_t addr;
108  uint16_t len;
109  uint8_t data[UDPDATASIZE];
110 };
111 
112 static void uipcall(void *state);
113 
114 PROCESS(codeprop_process, "Code propagator");
115 
116 struct codeprop_state {
117  uint8_t state;
118 #define STATE_NONE 0
119 #define STATE_RECEIVING_TCPDATA 1
120 #define STATE_RECEIVING_UDPDATA 2
121 #define STATE_SENDING_UDPDATA 3
122  uint16_t count;
123  uint16_t addr;
124  uint16_t len;
125  uint16_t id;
126  struct etimer sendtimer;
127  struct timer nacktimer, timer, starttimer;
128  uint8_t received;
129  uint8_t send_counter;
130  struct pt tcpthread_pt;
131  struct pt udpthread_pt;
132  struct pt recv_udpthread_pt;
133 };
134 
135 static struct uip_udp_conn *udp_conn;
136 
137 static struct codeprop_state s;
138 
139 process_event_t codeprop_event_quit;
140 
141 void system_log(char *msg);
142 
143 static clock_time_t send_time;
144 
145 #define CONNECTION_TIMEOUT (30 * CLOCK_SECOND)
146 
147 
148 enum {
149  EVENT_START_PROGRAM
150 };
151 /*---------------------------------------------------------------------*/
152 void
153 codeprop_set_rate(clock_time_t time)
154 {
155  send_time = time;
156 }
157 /*---------------------------------------------------------------------*/
158 PROCESS_THREAD(codeprop_process, ev, data)
159 {
160  PROCESS_BEGIN();
161 
162  s.id = 0/*random_rand()*/;
163 
164  send_time = CLOCK_SECOND/4;
165 
166  PT_INIT(&s.udpthread_pt);
167  PT_INIT(&s.recv_udpthread_pt);
168 
169  tcp_listen(UIP_HTONS(CODEPROP_DATA_PORT));
170 
171  udp_conn = udp_broadcast_new(UIP_HTONS(CODEPROP_DATA_PORT), NULL);
172 
173  codeprop_event_quit = process_alloc_event();
174 
175  s.state = STATE_NONE;
176  s.received = 0;
177  s.addr = 0;
178  s.len = 0;
179 
180  while(1) {
181 
182  PROCESS_YIELD();
183 
184  if(ev == EVENT_START_PROGRAM) {
185  /* First kill old program. */
186  elfloader_unload();
187  elfloader_load(EEPROMFS_ADDR_CODEPROP);
188  } else if(ev == tcpip_event) {
189  uipcall(data);
190  } else if(ev == PROCESS_EVENT_TIMER) {
191  tcpip_poll_udp(udp_conn);
192  }
193  }
194 
195  PROCESS_END();
196 }
197 /*---------------------------------------------------------------------*/
198 static uint16_t
199 send_udpdata(struct codeprop_udphdr *uh)
200 {
201  uint16_t len;
202 
203  uh->type = UIP_HTONS(TYPE_DATA);
204  uh->addr = uip_htons(s.addr);
205  uh->id = uip_htons(s.id);
206 
207  if(s.len - s.addr > UDPDATASIZE) {
208  len = UDPDATASIZE;
209  } else {
210  len = s.len - s.addr;
211  }
212 
213  eeprom_read(EEPROMFS_ADDR_CODEPROP + s.addr,
214  &uh->data[0], len);
215 
216  uh->len = uip_htons(s.len);
217 
218  PRINTF(("codeprop: sending packet from address 0x%04x\n", s.addr));
219  uip_udp_send(len + UDPHEADERSIZE);
220 
221  return len;
222 }
223 /*---------------------------------------------------------------------*/
224 static
225 PT_THREAD(send_udpthread(struct pt *pt))
226 {
227  int len;
228  struct codeprop_udphdr *uh = (struct codeprop_udphdr *)uip_appdata;
229 
230 
231  PT_BEGIN(pt);
232 
233  while(1) {
234  PT_WAIT_UNTIL(pt, s.state == STATE_SENDING_UDPDATA);
235 
236  for(s.addr = 0; s.addr < s.len; ) {
237  len = send_udpdata(uh);
238  s.addr += len;
239 
240  etimer_set(&s.sendtimer, CLOCK_SECOND/4);
241  do {
242  PT_WAIT_UNTIL(pt, uip_newdata() || etimer_expired(&s.sendtimer));
243 
244  if(uip_newdata()) {
245  if(uh->type == UIP_HTONS(TYPE_NACK)) {
246  PRINTF(("send_udpthread: got NACK for address 0x%x (now 0x%x)\n",
247  uip_htons(uh->addr), s.addr));
248  /* Only accept a NACK if it points to a lower byte. */
249  if(uip_htons(uh->addr) <= s.addr) {
250  s.addr = uip_htons(uh->addr);
251  }
252  }
253  PT_YIELD(pt);
254  }
255  } while(!etimer_expired(&s.sendtimer));
256  }
257 
258  s.state = STATE_NONE;
259 
260  process_post(PROCESS_BROADCAST, codeprop_event_quit, (process_data_t)NULL);
261  }
262  PT_END(pt);
263 }
264 /*---------------------------------------------------------------------*/
265 static void
266 send_nack(struct codeprop_udphdr *uh, unsigned short addr)
267 {
268  uh->type = UIP_HTONS(TYPE_NACK);
269  uh->addr = uip_htons(addr);
270  uip_udp_send(UDPHEADERSIZE);
271 }
272 /*---------------------------------------------------------------------*/
273 static
274 PT_THREAD(recv_udpthread(struct pt *pt))
275 {
276  int len;
277  struct codeprop_udphdr *uh = (struct codeprop_udphdr *)uip_appdata;
278 
279  /* if(uip_newdata()) {
280  PRINTF(("recv_udpthread: id %d uh->id %d\n", s.id, uip_htons(uh->id)));
281  }*/
282 
283  PT_BEGIN(pt);
284 
285  while(1) {
286 
287  do {
288  PT_WAIT_UNTIL(pt, uip_newdata() &&
289  uh->type == UIP_HTONS(TYPE_DATA) &&
290  uip_htons(uh->id) > s.id);
291 
292  if(uip_htons(uh->addr) != 0) {
293  s.addr = 0;
294  send_nack(uh, 0);
295  }
296 
297  } while(uip_htons(uh->addr) != 0);
298 
299  leds_on(LEDS_YELLOW);
300 
301  s.addr = 0;
302  s.id = uip_htons(uh->id);
303  s.len = uip_htons(uh->len);
304 
305  timer_set(&s.timer, CONNECTION_TIMEOUT);
306  process_post(PROCESS_BROADCAST, codeprop_event_quit, (process_data_t)NULL);
307 
308  while(s.addr < s.len) {
309 
310  if(uip_htons(uh->addr) == s.addr) {
311  leds_blink();
312  len = uip_datalen() - UDPHEADERSIZE;
313  if(len > 0) {
314  eeprom_write(EEPROMFS_ADDR_CODEPROP + s.addr,
315  &uh->data[0], len);
316  PRINTF(("Saved %d bytes at address %d, %d bytes left\n",
317  uip_datalen() - UDPHEADERSIZE, s.addr,
318  s.len - s.addr));
319 
320  s.addr += len;
321  }
322 
323  } else if(uip_htons(uh->addr) > s.addr) {
324  PRINTF(("sending nack since 0x%x != 0x%x\n", uip_htons(uh->addr), s.addr));
325  send_nack(uh, s.addr);
326  }
327 
328  if(s.addr < s.len) {
329 
330  /* timer_set(&s.nacktimer, NACK_TIMEOUT);*/
331 
332  do {
333  timer_set(&s.nacktimer, HIT_NACK_TIMEOUT);
334  PT_YIELD_UNTIL(pt, timer_expired(&s.nacktimer) ||
335  (uip_newdata() &&
336  uh->type == UIP_HTONS(TYPE_DATA) &&
337  uip_htons(uh->id) == s.id));
338  if(timer_expired(&s.nacktimer)) {
339  send_nack(uh, s.addr);
340  }
341  } while(timer_expired(&s.nacktimer));
342  }
343 
344  }
345 
346  leds_off(LEDS_YELLOW);
347  /* printf("Received entire bunary over udr\n");*/
348  process_post(PROCESS_BROADCAST, codeprop_event_quit, (process_data_t)NULL);
349  process_post(&codeprop_process, EVENT_START_PROGRAM, NULL);
350  PT_EXIT(pt);
351  }
352 
353  PT_END(pt);
354 }
355 /*---------------------------------------------------------------------*/
356 static
357 PT_THREAD(recv_tcpthread(struct pt *pt))
358 {
359  uint8_t *dataptr;
360  struct codeprop_tcphdr *th;
361  int datalen = uip_datalen();
362 
363  PT_BEGIN(pt);
364 
365  while(1) {
366 
368 
369  s.state = STATE_RECEIVING_TCPDATA;
370 
371  s.addr = 0;
372  s.count = 0;
373  process_post(PROCESS_BROADCAST, codeprop_event_quit, (process_data_t)NULL);
374 
375 
376  /* Read the header. */
377  PT_WAIT_UNTIL(pt, uip_newdata() && uip_datalen() > 0);
378  dataptr = uip_appdata;
379 
380  if(uip_datalen() < sizeof(struct codeprop_tcphdr)) {
381  PRINTF(("codeprop: header not found in first tcp segment\n"));
382  uip_abort();
383  }
384  th = (struct codeprop_tcphdr *)uip_appdata;
385  s.len = uip_htons(th->len);
386  s.addr = 0;
387  uip_appdata += sizeof(struct codeprop_tcphdr);
388  datalen = uip_datalen() - sizeof(struct codeprop_tcphdr);
389 
390  /* Read the rest of the data. */
391  do {
392  if(datalen > 0) {
393  /* printf("Got %d bytes\n", uip_len);*/
394  eeprom_write(EEPROMFS_ADDR_CODEPROP + s.addr,
395  uip_appdata,
396  datalen);
397  s.addr += datalen;
398  }
399  if(s.addr < s.len) {
401  }
402  } while(s.addr < s.len);
403 
404  /* Print out the "ok" message. */
405  do {
406  uip_send("ok\r\n", 4);
407  PT_WAIT_UNTIL(pt, uip_acked() || uip_rexmit() || uip_closed());
408  } while(uip_rexmit());
409 
410  /* Close the connection. */
411  uip_close();
412 
413  ++s.id;
414  s.state = STATE_SENDING_UDPDATA;
415  tcpip_poll_udp(udp_conn);
416 
417  codeprop_start_program();
418 
419  PT_WAIT_UNTIL(pt, s.state != STATE_SENDING_UDPDATA);
420  /* printf("recv_tcpthread: unblocked\n");*/
421  }
422 
423  PT_END(pt);
424 }
425 /*---------------------------------------------------------------------*/
426 void
427 codeprop_start_broadcast(unsigned int len)
428 {
429  s.addr = 0;
430  s.len = len;
431  ++s.id;
432  s.state = STATE_SENDING_UDPDATA;
433  tcpip_poll_udp(udp_conn);
434 }
435 /*---------------------------------------------------------------------*/
436 void
437 codeprop_start_program(void)
438 {
439  process_post(PROCESS_BROADCAST, codeprop_event_quit, (process_data_t)NULL);
440  process_post(&codeprop_process, EVENT_START_PROGRAM, NULL);
441 }
442 /*---------------------------------------------------------------------*/
443 static void
444 uipcall(void *state)
445 {
446  if(uip_udpconnection()) {
447  recv_udpthread(&s.recv_udpthread_pt);
448  send_udpthread(&s.udpthread_pt);
449  } else {
450  if(uip_conn->lport == UIP_HTONS(CODEPROP_DATA_PORT)) {
451  if(uip_connected()) {
452 
453  if(state == NULL) {
454  s.addr = 0;
455  s.count = 0;
456  PT_INIT(&s.tcpthread_pt);
457  process_poll(&codeprop_process);
458  tcp_markconn(uip_conn, &s);
459  process_post(PROCESS_BROADCAST, codeprop_event_quit,
460  (process_data_t)NULL);
461  } else {
462  PRINTF(("codeprop: uip_connected() and state != NULL\n"));
463  uip_abort();
464  }
465  }
466  recv_tcpthread(&s.tcpthread_pt);
467 
468 
469  if(uip_closed() || uip_aborted() || uip_timedout()) {
470  PRINTF(("codeprop: connection down\n"));
471  tcp_markconn(uip_conn, NULL);
472  }
473  }
474  }
475 }
476 /*---------------------------------------------------------------------*/
void eeprom_read(eeprom_addr_t addr, unsigned char *buf, int size)
Read data from the EEPROM.
Definition: eeprom.c:49
int etimer_expired(struct etimer *et)
Check if an event timer has expired.
Definition: etimer.c:205
void process_poll(struct process *p)
Request a process to be polled.
Definition: process.c:371
A timer.
Definition: timer.h:86
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120
Representation of a uIP TCP connection.
Definition: uip.h:1336
#define PT_YIELD(pt)
Yield from the current protothread.
Definition: pt.h:289
Header for the Contiki/uIP interface.
#define uip_aborted()
Has the connection been aborted by the other end?
Definition: uip.h:781
CCIF void uip_send(const void *data, int len)
Send data on the current connection.
Definition: uip6.c:2310
#define uip_newdata()
Is new incoming data available?
Definition: uip.h:738
void timer_set(struct timer *t, clock_time_t interval)
Set a timer.
Definition: timer.c:64
uint16_t lport
The local TCP port, in network byte order.
Definition: uip.h:1339
int elfloader_load(int fd)
Load and relocate an ELF file.
Definition: elfloader.c:339
#define NULL
The null pointer.
#define PT_INIT(pt)
Initialize a protothread.
Definition: pt.h:79
process_event_t process_alloc_event(void)
Allocate a global event number.
Definition: process.c:93
#define uip_udp_send(len)
Send a UDP datagram of length len on the current connection.
Definition: uip.h:901
#define UIP_HTONS(n)
Convert 16-bit quantity from host byte order to network byte order.
Definition: uip.h:1238
int process_post(struct process *p, process_event_t ev, process_data_t data)
Post an asynchronous event.
Definition: process.c:322
CCIF void tcpip_poll_udp(struct uip_udp_conn *conn)
Cause a specified UDP connection to be polled.
void eeprom_write(eeprom_addr_t addr, unsigned char *buf, int size)
Write a buffer into EEPROM.
Definition: eeprom.c:42
#define PT_THREAD(name_args)
Declaration of a protothread.
Definition: pt.h:99
#define uip_acked()
Has previously sent data been acknowledged?
Definition: uip.h:749
#define LEDS_YELLOW
LED2 (Yellow) -&gt; PC1.
Definition: board.h:80
#define uip_connected()
Has the connection just been connected?
Definition: uip.h:761
#define PT_WAIT_UNTIL(pt, condition)
Block and wait until condition is true.
Definition: pt.h:147
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition: process.h:273
EEPROM functions.
#define PROCESS_END()
Define the end of a process.
Definition: process.h:131
struct uip_udp_conn * udp_broadcast_new(uint16_t port, void *appstate)
Create a new UDP broadcast connection.
#define uip_abort()
Abort the current connection.
Definition: uip.h:682
CCIF uint16_t uip_htons(uint16_t val)
Convert a 16-bit quantity from host byte order to network byte order.
Definition: uip6.c:2298
#define PT_BEGIN(pt)
Declare the start of a protothread inside the C function implementing the protothread.
Definition: pt.h:114
#define uip_udpconnection()
Is the current connection a UDP connection?
Definition: uip.h:727
#define PROCESS(name, strname)
Declare a process.
Definition: process.h:307
#define uip_close()
Close the current connection.
Definition: uip.h:671
#define uip_datalen()
The length of any incoming data that is currently available (if available) in the uip_appdata buffer...
Definition: uip.h:651
#define PT_YIELD_UNTIL(pt, cond)
Yield from the protothread until a condition occurs.
Definition: pt.h:309
process_event_t tcpip_event
The uIP event.
Definition: tcpip.c:75
#define uip_timedout()
Has the connection timed out?
Definition: uip.h:791
#define PT_END(pt)
Declare the end of a protothread.
Definition: pt.h:126
#define PROCESS_YIELD()
Yield the currently running process.
Definition: process.h:164
void etimer_set(struct etimer *et, clock_time_t interval)
Set an event timer.
Definition: etimer.c:177
CCIF void tcp_listen(uint16_t port)
Open a TCP port.
#define uip_closed()
Has the connection been closed by the other end?
Definition: uip.h:771
Header file for the Contiki ELF loader.
Representation of a uIP UDP connection.
Definition: uip.h:1394
int timer_expired(struct timer *t)
Check if a timer has expired.
Definition: timer.c:121
A timer.
Definition: etimer.h:76
void leds_blink(void)
Blink all LEDs.
Definition: leds.c:79
#define PT_EXIT(pt)
Exit the protothread.
Definition: pt.h:245
uip_appdata
Pointer to the application data in the packet buffer.
Definition: tcp_loader.c:74
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82
#define uip_rexmit()
Do we need to retransmit previously data?
Definition: uip.h:803