#include <initguid.h>

#include "CDmaChannel.h"
#include <winerror.h>


IDmaChannel* CreateCDmaChannel(ULONG size, PHYSICAL_ADDRESS highestAddress)
{
   return CDmaChannel::Create(size, highestAddress);
}

IDmaChannel* CDmaChannel::Create(ULONG size, PHYSICAL_ADDRESS highestAddress)
{
	PVOID buffer = MmAllocateContiguousMemory(size, highestAddress);
   
	if(buffer != NULL)
	{
		return new CDmaChannel(buffer, size);
	}
	else
	{
      KdPrint(("Allocating continguous memory failed\n"));
		return NULL;
	}
}

void* CDmaChannel::operator new(unsigned int n)
{
   void* mem = ExAllocatePoolWithTag(NonPagedPool, n, memTag);
   RtlZeroMemory(mem, n);
   return mem;
}

void CDmaChannel::operator delete(void* mem)
{
   if(mem != NULL)
   {
      ExFreePoolWithTag(mem, memTag);
   }
}

CDmaChannel::CDmaChannel(PVOID buffer, ULONG size)
:  buffer(buffer), allocatedSize(size), physicalBufstart(MmGetPhysicalAddress(buffer)),
   bufferSize(size), ref(1)
{
   KdPrint(("System address: %llx\n", (unsigned long long)buffer));
   KdPrint(("Physical address: %llx\n", physicalBufstart.QuadPart));
}

CDmaChannel::~CDmaChannel()
{
	FreeBuffer();
}

// stub since this function is not required by IMiniportWaveCyclic::NewStream
STDMETHODIMP_(NTSTATUS) CDmaChannel::AllocateBuffer(ULONG bufferSize, PPHYSICAL_ADDRESS constraint OPTIONAL)
{
	return STATUS_UNSUCCESSFUL;
}

STDMETHODIMP_(ULONG) CDmaChannel::AllocatedBufferSize()
{
	return allocatedSize;
}

STDMETHODIMP_(ULONG) CDmaChannel::BufferSize()
{
	return bufferSize;
}

// possible sanity checks: see if source is within the common buffer, check count
void CDmaChannel::CopyFrom(PVOID destination, PVOID source, ULONG count)
{
	if(buffer != NULL)
	{
		RtlCopyMemory(destination, source, count);
	}
}

// possible sanity checks: see if destination is within the common buffer, check count
void CDmaChannel::CopyTo(PVOID destination, PVOID source, ULONG count)
{
	if(buffer != NULL)
	{
		RtlCopyMemory(destination, source, count);
	}
}

void CDmaChannel::FreeBuffer()
{
	if(buffer != NULL)
	{
		MmFreeContiguousMemory(buffer);
		buffer = NULL;
	}
}

// stub; function is not required by NewStream and we do not have an adapter object anyways
STDMETHODIMP_(PADAPTER_OBJECT) CDmaChannel::GetAdapterObject()
{
	return NULL;
}

STDMETHODIMP_(ULONG) CDmaChannel::MaximumBufferSize()
{
	return allocatedSize;
}

STDMETHODIMP_(PHYSICAL_ADDRESS) CDmaChannel::PhysicalAddress()
{
	return physicalBufstart;
}

void CDmaChannel::SetBufferSize(ULONG size)
{
	bufferSize = size;
}

PVOID CDmaChannel::SystemAddress()
{
	return buffer;
}

// stub since it is not required by NewStream
STDMETHODIMP_(ULONG) CDmaChannel::TransferCount()
{
	return 0;
}

STDMETHODIMP_(HRESULT) CDmaChannel::QueryInterface(REFIID iid, void** obj)
{
   if(!obj) return E_POINTER;
   *obj = NULL;

	if(IsEqualGUIDAligned(iid, IID_IDmaChannel))
	{
		*obj = this;
	}
	else if(IsEqualGUIDAligned(iid, IID_IUnknown))
	{
		*obj = static_cast<IUnknown*>(this);
	}

   if(*obj)
   {
      ++ref;
      return S_OK;
   }
	else return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) CDmaChannel::AddRef()
{
	return ++ref;
}

STDMETHODIMP_(ULONG) CDmaChannel::Release()
{
	if(--ref == 0)
	{
		delete this;
		return 0;
	}
	else return ref;
}
