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

📄 ichwave.cpp

📁 AC97 Sample Driver and Related Code Samples. This directory contains a sample AC97 adapter driver a
💻 CPP
📖 第 1 页 / 共 4 页
字号:
    PAGED_CODE ();

    ASSERT (Format);

    ULONG   TempRate;
    DWORD   dwControlReg;

    DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::SetFormat]"));
    
    //
    // Change sample rate when we are in the stop or pause states - not 
    // while running!
    //
    if (DMAEngineState & DMA_ENGINE_ON)
    {
        return STATUS_UNSUCCESSFUL;
    }

    //
    // Ensure format falls in proper range and is supported.
    //
    NTSTATUS ntStatus = Wave->TestDataFormat (Format, (WavePins)(Channel << 1));
    if (!NT_SUCCESS (ntStatus))
        return ntStatus;

    //
    // Retrieve wave format portion.
    //
    PWAVEFORMATPCMEX waveFormat = (PWAVEFORMATPCMEX)(Format + 1);
    
    //
    // Save current rate in this context.
    //
    TempRate = waveFormat->Format.nSamplesPerSec;
    
    //
    // Check if we have a codec with one sample rate converter and there are streams
    // already open.
    //
    if (Wave->Streams[PIN_WAVEIN_OFFSET] && Wave->Streams[PIN_WAVEOUT_OFFSET] &&
        !Wave->AdapterCommon->GetNodeConfig (NODEC_PCM_VSR_INDEPENDENT_RATES))
    {
        //
        // Figure out at which sample rate the other stream is running.
        //
        ULONG   ulFrequency;

        if (Wave->Streams[PIN_WAVEIN_OFFSET] == this)
            ulFrequency = Wave->Streams[PIN_WAVEOUT_OFFSET]->CurrentRate;
        else
            ulFrequency = Wave->Streams[PIN_WAVEIN_OFFSET]->CurrentRate;

        //
        // Check if this sample rate is requested sample rate.
        //
        if (ulFrequency != TempRate)
        {
            return STATUS_UNSUCCESSFUL;
        }
    }

    //
    // Program the ICH to support n channels.
    //
    if (Channel == PIN_WAVEOUT_OFFSET)
    {
        dwControlReg = Wave->AdapterCommon->ReadBMControlRegister32 (GLOB_CNT);
        dwControlReg = (dwControlReg & 0x03F) |
                       (((waveFormat->Format.nChannels >> 1) - 1) * GLOB_CNT_PCM4);
        Wave->AdapterCommon->WriteBMControlRegister (GLOB_CNT, dwControlReg);
    }
    
    //
    // Check for rate support by hardware.  If it is supported, then update 
    // hardware registers else return not implemented and audio stack will 
    // handle it.
    //
    if (Capture)
    {
        if (Channel == PIN_WAVEIN_OFFSET)
        {
            ntStatus = Wave->AdapterCommon->
                ProgramSampleRate (AC97REG_RECORD_SAMPLERATE, TempRate);
        }
        else
        {
            ntStatus = Wave->AdapterCommon->
                ProgramSampleRate (AC97REG_MIC_SAMPLERATE, TempRate);
        }
    }
    else
    {
        //
        // In the playback case we might need to update several DACs
        // with the new sample rate.
        //
        ntStatus = Wave->AdapterCommon->
            ProgramSampleRate (AC97REG_FRONT_SAMPLERATE, TempRate);

        if (Wave->AdapterCommon->GetNodeConfig (NODEC_SURROUND_DAC_PRESENT))
        {
            ntStatus = Wave->AdapterCommon->
                ProgramSampleRate (AC97REG_SURROUND_SAMPLERATE, TempRate);
        }
        if (Wave->AdapterCommon->GetNodeConfig (NODEC_LFE_DAC_PRESENT))
        {
            ntStatus = Wave->AdapterCommon->
                ProgramSampleRate (AC97REG_LFE_SAMPLERATE, TempRate);
        }
    }
        
    if (NT_SUCCESS (ntStatus))
    {
        //
        // print information and save the format information.
        //
        DataFormat = (PKSDATAFORMAT_WAVEFORMATEX)Format;
        CurrentRate = TempRate;
        NumberOfChannels = waveFormat->Format.nChannels;
    }
    
    return ntStatus;
}
    
/*****************************************************************************
 * CMiniportWaveICHStream::SetContentId
 *****************************************************************************
 * This routine gets called by drmk.sys to pass the content to the driver.
 * The driver has to enforce the rights passed.
 */
STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::SetContentId
(
    IN  ULONG       contentId,
    IN  PCDRMRIGHTS drmRights
)
{
    PAGED_CODE ();

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

    //
    // If "drmRights->DigitalOutputDisable" is set, we need to disable S/P-DIF.
    // Currently, we don't have knowledge about the S/P-DIF interface. However,
    // in case you expanded the driver with S/P-DIF features you need to disable
    // S/P-DIF or fail SetContentId. If you have HW that has S/P-DIF turned on
    // by default and you don't know how to turn off (or you cannot do that)
    // then you must fail SetContentId.
    //
    // In our case, we assume the codec has no S/P-DIF or disabled S/P-DIF by
    // default, so we can ignore the flag.
    //
    // Store the copyright flag. We have to disable PCM recording if it's set.
    //
    if (!Wave->AdapterCommon->GetMiniportTopology ())
    {
        DOUT (DBG_ERROR, ("Topology pointer not set!"));
        return STATUS_UNSUCCESSFUL;
    }
    else
    {
        Wave->AdapterCommon->GetMiniportTopology ()->
            SetCopyProtectFlag (drmRights->CopyProtect);
    }

    //
    // We assume that if we can enforce the rights, that the old content
    // will be destroyed. We don't need to store the content id since we
    // have only one playback channel, so we are finished here.
    //
    
    return STATUS_SUCCESS;
}

/*****************************************************************************
 * Non paged code begins here
 *****************************************************************************
 */

#pragma code_seg()
/*****************************************************************************
 * CMiniportWaveICHStream::PowerChangeNotify
 *****************************************************************************
 * This functions saves and maintains the stream state through power changes.
 */
NTSTATUS CMiniportWaveICHStream::PowerChangeNotify
(
    IN  POWER_STATE NewState
)
{
    PAGED_CODE ();

    KIRQL       OldIrql;
    NTSTATUS    ntStatus = STATUS_SUCCESS;
    
    DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::PowerChangeNotify]"));
    
    //
    // We don't have to check the power state, that's already done by the wave
    // miniport.
    //

    DOUT (DBG_POWER, ("Changing state to D%d.",
                     (ULONG)NewState.DeviceState - (ULONG)PowerDeviceD0));
    
    switch (NewState.DeviceState)
    {
        case PowerDeviceD0:
            //
            // If we are coming from D2 or D3 we have to restore the registers cause
            // there might have been a power loss.
            //
            if ((m_PowerState == PowerDeviceD3) || (m_PowerState == PowerDeviceD2))
            {
                //
                // The scatter gather list is already arranged. A reset of the DMA
                // brings all pointers to the default state. From there we can start.
                //
                
                // Acquire the mapping spin lock
                KeAcquireSpinLock (&MapLock,&OldIrql);
                
                ntStatus = ResetDMA ();

                // Restore the remaining DMA registers, that is last valid index
                // only if the index is not pointing to 0. Note that the index is
                // equal to head + entries.
                if (stBDList.nTail)
                {
                    Wave->AdapterCommon->WriteBMControlRegister (m_ulBDAddr + X_LVI,
                                            (UCHAR)((stBDList.nTail - 1) & BDL_MASK));
                }

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

        case PowerDeviceD1:
            // Here we do nothing. The device has still enough power to keep all
            // it's register values.
            break;

        case PowerDeviceD2:
        case PowerDeviceD3:
            //
            // If we power down to D2 or D3 we might loose power, so we have to be
            // aware of the DMA engine resetting. In that case a play would start
            // with scatter gather entry 0 (the current index is read only).
            // We just rearrange the scatter gather list (like we do on
            // RevokeMappings) so that the current buffer which is played is at
            // entry 0.
            //
                
            // Acquire the mapping spin lock
            KeAcquireSpinLock (&MapLock,&OldIrql);

            // Disable interrupts and stop DMA just in case.
            Wave->AdapterCommon->WriteBMControlRegister (m_ulBDAddr + X_CR, (UCHAR)0);
    
            // Get current index 
            int nCurrentIndex = (int)Wave->AdapterCommon->
                ReadBMControlRegister8 (m_ulBDAddr + X_CIV);
    
            //
            // First move the BD list to the beginning.
            //
            // In case the DMA engine was stopped, current index may point to an
            // empty BD entry. When we start the DMA engine it would then play this
            // (undefined) entry, so we check here for that condition.
            //
            if ((nCurrentIndex == ((stBDList.nHead - 1) & BDL_MASK)) &&
               (stBDList.nBDEntries != MAX_BDL_ENTRIES - 1))
            {
                nCurrentIndex = stBDList.nHead;     // point to head
            }
    
            //
            // Move BD list to (0-((current - head) & mask)) & mask, where
            // ((current - head) & mask) is the difference between head and
            // current index, no matter where they are :)
            //
            MoveBDList (stBDList.nHead, (stBDList.nTail - 1) & BDL_MASK,
                    (0 - ((nCurrentIndex - stBDList.nHead) & BDL_MASK)) & BDL_MASK);
        
            //
            // Update structure.
            //
            stBDList.nHead = (0 - ((nCurrentIndex - stBDList.nHead) & BDL_MASK)) & 
                BDL_MASK;
            stBDList.nTail = (stBDList.nHead + stBDList.nBDEntries) & BDL_MASK;
            
            // release the mapping spin lock
            KeReleaseSpinLock (&MapLock,OldIrql);
            break;
    }
    
    //
    // Save the new state.  This local value is used to determine when to 
    // cache property accesses and when to permit the driver from accessing 
    // the hardware.
    //
    m_PowerState = NewState.DeviceState;
    DOUT (DBG_POWER, ("Entering D%d",
                      (ULONG)m_PowerState - (ULONG)PowerDeviceD0));

    return ntStatus;
}

