Contiki 3.x
codeprop-tmp.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-tmp.h"
72 #include "loader/elfloader.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" };
79 
80 #define CODEPROP_DATA_PORT 6510
81 
82 /*static int random_rand(void) { return 1; }*/
83 
84 #if 1
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 struct codeprop_tcphdr {
113  uint16_t len;
114  uint16_t pad;
115 };
116 
117 static void uipcall(void *state);
118 
119 PROCESS(codeprop_process, "Code propagator");
120 
121 struct codeprop_state {
122  uint8_t state;
123 #define STATE_NONE 0
124 #define STATE_RECEIVING_TCPDATA 1
125 #define STATE_RECEIVING_UDPDATA 2
126 #define STATE_SENDING_UDPDATA 3
127  uint16_t count;
128  uint16_t addr;
129  uint16_t len;
130  uint16_t id;
131  struct etimer sendtimer;
132  struct timer nacktimer, timer, starttimer;
133  uint8_t received;
134  uint8_t send_counter;
135  struct pt tcpthread_pt;
136  struct pt udpthread_pt;
137  struct pt recv_udpthread_pt;
138 };
139 
140 static int fd;
141 
142 static struct uip_udp_conn *udp_conn;
143 
144 static struct codeprop_state s;
145 
146 void system_log(char *msg);
147 
148 static clock_time_t send_time;
149 
150 #define CONNECTION_TIMEOUT (30 * CLOCK_SECOND)
151 
152 /*---------------------------------------------------------------------*/
153 void
154 codeprop_set_rate(clock_time_t time)
155 {
156  send_time = time;
157 }
158 /*---------------------------------------------------------------------*/
159 PROCESS_THREAD(codeprop_process, ev, data)
160 {
161  PROCESS_BEGIN();
162 
163  elfloader_init();
164 
165  s.id = 0/*random_rand()*/;
166 
167  send_time = CLOCK_SECOND/4;
168 
169  PT_INIT(&s.udpthread_pt);
170  PT_INIT(&s.recv_udpthread_pt);
171 
172  tcp_listen(UIP_HTONS(CODEPROP_DATA_PORT));
173 
174  udp_conn = udp_broadcast_new(UIP_HTONS(CODEPROP_DATA_PORT), NULL);
175 
176  s.state = STATE_NONE;
177  s.received = 0;
178  s.addr = 0;
179  s.len = 0;
180 
181  fd = cfs_open("codeprop-image", CFS_READ | CFS_WRITE);
182 
183  while(1) {
184 
185  PROCESS_YIELD();
186 
187  if(ev == tcpip_event) {
188  uipcall(data);
189  } else if(ev == PROCESS_EVENT_TIMER) {
190  tcpip_poll_udp(udp_conn);
191  }
192  }
193 
194  PROCESS_END();
195 }
196 /*---------------------------------------------------------------------*/
197 static uint16_t
198 send_udpdata(struct codeprop_udphdr *uh)
199 {
200  uint16_t len;
201 
202  uh->type = UIP_HTONS(TYPE_DATA);
203  uh->addr = uip_htons(s.addr);
204  uh->id = uip_htons(s.id);
205 
206  if(s.len - s.addr > UDPDATASIZE) {
207  len = UDPDATASIZE;
208  } else {
209  len = s.len - s.addr;
210  }
211 
212  cfs_seek(fd, s.addr, CFS_SEEK_SET);
213  cfs_read(fd, &uh->data[0], len);
214  /* eeprom_read(EEPROMFS_ADDR_CODEPROP + s.addr,
215  &uh->data[0], len);*/
216 
217  uh->len = uip_htons(s.len);
218 
219  PRINTF(("codeprop: sending packet from address 0x%04x\n", s.addr));
220  uip_udp_send(len + UDPHEADERSIZE);
221 
222  return len;
223 }
224 /*---------------------------------------------------------------------*/
225 static
226 PT_THREAD(send_udpthread(struct pt *pt))
227 {
228  int len;
229  struct codeprop_udphdr *uh = (struct codeprop_udphdr *)uip_appdata;
230 
231 
232  PT_BEGIN(pt);
233 
234  while(1) {
235  PT_WAIT_UNTIL(pt, s.state == STATE_SENDING_UDPDATA);
236 
237  for(s.addr = 0; s.addr < s.len; ) {
238  len = send_udpdata(uh);
239  s.addr += len;
240 
241  etimer_set(&s.sendtimer, CLOCK_SECOND/4);
242  do {
243  PT_WAIT_UNTIL(pt, uip_newdata() || etimer_expired(&s.sendtimer));
244 
245  if(uip_newdata()) {
246  if(uh->type == UIP_HTONS(TYPE_NACK)) {
247  PRINTF(("send_udpthread: got NACK for address 0x%x (now 0x%x)\n",
248  uip_htons(uh->addr), s.addr));
249  /* Only accept a NACK if it points to a lower byte. */
250  if(uip_htons(uh->addr) <= s.addr) {
251  /* beep();*/
252  s.addr = uip_htons(uh->addr);
253  }
254  }
255  PT_YIELD(pt);
256  }
257  } while(!etimer_expired(&s.sendtimer));
258  }
259 
260  s.state = STATE_NONE;
261 
262 /* process_post(PROCESS_BROADCAST, codeprop_event_quit, (process_data_t)NULL); */
263  }
264  PT_END(pt);
265 }
266 /*---------------------------------------------------------------------*/
267 static void
268 send_nack(struct codeprop_udphdr *uh, unsigned short addr)
269 {
270  uh->type = UIP_HTONS(TYPE_NACK);
271  uh->addr = uip_htons(addr);
272  uip_udp_send(UDPHEADERSIZE);
273 }
274 /*---------------------------------------------------------------------*/
275 static
276 PT_THREAD(recv_udpthread(struct pt *pt))
277 {
278  int len;
279  struct codeprop_udphdr *uh = (struct codeprop_udphdr *)uip_appdata;
280 
281  /* if(uip_newdata()) {
282  PRINTF(("recv_udpthread: id %d uh->id %d\n", s.id, uip_htons(uh->id)));
283  }*/
284 
285  PT_BEGIN(pt);
286 
287  while(1) {
288 
289  do {
290  PT_WAIT_UNTIL(pt, uip_newdata() &&
291  uh->type == UIP_HTONS(TYPE_DATA) &&
292  uip_htons(uh->id) > s.id);
293 
294  if(uip_htons(uh->addr) != 0) {
295  s.addr = 0;
296  send_nack(uh, 0);
297  }
298 
299  } while(uip_htons(uh->addr) != 0);
300 
301  /* leds_on(LEDS_YELLOW);
302  beep_down(10000);*/
303 
304  s.addr = 0;
305  s.id = uip_htons(uh->id);
306  s.len = uip_htons(uh->len);
307 
308  timer_set(&s.timer, CONNECTION_TIMEOUT);
309 /* process_post(PROCESS_BROADCAST, codeprop_event_quit, (process_data_t)NULL); */
310 
311  while(s.addr < s.len) {
312 
313  if(uip_htons(uh->addr) == s.addr) {
314  /* leds_blink();*/
315  len = uip_datalen() - UDPHEADERSIZE;
316  if(len > 0) {
317  /* eeprom_write(EEPROMFS_ADDR_CODEPROP + s.addr,
318  &uh->data[0], len);*/
319  cfs_seek(fd, s.addr, CFS_SEEK_SET);
320  cfs_write(fd, &uh->data[0], len);
321 
322  /* beep();*/
323  PRINTF(("Saved %d bytes at address %d, %d bytes left\n",
324  uip_datalen() - UDPHEADERSIZE, s.addr,
325  s.len - s.addr));
326 
327  s.addr += len;
328  }
329 
330  } else if(uip_htons(uh->addr) > s.addr) {
331  PRINTF(("sending nack since 0x%x != 0x%x\n", uip_htons(uh->addr), s.addr));
332  send_nack(uh, s.addr);
333  }
334 
335  if(s.addr < s.len) {
336 
337  /* timer_set(&s.nacktimer, NACK_TIMEOUT);*/
338 
339  do {
340  timer_set(&s.nacktimer, HIT_NACK_TIMEOUT);
341  PT_YIELD_UNTIL(pt, timer_expired(&s.nacktimer) ||
342  (uip_newdata() &&
343  uh->type == UIP_HTONS(TYPE_DATA) &&
344  uip_htons(uh->id) == s.id));
345  if(timer_expired(&s.nacktimer)) {
346  send_nack(uh, s.addr);
347  }
348  } while(timer_expired(&s.nacktimer));
349  }
350 
351  }
352 
353  /* leds_off(LEDS_YELLOW);
354  beep_quick(2);*/
355  /* printf("Received entire bunary over udr\n");*/
356  codeprop_start_program();
357  PT_EXIT(pt);
358  }
359 
360  PT_END(pt);
361 }
362 /*---------------------------------------------------------------------*/
363 static
364 PT_THREAD(recv_tcpthread(struct pt *pt))
365 {
366  uint8_t *dataptr;
367  struct codeprop_tcphdr *th;
368  int datalen = uip_datalen();
369  PT_BEGIN(pt);
370 
371  while(1) {
372 
374 
375  codeprop_exit_program();
376 
377  s.state = STATE_RECEIVING_TCPDATA;
378 
379  s.addr = 0;
380  s.count = 0;
381 /* process_post(PROCESS_BROADCAST, codeprop_event_quit, (process_data_t)NULL); */
382 
383 
384  /* Read the header. */
385  PT_WAIT_UNTIL(pt, uip_newdata() && uip_datalen() > 0);
386  dataptr = uip_appdata;
387 
388  if(uip_datalen() < sizeof(struct codeprop_tcphdr)) {
389  PRINTF(("codeprop: header not found in first tcp segment\n"));
390  uip_abort();
391  }
392  th = (struct codeprop_tcphdr *)uip_appdata;
393  s.len = uip_htons(th->len);
394  s.addr = 0;
395  uip_appdata += sizeof(struct codeprop_tcphdr);
396  datalen -= sizeof(struct codeprop_tcphdr);
397 
398  /* Read the rest of the data. */
399  do {
400  if(datalen > 0) {
401  /* printf("Got %d bytes\n", uip_len);*/
402  /* eeprom_write(EEPROMFS_ADDR_CODEPROP + s.addr,
403  uip_appdata,
404  uip_datalen());*/
405  cfs_seek(fd, s.addr, CFS_SEEK_SET);
406  cfs_write(fd, uip_appdata, uip_datalen());
407  s.addr += datalen;
408  }
409  if(s.addr < s.len) {
411  }
412  } while(s.addr < s.len);
413 
414 #if 1
415 
416  {
417  static int err;
418 
419  err = codeprop_start_program();
420 
421  /* Print out the "OK"/error message. */
422  do {
423  uip_send(err_msgs[err], strlen(err_msgs[err]));
424  PT_WAIT_UNTIL(pt, uip_acked() || uip_rexmit() || uip_closed());
425  } while(uip_rexmit());
426 
427  /* Close the connection. */
428  uip_close();
429  }
430 #endif
431  ++s.id;
432  s.state = STATE_SENDING_UDPDATA;
433  tcpip_poll_udp(udp_conn);
434 
435  PT_WAIT_UNTIL(pt, s.state != STATE_SENDING_UDPDATA);
436  /* printf("recv_tcpthread: unblocked\n");*/
437  }
438 
439  PT_END(pt);
440 }
441 /*---------------------------------------------------------------------*/
442 void
443 codeprop_start_broadcast(unsigned int len)
444 {
445  s.addr = 0;
446  s.len = len;
447  ++s.id;
448  s.state = STATE_SENDING_UDPDATA;
449  tcpip_poll_udp(udp_conn);
450 }
451 /*---------------------------------------------------------------------*/
452 void
453 codeprop_exit_program(void)
454 {
456  autostart_exit(elfloader_autostart_processes);
457  }
458 }
459 /*---------------------------------------------------------------------*/
460 int
461 codeprop_start_program(void)
462 {
463  int err;
464 
465  codeprop_exit_program();
466 
467  err = elfloader_load(fd);
468  if(err == ELFLOADER_OK) {
469  PRINTF(("codeprop: starting %s\n",
471  autostart_start(elfloader_autostart_processes);
472  }
473  return err;
474 }
475 /*---------------------------------------------------------------------*/
476 static void
477 uipcall(void *state)
478 {
479  if(uip_udpconnection()) {
480  recv_udpthread(&s.recv_udpthread_pt);
481  send_udpthread(&s.udpthread_pt);
482  } else {
483  if(uip_conn->lport == UIP_HTONS(CODEPROP_DATA_PORT)) {
484  if(uip_connected()) {
485 
486  if(state == NULL) {
487  s.addr = 0;
488  s.count = 0;
489  PT_INIT(&s.tcpthread_pt);
490  process_poll(&codeprop_process);
491  tcp_markconn(uip_conn, &s);
492 /* process_post(PROCESS_BROADCAST, codeprop_event_quit, */
493 /* (process_data_t)NULL); */
494  } else {
495  PRINTF(("codeprop: uip_connected() and state != NULL\n"));
496  uip_abort();
497  }
498  }
499  recv_tcpthread(&s.tcpthread_pt);
500 
501 
502  if(uip_closed() || uip_aborted() || uip_timedout()) {
503  PRINTF(("codeprop: connection down\n"));
504  tcp_markconn(uip_conn, NULL);
505  }
506  }
507  }
508 }
509 /*---------------------------------------------------------------------*/
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
#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
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
#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.