/*
 * Copyright (c) 2019 - 2022, Nordic Semiconductor ASA
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

#define NRF_802154_MODULE_ID NRF_802154_DRV_MODULE_ID_TRX

#include "nrf_802154_trx.h"

#include <assert.h>
#include <string.h>

#include "nrf_802154_config.h"
#include "nrf_802154_const.h"
#include "nrf_802154_peripherals.h"
#include "nrf_802154_pib.h"
#include "nrf_802154_rssi.h"
#include "nrf_802154_swi.h"
#include "nrf_802154_trx_ppi_api.h"
#include "nrf_802154_utils.h"

#include "hal/nrf_egu.h"
#include "hal/nrf_radio.h"
#include "hal/nrf_timer.h"

#include "nrf_802154_procedures_duration.h"
#include "nrf_802154_critical_section.h"
#include "mpsl_fem_config_common.h"
#include "platform/nrf_802154_irq.h"
#include "protocol/mpsl_fem_protocol_api.h"

#include "nrf_802154_sl_ant_div.h"

#define EGU_SYNC_EVENT   NRF_EGU_EVENT_TRIGGERED3
#define EGU_SYNC_TASK    NRF_EGU_TASK_TRIGGER3
#define EGU_SYNC_INTMASK NRF_EGU_INT_TRIGGERED3

#if defined(NRF52840_XXAA) || \
    defined(NRF52833_XXAA)
#define PPI_CCAIDLE_FEM  NRF_802154_PPI_RADIO_CCAIDLE_TO_FEM_GPIOTE ///< PPI that connects RADIO CCAIDLE event with GPIOTE tasks used by FEM
#define PPI_CHGRP_ABORT  NRF_802154_PPI_ABORT_GROUP                 ///< PPI group used to disable PPIs when async event aborting radio operation is propagated through the system
#define RADIO_BASE       NRF_RADIO_BASE
#elif defined(NRF5340_XXAA)
#define PPI_CCAIDLE_FEM  0
#define RADIO_BASE       NRF_RADIO_NS_BASE
#define FICR_BASE        NRF_FICR_NS_BASE
#endif

#if NRF_802154_DISABLE_BCC_MATCHING
#define SHORT_ADDRESS_BCSTART 0UL
#else // NRF_802154_DISABLE_BCC_MATCHING
#define SHORT_ADDRESS_BCSTART NRF_RADIO_SHORT_ADDRESS_BCSTART_MASK
#endif  // NRF_802154_DISABLE_BCC_MATCHING

/// Value set to SHORTS register when no shorts should be enabled.
#define SHORTS_IDLE           0

/// Value set to SHORTS register for RX operation.
#define SHORTS_RX             (NRF_RADIO_SHORT_ADDRESS_RSSISTART_MASK | \
                               NRF_RADIO_SHORT_END_DISABLE_MASK |       \
                               SHORT_ADDRESS_BCSTART)

#define SHORTS_RX_FREE_BUFFER (NRF_RADIO_SHORT_RXREADY_START_MASK)

#define SHORTS_TX_ACK         (NRF_RADIO_SHORT_TXREADY_START_MASK | \
                               NRF_RADIO_SHORT_PHYEND_DISABLE_MASK)

#define SHORTS_CCA_TX         (NRF_RADIO_SHORT_RXREADY_CCASTART_MASK | \
                               NRF_RADIO_SHORT_CCABUSY_DISABLE_MASK |  \
                               NRF_RADIO_SHORT_CCAIDLE_TXEN_MASK |     \
                               NRF_RADIO_SHORT_TXREADY_START_MASK |    \
                               NRF_RADIO_SHORT_PHYEND_DISABLE_MASK)

#define SHORTS_TX             (NRF_RADIO_SHORT_TXREADY_START_MASK | \
                               NRF_RADIO_SHORT_PHYEND_DISABLE_MASK)

#define SHORTS_RX_ACK         (NRF_RADIO_SHORT_ADDRESS_RSSISTART_MASK | \
                               NRF_RADIO_SHORT_END_DISABLE_MASK)

#define SHORTS_MOD_CARRIER    (NRF_RADIO_SHORT_TXREADY_START_MASK | \
                               NRF_RADIO_SHORT_PHYEND_START_MASK)

#define SHORTS_ED             (NRF_RADIO_SHORT_READY_EDSTART_MASK)

#define SHORTS_CCA            (NRF_RADIO_SHORT_RXREADY_CCASTART_MASK | \
                               NRF_RADIO_SHORT_CCABUSY_DISABLE_MASK)

#define CRC_LENGTH            2          ///< Length of CRC in 802.15.4 frames [bytes]
#define CRC_POLYNOMIAL        0x011021   ///< Polynomial used for CRC calculation in 802.15.4 frames

#define TXRU_TIME             40         ///< Transmitter ramp up time [us]
#define EVENT_LAT             23         ///< END event latency [us]
#define MAX_RXRAMPDOWN_CYCLES 32         ///< Maximum number of cycles that RX ramp-down might take

#define RSSI_SETTLE_TIME_US   15         ///< Time required for RSSI measurements to become valid after signal level change.

#if NRF_802154_INTERNAL_RADIO_IRQ_HANDLING
void nrf_802154_radio_irq_handler(void); ///< Prototype required by internal RADIO IRQ handler
#endif  // NRF_802154_INTERNAL_RADIO_IRQ_HANDLING

/// Common parameters for the FEM handling.
static const mpsl_fem_event_t m_activate_rx_cc0 =
{
    .type        = MPSL_FEM_EVENT_TYPE_TIMER,
    .event.timer =
    {
        .p_timer_instance     = NRF_802154_TIMER_INSTANCE,
        .compare_channel_mask = ((1 << NRF_TIMER_CC_CHANNEL0) | (1 << NRF_TIMER_CC_CHANNEL2)),
        .counter_period       = {
            .end = RX_RAMP_UP_TIME
        },
    },
};

static const mpsl_fem_event_t m_activate_tx_cc0 =
{
    .type        = MPSL_FEM_EVENT_TYPE_TIMER,
    .event.timer =
    {
        .p_timer_instance     = NRF_802154_TIMER_INSTANCE,
        .compare_channel_mask = ((1 << NRF_TIMER_CC_CHANNEL0) | (1 << NRF_TIMER_CC_CHANNEL2)),
        .counter_period       = {
            .end = TX_RAMP_UP_TIME
        },
    },
};

static const mpsl_fem_event_t m_ccaidle =
{
    .type = MPSL_FEM_EVENT_TYPE_GENERIC,
#if defined(NRF52_SERIES)
    .override_ppi        = true,
    .ppi_ch_id           = PPI_CCAIDLE_FEM,
    .event.generic.event = ((uint32_t)RADIO_BASE + (uint32_t)NRF_RADIO_EVENT_CCAIDLE)
#elif defined(NRF53_SERIES)
    .event.generic.event = NRF_802154_DPPI_RADIO_CCAIDLE
#endif
};

/**@brief Fal event used by @ref nrf_802154_trx_transmit_ack and @ref txack_finish */
static mpsl_fem_event_t m_activate_tx_cc0_timeshifted;

static volatile trx_state_t m_trx_state;

typedef struct
{
#if !NRF_802154_DISABLE_BCC_MATCHING
    bool psdu_being_received; ///< If PSDU is currently being received.

#endif

    bool missing_receive_buffer; ///!< If trx entered receive state without receive buffer

#if NRF_802154_TX_STARTED_NOTIFY_ENABLED
    bool tx_started;             ///< If the requested transmission has started.

#endif  // NRF_802154_TX_STARTED_NOTIFY_ENABLED

    bool          rssi_started;

    volatile bool rssi_settled;
} nrf_802154_flags_t;

static nrf_802154_flags_t m_flags; ///< Flags used to store the current driver state.

/** @brief Value of TIMER internal counter from which the counting is resumed on RADIO.EVENTS_END event. */
static volatile uint32_t m_timer_value_on_radio_end_event;
static volatile bool     m_transmit_with_cca;
static int8_t            m_fem_gain_in_disabled;

static void rxframe_finish_disable_ppis(void);
static void rxack_finish_disable_ppis(void);
static void txframe_finish_disable_ppis(bool cca);

static void go_idle_abort(void);
static void receive_frame_abort(void);
static void receive_ack_abort(void);
static void transmit_frame_abort(void);
static void transmit_ack_abort(void);
static void standalone_cca_abort(void);

#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
static void continuous_carrier_abort(void);
static void modulated_carrier_abort(void);

#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED
static void energy_detection_abort(void);

/** Clear flags describing frame being received. */
void rx_flags_clear(void)
{
    m_flags.missing_receive_buffer = false;
#if !NRF_802154_DISABLE_BCC_MATCHING
    m_flags.psdu_being_received = false;
#endif // !NRF_802154_DISABLE_BCC_MATCHING
}

static void * volatile mp_receive_buffer;

/** Initialize TIMER peripheral used by the driver. */
void nrf_timer_init(void)
{
    nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
    nrf_timer_mode_set(NRF_802154_TIMER_INSTANCE, NRF_TIMER_MODE_TIMER);
    nrf_timer_bit_width_set(NRF_802154_TIMER_INSTANCE, NRF_TIMER_BIT_WIDTH_32);
    nrf_timer_frequency_set(NRF_802154_TIMER_INSTANCE, NRF_TIMER_FREQ_1MHz);

#if NRF_802154_DISABLE_BCC_MATCHING
    // Setup timer for detecting PSDU reception.
    nrf_timer_mode_set(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_MODE_COUNTER);
    nrf_timer_bit_width_set(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_BIT_WIDTH_8);
#endif
}

