1 /*
2 * Copyright (c) 2018 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <kernel_internal.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <zephyr/sys/atomic.h>
12 #include <zephyr/debug/stack.h>
13 #include <zephyr/portability/cmsis_types.h>
14 #include "wrapper.h"
15
16 static const osThreadAttr_t init_thread_attrs = {
17 .name = "ZephyrThread",
18 .attr_bits = osThreadDetached,
19 .cb_mem = NULL,
20 .cb_size = 0,
21 .stack_mem = NULL,
22 .stack_size = 0,
23 .priority = osPriorityNormal,
24 .tz_module = 0,
25 .reserved = 0,
26 };
27
28 static sys_dlist_t thread_list;
29
30 static atomic_t num_dynamic_cb;
31 #if CONFIG_CMSIS_V2_THREAD_MAX_COUNT != 0
32 static struct cmsis_rtos_thread_cb cmsis_rtos_thread_cb_pool[CONFIG_CMSIS_V2_THREAD_MAX_COUNT];
33 #endif
34
35 static atomic_t num_dynamic_stack;
36 #if CONFIG_CMSIS_V2_THREAD_DYNAMIC_MAX_COUNT != 0
37 static K_THREAD_STACK_ARRAY_DEFINE(cmsis_rtos_thread_stack_pool,
38 CONFIG_CMSIS_V2_THREAD_DYNAMIC_MAX_COUNT,
39 CONFIG_CMSIS_V2_THREAD_DYNAMIC_STACK_SIZE);
40 #endif
41
_is_thread_cmsis_inactive(struct k_thread * thread)42 static inline int _is_thread_cmsis_inactive(struct k_thread *thread)
43 {
44 uint8_t state = thread->base.thread_state;
45
46 return state & _THREAD_DEAD;
47 }
48
zephyr_to_cmsis_priority(uint32_t z_prio)49 static inline uint32_t zephyr_to_cmsis_priority(uint32_t z_prio)
50 {
51 return (osPriorityISR - z_prio);
52 }
53
cmsis_to_zephyr_priority(uint32_t c_prio)54 static inline uint32_t cmsis_to_zephyr_priority(uint32_t c_prio)
55 {
56 return (osPriorityISR - c_prio);
57 }
58
zephyr_thread_wrapper(void * arg1,void * arg2,void * arg3)59 static void zephyr_thread_wrapper(void *arg1, void *arg2, void *arg3)
60 {
61 osThreadFunc_t fun_ptr = arg3;
62 fun_ptr(arg1);
63 }
64
is_cmsis_rtos_v2_thread(void * thread_id)65 void *is_cmsis_rtos_v2_thread(void *thread_id)
66 {
67 sys_dnode_t *pnode;
68 struct cmsis_rtos_thread_cb *itr;
69
70 SYS_DLIST_FOR_EACH_NODE(&thread_list, pnode) {
71 itr = CONTAINER_OF(pnode, struct cmsis_rtos_thread_cb, node);
72
73 if ((void *)itr == thread_id) {
74 return itr;
75 }
76 }
77
78 return NULL;
79 }
80
get_cmsis_thread_id(k_tid_t tid)81 osThreadId_t get_cmsis_thread_id(k_tid_t tid)
82 {
83 sys_dnode_t *pnode;
84 struct cmsis_rtos_thread_cb *itr;
85
86 if (tid != NULL) {
87 SYS_DLIST_FOR_EACH_NODE(&thread_list, pnode) {
88 itr = CONTAINER_OF(pnode, struct cmsis_rtos_thread_cb, node);
89
90 if (&itr->z_thread == tid) {
91 return (osThreadId_t)itr;
92 }
93 }
94 }
95
96 return NULL;
97 }
98
99 /**
100 * @brief Create a thread and add it to Active Threads.
101 */
osThreadNew(osThreadFunc_t threadfunc,void * arg,const osThreadAttr_t * attr)102 osThreadId_t osThreadNew(osThreadFunc_t threadfunc, void *arg, const osThreadAttr_t *attr)
103 {
104 int32_t prio;
105 osPriority_t cv2_prio;
106 struct cmsis_rtos_thread_cb *tid;
107 static uint32_t one_time;
108 void *stack;
109 size_t stack_size;
110
111 if (k_is_in_isr()) {
112 return NULL;
113 }
114
115 if (attr == NULL) {
116 attr = &init_thread_attrs;
117 }
118
119 if (attr->priority == osPriorityNone) {
120 cv2_prio = osPriorityNormal;
121 } else {
122 cv2_prio = attr->priority;
123 }
124
125 if (attr->cb_mem == NULL && num_dynamic_cb >= CONFIG_CMSIS_V2_THREAD_MAX_COUNT) {
126 return NULL;
127 }
128
129 if (attr->stack_mem == NULL &&
130 num_dynamic_stack >= CONFIG_CMSIS_V2_THREAD_DYNAMIC_MAX_COUNT) {
131 return NULL;
132 }
133
134 BUILD_ASSERT(osPriorityISR <= CONFIG_NUM_PREEMPT_PRIORITIES,
135 "Configure NUM_PREEMPT_PRIORITIES to at least osPriorityISR");
136
137 BUILD_ASSERT(CONFIG_CMSIS_V2_THREAD_DYNAMIC_STACK_SIZE <=
138 CONFIG_CMSIS_V2_THREAD_MAX_STACK_SIZE,
139 "Default dynamic thread stack size cannot exceed max stack size");
140
141 __ASSERT(attr->stack_size <= CONFIG_CMSIS_V2_THREAD_MAX_STACK_SIZE, "invalid stack size\n");
142
143 __ASSERT((cv2_prio >= osPriorityIdle) && (cv2_prio <= osPriorityISR), "invalid priority\n");
144
145 if (attr->stack_mem != NULL && attr->stack_size == 0) {
146 return NULL;
147 }
148
149 #if CONFIG_CMSIS_V2_THREAD_MAX_COUNT != 0
150 if (attr->cb_mem == NULL) {
151 uint32_t this_dynamic_cb;
152 this_dynamic_cb = atomic_inc(&num_dynamic_cb);
153 tid = &cmsis_rtos_thread_cb_pool[this_dynamic_cb];
154 } else
155 #endif
156 {
157 tid = (struct cmsis_rtos_thread_cb *)attr->cb_mem;
158 }
159
160 tid->attr_bits = attr->attr_bits;
161
162 #if CONFIG_CMSIS_V2_THREAD_DYNAMIC_MAX_COUNT != 0
163 if (attr->stack_mem == NULL) {
164 uint32_t this_dynamic_stack;
165 __ASSERT(CONFIG_CMSIS_V2_THREAD_DYNAMIC_STACK_SIZE > 0,
166 "dynamic stack size must be configured to be non-zero\n");
167 this_dynamic_stack = atomic_inc(&num_dynamic_stack);
168 stack_size = CONFIG_CMSIS_V2_THREAD_DYNAMIC_STACK_SIZE;
169 stack = cmsis_rtos_thread_stack_pool[this_dynamic_stack];
170 } else
171 #endif
172 {
173 stack_size = attr->stack_size;
174 stack = attr->stack_mem;
175 }
176
177 k_poll_signal_init(&tid->poll_signal);
178 k_poll_event_init(&tid->poll_event, K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY,
179 &tid->poll_signal);
180 tid->signal_results = 0U;
181
182 /* TODO: Do this somewhere only once */
183 if (one_time == 0U) {
184 sys_dlist_init(&thread_list);
185 one_time = 1U;
186 }
187
188 sys_dlist_append(&thread_list, &tid->node);
189
190 prio = cmsis_to_zephyr_priority(cv2_prio);
191
192 (void)k_thread_create(&tid->z_thread, stack, stack_size, zephyr_thread_wrapper, (void *)arg,
193 NULL, threadfunc, prio, 0, K_NO_WAIT);
194
195 const char *name = (attr->name == NULL) ? init_thread_attrs.name : attr->name;
196
197 k_thread_name_set(&tid->z_thread, name);
198
199 return (osThreadId_t)tid;
200 }
201
202 /**
203 * @brief Get name of a thread.
204 * This function may be called from Interrupt Service Routines.
205 */
osThreadGetName(osThreadId_t thread_id)206 const char *osThreadGetName(osThreadId_t thread_id)
207 {
208 struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
209
210 if (tid == NULL) {
211 return NULL;
212 }
213 return k_thread_name_get(&tid->z_thread);
214 }
215
216 /**
217 * @brief Return the thread ID of the current running thread.
218 */
osThreadGetId(void)219 osThreadId_t osThreadGetId(void)
220 {
221 k_tid_t tid = k_current_get();
222
223 return get_cmsis_thread_id(tid);
224 }
225
226 /**
227 * @brief Get current priority of an active thread.
228 */
osThreadGetPriority(osThreadId_t thread_id)229 osPriority_t osThreadGetPriority(osThreadId_t thread_id)
230 {
231 struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
232 uint32_t priority;
233
234 if (k_is_in_isr() || (tid == NULL) || (is_cmsis_rtos_v2_thread(tid) == NULL) ||
235 (_is_thread_cmsis_inactive(&tid->z_thread))) {
236 return osPriorityError;
237 }
238
239 priority = k_thread_priority_get(&tid->z_thread);
240 return zephyr_to_cmsis_priority(priority);
241 }
242
243 /**
244 * @brief Change priority of an active thread.
245 */
osThreadSetPriority(osThreadId_t thread_id,osPriority_t priority)246 osStatus_t osThreadSetPriority(osThreadId_t thread_id, osPriority_t priority)
247 {
248 struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
249
250 if ((tid == NULL) || (is_cmsis_rtos_v2_thread(tid) == NULL) ||
251 (priority <= osPriorityNone) || (priority > osPriorityISR)) {
252 return osErrorParameter;
253 }
254
255 if (k_is_in_isr()) {
256 return osErrorISR;
257 }
258
259 if (_is_thread_cmsis_inactive(&tid->z_thread)) {
260 return osErrorResource;
261 }
262
263 k_thread_priority_set((k_tid_t)&tid->z_thread, cmsis_to_zephyr_priority(priority));
264
265 return osOK;
266 }
267
268 /**
269 * @brief Get current thread state of a thread.
270 */
osThreadGetState(osThreadId_t thread_id)271 osThreadState_t osThreadGetState(osThreadId_t thread_id)
272 {
273 struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
274 osThreadState_t state;
275
276 if (k_is_in_isr() || (tid == NULL) || (is_cmsis_rtos_v2_thread(tid) == NULL)) {
277 return osThreadError;
278 }
279
280 switch (tid->z_thread.base.thread_state) {
281 case _THREAD_DUMMY:
282 state = osThreadError;
283 break;
284 case _THREAD_DEAD:
285 state = osThreadTerminated;
286 break;
287 case _THREAD_SUSPENDED:
288 case _THREAD_SLEEPING:
289 case _THREAD_PENDING:
290 state = osThreadBlocked;
291 break;
292 case _THREAD_QUEUED:
293 state = osThreadReady;
294 break;
295 default:
296 state = osThreadError;
297 break;
298 }
299
300 if (osThreadGetId() == thread_id) {
301 state = osThreadRunning;
302 }
303
304 return state;
305 }
306
307 /**
308 * @brief Pass control to next thread that is in READY state.
309 */
osThreadYield(void)310 osStatus_t osThreadYield(void)
311 {
312 if (k_is_in_isr()) {
313 return osErrorISR;
314 }
315
316 k_yield();
317 return osOK;
318 }
319
320 /**
321 * @brief Get stack size of a thread.
322 */
osThreadGetStackSize(osThreadId_t thread_id)323 uint32_t osThreadGetStackSize(osThreadId_t thread_id)
324 {
325 struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
326
327 __ASSERT(tid, "");
328 __ASSERT(is_cmsis_rtos_v2_thread(tid), "");
329 __ASSERT(!k_is_in_isr(), "");
330
331 return tid->z_thread.stack_info.size;
332 }
333
334 /**
335 * @brief Get available stack space of a thread based on stack watermark
336 * recording during execution.
337 */
osThreadGetStackSpace(osThreadId_t thread_id)338 uint32_t osThreadGetStackSpace(osThreadId_t thread_id)
339 {
340 struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
341 size_t unused;
342 int ret;
343
344 __ASSERT(tid, "");
345 __ASSERT(is_cmsis_rtos_v2_thread(tid), "");
346 __ASSERT(!k_is_in_isr(), "");
347
348 ret = k_thread_stack_space_get(&tid->z_thread, &unused);
349 if (ret != 0) {
350 unused = 0;
351 }
352
353 return (uint32_t)unused;
354 }
355
356 /**
357 * @brief Suspend execution of a thread.
358 */
osThreadSuspend(osThreadId_t thread_id)359 osStatus_t osThreadSuspend(osThreadId_t thread_id)
360 {
361 struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
362
363 if ((tid == NULL) || (is_cmsis_rtos_v2_thread(tid) == NULL)) {
364 return osErrorParameter;
365 }
366
367 if (k_is_in_isr()) {
368 return osErrorISR;
369 }
370
371 if (_is_thread_cmsis_inactive(&tid->z_thread)) {
372 return osErrorResource;
373 }
374
375 k_thread_suspend(&tid->z_thread);
376
377 return osOK;
378 }
379
380 /**
381 * @brief Resume execution of a thread.
382 */
osThreadResume(osThreadId_t thread_id)383 osStatus_t osThreadResume(osThreadId_t thread_id)
384 {
385 struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
386
387 if ((tid == NULL) || (is_cmsis_rtos_v2_thread(tid) == NULL)) {
388 return osErrorParameter;
389 }
390
391 if (k_is_in_isr()) {
392 return osErrorISR;
393 }
394
395 if (_is_thread_cmsis_inactive(&tid->z_thread)) {
396 return osErrorResource;
397 }
398
399 k_thread_resume(&tid->z_thread);
400
401 return osOK;
402 }
403
404 /**
405 * @brief Detach a thread (thread storage can be reclaimed when thread
406 * terminates).
407 */
osThreadDetach(osThreadId_t thread_id)408 osStatus_t osThreadDetach(osThreadId_t thread_id)
409 {
410 struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
411
412 if ((tid == NULL) || (is_cmsis_rtos_v2_thread(tid) == NULL)) {
413 return osErrorParameter;
414 }
415
416 if (k_is_in_isr()) {
417 return osErrorISR;
418 }
419
420 if (_is_thread_cmsis_inactive(&tid->z_thread)) {
421 return osErrorResource;
422 }
423
424 __ASSERT(tid->attr_bits != osThreadDetached,
425 "Thread already detached, behaviour undefined.");
426
427 tid->attr_bits = osThreadDetached;
428
429 return osOK;
430 }
431
432 /**
433 * @brief Wait for specified thread to terminate.
434 */
osThreadJoin(osThreadId_t thread_id)435 osStatus_t osThreadJoin(osThreadId_t thread_id)
436 {
437 struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
438 int ret = 0;
439
440 if ((tid == NULL) || (is_cmsis_rtos_v2_thread(tid) == NULL)) {
441 return osErrorParameter;
442 }
443
444 if (k_is_in_isr()) {
445 return osErrorISR;
446 }
447
448 if (_is_thread_cmsis_inactive(&tid->z_thread)) {
449 return osErrorResource;
450 }
451
452 if (tid->attr_bits != osThreadJoinable) {
453 return osErrorResource;
454 }
455
456 ret = k_thread_join(&tid->z_thread, K_FOREVER);
457 return (ret == 0) ? osOK : osErrorResource;
458 }
459
460 /**
461 * @brief Terminate execution of current running thread.
462 */
osThreadExit(void)463 __NO_RETURN void osThreadExit(void)
464 {
465 struct cmsis_rtos_thread_cb *tid;
466
467 __ASSERT(!k_is_in_isr(), "");
468 tid = osThreadGetId();
469
470 k_thread_abort((k_tid_t)&tid->z_thread);
471
472 CODE_UNREACHABLE;
473 }
474
475 /**
476 * @brief Terminate execution of a thread.
477 */
osThreadTerminate(osThreadId_t thread_id)478 osStatus_t osThreadTerminate(osThreadId_t thread_id)
479 {
480 struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
481
482 if ((tid == NULL) || (is_cmsis_rtos_v2_thread(tid) == NULL)) {
483 return osErrorParameter;
484 }
485
486 if (k_is_in_isr()) {
487 return osErrorISR;
488 }
489
490 if (_is_thread_cmsis_inactive(&tid->z_thread)) {
491 return osErrorResource;
492 }
493
494 k_thread_abort((k_tid_t)&tid->z_thread);
495 return osOK;
496 }
497
498 /**
499 * @brief Get number of active threads.
500 */
osThreadGetCount(void)501 uint32_t osThreadGetCount(void)
502 {
503 struct k_thread *thread;
504 uint32_t count = 0U;
505
506 __ASSERT(!k_is_in_isr(), "");
507 for (thread = _kernel.threads; thread; thread = thread->next_thread) {
508 if (get_cmsis_thread_id(thread) && z_is_thread_queued(thread)) {
509 count++;
510 }
511 }
512
513 return count;
514 }
515
516 /**
517 * @brief Enumerate active threads.
518 */
osThreadEnumerate(osThreadId_t * thread_array,uint32_t array_items)519 uint32_t osThreadEnumerate(osThreadId_t *thread_array, uint32_t array_items)
520 {
521 struct k_thread *thread;
522 uint32_t count = 0U;
523 osThreadId_t tid;
524
525 __ASSERT(!k_is_in_isr(), "");
526 __ASSERT(thread_array != NULL, "");
527 __ASSERT(array_items, "");
528
529 for (thread = _kernel.threads; thread; thread = thread->next_thread) {
530 if (count == array_items) {
531 break;
532 }
533
534 tid = get_cmsis_thread_id(thread);
535 if (tid != NULL) {
536 thread_array[count] = tid;
537 count++;
538 }
539 }
540
541 return (count);
542 }
543