diff --git a/drivers/ieee802154/Kconfig b/drivers/ieee802154/Kconfig index 611af2b01b9..1202545e33d 100644 --- a/drivers/ieee802154/Kconfig +++ b/drivers/ieee802154/Kconfig @@ -135,6 +135,18 @@ config IEEE802154_2015 help Enable radio driver support for IEEE 802.15.4-2015 frames, including security handling of frames and ACKs. +config IEEE802154_DELAY_TRX_ACC + int "Clock accuracy for delayed operations" + default 20 + help + Accuracy of the clock used for scheduling radio delayed operations (delayed transmission + or delayed reception), in ppm. + +config IEEE802154_CSL_ENDPOINT + bool "Enable support for CSL Endpoint" + help + Enable support for CSL Endpoint with delayed reception handling and CSL IE injection. + module = IEEE802154_DRIVER module-str = IEEE 802.15.4 driver module-help = Sets log level for IEEE 802.15.4 Device Drivers. diff --git a/drivers/ieee802154/ieee802154_nrf5.c b/drivers/ieee802154/ieee802154_nrf5.c index c6cfd68f710..1b54d76270e 100644 --- a/drivers/ieee802154/ieee802154_nrf5.c +++ b/drivers/ieee802154/ieee802154_nrf5.c @@ -57,7 +57,11 @@ static struct nrf5_802154_data nrf5_data; #define ACK_REQUEST_BIT (1 << 5) #define FRAME_PENDING_BYTE 1 #define FRAME_PENDING_BIT (1 << 4) -#define TXTIME_OFFSET_US (5 * USEC_PER_MSEC) +#define TXTIME_OFFSET_US (1 * USEC_PER_MSEC) + +#define DRX_SLOT_PH 0 /* Placeholder delayed reception window ID */ +#define DRX_SLOT_RX 1 /* Actual delayed reception window ID */ +#define PH_DURATION 10 /* Duration of the placeholder window, in microseconds */ #if defined(CONFIG_IEEE802154_NRF5_UICR_EUI64_ENABLE) #if defined(CONFIG_SOC_NRF5340_CPUAPP) @@ -217,6 +221,7 @@ static void nrf5_get_capabilities_at_boot(void) IEEE802154_HW_TX_RX_ACK | IEEE802154_HW_ENERGY_SCAN | ((caps & NRF_802154_CAPABILITY_DELAYED_TX) ? IEEE802154_HW_TXTIME : 0UL) | + ((caps & NRF_802154_CAPABILITY_DELAYED_RX) ? IEEE802154_HW_RXTIME : 0UL) | IEEE802154_HW_SLEEP_TO_TX | ((caps & NRF_802154_CAPABILITY_SECURITY) ? IEEE802154_HW_TX_SEC : 0UL); } @@ -565,6 +570,13 @@ static uint64_t nrf5_get_time(const struct device *dev) return nrf_802154_time_get(); } +static uint8_t nrf5_get_acc(const struct device *dev) +{ + ARG_UNUSED(dev); + + return CONFIG_IEEE802154_DELAY_TRX_ACC; +} + static int nrf5_start(const struct device *dev) { ARG_UNUSED(dev); @@ -582,7 +594,18 @@ static int nrf5_start(const struct device *dev) static int nrf5_stop(const struct device *dev) { +#if defined(CONFIG_IEEE802154_CSL_ENDPOINT) + if (nrf_802154_sleep_if_idle() != NRF_802154_SLEEP_ERROR_NONE) { + if (nrf5_data.event_handler) { + nrf5_data.event_handler(dev, IEEE802154_EVENT_SLEEP, NULL); + } else { + LOG_WRN("Transition to radio sleep cannot be handled."); + } + return 0; + } +#else ARG_UNUSED(dev); +#endif if (!nrf_802154_sleep()) { LOG_ERR("Error while stopping radio"); @@ -692,6 +715,35 @@ static void nrf5_config_mac_keys(struct ieee802154_key *mac_keys) } #endif /* CONFIG_NRF_802154_ENCRYPTION */ +#if defined(CONFIG_IEEE802154_CSL_ENDPOINT) +static void nrf5_receive_at(uint32_t start, uint32_t duration, uint8_t channel, uint32_t id) +{ + nrf_802154_receive_at(start - TXTIME_OFFSET_US, TXTIME_OFFSET_US, duration, channel, id); +} + +static void nrf5_config_csl_period(uint16_t period) +{ + nrf_802154_receive_at_cancel(DRX_SLOT_PH); + nrf_802154_receive_at_cancel(DRX_SLOT_RX); + + nrf_802154_csl_writer_period_set(period); + + /* A placeholder reception window is scheduled so that the radio driver is able to inject + * the proper CSL Phase in the transmitted CSL Information Elements. + */ + nrf5_receive_at(nrf5_data.csl_rx_time, PH_DURATION, nrf_802154_channel_get(), DRX_SLOT_PH); +} + +static void nrf5_schedule_rx(uint8_t channel, uint32_t start, uint32_t duration) +{ + nrf5_receive_at(start, duration, channel, DRX_SLOT_RX); + + /* The placeholder reception window is rescheduled for the next period */ + nrf_802154_receive_at_cancel(DRX_SLOT_PH); + nrf5_receive_at(nrf5_data.csl_rx_time, PH_DURATION, channel, DRX_SLOT_PH); +} +#endif /* CONFIG_IEEE802154_CSL_ENDPOINT */ + static int nrf5_configure(const struct device *dev, enum ieee802154_config_type type, const struct ieee802154_config *config) @@ -766,6 +818,43 @@ static int nrf5_configure(const struct device *dev, break; #endif /* CONFIG_NRF_802154_ENCRYPTION */ + case IEEE802154_CONFIG_ENH_ACK_HEADER_IE: { + uint8_t short_addr_le[SHORT_ADDRESS_SIZE]; + uint8_t ext_addr_le[EXTENDED_ADDRESS_SIZE]; + + sys_put_le16(config->ack_ie.short_addr, short_addr_le); +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + memcpy(ext_addr_le, config->ack_ie.ext_addr, EXTENDED_ADDRESS_SIZE); +#else + sys_memcpy_swap(ext_addr_le, config->ack_ie.ext_addr, EXTENDED_ADDRESS_SIZE); +#endif + + if (config->ack_ie.data_len > 0) { + nrf_802154_ack_data_set(short_addr_le, false, config->ack_ie.data, + config->ack_ie.data_len, NRF_802154_ACK_DATA_IE); + nrf_802154_ack_data_set(ext_addr_le, true, config->ack_ie.data, + config->ack_ie.data_len, NRF_802154_ACK_DATA_IE); + } else { + nrf_802154_ack_data_clear(short_addr_le, false, NRF_802154_ACK_DATA_IE); + nrf_802154_ack_data_clear(ext_addr_le, true, NRF_802154_ACK_DATA_IE); + } + } break; + +#if defined(CONFIG_IEEE802154_CSL_ENDPOINT) + case IEEE802154_CONFIG_CSL_RX_TIME: + nrf5_data.csl_rx_time = config->csl_rx_time; + break; + + case IEEE802154_CONFIG_RX_SLOT: + nrf5_schedule_rx(config->rx_slot.channel, config->rx_slot.start, + config->rx_slot.duration); + break; + + case IEEE802154_CONFIG_CSL_PERIOD: + nrf5_config_csl_period(config->csl_period); + break; +#endif /* CONFIG_IEEE802154_CSL_ENDPOINT */ + default: return -EINVAL; } @@ -807,7 +896,14 @@ void nrf_802154_received_timestamp_raw(uint8_t *data, int8_t power, uint8_t lqi, void nrf_802154_receive_failed(nrf_802154_rx_error_t error, uint32_t id) { +#if defined(CONFIG_IEEE802154_CSL_ENDPOINT) + if ((id == DRX_SLOT_PH) || (id == DRX_SLOT_RX)) { + nrf5_stop(net_if_get_device(nrf5_data.iface)); + return; + } +#else ARG_UNUSED(id); +#endif enum ieee802154_rx_fail_reason reason; @@ -949,6 +1045,7 @@ static struct ieee802154_radio_api nrf5_radio_api = { .tx = nrf5_tx, .ed_scan = nrf5_energy_scan_start, .get_time = nrf5_get_time, + .get_sch_acc = nrf5_get_acc, .configure = nrf5_configure, }; diff --git a/drivers/ieee802154/ieee802154_nrf5.h b/drivers/ieee802154/ieee802154_nrf5.h index d1a1b12148c..45f8d357416 100644 --- a/drivers/ieee802154/ieee802154_nrf5.h +++ b/drivers/ieee802154/ieee802154_nrf5.h @@ -83,6 +83,9 @@ struct nrf5_802154_data { /* Capabilities of the network interface. */ enum ieee802154_hw_caps capabilities; + + /* Next CSL receive time */ + uint32_t csl_rx_time; }; #endif /* ZEPHYR_DRIVERS_IEEE802154_IEEE802154_NRF5_H_ */ diff --git a/include/net/ieee802154_radio.h b/include/net/ieee802154_radio.h index 3e8997eeda7..d34e719c9f1 100644 --- a/include/net/ieee802154_radio.h +++ b/include/net/ieee802154_radio.h @@ -69,7 +69,8 @@ enum ieee802154_filter_type { enum ieee802154_event { IEEE802154_EVENT_TX_STARTED, /* Data transmission started */ - IEEE802154_EVENT_RX_FAILED /* Data reception failed */ + IEEE802154_EVENT_RX_FAILED, /* Data reception failed */ + IEEE802154_EVENT_SLEEP, /* Sleep pending */ }; enum ieee802154_rx_fail_reason { @@ -168,11 +169,45 @@ enum ieee802154_config_type { /** Sets the current MAC frame counter value for radios supporting transmit security. */ IEEE802154_CONFIG_FRAME_COUNTER, - /** Configure a radio reception slot */ + /** Configure a radio reception slot. This can be used for any scheduler reception, e.g.: + * Zigbee GP device, CSL, TSCH, etc. + * + * In order to configure a CSL receiver the upper layer should combine several + * configuration options in the following way: + * 1. Use ``IEEE802154_CONFIG_ENH_ACK_HEADER_IE`` once to inform the radio driver of the + * short and extended addresses of the peer to which it should inject CSL IEs. + * 2. Use ``IEEE802154_CONFIG_CSL_RX_TIME`` periodically, before each use of + * ``IEEE802154_CONFIG_CSL_PERIOD`` setting parameters of the nearest CSL RX window, and + * before each use of IEEE_CONFIG_RX_SLOT setting parameters of the following (not the + * nearest one) CSL RX window, to allow the radio driver to calculate the proper CSL Phase + * to the nearest CSL window to inject in the CSL IEs for both transmitted data and ack + * frames. + * 3. Use ``IEEE802154_CONFIG_CSL_PERIOD`` on each value change to update the current CSL + * period value which will be injected in the CSL IEs together with the CSL Phase based on + * ``IEEE802154_CONFIG_CSL_RX_TIME``. + * 4. Use ``IEEE802154_CONFIG_RX_SLOT`` periodically to schedule the immediate receive + * window earlier enough before the expected window start time, taking into account + * possible clock drifts and scheduling uncertainties. + * + * This diagram shows the usage of the four options over time: + * Start CSL Schedule CSL window + * + * ENH_ACK_HEADER_IE CSL_RX_TIME (following window) + * | | + * | CSL_RX_TIME (nearest window) | RX_SLOT (nearest window) + * | | | | + * | | CSL_PERIOD | | + * | | | | | + * v v v v v + * ----------------------------------------------------------[ CSL window ]-----+ + * ^ | + * | | + * +--------------------- loop ---------+ + */ IEEE802154_CONFIG_RX_SLOT, - /** Enable CSL receiver (Endpoint) */ - IEEE802154_CONFIG_CSL_RECEIVER, + /** Configure CSL receiver (Endpoint) period */ + IEEE802154_CONFIG_CSL_PERIOD, /** Configure the next CSL receive window center, in units of microseconds, * based on the radio time. @@ -235,11 +270,8 @@ struct ieee802154_config { uint32_t duration; } rx_slot; - /** ``IEEE802154_CONFIG_CSL_RECEIVER`` */ - struct { - uint32_t period; - const uint8_t *addr; - } csl_recv; + /** ``IEEE802154_CONFIG_CSL_PERIOD`` */ + uint32_t csl_period; /** ``IEEE802154_CONFIG_CSL_RX_TIME`` */ uint32_t csl_rx_time; @@ -315,11 +347,11 @@ struct ieee802154_radio_api { uint64_t (*get_time)(const struct device *dev); /** Get the current accuracy, in units of ± ppm, of the clock used for - * scheduling CSL transmissions or receive windows. + * scheduling delayed receive or transmit radio operations. * Note: Implementations may optimize this value based on operational * conditions (i.e.: temperature). */ - uint8_t (*get_csl_acc)(const struct device *dev); + uint8_t (*get_sch_acc)(const struct device *dev); }; /* Make sure that the network interface API is properly setup inside diff --git a/subsys/net/l2/openthread/Kconfig.thread b/subsys/net/l2/openthread/Kconfig.thread index 90d1712cccd..06db1e03f09 100644 --- a/subsys/net/l2/openthread/Kconfig.thread +++ b/subsys/net/l2/openthread/Kconfig.thread @@ -88,7 +88,13 @@ config OPENTHREAD_CSL_SAMPLE_WINDOW config OPENTHREAD_CSL_RECEIVE_TIME_AHEAD int "CSL receiver wake up margin in units of 10 symbols" - default 3 + default 480 + +config OPENTHREAD_CSL_MIN_RECEIVE_ON + int "Minimum CSL receive window" + default 5696 + help + The minimum CSL receive window (in microseconds) required to receive a full IEEE 802.15.4 frame config OPENTHREAD_MAC_SOFTWARE_TX_SECURITY_ENABLE bool "Enable software transmission security logic" diff --git a/subsys/net/lib/openthread/platform/alarm.c b/subsys/net/lib/openthread/platform/alarm.c index 6337e431792..036f39e8696 100644 --- a/subsys/net/lib/openthread/platform/alarm.c +++ b/subsys/net/lib/openthread/platform/alarm.c @@ -22,6 +22,7 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME); #include #include "platform-zephyr.h" +#include "openthread-core-zephyr-config.h" static bool timer_ms_fired, timer_us_fired; diff --git a/subsys/net/lib/openthread/platform/openthread-core-zephyr-config.h b/subsys/net/lib/openthread/platform/openthread-core-zephyr-config.h index bd149d32ba7..d3a130ad875 100644 --- a/subsys/net/lib/openthread/platform/openthread-core-zephyr-config.h +++ b/subsys/net/lib/openthread/platform/openthread-core-zephyr-config.h @@ -140,8 +140,8 @@ * in platform. * */ -#define OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE \ - (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && \ +#define OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE \ + (CONFIG_OPENTHREAD_CSL_RECEIVER && \ (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)) /* Zephyr does not use OpenThread's heap. mbedTLS will use heap memory allocated @@ -283,7 +283,7 @@ * * For some reasons, CSL receivers wake up a little later than expected. This * variable specifies how much time that CSL receiver would wake up earlier - * than the expected sample window. The time is in unit of 10 symbols. + * than the expected sample window. The time is in unit of microseconds. * */ #ifdef CONFIG_OPENTHREAD_CSL_RECEIVE_TIME_AHEAD @@ -291,6 +291,16 @@ CONFIG_OPENTHREAD_CSL_RECEIVE_TIME_AHEAD #endif /* CONFIG_OPENTHREAD_CSL_RECEIVE_TIME_AHEAD */ +/** + * @def OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON + * + * The minimum CSL receive window (in microseconds) required to receive an IEEE 802.15.4 frame. + * + */ +#ifdef CONFIG_OPENTHREAD_CSL_MIN_RECEIVE_ON +#define OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON CONFIG_OPENTHREAD_CSL_MIN_RECEIVE_ON +#endif /* CONFIG_OPENTHREAD_CSL_MIN_RECEIVE_ON */ + /** * @def OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE * diff --git a/subsys/net/lib/openthread/platform/radio.c b/subsys/net/lib/openthread/platform/radio.c index f330df90253..b78918ce7de 100644 --- a/subsys/net/lib/openthread/platform/radio.c +++ b/subsys/net/lib/openthread/platform/radio.c @@ -60,11 +60,10 @@ enum pending_events { PENDING_EVENT_RX_FAILED, /* The RX failed */ PENDING_EVENT_TX_STARTED, /* Radio has started transmitting */ PENDING_EVENT_TX_DONE, /* Radio transmission finished */ - PENDING_EVENT_DETECT_ENERGY, /* Requested to start Energy Detection - * procedure. - */ - PENDING_EVENT_DETECT_ENERGY_DONE, /* Energy Detection finished. */ - PENDING_EVENT_COUNT /* keep last */ + PENDING_EVENT_DETECT_ENERGY, /* Requested to start Energy Detection procedure */ + PENDING_EVENT_DETECT_ENERGY_DONE, /* Energy Detection finished */ + PENDING_EVENT_SLEEP, /* Sleep if idle */ + PENDING_EVENT_COUNT /* Keep last */ }; K_SEM_DEFINE(radio_sem, 0, 1); @@ -200,6 +199,10 @@ void handle_radio_event(const struct device *dev, enum ieee802154_event evt, } set_pending_event(PENDING_EVENT_RX_FAILED); } + break; + case IEEE802154_EVENT_SLEEP: + set_pending_event(PENDING_EVENT_SLEEP); + break; default: /* do nothing - ignore event */ break; @@ -476,6 +479,11 @@ void platformRadioProcess(otInstance *aInstance) } } + if (is_pending_event_set(PENDING_EVENT_SLEEP)) { + reset_pending_event(PENDING_EVENT_SLEEP); + ARG_UNUSED(otPlatRadioSleep(aInstance)); + } + /* handle events that can't run during transmission */ if (sState != OT_RADIO_STATE_TRANSMIT) { if (is_pending_event_set(PENDING_EVENT_DETECT_ENERGY)) { @@ -570,8 +578,9 @@ otError otPlatRadioSleep(otInstance *aInstance) sState == OT_RADIO_STATE_RECEIVE || sState == OT_RADIO_STATE_TRANSMIT) { error = OT_ERROR_NONE; - sState = OT_RADIO_STATE_SLEEP; - radio_api->stop(radio_dev); + if (radio_api->stop(radio_dev)) { + sState = OT_RADIO_STATE_SLEEP; + } } return error; @@ -1015,33 +1024,38 @@ void otPlatRadioSetMacFrameCounter(otInstance *aInstance, #endif #if defined(CONFIG_OPENTHREAD_CSL_RECEIVER) -otError otPlatRadioEnableCsl(otInstance *aInstance, uint32_t aCslPeriod, +otError otPlatRadioEnableCsl(otInstance *aInstance, uint32_t aCslPeriod, otShortAddress aShortAddr, const otExtAddress *aExtAddr) { int result; + uint8_t ie_header[OT_IE_HEADER_SIZE + OT_CSL_IE_SIZE]; + struct ieee802154_config config; ARG_UNUSED(aInstance); - struct ieee802154_config config = { - .csl_recv.period = aCslPeriod, - .csl_recv.addr = aExtAddr->m8, - }; + ie_header[0] = CSL_IE_HEADER_BYTES_LO; + ie_header[1] = CSL_IE_HEADER_BYTES_HI; + /* Leave CSL Phase empty intentionally */ + sys_put_le16(aCslPeriod, &ie_header[OT_IE_HEADER_SIZE + 2]); + config.ack_ie.data = ie_header; + config.ack_ie.data_len = OT_IE_HEADER_SIZE + OT_CSL_IE_SIZE; + config.ack_ie.short_addr = aShortAddr; + config.ack_ie.ext_addr = aExtAddr->m8; + result = radio_api->configure(radio_dev, IEEE802154_CONFIG_ENH_ACK_HEADER_IE, &config); - result = radio_api->configure(radio_dev, IEEE802154_CONFIG_CSL_RECEIVER, - &config); + config.csl_period = aCslPeriod; + result += radio_api->configure(radio_dev, IEEE802154_CONFIG_CSL_PERIOD, &config); return result ? OT_ERROR_FAILED : OT_ERROR_NONE; } -void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, - uint32_t aCslSampleTime) +void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime) { ARG_UNUSED(aInstance); struct ieee802154_config config = { .csl_rx_time = aCslSampleTime }; - (void)radio_api->configure(radio_dev, IEEE802154_CONFIG_CSL_RX_TIME, - &config); + (void)radio_api->configure(radio_dev, IEEE802154_CONFIG_CSL_RX_TIME, &config); } #endif /* CONFIG_OPENTHREAD_CSL_RECEIVER */ @@ -1049,7 +1063,7 @@ uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance) { ARG_UNUSED(aInstance); - return radio_api->get_csl_acc(radio_dev); + return radio_api->get_sch_acc(radio_dev); } #if defined(CONFIG_OPENTHREAD_LINK_METRICS)