| /* |
| * Copyright (c) 2016 Intel Corporation. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** |
| * @file |
| * @brief IEEE 802.15.4 MAC layer implementation |
| * |
| * All references to the spec refer to IEEE 802.15.4-2020. |
| */ |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(net_ieee802154, CONFIG_NET_L2_IEEE802154_LOG_LEVEL); |
| |
| #include <errno.h> |
| |
| #include <zephyr/net/capture.h> |
| #include <zephyr/net/ethernet.h> |
| #include <zephyr/net/net_core.h> |
| #include <zephyr/net/net_if.h> |
| #include <zephyr/net/net_l2.h> |
| #include <zephyr/net/net_linkaddr.h> |
| #include <zephyr/random/random.h> |
| |
| #ifdef CONFIG_NET_6LO |
| #include "ieee802154_6lo.h" |
| |
| #include <6lo.h> |
| #include <ipv6.h> |
| |
| #ifdef CONFIG_NET_L2_IEEE802154_FRAGMENT |
| #include "ieee802154_6lo_fragment.h" |
| #endif /* CONFIG_NET_L2_IEEE802154_FRAGMENT */ |
| #endif /* CONFIG_NET_6LO */ |
| |
| #include "ieee802154_frame.h" |
| #include "ieee802154_mgmt_priv.h" |
| #include "ieee802154_priv.h" |
| #include "ieee802154_security.h" |
| #include "ieee802154_utils.h" |
| |
| #define BUF_TIMEOUT K_MSEC(50) |
| |
| NET_BUF_POOL_DEFINE(tx_frame_buf_pool, 1, IEEE802154_MTU, 8, NULL); |
| |
| #define PKT_TITLE "IEEE 802.15.4 packet content:" |
| #define TX_PKT_TITLE "> " PKT_TITLE |
| #define RX_PKT_TITLE "< " PKT_TITLE |
| |
| #ifdef CONFIG_NET_DEBUG_L2_IEEE802154_DISPLAY_PACKET |
| |
| #include "net_private.h" |
| |
| static inline void pkt_hexdump(const char *title, struct net_pkt *pkt, bool in) |
| { |
| if (IS_ENABLED(CONFIG_NET_DEBUG_L2_IEEE802154_DISPLAY_PACKET_RX) && in) { |
| net_pkt_hexdump(pkt, title); |
| } |
| |
| if (IS_ENABLED(CONFIG_NET_DEBUG_L2_IEEE802154_DISPLAY_PACKET_TX) && !in) { |
| net_pkt_hexdump(pkt, title); |
| } |
| } |
| |
| #else |
| #define pkt_hexdump(...) |
| #endif /* CONFIG_NET_DEBUG_L2_IEEE802154_DISPLAY_PACKET */ |
| |
| static inline void ieee802154_acknowledge(struct net_if *iface, struct ieee802154_mpdu *mpdu) |
| { |
| struct net_pkt *pkt; |
| |
| if (ieee802154_radio_get_hw_capabilities(iface) & IEEE802154_HW_RX_TX_ACK) { |
| return; |
| } |
| |
| if (!mpdu->mhr.fs->fc.ar) { |
| return; |
| } |
| |
| pkt = net_pkt_alloc_with_buffer(iface, IEEE802154_ACK_PKT_LENGTH, AF_UNSPEC, 0, |
| BUF_TIMEOUT); |
| if (!pkt) { |
| return; |
| } |
| |
| if (ieee802154_create_ack_frame(iface, pkt, mpdu->mhr.fs->sequence)) { |
| /* ACK frames must not use the CSMA/CA procedure, see section 6.2.5.1. */ |
| ieee802154_radio_tx(iface, IEEE802154_TX_MODE_DIRECT, pkt, pkt->buffer); |
| } |
| |
| net_pkt_unref(pkt); |
| |
| return; |
| } |
| |
| inline bool ieee802154_prepare_for_ack(struct net_if *iface, struct net_pkt *pkt, |
| struct net_buf *frag) |
| { |
| bool ack_required = ieee802154_is_ar_flag_set(frag); |
| |
| if (ieee802154_radio_get_hw_capabilities(iface) & IEEE802154_HW_TX_RX_ACK) { |
| return ack_required; |
| } |
| |
| if (ack_required) { |
| struct ieee802154_fcf_seq *fs = (struct ieee802154_fcf_seq *)frag->data; |
| struct ieee802154_context *ctx = net_if_l2_data(iface); |
| |
| ctx->ack_seq = fs->sequence; |
| if (k_sem_count_get(&ctx->ack_lock) == 1U) { |
| k_sem_take(&ctx->ack_lock, K_NO_WAIT); |
| } |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| enum net_verdict ieee802154_handle_ack(struct net_if *iface, struct net_pkt *pkt) |
| { |
| struct ieee802154_context *ctx = net_if_l2_data(iface); |
| |
| if (ieee802154_radio_get_hw_capabilities(iface) & IEEE802154_HW_TX_RX_ACK) { |
| __ASSERT_NO_MSG(ctx->ack_seq == 0U); |
| /* TODO: Release packet in L2 as we're taking ownership. */ |
| return NET_OK; |
| } |
| |
| if (pkt->buffer->len == IEEE802154_ACK_PKT_LENGTH) { |
| uint8_t len = IEEE802154_ACK_PKT_LENGTH; |
| struct ieee802154_fcf_seq *fs; |
| |
| fs = ieee802154_validate_fc_seq(net_pkt_data(pkt), NULL, &len); |
| if (!fs || fs->fc.frame_type != IEEE802154_FRAME_TYPE_ACK || |
| fs->sequence != ctx->ack_seq) { |
| return NET_CONTINUE; |
| } |
| |
| k_sem_give(&ctx->ack_lock); |
| |
| /* TODO: Release packet in L2 as we're taking ownership. */ |
| return NET_OK; |
| } |
| |
| return NET_CONTINUE; |
| } |
| |
| inline int ieee802154_wait_for_ack(struct net_if *iface, bool ack_required) |
| { |
| struct ieee802154_context *ctx = net_if_l2_data(iface); |
| int ret; |
| |
| if (!ack_required || |
| (ieee802154_radio_get_hw_capabilities(iface) & IEEE802154_HW_TX_RX_ACK)) { |
| __ASSERT_NO_MSG(ctx->ack_seq == 0U); |
| return 0; |
| } |
| |
| ret = k_sem_take(&ctx->ack_lock, K_MSEC(10)); |
| if (ret == 0) { |
| /* no-op */ |
| } else if (ret == -EAGAIN) { |
| ret = -ETIME; |
| } else { |
| NET_ERR("Error while waiting for ACK."); |
| ret = -EFAULT; |
| } |
| |
| ctx->ack_seq = 0U; |
| return ret; |
| } |
| |
| int ieee802154_radio_send(struct net_if *iface, struct net_pkt *pkt, struct net_buf *frag) |
| { |
| uint8_t remaining_attempts = CONFIG_NET_L2_IEEE802154_RADIO_TX_RETRIES + 1; |
| bool hw_csma, ack_required; |
| int ret; |
| |
| NET_DBG("frag %p", frag); |
| |
| if (ieee802154_radio_get_hw_capabilities(iface) & IEEE802154_HW_RETRANSMISSION) { |
| /* A driver that claims retransmission capability must also be able |
| * to wait for ACK frames otherwise it could not decide whether or |
| * not retransmission is required in a standard conforming way. |
| */ |
| __ASSERT_NO_MSG(ieee802154_radio_get_hw_capabilities(iface) & |
| IEEE802154_HW_TX_RX_ACK); |
| remaining_attempts = 1; |
| } |
| |
| hw_csma = IS_ENABLED(CONFIG_NET_L2_IEEE802154_RADIO_CSMA_CA) && |
| ieee802154_radio_get_hw_capabilities(iface) & IEEE802154_HW_CSMA; |
| |
| /* Media access (CSMA, ALOHA, ...) and retransmission, see section 6.7.4.4. */ |
| while (remaining_attempts) { |
| if (!hw_csma) { |
| ret = ieee802154_wait_for_clear_channel(iface); |
| if (ret != 0) { |
| NET_WARN("Clear channel assessment failed: dropping fragment %p on " |
| "interface %p.", |
| frag, iface); |
| return ret; |
| } |
| } |
| |
| /* No-op in case the driver has IEEE802154_HW_TX_RX_ACK capability. */ |
| ack_required = ieee802154_prepare_for_ack(iface, pkt, frag); |
| |
| /* TX including: |
| * - CSMA/CA in case the driver has IEEE802154_HW_CSMA capability, |
| * - waiting for ACK in case the driver has IEEE802154_HW_TX_RX_ACK capability, |
| * - retransmission on ACK timeout in case the driver has |
| * IEEE802154_HW_RETRANSMISSION capability. |
| */ |
| ret = ieee802154_radio_tx( |
| iface, hw_csma ? IEEE802154_TX_MODE_CSMA_CA : IEEE802154_TX_MODE_DIRECT, |
| pkt, frag); |
| if (ret) { |
| /* Transmission failure. */ |
| return ret; |
| } |
| |
| if (!ack_required) { |
| /* See section 6.7.4.4: "A device that sends a frame with the AR field set |
| * to indicate no acknowledgment requested may assume that the transmission |
| * was successfully received and shall not perform the retransmission |
| * procedure." |
| */ |
| return 0; |
| } |
| |
| |
| /* No-op in case the driver has IEEE802154_HW_TX_RX_ACK capability. */ |
| ret = ieee802154_wait_for_ack(iface, ack_required); |
| if (ret == 0) { |
| /* ACK received - transmission is successful. */ |
| return 0; |
| } |
| |
| remaining_attempts--; |
| } |
| |
| return -EIO; |
| } |
| |
| static inline void swap_and_set_pkt_ll_addr(struct net_linkaddr *addr, bool has_pan_id, |
| enum ieee802154_addressing_mode mode, |
| struct ieee802154_address_field *ll) |
| { |
| addr->type = NET_LINK_IEEE802154; |
| |
| switch (mode) { |
| case IEEE802154_ADDR_MODE_EXTENDED: |
| addr->len = IEEE802154_EXT_ADDR_LENGTH; |
| addr->addr = has_pan_id ? ll->plain.addr.ext_addr : ll->comp.addr.ext_addr; |
| break; |
| |
| case IEEE802154_ADDR_MODE_SHORT: |
| addr->len = IEEE802154_SHORT_ADDR_LENGTH; |
| addr->addr = (uint8_t *)(has_pan_id ? &ll->plain.addr.short_addr |
| : &ll->comp.addr.short_addr); |
| break; |
| |
| case IEEE802154_ADDR_MODE_NONE: |
| default: |
| addr->len = 0U; |
| addr->addr = NULL; |
| } |
| |
| /* The net stack expects link layer addresses to be in |
| * big endian format for posix compliance so we must swap it. |
| * This is ok as the L2 address field comes from the header |
| * part of the packet buffer which will not be directly accessible |
| * once the packet reaches the upper layers. |
| */ |
| if (addr->len > 0) { |
| sys_mem_swap(addr->addr, addr->len); |
| } |
| } |
| |
| /** |
| * Filters the destination address of the frame. |
| * |
| * This is done before deciphering and authenticating encrypted frames. |
| */ |
| static bool ieeee802154_check_dst_addr(struct net_if *iface, struct ieee802154_mhr *mhr) |
| { |
| struct ieee802154_address_field_plain *dst_plain = &mhr->dst_addr->plain; |
| struct ieee802154_context *ctx = net_if_l2_data(iface); |
| bool ret = false; |
| |
| /* Apply filtering requirements from section 6.7.2 c)-e). For a)-b), |
| * see ieee802154_parse_fcf_seq() |
| */ |
| |
| if (mhr->fs->fc.dst_addr_mode == IEEE802154_ADDR_MODE_NONE) { |
| if (mhr->fs->fc.frame_version < IEEE802154_VERSION_802154 && |
| mhr->fs->fc.frame_type == IEEE802154_FRAME_TYPE_BEACON) { |
| /* See IEEE 802.15.4-2015, section 7.3.1.1. */ |
| return true; |
| } |
| |
| /* TODO: apply d.4 and d.5 when PAN coordinator is implemented */ |
| /* also, macImplicitBroadcast is not implemented */ |
| return false; |
| } |
| |
| k_sem_take(&ctx->ctx_lock, K_FOREVER); |
| |
| /* c) If a destination PAN ID is included in the frame, it shall match |
| * macPanId or shall be the broadcast PAN ID. |
| */ |
| if (!(dst_plain->pan_id == IEEE802154_BROADCAST_PAN_ID || |
| dst_plain->pan_id == sys_cpu_to_le16(ctx->pan_id))) { |
| LOG_DBG("Frame PAN ID does not match!"); |
| return false; |
| } |
| |
| if (mhr->fs->fc.dst_addr_mode == IEEE802154_ADDR_MODE_SHORT) { |
| /* d.1) A short destination address is included in the frame, |
| * and it matches either macShortAddress or the broadcast |
| * address. |
| */ |
| if (!(dst_plain->addr.short_addr == IEEE802154_BROADCAST_ADDRESS || |
| dst_plain->addr.short_addr == sys_cpu_to_le16(ctx->short_addr))) { |
| LOG_DBG("Frame dst address (short) does not match!"); |
| goto out; |
| } |
| |
| } else if (mhr->fs->fc.dst_addr_mode == IEEE802154_ADDR_MODE_EXTENDED) { |
| /* d.2) An extended destination address is included in the frame and |
| * matches [...] macExtendedAddress [...]. |
| */ |
| if (memcmp(dst_plain->addr.ext_addr, ctx->ext_addr, |
| IEEE802154_EXT_ADDR_LENGTH) != 0) { |
| LOG_DBG("Frame dst address (ext) does not match!"); |
| goto out; |
| } |
| |
| /* TODO: d.3) The Destination Address field and the Destination PAN ID |
| * field are not included in the frame and macImplicitBroadcast is TRUE. |
| */ |
| |
| /* TODO: d.4) The device is the PAN coordinator, only source addressing fields |
| * are included in a Data frame or MAC command and the source PAN ID |
| * matches macPanId. |
| */ |
| } |
| ret = true; |
| |
| out: |
| k_sem_give(&ctx->ctx_lock); |
| return ret; |
| } |
| |
| static enum net_verdict ieee802154_recv(struct net_if *iface, struct net_pkt *pkt) |
| { |
| const struct ieee802154_radio_api *radio = net_if_get_device(iface)->api; |
| enum net_verdict verdict = NET_CONTINUE; |
| struct ieee802154_fcf_seq *fs; |
| struct ieee802154_mpdu mpdu; |
| bool is_broadcast; |
| size_t ll_hdr_len; |
| |
| /* The IEEE 802.15.4 stack assumes that drivers provide a single-fragment package. */ |
| __ASSERT_NO_MSG(pkt->buffer && pkt->buffer->frags == NULL); |
| |
| if (!ieee802154_validate_frame(net_pkt_data(pkt), net_pkt_get_len(pkt), &mpdu)) { |
| return NET_DROP; |
| } |
| |
| /* validate LL destination address (when IEEE802154_HW_FILTER not available) */ |
| if (!(radio->get_capabilities(net_if_get_device(iface)) & IEEE802154_HW_FILTER) && |
| !ieeee802154_check_dst_addr(iface, &mpdu.mhr)) { |
| return NET_DROP; |
| } |
| |
| fs = mpdu.mhr.fs; |
| |
| if (fs->fc.frame_type == IEEE802154_FRAME_TYPE_ACK) { |
| return NET_DROP; |
| } |
| |
| if (fs->fc.frame_type == IEEE802154_FRAME_TYPE_BEACON) { |
| verdict = ieee802154_handle_beacon(iface, &mpdu, net_pkt_ieee802154_lqi(pkt)); |
| if (verdict == NET_CONTINUE) { |
| net_pkt_unref(pkt); |
| return NET_OK; |
| } |
| /* Beacons must not be acknowledged, see section 6.7.4.1. */ |
| return verdict; |
| } |
| |
| if (ieee802154_is_scanning(iface)) { |
| return NET_DROP; |
| } |
| |
| if (fs->fc.frame_type == IEEE802154_FRAME_TYPE_MAC_COMMAND) { |
| verdict = ieee802154_handle_mac_command(iface, &mpdu); |
| if (verdict == NET_DROP) { |
| return verdict; |
| } |
| } |
| |
| /* At this point the frame is either a MAC command or a data frame |
| * which may have to be acknowledged, see section 6.7.4.1. |
| */ |
| |
| is_broadcast = false; |
| |
| if (fs->fc.dst_addr_mode == IEEE802154_ADDR_MODE_SHORT) { |
| struct ieee802154_address_field *dst_addr = mpdu.mhr.dst_addr; |
| uint16_t short_dst_addr; |
| |
| short_dst_addr = fs->fc.pan_id_comp ? dst_addr->comp.addr.short_addr |
| : dst_addr->plain.addr.short_addr; |
| is_broadcast = short_dst_addr == IEEE802154_BROADCAST_ADDRESS; |
| } |
| |
| /* Frames that are broadcast must not be acknowledged, see section 6.7.2. */ |
| if (!is_broadcast) { |
| ieee802154_acknowledge(iface, &mpdu); |
| } |
| |
| if (fs->fc.frame_type == IEEE802154_FRAME_TYPE_MAC_COMMAND) { |
| net_pkt_unref(pkt); |
| return NET_OK; |
| } |
| |
| if (!ieee802154_decipher_data_frame(iface, pkt, &mpdu)) { |
| return NET_DROP; |
| } |
| |
| /* Setting L2 addresses must be done after packet authentication and internal |
| * packet handling as it will mangle the package header to comply with upper |
| * network layers' (POSIX) requirement to represent network addresses in big endian. |
| */ |
| swap_and_set_pkt_ll_addr(net_pkt_lladdr_src(pkt), !fs->fc.pan_id_comp, |
| fs->fc.src_addr_mode, mpdu.mhr.src_addr); |
| |
| swap_and_set_pkt_ll_addr(net_pkt_lladdr_dst(pkt), true, fs->fc.dst_addr_mode, |
| mpdu.mhr.dst_addr); |
| |
| net_pkt_set_ll_proto_type(pkt, ETH_P_IEEE802154); |
| |
| pkt_hexdump(RX_PKT_TITLE " (with ll)", pkt, true); |
| |
| ll_hdr_len = (uint8_t *)mpdu.payload - net_pkt_data(pkt); |
| net_buf_pull(pkt->buffer, ll_hdr_len); |
| |
| #ifdef CONFIG_NET_6LO |
| verdict = ieee802154_6lo_decode_pkt(iface, pkt); |
| #endif /* CONFIG_NET_6LO */ |
| |
| if (verdict == NET_CONTINUE) { |
| pkt_hexdump(RX_PKT_TITLE, pkt, true); |
| } |
| |
| return verdict; |
| |
| /* At this point the call amounts to (part of) an |
| * MCPS-DATA.indication primitive, see section 8.3.3. |
| */ |
| } |
| |
| /** |
| * Implements (part of) the MCPS-DATA.request/confirm primitives, see sections 8.3.2/3. |
| */ |
| static int ieee802154_send(struct net_if *iface, struct net_pkt *pkt) |
| { |
| struct ieee802154_context *ctx = net_if_l2_data(iface); |
| uint8_t ll_hdr_len = 0, authtag_len = 0; |
| static struct net_buf *frame_buf; |
| static struct net_buf *pkt_buf; |
| bool send_raw = false; |
| int len; |
| #ifdef CONFIG_NET_L2_IEEE802154_FRAGMENT |
| struct ieee802154_6lo_fragment_ctx frag_ctx; |
| int requires_fragmentation = 0; |
| #endif |
| |
| if (frame_buf == NULL) { |
| frame_buf = net_buf_alloc(&tx_frame_buf_pool, K_FOREVER); |
| } |
| |
| if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) && net_pkt_family(pkt) == AF_PACKET) { |
| enum net_sock_type socket_type; |
| struct net_context *context; |
| |
| context = net_pkt_context(pkt); |
| if (!context) { |
| return -EINVAL; |
| } |
| |
| socket_type = net_context_get_type(context); |
| if (socket_type == SOCK_RAW) { |
| send_raw = true; |
| } else if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET_DGRAM) && |
| socket_type == SOCK_DGRAM) { |
| struct sockaddr_ll *dst_addr = (struct sockaddr_ll *)&context->remote; |
| struct sockaddr_ll_ptr *src_addr = |
| (struct sockaddr_ll_ptr *)&context->local; |
| |
| net_pkt_lladdr_dst(pkt)->addr = dst_addr->sll_addr; |
| net_pkt_lladdr_dst(pkt)->len = dst_addr->sll_halen; |
| net_pkt_lladdr_src(pkt)->addr = src_addr->sll_addr; |
| net_pkt_lladdr_src(pkt)->len = src_addr->sll_halen; |
| } else { |
| return -EINVAL; |
| } |
| } |
| |
| if (!send_raw) { |
| ieee802154_compute_header_and_authtag_len(iface, net_pkt_lladdr_dst(pkt), |
| net_pkt_lladdr_src(pkt), &ll_hdr_len, |
| &authtag_len); |
| |
| #ifdef CONFIG_NET_6LO |
| #ifdef CONFIG_NET_L2_IEEE802154_FRAGMENT |
| requires_fragmentation = |
| ieee802154_6lo_encode_pkt(iface, pkt, &frag_ctx, ll_hdr_len, authtag_len); |
| if (requires_fragmentation < 0) { |
| return requires_fragmentation; |
| } |
| #else |
| ieee802154_6lo_encode_pkt(iface, pkt, NULL, ll_hdr_len, authtag_len); |
| #endif /* CONFIG_NET_L2_IEEE802154_FRAGMENT */ |
| #endif /* CONFIG_NET_6LO */ |
| } |
| |
| net_capture_pkt(iface, pkt); |
| |
| len = 0; |
| pkt_buf = pkt->buffer; |
| while (pkt_buf) { |
| int ret; |
| |
| /* Reinitializing frame_buf */ |
| net_buf_reset(frame_buf); |
| net_buf_add(frame_buf, ll_hdr_len); |
| |
| #ifdef CONFIG_NET_L2_IEEE802154_FRAGMENT |
| if (requires_fragmentation) { |
| pkt_buf = ieee802154_6lo_fragment(&frag_ctx, frame_buf, true); |
| } else { |
| net_buf_add_mem(frame_buf, pkt_buf->data, pkt_buf->len); |
| pkt_buf = pkt_buf->frags; |
| } |
| #else |
| if (ll_hdr_len + pkt_buf->len + authtag_len > IEEE802154_MTU) { |
| NET_ERR("Frame too long: %d", pkt_buf->len); |
| return -EINVAL; |
| } |
| net_buf_add_mem(frame_buf, pkt_buf->data, pkt_buf->len); |
| pkt_buf = pkt_buf->frags; |
| #endif /* CONFIG_NET_L2_IEEE802154_FRAGMENT */ |
| |
| __ASSERT_NO_MSG(authtag_len <= net_buf_tailroom(frame_buf)); |
| net_buf_add(frame_buf, authtag_len); |
| |
| if (!(send_raw || ieee802154_create_data_frame(ctx, net_pkt_lladdr_dst(pkt), |
| net_pkt_lladdr_src(pkt), |
| frame_buf, ll_hdr_len))) { |
| return -EINVAL; |
| } |
| |
| ret = ieee802154_radio_send(iface, pkt, frame_buf); |
| if (ret) { |
| return ret; |
| } |
| |
| len += frame_buf->len; |
| } |
| |
| net_pkt_unref(pkt); |
| |
| return len; |
| } |
| |
| static int ieee802154_enable(struct net_if *iface, bool state) |
| { |
| struct ieee802154_context *ctx = net_if_l2_data(iface); |
| |
| NET_DBG("iface %p %s", iface, state ? "up" : "down"); |
| |
| k_sem_take(&ctx->ctx_lock, K_FOREVER); |
| |
| if (ctx->channel == IEEE802154_NO_CHANNEL) { |
| k_sem_give(&ctx->ctx_lock); |
| return -ENETDOWN; |
| } |
| |
| k_sem_give(&ctx->ctx_lock); |
| |
| if (state) { |
| return ieee802154_radio_start(iface); |
| } |
| |
| return ieee802154_radio_stop(iface); |
| } |
| |
| static enum net_l2_flags ieee802154_flags(struct net_if *iface) |
| { |
| struct ieee802154_context *ctx = net_if_l2_data(iface); |
| |
| /* No need for locking as these flags are set once |
| * during L2 initialization and then never changed. |
| */ |
| return ctx->flags; |
| } |
| |
| NET_L2_INIT(IEEE802154_L2, ieee802154_recv, ieee802154_send, ieee802154_enable, ieee802154_flags); |
| |
| void ieee802154_init(struct net_if *iface) |
| { |
| struct ieee802154_context *ctx = net_if_l2_data(iface); |
| const uint8_t *eui64_be = net_if_get_link_addr(iface)->addr; |
| int16_t tx_power = CONFIG_NET_L2_IEEE802154_RADIO_DFLT_TX_POWER; |
| |
| NET_DBG("Initializing IEEE 802.15.4 stack on iface %p", iface); |
| |
| k_sem_init(&ctx->ctx_lock, 1, 1); |
| k_sem_init(&ctx->ack_lock, 0, 1); |
| |
| /* no need to lock the context here as it has |
| * not been published yet. |
| */ |
| |
| /* See section 6.7.1 - Transmission: "Each device shall initialize its data sequence number |
| * (DSN) to a random value and store its current DSN value in the MAC PIB attribute macDsn |
| * [...]." |
| */ |
| ctx->sequence = sys_rand32_get() & 0xFF; |
| |
| ctx->channel = IEEE802154_NO_CHANNEL; |
| ctx->flags = NET_L2_MULTICAST; |
| if (ieee802154_radio_get_hw_capabilities(iface) & IEEE802154_HW_PROMISC) { |
| ctx->flags |= NET_L2_PROMISC_MODE; |
| } |
| |
| ctx->pan_id = IEEE802154_PAN_ID_NOT_ASSOCIATED; |
| ctx->short_addr = IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED; |
| ctx->coord_short_addr = IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED; |
| sys_memcpy_swap(ctx->ext_addr, eui64_be, IEEE802154_EXT_ADDR_LENGTH); |
| |
| /* We switch to a link address store that we |
| * own so that we can write user defined short |
| * or extended addresses w/o mutating internal |
| * driver storage. |
| */ |
| ctx->linkaddr.type = NET_LINK_IEEE802154; |
| ctx->linkaddr.len = IEEE802154_EXT_ADDR_LENGTH; |
| memcpy(ctx->linkaddr.addr, eui64_be, IEEE802154_EXT_ADDR_LENGTH); |
| net_if_set_link_addr(iface, ctx->linkaddr.addr, ctx->linkaddr.len, ctx->linkaddr.type); |
| |
| if (IS_ENABLED(CONFIG_IEEE802154_NET_IF_NO_AUTO_START) || |
| IS_ENABLED(CONFIG_NET_CONFIG_SETTINGS)) { |
| LOG_DBG("Interface auto start disabled."); |
| net_if_flag_set(iface, NET_IF_NO_AUTO_START); |
| } |
| |
| ieee802154_mgmt_init(iface); |
| |
| #ifdef CONFIG_NET_L2_IEEE802154_SECURITY |
| if (ieee802154_security_init(&ctx->sec_ctx)) { |
| NET_ERR("Initializing link-layer security failed"); |
| } |
| #endif |
| |
| sys_memcpy_swap(ctx->ext_addr, eui64_be, IEEE802154_EXT_ADDR_LENGTH); |
| ieee802154_radio_filter_ieee_addr(iface, ctx->ext_addr); |
| |
| if (!ieee802154_radio_set_tx_power(iface, tx_power)) { |
| ctx->tx_power = tx_power; |
| } |
| } |