Skip to content

ieee802154/submac: add automatic ACK transmission when AUTO_ACK is not supported by driver #21533

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion cpu/native/socket_zep/socket_zep.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
#define ENABLE_DEBUG 0
#include "debug.h"

#ifndef SOCKET_ZEP_AUTO_ACK
#define SOCKET_ZEP_AUTO_ACK 1
#endif

#define _UNIX_NTP_ERA_OFFSET (2208988800U)
/* can't use timex.h's US_PER_SEC as timeval's tv_usec is signed long
* (https://pubs.opengroup.org/onlinepubs/9699919799.2016edition/basedefs/time.h.html) */
Expand Down Expand Up @@ -257,6 +261,7 @@
}
}

MAYBE_UNUSED
static void _send_ack(void *arg)
{
ieee802154_dev_t *dev = arg;
Expand Down Expand Up @@ -359,13 +364,17 @@
zepdev->rcv_len = res;
dev->cb(dev, IEEE802154_RADIO_INDICATION_RX_START);

#if SOCKET_ZEP_AUTO_ACK
/* 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);
}
#else
dev->cb(dev, IEEE802154_RADIO_INDICATION_RX_DONE);
#endif

return;
out:
Expand Down Expand Up @@ -505,6 +514,13 @@
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;
Expand Down Expand Up @@ -544,7 +560,7 @@
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;

Check warning on line 563 in cpu/native/socket_zep/socket_zep.c

View workflow job for this annotation

GitHub Actions / static-tests

line is longer than 100 characters
DEBUG("socket_zep::request_transmit(%u bytes, %u µs)\n", zepdev->snd_len, time_tx);

dev->cb(dev, IEEE802154_RADIO_INDICATION_TX_START);
Expand All @@ -560,7 +576,14 @@
{
(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;
}

Expand Down Expand Up @@ -628,6 +651,7 @@
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);
Expand Down Expand Up @@ -659,6 +683,7 @@
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;
}

Expand Down Expand Up @@ -703,6 +728,7 @@
static const ieee802154_radio_ops_t socket_zep_rf_ops = {
.caps = IEEE802154_CAP_24_GHZ
| IEEE802154_CAP_AUTO_CSMA
| (IS_ACTIVE(SOCKET_ZEP_AUTO_ACK) ? IEEE802154_CAP_AUTO_ACK : 0)
| IEEE802154_CAP_IRQ_TX_DONE
| IEEE802154_CAP_IRQ_TX_START
| IEEE802154_CAP_IRQ_RX_START
Expand All @@ -723,6 +749,7 @@
.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)
Expand Down
2 changes: 1 addition & 1 deletion drivers/include/net/netdev/ieee802154_submac.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
138 changes: 97 additions & 41 deletions drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,40 @@
* @author José I. Alamos <[email protected]>
*/

#include "irq.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)
{
unsigned state = irq_disable();
uint32_t flags = netdev_submac->isr_flags;
netdev_submac->isr_flags &= ~clear;
irq_restore(state);
return flags;
}

static void _isr_flags_set(netdev_ieee802154_submac_t *netdev_submac, uint32_t set)
{
unsigned state = irq_disable();
netdev_submac->isr_flags |= set;
irq_restore(state);
}

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);
}

Expand All @@ -43,8 +63,9 @@
static int _get(netdev_t *netdev, netopt_t opt, void *value, size_t max_len)
{
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
netdev_ieee802154_submac_t *netdev_submac = container_of(netdev_ieee802154, netdev_ieee802154_submac_t, dev);

Check warning on line 66 in drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c

View workflow job for this annotation

GitHub Actions / static-tests

line is longer than 100 characters
ieee802154_submac_t *submac = &netdev_submac->submac;
int ret;

switch (opt) {
case NETOPT_STATE:
Expand All @@ -59,6 +80,20 @@
case NETOPT_IEEE802154_PHY:
*((uint8_t*) value) = ieee802154_get_phy_mode(submac);
return 1;
case NETOPT_PROMISCUOUSMODE: {
ieee802154_filter_mode_t mode;
ret = ieee802154_radio_get_frame_filter_mode(&submac->dev, &mode);
if (ret < 0) {
return ret;
}
*((netopt_enable_t *)value) = (mode == IEEE802154_FILTER_PROMISC)
? NETOPT_ENABLE : NETOPT_DISABLE;
return 1;
}
case NETOPT_AUTOACK:
*((netopt_enable_t *)value)
= ieee802154_radio_has_capability(&submac->dev, IEEE802154_CAP_AUTO_ACK);
return 1;
default:
break;
}
Expand Down Expand Up @@ -134,8 +169,8 @@
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);
}

Expand All @@ -144,7 +179,8 @@
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 %"PRIu32" us\n", submac->ack_timeout_us);
ztimer_set(ZTIMER_USEC, &netdev_submac->ack_timer, submac->ack_timeout_us);
}

Expand All @@ -153,11 +189,10 @@
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)
Expand Down Expand Up @@ -186,59 +221,68 @@
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)
Expand Down Expand Up @@ -279,17 +323,22 @@
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;
}
}

Expand All @@ -298,7 +347,9 @@
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;
}

Expand All @@ -316,19 +367,24 @@
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);
}

Expand Down
Loading
Loading