///////////////////////////////////////////////////////////////////////////////////////////////
///	\file	SangomaPort.cpp
///	\brief	This file contains the implementation of the SangomaPort class
///////////////////////////////////////////////////////////////////////////////////////////////

#if defined(__WINDOWS__)
# ifndef _DEBUG				
#  pragma optimize("2gt",on)		// Windows specific (MS_SPECIFIC), enables optimization in Release mode
# endif
#endif

#include <sstream>
#include <time.h>
#include "SangomaPort.h"
#include "driver_configurator.h"

#define DBG_PORT	if(0)printf
#define INFO_PORT	if(0)printf
//This macro is for Error reporting and should NOT be disabled.
#define ERR_PORT	printf("Error:%s():line:%d: ", __FUNCTION__, __LINE__);printf

#define DBG_OPEN	if(0)printf
#define PORT_FUNC()	if(0)printf("%s(): Line: %u\n", __FUNCTION__, __LINE__)

extern void print_data_buffer(unsigned char *buffer, int buffer_length);

using namespace Sangoma;

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		Configuration::Configuration()
///	\brief	Constructor (for the Configuration structure)
///	\author	J. Markwordt
///	\date	10/04/2007
///////////////////////////////////////////////////////////////////////////////////////////////	
Configuration::Configuration()
: 	e1Ort1(E1),
	Framing(E1_CAS_CRC4_ON),
	lineCoding(OFF),
	HighImpedanceMode(true),
	TxTristateMode(false)
{
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		Statistics::Statistics()
///	\brief	Constructor (for the Statistics structure)
///	\author	J. Markwordt
///	\date	10/04/2007
///////////////////////////////////////////////////////////////////////////////////////////////	
Statistics::Statistics()
:	TimeLastOpened(0),
	TimeLastClosed(0),
	TimeLastFoundSync(0),
	TimeLastLostSync(0),
	OpenCount(0),
	CloseCount(0),
	FoundSyncCount(0),
	LostSyncCount(0),
	BytesReceived(0),
	BytesSent(0)
{
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		Alarms::Alarms()
///	\brief	Constructor (for the Alarms structure)
///	\author	J. Markwordt
///	\date	10/19/2007
///////////////////////////////////////////////////////////////////////////////////////////////
Alarms::Alarms()
:	LossOfSignal(false),
	ReceiveLossOfSignal(false),
	AlternateLossOfSignalStatus(false),
	OutOfFrame(false),
	Red(false),
	AlarmIndicationSignal(false),
	LossOfSignalingMultiframe(false),
	LossOfCrcMultiframe(false),
	OutOfOfflineFrame(false),
	ReceiveLossOfSignalV(false),
	Yellow(false)
{
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		DriverVersion::DriverVersion()
///	\brief	Constructor (for the DriverVersion structure)
///	\author	J. Markwordt
///	\date	10/19/2007
///////////////////////////////////////////////////////////////////////////////////////////////
DriverVersion::DriverVersion()
:	Major(0),
	Minor(0),
	Build(0),
	Revision(0)
{
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		PortEvent::PortEvent()
///	\brief	Constructor (for the PortEvent structure)
///	\author	J. Markwordt
///	\date	10/18/2007
///////////////////////////////////////////////////////////////////////////////////////////////	
PortEvent::PortEvent()
:	EventType(Unsignalled),
	EventHandle(NULL)
{
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::SangomaPort(const unsigned int card_number, const unsigned int port_number, 
///		const PortEvent & event, const BufferSettings & ReceiveBufferSettings,
///		const BufferSettings & TransmitBufferSettings)
///	\brief	Constructor
///
///			Sets the size and number of the port's buffers for transmit and recieve (see the
///			BufferSettings structure for more details).  Receive or transmit can be disabled,
///			allowing more resources to be devoted to the other direction by setting the 
///			NumberOfBuffersPerPort for their respective BufferSettings structures.
///	\param	card_number						unsigned int number of the SangomaCard that this port is part of
///											(zero indexed)
///	\param	port_number						unsigned int number of the port that this object represents
///											(this is relative to the card and zero indexed, so the first
///											port of both card 1 and 2 will be 0)
///	\param	event							PortEvent structure to signal port events with (contains HANDLE
///											to signal the event with and EventType to set indicating the type of event)
///	\param	ReceiveBufferSettings			BufferSettings to configure the host memory buffers 
///											for receive operations on this port (set NumberOfBuffersPerPort
///											to 0 to disable receiving data on this port)
///	\param	TransmitBufferSettings		BufferSettings to configure the host memory buffers 
///											for transmit operations on this port (set NumberOfBuffersPerPort
///											to 0 to disable transmitting data on this port)
///	\author	J. Markwordt
///	\date	10/04/2007
///////////////////////////////////////////////////////////////////////////////////////////////	
SangomaPort::SangomaPort(const unsigned int card_number, const unsigned int port_number, 
		const BufferSettings & ReceiveBufferSettings,
		const BufferSettings & TransmitBufferSettings)
:	CardNumber(card_number),
	PortNumber(port_number),
	IsOpen(false),
	CurrentConfiguration(),
	PortStatistics()
{
	PORT_FUNC();

	LastError = NO_ERROR_OCCURRED;

	DBG_PORT("%s(): card_number: %u, port_number: %u\n", __FUNCTION__, card_number, port_number);

	InitializeCriticalSection(&cs_CriticalSection);

	SangomaInterface = new sangoma_interface(CardNumber, PortNumber);
	DBG_PORT("SangomaInterface: 0x%p\n", SangomaInterface);

	this->ReceiveBufferSettings = ReceiveBufferSettings;
	this->TransmitBufferSettings = TransmitBufferSettings;

	// Calculate the size of a host memory buffer by multiplying the maximum buffer size as defined
	// by the Sangoma API by a multiplier (buffer_multiplier_factor) for both the receive and transmit buffers
	ReceiveBuffer = new PortBuffer(sizeof(RX_DATA_STRUCT) * ReceiveBufferSettings.BufferMultiplierFactor);
	TransmitBuffer = new PortBuffer(sizeof(TX_DATA_STRUCT) * TransmitBufferSettings.BufferMultiplierFactor);

	pTxFile = NULL;
}
	
///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::~SangomaPort()
///	\brief	Destructor
///	\author	J. Markwordt
///	\date	10/04/2007
///////////////////////////////////////////////////////////////////////////////////////////////	
SangomaPort::~SangomaPort()
{
	PORT_FUNC();
	//EnterPortCriticalSection();
	//LeavePortCriticalSection();
	//PORT_FUNC();

	// If the has not been properly closed, close it
	if (IsOpen) {
		Close();
	}

	// Deallocate Rx/Tx Port buffers
	delete ReceiveBuffer;
	delete TransmitBuffer;

	if(SangomaInterface != NULL){
		delete SangomaInterface;
		SangomaInterface = NULL;
	}

	if (pTxFile) {
		fclose( pTxFile );
	}

	PORT_FUNC();
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::~SangomaPort(const Configuration & config)
///	\brief	Implementation of Sangoma's low level API call to set port configuration - T1/E1.
///	\author	David Rokhvarg
///	\date	11/08/2007
///////////////////////////////////////////////////////////////////////////////////////////////	
int SangomaPort::SetPortConfiguration(const Configuration & config)
{
	int				return_code;
	sdla_fe_cfg_t	sdla_fe_cfg;
	buffer_settings_t buffer_settings;
	std::ostringstream error_msg;

	driver_configurator *p_drv_cfg_obj = new driver_configurator();
	if(p_drv_cfg_obj->init(CardNumber, SangomaInterface->get_mapped_port_number())){
		error_msg << "ERROR: " << __FUNCTION__ << "(): failed to initialize Sangoma Driver Configuration object";
		LastError = error_msg.str();
		delete p_drv_cfg_obj;
		return 1;
	}

	memset(&sdla_fe_cfg, 0x00, sizeof(sdla_fe_cfg_t));
	switch(config.e1Ort1)
	{
	case T1:
		FE_LBO(&sdla_fe_cfg) = WAN_T1_LBO_0_DB;//important to set to a valid value for both T1 and E1!!
		FE_MEDIA(&sdla_fe_cfg) = WAN_MEDIA_T1;
		switch(config.lineCoding)
		{
		case OFF:
			FE_LCODE(&sdla_fe_cfg) = WAN_LCODE_AMI;
			break;
		case ON:
			FE_LCODE(&sdla_fe_cfg) = WAN_LCODE_B8ZS;
			break;
		default:
			error_msg << "ERROR: " << __FUNCTION__ << "(): T1 invalid lineCoding " << config.lineCoding;
			LastError = error_msg.str();
			delete p_drv_cfg_obj;
			return 1;
		}

		switch(config.Framing)
		{
		case T1_EXTENDED_SUPERFRAME:
			FE_FRAME(&sdla_fe_cfg) = WAN_FR_ESF;
			break;
		case T1_SUPERFRAME:
			FE_FRAME(&sdla_fe_cfg) = WAN_FR_D4;
			break;
		default:
			error_msg << "ERROR: " << __FUNCTION__ << "(): T1: invalid Framing " << config.Framing;
			LastError = error_msg.str();
			delete p_drv_cfg_obj;
			return 1;
		}

		FE_SIG_MODE(&sdla_fe_cfg)	= WAN_TE1_SIG_CCS;
		FE_LBO(&sdla_fe_cfg)		= WAN_T1_LBO_0_DB;
		//320*24=7680
		TxMtu = 7680;
		break;

	case E1:
		FE_LBO(&sdla_fe_cfg) = WAN_E1_120;//important to set to a valid value for both T1 and E1!!
		FE_MEDIA(&sdla_fe_cfg) = WAN_MEDIA_E1;
		switch(config.lineCoding)
		{
		case OFF:
			FE_LCODE(&sdla_fe_cfg) = WAN_LCODE_AMI;
			break;
		case ON:
			FE_LCODE(&sdla_fe_cfg) = WAN_LCODE_HDB3;
			break;
		default:
			error_msg << "ERROR: " << __FUNCTION__ << "(): E1 invalid lineCoding " << config.lineCoding;
			LastError = error_msg.str();
			delete p_drv_cfg_obj;
			return 1;
		}

		//FE_FRAME(&sdla_fe_cfg) = WAN_FR_CRC4;
		switch(config.Framing)
		{
		case E1_CAS_CRC4_ON:
			FE_FRAME(&sdla_fe_cfg) = WAN_FR_CRC4;
			FE_SIG_MODE(&sdla_fe_cfg) = WAN_TE1_SIG_CAS;
			//240*31=7440
			TxMtu = 7440;
			break;
		case E1_CAS_CRC4_OFF:
			FE_FRAME(&sdla_fe_cfg) = WAN_FR_NCRC4;
			FE_SIG_MODE(&sdla_fe_cfg) = WAN_TE1_SIG_CAS;
			//240*31=7440
			TxMtu = 7440;
			break;
		case E1_CCS_CRC4_ON:
			FE_FRAME(&sdla_fe_cfg) = WAN_FR_CRC4;
			FE_SIG_MODE(&sdla_fe_cfg) = WAN_TE1_SIG_CCS;
			//240*31=7440
			TxMtu = 7440;
			break;
		case E1_CCS_CRC4_OFF:
			FE_FRAME(&sdla_fe_cfg) = WAN_FR_NCRC4;
			FE_SIG_MODE(&sdla_fe_cfg) = WAN_TE1_SIG_CCS;
			//240*31=7440
			TxMtu = 7440;
			break;

		case E1_CAS_UNFRAMED:
			FE_FRAME(&sdla_fe_cfg) = WAN_FR_UNFRAMED;
			FE_SIG_MODE(&sdla_fe_cfg) = WAN_TE1_SIG_CAS;
			//240*32=7680
			TxMtu = 7680;
			break;
		case E1_CCS_UNFRAMED:
			FE_FRAME(&sdla_fe_cfg) = WAN_FR_UNFRAMED;
			FE_SIG_MODE(&sdla_fe_cfg) = WAN_TE1_SIG_CCS;
			//240*32=7680
			TxMtu = 7680;
			break;

		default:
			error_msg << "ERROR: " << __FUNCTION__ << "(): E1: invalid Framing " << config.Framing;
			LastError = error_msg.str();
			delete p_drv_cfg_obj;
			return 1;
		}
		break;

	default:
		error_msg << "ERROR: " << __FUNCTION__ << "(): Invalid configuration! Not T1 or E1.";
		LastError = error_msg.str();
		delete p_drv_cfg_obj;
		return 1;
	}//switch(config.e1Ort1)

	//FE_REFCLK(&sdla_fe_cfg)		= 0;	//optional

	///////////////////////////////////////////////////////////////////////////////
	//DAVIDR: for "TAPPING" set clock to WAN_NORMAL_CLK
	//FE_CLK(&sdla_fe_cfg)			= WAN_NORMAL_CLK;
	//DAVIDR: for "PLAYBACK/RECORD" set clock to WAN_NORMAL_CLK or WAN_MASTER_CLK,
	//			depending on how the other side	is configured!

	if (config.Master) {
		FE_CLK(&sdla_fe_cfg)			= WAN_MASTER_CLK;
	} else {
		FE_CLK(&sdla_fe_cfg)			= WAN_NORMAL_CLK;
	}
	///////////////////////////////////////////////////////////////////////////////

	//DAVIDR: High Impedance can be used only with external filters installed!!
	FE_HIMPEDANCE_MODE(&sdla_fe_cfg) = (config.HighImpedanceMode == true ? WANOPT_YES:WANOPT_NO);
	FE_RX_SLEVEL(&sdla_fe_cfg) = config.MaxCableLoss;
	FE_TXTRISTATE(&sdla_fe_cfg) = (config.TxTristateMode == true ? WANOPT_YES:WANOPT_NO);

	///////////////////////////////////////////////////////////////////////////////
	//The same buffer settings will be used in both directions - Tx and Rx.
	buffer_settings.buffer_multiplier_factor = ReceiveBufferSettings.BufferMultiplierFactor;
	buffer_settings.number_of_buffers_per_api_interface = ReceiveBufferSettings.NumberOfBuffersPerPort;

	return_code = p_drv_cfg_obj->set_t1_e1_configuration(&sdla_fe_cfg, &buffer_settings);

	delete p_drv_cfg_obj;
	return return_code;
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::Open(const Configuration & config)
///	\brief	Opens this port, setting its current configuration to the one provided
///	\author	J. Markwordt
///	\date	10/05/2007
///////////////////////////////////////////////////////////////////////////////////////////////
bool SangomaPort::Open(const Configuration & config)
{
	LastError = NO_ERROR_OCCURRED;	 // Reset so if the function is successful GetLastError() will return NO_ERROR_OCCURRED 

	EnterPortCriticalSection();

	PORT_FUNC();
	DBG_OPEN("%s(): CardNumber: %u, PortNumber: %u (mapped port: %u)\n", __FUNCTION__,
		CardNumber, PortNumber, SangomaInterface->get_mapped_port_number());

	DBG_PORT("%s, Framing: %u, lineCoding: %u, HighImpedanceMode: %u\n", 
		(config.e1Ort1 == T1 ? "T1" :
		(config.e1Ort1 == E1 ? "E1" : "Error: Not T1 or E1")),
		config.Framing, 
		config.lineCoding,
		config.HighImpedanceMode);

	// If the port is already opened, return false, it must be Closed first before it can be reopened
	if (IsOpen) {
		PORT_FUNC();
		std::ostringstream error_msg;
		error_msg << "ERROR: SangomaPort::Open() failed to open, port " << GetPortNumber() << " is already opened.";
		error_msg << "SangomaPort::Close() must be called before attempting to reopen.";
		LastError = error_msg.str();

		// Return false to indicate the open failed, but do not change 
		// IsOpen since port is currently open in a valid configuration
		LeavePortCriticalSection();
		return false;
	}

	// Apply configuration to the port after checking that the configuration is valid.
	if(SetPortConfiguration(config)){
		PORT_FUNC();

		std::ostringstream error_msg;
		error_msg << "ERROR: SangomaPort::SetPortConfiguration() failed for port " << GetPortNumber() << ".";
		LastError = error_msg.str();

		Event.EventHandle = NULL;
		ERR_PORT("SetPortConfiguration() failed! (CardNumber: %u, PortNumber: %u (mapped port: %u))\n",
			CardNumber, PortNumber, SangomaInterface->get_mapped_port_number());
		//axit(1);
		LeavePortCriticalSection();
		return false;
	}

	// Perform any operations necessary to open the port with the new configuration and prepare to receive data
	if(SangomaInterface->init()){
		PORT_FUNC();

		std::ostringstream error_msg;
		error_msg << "ERROR: SangomaPort: SangomaInterface->init() failed for port " << GetPortNumber() << ".";
		LastError = error_msg.str();

		Event.EventHandle = NULL;
		ERR_PORT("SangomaInterface->init() failed! (CardNumber: %u, PortNumber: %u (mapped port: %u))\n",
			CardNumber, PortNumber, SangomaInterface->get_mapped_port_number());
		//exit(1);
		LeavePortCriticalSection();
		return false;
	}

	if(SangomaInterface->set_buffer_multiplier(&wp_api, ReceiveBufferSettings.BufferMultiplierFactor)){
		PORT_FUNC();

		std::ostringstream error_msg;
		error_msg << "ERROR: SangomaPort: SangomaInterface->set_buffer_multiplier() failed for port " << GetPortNumber() << ".";
		LastError = error_msg.str();

		Event.EventHandle = NULL;
		ERR_PORT("SangomaInterface->set_buffer_multiplier() failed! (CardNumber: %u, PortNumber: %u (mapped port: %u))\n",
			CardNumber, PortNumber, SangomaInterface->get_mapped_port_number());
		//exit(1);
		LeavePortCriticalSection();
		return false;
	}

	Event.EventHandle = SangomaInterface->get_wait_object_reference();

	// Store the time that this successful Open() occurred, both for statistics
	// purposes and so that we can calculate time elapsed since opening for 
	// determining whether we have acheived syncronization
	time(&PortStatistics.TimeLastOpened);

	DBG_PORT("%s(): PortStatistics.TimeLastOpened:%u (ptr: 0x%p)\n",
		__FUNCTION__, (uint32_t)PortStatistics.TimeLastOpened, &PortStatistics.TimeLastOpened);

	IsOpen = true;
	
	PORT_FUNC();
	// Save the current configuration since it was successfully applied to the port
	CurrentConfiguration = config;		

	LeavePortCriticalSection();
	// Unless the port was successfully opened, IsOpen will still be false
	return IsOpen;
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::Close()
///	\brief	Closes this port
///	\return	true if the port was successfully closed, false if
///	\author	J. Markwordt
///	\date	10/05/2007
///////////////////////////////////////////////////////////////////////////////////////////////
bool SangomaPort::Close()
{
	EnterPortCriticalSection();
	PORT_FUNC();

	LastError = NO_ERROR_OCCURRED;	 // Reset so if the function is successful GetLastError() will return NO_ERROR_OCCURRED 

	DBG_OPEN("%s(): CardNumber: %u, PortNumber: %u (mapped port: %u)\n", __FUNCTION__,
		CardNumber, PortNumber, SangomaInterface->get_mapped_port_number());

	// If the port is not currently opened just return true, there is nothing to do
	if (!IsOpen) {
		LeavePortCriticalSection();
		return true;		// this is not an error, there is just nothing to do, already closed
	}

	IsOpen = false;

	///////////////////////////////////////////////////////////////////////////////////////
	// Signal the wait object BEFORE deleting it so anyone who is waiting will not use it anymore
	//sangoma_wait_obj_signal(SangomaInterface->get_wait_object_reference());
	///////////////////////////////////////////////////////////////////////////////////////

	// Perform actions to close and cleanup the port
	bool close_successful = false;
	if(SangomaInterface->cleanup() == 0){
		close_successful = true;
	}else{
		ERR_PORT("CardNumber: %u, PortNumber: %u (mapped port: %u): failed the cleanup()!!\n",
			CardNumber, PortNumber, SangomaInterface->get_mapped_port_number());
	}

	if (close_successful) {
		// Store the time that this successful Close() occurred for statistical purposes
		time(&PortStatistics.TimeLastClosed);
		LeavePortCriticalSection();
		return true;
	}

	LeavePortCriticalSection();
	return false;
}

bool SangomaPort::GetIsOpen()
{
	bool boolTmpIsOpen;

	if(TryEnterPortCriticalSection()){
		//Do NOT wait! We only want to check, not actually wait for another thread to complete its work.
		return false;
	}

	boolTmpIsOpen = IsOpen;
	LeavePortCriticalSection();

	return boolTmpIsOpen;
}

///////////////////////////////////////////////////////////////////////////////////////////////
///		\fn			efficient_32bit_byte_copy(unsigned char **p_destination_buf, unsigned char **p_source_buf, unsigned int num_bytes_to_copy){
///		\brief		Copies data bytes from one byte buffer to another. This routine is optimized
///					for 32 bit processors. This routine does not handle detection of overrunning
///					the buffer boundaries. That responsibility is left to the calling routine.
///
///		\param		**p_destination_buf	: Address of a pointer to the destination byte buffer
///		\param		**p_source_buf		: Address of a pointer to the source byte buffer.
///		\param		num_bytes_to_copy	: Number of bytes to copy from the source buffer to the 
///							destination buffer.
///		\author		Andrew Park
///		\date       07/18/2005
///////////////////////////////////////////////////////////////////////////////////////////////
const unsigned int BITMASK_FOR_LEAST_SIGNIFICANT_TWO_BITS = 0x3;
void efficient_32bit_byte_copy(unsigned char **p_destination_buf, unsigned char **p_source_buf, unsigned int num_bytes_to_copy){
		// We do not need to do a NULL check here, because the function that calls this checks.  NOTE: since this is a public
		// function, there does still exist the possibility that NULL arrays may be passed in, but I didn't want to mess with 
		// the efficiency here if at all possible

		//Transfer bytes in chunks of 32 bit words
		int num_words = static_cast<int>(num_bytes_to_copy >> 2);	//divide by 4 to get the integral # words
		unsigned int *p_destination_buf_32bit = reinterpret_cast<unsigned int*>(*p_destination_buf);
		unsigned int *p_source_buf_32bit = reinterpret_cast<unsigned int*>(*p_source_buf);
		unsigned int i=0;
		for(i=0;i<(unsigned int)num_words;i++){
			*p_destination_buf_32bit++ = *p_source_buf_32bit++;
		}
		//Convert pointers back from 32 bit to byte pointers
		*p_destination_buf = reinterpret_cast<unsigned char*>(p_destination_buf_32bit);
		*p_source_buf = reinterpret_cast<unsigned char*>(p_source_buf_32bit);

		//Transfer remaining bytes individually
		unsigned int remaining_bytes = static_cast<unsigned int>(num_bytes_to_copy & BITMASK_FOR_LEAST_SIGNIFICANT_TWO_BITS);
		for(i=0;i<remaining_bytes;i++) {
			*(*p_destination_buf)++ = *(*p_source_buf)++;
		}
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::Read()
///	\brief	Reads data and places the data in Port's ReceiveBuffer.
///			If ReceiveBuffer is not large enough to contain all of the data in the
///			Sangoma API receive buffer, an error ERROR_INVALID_DESTINATION_BUFFER_TOO_SMALL 
//			will be returned as this is a configuration error.
///	\return	One of the following Status or Error codes:
///			LineDisconnected - Sangoma API inidcated Line Disconnect.
///			LineConnected - Sangoma API inidcated Line Connect.
///			ReadDataAvailable - Sangoma API inidcated there is receive buffer ready in API queue.
///			ERROR_NO_DATA_AVILABLE - lower level code reported no data available in host memory
///			ERROR_INVALID_DESTINATION_BUFFER - a NULL destination buffer was provided so nothing was read
///			ERROR_INVALID_DESTINATION_BUFFER_TOO_SMALL - a destination buffer was provided that
///					is too small to contain a full buffer's worth of data
///			ERROR_PORT_NOT_OPEN - Read attempted before the port had been opened
///	\author	David Rokhvarg (davidr@sangoma.com)
///	\date	11/15/2007
///////////////////////////////////////////////////////////////////////////////////////////////
int SangomaPort::Read(unsigned int out_flags)
{
	int return_code = ERROR_NO_DATA_AVILABLE;
	
	EnterPortCriticalSection();

	// The port must have been successfully opened with a valid configuration before a read can be performed
	if (!IsOpen) {
		std::ostringstream error_msg;
		error_msg << "ERROR: SangomaPort::Read(unsigned char*, int) attempted when port " << GetPortNumber() << " had not been opened yet.";
		ERR_PORT("Port %u: SangomaPort::Read() attempted when port had not been opened yet!\n", GetPortNumber());
		LastError = error_msg.str();
		LeavePortCriticalSection();
		return ERROR_PORT_NOT_OPEN;
	}

	switch(out_flags)//note that 'out_flags' is NOT treated as BitMap anymore - it is expected only a SINGLE bit to be set
	{
	case POLLIN:
		if(SangomaInterface->read_data(&rxhdr, ReceiveBuffer->GetPortBuffer(), ReceiveBuffer->GetPortBufferSize()))
		{
			std::ostringstream error_msg;
			error_msg << "ERROR: read_data() failed! Port: " << GetPortNumber() << " error description: DeviceIoControl() failed\n";
			LastError = error_msg.str();
			LeavePortCriticalSection();
			return ERROR_DEVICE_IOCTL;
		}
		break;
	case POLLPRI:
		if(SangomaInterface->read_event(&wp_api))
		{
			std::ostringstream error_msg;
			error_msg << "ERROR: read_event() failed! Port: " << GetPortNumber() << " error description: DeviceIoControl() failed\n";
			LastError = error_msg.str();
			LeavePortCriticalSection();
			return ERROR_DEVICE_IOCTL;
		}
		break;
	case POLLOUT:
		return_code = TransmitBufferAvailable;
		break;
	default:
		std::ostringstream error_msg;
		error_msg << "ERROR: SangomaPort::Read(unsigned int out_flags) for port " << GetPortNumber() << " invalid 'out_flags'=" << out_flags << "\n";
		ERR_PORT("Port %u: SangomaPort::Read() invalid out_flags=0x%X!\n", GetPortNumber(), out_flags);
		LastError = error_msg.str();
		LeavePortCriticalSection();
		return ERROR_INVALID_READ_REQUEST;
	}//switch()

	if (POLLIN == out_flags) 
	{
		switch (rxhdr.operation_status) 
		{
		case SANG_STATUS_RX_DATA_AVAILABLE:
			return_code = ReadDataAvailable;
			ReceiveBuffer->SetUserDataLength(rxhdr.data_length);
			break;
		default:
			ERR_PORT("Port %u: Rx Error: Operation Status: %s (%d)\n", GetPortNumber(),
				SDLA_DECODE_SANG_STATUS(rxhdr.operation_status), rxhdr.operation_status);
			return_code = ERROR_NO_DATA_AVILABLE;
			break;
		}
	}

	if (POLLPRI == out_flags) 
	{
		wp_api_event_t	*wp_tdm_api_event = &wp_api.wp_cmd.event;

		switch (wp_api.wp_cmd.result)
		{
		case SANG_STATUS_SUCCESS:
				
			switch(wp_tdm_api_event->wp_tdm_api_event_type)
			{
			case WP_TDMAPI_EVENT_LINK_STATUS:
				if(wp_tdm_api_event->wp_tdm_api_event_link_status == WAN_EVENT_LINK_STATUS_CONNECTED){
					return_code = LineConnected;
				}else{
					return_code = LineDisconnected;
				}
				break;
			case WP_TDMAPI_EVENT_ALARM:
				if(wp_tdm_api_event->wp_tdm_api_event_alarm == 0){
					return_code = LineConnected;
				}else{
					return_code = LineDisconnected;
				}
				break;
			default:
				//FIXME: Remove this
				ERR_PORT("Port %u INVALID EVENT Type 0x%X\n",GetPortNumber(),wp_tdm_api_event->wp_tdm_api_event_type);
				exit(1);
			}
			break;

		default:

			if (rxhdr.operation_status == SANG_STATUS_RX_DATA_AVAILABLE) {
				return_code = ReadDataAvailable;
			} else {
				ERR_PORT("Port %u: Event Error: Operation Status: %s (%d)\n", GetPortNumber(),
					SDLA_DECODE_SANG_STATUS(rxhdr.operation_status), rxhdr.operation_status);
				return_code = ERROR_NO_DATA_AVILABLE;
			}
			//FIXME: Remove this
			ERR_PORT("Port %u INVALID EVENT\n",GetPortNumber());
			exit(1);
			break;
		}
	}

	if(return_code == ReadDataAvailable){
		PortStatistics.BytesReceived += ReceiveBuffer->GetUserDataLength();
	}

	LeavePortCriticalSection();
	return return_code;
}


///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::Write(unsigned char* p_source_buffer, const int bytes_to_write)
///	\brief	Writes the specified number of bytes from the source buffer to the port
///	\param	p_source_buffer		Pointer to an array of unsigned chars used as the source of the
///								data to write.  Assumes that it contains at least bytes_to_write
///								number of bytes.
///	\param	bytes_to_write		the number of bytes to write to the port
///	\return	Zero if successful (all bytes are written) or one of the following error codes:
///			ERROR_NO_DATA_WRITTEN - no data was able to be written on the port
///			ERROR_INVALID_SOURCE_BUFFER - a NULL source buffer was provided so nothing was written
///			ERROR_PORT_NOT_OPEN - Write attempted before the port had been opened
///	\author	J. Markwordt
///	\date	10/15/2007
///////////////////////////////////////////////////////////////////////////////////////////////
int SangomaPort::Write(unsigned char* p_source_buffer, const unsigned int bytes_to_write)
{
	EnterPortCriticalSection();
	//PORT_FUNC();
	
	// The port must have been successfully opened with a valid configuration before a write can be performed
	if (!IsOpen) {
		std::ostringstream error_msg;
		error_msg << "ERROR: SangomaPort::Write(unsigned char*, int) attempted when port " << GetPortNumber() << " had not been opened yet.";
		LastError = error_msg.str();
		LeavePortCriticalSection();
		return ERROR_PORT_NOT_OPEN;
	}

	// Check to make sure the source is valid before attempting to access it
	bool invalid_source_buffer = (NULL == p_source_buffer);
	if (invalid_source_buffer) {
		std::ostringstream error_msg;
		error_msg << "ERROR: SangomaPort::Write(unsigned char*, int) attempted with invalid source buffer on port " << GetPortNumber() << ".";
		LastError = error_msg.str();
		LeavePortCriticalSection();
		return ERROR_INVALID_SOURCE_BUFFER;
	}

	// Perform Sangoma API functions to write data to the port.
	if(bytes_to_write > TransmitBuffer->GetPortBufferSize()){
		std::ostringstream error_msg;
		error_msg << "ERROR: SangomaPort::Write(unsigned char*, int) attempted with Tx data length exceeding maximum " << GetPortNumber() << ".";
		LastError = error_msg.str();
		LeavePortCriticalSection();
		return ERROR_INVALID_TX_DATA_LENGTH;
	}
	
	TransmitBuffer->SetUserDataLength(bytes_to_write);
	//
	//If src and dst buffers are the same, there is no need to copy the data because
	//it is already there. This is the case for TxFile (see code in WriteChunkOfTxFile()). 
	//
	if (TransmitBuffer->GetUserDataBuffer() != p_source_buffer) {
		memcpy(TransmitBuffer->GetUserDataBuffer(), p_source_buffer, bytes_to_write);
	}

	int number_of_bytes_written = 0;

	memset(&txhdr, 0x00, sizeof(txhdr));

#if 0
	if (GetPortNumber() == 0) {
		print_data_buffer((unsigned char*)TransmitBuffer->GetUserDataBuffer(), TransmitBuffer->GetUserDataLength());
	}
#endif

	if(SangomaInterface->transmit(&txhdr, TransmitBuffer->GetUserDataBuffer(), TransmitBuffer->GetUserDataLength())){
		std::ostringstream error_msg;
		error_msg << "ERROR: SangomaPort::" << __FUNCTION__ << "() failed! Port: " << GetPortNumber() << " error description: DeviceIoControl() failed";
		LastError = error_msg.str();
		LeavePortCriticalSection();
		return ERROR_DEVICE_IOCTL;
	}

	std::ostringstream error_msg;

	switch(txhdr.operation_status)
	{
	case SANG_STATUS_SUCCESS:
		PortStatistics.BytesSent += bytes_to_write;
		number_of_bytes_written = bytes_to_write;
		break;
	case SANG_STATUS_TX_TIMEOUT:
		error_msg << "ERROR: SangomaPort::" << __FUNCTION__ << "() failed! Port: " << GetPortNumber() << " error description: TX_TIMEOUT";
		break;
	case SANG_STATUS_TX_DATA_TOO_LONG:
		error_msg << "ERROR: SangomaPort::" << __FUNCTION__ << "() failed! Port: " << GetPortNumber() << " error description: TX_DATA_TOO_LONG";
		break;
	case SANG_STATUS_TX_DATA_TOO_SHORT:
		error_msg << "ERROR: SangomaPort::" << __FUNCTION__ << "() failed! Port: " << GetPortNumber() << " error description: TX_DATA_TOO_SHORT";
		break;
	case SANG_STATUS_LINE_DISCONNECTED:
		error_msg << "ERROR: SangomaPort::" << __FUNCTION__ << "() failed! Port: " << GetPortNumber() << " error description: PORT_OUT_OF_SYNC";
		break;
	default:
		break;
	}//switch()

	bool no_data_written = (0 == number_of_bytes_written);
	if (no_data_written) {
		LastError = error_msg.str();
		LeavePortCriticalSection();
		return ERROR_NO_DATA_WRITTEN;
	}
		
	LeavePortCriticalSection();
	return 0;// ALL bytes were written
}
	
///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::GetSynchronizationStatus()
///	\brief	Closes this port
///	\return	true if the port was successfully closed, false if
///	\author	J. Markwordt
///	\date	10/05/2007
///////////////////////////////////////////////////////////////////////////////////////////////
SynchronizationStatus SangomaPort::GetSynchronizationStatus()
{
	EnterPortCriticalSection();
	PORT_FUNC();

	// If the port is not currently open with a valid configuration
	// the port cannot be in sync, simply return not in sync.
	if (!IsOpen) {
		PORT_FUNC();
		LeavePortCriticalSection();
		return PORT_NOT_IN_SYNC;
	}

	// Else we need to know if enough time has elapsed to be able to reliably
	// check for alarms.  If ALARM_WAIT_TIME_MS has not passed, the Sangoma card
	// will always return no alarms, giving a false positive that we have found
	// synchronization, when in fact we have not.
	time_t CurrentTime;
	time(&CurrentTime);

	DBG_PORT("%s(): CurrentTime:%u, PortStatistics.TimeLastOpened:%u (ptr: 0x%p)\n",
		__FUNCTION__, (uint32_t)CurrentTime, (uint32_t)PortStatistics.TimeLastOpened, &PortStatistics.TimeLastOpened);

#if 0
	time_t t_time_diff = CurrentTime - PortStatistics.TimeLastOpened;
	DBG_PORT("%s(): t_time_diff: %d\n", __FUNCTION__, t_time_diff);
	//translate time differemce into milliseconds
	t_time_diff *= 1000;
	bool ready_to_check_for_alarms = (t_time_diff >= ALARM_WAIT_TIME_MS);
#else
	double t_time_diff = difftime(CurrentTime, PortStatistics.TimeLastOpened);
	DBG_PORT("%s(): t_time_diff: %f\n", __FUNCTION__, t_time_diff);
	bool ready_to_check_for_alarms = (t_time_diff >= ALARM_WAIT_TIME_MS / 1000 / 2 /* allow to poll after 1/2 of maximum wait time (approximately 16 seconds) */);
#endif

	DBG_PORT("%s(): ready_to_check_for_alarms: %d\n", __FUNCTION__, ready_to_check_for_alarms);

	if (ready_to_check_for_alarms) {
		// Check to see if there are alarms present.  We aren't interested in
		// what individual ports are present or saving them, just see if any 
		// are present.
		char alarms_present = SangomaInterface->alarms_present();
		if (alarms_present) {
			sync_cnt=0;
			if (t_time_diff >= ALARM_WAIT_TIME_MS / 1000) {
				// Set the LastError string to indicate that 
				std::ostringstream error_msg;
				error_msg << "ERROR: SangomaPort::GetSynchronizationStatus() detected alarms present on port " << GetPortNumber() << " with the current configuration.  Closing the port.";
				LastError = error_msg.str();

				LeavePortCriticalSection();

				// DAVIDR: reached maximum wait time.
				// The *caller* will decide what to do with the port: to close it or not.
				return PORT_NOT_IN_SYNC;
			} else {
				// DAVIDR: the maximum wait time was not reached yet, keep waiting for sync.
				LeavePortCriticalSection();
				return PORT_WAITING_FOR_SYNC;
			}
		} else {
			PortStatistics.TimeLastFoundSync = CurrentTime;
			sync_cnt++;
			LeavePortCriticalSection();
			return PORT_IN_SYNC;
		}

	}else{
		PORT_FUNC();
	}

	LeavePortCriticalSection();
	return PORT_WAITING_FOR_SYNC;
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::GetPortNumber() const
///	\brief	Returns this port's number (zero indexed, relative to the card)
///	\return	This port's number (zero indexed, relative to the card)
///	\author	J. Markwordt
///	\date	10/15/2007
///////////////////////////////////////////////////////////////////////////////////////////////
unsigned int SangomaPort::GetPortNumber() const
{
	return PortNumber;
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::GetUnmappedPortNumber()
///	\brief	Returns this port's number UNMAPPED (Without G3 port mapping). Use for debugging only.
///	\return	This port's UNMAPPED number  (zero indexed, relative to the card)
///	\author	David Rokhvarg (davidr@sangoma.com)
///	\date	1/10/2008
///////////////////////////////////////////////////////////////////////////////////////////////
unsigned int SangomaPort::GetUnmappedPortNumber()
{
	unsigned int unmapped_port = 0;
	EnterPortCriticalSection();
	if(SangomaInterface != NULL){
		unmapped_port = SangomaInterface->get_mapped_port_number();
	}
	LeavePortCriticalSection();
	return 	unmapped_port;
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::GetConfiguration() const
///	\brief	Returns the current configuration of the port
///	\return	Current Configuration structure for the port
///	\author	J. Markwordt
///	\date	10/15/2007
///////////////////////////////////////////////////////////////////////////////////////////////
Configuration SangomaPort::GetConfiguration() const
{
	PORT_FUNC();
	return CurrentConfiguration;
}
	
///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::GetEvent() const
///	\brief	Return the PortEvent associated with this port
///	\return	PortEvent structure for events associated with this port
///	\author	J. Markwordt
///	\date	10/18/2007
///////////////////////////////////////////////////////////////////////////////////////////////
PortEvent SangomaPort::GetEvent() const
{
	return Event;
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::GetDriverVersion() const
///	\brief	Returns this port's hardware abstraction driver version number.
///
///			Sangoma has given the card a driver and the port what Sangoma refers to as a "Hardware 
///			Abstraction Driver", and although these two should have the same version number if 
///			installed properly, they are in fact two separate drivers.
///
///			This function is to retrieve the driver associated with the port's hardware abstraction
///			driver and can be used to verify that it matches the version number of the card as a whole.
///	\return	DriverVersion containing the version of the software abstraction driver associated
///			with this port.
///	\author	J. Markwordt
///	\date	10/17/2007
///////////////////////////////////////////////////////////////////////////////////////////////
DriverVersion SangomaPort::GetDriverVersion() /* DAVIDR: removed 'const' to get rid of compiler errors */
{
	DriverVersion software_abstraction_driver_version;
	wan_driver_version_t drv_version;

	if(SangomaInterface->get_api_driver_version(&drv_version)){
		std::ostringstream error_msg;
		error_msg << "ERROR: SangomaPort::%s()" << __FUNCTION__ << " failed! Port: " << GetPortNumber();
		LastError = error_msg.str();
	}

	//translate low level version structure into high level version structure
	software_abstraction_driver_version.Major = drv_version.major;
	software_abstraction_driver_version.Minor = drv_version.minor;
	software_abstraction_driver_version.Build = drv_version.minor1;
	software_abstraction_driver_version.Revision = drv_version.minor2;

	return software_abstraction_driver_version;
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::GetStatistics()
///	\brief	Get the statistics for this port.
///
///			The reason this function is not declared constant is that the Statistics strucure
///			that is returned contains both statisics that are maintained in software by this 
///			class, and statistics that are maintained in hardware on the port.  We do not want
///			to continuously poll to keep these statistics up to date, so when requested, we will
///			issue low level commands to fill in these statistics.
///	\return	Statistics structure containing information related to this port
///	\author	J. Markwordt
///	\date	10/17/2007
///////////////////////////////////////////////////////////////////////////////////////////////
Statistics SangomaPort::GetStatistics()
{
	///		  Perform low level calls to retrieve the net_device_stats_t structure and update 
	///		  PortStatistics with the statistics we are interested in.  For example,
	///		  net_device_stats_t.rx_bytes maps to Statistics.BytesReceived.  Statistics maintained
	///		  by the SangomaPort class (such as TimeLastOpened, TimeLastFoundSync, etc.) will be
	///		  returned as is.
	wanpipe_chan_stats_t stats;

	EnterPortCriticalSection();
	if (IsOpen) 
	{
		SangomaInterface->operational_stats(&stats);
		//translate low level statistics structure into high level statistics structure
		PortStatistics.BytesReceived = stats.rx_bytes;
		PortStatistics.BytesSent = stats.tx_bytes;
		PortStatistics.Errors = stats.errors;
	}
	LeavePortCriticalSection();
	return PortStatistics;
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::ClearStatistics()
///	\brief	Clear the statistics for this port
///	\author	J. Markwordt
///	\date	10/17/2007
///////////////////////////////////////////////////////////////////////////////////////////////
void SangomaPort::ClearStatistics()
{
	/// \todo Reset the PortStatistics member to default values

	/// Issue low level command to clear the statistics maintained on the port
	EnterPortCriticalSection();
	if (IsOpen) 
	{
		SangomaInterface->flush_operational_stats();
	}
	LeavePortCriticalSection();
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::FlushBuffers()
///	\brief	Clear the statistics for this port
///	\author	J. Markwordt
///	\date	10/17/2007
///////////////////////////////////////////////////////////////////////////////////////////////
void SangomaPort::FlushBuffers()
{
	/// \todo Reset the PortStatistics member to default values

	/// Issue low level command to clear the statistics maintained on the port
	EnterPortCriticalSection();
	if (IsOpen) 
	{
		SangomaInterface->flush_buffers();
	}
	LeavePortCriticalSection();
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::GetAlarms() const
///	\brief	Retrieves the alarms from the hardware for this port
///	\return	Alarms structure containing indications of the current alarms
///	\author	J. Markwordt
///	\date	10/17/2007
///////////////////////////////////////////////////////////////////////////////////////////////
Alarms SangomaPort::GetAlarms()
{
	Alarms alarms(FALSE);
	unsigned long u_alarms = 1;
	unsigned char adapter_type;

	EnterPortCriticalSection();
	if (IsOpen) 
	{
		adapter_type = SangomaInterface->get_adapter_type();
		SangomaInterface->read_te1_56k_stat(&u_alarms);
		/// translate low level alarms structure into high level alarms structure
		if (adapter_type == WAN_MEDIA_T1 || adapter_type == WAN_MEDIA_E1){
			if(!strcmp(WAN_TE_LOS_ALARM(u_alarms), ON_STR)){
				alarms.LossOfSignal = TRUE;
			}
			if(!strcmp(WAN_TE_ALOS_ALARM(u_alarms), ON_STR)){
				alarms.AlternateLossOfSignalStatus = TRUE;
			}
			if(!strcmp(WAN_TE_RED_ALARM(u_alarms), ON_STR)){
				alarms.Red = TRUE;
			}
			if(!strcmp(WAN_TE_AIS_ALARM(u_alarms), ON_STR)){
				alarms.AlarmIndicationSignal = TRUE;
			}
			if(!strcmp(WAN_TE_OOF_ALARM(u_alarms), ON_STR)){
				alarms.OutOfFrame = TRUE;
			}
			if (adapter_type == WAN_MEDIA_T1){ 
				if(!strcmp(WAN_TE_YEL_ALARM(u_alarms), ON_STR)){
					alarms.Yellow = TRUE;
				}
			}

			if(!strcmp(WAN_TE_OOSMF_ALARM(u_alarms), ON_STR)){
				alarms.LossOfSignalingMultiframe = TRUE;
			}
			if(!strcmp(WAN_TE_OOCMF_ALARM(u_alarms), ON_STR)){
				alarms.LossOfCrcMultiframe = TRUE;
			}
			if(!strcmp(WAN_TE_OOOF_ALARM(u_alarms), ON_STR)){
				alarms.OutOfOfflineFrame = TRUE;
			}
			if(!strcmp(WAN_TE_RAI_ALARM(u_alarms), ON_STR)){
				alarms.ReceiveLossOfSignalV = TRUE;
			}
		}
	}
	LeavePortCriticalSection();
	return alarms;
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::GetLastError() const
///	\brief	Returns a description of the last error that occurred 
///	\return Returns a description of the last error that occurred (NO_ERROR_OCCURRED if the last command was successful)
///	\author	J. Markwordt
///	\date	10/17/2007
///////////////////////////////////////////////////////////////////////////////////////////////
std::string SangomaPort::GetLastError() const
{
	return LastError;
}

////////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::GetRxDataBuffer()
///	\brief	Returns a pointer to received data.
///	\return Returns a pointer to received data after a successful Read()
///	\author	David Rokhvarg (davidr@sangoma.com)
///	\date	1/10/2008
///////////////////////////////////////////////////////////////////////////////////////////////
unsigned char *SangomaPort::GetRxDataBuffer()
{
	return ReceiveBuffer->GetUserDataBuffer();
}

////////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::GetRxDataLength()
///	\brief	Returns length of received data.
///	\return Returns length of received data after a successful Read().
///	\author	David Rokhvarg (davidr@sangoma.com)
///	\date	1/10/2008
///////////////////////////////////////////////////////////////////////////////////////////////
unsigned int SangomaPort::GetRxDataLength()
{
	return ReceiveBuffer->GetUserDataLength();
}

////////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::EnterPortCriticalSection()
///	\brief	Enter a SangomaPort operation in thread-safe manner.
///	\return No return value.
///	\author	David Rokhvarg (davidr@sangoma.com)
///	\date	1/10/2008
///////////////////////////////////////////////////////////////////////////////////////////////
void SangomaPort::EnterPortCriticalSection()
{
	EnterCriticalSection(&cs_CriticalSection);
}

////////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::LeavePortCriticalSection()
///	\brief	Leave a SangomaPort operation in thread-safe manner.
///	\return No return value.
///	\author	David Rokhvarg (davidr@sangoma.com)
///	\date	1/10/2008
///////////////////////////////////////////////////////////////////////////////////////////////
void SangomaPort::LeavePortCriticalSection()
{
	LeaveCriticalSection(&cs_CriticalSection);
}


////////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::TryEnterPortCriticalSection()
///	\brief	Try to enter a SangomaPort operation in thread-safe manner.
///	\return 1 - the mutex already taken, return without waiting 0 - the mutex was free
///	\author	David Rokhvarg (davidr@sangoma.com)
///	\date	6/2/2010
///////////////////////////////////////////////////////////////////////////////////////////////
int SangomaPort::TryEnterPortCriticalSection()
{
	if (! TryEnterCriticalSection(&cs_CriticalSection) )
	{
		//If another thread already owns the critical section, the return value of TryEnterCriticalSection() is zero.
		return 1;
	}
	return 0;
}

int SangomaPort::SetAndOpenTxFile(char *szTxFileName)
{
	pTxFile = fopen(szTxFileName, "rb" );
	if( pTxFile == NULL){
		ERR_PORT("Can't open file: [%s]\n", szTxFileName);
		return 1;
	}

	return 0;
}
	
int SangomaPort::WriteChunkOfTxFile()
{
	unsigned char *user_tx_buffer = NULL;

	if (!pTxFile) {
		ERR_PORT("TxFile is NOT open!\n");
		return 1;
	}

	user_tx_buffer = this->TransmitBuffer->GetUserDataBuffer();

	//Initialize the buffer to 0xFF, so when End-of-File is reached, 
	//the padding up-to-end of tx buffer is automatic.
	memset( user_tx_buffer, 0xFF, this->TransmitBuffer->GetUserDataLength() );

	if (feof( pTxFile )) {
		//End-of-File reached. Re-wind the file to zero.
		fseek( pTxFile, 0, SEEK_SET );
	}

	//Read tx data from the file.
	fread( user_tx_buffer, 1, TxMtu, pTxFile );

	return this->Write(user_tx_buffer, TxMtu);
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::LineLoopbackEnable()
///	\brief	Enable line loopback for testing. All transmitted data will be also received
//			locally. Requires the Port to be in Master clock mode.
///	\return	0 - success, non-zero - error
///	\author	David Rokhvarg
///	\date	01/27/2011
///////////////////////////////////////////////////////////////////////////////////////////////
int SangomaPort::LineLoopbackEnable()
{
	int rc = SangomaInterface->loopback_command(WAN_TE1_DDLB_MODE, WAN_TE1_LB_ENABLE, ENABLE_ALL_CHANNELS);

	if (rc) {
		ERR_PORT("Port %u: %s() failed! rc: %d\n", GetPortNumber(), __FUNCTION__, rc);
	}

	return rc;
}

///////////////////////////////////////////////////////////////////////////////////////////////
///	\fn		SangomaPort::LineLoopbackDisable()
///	\brief	Disable line loopback which was enabled by SangomaPort::LineLoopbackEnable()
///	\return	0 - success, non-zero - error
///	\author	David Rokhvarg
///	\date	01/27/2011
///////////////////////////////////////////////////////////////////////////////////////////////
int SangomaPort::LineLoopbackDisable()
{
	int rc = SangomaInterface->loopback_command(WAN_TE1_DDLB_MODE, WAN_TE1_LB_DISABLE, ENABLE_ALL_CHANNELS);

	if (rc) {
		ERR_PORT("Port %u: %s() failed! rc: %d\n", GetPortNumber(), __FUNCTION__, rc);
	}

	return rc;

}

#if defined(__WINDOWS__)
# ifndef _DEBUG				
#  pragma optimize("2gt",off)// Windows specific (MS_SPECIFIC), disables optimization in Release mode
# endif
#endif

