add files from nrf52832 bootloader project
This commit is contained in:
		@@ -0,0 +1,227 @@
 | 
			
		||||
/* Copyright (c) 2012 Nordic Semiconductor. All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * The information contained herein is property of Nordic Semiconductor ASA.
 | 
			
		||||
 * Terms and conditions of usage are described in detail in NORDIC
 | 
			
		||||
 * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensees are granted free, non-transferable use of the information. NO
 | 
			
		||||
 * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
 | 
			
		||||
 * the file.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "app_scheduler.h"
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include "nrf_soc.h"
 | 
			
		||||
#include "nrf_assert.h"
 | 
			
		||||
#include "app_util.h"
 | 
			
		||||
#include "app_util_platform.h"
 | 
			
		||||
 | 
			
		||||
/**@brief Structure for holding a scheduled event header. */
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
    app_sched_event_handler_t handler;          /**< Pointer to event handler to receive the event. */
 | 
			
		||||
    uint16_t                  event_data_size;  /**< Size of event data. */
 | 
			
		||||
} event_header_t;
 | 
			
		||||
 | 
			
		||||
STATIC_ASSERT(sizeof(event_header_t) <= APP_SCHED_EVENT_HEADER_SIZE);
 | 
			
		||||
 | 
			
		||||
static event_header_t * m_queue_event_headers;  /**< Array for holding the queue event headers. */
 | 
			
		||||
static uint8_t        * m_queue_event_data;     /**< Array for holding the queue event data. */
 | 
			
		||||
static volatile uint8_t m_queue_start_index;    /**< Index of queue entry at the start of the queue. */
 | 
			
		||||
static volatile uint8_t m_queue_end_index;      /**< Index of queue entry at the end of the queue. */
 | 
			
		||||
static uint16_t         m_queue_event_size;     /**< Maximum event size in queue. */
 | 
			
		||||
static uint16_t         m_queue_size;           /**< Number of queue entries. */
 | 
			
		||||
 | 
			
		||||
#ifdef APP_SCHEDULER_WITH_PROFILER
 | 
			
		||||
static uint16_t m_max_queue_utilization;    /**< Maximum observed queue utilization. */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/**@brief Function for incrementing a queue index, and handle wrap-around.
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in]   index   Old index.
 | 
			
		||||
 *
 | 
			
		||||
 * @return      New (incremented) index.
 | 
			
		||||
 */
 | 
			
		||||
