diff --git a/Makefile.dep b/Makefile.dep index 7aef140320b6..bb5be6fa067b 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -562,6 +562,11 @@ ifneq (,$(filter random,$(USEMODULE))) endif endif +ifneq (,$(filter openthread_contrib,$(USEMODULE))) + USEMODULE += openthread_contrib_netdev + FEATURES_REQUIRED += cpp +endif + ifneq (,$(filter emcute,$(USEMODULE))) USEMODULE += core_thread_flags USEMODULE += sock_udp diff --git a/drivers/at86rf2xx/at86rf2xx_netdev.c b/drivers/at86rf2xx/at86rf2xx_netdev.c index 6e165ae51592..a9452c85a735 100644 --- a/drivers/at86rf2xx/at86rf2xx_netdev.c +++ b/drivers/at86rf2xx/at86rf2xx_netdev.c @@ -565,11 +565,22 @@ static void _isr(netdev_t *netdev) if (netdev->event_callback && (dev->netdev.flags & AT86RF2XX_OPT_TELL_TX_END)) { switch (trac_status) { +#ifdef MODULE_OPENTHREAD + case AT86RF2XX_TRX_STATE__TRAC_SUCCESS: + netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); + DEBUG("[at86rf2xx] TX SUCCESS\n"); + break; + case AT86RF2XX_TRX_STATE__TRAC_SUCCESS_DATA_PENDING: + netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE_DATA_PENDING); + DEBUG("[at86rf2xx] TX SUCCESS DATA PENDING\n"); + break; +#else case AT86RF2XX_TRX_STATE__TRAC_SUCCESS: case AT86RF2XX_TRX_STATE__TRAC_SUCCESS_DATA_PENDING: netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); DEBUG("[at86rf2xx] TX SUCCESS\n"); break; +#endif case AT86RF2XX_TRX_STATE__TRAC_NO_ACK: netdev->event_callback(netdev, NETDEV_EVENT_TX_NOACK); DEBUG("[at86rf2xx] TX NO_ACK\n"); diff --git a/drivers/include/net/netdev.h b/drivers/include/net/netdev.h index 46d9aa7caa63..82011f4f9a5b 100644 --- a/drivers/include/net/netdev.h +++ b/drivers/include/net/netdev.h @@ -70,15 +70,16 @@ enum { * upper layer */ typedef enum { - NETDEV_EVENT_ISR, /**< driver needs it's ISR handled */ - NETDEV_EVENT_RX_STARTED, /**< started to receive a packet */ - NETDEV_EVENT_RX_COMPLETE, /**< finished receiving a packet */ - NETDEV_EVENT_TX_STARTED, /**< started to transfer a packet */ - NETDEV_EVENT_TX_COMPLETE, /**< finished transferring packet */ - NETDEV_EVENT_TX_NOACK, /**< ACK requested but not received */ - NETDEV_EVENT_TX_MEDIUM_BUSY, /**< couldn't transfer packet */ - NETDEV_EVENT_LINK_UP, /**< link established */ - NETDEV_EVENT_LINK_DOWN, /**< link gone */ + NETDEV_EVENT_ISR, /**< driver needs it's ISR handled */ + NETDEV_EVENT_RX_STARTED, /**< started to receive a packet */ + NETDEV_EVENT_RX_COMPLETE, /**< finished receiving a packet */ + NETDEV_EVENT_TX_STARTED, /**< started to transfer a packet */ + NETDEV_EVENT_TX_COMPLETE, /**< finished transferring packet */ + NETDEV_EVENT_TX_COMPLETE_DATA_PENDING, /**< finished transferring packet and has data pending flag */ + NETDEV_EVENT_TX_NOACK, /**< ACK requested but not received */ + NETDEV_EVENT_TX_MEDIUM_BUSY, /**< couldn't transfer packet */ + NETDEV_EVENT_LINK_UP, /**< link established */ + NETDEV_EVENT_LINK_DOWN, /**< link gone */ /* expand this list if needed */ } netdev_event_t; diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index d922c6211658..f94ac7eca09f 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -41,6 +41,7 @@ PSEUDOMODULES += netstats_ipv6 PSEUDOMODULES += netstats_rpl PSEUDOMODULES += newlib PSEUDOMODULES += newlib_nano +PSEUDOMODULES += openthread PSEUDOMODULES += pktqueue PSEUDOMODULES += posix PSEUDOMODULES += printf_float diff --git a/pkg/openthread/Makefile b/pkg/openthread/Makefile new file mode 100644 index 000000000000..4ea2fe5d0d5c --- /dev/null +++ b/pkg/openthread/Makefile @@ -0,0 +1,54 @@ +PKG_NAME=openthread +PKG_URL=https://github.com/openthread/openthread.git +PKG_VERSION=fbfd76a990b81f007957e1bd774e51bce742e53e +PKG_BUILDDIR ?= $(BINDIRBASE)/pkg/$(BOARD)/$(PKG_NAME) + +ifneq (,$(filter libopenthread-ftd,$(USEMODULE))) + $(info Compile OpenThread for FTD device) + OPENTHREAD_ARGS+= --enable-cli-app=ftd +endif +ifneq (,$(filter libopenthread-mtd,$(USEMODULE))) + $(info Compile OpenThread for MTD device) + OPENTHREAD_ARGS+= --enable-cli-app=mtd +endif +ifneq (,$(filter libopenthread-ncp-ftd,$(USEMODULE))) + $(info Compile OpenThread with NCP) + OPENTHREAD_ARGS += --enable-ncp-app=ftd --with-ncp-bus=uart +endif +$(info $$OPENTHREAD_ARGS is [${OPENTHREAD_ARGS}]) + +.PHONY: all + +all: git-download + cd $(PKG_BUILDDIR) && PREFIX="/" ./bootstrap + cd $(PKG_BUILDDIR) && ./configure \ + --enable-application-coap INSTALL="/usr/bin/install -p" CPP="$(CPP)" \ + CC="$(CC)" CXX="$(CXX)" OBJC="" OBJCXX="" AR="$(AR)" RANLIB="$(RANLIB)" NM="$(NM)" \ + STRIP="$(STRIP)" \ + CPPFLAGS="-fdata-sections -ffunction-sections -Os -g $(CFLAGS_CPU) " \ + CFLAGS="-fdata-sections -ffunction-sections -Os -g $(CFLAGS_CPU) " \ + CXXFLAGS="-fdata-sections -ffunction-sections -Os -g $(CFLAGS_CPU) -fno-exceptions -fno-rtti " \ + LDFLAGS="-fdata-sections -ffunction-sections -Os -g $(CFLAGS_CPU) -nostartfiles -specs=nano.specs \ + -specs=nosys.specs -Wl,--gc-sections -Wl,-Map=map.map " \ + --host=$(TARGET_ARCH) \ + --target=$(TARGET_ARCH) \ + --prefix=/ \ + --enable-default-logging \ + ${OPENTHREAD_ARGS} + cd $(PKG_BUILDDIR) && make -j99 --no-print-directory DESTDIR=$(PKG_BUILDDIR)/output install PREFIX=/ + + cp $(PKG_BUILDDIR)/output/lib/libmbedcrypto.a ${BINDIR}/libmbedcrypto.a +ifneq (,$(filter libopenthread-ftd,$(USEMODULE))) + cp $(PKG_BUILDDIR)/output/lib/libopenthread-ftd.a ${BINDIR}/libopenthread-ftd.a + cp $(PKG_BUILDDIR)/output/lib/libopenthread-cli-ftd.a ${BINDIR}/libopenthread-cli-ftd.a +endif +ifneq (,$(filter libopenthread-mtd,$(USEMODULE))) + cp $(PKG_BUILDDIR)/output/lib/libopenthread-mtd.a ${BINDIR}/libopenthread-mtd.a + cp $(PKG_BUILDDIR)/output/lib/libopenthread-cli-mtd.a ${BINDIR}/libopenthread-cli-mtd.a +endif +ifneq (,$(filter libopenthread-ncp-ftd,$(USEMODULE))) + cp $(PKG_BUILDDIR)/output/lib/libopenthread-ftd.a ${BINDIR}/libopenthread-ftd.a + cp $(PKG_BUILDDIR)/output/lib/libopenthread-ncp-ftd.a ${BINDIR}/libopenthread-ncp-ftd.a +endif + sed -ie 's/BASE/_BASE/g' $(PKG_BUILDDIR)/output/include/openthread/types.h +include $(RIOTBASE)/pkg/pkg.mk diff --git a/pkg/openthread/Makefile.include b/pkg/openthread/Makefile.include new file mode 100644 index 000000000000..f2c17217a666 --- /dev/null +++ b/pkg/openthread/Makefile.include @@ -0,0 +1,18 @@ +OPENTHREAD_DIR = $(RIOTBASE)/pkg/openthread + +INCLUDES += -I$(OPENTHREAD_DIR)/include \ + -I$(OPENTHREAD_DIR)/include/openthread \ + -I$(BINDIRBASE)/pkg/$(BOARD)/openthread/output/include \ + -I$(BINDIRBASE)/pkg/$(BOARD)/openthread/include/openthread \ + +ifneq (,$(filter openthread_contrib,$(USEMODULE))) + DIRS += $(OPENTHREAD_DIR)/contrib + DIRS += $(OPENTHREAD_DIR)/contrib/netdev +endif +ifneq (,$(filter openthread_sock,$(USEMODULE))) + DIRS += $(OPENTHREAD_DIR)/contrib/sock + CFLAGS += -DSOCK_HAS_IPV6 +endif +ifneq (,$(filter openthread_sock_udp,$(USEMODULE))) + DIRS += $(OPENTHREAD_DIR)/contrib/sock/udp +endif diff --git a/pkg/openthread/contrib/Makefile b/pkg/openthread/contrib/Makefile new file mode 100644 index 000000000000..6533faeb39bd --- /dev/null +++ b/pkg/openthread/contrib/Makefile @@ -0,0 +1,3 @@ +MODULE := openthread_contrib + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/openthread/contrib/netdev/Makefile b/pkg/openthread/contrib/netdev/Makefile new file mode 100644 index 000000000000..7f8ff815f523 --- /dev/null +++ b/pkg/openthread/contrib/netdev/Makefile @@ -0,0 +1,3 @@ +MODULE := openthread_contrib_netdev + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/openthread/contrib/netdev/openthread_netdev.c b/pkg/openthread/contrib/netdev/openthread_netdev.c new file mode 100644 index 000000000000..cdfcb8e7ecbf --- /dev/null +++ b/pkg/openthread/contrib/netdev/openthread_netdev.c @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2017 Fundacion Inria Chile + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @author Jose Ignacio Alamos + */ + +#include +#include +#include +#include "msg.h" +#include "openthread/cli.h" +#include "openthread/instance.h" +#include "openthread/ip6.h" +#include "openthread/platform/alarm.h" +#include "openthread/platform/uart.h" +#include "openthread/tasklet.h" +#include "openthread/thread.h" +#include "random.h" +#include "ot.h" + +#ifdef MODULE_OPENTHREAD_NCP +#include "openthread/ncp.h" +#endif + +#define ENABLE_DEBUG (1) +#include "debug.h" + +#define OPENTHREAD_QUEUE_LEN (8) +static msg_t _queue[OPENTHREAD_QUEUE_LEN]; + +static kernel_pid_t _pid; +static otInstance *sInstance; + +void ot_exec_job(OT_JOB (*job)(otInstance *, void *), void *context) +{ + ot_job_t _job; + + _job.function = job; + _job.context = context; + + msg_t msg, reply; + msg.type = OPENTHREAD_JOB_MSG_TYPE_EVENT; + msg.content.ptr = &_job; + msg_send_receive(&msg, &reply, openthread_get_pid()); +} + +/* OpenThread will call this when switching state from empty tasklet to non-empty tasklet. */ +void otSignalTaskletPending(otInstance *aInstance) +{ + /* Unused */ + (void) aInstance; +} + +static void *_openthread_event_loop(void *arg) +{ + _pid = thread_getpid(); + + /* enable OpenThread UART */ + otPlatUartEnable(); + + /* init OpenThread */ + sInstance = otInstanceInit(); + + msg_init_queue(_queue, OPENTHREAD_QUEUE_LEN); + netdev_t *dev; + msg_t msg, reply; + +#ifdef MODULE_OPENTHREAD_CLI + otCliUartInit(sInstance); +#else + +#ifdef MODULE_OPENTHREAD_NCP + otNcpInit(sInstance); +#endif + +#endif + +#if OPENTHREAD_ENABLE_DIAG + diagInit(sInstance); +#endif + + uint8_t *buf; + (void) buf; + ot_job_t *job; + while (1) { + otTaskletsProcess(sInstance); + msg_receive(&msg); + switch (msg.type) { + case OPENTHREAD_XTIMER_MSG_TYPE_EVENT: + /* Tell OpenThread a time event was received */ + otPlatAlarmFired(sInstance); + break; + case OPENTHREAD_NETDEV_MSG_TYPE_EVENT: + /* Received an event from driver */ + dev = msg.content.ptr; + dev->driver->isr(dev); + break; +#if defined(MODULE_OPENTHREAD_CLI) || defined(MODULE_OPENTHREAD_NCP) + case OPENTHREAD_SERIAL_MSG_TYPE_EVENT: + /* Tell OpenThread about the receotion of a CLI command */ + buf = msg.content.ptr; + otPlatUartReceived(buf, strlen((char *) buf)); + break; +#endif + case OPENTHREAD_JOB_MSG_TYPE_EVENT: + job = msg.content.ptr; + job->function(sInstance, job->context); + msg_reply(&msg, &reply); + break; + } + } + + return NULL; +} + +static void _event_cb(netdev_t *dev, netdev_event_t event) +{ + msg_t msg; + + switch (event) { + case NETDEV_EVENT_ISR: + assert(_pid != KERNEL_PID_UNDEF); + + msg.type = OPENTHREAD_NETDEV_MSG_TYPE_EVENT; + msg.content.ptr = dev; + + if (msg_send(&msg, _pid) <= 0) { + DEBUG("openthread_netdev: possibly lost interrupt.\n"); + } + break; + + case NETDEV_EVENT_RX_COMPLETE: + DEBUG("openthread_netdev: Reception of a packet\n"); + recv_pkt(sInstance, dev); + break; + case NETDEV_EVENT_TX_COMPLETE: + case NETDEV_EVENT_TX_NOACK: + case NETDEV_EVENT_TX_MEDIUM_BUSY: + DEBUG("openthread_netdev: Transmission of a packet\n"); + send_pkt(sInstance, dev, event); + break; + default: + break; + } +} + +/* get OpenThread thread pid */ +kernel_pid_t openthread_get_pid(void) +{ + return _pid; +} + +/* starts OpenThread thread */ +int openthread_netdev_init(char *stack, int stacksize, char priority, + const char *name, netdev_t *netdev) +{ + netdev->driver->init(netdev); + netdev->event_callback = _event_cb; + + netopt_enable_t enable = NETOPT_ENABLE; + netdev->driver->set(netdev, NETOPT_TX_END_IRQ, &enable, sizeof(enable)); + + _pid = thread_create(stack, stacksize, + priority, THREAD_CREATE_STACKTEST, + _openthread_event_loop, NULL, name); + + if (_pid <= 0) { + return -EINVAL; + } + + return _pid; +} +/** @} */ diff --git a/pkg/openthread/contrib/openthread.c b/pkg/openthread/contrib/openthread.c new file mode 100644 index 000000000000..fa01f530cfcb --- /dev/null +++ b/pkg/openthread/contrib/openthread.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2017 Fundacion Inria Chile + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @author Jose Ignacio Alamos + */ + +#include + +#include "openthread/platform/alarm.h" +#include "openthread/platform/uart.h" +#include "ot.h" +#include "random.h" +#include "thread.h" +#include "xtimer.h" + +#ifdef MODULE_AT86RF2XX +#include "at86rf2xx.h" +#include "at86rf2xx_params.h" +#endif + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#ifdef MODULE_AT86RF2XX /* is mutual exclusive with above ifdef */ +#define OPENTHREAD_NETIF_NUMOF (sizeof(at86rf2xx_params) / sizeof(at86rf2xx_params[0])) +#endif + +#ifdef MODULE_AT86RF2XX +static at86rf2xx_t at86rf2xx_dev; +#endif + +#define OPENTHREAD_NETDEV2_BUFLEN (ETHERNET_MAX_LEN) + +static uint8_t rx_buf[OPENTHREAD_NETDEV2_BUFLEN]; +static uint8_t tx_buf[OPENTHREAD_NETDEV2_BUFLEN]; +static char ot_thread_stack[2 * THREAD_STACKSIZE_MAIN]; + +void otTaskletsSignalPending(otInstance *aInstance) +{ + (void)aInstance; +} + +#if defined(MODULE_OPENTHREAD_CLI) || defined(MODULE_OPENTHREAD_NCP) +/* init and run OpeanThread's UART simulation (stdio) */ +void openthread_uart_run(void) +{ + char buf[256]; + // uint8_t index=0; + char c; + msg_t msg; + + msg.type = OPENTHREAD_SERIAL_MSG_TYPE_EVENT; + msg.content.ptr = buf; + + buf[1] = 0; + while (1) { + c = getchar(); + buf[0] = c; + msg_send(&msg, openthread_get_pid()); + } +} +#endif + +void openthread_bootstrap(void) +{ + /* init random */ + ot_random_init(); + + /* setup netdev modules */ +#ifdef MODULE_AT86RF2XX + at86rf2xx_setup(&at86rf2xx_dev, &at86rf2xx_params[0]); + netdev_t *netdev = (netdev_t *) &at86rf2xx_dev; +#endif + + openthread_radio_init(netdev, tx_buf, rx_buf); + openthread_netdev_init(ot_thread_stack, sizeof(ot_thread_stack), THREAD_PRIORITY_MAIN - 5, "openthread", netdev); +} + +/** @} */ diff --git a/pkg/openthread/contrib/platform_alarm.c b/pkg/openthread/contrib/platform_alarm.c new file mode 100644 index 000000000000..2e4fb8bb6773 --- /dev/null +++ b/pkg/openthread/contrib/platform_alarm.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2017 Fundacion Inria Chile + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @author Jose Ignacio Alamos + */ + +#include + +#include "msg.h" +#include "openthread/platform/alarm.h" +#include "ot.h" +#include "thread.h" +#include "xtimer.h" +#include "timex.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static xtimer_t ot_timer; +static msg_t ot_alarm_msg; + +/* OpenThread will call this for starting an aDt millisecs alarm when current time is aT0 millisecs */ +void otPlatAlarmStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) +{ + DEBUG("openthread: otPlatAlarmStartAt: aT0: %i, aDT: %i\n", (int) aT0, (int) aDt); + ot_alarm_msg.type = OPENTHREAD_XTIMER_MSG_TYPE_EVENT; + + int dt; + if (aDt == 0) { + msg_send(&ot_alarm_msg, thread_getpid()); + } + else { + dt = aDt * US_PER_MS; + xtimer_set_msg(&ot_timer, dt, &ot_alarm_msg, thread_getpid()); + } +} + +/* OpenThread will call this to stop alarms */ +void otPlatAlarmStop(otInstance *aInstance) +{ + DEBUG("openthread: otPlatAlarmStop\n"); + xtimer_remove(&ot_timer); +} + +/* OpenThread will call this for getting running time in millisecs */ +uint32_t otPlatAlarmGetNow(void) +{ + uint32_t now = xtimer_now_usec() / US_PER_MS; + + DEBUG("openthread: otPlatAlarmGetNow: %i\n", (int) now); + return now; +} +/** @} */ diff --git a/pkg/openthread/contrib/platform_diag.c b/pkg/openthread/contrib/platform_diag.c new file mode 100644 index 000000000000..5104fab92075 --- /dev/null +++ b/pkg/openthread/contrib/platform_diag.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 Fundacion Inria Chile + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @author Jose Ignacio Alamos + */ + +#include +#include + +static bool sDiagMode = false; + +void otPlatDiagProcess(int argc, char *argv[], char *aOutput, size_t aOutputMaxLen) +{ + /* add more plarform specific diagnostics features here */ + (void)argc; +} + +void otPlatDiagModeSet(bool aMode) +{ + sDiagMode = aMode; +} + +bool otPlatDiagModeGet(void) +{ + return sDiagMode; +} + +/** @} */ diff --git a/pkg/openthread/contrib/platform_logging.c b/pkg/openthread/contrib/platform_logging.c new file mode 100644 index 000000000000..fd10e801f3a8 --- /dev/null +++ b/pkg/openthread/contrib/platform_logging.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2017 Fundacion Inria Chile + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @author Jose Ignacio Alamos + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "openthread/platform/logging.h" + +/* adapted from OpenThread posix example: + * See: https://github.com/openthread/openthread/blob/master/examples/platforms/posix/logging.c */ +void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...) +{ + va_list args; + + switch (aLogLevel) { + case kLogLevelNone: + fprintf(stderr, "NONE "); + break; + + case kLogLevelCrit: + fprintf(stderr, "CRIT "); + break; + + case kLogLevelWarn: + fprintf(stderr, "WARN "); + break; + + case kLogLevelInfo: + fprintf(stderr, "INFO "); + break; + + case kLogLevelDebg: + fprintf(stderr, "DEBG "); + break; + } + + switch (aLogRegion) { + case kLogRegionApi: + fprintf(stderr, "API "); + break; + + case kLogRegionMle: + fprintf(stderr, "MLE "); + break; + + case kLogRegionArp: + fprintf(stderr, "ARP "); + break; + + case kLogRegionNetData: + fprintf(stderr, "NETD "); + break; + + case kLogRegionIp6: + fprintf(stderr, "IPV6 "); + break; + + case kLogRegionIcmp: + fprintf(stderr, "ICMP "); + break; + + case kLogRegionMac: + fprintf(stderr, "MAC "); + break; + + case kLogRegionMem: + fprintf(stderr, "MEM "); + break; + default: + break; + } + + va_start(args, aFormat); + vfprintf(stderr, aFormat, args); + fprintf(stderr, "\r"); + va_end(args); +} +/** @} */ diff --git a/pkg/openthread/contrib/platform_misc.c b/pkg/openthread/contrib/platform_misc.c new file mode 100644 index 000000000000..b217592d8084 --- /dev/null +++ b/pkg/openthread/contrib/platform_misc.c @@ -0,0 +1,33 @@ +/* + * Copyright (C) Baptiste Clenet + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @author Baptiste Clenet + */ + + +#include "openthread/types.h" +#include "openthread/platform/misc.h" +#include "periph/pm.h" + +void otPlatReset(otInstance *aInstance) +{ + (void)aInstance; + printf("reboot...\n"); + pm_reboot(); +} + +otPlatResetReason otPlatGetResetReason(otInstance *aInstance) +{ + (void)aInstance; + // TODO: Write me! + return kPlatResetReason_PowerOn; +} diff --git a/pkg/openthread/contrib/platform_radio.c b/pkg/openthread/contrib/platform_radio.c new file mode 100644 index 000000000000..1a1c911f9799 --- /dev/null +++ b/pkg/openthread/contrib/platform_radio.c @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2017 Fundacion Inria Chile + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @author Jose Ignacio Alamos + */ + +#include +#include +#include + +#include "byteorder.h" +#include "errno.h" +#include "net/ethernet/hdr.h" +#include "net/ethertype.h" +#include "net/ieee802154.h" +#include "net/netdev/ieee802154.h" +#include "openthread/platform/radio.h" +#include "ot.h" + +#define ENABLE_DEBUG (1) +#include "debug.h" + +static bool sDisabled; + +static RadioPacket sTransmitFrame; +static RadioPacket sReceiveFrame; +static int8_t Rssi; + +static netdev_t *_dev; + +/* set 15.4 channel */ +static int _set_channel(uint16_t channel) +{ + return _dev->driver->set(_dev, NETOPT_CHANNEL, &channel, sizeof(uint16_t)); +} + +/*get transmission power from driver */ +static int16_t _get_power(void) +{ + int16_t power; + + _dev->driver->get(_dev, NETOPT_TX_POWER, &power, sizeof(int16_t)); + return power; +} + +/* set transmission power */ +static int _set_power(int16_t power) +{ + return _dev->driver->set(_dev, NETOPT_TX_POWER, &power, sizeof(int16_t)); +} + +/* set IEEE802.15.4 PAN ID */ +static int _set_panid(uint16_t panid) +{ + return _dev->driver->set(_dev, NETOPT_NID, &panid, sizeof(uint16_t)); +} + +/* set extended HW address */ +static int _set_long_addr(uint8_t *ext_addr) +{ + return _dev->driver->set(_dev, NETOPT_ADDRESS_LONG, ext_addr, IEEE802154_LONG_ADDRESS_LEN); +} + +/* set short address */ +static int _set_addr(uint16_t addr) +{ + return _dev->driver->set(_dev, NETOPT_ADDRESS, &addr, sizeof(uint16_t)); +} + +/* check the state of promiscuous mode */ +static netopt_enable_t _is_promiscuous(void) +{ + netopt_enable_t en; + + _dev->driver->get(_dev, NETOPT_PROMISCUOUSMODE, &en, sizeof(en)); + return en == NETOPT_ENABLE ? true : false;; +} + +/* set the state of promiscuous mode */ +static int _set_promiscuous(netopt_enable_t enable) +{ + return _dev->driver->set(_dev, NETOPT_PROMISCUOUSMODE, &enable, sizeof(enable)); +} + +/* wrapper for setting device state */ +static void _set_state(netopt_state_t state) +{ + _dev->driver->set(_dev, NETOPT_STATE, &state, sizeof(netopt_state_t)); +} + +/* sets device state to SLEEP */ +static void _set_sleep(void) +{ + _set_state(NETOPT_STATE_SLEEP); +} + +/* set device state to IDLE */ +static void _set_idle(void) +{ + _set_state(NETOPT_STATE_IDLE); +} + +/* init framebuffers and initial state */ +void openthread_radio_init(netdev_t *dev, uint8_t *tb, uint8_t *rb) +{ + sTransmitFrame.mPsdu = tb; + sTransmitFrame.mLength = 0; + sReceiveFrame.mPsdu = rb; + sReceiveFrame.mLength = 0; + _dev = dev; +} + +/* Called upon NETDEV_EVENT_RX_COMPLETE event */ +void recv_pkt(otInstance *aInstance, netdev_t *dev) +{ + DEBUG("Openthread: Received pkt\n"); + netdev_ieee802154_rx_info_t rx_info; + /* Read frame length from driver */ + int len = dev->driver->recv(dev, NULL, 0, &rx_info); + Rssi = rx_info.rssi; + + /* very unlikely */ + if ((len > (unsigned) UINT16_MAX)) { + otPlatRadioReceiveDone(aInstance, NULL, kThreadError_Abort); + return; + } + + /* Fill OpenThread receive frame */ + sReceiveFrame.mLength = len; + sReceiveFrame.mPower = _get_power(); + + /* Read received frame */ + int res = dev->driver->recv(dev, (char *) sReceiveFrame.mPsdu, len, NULL); + + /* Tell OpenThread that receive has finished */ + otPlatRadioReceiveDone(aInstance, res > 0 ? &sReceiveFrame : NULL, res > 0 ? kThreadError_None : kThreadError_Abort); +} + +/* Called upon TX event */ +void send_pkt(otInstance *aInstance, netdev_t *dev, netdev_event_t event) +{ + /* Tell OpenThread transmission is done depending on the NETDEV event */ + switch (event) { + case NETDEV_EVENT_TX_COMPLETE: + DEBUG("openthread: NETDEV_EVENT_TX_COMPLETE\n"); + otPlatRadioTransmitDone(aInstance, &sTransmitFrame, false, kThreadError_None); + break; + case NETDEV_EVENT_TX_COMPLETE_DATA_PENDING: + DEBUG("openthread: NETDEV_EVENT_TX_COMPLETE_DATA_PENDING\n"); + otPlatRadioTransmitDone(aInstance, &sTransmitFrame, true, kThreadError_None); + break; + case NETDEV_EVENT_TX_NOACK: + DEBUG("openthread: NETDEV_EVENT_TX_NOACK\n"); + otPlatRadioTransmitDone(aInstance, &sTransmitFrame, false, kThreadError_NoAck); + break; + case NETDEV_EVENT_TX_MEDIUM_BUSY: + DEBUG("openthread: NETDEV_EVENT_TX_MEDIUM_BUSY\n"); + otPlatRadioTransmitDone(aInstance, &sTransmitFrame, false, kThreadError_ChannelAccessFailure); + break; + default: + break; + } +} + +/* OpenThread will call this for setting PAN ID */ +void otPlatRadioSetPanId(otInstance *aInstance, uint16_t panid) +{ + DEBUG("openthread: otPlatRadioSetPanId: setting PAN ID to %04x\n", panid); + _set_panid(panid); +} + +/* OpenThread will call this for setting extended address */ +void otPlatRadioSetExtendedAddress(otInstance *aInstance, uint8_t *aExtendedAddress) +{ + DEBUG("openthread: otPlatRadioSetExtendedAddress\n"); + uint8_t reversed_addr[IEEE802154_LONG_ADDRESS_LEN]; + for (int i = 0; i < IEEE802154_LONG_ADDRESS_LEN; i++) { + reversed_addr[i] = aExtendedAddress[IEEE802154_LONG_ADDRESS_LEN - 1 - i]; + } + _set_long_addr(reversed_addr); +} + +/* OpenThread will call this for setting short address */ +void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aShortAddress) +{ + DEBUG("openthread: otPlatRadioSetShortAddress: setting address to %04x\n", aShortAddress); + _set_addr(((aShortAddress & 0xff) << 8) | ((aShortAddress >> 8) & 0xff)); +} + +/* OpenThread will call this for enabling the radio */ +ThreadError otPlatRadioEnable(otInstance *aInstance) +{ + DEBUG("openthread: otPlatRadioEnable\n"); + (void) aInstance; + + ThreadError error; + + if (sDisabled) { + sDisabled = false; + error = kThreadError_None; + } + else { + error = kThreadError_InvalidState; + } + + return error; +} + +/* OpenThread will call this for disabling the radio */ +ThreadError otPlatRadioDisable(otInstance *aInstance) +{ + DEBUG("openthread: otPlatRadioDisable\n"); + (void) aInstance; + + ThreadError error; + + if (!sDisabled) { + sDisabled = true; + error = kThreadError_None; + } + else { + error = kThreadError_InvalidState; + } + + return error; +} + +bool otPlatRadioIsEnabled(otInstance *aInstance) +{ + DEBUG("otPlatRadioIsEnabled\n"); + (void) aInstance; + + return !sDisabled; +} + +/* OpenThread will call this for setting device state to SLEEP */ +ThreadError otPlatRadioSleep(otInstance *aInstance) +{ + DEBUG("otPlatRadioSleep\n"); + (void) aInstance; + + _set_sleep(); + return kThreadError_None; +} + +/*OpenThread will call this for waiting the reception of a packet */ +ThreadError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel) +{ + DEBUG("openthread: otPlatRadioReceive. Channel: %i\n", aChannel); + (void) aInstance; + + _set_idle(); + _set_channel(aChannel); + return kThreadError_None; +} + + +/* OpenThread will call this function to get the transmit buffer */ +RadioPacket *otPlatRadioGetTransmitBuffer(otInstance *aInstance) +{ + DEBUG("openthread: otPlatRadioGetTransmitBuffer\n"); + return &sTransmitFrame; +} + +/* OpenThread will call this function to set the transmit power */ +void otPlatRadioSetDefaultTxPower(otInstance *aInstance, int8_t aPower) +{ + (void)aInstance; + + _set_power(aPower); +} + +/* OpenThread will call this for transmitting a packet*/ +ThreadError otPlatRadioTransmit(otInstance *aInstance, RadioPacket *aPacket) +{ + (void) aInstance; + struct iovec pkt; + + /* Populate iovec with transmit data */ + pkt.iov_base = aPacket->mPsdu; + pkt.iov_len = aPacket->mLength; + + /*Set channel and power based on transmit frame */ + DEBUG("otPlatRadioTransmit->channel: %i, length %d\n", (int) aPacket->mChannel, aPacket->mLength); + _set_channel(aPacket->mChannel); + _set_power(aPacket->mPower); + + /* send packet though netdev */ + _dev->driver->send(_dev, &pkt, 1); + + return kThreadError_None; +} + +/* OpenThread will call this for getting the radio caps */ +otRadioCaps otPlatRadioGetCaps(otInstance *aInstance) +{ + DEBUG("openthread: otPlatRadioGetCaps\n"); + /* all drivers should handle ACK, including call of NETDEV_EVENT_TX_NOACK */ + return kRadioCapsNone; +} + +/* OpenThread will call this for getting the state of promiscuous mode */ +bool otPlatRadioGetPromiscuous(otInstance *aInstance) +{ + DEBUG("openthread: otPlatRadioGetPromiscuous\n"); + return _is_promiscuous(); +} + +/* OpenThread will call this for setting the state of promiscuous mode */ +void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable) +{ + DEBUG("openthread: otPlatRadioSetPromiscuous\n"); + _set_promiscuous((aEnable) ? NETOPT_ENABLE : NETOPT_DISABLE); +} + +int8_t otPlatRadioGetRssi(otInstance *aInstance) +{ + DEBUG("otPlatRadioGetRssi\n"); + (void) aInstance; + return Rssi; +} + +void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable) +{ + DEBUG("otPlatRadioEnableSrcMatch\n"); + (void)aInstance; + (void)aEnable; +} + +ThreadError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress) +{ + DEBUG("otPlatRadioAddSrcMatchShortEntry\n"); + (void)aInstance; + (void)aShortAddress; + return kThreadError_None; +} + +ThreadError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const uint8_t *aExtAddress) +{ + DEBUG("otPlatRadioAddSrcMatchExtEntry\n"); + (void)aInstance; + (void)aExtAddress; + return kThreadError_None; +} + +ThreadError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress) +{ + DEBUG("otPlatRadioClearSrcMatchShortEntry\n"); + (void)aInstance; + (void)aShortAddress; + return kThreadError_None; +} + +ThreadError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const uint8_t *aExtAddress) +{ + DEBUG("otPlatRadioClearSrcMatchExtEntry\n"); + (void)aInstance; + (void)aExtAddress; + return kThreadError_None; +} + +void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance) +{ + DEBUG("otPlatRadioClearSrcMatchShortEntries\n"); + (void)aInstance; +} + +void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance) +{ + DEBUG("otPlatRadioClearSrcMatchExtEntries\n"); + (void)aInstance; +} + +ThreadError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration) +{ + DEBUG("otPlatRadioEnergyScan\n"); + (void)aInstance; + (void)aScanChannel; + (void)aScanDuration; + return kThreadError_NotImplemented; +} + +void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeee64Eui64) +{ + _dev->driver->get(_dev, NETOPT_IPV6_IID, aIeee64Eui64, sizeof(eui64_t)); +} + + + +int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance) +{ + return -100; +} +/** @} */ diff --git a/pkg/openthread/contrib/platform_random.c b/pkg/openthread/contrib/platform_random.c new file mode 100644 index 000000000000..f41f5fde6e25 --- /dev/null +++ b/pkg/openthread/contrib/platform_random.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2017 Fundacion Inria Chile + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @author Jose Ignacio Alamos + */ + +#include + +#include "openthread/platform/random.h" +#include "periph/cpuid.h" +#include "random.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* init random */ +void ot_random_init(void) +{ +#ifdef CPUID_LEN + char cpu_id[CPUID_LEN]; + cpuid_get(cpu_id); + uint32_t seed = 0; + for (int i = 0; i < (int) CPUID_LEN; i++) { + seed += cpu_id[i]; + } + random_init(seed); +#else + #error "CPU not supported (current CPU doesn't provide CPUID, required for entropy)" +#endif +} + +/* OpenThread will call this to get a random number */ +uint32_t otPlatRandomGet(void) +{ + uint32_t rand_val = random_uint32(); + + DEBUG("otPlatRandomGet: %i\n", (int) rand_val); + return rand_val; +} + +/** @} */ diff --git a/pkg/openthread/contrib/platform_settings.c b/pkg/openthread/contrib/platform_settings.c new file mode 100644 index 000000000000..e04b9a3bdf2b --- /dev/null +++ b/pkg/openthread/contrib/platform_settings.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2017 Fundacion Inria Chile + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @author Jose Ignacio Alamos + */ + +#include "assert.h" +#include "openthread/types.h" + +#include "debug.h" +#define ENABLE_DEBUG (1) + +void otPlatSettingsInit(otInstance *aInstance) +{ +} + +ThreadError otPlatSettingsBeginChange(otInstance *aInstance) +{ + (void)aInstance; + return kThreadError_None; +} + +ThreadError otPlatSettingsCommitChange(otInstance *aInstance) +{ + DEBUG("openthread: otPlatSettingsCommitChange\n"); + (void)aInstance; + return kThreadError_None; +} + +ThreadError otPlatSettingsAbandonChange(otInstance *aInstance) +{ + (void)aInstance; + return kThreadError_None; +} + +ThreadError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength) +{ + DEBUG("openthread: otPlatSettingsGet\n"); + *aValueLength = 0; + return kThreadError_NotImplemented; +} + +ThreadError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) +{ + return kThreadError_None; +} + +ThreadError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) +{ + return kThreadError_None; +} + +ThreadError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex) +{ + return kThreadError_None; +} + +void otPlatSettingsWipe(otInstance *aInstance) +{ +} + diff --git a/pkg/openthread/contrib/platform_uart.c b/pkg/openthread/contrib/platform_uart.c new file mode 100644 index 000000000000..912fe70ef1a1 --- /dev/null +++ b/pkg/openthread/contrib/platform_uart.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2017 Fundacion Inria Chile + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @author Jose Ignacio Alamos + */ + +#include +#include +#include + +#include + +/* OpenThread will call this for enabling UART (required for OpenThread's CLI)*/ +ThreadError otPlatUartEnable(void) +{ + return kThreadError_None; +} + +/* OpenThread will call this for disabling UART */ +ThreadError otPlatUartDisable(void) +{ + return kThreadError_None; +} + +/* OpenThread will call this for sending data through UART */ +ThreadError otPlatUartSend(const uint8_t *aBuf, uint16_t aBufLength) +{ + uart_write(UART_DEV(0), aBuf, aBufLength); + + /* Tell OpenThread the sending of UART is done */ + otPlatUartSendDone(); + + return kThreadError_None; +} + +/** @} */ diff --git a/pkg/openthread/contrib/sock/Makefile b/pkg/openthread/contrib/sock/Makefile new file mode 100644 index 000000000000..4b4190ceb70d --- /dev/null +++ b/pkg/openthread/contrib/sock/Makefile @@ -0,0 +1,3 @@ +MODULE := openthread_sock + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/openthread/contrib/sock/openthread_sock.c b/pkg/openthread/contrib/sock/openthread_sock.c new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/pkg/openthread/contrib/sock/udp/Makefile b/pkg/openthread/contrib/sock/udp/Makefile new file mode 100644 index 000000000000..055a84deddb9 --- /dev/null +++ b/pkg/openthread/contrib/sock/udp/Makefile @@ -0,0 +1,3 @@ +MODULE := openthread_sock_udp + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/openthread/contrib/sock/udp/openthread_sock_udp.c b/pkg/openthread/contrib/sock/udp/openthread_sock_udp.c new file mode 100644 index 000000000000..382dc54e6f15 --- /dev/null +++ b/pkg/openthread/contrib/sock/udp/openthread_sock_udp.c @@ -0,0 +1,390 @@ +/* + * Copyright (C) 2017 Fundacion Inria Chile + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @author Jose Ignacio Alamos + */ + +#include + +#include "net/ipv6/addr.h" +#include "net/sock/udp.h" +#include "net/udp.h" +#include "net/sock.h" +#include "timex.h" +#include "net/af.h" +#include "ot.h" +#include "openthread/thread.h" +#include "openthread/udp.h" +#include "byteorder.h" +#include "net/protnum.h" + +#define ENABLE_DEBUG (1) +#include "debug.h" + +static void _handle_receive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) +{ + size_t payload_len = otMessageGetLength(aMessage) - otMessageGetOffset(aMessage); + sock_udp_t *sock = aContext; + + if (sock->data == NULL || payload_len > sock->max_len) { + return; + } + + otMessageRead(aMessage, otMessageGetOffset(aMessage), sock->data, payload_len); + sock->data = NULL; + sock->len = payload_len; + + // msg_t msg = { .type = _MSG_TYPE_RCV }; + msg_t msg = { .type = 1 }; + + //mutex_lock(&sock->mutex); + /*sock->recv_info.src_port = src_port; + sock->recv_info.src = (const ipv6_addr_t *)src_addr; + sock->recv_info.data = data; + sock->recv_info.datalen = datalen - sizeof(ipv6_hdr_t);*/ + //mutex_unlock(&sock->mutex); + mbox_put(&sock->mbox, &msg); +} + +/** + * @brief Internal helper functions for OPENTHREAD + * @internal + * @{ + */ +/** + * @brief Checks if address family is not supported + * @internal + */ +static inline bool openthread_af_not_supported(int af) +{ + /* TODO: add AF_INET support */ + return (af != AF_INET6); +} + +/** + * @brief Check if end point points to any address + * @internal + */ +static inline bool openthread_ep_addr_any(const sock_ip_ep_t *ep) +{ + assert(ep); + const uint8_t *p = (uint8_t *)&ep->addr; + for (uint8_t i = 0; i < sizeof(ep->addr); i++) { + if (p[i] != 0) { + return false; + } + } + return true; +} +/* + static OT_JOB _set_panid(otInstance *ot_instance, void *data) + { + uint16_t panid = *((uint16_t*) data); + otLinkSetPanId(ot_instance, panid); + } + + static OT_JOB _get_panid(otInstance *ot_instance, void *data) + { + *((uint16_t*) data) = otLinkGetPanId(ot_instance); + printf("PanID: %04x\n", *((uint16_t*) data)); + } + + static OT_JOB _thread_start(otInstance *ot_instance, void *data) + { + printf("Starting OpenThread\n"); + otIp6SetEnabled(ot_instance, true); + otThreadSetEnabled(ot_instance, true); + } + + static OT_JOB _read_state(otInstance *ot_instance, void *data) + { + uint8_t state = otThreadGetDeviceRole(ot_instance); + printf("State is: %i\n", state); + } + + static OT_JOB _get_ip_addresses(otInstance *ot_instance, void *data) + { + for(const otNetifAddress *addr=otIp6GetUnicastAddresses(ot_instance); addr; addr=addr->mNext) + { + char addrstr[IPV6_ADDR_MAX_STR_LEN]; + printf("inet6 %s\n", ipv6_addr_to_str(addrstr, (ipv6_addr_t*) &addr->mAddress.mFields, sizeof(addrstr))); + } + } + */ +static OT_JOB _create_udp_socket(otInstance *ot_instance, void *data) +{ + sock_udp_t *sock = (sock_udp_t *) data; + otSockAddr sockaddr; + + memset(&sockaddr, 0, sizeof(otSockAddr)); + sockaddr.mPort = *((uint16_t *) &(sock->local.port)); + + otUdpOpen(ot_instance, &sock->ot_udp_socket, _handle_receive, sock); + otUdpBind(&sock->ot_udp_socket, &sockaddr); +} + +static OT_JOB _send_udp_pkt(otInstance *ot_instance, void *data) +{ + sock_udp_t *sock = (sock_udp_t *) data; + udp_pkt_t *pkt = (udp_pkt_t *) &(sock->pkt); + otMessage *message; + + message = otUdpNewMessage(ot_instance, true); + + /* TODO: Handle errors here */ + otMessageSetLength(message, pkt->len); + otMessageWrite(message, 0, pkt->payload, pkt->len); + + otMessageInfo mPeer; + + /* Set dest address */ + memcpy(&mPeer.mPeerAddr.mFields, &(pkt->ip_addr), sizeof(ipv6_addr_t)); + + /* Set dest port */ + mPeer.mPeerPort = pkt->port; + + otUdpSend(&sock->ot_udp_socket, message, &mPeer); +} + +int sock_udp_create(sock_udp_t *sock, const sock_udp_ep_t *local, + const sock_udp_ep_t *remote, uint16_t flags) +{ + uint16_t socket_port; + + assert(sock); + assert(local == NULL || local->port != 0); + assert(remote == NULL || remote->port != 0); + if ((local) && (remote) && + (local->netif != SOCK_ADDR_ANY_NETIF) && + (remote->netif != SOCK_ADDR_ANY_NETIF) && + (local->netif != remote->netif)) { + return -EINVAL; + } + + mbox_init(&sock->mbox, sock->mbox_queue, SOCK_MBOX_SIZE); + sock->data = NULL; + memset(&sock->local, 0, sizeof(sock_udp_ep_t)); + + if (local) { + if (openthread_af_not_supported(local->family)) { + return -EAFNOSUPPORT; + } + memcpy(&sock->local, local, sizeof(sock_udp_ep_t)); + } + + memset(&sock->remote, 0, sizeof(sock_udp_ep_t)); + + if (remote) { + if (openthread_af_not_supported(remote->family)) { + return -EAFNOSUPPORT; + } + if (openthread_ep_addr_any((const sock_ip_ep_t *)remote)) { + return -EINVAL; + } + memcpy(&sock->remote, remote, sizeof(sock_udp_ep_t)); + } + + memset(&sock->ot_udp_socket, 0, sizeof(otUdpSocket)); + + socket_port = local->port; + + if (local) { + DEBUG("Socket port %d \n", socket_port); + ot_exec_job(_create_udp_socket, sock); + } + else { + DEBUG("Null local\n"); + } + sock->flags = flags; + + return 0; +} + +void sock_udp_close(sock_udp_t *sock) +{ + assert(sock); + if (sock->ot_udp_socket.mTransport) { + otUdpClose(&sock->ot_udp_socket); + memset(&sock->ot_udp_socket, 0, sizeof(otUdpSocket)); + } +} + +int sock_udp_get_local(sock_udp_t *sock, sock_udp_ep_t *local) +{ + assert(sock && local); + if (sock->local.family == AF_UNSPEC) { + return -EADDRNOTAVAIL; + } + memcpy(local, &sock->local, sizeof(sock_udp_ep_t)); + return 0; +} + +int sock_udp_get_remote(sock_udp_t *sock, sock_udp_ep_t *remote) +{ + assert(sock && remote); + if (sock->remote.family == AF_UNSPEC) { + return -ENOTCONN; + } + memcpy(remote, &sock->remote, sizeof(sock_udp_ep_t)); + return 0; +} + +ssize_t sock_udp_recv(sock_udp_t *sock, void *data, size_t max_len, + uint32_t timeout, sock_udp_ep_t *remote) +{ + // xtimer_t timeout_timer; + //int blocking = BLOCKING; + int res = -EIO; + msg_t msg; + + /* + assert((sock != NULL) && (data != NULL) && (max_len > 0)); + if (sock->sock.input_callback == NULL) { + return -EADDRNOTAVAIL; + } + if (timeout == 0) { + blocking = NON_BLOCKING; + }*/ + /* + else if (timeout != SOCK_NO_TIMEOUT) { + timeout_timer.callback = _timeout_callback; + timeout_timer.arg = &sock->mbox; + xtimer_set(&timeout_timer, timeout); + }*/ + + //atomic_fetch_add(&sock->receivers, 1); + sock->data = data; + sock->max_len = max_len; + + if (_mbox_get(&sock->mbox, &msg, true) == 0) { + /* do not need to remove xtimer, since we only get here in non-blocking + * mode (timeout > 0) */ + return -EAGAIN; + } + + switch (msg.type) { + /* TODO: Label this */ + case 1: + /* + if (remote != NULL) { + remote->family = AF_INET6; + remote->netif = SOCK_ADDR_ANY_NETIF; + memcpy(&remote->addr, &sock->recv_info.src, sizeof(ipv6_addr_t)); + remote->port = sock->recv_info.src_port; + } + */ + res = (int) sock->len; + break; +#if 0 + case _MSG_TYPE_CLOSE: + res = -EADDRNOTAVAIL; + break; + case _MSG_TYPE_TIMEOUT: + res = -ETIMEDOUT; + break; +#endif + } + //atomic_fetch_sub(&sock->receivers, 1); + return res; +} + +ssize_t sock_udp_send(sock_udp_t *sock, const void *data, size_t len, + const sock_udp_ep_t *remote) +{ + udp_pkt_t pkt; + uint16_t src_port = 0, dst_port; + sock_ip_ep_t local; + sock_ip_ep_t *rem; + + assert((sock) || (remote)); + assert((len == 0) || data); /* (len != 0) => (data != NULL) */ + + if (remote) { + if (remote->port == 0) { + return -EINVAL; + } + else if (openthread_ep_addr_any((const sock_ip_ep_t *)remote)) { + return -EINVAL; + } + else if (openthread_af_not_supported(remote->family)) { + return -EAFNOSUPPORT; + } + else if ((sock) && + (sock->local.netif != SOCK_ADDR_ANY_NETIF) && + (remote->netif != SOCK_ADDR_ANY_NETIF) && + (sock->local.netif != remote->netif)) { + return -EINVAL; + } + } + else if (sock->remote.family == AF_UNSPEC) { + return -ENOTCONN; + } + /* compiler evaluates lazily so this isn't a redundundant check and cppcheck + * is being weird here anyways */ + /* cppcheck-suppress nullPointerRedundantCheck */ + /* cppcheck-suppress nullPointer */ + if ((sock == NULL) || (sock->local.family == AF_UNSPEC)) { + /* no sock or sock currently unbound */ + if ((sock)) { + /* bind sock object implicitly */ + sock->local.port = src_port; + if (remote == NULL) { + sock->local.family = sock->remote.family; + } + else { + sock->local.family = remote->family; + } + ot_exec_job(_create_udp_socket, sock); + } + } + else { + src_port = sock->local.port; + memcpy(&local, &sock->local, sizeof(local)); + } + /* sock can't be NULL at this point */ + if (remote == NULL) { + rem = (sock_ip_ep_t *)&sock->remote; + dst_port = sock->remote.port; + } + else { + rem = (sock_ip_ep_t *)remote; + dst_port = remote->port; + } + /* check for matching address families in local and remote */ + if (local.family == AF_UNSPEC) { + local.family = rem->family; + } + else if (local.family != rem->family) { + return -EINVAL; + } + + /* openthread socket must have been opened*/ + if (sock->ot_udp_socket.mTransport == NULL) { + return -EINVAL; + } + + remote = (struct _sock_tl_ep *) remote; + + /* build packet and send */ + memcpy(&pkt.ip_addr, &remote->addr, sizeof(remote->addr)); + pkt.port = dst_port; + pkt.payload = (void *) data; + pkt.len = len; + + memcpy(&sock->pkt, &pkt, sizeof(pkt)); + + ot_exec_job(_send_udp_pkt, sock); + + return 0; +} + +/** @} */ diff --git a/pkg/openthread/include/openthread/openthread_sock_internal.h b/pkg/openthread/include/openthread/openthread_sock_internal.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/pkg/openthread/include/openthread/sock_types.h b/pkg/openthread/include/openthread/sock_types.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/pkg/openthread/include/ot.h b/pkg/openthread/include/ot.h new file mode 100644 index 000000000000..efe2aaa3e854 --- /dev/null +++ b/pkg/openthread/include/ot.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2017 Fundación Inria Chile + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup pkg_openthread_cli OpenThread + * @ingroup pkg_openthread + * @brief An open source implementation of Thread stack + * @see https://github.com/openthread/openthread + * + * Thread if a mesh oriented network stack running for IEEE802.15.4 networks. + * @{ + * + * @file + * + * @author José Ignacio Alamos + */ + +#ifndef OT_H +#define OT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "net/netopt.h" +#include "net/ieee802154.h" +#include "net/ethernet.h" +#include "net/gnrc/netdev.h" +#include "thread.h" +#include "openthread/types.h" + +#define OPENTHREAD_XTIMER_MSG_TYPE_EVENT (0x2235) /**< xtimer message receiver event*/ +#define OPENTHREAD_NETDEV_MSG_TYPE_EVENT (0x2236) /**< message received from driver */ +#define OPENTHREAD_SERIAL_MSG_TYPE_EVENT (0x2237) /**< event indicating a serial (UART) message was sent to OpenThread */ +#define OPENTHREAD_MSG_TYPE_RECV (0x2238) /**< event for frame reception */ +#define OPENTHREAD_JOB_MSG_TYPE_EVENT (0x2240) /**< event indicating an OT_JOB message */ + +typedef void OT_JOB; + +/** + * @brief Struct containing a serial message + * + */ +typedef struct { + void *buf; /**< buffer containing the message */ + size_t len; /**< length of the message */ +} serial_msg_t; + +/** + * @brief Struct containing an OpenThread job + */ +typedef struct { + void (*function)(otInstance*, void*); /**< function to be called when executing job */ + void *context; /**< context for the job **/ +} ot_job_t; + +/** + * @brief Gets packet from driver and tells OpenThread about the reception. + * + * @param[in] aInstance pointer to an OpenThread instance + */ +void recv_pkt(otInstance *aInstance, netdev_t *dev); + +/** + * @brief Gets packet from driver and tells OpenThread about the reception. + * + * @param[in] aInstance pointer to an OpenThread instance + */ + +/** + * @brief Inform OpenThread when tx is finished + * + * @param[in] aInstance pointer to an OpenThread instance + * @param[in] dev pointer to a netdev interface + * @param[in] event just occurred netdev event + */ +void send_pkt(otInstance *aInstance, netdev_t *dev, netdev_event_t event); + +/** + * @brief Bootstrap OpenThread + */ +void openthread_bootstrap(void); + +/** + * @brief Init OpenThread radio + * + * @param[in] dev pointer to a netdev interface + * @param[in] tb pointer to the TX buffer designed for OpenThread + * @param[in] event pointer to the RX buffer designed for Open_Thread + */ +void openthread_radio_init(netdev_t *dev, uint8_t *tb, uint8_t *rb); + + +/** + * @brief Starts OpenThread thread. + * + * @param[in] stack pointer to the stack designed for OpenThread + * @param[in] stacksize size of the stack + * @param[in] priority priority of the OpenThread stack + * @param[in] name name of the OpenThread stack + * @param[in] netdev pointer to the netdev interface + * + * @return PID of OpenThread thread + * @return -EINVAL if there was an error creating the thread + */ +int openthread_netdev_init(char *stack, int stacksize, char priority, const char *name, netdev_t *netdev); + +/** + * @brief get PID of OpenThread thread. + * + * @return PID of OpenThread thread + */ +kernel_pid_t openthread_get_pid(void); + +/** + * @brief Init OpenThread random + */ +void ot_random_init(void); + +#if defined(MODULE_OPENTHREAD_CLI) || defined(MODULE_OPENTHREAD_NCP) +/* + * @brief Run OpenThread UART simulator (stdio) + */ +void openthread_uart_run(void); + +#endif + +/** + * @brief Execute OpenThread job in same thread as OT core (dute to concurrency). + * + * @note An OpenThread job allows direct calls to OpenThread API (otXXX functions) without worrying about concurrency + * issues. All API calls should be made in OT_JOB type functions. + * + * @param[in] job callback pointer to an OpenThread job function + * @param[in] context context for the job + */ +void ot_exec_job(OT_JOB (*job)(otInstance*, void*), void *context); + +#ifdef __cplusplus +} +#endif + +#endif +/** @} */ diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index 30fc3be9de85..0a91baa15789 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -72,6 +72,10 @@ #include "lwip.h" #endif +#ifdef MODULE_OPENTHREAD +#include "ot.h" +#endif + #ifdef MODULE_FIB #include "net/fib.h" #endif @@ -140,6 +144,10 @@ void auto_init(void) DEBUG("Bootstraping lwIP.\n"); lwip_bootstrap(); #endif +#ifdef MODULE_OPENTHREAD + extern void openthread_bootstrap(void); + openthread_bootstrap(); +#endif #ifdef MODULE_GCOAP DEBUG("Auto init gcoap module.\n"); gcoap_init(); diff --git a/tests/openthread/Makefile b/tests/openthread/Makefile new file mode 100644 index 000000000000..aced508fbb9a --- /dev/null +++ b/tests/openthread/Makefile @@ -0,0 +1,47 @@ +APPLICATION = openthread_test + +# If no BOARD is found in the environment, use this default: +BOARD ?= samr21-xpro + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: + +CFLAGS += -DDEVELHELP -Wall + +#Uncomment the following line for running OpenThread CLI +#CFLAGS += -DMODULE_OPENTHREAD_CLI + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +USEMODULE += openthread + +ifneq (,$(filter samr21-xpro,$(BOARD))) + DRIVER := at86rf233 +endif +ifneq (,$(filter iotlab-m3 fox,$(BOARD))) + DRIVER := at86rf231 +endif + +ifneq (,$(filter at86rf2%,$(DRIVER))) + FEATURES_REQUIRED = periph_spi periph_gpio +endif + +USEMODULE += $(DRIVER) + +USEMODULE += random + +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps +USEMODULE += ipv6_addr + +#required for C++ compiling +CXXEXFLAGS += -fno-rtti +USEMODULE += cpp11-compat + +include $(RIOTBASE)/Makefile.include diff --git a/tests/openthread/README.md b/tests/openthread/README.md new file mode 100644 index 000000000000..ad4e517e1e42 --- /dev/null +++ b/tests/openthread/README.md @@ -0,0 +1,34 @@ +## OpenThread on RIOT + +This test demonstrates how to use NEST's OpenThread stack from [OpenThread](https://github.com/openthread/openthread) +from RIOT. It shows how to use OpenThread UDP capacilities and how to interact with the API. + +## Quick usage + +To test OpenThread on RIOT, you can do the following: + +1. Flash some nodes with `make BOARD= clean all flash` +2. Set the PAN ID in all nodes to the same with ifconfig command (e.g `ifconfig set panid 0x1234`). +3. Start OpenThread with `ifconfig thread start` +3. Check the state of each node with `ifconfig thread state`. After some + seconds you will see one node being `leader` and some nodes being `router` or `child`. +5. Get the mesh IP address of a node with `ifconfig`. + `ifconfig` + `fdde:ad00:beef::ff:fe00:8000` + `fe80::ff:fe00:8000` + `fdde:ad00:beef:0:946a:c722:a5d9:8481` + `fe80::3984:f4eb:d182:5dae` + + Addresses starting with `fd` are mesh-local, and addresses starting with `fe80` are link-local. + Mesh-local address types that contain `ff:fe00` are classified as Router Locator (RLOC). Mesh-local address types + that don't contain `ff:fe00` are Endpoint Identifies (EID). +6. Start a UDP server on each node with the `udp server` command (e.g `udp server 12345` will open port 12345) +7. Send a udp message with `udp send` command. Pick a mesh-local address (e.g `udp send fdde:ad00:beef:0:946a:c722:a5d9:8481 12345 "This is RIOT!"`) +8. Enjoy! + +## Note + +See the usage of OpenThread jobs (OT JOB functions). These are required because OpenThread is running in another thread, +and directly calling otApi functions may cause crashes due to concurrency. Please call all OpenThread API functions inside an OT JOB, +and execute with ot_exec_job. This may change in the future. +See the OpenThread documentation for more information about OpenThread API. diff --git a/tests/openthread/main.c b/tests/openthread/main.c new file mode 100644 index 000000000000..a184632aa7d9 --- /dev/null +++ b/tests/openthread/main.c @@ -0,0 +1,214 @@ +#include + +#include "net/ipv6/addr.h" +#include "openthread/ip6.h" +#include "openthread/thread.h" +#include "openthread/udp.h" +#include "ot.h" +#include "shell.h" +#include "shell_commands.h" + +otUdpSocket mSocket; + +static OT_JOB _set_panid(otInstance *ot_instance, void *context) +{ + uint16_t panid = *((uint16_t *) context); + + otLinkSetPanId(ot_instance, panid); +} + +static OT_JOB _get_panid(otInstance *ot_instance, void *context) +{ + *((uint16_t *) context) = otLinkGetPanId(ot_instance); + printf("PanID: %04x\n", *((uint16_t *) context)); +} + +static OT_JOB _thread_start(otInstance *ot_instance, void *context) +{ + printf("Starting OpenThread\n"); + otIp6SetEnabled(ot_instance, true); + otThreadSetEnabled(ot_instance, true); +} + +static OT_JOB _read_state(otInstance *ot_instance, void *context) +{ + uint8_t state = otThreadGetDeviceRole(ot_instance); + + switch (state) { + case kDeviceRoleOffline: + puts("offline"); + break; + case kDeviceRoleDisabled: + puts("disabled"); + break; + case kDeviceRoleDetached: + puts("detached"); + break; + case kDeviceRoleChild: + puts("child"); + break; + case kDeviceRoleRouter: + puts("router"); + break; + case kDeviceRoleLeader: + puts("leader"); + break; + } +} + +static OT_JOB _get_ip_addresses(otInstance *ot_instance, void *context) +{ + for (const otNetifAddress *addr = otIp6GetUnicastAddresses(ot_instance); addr; addr = addr->mNext) { + char addrstr[IPV6_ADDR_MAX_STR_LEN]; + ipv6_addr_to_str(addrstr, (ipv6_addr_t *) &addr->mAddress.mFields, sizeof(addrstr)); + printf("inet6 %s\n", addrstr); + } +} + +void _handle_receive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) +{ + size_t payload_len = otMessageGetLength(aMessage) - otMessageGetOffset(aMessage); + + char buf[100]; + + otMessageRead(aMessage, otMessageGetOffset(aMessage), buf, payload_len); + + printf("Message: "); + for (int i = 0; i < payload_len; i++) { + printf("%02x ", buf[i]); + } + printf("\n"); +} + +static OT_JOB _create_udp_socket(otInstance *ot_instance, void *context) +{ + otSockAddr sockaddr; + + memset(&sockaddr, 0, sizeof(otSockAddr)); + sockaddr.mPort = *((uint16_t *) context); + + otUdpOpen(ot_instance, &mSocket, _handle_receive, NULL); + otUdpBind(&mSocket, &sockaddr); +} + +typedef struct { + ipv6_addr_t ip_addr; + uint16_t port; + void *payload; + size_t len; +} udp_pkt_t; + +static OT_JOB _send_udp_pkt(otInstance *ot_instance, void *context) +{ + udp_pkt_t *pkt = (udp_pkt_t *) context; + otMessage *message; + + otUdpSocket socket; + + memset(&socket, 0, sizeof(otUdpSocket)); + + otUdpOpen(ot_instance, &socket, _handle_receive, NULL); + + message = otUdpNewMessage(ot_instance, true); + otMessageSetLength(message, pkt->len); + otMessageWrite(message, 0, pkt->payload, pkt->len); + + otMessageInfo mPeer; + + //Set dest address + memcpy(&mPeer.mPeerAddr.mFields, &(pkt->ip_addr), sizeof(ipv6_addr_t)); + + //Set dest port + mPeer.mPeerPort = pkt->port; + + otUdpSend(&socket, message, &mPeer); + otUdpClose(&socket); +} + +int _udp(int argc, char **argv) +{ + if (argc < 3) { + return 1; + } + else if (strcmp(argv[1], "server") == 0) { + uint16_t port = atoi(argv[2]); + ot_exec_job(_create_udp_socket, &port); + } + else if (argc >= 2 && strcmp(argv[1], "send") == 0) { + /* send packet */ + udp_pkt_t pkt; + ipv6_addr_from_str(&pkt.ip_addr, argv[2]); + pkt.port = atoi(argv[3]); + pkt.payload = argv[4]; + pkt.len = strlen(argv[4]); + ot_exec_job(_send_udp_pkt, &pkt); + } + return 0; +} + +static void _usage(char *cmd) +{ + printf("Usage: %s \n", cmd); + puts(" List Thread IPv6 addresses"); + printf(" %s \n", cmd); + puts(" Get/Set PAN ID"); + printf(" %s thread start \n", cmd); + puts(" Start the thread network"); + printf(" %s thread state \n", cmd); + puts(" Asks for the state of a thread network."); +} + +int _ifconfig(int argc, char **argv) +{ + uint16_t panid; + + if (argc >= 2) { + if (strcmp(argv[1], "get") == 0 && argc >= 3) { + if (strcmp(argv[2], "panid") == 0) { + ot_exec_job(_get_panid, &panid); + } + } + else if (strcmp(argv[1], "set") == 0 && argc >= 4) { + if (strcmp(argv[2], "panid") == 0) { + panid = strtol(argv[3], NULL, 0); + ot_exec_job(_set_panid, &panid); + } + } + else if (strcmp(argv[1], "thread") == 0) { + if (strcmp(argv[2], "start") == 0) { + ot_exec_job(_thread_start, NULL); + } + else if (strcmp(argv[2], "state") == 0) { + uint8_t state; + ot_exec_job(_read_state, &state); + } + } + else { + _usage(argv[0]); + } + } + else { + ot_exec_job(_get_ip_addresses, NULL); + } + return 0; +} + +#if !defined(MODULE_OPENTHREAD_CLI) && !defined(MODULE_OPENTHREAD_NCP) +static const shell_command_t shell_commands[] = { + { "ifconfig", "Get or set panid", _ifconfig }, + { "udp", "Test udp", _udp }, + { NULL, NULL, NULL } +}; +#endif + +int main(void) +{ +#if defined(MODULE_OPENTHREAD_CLI) || defined(MODULE_OPENTHREAD_NCP) + openthread_uart_run(); +#else + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); +#endif + + return 0; +} diff --git a/tests/openthread_sock_udp/Makefile b/tests/openthread_sock_udp/Makefile new file mode 100644 index 000000000000..c684f11b4ed0 --- /dev/null +++ b/tests/openthread_sock_udp/Makefile @@ -0,0 +1,52 @@ +APPLICATION = openthread_sock_udp + +# If no BOARD is found in the environment, use this default: +BOARD ?= samr21-xpro + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: + +CFLAGS += -DDEVELHELP -Wall + +#Uncomment the following line for running OpenThread CLI +#CFLAGS += -DMODULE_OPENTHREAD_CLI + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +USEMODULE += openthread +USEMODULE += openthread_contrib +USEMODULE += openthread_sock_udp +USEMODULE += openthread_sock +USEMODULE += core_mbox + + +ifneq (,$(filter samr21-xpro,$(BOARD))) + DRIVER := at86rf233 +endif +ifneq (,$(filter iotlab-m3 fox,$(BOARD))) + DRIVER := at86rf231 +endif + +ifneq (,$(filter at86rf2%,$(DRIVER))) + FEATURES_REQUIRED = periph_spi periph_gpio +endif + +USEMODULE += $(DRIVER) + +USEMODULE += random + +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps +USEMODULE += ipv6_addr + +#required for C++ compiling +CXXEXFLAGS += -fno-rtti +USEMODULE += cpp11-compat + +include $(RIOTBASE)/Makefile.include diff --git a/tests/openthread_sock_udp/README.md b/tests/openthread_sock_udp/README.md new file mode 100644 index 000000000000..6ac9293c3f34 --- /dev/null +++ b/tests/openthread_sock_udp/README.md @@ -0,0 +1 @@ +## To be written diff --git a/tests/openthread_sock_udp/main.c b/tests/openthread_sock_udp/main.c new file mode 100644 index 000000000000..9853b600703a --- /dev/null +++ b/tests/openthread_sock_udp/main.c @@ -0,0 +1,181 @@ +#include + +#include "net/ipv6/addr.h" +#include "net/sock/udp.h" +#include "openthread/ip6.h" +#include "openthread/thread.h" +#include "openthread/udp.h" +#include "ot.h" +#include "shell.h" +#include "shell_commands.h" + +static sock_udp_t sock; +static char _udp_buf[100]; + +static OT_JOB _set_panid(otInstance *ot_instance, void *context) +{ + uint16_t panid = *((uint16_t *) context); + + otLinkSetPanId(ot_instance, panid); +} + +static OT_JOB _get_panid(otInstance *ot_instance, void *context) +{ + *((uint16_t *) context) = otLinkGetPanId(ot_instance); + printf("PanID: %04x\n", *((uint16_t *) context)); +} + +static OT_JOB _thread_start(otInstance *ot_instance, void *context) +{ + printf("Starting OpenThread\n"); + otIp6SetEnabled(ot_instance, true); + otThreadSetEnabled(ot_instance, true); +} + +static OT_JOB _read_state(otInstance *ot_instance, void *context) +{ + uint8_t state = otThreadGetDeviceRole(ot_instance); + + switch (state) { + case kDeviceRoleOffline: + puts("offline"); + break; + case kDeviceRoleDisabled: + puts("disabled"); + break; + case kDeviceRoleDetached: + puts("detached"); + break; + case kDeviceRoleChild: + puts("child"); + break; + case kDeviceRoleRouter: + puts("router"); + break; + case kDeviceRoleLeader: + puts("leader"); + break; + } +} + +static OT_JOB _get_ip_addresses(otInstance *ot_instance, void *context) +{ + for (const otNetifAddress *addr = otIp6GetUnicastAddresses(ot_instance); addr; addr = addr->mNext) { + char addrstr[IPV6_ADDR_MAX_STR_LEN]; + printf("inet6 %s\n", ipv6_addr_to_str(addrstr, (ipv6_addr_t *) &addr->mAddress.mFields, sizeof(addrstr))); + } +} + +int _listen(int argc, char **argv) +{ + int bytes = sock_udp_recv(&sock, (char *)_udp_buf, sizeof(_udp_buf), SOCK_NO_TIMEOUT, NULL); + + for (int i = 0; i < bytes; i++) { + printf("%c", _udp_buf[i]); + } + puts("\n"); + return 0; +} + +int _udp(int argc, char **argv) +{ + if (argc < 2) { + printf("Usage: %s [send|server]\n", argv[0]); + puts(" "); + return 1; + } + else if (strcmp(argv[1], "server") == 0) { + if (argc == 2) { + printf("Usage: %s server \n", argv[0]); + return 1; + } + sock_udp_ep_t local = SOCK_IPV6_EP_ANY; + local.port = atoi(argv[2]); + if (sock_udp_create(&sock, &local, NULL, 0) < 0) { + puts("Error creating UDP sock"); + } + } + else if (strcmp(argv[1], "send") == 0) { + if (argc < 5) { + printf("Usage: %s send \n", argv[0]); + return 1; + } + sock_udp_ep_t remote = { .family = AF_INET6 }; + remote.port = atoi(argv[3]); + ipv6_addr_from_str((ipv6_addr_t *)&remote.addr.ipv6, argv[2]); + if (sock_udp_send(&sock, argv[4], strlen(argv[4]), &remote) < 0) { + puts("Error sending message"); + sock_udp_close(&sock); + return 1; + } + } + return 0; +} + +static void _usage(char *cmd) +{ + printf("Usage: %s \n", cmd); + puts(" List Thread IPv6 addresses"); + printf(" %s \n", cmd); + puts(" Get/Set PAN ID"); + printf(" %s thread start \n", cmd); + puts(" Start the thread network"); + printf(" %s thread state \n", cmd); + puts(" Asks for the state of a thread network."); +} + +int _ifconfig(int argc, char **argv) +{ + uint16_t panid; + + if (argc >= 2) { + if (strcmp(argv[1], "get") == 0 && argc >= 3) { + if (strcmp(argv[2], "panid") == 0) { + ot_exec_job(_get_panid, &panid); + } + } + else if (strcmp(argv[1], "set") == 0 && argc >= 4) { + if (strcmp(argv[2], "panid") == 0) { + panid = strtol(argv[3], NULL, 0); + ot_exec_job(_set_panid, &panid); + } + } + else if (strcmp(argv[1], "thread") == 0) { + if (strcmp(argv[2], "start") == 0) { + ot_exec_job(_thread_start, NULL); + } + else if (strcmp(argv[2], "state") == 0) { + uint8_t state; + ot_exec_job(_read_state, &state); + } + } + else { + _usage(argv[0]); + } + } + else { + ot_exec_job(_get_ip_addresses, NULL); + } + return 0; +} + +#if !defined(MODULE_OPENTHREAD_CLI) && !defined(MODULE_OPENTHREAD_NCP) +static const shell_command_t shell_commands[] = { + { "ifconfig", "Get or set panid", _ifconfig }, + { "udp", "Test udp", _udp }, + { "listen", "Listen in given server port", _listen }, + { NULL, NULL, NULL } +}; +#endif + +int main(void) +{ +#if defined(MODULE_OPENTHREAD_CLI) || defined(MODULE_OPENTHREAD_NCP) + openthread_uart_run(); +#else + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); +#endif + + return 0; +} diff --git a/tests/openthread_test_ftd/Makefile b/tests/openthread_test_ftd/Makefile new file mode 100644 index 000000000000..0834b8a57882 --- /dev/null +++ b/tests/openthread_test_ftd/Makefile @@ -0,0 +1,56 @@ +APPLICATION = openthread_test_ftd + +# If no BOARD is found in the environment, use this default: +BOARD ?= samr21-xpro + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: + +CFLAGS += -DDEVELHELP -Wall + +#Uncomment the following line for running OpenThread CLI +CFLAGS += -DMODULE_OPENTHREAD_CLI + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +# ftd: A Full Thread Device has router functionality compiled in +USEPKG += openthread +USEMODULE += openthread_contrib +USEMODULE += libmbedcrypto +USEMODULE += libopenthread-ftd +USEMODULE += libopenthread-cli-ftd +USEMODULE += xtimer + + +ifneq (,$(filter native,$(BOARD))) + USEMODULE += libopenthread-posix +endif + + +ifneq (,$(filter samr21-xpro,$(BOARD))) + DRIVER := at86rf233 +endif +ifneq (,$(filter iotlab-m3 fox,$(BOARD))) + DRIVER := at86rf231 +endif + +ifneq (,$(filter at86rf2%,$(DRIVER))) + FEATURES_REQUIRED = periph_spi periph_gpio +endif + +USEMODULE += $(DRIVER) + +USEMODULE += random +USEMODULE += ps +USEMODULE += ipv6_addr + +#required for C++ compiling +CXXEXFLAGS += -fno-rtti +USEMODULE += cpp11-compat + +include $(RIOTBASE)/Makefile.include diff --git a/tests/openthread_test_ftd/README.md b/tests/openthread_test_ftd/README.md new file mode 100644 index 000000000000..779e0c18876a --- /dev/null +++ b/tests/openthread_test_ftd/README.md @@ -0,0 +1,42 @@ +## OpenThread on RIOT + +This test demonstrates how to use NEST's OpenThread stack from [OpenThread](https://github.com/openthread/openthread) on +RIOT. The [Command Line Interface](https://github.com/openthread/openthread/blob/master/examples/apps/cli/README.md) of +OpenThread was ported. Please check the [full +documentation](https://github.com/openthread/openthread/blob/master/src/cli/README.md) of the CLI for usage information. + +This test compiles a Full Thread Device (FTD). A Full Thread Device has router functionality compiled in. + +## Quick usage + +To test OpenThread in RIOT, you can do the following: + +1. Flash nodes with `make BOARD= clean all flash` +2. Write `panid 0x1234`, `ifconfig up` then `thread start` on one node. +3. Check the state of the node with `state`. In the beggining should be `detached`, but after some seconds it should + become `leader` +4. Write `panid 0x1234`, `ifconfig up` then `thread start` on another node. +The second node should become `router` if there's a leader. +5. Get the mesh IP address of a node with `ipaddr`. +``` +ipaddr + fdde:ad00:beef::ff:fe00:8000 + fe80::ff:fe00:8000 + fdde:ad00:beef:0:946a:c722:a5d9:8481 + fe80::3984:f4eb:d182:5dae +``` +6. Ping from another node with `ping fdde:ad00:beef:0:946a:c722:a5d9:848`. +7. You can try IEEE802.15.4 scan with `scan` command +8. You can also check other commands with `help` + + +## OpenThread port on RIOT status + +OpenThread port on RIOT is still under development. In case of any bug, please report via GitHub issue. + + +## Known issues + +* When the network traffic is really high, sometimes some nodes crash silently. +* In some cases there are isolated leaders that are not able to recover to the main partition. These won't affect the + rest of the network, but this node becomes unreachable. diff --git a/tests/openthread_test_ftd/main.c b/tests/openthread_test_ftd/main.c new file mode 100644 index 000000000000..858ba6af625a --- /dev/null +++ b/tests/openthread_test_ftd/main.c @@ -0,0 +1,15 @@ +#include "stdio.h" +#include "ot.h" + +#include "shell.h" +#include "shell_commands.h" +#include "openthread/thread.h" +#include "openthread/ip6.h" +#include "net/ipv6/addr.h" + +int main(void) +{ + openthread_uart_run(); + + return 0; +} diff --git a/tests/openthread_test_mtd/Makefile b/tests/openthread_test_mtd/Makefile new file mode 100644 index 000000000000..95936f4a3e6c --- /dev/null +++ b/tests/openthread_test_mtd/Makefile @@ -0,0 +1,55 @@ +APPLICATION = openthread_test_mtd + +# If no BOARD is found in the environment, use this default: +BOARD ?= samr21-xpro + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: + +CFLAGS += -DDEVELHELP -Wall + +#Uncomment the following line for running OpenThread CLI +CFLAGS += -DMODULE_OPENTHREAD_CLI + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +# MTD: A Minimal Thread Device does not have router functionality compiled in. As a result, it is not necessary to configure the routerrole on an MTD. At the same time, an MTD may or may not be sleepy. +USEPKG += openthread +USEMODULE += openthread_contrib +USEMODULE += libmbedcrypto +USEMODULE += libopenthread-mtd +USEMODULE += libopenthread-cli-mtd +USEMODULE += xtimer + +ifneq (,$(filter native,$(BOARD))) + USEMODULE += libopenthread-posix +endif + + +ifneq (,$(filter samr21-xpro,$(BOARD))) + DRIVER := at86rf233 +endif +ifneq (,$(filter iotlab-m3 fox,$(BOARD))) + DRIVER := at86rf231 +endif + +ifneq (,$(filter at86rf2%,$(DRIVER))) + FEATURES_REQUIRED = periph_spi periph_gpio +endif + +USEMODULE += $(DRIVER) + +USEMODULE += random +USEMODULE += ps +USEMODULE += ipv6_addr + +#required for C++ compiling +CXXEXFLAGS += -fno-rtti +USEMODULE += cpp11-compat + +include $(RIOTBASE)/Makefile.include diff --git a/tests/openthread_test_mtd/README.md b/tests/openthread_test_mtd/README.md new file mode 100644 index 000000000000..f0831034d203 --- /dev/null +++ b/tests/openthread_test_mtd/README.md @@ -0,0 +1,43 @@ +## OpenThread on RIOT + +This test demonstrates how to use NEST's OpenThread stack from [OpenThread](https://github.com/openthread/openthread) on +RIOT. The [Command Line Interface](https://github.com/openthread/openthread/blob/master/examples/apps/cli/README.md) of +OpenThread was ported. Please check the [full +documentation](https://github.com/openthread/openthread/blob/master/src/cli/README.md) of the CLI for usage information. + +This test compiles a Minimal Thread Device (MTD). MTD does not have router functionality compiled in. +As a result, it is not necessary to configure the routerrole on an MTD. +At the same time, an MTD may or may not be sleepy. + +## Quick usage + +To test OpenThread in RIOT, you can do the following: + +1. Flash nodes with `make BOARD= clean all flash` +2. Write `panid 0x1234`, `ifconfig up` then `thread start` on one node. +3. Check the state of the node with `state`. In the beggining should be `detached`, but after some seconds it should + become `leader` +4. Write `panid 0x1234`, `ifconfig up` then `thread start` on another node. +The second node should become `router` if there's a leader. +5. Get the mesh IP address of a node with `ipaddr`. +``` +ipaddr + fdde:ad00:beef::ff:fe00:8000 + fe80::ff:fe00:8000 + fdde:ad00:beef:0:946a:c722:a5d9:8481 + fe80::3984:f4eb:d182:5dae +``` +6. Ping from another node with `ping fdde:ad00:beef:0:946a:c722:a5d9:848`. +7. You can try IEEE802.15.4 scan with `scan` command +8. You can also check other commands with `help` + +## OpenThread port on RIOT status + +OpenThread port on RIOT is still under development. In case of any bug, please report via GitHub issue. + + +## Known issues + +* When the network traffic is really high, sometimes some nodes crash silently. +* In some cases there are isolated leaders that are not able to recover to the main partition. These won't affect the + rest of the network, but this node becomes unreachable. diff --git a/tests/openthread_test_mtd/main.c b/tests/openthread_test_mtd/main.c new file mode 100644 index 000000000000..b55990f56c9a --- /dev/null +++ b/tests/openthread_test_mtd/main.c @@ -0,0 +1,14 @@ +#include "stdio.h" +#include "ot.h" + +#include "shell.h" +#include "shell_commands.h" +#include "openthread/thread.h" +#include "openthread/ip6.h" +#include "net/ipv6/addr.h" + +int main(void) +{ + openthread_uart_run(); + return 0; +} diff --git a/tests/openthread_test_ncp/Makefile b/tests/openthread_test_ncp/Makefile new file mode 100644 index 000000000000..a0c600ee1e11 --- /dev/null +++ b/tests/openthread_test_ncp/Makefile @@ -0,0 +1,57 @@ +APPLICATION = openthread_test_ncp + +# If no BOARD is found in the environment, use this default: +BOARD ?= samr21-xpro + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: + +CFLAGS += -DDEVELHELP -Wall + +#Uncomment the following line for running OpenThread NCP +CFLAGS += -DMODULE_OPENTHREAD_NCP + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +# NCP: A Network Co-Processor is used with wpantund software. wpantund is a user-space network +# interface driver/daemon that provides a native IPv6 network interface to a low-power wireless Network Co-Processor +# FTD (see openthread_test_ftd): NCP needs Full Thread Device to act as a router in the network +USEPKG += openthread +USEMODULE += openthread_contrib +USEMODULE += libmbedcrypto +USEMODULE += libopenthread-ftd +USEMODULE += libopenthread-ncp-ftd +USEMODULE += xtimer + +ifneq (,$(filter native,$(BOARD))) + USEMODULE += libopenthread-posix +endif + + +ifneq (,$(filter samr21-xpro,$(BOARD))) + DRIVER := at86rf233 +endif +ifneq (,$(filter iotlab-m3 fox,$(BOARD))) + DRIVER := at86rf231 +endif + +ifneq (,$(filter at86rf2%,$(DRIVER))) + FEATURES_REQUIRED = periph_spi periph_gpio +endif + +USEMODULE += $(DRIVER) + +USEMODULE += random +USEMODULE += ps +USEMODULE += ipv6_addr + +#required for C++ compiling +CXXEXFLAGS += -fno-rtti +USEMODULE += cpp11-compat + +include $(RIOTBASE)/Makefile.include diff --git a/tests/openthread_test_ncp/README.md b/tests/openthread_test_ncp/README.md new file mode 100644 index 000000000000..83c62404e32b --- /dev/null +++ b/tests/openthread_test_ncp/README.md @@ -0,0 +1,50 @@ +## OpenThread on RIOT + +This test demonstrates how to use NEST's OpenThread stack from [OpenThread](https://github.com/openthread/openthread) on +RIOT. The [Command Line Interface](https://github.com/openthread/openthread/blob/master/examples/apps/cli/README.md) of +OpenThread was ported. Please check the [full +documentation](https://github.com/openthread/openthread/blob/master/src/cli/README.md) of the CLI for usage information. + +This test show how to use a Network Co-Processor (NCP). A Network Co-Processor is used with wpantund software. +Wpantund is a user-space network interface driver/daemon that provides a native IPv6 network interface to a low-power +wireless Network Co-Processor. NCP and wpantund communicates by UART (UART(0) is used here) + +## Quick usage + +To test OpenThread NCP in RIOT, you can do the following: + +1. Flash nodes with `make BOARD= clean all flash` +2. Install wpantund: On Ubuntu 16.04, run +``` +sudo apt-get -y install build-essential subversion libncurses5-dev libssl-dev zlib1g-dev gawk gcc-multilib flex git-core gettext gcc binutils bzip2 python perl make unzip libz-dev tftp git shtool autogen automake libtool autotools-dev libdbus-1-dev +git clone https://github.com/openthread/wpantund.git +cd wpantund +./bootstrap.sh +./configure +make +sudo make install +``` + +3) Start wpantund on your host. Be sure to pass flags appropriate to connect wpantund to your NCP connection type. +You can also name the network interface at this time or let wpantund automatically assign a name (wpan0): + +`sudo /usr/local/sbin/wpantund -s /dev/ttyACM0 -I wpan0 -o SyslogMask all` + +4) Start wpanctl to access the command line management interface for controlling Thread features on your device. +This is similar to the Thread cli interface conceptually but with slightly different command syntax: + +`sudo /usr/local/bin/wpanctl -I wpan0` + +5) Confirm your network interface has started and is operational by running standard IP commands such `ifconfig` and `ping6`. + + +## OpenThread port on RIOT status + +OpenThread port on RIOT is still under development. In case of any bug, please report via GitHub issue. + + +## Known issues + +* When the network traffic is really high, sometimes some nodes crash silently. +* In some cases there are isolated leaders that are not able to recover to the main partition. These won't affect the + rest of the network, but this node becomes unreachable. diff --git a/tests/openthread_test_ncp/main.c b/tests/openthread_test_ncp/main.c new file mode 100644 index 000000000000..b55990f56c9a --- /dev/null +++ b/tests/openthread_test_ncp/main.c @@ -0,0 +1,14 @@ +#include "stdio.h" +#include "ot.h" + +#include "shell.h" +#include "shell_commands.h" +#include "openthread/thread.h" +#include "openthread/ip6.h" +#include "net/ipv6/addr.h" + +int main(void) +{ + openthread_uart_run(); + return 0; +}