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