/** Reset radio peripheral. */
static void nrf_radio_reset(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    nrf_radio_power_set(NRF_RADIO, false);
    nrf_radio_power_set(NRF_RADIO, true);

    nrf_802154_log_global_event(NRF_802154_LOG_VERBOSITY_LOW,
                                NRF_802154_LOG_GLOBAL_EVENT_ID_RADIO_RESET,
                                0U);

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

static void channel_set(uint8_t channel)
{
    assert(channel >= 11U && channel <= 26U);

    nrf_radio_frequency_set(NRF_RADIO, 2405U + 5U * (channel - 11U));
}

static void cca_configuration_update(void)
{
    nrf_802154_cca_cfg_t cca_cfg;

    nrf_802154_pib_cca_cfg_get(&cca_cfg);
    nrf_radio_cca_configure(NRF_RADIO,
                            cca_cfg.mode,
                            nrf_802154_rssi_cca_ed_threshold_corrected_get(cca_cfg.ed_threshold),
                            cca_cfg.corr_threshold,
                            cca_cfg.corr_limit);
}

/** Initialize interrupts for radio peripheral. */
static void irq_init(void)
{
#if NRF_802154_INTERNAL_RADIO_IRQ_HANDLING
    nrf_802154_irq_init(RADIO_IRQn, NRF_802154_IRQ_PRIORITY, nrf_802154_radio_irq_handler);
#endif
    nrf_802154_irq_enable(RADIO_IRQn);
}

static void trigger_disable_to_start_rampup(void)
{
    if (!nrf_802154_trx_ppi_for_ramp_up_was_triggered())
    {
        nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);
    }
}

/** Configure FEM to set LNA at appropriate time. */
static void fem_for_lna_set(void)
{
    if (mpsl_fem_lna_configuration_set(&m_activate_rx_cc0, NULL) == 0)
    {
        nrf_timer_shorts_enable(m_activate_rx_cc0.event.timer.p_timer_instance,
                                NRF_TIMER_SHORT_COMPARE0_STOP_MASK);

        nrf_802154_trx_ppi_for_fem_set();
    }
}

/** Reset FEM configuration for LNA. */
static void fem_for_lna_reset(void)
{
    mpsl_fem_lna_configuration_clear();
    nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
    nrf_timer_shorts_disable(NRF_802154_TIMER_INSTANCE, NRF_TIMER_SHORT_COMPARE0_STOP_MASK);
    nrf_802154_trx_ppi_for_fem_clear();
    /* There is no need to explicitly deactivate LNA pin during reset as mpsl_fem_abort_set is used
     * to provide a deactivation mechanism on DISABLED event.
     */
}

/** Configure FEM to set PA at appropriate time.
 *
 * @note This function must be called before ramp up PPIs are configured.
 */
static void fem_for_pa_set(int8_t gain)
{
    (void)mpsl_fem_pa_gain_set(gain);
    if (mpsl_fem_pa_configuration_set(&m_activate_tx_cc0, NULL) == 0)
    {
        nrf_timer_shorts_enable(m_activate_tx_cc0.event.timer.p_timer_instance,
                                NRF_TIMER_SHORT_COMPARE0_STOP_MASK);
        nrf_802154_trx_ppi_for_fem_set();
    }
}

/** Reset FEM configuration for PA.
 *
 * @note This function must be called before ramp up PPIs are configured.
 */
static void fem_for_pa_reset(void)
{
    mpsl_fem_pa_configuration_clear();
    nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
    nrf_802154_trx_ppi_for_fem_clear();
    mpsl_fem_deactivate_now(MPSL_FEM_PA);
}

/** Configure FEM for TX procedure.
 *
 * @note This function must be called before ramp up PPIs are configured.
 */
static void fem_for_tx_set(bool cca, int8_t gain)
{
    bool success;

    (void)mpsl_fem_pa_gain_set(gain);

    if (cca)
    {
        bool pa_set  = false;
        bool lna_set = false;

        if (mpsl_fem_lna_configuration_set(&m_activate_rx_cc0, &m_ccaidle) == 0)
        {
            lna_set = true;
        }

        if (mpsl_fem_pa_configuration_set(&m_ccaidle, NULL) == 0)
        {
            pa_set = true;
        }

        success = pa_set || lna_set;

    }
    else
    {
        success = (mpsl_fem_pa_configuration_set(&m_activate_tx_cc0, NULL) == 0);
    }

    if (success)
    {
        nrf_timer_shorts_enable(NRF_802154_TIMER_INSTANCE, NRF_TIMER_SHORT_COMPARE0_STOP_MASK);
        nrf_802154_trx_ppi_for_fem_set();
    }
}

static void fem_for_tx_reset(bool cca)
{
    nrf_timer_shorts_disable(NRF_802154_TIMER_INSTANCE, NRF_TIMER_SHORT_COMPARE0_STOP_MASK);

    if (cca)
    {
        mpsl_fem_lna_configuration_clear();
        mpsl_fem_pa_configuration_clear();
    }
    else
    {
        mpsl_fem_pa_configuration_clear();
    }

    nrf_802154_trx_ppi_for_fem_clear();
    nrf_802154_trx_ppi_for_ramp_up_propagation_delay_wait();
    nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
}

#if defined(NRF52840_XXAA) || \
    defined(NRF52833_XXAA)
/** @brief Applies DEVICE-CONFIG-254.
 *
 * Shall be called after every RADIO peripheral reset.
 */
static void device_config_254_apply_tx(void)
{
    uint32_t ficr_reg1 = *(volatile uint32_t *)0x10000330UL;
    uint32_t ficr_reg2 = *(volatile uint32_t *)0x10000334UL;
    uint32_t ficr_reg3 = *(volatile uint32_t *)0x10000338UL;

    /* Check if the device is fixed by testing every FICR register's value separately. */
    if (ficr_reg1 != 0xffffffffUL)
    {
        volatile uint32_t * p_radio_reg1 = (volatile uint32_t *)0x4000174cUL;

        *p_radio_reg1 = ficr_reg1;
    }

    if (ficr_reg2 != 0xffffffffUL)
    {
        volatile uint32_t * p_radio_reg2 = (volatile uint32_t *)0x40001584UL;

        *p_radio_reg2 = ficr_reg2;
    }

    if (ficr_reg3 != 0xffffffffUL)
    {
        volatile uint32_t * p_radio_reg3 = (volatile uint32_t *)0x40001588UL;

        *p_radio_reg3 = ficr_reg3;
    }
}

#endif

/** @brief Applies ERRATA-117
 *
 * Shall be called after setting RADIO mode to NRF_RADIO_MODE_IEEE802154_250KBIT.
 */
#if defined(NRF5340_XXAA)
static void errata_117_apply(void)
{
    /* Register at 0x01FF0084. */
    uint32_t ficr_reg = *(volatile uint32_t *)(FICR_BASE + 0x84UL);
    /* Register at 0x41008588. */
    volatile uint32_t * p_radio_reg = (volatile uint32_t *)(RADIO_BASE + 0x588UL);

    *p_radio_reg = ficr_reg;
}

#endif

void nrf_802154_trx_module_reset(void)
{
    m_trx_state                      = TRX_STATE_DISABLED;
    m_timer_value_on_radio_end_event = 0;
    m_transmit_with_cca              = false;
    m_fem_gain_in_disabled           = 0;
    mp_receive_buffer                = NULL;

    memset(&m_flags, 0, sizeof(m_flags));
}

void nrf_802154_trx_init(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    nrf_802154_trx_module_reset();

#if defined(RADIO_INTENSET_SYNC_Msk)
    nrf_802154_swi_init();
#endif

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

void nrf_802154_trx_enable(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    assert(m_trx_state == TRX_STATE_DISABLED);

    nrf_timer_init();
    nrf_radio_reset();

#if defined(NRF52840_XXAA) || \
    defined(NRF52833_XXAA)
    // Apply DEVICE-CONFIG-254 if needed.
    if (mpsl_fem_device_config_254_apply_get())
    {
        device_config_254_apply_tx();
    }
#endif

    nrf_radio_packet_conf_t packet_conf;

    nrf_radio_mode_set(NRF_RADIO, NRF_RADIO_MODE_IEEE802154_250KBIT);

#if defined(NRF5340_XXAA)
    // Apply ERRATA-117 after setting RADIO mode to NRF_RADIO_MODE_IEEE802154_250KBIT.
    errata_117_apply();
#endif

    memset(&packet_conf, 0, sizeof(packet_conf));
    packet_conf.lflen  = 8;
    packet_conf.plen   = NRF_RADIO_PREAMBLE_LENGTH_32BIT_ZERO;
    packet_conf.crcinc = true;
    packet_conf.maxlen = MAX_PACKET_SIZE;
    nrf_radio_packet_configure(NRF_RADIO, &packet_conf);

    nrf_radio_modecnf0_set(NRF_RADIO, true, 0);

    // Configure CRC
    nrf_radio_crc_configure(NRF_RADIO, CRC_LENGTH, NRF_RADIO_CRC_ADDR_IEEE802154, CRC_POLYNOMIAL);

    nrf_802154_trx_ppi_for_enable();

    // Configure CCA
    cca_configuration_update();

    // Set channel
    channel_set(nrf_802154_pib_channel_get());

    // Custom initialization operations
    nrf_802154_custom_part_of_radio_init();

    irq_init();

    assert(nrf_radio_shorts_get(NRF_RADIO) == SHORTS_IDLE);

    mpsl_fem_pa_is_configured(&m_fem_gain_in_disabled);

#if defined(NRF52840_XXAA) || \
    defined(NRF52833_XXAA)
    mpsl_fem_abort_set(nrf_radio_event_address_get(NRF_RADIO, NRF_RADIO_EVENT_DISABLED),
                       PPI_CHGRP_ABORT);
#elif defined(NRF53_SERIES)
    mpsl_fem_abort_set(NRF_802154_DPPI_RADIO_DISABLED,
                       0U); /* The group parameter is ignored by FEM for nRF53 */
#endif

    mpsl_fem_deactivate_now(MPSL_FEM_ALL);

    m_trx_state = TRX_STATE_IDLE;

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

static void ppi_all_clear(void)
{
    switch (m_trx_state)
    {
        case TRX_STATE_IDLE:
        case TRX_STATE_GOING_IDLE:
        case TRX_STATE_RXFRAME_FINISHED:
        case TRX_STATE_FINISHED:
            // Intentionally empty. PPIs are not configured in this state.
            break;

        case TRX_STATE_RXFRAME:
            rxframe_finish_disable_ppis();
            break;

        case TRX_STATE_RXACK:
            rxack_finish_disable_ppis();
            nrf_802154_trx_ppi_for_fem_clear();
            break;

        case TRX_STATE_TXFRAME:
            txframe_finish_disable_ppis(m_transmit_with_cca);
            nrf_802154_trx_ppi_for_fem_clear();
            break;

        case TRX_STATE_TXACK:
            nrf_802154_trx_ppi_for_ack_tx_clear();
            // FEM PPIs are not configured for this state. TIMER was started in TRX_STATE_RXFRAME
            // and PPIs starting timer were cleared when exiting TRX_STATE_RXFRAME.
            break;

        case TRX_STATE_STANDALONE_CCA:
            nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_RXEN, false);
            nrf_802154_trx_ppi_for_fem_clear();
            break;

#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
        case TRX_STATE_CONTINUOUS_CARRIER:
            nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_TXEN, false);
            nrf_802154_trx_ppi_for_fem_clear();
            break;

        case TRX_STATE_MODULATED_CARRIER:
            nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_TXEN, false);
            nrf_802154_trx_ppi_for_fem_clear();
            break;
#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED

        case TRX_STATE_ENERGY_DETECTION:
            nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_RXEN, false);
            nrf_802154_trx_ppi_for_fem_clear();
            break;

        default:
            assert(false);
    }
    nrf_802154_trx_ppi_for_disable();
}

