
/*
 * clock.c: version 3.3 of 6/4/85
 *
 *  clock.c -- Free-running clock routines for VIOS
 *  copyright (c)
 *	Daniel Steinberg	April, 1984
 */
#ifdef SCCS
static char *sccsid = "@(#)clock.c	3.3";
#endif

#include "vinc/viosconf.h"
#include "vinc/iopacket.h"
#include "vinc/pktfuncs.h"
#include "vinc/handlers.h"
#include "vinc/viocmds.h"



clock_interrupt ()

/* clock_interrupt () -- Handle free-running clock interrupts
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	Increment global count of ticks and reschedule any packet
 *	waiting for a kick.  Typically, this is just one packet
 *	whose handler is tick_handler(), below.
 */
{
    ticks++;	/* increment the global flag */
    Intstate++;
    wq_activate(TICK_WAIT_STATE, 0);	/* start waiting packets */
    Intstate--;
}

    PKT_STATE
tick_handler (pkt)
    PKT_PTR pkt;

/* tick_handler (pkt) -- Keeper of the VIOS Clock
 *	in:	pkt		I/O packet pointer
 *	return:	(PKT_STATE)	VIO_REQUEUED (pkt is requeued locally)
 *	thrown:	(NONE)
 *
 *	Perform all clock tick processing:
 *		1) Subtract the correct number of milliseconds from the
 *			timeout counts of waiting packets (see tmoctr()
 *			below).
 *		2) Make sure that the global variable 'ticks' is correctly
 *			synchronized with the clock tick interrupt
 *			(clock_interrupt() above).
 */
{
    register int msecs;
    register int t;

    DIS_INTS;

    /* if time has passed... */
    while ((t=ticks) NE 0)
	{
	ticks -= t;		/* subtract what is known */

	/* calculate the number of milliseconds that have passed */
	msecs = ((t * 1000) + (CLOCK_HERTZ/2)) / CLOCK_HERTZ;

	/* count down the timeouts of all packets on the wait queue */
	tmoctr(msecs, MSEC_TIMEOUT, &Wqueue);

	ENA_INTS;	/* field pending interrupts */
	DIS_INTS;	/* but disable again as a semaphore */
	} /*while*/
    
    State(*pkt) = TICK_WAIT_STATE;	/* wait for next tick */
    q_wait(pkt);

    ENA_INTS;		/* interlock with wq_activate at interrupt */
    return(VIO_REQUEUED);	/* flag no more processing */
}

    PKT_STATE
sec_handler (pkt)
    PKT_PTR pkt;

/* sec_handler (pkt) -- Countdown timeouts every second
 *	in:	pkt		I/O packet ptr
 *	return:	(PKT_STATE)	TMO_WAIT_STATE (wait another second)
 *	thrown:	(NONE)
 *
 *	Perform all one-second processing:
 *		1) Count down the packet timeouts that are in second
 *			quantities (on the wait queue) (see tmoctr()
 *			below).
 *		2) Reschedule all packets waiting for Host resources.
 */
{
    tmoctr(1, SEC_TIMEOUT, &Wqueue);	/* count down second timers */

    wq_activate(HRSRC_WAIT_STATE, 0);	/* retry host resource failures */

    Timecount(*pkt) = 1000;		/* wait another 1000 milliseconds */
    return(TMO_WAIT_STATE);		/* go back on wait queue */
}

    PKT_STATE
min_handler (pkt)
    PKT_PTR pkt;

/* min_handler (pkt) -- Countdown timeouts every minute
 *	in:	pkt		I/O packet ptr
 *	return:	(PKT_STATE)	TMO_WAIT_STATE (wait another minute)
 *	thrown:	(NONE)
 *
 *	Perform all one-minute processing:
 *		1) Count down the packet timeouts that are in minute
 *			quantities (on the wait queue) (see tmoctr()
 *			below).
 */
{
    tmoctr(1, MIN_TIMEOUT, &Wqueue);	/* count down minute timers */

    Timecount(*pkt) = 60;			/* wait another 60 seconds */
    return(TMO_WAIT_STATE);		/* go back on wait queue */
}


tmoctr (cnt, scale, que)
    int cnt;
    unsigned scale;
    LIST_HEAD *que;

/* tmoctr (cnt, scale, que) -- Decrement timeout counters on a queue
 *	in:	cnt		Number to subtract from timeout counts
 *	in:	scale		Timeout scale to match
 *	in:	que		Address of listhead of queue to search
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	Searches 'que' for i/o packets whose timeout count is non-zero
 *	and whose timeout scale matches 'scale'.  All qualifying packets
 *	have 'cnt' subtracted from their timeout counts.  If the timeout
 *	goes to or below zero, the timeout is set to zero and the
 *	packet is activated as follows:
 *		1) If the packet state is TMO_WAIT_STATE, the packet
 *			is simply reactivated.
 *		2) Otherwise, NOTHING HAPPENS.
 *		(eventually, timeout/kill processing will be implemented)
 */
{
    register PKT_PTR pkt;
    register IO_PACKET *pk;
    register PKT_PTR nptr;
    register int tmo;

    DIS_INTS;		/* don't let the queues change during search */

    pkt = (PKT_PTR) Next(que);		/* Get ptr to 1st pkt in queue */

    while (pkt NE NULL)
	{
	nptr = Next(pk = *pkt);		/* save ptr to next pkt */

	if ( ((tmo = Timecount(pk)) NE 0) AND (Timescale(pk) EQ scale) )
	    {
	    if ((tmo -= cnt) LE 0)	/* if this time it times out */
		{
		Timecount(pk) = 0;	/* always leave at zero */
		if (State(pk) EQ TMO_WAIT_STATE)
		    {
		    State(*pkt) = VIO_RUN_STATE;	/* activate */
		    q_virtual(uq_item(pkt, que));
		    } /*if-TMO_WAIT_STATE*/
		else
		    {
		    /* handle packet timeouts here */
		    } /*if-packet timed out*/
		} /*if-timeout*/
	    else
		{
		Timecount(pk) = tmo;	/* set subtracted value */
		}
	    } /*if-packet qualifies*/
	
	pkt = nptr;		/* point to next packet */
	} /*while*/
    ENA_INTS;
}
