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

    FILE....: VPBREG.CPP
    TYPE....: C++ Functions
    AUTHOR..: David Rowe, Ron Lee
    DATE....: 12/9/99

    Helper functions to set up the configuration database known as the DSP 
    registry.


         Voicetronix Voice Processing Board (VPB) Software
         Copyright (C) 1999-2008 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 "vpb_ioctl.h"
#include "mess.h"
#include "verbose.h"

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>


#define	BASE_ADDRESS	        0x310 // def base address of VPB

#define	SIZE_MESS_Q_HDSP	4*256   // size of message FIFOs (16-bit words) for Host DSP's
#define	SIZE_MESS_Q_OL4		256   // size of message FIFOs (16-bit words) for OpenLine4 DSP

// DR 24/9/02 - note changing any of these 3 below may kill host based EC
// code in dspfifo.cpp may also need mods

// use a 1 second buffer for cas loggers to catch first utterance
#ifdef CASLOG
    #define	SIZE_RX_Q	8000  // size of Rx (up) signal FIFOs
#else
    #define	SIZE_RX_Q	800   // size of Rx (up) signal FIFOs
#endif

#define	SIZE_TX_Q		800   // size of Tx (down) signal FIFOs
#define	SIZE_RELAY_BUF	        16000 // size of relay buffer in device driver

// default gains

#define DEF_RECORD_GAIN_OPCI_FXO    1.6
#define DEF_PLAY_GAIN_OPCI_FXO      3.4
#define DEF_RECORD_GAIN_OPCI_FXS   -2.0
#define DEF_PLAY_GAIN_OPCI_FXS     -4.0
#define DEF_RECORD_GAIN_V4PCI       6.0
#define DEF_PLAY_GAIN_V4PCI         7.0
#define DEF_RECORD_GAIN_OSW_FXO     3.0
#define DEF_PLAY_GAIN_OSW_FXO      12.0
#define DEF_RECORD_GAIN_OSW_FXS    -6.0
#define DEF_PLAY_GAIN_OSW_FXS       7.0
#define DEF_RECORD_GAIN_PRI         0.0
#define DEF_PLAY_GAIN_PRI           0.0

// default codec balance registers

#define DEF_BAL1                 0xc7
#define DEF_BAL2                 0x00
#define DEF_BAL3                 0x00


static int def_bal1 = DEF_BAL1;
static int def_bal2 = DEF_BAL2;
static int def_bal3 = DEF_BAL3;

static float defRecordGainDown = 20.0;	// DTMF CutThrough gain, <12 to enable


HostDSP::HostDSP( VPBREG &reg )
    : m_reg( reg )
{ //{{{
	// init message DSP FIFOs
	reg.dnmess = new HostFifo(reg.szmess);
	reg.upmess = new HostFifo(reg.szmess);

	reg.hostdsp = this;
} //}}}



static int GetVTCorePortCount( int board )
{ //{{{
	char node[128];
	char buf[32];
	int  channels = 0;

	snprintf(node, sizeof(node) - 1, "/proc/vt/board%u/maxports", board);
	if( ! GetProcfsInfo(node, buf, sizeof(buf)) )
		return 0;

	unsigned int maxports = strtoul(buf, NULL, 10);

	for( unsigned int i = 0; i < maxports; ++i )
	{
		snprintf(node, sizeof(node) - 1,
			 "/proc/vt/board%u/port%u/type", board, i);
		if( GetProcfsInfo(node, buf, sizeof(buf)) )
			++channels;
	}
	return channels;
} //}}}

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

	FUNCTION.: VPBRegister
	AUTHOR...: David Rowe
	DATE.....: 12/9/99

	Creates the VPB registry using compile-time constants.

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

