#ifndef __FreeBSD__
/*---------------------------------------------------------------------------*\

     FILE....: LOOPMON.CPP
     TYPE....: C++ Module
     AUTHOR..: Peter Wintulich
     DATE....: 20/NOV/2003

     Line voltage monitoring functions.

     "loopvolt_..." functions provide control varible access to read or adjust
     the set points and line voltages.
     
     "LoopSenseThread" provides message generation for phone line state sensing.
     Three messages are:
     VPB_LOOP_ONHOOK
     VPB_LOOP_OFFHOOK
     VPB_LOOP_POLARITY
     	e.data = polarity (0=NEG, 1=POS)


         Voicetronix Voice Processing Board (VPB) Software
    Copyright (C) 1999-2007 Voicetronix www.voicetronix.com.au

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
    MA  02110-1301  USA

\*---------------------------------------------------------------------------*/

#include "apifunc.h"
#include "mapdev.h"
#include "mess.h"
#include "generic.h"


#define	IICDEV_VSENSE	0x0a000		// Address of IIC dev on the bus

#define VSENSE_SUMMARY0	0		// Index to Register locations
#define VSENSE_SUMMARY1	1
#define VSENSE_SUMMARY2	2
#define VSENSE_SUMMARY3	3
#define VSENSE_SUMMARY4	4
#define VSENSE_SUMMARY5	5
#define VSENSE_ZERO	6		// Zero offset& Opt reset reg.
#define VSENSE_CH_LO	7		// Low SIG. Threshold (1 per card)
#define VSENSE_CH_0	8		// Current Voltage first CH.
#define VSENSE_CH_TH_0	20		// Threshold on/off Hook first CH.

#define LSDELAY		20		// Thread sleep time

void LoopSenseThread(void *);

typedef struct
{
    char 	polarity;
    char 	polarity_delta;
    char 	hook;
    char 	hook_delta;
    int		low_expire;
    int		onhook_validate;
    int		offhook_validate;
    char 	enabled;
} LOOPSENSE;

static 		LOOPSENSE *loopsense;
static int 	LST_flag = 0;
static int 	LST_done = 0;

/*-------------------------------------------------------------------------*\
	
	FUNCTION:	StartLoopSenseThread 
	AUTHOR..:	Peter Wintulich
	DATE....:	18/NOV/03
	
	This function configures and intialises the monitoring of the Loop
       	voltage. 
	
\*-------------------------------------------------------------------------*/
void StartLoopSenseThread(int NumChans)
{
//	int	ppc;
//	int	NumChans=0;
//	ppc = vpb_get_ports_per_card();
//	NumChans = vpb_c->GetBoardCount() * ppc;
//	mprintf("Starting loop sense thread monitoring on %d ports per card for [%d] channels\n",
//	        ppc, NumChans);

	mprintf("Starting loop sense thread monitoring on [%d] channels\n", NumChans);

	loopsense = new LOOPSENSE[NumChans];

	// Setup so no events generated to start with.
	for(int x=0; x < NumChans; ++x)
	{
		loopsense[x].polarity = 0;
		loopsense[x].polarity_delta = 0;
		loopsense[x].hook = 0;
		loopsense[x].hook_delta = 0;
		loopsense[x].low_expire =1;
		loopsense[x].enabled =1;
		loopsense[x].onhook_validate=11;	// 200ms to validate signal
		loopsense[x].offhook_validate=11;	// 200ms to validate signal
	}
	// Launch thread.
	LST_done = 0;
	LST_flag = 0;
	Generic_beginthread(LoopSenseThread, 0, NULL);
	mprintf("Loop Sense Thread started OK!\n");
}

/*-------------------------------------------------------------------------*\
	
	FUNCTION:	KillLoopSenseThread 
	AUTHOR..:	Peter Wintulich
	DATE....:	18/NOV/03
	
	This function kills the monitoring of the LOOP voltage. 
	
\*-------------------------------------------------------------------------*/
void KillLoopSenseThread()
{
	// signal LST to exit
	LST_flag = 1;

	// wait for LST to exit
	//while(LST_flag == 1)
	while(!LST_done)
		GenericSleep(LSDELAY);
	delete [] loopsense;
	loopsense = NULL;
}

