diff --git a/apps/er-coap/er-coap-constants.h b/apps/er-coap/er-coap-constants.h index cfa73abcf..bd02661de 100644 --- a/apps/er-coap/er-coap-constants.h +++ b/apps/er-coap/er-coap-constants.h @@ -94,6 +94,7 @@ typedef enum { NOT_FOUND_4_04 = 132, /* NOT_FOUND */ METHOD_NOT_ALLOWED_4_05 = 133, /* METHOD_NOT_ALLOWED */ NOT_ACCEPTABLE_4_06 = 134, /* NOT_ACCEPTABLE */ + REQUEST_ENTITY_INCOMPLETE_4_08 = 136, /* REQUEST_ENTITY_INCOMPLETE */ PRECONDITION_FAILED_4_12 = 140, /* BAD_REQUEST */ REQUEST_ENTITY_TOO_LARGE_4_13 = 141, /* REQUEST_ENTITY_TOO_LARGE */ UNSUPPORTED_MEDIA_TYPE_4_15 = 143, /* UNSUPPORTED_MEDIA_TYPE */ diff --git a/apps/er-coap/er-coap-engine.c b/apps/er-coap/er-coap-engine.c index 2f58eda94..805d40bdb 100644 --- a/apps/er-coap/er-coap-engine.c +++ b/apps/er-coap/er-coap-engine.c @@ -482,6 +482,7 @@ const struct rest_implementation coap_rest_implementation = { NOT_FOUND_4_04, METHOD_NOT_ALLOWED_4_05, NOT_ACCEPTABLE_4_06, + REQUEST_ENTITY_INCOMPLETE_4_08, REQUEST_ENTITY_TOO_LARGE_4_13, UNSUPPORTED_MEDIA_TYPE_4_15, INTERNAL_SERVER_ERROR_5_00, diff --git a/apps/rest-engine/rest-constants.h b/apps/rest-engine/rest-constants.h index addc0970b..f4db1238f 100644 --- a/apps/rest-engine/rest-constants.h +++ b/apps/rest-engine/rest-constants.h @@ -56,6 +56,7 @@ struct rest_implementation_status { const unsigned int NOT_FOUND; /* NOT_FOUND_4_04, NOT_FOUND_404 */ const unsigned int METHOD_NOT_ALLOWED; /* METHOD_NOT_ALLOWED_4_05, METHOD_NOT_ALLOWED_405 */ const unsigned int NOT_ACCEPTABLE; /* NOT_ACCEPTABLE_4_06, NOT_ACCEPTABLE_406 */ + const unsigned int REQUEST_ENTITY_INCOMPLETE; /* REQUEST_ENTITY_INCOMPLETE_4_08, REQUEST_ENTITY_INCOMPLETE_408 */ const unsigned int REQUEST_ENTITY_TOO_LARGE; /* REQUEST_ENTITY_TOO_LARGE_4_13, REQUEST_ENTITY_TOO_LARGE_413 */ const unsigned int UNSUPPORTED_MEDIA_TYPE; /* UNSUPPORTED_MEDIA_TYPE_4_15, UNSUPPORTED_MEDIA_TYPE_415 */ diff --git a/examples/osd/ota-update/Makefile b/examples/osd/ota-update/Makefile new file mode 100644 index 000000000..40ebe75f7 --- /dev/null +++ b/examples/osd/ota-update/Makefile @@ -0,0 +1,49 @@ +# Set this to the name of your sketch (without extension .pde) +SKETCH=sketch +EXE=ota + + +all: $(EXE) + +CONTIKI=../../.. + +# Contiki IPv6 configuration +CONTIKI_WITH_IPV6 = 1 + +CFLAGS += -DPROJECT_CONF_H=\"project-conf.h\" + +PROJECT_SOURCEFILES += res_upload_image.c ${SKETCH}.cpp + +# variable for Makefile.include +ifneq ($(TARGET), minimal-net) +CFLAGS += -DUIP_CONF_IPV6_RPL=1 +else +# minimal-net does not support RPL under Linux and is mostly used to test CoAP only +${info INFO: compiling without RPL} +CFLAGS += -DUIP_CONF_IPV6_RPL=0 +CFLAGS += -DHARD_CODED_ADDRESS=\"fdfd::10\" +${info INFO: compiling with large buffers} +CFLAGS += -DUIP_CONF_BUFFER_SIZE=2048 +CFLAGS += -DREST_MAX_CHUNK_SIZE=1024 +CFLAGS += -DCOAP_MAX_HEADER_SIZE=640 +endif + +# linker optimizations +SMALL=1 + +# REST Engine shall use Erbium CoAP implementation +APPS += er-coap +APPS += rest-engine +APPS += arduino #json-resource #time json arduino + +include $(CONTIKI)/Makefile.include +include $(CONTIKI)/apps/arduino/Makefile.include + +avr-size: $(EXE).$(TARGET).sz + +flash: $(EXE).$(TARGET).u # $(EXE).$(TARGET).eu + +.PHONY: flash avr-size +.PRECIOUS: $(EXE).$(TARGET).hex $(EXE).$(TARGET).eep + +AVRDUDE_PORT=/dev/ttyUSB1 diff --git a/examples/osd/ota-update/README.rst b/examples/osd/ota-update/README.rst new file mode 100644 index 000000000..ecc1c823d --- /dev/null +++ b/examples/osd/ota-update/README.rst @@ -0,0 +1,81 @@ +========== +OTA Update +========== + +OTA stands for "Over the Air". OTA update is used to flash a new +firmware to a device over the air. This document (some of) the +requirements for OTA. + +Security +======== + +Position Independent Code +========================= + +The new contiki-osd-merkur-256 target should have enough memory for two +independent application images. An application image should include the +code for over-the-air update to ensure it can be upgraded in the field. + +Upgrading an image means writing a new image into the other half of the +flash memory (the part which does not run the current image). Since an +image has internal addresses and is usually linked to fixed addresses we +have two options: + +- Find a way to generate position independent code for the Atmel + microcontrollers so that an image can be used in the lower- or upper + half of flash memory +- Generate two images, one linked for the lower, one linked for the + upper half of flash memory. + +It seems the GCC Atmel compiler cannot generate position independent +code. So we have to modify the first option to make it work: We can use +some magic during loading of an image to link it to the correct half of +flash-memory during loading. This can either mean full relocation of the +image (the same job that is normally done before runtime by the linker) +or offline-generation of a jump-table for all objects (functions) that +are accessed and the bootloader then only relocates the addresses in the +jump-table. + +The first implementation will use two images (one for upper-, one for +lower half). + +Memory Layout +============= + + +--------------------------------------+ + | 3E000-3FFFF Bootloader | + +--------------------------------------+ + | 3DE00-3DFFF Flash image directory | + +--------------------------------------+ + | 3DC00-3DDFF IRQVec copy upper image | + +--------------------------------------+ + | 1F100-3DBFF | + | Upper Image (w/o first two pages) | + | | + | | + +--------------------------------------+ + | 1EF00-1F0FF IRQVec upper image | + +--------------------------------------+ + | 1ED00-1EEFF IRQVec copy lower image | + +--------------------------------------+ + | 00200-1ECFF | + | Lower Image (w/o first two pages) | + | | + | | + +--------------------------------------+ + | 00000-001FF IRQVec running image | + +--------------------------------------+ + +We have two identical images. Each image contains the IRQ vectors (and +some code after the vector table) in the lower two pages. A copy of +these two pages is kept in two pages after the image. The reason is that +the IRQ vectors are fixed at address 00000 in this processor +architecture. So for running an image we need to copy the irq-vectors to +the fixed location (and therefore we keep a backup to be able to restore +the original image at that location). +Note that in the table above an image as generated by the compiler +consists of the IRQ vectors in the first two pages (00000-001FF for the +first and 1EF00-1F0FF for the second image) plus the rest of the code +for that image. + + diff --git a/examples/osd/ota-update/ota.c b/examples/osd/ota-update/ota.c new file mode 100644 index 000000000..ea74dd8b8 --- /dev/null +++ b/examples/osd/ota-update/ota.c @@ -0,0 +1,2 @@ +#include +AUTOSTART_PROCESSES(&arduino_sketch); diff --git a/examples/osd/ota-update/project-conf.h b/examples/osd/ota-update/project-conf.h new file mode 100644 index 000000000..574e15250 --- /dev/null +++ b/examples/osd/ota-update/project-conf.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2010, 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. + * + * + */ + +#ifndef PROJECT_RPL_WEB_CONF_H_ +#define PROJECT_RPL_WEB_CONF_H_ + +#define PLATFORM_HAS_LEDS 1 +//#define PLATFORM_HAS_BUTTON 1 +#define PLATFORM_HAS_BATTERY 1 + +#define SICSLOWPAN_CONF_FRAG 1 + +/* For Debug: Dont allow MCU sleeping between channel checks */ +#undef RDC_CONF_MCU_SLEEP +#define RDC_CONF_MCU_SLEEP 0 + +/* Disabling RDC for demo purposes. Core updates often require more memory. */ +/* For projects, optimize memory and enable RDC again. */ +// #undef NETSTACK_CONF_RDC +//#define NETSTACK_CONF_RDC nullrdc_driver + +/* Increase rpl-border-router IP-buffer when using more than 64. */ +#undef REST_MAX_CHUNK_SIZE +#define REST_MAX_CHUNK_SIZE 64 + +/* Estimate your header size, especially when using Proxy-Uri. */ +/* +#undef COAP_MAX_HEADER_SIZE +#define COAP_MAX_HEADER_SIZE 70 +*/ + +/* The IP buffer size must fit all other hops, in particular the border router. */ + +#undef UIP_CONF_BUFFER_SIZE +#define UIP_CONF_BUFFER_SIZE 256 + + +/* Multiplies with chunk size, be aware of memory constraints. */ +#undef COAP_MAX_OPEN_TRANSACTIONS +#define COAP_MAX_OPEN_TRANSACTIONS 4 + +/* Must be <= open transaction number, default is COAP_MAX_OPEN_TRANSACTIONS-1. */ +/* +#undef COAP_MAX_OBSERVERS +#define COAP_MAX_OBSERVERS 2 +*/ + +/* Filtering .well-known/core per query can be disabled to save space. */ +/* +#undef COAP_LINK_FORMAT_FILTERING +#define COAP_LINK_FORMAT_FILTERING 0 +*/ + +/* Save some memory for the sky platform. */ +/* +#undef NBR_TABLE_CONF_MAX_NEIGHBORS +#define NBR_TABLE_CONF_MAX_NEIGHBORS 10 +#undef UIP_CONF_MAX_ROUTES +#define UIP_CONF_MAX_ROUTES 10 +*/ + +/* Reduce 802.15.4 frame queue to save RAM. */ +/* +#undef QUEUEBUF_CONF_NUM +#define QUEUEBUF_CONF_NUM 4 +*/ + +/* +#undef SICSLOWPAN_CONF_FRAG +#define SICSLOWPAN_CONF_FRAG 1 +*/ + +#endif /* PROJECT_RPL_WEB_CONF_H_ */ diff --git a/examples/osd/ota-update/res_upload_image.c b/examples/osd/ota-update/res_upload_image.c new file mode 100644 index 000000000..5de57703d --- /dev/null +++ b/examples/osd/ota-update/res_upload_image.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2017, Marcus Priesch, Ralf Schlatterbeck + * with code from the res-plugtest-large-update.c by + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * 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. + */ + +/** + * \file + * Over-the-air update using blockwise transfer + * \author + * Marcus Priesch + * Ralf Schlatterbeck + */ + +#include +#include "sys/cc.h" +#include "rest-engine.h" +#include "er-coap.h" +#include "contiki.h" +#include "contiki-net.h" +#include "er-coap.h" +#include "Arduino.h" +#include +#include "bootloader_if.h" + +#if 1 +#include +#define PRINTF(x) printf x +#else +#define PRINTF(x) +#endif + +static const uint32_t partition_start = 0x1ef00; //bootloader_get_part_start (); +static const uint32_t partition_size = 5000; //bootloader_get_part_size (); + +// We allocate this statically, otherwise we cannot flash a new image +// when ram is exhausted! +static uint8_t current_page [256]; +static size_t current_offset = 0; +#define PAGESIZE (sizeof (current_page)) + +static void +res_put_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + coap_packet_t *const packet = (coap_packet_t *)request; + uint8_t *in_data = NULL; + size_t len = 0; + uint8_t sreg = SREG; + + unsigned int ct = -1; + + REST.get_header_content_type(request, &ct); + + /* Require content_type APPLICATION_OCTET_STREAM */ + if (ct != REST.type.APPLICATION_OCTET_STREAM) { + REST.set_response_status(response, REST.status.BAD_REQUEST); + const char *error_msg = "ContentType"; + REST.set_response_payload(response, error_msg, strlen(error_msg)); + return; + } + + len = REST.get_request_payload (request, (const uint8_t **)&in_data); + PRINTF (("cur: %lu len: %lu, offset: %lu\n", + (uint32_t)current_offset, (uint32_t)len, (uint32_t)*offset)); + PRINTF (("b1-offs: %lu, b1-size: %u, b1-num: %lu b1-more: %d b1-size1: %lu\n", + packet->block1_offset, packet->block1_size, packet->block1_num, + packet->block1_more, packet->size1)); + if (len == 0 || NULL == in_data) { + REST.set_response_status(response, REST.status.BAD_REQUEST); + const char *error_msg = "NoPayload"; + REST.set_response_payload(response, error_msg, strlen(error_msg)); + return; + } + + if (packet->block1_offset > current_offset) { + REST.set_response_status(response, REST.status.REQUEST_ENTITY_INCOMPLETE); + const char *error_msg = "OutOfSequence"; + REST.set_response_payload(response, error_msg, strlen(error_msg)); + return; + } + + /* Old packet or retransmission, immediately confirm */ + if (packet->block1_offset + len <= current_offset) { + REST.set_response_status(response, REST.status.CHANGED); + coap_set_header_block1 + (response, packet->block1_num, 0, packet->block1_size); + return; + } + + // FIXME: blocksize may be larger than our flash page size + if (len > PAGESIZE) { + REST.set_response_status(response, REST.status.INTERNAL_SERVER_ERROR); + const char *error_msg = "GRMPF: PageSize"; + REST.set_response_payload(response, error_msg, strlen(error_msg)); + return; + } + // FIXME: blocksize may be larger than our flash page size + // So we should handle this case and repeatedly flash a block until the + // received data is written. + if (current_offset % PAGESIZE + len > PAGESIZE) { + REST.set_response_status(response, REST.status.INTERNAL_SERVER_ERROR); + const char *error_msg = "GRMPF: blocksize"; + REST.set_response_payload(response, error_msg, strlen(error_msg)); + return; + } + // Should never happen, we test for < and > earlier. + if (packet->block1_offset != current_offset) { + REST.set_response_status(response, REST.status.INTERNAL_SERVER_ERROR); + const char *error_msg = "GRMPF: Offset"; + REST.set_response_payload(response, error_msg, strlen(error_msg)); + return; + } + + if(packet->block1_offset + len > partition_size) { + REST.set_response_status(response, + REST.status.REQUEST_ENTITY_TOO_LARGE); + REST.set_response_payload( + response, + buffer, + sprintf((char *)buffer, "%luB max.", partition_size)); + return; + } + + memcpy (current_page + current_offset % PAGESIZE, in_data, len); + if (current_offset % PAGESIZE == 0 || !packet->block1_more) { + // WRITE Flash here + PRINTF (("Flashing: %lu\n", (uint32_t)len)); + sreg = SREG; + cli (); + bootloader_write_page_to_flash + (partition_start + current_offset, len, current_page); + SREG = sreg; + } + current_offset += len; + + REST.set_response_status(response, REST.status.CHANGED); + coap_set_header_block1(response, packet->block1_num, 0, packet->block1_size); +} + +RESOURCE( + res_upload_image + , "title=\"Flash memory upgrade\";rt=\"block\"" + , NULL + , NULL + , res_put_handler + , NULL + ); + diff --git a/examples/osd/ota-update/sketch.pde b/examples/osd/ota-update/sketch.pde new file mode 100644 index 000000000..93069c986 --- /dev/null +++ b/examples/osd/ota-update/sketch.pde @@ -0,0 +1,28 @@ +/* + * Gardena 9V Magnet-Valve + * We have a CoAP Resource for the Valve, it can be in state 1 (on) and + * 0 (off). + * Transition on-off outputs a negative pulse + * Transition off-on outputs a positive pulse + */ + +extern "C" { +#include +#include "contiki.h" +#include "contiki-net.h" +#include "er-coap.h" +extern resource_t res_upload_image; +char resname[] = "update"; +} + + +void setup (void) +{ + rest_init_engine (); + rest_activate_resource (&res_upload_image, resname); +} + +void loop (void) +{ + //printf ("Huhu\n"); +} diff --git a/platform/osd-merkur-128/bootloader_if.h b/platform/osd-merkur-128/bootloader_if.h new file mode 100644 index 000000000..32a886a45 --- /dev/null +++ b/platform/osd-merkur-128/bootloader_if.h @@ -0,0 +1,6 @@ +#ifndef BOOTLOADER_IF_H_ +#define BOOTLOADER_IF_H_ + +extern uint8_t bootloader_get_mac(uint8_t); + +#endif /* BOOTLOADER_IF_H_ */ diff --git a/platform/osd-merkur-128/params.c b/platform/osd-merkur-128/params.c index 4b0252387..31e588ebe 100644 --- a/platform/osd-merkur-128/params.c +++ b/platform/osd-merkur-128/params.c @@ -39,6 +39,7 @@ #endif #include "contiki.h" +#include "bootloader_if.h" #include #include #include @@ -85,8 +86,6 @@ const uint8_t default_domain_name[] PROGMEM = PARAMS_DOMAINNAME; #if PARAMETER_STORAGE==0 /* 0 Hard coded, minmal program and eeprom usage. */ -extern uint8_t bootloader_get_mac(uint8_t); - uint8_t params_get_eui64(uint8_t *eui64) { #if CONTIKI_CONF_RANDOM_MAC diff --git a/platform/osd-merkur-256/bootloader_if.h b/platform/osd-merkur-256/bootloader_if.h new file mode 100644 index 000000000..8bbbf8a7e --- /dev/null +++ b/platform/osd-merkur-256/bootloader_if.h @@ -0,0 +1,8 @@ +#ifndef BOOTLOADER_IF_H_ +#define BOOTLOADER_IF_H_ + +extern uint8_t bootloader_get_mac(uint8_t); +extern int bootloader_write_page_to_flash + (uint32_t address, unsigned int size, unsigned char *p); + +#endif /* BOOTLOADER_IF_H_ */ diff --git a/platform/osd-merkur-256/params.c b/platform/osd-merkur-256/params.c index 4b0252387..31e588ebe 100644 --- a/platform/osd-merkur-256/params.c +++ b/platform/osd-merkur-256/params.c @@ -39,6 +39,7 @@ #endif #include "contiki.h" +#include "bootloader_if.h" #include #include #include @@ -85,8 +86,6 @@ const uint8_t default_domain_name[] PROGMEM = PARAMS_DOMAINNAME; #if PARAMETER_STORAGE==0 /* 0 Hard coded, minmal program and eeprom usage. */ -extern uint8_t bootloader_get_mac(uint8_t); - uint8_t params_get_eui64(uint8_t *eui64) { #if CONTIKI_CONF_RANDOM_MAC