/***************************************************************************
 *   Property rights (C) 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 "ftccomm.h"
#include "conf.h"
#include "common.h"

#define SINLINE_USB_DRIVER_BUILD 1
#include "sinline_usb.h"

/* 
 * Global variable declarations
 */
sdrv_config sdcSinlineUSB;
bool bSinlineUSB_BadFirmwarePass = false;
int iSinlineUSB_FirmwareRev = 0;
int iSinlineUSB_UpsSeries = 0;
int iSinlineUSB_ExtError = 0;
FTCComm	commSinlineUSB;

int sinlineusb_ioctl(long lCommand, void *lpvBuff, int *lpiBuffSize)
{
	switch(lCommand)
	{
	case IOCTL_INIT: 
		return SinlineUSB_DoInit(lpvBuff, lpiBuffSize);

	case IOCTL_UNINIT:
		return SinlineUSB_DoUnInit(lpvBuff, lpiBuffSize);
		
	case IOCTL_GETCONFIGFILENAME:
		return SinlineUSB_DoGetConfigFileName(lpvBuff, lpiBuffSize);

	case IOCTL_AUTOCONFIGURE: 
		return SinlineUSB_DoAutoConfigure(lpvBuff, lpiBuffSize);

	case IOCTL_CONFIGURE: 
		return SinlineUSB_DoConfigure(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSINFOMASK: 
		return SinlineUSB_DoGetUpsInfoMask(lpvBuff, lpiBuffSize);

	case IOCTL_SET_UPSINFOMASK:
		return SinlineUSB_DoSetUpsInfoMask(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSSTATESMASK:
		return SinlineUSB_DoGetUpsStateMask(lpvBuff, lpiBuffSize);

	case IOCTL_SET_UPSSTATESMASK:
		return SinlineUSB_DoSetUpsStateMask(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSSTATE:
		return SinlineUSB_DoGetUpsState(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSPARAMSMASK:
		return SinlineUSB_DoGetUpsParamsMask(lpvBuff, lpiBuffSize);

	case IOCTL_SET_UPSPARAMSMASK:
		return SinlineUSB_DoSetUpsParamsMask(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSPARAMS:
		return SinlineUSB_DoGetUpsParams(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSSETUPPARAMSMASK:
		return SinlineUSB_DoGetUpsSetupParamsMask(lpvBuff, lpiBuffSize);

	case IOCTL_SET_UPSSETUPPARAMSMASK:
		return SinlineUSB_DoSetUpsSetupParamsMask(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSSETUPPARAMS:
		return SinlineUSB_DoGetUpsSetupParams(lpvBuff, lpiBuffSize);

	case IOCTL_SET_UPSSETUPPARAMS:
		return SinlineUSB_DoSetUpsSetupParams(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSCHARACTERISTICMASK:
		return SinlineUSB_DoGetUpsCharacteristicMask(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSCHARACTERISTIC:
		return SinlineUSB_DoGetUpsCharacteristic(lpvBuff, lpiBuffSize);

	case IOCTL_GET_DRIVER_INFO:
		return SinlineUSB_DoGetDriverInfo(lpvBuff, lpiBuffSize);

	case IOCTL_GET_TABLE_INFORMATIONS:
		return SinlineUSB_DoGetUPSStructures(EU_INFORMATIONS, lpvBuff, lpiBuffSize);

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

	case IOCTL_GET_ERRORNO:
		return SinlineUSB_DoGetExtendedError(lpvBuff, lpiBuffSize);

	case IOCTL_TESTUPSLINK:
		return SinlineUSB_DoTestUpsLink(lpvBuff, lpiBuffSize);

	case IOCTL_GETCONFIGPARAMSCOUNT:
		return SinlineUSB_DoGetConfigParamsCount(lpvBuff, lpiBuffSize);

	case IOCTL_GETCONFIGPARAMS:
		return SinlineUSB_DoGetConfigParams(lpvBuff, lpiBuffSize);

	case IOCTL_GETCONFIGPARAM:
		return SinlineUSB_DoGetConfigParam(lpvBuff, lpiBuffSize);

	case IOCTL_SETCONFIGPARAMS:
		return SinlineUSB_DoSetConfigParams(lpvBuff, lpiBuffSize);

	case IOCTL_SETCONFIGPARAM:
		return SinlineUSB_DoSetConfigParam(lpvBuff, lpiBuffSize);

	case IOCTL_UPDATECONFIG:
		return SinlineUSB_DoUpdateConfig(lpvBuff, lpiBuffSize);

	case IOCTL_GETDRIVERMODE:
		return SinlineUSB_DoGetDriverMode(lpvBuff, lpiBuffSize);

	case IOCTL_SHUTDOWNUPS:
		return SinlineUSB_DoUpsShutdown(lpvBuff, lpiBuffSize);

	default:
		return IOCTL_ERROR_CMD_NOTSUPPORTED;
	}
}

// Function name	: SinlineUSB_DoInit
// Description		: Inicjalizacja drivera
int SinlineUSB_DoInit( void *lpvBuff, int *lpiBuffSize )
{
	iSinlineUSB_ExtError = 0;

	iSinlineUSB_UpsSeries=0;
	iSinlineUSB_FirmwareRev=0;
	bSinlineUSB_BadFirmwarePass=false;

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

	commSinlineUSB.init(sdcSinlineUSB.szSerialNumber, 19200);
	if (commSinlineUSB.open()!=COMM_SUCCESS) {
		iSinlineUSB_ExtError=IOCTL_ERROR_COMM_INITFAIL;
		return IOCTL_ERROR;
	}
	
	iSinlineUSB_ExtError = 0;
	if (SinlineUSB_DoUpdateConfig(NULL, NULL) != IOCTL_ERROR_SUCCESS)
		return IOCTL_ERROR;

	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineUSB_DoUnInit
// Description		: Deinicjalizacja drivera
int SinlineUSB_DoUnInit( void *lpvBuff, int *lpiBuffSize )
{
	if (commSinlineUSB.close()!=COMM_SUCCESS)
	{
		iSinlineUSB_ExtError=0;
		return IOCTL_ERROR;
	}
	
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineUSB_DoUnInit
// Description		:
int SinlineUSB_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;
}

// Function name	: SinlineUSB_DoAutoConfigure
// Description		: Self configure
int SinlineUSB_DoAutoConfigure( void *lpvBuff, int *lpiBuffSize )
{
	sdcSinlineUSB.uiStandbyTimeout = 85;
	sdcSinlineUSB.bAudibleAlarm = true;
	sdcSinlineUSB.iSetupTable[UPS_STANDBYDELAY]=sdcSinlineUSB.uiStandbyTimeout;
	sdcSinlineUSB.iSetupTable[UPS_WORKCONTROL]=KP_BUZZER;
	strcpy(sdcSinlineUSB.szSerialNumber, "SINLINE0");

	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineUSB_DoConfigure
// Description		: Configure driver to work properly with UPS
int SinlineUSB_DoConfigure( void *lpvBuff, int *lpiBuffSize )
{
	sdcSinlineUSB.uiStandbyTimeout = 85;
	sdcSinlineUSB.bAudibleAlarm = true;
	sdcSinlineUSB.iSetupTable[UPS_STANDBYDELAY]=sdcSinlineUSB.uiStandbyTimeout;
	sdcSinlineUSB.iSetupTable[UPS_WORKCONTROL]=KP_BUZZER;
	strcpy(sdcSinlineUSB.szSerialNumber, "SINLINE0");
	
	SinlineUSB_DoUnInit(NULL, NULL);
	SinlineUSB_DoInit(NULL, NULL);

	return IOCTL_ERROR_SUCCESS;
}

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

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

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

// Function name	: SinlineUSB_DoGetUpsStateMask
// Description		:
int	SinlineUSB_DoGetUpsStateMask(void *lpvBuff, int *lpiBuffSize)
{
	unsigned long ulMask = US_POWERON | US_POWERFAIL | US_STANDBY | 
		US_OVERLOAD | US_AVRUP | US_SHORT | US_BATTERYLOW;

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

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

// Function name	: SinlineUSB_DoGetUpsState
// Description		: Return actual UPS state
int	SinlineUSB_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 = new unsigned int[UPS_INF_COUNT];
	int				iSize = UPS_INF_COUNT;
	unsigned long	ulUpsState = 0;
	int				iRet;
	double			dBattVlt, dLoadOut, dLowBattery;

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

	iRet = SinlineUSB_DoGetUPSStructures(EU_INFORMATIONS, lpuiInfoTable, &iSize);
	if (iRet != IOCTL_ERROR_SUCCESS)
	{
		delete [] lpuiInfoTable;
		return iRet;
	}

	if (iSinlineUSB_UpsSeries==0)
		iSinlineUSB_UpsSeries=lpuiInfoTable[ UPS_SERIES ];
	if (iSinlineUSB_FirmwareRev==0)
		iSinlineUSB_FirmwareRev=lpuiInfoTable[ UPS_FIRMWARE_VER ];

	switch(iSinlineUSB_UpsSeries) 
	{
	case UPS_SINLINE_800:
		dBattVlt = (BATT_VLT_FORMULA_S08_11(lpuiInfoTable[VOLTAGE_BATTERY]));
		break;
	case UPS_SINLINE_1200:
		dBattVlt = (BATT_VLT_FORMULA_S12_11(lpuiInfoTable[VOLTAGE_BATTERY]));
		break;
	case UPS_SINLINE_1600:
		if (iSinlineUSB_FirmwareRev==FIRMWARE_10)
			dBattVlt = (BATT_VLT_FORMULA_S16_10(lpuiInfoTable[VOLTAGE_BATTERY]));
		else if (iSinlineUSB_FirmwareRev==FIRMWARE_11)
			dBattVlt = (BATT_VLT_FORMULA_S16_11(lpuiInfoTable[VOLTAGE_BATTERY]));
		break;
	case UPS_SINLINE_2000:
		dBattVlt = (BATT_VLT_FORMULA_S20_11(lpuiInfoTable[VOLTAGE_BATTERY]));
		break;
	case UPS_SINLINE_2500:
		dBattVlt = (BATT_VLT_FORMULA_S25_11(lpuiInfoTable[VOLTAGE_BATTERY]));
		break;
	case UPS_SINLINE_3000:
		dBattVlt = (BATT_VLT_FORMULA_S30_11(lpuiInfoTable[VOLTAGE_BATTERY]));
		break;
	default:
		dBattVlt = 0;
		break;
	}

	// moc czynna
	if (lpuiInfoTable[ POWER_OUTPUT ]>1)
		switch(iSinlineUSB_UpsSeries) 
		{
		case UPS_SINLINE_800:
			dLoadOut = (OUT_PWR_FORMULA_S08_11(lpuiInfoTable[POWER_OUTPUT]));
			break;
		case UPS_SINLINE_1200:
			dLoadOut = (OUT_PWR_FORMULA_S12_11(lpuiInfoTable[POWER_OUTPUT]));
			break;
		case UPS_SINLINE_1600:
			if (iSinlineUSB_FirmwareRev==FIRMWARE_10)
				dLoadOut = (OUT_PWR_FORMULA_S16_10(lpuiInfoTable[POWER_OUTPUT]));
			else if (iSinlineUSB_FirmwareRev==FIRMWARE_11)
				dLoadOut = (OUT_PWR_FORMULA_S16_11(lpuiInfoTable[POWER_OUTPUT]));
			break;
		case UPS_SINLINE_2000:
			dLoadOut = (OUT_PWR_FORMULA_S20_11(lpuiInfoTable[POWER_OUTPUT]));
			break;
		case UPS_SINLINE_2500:
			dLoadOut = (OUT_PWR_FORMULA_S25_11(lpuiInfoTable[POWER_OUTPUT]));
			break;
		case UPS_SINLINE_3000:
			dLoadOut = (OUT_PWR_FORMULA_S30_11(lpuiInfoTable[POWER_OUTPUT]));
			break;
		default:
			dLoadOut = 0;
			break;
		}
	else
		dLoadOut = (0);

	CONVERT_BIT( lpuiInfoTable[ UPS_OPERATINGMODE ], 0x0001, ulUpsState, 0x0 );
	CONVERT_BIT( lpuiInfoTable[ UPS_OPERATINGMODE ], 0x0002, ulUpsState, 0x0 );
	CONVERT_BIT( lpuiInfoTable[ UPS_OPERATINGMODE ], 0x0004, ulUpsState, US_AVRUP );
	CONVERT_BIT( lpuiInfoTable[ UPS_OPERATINGMODE ], 0x0008, ulUpsState, US_POWERON );
	CONVERT_BIT( lpuiInfoTable[ UPS_OPERATINGMODE ], 0x0010, ulUpsState, US_POWERFAIL );
	CONVERT_BIT( lpuiInfoTable[ UPS_OPERATINGMODE ], 0x0020, ulUpsState, US_OVERLOAD );
	CONVERT_BIT( lpuiInfoTable[ UPS_OPERATINGMODE ], 0x0040, ulUpsState, US_SHORT );
	CONVERT_BIT( lpuiInfoTable[ UPS_OPERATINGMODE ], 0x0080, ulUpsState, US_STANDBY );

	dLowBattery = SinlineUSB_GetLowBatteryLevel(dLoadOut, dBattVlt);
	if (flag_check(ulUpsState, US_POWERFAIL) && (dBattVlt<=dLowBattery)) {
		ulUpsState|=US_BATTERYLOW;
	} else {
		ulUpsState &= ~(US_BATTERYLOW);
	}
	
	memcpy( lpvBuff, &ulUpsState, *lpiBuffSize );

	delete [] lpuiInfoTable;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineUSB_DoGetUpsParamsMask
// Description		:
int	SinlineUSB_DoGetUpsParamsMask(void *lpvBuff, int *lpiBuffSize)
{
	__int64 iMask = UP_VLT_INPUT | UP_VLT_OUTPUT | UP_VLT_BATTERY | UP_FRQ_INPUT | UP_PRC_POWERWEIGHT | 
		UP_UID_VERSION | UP_UID_MODEL | UP_BATTERYSTATE | UP_PWR_EFFECTIVE | UP_CUR_OUTPUT;

	if ( *lpiBuffSize == sizeof( __int64 ) )
		memcpy( lpvBuff, &iMask, sizeof( __int64 ) );
	else
		return IOCTL_ERROR_INVALIDARGUMENT;
	return IOCTL_ERROR_SUCCESS;
}

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

// Function name	: SinlineUSB_DoGetUpsParams
// Description		:
int	SinlineUSB_DoGetUpsParams(void *lpvBuff, int *lpiBuffSize)
{
	unsigned int *lpuiInfoTable = new unsigned int[UPS_INF_COUNT];
	int	iSize, iRet, i, iv[2], iv2[2];
	supsp sup;
	char szUpsName[UPS_MAX_MODELS][64] = { 
		UPS_MODEL_800, UPS_MODEL_1200, UPS_MODEL_1600, 
		UPS_MODEL_2000, UPS_MODEL_2500, UPS_MODEL_3000
	};

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

	iSize = UPS_INF_COUNT;
	iRet = SinlineUSB_DoGetUPSStructures(EU_INFORMATIONS, lpuiInfoTable, &iSize);
	if (iRet != IOCTL_ERROR_SUCCESS) {
		delete [] lpuiInfoTable;
		return iRet;
	}
	
	sup.iMask = UP_VLT_INPUT | UP_VLT_OUTPUT | UP_VLT_BATTERY | UP_FRQ_INPUT | UP_PRC_POWERWEIGHT | 
		UP_UID_VERSION | UP_UID_MODEL | UP_BATTERYSTATE | UP_PWR_EFFECTIVE | UP_CUR_OUTPUT;
	
	i = iSinlineUSB_UpsSeries = lpuiInfoTable[ UPS_SERIES ];
	sprintf( sup.sz__uid_model, "%s %s", DRIVER_UPS_PREFIX, szUpsName[ i-1 ] );
	sprintf( sup.sz__uid_family, "%s", DRIVER_UPS_FAMILY );

	iSinlineUSB_FirmwareRev = lpuiInfoTable[ UPS_FIRMWARE_VER ];

	if ((bSinlineUSB_BadFirmwarePass==false) &&
		(iSinlineUSB_FirmwareRev>FIRMWARE_LAST || iSinlineUSB_FirmwareRev<FIRMWARE_FIRST))
	{
		bSinlineUSB_BadFirmwarePass=true;
		iv[0] = ( FIRMWARE_LAST & 0xFF ) >> 4;
		iv[1] = ( FIRMWARE_LAST & 0x0F );
		iv2[0] = ( lpuiInfoTable[ UPS_FIRMWARE_VER ] & 0xFF ) >> 4;
		iv2[1] = ( lpuiInfoTable[ UPS_FIRMWARE_VER ] & 0x0F );
		// unsupported version of UPS firmware
		error_report(TXT_ERR_FIRMWARE_UNSUPPORTED, 
			sup.sz__uid_model, iv2[0], iv2[1], iv[0], iv[1]);

		delete [] lpuiInfoTable;
		return IOCTL_CRITICAL_ERROR;
	}

	iv[0] = ( lpuiInfoTable[ UPS_FIRMWARE_VER ] & 0xFF ) >> 4;
	iv[1] = ( lpuiInfoTable[ UPS_FIRMWARE_VER ] & 0x0F );
	sprintf( sup.sz__uid_version, "Firmware: %d.%d", iv[0], iv[1] );

	if (lpuiInfoTable[ FREQ_INPUT ]!=0)
		sup.ui__frq_input = (unsigned int)(1152000/lpuiInfoTable[ FREQ_INPUT ]);
	else
		sup.ui__frq_input = 0;
	sup.ui__frq_input_div = 10;

	//sup.ui__prc_batterycapacity = lpuiInfoTable[ POJ_AKU ];
	//sup.ui__prc_batterycapacity_div = 1;
	
	/* battery voltage */
	switch(iSinlineUSB_UpsSeries)  {
	case UPS_SINLINE_800:
		sup.ui__vlt_battery = (unsigned int)(BATT_VLT_FORMULA_S08_11(lpuiInfoTable[VOLTAGE_BATTERY])*100.0);
		break;
	case UPS_SINLINE_1200:
		sup.ui__vlt_battery = (unsigned int)(BATT_VLT_FORMULA_S12_11(lpuiInfoTable[VOLTAGE_BATTERY])*100.0);
		break;
	case UPS_SINLINE_1600:
		if (iSinlineUSB_FirmwareRev==FIRMWARE_10)
			sup.ui__vlt_battery = (unsigned int)(BATT_VLT_FORMULA_S16_10(lpuiInfoTable[VOLTAGE_BATTERY])*100.0);
		else if (iSinlineUSB_FirmwareRev==FIRMWARE_11)
			sup.ui__vlt_battery = (unsigned int)(BATT_VLT_FORMULA_S16_11(lpuiInfoTable[VOLTAGE_BATTERY])*100.0);
		break;
	case UPS_SINLINE_2000:
		sup.ui__vlt_battery = (unsigned int)(BATT_VLT_FORMULA_S20_11(lpuiInfoTable[VOLTAGE_BATTERY])*100.0);
		break;
	case UPS_SINLINE_2500:
		sup.ui__vlt_battery = (unsigned int)(BATT_VLT_FORMULA_S25_11(lpuiInfoTable[VOLTAGE_BATTERY])*100.0);
		break;
	case UPS_SINLINE_3000:
		sup.ui__vlt_battery = (unsigned int)(BATT_VLT_FORMULA_S30_11(lpuiInfoTable[VOLTAGE_BATTERY])*100.0);
		break;
	default:
		sup.ui__vlt_battery = 0;
		break;
	}
	sup.ui__vlt_battery_div = 100;
	
	/* input voltage */
	if (lpuiInfoTable[ VOLTAGE_INPUT ] != 0) {
		switch(iSinlineUSB_UpsSeries) {
		case UPS_SINLINE_800:
			sup.ui__vlt_input = (unsigned int)INP_VLT_FORMULA_S08_11(lpuiInfoTable[VOLTAGE_INPUT]);
			break;
		case UPS_SINLINE_1200:
			sup.ui__vlt_input = (unsigned int)INP_VLT_FORMULA_S12_11(lpuiInfoTable[VOLTAGE_INPUT]);
			break;
		case UPS_SINLINE_1600:
			if (iSinlineUSB_FirmwareRev==FIRMWARE_10)
				sup.ui__vlt_input = (unsigned int)INP_VLT_FORMULA_S16_10(lpuiInfoTable[VOLTAGE_INPUT]);
			else if (iSinlineUSB_FirmwareRev==FIRMWARE_11)
				sup.ui__vlt_input = (unsigned int)INP_VLT_FORMULA_S16_11(lpuiInfoTable[VOLTAGE_INPUT]);
			break;
		case UPS_SINLINE_2000:
			sup.ui__vlt_input = (unsigned int)INP_VLT_FORMULA_S20_11(lpuiInfoTable[VOLTAGE_INPUT]);
			break;
		case UPS_SINLINE_2500:
			sup.ui__vlt_input = (unsigned int)INP_VLT_FORMULA_S25_11(lpuiInfoTable[VOLTAGE_INPUT]);
			break;
		case UPS_SINLINE_3000:
			sup.ui__vlt_input = (unsigned int)INP_VLT_FORMULA_S30_11(lpuiInfoTable[VOLTAGE_INPUT]);
			break;
		default:
			sup.ui__vlt_input = 0;
			break;
		}
	}
	else
		sup.ui__vlt_input = 0;
	sup.ui__vlt_input_div = 1;

	if (lpuiInfoTable[ VOLTAGE_OUTPUT ] != 0) {
		switch(iSinlineUSB_UpsSeries) {
		case UPS_SINLINE_800:
			sup.ui__vlt_output = (unsigned int)OUT_VLT_FORMULA_S08_11(lpuiInfoTable[VOLTAGE_OUTPUT]);
			break;
		case UPS_SINLINE_1200:
			sup.ui__vlt_output = (unsigned int)OUT_VLT_FORMULA_S12_11(lpuiInfoTable[VOLTAGE_OUTPUT]);
			break;
		case UPS_SINLINE_1600:
			if (iSinlineUSB_FirmwareRev==FIRMWARE_10)
				sup.ui__vlt_output = (unsigned int)OUT_VLT_FORMULA_S16_10(lpuiInfoTable[VOLTAGE_OUTPUT]);
			else if (iSinlineUSB_FirmwareRev==FIRMWARE_11)
				sup.ui__vlt_output = (unsigned int)OUT_VLT_FORMULA_S16_11(lpuiInfoTable[VOLTAGE_OUTPUT]);
			break;
		case UPS_SINLINE_2000:
			sup.ui__vlt_output = (unsigned int)OUT_VLT_FORMULA_S20_11(lpuiInfoTable[VOLTAGE_OUTPUT]);
			break;
		case UPS_SINLINE_2500:
			sup.ui__vlt_output = (unsigned int)OUT_VLT_FORMULA_S25_11(lpuiInfoTable[VOLTAGE_OUTPUT]);
			break;
		case UPS_SINLINE_3000:
			sup.ui__vlt_output = (unsigned int)OUT_VLT_FORMULA_S30_11(lpuiInfoTable[VOLTAGE_OUTPUT]);
			break;
		default:
			sup.ui__vlt_output = 0;
			break;
		}
	}
	else
		sup.ui__vlt_output = 0;
	sup.ui__vlt_output_div = 1;

	// prd wyjciowy
	if (lpuiInfoTable[CURRENT_OUTPUT] > 0) {
		switch(iSinlineUSB_UpsSeries) {
		case UPS_SINLINE_800:
			sup.ui__cur_output = (unsigned int)(OUT_CUR_FORMULA_S08_11(lpuiInfoTable[CURRENT_OUTPUT])*100.0);
			break;
		case UPS_SINLINE_1200:
			sup.ui__cur_output = (unsigned int)(OUT_CUR_FORMULA_S12_11(lpuiInfoTable[CURRENT_OUTPUT])*100.0);
			break;
		case UPS_SINLINE_1600:
			if (iSinlineUSB_FirmwareRev==FIRMWARE_10)
				sup.ui__cur_output = (unsigned int)(OUT_CUR_FORMULA_S16_10(lpuiInfoTable[CURRENT_OUTPUT])*100.0);
			else if (iSinlineUSB_FirmwareRev==FIRMWARE_11)
				sup.ui__cur_output = (unsigned int)(OUT_CUR_FORMULA_S16_11(lpuiInfoTable[CURRENT_OUTPUT])*100.0);
			break;
		case UPS_SINLINE_2000:
			sup.ui__cur_output = (unsigned int)(OUT_CUR_FORMULA_S20_11(lpuiInfoTable[CURRENT_OUTPUT])*100.0);
			break;
		case UPS_SINLINE_2500:
			sup.ui__cur_output = (unsigned int)(OUT_CUR_FORMULA_S25_11(lpuiInfoTable[CURRENT_OUTPUT])*100.0);
			break;
		case UPS_SINLINE_3000:
			sup.ui__cur_output = (unsigned int)(OUT_CUR_FORMULA_S30_11(lpuiInfoTable[CURRENT_OUTPUT])*100.0);
			break;
		default:
			sup.ui__cur_output = 0;
			break;
		}
	} else {
		sup.ui__cur_output = (unsigned int)0;
	}
	sup.ui__cur_output_div = 100;
	
	// moc pozorna
	sup.ui__pwr_apparent = (unsigned int)(
		((sup.ui__vlt_output * sup.ui__cur_output)+0.001) / sup.ui__cur_output_div);
	sup.ui__pwr_apparent_div = 1;

	// moc czynna
	if (lpuiInfoTable[ POWER_OUTPUT ]>1) {
		switch(iSinlineUSB_UpsSeries) {
		case UPS_SINLINE_800:
			sup.ui__pwr_effective = (unsigned int)(OUT_PWR_FORMULA_S08_11(lpuiInfoTable[POWER_OUTPUT]));
			break;
		case UPS_SINLINE_1200:
			sup.ui__pwr_effective = (unsigned int)(OUT_PWR_FORMULA_S12_11(lpuiInfoTable[POWER_OUTPUT]));
			break;
		case UPS_SINLINE_1600:
			if (iSinlineUSB_FirmwareRev==FIRMWARE_10)
				sup.ui__pwr_effective = (unsigned int)(OUT_PWR_FORMULA_S16_10(lpuiInfoTable[POWER_OUTPUT]));
			else if (iSinlineUSB_FirmwareRev==FIRMWARE_11)
				sup.ui__pwr_effective = (unsigned int)(OUT_PWR_FORMULA_S16_11(lpuiInfoTable[POWER_OUTPUT]));
			break;
		case UPS_SINLINE_2000:
			sup.ui__pwr_effective = (unsigned int)(OUT_PWR_FORMULA_S20_11(lpuiInfoTable[POWER_OUTPUT]));
			break;
		case UPS_SINLINE_2500:
			sup.ui__pwr_effective = (unsigned int)(OUT_PWR_FORMULA_S25_11(lpuiInfoTable[POWER_OUTPUT]));
			break;
		case UPS_SINLINE_3000:
			sup.ui__pwr_effective = (unsigned int)(OUT_PWR_FORMULA_S30_11(lpuiInfoTable[POWER_OUTPUT]));
			break;
		default:
			sup.ui__pwr_effective = 0;
			break;
		}
	} else {
		sup.ui__pwr_effective = (unsigned int)(0);
	}
	sup.ui__pwr_effective_div = 1;

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

	sup.ui__prc_powerweight = 0;
	if (sup.ui__pwr_effective > 0) {
		/* moc w procentach */
		switch(iSinlineUSB_UpsSeries) {
		case UPS_SINLINE_800:
			sup.ui__prc_powerweight = (unsigned int)(sup.ui__pwr_effective/(800 * 65));
			break;
		case UPS_SINLINE_1200:
			sup.ui__prc_powerweight = (unsigned int)(sup.ui__pwr_effective/(1200 * 65));
			break;
		case UPS_SINLINE_1600:
			sup.ui__prc_powerweight = (unsigned int)(sup.ui__pwr_effective/(1600 * 65));
			break;
		case UPS_SINLINE_2000:
			sup.ui__prc_powerweight = (unsigned int)(sup.ui__pwr_effective/(2000 * 65));
			break;
		case UPS_SINLINE_2500:
			sup.ui__prc_powerweight = (unsigned int)(sup.ui__pwr_effective/(2500 * 65));
			break;
		case UPS_SINLINE_3000:
			sup.ui__prc_powerweight = (unsigned int)(sup.ui__pwr_effective/(3000 * 65));
			break;
		default:
			sup.ui__prc_powerweight = 0;
			break;
		}
	}
	sup.ui__prc_powerweight_div = 1;
	if (sup.ui__prc_powerweight<0) sup.ui__prc_powerweight=0;
	if (sup.ui__prc_powerweight>100) sup.ui__prc_powerweight=100;
	
	// power coefficient
	sup.ui__pwr_coefficient = (unsigned int)(
		((sup.ui__pwr_effective+0.001)/(sup.ui__pwr_apparent+0.001))*100.0);
	sup.ui__pwr_coefficient_div = 100;


	if (flag_check((unsigned long)lpuiInfoTable[UPS_OPERATINGMODE],TP_PRACA_SIECIOWA)) {
		if (flag_check((unsigned long)lpuiInfoTable[UPS_OPERATINGMODE],TP_AKU_FULL))
			sup.ui__batterystate = ebattery_state(bs_full);
		else
			sup.ui__batterystate = ebattery_state(bs_charging);
	} else {
		if (flag_check((unsigned long)lpuiInfoTable[UPS_OPERATINGMODE],TP_PRACA_BATERYJNA))
			sup.ui__batterystate = ebattery_state(bs_discharging);
		else
			sup.ui__batterystate = ebattery_state(bs_discharged);
	}

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

	delete [] lpuiInfoTable;

	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineUSB_DoGetUpsSetupParamsMask
// Description		: 
int	SinlineUSB_DoGetUpsSetupParamsMask(void *lpvBuff, int *lpiBuffSize)
{
	unsigned long ulMask = UP_SET_AAL_ON | UP_SET_TMR_POWERFAILTOSTANDBY;

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

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

// Function name	: SinlineUSB_DoGetUpsSetupParams
// Description		: 
int	SinlineUSB_DoGetUpsSetupParams(void *lpvBuff, int *lpiBuffSize)
{
	unsigned int *lpuiSetupTable = new unsigned int[UPS_SETUP_COUNT];
	supssp susp;

	susp.ulMask = UP_SET_AAL_ON | UP_SET_TMR_POWERFAILTOSTANDBY;
	
	susp.ui__set_aal_on = sdcSinlineUSB.bAudibleAlarm?1:0;
	susp.ui__set_tmr_powerfailtostandby = sdcSinlineUSB.uiStandbyTimeout;
	susp.ui__set_tmr_powerfailtostandby_div = 1;

	memcpy( lpvBuff, &susp, *lpiBuffSize );

	delete [] lpuiSetupTable;
	return IOCTL_ERROR_SUCCESS;
}

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

// Function name	: SinlineUSB_DoGetDriverInfo
// Description		: 
int	SinlineUSB_DoGetDriverInfo(void *lpvBuff, int *lpiBuffSize)
{
	if (*lpiBuffSize != sizeof(sdrv_info))
		return IOCTL_ERROR_INVALIDARGUMENT;
	sprintf( (( lpsdrv_info )lpvBuff )->szName, "%s DRIVER", DRIVER_UPS_PREFIX );
	sprintf( (( lpsdrv_info )lpvBuff )->szFamily, "%s", DRIVER_UPS_FAMILY );
	((lpsdrv_info)lpvBuff)->eLink = ul_usb;
	((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	: SinlineUSB_DoGetExtendedError
// Description		: Get extended error information - return value of last error
int	SinlineUSB_DoGetExtendedError(void *lpvBuff, int *lpiBuffSize)
{
	return iSinlineUSB_ExtError;
}

// Function name	: SinlineUSB_DoGetUpsCharacteristicMask
// Description		: 
int	SinlineUSB_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	: SinlineUSB_DoGetUpsCharacteristic
// Description		: 
int	SinlineUSB_DoGetUpsCharacteristic(void *lpvBuff, int *lpiBuffSize)
{
	return IOCTL_ERROR_CMD_NOTSUPPORTED;
}

// Function name	: SinlineUSB_DoTestUpsLink
// Description		: 
int	SinlineUSB_DoTestUpsLink(void *lpvBuff, int *lpiBuffSize)
{
	unsigned int *lpuiInfoTable = new unsigned int[UPS_INF_COUNT];
	int iSize=UPS_INF_COUNT, iRet;
	
	iRet = SinlineUSB_DoGetUPSStructures(EU_INFORMATIONS, lpuiInfoTable, &iSize);
	delete [] lpuiInfoTable;
	
	return iRet;
}

// Function name	: SinlineUSB_DoGetUPSStructures
// Description		: Return specified structure from UPS
int SinlineUSB_DoGetUPSStructures(eups_structs eType, void *lpvBuff, int *lpiBuffSize)
{
#undef COMMIOFAIL
#define COMMIOFAIL( v, en ) { if ( COMMFAIL( v ) ) {	\
	iSinlineUSB_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 (!commSinlineUSB.isInitialized())
		return IOCTL_ERROR_NOTYETINITIALIZED;

	/* 
	 * Asking UPS
	 */
	for (int i = 0; i < 4; i++) {
		ucDataBuff[0]=0;
		commSinlineUSB.flush(1,1);
		COMMIOFAIL( commSinlineUSB.send(FRAME_INITCODE), IOCTL_ERROR_COMM_FAILTOSEND );
		COMMIOFAIL( commSinlineUSB.receive(&ucDataBuff[0]), IOCTL_ERROR_COMM_FAILTORECEIVE )

		if (ucDataBuff[0]==FRAME_INITCODE) {
			bInitialized = true;
			break;
		}
		usleep(20000);
	}
	if (!bInitialized) {
		iSinlineUSB_ExtError=IOCTL_ERROR_COMM_INVALIDCODE;
		return IOCTL_ERROR_COMM_LINKFAIL;
	}
	COMMIOFAIL( commSinlineUSB.send( FRAME_START ), IOCTL_ERROR_COMM_FAILTOSEND );
	ucLength=3;
	COMMIOFAIL( commSinlineUSB.send( ucLength ), IOCTL_ERROR_COMM_FAILTOSEND );
	COMMIOFAIL( commSinlineUSB.send( FRAME_CMD_GET ), IOCTL_ERROR_COMM_FAILTOSEND );
	ucCRC+=FRAME_CMD_GET;
	COMMIOFAIL( commSinlineUSB.send( eType ), IOCTL_ERROR_COMM_FAILTOSEND );
	ucCRC+=eType;
	ucCRC%=256;
	COMMIOFAIL( commSinlineUSB.send( ucCRC ), IOCTL_ERROR_COMM_FAILTOSEND );
	/* 
	 * USP response
	 */
	ucDataBuff[0]=ucDataBuff[1]=ucDataBuff[2]=0;
	COMMIOFAIL( commSinlineUSB.receive(&ucDataBuff[0]), IOCTL_ERROR_COMM_FAILTORECEIVE );
	if (ucDataBuff[0]!=FRAME_START) {
		iSinlineUSB_ExtError = IOCTL_ERROR_COMM_INVALIDFRAME;
		return IOCTL_ERROR_COMM_LINKFAIL;
	}
	
	COMMIOFAIL( commSinlineUSB.receive( &ucLength ), IOCTL_ERROR_COMM_FAILTORECEIVE );

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

	if (eType == EU_INFORMATIONS) {
		COMMIOFAIL( commSinlineUSB.receive( &ucDataBuff[0] ), IOCTL_ERROR_COMM_FAILTORECEIVE );
		uiDataBuff[iindex]=(unsigned int)ucDataBuff[0];
		ulCRC+=uiDataBuff[iindex++];
		COMMIOFAIL( commSinlineUSB.receive( &ucDataBuff[0] ), IOCTL_ERROR_COMM_FAILTORECEIVE );
		uiDataBuff[iindex]=(unsigned int)ucDataBuff[0];
		ulCRC+=uiDataBuff[iindex++];

		COMMIOFAIL( commSinlineUSB.receive2brev( &uiDataBuff[iindex], 1, &ulCRC2 ), IOCTL_ERROR_COMM_FAILTORECEIVE );
		iindex++;
		ulCRC+=ulCRC2; 
		ulCRC2=0;
		COMMIOFAIL( commSinlineUSB.receive2brev( &uiDataBuff[iindex], 1, &ulCRC2 ), IOCTL_ERROR_COMM_FAILTORECEIVE );
		iindex++;
		ulCRC+=ulCRC2; 
		ulCRC2=0;
		COMMIOFAIL( commSinlineUSB.receive2brev( &uiDataBuff[iindex], 1, &ulCRC2 ), IOCTL_ERROR_COMM_FAILTORECEIVE );
		iindex++;
		ulCRC+=ulCRC2; 
		ulCRC2=0;
		COMMIOFAIL( commSinlineUSB.receive2brev( &uiDataBuff[iindex], 1, &ulCRC2 ), IOCTL_ERROR_COMM_FAILTORECEIVE );
		iindex++;
		ulCRC+=ulCRC2; 
		ulCRC2=0;

		COMMIOFAIL( commSinlineUSB.receive( &ucDataBuff[0] ), IOCTL_ERROR_COMM_FAILTORECEIVE );
		uiDataBuff[iindex]=(unsigned int)ucDataBuff[0];
		ulCRC+=uiDataBuff[iindex++];
		COMMIOFAIL( commSinlineUSB.receive( &ucDataBuff[0] ), IOCTL_ERROR_COMM_FAILTORECEIVE );
		uiDataBuff[iindex]=(unsigned int)ucDataBuff[0];
		ulCRC+=uiDataBuff[iindex++];
		COMMIOFAIL( commSinlineUSB.receive( &ucDataBuff[0] ), IOCTL_ERROR_COMM_FAILTORECEIVE );
		uiDataBuff[iindex]=(unsigned int)ucDataBuff[0];
		ulCRC+=uiDataBuff[iindex++];
	} else if (eType == EU_SETUP) {
		COMMIOFAIL( commSinlineUSB.receive( &ucDataBuff[0] ), IOCTL_ERROR_COMM_FAILTORECEIVE );
		uiDataBuff[iindex]=(unsigned int)ucDataBuff[0];
		ulCRC+=uiDataBuff[iindex++];
		COMMIOFAIL( commSinlineUSB.receive( &ucDataBuff[0] ), IOCTL_ERROR_COMM_FAILTORECEIVE );
		uiDataBuff[iindex]=(unsigned int)ucDataBuff[0];
		ulCRC+=uiDataBuff[iindex++];
	}
	//CRC
	COMMIOFAIL( commSinlineUSB.receive( &ucCRC ), IOCTL_ERROR_COMM_FAILTORECEIVE );

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

	iSinlineUSB_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineUSB_DoSetUPSStructures
// Description		: 
int SinlineUSB_DoSetUPSStructures(eups_structs eType, void *lpvBuff, int *lpiBuffSize, int *lpiBuffErrPos )
{
#undef COMMIOFAIL
#define COMMIOFAIL( v, en ) { if ( COMMFAIL( v ) ) {	\
	iSinlineUSB_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 ( !commSinlineUSB.isInitialized() )
		return IOCTL_ERROR_NOTYETINITIALIZED;

	if ( !lpvBuff )
		return IOCTL_ERROR_INVALIDARGUMENT;
	memcpy( &uiDataBuff, lpvBuff, *lpiBuffSize * sizeof(unsigned int));
	/* 
	 * Asking UPS
	 */
	for (int i=0; i<4; i++) {
		ucDataBuff[0] = 0;

		commSinlineUSB.flush(1,1);
		COMMIOFAIL( commSinlineUSB.send(FRAME_INITCODE), IOCTL_ERROR_COMM_FAILTOSEND );
		COMMIOFAIL( commSinlineUSB.receive(&ucDataBuff[0]), IOCTL_ERROR_COMM_FAILTORECEIVE )
		if (ucDataBuff[0] == FRAME_INITCODE) {
			bInitialized = true;
			break;
		}
		usleep(20000);
	}
	if (!bInitialized) {
		iSinlineUSB_ExtError = IOCTL_ERROR_COMM_INVALIDCODE;
		return IOCTL_ERROR_COMM_LINKFAIL;
	}
	COMMIOFAIL( commSinlineUSB.send( FRAME_START ), IOCTL_ERROR_COMM_FAILTOSEND );
	//ucLength = 3 + (*lpiBuffSize * 2);
	ucLength = 3 + (*lpiBuffSize);
	ucCRC = 0;
	COMMIOFAIL( commSinlineUSB.send( ucLength ), IOCTL_ERROR_COMM_FAILTOSEND );
	COMMIOFAIL( commSinlineUSB.send( FRAME_CMD_SET ), IOCTL_ERROR_COMM_FAILTOSEND );
	ucCRC += FRAME_CMD_SET;
	COMMIOFAIL( commSinlineUSB.send( eType ), IOCTL_ERROR_COMM_FAILTOSEND );
	ucCRC += eType;

	if (eType == EU_SETUP) {
		COMMIOFAIL( commSinlineUSB.send((unsigned char)LOBYTE(uiDataBuff[0])), IOCTL_ERROR_COMM_FAILTOSEND );
		COMMIOFAIL( commSinlineUSB.send((unsigned char)LOBYTE(uiDataBuff[1])), IOCTL_ERROR_COMM_FAILTOSEND );
	}

	for ( int j = 0; j < *lpiBuffSize; j++ )
		ucCRC += (LOBYTE(uiDataBuff[j]) + HIBYTE(uiDataBuff[j]));
	ucCRC %= 256;
	
	COMMIOFAIL( commSinlineUSB.send( ucCRC ), IOCTL_ERROR_COMM_FAILTOSEND );
	/* 
	 * UPS response
	 */
	usleep(250000);
	ucDataBuff[0]=ucDataBuff[1]=ucDataBuff[2]=0;
	COMMIOFAIL( commSinlineUSB.receive( &ucDataBuff[0] ), IOCTL_ERROR_COMM_FAILTORECEIVE );
	if (ucDataBuff[0] != FRAME_START) {
		iSinlineUSB_ExtError = IOCTL_ERROR_COMM_INVALIDFRAME;
		return IOCTL_ERROR_COMM_LINKFAIL;
	}
	usleep(20000);
	COMMIOFAIL( commSinlineUSB.receive( &ucDataBuff[1] ), IOCTL_ERROR_COMM_FAILTORECEIVE );
	ucErrCode = ucDataBuff[1];
	switch( ucErrCode ) {
	case 0x05:	// Nieprawidlowa ilosc bajtow w ramce
		iSinlineUSB_ExtError = IOCTL_ERROR_COMM_BADFRAMELENGTH;
		return IOCTL_ERROR_COMM_LINKFAIL;
	case 0x0A:	// Nieznany typ rozkazu
		iSinlineUSB_ExtError = IOCTL_ERROR_COMM_UNKNOWNCOMMANDTYPE;
		return IOCTL_ERROR_COMM_LINKFAIL;
	case 0x15:	// Nieznany rozkaz
		iSinlineUSB_ExtError = IOCTL_ERROR_COMM_UNKNOWNCOMMAND;
		return IOCTL_ERROR_COMM_LINKFAIL;
	case 0x20:	// Blad CRC
		iSinlineUSB_ExtError = IOCTL_ERROR_COMM_BADCRC;
		return IOCTL_ERROR_COMM_LINKFAIL;
	case 0x5F:	// Data integrity error
		COMMIOFAIL( commSinlineUSB.receive( &ucErrCode ), IOCTL_ERROR_COMM_FAILTORECEIVE );
		if ( lpiBuffErrPos != NULL )
			*lpiBuffErrPos = ucErrCode;
		iSinlineUSB_ExtError = IOCTL_ERROR_COMM_DATAINTEGRITY;
		return IOCTL_ERROR_COMM_LINKFAIL;
	case 0xFF:	// Success = no error
		break;
	}

	iSinlineUSB_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineUSB_DoGetConfigParamsCount
// Description		: 
int	SinlineUSB_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 ) );

	iSinlineUSB_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineUSB_DoGetConfigParams
// Description		: 
int	SinlineUSB_DoGetConfigParams(void *lpvBuff, int *lpiBuffSize)
{
	iSinlineUSB_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

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

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

	strcpy(sdcSinlineUSB.szSerialNumber, "SINLINE0");
	
	/* WORKCONTROL:buzer */
	if (scfgvaldef[ INT_UPSCFG_BUZZER ].value.iValue == 1)
		sdcSinlineUSB.iSetupTable[ UPS_WORKCONTROL ] |= FLAG_WORKCONTROL_BUZZER;
	else
		sdcSinlineUSB.iSetupTable[ UPS_WORKCONTROL ] &= ~(FLAG_WORKCONTROL_BUZZER);
	/* OPOZNIENIE_STANDBY */
	if ((int)(scfgvaldef[ INT_UPSCFG_STANDBYDELAY ].value.iValue) < 60) {
		console_report( TXT_INF_STANDBYTIMETOOSHORT );
	}
	sdcSinlineUSB.iSetupTable[ UPS_STANDBYDELAY ] = (int)(scfgvaldef[ INT_UPSCFG_STANDBYDELAY ].value.iValue *
		scfgvaldef[ INT_UPSCFG_STANDBYDELAY ].dDivider) - 5;
	if (sdcSinlineUSB.iSetupTable[ UPS_STANDBYDELAY ]<0)
		sdcSinlineUSB.iSetupTable[ UPS_STANDBYDELAY ]=0;

	/* WORKCONTROL:progi */
	if (scfgvaldef[ INT_UPSCFG_PS_TRESHOLDS ].value.iValue == 1)
		sdcSinlineUSB.iSetupTable[ UPS_WORKCONTROL ] |= FLAG_WORKCONTROL_PROGI2;
	else
		sdcSinlineUSB.iSetupTable[ UPS_WORKCONTROL ] &= ~(FLAG_WORKCONTROL_PROGI2);

	if (commSinlineUSB.isInitialized()) {
		iSize=UPS_SETUP_COUNT;
		iRet=SinlineUSB_DoSetUPSStructures(EU_SETUP, sdcSinlineUSB.iSetupTable, &iSize, NULL);
		if (iRet!=IOCTL_ERROR_SUCCESS)
			return iRet;
	}

	iSinlineUSB_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineUSB_DoGetConfigParam
// Description		: 
int	SinlineUSB_DoGetConfigParam(void *lpvBuff, int *lpiBuffSize)
{
	iSinlineUSB_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineUSB_DoSetConfigParam
// Description		: 
int	SinlineUSB_DoSetConfigParam(void *lpvBuff, int *lpiBuffSize)
{
	iSinlineUSB_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineUSB_ReadConfig
// Description		: 
int	SinlineUSB_ReadConfig()
{
	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;
	strcpy(sdcSinlineUSB.szSerialNumber,
		((s=pCfgDev->getcfgitemvalue("serialnumber"))=="")?"SINLINE0":s);
	sdcSinlineUSB.bAudibleAlarm=(bool)atoi(((s=pCfgDev->getcfgitemvalue("audible_alarm"))=="")?"1":s);
	sdcSinlineUSB.uiStandbyTimeout=(unsigned int)strtoul(
		((s=pCfgDev->getcfgitemvalue("powerfail_to_standby_timeout"))=="")?"90":s, NULL, 10);
	sdcSinlineUSB.bExtendedTresholds=(bool)atoi(
		((s=pCfgDev->getcfgitemvalue("extended_transfer_tresholds"))=="")?"0":s);
	delete pCfgDev;
	
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SinlineUSB_DoUpdateConfig
// Description		: 
int	SinlineUSB_DoUpdateConfig(void *lpvBuff, int *lpiBuffSize)
{
	iSinlineUSB_ExtError = 0;
	
	if (SinlineUSB_ReadConfig() != IOCTL_ERROR_SUCCESS)
		return IOCTL_ERROR_CONFIG_READFAIL;
	
	/* dzwiek */
	if (sdcSinlineUSB.bAudibleAlarm)
		sdcSinlineUSB.iSetupTable[ UPS_WORKCONTROL ] |= FLAG_WORKCONTROL_BUZZER;
	else
		sdcSinlineUSB.iSetupTable[ UPS_WORKCONTROL ] &= ~(FLAG_WORKCONTROL_BUZZER);
	/* opoznienie standby */
	if ((int)sdcSinlineUSB.uiStandbyTimeout < 60) {
		console_report( TXT_INF_STANDBYTIMETOOSHORT );
	}
	sdcSinlineUSB.iSetupTable[ UPS_STANDBYDELAY ] = (int)sdcSinlineUSB.uiStandbyTimeout - 5;
	if (sdcSinlineUSB.iSetupTable[ UPS_STANDBYDELAY ]<0) 
		sdcSinlineUSB.iSetupTable[ UPS_STANDBYDELAY ]=0;
	/* WORKCONTROL:rozszerzone progi */
	if (sdcSinlineUSB.bExtendedTresholds == 1)
		sdcSinlineUSB.iSetupTable[ UPS_WORKCONTROL ] |= FLAG_WORKCONTROL_PROGI2;
	else
		sdcSinlineUSB.iSetupTable[ UPS_WORKCONTROL ] &= ~(FLAG_WORKCONTROL_PROGI2);
		
	int iSize=UPS_SETUP_COUNT;
	return SinlineUSB_DoSetUPSStructures(EU_SETUP, sdcSinlineUSB.iSetupTable, &iSize, NULL);
}

// Function name	: SinlineUSB_DoGetDriverMode
// Description		: 
int	SinlineUSB_DoGetDriverMode(void *lpvBuff, int *lpiBuffSize)
{
	iSinlineUSB_ExtError = 0;
	return IOCTL_ERROR_CMD_NOTSUPPORTED;
}

// Function name	: SinlineUSB_DoUpsShutdown
// Description		: 
int SinlineUSB_DoUpsShutdown(void *lpvBuff, int *lpiBuffSize)
{
	unsigned int *lpuiSetupTable = new unsigned int[ UPS_SETUP_COUNT ];
	int iRet, iSize = UPS_SETUP_COUNT;

	iRet = SinlineUSB_DoGetUPSStructures( EU_SETUP, lpuiSetupTable, &iSize );
	if (iRet != IOCTL_ERROR_SUCCESS) {
		delete [] lpuiSetupTable;
		return iRet;
	}
	
	/* KONTROLA_PRACY:shutdown */
	lpuiSetupTable[ UPS_WORKCONTROL ] &= ~(FLAG_WORKCONTROL_STANDBY);
	lpuiSetupTable[ UPS_WORKCONTROL ] |= FLAG_WORKCONTROL_STANDBY;

	iSize = UPS_SETUP_COUNT;
	iRet = SinlineUSB_DoSetUPSStructures( EU_SETUP, lpuiSetupTable, &iSize, NULL );
	if ( iRet != IOCTL_ERROR_SUCCESS )
		return iRet;

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

// Function name	: GetLowBatteryLevel
// Description		: Returns Battery Low value
double SinlineUSB_GetLowBatteryLevel(double dPower, double dAku)
{
	double dret, fbl = 2.0;
	fbl = (double)SinlineUSB_GetAccuCount(sdcSinlineUSB.iInfTable[UPS_SERIES]);
	const double dval = 10.50;
	if (dPower==0 || dAku==0)
		return (double)(dval*fbl);
	//return (double)(((0.0404*(dPower/dAku))+9.5908)*fbl);
	//return (double)(((0.055*(dPower/dAku))+10.0)*fbl);
	//dret = (0.030*(dPower/dAku))+10.40;
	dret = (0.026*(dPower/dAku))+10.45;
	//PrintDebugString("LB_EXPR: %s", ReplaceVariables(sup, "(0.026*($pwr_effective/$v_aku))+10.45"));
	if (dret>=dval)
		return (dret*fbl);
	else
		return (dval*fbl);
}

// returns numer of accumulators of the ups
int SinlineUSB_GetAccuCount(int iUpsTypeNo)
{
	switch(iUpsTypeNo) {
	case 1://800 = 24V
		return 2;
	case 2://1200= 24V
		return 2;
	case 3://1600= 36V
		return 3;
	case 4://2000= 48V
		return 4;
	case 5://2500= 48V
		return 4;
	case 6://3000= 48V
		return 4;
	default:
		return 2;
	}
}
