Contiki 3.x
codeprop-otf.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 <stdio.h>
68 
69 #include "contiki-net.h"
70 #include "cfs/cfs.h"
71 #include "codeprop-otf.h"
72 #include "loader/elfloader-otf.h"
73 #include <string.h>
74 
75 static const char *err_msgs[] =
76  {"OK\r\n", "Bad ELF header\r\n", "No symtab\r\n", "No strtab\r\n",
77  "No text\r\n", "Symbol not found\r\n", "Segment not found\r\n",
78  "No startpoint\r\n", "Unhandled relocation\r\n",
79  "Relocation out of range\r\n", "Relocations not sorted\r\n",
80  "Input error\r\n" , "Ouput error\r\n" };
81 
82 #define CODEPROP_DATA_PORT 6510
83 
84 /*static int random_rand(void) { return 1; }*/
85 
86 #if 0
87 #define PRINTF(x) printf x
88 #else
89 #define PRINTF(x)
90 #endif
91 
92 #define START_TIMEOUT 12 * CLOCK_SECOND
93 #define MISS_NACK_TIMEOUT (CLOCK_SECOND / 8) * (random_rand() % 8)
94 #define HIT_NACK_TIMEOUT (CLOCK_SECOND / 8) * (8 + random_rand() % 16)
95 #define NACK_REXMIT_TIMEOUT CLOCK_SECOND * (4 + random_rand() % 4)
96 
97 #define WAITING_TIME CLOCK_SECOND * 10
98 
99 #define NUM_SEND_DUPLICATES 2
100 
101 #define UDPHEADERSIZE 8
102 #define UDPDATASIZE 32
103 
104 struct codeprop_udphdr {
105  uint16_t id;
106  uint16_t type;
107 #define TYPE_DATA 0x0001
108 #define TYPE_NACK 0x0002
109  uint16_t addr;
110  uint16_t len;
111  uint8_t data[UDPDATASIZE];
112 };
113 
114 struct codeprop_tcphdr {
115  uint16_t len;
116  uint16_t pad;
117 };
118 
119 static void uipcall(void *state);
120 
121 PROCESS(codeprop_process, "Code propagator");
122 
123 struct codeprop_state {
124  uint8_t state;
125 #define STATE_NONE 0
126 #define STATE_RECEIVING_TCPDATA 1
127 #define STATE_RECEIVING_UDPDATA 2
128 #define STATE_SENDING_UDPDATA 3
129  uint16_t count;
130  uint16_t addr;
131  uint16_t len;
132  uint16_t id;
133  struct etimer sendtimer;
134  struct timer nacktimer, timer, starttimer;
135  uint8_t received;
136  uint8_t send_counter;
137  struct pt tcpthread_pt;
138  struct pt udpthread_pt;
139  struct pt recv_udpthread_pt;
140 };
141 
142 static int fd;
143 
144 static struct uip_udp_conn *udp_conn;
145 
146 static struct codeprop_state s;
147 
148 void system_log(char *msg);
149 
150 static clock_time_t send_time;
151 
152 #define CONNECTION_TIMEOUT (30 * CLOCK_SECOND)
153 
154 /*---------------------------------------------------------------------*/
155 void
156 codeprop_set_rate(clock_time_t time)
157 {
158  send_time = time;
159 }
160 /*---------------------------------------------------------------------*/
161 PROCESS_THREAD(codeprop_process, ev, data)
162 {
163  PROCESS_BEGIN();
164 
165  elfloader_init();
166 
167  s.id = 0/*random_rand()*/;
168 
169  send_time = CLOCK_SECOND/4;
170 
171  PT_INIT(&s.udpthread_pt);
172  PT_INIT(&s.recv_udpthread_pt);
173 
174  tcp_listen(UIP_HTONS(CODEPROP_DATA_PORT));
175 
176  udp_conn = udp_broadcast_new(UIP_HTONS(CODEPROP_DATA_PORT), NULL);
177 
178  s.state = STATE_NONE;
179  s.received = 0;
180  s.addr = 0;
181  s.len = 0;
182 
183  fd = cfs_open("codeprop-image", CFS_READ | CFS_WRITE);
184 
185  while(1) {
186 
187  PROCESS_YIELD();
188 
189  if(ev == tcpip_event) {
190  uipcall(data);
191  } else if(ev == PROCESS_EVENT_TIMER) {
192  tcpip_poll_udp(udp_conn);
193  }
194  }
195 
196  PROCESS_END();
197 }
198 /*---------------------------------------------------------------------*/
199 static uint16_t
200 send_udpdata(struct codeprop_udphdr *uh)
201 {
202  uint16_t len;
203 
204  uh->type = UIP_HTONS(TYPE_DATA);
205  uh->addr = uip_htons(s.addr);
206  uh->id = uip_htons(s.id);
207 
208  if(s.len - s.addr > UDPDATASIZE) {
209  len = UDPDATASIZE;
210  } else {
211  len = s.len - s.addr;
212  }
213 
214  cfs_seek(fd, s.addr, CFS_SEEK_SET);
215  cfs_read(fd, (char*)&uh->data[0], len);
216  /* eeprom_read(EEPROMFS_ADDR_CODEPROP + s.addr,
217  &uh->data[0], len);*/
218 
219  uh->len = uip_htons(s.len);
220 
221  PRINTF(("codeprop: sending packet from address 0x%04x\n", s.addr));
222  uip_udp_send(len + UDPHEADERSIZE);
223 
224  return len;
225 }
226 /*---------------------------------------------------------------------*/
227 static
228 PT_THREAD(send_udpthread(struct pt *pt))
229 {
230  int len;
231  struct codeprop_udphdr *uh = (struct codeprop_udphdr *)uip_appdata;
232 
233 
234  PT_BEGIN(pt);
235 
236  while(1) {
237  PT_WAIT_UNTIL(pt, s.state == STATE_SENDING_UDPDATA);
238 
239  for(s.addr = 0; s.addr < s.len; ) {
240  len = send_udpdata(uh);
241  s.addr += len;
242 
243  etimer_set(&s.sendtimer, CLOCK_SECOND/4);
244  do {
245  PT_WAIT_UNTIL(pt, uip_newdata() || etimer_expired(&s.sendtimer));
246 
247  if(uip_newdata()) {
248  if(uh->type == UIP_HTONS(TYPE_NACK)) {
249  PRINTF(("send_udpthread: got NACK for address 0x%x (now 0x%x)\n",
250  uip_htons(uh->addr), s.addr));
251  /* Only accept a NACK if it points to a lower byte. */
252  if(uip_htons(uh->addr) <= s.addr) {
253  /* beep();*/
254  s.addr = uip_htons(uh->addr);
255  }
256  }
257  PT_YIELD(pt);
258  }
259  } while(!etimer_expired(&s.sendtimer));
260  }
261 
262  s.state = STATE_NONE;
263 
264 /* process_post(PROCESS_BROADCAST, codeprop_event_quit, (process_data_t)NULL); */
265  }
266  PT_END(pt);
267 }
268 /*---------------------------------------------------------------------*/
269 static void
270 send_nack(struct codeprop_udphdr *uh, unsigned short addr)
271 {
272  uh->type = UIP_HTONS(TYPE_NACK);
273  uh->addr = uip_htons(addr);
274  uip_udp_send(UDPHEADERSIZE);
275 }
276 /*---------------------------------------------------------------------*/
277 static
278 PT_THREAD(recv_udpthread(struct pt *pt))
279 {
280  int len;
281  struct codeprop_udphdr *uh = (struct codeprop_udphdr *)uip_appdata;
282 
283  /* if(uip_newdata()) {
284  PRINTF(("recv_udpthread: id %d uh->id %d\n", s.id, uip_htons(uh->id)));
285  }*/
286 
287  PT_BEGIN(pt);
288 
289  while(1) {
290 
291  do {
292  PT_WAIT_UNTIL(pt, uip_newdata() &&
293  uh->type == UIP_HTONS(TYPE_DATA) &&
294  uip_htons(uh->id) > s.id);
295 
296  if(uip_htons(uh->addr) != 0) {
297  s.addr = 0;
298  send_nack(uh, 0);
299  }
300 
301  } while(uip_htons(uh->addr) != 0);
302 
303  /* leds_on(LEDS_YELLOW);
304  beep_down(10000);*/
305 
306  s.addr = 0;
307  s.id = uip_htons(uh->id);
308  s.len = uip_htons(uh->len);
309 
310  timer_set(&s.timer, CONNECTION_TIMEOUT);
311 /* process_post(PROCESS_BROADCAST, codeprop_event_quit, (process_data_t)NULL); */
312 
313  while(s.addr < s.len) {
314 
315  if(uip_htons(uh->addr) == s.addr) {
316  /* leds_blink();*/
317  len = uip_datalen() - UDPHEADERSIZE;
318  if(len > 0) {
319  /* eeprom_write(EEPROMFS_ADDR_CODEPROP + s.addr,
320  &uh->data[0], len);*/
321  cfs_seek(fd, s.addr, CFS_SEEK_SET);
322  cfs_write(fd, (char*)&uh->data[0], len);
323 
324  /* beep();*/
325  PRINTF(("Saved %d bytes at address %d, %d bytes left\n",
326  uip_datalen() - UDPHEADERSIZE, s.addr,
327  s.len - s.addr));
328 
329  s.addr += len;
330  }
331 
332  } else if(uip_htons(uh->addr) > s.addr) {
333  PRINTF(("sending nack since 0x%x != 0x%x\n", uip_htons(uh->addr), s.addr));
334  send_nack(uh, s.addr);
335  }
336 
337  if(s.addr < s.len) {
338 
339  /* timer_set(&s.nacktimer, NACK_TIMEOUT);*/
340 
341  do {
342  timer_set(&s.nacktimer, HIT_NACK_TIMEOUT);
343  PT_YIELD_UNTIL(pt, timer_expired(&s.nacktimer) ||
344  (uip_newdata() &&
345  uh->type == UIP_HTONS(TYPE_DATA) &&
346  uip_htons(uh->id) == s.id));
347  if(timer_expired(&s.nacktimer)) {
348  send_nack(uh, s.addr);
349  }
350  } while(timer_expired(&s.nacktimer));
351  }
352 
353  }
354 
355  /* leds_off(LEDS_YELLOW);
356  beep_quick(2);*/
357  /* printf("Received entire bunary over udr\n");*/
358  codeprop_start_program();
359  PT_EXIT(pt);
360  }
361 
362  PT_END(pt);
363 }
364 /*---------------------------------------------------------------------*/
365 
366 #define CODEPROP_TCPHDR_SIZE sizeof(struct codeprop_tcphdr)
367 
368 static
369 PT_THREAD(recv_tcpthread(struct pt *pt))
370 {
371  struct codeprop_tcphdr *th;
372  int datalen = uip_datalen();
373  PT_BEGIN(pt);
374 
375  while(1) {
376 
378 
379  codeprop_exit_program();
380 
381  s.state = STATE_RECEIVING_TCPDATA;
382 
383  s.addr = 0;
384  s.count = 0;
385 
386  /* Read the header. */
387  PT_WAIT_UNTIL(pt, uip_newdata() && uip_datalen() > 0);
388 
389  if(uip_datalen() < CODEPROP_TCPHDR_SIZE) {
390  PRINTF(("codeprop: header not found in first tcp segment\n"));
391  uip_abort();
392  }
393  th = (struct codeprop_tcphdr *)uip_appdata;
394  s.len = uip_htons(th->len);
395  s.addr = 0;
396  uip_appdata += CODEPROP_TCPHDR_SIZE;
397  datalen -= CODEPROP_TCPHDR_SIZE;
398 
399  /* Read the rest of the data. */
400  do {
401  if(datalen > 0) {
402  /* printf("Got %d bytes\n", datalen); */
403 
404  if (cfs_seek(fd, s.addr, CFS_SEEK_SET) != s.addr) {
405  PRINTF(("codeprop: seek in buffer file failed\n"));
406  uip_abort();
407  }
408 
409  if (cfs_write(fd, uip_appdata, datalen) != datalen) {
410  PRINTF(("codeprop: write to buffer file failed\n"));
411  uip_abort();
412  }
413  s.addr += datalen;
414  }
415  if(s.addr < s.len) {
417  }
418  } while(s.addr < s.len);
419 #if 1
420 
421  {
422  static int err;
423 
424  err = codeprop_start_program();
425 
426  /* Print out the "OK"/error message. */
427  do {
428  if (err >= 0 && err < sizeof(err_msgs)/sizeof(char*)) {
429  uip_send(err_msgs[err], strlen(err_msgs[err]));
430  } else {
431  uip_send("Unknown error\r\n", 15);
432  }
433  PT_WAIT_UNTIL(pt, uip_acked() || uip_rexmit() || uip_closed());
434  } while(uip_rexmit());
435 
436  /* Close the connection. */
437  uip_close();
438  }
439 #endif
440  ++s.id;
441  s.state = STATE_SENDING_UDPDATA;
442  tcpip_poll_udp(udp_conn);
443 
444  PT_WAIT_UNTIL(pt, s.state != STATE_SENDING_UDPDATA);
445  /* printf("recv_tcpthread: unblocked\n");*/
446  }
447 
448  PT_END(pt);
449 }
450 /*---------------------------------------------------------------------*/
451 void
452 codeprop_start_broadcast(unsigned int len)
453 {
454  s.addr = 0;
455  s.len = len;
456  ++s.id;
457  s.state = STATE_SENDING_UDPDATA;
458  tcpip_poll_udp(udp_conn);
459 }
460 /*---------------------------------------------------------------------*/
461 void
462 codeprop_exit_program(void)
463 {
465  autostart_exit(elfloader_autostart_processes);
466  }
467 }
468 /*---------------------------------------------------------------------*/
469 int
470 codeprop_start_program(void)
471 {
472  int err;
473 
474  codeprop_exit_program();
475 
476  err = elfloader_load(fd, codeprop_output);
477  if(err == ELFLOADER_OK) {
478  PRINTF(("codeprop: starting %s\n",
480  autostart_start(elfloader_autostart_processes);
481  }
482  return err;
483 }
484 /*---------------------------------------------------------------------*/
485 static void
486 uipcall(void *state)
487 {
488  if(uip_udpconnection()) {
489  recv_udpthread(&s.recv_udpthread_pt);
490  send_udpthread(&s.udpthread_pt);
491  } else {
492  if(uip_conn->lport == UIP_HTONS(CODEPROP_DATA_PORT)) {
493  if(uip_connected()) {
494 
495  if(state == NULL) {
496  s.addr = 0;
497  s.count = 0;
498  PT_INIT(&s.tcpthread_pt);
499  process_poll(&codeprop_process);
500  tcp_markconn(uip_conn, &s);
501 /* process_post(PROCESS_BROADCAST, codeprop_event_quit, */
502 /* (process_data_t)NULL); */
503  } else {
504  PRINTF(("codeprop: uip_connected() and state != NULL\n"));
505  uip_abort();
506  }
507  }
508  recv_tcpthread(&s.tcpthread_pt);
509 
510 
511  if(uip_closed() || uip_aborted() || uip_timedout()) {
512  PRINTF(("codeprop: connection down\n"));
513  tcp_markconn(uip_conn, NULL);
514  }
515  }
516  }
517 }
518 /*---------------------------------------------------------------------*/
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
void elfloader_init(void)
elfloader initialization function.
Definition: elfloader.c:317
int cfs_open(const char *name, int flags)
Open a file.
Definition: cfs-coffee.c:996
cfs_offset_t cfs_seek(int fd, cfs_offset_t offset, int whence)
Seek to a specified position in an open file.
Definition: cfs-coffee.c:1042
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120
#define CFS_WRITE
Specify that cfs_open() should open a file for writing.
Definition: cfs.h:104
Representation of a uIP TCP connection.
Definition: uip.h:1336
#define PT_YIELD(pt)
Yield from the current protothread.
Definition: pt.h:289
#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
#define CFS_READ
Specify that cfs_open() should open a file for reading.
Definition: cfs.h:90
#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
CCIF void tcpip_poll_udp(struct uip_udp_conn *conn)
Cause a specified UDP connection to be polled.
#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 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
#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
Header file for the Contiki ELF loader.
#define uip_close()
Close the current connection.
Definition: uip.h:671
struct process *const * elfloader_autostart_processes
A pointer to the processes loaded with elfloader_load().
Definition: elfloader.c:136
#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 ELFLOADER_OK
Return value from elfloader_load() indicating that loading worked.
Definition: elfloader.h:83
#define CFS_SEEK_SET
Specify that cfs_seek() should compute the offset from the beginning of the file. ...
Definition: cfs.h:127
#define uip_closed()
Has the connection been closed by the other end?
Definition: uip.h:771
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
#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
CFS header file.