// BQ Gauge Driver
#include "stdafx.h"

//These #include's are BSP dependant.  
//Function "SOCGetI2CDeviceByBus" here
#include <soc_cfg.h>

//All I2C Functions here (I2COpen, I2CClose, I2CWrite, I2CRead, etc.)
#include <sdk_i2c.h>

#define BQ_ADDRESS 0x55

//Enable/Disable Debug Messages
//#define BQ_DEBUG

DWORD BQD_Init(LPCTSTR pContext, LPCVOID lpvBusContext);
BOOL BQD_Deinit( DWORD hDeviceContext );
DWORD BQD_Open( DWORD hDeviceContext, DWORD AccessCode, DWORD ShareMode );
BOOL BQD_Close( DWORD hOpenContext );
BOOL BQD_IOControl( DWORD hOpenContext, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut );
void BQD_PowerUp( DWORD hDeviceContext );
void BQD_PowerDown( DWORD hDeviceContext );
DWORD BQD_Read( DWORD hOpenContext, LPVOID pBuffer, DWORD Count );
DWORD BQD_Write( DWORD hOpenContext, LPCVOID pBuffer, DWORD Count );
DWORD BQD_Seek( DWORD hOpenContext, long Amount, WORD Type );
inline void BQ_DBGPrint(PTCHAR buf);

//Also defined in bq_drv.h for use in applications.

#define IOCTL_SETSLAVEADDR 0x1001
#define IOCTL_I2CREAD 0x1002
#define IOCTL_I2CWRITE 0x1003

//Also defined in bq_drv.h for use in applications.

typedef struct sBQ_Data {
	USHORT Control;
	SHORT AtRate;
	USHORT AtRateTTE;
	SHORT Temp;
	USHORT Voltage;
	USHORT Flags;
	USHORT NomAvailCap;
	USHORT FullAvailCap;
	USHORT RemCap;
	USHORT FullChgCap;
	SHORT AvgCurr;
	USHORT TTE;
	USHORT TTF;
	USHORT StbyCurr;
	USHORT StbyTTE;
	USHORT MaxLoadCurr;
	USHORT MaxLoadTTE;
	USHORT AvailEnergy;
	USHORT AvgPow;
	USHORT TTEAtConstPow;
	USHORT CycleCnt;
	USHORT StateOfChg;
} BQ_Data ;


HANDLE hI2C;      // Handle for I2C Module
// ----------------------------------------------------

//DLLMain: Not much to do here.  Make sure that we set our handle to be invalid.
BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved )
{
    switch(ul_reason_for_call) {
        case DLL_PROCESS_ATTACH:
            BQ_DBGPrint(L"BQDrv - DLL_PROCESS_ATTACH\n");
			hI2C = INVALID_HANDLE_VALUE;
        break;
        case DLL_PROCESS_DETACH:
            BQ_DBGPrint(L"BQDrv - DLL_PROCESS_DETACH\n");
        break;
        case DLL_THREAD_ATTACH:
            BQ_DBGPrint(L"BQDrv - DLL_THREAD_ATTACH\n");
        break;
        case DLL_THREAD_DETACH:
            BQ_DBGPrint(L"BQDrv - DLL_THREAD_DETACH\n");
        break;
        default:
        break;
    }
return TRUE;
}

// Driver Init...  Nothing to do here
DWORD BQD_Init( LPCTSTR pContext, LPCVOID lpvBusContext)
{
  BQ_DBGPrint(L"BQDrv - BQD_Init");

  BQ_DBGPrint(L"BQDrv - ~ BQD_Init\n");
  return 0x1234;
}

// Driver DeInit... Nothing to do here either
BOOL BQD_Deinit( DWORD hDeviceContext )
{
BQ_DBGPrint(L"BQDrv - BQD_Deinit\n");

BQ_DBGPrint(L"BQDrv - ~ BQD_Deinit\n");
return TRUE;
}

