blob: ec80adff20e23df55c82ced45985889a13145130 [file] [log] [blame]
/*
* 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 <zephyr/net/ieee802154_radio.h>
#include <zephyr/sys/util_macro.h>
/**
* 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__ */