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

	FILE....: openpri.cpp
	AUTHOR..: Ben Kramer
	DATE....: 29/07/2004
	DESC....: Main part of the OpenPri driver


         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 <cmath>
#include <climits>
#include <assert.h>
#include <errno.h>
#include <pthread.h>

#include <cstdlib>
#include <cstring>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/if_wanpipe.h>

#include "openpri.h"
#include "hostvox.h"
#include "mess.h"
#include "mapdev.h"
#include "comm.h"
#include "wobbly.h"

#include "alawmulaw.h"
#include "dtmf.h"

extern "C" {
#include "toned.h"
#include "libpri/pri_q931.h"
#include "libpri/pri_q921.h"

// This one we include to get at the q931_call struct for slotmap support.
// Don't make a habit of using things from here, anything that is needed
// really should be made part of the public interface instead.
// ... but for now, leave libpri internals alone.
#include "libpri/pri_internal.h"
}


CONF_INFO * config_p;

typedef struct {
	int card;
	int channel;
} CON_THREAD_INFO;

struct cref_info {
	int            cref;
	VPB_CALL_INFO  cid;
	int            layer1;    // Encoding as from Call setup or for next call
	int            transcap;  // Transmit capabilities form Call setup or for next call
	cref_info     *next;
};



/* VPB Driver stuff */
#define ALAW_CODES	256
#define MAX_BOARDS	12
#define FRAME 		8
#define PRI_PORTS	32
//#define NBUF		160
//#define NBUF		80
//#define NBUF		40
//#define NBUF		16
#define NBUF		8
#define HOSTDSP_SLEEP	5


static int wanconnect(char *card, char *port);

static void prepare_rec_buf(unsigned char alawbuf[NBUF], VPBREG *v, int ch, int encoding);
static void prepare_rec_buf_log(unsigned char alawbuf[NBUF],
				unsigned char alawbuf2[NBUF],
				VPBREG *v, int ch, int encoding);
static void prepare_play_buf(unsigned char alawbuf[NBUF], VPBREG *v, int ch, int encoding);
static void toned_callback(word mess[],void *board);
static int findfreechan(VPBREG *v);

static void *audio_thread_multi(void *pv);
static void *audio_thread_log_multi(void *pv);


/* libpri stuff */
#define MAX_CHAN		32
#define	DCHANNEL_TIMESLOT	16

/* Channel state defines */

#define CH_IDLE		0
#define CH_RING		1
#define CH_PROCEEDING	2
#define CH_RINGING	3
#define CH_SETUP	4
#define CH_NEEDANS	5
#define CH_NEEDHANG	6
#define CH_HUNGEDUP	7
#define CH_NEEDRING	8
#define CH_TERMINATING	9
#define CH_CONNECTED	10
#define CH_NEEDTERMINATING	11
#define CH_NEEDONHK	12


static void set_callinfo(struct pri_chan &chan, const struct cref_info &cref)
{ //{{{
	chan.layer1           = cref.layer1;
	chan.transcap         = cref.transcap;
	chan.cid->callingplan = cref.cid.callingplan;
	chan.cid->callingpres = cref.cid.callingpres;
	chan.cid->calledplan  = cref.cid.calledplan;
	strcpy(chan.cid->callingnum,     cref.cid.callingnum);
	strcpy(chan.cid->callingname,    cref.cid.callingname);
	strcpy(chan.cid->callingsubaddr, cref.cid.callingsubaddr);
	strcpy(chan.cid->calledsubaddr,  cref.cid.calledsubaddr);
	strcpy(chan.cid->callednum,      cref.cid.callednum);
} //}}}

static void set_callinfo(struct pri_chan &chan, const pri_event_ring &ring_evt)
{ //{{{
	chan.cref             = ring_evt.cref;
	chan.layer1           = ring_evt.layer1;
	chan.transcap         = ring_evt.ctype;
	chan.cid->callingplan = ring_evt.callingplan;
	chan.cid->callingpres = ring_evt.callingpres;
	chan.cid->calledplan  = ring_evt.calledplan;
	strcpy(chan.cid->callingnum,     ring_evt.callingnum);
	strcpy(chan.cid->callingname,    ring_evt.callingname);
	strcpy(chan.cid->callingsubaddr, ring_evt.callingsubaddr);
	strcpy(chan.cid->calledsubaddr,  ring_evt.calledsubaddr);
	strcpy(chan.cid->callednum,      ring_evt.callednum);
} //}}}

static void set_callinfo(struct cref_info &cref, const pri_event_ring &ring_evt)
{ //{{{
	cref.cref            = ring_evt.cref;
	cref.layer1          = ring_evt.layer1;
	cref.transcap        = ring_evt.ctype;
	cref.cid.callingplan = ring_evt.callingplan;
	cref.cid.callingpres = ring_evt.callingpres;
	cref.cid.calledplan  = ring_evt.calledplan;
	strcpy(cref.cid.callingnum,     ring_evt.callingnum);
	strcpy(cref.cid.callingname,    ring_evt.callingname);
	strcpy(cref.cid.callingsubaddr, ring_evt.callingsubaddr);
	strcpy(cref.cid.calledsubaddr,  ring_evt.calledsubaddr);
	strcpy(cref.cid.callednum,      ring_evt.callednum);
} //}}}

void OpenPri::HostDSPOpen()
{ //{{{
	mprintf("[%d/-] Initialising PRI Host DSP ...\n", m_reg.cardnum);

	OP_CARD  *cardinfo  = (OP_CARD*)m_reg.cardinfo;
	int       dchan     = cardinfo->d_channel;
	int       islog     = 0;
	int       isdn_num  = 0;
	int       iface_num = 0;
	int       numch     = m_reg.numch;
	pri_chan *chans     = new pri_chan[numch];
	int   x;

	m_audio_multi = 0;
	m_reg.chans = chans;
	m_reg.toned = new TD*[numch];
	pthread_mutex_init(&m_mutex, NULL);

	if (cardinfo->node == ISDN_NODE_LOG) {
		islog = 1;
		mprintf("[%d/-] configured for logging\n", m_reg.cardnum);
	}

	mprintf("[%d/-] card has %d channels\n", m_reg.cardnum,m_reg.numch);
	for(int i=0 ; i < numch; ++i) {
		++iface_num;
//		mprintf("[%d/%d] Starting channel indexed %d ...\n",m_reg.cardnum,isdn_num,i);
		m_reg.toneg[i] = new HostToneGen;
		toned_open(&m_reg.toned[i], i , &toned_callback, &m_reg);

		/* setup chans structure */
		chans[i].state = CH_IDLE;
		chans[i].run = 0;
		chans[i].dtmf_dec = dtmf_init();
		chans[i].chan_num = isdn_num;	// Channel number (not true ISDN as doesnt map above d-chan)
		chans[i].cref = 0;
		chans[i].vox_states = NULL;

            #if defined(CASLOG) || defined(SOFTBRIDGE)
		hostvox_open(&chans[i].vox_states,i);
            #endif
            #ifdef CASLOG
		chans[i].run = 1;
            #endif
		if (iface_num >= dchan){
			chans[i].iface = &cardinfo->iface[iface_num+1];
		}
		else {
			chans[i].iface = &cardinfo->iface[iface_num];
		}
		chans[i].encoding = chans[i].iface->encoding;
		chans[i].layer1 = (chans[i].iface->encoding == VPB_MULAW ? PRI_LAYER_1_ULAW : PRI_LAYER_1_ALAW);
		chans[i].cause = 16;	// Set it as normal clear down
		chans[i].cid = NULL;
		chans[i].v = &m_reg;
		chans[i].call = NULL;
		chans[i].tx_fifo = NULL;
		chans[i].linear_play_gain = pow(10.0, m_reg.defPlayGain / 20.0);
		chans[i].linear_record_gain = pow(10.0, m_reg.defRecordGain / 20.0);

//		mprintf("Started channel [%d] on board [%d] with handle [%d] indexed [%d]\n",
//			chans[i].chan_num, m_reg.cardnum, chans[i].hand_num, i);
//		mprintf("[%d/%d] Started channel with handle [%d] indexed [%d]\n",
//			m_reg.cardnum, chans[i].chan_num, chans[i].hand_num, i);
		mprintf("[%d/%d] Started ISDN channel ...\n",m_reg.cardnum,isdn_num);
		++isdn_num;
	}

	// Set up audio buffers first
	OpenPorts();

	// Check if we are running audio_thread_multi
	// run through all channels and compare the interfaces,
	// counting the number of audio_thread_multi required
	m_audio_multi = 1;
	for(int i=0 ; i < numch-1; ++i) {
		if (strcmp((char*)(chans[i].iface->name),(char*)(chans[i+1].iface->name))){
			mprintf("Audio Multi [%d] [%s] != [%s]\n", m_audio_multi,
				chans[i].iface->name,chans[i+1].iface->name);
			++m_audio_multi;
		}
	}
	m_audio_multi_info = new AUDIO_THREAD_MULTI[m_audio_multi];
	x = 0;
	m_audio_multi_info[x].start_channel = 0;
	m_audio_multi_info[x].end_channel   = 0;
	m_audio_multi_info[x].v             = &m_reg;
	m_audio_multi_info[x].card          = chans[0].iface->card;
	m_audio_multi_info[x].interface     = chans[0].iface->name;
	int i = 0;
	for(; i < (numch-1) && x < m_audio_multi; ++i) {
		if (strcmp((char*)(chans[i].iface->name),(char*)(chans[i+1].iface->name))){
			m_audio_multi_info[x].end_channel=i;
			// Start up the audio thread
			pthread_create(&m_audiothread[x], NULL,
				       islog ? audio_thread_log_multi : audio_thread_multi,
				       &m_audio_multi_info[x]);
			mprintf("[%d/-] Started Audio Thread Multi [%d]...\n",m_reg.cardnum,x);

			// prepare for next audio thread if there is one
			if( ++x < m_audio_multi ) {
				m_audio_multi_info[x].start_channel = i+1;
				m_audio_multi_info[x].end_channel   = i+1;
				m_audio_multi_info[x].v             = &m_reg;
				m_audio_multi_info[x].card      = chans[i+1].iface->card;
				m_audio_multi_info[x].interface = chans[i+1].iface->name;
			}
		}
	}
	// Start up the last audio thread
	m_audio_multi_info[x].end_channel = i;
	pthread_create(&m_audiothread[x], NULL,
		       islog ? audio_thread_log_multi : audio_thread_multi,
		       &m_audio_multi_info[x]);
	mprintf("[%d/-] Started Audio Thread Multi [%d]...\n",m_reg.cardnum,x);

	int ret = pthread_create(&m_dspthread, NULL, SimulateDSP, this);
	if( ret )
		mprintf("[%d/-] FAILED to start DSP thread: %s\n",
			m_reg.cardnum, strerror(ret));

    #ifndef CASLOG
	ret = pthread_create(&m_dchanthread, NULL,
			     islog ? RunDchannelLog : RunDchannel, this);
	if( ret )
		mprintf("[%d/-] FAILED to start D channel thread: %s\n",
			m_reg.cardnum, strerror(ret));
    #endif
} //}}}

void OpenPri::HostDSPClose()
{ //{{{
	pri_chan *chans = (pri_chan*)m_reg.chans;
	int cardnum = m_reg.cardnum;

	mprintf("[%d/-] Closing Pri Host DSP...\n",cardnum);
    #ifdef CASLOG
	pthread_cancel(m_dspthread);
	pthread_join(m_dspthread, NULL);
    #else
	pthread_cancel(m_dspthread);
	pthread_cancel(m_dchanthread);
	pthread_join(m_dspthread, NULL);
	pthread_join(m_dchanthread, NULL);
    #endif

	for(int i = 0; i < m_audio_multi; ++i) {
		pthread_cancel(m_audiothread[i]);
		pthread_join(m_audiothread[i], NULL);
	}

	mprintf("[%d/-] Closing down ports...\n",cardnum);
	ClosePorts();

	mprintf("[%d/-] Releasing per channel data structures...\n",cardnum);
	pthread_mutex_lock(&m_mutex);
	for(int i=0; i < m_reg.numch; ++i) {
		dtmf_close(chans[i].dtmf_dec);
		delete m_reg.toneg[i];
		toned_close(m_reg.toned[i]);

		chans[i].state = CH_IDLE;
		//mprintf("[%d/%d] c = %p\n", cardnum, i, chans[i].call);
		pri_destroycall(m_pri, chans[i].call);
		q931_destroycall(m_pri2, chans[i].cref);
		chans[i].call = NULL;

		delete chans[i].cid;
		chans[i].cid = NULL;
	}
	pthread_mutex_unlock(&m_mutex);
	pthread_mutex_destroy(&m_mutex);

	mprintf("[%d/-] Freeing OpenPri structures...\n",cardnum);
	if( m_pri )  q921_discard_retransmissions(m_pri);
	if( m_pri2 ) q921_discard_retransmissions(m_pri2);

	delete [] m_reg.toned;         m_reg.toned = NULL;
	delete [] chans;               chans = NULL;
	delete [] m_audio_multi_info;  m_audio_multi_info = NULL;

	mprintf("[%d/-] Closed Pri Host DSP!\n",cardnum);
} //}}}

void OpenPri::OpenPorts()
{ //{{{
	int cardnum = m_reg.cardnum;

	for(int i=0; i < m_reg.numch; ++i) {
		// init DSP (audio) FIFOs
		m_reg.rxdf[i] = new HostFifo(m_reg.szrxdf[i]);
		//mprintf("record side writing [%02d] 0x%x\n", i, (int)v->rxdf[i]);
		m_reg.txdf[i] = new HostFifo(m_reg.sztxdf[i]);
		//mprintf("play side reading [%02d] 0x%x\n", ch, (int)v->txdf[i]);
		mprintf("[%d/%d] DSP Audio FIFOs opened OK\n",cardnum,i);
	}
} //}}}

