summaryrefslogtreecommitdiff
path: root/ports/linux/gnu/src/tx_initialize_low_level_sample.c
diff options
context:
space:
mode:
Diffstat (limited to 'ports/linux/gnu/src/tx_initialize_low_level_sample.c')
-rw-r--r--ports/linux/gnu/src/tx_initialize_low_level_sample.c443
1 files changed, 443 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);
+}
+
+