📄 hwctxt.cpp
字号:
else
{
m_InputDMAStatus &= ~DMA_DONEB;
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
DEBUGMSG(ZONE_ERROR, (TEXT("WAVDEV2.DLL:TransferInputBuffer() - ")
TEXT("EXCEPTION: %d"), GetExceptionCode()));
}
return BytesTransferred;
}
//-----------------------------------------------------------------------------
//
// Function: TransferInputBuffers
//
// This function determines which DMA input buffer (A and/or B) needs to be
// transferred to the audio application-supplied data buffers. The DMA input
// buffer can be reused once the data has been copied out.
//
// If there is insufficient space in the application-supplied data buffers to
// transfer out all of the data that is currently in the DMA input buffers,
// then all excess data is simply discarded.
//
// Parameters:
// dwDCSR
// [in] Current status of the input DMA.
// checkFirst
// [in] Identifies which of the two available DMA input buffers
// should be transferred first in order to maintain the
// correct sequencing of the input audio stream.
//
// Returns:
// Returns number of bytes placed into the input buffers.
//
//-----------------------------------------------------------------------------
ULONG HardwareContext::TransferInputBuffers(DWORD dwDCSR, DWORD checkFirst)
{
ULONG BytesTransferred=0;
if (checkFirst == IN_BUFFER_A)
{
if (dwDCSR & DMA_DONEA)
{
BytesTransferred = TransferInputBuffer(IN_BUFFER_A);
}
if (dwDCSR & DMA_DONEB)
{
BytesTransferred += TransferInputBuffer(IN_BUFFER_B);
}
}
else // (checkFirst == IN_BUFFER_B)
{
if (dwDCSR & DMA_DONEB)
{
BytesTransferred = TransferInputBuffer(IN_BUFFER_B);
}
if (dwDCSR & DMA_DONEA)
{
BytesTransferred += TransferInputBuffer(IN_BUFFER_A);
}
}
return BytesTransferred;
}
#endif // #ifdef AUDIO_RECORDING_ENABLED
//-----------------------------------------------------------------------------
//
// Function: InterruptThread
//
// This function is the IST for handling audio input and output DMA interrupts.
//
// Parameters:
// None.
//
// Returns:
// None.
//
//-----------------------------------------------------------------------------
void HardwareContext::InterruptThread()
{
#ifdef AUDIO_RECORDING_ENABLED
DWORD recCheckFirst = IN_BUFFER_A;
#endif
DWORD dwRet = 0;
UINT32 bufDescStatus[NUM_DMA_BUFFERS];
DEBUGMSG(ZONE_FUNCTION,(_T("+HardwareContext::InterruptThread\n")));
// Fast way to access embedded pointers in wave headers in other processes.
SetProcPermissions((DWORD)-1);
while (TRUE)
{
// Wait for the IST to be signaled by the OAL interrupt handler.
WaitForSingleObject(m_hAudioInterrupt, INFINITE);
// Grab the lock.
Lock();
__try
{
// Handle DMA output (i.e., audio playback) if it is active.
if (m_OutputDMARunning)
{
PCSP_SSI_REG pStDACSSI = BSPAudioGetStDACSSI();
DWORD ssi_sisr = INREG32(&pStDACSSI->SISR);
if (ssi_sisr & CSP_BITFMASK(SSI_SISR_ROE0))
{
// Don't expect to see this ever if the SSI is used only
// for audio playback to the PMIC Stereo DAC.
ERRORMSG(ZONE_ERROR, (_T("ERROR: SSI%d RX overrun ")
_T("error\n"),
(pStDACSSI == m_pSSI1 ? 1 : 2)));
}
// Get the transfer status of the DMA output buffers.
if (!DDKSdmaGetChainStatus(m_OutputDMAChan, bufDescStatus))
{
ERRORMSG(ZONE_ERROR,(_T("Could not retrieve output buffer ")
_T("status\r\n")));
}
if (bufDescStatus[OUT_BUFFER_A] & DDK_DMA_FLAGS_ERROR)
{
ERRORMSG(ZONE_ERROR,(_T("ERROR: Error flag set in ")
_T("OUT_BUFFER_A descriptor\r\n")));
}
if (bufDescStatus[OUT_BUFFER_B] & DDK_DMA_FLAGS_ERROR)
{
ERRORMSG(ZONE_ERROR,(_T("ERROR: Error flag set in ")
_T("OUT_BUFFER_B descriptor\r\n")));
}
// Set DMA status bits according to retrieved transfer status.
if (!(bufDescStatus[OUT_BUFFER_A] & DDK_DMA_FLAGS_BUSY))
{
// The data in DMA buffer A has been transmitted and the
// buffer may now be reused.
m_OutputDMAStatus |= DMA_DONEA;
DDKSdmaClearBufDescStatus(m_OutputDMAChan, OUT_BUFFER_A);
}
if (!(bufDescStatus[OUT_BUFFER_B] & DDK_DMA_FLAGS_BUSY))
{
// The data in DMA buffer B has been transmitted and the
// buffer may now be reused.
m_OutputDMAStatus |= DMA_DONEB;
DDKSdmaClearBufDescStatus(m_OutputDMAChan, OUT_BUFFER_B);
}
// Try to refill any empty output DMA buffers with new data
// and then start another DMA output operation only if at
// least one empty buffer was refilled.
if (TransferOutputBuffers(m_OutputDMAStatus))
{
// Check for SSI transmitter underrun errors. These are
// bad and can result in unpredictable swapping of the
// left/right audio channels.
if (ssi_sisr & CSP_BITFMASK(SSI_SISR_TUE0))
{
ERRORMSG(ZONE_ERROR, (_T("ERROR: SSI%d TX underrun ")
_T("error\n"),
(pStDACSSI == m_pSSI1 ? 1 : 2)));
}
// SDMA will disable itself on buffer underrun.
// Force channel to be enabled since we should now
// have a buffer ready
DDKSdmaStartChan(m_OutputDMAChan);
}
else if ((m_OutputDMAStatus & DMA_DONEA) &&
(m_OutputDMAStatus & DMA_DONEB))
{
// The upper audio driver layer has no more data, so we can
// terminate the current DMA operation because all DMA
// buffers are empty.
//
// Otherwise, wait until the currently still in-progress
// DMA operation has been completed (and we get another DMA
// event interrupt) before stopping the DMA.
//
StopOutputDMA();
}
}
#ifdef AUDIO_RECORDING_ENABLED
// Handle DMA input (i.e., audio recording) if it is active.
if (m_InputDMARunning)
{
PCSP_SSI_REG pVCodecSSI = BSPAudioGetVCodecSSI();
DWORD ssi_sisr = INREG32(&pVCodecSSI->SISR);
if (ssi_sisr & CSP_BITFMASK(SSI_SISR_TUE0))
{
// Don't expect to see this ever if the SSI is used only
// for audio recording using the PMIC Voice CODEC.
ERRORMSG(ZONE_ERROR, (_T("ERROR: SSI%d TX underrun ")
_T("error\n"),
(pVCodecSSI == m_pSSI1 ? 1 : 2)));
}
// Get the transfer status of the DMA input buffers.
if (!DDKSdmaGetChainStatus(m_InputDMAChan, bufDescStatus))
{
ERRORMSG(ZONE_ERROR,(_T("Could not retrieve input buffer ")
_T("status\r\n")));
}
if (bufDescStatus[IN_BUFFER_A] & DDK_DMA_FLAGS_ERROR)
{
ERRORMSG(ZONE_ERROR,(_T("ERROR: Error flag set in ")
_T("IN_BUFFER_A\r\n")));
}
if (bufDescStatus[IN_BUFFER_B] & DDK_DMA_FLAGS_ERROR)
{
ERRORMSG(ZONE_ERROR,(_T("ERROR: Error flag set in ")
_T("IN_BUFFER_B\r\n")));
}
// Set DMA status bits according to retrieved transfer status.
if (!(bufDescStatus[IN_BUFFER_A] & DDK_DMA_FLAGS_BUSY))
{
// Mark input buffer A as having new audio data.
m_InputDMAStatus |= DMA_DONEA;
DDKSdmaClearBufDescStatus(m_InputDMAChan, IN_BUFFER_A);
}
if (!(bufDescStatus[IN_BUFFER_B] & DDK_DMA_FLAGS_BUSY))
{
// Mark input buffer B as having new audio data.
m_InputDMAStatus |= DMA_DONEB;
DDKSdmaClearBufDescStatus(m_InputDMAChan, IN_BUFFER_B);
}
// Try to empty out the input DMA buffers by copying the data
// up to the application-supplied data buffers.
//
// Note that we require the higher-level audio application
// to provide enough data buffers to transfer all of the new
// data from the input DMA buffers. Otherwise, we will be
// forced to discard the data and just continue recording.
TransferInputBuffers(m_InputDMAStatus, recCheckFirst);
// Update the recCheckFirst flag to mark the first input DMA
// buffer that should be transferred in order to maintain the
// correct ABABABAB... input DMA buffer sequencing.
//
// Note that except for the 2 cases that we check for below, we
// do not need to change the current value of the recCheckFirst
// flag. Here are some pseudocode blocks to explain why:
//
// if ((recCheckFirst == IN_BUFFER_A) &&
// (m_InputDMAStatus & DMA_DONEA) &&
// (m_InputDMAStatus & DMA_DONEB))
// {
// // This means that both input DMA buffers A and
// // B were full and that the data was copied out
// // during the handling of this interrupt. So we
// // can just continue the input DMA operation in
// // the same state.
// }
// if ((recCheckFirst == IN_BUFFER_A) &&
// !(m_InputDMAStatus & DMA_DONEA) &&
// !(m_InputDMAStatus & DMA_DONEB))
// {
// // This is an invalid or error condition that
// // should never happen because it indicates that
// // we got an input DMA interrupt but neither
// // input buffer was full.
// }
// if ((recCheckFirst == IN_BUFFER_A) &&
// !(m_InputDMAStatus & DMA_DONEA) &&
// (m_InputDMAStatus & DMA_DONEB))
// {
// // This is an invalid or error condition that
// // should never happen because it indicates that
// // we've lost our expected ABABABAB... buffer
// // sequencing.
// }
// if ((recCheckFirst == IN_BUFFER_A) &&
// (m_InputDMAStatus & DMA_DONEA) &&
// !(m_InputDMAStatus & DMA_DONEB))
// {
// // This is the only scenario that we need to
// // check for (see the code below).
// }
//
// The same logic can be applied for the cases where the
// recCheckFirst flag is equal to IN_BUFFER_B.
if ((recCheckFirst == IN_BUFFER_A) &&
(m_InputDMAStatus & DMA_DONEA) &&
!(m_InputDMAStatus & DMA_DONEB))
{
// Input DMA buffer A was full and just transferred but
// input DMA buffer B was not yet full so we must check
// it first when handling the next input DMA interrupt.
recCheckFirst = IN_BUFFER_B;
}
else if ((recCheckFirst == IN_BUFFER_B) &&
(m_InputDMAStatus & DMA_DONEB) &&
!(m_InputDMAStatus & DMA_DONEA))
{
// Input DMA buffer B was full and just transferred but
// input DMA buffer A was not yet full so we must check
// it first when handling the next input DMA interrupt.
recCheckFirst = IN_BUFFER_A;
}
// Check for SSI receiver overrun errors. These are
// bad and can result in unpredictable swapping of the
// left/right audio channels.
if (ssi_sisr & CSP_BITFMASK(SSI_SISR_ROE0))
{
ERRORMSG(ZONE_ERROR, (_T("ERROR: SSI%d RX overrun ")
_T("error\n"),
(pVCodecSSI == m_pSSI1 ? 1 : 2)));
}
// Force the input DMA channel to be enabled again since we
// now have at least one input DMA buffer that's ready to be
// used.
DDKSdmaStartChan(m_InputDMAChan);
}
#endif // #ifdef AUDIO_RECORDING_ENABLED
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
DEBUGMSG(ZONE_ERROR, (TEXT("WAVDEV2.DLL:InterruptThread() - ")
TEXT("EXCEPTION: %d"),
GetExceptionCode()));
}
// No need to call InterruptDone since we are using SDMA interrupts
Unlock();
}
CloseHandle(m_hAudioInterrupt);
m_hAudioInterrupt = NULL;
DEBUGMSG(ZONE_FUNCTION,(_T("-HardwareContext::InterruptThread\n")));
}
//-----------------------------------------------------------------------------
//
// Function: CallInterruptThread
//
// This function serves as a wrapper for the IST called within the hardware
// context.
//
// Parameters:
// None.
//
// Returns:
// None.
//
//-----------------------------------------------------------------------------
void CallInterruptThread(HardwareContext *pHWContext)
{
if (pHWContext != NULL)
{
pHWContext->InterruptThread();
}
else
{
DEBUGMSG(ZONE_ERROR, (TEXT("CallInterruptThread() called with ")
TEXT("pHWContext == NULL\n")));
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -