rtems_status_code rtems_timer_server_fire_when( rtems_id id, rtems_time_of_day *wall_time, rtems_timer_service_routine_entry routine, void *user_data ) { Timer_Control *the_timer; Objects_Locations location; rtems_interval seconds; Timer_server_Control *timer_server = _Timer_server; if ( !timer_server ) return RTEMS_INCORRECT_STATE; if ( !_TOD.is_set ) return RTEMS_NOT_DEFINED; if ( !routine ) return RTEMS_INVALID_ADDRESS; if ( !_TOD_Validate( wall_time ) ) return RTEMS_INVALID_CLOCK; seconds = _TOD_To_seconds( wall_time ); if ( seconds <= _TOD_Seconds_since_epoch() ) return RTEMS_INVALID_CLOCK; the_timer = _Timer_Get( id, &location ); switch ( location ) { case OBJECTS_LOCAL: (void) _Watchdog_Remove( &the_timer->Ticker ); the_timer->the_class = TIMER_TIME_OF_DAY_ON_TASK; _Watchdog_Initialize( &the_timer->Ticker, routine, id, user_data ); the_timer->Ticker.initial = seconds - _TOD_Seconds_since_epoch(); (*timer_server->schedule_operation)( timer_server, the_timer ); _Thread_Enable_dispatch(); return RTEMS_SUCCESSFUL; #if defined(RTEMS_MULTIPROCESSING) case OBJECTS_REMOTE: /* should never return this */ #endif case OBJECTS_ERROR: break; } return RTEMS_INVALID_ID; }
void _TOD_Set_with_timestamp( const Timestamp_Control *tod_as_timestamp ) { TOD_Control *tod = &_TOD; uint32_t nanoseconds = _Timestamp_Get_nanoseconds( tod_as_timestamp ); Watchdog_Interval seconds_next = _Timestamp_Get_seconds( tod_as_timestamp ); Watchdog_Interval seconds_now; ISR_lock_Context lock_context; _Thread_Disable_dispatch(); seconds_now = _TOD_Seconds_since_epoch(); if ( seconds_next < seconds_now ) _Watchdog_Adjust_seconds( WATCHDOG_BACKWARD, seconds_now - seconds_next ); else _Watchdog_Adjust_seconds( WATCHDOG_FORWARD, seconds_next - seconds_now ); _TOD_Acquire( tod, &lock_context ); tod->now = *tod_as_timestamp; _TOD_Release( tod, &lock_context ); tod->seconds_trigger = nanoseconds; tod->is_set = true; _Thread_Enable_dispatch(); }
rtems_status_code _Timer_Fire_when( rtems_id id, const rtems_time_of_day *wall_time, rtems_timer_service_routine_entry routine, void *user_data, Timer_Classes the_class, Watchdog_Service_routine_entry adaptor ) { rtems_interval seconds; if ( !_TOD_Is_set() ) return RTEMS_NOT_DEFINED; if ( !routine ) return RTEMS_INVALID_ADDRESS; if ( !_TOD_Validate( wall_time ) ) return RTEMS_INVALID_CLOCK; seconds = _TOD_To_seconds( wall_time ); if ( seconds <= _TOD_Seconds_since_epoch() ) return RTEMS_INVALID_CLOCK; return _Timer_Fire( id, seconds, routine, user_data, the_class, adaptor ); }
static void _Timer_server_Process_tod_watchdogs( Timer_server_Watchdogs *watchdogs, Chain_Control *fire_chain ) { Watchdog_Interval snapshot = (Watchdog_Interval) _TOD_Seconds_since_epoch(); Watchdog_Interval last_snapshot = watchdogs->last_snapshot; Watchdog_Interval delta; /* * Process the seconds chain. Start by checking that the Time * of Day (TOD) has not been set backwards. If it has then * we want to adjust the watchdogs->Chain to indicate this. */ if ( snapshot > last_snapshot ) { /* * This path is for normal forward movement and cases where the * TOD has been set forward. */ delta = snapshot - last_snapshot; _Watchdog_Adjust_to_chain( &watchdogs->Chain, delta, fire_chain ); } else if ( snapshot < last_snapshot ) { /* * The current TOD is before the last TOD which indicates that * TOD has been set backwards. */ delta = last_snapshot - snapshot; _Watchdog_Adjust( &watchdogs->Chain, WATCHDOG_BACKWARD, delta ); } watchdogs->last_snapshot = snapshot; }
void _TOD_Set_with_timestamp( const Timestamp_Control *tod ) { uint32_t nanoseconds = _Timestamp_Get_nanoseconds( tod ); Watchdog_Interval seconds_next = _Timestamp_Get_seconds( tod ); Watchdog_Interval seconds_now; _Thread_Disable_dispatch(); _TOD_Deactivate(); seconds_now = _TOD_Seconds_since_epoch(); if ( seconds_next < seconds_now ) _Watchdog_Adjust_seconds( WATCHDOG_BACKWARD, seconds_now - seconds_next ); else _Watchdog_Adjust_seconds( WATCHDOG_FORWARD, seconds_next - seconds_now ); _TOD.now = *tod; _TOD.seconds_trigger = nanoseconds; _TOD.is_set = true; _TOD_Activate(); _Thread_Enable_dispatch(); }
rtems_status_code rtems_clock_get_seconds_since_epoch( rtems_interval *the_interval ) { if ( !the_interval ) return RTEMS_INVALID_ADDRESS; if ( !_TOD_Is_set ) return RTEMS_NOT_DEFINED; *the_interval = _TOD_Seconds_since_epoch(); return RTEMS_SUCCESSFUL; }
rtems_status_code rtems_task_wake_when( rtems_time_of_day *time_buffer ) { Watchdog_Interval seconds; if ( !_TOD_Is_set() ) return RTEMS_NOT_DEFINED; if ( !time_buffer ) return RTEMS_INVALID_ADDRESS; time_buffer->ticks = 0; if ( !_TOD_Validate( time_buffer ) ) return RTEMS_INVALID_CLOCK; seconds = _TOD_To_seconds( time_buffer ); if ( seconds <= _TOD_Seconds_since_epoch() ) return RTEMS_INVALID_CLOCK; _Thread_Disable_dispatch(); _Thread_Set_state( _Thread_Executing, STATES_WAITING_FOR_TIME ); _Watchdog_Initialize( &_Thread_Executing->Timer, _Thread_Delay_ended, _Thread_Executing->Object.id, NULL ); _Watchdog_Insert_seconds( &_Thread_Executing->Timer, seconds - _TOD_Seconds_since_epoch() ); _Thread_Enable_dispatch(); return RTEMS_SUCCESSFUL; }
void _TOD_Set( const struct timespec *time ) { long seconds; _Thread_Disable_dispatch(); _TOD_Deactivate(); seconds = _TOD_Seconds_since_epoch(); if ( time->tv_sec < seconds ) _Watchdog_Adjust_seconds( WATCHDOG_BACKWARD, seconds - time->tv_sec ); else _Watchdog_Adjust_seconds( WATCHDOG_FORWARD, time->tv_sec - seconds ); /* POSIX format TOD (timespec) */ _Timestamp_Set( &_TOD_Now, time->tv_sec, time->tv_nsec ); _TOD_Is_set = true; _TOD_Activate(); _Thread_Enable_dispatch(); }
/** * @brief rtems_timer_initiate_server * * This directive creates and starts the server for task-based timers. * It must be invoked before any task-based timers can be initiated. * * @param[in] priority is the timer server priority * @param[in] stack_size is the stack size in bytes * @param[in] attribute_set is the timer server attributes * * @return This method returns RTEMS_SUCCESSFUL if successful and an * error code otherwise. */ rtems_status_code rtems_timer_initiate_server( uint32_t priority, uint32_t stack_size, rtems_attribute attribute_set ) { rtems_id id; rtems_status_code status; rtems_task_priority _priority; static bool initialized = false; bool tmpInitialized; Timer_server_Control *ts = &_Timer_server_Default; /* * Make sure the requested priority is valid. The if is * structured so we check it is invalid before looking for * a specific invalid value as the default. */ _priority = priority; if ( !_RTEMS_tasks_Priority_is_valid( priority ) ) { if ( priority != RTEMS_TIMER_SERVER_DEFAULT_PRIORITY ) return RTEMS_INVALID_PRIORITY; _priority = 0; } /* * Just to make sure this is only called once. */ _Thread_Disable_dispatch(); tmpInitialized = initialized; initialized = true; _Thread_Enable_dispatch(); if ( tmpInitialized ) return RTEMS_INCORRECT_STATE; /* * Create the Timer Server with the name the name of "TIME". The attribute * RTEMS_SYSTEM_TASK allows us to set a priority to 0 which will makes it * higher than any other task in the system. It can be viewed as a low * priority interrupt. It is also always NO_PREEMPT so it looks like * an interrupt to other tasks. * * We allow the user to override the default priority because the Timer * Server can invoke TSRs which must adhere to language run-time or * other library rules. For example, if using a TSR written in Ada the * Server should run at the same priority as the priority Ada task. * Otherwise, the priority ceiling for the mutex used to protect the * GNAT run-time is violated. */ status = rtems_task_create( _Objects_Build_name('T','I','M','E'), /* "TIME" */ _priority, /* create with priority 1 since 0 is illegal */ stack_size, /* let user specify stack size */ RTEMS_NO_PREEMPT, /* no preempt is like an interrupt */ /* user may want floating point but we need */ /* system task specified for 0 priority */ attribute_set | RTEMS_SYSTEM_TASK, &id /* get the id back */ ); if (status) { initialized = false; return status; } /* * Do all the data structure initialization before starting the * Timer Server so we do not have to have a critical section. */ /* * We work with the TCB pointer, not the ID, so we need to convert * to a TCB pointer from here out. */ ts->thread = (Thread_Control *)_Objects_Get_local_object( &_RTEMS_tasks_Information, _Objects_Get_index(id) ); /* * Initialize the timer lists that the server will manage. */ _Chain_Initialize_empty( &ts->Interval_watchdogs.Chain ); _Chain_Initialize_empty( &ts->TOD_watchdogs.Chain ); /* * Initialize the timers that will be used to control when the * Timer Server wakes up and services the task-based timers. */ _Watchdog_Initialize( &ts->Interval_watchdogs.System_watchdog, _Thread_Delay_ended, id, NULL ); _Watchdog_Initialize( &ts->TOD_watchdogs.System_watchdog, _Thread_Delay_ended, id, NULL ); /* * Initialize the pointer to the timer schedule method so applications that * do not use the Timer Server do not have to pull it in. */ ts->schedule_operation = _Timer_server_Schedule_operation_method; ts->Interval_watchdogs.last_snapshot = _Watchdog_Ticks_since_boot; ts->TOD_watchdogs.last_snapshot = (Watchdog_Interval) _TOD_Seconds_since_epoch(); ts->insert_chain = NULL; ts->active = false; /* * The default timer server is now available. */ _Timer_server = ts; /* * Start the timer server */ status = rtems_task_start( id, _Timer_server_Body, (rtems_task_argument) ts ); #if defined(RTEMS_DEBUG) /* * One would expect a call to rtems_task_delete() here to clean up * but there is actually no way (in normal circumstances) that the * start can fail. The id and starting address are known to be * be good. If this service fails, something is weirdly wrong on the * target such as a stray write in an ISR or incorrect memory layout. */ if (status) { initialized = false; } #endif return status; }
static void _Timer_server_Insert_timer_and_make_snapshot( Timer_server_Control *ts, Timer_Control *timer ) { Watchdog_Control *first_watchdog; Watchdog_Interval delta_interval; Watchdog_Interval last_snapshot; Watchdog_Interval snapshot; Watchdog_Interval delta; ISR_Level level; /* * We have to update the time snapshots here, because otherwise we may have * problems with the integer range of the delta values. The time delta DT * from the last snapshot to now may be arbitrarily long. The last snapshot * is the reference point for the delta chain. Thus if we do not update the * reference point we have to add DT to the initial delta of the watchdog * being inserted. This could result in an integer overflow. */ _Thread_Disable_dispatch(); if ( timer->the_class == TIMER_INTERVAL_ON_TASK ) { /* * We have to advance the last known ticks value of the server and update * the watchdog chain accordingly. */ _ISR_Disable( level ); snapshot = _Watchdog_Ticks_since_boot; last_snapshot = ts->Interval_watchdogs.last_snapshot; if ( !_Chain_Is_empty( &ts->Interval_watchdogs.Chain ) ) { first_watchdog = _Watchdog_First( &ts->Interval_watchdogs.Chain ); /* * We assume adequate unsigned arithmetic here. */ delta = snapshot - last_snapshot; delta_interval = first_watchdog->delta_interval; if (delta_interval > delta) { delta_interval -= delta; } else { delta_interval = 0; } first_watchdog->delta_interval = delta_interval; } ts->Interval_watchdogs.last_snapshot = snapshot; _ISR_Enable( level ); _Watchdog_Insert( &ts->Interval_watchdogs.Chain, &timer->Ticker ); if ( !ts->active ) { _Timer_server_Reset_interval_system_watchdog( ts ); } } else if ( timer->the_class == TIMER_TIME_OF_DAY_ON_TASK ) { /* * We have to advance the last known seconds value of the server and update * the watchdog chain accordingly. */ _ISR_Disable( level ); snapshot = (Watchdog_Interval) _TOD_Seconds_since_epoch(); last_snapshot = ts->TOD_watchdogs.last_snapshot; if ( !_Chain_Is_empty( &ts->TOD_watchdogs.Chain ) ) { first_watchdog = _Watchdog_First( &ts->TOD_watchdogs.Chain ); delta_interval = first_watchdog->delta_interval; if ( snapshot > last_snapshot ) { /* * We advanced in time. */ delta = snapshot - last_snapshot; if (delta_interval > delta) { delta_interval -= delta; } else { delta_interval = 0; } } else { /* * Someone put us in the past. */ delta = last_snapshot - snapshot; delta_interval += delta; } first_watchdog->delta_interval = delta_interval; } ts->TOD_watchdogs.last_snapshot = snapshot; _ISR_Enable( level ); _Watchdog_Insert( &ts->TOD_watchdogs.Chain, &timer->Ticker ); if ( !ts->active ) { _Timer_server_Reset_tod_system_watchdog( ts ); } } _Thread_Enable_dispatch(); }