app_scheduler_serconn.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. /**
  2. * Copyright (c) 2012 - 2019, Nordic Semiconductor ASA
  3. *
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without modification,
  7. * are permitted provided that the following conditions are met:
  8. *
  9. * 1. Redistributions of source code must retain the above copyright notice, this
  10. * list of conditions and the following disclaimer.
  11. *
  12. * 2. Redistributions in binary form, except as embedded into a Nordic
  13. * Semiconductor ASA integrated circuit in a product or a software update for
  14. * such product, must reproduce the above copyright notice, this list of
  15. * conditions and the following disclaimer in the documentation and/or other
  16. * materials provided with the distribution.
  17. *
  18. * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
  19. * contributors may be used to endorse or promote products derived from this
  20. * software without specific prior written permission.
  21. *
  22. * 4. This software, with or without modification, must only be used with a
  23. * Nordic Semiconductor ASA integrated circuit.
  24. *
  25. * 5. Any software provided in binary form under this license must not be reverse
  26. * engineered, decompiled, modified and/or disassembled.
  27. *
  28. * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
  29. * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  30. * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
  31. * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
  32. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  33. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  34. * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  35. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  36. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  37. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  38. *
  39. */
  40. #include "app_scheduler.h"
  41. #include <stdlib.h>
  42. #include <stdint.h>
  43. #include <string.h>
  44. #include "nrf_soc.h"
  45. #include "nrf_assert.h"
  46. #include "app_util.h"
  47. #include "app_util_platform.h"
  48. /**@brief Structure for holding a scheduled event header. */
  49. typedef struct
  50. {
  51. app_sched_event_handler_t handler; /**< Pointer to event handler to receive the event. */
  52. uint16_t event_data_size; /**< Size of event data. */
  53. } event_header_t;
  54. STATIC_ASSERT(sizeof (event_header_t) <= APP_SCHED_EVENT_HEADER_SIZE);
  55. static event_header_t * m_queue_event_headers; /**< Array for holding the queue event headers. */
  56. static uint8_t * m_queue_event_data; /**< Array for holding the queue event data. */
  57. static volatile uint8_t m_queue_start_index; /**< Index of queue entry at the start of the queue. */
  58. static volatile uint8_t m_queue_end_index; /**< Index of queue entry at the end of the queue. */
  59. static uint16_t m_queue_event_size; /**< Maximum event size in queue. */
  60. static uint16_t m_queue_size; /**< Number of queue entries. */
  61. #if APP_SCHEDULER_WITH_PROFILER
  62. static uint16_t m_max_queue_utilization; /**< Maximum observed queue utilization. */
  63. #endif
  64. static uint32_t m_scheduler_paused_counter = 0; /**< Counter storing the difference between pausing
  65. and resuming the scheduler. */
  66. /**@brief Function for incrementing a queue index, and handle wrap-around.
  67. *
  68. * @param[in] index Old index.
  69. *
  70. * @return New (incremented) index.
  71. */
  72. static __INLINE uint8_t next_index(uint8_t index)
  73. {
  74. return (index < m_queue_size) ? (index + 1) : 0;
  75. }
  76. static __INLINE uint8_t app_sched_queue_full(void)
  77. {
  78. uint8_t tmp = m_queue_start_index;
  79. return next_index(m_queue_end_index) == tmp;
  80. }
  81. /**@brief Macro for checking if a queue is full. */
  82. #define APP_SCHED_QUEUE_FULL() app_sched_queue_full()
  83. static __INLINE uint8_t app_sched_queue_empty(void)
  84. {
  85. uint8_t tmp = m_queue_start_index;
  86. return m_queue_end_index == tmp;
  87. }
  88. /**@brief Macro for checking if a queue is empty. */
  89. #define APP_SCHED_QUEUE_EMPTY() app_sched_queue_empty()
  90. uint32_t app_sched_init(uint16_t event_size, uint16_t queue_size, void * p_event_buffer)
  91. {
  92. uint16_t data_start_index = (queue_size + 1) * sizeof (event_header_t);
  93. //Check that buffer is correctly aligned
  94. if (!is_word_aligned(p_event_buffer))
  95. {
  96. return NRF_ERROR_INVALID_PARAM;
  97. }
  98. //Initialize event scheduler
  99. m_queue_event_headers = p_event_buffer;
  100. m_queue_event_data = &((uint8_t *)p_event_buffer)[data_start_index];
  101. m_queue_end_index = 0;
  102. m_queue_start_index = 0;
  103. m_queue_event_size = event_size;
  104. m_queue_size = queue_size;
  105. #if APP_SCHEDULER_WITH_PROFILER
  106. m_max_queue_utilization = 0;
  107. #endif
  108. return NRF_SUCCESS;
  109. }
  110. uint16_t app_sched_queue_space_get()
  111. {
  112. uint16_t start = m_queue_start_index;
  113. uint16_t end = m_queue_end_index;
  114. uint16_t free_space = m_queue_size - ((end >= start) ?
  115. (end - start) : (m_queue_size + 1 - start + end));
  116. return free_space;
  117. }
  118. #if APP_SCHEDULER_WITH_PROFILER
  119. static __INLINE void check_queue_utilization(void)
  120. {
  121. uint16_t start = m_queue_start_index;
  122. uint16_t end = m_queue_end_index;
  123. uint16_t queue_utilization = (end >= start) ? (end - start) :
  124. (m_queue_size + 1 - start + end);
  125. if (queue_utilization > m_max_queue_utilization)
  126. {
  127. m_max_queue_utilization = queue_utilization;
  128. }
  129. }
  130. uint16_t app_sched_queue_utilization_get(void)
  131. {
  132. return m_max_queue_utilization;
  133. }
  134. #endif // APP_SCHEDULER_WITH_PROFILER
  135. uint32_t app_sched_event_put(void * p_event_data,
  136. uint16_t event_data_size,
  137. app_sched_event_handler_t handler)
  138. {
  139. uint32_t err_code;
  140. if (event_data_size <= m_queue_event_size)
  141. {
  142. uint16_t event_index = 0xFFFF;
  143. CRITICAL_REGION_ENTER();
  144. if (!APP_SCHED_QUEUE_FULL())
  145. {
  146. event_index = m_queue_end_index;
  147. m_queue_end_index = next_index(m_queue_end_index);
  148. }
  149. CRITICAL_REGION_EXIT();
  150. if (event_index != 0xFFFF)
  151. {
  152. //NOTE: This can be done outside the critical region since the event consumer will
  153. //always be called from the main loop, and will thus never interrupt this code.
  154. m_queue_event_headers[event_index].handler = handler;
  155. if ((p_event_data != NULL) && (event_data_size > 0))
  156. {
  157. memcpy(&m_queue_event_data[event_index * m_queue_event_size],
  158. p_event_data,
  159. event_data_size);
  160. m_queue_event_headers[event_index].event_data_size = event_data_size;
  161. }
  162. else
  163. {
  164. m_queue_event_headers[event_index].event_data_size = 0;
  165. }
  166. #if APP_SCHEDULER_WITH_PROFILER
  167. check_queue_utilization();
  168. #endif
  169. err_code = NRF_SUCCESS;
  170. }
  171. else
  172. {
  173. err_code = NRF_ERROR_NO_MEM;
  174. }
  175. }
  176. else
  177. {
  178. err_code = NRF_ERROR_INVALID_LENGTH;
  179. }
  180. return err_code;
  181. }
  182. /**@brief Function for reading the next event from specified event queue.
  183. *
  184. * @param[out] pp_event_data Pointer to pointer to event data.
  185. * @param[out] p_event_data_size Pointer to size of event data.
  186. * @param[out] p_event_handler Pointer to event handler function pointer.
  187. *
  188. * @return NRF_SUCCESS if new event, NRF_ERROR_NOT_FOUND if event queue is empty.
  189. */
  190. static uint32_t app_sched_event_get(void * * pp_event_data,
  191. uint16_t * p_event_data_size,
  192. app_sched_event_handler_t * p_event_handler)
  193. {
  194. uint32_t err_code = NRF_ERROR_NOT_FOUND;
  195. if (!APP_SCHED_QUEUE_EMPTY())
  196. {
  197. uint16_t event_index;
  198. //NOTE: There is no need for a critical region here, as this function will only be called
  199. //from app_sched_execute() from inside the main loop, so it will never interrupt
  200. //app_sched_event_put(). Also, updating of (i.e. writing to) the start index will be
  201. //an atomic operation.
  202. event_index = m_queue_start_index;
  203. m_queue_start_index = next_index(m_queue_start_index);
  204. *pp_event_data = &m_queue_event_data[event_index * m_queue_event_size];
  205. *p_event_data_size = m_queue_event_headers[event_index].event_data_size;
  206. *p_event_handler = m_queue_event_headers[event_index].handler;
  207. err_code = NRF_SUCCESS;
  208. }
  209. return err_code;
  210. }
  211. void app_sched_pause(void)
  212. {
  213. CRITICAL_REGION_ENTER();
  214. if (m_scheduler_paused_counter < UINT32_MAX)
  215. {
  216. m_scheduler_paused_counter++;
  217. }
  218. CRITICAL_REGION_EXIT();
  219. }
  220. void app_sched_resume(void)
  221. {
  222. CRITICAL_REGION_ENTER();
  223. if (m_scheduler_paused_counter > 0)
  224. {
  225. m_scheduler_paused_counter--;
  226. }
  227. CRITICAL_REGION_EXIT();
  228. }
  229. /**@brief Function for checking if scheduler is paused which means that should break processing
  230. * events.
  231. *
  232. * @return Boolean value - true if scheduler is paused, false otherwise.
  233. */
  234. static __INLINE bool is_app_sched_paused(void)
  235. {
  236. return (m_scheduler_paused_counter > 0);
  237. }
  238. void app_sched_execute(void)
  239. {
  240. void * p_event_data;
  241. uint16_t event_data_size;
  242. app_sched_event_handler_t event_handler;
  243. //Get next event (if any), and execute handler
  244. while ((!is_app_sched_paused()) &&
  245. (app_sched_event_get(&p_event_data, &event_data_size, &event_handler) == NRF_SUCCESS))
  246. {
  247. event_handler(p_event_data, event_data_size);
  248. }
  249. }