diff --git a/cpu/nrf52/include/nrf802154.h b/cpu/nrf52/include/nrf802154.h index de55018e7795..6da7afa4d885 100644 --- a/cpu/nrf52/include/nrf802154.h +++ b/cpu/nrf52/include/nrf802154.h @@ -42,6 +42,14 @@ extern "C" { #endif +typedef enum { + NRF802154_STATE_DISABLED, + NRF802154_STATE_RX, + NRF802154_STATE_TX, + NRF802154_STATE_AACK, + NRF802154_STATE_ACKWAIT, +} nrf802154_state_t; + /** * @brief Export the netdev device descriptor */ diff --git a/cpu/nrf52/radio/nrf802154/nrf802154.c b/cpu/nrf52/radio/nrf802154/nrf802154.c index d48f27e7107b..6d16eb6a9f81 100644 --- a/cpu/nrf52/radio/nrf802154/nrf802154.c +++ b/cpu/nrf52/radio/nrf802154/nrf802154.c @@ -17,6 +17,7 @@ * @author Hauke Petersen * @author Dimitri Nahm * @author Semjon Kerner + * @author Koen Zandberg * @} */ @@ -35,6 +36,21 @@ #define ENABLE_DEBUG (0) #include "debug.h" +/** + * @brief Default number of retransmissions + */ +#define NRF802154_DEFAULT_RETRANS (3U) + +/** + * @brief Maximum number of retransmissions as per IEEE802.15.4 + */ +#define NRF802154_MAX_RETRANS (7U) + +/* Internal device option flags */ +#define NRF802154_OPT_AUTOACK (0x0080) /**< Auto ACK active */ +#define NRF802154_OPT_ACK_REQUEST (0x0100) /**< Transmit unicast with + * Ack request set */ + static const netdev_driver_t nrf802154_netdev_driver; netdev_ieee802154_t nrf802154_dev = { @@ -59,6 +75,16 @@ netdev_ieee802154_t nrf802154_dev = { 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 aackbuf[4]; + +static uint8_t _last_seq_no; +/* Max number of retransmission attempts configured */ +static uint8_t _retrans_max; +/* Retransmission counter, the retrans_max + 1 indicates all retransmissions + * failed */ +static uint8_t _retransmissions; +/* Number of retransmissions required for the last transmission */ +static uint8_t _retrans_used; #define ED_RSSISCALE (4U) #define ED_RSSIOFFS (92U) @@ -67,10 +93,51 @@ static uint8_t txbuf[IEEE802154_FRAME_LEN_MAX + 3]; /* len PHR + PSDU + LQI */ #define TX_COMPLETE (0x2) #define LIFS (40U) #define SIFS (12U) +#define TACK (54U) /* Ack timeout in symbols */ #define SIFS_MAXPKTSIZE (18U) #define TIMER_FREQ (62500UL) -static volatile uint8_t _state; +static nrf802154_state_t _state; +static volatile uint8_t _event_flags; static mutex_t _txlock; +static uint16_t _setting_flags; + +static inline bool _setting_isset(uint16_t flag) +{ + return _setting_flags & flag; +} + +static inline void _setting_enable(uint16_t flag) +{ + _setting_flags |= flag; +} + +static inline void _setting_disable(uint16_t flag) +{ + _setting_flags &= ~flag; +} + +static inline bool _ack_expected(void) +{ + return (_setting_isset(NRF802154_OPT_ACK_REQUEST) && + txbuf[1] & IEEE802154_FCF_ACK_REQ); +} + +static inline bool _ack_xmit_required(void) +{ + return (_setting_isset(NRF802154_OPT_AUTOACK) && + rxbuf[1] & IEEE802154_FCF_ACK_REQ); +} + +static inline void _set_and_start_timer(unsigned timeout) +{ + timer_set(NRF802154_TIMER, 0, timeout); + timer_start(NRF802154_TIMER); +} + +static inline unsigned _get_tx_ifs(void) +{ + return (txbuf[0] > SIFS_MAXPKTSIZE) ? LIFS : SIFS; +} /** * @brief Set radio into DISABLED state @@ -87,37 +154,63 @@ static void _disable(void) } /** - * @brief Set radio into RXIDLE state + * @brief Set the radio in RXIDLE */ -static void _enable_rx(void) +static void _enable_listen(void) { DEBUG("[nrf802154] Set device state to RXIDLE\n"); + NRF_RADIO->PACKETPTR = (uint32_t)rxbuf; + NRF_RADIO->EVENTS_RXREADY = 0; /* set device into RXIDLE state */ if (NRF_RADIO->STATE != RADIO_STATE_STATE_RxIdle) { _disable(); } - NRF_RADIO->PACKETPTR = (uint32_t)rxbuf; - NRF_RADIO->EVENTS_RXREADY = 0; + else { + NRF_RADIO->TASKS_RXEN = 1; + return; + } NRF_RADIO->TASKS_RXEN = 1; while (!(NRF_RADIO->EVENTS_RXREADY)) {}; DEBUG("[nrf802154] Device state: RXIDLE\n"); } +/** + * @brief Set radio into RXIDLE state and start listening for new frames + */ +static void _enable_rx(void) +{ + _state = NRF802154_STATE_RX; + _enable_listen(); +} + +static void _load_tx(void) +{ + NRF_RADIO->PACKETPTR = (uint32_t)txbuf; + NRF_RADIO->EVENTS_TXREADY = 0; +} + +static void _isr_tx_complete(void) +{ + _set_and_start_timer(_get_tx_ifs()); + _event_flags |= TX_COMPLETE; + _retrans_used = _retransmissions; + nrf802154_dev.netdev.event_callback(&nrf802154_dev.netdev, NETDEV_EVENT_ISR); + _enable_rx(); +} + /** * @brief Set radio into TXIDLE state */ static void _enable_tx(void) { + _state = NRF802154_STATE_TX; DEBUG("[nrf802154] Set device state to TXIDLE\n"); /* set device into TXIDLE state */ if (NRF_RADIO->STATE != RADIO_STATE_STATE_TxIdle) { _disable(); } - NRF_RADIO->PACKETPTR = (uint32_t)txbuf; - NRF_RADIO->EVENTS_TXREADY = 0; + _load_tx(); NRF_RADIO->TASKS_TXEN = 1; - while (!(NRF_RADIO->EVENTS_TXREADY)) {}; - DEBUG("[nrf802154] Device state: TXIDLE\n"); } /** @@ -130,10 +223,34 @@ static void _enable_tx(void) } /* reset RX state and listen for new packets */ - _state &= ~RX_COMPLETE; + _event_flags &= ~RX_COMPLETE; NRF_RADIO->TASKS_START = 1; } +static void _send_ack(void) +{ + aackbuf[0] = 5; /* Length including 2 byte fcs */ + aackbuf[1] = IEEE802154_FCF_TYPE_ACK; /* Ack type */ + aackbuf[2] = 0; /* Other bits zeroed */ + aackbuf[3] = _last_seq_no; /* Sequence number */ + /* We should be in the Rx state, transitioning to disabled should be 0 us */ + if (NRF_RADIO->STATE != RADIO_STATE_STATE_TxIdle) { + _disable(); + } + NRF_RADIO->PACKETPTR = (uint32_t)aackbuf; + NRF_RADIO->EVENTS_TXREADY = 0; + NRF_RADIO->TASKS_TXEN = 1; +} + +static bool _ack_frame_filter(void) +{ + size_t psdu_len = rxbuf[0]; + return ((NRF_RADIO->CRCSTATUS == 1) && + (psdu_len == (IEEE802154_FCF_LEN + 1)) && + ((rxbuf[1] & IEEE802154_FCF_TYPE_MASK) == IEEE802154_FCF_TYPE_ACK) && + txbuf[3] == ieee802154_get_seq(&rxbuf[1])); +} + static void _set_chan(uint16_t chan) { assert((chan >= IEEE802154_CHANNEL_MIN) && (chan <= IEEE802154_CHANNEL_MAX)); @@ -187,8 +304,27 @@ static void _timer_cb(void *arg, int chan) { (void)arg; (void)chan; - mutex_unlock(&_txlock); timer_stop(NRF802154_TIMER); + switch (_state) { + case NRF802154_STATE_AACK: + /* Transmit ack */ + _send_ack(); + break; + case NRF802154_STATE_ACKWAIT: + /* Timeout waiting for ACK, TACK is more than SIFS and LIFS, no + * need to wait addionally */ + if (_retransmissions >= _retrans_max) { + _isr_tx_complete(); + } + else { + _enable_tx(); + } + _retransmissions++; + break; + default: + mutex_unlock(&_txlock); + break; + } } static int _init(netdev_t *dev) @@ -197,6 +333,7 @@ static int _init(netdev_t *dev) int result = timer_init(NRF802154_TIMER, TIMER_FREQ, _timer_cb, NULL); assert(result >= 0); + timer_stop(NRF802154_TIMER); (void)result; timer_stop(NRF802154_TIMER); @@ -206,7 +343,16 @@ static int _init(netdev_t *dev) /* reset buffer */ rxbuf[0] = 0; txbuf[0] = 0; - _state = 0; + _event_flags = 0; + _setting_flags = 0; + _retrans_max = NRF802154_DEFAULT_RETRANS; + + static const netopt_enable_t enable = NETOPT_ENABLE; + /* Use the setter here to ensure setting propagates to netdev_ieee802154 */ + nrf802154_dev.netdev.driver->set(&nrf802154_dev.netdev, NETOPT_AUTOACK, + &enable, sizeof(enable)); + nrf802154_dev.netdev.driver->set(&nrf802154_dev.netdev, NETOPT_ACK_REQ, + &enable, sizeof(enable)); /* power on peripheral */ NRF_RADIO->POWER = 1; @@ -266,6 +412,8 @@ static int _send(netdev_t *dev, const iolist_t *iolist) mutex_lock(&_txlock); + _retransmissions = 0; + /* copy packet data into the transmit buffer */ unsigned int len = 0; for (; iolist; iolist = iolist->iol_next) { @@ -301,7 +449,7 @@ static int _recv(netdev_t *dev, void *buf, size_t len, void *info) size_t pktlen = (size_t)rxbuf[0] - IEEE802154_FCS_LEN; /* check if packet data is readable */ - if (!(_state & RX_COMPLETE)) { + if (!(_event_flags & RX_COMPLETE)) { DEBUG("[nrf802154] recv: no packet data available\n"); return 0; } @@ -350,12 +498,17 @@ static void _isr(netdev_t *dev) if (!nrf802154_dev.netdev.event_callback) { return; } - if (_state & RX_COMPLETE) { + if (_event_flags & RX_COMPLETE) { nrf802154_dev.netdev.event_callback(dev, NETDEV_EVENT_RX_COMPLETE); } - if (_state & TX_COMPLETE) { - nrf802154_dev.netdev.event_callback(dev, NETDEV_EVENT_TX_COMPLETE); - _state &= ~TX_COMPLETE; + if (_event_flags & TX_COMPLETE) { + if (_retrans_used > _retrans_max) { + nrf802154_dev.netdev.event_callback(dev, NETDEV_EVENT_TX_NOACK); + } + else { + nrf802154_dev.netdev.event_callback(dev, NETDEV_EVENT_TX_COMPLETE); + } + _event_flags &= ~TX_COMPLETE; } } @@ -378,6 +531,23 @@ static int _get(netdev_t *dev, netopt_t opt, void *value, size_t max_len) assert(max_len >= sizeof(int16_t)); *((int16_t *)value) = _get_txpower(); return sizeof(int16_t); + case NETOPT_AUTOACK: + assert(max_len >= sizeof(netopt_enable_t)); + *(netopt_enable_t*)value = _setting_isset(NRF802154_OPT_AUTOACK); + return sizeof(netopt_enable_t); + case NETOPT_RETRANS: + assert(max_len >= sizeof(uint8_t)); + *(uint8_t*)value = _retrans_max; + return sizeof(uint8_t); + case NETOPT_ACK_REQ: + assert(max_len >= sizeof(netopt_enable_t)); + *(netopt_enable_t*)value = _setting_isset(NRF802154_OPT_ACK_REQUEST); + return sizeof(netopt_enable_t); + case NETOPT_TX_RETRIES_NEEDED: + assert(max_len >= sizeof(uint8_t)); + *(uint8_t*)value = _retrans_used > _retrans_max ? _retrans_max + : _retrans_used; + return sizeof(uint8_t); default: return netdev_ieee802154_get((netdev_ieee802154_t *)dev, @@ -405,7 +575,25 @@ static int _set(netdev_t *dev, netopt_t opt, assert(value_len == sizeof(int16_t)); _set_txpower(*((int16_t *)value)); return sizeof(int16_t); - + case NETOPT_AUTOACK: + assert(value_len == sizeof(netopt_enable_t)); + *(netopt_enable_t*)value ? _setting_enable(NRF802154_OPT_AUTOACK) + : _setting_disable(NRF802154_OPT_AUTOACK); + return sizeof(netopt_enable_t); + case NETOPT_RETRANS: + assert(value_len == sizeof(uint8_t)); + if (*(uint8_t*)value > NRF802154_MAX_RETRANS) { + return -EINVAL; + } + else { + _retrans_max = *(uint8_t*)value; + } + return sizeof(uint8_t); + case NETOPT_ACK_REQ: + assert(value_len == sizeof(netopt_enable_t)); + *(netopt_enable_t*)value ? _setting_enable(NRF802154_OPT_ACK_REQUEST) + : _setting_disable(NRF802154_OPT_ACK_REQUEST); + /* Intentionally falls through */ default: return netdev_ieee802154_set((netdev_ieee802154_t *)dev, opt, value, value_len); @@ -423,30 +611,53 @@ void isr_radio(void) switch(state) { case RADIO_STATE_STATE_RxIdle: /* only process packet if event callback is set and CRC is valid */ - if ((nrf802154_dev.netdev.event_callback) && - (NRF_RADIO->CRCSTATUS == 1) && - (netdev_ieee802154_dst_filter(&nrf802154_dev, - &rxbuf[1]) == 0)) { - _state |= RX_COMPLETE; + if (_state == NRF802154_STATE_RX) { + if ((nrf802154_dev.netdev.event_callback) && + (NRF_RADIO->CRCSTATUS == 1) && + (netdev_ieee802154_dst_filter(&nrf802154_dev, + &rxbuf[1]) == 0)) { + _event_flags |= RX_COMPLETE; + if (_ack_xmit_required()) { + _last_seq_no = ieee802154_get_seq(&rxbuf[1]); + _state = NRF802154_STATE_AACK; + _set_and_start_timer(SIFS); + } + nrf802154_dev.netdev.event_callback(&nrf802154_dev.netdev, NETDEV_EVENT_ISR); + } + else { + _reset_rx(); + } } - else { + else if (_state == NRF802154_STATE_ACKWAIT) { + /* Check if this is the expected ACK frame */ + if (_ack_frame_filter()) { + timer_stop(NRF802154_TIMER); + _state = NRF802154_STATE_TX; + _isr_tx_complete(); + } _reset_rx(); } break; case RADIO_STATE_STATE_Tx: case RADIO_STATE_STATE_TxIdle: case RADIO_STATE_STATE_TxDisable: - timer_start(NRF802154_TIMER); - DEBUG("[nrf802154] TX state: %x\n", (uint8_t)NRF_RADIO->STATE); - _state |= TX_COMPLETE; - _enable_rx(); + if (_state == NRF802154_STATE_TX) { + if (_ack_expected()) { + _state = NRF802154_STATE_ACKWAIT; + _set_and_start_timer(TACK); + _enable_listen(); + } + else { + _isr_tx_complete(); + } + } + else if (_state == NRF802154_STATE_AACK) { + _enable_rx(); + } break; default: DEBUG("[nrf802154] Unhandled state: %x\n", (uint8_t)NRF_RADIO->STATE); } - if (_state) { - nrf802154_dev.netdev.event_callback(&nrf802154_dev.netdev, NETDEV_EVENT_ISR); - } } else { DEBUG("[nrf802154] Unknown interrupt triggered\n");