/*
 * Copyright (c) 2017 - 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.
 *
 */

/**
 * @file
 *   This file implements requests to the driver triggered by the MAC layer through SWI.
 *
 */

#include "nrf_802154_request.h"

#include <assert.h>
#include <stdbool.h>
#include <stdint.h>

#include "nrf_802154_config.h"
#include "nrf_802154_core.h"
#include "nrf_802154_critical_section.h"
#include "nrf_802154_debug.h"
#include "nrf_802154_queue.h"
#include "nrf_802154_rx_buffer.h"
#include "nrf_802154_swi.h"
#include "nrf_802154_types.h"
#include "nrf_802154_utils.h"
#include "mac_features/nrf_802154_csma_ca.h"
#include "hal/nrf_radio.h"
#include "hal/nrf_egu.h"
#include "mac_features/nrf_802154_delayed_trx.h"
#include "platform/nrf_802154_irq.h"

#include <nrf.h>

/** Size of requests queue.
 *
 * Two is minimal queue size. It is not expected in current implementation to queue a few requests.
 */
#define REQ_QUEUE_SIZE 2

#define REQ_INT        NRF_EGU_INT_TRIGGERED2   ///< Label of request interrupt.
#define REQ_TASK       NRF_EGU_TASK_TRIGGER2    ///< Label of request task.
#define REQ_EVENT      NRF_EGU_EVENT_TRIGGERED2 ///< Label of request event.

/// Type of requests in request queue.
typedef enum
{
    REQ_TYPE_SLEEP,
    REQ_TYPE_RECEIVE,
    REQ_TYPE_TRANSMIT,
    REQ_TYPE_ENERGY_DETECTION,
    REQ_TYPE_CCA,
    REQ_TYPE_CONTINUOUS_CARRIER,
    REQ_TYPE_MODULATED_CARRIER,
    REQ_TYPE_BUFFER_FREE,
    REQ_TYPE_CHANNEL_UPDATE,
    REQ_TYPE_CCA_CFG_UPDATE,
    REQ_TYPE_RSSI_MEASURE,
    REQ_TYPE_RSSI_GET,
    REQ_TYPE_ANTENNA_UPDATE,
    REQ_TYPE_TRANSMIT_AT,
    REQ_TYPE_TRANSMIT_AT_CANCEL,
    REQ_TYPE_RECEIVE_AT,
    REQ_TYPE_RECEIVE_AT_CANCEL,
    REQ_TYPE_CSMA_CA_START,
} nrf_802154_req_type_t;

