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

#ifndef NRF_802154_SL_RSCH_H__
#define NRF_802154_SL_RSCH_H__

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

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @defgroup nrf_802154_sl_rsch Resource Scheduler Service
 * @{
 * @ingroup nrf_802154_sl_rsch
 * @brief The Resource Scheduler Service for the 802.15.4 Service Layer
 */

#define NRF_802154_SL_RSCH_TS_PRIORITY_LOWEST 0U   /**< Lowest possible priority of a timeslot. */
#define NRF_802154_SL_RSCH_SESSION_PRIV_SIZE  1U   /**< Number of words needed for private fields of a session. */
#define NRF_802154_SL_RSCH_DEFAULT_MARGIN_US  100U /**< Default margin in microseconds. Refer to @ref nrf_802154_sl_rsch_ts_param_t. */

/** @brief Scheduler's return codes.
 *
 * Possible values:
 * @ref NRF_802154_SL_RSCH_RET_SUCCESS
 * @ref NRF_802154_SL_RSCH_RET_ALREADY_REQUESTED
 * @ref NRF_802154_SL_RSCH_RET_INVALID_PARAM
 * @ref NRF_802154_SL_RSCH_RET_HIGHER_PRIO
 * @ref NRF_802154_SL_RSCH_RET_RSRC_DENIED
 * @ref NRF_802154_SL_RSCH_RET_TIMESLOT_INACTIVE
 * @ref NRF_802154_SL_RSCH_RET_DURATION_TIMEOUT
 * @ref NRF_802154_SL_RSCH_RET_NO_MEMORY

 */
typedef uint32_t nrf_802154_sl_rsch_ret_t;

#define NRF_802154_SL_RSCH_RET_SUCCESS           0U /**< Operation finished successfully. */
#define NRF_802154_SL_RSCH_RET_ALREADY_REQUESTED 1U /**< Operation failed because there is an already requested timeslot. */
#define NRF_802154_SL_RSCH_RET_INVALID_PARAM     2U /**< Operation failed because the provided parameters are invalid. */
#define NRF_802154_SL_RSCH_RET_HIGHER_PRIO       3U /**< Operation failed because of a higher priority request that overlaps in time. */
#define NRF_802154_SL_RSCH_RET_RSRC_DENIED       4U /**< Operation failed because of a denied resource. */
#define NRF_802154_SL_RSCH_RET_TIMESLOT_INACTIVE 5U /**< Operation failed because the timeslot's state does not allow it. */
#define NRF_802154_SL_RSCH_RET_DURATION_TIMEOUT  6U /**< Operation finished because its requested duration expired. */
#define NRF_802154_SL_RSCH_RET_NO_MEMORY         7U /**< Operation failed because there was no memory to complete it. */

/** @brief Flags indicating resources that can be accessed in a timeslot.
 *
 * This type is a mask of bits that represent access to different resources that may be necessary
 * for successful operation inside a timeslot.
 *
 * Possible masks:
 * @ref NRF_802154_SL_RSCH_RSRC_NONE
 * @ref NRF_802154_SL_RSCH_RSRC_RADIO
 * @ref NRF_802154_SL_RSCH_RSRC_RF
 */
typedef uint32_t nrf_802154_sl_rsch_resource_t;

#define NRF_802154_SL_RSCH_RSRC_NONE  0U       /**< Indicates that no resources are required. */
#define NRF_802154_SL_RSCH_RSRC_RADIO (1 << 0) /**< Indicates that exclusive access to RADIO peripheral is required. */
#define NRF_802154_SL_RSCH_RSRC_RF    (1 << 1) /**< Indicates that exclusive access to radio wave medium is required. */

/** @brief Types 802.15.4 operations.
 *
 * Possible values:
 * @ref NRF_802154_SL_RSCH_OP_NONE
 * @ref NRF_802154_SL_RSCH_OP_LISTEN
 * @ref NRF_802154_SL_RSCH_OP_RX
 * @ref NRF_802154_SL_RSCH_OP_TX
 */
typedef uint32_t nrf_802154_sl_rsch_operation_t;

#define NRF_802154_SL_RSCH_OP_NONE   0U /**< Non-802.15.4 related operation. */
#define NRF_802154_SL_RSCH_OP_LISTEN 1U /**< 802.15.4 idle listening. */
#define NRF_802154_SL_RSCH_OP_RX     2U /**< 802.15.4 active reception. */
#define NRF_802154_SL_RSCH_OP_TX     3U /**< 802.15.4 transmission. */

/** @brief Structure that represents private fields of a session required by the implementation. */
typedef struct
{
    uint32_t placeholder[NRF_802154_SL_RSCH_SESSION_PRIV_SIZE];
} nrf_802154_sl_rsch_session_priv_placeholder_t;

/** @brief Structure that represents the parameters of a session. */
typedef struct
{
    nrf_802154_sl_rsch_session_priv_placeholder_t priv;
} nrf_802154_sl_rsch_session_t;

/** @brief Structure that holds metadata passed in @ref nrf_802154_sl_rsch_ts_start_callback_t.
 */
typedef struct
{
    /** @brief Identifier of the started timeslot. */
    uint32_t                       ts_id;

    /** @brief Time in microseconds that the started timeslot is expected to last. */
    uint32_t                       duration_us;

    /** @brief Pointer to session that the started timeslot belongs to. */
    nrf_802154_sl_rsch_session_t * p_session;

} nrf_802154_sl_rsch_ts_start_callback_metadata_t;

/** @brief Structure that holds metadata passed in @ref nrf_802154_sl_rsch_ts_end_callback_t.
 */
typedef struct
{
    /** @brief Identifier of the ending timeslot. */
    uint32_t                 ts_id;

    /** @brief Return code that informs about the reason of timeslot end. */
    nrf_802154_sl_rsch_ret_t ret;

    /** @brief Pointer to resources that are to be handed over and do not require deconfiguration.
     *
     * When a timeslot is about to end, it's possible that there are other timeslots scheduled to
     * occur immediately after it such that their required resources overlap with the resources
     * requested by the ending timeslot. In order to avoid unnecessary deconfiguration
     * and reconfiguration of resources that are continuously granted and required by consecutive
     * timeslots, the scheduler fills this field with resources that need not be deconfigured
     * upon the timeslot end so that they can be handed over to the following session. All remaining
     * resources requested by the session must be deconfigured completely before
     * @ref nrf_802154_sl_rsch_ts_release is called.
     *
     * @note If this field is set to NULL, all resources requested upon request must be deconfigured
     * completely before @ref nrf_802154_sl_rsch_ts_release is called.
     */
    nrf_802154_sl_rsch_resource_t * p_rsrc;

    /** @brief Pointer to session the ending timeslot belongs to. */
    nrf_802154_sl_rsch_session_t  * p_session;

} nrf_802154_sl_rsch_ts_end_callback_metadata_t;

/** @brief Structure that holds metadata passed in @ref nrf_802154_sl_rsch_ts_cancelled_callback_t. */
typedef struct
{
    /** @brief Identifier of the cancelled timeslot. */
    uint32_t                       ts_id;

    /** @brief Return code that informs about the reason of timeslot cancellation. */
    nrf_802154_sl_rsch_ret_t       ret;

    /** @brief Pointer to session the cancelled timeslot belongs to. */
    nrf_802154_sl_rsch_session_t * p_session;

} nrf_802154_sl_rsch_ts_cancelled_callback_metadata_t;

/** @brief Structure that holds metadata passed in @ref nrf_802154_sl_rsch_ts_cleanup_callback_t. */
typedef struct
{
    /** @brief Identifier of the timeslot that should be cleaned up. */
    uint32_t                        ts_id;

    /** @brief Pointer to resources that ought to be deconfigured. */
    nrf_802154_sl_rsch_resource_t * p_rsrc;

    /** @brief Pointer to session the timeslot belongs to. */
    nrf_802154_sl_rsch_session_t  * p_session;

} nrf_802154_sl_rsch_ts_cleanup_callback_metadata_t;

/** @brief Function called by the scheduler to indicate that a requested timeslot has started.
 *
 * After this function is called, the session has exclusive access to the resources specified
 * upon request.
 *
 * @param[in]  p_context   Pointer to data provided by the user.
 * @param[in]  p_metadata  Pointer to data containing information regarding the started timeslot.
 */
typedef void (* nrf_802154_sl_rsch_ts_start_callback_t)(
    void * p_context, nrf_802154_sl_rsch_ts_start_callback_metadata_t * p_metadata);

/** @brief Function called by the scheduler to indicate that a requested and successfully started
 *         timeslot is about to end.
 *
 * The scheduler issues this call to occur when only a period of time specified upon timeslot
 * request is left until the expected end of the timeslot. The call can be a result of a number
 * of events, e.g. a preemption performed by a higher priority request, required resource denied
 * by an external arbiter or timeout of the specified duration of the timeslot. After this function
 * is called, the session is responsible for releasing exclusive access to the requested resources
 * by calling @ref nrf_802154_sl_rsch_ts_release. Failing to do so in the specified time margin
 * might result in blocking other 802.15.4 operations as well as other radio protocols.
 *
 * @param[in]  p_context   Pointer to data provided by the user.
 * @param[in]  p_metadata  Pointer to data containing information regarding the ending timeslot.
 */
typedef void (* nrf_802154_sl_rsch_ts_end_callback_t)(
    void * p_context, nrf_802154_sl_rsch_ts_end_callback_metadata_t * p_metadata);

/** @brief Function called by the scheduler to indicate that a requested and not yet started
 *         timeslot was cancelled by the scheduler.
 *
 * This function is called when a successful request from the past is cancelled by the scheduler
 * before the requested timeslot is started. The call can be a result of a higher priority timeslot
 * requested for the same period of time or a required resource certain to be unavailable
 * at the specified period of time.
 *
 * @param[in]  p_context   Pointer to data provided by the user.
 * @param[in]  p_metadata  Pointer to data containing information regarding the cancelled timeslot.
 */
typedef void (* nrf_802154_sl_rsch_ts_cancelled_callback_t)(
    void * p_context, nrf_802154_sl_rsch_ts_cancelled_callback_metadata_t * p_metadata);

/** @brief Function called by the scheduler to indicate that a requested and not yet started
 *         timeslot requires deconfiguration of resources that were handed over to it.
 *
 * It might happen that a timeslot had had its resources handed over, but they became unavailable
 * before the timeslot could start. Although the timeslot never starts, it must deconfigure
 * the resources that were handed over to it and that were denied by the scheduler. Therefore
 * the session gains exclusive access to the resources it requested, but only to deconfigure
 * those of them that are not granted anymore. After this function is called, the session
 * is responsible for releasing exclusive access to the requested resources by calling
 * @ref nrf_802154_sl_rsch_ts_release. Failing to do so in the specified time margin might result
 * in blocking other 802.15.4 operations as well as other radio protocols.
 */
typedef void (* nrf_802154_sl_rsch_ts_cleanup_callback_t)(
    void * p_context, nrf_802154_sl_rsch_ts_cleanup_callback_metadata_t * p_metadata);

/** @brief Structure that represents the parameters of a timeslot. */
typedef struct
{
    /** @brief Resources necessary for the operations inside the timeslot to be successful.
     */
    nrf_802154_sl_rsch_resource_t rsrc;

    /** @brief Type of the operation to be performed inside the timeslot.
     */
    nrf_802154_sl_rsch_operation_t op;

    /** @brief Function called by the scheduler to indicate that timeslot has started.
     */
    nrf_802154_sl_rsch_ts_start_callback_t start;

    /** @brief Function called by the scheduler to indicate that timeslot is about to end.
     */
    nrf_802154_sl_rsch_ts_end_callback_t end;

    /** @brief Function called by the scheduler to indicate that timeslot was cancelled.
     */
    nrf_802154_sl_rsch_ts_cancelled_callback_t cancelled;

    /** @brief Pointer to data provided by the caller that is unmodified by the scheduler
     *         and passed back to the caller in callbacks.
     */
    void * p_context;

    /** @brief Flag that indicates if timeslot should be started immediately.
     */
    bool immediate;

    /** @brief Absolute time in microseconds that the timeslot should start in.
     *
     * @note This field is used only if @p immediate is false.
     */
    uint32_t target_time;

    /** @brief Requested time in microseconds that the timeslot is expected to last.
     *
     * If the timeslot's priority is set to the lowest priority possible, setting this field
     * to 0 makes the timeslot last for an indefinite period of time. Such timeslot finished
     * with a @ref nrf_802154_sl_rsch_ts_release call or when its end callback is called.
     *
     * @note If this field is set to 0 when the timeslot's priority is not the lowest possible,
     * @ref nrf_802154_sl_rsch_ts_request returns @ref NRF_802154_SL_RSCH_RET_INVALID_PARAM
     * and the timeslot is not scheduled.
     */
    uint32_t duration_us;

    /** @brief Minimum time in microseconds that the timeslot can last to serve its purpose.
     *
     * If the caller wishes to allocate timeslot shorter than defined by @p duration_us
     * that can be started earlier, this field should be set to a value indicating minimum
     * duration of the timeslot that can be utilized by the caller. If the caller cannot
     * utilize shorter timeslots than requested, this field shall be set to 0.
     */
    uint32_t min_duration_us;

    /** @brief Minimum time in microseconds that a session needs to clean up after their timeslot.
     *
     * When a timeslot is about to be finished, @p end callback is issued to the session that
     * requested it. This parameter specifies the minimum time that the session might need to
     * perform all actions that need to be done before releasing the timeslot with
     * @ref nrf_802154_sl_rsch_ts_release.
     */
    uint32_t margin_us;

    /** @brief Priority of the timeslot.
     *
     * The lowest possible priority of a timeslot is equal @ref NRF_802154_SL_RSCH_TS_PRIORITY_LOWEST.
     * Any number larger than that value indicates a higher priority.
     */
    uint8_t priority;

} nrf_802154_sl_rsch_ts_param_t;

/** @brief Structure that represents timeslot parameters that can be updated. */
typedef struct
{
    /** @brief Resources necessary for the operations inside the timeslot to be successful.
     */
    nrf_802154_sl_rsch_resource_t rsrc;

    /** @brief Type of the operation to be performed inside the timeslot.
     */
    nrf_802154_sl_rsch_operation_t op;

    /** @brief Requested time in microseconds that the timeslot is expected to last.
     */
    uint32_t duration_us;

    /** @brief Priority of the timeslot.
     *
     * The lowest possible priority of a timeslot is equal @ref NRF_802154_SL_RSCH_TS_PRIORITY_LOWEST.
     * Any number larger than that value indicates a higher priority.
     */
    uint8_t priority;

} nrf_802154_sl_rsch_ts_update_param_t;

/** @brief Initializes the scheduler.
 *
 * @note This function must be called once, before any other function from this module.
 *
 * @note No timeslots are requested after initialization. In order to start radio activity,
 *       @ref nrf_802154_sl_rsch_ts_request should be called.
 */
void nrf_802154_sl_rsch_init(void);

/** @brief Deinitializes the scheduler. */
void nrf_802154_sl_rsch_deinit(void);

/** @brief Initializes a session.
 *
 * @param[out]  p_session   Pointer to a session structure.
 *
 * @retval  NRF_802154_SL_RSCH_RET_SUCCESS           Session was initialized successfully.
 * @retval  NRF_802154_SL_RSCH_RET_ALREADY_REQUESTED Initialization failed. Session was initialized already.
 * @retval  NRF_802154_SL_RSCH_NO_MEMORY             Initialization failed. The amount of available memory
 *                                                   is insufficient.
 */
nrf_802154_sl_rsch_ret_t nrf_802154_sl_rsch_session_init(nrf_802154_sl_rsch_session_t * p_session);

/** @brief Requests a timeslot.
 *
 * @note There can only be a single scheduled timeslot in a given session.
 *
 * When a timeslot is requested successfully and this function returns
 * @ref NRF_802154_SL_RSCH_RET_SUCCESS, the scheduler assigns it a unique (within the provided
 * session) identifier passed to the caller through @p p_ts_id. This identifier must be used
 * in order to update parameters of a timeslot with @ref nrf_802154_sl_rsch_ts_param_update
 * or release it by calling @ref nrf_802154_sl_rsch_ts_release. The identifier is also passed
 * as a parameter of all callbacks registered by the caller upon request.
 *
 * @param[inout] p_session   Pointer to the session that the timeslot belongs to.
 * @param[in]    p_param     Pointer to structure that holds parameters defining the timeslot.
 * @param[out]   p_ts_id     Pointer to the timeslot's identifier within a session.
 *
 * @retval  NRF_802154_SL_RSCH_RET_SUCCESS            Timeslot was requested successfully.
 * @retval  NRF_802154_SL_RSCH_RET_ALREADY_REQUESTED  The specified session already has a timeslot
 *                                                    request pending.
 * @retval  NRF_802154_SL_RSCH_RET_INVALID_PARAM      The provided parameters are invalid.
 * @retval  NRF_802154_SL_RSCH_RET_HIGHER_PRIO        There's a higher priority timeslot scheduled
 *                                                    for an overlapping period of time.
 */
nrf_802154_sl_rsch_ret_t nrf_802154_sl_rsch_ts_request(
    nrf_802154_sl_rsch_session_t        * p_session,
    const nrf_802154_sl_rsch_ts_param_t * p_param,
    uint32_t                            * p_ts_id);

/** @brief Releases a timeslot.
 *
 * Calling this function on behalf of a successfully requested but not yet started timeslot
 * cancels that request. After calling this function the timeslot is not scheduled and is not
 * going to start.
 *
 * Calling this function on behalf of an active (successfully requested and started) or ending
 * (i.e. after end callback was issued) timeslot ends that timeslot. Its resources are released
 * and can be used immediately by other sessions.
 *
 * @note The caller of this API is assumed to act cooperatively. Failing to release a timeslot
 * in the specified time margin after receiving its end callback might lead to blocking other
 * 802.15.4 operations as well as other radio protocols.
 *
 * @param[inout] p_session  Pointer to the session that the timeslot belongs to.
 * @param[in]    ts_id      Identifier of the timeslot to be released.
 *
 * @retval  NRF_802154_SL_RSCH_RET_SUCCESS            The timeslot was released successfully.
 * @retval  NRF_802154_SL_RSCH_RET_TIMESLOT_INACTIVE  The timeslot could not be released because
 *                                                    it has already been released or it was not
 *                                                    requested at all.
 */
nrf_802154_sl_rsch_ret_t nrf_802154_sl_rsch_ts_release(nrf_802154_sl_rsch_session_t * p_session,
                                                       uint32_t                       ts_id);

/** @brief Updates parameters of an active timeslot.
 *
 * This function can be called only to modify parameters of a currently active timeslot,
 * i.e. a timeslot that started and was not yet released. Only a limited subset of all timeslot
 * parameters can be modified when it is already active: its required resources, operation to be
 * performed inside of it, its duration and priority.
 *
 * If there is another active timeslot running in parallel, which is using a separate subset
 * of resources, and the resources passed in this function overlap with that separate subset,
 * the scheduler chooses timeslots based on priority. If the updated priority is higher than
 * the already running parallel timeslot, that timeslot is preempted with an end callback and its
 * resources can be granted to the higher priority updated timeslot. If the priorities are equal,
 * the already running timeslot is preferred and the update fails. If the updated priority is
 * lower than the already running timeslot, the update fails.
 *
 * @note The caller of this API is assumed to act cooperatively. Repeatably extending timeslot's
 * duration and increasing its priority might lead to blocking other 802.15.4 operations as well as
 * other radio protocols.
 *
 * @note If the update is successful, the scheduler notifies the caller about the required resources
 * being granted with a start callback.
 *
 * @note If the update fails, the original timeslot is not ended and its parameters still hold.
 *
 * @note If the update is not carried out before the timeslot ends, the session receives
 * an end callback and it shall release the timeslot immediately.
 *
 * @note Irrespectively of whether the update is successful, the timeslot's identifier is not modified.
 *
 * @param[in]  p_session    Pointer to the session that the timeslot belongs to.
 * @param[in]  p_param      Pointer to structure that holds timeslot parameters that can be updated.
 * @param[in]  ts_id        Identifier of the timeslot to be updated.
 *
 * @retval  NRF_802154_SL_RSCH_RET_SUCCESS            Timeslot's parameters were updated successfully.
 * @retval  NRF_802154_SL_RSCH_RET_HIGHER_PRIO        The update failed because the updated priority
 *                                                    was not high enough to perform it.
 * @retval  NRF_802154_SL_RSCH_RET_TIMESLOT_INACTIVE  The update failed because the timeslot is inactive.
 */
nrf_802154_sl_rsch_ret_t nrf_802154_sl_rsch_ts_param_update(
    const nrf_802154_sl_rsch_session_t         * p_session,
    const nrf_802154_sl_rsch_ts_update_param_t * p_update_param);

/** @brief Initializes timeslot request parameters to default values.
 *
 * @param[out] p_param  Pointer to structure that holds timeslot parameters to be set to default.
 */
static inline void nrf_802154_sl_rsch_ts_param_init(nrf_802154_sl_rsch_ts_param_t * p_param)
{
    p_param->rsrc            = NRF_802154_SL_RSCH_RSRC_NONE;
    p_param->op              = NRF_802154_SL_RSCH_OP_NONE;
    p_param->start           = NULL;
    p_param->end             = NULL;
    p_param->cancelled       = NULL;
    p_param->p_context       = NULL;
    p_param->immediate       = true;
    p_param->target_time     = 0;
    p_param->duration_us     = 0;
    p_param->min_duration_us = 0;
    p_param->margin_us       = NRF_802154_SL_RSCH_DEFAULT_MARGIN_US;
    p_param->priority        = NRF_802154_SL_RSCH_TS_PRIORITY_LOWEST + 1;
}

/**
 *@}
 **/

#ifdef __cplusplus
}
#endif

#endif // NRF_802154_SL_RSCH_H__
