/* * 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. * * @(#)$Id: codeprop-otf.c,v 1.2 2010/10/19 18:29:04 adamdunkels Exp $ */ /** \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 #include "contiki-net.h" #include "cfs/cfs.h" #include "codeprop-otf.h" #include "loader/elfloader-otf.h" #include static const char *err_msgs[] = {"OK\r\n", "Bad ELF header\r\n", "No symtab\r\n", "No strtab\r\n", "No text\r\n", "Symbol not found\r\n", "Segment not found\r\n", "No startpoint\r\n", "Unhandled relocation\r\n", "Relocation out of range\r\n", "Relocations not sorted\r\n", "Input error\r\n" , "Ouput error\r\n" }; #define CODEPROP_DATA_PORT 6510 /*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 { u16_t id; u16_t type; #define TYPE_DATA 0x0001 #define TYPE_NACK 0x0002 u16_t addr; u16_t len; u8_t data[UDPDATASIZE]; }; struct codeprop_tcphdr { u16_t len; u16_t pad; }; static void uipcall(void *state); PROCESS(codeprop_process, "Code propagator"); struct codeprop_state { u8_t state; #define STATE_NONE 0 #define STATE_RECEIVING_TCPDATA 1 #define STATE_RECEIVING_UDPDATA 2 #define STATE_SENDING_UDPDATA 3 u16_t count; u16_t addr; u16_t len; u16_t id; struct etimer sendtimer; struct timer nacktimer, timer, starttimer; u8_t received; u8_t send_counter; struct pt tcpthread_pt; struct pt udpthread_pt; struct pt recv_udpthread_pt; }; static int fd; static struct uip_udp_conn *udp_conn; static struct codeprop_state s; void system_log(char *msg); static clock_time_t send_time; #define CONNECTION_TIMEOUT (30 * CLOCK_SECOND) /*---------------------------------------------------------------------*/ void codeprop_set_rate(clock_time_t time) { send_time = time; } /*---------------------------------------------------------------------*/ PROCESS_THREAD(codeprop_process, ev, data) { PROCESS_BEGIN(); elfloader_init(); 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); s.state = STATE_NONE; s.received = 0; s.addr = 0; s.len = 0; fd = cfs_open("codeprop-image", CFS_READ | CFS_WRITE); while(1) { PROCESS_YIELD(); if(ev == tcpip_event) { uipcall(data); } else if(ev == PROCESS_EVENT_TIMER) { tcpip_poll_udp(udp_conn); } } PROCESS_END(); } /*---------------------------------------------------------------------*/ static u16_t send_udpdata(struct codeprop_udphdr *uh) { u16_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; } cfs_seek(fd, s.addr, CFS_SEEK_SET); cfs_read(fd, (char*)&uh->data[0], len); /* 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) { /* beep();*/ 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); beep_down(10000);*/ 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);*/ cfs_seek(fd, s.addr, CFS_SEEK_SET); cfs_write(fd, (char*)&uh->data[0], len); /* beep();*/ 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); beep_quick(2);*/ /* printf("Received entire bunary over udr\n");*/ codeprop_start_program(); PT_EXIT(pt); } PT_END(pt); } /*---------------------------------------------------------------------*/ #define CODEPROP_TCPHDR_SIZE sizeof(struct codeprop_tcphdr) static PT_THREAD(recv_tcpthread(struct pt *pt)) { struct codeprop_tcphdr *th; int datalen = uip_datalen(); PT_BEGIN(pt); while(1) { PT_WAIT_UNTIL(pt, uip_connected()); codeprop_exit_program(); s.state = STATE_RECEIVING_TCPDATA; s.addr = 0; s.count = 0; /* Read the header. */ PT_WAIT_UNTIL(pt, uip_newdata() && uip_datalen() > 0); if(uip_datalen() < CODEPROP_TCPHDR_SIZE) { 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 += CODEPROP_TCPHDR_SIZE; datalen -= CODEPROP_TCPHDR_SIZE; /* Read the rest of the data. */ do { if(datalen > 0) { /* printf("Got %d bytes\n", datalen); */ if (cfs_seek(fd, s.addr, CFS_SEEK_SET) != s.addr) { PRINTF(("codeprop: seek in buffer file failed\n")); uip_abort(); } if (cfs_write(fd, uip_appdata, datalen) != datalen) { PRINTF(("codeprop: write to buffer file failed\n")); uip_abort(); } s.addr += datalen; } if(s.addr < s.len) { PT_YIELD_UNTIL(pt, uip_newdata()); } } while(s.addr < s.len); #if 1 { static int err; err = codeprop_start_program(); /* Print out the "OK"/error message. */ do { if (err >= 0 && err < sizeof(err_msgs)/sizeof(char*)) { uip_send(err_msgs[err], strlen(err_msgs[err])); } else { uip_send("Unknown error\r\n", 15); } PT_WAIT_UNTIL(pt, uip_acked() || uip_rexmit() || uip_closed()); } while(uip_rexmit()); /* Close the connection. */ uip_close(); } #endif ++s.id; s.state = STATE_SENDING_UDPDATA; tcpip_poll_udp(udp_conn); 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_exit_program(void) { if(elfloader_autostart_processes != NULL) { autostart_exit(elfloader_autostart_processes); } } /*---------------------------------------------------------------------*/ int codeprop_start_program(void) { int err; codeprop_exit_program(); err = elfloader_load(fd, codeprop_output); if(err == ELFLOADER_OK) { PRINTF(("codeprop: starting %s\n", elfloader_autostart_processes[0]->name)); autostart_start(elfloader_autostart_processes); } return err; } /*---------------------------------------------------------------------*/ 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); } } } } /*---------------------------------------------------------------------*/ /** @} */