static void fem_power_down_now(void)
{
    mpsl_fem_deactivate_now(MPSL_FEM_ALL);
    mpsl_fem_disable();
}

void nrf_802154_trx_disable(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    if (m_trx_state != TRX_STATE_DISABLED)
    {
        nrf_radio_power_set(NRF_RADIO, false);
        nrf_802154_irq_clear_pending(RADIO_IRQn);

        /* While the RADIO is powered off deconfigure any PPIs used directly by trx module */
        ppi_all_clear();

#if !NRF_802154_DISABLE_BCC_MATCHING && defined(RADIO_INTENSET_SYNC_Msk)
        nrf_egu_int_disable(NRF_802154_EGU_INSTANCE, EGU_SYNC_INTMASK);
        nrf_egu_event_clear(NRF_802154_EGU_INSTANCE, EGU_SYNC_EVENT);
#endif

        /* Stop & deconfigure timer */
        nrf_timer_shorts_disable(NRF_802154_TIMER_INSTANCE,
                                 NRF_TIMER_SHORT_COMPARE0_STOP_MASK |
                                 NRF_TIMER_SHORT_COMPARE1_STOP_MASK);
        nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);

        nrf_radio_power_set(NRF_RADIO, true);

        mpsl_fem_lna_configuration_clear();
        mpsl_fem_pa_configuration_clear();
        mpsl_fem_abort_clear();

        /* Restore gain of the FEM to the state latched in nrf_802154_trx_enable */
        (void)mpsl_fem_pa_gain_set(m_fem_gain_in_disabled);

        if (m_trx_state != TRX_STATE_IDLE)
        {
            fem_power_down_now();
        }

#if NRF_802154_DISABLE_BCC_MATCHING
        // Anomaly 78: use SHUTDOWN instead of STOP and CLEAR.
        nrf_timer_task_trigger(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
        nrf_timer_shorts_disable(NRF_802154_COUNTER_TIMER_INSTANCE,
                                 NRF_TIMER_SHORT_COMPARE1_STOP_MASK);
#else
        m_flags.psdu_being_received = false;
#endif
        m_flags.missing_receive_buffer = false;
        m_flags.rssi_started           = false;
#if NRF_802154_TX_STARTED_NOTIFY_ENABLED
        m_flags.tx_started = false;
#endif

        m_trx_state = TRX_STATE_DISABLED;

        nrf_802154_log_global_event(NRF_802154_LOG_VERBOSITY_LOW,
                                    NRF_802154_LOG_GLOBAL_EVENT_ID_RADIO_RESET,
                                    0U);
    }
    else
    {
        // Intentionally empty
    }

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

static void rx_automatic_antenna_handle(void)
{
    switch (m_trx_state)
    {
        case TRX_STATE_RXFRAME:
        case TRX_STATE_RXFRAME_FINISHED:
            nrf_802154_sl_ant_div_rx_started_notify();
            break;

        case TRX_STATE_ENERGY_DETECTION:
            // Intentionally empty - notification is called from core when requesting energy detection.
            // This is done due to possibility of nrf_802154_trx_energy_detection being called multiple times
            // during one energy detection from the point of view of application, if the entire procedure does not
            // fit in single timeslot.
            break;

        case TRX_STATE_TXACK:
            nrf_802154_sl_ant_div_txack_notify();
            break;

        default:
            assert(false);
            break;
    }
}

/**
 * Updates the antenna for reception, according to antenna diversity configuration.
 */
static void rx_antenna_update(void)
{
    bool                         result = true;
    nrf_802154_sl_ant_div_mode_t mode   = nrf_802154_sl_ant_div_cfg_mode_get(
        NRF_802154_SL_ANT_DIV_OP_RX);

    switch (mode)
    {
        case NRF_802154_SL_ANT_DIV_MODE_DISABLED:
            break;

        case NRF_802154_SL_ANT_DIV_MODE_MANUAL:
            result = nrf_802154_sl_ant_div_antenna_set(
                nrf_802154_sl_ant_div_cfg_antenna_get(NRF_802154_SL_ANT_DIV_OP_RX));
            break;

        case NRF_802154_SL_ANT_DIV_MODE_AUTO:
            rx_automatic_antenna_handle();
            break;

        default:
            assert(false);
            break;
    }

    assert(result);
    (void)result;
}

/**
 * Updates the antenna for transmission, according to antenna diversity configuration.
 *
 * Antenna diversity for tx is not currently supported. If antenna diversity is not
 * in disabled state, default antenna will always be used for transmission.
 */
static void tx_antenna_update(void)
{
    bool                         result = true;
    nrf_802154_sl_ant_div_mode_t mode   = nrf_802154_sl_ant_div_cfg_mode_get(
        NRF_802154_SL_ANT_DIV_OP_TX);

    switch (mode)
    {
        case NRF_802154_SL_ANT_DIV_MODE_DISABLED:
            /* Intentionally empty. */
            break;

        case NRF_802154_SL_ANT_DIV_MODE_MANUAL:
            result = nrf_802154_sl_ant_div_antenna_set(
                nrf_802154_sl_ant_div_cfg_antenna_get(NRF_802154_SL_ANT_DIV_OP_TX));
            break;

        case NRF_802154_SL_ANT_DIV_MODE_AUTO:
        default:
            assert(false);
            break;
    }

    if (!result)
    {
        assert(false);
    }
}

void nrf_802154_trx_antenna_update(void)
{
    assert(m_trx_state != TRX_STATE_DISABLED);

    switch (m_trx_state)
    {
        case TRX_STATE_RXFRAME:
        case TRX_STATE_RXFRAME_FINISHED:
        case TRX_STATE_ENERGY_DETECTION:
        case TRX_STATE_TXACK:
            rx_antenna_update();
            break;

        case TRX_STATE_STANDALONE_CCA:
        case TRX_STATE_RXACK:
        case TRX_STATE_TXFRAME:
#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
        case TRX_STATE_CONTINUOUS_CARRIER:
        case TRX_STATE_MODULATED_CARRIER:
            tx_antenna_update();
            break;

#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED
        default:
            /* Intentionally empty */
            break;
    }
}

void nrf_802154_trx_channel_set(uint8_t channel)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    channel_set(channel);

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

void nrf_802154_trx_cca_configuration_update(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    cca_configuration_update();

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

/** Check if PSDU is currently being received.
 *
 * @returns True if radio is receiving PSDU, false otherwise.
 */
bool nrf_802154_trx_psdu_is_being_received(void)
{
#if NRF_802154_DISABLE_BCC_MATCHING
    nrf_timer_task_trigger(NRF_802154_COUNTER_TIMER_INSTANCE,
                           nrf_timer_capture_task_get(NRF_TIMER_CC_CHANNEL0));
    uint32_t counter = nrf_timer_cc_get(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_CC_CHANNEL0);

    assert(counter <= 1);

    return counter > 0;
#else // NRF_802154_DISABLE_BCC_MATCHING
    return m_flags.psdu_being_received;
#endif  // NRF_802154_DISABLE_BCC_MATCHING
}

bool nrf_802154_trx_receive_is_buffer_missing(void)
{
    switch (m_trx_state)
    {
        case TRX_STATE_RXFRAME:
        /* no break */
        case TRX_STATE_RXACK:
            return m_flags.missing_receive_buffer;

        default:
            assert(!m_flags.missing_receive_buffer);
            return false;
    }
}

static void receive_buffer_missing_buffer_set(void * p_receive_buffer)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    uint32_t shorts = SHORTS_IDLE;

    switch (m_trx_state)
    {
        case TRX_STATE_RXFRAME:
            shorts = SHORTS_RX | SHORTS_RX_FREE_BUFFER;
            break;

        case TRX_STATE_RXACK:
            shorts = SHORTS_RX_ACK | SHORTS_RX_FREE_BUFFER;
            break;

        default:
            assert(false);
    }

    m_flags.missing_receive_buffer = false;

    nrf_radio_packetptr_set(NRF_RADIO, p_receive_buffer);

    nrf_radio_shorts_set(NRF_RADIO, shorts);

    if (nrf_radio_state_get(NRF_RADIO) == NRF_RADIO_STATE_RXIDLE)
    {
        nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_START);
    }

    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
}

