mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-17 05:32:45 +01:00
ieee802154/submac: reimplement using FSM
This commit is contained in:
parent
0fb55b21c5
commit
8f97f73453
@ -41,6 +41,7 @@ extern "C" {
|
||||
#define NETDEV_SUBMAC_FLAGS_TX_DONE (1 << 1) /**< Flag for TX Done event */
|
||||
#define NETDEV_SUBMAC_FLAGS_RX_DONE (1 << 2) /**< Flag for RX Done event */
|
||||
#define NETDEV_SUBMAC_FLAGS_CRC_ERROR (1 << 3) /**< Flag for CRC ERROR event */
|
||||
#define NETDEV_SUBMAC_FLAGS_BH_REQUEST (1 << 4) /**< Flag for Bottom Half request event */
|
||||
|
||||
/**
|
||||
* @brief IEEE 802.15.4 SubMAC netdev descriptor
|
||||
@ -51,6 +52,8 @@ typedef struct {
|
||||
xtimer_t ack_timer; /**< xtimer descriptor for the ACK timeout timer */
|
||||
int isr_flags; /**< netdev submac @ref NETDEV_EVENT_ISR flags */
|
||||
int8_t retrans; /**< number of frame retransmissions of the last TX */
|
||||
bool dispatch; /**< whether an event should be dispatched or not */
|
||||
netdev_event_t ev; /**< event to be dispatched */
|
||||
} netdev_ieee802154_submac_t;
|
||||
|
||||
/**
|
||||
|
@ -22,7 +22,6 @@ static const netdev_driver_t netdev_submac_driver;
|
||||
|
||||
static void _ack_timeout(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
netdev_ieee802154_submac_t *netdev_submac = arg;
|
||||
netdev_t *netdev = arg;
|
||||
|
||||
@ -33,23 +32,12 @@ static void _ack_timeout(void *arg)
|
||||
|
||||
static netopt_state_t _get_submac_state(ieee802154_submac_t *submac)
|
||||
{
|
||||
ieee802154_submac_state_t state = ieee802154_get_state(submac);
|
||||
|
||||
netopt_state_t netopt_state;
|
||||
switch (state) {
|
||||
case IEEE802154_STATE_OFF:
|
||||
netopt_state = NETOPT_STATE_SLEEP;
|
||||
break;
|
||||
case IEEE802154_STATE_IDLE:
|
||||
netopt_state = NETOPT_STATE_STANDBY;
|
||||
break;
|
||||
case IEEE802154_STATE_LISTEN:
|
||||
default:
|
||||
netopt_state = NETOPT_STATE_IDLE;
|
||||
break;
|
||||
if (ieee802154_submac_state_is_idle(submac)) {
|
||||
return NETOPT_STATE_SLEEP;
|
||||
}
|
||||
else {
|
||||
return NETOPT_STATE_IDLE;
|
||||
}
|
||||
|
||||
return netopt_state;
|
||||
}
|
||||
|
||||
static int _get(netdev_t *netdev, netopt_t opt, void *value, size_t max_len)
|
||||
@ -82,12 +70,12 @@ static int _get(netdev_t *netdev, netopt_t opt, void *value, size_t max_len)
|
||||
static int _set_submac_state(ieee802154_submac_t *submac, netopt_state_t state)
|
||||
{
|
||||
switch (state) {
|
||||
case NETOPT_STATE_STANDBY:
|
||||
return ieee802154_set_state(submac, IEEE802154_STATE_IDLE);
|
||||
case NETOPT_STATE_SLEEP:
|
||||
return ieee802154_set_state(submac, IEEE802154_STATE_OFF);
|
||||
return ieee802154_set_idle(submac);
|
||||
break;
|
||||
case NETOPT_STATE_IDLE:
|
||||
return ieee802154_set_state(submac, IEEE802154_STATE_LISTEN);
|
||||
return ieee802154_set_rx(submac);
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
@ -135,6 +123,18 @@ static int _set(netdev_t *netdev, netopt_t opt, const void *value,
|
||||
opt, value, value_len);
|
||||
}
|
||||
|
||||
void ieee802154_submac_bh_request(ieee802154_submac_t *submac)
|
||||
{
|
||||
netdev_ieee802154_submac_t *netdev_submac = container_of(submac,
|
||||
netdev_ieee802154_submac_t,
|
||||
submac);
|
||||
|
||||
netdev_t *netdev = &netdev_submac->dev.netdev;
|
||||
netdev_submac->isr_flags |= NETDEV_SUBMAC_FLAGS_BH_REQUEST;
|
||||
|
||||
netdev->event_callback(netdev, NETDEV_EVENT_ISR);
|
||||
}
|
||||
|
||||
void ieee802154_submac_ack_timer_set(ieee802154_submac_t *submac, uint16_t us)
|
||||
{
|
||||
netdev_ieee802154_submac_t *netdev_submac = container_of(submac,
|
||||
@ -161,7 +161,13 @@ static int _send(netdev_t *netdev, const iolist_t *pkt)
|
||||
dev);
|
||||
ieee802154_submac_t *submac = &netdev_submac->submac;
|
||||
|
||||
return ieee802154_send(submac, pkt);
|
||||
int res = ieee802154_send(submac, pkt);
|
||||
if (res >= 0) {
|
||||
/* HACK: Used to mark a transmission when called
|
||||
* inside the TX Done callback */
|
||||
netdev_submac->ev = NETDEV_EVENT_TX_STARTED;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void _isr(netdev_t *netdev)
|
||||
@ -172,12 +178,17 @@ static void _isr(netdev_t *netdev)
|
||||
dev);
|
||||
ieee802154_submac_t *submac = &netdev_submac->submac;
|
||||
|
||||
bool can_dispatch = true;
|
||||
do {
|
||||
irq_disable();
|
||||
int flags = netdev_submac->isr_flags;
|
||||
netdev_submac->isr_flags = 0;
|
||||
irq_enable();
|
||||
|
||||
if (flags & NETDEV_SUBMAC_FLAGS_BH_REQUEST) {
|
||||
ieee802154_submac_bh_process(submac);
|
||||
}
|
||||
|
||||
if (flags & NETDEV_SUBMAC_FLAGS_ACK_TIMEOUT) {
|
||||
ieee802154_submac_ack_timeout_fired(&netdev_submac->submac);
|
||||
}
|
||||
@ -193,7 +204,33 @@ static void _isr(netdev_t *netdev)
|
||||
if (flags & NETDEV_SUBMAC_FLAGS_CRC_ERROR) {
|
||||
ieee802154_submac_crc_error_cb(submac);
|
||||
}
|
||||
|
||||
if (flags) {
|
||||
can_dispatch = false;
|
||||
}
|
||||
|
||||
} while (netdev_submac->isr_flags != 0);
|
||||
|
||||
if (netdev_submac->dispatch) {
|
||||
/* The SubMAC will not generate further events after calling TX Done
|
||||
* or RX Done, but there might be pending ISR events that might not be
|
||||
* caught by the previous loop.
|
||||
* This should be safe to make sure that all events are cached */
|
||||
if (!can_dispatch) {
|
||||
netdev->event_callback(netdev, NETDEV_EVENT_ISR);
|
||||
return;
|
||||
}
|
||||
netdev_submac->dispatch = false;
|
||||
/* TODO: Prevent race condition when state goes to PREPARE */
|
||||
netdev->event_callback(netdev, netdev_submac->ev);
|
||||
/* HACK: the TX_STARTED event is used to indicate a frame was
|
||||
* sent during the event callback.
|
||||
* If no frame was sent go back to RX */
|
||||
if (netdev_submac->ev != NETDEV_EVENT_TX_STARTED) {
|
||||
ieee802154_set_rx(submac);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int _recv(netdev_t *netdev, void *buf, size_t len, void *info)
|
||||
@ -231,24 +268,24 @@ static void submac_tx_done(ieee802154_submac_t *submac, int status,
|
||||
netdev_ieee802154_submac_t *netdev_submac = container_of(submac,
|
||||
netdev_ieee802154_submac_t,
|
||||
submac);
|
||||
netdev_t *netdev = &netdev_submac->dev.netdev;
|
||||
|
||||
if (info) {
|
||||
netdev_submac->retrans = info->retrans;
|
||||
}
|
||||
|
||||
netdev_submac->dispatch = true;
|
||||
|
||||
switch (status) {
|
||||
case TX_STATUS_SUCCESS:
|
||||
netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE);
|
||||
netdev_submac->ev = NETDEV_EVENT_TX_COMPLETE;
|
||||
break;
|
||||
case TX_STATUS_FRAME_PENDING:
|
||||
netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE_DATA_PENDING);
|
||||
netdev_submac->ev = NETDEV_EVENT_TX_COMPLETE_DATA_PENDING;
|
||||
break;
|
||||
case TX_STATUS_MEDIUM_BUSY:
|
||||
netdev->event_callback(netdev, NETDEV_EVENT_TX_MEDIUM_BUSY);
|
||||
netdev_submac->ev = NETDEV_EVENT_TX_MEDIUM_BUSY;
|
||||
break;
|
||||
case TX_STATUS_NO_ACK:
|
||||
netdev->event_callback(netdev, NETDEV_EVENT_TX_NOACK);
|
||||
netdev_submac->ev = NETDEV_EVENT_TX_NOACK;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -260,9 +297,8 @@ static void submac_rx_done(ieee802154_submac_t *submac)
|
||||
netdev_ieee802154_submac_t *netdev_submac = container_of(submac,
|
||||
netdev_ieee802154_submac_t,
|
||||
submac);
|
||||
netdev_t *netdev = &netdev_submac->dev.netdev;
|
||||
|
||||
netdev->event_callback(netdev, NETDEV_EVENT_RX_COMPLETE);
|
||||
netdev_submac->dispatch = true;
|
||||
netdev_submac->ev = NETDEV_EVENT_RX_COMPLETE;
|
||||
}
|
||||
|
||||
static const ieee802154_submac_cb_t _cb = {
|
||||
|
@ -20,6 +20,85 @@
|
||||
* - Maintaining part of the MAC Information Base, e.g IEEE 802.15.4 addresses,
|
||||
* channel settings, CSMA-CA params, etc.
|
||||
*
|
||||
* The SubMAC defines the following state machine:
|
||||
*
|
||||
* +--------+ +--------+ +--------+
|
||||
* | |------->| | | |
|
||||
* | RX | |PREPARE |<--->| TX |
|
||||
* | | +--->| | | |
|
||||
* +--------+ | +--------+ +--------+
|
||||
* ^ | ^ |
|
||||
* | | | |
|
||||
* | | | |
|
||||
* | | +--------+ |
|
||||
* | | | | v
|
||||
* | | |WAIT FOR|<--------+
|
||||
* | | | ACK | |
|
||||
* | | +--------+ |
|
||||
* | | | |
|
||||
* | | | |
|
||||
* | | v |
|
||||
* | | +--------+ |
|
||||
* | +-----| | |
|
||||
* | | IDLE | |
|
||||
* +------------->| |<-------+
|
||||
* +--------+
|
||||
*
|
||||
* - IDLE: The transceiver is off and therefore cannot receive frames. Sending
|
||||
* frames might be triggered using @ref ieee802154_send. The next SubMAC
|
||||
* state would be PREPARE.
|
||||
* - RX: The device is ready to receive frames. In case the SubMAC receives a
|
||||
* frame it will call @ref ieee802154_submac_cb_t::rx_done and immediately go
|
||||
* to IDLE. Same as the IDLE state, it's possible
|
||||
* to trigger frames using @ref ieee802154_send.
|
||||
* - PREPARE: The frame is already in the framebuffer and waiting to be
|
||||
* transmitted. This state might handle CSMA-CA backoff timer in case the
|
||||
* device doesn't support it. The SubMAC will then request the transmission
|
||||
* and go immediately to the TX state.
|
||||
* - TX: The frame was already sent and it's waiting for the TX DONE event from
|
||||
* the radio. The SubMAC might call @ref ieee802154_submac_cb_t::tx_done if
|
||||
* any of the following criteria are meet:
|
||||
* - The transmitted frame didn't request ACK
|
||||
* - The radio already handles retransmissions
|
||||
* - WAIT FOR ACK: The SubMAC is waiting for an ACK frame.
|
||||
* In case a valid ACK frame is received, the SubMAC will
|
||||
* either to IDLE.
|
||||
* In case the ACK frame is invalid or there's an ACK timeout event
|
||||
* (either triggered by the radio or a timer), the SubMAC goes to either
|
||||
* IDLE if there are no more retransmissions left or no more CSMA-CA
|
||||
* retries or PREPARE otherwise.
|
||||
*
|
||||
* The events that trigger state machine changes are defined in
|
||||
* @ref ieee802154_fsm_state_t
|
||||
*
|
||||
* The following events are valid for each state:
|
||||
*
|
||||
* +---------------+----+-------+---------+----+--------------+
|
||||
* | Event/State | RX | IDLE | PREPARE | TX | WAIT FOR ACK |
|
||||
* +---------------+----+-------+---------+----+--------------+
|
||||
* | TX_DONE | - | - | - | X | - |
|
||||
* | RX_DONE | X | X* | X* | X* | X |
|
||||
* | CRC_ERROR | X | X* | X* | X* | X |
|
||||
* | ACK_TIMEOUT | - | - | - | - | X |
|
||||
* | BH | - | - | X | - | - |
|
||||
* | REQ_TX | X | X | - | - | - |
|
||||
* | REQ_SET_RX_ON | - | X | - | - | - |
|
||||
* | REQ_SET_IDLE | X | - | - | - | - |
|
||||
* +---------------+----+-------+---------+----+--------------+
|
||||
* *: RX_DONE and CRC_ERROR during these events might be a race condition
|
||||
* between the ACK Timer and the radios RX_DONE event. If this happens, the
|
||||
* SubMAC will react accordingly
|
||||
*
|
||||
* Unexpected events will be reported and asserted.
|
||||
*
|
||||
* The upper layer needs to implement the following callbacks:
|
||||
*
|
||||
* - @ref ieee802154_submac_cb_t::rx_done.
|
||||
* - @ref ieee802154_submac_cb_t::tx_done.
|
||||
* - @ref ieee802154_submac_ack_timer_set
|
||||
* - @ref ieee802154_submac_ack_timer_cancel
|
||||
* - @ref ieee802154_submac_bh_request
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @author José I. Alamos <jose.alamos@haw-hamburg.de>
|
||||
@ -31,7 +110,9 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "assert.h"
|
||||
|
||||
#include "net/ieee802154.h"
|
||||
#include "net/ieee802154/radio.h"
|
||||
@ -43,29 +124,6 @@ extern "C" {
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@ -74,10 +132,11 @@ 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.
|
||||
* frame is ready to be fetched from the device. Use @ref
|
||||
* ieee802154_read_frame and/or @ref ieee802154_get_frame_length for this
|
||||
* purpose.
|
||||
*
|
||||
* @post If @ref ieee802154_submac_t::state is @ref IEEE802154_STATE_LISTEN, the
|
||||
* SubMAC is ready to receive frames
|
||||
* The SubMAC will automatically go to IDLE.
|
||||
*
|
||||
* @note ACK frames are automatically handled and discarded by the SubMAC.
|
||||
* @param[in] submac pointer to the SubMAC descriptor
|
||||
@ -89,8 +148,7 @@ typedef struct {
|
||||
* This function is called from the SubMAC to indicate that the TX
|
||||
* procedure finished.
|
||||
*
|
||||
* @pre If @ref ieee802154_submac_t::state is @ref IEEE802154_STATE_LISTEN, the
|
||||
* SubMAC is ready to receive frames.
|
||||
* The SubMAC will automatically go to IDLE.
|
||||
*
|
||||
* @param[in] submac pointer to the SubMAC descriptor
|
||||
* @param[out] info TX information associated to the transmission (status,
|
||||
@ -100,6 +158,34 @@ typedef struct {
|
||||
ieee802154_tx_info_t *info);
|
||||
} ieee802154_submac_cb_t;
|
||||
|
||||
/**
|
||||
* @brief Internal SubMAC FSM state machine states
|
||||
*/
|
||||
typedef enum {
|
||||
IEEE802154_FSM_STATE_INVALID, /**< Invalid state */
|
||||
IEEE802154_FSM_STATE_RX, /**< SubMAC is ready to receive frames */
|
||||
IEEE802154_FSM_STATE_IDLE, /**< The transceiver is off */
|
||||
IEEE802154_FSM_STATE_PREPARE, /**< The SubMAC is preparing the next transmission */
|
||||
IEEE802154_FSM_STATE_TX, /**< The SubMAC is currently transmitting a frame */
|
||||
IEEE802154_FSM_STATE_WAIT_FOR_ACK, /**< The SubMAC is waiting for an ACK frame */
|
||||
IEEE802154_FSM_STATE_NUMOF, /**< Number of SubMAC FSM states */
|
||||
} ieee802154_fsm_state_t;
|
||||
|
||||
/**
|
||||
* @brief Internal SubMAC FSM state machine events
|
||||
*/
|
||||
typedef enum {
|
||||
IEEE802154_FSM_EV_TX_DONE, /**< Radio reports frame was sent */
|
||||
IEEE802154_FSM_EV_RX_DONE, /**< Radio reports frame was received */
|
||||
IEEE802154_FSM_EV_CRC_ERROR, /**< Radio reports frame was received but CRC failed */
|
||||
IEEE802154_FSM_EV_ACK_TIMEOUT, /**< ACK timer fired */
|
||||
IEEE802154_FSM_EV_BH, /**< The Bottom Half should process an event */
|
||||
IEEE802154_FSM_EV_REQUEST_TX, /**< The upper layer requested to transmit a frame */
|
||||
IEEE802154_FSM_EV_REQUEST_SET_RX_ON, /**< The upper layer requested to go to RX */
|
||||
IEEE802154_FSM_EV_REQUEST_SET_IDLE, /**< The upper layer requested to go to IDLE */
|
||||
IEEE802154_FSM_EV_NUMOF, /**< Number of SubMAC FSM events */
|
||||
} ieee802154_fsm_ev_t;
|
||||
|
||||
/**
|
||||
* @brief IEEE 802.15.4 SubMAC descriptor
|
||||
*/
|
||||
@ -110,7 +196,6 @@ struct ieee802154_submac {
|
||||
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 */
|
||||
@ -119,33 +204,11 @@ struct ieee802154_submac {
|
||||
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 */
|
||||
ieee802154_fsm_state_t fsm_state; /**< State of the SubMAC */
|
||||
ieee802154_phy_mode_t phy_mode; /**< IEEE 802.15.4 PHY mode */
|
||||
const iolist_t *psdu; /**< stores the current PSDU */
|
||||
};
|
||||
|
||||
/**
|
||||
* @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
|
||||
*
|
||||
@ -157,7 +220,9 @@ int ieee802154_set_state(ieee802154_submac_t *submac, ieee802154_submac_state_t
|
||||
* @param[in] iolist pointer to the PSDU frame (without FCS)
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return negative errno on error
|
||||
* @return -EBUSY if the SubMAC is not in RX or IDLE state or if called inside
|
||||
* @ref ieee802154_submac_cb_t::rx_done or
|
||||
* @ref ieee802154_submac_cb_t::tx_done
|
||||
*/
|
||||
int ieee802154_send(ieee802154_submac_t *submac, const iolist_t *iolist);
|
||||
|
||||
@ -252,6 +317,9 @@ static inline ieee802154_phy_mode_t ieee802154_get_phy_mode(
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -ENOTSUP if the PHY settings are not supported
|
||||
* @return -EBUSY if the SubMAC is not in RX or IDLE state or if called inside
|
||||
* @ref ieee802154_submac_cb_t::rx_done or
|
||||
* @ref ieee802154_submac_cb_t::tx_done
|
||||
* @return negative errno on error
|
||||
*/
|
||||
int ieee802154_set_phy_conf(ieee802154_submac_t *submac, uint16_t channel_num,
|
||||
@ -267,6 +335,9 @@ int ieee802154_set_phy_conf(ieee802154_submac_t *submac, uint16_t channel_num,
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -ENOTSUP if the channel number is not supported
|
||||
* @return -EBUSY if the SubMAC is not in RX or IDLE state or if called inside
|
||||
* @ref ieee802154_submac_cb_t::rx_done or
|
||||
* @ref ieee802154_submac_cb_t::tx_done
|
||||
* @return negative errno on error
|
||||
*/
|
||||
static inline int ieee802154_set_channel_number(ieee802154_submac_t *submac,
|
||||
@ -286,6 +357,9 @@ static inline int ieee802154_set_channel_number(ieee802154_submac_t *submac,
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -ENOTSUP if the channel page is not supported
|
||||
* @return -EBUSY if the SubMAC is not in RX or IDLE state or if called inside
|
||||
* @ref ieee802154_submac_cb_t::rx_done or
|
||||
* @ref ieee802154_submac_cb_t::tx_done
|
||||
* @return negative errno on error
|
||||
*/
|
||||
static inline int ieee802154_set_channel_page(ieee802154_submac_t *submac,
|
||||
@ -305,6 +379,9 @@ static inline int ieee802154_set_channel_page(ieee802154_submac_t *submac,
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -ENOTSUP if the transmission power is not supported
|
||||
* @return -EBUSY if the SubMAC is not in RX or IDLE state or if called inside
|
||||
* @ref ieee802154_submac_cb_t::rx_done or
|
||||
* @ref ieee802154_submac_cb_t::tx_done
|
||||
* @return negative errno on error
|
||||
*/
|
||||
static inline int ieee802154_set_tx_power(ieee802154_submac_t *submac,
|
||||
@ -317,6 +394,9 @@ static inline int ieee802154_set_tx_power(ieee802154_submac_t *submac,
|
||||
/**
|
||||
* @brief Get the received frame length
|
||||
*
|
||||
* @pre this function MUST be called either inside @ref ieee802154_submac_cb_t::rx_done
|
||||
* or in SLEEP state.
|
||||
*
|
||||
* @param[in] submac pointer to the SubMAC
|
||||
*
|
||||
* @return length of the PSDU (excluding FCS length)
|
||||
@ -331,6 +411,9 @@ static inline int ieee802154_get_frame_length(ieee802154_submac_t *submac)
|
||||
*
|
||||
* This functions reads the received PSDU from the device (excluding FCS)
|
||||
*
|
||||
* @pre this function MUST be called either inside @ref ieee802154_submac_cb_t::rx_done
|
||||
* or in SLEEP state.
|
||||
*
|
||||
* @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
|
||||
@ -345,9 +428,64 @@ static inline int ieee802154_read_frame(ieee802154_submac_t *submac, void *buf,
|
||||
return ieee802154_radio_read(&submac->dev, buf, len, info);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the SubMAC to IDLE state.
|
||||
*
|
||||
* Frames won't be received in this state. However, it's still possible to send
|
||||
* frames.
|
||||
*
|
||||
* @param[in] submac pointer to the SubMAC descriptor
|
||||
*
|
||||
* @return success or error code.
|
||||
* @retval 0 on success
|
||||
* @retval -EBUSY if the SubMAC is currently busy
|
||||
*/
|
||||
int ieee802154_set_idle(ieee802154_submac_t *submac);
|
||||
|
||||
/**
|
||||
* @brief Set the SubMAC to RX state
|
||||
*
|
||||
* During this state the SubMAC accepts incoming frames.
|
||||
*
|
||||
* @param[in] submac pointer to the SubMAC descriptor
|
||||
*
|
||||
* @return success or error code.
|
||||
* @retval 0 on success
|
||||
* @retval -EBUSY if the SubMAC is currently busy
|
||||
*/
|
||||
int ieee802154_set_rx(ieee802154_submac_t *submac);
|
||||
|
||||
/**
|
||||
* @brief Check whether the SubMAC is in RX state
|
||||
*
|
||||
* @param[in] submac pointer to the SubMAC descriptor
|
||||
*
|
||||
* @retval true if the SubMAC is in RX state
|
||||
* @retval false otherwise
|
||||
*/
|
||||
static inline bool ieee802154_submac_state_is_rx(ieee802154_submac_t *submac)
|
||||
{
|
||||
return submac->fsm_state == IEEE802154_FSM_STATE_RX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether the SubMAC is in IDLE state
|
||||
*
|
||||
* @param[in] submac pointer to the SubMAC descriptor
|
||||
*
|
||||
* @retval true if the SubMAC is in IDLE state
|
||||
* @retval false otherwise
|
||||
*/
|
||||
static inline bool ieee802154_submac_state_is_idle(ieee802154_submac_t *submac)
|
||||
{
|
||||
return submac->fsm_state == IEEE802154_FSM_STATE_IDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Init the IEEE 802.15.4 SubMAC
|
||||
*
|
||||
* The SubMAC state machine starts in RX state.
|
||||
*
|
||||
* @param[in] submac pointer to the SubMAC descriptor
|
||||
* @param[in] short_addr pointer to the IEEE 802.15.4 short address
|
||||
* @param[in] ext_addr pointer to the IEEE 802.15.4 extended address
|
||||
@ -378,6 +516,28 @@ extern void ieee802154_submac_ack_timer_set(ieee802154_submac_t *submac,
|
||||
*/
|
||||
extern void ieee802154_submac_ack_timer_cancel(ieee802154_submac_t *submac);
|
||||
|
||||
/**
|
||||
* @brief @ref ieee802154_submac_bh_process should be called as soon as possible.
|
||||
*
|
||||
* @note This function should be implemented by the user of the SubMAC.
|
||||
*
|
||||
* @param[in] submac pointer to the SubMAC descriptor
|
||||
*/
|
||||
extern void ieee802154_submac_bh_request(ieee802154_submac_t *submac);
|
||||
|
||||
/**
|
||||
* @brief Process an FSM event
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @param[in] submac pointer to the SubMAC descriptor
|
||||
* @param[in] ev the event to be processed
|
||||
*
|
||||
* @return Next FSM event
|
||||
*/
|
||||
ieee802154_fsm_state_t ieee802154_submac_process_ev(ieee802154_submac_t *submac,
|
||||
ieee802154_fsm_ev_t ev);
|
||||
|
||||
/**
|
||||
* @brief Indicate the SubMAC that the ACK timeout fired.
|
||||
*
|
||||
@ -387,28 +547,50 @@ extern void ieee802154_submac_ack_timer_cancel(ieee802154_submac_t *submac);
|
||||
*
|
||||
* @param[in] submac pointer to the SubMAC descriptor
|
||||
*/
|
||||
void ieee802154_submac_ack_timeout_fired(ieee802154_submac_t *submac);
|
||||
static inline void ieee802154_submac_ack_timeout_fired(ieee802154_submac_t *submac)
|
||||
{
|
||||
ieee802154_submac_process_ev(submac, IEEE802154_FSM_EV_ACK_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Indicate the SubMAC that the BH should process an internal event
|
||||
*
|
||||
* @param[in] submac pointer to the SubMAC descriptor
|
||||
*/
|
||||
static inline void ieee802154_submac_bh_process(ieee802154_submac_t *submac)
|
||||
{
|
||||
ieee802154_submac_process_ev(submac, IEEE802154_FSM_EV_BH);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
static inline void ieee802154_submac_rx_done_cb(ieee802154_submac_t *submac)
|
||||
{
|
||||
ieee802154_submac_process_ev(submac, IEEE802154_FSM_EV_RX_DONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Indicate the SubMAC that a frame with invalid CRC was received.
|
||||
*
|
||||
* @param[in] submac pointer to the SubMAC descriptor
|
||||
*/
|
||||
void ieee802154_submac_crc_error_cb(ieee802154_submac_t *submac);
|
||||
static inline void ieee802154_submac_crc_error_cb(ieee802154_submac_t *submac)
|
||||
{
|
||||
ieee802154_submac_process_ev(submac, IEEE802154_FSM_EV_CRC_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
static inline void ieee802154_submac_tx_done_cb(ieee802154_submac_t *submac)
|
||||
{
|
||||
ieee802154_submac_process_ev(submac, IEEE802154_FSM_EV_TX_DONE);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
* @author José I. Alamos <jose.alamos@haw-hamburg.de>
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "net/ieee802154/submac.h"
|
||||
@ -22,12 +23,32 @@
|
||||
#include "luid.h"
|
||||
#include "kernel_defines.h"
|
||||
#include "errno.h"
|
||||
#include <assert.h>
|
||||
|
||||
#define CSMA_SENDER_BACKOFF_PERIOD_UNIT_MS (320U)
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
#define CSMA_SENDER_BACKOFF_PERIOD_UNIT_US (320U)
|
||||
#define ACK_TIMEOUT_US (864U)
|
||||
|
||||
static void _handle_tx_no_ack(ieee802154_submac_t *submac);
|
||||
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 void _req_set_trx_state_wait_busy(ieee802154_dev_t *dev,
|
||||
ieee802154_trx_state_t state)
|
||||
@ -40,25 +61,10 @@ static inline void _req_set_trx_state_wait_busy(ieee802154_dev_t *dev,
|
||||
*/
|
||||
do {
|
||||
res = ieee802154_radio_request_set_trx_state(dev, state);
|
||||
}
|
||||
while (res == -EBUSY);
|
||||
assert(res >= 0);
|
||||
}
|
||||
} while (res == -EBUSY);
|
||||
|
||||
static void _tx_end(ieee802154_submac_t *submac, int status,
|
||||
ieee802154_tx_info_t *info)
|
||||
{
|
||||
ieee802154_dev_t *dev = &submac->dev;
|
||||
|
||||
ieee802154_trx_state_t next_state = submac->state == IEEE802154_STATE_LISTEN
|
||||
? IEEE802154_TRX_STATE_RX_ON
|
||||
: IEEE802154_TRX_STATE_TRX_OFF;
|
||||
|
||||
_req_set_trx_state_wait_busy(dev, next_state);
|
||||
submac->wait_for_ack = false;
|
||||
submac->tx = false;
|
||||
while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN) {}
|
||||
submac->cb->tx_done(submac, status, info);
|
||||
assert(res >= 0);
|
||||
}
|
||||
|
||||
static inline bool _does_handle_ack(ieee802154_dev_t *dev)
|
||||
@ -67,77 +73,10 @@ static inline bool _does_handle_ack(ieee802154_dev_t *dev)
|
||||
ieee802154_radio_has_irq_ack_timeout(dev);
|
||||
}
|
||||
|
||||
static int _perform_csma_ca(ieee802154_submac_t *submac)
|
||||
static inline bool _does_handle_csma(ieee802154_dev_t *dev)
|
||||
{
|
||||
ieee802154_dev_t *dev = &submac->dev;
|
||||
|
||||
if (submac->csma_retries_nb <= submac->csma_retries) {
|
||||
_req_set_trx_state_wait_busy(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_frame_filter_mode(dev, IEEE802154_FILTER_ACCEPT);
|
||||
_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 */
|
||||
_req_set_trx_state_wait_busy(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;
|
||||
return ieee802154_radio_has_frame_retrans(dev) ||
|
||||
ieee802154_radio_has_auto_csma(dev);
|
||||
}
|
||||
|
||||
static bool _has_retrans_left(ieee802154_submac_t *submac)
|
||||
@ -145,180 +84,332 @@ static bool _has_retrans_left(ieee802154_submac_t *submac)
|
||||
return submac->retrans < IEEE802154_SUBMAC_MAX_RETRANSMISSIONS;
|
||||
}
|
||||
|
||||
static void _perform_retrans(ieee802154_submac_t *submac)
|
||||
static ieee802154_fsm_state_t _tx_end(ieee802154_submac_t *submac, int status,
|
||||
ieee802154_tx_info_t *info)
|
||||
{
|
||||
ieee802154_dev_t *dev = &submac->dev;
|
||||
submac->wait_for_ack = false;
|
||||
_req_set_trx_state_wait_busy(&submac->dev, IEEE802154_TRX_STATE_TRX_OFF);
|
||||
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)
|
||||
{
|
||||
/* In case of ACK Timeout, either trigger retransmissions or end
|
||||
* the TX procedure */
|
||||
if (_has_retrans_left(submac)) {
|
||||
submac->retrans++;
|
||||
int res = ieee802154_csma_ca_transmit(submac);
|
||||
(void) res;
|
||||
assert(res >= 0);
|
||||
_req_set_trx_state_wait_busy(&submac->dev, IEEE802154_TRX_STATE_TX_ON);
|
||||
ieee802154_submac_bh_request(submac);
|
||||
return IEEE802154_FSM_STATE_PREPARE;
|
||||
}
|
||||
else {
|
||||
ieee802154_radio_set_frame_filter_mode(dev, IEEE802154_FILTER_ACCEPT);
|
||||
_tx_end(submac, TX_STATUS_NO_ACK, NULL);
|
||||
ieee802154_radio_set_frame_filter_mode(&submac->dev, IEEE802154_FILTER_ACCEPT);
|
||||
return _tx_end(submac, TX_STATUS_NO_ACK, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void ieee802154_submac_ack_timeout_fired(ieee802154_submac_t *submac)
|
||||
static int _handle_fsm_ev_request_tx(ieee802154_submac_t *submac)
|
||||
{
|
||||
/* This is required to avoid race conditions */
|
||||
if (submac->wait_for_ack) {
|
||||
_handle_tx_no_ack(submac);
|
||||
ieee802154_dev_t *dev = &submac->dev;
|
||||
|
||||
/* Set state to TX_ON */
|
||||
int res = ieee802154_radio_request_set_trx_state(dev, IEEE802154_TRX_STATE_TX_ON);
|
||||
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
else {
|
||||
while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN) {}
|
||||
/* write frame to radio */
|
||||
ieee802154_radio_write(dev, submac->psdu);
|
||||
ieee802154_submac_bh_request(submac);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ieee802154_submac_crc_error_cb(ieee802154_submac_t *submac)
|
||||
static ieee802154_fsm_state_t _fsm_state_rx(ieee802154_submac_t *submac, ieee802154_fsm_ev_t ev)
|
||||
{
|
||||
|
||||
ieee802154_dev_t *dev = &submac->dev;
|
||||
/* switch back to RX_ON state */
|
||||
_req_set_trx_state_wait_busy(dev, IEEE802154_TRX_STATE_RX_ON);
|
||||
while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN) {}
|
||||
|
||||
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 */
|
||||
if (ieee802154_radio_len(&submac->dev) > (int)IEEE802154_MIN_FRAME_LEN) {
|
||||
_req_set_trx_state_wait_busy(&submac->dev, IEEE802154_TRX_STATE_TRX_OFF);
|
||||
submac->cb->rx_done(submac);
|
||||
return IEEE802154_FSM_STATE_IDLE;
|
||||
}
|
||||
else {
|
||||
ieee802154_radio_read(&submac->dev, NULL, 0, NULL);
|
||||
/* Keep on current state */
|
||||
return IEEE802154_FSM_STATE_RX;
|
||||
}
|
||||
case IEEE802154_FSM_EV_CRC_ERROR:
|
||||
ieee802154_radio_read(&submac->dev, NULL, 0, NULL);
|
||||
/* 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_trx_state(dev, IEEE802154_TRX_STATE_TRX_OFF)) < 0) {
|
||||
/* Keep on current state */
|
||||
return IEEE802154_FSM_STATE_RX;
|
||||
}
|
||||
while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN) {}
|
||||
return IEEE802154_FSM_STATE_IDLE;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return IEEE802154_FSM_STATE_INVALID;
|
||||
}
|
||||
|
||||
/* All callbacks run in the same context */
|
||||
void ieee802154_submac_rx_done_cb(ieee802154_submac_t *submac)
|
||||
static ieee802154_fsm_state_t _fsm_state_idle(ieee802154_submac_t *submac, ieee802154_fsm_ev_t ev)
|
||||
{
|
||||
ieee802154_dev_t *dev = &submac->dev;
|
||||
|
||||
if (!_does_handle_ack(dev) && submac->wait_for_ack) {
|
||||
uint8_t ack[3];
|
||||
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_request_set_trx_state(dev, IEEE802154_TRX_STATE_RX_ON)) < 0) {
|
||||
/* Keep on current state */
|
||||
return IEEE802154_FSM_STATE_IDLE;
|
||||
}
|
||||
while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN) {}
|
||||
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;
|
||||
}
|
||||
|
||||
if (ieee802154_radio_read(dev, ack, 3, NULL) &&
|
||||
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;
|
||||
|
||||
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);
|
||||
_req_set_trx_state_wait_busy(dev, IEEE802154_TRX_STATE_RX_ON);
|
||||
|
||||
/* 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)
|
||||
{
|
||||
int res;
|
||||
ieee802154_tx_info_t info;
|
||||
|
||||
switch (ev) {
|
||||
case IEEE802154_FSM_EV_TX_DONE:
|
||||
res = ieee802154_radio_confirm_transmit(&submac->dev, &info);
|
||||
(void)res;
|
||||
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);
|
||||
_tx_end(submac, fp ? TX_STATUS_FRAME_PENDING : TX_STATUS_SUCCESS,
|
||||
&tx_info);
|
||||
return _tx_end(submac, fp ? TX_STATUS_FRAME_PENDING : TX_STATUS_SUCCESS,
|
||||
&tx_info);
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert(!submac->tx);
|
||||
submac->cb->rx_done(submac);
|
||||
|
||||
/* Only set the radio to the SubMAC default state only if the upper
|
||||
* layer didn't try to send more data. Otherwise there's risk of not
|
||||
* being compliant with the Radio HAL API (e.g the radio might try
|
||||
* to set a different state in the middle of a transmission).
|
||||
*/
|
||||
if (submac->tx) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* The Radio HAL will be in "FB Lock" state. We need to do a state
|
||||
* transition here in order to release it */
|
||||
ieee802154_trx_state_t next_state = submac->state == IEEE802154_STATE_LISTEN ? IEEE802154_TRX_STATE_RX_ON : IEEE802154_TRX_STATE_TRX_OFF;
|
||||
|
||||
_req_set_trx_state_wait_busy(&submac->dev, next_state);
|
||||
while (ieee802154_radio_confirm_set_trx_state(&submac->dev) == -EAGAIN) {}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!ieee802154_radio_has_frame_retrans_info(dev)) {
|
||||
info->retrans = -1;
|
||||
}
|
||||
_tx_end(submac, info->status, info);
|
||||
}
|
||||
else {
|
||||
ieee802154_radio_set_frame_filter_mode(dev, IEEE802154_FILTER_ACK_ONLY);
|
||||
|
||||
/* 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)) {
|
||||
_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)) {
|
||||
_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);
|
||||
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;
|
||||
case TX_STATUS_NO_ACK:
|
||||
_handle_tx_no_ack(submac);
|
||||
}
|
||||
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 TX_STATUS_SUCCESS:
|
||||
case TX_STATUS_FRAME_PENDING:
|
||||
_handle_tx_success(submac, &info);
|
||||
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:
|
||||
assert(false);
|
||||
break;
|
||||
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_dev_t *dev = &submac->dev;
|
||||
ieee802154_fsm_state_t current_state = submac->fsm_state;
|
||||
|
||||
if (current_state != IEEE802154_FSM_STATE_RX && current_state != IEEE802154_FSM_STATE_IDLE) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
uint8_t *buf = iolist->iol_base;
|
||||
bool cnf = buf[0] & IEEE802154_FCF_ACK_REQ;
|
||||
|
||||
if (submac->state == IEEE802154_STATE_OFF) {
|
||||
return -ENETDOWN;
|
||||
}
|
||||
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 (submac->tx ||
|
||||
ieee802154_radio_request_set_trx_state(dev,
|
||||
IEEE802154_TRX_STATE_TX_ON) < 0) {
|
||||
if (ieee802154_submac_process_ev(submac, IEEE802154_FSM_EV_REQUEST_TX)
|
||||
!= IEEE802154_FSM_STATE_PREPARE) {
|
||||
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;
|
||||
|
||||
return ieee802154_csma_ca_transmit(submac);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ieee802154_submac_init(ieee802154_submac_t *submac, const network_uint16_t *short_addr,
|
||||
@ -326,10 +417,10 @@ int ieee802154_submac_init(ieee802154_submac_t *submac, const network_uint16_t *
|
||||
{
|
||||
ieee802154_dev_t *dev = &submac->dev;
|
||||
|
||||
submac->tx = false;
|
||||
submac->state = IEEE802154_STATE_LISTEN;
|
||||
submac->fsm_state = IEEE802154_FSM_STATE_RX;
|
||||
|
||||
int res;
|
||||
|
||||
if ((res = ieee802154_radio_request_on(dev)) < 0) {
|
||||
return res;
|
||||
}
|
||||
@ -358,6 +449,7 @@ int ieee802154_submac_init(ieee802154_submac_t *submac, const network_uint16_t *
|
||||
|
||||
/* 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);
|
||||
@ -400,7 +492,6 @@ int ieee802154_submac_init(ieee802154_submac_t *submac, const network_uint16_t *
|
||||
assert(res >= 0);
|
||||
|
||||
_req_set_trx_state_wait_busy(dev, IEEE802154_TRX_STATE_RX_ON);
|
||||
while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN) {};
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -414,14 +505,19 @@ int ieee802154_set_phy_conf(ieee802154_submac_t *submac, uint16_t channel_num,
|
||||
.channel = channel_num,
|
||||
.page = channel_page,
|
||||
.pow = tx_pow };
|
||||
int res;
|
||||
ieee802154_fsm_state_t current_state = submac->fsm_state;
|
||||
|
||||
if (submac->state == IEEE802154_STATE_OFF) {
|
||||
return -ENETDOWN;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
int res;
|
||||
if ((res = ieee802154_radio_request_set_trx_state(dev, IEEE802154_TRX_STATE_TRX_OFF)) < 0) {
|
||||
return res;
|
||||
/* If the radio is listening, turn it off first */
|
||||
if (current_state == IEEE802154_FSM_STATE_RX) {
|
||||
if ((res = ieee802154_radio_request_set_trx_state(dev, IEEE802154_TRX_STATE_TRX_OFF)) < 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
res = ieee802154_radio_config_phy(dev, &conf);
|
||||
@ -433,52 +529,61 @@ int ieee802154_set_phy_conf(ieee802154_submac_t *submac, uint16_t channel_num,
|
||||
}
|
||||
while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN) {}
|
||||
|
||||
ieee802154_radio_request_set_trx_state(dev, submac->state == IEEE802154_STATE_LISTEN ? IEEE802154_TRX_STATE_RX_ON : IEEE802154_TRX_STATE_TRX_OFF);
|
||||
while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN) {}
|
||||
/* Go back to RX if needed */
|
||||
if (current_state == IEEE802154_FSM_STATE_RX) {
|
||||
_req_set_trx_state_wait_busy(dev, IEEE802154_TRX_STATE_RX_ON);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int ieee802154_set_state(ieee802154_submac_t *submac, ieee802154_submac_state_t state)
|
||||
int ieee802154_set_rx(ieee802154_submac_t *submac)
|
||||
{
|
||||
int res;
|
||||
ieee802154_fsm_state_t current_state = submac->fsm_state;
|
||||
ieee802154_fsm_state_t next_state;
|
||||
int res = -EBUSY;
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
while (ieee802154_radio_confirm_on(dev) == -EAGAIN);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (state == IEEE802154_STATE_OFF) {
|
||||
res = ieee802154_radio_off(dev);
|
||||
}
|
||||
else {
|
||||
ieee802154_trx_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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
Loading…
Reference in New Issue
Block a user