// DxbDebugQueue.cpp : implementation file
//

#include "stdafx.h"
#include "NtKgs.h"
#include "DxbDebugQueue.h"

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

#pragma message("NOTE: NOTE: firmware packing is assumed to be 4")
#ifndef uint32
#define uint32 DWORD
#endif
#ifndef uint16
#define uint16 WORD
#endif
#ifndef uint8
#define uint8 BYTE
#endif
#pragma pack(push,4)
#include "64011.h"			// Galileo part
#pragma pack(pop)

IMPLEMENT_DYNAMIC( CDxbDebugQueue, CObject )

CDxbDebugQueue::CDxbDebugQueue()
{
	m_fInitialized = FALSE;
	m_hDriver = NULL;
}

CDxbDebugQueue::~CDxbDebugQueue()
{
	DxbShutdown();
	if (NULL != m_hDriver)
		DxbDeviceClose(m_hDriver);
}

BOOL CDxbDebugQueue::Init(CString const DriverName)
{
	if (m_fInitialized)
		return FALSE;	// no re-init

	SHIM_STATUS Status;
	ASSERT(!DriverName.IsEmpty());
	ASSERT(NULL == m_hDriver);
	Status = DxbDeviceOpen( DriverName, &m_hDriver);
	m_fInitialized = (SHIM_STATUS_SUCCESS == Status);
	return m_fInitialized;
}

// Magic macro
#define INSANE(q) ((Queue.cq_base<0x100)||(Queue.cq_base>=0x100000)||(Queue.cq_size<16)||(Queue.cq_size>1024)||(Queue.cq_in>=Queue.cq_size)||(Queue.cq_out>=Queue.cq_size))

int CDxbDebugQueue::WriteData(void * const pData, DWORD const cbData)
{
	SHIM_STATUS Status;
	ASSERT(NULL != m_hDriver);
	if (!m_fInitialized)
		return FALSE;

	CQ_T	Queue;
	DWORD cbThisRequest;
	int BytesWritten = 0;

	// Read the queue structure header off the board
	Status = DxbKmeRead(	m_hDriver,
								CQ_CMD,
								KME_MEMORY_FLAG_ADAPTER_MEMORY,
								(char *)(&Queue),
								sizeof Queue,
								&cbThisRequest,
								DEFAULT_TIMEOUT_MS, NULL, NULL);
	if ( (SHIM_STATUS_SUCCESS != Status) || (sizeof Queue != cbThisRequest)/* || (INSANE(Queue))*/)
		return(-1);

	int SpaceLeft = Queue.cq_out - Queue.cq_in - 1;
	if (0 > SpaceLeft)
		SpaceLeft += Queue.cq_size;

	if ( SpaceLeft <= 0)
		return(0);	// gotta have space

	int LeftToWrite = ((int)cbData > SpaceLeft) ? SpaceLeft : (int)cbData;

	int SpaceB4Wrap = Queue.cq_size - Queue.cq_in;
	if ( (Queue.cq_out <= Queue.cq_in) && (LeftToWrite > SpaceB4Wrap) )
	{
		// Need to wrap, write the first block
		Status = DxbKmeWrite(	m_hDriver,
										(Queue.cq_base + Queue.cq_in),
										KME_MEMORY_FLAG_ADAPTER_MEMORY,
										pData,
										SpaceB4Wrap,
										&cbThisRequest,
										DEFAULT_TIMEOUT_MS, NULL, NULL);

		if ( (SHIM_STATUS_SUCCESS != Status) || ((int)cbThisRequest != SpaceB4Wrap) )
			return(-1);

		BytesWritten += cbThisRequest;
		LeftToWrite -= cbThisRequest;
		Queue.cq_in = 0;
	}

	if (LeftToWrite > 0)
	{
		Status = DxbKmeWrite(	m_hDriver,
										(Queue.cq_base + Queue.cq_in),
										KME_MEMORY_FLAG_ADAPTER_MEMORY,
										( ((unsigned char *)pData) + BytesWritten),
										LeftToWrite,
										&cbThisRequest,
										DEFAULT_TIMEOUT_MS, NULL, NULL);

		if ( (SHIM_STATUS_SUCCESS != Status) || (LeftToWrite != (int)cbThisRequest) )
			return(-1);

		BytesWritten += cbThisRequest ;
		Queue.cq_in += cbThisRequest ;
	
		if (Queue.cq_in == Queue.cq_size)
			Queue.cq_in = 0 ;
	}

	// Update the q ptrs on the board
	Status = DxbKmeWrite(	m_hDriver,
									CQ_CMD + offsetof(CQ_T, cq_in),
									KME_MEMORY_FLAG_ADAPTER_MEMORY,
									(char *)(&Queue.cq_in),
									sizeof Queue.cq_in,
									&cbThisRequest,
									DEFAULT_TIMEOUT_MS, NULL, NULL);
	if ( (SHIM_STATUS_SUCCESS != Status) || ((sizeof Queue.cq_in) != cbThisRequest) )
		return(-1) ;

	NotifyBoard();

	return BytesWritten;
}

