/* * Copyright (c) 2017 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * @brief IEEE 802.15.4 internal MAC and PHY Utils * * All references to the standard in this file cite IEEE 802.15.4-2020. */ #ifndef __IEEE802154_UTILS_H__ #define __IEEE802154_UTILS_H__ #include #include /** * PHY utilities */ static inline enum ieee802154_hw_caps ieee802154_radio_get_hw_capabilities(struct net_if *iface) { const struct ieee802154_radio_api *radio = net_if_get_device(iface)->api; if (!radio) { return 0; } return radio->get_capabilities(net_if_get_device(iface)); } static inline int ieee802154_radio_cca(struct net_if *iface) { const struct ieee802154_radio_api *radio = net_if_get_device(iface)->api; if (!radio) { return -ENOENT; } return radio->cca(net_if_get_device(iface)); } static inline int ieee802154_radio_set_channel(struct net_if *iface, uint16_t channel) { const struct ieee802154_radio_api *radio = net_if_get_device(iface)->api; if (!radio) { return -ENOENT; } return radio->set_channel(net_if_get_device(iface), channel); } static inline int ieee802154_radio_set_tx_power(struct net_if *iface, int16_t dbm) { const struct ieee802154_radio_api *radio = net_if_get_device(iface)->api; if (!radio) { return -ENOENT; } return radio->set_txpower(net_if_get_device(iface), dbm); } static inline int ieee802154_radio_tx(struct net_if *iface, enum ieee802154_tx_mode mode, struct net_pkt *pkt, struct net_buf *buf) { const struct ieee802154_radio_api *radio = net_if_get_device(iface)->api; if (!radio) { return -ENOENT; } return radio->tx(net_if_get_device(iface), mode, pkt, buf); } static inline int ieee802154_radio_start(struct net_if *iface) { const struct ieee802154_radio_api *radio = net_if_get_device(iface)->api; if (!radio) { return -ENOENT; } return radio->start(net_if_get_device(iface)); } static inline int ieee802154_radio_stop(struct net_if *iface) { const struct ieee802154_radio_api *radio = net_if_get_device(iface)->api; if (!radio) { return -ENOENT; } return radio->stop(net_if_get_device(iface)); } static inline int ieee802154_radio_attr_get(struct net_if *iface, enum ieee802154_attr attr, struct ieee802154_attr_value *value) { const struct ieee802154_radio_api *radio = net_if_get_device(iface)->api; if (!radio || !radio->attr_get) { return -ENOENT; } return radio->attr_get(net_if_get_device(iface), attr, value); } /** * Sets the radio drivers extended address filter. * * @param iface pointer to the IEEE 802.15.4 interface * @param ieee_addr Pointer to an extended address in little endian byte order */ static inline void ieee802154_radio_filter_ieee_addr(struct net_if *iface, uint8_t *ieee_addr) { const struct ieee802154_radio_api *radio = net_if_get_device(iface)->api; if (radio && (radio->get_capabilities(net_if_get_device(iface)) & IEEE802154_HW_FILTER)) { struct ieee802154_filter filter; filter.ieee_addr = ieee_addr; if (radio->filter(net_if_get_device(iface), true, IEEE802154_FILTER_TYPE_IEEE_ADDR, &filter) != 0) { NET_WARN("Could not apply IEEE address filter"); } } } static inline void ieee802154_radio_filter_short_addr(struct net_if *iface, uint16_t short_addr) { const struct ieee802154_radio_api *radio = net_if_get_device(iface)->api; if (radio && (radio->get_capabilities(net_if_get_device(iface)) & IEEE802154_HW_FILTER)) { struct ieee802154_filter filter; filter.short_addr = short_addr; if (radio->filter(net_if_get_device(iface), true, IEEE802154_FILTER_TYPE_SHORT_ADDR, &filter) != 0) { NET_WARN("Could not apply short address filter"); } } } static inline void ieee802154_radio_filter_pan_id(struct net_if *iface, uint16_t pan_id) { const struct ieee802154_radio_api *radio = net_if_get_device(iface)->api; if (radio && (radio->get_capabilities(net_if_get_device(iface)) & IEEE802154_HW_FILTER)) { struct ieee802154_filter filter; filter.pan_id = pan_id; if (radio->filter(net_if_get_device(iface), true, IEEE802154_FILTER_TYPE_PAN_ID, &filter) != 0) { NET_WARN("Could not apply PAN ID filter"); } } } static inline void ieee802154_radio_filter_src_ieee_addr(struct net_if *iface, uint8_t *ieee_addr) { const struct ieee802154_radio_api *radio = net_if_get_device(iface)->api; if (radio && (radio->get_capabilities(net_if_get_device(iface)) & IEEE802154_HW_FILTER)) { struct ieee802154_filter filter; filter.ieee_addr = ieee_addr; if (radio->filter(net_if_get_device(iface), true, IEEE802154_FILTER_TYPE_SRC_IEEE_ADDR, &filter) != 0) { NET_WARN("Could not apply SRC IEEE address filter"); } } } static inline void ieee802154_radio_filter_src_short_addr(struct net_if *iface, uint16_t short_addr) { const struct ieee802154_radio_api *radio = net_if_get_device(iface)->api; if (radio && (radio->get_capabilities(net_if_get_device(iface)) & IEEE802154_HW_FILTER)) { struct ieee802154_filter filter; filter.short_addr = short_addr; if (radio->filter(net_if_get_device(iface), true, IEEE802154_FILTER_TYPE_SRC_SHORT_ADDR, &filter) != 0) { NET_WARN("Could not apply SRC short address filter"); } } } static inline void ieee802154_radio_remove_src_ieee_addr(struct net_if *iface, uint8_t *ieee_addr) { const struct ieee802154_radio_api *radio = net_if_get_device(iface)->api; if (radio && (radio->get_capabilities(net_if_get_device(iface)) & IEEE802154_HW_FILTER)) { struct ieee802154_filter filter; filter.ieee_addr = ieee_addr; if (radio->filter(net_if_get_device(iface), false, IEEE802154_FILTER_TYPE_SRC_IEEE_ADDR, &filter) != 0) { NET_WARN("Could not remove SRC IEEE address filter"); } } } static inline void ieee802154_radio_remove_src_short_addr(struct net_if *iface, uint16_t short_addr) { const struct ieee802154_radio_api *radio = net_if_get_device(iface)->api; if (radio && (radio->get_capabilities(net_if_get_device(iface)) & IEEE802154_HW_FILTER)) { struct ieee802154_filter filter; filter.short_addr = short_addr; if (radio->filter(net_if_get_device(iface), false, IEEE802154_FILTER_TYPE_SRC_SHORT_ADDR, &filter) != 0) { NET_WARN("Could not remove SRC short address filter"); } } } static inline void ieee802154_radio_remove_pan_id(struct net_if *iface, uint16_t pan_id) { const struct ieee802154_radio_api *radio = net_if_get_device(iface)->api; if (radio && (radio->get_capabilities(net_if_get_device(iface)) & IEEE802154_HW_FILTER)) { struct ieee802154_filter filter; filter.pan_id = pan_id; if (radio->filter(net_if_get_device(iface), false, IEEE802154_FILTER_TYPE_PAN_ID, &filter) != 0) { NET_WARN("Could not remove PAN ID filter"); } } } /** * MAC utilities * * @note While MAC utilities may refer to PHY utilities, the inverse is not * true. */ /** * @brief Retrieves the currently selected channel page from the driver (see * phyCurrentPage, section 11.3, table 11-2). This is PHY-related information * not configured by L2 but directly provided by the driver. * * @param iface pointer to the IEEE 802.15.4 interface * * @returns The currently active channel page. * @retval 0 if an error occurred */ static inline enum ieee802154_phy_channel_page ieee802154_radio_current_channel_page(struct net_if *iface) { struct ieee802154_attr_value value; /* Currently we assume that drivers are statically configured to only * support a single channel page. Once drivers need to switch channels at * runtime this can be changed here w/o affecting clients. */ if (ieee802154_radio_attr_get(iface, IEEE802154_ATTR_PHY_SUPPORTED_CHANNEL_PAGES, &value)) { return 0; } return value.phy_supported_channel_pages; } /** * @brief Calculates a multiple of the PHY's symbol period in nanoseconds. * * @details The PHY's symbol period depends on the interface's current PHY * configuration which usually can be derived from the currently chosen channel * page and channel (phyCurrentPage and phyCurrentChannel, section 11.3, table * 11-2). * * To calculate the symbol period of HRP UWB PHYs, the nominal pulse repetition * frequency (PRF) is required. HRP UWB drivers will be expected to expose the * supported norminal PRF rates as a driver attribute. Existing drivers do not * allow for runtime switching of the PRF, so currently the PRF is considered to * be read-only and known. * * TODO: Add an UwbPrf argument once drivers need to support PRF switching at * runtime. * * @note We do not expose an API for a single symbol period to avoid having to * deal with floats for PHYs that don't require it while maintaining precision * in calculations where PHYs operate at symbol periods involving fractions of * nanoseconds. * * @param iface pointer to the IEEE 802.15.4 interface * @param channel The channel for which the symbol period is to be calculated. * @param multiplier The factor by which the symbol period is to be multiplied. * * @returns A multiple of the symbol period for the given interface with * nanosecond precision. * @retval 0 if an error occurred. */ static inline net_time_t ieee802154_radio_get_multiple_of_symbol_period(struct net_if *iface, uint16_t channel, uint16_t multiplier) { /* To keep things simple we only calculate symbol periods for channel * pages that are implemented by existing in-tree drivers. Add additional * channel pages as required. */ switch (ieee802154_radio_current_channel_page(iface)) { case IEEE802154_ATTR_PHY_CHANNEL_PAGE_ZERO_OQPSK_2450_BPSK_868_915: return (channel >= 11 ? IEEE802154_PHY_OQPSK_780_TO_2450MHZ_SYMBOL_PERIOD_NS : (channel > 0 ? IEEE802154_PHY_BPSK_915MHZ_SYMBOL_PERIOD_NS : IEEE802154_PHY_BPSK_868MHZ_SYMBOL_PERIOD_NS)) * multiplier; case IEEE802154_ATTR_PHY_CHANNEL_PAGE_TWO_OQPSK_868_915: return (channel > 0 ? IEEE802154_PHY_OQPSK_780_TO_2450MHZ_SYMBOL_PERIOD_NS : IEEE802154_PHY_OQPSK_868MHZ_SYMBOL_PERIOD_NS) * multiplier; case IEEE802154_ATTR_PHY_CHANNEL_PAGE_FOUR_HRP_UWB: { struct ieee802154_attr_value value; /* Currently we assume that drivers are statically configured to * only support a single PRF. Once drivers support switching PRF * at runtime an UWB PRF argument needs to be added to this * function which then must be validated against the set of * supported PRFs. */ if (ieee802154_radio_attr_get(iface, IEEE802154_ATTR_PHY_HRP_UWB_SUPPORTED_PRFS, &value)) { return 0; } switch (value.phy_hrp_uwb_supported_nominal_prfs) { case IEEE802154_PHY_HRP_UWB_NOMINAL_4_M: return IEEE802154_PHY_HRP_UWB_PRF4_TPSYM_SYMBOL_PERIOD_NS * multiplier; case IEEE802154_PHY_HRP_UWB_NOMINAL_16_M: return IEEE802154_PHY_HRP_UWB_PRF16_TPSYM_SYMBOL_PERIOD_NS * multiplier; case IEEE802154_PHY_HRP_UWB_NOMINAL_64_M: return IEEE802154_PHY_HRP_UWB_PRF64_TPSYM_SYMBOL_PERIOD_NS * multiplier; case IEEE802154_PHY_HRP_UWB_NOMINAL_64_M_BPRF: case IEEE802154_PHY_HRP_UWB_NOMINAL_128_M_HPRF: case IEEE802154_PHY_HRP_UWB_NOMINAL_256_M_HPRF: return IEEE802154_PHY_HRP_UWB_ERDEV_TPSYM_SYMBOL_PERIOD_NS * multiplier; default: CODE_UNREACHABLE; } } case IEEE802154_ATTR_PHY_CHANNEL_PAGE_FIVE_OQPSK_780: return IEEE802154_PHY_OQPSK_780_TO_2450MHZ_SYMBOL_PERIOD_NS * multiplier; case IEEE802154_ATTR_PHY_CHANNEL_PAGE_NINE_SUN_PREDEFINED: /* Current SUN FSK drivers only implement legacy IEEE 802.15.4g * 863 MHz (Europe) and 915 MHz (US ISM) bands, see IEEE * 802.15.4g, section 5.1, table 0. Once more bands are required * we need to request the currently active frequency band from * the driver. */ return IEEE802154_PHY_SUN_FSK_863MHZ_915MHZ_SYMBOL_PERIOD_NS * multiplier; default: CODE_UNREACHABLE; } } /** * @brief Calculates the PHY's turnaround time for the current channel page (see * section 11.3, table 11-1, aTurnaroundTime) in PHY symbols. * * @details The PHY's turnaround time is used to calculate - among other * parameters - the TX-to-RX turnaround time (see section 10.2.2) and the * RX-to-TX turnaround time (see section 10.2.3). * * @param iface pointer to the IEEE 802.15.4 interface * * @returns The turnaround time for the given interface in symbols. * @retval 0 if an error occurred. */ static inline uint32_t ieee802154_radio_get_a_turnaround_time(struct net_if *iface) { enum ieee802154_phy_channel_page channel_page = ieee802154_radio_current_channel_page(iface); if (!channel_page) { return 0; } /* Section 11.3, table 11-1, aTurnaroundTime: "For the SUN [...] PHYs, * the value is 1 ms expressed in symbol periods, rounded up to the next * integer number of symbol periods using the ceiling() function. [...] * The value is 12 [symbol periods] for all other PHYs. */ if (channel_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_NINE_SUN_PREDEFINED) { /* Current SUN FSK drivers only implement legacy IEEE 802.15.4g * 863 MHz (Europe) and 915 MHz (US ISM) bands, see IEEE * 802.15.4g, section 5.1, table 0. Once more bands are required * we need to request the currently active frequency band from * the driver. */ return IEEE802154_PHY_A_TURNAROUND_TIME_1MS( IEEE802154_PHY_SUN_FSK_863MHZ_915MHZ_SYMBOL_PERIOD_NS); } return IEEE802154_PHY_A_TURNAROUND_TIME_DEFAULT; } /** * @brief Verify if the given channel lies within the allowed range of available * channels of the driver's currently selected channel page. * * @param iface pointer to the IEEE 802.15.4 interface * @param channel The channel to verify or IEEE802154_NO_CHANNEL * * @returns true if the channel is available, false otherwise */ bool ieee802154_radio_verify_channel(struct net_if *iface, uint16_t channel); /** * @brief Counts all available channels of the driver's currently selected * channel page. * * @param iface pointer to the IEEE 802.15.4 interface * * @returns The number of available channels. */ uint16_t ieee802154_radio_number_of_channels(struct net_if *iface); /** * @brief Calculates the MAC's superframe duration (see section 8.4.2, * table 8-93, aBaseSuperframeDuration) in microseconds. * * @details The number of symbols forming a superframe when the superframe order * is equal to zero. * * @param iface pointer to the IEEE 802.15.4 interface * * @returns The base superframe duration for the given interface in microseconds. */ static inline uint32_t ieee802154_get_a_base_superframe_duration(struct net_if *iface) { struct ieee802154_context *ctx = net_if_l2_data(iface); return ieee802154_radio_get_multiple_of_symbol_period( iface, ctx->channel, IEEE802154_MAC_A_BASE_SUPERFRAME_DURATION) / NSEC_PER_USEC; } /** * @brief Retrieves macResponseWaitTime, see section 8.4.3.1, table 8-94, * converted to microseconds. * * @details The maximum time, in multiples of aBaseSuperframeDuration converted * to microseconds, a device shall wait for a response command to be available * following a request command. * * macResponseWaitTime is a network-topology-dependent parameter and may be set * to match the specific requirements of the network that a device is operating * on. * * @note Currently this parameter is read-only and uses the specified default of 32. * * @param iface pointer to the IEEE 802.15.4 interface * * @returns The response wait time for the given interface in microseconds. */ static inline uint32_t ieee802154_get_response_wait_time_us(struct net_if *iface) { /* TODO: Make this parameter configurable. */ return IEEE802154_MAC_RESPONSE_WAIT_TIME_DEFAULT * ieee802154_get_a_base_superframe_duration(iface); } #endif /* __IEEE802154_UTILS_H__ */