a12a6ab5b9
Replaces the previous approach to define bands via hardware capabilities by the standard conforming concept of channel pages. In the short term this allows us to correctly calculate the PHY specific symbol rate and several parameters that directly depend from the symbol rate and were previously not being correctly calculated for some of the drivers whose channel pages could not be represented previously: * We now support sub-nanosecond precision symbol rates for UWB. Rounding errors are being minimized by switching from a divide-then-multiply approach to a multiply-then-divide approach. * UWB HRP: symbol rate depends on channel page specific preamble symbol rate which again requires the pulse repetition value to be known * Several MAC timings are being corrected based on the now correctly calculated symbol rates, namely aTurnaroundTime, aUnitBackoffPeriod, aBaseSuperframeDuration. In the long term, this change unlocks such highly promising functional areas as UWB ranging and SUN-PHY channel hopping in the SubG area (plus of course any other PHY specific feature). Signed-off-by: Florian Grandel <fgrandel@code-for-humans.de>
526 lines
16 KiB
C
526 lines
16 KiB
C
/*
|
|
* 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__ */
|