⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hardwarebuffer.cpp

📁 WinCE 3.0 BSP, 包含Inter SA1110, Intel_815E, Advantech_PCM9574 等
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// HardwareBuffer.cpp: implementation of the CHardwareBuffer class.
//
//////////////////////////////////////////////////////////////////////

#include "includes.h"
#include "HardwareBuffer.h"
//#define DO_LOG
#include "logger.h"

const int MAX_ENTRY = 2048;	// use smaller chunks to improve precision

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

CHardwareBuffer::CHardwareBuffer(HRESULT &hr, UINT iIndex, volatile struct BusMasterRegisters* pRegisters):
	m_bIsRunning(false),
	m_pDsPositionNotify(NULL),
	m_uNumberOfNotifications(0),
	m_ulLastOffset(0),
	m_pRegisters(pRegisters),
	m_uChannel(iIndex),
	m_dwLastAddr(0)
{
	ASSERT(pRegisters);
	hr = DS_OK;
	// Do initialization stuff here.
	m_pCX5530 = CCX5530Audio::GetCX5530AudioObject();

	if (!m_pCX5530)
	{
		ERRMSG("CHardwareBuffer::CHardwareBuffer can't get hardware object!");
		hr = DSERR_GENERIC;
		return;
	}

	if (!m_SGDTable.ReservePhysicalMem(DMA_TABLE_SIZE))
	{
		ERRMSG("CHardwareBuffer::CHardwareBuffer: SGD Table ReservePhysicalMem() fails!");
		hr = DSERR_OUTOFMEMORY;
		return;
	}
	if (!m_SGDTable.IsContiguous())
	{
		ERRMSG("CHardwareBuffer::CHardwareBuffer didn't allocate contiguous memory!");
		hr = DSERR_OUTOFMEMORY;
		return;
	}

	memset(&m_wfxFormat, 0, sizeof(WAVEFORMATEX));
	InitializeCriticalSection(&m_csBuffer);
	InitializeLog();

}

CHardwareBuffer::~CHardwareBuffer()
{
	FUNCMSG("+CHardwareBuffer::~CHardwareBuffer");
	
	if(m_pDsPositionNotify) 
		delete [] m_pDsPositionNotify;
	m_pDsPositionNotify = NULL;
	DeleteCriticalSection(&m_csBuffer);
	FUNCMSG("-CHardwareBuffer::~CHardwareBuffer");
}

//////////////////////////////////////////////////////////////////////////////
/*****************************************************************************

SetNotificationPositions is a method on the IDsPlaybackDriverNotify interface.
If your buffer supports this interfaces (in other words, if your hardware
provides support for alerting that play position has passed a certain point)
you need to implement this method.

*****************************************************************************/
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CHardwareBuffer::SetNotificationPositions(
	/* [in] */ DWORD dwCount,
	/* [in] */ LPCDSBPOSITIONNOTIFY pNotify)
{
    FUNCMSG2("CHardwareBuffer::SetNotificationPositions %d, 0x%8.8x", dwCount, pNotify);
	HRESULT hr = DS_OK;

	if (m_bIsRunning)
	{
		// you're not allowed to set notification positions when playing
		INFMSG("CHardwareBuffer::SetNotificationPositions: buffer is playing");
		return DSERR_INVALIDCALL;
	}

	// Do whatever you need to do to store the array of positions and the 
	// associated event for each position.

	if( (dwCount == 0) != (pNotify == NULL)) 
	{
		// they have to both be zero or both be non-zero
		ERRMSG2("CHardwareBuffer::SetNotificationPositions: bad parms: %d, 0x%x", dwCount, pNotify);
		return DSERR_INVALIDPARAM;
	}
	if (m_pDsPositionNotify != NULL) 
	{
		delete [] m_pDsPositionNotify;
		m_pDsPositionNotify = NULL;
		m_uNumberOfNotifications = 0;
	}
	m_hEventStop = NULL;

	if (dwCount == 0) 
	{
		// caller just wanted to clear existing notifications. We're set.
		return DS_OK;
	}

	m_uNumberOfNotifications = (USHORT)dwCount;
	if (pNotify[dwCount-1].dwOffset == DSBPN_OFFSETSTOP) 
	{
		INFMSG1("DSBPN_OFFSETSTOP found in position %d", dwCount-1);
		// copy out the last entry as a special case for Stop()
		m_hEventStop = pNotify[dwCount-1].hEventNotify;
		m_uNumberOfNotifications--;
	}
	// might just want STOP notifications, in which case, we're done
	if (m_uNumberOfNotifications == 0) 
	{
		return DS_OK;
	}

	m_pDsPositionNotify = new DSBPOSITIONNOTIFY [m_uNumberOfNotifications];
	if (!m_pDsPositionNotify) 
	{
		m_uNumberOfNotifications = 0;
		return DSERR_OUTOFMEMORY;
	}

	for (USHORT i = 0; i < m_uNumberOfNotifications; i++) 
	{
		m_pDsPositionNotify[i].hEventNotify = pNotify[i].hEventNotify;
		//m_pDsPositionNotify[i].dwOffset =( pNotify[i].dwOffset - ((i == 0)?0:(pNotify[i-1].dwOffset)) & (~3L));
		m_pDsPositionNotify[i].dwOffset = pNotify[i].dwOffset & (~0xF);	// force a multiple of 16 (for the CX5530)
		INFMSG2("\tm_pDsPositionNotify[%d].dwOffset = %d", i, m_pDsPositionNotify[i].dwOffset);
	}

	return hr;
}


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

	Function:

	SetDMABuffer

	Description:

	The functions sets up the DMA channel scatter-gather table; the table is set up as a
	set of consecutive entries, each pointing to a contiguous physical memory area; 
	the areas are computed based on the following criteria:

	- the actual physical memory layout: the CMemoryBuffer class reserves a buffer of
	virtual memory as an array of contiguous physical chunks; an area may not cross a 
	chunk boundary

	- the notification offsets from DirectX: the driver uses the SGD_EOP flag to generate 
	interrupts when an area has been played. Mostly what happens is that notification
	offset splits a chunk into two SGD table entries, so that each one can be flagged 
	with SGD_EOP. The entry after the last area is flagged with JUMP.

	- the minimum area size: If an area is too small, the corresponding interrupts 
	will be lost. The solution is to bundle two or more notification positions in a 
	single area: for example, DirectX may request notifications for offsets of 100 and 
	110 bytes; the driver won't be able to process interrupts for every ten bytes; 
	in this case, the driver will set one area, after say 110 bytes	and signal both 
	the 100 and the 110 events then.

	The notification offsets requested by DirectX sizes are passed as arguments.

	Arguments: none

	Return Value: bool
					  