/*-------------------------------------------------------------------------*\
 
        FUNCTION:       LoopSenseThread
        AUTHOR..:       Peter Wintulich
        DATE....:       18/NOV/03

	NOTE: older cards the firmware reports Zero in the unstable bit
	The A/D reading is stable (within +/- 2 counts for 8 samples)
 
\*-------------------------------------------------------------------------*/
void LoopSenseThread(void *unused)
{
	(void)unused;

	short       length = 2;
	char        pol,hook,low;
	uint16_t    buf[64];
	int         ch,i,h,c;
	VPB_EVENT   e;

	int	pol_settle = 2;
	int	numboards = vpb_c->GetBoardCount();

	do {
		c=0;	  		// absolute channel count, first ch = 0
		for(i=0; i<numboards ; i++) {
			VPBREG &vr = *vpb_c->vpbreg(i);

			// if this board has not got the loop voltage sense skip this board!    
			// set length according to board type (V4pci = 2, V12pci = 6, V6PCI =3)
			if(vr.model == VPB_V4PCI || vr.model == VPB_V4LOG){
				// read status bits.
				dynamic_cast<V4PCI_DSP*>(vr.hostdsp)
					->IicRead(IICDEV_VSENSE+VSENSE_SUMMARY0,
						  length, &buf[0]);

				//mprintf("IIC_Read state = %d\n",buf[0]);
				if(buf[0] ==1)	// IIC read was good so proceed checking for events
				{
					for(ch=0; ch < vr.numch; ++ch)
					{
						// only report events if ch is enabled ?? 	
						h=mapdevtohndle(i,ch);
						if(loopsense[ch+c].enabled) {
							pol = ((buf[1+(ch/2)] & (1<<(((ch%2)*4)+1)))==0 ? 0:1);
							hook= ((buf[1+(ch/2)] & (1<<(((ch%2)*4))))==0 ? 0:1);
							low = ((buf[1+(ch/2)] & (1<<(((ch%2)*4)+2)))==0 ? 0:1);
							// when low == 0 the signal is in the normal opperating range
							if(low == 0){
								loopsense[ch+c].low_expire = 100;	// 2 seconds delay	  
								// check for polarity event, Need to match 2 samples to
								//  send event.
								if(pol == loopsense[ch+c].polarity){
									if(loopsense[ch+c].polarity_delta > 0)
										loopsense[ch+c].polarity_delta--;
									if(loopsense[ch+c].polarity_delta == 1) {
										// loopsense[ch+c].polarity = pol;
										e.type = VPB_LOOP_POLARITY;
										e.handle = h;
										e.data =  loopsense[ch+c].polarity ;
										putevt(&e, VPB_LOOP_POLARITY);
										loopsense[ch+c].polarity_delta = 0;	// reset Delta
									}
								}
								else {
									loopsense[ch+c].polarity_delta = pol_settle;       // set Delta
									loopsense[ch+c].polarity = pol;
								}

								// check for loop on/off hook event
								if(hook == loopsense[ch+c].hook) {
									if(loopsense[ch+c].hook_delta >0)
										loopsense[ch+c].hook_delta--;
									if(loopsense[ch+c].hook_delta == 1) {
										//loopsense[ch+c].hook = hook;
										e.type = (loopsense[ch+c].hook ? VPB_LOOP_OFFHOOK:VPB_LOOP_ONHOOK);
										e.handle = h;
										e.data = 0;
										putevt(&e, (loopsense[ch+c].hook ? VPB_LOOP_OFFHOOK:VPB_LOOP_ONHOOK));
										loopsense[ch+c].hook_delta = 0;
									}
								}
								else {
									if(loopsense[ch+c].hook){
										loopsense[ch+c].hook_delta = loopsense[ch+c].onhook_validate;
									}
									else{
										loopsense[ch+c].hook_delta = loopsense[ch+c].offhook_validate;
									}
									loopsense[ch+c].hook = hook;
								}
							}
							else	// low == 1 signal is lower than line can function at. 
							// countdown delay then send onhook to prevent logging
							// on open circuit line
							{
								if(	loopsense[ch+c].low_expire > 0 ){
									loopsense[ch+c].low_expire--;
									if(loopsense[ch+c].low_expire == 1) {
										e.type = VPB_LOOP_ONHOOK;
										e.handle = h;
										e.data = 0;
										putevt(&e,VPB_LOOP_ONHOOK);

										loopsense[ch+c].hook = hook;
										loopsense[ch+c].polarity = pol;
									}
								}
							}
						}
					}	//for(ch..	    
				} //endif
			}
			c += vr.numch;
		} //for(i=..
		// sleep for a while
		GenericSleep(LSDELAY);
		// Heep going or shutdown?
	}while(!LST_flag);

 /*
 catch(Wobbly w){
	char s[MAX_STR];
	w.translate(s);
        if (w.file[0] != 0)
            mprintf("exception caught: %s, %s, line = %d\n",s, w.file, w.line);
        else
            mprintf("exception caught: %s\n",s);
            //mprintf("Press any key to exit....\n");
        //assert(0);
 }
 */
        // Signal LST kill function that LST is dead!
        LST_flag = 0;
        LST_done = 1;
        Generic_endthread();
}