bool nrf_802154_trx_receive_buffer_set(void * p_receive_buffer)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    bool result = false;

    mp_receive_buffer = p_receive_buffer;

    if ((p_receive_buffer != NULL) && m_flags.missing_receive_buffer)
    {
        receive_buffer_missing_buffer_set(p_receive_buffer);
        result = true;
    }

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);

    return result;
}

void nrf_802154_trx_receive_frame(uint8_t                                bcc,
                                  nrf_802154_trx_receive_notifications_t notifications_mask,
                                  const nrf_802154_tx_power_split_t    * p_ack_tx_power)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    uint32_t ints_to_enable = 0U;
    uint32_t shorts         = SHORTS_RX;

    // Force the TIMER to be stopped and count from 0.
    nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);

    m_trx_state = TRX_STATE_RXFRAME;

    // Clear filtering flag
    rx_flags_clear();
    // Clear the RSSI measurement flag.
    m_flags.rssi_started = false;

    m_flags.rssi_settled = false;

    nrf_radio_txpower_set(NRF_RADIO, p_ack_tx_power->radio_tx_power);

    if (mp_receive_buffer != NULL)
    {
        m_flags.missing_receive_buffer = false;
        nrf_radio_packetptr_set(NRF_RADIO, mp_receive_buffer);
        shorts |= SHORTS_RX_FREE_BUFFER;
    }
    else
    {
        m_flags.missing_receive_buffer = true;
    }

    // Set shorts
    nrf_radio_shorts_set(NRF_RADIO, shorts);

    // Set BCC
#if !NRF_802154_DISABLE_BCC_MATCHING
    assert(bcc != 0U);
    nrf_radio_bcc_set(NRF_RADIO, bcc * 8U);
#else
    assert(bcc == 0U);
    (void)bcc;
#endif // !NRF_802154_DISABLE_BCC_MATCHING

    // Enable IRQs
#if !NRF_802154_DISABLE_BCC_MATCHING || NRF_802154_NOTIFY_CRCERROR
    nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CRCERROR);
    ints_to_enable |= NRF_RADIO_INT_CRCERROR_MASK;
#endif // !NRF_802154_DISABLE_BCC_MATCHING ||NRF_802154_NOTIFY_CRCERROR
#if !NRF_802154_DISABLE_BCC_MATCHING
    nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_BCMATCH);
    ints_to_enable |= NRF_RADIO_INT_BCMATCH_MASK;
#endif // !NRF_802154_DISABLE_BCC_MATCHING
    nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CRCOK);
    ints_to_enable |= NRF_RADIO_INT_CRCOK_MASK;

    if ((notifications_mask & TRX_RECEIVE_NOTIFICATION_STARTED) != 0U)
    {
        nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_ADDRESS);
        ints_to_enable |= NRF_RADIO_INT_ADDRESS_MASK;
    }

    bool allow_sync_swi = false;

#ifdef RADIO_INTENSET_SYNC_Msk
    if (((notifications_mask & TRX_RECEIVE_NOTIFICATION_PRESTARTED) != 0U) ||
        (NRF_802154_SL_ANT_DIV_MODE_DISABLED !=
         nrf_802154_sl_ant_div_cfg_mode_get(NRF_802154_SL_ANT_DIV_OP_RX)))
    {
        allow_sync_swi = true;
    }
#endif

    if (allow_sync_swi)
    {
#if (NRF_802154_DISABLE_BCC_MATCHING || !defined(RADIO_INTENSET_SYNC_Msk))
        assert(false);
#else
        // The RADIO can't generate interrupt on EVENT_SYNC. Path to generate interrupt:
        // RADIO.EVENT_SYNC -> PPI_RADIO_SYNC_EGU_SYNC -> EGU.TASK_SYNC -> EGU.EVENT_SYNC ->
        // SWI_IRQHandler (in nrf_802154_swi.c), calls nrf_802154_trx_swi_irq_handler
        nrf_802154_trx_ppi_for_radio_sync_set(EGU_SYNC_TASK);

        nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_SYNC);
        nrf_egu_event_clear(NRF_802154_EGU_INSTANCE, EGU_SYNC_EVENT);
        nrf_egu_int_enable(NRF_802154_EGU_INSTANCE, EGU_SYNC_INTMASK);
#endif
    }

    nrf_radio_int_enable(NRF_RADIO, ints_to_enable);

    // Set FEM
    uint32_t delta_time;

    if (mpsl_fem_lna_configuration_set(&m_activate_rx_cc0, NULL) == 0)
    {
        delta_time = nrf_timer_cc_get(NRF_802154_TIMER_INSTANCE,
                                      NRF_TIMER_CC_CHANNEL0);
    }
    else
    {
        delta_time = 1;
        nrf_timer_cc_set(NRF_802154_TIMER_INSTANCE, NRF_TIMER_CC_CHANNEL0, delta_time);
    }

    // Set FEM PA gain for ACK transmission
    mpsl_fem_pa_gain_set(p_ack_tx_power->fem_gain);

    m_timer_value_on_radio_end_event = delta_time;

    // Select antenna
    nrf_802154_trx_antenna_update();

    // Let the TIMER stop on last event required by a FEM
    nrf_timer_shorts_enable(NRF_802154_TIMER_INSTANCE,
                            NRF_TIMER_SHORT_COMPARE0_STOP_MASK);

#if NRF_802154_DISABLE_BCC_MATCHING
    nrf_timer_shorts_enable(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_SHORT_COMPARE1_STOP_MASK);
    nrf_timer_cc_set(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_CC_CHANNEL1, 1);
#endif // NRF_802154_DISABLE_BCC_MATCHING

    // Set PPIs
#if NRF_802154_DISABLE_BCC_MATCHING
#error Configuration unsupported anymore
#endif // NRF_802154_DISABLE_BCC_MATCHING
    nrf_802154_trx_ppi_for_ramp_up_set(NRF_RADIO_TASK_RXEN, true);

    trigger_disable_to_start_rampup();

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

void nrf_802154_trx_receive_ack(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    uint32_t shorts         = SHORTS_RX_ACK;
    uint32_t ints_to_enable = 0U;

    m_trx_state = TRX_STATE_RXACK;

    if (mp_receive_buffer != NULL)
    {
        m_flags.missing_receive_buffer = false;
        nrf_radio_packetptr_set(NRF_RADIO, mp_receive_buffer);
        shorts |= SHORTS_RX_FREE_BUFFER;
    }
    else
    {
        m_flags.missing_receive_buffer = true;
    }

    nrf_radio_shorts_set(NRF_RADIO, shorts);

    nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_ADDRESS);
    ints_to_enable |= NRF_RADIO_INT_ADDRESS_MASK;
    nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CRCOK);
    ints_to_enable |= NRF_RADIO_INT_CRCOK_MASK;
    nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CRCERROR);
    ints_to_enable |= NRF_RADIO_INT_CRCERROR_MASK;

    nrf_radio_int_enable(NRF_RADIO, ints_to_enable);

    fem_for_lna_set();
    nrf_802154_trx_antenna_update();
    nrf_802154_trx_ppi_for_ramp_up_set(NRF_RADIO_TASK_RXEN, false);

    trigger_disable_to_start_rampup();

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

bool nrf_802154_trx_rssi_measure(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    bool result = false;

    if (m_trx_state == TRX_STATE_RXFRAME)
    {
        // When TRX is in RXFRAME the RADIO may be also ramping down after previous operation or still ramping up
        nrf_radio_state_t radio_state = nrf_radio_state_get(NRF_RADIO);

        if ((radio_state == RADIO_STATE_STATE_RxIdle) || (radio_state == RADIO_STATE_STATE_Rx))
        {
            if (!m_flags.rssi_settled)
            {
                // This operation is requested first time after nrf_802154_trx_receive_frame has been called
                // Radio is ramped up, but we need to wait RSSISETTLE time.
                // Precisely, we need to wait NRF_RADIO_EVENT_RSSIEND between READY->START short worked
                // and RSSI_START task is triggered. Due to limited resources we assume worst case and
                // wait whole time when rssi_measure is requested first time after nrf_802154_trx_receive_frame.
                nrf_802154_delay_us(RSSI_SETTLE_TIME_US);
                m_flags.rssi_settled = true;
            }

            nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_RSSIEND);
            nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_RSSISTART);
            m_flags.rssi_started = true;

            result = true;
        }
        else
        {
            // The RADIO may be:
            // - still ramping down after transmit
            // - ramping up for receive
            // - ramping down after frame is received (shorts)
        }
    }

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);

    return result;
}

bool nrf_802154_trx_rssi_measure_is_started(void)
{
    return m_flags.rssi_started;
}

uint8_t nrf_802154_trx_rssi_last_sample_get(void)
{
    return nrf_radio_rssi_sample_get(NRF_RADIO);
}

bool nrf_802154_trx_rssi_sample_is_available(void)
{
    return nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_RSSIEND);
}

