From d36939f5ae4ab62be705923fc73bf97e6ed63078 Mon Sep 17 00:00:00 2001 From: adamdunkels Date: Sun, 18 Jun 2006 07:44:36 +0000 Subject: [PATCH] Simple program for loading a Contiki ELF binary over the network --- apps/codeprop/codeprop-tmp.c | 514 +++++++++++++++++++++++++++++++++++ apps/codeprop/codeprop-tmp.h | 47 ++++ apps/codeprop/codeprop.c | 481 ++++++++++++++++++++++++++++++++ apps/codeprop/codeprop.h | 53 ++++ apps/codeprop/tcp_loader.c | 146 ++++++++++ 5 files changed, 1241 insertions(+) create mode 100644 apps/codeprop/codeprop-tmp.c create mode 100644 apps/codeprop/codeprop-tmp.h create mode 100644 apps/codeprop/codeprop.c create mode 100644 apps/codeprop/codeprop.h create mode 100644 apps/codeprop/tcp_loader.c diff --git a/apps/codeprop/codeprop-tmp.c b/apps/codeprop/codeprop-tmp.c new file mode 100644 index 000000000..6dc836392 --- /dev/null +++ b/apps/codeprop/codeprop-tmp.c @@ -0,0 +1,514 @@ +/* + * 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-tmp.c,v 1.1 2006/06/18 07:44:36 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-tmp.h" +#include "loader/elfloader-tmp.h" + +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" }; + +#define CODEPROP_DATA_PORT 6510 + +/*static int random_rand(void) { return 1; }*/ + +#if 1 +#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; +}; + +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) +{ + static int n; + + 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(HTONS(CODEPROP_DATA_PORT)); + + udp_conn = udp_broadcast_new(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 = HTONS(TYPE_DATA); + uh->addr = htons(s.addr); + uh->id = htons(s.id); + + if(s.len - s.addr > UDPDATASIZE) { + len = UDPDATASIZE; + } else { + len = s.len - s.addr; + } + + cfs_seek(fd, s.addr); + cfs_read(fd, &uh->data[0], len); + /* eeprom_read(EEPROMFS_ADDR_CODEPROP + s.addr, + &uh->data[0], len);*/ + + uh->len = 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 == HTONS(TYPE_NACK)) { + PRINTF(("send_udpthread: got NACK for address 0x%x (now 0x%x)\n", + htons(uh->addr), s.addr)); + /* Only accept a NACK if it points to a lower byte. */ + if(htons(uh->addr) <= s.addr) { + /* beep();*/ + s.addr = 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 = HTONS(TYPE_NACK); + uh->addr = 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, htons(uh->id))); + }*/ + + PT_BEGIN(pt); + + while(1) { + + do { + PT_WAIT_UNTIL(pt, uip_newdata() && + uh->type == HTONS(TYPE_DATA) && + htons(uh->id) > s.id); + + if(htons(uh->addr) != 0) { + s.addr = 0; + send_nack(uh, 0); + } + + } while(htons(uh->addr) != 0); + + /* leds_on(LEDS_YELLOW); + beep_down(10000);*/ + + s.addr = 0; + s.id = htons(uh->id); + s.len = 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(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_write(fd, &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(htons(uh->addr) > s.addr) { + PRINTF(("sending nack since 0x%x != 0x%x\n", 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 == HTONS(TYPE_DATA) && + 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); +} +/*---------------------------------------------------------------------*/ +static +PT_THREAD(recv_tcpthread(struct pt *pt)) +{ + u8_t *dataptr; + 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; +/* 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 = htons(th->len); + s.addr = 0; + uip_appdata += sizeof(struct codeprop_tcphdr); + 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, + uip_datalen());*/ + cfs_seek(fd, s.addr); + cfs_write(fd, uip_appdata, uip_datalen()); + 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 { + uip_send(err_msgs[err], strlen(err_msgs[err])); + 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); + 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 == 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); + } + } + } +} +/*---------------------------------------------------------------------*/ +/** @} */ diff --git a/apps/codeprop/codeprop-tmp.h b/apps/codeprop/codeprop-tmp.h new file mode 100644 index 000000000..8dfcbf444 --- /dev/null +++ b/apps/codeprop/codeprop-tmp.h @@ -0,0 +1,47 @@ +/* + * 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-tmp.h,v 1.1 2006/06/18 07:44:36 adamdunkels Exp $ + */ +#ifndef __CODEPROP_H__ +#define __CODEPROP_H__ + +#include "contiki.h" + +#define CODEPROP_DATA_PORT 6510 + +PROCESS_NAME(codeprop_process); + +void codeprop_set_rate(clock_time_t time); +void codeprop_start_broadcast(unsigned int len); +void codeprop_exit_program(void); +int codeprop_start_program(void); + +#endif /* __CODEPROP_H__ */ diff --git a/apps/codeprop/codeprop.c b/apps/codeprop/codeprop.c new file mode 100644 index 000000000..867be0e42 --- /dev/null +++ b/apps/codeprop/codeprop.c @@ -0,0 +1,481 @@ +/* + * 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.c,v 1.1 2006/06/18 07:44:36 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 "contiki.h" +#include "sys/clock.h" + +#include "loader/elfloader.h" +#include "net/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 { + u16_t id; + u16_t type; +#define TYPE_DATA 0x0001 +#define TYPE_NACK 0x0002 + u16_t addr; + u16_t len; + u8_t data[UDPDATASIZE]; +}; + +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 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(HTONS(CODEPROP_DATA_PORT)); + + udp_conn = udp_broadcast_new(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 u16_t +send_udpdata(struct codeprop_udphdr *uh) +{ + u16_t len; + + uh->type = HTONS(TYPE_DATA); + uh->addr = htons(s.addr); + uh->id = 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 = 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 == HTONS(TYPE_NACK)) { + PRINTF(("send_udpthread: got NACK for address 0x%x (now 0x%x)\n", + htons(uh->addr), s.addr)); + /* Only accept a NACK if it points to a lower byte. */ + if(htons(uh->addr) <= s.addr) { + s.addr = 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 = HTONS(TYPE_NACK); + uh->addr = 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, htons(uh->id))); + }*/ + + PT_BEGIN(pt); + + while(1) { + + do { + PT_WAIT_UNTIL(pt, uip_newdata() && + uh->type == HTONS(TYPE_DATA) && + htons(uh->id) > s.id); + + if(htons(uh->addr) != 0) { + s.addr = 0; + send_nack(uh, 0); + } + + } while(htons(uh->addr) != 0); + + leds_on(LEDS_YELLOW); + + s.addr = 0; + s.id = htons(uh->id); + s.len = 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(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(htons(uh->addr) > s.addr) { + PRINTF(("sending nack since 0x%x != 0x%x\n", 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 == HTONS(TYPE_DATA) && + 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)) +{ + u8_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 = 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 == 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); + } + } + } +} +/*---------------------------------------------------------------------*/ +/** @} */ diff --git a/apps/codeprop/codeprop.h b/apps/codeprop/codeprop.h new file mode 100644 index 000000000..acec1b436 --- /dev/null +++ b/apps/codeprop/codeprop.h @@ -0,0 +1,53 @@ +/* + * 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.h,v 1.1 2006/06/18 07:44:36 adamdunkels Exp $ + */ +#ifndef __CODEPROP_H__ +#define __CODEPROP_H__ + +#include "contiki.h" + +#define CODEPROP_DATA_PORT 6510 + +struct codeprop_tcphdr { + u16_t len; +}; + +PROCESS_NAME(codeprop_process); +PROCESS_NAME(tcp_loader_process); /* Loader only */ + +extern process_event_t codeprop_event_quit; + +void codeprop_set_rate(clock_time_t time); +void codeprop_start_broadcast(unsigned int len); +void codeprop_start_program(void); + +#endif /* __CODEPROP_H__ */ diff --git a/apps/codeprop/tcp_loader.c b/apps/codeprop/tcp_loader.c new file mode 100644 index 000000000..c8d299200 --- /dev/null +++ b/apps/codeprop/tcp_loader.c @@ -0,0 +1,146 @@ +/* + * 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: tcp_loader.c,v 1.1 2006/06/18 07:44:36 adamdunkels Exp $ + */ + +#include +#include + +#include "contiki.h" +#include "sys/etimer.h" +#include "loader/elfloader.h" + +#include "net/uip.h" + +#include "dev/xmem.h" + +#include "codeprop.h" + +#define PRINTF(x) + +PROCESS(tcp_loader_process, "TCP loader"); + +static +struct codeprop_state { + u16_t addr; + u16_t len; + struct pt tcpthread_pt; +} s; + +/*---------------------------------------------------------------------*/ +static +PT_THREAD(recv_tcpthread(struct pt *pt)) +{ + PT_BEGIN(pt); + + /* Read the header. */ + PT_WAIT_UNTIL(pt, uip_newdata() && uip_datalen() > 0); + + if(uip_datalen() < sizeof(struct codeprop_tcphdr)) { + PRINTF(("codeprop: header not found in first tcp segment\n")); + uip_abort(); + goto thread_done; + } + + s.len = htons(((struct codeprop_tcphdr *)uip_appdata)->len); + s.addr = 0; + uip_appdata += sizeof(struct codeprop_tcphdr); + uip_len -= sizeof(struct codeprop_tcphdr); + + xmem_erase(s.len, EEPROMFS_ADDR_CODEPROP); + + /* Read the rest of the data. */ + do { + if(uip_len > 0) { + xmem_pwrite(uip_appdata, uip_len, EEPROMFS_ADDR_CODEPROP + s.addr); + s.addr += uip_len; + } + if(s.addr < s.len) { + PT_YIELD_UNTIL(pt, uip_newdata()); + } + } while(s.addr < s.len); + + /* Kill old program. */ + elfloader_unload(); + + /* Link, load, and start new program. */ + int s; + static char msg[30 + 10]; + s = elfloader_load(EEPROMFS_ADDR_CODEPROP); + if (s == ELFLOADER_OK) + sprintf(msg, "ok\n"); + else + sprintf(msg, "err %d %s\n", s, elfloader_unknown); + + /* Return "ok" message. */ + do { + s = strlen(msg); + uip_send(msg, s); + PT_WAIT_UNTIL(pt, uip_acked() || uip_rexmit() || uip_closed()); + } while(uip_rexmit()); + + /* Close the connection. */ + uip_close(); + + thread_done:; + PT_END(pt); +} +/*---------------------------------------------------------------------*/ +PROCESS_THREAD(tcp_loader_process, ev, data) +{ + PROCESS_BEGIN(); + + tcp_listen(HTONS(CODEPROP_DATA_PORT)); + + while(1) { + PROCESS_YIELD(); + if(ev == tcpip_event && uip_conn->lport == HTONS(CODEPROP_DATA_PORT)) { + if(uip_connected()) { /* Really uip_connecting()!!! */ + if(data == NULL) { + PT_INIT(&s.tcpthread_pt); + process_poll(&tcp_loader_process); + tcp_markconn(uip_conn, &s); + } else { + PRINTF(("codeprop: uip_connected() and data != NULL\n")); + uip_abort(); + } + } + recv_tcpthread(&s.tcpthread_pt); /* Run thread */ + + if(uip_closed() || uip_aborted() || uip_timedout()) { + PRINTF(("codeprop: connection down\n")); + tcp_markconn(uip_conn, NULL); + } + } + } + + PROCESS_END(); +}