time.c



/*
 * time.c
 *
 * ------------------------------------------------------------
 * A Kla2 Module
 * Copyright (c) 2003, David Clifton
 * All Rights Reserved
 * http://www.codelode.com
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice, website reference, this permission
 * notice, and the disclaimer of warranty below shall be included
 * in all copies, derivatives, or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 * -------------------------------------------------------------
 *
 *   Provides routines for measuring time intervals
 *   and triggering prioritized functions on expiration
 *   of time intervals, as well as periodically,
 *   are located in this object.  One-shot, and
 *   N-shot triggered pfcns are supported, but they will
 *   introduce greater interrupt latency than
 *   periodic triggers.
 *
 */
#include "types.h"
#include "sizes.h"
#include "err.h"
#include "pfcn.h"
#include "intrpt.h"
#include "time.h"
/* -------------------------------------------------
 * local typedefs
 * ------------------------------------------------- */
struct tpfcn {
    s16 pfcnToPost;   // index of pfcn to post
    s16 repeatCount;  // 0 for indefinite repetition
    s32 totalTicks;   // ticks before/between invocations
    s32 ticksToGo;    // ticks before next invocation
    struct tpfcn *nextTpfcn; // next entry in priority chain
};
typedef struct tpfcn TPFCN; 

/* -------------------------------------------------
 *   data elements for time object
 * ------------------------------------------------- */
/* 
 * tick counter
 */
u32 Time_ticks=0;
/*
 * Timer interrupt overrun flag
 */
bool tickInProgress=FALSE;
/*
 * head of timed pfcn priority chain
 */
TPFCN *firstTpfcn=NULL;     // highest priority entry
/*
 * timed pfcn structure array
 */
TPFCN timedPfcns[SYSMAX_TIMED_PFCNS];
/*
 * private method declarations
 */
void postTpfcns(void);
void insertTpfcn(TPFCN *);
TPFCN *firstFreeTpfcn(void);
TPFCN *removeTpfcn(TPFCN *);


/* --------------------------------------------------
 *    Public methods
 * -------------------------------------------------- */
/*
 * Time_setup
 *
 *     Perform whatever actions are necessary to set up
 * the system clock and periodic timer interrupts,
 * and have them call the periodic Time function Time_ticks().
 * Don't start the clock or enable the interrupts yet.
 */
void Time_setup(void)
{
    /*
     * processor specific calls to set up processor clock
     * and timer interrupt period
     */
}
/*
 * Time_init
 *
 *    Initialize time interval processing and time
 * triggering of pfcns (prioritized functions)
 *
 * WARNING: do not call this method until after
 * Pfcn_init() has been called.
 */
void Time_init(void)
{
    s16 j;

    Time_ticks=0;     // clock starts at zero
    firstTpfcn=NULL;    // empty priority chain

    for(j=0;j<SYSMAX_TIMED_PFCNS;j++) {
        timedPfcns[j].pfcnToPost=NIL;
        timedPfcns[j].repeatCount=NIL;
        timedPfcns[j].totalTicks=0;
        timedPfcns[j].ticksToGo=0;
        timedPfcns[j].nextTpfcn=NULL;
    }
    tickInProgress=FALSE;
    /*
     * processor specific call to start timer
     */
    ;
}
/*
 * Time_get
 *    Return the value of Time_ticks
 */
u32 Time_get(void)
{
    return Time_ticks;
}
/*
 * Time_since
 *     Return the number of ticks that have occurred
 * since the time provided in the single argument
 */
u32 Time_since(u32 prev)
{
    bool intsav;
    u32 diff;

    intsav=Intrpt_disable();
    diff=Time_get()-prev;
    Intrpt_restore(intsav);
    return diff;
}
/*
 * Time_tick
 *    Routine called by the periodic timer interrupt
 * or signal routine (if rtos embedded in unix).
 *
 *    This routine must be called with interrupts
 * disabled.
 */