void OpenPri::ClosePorts()
{ //{{{
	for(int i = 0; i < m_reg.numch; ++i) {
		delete m_reg.rxdf[i]; m_reg.rxdf[i] = NULL;
		delete m_reg.txdf[i]; m_reg.txdf[i] = NULL;
	}
} //}}}

// This emulates the hardware gain as per the other cards
// (and is a clone of the software gain_vector function from playrec.cpp)
// This is applied to all signals, unlike the software gain which only is
// applied to samples processed by vpb_{play,record} functions.
static inline void hw_gain_vector(float g, short *v, int n)
{ //{{{
	// This is the maximum scale factor that lets us safely use
	// an int instead of a float as our temporary computed value.
	const unsigned int scalemax = INT_MAX / 32768;

	if( g > scalemax ) g = scalemax;
	for(short *end = v + n; v != end; ++v)
	{
		int tmp = lrintf(*v * g);
		if(__builtin_expect(tmp > 32767,0))       *v = 32767;
		else if(__builtin_expect(tmp < -32768,0)) *v = -32768;
		else                                      *v = tmp;
	}
} //}}}

// Reads bufs of play samples, and formats them ready for playing.
// Also iterates tone generation.
void prepare_play_buf(unsigned char alawbuf[NBUF], VPBREG *v, int ch, int encoding)
{ //{{{
	int	retread;
	int16_t	linbuf[NBUF];
	pri_chan *chans = (pri_chan *)v->chans;
	//OpenPri* pThis = (OpenPri *)v->hostdsp;
	int idx;

	try {
		idx = get_idx_from_ch(v,ch);
	} catch ( const Wobbly &w ) {
		mprintf("prepare_play_buf: Can't get IDX from ch[%d]!\n",ch);
		return;
	}

	// Read play fifos, add in tone, convert to alaw
	memset(linbuf, 0, sizeof(linbuf));
	retread = v->txdf[ch]->Read( (uint16_t*)linbuf, NBUF);

	//if(retread) mprintf("prepare_play_buf: FIFO empty on channel[%d]!\n",ch);

	//XXX Do we want to do this _after_ the tone is mixed in?
	//    That could be a little hard given we don't know the encoding.
	if(VPB_RAW == encoding){
		for(int i=0; i < NBUF; ++i) alawbuf[i] = linbuf[i];
		return;
	}

	//mprintf("Playing tone....");
	HostToneGen *tonegen = dynamic_cast<HostToneGen*>(v->toneg[ch]);
	if(tonegen) tonegen->MixTone( linbuf, NBUF );

	/* Apply the 'hardware' gain */
	hw_gain_vector(chans[idx].linear_play_gain, linbuf, NBUF);

	/* Needs to check if we are alaw or mu-law */
	if (encoding == VPB_ALAW){
		alaw_encode((char *)&alawbuf[0], linbuf, NBUF);
	}
	else if (encoding == VPB_MULAW){
		mulaw_encode((char *)&alawbuf[0], linbuf, NBUF);
	}
	else {
		mprintf("[%d/%d] Encoding format[%d] not supported!\n",
			v->cardnum,chans[idx].chan_num,encoding);
	}
} //}}}

// Transfers buffers of recorded samples to the driver FIFOs.
// Also runs DTMF decoders on each channel.
void prepare_rec_buf(unsigned char alawbuf[NBUF], VPBREG *v, int ch, int encoding)
{ //{{{
	int	digits_ret;
	int16_t	linbuf[NBUF];
	char	key[2];
	word	mess[COMM_MAX_MESSDSPPC];
	int	i,ret;
	int	keydown=0;
	//OpenPri *pThis = (OpenPri *)v->hostdsp;
	pri_chan *chans = (pri_chan *)v->chans;
	int idx;

	try {
		idx = get_idx_from_ch(v,ch);
	} catch ( const Wobbly &w ) {
		mprintf("prepare_rec_bug: Cant work out IDX from ch[%d]!\n",ch);
		return;
	}

	if(VPB_RAW == encoding){
		/* Just write it to the buffer */
		for(i=0;i<NBUF;i++){
			linbuf[i]=alawbuf[i];
		}
		v->rxdf[ch]->Write( (uint16_t*)linbuf, NBUF);
		if (chans[idx].tx_fifo != NULL){
			chans[idx].tx_fifo->Write( (uint16_t*)linbuf, NBUF);
		}
		return;
	}
	// convert to linear and send to fifos
	/* Needs to check if we are alaw or mu-law !! */
	if (encoding == VPB_ALAW){
		alaw_decode(linbuf, alawbuf, NBUF);
	}
	else if (encoding == VPB_MULAW){
		mulaw_decode(linbuf, alawbuf, NBUF);
	}
	else {
		mprintf("[%d/%d] Encoding format[%d] not supported!\n",
			v->cardnum,chans[idx].chan_num,encoding);
	}

	/* Apply the Hardware gain */
	hw_gain_vector(chans[idx].linear_record_gain, linbuf, NBUF);

#ifdef SOFTBRIDGE
	if (chans[ch].vox_states) {
		hostvox_analyse(chans[ch].vox_states, linbuf, NBUF);
	}
#endif

	/* for bridging and loop back  */
	if (chans[idx].tx_fifo != NULL){
		chans[idx].tx_fifo->Write( (uint16_t*)linbuf, NBUF);
	}

	/* write it to the buffer */
#ifdef SOFTBRIDGE
	if (v->rxdf[ch]->HowFull() > DROP_AUDIO_THRESHOLD) {
		// drop samples during silence if the buffer gets too big
		if (chans[ch].vox_states && hostvox_energy(chans[ch].vox_states) < 3000000)
			v->rxdf[ch]->Write( ((uint16_t*)linbuf) + 1, NBUF - 1);
		else
			v->rxdf[ch]->Write( (uint16_t*)linbuf, NBUF);
	} else {
		v->rxdf[ch]->Write( (uint16_t*)linbuf, NBUF);
	}
#else
	v->rxdf[ch]->Write( (uint16_t*)linbuf, NBUF);
#endif
#ifdef CASLOG
	if (chans[idx].vox_states){
		ret = hostvox_analyse(chans[idx].vox_states,linbuf,NBUF);
		if (ret == 1){ // Vox OFF
			mess[0] = DSP_LVOXOFF;
			mess[1] = DSP_VOXOFF;
			mess[2] = chans[idx].chan_num;
			ret = v->upmess->Write(mess, mess[0]);
			mprintf("ch = %d bd = %d VOX OFF\n", ch, v->cardnum);
		}
		else if (ret ==2){ // Vox ON
			mess[0] = DSP_LVOXON;
			mess[1] = DSP_VOXON;
			mess[2] = chans[idx].chan_num;
			ret = v->upmess->Write(mess, mess[0]);
			mprintf("ch = %d bd = %d VOX ON\n", ch, v->cardnum);
		}
	}
#endif
	// DTMF processing
	digits_ret = dtmf_decoder(chans[idx].dtmf_dec, key, 2, linbuf, NBUF, &keydown);
	if (digits_ret) {
		// it should be impossible to detect > 1 digit in NBUF sample buffer
		//assert(digits_ret == 1);
//		mprintf("ch = %d bd = %d DTMF[%d].....\n", chans[idx].chan_num, v->cardnum,key[0]);
		mess[0] = DSP_LDTMF;
		mess[1] = DSP_DTMF;
		mess[2] = chans[idx].chan_num;
		mess[3] = key[0];
		ret = v->upmess->Write(mess, mess[0]);
	}
	else if (keydown){
		mess[0] = DSP_LDTMF_DOWN;
		mess[1] = DSP_DTMF_DOWN;
		mess[2] = chans[idx].chan_num;
		mess[3] = keydown;
		ret = v->upmess->Write(mess, mess[0]);
	}

	// tone decoder processing
	toned_analyse(v->toned[idx], linbuf, NBUF);
} //}}}

// Transfers buffers of recorded samples to the driver FIFOs.
// Also runs DTMF decoders on each channel. Takes the two audio streams
// from the audio log thread.
void prepare_rec_buf_log(unsigned char alawbuf[NBUF],
			 unsigned char alawbuf2[NBUF],
			 VPBREG *v, int ch, int encoding)
{ //{{{
	int16_t	 linbuf[NBUF];
	int16_t	 linbuf2[NBUF];
	int	 sumbuf;
	uint16_t mess[COMM_MAX_MESSDSPPC];
	int	 i,ret;
#ifndef CASLOG
	int	 digits_ret;
	char	 key[2];
	int	 keydown=0;
#endif
	//OpenPri *pThis = (OpenPri *)v->hostdsp;
	pri_chan *chans = (pri_chan *)v->chans;
	int idx;

	try {
		idx = get_idx_from_ch(v,ch);
	} catch ( const Wobbly &w ) {
		mprintf("prepare_rec_bug_log: Cant work out IDX from ch[%d]!\n",ch);
		return;
	}

	if(VPB_RAW == encoding){
		/* Just write it to the buffer */
		for(i=0;i<NBUF;i++){
			linbuf[i]=alawbuf[i] + alawbuf2[i];
		}
		v->rxdf[ch]->Write( (uint16_t*)linbuf, NBUF);
		if (chans[idx].tx_fifo != NULL){
			chans[idx].tx_fifo->Write( (uint16_t*)linbuf, NBUF);
		}
		return;
	}
	// convert to linear and send to fifos
	/* Needs to check if we are alaw or mu-law !! */
	if (encoding == VPB_ALAW){
		alaw_decode(linbuf, alawbuf, NBUF);
		alaw_decode(linbuf2, alawbuf2, NBUF);
	}
	else if (encoding == VPB_MULAW){
		mulaw_decode(linbuf, alawbuf, NBUF);
		mulaw_decode(linbuf2, alawbuf2, NBUF);
	}
	else {
		mprintf("[%d/%d] Encoding format[%d] not supported!\n",
			v->cardnum,chans[idx].chan_num,encoding);
	}
	/* Converge audio streams */
	for(i=0;i<NBUF;i++){
		sumbuf=linbuf[i] + linbuf2[i];
		if (sumbuf > 32767){
			sumbuf=32767;
		} else if (sumbuf < -32768){
			sumbuf=-32768;
		}
		linbuf[i]=sumbuf;
	}

    #ifndef CASLOG
	// See how much difference it makes
	// In past, gain of 1.12 was used....not worth it??
	// 
	/* Apply the Hardware gain */
	hw_gain_vector(chans[idx].linear_record_gain, linbuf, NBUF);
    #endif

	/* write it to the buffer */
	v->rxdf[ch]->Write( (uint16_t*)linbuf, NBUF);

    #ifndef CASLOG
	// DTMF processing  ... not for cas loggers
	digits_ret = dtmf_decoder(chans[idx].dtmf_dec, key, 2, linbuf, NBUF, &keydown);
	if (digits_ret) {
		// it should be impossible to detect > 1 digit in NBUF sample buffer
		//assert(digits_ret == 1);
//		mprintf("ch = %d bd = %d DTMF[%d].....\n", chans[idx].chan_num, v->cardnum,key[0]);
		mess[0] = DSP_LDTMF;
		mess[1] = DSP_DTMF;
		mess[2] = chans[idx].chan_num;
		mess[3] = key[0];
		ret = v->upmess->Write(mess, mess[0]);
	}
	else if (keydown){
		mess[0] = DSP_LDTMF_DOWN;
		mess[1] = DSP_DTMF_DOWN;
		mess[2] = chans[idx].chan_num;
		mess[3] = keydown;
		ret = v->upmess->Write(mess, mess[0]);
	}
    #endif

	if (chans[idx].vox_states){
		ret = hostvox_analyse(chans[idx].vox_states,linbuf,NBUF);
		if (ret == 1){ // Vox OFF
			mess[0] = DSP_LVOXOFF;
			mess[1] = DSP_VOXOFF;
			mess[2] = chans[idx].chan_num;
			ret = v->upmess->Write(mess, mess[0]);
			mprintf("ch = %d bd = %d VOX OFF\n", ch, v->cardnum);
		}
		else if (ret ==2){ // Vox ON
			mess[0] = DSP_LVOXON;
			mess[1] = DSP_VOXON;
			mess[2] = chans[idx].chan_num;
			ret = v->upmess->Write(mess, mess[0]);
			mprintf("ch = %d bd = %d VOX ON\n", ch, v->cardnum);
		}
	}

    #ifndef CASLOG
	// tone decoder processing...not for cas loggers
	toned_analyse(v->toned[idx], linbuf, NBUF);
    #endif
} //}}}

// This function is called by the tone detector when a tone is detected
// or a tone detector log message is generated.
void toned_callback(word mess[],void *board)
{ //{{{
	VPBREG *vr = (VPBREG *)board;

	// convert from  idx number to ch

//	mprintf("Got Tone[%d] from[%d]\n",mess[3],mess[2]);
	int idx = mess[2];
	pri_chan *chans;
	chans = (pri_chan *)vr->chans;

	mess[2] = chans[idx].chan_num;
	vr->upmess->Write(mess, mess[0]);
} //}}}

// Simulates the DSP on host processing cards:
//  - polls card for signalling events
//  - audio signal I/O is handled by different threads
void *OpenPri::SimulateDSP(void *p)
{ //{{{
	OpenPri* openpri = (OpenPri*)p;

	mprintf("[%d/-] Starting PRI SimulateDSP\n", openpri->m_reg.cardnum);
	for(;;) {
		if(openpri->ProcessCommands()) GenericSleep(HOSTDSP_SLEEP);
		else                           pthread_testcancel();
	}
	return NULL;
} //}}}

