/*
* ptimer.c,v 1.1 1996/06/03 16:29:58 joel Exp
*/
#include <assert.h>
#include <time.h>
#include <errno.h>
#include <rtems/system.h>
#include <rtems/score/isr.h>
#include <rtems/score/thread.h>
#include <rtems/score/tod.h>
#include <rtems/posix/time.h>
/************************************/
/* These includes are now necessary */
/************************************/
#include <sys/features.h>
#include <rtems/rtems/status.h>
#include <rtems/rtems/types.h>
#include <rtems/rtems/timer.h>
#include <rtems/rtems/clock.h>
#include <rtems/posix/psignal.h>
#include <rtems/score/wkspace.h>
#include <pthread.h>
#include <stdio.h>
#include <signal.h>
/*****************************/
/* End of necessary includes */
/*****************************/
/* ************
* Constants
* ************/
#define STATE_FREE_C 0x01 /* Free position of the table of timers */
#define STATE_CREATE_NEW_C 0x02 /* Created timer but not running */
#define STATE_CREATE_RUN_C 0x03 /* Created timer and running */
#define STATE_CREATE_STOP_C 0x04 /* Created, ran and stopped timer */
#define MAX_NSEC_C 1000000000 /* Maximum number of nsec allowed */
#define MIN_NSEC_C 0 /* Minimum number of nsec allowew */
#define TIMER_RELATIVE_C 0 /* Indicates that the fire time is
* relative to the current one */
#define SEC_TO_TICKS_C _TOD_Ticks_per_second /* Number of ticks in a second*/
#define NSEC_PER_SEC_C 1000000000 /* Nanoseconds in a second */
#define NO_MORE_TIMERS_C 11 /* There is not available timers */
#define BAD_TIMER_C 11 /* The timer does not exist in the table */
#define SECONDS_PER_YEAR_C ( 360 * 24 * 60 * 60 )
#define SECONDS_PER_MONTH_C ( 30 * 24 * 60 * 60 )
#define SECONDS_PER_DAY_C ( 24 * 60 * 60 )
#define SECONDS_PER_HOUR_C ( 60 * 60 )
#define SECONDS_PER_MINUTE_C ( 60 )
/*
#define DEBUG_MESSAGES
*/
/* *********************************************************
* Types that will store the created timers and their data
* *********************************************************/
/*
* Data for a timer
*/
typedef struct {
pthread_t thread_id; /* Thread identifier */
char state; /* State of the timer */
struct sigevent inf; /* Information associated to the timer */
timer_t timer_id; /* Created timer identifier */
struct itimerspec timer_data; /* Timing data of the timer */
unsigned32 ticks; /* Number of ticks of the initialization */
unsigned32 overrun; /* Number of expirations of the timer */
rtems_time_of_day time; /* Time in which the timer was started */
} timer_alive_t;
/*
* Array of Timers
*/
int timer_max;
timer_alive_t *timer_struct;
/*
* Data for the signals
*/
struct sigaction signal_inf[SIGRTMAX];
/***********************************
* Definition of Internal Functions
***********************************/
/* ***************************************************************************
* PRINT_MSG_S
*
* Description: This function write a message in the display.
* It is used for debugging and all the calls must be deleted
* when the tests finish
* ***************************************************************************/
static void PRINT_MSG_S ( char *msg )
{
#ifdef DEBUG_MESSAGES
printf("%s\n", msg);
#endif
}
/* ***************************************************************************
* PRINT_ERRNO_S
*
* Description: Print the value of the global variable errno in the display
* ***************************************************************************/
static void PRINT_ERRNO_S ()
{
#ifdef DEBUG_MESSAGES
switch (errno)
{
case EINVAL:
PRINT_MSG_S ( "errno EINVAL"); break;
case EPERM:
PRINT_MSG_S ( "errno EPERM"); break;
case ESRCH:
PRINT_MSG_S ( "errno ESRCH"); break;
case EAGAIN:
PRINT_MSG_S ( "errno EAGAIN"); break;
default :
printf ("errno: %d\n", errno);
break;
}
#endif
}
/* ***************************************************************************
* TIMER_INITIALIZE_S
*
* Description: Initialize the data of a timer
* ***************************************************************************/
void TIMER_INITIALIZE_S ( int timer_pos )
{
/*
* Indicates that the position in the table is free
*/
timer_struct[timer_pos].state = STATE_FREE_C;
/*
* The initial data of timing are set with null value
*/
timer_struct[timer_pos].timer_data.it_value.tv_sec = 0;
timer_struct[timer_pos].timer_data.it_value.tv_nsec = 0;
timer_struct[timer_pos].timer_data.it_interval.tv_sec = 0;
timer_struct[timer_pos].timer_data.it_interval.tv_nsec = 0;
/*
* The count of expirations is 0
*/
timer_struct[timer_pos].overrun = 0;
}
/* ***************************************************************************
* _POSIX_Timer_Manager_initialization
*
* Description: Initialize the internal structure in which the data of all
* the timers are stored
* ***************************************************************************/
void _POSIX_Timer_Manager_initialization ( int max_timers )
{
int index;
timer_struct = _Workspace_Allocate_or_fatal_error(
max_timers * sizeof(timer_alive_t) );
/*
* Initialize all the timers
*/
timer_max = max_timers;
for (index=0; index<max_timers; index++)
TIMER_INITIALIZE_S( index );
timer_max = max_timers;
}
/* ***************************************************************************
* FIRST_FREE_POSITION_F
*
* Description: Returns the first free position in the table of timers.
* If there is not a free position, it returns NO_MORE_TIMERS_C
* ***************************************************************************/
int FIRST_FREE_POSITION_F ()
{
int index;
for (index=0; index<timer_max; index++) {
if ( timer_struct[index].state == STATE_FREE_C ) {
return index;
}
}
/* The function reaches this point only if all the position are occupied */
return NO_MORE_TIMERS_C;
}
/* ***************************************************************************
* TIMER_POSITION_F
*
* Description: Returns the position in the table of timers in which the
* data of the timer are stored.
* If the timer identifier does not exist, it returns
* BAD_TIMER_C
* ***************************************************************************/
int TIMER_POSITION_F ( timer_t timer_id )
{
int index;
for (index=0; index<timer_max; index++ ) {
/* Looks for the position of the timer. The timer must exist and the
* position can not be free */
if ( ( timer_struct[index].timer_id == timer_id ) &&
( timer_struct[index].state != STATE_FREE_C ) ) {
return index;
}
}
/* If the function reaches this point is because the timer identifier
* is not correct */
return BAD_TIMER_C;
}
/* ***************************************************************************
* COPY_ITIMERSPEC_S
*
* Description: Does a copy of a variable of type struct itimerspec
* ***************************************************************************/
void COPY_ITIMERSPEC_S ( const struct itimerspec *source,
struct itimerspec *target )
{
target->it_value.tv_sec = source->it_value.tv_sec;
target->it_value.tv_nsec = source->it_value.tv_nsec;
target->it_interval.tv_sec = source->it_interval.tv_sec;
target->it_interval.tv_nsec = source->it_interval.tv_nsec;
}
/* ***************************************************************************
* ITIMERSPEC_TO_RTEMS_TIME_OF_DAY_S
*
* Description: This function converts the data of a structure itimerspec
* into structure rtems_time_of_day
* ***************************************************************************/
void ITIMERSPEC_TO_RTEMS_TIME_OF_DAY_S
( const struct itimerspec *itimer, rtems_time_of_day *rtems_time )
{
unsigned long int seconds;
/* The leap years and the months with 28, 29 or 31 days have not been
* considerated. It will be made in the future */
seconds = itimer->it_value.tv_sec;
rtems_time->year = seconds / SECONDS_PER_YEAR_C;
seconds = seconds % SECONDS_PER_YEAR_C;
rtems_time->month = seconds / SECONDS_PER_MONTH_C;
seconds = seconds % SECONDS_PER_MONTH_C;
rtems_time->day = seconds / SECONDS_PER_DAY_C;
seconds = seconds % SECONDS_PER_DAY_C;
rtems_time->hour = seconds / SECONDS_PER_HOUR_C;
seconds = seconds % SECONDS_PER_HOUR_C;
rtems_time->minute = seconds / SECONDS_PER_MINUTE_C;
seconds = seconds % SECONDS_PER_MINUTE_C;
rtems_time->second = seconds;
rtems_time->ticks = ( itimer->it_value.tv_nsec * SEC_TO_TICKS_C ) /
NSEC_PER_SEC_C;
}
/* ***************************************************************************
* FIRE_TIMER_S
*
* Description: This is the operation that is ran when a timer expires
* ***************************************************************************/
rtems_timer_service_routine FIRE_TIMER_S (rtems_id timer, void *data)
{
int timer_pos; /* Position in the table of the timer that
* has expirated */
rtems_status_code return_v; /* Return value of rtems_timer_fire_after */
int sig_number; /* Number of the signal to send */
/* The position of the table of timers that contains the data of the
* expired timer will be stored in "timer_pos". In theory a timer can not
* expire if it has not been created or has been deleted */
PRINT_MSG_S ("FIRE_TIMER_S");
timer_pos = TIMER_POSITION_F(timer);
/* Increases the number of expiration of the timer in one unit. */
timer_struct[timer_pos].overrun = timer_struct[timer_pos].overrun + 1;
if ( ( timer_struct[timer_pos].timer_data.it_interval.tv_sec != 0 ) ||
( timer_struct[timer_pos].timer_data.it_interval.tv_nsec != 0 ) ) {
/* The timer must be reprogrammed */
return_v = rtems_timer_fire_after ( timer,
timer_struct[timer_pos].ticks,
FIRE_TIMER_S,
NULL );
/* Stores the time when the timer was started again */
return_v = rtems_clock_get ( RTEMS_CLOCK_GET_TOD,
&timer_struct[timer_pos].time );
/* The state has not to be actualized, because nothing modifies it */
timer_struct[timer_pos].state = STATE_CREATE_RUN_C;
} else {
/* Indicates that the timer is stopped */
timer_struct[timer_pos].state = STATE_CREATE_STOP_C;
}
/*
* The sending of the signal to the process running the handling function
* specified for that signal is simulated
*/
sig_number = timer_struct[timer_pos].inf.sigev_signo;
if( pthread_kill ( timer_struct[timer_pos].thread_id ,
timer_struct[timer_pos].inf.sigev_signo ) ) {
PRINT_MSG_S ("ERROR_PTHREAD_KILL");
} else {
PRINT_MSG_S ("SUCCESS_PTHREAD_KILL");
}
/*
* After the signal handler returns, the count of expirations of the
* timer must be set to 0.
*/
timer_struct[timer_pos].overrun = 0;
}
/* *********************************************************************
* 14.2.2 Create a Per-Process Timer, P1003.1b-1993, p. 264
* ********************************************************************/
/* **************
* timer_create
* **************/
int timer_create(
clockid_t clock_id,
struct sigevent *evp,
timer_t *timerid
)
{
rtems_status_code return_v; /* return value of the operation */
rtems_id timer_id; /* created timer identifier */
int timer_pos; /* Position in the table of timers */
/*
* The data of the structure evp are checked in order to verify if they
* are coherent.
*/
if (evp != NULL) {
/* The structure has data */
if ( ( evp->sigev_notify != SIGEV_NONE ) &&
( evp->sigev_notify != SIGEV_SIGNAL ) ) {
/* The value of the field sigev_notify is not valid */
return (-1);
}
}
/*
* A timer is created using the primitive rtems_timer_create
*/
return_v = rtems_timer_create ( clock_id, &timer_id );
switch (return_v) {
case RTEMS_SUCCESSFUL :
PRINT_MSG_S("SUCCESS: rtems create timer RTEMS_SUCCESSFUL");
/*
* The timer has been created properly
*/
/* Obtains the first free position in the table of timers */
timer_pos = FIRST_FREE_POSITION_F();
if ( timer_pos == NO_MORE_TIMERS_C ) {
/* There is not position for another timers in spite of RTEMS
* supports it. It will necessaty to increase the structure used */
errno = EAGAIN;
return -1;
}
/* Exit parameter */
*timerid = timer_id;
/* The data of the created timer are stored to use them later */
timer_struct[timer_pos].state = STATE_CREATE_NEW_C;
/* NEW VERSION*/
timer_struct[timer_pos].thread_id = pthread_self ();
if ( evp != NULL ) {
timer_struct[timer_pos].inf.sigev_notify = evp->sigev_notify;
timer_struct[timer_pos].inf.sigev_signo = evp->sigev_signo;
timer_struct[timer_pos].inf.sigev_value = evp->sigev_value;
}
timer_struct[timer_pos].timer_id = timer_id;
timer_struct[timer_pos].overrun = 0;
timer_struct[timer_pos].timer_data.it_value.tv_sec = 0;
timer_struct[timer_pos].timer_data.it_value.tv_nsec = 0;
timer_struct[timer_pos].timer_data.it_interval.tv_sec = 0;
timer_struct[timer_pos].timer_data.it_interval.tv_nsec = 0;
return 0;
case RTEMS_INVALID_NAME : /* The assigned name is not valid*/
PRINT_MSG_S ("ERROR: rtems create timer RTEMS_INVALID_NAME");
errno = EINVAL;
return (-1);
case RTEMS_TOO_MANY :
PRINT_MSG_S ("ERROR: rtems create timer RTEMS_TOO_MANY ");
/* There has been created too much timers for the same process */
errno = EAGAIN;
return (-1);
default :
/*
* Does nothing. It only returns the error without assigning a value
* to errno. In theory, it can not happen because the call to
* rtems_timer_create can not return other different value.
*/
return (-1);
}
/*
* The next sentence is used to avoid singular situations
*/
return (-1);
}
/*
* 14.2.3 Delete a Per_process Timer, P1003.1b-1993, p. 266
*/
int timer_delete(
timer_t timerid
)
{
/*
* IDEA: This function must probably stop the timer first and then delete it
*
* It will have to do a call to rtems_timer_cancel and then another
* call to rtems_timer_delete.
* The call to rtems_timer_delete will be probably unnecessary,
* because rtems_timer_delete stops the timer before deleting it.
*/
int timer_pos;
rtems_status_code status;
/* First the position in the table of timers is obtained */
timer_pos = TIMER_POSITION_F ( timerid );
if ( timer_pos == BAD_TIMER_C ) {
/* The timer identifier is erroneus */
errno = EINVAL;
return -1;
}
/* The timer is deleted */
status = rtems_timer_delete ( timerid );
if ( status == RTEMS_INVALID_ID ) {
/* The timer identifier is erroneus */
errno = EINVAL;
return -1;
}
/* Initializes the data of the timer */
TIMER_INITIALIZE_S ( timer_pos );
return 0;
}
/*
* 14.2.4 Per-Process Timers, P1003.1b-1993, p. 267
*/
/* **************
* timer_settime
* **************/
int timer_settime(
timer_t timerid,
int flags,
const struct itimerspec *value,
struct itimerspec *ovalue
)
{
rtems_status_code return_v; /* Return of the calls to RTEMS */
int timer_pos; /* Position of the timer in the table */
rtems_time_of_day rtems_time; /* Time in RTEMS */
/* First the position in the table of timers is obtained */
timer_pos = TIMER_POSITION_F ( timerid );
if ( timer_pos == BAD_TIMER_C ) {
/* The timer identifier is erroneus */
errno = EINVAL;
return -1;
}
if ( value == NULL ) {
/* The stucture of times of the timer is free, and then returns an
error but the variable errno is not actualized */
/* errno = ?????? */
return -1;
}
/* If the function reaches this point, then it will be necessary to do
* something with the structure of times of the timer: to stop, start
* or start it again */
/* First, it verifies if the timer must be stopped */
if ( value->it_value.tv_sec == 0 && value->it_value.tv_nsec == 0 ) {
/* The timer is stopped */
return_v = rtems_timer_cancel ( timerid );
/* The old data of the timer are returned */
COPY_ITIMERSPEC_S ( &timer_struct[timer_pos].timer_data, ovalue );
/* The new data are set */
COPY_ITIMERSPEC_S ( value, &timer_struct[timer_pos].timer_data );
/* Indicates that the timer is created and stopped */
timer_struct[timer_pos].state = STATE_CREATE_STOP_C;
/* Returns with success */
return 0;
}
/*
* If the function reaches this point, then the timer will have to be
* initialized with new values: to start it or start it again
*/
/* First, it verifies if the structure "value" is correct */
if ( ( value->it_value.tv_nsec > MAX_NSEC_C ) ||
( value->it_value.tv_nsec < MIN_NSEC_C ) ) {
/* The number of nanoseconds is not correct */
errno = EINVAL;
return -1;
}
/* Then, "value" must be converted from seconds and nanoseconds to clock
* ticks, to use it in the calls to RTEMS */
/* It is also necessary to take in account if the time is absolute
* or relative */
switch (flags) {
case TIMER_ABSTIME:
/* The fire time is absolute:
* It has to use "rtems_time_fire_when" */
/* First, it converts from struct itimerspec to rtems_time_of_day */
ITIMERSPEC_TO_RTEMS_TIME_OF_DAY_S ( value, &rtems_time );
return_v = rtems_timer_fire_when ( timerid, &rtems_time, FIRE_TIMER_S, NULL);
switch ( return_v ) {
case RTEMS_SUCCESSFUL:
PRINT_MSG_S ("SUCCESS: timer_settime RTEMS_SUCCESSFUL");
/* The timer has been started and is running */
/* Actualizes the data of the structure and
* returns the old ones in "ovalue" */
COPY_ITIMERSPEC_S ( &timer_struct[timer_pos].timer_data, ovalue );
COPY_ITIMERSPEC_S ( value, &timer_struct[timer_pos].timer_data );
/* It indicates that the time is running */
timer_struct[timer_pos].state = STATE_CREATE_RUN_C;
/* Stores the time in which the timer was started again */
return_v = rtems_clock_get ( RTEMS_CLOCK_GET_TOD,
&timer_struct[timer_pos].time );
return 0;
break;
case RTEMS_INVALID_ID:
PRINT_MSG_S ("ERROR: timer_settime RTEMS_INVALID_ID");
break;
case RTEMS_NOT_DEFINED:
PRINT_MSG_S ("ERROR: timer_settime RTEMS_NOT_DEFINED");
break;
case RTEMS_INVALID_CLOCK:
PRINT_MSG_S ("ERROR: timer_settime RTEMS_INVALID_CLOCK");
break;
default:
}
break;
case TIMER_RELATIVE_C:
/* The fire time is relative:
* It has to use "rtems_time_fire_after" */
/* First, it converts from seconds and nanoseconds to ticks */
/* The form in which this operation is done can produce a lost
* of precision of 1 second */
/* This is the process to convert from nanoseconds to ticks
*
* There is a tick every 10 miliseconds, then the nanoseconds are
* divided between 10**7. The result of this operation will be the
* number of ticks
*/
timer_struct[timer_pos].ticks =
( SEC_TO_TICKS_C * value->it_value.tv_sec ) +
( value->it_value.tv_nsec / ( 1000 * 1000 * 10 ) );
return_v = rtems_timer_fire_after ( timerid,
timer_struct[timer_pos].ticks,
FIRE_TIMER_S,
NULL );
switch (return_v) {
case RTEMS_SUCCESSFUL:
PRINT_MSG_S ( "SUCCESS: timer_settime RTEMS_SUCCESSFUL");
/* The timer has been started and is running */
/* Actualizes the data of the structure and
* returns the old ones in "ovalue" */
COPY_ITIMERSPEC_S ( &timer_struct[timer_pos].timer_data, ovalue );
COPY_ITIMERSPEC_S ( value, &timer_struct[timer_pos].timer_data );
/* It indicates that the time is running */
timer_struct[timer_pos].state = STATE_CREATE_RUN_C;
/* Stores the time in which the timer was started again */
return_v = rtems_clock_get ( RTEMS_CLOCK_GET_TOD,
&timer_struct[timer_pos].time );
return 0;
break;
case RTEMS_INVALID_ID:
PRINT_MSG_S ( "ERROR: timer_settime RTEMS_INVALID_ID");
/* The timer identifier is not correct. In theory, this
* situation can not occur, but the solution is easy */
errno = EINVAL;
return -1;
break;
case RTEMS_INVALID_NUMBER:
PRINT_MSG_S ( "ERROR: timer_settime RTEMS_INVALID_NUMBER");
/* In this case, RTEMS fails because the values of timing
* are incorrect */
/*
* I do not know if errno must be actualized
*
* errno = EINVAL;
*/
return -1;
break;
default:
}
break;
default:
/* It does nothing, although it will be probably necessary to
* return an error */
}
/* To avoid problems */
return 0;
}
/*
* 14.2.4 Per-Process Timers, P1003.1b-1993, p. 267
*/
/* **************
* timer_gettime
* **************/
int timer_gettime(
timer_t timerid,
struct itimerspec *value
)
{
/*
* IDEA: This function does not use functions of RTEMS to the handle
* of timers. It uses some functions for managing the time.
*
* A possible form to do this is the following:
*
* - When a timer is initialized, the value of the time in
* that moment is stored.
* - When this function is called, it returns the difference
* between the current time and the initialization time.
*/
rtems_time_of_day current_time;
rtems_status_code return_v;
int timer_pos;
unsigned32 hours;
unsigned32 minutes;
unsigned32 seconds;
unsigned32 ticks;
unsigned32 nanosec;
/* Reads the current time */
return_v = rtems_clock_get ( RTEMS_CLOCK_GET_TOD, ¤t_time );
timer_pos = TIMER_POSITION_F ( timerid );
if ( timer_pos == BAD_TIMER_C ) {
/* The timer identifier is erroneus */
errno = EINVAL;
return (-1);
}
/* Calculates the difference between the start time of the timer and
* the current one */
hours = current_time.hour - timer_struct[timer_pos].time.hour;
if ( current_time.minute < timer_struct[timer_pos].time.minute ) {
minutes = 60 - timer_struct[timer_pos].time.minute + current_time.minute;
hours--;
} else {
minutes = current_time.minute - timer_struct[timer_pos].time.minute;
}
if ( current_time.second < timer_struct[timer_pos].time.second ) {
seconds = 60 - timer_struct[timer_pos].time.second + current_time.second;
minutes--;
} else {
seconds = current_time.second - timer_struct[timer_pos].time.second;
}
if ( current_time.ticks < timer_struct[timer_pos].time.ticks ) {
ticks = 100 - timer_struct[timer_pos].time.ticks + current_time.ticks;
seconds--;
} else {
ticks = current_time.ticks - timer_struct[timer_pos].time.ticks;
}
/* The time that the timer is running is calculated */
seconds = hours * 60 * 60 +
minutes * 60 +
seconds;
nanosec = ticks * 10 * /* msec */
1000 * /* microsec */
1000; /* nanosec */
/* Calculates the time left before the timer finishes */
value->it_value.tv_sec =
timer_struct[timer_pos].timer_data.it_value.tv_sec - seconds;
value->it_value.tv_nsec =
timer_struct[timer_pos].timer_data.it_value.tv_nsec - nanosec;
value->it_interval.tv_sec =
timer_struct[timer_pos].timer_data.it_interval.tv_sec;
value->it_interval.tv_nsec =
timer_struct[timer_pos].timer_data.it_interval.tv_nsec;
return 0;
}
/*
* 14.2.4 Per-Process Timers, P1003.1b-1993, p. 267
*/
/* *****************
* timer_getoverrun
* *****************/
int timer_getoverrun(
timer_t timerid
)
{
/*
* IDEA: This function must count the times the timer expires.
*
* The expiration of a timer must increase by one a counter.
* After the signal handler associated to the timer finishs
* its execution, FIRE_TIMER_S will have to set this counter to 0.
*/
int timer_pos; /* Position of the timer in the structure */
int overrun; /* Overflow count */
timer_pos = TIMER_POSITION_F ( timerid );
if ( timer_pos == BAD_TIMER_C ) {
/* The timer identifier is erroneus */
errno = EINVAL;
return -1;
}
/* The overflow count of the timer is stored in "overrun" */
overrun = timer_struct[timer_pos].overrun;
/* It is set to 0 */
timer_struct[timer_pos].overrun = 0;
return overrun;
}