/*---------------------------------------------------------------------------*\
        FUNCTION: loopvolt_get
        AUTHOR..: Peter Wintulich
        DATE....: 20/NOV/2003

        Read the current line voltage including polarity.
	The units are not scaled but are ADC units in 2's complement format
	0x80 = -128, 0x00 = 0, 0x7f = +127 
        The sign represents the line polarity, normaly negetive.

	Return Value of the function indicates success or failure of the call
	If the Device is busy the call is aborted.
\*--------------------------------------------------------------------------*/
int loopvolt_get(int h, short *volts)
{
	uint16_t    buf[64];
	uint16_t    b, ch;

	maphndletodev(h, &b, &ch);

	dynamic_cast<V4PCI_DSP*>(vpb_c->vpbreg(b)->hostdsp)
		->IicRead(IICDEV_VSENSE+VSENSE_CH_0+ch, 1, &buf[0]);

	*volts= buf[1];
	//printf("Got loop volt result buf [%02x] [%02x]\n",buf[0],buf[1]);
	return buf[0];
}

/*---------------------------------------------------------------------------*\
        FUNCTION: loopvolt_get_threshold
        AUTHOR..: Peter Wintulich
        DATE....: 20/NOV/2003

        Read the current line voltage threshold.
	The units are not scaled but are ADC units.
        valid range 0 to 127, usable range 8 to 100

	Return Value of the function indicates success or failure of the call
	If the Device is busy the call is aborted.
\*--------------------------------------------------------------------------*/
int loopvolt_get_threshold(int h, short *volts)
{
	uint16_t    buf[5];
	uint16_t    b, ch;

	maphndletodev(h, &b, &ch);

	dynamic_cast<V4PCI_DSP*>(vpb_c->vpbreg(b)->hostdsp)
		->IicRead(IICDEV_VSENSE+VSENSE_CH_TH_0+ch, 1, &buf[0]);

	*volts = (short) buf[1];
	return buf[0];
}

/*---------------------------------------------------------------------------*\
        FUNCTION: loopvolt_set_threshold
        AUTHOR..: Peter Wintulich
        DATE....: 20/NOV/2003

        Set a new line voltage threshold.
	The units are not scaled but are ADC units.
        This threshold is per port, each channel is unique.
	
	volts:	Range 0 to 127

	Return Value of the function indicates success or failure of the call.
	Failure may mean the IIC bus was busy on another call, if so try again.
	If the Device is busy the call is aborted.
\*--------------------------------------------------------------------------*/
int loopvolt_set_threshold(int h, short volts)
{
	uint16_t  buf[5] = { uint16_t(volts) };
	uint16_t  b, ch;

	maphndletodev(h, &b, &ch);

	dynamic_cast<V4PCI_DSP*>(vpb_c->vpbreg(b)->hostdsp)
		->IicWrite(IICDEV_VSENSE+VSENSE_CH_TH_0+ch, 1, &buf[0]);

	return buf[0];
}