VPBRegister::VPBRegister()
    : m_confdata( vpb_read_confll(&m_conf) )
{ //{{{
	int   *cards = NULL;

	// First off, read what we can from the config file(s).
	if( m_confdata && m_conf.verbosity == 1 )
		verbose(true);

	// Next, permit some options to be overridden by the environment.
	char *vpb_verbose = getenv("VPB_VERBOSE");
	if(vpb_verbose) {
		// if argument == 1, just switch on console logging,
		// otherwise use file logging
		if(strcmp(vpb_verbose, "1") == 0) {
			verbose(true);
		} else {
			verbose_file(true, vpb_verbose);
		}
	}

	// If there is no config file, the user must specify log cards explicitly
	// There is no facility to mix OpenLog and OpenLine cards this way, if
	// you want that, use a config file.
	char *env_model = getenv("VPB_MODEL");
	bool default_openlog = (env_model && strcmp(env_model, "V4LOG") == 0)
				? true : false;

	// VPB_COUNTRY is checked for in Comm::InitBoards, it needs an
	// initialised VPBRegister and Comm object to fully do its thing.

    //XXX
    #if 0
    //{{{
	// env variable for DTMF length in ms
	new_dtmf = getenv("VPB_DTMF");
	if (new_dtmf) {
		vpbdial_change_dtmf_length(atoi(new_dtmf), atoi(new_dtmf));
		mprintf("new dtmf on/off time = %s\n", new_dtmf);
	}

	// env DTMF cut through HW Record gain override
	new_recordgaindown = getenv("VPB_CUTTHROUGH");
	if (new_recordgaindown) {
		sscanf(new_recordgaindown, "%f", &defRecordGainDown);
		mprintf("Enabling Record Gain Down for Cut Through [%f]\n",
			defRecordGainDown);
	}
    //}}}
    #endif


	if( m_confdata ) {
		mprintf("Config files report:\n");
		mprintf("    VTCore cards   : %d\n", m_conf.vtcore.cards);
		mprintf("    OpenPri cards  : %d\n", m_conf.OpenPri.cards);
		mprintf("    OpenLine cards : %d\n", m_conf.OpenLine.cards);
		mprintf("    Total cards    : %d\n", m_conf.cardcount);
	}

	// If we didn't find any cards in the config file, scan the system
	// and configure some sensible defaults for all the cards we find.
	VTCORE_CARD::List   VTCoreCards;
	OL_CARD::List       OpenLineCards;

	if( ! m_conf.cardcount )
	{
		char buf[32];
		if( GetProcfsInfo("/proc/vt/maxboards", buf, sizeof(buf)) )
		{
			unsigned int maxboards = strtoul(buf, NULL, 10);
			for( unsigned int i = 0; i < maxboards; ++i )
			{
				char node[128];

				snprintf(node, sizeof(node) - 1,
					 "/proc/vt/board%u/name", i);
				if( ! GetProcfsInfo(node, buf, sizeof(buf)) )
					break;

				VTCORE_CARD  card = { int(i), 0, NULL };

				if( strncmp("OpenPCI", buf, 7) == 0 )
					card.type = VPB_OPCI;
				else if( strcmp("OpenSwitch12", buf) == 0 )
					card.type = VPB_OSW;
				else {
					mprintf("UNKNOWN card type '%s'\n", buf);
					continue;
				}
				VTCoreCards.push_back( card );
			}
			m_conf.cardcount += VTCoreCards.size();
		}

		int fd = open("/dev/vpb0", O_RDWR);
		if(fd != -1) {
			unsigned short  numol = 0;
			VPB_DATA        io    = { 0, &numol, 0, 0, 0 };

			ioctl(fd, VPB_IOC_PCI_GET_NUM_CARDS, &io);
			close(fd);

			//m_conf.cardcount += numol;

			//XXX If we are to use autodetction here we must preload
			//    the m_conf[] with card types.   The trouble is, we
			//    have no way of telling an OpenLine from an OpenLog,
			//    so just crap out here until the user makes a config
			//    file to tell us what they really have.

			throw VpbException("Found %d OpenLine/Log cards, but no config.\n"
					   "\tRun VpbConfigurator and answer its questions.",
					   numol);
		}

		//XXX Do something to detect PRI cards too
		mprintf("Detected %d cards\n", m_conf.cardcount);
	}
	if( ! m_conf.cardcount )
		throw VpbException("No VPB hardware detected, and none configured.\n"
				   "\tHave you built and loaded the VPB kernel driver?");

	m_reg.resize( m_conf.cardcount );
	for(unsigned int i=0; i < m_conf.cardcount; ++i)
	{
		VPBREG     &preg      = m_reg[i];
		CARD_LIST  &cardlist  = m_conf.cards[i];
		VPB_MODEL   loc_model = VPB_MODEL_UNKNOWN;

		preg.cardnum  = i;
		preg.hostdsp  = NULL;
		preg.cardinfo = NULL;
		preg.useconf  = false;

		if( m_confdata ) {
			preg.useconf = true;

			switch(cardlist.type)
			{
			    case VPB_PRI:
				loc_model = VPB_PRI;
				preg.cardinfo = &m_conf.OpenPri.card[cardlist.number];
				break;

			    case VPB_V4PCI:
				loc_model = VPB_V4PCI;
				preg.cardinfo = &m_conf.OpenLine.card[cardlist.number];
				break;

			    case VPB_V4LOG:
				loc_model = VPB_V4LOG;
				preg.cardinfo = &m_conf.OpenLine.card[cardlist.number];
				break;

			    case VPB_OPCI:
				loc_model = VPB_OPCI;
				preg.cardinfo = &m_conf.vtcore.card[cardlist.number];
				break;

			    case VPB_OSW:
				loc_model = VPB_OSW;
				preg.cardinfo = &m_conf.vtcore.card[cardlist.number];
				break;
			}
			mprintf("Config file says card %d is model %d\n",
				i, loc_model);
		}
		else if( i < VTCoreCards.size() ) {
			switch( VTCoreCards[i].type )
			{
			    case VPB_OPCI:
				loc_model = VPB_OPCI;
				break;

			    case VPB_OSW:
				loc_model = VPB_OSW;
				break;
			}
		}
		else if( i - VTCoreCards.size() < OpenLineCards.size() ) {
			loc_model = default_openlog ? VPB_V4LOG : VPB_V4PCI;
		}

		// XXX Don't do this for MAXCH, just for the number we need.
		//     stuff the per channel data in its own sub-structure,
		//     at least for the data that isn't shared with the DSP
		//     firmware.
		for(unsigned int j=0; j < MAXCH; ++j) {
			preg.listen_info[j].chan  = -1;
			preg.listen_info[j].board = -1;
			preg.szrxdf[j]            = SIZE_RX_Q;
			preg.sztxdf[j]            = SIZE_TX_Q;
			preg.rxdf[j]              = NULL;
			preg.txdf[j]              = NULL;
			preg.chstat[j]            = 0;
			preg.a_rxdf[j]            = 0;
			preg.a_txdf[j]            = 0;
		}

		switch(loc_model)
		{
		    case VPB_V4PCI:
			preg.numch         = 4;
			preg.szmess        = SIZE_MESS_Q_OL4;
			preg.base          = i;
			preg.model         = VPB_V4PCI;
			preg.ddmodel       = DD_PCI;
			preg.defRecordGain = DEF_RECORD_GAIN_V4PCI;
			preg.defPlayGain   = DEF_PLAY_GAIN_V4PCI;

			if( m_confdata ) preg.cardtypnum = cardlist.number;
			else             preg.cardtypnum = preg.cardnum;

			break;

		    case VPB_V4LOG:
			preg.numch         = 4;
			preg.szmess        = SIZE_MESS_Q_OL4;
			preg.base          = i;
			preg.model         = VPB_V4LOG;
			preg.ddmodel       = DD_PCI;
			preg.defRecordGain = DEF_RECORD_GAIN_V4PCI;
			preg.defPlayGain   = DEF_PLAY_GAIN_V4PCI;

			if( m_confdata ) preg.cardtypnum = cardlist.number;
			else             preg.cardtypnum = preg.cardnum;

			break;

		    case VPB_PRI:
			preg.numch = (m_conf.OpenPri.card[cardlist.number].if_type
				       == VPB_PRI_E1) ? 30 : 23;
			preg.szmess        = SIZE_MESS_Q_HDSP;
			preg.base          = i;
			preg.model         = VPB_PRI;
			preg.ddmodel       = DD_PRI;
			preg.defRecordGain = DEF_RECORD_GAIN_PRI;
			preg.defPlayGain   = DEF_PLAY_GAIN_PRI;
			preg.cardtypnum    = cardlist.number;
			break;

		    case VPB_OPCI:
			if( m_confdata ) preg.cardtypnum = cardlist.number;
			else             preg.cardtypnum = i;

			preg.numch          = GetVTCorePortCount( preg.cardtypnum );
			preg.szmess         = SIZE_MESS_Q_HDSP;
			preg.base           = i;
			preg.model          = loc_model;
			preg.ddmodel        = DD_VTCORE;
			preg.defRecordGain  = DEF_RECORD_GAIN_OPCI_FXO;
			preg.defPlayGain    = DEF_PLAY_GAIN_OPCI_FXO;
			preg.defSRecordGain = DEF_RECORD_GAIN_OPCI_FXS;
			preg.defSPlayGain   = DEF_PLAY_GAIN_OPCI_FXS;
			break;

		    case VPB_OSW:
			if( m_confdata ) preg.cardtypnum = cardlist.number;
			else             preg.cardtypnum = i;

			preg.numch          = GetVTCorePortCount( preg.cardtypnum );
			preg.szmess         = SIZE_MESS_Q_HDSP;
			preg.base           = i;
			preg.model          = loc_model;
			preg.ddmodel        = DD_VTCORE;
			preg.defRecordGain  = DEF_RECORD_GAIN_OSW_FXO;
			preg.defPlayGain    = DEF_PLAY_GAIN_OSW_FXO;
			preg.defSRecordGain = DEF_RECORD_GAIN_OSW_FXS;
			preg.defSPlayGain   = DEF_PLAY_GAIN_OSW_FXS;
			break;

		    default:
			throw VpbException("Invalid card type %d for card #%u",
								loc_model, i);
		}
		preg.lsf               = V4PCI_LSF;
		preg.defRecordGainDown = defRecordGainDown;

		mprintf("defRecordGain = %f\n", preg.defRecordGain);
		mprintf("defPlayGain   = %f\n", preg.defPlayGain);

		preg.defbal1    = def_bal1;
		preg.defbal2    = def_bal2;
		preg.defbal3    = def_bal3;

		// init ptrs to PC copy of FIFO state variables
		preg.dnmess = NULL;
		preg.upmess = NULL;

		// Initialize rest of structures
		preg.data     = 0;
		preg.a_dnmess = 0;
		preg.a_upmess = 0;

		for(unsigned int j=0; j < 12; ++j) preg.mdate[j]    = '\0';
		for(unsigned int j=0; j < 8; ++j)  preg.revision[j] = '\0';
		for(unsigned int j=0; j < 10; ++j) preg.serial_n[j] = '\0';

		preg.country.resize(preg.numch, NULL);
		preg.toneg.resize(preg.numch, NULL);
	}
	free(cards);
} //}}}

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

	FUNCTION.: ~VPBRegister
	AUTHOR...: John Kostogiannis
	DATE.....: 18/11/97

	Closes the VPB registry.

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

VPBRegister::~VPBRegister()
{
	conf_release_list( m_confdata );
}