/// Request data in request queue.
typedef struct
{
    nrf_802154_req_type_t type; ///< Type of the request.

    union
    {
        struct
        {
            nrf_802154_term_t term_lvl; ///< Request priority.
            bool            * p_result; ///< Sleep request result.
        } sleep;                        ///< Sleep request details.

        struct
        {
            nrf_802154_notification_func_t notif_func;  ///< Error notified in case of success.
            nrf_802154_term_t              term_lvl;    ///< Request priority.
            req_originator_t               req_orig;    ///< Request originator.
            bool                           notif_abort; ///< If function termination should be notified.
            uint32_t                       id;          ///< Identifier of a reception window.
            bool                         * p_result;    ///< Receive request result.
        } receive;                                      ///< Receive request details.

        struct
        {
            nrf_802154_notification_func_t notif_func; ///< Error notified in case of success.
            nrf_802154_term_t              term_lvl;   ///< Request priority.
            req_originator_t               req_orig;   ///< Request originator.
            uint8_t                      * p_data;     ///< Pointer to a buffer containing PHR and PSDU of the frame to transmit.
            nrf_802154_transmit_params_t * p_params;   ///< Pointer to transmission parameters.
            bool                         * p_result;   ///< Transmit request result.
        } transmit;                                    ///< Transmit request details.

        struct
        {
            nrf_802154_term_t term_lvl; ///< Request priority.
            bool            * p_result; ///< Energy detection request result.
            uint32_t          time_us;  ///< Requested time of energy detection procedure.
        } energy_detection;             ///< Energy detection request details.

        struct
        {
            nrf_802154_term_t term_lvl; ///< Request priority.
            bool            * p_result; ///< CCA request result.
        } cca;                          ///< CCA request details.

#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
        struct
        {
            nrf_802154_term_t term_lvl; ///< Request priority.
            bool            * p_result; ///< Continuous carrier request result.
        } continuous_carrier;           ///< Continuous carrier request details.

        struct
        {
            nrf_802154_term_t term_lvl; ///< Request priority.
            const uint8_t   * p_data;   ///< Pointer to a buffer to modulate the carrier wave with.
            bool            * p_result; ///< Modulated carrier request result.
        } modulated_carrier;            ///< Modulated carrier request details.
#endif  // NRF_802154_CARRIER_FUNCTIONS_ENABLED

        struct
        {
            uint8_t * p_data;   ///< Pointer to receive buffer to free.
            bool    * p_result; ///< Buffer free request result.
        } buffer_free;          ///< Buffer free request details.

        struct
        {
            req_originator_t req_orig; ///< Request originator.
            bool           * p_result; ///< Channel update request result.
        } channel_update;              ///< Channel update request details.

        struct
        {
            bool * p_result; ///< CCA config update request result.
        } cca_cfg_update;    ///< CCA config update request details.

        struct
        {
            bool * p_result; ///< RSSI measurement request result.
        } rssi_measure;      ///< RSSI measurement request details.

        struct
        {
            int8_t * p_rssi;   ///< RSSI measurement result.
            bool   * p_result; ///< RSSI measurement status.
        } rssi_get;            ///< Details of the getter that retrieves the RSSI measurement result.

        struct
        {
            bool * p_result; ///< Antenna update request result.
        } antenna_update;    ///< Antenna update request details.

#if NRF_802154_DELAYED_TRX_ENABLED
        struct
        {
            uint8_t                                 * p_data;
            uint64_t                                  tx_time;
            const nrf_802154_transmit_at_metadata_t * p_metadata;
            bool                                    * p_result;
        } transmit_at;

        struct
        {
            bool * p_result;
        } transmit_at_cancel;

        struct
        {
            uint64_t rx_time;
            uint32_t timeout;
            uint8_t  channel;
            uint32_t id;
            bool   * p_result;
        } receive_at;

        struct
        {
            uint32_t id;
            bool   * p_result;
        } receive_at_cancel;

#endif // NRF_802154_DELAYED_TRX_ENABLED

        struct
        {
            uint8_t                                      * p_data;
            const nrf_802154_transmit_csma_ca_metadata_t * p_metadata;
            bool                                         * p_result;
        } csma_ca_start; ///< Antenna update request details.

    } data;              ///< Request data depending on its type.
} nrf_802154_req_data_t;

/**@brief Instance of a requests queue */
static nrf_802154_queue_t m_requests_queue;

/**@brief Memory holding requests queue items */
static nrf_802154_req_data_t m_requests_queue_memory[REQ_QUEUE_SIZE];

/**@brief State of the MCU critical section */
static volatile nrf_802154_mcu_critical_state_t m_mcu_cs;

/**
 * Enter request block.
 *
 * This is a helper function used in all request functions to atomically
 * find an empty slot in request queue and allow atomic slot update.
 *
 * @return Pointer to an empty slot in the request queue.
 */
static nrf_802154_req_data_t * req_enter(void)
{
    nrf_802154_mcu_critical_enter(m_mcu_cs);

    assert(!nrf_802154_queue_is_full(&m_requests_queue));

    return (nrf_802154_req_data_t *)nrf_802154_queue_push_begin(&m_requests_queue);
}

/**
 * Exit request block.
 *
 * This is a helper function used in all request functions to end atomic slot update
 * and trigger SWI to process the request from the slot.
 */
static void req_exit(void)
{
    nrf_802154_queue_push_commit(&m_requests_queue);

    nrf_egu_task_trigger(NRF_802154_EGU_INSTANCE, REQ_TASK);

    nrf_802154_mcu_critical_exit(m_mcu_cs);
}

/** Assert if SWI interrupt is disabled. */
static inline void assert_interrupt_status(void)
{
    assert(nrf_802154_irq_is_enabled(NRF_802154_EGU_IRQN));
}

