/***************************************************************************
 *   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 ECOPROCDS_DRIVER_BUILD 1
#include "ecoprocds.h"

/* 
 * Deklaracje zmiennych globalnych
 */
sdrv_config sdcEcoProCds;
int iEcoProCds_ExtError = 0;
FTCComm	commEcoProCds;

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

		case IOCTL_UNINIT:
			return EcoProCds_DoUnInit(lpvBuff, lpiBuffSize);
		
		case IOCTL_GETCONFIGFILENAME:
			return EcoProCds_DoGetConfigFileName(lpvBuff, lpiBuffSize);

		case IOCTL_AUTOCONFIGURE: 
			return EcoProCds_DoAutoConfigure(lpvBuff, lpiBuffSize);

		case IOCTL_CONFIGURE: 
			return EcoProCds_DoConfigure(lpvBuff, lpiBuffSize);

		case IOCTL_GET_UPSINFOMASK: 
			return EcoProCds_DoGetUpsInfoMask(lpvBuff, lpiBuffSize);

		case IOCTL_GET_UPSSTATESMASK:
			return EcoProCds_DoGetUpsStateMask(lpvBuff, lpiBuffSize);

		case IOCTL_GET_UPSSTATE:
			return EcoProCds_DoGetUpsState(lpvBuff, lpiBuffSize);

		case IOCTL_GET_UPSPARAMSMASK:
			return EcoProCds_DoGetUpsParamsMask(lpvBuff, lpiBuffSize);

		case IOCTL_GET_UPSPARAMS:
			return EcoProCds_DoGetUpsParams(lpvBuff, lpiBuffSize);

		case IOCTL_GET_UPSSETUPPARAMSMASK:
			return EcoProCds_DoGetUpsSetupParamsMask(lpvBuff, lpiBuffSize);

		case IOCTL_GET_UPSSETUPPARAMS:
			return EcoProCds_DoGetUpsSetupParams(lpvBuff, lpiBuffSize);

		case IOCTL_GET_UPSCHARACTERISTICMASK:
			return EcoProCds_DoGetUpsCharacteristicMask(lpvBuff, lpiBuffSize);

		case IOCTL_GET_UPSCHARACTERISTIC:
			return EcoProCds_DoGetUpsCharacteristic(lpvBuff, lpiBuffSize);

		case IOCTL_GET_DRIVER_INFO:
			return EcoProCds_DoGetDriverInfo(lpvBuff, lpiBuffSize);

		case IOCTL_GET_ERRORNO:
			return EcoProCds_DoGetExtendedError(lpvBuff, lpiBuffSize);

		case IOCTL_TESTUPSLINK:
			return EcoProCds_DoTestUpsLink(lpvBuff, lpiBuffSize);

		case IOCTL_GETCONFIGPARAMSCOUNT:
			return EcoProCds_DoGetConfigParamsCount(lpvBuff, lpiBuffSize);

		case IOCTL_GETCONFIGPARAMS:
			return EcoProCds_DoGetConfigParams(lpvBuff, lpiBuffSize);

		case IOCTL_GETCONFIGPARAM:
			return EcoProCds_DoGetConfigParam(lpvBuff, lpiBuffSize);

		case IOCTL_SETCONFIGPARAMS:
			return EcoProCds_DoSetConfigParams(lpvBuff, lpiBuffSize);

		case IOCTL_SETCONFIGPARAM:
			return EcoProCds_DoSetConfigParam(lpvBuff, lpiBuffSize);

		case IOCTL_UPDATECONFIG:
			return EcoProCds_DoUpdateConfig(lpvBuff, lpiBuffSize);

		case IOCTL_SHUTDOWNUPS:
			return EcoProCds_DoUpsShutdown(lpvBuff, lpiBuffSize);

		default:
			return IOCTL_ERROR_CMD_NOTSUPPORTED;
	}
}

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

	if (EcoProCds_ReadConfig() != IOCTL_ERROR_SUCCESS)
		return IOCTL_ERROR_CONFIG_READFAIL;
	
	commEcoProCds.init(sdcEcoProCds.szSerialNumber, 0);
	if (commEcoProCds.open_simplesignalling()!=COMM_SUCCESS) {
		iEcoProCds_ExtError=IOCTL_ERROR_COMM_INITFAIL;
		return IOCTL_ERROR;
	}
	
	commEcoProCds.clr_line(FT_S_DTR);
	
	iEcoProCds_ExtError = 0;
	EcoProCds_DoUpdateConfig(NULL, NULL);	

	return IOCTL_ERROR_SUCCESS;
}

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

