| /* | 
 |  * Copyright (c) 2010-2014 Wind River Systems, Inc. | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *     http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 |  | 
 | /** | 
 |  * @file | 
 |  * @brief Nanokernel fiber support primitives | 
 |  * | 
 |  * This module provides various nanokernel fiber related primitives, | 
 |  * either in the form of an actual function or an alias to a function. | 
 |  */ | 
 |  | 
 | #include <nano_private.h> | 
 | #include <nano_internal.h> | 
 | #include <string.h> | 
 | #include <toolchain.h> | 
 | #include <sections.h> | 
 |  | 
 | /** | 
 |  * | 
 |  * @brief Add a fiber to the list of runnable fibers | 
 |  * | 
 |  * The list of runnable fibers is maintained via a single linked list | 
 |  * in priority order. Numerically lower priorities represent higher priority | 
 |  * fibers. | 
 |  * | 
 |  * Interrupts must already be locked to ensure list cannot change | 
 |  * while this routine is executing! | 
 |  * | 
 |  * @return N/A | 
 |  */ | 
 | void _nano_fiber_ready(struct tcs *tcs) | 
 | { | 
 | 	struct tcs *pQ = (struct tcs *)&_nanokernel.fiber; | 
 |  | 
 | 	/* | 
 | 	 * Search until end of list or until a fiber with numerically | 
 | 	 * higher priority is located. | 
 | 	 */ | 
 |  | 
 | 	while (pQ->link && (tcs->prio >= pQ->link->prio)) { | 
 | 		pQ = pQ->link; | 
 | 	} | 
 |  | 
 | 	/* Insert fiber, following any equal priority fibers */ | 
 |  | 
 | 	tcs->link = pQ->link; | 
 | 	pQ->link = tcs; | 
 | } | 
 |  | 
 |  | 
 | /* currently the fiber and task implementations are identical */ | 
 |  | 
 | FUNC_ALIAS(_fiber_start, fiber_fiber_start, nano_thread_id_t); | 
 | FUNC_ALIAS(_fiber_start, task_fiber_start, nano_thread_id_t); | 
 | FUNC_ALIAS(_fiber_start, fiber_start, nano_thread_id_t); | 
 |  | 
 | nano_thread_id_t _fiber_start(char *pStack, | 
 | 		unsigned stackSize, /* stack size in bytes */ | 
 | 		nano_fiber_entry_t pEntry, | 
 | 		int parameter1, | 
 | 		int parameter2, | 
 | 		unsigned priority, | 
 | 		unsigned options) | 
 | { | 
 | 	struct tcs *tcs; | 
 | 	unsigned int imask; | 
 |  | 
 | 	tcs = (struct tcs *) pStack; | 
 | 	_new_thread(pStack, | 
 | 			stackSize, | 
 | 			NULL, | 
 | 			(_thread_entry_t)pEntry, | 
 | 			(void *)parameter1, | 
 | 			(void *)parameter2, | 
 | 			(void *)0, | 
 | 			priority, | 
 | 			options); | 
 |  | 
 | 	/* | 
 | 	 * _new_thread() has already set the flags depending on the 'options' | 
 | 	 * and 'priority' parameters passed to it | 
 | 	 */ | 
 |  | 
 | 	/* lock interrupts to prevent corruption of the runnable fiber list */ | 
 |  | 
 | 	imask = irq_lock(); | 
 |  | 
 | 	/* make the newly crafted TCS a runnable fiber */ | 
 |  | 
 | 	_nano_fiber_ready(tcs); | 
 |  | 
 | 	/* | 
 | 	 * Simply return to the caller if the current thread is FIBER, | 
 | 	 * otherwise swap into the newly created fiber | 
 | 	 */ | 
 |  | 
 | 	if ((_nanokernel.current->flags & TASK) == TASK) { | 
 | 		_Swap(imask); | 
 | 	} else { | 
 | 		irq_unlock(imask); | 
 | 	} | 
 |  | 
 | 	return tcs; | 
 | } | 
 |  | 
 | void fiber_yield(void) | 
 | { | 
 | 	unsigned int imask = irq_lock(); | 
 |  | 
 | 	if ((_nanokernel.fiber != (struct tcs *)NULL) && | 
 | 	    (_nanokernel.current->prio >= _nanokernel.fiber->prio)) { | 
 | 		/* | 
 | 		 * Reinsert current thread into the list of runnable threads, | 
 | 		 * and then swap to the thread at the head of the fiber list. | 
 | 		 */ | 
 |  | 
 | 		_nano_fiber_ready(_nanokernel.current); | 
 | 		_Swap(imask); | 
 | 	} else { | 
 | 		irq_unlock(imask); | 
 | 	} | 
 | } | 
 |  | 
 | /** | 
 |  * | 
 |  * @brief Pass control from the currently executing fiber | 
 |  * | 
 |  * This routine is used when a fiber voluntarily gives up control of the CPU. | 
 |  * | 
 |  * This routine can only be called from a fiber. | 
 |  * | 
 |  * @return This function never returns | 
 |  */ | 
 | FUNC_NORETURN void _nano_fiber_swap(void) | 
 | { | 
 | 	unsigned int imask; | 
 |  | 
 | 	/* | 
 | 	 * Since the currently running fiber is not queued onto the runnable | 
 | 	 * fiber list, simply performing a _Swap() shall initiate a context | 
 | 	 * switch to the highest priority fiber, or the highest priority task | 
 | 	 * if there are no runnable fibers. | 
 | 	 */ | 
 |  | 
 | 	imask = irq_lock(); | 
 | 	_Swap(imask); | 
 |  | 
 | 	/* | 
 | 	 * Compiler can't know that _Swap() won't return and will issue a | 
 | 	 * warning unless we explicitly tell it that control never gets this | 
 | 	 * far. | 
 | 	 */ | 
 |  | 
 | 	CODE_UNREACHABLE; | 
 | } | 
 |  | 
 | #ifndef CONFIG_ARCH_HAS_NANO_FIBER_ABORT | 
 | FUNC_NORETURN void fiber_abort(void) | 
 | { | 
 | 	/* Do normal thread exit cleanup, then give up CPU control */ | 
 |  | 
 | 	_thread_exit(_nanokernel.current); | 
 | 	_nano_fiber_swap(); | 
 | } | 
 | #endif | 
 |  | 
 | #ifdef CONFIG_NANO_TIMEOUTS | 
 |  | 
 | #include <wait_q.h> | 
 |  | 
 | FUNC_ALIAS(fiber_delayed_start, fiber_fiber_delayed_start, nano_thread_id_t); | 
 | FUNC_ALIAS(fiber_delayed_start, task_fiber_delayed_start, nano_thread_id_t); | 
 |  | 
 | nano_thread_id_t fiber_delayed_start(char *stack, | 
 | 			  unsigned int stack_size_in_bytes, | 
 | 			  nano_fiber_entry_t entry_point, int param1, | 
 | 			  int param2, unsigned int priority, | 
 | 			  unsigned int options, int32_t timeout_in_ticks) | 
 | { | 
 | 	unsigned int key; | 
 | 	struct tcs *tcs; | 
 |  | 
 | 	tcs = (struct tcs *)stack; | 
 | 	_new_thread(stack, stack_size_in_bytes, NULL, (_thread_entry_t)entry_point, | 
 | 		(void *)param1, (void *)param2, (void *)0, priority, options); | 
 |  | 
 | 	key = irq_lock(); | 
 |  | 
 | 	_nano_timeout_add(tcs, NULL, timeout_in_ticks); | 
 |  | 
 | 	irq_unlock(key); | 
 | 	return tcs; | 
 | } | 
 |  | 
 | FUNC_ALIAS(fiber_delayed_start_cancel, fiber_fiber_delayed_start_cancel, void); | 
 | FUNC_ALIAS(fiber_delayed_start_cancel, task_fiber_delayed_start_cancel, void); | 
 |  | 
 | void fiber_delayed_start_cancel(nano_thread_id_t handle) | 
 | { | 
 | 	struct tcs *cancelled_tcs = handle; | 
 | 	int key = irq_lock(); | 
 |  | 
 | 	_nano_timeout_abort(cancelled_tcs); | 
 | 	_thread_exit(cancelled_tcs); | 
 |  | 
 | 	irq_unlock(key); | 
 | } | 
 |  | 
 | #endif /* CONFIG_NANO_TIMEOUTS */ |