diff --git a/examples/6tisch/etsi-plugtest-2017/Makefile b/examples/6tisch/etsi-plugtest-2017/Makefile new file mode 100644 index 000000000..7a9f909c0 --- /dev/null +++ b/examples/6tisch/etsi-plugtest-2017/Makefile @@ -0,0 +1,21 @@ +CONTIKI_PROJECT = node +all: $(CONTIKI_PROJECT) + +MAKE_WITH_SECURITY ?= 0 # force Security from command line +ifeq ($(MAKE_WITH_SECURITY),1) +CFLAGS += -DWITH_SECURITY=1 +endif + +MODULES += os/services/shell +MODULES += os/net/mac/tsch os/net/mac/tsch/sixtop +PROJECT_SOURCEFILES += sf-plugtest.c +CFLAGS += -DPROJECT_CONF_H=\"project-conf.h\" -Wno-nonnull-compare + +ifeq ($(BOARD),launchpad/cc2650) +# Enable DAP and TAP by default for ETSI Plugtest +CFLAGS += -DSET_CCFG_CCFG_TAP_DAP_0_CPU_DAP_ENABLE=0xC5 +CFLAGS += -DSET_CCFG_CCFG_TAP_DAP_0_TEST_TAP_ENABLE=0xC5 +endif + +CONTIKI =../../.. +include $(CONTIKI)/Makefile.include diff --git a/examples/6tisch/etsi-plugtest-2017/README.md b/examples/6tisch/etsi-plugtest-2017/README.md new file mode 100644 index 000000000..ab296bff8 --- /dev/null +++ b/examples/6tisch/etsi-plugtest-2017/README.md @@ -0,0 +1,57 @@ +# The 1st F-Interop 6TiSCH Interoperability Event + +## Overview + +This project was used to build firmwares for [the 1st F-Interop 6TiSCH +Interoperability +Event](http://www.etsi.org/news-events/events/1197-6tisch-interop-prague-2017), +which worked well in all the tests except for "secjoin". + +## Authors + +* Simon Duquennoy +* Yasuyuki Tanaka + +## Supported Hardwares + +The following hardwares were used in the event: + +* Zolertia Remote (TARGET=`zoul`, BOARD=`remote`) +* JN156x (TARGET=`jn516`) +* CC2650 LaunchPad (TARGET=`srf06-cc26xx`, BOARD=`launchpad/cc2650`) + +## Usage + +Access to your target board through serial connection. You'll get available +commands by hit `help` on the shell prompt. + +```shell +> help +Available commands: +'> help': Shows this help +'> ip-addr': Shows all IPv6 addresses +'> ip-nbr': Shows all IPv6 neighbors +'> log module level': Sets log level (0--4) for a given module (or "all"). For module "mac", level 4 also enables per-slot logg'> ping addr': Pings the IPv6 address 'addr' +'> rpl-set-root 0/1 [prefix]': Sets node as root (on) or not (off). A /64 prefix can be optionally specified. +'> rpl-status': Shows a summary of the current RPL state +'> rpl-global-repair': Triggers a RPL global repair +'> rpl-local-repair': Triggers a RPL local repair +'> routes': Shows the route entries +'> tsch-schedule': Shows the current TSCH schedule +'> tsch-status': Shows a summary of the current TSCH state +'> reboot': Reboot the board by watchdog_reboot() +'> 6top help': Shows 6top command usage +``` + +Your board runs as a 6TiSCH node by default. Its role can be changed to DAG root +by `rpl-set-root 1`. + +You can see how it works with +[test-with-cooja-mote.csc](test-with-cooja-mote.csc). + +## Configuration + +Edit [project-confi.h](./project-conf.h) if necessary. + +* `UIP_CONF_IPV6_CHECKS`: set 0 if you want to disable checksum validation +* `SIXP_CONF_WITH_PAYLOAD_TERMINATION_IE`: set 1 if you want to append Paload Termination IE in 6P frames diff --git a/examples/6tisch/etsi-plugtest-2017/node.c b/examples/6tisch/etsi-plugtest-2017/node.c new file mode 100644 index 000000000..578727d1f --- /dev/null +++ b/examples/6tisch/etsi-plugtest-2017/node.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2015, SICS Swedish ICT. + * 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. + * + */ +/** + * \file + * A RPL+TSCH node able to act as either a simple node (6ln), + * DAG Root (6dr) or DAG Root with security (6dr-sec) + * Press use button at startup to configure. + * + * \author Simon Duquennoy + */ + +#include "contiki.h" +#include "node-id.h" +#include "rpl.h" +#include "rpl-dag-root.h" +#include "sys/log.h" +#include "net/ipv6/uip-ds6-route.h" +#include "net/mac/tsch/tsch.h" +#include "net/mac/tsch/tsch-log.h" +#if UIP_CONF_IPV6_RPL_LITE == 0 +#include "rpl-private.h" +#endif /* UIP_CONF_IPV6_RPL_LITE == 0 */ +#include "serial-shell.h" +#include "sf-plugtest.h" +#if CONTIKI_TARGET_SRF06_CC26XX +#include +#include +#endif + +/*---------------------------------------------------------------------------*/ +PROCESS(node_process, "RPL Node"); +AUTOSTART_PROCESSES(&node_process); + +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(node_process, ev, data) +{ + int is_coordinator; + + PROCESS_BEGIN(); + + is_coordinator = 0; + +#if CONTIKI_TARGET_SRF06_CC26XX + cc26xx_uart_set_input(serial_line_input_byte); +#endif + + serial_shell_init(); + log_set_level("all", LOG_LEVEL_WARN); + log_set_level("6top", LOG_LEVEL_INFO); + tsch_log_stop(); + +#if CONTIKI_TARGET_COOJA + is_coordinator = (node_id == 1); +#endif + + if(is_coordinator) { + rpl_dag_root_init_dag_immediately(); + } + NETSTACK_MAC.on(); + + sixtop_add_sf(&sf_plugtest); + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/6tisch/etsi-plugtest-2017/project-conf.h b/examples/6tisch/etsi-plugtest-2017/project-conf.h new file mode 100644 index 000000000..aaf3bb851 --- /dev/null +++ b/examples/6tisch/etsi-plugtest-2017/project-conf.h @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2015, SICS Swedish ICT. + * 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. + * + */ + +/** + * \author Simon Duquennoy + */ + +#ifndef __PROJECT_CONF_H__ +#define __PROJECT_CONF_H__ + +/* Set to use the Contiki shell */ +#define WITH_SHELL 1 + +/* Set to enable TSCH security */ +#ifndef WITH_SECURITY +#define WITH_SECURITY 0 +#endif /* WITH_SECURITY */ + +/*******************************************************/ +/********************* Enable TSCH *********************/ +/*******************************************************/ + +/* Netstack layers */ +#undef NETSTACK_CONF_MAC +#define NETSTACK_CONF_MAC tschmac_driver + +/* TSCH and RPL callbacks */ +#define RPL_CALLBACK_PARENT_SWITCH tsch_rpl_callback_parent_switch +#define RPL_CALLBACK_NEW_DIO_INTERVAL tsch_rpl_callback_new_dio_interval +#define TSCH_CALLBACK_KA_SENT tsch_rpl_callback_ka_sent +#define TSCH_CALLBACK_JOINING_NETWORK tsch_rpl_callback_joining_network +#define TSCH_CALLBACK_LEAVING_NETWORK tsch_rpl_callback_leaving_network + +/*******************************************************/ +/******************* Configure TSCH ********************/ +/*******************************************************/ + +/* IEEE802.15.4 PANID */ +#undef IEEE802154_CONF_PANID +#define IEEE802154_CONF_PANID 0x81a5 + +/* Do not start TSCH at init, wait for NETSTACK_MAC.on() */ +#undef TSCH_CONF_AUTOSTART +#define TSCH_CONF_AUTOSTART 0 + +/* 6TiSCH minimal schedule length. + * Larger values result in less frequent active slots: reduces capacity and saves energy. */ +#undef TSCH_SCHEDULE_CONF_DEFAULT_LENGTH +#define TSCH_SCHEDULE_CONF_DEFAULT_LENGTH 11 + +#if WITH_SECURITY +/* Enable security */ +#undef LLSEC802154_CONF_ENABLED +#define LLSEC802154_CONF_ENABLED 1 +#define LLSEC802154_CONF_USES_EXPLICIT_KEYS 0 +#define LLSEC802154_CONF_USES_FRAME_COUNTER 0 +#define TSCH_SECURITY_CONF_K1 { 0x11, 0x11, 0x11, 0x11, \ + 0x11, 0x11, 0x11, 0x11, \ + 0x11, 0x11, 0x11, 0x11, \ + 0x11, 0x11, 0x11, 0x11 } +#define TSCH_SECURITY_CONF_K2 { 0x22, 0x22, 0x22, 0x22, \ + 0x22, 0x22, 0x22, 0x22, \ + 0x22, 0x22, 0x22, 0x22, \ + 0x22, 0x22, 0x22, 0x22 } +#endif /* WITH_SECURITY */ + +#define TSCH_CONF_MAC_MAX_FRAME_RETRIES 3 + +#undef TSCH_CONF_DEFAULT_HOPPING_SEQUENCE +#define TSCH_CONF_DEFAULT_HOPPING_SEQUENCE (uint8_t[]){ 20 } +//#define TSCH_CONF_DEFAULT_HOPPING_SEQUENCE TSCH_HOPPING_SEQUENCE_16_16 + +#undef TSCH_PACKET_CONF_EACK_WITH_SRC_ADDR +#define TSCH_PACKET_CONF_EACK_WITH_SRC_ADDR 1 + +#undef TSCH_PACKET_CONF_EB_WITH_SLOTFRAME_AND_LINK +#define TSCH_PACKET_CONF_EB_WITH_SLOTFRAME_AND_LINK 1 + +#undef TSCH_CONF_EB_PERIOD +#define TSCH_CONF_EB_PERIOD (1 * CLOCK_SECOND) +#undef TSCH_CONF_MAX_EB_PERIOD +#define TSCH_CONF_MAX_EB_PERIOD (1 * CLOCK_SECOND) + +/*******************************************************/ +/******************* Configure 6top ********************/ +/*******************************************************/ + +#define TSCH_CONF_WITH_SIXTOP 1 +#define SF_PLUGTEST_SFID 0x00 +#define SF_PLUGTEST_TIMEOUT CLOCK_SECOND +#define SIXP_CONF_WITH_PAYLOAD_TERMINATION_IE 0 + +/*******************************************************/ +/************* Platform dependent configuration ********/ +/*******************************************************/ + +#if CONTIKI_TARGET_CC2538DK || CONTIKI_TARGET_ZOUL || \ + CONTIKI_TARGET_OPENMOTE_CC2538 +#define TSCH_CONF_HW_FRAME_FILTERING 0 +#endif /* CONTIKI_TARGET_CC2538DK || CONTIKI_TARGET_ZOUL \ + || CONTIKI_TARGET_OPENMOTE_CC2538 */ + +/* Needed for CC2538 platforms only */ +/* For TSCH we have to use the more accurate crystal oscillator + * by default the RC oscillator is activated */ +#undef SYS_CTRL_CONF_OSC32K_USE_XTAL +#define SYS_CTRL_CONF_OSC32K_USE_XTAL 1 + +#define USB_SERIAL_CONF_ENABLE 1 +/* USB serial takes space, free more space elsewhere */ +#undef SICSLOWPAN_CONF_FRAG +#define SICSLOWPAN_CONF_FRAG 0 +#undef UIP_CONF_BUFFER_SIZE +#define UIP_CONF_BUFFER_SIZE 160 + +#if CONTIKI_TARGET_SRF06_CC26XX +#define CC2650_FAST_RADIO_STARTUP 1 +#endif /* CONTIK_TARGET_SRF06_CC26XX */ + +#if CONTIKI_TARGET_COOJA +#define COOJA_CONF_SIMULATE_TURNAROUND 0 +#endif /* CONTIKI_TARGET_COOJA */ + +/* Needed for cc2420 platforms only */ +/* Disable DCO calibration (uses timerB) */ +#undef DCOSYNCH_CONF_ENABLED +#define DCOSYNCH_CONF_ENABLED 0 +/* Enable SFD timestamps (uses timerB) */ +#undef CC2420_CONF_SFD_TIMESTAMPS +#define CC2420_CONF_SFD_TIMESTAMPS 1 + +/*******************************************************/ +/******************* Configure 6LoWPAN/IPv6 ************/ +/*******************************************************/ + +#undef UIP_CONF_IPV6_CHECKS +#define UIP_CONF_IPV6_CHECKS 1 + +#undef SICSLOWPAN_CONF_COMPRESSION +#define SICSLOWPAN_CONF_COMPRESSION SICSLOWPAN_COMPRESSION_6LORH + +/*******************************************************/ +/********* Enable RPL non-storing mode *****************/ +/*******************************************************/ + +#undef RPL_CONF_MOP +#define RPL_CONF_MOP RPL_MOP_NON_STORING /* Mode of operation*/ + +/*******************************************************/ +/************* Other system configuration **************/ +/*******************************************************/ + +/* Logging */ +#define LOG_CONF_LEVEL_RPL LOG_LEVEL_INFO +#define LOG_CONF_LEVEL_TCPIP LOG_LEVEL_WARN +#define LOG_CONF_LEVEL_IPV6 LOG_LEVEL_WARN +#define LOG_CONF_LEVEL_6LOWPAN LOG_LEVEL_WARN +#define LOG_CONF_LEVEL_MAC LOG_LEVEL_INFO +#define LOG_CONF_LEVEL_FRAMER LOG_LEVEL_DBG +#define LOG_CONF_LEVEL_6TOP LOG_LEVEL_DBG +#define TSCH_LOG_CONF_PER_SLOT 1 + +#undef TCPIP_CONF_ANNOTATE_TRANSMISSIONS +#define TCPIP_CONF_ANNOTATE_TRANSMISSIONS 0 + +#endif /* __PROJECT_CONF_H__ */ diff --git a/examples/6tisch/etsi-plugtest-2017/sf-plugtest.c b/examples/6tisch/etsi-plugtest-2017/sf-plugtest.c new file mode 100644 index 000000000..8c6628f3f --- /dev/null +++ b/examples/6tisch/etsi-plugtest-2017/sf-plugtest.c @@ -0,0 +1,1009 @@ +/* + * Copyright (c) 2017, Toshiba Corporation + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#define LOG_MODULE "6top" +#define LOG_LEVEL LOG_LEVEL_6TOP + +#include +#include +#include +#include + +#define SIXP_PKT_BUFLEN 128 +static uint8_t sixp_pkt_buf[SIXP_PKT_BUFLEN]; + +static shell_output_func *shell_output = NULL; +/* this variable is used for LIST Request */ +static uint16_t cell_list_offset = 0; /* XXX: should be had in sixp_nbr */ + +typedef struct { + uint8_t slot_offset[2]; + uint8_t channel_offset[2]; +} sf_plugtest_cell_t; + +typedef struct { + sixp_pkt_cmd_t cmd; + linkaddr_t peer_addr; + uint16_t slot_offset; + uint16_t channel_offset; +} subcmd_args_t; + +typedef void subcmd(shell_output_func output, subcmd_args_t *args); + +typedef void (req_handler_t)(const linkaddr_t *peer_addr, + const uint8_t *body, size_t body_len); +typedef void (res_handler_t)(const linkaddr_t *peer_addr, sixp_pkt_rc_t rc, + const uint8_t *body, size_t body_len); + +static void advance_gen(const linkaddr_t *peer_addr); +static void add_res_sent_callback(void *arg, uint16_t arg_len, + const linkaddr_t *dest_addr, + sixp_output_status_t status); +static void delete_res_sent_callback(void *arg, uint16_t arg_len, + const linkaddr_t *dest_addr, + sixp_output_status_t status); + +static void send_list_req(const linkaddr_t *peer_addr); + +static int add_cell(const linkaddr_t *peer_addr, const sf_plugtest_cell_t *cell, + uint8_t link_options); +static int delete_cell(const linkaddr_t *peer_addr, + const sf_plugtest_cell_t *cell); +static int reserve_cell(const linkaddr_t *peer_addr, + const sf_plugtest_cell_t *cell); +static void clear_cells(const linkaddr_t *peer_addr, + struct tsch_slotframe *slotframe); + +static void help(shell_output_func output, subcmd_args_t *args); +static void add_delete(shell_output_func output, subcmd_args_t *args); +static void count(shell_output_func output, subcmd_args_t *args); +static void list(shell_output_func output, subcmd_args_t *args); +static void clear(shell_output_func output, subcmd_args_t *args); + +static int parse_args(shell_output_func output, + char *args, const char **subcmd, + subcmd_args_t *subcmd_args); + +static const char subcmd_help[] = "help"; +static void shell_subcmd(shell_output_func output, char *args); + +static req_handler_t add_req_handler; +static res_handler_t add_res_handler; +static req_handler_t delete_req_handler; +static res_handler_t delete_res_handler; +static req_handler_t count_req_handler; +static res_handler_t count_res_handler; +static req_handler_t list_req_handler; +static res_handler_t list_res_handler; +static req_handler_t clear_req_handler; +static res_handler_t clear_res_handler; + +static const struct { + char *name; + subcmd *func; + sixp_pkt_cmd_t cmd; +} subcmds[] = { + { "help", help, SIXP_PKT_CMD_UNAVAILABLE }, + { "add", add_delete, SIXP_PKT_CMD_ADD }, + { "delete", add_delete, SIXP_PKT_CMD_DELETE }, + { "count", count, SIXP_PKT_CMD_COUNT }, + { "list", list, SIXP_PKT_CMD_LIST }, + { "clear", clear, SIXP_PKT_CMD_CLEAR }, + { NULL, NULL, SIXP_PKT_CMD_UNAVAILABLE } +}; + +static const struct { + sixp_pkt_cmd_t cmd; + req_handler_t *req; + res_handler_t *res; +} handlers[] = { + { SIXP_PKT_CMD_ADD, add_req_handler, add_res_handler }, + { SIXP_PKT_CMD_DELETE, delete_req_handler, delete_res_handler }, + { SIXP_PKT_CMD_COUNT, count_req_handler, count_res_handler }, + { SIXP_PKT_CMD_LIST, list_req_handler, list_res_handler }, + { SIXP_PKT_CMD_CLEAR, clear_req_handler, clear_res_handler }, + { SIXP_PKT_CMD_UNAVAILABLE, NULL, NULL } +}; + +static void +advance_gen(const linkaddr_t *peer_addr) +{ + sixp_nbr_t *nbr; + assert((nbr = sixp_nbr_find(peer_addr)) != NULL); + if(nbr == NULL) { + return; + } + assert(sixp_nbr_advance_gen(nbr) == 0); +} + +static void +add_res_sent_callback(void *arg, uint16_t arg_len, + const linkaddr_t *dest_addr, + sixp_output_status_t status) +{ + if(arg_len != sizeof(sf_plugtest_cell_t) || + status == SIXP_OUTPUT_STATUS_FAILURE || + dest_addr == NULL) { + LOG_ERR("error in sending a response\n"); + } else { + add_cell(dest_addr, (sf_plugtest_cell_t *)arg, LINK_OPTION_RX); + } + advance_gen(dest_addr); +} + +static void +delete_res_sent_callback(void *arg, uint16_t arg_len, + const linkaddr_t *dest_addr, + sixp_output_status_t status) +{ + if(arg_len != sizeof(sf_plugtest_cell_t) || + status == SIXP_OUTPUT_STATUS_FAILURE || + dest_addr == NULL) { + LOG_ERR("error in sending a response\n"); + } else { + delete_cell(dest_addr, (sf_plugtest_cell_t *)arg); + advance_gen(dest_addr); + } +} + +static void +send_list_req(const linkaddr_t *peer_addr) +{ + const sixp_pkt_max_num_cells_t SF_PLUGTEST_MAC_NUM_CELLS = 1; + memset(&sixp_pkt_buf, 0, sizeof(sixp_pkt_buf)); + assert(sixp_pkt_set_cell_options(SIXP_PKT_TYPE_REQUEST, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_CMD_LIST, + SIXP_PKT_CELL_OPTION_TX, + sixp_pkt_buf, sizeof(sixp_pkt_buf)) == 0); + + assert(sixp_pkt_set_offset(SIXP_PKT_TYPE_REQUEST, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_CMD_LIST, + (sixp_pkt_offset_t)cell_list_offset, + sixp_pkt_buf, sizeof(sixp_pkt_buf)) == 0); + + assert(sixp_pkt_set_max_num_cells(SIXP_PKT_TYPE_REQUEST, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_CMD_LIST, + SF_PLUGTEST_MAC_NUM_CELLS, + sixp_pkt_buf, sizeof(sixp_pkt_buf)) == 0); + + assert(sixp_output(SIXP_PKT_TYPE_REQUEST, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_CMD_LIST, + SF_PLUGTEST_SFID, sixp_pkt_buf, + sizeof(sixp_pkt_metadata_t) + + sizeof(sixp_pkt_cell_options_t) + + sizeof(sixp_pkt_reserved_t) + + sizeof(sixp_pkt_offset_t) + + sizeof(sixp_pkt_max_num_cells_t), + peer_addr, + NULL, NULL, 0) == 0); +} + +static int +add_cell(const linkaddr_t *peer_addr, const sf_plugtest_cell_t *cell, + uint8_t link_options) +{ + struct tsch_slotframe *slotframe; + uint16_t timeslot; + uint16_t channel_offset; + + assert(peer_addr != NULL && cell != NULL); + if(peer_addr == NULL || cell == NULL) { + return -1; + } + + timeslot = cell->slot_offset[0] + (cell->slot_offset[1] << 8); + channel_offset = cell->channel_offset[0] + (cell->channel_offset[1] << 8); + + if((slotframe = tsch_schedule_get_slotframe_by_handle(0)) == NULL || + tsch_schedule_add_link(slotframe, link_options, LINK_TYPE_NORMAL, + peer_addr, timeslot, channel_offset) == NULL) { + LOG_ERR("cannot add a cell\n"); + return -1; + } + LOG_INFO("Succeeded to add a cell [slot:%u]\n", timeslot); + + return 0; +} + +static int +delete_cell(const linkaddr_t *peer_addr, const sf_plugtest_cell_t *cell) +{ + struct tsch_slotframe *slotframe; + uint16_t timeslot; + + assert(peer_addr != NULL && cell != NULL); + if(peer_addr == NULL || cell == NULL) { + return -1; + } + + timeslot = cell->slot_offset[0] + (cell->slot_offset[1] << 8); + + if((slotframe = tsch_schedule_get_slotframe_by_handle(0)) == NULL || + tsch_schedule_remove_link_by_timeslot(slotframe, timeslot) == 0) { + LOG_ERR("cannot delete a cell\n"); + return -1; + } + LOG_INFO("Succeeded to delete a cell [slot:%u]\n", timeslot); + + return 0; +} + +static int +reserve_cell(const linkaddr_t *peer_addr, const sf_plugtest_cell_t *cell) +{ + if(add_cell(peer_addr, cell, 0) < 0 || + delete_cell(peer_addr, cell) < 0) { + /* fail to reserve the cell */ + return -1; + } + return 0; +} + +static void +clear_cells(const linkaddr_t *peer_addr, struct tsch_slotframe *slotframe) +{ + struct tsch_link *cell, *next_cell; + + assert(peer_addr != NULL); + if(peer_addr == NULL) { + return; + } + for(cell = (struct tsch_link *)list_head(slotframe->links_list); + cell != NULL; cell = next_cell) { + next_cell = (struct tsch_link *)list_item_next(cell); + if(memcmp(&cell->addr, peer_addr, sizeof(linkaddr_t)) == 0) { + assert(tsch_schedule_remove_link(slotframe, cell) == 1); + } + } +} + +static void +add_req_handler(const linkaddr_t *peer_addr, + const uint8_t *body, size_t body_len) +{ + sixp_pkt_cell_options_t cell_options; + const uint8_t *cell; + sixp_pkt_offset_t cell_list_len; + static sf_plugtest_cell_t pending_cell; + uint16_t timeslot; + struct tsch_slotframe *slotframe; + + + assert(peer_addr != NULL && body != NULL); + if(body_len != (sizeof(sixp_pkt_metadata_t) + + sizeof(sixp_pkt_cell_options_t) + + sizeof(sixp_pkt_num_cells_t) + + sizeof(sf_plugtest_cell_t))) { + LOG_ERR("invalid Add Request length: %u\n", (unsigned int)body_len); + } + assert( + sixp_pkt_get_cell_options(SIXP_PKT_TYPE_REQUEST, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_CMD_ADD, + &cell_options, + body, body_len) == 0); + if(cell_options != SIXP_PKT_CELL_OPTION_TX && + cell_options != (SIXP_PKT_CELL_OPTION_TX | SIXP_PKT_CELL_OPTION_SHARED)) { + LOG_ERR("invalid Cell Options: %u\n", cell_options); + } + assert( + sixp_pkt_get_cell_list(SIXP_PKT_TYPE_REQUEST, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_CMD_ADD, + &cell, &cell_list_len, body, body_len) == 0); + assert(cell_list_len == sizeof(sf_plugtest_cell_t)); + memcpy(&pending_cell, cell, sizeof(pending_cell)); + timeslot = pending_cell.slot_offset[0] + (pending_cell.slot_offset[1] << 8); + + if((slotframe = tsch_schedule_get_slotframe_by_handle(0)) == NULL || + tsch_schedule_get_link_by_timeslot(slotframe, timeslot) != NULL || + reserve_cell(peer_addr, &pending_cell) < 0) { + LOG_ERR("Failed to add a cell [slot:%u]\n", timeslot); + sixp_output(SIXP_PKT_TYPE_RESPONSE, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_RC_NORES, + SF_PLUGTEST_SFID, NULL, 0, peer_addr, + NULL, NULL, 0); + } else { + sixp_output(SIXP_PKT_TYPE_RESPONSE, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_RC_SUCCESS, + SF_PLUGTEST_SFID, cell, cell_list_len, peer_addr, + add_res_sent_callback, &pending_cell, sizeof(pending_cell)); + } +} + +static void +add_res_handler(const linkaddr_t *peer_addr, sixp_pkt_rc_t rc, + const uint8_t *body, size_t body_len) +{ + const uint8_t *cell; + sixp_pkt_offset_t cell_list_len; + uint16_t timeslot; + struct tsch_slotframe *slotframe; + + if(body_len != 4) { + LOG_ERR("invalid Add Response length: %u\n", (unsigned int)body_len); + return; + } + + assert(sixp_pkt_get_cell_list(SIXP_PKT_TYPE_RESPONSE, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_RC_SUCCESS, + &cell, &cell_list_len, body, body_len) == 0); + timeslot = cell[0] + (cell[1] << 8); + + if(rc != SIXP_PKT_RC_SUCCESS) { + LOG_ERR("received return code of %u\n", rc); + return; + } + + if((slotframe = tsch_schedule_get_slotframe_by_handle(0)) == NULL || + tsch_schedule_get_link_by_timeslot(slotframe, timeslot) != NULL || + add_cell(peer_addr, (sf_plugtest_cell_t *)cell, LINK_OPTION_TX) < 0) { + LOG_ERR("Failed to add a cell [slot:%u]\n", timeslot); + } + advance_gen(peer_addr); +} + +static void +delete_req_handler(const linkaddr_t *peer_addr, + const uint8_t *body, size_t body_len) +{ + sixp_pkt_cell_options_t cell_options; + const uint8_t *cell; + sixp_pkt_offset_t cell_list_len; + static sf_plugtest_cell_t pending_cell; + uint16_t timeslot; + struct tsch_slotframe *slotframe; + struct tsch_link *link; + + + assert(peer_addr != NULL && body != NULL); + if(body_len != (sizeof(sixp_pkt_metadata_t) + + sizeof(sixp_pkt_cell_options_t) + + sizeof(sixp_pkt_num_cells_t) + + sizeof(sf_plugtest_cell_t))) { + LOG_ERR("invalid Delete Request length: %u\n", (unsigned int)body_len); + } + assert( + sixp_pkt_get_cell_options(SIXP_PKT_TYPE_REQUEST, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_CMD_DELETE, + &cell_options, + body, body_len) == 0); + if(cell_options != SIXP_PKT_CELL_OPTION_TX && + cell_options != (SIXP_PKT_CELL_OPTION_TX | SIXP_PKT_CELL_OPTION_SHARED)) { + LOG_ERR("invalid Cell Options: %u\n", cell_options); + } + assert( + sixp_pkt_get_cell_list(SIXP_PKT_TYPE_REQUEST, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_CMD_DELETE, + &cell, &cell_list_len, body, body_len) == 0); + assert(cell_list_len == sizeof(sf_plugtest_cell_t)); + memcpy(&pending_cell, cell, sizeof(pending_cell)); + timeslot = pending_cell.slot_offset[0] + (pending_cell.slot_offset[1] << 8); + + if((slotframe = tsch_schedule_get_slotframe_by_handle(0)) == NULL || + (link = tsch_schedule_get_link_by_timeslot(slotframe, timeslot)) == NULL || + memcmp(peer_addr, &link->addr, sizeof(linkaddr_t)) != 0) { + LOG_ERR("Failed to delete a cell [slot:%u]\n", timeslot); + sixp_output(SIXP_PKT_TYPE_RESPONSE, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_RC_NORES, + SF_PLUGTEST_SFID, NULL, 0, peer_addr, + NULL, NULL, 0); + } else { + sixp_output(SIXP_PKT_TYPE_RESPONSE, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_RC_SUCCESS, + SF_PLUGTEST_SFID, cell, cell_list_len, peer_addr, + delete_res_sent_callback, &pending_cell, sizeof(pending_cell)); + } +} + +static void +delete_res_handler(const linkaddr_t *peer_addr, sixp_pkt_rc_t rc, + const uint8_t *body, size_t body_len) +{ + struct tsch_slotframe *slotframe; + struct tsch_link *link; + const sf_plugtest_cell_t *cell; + sixp_pkt_offset_t cell_list_len; + sixp_nbr_t *nbr; + uint16_t timeslot; + + if(body_len != 4) { + LOG_ERR("invalid Delete Response length: %u\n", (unsigned int)body_len); + return; + } + + assert( + sixp_pkt_get_cell_list(SIXP_PKT_TYPE_RESPONSE, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_RC_SUCCESS, + (const uint8_t **)&cell, &cell_list_len, + body, body_len) == 0); + timeslot = cell->slot_offset[0] + (cell->slot_offset[1] << 8); + + if((nbr = sixp_nbr_find(peer_addr)) == NULL) { + LOG_ERR("unexpected error; cannot find nbr\n"); + return; + } + + if(rc != SIXP_PKT_RC_SUCCESS) { + LOG_ERR("received return code of %u\n", rc); + return; + } + + if((slotframe = tsch_schedule_get_slotframe_by_handle(0)) == NULL || + (link = tsch_schedule_get_link_by_timeslot(slotframe, timeslot)) == NULL || + memcmp(peer_addr, &link->addr, sizeof(linkaddr_t)) != 0 || + delete_cell(peer_addr, cell) < 0) { + LOG_ERR("Failed to delete a cell [slot:%u]\n", timeslot); + } + advance_gen(peer_addr); +} + +static void +count_req_handler(const linkaddr_t *peer_addr, + const uint8_t *body, size_t body_len) +{ + sixp_pkt_cell_options_t cell_options; + struct tsch_slotframe *slotframe; + struct tsch_link *cell; + sixp_pkt_total_num_cells_t total_num_cells = 0; + uint8_t buf[2]; + + assert(peer_addr != NULL && body != NULL); + if(body_len != (sizeof(sixp_pkt_metadata_t) + + sizeof(sixp_pkt_cell_options_t))) { + LOG_ERR("invalid Count Request length: %u\n", (unsigned int)body_len); + } + assert( + sixp_pkt_get_cell_options(SIXP_PKT_TYPE_REQUEST, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_CMD_COUNT, + &cell_options, + body, body_len) == 0); + if(cell_options != SIXP_PKT_CELL_OPTION_TX && + cell_options != (SIXP_PKT_CELL_OPTION_TX | SIXP_PKT_CELL_OPTION_SHARED)) { + LOG_ERR("invalid Cell Options: %u\n", cell_options); + } + + if((slotframe = tsch_schedule_get_slotframe_by_handle(0)) == NULL) { + sixp_output(SIXP_PKT_TYPE_RESPONSE, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_RC_ERROR, + SF_PLUGTEST_SFID, NULL, 0, peer_addr, + NULL, NULL, 0); + return; + } + + for(cell = (struct tsch_link *)list_head(slotframe->links_list); + cell != NULL; cell = (struct tsch_link *)list_item_next(cell)) { + if(memcmp(&cell->addr, peer_addr, sizeof(linkaddr_t)) == 0 && + cell->link_options == LINK_OPTION_RX) { + total_num_cells++; + } + } + + /* make sure total_num_cells are set in little-endian */ + buf[0] = (total_num_cells & 0xff); + buf[1] = (total_num_cells >> 8); + + sixp_output(SIXP_PKT_TYPE_RESPONSE, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_RC_SUCCESS, SF_PLUGTEST_SFID, + (const uint8_t *)buf, sizeof(buf), + peer_addr, NULL, NULL, 0); +} + +static void +count_res_handler(const linkaddr_t *peer_addr, sixp_pkt_rc_t rc, + const uint8_t *body, size_t body_len) +{ + sixp_pkt_total_num_cells_t total_num_cells; + + if(body_len != 2) { + LOG_ERR("invalid Count Response length: %u\n", (unsigned int)body_len); + return; + } + + if(rc != SIXP_PKT_RC_SUCCESS) { + LOG_ERR("received return code of %u\n", rc); + return; + } + + assert( + sixp_pkt_get_total_num_cells(SIXP_PKT_TYPE_RESPONSE, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_RC_SUCCESS, + &total_num_cells, body, body_len) == 0); + + LOG_INFO("Succeeded to get COUNT: %u\n", total_num_cells); +} + +static void +list_req_handler(const linkaddr_t *peer_addr, + const uint8_t *body, size_t body_len) +{ + sixp_pkt_cell_options_t cell_options; + sixp_pkt_offset_t cell_list_offset; + sixp_pkt_max_num_cells_t max_num_cells; + + struct tsch_slotframe *slotframe; + struct tsch_link *link; + sixp_pkt_offset_t cell_nums; + sf_plugtest_cell_t cell; + + assert(peer_addr != NULL && body != NULL); + if(body_len != (sizeof(sixp_pkt_metadata_t) + + sizeof(sixp_pkt_cell_options_t) + + sizeof(sixp_pkt_reserved_t) + + sizeof(sixp_pkt_offset_t) + + sizeof(sixp_pkt_max_num_cells_t))) { + LOG_ERR("invalid List Request length: %u\n", (unsigned int)body_len); + } + + assert( + sixp_pkt_get_cell_options(SIXP_PKT_TYPE_REQUEST, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_CMD_LIST, + &cell_options, + body, body_len) == 0); + assert( + sixp_pkt_get_offset(SIXP_PKT_TYPE_REQUEST, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_CMD_LIST, + &cell_list_offset, + body, body_len) == 0); + assert( + sixp_pkt_get_max_num_cells(SIXP_PKT_TYPE_REQUEST, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_CMD_LIST, + &max_num_cells, + body, body_len) == 0); + + if(cell_options != SIXP_PKT_CELL_OPTION_TX && + cell_options != (SIXP_PKT_CELL_OPTION_TX | SIXP_PKT_CELL_OPTION_SHARED)) { + LOG_ERR("invalid Cell Options: %u\n", cell_options); + return; + } + + if(max_num_cells < 1) { + LOG_ERR("invalid MaxNumCells: %u\n", max_num_cells); + return; + } + + if((slotframe = tsch_schedule_get_slotframe_by_handle(0)) == NULL) { + sixp_output(SIXP_PKT_TYPE_RESPONSE, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_RC_ERROR, + SF_PLUGTEST_SFID, NULL, 0, peer_addr, + NULL, NULL, 0); + return; + } + + cell_nums = 0; + for(link = (struct tsch_link *)list_head(slotframe->links_list); + link != NULL; link = (struct tsch_link *)list_item_next(link)) { + if(memcmp(&link->addr, peer_addr, sizeof(linkaddr_t)) == 0 && + link->link_options == LINK_OPTION_RX) { + if(cell_list_offset == cell_nums) { + cell.slot_offset[0] = link->timeslot & 0xff; + cell.slot_offset[1] = link->timeslot >> 8; + cell.channel_offset[0] = link->channel_offset & 0xff; + cell.channel_offset[1] = link->channel_offset >> 8; + } + cell_nums++; + } + } + + if(cell_nums == 0) { + assert(sixp_output(SIXP_PKT_TYPE_RESPONSE, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_RC_EOL, + SF_PLUGTEST_SFID, NULL, 0, + peer_addr, NULL, NULL, 0) == 0); + } else { + assert(sixp_output(SIXP_PKT_TYPE_RESPONSE, + (cell_list_offset + 1) == cell_nums ? + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_RC_EOL : + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_RC_SUCCESS, + SF_PLUGTEST_SFID, + (const uint8_t *)&cell, sizeof(cell), + peer_addr, NULL, NULL, 0) == 0); + } +} + +static void +list_res_handler(const linkaddr_t *peer_addr, sixp_pkt_rc_t rc, + const uint8_t *body, size_t body_len) +{ + sf_plugtest_cell_t *cell; + uint16_t slot_offset, channel_offset; + sixp_pkt_offset_t cell_list_len; + sixp_pkt_offset_t i; + + assert(sixp_pkt_get_cell_list(SIXP_PKT_TYPE_RESPONSE, + (sixp_pkt_code_t)(uint8_t)rc, + (const uint8_t **)&cell, &cell_list_len, + body, body_len) == 0); + + LOG_INFO("Succeeded to get LIST: "); + for(i = 0; i < cell_list_len; i += sizeof(sf_plugtest_cell_t)) { + slot_offset = cell->slot_offset[0] + (cell->slot_offset[1] << 8); + channel_offset = cell->channel_offset[0] + (cell->channel_offset[1] << 8); + LOG_INFO_("[slot:%u, channel:%u]", slot_offset, channel_offset); + } + + if(rc == SIXP_PKT_RC_SUCCESS) { + LOG_INFO_(" continued\n"); + cell_list_offset += (i / sizeof(sf_plugtest_cell_t)); + } else { + /* EOL */ + LOG_INFO_(" EOL\n"); + cell_list_offset = 0; + } +} + +static void +clear_req_handler(const linkaddr_t *peer_addr, + const uint8_t *body, size_t body_len) +{ + struct tsch_slotframe *slotframe; + sixp_nbr_t *nbr; + + assert(peer_addr != NULL && body != NULL); + if(body_len != sizeof(sixp_pkt_metadata_t)) { + LOG_ERR("invalid Clear Request length: %u\n", (unsigned int)body_len); + } + + if((slotframe = tsch_schedule_get_slotframe_by_handle(0)) == NULL) { + sixp_output(SIXP_PKT_TYPE_RESPONSE, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_RC_ERROR, + SF_PLUGTEST_SFID, NULL, 0, peer_addr, + NULL, NULL, 0); + return; + } + + clear_cells(peer_addr, slotframe); + + sixp_output(SIXP_PKT_TYPE_RESPONSE, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_RC_SUCCESS, + SF_PLUGTEST_SFID, NULL, 0, peer_addr, + NULL, NULL, 0); + + if((nbr = sixp_nbr_find(peer_addr)) != NULL) { + /* clear GEN */ + sixp_nbr_free(nbr); + } +} + +static void +clear_res_handler(const linkaddr_t *peer_addr, sixp_pkt_rc_t rc, + const uint8_t *body, size_t body_len) +{ + struct tsch_slotframe *slotframe; + sixp_nbr_t *nbr; + + assert(peer_addr != NULL && body != NULL); + + if(peer_addr == NULL) { + return; + } + + if(body_len != 0) { + LOG_ERR("invalid Clear Response length: %u\n", (unsigned int)body_len); + return; + } + + if((slotframe = tsch_schedule_get_slotframe_by_handle(0)) != NULL) { + clear_cells(peer_addr, slotframe); + } + if((nbr = sixp_nbr_find(peer_addr)) != NULL) { + /* clear GEN */ + sixp_nbr_free(nbr); + } + + LOG_INFO("Succeeded to CLEAR\n"); +} + +static void +help(shell_output_func output, subcmd_args_t *args) +{ + /* help doesn't use peer_addr */ + SHELL_OUTPUT(output, "cmd [slot_offset] [channel_offset] [peer_addr]\n"); + SHELL_OUTPUT(output, "available commands\n"); + SHELL_OUTPUT(output, "add - add a TX cell\n"); + SHELL_OUTPUT(output, "delete - delete a TX cell\n"); + SHELL_OUTPUT(output, "count - get cell count\n"); + SHELL_OUTPUT(output, "list - get a cell list\n"); + SHELL_OUTPUT(output, "clear - clear TX cells\n"); + SHELL_OUTPUT(output, "help - show this usage\n"); + SHELL_OUTPUT(output, "example> 6p add 4 5 01:01:01:01:01:01:01:01\n"); +} + +static void +add_delete(shell_output_func output, subcmd_args_t *args) +{ + assert(args != NULL); + sf_plugtest_cell_t cell; + + /* set cell attributes in little-endian */ + cell.slot_offset[0] = args->slot_offset & 0xff; + cell.slot_offset[1] = args->slot_offset >> 8; + cell.channel_offset[0] = args->channel_offset & 0xff; + cell.channel_offset[1] = args->channel_offset >> 8; + + if(args->cmd == SIXP_PKT_CMD_ADD && + reserve_cell(&(args->peer_addr), &cell) < 0) { + SHELL_OUTPUT(output, "invalid arguments for Add Request\n"); + return; + } + + memset(sixp_pkt_buf, 0, sizeof(sixp_pkt_buf)); + assert(sixp_pkt_set_cell_options(SIXP_PKT_TYPE_REQUEST, + (sixp_pkt_code_t)(uint8_t)args->cmd, + SIXP_PKT_CELL_OPTION_TX, + sixp_pkt_buf, sizeof(sixp_pkt_buf)) == 0); + assert(sixp_pkt_set_num_cells(SIXP_PKT_TYPE_REQUEST, + (sixp_pkt_code_t)(uint8_t)args->cmd, + 1, + sixp_pkt_buf, sizeof(sixp_pkt_buf)) == 0); + assert(sixp_pkt_set_cell_list(SIXP_PKT_TYPE_REQUEST, + (sixp_pkt_code_t)(uint8_t)args->cmd, + (const uint8_t *)&cell, + sizeof(cell), 0, + sixp_pkt_buf, sizeof(sixp_pkt_buf)) == 0); + assert(sixp_output(SIXP_PKT_TYPE_REQUEST, + (sixp_pkt_code_t)(uint8_t)args->cmd, + SF_PLUGTEST_SFID, sixp_pkt_buf, + sizeof(sixp_pkt_metadata_t) + + sizeof(sixp_pkt_cell_options_t) + + sizeof(sixp_pkt_num_cells_t) + + sizeof(cell), + &(args->peer_addr), + NULL, NULL, 0) == 0); + + SHELL_OUTPUT(output, "sent %s request [slot:%u, channel:%u]\n", + args->cmd == SIXP_PKT_CMD_ADD ? "an Add" : "a Delete", + args->slot_offset, args->channel_offset); +} + +static void +count(shell_output_func output, subcmd_args_t *args) +{ + memset(&sixp_pkt_buf, 0, sizeof(sixp_pkt_buf)); + assert(sixp_pkt_set_cell_options(SIXP_PKT_TYPE_REQUEST, + (sixp_pkt_code_t)(uint8_t)args->cmd, + SIXP_PKT_CELL_OPTION_TX, + sixp_pkt_buf, sizeof(sixp_pkt_buf)) == 0); + + assert(sixp_output(SIXP_PKT_TYPE_REQUEST, + (sixp_pkt_code_t)(uint8_t)args->cmd, + SF_PLUGTEST_SFID, sixp_pkt_buf, + sizeof(sixp_pkt_metadata_t) + + sizeof(sixp_pkt_cell_options_t), + &(args->peer_addr), + NULL, NULL, 0) == 0); + + SHELL_OUTPUT(output, "sent a Count request\n"); +} + +static void +list(shell_output_func output, subcmd_args_t *args) +{ + send_list_req(&(args->peer_addr)); + SHELL_OUTPUT(output, "sent a List request\n"); +} + +static void +clear(shell_output_func output, subcmd_args_t *args) +{ + cell_list_offset = 0; + memset(sixp_pkt_buf, 0, sizeof(sixp_pkt_buf)); + assert(sixp_output(SIXP_PKT_TYPE_REQUEST, + (sixp_pkt_code_t)(uint8_t)SIXP_PKT_CMD_CLEAR, + SF_PLUGTEST_SFID, sixp_pkt_buf, + sizeof(sixp_pkt_metadata_t), + &(args->peer_addr), + NULL, NULL, 0) == 0); + SHELL_OUTPUT(output, "sent a Clear request\n"); +} + +static int +parse_args(shell_output_func output, + char *args, const char **subcmd, subcmd_args_t *subcmd_args) +{ + struct tsch_neighbor *time_source = NULL; + char *next_args; +#if CONTIKI_TARGET_COOJA + char *saveptr; + char *octet; + int i; +#endif /* CONTIKI_TARGET_COOJA */ + + SHELL_ARGS_INIT(args, next_args); + SHELL_ARGS_NEXT(args, next_args); + + if(args == NULL) { + *subcmd = subcmd_help; + } else { + *subcmd = args; + } + + if(strncmp("help", *subcmd, sizeof("help")) == 0) { + /* we don't need peer_addr for the help sub-command. */ + return 0; + } + + /* add and delete need slot_offset and channel_offset */ + if(strncmp("add", *subcmd, sizeof("add")) == 0 || + strncmp("delete", *subcmd, sizeof("delete")) == 0) { + SHELL_ARGS_NEXT(args, next_args); + if(args != NULL) { + subcmd_args->slot_offset = strtol(args, NULL, 10); + } else { + return -1; + } + SHELL_ARGS_NEXT(args, next_args); + if(args != NULL) { + subcmd_args->channel_offset = strtol(args, NULL, 10); + } else { + return -1; + } + } + + SHELL_ARGS_NEXT(args, next_args); + if(args != NULL) { +#if CONTIKI_TARGET_COOJA + for(octet = strtok_r(args, ":", &saveptr), i = 0; + octet != NULL && i < LINKADDR_SIZE; + octet = strtok_r(NULL, ":", &saveptr), i++) { + subcmd_args->peer_addr.u8[i] = strtol(octet, NULL, 16); + } + if(i > 1 && i != LINKADDR_SIZE) { + /* invalid MAC address */ + memset(&(subcmd_args->peer_addr), 0, sizeof(linkaddr_t)); + return -1; + } +#else + SHELL_OUTPUT(output, "MAC address cannot be specified on this platform\n"); + return -1; +#endif /* CONTIKI_TARGET_COOJA */ + } else { + if((time_source = tsch_queue_get_time_source()) == NULL) { + SHELL_OUTPUT(output, "time source is not available\n"); + return -1; + } else { + memcpy(&(subcmd_args->peer_addr), &(time_source->addr), + sizeof(linkaddr_t)); + } + } + + return 0; +} + +static void +shell_subcmd(shell_output_func output, char *args) +{ + const char *subcmd; + subcmd_args_t subcmd_args; + + int i; + + if(shell_output == NULL) { + shell_output = output; + } + + if(parse_args(output, args, &subcmd, &subcmd_args) < 0) { + SHELL_OUTPUT(output, + "invalid argument; command argument parse error\n"); + return; + } + + for(i = 0; subcmds[i].name != NULL; i++) { + if(strcmp(subcmds[i].name, subcmd) == 0) { + subcmd_args.cmd = subcmds[i].cmd; + subcmds[i].func(output, &subcmd_args); + } + } +} + +static void +init(void) +{ + shell_commands_set_6top_sub_cmd(shell_subcmd); +} + +static void +input(sixp_pkt_type_t type, sixp_pkt_code_t code, + const uint8_t *body, uint16_t body_len, + const linkaddr_t *src_addr) +{ + sixp_trans_t *trans; + sixp_pkt_cmd_t cmd; + int i; + + switch(type) { + case SIXP_PKT_TYPE_REQUEST: + cmd = code.cmd; + break; + case SIXP_PKT_TYPE_RESPONSE: + if((trans = sixp_trans_find(src_addr)) == NULL) { + LOG_ERR("internal error; cannot find a trans\n"); + return; + } + cmd = sixp_trans_get_cmd(trans); + break; + default: + LOG_ERR("unsupported type %u by sf-plugtest\n", type); + return; + } + + if(type == SIXP_PKT_TYPE_RESPONSE && + code.rc != SIXP_PKT_RC_SUCCESS && + cmd != SIXP_PKT_CMD_LIST && + code.rc != SIXP_PKT_RC_EOL) { + LOG_ERR("received return code of %u\n", code.value); + return; + } + + for(i = 0; i < sizeof(handlers); i++) { + if(handlers[i].cmd == cmd) { + if(type == SIXP_PKT_TYPE_REQUEST && handlers[i].req != NULL) { + handlers[i].req(src_addr, body, body_len); + return; + } + if(type == SIXP_PKT_TYPE_RESPONSE && handlers[i].res != NULL) { + handlers[i].res(src_addr, code.rc, body, body_len); + return; + } + i = sizeof(handlers); + break; + } + } + + if(i == sizeof(handlers)) { + LOG_ERR("unsupported command %u by sf-plugtest\n", + code.cmd); + } +} + +static void +timeout(sixp_pkt_cmd_t cmd, const linkaddr_t *peer_addr) +{ + LOG_ERR("transaction timeout\n"); +} + +const sixtop_sf_t sf_plugtest = { + SF_PLUGTEST_SFID, + SF_PLUGTEST_TIMEOUT, + init, + input, + timeout +}; diff --git a/examples/6tisch/etsi-plugtest-2017/sf-plugtest.h b/examples/6tisch/etsi-plugtest-2017/sf-plugtest.h new file mode 100644 index 000000000..7a723edbb --- /dev/null +++ b/examples/6tisch/etsi-plugtest-2017/sf-plugtest.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017, Toshiba Corporation + * 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 _SF_PLUGTEST_H_ +#define _SF_PLUGTEST_H_ + +#include +#include + +extern const sixtop_sf_t sf_plugtest; + +#endif /* _SF_PLUGTEST_H_ */ diff --git a/examples/6tisch/etsi-plugtest-2017/test-with-cooja-mote.csc b/examples/6tisch/etsi-plugtest-2017/test-with-cooja-mote.csc new file mode 100644 index 000000000..fda05987f --- /dev/null +++ b/examples/6tisch/etsi-plugtest-2017/test-with-cooja-mote.csc @@ -0,0 +1,171 @@ + + + [APPS_DIR]/mrm + [APPS_DIR]/mspsim + [APPS_DIR]/avrora + [APPS_DIR]/serial_socket + [APPS_DIR]/collect-view + [APPS_DIR]/powertracker + [APPS_DIR]/radiologger-headless + + ETSI Plugtest 2017 with Cooja Mote + 1.0 + 123456 + 1000000 + + org.contikios.cooja.radiomediums.UDGM + 50.0 + 100.0 + 1.0 + 1.0 + + + 40000 + + + org.contikios.cooja.contikimote.ContikiMoteType + mtype205 + RPL/TSCH Node + [CONTIKI_DIR]/examples/6tisch/etsi-plugtest-2017/node.c + make TARGET=cooja clean + make TARGET=cooja MAKE_WITH_SIXTOP=1 node.cooja + org.contikios.cooja.interfaces.Position + org.contikios.cooja.interfaces.Battery + org.contikios.cooja.contikimote.interfaces.ContikiVib + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + org.contikios.cooja.contikimote.interfaces.ContikiRS232 + org.contikios.cooja.contikimote.interfaces.ContikiBeeper + org.contikios.cooja.interfaces.RimeAddress + org.contikios.cooja.contikimote.interfaces.ContikiIPAddress + org.contikios.cooja.contikimote.interfaces.ContikiRadio + org.contikios.cooja.contikimote.interfaces.ContikiButton + org.contikios.cooja.contikimote.interfaces.ContikiPIR + org.contikios.cooja.contikimote.interfaces.ContikiClock + org.contikios.cooja.contikimote.interfaces.ContikiLED + org.contikios.cooja.contikimote.interfaces.ContikiCFS + org.contikios.cooja.interfaces.Mote2MoteRelations + org.contikios.cooja.interfaces.MoteAttributes + false + + + + org.contikios.cooja.interfaces.Position + -1.285769821276336 + 38.58045647334346 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 1 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + mtype205 + + + + org.contikios.cooja.interfaces.Position + 39.27770695164709 + 38.31253538173354 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 2 + + + org.contikios.cooja.contikimote.interfaces.ContikiRS232 + 6top list~;6top add 2 3~;log 6top 3~;6top add 2~;6top add~;help~; + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + mtype205 + + + + org.contikios.cooja.plugins.SimControl + 242 + 0 + 160 + 11 + 241 + + + org.contikios.cooja.plugins.Visualizer + + true + org.contikios.cooja.plugins.skins.IDVisualizerSkin + org.contikios.cooja.plugins.skins.GridVisualizerSkin + org.contikios.cooja.plugins.skins.TrafficVisualizerSkin + org.contikios.cooja.plugins.skins.UDGMVisualizerSkin + 1.7405603810040515 0.0 0.0 1.7405603810040515 89.95980153208089 14.423865844552461 + + 236 + 2 + 230 + 1 + 1 + + + org.contikios.cooja.plugins.LogListener + + + + + + 1031 + 4 + 394 + 273 + 6 + + + org.contikios.cooja.plugins.TimeLine + + 0 + 1 + + + + 16529.88882215865 + + 1304 + 5 + 311 + 0 + 412 + + + org.contikios.cooja.plugins.RadioLogger + + 150 + + false + false + + + 500 + 3 + 300 + 255 + 141 + + + org.contikios.cooja.plugins.MoteInterfaceViewer + 1 + + Serial port + 0,0 + + 778 + 1 + 412 + 288 + 98 + + + diff --git a/tests/02-compile-arm-ports/Makefile b/tests/02-compile-arm-ports/Makefile index 023b58374..805a98ff4 100644 --- a/tests/02-compile-arm-ports/Makefile +++ b/tests/02-compile-arm-ports/Makefile @@ -44,6 +44,8 @@ ipv6/rpl-tsch/zoul \ ipv6/rpl-tsch/zoul:MAKE_WITH_ORCHESTRA=1 \ ipv6/rpl-tsch/zoul:MAKE_WITH_SECURITY=1 \ logging/zoul \ +6tisch/etsi-plugtest-2017/zoul:BOARD=remote \ +6tisch/etsi-plugtest-2017/srf06-cc26xx:BOARD=launchpad/cc2650 TOOLS= diff --git a/tests/03-compile-nxp-ports/Makefile b/tests/03-compile-nxp-ports/Makefile index 9f766534c..0672f9786 100644 --- a/tests/03-compile-nxp-ports/Makefile +++ b/tests/03-compile-nxp-ports/Makefile @@ -19,7 +19,8 @@ sensniff/jn516x \ ipv6/rpl-tsch/jn516x \ ipv6/rpl-tsch/jn516x:MAKE_WITH_ORCHESTRA=1 \ ipv6/rpl-tsch/jn516x:MAKE_WITH_SECURITY=1 \ -logging/jn516x +logging/jn516x \ +6tisch/etsi-plugtest-2017/jn516x TOOLS=