-------------------------------------------------------------------*/
void CHardwareBuffer::SetDMABuffer()
{
	UINT uCurrentChunk = 0;	// counter into the buffer chunk array
	USHORT uCurrentNotification = 0;	// counter into the notification array
	UINT uSGDCounter = 0;	// counter into the SGD table
	
	ULONG ulCurrentPhysicalOffset = 0;
	ULONG ulCurrentPhysicalAddress;
	ULONG ulNotificationAvailable;
	ULONG ulPhysicalAvailable;
	ULONG ulCurrentEntrySize = 0;

	FUNCMSG1("+CHardwareBuffer::SetDMABuffer() for %d", GetSize());
	
	if (!m_pDsPositionNotify || m_uNumberOfNotifications < 1)
	{
		ulNotificationAvailable = GetSize()+ 1;
	}
	else
	{
		ulNotificationAvailable = (m_pDsPositionNotify[0].dwOffset);
	}
	ulCurrentPhysicalAddress = GetChunkAddress(uCurrentChunk);
	ulPhysicalAvailable = GetChunkSize(uCurrentChunk);
	
	PSGD_FORMAT pSGDEntry = (PSGD_FORMAT)m_SGDTable.GetVirtualAddress();
	INFMSG1("CHardwareBuffer::SetDMABuffer: DMA table is at 0x%8.8x", pSGDEntry);
	
	ULONG ulMinAreaSize = m_wfxFormat.nAvgBytesPerSec * MIN_INTERRUPT_INTERVAL / 1000L;
	ULONG ulLastInterrupt = 0;
	INFMSG1("ulMinAreaSize is %d", ulMinAreaSize);
	while (true)
	{
		INFMSG2("ulNotificationAvailable = %d, ulPhysicalAvailable = %d", ulNotificationAvailable , ulPhysicalAvailable);
		
		// is the SGD table break determined by the next notification, or the next chunk boundary?
		
		if (ulNotificationAvailable + ulMinAreaSize >= ulPhysicalAvailable && MAX_ENTRY + ulMinAreaSize >= ulPhysicalAvailable)
		{
			// the next chunk boundary; this is not of interest to DirectX, 
			// so we do not need to flag the SGD table entry
			INFMSG2("Writing 0x%8.8x + %d", GetChunkAddress(uCurrentChunk), ulCurrentPhysicalOffset);
			pSGDEntry->PhysicalAddr = GetChunkAddress(uCurrentChunk) + (ulCurrentPhysicalOffset);
			
			ulCurrentEntrySize = ulPhysicalAvailable;
			uCurrentChunk++;
			INFMSG2("Chunk boundary: chunk is %d, notif is %d", uCurrentChunk, uCurrentNotification);
			if (uCurrentChunk >= GetNumberOfChunks())
			{
				INFMSG1("No more chunks! Writing size %d", ulCurrentEntrySize);
				pSGDEntry->CountAndFlag = ulCurrentEntrySize | SGD_EOP;
				break;
			}
			ulCurrentPhysicalOffset = 0;
			ulNotificationAvailable -= ulCurrentEntrySize;
			ulPhysicalAvailable = GetChunkSize(uCurrentChunk);
			INFMSG1("Writing size %d", ulCurrentEntrySize );
			pSGDEntry->CountAndFlag = ulCurrentEntrySize | SGD_EOP;
			pSGDEntry++;
		}
		else if (MAX_ENTRY + ulMinAreaSize > ulNotificationAvailable)
		{
			// notif boundary; is it acceptable?
			INFMSG2("Checking boundary: ulLastInterrupt = %d, m_pDsPositionNotify[uCurrentNotification].dwOffset = %d",
				ulLastInterrupt, m_pDsPositionNotify[uCurrentNotification].dwOffset);
			
			m_pDsPositionNotify[uCurrentNotification].dwOffset &= ~0x0F;	// ensure a 16 bit boundary (undocumented, but doesn't work otherwise)
			
			// first, it must be further than ulMinAreaSize from the last interrupt
			if ((m_pDsPositionNotify[uCurrentNotification].dwOffset- ulLastInterrupt) < ulMinAreaSize||
				// second, it must be further than ulMinAreaSize from the EOL interrupt (at the end of the buffer)
				(GetSize() - m_pDsPositionNotify[uCurrentNotification].dwOffset) < ulMinAreaSize )
			{
				//refuse interrupt
				INFMSG("Skipping notification");
				uCurrentNotification++;
				if (uCurrentNotification < m_uNumberOfNotifications)
				{
					ulNotificationAvailable  += (m_pDsPositionNotify[uCurrentNotification].dwOffset - m_pDsPositionNotify[uCurrentNotification-1].dwOffset);
				}
				else
				{
					ulNotificationAvailable  = GetSize()+ 1;
				}
			}
			else
			{
				INFMSG2("Writing 0x%8.8x + %d", GetChunkAddress(uCurrentChunk), ulCurrentPhysicalOffset);
				pSGDEntry->PhysicalAddr = GetChunkAddress(uCurrentChunk) + (ulCurrentPhysicalOffset);
				
				ulCurrentEntrySize = ulNotificationAvailable;
				ulCurrentPhysicalOffset += ulCurrentEntrySize;
				ulPhysicalAvailable -= ulCurrentEntrySize;
				uCurrentNotification++;
				INFMSG2("Notif boundary: chunk is %d, notif is %d", uCurrentChunk, uCurrentNotification);
				if (uCurrentNotification < m_uNumberOfNotifications)
				{
					ulNotificationAvailable  = m_pDsPositionNotify[uCurrentNotification].dwOffset - m_pDsPositionNotify[uCurrentNotification-1].dwOffset;
				}
				else
				{
					// the notifications vector ends before the end of the buffer; 
					// to ensure proper terminations set the next 
					// notification boundary to something guaranteed beyond the end of the last chunk
					INFMSG("Notifications done");
					ulNotificationAvailable  = GetSize()+ 1;
				}
				ulLastInterrupt += ulCurrentEntrySize;
				INFMSG1("Writing size %d", ulCurrentEntrySize );
				// the next notification: flag the entry with SGD_EOP
				pSGDEntry->CountAndFlag = ulCurrentEntrySize | SGD_EOP;
				pSGDEntry++;
			}
		}
		else
		{	// MAX_ENTRY breakpoint; this is not of interest to DirectX, 
			// so we do not need to flag the SGD table entry
			INFMSG2("Writing 0x%8.8x + %d", GetChunkAddress(uCurrentChunk), ulCurrentPhysicalOffset);
			pSGDEntry->PhysicalAddr = GetChunkAddress(uCurrentChunk) + (ulCurrentPhysicalOffset);
			
			ulCurrentEntrySize = MAX_ENTRY;
			INFMSG2("MAX_ENTRY boundary: chunk is %d, notif is %d", uCurrentChunk, uCurrentNotification);
			ulCurrentPhysicalOffset += ulCurrentEntrySize;
			ulNotificationAvailable -= ulCurrentEntrySize;
			ulPhysicalAvailable -= ulCurrentEntrySize;
			INFMSG1("Writing size %d", ulCurrentEntrySize );
			pSGDEntry->CountAndFlag = ulCurrentEntrySize | SGD_EOP;
			pSGDEntry++;
		}
	}
	
	// close the loop: add an entry that points back to the beginning of the table
	pSGDEntry++;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -