/***************************************************************************
 *   Property rights (C) 2004-2006 by EVER Sp. z o.o.                      *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include <unistd.h>
#include <string.h>
#include <stdio.h>

#include "ccomm.h"
#include "conf.h"
#include "common.h"

#define SINLINEXL_DRIVER_BUILD 1
#include "sinlinexl.h"

/* Global variable declarations */
CComm commSinlineXL;
supsp g_SinlineXL_sup;
sdrv_config	sdcSinlineXL;
int g_iFirmwareRev;
int	g_iUpsSeries, g_iUpsFamily;
int	g_iSinlineXL_ExtError;

/* static variables */
static bool g_bFirmwareUnsupportedPass = false;
static bool g_bConfigInitiated = false;
static int iAccuCount = 0;

static unsigned int uiUpsInformation[ UPS_INFORMATION_COUNT ];
static unsigned int uiUpsStatistics[ UPS_STATISTICS_COUNT ];

/* IMPLEMENTATION */

// Function name	: ioctl
// Description		: Control function
// Return type		: int 
// Argument         : long lCommand
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int sinlinexl_ioctl(long lCommand, void *lpvBuff, int *lpiBuffSize)
{
	switch(lCommand)
	{
	case IOCTL_INIT: 
		return SinlineXL_DoInit(lpvBuff, lpiBuffSize);

	case IOCTL_UNINIT:
		return SinlineXL_DoUnInit(lpvBuff, lpiBuffSize);

	case IOCTL_AUTOCONFIGURE: 
		return SinlineXL_DoAutoConfigure(lpvBuff, lpiBuffSize);

	case IOCTL_CONFIGURE: 
		return SinlineXL_DoConfigure(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSINFOMASK: 
		return SinlineXL_DoGetUpsInfoMask(lpvBuff, lpiBuffSize);

	case IOCTL_SET_UPSINFOMASK:
		return SinlineXL_DoSetUpsInfoMask(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSSTATESMASK:
		return SinlineXL_DoGetUpsStateMask(lpvBuff, lpiBuffSize);

	case IOCTL_SET_UPSSTATESMASK:
		return SinlineXL_DoSetUpsStateMask(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSSTATE:
		return SinlineXL_DoGetUpsState(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSPARAMSMASK:
		return SinlineXL_DoGetUpsParamsMask(lpvBuff, lpiBuffSize);

	case IOCTL_SET_UPSPARAMSMASK:
		return SinlineXL_DoSetUpsParamsMask(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSPARAMS:
		return SinlineXL_DoGetUpsParams(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSSETUPPARAMSMASK:
		return SinlineXL_DoGetUpsSetupParamsMask(lpvBuff, lpiBuffSize);

	case IOCTL_SET_UPSSETUPPARAMSMASK:
		return SinlineXL_DoSetUpsSetupParamsMask(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSSETUPPARAMS:
		return SinlineXL_DoGetUpsSetupParams(lpvBuff, lpiBuffSize);

	case IOCTL_SET_UPSSETUPPARAMS:
		return SinlineXL_DoSetUpsSetupParams(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSCHARACTERISTICMASK:
		return SinlineXL_DoGetUpsCharacteristicMask(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSCHARACTERISTIC:
		return SinlineXL_DoGetUpsCharacteristic(lpvBuff, lpiBuffSize);

	case IOCTL_GET_DRIVER_INFO:
		return SinlineXL_DoGetDriverInfo(lpvBuff, lpiBuffSize);

	case IOCTL_GET_TABLE_INFORMATIONS:
		return SinlineXL_DoGetUPSStructures(EU_INFORMATION, lpvBuff, lpiBuffSize);

	case IOCTL_GET_TABLE_SETUP:
		return SinlineXL_DoGetUPSStructures(EU_SETUP, lpvBuff, lpiBuffSize);

	case IOCTL_GET_ERRORNO:
		return SinlineXL_DoGetExtendedError(lpvBuff, lpiBuffSize);

	case IOCTL_TESTUPSLINK:
		return SinlineXL_DoTestUpsLink(lpvBuff, lpiBuffSize);

	case IOCTL_GETCONFIGPARAMSCOUNT:
		return SinlineXL_DoGetConfigParamsCount(lpvBuff, lpiBuffSize);

	case IOCTL_GETCONFIGPARAMS:
		return SinlineXL_DoGetConfigParams(lpvBuff, lpiBuffSize);

	case IOCTL_GETCONFIGPARAM:
		return SinlineXL_DoGetConfigParam(lpvBuff, lpiBuffSize);

	case IOCTL_SETCONFIGPARAMS:
		return SinlineXL_DoSetConfigParams(lpvBuff, lpiBuffSize);

	case IOCTL_SETCONFIGPARAM:
		return SinlineXL_DoSetConfigParam(lpvBuff, lpiBuffSize);

	case IOCTL_UPDATECONFIG:
		return SinlineXL_DoUpdateConfig(lpvBuff, lpiBuffSize);

	case IOCTL_SHUTDOWNUPS:
		return SinlineXL_DoUpsShutdown(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSSTATISTICSCOUNT:
		return SinlineXL_DoGetUpsStatisticsCount(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSSTATISTICS:
		return SinlineXL_DoGetUpsStatistics(lpvBuff, lpiBuffSize);

	case IOCTL_GETCONFIGFILENAME:
		return SinlineXL_DoGetConfigFileName(lpvBuff, lpiBuffSize);
	
	default:
		return IOCTL_ERROR_CMD_NOTSUPPORTED;
	}
}

// Function name	: SinlineXL_DoInit
// Description		: Inicjalizacja drivera
// Return type		: int 
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int SinlineXL_DoInit( void *lpvBuff, int *lpiBuffSize )
{
	g_iSinlineXL_ExtError = 0;

	g_iUpsSeries=0;
	g_iFirmwareRev=0;
	g_bFirmwareUnsupportedPass=false;

	if (SinlineXL_DoReadConfig() != IOCTL_ERROR_SUCCESS)
		return IOCTL_ERROR_CONFIG_READFAIL;

	commSinlineXL.init(sdcSinlineXL.szSerialPort, B19200);
	if (commSinlineXL.open()!=COMM_SUCCESS) {
		g_iSinlineXL_ExtError=IOCTL_ERROR_COMM_INITFAIL;
		return IOCTL_ERROR;
	}
	
	g_iSinlineXL_ExtError = 0;
	if (SinlineXL_DoUpdateConfig(NULL, NULL) != IOCTL_ERROR_SUCCESS)
		return IOCTL_ERROR;

	return IOCTL_ERROR_SUCCESS;

}

// Function name	: SinlineXL_DoUnInit
// Description		: Deinicjalizacja drivera
// Return type		: int 
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int SinlineXL_DoUnInit( void *lpvBuff, int *lpiBuffSize )
{
/*	char szDrvCfgPath[MAX_PATH];
	unsigned long ulSize;
	CWinUtil cUtils;

	if ( !sdcSinlineXL.bDrvSlaveMode ) {
		if ( !commSinlineXL.UnInitialize() )
			return IOCTL_ERROR;
	} else
		return IOCTL_ERROR_SUCCESS;

	if ( cUtils.GetStartupDir( szDrvCfgPath, sizeof(szDrvCfgPath) ) < 0 )
		return IOCTL_ERROR;
	strcat( szDrvCfgPath, DRIVER_CONFIG_FILE );

	sdcSinlineXL.bDrvActive = false;
	ulSize = sizeof(sdcSinlineXL);
	if ( !cUtils.WriteFile( szDrvCfgPath, ( void * )&sdcSinlineXL, 0, ulSize ) ) {
		g_iSinlineXL_ExtError = IOCTL_ERROR_CONFIG_WRITEFAIL;
		return IOCTL_ERROR;
	}

	return IOCTL_ERROR_SUCCESS;
*/
	if (commSinlineXL.close()!=COMM_SUCCESS)
	{
		g_iSinlineXL_ExtError=0;
		return IOCTL_ERROR;
	}
	
	return IOCTL_ERROR_SUCCESS;

}

// Function name	: SinlineXL_DoAutoConfigure
// Description		: Self configure
// Return type		: int 
// Argument         :  void *lpvBuff
// Argument         : int *lpiBuffSize
int SinlineXL_DoAutoConfigure( void *lpvBuff, int *lpiBuffSize )
{
/*	sdcSinlineXL.iSerialPort = 0;
	sprintf( sdcSinlineXL.szSerialPort, "/dev/ttyS%d", sdcSinlineXL.iSerialPort );
*/
	g_bConfigInitiated = true;
	
	if (commSinlineXL.isInitialized()) {
		int iSize = UPS_CONFIGURATION_COUNT;
		if (SinlineXL_DoGetUPSStructures(EU_SETUP, &sdcSinlineXL.uiUpsConfiguration, &iSize) != IOCTL_ERROR_SUCCESS)
			return IOCTL_ERROR_CONFIGURATIONFAIL;
		iSize = UPS_CONFIGURATION_COUNT;
		int iErrPos = -1;
		if (SinlineXL_DoSetUPSStructures(EU_SETUP, &sdcSinlineXL.uiUpsConfiguration, &iSize, &iErrPos) != IOCTL_ERROR_SUCCESS)
			return IOCTL_ERROR_CONFIGURATIONFAIL;
	} else {
		SinlineXL_DoUnInit(NULL, NULL);
		if (SinlineXL_DoInit(NULL, NULL) != IOCTL_ERROR_SUCCESS)
			return IOCTL_ERROR_COMM_INITCFGFAIL;
		int iSize = UPS_CONFIGURATION_COUNT;
		if (SinlineXL_DoGetUPSStructures(EU_SETUP, &sdcSinlineXL.uiUpsConfiguration, &iSize) != IOCTL_ERROR_SUCCESS)
			return IOCTL_ERROR_CONFIGURATIONFAIL;
		iSize = UPS_CONFIGURATION_COUNT;
		int iErrPos = -1;
		if (SinlineXL_DoSetUPSStructures(EU_SETUP, &sdcSinlineXL.uiUpsConfiguration, &iSize, &iErrPos) != IOCTL_ERROR_SUCCESS)
			return IOCTL_ERROR_CONFIGURATIONFAIL;
	}
	
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoConfigure
// Description		: Configure driver to work properly with UPS
// Return type		: int 
// Argument         :  void *lpvBuff
// Argument         : int *lpiBuffSize
int SinlineXL_DoConfigure( void *lpvBuff, int *lpiBuffSize )
{
/*	sdcSinlineXL.iSerialPort = 0;
	sprintf( sdcSinlineXL.szSerialPort, "/dev/ttyS%d", sdcSinlineXL.iSerialPort );
*/	
	g_bConfigInitiated = true;
	
	if (commSinlineXL.isInitialized()) {
		SinlineXL_DoUnInit(NULL, NULL);
		SinlineXL_DoInit(NULL, NULL);
	}
	
	if (commSinlineXL.isInitialized()) {
		int iSize = UPS_CONFIGURATION_COUNT;
		if (SinlineXL_DoGetUPSStructures(EU_SETUP, &sdcSinlineXL.uiUpsConfiguration, &iSize) != IOCTL_ERROR_SUCCESS)
			return IOCTL_ERROR_CONFIGURATIONFAIL;
		iSize = UPS_CONFIGURATION_COUNT;
		int iErrPos = -1;
		if (SinlineXL_DoSetUPSStructures(EU_SETUP, &sdcSinlineXL.uiUpsConfiguration, &iSize, &iErrPos) != IOCTL_ERROR_SUCCESS)
			return IOCTL_ERROR_CONFIGURATIONFAIL;
	} else {
		SinlineXL_DoUnInit(NULL, NULL);
		if (SinlineXL_DoInit(NULL, NULL) != IOCTL_ERROR_SUCCESS)
			return IOCTL_ERROR_COMM_INITCFGFAIL;
		int iSize = UPS_CONFIGURATION_COUNT;
		if (SinlineXL_DoGetUPSStructures(EU_SETUP, &sdcSinlineXL.uiUpsConfiguration, &iSize) != IOCTL_ERROR_SUCCESS)
			return IOCTL_ERROR_CONFIGURATIONFAIL;
		iSize = UPS_CONFIGURATION_COUNT;
		int iErrPos = -1;
		if (SinlineXL_DoSetUPSStructures(EU_SETUP, &sdcSinlineXL.uiUpsConfiguration, &iSize, &iErrPos) != IOCTL_ERROR_SUCCESS)
			return IOCTL_ERROR_CONFIGURATIONFAIL;
	}
	
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoGetUpsInfoMask
// Description		: Zwraca informacje o wypelnieniu struktury informacji o UPS-ie
// Return type		: int 
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int SinlineXL_DoGetUpsInfoMask( void *lpvBuff, int *lpiBuffSize )
{
	unsigned long ulMask = UI_PARAMETERS | UI_SETUP_PARAMETERS | UI_UPS_STATE | UI_UPS_STATISTICS;

	if ( *lpiBuffSize == sizeof( unsigned long ) )
		memcpy( lpvBuff, &ulMask, sizeof( unsigned long ) );
	else
		return IOCTL_ERROR_INVALIDARGUMENT;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoSetUpsInfoMask
// Description		: 
// Return type		: 
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int	SinlineXL_DoSetUpsInfoMask(void *lpvBuff, int *lpiBuffSize)
{
	return IOCTL_ERROR_CMD_NOTSUPPORTED;
}

// Function name	: SinlineXL_DoGetUpsStateMask
// Description		: 
int	SinlineXL_DoGetUpsStateMask(void *lpvBuff, int *lpiBuffSize)
{
	unsigned long ulMask = US_POWERON | US_POWERFAIL | US_STANDBY | US_OVERLOAD | US_FAILURE |
		US_AVRUP | US_AVRLOWER | US_SHORT | US_BATTERYLOW | US_BYPASS | US_TEMPERATURETOOHIGH |
		US_BATTERYDEPLETED | US_BADEXTERNALMODULES | US_BATTERYCHARGING;

	if ( *lpiBuffSize == sizeof( unsigned long ) )
		memcpy( lpvBuff, &ulMask, sizeof( unsigned long ) );
	else
		return IOCTL_ERROR_INVALIDARGUMENT;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoSetUpsStateMask
// Description		: 
int	SinlineXL_DoSetUpsStateMask(void *lpvBuff, int *lpiBuffSize)
{
	return IOCTL_ERROR_CMD_NOTSUPPORTED;
}

// Function name	: SinlineXL_DoGetUpsState
// Description		: Return actual UPS state
int	SinlineXL_DoGetUpsState(void *lpvBuff, int *lpiBuffSize)
{
#undef CONVERT_BIT
#define CONVERT_BIT(src, sbit, dest, dbit) { if (src & sbit) dest |= dbit; else dest &= ~(dbit); }

	unsigned int *lpuiInfoTable = NULL;
	unsigned long ulUpsState = 0;
	int	iRet, iSize;

	if (lpiBuffSize != NULL)
		if (*lpiBuffSize < sizeof(ulUpsState))
			return IOCTL_ERROR_INVALIDARGUMENT;

	iSize = UPS_INFORMATION_COUNT;
	lpuiInfoTable = new unsigned int[iSize];
	if (!lpuiInfoTable)
		return IOCTL_ERROR;
	iRet = SinlineXL_DoGetUPSStructures(EU_INFORMATION, lpuiInfoTable, &iSize);
	if (iRet != IOCTL_ERROR_SUCCESS) {
		delete [] lpuiInfoTable;
		return iRet;
	}

	CONVERT_BIT( lpuiInfoTable[UII_STATES], 0x0001, ulUpsState, US_AVRUP );
	CONVERT_BIT( lpuiInfoTable[UII_STATES], 0x0002, ulUpsState, US_AVRLOWER );
	CONVERT_BIT( lpuiInfoTable[UII_STATES], 0x0004, ulUpsState, US_SHORT );
	CONVERT_BIT( lpuiInfoTable[UII_STATES], 0x0008, ulUpsState, US_OVERLOAD );
	CONVERT_BIT( lpuiInfoTable[UII_STATES], 0x0010, ulUpsState, US_TEMPERATURETOOHIGH );
	CONVERT_BIT( lpuiInfoTable[UII_STATES], 0x0020, ulUpsState, US_BATTERYDEPLETED );
	CONVERT_BIT( lpuiInfoTable[UII_STATES], 0x0040, ulUpsState, US_BATTERYLOW );
	CONVERT_BIT( lpuiInfoTable[UII_STATES], 0x0080, ulUpsState, US_BADEXTERNALMODULES );
	CONVERT_BIT( lpuiInfoTable[UII_STATES], 0x0100, ulUpsState, 0x0 );
	CONVERT_BIT( lpuiInfoTable[UII_STATES], 0x0200, ulUpsState, US_BYPASS );
	CONVERT_BIT( lpuiInfoTable[UII_STATES], 0x0800, ulUpsState, US_BATTERYCHARGING );

	switch((lpuiInfoTable[UII_STATES] & 0xF000) >> 12) {
		default:
		case 0: ulUpsState |= 0x0;			break;
		case 1: ulUpsState |= US_POWERON;	break;
		case 2: ulUpsState |= US_POWERFAIL;	break;
		case 3: ulUpsState |= US_STANDBY;	break;
		case 4: ulUpsState |= US_STANDBY;	break;
		case 5: ulUpsState |= US_BYPASS;	break;
		case 6: ulUpsState |= US_FAILURE;	break;
	}

	delete [] lpuiInfoTable;
	
	memcpy( lpvBuff, &ulUpsState, *lpiBuffSize );

	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoGetUpsParamsMask
// Description		: 
int	SinlineXL_DoGetUpsParamsMask(void *lpvBuff, int *lpiBuffSize)
{
	long long iMask = UP_VLT_INPUT | UP_VLT_OUTPUT | UP_VLT_BATTERY | UP_FRQ_INPUT | 
		UP_FRQ_OUTPUT | UP_PWR_EFFECTIVE | UP_PWR_APPARENT | UP_PRC_POWERWEIGHT | 
		UP_CUR_OUTPUT | UP_PRC_BATTERYCAPACITY | UP_TMV_AUTONOMY | UP_UID_VERSION | 
		UP_UID_MODEL | UP_PWR_COEFFICIENT |
		// extended params
		UP_EX_CUR_ACCU | UP_EX_CUR_INPUT | UP_EX_PWR_INP_APPARENT | 
		UP_EX_PWR_INP_COEFF | UP_EX_PWR_INP_EFFECTIVE | 
		UP_EX_TMP_INVERTER_LEFT | UP_EX_TMP_INVERTER_RIGHT;
	if ( *lpiBuffSize == sizeof( __int64 ) )
		memcpy( lpvBuff, &iMask, sizeof( __int64 ) );
	else
		return IOCTL_ERROR_INVALIDARGUMENT;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoSetUpsParamsMask
// Description		: 
// Return type		: 
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int	SinlineXL_DoSetUpsParamsMask(void *lpvBuff, int *lpiBuffSize)
{
	return IOCTL_ERROR_CMD_NOTSUPPORTED;
}

// Function name	: SinlineXL_DoGetUpsParams
// Description		: 
// Return type		: 
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int	SinlineXL_DoGetUpsParams(void *lpvBuff, int *lpiBuffSize)
{
	unsigned int *lpuiInfoTable = NULL;
	int	iSize, iRet, iv[2], iv2[2];
	
	if (lpiBuffSize != NULL)
		if (*lpiBuffSize < sizeof(supsp))
			return IOCTL_ERROR_INVALIDARGUMENT;

	iSize = UPS_INFORMATION_COUNT;
	lpuiInfoTable = new unsigned int[iSize];
	iRet = SinlineXL_DoGetUPSStructures(EU_INFORMATION, lpuiInfoTable, &iSize);
	if (iRet != IOCTL_ERROR_SUCCESS) {
		delete [] lpuiInfoTable;
		return iRet;
	}
	// standard parameters
	g_SinlineXL_sup.iMask = UP_VLT_INPUT | UP_VLT_OUTPUT | UP_VLT_BATTERY | UP_FRQ_INPUT | 
		UP_FRQ_OUTPUT | UP_PWR_EFFECTIVE | UP_PWR_APPARENT | UP_PRC_POWERWEIGHT | 
		UP_CUR_OUTPUT | UP_PRC_BATTERYCAPACITY | UP_TMV_AUTONOMY | UP_UID_VERSION | 
		UP_UID_MODEL | UP_PWR_COEFFICIENT |
		// extended parameters
		UP_EX_CUR_ACCU | UP_EX_CUR_INPUT | UP_EX_PWR_INP_APPARENT | 
		UP_EX_PWR_INP_COEFF | UP_EX_PWR_INP_EFFECTIVE | 
		UP_EX_TMP_INVERTER_LEFT | UP_EX_TMP_INVERTER_RIGHT;


	/* 
	 * Modified on 20/april/2006, added support for multiple families (currently two)
	 */
	g_iUpsFamily = (lpuiInfoTable[UII_TYPE] >> 8) & 0xFF;
	switch(g_iUpsFamily) {
		case UPS_FAMILYP9:
			sprintf((char*)g_SinlineXL_sup.sz__uid_family, ("%s"), DRIVER_UPS_FAMILYP9);
			sprintf((char*)g_SinlineXL_sup.sz__uid_model, ("%s %ldVA/%ldW"), 
				DRIVER_UPS_PREFIXP9, lpuiInfoTable[UII_PWR_APPARENT], lpuiInfoTable[UII_PWR_EFFECTIVE]);
			break;
		case UPS_FAMILYP25:
			sprintf((char*)g_SinlineXL_sup.sz__uid_family, ("%s"), DRIVER_UPS_FAMILYP25);
			sprintf((char*)g_SinlineXL_sup.sz__uid_model, ("%s %ldVA/%ldW"), 
				DRIVER_UPS_PREFIXP25, lpuiInfoTable[UII_PWR_APPARENT], lpuiInfoTable[UII_PWR_EFFECTIVE]);
			break;
	}

	g_iUpsSeries = 0;

	g_iFirmwareRev = (lpuiInfoTable[UII_FIRMMWARE] >> 8) & 0xFF;
	iv[0] = (g_iFirmwareRev & 0xFF) >> 4;
	iv[1] = (g_iFirmwareRev & 0x0F);

	if ((g_bFirmwareUnsupportedPass==false) &&
		(g_iFirmwareRev>FIRMWARE_LAST || g_iFirmwareRev<FIRMWARE_FIRST))
	{
		g_bFirmwareUnsupportedPass=true;
		iv2[0] = ( FIRMWARE_LAST & 0xFF ) >> 4;
		iv2[1] = ( FIRMWARE_LAST & 0x0F );
		
		// unsupported version of UPS firmware alert message
		console_report(TXT_ERR_FIRMWARE_UNSUPPORTED, g_SinlineXL_sup.sz__uid_model, iv[0], iv[1], iv2[0], iv2[1]);

		delete [] lpuiInfoTable;
		return IOCTL_CRITICAL_ERROR;
	}

	sprintf((char*)g_SinlineXL_sup.sz__uid_version, ("Firmware: %d.%d #:%d"), 
		/*version*/iv[0], iv[1], /*build*/lpuiInfoTable[UII_FIRMMWARE] & 0xFF);

	g_SinlineXL_sup.ui__vlt_input = lpuiInfoTable[UII_VOLT_INP];
	g_SinlineXL_sup.ui__vlt_input_div = 1;

	g_SinlineXL_sup.ui__vlt_output = lpuiInfoTable[UII_VOLT_OUT];
	g_SinlineXL_sup.ui__vlt_output_div = 1;

	g_SinlineXL_sup.ui__cur_output = lpuiInfoTable[UII_CUR_OUT];
	g_SinlineXL_sup.ui__cur_output_div = 10;

	g_SinlineXL_sup.ui__frq_input = lpuiInfoTable[UII_FREQ_INP];
	g_SinlineXL_sup.ui__frq_input_div = 10;

	g_SinlineXL_sup.ui__frq_output = lpuiInfoTable[UII_FREQ_OUT];
	g_SinlineXL_sup.ui__frq_output_div = 10;

	g_SinlineXL_sup.ui__pwr_effective = lpuiInfoTable[UII_PWR_EFFECTIVE_OUT];
	g_SinlineXL_sup.ui__pwr_effective_div = 1;

	g_SinlineXL_sup.ui__pwr_apparent = lpuiInfoTable[UII_PWR_APPARENT_OUT];
	g_SinlineXL_sup.ui__pwr_apparent_div = 1;

	g_SinlineXL_sup.ui__pwr_coefficient = lpuiInfoTable[UII_PWR_OUT_COEFF];
	g_SinlineXL_sup.ui__pwr_coefficient_div = 100;

	g_SinlineXL_sup.ui__prc_powerweight = lpuiInfoTable[UII_PWR_OUT_PERC];
	g_SinlineXL_sup.ui__prc_powerweight_div = 1;

	g_SinlineXL_sup.ui__prc_batterycapacity = lpuiInfoTable[UII_CAPACITY_ACCU];
	g_SinlineXL_sup.ui__prc_batterycapacity_div = 1;

	g_SinlineXL_sup.ui__tmv_autonomy = lpuiInfoTable[UII_TIME_AUTONOMY];
	g_SinlineXL_sup.ui__tmv_autonomy_div = 1;

	g_SinlineXL_sup.ui__vlt_battery = lpuiInfoTable[UII_VOLT_ACCU];
	g_SinlineXL_sup.ui__vlt_battery_div = 10;

	if (g_SinlineXL_sup.ui__pwr_effective > g_SinlineXL_sup.ui__pwr_apparent)
		g_SinlineXL_sup.ui__pwr_effective = g_SinlineXL_sup.ui__pwr_apparent;

	if (g_SinlineXL_sup.ui__prc_powerweight<0) g_SinlineXL_sup.ui__prc_powerweight=0;
	
	g_SinlineXL_sup.ui_ex_pwr_inp_coeff = lpuiInfoTable[UII_PWR_IN_COEFF];
	g_SinlineXL_sup.ui_ex_pwr_inp_coeff_div = 100;

	g_SinlineXL_sup.ui_ex_cur_battery = lpuiInfoTable[UII_CUR_ACCU];
	g_SinlineXL_sup.ui_ex_cur_battery_div = 10;

	g_SinlineXL_sup.ui_ex_cur_input = lpuiInfoTable[UII_CUR_INP];
	g_SinlineXL_sup.ui_ex_cur_input_div = 10;

	g_SinlineXL_sup.ui_ex_pwr_inp_effective = lpuiInfoTable[UII_PWR_EFFECTIVE_INP];
	g_SinlineXL_sup.ui_ex_pwr_inp_effective_div = 1;

	g_SinlineXL_sup.ui_ex_pwr_inp_apparent = lpuiInfoTable[UII_PWR_APPARENT_INP];
	g_SinlineXL_sup.ui_ex_pwr_inp_apparent_div = 1;

	g_SinlineXL_sup.ui_ex_tmp_inverter_left = lpuiInfoTable[UII_TEMP_INTERNAL1];
	g_SinlineXL_sup.ui_ex_tmp_inverter_left_div = 1;

	g_SinlineXL_sup.ui_ex_tmp_inverter_right = lpuiInfoTable[UII_TEMP_INTERNAL2];
	g_SinlineXL_sup.ui_ex_tmp_inverter_right_div = 1;

	memcpy(lpvBuff, &g_SinlineXL_sup, sizeof(supsp));

	delete [] lpuiInfoTable;

	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoGetUpsSetupParamsMask
// Description		: 
// Return type		: 
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int	SinlineXL_DoGetUpsSetupParamsMask(void *lpvBuff, int *lpiBuffSize)
{
	unsigned long ulMask = UP_SET_AAL_ON|UP_SET_TMR_POWERFAILTOSTANDBY|
		UP_SET_PST_AVRLOW|UP_SET_PST_LOWER|UP_SET_PST_UPPER;

	if ( *lpiBuffSize == sizeof( unsigned long ) )
		memcpy( lpvBuff, &ulMask, sizeof( unsigned long ) );
	else
		return IOCTL_ERROR_INVALIDARGUMENT;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoSetUpsSetupParamsMask
// Description		: 
// Return type		: int
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int	SinlineXL_DoSetUpsSetupParamsMask(void *lpvBuff, int *lpiBuffSize)
{
	return IOCTL_ERROR_CMD_NOTSUPPORTED;
}

// Function name	: SinlineXL_DoGetUpsSetupParams
// Description		: 
// Return type		: int
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int	SinlineXL_DoGetUpsSetupParams(void *lpvBuff, int *lpiBuffSize)
{
	supssp	susp;

	susp.ulMask = UP_SET_AAL_ON|UP_SET_TMR_POWERFAILTOSTANDBY|
		UP_SET_PST_AVRLOW|UP_SET_PST_LOWER|UP_SET_PST_UPPER;

	susp.ui__set_aal_on = flag_check((unsigned long)sdcSinlineXL.uiUpsConfiguration[UCI_CONTROL], UCCI_BUZZER_ENABLED);
	susp.ui__set_tmr_powerfailtostandby = sdcSinlineXL.uiUpsConfiguration[UCI_TIMER_STANDBY];
	susp.ui__set_tmr_powerfailtostandby_div = 1;
	susp.ui__set_pst_avrlow = sdcSinlineXL.uiUpsConfiguration[UCI_AVR_TRIM_TRANSFER_TRESHOLDS];
	susp.ui__set_pst_avrlow_div = 1;
	susp.ui__set_pst_lower = sdcSinlineXL.uiUpsConfiguration[UCI_PF_LOWER_TRANSFER_TRESHOLDS];
	susp.ui__set_pst_lower_div = 1;
	susp.ui__set_pst_upper = sdcSinlineXL.uiUpsConfiguration[UCI_PF_UPPER_TRANSFER_TRESHOLDS];
	susp.ui__set_pst_upper_div = 1;

	memcpy( lpvBuff, &susp, *lpiBuffSize );

	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoSetUpsSetupParams
// Description		: 
// Return type		: int
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int	SinlineXL_DoSetUpsSetupParams(void *lpvBuff, int *lpiBuffSize)
{
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoGetDriverInfo
// Description		: 
// Return type		: 
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int	SinlineXL_DoGetDriverInfo(void *lpvBuff, int *lpiBuffSize)
{
	if (*lpiBuffSize != sizeof(sdrv_info))
		return IOCTL_ERROR_INVALIDARGUMENT;

	sprintf( (char*)(( lpsdrv_info )lpvBuff )->szName, "%s/%s", DRIVER_UPS_PREFIXP9, DRIVER_UPS_PREFIXP25 );
	sprintf( (char*)(( lpsdrv_info )lpvBuff )->szFamily, "%s/%s", DRIVER_UPS_FAMILYP9, DRIVER_UPS_FAMILYP25 );
	((lpsdrv_info)lpvBuff)->eLink = ul_serial;
	((lpsdrv_info)lpvBuff)->uiVersionMajor = DRIVER_VERSION_MAJOR;
	((lpsdrv_info)lpvBuff)->uiVersionMinor = DRIVER_VERSION_MINOR;
	strcpy(((lpsdrv_info)lpvBuff)->szCfgFileName, DRIVER_CONFIG_FILE);
	strcpy(((lpsdrv_info)lpvBuff)->szBmpFileName, DRIVER_BITMAP_FILE);


	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoGetExtendedError
// Description		: Get extended error information - return value of last error
// Return type		: int
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int	SinlineXL_DoGetExtendedError(void *lpvBuff, int *lpiBuffSize)
{
	return g_iSinlineXL_ExtError;
}

// Function name	: SinlineXL_DoGetUpsCharacteristicMask
// Description		: 
// Return type		: 
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int	SinlineXL_DoGetUpsCharacteristicMask(void *lpvBuff, int *lpiBuffSize)
{
	unsigned long ulMask = 0;

	if ( *lpiBuffSize == sizeof( unsigned long ) )
		memcpy( lpvBuff, &ulMask, sizeof( unsigned long ) );
	else
		return IOCTL_ERROR_INVALIDARGUMENT;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoGetUpsCharacteristic
// Description		: 
// Return type		: 
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int	SinlineXL_DoGetUpsCharacteristic(void *lpvBuff, int *lpiBuffSize)
{
	return IOCTL_ERROR_CMD_NOTSUPPORTED;
}

// Function name	: SinlineXL_DoTestUpsLink
// Description		: 
// Return type		: 
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int	SinlineXL_DoTestUpsLink(void *lpvBuff, int *lpiBuffSize)
{
	unsigned int *lpuiInfoTable = new unsigned int[UPS_INFORMATION_COUNT];
	int	iSize = UPS_INFORMATION_COUNT, iRet;

	iRet = SinlineXL_DoGetUPSStructures(EU_INFORMATION, lpuiInfoTable, &iSize);
	delete [] lpuiInfoTable;

	return iRet;
}

// Function name	: SinlineXL_DoGetUPSStructures
// Description		: Return specified structure from UPS
// Return type		: int 
// Argument         : eups_structs eType
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int SinlineXL_DoGetUPSStructures(eups_structs eType, void *lpvBuff, int *lpiBuffSize)
{
/* 
 * Helper macros
 */
#undef COMMIOFAIL
#define COMMIOFAIL( v, en ) { if ( COMMFAIL( v ) ) {	\
	g_iSinlineXL_ExtError = en; return IOCTL_ERROR_COMM_LINKFAIL; } }

	unsigned char	ucDataBuff[0xFF] = { 0 },
					ucLength = 0,
					ucCRC = 0;
	int				iindex;
	unsigned int	uiDataBuff[0xFF] = { 0 };
	unsigned long	ulCRC = 0, ulCRC2 = 0;
	bool			bInitialized = false;
	
	if (!commSinlineXL.isInitialized())
		return IOCTL_ERROR_NOTYETINITIALIZED;

	// Zapytanie UPS'a
	for (int i = 0; i < 10; i++) {
		ucDataBuff[0] = 0;

		commSinlineXL.flush(1,1);
		
		commSinlineXL.send(FRAME_INITCODE);
		commSinlineXL.receive(&ucDataBuff[0]);

		if (ucDataBuff[0] == FRAME_INITCODE) {
			bInitialized = true;
			break;
		}
		usleep(20000);
	}
	if (!bInitialized) {
		g_iSinlineXL_ExtError = IOCTL_ERROR_COMM_INVALIDCODE;
		return IOCTL_ERROR_COMM_LINKFAIL;
	}
	COMMIOFAIL( commSinlineXL.send( FRAME_START ), IOCTL_ERROR_COMM_FAILTOSEND );
	ucLength = 3;
	COMMIOFAIL( commSinlineXL.send( ucLength ), IOCTL_ERROR_COMM_FAILTOSEND );
	COMMIOFAIL( commSinlineXL.send( FRAME_CMD_GET ), IOCTL_ERROR_COMM_FAILTOSEND );
	ucCRC += FRAME_CMD_GET;
	COMMIOFAIL( commSinlineXL.send( eType ), IOCTL_ERROR_COMM_FAILTOSEND );
	ucCRC += eType;
	ucCRC %= 256;
	COMMIOFAIL( commSinlineXL.send( ucCRC ), IOCTL_ERROR_COMM_FAILTOSEND );
	/* 
	 * Odpowiedz od UPS'a
	 */
	ucDataBuff[0] = ucDataBuff[1] = ucDataBuff[2] = 0;
	COMMIOFAIL( commSinlineXL.receive( &ucDataBuff[0] ), IOCTL_ERROR_COMM_FAILTORECEIVE );
	if (ucDataBuff[0] != FRAME_START) {
		g_iSinlineXL_ExtError = IOCTL_ERROR_COMM_INVALIDFRAME;
		return IOCTL_ERROR_COMM_LINKFAIL;
	}
	
	COMMIOFAIL( commSinlineXL.receive( &ucLength ), IOCTL_ERROR_COMM_FAILTORECEIVE );

	/* :: length = (length - crc_byte) / 2 */
	if ( ((ucLength - 1) / 2) > *lpiBuffSize ) {
		g_iSinlineXL_ExtError = IOCTL_ERROR_COMM_INVALIDLENGTH;
		return IOCTL_ERROR_COMM_LINKFAIL;
	}
	ulCRC=0;
	iindex=0;

	for (int i = 0; i < *lpiBuffSize; i++) {
		COMMIOFAIL( commSinlineXL.receive( &ucDataBuff[0] ), IOCTL_ERROR_COMM_FAILTORECEIVE );
		COMMIOFAIL( commSinlineXL.receive( &ucDataBuff[1] ), IOCTL_ERROR_COMM_FAILTORECEIVE );
		uiDataBuff[iindex]=(unsigned int)(ucDataBuff[0] + (ucDataBuff[1]<<8));
		ulCRC += ucDataBuff[1] + ucDataBuff[0];
		iindex++;
	}

	//CRC
	COMMIOFAIL( commSinlineXL.receive( &ucCRC ), IOCTL_ERROR_COMM_FAILTORECEIVE );

	ulCRC %= 256;
	if (ulCRC != ucCRC) {
		g_iSinlineXL_ExtError = IOCTL_ERROR_COMM_INVALIDCRC;
		return IOCTL_ERROR_COMM_LINKFAIL;
	}
	
	memcpy( lpvBuff, &uiDataBuff, *lpiBuffSize * sizeof( unsigned int ) );

	g_iSinlineXL_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoSetUPSStructures
// Description		: 
// Return type		: int 
// Argument         : eups_structs eType
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
// Argument         : int *lpiBuffErrPos
int SinlineXL_DoSetUPSStructures(eups_structs eType, void *lpvBuff, int *lpiBuffSize, int *lpiBuffErrPos )
{
/* 
 * Helper macros
 */
#undef COMMIOFAIL
#define COMMIOFAIL( v, en ) { if ( COMMFAIL( v ) ) {	\
	g_iSinlineXL_ExtError = en; return IOCTL_ERROR_COMM_LINKFAIL; } }

	unsigned char	ucDataBuff[0xFF] = { 0 },
					ucLength = 0,
					ucErrCode = 0,
					ucCRC = 0;
	unsigned int	uiDataBuff[0xFF] = { 0 };
	unsigned long	ulCRC = 0;
	bool			bInitialized = false;

	if ( !commSinlineXL.isInitialized() )
		return IOCTL_ERROR_NOTYETINITIALIZED;

	if ( !lpvBuff )
		return IOCTL_ERROR_INVALIDARGUMENT;
	memcpy( &uiDataBuff, lpvBuff, *lpiBuffSize * sizeof(unsigned int));

	/* 
	 * Zapytanie UPS'a
	 */
	for (int i = 0; i < 10; i++) {
		ucDataBuff[0] = 0;

		commSinlineXL.flush(1, 1);

		commSinlineXL.send(FRAME_INITCODE);
		commSinlineXL.receive(&ucDataBuff[0]);

		if (ucDataBuff[0] == FRAME_INITCODE) {
			bInitialized = true;
			break;
		}
		usleep(50000);
	}
	if (!bInitialized) {
		g_iSinlineXL_ExtError = IOCTL_ERROR_COMM_INVALIDCODE;
		return IOCTL_ERROR_COMM_LINKFAIL;
	}
	commSinlineXL.flush(1, 0);
	COMMIOFAIL( commSinlineXL.send( FRAME_START ), IOCTL_ERROR_COMM_FAILTOSEND );
	ucLength = 3 + (*lpiBuffSize * 2);
	//ucLength = 3 + (*lpiBuffSize);
	ucCRC = 0;
	COMMIOFAIL( commSinlineXL.send( ucLength ), IOCTL_ERROR_COMM_FAILTOSEND );
	COMMIOFAIL( commSinlineXL.send( FRAME_CMD_SET ), IOCTL_ERROR_COMM_FAILTOSEND );
	ucCRC += FRAME_CMD_SET;
	COMMIOFAIL( commSinlineXL.send( eType ), IOCTL_ERROR_COMM_FAILTOSEND );
	ucCRC += eType;
	//COMMIOFAIL( commSinlineXL.Send2b( (unsigned int *)lpvBuff, *lpiBuffSize ), IOCTL_ERROR_COMM_FAILTOSEND );

	for (int i = 0; i < *lpiBuffSize; i++) {
		ucDataBuff[0] = uiDataBuff[i] & 0x00FF;
		ucDataBuff[1] = (uiDataBuff[i] & 0xFF00) >> 8;
		COMMIOFAIL( commSinlineXL.send( ucDataBuff[0] ), IOCTL_ERROR_COMM_FAILTOSEND );
		COMMIOFAIL( commSinlineXL.send( ucDataBuff[1] ), IOCTL_ERROR_COMM_FAILTOSEND );
		ucCRC += ucDataBuff[0] + ucDataBuff[1];
	}
	ucCRC %= 256;
	
	COMMIOFAIL( commSinlineXL.send( ucCRC ), IOCTL_ERROR_COMM_FAILTOSEND );

	// Odpowiedz od UPS'a
	ucDataBuff[0] = ucDataBuff[1] = ucDataBuff[2] = 0;

	usleep(100000);
	commSinlineXL.receive( ucDataBuff, 2 );
	if (ucDataBuff[0] != FRAME_START) {
		g_iSinlineXL_ExtError = IOCTL_ERROR_COMM_INVALIDFRAME;
		return IOCTL_ERROR_COMM_LINKFAIL;
	}
	ucErrCode = ucDataBuff[1];
	switch( ucErrCode )
	{
	case 0x05:	// Nieprawidowa ilo�bajt� w ramce
		g_iSinlineXL_ExtError = IOCTL_ERROR_COMM_BADFRAMELENGTH;
		return IOCTL_ERROR_COMM_LINKFAIL;
	case 0x0A:	// Nieznany typ rozkazu
		g_iSinlineXL_ExtError = IOCTL_ERROR_COMM_UNKNOWNCOMMANDTYPE;
		return IOCTL_ERROR_COMM_LINKFAIL;
	case 0x15:	// Nieznany rozkaz
		g_iSinlineXL_ExtError = IOCTL_ERROR_COMM_UNKNOWNCOMMAND;
		return IOCTL_ERROR_COMM_LINKFAIL;
	case 0x20:	// Bd CRC
		g_iSinlineXL_ExtError = IOCTL_ERROR_COMM_BADCRC;
		return IOCTL_ERROR_COMM_LINKFAIL;
	case 0x5F:	// Data integrity error
		COMMIOFAIL( commSinlineXL.receive( &ucErrCode ), IOCTL_ERROR_COMM_FAILTORECEIVE );
		if ( lpiBuffErrPos != NULL )
			*lpiBuffErrPos = ucErrCode;
		g_iSinlineXL_ExtError = IOCTL_ERROR_COMM_DATAINTEGRITY;
		return IOCTL_ERROR_COMM_LINKFAIL;
	case 0xFF:	// Success = no error
		break;
	}

	g_iSinlineXL_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoGetConfigParamsCount
// Description		: 
// Return type		: int
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int	SinlineXL_DoGetConfigParamsCount(void *lpvBuff, int *lpiBuffSize)
{
	unsigned long ulCount = INT_MAX_SETUPITEMS;

	if ( lpiBuffSize != NULL )
		if ( *lpiBuffSize < sizeof( unsigned long ) )
			return IOCTL_ERROR_INVALIDARGUMENT;

	memcpy( lpvBuff, &ulCount, sizeof( ulCount ) );

	g_iSinlineXL_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoGetConfigParams
// Description		:
int	SinlineXL_DoGetConfigParams(void *, int *)
{
	g_iSinlineXL_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoSetConfigParams
// Description		:
int	SinlineXL_DoSetConfigParams(void *lpvBuff, int *lpiBuffSize)
{
	int	iSize, iRet;
	unsigned long ulSize;
	scfg_value scfgvaldef[ INT_MAX_SETUPITEMS ];
	char szDrvCfgPath[MAX_PATH];

	if ( *lpiBuffSize != (sizeof(scfg_value) * INT_MAX_SETUPITEMS) )
		return IOCTL_ERROR_INVALIDARGUMENT;
	else
		memcpy( &scfgvaldef, lpvBuff, *lpiBuffSize );

	// Port komunikacyjny
	sdcSinlineXL.iSerialPort = scfgvaldef[ INT_UPSCFG_SERIALCOMM ].value.iValue + 1;
	sprintf(sdcSinlineXL.szSerialPort, "/dev/ttyS%d", sdcSinlineXL.iSerialPort);

	if (scfgvaldef[ INT_UPSCFG_BUZZER ].value.iValue == 1)
		sdcSinlineXL.uiUpsConfiguration[UCI_CONTROL] |= UCCI_BUZZER_ENABLED;
	else
		sdcSinlineXL.uiUpsConfiguration[UCI_CONTROL] &= ~(UCCI_BUZZER_ENABLED);

	if (scfgvaldef[ INT_UPSCFG_FORCE_STANDBY ].value.iValue == 1)
		sdcSinlineXL.uiUpsConfiguration[UCI_CONTROL] |= UCCI_FORCE_STANDBY;
	else
		sdcSinlineXL.uiUpsConfiguration[UCI_CONTROL] &= ~(UCCI_FORCE_STANDBY);

	sdcSinlineXL.uiUpsConfiguration[UCI_PF_UPPER_TRANSFER_TRESHOLDS] = 
		scfgvaldef[INT_UPSCFG_TR_TSH_UPPER].value.iValue;
	sdcSinlineXL.uiUpsConfiguration[UCI_PF_LOWER_TRANSFER_TRESHOLDS] = 
		scfgvaldef[INT_UPSCFG_TR_TSH_LOWER].value.iValue;

	sdcSinlineXL.uiUpsConfiguration[UCI_AVR_TRIM_TRANSFER_TRESHOLDS] = 
		scfgvaldef[INT_UPSCFG_TR_TSH_AVRTRIM].value.iValue;

	if ((int)(scfgvaldef[ INT_UPSCFG_STANDBYDELAY ].value.iValue) < 60) {
		console_report( TXT_INF_STANDBYTIMETOOSHORT );
	}
	sdcSinlineXL.uiUpsConfiguration[UCI_TIMER_STANDBY] = (int)(
		scfgvaldef[INT_UPSCFG_STANDBYDELAY].value.iValue * 
		scfgvaldef[INT_UPSCFG_STANDBYDELAY].dDivider);
	if (sdcSinlineXL.uiUpsConfiguration[UCI_TIMER_STANDBY]<0) 
		sdcSinlineXL.uiUpsConfiguration[UCI_TIMER_STANDBY]=0;

	sdcSinlineXL.uiUpsConfiguration[UCI_MODULE_NO] = (int)(
		scfgvaldef[INT_UPSCFG_MODULE_NO].value.iValue * 
		scfgvaldef[INT_UPSCFG_MODULE_NO].dDivider);

	if (commSinlineXL.isInitialized()) {
		iSize = UPS_CONFIGURATION_COUNT;
		int iExtErrCode = 0;
		iRet = SinlineXL_DoSetUPSStructures(EU_SETUP, sdcSinlineXL.uiUpsConfiguration, &iSize, &iExtErrCode);
		if (iRet != IOCTL_ERROR_SUCCESS) {
			sdcSinlineXL.iConfigError = g_iSinlineXL_ExtError;
			sdcSinlineXL.iConfigErrorArg = iExtErrCode;
			iSize = UPS_CONFIGURATION_COUNT;
			iRet = SinlineXL_DoGetUPSStructures(EU_SETUP, sdcSinlineXL.uiUpsConfiguration, &iSize);
			return iRet;
		}
	}

	g_iSinlineXL_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoGetConfigParam
// Description		: 
// Return type		: int
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int	SinlineXL_DoGetConfigParam(void *lpvBuff, int *lpiBuffSize)
{
	g_iSinlineXL_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoSetConfigParam
// Description		: 
// Return type		: int
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int	SinlineXL_DoSetConfigParam(void *lpvBuff, int *lpiBuffSize)
{
	g_iSinlineXL_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoUpdateConfig
// Description		: 
// Return type		: 
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int	SinlineXL_DoUpdateConfig(void *lpvBuff, int *lpiBuffSize)
{
	g_iSinlineXL_ExtError = 0;

	if (SinlineXL_DoReadConfig() != IOCTL_ERROR_SUCCESS)
		return IOCTL_ERROR_CONFIG_READFAIL;
	
	if (sdcSinlineXL.bAudibleAlarm)
		sdcSinlineXL.uiUpsConfiguration[UCI_CONTROL] |= UCCI_BUZZER_ENABLED;
	else
		sdcSinlineXL.uiUpsConfiguration[UCI_CONTROL] &= ~(UCCI_BUZZER_ENABLED);

	if (sdcSinlineXL.bForceStandby)
		sdcSinlineXL.uiUpsConfiguration[UCI_CONTROL] |= UCCI_FORCE_STANDBY;
	else
		sdcSinlineXL.uiUpsConfiguration[UCI_CONTROL] &= ~(UCCI_FORCE_STANDBY);

	sdcSinlineXL.uiUpsConfiguration[UCI_PF_UPPER_TRANSFER_TRESHOLDS] = sdcSinlineXL.uiTransferTrsh_Upper;
	sdcSinlineXL.uiUpsConfiguration[UCI_PF_LOWER_TRANSFER_TRESHOLDS] = sdcSinlineXL.uiTransferTrsh_Lower;
	sdcSinlineXL.uiUpsConfiguration[UCI_AVR_BOOST_TRANSFER_TRESHOLDS] = sdcSinlineXL.uiTransferTrsh_AVRBoost;

	// timer in sec. for powerfail to online transfer
	if (sdcSinlineXL.uiStandbyDelay < 60) {
		console_report( TXT_INF_STANDBYTIMETOOSHORT );
	}
	sdcSinlineXL.uiUpsConfiguration[UCI_TIMER_STANDBY] = sdcSinlineXL.uiStandbyDelay;
	if (sdcSinlineXL.uiUpsConfiguration[UCI_TIMER_STANDBY]<0) 
		sdcSinlineXL.uiUpsConfiguration[UCI_TIMER_STANDBY]=90;
	
	sdcSinlineXL.uiUpsConfiguration[UCI_MODULE_NO] = sdcSinlineXL.uiModuleNumber;
	
	// capacity treshold at which ups will switch to online mode
	sdcSinlineXL.uiUpsConfiguration[UCI_CAPACITY_FOR_STARTUP] = 0;
	sdcSinlineXL.uiUpsConfiguration[UCI_FREQ_OUT] = 500;
	sdcSinlineXL.uiUpsConfiguration[UCI_VOLT_OUT] = 230;
	
	// timer in sec. for standby to online transfer
	sdcSinlineXL.uiUpsConfiguration[UCI_TIMER_STARTUP] = 0;

	if (commSinlineXL.isInitialized()) {
		int iSize = UPS_CONFIGURATION_COUNT;
		int iExtErrCode = 0;
		int iRet = SinlineXL_DoSetUPSStructures(EU_SETUP, sdcSinlineXL.uiUpsConfiguration, &iSize, &iExtErrCode);
		if (iRet != IOCTL_ERROR_SUCCESS) {
			sdcSinlineXL.iConfigError = g_iSinlineXL_ExtError;
			sdcSinlineXL.iConfigErrorArg = iExtErrCode;
			return iRet;
		}
	}

	g_iSinlineXL_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoUpsShutdown
// Description		: 
int SinlineXL_DoUpsShutdown(void *lpvBuff, int *lpiBuffSize)
{
	unsigned int *lpuiSetupTable = NULL;
	int iRet, iSize;

	iSize = UPS_CONFIGURATION_COUNT;
	lpuiSetupTable = new unsigned int[iSize];
	iRet = SinlineXL_DoGetUPSStructures( EU_SETUP, lpuiSetupTable, &iSize );
	if (iRet != IOCTL_ERROR_SUCCESS) {
		delete [] lpuiSetupTable;
		return iRet;
	}
	unsigned int uiTemp = lpuiSetupTable[UCI_CONTROL];
	memcpy(lpuiSetupTable, sdcSinlineXL.uiUpsConfiguration, sizeof(unsigned int)*UPS_CONFIGURATION_COUNT);
	lpuiSetupTable[UCI_CONTROL] = uiTemp;
	lpuiSetupTable[UCI_CONTROL] &= ~(UCCI_FORCE_STANDBY);
	lpuiSetupTable[UCI_CONTROL] |= UCCI_FORCE_STANDBY;

	iSize = UPS_CONFIGURATION_COUNT;
	iRet = SinlineXL_DoSetUPSStructures( EU_SETUP, lpuiSetupTable, &iSize, NULL );
	if ( iRet != IOCTL_ERROR_SUCCESS ) {
		delete [] lpuiSetupTable;
		return iRet;
	}

	delete [] lpuiSetupTable;
	g_iSinlineXL_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoGetUpsStatisticsCount
// Description		: 
int	SinlineXL_DoGetUpsStatisticsCount(void *lpvBuff, int *lpiBuffSize)
{
	unsigned long ulCount = 0, ulTest = 0;
	if (lpvBuff != NULL) {
		memcpy(&ulTest, lpvBuff, sizeof(unsigned long));
		if (ulTest & 0x1)	// main statistics
			ulCount += UPS_STATISTICS_COUNT;
		if (ulTest & 0x2)	// extended statistic
			ulCount += UPS_FACTORYCONTROL_COUNT;
		if (ulTest == 0)
			ulCount = UPS_STATISTICS_COUNT;
	} else
		return IOCTL_ERROR_INVALIDARGUMENT;

	if (lpiBuffSize != NULL)
		if (*lpiBuffSize < sizeof(unsigned long))
			return IOCTL_ERROR_INVALIDARGUMENT;

	memcpy(lpvBuff, &ulCount, sizeof(ulCount));
	g_iSinlineXL_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoGetUpsStatistics
// Description		: 
int	SinlineXL_DoGetUpsStatistics(void *lpvBuff, int *lpiBuffSize)
{
	if ((*lpiBuffSize != (sizeof(STATISTIC)*UPS_STATISTICS_COUNT)) &&
		(*lpiBuffSize != (sizeof(STATISTIC)*UPS_FACTORYCONTROL_COUNT)) &&
		(*lpiBuffSize != (sizeof(STATISTIC)*(UPS_STATISTICS_COUNT + UPS_FACTORYCONTROL_COUNT))))
		return IOCTL_ERROR_INVALIDARGUMENT;

	LPSTATISTIC pStat = (LPSTATISTIC)lpvBuff;

	if (*lpiBuffSize == (sizeof(STATISTIC)*UPS_STATISTICS_COUNT)) {
		unsigned int uiStats[UPS_STATISTICS_COUNT];
		char tabStatNames[UPS_STATISTICS_COUNT][STAT_NAME_MAX] = {
			TXT_STAT_PWRFAILURE, TXT_STAT_PWRDROP, TXT_STAT_PWRINCREASE, TXT_STAT_OVERLOAD,
			TXT_STAT_SHORT, TXT_STAT_DISCHARGE, TXT_STAT_AVRTRIM, TXT_STAT_AVRBOOST, TXT_STAT_OVERHEAT,
			TXT_STAT_OVERLOAD_TIME, TXT_STAT_PWROK_TIME, TXT_STAT_PWRFAIL_TIME, TXT_STAT_SYNCLOST
		};

		if (commSinlineXL.isInitialized()) {
			int iSize = UPS_STATISTICS_COUNT;
			int iRet = SinlineXL_DoGetUPSStructures(EU_STATS, uiStats, &iSize);
			if (iRet != IOCTL_ERROR_SUCCESS)
				return iRet;
		}
		for (int i = 0; i < UPS_STATISTICS_COUNT; i++) {
			strcpy(pStat[i].statName, tabStatNames[i]);
			pStat[i].value = uiStats[i];
		}
	}
	if (*lpiBuffSize == (sizeof(STATISTIC)*UPS_FACTORYCONTROL_COUNT)) {
		unsigned int uiStats[UPS_FACTORYCONTROL_COUNT];
		char tabStatNames[UPS_FACTORYCONTROL_COUNT][STAT_NAME_MAX] = {
			TXT_FCTL_CHARGE_H, TXT_FCTL_CHARGE_L, TXT_FCTL_PWRFAIL_TIME_MIN,
			TXT_FCTL_OVERLOAD_TIME_SEC, TXT_FCTL_PWROK_TIME_MIN
		};

		if (commSinlineXL.isInitialized()) {
			int iSize = UPS_FACTORYCONTROL_COUNT;
			int iRet = SinlineXL_DoGetUPSStructures(EU_FACTORYCTL, uiStats, &iSize);
			if (iRet != IOCTL_ERROR_SUCCESS)
				return iRet;
		}
		for (int i = 0; i < UPS_FACTORYCONTROL_COUNT; i++) {
			strcpy(pStat[i].statName, tabStatNames[i]);
			pStat[i].value = uiStats[i];
		}
	}
	if (*lpiBuffSize == (sizeof(STATISTIC)*(UPS_STATISTICS_COUNT+UPS_FACTORYCONTROL_COUNT))) {
		unsigned int uiStats1[UPS_STATISTICS_COUNT], uiStats2[UPS_FACTORYCONTROL_COUNT];
		char tabStatNames1[UPS_STATISTICS_COUNT][STAT_NAME_MAX] = {
			TXT_STAT_PWRFAILURE, TXT_STAT_PWRDROP, TXT_STAT_PWRINCREASE, TXT_STAT_OVERLOAD,
			TXT_STAT_SHORT, TXT_STAT_DISCHARGE, TXT_STAT_AVRTRIM, TXT_STAT_AVRBOOST, TXT_STAT_OVERHEAT,
			TXT_STAT_OVERLOAD_TIME, TXT_STAT_PWROK_TIME, TXT_STAT_PWRFAIL_TIME, TXT_STAT_SYNCLOST
		}, tabStatNames2[UPS_FACTORYCONTROL_COUNT][STAT_NAME_MAX] = {
			TXT_FCTL_CHARGE_H, TXT_FCTL_CHARGE_L, TXT_FCTL_PWRFAIL_TIME_MIN,
			TXT_FCTL_OVERLOAD_TIME_SEC, TXT_FCTL_PWROK_TIME_MIN
		};
		if (commSinlineXL.isInitialized()) {
			int iSize = UPS_STATISTICS_COUNT;
			int iRet = SinlineXL_DoGetUPSStructures(EU_STATS, uiStats1, &iSize);
			if (iRet != IOCTL_ERROR_SUCCESS)
				return iRet;
			iSize = UPS_FACTORYCONTROL_COUNT;
			iRet = SinlineXL_DoGetUPSStructures(EU_FACTORYCTL, uiStats2, &iSize);
			if (iRet != IOCTL_ERROR_SUCCESS)
				return iRet;
		}
		for (int i = 0; i < UPS_STATISTICS_COUNT; i++) {
			strcpy(pStat[i].statName, tabStatNames1[i]);
			pStat[i].value = uiStats1[i];
		}
		for (int i = UPS_STATISTICS_COUNT; i < (UPS_STATISTICS_COUNT+UPS_FACTORYCONTROL_COUNT); i++) {
			strcpy(pStat[i].statName, tabStatNames2[i-UPS_STATISTICS_COUNT]);
			pStat[i].value = uiStats2[i-UPS_STATISTICS_COUNT];
		}
	}

	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoReadConfig
// Description		: 
int	SinlineXL_DoReadConfig()
{
	char *fp;
	fp = get_config_filepath(DRIVER_CONFIG_FILE);
	if (fp==NULL) {
		return IOCTL_ERROR;
	}
	CConf *pCfgDev = new CConf;
	if (pCfgDev->init(fp)!=CONF_SUCCESS) {
		free(fp);
		delete pCfgDev;
		return IOCTL_ERROR_CONFIG_READFAIL;
	} else {
		free(fp);
		pCfgDev->parse_config();
	}
	
	char szBuf[128] = "", *s;
	
	// serial port number (/dev/ttyS0 is used by default)
	s = pCfgDev->getcfgitemvalue("commport");
	strcpy(sdcSinlineXL.szSerialPort, (!strlen(s)) ? "/dev/ttyS0" : s);
	// buzzer mode
	s = pCfgDev->getcfgitemvalue("audible_alarm");
	sdcSinlineXL.bAudibleAlarm = (bool)atoi((!strlen(s)) ? "1" : s);
	// transfer tresholds (upper, lower, avr trimmer)
	s = pCfgDev->getcfgitemvalue("transfer_treshold_upper");
	sdcSinlineXL.uiTransferTrsh_Upper = (unsigned int)strtoul((!strlen(s)) ? "265" : s, NULL, 10);
	s = pCfgDev->getcfgitemvalue("transfer_treshold_lower");
	sdcSinlineXL.uiTransferTrsh_Lower = (unsigned int)strtoul((!strlen(s)) ? "160" : s, NULL, 10);
	s = pCfgDev->getcfgitemvalue("transfer_treshold_avr_booster");
	sdcSinlineXL.uiTransferTrsh_AVRBoost = (unsigned int)strtoul((!strlen(s)) ? "200" : s, NULL, 10);
	// powerfail to standby transfer delay in seconds
	// 90 seconds by default should be enough for most systems to shutdown gracefully
	s = pCfgDev->getcfgitemvalue("powerfail_to_standby_timeout");
	sdcSinlineXL.uiStandbyDelay = (unsigned int)strtoul((!strlen(s)) ? "90" : s, NULL, 10);
	
	// battery extension modules
	s = pCfgDev->getcfgitemvalue("extension_module_number");
	sdcSinlineXL.uiModuleNumber = (unsigned int)strtoul((!strlen(s)) ? "0" : s, NULL, 10);

	delete pCfgDev;
	
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineXL_DoGetConfigFileName
// Description		: 
int SinlineXL_DoGetConfigFileName( void *lpvBuff, int *lpiBuffSize )
{
	if (lpvBuff != NULL) {
		if ( *lpiBuffSize >= strlen(DRIVER_CONFIG_FILE) )
			strcpy((char *)lpvBuff, DRIVER_CONFIG_FILE );
		else
			return IOCTL_ERROR_INVALIDARGUMENT;
	} else
		return IOCTL_ERROR_INVALIDARGUMENT;
	return IOCTL_ERROR_SUCCESS;
}