// Driver Open: Get the I2C Module
DWORD BQD_Open( DWORD hDeviceContext, DWORD AccessCode, DWORD ShareMode )
{
  BQ_DBGPrint(L"BQDrv - BQD_Open\n");
  BQ_DBGPrint(L"Opening I2C Port\n");

  //NOTE: The following is platform specific.  We use a AM3517 Experimenter Kit,
  //and we have connected our gauge to I2C bus 2.

  //On other platforms, one will have to find similar functions to I2COpen.
  hI2C = I2COpen(SOCGetI2CDeviceByBus(2));
  if(hI2C == INVALID_HANDLE_VALUE) {
	  BQ_DBGPrint(L"Failed to open I2C1\n");
	  return -1;
  }
  TCHAR* printbuf = (TCHAR*)malloc(128);
  
  _stprintf_s(printbuf,128,L"I2C handle value: %d\n",hI2C);
  BQ_DBGPrint(printbuf);
  
  _stprintf_s(printbuf,128,L"Setting Slave Address to: %hx\n",BQ_ADDRESS);
  BQ_DBGPrint(printbuf);

  //set I2C slave address to 0x55 (BQ address)
  //Set Subaddress mode to 8 bits: NOTE: this is what our gauge uses, but is not universal!
  
  //On other platforms, these functions will have to be replaced with similar.
  I2CSetSlaveAddress(hI2C,BQ_ADDRESS);
  I2CSetSubAddressMode(hI2C,I2C_SUBADDRESS_MODE_8);
  free(printbuf);

  BQ_DBGPrint(L"BQDrv - ~ BQD_Open\n");
  return 0x5678;
}

// Driver Close: Close the I2C Module
BOOL BQD_Close( DWORD hOpenContext )
{
  BQ_DBGPrint(L"BQDrv - DEM_Close\n");

  I2CClose(hI2C);
  BQ_DBGPrint(L"Closed I2C Port; finishing\n");

  BQ_DBGPrint(L"BQDrv - ~ DEM_Close\n");
return TRUE;
}

// IOControl: This allows us low level I2C Access.  We can read/write whatever we want, but we have to know what we're doing
BOOL BQD_IOControl( DWORD hOpenContext, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut )
{
  BQ_DBGPrint(L"BQDrv - BQD_IOControl\n");
  switch (dwCode) {
	case IOCTL_SETSLAVEADDR:  //Sets the slave address
	{
		PTCHAR printbuf = (PTCHAR)malloc(128);
		BQ_DBGPrint(L"BQDrv - IOCTL_SETSLAVEADDR\n");
		_stprintf_s(printbuf,128,L"Setting Slave Address to: %hx\n",*pBufIn);
		BQ_DBGPrint(printbuf);

		//Set the slave address here
		*pdwActualOut = I2CSetSlaveAddress(hI2C,*pBufIn);
		free(printbuf);
	}
	break;
	case IOCTL_I2CREAD:   //Perform an I2C read
	{
		PTCHAR printbuf = (PTCHAR)malloc(128);
		BQ_DBGPrint(L"BQDrv - IOCTL_I2CREAD\n");

		_stprintf_s(printbuf,128,L"Reading %d bytes from address %hx\n",dwLenOut,*pBufIn);
		BQ_DBGPrint(printbuf);

		//perform the read here: see bq_drv.h for API details
		*pdwActualOut = I2CRead(hI2C,*pBufIn,pBufOut,dwLenOut);

		//print error messages, if any
		if(*pdwActualOut != dwLenOut) {
			_stprintf_s(printbuf,128,L"Read length does not equal readsize: %d, %d\n",*pdwActualOut,dwLenOut);
			BQ_DBGPrint(printbuf);
		}

		//print what we read:
		BQ_DBGPrint(L"Results:\n");
		_stprintf_s(printbuf,128,L"len: %d\n",*pdwActualOut);
		BQ_DBGPrint(printbuf);
		for(int i = 0;i < (int)*pdwActualOut; i++ ) {
			_stprintf_s(printbuf,128,L"value %d: %hx\n",i,pBufOut[i]);
			BQ_DBGPrint(printbuf);
		}

		free(printbuf);
	}
	break;
	case IOCTL_I2CWRITE:  //perform an I2C write
	{
		PTCHAR printbuf = (PTCHAR)malloc(128);
		BQ_DBGPrint(L"BQDrv - IOCTL_I2CWRITE\n");

		_stprintf_s(printbuf,128,L"Writing %d bytes to address %hx\n",dwLenIn - 1,*pBufIn);
		BQ_DBGPrint(printbuf);

		//Perform the write here, see bq_drv.h for API details
		*pdwActualOut = I2CWrite(hI2C,pBufIn[0],pBufIn + 1,dwLenIn - 1);

		//Print any error messages
		if(*pdwActualOut != dwLenIn - 1) {
			_stprintf_s(printbuf,128,L"Read length does not equal readsize: %d, %d\n",*pdwActualOut,dwLenIn - 1);
			BQ_DBGPrint(printbuf);
		}
		free(printbuf);
	}
	break;
    default:
       BQ_DBGPrint(L"Unknown IOCTL\n");
    break;
  }
  BQ_DBGPrint(L"BQDrv - ~ DEM_IOControl\n");
  return TRUE;
}

