diff --git a/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c b/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c index fca09449f1..61a6b3dc83 100644 --- a/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c +++ b/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c @@ -18,6 +18,7 @@ #include "atomic_utils.h" #include "irq.h" +#include "net/ieee802154/radio.h" #include "net/netdev/ieee802154_submac.h" #include "event/thread.h" @@ -264,9 +265,7 @@ static void _isr(netdev_t *netdev) /* 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); - } + ieee802154_set_rx(submac); } else { DEBUG("IEEE802154 submac: no events to dispatch\n"); @@ -297,7 +296,30 @@ static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) netdev_rx_info->lqi = rx_info.lqi; } - +#if !IS_ACTIVE(CONFIG_IEEE802154_AUTO_ACK_DISABLE) + 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/sys/net/link_layer/ieee802154/submac.c b/sys/net/link_layer/ieee802154/submac.c index 696def750f..a1c32bf042 100644 --- a/sys/net/link_layer/ieee802154/submac.c +++ b/sys/net/link_layer/ieee802154/submac.c @@ -130,6 +130,33 @@ static int _handle_fsm_ev_request_tx(ieee802154_submac_t *submac) } } +static ieee802154_fsm_state_t _fsm_state_prepare(ieee802154_submac_t *submac, + ieee802154_fsm_ev_t ev); + +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; + + /* 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); + /* 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; +} + static ieee802154_fsm_state_t _fsm_state_rx(ieee802154_submac_t *submac, ieee802154_fsm_ev_t ev) { ieee802154_dev_t *dev = &submac->dev; @@ -192,7 +219,16 @@ static ieee802154_fsm_state_t _fsm_state_idle(ieee802154_submac_t *submac, ieee8 switch (ev) { case IEEE802154_FSM_EV_REQUEST_TX: - if (_handle_fsm_ev_request_tx(submac) < 0) { + /* 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) { return IEEE802154_FSM_STATE_IDLE; } return IEEE802154_FSM_STATE_PREPARE; @@ -321,9 +357,10 @@ static ieee802154_fsm_state_t _fsm_state_tx(ieee802154_submac_t *submac, ieee802 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); + if ((res = ieee802154_radio_confirm_transmit(&submac->dev, &info)) >= 0) { + return _fsm_state_tx_process_tx_done(submac, &info); + } + break; 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 @@ -403,7 +440,7 @@ ieee802154_fsm_state_t ieee802154_submac_process_ev(ieee802154_submac_t *submac, if (new_state == IEEE802154_FSM_STATE_INVALID) { _print_debug(submac->fsm_state, new_state, ev); - assert(false); + new_state = submac->fsm_state; } submac->fsm_state = new_state; return submac->fsm_state; @@ -424,6 +461,8 @@ 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; @@ -431,8 +470,14 @@ 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 (ieee802154_submac_process_ev(submac, IEEE802154_FSM_EV_REQUEST_TX) + if (!is_ack && 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;