int CDxbDebugQueue::ReadData(void * const pData, DWORD const cbData)
{
	if (!m_fInitialized)
		return FALSE;

	SHIM_STATUS Status;
	ASSERT(NULL != m_hDriver);
	if (!m_fInitialized)
		return FALSE;

	CQ_T	Queue;
	DWORD cbThisRequest;
	int BytesRead = 0;

//#define MAX_READ 128
	int	LeftToRead = /* (MAX_READ < cbData) ? MAX_READ : */ cbData ;

	// Read the queue structure header off the board
	Status = DxbKmeRead(	m_hDriver,
								CQ_RESP,
								KME_MEMORY_FLAG_ADAPTER_MEMORY,
								(char *)(&Queue),
								sizeof Queue,
								&cbThisRequest,
								DEFAULT_TIMEOUT_MS, NULL, NULL);
	if ( (SHIM_STATUS_SUCCESS != Status) || (sizeof Queue != cbThisRequest) /* || (INSANE(Queue))*/ )
		return(-1);

	if (Queue.cq_in == Queue.cq_out)
		return(0);	// nothing to do

	int SpaceB4Wrap = Queue.cq_size - Queue.cq_out;
	if ( (Queue.cq_out > Queue.cq_in) && (LeftToRead > SpaceB4Wrap) )
	{
		Status = DxbKmeRead(	m_hDriver,
									(Queue.cq_base + Queue.cq_out),
									KME_MEMORY_FLAG_ADAPTER_MEMORY,
									pData,
									SpaceB4Wrap,
									&cbThisRequest,
									DEFAULT_TIMEOUT_MS, NULL, NULL);
		if ( (SHIM_STATUS_SUCCESS != Status) || (SpaceB4Wrap != (int)cbThisRequest) )
			return(-1);
		
		BytesRead += cbThisRequest;
		LeftToRead -= cbThisRequest;
		Queue.cq_out = 0;
	}

	int cbInQueue = (Queue.cq_in - Queue.cq_out);
	if (cbInQueue > 0)
	{
		LeftToRead = (LeftToRead > cbInQueue) ? cbInQueue : LeftToRead;

		Status = DxbKmeRead(	m_hDriver,
									(Queue.cq_base + Queue.cq_out),
									KME_MEMORY_FLAG_ADAPTER_MEMORY,
									( ((unsigned char *)pData) + BytesRead),
									LeftToRead,
									&cbThisRequest,
									DEFAULT_TIMEOUT_MS, NULL, NULL);
		if ( (SHIM_STATUS_SUCCESS != Status) || (LeftToRead != (int)cbThisRequest) )
			return(-1);

		BytesRead += cbThisRequest ;
		Queue.cq_out += cbThisRequest ;
	}

	// Update the q ptrs on the board
	Status = DxbKmeWrite(	m_hDriver,
									CQ_RESP + offsetof(CQ_T, cq_out),
									KME_MEMORY_FLAG_ADAPTER_MEMORY,
									(char *)(&Queue.cq_out),
									sizeof Queue.cq_out,
									&cbThisRequest,
									DEFAULT_TIMEOUT_MS, NULL, NULL);
	if ( (SHIM_STATUS_SUCCESS != Status) || ((sizeof Queue.cq_out) != cbThisRequest) )
		return(-1) ;

	return BytesRead;
}

void CDxbDebugQueue::NotifyBoard(void)
{
	ASSERT(NULL != m_hDriver);
	if (!m_fInitialized)
	{
		TRACE("Call to NotifyBoard prior to initialization");
		return;
	}

	size_t Offset = offsetof(REG64011, intr_cause);
	uint32 intr_cause = ~INT_PCIINT0;

	SHIM_STATUS Status;
	DWORD cbThisRequest;
	Status = DxbKmeWrite(	m_hDriver,
									Offset,
									KME_MEMORY_FLAG_PCI_INTERFACE_CHIP,
									(char *)(&intr_cause),
									sizeof intr_cause,
									&cbThisRequest,
									DEFAULT_TIMEOUT_MS, NULL, NULL);

	if ( (SHIM_STATUS_SUCCESS != Status) || ((sizeof intr_cause) != cbThisRequest) )
	{
		TRACE("KME Write in NotifyBoard failed");
	}

	return;
}
