u-isdn/timer/timer.c

440 lines
8.6 KiB
C

/**
** Streams TIMER module
**/
#include "f_module.h"
#include "primitives.h"
#include "kernel.h"
#include "f_signal.h"
#include "f_malloc.h"
#include "streams.h"
#include "stropts.h"
#ifdef DONT_ADDERROR
#include "f_user.h"
#endif
#include "streamlib.h"
#include "timer.h"
#include "isdn_proto.h"
#ifdef KERNEL
#ifdef linux
#include <linux/sched.h>
#define TIME jiffies
#else
extern struct timeval time;
#define TIME (time.tv_sec*HZ + time.tv_usec/(1000000/HZ))
#endif
#else
extern struct timeval Time;
#define TIME (time.tv_sec*HZ + time.tv_usec/(1000000/HZ))
#define time Time
#endif
/*
* Standard Streams stuff.
*/
static struct module_info timer_minfo =
{
0, "timer", 0, INFPSZ, 200,100
};
static qf_open timer_open;
static qf_close timer_close;
static qf_put timer_rput, timer_wput;
static qf_srv timer_rsrv, timer_wsrv;
static struct qinit timer_rinit =
{
timer_rput, timer_rsrv, timer_open, timer_close, NULL, &timer_minfo, NULL
};
static struct qinit timer_winit =
{
timer_wput, timer_wsrv, NULL, NULL, NULL, &timer_minfo, NULL
};
struct streamtab timerinfo =
{&timer_rinit, &timer_winit, NULL, NULL};
struct timer_ {
short flags;
#define TIMER_INUSE 01
#define TIMER_TIMER 02
/* #define TIMER_INT 04 */
#define TIMER_WHEN_IN 010
#define TIMER_WHEN_OUT 020
#define TIMER_INCOMING 040
#define TIMER_IFDATA_IN 0100
#define TIMER_IFDATA_OUT 0200
int lasttime;
int interval;
int pretime;
int maxread, maxwrite;
int lastread, lastwrite;
#ifdef NEW_TIMEOUT
int timer;
#endif
queue_t *qptr;
};
/* Streams code to open the driver. */
static int
timer_open (queue_t * q, dev_t dev, int flag, int sflag ERR_DECL)
{
register struct timer_ *tim;
if (q->q_ptr) {
return 0;
}
tim = malloc(sizeof(*tim));
if(tim == NULL)
ERR_RETURN(-ENOMEM);
memset(tim,0,sizeof (*tim));
tim->qptr = q;
WR (q)->q_ptr = (char *) tim;
q->q_ptr = (char *) tim;
tim->flags = TIMER_INUSE|TIMER_IFDATA_IN|TIMER_IFDATA_OUT|TIMER_WHEN_IN|TIMER_WHEN_OUT;
tim->interval = 60*HZ;
tim->pretime = 55*HZ;
tim->maxread=0; tim->maxwrite=0;
tim->lastread=tim->lastwrite=TIME;
MORE_USE;
return 0;
}
static void
timer_timeout(struct timer_ *tim)
{
if(!(tim->flags & TIMER_TIMER))
return;
if((tim->maxread != 0 && tim->maxread < TIME-tim->lastread)
&& (tim->maxwrite != 0 && tim->maxwrite < TIME-tim->lastwrite)) {
mblk_t *mb = allocb(3,BPRI_MED);
if(mb != NULL) {
m_putid(mb, /* (tim->flags & TIMER_INT) ? PROTO_WILL_INTERRUPT : */ PROTO_WILL_DISCONNECT);
DATA_TYPE(mb) = MSG_PROTO;
putnext(tim->qptr,mb);
tim->flags &=~ TIMER_TIMER;
return;
}
}
#ifdef NEW_TIMEOUT
tim->timer =
#endif
timeout((void *)timer_timeout,tim,tim->interval-HZ);
}
/* Streams code to close the driver. */
static void
timer_close (queue_t * q, int dummy)
{
register struct timer_ *tim;
tim = (struct timer_ *) q->q_ptr;
if(tim->flags & TIMER_TIMER) {
tim->flags &=~ TIMER_TIMER;
#ifdef NEW_TIMEOUT
untimeout(tim->timer);
#else
untimeout(timer_timeout,tim);
#endif
}
flushq (q, FLUSHALL);
flushq (WR (q), FLUSHALL);
free(tim);
LESS_USE;
return;
}
static void
timer_proto (queue_t * q, mblk_t * mp, char down)
{
register struct timer_ *tim = (struct timer_ *) q->q_ptr;
streamchar *origmp = mp->b_rptr;
ushort_t id;
int error = 0;
if (m_getid (mp, &id) != 0) {
mp->b_rptr = origmp;
putnext (q, mp);
return;
}
switch (id) {
default:
break;
case PROTO_TICK:
if(tim->lasttime && (tim->lasttime < TIME)) {
if(tim->interval > TIME - tim->lasttime) /* shorter! */
tim->interval = TIME - tim->lasttime;
/* if it gets longer we won't do a thing. */
}
tim->lasttime = TIME;
if(tim->flags & TIMER_TIMER) {
#ifdef NEW_TIMEOUT
untimeout(tim->timer);
#else
untimeout(timer_timeout,tim);
#endif
#ifdef NEW_TIMEOUT
tim->timer =
#endif
timeout((void *)timer_timeout,tim,tim->interval-HZ);
}
break;
case PROTO_INCOMING:
tim->flags |= TIMER_INCOMING;
break;
case PROTO_OUTGOING:
tim->flags &=~ TIMER_INCOMING;
break;
case PROTO_CONNECTED:
if(down) break;
switch(tim->flags & (TIMER_TIMER|TIMER_WHEN_IN|TIMER_WHEN_OUT|TIMER_INCOMING)) {
case TIMER_WHEN_IN|TIMER_WHEN_OUT|TIMER_INCOMING:
case TIMER_WHEN_IN|TIMER_WHEN_OUT:
case TIMER_WHEN_IN |TIMER_INCOMING:
case TIMER_WHEN_OUT:
if(tim->flags & TIMER_TIMER)
break;
#ifdef NEW_TIMEOUT
tim->timer =
#endif
timeout((void *)timer_timeout,tim,tim->pretime-HZ);
tim->flags |= TIMER_TIMER;
break;
default:
break;
}
tim->lasttime = 0;
break;
case PROTO_DISCONNECT:
case PROTO_INTERRUPT:
if(tim->flags & TIMER_TIMER) {
tim->flags &=~ TIMER_TIMER;
#ifdef NEW_TIMEOUT
untimeout(tim->timer);
#else
untimeout(timer_timeout,tim);
#endif
}
break;
case PROTO_DATA_IN:
tim->lastread = TIME;
break;
case PROTO_DATA_OUT:
tim->lastwrite = TIME;
break;
case PROTO_MODULE:
if (strnamecmp (q, mp)) { /* Config information for me. */
long z;
while (mp != NULL && m_getsx (mp, &id) == 0) {
switch (id) {
default:
goto err;
case PROTO_MODULE:
break;
case TIMER_IF_OUT:
tim->flags |= TIMER_WHEN_OUT;
tim->flags &=~ TIMER_WHEN_IN;
break;
case TIMER_IF_IN:
tim->flags |= TIMER_WHEN_IN;
tim->flags &=~ TIMER_WHEN_OUT;
break;
case TIMER_IF_BOTH:
tim->flags |= (TIMER_WHEN_IN|TIMER_WHEN_OUT);
break;
case TIMER_DATA_OUT:
tim->flags |= TIMER_IFDATA_OUT;
tim->flags &=~ TIMER_IFDATA_IN;
break;
case TIMER_DATA_IN:
tim->flags |= TIMER_IFDATA_IN;
tim->flags &=~ TIMER_IFDATA_OUT;
break;
case TIMER_DATA_BOTH:
tim->flags |= (TIMER_IFDATA_IN|TIMER_IFDATA_OUT);
break;
case TIMER_DATA_NONE:
tim->flags &=~ (TIMER_IFDATA_IN|TIMER_IFDATA_OUT);
break;
#if 0
case TIMER_DO_DISC:
tim->flags &=~ TIMER_INT;
break;
case TIMER_DO_INT:
tim->flags |= TIMER_INT;
break;
#endif
case TIMER_INTERVAL:
if ((error = m_geti (mp, &z)) != 0)
goto err;
if(z < 1 || z > 24*60*60)
goto err;
tim->interval = z*HZ;
tim->lasttime = 0; /* for sync */
break;
case TIMER_PRETIME:
if ((error = m_geti (mp, &z)) != 0)
goto err;
if(z < 1 || z > 24*60*60)
goto err;
tim->pretime = z*HZ;
break;
case TIMER_READMAX:
if ((error = m_geti (mp, &z)) != 0)
goto err;
if (z < 2 || z >= 7*24*60*60)
goto err;
tim->maxread = z*HZ;
break;
case TIMER_WRITEMAX:
if ((error = m_geti (mp, &z)) != 0)
goto err;
if (z < 2 || z >= 7*24*60*60)
goto err;
tim->maxwrite = z*HZ;
break;
}
}
if (mp != NULL) {
mp->b_rptr = origmp;
m_reply(q,mp,0);
mp = NULL;
}
}
}
if(mp != NULL) {
if(origmp != NULL)
mp->b_rptr = origmp;
putnext(q,mp);
}
return;
err:
mp->b_rptr = origmp;
m_reply (q, mp, error ? error : -EINVAL);
}
/* Streams code to write data. */
static void
timer_wput (queue_t * q, mblk_t * mp)
{
struct timer_ *tim = (struct timer_ *) q->q_ptr;
switch (DATA_TYPE(mp)) {
case CASE_DATA:
if(tim->flags & TIMER_IFDATA_OUT)
tim->lastwrite = TIME;
/* FALL THRU */
case MSG_PROTO:
putq (q, mp);
break;
case M_FLUSH:
if (*mp->b_rptr & FLUSHW)
flushq (q, FLUSHDATA);
putnext (q, mp);
break;
default:
putnext (q, mp);
break;
}
return;
}
/* Streams code to scan the write queue. */
static void
timer_wsrv (queue_t * q)
{
mblk_t *mp;
while ((mp = getq (q)) != NULL) {
switch (DATA_TYPE(mp)) {
case MSG_PROTO:
timer_proto (q, mp, 1);
break;
default:
if((DATA_TYPE(mp) > QPCTL) || canput (q->q_next)) {
putnext (q, mp);
continue;
} else {
putbq (q, mp);
return;
}
}
}
return;
}
/* Streams code to read data. */
static void
timer_rput (queue_t * q, mblk_t * mp)
{
struct timer_ *tim = (struct timer_ *) q->q_ptr;
switch (DATA_TYPE(mp)) {
case M_FLUSH:
if (*mp->b_rptr & FLUSHR) {
flushq (q, FLUSHDATA);
}
putnext (q, mp); /* send it along too */
break;
case CASE_DATA:
if(tim->flags & TIMER_IFDATA_IN)
tim->lastread = TIME;
/* FALL THRU */
case MSG_PROTO:
default:
putq (q, mp); /* queue it for my service routine */
break;
}
return;
}
/* Streams code to scan the read queue. */
static void
timer_rsrv (queue_t * q)
{
mblk_t *mp;
while ((mp = getq (q)) != NULL) {
switch (DATA_TYPE(mp)) {
case MSG_PROTO:
timer_proto (q, mp, 0);
break;
default:
if((DATA_TYPE(mp) > QPCTL) || canput (q->q_next)) {
putnext (q, mp);
continue;
} else {
putbq (q, mp);
return;
}
}
}
return;
}
#ifdef MODULE
static int do_init_module(void)
{
return register_strmod(&timerinfo);
}
static int do_exit_module(void)
{
return unregister_strmod(&timerinfo);
}
#endif