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

📄 ichwave.cpp

📁 AC97 Sample Driver and Related Code Samples. This directory contains a sample AC97 adapter driver a
💻 CPP
📖 第 1 页 / 共 4 页
字号:
 * This routine is called when new mappings are available from the port driver.
 * The routine places mappings into the input mapping queue. ICH can handle up
 * to 32 entries (descriptors). We program the DMA registers if we have at least
 * one mapping in the queue. The mapping spin lock must be held when calling
 * this routine.
 */
NTSTATUS CMiniportWaveICHStream::GetNewMappings (void)
{
    KIRQL OldIrql;
    
    NTSTATUS    ntStatus = STATUS_SUCCESS;
    ULONG       ulBytesMapped = 0;
    int         nInsertMappings = 0;
    int         nTail;                  // aut. variable

    DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::GetNewMappings]"));

    // acquire the mapping spin lock
    KeAcquireSpinLock (&MapLock,&OldIrql);

#if (DBG)
    if (Wave->AdapterCommon->ReadBMControlRegister16 (m_ulBDAddr + X_SR) & SR_CELV)
    {
        //
        // We starve.  :-(
        //
        DOUT (DBG_DMA, ("[GetNewMappings] We starved ... Head %d, Tail %d, Entries %d.",
                stBDList.nHead, stBDList.nTail, stBDList.nBDEntries));
    }
#endif

    //
    // Get available mappings up to the max of 31 that we can hold in the BDL.
    //
    while (stBDList.nBDEntries < (MAX_BDL_ENTRIES - 1))
    {
        //
        // Get the information from the list.
        //
        ULONG               Flags;
        ULONG               ulTag = stBDList.ulTagCounter++;
        ULONG               ulBufferLength;
        PHYSICAL_ADDRESS    PhysAddr;
        PVOID               VirtAddr;


        // Release the mapping spin lock
        KeReleaseSpinLock (&MapLock,OldIrql);

        //
        // Try to get the mapping from the port.
        // Here comes the problem: When calling GetMapping or ReleaseMapping we
        // cannot hold our spin lock. So we need to buffer the return values and
        // stick the information into the structure later when we have the spin
        // lock acquired.
        //
        ntStatus = PortStream->GetMapping ((PVOID)ULongToPtr(ulTag),
                                           (PPHYSICAL_ADDRESS)&PhysAddr,
                                           &VirtAddr,
                                           &ulBufferLength,
                                           &Flags);
    
        // Acquire the mapping spin lock
        KeAcquireSpinLock (&MapLock,&OldIrql);
        
        //
        // Quit this loop when we run out of mappings.
        //
        if (!NT_SUCCESS (ntStatus))
        {
            break;
        }

        // Sanity check: The audio stack will not give you data
        // that you cannot handle, but an application could use
        // DirectKS and send bad buffer down.

        // One mapping needs to be <0x1FFFF bytes for mono
        // streams on the ICH.
        if (ulBufferLength > 0x1FFFE)
        {
            // That is a little too long. That should never happen.
            DOUT (DBG_ERROR, ("[GetNewMappings] Buffer length too long!"));
            ulBufferLength = 0x1FFFE;
        }

        // The ICH can only handle WORD aligned buffers.
        if (PhysAddr.LowPart & 0x01)
        {
            // we cannot play that! Set the buffer length to 0 so
            // that the HW will skip the buffer.
            DOUT (DBG_WARNING, ("[GetNewMappings] Buffer address unaligned!"));
            ulBufferLength = 0;
        }

        // The ICH cannot handle unaligned mappings with respect
        // to the frame size (eg. 42 bytes on 4ch playback).
        if (ulBufferLength % NumberOfChannels)
        {
            // modify the length (don't play the rest of the bytes)
            DOUT (DBG_WARNING, ("[GetNewMappings] Buffer length unaligned!"));
            ulBufferLength -= ulBufferLength % NumberOfChannels;
        }

        //
        // Save the mapping.
        //
        nTail = stBDList.nTail;
        stBDList.pMapData[nTail].ulTag = ulTag;
        stBDList.pMapData[nTail].PhysAddr = PhysAddr;
        stBDList.pMapData[nTail].pVirtAddr = VirtAddr;
        stBDList.pMapData[nTail].ulBufferLength = ulBufferLength;
        ulBytesMapped += ulBufferLength;

        //
        // Fill in the BDL entry with pointer to physical address and length.
        //
        stBDList.pBDEntry[nTail].dwPtrToPhyAddress = PhysAddr.LowPart;
        stBDList.pBDEntry[nTail].wLength = (WORD)(ulBufferLength >> 1);
        stBDList.pBDEntry[nTail].wPolicyBits = BUP_SET;

        //
        // Generate an interrupt when portcls tells us to or roughly every 10ms.
        //
        if (Flags || (ulBytesMapped > (CurrentRate * NumberOfChannels * 2) / 100))
        {
            stBDList.pBDEntry[nTail].wPolicyBits |= IOC_ENABLE; 
            ulBytesMapped = 0;
        }

        //
        // Take the new mapping into account.
        //
        stBDList.nTail = (stBDList.nTail + 1) & BDL_MASK;
        stBDList.nBDEntries++;
        TotalBytesMapped += (ULONGLONG)ulBufferLength;
        nInsertMappings++;

        //
        // Set last valid index (LVI) register! We need to do this here to avoid inconsistency
        // of the BDList with the HW. Note that we need to release spin locks every time
        // we call into portcls, that means we can be interrupted by ReleaseUsedMappings.
        //
        Wave->AdapterCommon->WriteBMControlRegister (m_ulBDAddr + X_LVI, (UCHAR)nTail);
    }

    //
    // If there were processed mappings, print out some debug messages and eventually try to
    // restart DMA engine.
    //
    if (nInsertMappings)
    {
        //
        // Print debug information ...
        //
        DOUT (DBG_DMA, ("[GetNewMappings] Got %d mappings.", nInsertMappings));
        DOUT (DBG_DMA, ("[GetNewMappings] Head %d, Tail %d, Entries %d.",
                stBDList.nHead, stBDList.nTail, stBDList.nBDEntries));


        if (DMAEngineState & DMA_ENGINE_NEED_START)
            ResumeDMA ();
    }

    // Release the mapping spin lock
    KeReleaseSpinLock (&MapLock,OldIrql);
    
    return ntStatus;
}


