// VCanDrv.cpp: implementation of the CVCanDrv class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "VCanDrv.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

// setting last error
#define SUCC(expr) ((m_lastError=(expr)) == VSUCCESS)

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CVCanDrv::CVCanDrv()
{
	m_hPort = -1;
	m_chMask = 0;
	m_chGranted = 0;
	m_rxNotifyEv = NULL;
	m_rxEvOwner = FALSE;
	m_lastError = VSUCCESS;
}

CVCanDrv::~CVCanDrv()
{
	ClosePort();
}

BOOL CVCanDrv::OpenDriver()
{
	return ncdOpenDriver() == VSUCCESS;
}

BOOL CVCanDrv::CloseDriver()
{
	return ncdCloseDriver() == VSUCCESS;
}

BOOL CVCanDrv::OpenPort(LPCSTR userName, int channel, int rxQueueSize)
{
	m_chMask = 1 << channel;
	
	return SUCC(ncdOpenPort(&m_hPort, (char *) userName, m_chMask, m_chMask, &m_chGranted, rxQueueSize));
}

void CVCanDrv::ClosePort()
{
	if(m_hPort >= 0)
	{
		ncdDeactivateChannel(m_hPort, m_chMask);
		ncdClosePort(m_hPort);
		m_hPort = -1;
	}

	CloseRxEvent();
}

void CVCanDrv::CloseRxEvent()
{
	if(m_rxNotifyEv)
	{
		HANDLE nullHandle = NULL;

		// unbind from vcand first
		if(m_hPort >= 0)
			ncdSetNotification(m_hPort, (DWORD*) &nullHandle, 0);

		// do we own the handle ?
		if(m_rxEvOwner)
			CloseHandle(m_rxNotifyEv);

		m_rxNotifyEv = NULL;
		m_rxEvOwner = FALSE;
	}
}

BOOL CVCanDrv::SetBitrate(DWORD bitRate)
{
	return SUCC(ncdSetChannelBitrate(m_hPort, m_chMask, bitRate));
}

BOOL CVCanDrv::SetChannelMode(BOOL tx, BOOL txrq)
{
	return SUCC(ncdSetChannelMode(m_hPort, m_chMask, tx, txrq));
}

BOOL CVCanDrv::SetReceiveMode(BOOL errFrame, BOOL chipState)
{
	return SUCC(ncdSetReceiveMode(m_hPort, errFrame, chipState));
}

BOOL CVCanDrv::SetChannelAcceptance(DWORD id, DWORD mask)
{
	VsetAcceptance acc;

	acc.mask = (id & 0x80000000) ? ((mask & 0x1fffffff) | 0x80000000) : (mask & 0x7ff);
	acc.code = id;

	return SUCC(ncdSetChannelAcceptance(m_hPort, m_chMask, &acc));
}

BOOL CVCanDrv::ActivateChannel()
{
	return SUCC(ncdActivateChannel(m_hPort, m_chMask));
}

BOOL CVCanDrv::DeactivateChannel()
{
	return SUCC(ncdDeactivateChannel(m_hPort, m_chMask));
}

BOOL CVCanDrv::Transmit(DWORD id, void* data, int length, BYTE flags)
{
	VCAN_EVENT tx;

	tx.tag = V_TRANSMIT_MSG;
	tx.tagData.msg.id = id;
	tx.tagData.msg.flags = flags;
	memcpy(tx.tagData.msg.data, data, length);
	tx.tagData.msg.dlc = length;

	return SUCC(ncdTransmit(m_hPort, m_chMask, &tx));
}

BOOL CVCanDrv::SetNotification(int queueLevel, HANDLE hEvent, BOOL bAutoClose)
{
	// close previous event (if any)
	CloseRxEvent();

	// self create ?
	if(!hEvent)
	{
		hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
		bAutoClose = TRUE;
	}

	m_rxNotifyEv = hEvent;
	m_rxEvOwner = bAutoClose;

	return SUCC(ncdSetNotification(m_hPort, (DWORD*) &m_rxNotifyEv, queueLevel));
}

DWORD CVCanDrv::WaitForNotification(DWORD dwMilliseconds)
{
	if(!m_rxNotifyEv)
		return WAIT_ABANDONED;

	return WaitForSingleObject(m_rxNotifyEv, dwMilliseconds);
}

int CVCanDrv::Receive(VCAN_EVENT* rxFrames, int rxCnt)
{
	if(!SUCC(ncdReceive(m_hPort, VCAN_POLL, NULL, &rxCnt, rxFrames)))
		return -1;

	return rxCnt;
}

int CVCanDrv::GetReceiveQueueLevel()
{
	int ret;

	return SUCC(ncdGetReceiveQueueLevel(m_hPort, &ret)) ? ret : -1;
}

BOOL CVCanDrv::FlushReceiveQueue()
{
	return SUCC(ncdFlushReceiveQueue(m_hPort));
}

BOOL CVCanDrv::FlushTransmitQueue()
{
	return SUCC(ncdFlushTransmitQueue(m_hPort, m_chMask));
}

BOOL CVCanDrv::SetChannelParams(VchipParams* pParams)
{
	return SUCC(ncdSetChannelParams(m_hPort, m_chMask, pParams));
}