void nrf_802154_trx_transmit_frame(const void                            * p_transmit_buffer,
                                   bool                                    cca,
                                   const nrf_802154_tx_power_split_t     * p_tx_power,
                                   nrf_802154_trx_transmit_notifications_t notifications_mask)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    uint32_t ints_to_enable = 0U;

    // Force the TIMER to be stopped and count from 0.
    nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);

    m_trx_state         = TRX_STATE_TXFRAME;
    m_transmit_with_cca = cca;

    nrf_radio_txpower_set(NRF_RADIO, p_tx_power->radio_tx_power);

    nrf_radio_packetptr_set(NRF_RADIO, p_transmit_buffer);

    // Set shorts
    if (cca)
    {
        nrf_radio_shorts_set(NRF_RADIO, SHORTS_CCA_TX);
    }
    else
    {
        nrf_radio_shorts_set(NRF_RADIO, SHORTS_TX);
    }

    // Enable IRQs
    nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_PHYEND);
    ints_to_enable |= NRF_RADIO_INT_PHYEND_MASK;

    if (cca)
    {
        nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CCABUSY);

        ints_to_enable |= NRF_RADIO_INT_CCABUSY_MASK;

        if ((notifications_mask & TRX_TRANSMIT_NOTIFICATION_CCAIDLE) != 0U)
        {
            nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CCAIDLE);
            ints_to_enable |= NRF_RADIO_INT_CCAIDLE_MASK;
        }

        if ((notifications_mask & TRX_TRANSMIT_NOTIFICATION_CCASTARTED) != 0U)
        {
            nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_READY);
            ints_to_enable |= NRF_RADIO_INT_READY_MASK;
        }
    }

    nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_ADDRESS);
#if NRF_802154_TX_STARTED_NOTIFY_ENABLED
    ints_to_enable    |= NRF_RADIO_INT_ADDRESS_MASK;
    m_flags.tx_started = false;
#endif // NRF_802154_TX_STARTED_NOTIFY_ENABLED

    nrf_radio_int_enable(NRF_RADIO, ints_to_enable);

    fem_for_tx_set(cca, p_tx_power->fem_gain);
    nrf_802154_trx_antenna_update();
    nrf_802154_trx_ppi_for_ramp_up_set(cca ? NRF_RADIO_TASK_RXEN : NRF_RADIO_TASK_TXEN, false);

    trigger_disable_to_start_rampup();

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

bool nrf_802154_trx_transmit_ack(const void * p_transmit_buffer, uint32_t delay_us)
{
    /* Assumptions on peripherals
     * TIMER is running, is counting from value saved in m_timer_value_on_radio_end_event,
     * which trigered on END event, which happened EVENT_LAT us after frame on air receive was finished.
     * RADIO is DISABLED
     * PPIs are DISABLED
     */

    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    bool result = false;

    assert(m_trx_state == TRX_STATE_RXFRAME_FINISHED);
    assert(p_transmit_buffer != NULL);

    m_trx_state = TRX_STATE_TXACK;

    // Set TIMER's CC to the moment when ramp-up should occur.
    if (delay_us <= TXRU_TIME + EVENT_LAT)
    {
        nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
        nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
        return result;
    }

    uint32_t timer_cc_ramp_up_start = m_timer_value_on_radio_end_event + delay_us - TXRU_TIME -
                                      EVENT_LAT;

    nrf_timer_cc_set(NRF_802154_TIMER_INSTANCE,
                     NRF_TIMER_CC_CHANNEL1,
                     timer_cc_ramp_up_start);

    nrf_radio_packetptr_set(NRF_RADIO, p_transmit_buffer);

    // Set shorts
    nrf_radio_shorts_set(NRF_RADIO, SHORTS_TX_ACK);

    // Clear TXREADY event to detect if PPI worked
    nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_TXREADY);

    // Set FEM
    // Note: the TIMER is running, ramp up will start in timer_cc_ramp_up_start tick
    // Assumption here is that FEM activation takes no more than TXRU_TIME.
    m_activate_tx_cc0_timeshifted = m_activate_tx_cc0;

    // Set the moment for FEM at which real transmission starts.
    m_activate_tx_cc0_timeshifted.event.timer.counter_period.end = timer_cc_ramp_up_start +
                                                                   TXRU_TIME;

    if (mpsl_fem_pa_configuration_set(&m_activate_tx_cc0_timeshifted, NULL) == 0)
    {
        // FEM scheduled its operations on timer, so the timer must be running until last
        // operation scheduled by the FEM (TIMER's CC0), which is later than radio ramp up
        nrf_timer_shorts_enable(NRF_802154_TIMER_INSTANCE, NRF_TIMER_SHORT_COMPARE0_STOP_MASK);
    }
    else
    {
        // FEM didn't schedule anything on timer, so the timer may be stopped when radio ramp-up
        // is triggered
        nrf_timer_shorts_enable(NRF_802154_TIMER_INSTANCE, NRF_TIMER_SHORT_COMPARE1_STOP_MASK);
    }

    // Select antenna
    nrf_802154_trx_antenna_update();

    nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_PHYEND);
#if NRF_802154_TX_STARTED_NOTIFY_ENABLED
    nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_ADDRESS);
#endif

    // Set PPIs
    nrf_802154_trx_ppi_for_ack_tx_set();

    // Since this point the transmission is armed on TIMER's CC1

    // Detect if PPI will work in future or has just worked.
    nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_CAPTURE3);
    uint32_t timer_cc_now = nrf_timer_cc_get(NRF_802154_TIMER_INSTANCE,
                                             NRF_TIMER_CC_CHANNEL3);
    uint32_t timer_cc_fem_start = nrf_timer_cc_get(NRF_802154_TIMER_INSTANCE,
                                                   NRF_TIMER_CC_CHANNEL0);

    // When external PA uses a timer, it should be configured to a time later than ramp up time. In
    // such case, the timer stops with shorts on PA timer. But if external PA does not use a timer,
    // FEM time is set to a value in the past that was used by LNA. After the timer overflows,
    // the timer stops with a short on the past value used by LNA. We have to detect if the current
    // timer value is after the overflow.
    if ((timer_cc_now < timer_cc_ramp_up_start) &&
        ((timer_cc_fem_start >= timer_cc_ramp_up_start) || (timer_cc_now > timer_cc_fem_start)))
    {
        result = true;
    }
    else
    {
        nrf_802154_trx_ppi_for_ramp_up_propagation_delay_wait();

        if (nrf_radio_state_get(NRF_RADIO) == NRF_RADIO_STATE_TXRU)
        {
            result = true;
        }
        else if (nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_TXREADY))
        {
            result = true;
        }
        else
        {
            result = false;
        }
    }

    if (result)
    {
        uint32_t ints_to_enable = NRF_RADIO_INT_PHYEND_MASK;

#if NRF_802154_TX_STARTED_NOTIFY_ENABLED
        ints_to_enable |= NRF_RADIO_INT_ADDRESS_MASK;
#endif // NRF_802154_TX_STARTED_NOTIFY_ENABLED

        nrf_radio_int_enable(NRF_RADIO, ints_to_enable);
    }
    else
    {
        /* We were to late with setting up PPI_TIMER_ACK, ack transmission was not triggered and
         * will not be triggered in future.
         */
        nrf_802154_trx_ppi_for_ack_tx_clear();

        /* As the timer was running during operation, it is possible we were able to configure
         * FEM thus it may trigger in future or may started PA activation.
         */
        mpsl_fem_pa_configuration_clear();
        mpsl_fem_deactivate_now(MPSL_FEM_PA);

        nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);

        /* No callbacks will be called */
    }

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);

    return result;
}

