1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

ieee802154_submac: add initial implementation

This commit is contained in:
Jose Alamos 2020-09-04 11:18:02 +02:00
parent 612d723d6c
commit e100a67099
5 changed files with 805 additions and 0 deletions

View File

@ -437,6 +437,11 @@ ifneq (,$(filter gnrc_pktdump,$(USEMODULE)))
USEMODULE += od
endif
ifneq (,$(filter ieee802154_submac,$(USEMODULE)))
USEMODULE += luid
USEMODULE += xtimer
endif
ifneq (,$(filter od,$(USEMODULE)))
USEMODULE += fmt
endif

View File

@ -60,6 +60,7 @@ PSEUDOMODULES += gnrc_txtsnd
PSEUDOMODULES += heap_cmd
PSEUDOMODULES += i2c_scan
PSEUDOMODULES += ieee802154_radio_hal
PSEUDOMODULES += ieee802154_submac
PSEUDOMODULES += ina3221_alerts
PSEUDOMODULES += l2filter_blacklist
PSEUDOMODULES += l2filter_whitelist

View File

@ -0,0 +1,391 @@
/*
* Copyright (C) 2020 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for more
* details.
*/
/**
* @defgroup net_ieee802154_submac IEEE802.15.4 SubMAC layer
* @ingroup net_ieee802154
* @experimental This API is experimental and in an early state - expect
* changes!
* @brief This module defines a common layer for handling the lower
* part of the IEEE 802.15.4 MAC layer.
*
* This layer is responsible for:
* - Handling CSMA-CA and retransmissions.
* - Maintaining part of the MAC Information Base, e.g IEEE 802.15.4 addresses,
* channel settings, CSMA-CA params, etc.
*
* @{
*
* @author José I. Alamos <jose.alamos@haw-hamburg.de>
*/
#ifndef NET_IEEE802154_SUBMAC_H
#define NET_IEEE802154_SUBMAC_H
#ifdef __cplusplus
extern "C" {
#endif
#include <string.h>
#include "net/ieee802154.h"
#include "net/ieee802154/radio.h"
#define IEEE802154_SUBMAC_MAX_RETRANSMISSIONS (4U) /**< maximum number of frame retransmissions */
/**
* @brief IEEE 802.15.4 SubMAC forward declaration
*/
typedef struct ieee802154_submac ieee802154_submac_t;
/**
* @brief SubMAC states
*/
typedef enum {
/**
* @brief SubMAC and network devices are off.
*
* The corresponding network device is put in a state with the
* lowest energy consumption.
*/
IEEE802154_STATE_OFF,
/**
* @brief SubMAC is ready to be used.
*/
IEEE802154_STATE_IDLE,
/**
* @brief SubMAC is ready to be used and listening to incoming frames.
*/
IEEE802154_STATE_LISTEN,
} ieee802154_submac_state_t;
/**
* @brief IEEE 802.15.4 SubMAC callbacks.
*/
typedef struct {
/**
* @brief RX done event
*
* This function is called from the SubMAC to indicate a IEEE 802.15.4
* frame is ready to be fetched from the device.
*
* If @ref ieee802154_submac_t::state is @ref IEEE802154_STATE_LISTEN, the
* SubMAC is ready to receive frames.
*
* @note ACK frames are automatically handled and discarded by the SubMAC.
* @param[in] submac pointer to the SubMAC descriptor
*/
void (*rx_done)(ieee802154_submac_t *submac);
/**
* @brief TX done event
*
* This function is called from the SubMAC to indicate that the TX
* procedure finished.
*
* If @ref ieee802154_submac_t::state is @ref IEEE802154_STATE_LISTEN, the
* SubMAC is ready to receive frames.
*
* @param[in] submac pointer to the SubMAC descriptor
* @param[out] info TX information associated to the transmission (status,
* number of retransmissions, pending bit, etc).
*/
void (*tx_done)(ieee802154_submac_t *submac, int status,
ieee802154_tx_info_t *info);
} ieee802154_submac_cb_t;
/**
* @brief IEEE 802.15.4 SubMAC descriptor
*/
struct ieee802154_submac {
eui64_t ext_addr; /**< IEEE 802.15.4 extended address */
network_uint16_t short_addr; /**< IEEE 802.15.4 short address */
ieee802154_dev_t *dev; /**< pointer to the 802.15.4 HAL descriptor */
const ieee802154_submac_cb_t *cb; /**< pointer to the SubMAC callbacks */
ieee802154_csma_be_t be; /**< CSMA-CA backoff exponent params */
bool wait_for_ack; /**< SubMAC is waiting for an ACK frame */
bool tx; /**< SubMAC is currently transmitting a frame */
uint16_t panid; /**< IEEE 802.15.4 PAN ID */
uint16_t channel_num; /**< IEEE 802.15.4 channel number */
uint8_t channel_page; /**< IEEE 802.15.4 channel page */
uint8_t retrans; /**< current number of retransmissions */
uint8_t csma_retries_nb; /**< current number of CSMA-CA retries */
uint8_t backoff_mask; /**< internal value used for random backoff calculation */
uint8_t csma_retries; /**< maximum number of CSMA-CA retries */
int8_t tx_pow; /**< Transmission power (in dBm) */
ieee802154_submac_state_t state; /**< State of the SubMAC */
};
/**
* @brief Get the internal state of the SubMAC
*
* @param[in] submac pointer to the SubMAC descriptor
*
* @return the SubMAC state
*/
static inline ieee802154_submac_state_t ieee802154_get_state(ieee802154_submac_t *submac)
{
return submac->state;
}
/**
* @brief Set the internal state of the SubMAC
*
* @param[in] submac pointer to the SubMAC descriptor
* @param[in] state the desired state
*
* @return 0 on success
* @return negative errno on error.
*/
int ieee802154_set_state(ieee802154_submac_t *submac, ieee802154_submac_state_t state);
/**
* @brief Transmit an IEEE 802.15.4 PSDU
*
* This function performs an IEEE 802.15.4 transmission, including CSMA-CA and
* retransmissions (if ACK Request bit is set). When the transmission finishes
* an @ref ieee802154_submac_cb_t::tx_done event is issued.
*
* @param[in] submac pointer to the SubMAC descriptor
* @param[in] iolist pointer to the PSDU frame (without FCS)
*
* @return 0 on success
* @return negative errno on error
*/
int ieee802154_send(ieee802154_submac_t *submac, const iolist_t *iolist);
/**
* @brief Set the IEEE 802.15.4 short address
*
* @param[in] submac pointer to the SubMAC descriptor
* @param[in] short_addr IEEE 802.15.4 short address
*
* @return 0 on success
* @return negative errno on error
*/
static inline int ieee802154_set_short_addr(ieee802154_submac_t *submac,
const network_uint16_t *short_addr)
{
int res = ieee802154_radio_set_hw_addr_filter(submac->dev, short_addr, NULL,
NULL);
if (res >= 0) {
memcpy(&submac->short_addr, short_addr, IEEE802154_SHORT_ADDRESS_LEN);
}
return res;
}
/**
* @brief Set the IEEE 802.15.4 extended address
*
* @param[in] submac pointer to the SubMAC descriptor
* @param[in] ext_addr IEEE 802.15.4 extended address
*
* @return 0 on success
* @return negative errno on error
*/
static inline int ieee802154_set_ext_addr(ieee802154_submac_t *submac,
const eui64_t *ext_addr)
{
int res = ieee802154_radio_set_hw_addr_filter(submac->dev, NULL, ext_addr,
NULL);
if (res >= 0) {
memcpy(&submac->ext_addr, ext_addr, IEEE802154_LONG_ADDRESS_LEN);
}
return res;
}
/**
* @brief Set the IEEE 802.15.4 PAN ID
*
* @param[in] submac pointer to the SubMAC descriptor
* @param[in] panid IEEE 802.15.4 PAN ID
*
* @return 0 on success
* @return negative errno on error
*/
static inline int ieee802154_set_panid(ieee802154_submac_t *submac,
const uint16_t *panid)
{
int res = ieee802154_radio_set_hw_addr_filter(submac->dev, NULL, NULL,
panid);
if (res >= 0) {
submac->panid = *panid;
}
return res;
}
/**
* @brief Set IEEE 802.15.4 PHY configuration (channel, TX power)
*
* @param[in] submac pointer to the SubMAC descriptor
* @param[in] channel_num channel number
* @param[in] channel_page channel page
* @param[in] tx_pow transmission power (in dBm)
*
* @return 0 on success
* @return -ENOTSUP if the PHY settings are not supported
* @return negative errno on error
*/
int ieee802154_set_phy_conf(ieee802154_submac_t *submac, uint16_t channel_num,
uint8_t channel_page, int8_t tx_pow);
/**
* @brief Set IEEE 802.15.4 channel number
*
* This is a shortcut to @ref ieee802154_set_phy_conf
*
* @param[in] submac pointer the SubMAC descriptor
* @param[in] channel_num channel number
*
* @return 0 on success
* @return -ENOTSUP if the channel number is not supported
* @return negative errno on error
*/
static inline int ieee802154_set_channel_number(ieee802154_submac_t *submac,
uint16_t channel_num)
{
return ieee802154_set_phy_conf(submac, channel_num, submac->channel_page,
submac->tx_pow);
}
/**
* @brief Set IEEE 802.15.4 channel page
*
* This is a shortcut to @ref ieee802154_set_phy_conf
*
* @param[in] submac pointer the SubMAC descriptor
* @param[in] channel_page channel page
*
* @return 0 on success
* @return -ENOTSUP if the channel page is not supported
* @return negative errno on error
*/
static inline int ieee802154_set_channel_page(ieee802154_submac_t *submac,
uint16_t channel_page)
{
return ieee802154_set_phy_conf(submac, submac->channel_num, channel_page,
submac->tx_pow);
}
/**
* @brief Set IEEE 802.15.4 transmission power
*
* This is a shortcut to @ref ieee802154_set_phy_conf
*
* @param[in] submac pointer the SubMAC descriptor
* @param[in] tx_pow transmission power (in dBm)
*
* @return 0 on success
* @return -ENOTSUP if the transmission power is not supported
* @return negative errno on error
*/
static inline int ieee802154_set_tx_power(ieee802154_submac_t *submac,
int8_t tx_pow)
{
return ieee802154_set_phy_conf(submac, submac->channel_num,
submac->channel_page, tx_pow);
}
/**
* @brief Get the received frame length
*
* @param[in] submac pointer to the SubMAC
*
* @return length of the PSDU (excluding FCS length)
*/
static inline int ieee802154_get_frame_length(ieee802154_submac_t *submac)
{
return ieee802154_radio_len(submac->dev);
}
/**
* @brief Read the received frame
*
* This functions reads the received PSDU from the device (excluding FCS)
*
* @param[in] submac pointer to the SubMAC descriptor
* @param[out] buf buffer to write into. If NULL, the packet is discarded
* @param[in] len length of the buffer
* @param[out] info RX information of the packet. If NULL, the information is not fetched.
*
* @return the number of bytes written to @p buf
* @return negative errno on error
*/
static inline int ieee802154_read_frame(ieee802154_submac_t *submac, void *buf,
size_t len, ieee802154_rx_info_t *info)
{
return ieee802154_radio_indication_rx(submac->dev, buf, len, info);
}
/**
* @brief Init the IEEE 802.15.4 SubMAC
*
* @param[in] submac pointer to the SubMAC descriptor
*
* @return 0 on success
* @return negative errno on error
*/
int ieee802154_submac_init(ieee802154_submac_t *submac);
/**
* @brief Set the ACK timeout timer
*
* @note This function should be implemented by the user of the SubMAC.
*
* @param[in] submac pointer to the SubMAC descriptor
* @param[in] us microseconds until the ACK timeout timer is fired
*/
extern void ieee802154_submac_ack_timer_set(ieee802154_submac_t *submac,
uint16_t us);
/**
* @brief Cancel the ACK timeout timer
*
* @note This function should be implemented by the user of the SubMAC.
*
* @param[in] submac pointer to the SubMAC descriptor
*/
extern void ieee802154_submac_ack_timer_cancel(ieee802154_submac_t *submac);
/**
* @brief Indicate the SubMAC that the ACK timeout fired.
*
* This function must be called when the ACK timeout timer fires.
*
* @note this function should not be called inside ISR context.
*
* @param[in] submac pointer to the SubMAC descriptor
*/
void ieee802154_submac_ack_timeout_fired(ieee802154_submac_t *submac);
/**
* @brief Indicate the SubMAC that the device received a frame.
*
* @param[in] submac pointer to the SubMAC descriptor
*/
void ieee802154_submac_rx_done_cb(ieee802154_submac_t *submac);
/**
* @brief Indicate the SubMAC that the device finished the transmission procedure.
*
* @param[in] submac pointer to the SubMAC descriptor
*/
void ieee802154_submac_tx_done_cb(ieee802154_submac_t *submac);
#ifdef __cplusplus
}
#endif
#endif /* NET_IEEE802154_SUBMAC_H */
/** @} */

