summaryrefslogtreecommitdiff
path: root/ports/linux/gnu/src
diff options
context:
space:
mode:
authorPProvost <[email protected]>2020-05-11 08:55:07 -0600
committerPProvost <[email protected]>2020-05-11 08:55:07 -0600
commit852421fda03a7722d7adb45b187235cd66fa264e (patch)
treeb117e1339a047347a3d7ebf3eeb1891168cc9b9b /ports/linux/gnu/src
Initial commit
Diffstat (limited to 'ports/linux/gnu/src')
-rw-r--r--ports/linux/gnu/src/tx_initialize_low_level_sample.c443
-rw-r--r--ports/linux/gnu/src/tx_thread_context_restore.c163
-rw-r--r--ports/linux/gnu/src/tx_thread_context_save.c107
-rw-r--r--ports/linux/gnu/src/tx_thread_interrupt_control.c183
-rw-r--r--ports/linux/gnu/src/tx_thread_schedule.c264
-rw-r--r--ports/linux/gnu/src/tx_thread_stack_build.c153
-rw-r--r--ports/linux/gnu/src/tx_thread_system_return.c207
-rw-r--r--ports/linux/gnu/src/tx_timer_interrupt.c153
8 files changed, 1673 insertions, 0 deletions
diff --git a/ports/linux/gnu/src/tx_initialize_low_level_sample.c b/ports/linux/gnu/src/tx_initialize_low_level_sample.c
new file mode 100644
index 00000000..a4bc0c18
--- /dev/null
+++ b/ports/linux/gnu/src/tx_initialize_low_level_sample.c
@@ -0,0 +1,443 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) Microsoft Corporation. All rights reserved. */
+/* */
+/* This software is licensed under the Microsoft Software License */
+/* Terms for Microsoft Azure RTOS. Full text of the license can be */
+/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
+/* and in the root directory of this software. */
+/* */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/** */
+/** ThreadX Component */
+/** */
+/** Initialize */
+/** */
+/**************************************************************************/
+/**************************************************************************/
+
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files. */
+
+#include "tx_api.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/sysinfo.h>
+
+
+/* Define various Linux objects used by the ThreadX port. */
+
+pthread_mutex_t _tx_linux_mutex;
+sem_t _tx_linux_semaphore;
+sem_t _tx_linux_semaphore_no_idle;
+ULONG _tx_linux_global_int_disabled_flag;
+struct timespec _tx_linux_time_stamp;
+__thread int _tx_linux_threadx_thread = 0;
+
+/* Define signals for linux thread. */
+#define SUSPEND_SIG SIGUSR1
+#define RESUME_SIG SIGUSR2
+
+static sigset_t _tx_linux_thread_wait_mask;
+static __thread int _tx_linux_thread_suspended;
+static sem_t _tx_linux_thread_timer_wait;
+static sem_t _tx_linux_thread_other_wait;
+
+/* Define simulated timer interrupt. This is done inside a thread, which is
+ how other interrupts may be defined as well. See code below for an
+ example. */
+
+pthread_t _tx_linux_timer_id;
+sem_t _tx_linux_timer_semaphore;
+sem_t _tx_linux_isr_semaphore;
+void *_tx_linux_timer_interrupt(void *p);
+
+
+#ifdef TX_LINUX_DEBUG_ENABLE
+
+extern ULONG _tx_thread_system_state;
+extern UINT _tx_thread_preempt_disable;
+extern TX_THREAD *_tx_thread_current_ptr;
+extern TX_THREAD *_tx_thread_execute_ptr;
+
+
+/* Define debug log in order to debug Linux issues with this port. */
+
+typedef struct TX_LINUX_DEBUG_ENTRY_STRUCT
+{
+ char *tx_linux_debug_entry_action;
+ struct timespec tx_linux_debug_entry_timestamp;
+ char *tx_linux_debug_entry_file;
+ unsigned long tx_linux_debug_entry_line;
+ pthread_mutex_t tx_linux_debug_entry_mutex;
+ unsigned long tx_linux_debug_entry_int_disabled_flag;
+ ULONG tx_linux_debug_entry_system_state;
+ UINT tx_linux_debug_entry_preempt_disable;
+ TX_THREAD *tx_linux_debug_entry_current_thread;
+ TX_THREAD *tx_linux_debug_entry_execute_thread;
+} TX_LINUX_DEBUG_ENTRY;
+
+
+/* Define the maximum size of the Linux debug array. */
+
+#ifndef TX_LINUX_DEBUG_EVENT_SIZE
+#define TX_LINUX_DEBUG_EVENT_SIZE 400
+#endif
+
+
+/* Define the circular array of Linux debug entries. */
+
+TX_LINUX_DEBUG_ENTRY _tx_linux_debug_entry_array[TX_LINUX_DEBUG_EVENT_SIZE];
+
+
+/* Define the Linux debug index. */
+
+unsigned long _tx_linux_debug_entry_index = 0;
+
+
+/* Now define the debug entry function. */
+void _tx_linux_debug_entry_insert(char *action, char *file, unsigned long line)
+{
+
+pthread_mutex_t temp_copy;
+
+ /* Save the current critical section value. */
+ temp_copy = _tx_linux_mutex;
+
+ /* Lock mutex. */
+ tx_linux_mutex_lock(_tx_linux_mutex);
+
+ /* Get the time stamp. */
+ clock_gettime(CLOCK_REALTIME, &_tx_linux_time_stamp);
+
+ /* Setup the debub entry. */
+ _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_action = action;
+ _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_timestamp = _tx_linux_time_stamp;
+ _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_file = file;
+ _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_line = line;
+ _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_mutex = temp_copy;
+ _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_int_disabled_flag = _tx_linux_global_int_disabled_flag;
+ _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_system_state = _tx_thread_system_state;
+ _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_preempt_disable = _tx_thread_preempt_disable;
+ _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_current_thread = _tx_thread_current_ptr;
+ _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_execute_thread = _tx_thread_execute_ptr;
+
+ /* Now move to the next entry. */
+ _tx_linux_debug_entry_index++;
+
+ /* Determine if we need to wrap the list. */
+ if (_tx_linux_debug_entry_index >= TX_LINUX_DEBUG_EVENT_SIZE)
+ {
+
+ /* Yes, wrap the list! */
+ _tx_linux_debug_entry_index = 0;
+ }
+
+ /* Unlock mutex. */
+ tx_linux_mutex_unlock(_tx_linux_mutex);
+}
+
+#endif
+
+
+/* Define the ThreadX timer interrupt handler. */
+
+void _tx_timer_interrupt(void);
+
+
+/* Define other external function references. */
+
+VOID _tx_initialize_low_level(VOID);
+VOID _tx_thread_context_save(VOID);
+VOID _tx_thread_context_restore(VOID);
+
+
+/* Define other external variable references. */
+
+extern VOID *_tx_initialize_unused_memory;
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _tx_initialize_low_level Linux/GNU */
+/* 6.0 */
+/* AUTHOR */
+/* */
+/* William E. Lamie, Microsoft Corporation */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function is responsible for any low-level processor */
+/* initialization, including setting up interrupt vectors, setting */
+/* up a periodic timer interrupt source, saving the system stack */
+/* pointer for use in ISR processing later, and finding the first */
+/* available RAM memory address for tx_application_define. */
+/* */
+/* INPUT */
+/* */
+/* None */
+/* */
+/* OUTPUT */
+/* */
+/* None */
+/* */
+/* CALLS */
+/* */
+/* sched_setaffinity */
+/* getpid */
+/* _tx_linux_thread_init */
+/* pthread_setschedparam */
+/* pthread_mutexattr_init */
+/* pthread_mutex_init */
+/* _tx_linux_thread_suspend */
+/* sem_init */
+/* pthread_create */
+/* printf */
+/* */
+/* CALLED BY */
+/* */
+/* _tx_initialize_kernel_enter ThreadX entry function */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 05-19-2020 William E. Lamie Initial Version 6.0 */
+/* */
+/**************************************************************************/
+VOID _tx_initialize_low_level(VOID)
+{
+struct sched_param sp;
+pthread_mutexattr_t attr;
+
+#ifdef TX_LINUX_MULTI_CORE
+cpu_set_t mask;
+
+ sched_getaffinity(getpid(), sizeof(mask), &mask);
+ if (CPU_COUNT(&mask) > 1)
+ {
+
+ srand((ULONG)pthread_self());
+
+ /* Limit this ThreadX simulation on Linux to a single core. */
+ CPU_ZERO(&mask);
+ CPU_SET(rand() % get_nprocs(), &mask);
+ if (sched_setaffinity(getpid(), sizeof(mask), &mask) != 0)
+ {
+
+ /* Error restricting the process to one core. */
+ printf("ThreadX Linux error restricting the process to one core!\n");
+ while(1)
+ {
+ }
+ }
+ }
+#endif
+
+ /* Pickup the first available memory address. */
+
+ /* Save the first available memory address. */
+ _tx_initialize_unused_memory = malloc(TX_LINUX_MEMORY_SIZE);
+
+ /* Init Linux thread. */
+ _tx_linux_thread_init();
+
+ /* Set priority and schedual of main thread. */
+ sp.sched_priority = TX_LINUX_PRIORITY_SCHEDULE;
+ pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp);
+
+ /* Create the system critical section. This is used by the
+ scheduler thread (which is the main thread) to block all
+ other stuff out. */
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&_tx_linux_mutex, &attr);
+ sem_init(&_tx_linux_semaphore, 0, 0);
+#ifdef TX_LINUX_NO_IDLE_ENABLE
+ sem_init(&_tx_linux_semaphore_no_idle, 0, 0);
+#endif /* TX_LINUX_NO_IDLE_ENABLE */
+
+ /* Initialize the global interrupt disabled flag. */
+ _tx_linux_global_int_disabled_flag = TX_FALSE;
+
+ /* Create semaphore for timer thread. */
+ sem_init(&_tx_linux_timer_semaphore, 0, 0);
+
+ /* Create semaphore for ISR thread. */
+ sem_init(&_tx_linux_isr_semaphore, 0, 0);
+
+ /* Setup periodic timer interrupt. */
+ if(pthread_create(&_tx_linux_timer_id, NULL, _tx_linux_timer_interrupt, NULL))
+ {
+
+ /* Error creating the timer interrupt. */
+ printf("ThreadX Linux error creating timer interrupt thread!\n");
+ while(1)
+ {
+ }
+ }
+
+ /* Otherwise, we have a good thread create. Now set the priority to
+ a level lower than the system thread but higher than the application
+ threads. */
+ sp.sched_priority = TX_LINUX_PRIORITY_ISR;
+ pthread_setschedparam(_tx_linux_timer_id, SCHED_FIFO, &sp);
+
+ /* Done, return to caller. */
+}
+
+
+/* This routine is called after initialization is complete in order to start
+ all interrupt threads. Interrupt threads in addition to the timer may
+ be added to this routine as well. */
+
+void _tx_initialize_start_interrupts(void)
+{
+
+ /* Kick the timer thread off to generate the ThreadX periodic interrupt
+ source. */
+ tx_linux_sem_post(&_tx_linux_timer_semaphore);
+}
+
+
+/* Define the ThreadX system timer interrupt. Other interrupts may be simulated
+ in a similar way. */
+
+void *_tx_linux_timer_interrupt(void *p)
+{
+struct timespec ts;
+long timer_periodic_nsec;
+int err;
+
+ /* Calculate periodic timer. */
+ timer_periodic_nsec = 1000000000 / TX_TIMER_TICKS_PER_SECOND;
+ nice(10);
+
+ /* Wait startup semaphore. */
+ tx_linux_sem_wait(&_tx_linux_timer_semaphore);
+
+ while(1)
+ {
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_nsec += timer_periodic_nsec;
+ if (ts.tv_nsec > 1000000000)
+ {
+ ts.tv_nsec -= 1000000000;
+ ts.tv_sec++;
+ }
+ do
+ {
+ if (sem_timedwait(&_tx_linux_timer_semaphore, &ts) == 0)
+ {
+ break;
+ }
+ err = errno;
+ } while (err != ETIMEDOUT);
+
+ /* Call ThreadX context save for interrupt preparation. */
+ _tx_thread_context_save();
+
+ /* Call trace ISR enter event insert. */
+ _tx_trace_isr_enter_insert(0);
+
+ /* Call the ThreadX system timer interrupt processing. */
+ _tx_timer_interrupt();
+
+ /* Call trace ISR exit event insert. */
+ _tx_trace_isr_exit_insert(0);
+
+ /* Call ThreadX context restore for interrupt completion. */
+ _tx_thread_context_restore();
+
+#ifdef TX_LINUX_NO_IDLE_ENABLE
+ tx_linux_mutex_lock(_tx_linux_mutex);
+
+ /* Make sure semaphore is 0. */
+ while(!sem_trywait(&_tx_linux_semaphore_no_idle));
+
+ /* Wakeup the system thread by setting the system semaphore. */
+ tx_linux_sem_post(&_tx_linux_semaphore_no_idle);
+
+ tx_linux_mutex_unlock(_tx_linux_mutex);
+#endif /* TX_LINUX_NO_IDLE_ENABLE */
+ }
+}
+
+/* Define functions for linux thread. */
+void _tx_linux_thread_resume_handler(int sig)
+{
+}
+
+void _tx_linux_thread_suspend_handler(int sig)
+{
+ if(pthread_equal(pthread_self(), _tx_linux_timer_id))
+ tx_linux_sem_post_nolock(&_tx_linux_thread_timer_wait);
+ else
+ tx_linux_sem_post_nolock(&_tx_linux_thread_other_wait);
+
+ if(_tx_linux_thread_suspended)
+ return;
+
+ _tx_linux_thread_suspended = 1;
+ sigsuspend(&_tx_linux_thread_wait_mask);
+ _tx_linux_thread_suspended = 0;
+}
+
+void _tx_linux_thread_suspend(pthread_t thread_id)
+{
+
+ /* Send signal. */
+ tx_linux_mutex_lock(_tx_linux_mutex);
+ pthread_kill(thread_id, SUSPEND_SIG);
+ tx_linux_mutex_unlock(_tx_linux_mutex);
+
+ /* Wait until signal is received. */
+ if(pthread_equal(thread_id, _tx_linux_timer_id))
+ tx_linux_sem_wait(&_tx_linux_thread_timer_wait);
+ else
+ tx_linux_sem_wait(&_tx_linux_thread_other_wait);
+}
+
+void _tx_linux_thread_resume(pthread_t thread_id)
+{
+
+ /* Send signal. */
+ tx_linux_mutex_lock(_tx_linux_mutex);
+ pthread_kill(thread_id, RESUME_SIG);
+ tx_linux_mutex_unlock(_tx_linux_mutex);
+}
+
+void _tx_linux_thread_init()
+{
+struct sigaction sa;
+
+ /* Create semaphore for linux thread. */
+ sem_init(&_tx_linux_thread_timer_wait, 0, 0);
+ sem_init(&_tx_linux_thread_other_wait, 0, 0);
+
+ sigfillset(&_tx_linux_thread_wait_mask);
+ sigdelset(&_tx_linux_thread_wait_mask, RESUME_SIG);
+
+ sigfillset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = _tx_linux_thread_resume_handler;
+ sigaction(RESUME_SIG, &sa, NULL);
+
+ sa.sa_handler = _tx_linux_thread_suspend_handler;
+ sigaction(SUSPEND_SIG, &sa, NULL);
+}
+
+
diff --git a/ports/linux/gnu/src/tx_thread_context_restore.c b/ports/linux/gnu/src/tx_thread_context_restore.c
new file mode 100644
index 00000000..69a16b18
--- /dev/null
+++ b/ports/linux/gnu/src/tx_thread_context_restore.c
@@ -0,0 +1,163 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) Microsoft Corporation. All rights reserved. */
+/* */
+/* This software is licensed under the Microsoft Software License */
+/* Terms for Microsoft Azure RTOS. Full text of the license can be */
+/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
+/* and in the root directory of this software. */
+/* */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/** */
+/** ThreadX Component */
+/** */
+/** Thread */
+/** */
+/**************************************************************************/
+/**************************************************************************/
+
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files. */
+
+#include "tx_api.h"
+#include "tx_thread.h"
+#include "tx_timer.h"
+
+extern sem_t _tx_linux_isr_semaphore;
+UINT _tx_linux_timer_waiting = 0;
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _tx_thread_context_restore Linux/GNU */
+/* 6.0 */
+/* AUTHOR */
+/* */
+/* William E. Lamie, Microsoft Corporation */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function restores the interrupt context if it is processing a */
+/* nested interrupt. If not, it returns to the interrupt thread if no */
+/* preemption is necessary. Otherwise, if preemption is necessary or */
+/* if no thread was running, the function returns to the scheduler. */
+/* */
+/* INPUT */
+/* */
+/* None */
+/* */
+/* OUTPUT */
+/* */
+/* None */
+/* */
+/* CALLS */
+/* */
+/* _tx_linux_debug_entry_insert */
+/* tx_linux_mutex_lock */
+/* sem_trywait */
+/* tx_linux_sem_post */
+/* tx_linux_sem_wait */
+/* _tx_linux_thread_resume */
+/* tx_linux_mutex_recursive_unlock */
+/* */
+/* CALLED BY */
+/* */
+/* ISRs Interrupt Service Routines */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 05-19-2020 William E. Lamie Initial Version 6.0 */
+/* */
+/**************************************************************************/
+VOID _tx_thread_context_restore(VOID)
+{
+
+ /* Debug entry. */
+ _tx_linux_debug_entry_insert("CONTEXT_RESTORE", __FILE__, __LINE__);
+
+ /* Lock mutex to ensure other threads are not playing with
+ the core ThreadX data structures. */
+ tx_linux_mutex_lock(_tx_linux_mutex);
+
+ /* Decrement the nested interrupt count. */
+ _tx_thread_system_state--;
+
+ /* Determine if this is the first nested interrupt and if a ThreadX
+ application thread was running at the time. */
+ if ((!_tx_thread_system_state) && (_tx_thread_current_ptr))
+ {
+
+ /* Yes, this is the first and last interrupt processed. */
+
+ /* Check to see if preemption is required. */
+ if ((_tx_thread_preempt_disable == 0) && (_tx_thread_current_ptr != _tx_thread_execute_ptr))
+ {
+
+ /* Preempt the running application thread. We don't need to suspend the
+ application thread since that is done in the context save processing. */
+
+ /* Indicate that this thread was suspended asynchronously. */
+ _tx_thread_current_ptr -> tx_thread_linux_suspension_type = 1;
+
+ /* Save the remaining time-slice and disable it. */
+ if (_tx_timer_time_slice)
+ {
+
+ _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice;
+ _tx_timer_time_slice = 0;
+ }
+
+ /* Clear the current thread pointer. */
+ _tx_thread_current_ptr = TX_NULL;
+
+ /* Make sure semaphore is 0. */
+ while(!sem_trywait(&_tx_linux_semaphore));
+
+ /* Indicate it is in timer ISR. */
+ _tx_linux_timer_waiting = 1;
+
+ /* Wakeup the system thread by setting the system semaphore. */
+ tx_linux_sem_post(&_tx_linux_semaphore);
+
+ if(_tx_thread_execute_ptr)
+ {
+ if(_tx_thread_execute_ptr -> tx_thread_linux_suspension_type == 0)
+ {
+
+ /* Unlock linux mutex. */
+ tx_linux_mutex_recursive_unlock(_tx_linux_mutex);
+
+ /* Wait until TX_THREAD start running. */
+ tx_linux_sem_wait(&_tx_linux_isr_semaphore);
+
+ tx_linux_mutex_lock(_tx_linux_mutex);
+
+ /* Make sure semaphore is 0. */
+ while(!sem_trywait(&_tx_linux_isr_semaphore));
+ }
+ }
+
+ /* Indicate it is not in timer ISR. */
+ _tx_linux_timer_waiting = 0;
+ }
+ else
+ {
+
+ /* Since preemption is not required, resume the interrupted thread. */
+ _tx_linux_thread_resume(_tx_thread_current_ptr -> tx_thread_linux_thread_id);
+ }
+ }
+
+ /* Unlock linux mutex. */
+ tx_linux_mutex_recursive_unlock(_tx_linux_mutex);
+}
+
diff --git a/ports/linux/gnu/src/tx_thread_context_save.c b/ports/linux/gnu/src/tx_thread_context_save.c
new file mode 100644
index 00000000..90ff05f1
--- /dev/null
+++ b/ports/linux/gnu/src/tx_thread_context_save.c
@@ -0,0 +1,107 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) Microsoft Corporation. All rights reserved. */
+/* */
+/* This software is licensed under the Microsoft Software License */
+/* Terms for Microsoft Azure RTOS. Full text of the license can be */
+/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
+/* and in the root directory of this software. */
+/* */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/** */
+/** ThreadX Component */
+/** */
+/** Thread */
+/** */
+/**************************************************************************/
+/**************************************************************************/
+
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files. */
+
+#include "tx_api.h"
+#include "tx_thread.h"
+#include "tx_timer.h"
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _tx_thread_context_save Linux/GNU */
+/* 6.0 */
+/* AUTHOR */
+/* */
+/* William E. Lamie, Microsoft Corporation */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function saves the context of an executing thread in the */
+/* beginning of interrupt processing. The function also ensures that */
+/* the system stack is used upon return to the calling ISR. */
+/* */
+/* INPUT */
+/* */
+/* None */
+/* */
+/* OUTPUT */
+/* */
+/* None */
+/* */
+/* CALLS */
+/* */
+/* _tx_linux_debug_entry_insert */
+/* tx_linux_mutex_lock */
+/* _tx_linux_thread_suspend */
+/* tx_linux_mutex_unlock */
+/* */
+/* CALLED BY */
+/* */
+/* ISRs */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 05-19-2020 William E. Lamie Initial Version 6.0 */
+/* */
+/**************************************************************************/
+VOID _tx_thread_context_save(VOID)
+{
+
+ /* Debug entry. */
+ _tx_linux_debug_entry_insert("CONTEXT_SAVE", __FILE__, __LINE__);
+
+ /* Lock mutex to ensure other threads are not playing with
+ the core ThreadX data structures. */
+ tx_linux_mutex_lock(_tx_linux_mutex);
+
+ /* If an application thread is running, suspend it to simulate preemption. */
+ if ((_tx_thread_current_ptr) && (_tx_thread_system_state == 0))
+ {
+
+ /* Debug entry. */
+ _tx_linux_debug_entry_insert("CONTEXT_SAVE-suspend_thread", __FILE__, __LINE__);
+
+ /* Yes, this is the first interrupt and an application thread is running...
+ suspend it! */
+ _tx_linux_thread_suspend(_tx_thread_current_ptr -> tx_thread_linux_thread_id);
+
+ /* Indicate that this thread was suspended asynchronously. */
+ _tx_thread_current_ptr -> tx_thread_linux_suspension_type = 1;
+ }
+
+ /* Increment the nested interrupt condition. */
+ _tx_thread_system_state++;
+
+ /* Unlock linux mutex. */
+ tx_linux_mutex_unlock(_tx_linux_mutex);
+}
+
diff --git a/ports/linux/gnu/src/tx_thread_interrupt_control.c b/ports/linux/gnu/src/tx_thread_interrupt_control.c
new file mode 100644
index 00000000..f1e71d2c
--- /dev/null
+++ b/ports/linux/gnu/src/tx_thread_interrupt_control.c
@@ -0,0 +1,183 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) Microsoft Corporation. All rights reserved. */
+/* */
+/* This software is licensed under the Microsoft Software License */
+/* Terms for Microsoft Azure RTOS. Full text of the license can be */
+/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
+/* and in the root directory of this software. */
+/* */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/** */
+/** ThreadX Component */
+/** */
+/** Thread */
+/** */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files. */
+
+#include "tx_api.h"
+#include "tx_thread.h"
+
+/* Define small routines used for the TX_DISABLE/TX_RESTORE macros. */
+
+UINT _tx_thread_interrupt_disable(void)
+{
+
+UINT previous_value;
+
+
+ previous_value = _tx_thread_interrupt_control(TX_INT_DISABLE);
+ return(previous_value);
+}
+
+
+VOID _tx_thread_interrupt_restore(UINT previous_posture)
+{
+
+ previous_posture = _tx_thread_interrupt_control(previous_posture);
+}
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _tx_thread_interrupt_control Linux/GNU */
+/* 6.0 */
+/* AUTHOR */
+/* */
+/* William E. Lamie, Microsoft Corporation */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function is responsible for changing the interrupt lockout */
+/* posture of the system. */
+/* */
+/* INPUT */
+/* */
+/* new_posture New interrupt lockout posture */
+/* */
+/* OUTPUT */
+/* */
+/* old_posture Old interrupt lockout posture */
+/* */
+/* CALLS */
+/* */
+/* tx_linux_mutex_lock */
+/* pthread_self */
+/* pthread_getschedparam */
+/* tx_linux_mutex_recursive_unlock */
+/* pthread_exit */
+/* */
+/* CALLED BY */
+/* */
+/* Application Code */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 05-19-2020 William E. Lamie Initial Version 6.0 */
+/* */
+/**************************************************************************/
+UINT _tx_thread_interrupt_control(UINT new_posture)
+{
+
+UINT old_posture;
+TX_THREAD *thread_ptr;
+pthread_t thread_id;
+int exit_code = 0;
+
+
+ /* Lock Linux mutex. */
+ tx_linux_mutex_lock(_tx_linux_mutex);
+
+ /* Pickup the id of the current thread. */
+ thread_id = pthread_self();
+
+ /* Pickup the current thread pointer. */
+ thread_ptr = _tx_thread_current_ptr;
+
+ /* Determine if this is a thread and it does not
+ match the current thread pointer. */
+ if ((_tx_linux_threadx_thread) &&
+ ((!thread_ptr) || (!pthread_equal(thread_ptr -> tx_thread_linux_thread_id, thread_id))))
+ {
+
+ /* This indicates the Linux thread was actually terminated by ThreadX is only
+ being allowed to run in order to cleanup its resources. */
+ /* Unlock linux mutex. */
+ tx_linux_mutex_recursive_unlock(_tx_linux_mutex);
+ pthread_exit((void *)&exit_code);
+ }
+
+ /* Determine the current interrupt lockout condition. */
+ if (tx_linux_mutex_recursive_count == 1)
+ {
+
+ /* Interrupts are enabled. */
+ old_posture = TX_INT_ENABLE;
+ }
+ else
+ {
+
+ /* Interrupts are disabled. */
+ old_posture = TX_INT_DISABLE;
+ }
+
+ /* First, determine if this call is from a non-thread. */
+ if (_tx_thread_system_state)
+ {
+
+ /* Determine how to apply the new posture. */
+ if (new_posture == TX_INT_ENABLE)
+ {
+
+ /* Clear the disabled flag. */
+ _tx_linux_global_int_disabled_flag = TX_FALSE;
+
+ /* Determine if the critical section is locked. */
+ tx_linux_mutex_recursive_unlock(_tx_linux_mutex);
+ }
+ else if (new_posture == TX_INT_DISABLE)
+ {
+
+ /* Set the disabled flag. */
+ _tx_linux_global_int_disabled_flag = TX_TRUE;
+ }
+ }
+ else if (thread_ptr)
+ {
+
+ /* Determine how to apply the new posture. */
+ if (new_posture == TX_INT_ENABLE)
+ {
+
+ /* Clear the disabled flag. */
+ _tx_thread_current_ptr -> tx_thread_linux_int_disabled_flag = TX_FALSE;
+
+ /* Determine if the critical section is locked. */
+ tx_linux_mutex_recursive_unlock(_tx_linux_mutex);
+ }
+ else if (new_posture == TX_INT_DISABLE)
+ {
+
+ /* Set the disabled flag. */
+ _tx_thread_current_ptr -> tx_thread_linux_int_disabled_flag = TX_TRUE;
+ }
+ }
+
+ /* Return the previous interrupt disable posture. */
+ return(old_posture);
+}
+
diff --git a/ports/linux/gnu/src/tx_thread_schedule.c b/ports/linux/gnu/src/tx_thread_schedule.c
new file mode 100644
index 00000000..2bc91228
--- /dev/null
+++ b/ports/linux/gnu/src/tx_thread_schedule.c
@@ -0,0 +1,264 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) Microsoft Corporation. All rights reserved. */
+/* */
+/* This software is licensed under the Microsoft Software License */
+/* Terms for Microsoft Azure RTOS. Full text of the license can be */
+/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
+/* and in the root directory of this software. */
+/* */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/** */
+/** ThreadX Component */
+/** */
+/** Thread */
+/** */
+/**************************************************************************/
+/**************************************************************************/
+
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files. */
+
+#include "tx_api.h"
+#include "tx_thread.h"
+#include "tx_timer.h"
+#include <stdio.h>
+#include <errno.h>
+
+extern sem_t _tx_linux_timer_semaphore;
+extern sem_t _tx_linux_isr_semaphore;
+extern UINT _tx_linux_timer_waiting;
+extern pthread_t _tx_linux_timer_id;
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _tx_thread_schedule Linux/GNU */
+/* 6.0 */
+/* AUTHOR */
+/* */
+/* William E. Lamie, Microsoft Corporation */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function waits for a thread control block pointer to appear in */
+/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */
+/* in the variable, the corresponding thread is resumed. */
+/* */
+/* INPUT */
+/* */
+/* None */
+/* */
+/* OUTPUT */
+/* */
+/* None */
+/* */
+/* CALLS */
+/* */
+/* tx_linux_mutex_lock */
+/* tx_linux_mutex_unlock */
+/* _tx_linux_debug_entry_insert */
+/* _tx_linux_thread_resume */
+/* tx_linux_sem_post */
+/* sem_trywait */
+/* tx_linux_sem_wait */
+/* */
+/* CALLED BY */
+/* */
+/* _tx_initialize_kernel_enter ThreadX entry function */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 05-19-2020 William E. Lamie Initial Version 6.0 */
+/* */
+/**************************************************************************/
+VOID _tx_thread_schedule(VOID)
+{
+struct timespec ts;
+
+ /* Set timer. */
+ ts.tv_sec = 0;
+ ts.tv_nsec = 200000;
+
+ /* Loop forever. */
+ while(1)
+ {
+
+ /* Wait for a thread to execute and all ISRs to complete. */
+ while(1)
+ {
+
+ /* Lock Linux mutex. */
+ tx_linux_mutex_lock(_tx_linux_mutex);
+
+ /* Determine if there is a thread ready to execute AND all ISRs
+ are complete. */
+ if ((_tx_thread_execute_ptr != TX_NULL) && (_tx_thread_system_state == 0))
+ {
+
+ /* Get out of this loop and schedule the thread! */
+ break;
+ }
+ else
+ {
+
+ /* Unlock linux mutex. */
+ tx_linux_mutex_unlock(_tx_linux_mutex);
+
+ /* Don't waste all the processor time here in the master thread... */
+#ifdef TX_LINUX_NO_IDLE_ENABLE
+ while(!sem_trywait(&_tx_linux_timer_semaphore));
+ tx_linux_sem_post(&_tx_linux_timer_semaphore);
+ /*nanosleep(&ts, &ts);*/
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_nsec += 200000;
+ if (ts.tv_nsec > 1000000000)
+ {
+ ts.tv_nsec -= 1000000000;
+ ts.tv_sec++;
+ }
+ sem_timedwait(&_tx_linux_semaphore_no_idle, &ts);
+#else
+ nanosleep(&ts, &ts);
+#endif /* TX_LINUX_NO_IDLE_ENABLE */
+ }
+ }
+
+ /* Yes! We have a thread to execute. Note that the critical section is already
+ active from the scheduling loop above. */
+
+ /* Setup the current thread pointer. */
+ _tx_thread_current_ptr = _tx_thread_execute_ptr;
+
+ /* Increment the run count for this thread. */
+ _tx_thread_current_ptr -> tx_thread_run_count++;
+
+ /* Setup time-slice, if present. */
+ _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice;
+
+ /* Determine how the thread was suspended. */
+ if (_tx_thread_current_ptr -> tx_thread_linux_suspension_type)
+ {
+
+ /* Debug entry. */
+ _tx_linux_debug_entry_insert("SCHEDULE-resume_thread", __FILE__, __LINE__);
+
+ /* Pseudo interrupt suspension. The thread is not waiting on
+ its run semaphore. */
+ _tx_linux_thread_resume(_tx_thread_current_ptr -> tx_thread_linux_thread_id);
+ }
+ else
+ {
+
+ /* Debug entry. */
+ _tx_linux_debug_entry_insert("SCHEDULE-release_sem", __FILE__, __LINE__);
+
+ /* Make sure semaphore is 0. */
+ while(!sem_trywait(&_tx_thread_current_ptr -> tx_thread_linux_thread_run_semaphore));
+
+ /* Let the thread run again by releasing its run semaphore. */
+ tx_linux_sem_post(&_tx_thread_current_ptr -> tx_thread_linux_thread_run_semaphore);
+
+ /* Block timer ISR. */
+ if(_tx_linux_timer_waiting)
+ {
+
+ /* It is woken up by timer ISR. */
+ /* Let ThreadX thread wake up first. */
+ tx_linux_sem_wait(&_tx_linux_semaphore);
+
+ /* Wake up timer ISR. */
+ tx_linux_sem_post_nolock(&_tx_linux_isr_semaphore);
+ }
+ else
+ {
+
+ /* It is woken up by TX_THREAD. */
+ /* Suspend timer thread and let ThreadX thread wake up first. */
+ _tx_linux_thread_suspend(_tx_linux_timer_id);
+ tx_linux_sem_wait(&_tx_linux_semaphore);
+ _tx_linux_thread_resume(_tx_linux_timer_id);
+
+ }
+ }
+
+ /* Unlock linux mutex. */
+ tx_linux_mutex_unlock(_tx_linux_mutex);
+
+ /* Debug entry. */
+ _tx_linux_debug_entry_insert("SCHEDULE-self_suspend_sem", __FILE__, __LINE__);
+
+ /* Now suspend the main thread so the application thread can run. */
+ tx_linux_sem_wait(&_tx_linux_semaphore);
+
+ /* Debug entry. */
+ _tx_linux_debug_entry_insert("SCHEDULE-wake_up", __FILE__, __LINE__);
+
+ }
+}
+
+void _tx_thread_delete_port_completion(TX_THREAD *thread_ptr, UINT tx_saved_posture)
+{
+INT linux_status;
+sem_t *threadrunsemaphore;
+pthread_t thread_id;
+struct timespec ts;
+
+ thread_id = thread_ptr -> tx_thread_linux_thread_id;
+ threadrunsemaphore = &(thread_ptr -> tx_thread_linux_thread_run_semaphore);
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000;
+ TX_RESTORE
+ do
+ {
+ linux_status = pthread_cancel(thread_id);
+ if(linux_status != EAGAIN)
+ {
+ break;
+ }
+ _tx_linux_thread_resume(thread_id);
+ tx_linux_sem_post(threadrunsemaphore);
+ nanosleep(&ts, &ts);
+ } while (1);
+ pthread_join(thread_id, NULL);
+ sem_destroy(threadrunsemaphore);
+ TX_DISABLE
+}
+
+void _tx_thread_reset_port_completion(TX_THREAD *thread_ptr, UINT tx_saved_posture)
+{
+INT linux_status;
+sem_t *threadrunsemaphore;
+pthread_t thread_id;
+struct timespec ts;
+
+ thread_id = thread_ptr -> tx_thread_linux_thread_id;
+ threadrunsemaphore = &(thread_ptr -> tx_thread_linux_thread_run_semaphore);
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000;
+ TX_RESTORE
+ do
+ {
+ linux_status = pthread_cancel(thread_id);
+ if(linux_status != EAGAIN)
+ {
+ break;
+ }
+ _tx_linux_thread_resume(thread_id);
+ tx_linux_sem_post(threadrunsemaphore);
+ nanosleep(&ts, &ts);
+ } while (1);
+ pthread_join(thread_id, NULL);
+ sem_destroy(threadrunsemaphore);
+ TX_DISABLE
+}
diff --git a/ports/linux/gnu/src/tx_thread_stack_build.c b/ports/linux/gnu/src/tx_thread_stack_build.c
new file mode 100644
index 00000000..22f49ead
--- /dev/null
+++ b/ports/linux/gnu/src/tx_thread_stack_build.c
@@ -0,0 +1,153 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) Microsoft Corporation. All rights reserved. */
+/* */
+/* This software is licensed under the Microsoft Software License */
+/* Terms for Microsoft Azure RTOS. Full text of the license can be */
+/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
+/* and in the root directory of this software. */
+/* */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/** */
+/** ThreadX Component */
+/** */
+/** Thread */
+/** */
+/**************************************************************************/
+/**************************************************************************/
+
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files. */
+
+#include "tx_api.h"
+#include "tx_thread.h"
+#include <stdio.h>
+#include <unistd.h>
+
+
+/* Prototype for new thread entry function. */
+
+void *_tx_linux_thread_entry(void *ptr);
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _tx_thread_stack_build Linux/GNU */
+/* 6.0 */
+/* AUTHOR */
+/* */
+/* William E. Lamie, Microsoft Corporation */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function builds a stack frame on the supplied thread's stack. */
+/* The stack frame results in a fake interrupt return to the supplied */
+/* function pointer. */
+/* */
+/* INPUT */
+/* */
+/* thread_ptr Pointer to thread control blk */
+/* function_ptr Pointer to return function */
+/* */
+/* OUTPUT */
+/* */
+/* None */
+/* */
+/* CALLS */
+/* */
+/* pthread_create */
+/* pthread_setschedparam */
+/* _tx_linux_thread_suspend */
+/* sem_init */
+/* printf */
+/* _tx_linux_thread_resume */
+/* */
+/* CALLED BY */
+/* */
+/* _tx_thread_create Create thread service */
+/* _tx_thread_reset Reset thread service */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 05-19-2020 William E. Lamie Initial Version 6.0 */
+/* */
+/**************************************************************************/
+VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID))
+{
+struct sched_param sp;
+
+ /* Create the run semaphore for the thread. This will allow the scheduler
+ control over when the thread actually runs. */
+ if(sem_init(&thread_ptr -> tx_thread_linux_thread_run_semaphore, 0, 0))
+ {
+
+ /* Display an error message. */
+ printf("ThreadX Linux error creating thread running semaphore!\n");
+ while(1)
+ {
+ }
+ }
+
+ /* Create a Linux thread for the application thread. */
+ if(pthread_create(&thread_ptr -> tx_thread_linux_thread_id, NULL, _tx_linux_thread_entry, thread_ptr))
+ {
+
+ /* Display an error message. */
+ printf("ThreadX Linux error creating thread!\n");
+ while(1)
+ {
+ }
+ }
+
+ /* Otherwise, we have a good thread create. */
+ sp.sched_priority = TX_LINUX_PRIORITY_USER_THREAD;
+ pthread_setschedparam(thread_ptr -> tx_thread_linux_thread_id, SCHED_FIFO, &sp);
+
+ /* Setup the thread suspension type to solicited thread suspension.
+ Pseudo interrupt handlers will suspend with this field set to 1. */
+ thread_ptr -> tx_thread_linux_suspension_type = 0;
+
+ /* Clear the disabled count that will keep track of the
+ tx_interrupt_control nesting. */
+ thread_ptr -> tx_thread_linux_int_disabled_flag = 0;
+
+ /* Setup a fake thread stack pointer. */
+ thread_ptr -> tx_thread_stack_ptr = (VOID *) (((CHAR *) thread_ptr -> tx_thread_stack_end) - 8);
+
+ /* Clear the first word of the stack. */
+ *(((ULONG *) thread_ptr -> tx_thread_stack_ptr) - 1) = 0;
+}
+
+
+void *_tx_linux_thread_entry(void *ptr)
+{
+
+TX_THREAD *thread_ptr;
+
+ /* Pickup the current thread pointer. */
+ thread_ptr = (TX_THREAD *) ptr;
+ _tx_linux_threadx_thread = 1;
+ nice(20);
+
+ /* Now suspend the thread initially. If the thread has already
+ been scheduled, this will return immediately. */
+ tx_linux_sem_wait(&thread_ptr -> tx_thread_linux_thread_run_semaphore);
+ tx_linux_sem_post_nolock(&_tx_linux_semaphore);
+
+ /* Call ThreadX thread entry point. */
+ _tx_thread_shell_entry();
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/ports/linux/gnu/src/tx_thread_system_return.c b/ports/linux/gnu/src/tx_thread_system_return.c
new file mode 100644
index 00000000..e7db8750
--- /dev/null
+++ b/ports/linux/gnu/src/tx_thread_system_return.c
@@ -0,0 +1,207 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) Microsoft Corporation. All rights reserved. */
+/* */
+/* This software is licensed under the Microsoft Software License */
+/* Terms for Microsoft Azure RTOS. Full text of the license can be */
+/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
+/* and in the root directory of this software. */
+/* */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/** */
+/** ThreadX Component */
+/** */
+/** Thread */
+/** */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files. */
+
+#include "tx_api.h"
+#include "tx_thread.h"
+#include "tx_timer.h"
+#include <stdio.h>
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _tx_thread_system_return Linux/GNU */
+/* 6.0 */
+/* AUTHOR */
+/* */
+/* William E. Lamie, Microsoft Corporation */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function is target processor specific. It is used to transfer */
+/* control from a thread back to the system. Only a minimal context */
+/* is saved since the compiler assumes temp registers are going to get */
+/* slicked by a function call anyway. */
+/* */
+/* INPUT */
+/* */
+/* None */
+/* */
+/* OUTPUT */
+/* */
+/* None */
+/* */
+/* CALLS */
+/* */
+/* _tx_linux_debug_entry_insert */
+/* tx_linux_mutex_lock */
+/* pthread_self */
+/* pthread_getschedparam */
+/* pthread_equal */
+/* tx_linux_mutex_recursive_unlock */
+/* tx_linux_mutex_unlock */
+/* pthread_exit */
+/* tx_linux_sem_post */
+/* sem_trywait */
+/* tx_linux_sem_wait */
+/* */
+/* CALLED BY */
+/* */
+/* ThreadX components */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 05-19-2020 William E. Lamie Initial Version 6.0 */
+/* */
+/**************************************************************************/
+VOID _tx_thread_system_return(VOID)
+{
+
+TX_THREAD *temp_thread_ptr;
+sem_t *temp_run_semaphore;
+UINT temp_thread_state;
+pthread_t thread_id;
+int exit_code = 0;
+
+
+ /* Debug entry. */
+ _tx_linux_debug_entry_insert("SYSTEM_RETURN", __FILE__, __LINE__);
+
+ /* Lock Linux mutex. */
+ tx_linux_mutex_lock(_tx_linux_mutex);
+
+ /* First, determine if the thread was terminated. */
+
+ /* Pickup the id of the current thread. */
+ thread_id = pthread_self();
+
+ /* Pickup the current thread pointer. */
+ temp_thread_ptr = _tx_thread_current_ptr;
+
+ /* Determine if this is a thread (0) and it does not
+ match the current thread pointer. */
+ if ((_tx_linux_threadx_thread) &&
+ ((!temp_thread_ptr) || (!pthread_equal(temp_thread_ptr -> tx_thread_linux_thread_id, thread_id))))
+ {
+
+ /* This indicates the Linux thread was actually terminated by ThreadX is only
+ being allowed to run in order to cleanup its resources. */
+ /* Unlock linux mutex. */
+ tx_linux_mutex_recursive_unlock(_tx_linux_mutex);
+ pthread_exit((void *)&exit_code);
+ }
+
+ /* Determine if the time-slice is active. */
+ if (_tx_timer_time_slice)
+ {
+
+ /* Preserve current remaining time-slice for the thread and clear the current time-slice. */
+ temp_thread_ptr -> tx_thread_time_slice = _tx_timer_time_slice;
+ _tx_timer_time_slice = 0;
+ }
+
+ /* Save the run semaphore into a temporary variable as well. */
+ temp_run_semaphore = &temp_thread_ptr -> tx_thread_linux_thread_run_semaphore;
+
+ /* Pickup the current thread state. */
+ temp_thread_state = temp_thread_ptr -> tx_thread_state;
+
+ /* Setup the suspension type for this thread. */
+ temp_thread_ptr -> tx_thread_linux_suspension_type = 0;
+
+ /* Set the current thread pointer to NULL. */
+ _tx_thread_current_ptr = TX_NULL;
+
+ /* Unlock Linux mutex. */
+ tx_linux_mutex_recursive_unlock(_tx_linux_mutex);
+
+ /* Debug entry. */
+ _tx_linux_debug_entry_insert("SYSTEM_RETURN-release_sem", __FILE__, __LINE__);
+
+ /* Make sure semaphore is 0. */
+ while(!sem_trywait(&_tx_linux_semaphore));
+
+ /* Release the semaphore that the main scheduling thread is waiting
+ on. Note that the main scheduling algorithm will take care of
+ setting the current thread pointer to NULL. */
+ tx_linux_sem_post(&_tx_linux_semaphore);
+
+ /* Determine if the thread was self-terminating. */
+ if (temp_thread_state == TX_TERMINATED)
+ {
+
+ /* Exit the thread instead of waiting on the semaphore! */
+ pthread_exit((void *)&exit_code);
+ }
+
+ /* Wait on the run semaphore for this thread. This won't get set again
+ until the thread is scheduled. */
+ tx_linux_sem_wait(temp_run_semaphore);
+ tx_linux_sem_post_nolock(&_tx_linux_semaphore);
+
+ /* Lock Linux mutex. */
+ tx_linux_mutex_lock(_tx_linux_mutex);
+
+ /* Debug entry. */
+ _tx_linux_debug_entry_insert("SYSTEM_RETURN-wake_up", __FILE__, __LINE__);
+
+ /* Determine if the thread was terminated. */
+
+ /* Pickup the current thread pointer. */
+ temp_thread_ptr = _tx_thread_current_ptr;
+
+ /* Determine if this is a thread and it does not
+ match the current thread pointer. */
+ if ((_tx_linux_threadx_thread) &&
+ ((!temp_thread_ptr) || (!pthread_equal(temp_thread_ptr -> tx_thread_linux_thread_id, thread_id))))
+ {
+
+ /* Unlock Linux mutex. */
+ tx_linux_mutex_recursive_unlock(_tx_linux_mutex);
+
+ /* This indicates the Linux thread was actually terminated by ThreadX and is only
+ being allowed to run in order to cleanup its resources. */
+ pthread_exit((void *)&exit_code);
+ }
+
+ /* Now determine if the application thread last had interrupts disabled. */
+
+ /* Determine if this thread had interrupts disabled. */
+ if (!_tx_thread_current_ptr -> tx_thread_linux_int_disabled_flag)
+ {
+
+ /* Unlock Linux mutex. */
+ tx_linux_mutex_recursive_unlock(_tx_linux_mutex);
+ }
+
+ /* Debug entry. */
+ _tx_linux_debug_entry_insert("SYSTEM_RETURN-finish", __FILE__, __LINE__);
+}
+
diff --git a/ports/linux/gnu/src/tx_timer_interrupt.c b/ports/linux/gnu/src/tx_timer_interrupt.c
new file mode 100644
index 00000000..5461b302
--- /dev/null
+++ b/ports/linux/gnu/src/tx_timer_interrupt.c
@@ -0,0 +1,153 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) Microsoft Corporation. All rights reserved. */
+/* */
+/* This software is licensed under the Microsoft Software License */
+/* Terms for Microsoft Azure RTOS. Full text of the license can be */
+/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
+/* and in the root directory of this software. */
+/* */
+/**************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/** */
+/** ThreadX Component */
+/** */
+/** Timer */
+/** */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+
+/* Include necessary system files. */
+
+#include "tx_api.h"
+#include "tx_timer.h"
+#include "tx_thread.h"
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _tx_timer_interrupt Linux/GNU */
+/* 6.0 */
+/* AUTHOR */
+/* */
+/* William E. Lamie, Microsoft Corporation */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function processes the hardware timer interrupt. This */
+/* processing includes incrementing the system clock and checking for */
+/* time slice and/or timer expiration. If either is found, the */
+/* interrupt context save/restore functions are called along with the */
+/* expiration functions. */
+/* */
+/* INPUT */
+/* */
+/* None */
+/* */
+/* OUTPUT */
+/* */
+/* None */
+/* */
+/* CALLS */
+/* */
+/* _tx_linux_debug_entry_insert */
+/* tx_linux_mutex_lock */
+/* tx_linux_mutex_unlock */
+/* _tx_timer_expiration_process */
+/* _tx_thread_time_slice */
+/* */
+/* CALLED BY */
+/* */
+/* interrupt vector */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 05-19-2020 William E. Lamie Initial Version 6.0 */
+/* */
+/**************************************************************************/
+VOID _tx_timer_interrupt(VOID)
+{
+
+ /* Debug entry. */
+ _tx_linux_debug_entry_insert("TIMER INTERRUPT", __FILE__, __LINE__);
+
+ /* Lock mutex to ensure other threads are not playing with
+ the core ThreadX data structures. */
+ tx_linux_mutex_lock(_tx_linux_mutex);
+
+ /* Increment the system clock. */
+ _tx_timer_system_clock++;
+
+ /* Test for time-slice expiration. */
+ if (_tx_timer_time_slice)
+ {
+
+ /* Decrement the time_slice. */
+ _tx_timer_time_slice--;
+
+ /* Check for expiration. */
+ if (_tx_timer_time_slice == 0)
+ {
+
+ /* Set the time-slice expired flag. */
+ _tx_timer_expired_time_slice = TX_TRUE;
+ }
+ }
+
+ /* Test for timer expiration. */
+ if (*_tx_timer_current_ptr)
+ {
+
+ /* Set expiration flag. */
+ _tx_timer_expired = TX_TRUE;
+ }
+ else
+ {
+
+ /* No timer expired, increment the timer pointer. */
+ _tx_timer_current_ptr++;
+
+ /* Check for wrap-around. */
+ if (_tx_timer_current_ptr == _tx_timer_list_end)
+ {
+
+ /* Wrap to beginning of list. */
+ _tx_timer_current_ptr = _tx_timer_list_start;
+ }
+ }
+
+ /* See if anything has expired. */
+ if ((_tx_timer_expired_time_slice) || (_tx_timer_expired))
+ {
+
+ /* Did a timer expire? */
+ if (_tx_timer_expired)
+ {
+
+ /* Process timer expiration. */
+ _tx_timer_expiration_process();
+ }
+
+ /* Did time slice expire? */
+ if (_tx_timer_expired_time_slice)
+ {
+
+ /* Time slice interrupted thread. */
+ _tx_thread_time_slice();
+ }
+ }
+
+ /* Unlock linux mutex. */
+ tx_linux_mutex_unlock(_tx_linux_mutex);
+}
+