1 /*
2  * Copyright (c) 2018 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <ksched.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 	if (attr->name == NULL) {
196 		strncpy(tid->name, init_thread_attrs.name, sizeof(tid->name) - 1);
197 	} else {
198 		strncpy(tid->name, attr->name, sizeof(tid->name) - 1);
199 	}
200 
201 	k_thread_name_set(&tid->z_thread, tid->name);
202 
203 	return (osThreadId_t)tid;
204 }
205 
206 /**
207  * @brief Get name of a thread.
208  */
osThreadGetName(osThreadId_t thread_id)209 const char *osThreadGetName(osThreadId_t thread_id)
210 {
211 	const char *name = NULL;
212 
213 	if (k_is_in_isr() || (thread_id == NULL)) {
214 		name = NULL;
215 	} else {
216 		if (is_cmsis_rtos_v2_thread(thread_id) == NULL) {
217 			name = NULL;
218 		} else {
219 			struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
220 
221 			name = k_thread_name_get(&tid->z_thread);
222 		}
223 	}
224 
225 	return name;
226 }
227 
228 /**
229  * @brief Return the thread ID of the current running thread.
230  */
osThreadGetId(void)231 osThreadId_t osThreadGetId(void)
232 {
233 	k_tid_t tid = k_current_get();
234 
235 	return get_cmsis_thread_id(tid);
236 }
237 
238 /**
239  * @brief Get current priority of an active thread.
240  */
osThreadGetPriority(osThreadId_t thread_id)241 osPriority_t osThreadGetPriority(osThreadId_t thread_id)
242 {
243 	struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
244 	uint32_t priority;
245 
246 	if (k_is_in_isr() || (tid == NULL) || (is_cmsis_rtos_v2_thread(tid) == NULL) ||
247 	    (_is_thread_cmsis_inactive(&tid->z_thread))) {
248 		return osPriorityError;
249 	}
250 
251 	priority = k_thread_priority_get(&tid->z_thread);
252 	return zephyr_to_cmsis_priority(priority);
253 }
254 
255 /**
256  * @brief Change priority of an active thread.
257  */
osThreadSetPriority(osThreadId_t thread_id,osPriority_t priority)258 osStatus_t osThreadSetPriority(osThreadId_t thread_id, osPriority_t priority)
259 {
260 	struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
261 
262 	if ((tid == NULL) || (is_cmsis_rtos_v2_thread(tid) == NULL) ||
263 	    (priority <= osPriorityNone) || (priority > osPriorityISR)) {
264 		return osErrorParameter;
265 	}
266 
267 	if (k_is_in_isr()) {
268 		return osErrorISR;
269 	}
270 
271 	if (_is_thread_cmsis_inactive(&tid->z_thread)) {
272 		return osErrorResource;
273 	}
274 
275 	k_thread_priority_set((k_tid_t)&tid->z_thread, cmsis_to_zephyr_priority(priority));
276 
277 	return osOK;
278 }
279 
280 /**
281  * @brief Get current thread state of a thread.
282  */
osThreadGetState(osThreadId_t thread_id)283 osThreadState_t osThreadGetState(osThreadId_t thread_id)
284 {
285 	struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
286 	osThreadState_t state;
287 
288 	if (k_is_in_isr() || (tid == NULL) || (is_cmsis_rtos_v2_thread(tid) == NULL)) {
289 		return osThreadError;
290 	}
291 
292 	switch (tid->z_thread.base.thread_state) {
293 	case _THREAD_DUMMY:
294 		state = osThreadError;
295 		break;
296 	case _THREAD_DEAD:
297 		state = osThreadTerminated;
298 		break;
299 	case _THREAD_SUSPENDED:
300 	case _THREAD_SLEEPING:
301 	case _THREAD_PENDING:
302 		state = osThreadBlocked;
303 		break;
304 	case _THREAD_QUEUED:
305 		state = osThreadReady;
306 		break;
307 	default:
308 		state = osThreadError;
309 		break;
310 	}
311 
312 	if (osThreadGetId() == thread_id) {
313 		state = osThreadRunning;
314 	}
315 
316 	return state;
317 }
318 
319 /**
320  * @brief Pass control to next thread that is in READY state.
321  */
osThreadYield(void)322 osStatus_t osThreadYield(void)
323 {
324 	if (k_is_in_isr()) {
325 		return osErrorISR;
326 	}
327 
328 	k_yield();
329 	return osOK;
330 }
331 
332 /**
333  * @brief Get stack size of a thread.
334  */
osThreadGetStackSize(osThreadId_t thread_id)335 uint32_t osThreadGetStackSize(osThreadId_t thread_id)
336 {
337 	struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
338 
339 	__ASSERT(tid, "");
340 	__ASSERT(is_cmsis_rtos_v2_thread(tid), "");
341 	__ASSERT(!k_is_in_isr(), "");
342 
343 	return tid->z_thread.stack_info.size;
344 }
345 
346 /**
347  * @brief Get available stack space of a thread based on stack watermark
348  *        recording during execution.
349  */
osThreadGetStackSpace(osThreadId_t thread_id)350 uint32_t osThreadGetStackSpace(osThreadId_t thread_id)
351 {
352 	struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
353 	size_t unused;
354 	int ret;
355 
356 	__ASSERT(tid, "");
357 	__ASSERT(is_cmsis_rtos_v2_thread(tid), "");
358 	__ASSERT(!k_is_in_isr(), "");
359 
360 	ret = k_thread_stack_space_get(&tid->z_thread, &unused);
361 	if (ret != 0) {
362 		unused = 0;
363 	}
364 
365 	return (uint32_t)unused;
366 }
367 
368 /**
369  * @brief Suspend execution of a thread.
370  */
osThreadSuspend(osThreadId_t thread_id)371 osStatus_t osThreadSuspend(osThreadId_t thread_id)
372 {
373 	struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
374 
375 	if ((tid == NULL) || (is_cmsis_rtos_v2_thread(tid) == NULL)) {
376 		return osErrorParameter;
377 	}
378 
379 	if (k_is_in_isr()) {
380 		return osErrorISR;
381 	}
382 
383 	if (_is_thread_cmsis_inactive(&tid->z_thread)) {
384 		return osErrorResource;
385 	}
386 
387 	k_thread_suspend(&tid->z_thread);
388 
389 	return osOK;
390 }
391 
392 /**
393  * @brief Resume execution of a thread.
394  */
osThreadResume(osThreadId_t thread_id)395 osStatus_t osThreadResume(osThreadId_t thread_id)
396 {
397 	struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
398 
399 	if ((tid == NULL) || (is_cmsis_rtos_v2_thread(tid) == NULL)) {
400 		return osErrorParameter;
401 	}
402 
403 	if (k_is_in_isr()) {
404 		return osErrorISR;
405 	}
406 
407 	if (_is_thread_cmsis_inactive(&tid->z_thread)) {
408 		return osErrorResource;
409 	}
410 
411 	k_thread_resume(&tid->z_thread);
412 
413 	return osOK;
414 }
415 
416 /**
417  * @brief Detach a thread (thread storage can be reclaimed when thread
418  *        terminates).
419  */
osThreadDetach(osThreadId_t thread_id)420 osStatus_t osThreadDetach(osThreadId_t thread_id)
421 {
422 	struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
423 
424 	if ((tid == NULL) || (is_cmsis_rtos_v2_thread(tid) == NULL)) {
425 		return osErrorParameter;
426 	}
427 
428 	if (k_is_in_isr()) {
429 		return osErrorISR;
430 	}
431 
432 	if (_is_thread_cmsis_inactive(&tid->z_thread)) {
433 		return osErrorResource;
434 	}
435 
436 	__ASSERT(tid->attr_bits != osThreadDetached,
437 		 "Thread already detached, behaviour undefined.");
438 
439 	tid->attr_bits = osThreadDetached;
440 
441 	return osOK;
442 }
443 
444 /**
445  * @brief Wait for specified thread to terminate.
446  */
osThreadJoin(osThreadId_t thread_id)447 osStatus_t osThreadJoin(osThreadId_t thread_id)
448 {
449 	struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
450 	int ret = 0;
451 
452 	if ((tid == NULL) || (is_cmsis_rtos_v2_thread(tid) == NULL)) {
453 		return osErrorParameter;
454 	}
455 
456 	if (k_is_in_isr()) {
457 		return osErrorISR;
458 	}
459 
460 	if (_is_thread_cmsis_inactive(&tid->z_thread)) {
461 		return osErrorResource;
462 	}
463 
464 	if (tid->attr_bits != osThreadJoinable) {
465 		return osErrorResource;
466 	}
467 
468 	ret = k_thread_join(&tid->z_thread, K_FOREVER);
469 	return (ret == 0) ? osOK : osErrorResource;
470 }
471 
472 /**
473  * @brief Terminate execution of current running thread.
474  */
osThreadExit(void)475 __NO_RETURN void osThreadExit(void)
476 {
477 	struct cmsis_rtos_thread_cb *tid;
478 
479 	__ASSERT(!k_is_in_isr(), "");
480 	tid = osThreadGetId();
481 
482 	k_thread_abort((k_tid_t)&tid->z_thread);
483 
484 	CODE_UNREACHABLE;
485 }
486 
487 /**
488  * @brief Terminate execution of a thread.
489  */
osThreadTerminate(osThreadId_t thread_id)490 osStatus_t osThreadTerminate(osThreadId_t thread_id)
491 {
492 	struct cmsis_rtos_thread_cb *tid = (struct cmsis_rtos_thread_cb *)thread_id;
493 
494 	if ((tid == NULL) || (is_cmsis_rtos_v2_thread(tid) == NULL)) {
495 		return osErrorParameter;
496 	}
497 
498 	if (k_is_in_isr()) {
499 		return osErrorISR;
500 	}
501 
502 	if (_is_thread_cmsis_inactive(&tid->z_thread)) {
503 		return osErrorResource;
504 	}
505 
506 	k_thread_abort((k_tid_t)&tid->z_thread);
507 	return osOK;
508 }
509 
510 /**
511  * @brief Get number of active threads.
512  */
osThreadGetCount(void)513 uint32_t osThreadGetCount(void)
514 {
515 	struct k_thread *thread;
516 	uint32_t count = 0U;
517 
518 	__ASSERT(!k_is_in_isr(), "");
519 	for (thread = _kernel.threads; thread; thread = thread->next_thread) {
520 		if (get_cmsis_thread_id(thread) && z_is_thread_queued(thread)) {
521 			count++;
522 		}
523 	}
524 
525 	return count;
526 }
527 
528 /**
529  * @brief Enumerate active threads.
530  */
osThreadEnumerate(osThreadId_t * thread_array,uint32_t array_items)531 uint32_t osThreadEnumerate(osThreadId_t *thread_array, uint32_t array_items)
532 {
533 	struct k_thread *thread;
534 	uint32_t count = 0U;
535 	osThreadId_t tid;
536 
537 	__ASSERT(!k_is_in_isr(), "");
538 	__ASSERT(thread_array != NULL, "");
539 	__ASSERT(array_items, "");
540 
541 	for (thread = _kernel.threads; thread; thread = thread->next_thread) {
542 		if (count == array_items) {
543 			break;
544 		}
545 
546 		tid = get_cmsis_thread_id(thread);
547 		if (tid != NULL) {
548 			thread_array[count] = tid;
549 			count++;
550 		}
551 	}
552 
553 	return (count);
554 }
555