#define REQUEST_FUNCTION(func_core, func_swi, ...) \
    bool result = false;                           \
                                                   \
    if (active_vector_priority_is_high())          \
    {                                              \
        result = func_core(__VA_ARGS__);           \
    }                                              \
    else                                           \
    {                                              \
        assert_interrupt_status();                 \
        func_swi(__VA_ARGS__, &result);            \
    }                                              \
                                                   \
    return result;

#define REQUEST_FUNCTION_NO_ARGS(func_core, func_swi) \
    bool result = false;                              \
                                                      \
    if (active_vector_priority_is_high())             \
    {                                                 \
        result = func_core();                         \
    }                                                 \
    else                                              \
    {                                                 \
        assert_interrupt_status();                    \
        func_swi(&result);                            \
    }                                                 \
                                                      \
    return result;

/** Check if active vector priority is high enough to call requests directly.
 *
 *  @retval  true   Active vector priority is greater or equal to SWI priority.
 *  @retval  false  Active vector priority is lower than SWI priority.
 */
static bool active_vector_priority_is_high(void)
{

    return nrf_802154_critical_section_active_vector_priority_get() <=
           nrf_802154_irq_priority_get(NRF_802154_EGU_IRQN);
}

/**
 * @brief Requests entering the @ref RADIO_STATE_SLEEP state from the SWI priority.
 *
 * @param[in]   term_lvl  Termination level of this request. Selects procedures to abort.
 * @param[out]  p_result  Result of entering the sleep state.
 */
static void swi_sleep(nrf_802154_term_t term_lvl, bool * p_result)
{
    nrf_802154_req_data_t * p_slot = req_enter();

    p_slot->type                = REQ_TYPE_SLEEP;
    p_slot->data.sleep.term_lvl = term_lvl;
    p_slot->data.sleep.p_result = p_result;

    req_exit();
}

/**
 * @brief Requests entering the @ref RADIO_STATE_RX state from the SWI priority.
 *
 * @param[in]   term_lvl         Termination level of this request. Selects procedures to abort.
 * @param[in]   req_orig         Module that originates this request.
 * @param[in]   notify_function  Function called to notify the status of the procedure. May be NULL.
 * @param[in]   notify_abort     If abort notification should be triggered automatically.
 * @param[in]   id               Identifier of the reception window.
 * @param[out]  p_result         Result of entering the receive state.
 */
static void swi_receive(nrf_802154_term_t              term_lvl,
                        req_originator_t               req_orig,
                        nrf_802154_notification_func_t notify_function,
                        bool                           notify_abort,
                        uint32_t                       id,
                        bool                         * p_result)
{
    nrf_802154_req_data_t * p_slot = req_enter();

    p_slot->type                     = REQ_TYPE_RECEIVE;
    p_slot->data.receive.term_lvl    = term_lvl;
    p_slot->data.receive.req_orig    = req_orig;
    p_slot->data.receive.notif_func  = notify_function;
    p_slot->data.receive.notif_abort = notify_abort;
    p_slot->data.receive.id          = id;
    p_slot->data.receive.p_result    = p_result;

    req_exit();
}

/**
 * @biref Requests entering the @ref RADIO_STATE_TX state from the SWI priority.
 *
 * @param[in]   term_lvl         Termination level of this request. Selects procedures to abort.
 * @param[in]   req_orig         Module that originates this request.
 * @param[in]   p_data           Pointer to a buffer that contains PHR and PSDU of the frame to be
 *                               transmitted.
 * @param[in]   p_params         Pointer to transmission parameters.
 * @param[in]   notify_function  Function called to notify the status of this procedure instead of
 *                               the default notification. If NULL, the default notification
 *                               is used.
 * @param[out]  p_result         Result of entering the transmit state.
 */
