|  | /* | 
|  | * Copyright (c) 2010-2015 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 dynamic-size LIFO queue object | 
|  | * | 
|  | * This module provides the nanokernel LIFO object implementation, including | 
|  | * the following APIs: | 
|  | * | 
|  | *    nano_lifo_init | 
|  | *    nano_fiber_lifo_put, nano_task_lifo_put, nano_isr_lifo_put | 
|  | *    nano_fiber_lifo_get, nano_task_lifo_get, nano_isr_lifo_get | 
|  | *    nano_lifo_get | 
|  | */ | 
|  |  | 
|  | /** INTERNAL | 
|  | * | 
|  | * In some cases the compiler "alias" attribute is used to map two or more | 
|  | * APIs to the same function, since they have identical implementations. | 
|  | */ | 
|  |  | 
|  | #include <nano_private.h> | 
|  | #include <misc/debug/object_tracing_common.h> | 
|  | #include <toolchain.h> | 
|  | #include <sections.h> | 
|  | #include <wait_q.h> | 
|  |  | 
|  | /** INTERNAL | 
|  | * | 
|  | * Although the existing implementation will support invocation from an ISR | 
|  | * context, for future flexibility, this API will be restricted from ISR | 
|  | * level invocation. | 
|  | */ | 
|  | void nano_lifo_init(struct nano_lifo *lifo) | 
|  | { | 
|  | lifo->list = (void *) 0; | 
|  | _nano_wait_q_init(&lifo->wait_q); | 
|  | SYS_TRACING_OBJ_INIT(nano_lifo, lifo); | 
|  | } | 
|  |  | 
|  | FUNC_ALIAS(_lifo_put_non_preemptible, nano_isr_lifo_put, void); | 
|  | FUNC_ALIAS(_lifo_put_non_preemptible, nano_fiber_lifo_put, void); | 
|  |  | 
|  | /** INTERNAL | 
|  | * | 
|  | * This function is capable of supporting invocations from both a fiber and an | 
|  | * ISR context.  However, the nano_isr_lifo_put and nano_fiber_lifo_put aliases | 
|  | * are created to support any required implementation differences in the future | 
|  | * without introducing a source code migration issue. | 
|  | */ | 
|  | void _lifo_put_non_preemptible(struct nano_lifo *lifo, void *data) | 
|  | { | 
|  | struct tcs *tcs; | 
|  | unsigned int imask; | 
|  |  | 
|  | imask = irq_lock(); | 
|  | tcs = _nano_wait_q_remove(&lifo->wait_q); | 
|  | if (tcs) { | 
|  | _nano_timeout_abort(tcs); | 
|  | fiberRtnValueSet(tcs, (unsigned int) data); | 
|  | } else { | 
|  | *(void **) data = lifo->list; | 
|  | lifo->list = data; | 
|  | } | 
|  |  | 
|  | irq_unlock(imask); | 
|  | } | 
|  |  | 
|  | void nano_task_lifo_put(struct nano_lifo *lifo, void *data) | 
|  | { | 
|  | struct tcs *tcs; | 
|  | unsigned int imask; | 
|  |  | 
|  | imask = irq_lock(); | 
|  | tcs = _nano_wait_q_remove(&lifo->wait_q); | 
|  | if (tcs) { | 
|  | _nano_timeout_abort(tcs); | 
|  | fiberRtnValueSet(tcs, (unsigned int) data); | 
|  | _Swap(imask); | 
|  | return; | 
|  | } | 
|  |  | 
|  | *(void **) data = lifo->list; | 
|  | lifo->list = data; | 
|  |  | 
|  | irq_unlock(imask); | 
|  | } | 
|  |  | 
|  | void nano_lifo_put(struct nano_lifo *lifo, void *data) | 
|  | { | 
|  | static void (*func[3])(struct nano_lifo *, void *) = { | 
|  | nano_isr_lifo_put, | 
|  | nano_fiber_lifo_put, | 
|  | nano_task_lifo_put | 
|  | }; | 
|  |  | 
|  | func[sys_execution_context_type_get()](lifo, data); | 
|  | } | 
|  |  | 
|  | FUNC_ALIAS(_lifo_get, nano_isr_lifo_get, void *); | 
|  | FUNC_ALIAS(_lifo_get, nano_fiber_lifo_get, void *); | 
|  |  | 
|  | void *_lifo_get(struct nano_lifo *lifo, int32_t timeout_in_ticks) | 
|  | { | 
|  | void *data = NULL; | 
|  | unsigned int imask; | 
|  |  | 
|  | imask = irq_lock(); | 
|  |  | 
|  | if (likely(lifo->list != NULL)) { | 
|  | data = lifo->list; | 
|  | lifo->list = *(void **) data; | 
|  | } else if (timeout_in_ticks != TICKS_NONE) { | 
|  | _NANO_TIMEOUT_ADD(&lifo->wait_q, timeout_in_ticks); | 
|  | _nano_wait_q_put(&lifo->wait_q); | 
|  | data = (void *) _Swap(imask); | 
|  | return data; | 
|  | } | 
|  |  | 
|  | irq_unlock(imask); | 
|  | return data; | 
|  | } | 
|  |  | 
|  | void *nano_task_lifo_get(struct nano_lifo *lifo, int32_t timeout_in_ticks) | 
|  | { | 
|  | int64_t cur_ticks; | 
|  | int64_t limit = 0x7fffffffffffffffll; | 
|  | unsigned int imask; | 
|  |  | 
|  | imask = irq_lock(); | 
|  | cur_ticks = _NANO_TIMEOUT_TICK_GET(); | 
|  | if (timeout_in_ticks != TICKS_UNLIMITED) { | 
|  | limit = cur_ticks + timeout_in_ticks; | 
|  | } | 
|  |  | 
|  | do { | 
|  | /* | 
|  | * Predict that the branch will be taken to break out of the | 
|  | * loop.  There is little cost to a misprediction since that | 
|  | * leads to idle. | 
|  | */ | 
|  |  | 
|  | if (likely(lifo->list != NULL)) { | 
|  | void *data = lifo->list; | 
|  |  | 
|  | lifo->list = *(void **) data; | 
|  | irq_unlock(imask); | 
|  |  | 
|  | return data; | 
|  | } | 
|  |  | 
|  | if (timeout_in_ticks != TICKS_NONE) { | 
|  |  | 
|  | _NANO_TIMEOUT_SET_TASK_TIMEOUT(timeout_in_ticks); | 
|  |  | 
|  | /* see explanation in | 
|  | * nano_stack.c:nano_task_stack_pop() | 
|  | */ | 
|  | nano_cpu_atomic_idle(imask); | 
|  |  | 
|  | imask = irq_lock(); | 
|  | cur_ticks = _NANO_TIMEOUT_TICK_GET(); | 
|  | } | 
|  | } while (cur_ticks < limit); | 
|  |  | 
|  | irq_unlock(imask); | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | void *nano_lifo_get(struct nano_lifo *lifo, int32_t timeout) | 
|  | { | 
|  | static void *(*func[3])(struct nano_lifo *, int32_t) = { | 
|  | nano_isr_lifo_get, | 
|  | nano_fiber_lifo_get, | 
|  | nano_task_lifo_get | 
|  | }; | 
|  |  | 
|  | return func[sys_execution_context_type_get()](lifo, timeout); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * @brief Get first element from lifo and panic if NULL | 
|  | * | 
|  | * Get the first element from the specified lifo but generate a fatal error | 
|  | * if the element is NULL. | 
|  | * | 
|  | * @param lifo LIFO from which to receive. | 
|  | * | 
|  | * @return Pointer to first element in the list | 
|  | */ | 
|  | void *_nano_fiber_lifo_get_panic(struct nano_lifo *lifo) | 
|  | { | 
|  | void *element; | 
|  |  | 
|  | element = nano_fiber_lifo_get(lifo, TICKS_NONE); | 
|  |  | 
|  | if (element == NULL) { | 
|  | _NanoFatalErrorHandler(_NANO_ERR_ALLOCATION_FAIL, &_default_esf); | 
|  | } | 
|  |  | 
|  | return element; | 
|  | } |