/**************************************************************************** * * wave.c * * Wave routines for wdmaud.sys * * Copyright (C) Microsoft Corporation, 1997 - 1999 All Rights Reserved. * * History * S.Mohanraj (MohanS) * M.McLaughlin (MikeM) * 5-19-97 - Noel Cross (NoelC) * ***************************************************************************/ #include "wdmsys.h" // // This is just a scratch location that is never used for anything // but a parameter to core functions. // IO_STATUS_BLOCK gIoStatusBlock ; VOID SetVolumeDpc( IN PKDPC pDpc, IN PVOID DefferedContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ); VOID SetVolumeWorker( IN PWAVEDEVICE pDevice, IN PVOID pNotUsed ); VOID WaitForOutStandingIo( IN PWAVEDEVICE pWaveDevice, IN PWAVE_PIN_INSTANCE pCurWavePin ); // // Check whether the waveformat is supported by kmixer // purpose of this is to decide whether to use WaveQueued // OR Standard Streaming // BOOL PcmWaveFormat( LPWAVEFORMATEX lpFormat ) { PWAVEFORMATEXTENSIBLE pWaveExtended; WORD wFormatTag; PAGED_CODE(); if (lpFormat->wFormatTag == WAVE_FORMAT_PCM) { return (TRUE); } if (lpFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { return (TRUE); } if (lpFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { pWaveExtended = (PWAVEFORMATEXTENSIBLE) lpFormat; if (IS_VALID_WAVEFORMATEX_GUID(&pWaveExtended->SubFormat)) { wFormatTag = EXTRACT_WAVEFORMATEX_ID(&pWaveExtended->SubFormat); if (wFormatTag == WAVE_FORMAT_PCM) { return (TRUE); } if (wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { return (TRUE); } } } return (FALSE); } BOOL IsValidFormatTag( PKSDATARANGE_AUDIO pDataRange, LPWAVEFORMATEX lpFormat ) { PAGED_CODE(); // // See if we have a majorformat and subformat that // we want // if ( IsEqualGUID( &KSDATAFORMAT_TYPE_AUDIO, &pDataRange->DataRange.MajorFormat) ) { if (WAVE_FORMAT_EXTENSIBLE == lpFormat->wFormatTag) { PWAVEFORMATEXTENSIBLE lpFormatExtensible; lpFormatExtensible = (PWAVEFORMATEXTENSIBLE)lpFormat; if ( IsEqualGUID( &pDataRange->DataRange.SubFormat, &lpFormatExtensible->SubFormat) ) { return TRUE; } } else { if ( (EXTRACT_WAVEFORMATEX_ID(&pDataRange->DataRange.SubFormat) == lpFormat->wFormatTag) ) { return TRUE; } } } DPF(DL_TRACE|FA_WAVE,("Invalid Format Tag") ); return FALSE; } BOOL IsValidSampleFrequency( PKSDATARANGE_AUDIO pDataRange, DWORD nSamplesPerSec ) { PAGED_CODE(); // // See if this datarange support the requested frequency // if (pDataRange->MinimumSampleFrequency <= nSamplesPerSec && pDataRange->MaximumSampleFrequency >= nSamplesPerSec) { return TRUE; } else { DPF(DL_MAX|FA_WAVE,("Invalid Sample Frequency") ); return FALSE; } } BOOL IsValidBitsPerSample( PKSDATARANGE_AUDIO pDataRange, LPWAVEFORMATEX lpFormat ) { PAGED_CODE(); // // See if this datarange support the requested frequency // if (pDataRange->MinimumBitsPerSample <= lpFormat->wBitsPerSample && pDataRange->MaximumBitsPerSample >= lpFormat->wBitsPerSample) { if ( (lpFormat->wFormatTag == WAVE_FORMAT_PCM) && (lpFormat->wBitsPerSample > 32) ) { DPF(DL_TRACE|FA_WAVE,("Invalid BitsPerSample") ); return FALSE; } else if ( (lpFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (lpFormat->wBitsPerSample != 32) ) { DPF(DL_TRACE|FA_WAVE,("Invalid BitsPerSample") ); return FALSE; } return TRUE; } else { DPF(DL_TRACE|FA_WAVE,("Invalid BitsPerSample") ); return FALSE; } } BOOL IsValidChannels( PKSDATARANGE_AUDIO pDataRange, LPWAVEFORMATEX lpFormat ) { PAGED_CODE(); // // See if this datarange support the requested frequency // if (pDataRange->MaximumChannels >= lpFormat->nChannels) { if ( ( (lpFormat->wFormatTag == WAVE_FORMAT_PCM) || (lpFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) ) && (lpFormat->nChannels > 2) ) { DPF(DL_TRACE|FA_WAVE,("Invalid Channel") ); return FALSE; } return TRUE; } else { DPF(DL_TRACE|FA_WAVE,("Invalid Channel") ); return FALSE; } } NTSTATUS OpenWavePin( PWDMACONTEXT pWdmaContext, ULONG DeviceNumber, LPWAVEFORMATEX lpFormat, HANDLE32 DeviceHandle, DWORD dwFlags, ULONG DataFlow // DataFlow is either in or out. ) { PWAVE_PIN_INSTANCE pNewWavePin = NULL; PWAVE_PIN_INSTANCE pCurWavePin; PKSPIN_CONNECT pConnect = NULL; PKSDATAFORMAT_WAVEFORMATEX pWaveDataFormat; ULONG RegionSize; PCONTROLS_LIST pControlList = NULL; ULONG Device; ULONG PinId; NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST; PAGED_CODE(); // // Let's do this quickly and get out of here // if (WAVE_FORMAT_QUERY & dwFlags) { PDATARANGES AudioDataRanges; PKSDATARANGE_AUDIO pDataRange; ULONG d; // // WaveOut call? If so, use waveout info // if( KSPIN_DATAFLOW_IN == DataFlow ) AudioDataRanges = pWdmaContext->WaveOutDevs[DeviceNumber].AudioDataRanges; else AudioDataRanges = pWdmaContext->WaveInDevs[DeviceNumber].AudioDataRanges; pDataRange = (PKSDATARANGE_AUDIO)&AudioDataRanges->aDataRanges[0]; for(d = 0; d < AudioDataRanges->Count; d++) { if ( (IsValidFormatTag(pDataRange,lpFormat)) && (IsValidSampleFrequency(pDataRange,lpFormat->nSamplesPerSec)) && (IsValidBitsPerSample(pDataRange,lpFormat)) && (IsValidChannels(pDataRange,lpFormat)) ) { // // Found a good data range, successful query // Status = STATUS_SUCCESS; break; } // Get the pointer to the next data range (PUCHAR)pDataRange += ((pDataRange->DataRange.FormatSize + FILE_QUAD_ALIGNMENT) & ~FILE_QUAD_ALIGNMENT); } goto exit; } // // Need to allocate a pin instance for multiple wave // opens on the same device // Status = AudioAllocateMemory_Fixed(sizeof(WAVE_PIN_INSTANCE), TAG_Audi_PIN, ZERO_FILL_MEMORY, &pNewWavePin); if(!NT_SUCCESS(Status)) { goto exit; } // // Copy the application supplied waveformat so we can // use in the worker thread context. Don't need to zero // memory because we copy into the structure below. // Status = AudioAllocateMemory_Fixed((lpFormat->wFormatTag == WAVE_FORMAT_PCM) ? sizeof( PCMWAVEFORMAT ) : sizeof( WAVEFORMATEX ) + lpFormat->cbSize, TAG_AudF_FORMAT, DEFAULT_MEMORY, &pNewWavePin->lpFormat); if(!NT_SUCCESS(Status)) { AudioFreeMemory(sizeof(WAVE_PIN_INSTANCE),&pNewWavePin); goto exit; } RtlCopyMemory( pNewWavePin->lpFormat, lpFormat, (lpFormat->wFormatTag == WAVE_FORMAT_PCM) ? sizeof( PCMWAVEFORMAT ) : sizeof( WAVEFORMATEX ) + lpFormat->cbSize); pNewWavePin->DataFlow = DataFlow; pNewWavePin->dwFlags = dwFlags; pNewWavePin->DeviceNumber = DeviceNumber; pNewWavePin->WaveHandle = DeviceHandle; pNewWavePin->Next = NULL; pNewWavePin->NumPendingIos = 0; pNewWavePin->StoppingSource = FALSE; pNewWavePin->PausingSource = FALSE; pNewWavePin->dwSig = WAVE_PIN_INSTANCE_SIGNATURE; if( KSPIN_DATAFLOW_IN == DataFlow ) pNewWavePin->pWaveDevice = &pWdmaContext->WaveOutDevs[DeviceNumber]; else pNewWavePin->pWaveDevice = &pWdmaContext->WaveInDevs[DeviceNumber]; KeInitializeEvent ( &pNewWavePin->StopEvent, SynchronizationEvent, FALSE ) ; KeInitializeEvent ( &pNewWavePin->PauseEvent, SynchronizationEvent, FALSE ) ; KeInitializeSpinLock(&pNewWavePin->WavePinSpinLock); if( KSPIN_DATAFLOW_IN == DataFlow ) { if (NULL == pWdmaContext->WaveOutDevs[DeviceNumber].pWavePin) { pWdmaContext->WaveOutDevs[DeviceNumber].pWavePin = pNewWavePin; } else { for (pCurWavePin = pWdmaContext->WaveOutDevs[DeviceNumber].pWavePin; pCurWavePin->Next != NULL; ) { pCurWavePin = pCurWavePin->Next; } pCurWavePin->Next = pNewWavePin; DPF(DL_TRACE|FA_WAVE, ("Opening another waveout pin")); } } else { if (NULL == pWdmaContext->WaveInDevs[DeviceNumber].pWavePin) { pWdmaContext->WaveInDevs[DeviceNumber].pWavePin = pNewWavePin; } else { for (pCurWavePin = pWdmaContext->WaveInDevs[DeviceNumber].pWavePin; pCurWavePin->Next != NULL; ) { pCurWavePin = pCurWavePin->Next; } pCurWavePin->Next = pNewWavePin; DPF(DL_TRACE|FA_WAVE, ("Opening another wavein pin")); } } // // We only support one client at a time. // ASSERT( !pNewWavePin->fGraphRunning ); // // We need to allocate enough memory to handle the // extended waveformat structure // if (WAVE_FORMAT_PCM == lpFormat->wFormatTag) { RegionSize = sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX); } else { RegionSize = sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX) + lpFormat->cbSize; } Status = AudioAllocateMemory_Fixed(RegionSize, TAG_Audt_CONNECT, ZERO_FILL_MEMORY, &pConnect); if(!NT_SUCCESS(Status)) { DPF(DL_WARNING|FA_WAVE, ("pConnect not valid")); goto exit; } pWaveDataFormat = (PKSDATAFORMAT_WAVEFORMATEX)(pConnect + 1); // // Use WAVE_QUEUED for PCM waveOut and Standard Streaming for WaveIn // and non-PCM waveOut // if ( pNewWavePin->DataFlow == KSPIN_DATAFLOW_IN ) { if (PcmWaveFormat(lpFormat)) { // if it is KMIXER supported waveformat pConnect->Interface.Set = KSINTERFACESETID_Media; pConnect->Interface.Id = KSINTERFACE_MEDIA_WAVE_QUEUED; pNewWavePin->fWaveQueued = TRUE; } else { pConnect->Interface.Set = KSINTERFACESETID_Standard; pConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING; pNewWavePin->fWaveQueued = FALSE; } pConnect->Interface.Flags = 0; PinId = pNewWavePin->pWaveDevice->PinId; Device = pNewWavePin->pWaveDevice->Device; } else { pConnect->Interface.Set = KSINTERFACESETID_Standard; pConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING; pConnect->Interface.Flags = 0; PinId = pNewWavePin->pWaveDevice->PinId; Device = pNewWavePin->pWaveDevice->Device; } pConnect->Medium.Set = KSMEDIUMSETID_Standard; pConnect->Medium.Id = KSMEDIUM_STANDARD_DEVIO; pConnect->Medium.Flags = 0 ; pConnect->Priority.PriorityClass = KSPRIORITY_NORMAL; pConnect->Priority.PrioritySubClass = 1; pWaveDataFormat->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO; if (WAVE_FORMAT_EXTENSIBLE == lpFormat->wFormatTag) { PWAVEFORMATEXTENSIBLE lpFormatExtensible; lpFormatExtensible = (PWAVEFORMATEXTENSIBLE)lpFormat; pWaveDataFormat->DataFormat.SubFormat = lpFormatExtensible->SubFormat; } else { INIT_WAVEFORMATEX_GUID( &pWaveDataFormat->DataFormat.SubFormat, lpFormat->wFormatTag ); } pWaveDataFormat->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX; pWaveDataFormat->DataFormat.Flags = 0 ; pWaveDataFormat->DataFormat.FormatSize = RegionSize - sizeof(KSPIN_CONNECT); pWaveDataFormat->DataFormat.SampleSize = lpFormat->nBlockAlign ; pWaveDataFormat->DataFormat.Reserved = 0 ; // // Copy over the whole waveformat structure // RtlCopyMemory( &pWaveDataFormat->WaveFormatEx, lpFormat, (lpFormat->wFormatTag == WAVE_FORMAT_PCM) ? sizeof( PCMWAVEFORMAT ) : sizeof( WAVEFORMATEX ) + lpFormat->cbSize); Status = AudioAllocateMemory_Fixed(( sizeof(CONTROLS_LIST)+ ((MAX_WAVE_CONTROLS-1)*sizeof(CONTROL_NODE)) ), TAG_AudC_CONTROL, ZERO_FILL_MEMORY, &pControlList) ; if(!NT_SUCCESS(Status)) { AudioFreeMemory_Unknown( &pConnect ); DPF(DL_WARNING|FA_WAVE, ("Could not allocate ControlList")); goto exit; } pControlList->Count = MAX_WAVE_CONTROLS ; pControlList->Controls[WAVE_CONTROL_VOLUME].Control = KSNODETYPE_VOLUME ; pControlList->Controls[WAVE_CONTROL_RATE].Control = KSNODETYPE_SRC ; pControlList->Controls[WAVE_CONTROL_QUALITY].Control = KSNODETYPE_SRC ; pNewWavePin->pControlList = pControlList ; // // Open a pin // Status = OpenSysAudioPin(Device, PinId, pNewWavePin->DataFlow, pConnect, &pNewWavePin->pFileObject, &pNewWavePin->pDeviceObject, pNewWavePin->pControlList); AudioFreeMemory_Unknown( &pConnect ); if(!NT_SUCCESS(Status)) { CloseTheWavePin(pNewWavePin->pWaveDevice, pNewWavePin->WaveHandle); goto exit; } if ( pNewWavePin->DataFlow == KSPIN_DATAFLOW_IN ) { Status = AttachVirtualSource(pNewWavePin->pFileObject, pNewWavePin->pWaveDevice->pWdmaContext->VirtualWavePinId); if (!NT_SUCCESS(Status)) { CloseTheWavePin(pNewWavePin->pWaveDevice, pNewWavePin->WaveHandle); goto exit; } } // // Now we've gotten through everything so we can mark this one as running. // We do it here because of the close path. In that path fGraphRunning gets // decremented and the assert fires in the checked build. // pNewWavePin->fGraphRunning=TRUE; // // Why do we set this to KSSTATE_STOP and then change it to KSSTATE_PAUSE? If // StatePin is able to successfully change the state to KSSTATE_PAUSE, the // PinState will get updated to KSSTATE_PAUSE. // pNewWavePin->PinState = KSSTATE_STOP; StatePin(pNewWavePin->pFileObject, KSSTATE_PAUSE, &pNewWavePin->PinState); exit: RETURN( Status ); } void CloseTheWavePin( PWAVEDEVICE pWaveDevice, HANDLE32 DeviceHandle ) { PWAVE_PIN_INSTANCE *ppCur; PWAVE_PIN_INSTANCE pCurFree; PAGED_CODE(); // // Remove from device chain. Notice that ppCur gets the address of the // location of pWaveDevice->pWavePin. Thus, the *ppCur = (*ppCur)->Next // assignment below updates the pWaveDevice->pWavePin location if we // close the first pin. // for (ppCur = &pWaveDevice->pWavePin; *ppCur != NULL; ppCur = &(*ppCur)->Next) { if ( NULL == DeviceHandle || (*ppCur)->WaveHandle == DeviceHandle ) { // // Note that if there is outstanding Io we can not call CloseWavePin // until it's all come back. Thus, we'll need to tell the device // to stop and then wait for the Io here. // if( (*ppCur)->pFileObject ) { // // We will never have outstanding Io if we don't have a file object // to send it too. // WaitForOutStandingIo(pWaveDevice,*ppCur); } CloseWavePin ( *ppCur ); pCurFree = *ppCur; *ppCur = (*ppCur)->Next; AudioFreeMemory( sizeof(WAVE_PIN_INSTANCE), &pCurFree ); break; } } } // // This routine can not fail! // VOID CloseWavePin( PWAVE_PIN_INSTANCE pWavePin ) { ASSERT(pWavePin->NumPendingIos==0); PAGED_CODE(); // // This routine can get called on the error path thus fGraphRunning may be FALSE. // In either case, we will need to close sysaudio and free memory. // pWavePin->fGraphRunning = FALSE; // Close the file object (pFileObject, if it exists) if(pWavePin->pFileObject) { CloseSysAudio(pWavePin->pWaveDevice->pWdmaContext, pWavePin->pFileObject); pWavePin->pFileObject = NULL; } // // AudioFreeMemory_Unknown NULLs out this location after freeing the memory. // AudioFreeMemory_Unknown ( &pWavePin->lpFormat ); AudioFreeMemory_Unknown ( &pWavePin->pControlList ) ; // // Caller needs to free pWavePin if it wants to. // } #pragma LOCKED_CODE #pragma LOCKED_DATA // // This routine is used rather then an InterlockedIncrement and InterlockedDecrement // because the routine that needs to determine what to do based on this information // needs to perform multiple checks on different variables to determine exactly what // to do. Thus, we need a "critical section" for NumPendingIos. Also, SpinLocks // must be called from locked code. :) // void LockedWaveIoCount( PWAVE_PIN_INSTANCE pCurWavePin, BOOL bIncrease ) { KIRQL OldIrql; KeAcquireSpinLock(&pCurWavePin->WavePinSpinLock,&OldIrql); if( bIncrease ) pCurWavePin->NumPendingIos++; else pCurWavePin->NumPendingIos--; KeReleaseSpinLock(&pCurWavePin->WavePinSpinLock, OldIrql); } void CompleteNumPendingIos( PWAVE_PIN_INSTANCE pCurWavePin ) { KIRQL OldIrql; if( pCurWavePin ) { KeAcquireSpinLock(&pCurWavePin->WavePinSpinLock,&OldIrql); // // We always decrement NumPendingIos and then perform the comparisons. // If the count goes to zero, we're the last IRP so we need to check // to see if we need to signal any waiting thread. // if( ( --pCurWavePin->NumPendingIos == 0 ) && pCurWavePin->StoppingSource ) { // // If this Io is the last one to come through, and we're currently // sitting waiting for the reset to finish, then we signal it here. // KeSetEvent ( &pCurWavePin->StopEvent, 0, FALSE ) ; } // // Upon leaving this spinlock, pCurWavePin can be freed by the close // routine if NumPendingIos went to zero! // KeReleaseSpinLock(&pCurWavePin->WavePinSpinLock, OldIrql); } // // Must not touch pCurWavePin after this! // } void UnmapWriteContext( PWRITE_CONTEXT pWriteContext ) { wdmaudUnmapBuffer(pWriteContext->pBufferMdl); AudioFreeMemory_Unknown(&pWriteContext->pCapturedWaveHdr); AudioFreeMemory(sizeof(WRITE_CONTEXT),&pWriteContext); } void FreeWriteContext( PWRITE_CONTEXT pWriteContext, NTSTATUS IrpStatus ) { PIRP UserIrp; PWDMAPENDINGIRP_CONTEXT pPendingIrpContext; // // grab the parent IRP from the reserved field // UserIrp = (PIRP)pWriteContext->whInstance.wh.reserved; pPendingIrpContext = pWriteContext->pPendingIrpContext; UnmapWriteContext( pWriteContext ); if( UserIrp ) wdmaudUnprepareIrp( UserIrp,IrpStatus,0,pPendingIrpContext); } // // This is the Irp completion routine. // NTSTATUS wqWriteWaveCallBack( PDEVICE_OBJECT pDeviceObject, PIRP pIrp, IN PWAVEHDR pWriteData ) { PWAVE_PIN_INSTANCE pCurWavePin; PMDL Mdl; PMDL nextMdl; PIRP UserIrp; PWDMAPENDINGIRP_CONTEXT pPendingIrpContext; PWRITE_CONTEXT pWriteContext = (PWRITE_CONTEXT)pWriteData; ASSERT(pWriteData); pCurWavePin = pWriteContext->whInstance.pWaveInstance; DPF(DL_TRACE|FA_WAVE, ("R%d: 0x%08x", pCurWavePin->NumPendingIos,pIrp)); // // After we get our pCurWavePin, we don't need the write context any longer. // FreeWriteContext(pWriteContext, pIrp->IoStatus.Status); // // Consider putting this in a routine. // if (pIrp->MdlAddress != NULL) { // // Unlock any pages that may be described by MDLs. // Mdl = pIrp->MdlAddress; while (Mdl != NULL) { MmUnlockPages( Mdl ); Mdl = Mdl->Next; } } if (pIrp->MdlAddress != NULL) { for (Mdl = pIrp->MdlAddress; Mdl != (PMDL) NULL; Mdl = nextMdl) { nextMdl = Mdl->Next; if (Mdl->MdlFlags & MDL_PARTIAL_HAS_BEEN_MAPPED) { ASSERT( Mdl->MdlFlags & MDL_PARTIAL ); MmUnmapLockedPages( Mdl->MappedSystemVa, Mdl ); } else if (!(Mdl->MdlFlags & MDL_PARTIAL)) { ASSERT(!(Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA )); } AudioFreeMemory_Unknown( &Mdl ); } } IoFreeIrp( pIrp ); CompleteNumPendingIos( pCurWavePin ); return ( STATUS_MORE_PROCESSING_REQUIRED ); } // // Consider combining ssWriteWaveCallback and wqWriteWaveCallBack. They look // like the same routine! // NTSTATUS ssWriteWaveCallBack( PDEVICE_OBJECT pDeviceObject, PIRP pIrp, IN PSTREAM_HEADER_EX pStreamHeader ) { PWAVE_PIN_INSTANCE pCurWavePin; PIRP UserIrp; PWDMAPENDINGIRP_CONTEXT pPendingIrpContext; PWRITE_CONTEXT pWriteContext = (PWRITE_CONTEXT)pStreamHeader->pWaveHdr; ASSERT(pWriteContext); pCurWavePin = pWriteContext->whInstance.pWaveInstance; DPF(DL_TRACE|FA_WAVE, ("R%d: 0x%08x", pCurWavePin->NumPendingIos,pIrp)); FreeWriteContext( pWriteContext, pIrp->IoStatus.Status ); CompleteNumPendingIos( pCurWavePin ); return STATUS_SUCCESS; } #pragma PAGEABLE_CODE #pragma PAGEABLE_DATA // // Walk the list and if we find a matching pin, write it back for the caller. // NTSTATUS FindRunningPin( IN PWAVEDEVICE pWaveDevice, IN HANDLE32 DeviceHandle, OUT PWAVE_PIN_INSTANCE* ppCurWavePin ) { PWAVE_PIN_INSTANCE pCurWavePin; NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST; // // Prepare for the error condition. // *ppCurWavePin = NULL; // // find the right pin based off of the wave handle // for (pCurWavePin = pWaveDevice->pWavePin; pCurWavePin != NULL; pCurWavePin = pCurWavePin->Next) { if (pCurWavePin->WaveHandle == DeviceHandle) { if (pCurWavePin->fGraphRunning) { // // Write back the pointer and return success // *ppCurWavePin = pCurWavePin; Status = STATUS_SUCCESS; } else { DPF(DL_WARNING|FA_WAVE,("Invalid fGraphRunning") ); Status = STATUS_DEVICE_NOT_READY; } return Status; } } return Status; } // // WriteWaveOutPin walks the device list like the other routines. // // pUserIrp is the Irp on which this call from user mode was made. It's // always going to be valid. We don't need to check it. // // This routine needs to set pCompletedIrp to either TRUE or FALSE. If // TRUE, the Irp was successfully marked STATUS_PENDING and it will get // completed later. If FALSE, there was some type of error that prevented // us from submitting the Irp. The caller to this routine will need to // handle freeing the Irp. // // // This routine should be the one storing the user's irp in the reserved field. // Not the caller. // pWriteContext->whInstance.wh.reserved = (DWORD_PTR)pIrp; // store to complete later // NTSTATUS WriteWaveOutPin( PWAVEDEVICE pWaveOutDevice, HANDLE32 DeviceHandle, LPWAVEHDR pWriteData, PSTREAM_HEADER_EX pStreamHeader, PIRP pUserIrp, PWDMACONTEXT pContext, BOOL *pCompletedIrp ) { PWAVE_PIN_INSTANCE pCurWavePin; PWRITE_CONTEXT pWriteContext = (PWRITE_CONTEXT)pWriteData; PWDMAPENDINGIRP_CONTEXT pPendingIrpContext; NTSTATUS Status; PAGED_CODE(); // // We assumee that pCompletedIrp is FALSE on entry. // ASSERT( *pCompletedIrp == FALSE ); Status = FindRunningPin(pWaveOutDevice,DeviceHandle,&pCurWavePin); if( NT_SUCCESS(Status) ) { if (pCurWavePin->fWaveQueued) { PIO_STACK_LOCATION pIrpStack; LARGE_INTEGER StartingOffset; PIRP pIrp = NULL; // // Can't use KsStreamIo because these are not // true stream headers. Sending down headers // using the WAVE_QUEUED interface // StartingOffset.QuadPart = 0; pIrp = IoBuildAsynchronousFsdRequest(IRP_MJ_WRITE, pCurWavePin->pDeviceObject, (PVOID)pWriteContext, sizeof(WAVEHDR), &StartingOffset, &gIoStatusBlock); if( pIrp ) { Status = wdmaudPrepareIrp( pUserIrp, WaveOutDevice, pContext, &pPendingIrpContext ); if( NT_SUCCESS(Status) ) { // // The Irp was successfully marked STATUS_PENDING and put in // our queue. Now let's send it. // pWriteContext->whInstance.pWaveInstance = pCurWavePin; pWriteContext->pPendingIrpContext = pPendingIrpContext; pIrp->RequestorMode = KernelMode; pIrp->Tail.Overlay.OriginalFileObject = pCurWavePin->pFileObject; pIrpStack = IoGetNextIrpStackLocation(pIrp); pIrpStack->FileObject = pCurWavePin->pFileObject; IoSetCompletionRoutine(pIrp, wqWriteWaveCallBack, pWriteData, TRUE,TRUE,TRUE); // // one more IRP pending // LockedWaveIoCount(pCurWavePin,INCREASE); DPF(DL_TRACE|FA_WAVE, ("A%d", pCurWavePin->NumPendingIos)); // // We don't need to check the return code because the // completion routine will ALWAYS be called. See // IoSetCompletionRoutine(...TRUE,TRUE,TRUE). // IofCallDriver( pCurWavePin->pDeviceObject, pIrp ); // // At this point, the Irp may have been completed and our // callback routine will have been called. We can not touch // the irp after this call. The Callback routine Completes // the Irp and unprepares the user's Irp. // *pCompletedIrp = TRUE; // // In wdmaudPrepareIrp we call IoCsqInsertIrp which calls // IoMarkIrpPending, thus we must always return STATUS_PENDING. // return STATUS_PENDING; } else { // // We where not successful at putting the Irp in the queue. // cleanup and indicated that we did not complete the Irp. // The status will have been set by wdmaudPrepareIrp. DPF(DL_WARNING|FA_WAVE,("wdmaudPrepareIrp failed Status=%X",Status) ); } } else { // // Could not create a Irp to send down - error out! // DPF(DL_WARNING|FA_WAVE,("IoBuildAsynchronousFsdRequest failed") ); Status = STATUS_UNSUCCESSFUL; // // We can't get an Irp to schedule. Cleanup memory // and return. The caller will complete the Irp. // } } else { // // If it's not wave queued we need to make sure that it's a PCM // looped call. // if ( (pWriteData->dwFlags & (WHDR_BEGINLOOP|WHDR_ENDLOOP)) ) { // // Error out non-PCM looped calls // Status = STATUS_NOT_IMPLEMENTED; } else { // // The graph is running so we can use it. Proceed. // Status = wdmaudPrepareIrp( pUserIrp, WaveOutDevice, pContext, &pPendingIrpContext ); if( NT_SUCCESS(Status) ) { // // The Irp was successfully marked STATUS_PENDING and put in // our queue. Now let's send it. // pWriteContext->whInstance.pWaveInstance = pCurWavePin; pWriteContext->pPendingIrpContext = pPendingIrpContext; // // one more IRP pending // LockedWaveIoCount(pCurWavePin,INCREASE); DPF(DL_TRACE|FA_WAVE, ("A%d", pCurWavePin->NumPendingIos)); pStreamHeader->pWavePin = pCurWavePin; pStreamHeader->Header.FrameExtent = pWriteData->dwBufferLength ; pStreamHeader->Header.DataUsed = pWriteData->dwBufferLength; pStreamHeader->Header.OptionsFlags = 0 ; pStreamHeader->Header.Size = sizeof( KSSTREAM_HEADER ); pStreamHeader->Header.TypeSpecificFlags = 0; pStreamHeader->pWaveHdr = pWriteData; // store so we can use later Status = KsStreamIo(pCurWavePin->pFileObject, NULL, // Event NULL, // PortContext ssWriteWaveCallBack, pStreamHeader, // CompletionContext KsInvokeOnSuccess | KsInvokeOnCancel | KsInvokeOnError, &gIoStatusBlock, &pStreamHeader->Header, sizeof( KSSTREAM_HEADER ), KSSTREAM_WRITE, KernelMode ); // // At this point, the Irp may have been completed and our // callback routine will have been called. We can not touch // the irp after this call. The Callback routine Completes // the Irp and unprepares the user's Irp. // *pCompletedIrp = TRUE; // // In wdmaudPrepareIrp we call IoCsqInsertIrp which calls // IoMarkIrpPending, thus we must always return STATUS_PENDING. // also, we don't want to clean up anything.... just return. // return STATUS_PENDING; // // Warning: If, for any reason, the completion routine is not called // for this Irp, wdmaud.sys will hang. It's been discovered that // KsStreamIo may error out in low memory conditions. There is an // outstanding bug to address this. // } else { // // We where not successful at putting the Irp in the queue. // cleanup and indicated that we did not complete the Irp. // The Status was set by wdmaudPrepareIrp. DPF(DL_WARNING|FA_WAVE,("wdmaudPrepareIrp failed Status=%X",Status) ); } } } } // // All error paths end up here. All error paths should cleanup the // memory so we don't leak. // UnmapWriteContext( pWriteContext ); RETURN( Status ); } // // These next three routines all perform the same type of walk and checks. // They should be combined into one walk routine and a callback. // NTSTATUS PosWavePin( PWAVEDEVICE pWaveDevice, HANDLE32 DeviceHandle, PWAVEPOSITION pWavePos ) { PWAVE_PIN_INSTANCE pCurWavePin; KSAUDIO_POSITION AudioPosition; NTSTATUS Status; PAGED_CODE(); Status = FindRunningPin(pWaveDevice,DeviceHandle,&pCurWavePin); if( NT_SUCCESS(Status) ) { if ( pWavePos->Operation == KSPROPERTY_TYPE_SET ) { AudioPosition.WriteOffset = pWavePos->BytePos; } Status = PinProperty(pCurWavePin->pFileObject, &KSPROPSETID_Audio, KSPROPERTY_AUDIO_POSITION, pWavePos->Operation, sizeof(AudioPosition), &AudioPosition); if (NT_SUCCESS(Status)) { pWavePos->BytePos = (DWORD)AudioPosition.PlayOffset; } } RETURN( Status ); } NTSTATUS BreakLoopWaveOutPin( PWAVEDEVICE pWaveOutDevice, HANDLE32 DeviceHandle ) { PWAVE_PIN_INSTANCE pCurWavePin; NTSTATUS Status; PAGED_CODE(); Status = FindRunningPin(pWaveOutDevice,DeviceHandle,&pCurWavePin); if( NT_SUCCESS(Status) ) { if (pCurWavePin->fWaveQueued) { Status = PinMethod ( pCurWavePin->pFileObject, &KSMETHODSETID_Wave_Queued, KSMETHOD_WAVE_QUEUED_BREAKLOOP, KSMETHOD_TYPE_WRITE, // TODO :: change to TYPE_NONE 0, NULL ) ; } else { // // Error out non-pcm loop related commands // Status = STATUS_NOT_IMPLEMENTED; } } RETURN( Status ); } NTSTATUS ResetWaveOutPin( PWAVEDEVICE pWaveOutDevice, HANDLE32 DeviceHandle ) { PWAVE_PIN_INSTANCE pCurWavePin; NTSTATUS Status; KSRESET ResetValue ; PAGED_CODE(); Status = FindRunningPin(pWaveOutDevice,DeviceHandle,&pCurWavePin); if( NT_SUCCESS(Status)) { pCurWavePin->StoppingSource = TRUE ; ResetValue = KSRESET_BEGIN ; Status = ResetWavePin(pCurWavePin, &ResetValue) ; // // If the driver fails to reset will will not wait for the // Irps to complete. But, that would be bad in the // CleanupWavePins case because we're going to free // the memory when we return from this call. Thus, // will choose a hang over a bugcheck and wait for // the Irps to complete. // if ( pCurWavePin->NumPendingIos ) { DPF(DL_TRACE|FA_WAVE, ("Start waiting for stop to complete")); KeWaitForSingleObject ( &pCurWavePin->StopEvent, Executive, KernelMode, FALSE, NULL ) ; } DPF(DL_TRACE|FA_WAVE, ("Done waiting for stop to complete")); ResetValue = KSRESET_END ; ResetWavePin(pCurWavePin, &ResetValue) ; // // Why do we have this KeClearEvent ??? // KeClearEvent ( &pCurWavePin->StopEvent ); pCurWavePin->StoppingSource = FALSE ; } RETURN( Status ); } // // The only difference between this and StatePin is KSPROPERTY_CONNECTION_STATE // and IOCTL_KS_RESET_STATE. Consider using StatePin if possible. // NTSTATUS ResetWavePin( PWAVE_PIN_INSTANCE pWavePin, KSRESET *pResetValue ) { NTSTATUS Status = STATUS_SUCCESS; ULONG BytesReturned ; PAGED_CODE(); if (!pWavePin->fGraphRunning) { DPF(DL_WARNING|FA_WAVE,("Invalid fGraphRunning") ); RETURN( STATUS_INVALID_DEVICE_REQUEST ); } DPF(DL_TRACE|FA_SYSAUDIO,("IOCTL_KS_RESET_STATE pResetValue=%X",pResetValue) ); Status = KsSynchronousIoControlDevice(pWavePin->pFileObject, KernelMode, IOCTL_KS_RESET_STATE, pResetValue, sizeof(KSRESET), NULL, 0, &BytesReturned); DPF(DL_TRACE|FA_SYSAUDIO,("IOCTL_KS_RESET_STATE result=%X",Status) ); RETURN( Status ); } // // Looks the same, different flavor. // NTSTATUS StateWavePin( PWAVEDEVICE pWaveInDevice, HANDLE32 DeviceHandle, KSSTATE State ) { PWAVE_PIN_INSTANCE pCurWavePin; NTSTATUS Status; PAGED_CODE(); Status = FindRunningPin(pWaveInDevice,DeviceHandle,&pCurWavePin); if( NT_SUCCESS(Status) ) { if( pCurWavePin->DataFlow == KSPIN_DATAFLOW_OUT ) { // // We have an In pin. // // // On a waveInStop, one more buffer needs to make // it up to the application before the device can // stop. The caveat is that if the buffer is // large it might take awhile for the stop to happen. // // Don't return let this extra buffer complete if the // device is already in a paused state. // if( (KSSTATE_PAUSE == State) && (KSSTATE_PAUSE != pCurWavePin->PinState) ) { pCurWavePin->PausingSource = TRUE ; if ( pCurWavePin->NumPendingIos ) { DPF(DL_TRACE|FA_WAVE, ("Waiting for PauseEvent...")); KeWaitForSingleObject ( &pCurWavePin->PauseEvent, Executive, KernelMode, FALSE, NULL ) ; DPF(DL_TRACE|FA_WAVE, ("...Done waiting for PauseEvent")); } KeClearEvent ( &pCurWavePin->PauseEvent ); pCurWavePin->PausingSource = FALSE ; } Status = StatePin ( pCurWavePin->pFileObject, State, &pCurWavePin->PinState ) ; if ( NT_SUCCESS(Status) ) { ASSERT(pCurWavePin->PinState == State); if ( KSSTATE_STOP == State ) { Status = StatePin( pCurWavePin->pFileObject,KSSTATE_PAUSE,&pCurWavePin->PinState); } } } else { // // We have an out pin. // Status = StatePin ( pCurWavePin->pFileObject, State, &pCurWavePin->PinState ) ; } } RETURN( Status ); } #pragma LOCKED_CODE #pragma LOCKED_DATA void UnmapStreamHeader( PSTREAM_HEADER_EX pStreamHeader ) { wdmaudUnmapBuffer(pStreamHeader->pBufferMdl); wdmaudUnmapBuffer(pStreamHeader->pHeaderMdl); AudioFreeMemory_Unknown(&pStreamHeader->pWaveHdrAligned); AudioFreeMemory(sizeof(STREAM_HEADER_EX),&pStreamHeader); } void FreeStreamHeader( PSTREAM_HEADER_EX pStreamHeader, NTSTATUS IrpStatus ) { PIRP UserIrp; PWDMAPENDINGIRP_CONTEXT pPendingIrpContext; UserIrp = pStreamHeader->pIrp; ASSERT(UserIrp); pPendingIrpContext = pStreamHeader->pPendingIrpContext; UnmapStreamHeader( pStreamHeader ); wdmaudUnprepareIrp( UserIrp,IrpStatus,sizeof(DEVICEINFO),pPendingIrpContext ); } // // This is the read Irp completion routine. // NTSTATUS ReadWaveCallBack( PDEVICE_OBJECT pDeviceObject, PIRP pIrp, IN PSTREAM_HEADER_EX pStreamHeader ) { // cast the reserved field to the parent IRP that we stored in here PWAVE_PIN_INSTANCE pCurWavePin; NTSTATUS Status; KIRQL OldIrql; // // Must get the current pin before we free the stream header structure. // pCurWavePin = pStreamHeader->pWavePin; // // Get the dataused and fill the bytes recorded field // if (pIrp->IoStatus.Status == STATUS_CANCELLED) pStreamHeader->pWaveHdrAligned->dwBytesRecorded = 0L; else pStreamHeader->pWaveHdrAligned->dwBytesRecorded = pStreamHeader->Header.DataUsed; // // Copy back the contents of the captured buffer // try { RtlCopyMemory( pStreamHeader->pdwBytesRecorded, &pStreamHeader->pWaveHdrAligned->dwBytesRecorded, sizeof(DWORD)); } except (EXCEPTION_EXECUTE_HANDLER) { DPF(DL_WARNING|FA_WAVE, ("Couldn't copy waveheader (0x%08x)", GetExceptionCode()) ); } FreeStreamHeader( pStreamHeader, pIrp->IoStatus.Status ); if ( pCurWavePin ) { // // Need to lock this code so we can decrement and check and set an event // with no preemption windows. // KeAcquireSpinLock(&pCurWavePin->WavePinSpinLock, &OldIrql); // // We always decrement NumPendingIos before doing any comparisons. This // is so that we're consistant. // pCurWavePin->NumPendingIos--; if( pCurWavePin->PausingSource ) { // // Let this I/O squeeze out of the queue on a waveInStop // KeSetEvent ( &pCurWavePin->PauseEvent, 0, FALSE ) ; } // // If the count went to zero, we're the last IRP so we need to check // to see if we need to signal any waiting thread. // if( (pCurWavePin->NumPendingIos == 0) && pCurWavePin->StoppingSource ) { // // Because we do not block (FALSE), we can call KeSetEvent in this // Lock. // KeSetEvent ( &pCurWavePin->StopEvent, 0, FALSE ) ; } // // Upon leaving this spinlock, pCurWavePin can be freed by the close // routine if NumPendingIos went to zero! // KeReleaseSpinLock(&pCurWavePin->WavePinSpinLock, OldIrql); } return STATUS_SUCCESS; } #pragma PAGEABLE_CODE #pragma PAGEABLE_DATA // // pUserIrp will always be valid when this call is made. It is the Irp // that we got for the user mode request. // // pStreamHeader is alway going to be valid. // NTSTATUS ReadWaveInPin( PWAVEDEVICE pWaveInDevice, HANDLE32 DeviceHandle, PSTREAM_HEADER_EX pStreamHeader, PIRP pUserIrp, PWDMACONTEXT pContext, BOOL *pCompletedIrp ) { PWAVE_PIN_INSTANCE pCurWavePin; PWDMAPENDINGIRP_CONTEXT pPendingIrpContext; NTSTATUS Status; PAGED_CODE(); // // We assumee that pCompletedIrp is FALSE on entry. // ASSERT( *pCompletedIrp == FALSE ); Status = FindRunningPin(pWaveInDevice,DeviceHandle,&pCurWavePin); if( NT_SUCCESS(Status) ) { Status = wdmaudPrepareIrp( pUserIrp, WaveInDevice, pContext, &pPendingIrpContext ); if( NT_SUCCESS(Status) ) { pStreamHeader->pWavePin = pCurWavePin; pStreamHeader->pPendingIrpContext = pPendingIrpContext; ASSERT(pPendingIrpContext); pStreamHeader->Header.OptionsFlags = 0 ; pStreamHeader->Header.Size = sizeof( KSSTREAM_HEADER ); pStreamHeader->Header.TypeSpecificFlags = 0; LockedWaveIoCount(pCurWavePin,INCREASE); DPF(DL_TRACE|FA_WAVE, ("A%d: 0x%08x", pCurWavePin->NumPendingIos, pStreamHeader)); Status = KsStreamIo(pCurWavePin->pFileObject, NULL, // Event NULL, // PortContext ReadWaveCallBack, pStreamHeader, // CompletionContext KsInvokeOnSuccess | KsInvokeOnCancel | KsInvokeOnError, &gIoStatusBlock, &pStreamHeader->Header, sizeof( KSSTREAM_HEADER ), KSSTREAM_READ, KernelMode ); // // In wdmaudPrepareIrp we call IoCsqInsertIrp which calls // IoMarkIrpPending, thus we must always return STATUS_PENDING. // And we completed the Irp. // *pCompletedIrp = TRUE; return STATUS_PENDING; // // Warning: If, for any reason, the completion routine is not called // for this Irp, wdmaud.sys will hang. It's been discovered that // KsStreamIo may error out in low memory conditions. There is an // outstanding bug to address this. // } else { // // wdmaudPrepareIrp would have set Status for this error path // DPF(DL_WARNING|FA_WAVE,("wdmaudPrepareIrp failed Status=%X",Status) ); } } // // All error paths lead here. // UnmapStreamHeader( pStreamHeader ); RETURN( Status ); } NTSTATUS FindVolumeControl( IN PWDMACONTEXT pWdmaContext, IN PCWSTR DeviceInterface, IN DWORD DeviceType ) { PCOMMONDEVICE *papCommonDevice; PWAVEDEVICE paWaveOutDevs; PMIDIDEVICE paMidiOutDevs; PAUXDEVICE paAuxDevs; ULONG DeviceNumber; ULONG MixerIndex; NTSTATUS Status; PAGED_CODE(); papCommonDevice = &pWdmaContext->apCommonDevice[DeviceType][0]; paWaveOutDevs = pWdmaContext->WaveOutDevs; paMidiOutDevs = pWdmaContext->MidiOutDevs; paAuxDevs = pWdmaContext->AuxDevs; for( DeviceNumber = 0; DeviceNumber < MAXNUMDEVS; DeviceNumber++ ) { if(papCommonDevice[DeviceNumber]->Device == UNUSED_DEVICE) { continue; } if(MyWcsicmp(papCommonDevice[DeviceNumber]->DeviceInterface, DeviceInterface)) { continue; } MixerIndex = FindMixerForDevNode(pWdmaContext->MixerDevs, DeviceInterface); if ( (MixerIndex == UNUSED_DEVICE) || (pWdmaContext->MixerDevs[MixerIndex].pwstrName == NULL) ) { continue; } switch(DeviceType) { case WaveOutDevice: Status = IsVolumeControl( pWdmaContext, DeviceInterface, MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT, &paWaveOutDevs[ DeviceNumber ].dwVolumeID, &paWaveOutDevs[ DeviceNumber ].cChannels); if(!NT_SUCCESS(Status)) { break; } if( paWaveOutDevs[ DeviceNumber ].pTimer == NULL ) { Status = AudioAllocateMemory_Fixed(sizeof(KTIMER), TAG_AudT_TIMER, ZERO_FILL_MEMORY, &paWaveOutDevs[ DeviceNumber ].pTimer); if(!NT_SUCCESS(Status)) { return Status; } KeInitializeTimerEx( paWaveOutDevs[ DeviceNumber ].pTimer, NotificationTimer ); } if( paWaveOutDevs[ DeviceNumber ].pDpc == NULL ) { Status = AudioAllocateMemory_Fixed(sizeof(KDPC), TAG_AudE_EVENT, ZERO_FILL_MEMORY, &paWaveOutDevs[ DeviceNumber ].pDpc); if(!NT_SUCCESS(Status)) { return Status; } KeInitializeDpc( paWaveOutDevs[ DeviceNumber ].pDpc, SetVolumeDpc, &paWaveOutDevs[ DeviceNumber ] ); // Initialize the left and right channels to goofy values. // This signifies that the cache is invalid paWaveOutDevs[ DeviceNumber ].LeftVolume = 0x4321; paWaveOutDevs[ DeviceNumber ].RightVolume = 0x6789; paWaveOutDevs[ DeviceNumber ].fNeedToSetVol = FALSE; } break; case MidiOutDevice: Status = IsVolumeControl( pWdmaContext, DeviceInterface, MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER, &paMidiOutDevs[ DeviceNumber ].dwVolumeID, &paMidiOutDevs[ DeviceNumber ].cChannels); break; case AuxDevice: Status = IsVolumeControl( pWdmaContext, DeviceInterface, MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC, &paAuxDevs[ DeviceNumber ].dwVolumeID, &paAuxDevs[ DeviceNumber ].cChannels); break; } } // while return( STATUS_SUCCESS ); } NTSTATUS IsVolumeControl( IN PWDMACONTEXT pWdmaContext, IN PCWSTR DeviceInterface, IN DWORD dwComponentType, IN PDWORD pdwControlID, IN PDWORD pcChannels ) { MIXERLINE ml; MIXERLINECONTROLS mlc; MIXERCONTROL mc; MMRESULT mmr; PAGED_CODE(); ml.dwComponentType = dwComponentType; ml.cbStruct = sizeof( MIXERLINE ); mmr = kmxlGetLineInfo( pWdmaContext, DeviceInterface, &ml, MIXER_GETLINEINFOF_COMPONENTTYPE ); if( mmr != MMSYSERR_NOERROR ) { DPF(DL_WARNING|FA_WAVE,("kmxlGetLineInfo failed mmr=%X",mmr) ); RETURN( STATUS_UNSUCCESSFUL ); } mlc.cbStruct = sizeof( MIXERLINECONTROLS ); mlc.dwLineID = ml.dwLineID; mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; mlc.cControls = 1; mlc.cbmxctrl = sizeof( MIXERCONTROL ); mlc.pamxctrl = &mc; mmr = kmxlGetLineControls( pWdmaContext, DeviceInterface, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE ); if( mmr != MMSYSERR_NOERROR ) { DPF(DL_WARNING|FA_WAVE,( "kmxlGetLineControls failed mmr=%x!", mmr ) ); return( STATUS_UNSUCCESSFUL ); } *pdwControlID = mc.dwControlID; *pcChannels = ml.cChannels; return( STATUS_SUCCESS ); } #pragma LOCKED_CODE NTSTATUS MapMmSysError( IN MMRESULT mmr ) { if ( (mmr == MMSYSERR_INVALPARAM) || (mmr == MIXERR_INVALCONTROL) ) { return (STATUS_INVALID_PARAMETER); } if (mmr == MMSYSERR_NOTSUPPORTED) { return (STATUS_NOT_SUPPORTED); } if (mmr == MMSYSERR_NOMEM) { return(STATUS_INSUFFICIENT_RESOURCES); } if (mmr == MMSYSERR_NOERROR) { return(STATUS_SUCCESS); } return(STATUS_UNSUCCESSFUL); } NTSTATUS SetVolume( PWDMACONTEXT pWdmaContext, IN DWORD DeviceNumber, IN DWORD DeviceType, IN DWORD LeftChannel, IN DWORD RightChannel ) { MIXERCONTROLDETAILS mcd; MIXERCONTROLDETAILS_UNSIGNED mcd_u[ 2 ]; LARGE_INTEGER li; if( DeviceNumber == (ULONG) -1 ) { RETURN( STATUS_INVALID_PARAMETER ); } if( DeviceType == WaveOutDevice ) { PWAVEDEVICE paWaveOutDevs = pWdmaContext->WaveOutDevs; mcd_u[ 0 ].dwValue = LeftChannel; mcd_u[ 1 ].dwValue = RightChannel; mcd.cbStruct = sizeof( MIXERCONTROLDETAILS ); mcd.dwControlID = paWaveOutDevs[ DeviceNumber ].dwVolumeID; mcd.cChannels = paWaveOutDevs[ DeviceNumber ].cChannels; mcd.cMultipleItems = 0; mcd.cbDetails = mcd.cChannels * sizeof( MIXERCONTROLDETAILS_UNSIGNED ); mcd.paDetails = &mcd_u[0]; return( MapMmSysError(kmxlSetControlDetails( pWdmaContext, paWaveOutDevs[ DeviceNumber ].DeviceInterface, &mcd, 0 )) ); } if( DeviceType == MidiOutDevice ) { PMIDIDEVICE paMidiOutDevs = pWdmaContext->MidiOutDevs; // // We don't support volume changes on a MIDIPORT // if ( paMidiOutDevs[ DeviceNumber ].MusicDataRanges ) { WORD wTechnology; wTechnology = GetMidiTechnology( (PKSDATARANGE_MUSIC) &paMidiOutDevs[ DeviceNumber ].MusicDataRanges->aDataRanges[0] ); if (wTechnology == MOD_MIDIPORT) { RETURN( STATUS_INVALID_DEVICE_REQUEST ); } } mcd_u[ 0 ].dwValue = LeftChannel; mcd_u[ 1 ].dwValue = RightChannel; mcd.cbStruct = sizeof( MIXERCONTROLDETAILS ); mcd.dwControlID = paMidiOutDevs[ DeviceNumber ].dwVolumeID; mcd.cChannels = paMidiOutDevs[ DeviceNumber ].cChannels; mcd.cMultipleItems = 0; mcd.cbDetails = mcd.cChannels * sizeof( MIXERCONTROLDETAILS_UNSIGNED ); mcd.paDetails = &mcd_u[0]; return( MapMmSysError(kmxlSetControlDetails( pWdmaContext, paMidiOutDevs[ DeviceNumber ].DeviceInterface, &mcd, 0 )) ); } if( DeviceType == AuxDevice ) { PAUXDEVICE paAuxDevs = pWdmaContext->AuxDevs; mcd_u[ 0 ].dwValue = LeftChannel; mcd_u[ 1 ].dwValue = RightChannel; mcd.cbStruct = sizeof( MIXERCONTROLDETAILS ); mcd.dwControlID = paAuxDevs[ DeviceNumber ].dwVolumeID; mcd.cChannels = paAuxDevs[ DeviceNumber ].cChannels; mcd.cMultipleItems = 0; mcd.cbDetails = mcd.cChannels * sizeof( MIXERCONTROLDETAILS_UNSIGNED ); mcd.paDetails = &mcd_u[0]; return( MapMmSysError(kmxlSetControlDetails( pWdmaContext, paAuxDevs[ DeviceNumber ].DeviceInterface, &mcd, 0 )) ); } return( STATUS_SUCCESS ); } VOID SetVolumeDpc( IN PKDPC pDpc, IN PWAVEDEVICE pWaveDevice, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) { QueueWorkList(pWaveDevice->pWdmaContext, SetVolumeWorker, pWaveDevice, NULL ) ; } VOID SetVolumeWorker( IN PWAVEDEVICE pDevice, IN PVOID pNotUsed ) { MIXERCONTROLDETAILS mcd; MIXERCONTROLDETAILS_UNSIGNED mcd_u[ 2 ]; DPF(DL_TRACE|FA_WAVE,( "Left %X Right %X", pDevice->LeftVolume, pDevice->RightVolume ) ); pDevice->fNeedToSetVol = FALSE; mcd_u[ 0 ].dwValue = pDevice->LeftVolume; mcd_u[ 1 ].dwValue = pDevice->RightVolume; mcd.cbStruct = sizeof( MIXERCONTROLDETAILS ); mcd.dwControlID = pDevice->dwVolumeID; mcd.cChannels = pDevice->cChannels; mcd.cMultipleItems = 0; mcd.cbDetails = mcd.cChannels * sizeof( MIXERCONTROLDETAILS_UNSIGNED ); mcd.paDetails = &mcd_u[0]; kmxlSetControlDetails( pDevice->pWdmaContext, pDevice->DeviceInterface, &mcd, 0 ); } #pragma PAGEABLE_CODE NTSTATUS GetVolume( IN PWDMACONTEXT pWdmaContext, IN DWORD DeviceNumber, IN DWORD DeviceType, OUT PDWORD LeftChannel, OUT PDWORD RightChannel ) { MIXERCONTROLDETAILS mcd; MIXERCONTROLDETAILS_UNSIGNED mcd_u[ 2 ]; MMRESULT mmr; PAGED_CODE(); if( DeviceType == WaveOutDevice ) { PWAVEDEVICE pWaveOutDevice = &pWdmaContext->WaveOutDevs[DeviceNumber]; if( ( pWaveOutDevice->LeftVolume != 0x4321 ) && ( pWaveOutDevice->RightVolume != 0x6789 ) ) { *LeftChannel = pWaveOutDevice->LeftVolume; *RightChannel = pWaveOutDevice->RightVolume; return( STATUS_SUCCESS ); } else { mcd.cbStruct = sizeof( MIXERCONTROLDETAILS ); mcd.dwControlID = pWaveOutDevice->dwVolumeID; mcd.cChannels = pWaveOutDevice->cChannels; mcd.cMultipleItems = 0; mcd.cbDetails = mcd.cChannels * sizeof( MIXERCONTROLDETAILS_UNSIGNED ); mcd.paDetails = &mcd_u[0]; mmr = kmxlGetControlDetails( pWdmaContext, pWaveOutDevice->DeviceInterface, &mcd, 0 ); if( mmr == MMSYSERR_NOERROR ) { *LeftChannel = mcd_u[ 0 ].dwValue; *RightChannel = mcd_u[ 1 ].dwValue; } return( MapMmSysError(mmr) ); } } if( DeviceType == MidiOutDevice ) { PMIDIDEVICE pMidiOutDevice = &pWdmaContext->MidiOutDevs[DeviceNumber]; // // We don't support volume changes on a MIDIPORT // if ( pMidiOutDevice->MusicDataRanges ) { WORD wTechnology; wTechnology = GetMidiTechnology( (PKSDATARANGE_MUSIC) &pMidiOutDevice->MusicDataRanges->aDataRanges[0] ); if (wTechnology == MOD_MIDIPORT) { RETURN( STATUS_INVALID_DEVICE_REQUEST ); } } mcd.cbStruct = sizeof( MIXERCONTROLDETAILS ); mcd.dwControlID = pMidiOutDevice->dwVolumeID; mcd.cChannels = pMidiOutDevice->cChannels; mcd.cMultipleItems = 0; mcd.cbDetails = mcd.cChannels * sizeof( MIXERCONTROLDETAILS_UNSIGNED ); mcd.paDetails = &mcd_u[0]; mmr = kmxlGetControlDetails( pWdmaContext, pMidiOutDevice->DeviceInterface, &mcd, 0 ); if( mmr == MMSYSERR_NOERROR ) { *LeftChannel = mcd_u[ 0 ].dwValue; *RightChannel = mcd_u[ 1 ].dwValue; } return( MapMmSysError(mmr) ); } if( DeviceType == AuxDevice ) { PAUXDEVICE pAuxDevice = &pWdmaContext->AuxDevs[DeviceNumber]; mcd.cbStruct = sizeof( MIXERCONTROLDETAILS ); mcd.dwControlID = pAuxDevice->dwVolumeID; mcd.cChannels = pAuxDevice->cChannels; mcd.cMultipleItems = 0; mcd.cbDetails = mcd.cChannels * sizeof( MIXERCONTROLDETAILS_UNSIGNED ); mcd.paDetails = &mcd_u[0]; mmr = kmxlGetControlDetails( pWdmaContext, pAuxDevice->DeviceInterface, &mcd, 0 ); if( mmr == MMSYSERR_NOERROR ) { *LeftChannel = mcd_u[ 0 ].dwValue; *RightChannel = mcd_u[ 1 ].dwValue; } return( MapMmSysError(mmr) ); } RETURN( STATUS_INVALID_PARAMETER ); } // // This routine waits for the Io to complete on the device after telling // the device to stop. // VOID WaitForOutStandingIo( IN PWAVEDEVICE pWaveDevice, IN PWAVE_PIN_INSTANCE pCurWavePin ) { if( pCurWavePin->DataFlow == KSPIN_DATAFLOW_IN) { // // We have a wave out pin to close. Force pending data // to come back on running pins. Non-running pins are // ignored on this call. // ResetWaveOutPin( pWaveDevice, pCurWavePin->WaveHandle); } else { // // We have a wave in pin to close // pCurWavePin->StoppingSource = TRUE ; // // We can not fail on this path. Doesn't look like we need to make sure // that we're running here. // StatePin ( pCurWavePin->pFileObject, KSSTATE_STOP, &pCurWavePin->PinState ) ; // // Regardless of the return code we're going to wait for all the // irps to complete. If the driver do not successfuly complete // the irps, we will hang waiting for them here. // if( pCurWavePin->NumPendingIos ) { KeWaitForSingleObject ( &pCurWavePin->StopEvent, Executive, KernelMode, FALSE, NULL ) ; } // // Why do we have this KeClearEvent??? // KeClearEvent ( &pCurWavePin->StopEvent ); pCurWavePin->StoppingSource = FALSE ; } } // // Replaces CleanupWaveOutPins and CleanupWaveInPins. // VOID CleanupWavePins( IN PWAVEDEVICE pWaveDevice ) { PWAVE_PIN_INSTANCE pCurWavePin; PWAVE_PIN_INSTANCE pFreeWavePin; PAGED_CODE(); while (pCurWavePin = pWaveDevice->pWavePin) { DPF(DL_TRACE|FA_WAVE, ("0x%08x", pCurWavePin)); WaitForOutStandingIo(pWaveDevice,pCurWavePin); CloseWavePin( pCurWavePin ); pFreeWavePin = pCurWavePin; pWaveDevice->pWavePin = pCurWavePin->Next; AudioFreeMemory( sizeof(WAVE_PIN_INSTANCE),&pFreeWavePin ); } } VOID CleanupWaveDevices( IN PWDMACONTEXT pWdmaContext ) { DWORD DeviceNumber; PAGED_CODE(); for (DeviceNumber = 0; DeviceNumber < MAXNUMDEVS; DeviceNumber++) { // // Handle waveout devices first... // if (pWdmaContext->apCommonDevice[WaveOutDevice][DeviceNumber]->Device != UNUSED_DEVICE) { if ( pWdmaContext->WaveOutDevs[DeviceNumber].pTimer != NULL) KeCancelTimer(pWdmaContext->WaveOutDevs[DeviceNumber].pTimer); CleanupWavePins(&pWdmaContext->WaveOutDevs[DeviceNumber]); // // Sense we have removed it from the list, the other routine that would do // the same thing (RemoveDevNode) will also attempt to remove it from the // the list because the value it non-null. Thus, the only safe thing that // we can do here is free the memory. // // Note: This routine will only get called when the handle to the driver is // closed. // AudioFreeMemory_Unknown(&pWdmaContext->WaveOutDevs[DeviceNumber].pTimer); } // // ...then handle wavein devices // if (pWdmaContext->apCommonDevice[WaveInDevice][DeviceNumber]->Device != UNUSED_DEVICE) { CleanupWavePins(&pWdmaContext->WaveInDevs[DeviceNumber]); } } } #pragma LOCKED_CODE #pragma LOCKED_DATA // // -------------------------------------------------------------------------------- // // The following routines are used by more then just wave.c // // -------------------------------------------------------------------------------- // NTSTATUS wdmaudPrepareIrp( PIRP pIrp, ULONG IrpDeviceType, PWDMACONTEXT pContext, PWDMAPENDINGIRP_CONTEXT *ppPendingIrpContext ) { return AddIrpToPendingList( pIrp, IrpDeviceType, pContext, ppPendingIrpContext ); } NTSTATUS wdmaudUnprepareIrp( PIRP pIrp, NTSTATUS IrpStatus, ULONG_PTR Information, PWDMAPENDINGIRP_CONTEXT pPendingIrpContext ) { NTSTATUS Status; // Note that the IrpContext may have been zero'ed out already because the cancel // routine has already been called. The cancel safe queue API zeroes out the Irp // field in the context when it performs a cancel. Status = RemoveIrpFromPendingList( pPendingIrpContext ); if (NT_SUCCESS(Status)) { pIrp->IoStatus.Status = IrpStatus; if (Information) { pIrp->IoStatus.Information = Information; } IoCompleteRequest( pIrp, IO_NO_INCREMENT ); } RETURN( Status ); } #pragma PAGEABLE_CODE #pragma PAGEABLE_DATA // // StatePin - This is used by both Midi and Wave functionality. // // On success State will get updated to the new state. Must make sure that // fGraphRunning is TRUE before calling this routine. // // call like: // if( pWavePin->fGraphRunning ) // StatePin(pWavePin->pFileObject, KSSTATE_PAUSE, &pWavePin->State); // NTSTATUS StatePin( IN PFILE_OBJECT pFileObject, IN KSSTATE State, OUT PKSSTATE pResultingState ) { NTSTATUS Status; PAGED_CODE(); Status = PinProperty(pFileObject, &KSPROPSETID_Connection, KSPROPERTY_CONNECTION_STATE, KSPROPERTY_TYPE_SET, sizeof(State), &State); if(NT_SUCCESS(Status)) { *pResultingState = State; } RETURN( Status ); }