/***************************************************************************
 *
 * dma.c
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

/*
 * includes
 */

#include "ult_64.h"
#include "dma.h"
//#include "sram.h"

/*
 * defines
 */

#define MAX_DMA_CHANNELS	8
#define NUM_PI_MSGS		8

/*
 * typedefs
 */

/*
 * structures
 */

typedef struct _dmaChannel
{
	int			status;
	OSMesgQueue         	dmaMessageQ;
	OSMesg              	dmaMessageBuf;
	OSIoMesg            	dmaIOMessageBuf;
	OSPiHandle		*device;
	unsigned long		dest;
	unsigned long		src;
	unsigned long		len;
	int			dir;
} dmaChannel;

/*
 * globals
 */

int			lastActiveCount;
int			lastPendingCount;

// PI command queue
OSMesg			dmaMessages[NUM_PI_MSGS];
OSMesgQueue		dmaMessageQ;

// DMA message queues
dmaChannel		dmaChannels[MAX_DMA_CHANNELS];

OSPiHandle		*cartRomHandle;
//OSPiHandle		*sramHandle;

int			dmaStatus;

// DMA polling callback function
void			(*dmaPoll)(void)=NULL;

/*
 * function prototypes
 */

/*
 * code
 */


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

int DmaOpen(void)
{
	int i;

	/*
	 * start PI mgr
	 */

	osCreatePiManager((OSPri)OS_PRIORITY_PIMGR, &dmaMessageQ, dmaMessages,
			NUM_PI_MSGS);

	/*
	 * create DMA completion message queue
	 */

	for(i=0;i<MAX_DMA_CHANNELS;i++)
	{
		osCreateMesgQueue(&dmaChannels[i].dmaMessageQ,
		                  &dmaChannels[i].dmaMessageBuf,
				  1);
		dmaChannels[i].status=DMA_IDLE;
	}

	/*
	 * init cartridge ROM interface
	 */

	cartRomHandle=osCartRomInit();

	/*
	 * initialise SRAM handle
	 */

	//sramHandle=osSramInit();

	return(0);
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

int DmaStart(OSPiHandle *device,
             unsigned long dir,unsigned long devAddr,
             unsigned long ramAddr,unsigned long byteCount)
{
	int	rc;
	int	i;

	DmaProcess();

	// ensure other functions get a look-in
	if(dmaPoll) dmaPoll();

	/*
	 * find idle DMA channel
	 */

	for(i=0;i<MAX_DMA_CHANNELS;i++)
	{
		if(dmaChannels[i].status==DMA_IDLE) break;
	}
	if(i==MAX_DMA_CHANNELS) return(-2);

	/*
	 * kick off new DMA
	 */

	dmaChannels[i].dest=ramAddr;
	dmaChannels[i].src=devAddr;
	dmaChannels[i].len=byteCount;
	dmaChannels[i].device=device;
	dmaChannels[i].dir=dir;
	dmaChannels[i].dmaIOMessageBuf.hdr.pri=OS_MESG_PRI_NORMAL;
	dmaChannels[i].dmaIOMessageBuf.hdr.retQueue=&dmaChannels[i].dmaMessageQ;
	dmaChannels[i].dmaIOMessageBuf.dramAddr=(void *)dmaChannels[i].dest;
	dmaChannels[i].dmaIOMessageBuf.devAddr=dmaChannels[i].src;
	dmaChannels[i].dmaIOMessageBuf.size=dmaChannels[i].len;

	dmaChannels[i].status=DMA_PENDING;

	return(i);
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void DmaRelease(int chan)
{
	dmaChannels[chan].status=DMA_IDLE;
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

int DmaStatus(int channel)
{
	int	i,rc;

	// ensure other functions get a look-in
	if(dmaPoll) dmaPoll();

	// kick off pending DMAs if possible
	DmaProcess();

	// return value
	return(dmaChannels[channel].status);
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void DmaWait(int channel)
{
	int rc;

	while(1)
	{
		rc=DmaStatus(channel);
		if(rc==DMA_COMPLETE || rc==DMA_IDLE)
		{
			// ensure it's released
			DmaRelease(channel);

			return;
		}
	}
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void DmaProcess(void)
{
	int	i,rc;

	lastActiveCount=0;
	lastPendingCount=0;

	// process all channels... try and kick off pending DMA's
	for(i=0;i<MAX_DMA_CHANNELS;i++)
	{
		if(dmaChannels[i].status==DMA_ACTIVE)
		{
			// see if the current DMA is complete
			rc=osRecvMesg(&dmaChannels[i].dmaMessageQ,
	              		NULL,
		      		OS_MESG_NOBLOCK);
			if(!rc)
			{
				dmaChannels[i].status=DMA_COMPLETE;
			}
			else
			{
				lastActiveCount++;
				break;
			}
		}
		else if(dmaChannels[i].status==DMA_PENDING)
		{
			lastPendingCount++;

			dmaChannels[i].dmaIOMessageBuf.hdr.pri=OS_MESG_PRI_NORMAL;
			dmaChannels[i].dmaIOMessageBuf.hdr.retQueue=&dmaChannels[i].dmaMessageQ;
			dmaChannels[i].dmaIOMessageBuf.dramAddr=(void *)dmaChannels[i].dest;
			dmaChannels[i].dmaIOMessageBuf.devAddr=dmaChannels[i].src;
			dmaChannels[i].dmaIOMessageBuf.size=dmaChannels[i].len;
			rc=osEPiStartDma(dmaChannels[i].device,&dmaChannels[i].dmaIOMessageBuf,dmaChannels[i].dir);

			if(!rc)
			{
				dmaChannels[i].status=DMA_ACTIVE;
				break;
			}
		}
	}
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/



/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

