/* * 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 LOG_MODULE_REGISTER(net_ieee802154_mgmt, CONFIG_NET_L2_IEEE802154_LOG_LEVEL); #include #include #include #include #include #include #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); int beacon_hdr_len; NET_DBG("Beacon received"); if (!ctx->scan_ctx) { return NET_DROP; } ctx->scan_ctx->association_permitted = mpdu->beacon->sf.association; 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); } beacon_hdr_len = ieee802514_beacon_header_length(mpdu->payload, mpdu->payload_length); ctx->scan_ctx->beacon_payload_len = mpdu->payload_length - beacon_hdr_len; ctx->scan_ctx->beacon_payload = (uint8_t *)mpdu->payload + beacon_hdr_len; 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, ¶ms); if (!pkt) { k_sem_give(&ctx->scan_ctx_lock); NET_ERR("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_ERR("Scan request failed: could not start device"); ret = -EIO; goto out; } if (ieee802154_radio_attr_get(iface, IEEE802154_ATTR_PHY_SUPPORTED_CHANNEL_RANGES, &attr_value)) { NET_ERR("Scan request failed: 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_ERR("Scan request failed: 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_WARN("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) { NET_ERR("Could not associate: invalid request"); return -EINVAL; } req = (struct ieee802154_req_params *)data; /* Validate the coordinator's PAN ID. */ if (req->pan_id == IEEE802154_PAN_ID_NOT_ASSOCIATED) { NET_ERR("Could not associate: PAN ID is special value '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) { NET_ERR("Could not associate: invalid short address ('not associated' or " "'not 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 { NET_ERR("Could not associate: invalid address type"); 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); NET_WARN("Could not associate: already associated"); return -EALREADY; } k_sem_give(&ctx->ctx_lock); pkt = ieee802154_create_mac_cmd_frame( iface, IEEE802154_CFI_ASSOCIATION_REQUEST, ¶ms); if (!pkt) { ret = -ENOBUFS; NET_ERR("Could not associate: cannot allocate association request frame"); 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; NET_ERR("Could not associate: cannot set channel %d", req->channel); 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; k_sem_give(&ctx->scan_ctx_lock); NET_ERR("Could not associate: cannot send association request"); 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; NET_ERR("Could not associate: invalid address assigned by coordinator"); 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); NET_WARN("Could not disassociate: not associated"); 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, ¶ms); if (!pkt) { NET_ERR("Could not disassociate: cannot allocate disassociation notification " "frame"); 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); NET_ERR("Could not disassociate: cannot send disassociation notification"); 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; NET_ERR("Could not set parameter: already associated to a PAN"); goto out; } if (mgmt_request == NET_REQUEST_IEEE802154_SET_CHANNEL) { if (ctx->channel != value) { if (!ieee802154_radio_verify_channel(iface, value)) { ret = -EINVAL; NET_ERR("Could not set channel: channel %d not supported by " "driver", value); goto out; } ret = ieee802154_radio_set_channel(iface, value); if (ret) { NET_ERR("Could not set channel: driver error (%d)", ret); } else { 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; NET_ERR("Could not set short address: not yet associated " "to a PAN"); 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) { NET_ERR("Could not set TX power (%d dB)", value); } else { 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) { NET_ERR("Could not get parameter: invalid extended address length"); return -EINVAL; } } else { if (len != sizeof(uint16_t)) { NET_ERR("Could not get parameter: invalid short address length"); 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; NET_ERR("Could not set security parameters: already associated to a PAN"); 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 security parameters: invalid 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 */