/***************************************************************
 * PrITimer.cpp
 *
 * This class provides an implementation of the timer class.
 *
 ****************************************************************/

/*
 * modification history
 *----------------------
 *
 * 10/26/97: K.C. Chang: created.
 */

#include "PrITimer.h"

#include <iostream.h>
#include <stdio.h>
#include <time.h>
#include <sys/neutrino.h>
#include <sys/netmgr.h>

PrITimer::PrITimer(int frequency)
{
    frequency_ = frequency;
    if (frequency_ > PR_ITIMER_FREQUENCY_MAX)
      {
	  frequency_ = PR_ITIMER_FREQUENCY_MAX;
	  cout<<"PrITimer::PrITimer():frequency = max("<<frequency_<<")"<<endl;
      }
    else if (frequency_ < PR_ITIMER_FREQUENCY_MIN)
      {
	  frequency_ = PR_ITIMER_FREQUENCY_MIN;
	  cout<<"PrITimer::PrITimer():frequency = min("<<frequency_<<")"<<endl;
      }

    flag_ = Initialize();
}

PrITimer::~PrITimer()
{
    Quit();
}

/*
Please note that stack checking must be disabled in your
handler. You can use the pragmas below and/or the
"-Wc,-s" option to the cc command.
*/

//pid_t proxy_G;
int pulseID_G;
volatile unsigned counter_G = 0;
volatile unsigned ticks_G = 1;
struct sigevent event_G;

/* The hardware interrupt handler */
#pragma off(check_stack);
const struct sigevent * handler( void * area, int id )
{
    /* Kick a proxy every "ticks" timer interrupts */
    if((++counter_G % ticks_G) == 0)
      return(&event_G);

    return NULL;
}
#pragma on(check_stack);

int PrITimer::start()
{
    /* Get a proxy for the interrupt handler to kick */
/* MIG4NTO qnx_proxy_attach Consider replacing proxies with QNX Neutrino */
/* MIG4NTO                  pulses. */
/*    proxy_G = qnx_proxy_attach(0,0,0,-1);
    if (proxy_G == -1) 
      return Error("start(): Unable to attach proxy");
*/
    pulseID_G = ChannelCreate( 0 );
    event_G.sigev_notify = SIGEV_PULSE;
    event_G.sigev_coid = ConnectAttach( ND_LOCAL_NODE, 0, pulseID_G,
        _NTO_SIDE_CHANNEL, 0 );
    event_G.sigev_priority = getprio( 0 );
    event_G.sigev_code = _PULSE_CODE_MINAVAIL;

    /* Attach to the timer */
/* MIG4NTO  qnx_hint_attach Use InterruptAttach() or InterruptAttachEvent() */
/* MIG4NTO                  instead. */
/* MIG4NTO           FP_SEG NO LONGER SUPPORTED */
/*    interruptHandlerID_ = qnx_hint_attach(0, &handler, FP_SEG(&counter_G));
    if (interruptHandlerID_ == -1) 
      return Error("start(): Unable to attach interrupt");
*/
    ThreadCtl( _NTO_TCTL_IO, 0 );
    interruptHandlerID_ = InterruptAttach( 0, handler, NULL, 0, NULL );
    if (interruptHandlerID_ == -1) 
      return Error("start(): Unable to attach interrupt");

    return PR_ITIMER_OK;
}

int PrITimer::wait()
{
    /* Wait for the proxy */
/* MIG4NTO          Receive Use MsgReceive() instead.  Note that this takes a */
/* MIG4NTO                  channel id returned from ChannelCreate() instead */
/* MIG4NTO                  of a process id. See the migration guide as a */
/* MIG4NTO                  starting place for more information on this. */
/*    if (Receive(proxy_G, 0, 0) == -1)
      return Error("wait(): Unable to receive a message");
*/
    struct _pulse pulse;
    if ( MsgReceive( pulseID_G, &pulse, sizeof(pulse), NULL ) == -1 )
        return Error("wait(): Unable to receive a message");

    int counter = counter_G;
    counter_G = 0;

    return counter;
}

int PrITimer::zero()
{
    counter_G = 0;

    return Resolution();
}

double PrITimer::time()
{
    struct timespec t;

    if (clock_gettime(CLOCK_REALTIME, &t) == -1) 
      return Error("time(): Unable to get clock time");

    return (t.tv_sec + t.tv_nsec/(double)PR_BILLION);
}

int PrITimer::usleep(long usec)
{
    struct timespec rqtp;
    rqtp.tv_sec= usec/PR_MILLION;
    rqtp.tv_nsec=(usec - rqtp.tv_sec*PR_MILLION)*PR_THOUSAND;

    if (nanosleep(&rqtp,NULL) == -1)
      return PR_ITIMER_ERROR;

    return PR_ITIMER_OK;
}

int PrITimer::selfTest(int loop)
{
    cout<<"PrITimer:: selfTest(): starts"<<endl;

    if (flag() != PR_ITIMER_OK)
      return Error("constructor error");

    if (start() != PR_ITIMER_OK)
      return Error("start error");

    for(int i=0;i<loop;i++)
      {
	  printf("%d: time=%.9f\n",i,time());
	  for(int j=0;j<loop*1000;j++)
	    {
		Float f=1.0;
		f *= j;
		f = 1.0/f;
	    }
	  int ticks = wait();
	  if (ticks - ticks_G> 0)
	    cout<<"ticks skipped="<<(ticks-ticks_G)/2.0<<endl;
      }
    
    cout<<"PrITimer:: selfTest(): done"<<endl;

    return PR_ITIMER_OK;
}


// private methods

inline int PrITimer::Initialize()
{
    if (zero() != PR_ITIMER_OK)
      return PR_ITIMER_ERROR;

    ticks_G = (int) (PR_ITIMER_FREQUENCY_MAX/frequency_);
    if (ticks_G < 1)
      ticks_G = 1;

    return PR_ITIMER_OK;
}

inline int PrITimer::Resolution()
{
    struct timespec res;

    if (clock_getres(CLOCK_REALTIME, &res) == -1) 
      return Error("Resolution(): Unable to get clock resolution");

    if ( res.tv_nsec == PR_BILLION/PR_ITIMER_FREQUENCY_MAX ) // 0.5 msec
      return PR_ITIMER_OK;

    /* Set the resolution */

    res.tv_nsec = PR_BILLION/PR_ITIMER_FREQUENCY_MAX; // 0.5 msec

/* MIG4NTO     clock_setres Replaced with ClockPeriod(). */
/*    if( clock_setres(CLOCK_REALTIME, &res) == -1 ) 
      return Error("Resolution(): Unable to set clock resolution");
*/
    _clockperiod clockPeriod;
    clockPeriod.nsec = res.tv_nsec;
    clockPeriod.fract = 0;
    if ( ClockPeriod( CLOCK_REALTIME, &clockPeriod, NULL, 0 ) == -1 )
        return Error("Resolution(): Unable to set clock resolution");

    return PR_ITIMER_OK;
}


inline int PrITimer::Quit()
{
/* MIG4NTO  qnx_hint_detach Use InterruptDetach() */
//    qnx_hint_detach(interruptHandlerID_);
    InterruptDetach( interruptHandlerID_ );
    return PR_ITIMER_OK;
}

inline int PrITimer::Error(char *mesg)
{
    cout<<"PrITimer:: Error(): "<<mesg<<endl;
    return PR_ITIMER_ERROR;
}
