diff --git a/cpu/native/Makefile.dep b/cpu/native/Makefile.dep index 217f00ada2d9..54a9b085416f 100644 --- a/cpu/native/Makefile.dep +++ b/cpu/native/Makefile.dep @@ -43,7 +43,6 @@ ifneq (,$(filter socket_zep,$(USEMODULE))) USEMODULE += ieee802154 ifneq (,$(filter netdev,$(USEMODULE))) USEMODULE += netdev_ieee802154_submac - USEMODULE += netdev_ieee802154_submac_soft_ack endif endif diff --git a/cpu/nrf52/Makefile.dep b/cpu/nrf52/Makefile.dep index e474509e6aae..52a3444b4fdc 100644 --- a/cpu/nrf52/Makefile.dep +++ b/cpu/nrf52/Makefile.dep @@ -14,7 +14,6 @@ ifneq (,$(filter nrf802154,$(USEMODULE))) USEMODULE += luid ifneq (,$(filter netdev,$(USEMODULE))) USEMODULE += netdev_ieee802154_submac - USEMODULE += netdev_ieee802154_submac_soft_ack endif endif diff --git a/drivers/netdev_ieee802154_submac/Makefile b/drivers/netdev_ieee802154_submac/Makefile index 48422e909a47..f4930fec25b2 100644 --- a/drivers/netdev_ieee802154_submac/Makefile +++ b/drivers/netdev_ieee802154_submac/Makefile @@ -1 +1,5 @@ +ifneq (,$(filter netdev_ieee802154_submac_soft_ack,$(USEMODULE))) + $(warning SubMAC Soft ACK is now on by default and will be deprecated.) +endif + include $(RIOTBASE)/Makefile.base diff --git a/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c b/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c index 0205f605f620..aa48fbf6bcfa 100644 --- a/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c +++ b/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c @@ -286,7 +286,7 @@ static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) netdev_ieee802154_submac_t, dev); ieee802154_submac_t *submac = &netdev_submac->submac; - ieee802154_rx_info_t rx_info; + ieee802154_rx_info_t rx_info = { 0 }; if (buf == NULL && len == 0) { return ieee802154_get_frame_length(submac); @@ -303,30 +303,7 @@ static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) netdev_rx_info->lqi = rx_info.lqi; } -#if IS_USED(MODULE_NETDEV_IEEE802154_SUBMAC_SOFT_ACK) - const uint8_t *mhr = buf; - if ((mhr[0] & IEEE802154_FCF_TYPE_MASK) == IEEE802154_FCF_TYPE_DATA && - (mhr[0] & IEEE802154_FCF_ACK_REQ)) { - ieee802154_filter_mode_t mode; - if (!ieee802154_radio_has_capability(&submac->dev, IEEE802154_CAP_AUTO_ACK) && - (ieee802154_radio_get_frame_filter_mode(&submac->dev, &mode) < 0 - || mode == IEEE802154_FILTER_ACCEPT)) { - /* send ACK if not handled by the driver and not in promiscuous mode */ - uint8_t ack[IEEE802154_ACK_FRAME_LEN - IEEE802154_FCS_LEN] - = { IEEE802154_FCF_TYPE_ACK, 0x00, ieee802154_get_seq(mhr) }; - iolist_t io = { - .iol_base = ack, - .iol_len = sizeof(ack), - .iol_next = NULL - }; - DEBUG("IEEE802154 submac: Sending ACK\n"); - int snd = _send(netdev, &io); - if (snd < 0) { - DEBUG("IEEE802154 submac: failed to send ACK (%d)\n", snd); - } - } - } -#endif + return res; } diff --git a/makefiles/deprecated_modules.inc.mk b/makefiles/deprecated_modules.inc.mk index 5fe57d31d826..545e0e09234c 100644 --- a/makefiles/deprecated_modules.inc.mk +++ b/makefiles/deprecated_modules.inc.mk @@ -2,3 +2,4 @@ # Keep this list ALPHABETICALLY SORTED!!!!111elven DEPRECATED_MODULES += gnrc_nettype_lorawan DEPRECATED_MODULES += sema_deprecated +DEPRECATED_MODULES += netdev_ieee802154_submac_soft_ack diff --git a/sys/include/net/ieee802154/submac.h b/sys/include/net/ieee802154/submac.h index 6c340d459374..eebf58b65457 100644 --- a/sys/include/net/ieee802154/submac.h +++ b/sys/include/net/ieee802154/submac.h @@ -30,30 +30,36 @@ * | RX | |PREPARE |<--->| TX | * | | +--->| | | | * +--------+ | +--------+ +--------+ - * ^ | ^ | - * | | | | - * | | | | - * | | +--------+ | - * | | | | v - * | | |WAIT FOR|<--------+ - * | | | ACK | | - * | | +--------+ | - * | | | | - * | | | | - * | | v | - * | | +--------+ | - * | +-----| | | - * | | IDLE | | - * +------------->| |<-------+ - * +--------+ + * | ^ | ^ | + * | | | | | + * | | | | | + * | | | +--------+ | + * | | | | | v + * | | | |WAIT FOR|<--------+ + * | | | | ACK | | + * | | | +--------+ | + * | | | | | + * | | | | | + * | | | v | + * | | | +--------+ | + * | | +-----| | | + * | | | IDLE | | + * | +----------->| |<-------+ + * | +--------+ + * | +--------+ ^ + * | | | | + * +--->| TX ACK |-----+ + * | | + * +--------+ * ``` * * - 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 + * frame it will transmit an ACK frame if necessary then call + * @ref ieee802154_submac_cb_t::rx_done and immediately go + * to IDLE or TX ACK. 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 @@ -71,22 +77,25 @@ * (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. + * - TX ACK: The received frame requires instantanous acknowledgement. Sending + * further frames in this state is not permitted. After ACK transmission is + * completed, the SubMAC will go IDLE. * * 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 | - | - | - | - + * Event/State | RX | IDLE | PREPARE | TX | WAIT FOR ACK | TX ACK | + * --------------|----|-------|---------|----|------------------------ + * TX_DONE | - | - | - | X | - |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 @@ -167,6 +176,7 @@ typedef enum { 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_TX_ACK, /**< The SubMAC is transmitting an ACK frame */ IEEE802154_FSM_STATE_NUMOF, /**< Number of SubMAC FSM states */ } ieee802154_fsm_state_t; @@ -208,6 +218,9 @@ struct ieee802154_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 */ + uint8_t rx_buf[IEEE802154_FRAME_LEN_MAX]; /**< stores received frame */ + size_t rx_len; /**< stores length of received frame */ + ieee802154_rx_info_t rx_info; /**< stores lqi and rssi of received frame */ }; /** @@ -410,16 +423,13 @@ 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) */ static inline int ieee802154_get_frame_length(ieee802154_submac_t *submac) { - return ieee802154_radio_len(&submac->dev); + return submac->rx_len; } /** @@ -427,9 +437,6 @@ 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 @@ -441,7 +448,15 @@ static inline int ieee802154_get_frame_length(ieee802154_submac_t *submac) static inline int ieee802154_read_frame(ieee802154_submac_t *submac, void *buf, size_t len, ieee802154_rx_info_t *info) { - return ieee802154_radio_read(&submac->dev, buf, len, info); + if (submac->rx_len > len) { + return -ENOBUFS; + } + if (info != NULL) { + info->rssi = submac->rx_info.rssi; + info->lqi = submac->rx_info.lqi; + } + memcpy(buf, submac->rx_buf, submac->rx_len); + return submac->rx_len; } /** diff --git a/sys/net/link_layer/ieee802154/submac.c b/sys/net/link_layer/ieee802154/submac.c index d5f5a9a2cad5..aa28af486e7b 100644 --- a/sys/net/link_layer/ieee802154/submac.c +++ b/sys/net/link_layer/ieee802154/submac.c @@ -48,6 +48,7 @@ static char *str_states[IEEE802154_FSM_STATE_NUMOF] = { "PREPARE", "TX", "WAIT_FOR_ACK", + "TX_ACK", }; static char *str_ev[IEEE802154_FSM_EV_NUMOF] = { @@ -67,6 +68,11 @@ static inline bool _does_handle_ack(ieee802154_dev_t *dev) ieee802154_radio_has_irq_ack_timeout(dev); } +static inline bool _does_send_ack(ieee802154_dev_t *dev) +{ + return ieee802154_radio_has_capability(dev, IEEE802154_CAP_AUTO_ACK); +} + static inline bool _does_handle_csma(ieee802154_dev_t *dev) { return ieee802154_radio_has_frame_retrans(dev) || @@ -91,7 +97,10 @@ static ieee802154_fsm_state_t _tx_end(ieee802154_submac_t *submac, int status, res = ieee802154_radio_set_idle(&submac->dev, true); assert(res >= 0); - submac->cb->tx_done(submac, status, info); + /* no need to inform upper layer about ACK transmission */ + if (submac->fsm_state != IEEE802154_FSM_STATE_TX_ACK) { + submac->cb->tx_done(submac, status, info); + } return IEEE802154_FSM_STATE_IDLE; } @@ -148,25 +157,24 @@ static ieee802154_fsm_state_t _fsm_state_prepare(ieee802154_submac_t *submac, static ieee802154_fsm_state_t _fsm_state_tx(ieee802154_submac_t *submac, ieee802154_fsm_ev_t ev); -static int _handle_fsm_ev_tx_ack(ieee802154_submac_t *submac) -{ - ieee802154_dev_t *dev = &submac->dev; +static int _handle_fsm_ev_tx_ack(ieee802154_submac_t *submac, uint8_t seq_num) +{ + uint8_t ack[] + = { IEEE802154_FCF_TYPE_ACK, 0x00, seq_num }; + iolist_t iolist = { + .iol_base = ack, + .iol_len = sizeof(ack), + .iol_next = NULL + }; + submac->wait_for_ack = 0; + submac->psdu = &iolist; + submac->retrans = 0; + submac->csma_retries_nb = 0; + submac->backoff_mask = (1 << submac->be.min) - 1; - /* Set state to TX_ON */ - int res; - if ((res = ieee802154_radio_set_idle(dev, false)) < 0) { - return res; - } - if ((res = ieee802154_radio_write(dev, submac->psdu)) < 0) { - return res; - } - /* skip async Tx request */ - _fsm_state_prepare(submac, IEEE802154_FSM_EV_BH, IEEE802154_FCF_TYPE_ACK); - /* wait for Tx done */ - while (_fsm_state_tx(submac, IEEE802154_FSM_EV_TX_DONE) == IEEE802154_FSM_STATE_INVALID) { - DEBUG("IEEE802154 submac: wait until ACK sent\n"); - } - return 0; + DEBUG("IEEE802154 submac: Sending ACK\n"); + + return _handle_fsm_ev_request_tx(submac); } static ieee802154_fsm_state_t _fsm_state_rx(ieee802154_submac_t *submac, ieee802154_fsm_ev_t ev) @@ -184,27 +192,47 @@ static ieee802154_fsm_state_t _fsm_state_rx(ieee802154_submac_t *submac, ieee802 } return IEEE802154_FSM_STATE_PREPARE; case IEEE802154_FSM_EV_RX_DONE: + while (ieee802154_radio_set_idle(dev, false) < 0) {} + submac->rx_len = ieee802154_radio_len(dev); + assert(submac->rx_len < IEEE802154_FRAME_LEN_MAX); + res = ieee802154_radio_read(dev, submac->rx_buf, submac->rx_len, &submac->rx_info); + assert(res == (int)submac->rx_len); /* 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) { + if (ieee802154_radio_len(dev) > (int)IEEE802154_MIN_FRAME_LEN) { + /* sending ACK if radio does not support auto-ACK */ + if (!_does_send_ack(dev)) { + ieee802154_filter_mode_t mode; + if ((submac->rx_buf[0] & IEEE802154_FCF_TYPE_MASK) == IEEE802154_FCF_TYPE_DATA && + (submac->rx_buf[0] & IEEE802154_FCF_ACK_REQ) && + (ieee802154_radio_get_frame_filter_mode(dev, &mode) < 0 || + mode == IEEE802154_FILTER_ACCEPT)) { + res = _handle_fsm_ev_tx_ack(submac, ieee802154_get_seq(submac->rx_buf)); + submac->cb->rx_done(submac); + if (res < 0) { + DEBUG("IEEE802154 submac: Sending ACK failed with status: %d\n", res); + return IEEE802154_FSM_STATE_IDLE; + } + return IEEE802154_FSM_STATE_TX_ACK; + } + } submac->cb->rx_done(submac); return IEEE802154_FSM_STATE_IDLE; } else { - ieee802154_radio_read(&submac->dev, NULL, 0, NULL); + ieee802154_radio_read(dev, NULL, 0, NULL); /* If the radio doesn't support RX Continuous, go to RX */ - res = ieee802154_radio_set_rx(&submac->dev); + res = ieee802154_radio_set_rx(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); + while (ieee802154_radio_set_idle(dev, false) < 0) {} + ieee802154_radio_read(dev, NULL, 0, NULL); /* If the radio doesn't support RX Continuous, go to RX */ - res = ieee802154_radio_set_rx(&submac->dev); + res = ieee802154_radio_set_rx(dev); assert(res >= 0); /* Keep on current state */ return IEEE802154_FSM_STATE_RX; @@ -231,16 +259,7 @@ static ieee802154_fsm_state_t _fsm_state_idle(ieee802154_submac_t *submac, ieee8 switch (ev) { case IEEE802154_FSM_EV_REQUEST_TX: - /* An ACK is sent synchronous to prevent that the upper layer (IPv6) - can initiate the next transmission which would fail because the transceiver - is still busy. */ - if (submac->psdu->iol_len == IEEE802154_ACK_FRAME_LEN - IEEE802154_FCS_LEN && - (((uint8_t *)submac->psdu->iol_base)[0] & IEEE802154_FCF_TYPE_ACK) && - submac->psdu->iol_next == NULL) { - _handle_fsm_ev_tx_ack(submac); - return IEEE802154_FSM_STATE_IDLE; - } - else if (_handle_fsm_ev_request_tx(submac) < 0) { + if (_handle_fsm_ev_request_tx(submac) < 0) { return IEEE802154_FSM_STATE_IDLE; } return IEEE802154_FSM_STATE_PREPARE; @@ -257,7 +276,7 @@ static ieee802154_fsm_state_t _fsm_state_idle(ieee802154_submac_t *submac, ieee8 * and TX_DONE. We simply discard the frame and keep the state as * it is */ - ieee802154_radio_read(&submac->dev, NULL, 0, NULL); + ieee802154_radio_read(dev, NULL, 0, NULL); return IEEE802154_FSM_STATE_IDLE; default: break; @@ -299,7 +318,7 @@ static ieee802154_fsm_state_t _fsm_state_prepare(ieee802154_submac_t *submac, * and TX_DONE. We simply discard the frame and keep the state as * it is */ - ieee802154_radio_read(&submac->dev, NULL, 0, NULL); + ieee802154_radio_read(dev, NULL, 0, NULL); return IEEE802154_FSM_STATE_PREPARE; default: break; @@ -319,13 +338,13 @@ static ieee802154_fsm_state_t _fsm_state_tx_process_tx_done(ieee802154_submac_t switch (info->status) { case TX_STATUS_FRAME_PENDING: - assert(_does_handle_ack(&submac->dev)); + assert(_does_handle_ack(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) { + if (_does_handle_ack(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 @@ -341,7 +360,7 @@ static ieee802154_fsm_state_t _fsm_state_tx_process_tx_done(ieee802154_submac_t } break; case TX_STATUS_NO_ACK: - assert(_does_handle_ack(&submac->dev)); + assert(_does_handle_ack(dev)); submac->csma_retries_nb = 0; return _handle_tx_no_ack(submac); case TX_STATUS_MEDIUM_BUSY: @@ -349,7 +368,7 @@ static ieee802154_fsm_state_t _fsm_state_tx_process_tx_done(ieee802154_submac_t * procedure failed. We finish the SubMAC operation and report * medium busy */ - if (_does_handle_csma(&submac->dev) + if (_does_handle_csma(dev) || submac->csma_retries_nb++ >= submac->csma_retries) { return _tx_end(submac, info->status, info); } @@ -425,6 +444,28 @@ static ieee802154_fsm_state_t _fsm_state_wait_for_ack(ieee802154_submac_t *subma return IEEE802154_FSM_STATE_INVALID; } +static ieee802154_fsm_state_t _fsm_state_tx_ack(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: + if ((res = ieee802154_radio_confirm_transmit(&submac->dev, &info)) >= 0) { + return _fsm_state_tx_process_tx_done(submac, &info); + } + break; + default: + break; + } + + return IEEE802154_FSM_STATE_INVALID; +} + ieee802154_fsm_state_t ieee802154_submac_process_ev(ieee802154_submac_t *submac, ieee802154_fsm_ev_t ev) { @@ -451,6 +492,10 @@ ieee802154_fsm_state_t ieee802154_submac_process_ev(ieee802154_submac_t *submac, DEBUG("IEEE802154 submac: ieee802154_submac_process_ev(): IEEE802154_FSM_STATE_WAIT_FOR_ACK + %s\n", str_ev[ev]); new_state = _fsm_state_wait_for_ack(submac, ev); break; + case IEEE802154_FSM_STATE_TX_ACK: + DEBUG("IEEE802154 submac: ieee802154_submac_process_ev(): IEEE802154_FSM_STATE_TX_ACK + %s\n", str_ev[ev]); + new_state = _fsm_state_tx_ack(submac, ev); + break; default: DEBUG("IEEE802154 submac: ieee802154_submac_process_ev(): INVALID STATE\n"); new_state = IEEE802154_FSM_STATE_INVALID; @@ -479,8 +524,6 @@ int ieee802154_send(ieee802154_submac_t *submac, const iolist_t *iolist) uint8_t *buf = iolist->iol_base; bool cnf = buf[0] & IEEE802154_FCF_ACK_REQ; - bool is_ack = iolist->iol_len == IEEE802154_ACK_FRAME_LEN - IEEE802154_FCS_LEN && - (buf[0] & IEEE802154_FCF_TYPE_MASK) == IEEE802154_FCF_TYPE_ACK; submac->wait_for_ack = cnf; submac->psdu = iolist; @@ -488,16 +531,11 @@ int ieee802154_send(ieee802154_submac_t *submac, const iolist_t *iolist) submac->csma_retries_nb = 0; submac->backoff_mask = (1 << submac->be.min) - 1; - if (!is_ack && ieee802154_submac_process_ev(submac, IEEE802154_FSM_EV_REQUEST_TX) + if (ieee802154_submac_process_ev(submac, IEEE802154_FSM_EV_REQUEST_TX) != IEEE802154_FSM_STATE_PREPARE) { DEBUG("IEEE802154 submac: ieee802154_send(): Tx frame failed %s\n", str_states[current_state]); return -EBUSY; } - if (is_ack && ieee802154_submac_process_ev(submac, IEEE802154_FSM_EV_REQUEST_TX) - != IEEE802154_FSM_STATE_IDLE) { - DEBUG("IEEE802154 submac: ieee802154_send(): Tx ACK failed %s\n", str_states[current_state]); - return -EBUSY; - } return 0; } @@ -711,6 +749,11 @@ int ieee802154_submac_init(ieee802154_submac_t *submac, const network_uint16_t * submac->fsm_state = IEEE802154_FSM_STATE_RX; + submac->rx_len = 0; + submac->rx_info.rssi = 0; + submac->rx_info.lqi = 0; + memset(submac->rx_buf, 0, sizeof(submac->rx_buf)); + int res; if ((res = ieee802154_radio_request_on(dev)) < 0) {