123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700 |
- /*
- * FreeRTOS Kernel V10.4.6
- * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
- * SPDX-License-Identifier: MIT
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- * the Software, and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- * https://www.FreeRTOS.org
- * https://github.com/FreeRTOS
- *
- */
- /* Standard includes. */
- #include <stdio.h>
- /* Scheduler includes. */
- #include "FreeRTOS.h"
- #include "task.h"
- #ifdef __GNUC__
- #include "mmsystem.h"
- #else
- #pragma comment(lib, "winmm.lib")
- #endif
- #define portMAX_INTERRUPTS ( ( uint32_t ) sizeof( uint32_t ) * 8UL ) /* The number of bits in an uint32_t. */
- #define portNO_CRITICAL_NESTING ( ( uint32_t ) 0 )
- /* The priorities at which the various components of the simulation execute. */
- #define portDELETE_SELF_THREAD_PRIORITY THREAD_PRIORITY_TIME_CRITICAL /* Must be highest. */
- #define portSIMULATED_INTERRUPTS_THREAD_PRIORITY THREAD_PRIORITY_TIME_CRITICAL
- #define portSIMULATED_TIMER_THREAD_PRIORITY THREAD_PRIORITY_HIGHEST
- #define portTASK_THREAD_PRIORITY THREAD_PRIORITY_ABOVE_NORMAL
- /*
- * Created as a high priority thread, this function uses a timer to simulate
- * a tick interrupt being generated on an embedded target. In this Windows
- * environment the timer does not achieve anything approaching real time
- * performance though.
- */
- static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter );
- /*
- * Process all the simulated interrupts - each represented by a bit in
- * ulPendingInterrupts variable.
- */
- static void prvProcessSimulatedInterrupts( void );
- /*
- * Interrupt handlers used by the kernel itself. These are executed from the
- * simulated interrupt handler thread.
- */
- static uint32_t prvProcessYieldInterrupt( void );
- static uint32_t prvProcessTickInterrupt( void );
- /*
- * Exiting a critical section will cause the calling task to block on yield
- * event to wait for an interrupt to process if an interrupt was pended while
- * inside the critical section. This variable protects against a recursive
- * attempt to obtain pvInterruptEventMutex if a critical section is used inside
- * an interrupt handler itself.
- */
- volatile BaseType_t xInsideInterrupt = pdFALSE;
- /*
- * Called when the process exits to let Windows know the high timer resolution
- * is no longer required.
- */
- static BOOL WINAPI prvEndProcess( DWORD dwCtrlType );
- /*-----------------------------------------------------------*/
- /* The WIN32 simulator runs each task in a thread. The context switching is
- managed by the threads, so the task stack does not have to be managed directly,
- although the task stack is still used to hold an xThreadState structure this is
- the only thing it will ever hold. The structure indirectly maps the task handle
- to a thread handle. */
- typedef struct
- {
- /* Handle of the thread that executes the task. */
- void *pvThread;
- /* Event used to make sure the thread does not execute past a yield point
- between the call to SuspendThread() to suspend the thread and the
- asynchronous SuspendThread() operation actually being performed. */
- void *pvYieldEvent;
- } ThreadState_t;
- /* Simulated interrupts waiting to be processed. This is a bit mask where each
- bit represents one interrupt, so a maximum of 32 interrupts can be simulated. */
- static volatile uint32_t ulPendingInterrupts = 0UL;
- /* An event used to inform the simulated interrupt processing thread (a high
- priority thread that simulated interrupt processing) that an interrupt is
- pending. */
- static void *pvInterruptEvent = NULL;
- /* Mutex used to protect all the simulated interrupt variables that are accessed
- by multiple threads. */
- static void *pvInterruptEventMutex = NULL;
- /* The critical nesting count for the currently executing task. This is
- initialised to a non-zero value so interrupts do not become enabled during
- the initialisation phase. As each task has its own critical nesting value
- ulCriticalNesting will get set to zero when the first task runs. This
- initialisation is probably not critical in this simulated environment as the
- simulated interrupt handlers do not get created until the FreeRTOS scheduler is
- started anyway. */
- static volatile uint32_t ulCriticalNesting = 9999UL;
- /* Handlers for all the simulated software interrupts. The first two positions
- are used for the Yield and Tick interrupts so are handled slightly differently,
- all the other interrupts can be user defined. */
- static uint32_t (*ulIsrHandler[ portMAX_INTERRUPTS ])( void ) = { 0 };
- /* Pointer to the TCB of the currently executing task. */
- extern void * volatile pxCurrentTCB;
- /* Used to ensure nothing is processed during the startup sequence. */
- static BaseType_t xPortRunning = pdFALSE;
- /*-----------------------------------------------------------*/
- static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter )
- {
- TickType_t xMinimumWindowsBlockTime;
- TIMECAPS xTimeCaps;
- /* Set the timer resolution to the maximum possible. */
- if( timeGetDevCaps( &xTimeCaps, sizeof( xTimeCaps ) ) == MMSYSERR_NOERROR )
- {
- xMinimumWindowsBlockTime = ( TickType_t ) xTimeCaps.wPeriodMin;
- timeBeginPeriod( xTimeCaps.wPeriodMin );
- /* Register an exit handler so the timeBeginPeriod() function can be
- matched with a timeEndPeriod() when the application exits. */
- SetConsoleCtrlHandler( prvEndProcess, TRUE );
- }
- else
- {
- xMinimumWindowsBlockTime = ( TickType_t ) 20;
- }
- /* Just to prevent compiler warnings. */
- ( void ) lpParameter;
- for( ;; )
- {
- /* Wait until the timer expires and we can access the simulated interrupt
- variables. *NOTE* this is not a 'real time' way of generating tick
- events as the next wake time should be relative to the previous wake
- time, not the time that Sleep() is called. It is done this way to
- prevent overruns in this very non real time simulated/emulated
- environment. */
- if( portTICK_PERIOD_MS < xMinimumWindowsBlockTime )
- {
- Sleep( xMinimumWindowsBlockTime );
- }
- else
- {
- Sleep( portTICK_PERIOD_MS );
- }
- configASSERT( xPortRunning );
- /* Can't proceed if in a critical section as pvInterruptEventMutex won't
- be available. */
- WaitForSingleObject( pvInterruptEventMutex, INFINITE );
- /* The timer has expired, generate the simulated tick event. */
- ulPendingInterrupts |= ( 1 << portINTERRUPT_TICK );
- /* The interrupt is now pending - notify the simulated interrupt
- handler thread. Must be outside of a critical section to get here so
- the handler thread can execute immediately pvInterruptEventMutex is
- released. */
- configASSERT( ulCriticalNesting == 0UL );
- SetEvent( pvInterruptEvent );
- /* Give back the mutex so the simulated interrupt handler unblocks
- and can access the interrupt handler variables. */
- ReleaseMutex( pvInterruptEventMutex );
- }
- #ifdef __GNUC__
- /* Should never reach here - MingW complains if you leave this line out,
- MSVC complains if you put it in. */
- return 0;
- #endif
- }
- /*-----------------------------------------------------------*/
- static BOOL WINAPI prvEndProcess( DWORD dwCtrlType )
- {
- TIMECAPS xTimeCaps;
- ( void ) dwCtrlType;
- if( timeGetDevCaps( &xTimeCaps, sizeof( xTimeCaps ) ) == MMSYSERR_NOERROR )
- {
- /* Match the call to timeBeginPeriod( xTimeCaps.wPeriodMin ) made when
- the process started with a timeEndPeriod() as the process exits. */
- timeEndPeriod( xTimeCaps.wPeriodMin );
- }
- return pdFALSE;
- }
- /*-----------------------------------------------------------*/
- StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
- {
- ThreadState_t *pxThreadState = NULL;
- int8_t *pcTopOfStack = ( int8_t * ) pxTopOfStack;
- const SIZE_T xStackSize = 1024; /* Set the size to a small number which will get rounded up to the minimum possible. */
- /* In this simulated case a stack is not initialised, but instead a thread
- is created that will execute the task being created. The thread handles
- the context switching itself. The ThreadState_t object is placed onto
- the stack that was created for the task - so the stack buffer is still
- used, just not in the conventional way. It will not be used for anything
- other than holding this structure. */
- pxThreadState = ( ThreadState_t * ) ( pcTopOfStack - sizeof( ThreadState_t ) );
- /* Create the event used to prevent the thread from executing past its yield
- point if the SuspendThread() call that suspends the thread does not take
- effect immediately (it is an asynchronous call). */
- pxThreadState->pvYieldEvent = CreateEvent( NULL, /* Default security attributes. */
- FALSE, /* Auto reset. */
- FALSE, /* Start not signalled. */
- NULL );/* No name. */
- /* Create the thread itself. */
- pxThreadState->pvThread = CreateThread( NULL, xStackSize, ( LPTHREAD_START_ROUTINE ) pxCode, pvParameters, CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, NULL );
- configASSERT( pxThreadState->pvThread ); /* See comment where TerminateThread() is called. */
- SetThreadAffinityMask( pxThreadState->pvThread, 0x01 );
- SetThreadPriorityBoost( pxThreadState->pvThread, TRUE );
- SetThreadPriority( pxThreadState->pvThread, portTASK_THREAD_PRIORITY );
- return ( StackType_t * ) pxThreadState;
- }
- /*-----------------------------------------------------------*/
- BaseType_t xPortStartScheduler( void )
- {
- void *pvHandle = NULL;
- int32_t lSuccess;
- ThreadState_t *pxThreadState = NULL;
- SYSTEM_INFO xSystemInfo;
- /* This port runs windows threads with extremely high priority. All the
- threads execute on the same core - to prevent locking up the host only start
- if the host has multiple cores. */
- GetSystemInfo( &xSystemInfo );
- if( xSystemInfo.dwNumberOfProcessors <= 1 )
- {
- printf( "This version of the FreeRTOS Windows port can only be used on multi-core hosts.\r\n" );
- lSuccess = pdFAIL;
- }
- else
- {
- lSuccess = pdPASS;
- /* The highest priority class is used to [try to] prevent other Windows
- activity interfering with FreeRTOS timing too much. */
- if( SetPriorityClass( GetCurrentProcess(), REALTIME_PRIORITY_CLASS ) == 0 )
- {
- printf( "SetPriorityClass() failed\r\n" );
- }
- /* Install the interrupt handlers used by the scheduler itself. */
- vPortSetInterruptHandler( portINTERRUPT_YIELD, prvProcessYieldInterrupt );
- vPortSetInterruptHandler( portINTERRUPT_TICK, prvProcessTickInterrupt );
- /* Create the events and mutexes that are used to synchronise all the
- threads. */
- pvInterruptEventMutex = CreateMutex( NULL, FALSE, NULL );
- pvInterruptEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
- if( ( pvInterruptEventMutex == NULL ) || ( pvInterruptEvent == NULL ) )
- {
- lSuccess = pdFAIL;
- }
- /* Set the priority of this thread such that it is above the priority of
- the threads that run tasks. This higher priority is required to ensure
- simulated interrupts take priority over tasks. */
- pvHandle = GetCurrentThread();
- if( pvHandle == NULL )
- {
- lSuccess = pdFAIL;
- }
- }
- if( lSuccess == pdPASS )
- {
- if( SetThreadPriority( pvHandle, portSIMULATED_INTERRUPTS_THREAD_PRIORITY ) == 0 )
- {
- lSuccess = pdFAIL;
- }
- SetThreadPriorityBoost( pvHandle, TRUE );
- SetThreadAffinityMask( pvHandle, 0x01 );
- }
- if( lSuccess == pdPASS )
- {
- /* Start the thread that simulates the timer peripheral to generate
- tick interrupts. The priority is set below that of the simulated
- interrupt handler so the interrupt event mutex is used for the
- handshake / overrun protection. */
- pvHandle = CreateThread( NULL, 0, prvSimulatedPeripheralTimer, NULL, CREATE_SUSPENDED, NULL );
- if( pvHandle != NULL )
- {
- SetThreadPriority( pvHandle, portSIMULATED_TIMER_THREAD_PRIORITY );
- SetThreadPriorityBoost( pvHandle, TRUE );
- SetThreadAffinityMask( pvHandle, 0x01 );
- ResumeThread( pvHandle );
- }
- /* Start the highest priority task by obtaining its associated thread
- state structure, in which is stored the thread handle. */
- pxThreadState = ( ThreadState_t * ) *( ( size_t * ) pxCurrentTCB );
- ulCriticalNesting = portNO_CRITICAL_NESTING;
- /* Start the first task. */
- ResumeThread( pxThreadState->pvThread );
- /* Handle all simulated interrupts - including yield requests and
- simulated ticks. */
- prvProcessSimulatedInterrupts();
- }
- /* Would not expect to return from prvProcessSimulatedInterrupts(), so should
- not get here. */
- return 0;
- }
- /*-----------------------------------------------------------*/
- static uint32_t prvProcessYieldInterrupt( void )
- {
- /* Always return true as this is a yield. */
- return pdTRUE;
- }
- /*-----------------------------------------------------------*/
- static uint32_t prvProcessTickInterrupt( void )
- {
- uint32_t ulSwitchRequired;
- /* Process the tick itself. */
- configASSERT( xPortRunning );
- ulSwitchRequired = ( uint32_t ) xTaskIncrementTick();
- return ulSwitchRequired;
- }
- /*-----------------------------------------------------------*/
- static void prvProcessSimulatedInterrupts( void )
- {
- uint32_t ulSwitchRequired, i;
- ThreadState_t *pxThreadState;
- void *pvObjectList[ 2 ];
- CONTEXT xContext;
- /* Going to block on the mutex that ensured exclusive access to the simulated
- interrupt objects, and the event that signals that a simulated interrupt
- should be processed. */
- pvObjectList[ 0 ] = pvInterruptEventMutex;
- pvObjectList[ 1 ] = pvInterruptEvent;
- /* Create a pending tick to ensure the first task is started as soon as
- this thread pends. */
- ulPendingInterrupts |= ( 1 << portINTERRUPT_TICK );
- SetEvent( pvInterruptEvent );
- xPortRunning = pdTRUE;
- for(;;)
- {
- xInsideInterrupt = pdFALSE;
- WaitForMultipleObjects( sizeof( pvObjectList ) / sizeof( void * ), pvObjectList, TRUE, INFINITE );
- /* Cannot be in a critical section to get here. Tasks that exit a
- critical section will block on a yield mutex to wait for an interrupt to
- process if an interrupt was set pending while the task was inside the
- critical section. xInsideInterrupt prevents interrupts that contain
- critical sections from doing the same. */
- xInsideInterrupt = pdTRUE;
- /* Used to indicate whether the simulated interrupt processing has
- necessitated a context switch to another task/thread. */
- ulSwitchRequired = pdFALSE;
- /* For each interrupt we are interested in processing, each of which is
- represented by a bit in the 32bit ulPendingInterrupts variable. */
- for( i = 0; i < portMAX_INTERRUPTS; i++ )
- {
- /* Is the simulated interrupt pending? */
- if( ( ulPendingInterrupts & ( 1UL << i ) ) != 0 )
- {
- /* Is a handler installed? */
- if( ulIsrHandler[ i ] != NULL )
- {
- /* Run the actual handler. Handlers return pdTRUE if they
- necessitate a context switch. */
- if( ulIsrHandler[ i ]() != pdFALSE )
- {
- /* A bit mask is used purely to help debugging. */
- ulSwitchRequired |= ( 1 << i );
- }
- }
- /* Clear the interrupt pending bit. */
- ulPendingInterrupts &= ~( 1UL << i );
- }
- }
- if( ulSwitchRequired != pdFALSE )
- {
- void *pvOldCurrentTCB;
- pvOldCurrentTCB = pxCurrentTCB;
- /* Select the next task to run. */
- vTaskSwitchContext();
- /* If the task selected to enter the running state is not the task
- that is already in the running state. */
- if( pvOldCurrentTCB != pxCurrentTCB )
- {
- /* Suspend the old thread. In the cases where the (simulated)
- interrupt is asynchronous (tick event swapping a task out rather
- than a task blocking or yielding) it doesn't matter if the
- 'suspend' operation doesn't take effect immediately - if it
- doesn't it would just be like the interrupt occurring slightly
- later. In cases where the yield was caused by a task blocking
- or yielding then the task will block on a yield event after the
- yield operation in case the 'suspend' operation doesn't take
- effect immediately. */
- pxThreadState = ( ThreadState_t *) *( ( size_t * ) pvOldCurrentTCB );
- SuspendThread( pxThreadState->pvThread );
- /* Ensure the thread is actually suspended by performing a
- synchronous operation that can only complete when the thread is
- actually suspended. The below code asks for dummy register
- data. Experimentation shows that these two lines don't appear
- to do anything now, but according to
- https://devblogs.microsoft.com/oldnewthing/20150205-00/?p=44743
- they do - so as they do not harm (slight run-time hit). */
- xContext.ContextFlags = CONTEXT_INTEGER;
- ( void ) GetThreadContext( pxThreadState->pvThread, &xContext );
- /* Obtain the state of the task now selected to enter the
- Running state. */
- pxThreadState = ( ThreadState_t * ) ( *( size_t *) pxCurrentTCB );
- /* pxThreadState->pvThread can be NULL if the task deleted
- itself - but a deleted task should never be resumed here. */
- configASSERT( pxThreadState->pvThread != NULL );
- ResumeThread( pxThreadState->pvThread );
- }
- }
- /* If the thread that is about to be resumed stopped running
- because it yielded then it will wait on an event when it resumed
- (to ensure it does not continue running after the call to
- SuspendThread() above as SuspendThread() is asynchronous).
- Signal the event to ensure the thread can proceed now it is
- valid for it to do so. Signaling the event is benign in the case that
- the task was switched out asynchronously by an interrupt as the event
- is reset before the task blocks on it. */
- pxThreadState = ( ThreadState_t * ) ( *( size_t *) pxCurrentTCB );
- SetEvent( pxThreadState->pvYieldEvent );
- ReleaseMutex( pvInterruptEventMutex );
- }
- }
- /*-----------------------------------------------------------*/
- void vPortDeleteThread( void *pvTaskToDelete )
- {
- ThreadState_t *pxThreadState;
- uint32_t ulErrorCode;
- /* Remove compiler warnings if configASSERT() is not defined. */
- ( void ) ulErrorCode;
- /* Find the handle of the thread being deleted. */
- pxThreadState = ( ThreadState_t * ) ( *( size_t *) pvTaskToDelete );
- /* Check that the thread is still valid, it might have been closed by
- vPortCloseRunningThread() - which will be the case if the task associated
- with the thread originally deleted itself rather than being deleted by a
- different task. */
- if( pxThreadState->pvThread != NULL )
- {
- WaitForSingleObject( pvInterruptEventMutex, INFINITE );
- /* !!! This is not a nice way to terminate a thread, and will eventually
- result in resources being depleted if tasks frequently delete other
- tasks (rather than deleting themselves) as the task stacks will not be
- freed. */
- ulErrorCode = TerminateThread( pxThreadState->pvThread, 0 );
- configASSERT( ulErrorCode );
- ulErrorCode = CloseHandle( pxThreadState->pvThread );
- configASSERT( ulErrorCode );
- ReleaseMutex( pvInterruptEventMutex );
- }
- }
- /*-----------------------------------------------------------*/
- void vPortCloseRunningThread( void *pvTaskToDelete, volatile BaseType_t *pxPendYield )
- {
- ThreadState_t *pxThreadState;
- void *pvThread;
- uint32_t ulErrorCode;
- /* Remove compiler warnings if configASSERT() is not defined. */
- ( void ) ulErrorCode;
- /* Find the handle of the thread being deleted. */
- pxThreadState = ( ThreadState_t * ) ( *( size_t *) pvTaskToDelete );
- pvThread = pxThreadState->pvThread;
- /* Raise the Windows priority of the thread to ensure the FreeRTOS scheduler
- does not run and swap it out before it is closed. If that were to happen
- the thread would never run again and effectively be a thread handle and
- memory leak. */
- SetThreadPriority( pvThread, portDELETE_SELF_THREAD_PRIORITY );
- /* This function will not return, therefore a yield is set as pending to
- ensure a context switch occurs away from this thread on the next tick. */
- *pxPendYield = pdTRUE;
- /* Mark the thread associated with this task as invalid so
- vPortDeleteThread() does not try to terminate it. */
- pxThreadState->pvThread = NULL;
- /* Close the thread. */
- ulErrorCode = CloseHandle( pvThread );
- configASSERT( ulErrorCode );
- /* This is called from a critical section, which must be exited before the
- thread stops. */
- taskEXIT_CRITICAL();
- CloseHandle( pxThreadState->pvYieldEvent );
- ExitThread( 0 );
- }
- /*-----------------------------------------------------------*/
- void vPortEndScheduler( void )
- {
- exit( 0 );
- }
- /*-----------------------------------------------------------*/
- void vPortGenerateSimulatedInterrupt( uint32_t ulInterruptNumber )
- {
- ThreadState_t *pxThreadState = ( ThreadState_t *) *( ( size_t * ) pxCurrentTCB );
- configASSERT( xPortRunning );
- if( ( ulInterruptNumber < portMAX_INTERRUPTS ) && ( pvInterruptEventMutex != NULL ) )
- {
- WaitForSingleObject( pvInterruptEventMutex, INFINITE );
- ulPendingInterrupts |= ( 1 << ulInterruptNumber );
- /* The simulated interrupt is now held pending, but don't actually
- process it yet if this call is within a critical section. It is
- possible for this to be in a critical section as calls to wait for
- mutexes are accumulative. If in a critical section then the event
- will get set when the critical section nesting count is wound back
- down to zero. */
- if( ulCriticalNesting == portNO_CRITICAL_NESTING )
- {
- SetEvent( pvInterruptEvent );
- /* Going to wait for an event - make sure the event is not already
- signaled. */
- ResetEvent( pxThreadState->pvYieldEvent );
- }
- ReleaseMutex( pvInterruptEventMutex );
- if( ulCriticalNesting == portNO_CRITICAL_NESTING )
- {
- /* An interrupt was pended so ensure to block to allow it to
- execute. In most cases the (simulated) interrupt will have
- executed before the next line is reached - so this is just to make
- sure. */
- WaitForSingleObject( pxThreadState->pvYieldEvent, INFINITE );
- }
- }
- }
- /*-----------------------------------------------------------*/
- void vPortSetInterruptHandler( uint32_t ulInterruptNumber, uint32_t (*pvHandler)( void ) )
- {
- if( ulInterruptNumber < portMAX_INTERRUPTS )
- {
- if( pvInterruptEventMutex != NULL )
- {
- WaitForSingleObject( pvInterruptEventMutex, INFINITE );
- ulIsrHandler[ ulInterruptNumber ] = pvHandler;
- ReleaseMutex( pvInterruptEventMutex );
- }
- else
- {
- ulIsrHandler[ ulInterruptNumber ] = pvHandler;
- }
- }
- }
- /*-----------------------------------------------------------*/
- void vPortEnterCritical( void )
- {
- if( xPortRunning == pdTRUE )
- {
- /* The interrupt event mutex is held for the entire critical section,
- effectively disabling (simulated) interrupts. */
- WaitForSingleObject( pvInterruptEventMutex, INFINITE );
- }
- ulCriticalNesting++;
- }
- /*-----------------------------------------------------------*/
- void vPortExitCritical( void )
- {
- int32_t lMutexNeedsReleasing;
- /* The interrupt event mutex should already be held by this thread as it was
- obtained on entry to the critical section. */
- lMutexNeedsReleasing = pdTRUE;
- if( ulCriticalNesting > portNO_CRITICAL_NESTING )
- {
- ulCriticalNesting--;
- /* Don't need to wait for any pending interrupts to execute if the
- critical section was exited from inside an interrupt. */
- if( ( ulCriticalNesting == portNO_CRITICAL_NESTING ) && ( xInsideInterrupt == pdFALSE ) )
- {
- /* Were any interrupts set to pending while interrupts were
- (simulated) disabled? */
- if( ulPendingInterrupts != 0UL )
- {
- ThreadState_t *pxThreadState = ( ThreadState_t *) *( ( size_t * ) pxCurrentTCB );
- configASSERT( xPortRunning );
- /* The interrupt won't actually executed until
- pvInterruptEventMutex is released as it waits on both
- pvInterruptEventMutex and pvInterruptEvent.
- pvInterruptEvent is only set when the simulated
- interrupt is pended if the interrupt is pended
- from outside a critical section - hence it is set
- here. */
- SetEvent( pvInterruptEvent );
- /* The calling task is going to wait for an event to ensure the
- interrupt that is pending executes immediately after the
- critical section is exited - so make sure the event is not
- already signaled. */
- ResetEvent( pxThreadState->pvYieldEvent );
- /* Mutex will be released now so the (simulated) interrupt can
- execute, so does not require releasing on function exit. */
- lMutexNeedsReleasing = pdFALSE;
- ReleaseMutex( pvInterruptEventMutex );
- WaitForSingleObject( pxThreadState->pvYieldEvent, INFINITE );
- }
- }
- }
- if( pvInterruptEventMutex != NULL )
- {
- if( lMutexNeedsReleasing == pdTRUE )
- {
- configASSERT( xPortRunning );
- ReleaseMutex( pvInterruptEventMutex );
- }
- }
- }
- /*-----------------------------------------------------------*/
|