// Reads commands sent to "DSP" and processes them.
bool OpenPri::ProcessCommands()
{ //{{{
	word	  mess[COMM_MAX_MESSPCDSP];
	word	  rmess[COMM_MAX_MESSDSPPC];
	pri_chan *chans    = (pri_chan *)m_reg.chans;
	OP_CARD  *cardinfo = (OP_CARD *)m_reg.cardinfo;
	int       cardnum  = m_reg.cardnum;
	int	  ret;
	VPB_CALL_INFO	*tmpp;

	if( m_reg.dnmess->Read(&mess[0], 1) == Fifo::EMPTY )
		return true;  //DSP idle

	if( m_reg.dnmess->Read(&mess[1], mess[0]-1) != Fifo::OK ) {
		mprintf("[%d/-] Error on second read of fifo!\n",cardnum);
		return true;
	}
	if( mess[0] < 3 ) {
		mprintf("[%d/-] Ignoring short fifo message %d < 3 [%#x]\n",
			cardnum, mess[0], mess[1]);
		return true;
	}

	int ch  = mess[2];
	int idx = -1;
	if( mess[1] != PC_TONEG ){
	    try {
		idx = get_idx_from_ch(&m_reg, ch);
	    } catch ( const Wobbly &w ) {
		mprintf("priProcessCommands: Cant work out IDX from ch[%d][%d][%d]!\n",
			mess[0],mess[1],mess[2]);
		return false;
	    }
	}
	//mprintf("message bytes[%d]: on ch[%d]\n",mess[0],mess[2]);

	if( mess[1] == PC_VOX_UPDATE ) {
		assert(mess[0] == PC_LVOX_UPDATE);
		if(chans[idx].vox_states != NULL){
			hostvox_update_levels(chans[idx].vox_states, mess);
			//mprintf("[%d/-]Updating vox information\n",cardnum);
		} else  mprintf("[%d/-]No vox_states !\n",cardnum);
	}

	// If this is CAS, or we are in logging mode,
	// then don't process any other 'dsp' messages.
    #ifndef CASLOG
	if( cardinfo->node == ISDN_NODE_LOG )
    #endif
		return false;


	switch(mess[1]) {
	    case PC_CODEC_ONHK:
		assert(mess[0] == PC_LCODEC_ONHK);
		pthread_mutex_lock(&m_mutex);
		if (chans[idx].state != CH_IDLE){
			mprintf("[%d/%d] Hanging up call\n",cardnum,chans[idx].chan_num);
			/* Stop Audio Thread */
			chans[idx].run=0;
			/* send Pri Message */
			ret = pri_hangup(m_pri,chans[idx].call,PRI_CAUSE_NORMAL_CLEARING);
			if (ret != 0){
				mprintf("[%d/%d] Error hanging up call\n",
					cardnum,chans[idx].chan_num);
			}
			/* Clear out caller ID information */
			delete chans[idx].cid;
			chans[idx].cid = NULL;

			pri_destroycall(m_pri,chans[idx].call);
			chans[idx].call = NULL;
			chans[idx].state = CH_IDLE;
		}
		pthread_mutex_unlock(&m_mutex);
		//else mprintf("[%d/%d] Hanging up call - nothing to do, IDLE\n",
		//	       cardnum,chans[idx].chan_num);
		break;

	    case PC_CODEC_OFFHK:
		assert(mess[0] == PC_LCODEC_OFFHK);
		ret = -1;
		pthread_mutex_lock(&m_mutex);
		if (chans[idx].state == CH_RING){
			mprintf("[%d/%d] Answering call \n",cardnum,chans[idx].chan_num);
			/* send pri message */
			ret = pri_answer(m_pri,chans[idx].call,chans[idx].iface->channel,0);
			if (ret == 0) {
				chans[idx].state = CH_CONNECTED;
				/* start audio thread */
				chans[idx].run=1;
				//pthread_create(&audio_thread_t[idx], NULL,
				//		 audio_thread,(void*)&chans[idx]);
			} else {
				mprintf("[%d/%d] Failed to  answer call!\n",
					cardnum,chans[idx].chan_num);
				rmess[0] = DSP_LISDN_ANS_FAIL;
				rmess[1] = DSP_ISDN_ANS_FAIL;
				rmess[2] = chans[idx].chan_num;
				rmess[3] = ret;
				m_reg.upmess->Write(rmess, rmess[0]);
			}
		} else {
			mprintf("[%d/%d]Cant answer call",cardnum,chans[idx].chan_num);
			mprintf(" not in ring state (state[%d])\n",chans[idx].state);
		}
		pthread_mutex_unlock(&m_mutex);
		break;

	    case PC_SPI_BRIDGE:
		assert(mess[0] == PC_LSPI_BRIDGE);
		chans[idx].tx_fifo = m_reg.txdf[mess[3]];
		try {
			idx=get_idx_from_ch(&m_reg, mess[3]);
		} catch ( const Wobbly &w ) {
			mprintf("priProcessCommands:PC_SPI_BRIDGE Can't get IDX from ch[%d]!\n",
				mess[3]);
			return false;
		}
		chans[idx].tx_fifo = m_reg.txdf[ch];
		break;

	    case PC_SPI_BRIDGE_OFF:
		assert(mess[0] == PC_LSPI_BRIDGE_OFF);
		chans[idx].tx_fifo = NULL;
		try {
			idx=get_idx_from_ch(&m_reg, mess[3]);
		} catch ( const Wobbly &w ) {
			mprintf("priProcessCommands:PC_SPI_BRIDGE_OFF Can't get IDX from ch[%d]!\n",
				mess[3]);
			return false;
		}
		chans[idx].tx_fifo = NULL;
		break;

	    case PC_SPI_SBRIDGE:
		assert(mess[0] == PC_LSPI_SBRIDGE);
		chans[idx].tx_fifo = m_reg.txdf[mess[4]];
		break;

	    case PC_SPI_SBRIDGE_OFF:
		assert(mess[0] == PC_LSPI_SBRIDGE_OFF);
		chans[idx].tx_fifo = NULL;
		break;

/*
	    case PC_CONF_JOIN:
		assert(mess[0] == PC_LCONF_JOIN);
		hdaports[offset + mess[2]].conf_join(mess[3]);
		break;

	    case PC_CONF_LEAVE:
		assert(mess[0] == PC_LCONF_LEAVE);
		hdaports[offset + mess[2]].conf_leave();
		break;
*/

	    case PC_CODEC_RECGAIN:
		assert(mess[0] == PC_LCODEC_RECGAIN);
		chans[idx].linear_record_gain = pow(10.0, ((int)mess[3] - 0x80) / 200.0);
		break;

	    case PC_CODEC_PLAYGAIN:
		assert(mess[0] == PC_LCODEC_PLAYGAIN);
		chans[idx].linear_play_gain = pow(10.0, ((int)mess[3] - 0x80) / 200.0);
		break;

	  #if 0
	    // We don't expect to receive these two any more,
	    // the tone generator is called directly instead.
	    case PC_TONEG:
		assert(mess[0] == PC_LTONEG);
		try {
			idx = get_idx_from_ch(&m_reg, mess[12]);
		} catch ( const Wobbly &w ) {
			mprintf("priProcessCommands:PC_TONEG Cant work out IDX from ch[%d]!\n",
				mess[12]);
			return false;
		}
		mprintf("[%d/%d] Starting Tone generation idx(%d)\n",
			cardnum,mess[12],idx);
		toneg_start(m_reg.toneg[idx], mess);
		break;

	    case PC_TONEG_RESET:
		//mprintf("[%d/%d] Reseting Tone generation\n",cardnum,ch);
		toneg_reset(m_reg.toneg[idx]);
		break;
	  #endif

	    case PC_CODEC_BREAK:
		mprintf("[%d/-]We dont FLASH on PRI\n",cardnum);
		m_reg.toneg[ch]->SignalCompletion();
		break;

	    case PC_LOOPBACK_ON:
		assert(mess[0] == PC_LLOOPBACK_ON);
		chans[idx].tx_fifo = m_reg.txdf[ch];
		mprintf("[%d/%02d] LoopBack On!\n",cardnum,ch);
		break;

	    case PC_LOOPBACK_OFF:
		assert(mess[0] == PC_LLOOPBACK_OFF);
		chans[idx].tx_fifo = NULL;
		mprintf("[%d/%02d] LoopBack Off!\n",cardnum,ch);
		break;

	  #if 0
	    case PC_HOSTECHO_ON:
		assert(mess[0] == PC_LHOSTECHO_ON);
		if (chans[idx].echo_enable == 0){
			chans[idx].echo_enable = 1;
			mprintf("[%d/%02d] Echo cancelor enabled \n",cardnum,ch);
		}
		break;

	    case PC_HOSTECHO_OFF:
		assert(mess[0] == PC_LHOSTECHO_OFF);
		if (chans[idx].echo_enable > 0){
			chans[idx].echo_enable = 0;
			mprintf("[%d/%02d] Echo cancelor disabled \n",cardnum,ch);
		}
		break;

	    case PC_HOSTECHO_OPT:
		assert(mess[0] == PC_LHOSTECHO_OFF);
		/* none at the moment! */
		mprintf("[%d/%02d] Echo cancelor - no optimization at the moment \n",
			cardnum,ch);
		break;
	  #endif

	    case PC_TONED_ST:
		// call progress detector messages 
		toned_add_tone(m_reg.toned[ch], mess);
		break;

	    case PC_TONED_ST_DEL:
		toned_del_tone(m_reg.toned[ch], mess);
		break;

	  #if 0
	    case PC_CODEC_GEN:  	
		// general purpose word sent to codec config reg
		id = offset + mess[2];
		//printf("set codec reg ch %d  reg 0x%x val 0x%x\n", id, mess[3], mess[4]);
		hdaports[id].set_codec_reg(mess[3], mess[4]);
		break;
	  #endif

	    case PC_TONED_DEBUG:
		assert(mess[0] == PC_LTONED_DEBUG);
		toned_start_logging(m_reg.toned[ch], mess);
		break;

	    case PC_ISDN_CALL:
		mprintf("[%d/%d] Making an ISDN call!\n",
			cardnum,chans[idx].chan_num);
		ret = make_isdn_call(mess);
		if(ret){
			rmess[0] = DSP_LISDN_CALL_FAIL;
			rmess[1] = DSP_ISDN_CALL_FAIL;
			rmess[2] = chans[idx].chan_num;
			rmess[3] = ret;
			mprintf("[%d/%d] ISDN CALL Failed (%d)\n",
				cardnum,chans[idx].chan_num,ret);
			m_reg.upmess->Write(rmess, rmess[0]);
		}
		break;

	    case PC_ISDN_GET_CINFO:
		mprintf("[%d/%d]Getting Calling Number\n",
			cardnum,chans[idx].chan_num);
		rmess[0] = DSP_LISDN_GET_CINFO;
		rmess[1] = DSP_ISDN_GET_CINFO;
		rmess[2] = chans[idx].chan_num;
		mprintf("idx(%d) cd(%d) ch(%d) m3(%d)m4(%d)m5(%d)\n",
			idx,cardnum,chans[idx].chan_num,mess[3],mess[4],mess[5]);
		memcpy(&tmpp,&mess[4],mess[3]);
		if ((tmpp != NULL)&&(chans[idx].cid != NULL)){
			memcpy(tmpp,chans[idx].cid,sizeof(VPB_CALL_INFO));
		}
//			mprintf("[%d/-]Getting Calling Number: cnum(%d)\n",cardnum,tchp);
//			strcpy(tchp,chans[idx].cid->callingnum);
//			strcpy((char *)mess[3],chans[idx].cid->callingnum);
		m_reg.upmess->Write(rmess, rmess[0]);
		break;

	    default:
		mprintf("[%d/-]Unknown DSP message [%d]\n",cardnum, mess[1]);
		//assert(0);
		break;
	}
	return false;
} //}}}

static void mprint_pri_message( struct pri* /*pri*/, const char *msg )
{
	mprintf("%s\n", msg);
}

struct DchannelData
{ //{{{
	int cardnum;
	int dfd;
	int dfd2;

	DchannelData( int num, int fd, int fd2 = -1 )
	    : cardnum( num )
	    , dfd( fd )
	    , dfd2( fd2 )
	{}
}; //}}}

static void RunDchannel_cleanup( void *p )
{ //{{{
	DchannelData	*d = (DchannelData*)p;

	mprintf("[%d/-] Closing socket (%d,%d) to D-channel\n",
		d->cardnum, d->dfd, d->dfd2);
	if( d->dfd != -1 )  close(d->dfd);
	if( d->dfd2 != -1 ) close(d->dfd2);
} //}}}