static void swi_transmit(nrf_802154_term_t              term_lvl,
                         req_originator_t               req_orig,
                         uint8_t                      * p_data,
                         nrf_802154_transmit_params_t * p_params,
                         nrf_802154_notification_func_t notify_function,
                         bool                         * p_result)
{
    nrf_802154_req_data_t * p_slot = req_enter();

    p_slot->type                     = REQ_TYPE_TRANSMIT;
    p_slot->data.transmit.term_lvl   = term_lvl;
    p_slot->data.transmit.req_orig   = req_orig;
    p_slot->data.transmit.p_data     = p_data;
    p_slot->data.transmit.p_params   = p_params;
    p_slot->data.transmit.notif_func = notify_function;
    p_slot->data.transmit.p_result   = p_result;

    req_exit();
}

/**
 * @brief Requests entering the @ref RADIO_STATE_ED state from the SWI priority.
 *
 * @param[in]   term_lvl  Termination level of this request. Selects procedures to abort.
 * @param[in]   time_us   Requested duration of the energy detection procedure.
 * @param[out]  p_result  Result of entering the energy detection state.
 */
static void swi_energy_detection(nrf_802154_term_t term_lvl,
                                 uint32_t          time_us,
                                 bool            * p_result)
{
    nrf_802154_req_data_t * p_slot = req_enter();

    p_slot->type                           = REQ_TYPE_ENERGY_DETECTION;
    p_slot->data.energy_detection.term_lvl = term_lvl;
    p_slot->data.energy_detection.time_us  = time_us;
    p_slot->data.energy_detection.p_result = p_result;

    req_exit();
}

/**
 * @brief Requests entering the @ref RADIO_STATE_CCA state from the SWI priority.
 *
 * @param[in]   term_lvl  Termination level of this request. Selects procedures to abort.
 * @param[out]  p_result  Result of entering the CCA state.
 */
static void swi_cca(nrf_802154_term_t term_lvl, bool * p_result)
{
    nrf_802154_req_data_t * p_slot = req_enter();

    p_slot->type              = REQ_TYPE_CCA;
    p_slot->data.cca.term_lvl = term_lvl;
    p_slot->data.cca.p_result = p_result;

    req_exit();
}

#if NRF_802154_CARRIER_FUNCTIONS_ENABLED

/**
 * @brief Requests entering the @ref RADIO_STATE_CONTINUOUS_CARRIER state from the SWI priority.
 *
 * @param[in]   term_lvl  Termination level of this request. Selects procedures to abort.
 * @param[out]  p_result  Result of entering the continuous carrier state.
 */
static void swi_continuous_carrier(nrf_802154_term_t term_lvl, bool * p_result)
{
    nrf_802154_req_data_t * p_slot = req_enter();

    p_slot->type                             = REQ_TYPE_CONTINUOUS_CARRIER;
    p_slot->data.continuous_carrier.term_lvl = term_lvl;
    p_slot->data.continuous_carrier.p_result = p_result;

    req_exit();
}

/**
 * @brief Requests entering the @ref RADIO_STATE_MODULATED_CARRIER state from the SWI priority.
 *
 * @param[in]   term_lvl  Termination level of this request. Selects procedures to abort.
 * @param[in]   p_data    Pointer to the data buffer to modulate the carrier wave with.
 * @param[out]  p_result  Result of entering the modulated carrier state.
 */
static void swi_modulated_carrier(nrf_802154_term_t term_lvl,
                                  const uint8_t   * p_data,
                                  bool            * p_result)
{
    nrf_802154_req_data_t * p_slot = req_enter();

    p_slot->type                            = REQ_TYPE_MODULATED_CARRIER;
    p_slot->data.modulated_carrier.term_lvl = term_lvl;
    p_slot->data.modulated_carrier.p_data   = p_data;
    p_slot->data.modulated_carrier.p_result = p_result;

    req_exit();
}

#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED

/**
 * @brief Notifies the core module that the given buffer is not used anymore and can be freed.
 *
 * @param[in]  p_data   Pointer to the buffer to be freed.
 * @param[out] p_result Pointer where the result to be returned by
 *                      nrf_802154_request_buffer_free should be written by the swi handler.
 */
static void swi_buffer_free(uint8_t * p_data, bool * p_result)
{
    nrf_802154_req_data_t * p_slot = req_enter();

    p_slot->type                      = REQ_TYPE_BUFFER_FREE;
    p_slot->data.buffer_free.p_data   = p_data;
    p_slot->data.buffer_free.p_result = p_result;

    req_exit();
}

