summaryrefslogtreecommitdiffstats
path: root/cpukit/posix/src/timersettime.c
blob: 1f98a7a08cec7dbdaa967ac500af213663d9af6e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/**
 * @file
 *
 * @brief Function Arms or Disarms the Timer Identified by timerid 
 * @ingroup POSIXAPI
 */

/*
 *  14.2.4 Per-Process Timers, P1003.1b-1993, p. 267
 *
 *  COPYRIGHT (c) 1989-2008.
 *  On-Line Applications Research Corporation (OAR).
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.org/license/LICENSE.
 */

#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <time.h>
#include <errno.h>

#include <rtems/posix/ptimer.h>
#include <rtems/posix/timerimpl.h>
#include <rtems/score/todimpl.h>
#include <rtems/score/watchdogimpl.h>
#include <rtems/seterr.h>

static void _POSIX_Timer_Insert(
  POSIX_Timer_Control *ptimer,
  Per_CPU_Control     *cpu,
  Watchdog_Interval    ticks
)
{
  /* The state really did not change but just to be safe */
  ptimer->state = POSIX_TIMER_STATE_CREATE_RUN;

  /* Store the time when the timer was started again */
  _TOD_Get( &ptimer->time );

  _Watchdog_Insert(
    &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_RELATIVE ],
    &ptimer->Timer,
    cpu->Watchdog.ticks + ticks
  );
}

/*
 *  This is the operation that is run when a timer expires
 */
void _POSIX_Timer_TSR( Watchdog_Control *the_watchdog )
{
  POSIX_Timer_Control *ptimer;
  ISR_lock_Context     lock_context;
  Per_CPU_Control     *cpu;

  ptimer = RTEMS_CONTAINER_OF( the_watchdog, POSIX_Timer_Control, Timer );
  _ISR_lock_ISR_disable( &lock_context );
  cpu = _POSIX_Timer_Acquire_critical( ptimer, &lock_context );

  /* Increment the number of expirations. */
  ptimer->overrun = ptimer->overrun + 1;

  /* The timer must be reprogrammed */
  if ( ( ptimer->timer_data.it_interval.tv_sec  != 0 ) ||
       ( ptimer->timer_data.it_interval.tv_nsec != 0 ) ) {
    _POSIX_Timer_Insert( ptimer, cpu, ptimer->ticks );
  } else {
   /* Indicates that the timer is stopped */
   ptimer->state = POSIX_TIMER_STATE_CREATE_STOP;
  }

  _POSIX_Timer_Release( cpu, &lock_context );

  /*
   * The sending of the signal to the process running the handling function
   * specified for that signal is simulated
   */

  if ( pthread_kill ( ptimer->thread_id, ptimer->inf.sigev_signo ) ) {
    _Assert( FALSE );
    /*
     * TODO: What if an error happens at run-time? This should never
     *       occur because the timer should be canceled if the thread
     *       is deleted. This method is being invoked from the Clock
     *       Tick ISR so even if we decide to take action on an error,
     *       we don't have many options. We shouldn't shut the system down.
     */
  }

  /* After the signal handler returns, the count of expirations of the
   * timer must be set to 0.
   */
  ptimer->overrun = 0;
}

int timer_settime(
  timer_t                  timerid,
  int                      flags,
  const struct itimerspec *__restrict value,
  struct itimerspec       *__restrict ovalue
)
{
  POSIX_Timer_Control *ptimer;
  ISR_lock_Context     lock_context;
  uint32_t             initial_period;
  struct itimerspec    normalize;

  if ( !value )
    rtems_set_errno_and_return_minus_one( EINVAL );

  /* 
   * First, it verifies if the structure "value" is correct   
   * if the number of nanoseconds is not correct return EINVAL
   */
  if ( !_Timespec_Is_valid( &(value->it_value) ) ) {
    rtems_set_errno_and_return_minus_one( EINVAL );
  }
  if ( !_Timespec_Is_valid( &(value->it_interval) ) ) {
    rtems_set_errno_and_return_minus_one( EINVAL );
  }

  if ( flags != TIMER_ABSTIME && flags != POSIX_TIMER_RELATIVE ) {
    rtems_set_errno_and_return_minus_one( EINVAL );
  }

  normalize = *value;

  /* Convert absolute to relative time */
  if (flags == TIMER_ABSTIME) {
    struct timespec now;
    _TOD_Get( &now );
    /* Check for seconds in the past */
    if ( _Timespec_Greater_than( &now, &normalize.it_value ) )
      rtems_set_errno_and_return_minus_one( EINVAL );
    _Timespec_Subtract( &now, &normalize.it_value, &normalize.it_value );
  }

  /* 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
   */

  ptimer = _POSIX_Timer_Get( timerid, &lock_context );
  if ( ptimer != NULL ) {
    Per_CPU_Control *cpu;

    cpu = _POSIX_Timer_Acquire_critical( ptimer, &lock_context );

    /* Stop the timer */
    _Watchdog_Remove(
      &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_RELATIVE ],
      &ptimer->Timer
    );

    /* First, it verifies if the timer must be stopped */
    if ( normalize.it_value.tv_sec == 0 && normalize.it_value.tv_nsec == 0 ) {
      /* The old data of the timer are returned */
      if ( ovalue )
        *ovalue = ptimer->timer_data;
      /* The new data are set */
      ptimer->timer_data = normalize;
      /* Indicates that the timer is created and stopped */
      ptimer->state = POSIX_TIMER_STATE_CREATE_STOP;
      /* Returns with success */
      _POSIX_Timer_Release( cpu, &lock_context );
      return 0;
    }

    /* Convert from seconds and nanoseconds to ticks */
    ptimer->ticks  = _Timespec_To_ticks( &value->it_interval );
    initial_period = _Timespec_To_ticks( &normalize.it_value );

    _POSIX_Timer_Insert( ptimer, cpu, initial_period );

    /*
     * The timer has been started and is running.  So we return the
     * old ones in "ovalue"
     */
    if ( ovalue )
      *ovalue = ptimer->timer_data;
    ptimer->timer_data = normalize;
    _POSIX_Timer_Release( cpu, &lock_context );
    return 0;
  }

  rtems_set_errno_and_return_minus_one( EINVAL );
}