void *OpenPri::RunDchannel(void *p)
{ //{{{
	OpenPri* openpri = (OpenPri*)p;

	pri_event *e;
	struct timeval tv = {0,0}, *next, ts;
	fd_set rfds, efds;
	int res, tmp;
	int x = 0;

	word	rmess[COMM_MAX_MESSDSPPC];

	VPBREG *v      = &openpri->m_reg;
	int cardnum    = v->cardnum;
	int numch      = v->numch;

	OP_CARD *cardinfo = (OP_CARD *)v->cardinfo;
	pri_chan *chans   = (pri_chan *)v->chans;

	// Set switch type from config file
	int swtype = cardinfo->protocol;
	int node = cardinfo->node;

	// Get D-channel from config file
	int d_chan = cardinfo->d_channel;

	int dfd = wanconnect((char*)cardinfo->iface[d_chan].card,
			     (char*)cardinfo->iface[d_chan].name);
	if (dfd < 0){
		mprintf("[%d/-] Failed[%d] to open dchannel '%s'\n",
			cardnum, dfd, cardinfo->iface[d_chan].name);
		pthread_exit(NULL);
	}
	mprintf("[%d/-] Opened D-Channel on fd[%d] using '%s'\n",
		cardnum,dfd,cardinfo->iface[d_chan].name);

	DchannelData	cleanup( cardnum, dfd );
	pthread_cleanup_push(RunDchannel_cleanup, &cleanup);

	// Setup the pri logging to use mprint
	pri_set_message(mprint_pri_message);
	pri_set_error(mprint_pri_message);

	struct pri *pri = pri_new(dfd, node, swtype);
	if (!pri) {
		mprintf("[%d/-] Unable to create PRI\n",cardnum);
		pthread_exit(NULL);
	}
	openpri->m_pri = pri;
	pri_set_debug(pri, cardinfo->priverbose);
	mprintf("[%d/-] Created PRI [%p]\n", cardnum, pri);

	for(;;) {
		/* Run the D-Channel */
		FD_ZERO(&rfds);
		FD_ZERO(&efds);
		FD_SET(dfd, &rfds);
		FD_SET(dfd, &efds);

		pthread_mutex_lock(&openpri->m_mutex);
		if ((next = pri_schedule_next(pri))) {
			gettimeofday(&tv, NULL);
			tv.tv_sec = next->tv_sec - tv.tv_sec;
			tv.tv_usec = next->tv_usec - tv.tv_usec;
			if (tv.tv_usec < 0) {
				tv.tv_usec += 1000000;
				tv.tv_sec -= 1;
			}
			if (tv.tv_sec < 0) {
				tv.tv_sec = 0;
				tv.tv_usec = 0;
			}
		}
		pthread_mutex_unlock(&openpri->m_mutex);

		res = select(dfd + 1, &rfds, NULL, &efds, next ? &tv : NULL);
		e = NULL;

		pthread_mutex_lock(&openpri->m_mutex);
		if (!res) {
			e = pri_schedule_run(pri);
		} else if (res > 0) {
		//	mprintf("[%d/-] PRI check event\n",cardnum );
			e = pri_check_event(pri);
		}
		pthread_mutex_unlock(&openpri->m_mutex);

		gettimeofday(&ts, NULL);
		if (e) {
			mprintf("[%d/-] Got an event[%d] on PRI interface at [%ld]...\n",
				cardnum ,e->e, ts.tv_sec);
			openpri->handle_pri_event(pri, e);
		}
		//else mprintf("[%ld]No events, checking channel statii...\n",ts.tv_sec);

		for(x=0; x < numch; ++x) {
			//mprintf("Checking [%d] state[%d]\n",x,chans[x].state);
			tmp = chans[x].state;
			switch(tmp){
			    case CH_NEEDHANG:
				pthread_mutex_lock(&openpri->m_mutex);
				if (chans[x].state != CH_IDLE) {
					/* Stop Audio Thread */
					chans[x].run=0;
					/* Clear out caller ID information */
					delete chans[x].cid;
					chans[x].cid = NULL;

					res = pri_hangup(pri, chans[x].call,
							 PRI_CAUSE_NORMAL_CLEARING);
					if (res != 0){
						mprintf("[%d/%d] Error hanging up call\n",
							cardnum,chans[x].chan_num);
					}
					// generate event
					if( chans[x].cause != PRI_CAUSE_NORMAL_CLEARING
					 && chans[x].cause != -1 )
					{
						mprintf("[%d/%02d] Sending ISDN busy, cause [%d]..\n",
							cardnum,chans[x].chan_num,chans[x].cause);
						rmess[0] = DSP_LISDN_BUSY;
						rmess[1] = DSP_ISDN_BUSY;
						rmess[2] = chans[x].chan_num;
						rmess[3] = chans[x].cause;
						v->upmess->Write(rmess, rmess[0]);
					}
					mprintf("[%d/%02d] Sending drop event up...\n",
						cardnum,chans[x].chan_num);
					rmess[0] = DSP_LDROP;
					rmess[1] = DSP_DROP;
					rmess[2] = chans[x].chan_num;
					v->upmess->Write(rmess, rmess[0]);
					/* Destroy the call */
					pri_destroycall(pri,chans[x].call);
					chans[x].call = NULL;
					chans[x].state = CH_NEEDONHK;
				}
				pthread_mutex_unlock(&openpri->m_mutex);
				break;

			    case CH_NEEDANS:
				chans[x].run=1;
				//pthread_create(&audio_thread_t[x], NULL,
				//		 audio_thread,(void*)&chans[x]);
				chans[x].state = CH_CONNECTED;
				// generate event
				mprintf("[%d/%02d] Sending isdn answer event up...\n",
					cardnum,chans[x].chan_num);
				rmess[0] = DSP_LISDN_ANS;
				rmess[1] = DSP_ISDN_ANS;
				rmess[2] = chans[x].chan_num;
				v->upmess->Write(rmess, rmess[0]);
				break;

			    case CH_PROCEEDING:
				chans[x].state = CH_SETUP;
				// generate event
				mprintf("[%d/%02d] Sending isdn proceeding event up...\n",
					cardnum,chans[x].chan_num);
				rmess[0] = DSP_LISDN_PROCEEDING;
				rmess[1] = DSP_ISDN_PROCEEDING;
				rmess[2] = chans[x].chan_num;
				v->upmess->Write(rmess, rmess[0]);
				break;

			    case CH_RINGING:
				chans[x].state = CH_SETUP;
				// generate event
				mprintf("[%d/%02d] Sending isdn ringing event up...\n",
					cardnum,chans[x].chan_num);
				rmess[0] = DSP_LISDN_RINGING;
				rmess[1] = DSP_ISDN_RINGING;
				rmess[2] = chans[x].chan_num;
				v->upmess->Write(rmess, rmess[0]);
				break;

			    case CH_NEEDRING:
				if (chans[x].cid)
					mprintf("[%d/%02d] Sending ring event up...(cref:%d)(%s %s %s %s %s)\n",
						cardnum,
						chans[x].chan_num,
						chans[x].cref,
						chans[x].cid->callingnum,
						chans[x].cid->callingname,
						chans[x].cid->callingsubaddr,
						chans[x].cid->callednum,
						chans[x].cid->calledsubaddr);
				else
					mprintf("[%d/%02d] Sending ring event up...(cref:%d)\n",
						cardnum,
						chans[x].chan_num,
						chans[x].cref);
				rmess[0] = DSP_LISDN_RING;
				rmess[1] = DSP_ISDN_RING;
				rmess[2] = chans[x].chan_num;
				chans[x].state=CH_RING;
				v->upmess->Write(rmess, rmess[0]);
				break;
			}
		}
	}
	// We should never get here under normal circumstances
	pthread_cleanup_pop(1);
	mprintf("[%d/-] RunDchannel thread: abnormal exit\n",cardnum);
	pthread_exit(NULL);
} //}}}

// Runs the libpri library on 2 interfaces for logging and combines the results
void *OpenPri::RunDchannelLog(void *p)
{ //{{{
	OpenPri* openpri = (OpenPri*)p;

	pri_event *e;
	struct timeval ts;
	fd_set rfds, efds;
	int res, x = 0;
	struct pri *pri;
	struct pri *pri2;
	struct pri *pritmp = NULL;

	char *tmpstr;

	struct cref_info *cref_info;
	struct cref_info *cref_info_last = NULL;

	word	rmess[COMM_MAX_MESSDSPPC];

	VPBREG *v      = &openpri->m_reg;
	int cardnum    = v->cardnum;

	OP_CARD *cardinfo = (OP_CARD *)v->cardinfo;
	pri_chan *chans   = (pri_chan *)v->chans;

	// Set switch type from config file
	int swtype = cardinfo->protocol;
	int node   = PRI_CPE;

	// Get D-channel from config file
	int d_chan = cardinfo->d_channel;

	// Split up channel and card names
	// iface is initialised from config file which uses channel1 - channel31
	// when numch = 30.
	char card2[32];
	char name2[32];

	tmpstr = strchr((char*)cardinfo->iface[d_chan].name, ',');
	if (tmpstr != NULL) {
		*tmpstr++ = '\0';
		strcpy(name2, tmpstr);
	}
	tmpstr = strchr((char*)cardinfo->iface[d_chan].card, ',');
	if (tmpstr != NULL) {
		*tmpstr++ = '\0';
		strcpy(card2, tmpstr);
	}

	int dfd = wanconnect((char*)cardinfo->iface[d_chan].card,
			     (char*)cardinfo->iface[d_chan].name);
	int dfd2 = wanconnect(card2, name2);

	if( dfd < 0 || dfd2 < 0 ) {
		mprintf("[%d/-] Failed[%d] to open dchannel '%s'\n",
			cardnum, dfd, cardinfo->iface[d_chan].name);
		pthread_exit( NULL );
	} else {
		mprintf("[%d/-] Opened 1/2 D-Channel on fd[%d] using '%s'\n",
			cardnum,dfd,cardinfo->iface[d_chan].name);
		mprintf("[%d/-] Opened 1/2 D-Channel on fd[%d] using '%s'\n",
			cardnum, dfd2, name2);
	}
	mprintf("[%d/-] D-Channel is in logging mode!\n",cardnum);

	int seldfd = dfd2 > dfd ? dfd2 : dfd;

	DchannelData	cleanup( cardnum, dfd, dfd2 );
	pthread_cleanup_push(RunDchannel_cleanup, &cleanup);

	// Setup the pri logging to use mprint
	pri_set_message(mprint_pri_message);
	pri_set_error(mprint_pri_message);

	pri = pri_new(dfd, node, swtype);
	if( !pri ) {
		mprintf("[%d/-] Unable to create PRI (1)\n",cardnum);
		pthread_exit( NULL );
	}
	pri2 = pri_new(dfd2, node, swtype);
	if( !pri2 ) {
		mprintf("[%d/-] Unable to create PRI (2)\n",cardnum);
		free( pri );
		pthread_exit( NULL );
	}
	openpri->m_pri = pri;
	openpri->m_pri2 = pri2;
	pri_set_debug(pri, cardinfo->priverbose);
	pri_set_debug(pri2, cardinfo->priverbose);
	mprintf("[%d/-] Created PRI [%p]\n",cardnum,pri);

	for(;;) {
		struct timeval tv = {1,0};

		/* Run the D-Channel */
		FD_ZERO(&rfds);
		FD_ZERO(&efds);
		FD_SET(dfd, &rfds);
		FD_SET(dfd, &efds);
		FD_SET(dfd2, &rfds);
		FD_SET(dfd2, &efds);

		pri = openpri->m_pri;
		pri2 = openpri->m_pri2;
		res = select(seldfd + 1, &rfds, NULL, &efds, &tv);
		if( res <= 0 ) continue;

		//mprintf("[%d/-] PRI check event\n",cardnum );
		e = NULL;
		if (FD_ISSET(dfd,&rfds)){
			e = pri_check_event_log(pri);
			pritmp = pri;
		} else if (FD_ISSET(dfd2,&rfds)){
			e = pri_check_event_log(pri2);
			pritmp = pri2;
		}

		gettimeofday(&ts, NULL);
		if(e) {
			openpri->handle_pri_event_log(pritmp,e);
		}
		// else mprintf("[%ld]No events, checking channel statii...\n",ts.tv_sec);

		for (x=0; x < v->numch; ++x) {
			// mprintf("Checking [%d] state[%d]\n",x,chans[x].state);
			switch(chans[x].state){
			case CH_NEEDHANG:
				pthread_mutex_lock(&openpri->m_mutex);
				if (chans[x].state != CH_IDLE) {
					/* Stop Audio Thread */
					chans[x].run=0;

					/* Clear out caller ID information */
					delete chans[x].cid;
					chans[x].cid = NULL;

					// generate event
					if (chans[x].cause != PRI_CAUSE_NORMAL_CLEARING){
						mprintf("[%d/%02d] Sending ISDN busy, cause [%d]..\n",
							cardnum,chans[x].chan_num,chans[x].cause);
						rmess[0] = DSP_LISDN_BUSY;
						rmess[1] = DSP_ISDN_BUSY;
						rmess[2] = chans[x].chan_num;
						rmess[3] = chans[x].cause;
						v->upmess->Write(rmess, rmess[0]);
					}
					mprintf("[%d/%02d] Sending drop event up...\n",
						cardnum,chans[x].chan_num);
					rmess[0] = DSP_LDROP;
					rmess[1] = DSP_DROP;
					rmess[2] = chans[x].chan_num;
					v->upmess->Write(rmess, rmess[0]);
					//pri_destroycall(pri,chans[x].call);
					pri_destroycall(pritmp, e->hangup.call);
					chans[x].call = NULL;
					chans[x].cref = -1;
					chans[x].cref_check = 0;
					chans[x].state = CH_IDLE;
				}
				pthread_mutex_unlock(&openpri->m_mutex);
				break;

			case CH_NEEDANS:
				pthread_mutex_lock(&openpri->m_mutex);
				chans[x].run=1;
				//pthread_create(&audio_thread_t[x], NULL,
				//		 audio_thread_log,(void*)&chans[x]);
				chans[x].state = CH_CONNECTED;
				// generate event
				if (chans[x].cref_check == 0){
					mprintf("[%d/%02d] checking cref_info linked list for 0x%X\n",
						cardnum,chans[x].chan_num, chans[x].cref);
					if (chans[x].cref != -1){
						cref_info = openpri->m_cref_info;
						cref_info_last = NULL;
						while(cref_info != NULL){
							mprintf("[%d/%02d] comparing cref_info  0x%X to 0x%X (0x%X)\n",
								cardnum,chans[x].chan_num,
								cref_info->cref, chans[x].cref,
								(chans[x].cref&0x7fff));

							if(cref_info->cref == (chans[x].cref&0x7fff)){
								mprintf("[%d/%02d] Found call information from cref_info linked list\n",
									cardnum,chans[x].chan_num);
								if (chans[x].cid == NULL){
									chans[x].cid = new VPB_CALL_INFO;
								}
								set_callinfo(chans[x], *cref_info);
								chans[x].cref_check = 1;
								if(cref_info_last != NULL){
									cref_info_last->next = cref_info->next;
								}
								else {
									openpri->m_cref_info = cref_info->next;
								}
								delete cref_info;
								cref_info = NULL;
							}
							else {
								cref_info_last = cref_info;
								cref_info = cref_info->next;
							}
						}
					}
					else {
						mprintf("[%d/%02d] No caller reference number!\n",
							cardnum,chans[x].chan_num);
					}
				}

				mprintf("[%d/%02d] Sending isdn answer event up...\n",
					cardnum,chans[x].chan_num);
				rmess[0] = DSP_LISDN_ANS;
				rmess[1] = DSP_ISDN_ANS;
				rmess[2] = chans[x].chan_num;
				v->upmess->Write(rmess, rmess[0]);
				pthread_mutex_unlock(&openpri->m_mutex);
				break;

			case CH_NEEDRING:
				pthread_mutex_lock(&openpri->m_mutex);
				if (chans[x].cid)
					mprintf("[%d/%02d] Sending ring event up...(cref:%d)(%s %s %s)\n",
						cardnum,chans[x].chan_num,
						chans[x].cref,chans[x].cid->callingnum,
						chans[x].cid->callingname,chans[x].cid->callednum);
				else
					mprintf("[%d/%02d] Sending ring event up...(cref:%d)\n",
						cardnum,chans[x].chan_num,
						chans[x].cref);

				rmess[0] = DSP_LISDN_RING;
				rmess[1] = DSP_ISDN_RING;
				rmess[2] = chans[x].chan_num;
				v->upmess->Write(rmess, rmess[0]);
				chans[x].state=CH_RING;
				pthread_mutex_unlock(&openpri->m_mutex);
				break;

			case CH_NEEDTERMINATING:
				/* Stop Audio Thread */
				chans[x].run=0;
				chans[x].state=CH_TERMINATING;
				break;
			}
		}
	}
	// We should never get here under normal circumstances
	pthread_cleanup_pop(1);
	mprintf("[%d/-] RunDchannel thread: abnormal exit\n",cardnum);
	pthread_exit(NULL);
} //}}}