// PowerUp not Implemented
void BQD_PowerUp( DWORD hDeviceContext )
{
  BQ_DBGPrint(L"BQDrv - BQD_PowerUp\n");

  BQ_DBGPrint(L"BQDrv - ~ BQD_PowerUp\n");
}

// PowerDown Not Implemented
void BQD_PowerDown( DWORD hDeviceContext )
{
  BQ_DBGPrint(L"BQDrv - BQD_PowerDown\n");

  BQ_DBGPrint(L"BQDrv - ~ BQD_PowerDown\n");
}

// Read: Reads the dataflash into a structure
DWORD BQD_Read( DWORD hOpenContext, LPVOID pBuffer, DWORD Count )
{
  DWORD dwRetCount=0xffff;      // default to error
  BQ_DBGPrint(L"BQDrv - BQD_Read\n");

  if(Count != sizeof(BQ_Data)) //Don't let someone read into a buffer that is the wrong size.
  {
		BQ_DBGPrint(L"Wrong Read Size!\n");
		return -1;
  }

  PTCHAR printbuf = (PTCHAR)malloc(128);
  BQ_DBGPrint(L"BQDrv - BQD_READ\n");

  PBYTE pbReadBuf = (PBYTE)pBuffer;

  _stprintf_s(printbuf,128,L"Reading %d bytes from address %hx\n",Count,0x00);
  BQ_DBGPrint(printbuf);
  
  //Perform the read here
  dwRetCount = I2CRead(hI2C,0x00,pbReadBuf,Count);
  
  //Print any errors
  if(dwRetCount != Count) {
	  _stprintf_s(printbuf,128,L"Read length does not equal readsize: %d, %d\n",dwRetCount,Count);
	  BQ_DBGPrint(printbuf);
  }
  
  //Print the results
  BQ_DBGPrint(L"Results:\n");
  _stprintf_s(printbuf,128,L"len: %d\n",dwRetCount);
  BQ_DBGPrint(printbuf);
  for(int i = 0;i < (int)dwRetCount; i++ ) {
	  _stprintf_s(printbuf,128,L"value %d: %hx\n",i,pbReadBuf[i]);
	  BQ_DBGPrint(printbuf);
  }

  free(printbuf);
  
  //Return how much we read
  return dwRetCount;
}

// Write: Not implemented:  any writes must be done through IOCTLs
DWORD BQD_Write( DWORD hOpenContext, LPCVOID pBuffer, DWORD Count )
{
  BQ_DBGPrint(L"BQDrv - BQD_Write\n");

  BQ_DBGPrint(L"BQDrv - BQD_Write Not Implemented!\n");

  BQ_DBGPrint(L"BQDrv - ~ BQD_Write\n");

return -1;
}


// Seek: Not implemented
DWORD BQD_Seek( DWORD hOpenContext, long Amount, WORD Type )
{
  BQ_DBGPrint(L"BQDrv - BQD_Seek\n");

  BQ_DBGPrint(L"BQDrv - ~ BQD_Seek\n");

return 0;
}

//Debug Print: only print if we have the debug flag set.
inline void BQ_DBGPrint(PTCHAR buf)
{
#ifdef BQ_DEBUG
  OutputDebugString(buf);
#endif /* BQ_DEBUG */
}


