/* * Copyright (c) 2005, Swedish Institute of Computer Science * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This file is part of the Contiki operating system. * */ /** \addtogroup esb * @{ */ /** * * \file * Code propagation and storage. * \author * Adam Dunkels * * This file implements a simple form of code propagation, which * allows a binary program to be downloaded and propagated throughout * a network of devices. * * Features: * * Commands: load code, start code * Point-to-point download over TCP * Point-to-multipoint delivery over UDP broadcasts * Versioning of code modules * * Procedure: * * 1. Receive code over TCP * 2. Send code packets over UDP * * When a code packet is deemed to be missed, a NACK is sent. If a * NACK is received, the sending restarts at the point in the * binary where the NACK pointed to. (This is *not* very efficient, * but simple to implement...) * * States: * * Receiving code header -> receiving code -> sending code * */ #include "contiki.h" #include "sys/clock.h" #include "loader/elfloader.h" #include "net/ip/tcpip.h" #include "dev/eeprom.h" #include "dev/leds.h" #include "lib/random.h" #include "codeprop.h" void codeprop_set_rate(clock_time_t time); /*static int random_rand(void) { return 1; }*/ #if 0 #define PRINTF(x) printf x #else #define PRINTF(x) #endif #define START_TIMEOUT 12 * CLOCK_SECOND #define MISS_NACK_TIMEOUT (CLOCK_SECOND / 8) * (random_rand() % 8) #define HIT_NACK_TIMEOUT (CLOCK_SECOND / 8) * (8 + random_rand() % 16) #define NACK_REXMIT_TIMEOUT CLOCK_SECOND * (4 + random_rand() % 4) #define WAITING_TIME CLOCK_SECOND * 10 #define NUM_SEND_DUPLICATES 2 #define UDPHEADERSIZE 8 #define UDPDATASIZE 32 struct codeprop_udphdr { uint16_t id; uint16_t type; #define TYPE_DATA 0x0001 #define TYPE_NACK 0x0002 uint16_t addr; uint16_t len; uint8_t data[UDPDATASIZE]; }; static void uipcall(void *state); PROCESS(codeprop_process, "Code propagator"); struct codeprop_state { uint8_t state; #define STATE_NONE 0 #define STATE_RECEIVING_TCPDATA 1 #define STATE_RECEIVING_UDPDATA 2 #define STATE_SENDING_UDPDATA 3 uint16_t count; uint16_t addr; uint16_t len; uint16_t id; struct etimer sendtimer; struct timer nacktimer, timer, starttimer; uint8_t received; uint8_t send_counter; struct pt tcpthread_pt; struct pt udpthread_pt; struct pt recv_udpthread_pt; }; static struct uip_udp_conn *udp_conn; static struct codeprop_state s; process_event_t codeprop_event_quit; void system_log(char *msg); static clock_time_t send_time; #define CONNECTION_TIMEOUT (30 * CLOCK_SECOND) enum { EVENT_START_PROGRAM }; /*---------------------------------------------------------------------*/ void codeprop_set_rate(clock_time_t time) { send_time = time; } /*---------------------------------------------------------------------*/ PROCESS_THREAD(codeprop_process, ev, data) { PROCESS_BEGIN(); s.id = 0/*random_rand()*/; send_time = CLOCK_SECOND/4; PT_INIT(&s.udpthread_pt); PT_INIT(&s.recv_udpthread_pt); tcp_listen(UIP_HTONS(CODEPROP_DATA_PORT)); udp_conn = udp_broadcast_new(UIP_HTONS(CODEPROP_DATA_PORT), NULL); codeprop_event_quit = process_alloc_event(); s.state = STATE_NONE; s.received = 0; s.addr = 0; s.len = 0; while(1) { PROCESS_YIELD(); if(ev == EVENT_START_PROGRAM) { /* First kill old program. */ elfloader_unload(); elfloader_load(EEPROMFS_ADDR_CODEPROP); } else if(ev == tcpip_event) { uipcall(data); } else if(ev == PROCESS_EVENT_TIMER) { tcpip_poll_udp(udp_conn); } } PROCESS_END(); } /*---------------------------------------------------------------------*/ static uint16_t send_udpdata(struct codeprop_udphdr *uh) { uint16_t len; uh->type = UIP_HTONS(TYPE_DATA); uh->addr = uip_htons(s.addr); uh->id = uip_htons(s.id); if(s.len - s.addr > UDPDATASIZE) { len = UDPDATASIZE; } else { len = s.len - s.addr; } eeprom_read(EEPROMFS_ADDR_CODEPROP + s.addr, &uh->data[0], len); uh->len = uip_htons(s.len); PRINTF(("codeprop: sending packet from address 0x%04x\n", s.addr)); uip_udp_send(len + UDPHEADERSIZE); return len; } /*---------------------------------------------------------------------*/ static PT_THREAD(send_udpthread(struct pt *pt)) { int len; struct codeprop_udphdr *uh = (struct codeprop_udphdr *)uip_appdata; PT_BEGIN(pt); while(1) { PT_WAIT_UNTIL(pt, s.state == STATE_SENDING_UDPDATA); for(s.addr = 0; s.addr < s.len; ) { len = send_udpdata(uh); s.addr += len; etimer_set(&s.sendtimer, CLOCK_SECOND/4); do { PT_WAIT_UNTIL(pt, uip_newdata() || etimer_expired(&s.sendtimer)); if(uip_newdata()) { if(uh->type == UIP_HTONS(TYPE_NACK)) { PRINTF(("send_udpthread: got NACK for address 0x%x (now 0x%x)\n", uip_htons(uh->addr), s.addr)); /* Only accept a NACK if it points to a lower byte. */ if(uip_htons(uh->addr) <= s.addr) { s.addr = uip_htons(uh->addr); } } PT_YIELD(pt); } } while(!etimer_expired(&s.sendtimer)); } s.state = STATE_NONE; process_post(PROCESS_BROADCAST, codeprop_event_quit, (process_data_t)NULL); } PT_END(pt); } /*---------------------------------------------------------------------*/ static void send_nack(struct codeprop_udphdr *uh, unsigned short addr) { uh->type = UIP_HTONS(TYPE_NACK); uh->addr = uip_htons(addr); uip_udp_send(UDPHEADERSIZE); } /*---------------------------------------------------------------------*/ static PT_THREAD(recv_udpthread(struct pt *pt)) { int len; struct codeprop_udphdr *uh = (struct codeprop_udphdr *)uip_appdata; /* if(uip_newdata()) { PRINTF(("recv_udpthread: id %d uh->id %d\n", s.id, uip_htons(uh->id))); }*/ PT_BEGIN(pt); while(1) { do { PT_WAIT_UNTIL(pt, uip_newdata() && uh->type == UIP_HTONS(TYPE_DATA) && uip_htons(uh->id) > s.id); if(uip_htons(uh->addr) != 0) { s.addr = 0; send_nack(uh, 0); } } while(uip_htons(uh->addr) != 0); leds_on(LEDS_YELLOW); s.addr = 0; s.id = uip_htons(uh->id); s.len = uip_htons(uh->len); timer_set(&s.timer, CONNECTION_TIMEOUT); process_post(PROCESS_BROADCAST, codeprop_event_quit, (process_data_t)NULL); while(s.addr < s.len) { if(uip_htons(uh->addr) == s.addr) { leds_blink(); len = uip_datalen() - UDPHEADERSIZE; if(len > 0) { eeprom_write(EEPROMFS_ADDR_CODEPROP + s.addr, &uh->data[0], len); PRINTF(("Saved %d bytes at address %d, %d bytes left\n", uip_datalen() - UDPHEADERSIZE, s.addr, s.len - s.addr)); s.addr += len; } } else if(uip_htons(uh->addr) > s.addr) { PRINTF(("sending nack since 0x%x != 0x%x\n", uip_htons(uh->addr), s.addr)); send_nack(uh, s.addr); } if(s.addr < s.len) { /* timer_set(&s.nacktimer, NACK_TIMEOUT);*/ do { timer_set(&s.nacktimer, HIT_NACK_TIMEOUT); PT_YIELD_UNTIL(pt, timer_expired(&s.nacktimer) || (uip_newdata() && uh->type == UIP_HTONS(TYPE_DATA) && uip_htons(uh->id) == s.id)); if(timer_expired(&s.nacktimer)) { send_nack(uh, s.addr); } } while(timer_expired(&s.nacktimer)); } } leds_off(LEDS_YELLOW); /* printf("Received entire bunary over udr\n");*/ process_post(PROCESS_BROADCAST, codeprop_event_quit, (process_data_t)NULL); process_post(&codeprop_process, EVENT_START_PROGRAM, NULL); PT_EXIT(pt); } PT_END(pt); } /*---------------------------------------------------------------------*/ static PT_THREAD(recv_tcpthread(struct pt *pt)) { uint8_t *dataptr; struct codeprop_tcphdr *th; int datalen = uip_datalen(); PT_BEGIN(pt); while(1) { PT_WAIT_UNTIL(pt, uip_connected()); s.state = STATE_RECEIVING_TCPDATA; s.addr = 0; s.count = 0; process_post(PROCESS_BROADCAST, codeprop_event_quit, (process_data_t)NULL); /* Read the header. */ PT_WAIT_UNTIL(pt, uip_newdata() && uip_datalen() > 0); dataptr = uip_appdata; if(uip_datalen() < sizeof(struct codeprop_tcphdr)) { PRINTF(("codeprop: header not found in first tcp segment\n")); uip_abort(); } th = (struct codeprop_tcphdr *)uip_appdata; s.len = uip_htons(th->len); s.addr = 0; uip_appdata += sizeof(struct codeprop_tcphdr); datalen = uip_datalen() - sizeof(struct codeprop_tcphdr); /* Read the rest of the data. */ do { if(datalen > 0) { /* printf("Got %d bytes\n", uip_len);*/ eeprom_write(EEPROMFS_ADDR_CODEPROP + s.addr, uip_appdata, datalen); s.addr += datalen; } if(s.addr < s.len) { PT_YIELD_UNTIL(pt, uip_newdata()); } } while(s.addr < s.len); /* Print out the "ok" message. */ do { uip_send("ok\r\n", 4); PT_WAIT_UNTIL(pt, uip_acked() || uip_rexmit() || uip_closed()); } while(uip_rexmit()); /* Close the connection. */ uip_close(); ++s.id; s.state = STATE_SENDING_UDPDATA; tcpip_poll_udp(udp_conn); codeprop_start_program(); PT_WAIT_UNTIL(pt, s.state != STATE_SENDING_UDPDATA); /* printf("recv_tcpthread: unblocked\n");*/ } PT_END(pt); } /*---------------------------------------------------------------------*/ void codeprop_start_broadcast(unsigned int len) { s.addr = 0; s.len = len; ++s.id; s.state = STATE_SENDING_UDPDATA; tcpip_poll_udp(udp_conn); } /*---------------------------------------------------------------------*/ void codeprop_start_program(void) { process_post(PROCESS_BROADCAST, codeprop_event_quit, (process_data_t)NULL); process_post(&codeprop_process, EVENT_START_PROGRAM, NULL); } /*---------------------------------------------------------------------*/ static void uipcall(void *state) { if(uip_udpconnection()) { recv_udpthread(&s.recv_udpthread_pt); send_udpthread(&s.udpthread_pt); } else { if(uip_conn->lport == UIP_HTONS(CODEPROP_DATA_PORT)) { if(uip_connected()) { if(state == NULL) { s.addr = 0; s.count = 0; PT_INIT(&s.tcpthread_pt); process_poll(&codeprop_process); tcp_markconn(uip_conn, &s); process_post(PROCESS_BROADCAST, codeprop_event_quit, (process_data_t)NULL); } else { PRINTF(("codeprop: uip_connected() and state != NULL\n")); uip_abort(); } } recv_tcpthread(&s.tcpthread_pt); if(uip_closed() || uip_aborted() || uip_timedout()) { PRINTF(("codeprop: connection down\n")); tcp_markconn(uip_conn, NULL); } } } } /*---------------------------------------------------------------------*/ /** @} */