/**
 * @brief Notifies the core module that the next higher layer has requested an antenna change.
 *
 * @param[out] p_result Pointer where the result to be returned by
 *                      nrf_802154_request_antenna_update should be written by the swi handler.
 */
static void swi_antenna_update(bool * p_result)
{
    nrf_802154_req_data_t * p_slot = req_enter();

    p_slot->type                         = REQ_TYPE_ANTENNA_UPDATE;
    p_slot->data.antenna_update.p_result = p_result;

    req_exit();
}

/**
 * @brief Notifies the core module that the next higher layer has requested a channel change.
 *
 * @param[out] p_result Pointer where the result to be returned by
 *                      nrf_802154_request_channel_update should be written by the swi handler.
 */
static void swi_channel_update(req_originator_t req_orig, bool * p_result)
{
    nrf_802154_req_data_t * p_slot = req_enter();

    p_slot->type                         = REQ_TYPE_CHANNEL_UPDATE;
    p_slot->data.channel_update.p_result = p_result;
    p_slot->data.channel_update.req_orig = req_orig;

    req_exit();
}

/**
 * @brief Notifies the core module that the next higher layer has requested a CCA configuration
 * change.
 * @param[out] p_result Pointer where the result to be returned by
 *                      nrf_802154_request_cca_cfg_update should be written by the swi handler.
 */
static void swi_cca_cfg_update(bool * p_result)
{
    nrf_802154_req_data_t * p_slot = req_enter();

    p_slot->type                         = REQ_TYPE_CCA_CFG_UPDATE;
    p_slot->data.cca_cfg_update.p_result = p_result;

    req_exit();
}

/**
 * @brief Notifies the core module that the next higher layer requested the RSSI measurement.
 *
 * @param[out] p_result Pointer where the result to be returned by
 *                      nrf_802154_request_rssi_measure should be written by the swi handler.
 */
static void swi_rssi_measure(bool * p_result)
{
    nrf_802154_req_data_t * p_slot = req_enter();

    p_slot->type                       = REQ_TYPE_RSSI_MEASURE;
    p_slot->data.rssi_measure.p_result = p_result;

    req_exit();
}

/**
 * @brief Gets the last RSSI measurement result from the core module.
 *
 * @param[out] p_rssi   Pointer where RSSI measurement value will be stored.
 * @param[out] p_result Pointer where the result to be returned by
 *                      nrf_802154_request_rssi_measurement_get should be written by the swi handler.
 */
static void swi_rssi_measurement_get(int8_t * p_rssi, bool * p_result)
{
    nrf_802154_req_data_t * p_slot = req_enter();

    p_slot->type                   = REQ_TYPE_RSSI_GET;
    p_slot->data.rssi_get.p_rssi   = p_rssi;
    p_slot->data.rssi_get.p_result = p_result;

    req_exit();
}

#if NRF_802154_DELAYED_TRX_ENABLED
static void swi_transmit_at(uint8_t                                 * p_data,
                            uint64_t                                  tx_time,
                            const nrf_802154_transmit_at_metadata_t * p_metadata,
                            bool                                    * p_result)
{
    nrf_802154_req_data_t * p_slot = req_enter();

    p_slot->type                        = REQ_TYPE_TRANSMIT_AT;
    p_slot->data.transmit_at.p_data     = p_data;
    p_slot->data.transmit_at.tx_time    = tx_time;
    p_slot->data.transmit_at.p_metadata = p_metadata;
    p_slot->data.transmit_at.p_result   = p_result;

    req_exit();
}

static void swi_transmit_at_cancel(bool * p_result)
{
    nrf_802154_req_data_t * p_slot = req_enter();

    p_slot->type                             = REQ_TYPE_TRANSMIT_AT_CANCEL;
    p_slot->data.transmit_at_cancel.p_result = p_result;

    req_exit();
}