/*****************************************************************************
 * CMiniportWaveICHStream::ReleaseUsedMappings
 *****************************************************************************
 * This routine unmaps previously mapped memory that the hardware has 
 * completed processing on.  This routine is typically called at DPC level 
 * from the stream deferred procedure call that results from a stream 
 * interrupt. The mapping spin lock must be held when calling this routine.
 */
NTSTATUS CMiniportWaveICHStream::ReleaseUsedMappings (void)
{
    KIRQL OldIrql;
    int   nMappingsReleased = 0;
    
    DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::ReleaseUsedMappings]"));

    // acquire the mapping spin lock
    KeAcquireSpinLock (&MapLock,&OldIrql);

    //
    // Clean up everything to that index.
    //
    while (stBDList.nBDEntries)
    {
        //
        // Get current index 
        //
        int nCurrentIndex = (int)Wave->AdapterCommon->
            ReadBMControlRegister8 (m_ulBDAddr + X_CIV);

        //
        // When CIV is == Head -1 we released all mappings.
        //
        if (nCurrentIndex == ((stBDList.nHead - 1) & BDL_MASK))
        {
           break;
        }
        
        //
        // Check if CIV is between head and tail.
        //
        if (nCurrentIndex < stBDList.nHead)
        {
            //
            // Check for CIV being outside range.
            //
            if ((nCurrentIndex + MAX_BDL_ENTRIES) >=
                (stBDList.nHead + stBDList.nBDEntries))
            {
                DOUT (DBG_ERROR, ("[ReleaseUsedMappings] CIV out of range!"));
                break;
            }
        }
        else
        {
            //
            // Check for CIV being outside range.
            //
            if (nCurrentIndex >= (stBDList.nHead + stBDList.nBDEntries))
            {
                DOUT (DBG_ERROR, ("[ReleaseUsedMappings] CIV out of range!"));
                break;
            }
        }

        //
        // Check to see if we've released all the buffers.
        //
        if (stBDList.nHead == nCurrentIndex)
        {
            if (nCurrentIndex == ((stBDList.nTail - 1) & BDL_MASK))
            {
                //
                // A special case is starvation or stop of stream, when the 
                // DMA engine finished playing the buffers, CVI is equal LVI 
                // and SR_CELV is set.
                //
                if (!(Wave->AdapterCommon->
                     ReadBMControlRegister16 (m_ulBDAddr + X_SR) & SR_CELV))
                {
                    // It is still playing the last buffer.
                    break;
                }

                //
                // In case the CVI=LVI bit is set, nBDEntries should be 1
                //
                if (stBDList.nBDEntries != 1)
                {
                    DOUT (DBG_ERROR, ("[ReleaseUsedMappings] Inconsitency: Tail reached and Entries != 1."));
                }
            }
            else
            {
                //
                // Bail out. Current Index did not move.
                //
                break;
            }
        }

        //
        // Save the tag and remove the entry from the list.
        //
        ULONG   ulTag = stBDList.pMapData[stBDList.nHead].ulTag;
        TotalBytesReleased += (ULONGLONG)stBDList.pMapData[stBDList.nHead].ulBufferLength;
        stBDList.nBDEntries--;
        stBDList.nHead = (stBDList.nHead + 1) & BDL_MASK;
        nMappingsReleased++;

        // Release the mapping spin lock
        KeReleaseSpinLock (&MapLock,OldIrql);

        //
        // Release this entry.
        //
        PortStream->ReleaseMapping ((PVOID)ULongToPtr(ulTag));
        
        // acquire the mapping spin lock
        KeAcquireSpinLock (&MapLock,&OldIrql);
    }


    // Print some debug information in case we released mappings.
    if (nMappingsReleased)
    {
        //
        // Print release information and return.
        //
        DOUT (DBG_DMA, ("[ReleaseUsedMappings] Head %d, Tail %d, Entries %d.",
                       stBDList.nHead, stBDList.nTail, stBDList.nBDEntries));
    }        

    // Release the mapping spin lock
    KeReleaseSpinLock (&MapLock,OldIrql);
    
    return STATUS_SUCCESS;
}