static __INLINE uint8_t next_index(uint8_t index)
 | 
			
		||||
{
 | 
			
		||||
    return (index < m_queue_size) ? (index + 1) : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static __INLINE uint8_t app_sched_queue_full()
 | 
			
		||||
{
 | 
			
		||||
  uint8_t tmp = m_queue_start_index;
 | 
			
		||||
  return next_index(m_queue_end_index) == tmp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**@brief Macro for checking if a queue is full. */
 | 
			
		||||
#define APP_SCHED_QUEUE_FULL() app_sched_queue_full()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static __INLINE uint8_t app_sched_queue_empty()
 | 
			
		||||
{
 | 
			
		||||
  uint8_t tmp = m_queue_start_index;
 | 
			
		||||
  return m_queue_end_index == tmp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**@brief Macro for checking if a queue is empty. */
 | 
			
		||||
#define APP_SCHED_QUEUE_EMPTY() app_sched_queue_empty()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
uint32_t app_sched_init(uint16_t event_size, uint16_t queue_size, void * p_event_buffer)
 | 
			
		||||
{
 | 
			
		||||
    uint16_t data_start_index = (queue_size + 1) * sizeof(event_header_t);
 | 
			
		||||
 | 
			
		||||
    // Check that buffer is correctly aligned
 | 
			
		||||
    if (!is_word_aligned(p_event_buffer))
 | 
			
		||||
    {
 | 
			
		||||
        return NRF_ERROR_INVALID_PARAM;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Initialize event scheduler
 | 
			
		||||
    m_queue_event_headers = p_event_buffer;
 | 
			
		||||
    m_queue_event_data    = &((uint8_t *)p_event_buffer)[data_start_index];
 | 
			
		||||
    m_queue_end_index     = 0;
 | 
			
		||||
    m_queue_start_index   = 0;
 | 
			
		||||
    m_queue_event_size    = event_size;
 | 
			
		||||
    m_queue_size          = queue_size;
 | 
			
		||||
 | 
			
		||||
#ifdef APP_SCHEDULER_WITH_PROFILER
 | 
			
		||||
    m_max_queue_utilization = 0;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    return NRF_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef APP_SCHEDULER_WITH_PROFILER
 | 
			
		||||
static void queue_utilization_check(void)
 | 
			
		||||
{
 | 
			
		||||
    uint16_t start = m_queue_start_index;
 | 
			
		||||
    uint16_t end   = m_queue_end_index;
 | 
			
		||||
    uint16_t queue_utilization = (end >= start) ? (end - start) :
 | 
			
		||||
        (m_queue_size + 1 - start + end);
 | 
			
		||||
 | 
			
		||||
    if (queue_utilization > m_max_queue_utilization)
 | 
			
		||||
    {
 | 
			
		||||
        m_max_queue_utilization = queue_utilization;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t app_sched_queue_utilization_get(void)
 | 
			
		||||
{
 | 
			
		||||
    return m_max_queue_utilization;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
uint32_t app_sched_event_put(void                    * p_event_data,
 | 
			
		||||
                             uint16_t                  event_data_size,
 | 
			
		||||
                             app_sched_event_handler_t handler)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t err_code;
 | 
			
		||||
 | 
			
		||||
    if (event_data_size <= m_queue_event_size)
 | 
			
		||||
    {
 | 
			
		||||
        uint16_t event_index = 0xFFFF;
 | 
			
		||||
 | 
			
		||||
        CRITICAL_REGION_ENTER();
 | 
			
		||||
 | 
			
		||||
        if (!APP_SCHED_QUEUE_FULL())
 | 
			
		||||
        {
 | 
			
		||||
            event_index       = m_queue_end_index;
 | 
			
		||||
            m_queue_end_index = next_index(m_queue_end_index);
 | 
			
		||||
 | 
			
		||||
        #ifdef APP_SCHEDULER_WITH_PROFILER
 | 
			
		||||
            // This function call must be protected with critical region because
 | 
			
		||||
            // it modifies 'm_max_queue_utilization'.
 | 
			
		||||
            queue_utilization_check();
 | 
			
		||||
        #endif
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CRITICAL_REGION_EXIT();
 | 
			
		||||
 | 
			
		||||
        if (event_index != 0xFFFF)
 | 
			
		||||
        {
 | 
			
		||||
            // NOTE: This can be done outside the critical region since the event consumer will
 | 
			
		||||
            //       always be called from the main loop, and will thus never interrupt this code.
 | 
			
		||||
            m_queue_event_headers[event_index].handler = handler;
 | 
			
		||||
            if ((p_event_data != NULL) && (event_data_size > 0))
 | 
			
		||||
            {
 | 
			
		||||
                memcpy(&m_queue_event_data[event_index * m_queue_event_size],
 | 
			
		||||
                       p_event_data,
 | 
			
		||||
                       event_data_size);
 | 
			
		||||
                m_queue_event_headers[event_index].event_data_size = event_data_size;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                m_queue_event_headers[event_index].event_data_size = 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            err_code = NRF_SUCCESS;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            err_code = NRF_ERROR_NO_MEM;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        err_code = NRF_ERROR_INVALID_LENGTH;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return err_code;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**@brief Function for reading the next event from specified event queue.
 | 
			
		||||
 *
 | 
			
		||||
 * @param[out]  pp_event_data       Pointer to pointer to event data.
 | 
			
		||||
 * @param[out]  p_event_data_size   Pointer to size of event data.
 | 
			
		||||
 * @param[out]  p_event_handler     Pointer to event handler function pointer.
 | 
			
		||||
 *
 | 
			
		||||
 * @return      NRF_SUCCESS if new event, NRF_ERROR_NOT_FOUND if event queue is empty.
 | 
			
		||||
 */
 | 
			
		||||
static uint32_t app_sched_event_get(void                     ** pp_event_data,
 | 
			
		||||
                                    uint16_t *                  p_event_data_size,
 | 
			
		||||
                                    app_sched_event_handler_t * p_event_handler)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t err_code = NRF_ERROR_NOT_FOUND;
 | 
			
		||||
 | 
			
		||||
    if (!APP_SCHED_QUEUE_EMPTY())
 | 
			
		||||
    {
 | 
			
		||||
        uint16_t event_index;
 | 
			
		||||
 | 
			
		||||
        // NOTE: There is no need for a critical region here, as this function will only be called
 | 
			
		||||
        //       from app_sched_execute() from inside the main loop, so it will never interrupt
 | 
			
		||||
        //       app_sched_event_put(). Also, updating of (i.e. writing to) the start index will be
 | 
			
		||||
        //       an atomic operation.
 | 
			
		||||
        event_index         = m_queue_start_index;
 | 
			
		||||
        m_queue_start_index = next_index(m_queue_start_index);
 | 
			
		||||
 | 
			
		||||
        *pp_event_data     = &m_queue_event_data[event_index * m_queue_event_size];
 | 
			
		||||
        *p_event_data_size = m_queue_event_headers[event_index].event_data_size;
 | 
			
		||||
        *p_event_handler   = m_queue_event_headers[event_index].handler;
 | 
			
		||||
 | 
			
		||||
        err_code = NRF_SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return err_code;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void app_sched_execute(void)
 | 
			
		||||
{
 | 
			
		||||
    void                    * p_event_data;
 | 
			
		||||
    uint16_t                  event_data_size;
 | 
			
		||||
    app_sched_event_handler_t event_handler;
 | 
			
		||||
 | 
			
		||||
    // Get next event (if any), and execute handler
 | 
			
		||||
    while ((app_sched_event_get(&p_event_data, &event_data_size, &event_handler) == NRF_SUCCESS))
 | 
			
		||||
    {
 | 
			
		||||
        event_handler(p_event_data, event_data_size);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,163 @@
 | 
			
		||||
/* Copyright (c) 2012 Nordic Semiconductor. All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * The information contained herein is property of Nordic Semiconductor ASA.
 | 
			
		||||
 * Terms and conditions of usage are described in detail in NORDIC
 | 
			
		||||
 * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensees are granted free, non-transferable use of the information. NO
 | 
			
		||||
 * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
 | 
			
		||||
 * the file.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/** @file
 | 
			
		||||
 *
 | 
			
		||||
 * @defgroup app_scheduler Scheduler
 | 
			
		||||
 * @{
 | 
			
		||||
 * @ingroup app_common
 | 
			
		||||
 *
 | 
			
		||||
 * @brief The scheduler is used for transferring execution from the interrupt context to the main
 | 
			
		||||
 *        context.
 | 
			
		||||
 *
 | 
			
		||||
 * @details See @ref seq_diagrams_sched for sequence diagrams illustrating the flow of events
 | 
			
		||||
 *          when using the Scheduler.
 | 
			
		||||
 *
 | 
			
		||||
 * @section app_scheduler_req Requirements:
 | 
			
		||||
 *
 | 
			
		||||
 * @subsection main_context_logic Logic in main context:
 | 
			
		||||
 *
 | 
			
		||||
 *   - Define an event handler for each type of event expected.
 | 
			
		||||
 *   - Initialize the scheduler by calling the APP_SCHED_INIT() macro before entering the
 | 
			
		||||
 *     application main loop.
 | 
			
		||||
 *   - Call app_sched_execute() from the main loop each time the application wakes up because of an
 | 
			
		||||
 *     event (typically when sd_app_evt_wait() returns).
 | 
			
		||||
 *
 | 
			
		||||
 * @subsection int_context_logic Logic in interrupt context:
 | 
			
		||||
 *
 | 
			
		||||
 *   - In the interrupt handler, call app_sched_event_put()
 | 
			
		||||
 *     with the appropriate data and event handler. This will insert an event into the
 | 
			
		||||
 *     scheduler's queue. The app_sched_execute() function will pull this event and call its
 | 
			
		||||
 *     handler in the main context.
 | 
			
		||||
 *
 | 
			
		||||
 * @if (PERIPHERAL)
 | 
			
		||||
 * For an example usage of the scheduler, see the implementations of
 | 
			
		||||
 * @ref ble_sdk_app_hids_mouse and @ref ble_sdk_app_hids_keyboard.
 | 
			
		||||
 * @endif
 | 
			
		||||
 *
 | 
			
		||||
 * @image html scheduler_working.jpg The high level design of the scheduler
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef APP_SCHEDULER_H__
 | 
			
		||||
#define APP_SCHEDULER_H__
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include "app_error.h"
 | 
			
		||||
#include "app_util.h"
 | 
			
		||||
 | 
			
		||||
#define APP_SCHED_EVENT_HEADER_SIZE 8       /**< Size of app_scheduler.event_header_t (only for use inside APP_SCHED_BUF_SIZE()). */
 | 
			
		||||
 | 
			
		||||
/**@brief Compute number of bytes required to hold the scheduler buffer.
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in] EVENT_SIZE   Maximum size of events to be passed through the scheduler.
 | 
			
		||||
 * @param[in] QUEUE_SIZE   Number of entries in scheduler queue (i.e. the maximum number of events
 | 
			
		||||
 *                         that can be scheduled for execution).
 | 
			
		||||
 *
 | 
			
		||||
 * @return    Required scheduler buffer size (in bytes).
 | 
			
		||||
 */
 | 
			
		||||
#define APP_SCHED_BUF_SIZE(EVENT_SIZE, QUEUE_SIZE)                                                 \
 | 
			
		||||
            (((EVENT_SIZE) + APP_SCHED_EVENT_HEADER_SIZE) * ((QUEUE_SIZE) + 1))
 | 
			
		||||
            
 | 
			
		||||
/**@brief Scheduler event handler type. */
 | 
			
		||||
typedef void (*app_sched_event_handler_t)(void * p_event_data, uint16_t event_size);
 | 
			
		||||
 | 
			
		||||
/**@brief Macro for initializing the event scheduler.
 | 
			
		||||
 *
 | 
			
		||||
 * @details It will also handle dimensioning and allocation of the memory buffer required by the
 | 
			
		||||
 *          scheduler, making sure the buffer is correctly aligned.
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in] EVENT_SIZE   Maximum size of events to be passed through the scheduler.
 | 
			
		||||
 * @param[in] QUEUE_SIZE   Number of entries in scheduler queue (i.e. the maximum number of events
 | 
			
		||||
 *                         that can be scheduled for execution).
 | 
			
		||||
 *
 | 
			
		||||
 * @note Since this macro allocates a buffer, it must only be called once (it is OK to call it
 | 
			
		||||
 *       several times as long as it is from the same location, e.g. to do a reinitialization).
 | 
			
		||||
 */
 | 
			
		||||
#define APP_SCHED_INIT(EVENT_SIZE, QUEUE_SIZE)                                                     \
 | 
			
		||||
    do                                                                                             \
 | 
			
		||||
    {                                                                                              \
 | 
			
		||||
        static uint32_t APP_SCHED_BUF[CEIL_DIV(APP_SCHED_BUF_SIZE((EVENT_SIZE), (QUEUE_SIZE)),     \
 | 
			
		||||
                                               sizeof(uint32_t))];                                 \
 | 
			
		||||
        uint32_t ERR_CODE = app_sched_init((EVENT_SIZE), (QUEUE_SIZE), APP_SCHED_BUF);             \
 | 
			
		||||
        APP_ERROR_CHECK(ERR_CODE);                                                                 \
 | 
			
		||||
    } while (0)
 | 
			
		||||
 | 
			
		||||
/**@brief Function for initializing the Scheduler.
 | 
			
		||||
 *
 | 
			
		||||
 * @details It must be called before entering the main loop.
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in]   max_event_size   Maximum size of events to be passed through the scheduler.
 | 
			
		||||
 * @param[in]   queue_size       Number of entries in scheduler queue (i.e. the maximum number of
 | 
			
		||||
 *                               events that can be scheduled for execution).
 | 
			
		||||
 * @param[in]   p_evt_buffer   Pointer to memory buffer for holding the scheduler queue. It must
 | 
			
		||||
 *                               be dimensioned using the APP_SCHED_BUFFER_SIZE() macro. The buffer
 | 
			
		||||
 *                               must be aligned to a 4 byte boundary.
 | 
			
		||||
 *
 | 
			
		||||
 * @note Normally initialization should be done using the APP_SCHED_INIT() macro, as that will both
 | 
			
		||||
 *       allocate the scheduler buffer, and also align the buffer correctly.
 | 
			
		||||
 *
 | 
			
		||||
 * @retval      NRF_SUCCESS               Successful initialization.
 | 
			
		||||
 * @retval      NRF_ERROR_INVALID_PARAM   Invalid parameter (buffer not aligned to a 4 byte
 | 
			
		||||
 *                                        boundary).
 | 
			
		||||
 */
 | 
			
		||||
uint32_t app_sched_init(uint16_t max_event_size, uint16_t queue_size, void * p_evt_buffer);
 | 
			
		||||
 | 
			
		||||
/**@brief Function for executing all scheduled events.
 | 
			
		||||
 *
 | 
			
		||||
 * @details This function must be called from within the main loop. It will execute all events
 | 
			
		||||
 *          scheduled since the last time it was called.
 | 
			
		||||
 */
 | 
			
		||||
void app_sched_execute(void);
 | 
			
		||||
 | 
			
		||||
/**@brief Function for scheduling an event.
 | 
			
		||||
 *
 | 
			
		||||
 * @details Puts an event into the event queue.
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in]   p_event_data   Pointer to event data to be scheduled.
 | 
			
		||||
 * @param[in]   event_size   Size of event data to be scheduled.
 | 
			
		||||
 * @param[in]   handler        Event handler to receive the event.
 | 
			
		||||
 *
 | 
			
		||||
 * @return      NRF_SUCCESS on success, otherwise an error code.
 | 
			
		||||
 */
 | 
			
		||||
uint32_t app_sched_event_put(void *                    p_event_data,
 | 
			
		||||
                             uint16_t                  event_size,
 | 
			
		||||
                             app_sched_event_handler_t handler);
 | 
			
		||||
 | 
			
		||||
#ifdef APP_SCHEDULER_WITH_PROFILER
 | 
			
		||||
/**@brief Function for getting the maximum observed queue utilization.
 | 
			
		||||
 *
 | 
			
		||||
 * Function for tuning the module and determining QUEUE_SIZE value and thus module RAM usage.
 | 
			
		||||
 *
 | 
			
		||||
 * @return Maximum number of events in queue observed so far.
 | 
			
		||||
 */
 | 
			
		||||
uint16_t app_sched_queue_utilization_get(void);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef APP_SCHEDULER_WITH_PAUSE
 | 
			
		||||
/**@brief A function to pause the scheduler.
 | 
			
		||||
 *
 | 
			
		||||
 * @details When the scheduler is paused events are not pulled from the scheduler queue for
 | 
			
		||||
 *          processing. The function can be called multiple times. To unblock the scheduler the
 | 
			
		||||
 *          function @ref app_sched_resume has to be called the same number of times.
 | 
			
		||||
 */
 | 
			
		||||
void app_sched_pause(void);
 | 
			
		||||
 | 
			
		||||
/**@brief A function to resume a scheduler.
 | 
			
		||||
 *
 | 
			
		||||
 * @details To unblock the scheduler this function has to be called the same number of times as
 | 
			
		||||
 *          @ref app_sched_pause function.
 | 
			
		||||
 */
 | 
			
		||||
void app_sched_resume(void);
 | 
			
		||||
#endif
 | 
			
		||||
#endif // APP_SCHEDULER_H__
 | 
			
		||||
 | 
			
		||||
/** @} */
 | 
			
		||||
@@ -0,0 +1,262 @@
 | 
			
		||||
/* Copyright (c) 2012 Nordic Semiconductor. All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * The information contained herein is property of Nordic Semiconductor ASA.
 | 
			
		||||
 * Terms and conditions of usage are described in detail in NORDIC
 | 
			
		||||
 * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensees are granted free, non-transferable use of the information. NO
 | 
			
		||||
 * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
 | 
			
		||||
 * the file.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "app_scheduler.h"
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include "nrf_soc.h"
 | 
			
		||||
#include "nrf_assert.h"
 | 
			
		||||
#include "app_util.h"
 | 
			
		||||
#include "app_util_platform.h"
 | 
			
		||||
 | 
			
		||||
/**@brief Structure for holding a scheduled event header. */
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
    app_sched_event_handler_t handler;         /**< Pointer to event handler to receive the event. */
 | 
			
		||||
    uint16_t                  event_data_size; /**< Size of event data. */
 | 
			
		||||
} event_header_t;
 | 
			
		||||
 | 
			
		||||
STATIC_ASSERT(sizeof (event_header_t) <= APP_SCHED_EVENT_HEADER_SIZE);
 | 
			
		||||
 | 
			
		||||
static event_header_t * m_queue_event_headers; /**< Array for holding the queue event headers. */
 | 
			
		||||
static uint8_t *        m_queue_event_data;    /**< Array for holding the queue event data. */
 | 
			
		||||
static volatile uint8_t m_queue_start_index;   /**< Index of queue entry at the start of the queue. */
 | 
			
		||||
static volatile uint8_t m_queue_end_index;     /**< Index of queue entry at the end of the queue. */
 | 
			
		||||
static uint16_t         m_queue_event_size;    /**< Maximum event size in queue. */
 | 
			
		||||
static uint16_t         m_queue_size;          /**< Number of queue entries. */
 | 
			
		||||
 | 
			
		||||
#ifdef APP_SCHEDULER_WITH_PROFILER
 | 
			
		||||
static uint16_t m_max_queue_utilization;    /**< Maximum observed queue utilization. */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static uint32_t m_scheduler_paused_counter = 0; /**< Counter storing the difference between pausing
 | 
			
		||||
                                                     and resuming the scheduler. */
 | 
			
		||||
 | 
			
		||||
/**@brief Function for incrementing a queue index, and handle wrap-around.
 | 
			
		||||
 *
 | 
			
		||||
 * @param[in]   index   Old index.
 | 
			
		||||
 *
 | 
			
		||||
 * @return      New (incremented) index.
 | 
			
		||||
 */
 | 
			
		||||
static __INLINE uint8_t next_index(uint8_t index)
 | 
			
		||||
{
 | 
			
		||||
    return (index < m_queue_size) ? (index + 1) : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static __INLINE uint8_t app_sched_queue_full(void)
 | 
			
		||||
{
 | 
			
		||||
  uint8_t tmp = m_queue_start_index;
 | 
			
		||||
  return next_index(m_queue_end_index) == tmp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**@brief Macro for checking if a queue is full. */
 | 
			
		||||
#define APP_SCHED_QUEUE_FULL() app_sched_queue_full()
 | 
			
		||||
 | 
			
		||||
static __INLINE uint8_t app_sched_queue_empty(void)
 | 
			
		||||
{
 | 
			
		||||
  uint8_t tmp = m_queue_start_index;
 | 
			
		||||
  return m_queue_end_index == tmp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**@brief Macro for checking if a queue is empty. */
 | 
			
		||||
#define APP_SCHED_QUEUE_EMPTY() app_sched_queue_empty()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
uint32_t app_sched_init(uint16_t event_size, uint16_t queue_size, void * p_event_buffer)
 | 
			
		||||
{
 | 
			
		||||
    uint16_t data_start_index = (queue_size + 1) * sizeof (event_header_t);
 | 
			
		||||
 | 
			
		||||
    //Check that buffer is correctly aligned
 | 
			
		||||
    if (!is_word_aligned(p_event_buffer))
 | 
			
		||||
    {
 | 
			
		||||
        return NRF_ERROR_INVALID_PARAM;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //Initialize event scheduler
 | 
			
		||||
    m_queue_event_headers = p_event_buffer;
 | 
			
		||||
    m_queue_event_data    = &((uint8_t *)p_event_buffer)[data_start_index];
 | 
			
		||||
    m_queue_end_index     = 0;
 | 
			
		||||
    m_queue_start_index   = 0;
 | 
			
		||||
    m_queue_event_size    = event_size;
 | 
			
		||||
    m_queue_size          = queue_size;
 | 
			
		||||
 | 
			
		||||
#ifdef APP_SCHEDULER_WITH_PROFILER
 | 
			
		||||
    m_max_queue_utilization = 0;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    return NRF_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef APP_SCHEDULER_WITH_PROFILER
 | 
			
		||||
static void check_queue_utilization(void)
 | 
			
		||||
{
 | 
			
		||||
    uint16_t start = m_queue_start_index;
 | 
			
		||||
    uint16_t end   = m_queue_end_index;
 | 
			
		||||
    uint16_t queue_utilization = (end >= start) ? (end - start) :
 | 
			
		||||
        (m_queue_size + 1 - start + end);
 | 
			
		||||
 | 
			
		||||
    if (queue_utilization > m_max_queue_utilization)
 | 
			
		||||
    {
 | 
			
		||||
        m_max_queue_utilization = queue_utilization;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t app_sched_queue_utilization_get(void)
 | 
			
		||||
{
 | 
			
		||||
    return m_max_queue_utilization;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
uint32_t app_sched_event_put(void *                    p_event_data,
 | 
			
		||||
                             uint16_t                  event_data_size,
 | 
			
		||||
                             app_sched_event_handler_t handler)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t err_code;
 | 
			
		||||
 | 
			
		||||
    if (event_data_size <= m_queue_event_size)
 | 
			
		||||
    {
 | 
			
		||||
        uint16_t event_index = 0xFFFF;
 | 
			
		||||
 | 
			
		||||
        CRITICAL_REGION_ENTER();
 | 
			
		||||
 | 
			
		||||
        if (!APP_SCHED_QUEUE_FULL())
 | 
			
		||||
        {
 | 
			
		||||
            event_index       = m_queue_end_index;
 | 
			
		||||
            m_queue_end_index = next_index(m_queue_end_index);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CRITICAL_REGION_EXIT();
 | 
			
		||||
 | 
			
		||||
        if (event_index != 0xFFFF)
 | 
			
		||||
        {
 | 
			
		||||
            //NOTE: This can be done outside the critical region since the event consumer will
 | 
			
		||||
            //always be called from the main loop, and will thus never interrupt this code.
 | 
			
		||||
            m_queue_event_headers[event_index].handler = handler;
 | 
			
		||||
 | 
			
		||||
            if ((p_event_data != NULL) && (event_data_size > 0))
 | 
			
		||||
            {
 | 
			
		||||
                memcpy(&m_queue_event_data[event_index * m_queue_event_size],
 | 
			
		||||
                       p_event_data,
 | 
			
		||||
                       event_data_size);
 | 
			
		||||
                m_queue_event_headers[event_index].event_data_size = event_data_size;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                m_queue_event_headers[event_index].event_data_size = 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        #ifdef APP_SCHEDULER_WITH_PROFILER
 | 
			
		||||
            check_queue_utilization();
 | 
			
		||||
        #endif
 | 
			
		||||
 | 
			
		||||
            err_code = NRF_SUCCESS;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            err_code = NRF_ERROR_NO_MEM;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        err_code = NRF_ERROR_INVALID_LENGTH;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return err_code;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**@brief Function for reading the next event from specified event queue.
 | 
			
		||||
 *
 | 
			
		||||
 * @param[out]  pp_event_data       Pointer to pointer to event data.
 | 
			
		||||
 * @param[out]  p_event_data_size   Pointer to size of event data.
 | 
			
		||||
 * @param[out]  p_event_handler     Pointer to event handler function pointer.
 | 
			
		||||
 *
 | 
			
		||||
 * @return      NRF_SUCCESS if new event, NRF_ERROR_NOT_FOUND if event queue is empty.
 | 
			
		||||
 */
 | 
			
		||||
static uint32_t app_sched_event_get(void * *                    pp_event_data,
 | 
			
		||||
                                    uint16_t *                  p_event_data_size,
 | 
			
		||||
                                    app_sched_event_handler_t * p_event_handler)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t err_code = NRF_ERROR_NOT_FOUND;
 | 
			
		||||
 | 
			
		||||
    if (!APP_SCHED_QUEUE_EMPTY())
 | 
			
		||||
    {
 | 
			
		||||
        uint16_t event_index;
 | 
			
		||||
 | 
			
		||||
        //NOTE: There is no need for a critical region here, as this function will only be called
 | 
			
		||||
        //from app_sched_execute() from inside the main loop, so it will never interrupt
 | 
			
		||||
        //app_sched_event_put(). Also, updating of (i.e. writing to) the start index will be
 | 
			
		||||
        //an atomic operation.
 | 
			
		||||
        event_index         = m_queue_start_index;
 | 
			
		||||
        m_queue_start_index = next_index(m_queue_start_index);
 | 
			
		||||
 | 
			
		||||
        *pp_event_data     = &m_queue_event_data[event_index * m_queue_event_size];
 | 
			
		||||
        *p_event_data_size = m_queue_event_headers[event_index].event_data_size;
 | 
			
		||||
        *p_event_handler   = m_queue_event_headers[event_index].handler;
 | 
			
		||||
 | 
			
		||||
        err_code = NRF_SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return err_code;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void app_sched_pause(void)
 | 
			
		||||
{
 | 
			
		||||
    CRITICAL_REGION_ENTER();
 | 
			
		||||
 | 
			
		||||
    if (m_scheduler_paused_counter < UINT32_MAX)
 | 
			
		||||
    {
 | 
			
		||||
        m_scheduler_paused_counter++;
 | 
			
		||||
    }
 | 
			
		||||
    CRITICAL_REGION_EXIT();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void app_sched_resume(void)
 | 
			
		||||
{
 | 
			
		||||
    CRITICAL_REGION_ENTER();
 | 
			
		||||
 | 
			
		||||
    if (m_scheduler_paused_counter > 0)
 | 
			
		||||
    {
 | 
			
		||||
        m_scheduler_paused_counter--;
 | 
			
		||||
    }
 | 
			
		||||
    CRITICAL_REGION_EXIT();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**@brief Function for checking if scheduler is paused which means that should break processing
 | 
			
		||||
 *        events.
 | 
			
		||||
 *
 | 
			
		||||
 * @return    Boolean value - true if scheduler is paused, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static __INLINE bool is_app_sched_paused(void)
 | 
			
		||||
{
 | 
			
		||||
    return (m_scheduler_paused_counter > 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void app_sched_execute(void)
 | 
			
		||||
{
 | 
			
		||||
    void *                    p_event_data;
 | 
			
		||||
    uint16_t                  event_data_size;
 | 
			
		||||
    app_sched_event_handler_t event_handler;
 | 
			
		||||
 | 
			
		||||
    //Get next event (if any), and execute handler
 | 
			
		||||
    while ((!is_app_sched_paused()) &&
 | 
			
		||||
           (app_sched_event_get(&p_event_data, &event_data_size, &event_handler) == NRF_SUCCESS))
 | 
			
		||||
    {
 | 
			
		||||
        event_handler(p_event_data, event_data_size);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user