static void swi_receive_at(uint64_t rx_time,
                           uint32_t timeout,
                           uint8_t  channel,
                           uint32_t id,
                           bool   * p_result)
{
    nrf_802154_req_data_t * p_slot = req_enter();

    p_slot->type                     = REQ_TYPE_RECEIVE_AT;
    p_slot->data.receive_at.rx_time  = rx_time;
    p_slot->data.receive_at.timeout  = timeout;
    p_slot->data.receive_at.channel  = channel;
    p_slot->data.receive_at.id       = id;
    p_slot->data.receive_at.p_result = p_result;

    req_exit();
}

static void swi_receive_at_cancel(uint32_t id, bool * p_result)
{
    nrf_802154_req_data_t * p_slot = req_enter();

    p_slot->type                            = REQ_TYPE_RECEIVE_AT_CANCEL;
    p_slot->data.receive_at_cancel.id       = id;
    p_slot->data.receive_at_cancel.p_result = p_result;

    req_exit();
}

#endif // NRF_802154_DELAYED_TRX_ENABLED

static void swi_csma_ca_start(uint8_t                                      * p_data,
                              const nrf_802154_transmit_csma_ca_metadata_t * p_metadata,
                              bool                                         * p_result)
{
    nrf_802154_req_data_t * p_slot = req_enter();

    p_slot->type                          = REQ_TYPE_CSMA_CA_START;
    p_slot->data.csma_ca_start.p_data     = p_data;
    p_slot->data.csma_ca_start.p_metadata = p_metadata;
    p_slot->data.csma_ca_start.p_result   = p_result;
    req_exit();
}

void nrf_802154_request_init(void)
{
    nrf_802154_queue_init(&m_requests_queue,
                          m_requests_queue_memory,
                          sizeof(m_requests_queue_memory),
                          sizeof(m_requests_queue_memory[0]));

    nrf_egu_int_enable(NRF_802154_EGU_INSTANCE, REQ_INT);

    nrf_802154_swi_init();
}

bool nrf_802154_request_sleep(nrf_802154_term_t term_lvl)
{
    REQUEST_FUNCTION(nrf_802154_core_sleep, swi_sleep, term_lvl)
}

bool nrf_802154_request_receive(nrf_802154_term_t              term_lvl,
                                req_originator_t               req_orig,
                                nrf_802154_notification_func_t notify_function,
                                bool                           notify_abort,
                                uint32_t                       id)
{
    REQUEST_FUNCTION(nrf_802154_core_receive,
                     swi_receive,
                     term_lvl,
                     req_orig,
                     notify_function,
                     notify_abort,
                     id)
}

bool nrf_802154_request_transmit(nrf_802154_term_t              term_lvl,
                                 req_originator_t               req_orig,
                                 uint8_t                      * p_data,
                                 nrf_802154_transmit_params_t * p_params,
                                 nrf_802154_notification_func_t notify_function)
{
    REQUEST_FUNCTION(nrf_802154_core_transmit,
                     swi_transmit,
                     term_lvl,
                     req_orig,
                     p_data,
                     p_params,
                     notify_function)
}

bool nrf_802154_request_energy_detection(nrf_802154_term_t term_lvl,
                                         uint32_t          time_us)
{
    REQUEST_FUNCTION(nrf_802154_core_energy_detection,
                     swi_energy_detection,
                     term_lvl,
                     time_us)
}

bool nrf_802154_request_cca(nrf_802154_term_t term_lvl)
{
    REQUEST_FUNCTION(nrf_802154_core_cca, swi_cca, term_lvl)
}

#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
bool nrf_802154_request_continuous_carrier(nrf_802154_term_t term_lvl)
{
    REQUEST_FUNCTION(nrf_802154_core_continuous_carrier,
                     swi_continuous_carrier,
                     term_lvl)
}

bool nrf_802154_request_modulated_carrier(nrf_802154_term_t term_lvl,
                                          const uint8_t   * p_data)
{
    REQUEST_FUNCTION(nrf_802154_core_modulated_carrier,
                     swi_modulated_carrier,
                     term_lvl,
                     p_data)
}

#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED

bool nrf_802154_request_buffer_free(uint8_t * p_data)
{
    REQUEST_FUNCTION(nrf_802154_core_notify_buffer_free, swi_buffer_free, p_data)
}