/*****************************************************************************
 * CMiniportWaveICHStream::ResetDMA
 *****************************************************************************
 * This routine resets the Run/Pause bit in the control register. In addition, it
 * resets all DMA registers contents.
 * You need to have the spin lock "MapLock" acquired.
 */
NTSTATUS CMiniportWaveICHStream::ResetDMA (void)
{
    DOUT (DBG_PRINT, ("ResetDMA"));

    //
    // Turn off DMA engine (or make sure it's turned off)
    //
    UCHAR RegisterValue = Wave->AdapterCommon->
        ReadBMControlRegister8 (m_ulBDAddr + X_CR) & ~CR_RPBM;
    Wave->AdapterCommon->
        WriteBMControlRegister (m_ulBDAddr + X_CR, RegisterValue);

    //
    // Reset all register contents.
    //
    RegisterValue |= CR_RR;
    Wave->AdapterCommon->
        WriteBMControlRegister (m_ulBDAddr + X_CR, RegisterValue);
    
    //
    // Wait until reset condition is cleared by HW; should not take long.
    //
    ULONGLONG ullStartTime = PcGetTimeInterval (0);
    BOOL bTimedOut = TRUE;
    do
    {
        if (!(Wave->AdapterCommon->
           ReadBMControlRegister8 (m_ulBDAddr + X_CR) & CR_RR))
        {
            bTimedOut = FALSE;
            break;
        }
    } while (PcGetTimeInterval (ullStartTime) < GTI_MILLISECONDS (1000));

    if (bTimedOut)
    {
        DOUT (DBG_ERROR, ("ResetDMA TIMEOUT!!"));
    }
    
    //
    // We only want interrupts upon completion.
    //
    RegisterValue = CR_IOCE | CR_LVBIE;
    Wave->AdapterCommon->
        WriteBMControlRegister (m_ulBDAddr + X_CR, RegisterValue);
    
    //
    // Setup the Buffer Descriptor Base Address (BDBA) register.
    //
    Wave->AdapterCommon->
        WriteBMControlRegister (m_ulBDAddr,
                               stBDList.PhysAddr.u.LowPart);

    //
    // Set the DMA engine state.
    //
    DMAEngineState = DMA_ENGINE_RESET;


    return STATUS_SUCCESS;
}