/*****************************************************************************
 * CMiniportWaveICHStream::SetState
 *****************************************************************************
 * This routine sets/changes the DMA engine state to play, stop, or pause
 */
STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::SetState
(
    IN  KSSTATE State
)
{
    PAGED_CODE ();      // Gets called at PASSIVE_LEVEL
    
    KIRQL OldIrql;
    
    DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::SetState]"));
    DOUT (DBG_STREAM, ("SetState to %d", State));


    //
    // Start or stop the DMA engine dependent of the state.
    //
    switch (State)
    {
        case KSSTATE_STOP:
            // acquire the mapping spin lock
            KeAcquireSpinLock (&MapLock,&OldIrql);

            // Just pause DMA. If we have mappings left in our queue,
            // portcls will call RevokeMappings to destroy them.
            PauseDMA ();

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

            // Release processed mappings
            ReleaseUsedMappings ();

            // Reset the position counters.
            TotalBytesMapped = TotalBytesReleased = 0;
            break;
    
        case KSSTATE_ACQUIRE:
        case KSSTATE_PAUSE:
            // acquire the mapping spin lock
            KeAcquireSpinLock (&MapLock,&OldIrql);
            
            // pause now.
            PauseDMA ();

            // release the mapping spin lock
            KeReleaseSpinLock (&MapLock,OldIrql);
            
            // Release processed mappings
            ReleaseUsedMappings ();

            break;
    
        case KSSTATE_RUN:
            //
            // Let's rock.
            //
            
            
            // Make sure we are not running already.
            if (DMAEngineState & DMA_ENGINE_ON)
            {
                return STATUS_SUCCESS;
            }

            // Release processed mappings.
            ReleaseUsedMappings ();

            // Get new mappings.
            GetNewMappings ();

            // acquire the mapping spin lock
            KeAcquireSpinLock (&MapLock,&OldIrql);
            
            // Kick DMA again just in case.
            ResumeDMA ();
            
            // release the mapping spin lock
            KeReleaseSpinLock (&MapLock,OldIrql);
            break;
    }
    
    return STATUS_SUCCESS;
}

/*****************************************************************************
 * CMiniportWaveICHStream::MoveBDList
 *****************************************************************************
 * Moves the BDList.
 * This function is used to remove entries from the scatter gather list or to
 * move the valid entries to the top. The mapping table which is hard linked
 * to the scatter gather entries is moved too.
 * The function does not change any variables in tBDList.
 * The mapping spin lock must be held when calling this routine.
 * We use this function to remove mappings (RevokeMappings) or to rearrange the
 * list for powerdown/up management (the DMA starts at position zero again).
 * Note that there is a simple way of doing this also. When you zero the buffer
 * length in the scatter gather, the DMA engine ignores the entry and continues
 * with the next. But our way is more generic and if you ever want to port the
 * driver to another DMA engine you might be thankful for this code.
 */
void CMiniportWaveICHStream::MoveBDList
(
    IN  int nFirst, 
    IN  int nLast, 
    IN  int nNewPos
)
{
    DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::MoveBDList]"));
    
    //
    // Print information about the scatter gather list.
    //
    DOUT (DBG_DMA, ("Moving BD entry %d-%d to %d.", nFirst, nLast, nNewPos));

    //
    // First copy the tables to a save place.
    //
    RtlCopyMemory ((PVOID)stBDList.pBDEntryBackup,
                   (PVOID)stBDList.pBDEntry,
                   sizeof (tBDEntry) * MAX_BDL_ENTRIES);
    RtlCopyMemory ((PVOID)stBDList.pMapDataBackup,
                   (PVOID)stBDList.pMapData,
                   sizeof (tMapData) * MAX_BDL_ENTRIES);

    //
    // We move all the entries in blocks to the new position.
    //
    int nBlockCounter = 0;
    do
    {
        nBlockCounter++;
        //
        // We must copy the block when the index wraps around (ring buffer)
        // or we are at the last entry.
        //
        if (((nNewPos + nBlockCounter) == MAX_BDL_ENTRIES) ||   // wrap around
            ((nFirst + nBlockCounter) == MAX_BDL_ENTRIES) ||    // wrap around
            ((nFirst + nBlockCounter) == (nLast + 1)))          // last entry
        {
            //
            // copy one block (multiple entries).
            //
            RtlCopyMemory ((PVOID)&stBDList.pBDEntry[nNewPos],
                           (PVOID)&stBDList.pBDEntryBackup[nFirst],
                           sizeof (tBDEntry) * nBlockCounter);
            RtlCopyMemory ((PVOID)&stBDList.pMapData[nNewPos],
                           (PVOID)&stBDList.pMapDataBackup[nFirst],
                           sizeof (tMapData) * nBlockCounter);
            // adjust the index
            nNewPos = (nNewPos + nBlockCounter) & BDL_MASK;
            nFirst = (nFirst + nBlockCounter) & BDL_MASK;
            nBlockCounter = 0;
        }
    // nBlockCounter should be zero when the end condition hits.
    } while (((nFirst + nBlockCounter - 1) & BDL_MASK) != nLast);
}

⌨️ 快捷键说明

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