bool nrf_802154_request_antenna_update(void)
{
    REQUEST_FUNCTION_NO_ARGS(nrf_802154_core_antenna_update, swi_antenna_update)
}

bool nrf_802154_request_channel_update(req_originator_t req_orig)
{
    REQUEST_FUNCTION(nrf_802154_core_channel_update, swi_channel_update, req_orig)
}

bool nrf_802154_request_cca_cfg_update(void)
{
    REQUEST_FUNCTION_NO_ARGS(nrf_802154_core_cca_cfg_update, swi_cca_cfg_update)
}

bool nrf_802154_request_rssi_measure(void)
{
    REQUEST_FUNCTION_NO_ARGS(nrf_802154_core_rssi_measure, swi_rssi_measure)
}

bool nrf_802154_request_rssi_measurement_get(int8_t * p_rssi)
{
    REQUEST_FUNCTION(nrf_802154_core_last_rssi_measurement_get,
                     swi_rssi_measurement_get,
                     p_rssi)
}

#if NRF_802154_DELAYED_TRX_ENABLED
bool nrf_802154_request_transmit_raw_at(uint8_t                                 * p_data,
                                        uint64_t                                  tx_time,
                                        const nrf_802154_transmit_at_metadata_t * p_metadata)
{
    REQUEST_FUNCTION(nrf_802154_delayed_trx_transmit, swi_transmit_at, p_data, tx_time, p_metadata);
}

bool nrf_802154_request_transmit_at_cancel(void)
{
    REQUEST_FUNCTION_NO_ARGS(nrf_802154_delayed_trx_transmit_cancel, swi_transmit_at_cancel);
}

bool nrf_802154_request_receive_at(uint64_t rx_time,
                                   uint32_t timeout,
                                   uint8_t  channel,
                                   uint32_t id)
{
    REQUEST_FUNCTION(nrf_802154_delayed_trx_receive, swi_receive_at, rx_time, timeout, channel, id);
}

bool nrf_802154_request_receive_at_cancel(uint32_t id)
{
    REQUEST_FUNCTION(nrf_802154_delayed_trx_receive_cancel, swi_receive_at_cancel, id);
}

bool nrf_802154_request_csma_ca_start(uint8_t                                      * p_data,
                                      const nrf_802154_transmit_csma_ca_metadata_t * p_metadata)
{
    REQUEST_FUNCTION(nrf_802154_csma_ca_start,
                     swi_csma_ca_start,
                     p_data,
                     p_metadata);
}

#endif // NRF_802154_DELAYED_TRX_ENABLED