/*---------------------------------------------------------------------------*\
        FUNCTION: loopvolt_get_lowlimit
        AUTHOR..: Peter Wintulich
        DATE....: 20/NOV/2003

        Read the current line voltage low limit threshold.
	The units are not scaled but are ADC units.
        valid range 0 to 127, usable range 0 to 10, default 2

	There is only one set register per card. Therefor the handle number
       	is only resolved to identify the card number.

	Return Value of the function indicates success or failure of the call
	If the Device is busy the call is aborted.
\*--------------------------------------------------------------------------*/
int loopvolt_get_lowlimit(int h, short *volts)
{
	uint16_t  buf[5];
	uint16_t  b, ch;

	maphndletodev(h, &b, &ch);

	dynamic_cast<V4PCI_DSP*>(vpb_c->vpbreg(b)->hostdsp)
		->IicRead(IICDEV_VSENSE+VSENSE_CH_LO, 1, &buf[0]);

	*volts = (short) buf[1];
	return buf[0];
}

/*---------------------------------------------------------------------------*\
        FUNCTION: loopvolt_set_lowlimit
        AUTHOR..: Peter Wintulich
        DATE....: 20/NOV/2003

	Read the current line voltage low limit threshold.
	The units are not scaled but are ADC units.
	valid range 0 to 127, usable range 0 to 10, default 2

	There is only one set register per card. Therefor the handle number
	is only resolved to identify the card number.
						
	Return Value of the function indicates success or failure of the call.
	Failure may mean the IIC bus was busy on another call, if so try again.
	If the Device is busy the call is aborted.
\*--------------------------------------------------------------------------*/
int loopvolt_set_lowlimit(int h, short volts)
{
	uint16_t  buf[5] = { uint16_t(volts) };
	uint16_t  b, ch;

	maphndletodev(h, &b, &ch);

	dynamic_cast<V4PCI_DSP*>(vpb_c->vpbreg(b)->hostdsp)
		->IicWrite(IICDEV_VSENSE+VSENSE_CH_LO, 1, &buf[0]);

	return buf[0];
}

/*---------------------------------------------------------------------------*\
        FUNCTION: loop_get_onhookwindow
        AUTHOR..: Peter Wintulich
        DATE....: 15/APR/2005

\*---------------------------------------------------------------------------*/
int loop_get_onhookwindow(int h, int *ms)
{
	uint16_t b, ch, nc, c;
	maphndletodev(h, &b, &ch);
	nc = vpb_c->vpbreg(b)->numch;
	c =(b*nc)+ch;
		*ms = (loopsense[c].onhook_validate -1)*LSDELAY;
	return(0);
}

/*---------------------------------------------------------------------------*\
        FUNCTION: loop_set_onhookwindow
        AUTHOR..: Peter Wintulich
        DATE....: 15/APR/2005

\*---------------------------------------------------------------------------*/
int loop_set_onhookwindow(int h, int ms)
{
	uint16_t b, ch, nc, c;
	if( (ms >19) && (ms <10000) )
	{
		maphndletodev(h, &b, &ch);
		nc = vpb_c->vpbreg(b)->numch;
		c =(b*nc)+ch;
		loopsense[c].onhook_validate= 1+(ms/LSDELAY);
	}
	else
		return(-1);
	return(0);
}

/*---------------------------------------------------------------------------*\
        FUNCTION: loop_get_offhookwindow
        AUTHOR..: Peter Wintulich
        DATE....: 15/APR/2005

\*---------------------------------------------------------------------------*/
int loop_get_offhookwindow(int h, int *ms)
{
	uint16_t b, ch, nc, c;
	maphndletodev(h, &b, &ch);
	nc = vpb_c->vpbreg(b)->numch;
	c =(b*nc)+ch;
	*ms = (loopsense[c].offhook_validate -1)*LSDELAY;
	return(0);
}

/*---------------------------------------------------------------------------*\
        FUNCTION: loop_set_offhookwindow
        AUTHOR..: Peter Wintulich
        DATE....: 15/APR/2005

\*---------------------------------------------------------------------------*/
int loop_set_offhookwindow(int h, int ms)
{	// Limit range 20ms to 10 seconds
	uint16_t b, ch, nc, c;
	if( (ms >19) && (ms <10000) )
	{
		maphndletodev(h, &b, &ch);
		nc = vpb_c->vpbreg(b)->numch;
		c =(b*nc)+ch;
		loopsense[c].offhook_validate= 1+(ms/LSDELAY);
	}
	else
		return(-1);
	return(0);
}

#endif
