81d34e1d51
The 1st F-Interop 6TiSCH Interoperability Event: http://www.etsi.org/news-events/events/1197-6tisch-interop-prague-2017
1010 lines
33 KiB
C
1010 lines
33 KiB
C
/*
|
|
* 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 <contiki.h>
|
|
#include <shell.h>
|
|
#include <shell-commands.h>
|
|
#include <net/linkaddr.h>
|
|
#include <net/mac/tsch/tsch-schedule.h>
|
|
#include <net/mac/tsch/sixtop/sixtop.h>
|
|
#include <net/mac/tsch/sixtop/sixp.h>
|
|
#include <net/mac/tsch/sixtop/sixp-nbr.h>
|
|
#include <net/mac/tsch/sixtop/sixp-trans.h>
|
|
#include <net/mac/tsch/sixtop/sixp-pkt.h>
|
|
|
|
#include <sys/log.h>
|
|
#define LOG_MODULE "6top"
|
|
#define LOG_LEVEL LOG_LEVEL_6TOP
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <lib/assert.h>
|
|
|
|
#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
|
|
};
|