/*****************************************************************************
 * CMiniportWaveICHStream::PauseDMA
 *****************************************************************************
 * This routine pauses a hardware stream by reseting the Run/Pause bit in the
 * control registers, leaving DMA registers content intact so that the stream
 * can later be resumed.
 * You need to have the spin lock "MapLock" acquired.
 */
NTSTATUS CMiniportWaveICHStream::PauseDMA (void)
{
    DOUT (DBG_PRINT, ("PauseDMA"));

    //
    // Only pause if we're actually "ON" (DMA_ENGINE_ON or DMA_ENGINE_NEED_START)
    //
    if (!(DMAEngineState & DMA_ENGINE_ON))
    {
        return STATUS_SUCCESS;
    }

    //
    // Turn off DMA engine by resetting the RPBM bit to 0. Don't reset any 
    // registers.
    //
    UCHAR RegisterValue = Wave->AdapterCommon->
        ReadBMControlRegister8 (m_ulBDAddr + X_CR);
    RegisterValue &= ~CR_RPBM;
    Wave->AdapterCommon->
        WriteBMControlRegister (m_ulBDAddr + X_CR, RegisterValue);

    //
    // DMA_ENGINE_NEED_START transitions to DMA_ENGINE_RESET.
    // DMA_ENGINE_ON transitions to DMA_ENGINE_OFF.
    //
    DMAEngineState &= DMA_ENGINE_RESET;

    return STATUS_SUCCESS;
}


/*****************************************************************************
 * CMiniportWaveICHStream::ResumeDMA
 *****************************************************************************
 * This routine sets the Run/Pause bit for the particular DMA engine to resume
 * it after it's been paused. This assumes that DMA registers content have 
 * been preserved.
 * You need to have the spin lock "MapLock" acquired.
 */
NTSTATUS CMiniportWaveICHStream::ResumeDMA (void)
{
    DOUT (DBG_PRINT, ("ResumeDMA"));

    //
    // Before we can turn on the DMA engine the first time, we need to check
    // if we have at least 2 mappings in the scatter gather table.
    //
    if ((DMAEngineState & DMA_ENGINE_RESET) && (stBDList.nBDEntries < 2))
    {
        //
        // That won't work. Set engine state to DMA_ENGINE_NEED_START so that
        // we don't forget to call here regularly.
        //
        DMAEngineState = DMA_ENGINE_NEED_START;
        return STATUS_SUCCESS;
    }

    //
    // Turn DMA engine on by setting the RPBM bit to 1. Don't do anything to 
    // the registers.
    //
    UCHAR RegisterValue = Wave->AdapterCommon->
        ReadBMControlRegister8 (m_ulBDAddr + X_CR) | CR_RPBM;
    Wave->AdapterCommon->
        WriteBMControlRegister (m_ulBDAddr + X_CR, RegisterValue);

    //
    // Set the DMA engine state.
    //
    DMAEngineState = DMA_ENGINE_ON;

    return STATUS_SUCCESS;
}



⌨️ 快捷键说明

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