void OpenPri::handle_pri_event(struct pri *pri, pri_event *e)
{ //{{{
	pri_chan *chans;
	chans = (pri_chan*)m_reg.chans;
	int idx,i;

	switch(e->e) {
	    case PRI_EVENT_DCHAN_UP:
		mprintf("[%d/-] PRI D-Channel is now up!  :-)\n",m_reg.cardnum);
		break;

	    case PRI_EVENT_DCHAN_DOWN:
		mprintf("[%d/-] PRI D-Channel is now down! :-(\n",m_reg.cardnum);
		break;

	    case PRI_EVENT_RESTART:
		mprintf("[%d/-] PRI Restarting channel %d\n",m_reg.cardnum, e->restart.channel);
//		hangup_channel(e->restart.channel);
		break;

	    case PRI_EVENT_CONFIG_ERR:
		mprintf("[%d/-] PRI Configuration error detected: %s\n",m_reg.cardnum, e->err.err);
		break;

	    case PRI_EVENT_RING:
		mprintf("[%d/%d] PRI Ring on channel (from [%s] to [%s] sub [%s])(cref:%d)\n",
			m_reg.cardnum, e->ring.channel, e->ring.callingnum,
			e->ring.callednum,e->ring.calledsubaddr,e->ring.cref);
		if (e->ring.channel != -1){
			try {
				idx = get_idx_from_ich(&m_reg,e->ring.channel);
			} catch ( const Wobbly &w ) {
				mprintf("handle_pri_event: EVENT_RING Failed to get channel from %d\n",
					e->ring.channel);
				return;
			}
		}
		else {
			//i = slot2chan(e->ring.slotmap,m_reg.numch);
			//mprintf("[%d/%d]       Slotmap'ed to channel %d\n",m_reg.cardnum, i,i);
			//idx = get_idx_from_ich(m_reg,i);
			idx = findfreechan(&m_reg);
			mprintf("[%d/%d]       Got Slotmap, found free channel %d\n",
				m_reg.cardnum,idx,idx);
		}
		pthread_mutex_lock(&m_mutex);
		chans[idx].state=CH_NEEDRING;
		/* record call informaton */
		chans[idx].call = e->ring.call;
		/* Save caller ID info */
		if (chans[idx].cid == NULL){
			chans[idx].cid = new VPB_CALL_INFO;
		}
		set_callinfo(chans[idx], e->ring);
		for(i=0;i<e->ring.lowlayercompatibility[0]+1;i++){
			chans[idx].lowlayercompatibility[i]=e->ring.lowlayercompatibility[i];
		}
		pthread_mutex_unlock(&m_mutex);
		break;

	    case PRI_EVENT_HANGUP:
		try {
			idx = get_idx_from_ich(&m_reg,e->hangup.channel);
		} catch ( const Wobbly &w ) {
			mprintf("handle_pri_event: EVENT_HANGUP Failed to get channel from %d\n",
				e->hangup.channel);
			return;
		}
		mprintf("[%d/%d] PRI Hangup on channel (cref:%d)\n",
			m_reg.cardnum , idx, e->hangup.cref);
		chans[idx].cause=e->hangup.cause;
		chans[idx].state=CH_NEEDHANG;
		break;

	    case PRI_EVENT_HANGUP_REQ:
		try {
			idx = get_idx_from_ich(&m_reg,e->hangup.channel);
		} catch ( const Wobbly &w ) {
			mprintf("handle_pri_event: EVENT_HANGUP_REQ Failed to get channel from %d\n",
				e->hangup.channel);
			return;
		}
		mprintf("[%d/%d] PRI Hangup requested on channel (cref:%d)\n",
			m_reg.cardnum , idx, e->hangup.cref);
		chans[idx].cause=e->hangup.cause;
		chans[idx].state=CH_NEEDHANG;
		break;

	    case PRI_EVENT_RINGING:
		try {
			idx = get_idx_from_ich(&m_reg,e->ringing.channel);
		} catch ( const Wobbly &w ) {
			mprintf("handle_pri_event: EVENT_RINGING Failed to get channel from %d\n",
				e->ringing.channel);
			return;
		}
		mprintf("[%d/%d] PRI Got a Ringing on channel (cref:%d)\n",
			m_reg.cardnum, e->ringing.channel, e->ringing.cref);
		chans[idx].state=CH_RINGING;
		break;

	    case PRI_EVENT_PROCEEDING:
		try {
			idx = get_idx_from_ich(&m_reg,e->proceeding.channel);
		} catch ( const Wobbly &w ) {
			mprintf("handle_pri_event: EVENT_PROCEEDING Failed to get channel from %d\n",
				e->proceeding.channel);
			return;
		}
		mprintf("[%d/%d] PRI Got a Call Proceeding on channel \n",
			m_reg.cardnum , e->proceeding.channel);
		chans[idx].state=CH_PROCEEDING;
		break;

	    case PRI_EVENT_ANSWER:
		mprintf("[%d/%d] PRI Got a Answer on channel (cref:%d)\n",
			m_reg.cardnum , e->answer.channel,e->answer.cref);
		try {
			idx = get_idx_from_ich(&m_reg,e->answer.channel);
		} catch ( const Wobbly &w ) {
			mprintf("handle_pri_event: EVENT_ANSWER Failed to get channel from %d\n",
				e->answer.channel);
			return;
		}
		if (chans[idx].state == CH_SETUP){
			chans[idx].state=CH_NEEDANS;
		}
		else {
			mprintf("[%d/%d] PRI Channel not in setup state, in %d, hanging up\n",
				m_reg.cardnum , e->answer.channel,chans[e->ring.channel].state);
			chans[idx].state=CH_NEEDHANG;
		}
		break;

	    case PRI_EVENT_HANGUP_ACK:
		try {
			idx = get_idx_from_ich(&m_reg,e->hangup.channel);
		} catch ( const Wobbly &w ) {
			mprintf("handle_pri_event: EVENT_HANGUP_ACK Failed to get channel from %d\n",
				e->hangup.channel);
			q931_destroycall(pri, e->hangup.cref);
			return;
		}
		mprintf("[%d/%d] PRI Hangup ACK on channel (cref:%d)\n",
			m_reg.cardnum , idx, e->hangup.cref);
		chans[idx].run=0;
		chans[idx].cause=e->hangup.cause;
		chans[idx].state=CH_NEEDHANG;
		break;

	    case PRI_EVENT_INFO_RECEIVED:
		mprintf("[%d/%d] PRI number is: %s (cref:%d)\n",
			m_reg.cardnum, e->ring.channel , e->ring.callednum,e->ring.cref);
		if(!(strlen(e->ring.callednum) < 3)){
			mprintf("[%d/%d] PRI Final number is: %s\n",
				m_reg.cardnum, e->ring.channel, e->ring.callednum);
			pri_answer(pri, e->ring.call, e->ring.channel, 1);
		}
		break;

	    default:
		mprintf("[%d/-] PRI Unknown PRI event [%d] !!\n",m_reg.cardnum , e->e);
	}
} //}}}

