--- /dev/null
+/*\r
+ FreeRTOS V6.1.0 - Copyright (C) 2010 Real Time Engineers Ltd.\r
+\r
+ ***************************************************************************\r
+ * *\r
+ * If you are: *\r
+ * *\r
+ * + New to FreeRTOS, *\r
+ * + Wanting to learn FreeRTOS or multitasking in general quickly *\r
+ * + Looking for basic training, *\r
+ * + Wanting to improve your FreeRTOS skills and productivity *\r
+ * *\r
+ * then take a look at the FreeRTOS books - available as PDF or paperback *\r
+ * *\r
+ * "Using the FreeRTOS Real Time Kernel - a Practical Guide" *\r
+ * http://www.FreeRTOS.org/Documentation *\r
+ * *\r
+ * A pdf reference manual is also available. Both are usually delivered *\r
+ * to your inbox within 20 minutes to two hours when purchased between 8am *\r
+ * and 8pm GMT (although please allow up to 24 hours in case of *\r
+ * exceptional circumstances). Thank you for your support! *\r
+ * *\r
+ ***************************************************************************\r
+\r
+ This file is part of the FreeRTOS distribution.\r
+\r
+ FreeRTOS is free software; you can redistribute it and/or modify it under\r
+ the terms of the GNU General Public License (version 2) as published by the\r
+ Free Software Foundation AND MODIFIED BY the FreeRTOS exception.\r
+ ***NOTE*** The exception to the GPL is included to allow you to distribute\r
+ a combined work that includes FreeRTOS without being obliged to provide the\r
+ source code for proprietary components outside of the FreeRTOS kernel.\r
+ FreeRTOS is distributed in the hope that it will be useful, but WITHOUT\r
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\r
+ more details. You should have received a copy of the GNU General Public\r
+ License and the FreeRTOS license exception along with FreeRTOS; if not it\r
+ can be viewed here: http://www.freertos.org/a00114.html and also obtained\r
+ by writing to Richard Barry, contact details for whom are available on the\r
+ FreeRTOS WEB site.\r
+\r
+ 1 tab == 4 spaces!\r
+\r
+ http://www.FreeRTOS.org - Documentation, latest information, license and\r
+ contact details.\r
+\r
+ http://www.SafeRTOS.com - A version that is certified for use in safety\r
+ critical systems.\r
+\r
+ http://www.OpenRTOS.com - Commercial support, development, porting,\r
+ licensing and training services.\r
+*/\r
+\r
+/* Scheduler includes. */\r
+#include "FreeRTOS.h"\r
+#include "task.h"\r
+#include <stdio.h>\r
+\r
+\r
+\r
+\r
+\r
+typedef struct xTaskControlBlock\r
+{\r
+ volatile portSTACK_TYPE *pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE STRUCT. */\r
+\r
+ #if ( portUSING_MPU_WRAPPERS == 1 )\r
+ xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE STRUCT. */\r
+ #endif \r
+ \r
+ xListItem xGenericListItem; /*< List item used to place the TCB in ready and blocked queues. */\r
+ xListItem xEventListItem; /*< List item used to place the TCB in event lists. */\r
+ unsigned portBASE_TYPE uxPriority; /*< The priority of the task where 0 is the lowest priority. */\r
+ portSTACK_TYPE *pxStack; /*< Points to the start of the stack. */\r
+ signed char pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created. Facilitates debugging only. */\r
+\r
+ #if ( portSTACK_GROWTH > 0 )\r
+ portSTACK_TYPE *pxEndOfStack; /*< Used for stack overflow checking on architectures where the stack grows up from low memory. */\r
+ #endif\r
+\r
+ #if ( portCRITICAL_NESTING_IN_TCB == 1 )\r
+ unsigned portBASE_TYPE uxCriticalNesting;\r
+ #endif\r
+\r
+ #if ( configUSE_TRACE_FACILITY == 1 )\r
+ unsigned portBASE_TYPE uxTCBNumber; /*< This is used for tracing the scheduler and making debugging easier only. */\r
+ #endif\r
+\r
+ #if ( configUSE_MUTEXES == 1 )\r
+ unsigned portBASE_TYPE uxBasePriority; /*< The priority last assigned to the task - used by the priority inheritance mechanism. */\r
+ #endif\r
+\r
+ #if ( configUSE_APPLICATION_TASK_TAG == 1 )\r
+ pdTASK_HOOK_CODE pxTaskTag;\r
+ #endif\r
+\r
+ #if ( configGENERATE_RUN_TIME_STATS == 1 )\r
+ unsigned long ulRunTimeCounter; /*< Used for calculating how much CPU time each task is utilising. */\r
+ #endif\r
+\r
+} xTCB;\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+FILE *pfTraceFile = NULL;\r
+//#define vPortTrace( x ) if( pfTraceFile == NULL ) pfTraceFile = fopen( "c:/temp/trace.txt", "w" ); if( pfTraceFile != NULL ) fprintf( pfTraceFile, x )\r
+#define vPortTrace( x ) ( void ) x\r
+\r
+#define portMAX_INTERRUPTS ( ( unsigned long ) sizeof( unsigned long ) * 8UL ) /* The number of bits in an unsigned long. */\r
+#define portNO_CRITICAL_NESTING ( ( unsigned long ) 0 )\r
+\r
+/*\r
+ * Created as a high priority thread, this function uses a timer to simulate\r
+ * a tick interrupt being generated on an embedded target. In this Windows\r
+ * environment the timer does not achieve real time performance though.\r
+ */\r
+static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter );\r
+\r
+/* \r
+ * Process all the simulated interrupts - each represented by a bit in \r
+ * ulPendingInterrupts variable.\r
+ */\r
+static void prvProcessEvents( void );\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/* The WIN32 simulator runs each task in a thread. The context switching is\r
+managed by the threads, so the task stack does not have to be managed directly,\r
+although the task stack is still used to hold an xThreadState structure this is\r
+the only thing it will ever hold. The structure indirectly maps the task handle \r
+to a thread handle. */\r
+typedef struct\r
+{\r
+ portSTACK_TYPE ulCriticalNesting; /* Critical nesting count of the task. */\r
+ void * pvThread; /* Handle of the thread that executes the task. */\r
+} xThreadState;\r
+\r
+/* The parameters passed to a thread when it is created. */\r
+typedef struct XPARAMS\r
+{\r
+ pdTASK_CODE pxCode; /* The entry point of the task (rather than thread) code. */\r
+ void *pvParameters; /* The parameters that are passed to the task (rather than thread. */\r
+} xParams;\r
+\r
+/* Pseudo interrupts waiting to be processed. This is a bit mask where each\r
+bit represents one interrupt, so a maximum of 32 interrupts can be simulated. */\r
+static volatile unsigned long ulPendingInterrupts = 0UL;\r
+\r
+/* An event used to inform the interrupt dispatch thread (a high priority thread\r
+that simulated interrupt processing) that an IRQ or SWI type interrupt is\r
+pending. */\r
+static void *pvInterruptEvent = NULL;\r
+\r
+/* Mutex used to protect all the pseudo interrupt variables that are accessed by\r
+multiple threads. */\r
+static void *pvInterruptEventMutex = NULL;\r
+\r
+/* The main thread, which also acts as the pseudo interrupt handler. */\r
+static void *pvMainThreadAndInterrupHandler;\r
+\r
+/* Events used to manage sequencing. */\r
+static void *pvTickAcknowledgeEvent = NULL, *pvInterruptAcknowledgeEvent = NULL;\r
+\r
+/* The critical nesting count for the currently executing task. This is \r
+initialised to a non-zero value so interrupts do not become enabled during \r
+the initialisation phase. As each task has its own critical nesting value \r
+ulCriticalNesting will get set to zero when the first task runs. This \r
+initialisation is probably not critical in this simulated environment as the\r
+pseudo interrupt handlers/dispatchers do not get created until the FreeRTOS\r
+scheduler is started. */\r
+static unsigned portLONG ulCriticalNesting = 9999UL;\r
+\r
+/* Handlers for all the simulated software interrupts. The first two positions\r
+are used for the Yield and Tick interrupts so are handled slightly differently,\r
+all the other interrupts can be user defined. */\r
+static void (*vIsrHandler[ portMAX_INTERRUPTS ])( void ) = { 0 };\r
+\r
+/* Pointer to the TCB of the currently executing task. */\r
+extern void *pxCurrentTCB;\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter )\r
+{\r
+void *pvTimer;\r
+LARGE_INTEGER liDueTime;\r
+void *pvObjectList[ 2 ];\r
+const long long ll_ms_In_100ns_units = ( long long ) -1000;\r
+extern volatile unsigned long ulTicks;\r
+\r
+ /* Just to prevent compiler warnings. */\r
+ ( void ) lpParameter;\r
+\r
+ /* The timer is created as a one shot timer even though we want it to repeat\r
+ at a given frequency. This is because Windows is not a real time environment,\r
+ and attempting to set a high frequency periodic timer will result in event\r
+ overruns. Therefore the timer is just reset after each time the pseudo \r
+ interrupt handler has processed each tick event. */\r
+ pvTimer = CreateWaitableTimer( NULL, TRUE, NULL );\r
+ \r
+ liDueTime.QuadPart = ( long long ) portTICK_RATE_MS * ll_ms_In_100ns_units;\r
+\r
+ /* Block on the timer itself and the event mutex that grants access to the \r
+ interrupt variables. */\r
+ pvObjectList[ 0 ] = pvInterruptEventMutex;\r
+ pvObjectList[ 1 ] = pvTimer;\r
+\r
+ for(;;)\r
+ {\r
+ ulTicks++;\r
+\r
+ /* The timer is reset on each itteration of this loop rather than being set\r
+ to function periodicallys - this is for the reasons stated in the comments\r
+ where the timer is created. */\r
+ vPortTrace( "prvSimulatedPeripheralTimer: Tick acked, setting new tick timer\r\n" );\r
+ SetWaitableTimer( pvTimer, &liDueTime, 0, NULL, NULL, TRUE );\r
+\r
+ /* Wait until the timer expires and we can access the pseudo interrupt \r
+ variables. */\r
+ //WaitForMultipleObjects( ( sizeof( pvObjectList ) / sizeof( void * ) ), pvObjectList, TRUE, INFINITE );\r
+ WaitForSingleObject( pvTimer, INFINITE );\r
+ vPortTrace( "prvSimulatedPeripheralTimer: Timer signalled, waiting interrupt event mutex\r\n" );\r
+ WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
+ vPortTrace( "prvSimulatedPeripheralTimer: Got interrupt event mutex\r\n" );\r
+ \r
+ /* The timer has expired, generate the simulated tick event. */\r
+ ulPendingInterrupts |= ( 1 << portINTERRUPT_TICK );\r
+ if( pvInterruptEvent != NULL )\r
+ {\r
+ vPortTrace( "prvSimulatedPeripheralTimer: Setting interrupt event to signal tick\r\n" );\r
+ SetEvent( pvInterruptEvent );\r
+ }\r
+\r
+ /* Give back the mutex so the pseudo interrupt handler unblocks and can\r
+ access the interrupt handler variables. This high priority task will then\r
+ loop back round to wait for the lower priority psuedo interrupt handler \r
+ thread to acknowledge the tick. */\r
+ if( pvInterruptEventMutex != NULL )\r
+ {\r
+ vPortTrace( "prvSimulatedPeripheralTimer: Releasing interrupt event mutex so tick can be processed\r\n" );\r
+ ReleaseMutex( pvInterruptEventMutex );\r
+ }\r
+\r
+ /* Wait for the previous tick to be acknowledged before resetting the timer.\r
+ As mentioned above this is done to prevent timer overruns in the non real-\r
+ time environment. THIS IS NOT HOW A REAL PORT SHOULD USE TIMERS! */\r
+ WaitForSingleObject( pvTickAcknowledgeEvent, INFINITE );\r
+ }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters )\r
+{\r
+xThreadState *pxThreadState = NULL;\r
+xParams *pxThreadParams = ( void * ) pvPortMalloc( sizeof( xParams ) );\r
+\r
+ if( pxThreadParams != NULL )\r
+ {\r
+ /* In this simulated case a stack is not initialised, but instead a thread\r
+ is created that will execute the task being created. The thread handles\r
+ the context switching itself. The xThreadState object is placed onto\r
+ the stack that was created for the task - so the stack buffer is still\r
+ used, just not in the conventional way. It will not be used for anything\r
+ other than holding this structure. */\r
+ pxThreadState = ( xThreadState * ) ( pxTopOfStack - sizeof( xThreadState ) );\r
+\r
+ /* The parameters that are passed into the thread so it knows how to\r
+ start the task executing. */\r
+ pxThreadParams->pxCode = pxCode;\r
+ pxThreadParams->pvParameters = pvParameters;\r
+\r
+ /* Create the thread itself. */\r
+ //pxThreadState->pvThread = ( void * ) CreateThread( NULL, 0, ( LPTHREAD_START_ROUTINE ) prvThreadEntryPoint, pxThreadParams, CREATE_SUSPENDED, NULL );\r
+ pxThreadState->pvThread = ( void * ) CreateThread( NULL, 0, ( LPTHREAD_START_ROUTINE ) pxCode, pvParameters, CREATE_SUSPENDED, NULL );\r
+ pxThreadState->ulCriticalNesting = portNO_CRITICAL_NESTING;\r
+ SetThreadPriority( pxThreadState->pvThread, THREAD_PRIORITY_IDLE );\r
+ }\r
+ \r
+ return ( portSTACK_TYPE * ) pxThreadState;\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+portBASE_TYPE xPortStartScheduler( void )\r
+{\r
+void *pvHandle;\r
+long lSuccess = pdPASS;\r
+xThreadState *pxThreadState;\r
+\r
+ /* Set the priority of this thread such that it is above the priority of the\r
+ threads that run tasks, but below the priority of the thread that generates\r
+ the pseudo tick interrupts. This priority is chosen because this is the\r
+ thread that actually handles the psuedo interrupts. */\r
+ pvHandle = GetCurrentThread();\r
+ if( pvHandle == NULL )\r
+ {\r
+ lSuccess = pdFAIL;\r
+ }\r
+ \r
+ if( lSuccess == pdPASS )\r
+ {\r
+ if( SetThreadPriority( pvHandle, THREAD_PRIORITY_BELOW_NORMAL ) == 0 )\r
+ {\r
+ lSuccess = pdFAIL;\r
+ }\r
+ }\r
+\r
+ if( lSuccess == pdPASS )\r
+ {\r
+ /* Create the events and mutexes that are used to synchronise all the\r
+ threads. */\r
+ pvInterruptEventMutex = CreateMutex( NULL, FALSE, NULL );\r
+ pvInterruptEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
+ pvTickAcknowledgeEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
+ pvInterruptAcknowledgeEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
+\r
+ /* Start the thread that simulates the timer peripheral to generate\r
+ tick interrupts. */\r
+ pvHandle = CreateThread( NULL, 0, prvSimulatedPeripheralTimer, NULL, 0, NULL );\r
+ if( pvHandle != NULL )\r
+ {\r
+ SetThreadPriority( pvHandle, THREAD_PRIORITY_ABOVE_NORMAL );\r
+ }\r
+ \r
+ /* Start the highest priority task by obtaining its associated thread state\r
+ structure, in which is stored the thread handle. */\r
+ pxThreadState = ( xThreadState * ) *( ( unsigned long * ) pxCurrentTCB );\r
+ ulCriticalNesting = portNO_CRITICAL_NESTING;\r
+\r
+ vPortTrace( "Created system threads, starting task" );\r
+\r
+ ResumeThread( pxThreadState->pvThread );\r
+ } \r
+ \r
+ /* Handle all pseudo interrupts - including yield requests and simulated ticks. */\r
+ prvProcessEvents();\r
+\r
+ /* Would not expect to return from prvProcessEvents(), so should not get here. */\r
+ return 0;\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvProcessEvents( void )\r
+{\r
+long lSwitchRequired, lAcknowledgeTick, lAcknowledgeInterrupt;\r
+xThreadState *pxThreadState;\r
+void *pvObjectList[ 2 ];\r
+unsigned long i;\r
+char cTraceBuffer[ 256 ];\r
+\r
+ vPortTrace( "Entering prvProcessEvents\r\n" );\r
+\r
+ /* Going to block on the mutex that ensured exclusive access to the pdeudo \r
+ interrupt objects, and the event that signals that an interrupt is waiting\r
+ to be processed. */\r
+ pvObjectList[ 0 ] = pvInterruptEventMutex;\r
+ pvObjectList[ 1 ] = pvInterruptEvent;\r
+\r
+ for(;;)\r
+ {\r
+ vPortTrace( "prvProcessEvents: Waiting for next interrupt event\r\n" );\r
+ WaitForMultipleObjects( sizeof( pvObjectList ) / sizeof( void * ), pvObjectList, TRUE, INFINITE );\r
+ vPortTrace( "prvProcessEvents: Got interrupt event and mutex\r\n" );\r
+ //vPortTrace( "prvProcessEvents: Waiting for next interrupt event\r\n" );\r
+ //WaitForSingleObject( pvInterruptEvent, INFINITE );\r
+ //vPortTrace( "prvProcessEvents: Waiting interrupt event mutex to access interrupt data\r\n" );\r
+ //WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
+\r
+ lSwitchRequired = pdFALSE;\r
+ lAcknowledgeTick = pdFALSE;\r
+ lAcknowledgeInterrupt = pdFALSE;\r
+\r
+ /* For each interrupt we are interested in processing, each of which is \r
+ represented by a bit in the 32bit ulPendingInterrupts variable. */\r
+ for( i = 0; i < portMAX_INTERRUPTS; i++ )\r
+ {\r
+ /* Is the pseudo interrupt pending? */\r
+ if( ulPendingInterrupts & ( 1UL << i ) )\r
+ {\r
+ switch( i )\r
+ {\r
+ case portINTERRUPT_YIELD:\r
+\r
+ vPortTrace( "prvProcessEvents: Processing Yield\r\n" );\r
+ /* Yield interrupts occur no matter what the critical nesting count. */\r
+ lSwitchRequired = pdTRUE;\r
+\r
+ /* Clear the interrupt pending bit. */\r
+ ulPendingInterrupts &= ~( 1UL << portINTERRUPT_YIELD );\r
+\r
+ lAcknowledgeInterrupt = pdTRUE;\r
+ break;\r
+\r
+ case portINTERRUPT_TICK:\r
+ \r
+ /* Tick interrupts should only be processed if the critical nesting count\r
+ is zero. The critical nesting count represents the interrupt mask on \r
+ real target hardware. */\r
+ vPortTrace( "prvProcessEvents: Processing tick event\r\n" );\r
+ if( ulCriticalNesting == 0 )\r
+ {\r
+ /* Process the tick itself. */\r
+ vPortTrace( "prvProcessEvents: Incrementing tick\r\n" );\r
+ vTaskIncrementTick();\r
+ #if( configUSE_PREEMPTION != 0 )\r
+ {\r
+ /* A context switch is only automatically performed from the tick\r
+ interrupt if the pre-emptive scheduler is being used. */\r
+ lSwitchRequired = pdTRUE;\r
+ }\r
+ #endif\r
+ \r
+ lAcknowledgeTick = pdTRUE;\r
+\r
+ /* Clear the interrupt pending bit. */\r
+ ulPendingInterrupts &= ~( 1UL << portINTERRUPT_TICK );\r
+ }\r
+ break;\r
+\r
+ default:\r
+\r
+ /* Is a handler installed? */\r
+ if( vIsrHandler[ i ] != NULL )\r
+ {\r
+ lSwitchRequired = pdTRUE;\r
+\r
+ /* Run the actual handler. */\r
+ vIsrHandler[ i ]();\r
+\r
+ /* Clear the interrupt pending bit. */\r
+ ulPendingInterrupts &= ~( 1UL << i );\r
+\r
+ lAcknowledgeInterrupt = pdTRUE;\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ if( lSwitchRequired != pdFALSE )\r
+ {\r
+ void *pvOldCurrentTCB;\r
+\r
+ pvOldCurrentTCB = pxCurrentTCB;\r
+\r
+ /* Save the state of the current thread before suspending it. */\r
+ pxThreadState = ( xThreadState *) *( ( unsigned long * ) pxCurrentTCB );\r
+ pxThreadState->ulCriticalNesting = ulCriticalNesting ;\r
+ \r
+ /* Select the next task to run. */\r
+ vTaskSwitchContext();\r
+ \r
+ /* If the task selected to enter the running state is not the task\r
+ that is already in the running state. */\r
+ if( pvOldCurrentTCB != pxCurrentTCB )\r
+ {\r
+ /* Suspend the old thread. */\r
+ SuspendThread( pxThreadState->pvThread );\r
+ sprintf( cTraceBuffer, "Event processor: suspending %s, resuming %s\r\n", ((xTCB*)pvOldCurrentTCB)->pcTaskName, ((xTCB*)pxCurrentTCB)->pcTaskName );\r
+ vPortTrace( cTraceBuffer );\r
+\r
+ /* Obtain the state of the task now selected to enter the Running state. */\r
+ pxThreadState = ( xThreadState * ) ( *( unsigned long *) pxCurrentTCB );\r
+ ulCriticalNesting = pxThreadState->ulCriticalNesting;\r
+ ResumeThread( pxThreadState->pvThread );\r
+ }\r
+ }\r
+\r
+ /* Was a tick processed? */\r
+ if( lAcknowledgeTick != pdFALSE )\r
+ {\r
+ vPortTrace( "prvProcessEvents: Acking tick\r\n" );\r
+ SetEvent( pvTickAcknowledgeEvent );\r
+ }\r
+\r
+ if( lAcknowledgeInterrupt != pdFALSE )\r
+ {\r
+ vPortTrace( "prvProcessEvents: Acking interrupt\r\n" );\r
+ SetEvent( pvInterruptAcknowledgeEvent );\r
+ }\r
+\r
+ ReleaseMutex( pvInterruptEventMutex );\r
+ }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+void vPortEndScheduler( void )\r
+{\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+void vPortGeneratePseudoInterrupt( unsigned long ulInterruptNumber )\r
+{\r
+ if( ( ulInterruptNumber < portMAX_INTERRUPTS ) && ( pvInterruptEventMutex != NULL ) )\r
+ {\r
+ /* Yield interrupts are processed even when critical nesting is non-zero. */\r
+ if( ( ulCriticalNesting == 0 ) || ( ulInterruptNumber == portINTERRUPT_YIELD ) )\r
+ {\r
+ /* In case this task has just started running, reset the interrupt\r
+ acknowledge event as it might have been set due to the activities\r
+ of a thread that has already been executed and suspended. */\r
+ ResetEvent( pvInterruptAcknowledgeEvent );\r
+\r
+ WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
+ ulPendingInterrupts |= ( 1 << ulInterruptNumber );\r
+ vPortTrace( "vPortGeneratePseudoInterrupt: Got interrupt mutex, about to signal interrupt event\r\n" );\r
+ SetEvent( pvInterruptEvent );\r
+ vPortTrace( "vPortGeneratePseudoInterrupt: About to release interrupt event mutex\r\n" );\r
+ ReleaseMutex( pvInterruptEventMutex );\r
+ vPortTrace( "vPortGeneratePseudoInterrupt: Interrupt event mutex released, going to wait for next interrupt input\r\n" );\r
+\r
+ WaitForSingleObject( pvInterruptAcknowledgeEvent, INFINITE );\r
+ vPortTrace( "vPortGeneratePseudoInterrupt: Interrupt acknowledged, leaving vPortGeneratePseudoInterrupt()\r\n" );\r
+ }\r
+ }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+void vPortSetInterruptHandler( unsigned long ulInterruptNumber, void (*pvHandler)( void ) )\r
+{\r
+ if( ulInterruptNumber < portMAX_INTERRUPTS )\r
+ {\r
+ if( pvInterruptEventMutex != NULL )\r
+ {\r
+ WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
+ vIsrHandler[ ulInterruptNumber ] = pvHandler;\r
+ ReleaseMutex( pvInterruptEventMutex );\r
+ }\r
+ else\r
+ {\r
+ vIsrHandler[ ulInterruptNumber ] = pvHandler;\r
+ }\r
+ }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+void vPortEnterCritical( void )\r
+{\r
+ ulCriticalNesting++;\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+void vPortExitCritical( void )\r
+{\r
+ if( ulCriticalNesting > portNO_CRITICAL_NESTING )\r
+ {\r
+ ulCriticalNesting--;\r
+\r
+ if( ulCriticalNesting == 0 )\r
+ {\r
+ /* Were any interrupts set to pending while interrupts were \r
+ (pseudo) disabled? */\r
+ if( ulPendingInterrupts != 0UL )\r
+ {\r
+ WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
+ vPortTrace( "vPortExitCritical: Setting interrupt event\r\n" );\r
+ SetEvent( pvInterruptEvent );\r
+ ReleaseMutex( pvInterruptEventMutex );\r
+\r
+ vPortTrace( "vPortExitCritical: Waiting interrupt ack\r\n" );\r
+ WaitForSingleObject( pvInterruptAcknowledgeEvent, INFINITE );\r
+ vPortTrace( "vPortExitCritical: Interrupt acknowledged, leaving critical section code\r\n" );\r
+\r
+ /* Just in case the Yield does not happen immediately. This\r
+ line could be dangerious if not all interrupts are being\r
+ processed. */\r
+// while( ulPendingInterrupts != 0UL );\r
+ }\r
+ }\r
+ }\r
+}\r