2019-11-21 03:24:35 +08:00
|
|
|
/*
|
2019-11-21 03:38:08 +08:00
|
|
|
* Amazon FreeRTOS POSIX V1.1.0
|
2019-11-21 03:24:35 +08:00
|
|
|
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* http://aws.amazon.com/freertos
|
|
|
|
* http://www.FreeRTOS.org
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @file FreeRTOS_POSIX_timer.c
|
|
|
|
* @brief Implementation of timer functions in time.h
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* C standard library includes. */
|
|
|
|
#include <stddef.h>
|
|
|
|
|
|
|
|
/* FreeRTOS+POSIX includes. */
|
|
|
|
#include "FreeRTOS_POSIX.h"
|
|
|
|
#include "FreeRTOS_POSIX/errno.h"
|
|
|
|
#include "FreeRTOS_POSIX/pthread.h"
|
|
|
|
#include "FreeRTOS_POSIX/signal.h"
|
|
|
|
#include "FreeRTOS_POSIX/time.h"
|
|
|
|
#include "FreeRTOS_POSIX/utils.h"
|
|
|
|
|
|
|
|
/* FreeRTOS timer include. */
|
|
|
|
#include "timers.h"
|
|
|
|
|
2019-11-21 03:38:08 +08:00
|
|
|
/* Timespec zero check macros. */
|
2019-11-21 03:24:35 +08:00
|
|
|
#define TIMESPEC_IS_ZERO( xTimespec ) ( xTimespec.tv_sec == 0 && xTimespec.tv_nsec == 0 ) /**< Check for 0. */
|
|
|
|
#define TIMESPEC_IS_NOT_ZERO( xTimespec ) ( !( TIMESPEC_IS_ZERO( xTimespec ) ) ) /**< Check for not 0. */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Internal timer structure.
|
|
|
|
*/
|
|
|
|
typedef struct timer_internal
|
|
|
|
{
|
|
|
|
StaticTimer_t xTimerBuffer; /**< Memory that holds the FreeRTOS timer. */
|
|
|
|
struct sigevent xTimerEvent; /**< What to do when this timer expires. */
|
|
|
|
TickType_t xTimerPeriod; /**< Period of this timer. */
|
|
|
|
} timer_internal_t;
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
|
|
|
|
void prvTimerCallback( TimerHandle_t xTimerHandle )
|
|
|
|
{
|
|
|
|
timer_internal_t * pxTimer = ( timer_internal_t * ) pvTimerGetTimerID( xTimerHandle );
|
|
|
|
pthread_t xTimerNotificationThread;
|
|
|
|
|
|
|
|
/* The value of the timer ID, set in timer_create, should not be NULL. */
|
|
|
|
configASSERT( pxTimer != NULL );
|
|
|
|
|
|
|
|
/* A value of SIGEV_SIGNAL isn't supported and should not have been successfully
|
|
|
|
* set. */
|
|
|
|
configASSERT( pxTimer->xTimerEvent.sigev_notify != SIGEV_SIGNAL );
|
|
|
|
|
|
|
|
/* Update the timer period, which may need to be set to an it_interval
|
|
|
|
* argument. This call should not block. */
|
|
|
|
if( pxTimer->xTimerPeriod > 0 )
|
|
|
|
{
|
|
|
|
xTimerChangePeriod( xTimerHandle, pxTimer->xTimerPeriod, 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create the timer notification thread if requested. */
|
|
|
|
if( pxTimer->xTimerEvent.sigev_notify == SIGEV_THREAD )
|
|
|
|
{
|
2019-11-21 03:38:08 +08:00
|
|
|
/* if the user has provided thread attributes, create a thread
|
|
|
|
* with the provided attributes. Otherwise dispatch callback directly */
|
2019-11-21 03:24:35 +08:00
|
|
|
if( pxTimer->xTimerEvent.sigev_notify_attributes == NULL )
|
|
|
|
{
|
2019-11-21 03:38:08 +08:00
|
|
|
( *pxTimer->xTimerEvent.sigev_notify_function )( pxTimer->xTimerEvent.sigev_value );
|
2019-11-21 03:24:35 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
( void ) pthread_create( &xTimerNotificationThread,
|
|
|
|
pxTimer->xTimerEvent.sigev_notify_attributes,
|
|
|
|
( void * ( * )( void * ) )pxTimer->xTimerEvent.sigev_notify_function,
|
|
|
|
pxTimer->xTimerEvent.sigev_value.sival_ptr );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
|
|
|
|
int timer_create( clockid_t clockid,
|
|
|
|
struct sigevent * evp,
|
|
|
|
timer_t * timerid )
|
|
|
|
{
|
|
|
|
int iStatus = 0;
|
|
|
|
timer_internal_t * pxTimer = NULL;
|
|
|
|
|
|
|
|
/* Silence warnings about unused parameters. */
|
|
|
|
( void ) clockid;
|
|
|
|
|
|
|
|
/* POSIX specifies that when evp is NULL, the behavior shall be as is
|
|
|
|
* sigev_notify is SIGEV_SIGNAL. SIGEV_SIGNAL is currently not supported. */
|
|
|
|
if( ( evp == NULL ) || ( evp->sigev_notify == SIGEV_SIGNAL ) )
|
|
|
|
{
|
|
|
|
errno = ENOTSUP;
|
|
|
|
iStatus = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate memory for a new timer object. */
|
|
|
|
if( iStatus == 0 )
|
|
|
|
{
|
|
|
|
pxTimer = pvPortMalloc( sizeof( timer_internal_t ) );
|
|
|
|
|
|
|
|
if( pxTimer == NULL )
|
|
|
|
{
|
|
|
|
errno = EAGAIN;
|
|
|
|
iStatus = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( iStatus == 0 )
|
|
|
|
{
|
|
|
|
/* Copy the event notification structure and set the current timer period. */
|
|
|
|
pxTimer->xTimerEvent = *evp;
|
|
|
|
pxTimer->xTimerPeriod = 0;
|
|
|
|
|
|
|
|
/* Create a new FreeRTOS timer. This call will not fail because the
|
|
|
|
* memory for it has already been allocated, so the output parameter is
|
|
|
|
* also set. */
|
|
|
|
*timerid = ( timer_t ) xTimerCreateStatic( posixconfigTIMER_NAME, /* Timer name. */
|
|
|
|
portMAX_DELAY, /* Initial timer period. Timers are created disarmed. */
|
|
|
|
pdFALSE, /* Don't auto-reload timer. */
|
|
|
|
( void * ) pxTimer, /* Timer id. */
|
|
|
|
prvTimerCallback, /* Timer expiration callback. */
|
|
|
|
&pxTimer->xTimerBuffer ); /* Pre-allocated memory for timer. */
|
|
|
|
}
|
|
|
|
|
|
|
|
return iStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
|
|
|
|
int timer_delete( timer_t timerid )
|
|
|
|
{
|
|
|
|
TimerHandle_t xTimerHandle = timerid;
|
|
|
|
timer_internal_t * pxTimer = ( timer_internal_t * ) pvTimerGetTimerID( xTimerHandle );
|
|
|
|
|
|
|
|
/* The value of the timer ID, set in timer_create, should not be NULL. */
|
|
|
|
configASSERT( pxTimer != NULL );
|
|
|
|
|
|
|
|
/* Stop the FreeRTOS timer. Because the timer is statically allocated, no call
|
|
|
|
* to xTimerDelete is necessary. The timer is stopped so that it's not referenced
|
|
|
|
* anywhere. xTimerStop will not fail when it has unlimited block time. */
|
|
|
|
( void ) xTimerStop( xTimerHandle, portMAX_DELAY );
|
|
|
|
|
|
|
|
/* Wait until the timer stop command is processed. */
|
|
|
|
while( xTimerIsTimerActive( xTimerHandle ) == pdTRUE )
|
|
|
|
{
|
|
|
|
vTaskDelay( 1 );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free the memory in use by the timer. */
|
|
|
|
vPortFree( pxTimer );
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
|
|
|
|
int timer_getoverrun( timer_t timerid )
|
|
|
|
{
|
2019-11-21 03:38:08 +08:00
|
|
|
/* Silence warnings about unused parameters. */
|
|
|
|
( void ) timerid;
|
|
|
|
|
2019-11-21 03:24:35 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
|
|
|
|
int timer_settime( timer_t timerid,
|
|
|
|
int flags,
|
|
|
|
const struct itimerspec * value,
|
|
|
|
struct itimerspec * ovalue )
|
|
|
|
{
|
|
|
|
int iStatus = 0;
|
|
|
|
TimerHandle_t xTimerHandle = timerid;
|
|
|
|
timer_internal_t * pxTimer = ( timer_internal_t * ) pvTimerGetTimerID( xTimerHandle );
|
|
|
|
TickType_t xNextTimerExpiration = 0, xTimerExpirationPeriod = 0;
|
2022-02-09 06:57:54 +08:00
|
|
|
BaseType_t xTimerCommandSent = pdFAIL;
|
2019-11-21 03:24:35 +08:00
|
|
|
|
|
|
|
/* Validate the value argument, but only if the timer isn't being disarmed. */
|
|
|
|
if( TIMESPEC_IS_NOT_ZERO( value->it_value ) )
|
|
|
|
{
|
|
|
|
if( ( UTILS_ValidateTimespec( &value->it_interval ) == false ) ||
|
|
|
|
( UTILS_ValidateTimespec( &value->it_value ) == false ) )
|
|
|
|
{
|
|
|
|
errno = EINVAL;
|
|
|
|
iStatus = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set ovalue, if given. */
|
|
|
|
if( ovalue != NULL )
|
|
|
|
{
|
|
|
|
( void ) timer_gettime( timerid, ovalue );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stop the timer if it's currently active. */
|
|
|
|
if( ( iStatus == 0 ) && xTimerIsTimerActive( xTimerHandle ) )
|
|
|
|
{
|
|
|
|
( void ) xTimerStop( xTimerHandle, portMAX_DELAY );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Only restart the timer if it_value is not zero. */
|
|
|
|
if( ( iStatus == 0 ) && TIMESPEC_IS_NOT_ZERO( value->it_value ) )
|
|
|
|
{
|
|
|
|
/* Convert it_interval to ticks, but only if it_interval is not 0. If
|
|
|
|
* it_interval is 0, then the timer is not periodic. */
|
|
|
|
if( TIMESPEC_IS_NOT_ZERO( value->it_interval ) )
|
|
|
|
{
|
|
|
|
( void ) UTILS_TimespecToTicks( &value->it_interval, &xTimerExpirationPeriod );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the new timer period. A non-periodic timer will have its period set
|
|
|
|
* to portMAX_DELAY. */
|
|
|
|
pxTimer->xTimerPeriod = xTimerExpirationPeriod;
|
|
|
|
|
|
|
|
/* Convert it_value to ticks, but only if it_value is not 0. If it_value
|
|
|
|
* is 0, then the timer will remain disarmed. */
|
|
|
|
if( TIMESPEC_IS_NOT_ZERO( value->it_value ) )
|
|
|
|
{
|
|
|
|
/* Absolute timeout. */
|
|
|
|
if( ( flags & TIMER_ABSTIME ) == TIMER_ABSTIME )
|
|
|
|
{
|
2019-11-21 03:38:08 +08:00
|
|
|
struct timespec xCurrentTime = { 0 };
|
|
|
|
|
|
|
|
/* Get current time */
|
|
|
|
if( clock_gettime( CLOCK_REALTIME, &xCurrentTime ) != 0 )
|
|
|
|
{
|
|
|
|
iStatus = EINVAL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
iStatus = UTILS_AbsoluteTimespecToDeltaTicks( &value->it_value, &xCurrentTime, &xNextTimerExpiration );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make sure xNextTimerExpiration is zero in case we got negative time difference */
|
|
|
|
if( iStatus != 0 )
|
|
|
|
{
|
|
|
|
xNextTimerExpiration = 0;
|
|
|
|
|
|
|
|
if( iStatus == ETIMEDOUT )
|
|
|
|
{
|
|
|
|
/* Set Status to 0 as absolute time is past is treated as expiry but not an error */
|
|
|
|
iStatus = 0;
|
|
|
|
}
|
|
|
|
}
|
2019-11-21 03:24:35 +08:00
|
|
|
}
|
|
|
|
/* Relative timeout. */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
( void ) UTILS_TimespecToTicks( &value->it_value, &xNextTimerExpiration );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If xNextTimerExpiration is still 0, that means that it_value specified
|
|
|
|
* an absolute timeout in the past. Per POSIX spec, a notification should be
|
|
|
|
* triggered immediately. */
|
|
|
|
if( xNextTimerExpiration == 0 )
|
|
|
|
{
|
|
|
|
prvTimerCallback( xTimerHandle );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Set the timer to expire at the it_value, then start it. */
|
|
|
|
( void ) xTimerChangePeriod( xTimerHandle, xNextTimerExpiration, portMAX_DELAY );
|
2022-02-09 06:57:54 +08:00
|
|
|
xTimerCommandSent = xTimerStart( xTimerHandle, xNextTimerExpiration );
|
|
|
|
|
|
|
|
/* Wait until the timer start command is processed. */
|
|
|
|
while( ( xTimerCommandSent != pdFAIL ) && ( xTimerIsTimerActive( xTimerHandle ) == pdFALSE ) )
|
|
|
|
{
|
|
|
|
vTaskDelay( 1 );
|
|
|
|
}
|
2019-11-21 03:24:35 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return iStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
|
|
|
|
int timer_gettime( timer_t timerid,
|
|
|
|
struct itimerspec * value )
|
|
|
|
{
|
|
|
|
TimerHandle_t xTimerHandle = timerid;
|
|
|
|
timer_internal_t * pxTimer = ( timer_internal_t * ) pvTimerGetTimerID( xTimerHandle );
|
|
|
|
TickType_t xNextExpirationTime = xTimerGetExpiryTime( xTimerHandle ) - xTaskGetTickCount(),
|
|
|
|
xTimerExpirationPeriod = pxTimer->xTimerPeriod;
|
|
|
|
|
|
|
|
/* Set it_value only if the timer is armed. Otherwise, set it to 0. */
|
|
|
|
if( xTimerIsTimerActive( xTimerHandle ) != pdFALSE )
|
|
|
|
{
|
|
|
|
value->it_value.tv_sec = ( time_t ) ( xNextExpirationTime / configTICK_RATE_HZ );
|
|
|
|
value->it_value.tv_nsec = ( long ) ( ( xNextExpirationTime % configTICK_RATE_HZ ) * NANOSECONDS_PER_TICK );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
value->it_value.tv_sec = 0;
|
|
|
|
value->it_value.tv_nsec = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set it_interval only if the timer is periodic. Otherwise, set it to 0. */
|
|
|
|
if( xTimerExpirationPeriod != portMAX_DELAY )
|
|
|
|
{
|
|
|
|
value->it_interval.tv_sec = ( time_t ) ( xTimerExpirationPeriod / configTICK_RATE_HZ );
|
|
|
|
value->it_interval.tv_nsec = ( long ) ( ( xTimerExpirationPeriod % configTICK_RATE_HZ ) * NANOSECONDS_PER_TICK );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
value->it_interval.tv_sec = 0;
|
|
|
|
value->it_interval.tv_nsec = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------*/
|