1493 lines
55 KiB
C++
1493 lines
55 KiB
C++
#if defined WIN32 && defined NDEBUG
|
|
#pragma optimize("gt",on)
|
|
#endif
|
|
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <sstream>
|
|
#include <time.h>
|
|
#include "SangomaPort.h"
|
|
#include "sangoma_interface.h"
|
|
#include "driver_configurator.h"
|
|
|
|
#ifdef linux
|
|
#include <math.h>
|
|
#include <float.h>
|
|
#endif
|
|
|
|
#ifndef INT_MAX
|
|
#define INT_MAX ((int)(~0U>>1))
|
|
#endif
|
|
|
|
#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 if(0)printf("Error:%s():line:%d: ", __FUNCTION__, __LINE__);if(0)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 NotWhitespace(const char character)
|
|
/// \brief Determines whether the specified character is a
|
|
/// non-whitespace character.
|
|
/// \param[in] character - The character to be examined.
|
|
/// \return True if the character is a non-whitespace character
|
|
/// or false if the character is a whitespace character.
|
|
/// \author Kyle Hickernell
|
|
/// \date 06/07/2011
|
|
///////////////////////////////////////////////////////////////////////
|
|
bool NotWhitespace(const char character)
|
|
{
|
|
const int NON_WHITE_SPACE_RETURN_CODE = 0;
|
|
bool character_is_whitespace = (NON_WHITE_SPACE_RETURN_CODE != isspace(character));
|
|
return !character_is_whitespace;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
/// \fn ToFloat(const std::string& string_to_convert, float& float_value)
|
|
/// \brief Converts a string to a float.
|
|
/// \param[in] string_to_convert - The string to convert.
|
|
/// \param[out] float_value - The converted float. If this function returns false,
|
|
/// then this data should be considered invalid.
|
|
/// \return True if the conversion succeeded. False otherwise.
|
|
/// \author Luke Roling
|
|
/// \date 09/16/2011
|
|
///////////////////////////////////////////////////////////////////////
|
|
bool ToFloat(const std::string& string_to_convert, float& float_value)
|
|
{
|
|
// RESET THE OUTPUT PARAMETER TO A DEFAULT VALUE.
|
|
float_value = 0;
|
|
|
|
// CONVERT THE STRING TO A DOUBLE.
|
|
// Set errno to 0 for error checking.
|
|
#if defined WIN32
|
|
const int DEFAULT_ERRNO_VALUE = 0;
|
|
errno_t set_errno_result = _set_errno(DEFAULT_ERRNO_VALUE);
|
|
const int SET_ERRNO_SUCCESS = 0;
|
|
bool set_errno_successfully = (SET_ERRNO_SUCCESS == set_errno_result);
|
|
if(!set_errno_successfully)
|
|
{
|
|
// Return since the errno value could not be set.
|
|
return false;
|
|
}
|
|
#elif defined LINUX
|
|
errno = 0;
|
|
#endif
|
|
|
|
char* remaining_characters;
|
|
double double_value = strtod(string_to_convert.c_str(), &remaining_characters);
|
|
|
|
// VERIFY THAT THE CONVERSION WAS SUCCESSFUL.
|
|
// Check if valid conversion occurred.
|
|
const double POSSIBLE_CONVERSION_ERROR_OCCURRED = 0.0;
|
|
bool possible_conversion_error_occurred = (POSSIBLE_CONVERSION_ERROR_OCCURRED == double_value);
|
|
if (possible_conversion_error_occurred)
|
|
{
|
|
// Check if 0 was parsed.
|
|
bool string_to_convert_valid = (string_to_convert.c_str() != remaining_characters);
|
|
if (!string_to_convert_valid)
|
|
{
|
|
// The conversion failed due to the input string not containing valid digits, so exit here.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check if number parsed is out of range.
|
|
bool double_value_in_range = (ERANGE != errno);
|
|
if (!double_value_in_range)
|
|
{
|
|
// Conversion failed because the value was out of a Double's range, so exit here.
|
|
return false;
|
|
}
|
|
|
|
// CHECK IF THERE ARE TRAILING INVALID CHARACTERS.
|
|
// Since trailing whitespace characters are acceptable, the only
|
|
// characters we are looking for are non-whitespace characters.
|
|
std::string remaining_string = remaining_characters;
|
|
std::string::iterator non_whitespace_character_index = std::find_if(
|
|
remaining_string.begin(),
|
|
remaining_string.end(),
|
|
NotWhitespace);
|
|
bool trailing_characters_valid = (remaining_string.end() == non_whitespace_character_index);
|
|
if (!trailing_characters_valid)
|
|
{
|
|
// Conversion failed because invalid trailing characters were found, so exit here.
|
|
return false;
|
|
}
|
|
|
|
// CHECK IF DOUBLE CAN BE CONVERTED TO FLOAT.
|
|
// A float contains three different ranges. The first two ranges contain the possible positive
|
|
// and negative values a float can represent. The third range is the value zero which does not
|
|
// fall into the other two ranges. The diagram below shows the ranges on a number line.
|
|
//
|
|
// Float -Max Float -Min Float Min Float Max
|
|
// |---------------------| | |---------------------|
|
|
// |-------------------------------| | |-------------------------------|
|
|
// Double -Max Double -Min 0 Double Min Double Max
|
|
// <--------------------------------------Real Number line--------------------------------------->
|
|
|
|
// Check if double is in the positive float range.
|
|
const double MAX_POSITIVE_FLOAT = FLT_MAX;
|
|
const double MIN_POSITIVE_FLOAT = FLT_MIN;
|
|
bool double_value_is_in_valid_positive_range = ((double_value >= MIN_POSITIVE_FLOAT) && (double_value <= MAX_POSITIVE_FLOAT));
|
|
|
|
// Check if the double is zero which a float can represent.
|
|
const double FLOAT_ZERO = 0;
|
|
bool double_value_is_equal_to_zero = (double_value == FLOAT_ZERO);
|
|
|
|
// Check if double is in the negative float range.
|
|
const double MAX_NEGATIVE_FLOAT = -MAX_POSITIVE_FLOAT;
|
|
const double MIN_NEGATIVE_FLOAT = -MIN_POSITIVE_FLOAT;
|
|
bool double_value_is_in_valid_negative_range = ((double_value <= MIN_NEGATIVE_FLOAT) && (double_value >= MAX_NEGATIVE_FLOAT));
|
|
|
|
// Check if double falls into one of the float ranges.
|
|
bool double_to_float_conversion_possible = (
|
|
double_value_is_in_valid_positive_range ||
|
|
double_value_is_equal_to_zero ||
|
|
double_value_is_in_valid_negative_range);
|
|
if(!double_to_float_conversion_possible)
|
|
{
|
|
// Conversion failed because the double could not be converted to a float, so exit here.
|
|
return false;
|
|
}
|
|
|
|
// Conversion was successful.
|
|
float_value = static_cast<float>(double_value);
|
|
return true;
|
|
}
|
|
|
|
const unsigned int BufferSettings::MAXIMUM_BUFFER_MULTIPLIER_FACTOR = MAX_BUFFER_MULTIPLIER_FACTOR;
|
|
const unsigned int BufferSettings::MINIMUM_BUFFER_MULTIPLIER_FACTOR = MIN_BUFFER_MULTIPLIER_FACTOR;
|
|
const unsigned int BufferSettings::MINIMUM_NUMBER_OF_BUFFERS_PER_API_INTERFACE = MIN_NUMBER_OF_BUFFERS_PER_API_INTERFACE;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
/// \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 DriverVersion::ToString()
|
|
/// \brief Converts the DriverVersion to a string.
|
|
/// \return The string representation of the driver version (e.g. "6.0.6.3")
|
|
/// \author Kyle Hickernell
|
|
/// \date 03/21/2011
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
std::string DriverVersion::ToString()
|
|
{
|
|
std::ostringstream driver_version;
|
|
driver_version << Major << "." << Minor << "." << Build << "." << Revision;
|
|
return driver_version.str();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
/// \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 BufferSettings & receive_buffer_settings,
|
|
/// const BufferSettings & transmit_buffer_settings)
|
|
/// \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 receive_buffer_settings 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 transmit_buffer_settings 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 & receive_buffer_settings,
|
|
const BufferSettings & transmit_buffer_settings)
|
|
: pReceiveBuffer(NULL),
|
|
pTransmitBuffer(NULL),
|
|
RxDiscardCounter(0),
|
|
RxCount(0),
|
|
SyncCount(0),
|
|
RxLength(0),
|
|
CardNumber(card_number),
|
|
PortNumber(port_number),
|
|
Event(),
|
|
IsOpen(false),
|
|
CurrentConfiguration(),
|
|
PortStatistics(),
|
|
LastError(NO_ERROR_OCCURRED),
|
|
ReceiveBufferSettings(receive_buffer_settings),
|
|
TransmitBufferSettings(transmit_buffer_settings),
|
|
pSangomaInterface(NULL),
|
|
cs_CriticalSection(),
|
|
rxhdr(),
|
|
txhdr(),
|
|
wp_api(),
|
|
pTxFile(NULL),
|
|
TxMtu(INT_MAX)
|
|
{
|
|
PORT_FUNC();
|
|
|
|
DBG_PORT("%s(): card_number: %u, port_number: %u\n", __FUNCTION__, card_number, port_number);
|
|
|
|
InitializeCriticalSection(&cs_CriticalSection);
|
|
|
|
pSangomaInterface = new sangoma_interface(CardNumber, PortNumber);
|
|
DBG_PORT("pSangomaInterface: 0x%p\n", pSangomaInterface);
|
|
|
|
rxhdr = new wp_api_hdr_t();
|
|
txhdr = new wp_api_hdr_t();
|
|
wp_api = new wanpipe_api_t();
|
|
|
|
// 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
|
|
pReceiveBuffer = new PortBuffer(sizeof(RX_DATA_STRUCT) * ReceiveBufferSettings.BufferMultiplierFactor);
|
|
pTransmitBuffer = new PortBuffer(sizeof(TX_DATA_STRUCT) * TransmitBufferSettings.BufferMultiplierFactor);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
/// \fn SangomaPort::~SangomaPort()
|
|
/// \brief Destructor
|
|
/// \author J. Markwordt
|
|
/// \date 10/04/2007
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
SangomaPort::~SangomaPort()
|
|
{
|
|
PORT_FUNC();
|
|
|
|
// If the has not been properly closed, close it
|
|
if (IsOpen) {
|
|
Close();
|
|
}
|
|
|
|
delete rxhdr;
|
|
delete txhdr;
|
|
delete wp_api;
|
|
|
|
// Deallocate Rx/Tx Port buffers
|
|
delete pReceiveBuffer;
|
|
delete pTransmitBuffer;
|
|
|
|
if(pSangomaInterface != NULL){
|
|
delete pSangomaInterface;
|
|
pSangomaInterface = NULL;
|
|
}
|
|
|
|
if (pTxFile) {
|
|
fclose( pTxFile );
|
|
}
|
|
|
|
PORT_FUNC();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
/// \fn SangomaPort::SetPortConfiguration(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, pSangomaInterface->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;
|
|
|
|
p_drv_cfg_obj->set_dchan(config.dchan,config.dchan_seven_bit,config.dchan_mtp1_filter);
|
|
p_drv_cfg_obj->set_chunk_ms(config.chunk_ms);
|
|
p_drv_cfg_obj->set_span_api_mode();
|
|
|
|
switch (config.api_mode) {
|
|
case SNG_SPAN_MODE:
|
|
p_drv_cfg_obj->set_span_api_mode();
|
|
break;
|
|
case SNG_CHAN_MODE:
|
|
p_drv_cfg_obj->set_chan_api_mode();
|
|
buffer_settings.buffer_multiplier_factor=1;
|
|
ReceiveBufferSettings.BufferMultiplierFactor=0;
|
|
break;
|
|
case SNG_DATA_MODE:
|
|
p_drv_cfg_obj->set_data_api_mode();
|
|
buffer_settings.buffer_multiplier_factor=1;
|
|
ReceiveBufferSettings.BufferMultiplierFactor=0;
|
|
break;
|
|
}
|
|
|
|
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, pSangomaInterface->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, pSangomaInterface->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(pSangomaInterface->init()){
|
|
PORT_FUNC();
|
|
|
|
std::ostringstream error_msg;
|
|
error_msg << "ERROR: SangomaPort: pSangomaInterface->init() failed for port " << GetPortNumber() << ".";
|
|
LastError = error_msg.str();
|
|
|
|
Event.EventHandle = NULL;
|
|
ERR_PORT("pSangomaInterface->init() failed! (CardNumber: %u, PortNumber: %u (mapped port: %u))\n",
|
|
CardNumber, PortNumber, pSangomaInterface->get_mapped_port_number());
|
|
//exit(1);
|
|
LeavePortCriticalSection();
|
|
return false;
|
|
}
|
|
|
|
if(ReceiveBufferSettings.BufferMultiplierFactor &&
|
|
pSangomaInterface->set_buffer_multiplier(wp_api, ReceiveBufferSettings.BufferMultiplierFactor)){
|
|
PORT_FUNC();
|
|
|
|
std::ostringstream error_msg;
|
|
error_msg << "ERROR: SangomaPort: pSangomaInterface->set_buffer_multiplier() failed for port " << GetPortNumber() << ".";
|
|
LastError = error_msg.str();
|
|
|
|
Event.EventHandle = NULL;
|
|
ERR_PORT("pSangomaInterface->set_buffer_multiplier() failed! (CardNumber: %u, PortNumber: %u (mapped port: %u))\n",
|
|
CardNumber, PortNumber, pSangomaInterface->get_mapped_port_number());
|
|
//exit(1);
|
|
LeavePortCriticalSection();
|
|
return false;
|
|
}
|
|
|
|
Event.EventHandle = pSangomaInterface->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 achieved synchronization.
|
|
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, pSangomaInterface->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(pSangomaInterface->get_wait_object_reference());
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Perform actions to close and cleanup the port
|
|
bool close_successful = false;
|
|
if(pSangomaInterface->cleanup() == 0){
|
|
close_successful = true;
|
|
}else{
|
|
ERR_PORT("CardNumber: %u, PortNumber: %u (mapped port: %u): failed the cleanup()!!\n",
|
|
CardNumber, PortNumber, pSangomaInterface->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 SangomaPort::Read(unsigned int event_flag)
|
|
/// \brief When the wait object for this port is signaled, this method is used to process the
|
|
/// result. If the flag indicates a read event, then the data will be placed in the port's
|
|
/// receive buffer. If the flag indicates a write event, then the method returns that a
|
|
/// transmit buffer is available. If the flag indicates a status event, then the status
|
|
/// event type will be returned. On error, the return value describes the error that occurred.
|
|
///
|
|
/// \param[in] event_flag - An integer value representing the type of event to process. The value may
|
|
/// be either POLLIN, POLLOUT, or POLLPRI.
|
|
/// \return For each available event, the method's return value is defined below.
|
|
///
|
|
/// Read Event (POLLIN):
|
|
/// ReadDataAvailable - The data for the read event is available in the port's receive buffer.
|
|
/// ERROR_NO_DATA_AVAILABLE - No data is available from the receiver.
|
|
/// ERROR_PORT_NOT_OPEN - A read event cannot be processed on a closed port.
|
|
/// ERROR_DEVICE_IOCTL - Failed to retrieve information from the driver.
|
|
///
|
|
/// Write Event (POLLOUT):
|
|
/// TransmitBufferAvailable - A buffer is available to transmit user data.
|
|
/// ERROR_PORT_NOT_OPEN - A write event cannot be processed on a closed port.
|
|
///
|
|
/// Line Status Event (POLLPRI):
|
|
/// LineConnected - An line has been connected to the port.
|
|
/// LineDisconnected - The line has been disconnected from the port.
|
|
/// ERROR_PORT_NOT_OPEN - A status event cannot be processed on a closed port.
|
|
/// ERROR_DEVICE_IOCTL - Failed to retrieve information from the driver.
|
|
///
|
|
/// Invalid Event Flag:
|
|
/// ERROR_INVALID_READ_REQUEST - The specified event flag is invalid.
|
|
///
|
|
/// \author David Rokhvarg
|
|
/// \date 11/15/2007
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
int SangomaPort::Read(unsigned int event_flag)
|
|
{
|
|
int return_code = ERROR_NO_DATA_AVAILABLE;
|
|
|
|
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.";
|
|
LastError = error_msg.str();
|
|
LeavePortCriticalSection();
|
|
return ERROR_PORT_NOT_OPEN;
|
|
}
|
|
|
|
switch(event_flag)
|
|
{
|
|
case POLLIN:
|
|
if(pSangomaInterface->read_data(rxhdr, pReceiveBuffer->GetPortBuffer(), pReceiveBuffer->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(pSangomaInterface->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 event_flag) for port " << GetPortNumber() << " invalid 'event_flag'=" << event_flag << "\n";
|
|
ERR_PORT("Port %u: SangomaPort::Read() invalid event_flag=0x%X!\n", GetPortNumber(), event_flag);
|
|
LastError = error_msg.str();
|
|
LeavePortCriticalSection();
|
|
return ERROR_INVALID_READ_REQUEST;
|
|
}//switch()
|
|
|
|
if (POLLIN == event_flag)
|
|
{
|
|
switch (rxhdr->operation_status)
|
|
{
|
|
case SANG_STATUS_RX_DATA_AVAILABLE:
|
|
return_code = ReadDataAvailable;
|
|
pReceiveBuffer->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_AVAILABLE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (POLLPRI == event_flag)
|
|
{
|
|
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:
|
|
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_AVAILABLE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(return_code == ReadDataAvailable){
|
|
PortStatistics.BytesReceived += pReceiveBuffer->GetUserDataLength();
|
|
}
|
|
|
|
LeavePortCriticalSection();
|
|
return return_code;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
/// \fn SangomaPort::Write(unsigned char* p_source_buffer, const unsigned 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 > pTransmitBuffer->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;
|
|
}
|
|
|
|
pTransmitBuffer->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 (pTransmitBuffer->GetUserDataBuffer() != p_source_buffer) {
|
|
memcpy(pTransmitBuffer->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*)pTransmitBuffer->GetUserDataBuffer(), pTransmitBuffer->GetUserDataLength());
|
|
}
|
|
#endif
|
|
|
|
if(pSangomaInterface->transmit(txhdr, pTransmitBuffer->GetUserDataBuffer(), pTransmitBuffer->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 Returns whether the port has achieved frame synchronization using the current
|
|
/// configuration. If the port has not yet been opened with a configuration, then this
|
|
/// method will fail and return false.
|
|
///
|
|
/// Frame synchronization is determined by checking the presence of Sangoma hardware
|
|
/// alarms on the port. When the port is first opened, it is initialized with an alarm
|
|
/// present. If frame synchronization is achieved, all hardware alarms are cleared. If
|
|
/// frame synchronization is not achieved, alarms will remain present.
|
|
///
|
|
/// Failing to achieve synchronization can be caused by the configuration being
|
|
/// incompatible with the current data or by attenuation on the port.
|
|
/// \return True if the port has achieved synchronization, false otherwise.
|
|
/// \author Jeff Barbieri
|
|
/// \date 12/29/2011
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool SangomaPort::GetSynchronizationStatus()
|
|
{
|
|
// ENTER A CRITICAL SECTION TO PROTECT HARDWARE RESOURCES FROM SIMULTANEOUS ACCESS.
|
|
EnterPortCriticalSection();
|
|
|
|
// CHECK IF THE PORT IS OPEN.
|
|
if (!IsOpen)
|
|
{
|
|
// Synchronization can only be achieved on an open port.
|
|
LeavePortCriticalSection();
|
|
return false;
|
|
}
|
|
|
|
// CHECK IF HARDWARE ALARMS ARE PRESENT ON THE PORT.
|
|
char alarms_present_result = pSangomaInterface->alarms_present();
|
|
const char ALARMS_PRESENT_RESULT_TRUE = 1;
|
|
bool hardware_alarms_present_on_port = (ALARMS_PRESENT_RESULT_TRUE == alarms_present_result);
|
|
if (hardware_alarms_present_on_port)
|
|
{
|
|
// Synchronization has not been achieved.
|
|
LeavePortCriticalSection();
|
|
return false;
|
|
}
|
|
|
|
// SYNCHRONIZATION HAS BEEN ACHIEVED.
|
|
// Update the port statistics to indicate that the port has found synchronization.
|
|
time_t CurrentTime;
|
|
time(&CurrentTime);
|
|
PortStatistics.TimeLastFoundSync = CurrentTime;
|
|
|
|
// Return true to indicate that synchronization has been achieved.
|
|
LeavePortCriticalSection();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
/// \fn SangomaPort::GetReceiverLevelInDb(float& receiver_level_in_db)
|
|
/// \brief Returns the current receiver level reported by the front end E1/T1 framer on the
|
|
/// Sangoma card. This is a relative value reported in decibels. The value is dependent
|
|
/// on the current port configuration and is therefore only defined for an open port.
|
|
/// \note The front end E1/T1 framer reports a range of values with a resolution of -2.5dB.
|
|
/// This method returns the minimum receiver level reported. For all possible receiver
|
|
/// level ranges see Table 9-17 in the front end E1/T1 framer specification.
|
|
/// \param[out] receiver_level_in_db - The current receiver level reported by the front end E1/T1
|
|
/// framer on the Sangoma card. This value will range between -2.5dB and -44dB.
|
|
/// \author Jeff Barbieri
|
|
/// \date 12/28/2011
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool SangomaPort::GetReceiverLevelInDb(float& receiver_level_in_db)
|
|
{
|
|
// ENTER A CRITICAL SECTION TO PROTECT HARDWARE RESOURCES FROM SIMULTANEOUS ACCESS.
|
|
EnterPortCriticalSection();
|
|
|
|
// RESET THE OUT PARAMETER.
|
|
receiver_level_in_db = 0;
|
|
|
|
// CHECK IF THE PORT IS OPEN.
|
|
if (!IsOpen)
|
|
{
|
|
// The Rx Level is dependent on the current port configuration and therefore only defined
|
|
// for an open port. Return false to indicate that an error has occurred.
|
|
LeavePortCriticalSection();
|
|
return false;
|
|
}
|
|
|
|
// ATTEMPT TO RETRIEVE THE E1/T1 FRONT END FRAMER STATISTICS.
|
|
std::string receiver_level_range;
|
|
bool receiver_level_range_retrieved = pSangomaInterface->GetReceiverLevelRange(receiver_level_range);
|
|
if (!receiver_level_range_retrieved)
|
|
{
|
|
// The receiver level range could not be retrieved.
|
|
LeavePortCriticalSection();
|
|
return false;
|
|
}
|
|
|
|
// PARSE THE NUMERICAL RECEIVER LEVEL IN DECIBELS FROM ITS STRING REPRESENTATION.
|
|
// The text representing the receiver level range has the following format. The values range from -2.5
|
|
// decibels to -22 decibels.
|
|
//
|
|
// >-2.5db
|
|
// -2.5db to -5db
|
|
// ...
|
|
const std::string NEGATIVE_SIGN = "-";
|
|
std::string::size_type negative_sign_location = receiver_level_range.find_first_of(NEGATIVE_SIGN);
|
|
const std::string DECIBEL_UNIT = "db";
|
|
std::string::size_type db_unit_location = receiver_level_range.find(DECIBEL_UNIT);
|
|
|
|
// Check whether the format of the receiver level range is valid.
|
|
bool receiver_level_range_valid =
|
|
(negative_sign_location != std::string::npos) &&
|
|
(db_unit_location != std::string::npos) &&
|
|
(negative_sign_location < db_unit_location);
|
|
if(!receiver_level_range_valid)
|
|
{
|
|
// The receiver level range did not match the expected format. Return false
|
|
// to indicate that an error has occurred.
|
|
LeavePortCriticalSection();
|
|
return false;
|
|
}
|
|
|
|
// Convert the minimum receiver level string to its associated floating point value.
|
|
std::string::size_type numerical_db_string_length = (db_unit_location - negative_sign_location);
|
|
std::string receiver_level_string = receiver_level_range.substr(negative_sign_location, numerical_db_string_length);
|
|
float receiver_level_float = 0;
|
|
bool conversion_to_float_successful = ToFloat(
|
|
receiver_level_string,
|
|
receiver_level_float);
|
|
if (!conversion_to_float_successful)
|
|
{
|
|
// The minimum receiver level could not be converted. Return false to indicate
|
|
// that an error has occurred.
|
|
LeavePortCriticalSection();
|
|
return false;
|
|
}
|
|
|
|
// CHECK IF THE RECEIVER LEVEL IS WITHIN THE EXPECTED RANGE.
|
|
const float MAXIMUM_EXPECTED_RECEIVE_LEVEL = -2.5f;
|
|
const float MINIMUM_EXPECTED_RECEIVE_LEVEL = -44.0f;
|
|
bool receiver_level_float_within_expected_range =
|
|
(receiver_level_float <= MAXIMUM_EXPECTED_RECEIVE_LEVEL) &&
|
|
(receiver_level_float >= MINIMUM_EXPECTED_RECEIVE_LEVEL);
|
|
if (!receiver_level_float_within_expected_range)
|
|
{
|
|
// The receiver level is outside the expected range.
|
|
LeavePortCriticalSection();
|
|
return false;
|
|
}
|
|
|
|
// STORE THE RECEIVER LEVEL TO RETURN TO THE USER.
|
|
receiver_level_in_db = receiver_level_float;
|
|
LeavePortCriticalSection();
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
/// \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(pSangomaInterface != NULL){
|
|
unmapped_port = pSangomaInterface->get_mapped_port_number();
|
|
}
|
|
LeavePortCriticalSection();
|
|
return unmapped_port;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
/// \fn SangomaPort::GetConfiguration(Configuration& current_configuration)
|
|
/// \brief Returns the current port configuration.
|
|
/// \param[out] current_configuration - The configuration currently in use on the port.
|
|
/// \return The current port configuration.
|
|
/// \author Jeff Barbieri
|
|
/// \date 12/30/2011
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool SangomaPort::GetConfiguration(Configuration& current_configuration)
|
|
{
|
|
// ENTER A CRITICAL SECTION TO PROTECT HARDWARE RESOURCES FROM SIMULTANEOUS ACCESS.
|
|
EnterPortCriticalSection();
|
|
|
|
// CLEAR THE OUT PARAMETER.
|
|
current_configuration = Configuration();
|
|
|
|
// CHECK IF THE PORT IS OPEN.
|
|
if(!IsOpen)
|
|
{
|
|
// No configuration is currently defined.
|
|
LeavePortCriticalSection();
|
|
return false;
|
|
}
|
|
|
|
// STORE THE CURRENT CONFIGURATION TO RETURN TO THE USER.
|
|
current_configuration = CurrentConfiguration;
|
|
LeavePortCriticalSection();
|
|
return true;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
/// \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()
|
|
/// \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()
|
|
{
|
|
DriverVersion software_abstraction_driver_version;
|
|
wan_driver_version_t drv_version;
|
|
|
|
if(pSangomaInterface->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)
|
|
{
|
|
pSangomaInterface->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)
|
|
{
|
|
pSangomaInterface->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)
|
|
{
|
|
pSangomaInterface->flush_buffers();
|
|
}
|
|
LeavePortCriticalSection();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
/// \fn SangomaPort::GetAlarms()
|
|
/// \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 = pSangomaInterface->get_adapter_type();
|
|
pSangomaInterface->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 pReceiveBuffer->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 pReceiveBuffer->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(const 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;
|
|
|
|
bool file_valid = (pTxFile != NULL);
|
|
if (!file_valid)
|
|
{
|
|
//ERR_PORT("TxFile is NOT open!\n");
|
|
return 1;
|
|
}
|
|
|
|
user_tx_buffer = this->pTransmitBuffer->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->pTransmitBuffer->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 = pSangomaInterface->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 = pSangomaInterface->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;
|
|
|
|
}
|
|
|