diff --git a/drivers/ieee802154/Kconfig b/drivers/ieee802154/Kconfig index 916cc1a9ddb..629b492a579 100644 --- a/drivers/ieee802154/Kconfig +++ b/drivers/ieee802154/Kconfig @@ -92,6 +92,20 @@ config IEEE802154_CSL_DEBUG help Enable support for CSL debugging by avoiding sleep state in favor of receive state. +config IEEE802154_SELECTIVE_TXCHANNEL + bool "Support for selective TX channel setting" + help + Enable support for selectively setting TX channel for every timed transmission request. + The drivers MAY have the capability IEEE802154_HW_SELECTIVE_TXCHANNEL only if + this Kconfig option is enabled. If the Kconfig option is disabled the drivers + MUST NOT have the capability. + +config IEEE802154_CARRIER_FUNCTIONS + bool "Support for carrier functions" + default y if OPENTHREAD_DIAG + help + Enable support for functions such as modulated carrier and continuous carrier. + 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 613c913868b..a74606edf44 100644 --- a/drivers/ieee802154/ieee802154_nrf5.c +++ b/drivers/ieee802154/ieee802154_nrf5.c @@ -720,7 +720,7 @@ static int nrf5_stop(const struct device *dev) return 0; } -#if defined(CONFIG_NRF_802154_CARRIER_FUNCTIONS) +#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) static int nrf5_continuous_carrier(const struct device *dev) { ARG_UNUSED(dev); @@ -737,6 +737,23 @@ static int nrf5_continuous_carrier(const struct device *dev) return 0; } + +static int nrf_modulated_carrier(const struct device *dev, const uint8_t *data) +{ + ARG_UNUSED(dev); + + nrf_802154_tx_power_set(nrf5_data.txpwr); + + if (!nrf_802154_modulated_carrier(data)) { + LOG_ERR("Failed to enter modulated carrier state"); + return -EIO; + } + + LOG_DBG("Modulated carrier wave transmission started (channel: %d)", + nrf_802154_channel_get()); + + return 0; +} #endif #if !defined(CONFIG_IEEE802154_NRF5_EXT_IRQ_MGMT) @@ -1244,15 +1261,16 @@ static const struct ieee802154_radio_api nrf5_radio_api = { .set_txpower = nrf5_set_txpower, .start = nrf5_start, .stop = nrf5_stop, -#if defined(CONFIG_NRF_802154_CARRIER_FUNCTIONS) +#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) .continuous_carrier = nrf5_continuous_carrier, + .modulated_carrier = nrf_modulated_carrier, #endif .tx = nrf5_tx, .ed_scan = nrf5_energy_scan_start, .get_time = nrf5_get_time, .get_sch_acc = nrf5_get_acc, .configure = nrf5_configure, - .attr_get = nrf5_attr_get + .attr_get = nrf5_attr_get, }; #if defined(CONFIG_NET_L2_IEEE802154) diff --git a/include/zephyr/net/ieee802154_pkt.h b/include/zephyr/net/ieee802154_pkt.h index 3270f994248..b51db139c60 100644 --- a/include/zephyr/net/ieee802154_pkt.h +++ b/include/zephyr/net/ieee802154_pkt.h @@ -59,6 +59,16 @@ struct net_pkt_cb_ieee802154 { */ uint8_t rssi; }; + struct { +#if defined(CONFIG_IEEE802154_SELECTIVE_TXCHANNEL) + /* The channel used for timed transmissions. + * + * Please refer to `ieee802154_radio_api::tx` documentation for + * details. + */ + uint8_t txchannel; +#endif /* CONFIG_IEEE802154_SELECTIVE_TXCHANNEL */ + }; }; /* Flags */ @@ -179,6 +189,18 @@ static inline void net_pkt_set_ieee802154_rssi_dbm(struct net_pkt *pkt, int16_t CODE_UNREACHABLE; } +#if defined(CONFIG_IEEE802154_SELECTIVE_TXCHANNEL) +static inline uint8_t net_pkt_ieee802154_txchannel(struct net_pkt *pkt) +{ + return net_pkt_cb_ieee802154(pkt)->txchannel; +} + +static inline void net_pkt_set_ieee802154_txchannel(struct net_pkt *pkt, uint8_t channel) +{ + net_pkt_cb_ieee802154(pkt)->txchannel = channel; +} +#endif /* CONFIG_IEEE802154_SELECTIVE_TXCHANNEL */ + static inline bool net_pkt_ieee802154_ack_fpb(struct net_pkt *pkt) { return net_pkt_cb_ieee802154(pkt)->ack_fpb; diff --git a/include/zephyr/net/ieee802154_radio.h b/include/zephyr/net/ieee802154_radio.h index 10a1922f6fa..e2ab96ffac4 100644 --- a/include/zephyr/net/ieee802154_radio.h +++ b/include/zephyr/net/ieee802154_radio.h @@ -520,13 +520,26 @@ enum ieee802154_hw_caps { /** RxOnWhenIdle handling supported */ IEEE802154_RX_ON_WHEN_IDLE = BIT(12), + /** Support for timed transmissions on selective channel. + * + * This capability informs that transmissions with modes + * @ref IEEE802154_TX_MODE_TXTIME and @ref IEEE802154_TX_MODE_TXTIME_CCA support + * scheduling of timed transmissions on selective tx channel. + * The driver MAY have this capability only if the Kconfig option + * `CONFIG_IEEE802154_SELECTIVE_TXCHANNEL` is set, otherwise the driver MUST + * NOT have this capability. + * + * Please refer to the `ieee802154_radio_api::tx` documentation for details. + */ + IEEE802154_HW_SELECTIVE_TXCHANNEL = BIT(13), + /* Note: Update also IEEE802154_HW_CAPS_BITS_COMMON_COUNT when changing * the ieee802154_hw_caps type. */ }; /** @brief Number of bits used by ieee802154_hw_caps type. */ -#define IEEE802154_HW_CAPS_BITS_COMMON_COUNT (13) +#define IEEE802154_HW_CAPS_BITS_COMMON_COUNT (14) /** @brief This and higher values are specific to the protocol- or driver-specific extensions. */ #define IEEE802154_HW_CAPS_BITS_PRIV_START IEEE802154_HW_CAPS_BITS_COMMON_COUNT @@ -625,6 +638,8 @@ enum ieee802154_tx_mode { * Transmit packet in the future, at the specified time, no CCA. * * @note requires IEEE802154_HW_TXTIME capability. + * + * @note capability IEEE802154_HW_SELECTIVE_TXCHANNEL may apply. */ IEEE802154_TX_MODE_TXTIME, @@ -635,6 +650,8 @@ enum ieee802154_tx_mode { * * @note Required for Thread 1.2 Coordinated Sampled Listening feature * (see Thread specification 1.2.0, ch. 3.2.6.3). + * + * @note capability IEEE802154_HW_SELECTIVE_TXCHANNEL may apply. */ IEEE802154_TX_MODE_TXTIME_CCA, @@ -1657,6 +1674,23 @@ struct ieee802154_radio_api { * considerable idle waiting time. SHALL return `-ENETDOWN` unless the * interface is "UP". * + * @note The transmission occurs on the radio channel set by the call to + * `set_channel()`. However, if the `CONFIG_IEEE802154_SELECTIVE_TXCHANNEL` + * is set and the driver has the capability `IEEE802154_HW_SELECTIVE_TXCHANNEL` + * then the transmissions requested with `mode` IEEE802154_TX_MODE_TXTIME + * or `IEEE802154_TX_MODE_TXTIME_CCA` SHALL use the radio channel + * returned by `net_pkt_ieee802154_txchannel()` to transmit the packet + * and receive an ACK on that channel if the frame requested it. After + * the operation the driver should return to the channel set previously by + * `set_channel()` call. + * It is responsibility of an upper layer to set the required radio channel + * for the packet by a call to `net_pkt_set_ieee802154_txchannel()`. + * This feature allows CSL transmissions as stated in IEEE 802.15.4-2020 + * chapter 6.12.2.7 CSL over multiple channels. This feature allows to perform + * a switch of the radio channel as late as possible before transmission without + * interrupting possible reception that could occur if separate `set_channel()` + * was called. + * * @param dev pointer to IEEE 802.15.4 driver device * @param mode the transmission mode, some of which require specific * offloading capabilities. @@ -1731,6 +1765,7 @@ struct ieee802154_radio_api { */ int (*stop)(const struct device *dev); +#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) /** * @brief Start continuous carrier wave transmission. * @@ -1752,6 +1787,30 @@ struct ieee802154_radio_api { */ int (*continuous_carrier)(const struct device *dev); + /** + * @brief Start modulated carrier wave transmission. + * + * @details When the radio is emitting modulated carrier signals, it + * blocks all transmissions on the selected channel. + * This function is to be called only during radio + * tests. Do not use it during normal device operation. + * + * @note Implementations MAY **sleep** and will usually NOT be + * **isr-ok**. MAY be called in any interface state once the driver is + * fully initialized ("ready"). + * + * @param dev pointer to IEEE 802.15.4 driver device + * @param data Pointer to a buffer to modulate the carrier with. + * The first byte is the data length. + * + * @retval 0 modulated carrier wave transmission started + * @retval -EALREADY The driver was already in "TESTING" state and + * emitting a modulated carrier. + * @retval -EIO not started + */ + int (*modulated_carrier)(const struct device *dev, const uint8_t *data); +#endif /* CONFIG_IEEE802154_CARRIER_FUNCTIONS */ + /** * @brief Set or update driver configuration. * diff --git a/include/zephyr/net/ieee802154_radio_openthread.h b/include/zephyr/net/ieee802154_radio_openthread.h index e45bbc6b85b..3606c7a2b78 100644 --- a/include/zephyr/net/ieee802154_radio_openthread.h +++ b/include/zephyr/net/ieee802154_radio_openthread.h @@ -64,6 +64,9 @@ enum ieee802154_openthread_tx_mode { * section 11.3, table 11-2). * * Requires IEEE802154_OPENTHREAD_HW_MULTIPLE_CCA capability. + * + * @note Capability @ref IEEE802154_HW_SELECTIVE_TXCHANNEL applies as for + * @ref IEEE802154_TX_MODE_TXTIME_CCA. */ IEEE802154_OPENTHREAD_TX_MODE_TXTIME_MULTIPLE_CCA = IEEE802154_TX_MODE_PRIV_START }; diff --git a/modules/hal_nordic/Kconfig b/modules/hal_nordic/Kconfig index dd4e0a0859b..f29b77e120e 100644 --- a/modules/hal_nordic/Kconfig +++ b/modules/hal_nordic/Kconfig @@ -204,7 +204,7 @@ config NRF_802154_SECURITY_KEY_STORAGE_SIZE config NRF_802154_CARRIER_FUNCTIONS bool "nRF 802.15.4 carrier functions" - default y if OPENTHREAD_DIAG + default y if (OPENTHREAD_DIAG || IEEE802154_CARRIER_FUNCTIONS) help This option enables functions such as modulated carrier and continuous carrier. If this option is modified on a multicore SoC, its remote counterpart must be set to the exact same value. diff --git a/modules/openthread/platform/diag.c b/modules/openthread/platform/diag.c index a55368bb10f..23ea3b97986 100644 --- a/modules/openthread/platform/diag.c +++ b/modules/openthread/platform/diag.c @@ -7,9 +7,11 @@ #include #include +#include #include #include "platform-zephyr.h" +#include "zephyr/sys/util.h" /** * Diagnostics mode variables. @@ -19,6 +21,8 @@ static bool sDiagMode; static void *sDiagCallbackContext; static otPlatDiagOutputCallback sDiagOutputCallback; +static otError startModCarrier(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]); + static void diag_output(const char *aFormat, ...) { va_list args; @@ -47,6 +51,12 @@ otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArg ARG_UNUSED(aInstance); ARG_UNUSED(aArgsLength); +#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) + if (strcmp(aArgs[0], "modcarrier") == 0) { + return startModCarrier(aInstance, aArgsLength - 1, aArgs + 1); + } +#endif + /* Add more platform specific diagnostics features here. */ diag_output("diag feature '%s' is not supported\r\n", aArgs[0]); @@ -86,6 +96,7 @@ void otPlatDiagRadioReceived(otInstance *aInstance, ARG_UNUSED(aError); } +#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable) { if (!otPlatDiagModeGet()) { @@ -94,6 +105,7 @@ otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable) return platformRadioTransmitCarrier(aInstance, aEnable); } +#endif /* CONFIG_IEEE802154_CARRIER_FUNCTIONS */ void otPlatDiagAlarmCallback(otInstance *aInstance) { @@ -274,3 +286,30 @@ otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode) #endif /* DT_HAS_COMPAT_STATUS_OKAY(openthread_config) && \ * DT_NODE_HAS_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(openthread_config), diag_gpios) */ + +#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) + +static otError startModCarrier(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]) +{ + ARG_UNUSED(aInstance); + ARG_UNUSED(aArgsLength); + + bool enable = true; + uint8_t data[OT_RADIO_FRAME_MAX_SIZE + 1]; + + if (aArgsLength <= 0) { + return OT_ERROR_INVALID_ARGS; + } + + if (strcmp(aArgs[0], "stop") == 0) { + enable = false; + } else { + if (hex2bin(aArgs[0], strlen(aArgs[0]), data, ARRAY_SIZE(data)) == 0) { + return OT_ERROR_INVALID_ARGS; + } + } + + return platformRadioTransmitModulatedCarrier(aInstance, enable, data); +} + +#endif diff --git a/modules/openthread/platform/platform-zephyr.h b/modules/openthread/platform/platform-zephyr.h index 8e2d91bd5ef..ca25fedf2b1 100644 --- a/modules/openthread/platform/platform-zephyr.h +++ b/modules/openthread/platform/platform-zephyr.h @@ -70,10 +70,20 @@ void platformUartPanic(void); */ uint16_t platformRadioChannelGet(otInstance *aInstance); +#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) /** * Start/stop continuous carrier wave transmission. */ otError platformRadioTransmitCarrier(otInstance *aInstance, bool aEnable); +#endif /* CONFIG_IEEE802154_CARRIER_FUNCTIONS */ + +#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) +/** + * Start/stop modulated carrier wave transmission. + */ +otError platformRadioTransmitModulatedCarrier(otInstance *aInstance, bool aEnable, + const uint8_t *aData); +#endif /** * This function initializes the random number service used by OpenThread. @@ -81,7 +91,6 @@ otError platformRadioTransmitCarrier(otInstance *aInstance, bool aEnable); */ void platformRandomInit(void); - /** * Initialize platform Shell driver. */ diff --git a/modules/openthread/platform/radio.c b/modules/openthread/platform/radio.c index 7e2943e734f..73f6f4dc9cc 100644 --- a/modules/openthread/platform/radio.c +++ b/modules/openthread/platform/radio.c @@ -11,6 +11,7 @@ * */ +#include #define LOG_MODULE_NAME net_otPlat_radio #include @@ -809,6 +810,7 @@ otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, } #endif +#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) otError platformRadioTransmitCarrier(otInstance *aInstance, bool aEnable) { if (radio_api->continuous_carrier == NULL) { @@ -832,6 +834,35 @@ otError platformRadioTransmitCarrier(otInstance *aInstance, bool aEnable) return OT_ERROR_NONE; } +otError platformRadioTransmitModulatedCarrier(otInstance *aInstance, bool aEnable, + const uint8_t *aData) +{ + if (radio_api->modulated_carrier == NULL) { + return OT_ERROR_NOT_IMPLEMENTED; + } + + if (aEnable && sState == OT_RADIO_STATE_RECEIVE) { + if (aData == NULL) { + return OT_ERROR_INVALID_ARGS; + } + + radio_api->set_txpower(radio_dev, get_transmit_power_for_channel(channel)); + + if (radio_api->modulated_carrier(radio_dev, aData) != 0) { + return OT_ERROR_FAILED; + } + sState = OT_RADIO_STATE_TRANSMIT; + } else if ((!aEnable) && sState == OT_RADIO_STATE_TRANSMIT) { + return otPlatRadioReceive(aInstance, channel); + } else { + return OT_ERROR_INVALID_STATE; + } + + return OT_ERROR_NONE; +} + +#endif /* CONFIG_IEEE802154_CARRIER_FUNCTIONS */ + otRadioState otPlatRadioGetState(otInstance *aInstance) { ARG_UNUSED(aInstance);