// Function name	: EcoProCds_DoGetConfigFileName
// Description		: 
int EcoProCds_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	: DoAutoConfigure
// Description		: Self configure
int EcoProCds_DoAutoConfigure( void *lpvBuff, int *lpiBuffSize )
{
	strcpy( sdcEcoProCds.szSerialNumber, "ECOPRO00" );

	return IOCTL_ERROR_SUCCESS;
}

// Function name	: DoConfigure
// Description		: Configure driver to work properly with UPS
int EcoProCds_DoConfigure( void *lpvBuff, int *lpiBuffSize )
{
	strcpy( sdcEcoProCds.szSerialNumber, "ECOPRO00" );
	
	EcoProCds_DoUnInit(NULL, NULL);
	EcoProCds_DoInit(NULL, NULL);

	return IOCTL_ERROR_SUCCESS;
}

// Function name	: EcoProCds_DoGetUpsInfoMask
// Description		: Zwraca informacje o wypelnieniu struktury informacji o UPS-ie
int EcoProCds_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	: EcoProCds_DoGetUpsStateMask
// Description		: 
int	EcoProCds_DoGetUpsStateMask(void *lpvBuff, int *lpiBuffSize)
{
	unsigned long ulMask = US_POWERON | US_POWERFAIL | US_BATTERYLOW;

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

// Function name	: EcoProCds_DoGetUpsState
// Description		: Return actual UPS state
int	EcoProCds_DoGetUpsState(void *lpvBuff, int *lpiBuffSize)
{
#undef NOTSET
#define NOTSET(src, bit) (((src & bit)==bit)?0:1)

	unsigned long ulUpsState = 0;
	unsigned int uiUpsState[3] = { 0 };
	unsigned int uiCnt = 0;

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

	if (commEcoProCds.isInitialized()) {
		// check if communication is still active
		for (uiCnt=0; uiCnt<3; uiCnt++)
			if (commEcoProCds.status_check(FT_CTS) < 0)
				iEcoProCds_ExtError = IOCTL_ERROR_COMM_LINKTERMINATED;
		else
			iEcoProCds_ExtError = 0;
		if (iEcoProCds_ExtError != 0)
			return IOCTL_ERROR;

		for (uiCnt=0; uiCnt<3; uiCnt++) {
			usleep(30000);
			uiUpsState[0] = commEcoProCds.getStatus();
			usleep(30000);
			uiUpsState[1] = commEcoProCds.getStatus();
			usleep(30000);
			uiUpsState[2] = commEcoProCds.getStatus();
			if (uiUpsState[0]==uiUpsState[1])
				uiUpsState[2]=uiUpsState[0]=uiUpsState[1];
			else if (uiUpsState[0]==uiUpsState[2])
				uiUpsState[0]=uiUpsState[1]=uiUpsState[2];
			else if (uiUpsState[1]==uiUpsState[2])
				uiUpsState[0]=uiUpsState[1]=uiUpsState[2];
			else
				continue;
			break;
		}
	} else {
		iEcoProCds_ExtError = IOCTL_ERROR_NOTYETINITIALIZED;
		return IOCTL_ERROR;
	}
	
	// Only these flags are supported by now and forever
	// US_POWERON | US_POWERFAIL | US_BATTERYLOW

	// bits conversion to the pscore standard
	if ( ( uiUpsState[0] & USB_AND_POWERFAIL ) == USB_FLAG_POWERFAIL )
		ulUpsState |= US_POWERFAIL;
	else
		ulUpsState &= ~(US_POWERFAIL);

	ulUpsState &= ~(US_POWERON);
	if (NOTSET(ulUpsState, US_POWERFAIL))
		ulUpsState |= US_POWERON;
	
	if ( !(( uiUpsState[0] & USB_AND_LOWBATTERY ) == USB_FLAG_LOWBATTERY) )
	{
		if (NOTSET(ulUpsState, US_POWERON))
			ulUpsState |= US_BATTERYLOW;
	}
	else
		ulUpsState &= ~(US_BATTERYLOW);

	memcpy( lpvBuff, &ulUpsState, *lpiBuffSize );

	return IOCTL_ERROR_SUCCESS;
}

// Function name	: EcoProCds_DoGetUpsParamsMask
// Description		:
int	EcoProCds_DoGetUpsParamsMask(void *lpvBuff, int *lpiBuffSize)
{
	__int64 iMask = UP_UID_FAMILY | UP_UID_MODEL;

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

// Function name	: EcoProCds_DoGetUpsParams
// Description		: 
int	EcoProCds_DoGetUpsParams(void *lpvBuff, int *lpiBuffSize)
{
	int iSize, iRet;
	supsp sup;

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

	iSize = PARAMS_TABLE_ITEMS;
	iRet = EcoProCds_GetAllUpsParams( sdcEcoProCds.uiParamsTable, &iSize );
	if (iRet != IOCTL_ERROR_SUCCESS)
		return iRet;

	// fill params mask
	sup.iMask = UP_UID_FAMILY | UP_UID_MODEL;

	sprintf( sup.sz__uid_model, "%s %s", DRIVER_UPS_PREFIX, DRIVER_UPS_SUFFIX );
	sprintf( sup.sz__uid_family, "%s", DRIVER_UPS_FAMILY );

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

	return IOCTL_ERROR_SUCCESS;
}

// Function name	: EcoProCds_DoGetUpsSetupParamsMask
// Description		: 
int	EcoProCds_DoGetUpsSetupParamsMask(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	: EcoProCds_DoGetUpsSetupParams
// Description		: 
int	EcoProCds_DoGetUpsSetupParams(void *lpvBuff, int *lpiBuffSize)
{
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: EcoProCds_DoGetDriverInfo
// Description		: 
int	EcoProCds_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_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	: EcoProCds_DoGetExtendedError
// Description		: Get extended error information - return value of last error
int	EcoProCds_DoGetExtendedError(void *lpvBuff, int *lpiBuffSize)
{
	return iEcoProCds_ExtError;
}

// Function name	: EcoProCds_DoGetUpsCharacteristicMask
// Description		: 
int	EcoProCds_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	: EcoProCds_DoGetUpsCharacteristic
// Description		: 
int	EcoProCds_DoGetUpsCharacteristic(void *lpvBuff, int *lpiBuffSize)
{
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: EcoProCds_DoTestUpsLink
// Description		: 
int	EcoProCds_DoTestUpsLink(void *lpvBuff, int *lpiBuffSize)
{
	int iCnt=0;
	
	for (iCnt=0; iCnt<3; iCnt++) {
		commEcoProCds.clr_line(FT_S_RTS);
		sleep(2);
		if (!commEcoProCds.status_check(FT_RING)) {
			commEcoProCds.set_line(FT_S_RTS);
			sleep(2);
			if (!commEcoProCds.status_check(FT_RING)) {
				commEcoProCds.clr_line(FT_S_RTS);
				if (iCnt>=2)
					return IOCTL_ERROR_COMM_LINKFAIL;
				else
					continue;
			}
			commEcoProCds.clr_line(FT_S_RTS);
			sleep(1);
			break;
		}
		else
			if (iCnt>=2)
				return IOCTL_ERROR_COMM_LINKFAIL;
		else
			continue;
	}
	return IOCTL_ERROR_SUCCESS;
}

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

	iEcoProCds_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

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

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

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

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

// Function name	: EcoProCds_ReadConfig
// Description		: 
int	EcoProCds_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(sdcEcoProCds.szSerialNumber, ((s=pCfgDev->getcfgitemvalue("serialnumber"))=="")?"ECOPRO00":s);
	delete pCfgDev;
	
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: EcoProCds_DoUpdateConfig
// Description		: 
int	EcoProCds_DoUpdateConfig(void *lpvBuff, int *lpiBuffSize)
{
	if (EcoProCds_ReadConfig() != IOCTL_ERROR_SUCCESS)
		return IOCTL_ERROR_CONFIG_READFAIL;
		
	iEcoProCds_ExtError = 0;
	if (!commEcoProCds.isInitialized())
		return IOCTL_ERROR_NOTYETINITIALIZED;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: EcoProCds_DoUpsShutdown
// Description		: 
int EcoProCds_DoUpsShutdown(void *lpvBuff, int *lpiBuffSize)
{
	if (commEcoProCds.isInitialized()) {
		commEcoProCds.set_line(FT_S_DTR);
		sleep(5);
		commEcoProCds.clr_line(FT_S_DTR);
	} else {
		iEcoProCds_ExtError = IOCTL_ERROR_NOTYETINITIALIZED;
		return IOCTL_ERROR;
	}
	iEcoProCds_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: EcoProCds_GetAllUpsParams
// Description		: 
int EcoProCds_GetAllUpsParams(void *lpvBuff, int *lpiBuffSize)
{
	/* parameters table */
	unsigned int uiParamsTable[PARAMS_TABLE_ITEMS];
	
	if (lpiBuffSize != NULL)
		if (*lpiBuffSize < PARAMS_TABLE_ITEMS)
			return IOCTL_ERROR_INVALIDARGUMENT;

	if (commEcoProCds.isInitialized()) {
		uiParamsTable[IDXP_STATEFLAG] = commEcoProCds.getStatus();
	} else {
		iEcoProCds_ExtError = IOCTL_ERROR_NOTYETINITIALIZED;
		return IOCTL_ERROR;
	}

	memcpy( lpvBuff, uiParamsTable, PARAMS_TABLE_ITEMS * sizeof(unsigned int) );

	iEcoProCds_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: EcoProCds_GetAllUpsSetupParams
// Description		: 
int EcoProCds_GetAllUpsSetupParams(void *lpvBuff, int *lpiBuffSize)
{
	/* tablica parametrow */
	unsigned int uiSetupTable[SETUP_TABLE_ITEMS];
	
	if (lpiBuffSize != NULL)
		if (*lpiBuffSize < SETUP_TABLE_ITEMS)
			return IOCTL_ERROR_INVALIDARGUMENT;

	memcpy( lpvBuff, uiSetupTable, SETUP_TABLE_ITEMS * sizeof(unsigned int) );

	iEcoProCds_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: EcoProCds_SetAllUpsSetupParams
// Description		: 
int EcoProCds_SetAllUpsSetupParams(void *lpvBuff, int *lpiBuffSize)
{
	/* tablica parametrow */
	unsigned int uiSetupTable[SETUP_TABLE_ITEMS];
	
	if (lpiBuffSize != NULL)
		if (*lpiBuffSize < SETUP_TABLE_ITEMS)
			return IOCTL_ERROR_INVALIDARGUMENT;

	memcpy( &uiSetupTable, lpvBuff, sizeof(unsigned int) * (*lpiBuffSize) );

	iEcoProCds_ExtError = 0;
	return IOCTL_ERROR_CMD_NOTSUPPORTED;
}

