mirror of
https://github.com/kelvinlawson/atomthreads.git
synced 2026-01-24 00:33:14 +01:00
Add Atomthreads RTOS source files.
This commit is contained in:
796
kernel/atomkernel.c
Executable file
796
kernel/atomkernel.c
Executable file
@@ -0,0 +1,796 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. No personal names or organizations' names associated with the
|
||||
* Atomthreads project may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include "atom.h"
|
||||
#include "atomuser.h"
|
||||
|
||||
|
||||
/* Global data */
|
||||
|
||||
/**
|
||||
* This is the head of the queue of threads that are ready to run. It is
|
||||
* ordered by priority, with the higher priority threads coming first.
|
||||
* Where there are multiple threads of the same priority, the TCB pointers
|
||||
* are FIFO-ordered.
|
||||
*
|
||||
* Dequeuing the head is a fast operation because the list is ordered.
|
||||
* Enqueuing may have to walk up to the end of the list. This means that
|
||||
* context-switch times depend on the number of threads on the ready queue,
|
||||
* but efficient use is made of available RAM on tiny systems by avoiding
|
||||
* priority tables etc. This scheme can be easily swapped out for other
|
||||
* scheduler schemes by replacing the TCB enqueue and dequeue functions.
|
||||
*
|
||||
* Once a thread is scheduled in, it is not present on the ready queue while
|
||||
* it is running. When scheduled out it will be either placed back on the
|
||||
* ready queue, or will be suspended on some OS primitive (e.g. on the
|
||||
* suspended TCB queue for a semaphore, or in the timer list if suspended on
|
||||
* a timer delay).
|
||||
*/
|
||||
ATOM_TCB *tcbReadyQ = NULL;
|
||||
|
||||
/** Set to TRUE when OS is started and running threads */
|
||||
uint8_t atomOSStarted = FALSE;
|
||||
|
||||
|
||||
/* Local data */
|
||||
|
||||
/** This is a pointer to the TCB for the currently-running thread */
|
||||
static ATOM_TCB *curr_tcb = NULL;
|
||||
|
||||
/** Storage for the idle thread's TCB */
|
||||
static ATOM_TCB idle_tcb;
|
||||
|
||||
/* Number of nested interrupts */
|
||||
static int atomIntCnt = 0;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
static void atomThreadSwitch(ATOM_TCB *old_tcb, ATOM_TCB *new_tcb);
|
||||
static void atomIdleThread (uint32_t data);
|
||||
|
||||
|
||||
/**
|
||||
* \b atomSched
|
||||
*
|
||||
* This is an internal function not for use by application code.
|
||||
*
|
||||
* This is the main scheduler routine. It is called by the various OS
|
||||
* library routines to check if any threads should be scheduled in now.
|
||||
* If so, the context will be switched from the current thread to the
|
||||
* new one.
|
||||
*
|
||||
* The scheduler is priority-based with round-robin performed on threads
|
||||
* with the same priority. Round-robin is only performed on timer ticks
|
||||
* however. During reschedules caused by an OS operation (e.g. after
|
||||
* giving or taking a semaphore) we only allow the scheduling in of
|
||||
* threads with higher priority than current priority. On timer ticks we
|
||||
* also allow the scheduling of same-priority threads - in that case we
|
||||
* schedule in the head of the ready list for that priority and put the
|
||||
* current thread at the tail.
|
||||
*
|
||||
* @param[in] timer_tick Should be TRUE when called from the system tick
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void atomSched (uint8_t timer_tick)
|
||||
{
|
||||
CRITICAL_STORE;
|
||||
ATOM_TCB *new_tcb = NULL;
|
||||
int16_t lowest_pri;
|
||||
|
||||
/**
|
||||
* Check the OS has actually started. As long as the proper initialisation
|
||||
* sequence is followed there should be no calls here until the OS is
|
||||
* started, but we check to handle badly-behaved ports.
|
||||
*/
|
||||
if (atomOSStarted == FALSE)
|
||||
{
|
||||
/* Don't schedule anything in until the OS is started */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Enter critical section */
|
||||
CRITICAL_START ();
|
||||
|
||||
/**
|
||||
* If the current thread is going into suspension, then
|
||||
* unconditionally dequeue the next thread for execution.
|
||||
*/
|
||||
if (curr_tcb->suspended == TRUE)
|
||||
{
|
||||
/**
|
||||
* Dequeue the next ready to run thread. There will always be
|
||||
* at least the idle thread waiting. Note that this could
|
||||
* actually be the suspending thread if it was unsuspended
|
||||
* before the scheduler was called.
|
||||
*/
|
||||
new_tcb = tcbDequeueHead (&tcbReadyQ);
|
||||
|
||||
/**
|
||||
* Don't need to add the current thread to any queue because
|
||||
* it was suspended by another OS mechanism and will be
|
||||
* sitting on a suspend queue or similar within one of the OS
|
||||
* primitive libraries (e.g. semaphore).
|
||||
*/
|
||||
|
||||
/* Switch to the new thread */
|
||||
atomThreadSwitch (curr_tcb, new_tcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Otherwise the current thread is still ready, but check
|
||||
* if any other threads are ready.
|
||||
*/
|
||||
else
|
||||
{
|
||||
/* Calculate which priority is allowed to be scheduled in */
|
||||
if (timer_tick == TRUE)
|
||||
{
|
||||
/* Same priority or higher threads can preempt */
|
||||
lowest_pri = (int16_t)curr_tcb->priority;
|
||||
}
|
||||
else if (curr_tcb->priority > 0)
|
||||
{
|
||||
/* Only higher priority threads can preempt, invalid for 0 (highest) */
|
||||
lowest_pri = (int16_t)(curr_tcb->priority - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/**
|
||||
* Current priority is already highest (0), don't allow preempt by
|
||||
* threads of any priority because this is not a time-slice.
|
||||
*/
|
||||
lowest_pri = -1;
|
||||
}
|
||||
|
||||
/* Check if a reschedule is allowed */
|
||||
if (lowest_pri >= 0)
|
||||
{
|
||||
/* Check for a thread at the given minimum priority level or higher */
|
||||
new_tcb = tcbDequeuePriority (&tcbReadyQ, (uint8_t)lowest_pri);
|
||||
|
||||
/* If a thread was found, schedule it in */
|
||||
if (new_tcb)
|
||||
{
|
||||
/* Add the current thread to the ready queue */
|
||||
(void)tcbEnqueuePriority (&tcbReadyQ, curr_tcb);
|
||||
|
||||
/* Switch to the new thread */
|
||||
atomThreadSwitch (curr_tcb, new_tcb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Exit critical section */
|
||||
CRITICAL_END ();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomThreadSwitch
|
||||
*
|
||||
* This is an internal function not for use by application code.
|
||||
*
|
||||
* The function is called by the scheduler to perform a context switch.
|
||||
* Execution will switch to the new thread's context, therefore the
|
||||
* function doesn't actually return until the old thread is scheduled
|
||||
* back in.
|
||||
*
|
||||
* @param[in] old_tcb Pointer to TCB for thread being scheduled out
|
||||
* @param[in] new_tcb Pointer to TCB for thread being scheduled in
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void atomThreadSwitch(ATOM_TCB *old_tcb, ATOM_TCB *new_tcb)
|
||||
{
|
||||
/**
|
||||
* Check if the new thread is actually the current one, in which
|
||||
* case we don't need to do any context switch. This can happen
|
||||
* if a thread goes into suspend but is unsuspended again before
|
||||
* it is fully scheduled out.
|
||||
*/
|
||||
if (old_tcb != new_tcb)
|
||||
{
|
||||
/* Set the new currently-running thread pointer */
|
||||
curr_tcb = new_tcb;
|
||||
|
||||
/* Call the architecture-specific context switch */
|
||||
archContextSwitch (old_tcb, new_tcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* The context switch shifted execution to a different thread. By the time
|
||||
* we get back here, we are running in old_tcb context again. Clear its
|
||||
* suspend status now that we're back.
|
||||
*/
|
||||
old_tcb->suspended = FALSE;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomThreadCreate
|
||||
*
|
||||
* Creates and starts a new thread.
|
||||
*
|
||||
* Callers provide the ATOM_TCB structure storage, these are not obtained
|
||||
* from an internal TCB free list.
|
||||
*
|
||||
* The function puts the new thread on the ready queue and calls the
|
||||
* scheduler. If the priority is higher than the current priority, then the
|
||||
* new thread may be scheduled in before the function returns.
|
||||
*
|
||||
* @param[in] tcb_ptr Pointer to the thread's TCB storage
|
||||
* @param[in] priority Priority of the thread (0 to 255)
|
||||
* @param[in] entry_point Thread entry point
|
||||
* @param[in] entry_param Parameter passed to thread entry point
|
||||
* @param[in] stack_top Top of the stack area
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_ERR_PARAM Bad parameters
|
||||
* @retval ATOM_ERR_QUEUE Error putting the thread on the ready queue
|
||||
*/
|
||||
uint8_t atomThreadCreate (ATOM_TCB *tcb_ptr, uint8_t priority, void (*entry_point)(uint32_t), uint32_t entry_param, void *stack_top)
|
||||
{
|
||||
CRITICAL_STORE;
|
||||
uint8_t status;
|
||||
|
||||
if ((tcb_ptr == NULL) || (entry_point == NULL) || (stack_top == NULL))
|
||||
{
|
||||
/* Bad parameters */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Set up the TCB initial values */
|
||||
tcb_ptr->suspended = FALSE;
|
||||
tcb_ptr->priority = priority;
|
||||
tcb_ptr->prev_tcb = NULL;
|
||||
tcb_ptr->next_tcb = NULL;
|
||||
tcb_ptr->suspend_timo_cb = NULL;
|
||||
|
||||
/**
|
||||
* Store the thread entry point and parameter in the TCB. This may
|
||||
* not be necessary for all architecture ports if they put all of
|
||||
* this information in the initial thread stack.
|
||||
*/
|
||||
tcb_ptr->entry_point = entry_point;
|
||||
tcb_ptr->entry_param = entry_param;
|
||||
|
||||
/**
|
||||
* Call the arch-specific routine to set up the stack. This routine
|
||||
* is responsible for creating the context save area necessary for
|
||||
* allowing atomThreadSwitch() to schedule it in. The initial
|
||||
* archContextSwitch() call when this thread gets scheduled in the
|
||||
* first time will then restore the program counter to the thread
|
||||
* entry point, and any other necessary register values ready for
|
||||
* it to start running.
|
||||
*/
|
||||
archThreadContextInit (tcb_ptr, stack_top, entry_point, entry_param);
|
||||
|
||||
/* Protect access to the OS queue */
|
||||
CRITICAL_START ();
|
||||
|
||||
/* Put this thread on the ready queue */
|
||||
if (tcbEnqueuePriority (&tcbReadyQ, tcb_ptr) != ATOM_OK)
|
||||
{
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/* Queue-related error */
|
||||
status = ATOM_ERR_QUEUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Exit critical region */
|
||||
CRITICAL_END ();
|
||||
|
||||
/**
|
||||
* If the OS is started and we're in thread context, check if we
|
||||
* should be scheduled in now.
|
||||
*/
|
||||
if ((atomOSStarted == TRUE) && atomCurrentContext())
|
||||
atomSched (FALSE);
|
||||
|
||||
/* Success */
|
||||
status = ATOM_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomIntEnter
|
||||
*
|
||||
* Interrupt handler entry routine.
|
||||
*
|
||||
* Must be called at the start of any interrupt handlers that may
|
||||
* call an OS primitive and make a thread ready.
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void atomIntEnter (void)
|
||||
{
|
||||
/* Increment the interrupt count */
|
||||
atomIntCnt++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomIntExit
|
||||
*
|
||||
* Interrupt handler exit routine.
|
||||
*
|
||||
* Must be called at the end of any interrupt handlers that may
|
||||
* call an OS primitive and make a thread ready.
|
||||
*
|
||||
* This is responsible for calling the scheduler at the end of
|
||||
* interrupt handlers to determine whether a new thread has now
|
||||
* been made ready and should be scheduled in.
|
||||
*
|
||||
* @param timer_tick TRUE if this is a timer tick
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void atomIntExit (uint8_t timer_tick)
|
||||
{
|
||||
/* Decrement the interrupt count */
|
||||
atomIntCnt--;
|
||||
|
||||
/* Call the scheduler */
|
||||
atomSched (timer_tick);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomCurrentContext
|
||||
*
|
||||
* Get the current thread context.
|
||||
*
|
||||
* Returns a pointer to the current thread's TCB, or NULL if not in
|
||||
* thread-context (in interrupt context).
|
||||
*
|
||||
* @retval Pointer to current thread's TCB, NULL if in interrupt context
|
||||
*/
|
||||
ATOM_TCB *atomCurrentContext (void)
|
||||
{
|
||||
/* Return the current thread's TCB or NULL if in interrupt context */
|
||||
if (atomIntCnt == 0)
|
||||
return (curr_tcb);
|
||||
else
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomOSInit
|
||||
*
|
||||
* Initialise the atomthreads OS.
|
||||
*
|
||||
* Must be called before any application code uses the atomthreads APIs. No
|
||||
* threads are actually started until the application calls atomOSStart().
|
||||
*
|
||||
* Callers must provide a pointer to some storage for the idle thread stack.
|
||||
* The caller is responsible for calculating the appropriate space required
|
||||
* for their particular architecture.
|
||||
*
|
||||
* Applications should use the following initialisation sequence:
|
||||
*
|
||||
* -> Call atomOSInit() before calling any atomthreads APIs
|
||||
* -> Arrange for a timer to call atomTimerTick() periodically
|
||||
* -> Create one or more application threads using atomThreadCreate()
|
||||
* -> Start the OS using atomOSStart(). At this point the highest
|
||||
* priority application thread created will be started.
|
||||
*
|
||||
* Interrupts should be disabled until the first thread restore is complete,
|
||||
* to avoid any complications due to interrupts occurring while crucial
|
||||
* operating system facilities are being initialised. They are normally
|
||||
* enabled by the archFirstThreadRestore() routine in the architecture port.
|
||||
*
|
||||
* @param[in] idle_thread_stack_top Ptr to top of stack area for idle thread
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_ERROR Initialisation error
|
||||
*/
|
||||
uint8_t atomOSInit (void *idle_thread_stack_top)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/* Initialise data */
|
||||
curr_tcb = NULL;
|
||||
tcbReadyQ = NULL;
|
||||
atomOSStarted = FALSE;
|
||||
|
||||
/* Create the idle thread */
|
||||
status = atomThreadCreate(&idle_tcb,
|
||||
IDLE_THREAD_PRIORITY,
|
||||
atomIdleThread,
|
||||
0,
|
||||
idle_thread_stack_top);
|
||||
|
||||
/* Return status */
|
||||
return (status);
|
||||
|
||||
}
|
||||
/**
|
||||
* \b atomOSStart
|
||||
*
|
||||
* Start the highest priority thread running.
|
||||
*
|
||||
* This function must be called after all OS initialisation is complete, and
|
||||
* at least one application thread has been created. It will start executing
|
||||
* the highest priority thread created (or first created if multiple threads
|
||||
* share the highest priority).
|
||||
*
|
||||
* Interrupts must still be disabled at this point. They must only be enabled
|
||||
* when the first thread is restored and started by the architecture port's
|
||||
* archFirstThreadRestore() routine.
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void atomOSStart (void)
|
||||
{
|
||||
ATOM_TCB *new_tcb;
|
||||
|
||||
/**
|
||||
* Enable the OS started flag. This stops routines like atomThreadCreate()
|
||||
* attempting to schedule in a newly-created thread until the scheduler is
|
||||
* up and running.
|
||||
*/
|
||||
atomOSStarted = TRUE;
|
||||
|
||||
/**
|
||||
* Application calls to atomThreadCreate() should have added at least one
|
||||
* thread to the ready queue. Take the highest priority one off and
|
||||
* schedule it in. If no threads were created, the OS will simply start
|
||||
* the idle thread (the lowest priority allowed to be scheduled is the
|
||||
* idle thread's priority, 255).
|
||||
*/
|
||||
new_tcb = tcbDequeuePriority (&tcbReadyQ, 255);
|
||||
if (new_tcb)
|
||||
{
|
||||
/* Set the new currently-running thread pointer */
|
||||
curr_tcb = new_tcb;
|
||||
|
||||
/* Restore and run the first thread */
|
||||
archFirstThreadRestore (new_tcb);
|
||||
|
||||
/* Never returns to here, execution shifts to new thread context */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No ready threads were found. atomOSInit() probably was not called */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b atomIdleThread
|
||||
*
|
||||
* Entry point for idle thread.
|
||||
*
|
||||
* This thread must always be present, and will be the thread executed when
|
||||
* no other threads are ready to run. It must not call any library routines
|
||||
* which would cause it to block.
|
||||
*
|
||||
* @param[in] data Unused (optional thread entry parameter)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static void atomIdleThread (uint32_t data)
|
||||
{
|
||||
/* Loop forever */
|
||||
while (1)
|
||||
{
|
||||
/** \todo Provide user idle hooks*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b tcbEnqueuePriority
|
||||
*
|
||||
* This is an internal function not for use by application code.
|
||||
*
|
||||
* Enqueues the TCB \c tcb_ptr on the TCB queue pointed to by \c tcb_queue_ptr.
|
||||
* TCBs are placed on the queue in priority order. If there are existing TCBs
|
||||
* at the same priority as the TCB to be enqueued, the enqueued TCB will be
|
||||
* placed at the end of the same-priority TCBs. Calls to tcbDequeuePriority()
|
||||
* will dequeue same-priority TCBs in FIFO order.
|
||||
*
|
||||
* \c tcb_queue_ptr may be modified by the routine if the enqueued TCB becomes
|
||||
* the new list head. It is valid for tcb_queue_ptr to point to a NULL pointer,
|
||||
* which is the case if the queue is currently empty.
|
||||
*
|
||||
* \b NOTE: Assumes that the caller is already in a critical section.
|
||||
*
|
||||
* @param[in,out] tcb_queue_ptr Pointer to TCB queue head pointer
|
||||
* @param[in] tcb_ptr Pointer to TCB to enqueue
|
||||
*
|
||||
* @retval ATOM_OK Success
|
||||
* @retval ATOM_ERR_PARAM Bad parameters
|
||||
*/
|
||||
uint8_t tcbEnqueuePriority (ATOM_TCB **tcb_queue_ptr, ATOM_TCB *tcb_ptr)
|
||||
{
|
||||
uint8_t status;
|
||||
ATOM_TCB *prev_ptr, *next_ptr;
|
||||
|
||||
/* Parameter check */
|
||||
if ((tcb_queue_ptr == NULL) || (tcb_ptr == NULL))
|
||||
{
|
||||
/* Return error */
|
||||
status = ATOM_ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Walk the list and enqueue at the end of the TCBs at this priority */
|
||||
prev_ptr = next_ptr = *tcb_queue_ptr;
|
||||
do
|
||||
{
|
||||
/* Insert if:
|
||||
* next_ptr = NULL (we're at the head of an empty queue or at the tail)
|
||||
* the next TCB in the list is lower priority than the one we're enqueuing.
|
||||
*/
|
||||
if ((next_ptr == NULL) || (next_ptr->priority > tcb_ptr->priority))
|
||||
{
|
||||
/* Make this TCB the new listhead */
|
||||
if (next_ptr == *tcb_queue_ptr)
|
||||
{
|
||||
*tcb_queue_ptr = tcb_ptr;
|
||||
tcb_ptr->prev_tcb = NULL;
|
||||
tcb_ptr->next_tcb = next_ptr;
|
||||
if (next_ptr)
|
||||
next_ptr->prev_tcb = tcb_ptr;
|
||||
}
|
||||
/* Insert between two TCBs or at the tail */
|
||||
else
|
||||
{
|
||||
tcb_ptr->prev_tcb = prev_ptr;
|
||||
tcb_ptr->next_tcb = next_ptr;
|
||||
prev_ptr->next_tcb = tcb_ptr;
|
||||
if (next_ptr)
|
||||
next_ptr->prev_tcb = tcb_ptr;
|
||||
}
|
||||
|
||||
/* Quit the loop, we've finished inserting */
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Not inserting here, try the next one */
|
||||
prev_ptr = next_ptr;
|
||||
next_ptr = next_ptr->next_tcb;
|
||||
}
|
||||
|
||||
}
|
||||
while (prev_ptr != NULL);
|
||||
|
||||
/* Successful */
|
||||
status = ATOM_OK;
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b tcbDequeueHead
|
||||
*
|
||||
* This is an internal function not for use by application code.
|
||||
*
|
||||
* Dequeues the highest priority TCB on the queue pointed to by
|
||||
* \c tcb_queue_ptr.
|
||||
*
|
||||
* The TCB will be removed from the queue. Same priority TCBs are dequeued in
|
||||
* FIFO order.
|
||||
*
|
||||
* \c tcb_queue_ptr will be modified by the routine if a TCB is dequeued,
|
||||
* as this will be the list head. It is valid for tcb_queue_ptr to point to a
|
||||
* NULL pointer, which is the case if the queue is currently empty. In this
|
||||
* case the function returns NULL.
|
||||
*
|
||||
* \b NOTE: Assumes that the caller is already in a critical section.
|
||||
*
|
||||
* @param[in,out] tcb_queue_ptr Pointer to TCB queue head pointer
|
||||
*
|
||||
* @return Pointer to highest priority TCB on queue, or NULL if queue empty
|
||||
*/
|
||||
ATOM_TCB *tcbDequeueHead (ATOM_TCB **tcb_queue_ptr)
|
||||
{
|
||||
ATOM_TCB *ret_ptr;
|
||||
|
||||
/* Parameter check */
|
||||
if (tcb_queue_ptr == NULL)
|
||||
{
|
||||
/* Return NULL */
|
||||
ret_ptr = NULL;
|
||||
}
|
||||
/* Check for an empty queue */
|
||||
else if (*tcb_queue_ptr == NULL)
|
||||
{
|
||||
/* Return NULL */
|
||||
ret_ptr = NULL;
|
||||
}
|
||||
/* Remove and return the listhead */
|
||||
else
|
||||
{
|
||||
ret_ptr = *tcb_queue_ptr;
|
||||
*tcb_queue_ptr = ret_ptr->next_tcb;
|
||||
if (*tcb_queue_ptr)
|
||||
(*tcb_queue_ptr)->prev_tcb = NULL;
|
||||
ret_ptr->next_tcb = ret_ptr->prev_tcb = NULL;
|
||||
}
|
||||
|
||||
return (ret_ptr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b tcbDequeueEntry
|
||||
*
|
||||
* This is an internal function not for use by application code.
|
||||
*
|
||||
* Dequeues a particular TCB from the queue pointed to by \c tcb_queue_ptr.
|
||||
*
|
||||
* The TCB will be removed from the queue.
|
||||
*
|
||||
* \c tcb_queue_ptr may be modified by the routine if the dequeued TCB was
|
||||
* the list head. It is valid for tcb_queue_ptr to point to a NULL pointer,
|
||||
* which is the case if the queue is currently empty. In this case the
|
||||
* function returns NULL.
|
||||
*
|
||||
* \b NOTE: Assumes that the caller is already in a critical section.
|
||||
*
|
||||
* @param[in,out] tcb_queue_ptr Pointer to TCB queue head pointer
|
||||
* @param[in] tcb_ptr Pointer to TCB to dequeue
|
||||
*
|
||||
* @return Pointer to the dequeued TCB, or NULL if entry wasn't found
|
||||
*/
|
||||
ATOM_TCB *tcbDequeueEntry (ATOM_TCB **tcb_queue_ptr, ATOM_TCB *tcb_ptr)
|
||||
{
|
||||
ATOM_TCB *ret_ptr, *prev_ptr, *next_ptr;
|
||||
|
||||
/* Parameter check */
|
||||
if (tcb_queue_ptr == NULL)
|
||||
{
|
||||
/* Return NULL */
|
||||
ret_ptr = NULL;
|
||||
}
|
||||
/* Check for an empty queue */
|
||||
else if (*tcb_queue_ptr == NULL)
|
||||
{
|
||||
/* Return NULL */
|
||||
ret_ptr = NULL;
|
||||
}
|
||||
/* Find and remove/return the specified entry */
|
||||
else
|
||||
{
|
||||
ret_ptr = NULL;
|
||||
prev_ptr = next_ptr = *tcb_queue_ptr;
|
||||
while (next_ptr)
|
||||
{
|
||||
/* Is this entry the one we're looking for? */
|
||||
if (next_ptr == tcb_ptr)
|
||||
{
|
||||
if (next_ptr == *tcb_queue_ptr)
|
||||
{
|
||||
/* We're removing the list head */
|
||||
*tcb_queue_ptr = next_ptr->next_tcb;
|
||||
if (*tcb_queue_ptr)
|
||||
(*tcb_queue_ptr)->prev_tcb = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We're removing a mid or tail TCB */
|
||||
prev_ptr->next_tcb = next_ptr->next_tcb;
|
||||
if (next_ptr->next_tcb)
|
||||
next_ptr->next_tcb->prev_tcb = prev_ptr;
|
||||
}
|
||||
ret_ptr = next_ptr;
|
||||
ret_ptr->prev_tcb = ret_ptr->next_tcb = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Move on to the next in the list */
|
||||
prev_ptr = next_ptr;
|
||||
next_ptr = next_ptr->next_tcb;
|
||||
}
|
||||
}
|
||||
|
||||
return (ret_ptr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \b tcbDequeuePriority
|
||||
*
|
||||
* This is an internal function not for use by application code.
|
||||
*
|
||||
* Dequeues the first TCB of the given priority or higher, from the queue
|
||||
* pointed to by \c tcb_queue_ptr. Because the queue is ordered high priority
|
||||
* first, we only ever dequeue the list head, if any. If the list head is
|
||||
* lower priority than we wish to dequeue, then all following ones will also
|
||||
* be lower priority and hence are not parsed.
|
||||
*
|
||||
* The TCB will be removed from the queue. Same priority TCBs will be dequeued
|
||||
* in FIFO order.
|
||||
*
|
||||
* \c tcb_queue_ptr may be modified by the routine if the dequeued TCB was
|
||||
* the list head. It is valid for tcb_queue_ptr to point to a NULL pointer,
|
||||
* which is the case if the queue is currently empty. In this case the
|
||||
* function returns NULL.
|
||||
*
|
||||
* \b NOTE: Assumes that the caller is already in a critical section.
|
||||
*
|
||||
* @param[in,out] tcb_queue_ptr Pointer to TCB queue head pointer
|
||||
* @param[in] priority Minimum priority to qualify for dequeue
|
||||
*
|
||||
* @return Pointer to the dequeued TCB, or NULL if none found within priority
|
||||
*/
|
||||
ATOM_TCB *tcbDequeuePriority (ATOM_TCB **tcb_queue_ptr, uint8_t priority)
|
||||
{
|
||||
ATOM_TCB *ret_ptr;
|
||||
|
||||
/* Parameter check */
|
||||
if (tcb_queue_ptr == NULL)
|
||||
{
|
||||
/* Return NULL */
|
||||
ret_ptr = NULL;
|
||||
}
|
||||
/* Check for an empty queue */
|
||||
else if (*tcb_queue_ptr == NULL)
|
||||
{
|
||||
/* Return NULL */
|
||||
ret_ptr = NULL;
|
||||
}
|
||||
/* Check if the list head priority is within our range */
|
||||
else if ((*tcb_queue_ptr)->priority <= priority)
|
||||
{
|
||||
/* Remove the list head */
|
||||
ret_ptr = *tcb_queue_ptr;
|
||||
*tcb_queue_ptr = (*tcb_queue_ptr)->next_tcb;
|
||||
if (*tcb_queue_ptr)
|
||||
{
|
||||
(*tcb_queue_ptr)->prev_tcb = NULL;
|
||||
ret_ptr->next_tcb = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No higher priority ready threads found */
|
||||
ret_ptr = NULL;
|
||||
}
|
||||
|
||||
return (ret_ptr);
|
||||
}
|
||||
Reference in New Issue
Block a user