1 /*
2 * Copyright (c) 2016-2017 Nordic Semiconductor ASA
3 * Copyright (c) 2016 Vinayak Kariappa Chettimada
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <stddef.h>
9
10 #include <soc.h>
11 #include <zephyr/types.h>
12 #include <zephyr/sys/printk.h>
13
14 #include "hal/cpu.h"
15
16 #include "memq.h"
17 #include "mayfly.h"
18
19 static struct {
20 memq_link_t *head;
21 memq_link_t *tail;
22 uint8_t enable_req;
23 uint8_t enable_ack;
24 uint8_t disable_req;
25 uint8_t disable_ack;
26 } mft[MAYFLY_CALLEE_COUNT][MAYFLY_CALLER_COUNT];
27
28 static memq_link_t mfl[MAYFLY_CALLEE_COUNT][MAYFLY_CALLER_COUNT];
29 static uint8_t mfp[MAYFLY_CALLEE_COUNT];
30
31 #if defined(MAYFLY_UT)
32 static uint8_t _state;
33 #endif /* MAYFLY_UT */
34
mayfly_init(void)35 void mayfly_init(void)
36 {
37 uint8_t callee_id;
38
39 callee_id = MAYFLY_CALLEE_COUNT;
40 while (callee_id--) {
41 uint8_t caller_id;
42
43 caller_id = MAYFLY_CALLER_COUNT;
44 while (caller_id--) {
45 memq_init(&mfl[callee_id][caller_id],
46 &mft[callee_id][caller_id].head,
47 &mft[callee_id][caller_id].tail);
48 }
49 }
50 }
51
mayfly_enable(uint8_t caller_id,uint8_t callee_id,uint8_t enable)52 void mayfly_enable(uint8_t caller_id, uint8_t callee_id, uint8_t enable)
53 {
54 if (enable) {
55 if (mft[callee_id][caller_id].enable_req ==
56 mft[callee_id][caller_id].enable_ack) {
57 mft[callee_id][caller_id].enable_req++;
58 }
59
60 mayfly_enable_cb(caller_id, callee_id, enable);
61 } else {
62 if (mft[callee_id][caller_id].disable_req ==
63 mft[callee_id][caller_id].disable_ack) {
64 mft[callee_id][caller_id].disable_req++;
65
66 /* set mayfly callee pending */
67 mfp[callee_id] = 1U;
68
69 /* pend the callee for execution */
70 mayfly_pend(caller_id, callee_id);
71 }
72 }
73 }
74
mayfly_enqueue(uint8_t caller_id,uint8_t callee_id,uint8_t chain,struct mayfly * m)75 uint32_t mayfly_enqueue(uint8_t caller_id, uint8_t callee_id, uint8_t chain,
76 struct mayfly *m)
77 {
78 uint8_t state;
79 uint8_t ack;
80
81 chain = chain || !mayfly_prio_is_equal(caller_id, callee_id) ||
82 !mayfly_is_enabled(caller_id, callee_id) ||
83 (mft[callee_id][caller_id].disable_req !=
84 mft[callee_id][caller_id].disable_ack);
85
86 /* shadow the ack */
87 ack = m->_ack;
88
89 /* already in queue */
90 state = (m->_req - ack) & 0x03;
91 if (state != 0U) {
92 if (chain) {
93 if (state != 1U) {
94 /* mark as ready in queue */
95 m->_req = ack + 1;
96
97 goto mayfly_enqueue_pend;
98 }
99
100 /* already ready */
101 return 1;
102 }
103
104 /* mark as done in queue, and fall thru */
105 m->_req = ack + 2;
106 }
107
108 /* handle mayfly(s) that can be inline */
109 if (!chain) {
110 /* call fp */
111 m->fp(m->param);
112
113 return 0;
114 }
115
116 /* new, add as ready in the queue */
117 m->_req = ack + 1;
118 memq_enqueue(m->_link, m, &mft[callee_id][caller_id].tail);
119
120 mayfly_enqueue_pend:
121 /* set mayfly callee pending */
122 mfp[callee_id] = 1U;
123
124 /* pend the callee for execution */
125 mayfly_pend(caller_id, callee_id);
126
127 return 0;
128 }
129
dequeue(uint8_t callee_id,uint8_t caller_id,memq_link_t * link,struct mayfly * m)130 static void dequeue(uint8_t callee_id, uint8_t caller_id, memq_link_t *link,
131 struct mayfly *m)
132 {
133 uint8_t req;
134
135 req = m->_req;
136 if (((req - m->_ack) & 0x03) != 1U) {
137 uint8_t ack;
138
139 #if defined(MAYFLY_UT)
140 uint32_t mayfly_ut_run_test(void);
141 void mayfly_ut_mfy(void *param);
142
143 if (_state && m->fp == mayfly_ut_mfy) {
144 static uint8_t single;
145
146 if (!single) {
147 single = 1U;
148 mayfly_ut_run_test();
149 }
150 }
151 #endif /* MAYFLY_UT */
152
153 /* dequeue mayfly struct */
154 memq_dequeue(mft[callee_id][caller_id].tail,
155 &mft[callee_id][caller_id].head,
156 0);
157
158 /* release link into dequeued mayfly struct */
159 m->_link = link;
160
161 /* reset mayfly state to idle */
162 cpu_dmb();
163 ack = m->_ack;
164 m->_ack = req;
165
166 /* re-insert, if re-pended by interrupt */
167 cpu_dmb();
168 if (((m->_req - ack) & 0x03) == 1U) {
169 #if defined(MAYFLY_UT)
170 printk("%s: RACE\n", __func__);
171 #endif /* MAYFLY_UT */
172
173 m->_ack = ack;
174 memq_enqueue(link, m, &mft[callee_id][callee_id].tail);
175 }
176 }
177 }
178
mayfly_run(uint8_t callee_id)179 void mayfly_run(uint8_t callee_id)
180 {
181 uint8_t disable = 0U;
182 uint8_t enable = 0U;
183 uint8_t caller_id;
184
185 if (!mfp[callee_id]) {
186 return;
187 }
188 mfp[callee_id] = 0U;
189
190 /* iterate through each caller queue to this callee_id */
191 caller_id = MAYFLY_CALLER_COUNT;
192 while (caller_id--) {
193 memq_link_t *link;
194 struct mayfly *m = 0;
195
196 /* fetch mayfly in callee queue, if any */
197 link = memq_peek(mft[callee_id][caller_id].head,
198 mft[callee_id][caller_id].tail,
199 (void **)&m);
200 while (link) {
201 uint8_t state;
202 #if defined(MAYFLY_UT)
203 _state = 0U;
204 #endif /* MAYFLY_UT */
205
206 /* execute work if ready */
207 state = (m->_req - m->_ack) & 0x03;
208 if (state == 1U) {
209 #if defined(MAYFLY_UT)
210 _state = 1U;
211 #endif /* MAYFLY_UT */
212
213 /* mark mayfly as ran */
214 m->_ack--;
215
216 /* call the mayfly function */
217 m->fp(m->param);
218 }
219
220 /* dequeue if not re-pended */
221 dequeue(callee_id, caller_id, link, m);
222
223 /* fetch next mayfly in callee queue, if any */
224 link = memq_peek(mft[callee_id][caller_id].head,
225 mft[callee_id][caller_id].tail,
226 (void **)&m);
227
228 /**
229 * When using cooperative thread implementation, an issue has been seen where
230 * pended mayflies are never executed in certain scenarios.
231 * This happens when mayflies with higher caller_id are constantly pended, in
232 * which case lower value caller ids never get to be executed.
233 * By allowing complete traversal of mayfly queues for all caller_ids, this
234 * does not happen, however this means that more than one mayfly function is
235 * potentially executed in a mayfly_run(), with added execution time as
236 * consequence.
237 */
238 #if defined(CONFIG_BT_MAYFLY_YIELD_AFTER_CALL)
239 /* yield out of mayfly_run if a mayfly function was
240 * called.
241 */
242 if (state == 1U) {
243 /* pend callee (tailchain) if mayfly queue is
244 * not empty or all caller queues are not
245 * processed.
246 */
247 if (caller_id || link) {
248 /* set mayfly callee pending */
249 mfp[callee_id] = 1U;
250
251 /* pend the callee for execution */
252 mayfly_pend(callee_id, callee_id);
253
254 return;
255 }
256 }
257 #endif
258 }
259
260 if (mft[callee_id][caller_id].disable_req !=
261 mft[callee_id][caller_id].disable_ack) {
262 disable = 1U;
263
264 mft[callee_id][caller_id].disable_ack =
265 mft[callee_id][caller_id].disable_req;
266 }
267
268 if (mft[callee_id][caller_id].enable_req !=
269 mft[callee_id][caller_id].enable_ack) {
270 enable = 1U;
271
272 mft[callee_id][caller_id].enable_ack =
273 mft[callee_id][caller_id].enable_req;
274 }
275 }
276
277 if (disable && !enable) {
278 mayfly_enable_cb(callee_id, callee_id, 0);
279 }
280 }
281
282 #if defined(MAYFLY_UT)
283 #define MAYFLY_CALL_ID_CALLER MAYFLY_CALL_ID_0
284 #define MAYFLY_CALL_ID_CALLEE MAYFLY_CALL_ID_2
285
mayfly_ut_mfy(void * param)286 void mayfly_ut_mfy(void *param)
287 {
288 printk("%s: ran.\n", __func__);
289
290 (*((uint32_t *)param))++;
291 }
292
mayfly_ut_test(void * param)293 void mayfly_ut_test(void *param)
294 {
295 static uint32_t *count;
296 static memq_link_t link;
297 static struct mayfly mfy = {0, 0, &link, NULL, mayfly_ut_mfy};
298 uint32_t err;
299
300 printk("%s: req= %u, ack= %u\n", __func__, mfy._req, mfy._ack);
301
302 if (param) {
303 count = param;
304 }
305
306 mfy.param = count;
307
308 err = mayfly_enqueue(MAYFLY_CALL_ID_CALLER, MAYFLY_CALL_ID_CALLEE, 1,
309 &mfy);
310 if (err) {
311 printk("%s: FAILED (%u).\n", __func__, err);
312 } else {
313 printk("%s: SUCCESS.\n", __func__);
314 }
315 }
316
mayfly_ut_run_test(void)317 uint32_t mayfly_ut_run_test(void)
318 {
319 static memq_link_t link;
320 static struct mayfly mfy = {0, 0, &link, NULL, mayfly_ut_test};
321 uint32_t err;
322
323 printk("%s: req= %u, ack= %u\n", __func__, mfy._req, mfy._ack);
324
325 err = mayfly_enqueue(MAYFLY_CALL_ID_CALLEE, MAYFLY_CALL_ID_CALLER, 0,
326 &mfy);
327
328 if (err) {
329 printk("%s: FAILED.\n", __func__);
330 return err;
331 }
332
333 printk("%s: SUCCESS.\n", __func__);
334
335 return 0;
336 }
337
mayfly_ut(void)338 uint32_t mayfly_ut(void)
339 {
340 static uint32_t count;
341 static memq_link_t link;
342 static struct mayfly mfy = {0, 0, &link, &count, mayfly_ut_test};
343 uint32_t err;
344
345 printk("%s: req= %u, ack= %u\n", __func__, mfy._req, mfy._ack);
346
347 err = mayfly_enqueue(MAYFLY_CALL_ID_PROGRAM, MAYFLY_CALL_ID_CALLER, 0,
348 &mfy);
349
350 if (err) {
351 printk("%s: FAILED.\n", __func__);
352 return err;
353 }
354
355 printk("%s: count = %u.\n", __func__, count);
356 printk("%s: SUCCESS.\n", __func__);
357 return 0;
358 }
359 #endif /* MAYFLY_UT */
360