mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
610 lines
19 KiB
C
610 lines
19 KiB
C
/*
|
|
* 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 <assert.h>
|
|
#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"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
#define CSMA_SENDER_BACKOFF_PERIOD_UNIT_US (320U)
|
|
#define ACK_TIMEOUT_US (864U)
|
|
|
|
static char *str_states[IEEE802154_FSM_STATE_NUMOF] = {
|
|
"INVALID",
|
|
"RX",
|
|
"IDLE",
|
|
"PREPARE",
|
|
"TX",
|
|
"WAIT_FOR_ACK",
|
|
};
|
|
|
|
static char *str_ev[IEEE802154_FSM_EV_NUMOF] = {
|
|
"TX_DONE",
|
|
"RX_DONE",
|
|
"CRC_ERROR",
|
|
"ACK_TIMEOUT",
|
|
"BH",
|
|
"REQUEST_TX",
|
|
"REQUEST_SET_RX_ON",
|
|
"REQUEST_SET_IDLE",
|
|
};
|
|
|
|
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 inline bool _does_handle_csma(ieee802154_dev_t *dev)
|
|
{
|
|
return ieee802154_radio_has_frame_retrans(dev) ||
|
|
ieee802154_radio_has_auto_csma(dev);
|
|
}
|
|
|
|
static bool _has_retrans_left(ieee802154_submac_t *submac)
|
|
{
|
|
return submac->retrans < CONFIG_IEEE802154_DEFAULT_MAX_FRAME_RETRANS;
|
|
}
|
|
|
|
static ieee802154_fsm_state_t _tx_end(ieee802154_submac_t *submac, int status,
|
|
ieee802154_tx_info_t *info)
|
|
{
|
|
int res;
|
|
|
|
/* This is required to prevent unused variable warnings */
|
|
(void) res;
|
|
|
|
submac->wait_for_ack = false;
|
|
|
|
res = ieee802154_radio_set_idle(&submac->dev, true);
|
|
|
|
assert(res >= 0);
|
|
submac->cb->tx_done(submac, status, info);
|
|
return IEEE802154_FSM_STATE_IDLE;
|
|
}
|
|
|
|
static void _print_debug(ieee802154_fsm_state_t old, ieee802154_fsm_state_t new,
|
|
ieee802154_fsm_ev_t ev)
|
|
{
|
|
DEBUG("%s--(%s)->%s\n", str_states[old], str_ev[ev], str_states[new]);
|
|
}
|
|
|
|
static ieee802154_fsm_state_t _handle_tx_no_ack(ieee802154_submac_t *submac)
|
|
{
|
|
int res;
|
|
|
|
/* This is required to prevent unused variable warnings */
|
|
(void) res;
|
|
|
|
/* In case of ACK Timeout, either trigger retransmissions or end
|
|
* the TX procedure */
|
|
if (_has_retrans_left(submac)) {
|
|
submac->retrans++;
|
|
res = ieee802154_radio_set_idle(&submac->dev, true);
|
|
assert(res >= 0);
|
|
ieee802154_submac_bh_request(submac);
|
|
return IEEE802154_FSM_STATE_PREPARE;
|
|
}
|
|
else {
|
|
ieee802154_radio_set_frame_filter_mode(&submac->dev, IEEE802154_FILTER_ACCEPT);
|
|
return _tx_end(submac, TX_STATUS_NO_ACK, NULL);
|
|
}
|
|
}
|
|
|
|
static int _handle_fsm_ev_request_tx(ieee802154_submac_t *submac)
|
|
{
|
|
ieee802154_dev_t *dev = &submac->dev;
|
|
|
|
/* Set state to TX_ON */
|
|
int res = ieee802154_radio_set_idle(dev, false);
|
|
|
|
if (res < 0) {
|
|
return res;
|
|
}
|
|
else {
|
|
/* write frame to radio */
|
|
ieee802154_radio_write(dev, submac->psdu);
|
|
ieee802154_submac_bh_request(submac);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static ieee802154_fsm_state_t _fsm_state_rx(ieee802154_submac_t *submac, ieee802154_fsm_ev_t ev)
|
|
{
|
|
ieee802154_dev_t *dev = &submac->dev;
|
|
int res;
|
|
|
|
/* This is required to prevent unused variable warnings */
|
|
(void) res;
|
|
|
|
switch (ev) {
|
|
case IEEE802154_FSM_EV_REQUEST_TX:
|
|
if (_handle_fsm_ev_request_tx(submac) < 0) {
|
|
return IEEE802154_FSM_STATE_RX;
|
|
}
|
|
return IEEE802154_FSM_STATE_PREPARE;
|
|
case IEEE802154_FSM_EV_RX_DONE:
|
|
/* Make sure it's not an ACK frame */
|
|
while (ieee802154_radio_set_idle(&submac->dev, false) < 0) {}
|
|
if (ieee802154_radio_len(&submac->dev) > (int)IEEE802154_MIN_FRAME_LEN) {
|
|
submac->cb->rx_done(submac);
|
|
return IEEE802154_FSM_STATE_IDLE;
|
|
}
|
|
else {
|
|
ieee802154_radio_read(&submac->dev, NULL, 0, NULL);
|
|
|
|
/* If the radio doesn't support RX Continuous, go to RX */
|
|
res = ieee802154_radio_set_rx(&submac->dev);
|
|
assert(res >= 0);
|
|
|
|
/* Keep on current state */
|
|
return IEEE802154_FSM_STATE_RX;
|
|
}
|
|
case IEEE802154_FSM_EV_CRC_ERROR:
|
|
while (ieee802154_radio_set_idle(&submac->dev, false) < 0) {}
|
|
ieee802154_radio_read(&submac->dev, NULL, 0, NULL);
|
|
/* If the radio doesn't support RX Continuous, go to RX */
|
|
res = ieee802154_radio_set_rx(&submac->dev);
|
|
assert(res >= 0);
|
|
/* Keep on current state */
|
|
return IEEE802154_FSM_STATE_RX;
|
|
|
|
case IEEE802154_FSM_EV_REQUEST_SET_IDLE:
|
|
/* Try to turn off the transceiver */
|
|
if ((ieee802154_radio_request_set_idle(dev, false)) < 0) {
|
|
/* Keep on current state */
|
|
return IEEE802154_FSM_STATE_RX;
|
|
}
|
|
while (ieee802154_radio_confirm_set_idle(dev) == -EAGAIN) {}
|
|
return IEEE802154_FSM_STATE_IDLE;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return IEEE802154_FSM_STATE_INVALID;
|
|
}
|
|
|
|
static ieee802154_fsm_state_t _fsm_state_idle(ieee802154_submac_t *submac, ieee802154_fsm_ev_t ev)
|
|
{
|
|
ieee802154_dev_t *dev = &submac->dev;
|
|
|
|
switch (ev) {
|
|
case IEEE802154_FSM_EV_REQUEST_TX:
|
|
if (_handle_fsm_ev_request_tx(submac) < 0) {
|
|
return IEEE802154_FSM_STATE_IDLE;
|
|
}
|
|
return IEEE802154_FSM_STATE_PREPARE;
|
|
case IEEE802154_FSM_EV_REQUEST_SET_RX_ON:
|
|
/* Try to go turn on the transceiver */
|
|
if ((ieee802154_radio_set_rx(dev) < 0)) {
|
|
/* Keep on current state */
|
|
return IEEE802154_FSM_STATE_IDLE;
|
|
}
|
|
return IEEE802154_FSM_STATE_RX;
|
|
case IEEE802154_FSM_EV_RX_DONE:
|
|
case IEEE802154_FSM_EV_CRC_ERROR:
|
|
/* This might happen in case there's a race condition between ACK_TIMEOUT
|
|
* and TX_DONE. We simply discard the frame and keep the state as
|
|
* it is
|
|
*/
|
|
ieee802154_radio_read(&submac->dev, NULL, 0, NULL);
|
|
return IEEE802154_FSM_STATE_IDLE;
|
|
default:
|
|
break;
|
|
}
|
|
return IEEE802154_FSM_STATE_INVALID;
|
|
}
|
|
|
|
static ieee802154_fsm_state_t _fsm_state_prepare(ieee802154_submac_t *submac,
|
|
ieee802154_fsm_ev_t ev)
|
|
{
|
|
ieee802154_dev_t *dev = &submac->dev;
|
|
|
|
switch (ev) {
|
|
case IEEE802154_FSM_EV_BH:
|
|
if (!_does_handle_csma(dev)) {
|
|
/* delay for an adequate random backoff period */
|
|
uint32_t bp = (random_uint32() & submac->backoff_mask) *
|
|
CSMA_SENDER_BACKOFF_PERIOD_UNIT_US;
|
|
|
|
xtimer_usleep(bp);
|
|
/* Prepare for next iteration */
|
|
uint8_t curr_be = (submac->backoff_mask + 1) >> 1;
|
|
if (curr_be < submac->be.max) {
|
|
submac->backoff_mask = (submac->backoff_mask << 1) | 1;
|
|
}
|
|
}
|
|
|
|
while (ieee802154_radio_request_transmit(dev) == -EBUSY) {}
|
|
return IEEE802154_FSM_STATE_TX;
|
|
case IEEE802154_FSM_EV_RX_DONE:
|
|
case IEEE802154_FSM_EV_CRC_ERROR:
|
|
/* This might happen in case there's a race condition between ACK_TIMEOUT
|
|
* and TX_DONE. We simply discard the frame and keep the state as
|
|
* it is
|
|
*/
|
|
ieee802154_radio_read(&submac->dev, NULL, 0, NULL);
|
|
return IEEE802154_FSM_STATE_PREPARE;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return IEEE802154_FSM_STATE_INVALID;
|
|
}
|
|
|
|
static ieee802154_fsm_state_t _fsm_state_tx_process_tx_done(ieee802154_submac_t *submac,
|
|
ieee802154_tx_info_t *info)
|
|
{
|
|
ieee802154_dev_t *dev = &submac->dev;
|
|
int res;
|
|
|
|
/* This is required to prevent unused variable warnings */
|
|
(void) res;
|
|
|
|
switch (info->status) {
|
|
case TX_STATUS_FRAME_PENDING:
|
|
assert(_does_handle_ack(&submac->dev));
|
|
/* FALL-THRU */
|
|
case TX_STATUS_SUCCESS:
|
|
submac->csma_retries_nb = 0;
|
|
/* If the radio handles ACK, the TX_DONE event marks completion of
|
|
* the transmission procedure. Report TX done to the upper layer */
|
|
if (_does_handle_ack(&submac->dev) || !submac->wait_for_ack) {
|
|
return _tx_end(submac, info->status, info);
|
|
}
|
|
/* If the radio doesn't handle ACK, set the transceiver state to RX_ON
|
|
* and enable the ACK filter */
|
|
else {
|
|
ieee802154_radio_set_frame_filter_mode(dev, IEEE802154_FILTER_ACK_ONLY);
|
|
res = ieee802154_radio_set_rx(dev);
|
|
assert (res >= 0);
|
|
|
|
/* Handle ACK reception */
|
|
ieee802154_submac_ack_timer_set(submac, ACK_TIMEOUT_US);
|
|
return IEEE802154_FSM_STATE_WAIT_FOR_ACK;
|
|
}
|
|
break;
|
|
case TX_STATUS_NO_ACK:
|
|
assert(_does_handle_ack(&submac->dev));
|
|
submac->csma_retries_nb = 0;
|
|
return _handle_tx_no_ack(submac);
|
|
case TX_STATUS_MEDIUM_BUSY:
|
|
/* If radio has retransmissions or CSMA-CA, this means the CSMA-CA
|
|
* procedure failed. We finish the SubMAC operation and report
|
|
* medium busy
|
|
*/
|
|
if (_does_handle_csma(&submac->dev)
|
|
|| submac->csma_retries_nb++ >= submac->csma_retries) {
|
|
return _tx_end(submac, info->status, info);
|
|
}
|
|
/* Otherwise, this is a failed CCA attempt. Proceed with CSMA-CA */
|
|
else {
|
|
/* The HAL should guarantee that's still possible to transmit
|
|
* in the current state, since the radio is still in TX_ON.
|
|
* Therefore, this is valid */
|
|
ieee802154_submac_bh_request(submac);
|
|
return IEEE802154_FSM_STATE_PREPARE;
|
|
}
|
|
}
|
|
return IEEE802154_FSM_STATE_INVALID;
|
|
}
|
|
|
|
static ieee802154_fsm_state_t _fsm_state_tx(ieee802154_submac_t *submac, ieee802154_fsm_ev_t ev)
|
|
{
|
|
ieee802154_tx_info_t info;
|
|
int res;
|
|
|
|
/* This is required to prevent unused variable warnings */
|
|
(void) res;
|
|
|
|
switch (ev) {
|
|
case IEEE802154_FSM_EV_TX_DONE:
|
|
res = ieee802154_radio_confirm_transmit(&submac->dev, &info);
|
|
assert(res >= 0);
|
|
return _fsm_state_tx_process_tx_done(submac, &info);
|
|
case IEEE802154_FSM_EV_RX_DONE:
|
|
case IEEE802154_FSM_EV_CRC_ERROR:
|
|
/* This might happen in case there's a race condition between ACK_TIMEOUT
|
|
* and TX_DONE. We simply discard the frame and keep the state as
|
|
* it is
|
|
*/
|
|
ieee802154_radio_read(&submac->dev, NULL, 0, NULL);
|
|
return IEEE802154_FSM_STATE_TX;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return IEEE802154_FSM_STATE_INVALID;
|
|
}
|
|
|
|
static ieee802154_fsm_state_t _fsm_state_wait_for_ack(ieee802154_submac_t *submac,
|
|
ieee802154_fsm_ev_t ev)
|
|
{
|
|
uint8_t ack[3];
|
|
|
|
switch (ev) {
|
|
case IEEE802154_FSM_EV_RX_DONE:
|
|
assert(!ieee802154_radio_has_irq_ack_timeout(&submac->dev));
|
|
if (ieee802154_radio_read(&submac->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);
|
|
ieee802154_radio_set_frame_filter_mode(&submac->dev, IEEE802154_FILTER_ACCEPT);
|
|
return _tx_end(submac, fp ? TX_STATUS_FRAME_PENDING : TX_STATUS_SUCCESS,
|
|
&tx_info);
|
|
}
|
|
return IEEE802154_FSM_STATE_WAIT_FOR_ACK;
|
|
case IEEE802154_FSM_EV_CRC_ERROR:
|
|
/* Received invalid ACK. Drop frame */
|
|
ieee802154_radio_read(&submac->dev, NULL, 0, NULL);
|
|
return IEEE802154_FSM_STATE_WAIT_FOR_ACK;
|
|
case IEEE802154_FSM_EV_ACK_TIMEOUT:
|
|
return _handle_tx_no_ack(submac);
|
|
default:
|
|
break;
|
|
}
|
|
return IEEE802154_FSM_STATE_INVALID;
|
|
}
|
|
|
|
ieee802154_fsm_state_t ieee802154_submac_process_ev(ieee802154_submac_t *submac,
|
|
ieee802154_fsm_ev_t ev)
|
|
{
|
|
ieee802154_fsm_state_t new_state;
|
|
|
|
switch (submac->fsm_state) {
|
|
case IEEE802154_FSM_STATE_RX:
|
|
new_state = _fsm_state_rx(submac, ev);
|
|
break;
|
|
case IEEE802154_FSM_STATE_IDLE:
|
|
new_state = _fsm_state_idle(submac, ev);
|
|
break;
|
|
case IEEE802154_FSM_STATE_PREPARE:
|
|
new_state = _fsm_state_prepare(submac, ev);
|
|
break;
|
|
case IEEE802154_FSM_STATE_TX:
|
|
new_state = _fsm_state_tx(submac, ev);
|
|
break;
|
|
case IEEE802154_FSM_STATE_WAIT_FOR_ACK:
|
|
new_state = _fsm_state_wait_for_ack(submac, ev);
|
|
break;
|
|
default:
|
|
new_state = IEEE802154_FSM_STATE_INVALID;
|
|
}
|
|
|
|
if (new_state == IEEE802154_FSM_STATE_INVALID) {
|
|
_print_debug(submac->fsm_state, new_state, ev);
|
|
assert(false);
|
|
}
|
|
submac->fsm_state = new_state;
|
|
return submac->fsm_state;
|
|
}
|
|
|
|
int ieee802154_send(ieee802154_submac_t *submac, const iolist_t *iolist)
|
|
{
|
|
ieee802154_fsm_state_t current_state = submac->fsm_state;
|
|
|
|
if (current_state != IEEE802154_FSM_STATE_RX && current_state != IEEE802154_FSM_STATE_IDLE) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
if (iolist == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
uint8_t *buf = iolist->iol_base;
|
|
bool cnf = buf[0] & IEEE802154_FCF_ACK_REQ;
|
|
|
|
submac->wait_for_ack = cnf;
|
|
submac->psdu = iolist;
|
|
submac->retrans = 0;
|
|
submac->csma_retries_nb = 0;
|
|
submac->backoff_mask = (1 << submac->be.min) - 1;
|
|
|
|
if (ieee802154_submac_process_ev(submac, IEEE802154_FSM_EV_REQUEST_TX)
|
|
!= IEEE802154_FSM_STATE_PREPARE) {
|
|
return -EBUSY;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ieee802154_submac_init(ieee802154_submac_t *submac, const network_uint16_t *short_addr,
|
|
const eui64_t *ext_addr)
|
|
{
|
|
ieee802154_dev_t *dev = &submac->dev;
|
|
|
|
submac->fsm_state = IEEE802154_FSM_STATE_RX;
|
|
|
|
int res;
|
|
|
|
if ((res = ieee802154_radio_request_on(dev)) < 0) {
|
|
return res;
|
|
}
|
|
|
|
/* generate EUI-64 and short address */
|
|
memcpy(&submac->ext_addr, ext_addr, sizeof(eui64_t));
|
|
memcpy(&submac->short_addr, short_addr, sizeof(network_uint16_t));
|
|
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;
|
|
}
|
|
|
|
/* Get supported PHY modes */
|
|
int supported_phy_modes = ieee802154_radio_get_phy_modes(dev);
|
|
|
|
assert(supported_phy_modes != 0);
|
|
|
|
uint32_t default_phy_cap = ieee802154_phy_mode_to_cap(CONFIG_IEEE802154_DEFAULT_PHY_MODE);
|
|
|
|
/* Check if configuration provides valid PHY */
|
|
if (CONFIG_IEEE802154_DEFAULT_PHY_MODE != IEEE802154_PHY_DISABLED &&
|
|
(supported_phy_modes & default_phy_cap)) {
|
|
/* Check if default PHY is supported */
|
|
submac->phy_mode = CONFIG_IEEE802154_DEFAULT_PHY_MODE;
|
|
}
|
|
else {
|
|
/* Get first set bit, and use it as the default,
|
|
*
|
|
* by this order, the priority is defined on the ieee802154_rf_caps_t
|
|
* definition, first IEEE 802.15.4-2006 PHY modes, then
|
|
* IEEE 802.15.4g-2012 PHY modes. */
|
|
unsigned bit = bitarithm_lsb(supported_phy_modes);
|
|
|
|
submac->phy_mode = ieee802154_cap_to_phy_mode(1 << bit);
|
|
}
|
|
|
|
/* If the radio is still not in TRX_OFF state, spin */
|
|
while (ieee802154_radio_confirm_on(dev) == -EAGAIN) {}
|
|
|
|
/* Configure address filter */
|
|
ieee802154_radio_config_addr_filter(dev, IEEE802154_AF_SHORT_ADDR, &submac->short_addr);
|
|
ieee802154_radio_config_addr_filter(dev, IEEE802154_AF_EXT_ADDR, &submac->ext_addr);
|
|
ieee802154_radio_config_addr_filter(dev, IEEE802154_AF_PANID, &submac->panid);
|
|
|
|
/* Configure PHY settings (mode, channel, TX power) */
|
|
ieee802154_phy_conf_t conf =
|
|
{ .phy_mode = submac->phy_mode,
|
|
.channel = submac->channel_num,
|
|
.page = submac->channel_page,
|
|
.pow = submac->tx_pow };
|
|
|
|
ieee802154_radio_config_phy(dev, &conf);
|
|
ieee802154_radio_set_cca_threshold(dev,
|
|
CONFIG_IEEE802154_CCA_THRESH_DEFAULT);
|
|
assert(res >= 0);
|
|
|
|
while (ieee802154_radio_set_rx(dev) < 0) {}
|
|
|
|
return res;
|
|
}
|
|
|
|
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 =
|
|
{ .phy_mode = submac->phy_mode,
|
|
.channel = channel_num,
|
|
.page = channel_page,
|
|
.pow = tx_pow };
|
|
int res;
|
|
ieee802154_fsm_state_t current_state = submac->fsm_state;
|
|
|
|
/* Changing state can be only performed on IDLE or RX state */
|
|
if (current_state != IEEE802154_FSM_STATE_RX && current_state != IEEE802154_FSM_STATE_IDLE) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* If the radio is listening, turn it off first */
|
|
if (current_state == IEEE802154_FSM_STATE_RX) {
|
|
if ((res = ieee802154_radio_request_set_idle(dev, false)) < 0) {
|
|
return res;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
while (ieee802154_radio_confirm_set_idle(dev) == -EAGAIN) {}
|
|
|
|
/* Go back to RX if needed */
|
|
if (current_state == IEEE802154_FSM_STATE_RX) {
|
|
res = ieee802154_radio_set_rx(dev);
|
|
assert (res >= 0);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
int ieee802154_set_rx(ieee802154_submac_t *submac)
|
|
{
|
|
ieee802154_fsm_state_t current_state = submac->fsm_state;
|
|
ieee802154_fsm_state_t next_state;
|
|
int res = -EBUSY;
|
|
|
|
switch (current_state) {
|
|
case IEEE802154_FSM_STATE_RX:
|
|
res = -EALREADY;
|
|
break;
|
|
case IEEE802154_FSM_STATE_IDLE:
|
|
next_state = ieee802154_submac_process_ev(submac,
|
|
IEEE802154_FSM_EV_REQUEST_SET_RX_ON);
|
|
if (next_state == IEEE802154_FSM_STATE_RX) {
|
|
res = 0;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
int ieee802154_set_idle(ieee802154_submac_t *submac)
|
|
{
|
|
ieee802154_fsm_state_t current_state = submac->fsm_state;
|
|
ieee802154_fsm_state_t next_state;
|
|
int res = -EBUSY;
|
|
|
|
switch (current_state) {
|
|
case IEEE802154_FSM_STATE_IDLE:
|
|
res = -EALREADY;
|
|
break;
|
|
case IEEE802154_FSM_STATE_RX:
|
|
next_state = ieee802154_submac_process_ev(submac,
|
|
IEEE802154_FSM_EV_REQUEST_SET_IDLE);
|
|
if (next_state == IEEE802154_FSM_STATE_IDLE) {
|
|
res = 0;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
/** @} */
|