/**@brief Handles REQ_EVENT on NRF_802154_EGU_INSTANCE */
static void irq_handler_req_event(void)
{
    while (!nrf_802154_queue_is_empty(&m_requests_queue))
    {
        nrf_802154_req_data_t * p_slot =
            (nrf_802154_req_data_t *)nrf_802154_queue_pop_begin(&m_requests_queue);

        switch (p_slot->type)
        {
            case REQ_TYPE_SLEEP:
                *(p_slot->data.sleep.p_result) =
                    nrf_802154_core_sleep(p_slot->data.sleep.term_lvl);
                break;

            case REQ_TYPE_RECEIVE:
                *(p_slot->data.receive.p_result) =
                    nrf_802154_core_receive(p_slot->data.receive.term_lvl,
                                            p_slot->data.receive.req_orig,
                                            p_slot->data.receive.notif_func,
                                            p_slot->data.receive.notif_abort,
                                            p_slot->data.receive.id);
                break;

            case REQ_TYPE_TRANSMIT:
                *(p_slot->data.transmit.p_result) =
                    nrf_802154_core_transmit(p_slot->data.transmit.term_lvl,
                                             p_slot->data.transmit.req_orig,
                                             p_slot->data.transmit.p_data,
                                             p_slot->data.transmit.p_params,
                                             p_slot->data.transmit.notif_func);
                break;

            case REQ_TYPE_ENERGY_DETECTION:
                *(p_slot->data.energy_detection.p_result) =
                    nrf_802154_core_energy_detection(
                        p_slot->data.energy_detection.term_lvl,
                        p_slot->data.energy_detection.time_us);
                break;

            case REQ_TYPE_CCA:
                *(p_slot->data.cca.p_result) = nrf_802154_core_cca(p_slot->data.cca.term_lvl);
                break;

#if NRF_802154_CARRIER_FUNCTIONS_ENABLED

            case REQ_TYPE_CONTINUOUS_CARRIER:
                *(p_slot->data.continuous_carrier.p_result) =
                    nrf_802154_core_continuous_carrier(
                        p_slot->data.continuous_carrier.term_lvl);
                break;

            case REQ_TYPE_MODULATED_CARRIER:
                *(p_slot->data.modulated_carrier.p_result) =
                    nrf_802154_core_modulated_carrier(p_slot->data.modulated_carrier.term_lvl,
                                                      p_slot->data.modulated_carrier.p_data);
                break;

#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED

            case REQ_TYPE_BUFFER_FREE:
                *(p_slot->data.buffer_free.p_result) =
                    nrf_802154_core_notify_buffer_free(p_slot->data.buffer_free.p_data);
                break;

            case REQ_TYPE_CHANNEL_UPDATE:
                *(p_slot->data.channel_update.p_result) =
                    nrf_802154_core_channel_update(p_slot->data.channel_update.req_orig);
                break;

            case REQ_TYPE_CCA_CFG_UPDATE:
                *(p_slot->data.cca_cfg_update.p_result) = nrf_802154_core_cca_cfg_update();
                break;

            case REQ_TYPE_RSSI_MEASURE:
                *(p_slot->data.rssi_measure.p_result) = nrf_802154_core_rssi_measure();
                break;

            case REQ_TYPE_RSSI_GET:
                *(p_slot->data.rssi_get.p_result) =
                    nrf_802154_core_last_rssi_measurement_get(p_slot->data.rssi_get.p_rssi);
                break;

            case REQ_TYPE_ANTENNA_UPDATE:
                *(p_slot->data.antenna_update.p_result) = nrf_802154_core_antenna_update();
                break;

#if NRF_802154_DELAYED_TRX_ENABLED
            case REQ_TYPE_TRANSMIT_AT:
                *(p_slot->data.transmit_at.p_result) =
                    nrf_802154_delayed_trx_transmit(p_slot->data.transmit_at.p_data,
                                                    p_slot->data.transmit_at.tx_time,
                                                    p_slot->data.transmit_at.p_metadata);
                break;

            case REQ_TYPE_TRANSMIT_AT_CANCEL:
                *(p_slot->data.transmit_at_cancel.p_result) =
                    nrf_802154_delayed_trx_transmit_cancel();
                break;

            case REQ_TYPE_RECEIVE_AT:
                *(p_slot->data.receive_at.p_result) =
                    nrf_802154_delayed_trx_receive(p_slot->data.receive_at.rx_time,
                                                   p_slot->data.receive_at.timeout,
                                                   p_slot->data.receive_at.channel,
                                                   p_slot->data.receive_at.id);
                break;

            case REQ_TYPE_RECEIVE_AT_CANCEL:
                *(p_slot->data.receive_at_cancel.p_result) =
                    nrf_802154_delayed_trx_receive_cancel(p_slot->data.receive_at_cancel.id);
                break;

            case REQ_TYPE_CSMA_CA_START:
                *(p_slot->data.receive_at_cancel.p_result) =
                    nrf_802154_csma_ca_start(p_slot->data.csma_ca_start.p_data,
                                             p_slot->data.csma_ca_start.p_metadata);
                break;
#endif // NRF_802154_DELAYED_TRX_ENABLED

            default:
                assert(false);
        }

        nrf_802154_queue_pop_commit(&m_requests_queue);
    }
}

void nrf_802154_request_swi_irq_handler(void)
{
    if (nrf_egu_event_check(NRF_802154_EGU_INSTANCE, REQ_EVENT))
    {
        nrf_egu_event_clear(NRF_802154_EGU_INSTANCE, REQ_EVENT);

        irq_handler_req_event();
    }
}
