blob: 4294ba7bd9ff27f4d4c46ff5b32f3de996fc85a2 [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");
}
}
}
/**
* @brief Calculates the PHY's symbol period in microseconds.
*
* @details The PHY's symbol period depends on the interface's current PHY which
* can be derived from the currently chosen channel page (phyCurrentPage).
*
* Examples:
* * SUN FSK: see section 19.1, table 19-1
* * O-QPSK: see section 12.3.3
* * HRP UWB: derived from the preamble symbol period (T_psym), see section
* 11.3, table 11-1 and section 15.2.5, table 15-4
*
* @note Currently the symbol period can only be calculated for SUN FSK and O-QPSK.
*
* @param iface The interface for which the symbol period should be calculated.
*
* @returns The symbol period for the given interface in microseconds.
*/
static inline uint32_t ieee802154_radio_get_symbol_period_us(struct net_if *iface)
{
/* TODO: Move symbol period calculation to radio driver. */
if (IS_ENABLED(CONFIG_NET_L2_IEEE802154_SUB_GHZ) &&
ieee802154_radio_get_hw_capabilities(iface) & IEEE802154_HW_SUB_GHZ) {
return IEEE802154_PHY_SYMBOL_PERIOD_US(true);
}
return IEEE802154_PHY_SYMBOL_PERIOD_US(false);
}
/**
* @brief Calculates the PHY's turnaround time (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).
*
* @note Currently the turnaround time can only be calculated for SUN FSK and O-QPSK.
*
* @param iface The interface for which the turnaround time should be calculated.
*
* @returns The turnaround time for the given interface.
*/
static inline uint32_t ieee802154_radio_get_a_turnaround_time(struct net_if *iface)
{
if (IS_ENABLED(CONFIG_NET_L2_IEEE802154_SUB_GHZ) &&
ieee802154_radio_get_hw_capabilities(iface) & IEEE802154_HW_SUB_GHZ) {
return IEEE802154_PHY_A_TURNAROUND_TIME(true);
}
return IEEE802154_PHY_A_TURNAROUND_TIME(false);
}
static inline bool ieee802154_radio_verify_channel(struct net_if *iface, uint16_t channel)
{
if (channel == IEEE802154_NO_CHANNEL) {
return false;
}
#ifdef CONFIG_NET_L2_IEEE802154_SUB_GHZ
const struct ieee802154_radio_api *radio =
net_if_get_device(iface)->api;
if (!radio) {
return false;
}
if (radio->get_capabilities(net_if_get_device(iface)) &
IEEE802154_HW_SUB_GHZ) {
if (channel >
radio->get_subg_channel_count(net_if_get_device(iface))) {
return false;
}
}
#endif /* CONFIG_NET_L2_IEEE802154_SUB_GHZ */
return true;
}
/**
* MAC utilities
*
* Note: While MAC utilities may refer to PHY utilities,
* the inverse is not true.
*/
/**
* The number of PHY symbols forming a superframe slot when the superframe order
* is equal to zero, see sections 8.4.2, table 8-93, aBaseSlotDuration and 6.2.1.
*/
#define IEEE802154_MAC_A_BASE_SLOT_DURATION 60U
/**
* The number of slots contained in any superframe, see section 8.4.2,
* table 8-93, aNumSuperframeSlots.
*/
#define IEEE802154_MAC_A_NUM_SUPERFRAME_SLOTS 16U
/**
* The number of PHY symbols forming a superframe when the superframe order is
* equal to zero, see section 8.4.2, table 8-93, aBaseSuperframeDuration.
*/
#define IEEE802154_MAC_A_BASE_SUPERFRAME_DURATION \
(IEEE802154_MAC_A_BASE_SLOT_DURATION * IEEE802154_MAC_A_NUM_SUPERFRAME_SLOTS)
/**
* @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 The interface for which the base superframe duration should be
* calculated.
*
* @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)
{
return IEEE802154_MAC_A_BASE_SUPERFRAME_DURATION *
ieee802154_radio_get_symbol_period_us(iface);
}
/**
* Default macResponseWaitTime in multiples of aBaseSuperframeDuration as
* defined in section 8.4.3.1, table 8-94.
*/
#define IEEE802154_MAC_RESONSE_WAIT_TIME_DEFAULT 32U
/**
* @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 The interface for which the response wait time should be calculated.
*
* @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_RESONSE_WAIT_TIME_DEFAULT *
ieee802154_get_a_base_superframe_duration(iface);
}
#endif /* __IEEE802154_UTILS_H__ */