void Time_tick(void)
{
    Intrpt_markDisabled();
    if(tickInProgress) {
        Err_shutdown(TIMER_INTERRUPT_OVERRUN);
    } else {
        tickInProgress=TRUE;
        Time_ticks++;
        Intrpt_enable();
        postTpfcns();
        tickInProgress=FALSE;
    }
 }
/*
 * Time_registerPfcn
 *     Register a prioritized function (pfcn), identified by
 * the first argument, to be posted after the number of ticks
 * indicated in the second argument(#ticks>=1), This will be
 * repeated the number of times indicated in the third argument.
 * If the third argument is zero, it will be reposted indefinitely,
 * each time after waiting the number of ticks specified in
 * the second argument.
 *
 * EXAMPLES:
 * 1) To register a one-shot timer, which posts a
 * prioritized method on expiration:
 *     Time_registerPfcn(pfcndex,ticks,1)
 *
 * 2) To register an N-shot timer, which posts a
 * prioritized method on each of N expirations:
 *     Time_registerPfcn(pfcndex,ticks,N)
 *
 * 3) To register a timer which periodically posts
 * a prioritized method:
 *     Time_registerPfcn(pfcndex,ticks,0) 
 *
 */
void Time_registerPfcn(s16 pfcndex,s32 ticks,s16 repeat)
{
    s16 pri;
    bool intsav;
    TPFCN *tpptr;

  /*
   * timed pfcns will be chained in priority order
   * that is the order in which they will be executed
   * when more than one is to be executed in a particular
   * periodic interrupt
   */
    pri=Pfcn_priority(pfcndex);
    if(pri==NIL) {                        // No active pfcn
        Err_shutdown(NO_SUCH_PFCN);     // at the supplied index
    } else {
        intsav=Intrpt_disable();
        tpptr=firstFreeTpfcn();
        if(tpptr==NULL) {                   // No more slots available
            Err_shutdown(OUT_OF_TIMED_PFCNS);  // for timed pfcns
        } else {
            tpptr->pfcnToPost=pfcndex;     // fill in the
            tpptr->repeatCount=repeat;     // with the data
            if(ticks<=1) ticks=1;          // don't allow a bad value
            tpptr->totalTicks=ticks;       // supplied
            tpptr->ticksToGo=ticks;
            tpptr->nextTpfcn=NULL;
            insertTpfcn(tpptr);      // insert entry into list
            Intrpt_restore(intsav);
        }
    }
}
/* --------------------------------------------------
 *   Private methods
 * -------------------------------------------------- */
/*
 * postTpfcns
 *
 * This routine scans the priority list for active
 * TPFCN's.  For each one it finds, it checks to see
 * if ticksToGo is zero.  If so, it posts the associated
 * pfcn, and performs logic to decide whether to re-start
 * the timer for another interval.  If ticksToGo is not
 * zero, it just decrements it.
 *
 * NOTE:  If you don't want to pay the interrupt latency 
 * penalty associated with inserting and removing one-shot
 * and N-shot timers, just don't use them.  Insertion of
 * periodic timers will not pose as much of a latency problem
 * if you register them during powerup, before latency is
 * an issue.
 *
 * NOTE: this routine may be called with interrupts
 * enabled.  The only place in the system where timed
 * pfcns are removed from the tpfcn list is here, when
 * a one-shot or N-repeater timer is finished.  Therefore
 * all the links followed in the priority chain will be
 * to valid entries, even if a new entry is missed for
 * one tick period.
 */
 void postTpfcns(void)
 {
     s16 pfcnToPost;
     bool intsav;
     TPFCN *next;

     next=firstTpfcn;
     while(next!=NULL) {
         pfcnToPost=next->pfcnToPost;
         if(next->ticksToGo==0) {   // time to post
             Pfcn_post(pfcnToPost); // post it
             if(next->repeatCount==0) {  // reset periodic poster
                 next->ticksToGo=(next->totalTicks - 1);
                 next=next->nextTpfcn;
             } else {                   // N repeater... see if last shot
                 next->repeatCount--;
                 if(intsav=next->repeatCount==0) {  // when done, remove it and
                     intsav=Intrpt_disable();
                     next=removeTpfcn(next); // point next to next one in list
                     Intrpt_restore(intsav);
                 } else {
					 next->ticksToGo=(next->totalTicks - 1);
                     next=next->nextTpfcn;
                 }
             }
         } else {
             next->ticksToGo--;
             next=next->nextTpfcn;
         }
     }
 }
 /*
 * insertTpfcn
 *
 *    Insert the timed pfcn pointed to by the
 * single argument into the priority chain of
 * registered timed pfcns.
 *
 * Make sure this routine is called with
 * interrupts disabled.
 */