void OpenPri::handle_pri_event_log(struct pri *pri, pri_event *e)
{ //{{{
	struct cref_info *crefinfo;

	pri_chan *chans;
	chans = (pri_chan*)m_reg.chans;
	struct cref_info *prevcrefinfo = NULL;
	int idx = -1;

	switch(e->e) {
	    case PRI_EVENT_DCHAN_UP:
		//mprintf("[%d/-] PRI D-Channel is now up!  :-)\n",m_reg.cardnum);
		break;

	    case PRI_EVENT_DCHAN_DOWN:
		//mprintf("[%d/-] PRI D-Channel is now down! :-(\n",m_reg.cardnum);
		break;

	    case PRI_EVENT_RESTART:
		//mprintf("[%d/-] PRI Restarting channel %d\n",
		//	  m_reg.cardnum, e->restart.channel);
		//hangup_channel(e->restart.channel);
		break;

	    case PRI_EVENT_CONFIG_ERR:
		mprintf("[%d/-] PRI Configuration error detected: %s\n",
			m_reg.cardnum, e->err.err);
		break;

	    case PRI_EVENT_RING:
		//XXX ring.call is internal libpri state, not public state data
		if ((e->ring.channel == -1) && (e->ring.call->slotmap > 0))
			e->ring.channel = ffs(e->ring.call->slotmap);

		mprintf("[%d/%d] PRI Ring on channel (from [%s] to [%s] sub [%s])(cref:%d)\n",
			m_reg.cardnum, e->ring.channel, e->ring.callingnum,
			e->ring.callednum,e->ring.calledsubaddr,e->ring.cref);

		if (e->ring.channel != -1) {
			try {
				if (idx == -1)
					idx = get_idx_from_ich(&m_reg, e->ring.channel);
			} catch ( const Wobbly &w ) {
				mprintf("handle_pri_event_log: EVENT_RING Failed to get channel from %d\n",
					e->ring.channel);
				return;
			}
			pthread_mutex_lock(&m_mutex);

			/* record call informaton */
			chans[idx].call = e->ring.call;
			/* Save caller ID info */
			if (chans[idx].cid == NULL){
				chans[idx].cid = new VPB_CALL_INFO;
			}
			set_callinfo(chans[idx], e->ring);
			chans[idx].cref_check=1;
			pthread_mutex_unlock(&m_mutex);
		}
		else {
			pthread_mutex_lock(&m_mutex);
			crefinfo = m_cref_info;
			if(crefinfo != NULL){
				while(crefinfo->next != NULL)
					crefinfo = crefinfo->next;
				crefinfo->next = new struct cref_info;
				crefinfo = crefinfo->next;
			}
			else {
				crefinfo = new struct cref_info;
				m_cref_info = crefinfo;
			}
			crefinfo->next = NULL;
			set_callinfo(*crefinfo, e->ring);
			pthread_mutex_unlock(&m_mutex);
		}
		break;

	    case PRI_EVENT_HANGUP:
		if (e->hangup.channel == -1) {
			for (int i = 0; i < m_reg.numch; i++) {
				if ((chans[i].cref&0x7fff) == (e->hangup.cref&0x7fff)) {
					idx = i;
					break;
				}
			}
		}
		mprintf("[%d/%d] PRI Hangup on channel (cref:%d)\n",
			m_reg.cardnum , e->hangup.channel, e->hangup.cref);

		// need to ensure that we clean up the crefinfo linked list,
		// otherwise the cref of unanswered calls remains in the list
		// and leaks memory
		pthread_mutex_lock(&m_mutex);
		crefinfo = m_cref_info;
		prevcrefinfo = NULL;
		while (crefinfo && crefinfo->cref != (e->hangup.cref&0x7fff)) {
			prevcrefinfo = crefinfo;
			crefinfo = crefinfo->next;
		}
		if (crefinfo && crefinfo->cref == (e->hangup.cref&0x7fff)) {
			if (prevcrefinfo)
				prevcrefinfo->next = crefinfo->next;
			else
				m_cref_info = crefinfo->next;
			delete crefinfo;
		}
		pthread_mutex_unlock(&m_mutex);

		try {
			if (idx == -1)
				idx = get_idx_from_ich(&m_reg,e->hangup.channel);
		} catch ( const Wobbly &w ) {
			mprintf("handle_pri_event_log: EVENT_HANGUP Failed to get channel from %d\n",
				e->hangup.channel);
			q931_destroycall(pri, e->hangup.cref);
			return;
		}
		chans[idx].run=0;
		chans[idx].cause=e->hangup.cause;
		chans[idx].state=CH_NEEDHANG;
		break;

	    case PRI_EVENT_HANGUP_REQ:
		if (e->hangup.channel == -1) {
			for (int i = 0; i < m_reg.numch; i++) {
				if ((chans[i].cref&0x7fff) == (e->hangup.cref&0x7fff)) {
					idx = i;
					break;
				}
			}
		}
		mprintf("[%d/%d] PRI Hangup requested on channel (cref:%d)\n",
			m_reg.cardnum , e->hangup.channel, e->hangup.cref);
		try {
			if (idx == -1)
				idx = get_idx_from_ich(&m_reg,e->hangup.channel);
		} catch ( const Wobbly &w ) {
			mprintf("handle_pri_event_log: EVENT_HANGUP_REQ Failed to get channel from %d\n",
				e->hangup.channel);
			q931_destroycall(pri, e->hangup.cref);
			return;
		}
		chans[idx].run=0;
		chans[idx].cause=e->hangup.cause;
		chans[idx].state=CH_NEEDTERMINATING;
		break;

	    case PRI_EVENT_RINGING:
		if (e->ringing.channel == -1) {
			for (int i = 0; i < m_reg.numch; i++) {
				if ((chans[i].cref&0x7fff) == (e->ringing.cref&0x7fff)) {
					idx = i;
					break;
				}
			}
		}
		mprintf("[%d/%d] PRI Got a Ringing on channel (cref:%d)\n",
			m_reg.cardnum , e->ringing.channel,e->ringing.cref);
		// For UK logger outgoing calls
		if(idx != -1 || e->ringing.channel != -1){
			try {
				if (idx == -1)
					idx = get_idx_from_ich(&m_reg,e->ringing.channel);
			} catch ( const Wobbly &w ) {
				mprintf("handle_pri_event_log: EVENT_RING Failed to get channel from %d\n",
					e->ringing.channel);
				return;
			}
			if(chans[idx].state==CH_RING) {
				// ok... already in CH_RING
				mprintf("[%d/%d] PRI Ringing : already in CH_RING \n",
					m_reg.cardnum , e->ringing.channel);

			}else{

					chans[idx].state=CH_NEEDRING;
   					mprintf("[%d/%d] PRI Ringing : added a CH_NEEDRING \n",
						m_reg.cardnum , e->ringing.channel);
			}

		}

		break;

	    case PRI_EVENT_PROCEEDING:
		if (e->proceeding.channel == -1) {
			for (int i = 0; i < m_reg.numch; i++) {
				if ((chans[i].cref&0x7fff) == (e->proceeding.cref&0x7fff)) {
					idx = i;
					break;
				}
			}
		}
		mprintf("[%d/%d] PRI Got a Call Proceeding on channel \n",
			m_reg.cardnum , e->proceeding.channel);

		if(idx != -1 || e->proceeding.channel != -1){
			try {
				if (idx == -1)
					idx = get_idx_from_ich(&m_reg,e->proceeding.channel);
			} catch ( const Wobbly &w ) {
				mprintf("handle_pri_event_log: EVENT_RING Failed to get channel from %d\n",
					e->proceeding.channel);
				return;
			}
			if(chans[idx].state==CH_RING) {
				// ok... already in CH_RING
  				mprintf("[%d/%d] PRI Call Proceeding : already in CH_RING \n",
					m_reg.cardnum , e->proceeding.channel);

			}else{

					chans[idx].state=CH_NEEDRING;
	   				mprintf("[%d/%d] PRI Call Proceeding : added a CH_NEEDRING \n",
						m_reg.cardnum , e->proceeding.channel);

			}
		}
		break;

	    case PRI_EVENT_ANSWER:
		if (e->answer.channel == -1) {
			for (int i = 0; i < m_reg.numch; i++) {
				if ((chans[i].cref&0x7fff) == (e->answer.cref&0x7fff)) {
					idx = i;
					break;
				}
			}
		}
		mprintf("[%d/%d] PRI Got a Answer on channel (ans.cref:%d)\n",
			m_reg.cardnum , e->answer.channel,e->answer.cref);
		try {
			if (idx == -1)
				idx = get_idx_from_ich(&m_reg,e->answer.channel);
		} catch ( const Wobbly &w ) {
			mprintf("handle_pri_event_log: EVENT_ANSWER Failed to get channel from %d\n",
				e->answer.channel);
			return;
		}
		chans[idx].state=CH_NEEDANS;
		if (chans[idx].cref != e->answer.cref){
			chans[idx].cref = e->answer.cref;
			mprintf("[%d/%d] Updated cref %d\n",
				m_reg.cardnum , chans[idx].chan_num, chans[idx].cref);
		}

		break;

	    case PRI_EVENT_SETUP_ACK:
		mprintf("[%d/%d] PRI Setup ACK on channel\n",
			m_reg.cardnum,e->setup_ack.channel);
		break;

	    case PRI_EVENT_HANGUP_ACK:
		if (e->hangup.channel == -1) {
			for (int i = 0; i < m_reg.numch; i++) {
				if ((chans[i].cref&0x7fff) == (e->hangup.cref&0x7fff)) {
					idx = i;
					break;
				}
			}
		}
		mprintf("[%d/%d] PRI Hangup ACK on channel (cref:%d)\n",
			m_reg.cardnum , e->hangup.channel,e->hangup.cref);
		/* Ignore */
		try {
			if (idx == -1)
				idx = get_idx_from_ich(&m_reg,e->hangup.channel);
		} catch ( const Wobbly &w ) {
			mprintf("handle_pri_event_log: EVENT_HANGUP_ACK Failed to get channel from %d\n",
				e->hangup.channel);
			q931_destroycall(pri, e->hangup.cref);
			return;
		}
		chans[idx].run=0;
		chans[idx].cause=e->hangup.cause;
		chans[idx].state=CH_NEEDHANG;
		break;

	    case PRI_EVENT_INFO_RECEIVED:
		if (e->ring.channel == -1) {
			for (int i = 0; i < m_reg.numch; i++) {
				if ((chans[i].cref&0x7fff) == (e->ring.cref&0x7fff)) {
					idx = i;
					break;
				}
			}
		}
		mprintf("[%d/%d] PRI number is: %s (cref:%d)\n",
			m_reg.cardnum, e->ring.channel,
			e->ring.callednum, e->ring.cref);
		if(idx != -1 || e->ring.channel != -1){
			try {
				if (idx == -1)
					idx = get_idx_from_ich(&m_reg, e->ring.channel);
			} catch ( const Wobbly &w ) {
				mprintf("handle_pri_event_log: EVENT_RING Failed to get channel from %d\n",
					e->ring.channel);
				return;
			}

			// we have a valid channel number... store the
			// information received against that channel

			pthread_mutex_lock(&m_mutex);
			/* record call informaton */
			chans[idx].call = e->ring.call;
			/* Save caller ID info */
			if (chans[idx].cid == NULL){
				chans[idx].cid = new VPB_CALL_INFO;

				set_callinfo(chans[idx], e->ring);
				chans[idx].cref_check=1;
			} else {

				// Collect the pri numbers coming thru in INFO events
				// This happens with numbers dialed by hand
				strcat(chans[idx].cid->callednum,e->ring.callednum);
				mprintf("[%d/%d] PRI called number is now : %s (cref:%d)\n",m_reg.cardnum,
						e->ring.channel , chans[idx].cid->callednum ,e->ring.cref);

			}
			pthread_mutex_unlock(&m_mutex);
		} else {
			// no channel number selected yet - need to keep track
			// of information in cref_info linked list for now

			pthread_mutex_lock(&m_mutex);
			crefinfo = m_cref_info;
			while (crefinfo != NULL && crefinfo->cref != (e->ring.cref&0x7fff))
				crefinfo = crefinfo->next;

			if (crefinfo != NULL) {
				strcat(crefinfo->cid.callednum, e->ring.callednum);
				mprintf("[%d/%d] PRI called number is now : %s (cref:%d)\n",m_reg.cardnum,
						e->ring.channel, crefinfo->cid.callednum, e->ring.cref);
			}
			pthread_mutex_unlock(&m_mutex);
		}
		break;

	    default:
		mprintf("[%d/-] PRI Unknown PRI event [%d] !!\n",
			m_reg.cardnum , e->e);
	}
} //}}}

int check_encoding(pri_chan * c)
{ //{{{
	int encoding;

	mprintf("check_encoding: Checking channel %d [layer1: %s (%d)]...\n",
		c->chan_num,
		(c->layer1 == PRI_LAYER_1_ALAW ? "alaw"
		    : (c->layer1 == PRI_LAYER_1_ULAW ? "mulaw" : "unknown")),
		c->layer1);
	// check encoding
	if( (c->layer1 == PRI_LAYER_1_ULAW && c->encoding != VPB_MULAW)  // Compare if mulaw
	 || (c->layer1 == PRI_LAYER_1_ALAW && c->encoding != VPB_ALAW) ) // Compare if alaw
	{
		//mprintf("Fixing Layer1(0x%02X) and encoding(%d)!\n",c->layer1,c->encoding);
		// Encoding doesnt match layer1 from call setup, fixit!
		if (c->layer1 == PRI_LAYER_1_ULAW){
			encoding = VPB_MULAW;
		} else if (c->layer1 == PRI_LAYER_1_ALAW){
			encoding = VPB_ALAW;
		} else {
			encoding = VPB_RAW;
		}
	} else {
		encoding = c->encoding;
	}
	if (c->transcap == PRI_TRANS_CAP_DIGITAL){
		encoding = VPB_RAW;
	}
	mprintf("check_encoding: channel encoding set to [%s].\n",
		(VPB_ALAW==encoding ? "alaw"
				    :(VPB_MULAW==encoding ? "mulaw" : "raw")));
	return encoding;
} //}}}

static inline void flush_socket_buffer( int fd, int channels )
{ //{{{
	char buf[16 + channels * NBUF];

	for(;;) switch( recv(fd, buf, channels * NBUF + 16, MSG_DONTWAIT) )
	{
	    case 0:
		mprintf("flush_socket_buffer(%d): remote end closed connection\n", fd);
		//XXX If this really happens we probably need to do
		//    more than just return as if everything is ok.
		return;

	    case -1:
		if( errno == EAGAIN )
			mprintf("flush_socket_buffer(%d): audio buffer clear\n", fd);
		else
			mprintf("flush_socket_buffer(%d): recv failed: %m\n", fd);
			//XXX If this really happens we are probably
			//    also in somewhat deeper trouble too ...
		return;

	    default:
		//XXX Do we actually want to just flush 1ms at a time here
		//    or should we try to flush a much bigger chunk instead?
		mprintf("flush_socket_buffer(%d): cleared 1ms of audio\n", fd);
	}
} //}}}

// Connects to the correct wanpipe interface and gets/puts the audio onto
// the correct driver FIFO's for multiple channels on single interface
static void *audio_thread_multi(void *pv)
{ //{{{
	unsigned char bit_inv[256];
	AUDIO_THREAD_MULTI * info;
	info = (AUDIO_THREAD_MULTI *)pv;
	pri_chan * c;
	VPBREG * v = info->v;
	int c_fd,i,x,tmp;
	int ret,nbytes,err;
	unsigned char chbuf[32][NBUF];
	int chan_state[33];
	int encoding[33];
	int channels=0;

	// Select stuff
	fd_set port_fds,write_fds,oob_fds;
	struct timeval tv;

	memset( chan_state, 0, sizeof(chan_state) );
	memset( encoding, 0, sizeof(encoding) );

	// Work out number of channels
	channels = info->end_channel - info->start_channel +1;

	unsigned char buf[16 + (channels * NBUF)];

	for(i=0; i < 256; ++i) {
		bit_inv[i] = (i&1?0x80:0);
		bit_inv[i] |= (i&2?0x40:0);
		bit_inv[i] |= (i&4?0x20:0);
		bit_inv[i] |= (i&8?0x10:0);
		bit_inv[i] |= (i&0x10?0x08:0);
		bit_inv[i] |= (i&0x20?0x04:0);
		bit_inv[i] |= (i&0x40?0x02:0);
		bit_inv[i] |= (i&0x80?0x01:0);
	}

	mprintf("Starting audio thread for channels [%d] to [%d], total [%d]"
		" using card [%s] interface [%s]\n",
		info->start_channel, info->end_channel,
		channels, info->card, info->interface);

#ifdef REALTIME_THREADS
	// Set this thread to run with realtime priority
	struct sched_param mysched;
	mysched.sched_priority = 99;
	if (pthread_setschedparam(pthread_self(), SCHED_RR, &mysched) != 0) {
		mprintf("Failed to set realtime priority in audio_thread_multi %#lx\n", pthread_self());
	}
#endif

	for(;;) {
	    c_fd = wanconnect((char *)info->card,(char *)info->interface);
	    if (c_fd < 0) {
		mprintf("Audio thread failed to connect interface [%s] on card '%s'\n",
			info->interface, info->card);
		pthread_exit(NULL);
	    }

	    mprintf("Audio thread connected to interface[%s] on card [%s]"
		    " using socket on fd[%d]\n", info->interface, info->card, c_fd);

	    flush_socket_buffer( c_fd, channels );

	    for(;;) {
		/* Set up file descriptor set for select()ing on sockets */
		FD_ZERO(&port_fds);
		FD_ZERO(&write_fds);
		FD_ZERO(&oob_fds);
		FD_SET(c_fd,&port_fds);
		FD_SET(c_fd,&write_fds);
		FD_SET(c_fd,&oob_fds);

		/* wait for a socket to be ready */
		tv.tv_sec = 1;
		tv.tv_usec = 1000;
		ret = select(c_fd+1,&port_fds,NULL,&oob_fds,&tv);
		if( ret <= 0 ) continue;

		if (FD_ISSET(c_fd,&oob_fds)){
			nbytes = recv(c_fd, buf, channels * NBUF + 16, MSG_OOB);
			if(nbytes < 0 ) {
				mprintf("audio_thread_multi: Failed to receive OOB \n");
				err = ioctl(c_fd,SIOC_WANPIPE_SOCK_STATE,0);
				mprintf("audio_thread_multi: Sock state is %s\n",
					(err == 0) ? "CONNECTED" :
					(err == 1) ? "DISCONNECTED" :
					"CONNECTING");
				if (err == 1){
					mprintf("audio_thread_multi: disconnected\n");
					goto disconnected;
				}
			}
		}
		if (FD_ISSET(c_fd,&port_fds)){
			/* Get data from socket - record */
			nbytes = recv(c_fd, buf, channels * NBUF + 16, 0);
			// got error or connection closed by client
			if (nbytes == 0) {
				// connection closed
				mprintf("audio_thread_multi: Socket hangup on [%s] [%s]\n",
					info->card, info->interface);
			} else if (nbytes < 0) {
				mprintf("audio_thread_multi: Socket error[%d] on [%s] [%s]\n",
					errno, info->card, info->interface);
				if( errno != EINTR && errno != EAGAIN )
					goto disconnected;
			} else {
				for(i=0; i < (nbytes-16); ++i){
					//buf[16+i] = bit_inv[buf[16+i]];
					chbuf[i%channels][i/channels] = bit_inv[buf[16+i]];
				}
				/* Ok, split audio up into channels */
				for (i=info->start_channel,x=0; i <= info->end_channel; ++i, ++x){
					c = &((pri_chan *)v->chans)[i];
					tmp = c->run;
					if (tmp == 1){
						if (chan_state[i] != 1){
							mprintf("audio_thread_multi: Starting audio on [%d](%d/%d)\n",
								c->chan_num,i,x);
							chan_state[i]=1;
							c->v->rxdf[c->chan_num]->Flush();
							c->v->txdf[c->chan_num]->Flush();
							encoding[i]=check_encoding(c);
						}
						/* Send data up FIFO - record  */
						prepare_rec_buf(chbuf[x], c->v,
								c->chan_num,encoding[i]);
						/* Get data from FIFO - play */
						prepare_play_buf(chbuf[x], c->v,
								 c->chan_num,encoding[i]);
					} else {
						if (chan_state[i] == 1){
							mprintf("audio_thread_multi: Finishing audio on [%d]\n",
								c->chan_num);
							chan_state[i]=0;
							c->v->toneg[c->chan_num]->Stop();
							vpb_play_terminate(c->v->handles[c->chan_num]);
							vpb_record_terminate(c->v->handles[c->chan_num]);
						}
					}
				}

				for(i=0; i < (nbytes-16); ++i){
					buf[16+i] = bit_inv[chbuf[i%channels][i/channels]];
				}
				/* Send FIFO up socket  - play */
				err = send(c_fd,buf,sizeof(unsigned char [16+(channels*NBUF)]),0);
			}
		}
		nbytes=0;
	    }

	  disconnected:
	    close(c_fd);
	}

	// We should never get here under normal circumstances
	mprintf("Audio thread: abnormal exit for interface [%s] on card '%s'\n",
		info->interface, info->card);
	pthread_exit(NULL);
} //}}}

