📄 hardwarebuffer.cpp
字号:
pSGDEntry->PhysicalAddr = m_SGDTable.GetPhysicalAddress();
// and flag it with SGD_JMP
pSGDEntry->CountAndFlag = SGD_JMP;
#ifdef DEBUG
INFMSG("DsCaptureDriverBuffer::SetDMAChannelBuffer: SGD table is:");
pSGDEntry = (PSGD_FORMAT)m_SGDTable.GetVirtualAddress();
while(true)
{
INFMSG3("\tAddr: 0x%8.8x, size %d, %s",
pSGDEntry->PhysicalAddr,
pSGDEntry->CountAndFlag & ~(SGD_EOP | SGD_EOT |SGD_JMP),
(pSGDEntry->CountAndFlag & SGD_EOP)?TEXT("SGD_EOP"):
(pSGDEntry->CountAndFlag & SGD_JMP)?TEXT("SGD_JMP"):
(pSGDEntry->CountAndFlag & SGD_EOT)?TEXT("SGD_EOT"):TEXT("no flag"));
if (pSGDEntry->CountAndFlag & (SGD_EOT | SGD_JMP))
break;
pSGDEntry++;
}
#endif
FUNCMSG1("-CHardwareBuffer::SetDMABuffer() for %d", GetSize());
}
#if 0
// old (without MAX_ENTRY)
{
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;
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 >= 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;
pSGDEntry++;
}
else
{
// 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++;
}
}
}
// close the loop: add an entry that points back to the beginning of the table
pSGDEntry++;
pSGDEntry->PhysicalAddr = m_SGDTable.GetPhysicalAddress();
// and flag it with SGD_JMP
pSGDEntry->CountAndFlag = SGD_JMP;
#ifdef DEBUG
INFMSG("DsCaptureDriverBuffer::SetDMAChannelBuffer: SGD table is:");
pSGDEntry = (PSGD_FORMAT)m_SGDTable.GetVirtualAddress();
while(true)
{
INFMSG3("\tAddr: 0x%8.8x, size %d, %s",
pSGDEntry->PhysicalAddr,
pSGDEntry->CountAndFlag & ~(SGD_EOP | SGD_EOT |SGD_JMP),
(pSGDEntry->CountAndFlag & SGD_EOP)?TEXT("SGD_EOP"):
(pSGDEntry->CountAndFlag & SGD_JMP)?TEXT("SGD_JMP"):
(pSGDEntry->CountAndFlag & SGD_EOT)?TEXT("SGD_EOT"):TEXT("no flag"));
if (pSGDEntry->CountAndFlag & (SGD_EOT | SGD_JMP))
break;
pSGDEntry++;
}
#endif
}
#endif
bool CHardwareBuffer::Allocate(ULONG uSize)
{
FUNCMSG1("CHardwareBuffer::Allocate(%lu)", uSize);
ASSERT( ! m_pVirtualAddress );
ASSERT( ! GetSize() );
// the Hardware Secondary Buffer is limited to 64K
// because the DMA channel can only address so much
if(uSize > 0x10000)
{
ERRMSG1("CHardwareBuffer::Allocate: size too large (%d)", uSize);
return false;
}
if(!ReservePhysicalMem(uSize))
{
ERRMSG("CHardwareBuffer::Allocate: can't reserve phys mem");
return false;
}
INFMSG3("CHardwareBuffer::Allocate: buf %d: size 0x%x locked; number of chunks: %d \r\n",
m_uChannel, GetSize(), GetNumberOfChunks());
return true;
}
void CHardwareBuffer::ProcessInterrupt(void)
{
//FUNCMSG("CHardwareBuffer::ProcessInterrupt");
DWORD dwAddr = m_pRegisters->regBusMasterPRD;
// synchronize offsets; when we get an interrupt we know where the DMA is
// what chunk are we in?
// the device automatically advances the pointer, i.e., the register points to the *next* entry
// note that we'll never actually get 0 as an entry, because the device spends very little time on the *last*
// SGD table entry. The last entry is not a data one, but only a placeholder for a jump
// flag, so the device reloads the beginning of the table and increments the
// sgd pointer immediately to point to entry numero uno.
int nEntry = (dwAddr - m_SGDTable.GetPhysicalAddress())/sizeof(SGD_FORMAT) - 1;
ULONG ulCurrentOffset = 0;
// the CX5530 doesn't allow us to read the DMA counter; in order to give a semblance
// of precision we use the DMA channel guesstimation procedure;
// we save in the channel the position where we are now
// When DirectSound calls GetDMAPosition the DMA channel will return this value
// or compute an approximate one at a later date
for (int i = 0; i < nEntry; i++)
{
ulCurrentOffset += (m_SGDTable.GetEntry(i)->CountAndFlag) & ~(SGD_EOT | SGD_EOP);
}
LOG('I', ulCurrentOffset, m_ulLastOffset);
// deal with notifications
if (m_uNumberOfNotifications > 0)
{
UINT i = 0;
//INFMSG2("DsPlaybackDriverBuffer::ProcessInterrupt(%d): position is %d", intsrc, ulCurrentOffset);
// the events might belong to another process, so set permissions long enough
// to set the event (s).
DWORD dwSavedPermissions = GetCurrentPermissions();
SetProcPermissions( DWORD (-1) );
// ulCurrentOffset += 64;
// if (ulCurrentOffset >= GetSize())
// ulCurrentOffset -= GetSize();
//set off notification events
if (m_ulLastOffset > ulCurrentOffset) // wraparound;
{
for (i = 0; i < m_uNumberOfNotifications; i++)
{
if (m_pDsPositionNotify[i].dwOffset <= (ulCurrentOffset ) || m_pDsPositionNotify[i].dwOffset > m_ulLastOffset)
{
SetEvent(m_pDsPositionNotify[i].hEventNotify);
LOG('W', ulCurrentOffset, m_pDsPositionNotify[i].dwOffset);
}
}
}
else
{
for (i = 0; i < m_uNumberOfNotifications; i++)
{
if (m_pDsPositionNotify[i].dwOffset <= (ulCurrentOffset) && m_pDsPositionNotify[i].dwOffset > m_ulLastOffset)
{
SetEvent(m_pDsPositionNotify[i].hEventNotify);
LOG('N', ulCurrentOffset, m_pDsPositionNotify[i].dwOffset);
}
}
}
SetProcPermissions( dwSavedPermissions );
}
m_ulLastOffset = ulCurrentOffset;
}
void CHardwareBuffer::StartDMAChannel(bool bCapture)
{
FUNCMSG1("+CHardwareBuffer::StartDMAChannel for %s", bCapture?TEXT("recording"):TEXT("playback"));
BYTE byStatus = m_pRegisters->regBusMasterStatus; // clear status
INFMSG1("Trigger Master DMA: Status is 0x%2.2x", byStatus);
m_dwLastAddr = m_pRegisters->regBusMasterPRD = m_SGDTable.GetPhysicalAddress();
m_ulLastOffset = 0;
m_pRegisters->regBusMasterCommand.byValue = 0x01 | (bCapture?CAPTURE_FLAG:0); // enable play DMA
m_bIsRunning = true;
}
void CHardwareBuffer::StopDMAChannel()
{
FUNCMSG("+CHardwareBuffer::StopDMAChannel");
// Terminate Master DMA.
// put an EOT in the DMA table
int nEntry = (m_pRegisters->regBusMasterPRD - m_SGDTable.GetPhysicalAddress())/sizeof(SGD_FORMAT);
PSGD_FORMAT pTable = (PSGD_FORMAT )m_SGDTable.GetVirtualAddress();
if (pTable[nEntry].CountAndFlag & SGD_JMP)
nEntry = 0;
pTable[nEntry].CountAndFlag = (pTable[nEntry].CountAndFlag & ~SGD_EOP)|SGD_EOT;
INFMSG1("Stopping entry nEntry is %d", nEntry);
}
DWORD CHardwareBuffer::GetDMAPosition()
{
//CriticalSectionAcquisition c(&m_csBuffer);
LOG('P', m_ulLastOffset, 0);
return m_ulLastOffset;
}
bool CHardwareBuffer::HasInterrupt()
{
DWORD dwAddr = m_pRegisters->regBusMasterPRD;
if (dwAddr != m_dwLastAddr)
{
// Ok, the PRD controller advanced; we assume we have an interrupt;
m_dwLastAddr = dwAddr;
return true;
}
return false;
}
bool CHardwareBuffer::IsStopInterrupt()
{
return (m_pRegisters->regBusMasterStatus & BUSMASTER_ERROR) != 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -