diff --git a/arch/cpu/native/Makefile.native b/arch/cpu/native/Makefile.native index 97674d5ea..af7328b31 100644 --- a/arch/cpu/native/Makefile.native +++ b/arch/cpu/native/Makefile.native @@ -1,6 +1,7 @@ CONTIKI_CPU_DIRS = . net dev CONTIKI_SOURCEFILES += rtimer-arch.c watchdog.c eeprom.c int-master.c +CONTIKI_SOURCEFILES += gpio-hal-arch.c ### Compiler definitions CC ?= gcc diff --git a/arch/cpu/native/dev/gpio-hal-arch.c b/arch/cpu/native/dev/gpio-hal-arch.c new file mode 100644 index 000000000..553a23444 --- /dev/null +++ b/arch/cpu/native/dev/gpio-hal-arch.c @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2018, George Oikonomou - http://www.spd.gr + * 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 copyright holder 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDER 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. + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "dev/gpio-hal.h" +#include "sys/log.h" + +#include +/*---------------------------------------------------------------------------*/ +/* Log configuration */ +#define LOG_MODULE "GPIO arch" +#define LOG_LEVEL LOG_LEVEL_NONE +/*---------------------------------------------------------------------------*/ +static gpio_hal_pin_cfg_t pin_cfg[GPIO_HAL_PIN_COUNT]; +static uint8_t pin_state[GPIO_HAL_PIN_COUNT]; +/*---------------------------------------------------------------------------*/ +void +gpio_hal_arch_interrupt_enable(gpio_hal_pin_t pin) +{ + if(pin >= GPIO_HAL_PIN_COUNT) { + LOG_ERR("Pin %u out of bounds\n", pin); + return; + } + + LOG_DBG("Pin %u: Enabled interrupt\n", pin); +} +/*---------------------------------------------------------------------------*/ +void +gpio_hal_arch_interrupt_disable(gpio_hal_pin_t pin) +{ + if(pin >= GPIO_HAL_PIN_COUNT) { + LOG_ERR("Pin %u out of bounds\n", pin); + return; + } + + LOG_DBG("Pin %u: Disabled interrupt\n", pin); +} +/*---------------------------------------------------------------------------*/ +void +gpio_hal_arch_pin_cfg_set(gpio_hal_pin_t pin, gpio_hal_pin_cfg_t cfg) +{ + if(pin >= GPIO_HAL_PIN_COUNT) { + LOG_ERR("Pin %u out of bounds\n", pin); + return; + } + + pin_cfg[pin] = cfg; + LOG_DBG("Pin %u: Set config=0x%02x\n", pin, pin_cfg[pin]); +} +/*---------------------------------------------------------------------------*/ +gpio_hal_pin_cfg_t +gpio_hal_arch_pin_cfg_get(gpio_hal_pin_t pin) +{ + if(pin >= GPIO_HAL_PIN_COUNT) { + LOG_ERR("Pin %u out of bounds\n", pin); + return 0; + } + + LOG_DBG("Pin %u: Config=0x%02x\n", pin, pin_cfg[pin]); + return pin_cfg[pin]; +} +/*---------------------------------------------------------------------------*/ +void +gpio_hal_arch_pin_set_input(gpio_hal_pin_t pin) +{ + if(pin >= GPIO_HAL_PIN_COUNT) { + LOG_ERR("Pin %u out of bounds\n", pin); + return; + } + + LOG_DBG("Pin %u: Set input\n", pin); +} +/*---------------------------------------------------------------------------*/ +void +gpio_hal_arch_pin_set_output(gpio_hal_pin_t pin) +{ + if(pin >= GPIO_HAL_PIN_COUNT) { + LOG_ERR("Pin %u out of bounds\n", pin); + return; + } + + LOG_DBG("Pin %u: Set output\n", pin); +} +/*---------------------------------------------------------------------------*/ +void +gpio_hal_arch_set_pin(gpio_hal_pin_t pin) +{ + if(pin >= GPIO_HAL_PIN_COUNT) { + LOG_ERR("Pin %u out of bounds\n", pin); + return; + } + + pin_state[pin] = 1; + LOG_DBG("Pin %u: Set\n", pin); +} +/*---------------------------------------------------------------------------*/ +void +gpio_hal_arch_clear_pin(gpio_hal_pin_t pin) +{ + if(pin >= GPIO_HAL_PIN_COUNT) { + LOG_ERR("Pin %u out of bounds\n", pin); + return; + } + + pin_state[pin] = 0; + LOG_DBG("Pin %u: Clear\n", pin); +} +/*---------------------------------------------------------------------------*/ +uint8_t +gpio_hal_arch_read_pin(gpio_hal_pin_t pin) +{ + if(pin >= GPIO_HAL_PIN_COUNT) { + LOG_ERR("Pin %u out of bounds\n", pin); + return 0; + } + + LOG_DBG("Pin %u: Read=%u\n", pin, pin_state[pin]); + return pin_state[pin]; +} +/*---------------------------------------------------------------------------*/ +void +gpio_hal_arch_write_pin(gpio_hal_pin_t pin, uint8_t value) +{ + if(pin >= GPIO_HAL_PIN_COUNT) { + LOG_ERR("Pin %u out of bounds\n", pin); + return; + } + + pin_state[pin] = value; + LOG_DBG("Pin %u: Write=%u\n", pin, pin_state[pin]); +} +/*---------------------------------------------------------------------------*/ +void +gpio_hal_arch_set_pins(gpio_hal_pin_mask_t pins) +{ + gpio_hal_pin_t pin; + + for(pin = 0; pin < GPIO_HAL_PIN_COUNT; pin++) { + if(pins & (1 << pin)) { + pin_state[pin] = 1; + } + } + + LOG_DBG("Set pins 0x%08" PRIx32 "\n", pins); +} +/*---------------------------------------------------------------------------*/ +void +gpio_hal_arch_clear_pins(gpio_hal_pin_mask_t pins) +{ + gpio_hal_pin_t pin; + + for(pin = 0; pin < GPIO_HAL_PIN_COUNT; pin++) { + if(pins & (1 << pin)) { + pin_state[pin] = 0; + } + } + + LOG_DBG("Clear pins 0x%08" PRIx32 "\n", pins); +} +/*---------------------------------------------------------------------------*/ +gpio_hal_pin_mask_t +gpio_hal_arch_read_pins(gpio_hal_pin_mask_t pins) +{ + gpio_hal_pin_t pin; + gpio_hal_pin_mask_t state = 0; + + for(pin = 0; pin < GPIO_HAL_PIN_COUNT; pin++) { + state |= (pin_state[pin] << pin); + } + + LOG_DBG("Read pins 0x%08" PRIx32 "\n", state); + return state; +} +/*---------------------------------------------------------------------------*/ +void +gpio_hal_arch_write_pins(gpio_hal_pin_mask_t pins, gpio_hal_pin_mask_t value) +{ + gpio_hal_pin_t pin; + + for(pin = 0; pin < GPIO_HAL_PIN_COUNT; pin++) { + if(pins & (1 << pin)) { + pin_state[pin] = (value & (1 << pin)) == 0 ? 0 : 1; + } + } + + LOG_DBG("Write pins 0x%08" PRIx32 "->0x%08" PRIx32 "\n", pins, value); +} +/*---------------------------------------------------------------------------*/ diff --git a/arch/cpu/native/native-def.h b/arch/cpu/native/native-def.h new file mode 100644 index 000000000..d85ae7100 --- /dev/null +++ b/arch/cpu/native/native-def.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018, George Oikonomou - http://www.spd.gr + * 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 copyright holder 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDER 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 NATIVE_DEF_H_ +#define NATIVE_DEF_H_ +/*---------------------------------------------------------------------------*/ +#define GPIO_HAL_CONF_ARCH_SW_TOGGLE 1 +/*---------------------------------------------------------------------------*/ +#endif /* NATIVE_DEF_H_ */ +/*---------------------------------------------------------------------------*/ diff --git a/arch/platform/native/Makefile.native b/arch/platform/native/Makefile.native index f8fd7286c..afeddd22f 100644 --- a/arch/platform/native/Makefile.native +++ b/arch/platform/native/Makefile.native @@ -6,8 +6,8 @@ endif CONTIKI_TARGET_DIRS = . dev CONTIKI_TARGET_MAIN = ${addprefix $(OBJECTDIR)/,contiki-main.o} -CONTIKI_TARGET_SOURCEFILES += platform.c clock.c xmem.c leds-arch.c -CONTIKI_TARGET_SOURCEFILES += cfs-posix.c cfs-posix-dir.c +CONTIKI_TARGET_SOURCEFILES += platform.c clock.c xmem.c +CONTIKI_TARGET_SOURCEFILES += cfs-posix.c cfs-posix-dir.c buttons.c ifeq ($(HOST_OS),Windows) CONTIKI_TARGET_SOURCEFILES += wpcap-drv.c wpcap.c diff --git a/arch/platform/native/contiki-conf.h b/arch/platform/native/contiki-conf.h index ea1383660..8d2c13820 100644 --- a/arch/platform/native/contiki-conf.h +++ b/arch/platform/native/contiki-conf.h @@ -37,7 +37,9 @@ #ifdef PROJECT_CONF_PATH #include PROJECT_CONF_PATH #endif /* PROJECT_CONF_PATH */ - +/*---------------------------------------------------------------------------*/ +#include "native-def.h" +/*---------------------------------------------------------------------------*/ #include #ifndef WIN32_LEAN_AND_MEAN #include @@ -60,8 +62,6 @@ int select_set_callback(int fd, const struct select_callback *callback); typedef unsigned int uip_stats_t; -#define LEDS_CONF_LEGACY_API 1 - #ifndef UIP_CONF_BYTE_ORDER #define UIP_CONF_BYTE_ORDER UIP_LITTLE_ENDIAN #endif @@ -91,6 +91,8 @@ typedef unsigned long clock_time_t; #define LOG_CONF_ENABLED 1 +#define PLATFORM_SUPPORTS_BUTTON_HAL 1 + /* Not part of C99 but actually present */ int strcasecmp(const char*, const char*); diff --git a/arch/platform/native/dev/buttons.c b/arch/platform/native/dev/buttons.c new file mode 100644 index 000000000..359f7a409 --- /dev/null +++ b/arch/platform/native/dev/buttons.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018, George Oikonomou - http://www.spd.gr + * 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 copyright holder 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDER 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. + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "dev/button-hal.h" + +#include +#include +/*---------------------------------------------------------------------------*/ +button_hal_button_t *button_hal_buttons[] = { NULL }; +const uint8_t button_hal_button_count = 0; +/*---------------------------------------------------------------------------*/ diff --git a/arch/platform/native/dev/leds-arch.c b/arch/platform/native/dev/leds-arch.c deleted file mode 100644 index 07addc84e..000000000 --- a/arch/platform/native/dev/leds-arch.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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. - * - */ - -/** - * \file - * A brief description of what this file is. - * \author - * Adam Dunkels - */ - -#include "dev/leds.h" -static leds_mask_t leds; -/*---------------------------------------------------------------------------*/ -void -leds_arch_init(void) -{ - leds = 0; -} -/*---------------------------------------------------------------------------*/ -leds_mask_t -leds_arch_get(void) -{ - return leds; -} -/*---------------------------------------------------------------------------*/ -void -leds_arch_set(leds_mask_t l) -{ - leds = l; -} -/*---------------------------------------------------------------------------*/ diff --git a/arch/platform/native/platform.c b/arch/platform/native/platform.c index fceab0207..c5c4f23e7 100644 --- a/arch/platform/native/platform.c +++ b/arch/platform/native/platform.c @@ -56,6 +56,9 @@ #include "net/netstack.h" #include "dev/serial-line.h" +#include "dev/button-hal.h" +#include "dev/gpio-hal.h" +#include "dev/leds.h" #include "net/ipv6/uip.h" #include "net/ipv6/uip-debug.h" @@ -251,6 +254,9 @@ platform_process_args(int argc, char**argv) void platform_init_stage_one() { + gpio_hal_init(); + button_hal_init(); + leds_init(); return; } /*---------------------------------------------------------------------------*/ diff --git a/examples/dev/button-hal/Makefile b/examples/dev/button-hal/Makefile index 071ec02ec..0c209918c 100644 --- a/examples/dev/button-hal/Makefile +++ b/examples/dev/button-hal/Makefile @@ -3,6 +3,6 @@ CONTIKI = ../../.. all: $(CONTIKI_PROJECT) -PLATFORMS_ONLY = srf06-cc26xx cc2538dk openmote-cc2538 zoul +PLATFORMS_ONLY = srf06-cc26xx cc2538dk openmote-cc2538 zoul native include $(CONTIKI)/Makefile.include diff --git a/examples/dev/button-hal/button-hal-example.c b/examples/dev/button-hal/button-hal-example.c index b4d584e3e..db34b86af 100644 --- a/examples/dev/button-hal/button-hal-example.c +++ b/examples/dev/button-hal/button-hal-example.c @@ -47,10 +47,13 @@ PROCESS_THREAD(button_hal_example, ev, data) printf("Button HAL example.\n"); printf("Device button count: %u.\n", button_hal_button_count); - printf("%s on pin %u with ID=0, Logic=%s, Pull=%s\n", - BUTTON_HAL_GET_DESCRIPTION(btn), btn->pin, - btn->negative_logic ? "Negative" : "Positive", - btn->pull == GPIO_HAL_PIN_CFG_PULL_UP ? "Pull Up" : "Pull Down"); + + if(btn) { + printf("%s on pin %u with ID=0, Logic=%s, Pull=%s\n", + BUTTON_HAL_GET_DESCRIPTION(btn), btn->pin, + btn->negative_logic ? "Negative" : "Positive", + btn->pull == GPIO_HAL_PIN_CFG_PULL_UP ? "Pull Up" : "Pull Down"); + } while(1) { diff --git a/examples/dev/gpio-hal/Makefile b/examples/dev/gpio-hal/Makefile index efbdf6e05..016f53377 100644 --- a/examples/dev/gpio-hal/Makefile +++ b/examples/dev/gpio-hal/Makefile @@ -1,7 +1,7 @@ CONTIKI_PROJECT = gpio-hal-example CONTIKI = ../../.. -PLATFORMS_ONLY = srf06-cc26xx cc2538dk openmote-cc2538 zoul +PLATFORMS_ONLY = srf06-cc26xx cc2538dk openmote-cc2538 zoul native include $(CONTIKI)/Makefile.identify-target diff --git a/examples/dev/gpio-hal/gpio-hal-example.c b/examples/dev/gpio-hal/gpio-hal-example.c index 040df3b38..380c06388 100644 --- a/examples/dev/gpio-hal/gpio-hal-example.c +++ b/examples/dev/gpio-hal/gpio-hal-example.c @@ -36,6 +36,7 @@ #include "dev/button-hal.h" #include +#include /*---------------------------------------------------------------------------*/ extern gpio_hal_pin_t out_pin1, out_pin2, out_pin3; extern gpio_hal_pin_t btn_pin; @@ -43,6 +44,13 @@ extern gpio_hal_pin_t btn_pin; static struct etimer et; static uint8_t counter; /*---------------------------------------------------------------------------*/ +/* Print gpio_hal_pin_mask_t using the correct format */ +#if GPIO_HAL_PIN_COUNT > 32 +#define PIN_MASK_FMT PRIx64 +#else +#define PIN_MASK_FMT PRIx32 +#endif +/*---------------------------------------------------------------------------*/ PROCESS(gpio_hal_example, "GPIO HAL Example"); AUTOSTART_PROCESSES(&gpio_hal_example); /*---------------------------------------------------------------------------*/ @@ -119,7 +127,8 @@ PROCESS_THREAD(gpio_hal_example, ev, data) } /* Test read */ - printf("%u: Pins are 1-%u, 2=%u, 3=%u, mask=0x%08lx\n", counter & 7, + printf("%u: Pins are 1-%u, 2=%u, 3=%u, mask=0x%08" PIN_MASK_FMT "\n", + counter & 7, gpio_hal_arch_read_pin(out_pin1), gpio_hal_arch_read_pin(out_pin2), gpio_hal_arch_read_pin(out_pin3), diff --git a/examples/dev/gpio-hal/native/pins.c b/examples/dev/gpio-hal/native/pins.c new file mode 100644 index 000000000..10472b78a --- /dev/null +++ b/examples/dev/gpio-hal/native/pins.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018, George Oikonomou - http://www.spd.gr + * 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 copyright holder 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDER 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. + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "dev/gpio-hal.h" +/*---------------------------------------------------------------------------*/ +gpio_hal_pin_t out_pin1 = 0; +gpio_hal_pin_t out_pin2 = 1; +gpio_hal_pin_t out_pin3 = 2; +/*---------------------------------------------------------------------------*/ +gpio_hal_pin_t btn_pin = 4; +/*---------------------------------------------------------------------------*/ diff --git a/examples/mqtt-client/Makefile b/examples/mqtt-client/Makefile index c4741bec9..d315cfea3 100644 --- a/examples/mqtt-client/Makefile +++ b/examples/mqtt-client/Makefile @@ -8,6 +8,6 @@ CONTIKI = ../.. MODULES_REL += arch/platform/$(TARGET) -PLATFORMS_ONLY = srf06-cc26xx cc2538dk openmote-cc2538 zoul +PLATFORMS_ONLY = srf06-cc26xx cc2538dk openmote-cc2538 zoul native include $(CONTIKI)/Makefile.include diff --git a/os/net/app-layer/coap/coap-engine.c b/os/net/app-layer/coap/coap-engine.c index fb85b5b15..cbfa70ee9 100644 --- a/os/net/app-layer/coap/coap-engine.c +++ b/os/net/app-layer/coap/coap-engine.c @@ -199,10 +199,18 @@ coap_receive(const coap_endpoint_t *src, new_offset = block_offset; } - /* call CoAP framework and check if found and allowed */ - status = call_service(message, response, - transaction->message + COAP_MAX_HEADER_SIZE, - block_size, &new_offset); + if(new_offset < 0) { + LOG_DBG("Blockwise: block request offset overflow\n"); + coap_status_code = BAD_OPTION_4_02; + coap_error_message = "BlockOutOfScope"; + status = COAP_HANDLER_STATUS_CONTINUE; + } else { + /* call CoAP framework and check if found and allowed */ + status = call_service(message, response, + transaction->message + COAP_MAX_HEADER_SIZE, + block_size, &new_offset); + } + if(status != COAP_HANDLER_STATUS_CONTINUE) { if(coap_status_code == NO_ERROR) { diff --git a/os/net/app-layer/coap/coap.c b/os/net/app-layer/coap/coap.c index 42a2d5cef..c28e2d8b5 100644 --- a/os/net/app-layer/coap/coap.c +++ b/os/net/app-layer/coap/coap.c @@ -1115,13 +1115,10 @@ coap_set_header_size1(coap_message_t *coap_pkt, uint32_t size) int coap_get_payload(coap_message_t *coap_pkt, const uint8_t **payload) { - if(coap_pkt->payload) { + if(payload != NULL) { *payload = coap_pkt->payload; - return coap_pkt->payload_len; - } else { - *payload = NULL; - return 0; } + return coap_pkt->payload != NULL ? coap_pkt->payload_len : 0; } int coap_set_payload(coap_message_t *coap_pkt, const void *payload, size_t length) diff --git a/os/net/ipv6/sicslowpan.c b/os/net/ipv6/sicslowpan.c index 4708acca7..4e3e4e00c 100644 --- a/os/net/ipv6/sicslowpan.c +++ b/os/net/ipv6/sicslowpan.c @@ -97,6 +97,7 @@ /* define the buffer as a byte array */ #define PACKETBUF_IPHC_BUF ((uint8_t *)(packetbuf_ptr + packetbuf_hdr_len)) +#define PACKETBUF_PAYLOAD_END ((uint8_t *)(packetbuf_ptr + mac_max_payload)) #define PACKETBUF_6LO_PTR (packetbuf_ptr + packetbuf_hdr_len) #define PACKETBUF_6LO_DISPATCH 0 /* 8 bit */ @@ -197,6 +198,11 @@ static int packetbuf_payload_len; */ static uint8_t uncomp_hdr_len; +/** + * mac_max_payload is the maimum payload space on the MAC frame. + */ +static int mac_max_payload; + /** * The current page (RFC 4944) */ @@ -663,8 +669,9 @@ uncompress_addr(uip_ipaddr_t *ipaddr, uint8_t const prefix[], * compress the IID. * \param link_destaddr L2 destination address, needed to compress IP * dest + * \return 1 if success, else 0 */ -static void +static int compress_hdr_iphc(linkaddr_t *link_destaddr) { uint8_t tmp, iphc0, iphc1, *next_hdr, *next_nhc; @@ -681,7 +688,23 @@ compress_hdr_iphc(linkaddr_t *link_destaddr) LOG_DBG_("\n"); } +/* Macro used only internally, during header compression. Checks if there + * is sufficient space in packetbuf before writing any further. */ +#define CHECK_BUFFER_SPACE(writelen) do { \ + if(hc06_ptr + (writelen) >= PACKETBUF_PAYLOAD_END) { \ + LOG_WARN("Not enough packetbuf space to compress header (%u bytes, %u left). Aborting.\n", \ + (unsigned)(writelen), (unsigned)(PACKETBUF_PAYLOAD_END - hc06_ptr)); \ + return 0; \ + } \ +} while(0); + hc06_ptr = PACKETBUF_IPHC_BUF + 2; + + /* Check if there is enough space for the compressed IPv6 header, in the + * worst case (least compressed case). Extension headers and transport + * layer will be checked when they are compressed. */ + CHECK_BUFFER_SPACE(38); + /* * As we copy some bit-length fields, in the IPHC encoding bytes, * we sometimes use |= @@ -910,11 +933,13 @@ compress_hdr_iphc(linkaddr_t *link_destaddr) NHC byte extra - before the next header here. This is due to next not being elided in that case. */ if(!IS_COMPRESSABLE_PROTO(*next_hdr)) { + CHECK_BUFFER_SPACE(1); hc06_ptr++; LOG_INFO("Keeping the next header in this ext hdr: %d\n", ext_hdr->next); } /* copy the ext-hdr into the hc06 buffer */ + CHECK_BUFFER_SPACE(len); memcpy(hc06_ptr, ext_hdr, len); /* modify the len to octets */ ext_hdr = (struct uip_ext_hdr *) hc06_ptr; @@ -944,6 +969,7 @@ compress_hdr_iphc(linkaddr_t *link_destaddr) /* we can compress 12 bits of both source and dest */ *next_nhc = SICSLOWPAN_NHC_UDP_CS_P_11; LOG_INFO("IPHC: remove 12 b of both source & dest with prefix 0xFOB\n"); + CHECK_BUFFER_SPACE(1); *hc06_ptr = (uint8_t)((UIP_HTONS(udp_buf->srcport) - SICSLOWPAN_UDP_4_BIT_PORT_MIN) << 4) + @@ -954,6 +980,7 @@ compress_hdr_iphc(linkaddr_t *link_destaddr) /* we can compress 8 bits of dest, leave source. */ *next_nhc = SICSLOWPAN_NHC_UDP_CS_P_01; LOG_INFO("IPHC: leave source, remove 8 bits of dest with prefix 0xF0\n"); + CHECK_BUFFER_SPACE(3); memcpy(hc06_ptr, &udp_buf->srcport, 2); *(hc06_ptr + 2) = (uint8_t)((UIP_HTONS(udp_buf->destport) - @@ -963,6 +990,7 @@ compress_hdr_iphc(linkaddr_t *link_destaddr) /* we can compress 8 bits of src, leave dest. Copy compressed port */ *next_nhc = SICSLOWPAN_NHC_UDP_CS_P_10; LOG_INFO("IPHC: remove 8 bits of source with prefix 0xF0, leave dest. hch: %i\n", *next_nhc); + CHECK_BUFFER_SPACE(3); *hc06_ptr = (uint8_t)((UIP_HTONS(udp_buf->srcport) - SICSLOWPAN_UDP_8_BIT_PORT_MIN)); @@ -972,10 +1000,12 @@ compress_hdr_iphc(linkaddr_t *link_destaddr) /* we cannot compress. Copy uncompressed ports, full checksum */ *next_nhc = SICSLOWPAN_NHC_UDP_CS_P_00; LOG_INFO("IPHC: cannot compress UDP headers\n"); + CHECK_BUFFER_SPACE(4); memcpy(hc06_ptr, &udp_buf->srcport, 4); hc06_ptr += 4; } /* always inline the checksum */ + CHECK_BUFFER_SPACE(2); memcpy(hc06_ptr, &udp_buf->udpchksum, 2); hc06_ptr += 2; uncomp_hdr_len += UIP_UDPH_LEN; @@ -1006,6 +1036,8 @@ compress_hdr_iphc(linkaddr_t *link_destaddr) } packetbuf_hdr_len = hc06_ptr - packetbuf_ptr; + + return 1; } /*--------------------------------------------------------------------*/ @@ -1477,7 +1509,6 @@ static uint8_t output(const linkaddr_t *localdest) { int framer_hdrlen; - int max_payload; /* The MAC address of the destination of the packet */ linkaddr_t dest; @@ -1513,6 +1544,20 @@ output(const linkaddr_t *localdest) packetbuf_set_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS, uipbuf_get_attr(UIPBUF_ATTR_MAX_MAC_TRANSMISSIONS)); +/* Calculate NETSTACK_FRAMER's header length, that will be added in the NETSTACK_MAC */ +#ifndef SICSLOWPAN_USE_FIXED_HDRLEN + packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER, &dest); + framer_hdrlen = NETSTACK_FRAMER.length(); + if(framer_hdrlen < 0) { + /* Framing failed, we assume the maximum header length */ + framer_hdrlen = SICSLOWPAN_FIXED_HDRLEN; + } +#else /* USE_FRAMER_HDRLEN */ + framer_hdrlen = SICSLOWPAN_FIXED_HDRLEN; +#endif /* USE_FRAMER_HDRLEN */ + + mac_max_payload = MAC_MAX_PAYLOAD - framer_hdrlen; + /* Try to compress the headers */ #if SICSLOWPAN_COMPRESSION == SICSLOWPAN_COMPRESSION_IPV6 compress_hdr_ipv6(&dest); @@ -1526,26 +1571,14 @@ output(const linkaddr_t *localdest) } #endif /* SICSLOWPAN_COMPRESSION == SICSLOWPAN_COMPRESSION_6LORH */ #if SICSLOWPAN_COMPRESSION >= SICSLOWPAN_COMPRESSION_IPHC - compress_hdr_iphc(&dest); + if(compress_hdr_iphc(&dest) == 0) { + /* Warning should already be issued by function above */ + return 0; + } #endif /* SICSLOWPAN_COMPRESSION >= SICSLOWPAN_COMPRESSION_IPHC */ LOG_INFO("output: header of len %d\n", packetbuf_hdr_len); - /* Calculate NETSTACK_FRAMER's header length, that will be added in the NETSTACK_MAC. - * We calculate it here only to make a better decision of whether the outgoing packet - * needs to be fragmented or not. */ -#ifndef SICSLOWPAN_USE_FIXED_HDRLEN - packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER, &dest); - framer_hdrlen = NETSTACK_FRAMER.length(); - if(framer_hdrlen < 0) { - /* Framing failed, we assume the maximum header length */ - framer_hdrlen = SICSLOWPAN_FIXED_HDRLEN; - } -#else /* USE_FRAMER_HDRLEN */ - framer_hdrlen = SICSLOWPAN_FIXED_HDRLEN; -#endif /* USE_FRAMER_HDRLEN */ - - max_payload = MAC_MAX_PAYLOAD - framer_hdrlen; - if((int)uip_len - (int)uncomp_hdr_len > max_payload - (int)packetbuf_hdr_len) { + if((int)uip_len - (int)uncomp_hdr_len > mac_max_payload - (int)packetbuf_hdr_len) { #if SICSLOWPAN_CONF_FRAG /* Number of bytes processed. */ uint16_t processed_ip_out_len; @@ -1560,7 +1593,7 @@ output(const linkaddr_t *localdest) * IPv6/IPHC/HC_UDP dispatchs/headers. * The following fragments contain only the fragn dispatch. */ - int estimated_fragments = ((int)uip_len) / (max_payload - SICSLOWPAN_FRAGN_HDR_LEN) + 1; + int estimated_fragments = ((int)uip_len) / (mac_max_payload - SICSLOWPAN_FRAGN_HDR_LEN) + 1; int freebuf = queuebuf_numfree() - 1; LOG_INFO("uip_len: %d, fragments: %d, free bufs: %d\n", uip_len, estimated_fragments, freebuf); if(freebuf < estimated_fragments) { @@ -1590,8 +1623,17 @@ output(const linkaddr_t *localdest) /* Copy payload and send */ packetbuf_hdr_len += SICSLOWPAN_FRAG1_HDR_LEN; - packetbuf_payload_len = (max_payload - packetbuf_hdr_len) & 0xfffffff8; + packetbuf_payload_len = (mac_max_payload - packetbuf_hdr_len) & 0xfffffff8; LOG_INFO_("(len %d, tag %d)\n", packetbuf_payload_len, frag_tag); + + if(packetbuf_payload_len < 0) { + /* The current implementation requires that all headers fit in the first + * fragment. Here is a corner case where the header did fit packetbuf + * but do no longer fit after truncating for a length multiple of 8. */ + LOG_WARN("compressed header does not fit first fragment\n"); + return 0; + } + memcpy(packetbuf_ptr + packetbuf_hdr_len, (uint8_t *)UIP_IP_BUF + uncomp_hdr_len, packetbuf_payload_len); packetbuf_set_datalen(packetbuf_payload_len + packetbuf_hdr_len); @@ -1626,7 +1668,7 @@ output(const linkaddr_t *localdest) /* uip_htons((SICSLOWPAN_DISPATCH_FRAGN << 8) | uip_len); */ SET16(PACKETBUF_FRAG_PTR, PACKETBUF_FRAG_DISPATCH_SIZE, ((SICSLOWPAN_DISPATCH_FRAGN << 8) | uip_len)); - packetbuf_payload_len = (max_payload - packetbuf_hdr_len) & 0xfffffff8; + packetbuf_payload_len = (mac_max_payload - packetbuf_hdr_len) & 0xfffffff8; while(processed_ip_out_len < uip_len) { LOG_INFO("output: fragment "); PACKETBUF_FRAG_PTR[PACKETBUF_FRAG_OFFSET] = processed_ip_out_len >> 3; diff --git a/os/net/routing/rpl-lite/rpl-dag.c b/os/net/routing/rpl-lite/rpl-dag.c index 00ee1663e..68d9e7aaa 100644 --- a/os/net/routing/rpl-lite/rpl-dag.c +++ b/os/net/routing/rpl-lite/rpl-dag.c @@ -183,14 +183,14 @@ void rpl_refresh_routes(const char *str) { if(rpl_dag_root_is_root()) { - LOG_WARN("incrementing DTSN (%s), current %u)\n", + /* Increment DTSN */ + RPL_LOLLIPOP_INCREMENT(curr_instance.dtsn_out); + + LOG_WARN("incremented DTSN (%s), current %u\n", str, curr_instance.dtsn_out); if(LOG_INFO_ENABLED) { rpl_neighbor_print_list("Refresh routes (before)"); } - - /* Increment DTSN */ - RPL_LOLLIPOP_INCREMENT(curr_instance.dtsn_out); } } /*---------------------------------------------------------------------------*/ @@ -198,15 +198,16 @@ void rpl_global_repair(const char *str) { if(rpl_dag_root_is_root()) { - LOG_WARN("initiating global repair (%s), version %u, rank %u)\n", + RPL_LOLLIPOP_INCREMENT(curr_instance.dag.version); /* New DAG version */ + curr_instance.dtsn_out = RPL_LOLLIPOP_INIT; /* Re-initialize DTSN */ + + LOG_WARN("initiating global repair (%s), version %u, rank %u\n", str, curr_instance.dag.version, curr_instance.dag.rank); if(LOG_INFO_ENABLED) { rpl_neighbor_print_list("Global repair (before)"); } - /* Initiate global repair */ - RPL_LOLLIPOP_INCREMENT(curr_instance.dag.version); /* New DAG version */ - RPL_LOLLIPOP_INCREMENT(curr_instance.dtsn_out); /* Request new DAOs */ + /* Now do a local repair to disseminate the new version */ rpl_local_repair("Global repair"); } } @@ -215,12 +216,13 @@ static void global_repair_non_root(rpl_dio_t *dio) { if(!rpl_dag_root_is_root()) { - LOG_WARN("participating in global repair, version %u, rank %u)\n", - curr_instance.dag.version, curr_instance.dag.rank); + LOG_WARN("participating in global repair, version %u, rank %u\n", + dio->version, curr_instance.dag.rank); if(LOG_INFO_ENABLED) { rpl_neighbor_print_list("Global repair (before)"); } /* Re-initialize configuration from DIO */ + rpl_timers_stop_dag_timers(); init_dag_from_dio(dio); rpl_local_repair("Global repair"); } @@ -395,9 +397,15 @@ process_dio_from_current_dag(uip_ipaddr_t *from, rpl_dio_t *dio) return; } - /* If the DIO sender is on an older version of the DAG, ignore it. The node - will eventually hear the global repair and catch up. */ + /* If the DIO sender is on an older version of the DAG, do not process it + * further. The sender will eventually hear the global repair and catch up. */ if(rpl_lollipop_greater_than(curr_instance.dag.version, dio->version)) { + if(dio->rank == ROOT_RANK) { + /* Before returning, if the DIO was from the root, an old DAG versions + * likely incidates a root reboot. Reset our DIO timer to make sure the + * root hears our version ASAP, and in turn triggers a global repair. */ + rpl_timers_dio_reset("Heard old version from root"); + } return; } @@ -412,10 +420,12 @@ process_dio_from_current_dag(uip_ipaddr_t *from, rpl_dio_t *dio) * Must come first, as it might remove all neighbors, and we then need * to re-add this source of the DIO to the neighbor table */ if(rpl_lollipop_greater_than(dio->version, curr_instance.dag.version)) { - if(curr_instance.dag.rank == ROOT_RANK) { /* The root should not hear newer versions */ + if(curr_instance.dag.rank == ROOT_RANK) { + /* The root should not hear newer versions unless it just rebooted */ LOG_ERR("inconsistent DIO version (current: %u, received: %u), initiate global repair\n", curr_instance.dag.version, dio->version); - curr_instance.dag.version = dio->version; /* Update version and trigger global repair */ + /* Update version and trigger global repair */ + curr_instance.dag.version = dio->version; rpl_global_repair("Inconsistent DIO version"); } else { LOG_WARN("new DIO version (current: %u, received: %u), apply global repair\n", @@ -451,7 +461,7 @@ process_dio_from_current_dag(uip_ipaddr_t *from, rpl_dio_t *dio) if(curr_instance.mop != RPL_MOP_NO_DOWNWARD_ROUTES) { if(nbr != NULL && nbr == curr_instance.dag.preferred_parent && rpl_lollipop_greater_than(dio->dtsn, last_dtsn)) { RPL_LOLLIPOP_INCREMENT(curr_instance.dtsn_out); - LOG_INFO("DTSN increment %u->%u, schedule new DAO with DTSN %u", + LOG_WARN("DTSN increment %u->%u, schedule new DAO with DTSN %u\n", last_dtsn, dio->dtsn, curr_instance.dtsn_out); rpl_timers_schedule_dao(); } diff --git a/os/net/routing/rpl-lite/rpl-neighbor.c b/os/net/routing/rpl-lite/rpl-neighbor.c index d6a545e13..8afa2440f 100644 --- a/os/net/routing/rpl-lite/rpl-neighbor.c +++ b/os/net/routing/rpl-lite/rpl-neighbor.c @@ -63,6 +63,18 @@ static rpl_nbr_t * best_parent(int fresh_only); /* Per-neighbor RPL information */ NBR_TABLE_GLOBAL(rpl_nbr_t, rpl_neighbors); +/*---------------------------------------------------------------------------*/ +static int +max_acceptable_rank(void) +{ + if(curr_instance.max_rankinc == 0) { + /* There is no max rank increment */ + return RPL_INFINITE_RANK; + } else { + /* Make sure not to exceed RPL_INFINITE_RANK */ + return MIN((uint32_t)curr_instance.dag.lowest_rank + curr_instance.max_rankinc, RPL_INFINITE_RANK); + } +} /*---------------------------------------------------------------------------*/ /* As per RFC 6550, section 8.2.2.4 */ static int @@ -70,8 +82,7 @@ acceptable_rank(rpl_rank_t rank) { return rank != RPL_INFINITE_RANK && rank >= ROOT_RANK - && ((curr_instance.max_rankinc == 0) || - rank <= curr_instance.dag.lowest_rank + curr_instance.max_rankinc); + && rank <= max_acceptable_rank(); } /*---------------------------------------------------------------------------*/ void @@ -89,7 +100,7 @@ rpl_neighbor_print_list(const char *str) LOG_INFO_(", DAG state: %s, MOP %u OCP %u rank %u max-rank %u, dioint %u, nbr count %u (%s)\n", rpl_dag_state_to_str(curr_instance.dag.state), curr_instance.mop, curr_instance.of->ocp, curr_rank, - curr_instance.max_rankinc != 0 ? curr_instance.dag.lowest_rank + curr_instance.max_rankinc : 0xffff, + max_acceptable_rank(), curr_dio_interval, rpl_neighbor_count(), str); while(nbr != NULL) { const struct link_stats *stats = rpl_neighbor_get_link_stats(nbr); diff --git a/os/net/routing/rpl-lite/rpl-timers.c b/os/net/routing/rpl-lite/rpl-timers.c index f7a50b858..6cd188f9d 100644 --- a/os/net/routing/rpl-lite/rpl-timers.c +++ b/os/net/routing/rpl-lite/rpl-timers.c @@ -435,7 +435,7 @@ handle_probing_timer(void *ptr) LOG_INFO_6ADDR(target_ipaddr); LOG_INFO_(" %s last tx %u min ago\n", curr_instance.dag.urgent_probing_target != NULL ? "(urgent)" : "", - probing_target != NULL ? + stats != NULL ? (unsigned)((clock_time() - stats->last_tx_time) / (60 * CLOCK_SECOND)) : 0 ); /* Send probe, e.g. unicast DIO or DIS */ diff --git a/os/services/shell/shell-commands.c b/os/services/shell/shell-commands.c index 46ee92fe5..7920fb086 100644 --- a/os/services/shell/shell-commands.c +++ b/os/services/shell/shell-commands.c @@ -174,7 +174,7 @@ PT_THREAD(cmd_rpl_status(struct pt *pt, shell_output_func output, char *args)) SHELL_OUTPUT(output, "-- State: %s\n", rpl_state_to_str(curr_instance.dag.state)); SHELL_OUTPUT(output, "-- Preferred parent: "); shell_output_6addr(output, rpl_neighbor_get_ipaddr(curr_instance.dag.preferred_parent)); - SHELL_OUTPUT(output, "\n"); + SHELL_OUTPUT(output, " (last DTSN: %u)\n", curr_instance.dag.preferred_parent->dtsn); SHELL_OUTPUT(output, "-- Rank: %u\n", curr_instance.dag.rank); SHELL_OUTPUT(output, "-- Lowest rank: %u (%u)\n", curr_instance.dag.lowest_rank, curr_instance.max_rankinc); SHELL_OUTPUT(output, "-- DTSN out: %u\n", curr_instance.dtsn_out); diff --git a/tests/01-compile-base/Makefile b/tests/01-compile-base/Makefile index 76722e7f2..1d09fd3a0 100644 --- a/tests/01-compile-base/Makefile +++ b/tests/01-compile-base/Makefile @@ -22,6 +22,7 @@ rpl-border-router/sky \ slip-radio/sky \ libs/ipv6-hooks/sky \ nullnet/native \ +mqtt-client/native \ TOOLS=