1 /*
2 * FreeRTOS Kernel <DEVELOPMENT BRANCH>
3 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy of
8 * this software and associated documentation files (the "Software"), to deal in
9 * the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11 * the Software, and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in all
15 * copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * https://www.FreeRTOS.org
25 * https://github.com/FreeRTOS
26 *
27 */
28
29 /* Standard includes. */
30 #include <stdio.h>
31
32 /* Scheduler includes. */
33 #include "FreeRTOS.h"
34 #include "task.h"
35
36 #ifdef WIN32_LEAN_AND_MEAN
37 #include <winsock2.h>
38 #else
39 #include <winsock.h>
40 #endif
41
42 #include <timeapi.h>
43
44 #ifdef __GNUC__
45 #include "mmsystem.h"
46 #else
47 #pragma comment(lib, "winmm.lib")
48 #endif
49
50 #define portMAX_INTERRUPTS ( ( uint32_t ) sizeof( uint32_t ) * 8UL ) /* The number of bits in an uint32_t. */
51 #define portNO_CRITICAL_NESTING ( ( uint32_t ) 0 )
52
53 /* The priorities at which the various components of the simulation execute. */
54 #define portDELETE_SELF_THREAD_PRIORITY THREAD_PRIORITY_TIME_CRITICAL /* Must be highest. */
55 #define portSIMULATED_INTERRUPTS_THREAD_PRIORITY THREAD_PRIORITY_TIME_CRITICAL
56 #define portSIMULATED_TIMER_THREAD_PRIORITY THREAD_PRIORITY_HIGHEST
57 #define portTASK_THREAD_PRIORITY THREAD_PRIORITY_ABOVE_NORMAL
58
59 /*
60 * Created as a high priority thread, this function uses a timer to simulate
61 * a tick interrupt being generated on an embedded target. In this Windows
62 * environment the timer does not achieve anything approaching real time
63 * performance though.
64 */
65 static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter );
66
67 /*
68 * Process all the simulated interrupts - each represented by a bit in
69 * ulPendingInterrupts variable.
70 */
71 static void prvProcessSimulatedInterrupts( void );
72
73 /*
74 * Interrupt handlers used by the kernel itself. These are executed from the
75 * simulated interrupt handler thread.
76 */
77 static uint32_t prvProcessYieldInterrupt( void );
78 static uint32_t prvProcessTickInterrupt( void );
79
80 /*
81 * Exiting a critical section will cause the calling task to block on yield
82 * event to wait for an interrupt to process if an interrupt was pended while
83 * inside the critical section. This variable protects against a recursive
84 * attempt to obtain pvInterruptEventMutex if a critical section is used inside
85 * an interrupt handler itself.
86 */
87 volatile BaseType_t xInsideInterrupt = pdFALSE;
88
89 /*
90 * Called when the process exits to let Windows know the high timer resolution
91 * is no longer required.
92 */
93 static BOOL WINAPI prvEndProcess( DWORD dwCtrlType );
94
95 /*-----------------------------------------------------------*/
96
97 /* The WIN32 simulator runs each task in a thread. The context switching is
98 * managed by the threads, so the task stack does not have to be managed directly,
99 * although the task stack is still used to hold an xThreadState structure this is
100 * the only thing it will ever hold. The structure indirectly maps the task handle
101 * to a thread handle. */
102 typedef struct
103 {
104 /* Handle of the thread that executes the task. */
105 void * pvThread;
106
107 /* Event used to make sure the thread does not execute past a yield point
108 * between the call to SuspendThread() to suspend the thread and the
109 * asynchronous SuspendThread() operation actually being performed. */
110 void * pvYieldEvent;
111 } ThreadState_t;
112
113 /* Simulated interrupts waiting to be processed. This is a bit mask where each
114 * bit represents one interrupt, so a maximum of 32 interrupts can be simulated. */
115 static volatile uint32_t ulPendingInterrupts = 0UL;
116
117 /* An event used to inform the simulated interrupt processing thread (a high
118 * priority thread that simulated interrupt processing) that an interrupt is
119 * pending. */
120 static void * pvInterruptEvent = NULL;
121
122 /* Mutex used to protect all the simulated interrupt variables that are accessed
123 * by multiple threads. */
124 static void * pvInterruptEventMutex = NULL;
125
126 /* The critical nesting count for the currently executing task. This is
127 * initialised to a non-zero value so interrupts do not become enabled during
128 * the initialisation phase. As each task has its own critical nesting value
129 * ulCriticalNesting will get set to zero when the first task runs. This
130 * initialisation is probably not critical in this simulated environment as the
131 * simulated interrupt handlers do not get created until the FreeRTOS scheduler is
132 * started anyway. */
133 static volatile uint32_t ulCriticalNesting = 9999UL;
134
135 /* Handlers for all the simulated software interrupts. The first two positions
136 * are used for the Yield and Tick interrupts so are handled slightly differently,
137 * all the other interrupts can be user defined. */
138 static uint32_t (* ulIsrHandler[ portMAX_INTERRUPTS ])( void ) = { 0 };
139
140 /* Pointer to the TCB of the currently executing task. */
141 extern void * volatile pxCurrentTCB;
142
143 /* Used to ensure nothing is processed during the startup sequence. */
144 static BaseType_t xPortRunning = pdFALSE;
145
146 /*-----------------------------------------------------------*/
147
prvSimulatedPeripheralTimer(LPVOID lpParameter)148 static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter )
149 {
150 TickType_t xMinimumWindowsBlockTime;
151 TIMECAPS xTimeCaps;
152 TickType_t xWaitTimeBetweenTicks = portTICK_PERIOD_MS;
153 HANDLE hTimer = NULL;
154 LARGE_INTEGER liDueTime;
155 BOOL bSuccess;
156
157 /* Set the timer resolution to the maximum possible. */
158 if( timeGetDevCaps( &xTimeCaps, sizeof( xTimeCaps ) ) == MMSYSERR_NOERROR )
159 {
160 xMinimumWindowsBlockTime = ( TickType_t ) xTimeCaps.wPeriodMin;
161 timeBeginPeriod( xTimeCaps.wPeriodMin );
162
163 /* Register an exit handler so the timeBeginPeriod() function can be
164 * matched with a timeEndPeriod() when the application exits. */
165 SetConsoleCtrlHandler( prvEndProcess, TRUE );
166 }
167 else
168 {
169 xMinimumWindowsBlockTime = ( TickType_t ) 20;
170 }
171
172 /* Just to prevent compiler warnings. */
173 ( void ) lpParameter;
174
175 /* Tick time for the timer is adjusted with the maximum available
176 resolution. */
177 if( portTICK_PERIOD_MS < xMinimumWindowsBlockTime )
178 {
179 xWaitTimeBetweenTicks = xMinimumWindowsBlockTime;
180 }
181
182 /* Convert the tick time in milliseconds to nanoseconds resolution
183 for the Waitable Timer. */
184 liDueTime.u.LowPart = xWaitTimeBetweenTicks * 1000 * 1000;
185 liDueTime.u.HighPart = 0;
186
187 /* Create a synchronization Waitable Timer.*/
188 hTimer = CreateWaitableTimer( NULL, FALSE, NULL );
189
190 configASSERT( hTimer != NULL );
191
192 /* Set the Waitable Timer. The timer is set to run periodically at every
193 xWaitTimeBetweenTicks milliseconds. */
194 bSuccess = SetWaitableTimer( hTimer, &liDueTime, xWaitTimeBetweenTicks, NULL, NULL, 0 );
195 configASSERT( bSuccess );
196
197 while( xPortRunning == pdTRUE )
198 {
199 /* Wait until the timer expires and we can access the simulated interrupt
200 * variables. */
201
202 WaitForSingleObject( hTimer, INFINITE );
203
204 vPortGenerateSimulatedInterruptFromWindowsThread( portINTERRUPT_TICK );
205 }
206
207 return 0;
208 }
209 /*-----------------------------------------------------------*/
210
prvEndProcess(DWORD dwCtrlType)211 static BOOL WINAPI prvEndProcess( DWORD dwCtrlType )
212 {
213 TIMECAPS xTimeCaps;
214
215 ( void ) dwCtrlType;
216
217 if( timeGetDevCaps( &xTimeCaps, sizeof( xTimeCaps ) ) == MMSYSERR_NOERROR )
218 {
219 /* Match the call to timeBeginPeriod( xTimeCaps.wPeriodMin ) made when
220 * the process started with a timeEndPeriod() as the process exits. */
221 timeEndPeriod( xTimeCaps.wPeriodMin );
222 }
223
224 return pdFALSE;
225 }
226 /*-----------------------------------------------------------*/
227
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)228 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
229 TaskFunction_t pxCode,
230 void * pvParameters )
231 {
232 ThreadState_t * pxThreadState = NULL;
233 int8_t * pcTopOfStack = ( int8_t * ) pxTopOfStack;
234 const SIZE_T xStackSize = 1024; /* Set the size to a small number which will get rounded up to the minimum possible. */
235
236 /* In this simulated case a stack is not initialised, but instead a thread
237 * is created that will execute the task being created. The thread handles
238 * the context switching itself. The ThreadState_t object is placed onto
239 * the stack that was created for the task - so the stack buffer is still
240 * used, just not in the conventional way. It will not be used for anything
241 * other than holding this structure. */
242 pxThreadState = ( ThreadState_t * ) ( pcTopOfStack - sizeof( ThreadState_t ) );
243
244 /* Create the event used to prevent the thread from executing past its yield
245 * point if the SuspendThread() call that suspends the thread does not take
246 * effect immediately (it is an asynchronous call). */
247 pxThreadState->pvYieldEvent = CreateEvent( NULL, /* Default security attributes. */
248 FALSE, /* Auto reset. */
249 FALSE, /* Start not signalled. */
250 NULL ); /* No name. */
251
252
253 #ifdef __GNUC__
254 /* GCC reports the warning for the cast operation from TaskFunction_t to LPTHREAD_START_ROUTINE. */
255 /* Disable this warning here by the #pragma option. */
256 #pragma GCC diagnostic push
257 #pragma GCC diagnostic ignored "-Wcast-function-type"
258 #endif
259 /* Create the thread itself. */
260 pxThreadState->pvThread = CreateThread( NULL, xStackSize, ( LPTHREAD_START_ROUTINE ) pxCode, pvParameters, CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, NULL );
261 #ifdef __GNUC__
262 #pragma GCC diagnostic pop
263 #endif
264
265 configASSERT( pxThreadState->pvThread ); /* See comment where TerminateThread() is called. */
266 SetThreadAffinityMask( pxThreadState->pvThread, 0x01 );
267 SetThreadPriorityBoost( pxThreadState->pvThread, TRUE );
268 SetThreadPriority( pxThreadState->pvThread, portTASK_THREAD_PRIORITY );
269
270 return ( StackType_t * ) pxThreadState;
271 }
272 /*-----------------------------------------------------------*/
273
xPortStartScheduler(void)274 BaseType_t xPortStartScheduler( void )
275 {
276 void * pvHandle = NULL;
277 int32_t lSuccess;
278 ThreadState_t * pxThreadState = NULL;
279 SYSTEM_INFO xSystemInfo;
280
281 /* This port runs windows threads with extremely high priority. All the
282 * threads execute on the same core - to prevent locking up the host only start
283 * if the host has multiple cores. */
284 GetSystemInfo( &xSystemInfo );
285
286 if( xSystemInfo.dwNumberOfProcessors <= 1 )
287 {
288 printf( "This version of the FreeRTOS Windows port can only be used on multi-core hosts.\r\n" );
289 lSuccess = pdFAIL;
290 }
291 else
292 {
293 lSuccess = pdPASS;
294
295 /* The highest priority class is used to [try to] prevent other Windows
296 * activity interfering with FreeRTOS timing too much. */
297 if( SetPriorityClass( GetCurrentProcess(), REALTIME_PRIORITY_CLASS ) == 0 )
298 {
299 printf( "SetPriorityClass() failed\r\n" );
300 }
301
302 /* Install the interrupt handlers used by the scheduler itself. */
303 vPortSetInterruptHandler( portINTERRUPT_YIELD, prvProcessYieldInterrupt );
304 vPortSetInterruptHandler( portINTERRUPT_TICK, prvProcessTickInterrupt );
305
306 /* Create the events and mutexes that are used to synchronise all the
307 * threads. */
308 pvInterruptEventMutex = CreateMutex( NULL, FALSE, NULL );
309 pvInterruptEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
310
311 if( ( pvInterruptEventMutex == NULL ) || ( pvInterruptEvent == NULL ) )
312 {
313 lSuccess = pdFAIL;
314 }
315
316 /* Set the priority of this thread such that it is above the priority of
317 * the threads that run tasks. This higher priority is required to ensure
318 * simulated interrupts take priority over tasks. */
319 pvHandle = GetCurrentThread();
320
321 if( pvHandle == NULL )
322 {
323 lSuccess = pdFAIL;
324 }
325 }
326
327 if( lSuccess == pdPASS )
328 {
329 if( SetThreadPriority( pvHandle, portSIMULATED_INTERRUPTS_THREAD_PRIORITY ) == 0 )
330 {
331 lSuccess = pdFAIL;
332 }
333
334 SetThreadPriorityBoost( pvHandle, TRUE );
335 SetThreadAffinityMask( pvHandle, 0x01 );
336 }
337
338 if( lSuccess == pdPASS )
339 {
340 /* Start the thread that simulates the timer peripheral to generate
341 * tick interrupts. The priority is set below that of the simulated
342 * interrupt handler so the interrupt event mutex is used for the
343 * handshake / overrun protection. */
344 pvHandle = CreateThread( NULL, 0, prvSimulatedPeripheralTimer, NULL, CREATE_SUSPENDED, NULL );
345
346 if( pvHandle != NULL )
347 {
348 SetThreadPriority( pvHandle, portSIMULATED_TIMER_THREAD_PRIORITY );
349 SetThreadPriorityBoost( pvHandle, TRUE );
350 SetThreadAffinityMask( pvHandle, 0x01 );
351 ResumeThread( pvHandle );
352 }
353
354 /* Start the highest priority task by obtaining its associated thread
355 * state structure, in which is stored the thread handle. */
356 pxThreadState = ( ThreadState_t * ) *( ( size_t * ) pxCurrentTCB );
357 ulCriticalNesting = portNO_CRITICAL_NESTING;
358
359 /* The scheduler is now running. */
360 xPortRunning = pdTRUE;
361
362 /* Start the first task. */
363 ResumeThread( pxThreadState->pvThread );
364
365 /* Handle all simulated interrupts - including yield requests and
366 * simulated ticks. */
367 prvProcessSimulatedInterrupts();
368 }
369
370 /* Would not expect to return from prvProcessSimulatedInterrupts(), so should
371 * not get here. */
372 return 0;
373 }
374 /*-----------------------------------------------------------*/
375
prvProcessYieldInterrupt(void)376 static uint32_t prvProcessYieldInterrupt( void )
377 {
378 /* Always return true as this is a yield. */
379 return pdTRUE;
380 }
381 /*-----------------------------------------------------------*/
382
prvProcessTickInterrupt(void)383 static uint32_t prvProcessTickInterrupt( void )
384 {
385 uint32_t ulSwitchRequired;
386
387 /* Process the tick itself. */
388 configASSERT( xPortRunning );
389 ulSwitchRequired = ( uint32_t ) xTaskIncrementTick();
390
391 return ulSwitchRequired;
392 }
393 /*-----------------------------------------------------------*/
394
prvProcessSimulatedInterrupts(void)395 static void prvProcessSimulatedInterrupts( void )
396 {
397 uint32_t ulSwitchRequired, i;
398 ThreadState_t * pxThreadState;
399 void * pvObjectList[ 2 ];
400 CONTEXT xContext;
401 DWORD xWinApiResult;
402 const DWORD xTimeoutMilliseconds = 1000;
403
404 /* Going to block on the mutex that ensured exclusive access to the simulated
405 * interrupt objects, and the event that signals that a simulated interrupt
406 * should be processed. */
407 pvObjectList[ 0 ] = pvInterruptEventMutex;
408 pvObjectList[ 1 ] = pvInterruptEvent;
409
410 /* Create a pending tick to ensure the first task is started as soon as
411 * this thread pends. */
412 ulPendingInterrupts |= ( 1 << portINTERRUPT_TICK );
413 SetEvent( pvInterruptEvent );
414
415 while( xPortRunning == pdTRUE )
416 {
417 xInsideInterrupt = pdFALSE;
418
419 /* Wait with timeout so that we can exit from this loop when
420 * the scheduler is stopped by calling vPortEndScheduler. */
421 xWinApiResult = WaitForMultipleObjects( sizeof( pvObjectList ) / sizeof( void * ), pvObjectList, TRUE, xTimeoutMilliseconds );
422
423 if( xWinApiResult != WAIT_TIMEOUT )
424 {
425 /* Cannot be in a critical section to get here. Tasks that exit a
426 * critical section will block on a yield mutex to wait for an interrupt to
427 * process if an interrupt was set pending while the task was inside the
428 * critical section. xInsideInterrupt prevents interrupts that contain
429 * critical sections from doing the same. */
430 xInsideInterrupt = pdTRUE;
431
432 /* Used to indicate whether the simulated interrupt processing has
433 * necessitated a context switch to another task/thread. */
434 ulSwitchRequired = pdFALSE;
435
436 /* For each interrupt we are interested in processing, each of which is
437 * represented by a bit in the 32bit ulPendingInterrupts variable. */
438 for( i = 0; i < portMAX_INTERRUPTS; i++ )
439 {
440 /* Is the simulated interrupt pending? */
441 if( ( ulPendingInterrupts & ( 1UL << i ) ) != 0 )
442 {
443 /* Is a handler installed? */
444 if( ulIsrHandler[ i ] != NULL )
445 {
446 /* Run the actual handler. Handlers return pdTRUE if they
447 * necessitate a context switch. */
448 if( ulIsrHandler[ i ]() != pdFALSE )
449 {
450 /* A bit mask is used purely to help debugging. */
451 ulSwitchRequired |= ( 1 << i );
452 }
453 }
454
455 /* Clear the interrupt pending bit. */
456 ulPendingInterrupts &= ~( 1UL << i );
457 }
458 }
459
460 if( ulSwitchRequired != pdFALSE )
461 {
462 /* Suspend the old thread. */
463 pxThreadState = ( ThreadState_t * ) *( ( size_t * ) pxCurrentTCB );
464 SuspendThread( pxThreadState->pvThread );
465
466 /* Ensure the thread is actually suspended by performing a
467 * synchronous operation that can only complete when the thread
468 * is actually suspended. The below code asks for dummy register
469 * data. Experimentation shows that these two lines don't appear
470 * to do anything now, but according to
471 * https://devblogs.microsoft.com/oldnewthing/20150205-00/?p=44743
472 * they do - so as they do not harm (slight run-time hit). */
473 xContext.ContextFlags = CONTEXT_INTEGER;
474 ( void ) GetThreadContext( pxThreadState->pvThread, &xContext );
475
476 /* Select the next task to run. */
477 vTaskSwitchContext();
478
479 /* Obtain the state of the task now selected to enter the
480 * Running state. */
481 pxThreadState = ( ThreadState_t * ) ( *( size_t * ) pxCurrentTCB );
482
483 /* pxThreadState->pvThread can be NULL if the task deleted
484 * itself - but a deleted task should never be resumed here. */
485 configASSERT( pxThreadState->pvThread != NULL );
486 ResumeThread( pxThreadState->pvThread );
487 }
488
489 /* If the thread that is about to be resumed stopped running
490 * because it yielded then it will wait on an event when it resumed
491 * (to ensure it does not continue running after the call to
492 * SuspendThread() above as SuspendThread() is asynchronous).
493 * Signal the event to ensure the thread can proceed now it is
494 * valid for it to do so. Signaling the event is benign in the case that
495 * the task was switched out asynchronously by an interrupt as the event
496 * is reset before the task blocks on it. */
497 pxThreadState = ( ThreadState_t * ) ( *( size_t * ) pxCurrentTCB );
498 SetEvent( pxThreadState->pvYieldEvent );
499 ReleaseMutex( pvInterruptEventMutex );
500 }
501 }
502 }
503 /*-----------------------------------------------------------*/
504
vPortDeleteThread(void * pvTaskToDelete)505 void vPortDeleteThread( void * pvTaskToDelete )
506 {
507 ThreadState_t * pxThreadState;
508 uint32_t ulErrorCode;
509
510 /* Remove compiler warnings if configASSERT() is not defined. */
511 ( void ) ulErrorCode;
512
513 /* Find the handle of the thread being deleted. */
514 pxThreadState = ( ThreadState_t * ) ( *( size_t * ) pvTaskToDelete );
515
516 /* Check that the thread is still valid, it might have been closed by
517 * vPortCloseRunningThread() - which will be the case if the task associated
518 * with the thread originally deleted itself rather than being deleted by a
519 * different task. */
520 if( pxThreadState->pvThread != NULL )
521 {
522 WaitForSingleObject( pvInterruptEventMutex, INFINITE );
523
524 /* !!! This is not a nice way to terminate a thread, and will eventually
525 * result in resources being depleted if tasks frequently delete other
526 * tasks (rather than deleting themselves) as the task stacks will not be
527 * freed. */
528 ulErrorCode = TerminateThread( pxThreadState->pvThread, 0 );
529 configASSERT( ulErrorCode );
530
531 ulErrorCode = CloseHandle( pxThreadState->pvThread );
532 configASSERT( ulErrorCode );
533
534 ReleaseMutex( pvInterruptEventMutex );
535 }
536 }
537 /*-----------------------------------------------------------*/
538
vPortCloseRunningThread(void * pvTaskToDelete,volatile BaseType_t * pxPendYield)539 void vPortCloseRunningThread( void * pvTaskToDelete,
540 volatile BaseType_t * pxPendYield )
541 {
542 ThreadState_t * pxThreadState;
543 void * pvThread;
544 uint32_t ulErrorCode;
545
546 /* Remove compiler warnings if configASSERT() is not defined. */
547 ( void ) ulErrorCode;
548
549 /* Find the handle of the thread being deleted. */
550 pxThreadState = ( ThreadState_t * ) ( *( size_t * ) pvTaskToDelete );
551 pvThread = pxThreadState->pvThread;
552
553 /* Raise the Windows priority of the thread to ensure the FreeRTOS scheduler
554 * does not run and swap it out before it is closed. If that were to happen
555 * the thread would never run again and effectively be a thread handle and
556 * memory leak. */
557 SetThreadPriority( pvThread, portDELETE_SELF_THREAD_PRIORITY );
558
559 /* This function will not return, therefore a yield is set as pending to
560 * ensure a context switch occurs away from this thread on the next tick. */
561 *pxPendYield = pdTRUE;
562
563 /* Mark the thread associated with this task as invalid so
564 * vPortDeleteThread() does not try to terminate it. */
565 pxThreadState->pvThread = NULL;
566
567 /* Close the thread. */
568 ulErrorCode = CloseHandle( pvThread );
569 configASSERT( ulErrorCode );
570
571 /* This is called from a critical section, which must be exited before the
572 * thread stops. */
573 taskEXIT_CRITICAL();
574
575 /* Record that a yield is pending so that the next tick interrupt switches
576 * out this thread regardless of the value of configUSE_PREEMPTION. This is
577 * needed when a task deletes itself - the taskYIELD_WITHIN_API within
578 * vTaskDelete does not get called because this function never returns. If
579 * we do not pend portINTERRUPT_YIELD here, the next task is not scheduled
580 * when configUSE_PREEMPTION is set to 0. */
581 if( pvInterruptEventMutex != NULL )
582 {
583 WaitForSingleObject( pvInterruptEventMutex, INFINITE );
584 ulPendingInterrupts |= ( 1 << portINTERRUPT_YIELD );
585 ReleaseMutex( pvInterruptEventMutex );
586 }
587
588 CloseHandle( pxThreadState->pvYieldEvent );
589 ExitThread( 0 );
590 }
591 /*-----------------------------------------------------------*/
592
vPortEndScheduler(void)593 void vPortEndScheduler( void )
594 {
595 xPortRunning = pdFALSE;
596 }
597 /*-----------------------------------------------------------*/
598
vPortGenerateSimulatedInterrupt(uint32_t ulInterruptNumber)599 void vPortGenerateSimulatedInterrupt( uint32_t ulInterruptNumber )
600 {
601 ThreadState_t * pxThreadState = ( ThreadState_t * ) *( ( size_t * ) pxCurrentTCB );
602
603 configASSERT( xPortRunning );
604
605 if( ( ulInterruptNumber < portMAX_INTERRUPTS ) && ( pvInterruptEventMutex != NULL ) )
606 {
607 WaitForSingleObject( pvInterruptEventMutex, INFINITE );
608 ulPendingInterrupts |= ( 1 << ulInterruptNumber );
609
610 /* The simulated interrupt is now held pending, but don't actually
611 * process it yet if this call is within a critical section. It is
612 * possible for this to be in a critical section as calls to wait for
613 * mutexes are accumulative. If in a critical section then the event
614 * will get set when the critical section nesting count is wound back
615 * down to zero. */
616 if( ulCriticalNesting == portNO_CRITICAL_NESTING )
617 {
618 SetEvent( pvInterruptEvent );
619
620 /* Going to wait for an event - make sure the event is not already
621 * signaled. */
622 ResetEvent( pxThreadState->pvYieldEvent );
623 }
624
625 ReleaseMutex( pvInterruptEventMutex );
626
627 if( ulCriticalNesting == portNO_CRITICAL_NESTING )
628 {
629 /* An interrupt was pended so ensure to block to allow it to
630 * execute. In most cases the (simulated) interrupt will have
631 * executed before the next line is reached - so this is just to make
632 * sure. */
633 WaitForSingleObject( pxThreadState->pvYieldEvent, INFINITE );
634 }
635 }
636 }
637 /*-----------------------------------------------------------*/
638
vPortGenerateSimulatedInterruptFromWindowsThread(uint32_t ulInterruptNumber)639 void vPortGenerateSimulatedInterruptFromWindowsThread( uint32_t ulInterruptNumber )
640 {
641 if( xPortRunning == pdTRUE )
642 {
643 /* Can't proceed if in a critical section as pvInterruptEventMutex won't
644 * be available. */
645 WaitForSingleObject( pvInterruptEventMutex, INFINITE );
646
647 /* Pending a user defined interrupt to be handled in simulated interrupt
648 * handler thread. */
649 ulPendingInterrupts |= ( 1 << ulInterruptNumber );
650
651 /* The interrupt is now pending - notify the simulated interrupt
652 * handler thread. Must be outside of a critical section to get here so
653 * the handler thread can execute immediately pvInterruptEventMutex is
654 * released. */
655 configASSERT( ulCriticalNesting == 0UL );
656 SetEvent( pvInterruptEvent );
657
658 /* Give back the mutex so the simulated interrupt handler unblocks
659 * and can access the interrupt handler variables. */
660 ReleaseMutex( pvInterruptEventMutex );
661 }
662 }
663 /*-----------------------------------------------------------*/
664
vPortSetInterruptHandler(uint32_t ulInterruptNumber,uint32_t (* pvHandler)(void))665 void vPortSetInterruptHandler( uint32_t ulInterruptNumber,
666 uint32_t ( * pvHandler )( void ) )
667 {
668 if( ulInterruptNumber < portMAX_INTERRUPTS )
669 {
670 if( pvInterruptEventMutex != NULL )
671 {
672 WaitForSingleObject( pvInterruptEventMutex, INFINITE );
673 ulIsrHandler[ ulInterruptNumber ] = pvHandler;
674 ReleaseMutex( pvInterruptEventMutex );
675 }
676 else
677 {
678 ulIsrHandler[ ulInterruptNumber ] = pvHandler;
679 }
680 }
681 }
682 /*-----------------------------------------------------------*/
683
vPortEnterCritical(void)684 void vPortEnterCritical( void )
685 {
686 if( xPortRunning == pdTRUE )
687 {
688 /* The interrupt event mutex is held for the entire critical section,
689 * effectively disabling (simulated) interrupts. */
690 WaitForSingleObject( pvInterruptEventMutex, INFINITE );
691 }
692
693 ulCriticalNesting++;
694 }
695 /*-----------------------------------------------------------*/
696
vPortExitCritical(void)697 void vPortExitCritical( void )
698 {
699 int32_t lMutexNeedsReleasing;
700
701 /* The interrupt event mutex should already be held by this thread as it was
702 * obtained on entry to the critical section. */
703 lMutexNeedsReleasing = pdTRUE;
704
705 if( ulCriticalNesting > portNO_CRITICAL_NESTING )
706 {
707 ulCriticalNesting--;
708
709 /* Don't need to wait for any pending interrupts to execute if the
710 * critical section was exited from inside an interrupt. */
711 if( ( ulCriticalNesting == portNO_CRITICAL_NESTING ) && ( xInsideInterrupt == pdFALSE ) )
712 {
713 /* Were any interrupts set to pending while interrupts were
714 * (simulated) disabled? */
715 if( ulPendingInterrupts != 0UL )
716 {
717 ThreadState_t * pxThreadState = ( ThreadState_t * ) *( ( size_t * ) pxCurrentTCB );
718
719 configASSERT( xPortRunning );
720
721 /* The interrupt won't actually executed until
722 * pvInterruptEventMutex is released as it waits on both
723 * pvInterruptEventMutex and pvInterruptEvent.
724 * pvInterruptEvent is only set when the simulated
725 * interrupt is pended if the interrupt is pended
726 * from outside a critical section - hence it is set
727 * here. */
728 SetEvent( pvInterruptEvent );
729
730 /* The calling task is going to wait for an event to ensure the
731 * interrupt that is pending executes immediately after the
732 * critical section is exited - so make sure the event is not
733 * already signaled. */
734 ResetEvent( pxThreadState->pvYieldEvent );
735
736 /* Mutex will be released now so the (simulated) interrupt can
737 * execute, so does not require releasing on function exit. */
738 lMutexNeedsReleasing = pdFALSE;
739 ReleaseMutex( pvInterruptEventMutex );
740 WaitForSingleObject( pxThreadState->pvYieldEvent, INFINITE );
741 }
742 }
743 }
744
745 if( pvInterruptEventMutex != NULL )
746 {
747 if( lMutexNeedsReleasing == pdTRUE )
748 {
749 configASSERT( xPortRunning );
750 ReleaseMutex( pvInterruptEventMutex );
751 }
752 }
753 }
754 /*-----------------------------------------------------------*/
755