diff --git a/cpu/cc2538/radio/cc2538_rf_radio_ops.c b/cpu/cc2538/radio/cc2538_rf_radio_ops.c index a1505262950e..2780fbf12969 100644 --- a/cpu/cc2538/radio/cc2538_rf_radio_ops.c +++ b/cpu/cc2538/radio/cc2538_rf_radio_ops.c @@ -619,6 +619,7 @@ void cc2538_rf_hal_setup(ieee802154_dev_t *hal) static const ieee802154_radio_ops_t cc2538_rf_ops = { .caps = IEEE802154_CAP_24_GHZ + | IEEE802154_CAP_AUTO_ACK | IEEE802154_CAP_AUTO_CSMA | IEEE802154_CAP_IRQ_CRC_ERROR | IEEE802154_CAP_IRQ_TX_DONE diff --git a/cpu/esp32/esp-ieee802154/esp_ieee802154_hal.c b/cpu/esp32/esp-ieee802154/esp_ieee802154_hal.c index 8754fa62b5fd..b4be883079c5 100644 --- a/cpu/esp32/esp-ieee802154/esp_ieee802154_hal.c +++ b/cpu/esp32/esp-ieee802154/esp_ieee802154_hal.c @@ -438,6 +438,7 @@ int esp_ieee802154_init(void) static const ieee802154_radio_ops_t esp_ieee802154_driver = { .caps = IEEE802154_CAP_24_GHZ | IEEE802154_CAP_PHY_OQPSK + | IEEE802154_CAP_AUTO_ACK | IEEE802154_CAP_AUTO_CSMA | IEEE802154_CAP_SRC_ADDR_MATCH #if _USE_CCA_DONE diff --git a/cpu/native/Makefile.dep b/cpu/native/Makefile.dep index cd33b6ea55fa..2921b4c70b59 100644 --- a/cpu/native/Makefile.dep +++ b/cpu/native/Makefile.dep @@ -37,6 +37,7 @@ 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/native/socket_zep/socket_zep.c b/cpu/native/socket_zep/socket_zep.c index 751c5d3ca1e4..c1a5a00ae04a 100644 --- a/cpu/native/socket_zep/socket_zep.c +++ b/cpu/native/socket_zep/socket_zep.c @@ -257,37 +257,6 @@ static void _send_zep_hello(socket_zep_t *dev) } } -static void _send_ack(void *arg) -{ - ieee802154_dev_t *dev = arg; - socket_zep_t *zepdev = dev->priv; - const uint8_t *rxbuf = &zepdev->rcv_buf[sizeof(zep_v2_data_hdr_t)]; - uint8_t ack[3]; - zep_v2_data_hdr_t hdr; - - /* sending ACK should only happen if we received a frame */ - assert(zepdev->state == ZEPDEV_STATE_RX_RECV); - /* ACK request bit should be set if we get here */ - assert((rxbuf[0] & IEEE802154_FCF_ACK_REQ) != 0); - - DEBUG("socket_zep::send_ack: seq_no: %u\n", rxbuf[2]); - - _zep_hdr_fill(zepdev, &hdr.hdr, sizeof(ack) + IEEE802154_FCF_LEN); - - ack[0] = IEEE802154_FCF_TYPE_ACK; /* FCF */ - ack[1] = 0; /* FCF */ - ack[2] = rxbuf[2]; /* SeqNum */ - - /* calculate checksum */ - uint16_t chksum = crc16_ccitt_false_update(0, ack, 3); - - real_send(zepdev->sock_fd, &hdr, sizeof(hdr), MSG_MORE); - real_send(zepdev->sock_fd, ack, sizeof(ack), MSG_MORE); - real_send(zepdev->sock_fd, &chksum, sizeof(chksum), 0); - - dev->cb(dev, IEEE802154_RADIO_INDICATION_RX_DONE); -} - static void _send_frame(void *arg) { ieee802154_dev_t *dev = arg; @@ -359,13 +328,7 @@ static void _socket_isr(int fd, void *arg) zepdev->rcv_len = res; dev->cb(dev, IEEE802154_RADIO_INDICATION_RX_START); - /* send ACK after 192 µs */ - if ((((uint8_t *)(zep + 1))[0] & IEEE802154_FCF_ACK_REQ) != 0) { - zepdev->ack_timer.callback = _send_ack; - ztimer_set(ZTIMER_USEC, &zepdev->ack_timer, ACK_DELAY_US); - } else { - dev->cb(dev, IEEE802154_RADIO_INDICATION_RX_DONE); - } + dev->cb(dev, IEEE802154_RADIO_INDICATION_RX_DONE); return; out: @@ -505,6 +468,13 @@ static int _set_frame_filter_mode(ieee802154_dev_t *dev, ieee802154_filter_mode_ return 0; } +static int _get_frame_filter_mode(ieee802154_dev_t *dev, ieee802154_filter_mode_t *mode) +{ + socket_zep_t *zepdev = dev->priv; + *mode = zepdev->filter_mode; + return 0; +} + static int _write(ieee802154_dev_t *dev, const iolist_t *iolist) { socket_zep_t *zepdev = dev->priv; @@ -544,7 +514,7 @@ static int _request_transmit(ieee802154_dev_t *dev) zepdev->state = ZEPDEV_STATE_TX; /* 8 bit are mapped to 2 symbols */ - unsigned time_tx = 2 * zepdev->snd_len * IEEE802154_SYMBOL_TIME_US; + unsigned time_tx = 2 * (zepdev->snd_len - sizeof(zep_v2_data_hdr_t)) * IEEE802154_SYMBOL_TIME_US; DEBUG("socket_zep::request_transmit(%u bytes, %u µs)\n", zepdev->snd_len, time_tx); dev->cb(dev, IEEE802154_RADIO_INDICATION_TX_START); @@ -560,7 +530,14 @@ static int _confirm_transmit(ieee802154_dev_t *dev, ieee802154_tx_info_t *info) { (void) dev; + socket_zep_t *zepdev = dev->priv; + + if (zepdev->state == ZEPDEV_STATE_TX) { + DEBUG("socket_zep::confirm_transmit: still in TX state\n"); + return -EAGAIN; /* TX is still in progress */ + } if (info) { + DEBUG("socket_zep::confirm_transmit: success\n"); info->status = TX_STATUS_SUCCESS; } @@ -628,6 +605,7 @@ static int _request_op(ieee802154_dev_t *dev, ieee802154_hal_op_t op, void *ctx) switch (op) { case IEEE802154_HAL_OP_TRANSMIT: if (zepdev->state != ZEPDEV_STATE_IDLE) { + DEBUG("socket_zep::request_op: request TX in state %u (busy)\n", zepdev->state); return -EBUSY; } res = _request_transmit(dev); @@ -659,6 +637,7 @@ static int _request_op(ieee802154_dev_t *dev, ieee802154_hal_op_t op, void *ctx) ztimer_remove(ZTIMER_USEC, &zepdev->ack_timer); zepdev->state = ZEPDEV_STATE_IDLE; } else { + DEBUG("socket_zep::request_op: request IDLE in state TX\n"); return -EBUSY; } @@ -723,6 +702,7 @@ static const ieee802154_radio_ops_t socket_zep_rf_ops = { .config_src_addr_match = _config_src_addr_match, .set_csma_params = _set_csma_params, .set_frame_filter_mode = _set_frame_filter_mode, + .get_frame_filter_mode = _get_frame_filter_mode, }; void socket_zep_hal_setup(socket_zep_t *dev, ieee802154_dev_t *hal) diff --git a/cpu/nrf52/Makefile.dep b/cpu/nrf52/Makefile.dep index 52a3444b4fdc..e474509e6aae 100644 --- a/cpu/nrf52/Makefile.dep +++ b/cpu/nrf52/Makefile.dep @@ -14,6 +14,7 @@ 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/cpu/nrf52/radio/nrf802154/nrf802154_radio.c b/cpu/nrf52/radio/nrf802154/nrf802154_radio.c index 404a6e676650..dfb5b293007c 100644 --- a/cpu/nrf52/radio/nrf802154/nrf802154_radio.c +++ b/cpu/nrf52/radio/nrf802154/nrf802154_radio.c @@ -69,7 +69,6 @@ static uint8_t rxbuf[IEEE802154_FRAME_LEN_MAX + 3]; /* len PHR + PSDU + LQI */ static uint8_t txbuf[IEEE802154_FRAME_LEN_MAX + 3]; /* len PHR + PSDU + LQI */ -static uint8_t ack[IEEE802154_ACK_FRAME_LEN]; typedef enum { STATE_IDLE, @@ -430,30 +429,6 @@ static void _set_ifs_timer(bool lifs) timer_start(NRF802154_TIMER); } -static void _timer_cb(void *arg, int chan) -{ - (void)arg; - ieee802154_dev_t *dev = nrf802154_hal_dev; - - if (chan == MAC_TIMER_CHAN_ACK) { - /* Copy sqn */ - ack[1] = IEEE802154_FCF_TYPE_ACK; - if (cfg.pending) { - ack[1] |= IEEE802154_FCF_FRAME_PEND; - } - - ack[3] = rxbuf[3]; - - NRF_RADIO->PACKETPTR = (uint32_t) &ack; - NRF_RADIO->TASKS_TXEN = 1; - dev->cb(dev, IEEE802154_RADIO_INDICATION_RX_DONE); - } - else if (chan == MAC_TIMER_CHAN_IFS) { - cfg.ifs = false; - } - - timer_stop(NRF802154_TIMER); -} /** * @brief Set radio into DISABLED state */ @@ -463,16 +438,6 @@ int nrf802154_init(void) /* reset buffer */ rxbuf[0] = 0; txbuf[0] = 0; - - ack[0] = IEEE802154_ACK_FRAME_LEN; /* PSDU length */ - ack[1] = IEEE802154_FCF_TYPE_ACK; /* FCF */ - ack[2] = 0; /* FCF */ - - int result = timer_init(NRF802154_TIMER, TIMER_FREQ, _timer_cb, NULL); - assert(result >= 0); - (void)result; - timer_stop(NRF802154_TIMER); - /* power off peripheral (but do not release the HFXO as we never requested * it so far) */ NRF_RADIO->POWER = 0; @@ -528,9 +493,7 @@ void isr_radio(void) case STATE_RX: if (NRF_RADIO->CRCSTATUS) { bool l2filter_passed = _l2filter(rxbuf+1); - bool is_auto_ack_en = !IS_ACTIVE(CONFIG_IEEE802154_AUTO_ACK_DISABLE); bool is_ack = rxbuf[1] & IEEE802154_FCF_TYPE_ACK; - bool ack_req = rxbuf[1] & IEEE802154_FCF_ACK_REQ; /* If radio is in promiscuos mode, indicate packet and * don't event think of sending an ACK frame :) */ @@ -543,17 +506,9 @@ void isr_radio(void) * directly or if the driver should send an ACK frame before * the indication */ else if (l2filter_passed) { - if (ack_req && is_auto_ack_en) { - timer_set(NRF802154_TIMER, MAC_TIMER_CHAN_ACK, IEEE802154_SIFS_SYMS); - timer_start(NRF802154_TIMER); - _disable(); - _state = STATE_ACK; - } - else { - DEBUG("[nrf802154] RX frame doesn't require ACK frame.\n"); - _state = STATE_IDLE; - dev->cb(dev, IEEE802154_RADIO_INDICATION_RX_DONE); - } + DEBUG("[nrf802154] RX frame doesn't require ACK frame.\n"); + _state = STATE_IDLE; + dev->cb(dev, IEEE802154_RADIO_INDICATION_RX_DONE); } /* In case the packet is an ACK and the ACK filter is disabled, * indicate the frame reception */ @@ -770,6 +725,21 @@ static int _set_frame_filter_mode(ieee802154_dev_t *dev, ieee802154_filter_mode_ return 0; } +static int _get_frame_filter_mode(ieee802154_dev_t *dev, ieee802154_filter_mode_t *mode) +{ + (void) dev; + if (cfg.promisc) { + *mode = IEEE802154_FILTER_PROMISC; + } + else if (!cfg.ack_filter) { + *mode = IEEE802154_FILTER_ACK_ONLY; + } + else { + *mode = IEEE802154_FILTER_ACCEPT; + } + return 0; +} + static int _set_csma_params(ieee802154_dev_t *dev, const ieee802154_csma_be_t *bd, int8_t retries) { @@ -823,4 +793,5 @@ static const ieee802154_radio_ops_t nrf802154_ops = { .config_addr_filter = _config_addr_filter, .config_src_addr_match = _config_src_addr_match, .set_frame_filter_mode = _set_frame_filter_mode, + .get_frame_filter_mode = _get_frame_filter_mode, }; diff --git a/drivers/include/net/netdev/ieee802154_submac.h b/drivers/include/net/netdev/ieee802154_submac.h index ca623d931005..87f2d39e32c2 100644 --- a/drivers/include/net/netdev/ieee802154_submac.h +++ b/drivers/include/net/netdev/ieee802154_submac.h @@ -49,7 +49,7 @@ typedef struct { netdev_ieee802154_t dev; /**< IEEE 802.15.4 netdev descriptor */ ieee802154_submac_t submac; /**< IEEE 802.15.4 SubMAC descriptor */ ztimer_t ack_timer; /**< ztimer descriptor for the ACK timeout timer */ - int isr_flags; /**< netdev submac @ref NETDEV_EVENT_ISR flags */ + uint32_t isr_flags; /**< netdev submac @ref NETDEV_EVENT_ISR flags */ int bytes_tx; /**< size of the sent frame or tx error */ int8_t retrans; /**< number of frame retransmissions of the last TX */ bool dispatch; /**< whether an event should be dispatched or not */ diff --git a/drivers/kw2xrf/kw2xrf_radio_hal.c b/drivers/kw2xrf/kw2xrf_radio_hal.c index e66f47bfb00d..be6da4d2567e 100644 --- a/drivers/kw2xrf/kw2xrf_radio_hal.c +++ b/drivers/kw2xrf/kw2xrf_radio_hal.c @@ -705,6 +705,7 @@ static int _set_csma_params(ieee802154_dev_t *dev, const ieee802154_csma_be_t *b static const ieee802154_radio_ops_t kw2xrf_ops = { .caps = IEEE802154_CAP_24_GHZ + | IEEE802154_CAP_AUTO_ACK | IEEE802154_CAP_IRQ_CRC_ERROR | IEEE802154_CAP_IRQ_RX_START | IEEE802154_CAP_IRQ_TX_DONE diff --git a/drivers/mrf24j40/mrf24j40_radio_hal.c b/drivers/mrf24j40/mrf24j40_radio_hal.c index ee01f9c68a4f..0cfb1ceaeff7 100644 --- a/drivers/mrf24j40/mrf24j40_radio_hal.c +++ b/drivers/mrf24j40/mrf24j40_radio_hal.c @@ -343,6 +343,7 @@ int _set_frame_retrans(ieee802154_dev_t *hal, uint8_t retrans) static const ieee802154_radio_ops_t mrf24j40_ops = { .caps = IEEE802154_CAP_24_GHZ + | IEEE802154_CAP_AUTO_ACK | IEEE802154_CAP_IRQ_TX_DONE | IEEE802154_CAP_FRAME_RETRANS | IEEE802154_CAP_FRAME_RETRANS_INFO diff --git a/drivers/netdev_ieee802154_submac/Makefile.include b/drivers/netdev_ieee802154_submac/Makefile.include new file mode 100644 index 000000000000..3b3230fef92c --- /dev/null +++ b/drivers/netdev_ieee802154_submac/Makefile.include @@ -0,0 +1 @@ +PSEUDOMODULES += netdev_ieee802154_submac_soft_ack diff --git a/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c b/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c index 9a96cb4ec66d..32bc07cc64cb 100644 --- a/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c +++ b/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c @@ -13,20 +13,39 @@ * @author José I. Alamos */ +#include +#include + +#include "atomic_utils.h" +#include "irq.h" +#include "net/ieee802154/radio.h" #include "net/netdev/ieee802154_submac.h" #include "event/thread.h" +#define ENABLE_DEBUG 0 +#include "debug.h" + static const ieee802154_submac_cb_t _cb; static const netdev_driver_t netdev_submac_driver; +static uint32_t _isr_flags_get_clear(netdev_ieee802154_submac_t *netdev_submac, uint32_t clear) +{ + return atomic_fetch_and_u32(&netdev_submac->isr_flags, ~clear); +} + +static void _isr_flags_set(netdev_ieee802154_submac_t *netdev_submac, uint32_t set) +{ + atomic_fetch_or_u32(&netdev_submac->isr_flags, set); +} + static void _ack_timeout(void *arg) { netdev_ieee802154_submac_t *netdev_submac = arg; netdev_t *netdev = arg; - netdev_submac->isr_flags |= NETDEV_SUBMAC_FLAGS_ACK_TIMEOUT; - + _isr_flags_set(netdev_submac, NETDEV_SUBMAC_FLAGS_ACK_TIMEOUT); + DEBUG("IEEE802154 submac: _ack_timeout(): post NETDEV_EVENT_ISR\n"); netdev->event_callback(netdev, NETDEV_EVENT_ISR); } @@ -137,8 +156,8 @@ void ieee802154_submac_bh_request(ieee802154_submac_t *submac) submac); netdev_t *netdev = &netdev_submac->dev.netdev; - netdev_submac->isr_flags |= NETDEV_SUBMAC_FLAGS_BH_REQUEST; - + _isr_flags_set(netdev_submac, NETDEV_SUBMAC_FLAGS_BH_REQUEST); + DEBUG("IEEE802154 submac: ieee802154_submac_bh_request(): post NETDEV_EVENT_ISR\n"); netdev->event_callback(netdev, NETDEV_EVENT_ISR); } @@ -147,7 +166,8 @@ void ieee802154_submac_ack_timer_set(ieee802154_submac_t *submac) netdev_ieee802154_submac_t *netdev_submac = container_of(submac, netdev_ieee802154_submac_t, submac); - + ieee802154_submac_ack_timer_cancel(submac); + DEBUG("IEEE802154 submac: Setting ACK timeout %"PRIu16" us\n", submac->ack_timeout_us); ztimer_set(ZTIMER_USEC, &netdev_submac->ack_timer, submac->ack_timeout_us); } @@ -156,11 +176,10 @@ void ieee802154_submac_ack_timer_cancel(ieee802154_submac_t *submac) netdev_ieee802154_submac_t *netdev_submac = container_of(submac, netdev_ieee802154_submac_t, submac); - + DEBUG("IEEE802154 submac: Removing ACK timeout\n"); ztimer_remove(ZTIMER_USEC, &netdev_submac->ack_timer); /* Prevent a race condition between the RX_DONE event and the ACK timeout */ - netdev_submac->isr_flags &= ~NETDEV_SUBMAC_FLAGS_ACK_TIMEOUT; - + _isr_flags_get_clear(netdev_submac, NETDEV_SUBMAC_FLAGS_ACK_TIMEOUT); } static int _send(netdev_t *netdev, const iolist_t *pkt) @@ -189,59 +208,68 @@ static void _isr(netdev_t *netdev) dev); ieee802154_submac_t *submac = &netdev_submac->submac; - bool can_dispatch = true; + uint32_t flags; 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); + flags = _isr_flags_get_clear(netdev_submac, NETDEV_SUBMAC_FLAGS_CRC_ERROR); + if (flags & NETDEV_SUBMAC_FLAGS_CRC_ERROR) { + DEBUG("IEEE802154 submac:c NETDEV_SUBMAC_FLAGS_CRC_ERROR\n"); + ieee802154_submac_crc_error_cb(submac); + flags &= ~NETDEV_SUBMAC_FLAGS_CRC_ERROR; + continue; } + flags = _isr_flags_get_clear(netdev_submac, NETDEV_SUBMAC_FLAGS_ACK_TIMEOUT); if (flags & NETDEV_SUBMAC_FLAGS_ACK_TIMEOUT) { - ieee802154_submac_ack_timeout_fired(&netdev_submac->submac); + DEBUG("IEEE802154 submac: _isr(): NETDEV_SUBMAC_FLAGS_ACK_TIMEOUT\n"); + ieee802154_submac_ack_timeout_fired(submac); + flags &= ~NETDEV_SUBMAC_FLAGS_ACK_TIMEOUT; + continue; } - if (flags & NETDEV_SUBMAC_FLAGS_TX_DONE) { - ieee802154_submac_tx_done_cb(&netdev_submac->submac); + flags = _isr_flags_get_clear(netdev_submac, NETDEV_SUBMAC_FLAGS_BH_REQUEST); + if (flags & NETDEV_SUBMAC_FLAGS_BH_REQUEST) { + DEBUG("IEEE802154 submac: _isr(): NETDEV_SUBMAC_FLAGS_BH_REQUEST\n"); + ieee802154_submac_bh_process(submac); + flags &= ~NETDEV_SUBMAC_FLAGS_BH_REQUEST; + continue; } + flags = _isr_flags_get_clear(netdev_submac, NETDEV_SUBMAC_FLAGS_RX_DONE); if (flags & NETDEV_SUBMAC_FLAGS_RX_DONE) { + DEBUG("IEEE802154 submac: _isr(): NETDEV_SUBMAC_FLAGS_RX_DONE\n"); ieee802154_submac_rx_done_cb(submac); + flags &= ~NETDEV_SUBMAC_FLAGS_RX_DONE; + /* dispatch to netif */ } - if (flags & NETDEV_SUBMAC_FLAGS_CRC_ERROR) { - ieee802154_submac_crc_error_cb(submac); + flags = _isr_flags_get_clear(netdev_submac, NETDEV_SUBMAC_FLAGS_TX_DONE); + if (flags & NETDEV_SUBMAC_FLAGS_TX_DONE) { + DEBUG("IEEE802154 submac: _isr(): NETDEV_SUBMAC_FLAGS_TX_DONE\n"); + ieee802154_submac_tx_done_cb(submac); + flags &= ~NETDEV_SUBMAC_FLAGS_TX_DONE; + /* dispatch to netif */ } - if (flags) { - can_dispatch = false; - } + DEBUG("IEEE802154 submac: _isr_flags_get_clear(): pending flags: %"PRIu32"\n", flags); + assert(!flags); + break; - } while (netdev_submac->isr_flags != 0); + } while (1); 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; - } + /* The SubMAC will not generate further events after calling TX Done or RX Done. */ netdev_submac->dispatch = false; /* TODO: Prevent race condition when state goes to PREPARE */ + DEBUG("IEEE802154 submac: _isr(): dispatching %d\n", netdev_submac->ev); 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); - } + ieee802154_set_rx(submac); + } + else { + DEBUG("IEEE802154 submac: no events to dispatch\n"); } - } static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) @@ -268,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_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; } @@ -282,17 +333,22 @@ static void submac_tx_done(ieee802154_submac_t *submac, int status, if (info) { netdev_submac->retrans = info->retrans; } - + assert(!netdev_submac->dispatch); netdev_submac->dispatch = true; netdev_submac->ev = NETDEV_EVENT_TX_COMPLETE; switch (status) { case TX_STATUS_MEDIUM_BUSY: + DEBUG("IEEE802154 submac: NETDEV_EVENT_TX_MEDIUM_BUSY\n"); netdev_submac->bytes_tx = -EBUSY; break; case TX_STATUS_NO_ACK: + DEBUG("IEEE802154 submac: NETDEV_EVENT_TX_NOACK\n"); netdev_submac->bytes_tx = -EHOSTUNREACH; break; + default: + DEBUG("IEEE802154 submac: TX complete status: %d\n", status); + break; } } @@ -301,7 +357,9 @@ static void submac_rx_done(ieee802154_submac_t *submac) netdev_ieee802154_submac_t *netdev_submac = container_of(submac, netdev_ieee802154_submac_t, submac); + assert(!netdev_submac->dispatch); netdev_submac->dispatch = true; + DEBUG("IEEE802154 submac: NETDEV_EVENT_RX_COMPLETE\n"); netdev_submac->ev = NETDEV_EVENT_RX_COMPLETE; } @@ -319,19 +377,24 @@ static void _hal_radio_cb(ieee802154_dev_t *dev, ieee802154_trx_ev_t status) submac); netdev_t *netdev = &netdev_submac->dev.netdev; + DEBUG("IEEE802154 submac: _hal_radio_cb():\n"); switch (status) { case IEEE802154_RADIO_CONFIRM_TX_DONE: - netdev_submac->isr_flags |= NETDEV_SUBMAC_FLAGS_TX_DONE; + DEBUG("IEEE802154 submac: _hal_radio_cb(): IEEE802154_RADIO_CONFIRM_TX_DONE\n"); + _isr_flags_set(netdev_submac, NETDEV_SUBMAC_FLAGS_TX_DONE); break; case IEEE802154_RADIO_INDICATION_RX_DONE: - netdev_submac->isr_flags |= NETDEV_SUBMAC_FLAGS_RX_DONE; + DEBUG("IEEE802154 submac: _hal_radio_cb(): IEEE802154_RADIO_INDICATION_RX_DONE\n"); + _isr_flags_set(netdev_submac, NETDEV_SUBMAC_FLAGS_RX_DONE); break; case IEEE802154_RADIO_INDICATION_CRC_ERROR: - netdev_submac->isr_flags |= NETDEV_SUBMAC_FLAGS_CRC_ERROR; + DEBUG("IEEE802154 submac: _hal_radio_cb(): IEEE802154_RADIO_INDICATION_CRC_ERROR\n"); + _isr_flags_set(netdev_submac, NETDEV_SUBMAC_FLAGS_CRC_ERROR); break; default: break; } + DEBUG("IEEE802154 submac: _hal_radio_cb(): post NETDEV_EVENT_ISR\n"); netdev->event_callback(netdev, NETDEV_EVENT_ISR); } diff --git a/sys/include/net/ieee802154/radio.h b/sys/include/net/ieee802154/radio.h index b5ca731a03f0..9528e9a4dce3 100644 --- a/sys/include/net/ieee802154/radio.h +++ b/sys/include/net/ieee802154/radio.h @@ -72,6 +72,13 @@ typedef enum { * @ref TX_STATUS_MEDIUM_BUSY. */ IEEE802154_CAP_AUTO_CSMA = BIT1, + /** + * @brief the device supports automatic ACK frame transmission + * + * The device automatically sends an ACK frame when it receives a frame + * with the ACK Req bit set. + */ + IEEE802154_CAP_AUTO_ACK = BIT2, /** * @brief the device support ACK timeout interrupt * @@ -83,71 +90,71 @@ typedef enum { * * The ACK frame is not indicated to the upper layer. */ - IEEE802154_CAP_IRQ_ACK_TIMEOUT = BIT2, + IEEE802154_CAP_IRQ_ACK_TIMEOUT = BIT3, /** * @brief the device supports the IEEE802.15.4 2.4 GHz band * * It's assumed that @ref IEEE802154_CAP_IRQ_TX_DONE is present. */ - IEEE802154_CAP_24_GHZ = BIT3, + IEEE802154_CAP_24_GHZ = BIT4, /** * @brief the device support the IEEE802.15.4 Sub GHz band */ - IEEE802154_CAP_SUB_GHZ = BIT4, + IEEE802154_CAP_SUB_GHZ = BIT5, /** * @brief the device reports reception off frames with invalid CRC. */ - IEEE802154_CAP_IRQ_CRC_ERROR = BIT5, + IEEE802154_CAP_IRQ_CRC_ERROR = BIT6, /** * @brief the device reports when the transmission is done */ - IEEE802154_CAP_IRQ_TX_DONE = BIT6, + IEEE802154_CAP_IRQ_TX_DONE = BIT7, /** * @brief the device reports the start of a frame (SFD) when received. */ - IEEE802154_CAP_IRQ_RX_START = BIT7, + IEEE802154_CAP_IRQ_RX_START = BIT8, /** * @brief the device reports the start of a frame (SFD) was sent. */ - IEEE802154_CAP_IRQ_TX_START = BIT8, + IEEE802154_CAP_IRQ_TX_START = BIT9, /** * @brief the device reports the end of the CCA procedure */ - IEEE802154_CAP_IRQ_CCA_DONE = BIT9, + IEEE802154_CAP_IRQ_CCA_DONE = BIT10, /** * @brief the device provides the number of retransmissions * * It's assumed that @ref IEEE802154_CAP_FRAME_RETRANS is present. */ - IEEE802154_CAP_FRAME_RETRANS_INFO = BIT10, + IEEE802154_CAP_FRAME_RETRANS_INFO = BIT11, /** * @brief the device retains all register values when off. */ - IEEE802154_CAP_REG_RETENTION = BIT11, + IEEE802154_CAP_REG_RETENTION = BIT12, /** * @brief Binary Phase Shift Keying PHY mode */ - IEEE802154_CAP_PHY_BPSK = BIT12, + IEEE802154_CAP_PHY_BPSK = BIT13, /** * @brief Amplitude-Shift Keying PHY mode */ - IEEE802154_CAP_PHY_ASK = BIT13, + IEEE802154_CAP_PHY_ASK = BIT14, /** * @brief Offset Quadrature Phase-Shift Keying */ - IEEE802154_CAP_PHY_OQPSK = BIT14, + IEEE802154_CAP_PHY_OQPSK = BIT15, /** * @brief Multi-Rate Offset Quadrature Phase-Shift Keying PHY mode */ - IEEE802154_CAP_PHY_MR_OQPSK = BIT15, + IEEE802154_CAP_PHY_MR_OQPSK = BIT16, /** * @brief Multi-Rate Orthogonal Frequency-Division Multiplexing PHY mode */ - IEEE802154_CAP_PHY_MR_OFDM = BIT16, + IEEE802154_CAP_PHY_MR_OFDM = BIT17, /** * @brief Multi-Rate Frequency Shift Keying PHY mode */ - IEEE802154_CAP_PHY_MR_FSK = BIT17, + IEEE802154_CAP_PHY_MR_FSK = BIT18, /** * @brief the device supports source address match table. * @@ -156,7 +163,7 @@ typedef enum { * Request command from a child node, the Frame Pending bit of the ACK is * set if the source address matches one from the table. */ - IEEE802154_CAP_SRC_ADDR_MATCH = BIT18, + IEEE802154_CAP_SRC_ADDR_MATCH = BIT19, } ieee802154_rf_caps_t; /** @@ -532,8 +539,8 @@ struct ieee802154_radio_ops { * @param[in] dev IEEE802.15.4 device descriptor * @param[in] psdu PSDU frame to be sent * - * @return 0 on success - * @return negative errno on error + * @retval 0 on success + * @retval negative errno on error */ int (*write)(ieee802154_dev_t *dev, const iolist_t *psdu); @@ -572,8 +579,8 @@ struct ieee802154_radio_ops { * @param[in] info information of the received frame (LQI, RSSI). Can be * NULL if this information is not needed. * - * @return number of bytes written in @p buffer (0 if @p buf == NULL) - * @return -ENOBUFS if the frame doesn't fit in @p + * @retval number of bytes written in @p buffer (0 if @p buf == NULL) + * @retval -ENOBUFS if the frame doesn't fit in @p buf */ int (*read)(ieee802154_dev_t *dev, void *buf, size_t size, ieee802154_rx_info_t *info); /** @@ -585,8 +592,8 @@ struct ieee802154_radio_ops { * * @post the device is off * - * @return 0 on success - * @return negative errno on error + * @retval 0 on success + * @retval negative errno on error */ int (*off)(ieee802154_dev_t *dev); @@ -600,8 +607,8 @@ struct ieee802154_radio_ops { * * @param[in] dev IEEE802.15.4 device descriptor * - * @return 0 on success - * @return negative errno on error + * @retval 0 on success + * @retval negative errno on error */ int (*request_on)(ieee802154_dev_t *dev); @@ -627,9 +634,9 @@ struct ieee802154_radio_ops { * * @param[in] dev IEEE802.15.4 device descriptor * - * @return 0 if the device is on - * @return -EAGAIN if the device is still busy turning on - * @return negative errno on error + * @retval 0 if the device is on + * @retval -EAGAIN if the device is still busy turning on + * @retval negative errno on error */ int (*confirm_on)(ieee802154_dev_t *dev); @@ -674,8 +681,8 @@ struct ieee802154_radio_ops { * @param[in] dev IEEE802.15.4 device descriptor * @param[in] threshold the threshold in dBm. * - * @return 0 on success - * @return negative errno on error + * @retval 0 on success + * @retval negative errno on error */ int (*set_cca_threshold)(ieee802154_dev_t *dev, int8_t threshold); @@ -689,9 +696,9 @@ struct ieee802154_radio_ops { * @param[in] dev IEEE802.15.4 device descriptor * @param[in] mode the CCA mode * - * @return 0 on success - * @return -ENOTSUP if the mode is not supported - * @return negative errno on error + * @retval 0 on success + * @retval -ENOTSUP if the mode is not supported + * @retval negative errno on error */ int (*set_cca_mode)(ieee802154_dev_t *dev, ieee802154_cca_mode_t mode); @@ -710,9 +717,9 @@ struct ieee802154_radio_ops { * @param[in] dev IEEE802.15.4 device descriptor * @param[in] conf the PHY configuration * - * @return 0 on success - * @return -EINVAL if the configuration is not valid for the device. - * @return <0 error, return value is negative errno indicating the cause. + * @retval 0 on success + * @retval -EINVAL if the configuration is not valid for the device. + * @retval <0 error, return value is negative errno indicating the cause. */ int (*config_phy)(ieee802154_dev_t *dev, const ieee802154_phy_conf_t *conf); @@ -727,8 +734,8 @@ struct ieee802154_radio_ops { * @param[in] dev IEEE802.15.4 device descriptor * @param[in] retrans the number of retransmissions attempts. * - * @return 0 on success - * @return negative errno on error + * @retval 0 on success + * @retval negative errno on error */ int (*set_frame_retrans)(ieee802154_dev_t *dev, uint8_t retrans); @@ -746,26 +753,39 @@ struct ieee802154_radio_ops { * ieee802154_radio_request_transmit function is * equivalent to CCA send. * - * @return 0 on success - * @return -EINVAL if the settings are not supported. - * @return negative errno on error + * @retval 0 on success + * @retval -EINVAL if the settings are not supported. + * @retval negative errno on error */ int (*set_csma_params)(ieee802154_dev_t *dev, const ieee802154_csma_be_t *bd, int8_t retries); /** - * @brief Set the frame filter moder. + * @brief Set the frame filter mode. * * @pre the device is on * * @param[in] dev IEEE802.15.4 device descriptor * @param[in] mode address filter mode * - * @return 0 on success - * @return negative errno on error + * @retval 0 on success + * @retval negative errno on error */ int (*set_frame_filter_mode)(ieee802154_dev_t *dev, ieee802154_filter_mode_t mode); + /** + * @brief Get the frame filter mode. + * + * @pre the device is on + * + * @param[in] dev IEEE802.15.4 device descriptor + * @param[out] mode address filter mode + * + * @retval 0 on success + * @retval negative errno on error + */ + int (*get_frame_filter_mode)(ieee802154_dev_t *dev, ieee802154_filter_mode_t *mode); + /** * @brief Configure the address filter. * @@ -778,8 +798,8 @@ struct ieee802154_radio_ops { * @param[in] cmd command for the address filter * @param[in] value value for @p cmd. * - * @return 0 on success - * @return negative errno on error + * @retval 0 on success + * @retval negative errno on error */ int (*config_addr_filter)(ieee802154_dev_t *dev, ieee802154_af_cmd_t cmd, const void *value); @@ -798,13 +818,27 @@ struct ieee802154_radio_ops { * @param[in] cmd command for the source address match configuration * @param[in] value value associated to @p cmd. * - * @return 0 on success - * @return negative errno on error + * @retval 0 on success + * @retval negative errno on error */ int (*config_src_addr_match)(ieee802154_dev_t *dev, ieee802154_src_match_t cmd, const void *value); }; +/** + * @brief Check if the device has a specific capability + * + * @param[in] dev IEEE802.15.4 device descriptor + * @param[in] cap capabilities to check for + * + * @retval true if the device has the capability + * @retval false if the device doesn't have the capability + */ +static inline bool ieee802154_radio_has_capability(ieee802154_dev_t *dev, uint32_t cap) +{ + return (dev->driver->caps & cap) == cap; +} + /** * @brief Shortcut to @ref ieee802154_radio_ops::write * @@ -1014,6 +1048,25 @@ static inline int ieee802154_radio_set_frame_filter_mode(ieee802154_dev_t *dev, return dev->driver->set_frame_filter_mode(dev, mode); } +/** + * @brief Shortcut to @ref ieee802154_radio_ops::get_frame_filter_mode + * + * @pre the device is on + * + * @param[in] dev IEEE802.15.4 device descriptor + * @param[out] mode frame filter mode + * + * @return result of @ref ieee802154_radio_ops::get_frame_filter_mode + */ +static inline int ieee802154_radio_get_frame_filter_mode(ieee802154_dev_t *dev, + ieee802154_filter_mode_t *mode) +{ + if (dev->driver->get_frame_filter_mode) { + return dev->driver->get_frame_filter_mode(dev, mode); + } + return -ENOTSUP; +} + /** * @brief Shortcut to @ref ieee802154_radio_ops::set_frame_retrans * diff --git a/sys/net/link_layer/ieee802154/submac.c b/sys/net/link_layer/ieee802154/submac.c index aae582fdb20b..a1c32bf04257 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 @@ -377,27 +414,33 @@ ieee802154_fsm_state_t ieee802154_submac_process_ev(ieee802154_submac_t *submac, switch (submac->fsm_state) { case IEEE802154_FSM_STATE_RX: + DEBUG("IEEE802154 submac: ieee802154_submac_process_ev(): IEEE802154_FSM_STATE_RX + %s\n", str_ev[ev]); new_state = _fsm_state_rx(submac, ev); break; case IEEE802154_FSM_STATE_IDLE: + DEBUG("IEEE802154 submac: ieee802154_submac_process_ev(): IEEE802154_FSM_STATE_IDLE + %s\n", str_ev[ev]); new_state = _fsm_state_idle(submac, ev); break; case IEEE802154_FSM_STATE_PREPARE: + DEBUG("IEEE802154 submac: ieee802154_submac_process_ev(): IEEE802154_FSM_STATE_PREPARE + %s\n", str_ev[ev]); new_state = _fsm_state_prepare(submac, ev); break; case IEEE802154_FSM_STATE_TX: + DEBUG("IEEE802154 submac: ieee802154_submac_process_ev(): IEEE802154_FSM_STATE_TX + %s\n", str_ev[ev]); new_state = _fsm_state_tx(submac, ev); break; case IEEE802154_FSM_STATE_WAIT_FOR_ACK: + 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; default: + DEBUG("IEEE802154 submac: ieee802154_submac_process_ev(): INVALID STATE\n"); new_state = IEEE802154_FSM_STATE_INVALID; } 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; @@ -408,6 +451,7 @@ 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) { + DEBUG("IEEE802154 submac: ieee802154_send(): Sending aborted, current state is %s\n", str_states[current_state]); return -EBUSY; } @@ -417,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; @@ -424,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; @@ -809,6 +861,8 @@ int ieee802154_set_rx(ieee802154_submac_t *submac) } break; default: + DEBUG("IEEE802154 submac: ieee802154_set_rx(): Setting RX failed, currently in %s\n", + str_states[current_state]); break; }