// Connects to the correct wanpipe interfaces and gets/puts the audio onto
// the correct driver FIFO's after combing them from both interfaces
static void *audio_thread_log_multi(void *pv)
{ //{{{
	AUDIO_THREAD_MULTI * info;
	info = (AUDIO_THREAD_MULTI *)pv;
	pri_chan * c;
	VPBREG * v = info->v;

	unsigned char bit_inv[256];
	int i,x;
	int c_fd,c_fd2,selfd;
	int ret, err;
	int nbytes, nbytes2;
	unsigned char chbuf[32][NBUF];
	unsigned char chbuf2[32][NBUF];
	char card2[32];
	char interface2[32];
	int chan_state[33];
	int encoding[33];
	int channels=0;
	char *tmpstr;

//	api_rx_element_t* api_rx_el;

	// Select stuff
	fd_set port_fds,write_fds,oob_fds;
	struct timeval tv;

	memset( chan_state, 0, sizeof(chan_state) );
	memset( encoding, 0, sizeof(encoding) );

	// Work out number of channels
	channels = info->end_channel - info->start_channel +1;

	unsigned char buf[16 + (channels * NBUF)];
	unsigned char buf2[16 + (channels * NBUF)];

	// Split up channel and card names
	tmpstr = strchr((char*)info->interface, ',');
	if (tmpstr != NULL) {
		*tmpstr++ = '\0';
		strcpy(interface2, tmpstr);
	}
	tmpstr = strchr((char*)info->card, ',');
	if (tmpstr != NULL) {
		*tmpstr++ = '\0';
		strcpy(card2, tmpstr);
	}

	for(i=0; i < 256; ++i) {
		bit_inv[i] = (i&1?0x80:0);
		bit_inv[i] |= (i&2?0x40:0);
		bit_inv[i] |= (i&4?0x20:0);
		bit_inv[i] |= (i&8?0x10:0);
		bit_inv[i] |= (i&0x10?0x08:0);
		bit_inv[i] |= (i&0x20?0x04:0);
		bit_inv[i] |= (i&0x40?0x02:0);
		bit_inv[i] |= (i&0x80?0x01:0);
	}

	mprintf("Starting LOGGING audio thread for channels [%d] to [%d],"
		" total [%d] using card [%s] interface [%s]\n",
		info->start_channel, info->end_channel, channels,
		info->card, info->interface);

	for(;;) {
	    c_fd = wanconnect((char*)info->card, (char*)info->interface);
	    if (c_fd < 0) {
		mprintf("Failed to connect to channel interface on card '%s' iface '%s': %d\n",
			info->card,info->interface, c_fd);
		pthread_exit(NULL);
	    }

	    c_fd2 = wanconnect(card2, interface2);
	    if (c_fd2 < 0) {
		mprintf("Failed to connect to channel interface on card '%s' iface '%s': %d\n",
			card2, interface2, c_fd2);
		pthread_exit(NULL);
	    }

	    flush_socket_buffer( c_fd, channels );
	    flush_socket_buffer( c_fd2, channels );

	    mprintf("Connected to interfaces [%s] and [%s]\n",info->interface,interface2);
	    selfd = (c_fd2 > c_fd) ? c_fd2 : c_fd;
	    for(;;) {
		/* Set up file descriptor set for select()ing on sockets */
		FD_ZERO(&port_fds);
		FD_ZERO(&write_fds);
		FD_ZERO(&oob_fds);
		FD_SET(c_fd,&port_fds);
		FD_SET(c_fd,&write_fds);
		FD_SET(c_fd,&oob_fds);

		/* wait for a socket to be ready */
		tv.tv_sec = 1;
		tv.tv_usec = 0;
		ret = select(c_fd+1,&port_fds,NULL,&oob_fds,&tv);
		if( ret <= 0 ) continue;

		if (FD_ISSET(c_fd,&oob_fds)){
			nbytes = recv(c_fd, buf, channels * NBUF + 16, MSG_OOB);
			if(nbytes < 0 ) {
				mprintf("audio_thread: Failed to receive OOB \n");
				err = ioctl(c_fd,SIOC_WANPIPE_SOCK_STATE,0);
				mprintf("audio_thread: Sock state is %s\n",
					(err == 0) ? "CONNECTED" :
					(err == 1) ? "DISCONNECTED" :
					"CONNECTING");
				if (err == 1){
					mprintf("audio_thread: disconnected\n");
					goto disconnected;
				}
			}
		}
		if (FD_ISSET(c_fd,&port_fds)){
			nbytes = recv(c_fd, buf, channels * NBUF + 16, 0);
			if( nbytes == 0 ) {
				mprintf("[%s][%s] Socket hangup ...\n",
					info->card, info->interface);
				goto disconnected;
			} else if( nbytes < 0 ) {
				mprintf("[%s][%s] Socket error[%d] ...\n",
					info->card, info->interface,errno);
				if( errno != EINTR && errno != EAGAIN )
					goto disconnected;
			} else {
				nbytes2 = recv(c_fd2, buf2, channels * NBUF + 16, 0);
				if( nbytes2 == 0 ) {
					mprintf("[%s][%s] Socket hangup ...\n",
						card2, interface2);
					goto disconnected;
				} else if( nbytes2 < 0 ) {
					mprintf("[%s][%s] Socket error[%d] ...\n",
						card2, interface2,errno);
					if( errno != EINTR && errno != EAGAIN )
						goto disconnected;
				} else {
					for(i=0;i<(nbytes-16);i++){
						chbuf[i%channels][i/channels] = bit_inv[buf[16+i]];
						chbuf2[i%channels][i/channels] = bit_inv[buf2[16+i]];
					}
					for (i=info->start_channel,x=0; i <= info->end_channel; ++i,++x){
						c = &((pri_chan *)v->chans)[i];
						if( c->run ){
							if (chan_state[i] != 1){
								mprintf("audio_thread_multi: Starting audio on [%d](%d/%d)\n",
									c->chan_num,i,x);
								chan_state[i]=1;
								// Dont flush here
								// vpb_record_buf_start will flush
								//c->v->rxdf[c->chan_num]->Flush();
								//c->v->txdf[c->chan_num]->Flush();
								encoding[i]=check_encoding(c);
							}
							/* Send data up FIFO - record  */
							prepare_rec_buf_log(chbuf[x],chbuf2[x],
									    c->v,
									    c->chan_num,encoding[i]);
						} else {
							if (chan_state[i] == 1){
								mprintf("audio_thread_multi: Finishing audio on [%d]\n",
									c->chan_num);
								chan_state[i]=0;
								vpb_play_terminate(c->v->handles[c->chan_num]);
								vpb_record_terminate(c->v->handles[c->chan_num]);
							}
						}
					}
				}
			}
		}
		nbytes=0;
	    }

	  disconnected:
	    close(c_fd);
	    close(c_fd2);
	}

	// We should never get here under normal circumstances
	mprintf("Audio thread: abnormal exit for interface [%s] on card '%s'\n",
		info->interface, info->card);
	pthread_exit(NULL);
} //}}}


// Actually places a call out of the system.
int OpenPri::make_isdn_call(uint16_t *mp)
{ //{{{
	USHORT ch = 0;
	int handle = -1;
	char number[21];
	char cid[21];
	int ret;
	int ulayer1=PRI_LAYER_1_ALAW;
	int layer1;
	int dialplan;
	int localdialplan;
	char *calledsubaddr;
	// Set up dial plans
	OP_CARD *cardinfo = (OP_CARD*)m_reg.cardinfo;
	dialplan = cardinfo->dialplan;
	localdialplan = cardinfo->localdialplan;

	memcpy(number,&mp[4],sizeof(char)*21);
	memcpy(cid,&mp[15],sizeof(char)*21);
	ch = (USHORT)mp[2];
	layer1 = (int)mp[3];
	handle=mapdevtohndle(m_reg.cardnum,ch);
	int idx;

	try {
		idx = get_idx_from_ch(&m_reg,ch);
	} catch ( const Wobbly &w ) {
		mprintf("[%d/??] make_isdn_call: Cant work out IDX from ch[%d]!\n",
			m_reg.cardnum, ch);
		return -1;
	}

	pri_chan *chans = (pri_chan*)m_reg.chans;

	if( ! m_pri ) {
		mprintf("[%d/%d] No Primary channel\n",
			m_reg.cardnum, chans[idx].chan_num);
		return 1;
	}

	// Check that the channel is not already in a call
	pthread_mutex_lock(&m_mutex);
	if (chans[idx].state != CH_IDLE) {
		mprintf("[%d/%d] Channel is not idle! is [%d]\n",
			m_reg.cardnum, chans[idx].chan_num, chans[idx].state);
		pthread_mutex_unlock(&m_mutex);
		return 2;
	}
	// Create a new call
	if (!(chans[idx].call = pri_new_call(m_pri))){
		mprintf("[%d/%02d] Couldnt create a new call!\n",
			m_reg.cardnum, chans[idx].chan_num);
		pthread_mutex_unlock(&m_mutex);
		return 3;
	}
	chans[idx].state = CH_SETUP;
	if (layer1 >0){
		ulayer1=layer1;
		mprintf("[%d/%02d] Using passed Encoding (%d)",
			m_reg.cardnum, chans[idx].chan_num, ulayer1);
	} else if (chans[idx].encoding == VPB_ALAW){
		ulayer1 = PRI_LAYER_1_ALAW;
		mprintf("[%d/%02d] Using ALAW in new call",
			m_reg.cardnum, chans[idx].chan_num);
	} else if (chans[idx].encoding == VPB_MULAW){
		ulayer1 = PRI_LAYER_1_ULAW;
		mprintf("[%d/%02d] Using MULAW in new call",
			m_reg.cardnum, chans[idx].chan_num);
	} else {
		ulayer1 = PRI_LAYER_1_ALAW;
		mprintf("[%d/%02d] Defaulting to ALAW in new call",
			m_reg.cardnum, chans[idx].chan_num);
	}

	// Check number for sub-address and copy into local variables
	calledsubaddr=NULL;
	for( int i = 0; number[i] != '\0'; ++i) {
		if(number[i] == '*'){
			calledsubaddr = &number[i+1];
			number[i]     = '\0';
			mprintf("[%d/%02d] Called SubAddress [%s]\n",
				m_reg.cardnum, chans[idx].chan_num, calledsubaddr);
			break;
		}
	}

	ret = pri_call(	m_pri,
			chans[idx].call,
			PRI_TRANS_CAP_SPEECH,
			chans[idx].iface->channel,
			1,
			0,
			cid,
			localdialplan,
			cid,
			PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN,
			number,
			dialplan,
			calledsubaddr,
			ulayer1,
			NULL);

	pthread_mutex_unlock(&m_mutex);

	if (ret) {
		mprintf("[%d/%02d] Couldnt set up call!",
			m_reg.cardnum, chans[idx].chan_num);
		return 4;
	}
	return 0;
} //}}}


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

	FUNCTION.: OpenPri::OpenPri
	AUTHOR...: Ben Kramer
	DATE.....: xx/12/04
	
	Constructor calls HostDSPOpen()

\*--------------------------------------------------------------------------*/
OpenPri::OpenPri(VPBREG &reg)
    : HostDSP( reg )
    , m_cref_info( NULL )
    , m_pri( NULL )
    , m_pri2( NULL )
{
	HostDSPOpen();
}

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

	FUNCTION.: OpenPri::~OpenPri
	AUTHOR...: Ben Kramer
	DATE.....: xx/12/04
	
	Destructor calls HostDSPClose()

\*--------------------------------------------------------------------------*/
OpenPri::~OpenPri()
{
	HostDSPClose();
	free( m_pri );
	free( m_pri2 );
}

void OpenPri::SetCountry( int port, const Country *country )
{ //{{{
	mprintf("[%d/%d] OpenPri::SetCountry: NOT IMPLEMENTED\n",
		m_reg.cardnum, port);
} //}}}