void insertTpfcn(TPFCN *tpptr)
{
    TPFCN *next,*prev;
    s16 thispri,nextpri;

    
    if(firstTpfcn==NULL) { // if list is empty
        firstTpfcn=tpptr;      // just add the
        tpptr->nextTpfcn=NULL;   // entry supplied
    } else {         // otherwise insert by priority
        prev=NULL;
        thispri=Pfcn_priority(tpptr->pfcnToPost);
        next=firstTpfcn;
        nextpri=Pfcn_priority (next->pfcnToPost);
        while(thispri<=nextpri) {  // list starts with highest priority
            prev=next;            // ends with lowest
            next=next->nextTpfcn;
            if(next==NULL) {
                break;
            } else {
                nextpri=Pfcn_priority(next->pfcnToPost);
            }
        }
        if(prev==NULL) {  // if new one is highest priority
            tpptr->nextTpfcn=firstTpfcn;  // put it first
            firstTpfcn=tpptr;
        } else {         // otherwise just link it in
            tpptr->nextTpfcn=prev->nextTpfcn; // where
            prev->nextTpfcn=tpptr;      // it belongs
        }
    }
}
/*
 * removeTpfcn
 *
 *    Remove from the timed pfcn priority list, the
 * entry pointed to by the argument supplied.  Return
 * NULL if it was the last one of the list.  Otherwise
 * return a pointer to the one that followed it
 * in the list.
 *
 * Make sure this routine is called with
 * interrupts disabled.
 */
TPFCN *removeTpfcn(TPFCN *tpptr)
{
    TPFCN *next,*prev;
/* 
 * check pointer argument
 */
    if(tpptr==NULL) {
        Err_shutdown(REMOVED_NONEXISTENT_TIMER);
    }
/*
 * take care of list first
 */
    prev=NULL;
    next=firstTpfcn;
    while(next!=tpptr) {
        if(next==NULL) {
            Err_shutdown(REMOVED_NONEXISTENT_TIMER);
        }
        prev=next;
        next=next->nextTpfcn;
    }
    next=tpptr->nextTpfcn;
    if(prev==NULL) {
        firstTpfcn=next;
    } else {
        prev->nextTpfcn=next;
    }

/*
 * next clear the entry removed
 */
    tpptr->pfcnToPost=NIL;
    tpptr->repeatCount=NIL;
    tpptr->totalTicks=0;
    tpptr->ticksToGo=0;
    tpptr->nextTpfcn=NULL;

    return next;
}
/*
 * firstFreeTpfcn
 *
 * This routine returns a pointer to the first free
 * TPFCN in the array timedPfcns[].  If there is no
 * free TPFCN, this routine returns NULL.
 */
 TPFCN *firstFreeTpfcn(void)
 { 
     s16 j;
     
     for(j=0;j<SYSMAX_TIMED_PFCNS;j++) {
          if(timedPfcns[j].pfcnToPost==NIL) {
               return &timedPfcns[j];
          }         
     }
     return NULL;
 }