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