1 /*
2  * Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #ifndef _PICO_ASYNC_CONTEXT_FREERTOS_H
8 #define _PICO_ASYNC_CONTEXT_FREERTOS_H
9 
10 /** \file pico/async_context.h
11  *  \defgroup async_context_freertos async_context_freertos
12  *  \ingroup pico_async_context
13  *
14  * \brief async_context_freertos provides an implementation of \ref async_context that handles asynchronous
15  * work in a separate FreeRTOS task.
16  */
17 #include "pico/async_context.h"
18 
19 // FreeRTOS includes
20 #include "FreeRTOS.h"
21 #include "semphr.h"
22 #include "timers.h"
23 
24 #ifdef __cplusplus
25 extern "C" {
26 #endif
27 
28 #ifndef ASYNC_CONTEXT_DEFAULT_FREERTOS_TASK_PRIORITY
29 #define ASYNC_CONTEXT_DEFAULT_FREERTOS_TASK_PRIORITY ( tskIDLE_PRIORITY + 4)
30 #endif
31 
32 #ifndef ASYNC_CONTEXT_DEFAULT_FREERTOS_TASK_STACK_SIZE
33 #define ASYNC_CONTEXT_DEFAULT_FREERTOS_TASK_STACK_SIZE configMINIMAL_STACK_SIZE
34 #endif
35 
36 typedef struct async_context_freertos async_context_freertos_t;
37 
38 #if !defined(configNUMBER_OF_CORES) && defined(configNUM_CORES)
39 #if !portSUPPORT_SMP
40 #error configNUMBER_OF_CORES is the new name for configNUM_CORES
41 #else
42 // portSUPPORT_SMP was defined in old smp branch
43 #error configNUMBER_OF_CORES is the new name for configNUM_CORES, however it looks like you may need to define both as you are using an old SMP branch of FreeRTOS
44 #endif
45 #endif
46 
47 /**
48  * \brief Configuration object for async_context_freertos instances.
49  */
50 typedef struct async_context_freertos_config {
51     /**
52      * \brief Task priority for the async_context task
53      */
54     UBaseType_t task_priority;
55     /**
56      * \brief Stack size for the async_context task
57      */
58     configSTACK_DEPTH_TYPE task_stack_size;
59     /**
60      * \brief the core ID (see \ref portGET_CORE_ID()) to pin the task to.
61      * This is only relevant in SMP mode.
62      */
63 #if configUSE_CORE_AFFINITY && configNUMBER_OF_CORES > 1
64     UBaseType_t task_core_id;
65 #endif
66 } async_context_freertos_config_t;
67 
68 struct async_context_freertos {
69     async_context_t core;
70     SemaphoreHandle_t lock_mutex;
71     SemaphoreHandle_t work_needed_sem;
72     TimerHandle_t timer_handle;
73     TaskHandle_t task_handle;
74     uint8_t nesting;
75     volatile bool task_should_exit;
76 };
77 
78 /*!
79  * \brief Initialize an async_context_freertos instance using the specified configuration
80  * \ingroup async_context_freertos
81  *
82  * If this method succeeds (returns true), then the async_context is available for use
83  * and can be de-initialized by calling async_context_deinit().
84  *
85  * \param self a pointer to async_context_freertos structure to initialize
86  * \param config the configuration object specifying characteristics for the async_context
87  * \return true if initialization is successful, false otherwise
88  */
89 bool async_context_freertos_init(async_context_freertos_t *self, async_context_freertos_config_t *config);
90 
91 /*!
92  * \brief Return a copy of the default configuration object used by \ref async_context_freertos_init_with_defaults()
93  * \ingroup async_context_freertos
94  *
95  * The caller can then modify just the settings it cares about, and call \ref async_context_freertos_init()
96  * \return the default configuration object
97  */
async_context_freertos_default_config(void)98  static inline async_context_freertos_config_t async_context_freertos_default_config(void) {
99     async_context_freertos_config_t config = {
100             .task_priority = ASYNC_CONTEXT_DEFAULT_FREERTOS_TASK_PRIORITY,
101             .task_stack_size = ASYNC_CONTEXT_DEFAULT_FREERTOS_TASK_STACK_SIZE,
102 #if configUSE_CORE_AFFINITY && configNUMBER_OF_CORES > 1
103             .task_core_id = (UBaseType_t)-1, // none
104 #endif
105     };
106     return config;
107 
108 }
109 
110 /*!
111  * \brief Initialize an async_context_freertos instance with default values
112  * \ingroup async_context_freertos
113  *
114  * If this method succeeds (returns true), then the async_context is available for use
115  * and can be de-initialized by calling async_context_deinit().
116  *
117  * \param self a pointer to async_context_freertos structure to initialize
118  * \return true if initialization is successful, false otherwise
119  */
async_context_freertos_init_with_defaults(async_context_freertos_t * self)120  static inline bool async_context_freertos_init_with_defaults(async_context_freertos_t *self) {
121     async_context_freertos_config_t config = async_context_freertos_default_config();
122     return async_context_freertos_init(self, &config);
123 }
124 
125 #ifdef __cplusplus
126 }
127 #endif
128 
129 #endif
130