mbed-os/features/nanostack/mbed-mesh-api/source/WisunInterface.cpp

730 lines
22 KiB
C++

/*
* Copyright (c) 2018-2019 ARM Limited. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "WisunInterface.h"
#include "NanostackRfPhy.h"
#include "include/wisun_tasklet.h"
#include "callback_handler.h"
#include "NanostackLockGuard.h"
#include "mesh_system.h"
#include "randLIB.h"
#include "fhss_api.h"
#include "fhss_config.h"
#include "ws_management_api.h"
#include "net_rpl.h"
#include "net_interface.h"
#include "ns_trace.h"
#define TRACE_GROUP "WSIn"
class Nanostack::WisunInterface : public Nanostack::MeshInterface {
public:
virtual nsapi_error_t bringup(bool dhcp, const char *ip,
const char *netmask, const char *gw,
nsapi_ip_stack_t stack = IPV6_STACK,
bool blocking = true);
virtual nsapi_error_t bringdown();
virtual char *get_gateway(char *buf, nsapi_size_t buflen);
friend class Nanostack;
friend class ::WisunInterface;
private:
WisunInterface(NanostackRfPhy &phy) : MeshInterface(phy) { }
mesh_error_t init();
mesh_error_t mesh_connect();
mesh_error_t mesh_disconnect();
};
Nanostack::WisunInterface *WisunInterface::get_interface() const
{
return static_cast<Nanostack::WisunInterface *>(_interface);
}
nsapi_error_t WisunInterface::do_initialize()
{
if (!_interface) {
_interface = new (std::nothrow) Nanostack::WisunInterface(*_phy);
if (!_interface) {
return NSAPI_ERROR_NO_MEMORY;
}
_interface->attach(_connection_status_cb);
}
// Apply mbed configuration to Wi-SUN
configure();
return NSAPI_ERROR_OK;
}
nsapi_error_t WisunInterface::configure()
{
mesh_error_t status;
if (_configured) {
// Already configured
return NSAPI_ERROR_OK;
}
_configured = true;
#ifdef MBED_CONF_MBED_MESH_API_WISUN_NETWORK_NAME
char network_name[] = {MBED_CONF_MBED_MESH_API_WISUN_NETWORK_NAME};
status = set_network_name((char *) &network_name);
if (status != MESH_ERROR_NONE) {
tr_error("Failed to set network name!");
return NSAPI_ERROR_PARAMETER;
}
#endif
#if (MBED_CONF_MBED_MESH_API_WISUN_REGULATORY_DOMAIN != 255) || (MBED_CONF_MBED_MESH_API_WISUN_OPERATING_CLASS != 255) || (MBED_CONF_MBED_MESH_API_WISUN_OPERATING_MODE != 255)
status = set_network_regulatory_domain(MBED_CONF_MBED_MESH_API_WISUN_REGULATORY_DOMAIN,
MBED_CONF_MBED_MESH_API_WISUN_OPERATING_CLASS,
MBED_CONF_MBED_MESH_API_WISUN_OPERATING_MODE);
if (status != MESH_ERROR_NONE) {
tr_error("Failed to set regulatory domain!");
return NSAPI_ERROR_PARAMETER;
}
#endif
#if (MBED_CONF_MBED_MESH_API_WISUN_UC_CHANNEL_FUNCTION != 255)
status = set_unicast_channel_function(static_cast<mesh_channel_function_t>(MBED_CONF_MBED_MESH_API_WISUN_UC_CHANNEL_FUNCTION),
MBED_CONF_MBED_MESH_API_WISUN_UC_FIXED_CHANNEL,
MBED_CONF_MBED_MESH_API_WISUN_UC_DWELL_INTERVAL);
if (status != MESH_ERROR_NONE) {
tr_error("Failed to set unicast channel function configuration");
return NSAPI_ERROR_PARAMETER;
}
#endif
#if (MBED_CONF_MBED_MESH_API_WISUN_BC_CHANNEL_FUNCTION != 255) || (MBED_CONF_MBED_MESH_API_WISUN_BC_DWELL_INTERVAL != 0) || (MBED_CONF_MBED_MESH_API_WISUN_BC_INTERVAL != 0)
status = set_broadcast_channel_function(static_cast<mesh_channel_function_t>(MBED_CONF_MBED_MESH_API_WISUN_BC_CHANNEL_FUNCTION),
MBED_CONF_MBED_MESH_API_WISUN_BC_FIXED_CHANNEL,
MBED_CONF_MBED_MESH_API_WISUN_BC_DWELL_INTERVAL,
MBED_CONF_MBED_MESH_API_WISUN_BC_INTERVAL);
if (status != MESH_ERROR_NONE) {
tr_error("Failed to set broadcast channel function configuration");
return NSAPI_ERROR_PARAMETER;
}
#endif
#ifdef MBED_CONF_MBED_MESH_API_WISUN_NETWORK_SIZE
status = set_network_size(MBED_CONF_MBED_MESH_API_WISUN_NETWORK_SIZE);
if (status < 0) {
tr_error("Failed to set network size");
return NSAPI_ERROR_PARAMETER;
}
#endif
return NSAPI_ERROR_OK;
}
nsapi_error_t Nanostack::WisunInterface::bringup(bool dhcp, const char *ip,
const char *netmask, const char *gw,
nsapi_ip_stack_t stack, bool blocking)
{
nanostack_lock();
if (register_phy() < 0) {
nanostack_unlock();
return NSAPI_ERROR_DEVICE_ERROR;
}
_blocking = blocking;
// After the RF is up, we can seed the random from it.
randLIB_seed_random();
mesh_error_t status = init();
if (status != MESH_ERROR_NONE) {
nanostack_unlock();
return map_mesh_error(status);
}
status = mesh_connect();
if (status != MESH_ERROR_NONE) {
nanostack_unlock();
return map_mesh_error(status);
}
// Release mutex before blocking
nanostack_unlock();
if (blocking) {
// wait connection for ever
connect_semaphore.acquire();
}
return 0;
}
nsapi_error_t Nanostack::WisunInterface::bringdown()
{
NanostackLockGuard lock;
mesh_error_t status = mesh_disconnect();
return map_mesh_error(status);
}
mesh_error_t Nanostack::WisunInterface::init()
{
wisun_tasklet_init();
__mesh_handler_set_callback(this);
interface_id = wisun_tasklet_network_init(_device_id);
if (interface_id == -2) {
return MESH_ERROR_PARAM;
} else if (interface_id == -3) {
return MESH_ERROR_MEMORY;
} else if (interface_id < 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t Nanostack::WisunInterface::mesh_connect()
{
int8_t status = -9; // init to unknown error
tr_debug("connect()");
status = wisun_tasklet_connect(&__mesh_handler_c_callback, interface_id);
if (status >= 0) {
return MESH_ERROR_NONE;
} else if (status == -1) {
return MESH_ERROR_PARAM;
} else if (status == -2) {
return MESH_ERROR_MEMORY;
} else if (status == -3) {
return MESH_ERROR_STATE;
} else {
return MESH_ERROR_UNKNOWN;
}
}
mesh_error_t Nanostack::WisunInterface::mesh_disconnect()
{
int8_t status = -1;
status = wisun_tasklet_disconnect(true);
if (status >= 0) {
return MESH_ERROR_NONE;
}
return MESH_ERROR_UNKNOWN;
}
char *Nanostack::WisunInterface::get_gateway(char *buf, nsapi_size_t buflen)
{
NanostackLockGuard lock;
if (wisun_tasklet_get_router_ip_address(buf, buflen) == 0) {
return buf;
}
return NULL;
}
bool WisunInterface::getRouterIpAddress(char *address, int8_t len)
{
SocketAddress sock_addr;
if (_interface->get_gateway(&sock_addr) == NSAPI_ERROR_OK) {
strncpy(address, sock_addr.get_ip_address(), len);
return true;
}
return false;
}
mesh_error_t WisunInterface::set_network_name(char *network_name)
{
int status = ws_management_network_name_set(get_interface_id(), network_name);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::get_network_name(char *network_name)
{
int status = ws_management_network_name_get(get_interface_id(), network_name);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::validate_network_name(char *network_name)
{
int status = ws_management_network_name_validate(get_interface_id(), network_name);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::set_network_regulatory_domain(uint8_t regulatory_domain, uint8_t operating_class, uint8_t operating_mode)
{
int status = ws_management_regulatory_domain_set(get_interface_id(), regulatory_domain, operating_class, operating_mode);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::get_network_regulatory_domain(uint8_t *regulatory_domain, uint8_t *operating_class, uint8_t *operating_mode)
{
int status = ws_management_regulatory_domain_get(get_interface_id(), regulatory_domain, operating_class, operating_mode);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::validate_network_regulatory_domain(uint8_t regulatory_domain, uint8_t operating_class, uint8_t operating_mode)
{
int status = ws_management_regulatory_domain_validate(get_interface_id(), regulatory_domain, operating_class, operating_mode);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::set_network_size(uint8_t network_size)
{
if (network_size == 0xff) {
// Size 0xff is internal API
network_size = 0xfe;
}
int status = ws_management_network_size_set(get_interface_id(), network_size);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::get_network_size(uint8_t *network_size)
{
int status = ws_management_network_size_get(get_interface_id(), network_size);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::validate_network_size(uint8_t network_size)
{
if (network_size == 0xff) {
// Size 0xff is internal API
network_size = 0xfe;
}
int status = ws_management_network_size_validate(get_interface_id(), network_size);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::set_channel_mask(uint32_t channel_mask[8])
{
int status = ws_management_channel_mask_set(get_interface_id(), channel_mask);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::get_channel_mask(uint32_t *channel_mask)
{
int status = ws_management_channel_mask_get(get_interface_id(), channel_mask);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::validate_channel_mask(uint32_t channel_mask[8])
{
int status = ws_management_channel_mask_validate(get_interface_id(), channel_mask);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::set_unicast_channel_function(mesh_channel_function_t channel_function, uint16_t fixed_channel, uint8_t dwell_interval)
{
int status = ws_management_fhss_unicast_channel_function_configure(get_interface_id(), channel_function, fixed_channel, dwell_interval);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::get_unicast_channel_function(mesh_channel_function_t *channel_function, uint16_t *fixed_channel, uint8_t *dwell_interval)
{
uint8_t ch_function;
int status = ws_management_fhss_unicast_channel_function_get(get_interface_id(), &ch_function, fixed_channel, dwell_interval);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
*channel_function = static_cast<mesh_channel_function_t>(ch_function);
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::validate_unicast_channel_function(mesh_channel_function_t channel_function, uint16_t fixed_channel, uint8_t dwell_interval)
{
int status = ws_management_fhss_unicast_channel_function_validate(get_interface_id(), channel_function, fixed_channel, dwell_interval);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::set_broadcast_channel_function(mesh_channel_function_t channel_function, uint16_t fixed_channel, uint8_t dwell_interval, uint32_t broadcast_interval)
{
int status = ws_management_fhss_broadcast_channel_function_configure(get_interface_id(), channel_function, fixed_channel, dwell_interval, broadcast_interval);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::get_broadcast_channel_function(mesh_channel_function_t *channel_function, uint16_t *fixed_channel, uint8_t *dwell_interval, uint32_t *broadcast_interval)
{
uint8_t ch_function;
int status = ws_management_fhss_broadcast_channel_function_get(get_interface_id(), &ch_function, fixed_channel, dwell_interval, broadcast_interval);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
*channel_function = static_cast<mesh_channel_function_t>(ch_function);
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::validate_broadcast_channel_function(mesh_channel_function_t channel_function, uint16_t fixed_channel, uint8_t dwell_interval, uint32_t broadcast_interval)
{
int status = ws_management_fhss_broadcast_channel_function_validate(get_interface_id(), channel_function, fixed_channel, dwell_interval, broadcast_interval);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::set_timing_parameters(uint16_t disc_trickle_imin, uint16_t disc_trickle_imax, uint8_t disc_trickle_k, uint16_t pan_timeout)
{
int status = ws_management_timing_parameters_set(get_interface_id(), disc_trickle_imin, disc_trickle_imax, disc_trickle_k, pan_timeout);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::get_timing_parameters(uint16_t *disc_trickle_imin, uint16_t *disc_trickle_imax, uint8_t *disc_trickle_k, uint16_t *pan_timeout)
{
int status = ws_management_timing_parameters_get(get_interface_id(), disc_trickle_imin, disc_trickle_imax, disc_trickle_k, pan_timeout);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::validate_timing_parameters(uint16_t disc_trickle_imin, uint16_t disc_trickle_imax, uint8_t disc_trickle_k, uint16_t pan_timeout)
{
int status = ws_management_timing_parameters_validate(get_interface_id(), disc_trickle_imin, disc_trickle_imax, disc_trickle_k, pan_timeout);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::set_device_min_sens(uint8_t device_min_sens)
{
int status = ws_device_min_sens_set(get_interface_id(), device_min_sens);
if (status != 0) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::get_device_min_sens(uint8_t *device_min_sens)
{
if (device_min_sens == NULL) {
return MESH_ERROR_PARAM;
}
/* To-Do :: Update this when device_min_sense get API is available */
*device_min_sens = 0;
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::validate_device_min_sens(uint8_t device_min_sens)
{
if (device_min_sens == 0xFF) {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::set_own_certificate(uint8_t *cert, uint16_t cert_len, uint8_t *cert_key, uint16_t cert_key_len)
{
mesh_error_t ret_val = MESH_ERROR_NONE;
int status = wisun_tasklet_set_own_certificate(cert, cert_len, cert_key, cert_key_len);
if (status == -1) {
ret_val = MESH_ERROR_MEMORY;
} else if (status == -2) {
ret_val = MESH_ERROR_STATE;
}
return ret_val;
}
mesh_error_t WisunInterface::remove_own_certificates(void)
{
mesh_error_t ret_val = MESH_ERROR_NONE;
int status = wisun_tasklet_remove_own_certificates();
if (status == -1) {
ret_val = MESH_ERROR_MEMORY;
} else if (status == -2) {
ret_val = MESH_ERROR_STATE;
}
return ret_val;
}
mesh_error_t WisunInterface::set_trusted_certificate(uint8_t *cert, uint16_t cert_len)
{
mesh_error_t ret_val = MESH_ERROR_NONE;
int status = wisun_tasklet_set_trusted_certificate(cert, cert_len);
if (status == -1) {
ret_val = MESH_ERROR_MEMORY;
} else if (status == -2) {
ret_val = MESH_ERROR_STATE;
}
return ret_val;
}
mesh_error_t WisunInterface::remove_trusted_certificates(void)
{
mesh_error_t ret_val = MESH_ERROR_NONE;
int status = wisun_tasklet_remove_trusted_certificates();
if (status == -1) {
ret_val = MESH_ERROR_MEMORY;
} else if (status == -2) {
ret_val = MESH_ERROR_STATE;
}
return ret_val;
}
mesh_error_t WisunInterface::enable_statistics(void)
{
mesh_error_t ret_val = MESH_ERROR_NONE;
int status = wisun_tasklet_statistics_start();
if (status < 0) {
ret_val = MESH_ERROR_UNKNOWN;
}
return ret_val;
}
mesh_error_t WisunInterface::reset_statistics(void)
{
mesh_error_t ret_val = MESH_ERROR_NONE;
int status = wisun_tasklet_statistics_reset();
if (status < 0) {
ret_val = MESH_ERROR_UNKNOWN;
}
return ret_val;
}
mesh_error_t WisunInterface::read_nw_statistics(mesh_nw_statistics_t *statistics)
{
mesh_error_t ret_val = MESH_ERROR_NONE;
int status = wisun_tasklet_statistics_nw_read(statistics);
if (status < 0) {
ret_val = MESH_ERROR_UNKNOWN;
}
return ret_val;
}
mesh_error_t WisunInterface::read_mac_statistics(mesh_mac_statistics_t *statistics)
{
mesh_error_t ret_val = MESH_ERROR_NONE;
int status = wisun_tasklet_statistics_mac_read(statistics);
if (status < 0) {
ret_val = MESH_ERROR_UNKNOWN;
}
return ret_val;
}
mesh_error_t WisunInterface::info_get(ws_rpl_info_t *info_ptr)
{
if (info_ptr == NULL) {
return MESH_ERROR_PARAM;
}
rpl_dodag_info_t dodag_ptr = {0};
uint8_t rpl_instance_count;
uint8_t instance_id_list[10];
uint8_t instance_id = RPL_INSTANCE_LOCAL;
uint8_t instance_id_new;
uint8_t instance_index;
rpl_instance_count = rpl_instance_list_read(&instance_id_list[0], sizeof(instance_id_list));
if (rpl_instance_count > 10) {
return MESH_ERROR_UNKNOWN;
}
/* Find lowest global instance ID (assumption: RPL instance with lowest instance ID has
most generic routing rule and its rank should be indicated in beacon) */
for (instance_index = 0; instance_index < rpl_instance_count; instance_index++) {
instance_id_new = instance_id_list[instance_index];
if ((instance_id_new & RPL_INSTANCE_LOCAL) == RPL_INSTANCE_LOCAL) {
break;
} else {
if (instance_id_new < instance_id) {
instance_id = instance_id_new;
}
}
}
if (instance_id == RPL_INSTANCE_LOCAL) {
return MESH_ERROR_UNKNOWN;
}
if (!rpl_read_dodag_info(&dodag_ptr, instance_id)) {
return MESH_ERROR_UNKNOWN;
}
info_ptr->instance_id = dodag_ptr.instance_id;
info_ptr->version = dodag_ptr.version_num;
info_ptr->current_rank = dodag_ptr.curent_rank;
info_ptr->primary_parent_rank = dodag_ptr.primary_parent_rank;
memcpy(info_ptr->rpl_dodag_id, dodag_ptr.dodag_id, 16);
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::stack_info_get(ws_stack_state_t *stack_info_ptr)
{
if (stack_info_ptr == NULL) {
return MESH_ERROR_PARAM;
}
ws_stack_info_t stack_info = {0};
uint8_t global_address[16] = {0};
uint8_t link_local_address[16] = {0};
if (ws_stack_info_get(get_interface_id(), &stack_info)) {
return MESH_ERROR_UNKNOWN;
}
if (arm_net_address_get(get_interface_id(), ADDR_IPV6_GP, global_address) != 0) {
// No global prefix available, Nothing to do.
}
if (arm_net_address_get(get_interface_id(), ADDR_IPV6_LL, link_local_address) != 0) {
// No local prefix available, Nothing to do.
}
stack_info_ptr->join_state = stack_info.join_state;
stack_info_ptr->pan_id = stack_info.pan_id;
stack_info_ptr->rsl_in = stack_info.rsl_in;
stack_info_ptr->rsl_out = stack_info.rsl_out;
stack_info_ptr->device_min_sens = stack_info.device_min_sens;
memcpy(stack_info_ptr->parent_addr, stack_info.parent, 16);
memcpy(stack_info_ptr->global_addr, global_address, 16);
memcpy(stack_info_ptr->link_local_addr, link_local_address, 16);
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::cca_threshold_table_get(ws_cca_threshold_table_t *table)
{
if (table == NULL) {
return MESH_ERROR_PARAM;
}
const cca_threshold_table_s *cca_table = arm_nwk_get_cca_threshold_table(get_interface_id());
if (cca_table != NULL) {
table->number_of_channels = cca_table->number_of_channels;
table->cca_threshold_table = cca_table->cca_threshold_table;
} else {
return MESH_ERROR_UNKNOWN;
}
return MESH_ERROR_NONE;
}
mesh_error_t WisunInterface::nbr_info_get(ws_nbr_info_t *nbr_ptr, uint16_t *count)
{
uint16_t nbr_count;
if (count == NULL) {
return MESH_ERROR_UNKNOWN;
}
nbr_count = ws_neighbor_info_get(get_interface_id(), (ws_neighbour_info_t *)nbr_ptr, *count);
*count = nbr_count;
return MESH_ERROR_NONE;
}
#define WISUN 0x2345
#if MBED_CONF_NSAPI_DEFAULT_MESH_TYPE == WISUN && DEVICE_802_15_4_PHY
MBED_WEAK MeshInterface *MeshInterface::get_target_default_instance()
{
static bool inited;
static WisunInterface interface;
singleton_lock();
if (!inited) {
nsapi_error_t result = interface.initialize(&NanostackRfPhy::get_default_instance());
if (result != 0) {
tr_error("Wi-SUN initialize failed: %d", result);
singleton_unlock();
return NULL;
}
inited = true;
}
singleton_unlock();
return &interface;
}
#endif