void OpenPri::SetHookState( int port, HookState hookstate )
{ //{{{
	extern Comm *vpb_c;

	switch( hookstate )
	{
	    //XXX We should probably just move the implementation from
	    //    ProcessCommands() to here, but since I'm updating this
	    //    blind for pri keep it simple right now.
	    case VPB_ONHOOK:
	    {
		uint16_t mess[] = { PC_LCODEC_ONHK, PC_CODEC_ONHK, port };

		mprintf("[%d/%d] Set On Hook\n", m_reg.cardnum, port);
		vpb_c->PutMessageVPB(m_reg.cardtypnum, mess);
		break;
	    }
	    case VPB_OFFHOOK:
	    case VPB_FASTOFFHOOK:
	    {
		uint16_t mess[] = { PC_LCODEC_OFFHK, PC_CODEC_OFFHK, port };

		mprintf("[%d/%d] Set Off Hook\n", m_reg.cardnum, port);
		vpb_c->PutMessageVPB(m_reg.cardtypnum, mess);
		break;
	    }
	}
} //}}}

//static uint16_t silence[NBUF];

void OpenPri::PadTxFrame( int port )
{ //{{{
//XXX What, if anything, do we need to do here?
#if 0
	int remains = m_reg.txdf[port]->HowFull() % NBUF;

	if(remains)
		m_reg.txdf[port]->Write(silence, NBUF - remains);
#endif
} //}}}

void OpenPri::WaitForTxEmpty( int port )
{ //{{{
//XXX What, if anything, do we need to do here?
#if 0
	for(;;) {
		int remains = m_reg.txdf[port]->HowFull();

		if(remains == 0)   break;
		if(remains < NBUF) m_reg.txdf[port]->Write(silence, NBUF - remains);

		//mprintf("[%d/%d] WaitForTxEmpty: %d remain to flush\n",
		//	m_reg.cardnum, port, remains);
		usleep( 20 * 1000 );
	}
#endif
} //}}}

void OpenPri::UNListen( unsigned int port )
{ //{{{
	mprintf("[%d/%d] OpenPri::UNListen: not implemented\n",
		m_reg.cardnum, port);
} //}}}

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

	FUNCTION.: OpenPri::chan_state()
	AUTHOR...: Ben Kramer
	DATE.....: xx/12/04
	
	Returns the state of a channel

\*--------------------------------------------------------------------------*/
int OpenPri::chan_state(int ch)
{
	try {
		pri_chan *chans = (pri_chan *)m_reg.chans;
		return chans[get_idx_from_ch(&m_reg,ch)].state;
	} catch ( const Wobbly &w ) {
		mprintf("OpenPri::chan_state: Cant work out IDX from ch[%d]!\n",ch);
		throw;	// Nothing much else we can do here.
	}
}

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

	FUNCTION.: OpenPri::chan_layer1()
	AUTHOR...: Ben Kramer
	DATE.....: xx/07/05
	
	Returns the layer1 of a channel

\*--------------------------------------------------------------------------*/
int OpenPri::chan_layer1(int ch)
{
	try {
		pri_chan *chans = (pri_chan *)m_reg.chans;
		return chans[get_idx_from_ch(&m_reg,ch)].layer1;
	} catch ( const Wobbly &w ) {
		mprintf("OpenPri::chan_layer1: Cant work out IDX from ch[%d]!\n",ch);
		throw;	// Nothing much else we can do here.
	}
}

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

	FUNCTION.: OpenPri::chan_transcap()
	AUTHOR...: Ben Kramer
	DATE.....: 11/07/05
	
	Returns the transmit capability of a channel

\*--------------------------------------------------------------------------*/
int OpenPri::chan_transcap(int ch)
{
	try {
		pri_chan *chans = (pri_chan *)m_reg.chans;
		return chans[get_idx_from_ch(&m_reg,ch)].transcap;
	} catch ( const Wobbly &w ) {
		mprintf("OpenPri::chan_transcap: Cant work out IDX from ch[%d]!\n",ch);
		throw;	// Nothing much else we can do here.
	}
}


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

	FUNCTION.: OpenPri::chan_cinfo()
	AUTHOR...: Ben Kramer
	DATE.....: xx/12/04
	
	Provides the call info of a channel

\*--------------------------------------------------------------------------*/
int OpenPri::chan_cinfo(int ch, VPB_CALL_INFO *info)
{
	try {
		int idx = get_idx_from_ch(&m_reg,ch);
		pri_chan *chans = (pri_chan *)m_reg.chans;
		if( info != NULL && chans[idx].cid != NULL ){
			memcpy(info,chans[idx].cid,sizeof(VPB_CALL_INFO));
			return 0;
		}
	} catch ( const Wobbly &w ) {
		mprintf("OpenPri::chan_cinfo: Cant work out IDX from ch[%d]!\n",ch);
	}
	return -1;
}

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

	FUNCTION.: OpenPri::chan_cause()
	AUTHOR...: Ben Kramer
	DATE.....: xx/12/04
	
	Provides the cause code from the last call

\*--------------------------------------------------------------------------*/
int OpenPri::chan_cause(int ch)
{
	try {
		pri_chan *chans = (pri_chan *)m_reg.chans;
		return chans[get_idx_from_ch(&m_reg,ch)].cause;
	} catch ( const Wobbly &w ) {
		mprintf("OpenPri::chan_cause: Cant work out IDX from ch[%d]!\n",ch);
		throw;	// Nothing much else we can do here.
	}
}

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

	FUNCTION.: OpenPri::call_proceeding()
	AUTHOR...: Ben Kramer
	DATE.....: 11/07/05
	
	Sends a call proceeding

\*--------------------------------------------------------------------------*/
int OpenPri::call_proceeding(int ch)
{
	pri_chan *chans = (pri_chan *)m_reg.chans;
	int idx;

	try {
		idx = get_idx_from_ch(&m_reg,ch);
	} catch ( const Wobbly &w ) {
		mprintf("OpenPri::chan_proceeding: Cant work out IDX from ch[%d]!\n",ch);
		return 1;
	}
	if( chans[idx].state != CH_RING ) {
		mprintf("[%d/%d]Cant send call proceeding/alerting",
			m_reg.cardnum,chans[idx].chan_num);
		mprintf(" not in ring state (state[%d])\n",chans[idx].state);
		return 1;
	}

	mprintf("[%d/%d] Call proceeding/alerting \n",
		m_reg.cardnum,chans[idx].chan_num);

	/* send pri message */
	#if 0
	/* Changed to pri_acknowldege aka PROCEEDING+ALERTING */
	//ret = pri_need_more_info(pri,chans[idx].call,chans[idx].iface->channel,1);
	ret = pri_acknowledge(pri,chans[idx].call,chans[idx].iface->channel,0);
	//ret = pri_progress(pri,chans[idx].call,chans[idx].iface->channel,8);
	//ret = pri_proceeding(pri,chans[idx].call,chans[idx].iface->channel,0);
	if (ret == 0) {
		chans[idx].state = CH_RING;
		return 0;
	} else {
		mprintf("[%d/%d] Failed to send call proceeding!\n",
			m_reg.cardnum,chans[idx].chan_num);
		return 1;
	}
	#endif

	if(pri_acknowledge(m_pri, chans[idx].call, chans[idx].iface->channel, 1) != 0) {
		mprintf("[%d/%d] Failed to send call proceeding/alerting!\n",
			m_reg.cardnum,chans[idx].chan_num);
		return 1;
	}
	chans[idx].state = CH_RING;
	return 0;
}

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

	FUNCTION.: OpenPri::make_isdn_call_api
	AUTHOR...: Ben Kramer
	DATE.....: 11/07/05
	
	Actually places a call out of the system.

\*--------------------------------------------------------------------------*/
int OpenPri::make_isdn_call_api(int ch, char *number, char *cid, int layer1,
				int transcap, unsigned char *lowlayercompatibility)
{
	int ret;
	int idx;
	int ulayer1=PRI_LAYER_1_ALAW;
	char *calledsubaddr;
	int i;
	// Set up dial plans
	OP_CARD *cardinfo = (OP_CARD *)m_reg.cardinfo;
	int dialplan = cardinfo->dialplan;
	int localdialplan = cardinfo->localdialplan;
	pri_chan *chans = (pri_chan *)m_reg.chans;

	try {
		idx = get_idx_from_ch(&m_reg,ch);
	} catch ( const Wobbly &w ) {
		mprintf("OpenPri::make_isdn_call_api: Cant work out IDX from ch[%d]!\n",ch);
		throw;
	}

	if( ! m_pri ) {
		mprintf("[%d/%d] No Primary channel\n",
			m_reg.cardnum,chans[idx].chan_num);
		return 1;
	}

	// Check that the channel is not already in a call
	pthread_mutex_lock(&m_mutex);
	if (chans[idx].state != CH_IDLE) {
		mprintf("[%d/%d] Channel is not idle! is [%d]\n",
			m_reg.cardnum,chans[idx].chan_num,chans[idx].state);
		pthread_mutex_unlock(&m_mutex);
		return 2;
	}
	// Create a new call
	if (!(chans[idx].call = pri_new_call(m_pri))){
		mprintf("[%d/%02d] Couldnt create a new call!\n",
			m_reg.cardnum,chans[idx].chan_num);
		pthread_mutex_unlock(&m_mutex);
		return 3;
	}
	chans[idx].state = CH_SETUP;
	if (layer1 > 0){
		ulayer1=layer1;
		mprintf("[%d/%02d] Using passed Encoding (%d)\n",
			m_reg.cardnum,chans[idx].chan_num,ulayer1);
	} else if (chans[idx].encoding == VPB_ALAW){
		ulayer1 = PRI_LAYER_1_ALAW;
		mprintf("[%d/%02d] Using ALAW in new call\n",
			m_reg.cardnum,chans[idx].chan_num);
	} else if (chans[idx].encoding == VPB_MULAW){
		ulayer1 = PRI_LAYER_1_ULAW;
		mprintf("[%d/%02d] Using MULAW in new call\n",
			m_reg.cardnum,chans[idx].chan_num);
	} else {
		ulayer1 = PRI_LAYER_1_ALAW;
		mprintf("[%d/%02d] Defaulting to ALAW in new call\n",
			m_reg.cardnum,chans[idx].chan_num);
	}
	chans[idx].layer1 = ulayer1;

	// Check for trans cap
	if(transcap < 1){
		transcap = PRI_TRANS_CAP_SPEECH;
	} else if( transcap == PRI_TRANS_CAP_DIGITAL
		|| transcap == PRI_TRANS_CAP_RESTRICTED_DIGITAL ){
		chans[idx].encoding = VPB_RAW;
		ulayer1 = -1;
	}
	chans[idx].transcap = transcap;

	mprintf("[%d/%02d] Checking for Called SubAddress in [%s]\n",
		m_reg.cardnum,chans[idx].chan_num, number);
	// Check number for sub-address and copy into local variables
	calledsubaddr=NULL;
	i=0;
	while(number[i] != '\0'){
		if(number[i] == '*'){
			calledsubaddr = &number[i+1];
			number[i]='\0';
			mprintf("[%d/%02d] Called SubAddress [%s]\n",
				m_reg.cardnum,chans[idx].chan_num, calledsubaddr);
			break;
		}
		i++;
	}
	mprintf("[%d/%02d] Done Checking for Called SubAddress in [%s]\n",
		m_reg.cardnum,chans[idx].chan_num, number);

	ret = pri_call(	m_pri,
			chans[idx].call,
			transcap,
			chans[idx].iface->channel,
			1,
			0,
			cid,
			localdialplan,
			cid,
			PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN,
			number,
			dialplan,
			calledsubaddr,
			ulayer1,
			lowlayercompatibility);

	pthread_mutex_unlock(&m_mutex);

	if (ret) {
		mprintf("[%d/%02d] Couldnt set up call!",m_reg.cardnum,chans[idx].chan_num);
		return 4;
	}
	return 0;
}

int OpenPri::chan_lowlayercompatibility(int ch, unsigned char *lowlayercompatibility)
{
	int idx;

	try {
		idx = get_idx_from_ch(&m_reg,ch);
	} catch ( const Wobbly &w ) {
		mprintf("OpenPri::chan_lowlayercompatibility: Cant work out IDX from ch[%d]!\n",ch);
		return -1;
	}
	pri_chan *chans = (pri_chan *)m_reg.chans;
	for(int i=0; i < chans[idx].lowlayercompatibility[0] + 1; ++i){
		lowlayercompatibility[i]=chans[idx].lowlayercompatibility[i];
		mprintf("LLC (%d) (%02X)",i,lowlayercompatibility[i]);
	}
	return VPB_OK;
}

#if 0
static int slot2chan(int slotmap, int len)
{
	int x;
	for (x=1;x<=len;x++){
		if(slotmap & (1 << (len - x)))
			return(x);
	}
	return 0;
}
#endif

static int findfreechan(VPBREG *v)
{
	pri_chan *chans;
	int	num_chans,i;
	chans = (pri_chan *)v->chans;
	num_chans = v->numch;
	for (i=0;i<num_chans;i++){
		if (chans[i].state == CH_IDLE){
			return(i);
		}
	}
	return(-1);
}

static int wanconnect(char *card, char *port)
{
	struct wan_sockaddr_ll  sa;
	int dfd;

	/* Connect to the Wanpipe interface */
	memset(&sa,0,sizeof(struct wan_sockaddr_ll));
	if ((dfd = socket(AF_WANPIPE, SOCK_RAW, 0)) < 0) {
		mprintf("wanconnect: Failed to make wanpipe socket\n");
		return -1;
	}

	strcpy((char *)sa.sll_device, port);
	strcpy((char *)sa.sll_card, card);
	sa.sll_protocol = htons(PVC_PROT);
	sa.sll_family=AF_WANPIPE;

	if (bind(dfd, (struct sockaddr *)&sa, sizeof(struct wan_sockaddr_ll)) < 0) {
		mprintf("wanconnect: Failed to bind to wanpipe socket\n");
		return -2;
	}

	return dfd;
}
