Rev 2 |
Go to most recent revision |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* Project: S.Ha.R.K.
*
* Coordinators:
* Giorgio Buttazzo <giorgio@sssup.it>
* Paolo Gai <pj@gandalf.sssup.it>
*
* Authors :
* Paolo Gai <pj@gandalf.sssup.it>
* (see the web pages for full authors list)
*
* ReTiS Lab (Scuola Superiore S.Anna - Pisa - Italy)
*
* http://www.sssup.it
* http://retis.sssup.it
* http://shark.sssup.it
*/
/**
------------
CVS : $Id: time.c,v 1.1.1.1 2002-03-29 14:12:52 pj Exp $
File: $File$
Revision: $Revision: 1.1.1.1 $
Last update: $Date: 2002-03-29 14:12:52 $
------------
This file contains the functions defined in time.h
**/
/*
* Copyright (C) 2000 Paolo Gai
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <ll/ll.h>
#include <errno.h>
#include <kernel/func.h>
#include <pthread.h>
#include <signal.h>
#include <time.h>
#include <limits.h>
struct internal_timer_struct {
struct sigevent evp; /* the sigevent MUST be allocated by the applic. */
int event; /* the timer event... the timer is disarmed when
event == -1 */
struct timespec period; /* period for periodic timers */
struct timespec current; /* time at whitch the event is posted */
/* these fields are used only if SIGEV_SIGNAL is specified */
int signal; /* the reserved signal entry for the timer */
int overrun; /* the signal overrun counter */
int used; /* 1 if the timer is used */
int next;
};
/* the timer table */
static struct internal_timer_struct timer_table[TIMER_MAX];
/* the free timer pointer */
static int timerqueue_free;
void TIMER_register_module()
{
int x;
for (x = 0; x < TIMER_MAX; x++) {
timer_table[x].event = -1; /* invalid value */
NULL_TIMESPEC(&timer_table[x].period);
timer_table[x].used = 0;
timer_table[x].overrun = 0;
// evp is not initialized
timer_table[x].next = x+1;
}
timer_table[TIMER_MAX-1].next = -1;
timerqueue_free = 0;
}
/*---------------------------------------------------------------------*/
/* 14.2.1 - Clocks */
/*---------------------------------------------------------------------*/
int clock_settime(clockid_t clock_id, const struct timespec *tp)
{
if (clock_id != CLOCK_REALTIME)
errno = EINVAL;
else
errno = EPERM;
return -1;
}
int clock_gettime(clockid_t clock_id, struct timespec *tp)
{
if (clock_id != CLOCK_REALTIME) {
errno = EINVAL;
return -1;
}
sys_gettime(tp);
return 0;
}
int clock_getres(clockid_t clock_id, struct timespec *res)
{
if (clock_id != CLOCK_REALTIME) {
errno = EINVAL;
return -1;
}
if (res) {
/* 1 usec */
res->tv_sec = 0;
res->tv_nsec = 1000;
}
return 0;
}
/*---------------------------------------------------------------------*/
/* 14.2.2 - Create a Per-Process Timer */
/*---------------------------------------------------------------------*/
int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
{
if (clock_id != CLOCK_REALTIME) {
errno = EINVAL;
return -1;
}
kern_cli();
if (timerqueue_free == -1 || sigqueue_free == -1) {
kern_sti();
errno = EAGAIN;
return -1;
}
/* alloc a timer descriptor */
*timerid = timerqueue_free;
timerqueue_free = timer_table[timerqueue_free].next;
timer_table[*timerid].used = 1;
/* alloc a signal descriptor and fill the timer struct */
if (!evp) {
// set default data for the evp field
timer_table[*timerid].evp.sigev_notify = SIGEV_SIGNAL;
timer_table[*timerid].evp.sigev_signo = DEFAULT_TIMER_SIGNAL;
timer_table[*timerid].evp.sigev_value.sival_int = *timerid;
}
else
timer_table[*timerid].evp = *evp;
if (timer_table[*timerid].evp.sigev_notify == SIGEV_SIGNAL) {
// alloc a signal descriptor
timer_table[*timerid].signal = sigqueue_free;
sig_queue[sigqueue_free].flags |= USED_FOR_TIMER;
sigqueue_free = sig_queue[sigqueue_free].next;
}
timer_table[*timerid].event = -1;
NULL_TIMESPEC(&timer_table[*timerid].period);
NULL_TIMESPEC(&timer_table[*timerid].current);
timer_table[*timerid].overrun = 0;
kern_sti();
return 0;
}
/*---------------------------------------------------------------------*/
/* 14.2.3 - Delete a Per-Process Timer */
/*---------------------------------------------------------------------*/
int timer_delete(timer_t timerid)
{
if (timerid < 0 || timerid >= TIMER_MAX) {
errno = EINVAL;
return -1;
}
kern_cli();
if (!timer_table[timerid].used) {
kern_sti();
errno = EINVAL;
return -1;
}
timer_table[timerid].used = 0;
/* delete the event if the timer is armed */
if (timer_table[timerid].event != -1)
event_delete(timer_table[timerid].event);
if (timer_table[timerid].evp.sigev_notify == SIGEV_SIGNAL) {
if (!(sig_queue[ timer_table[timerid].signal ].flags & SIGNAL_POSTED)) {
/* if the signal is not pending, we insert it into the sigqueue_free.
instead, if it is pending, it will be inserted into the queue when
delivered */
sig_queue[ timer_table[timerid].signal ].next = sigqueue_free;
sigqueue_free = timer_table[timerid].signal;
}
/* reset the timer flags... */
sig_queue[ timer_table[timerid].signal ].flags &=
~(USED_FOR_TIMER | SIGNAL_POSTED);
}
kern_sti();
return 0;
}
/*---------------------------------------------------------------------*/
/* 14.2.4 - Per-Process Timers */
/*---------------------------------------------------------------------*/
void timer_timerfire(void *arg)
{
/* Now, we queue the signal:
- if the signal is already pending, only increment the pending
activations
- if the signal isn't pending,
- we insert the reserved signal into
the sigqueue_free (so it will be popped by the sigqueue)
- we set the posted flag (it will be resetted when the signal
will be dispatched)
*/
int t = (int)arg;
// kern_printf("*%d",t);
// do the action required...
if (timer_table[t].evp.sigev_notify == SIGEV_SIGNAL) {
if (sig_queue[ timer_table[t].signal ].flags & SIGNAL_POSTED) {
// the signal is already pending, increment the pending activations...
if (timer_table[t].overrun != DELAYTIMER_MAX)
timer_table[t].overrun++;
}
else {
timer_table[t].overrun = 0;
// there is no signal pending... post the signal!!!
// This a dirty trick: The timer has allocated a signal descriptor,
// then the timer put at the top of the free queue,
// so sigqueue_internal pick the right number!!!
sig_queue[ timer_table[t].signal ].next = sigqueue_free;
sigqueue_free = timer_table[t].signal;
sigqueue_internal(0,
timer_table[t].evp.sigev_signo,
timer_table[t].evp.sigev_value,
SI_TIMER);
// setting this flag is used for counting overruns...
sig_queue[ timer_table[t].signal ].flags |= SIGNAL_POSTED;
}
} else if (timer_table[t].evp.sigev_notify == SIGEV_THREAD) {
/* a new thread must be created; note that the pthread_create
calls task_createn and task_activate; task_activate works into
signal handlers and calls event_need_reschedule */
pthread_t new_thread;
if (timer_table[t].evp.sigev_notify_attributes)
pthread_create(&new_thread,
timer_table[t].evp.sigev_notify_attributes,
(void *(*)(void *))timer_table[t].evp.sigev_notify_function,
timer_table[t].evp.sigev_value.sival_ptr);
else {
pthread_attr_t new_attr;
// the task must be created detached
pthread_attr_init(&new_attr);
pthread_attr_setdetachstate(&new_attr, PTHREAD_CREATE_DETACHED);
pthread_create(&new_thread,
&new_attr,
(void *(*)(void *))timer_table[t].evp.sigev_notify_function,
&timer_table[t].evp.sigev_value);
}
}
if (timer_table[t].period.tv_sec != 0 ||
timer_table[t].period.tv_nsec != 0) {
struct timespec temp;
TIMESPEC_ASSIGN(&temp,&timer_table[t].current);
ADDTIMESPEC(&temp, &timer_table[t].period, &timer_table[t].current);
timer_table[t].event =
kern_event_post(&timer_table[t].current,
timer_timerfire,
(void *)t);
/* kern_printf("(post e%d %d.%d)", t,
timer_table[t].current.tv_sec,
timer_table[t].current.tv_nsec/1000); */
}
else
timer_table[t].event = -1;
}
int timer_settime(timer_t timerid, int flags, const struct itimerspec *value,
struct itimerspec *ovalue)
{
struct timespec ct; /* current time */
int ct_read = 0; /* we take the current time only once */
if (timerid < 0 || timerid >= TIMER_MAX || !value) {
errno = EINVAL;
return -1;
}
if (value->it_interval.tv_nsec < 0 ||
value->it_interval.tv_nsec >= 1000000000) {
errno = EINVAL;
return -1;
}
if (value->it_value.tv_nsec < 0 ||
value->it_value.tv_nsec >= 1000000000) {
errno = EINVAL;
return -1;
}
kern_cli();
if (!timer_table[timerid].used) {
kern_sti();
errno = EINVAL;
return -1;
}
if (ovalue) {
if (timer_table[timerid].event == -1)
/* the timer is disarmed, set it_value to 0 */
NULL_TIMESPEC(&ovalue->it_value);
else {
/* the timer is armed, return the remaining expiration time */
ll_gettime(TIME_EXACT, &ct);
ct_read = 1;
SUBTIMESPEC(&timer_table[timerid].current, &ct, &ovalue->it_value);
}
/* and return the reactivation period */
TIMESPEC_ASSIGN(&ovalue->it_interval, &timer_table[timerid].period);
}
/* if it_value is 0, the timer shall be disarmed; if != 0, the timer is
armed: in all the cases, the event must be deleted... */
if (timer_table[timerid].event != -1)
event_delete(timer_table[timerid].event);
if (value->it_value.tv_sec != 0 || value->it_value.tv_nsec != 0) {
/* it_value != 0 -> arm the timer! */
TIMESPEC_ASSIGN(&timer_table[timerid].period, &value->it_interval);
if (flags & TIMER_ABSTIME)
/* the time is absolute */
TIMESPEC_ASSIGN(&timer_table[timerid].current, &value->it_value);
else {
/* the time is relative to current time */
if (!ct_read)
ll_gettime(TIME_EXACT, &ct);
ADDTIMESPEC(&ct, &value->it_value, &timer_table[timerid].current);
}
timer_table[timerid].event =
kern_event_post(&timer_table[timerid].current,
timer_timerfire,
(void *)timerid);
/* kern_printf("(post e%d %d.%d)", timerid,
timer_table[timerid].current.tv_sec,
timer_table[timerid].current.tv_nsec/1000); */
}
kern_sti();
return 0;
}
int timer_gettime(timer_t timerid, struct itimerspec *value)
{
struct timespec ct; /* current time */
if (timerid < 0 || timerid >= TIMER_MAX) {
errno = EINVAL;
return -1;
}
kern_cli();
if (!timer_table[timerid].used) {
kern_sti();
errno = EINVAL;
return -1;
}
if (timer_table[timerid].event == -1)
/* the timer is disarmed, set it_value to 0 */
NULL_TIMESPEC(&value->it_value);
else {
/* the timer is armed, return the remaining expiration time */
ll_gettime(TIME_EXACT, &ct);
SUBTIMESPEC(&timer_table[timerid].current, &ct, &value->it_value);
}
/* and return the reactivation period */
TIMESPEC_ASSIGN(&value->it_interval, &timer_table[timerid].period);
kern_sti();
return 0;
}
int timer_getoverrun(timer_t timerid)
{
int returnvalue;
if (timerid < 0 || timerid >= TIMER_MAX) {
errno = EINVAL;
return -1;
}
kern_cli();
if (!timer_table[timerid].used) {
kern_sti();
errno = EINVAL;
return -1;
}
returnvalue = timer_table[timerid].overrun;
kern_sti();
return returnvalue;
}