static inline void wait_until_radio_is_disabled(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    bool radio_is_disabled = false;

    // RADIO should enter DISABLED state after no longer than RX ramp-down time, which is equal
    // approximately 0.5us. Taking a bold assumption that a single iteration of the loop takes
    // one cycle to complete, 32 iterations would amount to exactly 0.5 us of execution time.
    // Please note that this approach ignores software latency completely, i.e. RADIO should
    // have changed state already before entering this function due to ISR processing delays.
    for (uint32_t i = 0; i < MAX_RXRAMPDOWN_CYCLES; i++)
    {
        if (nrf_radio_state_get(NRF_RADIO) == NRF_RADIO_STATE_DISABLED)
        {
            radio_is_disabled = true;
            break;
        }
    }

    assert(radio_is_disabled);
    (void)radio_is_disabled;

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void rxframe_finish_disable_ppis(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_RXEN, true);
#if defined(RADIO_INTENSET_SYNC_Msk)
    nrf_802154_trx_ppi_for_radio_sync_clear(EGU_SYNC_TASK);
#endif

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void rxframe_finish_disable_fem_activation(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    // Disable LNA activation
    mpsl_fem_lna_configuration_clear();
    // Disable short used by LNA activation
    nrf_timer_shorts_disable(NRF_802154_TIMER_INSTANCE,
                             NRF_TIMER_SHORT_COMPARE0_STOP_MASK);

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void rxframe_finish_disable_ints(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    uint32_t ints_to_disable = NRF_RADIO_INT_CRCOK_MASK | NRF_RADIO_INT_ADDRESS_MASK;

#if !NRF_802154_DISABLE_BCC_MATCHING || NRF_802154_NOTIFY_CRCERROR
    ints_to_disable |= NRF_RADIO_INT_CRCERROR_MASK;
#endif // !NRF_802154_DISABLE_BCC_MATCHING || NRF_802154_NOTIFY_CRCERROR
#if !NRF_802154_DISABLE_BCC_MATCHING
    ints_to_disable |= NRF_RADIO_INT_BCMATCH_MASK;
#endif // !NRF_802154_DISABLE_BCC_MATCHING
    nrf_radio_int_disable(NRF_RADIO, ints_to_disable);

#if !NRF_802154_DISABLE_BCC_MATCHING && defined(RADIO_INTENSET_SYNC_Msk)
    nrf_egu_int_disable(NRF_802154_EGU_INSTANCE, EGU_SYNC_INTMASK);
#endif

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void rxframe_finish_psdu_is_not_being_received(void)
{
#if NRF_802154_DISABLE_BCC_MATCHING
    // Anomaly 78: use SHUTDOWN instead of STOP and CLEAR.
    nrf_timer_task_trigger(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
    nrf_timer_shorts_disable(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_SHORT_COMPARE1_STOP_MASK);
#else
    m_flags.psdu_being_received = false;
#endif // NRF_802154_DISABLE_BCC_MATCHING
}

static void rxframe_finish(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    /* Note that CRCOK/CRCERROR event is generated several cycles before END event.
     *
     * Below shown what is happening in the hardware
     *
     * TIMER is started by path:
     * RADIO.SHORT_END_DISABLE -> RADIO.TASKS_DISABLE -> RADIO.EVENTS_DISABLED ->
     *    PPI_DISABLED_EGU -> EGU.TASKS_TRIGGER -> EGU.EVENTS_TRIGGERED -> PPI_EGU_TIMER_START -> TIMER.TASKS_START
     *
     * FEM's LNA mode is disabled by path:
     * RADIO.SHORT_END_DISABLE -> RADIO.TASKS_DISABLE -> RADIO.EVENTS_DISABLED -> (FEM's PPI triggering disable LNA operation,
     *                                                                             see mpsl_fem_abort_set() )
     *
     * RADIO will not ramp up, as PPI_EGU_RAMP_UP channel is self-disabling, and
     * it was disabled when receive ramp-up was started.
     */
    wait_until_radio_is_disabled(); // This includes waiting since CRCOK/CRCERROR (several cycles) event until END
                                    // and then during RXDISABLE state (0.5us)

    nrf_802154_trx_ppi_for_ramp_up_propagation_delay_wait();

    /* Now it is guaranteed, that:
     * - FEM operation to disable LNA mode is triggered through FEM's PPIs
     * - TIMER is started again allowing operation of nrf_802154_trx_transmit_ack
     */

    rxframe_finish_disable_ppis();
    rxframe_finish_disable_fem_activation();
    rxframe_finish_psdu_is_not_being_received();
    rxframe_finish_disable_ints();
    nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);
    m_flags.missing_receive_buffer = false;

    /* Current state of peripherals:
     * RADIO is DISABLED
     * FEM is powered but LNA mode has just been turned off
     * TIMER is running, counting from the value stored in m_timer_value_on_radio_end_event
     * All PPIs used by receive operation are disabled, forks are cleared, PPI groups that were used are cleared
     * RADIO.SHORTS are cleared
     */

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

void nrf_802154_trx_abort(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    switch (m_trx_state)
    {
        case TRX_STATE_DISABLED:
        case TRX_STATE_IDLE:
        case TRX_STATE_FINISHED:
            /* Nothing to do, intentionally empty */
            break;

        case TRX_STATE_GOING_IDLE:
            go_idle_abort();
            break;

        case TRX_STATE_RXFRAME:
            receive_frame_abort();
            break;

        case TRX_STATE_RXFRAME_FINISHED:
            nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
            m_trx_state = TRX_STATE_FINISHED;
            break;

        case TRX_STATE_RXACK:
            receive_ack_abort();
            break;

        case TRX_STATE_TXFRAME:
            transmit_frame_abort();
            break;

        case TRX_STATE_TXACK:
            transmit_ack_abort();
            break;

        case TRX_STATE_STANDALONE_CCA:
            standalone_cca_abort();
            break;

#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
        case TRX_STATE_CONTINUOUS_CARRIER:
            continuous_carrier_abort();
            break;

        case TRX_STATE_MODULATED_CARRIER:
            modulated_carrier_abort();
            break;
#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED

        case TRX_STATE_ENERGY_DETECTION:
            energy_detection_abort();
            break;

        default:
            assert(false);
    }

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

trx_state_t nrf_802154_trx_state_get(void)
{
    return m_trx_state;
}

static void go_idle_from_state_finished(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    m_trx_state = TRX_STATE_GOING_IDLE;

    nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_DISABLED);
    nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);

    nrf_radio_int_enable(NRF_RADIO, NRF_RADIO_INT_DISABLED_MASK);

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

bool nrf_802154_trx_go_idle(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    bool result = false;

    switch (m_trx_state)
    {
        case TRX_STATE_DISABLED:
            assert(false);
            break;

        case TRX_STATE_IDLE:
            /* There will be no callout */
            break;

        case TRX_STATE_GOING_IDLE:
            /* There will be callout */
            result = true;
            break;

        case TRX_STATE_RXFRAME_FINISHED:
            nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
        /* Fallthrough */

        case TRX_STATE_FINISHED:
            go_idle_from_state_finished();
            result = true;
            break;

        default:
            assert(false);
    }

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);

    return result;
}

static void go_idle_abort(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_INT_DISABLED_MASK);
    m_trx_state = TRX_STATE_FINISHED;

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void receive_frame_abort(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    rxframe_finish_disable_ppis();
    rxframe_finish_disable_fem_activation();
    rxframe_finish_psdu_is_not_being_received();
    rxframe_finish_disable_ints();
    nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);

    m_flags.missing_receive_buffer = false;
    nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);

    nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);

    m_trx_state = TRX_STATE_FINISHED;

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void rxack_finish_disable_ppis(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_RXEN, false);

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void rxack_finish_disable_ints(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    nrf_radio_int_disable(NRF_RADIO,
                          NRF_RADIO_INT_ADDRESS_MASK | NRF_RADIO_INT_CRCERROR_MASK |
                          NRF_RADIO_INT_CRCOK_MASK);

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void rxack_finish_disable_fem_activation(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    // Disable LNA activation
    mpsl_fem_lna_configuration_clear();

    // Clear LNA PPIs
    nrf_802154_trx_ppi_for_fem_clear();

    // Disable short used by LNA activation
    nrf_timer_shorts_disable(NRF_802154_TIMER_INSTANCE,
                             NRF_TIMER_SHORT_COMPARE0_STOP_MASK);

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void rxack_finish(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    rxack_finish_disable_ppis();
    rxack_finish_disable_ints();
    rxack_finish_disable_fem_activation();
    nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
    nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);
    m_flags.missing_receive_buffer = false;

    /* Current state of peripherals
     * RADIO is DISABLED
     * FEM is powered but LNA mode has just been turned off
     * TIMER is shutdown
     * PPIs used by receive operation are disabled, forks are cleared, PPI groups used are cleared
     * RADIO.SHORTS are cleared
     */

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void receive_ack_abort(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    rxack_finish_disable_ppis();
    rxack_finish_disable_ints();
    rxack_finish_disable_fem_activation();
    nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);
    m_flags.missing_receive_buffer = false;

    nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);

    nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);

    m_trx_state = TRX_STATE_FINISHED;

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

void nrf_802154_trx_standalone_cca(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    assert((m_trx_state == TRX_STATE_IDLE) || (m_trx_state == TRX_STATE_FINISHED));

    m_trx_state = TRX_STATE_STANDALONE_CCA;

    // Set shorts
    nrf_radio_shorts_set(NRF_RADIO, SHORTS_CCA);

    // Enable IRQs
    nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CCABUSY);
    nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CCAIDLE);
    nrf_radio_int_enable(NRF_RADIO, NRF_RADIO_INT_CCABUSY_MASK | NRF_RADIO_INT_CCAIDLE_MASK);

    // Set FEM
    fem_for_lna_set();

    // Select antenna
    nrf_802154_trx_antenna_update();

    // Set PPIs
    nrf_802154_trx_ppi_for_ramp_up_set(NRF_RADIO_TASK_RXEN, false);

    trigger_disable_to_start_rampup();

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

static void standalone_cca_finish(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_RXEN, false);

    nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);

    fem_for_lna_reset();

    nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_INT_CCABUSY_MASK | NRF_RADIO_INT_CCAIDLE_MASK);

    nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_CCASTOP);
    nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void standalone_cca_abort(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    standalone_cca_finish();

    m_trx_state = TRX_STATE_FINISHED;

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

#if NRF_802154_CARRIER_FUNCTIONS_ENABLED

void nrf_802154_trx_continuous_carrier(const nrf_802154_tx_power_split_t * p_tx_power)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    assert((m_trx_state == TRX_STATE_IDLE) || (m_trx_state == TRX_STATE_FINISHED));

    m_trx_state = TRX_STATE_CONTINUOUS_CARRIER;

    // Set Tx Power
    nrf_radio_txpower_set(NRF_RADIO, p_tx_power->radio_tx_power);

    // Set FEM
    fem_for_pa_set(p_tx_power->fem_gain);

    // Select antenna
    nrf_802154_trx_antenna_update();

    // Set PPIs
    nrf_802154_trx_ppi_for_ramp_up_set(NRF_RADIO_TASK_TXEN, false);

    trigger_disable_to_start_rampup();

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

void nrf_802154_trx_continuous_carrier_restart(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    assert(m_trx_state == TRX_STATE_CONTINUOUS_CARRIER);

    // Continuous carrier PPIs are configured without self-disabling
    // Triggering RADIO.TASK_DISABLE causes ramp-down -> RADIO.EVENTS_DISABLED -> EGU.TASK -> EGU.EVENT ->
    // RADIO.TASK_TXEN -> ramp_up -> new continous carrier

    nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

static void continuous_carrier_abort(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_TXEN, false);

    fem_for_pa_reset();

    nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);

    m_trx_state = TRX_STATE_FINISHED;

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

void nrf_802154_trx_modulated_carrier(const void                        * p_transmit_buffer,
                                      const nrf_802154_tx_power_split_t * p_tx_power)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    assert((m_trx_state == TRX_STATE_IDLE) || (m_trx_state == TRX_STATE_FINISHED));
    assert(p_transmit_buffer != NULL);

    m_trx_state = TRX_STATE_MODULATED_CARRIER;

    // Set Tx Power
    nrf_radio_txpower_set(NRF_RADIO, p_tx_power->radio_tx_power);

    // Set Tx buffer
    nrf_radio_packetptr_set(NRF_RADIO, p_transmit_buffer);

    // Set shorts
    nrf_radio_shorts_set(NRF_RADIO, SHORTS_MOD_CARRIER);

    // Set FEM
    fem_for_pa_set(p_tx_power->fem_gain);

    // Select antenna
    nrf_802154_trx_antenna_update();

    // Set PPIs
    nrf_802154_trx_ppi_for_ramp_up_set(NRF_RADIO_TASK_TXEN, false);

    trigger_disable_to_start_rampup();

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

void nrf_802154_trx_modulated_carrier_restart(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    assert(m_trx_state == TRX_STATE_MODULATED_CARRIER);

    // Modulated carrier PPIs are configured without self-disabling
    // Triggering RADIO.TASK_DISABLE causes ramp-down -> RADIO.EVENTS_DISABLED -> EGU.TASK -> EGU.EVENT ->
    // RADIO.TASK_TXEN -> ramp_up -> new modulated carrier

    nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

static void modulated_carrier_abort()
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_TXEN, false);

    nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);

    fem_for_pa_reset();

    nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);

    m_trx_state = TRX_STATE_FINISHED;

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED

void nrf_802154_trx_energy_detection(uint32_t ed_count)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    assert((m_trx_state == TRX_STATE_FINISHED) || (m_trx_state == TRX_STATE_IDLE));

    m_trx_state = TRX_STATE_ENERGY_DETECTION;

    ed_count--;
    /* Check that vd_count will fit into defined bits of register */
    assert( (ed_count & (~RADIO_EDCNT_EDCNT_Msk)) == 0U);

    nrf_radio_ed_loop_count_set(NRF_RADIO, ed_count);

    // Set shorts
    nrf_radio_shorts_set(NRF_RADIO, SHORTS_ED);

    // Enable IRQs
    nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_EDEND);
    nrf_radio_int_enable(NRF_RADIO, NRF_RADIO_INT_EDEND_MASK);

    // Set FEM
    fem_for_lna_set();

    // Select antenna
    nrf_802154_trx_antenna_update();

    // Set PPIs
    nrf_802154_trx_ppi_for_ramp_up_set(NRF_RADIO_TASK_RXEN, false);

    trigger_disable_to_start_rampup();

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

static void energy_detection_finish(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_RXEN, false);
    fem_for_lna_reset();

    nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_INT_EDEND_MASK);
    nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);

    nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_EDSTOP);
    nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void energy_detection_abort(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    energy_detection_finish();
    m_trx_state = TRX_STATE_FINISHED;

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void irq_handler_ready(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    assert(m_trx_state == TRX_STATE_TXFRAME);

    nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_INT_READY_MASK);

    nrf_802154_trx_transmit_frame_ccastarted();

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

static void irq_handler_address(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    switch (m_trx_state)
    {
        case TRX_STATE_RXFRAME:
            nrf_802154_trx_receive_frame_started();
            break;

        case TRX_STATE_RXACK:
            m_flags.rssi_started = true;
            nrf_802154_trx_receive_ack_started();
            break;

#if NRF_802154_TX_STARTED_NOTIFY_ENABLED
        case TRX_STATE_TXFRAME:
            nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_INT_ADDRESS_MASK);
            m_flags.tx_started = true;
            nrf_802154_trx_transmit_frame_started();
            break;
#endif // NRF_802154_TX_STARTED_NOTIFY_ENABLED

#if NRF_802154_TX_STARTED_NOTIFY_ENABLED
        case TRX_STATE_TXACK:
            nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_INT_ADDRESS_MASK);
            nrf_802154_trx_transmit_ack_started();
            break;
#endif

        default:
            assert(false);
    }

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

#if !NRF_802154_DISABLE_BCC_MATCHING
static void irq_handler_bcmatch(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    uint8_t current_bcc;
    uint8_t next_bcc;

    assert(m_trx_state == TRX_STATE_RXFRAME);

    m_flags.psdu_being_received = true;

    // If CRCERROR event is set, it means that events are handled out of order due to software
    // latency. Just skip this handler in this case - frame will be dropped.
    if (nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_CRCERROR))
    {
        nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
        return;
    }

    current_bcc = nrf_radio_bcc_get(NRF_RADIO) / 8U;

    next_bcc = nrf_802154_trx_receive_frame_bcmatched(current_bcc);

    if (next_bcc > current_bcc)
    {
        /* Note: If we don't make it before given octet is received by RADIO bcmatch will not be triggered.
         * The fact that filtering may be not completed at the call to nrf_802154_trx_receive_received handler
         * should be handled by the handler.
         */
        nrf_radio_bcc_set(NRF_RADIO, next_bcc * 8);
    }

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

#endif

#if !NRF_802154_DISABLE_BCC_MATCHING || NRF_802154_NOTIFY_CRCERROR
static void irq_handler_crcerror(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    switch (m_trx_state)
    {
        case TRX_STATE_RXFRAME:
#if NRF_802154_DISABLE_BCC_MATCHING
            /* The hardware restarts receive at the moment.
             * The TIMER is being shutdown and restarted by the hardware see
             * PPI_CRCERROR_CLEAR in nrf_802154_trx_receive_frame
             */
#else
            rxframe_finish();
            /* On crc error TIMER is not needed, no ACK may be sent */
            nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
            m_trx_state = TRX_STATE_FINISHED;
#endif
            nrf_802154_trx_receive_frame_crcerror();
            break;

        case TRX_STATE_RXACK:
            rxack_finish();
            m_trx_state = TRX_STATE_FINISHED;
            nrf_802154_trx_receive_ack_crcerror();
            break;

        default:
            assert(false);
    }

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

#endif

static void irq_handler_crcok(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    switch (m_trx_state)
    {
        case TRX_STATE_RXFRAME:
            m_flags.rssi_started = true;
            rxframe_finish();
            m_trx_state = TRX_STATE_RXFRAME_FINISHED;
            nrf_802154_trx_receive_frame_received();
            break;

        case TRX_STATE_RXACK:
            rxack_finish();
            m_trx_state = TRX_STATE_FINISHED;
            nrf_802154_trx_receive_ack_received();
            break;

        default:
            assert(false);
    }

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

static void txframe_finish_disable_ppis(bool cca)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    nrf_802154_trx_ppi_for_ramp_up_clear(cca ? NRF_RADIO_TASK_RXEN : NRF_RADIO_TASK_TXEN, false);

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void txframe_finish_disable_ints(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    nrf_radio_int_disable(NRF_RADIO,
                          NRF_RADIO_INT_PHYEND_MASK |
                          NRF_RADIO_INT_CCAIDLE_MASK |
                          NRF_RADIO_INT_CCABUSY_MASK |
                          NRF_RADIO_INT_ADDRESS_MASK |
                          NRF_RADIO_INT_READY_MASK);

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void txframe_finish(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    /* Below shown what is happening in the hardware
     *
     * Due to short RADIO.SHORT_PHYEND_DISABLE the RADIO is in TXDISABLE (currently ramping down for 21us) state or
     * has already reached DISABLED state (if we entered into ISR with greater latency)
     *
     * Even if RADIO reaches state DISABLED (event DISABLED was triggered by short), no transmission will be triggered
     * as PPI_EGU_RAMP_UP is self-disabling PPI channel.
     *
     * If FEM is in use the PPI_EGU_TIMER_START might be triggered if radio reached DISABLED state,
     * so the TIMER may start counting from the value on which FEM activation finished. The TIMER's CC registers
     * are set in the past so even if TIMER started no spurious FEM PA activation will occur.
     * We need to disable PPI_EGU_TIMER_START and then shutdown TIMER as it is not used.
     */
    txframe_finish_disable_ppis(m_transmit_with_cca);

    fem_for_tx_reset(m_transmit_with_cca);

    txframe_finish_disable_ints();

    nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);
#if NRF_802154_TX_STARTED_NOTIFY_ENABLED
    m_flags.tx_started = false;
#endif
    m_flags.missing_receive_buffer = false;

    /* Current state of peripherals
     * RADIO is either in TXDISABLE or DISABLED
     * FEM is powered but PA mode will be turned off on entry into DISABLED state or is already turned off
     * TIMER is shutdown
     * All PPIs that were used are disabled (forks are cleared if used)
     * RADIO.SHORTS are cleared
     */

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void transmit_frame_abort(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    txframe_finish_disable_ppis(m_transmit_with_cca);
    nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);

    fem_for_tx_reset(m_transmit_with_cca);

    txframe_finish_disable_ints();
#if NRF_802154_TX_STARTED_NOTIFY_ENABLED
    m_flags.tx_started = false;
#endif
    m_flags.missing_receive_buffer = false;

    nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_CCASTOP);
    nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);

    m_trx_state = TRX_STATE_FINISHED;

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void txack_finish(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    /* Below shown what is happening in the hardware
     *
     * Due to short RADIO.SHORT_PHYEND_DISABLE the RADIO is in TXDISABLE (currently ramping down for 21us) state or
     * has already reached DISABLED state (if we entered into ISR with greater latency)
     *
     * Even if RADIO reaches state DISABLED (event DISABLED was triggered by short), no transmission will be triggered
     * as only PPI_TIMER_TX_ACK was enabled.
     *
     * FEM will disable PA mode on RADIO.DISABLED event
     *
     * The TIMER was counting to trigger RADIO ramp up and FEM (if required). The TIMER is configured
     * to trigger STOP task on one of these events (whichever is later). As we finished the TIMER is
     * stopped now, and there is no PPIs starting it automatically by the hardware.
     */
    nrf_802154_trx_ppi_for_ack_tx_clear();

    nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);

    mpsl_fem_pa_configuration_clear();

    nrf_timer_shorts_disable(NRF_802154_TIMER_INSTANCE,
                             NRF_TIMER_SHORT_COMPARE0_STOP_MASK |
                             NRF_TIMER_SHORT_COMPARE1_STOP_MASK);

    // Anomaly 78: use SHUTDOWN instead of STOP and CLEAR.
    nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);

    nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_INT_PHYEND_MASK | NRF_RADIO_INT_ADDRESS_MASK);

    /* Current state of peripherals
     * RADIO is either in TXDISABLE or DISABLED
     * FEM is powered but PA mode will be turned off on entry into DISABLED state or is already turned off
     * TIMER is shutdown
     * All PPIs that were used are disabled (forks are cleared if used)
     * RADIO.SHORTS are cleared
     */

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void transmit_ack_abort(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    nrf_802154_trx_ppi_for_ack_tx_clear();

    nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);

    mpsl_fem_pa_configuration_clear();

    nrf_timer_shorts_disable(NRF_802154_TIMER_INSTANCE,
                             NRF_TIMER_SHORT_COMPARE0_STOP_MASK |
                             NRF_TIMER_SHORT_COMPARE1_STOP_MASK);

    // Anomaly 78: use SHUTDOWN instead of STOP and CLEAR.
    nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);

    nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_INT_PHYEND_MASK | NRF_RADIO_INT_ADDRESS_MASK);

    nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);

    m_trx_state = TRX_STATE_FINISHED;

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void irq_handler_phyend(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    switch (m_trx_state)
    {
        case TRX_STATE_TXFRAME:
            txframe_finish();
            m_trx_state = TRX_STATE_FINISHED;
            nrf_802154_trx_transmit_frame_transmitted();
            break;

        case TRX_STATE_TXACK:
            txack_finish();
            m_trx_state = TRX_STATE_FINISHED;
            nrf_802154_trx_transmit_ack_transmitted();
            break;

        default:
            assert(false);
    }

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

static void go_idle_finish(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);

    nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_INT_DISABLED_MASK);

    fem_power_down_now();

    m_trx_state = TRX_STATE_IDLE;

    nrf_802154_trx_go_idle_finished();

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}

