blob: f9dd3dd5c6a6b960cb3e95c0ddcfd67d4ff6213b [file] [log] [blame]
/*
* Copyright (c) 2016 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief IEEE 802.15.4 Net Management Implementation
*
* All references to the spec refer to IEEE 802.15.4-2020.
*/
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(net_ieee802154_mgmt, CONFIG_NET_L2_IEEE802154_LOG_LEVEL);
#include <zephyr/net/net_core.h>
#include <errno.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/ieee802154_radio.h>
#include <zephyr/net/ieee802154_mgmt.h>
#include <zephyr/net/ieee802154.h>
#include "ieee802154_frame.h"
#include "ieee802154_mgmt_priv.h"
#include "ieee802154_priv.h"
#include "ieee802154_security.h"
#include "ieee802154_utils.h"
/**
* Implements (part of) the MLME-BEACON.notify primitive, see section 8.2.5.2.
*/
enum net_verdict ieee802154_handle_beacon(struct net_if *iface,
struct ieee802154_mpdu *mpdu,
uint8_t lqi)
{
struct ieee802154_context *ctx = net_if_l2_data(iface);
NET_DBG("Beacon received");
if (!ctx->scan_ctx) {
return NET_DROP;
}
if (!mpdu->beacon->sf.association) {
return NET_DROP;
}
k_sem_take(&ctx->scan_ctx_lock, K_FOREVER);
ctx->scan_ctx->pan_id = mpdu->mhr.src_addr->plain.pan_id;
ctx->scan_ctx->lqi = lqi;
if (mpdu->mhr.fs->fc.src_addr_mode == IEEE802154_ADDR_MODE_SHORT) {
ctx->scan_ctx->len = IEEE802154_SHORT_ADDR_LENGTH;
ctx->scan_ctx->short_addr =
sys_le16_to_cpu(mpdu->mhr.src_addr->plain.addr.short_addr);
} else {
ctx->scan_ctx->len = IEEE802154_EXT_ADDR_LENGTH;
sys_memcpy_swap(ctx->scan_ctx->addr,
mpdu->mhr.src_addr->plain.addr.ext_addr,
IEEE802154_EXT_ADDR_LENGTH);
}
net_mgmt_event_notify(NET_EVENT_IEEE802154_SCAN_RESULT, iface);
k_sem_give(&ctx->scan_ctx_lock);
return NET_CONTINUE;
}
static int ieee802154_cancel_scan(uint32_t mgmt_request, struct net_if *iface,
void *data, size_t len)
{
struct ieee802154_context *ctx = net_if_l2_data(iface);
ARG_UNUSED(data);
ARG_UNUSED(len);
NET_DBG("Cancelling scan request");
k_sem_take(&ctx->scan_ctx_lock, K_FOREVER);
ctx->scan_ctx = NULL;
k_sem_give(&ctx->scan_ctx_lock);
return 0;
}
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_CANCEL_SCAN,
ieee802154_cancel_scan);
static int ieee802154_scan(uint32_t mgmt_request, struct net_if *iface,
void *data, size_t len)
{
const struct ieee802154_phy_supported_channels *supported_channels;
struct ieee802154_context *ctx = net_if_l2_data(iface);
struct ieee802154_attr_value attr_value;
struct ieee802154_req_params *scan;
struct net_pkt *pkt = NULL;
int ret;
if (len != sizeof(struct ieee802154_req_params) || !data) {
return -EINVAL;
}
scan = (struct ieee802154_req_params *)data;
NET_DBG("%s scan requested",
mgmt_request == NET_REQUEST_IEEE802154_ACTIVE_SCAN ?
"Active" : "Passive");
k_sem_take(&ctx->scan_ctx_lock, K_FOREVER);
if (ctx->scan_ctx) {
ret = -EALREADY;
goto out;
}
if (mgmt_request == NET_REQUEST_IEEE802154_ACTIVE_SCAN) {
struct ieee802154_frame_params params = {0};
params.dst.len = IEEE802154_SHORT_ADDR_LENGTH;
params.dst.short_addr = IEEE802154_BROADCAST_ADDRESS;
params.dst.pan_id = IEEE802154_BROADCAST_PAN_ID;
pkt = ieee802154_create_mac_cmd_frame(
iface, IEEE802154_CFI_BEACON_REQUEST, &params);
if (!pkt) {
k_sem_give(&ctx->scan_ctx_lock);
NET_DBG("Could not create Beacon Request");
ret = -ENOBUFS;
goto out;
}
ieee802154_mac_cmd_finalize(pkt, IEEE802154_CFI_BEACON_REQUEST);
}
ctx->scan_ctx = scan;
k_sem_give(&ctx->scan_ctx_lock);
ret = 0;
k_sem_take(&ctx->ctx_lock, K_FOREVER);
ieee802154_radio_remove_pan_id(iface, ctx->pan_id);
k_sem_give(&ctx->ctx_lock);
ieee802154_radio_filter_pan_id(iface, IEEE802154_BROADCAST_PAN_ID);
if (ieee802154_radio_start(iface)) {
NET_DBG("Could not start device");
ret = -EIO;
goto out;
}
if (ieee802154_radio_attr_get(iface, IEEE802154_ATTR_PHY_SUPPORTED_CHANNEL_RANGES,
&attr_value)) {
NET_DBG("Could not determine supported channels");
ret = -ENOENT;
goto out;
}
supported_channels = attr_value.phy_supported_channels;
for (int channel_range = 0; channel_range < supported_channels->num_ranges;
channel_range++) {
for (uint16_t channel = supported_channels->ranges[channel_range].from_channel;
channel <= supported_channels->ranges[channel_range].to_channel; channel++) {
if (IEEE802154_IS_CHAN_UNSCANNED(scan->channel_set, channel)) {
continue;
}
scan->channel = channel;
NET_DBG("Scanning channel %u", channel);
ieee802154_radio_set_channel(iface, channel);
/* Active scan sends a beacon request */
if (mgmt_request == NET_REQUEST_IEEE802154_ACTIVE_SCAN) {
net_pkt_ref(pkt);
net_pkt_frag_ref(pkt->buffer);
ret = ieee802154_radio_send(iface, pkt, pkt->buffer);
if (ret) {
NET_DBG("Could not send Beacon Request (%d)", ret);
net_pkt_unref(pkt);
goto out;
}
}
/* Context aware sleep */
k_sleep(K_MSEC(scan->duration));
k_sem_take(&ctx->scan_ctx_lock, K_FOREVER);
if (!ctx->scan_ctx) {
NET_DBG("Scan request cancelled");
ret = -ECANCELED;
goto out;
}
k_sem_give(&ctx->scan_ctx_lock);
}
}
out:
/* Let's come back to context's settings. */
ieee802154_radio_remove_pan_id(iface, IEEE802154_BROADCAST_PAN_ID);
k_sem_take(&ctx->ctx_lock, K_FOREVER);
ieee802154_radio_filter_pan_id(iface, ctx->pan_id);
ieee802154_radio_set_channel(iface, ctx->channel);
k_sem_give(&ctx->ctx_lock);
k_sem_take(&ctx->scan_ctx_lock, K_FOREVER);
if (ctx->scan_ctx) {
ctx->scan_ctx = NULL;
}
k_sem_give(&ctx->scan_ctx_lock);
if (pkt) {
net_pkt_unref(pkt);
}
return ret;
}
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_PASSIVE_SCAN,
ieee802154_scan);
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_ACTIVE_SCAN,
ieee802154_scan);
/* Requires the context lock to be held. */
static inline void update_net_if_link_addr(struct net_if *iface, struct ieee802154_context *ctx)
{
bool was_if_up;
was_if_up = net_if_flag_test_and_clear(iface, NET_IF_RUNNING);
net_if_set_link_addr(iface, ctx->linkaddr.addr, ctx->linkaddr.len, ctx->linkaddr.type);
if (was_if_up) {
net_if_flag_set(iface, NET_IF_RUNNING);
}
}
/* Requires the context lock to be held. */
static inline void set_linkaddr_to_ext_addr(struct net_if *iface, struct ieee802154_context *ctx)
{
ctx->linkaddr.len = IEEE802154_EXT_ADDR_LENGTH;
sys_memcpy_swap(ctx->linkaddr.addr, ctx->ext_addr, IEEE802154_EXT_ADDR_LENGTH);
update_net_if_link_addr(iface, ctx);
}
/* Requires the context lock to be held and the PAN ID to be set. */
static inline void set_association(struct net_if *iface, struct ieee802154_context *ctx,
uint16_t short_addr)
{
uint16_t short_addr_be;
__ASSERT_NO_MSG(ctx->pan_id != IEEE802154_PAN_ID_NOT_ASSOCIATED);
__ASSERT_NO_MSG(short_addr != IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED);
ieee802154_radio_remove_src_short_addr(iface, ctx->short_addr);
ctx->short_addr = short_addr;
if (short_addr == IEEE802154_NO_SHORT_ADDRESS_ASSIGNED) {
set_linkaddr_to_ext_addr(iface, ctx);
} else {
ctx->linkaddr.len = IEEE802154_SHORT_ADDR_LENGTH;
short_addr_be = htons(short_addr);
memcpy(ctx->linkaddr.addr, &short_addr_be, IEEE802154_SHORT_ADDR_LENGTH);
update_net_if_link_addr(iface, ctx);
ieee802154_radio_filter_short_addr(iface, ctx->short_addr);
}
}
/* Requires the context lock to be held. */
static inline void remove_association(struct net_if *iface, struct ieee802154_context *ctx)
{
/* An associated device shall disassociate itself by removing all
* references to the PAN; the MLME shall set macPanId, macShortAddress,
* macAssociatedPanCoord [TODO: implement], macCoordShortAddress, and
* macCoordExtendedAddress to the default values, see section 6.4.2.
*/
ieee802154_radio_remove_pan_id(iface, ctx->pan_id);
ieee802154_radio_remove_src_short_addr(iface, ctx->short_addr);
ctx->pan_id = IEEE802154_PAN_ID_NOT_ASSOCIATED;
ctx->short_addr = IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED;
memset(ctx->coord_ext_addr, 0, sizeof(ctx->coord_ext_addr));
ctx->coord_short_addr = IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED;
set_linkaddr_to_ext_addr(iface, ctx);
ieee802154_radio_filter_pan_id(iface, IEEE802154_BROADCAST_PAN_ID);
ieee802154_radio_filter_short_addr(iface, IEEE802154_BROADCAST_ADDRESS);
}
/* Requires the context lock to be held. */
static inline bool is_associated(struct ieee802154_context *ctx)
{
/* see section 8.4.3.1, table 8-94, macPanId and macShortAddress */
return ctx->pan_id != IEEE802154_PAN_ID_NOT_ASSOCIATED &&
ctx->short_addr != IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED;
}
enum net_verdict ieee802154_handle_mac_command(struct net_if *iface,
struct ieee802154_mpdu *mpdu)
{
struct ieee802154_context *ctx = net_if_l2_data(iface);
if (mpdu->command->cfi == IEEE802154_CFI_ASSOCIATION_RESPONSE) {
if (mpdu->command->assoc_res.status !=
IEEE802154_ASF_SUCCESSFUL) {
return NET_DROP;
}
/* Validation of the association response, see section 7.5.3:
* * The Destination Addressing Mode and Source Addressing Mode
* fields shall each be set to indicate extended addressing.
* * The Frame Pending field shall be set to zero and ignored
* upon reception, and the AR field shall be set to one.
* * The Destination PAN ID field shall contain the value of
* macPanId, while the Source PAN ID field shall be omitted.
* * The Destination Address field shall contain the extended
* address of the device requesting association (has been
* tested during generic filtering already).
*
* Note: Unless the packet is authenticated, it cannot be verified
* that the response comes from the requested coordinator.
*/
if (mpdu->mhr.fs->fc.src_addr_mode !=
IEEE802154_ADDR_MODE_EXTENDED ||
mpdu->mhr.fs->fc.dst_addr_mode !=
IEEE802154_ADDR_MODE_EXTENDED ||
mpdu->mhr.fs->fc.ar != 1 ||
mpdu->mhr.fs->fc.pan_id_comp != 1 ||
mpdu->mhr.dst_addr->plain.pan_id != sys_cpu_to_le16(ctx->pan_id) ||
mpdu->command->assoc_res.short_addr == IEEE802154_PAN_ID_NOT_ASSOCIATED) {
return NET_DROP;
}
k_sem_take(&ctx->ctx_lock, K_FOREVER);
if (is_associated(ctx)) {
k_sem_give(&ctx->ctx_lock);
return NET_DROP;
}
/* If the Association Status field of the Association Response
* command indicates that the association was successful, the
* device shall store the address contained in the Short Address
* field of the command in macShortAddress, see section 6.4.1.
*/
set_association(iface, ctx, sys_le16_to_cpu(mpdu->command->assoc_res.short_addr));
/* If the [association request] contained the short address of
* the coordinator, the extended address of the coordinator,
* contained in the MHR of the Association Response command,
* shall be stored in macCoordExtendedAddress, see section 6.4.1.
*/
memcpy(ctx->coord_ext_addr, mpdu->mhr.src_addr->comp.addr.ext_addr,
IEEE802154_EXT_ADDR_LENGTH);
k_sem_give(&ctx->ctx_lock);
k_sem_give(&ctx->scan_ctx_lock);
return NET_CONTINUE;
}
if (mpdu->command->cfi == IEEE802154_CFI_DISASSOCIATION_NOTIFICATION) {
enum net_verdict ret = NET_DROP;
if (mpdu->command->disassoc_note.reason !=
IEEE802154_DRF_COORDINATOR_WISH) {
return NET_DROP;
}
k_sem_take(&ctx->ctx_lock, K_FOREVER);
if (!is_associated(ctx)) {
goto out;
}
/* Validation of the disassociation notification, see section 7.5.4:
* * The Source Addressing Mode field shall be set to indicate
* extended addressing.
* * The Frame Pending field shall be set to zero and ignored
* upon reception, and the AR field shall be set to one.
* * The Destination PAN ID field shall contain the value of macPanId.
* * The Source PAN ID field shall be omitted.
* * If the coordinator is disassociating a device from the
* PAN, then the Destination Address field shall contain the
* address of the device being removed from the PAN (asserted
* during generic package filtering).
*
* Note: Unless the packet is authenticated, it cannot be verified
* that the command comes from the requested coordinator.
*/
if (mpdu->mhr.fs->fc.src_addr_mode !=
IEEE802154_ADDR_MODE_EXTENDED ||
mpdu->mhr.fs->fc.ar != 1 ||
mpdu->mhr.fs->fc.pan_id_comp != 1 ||
mpdu->mhr.dst_addr->plain.pan_id != sys_cpu_to_le16(ctx->pan_id)) {
goto out;
}
/* If the source address contained in the Disassociation
* Notification command is equal to macCoordExtendedAddress,
* the device should consider itself disassociated,
* see section 6.4.2.
*/
if (memcmp(ctx->coord_ext_addr,
mpdu->mhr.src_addr->comp.addr.ext_addr,
IEEE802154_EXT_ADDR_LENGTH)) {
goto out;
}
remove_association(iface, ctx);
ret = NET_OK;
out:
k_sem_give(&ctx->ctx_lock);
return ret;
}
NET_DBG("Drop MAC command, unsupported CFI: 0x%x",
mpdu->command->cfi);
return NET_DROP;
}
static int ieee802154_associate(uint32_t mgmt_request, struct net_if *iface,
void *data, size_t len)
{
struct ieee802154_context *ctx = net_if_l2_data(iface);
struct ieee802154_frame_params params = {0};
struct ieee802154_req_params *req;
struct ieee802154_command *cmd;
struct net_pkt *pkt;
int ret = 0;
if (len != sizeof(struct ieee802154_req_params) || !data) {
return -EINVAL;
}
req = (struct ieee802154_req_params *)data;
/* Validate the coordinator's PAN ID. */
if (req->pan_id == IEEE802154_PAN_ID_NOT_ASSOCIATED) {
return -EINVAL;
}
params.dst.pan_id = req->pan_id;
/* If the Version field is set to 0b10, the Source PAN ID field is
* omitted. Otherwise, the Source PAN ID field shall contain the
* broadcast PAN ID.
*/
params.pan_id = IEEE802154_BROADCAST_PAN_ID;
/* Validate the coordinator's short address - if any. */
if (req->len == IEEE802154_SHORT_ADDR_LENGTH) {
if (req->short_addr == IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED ||
req->short_addr == IEEE802154_NO_SHORT_ADDRESS_ASSIGNED) {
return -EINVAL;
}
params.dst.short_addr = req->short_addr;
} else if (req->len == IEEE802154_EXT_ADDR_LENGTH) {
memcpy(params.dst.ext_addr, req->addr, sizeof(params.dst.ext_addr));
} else {
return -EINVAL;
}
params.dst.len = req->len;
k_sem_take(&ctx->ctx_lock, K_FOREVER);
if (is_associated(ctx)) {
k_sem_give(&ctx->ctx_lock);
return -EALREADY;
}
k_sem_give(&ctx->ctx_lock);
pkt = ieee802154_create_mac_cmd_frame(
iface, IEEE802154_CFI_ASSOCIATION_REQUEST, &params);
if (!pkt) {
ret = -ENOBUFS;
goto out;
}
k_sem_take(&ctx->ctx_lock, K_FOREVER);
cmd = ieee802154_get_mac_command(pkt);
cmd->assoc_req.ci.reserved_1 = 0U; /* Reserved */
cmd->assoc_req.ci.dev_type = 0U; /* RFD */
cmd->assoc_req.ci.power_src = 0U; /* TODO: set right power source */
cmd->assoc_req.ci.rx_on = 1U; /* TODO: derive from PM settings */
cmd->assoc_req.ci.association_type = 0U; /* normal association */
cmd->assoc_req.ci.reserved_2 = 0U; /* Reserved */
#ifdef CONFIG_NET_L2_IEEE802154_SECURITY
cmd->assoc_req.ci.sec_capability = ctx->sec_ctx.level > IEEE802154_SECURITY_LEVEL_NONE;
#else
cmd->assoc_req.ci.sec_capability = 0U;
#endif
/* request short address
* TODO: support operation with ext addr.
*/
cmd->assoc_req.ci.alloc_addr = 1U;
ieee802154_mac_cmd_finalize(pkt, IEEE802154_CFI_ASSOCIATION_REQUEST);
/* section 6.4.1, Association: Set phyCurrentPage [TODO: implement] and
* phyCurrentChannel to the requested channel and channel page
* parameters.
*/
if (ieee802154_radio_set_channel(iface, req->channel)) {
ret = -EIO;
goto release;
}
/* section 6.4.1, Association: Set macPanId to the coordinator's PAN ID. */
ieee802154_radio_remove_pan_id(iface, ctx->pan_id);
ctx->pan_id = req->pan_id;
ieee802154_radio_filter_pan_id(iface, req->pan_id);
/* section 6.4.1, Association: Set macCoordExtendedAddress or
* macCoordShortAddress, depending on which is known from the Beacon
* frame from the coordinator through which the device wishes to
* associate.
*/
if (req->len == IEEE802154_SHORT_ADDR_LENGTH) {
ctx->coord_short_addr = req->short_addr;
} else {
ctx->coord_short_addr = IEEE802154_NO_SHORT_ADDRESS_ASSIGNED;
sys_memcpy_swap(ctx->coord_ext_addr, req->addr, IEEE802154_EXT_ADDR_LENGTH);
}
k_sem_give(&ctx->ctx_lock);
/* Acquire the scan lock so that the next k_sem_take() blocks. */
k_sem_take(&ctx->scan_ctx_lock, K_FOREVER);
/* section 6.4.1, Association: The MAC sublayer of an unassociated device
* shall initiate the association procedure by sending an Association
* Request command, as described in 7.5.2, to the coordinator of an
* existing PAN.
*/
if (ieee802154_radio_send(iface, pkt, pkt->buffer)) {
net_pkt_unref(pkt);
ret = -EIO;
goto out;
}
/* Wait macResponseWaitTime PHY symbols for the association response, see
* ieee802154_handle_mac_command() and section 6.4.1.
*
* TODO: The Association Response command shall be sent to the device
* requesting association using indirect transmission.
*/
k_sem_take(&ctx->scan_ctx_lock, K_USEC(ieee802154_get_response_wait_time_us(iface)));
/* Release the scan lock in case an association response was not received
* within macResponseWaitTime and we got a timeout instead.
*/
k_sem_give(&ctx->scan_ctx_lock);
k_sem_take(&ctx->ctx_lock, K_FOREVER);
if (is_associated(ctx)) {
bool validated = false;
if (req->len == IEEE802154_SHORT_ADDR_LENGTH &&
ctx->coord_short_addr == req->short_addr) {
validated = true;
} else {
uint8_t ext_addr_le[IEEE802154_EXT_ADDR_LENGTH];
__ASSERT_NO_MSG(req->len == IEEE802154_EXT_ADDR_LENGTH);
sys_memcpy_swap(ext_addr_le, req->addr, sizeof(ext_addr_le));
if (!memcmp(ctx->coord_ext_addr, ext_addr_le, IEEE802154_EXT_ADDR_LENGTH)) {
validated = true;
}
}
if (!validated) {
ret = -EFAULT;
goto release;
}
ctx->channel = req->channel;
} else {
ret = -EACCES;
}
release:
k_sem_give(&ctx->ctx_lock);
out:
if (ret) {
k_sem_take(&ctx->ctx_lock, K_FOREVER);
remove_association(iface, ctx);
ieee802154_radio_set_channel(iface, ctx->channel);
k_sem_give(&ctx->ctx_lock);
}
return ret;
}
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_ASSOCIATE,
ieee802154_associate);
static int ieee802154_disassociate(uint32_t mgmt_request, struct net_if *iface,
void *data, size_t len)
{
struct ieee802154_context *ctx = net_if_l2_data(iface);
struct ieee802154_frame_params params = {0};
struct ieee802154_command *cmd;
struct net_pkt *pkt;
ARG_UNUSED(data);
ARG_UNUSED(len);
k_sem_take(&ctx->ctx_lock, K_FOREVER);
if (!is_associated(ctx)) {
k_sem_give(&ctx->ctx_lock);
return -EALREADY;
}
/* See section 7.5.4:
* * The Destination PAN ID field shall contain the value of macPanId.
* * If an associated device is disassociating from the PAN, then the
* Destination Address field shall contain the value of either
* macCoordShortAddress, if the Destination Addressing Mode field is
* set to indicated short addressing, or macCoordExtendedAddress, if
* the Destination Addressing Mode field is set to indicated extended
* addressing.
*/
params.dst.pan_id = ctx->pan_id;
if (ctx->coord_short_addr != IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED &&
ctx->coord_short_addr != IEEE802154_NO_SHORT_ADDRESS_ASSIGNED) {
params.dst.len = IEEE802154_SHORT_ADDR_LENGTH;
params.dst.short_addr = ctx->coord_short_addr;
} else {
params.dst.len = IEEE802154_EXT_ADDR_LENGTH;
sys_memcpy_swap(params.dst.ext_addr, ctx->coord_ext_addr,
sizeof(params.dst.ext_addr));
}
k_sem_give(&ctx->ctx_lock);
/* If an associated device wants to leave the PAN, the MLME of the device
* shall send a Disassociation Notification command to its coordinator.
*/
pkt = ieee802154_create_mac_cmd_frame(
iface, IEEE802154_CFI_DISASSOCIATION_NOTIFICATION, &params);
if (!pkt) {
return -ENOBUFS;
}
cmd = ieee802154_get_mac_command(pkt);
cmd->disassoc_note.reason = IEEE802154_DRF_DEVICE_WISH;
ieee802154_mac_cmd_finalize(
pkt, IEEE802154_CFI_DISASSOCIATION_NOTIFICATION);
if (ieee802154_radio_send(iface, pkt, pkt->buffer)) {
net_pkt_unref(pkt);
return -EIO;
}
k_sem_take(&ctx->ctx_lock, K_FOREVER);
remove_association(iface, ctx);
k_sem_give(&ctx->ctx_lock);
return 0;
}
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_DISASSOCIATE,
ieee802154_disassociate);
static int ieee802154_set_ack(uint32_t mgmt_request, struct net_if *iface,
void *data, size_t len)
{
struct ieee802154_context *ctx = net_if_l2_data(iface);
ARG_UNUSED(data);
ARG_UNUSED(len);
k_sem_take(&ctx->ctx_lock, K_FOREVER);
if (mgmt_request == NET_REQUEST_IEEE802154_SET_ACK) {
ctx->ack_requested = true;
} else if (mgmt_request == NET_REQUEST_IEEE802154_UNSET_ACK) {
ctx->ack_requested = false;
}
k_sem_give(&ctx->ctx_lock);
return 0;
}
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_ACK,
ieee802154_set_ack);
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_UNSET_ACK,
ieee802154_set_ack);
static int ieee802154_set_parameters(uint32_t mgmt_request,
struct net_if *iface,
void *data, size_t len)
{
struct ieee802154_context *ctx = net_if_l2_data(iface);
uint16_t value;
int ret = 0;
if (!data) {
return -EINVAL;
}
if (mgmt_request == NET_REQUEST_IEEE802154_SET_EXT_ADDR) {
if (len != IEEE802154_EXT_ADDR_LENGTH) {
return -EINVAL;
}
} else {
if (len != sizeof(uint16_t)) {
return -EINVAL;
}
}
value = *((uint16_t *) data);
k_sem_take(&ctx->ctx_lock, K_FOREVER);
if (is_associated(ctx) && !(mgmt_request == NET_REQUEST_IEEE802154_SET_SHORT_ADDR &&
value == IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED)) {
ret = -EBUSY;
goto out;
}
if (mgmt_request == NET_REQUEST_IEEE802154_SET_CHANNEL) {
if (ctx->channel != value) {
if (!ieee802154_radio_verify_channel(iface, value)) {
ret = -EINVAL;
goto out;
}
ret = ieee802154_radio_set_channel(iface, value);
if (!ret) {
ctx->channel = value;
}
}
} else if (mgmt_request == NET_REQUEST_IEEE802154_SET_PAN_ID) {
if (ctx->pan_id != value) {
ieee802154_radio_remove_pan_id(iface, ctx->pan_id);
ctx->pan_id = value;
ieee802154_radio_filter_pan_id(iface, ctx->pan_id);
}
} else if (mgmt_request == NET_REQUEST_IEEE802154_SET_EXT_ADDR) {
uint8_t ext_addr_le[IEEE802154_EXT_ADDR_LENGTH];
sys_memcpy_swap(ext_addr_le, data, IEEE802154_EXT_ADDR_LENGTH);
if (memcmp(ctx->ext_addr, ext_addr_le, IEEE802154_EXT_ADDR_LENGTH)) {
memcpy(ctx->ext_addr, ext_addr_le, IEEE802154_EXT_ADDR_LENGTH);
if (net_if_get_link_addr(iface)->len == IEEE802154_EXT_ADDR_LENGTH) {
set_linkaddr_to_ext_addr(iface, ctx);
}
ieee802154_radio_filter_ieee_addr(iface, ctx->ext_addr);
}
} else if (mgmt_request == NET_REQUEST_IEEE802154_SET_SHORT_ADDR) {
if (ctx->short_addr != value) {
if (value == IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED) {
remove_association(iface, ctx);
} else {
/* A PAN is required when associating,
* see section 8.4.3.1, table 8-94.
*/
if (ctx->pan_id == IEEE802154_PAN_ID_NOT_ASSOCIATED) {
ret = -EPERM;
goto out;
}
set_association(iface, ctx, value);
}
}
} else if (mgmt_request == NET_REQUEST_IEEE802154_SET_TX_POWER) {
if (ctx->tx_power != (int16_t)value) {
ret = ieee802154_radio_set_tx_power(iface, (int16_t)value);
if (!ret) {
ctx->tx_power = (int16_t)value;
}
}
}
out:
k_sem_give(&ctx->ctx_lock);
return ret;
}
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_CHANNEL,
ieee802154_set_parameters);
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_PAN_ID,
ieee802154_set_parameters);
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_EXT_ADDR,
ieee802154_set_parameters);
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_SHORT_ADDR,
ieee802154_set_parameters);
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_TX_POWER,
ieee802154_set_parameters);
static int ieee802154_get_parameters(uint32_t mgmt_request,
struct net_if *iface,
void *data, size_t len)
{
struct ieee802154_context *ctx = net_if_l2_data(iface);
uint16_t *value;
int ret = 0;
if (!data) {
return -EINVAL;
}
if (mgmt_request == NET_REQUEST_IEEE802154_GET_EXT_ADDR) {
if (len != IEEE802154_EXT_ADDR_LENGTH) {
return -EINVAL;
}
} else {
if (len != sizeof(uint16_t)) {
return -EINVAL;
}
}
value = (uint16_t *)data;
k_sem_take(&ctx->ctx_lock, K_FOREVER);
if (mgmt_request == NET_REQUEST_IEEE802154_GET_CHANNEL) {
*value = ctx->channel;
} else if (mgmt_request == NET_REQUEST_IEEE802154_GET_PAN_ID) {
*value = ctx->pan_id;
} else if (mgmt_request == NET_REQUEST_IEEE802154_GET_EXT_ADDR) {
sys_memcpy_swap(data, ctx->ext_addr, IEEE802154_EXT_ADDR_LENGTH);
} else if (mgmt_request == NET_REQUEST_IEEE802154_GET_SHORT_ADDR) {
*value = ctx->short_addr;
} else if (mgmt_request == NET_REQUEST_IEEE802154_GET_TX_POWER) {
int16_t *s_value = (int16_t *)data;
*s_value = ctx->tx_power;
}
k_sem_give(&ctx->ctx_lock);
return ret;
}
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_GET_CHANNEL,
ieee802154_get_parameters);
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_GET_PAN_ID,
ieee802154_get_parameters);
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_GET_EXT_ADDR,
ieee802154_get_parameters);
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_GET_SHORT_ADDR,
ieee802154_get_parameters);
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_GET_TX_POWER,
ieee802154_get_parameters);
#ifdef CONFIG_NET_L2_IEEE802154_SECURITY
static int ieee802154_set_security_settings(uint32_t mgmt_request,
struct net_if *iface,
void *data, size_t len)
{
struct ieee802154_context *ctx = net_if_l2_data(iface);
struct ieee802154_security_params *params;
int ret = 0;
if (len != sizeof(struct ieee802154_security_params) || !data) {
return -EINVAL;
}
params = (struct ieee802154_security_params *)data;
k_sem_take(&ctx->ctx_lock, K_FOREVER);
if (is_associated(ctx)) {
ret = -EBUSY;
goto out;
}
ieee802154_security_teardown_session(&ctx->sec_ctx);
if (ieee802154_security_setup_session(&ctx->sec_ctx, params->level,
params->key_mode, params->key,
params->key_len)) {
NET_ERR("Could not set the security parameters");
ret = -EINVAL;
}
out:
k_sem_give(&ctx->ctx_lock);
return ret;
}
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_SECURITY_SETTINGS,
ieee802154_set_security_settings);
static int ieee802154_get_security_settings(uint32_t mgmt_request,
struct net_if *iface,
void *data, size_t len)
{
struct ieee802154_context *ctx = net_if_l2_data(iface);
struct ieee802154_security_params *params;
if (len != sizeof(struct ieee802154_security_params) || !data) {
return -EINVAL;
}
params = (struct ieee802154_security_params *)data;
k_sem_take(&ctx->ctx_lock, K_FOREVER);
memcpy(params->key, ctx->sec_ctx.key, ctx->sec_ctx.key_len);
params->key_len = ctx->sec_ctx.key_len;
params->key_mode = ctx->sec_ctx.key_mode;
params->level = ctx->sec_ctx.level;
k_sem_give(&ctx->ctx_lock);
return 0;
}
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_GET_SECURITY_SETTINGS,
ieee802154_get_security_settings);
#endif /* CONFIG_NET_L2_IEEE802154_SECURITY */