View File

@ -1 +1,11 @@
MODULE = ieee802154
SRC = \
ieee802154.c \
#
ifneq (,$(filter ieee802154_submac,$(USEMODULE)))
SRC += submac.c
endif
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,398 @@
/*
* Copyright (C) 2020 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @{
*
* @file
* @author José I. Alamos <jose.alamos@haw-hamburg.de>
*/
#include <stdio.h>
#include <string.h>
#include "net/ieee802154/submac.h"
#include "net/ieee802154.h"
#include "xtimer.h"
#include "random.h"
#include "luid.h"
#include "kernel_defines.h"
#include "errno.h"
#include <assert.h>
#define CSMA_SENDER_BACKOFF_PERIOD_UNIT_MS (320U)
#define ACK_TIMEOUT_US (864U)
static void _handle_tx_no_ack(ieee802154_submac_t *submac);
static void _tx_end(ieee802154_submac_t *submac, int status,
ieee802154_tx_info_t *info)
{
ieee802154_dev_t *dev = submac->dev;
ieee802154_radio_request_set_trx_state(dev, submac->state == IEEE802154_STATE_LISTEN ? IEEE802154_TRX_STATE_RX_ON : IEEE802154_TRX_STATE_TRX_OFF);
submac->tx = false;
while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN) {}
submac->cb->tx_done(submac, status, info);
}
static inline bool _does_handle_ack(ieee802154_dev_t *dev)
{
return ieee802154_radio_has_frame_retrans(dev) ||
ieee802154_radio_has_irq_ack_timeout(dev);
}
static int _perform_csma_ca(ieee802154_submac_t *submac)
{
ieee802154_dev_t *dev = submac->dev;
if (submac->csma_retries_nb <= submac->csma_retries) {
ieee802154_radio_request_set_trx_state(dev, IEEE802154_TRX_STATE_TX_ON);
/* delay for an adequate random backoff period */
uint32_t bp = (random_uint32() & submac->backoff_mask) *
CSMA_SENDER_BACKOFF_PERIOD_UNIT_MS;
xtimer_usleep(bp);
/* try to send after a CCA */
while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN) {}
while (ieee802154_radio_request_transmit(dev) == -EBUSY) {}
/* Prepare for next iteration */
if (submac->backoff_mask + 1 < submac->be.max) {
submac->backoff_mask = (submac->backoff_mask << 1) | 1;
}
else {
submac->backoff_mask = (1 << submac->be.max) - 1;
}
submac->csma_retries_nb++;
}
else {
ieee802154_radio_set_rx_mode(dev, IEEE802154_RX_AACK_ENABLED);
_tx_end(submac, TX_STATUS_MEDIUM_BUSY, NULL);
}
return 0;
}
/**
* @brief Perform CSMA-CA transmission (possibly with retransmission)
*
* If radio supports @ref IEEE802154_CAP_FRAME_RETRANS, the device will automatically retransmit.
* If radio supports @ref IEEE802154_CAP_AUTO_CSMA, this function will use the
* internal CSMA-CA acceleration to perform the transmission.
*
* @param submac pointer to the SubMAC
*
* @return 0 on success
* @return negative errno on error
*/
int ieee802154_csma_ca_transmit(ieee802154_submac_t *submac)
{
ieee802154_dev_t *dev = submac->dev;
/* If radio has Auto CSMA-CA or Frame Retransmissions, simply send and wait for the transmit confirmation. */
if (ieee802154_radio_has_auto_csma(dev) ||
ieee802154_radio_has_frame_retrans(dev)) {
/* Make sure we are in TX_ON */
ieee802154_radio_request_set_trx_state(dev, IEEE802154_TRX_STATE_TX_ON);
while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN) {}
int res;
while ((res = ieee802154_radio_request_transmit(dev)) == -EBUSY) {}
return res;
}
else {
submac->csma_retries_nb = 0;
submac->backoff_mask = (1 << submac->be.min) - 1;
_perform_csma_ca(submac);
}
return 0;
}
static bool _has_retrans_left(ieee802154_submac_t *submac)
{
return submac->retrans < IEEE802154_SUBMAC_MAX_RETRANSMISSIONS;
}
static void _perform_retrans(ieee802154_submac_t *submac)
{
ieee802154_dev_t *dev = submac->dev;
if (_has_retrans_left(submac)) {
submac->retrans++;
ieee802154_csma_ca_transmit(submac);
}
else {
ieee802154_radio_set_rx_mode(dev, IEEE802154_RX_AACK_ENABLED);
_tx_end(submac, TX_STATUS_NO_ACK, NULL);
}
}
void ieee802154_submac_ack_timeout_fired(ieee802154_submac_t *submac)
{
/* This is required to avoid race conditions */
if (submac->wait_for_ack) {
_handle_tx_no_ack(submac);
}
}
/* All callbacks run in the same context */
void ieee802154_submac_rx_done_cb(ieee802154_submac_t *submac)
{
ieee802154_dev_t *dev = submac->dev;
if (!_does_handle_ack(dev) && submac->wait_for_ack) {
uint8_t ack[3];
if (ieee802154_radio_indication_rx(dev, ack, 3, NULL) &&
ack[0] & IEEE802154_FCF_TYPE_ACK) {
ieee802154_submac_ack_timer_cancel(submac);
ieee802154_tx_info_t tx_info;
tx_info.retrans = submac->retrans;
bool fp = (ack[0] & IEEE802154_FCF_FRAME_PEND);
submac->wait_for_ack = false;
ieee802154_radio_set_rx_mode(submac->dev,
IEEE802154_RX_AACK_ENABLED);
_tx_end(submac, fp ? TX_STATUS_FRAME_PENDING : TX_STATUS_SUCCESS,
&tx_info);
}
}
else {
submac->cb->rx_done(submac);
}
}
static void _handle_tx_success(ieee802154_submac_t *submac,
ieee802154_tx_info_t *info)
{
ieee802154_dev_t *dev = submac->dev;
ieee802154_radio_request_set_trx_state(dev, IEEE802154_TRX_STATE_RX_ON);
while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN) {}
if (ieee802154_radio_has_frame_retrans(dev) ||
ieee802154_radio_has_irq_ack_timeout(dev) || !submac->wait_for_ack) {
_tx_end(submac, info->status, info);
}
else {
ieee802154_radio_set_rx_mode(dev, IEEE802154_RX_WAIT_FOR_ACK);
/* Handle ACK reception */
ieee802154_submac_ack_timer_set(submac, ACK_TIMEOUT_US);
}
}
static void _handle_tx_medium_busy(ieee802154_submac_t *submac)
{
ieee802154_dev_t *dev = submac->dev;
if (ieee802154_radio_has_frame_retrans(dev) ||
ieee802154_radio_has_auto_csma(dev)) {
ieee802154_radio_request_set_trx_state(dev, IEEE802154_TRX_STATE_RX_ON);
_tx_end(submac, TX_STATUS_MEDIUM_BUSY, NULL);
}
else {
/* CCA failed. Continue with the CSMA-CA algorithm */
_perform_csma_ca(submac);
}
}
static void _handle_tx_no_ack(ieee802154_submac_t *submac)
{
ieee802154_dev_t *dev = submac->dev;
if (ieee802154_radio_has_frame_retrans(dev)) {
ieee802154_radio_request_set_trx_state(dev, IEEE802154_TRX_STATE_RX_ON);
submac->wait_for_ack = false;
_tx_end(submac, TX_STATUS_NO_ACK, NULL);
}
else {
/* Perform retransmissions */
_perform_retrans(submac);
}
}
void ieee802154_submac_tx_done_cb(ieee802154_submac_t *submac)
{
ieee802154_dev_t *dev = submac->dev;
ieee802154_tx_info_t info;
ieee802154_radio_confirm_transmit(dev, &info);
switch (info.status) {
case TX_STATUS_MEDIUM_BUSY:
_handle_tx_medium_busy(submac);
break;
case TX_STATUS_NO_ACK:
_handle_tx_no_ack(submac);
break;
case TX_STATUS_SUCCESS:
case TX_STATUS_FRAME_PENDING:
_handle_tx_success(submac, &info);
break;
default:
assert(false);
break;
}
}
int ieee802154_send(ieee802154_submac_t *submac, const iolist_t *iolist)
{
ieee802154_dev_t *dev = submac->dev;
uint8_t *buf = iolist->iol_base;
bool cnf = buf[0] & IEEE802154_FCF_ACK_REQ;
if (submac->state == IEEE802154_STATE_OFF) {
return -ENETDOWN;
}
if (submac->tx ||
ieee802154_radio_request_set_trx_state(dev,
IEEE802154_TRX_STATE_TX_ON) < 0) {
return -EBUSY;
}
submac->tx = true;
ieee802154_radio_write(dev, iolist);
while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN) {}
submac->wait_for_ack = cnf;
submac->retrans = 0;
ieee802154_csma_ca_transmit(submac);
return 0;
}
int ieee802154_submac_init(ieee802154_submac_t *submac)
{
ieee802154_dev_t *dev = submac->dev;
submac->tx = false;
submac->state = IEEE802154_STATE_LISTEN;
ieee802154_radio_request_on(dev);
/* generate EUI-64 and short address */
luid_get_eui64(&submac->ext_addr);
luid_get_short(&submac->short_addr);
submac->panid = CONFIG_IEEE802154_DEFAULT_PANID;
submac->be.min = CONFIG_IEEE802154_DEFAULT_CSMA_CA_MIN_BE;
submac->csma_retries = CONFIG_IEEE802154_DEFAULT_CSMA_CA_RETRIES;
submac->be.max = CONFIG_IEEE802154_DEFAULT_CSMA_CA_MAX_BE;
submac->tx_pow = CONFIG_IEEE802154_DEFAULT_TXPOWER;
if (ieee802154_radio_has_24_ghz(dev)) {
submac->channel_num = CONFIG_IEEE802154_DEFAULT_CHANNEL;
/* 2.4 GHz only use page 0 */
submac->channel_page = 0;
}
else {
submac->channel_num = CONFIG_IEEE802154_DEFAULT_SUBGHZ_CHANNEL;
submac->channel_page = CONFIG_IEEE802154_DEFAULT_SUBGHZ_PAGE;
}
/* If the radio is still not in TRX_OFF state, spin */
while (ieee802154_radio_confirm_on(dev) == -EAGAIN) {}
/* Enable Auto ACK */
ieee802154_radio_set_rx_mode(dev, IEEE802154_RX_AACK_ENABLED);
/* Configure address filter */
ieee802154_radio_set_hw_addr_filter(dev, &submac->short_addr,
&submac->ext_addr, &submac->panid);
/* Configure PHY settings (channel, TX power) */
ieee802154_phy_conf_t conf =
{ .channel = CONFIG_IEEE802154_DEFAULT_CHANNEL,
.page = CONFIG_IEEE802154_DEFAULT_CHANNEL,
.pow = CONFIG_IEEE802154_DEFAULT_TXPOWER };
ieee802154_radio_config_phy(dev, &conf);
assert(ieee802154_radio_set_cca_threshold(dev,
CONFIG_IEEE802154_CCA_THRESH_DEFAULT) >= 0);
ieee802154_radio_request_set_trx_state(dev, IEEE802154_TRX_STATE_RX_ON);
return 0;
}
int ieee802154_set_phy_conf(ieee802154_submac_t *submac, uint16_t channel_num,
uint8_t channel_page, int8_t tx_pow)
{
ieee802154_dev_t *dev = submac->dev;
const ieee802154_phy_conf_t conf =
{ .channel = channel_num, .page = channel_page, .pow = tx_pow };
if (submac->state == IEEE802154_STATE_OFF) {
return -ENETDOWN;
}
int res = ieee802154_radio_config_phy(dev, &conf);
if (res >= 0) {
submac->channel_num = channel_num;
submac->channel_page = channel_page;
submac->tx_pow = tx_pow;
}
return res;
}
int ieee802154_set_state(ieee802154_submac_t *submac, ieee802154_submac_state_t state)
{
int res;
ieee802154_dev_t *dev = submac->dev;
if (submac->tx) {
return -EBUSY;
}
if (state == submac->state) {
return -EALREADY;
}
/* Wake up the radio if it was off */
if (submac->state == IEEE802154_STATE_OFF) {
if ((res = ieee802154_radio_request_on(dev)) < 0) {
return res;
}
while (ieee802154_radio_confirm_on(dev) == -EAGAIN);
}
if (state == IEEE802154_STATE_OFF) {
res = ieee802154_radio_off(dev);
}
else {
ieee802154_submac_state_t new_state =
state == IEEE802154_STATE_IDLE
? IEEE802154_TRX_STATE_TRX_OFF
: IEEE802154_TRX_STATE_RX_ON;
if ((res = ieee802154_radio_request_set_trx_state(dev, new_state)) < 0) {
return res;
}
while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN);
}
submac->state = state;
return res;
}
/** @} */