static void irq_handler_disabled(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    switch (m_trx_state)
    {
        case TRX_STATE_GOING_IDLE:
            go_idle_finish();
            break;

        default:
            assert(false);
    }

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

static void irq_handler_ccaidle(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    switch (m_trx_state)
    {
        case TRX_STATE_STANDALONE_CCA:
            standalone_cca_finish();
            m_trx_state = TRX_STATE_FINISHED;
            nrf_802154_trx_standalone_cca_finished(true);
            break;

        case TRX_STATE_TXFRAME:
            nrf_802154_trx_transmit_frame_ccaidle();
            break;

        default:
            assert(false);
    }

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

static void irq_handler_ccabusy(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    switch (m_trx_state)
    {
        case TRX_STATE_TXFRAME:
            assert(m_transmit_with_cca);
            txframe_finish();
            m_trx_state = TRX_STATE_FINISHED;
            nrf_802154_trx_transmit_frame_ccabusy();
            break;

        case TRX_STATE_STANDALONE_CCA:
            standalone_cca_finish();
            m_trx_state = TRX_STATE_FINISHED;
            nrf_802154_trx_standalone_cca_finished(false);
            break;

        default:
            assert(false);
    }

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

static void irq_handler_edend(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    assert(m_trx_state == TRX_STATE_ENERGY_DETECTION);

    uint8_t ed_sample = nrf_radio_ed_sample_get(NRF_RADIO);

    energy_detection_finish();
    m_trx_state = TRX_STATE_FINISHED;

    nrf_802154_trx_energy_detection_finished(ed_sample);

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

#if defined(RADIO_INTENSET_SYNC_Msk)
static void irq_handler_sync(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    assert(m_trx_state == TRX_STATE_RXFRAME);

    nrf_802154_trx_receive_frame_prestarted();

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

#endif

void nrf_802154_radio_irq_handler(void)
{
    nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);

    // Prevent interrupting of this handler by requests from higher priority code.
    nrf_802154_critical_section_forcefully_enter();

#if defined(RADIO_INTENSET_SYNC_Msk)
    // Note: For NRF_RADIO_EVENT_SYNC we enable interrupt through EGU.
    // That's why we check here EGU's EGU_SYNC_INTMASK.
    // The RADIO does not have interrupt from SYNC event.
    if (nrf_egu_int_enable_check(NRF_802154_EGU_INSTANCE, EGU_SYNC_INTMASK) &&
        nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_SYNC))
    {
        nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_SYNC);
        nrf_egu_event_clear(NRF_802154_EGU_INSTANCE, EGU_SYNC_EVENT);

        irq_handler_sync();
    }
#endif

    if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_READY_MASK) &&
        nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_READY))
    {
        nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_READY);

        irq_handler_ready();
    }

    if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_ADDRESS_MASK) &&
        nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_ADDRESS))
    {
        nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_ADDRESS);

        irq_handler_address();
    }

#if !NRF_802154_DISABLE_BCC_MATCHING
    // Check MAC frame header.
    if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_BCMATCH_MASK) &&
        nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_BCMATCH))
    {
        nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_BCMATCH);

        irq_handler_bcmatch();
    }

#endif // !NRF_802154_DISABLE_BCC_MATCHING

#if !NRF_802154_DISABLE_BCC_MATCHING || NRF_802154_NOTIFY_CRCERROR
    if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_CRCERROR_MASK) &&
        nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_CRCERROR))
    {
        nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CRCERROR);

        irq_handler_crcerror();
    }
#endif // !NRF_802154_DISABLE_BCC_MATCHING || NRF_802154_NOTIFY_CRCERROR

    if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_CRCOK_MASK) &&
        nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_CRCOK))
    {
        nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CRCOK);

        irq_handler_crcok();
    }

    if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_PHYEND_MASK) &&
        nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_PHYEND))
    {
        nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_PHYEND);

        irq_handler_phyend();
    }

    if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_DISABLED_MASK) &&
        nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_DISABLED))
    {
        nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_DISABLED);

        irq_handler_disabled();
    }

    if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_CCAIDLE_MASK) &&
        nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_CCAIDLE))
    {
        nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CCAIDLE);

        irq_handler_ccaidle();
    }

    if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_CCABUSY_MASK) &&
        nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_CCABUSY))
    {
        nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CCABUSY);

        irq_handler_ccabusy();
    }

    if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_EDEND_MASK) &&
        nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_EDEND))
    {
        nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_EDEND);

        irq_handler_edend();
    }

    nrf_802154_critical_section_exit();

    nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}

#if NRF_802154_INTERNAL_RADIO_IRQ_HANDLING
void RADIO_IRQHandler(void)
{
    nrf_802154_radio_irq_handler();
}

#endif // NRF_802154_INTERNAL_RADIO_IRQ_HANDLING

#if defined(RADIO_INTENSET_SYNC_Msk)
void nrf_802154_trx_swi_irq_handler(void)
{
    // If this handler is preempted by MARGIN, RADIO IRQ might be set to pending
    // and executed after MARGIN processing is finished, i.e. after the end of a timeslot.
    // To prevent that from happening, the handler is executed with disabled interrupts.
    nrf_802154_mcu_critical_state_t mcu_crit_state;

    nrf_802154_mcu_critical_enter(mcu_crit_state);

    if (nrf_egu_int_enable_check(NRF_802154_EGU_INSTANCE, EGU_SYNC_INTMASK) &&
        nrf_egu_event_check(NRF_802154_EGU_INSTANCE, EGU_SYNC_EVENT))
    {
        nrf_egu_event_clear(NRF_802154_EGU_INSTANCE, EGU_SYNC_EVENT);

        // We are in SWI_IRQHandler, which priority is usually lower than RADIO_IRQHandler.
        // To avoid problems with critical sections, trigger RADIO_IRQ manually.
        // - If we are not in critical section, RADIO_IRQ will start shortly (calling
        // nrf_802154_radio_irq_handler) preempting current SWI_IRQHandler. From
        // nrf_802154_radio_irq_handler we acquire critical section and
        // process sync event.
        // If we are in critical section, the RADIO_IRQ is disabled on NVIC.
        // Following will make it pending, and processing of RADIO_IRQ will start
        // when critical section is left.

        nrf_802154_irq_set_pending(RADIO_IRQn);
    }

    nrf_802154_mcu_critical_exit(mcu_crit_state);
}

#endif

const nrf_802154_sl_event_handle_t * nrf_802154_trx_radio_end_event_handle_get(void)
{
    static const nrf_802154_sl_event_handle_t r = {
#if defined(DPPI_PRESENT)
        .event_addr = NRF_802154_DPPI_RADIO_END,
        .shared     = true
#else
        .event_addr = (uint32_t)&NRF_RADIO->EVENTS_END
#endif
    };

    return &r;
}

const nrf_802154_sl_event_handle_t * nrf_802154_trx_radio_ready_event_handle_get(void)
{
    static const nrf_802154_sl_event_handle_t r = {
#if defined(DPPI_PRESENT)
        .event_addr = NRF_802154_DPPI_RADIO_READY,
        .shared     = true
#else
        .event_addr = (uint32_t)&NRF_RADIO->EVENTS_READY
#endif
    };

    return &r;
}

const nrf_802154_sl_event_handle_t * nrf_802154_trx_radio_crcok_event_handle_get(void)
{
    static const nrf_802154_sl_event_handle_t r = {
        .event_addr = (uint32_t)&NRF_RADIO->EVENTS_CRCOK,
#if defined(DPPI_PRESENT)
        .shared = false
#endif
    };

    return &r;
}

const nrf_802154_sl_event_handle_t * nrf_802154_trx_radio_phyend_event_handle_get(void)
{
    static const nrf_802154_sl_event_handle_t r = {
#if defined(DPPI_PRESENT)
        .event_addr = NRF_802154_DPPI_RADIO_PHYEND,
        .shared     = true
#else
        .event_addr = (uint32_t)&NRF_RADIO->EVENTS_PHYEND
#endif
    };

    return &r;
}
