diff options
| author | Daniel Rugerio <[email protected]> | 2021-08-13 15:05:12 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-08-13 15:05:12 -0700 |
| commit | 7895dd22785ddba5e973662ed942be3b3452b89d (patch) | |
| tree | 5c4572d5bfd076d3776a88c5757e37f3ed50c0e7 | |
| parent | df47b2d284558fa9aacd19257153037e4ebba60e (diff) | |
audio: Updates for Windows 10 22000 (#655)
* Updates for Windows 10 22000.
* Change how WIL is consumed.
103 files changed, 12312 insertions, 1416 deletions
diff --git a/audio/simpleaudiosample/README.md b/audio/simpleaudiosample/README.md index 5309626a..f7d41b38 100644 --- a/audio/simpleaudiosample/README.md +++ b/audio/simpleaudiosample/README.md @@ -1,6 +1,6 @@ --- page_type: sample -description: "The Microsoft Simple Audio Sample Device Driver shows how to develop a simple WDM audio driver that exposes support for two basic audio devices (a speaker and microphone array).” +description: "The Microsoft Simple Audio Sample Device Driver shows how to develop a simple WDM audio driver that exposes support for two basic audio devices (a speaker and microphone array)." languages: - cpp products: diff --git a/audio/simpleaudiosample/Source/Utilities/ToneGenerator.cpp b/audio/simpleaudiosample/Source/Utilities/ToneGenerator.cpp index f50e81e1..868b7fe5 100644 --- a/audio/simpleaudiosample/Source/Utilities/ToneGenerator.cpp +++ b/audio/simpleaudiosample/Source/Utilities/ToneGenerator.cpp @@ -95,21 +95,25 @@ VOID ToneGenerator::InitNewFrame RtlZeroMemory(Frame, FrameSize); return; } - + + /* Use __analysis_assume to suppress the reports of a false OACR error. */ for(ULONG i = 0; i < m_ChannelCount; ++i) { if (m_BitsPerSample == 8) { + __analysis_assume((DWORD)m_ChannelCount == FrameSize); unsigned char *dataBuffer = reinterpret_cast<unsigned char *>(Frame); - dataBuffer[i] = ConvertToUChar(sinValue); + dataBuffer[i] = ConvertToUChar(sinValue); } else if (m_BitsPerSample == 16) { + __analysis_assume((DWORD)(m_ChannelCount) * 2 == FrameSize); short *dataBuffer = reinterpret_cast<short *>(Frame); dataBuffer[i] = ConvertToShort(sinValue); } else if (m_BitsPerSample == 24) { + __analysis_assume((DWORD)(m_ChannelCount) * 3 == FrameSize); BYTE *dataBuffer = Frame; long val = ConvertToLong(sinValue); val = val >> 8; @@ -117,6 +121,7 @@ VOID ToneGenerator::InitNewFrame } else if (m_BitsPerSample == 32) { + __analysis_assume((DWORD)(m_ChannelCount) * 4 == FrameSize); long *dataBuffer = reinterpret_cast<long *>(Frame); dataBuffer[i] = ConvertToLong(sinValue); } diff --git a/audio/sysvad/A2dpHpDevice.cpp b/audio/sysvad/A2dpHpDevice.cpp new file mode 100644 index 00000000..00c40831 --- /dev/null +++ b/audio/sysvad/A2dpHpDevice.cpp @@ -0,0 +1,4006 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + A2dpHpDevice.cpp + +Abstract: + + Implementation of the A2dpHpDevice class. + +--*/ + +#pragma warning (disable : 4127) + +#include <initguid.h> +#include <sysvad.h> +#include "hw.h" +#include "savedata.h" +#include "IHVPrivatePropertySet.h" +#include "simple.h" + +#ifdef SYSVAD_A2DP_SIDEBAND +#include <limits.h> +#include <SidebandAudio.h> +#include <A2DPSidebandAudio.h> +#include <wdmguid.h> // guild-arrival/removal +#include <devpkey.h> +#include "a2dphpminipairs.h" +#include "A2dpHpDevice.h" +#include "A2dpHpDeviceFormats.h" + +// +// A2dpHpDevice implementation. +// + +// # of sec before sync request is cancelled. +#define A2DP_SIDEBAND_SYNC_REQ_TIMEOUT_IN_SEC 60 +#define A2DP_SIDEBAND_NOTIFICATION_MAX_ERROR_COUNT 5 + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP +A2dpHpDevice::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface routine for A2dpHpDevice + +Arguments: + + Interface - + + Object - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(PSIDEBANDDEVICECOMMON(this))); + } + else if (IsEqualGUIDAligned(Interface, IID_IA2dpHpDeviceCommon)) + { + *Object = PVOID(PSIDEBANDDEVICECOMMON(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + PUNKNOWN(*Object)->AddRef(); + return STATUS_SUCCESS; + } + + return STATUS_INVALID_PARAMETER; +} // NonDelegatingQueryInterface + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::Init +( + _In_ IAdapterCommon * Adapter, + _In_ PUNICODE_STRING SymbolicLinkName +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + A2dpHpDeviceNotificationWorkItemContext *wiCtx = NULL; + A2dpHpDeviceNotificationReqContext *reqCtx = NULL; + WDF_OBJECT_ATTRIBUTES attributes; + WDF_IO_TARGET_OPEN_PARAMS openParams; + WDF_WORKITEM_CONFIG wiConfig; + + AddRef(); // first ref. + + // + // Basic init of all the class' members. + // + m_State = eA2dpHpStateInitializing; + m_Adapter = Adapter; + + // Static config. + m_WdfIoTarget = NULL; + m_SpeakerMiniports = NULL; + m_UnknownSpeakerTopology = NULL; + m_UnknownSpeakerWave = NULL; + m_Descriptor = NULL; + m_SpeakerVolumePropValues = NULL; + m_SpeakerMutePropValues = NULL; + + // Notification updates. + m_SpeakerVolumeLevel = 0; + m_SpeakerMute = 0; + RtlZeroMemory(&m_ConnectedCodecCaps, sizeof(A2DPHPDEVICE_CODEC_CAPABILITIES)); + ExInitializeFastMutex(&m_ConnectedCodecLock); + m_ConnectionStatusLong = FALSE; + m_SpeakerStreamStatusLong = STATUS_SUCCESS; // A2DP stream is not open. + + m_SpeakerStreamReq = NULL; + m_SpeakerVolumeReq = NULL; + m_SpeakerMuteReq = NULL; + m_ConnectionReq = NULL; + m_SiopUpdateReq = NULL; + + m_WorkItem = NULL; + m_ReqCollection = NULL; + + m_nSpeakerStreams = 0; + m_nSpeakerStartedStreams = 0; + + m_pSpeakerSupportedFormatsIntersection = NULL; + + KeInitializeEvent(&m_SpeakerStreamStatusEvent, NotificationEvent, TRUE); + + InitializeListHead(&m_ListEntry); + KeInitializeSpinLock(&m_Lock); + + RtlZeroMemory(&m_SymbolicLinkName, sizeof(m_SymbolicLinkName)); + + RtlZeroMemory(&m_SpeakerVolumeCallback, sizeof(m_SpeakerVolumeCallback)); + RtlZeroMemory(&m_SpeakerMuteCallback, sizeof(m_SpeakerMuteCallback)); + RtlZeroMemory(&m_SpeakerConnectionStatusCallback, sizeof(m_SpeakerConnectionStatusCallback)); + RtlZeroMemory(&m_SpeakerFormatChangeCallback, sizeof(m_SpeakerFormatChangeCallback)); + + RtlZeroMemory(&m_SpeakerTransportResources, sizeof(A2DPHPDEVICE_EP_TRANSPORT_RESOURCES)); + + // + // Allocate a notification WDF work-item. + // + WDF_WORKITEM_CONFIG_INIT(&wiConfig, EvtA2dpHpDeviceNotificationStatusWorkItem); + wiConfig.AutomaticSerialization = FALSE; + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, A2dpHpDeviceNotificationWorkItemContext); + attributes.ParentObject = Adapter->GetWdfDevice(); + ntStatus = WdfWorkItemCreate( &wiConfig, + &attributes, + &m_WorkItem); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfWorkItemCreate failed: 0x%x", ntStatus)), + Done); + + wiCtx = GetA2dpHpDeviceNotificationWorkItemContext(m_WorkItem); + wiCtx->A2dpHpDevice = this; // weak ref. + + // + // Allocate a collection to hold notification requests for the notification work-item. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfCollectionCreate( + &attributes, + &m_ReqCollection); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfCollectionCreate failed: 0x%x", ntStatus)), + Done); + + // + // Open the target interface. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfIoTargetCreate(m_Adapter->GetWdfDevice(), + &attributes, + &m_WdfIoTarget); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfIoTargetCreate failed: 0x%x", ntStatus)), + Done); + + WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME( + &openParams, + SymbolicLinkName, + STANDARD_RIGHTS_ALL); + + ntStatus = WdfIoTargetOpen(m_WdfIoTarget, &openParams); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfIoTargetOpen(%wZ) failed: 0x%x", SymbolicLinkName, ntStatus)), + Done); + + // + // Make a copy of the symbolic link name. + // + m_SymbolicLinkName.MaximumLength = SymbolicLinkName->MaximumLength; + m_SymbolicLinkName.Length = SymbolicLinkName->Length; + m_SymbolicLinkName.Buffer = (PWSTR) ExAllocatePool2(POOL_FLAG_NON_PAGED, + SymbolicLinkName->MaximumLength, + MINADAPTER_POOLTAG); + if (m_SymbolicLinkName.Buffer == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: ExAllocatePool2 failed, out of memory")), + Done); + + RtlCopyUnicodeString(&m_SymbolicLinkName, SymbolicLinkName); + + // + // Allocate the WDF requests for status notifications. + // + + // + // IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE + // SPEAKER + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, A2dpHpDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_SpeakerVolumeReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate(Speaker-Volume) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerVolumeReq); + reqCtx->A2dpHpDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.Volume), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.Volume), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE + // SPEAKER + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, A2dpHpDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_SpeakerMuteReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate(Speaker-Mute) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerMuteReq); + reqCtx->A2dpHpDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.Mute), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.Mute), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_SBAUD_GET_CONNECTION_STATUS_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, A2dpHpDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_ConnectionReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfRequestCreate(Connection-Status) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetA2dpHpDeviceNotificationReqContext(m_ConnectionReq); + reqCtx->A2dpHpDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.ConnectionStatus), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.ConnectionStatus), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_SBAUD_GET_STREAM_STATUS_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, A2dpHpDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_SpeakerStreamReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate(Stream-Status) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerStreamReq); + reqCtx->A2dpHpDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.StreamStatus), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.StreamStatus), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_SBAUD_GET_SIOP_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, A2dpHpDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_SiopUpdateReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate(SIOP Update) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetA2dpHpDeviceNotificationReqContext(m_SiopUpdateReq); + reqCtx->A2dpHpDevice = this; + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.SiopUpdate), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC! WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.SiopUpdate), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC! WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // Create wave and filter names unique to this HFP device + // + ntStatus = CreateFilterNames(SymbolicLinkName); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: Creating unique filter names for the A2DP device failed, 0x%x", ntStatus)), + Done); + + // + // This remote device is now in running state. No need to use interlock operations + // b/c at this time this is the only thread accessing this info. + // + m_State = eA2dpHpStateRunning; + + // + // Init successful. + // + ntStatus = STATUS_SUCCESS; + +Done: + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +A2dpHpDevice::~A2dpHpDevice +( + void +) +/*++ + +Routine Description: + + Destructor for A2dpHpDevice. + +Arguments: + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + ASSERT(m_State != eA2dpHpStateRunning); + ASSERT(IsListEmpty(&m_ListEntry)); + + // + // Release ref to remote stack. + // + if (m_WdfIoTarget != NULL) + { + WdfObjectDelete(m_WdfIoTarget); + m_WdfIoTarget = NULL; + } + + // + // Free symbolic links. + // + if (m_SymbolicLinkName.Buffer != NULL) + { + ExFreePoolWithTag(m_SymbolicLinkName.Buffer, MINADAPTER_POOLTAG); + RtlZeroMemory(&m_SymbolicLinkName, sizeof(m_SymbolicLinkName)); + } + + DeleteCustomEndpointMinipair(m_SpeakerMiniports); + m_SpeakerMiniports = NULL; + + if (m_Descriptor != NULL) + { + ExFreePoolWithTag(m_Descriptor, MINADAPTER_POOLTAG); + m_Descriptor = NULL; + } + + SAFE_DELETE_PTR_WITH_TAG(m_pSpeakerDescriptor, A2DPSIDEBANDTEST_POOLTAG02); + + SAFE_DELETE_PTR_WITH_TAG(m_pSpeakerSupportedFormatsIntersection, A2DPSIDEBANDTEST_POOLTAG03); + + RtlZeroMemory(&m_SpeakerTransportResources, sizeof(A2DPHPDEVICE_EP_TRANSPORT_RESOURCES)); + + if (m_SpeakerVolumePropValues != NULL) + { + ExFreePoolWithTag(m_SpeakerVolumePropValues, A2DPSIDEBANDTEST_POOLTAG02); + m_SpeakerVolumePropValues = NULL; + } + + if (m_SpeakerMutePropValues != NULL) + { + ExFreePoolWithTag(m_SpeakerMutePropValues, A2DPSIDEBANDTEST_POOLTAG06); + m_SpeakerMutePropValues = NULL; + } + + if (m_ConnectedCodecCaps) + { + ExFreePoolWithTag(m_ConnectedCodecCaps, A2DPSIDEBANDTEST_POOLTAG07); + m_ConnectedCodecCaps = NULL; + } + + // + // Free Irps. + // + if (m_SpeakerVolumeReq != NULL) + { + A2dpHpDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerVolumeReq); + if (ctx->MemIn != NULL) + { + WdfObjectDelete(ctx->MemIn); + ctx->MemIn = NULL; + } + + if (ctx->MemOut != NULL) + { + WdfObjectDelete(ctx->MemOut); + ctx->MemOut = NULL; + } + + // Delete the request. + WdfObjectDelete(m_SpeakerVolumeReq); + m_SpeakerVolumeReq = NULL; + } + + if (m_SpeakerMuteReq != NULL) + { + A2dpHpDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerMuteReq); + if (ctx->MemIn != NULL) + { + WdfObjectDelete(ctx->MemIn); + ctx->MemIn = NULL; + } + + if (ctx->MemOut != NULL) + { + WdfObjectDelete(ctx->MemOut); + ctx->MemOut = NULL; + } + + // Delete the request. + WdfObjectDelete(m_SpeakerMuteReq); + m_SpeakerMuteReq = NULL; + } + + if (m_ConnectionReq != NULL) + { + A2dpHpDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetA2dpHpDeviceNotificationReqContext(m_ConnectionReq); + if (ctx->MemIn != NULL) + { + WdfObjectDelete(ctx->MemIn); + ctx->MemIn = NULL; + } + + if (ctx->MemOut != NULL) + { + WdfObjectDelete(ctx->MemOut); + ctx->MemOut = NULL; + } + + // Delete the request. + WdfObjectDelete(m_ConnectionReq); + m_ConnectionReq = NULL; + } + + if (m_SpeakerStreamReq != NULL) + { + A2dpHpDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerStreamReq); + if (ctx->MemIn != NULL) + { + WdfObjectDelete(ctx->MemIn); + ctx->MemIn = NULL; + } + + if (ctx->MemOut != NULL) + { + WdfObjectDelete(ctx->MemOut); + ctx->MemOut = NULL; + } + + // Delete the request. + WdfObjectDelete(m_SpeakerStreamReq); + m_SpeakerStreamReq = NULL; + } + + if (m_SiopUpdateReq != NULL) + { + A2dpHpDeviceNotificationReqContext* ctx; + + // Delete the associated memory objects. + ctx = GetA2dpHpDeviceNotificationReqContext(m_SiopUpdateReq); + if (ctx->MemIn != NULL) + { + WdfObjectDelete(ctx->MemIn); + ctx->MemIn = NULL; + } + + if (ctx->MemOut != NULL) + { + WdfObjectDelete(ctx->MemOut); + ctx->MemOut = NULL; + } + + // Delete the request. + WdfObjectDelete(m_SiopUpdateReq); + m_SiopUpdateReq = NULL; + } + + // + // Notification work-item. + // + if (m_WorkItem != NULL) + { + WdfObjectDelete(m_WorkItem); + m_WorkItem = NULL; + } + + // + // Notification req. collection. + // + if (m_ReqCollection != NULL) + { + WdfObjectDelete(m_ReqCollection); + m_ReqCollection = NULL; + } + + ASSERT(m_UnknownSpeakerTopology == NULL); + SAFE_RELEASE(m_UnknownSpeakerTopology); + + ASSERT(m_UnknownSpeakerWave == NULL); + SAFE_RELEASE(m_UnknownSpeakerWave); + + ASSERT(m_nSpeakerStreams == 0); + + ASSERT(m_SpeakerVolumeCallback.Handler == NULL); + ASSERT(m_SpeakerMuteCallback.Handler == NULL); + ASSERT(m_SpeakerConnectionStatusCallback.Handler == NULL); + ASSERT(m_SpeakerFormatChangeCallback.Handler == NULL); +} // ~A2dpHpDevice + +// +// ISidebandDeviceCommon implementation. +// + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +A2dpHpDevice::IsVolumeSupported +( + _In_ eDeviceType deviceType +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eA2dpHpSpeakerDevice) + { + return m_pSpeakerDescriptor->Capabilities.Volume; + } + + return FALSE; +} + +//============================================================================= +#pragma code_seg("PAGE") +PVOID +A2dpHpDevice::GetVolumeSettings +( + _In_ eDeviceType deviceType, + _Out_ PULONG Size +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + ASSERT(Size != NULL); + + if (deviceType == eA2dpHpSpeakerDevice) + { + *Size = m_pSpeakerDescriptor->VolumePropertyValuesSize; + return m_SpeakerVolumePropValues; + } + + return NULL; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::GetVolume +( + _In_ eDeviceType deviceType, + _In_ LONG Channel, + _Out_ LONG *pVolume +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Channel); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS status = STATUS_SUCCESS; + + ASSERT(pVolume); + + if (eA2dpHpSpeakerDevice == deviceType) + { + status = GetA2dpHpSpeakerVolume(Channel, pVolume); + IF_FAILED_ACTION_JUMP( + status, + DPF(D_ERROR, ("Start: GetA2dpHpSpeakerVolume: failed, 0x%x", status)), + Done); + + InterlockedExchange(&m_SpeakerVolumeLevel, *pVolume); + } + else + { + status = STATUS_INVALID_PARAMETER; + } + +Done: + return status; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::SetVolume +( + _In_ eDeviceType deviceType, + _In_ LONG Channel, + _In_ LONG Volume +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS status = STATUS_SUCCESS; + + if (eA2dpHpSpeakerDevice == deviceType) + { + status = SetA2dpHpSpeakerVolume(Channel, Volume); + } + else + { + status = STATUS_INVALID_PARAMETER; + } + + return status; +} + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +A2dpHpDevice::IsMuteSupported +( + _In_ eDeviceType deviceType +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eA2dpHpSpeakerDevice) + { + return m_pSpeakerDescriptor->Capabilities.Mute; + } + + return FALSE; +} + +//============================================================================= +#pragma code_seg("PAGE") +PVOID +A2dpHpDevice::GetMuteSettings +( + _In_ eDeviceType deviceType, + _Out_ PULONG Size +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + ASSERT(Size != NULL); + + if (deviceType == eA2dpHpSpeakerDevice) + { + *Size = m_pSpeakerDescriptor->MutePropertyValuesSize; + return m_SpeakerMutePropValues; + } + + return NULL; +} + +//============================================================================= +#pragma code_seg("PAGE") +LONG +A2dpHpDevice::GetMute +( + _In_ eDeviceType deviceType, + _In_ LONG Channel +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Channel); + DPF_ENTER(("[%!FUNC!]")); + + LONG ulMute = 0; + NTSTATUS status = STATUS_SUCCESS; + + if (eA2dpHpSpeakerDevice == deviceType) + { + status = GetA2dpHpSpeakerMute(Channel, &ulMute); + } + else + { + status = STATUS_INVALID_PARAMETER; + } + + if (NT_SUCCESS(status)) + { + return ulMute; + } + + return 0; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::SetMute +( + _In_ eDeviceType deviceType, + _In_ LONG Channel, + _In_ LONG Mute +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS status = STATUS_SUCCESS; + + if (eA2dpHpSpeakerDevice == deviceType) + { + status = SetA2dpHpSpeakerMute(Channel, Mute); + } + else + { + status = STATUS_INVALID_PARAMETER; + } + + return status; +} + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +A2dpHpDevice::GetConnectionStatus() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + return (BOOL)InterlockedCompareExchange(&m_ConnectionStatusLong, 0, 0); +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::GetA2dpHpCodecCaps +( + _Outptr_ PA2DPHPDEVICE_CODEC_CAPABILITIES* ppCodecCaps +) +{ + PAGED_CODE(); + DPF_ENTER(("[A2dpHpDevice::GetA2dpHpCodecCaps]")); + + ASSERT(ppCodecCaps != NULL); + + NTSTATUS ntStatus = STATUS_SUCCESS; + WDF_OBJECT_ATTRIBUTES attributes; + WDF_REQUEST_REUSE_PARAMS reuseParams; + WDFREQUEST req = NULL; + ULONG_PTR information = 0; + PBYTE pOutput = NULL; + SIDEBANDAUDIO_SIOP_REQUEST_PARAM rp = { 0 }; + + *ppCodecCaps = NULL; + rp.EpIndex = m_SpeakerEpIndex; + rp.RequestedSiop.ParamSet = SIDEBANDAUDIO_PARAMS_SET_A2DP; + rp.RequestedSiop.TypeId = SIDEANDAUDIO_PARAM_A2DP_CONFIGURED_CODEC; + rp.RequestedSiop.Size = 0; + + // + // Allocate and format a WDF request. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &req); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate failed, 0x%x", ntStatus)), + Done); + + // + // Get the SIOP buffer size. + // + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_SBAUD_GET_SIOP, + sizeof(rp), + 0, + &rp, + NULL); + + if (ntStatus == STATUS_BUFFER_TOO_SMALL) + { + information = WdfRequestGetInformation(req); + if (information == 0 || information > ULONG_MAX) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + DPF(D_ERROR, ("%!FUNC!: IOCTL_SBAUD_GET_SIOP invalid buffer size (%Id): 0x%x", information, ntStatus)); + goto Done; + } + + rp.RequestedSiop.Size = (ULONG)information; + ntStatus = STATUS_SUCCESS; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetA2dpHpCodecCaps: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_SIOP, SIDEANDAUDIO_PARAM_A2DP_CONFIGURED_CODEC) failed, 0x%x", ntStatus)), + Done); + + pOutput = (PBYTE)ExAllocatePool2(POOL_FLAG_NON_PAGED, rp.RequestedSiop.Size, A2DPSIDEBANDTEST_POOLTAG07); + if (pOutput == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetA2dpHpCodecCaps: out of memory")), + Done); + + WDF_REQUEST_REUSE_PARAMS_INIT( + &reuseParams, + WDF_REQUEST_REUSE_NO_FLAGS, + STATUS_SUCCESS); + ntStatus = WdfRequestReuse(req, &reuseParams); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus)), + Done); + + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_SBAUD_GET_SIOP, + sizeof(rp), + rp.RequestedSiop.Size, + &rp, + pOutput); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetA2dpHpCodecCaps: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_SIOP, SIDEANDAUDIO_PARAM_A2DP_CONFIGURED_CODEC) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *ppCodecCaps = (PA2DPHPDEVICE_CODEC_CAPABILITIES)pOutput; +Done: + if (req != NULL) + { + WdfObjectDelete(req); + req = NULL; + } + + if (!NT_SUCCESS(ntStatus)) + { + if (pOutput) + { + ExFreePoolWithTag(pOutput, A2DPSIDEBANDTEST_POOLTAG07); + pOutput = NULL; + } + } + + return ntStatus; +} + +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::UpdateA2dpHpCodecCaps +( + _In_ PA2DPHPDEVICE_CODEC_CAPABILITIES pCodecCaps +) +{ + PAGED_CODE(); + PA2DPHPDEVICE_CODEC_CAPABILITIES oldCodecCaps = NULL; + BOOL capabilitiesChanged = FALSE; + + ExAcquireFastMutex(&m_ConnectedCodecLock); + oldCodecCaps = m_ConnectedCodecCaps; + m_ConnectedCodecCaps = pCodecCaps; + // + // Check if any codec configuration parameters changed. + // + if ((m_ConnectedCodecCaps != NULL) && (oldCodecCaps != NULL)) + { + capabilitiesChanged = (RtlCompareMemory(oldCodecCaps, m_ConnectedCodecCaps, sizeof(*oldCodecCaps)) != sizeof(*oldCodecCaps)); + } + ExReleaseFastMutex(&m_ConnectedCodecLock); + + if (capabilitiesChanged) + { + // Notify audio miniport about this change. + if (m_SpeakerFormatChangeCallback.Handler != NULL) + { + m_SpeakerFormatChangeCallback.Handler(m_SpeakerFormatChangeCallback.Context); + } + } + + if (oldCodecCaps != NULL) + { + ExFreePoolWithTag(oldCodecCaps, A2DPSIDEBANDTEST_POOLTAG07); + oldCodecCaps = NULL; + } + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::Connect() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + return STATUS_DEVICE_FEATURE_NOT_SUPPORTED; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::Disconnect() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + return STATUS_DEVICE_FEATURE_NOT_SUPPORTED; +} + +//============================================================================= +#pragma code_seg() +BOOL +A2dpHpDevice::GetStreamStatus(_In_ eDeviceType deviceType) +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (deviceType == eA2dpHpSpeakerDevice) + { + ntStatus = (NTSTATUS)InterlockedCompareExchange(&m_SpeakerStreamStatusLong, 0, 0); + } + + return NT_SUCCESS(ntStatus) ? TRUE : FALSE; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::SpeakerStreamOpen() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + NTSTATUS ntStatus = STATUS_SUCCESS; + LONG nStreams = 0; + + ASSERT(m_nSpeakerStreams >= 0); + + nStreams = InterlockedIncrement(&m_nSpeakerStreams); + if (nStreams == 1) + { + BOOLEAN streamOpen = FALSE; + + ntStatus = SetA2dpHpStreamOpen(m_SpeakerEpIndex, + &A2dpHpSpeakerSupportedDeviceFormats[m_SpeakerSelectedFormat], + m_pSpeakerDescriptor); + + if (NT_SUCCESS(ntStatus)) + { + streamOpen = TRUE; + m_SpeakerStreamStatus = STATUS_SUCCESS; + ntStatus = EnableA2dpHpSpeakerStreamStatusNotification(); + } + + // + // Cleanup if any error. + // + if (!NT_SUCCESS(ntStatus)) + { + nStreams = InterlockedDecrement(&m_nSpeakerStreams); + ASSERT(nStreams == 0); + UNREFERENCED_VAR(nStreams); + + RtlZeroMemory(&m_SpeakerTransportResources, sizeof(A2DPHPDEVICE_EP_TRANSPORT_RESOURCES)); + + if (streamOpen) + { + SetA2dpHpStreamClose(); + } + + m_SpeakerStreamStatus = STATUS_INVALID_DEVICE_STATE; + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::StreamOpen(_In_ eDeviceType deviceType) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eA2dpHpSpeakerDevice) + { + return SpeakerStreamOpen(); + } + + return STATUS_INVALID_PARAMETER; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS A2dpHpDevice::SpeakerStreamStart() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + LONG nStreams = 0; + + ASSERT(m_nSpeakerStartedStreams >= 0); + nStreams = InterlockedIncrement(&m_nSpeakerStartedStreams); + + if (nStreams == 1) + { + ntStatus = SetA2dpHpStreamStart(); + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::StreamStart(_In_ eDeviceType deviceType) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eA2dpHpSpeakerDevice) + { + return SpeakerStreamStart(); + } + + return STATUS_INVALID_PARAMETER; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::StreamSuspend(_In_ eDeviceType deviceType) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eA2dpHpSpeakerDevice) + { + return SpeakerStreamSuspend(); + } + + return STATUS_INVALID_PARAMETER; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS A2dpHpDevice::SpeakerStreamSuspend() +{ + PAGED_CODE(); + NTSTATUS ntStatus = STATUS_SUCCESS; + LONG nStreams = 0; + ASSERT(m_nSpeakerStartedStreams > 0); + nStreams = InterlockedDecrement(&m_nSpeakerStartedStreams); + + if (m_nSpeakerStartedStreams == 0) + { + ntStatus = SetA2dpHpStreamSuspend(); + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::SpeakerStreamClose() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + LONG nStreams = 0; + + ASSERT(m_nSpeakerStreams > 0); + + nStreams = InterlockedDecrement(&m_nSpeakerStreams); + if (nStreams == 0) + { + ntStatus = SetA2dpHpStreamClose(); + + RtlZeroMemory(&m_SpeakerTransportResources, sizeof(A2DPHPDEVICE_EP_TRANSPORT_RESOURCES)); + + StopA2dpHpSpeakerStreamStatusNotification(); + + m_SpeakerStreamStatus = STATUS_INVALID_DEVICE_STATE; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::StreamClose(_In_ eDeviceType deviceType) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eA2dpHpSpeakerDevice) + { + return SpeakerStreamClose(); + } + + return STATUS_INVALID_PARAMETER; +} + +//============================================================================= +#pragma code_seg("PAGE") +GUID +A2dpHpDevice::GetContainerId(_In_ eDeviceType deviceType) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eA2dpHpSpeakerDevice) + { + return m_pSpeakerDescriptor->ContainerId; + } + else + return { 0 }; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +A2dpHpDevice::SetVolumeHandler +( + _In_ eDeviceType deviceType, + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eA2dpHpSpeakerDevice) + { + ASSERT(EventHandler == NULL || m_SpeakerVolumeCallback.Handler == NULL); + + m_SpeakerVolumeCallback.Handler = EventHandler; // weak ref. + m_SpeakerVolumeCallback.Context = EventHandlerContext; + } +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +A2dpHpDevice::SetMuteHandler +( + _In_ eDeviceType deviceType, + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eA2dpHpSpeakerDevice) + { + ASSERT(EventHandler == NULL || m_SpeakerMuteCallback.Handler == NULL); + + m_SpeakerMuteCallback.Handler = EventHandler; // weak ref. + m_SpeakerMuteCallback.Context = EventHandlerContext; + } +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +A2dpHpDevice::SetConnectionStatusHandler +( + _In_ eDeviceType deviceType, + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eA2dpHpSpeakerDevice) + { + ASSERT(EventHandler == NULL || m_SpeakerConnectionStatusCallback.Handler == NULL); + + m_SpeakerConnectionStatusCallback.Handler = EventHandler; // weak ref. + m_SpeakerConnectionStatusCallback.Context = EventHandlerContext; + } + +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +A2dpHpDevice::SetFormatChangeHandler +( + _In_ eDeviceType deviceType, + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + //m_SpeakerFormatChangeCallback + //m_MicFormatChangeCallback + if (deviceType == eA2dpHpSpeakerDevice) + { + ASSERT(EventHandler == NULL || m_SpeakerFormatChangeCallback.Handler == NULL); + + m_SpeakerFormatChangeCallback.Handler = EventHandler; // weak ref. + m_SpeakerFormatChangeCallback.Context = EventHandlerContext; + } + +} + +//============================================================================= +#pragma code_seg("PAGE") +PPIN_DEVICE_FORMATS_AND_MODES +A2dpHpDevice::GetFormatsAndModes +( + _In_ eDeviceType deviceType +) +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER(deviceType); + PPIN_DEVICE_FORMATS_AND_MODES pFormatsAndModes = NULL; + return pFormatsAndModes; +} + +//============================================================================= +#pragma code_seg() +_IRQL_requires_max_(DISPATCH_LEVEL) +BOOL +A2dpHpDevice::IsNRECSupported() +{ + DPF_ENTER(("[%!FUNC!]")); + + return FALSE; +} + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +A2dpHpDevice::GetNRECDisableStatus() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + // Return TRUE if HF wants to disable the NREC on the AG. + return FALSE; +} + +// +// Helper functions. +// + +//============================================================================= +#pragma code_seg() +NTSTATUS +A2dpHpDevice::SendIoCtrlAsynchronously +( + _In_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_opt_ WDFMEMORY MemIn, + _In_opt_ WDFMEMORY MemOut, + _In_ PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine, + _In_ WDFCONTEXT Context +) +/*++ + +Routine Description: + + This function aynchronously sends an I/O Ctrl request to the A2DP Headphone + device. + +--*/ +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BOOLEAN fSent = FALSE; + + // + // Format and send the request. + // + ntStatus = WdfIoTargetFormatRequestForIoctl( + m_WdfIoTarget, + Request, + IoControlCode, + MemIn, + NULL, + MemOut, + NULL); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfIoTargetFormatRequestForIoctl(0x%x) failed, 0x%x", IoControlCode, ntStatus)), + Done); + + WdfRequestSetCompletionRoutine( + Request, + CompletionRoutine, + Context); + + fSent = WdfRequestSend(Request, m_WdfIoTarget, NULL); // no options. + if (fSent == FALSE) + { + ntStatus = WdfRequestGetStatus(Request); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + } + + DPF(D_ERROR, ("%!FUNC!: WdfRequestSend(0x%x) failed, 0x%x", IoControlCode, ntStatus)); + goto Done; + } + + // + // All Done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::SendIoCtrlSynchronously +( + _In_opt_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_ ULONG InLength, + _In_ ULONG OutLength, + _When_(InLength > 0 || OutLength > 0, _In_) + _When_(InLength == 0 && OutLength == 0, _In_opt_) + PVOID Buffer +) +/*++ + +Routine Description: + + This function inits and synchronously sends an I/O Ctrl request to the A2DP Headphone + device. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PWDF_MEMORY_DESCRIPTOR memInPtr = NULL; + PWDF_MEMORY_DESCRIPTOR memOutPtr = NULL; + WDF_MEMORY_DESCRIPTOR memIn; + WDF_MEMORY_DESCRIPTOR memOut; + WDF_REQUEST_SEND_OPTIONS reqOpts; + + // + // Format and send the request. + // + if (InLength) + { + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memIn, Buffer, InLength); + memInPtr = &memIn; + } + + if (OutLength) + { + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memOut, Buffer, OutLength); + memOutPtr = &memOut; + } + + WDF_REQUEST_SEND_OPTIONS_INIT( + &reqOpts, + WDF_REQUEST_SEND_OPTION_TIMEOUT | + WDF_REQUEST_SEND_OPTION_SYNCHRONOUS); + + reqOpts.Timeout = WDF_REL_TIMEOUT_IN_SEC(A2DP_SIDEBAND_SYNC_REQ_TIMEOUT_IN_SEC); + + ntStatus = WdfIoTargetSendIoctlSynchronously( + m_WdfIoTarget, + Request, + IoControlCode, + memInPtr, + memOutPtr, + &reqOpts, + NULL); // bytes returned. + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_VERBOSE, ("%!FUNC!: WdfIoTargetSendIoctlSynchronously(0x%x) failed, 0x%x", IoControlCode, ntStatus)), + Done); + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::SendIoCtrlSynchronously +( + _In_opt_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_ ULONG InLength, + _In_ ULONG OutLength, + _When_(InLength > 0, _In_) + _When_(InLength == 0, _In_opt_) + PVOID InBuffer, + _When_(OutLength > 0, _In_) + _When_(OutLength == 0, _In_opt_) + PVOID OutBuffer +) +/*++ + +Routine Description: + + This function inits and synchronously sends an I/O Ctrl request to the A2DP Headphone + device. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PWDF_MEMORY_DESCRIPTOR memInPtr = NULL; + PWDF_MEMORY_DESCRIPTOR memOutPtr = NULL; + WDF_MEMORY_DESCRIPTOR memIn; + WDF_MEMORY_DESCRIPTOR memOut; + WDF_REQUEST_SEND_OPTIONS reqOpts; + + // + // Format and send the request. + // + if (InLength) + { + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memIn, InBuffer, InLength); + memInPtr = &memIn; + } + + if (OutLength) + { + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memOut, OutBuffer, OutLength); + memOutPtr = &memOut; + } + + WDF_REQUEST_SEND_OPTIONS_INIT( + &reqOpts, + WDF_REQUEST_SEND_OPTION_TIMEOUT | + WDF_REQUEST_SEND_OPTION_SYNCHRONOUS); + + reqOpts.Timeout = WDF_REL_TIMEOUT_IN_SEC(A2DP_SIDEBAND_SYNC_REQ_TIMEOUT_IN_SEC); + + ntStatus = WdfIoTargetSendIoctlSynchronously( + m_WdfIoTarget, + Request, + IoControlCode, + memInPtr, + memOutPtr, + &reqOpts, + NULL); // bytes returned. + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_VERBOSE, ("%!FUNC!: WdfIoTargetSendIoctlSynchronously(0x%x) failed, 0x%x", IoControlCode, ntStatus)), + Done); + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +VOID +A2dpHpDevice::EvtA2dpHpDeviceNotificationStatusWorkItem +( + _In_ WDFWORKITEM WorkItem +) +/*++ + +Routine Description: + + The function processes status notification updates. + +Arguments: + + WorkItem - WDF work-item object. + +--*/ +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + A2dpHpDevice * This; + KIRQL oldIrql; + + if (WorkItem == NULL) + { + return; + } + + This = GetA2dpHpDeviceNotificationWorkItemContext(WorkItem)->A2dpHpDevice; + ASSERT(This != NULL); + + for (;;) + { + BOOL resend = TRUE; + WDFREQUEST req = NULL; + A2dpHpDeviceNotificationReqContext * reqCtx; + WDF_REQUEST_COMPLETION_PARAMS params; + + // + // Retrieve a task. + // + KeAcquireSpinLock(&This->m_Lock, &oldIrql); + req = (WDFREQUEST) WdfCollectionGetFirstItem(This->m_ReqCollection); + if (req != NULL) + { + WdfCollectionRemove(This->m_ReqCollection, req); + } + KeReleaseSpinLock(&This->m_Lock, oldIrql); + + if (req == NULL) + { + break; + } + + // + // Get request parameters and context. + // + WDF_REQUEST_COMPLETION_PARAMS_INIT(¶ms); + WdfRequestGetCompletionParams(req, ¶ms); + + reqCtx = GetA2dpHpDeviceNotificationReqContext(req); + ASSERT(reqCtx != NULL); + + // + // Handle this notification. + // + if (NT_SUCCESS(params.IoStatus.Status)) + { + switch(params.Parameters.Ioctl.IoControlCode) + { + case IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE: + { + LONG oldVolume; + + // Prepare for request resent + reqCtx->Buffer.Volume.Immediate = FALSE; + + if (reqCtx->Buffer.Volume.EpIndex == This->m_SpeakerEpIndex) + { + oldVolume = InterlockedExchange(&This->m_SpeakerVolumeLevel, reqCtx->Buffer.Volume.Value); + if (reqCtx->Buffer.Volume.Value != oldVolume) + { + // Notify audio miniport about this change. + if (This->m_SpeakerVolumeCallback.Handler != NULL) + { + This->m_SpeakerVolumeCallback.Handler( + This->m_SpeakerVolumeCallback.Context); + } + } + } + + } + break; + + case IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE: + { + LONG oldMute; + + // Prepare for request resent + reqCtx->Buffer.Mute.Immediate = FALSE; + + if (reqCtx->Buffer.Mute.EpIndex == This->m_SpeakerEpIndex) + { + oldMute = InterlockedExchange(&This->m_SpeakerMute, reqCtx->Buffer.Mute.Value); + if (reqCtx->Buffer.Mute.Value != oldMute) + { + // Notify audio miniport about this change. + if (This->m_SpeakerMuteCallback.Handler != NULL) + { + This->m_SpeakerMuteCallback.Handler( + This->m_SpeakerMuteCallback.Context); + } + } + } + + } + break; + + case IOCTL_SBAUD_GET_CONNECTION_STATUS_UPDATE: + { + BOOL oldStatus = (BOOL)InterlockedExchange(&This->m_ConnectionStatusLong, (LONG)reqCtx->Buffer.ConnectionStatus.Connected); + if (reqCtx->Buffer.ConnectionStatus.Connected != oldStatus) + { + // Notify audio miniport about this change. + if (This->m_SpeakerConnectionStatusCallback.Handler != NULL) + { + This->m_SpeakerConnectionStatusCallback.Handler( + This->m_SpeakerConnectionStatusCallback.Context); + } + } + } + break; + + case IOCTL_SBAUD_GET_SIOP_UPDATE: + { + if ((reqCtx->Buffer.SiopUpdate.EpIndex == This->m_SpeakerEpIndex) && + (reqCtx->Buffer.SiopUpdate.RequestedSiop.ParamSet == SIDEBANDAUDIO_PARAMS_SET_A2DP) && + (reqCtx->Buffer.SiopUpdate.RequestedSiop.TypeId == SIDEANDAUDIO_PARAM_A2DP_CONFIGURED_CODEC)) + { + PA2DPHPDEVICE_CODEC_CAPABILITIES codecCaps = NULL; + ntStatus = This->GetA2dpHpCodecCaps(&codecCaps); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = This->UpdateA2dpHpCodecCaps(codecCaps); + } + } + } + break; + + default: + // This should never happen. + resend = FALSE; + DPF(D_ERROR, ("%!FUNC!: invalid request ctrl 0x%x", + params.Parameters.Ioctl.IoControlCode)); + break; + } + } + + if (resend) + { + WDF_REQUEST_REUSE_PARAMS reuseParams; + + WDF_REQUEST_REUSE_PARAMS_INIT( + &reuseParams, + WDF_REQUEST_REUSE_NO_FLAGS, + STATUS_SUCCESS); + + ntStatus = WdfRequestReuse(req, &reuseParams); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus)); + break; + } + + // Resend status notification request. + ntStatus = This->SendIoCtrlAsynchronously( + req, + params.Parameters.Ioctl.IoControlCode, + reqCtx->MemIn, + reqCtx->MemOut, + EvtA2dpHpDeviceNotificationStatusCompletion, + This); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlAsynchronously" + "(0x%x) failed, 0x%x", + params.Parameters.Ioctl.IoControlCode, ntStatus)); + break; + } + } + } +} + +//============================================================================= +#pragma code_seg() +void +A2dpHpDevice::EvtA2dpHpDeviceNotificationStatusCompletion +( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, + _In_ WDFCONTEXT Context +) +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + A2dpHpDeviceNotificationReqContext *ctx = NULL; + A2dpHpDevice *This = NULL; + KIRQL oldIrql; + + UNREFERENCED_PARAMETER(Target); + UNREFERENCED_PARAMETER(Context); + + ctx = GetA2dpHpDeviceNotificationReqContext(Request); + This = ctx->A2dpHpDevice; + ASSERT(This != NULL); + + ntStatus = Params->IoStatus.Status; + if (ntStatus == STATUS_CANCELLED) + { + // A2DP device is shutting down. Do not re-send this request. + goto Done; + } + + // + // If something is wrong with the A2DP interface, do not loop forever. + // + if (!NT_SUCCESS(ntStatus)) + { + if (++ctx->Errors > A2DP_SIDEBAND_NOTIFICATION_MAX_ERROR_COUNT) + { + // Too many errors. Do not re-send this request. + goto Done; + } + } + else + { + // reset the # of errors. + ctx->Errors = 0; + } + + // + // Let the work-item thread process this request. + // + KeAcquireSpinLock(&This->m_Lock, &oldIrql); + + ntStatus = WdfCollectionAdd(This->m_ReqCollection, Request); + if (NT_SUCCESS(ntStatus)) + { + WdfWorkItemEnqueue(This->m_WorkItem); + } + + KeReleaseSpinLock(&This->m_Lock, oldIrql); + +Done:; +} + +//============================================================================= + +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::SetA2dpHpDeviceCapabilities +( +) +/*++ + +Routine Description: + +This function synchronously informs the Audio Driver capabilities to A2DP stack. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[A2dpHpDevice::SetA2dpHpDeviceCapabilities]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + typedef struct _SidebandDeviceSupportedCodecs + { + A2DP_SBC_MEDIA_HEADER SupportedSbcCodec; + A2DP_AAC_MEDIA_HEADER SupportedAacCodec; + } SidebandDeviceSupportedCodecs, * PSidebandDeviceSupportedCodecs; + + typedef struct _SidebandDeviceCapsInputParams + { + SIDEBANDAUDIO_SIOP_REQUEST_PARAM DeviceCapsHeader; + SidebandDeviceSupportedCodecs SupportedCodecs; + }SidebandDeviceCapsInputParams, * PSidebandDeviceCapsInputParams; + + SidebandDeviceCapsInputParams deviceCapsParams = { 0 }; + deviceCapsParams.DeviceCapsHeader.RequestedSiop = { SIDEBANDAUDIO_PARAMS_SET_A2DP, SIDEBANDAUDIO_PARAM_A2DP_CODECS, sizeof(deviceCapsParams.SupportedCodecs) }; + deviceCapsParams.DeviceCapsHeader.EpIndex = 0; + // + // Set SBC codec settings. + // + deviceCapsParams.SupportedCodecs.SupportedSbcCodec.CategoryCodecHeader.SvcCategory = 0x7; // Service category is media codec. + deviceCapsParams.SupportedCodecs.SupportedSbcCodec.CategoryCodecHeader.Losc = A2DP_SBC_MEDIA_HEADER::CorrectLosc(); + deviceCapsParams.SupportedCodecs.SupportedSbcCodec.CodecType = 0x0; // Codec type is SBC + deviceCapsParams.SupportedCodecs.SupportedSbcCodec.MediaType = 0x0; // Media type is audio + deviceCapsParams.SupportedCodecs.SupportedSbcCodec.ChannelMode = 0x1; // Channel Mode is joint stereo + deviceCapsParams.SupportedCodecs.SupportedSbcCodec.SamplingFrequency = 0x3; // 48 and 44.1 kHz + deviceCapsParams.SupportedCodecs.SupportedSbcCodec.AllocMethod = 0x1; // Allocation method is loudness + deviceCapsParams.SupportedCodecs.SupportedSbcCodec.Subbands = 0x1; // 8 subbands + deviceCapsParams.SupportedCodecs.SupportedSbcCodec.BlockLength = 0x1; + deviceCapsParams.SupportedCodecs.SupportedSbcCodec.MinBitpoolVal = 0x2; + deviceCapsParams.SupportedCodecs.SupportedSbcCodec.MaxBitpoolVal = 0x53; + + // + // Set AAC codec settings. + // + deviceCapsParams.SupportedCodecs.SupportedAacCodec.CategoryCodecHeader.SvcCategory = 0x7; // Service category is media codec. + deviceCapsParams.SupportedCodecs.SupportedAacCodec.CategoryCodecHeader.Losc = A2DP_AAC_MEDIA_HEADER::CorrectLosc(); + deviceCapsParams.SupportedCodecs.SupportedAacCodec.MediaType = 0x0; // Media type is audio. + deviceCapsParams.SupportedCodecs.SupportedAacCodec.CodecType = 0x2; // Codec type is MPEG 2 AAC + deviceCapsParams.SupportedCodecs.SupportedAacCodec.ObjectType = 0x80; // Object type is AAC MPEG 2 LC + deviceCapsParams.SupportedCodecs.SupportedAacCodec.SamplingFrequencyLower4Bits = 0x8; // Sampling frequency is 48kHz. + deviceCapsParams.SupportedCodecs.SupportedAacCodec.Channels = 0x1; // Dual channel. + deviceCapsParams.SupportedCodecs.SupportedAacCodec.VariableBitRate = 1; // Vbr is enabled. + // Set bitrate to 256kbps + deviceCapsParams.SupportedCodecs.SupportedAacCodec.BitRateUpper7Bits = 0x3; + deviceCapsParams.SupportedCodecs.SupportedAacCodec.BitRateMiddle8Bits = 0xe8; + deviceCapsParams.SupportedCodecs.SupportedAacCodec.BitRateLower8Bits = 0; + + // + // Set the audio codec capabilities SIOP. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_SET_SIOP, + sizeof(deviceCapsParams), + 0, + &deviceCapsParams); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetA2dpHpDeviceCapabilities: SendIoCtrlSynchronously(IOCTL_SBAUD_SET_SIOP) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::GetA2dpHpDescriptor +( + _Out_ PSIDEBANDAUDIO_DEVICE_DESCRIPTOR *Descriptor +) +/*++ + +Routine Description: + + This function synchronously gets the remote A2DP Headphone Device descriptor. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + WDFREQUEST req = NULL; + PSIDEBANDAUDIO_DEVICE_DESCRIPTOR descriptor = NULL; + ULONG length = 0; + ULONG_PTR information = 0; + WDF_REQUEST_REUSE_PARAMS reuseParams; + WDF_OBJECT_ATTRIBUTES attributes; + + *Descriptor = NULL; + + // + // Allocate and format a WDF request. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &req); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate failed, 0x%x", ntStatus)), + Done); + + // + // Get the size of the buffer. + // + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_SBAUD_GET_DEVICE_DESCRIPTOR, + NULL, + NULL, + NULL); + + if (ntStatus != STATUS_BUFFER_TOO_SMALL) + { + if (NT_SUCCESS(ntStatus)) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + } + + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_DEVICE_DESCRIPTOR): failed, 0x%x", ntStatus)); + goto Done; + } + + ntStatus = STATUS_SUCCESS; + + information = WdfRequestGetInformation(req); + if (information == 0 || information > ULONG_MAX) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + DPF(D_ERROR, ("%!FUNC!: IOCTL_SBAUD_GET_DEVICE_DESCRIPTOR buffer too big (%Id): 0x%x", information, ntStatus)); + goto Done; + } + + length = (ULONG)information; + + // + // Allocate memory needed to hold the info. + // + descriptor = (PSIDEBANDAUDIO_DEVICE_DESCRIPTOR) ExAllocatePool2(POOL_FLAG_NON_PAGED, length, MINADAPTER_POOLTAG); + if (descriptor == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: ExAllocatePool2 failed, out of memory")), + Done); + + // + // Get the A2DP Headphone descriptor. + // + WDF_REQUEST_REUSE_PARAMS_INIT( + &reuseParams, + WDF_REQUEST_REUSE_NO_FLAGS, + STATUS_SUCCESS); + + ntStatus = WdfRequestReuse(req, &reuseParams); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus)), + Done); + + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_SBAUD_GET_DEVICE_DESCRIPTOR, + NULL, + length, + descriptor); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_DEVICE_DESCRIPTOR) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *Descriptor = descriptor; + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + if (descriptor != NULL) + { + ExFreePoolWithTag(descriptor, MINADAPTER_POOLTAG); + } + } + + if (req != NULL) + { + WdfObjectDelete(req); + req = NULL; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::GetA2dpHpEndpointDescriptor +( + _In_ ULONG EpIndex, + _Out_ PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 *EpDescriptor +) +/*++ + +Routine Description: + + This function synchronously gets the A2DP Headphone Endpoint Descriptor. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + WDFREQUEST req = NULL; + PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 descriptor = NULL; + ULONG length = 0; + ULONG_PTR information = 0; + WDF_REQUEST_REUSE_PARAMS reuseParams; + WDF_OBJECT_ATTRIBUTES attributes; + + *EpDescriptor = NULL; + + // + // Allocate and format a WDF request. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &req); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate failed, 0x%x", ntStatus)), + Done); + + // + // Get the size of the buffer. + // + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR2, + sizeof(EpIndex), + NULL, + &EpIndex); + + if (ntStatus != STATUS_BUFFER_TOO_SMALL) + { + if (NT_SUCCESS(ntStatus)) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + } + + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR): failed, 0x%x", ntStatus)); + goto Done; + } + + ntStatus = STATUS_SUCCESS; + + information = WdfRequestGetInformation(req); + if (information == 0 || information > ULONG_MAX) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + DPF(D_ERROR, ("%!FUNC!: IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR buffer too big (%Id): 0x%x", information, ntStatus)); + goto Done; + } + + length = (ULONG)information; + + // + // Allocate memory needed to hold the info. + // + descriptor = (PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2) ExAllocatePool2(POOL_FLAG_NON_PAGED, length, MINADAPTER_POOLTAG); + if (descriptor == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: ExAllocatePool2 failed, out of memory")), + Done); + + // + // Get the A2DP Headphone descriptor. + // + WDF_REQUEST_REUSE_PARAMS_INIT( + &reuseParams, + WDF_REQUEST_REUSE_NO_FLAGS, + STATUS_SUCCESS); + + ntStatus = WdfRequestReuse(req, &reuseParams); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus)), + Done); + + // Same buffer used for input and output + // Cast into ULONG to provide EpIndex + *((ULONG *)descriptor) = EpIndex; + + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR2, + sizeof(EpIndex), + length, + descriptor); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *EpDescriptor = descriptor; + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + if (descriptor != NULL) + { + ExFreePoolWithTag(descriptor, MINADAPTER_POOLTAG); + } + } + + if (req != NULL) + { + WdfObjectDelete(req); + req = NULL; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::VerifyEndpointFormatCompatibility +( + _In_ PSIDEBANDAUDIO_SUPPORTED_FORMATS EpSupportedFormats, + _In_ PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pHardwareSupportedFormats, + _In_ ULONG NumHardwareSupportedFormats, + _Out_ PULONG pHardwareFormatIndex +) +{ + PAGED_CODE(); + + NTSTATUS status = STATUS_NOT_SUPPORTED; + ULONG iHardwareFormats = 0; + ULONG iEpDataFormats = 0; + BOOL bFound = FALSE; + + // Verify here whether hardware/dsp mixer can support the A2DP device formats + // This format will be later used to open the ISOCH channel + + for (iHardwareFormats = 0; iHardwareFormats < NumHardwareSupportedFormats; iHardwareFormats++) + { + // for each format supported by hardware, search in EP data formats + for (iEpDataFormats = 0; iEpDataFormats < EpSupportedFormats->NumFormats; iEpDataFormats++) + { + PKSDATAFORMAT pEpDataFormat = (PKSDATAFORMAT)EpSupportedFormats->Formats[iEpDataFormats]; + + // Verify KSDATAFORMAT + if (pEpDataFormat->FormatSize >= sizeof(KSDATAFORMAT)) + { + if (pEpDataFormat->FormatSize >= sizeof(KSDATAFORMAT_WAVEFORMATEX)) + { + // Check other parameters as well + // Ignoring for simplicity + if ((((PKSDATAFORMAT_WAVEFORMATEX)pEpDataFormat)->WaveFormatEx.nSamplesPerSec == pHardwareSupportedFormats[iHardwareFormats].WaveFormatExt.Format.nSamplesPerSec) && + (((PKSDATAFORMAT_WAVEFORMATEX)pEpDataFormat)->WaveFormatEx.nChannels == pHardwareSupportedFormats[iHardwareFormats].WaveFormatExt.Format.nChannels) && + (((PKSDATAFORMAT_WAVEFORMATEX)pEpDataFormat)->WaveFormatEx.wBitsPerSample == pHardwareSupportedFormats[iHardwareFormats].WaveFormatExt.Format.wBitsPerSample)) + { + bFound = TRUE; + *pHardwareFormatIndex = iHardwareFormats; + status = STATUS_SUCCESS; + break; + } + } + } + } + } + + // Iterate through Sideband Device data range to figure out + // intersection with hardware supported formats + + return status; +} + + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS A2dpHpDevice::EnableA2dpHpConnectionStatusNotification() +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + A2dpHpDeviceNotificationReqContext* ctx = NULL; + + ctx = GetA2dpHpDeviceNotificationReqContext(m_ConnectionReq); + + // + // This is a notification request. + // + ctx->Buffer.ConnectionStatus.Immediate = FALSE; + ctx->Buffer.ConnectionStatus.EpIndex = m_SpeakerEpIndex; + ctx->Buffer.ConnectionStatus.Connected = FALSE; + + // + // Register for connection status updates. + // + ntStatus = SendIoCtrlAsynchronously( + m_ConnectionReq, + IOCTL_SBAUD_GET_CONNECTION_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtA2dpHpDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlAsynchronously(IOCTL_SBAUD_GET_CONNECTION_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +NTSTATUS +A2dpHpDevice::GetA2dpHpEndpointFormatsIntersection +( + _In_ PSIDEBANDAUDIO_SUPPORTED_FORMATS pDeviceFormats, + _Out_ PSIDEBANDAUDIO_SUPPORTED_FORMATS *ppSupportedFormatsIntersection +) +/*++ + +Routine Description: + + This function synchronously gets the A2DP Headphone supported formats for + a given endpoint. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + WDFREQUEST req = NULL; + PSIDEBANDAUDIO_SUPPORTED_FORMATS formats = NULL; + ULONG length = 0; + ULONG_PTR information = 0; + WDF_REQUEST_REUSE_PARAMS reuseParams; + WDF_OBJECT_ATTRIBUTES attributes; + + *ppSupportedFormatsIntersection = NULL; + + // + // Allocate and format a WDF request. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &req); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate failed, 0x%x", ntStatus)), + Done); + + // + // Get the size of the buffer. + // + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_SBAUD_GET_SUPPORTED_FORMATS, + pDeviceFormats->CbSize, + 0, + pDeviceFormats, + NULL); + + if (ntStatus != STATUS_BUFFER_TOO_SMALL) + { + if (NT_SUCCESS(ntStatus)) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + } + + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_SUPPORTED_FORMATS): failed, 0x%x", ntStatus)); + goto Done; + } + + ntStatus = STATUS_SUCCESS; + + information = WdfRequestGetInformation(req); + if (information == 0 || information > ULONG_MAX) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + DPF(D_ERROR, ("%!FUNC!: IOCTL_SBAUD_GET_SUPPORTED_FORMATS buffer too big (%Id): 0x%x", information, ntStatus)); + goto Done; + } + + length = (ULONG)information; + + // + // Allocate memory needed to hold the info. + // + formats = (PSIDEBANDAUDIO_SUPPORTED_FORMATS) ExAllocatePool2(POOL_FLAG_NON_PAGED, length, A2DPSIDEBANDTEST_POOLTAG03); + if (formats == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: ExAllocatePool2 failed, out of memory")), + Done); + + // + // Get the A2DP Headphone descriptor. + // + WDF_REQUEST_REUSE_PARAMS_INIT( + &reuseParams, + WDF_REQUEST_REUSE_NO_FLAGS, + STATUS_SUCCESS); + + ntStatus = WdfRequestReuse(req, &reuseParams); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus)), + Done); + + // Same buffer used for input and output + // Cast into ULONG to provide EpIndex + + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_SBAUD_GET_SUPPORTED_FORMATS, + pDeviceFormats->CbSize, + length, + pDeviceFormats, + formats); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_SUPPORTED_FORMATS) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *ppSupportedFormatsIntersection = formats; + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + if (formats != NULL) + { + ExFreePoolWithTag(formats, A2DPSIDEBANDTEST_POOLTAG03); + } + } + + if (req != NULL) + { + WdfObjectDelete(req); + req = NULL; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::GetA2dpHpVolumePropertyValues +( + _In_ ULONG EpIndex, + _In_ ULONG Length, + _Out_ PKSPROPERTY_DESCRIPTION *PropValues +) +/*++ + +Routine Description: + + This function synchronously gets the remote A2DP Headphone volume values. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PKSPROPERTY_DESCRIPTION propValues = NULL; + + *PropValues = NULL; + + // + // Allocate memory. + // + propValues = (PKSPROPERTY_DESCRIPTION) ExAllocatePool2(POOL_FLAG_NON_PAGED, Length, A2DPSIDEBANDTEST_POOLTAG02); + if (propValues == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: ExAllocatePool2 failed, out of memory")), + Done); + + // + // Send EpIndex in same buffer as input param + // + *((ULONG *)propValues) = EpIndex; + + // + // Get the A2DP Headphone volume property values. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_GET_VOLUMEPROPERTYVALUES, + sizeof(EpIndex), + Length, + propValues); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_VOLUMEPROPERTYVALUES) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *PropValues = propValues; + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + if (propValues != NULL) + { + ExFreePoolWithTag(propValues, MINADAPTER_POOLTAG); + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::SetA2dpHpSpeakerVolume +( + _In_ LONG Channel, + _In_ LONG Volume +) +/*++ + +Routine Description: + + This function synchronously sets the remote A2DP Headphone speaker volume. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + SIDEBANDAUDIO_VOLUME_PARAMS vp; + + // + // Set the A2DP Headphone speaker volume. + // + + vp.EpIndex = m_SpeakerEpIndex; + vp.Value = Volume; + vp.Channel = Channel; + vp.Immediate = TRUE; // not required for SET + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_SET_VOLUME, + sizeof(vp), + 0, + &vp); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_SET_VOLUME) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::GetA2dpHpSpeakerVolume +( + _In_ LONG Channel, + _Out_ LONG * Volume +) +/*++ + +Routine Description: + + This function synchronously gets the remote A2DP Headphone speaker volume. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + A2dpHpDeviceNotificationBuffer buffer = {0}; + + *Volume = 0; + + buffer.Volume.Immediate = TRUE; + buffer.Volume.EpIndex = m_SpeakerEpIndex; + buffer.Volume.Channel = Channel; + buffer.Volume.Value = 0; + + // + // Get the A2DP Headphone speaker volume. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE, + sizeof(buffer.Volume), + sizeof(buffer.Volume), + &buffer); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *Volume = buffer.Volume.Value; + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +A2dpHpDevice::EnableA2dpHpSpeakerVolumeStatusNotification() +/*++ + +Routine Description: + + This function registers for A2DP Headphone speaker + volume change notification. + +--*/ +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + A2dpHpDeviceNotificationReqContext *ctx = NULL; + + ctx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerVolumeReq); + + // + // This is a notification request. + // + ctx->Buffer.Volume.Immediate = FALSE; + ctx->Buffer.Volume.EpIndex = m_SpeakerEpIndex; + ctx->Buffer.Volume.Value = 0; + + // + // Register for speaker volume updates. + // + ntStatus = SendIoCtrlAsynchronously( + m_SpeakerVolumeReq, + IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtA2dpHpDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlAsynchronously(IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::GetA2dpHpMutePropertyValues +( + _In_ ULONG EpIndex, + _In_ ULONG Length, + _Out_ PKSPROPERTY_DESCRIPTION *PropValues +) +/*++ + +Routine Description: + + This function synchronously gets the remote A2DP Headphone mute values. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PKSPROPERTY_DESCRIPTION propValues = NULL; + *PropValues = NULL; + + // + // Allocate memory + // + propValues = (PKSPROPERTY_DESCRIPTION) ExAllocatePool2(POOL_FLAG_NON_PAGED, Length, A2DPSIDEBANDTEST_POOLTAG06); + if (propValues == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: ExAllocatePool2 failed, out of memory")), + Done); + + // + // Send EpIndex in same buffer as input param + // + *((ULONG*)propValues) = EpIndex; + + // + // Get the A2DP Headphone volume property values. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_GET_MUTEPROPERTYVALUES, + sizeof(EpIndex), + Length, + propValues); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_VOLUMEPROPERTYVALUES) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *PropValues = propValues; + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + if (propValues != NULL) + { + ExFreePoolWithTag(propValues, A2DPSIDEBANDTEST_POOLTAG06); + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::SetA2dpHpSpeakerMute +( + _In_ LONG Channel, + _In_ LONG Mute +) +/*++ + +Routine Description: + + This function synchronously sets the remote A2DP Headphone speaker mute. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + SIDEBANDAUDIO_MUTE_PARAMS mp; + + // + // Set the A2DP Headphone speaker mute. + // + + mp.EpIndex = m_SpeakerEpIndex; + mp.Value = Mute; + mp.Channel = Channel; + mp.Immediate = TRUE; // not required for SET + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_SET_MUTE, + sizeof(mp), + 0, + &mp); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_SET_MUTE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::GetA2dpHpSpeakerMute +( + _In_ LONG Channel, + _Out_ LONG *Mute +) +/*++ + +Routine Description: + + This function synchronously gets the remote A2DP Headphone speaker mute. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + A2dpHpDeviceNotificationBuffer buffer = {0}; + + *Mute = 0; + + buffer.Mute.Immediate = TRUE; + buffer.Mute.EpIndex = m_SpeakerEpIndex; + buffer.Mute.Channel = Channel; + buffer.Mute.Value = 0; + + // + // Get the A2DP Headphone speaker mute. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE, + sizeof(buffer.Mute), + sizeof(buffer.Mute), + &buffer); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *Mute = buffer.Mute.Value; + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +A2dpHpDevice::EnableA2dpHpSpeakerMuteStatusNotification() +/*++ + +Routine Description: + + This function registers for A2DP Headphone speaker + mute change notification. + +--*/ +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + A2dpHpDeviceNotificationReqContext *ctx = NULL; + + ctx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerMuteReq); + + // + // This is a notification request. + // + ctx->Buffer.Mute.Immediate = FALSE; + ctx->Buffer.Mute.EpIndex = m_SpeakerEpIndex; + ctx->Buffer.Mute.Value = 0; + + // + // Register for speaker mute updates. + // + ntStatus = SendIoCtrlAsynchronously( + m_SpeakerMuteReq, + IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtA2dpHpDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlAsynchronously(IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +#pragma code_seg("PAGE") +NTSTATUS A2dpHpDevice::GetA2dpHpConnectionStatus(BOOL* ConnectionStatus) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + A2dpHpDeviceNotificationBuffer buffer = { 0 }; + + *ConnectionStatus = FALSE; + + buffer.ConnectionStatus.Immediate = TRUE; + buffer.ConnectionStatus.EpIndex = m_SpeakerEpIndex; + + // + // Get the A2DP connection status. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_GET_CONNECTION_STATUS_UPDATE, + sizeof(buffer.ConnectionStatus), + sizeof(buffer.ConnectionStatus), + &buffer); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_CONNECTION_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *ConnectionStatus = buffer.ConnectionStatus.Connected; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::SetSidebandClaimed(_In_ BOOL bClaimed) +/*++ + +Routine Description: + + This function synchronously requests an open ISOCH channel to transmit audio + data over transport. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_SET_DEVICE_CLAIMED, + sizeof(BOOL), + 0, + &bClaimed); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_SET_DEVICE_CLAIMED) failed, 0x%x", ntStatus)), + Done); + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +void +A2dpHpDevice::EvtA2dpHpDeviceStreamStatusCompletion +( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, + _In_ WDFCONTEXT Context +) +/*++ + +Routine Description: + + Completion callback for the A2DP Headphone stream status notification. + +--*/ +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + A2dpHpDeviceNotificationReqContext *reqCtx = NULL; + A2dpHpDevice *This = NULL; + NTSTATUS ntResult = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(Target); + UNREFERENCED_PARAMETER(Context); + + // + // Get the A2DP stream status. + // + reqCtx = GetA2dpHpDeviceNotificationReqContext(Request); + This = reqCtx->A2dpHpDevice; + ASSERT(This != NULL); + + ntStatus = Params->IoStatus.Status; + if (!NT_SUCCESS(ntStatus)) + { + ntResult = STATUS_INVALID_DEVICE_STATE; + } + else + { + ntResult = reqCtx->Buffer.StreamStatus.Status; + } + + if (reqCtx->Buffer.StreamStatus.EpIndex == This->m_SpeakerEpIndex) + { + InterlockedExchange(&This->m_SpeakerStreamStatusLong, (LONG)ntResult); + + // + // Let the stop routine know we are done. Stop routine will + // re-init the request. + // + KeSetEvent(&This->m_SpeakerStreamStatusEvent, IO_NO_INCREMENT, FALSE); + } +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +A2dpHpDevice::EnableA2dpHpSpeakerStreamStatusNotification() +/*++ + +Routine Description: + + This function registers for A2DP Headphone stream status notification. + +--*/ +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + A2dpHpDeviceNotificationReqContext *ctx = NULL; + + ASSERT(m_nSpeakerStreams > 0); + + ctx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerStreamReq); + ctx->Buffer.StreamStatus.Immediate = FALSE; + ctx->Buffer.StreamStatus.EpIndex = m_SpeakerEpIndex; + + KeClearEvent(&m_SpeakerStreamStatusEvent); + + // + // Get the A2DP Headphone speaker stream status. + // + ntStatus = SendIoCtrlAsynchronously( + m_SpeakerStreamReq, + IOCTL_SBAUD_GET_STREAM_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtA2dpHpDeviceStreamStatusCompletion, + this); + + if (!NT_SUCCESS(ntStatus)) + { + KeSetEvent(&m_SpeakerStreamStatusEvent, IO_NO_INCREMENT, FALSE); + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlAsynchronously(IOCTL_SBAUD_GET_STREAM_STATUS_UPDATE) failed, 0x%x", ntStatus)); + goto Done; + } + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +NTSTATUS +A2dpHpDevice::EnableA2dpHpSpeakerSiopNotification() +/*++ + +Routine Description: + + This function registers for A2DP Headphone updates for the configured codec SIOP. + +--*/ +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + A2dpHpDeviceNotificationReqContext* ctx = NULL; + + ctx = GetA2dpHpDeviceNotificationReqContext(m_SiopUpdateReq); + ctx->Buffer.SiopUpdate.EpIndex = m_SpeakerEpIndex; + ctx->Buffer.SiopUpdate.RequestedSiop.ParamSet = SIDEBANDAUDIO_PARAMS_SET_A2DP; + ctx->Buffer.SiopUpdate.RequestedSiop.Size = 0; + ctx->Buffer.SiopUpdate.RequestedSiop.TypeId = SIDEANDAUDIO_PARAM_A2DP_CONFIGURED_CODEC; + + // + // Register for updates to the A2DP configured codec SIOP. + // + ntStatus = SendIoCtrlAsynchronously( + m_SiopUpdateReq, + IOCTL_SBAUD_GET_SIOP_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtA2dpHpDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlAsynchronously(IOCTL_SBAUD_GET_SIOP_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::StopA2dpHpSpeakerStreamStatusNotification() +/*++ + +Routine Description: + + This function stops the A2DP Headphone speaker stream status notification. + The function waits for the request to be done before returning. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + A2dpHpDeviceNotificationReqContext *reqCtx = NULL; + WDF_REQUEST_REUSE_PARAMS reuseParams; + + WdfRequestCancelSentRequest(m_SpeakerStreamReq); + KeWaitForSingleObject(&m_SpeakerStreamStatusEvent, Executive, KernelMode, FALSE, NULL); + + reqCtx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerStreamReq); + ASSERT(reqCtx != NULL); + UNREFERENCED_VAR(reqCtx); + + // + // Re-init the request for later. + // + WDF_REQUEST_REUSE_PARAMS_INIT( + &reuseParams, + WDF_REQUEST_REUSE_NO_FLAGS, + STATUS_SUCCESS); + + ntStatus = WdfRequestReuse(m_SpeakerStreamReq, &reuseParams); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus)); + } + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::SetA2dpHpStreamOpen +( + ULONG EpIndex, + PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pStreamFormat, + PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 pEndpointDescriptor +) +/*++ + +Routine Description: + +This function synchronously requests an open ISOCH channel to transmit audio +data over transport. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[A2dpHpDevice::SetA2dpHpStreamOpen]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(pEndpointDescriptor); + + SIDEBANDAUDIO_STREAM_OPEN_PARAMS sp = { 0 }; + sp.EpIndex = EpIndex; + sp.Format = (PKSDATAFORMAT)pStreamFormat; + sp.SiopCount = 0; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_STREAM_OPEN, + sizeof(sp), + 0, + &sp); + + if (ntStatus == STATUS_DEVICE_BUSY) + { + // The stream channel is already open. + DPF(D_VERBOSE, ("SetA2dpHpStreamOpen: the stream channel is already open")); + ntStatus = STATUS_SUCCESS; + } + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetA2dpHpStreamOpen: SendIoCtrlSynchronously(IOCTL_SBAUD_STREAM_OPEN) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS A2dpHpDevice::SetA2dpHpStreamStart() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_STREAM_START, + sizeof(m_SpeakerEpIndex), + 0, + &m_SpeakerEpIndex); + + if (ntStatus == STATUS_DEVICE_BUSY) + { + DPF(D_VERBOSE, ("%!FUNC!: the stream channel is already started")); + ntStatus = STATUS_SUCCESS; + } + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_STREAM_START) failes, 0x%x", ntStatus)), + Done); + + ntStatus = STATUS_SUCCESS; + +Done: + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS A2dpHpDevice::SetA2dpHpStreamSuspend() +{ + PAGED_CODE(); + + DPF_ENTER(("[%!FUNC!]")); + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_STREAM_SUSPEND, + sizeof(m_SpeakerEpIndex), + 0, + &m_SpeakerEpIndex); + + if (ntStatus == STATUS_DEVICE_BUSY) + { + DPF(D_VERBOSE, ("%!FUNC!: the stream channel is already suspended")); + ntStatus = STATUS_SUCCESS; + } + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_STREAM_SUSPEND) failes, 0x%x", ntStatus)), + Done); + + ntStatus = STATUS_SUCCESS; + +Done: + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::SetA2dpHpStreamClose() +/*++ + +Routine Description: + + This function synchronously requests to close the ISOCH channel. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_STREAM_CLOSE, + sizeof(m_SpeakerEpIndex), + 0, + &m_SpeakerEpIndex); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_STREAM_CLOSE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +A2dpHpDevice::Start() +/*++ + +Routine Description: + + Asynchronously called to start the audio device. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + ULONG speakerEndpoints = 0; + + // Indicate audio driver capabilities indicating support for 16KHz sampling rate + ntStatus = SetA2dpHpDeviceCapabilities(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: SetA2dpHpDeviceCapabilities: failed to set Wideband Support, 0x%x", ntStatus)), + Done); + + // + // Get A2DP descriptor + // + ntStatus = GetA2dpHpDescriptor(&m_Descriptor); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: GetA2dpHpDescriptor: failed to retrieve A2DP Headphone Descriptor, 0x%x", ntStatus)), + Done); + + for (ULONG i = 0; i < m_Descriptor->NumberOfEndpoints; i++) + { + PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 ed; + ntStatus = GetA2dpHpEndpointDescriptor(i, &ed); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: GetA2dpHpEndpointDescriptor: failed to retrieve A2DP Headphone Endpoint %d Descriptor, 0x%x", i, ntStatus)), + Done); + + if (ed->Direction == KSPIN_DATAFLOW_OUT) + { + speakerEndpoints++; + m_SpeakerEpIndex = i; + m_pSpeakerDescriptor = ed; + } + } + + if (speakerEndpoints > 1) + { + ntStatus = STATUS_DEVICE_FEATURE_NOT_SUPPORTED; + m_SpeakerEpIndex = 0; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: Number of endpoints on A2DP device not supported, 0x%x", ntStatus)), + Done); + + // + // Get volume settings. + // + if (m_pSpeakerDescriptor) + { + if (m_pSpeakerDescriptor->Capabilities.Volume) + { + PKSPROPERTY_DESCRIPTION volumePropValues = NULL; + LONG volume = 0; + + // Volume settings. + ntStatus = GetA2dpHpVolumePropertyValues( + m_SpeakerEpIndex, + m_pSpeakerDescriptor->VolumePropertyValuesSize, + &volumePropValues); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: GetA2dpHpVolumePropertyValues: failed to retrieve KSPROPERTY_VALUES, 0x%x", ntStatus)), + Done); + + m_SpeakerVolumePropValues = volumePropValues; + + // Speaker volume. + ntStatus = GetA2dpHpSpeakerVolume(0, &volume); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: GetA2dpHpSpeakerVolume: failed, 0x%x", ntStatus)), + Done); + + m_SpeakerVolumeLevel = volume; + } + + if (m_pSpeakerDescriptor->Capabilities.Mute) + { + PKSPROPERTY_DESCRIPTION mutePropValues = NULL; + LONG mute = 0; + + // Mute settings. + ntStatus = GetA2dpHpMutePropertyValues( + m_SpeakerEpIndex, + m_pSpeakerDescriptor->MutePropertyValuesSize, + &mutePropValues); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: GetA2dpHpMutePropertyValues: failed to retrieve KSPROPERTY_DESCRIPTION, 0x%x", ntStatus)), + Done); + + m_SpeakerMutePropValues = mutePropValues; + + // Speaker mute. + ntStatus = GetA2dpHpSpeakerMute(0, &mute); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: GetA2dpHpSpeakerMute: failed, 0x%x", ntStatus)), + Done); + + m_SpeakerMute = mute; + } + + // Speaker Formats + PKSDATAFORMAT speakerFormatsArray[SIZEOF_ARRAY(A2dpHpSpeakerSupportedDeviceFormats)]; + for (ULONG i = 0; i < SIZEOF_ARRAY(A2dpHpSpeakerSupportedDeviceFormats); i++) + { + speakerFormatsArray[i] = (PKSDATAFORMAT)(&A2dpHpSpeakerSupportedDeviceFormats[i]); + } + SIDEBANDAUDIO_SUPPORTED_FORMATS speakerDeviceFormats = + { + sizeof(SIDEBANDAUDIO_SUPPORTED_FORMATS) + SIZEOF_ARRAY(speakerFormatsArray)*sizeof(*speakerFormatsArray), + m_SpeakerEpIndex, + SIZEOF_ARRAY(speakerFormatsArray), + speakerFormatsArray + }; + + m_pSpeakerSupportedFormatsIntersection = (PSIDEBANDAUDIO_SUPPORTED_FORMATS)ExAllocatePool2(POOL_FLAG_NON_PAGED, speakerDeviceFormats.CbSize, SYSVAD_POOLTAG); + RtlCopyMemory(m_pSpeakerSupportedFormatsIntersection, &speakerDeviceFormats, sizeof(speakerDeviceFormats)); + RtlCopyMemory(m_pSpeakerSupportedFormatsIntersection->Formats, speakerFormatsArray, SIZEOF_ARRAY(speakerFormatsArray)*sizeof(*speakerFormatsArray)); + + // + // Customize the topology/wave descriptors for this instance + // + ntStatus = CreateCustomEndpointMinipair( + g_A2dpHpRenderEndpoints[0], + &m_pSpeakerDescriptor->FriendlyName, + &m_pSpeakerDescriptor->Category, + m_pSpeakerDescriptor->FilterInterfacePropertyCount, + m_pSpeakerDescriptor->FilterInterfaceProperties, + &m_SpeakerMiniports); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: CreateCustomEndpointMinipair for Render: failed, 0x%x", ntStatus)), + Done); + m_SpeakerMiniports->TopoName = m_SpeakerTopologyNameBuffer; + m_SpeakerMiniports->WaveName = m_SpeakerWaveNameBuffer; + + // Verify format compat + ntStatus = VerifyEndpointFormatCompatibility( + m_pSpeakerSupportedFormatsIntersection, + A2dpHpSpeakerSupportedDeviceFormats, + SIZEOF_ARRAY(A2dpHpSpeakerSupportedDeviceFormats), + &m_SpeakerSelectedFormat); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: VerifyEndpointFormatCompatibility : failed for speaker, 0x%x", ntStatus)), + Done); + + ASSERT(m_SpeakerMiniports != NULL); + _Analysis_assume_(m_SpeakerMiniports != NULL); + + // + // Register topology and wave filters. + // + ntStatus = m_Adapter->InstallEndpointFilters( + NULL, + m_SpeakerMiniports, + PSIDEBANDDEVICECOMMON(this), + &m_UnknownSpeakerTopology, + &m_UnknownSpeakerWave, NULL, NULL + ); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: InstallEndpointRenderFilters (A2DP Headphone): failed, 0x%x", ntStatus)), + Done); + + // + // Pend status notifications. + // + + ntStatus = EnableA2dpHpConnectionStatusNotification(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: EnableA2dpHpConnectionStatusNotification: failed, 0x%x", ntStatus)), + Done); + + ntStatus = EnableA2dpHpSpeakerSiopNotification(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: EnableA2dpHpSpeakerSiopNotification: failed, 0x%x", ntStatus)), + Done); + + if (m_pSpeakerDescriptor->Capabilities.Volume) + { + // Volume speaker status. + ntStatus = EnableA2dpHpSpeakerVolumeStatusNotification(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: EnableA2dpHpSpeakerVolumeStatusNotification: failed, 0x%x", ntStatus)), + Done); + } + } + + // Set Sideband claimed + ntStatus = SetSidebandClaimed(TRUE); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SetSidebandClaimed TRUE: failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + ntStatus = SetSidebandClaimed(FALSE); + InterlockedExchange((PLONG)&m_State, eA2dpHpStateFailed); + } +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::CreateCustomEndpointMinipair +( + _In_ PENDPOINT_MINIPAIR pBaseMinipair, + _In_ PUNICODE_STRING FriendlyName, + _In_ PGUID pCategory, + _In_ ULONG customFilterInterfacePropertyCount, + _In_count_(customFilterInterfacePropertyCount) DEVPROPERTY* customFilterInterfaceProperties, + _Outptr_ PENDPOINT_MINIPAIR *ppCustomMinipair +) +{ + NTSTATUS ntStatus; + PENDPOINT_MINIPAIR pNewMinipair = NULL; + SYSVAD_DEVPROPERTY* pTopoProperties = NULL; + PPCFILTER_DESCRIPTOR pNewTopoFilterDesc = NULL; + PPCPIN_DESCRIPTOR pNewTopoPins = NULL; + ULONG cTopoProperties; + ULONG cTopoPins; + ULONG cWaveProperties; + SYSVAD_DEVPROPERTY* pWaveProperties = NULL; + + PAGED_CODE(); + + // + // This routine will add one more property to whatever the base minipair describes for the topo filter interface properties for the friendly name. + // Additional properties will be added to the topo filter interface for custom properties set by the profile driver. + // It will also allocate and set up custom filter and pin descriptors to allow changing the KSNODETYPE for the a2dp device + // + cTopoPins = pBaseMinipair->TopoDescriptor->PinCount; + // Additional topo properties consist of the friendly name and custom filter properties set by the profile driver. + cTopoProperties = pBaseMinipair->TopoInterfacePropertyCount + 1 + customFilterInterfacePropertyCount; + pTopoProperties = (SYSVAD_DEVPROPERTY*)ExAllocatePool2(POOL_FLAG_NON_PAGED, cTopoProperties * sizeof(SYSVAD_DEVPROPERTY), SYSVAD_POOLTAG); + pNewMinipair = (ENDPOINT_MINIPAIR*)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(ENDPOINT_MINIPAIR), SYSVAD_POOLTAG); + pNewTopoFilterDesc = (PCFILTER_DESCRIPTOR*)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(PCFILTER_DESCRIPTOR), SYSVAD_POOLTAG); + pNewTopoPins = (PCPIN_DESCRIPTOR*)ExAllocatePool2(POOL_FLAG_NON_PAGED, cTopoPins * sizeof(PCPIN_DESCRIPTOR), SYSVAD_POOLTAG); + cWaveProperties = pBaseMinipair->WaveInterfacePropertyCount; + pWaveProperties = (SYSVAD_DEVPROPERTY*)ExAllocatePool2(POOL_FLAG_NON_PAGED, cWaveProperties * sizeof(SYSVAD_DEVPROPERTY), SYSVAD_POOLTAG); + + if ((pTopoProperties != NULL) && (pNewMinipair != NULL) && (pNewTopoFilterDesc != NULL) && (pNewTopoPins != NULL) && (pWaveProperties != NULL)) + { + SYSVAD_DEVPROPERTY *pLastProperty; + + // Copy base minipair properties to new property list + if (pBaseMinipair->TopoInterfacePropertyCount > 0) + { + RtlCopyMemory(pTopoProperties, pBaseMinipair->TopoInterfaceProperties, pBaseMinipair->TopoInterfacePropertyCount * sizeof(SYSVAD_DEVPROPERTY)); + } + + // Add friendly name property to the topo property list + NT_ASSERT(FriendlyName->Length + sizeof(UNICODE_NULL) <= FriendlyName->MaximumLength); // Assuming NULL terminated string + pLastProperty = pTopoProperties + pBaseMinipair->TopoInterfacePropertyCount; + pLastProperty->PropertyKey = &DEVPKEY_DeviceInterface_FriendlyName; + pLastProperty->Type = DEVPROP_TYPE_STRING_INDIRECT; + pLastProperty->BufferSize = FriendlyName->Length + sizeof(UNICODE_NULL); + pLastProperty->Buffer = FriendlyName->Buffer; + pLastProperty++; + + // Add custom filter interface properties to the topo property list. + for (ULONG i = 0; i < customFilterInterfacePropertyCount; i++) + { + pLastProperty->PropertyKey = &customFilterInterfaceProperties[i].CompKey.Key; + pLastProperty->Type = customFilterInterfaceProperties[i].Type; + pLastProperty->BufferSize = customFilterInterfaceProperties[i].BufferSize; + pLastProperty->Buffer = customFilterInterfaceProperties[i].Buffer; + pLastProperty++; + } + + if (pBaseMinipair->WaveInterfacePropertyCount > 0) + { + RtlCopyMemory(pWaveProperties, pBaseMinipair->WaveInterfaceProperties, pBaseMinipair->WaveInterfacePropertyCount * sizeof(SYSVAD_DEVPROPERTY)); + } + + // Copy base minipair structure + RtlCopyMemory(pNewMinipair, pBaseMinipair, sizeof(ENDPOINT_MINIPAIR)); + + RtlCopyMemory(pNewTopoFilterDesc, pBaseMinipair->TopoDescriptor, sizeof(PCFILTER_DESCRIPTOR)); + RtlCopyMemory(pNewTopoPins, pBaseMinipair->TopoDescriptor->Pins, cTopoPins * sizeof(PCPIN_DESCRIPTOR)); + + pNewTopoFilterDesc->Pins = pNewTopoPins; + pNewMinipair->TopoDescriptor = pNewTopoFilterDesc; + + // Update it to point to new property list + pNewMinipair->TopoInterfacePropertyCount = cTopoProperties; + pNewMinipair->TopoInterfaceProperties = pTopoProperties; + pNewMinipair->WaveInterfacePropertyCount = cWaveProperties; + pNewMinipair->WaveInterfaceProperties = pWaveProperties; + + ntStatus = UpdateCustomEndpointCategory(pNewTopoFilterDesc, pNewTopoPins, pCategory); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("%!FUNC!: UpdateCustomEndpointCategory failed, 0x%x", ntStatus)); + } + else + { + *ppCustomMinipair = pNewMinipair; + + pTopoProperties = NULL; + pWaveProperties = NULL; + pNewMinipair = NULL; + pNewTopoFilterDesc = NULL; + pNewTopoPins = NULL; + + ntStatus = STATUS_SUCCESS; + } + } + else + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + if (pTopoProperties != NULL) + { + ExFreePoolWithTag(pTopoProperties, SYSVAD_POOLTAG); + } + if (pWaveProperties != NULL) + { + ExFreePoolWithTag(pWaveProperties, SYSVAD_POOLTAG); + } + if (pNewMinipair != NULL) + { + ExFreePoolWithTag(pNewMinipair, SYSVAD_POOLTAG); + } + if (pNewTopoFilterDesc != NULL) + { + ExFreePoolWithTag(pNewTopoFilterDesc, SYSVAD_POOLTAG); + } + if (pNewTopoPins != NULL) + { + ExFreePoolWithTag(pNewTopoPins, SYSVAD_POOLTAG); + } + + return ntStatus; +} +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +A2dpHpDevice::UpdateCustomEndpointCategory +( + _In_ PPCFILTER_DESCRIPTOR pCustomMinipairTopoFilter, + _In_ PPCPIN_DESCRIPTOR pCustomMinipairTopoPins, + _In_ PGUID pCategory +) +{ + NTSTATUS ntStatus = STATUS_NOT_FOUND; + ULONG cPinCount = 0; + BOOL FoundCategoryAudio = FALSE; + BOOL FoundNodeType = FALSE; + + PAGED_CODE(); + + cPinCount = pCustomMinipairTopoFilter->PinCount; + + // Find the right pin: There should be two pins, one with Category KSCATEGORY_AUDIO, + // and one with a KSNODETYPE_* Category. We need to modify the KSNODETYPE category. + for (ULONG i = 0; i < cPinCount; ++i) + { + if (IsEqualGUID(*pCustomMinipairTopoPins[i].KsPinDescriptor.Category, KSCATEGORY_AUDIO)) + { + ASSERT(FoundCategoryAudio == FALSE); + if (FoundCategoryAudio) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + DPF(D_ERROR, ("%!FUNC!: KSCATEGORY_AUDIO found more than once, 0x%x", ntStatus)); + break; + } + + FoundCategoryAudio = TRUE; + continue; + } + + ASSERT(FoundNodeType == FALSE); + if (FoundNodeType) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + DPF(D_ERROR, ("%!FUNC!: Found more than one applicable Pin, 0x%x", ntStatus)); + break; + } + + pCustomMinipairTopoPins[i].KsPinDescriptor.Category = pCategory; + FoundNodeType = TRUE; + ntStatus = STATUS_SUCCESS; + } + + return ntStatus; +} +//============================================================================= +#pragma code_seg("PAGE") +VOID +A2dpHpDevice::DeleteCustomEndpointMinipair +( + _In_ PENDPOINT_MINIPAIR pCustomMinipair +) +{ + PAGED_CODE(); + + if (pCustomMinipair != NULL) + { + if (pCustomMinipair->TopoInterfaceProperties != NULL) + { + ExFreePoolWithTag(const_cast<SYSVAD_DEVPROPERTY*>(pCustomMinipair->TopoInterfaceProperties), SYSVAD_POOLTAG); + pCustomMinipair->TopoInterfaceProperties = NULL; + } + if (pCustomMinipair->TopoDescriptor != NULL) + { + if (pCustomMinipair->TopoDescriptor->Pins != NULL) + { + ExFreePoolWithTag((PVOID)pCustomMinipair->TopoDescriptor->Pins, SYSVAD_POOLTAG); + pCustomMinipair->TopoDescriptor->Pins = NULL; + } + ExFreePoolWithTag(pCustomMinipair->TopoDescriptor, SYSVAD_POOLTAG); + pCustomMinipair->TopoDescriptor = NULL; + } + ExFreePoolWithTag(pCustomMinipair, SYSVAD_POOLTAG); + } +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +A2dpHpDevice::Stop() +/*++ + +Routine Description: + + Asynchronously called to stop the audio device. + After returning from this function, there are no more async notifications + pending (volume, etc.). + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + eA2dpHpState state = eA2dpHpStateInvalid; + + state = (eA2dpHpState) InterlockedExchange((PLONG)&m_State, eA2dpHpStateStopping); + ASSERT(state == eA2dpHpStateRunning || state == eA2dpHpStateFailed); + UNREFERENCED_VAR(state); + + // + // Stop async notifications. + // + WdfIoTargetPurge(m_WdfIoTarget, WdfIoTargetPurgeIoAndWait); + + // + // Wait for work-item. + // + WdfWorkItemFlush(m_WorkItem); + + // + // Remove the topology and wave render filters. + // + if (m_UnknownSpeakerTopology || m_UnknownSpeakerWave) + { + ntStatus = m_Adapter->RemoveEndpointFilters( + m_SpeakerMiniports, + m_UnknownSpeakerTopology, + m_UnknownSpeakerWave); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("%!FUNC!: RemoveEndpointFilters (A2DP Headphone Speaker): failed, 0x%x", ntStatus)); + } + } + + // + // Release port/miniport pointers. + // + SAFE_RELEASE(m_UnknownSpeakerTopology); + SAFE_RELEASE(m_UnknownSpeakerWave); + + // + // The device is in the stopped state. + // + InterlockedExchange((PLONG)&m_State, eA2dpHpStateStopped); + +} + +#pragma code_seg("PAGE") +NTSTATUS A2dpHpDevice::CreateFilterNames( + PUNICODE_STRING A2dpHpDeviceSymbolicLinkName +) +/*++ + +Routine Description: + +Creates unique wave and topology filter reference names. + +Since each subdevice representing a A2DP sideband interface arrival needs to have a unique +reference string, this function generates a hash on 'A2dpHpDeviceSymbolicLinkName' parameter +and appends to static interface strings. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS status = STATUS_SUCCESS; + ULONG hashValue = 0; + + status = RtlHashUnicodeString(A2dpHpDeviceSymbolicLinkName, TRUE, HASH_STRING_ALGORITHM_X65599, &hashValue); + IF_FAILED_ACTION_JUMP( + status, + DPF(D_ERROR, ("%!FUNC!: Failed to create KSCATEGORY_REALTIME wave interface: 0x%x", status)), + End); + + // create wave interfaces for speaker + RtlStringCbPrintfW(m_SpeakerWaveNameBuffer, sizeof(m_SpeakerWaveNameBuffer), L"%s-%lu", A2DPHP_SPEAKER_WAVE_NAME, hashValue); + RtlUnicodeStringInit(&m_SpeakerWaveRefString, m_SpeakerWaveNameBuffer); + + // create topology interfaces for speaker + RtlStringCbPrintfW(m_SpeakerTopologyNameBuffer, sizeof(m_SpeakerTopologyNameBuffer), L"%s-%lu", A2DPHP_SPEAKER_TOPO_NAME, hashValue); + RtlUnicodeStringInit(&m_SpeakerTopologyRefString, m_SpeakerTopologyNameBuffer); + +End: + return status; +} +#endif // SYSVAD_A2DP_SIDEBAND diff --git a/audio/sysvad/A2dpHpDevice.h b/audio/sysvad/A2dpHpDevice.h new file mode 100644 index 00000000..1f07b89a --- /dev/null +++ b/audio/sysvad/A2dpHpDevice.h @@ -0,0 +1,629 @@ +// +// This class represents a the Bluetooth A2DP Headphone device. +// There is one class for each interface (id = symbolic-link-name). +// +#ifdef SYSVAD_A2DP_SIDEBAND + + +//===================================================================== +// +// A2DP Sideband Headphone class definition. +// + +class CAdapterCommon; +class A2dpHpDevice; + +struct A2dpHpWorkItemContext +{ + CAdapterCommon * Adapter; +}; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME +( + A2dpHpWorkItemContext, + GetA2dpHpWorkItemContext +) + +// start/stop the device +enum eA2dpHpTaskAction +{ + eA2dpHpTaskStart = 1, + eA2dpHpTaskStop = 2, +}; + +struct A2dpHpWorkTask +{ + LIST_ENTRY ListEntry; + A2dpHpDevice * Device; + eA2dpHpTaskAction Action; +}; + + +//===================================================================== +// +// Device: Bluetooth A2DP Headphone definitions. +// + +// BTH A2DP device's notification work-item context. +struct A2dpHpDeviceNotificationWorkItemContext +{ + A2dpHpDevice * A2dpHpDevice; +}; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME +( + A2dpHpDeviceNotificationWorkItemContext, + GetA2dpHpDeviceNotificationWorkItemContext +) + +// A2DP HP device's notification request context. +union A2dpHpDeviceNotificationBuffer +{ + SIDEBANDAUDIO_VOLUME_PARAMS Volume; + SIDEBANDAUDIO_MUTE_PARAMS Mute; + SIDEBANDAUDIO_STREAM_STATUS_PARAMS StreamStatus; + SIDEBANDAUDIO_CONNECTION_PARAMS ConnectionStatus; + SIDEBANDAUDIO_SIOP_REQUEST_PARAM SiopUpdate; +}; + +struct A2dpHpDeviceNotificationReqContext +{ + A2dpHpDevice *A2dpHpDevice; + LONG Errors; + A2dpHpDeviceNotificationBuffer Buffer; + WDFMEMORY MemIn; + WDFMEMORY MemOut; +}; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME +( + A2dpHpDeviceNotificationReqContext, + GetA2dpHpDeviceNotificationReqContext +) + +enum eA2dpHpState +{ + eA2dpHpStateInvalid = 0, + eA2dpHpStateInitializing = 1, + eA2dpHpStateRunning = 2, + eA2dpHpStateStopping = 3, + eA2dpHpStateStopped = 4, + eA2dpHpStateFailed = 5, +}; + +// To support event notification. +struct A2dpHpEventCallback +{ + PFNEVENTNOTIFICATION Handler; + PVOID Context; +}; + +typedef struct _AVDTP_CATEGORY_HEADER +{ + UCHAR SvcCategory; + UCHAR Losc; //Length Of Service Capability +} AVDTP_CATEGORY_HEADER; + +// +// Contains codec information specific for the Subband Codec. +// Structure is defined in section 4.3.2 of the Bluetooth A2DP 1.3.2 specification. +// +typedef struct _A2DP_SBC_MEDIA_HEADER +{ + AVDTP_CATEGORY_HEADER CategoryCodecHeader; + + UCHAR Rfa : 4; + UCHAR MediaType : 4; + UCHAR CodecType; + + UCHAR ChannelMode : 4; + UCHAR SamplingFrequency : 4; + + UCHAR AllocMethod : 2; + UCHAR Subbands : 2; + UCHAR BlockLength : 4; + + UCHAR MinBitpoolVal; + UCHAR MaxBitpoolVal; + + // return the appropriate Losc value for this structure + static unsigned char CorrectLosc() + { + return static_cast<unsigned char>((sizeof(_A2DP_SBC_MEDIA_HEADER) - sizeof(AVDTP_CATEGORY_HEADER))); + } +} A2DP_SBC_MEDIA_HEADER; + +// +// Contains codec information specific for the Advanced Audio Codec (AAC) +// Structure is defined in section 4.5.2 of the Bluetooth A2DP 1.3.2 specification. +// +typedef struct _A2DP_AAC_MEDIA_HEADER +{ + AVDTP_CATEGORY_HEADER CategoryCodecHeader; + + UCHAR Rfa : 4; + UCHAR MediaType : 4; + UCHAR CodecType; + + // Octet0 + UCHAR ObjectType; + + // Octet1 + UCHAR SamplingFrequencyUpper8Bits; + + // Octet2 + UCHAR RFA : 2; + UCHAR Channels : 2; + UCHAR SamplingFrequencyLower4Bits : 4; + + // Octet3 + UCHAR BitRateUpper7Bits : 7; + UCHAR VariableBitRate : 1; + + // Octet4 + UCHAR BitRateMiddle8Bits; + + // Octet5 + UCHAR BitRateLower8Bits; + + // return the appropriate Losc value for this structure + static UCHAR CorrectLosc() + { + return static_cast<UCHAR>((sizeof(A2DP_AAC_MEDIA_HEADER) - sizeof(AVDTP_CATEGORY_HEADER))); + } +} A2DP_AAC_MEDIA_HEADER; + +typedef union +{ + A2DP_SBC_MEDIA_HEADER SbcHeader; + A2DP_AAC_MEDIA_HEADER AacHeader; +}A2DPHPDEVICE_CODEC_CAPABILITIES, *PA2DPHPDEVICE_CODEC_CAPABILITIES; + +typedef struct _A2DPHPDEVICE_EP_TRANSPORT_RESOURCES +{ + ULONG A2dpHwTransportResource; +}A2DPHPDEVICE_EP_TRANSPORT_RESOURCES, *PA2DPHPDEVICE_EP_TRANSPORT_RESOURCES; + +#endif // SYSVAD_A2DP_SIDEBAND +class A2dpHpDevice : + ISidebandDeviceCommon, + public CUnknown +{ + private: + eA2dpHpState m_State; + + IAdapterCommon * m_Adapter; + WDFIOTARGET m_WdfIoTarget; + + LIST_ENTRY m_ListEntry; + UNICODE_STRING m_SymbolicLinkName; + WCHAR m_SpeakerWaveNameBuffer[A2DPHP_INTERFACE_REFSTRING_MAX_LENGTH]; + UNICODE_STRING m_SpeakerWaveRefString; + WCHAR m_SpeakerTopologyNameBuffer[A2DPHP_INTERFACE_REFSTRING_MAX_LENGTH]; + UNICODE_STRING m_SpeakerTopologyRefString; + + // + // The Topo Filter Desc and Topo Pins structures referenced from + // the Miniports structure are deep copies: they start as copies of the + // static structures and are modified to allow A2DP-Device pin + // categories based on the info obtained from + // IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR + // + PENDPOINT_MINIPAIR m_SpeakerMiniports; + + PUNKNOWN m_UnknownSpeakerTopology; + PUNKNOWN m_UnknownSpeakerWave; + + PSIDEBANDAUDIO_DEVICE_DESCRIPTOR m_Descriptor; + PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 m_pSpeakerDescriptor; + PKSPROPERTY_DESCRIPTION m_SpeakerVolumePropValues; + LONG m_SpeakerVolumeLevel; + PA2DPHPDEVICE_CODEC_CAPABILITIES m_ConnectedCodecCaps; + FAST_MUTEX m_ConnectedCodecLock; + union { + NTSTATUS m_ConnectionStatus; + LONG m_ConnectionStatusLong; + }; // unnamed. + + PKSPROPERTY_DESCRIPTION m_SpeakerMutePropValues; + LONG m_SpeakerMute; + + + ULONG m_SpeakerEpIndex; + + union{ + NTSTATUS m_SpeakerStreamStatus; + LONG m_SpeakerStreamStatusLong; + }; // unnamed. + + KEVENT m_SpeakerStreamStatusEvent; + + PSIDEBANDAUDIO_SUPPORTED_FORMATS m_pSpeakerSupportedFormatsIntersection; + ULONG m_SpeakerSelectedFormat; + + A2DPHPDEVICE_EP_TRANSPORT_RESOURCES m_SpeakerTransportResources; + + WDFREQUEST m_SpeakerStreamReq; + WDFREQUEST m_SpeakerVolumeReq; + WDFREQUEST m_SpeakerMuteReq; + WDFREQUEST m_ConnectionReq; + WDFREQUEST m_SiopUpdateReq; + WDFWORKITEM m_WorkItem; + WDFCOLLECTION m_ReqCollection; + KSPIN_LOCK m_Lock; + + LONG m_nSpeakerStreams; // # of open streams. + LONG m_nSpeakerStartedStreams; + + A2dpHpEventCallback m_SpeakerVolumeCallback; + + A2dpHpEventCallback m_SpeakerMuteCallback; + + A2dpHpEventCallback m_SpeakerConnectionStatusCallback; + + A2dpHpEventCallback m_SpeakerFormatChangeCallback; + + public: + //===================================================================== + // Default CUnknown + DECLARE_STD_UNKNOWN(); + DEFINE_STD_CONSTRUCTOR(A2dpHpDevice); + ~A2dpHpDevice(); + + NTSTATUS Init + ( + _In_ IAdapterCommon * Adapter, + _In_ PUNICODE_STRING SymbolicLinkName + ); + + public: + //===================================================================== + // + // Public functions used by CAdapterCommon object. + // + VOID Start(); + VOID Stop(); + + PLIST_ENTRY GetListEntry() + { + return &m_ListEntry; + } + + PUNICODE_STRING GetSymbolicLinkName() + { + return &m_SymbolicLinkName; + } + + static + A2dpHpDevice * + GetA2dpHpDevice + ( + _In_ PLIST_ENTRY le + ) + { + return CONTAINING_RECORD(le, A2dpHpDevice, m_ListEntry); + } + + public: + //===================================================================== + // + // ISidebandDeviceCommon functions. + // + STDMETHODIMP_(BOOL) IsVolumeSupported + ( + _In_ eDeviceType deviceType + ); + + STDMETHODIMP_(PVOID) GetVolumeSettings + ( + _In_ eDeviceType deviceType, + _Out_ PULONG Size + ); + + STDMETHODIMP_(NTSTATUS) GetVolume + ( + _In_ eDeviceType deviceType, + _In_ LONG Channel, + _Out_ LONG *pVolume + ); + + STDMETHODIMP_(NTSTATUS) SetVolume + ( + _In_ eDeviceType deviceType, + _In_ LONG Channel, + _In_ LONG Volume + ); + + STDMETHODIMP_(BOOL) IsMuteSupported + ( + _In_ eDeviceType deviceType + ); + + STDMETHODIMP_(PVOID) GetMuteSettings + ( + _In_ eDeviceType deviceType, + _Out_ PULONG Size + ); + + STDMETHODIMP_(LONG) GetMute + ( + _In_ eDeviceType deviceType, + _In_ LONG Channel + ); + + STDMETHODIMP_(NTSTATUS) SetMute + ( + _In_ eDeviceType deviceType, + _In_ LONG Channel, + _In_ LONG Mute + ); + + STDMETHODIMP_(BOOL) GetConnectionStatus(); + + STDMETHODIMP_(NTSTATUS) Connect(); + + STDMETHODIMP_(NTSTATUS) Disconnect(); + + STDMETHODIMP_(BOOL) GetStreamStatus(_In_ eDeviceType deviceType); + + STDMETHODIMP_(NTSTATUS) StreamOpen(_In_ eDeviceType deviceType); + + STDMETHODIMP_(NTSTATUS) StreamStart(_In_ eDeviceType deviceType); + + STDMETHODIMP_(NTSTATUS) StreamSuspend(_In_ eDeviceType deviceType); + + STDMETHODIMP_(NTSTATUS) StreamClose(_In_ eDeviceType deviceType); + + STDMETHODIMP_(GUID) GetContainerId(_In_ eDeviceType deviceType); + + STDMETHODIMP_(VOID) SetVolumeHandler + ( + _In_ eDeviceType deviceType, + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ); + + STDMETHODIMP_(VOID) SetMuteHandler + ( + _In_ eDeviceType deviceType, + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ); + + STDMETHODIMP_(VOID) SetConnectionStatusHandler + ( + _In_ eDeviceType deviceType, + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ); + + STDMETHODIMP_(VOID) SetFormatChangeHandler + ( + _In_ eDeviceType deviceType, + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ); + + STDMETHODIMP_(PPIN_DEVICE_FORMATS_AND_MODES) GetFormatsAndModes + ( + _In_ eDeviceType deviceType + ); + + _IRQL_requires_max_(DISPATCH_LEVEL) + STDMETHODIMP_(BOOL) IsNRECSupported(); + + STDMETHODIMP_(BOOL) GetNRECDisableStatus(); + + private: + //===================================================================== + // + // Helper functions. + // + NTSTATUS SendIoCtrlSynchronously + ( + _In_opt_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_ ULONG InLength, + _In_ ULONG OutLength, + _When_(InLength > 0 || OutLength > 0, _In_) + _When_(InLength == 0 && OutLength == 0, _In_opt_) + PVOID Buffer + ); + + NTSTATUS SendIoCtrlSynchronously + ( + _In_opt_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_ ULONG InLength, + _In_ ULONG OutLength, + _When_(InLength > 0, _In_) + _When_(InLength == 0, _In_opt_) + PVOID InBuffer, + _When_(OutLength > 0, _In_) + _When_(OutLength == 0, _In_opt_) + PVOID OutBuffer + ); + + NTSTATUS SendIoCtrlAsynchronously + ( + _In_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_opt_ WDFMEMORY MemIn, + _In_opt_ WDFMEMORY MemOut, + _In_ PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine, + _In_ WDFCONTEXT Context + ); + + NTSTATUS SetA2dpHpDeviceCapabilities + ( + ); + + NTSTATUS GetA2dpHpDescriptor + ( + _Out_ PSIDEBANDAUDIO_DEVICE_DESCRIPTOR *Descriptor + ); + + NTSTATUS GetA2dpHpEndpointDescriptor + ( + _In_ ULONG EpIndex, + _Out_ PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 *EpDescriptor + ); + + NTSTATUS GetA2dpHpVolumePropertyValues + ( + _In_ ULONG EpIndex, + _In_ ULONG Length, + _Out_ PKSPROPERTY_DESCRIPTION *PropValues + ); + + NTSTATUS SetA2dpHpSpeakerVolume + ( + _In_ LONG Channel, + _In_ LONG Volume + ); + + NTSTATUS GetA2dpHpSpeakerVolume + ( + _In_ LONG Channel, + _Out_ LONG *Volume + ); + + NTSTATUS EnableA2dpHpSpeakerVolumeStatusNotification(); + + NTSTATUS GetA2dpHpMutePropertyValues + ( + _In_ ULONG EpIndex, + _In_ ULONG Length, + _Out_ PKSPROPERTY_DESCRIPTION *PropValues + ); + + NTSTATUS SetA2dpHpSpeakerMute + ( + _In_ LONG Channel, + _In_ LONG Mute + ); + + NTSTATUS GetA2dpHpSpeakerMute + ( + _In_ LONG Channel, + _Out_ LONG *Mute + ); + + NTSTATUS EnableA2dpHpSpeakerMuteStatusNotification(); + + NTSTATUS GetA2dpHpConnectionStatus + ( + _Out_ BOOL *ConnectionStatus + ); + + NTSTATUS EnableA2dpHpConnectionStatusNotification(); + + + NTSTATUS GetA2dpHpEndpointFormatsIntersection + ( + _In_ PSIDEBANDAUDIO_SUPPORTED_FORMATS pDeviceFormats, + _Out_ PSIDEBANDAUDIO_SUPPORTED_FORMATS *ppSupportedFormatsIntersection + ); + + NTSTATUS VerifyEndpointFormatCompatibility + ( + _In_ PSIDEBANDAUDIO_SUPPORTED_FORMATS EpSupportedFormats, + _In_ PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pHardwareSupportedFormats, + _In_ ULONG NumHardwareSupportedFormats, + _Out_ PULONG pHardwareFormatIndex + ); + + NTSTATUS GetA2dpHpEndpointTransportResources + ( + _In_ ULONG EpIndex, + _In_ BOOL bFeedback, + _Out_ PA2DPHPDEVICE_EP_TRANSPORT_RESOURCES pTransportResources + ); + + static + NTSTATUS CreateCustomEndpointMinipair + ( + _In_ PENDPOINT_MINIPAIR pBaseMinipair, + _In_ PUNICODE_STRING FriendlyName, + _In_ PGUID pCategory, + _In_ ULONG customFilterInterfacePropertyCount, + _In_count_(customFilterInterfacePropertyCount) DEVPROPERTY* customFilterInterfaceProperties, + _Outptr_ PENDPOINT_MINIPAIR* ppCustomMinipair + ); + + static + NTSTATUS UpdateCustomEndpointCategory + ( + _In_ PPCFILTER_DESCRIPTOR pCustomMinipairTopoFilter, + _In_ PPCPIN_DESCRIPTOR pCustomMinipairTopoPins, + _In_ PGUID pCategory + ); + + static + VOID DeleteCustomEndpointMinipair + ( + _In_ PENDPOINT_MINIPAIR CustomMinipair + ); + + NTSTATUS GetA2dpHpCodecCaps + ( + _Outptr_ PA2DPHPDEVICE_CODEC_CAPABILITIES* ppCodecCaps + ); + + NTSTATUS UpdateA2dpHpCodecCaps + ( + _In_ PA2DPHPDEVICE_CODEC_CAPABILITIES pCodecCaps + ); + + NTSTATUS SetSidebandClaimed(_In_ BOOL bClaimed); + + NTSTATUS SpeakerStreamOpen(); + + NTSTATUS SpeakerStreamStart(); + + NTSTATUS SpeakerStreamSuspend(); + + NTSTATUS SpeakerStreamClose(); + + NTSTATUS SetA2dpHpStreamOpen(ULONG EpIndex, PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pStreamFormat, PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 pEndpointDescriptor); + + NTSTATUS SetA2dpHpStreamStart(); + + NTSTATUS SetA2dpHpStreamSuspend(); + + NTSTATUS SetA2dpHpStreamClose(); + + NTSTATUS EnableA2dpHpSpeakerStreamStatusNotification(); + + NTSTATUS EnableA2dpHpSpeakerSiopNotification(); + + NTSTATUS StopA2dpHpSpeakerStreamStatusNotification(); + + NTSTATUS CreateFilterNames( + _In_ PUNICODE_STRING A2dpHpDeviceSymbolicLinkName + ); + + // + // WDF I/O Target callback. + // + static + EVT_WDF_IO_TARGET_QUERY_REMOVE EvtA2dpHpTargetQueryRemove; + + static + EVT_WDF_IO_TARGET_REMOVE_CANCELED EvtA2dpHpTargetRemoveCanceled; + + static + EVT_WDF_IO_TARGET_REMOVE_COMPLETE EvtA2dpHpTargetRemoveComplete; + + // + // Status notifications callbacks. + // + static + EVT_WDF_REQUEST_COMPLETION_ROUTINE EvtA2dpHpDeviceStreamStatusCompletion; + + static + EVT_WDF_REQUEST_COMPLETION_ROUTINE EvtA2dpHpDeviceNotificationStatusCompletion; + + static + EVT_WDF_WORKITEM EvtA2dpHpDeviceNotificationStatusWorkItem; +}; diff --git a/audio/sysvad/APO/AecApo/AecApo.h b/audio/sysvad/APO/AecApo/AecApo.h new file mode 100644 index 00000000..a62b69ce --- /dev/null +++ b/audio/sysvad/APO/AecApo/AecApo.h @@ -0,0 +1,135 @@ +// +// AecApo.h -- Copyright (c) Microsoft Corporation. All rights reserved. +// +// Description: +// +// Declaration of the CAecApoMFX class. +// + +#pragma once + +#include <audioenginebaseapo.h> +#include <BaseAudioProcessingObject.h> +#include <AecApoDll.h> + +#include <commonmacros.h> +#include <devicetopology.h> + +#include <audioengineextensionapo.h> + +#include <wil\com.h> + +_Analysis_mode_(_Analysis_code_type_user_driver_) + +#pragma AVRT_VTABLES_BEGIN +// Aec APO class - MFX +class CAecApoMFX : + public CComObjectRootEx<CComMultiThreadModel>, + public CComCoClass<CAecApoMFX, &CLSID_AecApoMFX>, + public CBaseAudioProcessingObject, + public IAudioSystemEffects3, + public IAudioProcessingObjectNotifications, + public IApoAcousticEchoCancellation, + public IApoAuxiliaryInputConfiguration, + public IApoAuxiliaryInputRT +{ +public: + // constructor + CAecApoMFX() + : CBaseAudioProcessingObject(sm_RegProperties) + { + } + +DECLARE_REGISTRY_RESOURCEID(IDR_AECAPOMFX) + +BEGIN_COM_MAP(CAecApoMFX) + COM_INTERFACE_ENTRY(IAudioSystemEffects) + COM_INTERFACE_ENTRY(IAudioSystemEffects2) + COM_INTERFACE_ENTRY(IAudioSystemEffects3) + COM_INTERFACE_ENTRY(IAudioProcessingObjectNotifications) + COM_INTERFACE_ENTRY(IAudioProcessingObjectRT) + COM_INTERFACE_ENTRY(IAudioProcessingObject) + COM_INTERFACE_ENTRY(IAudioProcessingObjectConfiguration) + COM_INTERFACE_ENTRY(IApoAcousticEchoCancellation) + COM_INTERFACE_ENTRY(IApoAuxiliaryInputConfiguration) + COM_INTERFACE_ENTRY(IApoAuxiliaryInputRT) +END_COM_MAP() + +DECLARE_PROTECT_FINAL_CONSTRUCT() + +public: + STDMETHOD_(void, APOProcess)(UINT32 u32NumInputConnections, + APO_CONNECTION_PROPERTY** ppInputConnections, UINT32 u32NumOutputConnections, + APO_CONNECTION_PROPERTY** ppOutputConnections); + + STDMETHOD(GetLatency)(HNSTIME* pTime); + + STDMETHOD(LockForProcess)(UINT32 u32NumInputConnections, + APO_CONNECTION_DESCRIPTOR** ppInputConnections, + UINT32 u32NumOutputConnections, APO_CONNECTION_DESCRIPTOR** ppOutputConnections); + + STDMETHOD(Initialize)(UINT32 cbDataSize, BYTE* pbyData); + + // IAudioSystemEffects2 + STDMETHOD(GetEffectsList)(_Outptr_result_buffer_maybenull_(*pcEffects) LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE Event); + + // IAudioProcessingObject + STDMETHOD(IsInputFormatSupported)(IAudioMediaType *pOutputFormat, IAudioMediaType *pRequestedInputFormat, IAudioMediaType **ppSupportedInputFormat); + STDMETHOD(IsOutputFormatSupported)(IAudioMediaType *pInputFormat, IAudioMediaType *pRequestedOutputFormat, IAudioMediaType **ppSupportedOutputFormat); + + // IAPOAuxiliaryInputConfiguration + STDMETHOD(AddAuxiliaryInput)( + DWORD dwInputId, + UINT32 cbDataSize, + BYTE *pbyData, + APO_CONNECTION_DESCRIPTOR *pInputConnection + ) override; + STDMETHOD(RemoveAuxiliaryInput)( + DWORD dwInputId + ) override; + STDMETHOD(IsInputFormatSupported)( + IAudioMediaType* pRequestedInputFormat, + IAudioMediaType** ppSupportedInputFormat + ) override; + + // IAPOAuxiliaryInputRT + STDMETHOD_(void, AcceptInput)( + DWORD dwInputId, + const APO_CONNECTION_PROPERTY *pInputConnection + ) override; + + // IAudioSystemEffects3 + STDMETHODIMP GetControllableSystemEffectsList(_Outptr_result_buffer_maybenull_(*numEffects) AUDIO_SYSTEMEFFECT** effects, _Out_ UINT* numEffects, _In_opt_ HANDLE event) override + { + UNREFERENCED_PARAMETER(effects); + UNREFERENCED_PARAMETER(numEffects); + UNREFERENCED_PARAMETER(event); + return S_OK; + } + + STDMETHODIMP SetAudioSystemEffectState(GUID, AUDIO_SYSTEMEFFECT_STATE) override {return S_OK;} + + // IAudioProcessingObjectNotifications + STDMETHOD(GetApoNotificationRegistrationInfo)(_Out_writes_(*count) APO_NOTIFICATION_DESCRIPTOR** apoNotifications, _Out_ DWORD* count) override; + STDMETHOD_(void, HandleNotification)(_In_ APO_NOTIFICATION* apoNotification) override; + +public: + UINT64 m_auxiliaryInputId = 0; + static const CRegAPOProperties<1> sm_RegProperties; // registration properties + BOOL m_initializedForEffectsDiscovery = FALSE; + GUID m_audioSignalProcessingMode = GUID_NULL; + + CComPtr<IMMDevice> m_spCaptureDevice; + CComPtr<IMMDevice> m_spLoopbackDevice; + + float m_captureEndpointMasterVolume = 0; + float m_loopbackEndpointMasterVolume = 0; + +private: + wil::com_ptr_nothrow<IAudioProcessingObjectLoggingService> m_apoLoggingService; +}; +#pragma AVRT_VTABLES_END + +OBJECT_ENTRY_AUTO(__uuidof(AecApoMFX), CAecApoMFX) + + diff --git a/audio/sysvad/APO/AecApo/AecApo.png b/audio/sysvad/APO/AecApo/AecApo.png Binary files differnew file mode 100644 index 00000000..76115f4d --- /dev/null +++ b/audio/sysvad/APO/AecApo/AecApo.png diff --git a/audio/sysvad/APO/AecApo/AecApo.vcxproj b/audio/sysvad/APO/AecApo/AecApo.vcxproj new file mode 100644 index 00000000..f40ce919 --- /dev/null +++ b/audio/sysvad/APO/AecApo/AecApo.vcxproj @@ -0,0 +1,353 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|ARM64"> + <Configuration>Debug</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|ARM64"> + <Configuration>Release</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{83A1B9B0-3CB7-4249-ADEC-C336B0EDDE76}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <SupportsPackaging>false</SupportsPackaging> + <RequiresPackageProject>true</RequiresPackageProject> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{D5CCDCB1-348E-414E-BB0F-33C773760034}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <EmbedManifest>false</EmbedManifest> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <EmbedManifest>false</EmbedManifest> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <EmbedManifest>false</EmbedManifest> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <EmbedManifest>false</EmbedManifest> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <EmbedManifest>false</EmbedManifest> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <EmbedManifest>false</EmbedManifest> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>AecApo</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <TargetName>AecApo</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>AecApo</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <TargetName>AecApo</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>AecApo</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>AecApo</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <UseOfAtl>Dynamic</UseOfAtl> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <UseOfAtl>Dynamic</UseOfAtl> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <UseOfAtl>Dynamic</UseOfAtl> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <UseOfAtl>Dynamic</UseOfAtl> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <UseOfAtl>Dynamic</UseOfAtl> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <UseOfAtl>Dynamic</UseOfAtl> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.;..\..\..\..\wil\include</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + <RuntimeLibrary /> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib</AdditionalDependencies> + <ModuleDefinitionFile>AecApoDll.def</ModuleDefinitionFile> + <AdditionalOptions>/ignore:4217,4049 %(AdditionalOptions)</AdditionalOptions> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.;..\..\..\..\wil\include</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + <RuntimeLibrary> + </RuntimeLibrary> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib</AdditionalDependencies> + <ModuleDefinitionFile>AecApoDll.def</ModuleDefinitionFile> + <AdditionalOptions>/ignore:4217,4049 %(AdditionalOptions)</AdditionalOptions> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.;..\..\..\..\wil\include</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + <RuntimeLibrary /> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib</AdditionalDependencies> + <ModuleDefinitionFile>AecApoDll.def</ModuleDefinitionFile> + <AdditionalOptions>/ignore:4217,4049 %(AdditionalOptions)</AdditionalOptions> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.;..\..\..\..\wil\include</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + <RuntimeLibrary> + </RuntimeLibrary> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib</AdditionalDependencies> + <ModuleDefinitionFile>AecApoDll.def</ModuleDefinitionFile> + <AdditionalOptions>/ignore:4217,4049 %(AdditionalOptions)</AdditionalOptions> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.;..\..\..\..\wil\include</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + <RuntimeLibrary /> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib</AdditionalDependencies> + <ModuleDefinitionFile>AecApoDll.def</ModuleDefinitionFile> + <AdditionalOptions>/ignore:4217,4049 %(AdditionalOptions)</AdditionalOptions> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.;..\..\..\..\wil\include</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + <RuntimeLibrary /> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib</AdditionalDependencies> + <ModuleDefinitionFile>AecApoDll.def</ModuleDefinitionFile> + <AdditionalOptions>/ignore:4217,4049 %(AdditionalOptions)</AdditionalOptions> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="AecApoDll.cpp" /> + <ClCompile Include="AecApoMFX.cpp" /> + <Midl Include="AecApoDll.idl" /> + <ResourceCompile Include="AecApoDll.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="AecApo.png" /> + <None Exclude="@(None)" Include="AecApoDll.def" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="AecApo.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/audio/sysvad/APO/AecApo/AecApo.vcxproj.Filters b/audio/sysvad/APO/AecApo/AecApo.vcxproj.Filters new file mode 100644 index 00000000..36906cda --- /dev/null +++ b/audio/sysvad/APO/AecApo/AecApo.vcxproj.Filters @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{5f4ff11d-7a0b-4f75-9d79-744ae028cc53}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{bd103b81-05b2-440e-84e7-0723d7fe4109}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{a57b6b78-38ae-4348-ade3-0200e81a3dd7}</UniqueIdentifier> + </Filter> + <Filter Include="IDL Files"> + <Extensions>idl</Extensions> + <UniqueIdentifier>{c6318700-1bce-4812-92f1-160881e4c976}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="Resource.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="AecApo.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <None Include="AecApo.png"> + <Filter>Resource Files</Filter> + </None> + <None Include="AecApoDll.cpp"> + <Filter>Source Files</Filter> + </None> + <None Include="AecApoMfx.cpp"> + <Filter>Source Files</Filter> + </None> + <None Include="AecApoDll.def"> + <Filter>Source Files</Filter> + </None> + <None Include="AecApo.png"> + <Filter>Resource Files</Filter> + </None> + <None Include="AecApoDll.def"> + <Filter>Source Files</Filter> + </None> + </ItemGroup> + <ItemGroup> + <ClCompile Include="AecApoDll.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="AecApoMFX.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="AecApoDll.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> + <ItemGroup> + <Midl Include="AecApoDll.idl"> + <Filter>IDL Files</Filter> + </Midl> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/audio/sysvad/APO/AecApo/AecApoDll.cpp b/audio/sysvad/APO/AecApo/AecApoDll.cpp new file mode 100644 index 00000000..70fa103d --- /dev/null +++ b/audio/sysvad/APO/AecApo/AecApoDll.cpp @@ -0,0 +1,71 @@ +// +// AecApoDll.cpp -- Copyright (c) Microsoft Corporation. All rights reserved. +// +// Author: +// +// Description: +// +// AecApoDll.cpp : Implementation of DLL Exports. + +#include <atlbase.h> +#include <atlcom.h> +#include <atlcoll.h> +#include <atlsync.h> +#include <mmreg.h> + +#include "resource.h" +#include "AecApoDll.h" +#include <AecApo.h> + +#include <AecApoDll_i.c> + + +//------------------------------------------------------------------------- +// Array of APO_REG_PROPERTIES structures implemented in this module. +// Each new APO implementation will be added to this array. +// +APO_REG_PROPERTIES const *gCoreAPOs[] = +{ + &CAecApoMFX::sm_RegProperties.m_Properties +}; + +// {secret} +class CAecApoDllModule : public CAtlDllModuleT< CAecApoDllModule > +{ +public : + DECLARE_LIBID(LIBID_AecApoDlllib) + DECLARE_REGISTRY_APPID_RESOURCEID(IDR_AECAPODLL, "{1314DF8C-99BA-4E06-8ABD-CB38614EDFF7}") + +}; + +// {secret} +CAecApoDllModule _AtlModule; + + +// {secret} +extern "C" BOOL WINAPI DllMain(HINSTANCE /* hInstance */, DWORD dwReason, LPVOID lpReserved) +{ + if (DLL_PROCESS_ATTACH == dwReason) + { + } + // do necessary cleanup only if the DLL is being unloaded dynamically + else if ((DLL_PROCESS_DETACH == dwReason) && (NULL == lpReserved)) + { + } + + return _AtlModule.DllMain(dwReason, lpReserved); +} + + +// {secret} +STDAPI DllCanUnloadNow(void) +{ + return _AtlModule.DllCanUnloadNow(); +} + + +// {secret} +STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID FAR* ppv) +{ + return _AtlModule.DllGetClassObject(rclsid, riid, ppv); +} diff --git a/audio/sysvad/APO/AecApo/AecApoDll.def b/audio/sysvad/APO/AecApo/AecApoDll.def new file mode 100644 index 00000000..3f3b0c5a --- /dev/null +++ b/audio/sysvad/APO/AecApo/AecApoDll.def @@ -0,0 +1,4 @@ + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE
\ No newline at end of file diff --git a/audio/sysvad/APO/AecApo/AecApoDll.idl b/audio/sysvad/APO/AecApo/AecApoDll.idl new file mode 100644 index 00000000..17461922 --- /dev/null +++ b/audio/sysvad/APO/AecApo/AecApoDll.idl @@ -0,0 +1,39 @@ +// +// AecAPODll.idl -- Copyright (c) Microsoft Corporation. All rights reserved. +// +// Author: +// +// Description: +// +// AecAPODll.idl : Definition of COM interfaces and coclasses for the DLL. + +import "oaidl.idl"; +import "ocidl.idl"; +import "audioenginebaseapo.idl"; + +//------------------------------------------------------------------------- +// AecApoDlllib +// +[ + uuid(6D07D481-D230-493D-A521-3FBBB4619E2E), + version(1.0) +] +library AecApoDlllib +{ + importlib("stdole2.tlb"); + + // for Aec APO - MFX + [ + uuid(325B7F6F-ED6C-40CE-814C-00D91FED053F) + ] + coclass AecApoMFX + { + [default] interface IAudioProcessingObject; + interface IAudioProcessingObjectRT; + interface IAudioProcessingObjectConfiguration; + interface IAudioSystemEffects; + // KK add to this + }; + + +} diff --git a/audio/sysvad/APO/AecApo/AecApoDll.rc b/audio/sysvad/APO/AecApo/AecApoDll.rc new file mode 100644 index 00000000..796d5204 --- /dev/null +++ b/audio/sysvad/APO/AecApo/AecApoDll.rc @@ -0,0 +1,20 @@ +#if 0 +Copyright (c) Microsoft Corporation. All Rights Reserved +#endif + +#include "resource.h" +#include "winres.h" +#include <ntverp.h> +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "AEC APO" +#define VER_INTERNALNAME_STR "AecApo" +#define VER_ORIGINALFILENAME_STR "AecApo.Dll" +#include <Common.ver> + +IDR_AECAPODLL REGISTRY "AecApoDll.rgs" +IDR_AECAPOMFX REGISTRY "AecApoMFX.rgs" + +// ICON +IDI_EFFECT_ICON RCDATA "AecApo.png" + diff --git a/audio/sysvad/APO/AecApo/AecApoDll.rgs b/audio/sysvad/APO/AecApo/AecApoDll.rgs new file mode 100644 index 00000000..cf3415a1 --- /dev/null +++ b/audio/sysvad/APO/AecApo/AecApoDll.rgs @@ -0,0 +1,11 @@ +HKCR +{ + NoRemove AppID + { + '%APPID%' = s 'AecApoDll' + 'AecApoDll.DLL' + { + val AppID = s '%APPID%' + } + } +} diff --git a/audio/sysvad/APO/AecApo/AecApoMfx.cpp b/audio/sysvad/APO/AecApo/AecApoMfx.cpp new file mode 100644 index 00000000..21f2c20d --- /dev/null +++ b/audio/sysvad/APO/AecApo/AecApoMfx.cpp @@ -0,0 +1,785 @@ +// +// AecApoMFX.cpp -- Copyright (c) Microsoft Corporation. All rights reserved. +// +// Description: +// +// Implementation of CAecApoMFX +// + +#include <atlbase.h> +#include <atlcom.h> +#include <atlcoll.h> +#include <atlsync.h> +#include <mmreg.h> + +#include <initguid.h> +#include <audioenginebaseapo.h> +#include <baseaudioprocessingobject.h> +#include <resource.h> + +#include <float.h> + +#include "AecApo.h" +#include <devicetopology.h> +#include <CustomPropKeys.h> + +#define SUPPORTED_AEC_SAMPLINGRATE (16000) + +// Static declaration of the APO_REG_PROPERTIES structure +// associated with this APO. The number in <> brackets is the +// number of IIDs supported by this APO. If more than one, then additional +// IIDs are added at the end +#pragma warning (disable : 4815) +const AVRT_DATA CRegAPOProperties<1> CAecApoMFX::sm_RegProperties( + __uuidof(AecApoMFX), // clsid of this APO + L"Sample MFX Aec APO", // friendly name of this APO + L"Copyright (c) Microsoft Corporation", // copyright info + 1, // major version # + 0, // minor version # + __uuidof(IAudioProcessingObject), // iid of primary interface + (APO_FLAG) (APO_FLAG_BITSPERSAMPLE_MUST_MATCH | APO_FLAG_FRAMESPERSECOND_MUST_MATCH), // kak check this + DEFAULT_APOREG_MININPUTCONNECTIONS, + DEFAULT_APOREG_MAXINPUTCONNECTIONS, + DEFAULT_APOREG_MINOUTPUTCONNECTIONS, + DEFAULT_APOREG_MAXOUTPUTCONNECTIONS, + DEFAULT_APOREG_MAXINSTANCES + ); + + +#pragma AVRT_CODE_BEGIN +//------------------------------------------------------------------------- +// Description: +// +// Do the actual processing of data. +// +// Parameters: +// +// u32NumInputConnections - [in] number of input connections +// ppInputConnections - [in] pointer to list of input APO_CONNECTION_PROPERTY pointers +// u32NumOutputConnections - [in] number of output connections +// ppOutputConnections - [in] pointer to list of output APO_CONNECTION_PROPERTY pointers +// +// Return values: +// +// void +// +// Remarks: +// +// This function processes data in a manner dependent on the implementing +// object. This routine can not fail and can not block, or call any other +// routine that blocks, or touch pagable memory. +// +STDMETHODIMP_(void) CAecApoMFX::APOProcess( + UINT32 u32NumInputConnections, + APO_CONNECTION_PROPERTY** ppInputConnections, + UINT32 u32NumOutputConnections, + APO_CONNECTION_PROPERTY** ppOutputConnections) +{ + UNREFERENCED_PARAMETER(u32NumInputConnections); + UNREFERENCED_PARAMETER(u32NumOutputConnections); + + FLOAT32 *pf32InputFrames, *pf32OutputFrames; + + ATLASSERT(m_bIsLocked); + + // assert that the number of input and output connectins fits our registration properties + ATLASSERT(m_pRegProperties->u32MinInputConnections <= u32NumInputConnections); + ATLASSERT(m_pRegProperties->u32MaxInputConnections >= u32NumInputConnections); + ATLASSERT(m_pRegProperties->u32MinOutputConnections <= u32NumOutputConnections); + ATLASSERT(m_pRegProperties->u32MaxOutputConnections >= u32NumOutputConnections); + + ATLASSERT(ppInputConnections[0]->u32Signature == APO_CONNECTION_PROPERTY_V2_SIGNATURE); + ATLASSERT(ppOutputConnections[0]->u32Signature == APO_CONNECTION_PROPERTY_V2_SIGNATURE); + + APO_CONNECTION_PROPERTY_V2* inConnection = reinterpret_cast<APO_CONNECTION_PROPERTY_V2*>(ppInputConnections[0]); + APO_CONNECTION_PROPERTY_V2* outConnection = reinterpret_cast<APO_CONNECTION_PROPERTY_V2*>(ppOutputConnections[0]); + + // check APO_BUFFER_FLAGS. + switch( ppInputConnections[0]->u32BufferFlags ) + { + case BUFFER_INVALID: + { + ATLASSERT(false); // invalid flag - should never occur. don't do anything. + break; + } + case BUFFER_VALID: + case BUFFER_SILENT: + { + // get input pointer to connection buffer + pf32InputFrames = reinterpret_cast<FLOAT32*>(ppInputConnections[0]->pBuffer); + ATLASSERT( IS_VALID_TYPED_READ_POINTER(pf32InputFrames) ); + + // get output pointer to connection buffer + pf32OutputFrames = reinterpret_cast<FLOAT32*>(ppOutputConnections[0]->pBuffer); + ATLASSERT( IS_VALID_TYPED_WRITE_POINTER(pf32OutputFrames) ); + + // + // Provide microphone buffer and timestamps to AEC algorithm + // + UNREFERENCED_PARAMETER(inConnection); + UNREFERENCED_PARAMETER(outConnection); + + // Set the valid frame count. + ppOutputConnections[0]->u32ValidFrameCount = ppInputConnections[0]->u32ValidFrameCount; + ppOutputConnections[0]->u32BufferFlags = ppInputConnections[0]->u32BufferFlags; + + break; + } + default: + { + ATLASSERT(false); // invalid flag - should never occur + break; + } + } // switch + +} // APOProcess +#pragma AVRT_CODE_END + +//------------------------------------------------------------------------- +// Description: +// +// Parameters: +// +// pTime - [out] hundreds-of-nanoseconds +// +// Return values: +// +// S_OK on success, a failure code on failure +STDMETHODIMP CAecApoMFX::GetLatency(HNSTIME* pTime) +{ + ASSERT_NONREALTIME(); + HRESULT hr = S_OK; + + IF_TRUE_ACTION_JUMP(NULL == pTime, hr = E_POINTER, Exit); + + *pTime = 0; + +Exit: + return hr; +} + + +//------------------------------------------------------------------------- +// Description: +// +// Verifies that the APO is ready to process and locks its state if so. +// +// Parameters: +// +// u32NumInputConnections - [in] number of input connections attached to this APO +// ppInputConnections - [in] connection descriptor of each input connection attached to this APO +// u32NumOutputConnections - [in] number of output connections attached to this APO +// ppOutputConnections - [in] connection descriptor of each output connection attached to this APO +// +// Return values: +// +// S_OK Object is locked and ready to process. +// E_POINTER Invalid pointer passed to function. +// APOERR_INVALID_CONNECTION_FORMAT Invalid connection format. +// APOERR_NUM_CONNECTIONS_INVALID Number of input or output connections is not valid on +// this APO. +STDMETHODIMP CAecApoMFX::LockForProcess(UINT32 u32NumInputConnections, + APO_CONNECTION_DESCRIPTOR** ppInputConnections, + UINT32 u32NumOutputConnections, APO_CONNECTION_DESCRIPTOR** ppOutputConnections) +{ + ASSERT_NONREALTIME(); + HRESULT hr = S_OK; + + UNCOMPRESSEDAUDIOFORMAT uncompAudioFormat; + + // fill in the samples per frame for the output (since APO_FLAG_SAMPLESPERFRAME_MUST_MATCH is not selected) + // There are two potentially different samples per frame values here. The input, which will be interleaved + primary. + // And the output, which is just the primary. Because this is used for clearing the zeroing the output buffer, we're going + // to fill it in with the output samples per frame. ProcessBuffer has both. + hr = ppOutputConnections[0]->pFormat->GetUncompressedAudioFormat(&uncompAudioFormat); + IF_FAILED_JUMP(hr, Exit); + + m_u32SamplesPerFrame = uncompAudioFormat.dwSamplesPerFrame; + + hr = CBaseAudioProcessingObject::LockForProcess(u32NumInputConnections, + ppInputConnections, u32NumOutputConnections, ppOutputConnections); + IF_FAILED_JUMP(hr, Exit); + +Exit: + return hr; +} + +// The method that this long comment refers to is "Initialize()" +//------------------------------------------------------------------------- +// Description: +// +// Generic initialization routine for APOs. +// +// Parameters: +// +// cbDataSize - [in] the size in bytes of the initialization data. +// pbyData - [in] initialization data specific to this APO +// +// Return values: +// +// S_OK Successful completion. +// E_POINTER Invalid pointer passed to this function. +// E_INVALIDARG Invalid argument +// AEERR_ALREADY_INITIALIZED APO is already initialized +// +// Remarks: +// +// This method initializes the APO. The data is variable length and +// should have the form of: +// +// struct MyAPOInitializationData +// { +// APOInitBaseStruct APOInit; +// ... // add additional fields here +// }; +// +// If the APO needs no initialization or needs no data to initialize +// itself, it is valid to pass NULL as the pbyData parameter and 0 as +// the cbDataSize parameter. +// +// As part of designing an APO, decide which parameters should be +// immutable (set once during initialization) and which mutable (changeable +// during the lifetime of the APO instance). Immutable parameters must +// only be specifiable in the Initialize call; mutable parameters must be +// settable via methods on whichever parameter control interface(s) your +// APO provides. Mutable values should either be set in the initialize +// method (if they are required for proper operation of the APO prior to +// LockForProcess) or default to reasonable values upon initialize and not +// be required to be set before LockForProcess. +// +// Within the mutable parameters, you must also decide which can be changed +// while the APO is locked for processing and which cannot. +// +// All parameters should be considered immutable as a first choice, unless +// there is a specific scenario which requires them to be mutable; similarly, +// no mutable parameters should be changeable while the APO is locked, unless +// a specific scenario requires them to be. Following this guideline will +// simplify the APO's state diagram and implementation and prevent certain +// types of bug. +// +// If a parameter changes the APOs latency or MaxXXXFrames values, it must be +// immutable. +// +// The default version of this function uses no initialization data, but does verify +// the passed parameters and set the m_bIsInitialized member to true. +// +// Note: This method may not be called from a real-time processing thread. +// + +HRESULT CAecApoMFX::Initialize(UINT32 cbDataSize, BYTE* pbyData) +{ + HRESULT hr = S_OK; + + IF_TRUE_ACTION_JUMP( ((NULL == pbyData) && (0 != cbDataSize)), hr = E_INVALIDARG, Exit); + IF_TRUE_ACTION_JUMP( ((NULL != pbyData) && (0 == cbDataSize)), hr = E_INVALIDARG, Exit); + + if (cbDataSize == sizeof(APOInitSystemEffects3)) + { + // + // pbyData contains APOInitSystemEffects3 structure describing the microphone endpoint + // + APOInitSystemEffects3* papoSysFxInit3 = (APOInitSystemEffects3*)pbyData; + m_initializedForEffectsDiscovery = papoSysFxInit3->InitializeForDiscoveryOnly; + + // Support for COMMUNICATIONS mode only when streaming + IF_TRUE_ACTION_JUMP( + !m_initializedForEffectsDiscovery && (papoSysFxInit3->AudioProcessingMode != AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS), + hr = E_INVALIDARG, + Exit); + m_audioSignalProcessingMode = papoSysFxInit3->AudioProcessingMode; + + // Register for notification of endpoint volume change in GetApoNotificationRegistrationInfo + // Keep a reference to the device that will be registering for endpoint volume notifcations. + + IF_TRUE_ACTION_JUMP(papoSysFxInit3->pDeviceCollection == nullptr, hr = E_INVALIDARG, Exit); + // Get the endpoint on which this APO has been created. It is the last device in the device collection. + UINT32 numDevices; + hr = papoSysFxInit3->pDeviceCollection->GetCount(&numDevices); + IF_FAILED_JUMP(hr, Exit); + IF_TRUE_ACTION_JUMP(numDevices <= 0, hr = E_INVALIDARG, Exit); + + hr = papoSysFxInit3->pDeviceCollection->Item(numDevices - 1, &m_spCaptureDevice); + IF_FAILED_JUMP(hr, Exit); + + m_bIsInitialized = true; + + // Try to get the logging service, but ignore errors as failure to do logging it is not fatal. + if(SUCCEEDED(papoSysFxInit3->pServiceProvider->QueryService(SID_AudioProcessingObjectLoggingService, IID_PPV_ARGS(&m_apoLoggingService)))) + { + m_apoLoggingService->ApoLog(APO_LOG_LEVEL_INFO, L"CAecApoMFX::Initialize called with APOInitSystemEffects3."); + } + } + else if (cbDataSize == sizeof(APOInitSystemEffects2)) + { + // + // pbyData contains APOInitSystemEffects2 structure describing the microphone endpoint + // + APOInitSystemEffects2* papoSysFxInit2 = (APOInitSystemEffects2*)pbyData; + m_initializedForEffectsDiscovery = papoSysFxInit2->InitializeForDiscoveryOnly; + + // Support for COMMUNICATIONS mode only when streaming + IF_TRUE_ACTION_JUMP( + !m_initializedForEffectsDiscovery && (papoSysFxInit2->AudioProcessingMode != AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS), + hr = E_INVALIDARG, + Exit); + m_audioSignalProcessingMode = papoSysFxInit2->AudioProcessingMode; + + m_bIsInitialized = true; + } + else + { + hr = E_INVALIDARG; + } + +Exit: + return hr; +} + +//------------------------------------------------------------------------- +// Description: +// +// +// +// Parameters: +// +// +// +// Return values: +// +// +// +// Remarks: +// +// +STDMETHODIMP CAecApoMFX::GetEffectsList(_Outptr_result_buffer_maybenull_(*pcEffects) LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE Event) +{ + UNREFERENCED_PARAMETER(Event); + + *ppEffectsIds = NULL; + *pcEffects = 0; + + if (m_audioSignalProcessingMode == AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS) + { + // Return the list of effects implemented by this APO for COMMUNICATIONS processing mode + static const GUID effectsList[] = { AUDIO_EFFECT_TYPE_ACOUSTIC_ECHO_CANCELLATION }; + + *ppEffectsIds = (LPGUID)CoTaskMemAlloc(sizeof(effectsList)); + if (!*ppEffectsIds) + { + return E_OUTOFMEMORY; + } + *pcEffects = ARRAYSIZE(effectsList); + CopyMemory(*ppEffectsIds, effectsList, sizeof(effectsList)); + } + + return S_OK; +} + +HRESULT IsInputFormatSupportedForAec(IAudioMediaType* pMediaType, BOOL * pSupported) +{ + ASSERT_NONREALTIME(); + + HRESULT hr = S_OK; + UNCOMPRESSEDAUDIOFORMAT format; + + IF_TRUE_ACTION_JUMP((pMediaType == nullptr || pSupported == nullptr), hr = E_INVALIDARG, exit); + hr = pMediaType->GetUncompressedAudioFormat(&format); + IF_FAILED_JUMP(hr, exit); + + *pSupported = format.dwBytesPerSampleContainer == 4 && + format.dwValidBitsPerSample == 32 && + format.fFramesPerSecond == SUPPORTED_AEC_SAMPLINGRATE && // We only support one sampling rate at the input + format.dwSamplesPerFrame <= 16 && // We only support <= 16 channels at the input + format.guidFormatType == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + +exit: + return hr; +} + +HRESULT IsOutputFormatSupportedForAec(IAudioMediaType* pMediaType, BOOL * pSupported) +{ + ASSERT_NONREALTIME(); + + HRESULT hr = S_OK; + UNCOMPRESSEDAUDIOFORMAT format; + + IF_TRUE_ACTION_JUMP((pMediaType == nullptr || pSupported == nullptr), hr = E_INVALIDARG, exit); + hr = pMediaType->GetUncompressedAudioFormat(&format); + IF_FAILED_JUMP(hr, exit); + + *pSupported = format.dwBytesPerSampleContainer == 4 && + format.dwValidBitsPerSample == 32 && + format.fFramesPerSecond == SUPPORTED_AEC_SAMPLINGRATE && + format.dwSamplesPerFrame == 1 && // We only mono output + format.guidFormatType == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + +exit: + return hr; +} + +HRESULT +CreatePreferredInputMediaType(IAudioMediaType** ppMediaType, UINT32 requestedInputChannelCount) +{ + ASSERT_NONREALTIME(); + + // Default to mono format + // We will adjust the channel count based on the requested format later on + UNCOMPRESSEDAUDIOFORMAT format = + { + KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, + 1, 4, 32, SUPPORTED_AEC_SAMPLINGRATE, KSAUDIO_SPEAKER_DIRECTOUT + }; + + // Match the channel count of the input if it is less than 16 + if (requestedInputChannelCount <= 16) + { + format.dwSamplesPerFrame = requestedInputChannelCount; + format.dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT; + } + + return CreateAudioMediaTypeFromUncompressedAudioFormat(&format, ppMediaType); +} + +HRESULT +CreatePreferredOutputMediaType(IAudioMediaType** ppMediaType) +{ + ASSERT_NONREALTIME(); + + // Output is mono @ the same sampling rate as the input + UNCOMPRESSEDAUDIOFORMAT format = + { + KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, + 1, 4, 32, SUPPORTED_AEC_SAMPLINGRATE, KSAUDIO_SPEAKER_DIRECTOUT + }; + + return CreateAudioMediaTypeFromUncompressedAudioFormat(&format, ppMediaType); +} + +//------------------------------------------------------------------------- +// Description: +// +// +// +// Parameters: +// +// +// +// Return values: +// +// +// +// Remarks: +// +// +STDMETHODIMP CAecApoMFX::IsInputFormatSupported(IAudioMediaType *pOutputFormat, IAudioMediaType *pRequestedInputFormat, IAudioMediaType **ppSupportedInputFormat) +{ + ASSERT_NONREALTIME(); + HRESULT hResult; + + IF_TRUE_ACTION_JUMP((NULL == pRequestedInputFormat) || (NULL == ppSupportedInputFormat), hResult = E_POINTER, Exit); + *ppSupportedInputFormat = NULL; + + // This method here is called in the context of the MIC endpoint + // There are 2 supported scenarios + // - The AEC APO can handle any mic format + // - The AEC APO can support exactly 1 input format + // + // For the purposes of this sample AEC APO, we pretend that the AEC APO supports exactly 1 format + // - a sampling rate of SUPPORTED_AEC_SAMPLINGRATE. The APO can accept upto 16 channels at the input + // and will output mono audio. + // + + if (pOutputFormat) + { + // Is this a valid format that we support at the output? + BOOL bSupportedOut = FALSE; + hResult = IsOutputFormatSupportedForAec(pOutputFormat, &bSupportedOut); + IF_FAILED_JUMP(hResult, Exit); + if (!bSupportedOut) + { + return APOERR_FORMAT_NOT_SUPPORTED; + } + } + + BOOL bSupported = FALSE; + hResult = IsInputFormatSupportedForAec(pRequestedInputFormat, &bSupported); + IF_FAILED_JUMP(hResult, Exit); + + if (!bSupported) + { + hResult = CreatePreferredInputMediaType(ppSupportedInputFormat, pRequestedInputFormat->GetAudioFormat()->nChannels); + IF_FAILED_JUMP(hResult, Exit); + return S_FALSE; + } + + pRequestedInputFormat->AddRef(); + *ppSupportedInputFormat = pRequestedInputFormat; + +Exit: + + return hResult; +} + +//------------------------------------------------------------------------- +// Description: +// +// +// +// Parameters: +// +// +// +// Return values: +// +// +// +// Remarks: +// +// +STDMETHODIMP CAecApoMFX::IsOutputFormatSupported(IAudioMediaType *pInputFormat, IAudioMediaType *pRequestedOutputFormat, IAudioMediaType **ppSupportedOutputFormat) +{ + ASSERT_NONREALTIME(); + HRESULT hResult; + + IF_TRUE_ACTION_JUMP((NULL == pRequestedOutputFormat) || (NULL == ppSupportedOutputFormat), hResult = E_POINTER, Exit); + *ppSupportedOutputFormat = NULL; + + if (pInputFormat != nullptr) + { + BOOL bSupportedIn = FALSE; + hResult = IsInputFormatSupportedForAec(pInputFormat, &bSupportedIn); + IF_FAILED_JUMP(hResult, Exit); + if (!bSupportedIn) + { + return APOERR_FORMAT_NOT_SUPPORTED; + } + } + + BOOL bSupported = FALSE; + hResult = IsOutputFormatSupportedForAec(pRequestedOutputFormat, &bSupported); + IF_FAILED_JUMP(hResult, Exit); + + if (!bSupported) + { + hResult = CreatePreferredOutputMediaType(ppSupportedOutputFormat); + IF_FAILED_JUMP(hResult, Exit); + return S_FALSE; + } + + pRequestedOutputFormat->AddRef(); + *ppSupportedOutputFormat = pRequestedOutputFormat; + +Exit: + + return hResult; +} + +STDMETHODIMP +CAecApoMFX::AddAuxiliaryInput( + DWORD dwInputId, + UINT32 cbDataSize, + BYTE *pbyData, + APO_CONNECTION_DESCRIPTOR * pInputConnection +) +{ + HRESULT hResult = S_OK; + + CComPtr<IAudioMediaType> spSupportedType; + ASSERT_NONREALTIME(); + + IF_TRUE_ACTION_JUMP(m_bIsLocked, hResult = APOERR_APO_LOCKED, Exit); + IF_TRUE_ACTION_JUMP(!m_bIsInitialized, hResult = APOERR_NOT_INITIALIZED, Exit); + + BOOL bSupported = FALSE; + hResult = IsInputFormatSupportedForAec(pInputConnection->pFormat, &bSupported); + IF_FAILED_JUMP(hResult, Exit); + IF_TRUE_ACTION_JUMP(!bSupported, hResult = APOERR_FORMAT_NOT_SUPPORTED, Exit); + + // This APO can only handle 1 auxiliary input + IF_TRUE_ACTION_JUMP(m_auxiliaryInputId != 0, hResult = APOERR_NUM_CONNECTIONS_INVALID, Exit); + + m_auxiliaryInputId = dwInputId; + + IF_TRUE_ACTION_JUMP( ((NULL == pbyData) && (0 != cbDataSize)), hResult = E_INVALIDARG, Exit); + IF_TRUE_ACTION_JUMP( ((NULL != pbyData) && (0 == cbDataSize)), hResult = E_INVALIDARG, Exit); + if (cbDataSize == sizeof(APOInitSystemEffects3)) + { + /* + // + // pbyData contains APOInitSystemEffects3 structure describing the loopback endpoint + // + APOInitSystemEffects3* papoSysFxInit3 = (APOInitSystemEffects3*)pbyData; + + // Register for notification in GetApoNotificationRegistrationInfo + + // Keep a reference to the loopback device that will be registering for endpoint volume notifcations. + + IF_TRUE_ACTION_JUMP(papoSysFxInit3->pDeviceCollection == nullptr, hResult = E_INVALIDARG, Exit); + UINT32 numDevices; + hResult = papoSysFxInit3->pDeviceCollection->GetCount(&numDevices); + IF_FAILED_JUMP(hResult, Exit); + IF_TRUE_ACTION_JUMP(numDevices <= 0, hResult = E_INVALIDARG, Exit); + + hResult = papoSysFxInit3->pDeviceCollection->Item(numDevices - 1, &m_spLoopbackDevice); + IF_FAILED_JUMP(hResult, Exit); + */ + } + else + { + // + // pbyData contains APOInitSystemEffects2 structure describing the render endpoint + // + } + + // Signal to AEC algorithm that there is a reference audio stream + +Exit: + return hResult; +} + +STDMETHODIMP +CAecApoMFX::RemoveAuxiliaryInput(DWORD dwInputId) +{ + HRESULT hResult = S_OK; + ASSERT_NONREALTIME(); + + IF_TRUE_ACTION_JUMP(m_bIsLocked, hResult = APOERR_APO_LOCKED, Exit); + IF_TRUE_ACTION_JUMP(!m_bIsInitialized, hResult = APOERR_NOT_INITIALIZED, Exit); + + // This APO can only handle 1 auxiliary input + IF_TRUE_ACTION_JUMP(m_auxiliaryInputId != dwInputId, hResult = APOERR_INVALID_INPUTID, Exit); + + m_auxiliaryInputId = 0; + + // Signal to AEC algorithm that there is no longer any reference audio stream + +Exit: + return hResult; +} + +STDMETHODIMP +CAecApoMFX::IsInputFormatSupported(IAudioMediaType* pRequestedInputFormat, + IAudioMediaType** ppSupportedInputFormat) +{ + ASSERT_NONREALTIME(); + HRESULT hResult = S_OK; + + IF_TRUE_ACTION_JUMP((NULL == pRequestedInputFormat) || (NULL == ppSupportedInputFormat), hResult = E_POINTER, Exit); + + BOOL bSupported = FALSE; + hResult = IsInputFormatSupportedForAec(pRequestedInputFormat, &bSupported); + IF_FAILED_JUMP(hResult, Exit); + + if (!bSupported) + { + hResult = CreatePreferredInputMediaType(ppSupportedInputFormat, pRequestedInputFormat->GetAudioFormat()->nChannels); + IF_FAILED_JUMP(hResult, Exit); + return S_FALSE; + } + + pRequestedInputFormat->AddRef(); + *ppSupportedInputFormat = pRequestedInputFormat; + +Exit: + return hResult; +} + +// IAPOAuxiliaryInputRT +STDMETHODIMP_(void) +CAecApoMFX::AcceptInput(DWORD dwInputId, + const APO_CONNECTION_PROPERTY * pInputConnection) +{ + ASSERT_REALTIME(); + ATLASSERT(m_bIsInitialized); + ATLASSERT(m_bIsLocked); + + ATLASSERT(pInputConnection->u32Signature == APO_CONNECTION_PROPERTY_V2_SIGNATURE); + ATLASSERT(inputId == m_auxiliaryInputId); + UNREFERENCED_PARAMETER(dwInputId); + + const APO_CONNECTION_PROPERTY_V2* connectionV2 = reinterpret_cast<const APO_CONNECTION_PROPERTY_V2*>(pInputConnection); + + // Check connectionV2->property.u32BufferFlags to see whether loopback buffer is silent + // Provide loopback buffer and timestamp to AEC algorithm + UNREFERENCED_PARAMETER(connectionV2); +} + +STDMETHODIMP CAecApoMFX::GetApoNotificationRegistrationInfo(_Out_writes_(*count) APO_NOTIFICATION_DESCRIPTOR** apoNotifications, _Out_ DWORD* count) +{ + *apoNotifications = nullptr; + *count = 0; + + /* + RETURN_HR_IF_NULL(E_FAIL, m_spCaptureDevice); + RETURN_HR_IF_NULL(E_FAIL, m_spLoopbackDevice); + + // Let the OS know what notifications we are interested in by returning an array of + // APO_NOTIFICATION_DESCRIPTORs. + constexpr DWORD numDescriptors = 2; + wil::unique_cotaskmem_ptr<APO_NOTIFICATION_DESCRIPTOR[]> apoNotificationDescriptors; + + apoNotificationDescriptors.reset( + static_cast<APO_NOTIFICATION_DESCRIPTOR*>(CoTaskMemAlloc(sizeof(APO_NOTIFICATION_DESCRIPTOR) * numDescriptors))); + RETURN_IF_NULL_ALLOC(apoNotificationDescriptors); + + // Our APO wants to get notified when a endpoint volume changes on the capture endpoint. + apoNotificationDescriptors[0].type = APO_NOTIFICATION_TYPE_ENDPOINT_VOLUME; + (void)m_spCaptureDevice->QueryInterface(&apoNotificationDescriptors[0].audioEndpointVolume.device); + + // Our APO wants to get notified when a endpoint volume changes on the auxiliary input endpoint. + apoNotificationDescriptors[1].type = APO_NOTIFICATION_TYPE_ENDPOINT_VOLUME; + (void)m_spLoopbackDevice->QueryInterface(&apoNotificationDescriptors[1].audioEndpointVolume.device); + + *apoNotifications = apoNotificationDescriptors.release(); + *count = numDescriptors; + */ + + return S_OK; +} + +static bool IsSameEndpointId(IMMDevice* device1, IMMDevice* device2) +{ + bool isSameEndpointId = false; + + wil::unique_cotaskmem_string deviceId1; + if (SUCCEEDED(device1->GetId(&deviceId1))) + { + wil::unique_cotaskmem_string deviceId2; + if (SUCCEEDED(device2->GetId(&deviceId2))) + { + isSameEndpointId = (CompareStringOrdinal(deviceId1.get(), -1, deviceId2.get(), -1, TRUE) == CSTR_EQUAL); + } + } + return isSameEndpointId; +} + +// HandleNotification is called whenever there is a change that matches any of the +// APO_NOTIFICATION_DESCRIPTOR elements in the array that was returned by GetApoNotificationRegistrationInfo. +// Note that the APO will have to query each property once to get its initial value because this method is +// only invoked when any of the properties have changed. +STDMETHODIMP_(void) CAecApoMFX::HandleNotification(_In_ APO_NOTIFICATION* /* apoNotification */) +{ + // Handle endpoint volume change + /* + if (apoNotification->type == APO_NOTIFICATION_TYPE_ENDPOINT_PROPERTY_CHANGE) + { + if (IsSameEndpointId(apoNotification->audioEndpointVolumeChange.endpoint, m_spCaptureDevice)) + { + m_captureEndpointMasterVolume = apoNotification->audioEndpointVolumeChange.volume->fMasterVolume; + } + else if (IsSameEndpointId(apoNotification->audioEndpointVolumeChange.endpoint, m_spLoopbackDevice)) + { + m_loopbackEndpointMasterVolume = apoNotification->audioEndpointVolumeChange.volume->fMasterVolume; + } + } + else if (apoNotification->type == APO_NOTIFICATION_TYPE_ENDPOINT_PROPERTY_CHANGE) + { + + } + else if(apoNotification->type == APO_NOTIFICATION_TYPE_SYSTEM_EFFECTS_PROPERTY_CHANGE) + { + + } + */ +} + diff --git a/audio/sysvad/APO/AecApo/AecApoMfx.rgs b/audio/sysvad/APO/AecApo/AecApoMfx.rgs new file mode 100644 index 00000000..13391e8a --- /dev/null +++ b/audio/sysvad/APO/AecApo/AecApoMfx.rgs @@ -0,0 +1,13 @@ +HKCR +{ + NoRemove CLSID + { + ForceRemove {325B7F6F-ED6C-40CE-814C-00D91FED053F} = s 'AecApoMFX Class' + { + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Both' + } + } + } +} diff --git a/audio/sysvad/APO/AecApo/Resource.h b/audio/sysvad/APO/AecApo/Resource.h new file mode 100644 index 00000000..7f4b89a4 --- /dev/null +++ b/audio/sysvad/APO/AecApo/Resource.h @@ -0,0 +1,19 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by AecApoDll.rc +// +#define IDS_PROJNAME 100 +#define IDR_AECAPODLL 101 +#define IDR_AECAPOMFX 110 +#define IDI_EFFECT_ICON 200 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 201 +#define _APS_NEXT_COMMAND_VALUE 32768 +#define _APS_NEXT_CONTROL_VALUE 201 +#define _APS_NEXT_SYMED_VALUE 132 +#endif +#endif diff --git a/audio/sysvad/APO/DelayAPO/DelayAPODll.cpp b/audio/sysvad/APO/DelayAPO/DelayAPODll.cpp index 9b24637a..968d7f86 100644 --- a/audio/sysvad/APO/DelayAPO/DelayAPODll.cpp +++ b/audio/sysvad/APO/DelayAPO/DelayAPODll.cpp @@ -64,7 +64,7 @@ STDAPI DllCanUnloadNow(void) // {secret} -STDAPI DllGetClassObject(_In_ REFCLSID rclsid,_In_ REFIID riid, _Outptr_ LPVOID* ppv) +STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID FAR* ppv) { return _AtlModule.DllGetClassObject(rclsid, riid, ppv); -}
\ No newline at end of file +} diff --git a/audio/sysvad/APO/DelayAPO/DelayAPOMFX.cpp b/audio/sysvad/APO/DelayAPO/DelayAPOMFX.cpp index 8b53b42c..8778b252 100644 --- a/audio/sysvad/APO/DelayAPO/DelayAPOMFX.cpp +++ b/audio/sysvad/APO/DelayAPO/DelayAPOMFX.cpp @@ -863,9 +863,9 @@ HRESULT CDelayAPOMFX::ValidateAndCacheConnectionInfo(UINT32 u32NumInputConnectio // Set scalars to decrease volume from 1.0 to 1.0/N where N is the number of channels // starting with the first channel. f32InverseChannelCount = 1.0f/m_u32SamplesPerFrame; - for (UINT16 u16Index=0; u16Index<m_u32SamplesPerFrame; u16Index++) + for (UINT32 u32Index=0; u32Index<m_u32SamplesPerFrame; u32Index++) { - m_pf32Coefficients[u16Index] = 1.0f - (FLOAT32)(f32InverseChannelCount)*u16Index; + m_pf32Coefficients[u32Index] = 1.0f - (FLOAT32)(f32InverseChannelCount)*u32Index; } diff --git a/audio/sysvad/APO/KWSApo/KWSApo.h b/audio/sysvad/APO/KWSApo/KWSApo.h index 38c3434e..faa8592e 100644 --- a/audio/sysvad/APO/KWSApo/KWSApo.h +++ b/audio/sysvad/APO/KWSApo/KWSApo.h @@ -16,6 +16,10 @@ #include <commonmacros.h> #include <devicetopology.h> +#include <audioengineextensionapo.h> + +#include <wil\com.h> + _Analysis_mode_(_Analysis_code_type_user_driver_) #pragma AVRT_VTABLES_BEGIN @@ -25,7 +29,8 @@ class CKWSApoEFX : public CComCoClass<CKWSApoEFX, &CLSID_KWSApoEFX>, public CBaseAudioProcessingObject, public IMMNotificationClient, - public IAudioSystemEffects2, + public IAudioSystemEffects3, + public IAudioProcessingObjectNotifications, public IKWSApoEFX { public: @@ -43,6 +48,8 @@ BEGIN_COM_MAP(CKWSApoEFX) COM_INTERFACE_ENTRY(IKWSApoEFX) COM_INTERFACE_ENTRY(IAudioSystemEffects) COM_INTERFACE_ENTRY(IAudioSystemEffects2) + COM_INTERFACE_ENTRY(IAudioSystemEffects3) + COM_INTERFACE_ENTRY(IAudioProcessingObjectNotifications) COM_INTERFACE_ENTRY(IMMNotificationClient) COM_INTERFACE_ENTRY(IAudioProcessingObjectRT) COM_INTERFACE_ENTRY(IAudioProcessingObject) @@ -103,11 +110,32 @@ public: return S_OK; } + // IAudioSystemEffects3 + STDMETHOD(GetControllableSystemEffectsList)(_Outptr_result_buffer_maybenull_(*numEffects) AUDIO_SYSTEMEFFECT** effects, _Out_ UINT* numEffects, _In_opt_ HANDLE event) override; + STDMETHODIMP SetAudioSystemEffectState(GUID, AUDIO_SYSTEMEFFECT_STATE) override {return S_OK;} + + // IAudioProcessingObjectNotifications + STDMETHODIMP GetApoNotificationRegistrationInfo(_Out_writes_(*count) APO_NOTIFICATION_DESCRIPTOR** apoNotifications, _Out_ DWORD* count) override + { + UNREFERENCED_PARAMETER(apoNotifications); + UNREFERENCED_PARAMETER(count); + return S_OK; + } + + STDMETHODIMP_(void) HandleNotification(_In_ APO_NOTIFICATION* apoNotification) override + { + UNREFERENCED_PARAMETER(apoNotification); + } + public: CComPtr<IPropertyStore> m_spAPOSystemEffectsProperties; CComPtr<IMMDeviceEnumerator> m_spEnumerator; static const CRegAPOProperties<1> sm_RegProperties; // registration properties INTERLEAVED_AUDIO_FORMAT_INFORMATION m_FormatInfo; + +private: + BOOL m_bRegisteredEndpointNotificationCallback = FALSE; + wil::com_ptr_nothrow<IAudioProcessingObjectLoggingService> m_apoLoggingService; }; #pragma AVRT_VTABLES_END diff --git a/audio/sysvad/APO/KWSApo/KWSApo.vcxproj b/audio/sysvad/APO/KWSApo/KWSApo.vcxproj index 2000bcf4..b34e03d9 100644 --- a/audio/sysvad/APO/KWSApo/KWSApo.vcxproj +++ b/audio/sysvad/APO/KWSApo/KWSApo.vcxproj @@ -190,7 +190,7 @@ <TreatWarningAsError>true</TreatWarningAsError> <WarningLevel>Level4</WarningLevel> <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> - <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.;..\..\..\..\wil\include</AdditionalIncludeDirectories> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary /> @@ -214,7 +214,7 @@ <TreatWarningAsError>true</TreatWarningAsError> <WarningLevel>Level4</WarningLevel> <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> - <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.;..\..\..\..\wil\include</AdditionalIncludeDirectories> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary> @@ -239,7 +239,7 @@ <TreatWarningAsError>true</TreatWarningAsError> <WarningLevel>Level4</WarningLevel> <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> - <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.;..\..\..\..\wil\include</AdditionalIncludeDirectories> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary /> @@ -263,7 +263,7 @@ <TreatWarningAsError>true</TreatWarningAsError> <WarningLevel>Level4</WarningLevel> <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> - <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.;..\..\..\..\wil\include</AdditionalIncludeDirectories> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary> @@ -288,7 +288,7 @@ <TreatWarningAsError>true</TreatWarningAsError> <WarningLevel>Level4</WarningLevel> <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> - <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.;..\..\..\..\wil\include</AdditionalIncludeDirectories> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary /> @@ -312,7 +312,7 @@ <TreatWarningAsError>true</TreatWarningAsError> <WarningLevel>Level4</WarningLevel> <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> - <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.;..\..\..\..\wil\include</AdditionalIncludeDirectories> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary /> @@ -352,4 +352,4 @@ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> -</Project> +</Project>
\ No newline at end of file diff --git a/audio/sysvad/APO/KWSApo/KWSApoDll.cpp b/audio/sysvad/APO/KWSApo/KWSApoDll.cpp index f8cbc95f..286b7869 100644 --- a/audio/sysvad/APO/KWSApo/KWSApoDll.cpp +++ b/audio/sysvad/APO/KWSApo/KWSApoDll.cpp @@ -65,7 +65,7 @@ STDAPI DllCanUnloadNow(void) // {secret} -STDAPI DllGetClassObject(_In_ REFCLSID rclsid,_In_ REFIID riid, _Outptr_ LPVOID* ppv) +STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID FAR* ppv) { return _AtlModule.DllGetClassObject(rclsid, riid, ppv); } diff --git a/audio/sysvad/APO/KWSApo/KWSApoEfx.cpp b/audio/sysvad/APO/KWSApo/KWSApoEfx.cpp index 5924cd36..77c78364 100644 --- a/audio/sysvad/APO/KWSApo/KWSApoEfx.cpp +++ b/audio/sysvad/APO/KWSApo/KWSApoEfx.cpp @@ -22,6 +22,7 @@ #include "KWSApo.h" #include <devicetopology.h> #include <CustomPropKeys.h> +#include <wil\result.h> // Static declaration of the APO_REG_PROPERTIES structure // associated with this APO. The number in <> brackets is the @@ -277,18 +278,45 @@ HRESULT CKWSApoEFX::Initialize(UINT32 cbDataSize, BYTE* pbyData) UINT myPartId; CComPtr<IKsControl> spKsControl; ULONG cbReturned = 0; + UINT nSoftwareIoConnectorIndex; IF_TRUE_ACTION_JUMP( ((NULL == pbyData) && (0 != cbDataSize)), hr = E_INVALIDARG, Exit); IF_TRUE_ACTION_JUMP( ((NULL != pbyData) && (0 == cbDataSize)), hr = E_INVALIDARG, Exit); - if (cbDataSize == sizeof(APOInitSystemEffects2)) + if (cbDataSize == sizeof(APOInitSystemEffects3)) + { + // + // Initialize for mode-specific signal processing + // + APOInitSystemEffects3* papoSysFxInit3 = (APOInitSystemEffects3*)pbyData; + + nSoftwareIoConnectorIndex = papoSysFxInit3->nSoftwareIoConnectorIndex; + + // Windows should pass a valid collection. + ATLASSERT(papoSysFxInit3->pDeviceCollection != nullptr); + IF_TRUE_ACTION_JUMP(papoSysFxInit3->pDeviceCollection == nullptr, hr = E_INVALIDARG, Exit); + + // Get the IDeviceTopology and IConnector interfaces to communicate with this + // APO's counterpart audio driver. This can be used for any proprietary + // communication. + hr = papoSysFxInit3->pDeviceCollection->Item(papoSysFxInit3->nSoftwareIoDeviceInCollection, &spMyDevice); + IF_FAILED_JUMP(hr, Exit); + + // Try to get the logging service, but ignore errors as failure to do logging it is not fatal. + if(SUCCEEDED(papoSysFxInit3->pServiceProvider->QueryService(SID_AudioProcessingObjectLoggingService, IID_PPV_ARGS(&m_apoLoggingService)))) + { + m_apoLoggingService->ApoLog(APO_LOG_LEVEL_INFO, L"CKWSApoEFX::Initialize called with APOInitSystemEffects3."); + } + } + else if (cbDataSize == sizeof(APOInitSystemEffects2)) { // // Initialize for mode-specific signal processing // APOInitSystemEffects2* papoSysFxInit2 = (APOInitSystemEffects2*)pbyData; - KSP_PIN ksPinProperty; + nSoftwareIoConnectorIndex = papoSysFxInit2->nSoftwareIoConnectorIndex; + // Save reference to the effects property store. This saves effects settings // and is the communication medium between this APO and any associated UI. m_spAPOSystemEffectsProperties = papoSysFxInit2->pAPOSystemEffectsProperties; @@ -303,32 +331,6 @@ HRESULT CKWSApoEFX::Initialize(UINT32 cbDataSize, BYTE* pbyData) hr = papoSysFxInit2->pDeviceCollection->Item(papoSysFxInit2->nSoftwareIoDeviceInCollection, &spMyDevice); IF_FAILED_JUMP(hr, Exit); - hr = spMyDevice->Activate(__uuidof(IKsControl), CLSCTX_ALL, NULL, (void**)&spKsControl); - IF_FAILED_JUMP(hr, Exit); - - hr = spMyDevice->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&spMyDeviceTopology); - IF_FAILED_JUMP(hr, Exit); - - hr = spMyDeviceTopology->GetConnector(papoSysFxInit2->nSoftwareIoConnectorIndex, &spMyConnector); - IF_FAILED_JUMP(hr, Exit); - - spPart = spMyConnector; - - hr = spPart->GetLocalId(&myPartId); - IF_FAILED_JUMP(hr, Exit); - - ::ZeroMemory(&ksPinProperty, sizeof(ksPinProperty)); - ksPinProperty.Property.Set = KSPROPSETID_InterleavedAudio; - ksPinProperty.Property.Id = KSPROPERTY_INTERLEAVEDAUDIO_FORMATINFORMATION; - ksPinProperty.Property.Flags = KSPROPERTY_TYPE_GET; - ksPinProperty.PinId = myPartId & 0x0000ffff; - - ::ZeroMemory(&m_FormatInfo, sizeof(m_FormatInfo)); - - hr = spKsControl->KsProperty(&(ksPinProperty.Property), sizeof(ksPinProperty), &m_FormatInfo, sizeof(m_FormatInfo), &cbReturned); - IF_FAILED_JUMP(hr, Exit); - - IF_TRUE_ACTION_JUMP( m_FormatInfo.Size != sizeof(m_FormatInfo), hr = E_INVALIDARG, Exit); } else { @@ -337,17 +339,53 @@ HRESULT CKWSApoEFX::Initialize(UINT32 cbDataSize, BYTE* pbyData) goto Exit; } - // - // Register for notification of registry updates - // - hr = m_spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)); + hr = spMyDevice->Activate(__uuidof(IKsControl), CLSCTX_ALL, NULL, (void**)&spKsControl); IF_FAILED_JUMP(hr, Exit); - hr = m_spEnumerator->RegisterEndpointNotificationCallback(this); + hr = spMyDevice->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&spMyDeviceTopology); IF_FAILED_JUMP(hr, Exit); - m_bIsInitialized = true; + hr = spMyDeviceTopology->GetConnector(nSoftwareIoConnectorIndex, &spMyConnector); + IF_FAILED_JUMP(hr, Exit); + + spPart = spMyConnector; + + hr = spPart->GetLocalId(&myPartId); + IF_FAILED_JUMP(hr, Exit); + + KSP_PIN ksPinProperty; + ::ZeroMemory(&ksPinProperty, sizeof(ksPinProperty)); + ksPinProperty.Property.Set = KSPROPSETID_InterleavedAudio; + ksPinProperty.Property.Id = KSPROPERTY_INTERLEAVEDAUDIO_FORMATINFORMATION; + ksPinProperty.Property.Flags = KSPROPERTY_TYPE_GET; + ksPinProperty.PinId = myPartId & 0x0000ffff; + + ::ZeroMemory(&m_FormatInfo, sizeof(m_FormatInfo)); + + hr = spKsControl->KsProperty(&(ksPinProperty.Property), sizeof(ksPinProperty), &m_FormatInfo, sizeof(m_FormatInfo), &cbReturned); + IF_FAILED_JUMP(hr, Exit); + + IF_TRUE_ACTION_JUMP(m_FormatInfo.Size != sizeof(m_FormatInfo), hr = E_INVALIDARG, Exit); + + if (cbDataSize == sizeof(APOInitSystemEffects3)) + { + // Register for notification of registry updates in GetApoNotificationRegistrationInfo + } + else if (cbDataSize == sizeof(APOInitSystemEffects2)) + { + // + // Register for notification of registry updates + // + hr = m_spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)); + IF_FAILED_JUMP(hr, Exit); + hr = m_spEnumerator->RegisterEndpointNotificationCallback(this); + IF_FAILED_JUMP(hr, Exit); + + m_bRegisteredEndpointNotificationCallback = TRUE; + } + + m_bIsInitialized = true; Exit: return hr; @@ -369,13 +407,21 @@ Exit: // Remarks: // // -STDMETHODIMP CKWSApoEFX::GetEffectsList(_Outptr_result_buffer_maybenull_(*pcEffects) LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE Event) +STDMETHODIMP CKWSApoEFX::GetEffectsList(_Outptr_result_buffer_maybenull_(*pcEffects) LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE) { - UNREFERENCED_PARAMETER(Event); + RETURN_HR_IF(E_POINTER, ppEffectsIds == nullptr); + RETURN_HR_IF(E_POINTER, pcEffects == nullptr); - *ppEffectsIds = NULL; + *ppEffectsIds = nullptr; *pcEffects = 0; - + + // Return the list of effects implemented by this APO + static const GUID effectsList[] = {AUDIO_EFFECT_TYPE_ACOUSTIC_ECHO_CANCELLATION}; + *ppEffectsIds = (LPGUID)CoTaskMemAlloc(sizeof(effectsList)); + RETURN_IF_NULL_ALLOC(*ppEffectsIds); + *pcEffects = ARRAYSIZE(effectsList); + CopyMemory(*ppEffectsIds, effectsList, sizeof(effectsList)); + return S_OK; } @@ -592,8 +638,26 @@ CKWSApoEFX::~CKWSApoEFX(void) // // unregister for callbacks // - if (m_bIsInitialized) + if (m_bRegisteredEndpointNotificationCallback) { m_spEnumerator->UnregisterEndpointNotificationCallback(this); } } // ~CKWSApoEFX + +STDMETHODIMP CKWSApoEFX::GetControllableSystemEffectsList(_Outptr_result_buffer_maybenull_(*numEffects) AUDIO_SYSTEMEFFECT** effects, _Out_ UINT* numEffects, _In_opt_ HANDLE) +{ + RETURN_HR_IF(E_POINTER, effects == nullptr); + RETURN_HR_IF(E_POINTER, numEffects == nullptr); + + *effects = nullptr; + *numEffects = 0; + + // Return the list of effects implemented by this APO + static const AUDIO_SYSTEMEFFECT effectsList[] = {{AUDIO_EFFECT_TYPE_ACOUSTIC_ECHO_CANCELLATION, FALSE, AUDIO_SYSTEMEFFECT_STATE_ON}}; + *effects = (AUDIO_SYSTEMEFFECT*)CoTaskMemAlloc(sizeof(effectsList)); + RETURN_IF_NULL_ALLOC(*effects); + *numEffects = ARRAYSIZE(effectsList); + CopyMemory(*effects, effectsList, sizeof(effectsList)); + + return S_OK; +}
\ No newline at end of file diff --git a/audio/sysvad/APO/SwapAPO/SwapAPO.h b/audio/sysvad/APO/SwapAPO/SwapAPO.h index 83702e6f..aeb9d259 100644 --- a/audio/sysvad/APO/SwapAPO/SwapAPO.h +++ b/audio/sysvad/APO/SwapAPO/SwapAPO.h @@ -9,16 +9,23 @@ #pragma once #include <audioenginebaseapo.h> +#include <audioengineextensionapo.h> #include <BaseAudioProcessingObject.h> #include <SwapAPOInterface.h> #include <SwapAPODll.h> #include <commonmacros.h> #include <devicetopology.h> +#include <rtworkq.h> + +#include <wil\com.h> _Analysis_mode_(_Analysis_code_type_user_driver_) #define PK_EQUAL(x, y) ((x.fmtid == y.fmtid) && (x.pid == y.pid)) +#define GUID_FORMAT_STRING "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}" +#define GUID_FORMAT_ARGS(guidVal) (guidVal).Data1, (guidVal).Data2, (guidVal).Data3, (guidVal).Data4[0], (guidVal).Data4[1], (guidVal).Data4[2], (guidVal).Data4[3], (guidVal).Data4[4], (guidVal).Data4[5], (guidVal).Data4[6], (guidVal).Data4[7] +#define NUM_OF_EFFECTS 1 // // Define a GUID identifying the type of this APO's custom effect. @@ -30,8 +37,43 @@ _Analysis_mode_(_Analysis_code_type_user_driver_) // {B8EC75BA-00ED-434C-A732-064A0F00788E} DEFINE_GUID(SwapEffectId, 0xb8ec75ba, 0x00ed, 0x434c, 0xa7, 0x32, 0x06, 0x4a, 0x0f, 0x00, 0x78, 0x8e); +// {5DB5B4C8-6C37-450E-93F5-1E275AFDF87F} +DEFINE_GUID(SWAP_APO_MFX_CONTEXT, 0x5db5b4c8, 0x6c37, 0x450e, 0x93, 0xf5, 0x1e, 0x27, 0x5a, 0xfd, 0xf8, 0x7f); + +// {99817AE5-E6DC-4074-B513-8A872178DA12} +DEFINE_GUID(SWAP_APO_SFX_CONTEXT, 0x99817ae5, 0xe6dc, 0x4074, 0xb5, 0x13, 0x8a, 0x87, 0x21, 0x78, 0xda, 0x12); + LONG GetCurrentEffectsSetting(IPropertyStore* properties, PROPERTYKEY pkeyEnable, GUID processingMode); +class SwapMFXApoAsyncCallback : + public IRtwqAsyncCallback +{ +private: + DWORD m_queueId; + volatile ULONG _refCount = 1; + +public: + SwapMFXApoAsyncCallback(DWORD queueId) : m_queueId(queueId) + { + } + + static HRESULT Create(_Outptr_ SwapMFXApoAsyncCallback** workItemOut, DWORD queueId); + + // IRtwqAsyncCallback + STDMETHOD(GetParameters)(_Out_opt_ DWORD* pdwFlags, _Out_opt_ DWORD* pdwQueue) + { + *pdwFlags = 0; + *pdwQueue = m_queueId; + return S_OK; + } + STDMETHOD(Invoke)(_In_ IRtwqAsyncResult* asyncResult); + + // IUnknown (needed for IRtwqAsyncCallback) + STDMETHOD(QueryInterface)(REFIID riid, __deref_out void** interfaceOut); + STDMETHOD_(ULONG, AddRef)(); + STDMETHOD_(ULONG, Release)(); +}; + #pragma AVRT_VTABLES_BEGIN // Swap APO class - MFX class CSwapAPOMFX : @@ -39,7 +81,8 @@ class CSwapAPOMFX : public CComCoClass<CSwapAPOMFX, &CLSID_SwapAPOMFX>, public CBaseAudioProcessingObject, public IMMNotificationClient, - public IAudioSystemEffects2, + public IAudioProcessingObjectNotifications, + public IAudioSystemEffects3, // IAudioSystemEffectsCustomFormats may be optionally supported // by APOs that attach directly to the connector in the DEFAULT mode streaming graph public IAudioSystemEffectsCustomFormats, @@ -64,10 +107,12 @@ BEGIN_COM_MAP(CSwapAPOMFX) COM_INTERFACE_ENTRY(ISwapAPOMFX) COM_INTERFACE_ENTRY(IAudioSystemEffects) COM_INTERFACE_ENTRY(IAudioSystemEffects2) + COM_INTERFACE_ENTRY(IAudioSystemEffects3) // IAudioSystemEffectsCustomFormats may be optionally supported // by APOs that attach directly to the connector in the DEFAULT mode streaming graph COM_INTERFACE_ENTRY(IAudioSystemEffectsCustomFormats) COM_INTERFACE_ENTRY(IMMNotificationClient) + COM_INTERFACE_ENTRY(IAudioProcessingObjectNotifications) COM_INTERFACE_ENTRY(IAudioProcessingObjectRT) COM_INTERFACE_ENTRY(IAudioProcessingObject) COM_INTERFACE_ENTRY(IAudioProcessingObjectConfiguration) @@ -91,6 +136,11 @@ public: // IAudioSystemEffects2 STDMETHOD(GetEffectsList)(_Outptr_result_buffer_maybenull_(*pcEffects) LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE Event); + // IAudioSystemEffects3 + STDMETHOD(GetControllableSystemEffectsList)(_Outptr_result_buffer_maybenull_(*numEffects) AUDIO_SYSTEMEFFECT** effects, _Out_ UINT* numEffects, _In_opt_ HANDLE event); + + STDMETHOD(SetAudioSystemEffectState)(GUID effectId, AUDIO_SYSTEMEFFECT_STATE state); + virtual HRESULT ValidateAndCacheConnectionInfo( UINT32 u32NumInputConnections, APO_CONNECTION_DESCRIPTOR** ppInputConnections, @@ -123,6 +173,10 @@ public: } STDMETHODIMP OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key); + // IAudioProcessingObjectNotifications + STDMETHODIMP GetApoNotificationRegistrationInfo(_Out_writes_(*count) APO_NOTIFICATION_DESCRIPTOR** apoNotifications, _Out_ DWORD* count); + STDMETHODIMP_(void) HandleNotification(_In_ APO_NOTIFICATION* apoNotification); + // IAudioSystemEffectsCustomFormats // This interface may be optionally supported by APOs that attach directly to the connector in the DEFAULT mode streaming graph STDMETHODIMP GetFormatCount(UINT* pcFormats); @@ -133,13 +187,19 @@ public: STDMETHODIMP IsOutputFormatSupported(IAudioMediaType *pOppositeFormat, IAudioMediaType *pRequestedOutputFormat, IAudioMediaType **ppSupportedOutputFormat); STDMETHODIMP CheckCustomFormats(IAudioMediaType *pRequestedFormat); + + STDMETHODIMP DoWorkOnRealTimeThread(); + + void HandleWorkItemCompleted(_In_ IRtwqAsyncResult* asyncResult); public: LONG m_fEnableSwapMFX; GUID m_AudioProcessingMode; + wil::com_ptr_nothrow<IMMDevice> m_device; CComPtr<IPropertyStore> m_spAPOSystemEffectsProperties; CComPtr<IMMDeviceEnumerator> m_spEnumerator; static const CRegAPOProperties<1> sm_RegProperties; // registration properties + AUDIO_SYSTEMEFFECT m_effectInfos[NUM_OF_EFFECTS]; // Locked memory FLOAT32 *m_pf32Coefficients; @@ -147,8 +207,15 @@ public: private: CCriticalSection m_EffectsLock; HANDLE m_hEffectsChangedEvent; + BOOL m_bRegisteredEndpointNotificationCallback = FALSE; + + wil::com_ptr_nothrow<IPropertyStore> m_userStore; + wil::com_ptr_nothrow<IAudioProcessingObjectLoggingService> m_apoLoggingService; + + DWORD m_queueId = 0; + wil::com_ptr_nothrow<SwapMFXApoAsyncCallback> m_asyncCallback; - HRESULT ProprietaryCommunicationWithDriver(APOInitSystemEffects2 *_pAPOSysFxInit2); + HRESULT ProprietaryCommunicationWithDriver(IMMDeviceCollection *pDeviceCollection, UINT nSoftwareIoDeviceInCollection, UINT nSoftwareIoConnectorIndex); }; #pragma AVRT_VTABLES_END @@ -161,7 +228,8 @@ class CSwapAPOSFX : public CComCoClass<CSwapAPOSFX, &CLSID_SwapAPOSFX>, public CBaseAudioProcessingObject, public IMMNotificationClient, - public IAudioSystemEffects2, + public IAudioProcessingObjectNotifications, + public IAudioSystemEffects3, public ISwapAPOSFX { public: @@ -183,7 +251,9 @@ BEGIN_COM_MAP(CSwapAPOSFX) COM_INTERFACE_ENTRY(ISwapAPOSFX) COM_INTERFACE_ENTRY(IAudioSystemEffects) COM_INTERFACE_ENTRY(IAudioSystemEffects2) + COM_INTERFACE_ENTRY(IAudioSystemEffects3) COM_INTERFACE_ENTRY(IMMNotificationClient) + COM_INTERFACE_ENTRY(IAudioProcessingObjectNotifications) COM_INTERFACE_ENTRY(IAudioProcessingObjectRT) COM_INTERFACE_ENTRY(IAudioProcessingObject) COM_INTERFACE_ENTRY(IAudioProcessingObjectConfiguration) @@ -207,6 +277,10 @@ public: // IAudioSystemEffects2 STDMETHOD(GetEffectsList)(_Outptr_result_buffer_maybenull_(*pcEffects) LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE Event); + // IAudioSystemEffects3 + STDMETHOD(GetControllableSystemEffectsList)(_Outptr_result_buffer_maybenull_(*numEffects) AUDIO_SYSTEMEFFECT** effects, _Out_ UINT* numEffects, _In_opt_ HANDLE event); + + STDMETHOD(SetAudioSystemEffectState)(GUID effectId, AUDIO_SYSTEMEFFECT_STATE state); // IMMNotificationClient STDMETHODIMP OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) { @@ -232,17 +306,28 @@ public: return S_OK; } STDMETHODIMP OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key); + + // IAudioProcessingObjectNotifications + STDMETHODIMP GetApoNotificationRegistrationInfo(_Out_writes_(*count) APO_NOTIFICATION_DESCRIPTOR** apoNotifications, _Out_ DWORD* count); + STDMETHODIMP_(void) HandleNotification(_In_ APO_NOTIFICATION* apoNotification); public: LONG m_fEnableSwapSFX; LONG m_fEnableDelaySFX; GUID m_AudioProcessingMode; + wil::com_ptr_nothrow<IMMDevice> m_device; CComPtr<IPropertyStore> m_spAPOSystemEffectsProperties; CComPtr<IMMDeviceEnumerator> m_spEnumerator; static const CRegAPOProperties<1> sm_RegProperties; // registration properties + AUDIO_SYSTEMEFFECT m_effectInfos[NUM_OF_EFFECTS]; CCriticalSection m_EffectsLock; HANDLE m_hEffectsChangedEvent; + +private: + wil::com_ptr_nothrow<IPropertyStore> m_userStore; + wil::com_ptr_nothrow<IAudioProcessingObjectLoggingService> m_apoLoggingService; + BOOL m_bRegisteredEndpointNotificationCallback = FALSE; }; #pragma AVRT_VTABLES_END diff --git a/audio/sysvad/APO/SwapAPO/SwapAPO.vcxproj b/audio/sysvad/APO/SwapAPO/SwapAPO.vcxproj index 4e680972..2a26307e 100644 --- a/audio/sysvad/APO/SwapAPO/SwapAPO.vcxproj +++ b/audio/sysvad/APO/SwapAPO/SwapAPO.vcxproj @@ -214,7 +214,7 @@ <TreatWarningAsError>true</TreatWarningAsError> <WarningLevel>Level4</WarningLevel> <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> - <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.;..\..\..\..\wil\include</AdditionalIncludeDirectories> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary /> @@ -228,7 +228,7 @@ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> </ResourceCompile> <Link> - <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib</AdditionalDependencies> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib;rtworkq.lib</AdditionalDependencies> <ModuleDefinitionFile>SwapAPODll.def</ModuleDefinitionFile> <AdditionalOptions>/ignore:4217,4049 %(AdditionalOptions)</AdditionalOptions> </Link> @@ -238,7 +238,7 @@ <TreatWarningAsError>true</TreatWarningAsError> <WarningLevel>Level4</WarningLevel> <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> - <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.;..\..\..\..\wil\include</AdditionalIncludeDirectories> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary> @@ -253,7 +253,7 @@ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> </ResourceCompile> <Link> - <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib</AdditionalDependencies> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib;rtworkq.lib</AdditionalDependencies> <ModuleDefinitionFile>SwapAPODll.def</ModuleDefinitionFile> <AdditionalOptions>/ignore:4217,4049 %(AdditionalOptions)</AdditionalOptions> </Link> @@ -263,7 +263,7 @@ <TreatWarningAsError>true</TreatWarningAsError> <WarningLevel>Level4</WarningLevel> <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> - <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.;..\..\..\..\wil\include</AdditionalIncludeDirectories> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary /> @@ -277,7 +277,7 @@ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> </ResourceCompile> <Link> - <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib</AdditionalDependencies> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib;rtworkq.lib</AdditionalDependencies> <ModuleDefinitionFile>SwapAPODll.def</ModuleDefinitionFile> <AdditionalOptions>/ignore:4217,4049 %(AdditionalOptions)</AdditionalOptions> </Link> @@ -287,7 +287,7 @@ <TreatWarningAsError>true</TreatWarningAsError> <WarningLevel>Level4</WarningLevel> <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> - <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.;..\..\..\..\wil\include</AdditionalIncludeDirectories> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary> @@ -302,7 +302,7 @@ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> </ResourceCompile> <Link> - <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib</AdditionalDependencies> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib;rtworkq.lib</AdditionalDependencies> <ModuleDefinitionFile>SwapAPODll.def</ModuleDefinitionFile> <AdditionalOptions>/ignore:4217,4049 %(AdditionalOptions)</AdditionalOptions> </Link> @@ -312,7 +312,7 @@ <TreatWarningAsError>true</TreatWarningAsError> <WarningLevel>Level4</WarningLevel> <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> - <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.;..\..\..\..\wil\include</AdditionalIncludeDirectories> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary /> @@ -326,7 +326,7 @@ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> </ResourceCompile> <Link> - <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib</AdditionalDependencies> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib;rtworkq.lib</AdditionalDependencies> <ModuleDefinitionFile>SwapAPODll.def</ModuleDefinitionFile> <AdditionalOptions>/ignore:4217,4049 %(AdditionalOptions)</AdditionalOptions> </Link> @@ -351,7 +351,7 @@ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> </ResourceCompile> <Link> - <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib</AdditionalDependencies> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib;rtworkq.lib</AdditionalDependencies> <ModuleDefinitionFile>SwapAPODll.def</ModuleDefinitionFile> <AdditionalOptions>/ignore:4217,4049 %(AdditionalOptions)</AdditionalOptions> </Link> @@ -361,7 +361,7 @@ <TreatWarningAsError>true</TreatWarningAsError> <WarningLevel>Level4</WarningLevel> <PreprocessorDefinitions>%(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE</PreprocessorDefinitions> - <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\;.;..\..\..\..\wil\include</AdditionalIncludeDirectories> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary /> @@ -375,7 +375,7 @@ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> </ResourceCompile> <Link> - <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib</AdditionalDependencies> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib;rtworkq.lib</AdditionalDependencies> <ModuleDefinitionFile>SwapAPODll.def</ModuleDefinitionFile> <AdditionalOptions>/ignore:4217,4049 %(AdditionalOptions)</AdditionalOptions> </Link> @@ -400,7 +400,7 @@ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\</AdditionalIncludeDirectories> </ResourceCompile> <Link> - <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib</AdditionalDependencies> + <AdditionalDependencies>%(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib;rtworkq.lib</AdditionalDependencies> <ModuleDefinitionFile>SwapAPODll.def</ModuleDefinitionFile> <AdditionalOptions>/ignore:4217,4049 %(AdditionalOptions)</AdditionalOptions> </Link> @@ -427,4 +427,4 @@ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> -</Project> +</Project>
\ No newline at end of file diff --git a/audio/sysvad/APO/SwapAPO/SwapAPOInterface.idl b/audio/sysvad/APO/SwapAPO/SwapAPOInterface.idl index e83f16fb..a06b5f0d 100644 --- a/audio/sysvad/APO/SwapAPO/SwapAPOInterface.idl +++ b/audio/sysvad/APO/SwapAPO/SwapAPOInterface.idl @@ -28,4 +28,3 @@ interface ISwapAPOMFX : IUnknown interface ISwapAPOSFX : IUnknown { }; - diff --git a/audio/sysvad/APO/SwapAPO/swapapomfx.cpp b/audio/sysvad/APO/SwapAPO/swapapomfx.cpp index a6c0d111..7a772a07 100644 --- a/audio/sysvad/APO/SwapAPO/swapapomfx.cpp +++ b/audio/sysvad/APO/SwapAPO/swapapomfx.cpp @@ -14,6 +14,7 @@ #include <initguid.h> #include <audioenginebaseapo.h> +#include <audioengineextensionapo.h> #include <baseaudioprocessingobject.h> #include <resource.h> @@ -21,6 +22,7 @@ #include "SwapAPO.h" #include "SysVadShared.h" #include <CustomPropKeys.h> +#include <propvarutil.h> @@ -101,6 +103,95 @@ LONG GetCurrentEffectsSetting(IPropertyStore* properties, PROPERTYKEY pkeyEnable return (LONG)enabled; } +HRESULT SwapMFXApoAsyncCallback::Create( + _Outptr_ SwapMFXApoAsyncCallback** workItemOut, + DWORD queueId) +{ + HRESULT hr = S_OK; + SwapMFXApoAsyncCallback* workItem = new SwapMFXApoAsyncCallback(queueId); + + if (workItem != nullptr) + { + *workItemOut = workItem; + } + else + { + hr = E_OUTOFMEMORY; + } + return hr; +} + +STDMETHODIMP SwapMFXApoAsyncCallback::Invoke(_In_ IRtwqAsyncResult* asyncResult) +{ + // We are now executing on the real-time thread. Invoke the APO and let it execute the work. + HRESULT hr = S_OK; + wil::com_ptr_nothrow<IUnknown> objectUnknown; + hr = asyncResult->GetObject(objectUnknown.put_unknown()); + + if (hr == S_OK) + { + wil::com_ptr_nothrow<CSwapAPOMFX> swapMFXAPO = static_cast<CSwapAPOMFX*>(static_cast<IAudioProcessingObject*>(objectUnknown.get())); + hr = swapMFXAPO->DoWorkOnRealTimeThread(); + asyncResult->SetStatus(hr); + + swapMFXAPO->HandleWorkItemCompleted(asyncResult); + } + + return hr; +} + + +//-------------------------------------------------------------------------------- +// IUnknown::QueryInterface +//-------------------------------------------------------------------------------- +STDMETHODIMP SwapMFXApoAsyncCallback::QueryInterface( + REFIID riid, + void** interfaceOut + ) +{ + ATLASSERT(interfaceOut != nullptr); + + if (riid == __uuidof(IRtwqAsyncCallback)) + { + *interfaceOut = static_cast<IRtwqAsyncCallback*>(this); + AddRef(); + } + else if (riid == __uuidof(IUnknown)) + { + *interfaceOut = static_cast<IUnknown*>(this); + AddRef(); + } + else + { + *interfaceOut = nullptr; + return E_NOINTERFACE; + } + + return S_OK; +} + +//-------------------------------------------------------------------------------- +// IUnknown::AddRef +//-------------------------------------------------------------------------------- +STDMETHODIMP_(ULONG) SwapMFXApoAsyncCallback::AddRef() +{ + return InterlockedIncrement(&_refCount); +} + +//-------------------------------------------------------------------------------- +// IUnknown::Release +//-------------------------------------------------------------------------------- +STDMETHODIMP_(ULONG) SwapMFXApoAsyncCallback::Release() +{ + LONG refCount = InterlockedDecrement(&_refCount); + if (refCount == 0) + { + delete this; + } + return refCount; +} + + #pragma AVRT_CODE_BEGIN //------------------------------------------------------------------------- // Description: @@ -259,7 +350,20 @@ STDMETHODIMP CSwapAPOMFX::LockForProcess(UINT32 u32NumInputConnections, { ASSERT_NONREALTIME(); HRESULT hr = S_OK; - + + if (m_queueId != 0) + { + hr = SwapMFXApoAsyncCallback::Create(&m_asyncCallback, m_queueId); + IF_FAILED_JUMP(hr, Exit); + + wil::com_ptr_nothrow<IRtwqAsyncResult> asyncResult; + hr = RtwqCreateAsyncResult(static_cast<IAudioProcessingObject*>(this), m_asyncCallback.get(), nullptr, &asyncResult); + IF_FAILED_JUMP(hr, Exit); + + hr = RtwqPutWorkItem(m_queueId, 0, asyncResult.get()); + IF_FAILED_JUMP(hr, Exit); + } + hr = CBaseAudioProcessingObject::LockForProcess(u32NumInputConnections, ppInputConnections, u32NumOutputConnections, ppOutputConnections); IF_FAILED_JUMP(hr, Exit); @@ -338,7 +442,33 @@ HRESULT CSwapAPOMFX::Initialize(UINT32 cbDataSize, BYTE* pbyData) IF_TRUE_ACTION_JUMP( ((NULL == pbyData) && (0 != cbDataSize)), hr = E_INVALIDARG, Exit); IF_TRUE_ACTION_JUMP( ((NULL != pbyData) && (0 == cbDataSize)), hr = E_INVALIDARG, Exit); - if (cbDataSize == sizeof(APOInitSystemEffects2)) + if (cbDataSize == sizeof(APOInitSystemEffects3)) + { + APOInitSystemEffects3* papoSysFxInit3 = (APOInitSystemEffects3*)pbyData; + + // Try to get the logging service, but ignore errors as failure to do logging it is not fatal. + hr = papoSysFxInit3->pServiceProvider->QueryService(SID_AudioProcessingObjectLoggingService, IID_PPV_ARGS(&m_apoLoggingService)); + IF_FAILED_JUMP(hr, Exit); + + wil::com_ptr_nothrow<IAudioProcessingObjectRTQueueService> apoRtQueueService; + hr = papoSysFxInit3->pServiceProvider->QueryService(SID_AudioProcessingObjectRTQueue, IID_PPV_ARGS(&apoRtQueueService)); + IF_FAILED_JUMP(hr, Exit); + + // Call the GetRealTimeWorkQueue to get the ID of a work queue that can be used for scheduling tasks + // that need to run at a real-time priority. The work queue ID is used with the Rtwq APIs. + hr = apoRtQueueService->GetRealTimeWorkQueue(&m_queueId); + IF_FAILED_JUMP(hr, Exit); + + // Windows should pass a valid collection. + ATLASSERT(papoSysFxInit3->pDeviceCollection != nullptr); + IF_TRUE_ACTION_JUMP(papoSysFxInit3->pDeviceCollection == nullptr, hr = E_INVALIDARG, Exit); + + // Save the processing mode being initialized. + processingMode = papoSysFxInit3->AudioProcessingMode; + + ProprietaryCommunicationWithDriver(papoSysFxInit3->pDeviceCollection, papoSysFxInit3->nSoftwareIoDeviceInCollection, papoSysFxInit3->nSoftwareIoConnectorIndex); + } + else if (cbDataSize == sizeof(APOInitSystemEffects2)) { // // Initialize for mode-specific signal processing @@ -362,7 +492,7 @@ HRESULT CSwapAPOMFX::Initialize(UINT32 cbDataSize, BYTE* pbyData) // an driver (either host CPU based or offload DSP based), the example below uses a combination of // IDeviceTopology, IConnector, and IKsControl interfaces to communicate with the underlying audio driver. // the following following routine demonstrates how to implement how to communicate to an audio driver from a APO. - ProprietaryCommunicationWithDriver(papoSysFxInit2); + ProprietaryCommunicationWithDriver(papoSysFxInit2->pDeviceCollection, papoSysFxInit2->nSoftwareIoDeviceInCollection, papoSysFxInit2->nSoftwareIoConnectorIndex); } else if (cbDataSize == sizeof(APOInitSystemEffects)) { @@ -408,19 +538,32 @@ HRESULT CSwapAPOMFX::Initialize(UINT32 cbDataSize, BYTE* pbyData) // // Get current effects settings // + if (m_userStore != nullptr) + { + m_fEnableSwapMFX = GetCurrentEffectsSetting(m_userStore.get(), PKEY_Endpoint_Enable_Channel_Swap_MFX, m_AudioProcessingMode); + } + if (m_spAPOSystemEffectsProperties != NULL) { m_fEnableSwapMFX = GetCurrentEffectsSetting(m_spAPOSystemEffectsProperties, PKEY_Endpoint_Enable_Channel_Swap_MFX, m_AudioProcessingMode); } - // - // Register for notification of registry updates - // - hr = m_spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)); - IF_FAILED_JUMP(hr, Exit); + RtlZeroMemory(m_effectInfos, sizeof(m_effectInfos)); + m_effectInfos[0] = { SwapEffectId, FALSE, m_fEnableSwapMFX ? AUDIO_SYSTEMEFFECT_STATE_ON : AUDIO_SYSTEMEFFECT_STATE_OFF }; - hr = m_spEnumerator->RegisterEndpointNotificationCallback(this); - IF_FAILED_JUMP(hr, Exit); + if (cbDataSize != sizeof(APOInitSystemEffects3)) + { + // + // Register for notification of registry updates + // + hr = m_spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)); + IF_FAILED_JUMP(hr, Exit); + + hr = m_spEnumerator->RegisterEndpointNotificationCallback(this); + IF_FAILED_JUMP(hr, Exit); + + m_bRegisteredEndpointNotificationCallback = TRUE; + } m_bIsInitialized = true; Exit: @@ -552,10 +695,140 @@ Exit: return hr; } -HRESULT CSwapAPOMFX::ProprietaryCommunicationWithDriver(APOInitSystemEffects2 *_pAPOSysFxInit2) +HRESULT CSwapAPOMFX::GetControllableSystemEffectsList(_Outptr_result_buffer_maybenull_(*numEffects) AUDIO_SYSTEMEFFECT** effects, _Out_ UINT* numEffects, _In_opt_ HANDLE event) +{ + RETURN_HR_IF_NULL(E_POINTER, effects); + RETURN_HR_IF_NULL(E_POINTER, numEffects); + + *effects = nullptr; + *numEffects = 0; + + // Always close existing effects change event handle + if (m_hEffectsChangedEvent != NULL) + { + CloseHandle(m_hEffectsChangedEvent); + m_hEffectsChangedEvent = NULL; + } + + // If an event handle was specified, save it here (duplicated to control lifetime) + if (event != NULL) + { + if (!DuplicateHandle(GetCurrentProcess(), event, GetCurrentProcess(), &m_hEffectsChangedEvent, EVENT_MODIFY_STATE, FALSE, 0)) + { + RETURN_IF_FAILED(HRESULT_FROM_WIN32(GetLastError())); + } + } + + if (!IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW)) + { + wil::unique_cotaskmem_array_ptr<AUDIO_SYSTEMEFFECT> audioEffects( + static_cast<AUDIO_SYSTEMEFFECT*>(CoTaskMemAlloc(NUM_OF_EFFECTS * sizeof(AUDIO_SYSTEMEFFECT))), NUM_OF_EFFECTS); + RETURN_IF_NULL_ALLOC(audioEffects.get()); + + for (UINT i = 0; i < NUM_OF_EFFECTS; i++) + { + audioEffects[i].id = m_effectInfos[i].id; + audioEffects[i].state = m_effectInfos[i].state; + audioEffects[i].canSetState = m_effectInfos[i].canSetState; + } + + *numEffects = (UINT)audioEffects.size(); + *effects = audioEffects.release(); + } + + return S_OK; +} + +HRESULT CSwapAPOMFX::SetAudioSystemEffectState(GUID effectId, AUDIO_SYSTEMEFFECT_STATE state) +{ + for (auto effectInfo : m_effectInfos) + { + if (effectId == effectInfo.id) + { + AUDIO_SYSTEMEFFECT_STATE oldState = effectInfo.state; + effectInfo.state = state; + + // Synchronize access to the effects list and effects changed event + m_EffectsLock.Enter(); + + // If anything changed and a change event handle exists + if (oldState != effectInfo.state) + { + SetEvent(m_hEffectsChangedEvent); + m_apoLoggingService->ApoLog(APO_LOG_LEVEL_INFO, L"SetAudioSystemEffectState - effect: " GUID_FORMAT_STRING L", state: %i", effectInfo.id, effectInfo.state); + } + + m_EffectsLock.Leave(); + + return S_OK; + } + } + + return E_NOTFOUND; +} + +HRESULT CSwapAPOMFX::GetApoNotificationRegistrationInfo(_Out_writes_(*count) APO_NOTIFICATION_DESCRIPTOR **apoNotifications, _Out_ DWORD *count) +{ + *apoNotifications = nullptr; + *count = 0; + + RETURN_HR_IF_NULL(E_FAIL, m_device); + + // Let the OS know what notifications we are interested in by returning an array of + // APO_NOTIFICATION_DESCRIPTORs. + constexpr DWORD numDescriptors = 1; + wil::unique_cotaskmem_ptr<APO_NOTIFICATION_DESCRIPTOR[]> apoNotificationDescriptors; + + apoNotificationDescriptors.reset(static_cast<APO_NOTIFICATION_DESCRIPTOR*>( + CoTaskMemAlloc(sizeof(APO_NOTIFICATION_DESCRIPTOR) * numDescriptors))); + RETURN_IF_NULL_ALLOC(apoNotificationDescriptors); + + // Our APO wants to get notified when a endpoint property changes on the audio endpoint. + apoNotificationDescriptors[0].type = APO_NOTIFICATION_TYPE_ENDPOINT_PROPERTY_CHANGE; + (void)m_device.query_to(&apoNotificationDescriptors[0].audioEndpointPropertyChange.device); + + *apoNotifications = apoNotificationDescriptors.release(); + *count = numDescriptors; + + return S_OK; +} + +void CSwapAPOMFX::HandleNotification(APO_NOTIFICATION *apoNotification) +{ + if (apoNotification->type == APO_NOTIFICATION_TYPE_ENDPOINT_PROPERTY_CHANGE) + { + // If either the master disable or our APO's enable properties changed... + if (PK_EQUAL(apoNotification->audioEndpointPropertyChange.propertyKey, PKEY_Endpoint_Enable_Channel_Swap_MFX) || + PK_EQUAL(apoNotification->audioEndpointPropertyChange.propertyKey, PKEY_AudioEndpoint_Disable_SysFx)) + { + struct KeyControl + { + PROPERTYKEY key; + LONG* value; + }; + + KeyControl controls[] = { + {PKEY_Endpoint_Enable_Channel_Swap_MFX, &m_fEnableSwapMFX}, + }; + + m_apoLoggingService->ApoLog(APO_LOG_LEVEL_INFO, L"HandleNotification - pkey: " GUID_FORMAT_STRING L" %d", GUID_FORMAT_ARGS(apoNotification->audioEndpointPropertyChange.propertyKey.fmtid), apoNotification->audioEndpointPropertyChange.propertyKey.pid); + + for (int i = 0; i < ARRAYSIZE(controls); i++) + { + LONG fNewValue = true; + + // Get the state of whether channel swap MFX is enabled or not + fNewValue = GetCurrentEffectsSetting(m_userStore.get(), controls[i].key, m_AudioProcessingMode); + + SetAudioSystemEffectState(m_effectInfos[i].id, fNewValue ? AUDIO_SYSTEMEFFECT_STATE_ON : AUDIO_SYSTEMEFFECT_STATE_OFF); + } + } + } +} + +HRESULT CSwapAPOMFX::ProprietaryCommunicationWithDriver(IMMDeviceCollection *pDeviceCollection, UINT nSoftwareIoDeviceInCollection, UINT nSoftwareIoConnectorIndex) { HRESULT hr = S_OK; - CComPtr<IMMDevice> spMyDevice; CComPtr<IDeviceTopology> spMyDeviceTopology; CComPtr<IConnector> spMyConnector; CComPtr<IPart> spMyConnectorPart; @@ -567,18 +840,18 @@ HRESULT CSwapAPOMFX::ProprietaryCommunicationWithDriver(APOInitSystemEffects2 *_ CComHeapPtr<KSMULTIPLE_ITEM> spKsMultipleItem; KSP_PIN ksPin = {0}; - UINT nSoftwareIoDeviceInCollection = 0 ; - UINT nSoftwareIoConnectorIndex = 0 ; - - nSoftwareIoDeviceInCollection = _pAPOSysFxInit2->nSoftwareIoDeviceInCollection; - nSoftwareIoConnectorIndex = _pAPOSysFxInit2->nSoftwareIoConnectorIndex; + if (pDeviceCollection == nullptr) + { + hr = E_POINTER; + IF_FAILED_JUMP(hr, Exit); + } // Get the target IMMDevice - hr = _pAPOSysFxInit2->pDeviceCollection->Item(nSoftwareIoDeviceInCollection, &spMyDevice); + hr = pDeviceCollection->Item(nSoftwareIoDeviceInCollection, &m_device); IF_FAILED_JUMP(hr, Exit); // Instantiate a device topology instance - hr = spMyDevice->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&spMyDeviceTopology); + hr = m_device->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&spMyDeviceTopology); IF_FAILED_JUMP(hr, Exit); // retrieve connect instance @@ -586,7 +859,7 @@ HRESULT CSwapAPOMFX::ProprietaryCommunicationWithDriver(APOInitSystemEffects2 *_ IF_FAILED_JUMP(hr, Exit); // activate IKsControl on the IMMDevice - hr = spMyDevice->Activate(__uuidof(IKsControl), CLSCTX_INPROC_SERVER, NULL, (void**)&spKsControl); + hr = m_device->Activate(__uuidof(IKsControl), CLSCTX_INPROC_SERVER, NULL, (void**)&spKsControl); IF_FAILED_JUMP(hr, Exit); // get KS pin id @@ -730,15 +1003,9 @@ HRESULT CSwapAPOMFX::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERT // CSwapAPOMFX::~CSwapAPOMFX(void) { - if (m_bIsInitialized) + if (m_bRegisteredEndpointNotificationCallback) { - // - // unregister for callbacks - // - if (m_spEnumerator != NULL) - { - m_spEnumerator->UnregisterEndpointNotificationCallback(this); - } + m_spEnumerator->UnregisterEndpointNotificationCallback(this); } if (m_hEffectsChangedEvent != NULL) @@ -826,11 +1093,11 @@ HRESULT CSwapAPOMFX::ValidateAndCacheConnectionInfo(UINT32 u32NumInputConnection // Set scalars to decrease volume from 1.0 to 1.0/N where N is the number of channels // starting with the first channel. f32InverseChannelCount = 1.0f/m_u32SamplesPerFrame; - for (UINT16 u16Index=0; u16Index<m_u32SamplesPerFrame; u16Index++) + for (UINT32 u32Index=0; u32Index<m_u32SamplesPerFrame; u32Index++) { // m_u32SamplesPerFrame will not be modified by any other entity or context #pragma warning(suppress:6386) - m_pf32Coefficients[u16Index] = 1.0f - (FLOAT32)(f32InverseChannelCount)*u16Index; + m_pf32Coefficients[u32Index] = 1.0f - (FLOAT32)(f32InverseChannelCount)*u32Index; } @@ -1130,4 +1397,22 @@ HRESULT CSwapAPOMFX::CheckCustomFormats(IAudioMediaType *pRequestedFormat) } return hResult; +} + +HRESULT CSwapAPOMFX::DoWorkOnRealTimeThread() +{ + // Here is where any parallel processing that needs to be done on a real time thread can be performed. + return S_OK; +} + +void CSwapAPOMFX::HandleWorkItemCompleted(_In_ IRtwqAsyncResult* asyncResult) +{ + // check the status of the result + if (FAILED(asyncResult->GetStatus())) + { + // Handle failure + } + + // Here the app could call RtwqPutWorkItem again with m_queueId if it has more work that needs to + // execute on a real-time thread. }
\ No newline at end of file diff --git a/audio/sysvad/APO/SwapAPO/swapaposfx.cpp b/audio/sysvad/APO/SwapAPO/swapaposfx.cpp index c7bba1e1..8483d8df 100644 --- a/audio/sysvad/APO/SwapAPO/swapaposfx.cpp +++ b/audio/sysvad/APO/SwapAPO/swapaposfx.cpp @@ -21,6 +21,7 @@ #include "SwapAPO.h" #include <devicetopology.h> #include <CustomPropKeys.h> +#include <propvarutil.h> // Static declaration of the APO_REG_PROPERTIES structure @@ -281,7 +282,6 @@ Exit: HRESULT CSwapAPOSFX::Initialize(UINT32 cbDataSize, BYTE* pbyData) { HRESULT hr = S_OK; - CComPtr<IMMDevice> spMyDevice; CComPtr<IDeviceTopology> spMyDeviceTopology; CComPtr<IConnector> spMyConnector; GUID processingMode; @@ -289,7 +289,65 @@ HRESULT CSwapAPOSFX::Initialize(UINT32 cbDataSize, BYTE* pbyData) IF_TRUE_ACTION_JUMP( ((NULL == pbyData) && (0 != cbDataSize)), hr = E_INVALIDARG, Exit); IF_TRUE_ACTION_JUMP( ((NULL != pbyData) && (0 == cbDataSize)), hr = E_INVALIDARG, Exit); - if (cbDataSize == sizeof(APOInitSystemEffects2)) + if (cbDataSize == sizeof(APOInitSystemEffects3)) + { + APOInitSystemEffects3* papoSysFxInit3 = (APOInitSystemEffects3*)pbyData; + + // Try to get the logging service, but ignore errors as failure to do logging it is not fatal. + hr = papoSysFxInit3->pServiceProvider->QueryService(SID_AudioProcessingObjectLoggingService, IID_PPV_ARGS(&m_apoLoggingService)); + IF_FAILED_JUMP(hr, Exit); + + // SampleApo supports the new IAudioSystemEffects3 interface so it will receive APOInitSystemEffects3 + // in pbyData if the audio driver has declared support for this. + + // Use IMMDevice to activate IAudioSystemEffectsPropertyStore that contains the default, user and + // volatile settings. + IMMDeviceCollection* deviceCollection = reinterpret_cast<APOInitSystemEffects3*>(pbyData)->pDeviceCollection; + if (deviceCollection != nullptr) + { + UINT32 numDevices; + wil::com_ptr_nothrow<IMMDevice> endpoint; + + // Get the endpoint on which this APO has been created + // (It is the last device in the device collection) + if (SUCCEEDED(deviceCollection->GetCount(&numDevices)) && numDevices > 0 && + SUCCEEDED(deviceCollection->Item(numDevices - 1, &endpoint))) + { + wil::unique_prop_variant activationParam; + hr = InitPropVariantFromCLSID(SWAP_APO_SFX_CONTEXT, &activationParam); + IF_FAILED_JUMP(hr, Exit); + + wil::com_ptr_nothrow<IAudioSystemEffectsPropertyStore> effectsPropertyStore; + hr = endpoint->Activate(__uuidof(effectsPropertyStore), CLSCTX_ALL, &activationParam, effectsPropertyStore.put_void()); + IF_FAILED_JUMP(hr, Exit); + + // This is where an APO might want to open the volatile or default property stores as well + // Use STGM_READWRITE if IPropertyStore::SetValue is needed. + hr = effectsPropertyStore->OpenUserPropertyStore(STGM_READ, m_userStore.put()); + IF_FAILED_JUMP(hr, Exit); + } + } + + // Windows should pass a valid collection. + ATLASSERT(papoSysFxInit2->pDeviceCollection != nullptr); + IF_TRUE_ACTION_JUMP(papoSysFxInit3->pDeviceCollection == nullptr, hr = E_INVALIDARG, Exit); + + // Get the IDeviceTopology and IConnector interfaces to communicate with this + // APO's counterpart audio driver. This can be used for any proprietary + // communication. + hr = papoSysFxInit3->pDeviceCollection->Item(papoSysFxInit3->nSoftwareIoDeviceInCollection, &m_device); + IF_FAILED_JUMP(hr, Exit); + + hr = m_device->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&spMyDeviceTopology); + IF_FAILED_JUMP(hr, Exit); + + hr = spMyDeviceTopology->GetConnector(papoSysFxInit3->nSoftwareIoConnectorIndex, &spMyConnector); + IF_FAILED_JUMP(hr, Exit); + + // Save the processing mode being initialized. + processingMode = papoSysFxInit3->AudioProcessingMode; + } + else if (cbDataSize == sizeof(APOInitSystemEffects2)) { // // Initialize for mode-specific signal processing @@ -307,10 +365,10 @@ HRESULT CSwapAPOSFX::Initialize(UINT32 cbDataSize, BYTE* pbyData) // Get the IDeviceTopology and IConnector interfaces to communicate with this // APO's counterpart audio driver. This can be used for any proprietary // communication. - hr = papoSysFxInit2->pDeviceCollection->Item(papoSysFxInit2->nSoftwareIoDeviceInCollection, &spMyDevice); + hr = papoSysFxInit2->pDeviceCollection->Item(papoSysFxInit2->nSoftwareIoDeviceInCollection, &m_device); IF_FAILED_JUMP(hr, Exit); - hr = spMyDevice->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&spMyDeviceTopology); + hr = m_device->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&spMyDeviceTopology); IF_FAILED_JUMP(hr, Exit); hr = spMyDeviceTopology->GetConnector(papoSysFxInit2->nSoftwareIoConnectorIndex, &spMyConnector); @@ -364,22 +422,34 @@ HRESULT CSwapAPOSFX::Initialize(UINT32 cbDataSize, BYTE* pbyData) // // Get the current values // + if (m_userStore != nullptr) + { + m_fEnableSwapSFX = GetCurrentEffectsSetting(m_userStore.get(), PKEY_Endpoint_Enable_Channel_Swap_SFX, m_AudioProcessingMode); + } + if (m_spAPOSystemEffectsProperties != NULL) { m_fEnableSwapSFX = GetCurrentEffectsSetting(m_spAPOSystemEffectsProperties, PKEY_Endpoint_Enable_Channel_Swap_SFX, m_AudioProcessingMode); } + + RtlZeroMemory(m_effectInfos, sizeof(m_effectInfos)); + m_effectInfos[0] = { SwapEffectId, TRUE, m_fEnableSwapSFX ? AUDIO_SYSTEMEFFECT_STATE_ON : AUDIO_SYSTEMEFFECT_STATE_OFF }; - // - // Register for notification of registry updates - // - hr = m_spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)); - IF_FAILED_JUMP(hr, Exit); + if (cbDataSize != sizeof(APOInitSystemEffects3)) + { + // + // Register for notification of registry updates + // + hr = m_spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)); + IF_FAILED_JUMP(hr, Exit); - hr = m_spEnumerator->RegisterEndpointNotificationCallback(this); - IF_FAILED_JUMP(hr, Exit); + hr = m_spEnumerator->RegisterEndpointNotificationCallback(this); + IF_FAILED_JUMP(hr, Exit); - m_bIsInitialized = true; + m_bRegisteredEndpointNotificationCallback = TRUE; + } + m_bIsInitialized = true; Exit: return hr; @@ -510,6 +580,78 @@ Exit: return hr; } +HRESULT CSwapAPOSFX::GetControllableSystemEffectsList(_Outptr_result_buffer_maybenull_(*numEffects) AUDIO_SYSTEMEFFECT** effects, _Out_ UINT* numEffects, _In_opt_ HANDLE event) +{ + RETURN_HR_IF_NULL(E_POINTER, effects); + RETURN_HR_IF_NULL(E_POINTER, numEffects); + + *effects = nullptr; + *numEffects = 0; + + // Always close existing effects change event handle + if (m_hEffectsChangedEvent != NULL) + { + CloseHandle(m_hEffectsChangedEvent); + m_hEffectsChangedEvent = NULL; + } + + // If an event handle was specified, save it here (duplicated to control lifetime) + if (event != NULL) + { + if (!DuplicateHandle(GetCurrentProcess(), event, GetCurrentProcess(), &m_hEffectsChangedEvent, EVENT_MODIFY_STATE, FALSE, 0)) + { + RETURN_IF_FAILED(HRESULT_FROM_WIN32(GetLastError())); + } + } + + if (!IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW)) + { + wil::unique_cotaskmem_array_ptr<AUDIO_SYSTEMEFFECT> audioEffects( + static_cast<AUDIO_SYSTEMEFFECT*>(CoTaskMemAlloc(NUM_OF_EFFECTS * sizeof(AUDIO_SYSTEMEFFECT))), NUM_OF_EFFECTS); + RETURN_IF_NULL_ALLOC(audioEffects.get()); + + for (UINT i = 0; i < NUM_OF_EFFECTS; i++) + { + audioEffects[i].id = m_effectInfos[i].id; + audioEffects[i].state = m_effectInfos[i].state; + audioEffects[i].canSetState = m_effectInfos[i].canSetState; + } + + *numEffects = (UINT)audioEffects.size(); + *effects = audioEffects.release(); + } + + return S_OK; +} + +HRESULT CSwapAPOSFX::SetAudioSystemEffectState(GUID effectId, AUDIO_SYSTEMEFFECT_STATE state) +{ + for (auto effectInfo : m_effectInfos) + { + if (effectId == effectInfo.id) + { + AUDIO_SYSTEMEFFECT_STATE oldState = effectInfo.state; + effectInfo.state = state; + + // Synchronize access to the effects list and effects changed event + m_EffectsLock.Enter(); + + // If anything changed and a change event handle exists + if (oldState != effectInfo.state) + { + SetEvent(m_hEffectsChangedEvent); + m_apoLoggingService->ApoLog(APO_LOG_LEVEL_INFO, L"SetAudioSystemEffectState - effect: " GUID_FORMAT_STRING L", state: %i", effectInfo.id, effectInfo.state); + } + + m_EffectsLock.Leave(); + + return S_OK; + } + } + + return E_NOTFOUND; +} + //------------------------------------------------------------------------- // Description: // @@ -586,6 +728,64 @@ HRESULT CSwapAPOSFX::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERT return hr; } +HRESULT CSwapAPOSFX::GetApoNotificationRegistrationInfo(_Out_writes_(*count) APO_NOTIFICATION_DESCRIPTOR **apoNotifications, _Out_ DWORD *count) +{ + *apoNotifications = nullptr; + *count = 0; + + RETURN_HR_IF_NULL(E_FAIL, m_device); + + // Let the OS know what notifications we are interested in by returning an array of + // APO_NOTIFICATION_DESCRIPTORs. + constexpr DWORD numDescriptors = 1; + wil::unique_cotaskmem_ptr<APO_NOTIFICATION_DESCRIPTOR[]> apoNotificationDescriptors; + + apoNotificationDescriptors.reset(static_cast<APO_NOTIFICATION_DESCRIPTOR*>( + CoTaskMemAlloc(sizeof(APO_NOTIFICATION_DESCRIPTOR) * numDescriptors))); + RETURN_IF_NULL_ALLOC(apoNotificationDescriptors); + + // Our APO wants to get notified when a endpoint property changes on the audio endpoint. + apoNotificationDescriptors[0].type = APO_NOTIFICATION_TYPE_ENDPOINT_PROPERTY_CHANGE; + (void)m_device.query_to(&apoNotificationDescriptors[0].audioEndpointPropertyChange.device); + + *apoNotifications = apoNotificationDescriptors.release(); + *count = numDescriptors; + + return S_OK; +} + +void CSwapAPOSFX::HandleNotification(APO_NOTIFICATION *apoNotification) +{ + if (apoNotification->type == APO_NOTIFICATION_TYPE_ENDPOINT_PROPERTY_CHANGE) + { + // If either the master disable or our APO's enable properties changed... + if (PK_EQUAL(apoNotification->audioEndpointPropertyChange.propertyKey, PKEY_Endpoint_Enable_Channel_Swap_SFX) || + PK_EQUAL(apoNotification->audioEndpointPropertyChange.propertyKey, PKEY_AudioEndpoint_Disable_SysFx)) + { + struct KeyControl + { + PROPERTYKEY key; + LONG* value; + }; + + KeyControl controls[] = { + {PKEY_Endpoint_Enable_Channel_Swap_SFX, &m_fEnableSwapSFX}, + }; + + m_apoLoggingService->ApoLog(APO_LOG_LEVEL_INFO, L"HandleNotification - pkey: " GUID_FORMAT_STRING L" %d", GUID_FORMAT_ARGS(apoNotification->audioEndpointPropertyChange.propertyKey.fmtid), apoNotification->audioEndpointPropertyChange.propertyKey.pid); + + for (int i = 0; i < ARRAYSIZE(controls); i++) + { + LONG fNewValue = true; + + // Get the state of whether channel swap MFX is enabled or not + fNewValue = GetCurrentEffectsSetting(m_userStore.get(), controls[i].key, m_AudioProcessingMode); + + SetAudioSystemEffectState(m_effectInfos[i].id, fNewValue ? AUDIO_SYSTEMEFFECT_STATE_ON : AUDIO_SYSTEMEFFECT_STATE_OFF); + } + } + } +} //------------------------------------------------------------------------- // Description: @@ -611,7 +811,7 @@ CSwapAPOSFX::~CSwapAPOSFX(void) // // unregister for callbacks // - if (m_bIsInitialized) + if (m_bRegisteredEndpointNotificationCallback) { m_spEnumerator->UnregisterEndpointNotificationCallback(this); } diff --git a/audio/sysvad/BthhfpDevice.cpp b/audio/sysvad/BthhfpDevice.cpp index 3d0a388f..389563ed 100644 --- a/audio/sysvad/BthhfpDevice.cpp +++ b/audio/sysvad/BthhfpDevice.cpp @@ -271,9 +271,9 @@ BthHfpDevice::Init // m_SymbolicLinkName.MaximumLength = SymbolicLinkName->MaximumLength; m_SymbolicLinkName.Length = SymbolicLinkName->Length; - m_SymbolicLinkName.Buffer = (PWSTR) ExAllocatePoolWithTag(NonPagedPoolNx, - SymbolicLinkName->MaximumLength, - MINADAPTER_POOLTAG); + m_SymbolicLinkName.Buffer = (PWSTR) ExAllocatePool2(POOL_FLAG_NON_PAGED, + SymbolicLinkName->MaximumLength, + MINADAPTER_POOLTAG); if (m_SymbolicLinkName.Buffer == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; @@ -281,7 +281,7 @@ BthHfpDevice::Init IF_FAILED_ACTION_JUMP( ntStatus, - DPF(D_ERROR, ("Init: ExAllocatePoolWithTag failed, out of memory")), + DPF(D_ERROR, ("Init: ExAllocatePool2 failed, out of memory")), Done); RtlCopyUnicodeString(&m_SymbolicLinkName, SymbolicLinkName); @@ -1235,6 +1235,8 @@ BthHfpDevice::GetFormatsAndModes _In_ eDeviceType deviceType ) { + PAGED_CODE(); + PPIN_DEVICE_FORMATS_AND_MODES pFormatsAndModes = NULL; ULONG endpointIndex = 0; @@ -1270,11 +1272,11 @@ BthHfpDevice::GetFormatsAndModes } //============================================================================= -#pragma code_seg("PAGE") +#pragma code_seg() +_IRQL_requires_max_(DISPATCH_LEVEL) BOOL BthHfpDevice::IsNRECSupported() { - PAGED_CODE(); DPF_ENTER(("[BthHfpDevice::IsNRECSupported]")); return m_Descriptor->SupportsNREC; @@ -1853,14 +1855,14 @@ Bypass descriptor. // // Allocate memory needed to hold the info. // - descriptor = (PBTHHFP_DESCRIPTOR2)ExAllocatePoolWithTag(NonPagedPoolNx, length, MINADAPTER_POOLTAG); + descriptor = (PBTHHFP_DESCRIPTOR2)ExAllocatePool2(POOL_FLAG_NON_PAGED, length, MINADAPTER_POOLTAG); if (descriptor == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } IF_FAILED_ACTION_JUMP( ntStatus, - DPF(D_ERROR, ("GetBthHfpDescriptor: ExAllocatePoolWithTag failed, out of memory")), + DPF(D_ERROR, ("GetBthHfpDescriptor: ExAllocatePool2 failed, out of memory")), Done); // @@ -1991,14 +1993,14 @@ Bypass volume values. // // Allocate memory. // - propValues = (PKSPROPERTY_VALUES)ExAllocatePoolWithTag(NonPagedPoolNx, Length, MINADAPTER_POOLTAG); + propValues = (PKSPROPERTY_VALUES)ExAllocatePool2(POOL_FLAG_NON_PAGED, Length, MINADAPTER_POOLTAG); if (propValues == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } IF_FAILED_ACTION_JUMP( ntStatus, - DPF(D_ERROR, ("GetBthHfpVolumePropertyValues: ExAllocatePoolWithTag failed, out of memory")), + DPF(D_ERROR, ("GetBthHfpVolumePropertyValues: ExAllocatePool2 failed, out of memory")), Done); // @@ -3028,10 +3030,10 @@ BthHfpDevice::CreateCustomEndpointMinipair // cTopoPins = pBaseMinipair->TopoDescriptor->PinCount; cProperties = pBaseMinipair->TopoInterfacePropertyCount + 1; - pProperties = (SYSVAD_DEVPROPERTY*)ExAllocatePoolWithTag(NonPagedPoolNx, cProperties * sizeof(SYSVAD_DEVPROPERTY), SYSVAD_POOLTAG); - pNewMinipair = (ENDPOINT_MINIPAIR*)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(ENDPOINT_MINIPAIR), SYSVAD_POOLTAG); - pNewTopoFilterDesc = (PCFILTER_DESCRIPTOR*)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(PCFILTER_DESCRIPTOR), SYSVAD_POOLTAG); - pNewTopoPins = (PCPIN_DESCRIPTOR*)ExAllocatePoolWithTag(NonPagedPoolNx, cTopoPins * sizeof(PCPIN_DESCRIPTOR), SYSVAD_POOLTAG); + pProperties = (SYSVAD_DEVPROPERTY*)ExAllocatePool2(POOL_FLAG_NON_PAGED, cProperties * sizeof(SYSVAD_DEVPROPERTY), SYSVAD_POOLTAG); + pNewMinipair = (ENDPOINT_MINIPAIR*)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(ENDPOINT_MINIPAIR), SYSVAD_POOLTAG); + pNewTopoFilterDesc = (PCFILTER_DESCRIPTOR*)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(PCFILTER_DESCRIPTOR), SYSVAD_POOLTAG); + pNewTopoPins = (PCPIN_DESCRIPTOR*)ExAllocatePool2(POOL_FLAG_NON_PAGED, cTopoPins * sizeof(PCPIN_DESCRIPTOR), SYSVAD_POOLTAG); if ((pProperties != NULL) && (pNewMinipair != NULL) && (pNewTopoFilterDesc != NULL) && (pNewTopoPins != NULL)) { diff --git a/audio/sysvad/BthhfpDevice.h b/audio/sysvad/BthhfpDevice.h index c22fe0fa..7cee2616 100644 --- a/audio/sysvad/BthhfpDevice.h +++ b/audio/sysvad/BthhfpDevice.h @@ -325,6 +325,7 @@ public: _In_ eDeviceType deviceType ); + _IRQL_requires_max_(DISPATCH_LEVEL) STDMETHODIMP_(BOOL) IsNRECSupported(); STDMETHODIMP_(BOOL) GetNRECDisableStatus(); diff --git a/audio/sysvad/EndpointsCommon/A2dpHpDeviceFormats.h b/audio/sysvad/EndpointsCommon/A2dpHpDeviceFormats.h new file mode 100644 index 00000000..b6894fc3 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/A2dpHpDeviceFormats.h @@ -0,0 +1,222 @@ +//---------------------------------------------------------------------------- +// +// Formats +// This reflects the Data formats of the hardware mixer output +// Hardware vendor should retrieve these formats from the hardware and use to figure +// out an intersection with the sideband data-range +// +// Defines a data structure containing the KSDATAFORMAT_WAVEFORMATEXTENSIBLE +// structure for Audio Driver to build data range intersection +// +// NOTE: This is defined here because of the dependencies on the ks headers +//---------------------------------------------------------------------------- + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE A2dpHpSpeakerSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 44100, + 88200, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 48000, + 96000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE A2dpHpMicSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 44100, + 88200, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 48000, + 96000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; diff --git a/audio/sysvad/EndpointsCommon/AudioModuleHelper.cpp b/audio/sysvad/EndpointsCommon/AudioModuleHelper.cpp index f93c2bff..25ded875 100644 --- a/audio/sysvad/EndpointsCommon/AudioModuleHelper.cpp +++ b/audio/sysvad/EndpointsCommon/AudioModuleHelper.cpp @@ -243,6 +243,8 @@ AudioModule_FindModuleInList( _In_ ULONG InstanceId ) { + PAGED_CODE(); + for (ULONG i=0; i<AudioModuleCount; ++i) { AUDIOMODULE * module = &AudioModules[i]; diff --git a/audio/sysvad/EndpointsCommon/EndpointsCommon.vcxproj b/audio/sysvad/EndpointsCommon/EndpointsCommon.vcxproj index 6a7a4678..5b819cf3 100644 --- a/audio/sysvad/EndpointsCommon/EndpointsCommon.vcxproj +++ b/audio/sysvad/EndpointsCommon/EndpointsCommon.vcxproj @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|ARM64"> @@ -245,6 +245,9 @@ </DriverSign> </ItemDefinitionGroup> <ItemGroup> + <ClCompile Include="a2dphpminwavert.cpp" /> + <ClCompile Include="a2dphpspeakertopo.cpp" /> + <ClCompile Include="a2dphptopo.cpp" /> <ClCompile Include="AudioModuleHelper.cpp" /> <ClCompile Include="bthhfpmictopo.cpp" /> <ClCompile Include="bthhfpminwavert.cpp" /> @@ -277,4 +280,4 @@ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> -</Project>
\ No newline at end of file +</Project> diff --git a/audio/sysvad/EndpointsCommon/MiniportAudioEngineNode.cpp b/audio/sysvad/EndpointsCommon/MiniportAudioEngineNode.cpp index 5f45d15a..34477606 100644 --- a/audio/sysvad/EndpointsCommon/MiniportAudioEngineNode.cpp +++ b/audio/sysvad/EndpointsCommon/MiniportAudioEngineNode.cpp @@ -57,9 +57,9 @@ Remarks: } KSAUDIOENGINE_DESCRIPTOR, *PKSAUDIOENGINE_DESCRIPTOR; The fields are defined as: - nHostPinId � The ID of the pin factory connected to the audio engine node that is intended for host processed audio data. This is the pin factory on which a software audio engine will run. - nOffloadPinId � The ID of the pin factory connected to the audio engine node that is intended for offloaded streams. - nLoopbackPinId � The ID of the pin factory connected to the audio engine that is intended for supplying a post-mix loopback or reference stream. + nHostPinId – The ID of the pin factory connected to the audio engine node that is intended for host processed audio data. This is the pin factory on which a software audio engine will run. + nOffloadPinId – The ID of the pin factory connected to the audio engine node that is intended for offloaded streams. + nLoopbackPinId – The ID of the pin factory connected to the audio engine that is intended for supplying a post-mix loopback or reference stream. All pin ids need to be unique for each audio engine node. @@ -587,13 +587,10 @@ STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::SetDeviceChannelVolume(_In_ ULONG _ulN PAGED_CODE (); - DPF_ENTER(("[CMiniportWaveRT::SetEndpointChannelVolume]")); + DPF_ENTER(("[CMiniportWaveRT::SetDeviceChannelVolume]")); IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); - // Snap the volume level to our range of steppings. - LONG lVolume = VOLUME_NORMALIZE_IN_RANGE(_Volume); - - ntStatus = SetChannelVolume(_uiChannel, lVolume); + ntStatus = SetChannelVolume(_uiChannel, _Volume); Exit: return ntStatus; @@ -764,27 +761,20 @@ NTSTATUS CMiniportWaveRT::GetBufferSizeRange(_In_ ULONG _ulNodeId, _In_ KSDATAF NTSTATUS CMiniportWaveRT::GetVolumeChannelCount(_Out_ UINT32 *_pulChannelCount) { NTSTATUS ntStatus = STATUS_SUCCESS; - BOOL bHandledInSideband = FALSE; PAGED_CODE (); ASSERT (_pulChannelCount); DPF_ENTER(("[CMiniportWaveRT::GetVolumeChannelCount]")); - if (IsSidebandDevice()) + if (IsSidebandDevice() && m_pSidebandDevice->IsVolumeSupported(m_DeviceType)) { - if (m_pSidebandDevice->IsVolumeSupported(m_DeviceType)) - { - ULONG propSize = 0; - PKSPROPERTY_DESCRIPTION pPropDesc = (PKSPROPERTY_DESCRIPTION)m_pSidebandDevice->GetVolumeSettings(m_DeviceType, &propSize); - ASSERT(propSize >= sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) + sizeof(KSPROPERTY_STEPPING_LONG)); - PKSPROPERTY_MEMBERSHEADER pMembers = (PKSPROPERTY_MEMBERSHEADER)(pPropDesc + 1); - *_pulChannelCount = pMembers->MembersCount; - - bHandledInSideband = TRUE; - } + ULONG propSize = 0; + PKSPROPERTY_DESCRIPTION pPropDesc = (PKSPROPERTY_DESCRIPTION)m_pSidebandDevice->GetVolumeSettings(m_DeviceType, &propSize); + ASSERT(propSize >= sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) + sizeof(KSPROPERTY_STEPPING_LONG)); + PKSPROPERTY_MEMBERSHEADER pMembers = (PKSPROPERTY_MEMBERSHEADER)(pPropDesc + 1); + *_pulChannelCount = pMembers->MembersCount; } - - if(!bHandledInSideband) + else { *_pulChannelCount = m_DeviceMaxChannels; } @@ -796,41 +786,34 @@ NTSTATUS CMiniportWaveRT::GetVolumeSteppings(_Out_writes_bytes_(_ui32DataSize) P { PAGED_CODE (); UINT32 ulChannelCount = _ui32DataSize / sizeof(KSPROPERTY_STEPPING_LONG); - BOOL bHandledInSideband = FALSE; ASSERT (_pKsPropStepLong); DPF_ENTER(("[CMiniportWaveRT::GetVolumeSteppings]")); - if (IsSidebandDevice()) + if (IsSidebandDevice() && m_pSidebandDevice->IsVolumeSupported(m_DeviceType)) { - if (m_pSidebandDevice->IsVolumeSupported(m_DeviceType)) - { - // For Sideband Devices copy values from the Sideband DDI property descriptor - ULONG propSize = 0; - PKSPROPERTY_DESCRIPTION pPropDesc = (PKSPROPERTY_DESCRIPTION)m_pSidebandDevice->GetVolumeSettings(m_DeviceType, &propSize); + // For Sideband Devices copy values from the Sideband DDI property descriptor + ULONG propSize = 0; + PKSPROPERTY_DESCRIPTION pPropDesc = (PKSPROPERTY_DESCRIPTION)m_pSidebandDevice->GetVolumeSettings(m_DeviceType, &propSize); - ASSERT(propSize >= sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) + sizeof(KSPROPERTY_STEPPING_LONG)); + ASSERT(propSize >= sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) + sizeof(KSPROPERTY_STEPPING_LONG)); - PKSPROPERTY_MEMBERSHEADER pMembers = (PKSPROPERTY_MEMBERSHEADER)(pPropDesc + 1); + PKSPROPERTY_MEMBERSHEADER pMembers = (PKSPROPERTY_MEMBERSHEADER)(pPropDesc + 1); - ASSERT(ulChannelCount <= pMembers->MembersCount); - - if (ulChannelCount > pMembers->MembersCount) - { - return STATUS_INVALID_PARAMETER; - } + ASSERT(ulChannelCount <= pMembers->MembersCount); - PKSPROPERTY_STEPPING_LONG pRange = (PKSPROPERTY_STEPPING_LONG)(pMembers + 1); + if (ulChannelCount > pMembers->MembersCount) + { + return STATUS_INVALID_PARAMETER; + } - for (UINT i = 0; i < ulChannelCount; i++) - { - RtlCopyMemory(&_pKsPropStepLong[i], &pRange[i], sizeof(KSPROPERTY_STEPPING_LONG)); - } + PKSPROPERTY_STEPPING_LONG pRange = (PKSPROPERTY_STEPPING_LONG)(pMembers + 1); - bHandledInSideband = TRUE; + for (UINT i = 0; i < ulChannelCount; i++) + { + RtlCopyMemory(&_pKsPropStepLong[i], &pRange[i], sizeof(KSPROPERTY_STEPPING_LONG)); } } - - if(!bHandledInSideband) + else { ASSERT(ulChannelCount <= m_DeviceMaxChannels); if (ulChannelCount > m_DeviceMaxChannels) @@ -869,22 +852,11 @@ NTSTATUS CMiniportWaveRT::GetChannelVolume(_In_ UINT32 _uiChannel, _Out_ LONG * NTSTATUS status = STATUS_SUCCESS; - BOOL bHandledInSideband = FALSE; - - if (IsSidebandDevice()) + if (IsSidebandDevice() && m_pSidebandDevice->IsVolumeSupported(m_DeviceType)) { - if (m_pSidebandDevice->IsVolumeSupported(m_DeviceType)) - { - if (m_pSidebandDevice->IsVolumeSupported(m_DeviceType)) - { - status = m_pSidebandDevice->GetVolume(m_DeviceType, _uiChannel, _pVolume); - } - - bHandledInSideband = TRUE; - } + status = m_pSidebandDevice->GetVolume(m_DeviceType, _uiChannel, _pVolume); } - - if(!bHandledInSideband) + else { if (_uiChannel == ALL_CHANNELS_ID) { @@ -905,26 +877,32 @@ NTSTATUS CMiniportWaveRT::SetChannelVolume(_In_ UINT32 _uiChannel, _In_ LONG _ PAGED_CODE (); DPF_ENTER(("[CMiniportWaveRT::SetChannelVolume]")); - if (IsSidebandDevice()) + if (IsSidebandDevice() && m_pSidebandDevice->IsVolumeSupported(m_DeviceType)) { - if (m_pSidebandDevice->IsVolumeSupported(m_DeviceType)) - { - return m_pSidebandDevice->SetVolume(m_DeviceType, _uiChannel, _Volume); - } + return m_pSidebandDevice->SetVolume(m_DeviceType, _uiChannel, _Volume); } - - // Will reach here only if not handled through Sideband - if (_uiChannel == ALL_CHANNELS_ID) + else { - for (int i = 0; i < m_DeviceMaxChannels; i++) + // Normalize volume only for non-sideband devices. + // Volume levels are governed by Sideband provider + // e.g. USB, A2DP, HFP, etc. + + // Snap the volume level to our range of steppings. + LONG lVolume = VOLUME_NORMALIZE_IN_RANGE(_Volume); + + // Will reach here only if not handled through Sideband + if (_uiChannel == ALL_CHANNELS_ID) { - m_plVolumeLevel[i] = _Volume; + for (int i = 0; i < m_DeviceMaxChannels; i++) + { + m_plVolumeLevel[i] = lVolume; + } + } + else + { + ASSERT(_uiChannel <= m_DeviceMaxChannels); + m_plVolumeLevel[_uiChannel] = lVolume; } - } - else - { - ASSERT(_uiChannel <= m_DeviceMaxChannels); - m_plVolumeLevel[_uiChannel] = _Volume; } return STATUS_SUCCESS; @@ -996,27 +974,20 @@ NTSTATUS CMiniportWaveRT::GetChannelPeakMeter(_In_ UINT32 _uiChannel, _Out_ LO NTSTATUS CMiniportWaveRT::GetMuteChannelCount(_Out_ UINT32 *_pulChannelCount) { NTSTATUS ntStatus = STATUS_SUCCESS; - BOOL bHandledInSideband = FALSE; PAGED_CODE (); ASSERT (_pulChannelCount); DPF_ENTER(("[CMiniportWaveRT::GetMuteChannelCount]")); - if (IsSidebandDevice()) + if (IsSidebandDevice() && m_pSidebandDevice->IsMuteSupported(m_DeviceType)) { - if (m_pSidebandDevice->IsMuteSupported(m_DeviceType)) - { - ULONG propSize = 0; - PKSPROPERTY_DESCRIPTION pPropDesc = (PKSPROPERTY_DESCRIPTION)m_pSidebandDevice->GetMuteSettings(m_DeviceType, &propSize); - ASSERT(propSize >= sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) + sizeof(KSPROPERTY_STEPPING_LONG)); - PKSPROPERTY_MEMBERSHEADER pMembers = (PKSPROPERTY_MEMBERSHEADER)(pPropDesc + 1); - *_pulChannelCount = pMembers->MembersCount; - - bHandledInSideband = TRUE; - } + ULONG propSize = 0; + PKSPROPERTY_DESCRIPTION pPropDesc = (PKSPROPERTY_DESCRIPTION)m_pSidebandDevice->GetMuteSettings(m_DeviceType, &propSize); + ASSERT(propSize >= sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) + sizeof(KSPROPERTY_STEPPING_LONG)); + PKSPROPERTY_MEMBERSHEADER pMembers = (PKSPROPERTY_MEMBERSHEADER)(pPropDesc + 1); + *_pulChannelCount = pMembers->MembersCount; } - - if(!bHandledInSideband) + else { *_pulChannelCount = m_DeviceMaxChannels; } @@ -1027,43 +998,36 @@ NTSTATUS CMiniportWaveRT::GetMuteSteppings(_Out_writes_bytes_(_ui32DataSize) PK { PAGED_CODE (); UINT32 ulChannelCount = _ui32DataSize / sizeof(KSPROPERTY_STEPPING_LONG); - BOOL bHandledInSideband = FALSE; ASSERT (_pKsPropStepLong); DPF_ENTER(("[CMiniportWaveRT::GetMuteSteppings]")); - if (IsSidebandDevice()) + if (IsSidebandDevice() && m_pSidebandDevice->IsMuteSupported(m_DeviceType)) { - if (m_pSidebandDevice->IsMuteSupported(m_DeviceType)) - { - // For Sideband Devices copy values from the Sideband DDI property descriptor - ULONG propSize = 0; - PKSPROPERTY_DESCRIPTION pPropDesc = (PKSPROPERTY_DESCRIPTION)m_pSidebandDevice->GetMuteSettings(m_DeviceType, &propSize); + // For Sideband Devices copy values from the Sideband DDI property descriptor + ULONG propSize = 0; + PKSPROPERTY_DESCRIPTION pPropDesc = (PKSPROPERTY_DESCRIPTION)m_pSidebandDevice->GetMuteSettings(m_DeviceType, &propSize); - ASSERT(propSize >= sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) + sizeof(KSPROPERTY_STEPPING_LONG)); + ASSERT(propSize >= sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) + sizeof(KSPROPERTY_STEPPING_LONG)); - PKSPROPERTY_MEMBERSHEADER pMembers = (PKSPROPERTY_MEMBERSHEADER)(pPropDesc + 1); + PKSPROPERTY_MEMBERSHEADER pMembers = (PKSPROPERTY_MEMBERSHEADER)(pPropDesc + 1); - ASSERT(ulChannelCount <= pMembers->MembersCount); - - if (ulChannelCount > pMembers->MembersCount) - { - return STATUS_INVALID_PARAMETER; - } + ASSERT(ulChannelCount <= pMembers->MembersCount); - PKSPROPERTY_STEPPING_LONG pRange = (PKSPROPERTY_STEPPING_LONG)(pMembers + 1); + if (ulChannelCount > pMembers->MembersCount) + { + return STATUS_INVALID_PARAMETER; + } - for (UINT i = 0; i < ulChannelCount; i++) - { - RtlCopyMemory(&_pKsPropStepLong[i], &pRange[i], sizeof(KSPROPERTY_STEPPING_LONG)); - } + PKSPROPERTY_STEPPING_LONG pRange = (PKSPROPERTY_STEPPING_LONG)(pMembers + 1); - bHandledInSideband = TRUE; + for (UINT i = 0; i < ulChannelCount; i++) + { + RtlCopyMemory(&_pKsPropStepLong[i], &pRange[i], sizeof(KSPROPERTY_STEPPING_LONG)); } } - - if(!bHandledInSideband) + else { ASSERT(ulChannelCount <= m_DeviceMaxChannels); @@ -1088,19 +1052,11 @@ NTSTATUS CMiniportWaveRT::GetChannelMute(_In_ UINT32 _uiChannel, _Out_ BOOL *_ ASSERT (_pbMute); DPF_ENTER(("[CMiniportWaveRT::GetChannelMute]")); - BOOL bHandledInSideband = FALSE; - - if (IsSidebandDevice()) + if (IsSidebandDevice() && m_pSidebandDevice->IsMuteSupported(m_DeviceType)) { - if (m_pSidebandDevice->IsMuteSupported(m_DeviceType)) - { - *_pbMute = m_pSidebandDevice->GetMute(m_DeviceType, _uiChannel); - - bHandledInSideband = TRUE; - } + *_pbMute = m_pSidebandDevice->GetMute(m_DeviceType, _uiChannel); } - - if (!bHandledInSideband) + else { if (_uiChannel == ALL_CHANNELS_ID) { @@ -1121,12 +1077,9 @@ NTSTATUS CMiniportWaveRT::SetChannelMute(_In_ UINT32 _uiChannel, _In_ BOOL _bM PAGED_CODE (); DPF_ENTER(("[CMiniportWaveRT::SetChannelMute]")); - if (IsSidebandDevice()) + if (IsSidebandDevice() && m_pSidebandDevice->IsMuteSupported(m_DeviceType)) { - if (m_pSidebandDevice->IsMuteSupported(m_DeviceType)) - { - return m_pSidebandDevice->SetMute(m_DeviceType, _uiChannel, _bMute); - } + return m_pSidebandDevice->SetMute(m_DeviceType, _uiChannel, _bMute); } // Will reach here only if not handled through Sideband diff --git a/audio/sysvad/EndpointsCommon/NewDelete.cpp b/audio/sysvad/EndpointsCommon/NewDelete.cpp index 7a4f89d6..64c64cc0 100644 --- a/audio/sysvad/EndpointsCommon/NewDelete.cpp +++ b/audio/sysvad/EndpointsCommon/NewDelete.cpp @@ -37,20 +37,12 @@ extern "C" { */ PVOID operator new ( - size_t iSize, - _When_((poolType & NonPagedPoolMustSucceed) != 0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) - POOL_TYPE poolType, - ULONG tag + size_t iSize, + POOL_FLAGS poolFlags, + ULONG tag ) { - PVOID result = ExAllocatePoolWithTag(poolType, iSize, tag); - - if (result) - { - RtlZeroMemory(result, iSize); - } + PVOID result = ExAllocatePool2(poolFlags, iSize, tag); return result; } @@ -63,19 +55,11 @@ PVOID operator new */ PVOID operator new ( - size_t iSize, - _When_((poolType & NonPagedPoolMustSucceed) != 0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) - POOL_TYPE poolType + size_t iSize, + POOL_FLAGS poolFlags ) { - PVOID result = ExAllocatePoolWithTag(poolType, iSize, SYSVAD_POOLTAG); - - if (result) - { - RtlZeroMemory(result, iSize); - } + PVOID result = ExAllocatePool2(poolFlags, iSize, SYSVAD_POOLTAG); return result; } diff --git a/audio/sysvad/EndpointsCommon/NewDelete.h b/audio/sysvad/EndpointsCommon/NewDelete.h index 1b5d5e61..be46cbf5 100644 --- a/audio/sysvad/EndpointsCommon/NewDelete.h +++ b/audio/sysvad/EndpointsCommon/NewDelete.h @@ -28,12 +28,9 @@ Declaration of placement new and delete operators. */ PVOID operator new ( - size_t iSize, - _When_((poolType & NonPagedPoolMustSucceed) != 0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) - POOL_TYPE poolType, - ULONG tag + size_t iSize, + POOL_FLAGS poolFlags, + ULONG tag ); @@ -44,11 +41,8 @@ PVOID operator new */ PVOID operator new ( - size_t iSize, - _When_((poolType & NonPagedPoolMustSucceed) != 0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) - POOL_TYPE poolType + size_t iSize, + POOL_FLAGS poolFlags ); diff --git a/audio/sysvad/EndpointsCommon/a2dphpminipairs.h b/audio/sysvad/EndpointsCommon/a2dphpminipairs.h new file mode 100644 index 00000000..b46796c7 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/a2dphpminipairs.h @@ -0,0 +1,137 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + a2dphpminipairs.h + +Abstract: + + A2DP Sideband Audio endpoint filter definitions. + +--*/ + +#ifndef _SYSVAD_A2DPHPMINIPAIRS_H_ +#define _SYSVAD_A2DPHPMINIPAIRS_H_ + +#include "a2dphpspeakertopo.h" +#include "a2dphpspeakertoptable.h" +#include "a2dphpspeakerwavtable.h" + +NTSTATUS +CreateMiniportWaveRTSYSVAD +( + _Out_ PUNKNOWN *, + _In_ REFCLSID, + _In_opt_ PUNKNOWN, + _In_ POOL_FLAGS, + _In_ PUNKNOWN, + _In_opt_ PVOID, + _In_ PENDPOINT_MINIPAIR +); + +NTSTATUS +CreateMiniportTopologySYSVAD +( + _Out_ PUNKNOWN *, + _In_ REFCLSID, + _In_opt_ PUNKNOWN, + _In_ POOL_FLAGS, + _In_ PUNKNOWN, + _In_opt_ PVOID, + _In_ PENDPOINT_MINIPAIR +); + +/* +* A2DP Headphone speaker miniports. +* +********************************************************************************************** +* Topology/Wave bridge connection for A2DP Headphone Speaker * +* * +* +---------------------------------+ +-------+ * +* | Wave | | Topo | * +* | | | | * +* | +---------------------+ | | | * +* | | | | | | * +* System --->|0-----|1 HW Audio Engine 3|---2|---> Loopback | | * +* | | Node | | | | * +* Offload --->|1-----|2 0|---3|===============>|0---->1|---> Line Out * +* | +---------------------+ | | | * +* | | | | * +* +---------------------------------+ +-------+ * +* * +**********************************************************************************************/ + +static +PHYSICALCONNECTIONTABLE A2dpHpSpeakerTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_WAVEOUT_SOURCE, // TopologyIn + KSPIN_WAVE_RENDER_SOURCE, // WaveOut + CONNECTIONTYPE_WAVE_OUTPUT + } +}; + +// A subdevice is created for each HFP device using the reference string in the form of: +// <Base_Reference_String>-<Hashcode> +// where Base_Reference_String is one of the following template names +#define A2DPHP_SPEAKER_TOPO_NAME L"TopologyA2dpHpSpeaker" +#define A2DPHP_SPEAKER_WAVE_NAME L"WaveA2dpHpSpeaker" +// And Hashcode is 8-character long hexadecimal value + +// A reference string max length is being set to a value to accommodate them both +#define A2DPHP_INTERFACE_REFSTRING_MAX_LENGTH 100 + +static +ENDPOINT_MINIPAIR A2dpHpSpeakerMiniports = +{ + eA2dpHpSpeakerDevice, + NULL, // Will be generated based on A2DPHP_SPEAKER_TOPO_NAME and made unique for each side band interface + A2DPHP_SPEAKER_TOPO_NAME, // template name matches with KSNAME_TopologyBthHfpSpeaker in the inf's [Strings] section + CreateMiniportTopologySYSVAD, + &A2dpHpSpeakerTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + NULL, // Will be generated based on A2DPHP_SPEAKER_WAVE_NAME and made unique for each side band interface + A2DPHP_SPEAKER_WAVE_NAME, // template name matches with KSNAME_WaveBthHfpSpeaker in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &A2dpHpSpeakerWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + 1, + A2dpHpSpeakerPinDeviceFormatsAndModes, + SIZEOF_ARRAY(A2dpHpSpeakerPinDeviceFormatsAndModes), + A2dpHpSpeakerTopologyPhysicalConnections, + SIZEOF_ARRAY(A2dpHpSpeakerTopologyPhysicalConnections), + ENDPOINT_OFFLOAD_SUPPORTED +}; + +//============================================================================= +// +// Render miniport pairs. +// +static +PENDPOINT_MINIPAIR g_A2dpHpRenderEndpoints[] = +{ + &A2dpHpSpeakerMiniports +}; + +#define g_cA2dpHpRenderEndpoints (SIZEOF_ARRAY(g_A2dpHpRenderEndpoints)) + +// If the count of Render endpoints changes, update formula below +C_ASSERT(g_cA2dpHpRenderEndpoints == 1); + +//============================================================================= +// +// Total miniports = # endpoints (topology + wave) +// +// +// Allow 5 A2DP Headphone Endpoints. +// +#define MAX_A2DPHS_DEVICES (5) +#define g_MaxA2dpHpMiniports ((g_cA2dpHpRenderEndpoints) * 2 * MAX_A2DPHS_DEVICES) + +// g_MaxA2dpHpMiniports is used when calculating the MaxObjects inside AddDevice. +C_ASSERT(g_MaxA2dpHpMiniports == 10); + +#endif // _SYSVAD_A2DPHPMINIPAIRS_H_ + diff --git a/audio/sysvad/EndpointsCommon/a2dphpminwavert.cpp b/audio/sysvad/EndpointsCommon/a2dphpminwavert.cpp new file mode 100644 index 00000000..532fc92f --- /dev/null +++ b/audio/sysvad/EndpointsCommon/a2dphpminwavert.cpp @@ -0,0 +1,200 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + a2dphpminwavert.cpp + +Abstract: + + Implementation of wavert miniport. + +--*/ +#ifdef SYSVAD_A2DP_SIDEBAND + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include <limits.h> +#include "simple.h" +#include "minwavert.h" +#include "minwavertstream.h" +#include "IHVPrivatePropertySet.h" + + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::PropertyHandler_A2dpHpAudioEffectsDiscoveryEffectsList +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_AudioEffectsDiscovery, KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[PropertyHandler_A2dpHpAudioEffectsDiscoveryEffectsList]")); + + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + ULONG nPinId = (ULONG)-1; + + // + // Validate Pin ID. + // + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + // This prop is valid only on streaming pins. + if (IsSystemRenderPin(nPinId) || IsSystemCapturePin(nPinId)) + { + ntStatus = STATUS_SUCCESS; + } + else if (IsBridgePin(nPinId) || + IsLoopbackPin(nPinId) || + IsOffloadPin(nPinId)) + { + ntStatus = STATUS_NOT_SUPPORTED; + } + } + + IF_FAILED_JUMP(ntStatus, Done); + + // + // Valid actions: get and basicsupport. + // + ntStatus = STATUS_INVALID_PARAMETER; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + ASSERT(m_pSidebandDevice != NULL); + + + // If it is render pin (data flow in), + // return size 0 effect list + if (IsSystemRenderPin(nPinId)) + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_SUCCESS; + } + else + { + // Compute total size, two effects: NS and EC (see below). + ULONG cbMinSize = sizeof(GUID) * 2; + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbMinSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + PGUID effectList = PGUID(PropertyRequest->Value); + + *effectList = AUDIO_EFFECT_TYPE_ACOUSTIC_ECHO_CANCELLATION; + *(effectList + 1) = AUDIO_EFFECT_TYPE_NOISE_SUPPRESSION; + + PropertyRequest->ValueSize = cbMinSize; + ntStatus = STATUS_SUCCESS; + } + } + } + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_A2dpHpWaveFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects general property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + CMiniportWaveRT* waveRt = reinterpret_cast<CMiniportWaveRT*>(PropertyRequest->MajorTarget); + + if (waveRt == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + waveRt->AddRef(); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Pin)) + { + switch (PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_PIN_PROPOSEDATAFORMAT: + ntStatus = waveRt->PropertyHandlerProposedFormat(PropertyRequest); + break; + } + } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_AudioEffectsDiscovery)) + { + switch(PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST: + ntStatus = waveRt->PropertyHandler_A2dpHpAudioEffectsDiscoveryEffectsList(PropertyRequest); + break; + } + } + + waveRt->Release(); + + return ntStatus; +} // PropertyHandler_A2dpHpWaveFilter + +#pragma code_seg("PAGE") +#endif // SYSVAD_A2DP_SIDEBAND + + + diff --git a/audio/sysvad/EndpointsCommon/a2dphpspeakertopo.cpp b/audio/sysvad/EndpointsCommon/a2dphpspeakertopo.cpp new file mode 100644 index 00000000..0f24a4dc --- /dev/null +++ b/audio/sysvad/EndpointsCommon/a2dphpspeakertopo.cpp @@ -0,0 +1,120 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + a2dphpspeakertopo.cpp + +Abstract: + + Implementation of topology miniport for the A2DP Headphone speaker (external). + +--*/ +#ifdef SYSVAD_A2DP_SIDEBAND + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "mintopo.h" +#include "a2dphptopo.h" +#include "a2dphpspeakertopo.h" +#include "a2dphpspeakertoptable.h" + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_A2dpHpSpeakerTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_A2dpHpSpeakerTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + switch(PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_JACK_DESCRIPTION: + ntStatus = PropertyHandler_A2dpHpJackDescription( + PropertyRequest, + ARRAYSIZE(A2dpHpSpeakerJackDescriptions), + A2dpHpSpeakerJackDescriptions); + break; + + case KSPROPERTY_JACK_DESCRIPTION2: + ntStatus = PropertyHandler_A2dpHpJackDescription2( + PropertyRequest, + ARRAYSIZE(A2dpHpSpeakerJackDescriptions), + A2dpHpSpeakerJackDescriptions); + break; + + case KSPROPERTY_JACK_CONTAINERID: + ntStatus = PropertyHandler_A2dpHpJackContainerId( + PropertyRequest, + ARRAYSIZE(A2dpHpSpeakerJackDescriptions), + A2dpHpSpeakerJackDescriptions); + break; + } + } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_BtAudio)) + { + switch (PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_ONESHOT_RECONNECT: + ntStatus = PropertyHandler_A2dpHpOneShotReconnect(PropertyRequest); + break; + + case KSPROPERTY_ONESHOT_DISCONNECT: + ntStatus = PropertyHandler_A2dpHpOneShotDisconnect(PropertyRequest); + break; + } + } + + return ntStatus; +} // PropertyHandler_A2dpHpSpeakerTopoFilter + +//============================================================================= +#pragma code_seg() +NTSTATUS +PropertyHandler_A2dpHpSpeakerTopoFilterEvent +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + ASSERT(EventRequest); + + DPF_ENTER(("[PropertyHandler_A2dpHpSpeakerTopoFilterEvent]")); + + return PropertyHandler_A2dpHpTopoNodeEvent(EventRequest); +} + +#pragma code_seg() +#endif // SYSVAD_A2DP_SIDEBAND + + diff --git a/audio/sysvad/EndpointsCommon/a2dphpspeakertopo.h b/audio/sysvad/EndpointsCommon/a2dphpspeakertopo.h new file mode 100644 index 00000000..71f68030 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/a2dphpspeakertopo.h @@ -0,0 +1,30 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + a2dphpspeakertopo.h + +Abstract: + + Declaration of topology miniport for the A2DP Headphone speaker (external). + +--*/ + +#ifndef _SYSVAD_A2DPHPSPEAKERTOPO_H_ +#define _SYSVAD_A2DPHPSPEAKERTOPO_H_ + +// Function declarations. +NTSTATUS +PropertyHandler_A2dpHpSpeakerTopoFilter( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +NTSTATUS +PropertyHandler_A2dpHpSpeakerTopoFilterEvent( + _In_ PPCEVENT_REQUEST EventRequest + ); + +#endif // _SYSVAD_A2DPHPSPEAKERTOPO_H_ + diff --git a/audio/sysvad/EndpointsCommon/a2dphpspeakertoptable.h b/audio/sysvad/EndpointsCommon/a2dphpspeakertoptable.h new file mode 100644 index 00000000..7a3e23de --- /dev/null +++ b/audio/sysvad/EndpointsCommon/a2dphpspeakertoptable.h @@ -0,0 +1,188 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + a2dphpspeakertoptable.h + +Abstract: + + Declaration of topology tables for the A2DP Headphone speaker (external). + +--*/ + +#ifndef _SYSVAD_A2DPHPSPEAKERTOPTABLE_H_ +#define _SYSVAD_A2DPHPSPEAKERTOPTABLE_H_ + +#include "a2dphpspeakertopo.h" + +//============================================================================= +static +KSDATARANGE A2dpHpSpeakerTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE A2dpHpSpeakerTopoPinDataRangePointersBridge[] = +{ + &A2dpHpSpeakerTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR A2dpHpSpeakerTopoMiniportPins[] = +{ + // KSPIN_TOPO_WAVEOUT_SOURCE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(A2dpHpSpeakerTopoPinDataRangePointersBridge),// DataRangesCount + A2dpHpSpeakerTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_LINEOUT_DEST + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(A2dpHpSpeakerTopoPinDataRangePointersBridge),// DataRangesCount + A2dpHpSpeakerTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_HEADSET_SPEAKERS, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION A2dpHpSpeakerJackDesc = +{ + KSAUDIO_SPEAKER_STEREO, + 0, // color + eConnTypeOtherDigital, + eGeoLocBottom, + eGenLocPrimaryBox, + ePortConnJack, + FALSE // run-time code sets this value. +}; + +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION A2dpHpSpeakerJackDescriptions[] = +{ + NULL, + &A2dpHpSpeakerJackDesc +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR A2dpHpSpeakerTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_WAVEOUT_SOURCE, PCFILTER_NODE, KSPIN_TOPO_LINEOUT_DEST } +}; + +//============================================================================= +static +PCPROPERTY_ITEM A2dpHpSpeakerPropertiesTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_A2dpHpSpeakerTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_A2dpHpSpeakerTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_CONTAINERID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_A2dpHpSpeakerTopoFilter + }, + { + &KSPROPSETID_BtAudio, + KSPROPERTY_ONESHOT_RECONNECT, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_A2dpHpSpeakerTopoFilter + }, + { + &KSPROPSETID_BtAudio, + KSPROPERTY_ONESHOT_DISCONNECT, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_A2dpHpSpeakerTopoFilter + } +}; + +static +PCEVENT_ITEM A2dpHpSpeakerPropertiesTopoFilterEvent[] = +{ + { + &KSEVENTSETID_PinCapsChange, // Something changed! + KSEVENT_PINCAPS_JACKINFOCHANGE, // The only event-property defined. + KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT, + PropertyHandler_A2dpHpSpeakerTopoFilterEvent + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationA2dpHpSpeakerTopoFilterWithEvent, + A2dpHpSpeakerPropertiesTopoFilter, + A2dpHpSpeakerPropertiesTopoFilterEvent); + +//============================================================================= +static +PCFILTER_DESCRIPTOR A2dpHpSpeakerTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationA2dpHpSpeakerTopoFilterWithEvent, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(A2dpHpSpeakerTopoMiniportPins), // PinCount + A2dpHpSpeakerTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(A2dpHpSpeakerTopoMiniportConnections), // ConnectionCount + A2dpHpSpeakerTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_A2DPHPSPEAKERTOPTABLE_H_ + + diff --git a/audio/sysvad/EndpointsCommon/a2dphpspeakerwavtable.h b/audio/sysvad/EndpointsCommon/a2dphpspeakerwavtable.h new file mode 100644 index 00000000..00f69958 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/a2dphpspeakerwavtable.h @@ -0,0 +1,759 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + s2dphppeakerwavtable.h + +Abstract: + + Declaration of wave miniport tables for the A2DP Headphone speaker (external). + +--*/ + +#ifndef _SYSVAD_A2DPHPSPEAKERWAVTABLE_H_ +#define _SYSVAD_A2DPHPSPEAKERWAVTABLE_H_ + +// +// Function prototypes. +// +NTSTATUS PropertyHandler_A2dpHpWaveFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + + + +#define A2DPHPSPEAKER_DEVICE_MAX_CHANNELS 2 // Max Channels. + +#define A2DPHPSPEAKER_HOST_MAX_CHANNELS 2 // Max Channels. +#define A2DPHPSPEAKER_HOST_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define A2DPHPSPEAKER_HOST_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define A2DPHPSPEAKER_HOST_MIN_SAMPLE_RATE 24000 // Min Sample Rate +#define A2DPHPSPEAKER_HOST_MAX_SAMPLE_RATE 96000 // Max Sample Rate + +#define A2DPHPSPEAKER_OFFLOAD_MAX_CHANNELS 2 // Max Channels. +#define A2DPHPSPEAKER_OFFLOAD_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define A2DPHPSPEAKER_OFFLOAD_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define A2DPHPSPEAKER_OFFLOAD_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define A2DPHPSPEAKER_OFFLOAD_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +#define A2DPHPSPEAKER_LOOPBACK_MAX_CHANNELS A2DPHPSPEAKER_HOST_MAX_CHANNELS // Must be equal to host pin's Max Channels. +#define A2DPHPSPEAKER_LOOPBACK_MIN_BITS_PER_SAMPLE A2DPHPSPEAKER_HOST_MIN_BITS_PER_SAMPLE // Must be equal to host pin's Min Bits Per Sample +#define A2DPHPSPEAKER_LOOPBACK_MAX_BITS_PER_SAMPLE A2DPHPSPEAKER_HOST_MAX_BITS_PER_SAMPLE // Must be equal to host pin's Max Bits Per Sample +#define A2DPHPSPEAKER_LOOPBACK_MIN_SAMPLE_RATE A2DPHPSPEAKER_HOST_MIN_SAMPLE_RATE // Must be equal to host pin's Min Sample Rate +#define A2DPHPSPEAKER_LOOPBACK_MAX_SAMPLE_RATE A2DPHPSPEAKER_HOST_MAX_SAMPLE_RATE // Must be equal to host pin's Max Sample Rate + +#define A2DPHPSPEAKER_DOLBY_DIGITAL_MAX_CHANNELS 2 // Max Channels. +#define A2DPHPSPEAKER_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define A2DPHPSPEAKER_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define A2DPHPSPEAKER_DOLBY_DIGITAL_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define A2DPHPSPEAKER_DOLBY_DIGITAL_MAX_SAMPLE_RATE 44100 // Max Sample Rate + +// +// Max # of pin instances. +// +#define A2DPHPSPEAKER_MAX_INPUT_SYSTEM_STREAMS 6 +#define A2DPHPSPEAKER_MAX_INPUT_OFFLOAD_STREAMS MAX_INPUT_OFFLOAD_STREAMS +#define A2DPHPSPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS MAX_OUTPUT_LOOPBACK_STREAMS + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE A2dpHpSpeakerAudioEngineSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE A2dpHpSpeakerHostPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 32000, + 128000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 5 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE A2dpHpSpeakerOffloadPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT A2dpHpSpeakerHostPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &A2dpHpSpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &A2dpHpSpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MEDIA, + &A2dpHpSpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MOVIE, + &A2dpHpSpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, + &A2dpHpSpeakerHostPinSupportedDeviceFormats[0].DataFormat // 24KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_NOTIFICATION, + &A2dpHpSpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + } +}; + +static +MODE_AND_DEFAULT_FORMAT A2dpHpSpeakerOffloadPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &A2dpHpSpeakerOffloadPinSupportedDeviceFormats[1].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MEDIA, + &A2dpHpSpeakerOffloadPinSupportedDeviceFormats[1].DataFormat // 48KHz + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES A2dpHpSpeakerPinDeviceFormatsAndModes[] = +{ + { + SystemRenderPin, + A2dpHpSpeakerHostPinSupportedDeviceFormats, + SIZEOF_ARRAY(A2dpHpSpeakerHostPinSupportedDeviceFormats), + A2dpHpSpeakerHostPinSupportedDeviceModes, + SIZEOF_ARRAY(A2dpHpSpeakerHostPinSupportedDeviceModes) + }, + { + OffloadRenderPin, + A2dpHpSpeakerOffloadPinSupportedDeviceFormats, + SIZEOF_ARRAY(A2dpHpSpeakerOffloadPinSupportedDeviceFormats), + A2dpHpSpeakerOffloadPinSupportedDeviceModes, + SIZEOF_ARRAY(A2dpHpSpeakerOffloadPinSupportedDeviceModes), + }, + { + RenderLoopbackPin, + A2dpHpSpeakerHostPinSupportedDeviceFormats, // Must support all the formats supported by host pin + SIZEOF_ARRAY(A2dpHpSpeakerHostPinSupportedDeviceFormats), + NULL, // loopback doesn't support modes. + 0 + }, + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + NoPin, // For convenience, offload engine device formats appended here + A2dpHpSpeakerAudioEngineSupportedDeviceFormats, + SIZEOF_ARRAY(A2dpHpSpeakerAudioEngineSupportedDeviceFormats), + NULL, // no modes for this entry. + 0 + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO A2dpHpSpeakerPinDataRangesStream[] = +{ + { // 0 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + A2DPHPSPEAKER_HOST_MAX_CHANNELS, + A2DPHPSPEAKER_HOST_MIN_BITS_PER_SAMPLE, + A2DPHPSPEAKER_HOST_MAX_BITS_PER_SAMPLE, + A2DPHPSPEAKER_HOST_MIN_SAMPLE_RATE, + A2DPHPSPEAKER_HOST_MAX_SAMPLE_RATE + }, + { // 1 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + A2DPHPSPEAKER_OFFLOAD_MAX_CHANNELS, + A2DPHPSPEAKER_OFFLOAD_MIN_BITS_PER_SAMPLE, + A2DPHPSPEAKER_OFFLOAD_MAX_BITS_PER_SAMPLE, + A2DPHPSPEAKER_OFFLOAD_MIN_SAMPLE_RATE, + A2DPHPSPEAKER_OFFLOAD_MAX_SAMPLE_RATE + }, + { // 2 + { + sizeof(KSDATARANGE_AUDIO), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + A2DPHPSPEAKER_LOOPBACK_MAX_CHANNELS, + A2DPHPSPEAKER_LOOPBACK_MIN_BITS_PER_SAMPLE, + A2DPHPSPEAKER_LOOPBACK_MAX_BITS_PER_SAMPLE, + A2DPHPSPEAKER_LOOPBACK_MIN_SAMPLE_RATE, + A2DPHPSPEAKER_LOOPBACK_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE A2dpHpSpeakerPinDataRangePointersStream[] = +{ + PKSDATARANGE(&A2dpHpSpeakerPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE A2dpHpSpeakerPinDataRangePointersOffloadStream[] = +{ + PKSDATARANGE(&A2dpHpSpeakerPinDataRangesStream[1]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE A2dpHpSpeakerPinDataRangePointersLoopbackStream[] = +{ + PKSDATARANGE(&A2dpHpSpeakerPinDataRangesStream[2]) +}; + +//============================================================================= +static +KSDATARANGE A2dpHpSpeakerPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE A2dpHpSpeakerPinDataRangePointersBridge[] = +{ + &A2dpHpSpeakerPinDataRangesBridge[0] +}; + +//============================================================================= + +static +PCPROPERTY_ITEM PropertiesA2dpHpSpeakerOffloadPin[] = +{ + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_GET_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + }, + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationA2dpHpSpeakerOffloadPin, PropertiesA2dpHpSpeakerOffloadPin); + +//============================================================================= +static +PCPIN_DESCRIPTOR A2dpHpSpeakerWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_SYSTEM + { + A2DPHPSPEAKER_MAX_INPUT_SYSTEM_STREAMS, + A2DPHPSPEAKER_MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(A2dpHpSpeakerPinDataRangePointersStream), + A2dpHpSpeakerPinDataRangePointersStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_OFFLOAD + { + A2DPHPSPEAKER_MAX_INPUT_OFFLOAD_STREAMS, + A2DPHPSPEAKER_MAX_INPUT_OFFLOAD_STREAMS, + 0, + &AutomationA2dpHpSpeakerOffloadPin, // AutomationTable + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(A2dpHpSpeakerPinDataRangePointersOffloadStream), + A2dpHpSpeakerPinDataRangePointersOffloadStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_LOOPBACK + { + A2DPHPSPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS, + A2DPHPSPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(A2dpHpSpeakerPinDataRangePointersLoopbackStream), + A2dpHpSpeakerPinDataRangePointersLoopbackStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSNODETYPE_AUDIO_LOOPBACK, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER_SOURCE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(A2dpHpSpeakerPinDataRangePointersBridge), + A2dpHpSpeakerPinDataRangePointersBridge, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR A2dpHpSpeakerWaveMiniportNodes[] = +{ + // KSNODE_WAVE_AUDIO_ENGINE + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_AUDIO_ENGINE, // Type KSNODETYPE_AUDIO_ENGINE + NULL // Name + } +}; +//============================================================================= +// +// ---------------------------- +// | | +// System Pin 0-->| |--> 2 Loopback Pin +// | HW Audio Engine node | +// Offload Pin 1-->| |--> 3 KSPIN_WAVE_RENDER_SOURCE +// | | +// ---------------------------- +static +PCCONNECTION_DESCRIPTOR A2dpHpSpeakerWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_SYSTEM, KSNODE_WAVE_AUDIO_ENGINE, 1 }, + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_OFFLOAD, KSNODE_WAVE_AUDIO_ENGINE, 2 }, + { KSNODE_WAVE_AUDIO_ENGINE, 3, PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_LOOPBACK }, + { KSNODE_WAVE_AUDIO_ENGINE, 0, PCFILTER_NODE, KSPIN_WAVE_RENDER_SOURCE }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesA2dpHpSpeakerWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_A2dpHpWaveFilter + }, + { + &KSPROPSETID_AudioEffectsDiscovery, + KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_A2dpHpWaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationA2dpHpSpeakerWaveFilter, PropertiesA2dpHpSpeakerWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR A2dpHpSpeakerWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationA2dpHpSpeakerWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(A2dpHpSpeakerWaveMiniportPins), // PinCount + A2dpHpSpeakerWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(A2dpHpSpeakerWaveMiniportNodes), // NodeCount + A2dpHpSpeakerWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(A2dpHpSpeakerWaveMiniportConnections),// ConnectionCount + A2dpHpSpeakerWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_A2DPHPSPEAKERWAVTABLE_H_ + diff --git a/audio/sysvad/EndpointsCommon/a2dphptopo.cpp b/audio/sysvad/EndpointsCommon/a2dphptopo.cpp new file mode 100644 index 00000000..661569f8 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/a2dphptopo.cpp @@ -0,0 +1,581 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + a2dphptopo.cpp + +Abstract: + + Implementation of topology miniport for the A2DP Headphone (external). + +--*/ +#ifdef SYSVAD_A2DP_SIDEBAND + +#pragma warning (disable : 4127) + +#include <sysvad.h> +#include "simple.h" +#include "mintopo.h" +#include "a2dphptopo.h" + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_A2dpHpVolumeLevel_BasicSupport +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles basic support for A2DP Headphone ( KSPROPSETID_Audio, KSPROPERTY_AUDIO_VOLUMELEVEL ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + ULONG cbDescriptionSize = sizeof(KSPROPERTY_DESCRIPTION); + + ASSERT(PropertyRequest); + + if (PropertyRequest->ValueSize >= cbDescriptionSize) + { + PCMiniportTopology miniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + ULONG volumeSettingsSize = 0; + PKSPROPERTY_DESCRIPTION volumeSettings; + PKSPROPERTY_DESCRIPTION propDesc; + PSIDEBANDDEVICECOMMON a2dpHpDevice; + + propDesc = PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + a2dpHpDevice = miniport->GetSidebandDevice(); // weak ref. + ASSERT(a2dpHpDevice != NULL); + + volumeSettings = (PKSPROPERTY_DESCRIPTION)a2dpHpDevice->GetVolumeSettings(miniport->m_DeviceType, &volumeSettingsSize); + ASSERT(volumeSettings != NULL); + ASSERT(volumeSettingsSize != 0); + + ULONG cbFullProperty = volumeSettings->DescriptionSize; + + // if return buffer can hold a range description, return it + if(PropertyRequest->ValueSize >= cbFullProperty) + { + RtlCopyMemory(propDesc, volumeSettings, volumeSettings->DescriptionSize); + // set the return value size + PropertyRequest->ValueSize = cbFullProperty; + } + else + { + // otherwise only return description header + RtlCopyMemory(propDesc, volumeSettings, cbDescriptionSize); + // set the return value size + PropertyRequest->ValueSize = cbDescriptionSize; + } + } + else if(PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags + PULONG AccessFlags = PULONG(PropertyRequest->Value); + + PropertyRequest->ValueSize = sizeof(ULONG); + *AccessFlags = KSPROPERTY_TYPE_ALL; + } + else + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_A2dpHpMute_BasicSupport +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles basic support for A2DP Headphone ( KSPROPSETID_Audio, KSPROPERTY_AUDIO_MUTE ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + ULONG cbDescriptionSize = sizeof(KSPROPERTY_DESCRIPTION); + ASSERT(PropertyRequest); + + if (PropertyRequest->ValueSize >= cbDescriptionSize) + { + PCMiniportTopology miniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + ULONG muteSettingsSize = 0; + PKSPROPERTY_DESCRIPTION muteSettings; + PKSPROPERTY_DESCRIPTION propDesc; + PSIDEBANDDEVICECOMMON a2dpHpDevice; + + propDesc = PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + a2dpHpDevice = miniport->GetSidebandDevice(); // weak ref. + ASSERT(a2dpHpDevice != NULL); + + muteSettings = (PKSPROPERTY_DESCRIPTION)a2dpHpDevice->GetMuteSettings(miniport->m_DeviceType, &muteSettingsSize); + ASSERT(muteSettings != NULL); + ASSERT(muteSettingsSize != 0); + + ULONG cbFullProperty = muteSettings->DescriptionSize; + + // if return buffer can hold mute description, return it too + if(PropertyRequest->ValueSize >= cbFullProperty) + { + RtlCopyMemory(propDesc, muteSettings, muteSettings->DescriptionSize); + // set the return value size + PropertyRequest->ValueSize = cbFullProperty; + } + else + { + // otherwise only return description header + RtlCopyMemory(propDesc, muteSettings, cbDescriptionSize); + // set the return value size + PropertyRequest->ValueSize = cbDescriptionSize; + } + } + else if(PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags + PULONG AccessFlags = PULONG(PropertyRequest->Value); + + PropertyRequest->ValueSize = sizeof(ULONG); + *AccessFlags = KSPROPERTY_TYPE_ALL; + } + else + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_A2dpHpJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest - + + cJackDescriptions - + + JackDescriptions - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + PCMiniportTopology miniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PSIDEBANDDEVICECOMMON a2dpHpDevice = NULL; + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + a2dpHpDevice = miniport->GetSidebandDevice(); // weak ref. + ASSERT(a2dpHpDevice != NULL); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + + pDesc->IsConnected = a2dpHpDevice->GetConnectionStatus() ? TRUE : FALSE; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_A2dpHpJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + + cJackDescriptions - + + JackDescriptions - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_A2dpHpJackContainerId +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_CONTAINERID ) + +Arguments: + + PropertyRequest - + + cJackDescriptions - + + JackDescriptions - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCMiniportTopology miniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + // This property is only valid on bridge pins. + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(GUID); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PSIDEBANDDEVICECOMMON a2dpHpDevice = NULL; + + GUID* guid = (GUID *)PropertyRequest->Value; + + a2dpHpDevice = miniport->GetSidebandDevice(); // weak ref. + ASSERT(a2dpHpDevice != NULL); + + *guid = a2dpHpDevice->GetContainerId(miniport->m_DeviceType); + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +PropertyHandler_A2dpHpTopoNodeEvent +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + ASSERT(EventRequest); + + DPF_ENTER(("[%!FUNC!]")); + + // The major target is the object pointer to the topology miniport. + PCMiniportTopology pMiniport = (PCMiniportTopology)EventRequest->MajorTarget; + ASSERT (pMiniport); + + switch (EventRequest->Verb) + { + // Do we support event handling?!? + case PCEVENT_VERB_SUPPORT: + DPF(D_VERBOSE, ("BasicSupport Query for event.")); + break; + + // We should add the event now! + case PCEVENT_VERB_ADD: + DPF(D_VERBOSE, ("Adding event.")); + + // If we have the interface and EventEntry is defined ... + if (EventRequest->EventEntry) + { + pMiniport->AddEventToEventList(EventRequest->EventEntry); + } + else + { + return STATUS_UNSUCCESSFUL; + } + break; + + case PCEVENT_VERB_REMOVE: + // We cannot remove the event but we can stop generating the + // events. However, it also doesn't hurt to always generate them ... + DPF(D_VERBOSE, ("Removing event.")); + break; + + default: + return STATUS_INVALID_PARAMETER; + } + + return STATUS_SUCCESS; +} + +NTSTATUS PropertyHandler_A2dpHpOneShotReconnect(PPCPROPERTY_REQUEST PropertyRequest) +{ + DPF_ENTER(("[%!FUNC!]")); + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = PropertyHandler_BasicSupport( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + // Nothing needs to be done in this handler. It only needs to be defined to enable connect and disconnect through the sound control panel. + ntStatus = STATUS_SUCCESS; + } + return ntStatus; +} + +NTSTATUS PropertyHandler_A2dpHpOneShotDisconnect(PPCPROPERTY_REQUEST PropertyRequest) +{ + DPF_ENTER(("[%!FUNC!]")); + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = PropertyHandler_BasicSupport( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + // Nothing needs to be done in this handler. It only needs to be defined to enable connect and disconnect through the sound control panel. + ntStatus = STATUS_SUCCESS; + } + return ntStatus; +} + +#pragma code_seg() +#endif //SYSVAD_A2DP_SIDEBAND + + diff --git a/audio/sysvad/EndpointsCommon/a2dphptopo.h b/audio/sysvad/EndpointsCommon/a2dphptopo.h new file mode 100644 index 00000000..0b2303bd --- /dev/null +++ b/audio/sysvad/EndpointsCommon/a2dphptopo.h @@ -0,0 +1,75 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + a2dphptopo.h + +Abstract: + + Declaration of topology miniport for the mic (external: A2DP headphone). + +--*/ + +#ifndef _SYSVAD_A2DPHPTOPO_H_ +#define _SYSVAD_A2DPHPTOPO_H_ + +NTSTATUS +PropertyHandler_A2dpHpVolumeLevel_BasicSupport( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +NTSTATUS +PropertyHandler_A2dpHpMicVolumeLevel( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +NTSTATUS +PropertyHandler_A2dpHpMute_BasicSupport( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +NTSTATUS +PropertyHandler_A2dpHpMicMute( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +NTSTATUS +PropertyHandler_A2dpHpJackDescription( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions + ); + +NTSTATUS +PropertyHandler_A2dpHpJackDescription2( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions + ); + +NTSTATUS +PropertyHandler_A2dpHpJackContainerId( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions + ); + +NTSTATUS +PropertyHandler_A2dpHpTopoNodeEvent( + _In_ PPCEVENT_REQUEST EventRequest + ); + +NTSTATUS +PropertyHandler_A2dpHpOneShotReconnect( + _In_ PPCPROPERTY_REQUEST PropertyRequest +); + +NTSTATUS +PropertyHandler_A2dpHpOneShotDisconnect( + _In_ PPCPROPERTY_REQUEST PropertyRequest +); + +#endif // _SYSVAD_A2DPHPTOPO_H_ + diff --git a/audio/sysvad/EndpointsCommon/bthhfpmictopo.cpp b/audio/sysvad/EndpointsCommon/bthhfpmictopo.cpp index bcaaca94..1a74de8f 100644 --- a/audio/sysvad/EndpointsCommon/bthhfpmictopo.cpp +++ b/audio/sysvad/EndpointsCommon/bthhfpmictopo.cpp @@ -56,12 +56,6 @@ Return Value: // NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; - // - // This line shows how to get a pointer to the miniport topology object. - // - PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; - UNREFERENCED_VAR(pMiniport); - if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) { switch(PropertyRequest->PropertyItem->Id) @@ -220,12 +214,6 @@ Return Value: // MajorTarget is a pointer to miniport object for miniports. // NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; - - // - // This line shows how to get a pointer to the miniport topology object. - // - PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; - UNREFERENCED_VAR(pMiniport); if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Audio)) { diff --git a/audio/sysvad/EndpointsCommon/bthhfpminipairs.h b/audio/sysvad/EndpointsCommon/bthhfpminipairs.h index 1ec7bd5a..52c33c02 100644 --- a/audio/sysvad/EndpointsCommon/bthhfpminipairs.h +++ b/audio/sysvad/EndpointsCommon/bthhfpminipairs.h @@ -31,7 +31,7 @@ CreateMiniportWaveRTSYSVAD _Out_ PUNKNOWN *, _In_ REFCLSID, _In_opt_ PUNKNOWN, - _In_ POOL_TYPE, + _In_ POOL_FLAGS, _In_ PUNKNOWN, _In_opt_ PVOID, _In_ PENDPOINT_MINIPAIR @@ -43,7 +43,7 @@ CreateMiniportTopologySYSVAD _Out_ PUNKNOWN *, _In_ REFCLSID, _In_opt_ PUNKNOWN, - _In_ POOL_TYPE, + _In_ POOL_FLAGS, _In_ PUNKNOWN, _In_opt_ PVOID, _In_ PENDPOINT_MINIPAIR diff --git a/audio/sysvad/EndpointsCommon/bthhfpspeakertopo.cpp b/audio/sysvad/EndpointsCommon/bthhfpspeakertopo.cpp index 5672b342..c5f7b70c 100644 --- a/audio/sysvad/EndpointsCommon/bthhfpspeakertopo.cpp +++ b/audio/sysvad/EndpointsCommon/bthhfpspeakertopo.cpp @@ -56,12 +56,6 @@ Return Value: // NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; - // - // This line shows how to get a pointer to the miniport topology object. - // - PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; - UNREFERENCED_VAR(pMiniport); - if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) { switch(PropertyRequest->PropertyItem->Id) @@ -220,12 +214,6 @@ Return Value: // MajorTarget is a pointer to miniport object for miniports. // NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; - - // - // This line shows how to get a pointer to the miniport topology object. - // - PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; - UNREFERENCED_VAR(pMiniport); if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Audio)) { diff --git a/audio/sysvad/EndpointsCommon/micarraytopo.cpp b/audio/sysvad/EndpointsCommon/micarraytopo.cpp index 88431df1..1898243a 100644 --- a/audio/sysvad/EndpointsCommon/micarraytopo.cpp +++ b/audio/sysvad/EndpointsCommon/micarraytopo.cpp @@ -37,10 +37,7 @@ CreateMicArrayMiniportTopology _Out_ PUNKNOWN * Unknown, _In_ REFCLSID, _In_opt_ PUNKNOWN UnknownOuter, - _When_((PoolType & NonPagedPoolMustSucceed) != 0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) - _In_ POOL_TYPE PoolType, + _In_ POOL_FLAGS PoolFlags, _In_ PUNKNOWN UnknownAdapter, _In_opt_ PVOID DeviceContext, _In_ PENDPOINT_MINIPAIR MiniportPair @@ -82,7 +79,7 @@ Return Value: UNREFERENCED_PARAMETER(DeviceContext); CMicArrayMiniportTopology *obj = - new (PoolType, MINTOPORT_POOLTAG) + new (PoolFlags, MINTOPORT_POOLTAG) CMicArrayMiniportTopology( UnknownOuter, MiniportPair->TopoDescriptor, MiniportPair->DeviceMaxChannels, diff --git a/audio/sysvad/EndpointsCommon/micarraytopo.h b/audio/sysvad/EndpointsCommon/micarraytopo.h index 41f9a792..a4c2d8ae 100644 --- a/audio/sysvad/EndpointsCommon/micarraytopo.h +++ b/audio/sysvad/EndpointsCommon/micarraytopo.h @@ -26,6 +26,7 @@ Abstract: // CMicArrayMiniportTopology // +#pragma code_seg() class CMicArrayMiniportTopology : public CMiniportTopologySYSVAD, public IMiniportTopology, @@ -101,10 +102,7 @@ CreateMicArrayMiniportTopology( _Out_ PUNKNOWN * Unknown, _In_ REFCLSID, _In_opt_ PUNKNOWN UnknownOuter, - _When_((PoolType & NonPagedPoolMustSucceed) != 0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) - _In_ POOL_TYPE PoolType, + _In_ POOL_FLAGS PoolFlags, _In_ PUNKNOWN UnknownAdapter, _In_opt_ PVOID DeviceContext, _In_ PENDPOINT_MINIPAIR MiniportPair diff --git a/audio/sysvad/EndpointsCommon/mintopo.cpp b/audio/sysvad/EndpointsCommon/mintopo.cpp index 4e417c4a..c1e4bc14 100644 --- a/audio/sysvad/EndpointsCommon/mintopo.cpp +++ b/audio/sysvad/EndpointsCommon/mintopo.cpp @@ -27,10 +27,7 @@ CreateMiniportTopologySYSVAD _Out_ PUNKNOWN * Unknown, _In_ REFCLSID, _In_opt_ PUNKNOWN UnknownOuter, - _When_((PoolType & NonPagedPoolMustSucceed) != 0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) - _In_ POOL_TYPE PoolType, + _In_ POOL_FLAGS PoolFlags, _In_ PUNKNOWN UnknownAdapter, _In_opt_ PVOID DeviceContext, _In_ PENDPOINT_MINIPAIR MiniportPair @@ -69,7 +66,7 @@ Return Value: ASSERT(MiniportPair); CMiniportTopology *obj = - new (PoolType, MINWAVERT_POOLTAG) + new (PoolFlags, MINWAVERT_POOLTAG) CMiniportTopology( UnknownOuter, MiniportPair->TopoDescriptor, MiniportPair->DeviceMaxChannels, @@ -581,6 +578,266 @@ Return Value: return ntStatus; } +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopology::PropertyHandlerAudioResourceGroup +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_AudioResourceManagement, KSPROPERTY_AUDIORESOURCEMANAGEMENT_RESOURCEGROUP ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertryHandlerAudioResourceGroup]")); + + NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + status = PropertyHandler_SetAudioResourceGroup(PropertyRequest); + } + + return status; +} + +NTSTATUS +CMiniportTopology::PropertyHandler_SetAudioResourceGroup +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_SetAudioResourceGroup]")); + + NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; + + ULONG cbNeeded = sizeof(m_ResourceGroup); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + status = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + status = STATUS_BUFFER_TOO_SMALL; + } + else + { + PAUDIORESOURCEMANAGEMENT_RESOURCEGROUP pResourceGroup = (PAUDIORESOURCEMANAGEMENT_RESOURCEGROUP)PropertyRequest->Value; + + // When resource group is released, the resource group should be one that was previously assigned to the endpoint + // Check the cached resource group, make sure the release resource group and the cached resource group are the same, if not, return failure + if (!pResourceGroup->ResourceGroupAcquired && (wcscmp(pResourceGroup->ResourceGroupName, m_ResourceGroup.ResourceGroupName) != 0)) + { + status = STATUS_INVALID_DEVICE_REQUEST; + } + else + { + if (pResourceGroup->ResourceGroupAcquired) + { + RtlCopyMemory(&m_ResourceGroup, pResourceGroup, sizeof(m_ResourceGroup)); + } + else + { + m_ResourceGroup = {0}; + } + + DbgPrintEx(DPFLTR_IHVAUDIO_ID, DPFLTR_INFO_LEVEL, "AudioResourceGroup - SET Resource Group %S %S\n", pResourceGroup->ResourceGroupName, pResourceGroup->ResourceGroupAcquired ? L"acquired" : L"released"); + + status = STATUS_SUCCESS; + } + } + + return status; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopology::PropertyHandlerAudioPostureOrientation +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cAudioPostureInfos, + _In_reads_(cAudioPostureInfos) PSYSVAD_AUDIOPOSTURE_INFO *AudioPostureInfos +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_AudioPosture, KSPROPERTY_AUDIOPOSTURE_ORIENTATION ) + +Arguments: + + PropertyRequest - + cAudioPostureInfos - # of elements in the AudioPosture Infos array. + AudioPostureInfos - Array of AudioPosture Infos pointers. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerAudioPostureOrientation]")); + + NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cAudioPostureInfos) && (AudioPostureInfos[nPinId] != NULL) && (AudioPostureInfos[nPinId]->OrientationSupported)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + status = PropertyHandler_AudioPostureOrientationBasicSupport(PropertyRequest); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + status = PropertyHandler_SetAudioPostureOrientation(PropertyRequest); + } + } + } + + return status; +} + +NTSTATUS +CMiniportTopology::PropertyHandler_AudioPostureOrientationBasicSupport +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_AudioPostureOrientationBasicSupport]")); + + NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; + + ULONG ulExpectedSize = sizeof(KSPROPERTY_DESCRIPTION); + + if (PropertyRequest->ValueSize >= sizeof(KSPROPERTY_DESCRIPTION)) + { + // if return buffer can hold a KSPROPERTY_DESCRIPTION, return it + // + PKSPROPERTY_DESCRIPTION PropDesc = PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + + PropDesc->AccessFlags = KSPROPERTY_TYPE_BASICSUPPORT | + KSPROPERTY_TYPE_GET | + KSPROPERTY_TYPE_SET; + PropDesc->DescriptionSize = ulExpectedSize; + PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General; + PropDesc->PropTypeSet.Id = VT_UI4; + PropDesc->PropTypeSet.Flags = 0; + PropDesc->MembersListCount = 0; + PropDesc->Reserved = 0; + + // tell them how much space we really used, which controls how much data is copied into the user buffer. + PropertyRequest->ValueSize = ulExpectedSize; + + status = STATUS_SUCCESS; + } + else if (PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags. + *(PULONG(PropertyRequest->Value)) = KSPROPERTY_TYPE_BASICSUPPORT | + KSPROPERTY_TYPE_GET | + KSPROPERTY_TYPE_SET; + PropertyRequest->ValueSize = sizeof(ULONG); + status = STATUS_SUCCESS; + } + else if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = ulExpectedSize; + status = STATUS_BUFFER_OVERFLOW; + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + + return status; +} + +NTSTATUS +CMiniportTopology::PropertyHandler_SetAudioPostureOrientation +( + + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_SetAudioPostureOrientation]")); + + NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; + + ULONG cbNeeded = sizeof(AUDIOPOSTURE_ORIENTATION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + status = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + status = STATUS_BUFFER_TOO_SMALL; + } + else + { + AUDIOPOSTURE_ORIENTATION Orientation = *((AUDIOPOSTURE_ORIENTATION*)PropertyRequest->Value); + + // Validation + switch (Orientation) + { + case AUDIOPOSTURE_ORIENTATION_NOTROTATED: + case AUDIOPOSTURE_ORIENTATION_ROTATED90DEGREESCOUNTERCLOCKWISE: + case AUDIOPOSTURE_ORIENTATION_ROTATED180DEGREESCOUNTERCLOCKWISE: + case AUDIOPOSTURE_ORIENTATION_ROTATED270DEGREESCOUNTERCLOCKWISE: + DbgPrintEx(DPFLTR_IHVAUDIO_ID, DPFLTR_INFO_LEVEL, "AudioPosture - SET Orientation = %d\n", Orientation); + + m_PostureCache.Orientation = Orientation; + + status = STATUS_SUCCESS; + break; + default: + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + } + + return status; +} #if defined(SYSVAD_BTH_BYPASS) || defined(SYSVAD_USB_SIDEBAND) //============================================================================= diff --git a/audio/sysvad/EndpointsCommon/mintopo.h b/audio/sysvad/EndpointsCommon/mintopo.h index b4eda617..596e13b1 100644 --- a/audio/sysvad/EndpointsCommon/mintopo.h +++ b/audio/sysvad/EndpointsCommon/mintopo.h @@ -26,6 +26,7 @@ Abstract: // CMiniportTopology // +#pragma code_seg() class CMiniportTopology : public CMiniportTopologySYSVAD, public IMiniportTopology, @@ -34,6 +35,15 @@ class CMiniportTopology : private: eDeviceType m_DeviceType; + typedef struct + { + AUDIOPOSTURE_ORIENTATION Orientation; + }SYSVAD_AUDIOPOSTURE; + + // Assuming that Each filter will have only one endpoint supporting posture + SYSVAD_AUDIOPOSTURE m_PostureCache; + + AUDIORESOURCEMANAGEMENT_RESOURCEGROUP m_ResourceGroup {0}; union { PVOID m_DeviceContext; @@ -57,6 +67,7 @@ public: m_DeviceType(DeviceType), m_DeviceContext(DeviceContext) { + m_PostureCache.Orientation = AUDIOPOSTURE_ORIENTATION_NOTROTATED; #if defined(SYSVAD_BTH_BYPASS) || defined(SYSVAD_USB_SIDEBAND) if (IsSidebandDevice()) @@ -89,6 +100,32 @@ public: _In_ DWORD JackCapabilities ); + NTSTATUS PropertyHandlerAudioResourceGroup + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandler_SetAudioResourceGroup + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerAudioPostureOrientation + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cAudioPostureInfos, + _In_reads_(cAudioPostureInfos) PSYSVAD_AUDIOPOSTURE_INFO *AudioPostureInfos + ); + + NTSTATUS PropertyHandler_AudioPostureOrientationBasicSupport + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandler_SetAudioPostureOrientation + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); #if defined(SYSVAD_BTH_BYPASS) || defined(SYSVAD_USB_SIDEBAND) BOOL IsSidebandDevice() diff --git a/audio/sysvad/EndpointsCommon/minwavert.cpp b/audio/sysvad/EndpointsCommon/minwavert.cpp index 4cabc7c4..bd81c110 100644 --- a/audio/sysvad/EndpointsCommon/minwavert.cpp +++ b/audio/sysvad/EndpointsCommon/minwavert.cpp @@ -40,10 +40,7 @@ CreateMiniportWaveRTSYSVAD _Out_ PUNKNOWN * Unknown, _In_ REFCLSID, _In_opt_ PUNKNOWN UnknownOuter, - _When_((PoolType & NonPagedPoolMustSucceed) != 0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) - _In_ POOL_TYPE PoolType, + _In_ POOL_FLAGS PoolFlags, _In_ PUNKNOWN UnknownAdapter, _In_opt_ PVOID DeviceContext, _In_ PENDPOINT_MINIPAIR MiniportPair @@ -83,7 +80,7 @@ Return Value: ASSERT(Unknown); ASSERT(MiniportPair); - CMiniportWaveRT *obj = new (PoolType, MINWAVERT_POOLTAG) CMiniportWaveRT + CMiniportWaveRT *obj = new (PoolFlags, MINWAVERT_POOLTAG) CMiniportWaveRT ( UnknownAdapter, MiniportPair, @@ -426,12 +423,11 @@ Return Value: // Module list size. // size = cModules * sizeof(AUDIOMODULE); - m_pAudioModules = (AUDIOMODULE *)ExAllocatePoolWithTag(NonPagedPoolNx, size, MINWAVERT_POOLTAG); + m_pAudioModules = (AUDIOMODULE *)ExAllocatePool2(POOL_FLAG_NON_PAGED, size, MINWAVERT_POOLTAG); if (m_pAudioModules == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(m_pAudioModules, size); for (ULONG i=0; i<cModules; ++i) { @@ -451,13 +447,12 @@ Return Value: if (size) { m_pAudioModules[i].Context = - ExAllocatePoolWithTag(NonPagedPoolNx, size, MINWAVERT_POOLTAG); + ExAllocatePool2(POOL_FLAG_NON_PAGED, size, MINWAVERT_POOLTAG); if (m_pAudioModules[i].Context == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(m_pAudioModules[i].Context, size); } @@ -494,12 +489,11 @@ Return Value: // System streams. size = sizeof(PCMiniportWaveRTStream) * m_ulMaxSystemStreams; - m_SystemStreams = (PCMiniportWaveRTStream *)ExAllocatePoolWithTag(NonPagedPoolNx, size, MINWAVERT_POOLTAG); + m_SystemStreams = (PCMiniportWaveRTStream *)ExAllocatePool2(POOL_FLAG_NON_PAGED, size, MINWAVERT_POOLTAG); if (m_SystemStreams == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(m_SystemStreams, size); if (IsLoopbackSupported()) { @@ -510,12 +504,11 @@ Return Value: // Loopback streams. size = sizeof(PCMiniportWaveRTStream) * m_ulMaxLoopbackStreams; - m_LoopbackStreams = (PCMiniportWaveRTStream *)ExAllocatePoolWithTag(NonPagedPoolNx, size, MINWAVERT_POOLTAG); + m_LoopbackStreams = (PCMiniportWaveRTStream *)ExAllocatePool2(POOL_FLAG_NON_PAGED, size, MINWAVERT_POOLTAG); if (m_LoopbackStreams == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(m_LoopbackStreams, size); } if (IsOffloadSupported()) @@ -530,15 +523,14 @@ Return Value: // Offload streams. size = sizeof(PCMiniportWaveRTStream) * m_ulMaxOffloadStreams; - m_OffloadStreams = (PCMiniportWaveRTStream *)ExAllocatePoolWithTag(NonPagedPoolNx, size, MINWAVERT_POOLTAG); + m_OffloadStreams = (PCMiniportWaveRTStream *)ExAllocatePool2(POOL_FLAG_NON_PAGED, size, MINWAVERT_POOLTAG); if (m_OffloadStreams == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(m_OffloadStreams, size); // Formats. - m_pDeviceFormat = (PKSDATAFORMAT_WAVEFORMATEXTENSIBLE)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), MINWAVERT_POOLTAG); + m_pDeviceFormat = (PKSDATAFORMAT_WAVEFORMATEXTENSIBLE)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), MINWAVERT_POOLTAG); if (m_pDeviceFormat == NULL) { return STATUS_INSUFFICIENT_RESOURCES; @@ -552,7 +544,7 @@ Return Value: } RtlCopyMemory((PVOID)(m_pDeviceFormat), (PVOID)(pDeviceFormats), sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE)); - m_pMixFormat = (PKSDATAFORMAT_WAVEFORMATEXTENSIBLE)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), MINWAVERT_POOLTAG); + m_pMixFormat = (PKSDATAFORMAT_WAVEFORMATEXTENSIBLE)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), MINWAVERT_POOLTAG); if (m_pMixFormat == NULL) { return STATUS_INSUFFICIENT_RESOURCES; @@ -577,26 +569,23 @@ Return Value: m_bGfxEnabled = FALSE; - m_pbMuted = (PBOOL)ExAllocatePoolWithTag(NonPagedPoolNx, m_DeviceMaxChannels * sizeof(BOOL), MINWAVERT_POOLTAG); + m_pbMuted = (PBOOL)ExAllocatePool2(POOL_FLAG_NON_PAGED, m_DeviceMaxChannels * sizeof(BOOL), MINWAVERT_POOLTAG); if (m_pbMuted == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(m_pbMuted, m_DeviceMaxChannels * sizeof(BOOL)); - m_plVolumeLevel = (PLONG)ExAllocatePoolWithTag(NonPagedPoolNx, m_DeviceMaxChannels * sizeof(LONG), MINWAVERT_POOLTAG); + m_plVolumeLevel = (PLONG)ExAllocatePool2(POOL_FLAG_NON_PAGED, m_DeviceMaxChannels * sizeof(LONG), MINWAVERT_POOLTAG); if (m_plVolumeLevel == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(m_plVolumeLevel, m_DeviceMaxChannels * sizeof(LONG)); - m_plPeakMeter = (PLONG)ExAllocatePoolWithTag(NonPagedPoolNx, m_DeviceMaxChannels * sizeof(LONG), MINWAVERT_POOLTAG); + m_plPeakMeter = (PLONG)ExAllocatePool2(POOL_FLAG_NON_PAGED, m_DeviceMaxChannels * sizeof(LONG), MINWAVERT_POOLTAG); if (m_plPeakMeter == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(m_plPeakMeter, m_DeviceMaxChannels * sizeof(LONG)); } // @@ -717,7 +706,7 @@ Return Value: // if (NT_SUCCESS(ntStatus)) { - stream = new (NonPagedPoolNx, MINWAVERT_POOLTAG) + stream = new (POOL_FLAG_NON_PAGED, MINWAVERT_POOLTAG) CMiniportWaveRTStream(NULL); if (stream) @@ -1845,8 +1834,6 @@ CMiniportWaveRT::SendPNPNotification( NTSTATUS status = STATUS_SUCCESS; PPCNOTIFICATION_BUFFER buffer = NULL; - PAGED_CODE(); - DPF_ENTER(("[SendPNPNotification]")); // Allocate a notification buffer. @@ -2383,7 +2370,7 @@ Return Value: return STATUS_UNSUCCESSFUL; } - ulContentIds = new (NonPagedPoolNx, MINWAVERT_POOLTAG) ULONG[m_ulMaxSystemStreams + m_ulMaxOffloadStreams]; + ulContentIds = new (POOL_FLAG_NON_PAGED, MINWAVERT_POOLTAG) ULONG[m_ulMaxSystemStreams + m_ulMaxOffloadStreams]; if (!ulContentIds) { return STATUS_INSUFFICIENT_RESOURCES; @@ -2541,13 +2528,12 @@ CMiniportWaveRT::AllocStreamAudioModules // size = cModules * sizeof(AUDIOMODULE); #pragma prefast(suppress:__WARNING_MEMORY_LEAK,"No leaking, stream obj dtor calls FreeStreamAudioModules") - pAudioModules = (AUDIOMODULE *)ExAllocatePoolWithTag(NonPagedPoolNx, size, MINWAVERT_POOLTAG); + pAudioModules = (AUDIOMODULE *)ExAllocatePool2(POOL_FLAG_NON_PAGED, size, MINWAVERT_POOLTAG); if (pAudioModules == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; goto exit; } - RtlZeroMemory(pAudioModules, size); for (i=0, j=0; i<GetAudioModuleListCount() && j<cModules; ++i) { @@ -2588,14 +2574,13 @@ CMiniportWaveRT::AllocStreamAudioModules { #pragma prefast(suppress:__WARNING_MEMORY_LEAK,"No leaking, stream obj dtor calls FreeStreamAudioModules") pAudioModules[j].Context = - ExAllocatePoolWithTag(NonPagedPoolNx, size, MINWAVERT_POOLTAG); + ExAllocatePool2(POOL_FLAG_NON_PAGED, size, MINWAVERT_POOLTAG); if (pAudioModules[j].Context == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; goto exit; } - RtlZeroMemory(pAudioModules[j].Context, size); } // @@ -3482,14 +3467,12 @@ NTSTATUS CKeywordDetector::ReadKeywordTimestampRegistry() parametersPath.MaximumLength = g_RegistryPath.Length + sizeof(L"\\Parameters") + sizeof(WCHAR); - parametersPath.Buffer = (PWCH)ExAllocatePoolWithTag(PagedPool, parametersPath.MaximumLength, MINWAVERT_POOLTAG); + parametersPath.Buffer = (PWCH)ExAllocatePool2(POOL_FLAG_PAGED, parametersPath.MaximumLength, MINWAVERT_POOLTAG); if (parametersPath.Buffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(parametersPath.Buffer, parametersPath.MaximumLength); - RtlAppendUnicodeToString(¶metersPath, g_RegistryPath.Buffer); RtlAppendUnicodeToString(¶metersPath, L"\\Parameters"); @@ -3861,6 +3844,7 @@ VOID CKeywordDetector::DpcRoutine(_In_ LONGLONG PerformanceCounter, _In_ LONGLON RtlZeroMemory(&packetEntry->Samples[0], sizeof(packetEntry->Samples)); + InitializeListHead(packetListEntry); ExInterlockedInsertTailList(&PacketFifoHead, packetListEntry, &PacketFifoSpinLock); packetsToQueue -= 1; diff --git a/audio/sysvad/EndpointsCommon/minwavert.h b/audio/sysvad/EndpointsCommon/minwavert.h index 00534650..42cad965 100644 --- a/audio/sysvad/EndpointsCommon/minwavert.h +++ b/audio/sysvad/EndpointsCommon/minwavert.h @@ -127,6 +127,7 @@ private: /////////////////////////////////////////////////////////////////////////////// // CMiniportWaveRT // +#pragma code_seg() class CMiniportWaveRT : public IMiniportWaveRT, public IMiniportAudioEngineNode, @@ -454,6 +455,12 @@ public: _In_ PPCPROPERTY_REQUEST PropertyRequest ); #endif // SYSVAD_USB_SIDEBAND +#ifdef SYSVAD_A2DP_SIDEBAND + NTSTATUS PropertyHandler_A2dpHpAudioEffectsDiscoveryEffectsList + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); +#endif // SYSVAD_A2DP_SIDEBAND //--------------------------------------------------------------------------------------------------------- // volume diff --git a/audio/sysvad/EndpointsCommon/minwavertstream.cpp b/audio/sysvad/EndpointsCommon/minwavertstream.cpp index 499d5b38..08467e05 100644 --- a/audio/sysvad/EndpointsCommon/minwavertstream.cpp +++ b/audio/sysvad/EndpointsCommon/minwavertstream.cpp @@ -118,6 +118,8 @@ Return Value: NTSTATUS CMiniportWaveRTStream::ReadRegistrySettings() { + PAGED_CODE(); + NTSTATUS ntStatus; UNICODE_STRING parametersPath; @@ -140,14 +142,12 @@ NTSTATUS CMiniportWaveRTStream::ReadRegistrySettings() parametersPath.MaximumLength = g_RegistryPath.Length + sizeof(L"\\Parameters") + sizeof(WCHAR); - parametersPath.Buffer = (PWCH)ExAllocatePoolWithTag(PagedPool, parametersPath.MaximumLength, MINWAVERT_POOLTAG); + parametersPath.Buffer = (PWCH)ExAllocatePool2(POOL_FLAG_PAGED, parametersPath.MaximumLength, MINWAVERT_POOLTAG); if (parametersPath.Buffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(parametersPath.Buffer, parametersPath.MaximumLength); - RtlAppendUnicodeToString(¶metersPath, g_RegistryPath.Buffer); RtlAppendUnicodeToString(¶metersPath, L"\\Parameters"); @@ -294,39 +294,36 @@ Return Value: m_bCapture = Capture_; m_ulDmaMovementRate = pWfEx->nAvgBytesPerSec; - m_pDpc = (PRKDPC)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(KDPC), MINWAVERTSTREAM_POOLTAG); + m_pDpc = (PRKDPC)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(KDPC), MINWAVERTSTREAM_POOLTAG); if (!m_pDpc) { return STATUS_INSUFFICIENT_RESOURCES; } - m_pWfExt = (PWAVEFORMATEXTENSIBLE)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(WAVEFORMATEX) + pWfEx->cbSize, MINWAVERTSTREAM_POOLTAG); + m_pWfExt = (PWAVEFORMATEXTENSIBLE)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(WAVEFORMATEX) + pWfEx->cbSize, MINWAVERTSTREAM_POOLTAG); if (m_pWfExt == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyMemory(m_pWfExt, pWfEx, sizeof(WAVEFORMATEX) + pWfEx->cbSize); - m_pbMuted = (PBOOL)ExAllocatePoolWithTag(NonPagedPoolNx, m_pWfExt->Format.nChannels * sizeof(BOOL), MINWAVERTSTREAM_POOLTAG); + m_pbMuted = (PBOOL)ExAllocatePool2(POOL_FLAG_NON_PAGED, m_pWfExt->Format.nChannels * sizeof(BOOL), MINWAVERTSTREAM_POOLTAG); if (m_pbMuted == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(m_pbMuted, m_pWfExt->Format.nChannels * sizeof(BOOL)); - m_plVolumeLevel = (PLONG)ExAllocatePoolWithTag(NonPagedPoolNx, m_pWfExt->Format.nChannels * sizeof(LONG), MINWAVERTSTREAM_POOLTAG); + m_plVolumeLevel = (PLONG)ExAllocatePool2(POOL_FLAG_NON_PAGED, m_pWfExt->Format.nChannels * sizeof(LONG), MINWAVERTSTREAM_POOLTAG); if (m_plVolumeLevel == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(m_plVolumeLevel, m_pWfExt->Format.nChannels * sizeof(LONG)); - m_plPeakMeter = (PLONG)ExAllocatePoolWithTag(NonPagedPoolNx, m_pWfExt->Format.nChannels * sizeof(LONG), MINWAVERTSTREAM_POOLTAG); + m_plPeakMeter = (PLONG)ExAllocatePool2(POOL_FLAG_NON_PAGED, m_pWfExt->Format.nChannels * sizeof(LONG), MINWAVERTSTREAM_POOLTAG); if (m_plPeakMeter == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(m_plPeakMeter, m_pWfExt->Format.nChannels * sizeof(LONG)); // // Allocate stream audio module resources. @@ -639,8 +636,8 @@ NTSTATUS CMiniportWaveRTStream::RegisterNotificationEvent PAGED_CODE(); - NotificationListEntry *nleNew = (NotificationListEntry*)ExAllocatePoolWithTag( - NonPagedPoolNx, + NotificationListEntry *nleNew = (NotificationListEntry*)ExAllocatePool2( + POOL_FLAG_NON_PAGED, sizeof(NotificationListEntry), MINWAVERTSTREAM_POOLTAG); if (NULL == nleNew) diff --git a/audio/sysvad/EndpointsCommon/speakerhptopo.cpp b/audio/sysvad/EndpointsCommon/speakerhptopo.cpp index 76b82f13..1cd1d97b 100644 --- a/audio/sysvad/EndpointsCommon/speakerhptopo.cpp +++ b/audio/sysvad/EndpointsCommon/speakerhptopo.cpp @@ -245,16 +245,11 @@ Return Value: DPF_ENTER(("[PropertyHandler_SpeakerHpTopoFilter]")); - // PropertryRequest structure is filled by portcls. + // PropertyRequest structure is filled by portcls. // MajorTarget is a pointer to miniport object for miniports. // NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; - - // - // This line shows how to get a pointer to the miniport topology object. - // PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; - UNREFERENCED_VAR(pMiniport); if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) { @@ -267,6 +262,13 @@ Return Value: ntStatus = PropertyHandler_SpeakerHpJackDescription2(PropertyRequest); } } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_AudioResourceManagement)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIORESOURCEMANAGEMENT_RESOURCEGROUP) + { + ntStatus = pMiniport->PropertyHandlerAudioResourceGroup(PropertyRequest); + } + } return ntStatus; } // PropertyHandler_SpeakerHpTopoFilter diff --git a/audio/sysvad/EndpointsCommon/speakerhptoptable.h b/audio/sysvad/EndpointsCommon/speakerhptoptable.h index 3a851dce..936ec0b6 100644 --- a/audio/sysvad/EndpointsCommon/speakerhptoptable.h +++ b/audio/sysvad/EndpointsCommon/speakerhptoptable.h @@ -131,6 +131,12 @@ PCPROPERTY_ITEM PropertiesSpeakerHpTopoFilter[] = KSPROPERTY_JACK_DESCRIPTION2, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_SpeakerHpTopoFilter + }, + { + &KSPROPSETID_AudioResourceManagement, + KSPROPERTY_AUDIORESOURCEMANAGEMENT_RESOURCEGROUP, + KSPROPERTY_TYPE_SET, + PropertyHandler_SpeakerHpTopoFilter } }; diff --git a/audio/sysvad/EndpointsCommon/speakertopo.cpp b/audio/sysvad/EndpointsCommon/speakertopo.cpp index fee3d542..fba72036 100644 --- a/audio/sysvad/EndpointsCommon/speakertopo.cpp +++ b/audio/sysvad/EndpointsCommon/speakertopo.cpp @@ -50,7 +50,7 @@ Return Value: DPF_ENTER(("[PropertyHandler_SpeakerTopoFilter]")); - // PropertryRequest structure is filled by portcls. + // PropertyRequest structure is filled by portcls. // MajorTarget is a pointer to miniport object for miniports. // NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; @@ -76,6 +76,24 @@ Return Value: ); } } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_AudioResourceManagement)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIORESOURCEMANAGEMENT_RESOURCEGROUP) + { + ntStatus = pMiniport->PropertyHandlerAudioResourceGroup(PropertyRequest); + } + } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_AudioPosture)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIOPOSTURE_ORIENTATION) + { + ntStatus = pMiniport->PropertyHandlerAudioPostureOrientation( + PropertyRequest, + ARRAYSIZE(SpeakerAudioPostureInfoPointers), + SpeakerAudioPostureInfoPointers + ); + } + } return ntStatus; } // PropertyHandler_SpeakerTopoFilter diff --git a/audio/sysvad/EndpointsCommon/speakertoptable.h b/audio/sysvad/EndpointsCommon/speakertoptable.h index 6850c0cf..3d31d7a4 100644 --- a/audio/sysvad/EndpointsCommon/speakertoptable.h +++ b/audio/sysvad/EndpointsCommon/speakertoptable.h @@ -104,6 +104,15 @@ PKSJACK_DESCRIPTION SpeakerJackDescriptions[] = &SpeakerJackDescBridge }; +static SYSVAD_AUDIOPOSTURE_INFO SpeakerAudioPostureInfo = { TRUE }; + +// Only support property for the physical bridge pin. +static +PSYSVAD_AUDIOPOSTURE_INFO SpeakerAudioPostureInfoPointers[] +{ + NULL, + &SpeakerAudioPostureInfo +}; //============================================================================= static @@ -131,6 +140,19 @@ PCPROPERTY_ITEM PropertiesSpeakerTopoFilter[] = KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_SpeakerTopoFilter + }, + { + &KSPROPSETID_AudioResourceManagement, + KSPROPERTY_AUDIORESOURCEMANAGEMENT_RESOURCEGROUP, + KSPROPERTY_TYPE_SET, + PropertyHandler_SpeakerTopoFilter + } + ,{ + &KSPROPSETID_AudioPosture, + KSPROPERTY_AUDIOPOSTURE_ORIENTATION, + KSPROPERTY_TYPE_SET | + KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpeakerTopoFilter } }; diff --git a/audio/sysvad/EndpointsCommon/usbhsmictopo.cpp b/audio/sysvad/EndpointsCommon/usbhsmictopo.cpp index 6814b5ce..0be275f3 100644 --- a/audio/sysvad/EndpointsCommon/usbhsmictopo.cpp +++ b/audio/sysvad/EndpointsCommon/usbhsmictopo.cpp @@ -56,12 +56,6 @@ Return Value: // NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; - // - // This line shows how to get a pointer to the miniport topology object. - // - PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; - UNREFERENCED_VAR(pMiniport); - if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) { switch(PropertyRequest->PropertyItem->Id) @@ -286,12 +280,6 @@ Return Value: // MajorTarget is a pointer to miniport object for miniports. // NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; - - // - // This line shows how to get a pointer to the miniport topology object. - // - PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; - UNREFERENCED_VAR(pMiniport); if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Audio)) { diff --git a/audio/sysvad/EndpointsCommon/usbhsminipairs.h b/audio/sysvad/EndpointsCommon/usbhsminipairs.h index 0b71bce9..76423240 100644 --- a/audio/sysvad/EndpointsCommon/usbhsminipairs.h +++ b/audio/sysvad/EndpointsCommon/usbhsminipairs.h @@ -29,7 +29,7 @@ CreateMiniportWaveRTSYSVAD _Out_ PUNKNOWN *, _In_ REFCLSID, _In_opt_ PUNKNOWN, - _In_ POOL_TYPE, + _In_ POOL_FLAGS, _In_ PUNKNOWN, _In_opt_ PVOID, _In_ PENDPOINT_MINIPAIR @@ -41,7 +41,7 @@ CreateMiniportTopologySYSVAD _Out_ PUNKNOWN *, _In_ REFCLSID, _In_opt_ PUNKNOWN, - _In_ POOL_TYPE, + _In_ POOL_FLAGS, _In_ PUNKNOWN, _In_opt_ PVOID, _In_ PENDPOINT_MINIPAIR diff --git a/audio/sysvad/EndpointsCommon/usbhsspeakertopo.cpp b/audio/sysvad/EndpointsCommon/usbhsspeakertopo.cpp index 21d5fa8c..e6899e36 100644 --- a/audio/sysvad/EndpointsCommon/usbhsspeakertopo.cpp +++ b/audio/sysvad/EndpointsCommon/usbhsspeakertopo.cpp @@ -56,12 +56,6 @@ Return Value: // NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; - // - // This line shows how to get a pointer to the miniport topology object. - // - PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; - UNREFERENCED_VAR(pMiniport); - if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) { switch(PropertyRequest->PropertyItem->Id) diff --git a/audio/sysvad/Package/package.VcxProj b/audio/sysvad/Package/package.VcxProj index aedc7dc4..0d3bb44a 100644 --- a/audio/sysvad/Package/package.VcxProj +++ b/audio/sysvad/Package/package.VcxProj @@ -39,6 +39,9 @@ <ProjectReference Include="..\APO\KWSApo\KwsAPO.vcxproj"> <Project>{47358AD6-A48A-465B-965F-0A66F8BDFE23}</Project> </ProjectReference> + <ProjectReference Include="..\APO\AecApo\AecAPO.vcxproj"> + <Project>{83A1B9B0-3CB7-4249-ADEC-C336B0EDDE76}</Project> + </ProjectReference> <ProjectReference Include="..\TabletAudioSample\TabletAudioSample.vcxproj"> <Project>{E4DF0EEE-D35B-47F2-A9B1-41EA97C465FF}</Project> </ProjectReference> diff --git a/audio/sysvad/README.md b/audio/sysvad/README.md index 3a799ba2..76ef8be2 100644 --- a/audio/sysvad/README.md +++ b/audio/sysvad/README.md @@ -11,9 +11,8 @@ urlFragment: sysvad-virtual-audio-device-driver-sample # SysVAD Virtual Audio Device Driver Sample -## Important note - -With the [end of support for Windows 10 Mobile](https://support.microsoft.com/help/4484693/windows-10-mobile-end-of-support), the Phone Audio sample has been also removed from the SysVAD driver sample. +> [!NOTE] +> With the [end of support for Windows 10 Mobile](https://support.microsoft.com/help/4484693/windows-10-mobile-end-of-support), the Phone Audio sample has been also removed from the SysVAD driver sample. ## Introduction @@ -23,9 +22,9 @@ Some of these audio devices are embedded in the system (for example, speakers, m Driver developers can use the framework in this sample to provide support for various audio devices without concern for hardware dependencies. The framework includes implementations of the following interfaces: -- The CAdapterCommon interface gives the miniports access to virtual mixer hardware. It also implements the **IAdapterPowerManagement** interface. +- The *CAdapterCommon* interface gives the miniports access to virtual mixer hardware. It also implements the *IAdapterPowerManagement* interface. -- The CMiniportTopologySYSVAD interface is the base class for all sample topologies. It has very basic common functions. In addition, this class contains common topology property handlers. +- The *CMiniportTopologySYSVAD* interface is the base class for all sample topologies. It has very basic common functions. In addition, this class contains common topology property handlers. The following table shows the features that are implemented in the various subdirectories of this sample. @@ -36,35 +35,63 @@ The following table shows the features that are implemented in the various subdi | SwapAPO | Sample APO that installs onto endpoints exposed by the SysVAD sample driver and swaps the left and right channels. | | DelayAPO | Sample APO that adds a delay to the input samples. | | KwsAPO | Sample APO that uses KSPROPERTY_INTERLEAVEDAUDIO_FORMATINFORMATION to determine if the keyword spotter pin is interleaving loopback audio with the microphone audio and identify which channels contain loopback audio. If it is interleaved the APO will strip out the loopback audio and deliver only the microphone audio upstream. Because channel data is removed, the APO negotiates an output format which is different than the input format. | +| AecAPO | Sample capture MFX APO that implements Acoustic Echo Cancellation. This APO demonstrates the use of echo cancellation interfaces that an AEC APO uses to obtain reference audio for cancellation. | | KeywordDetectorAdapter | Sample Keyword Detector Adapter. | For more information about the Windows audio engine, see [Hardware-Offloaded Audio Processing](https://docs.microsoft.com/windows-hardware/drivers/audio/hardware-offloaded-audio-processing), and note that audio hardware that is offload-capable replicates the architecture that is presented in the diagram shown in the topic. ## Build the sample -If you simply want to Build this sample driver and don't intend to run or test it, then you do not need a target computer (also called a test computer). If, however, you would like to deploy, run and test this sample driver, then you need a second computer that will serve as your target computer. Instructions are provided in the **Run the sample** section to show you how to set up the target computer - also referred to as *provisioning* a target computer. +If you simply want to build this sample driver and don't intend to run or test it, then you do not need a target computer (also called a test computer). If, however, you would like to deploy, run and test this sample driver, then you need a second computer that will serve as your target computer. Instructions are provided in [Run the sample](#run-the-sample) to show you how to set up the target computer - also referred to as *provisioning* a target computer. Perform the following steps to build this sample driver. -### 1. Open the driver solution in Visual Studio +### Setup the development computer + +For building the sample driver, the following components must be installed: +- Visual Studio, +- Windows SDK, +- Windows Driver Kit (WDK). + +Please refer to [Download the Windows Driver Kit](https://docs.microsoft.com/windows-hardware/drivers/download-the-wdk) for instructions on how to download and install each one. + +From the Visual Studio Installer, the following individual packages must be installed in order to build SysVAD: +- C++ ATL for latest v142 build tools \<arch\> +- C++ ATL for latest v142 build tools with Spectre Mitigations \<arch\> +- MSVC v142 - VS 2019 C++ \<arch\> build tools (Latest) +- MSVC v142 - VS 2019 C++ \<arch\> build tools Spectre-mitigated libs (Latest) + +Where "\<arch\>" refers to your target architecture. + +#### Configure the repository for Windows Implementation Library (WIL) + +In order to build the project, it is necessary to include WIL. The Windows Driver Samples repository includes WIL as a Git submodule. + +If it has not been done before (such as in a fresh clone), the following command should be run at the root of the repository: + +`git submodule update --init` + +This will populate a folder named *wil* at the root of the repository. + +### Open the driver solution in Visual Studio -In Microsoft Visual Studio, Click **File** \> **Open** \> **Project/Solution...** and navigate to the folder that contains the sample files (for example, *C:\Windows-driver-samples\audio\sysvad*). Double-click the *sysvad* solution file. +In Microsoft Visual Studio, click *File* \> *Open* \> *Project/Solution...* and navigate to the folder that contains the sample files (for example, *C:\Windows-driver-samples\audio\sysvad*). Double-click the *sysvad* solution file. -In Visual Studio locate the Solution Explorer. (If this is not already open, choose **Solution Explorer** from the **View** menu.) In Solution Explorer, you can see one solution that has seven projects. +In Visual Studio locate the Solution Explorer. (If this is not already open, choose *Solution Explorer* from the *View* menu.) In Solution Explorer, you can see one solution that has eight projects. -### 2. Set the sample's configuration and platform +### Set the sample's configuration and platform -In Solution Explorer, right-click **Solution 'sysvad' (7 of 7 projects)**, and choose **Configuration Manager**. Make sure that the configuration and platform settings are the same for the seven projects. By default, the configuration is set to **Debug**, and the platform is set to **Win32** for all the projects. If you make any configuration and/or platform changes for one project, you must make the same changes for all the remaining projects. +Right-click *Solution 'sysvad' (8 of 8 projects)* once more and choose *Configuration Manager*. Make sure that the configuration and platform settings are the same for the eight projects. By default, the configuration is set to *Debug*, and the platform is set to *Win32* for all the projects. If you make any configuration and/or platform changes for one project, you must make the same changes for all the remaining projects. -### 3. Build the sample using Visual Studio +### Build the sample using Visual Studio -In Visual Studio, click **Build** \> **Build Solution**. +In Visual Studio, click *Build* \> *Build Solution*. -### 4. Locate the built driver package +### Locate the built driver package In File Explorer, navigate to the folder that contains the sample files. For example, you would navigate to *C:\\Windows-driver-samples\\audio\\sysvad*, if that's the folder you specified in the preceding Step 1. -In the folder, the location of the driver package varies depending on the configuration and platform settings that you selected in the **Configuration Manager**. For example, if you left the default settings unchanged, then the built driver package will be saved to a folder named *Debug* inside the same folder as the sample files. Double-click the folder for the built driver package, and then double-click the folder named *package*. +In the folder, the location of the driver package varies depending on the configuration and platform settings that you selected in the *Configuration Manager*. For example, if you left the default settings unchanged, then the built driver package will be saved to a folder named *Debug* inside the same folder as the sample files. Double-click the folder for the built driver package, and then double-click the folder named *package*. The package should contain these files: @@ -73,6 +100,7 @@ The package should contain these files: | TabletAudioSample.sys | The driver file. | | DelayAPO.dll | The delay APO. | | KeywordDetectorContosoAdapter.dll | Sample Keyword detector adapter. | +| AecApo.dll | The AEC APO. | | KWSApo.dll | The KWS APO. | | SwapAPO.dll | The swap APO. | | sysvad.cat | A signed catalog file, which serves as the signature for the entire package. | @@ -89,7 +117,7 @@ The computer where you install the driver is called the *target computer* or the The process of moving the driver package to the target computer and installing the driver is called *deploying* the driver. You can deploy the TabletAudioSample sample driver automatically or manually. -### Prepare the target computer +### Setup the target computer First of all, install the latest [Windows Driver Kit](https://docs.microsoft.com/windows-hardware/drivers/download-the-wdk) (WDK) on the target computer. @@ -114,7 +142,7 @@ Copy *devcon.exe* to a folder on the target computer where it is easier to find. Create a folder on the target for the built driver package (for example, *C:\\SysvadDriver*). Copy all the files from the built driver package on the host computer and save them to the folder that you created on the target computer. -Create a folder on the target computer for the certificate created by the build process. For example, you could create a folder named *C:\\Certificates* on the target computer, and then copy *package.cer* to it from the host computer. You can find this certificate in the same folder on the host computer, as the *package* folder that contains the built driver files. On the target computer, right-click the certificate file, and click **Install**, then follow the prompts to install the test certificate. +Create a folder on the target computer for the certificate created by the build process. For example, you could create a folder named *C:\\Certificates* on the target computer, and then copy *package.cer* to it from the host computer. You can find this certificate in the same folder on the host computer, as the *package* folder that contains the built driver files. On the target computer, right-click the certificate file, and click *Install*, then follow the prompts to install the test certificate. If you need more detailed instructions for setting up the target computer, see [Preparing a Computer for Manual Driver Deployment](https://docs.microsoft.com/windows-hardware/drivers/develop/preparing-a-computer-for-manual-driver-deployment). @@ -164,15 +192,15 @@ Componentized driver packages are required since Windows 10 1809 release. Howeve On the target computer, open a Command Prompt window as Administrator. Navigate to your driver package folder, and enter the following command: -`devcon dp_add TabletAudioSample.inf` +`devcon install TabletAudioSample.inf Root\Sysvad_TabletAudioSample` After successfully installing the sample driver, you're now ready to test it. ### Test the driver -On the target computer, in a Command Prompt window, enter **devmgmt.msc** to open Device Manager. In Device Manager, on the **View** menu, choose **Devices by type**. In the device tree, locate *SYSVAD (with APO Extensions)*. This is typically under the **Sound, video and game controllers** node. +On the target computer, in a Command Prompt window, enter *devmgmt.msc* to open Device Manager. In Device Manager, on the *View* menu, choose *Devices by type*. In the device tree, locate *SYSVAD (with APO Extensions)*. This is typically under the *Sound, video and game controllers* node. -On the target computer, open Control Panel and navigate to **Hardware and Sound** \> **Manage audio devices**. In the Sound dialog box, select the speaker icon labeled as *SYSVAD (with APO Extensions)*, then click **Set Default**, but do not click **OK**. This will keep the Sound dialog box open. +On the target computer, open Control Panel and navigate to *Hardware and Sound* \> *Manage audio devices*. In the Sound dialog box, select the speaker icon labeled as *SYSVAD (with APO Extensions)*, then click *Set Default*, but do not click *OK*. This will keep the Sound dialog box open. Locate an MP3 or other audio file on the target computer and double-click to play it. Then in the Sound dialog box, verify that there is activity in the volume level indicator associated with the *SYSVAD (with APO Extensions)* driver. diff --git a/audio/sysvad/TabletAudioSample/ComponentizedApoSample.inx b/audio/sysvad/TabletAudioSample/ComponentizedApoSample.inx Binary files differindex be9428fb..28069e1c 100644 --- a/audio/sysvad/TabletAudioSample/ComponentizedApoSample.inx +++ b/audio/sysvad/TabletAudioSample/ComponentizedApoSample.inx diff --git a/audio/sysvad/TabletAudioSample/ComponentizedAudioSample.inx b/audio/sysvad/TabletAudioSample/ComponentizedAudioSample.inx Binary files differindex 8f04a934..2f2f9638 100644 --- a/audio/sysvad/TabletAudioSample/ComponentizedAudioSample.inx +++ b/audio/sysvad/TabletAudioSample/ComponentizedAudioSample.inx diff --git a/audio/sysvad/TabletAudioSample/ComponentizedAudioSampleExtension.inx b/audio/sysvad/TabletAudioSample/ComponentizedAudioSampleExtension.inx Binary files differindex 1c89827a..70cd79b8 100644 --- a/audio/sysvad/TabletAudioSample/ComponentizedAudioSampleExtension.inx +++ b/audio/sysvad/TabletAudioSample/ComponentizedAudioSampleExtension.inx diff --git a/audio/sysvad/TabletAudioSample/TabletAudioSample.vcxproj b/audio/sysvad/TabletAudioSample/TabletAudioSample.vcxproj index 6d60c71a..6e4e4c44 100644 --- a/audio/sysvad/TabletAudioSample/TabletAudioSample.vcxproj +++ b/audio/sysvad/TabletAudioSample/TabletAudioSample.vcxproj @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|ARM64"> @@ -399,6 +399,7 @@ </DriverSign> </ItemDefinitionGroup> <ItemGroup> + <ClCompile Include="..\A2dpHpDevice.cpp" /> <ClCompile Include="..\adapter.cpp" /> <ClCompile Include="..\basetopo.cpp" /> <ClCompile Include="..\BthhfpDevice.cpp" /> diff --git a/audio/sysvad/TabletAudioSample/hdmitopo.cpp b/audio/sysvad/TabletAudioSample/hdmitopo.cpp index 9deb16cd..d187f467 100644 --- a/audio/sysvad/TabletAudioSample/hdmitopo.cpp +++ b/audio/sysvad/TabletAudioSample/hdmitopo.cpp @@ -31,10 +31,7 @@ CreateHdmiMiniportTopology _Out_ PUNKNOWN * Unknown, _In_ REFCLSID, _In_opt_ PUNKNOWN UnknownOuter, - _When_((PoolType & NonPagedPoolMustSucceed) != 0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) - _In_ POOL_TYPE PoolType, + _In_ POOL_FLAGS PoolFlags, _In_ PUNKNOWN UnknownAdapter, _In_opt_ PVOID DeviceContext, _In_ PENDPOINT_MINIPAIR MiniportPair @@ -76,7 +73,7 @@ Return Value: UNREFERENCED_PARAMETER(DeviceContext); CHdmiMiniportTopology *obj = - new (PoolType, MINWAVERT_POOLTAG) + new (PoolFlags, MINWAVERT_POOLTAG) CHdmiMiniportTopology( UnknownOuter, MiniportPair->TopoDescriptor, MiniportPair->DeviceMaxChannels); diff --git a/audio/sysvad/TabletAudioSample/hdmitopo.h b/audio/sysvad/TabletAudioSample/hdmitopo.h index cf873b70..a0e8a2d8 100644 --- a/audio/sysvad/TabletAudioSample/hdmitopo.h +++ b/audio/sysvad/TabletAudioSample/hdmitopo.h @@ -26,6 +26,7 @@ Abstract: // CHdmiMiniportTopology // +#pragma code_seg() class CHdmiMiniportTopology : public CMiniportTopologySYSVAD, public IMiniportTopology, @@ -71,10 +72,7 @@ CreateHdmiMiniportTopology( _Out_ PUNKNOWN * Unknown, _In_ REFCLSID, _In_opt_ PUNKNOWN UnknownOuter, - _When_((PoolType & NonPagedPoolMustSucceed) != 0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) - _In_ POOL_TYPE PoolType, + _In_ POOL_FLAGS PoolFlags, _In_ PUNKNOWN UnknownAdapter, _In_opt_ PVOID DeviceContext, _In_ PENDPOINT_MINIPAIR MiniportPair diff --git a/audio/sysvad/TabletAudioSample/hdmitoptable.h b/audio/sysvad/TabletAudioSample/hdmitoptable.h index 23bd8765..c360d798 100644 --- a/audio/sysvad/TabletAudioSample/hdmitoptable.h +++ b/audio/sysvad/TabletAudioSample/hdmitoptable.h @@ -70,6 +70,24 @@ KSDATARANGE HdmiTopoPinDataRangesBridge[] = 0, 0, STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTSX_E1), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + }, + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTSX_E2), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + }, + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MAT20), STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) }, diff --git a/audio/sysvad/TabletAudioSample/hdmiwavtable.h b/audio/sysvad/TabletAudioSample/hdmiwavtable.h index ad3b26e0..bd0f9236 100644 --- a/audio/sysvad/TabletAudioSample/hdmiwavtable.h +++ b/audio/sysvad/TabletAudioSample/hdmiwavtable.h @@ -330,6 +330,56 @@ KSDATAFORMAT_WAVEFORMATEXTENSIBLE HdmiHostPinSupportedDeviceFormats[] = 0, 0, STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTSX_E1), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 8, + 192000, + 3072000, + 16, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_7POINT1, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTSX_E1) + } + }, + { // 11 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTSX_E2), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 8, + 192000, + 3072000, + 16, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_7POINT1, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTSX_E2) + } + }, + { // 12 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MAT20), STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) }, @@ -348,7 +398,7 @@ KSDATAFORMAT_WAVEFORMATEXTENSIBLE HdmiHostPinSupportedDeviceFormats[] = STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MAT20) } }, - { // 11 + { // 13 { sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), 0, @@ -519,7 +569,39 @@ KSDATARANGE_AUDIO HdmiPinDataRangesStream[] = HDMI_DTS_HD_MIN_SAMPLE_RATE, HDMI_DTS_HD_MAX_SAMPLE_RATE }, - { // 6 - DOLBY-MAT20 host + { // 6 - DTS:X E1 host + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTSX_E1), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + HDMI_DTS_HD_MAX_CHANNELS, + HDMI_DTS_HD_MIN_BITS_PER_SAMPLE, + HDMI_DTS_HD_MAX_BITS_PER_SAMPLE, + HDMI_DTS_HD_MIN_SAMPLE_RATE, + HDMI_DTS_HD_MAX_SAMPLE_RATE + }, + { // 7 - DTS:X E2 host + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTSX_E2), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + HDMI_DTS_HD_MAX_CHANNELS, + HDMI_DTS_HD_MIN_BITS_PER_SAMPLE, + HDMI_DTS_HD_MAX_BITS_PER_SAMPLE, + HDMI_DTS_HD_MIN_SAMPLE_RATE, + HDMI_DTS_HD_MAX_SAMPLE_RATE + }, + { // 8 - DOLBY-MAT20 host { sizeof(KSDATARANGE_AUDIO), KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range @@ -535,7 +617,7 @@ KSDATARANGE_AUDIO HdmiPinDataRangesStream[] = HDMI_DOLBY_MAT_MIN_SAMPLE_RATE, HDMI_DOLBY_MAT_MAX_SAMPLE_RATE }, - { // 7 - DOLBY-MAT21 host + { // 9 - DOLBY-MAT21 host { sizeof(KSDATARANGE_AUDIO), KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range @@ -569,6 +651,10 @@ PKSDATARANGE HdmiPinDataRangePointersStream[] = PKSDATARANGE(&HdmiPinDataRangesStream[6]), PKSDATARANGE(&PinDataRangeAttributeList), PKSDATARANGE(&HdmiPinDataRangesStream[7]), + PKSDATARANGE(&PinDataRangeAttributeList), + PKSDATARANGE(&HdmiPinDataRangesStream[8]), + PKSDATARANGE(&PinDataRangeAttributeList), + PKSDATARANGE(&HdmiPinDataRangesStream[9]), PKSDATARANGE(&PinDataRangeAttributeList) }; @@ -633,7 +719,7 @@ KSDATARANGE HdmiPinDataRangesBridge[] = 0, 0, STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), - STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MAT20), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTSX_E1), STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) }, { // 6 @@ -642,6 +728,24 @@ KSDATARANGE HdmiPinDataRangesBridge[] = 0, 0, STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTSX_E2), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + }, + { // 7 + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MAT20), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + }, + { // 8 + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MAT21), STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) } @@ -656,7 +760,9 @@ PKSDATARANGE HdmiPinDataRangePointersBridge[] = &HdmiPinDataRangesBridge[3], &HdmiPinDataRangesBridge[4], &HdmiPinDataRangesBridge[5], - &HdmiPinDataRangesBridge[6] + &HdmiPinDataRangesBridge[6], + &HdmiPinDataRangesBridge[7], + &HdmiPinDataRangesBridge[8] }; diff --git a/audio/sysvad/TabletAudioSample/micintopo.cpp b/audio/sysvad/TabletAudioSample/micintopo.cpp index 22746b95..4927a9b2 100644 --- a/audio/sysvad/TabletAudioSample/micintopo.cpp +++ b/audio/sysvad/TabletAudioSample/micintopo.cpp @@ -249,12 +249,6 @@ Return Value: // NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; - // - // This line shows how to get a pointer to the miniport topology object. - // - PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; - UNREFERENCED_VAR(pMiniport); - if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) { if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) diff --git a/audio/sysvad/TabletAudioSample/micinwavtable.h b/audio/sysvad/TabletAudioSample/micinwavtable.h index 2f923a31..64acba00 100644 --- a/audio/sysvad/TabletAudioSample/micinwavtable.h +++ b/audio/sysvad/TabletAudioSample/micinwavtable.h @@ -27,7 +27,7 @@ Abstract: // // Max # of pin instances. // -#define MICIN_MAX_INPUT_STREAMS 4 +#define MICIN_MAX_INPUT_STREAMS 5 //============================================================================= static @@ -256,7 +256,11 @@ MODE_AND_DEFAULT_FORMAT MicInPinSupportedDeviceModes[] = { STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, &MicInPinSupportedDeviceFormats[4].DataFormat, // 24KHz - } + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_FAR_FIELD_SPEECH, + &MicInPinSupportedDeviceFormats[2].DataFormat, // 16KHz + }, }; // diff --git a/audio/sysvad/TabletAudioSample/minipairs.h b/audio/sysvad/TabletAudioSample/minipairs.h index d9060e9f..0d00f5a5 100644 --- a/audio/sysvad/TabletAudioSample/minipairs.h +++ b/audio/sysvad/TabletAudioSample/minipairs.h @@ -49,7 +49,7 @@ CreateMiniportWaveRTSYSVAD _Out_ PUNKNOWN *, _In_ REFCLSID, _In_opt_ PUNKNOWN, - _In_ POOL_TYPE, + _In_ POOL_FLAGS, _In_ PUNKNOWN, _In_opt_ PVOID, _In_ PENDPOINT_MINIPAIR @@ -61,7 +61,7 @@ CreateMiniportTopologySYSVAD _Out_ PUNKNOWN *, _In_ REFCLSID, _In_opt_ PUNKNOWN, - _In_ POOL_TYPE, + _In_ POOL_FLAGS, _In_ PUNKNOWN, _In_opt_ PVOID, _In_ PENDPOINT_MINIPAIR diff --git a/audio/sysvad/TabletAudioSample/tabletaudiosample.inx b/audio/sysvad/TabletAudioSample/tabletaudiosample.inx Binary files differindex 58ee4d88..29c74cfb 100644 --- a/audio/sysvad/TabletAudioSample/tabletaudiosample.inx +++ b/audio/sysvad/TabletAudioSample/tabletaudiosample.inx diff --git a/audio/sysvad/ToneGenerator.cpp b/audio/sysvad/ToneGenerator.cpp index 6ef2a39a..2ff7cbdd 100644 --- a/audio/sysvad/ToneGenerator.cpp +++ b/audio/sysvad/ToneGenerator.cpp @@ -271,8 +271,8 @@ NTSTATUS ToneGenerator::Init // // Allocate a buffer to hold a partial frame. // - m_PartialFrame = (BYTE*)ExAllocatePoolWithTag( - NonPagedPoolNx, + m_PartialFrame = (BYTE*)ExAllocatePool2( + POOL_FLAG_NON_PAGED, m_FrameSize, SYSVAD_POOLTAG); diff --git a/audio/sysvad/UsbHsDevice.cpp b/audio/sysvad/UsbHsDevice.cpp index 6bc42384..2b92bd94 100644 --- a/audio/sysvad/UsbHsDevice.cpp +++ b/audio/sysvad/UsbHsDevice.cpp @@ -178,8 +178,6 @@ UsbHsDevice::Init m_MicVolumePropValues = NULL; m_SpeakerMutePropValues = NULL; m_MicMutePropValues = NULL; - m_ulSpeakerMutePropValuesSize = 0; - m_ulMicMutePropValuesSize = 0; // Notification updates. m_SpeakerVolumeLevel = 0; @@ -300,9 +298,9 @@ UsbHsDevice::Init // m_SymbolicLinkName.MaximumLength = SymbolicLinkName->MaximumLength; m_SymbolicLinkName.Length = SymbolicLinkName->Length; - m_SymbolicLinkName.Buffer = (PWSTR) ExAllocatePoolWithTag(NonPagedPoolNx, - SymbolicLinkName->MaximumLength, - MINADAPTER_POOLTAG); + m_SymbolicLinkName.Buffer = (PWSTR) ExAllocatePool2(POOL_FLAG_NON_PAGED, + SymbolicLinkName->MaximumLength, + MINADAPTER_POOLTAG); if (m_SymbolicLinkName.Buffer == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; @@ -310,7 +308,7 @@ UsbHsDevice::Init IF_FAILED_ACTION_JUMP( ntStatus, - DPF(D_ERROR, ("%!FUNC!: ExAllocatePoolWithTag failed, out of memory")), + DPF(D_ERROR, ("%!FUNC!: ExAllocatePool2 failed, out of memory")), Done); RtlCopyUnicodeString(&m_SymbolicLinkName, SymbolicLinkName); @@ -659,14 +657,12 @@ Return Value: { ExFreePoolWithTag(m_SpeakerMutePropValues, USBSIDEBANDTEST_POOLTAG06); m_SpeakerMutePropValues = NULL; - m_ulSpeakerMutePropValuesSize = 0; } if (m_MicMutePropValues != NULL) { ExFreePoolWithTag(m_MicMutePropValues, USBSIDEBANDTEST_POOLTAG06); m_MicMutePropValues = NULL; - m_ulMicMutePropValuesSize = 0; } // @@ -1031,12 +1027,12 @@ UsbHsDevice::GetMuteSettings if (deviceType == eUsbHsSpeakerDevice) { - *Size = m_ulSpeakerMutePropValuesSize; + *Size = m_pSpeakerDescriptor->MutePropertyValuesSize; return m_SpeakerMutePropValues; } else if (deviceType == eUsbHsMicDevice) { - *Size = m_ulMicMutePropValuesSize; + *Size = m_pMicDescriptor->MutePropertyValuesSize; return m_MicMutePropValues; } @@ -1695,17 +1691,19 @@ UsbHsDevice::GetFormatsAndModes _In_ eDeviceType deviceType ) { + PAGED_CODE(); + UNREFERENCED_PARAMETER(deviceType); PPIN_DEVICE_FORMATS_AND_MODES pFormatsAndModes = NULL; return pFormatsAndModes; } //============================================================================= -#pragma code_seg("PAGE") +#pragma code_seg() +_IRQL_requires_max_(DISPATCH_LEVEL) BOOL UsbHsDevice::IsNRECSupported() { - PAGED_CODE(); DPF_ENTER(("[%!FUNC!]")); return FALSE; @@ -2281,14 +2279,14 @@ Routine Description: // // Allocate memory needed to hold the info. // - descriptor = (PSIDEBANDAUDIO_DEVICE_DESCRIPTOR) ExAllocatePoolWithTag(NonPagedPoolNx, length, MINADAPTER_POOLTAG); + descriptor = (PSIDEBANDAUDIO_DEVICE_DESCRIPTOR) ExAllocatePool2(POOL_FLAG_NON_PAGED, length, MINADAPTER_POOLTAG); if (descriptor == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } IF_FAILED_ACTION_JUMP( ntStatus, - DPF(D_ERROR, ("%!FUNC!: ExAllocatePoolWithTag failed, out of memory")), + DPF(D_ERROR, ("%!FUNC!: ExAllocatePool2 failed, out of memory")), Done); // @@ -2346,7 +2344,7 @@ NTSTATUS UsbHsDevice::GetUsbHsEndpointDescriptor ( _In_ ULONG EpIndex, - _Out_ PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR *EpDescriptor + _Out_ PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 *EpDescriptor ) /*++ @@ -2361,7 +2359,7 @@ Routine Description: NTSTATUS ntStatus = STATUS_SUCCESS; WDFREQUEST req = NULL; - PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR descriptor = NULL; + PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 descriptor = NULL; ULONG length = 0; ULONG_PTR information = 0; WDF_REQUEST_REUSE_PARAMS reuseParams; @@ -2388,7 +2386,7 @@ Routine Description: // ntStatus = SendIoCtrlSynchronously( req, - IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR, + IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR2, sizeof(EpIndex), NULL, &EpIndex); @@ -2419,14 +2417,14 @@ Routine Description: // // Allocate memory needed to hold the info. // - descriptor = (PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR) ExAllocatePoolWithTag(NonPagedPoolNx, length, MINADAPTER_POOLTAG); + descriptor = (PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2) ExAllocatePool2(POOL_FLAG_NON_PAGED, length, MINADAPTER_POOLTAG); if (descriptor == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } IF_FAILED_ACTION_JUMP( ntStatus, - DPF(D_ERROR, ("%!FUNC!: ExAllocatePoolWithTag failed, out of memory")), + DPF(D_ERROR, ("%!FUNC!: ExAllocatePool2 failed, out of memory")), Done); // @@ -2449,7 +2447,7 @@ Routine Description: ntStatus = SendIoCtrlSynchronously( req, - IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR, + IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR2, sizeof(EpIndex), length, descriptor); @@ -2493,6 +2491,8 @@ UsbHsDevice::VerifyEndpointFormatCompatibility _Out_ PULONG pHardwareFormatIndex ) { + PAGED_CODE(); + NTSTATUS status = STATUS_NOT_SUPPORTED; ULONG iHardwareFormats = 0; ULONG iEpDataFormats = 0; @@ -2535,12 +2535,14 @@ UsbHsDevice::VerifyEndpointFormatCompatibility return status; } - +#pragma code_seg("PAGE") NTSTATUS UsbHsDevice::FreeTransportResources ( _In_ PUSBHSDEVICE_EP_TRANSPORT_RESOURCES pTransportResources ) { + PAGED_CODE(); + SAFE_DELETE_PTR_WITH_TAG(pTransportResources->pUsbInterfaceDescriptor, USBSIDEBANDTEST_POOLTAG07); SAFE_DELETE_PTR_WITH_TAG(pTransportResources->pUsbEndpointDescriptor, USBSIDEBANDTEST_POOLTAG08); SAFE_DELETE_PTR_WITH_TAG(pTransportResources->pUsbOffloadInformation, USBSIDEBANDTEST_POOLTAG09); @@ -2556,10 +2558,11 @@ NTSTATUS UsbHsDevice::FreeTransportResources #pragma code_seg("PAGE") NTSTATUS UsbHsDevice::GetSiop ( - _In_ ULONG EpIndex, - _In_ ULONG SiopTypeId, - _In_ ULONG PoolTag, - _Out_ PBYTE *ppOutputBuffer + _In_ const GUID *ParamSet, + _In_ ULONG SiopTypeId, + _In_ ULONG EpIndex, + _In_ ULONG PoolTag, + _Outptr_ PBYTE *ppOutputBuffer ) { PAGED_CODE(); @@ -2591,7 +2594,7 @@ NTSTATUS UsbHsDevice::GetSiop Done); siop.EpIndex = EpIndex; - siop.RequestedSiop.ParamSet = SIDEBANDAUDIO_PARAMS_SET_USBAUDIO; + siop.RequestedSiop.ParamSet = *ParamSet; siop.RequestedSiop.TypeId = SiopTypeId; siop.RequestedSiop.Size = 0; @@ -2632,14 +2635,14 @@ NTSTATUS UsbHsDevice::GetSiop // // Allocate memory needed to hold the info. // - pOutput = (PBYTE)ExAllocatePoolWithTag(NonPagedPoolNx, length, PoolTag); + pOutput = (PBYTE)ExAllocatePool2(POOL_FLAG_NON_PAGED, length, PoolTag); if (pOutput == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; } IF_FAILED_ACTION_JUMP( status, - DPF(D_ERROR, ("%!FUNC!: ExAllocatePoolWithTag failed, out of memory")), + DPF(D_ERROR, ("%!FUNC!: ExAllocatePool2 failed, out of memory")), Done); // @@ -2701,7 +2704,7 @@ NTSTATUS UsbHsDevice::GetTransportResources ( _In_ ULONG EpIndex, - _In_ PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR pEndpointDescriptor, + _In_ PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 pEndpointDescriptor, _Out_ PUSBHSDEVICE_EP_TRANSPORT_RESOURCES pTransportResources ) { @@ -2711,7 +2714,7 @@ UsbHsDevice::GetTransportResources NTSTATUS status = STATUS_SUCCESS; // SIOP_TYPE_USBAUD_USBD_INTERFACE_DESCRIPTOR - status = GetSiop(EpIndex, SIOP_TYPE_USBAUD_USBD_INTERFACE_DESCRIPTOR, USBSIDEBANDTEST_POOLTAG07, (PBYTE *)&pTransportResources->pUsbInterfaceDescriptor); + status = GetSiop(&SIDEBANDAUDIO_PARAMS_SET_USBAUDIO, SIOP_TYPE_USBAUD_USBD_INTERFACE_DESCRIPTOR, EpIndex, USBSIDEBANDTEST_POOLTAG07, (PBYTE *)&pTransportResources->pUsbInterfaceDescriptor); if (!NT_SUCCESS(status)) { DPF(D_ERROR, ("%!FUNC!: GetSiop(SIOP_TYPE_USBAUD_USBD_INTERFACE_DESCRIPTOR) failed, 0x%x", status)); @@ -2719,7 +2722,7 @@ UsbHsDevice::GetTransportResources } // SIOP_TYPE_USBAUD_EP_USBD_ENDPOINT_DESCRIPTOR - status = GetSiop(EpIndex, SIOP_TYPE_USBAUD_EP_USBD_ENDPOINT_DESCRIPTOR, USBSIDEBANDTEST_POOLTAG08, (PBYTE *)&pTransportResources->pUsbEndpointDescriptor); + status = GetSiop(&SIDEBANDAUDIO_PARAMS_SET_USBAUDIO, SIOP_TYPE_USBAUD_EP_USBD_ENDPOINT_DESCRIPTOR, EpIndex, USBSIDEBANDTEST_POOLTAG08, (PBYTE *)&pTransportResources->pUsbEndpointDescriptor); if (!NT_SUCCESS(status)) { DPF(D_ERROR, ("%!FUNC!: GetSiop(SIOP_TYPE_USBAUD_EP_USBD_ENDPOINT_DESCRIPTOR) failed, 0x%x", status)); @@ -2727,7 +2730,7 @@ UsbHsDevice::GetTransportResources } // SIOP_TYPE_USBAUD_EP_USBD_ENDPOINT_OFFLOAD_INFORMATION - status = GetSiop(EpIndex, SIOP_TYPE_USBAUD_EP_USBD_ENDPOINT_OFFLOAD_INFORMATION, USBSIDEBANDTEST_POOLTAG09, (PBYTE *)&pTransportResources->pUsbOffloadInformation); + status = GetSiop(&SIDEBANDAUDIO_PARAMS_SET_USBAUDIO, SIOP_TYPE_USBAUD_EP_USBD_ENDPOINT_OFFLOAD_INFORMATION, EpIndex, USBSIDEBANDTEST_POOLTAG09, (PBYTE *)&pTransportResources->pUsbOffloadInformation); if (!NT_SUCCESS(status)) { DPF(D_ERROR, ("%!FUNC!: GetSiop(SIOP_TYPE_USBAUD_EP_USBD_ENDPOINT_OFFLOAD_INFORMATION) failed, 0x%x", status)); @@ -2735,7 +2738,7 @@ UsbHsDevice::GetTransportResources } // SIOP_TYPE_USBAUD_EP_USBAUDIO_TRANSPORT_RESOURCES - status = GetSiop(EpIndex, SIOP_TYPE_USBAUD_EP_USBAUDIO_TRANSPORT_RESOURCES, USBSIDEBANDTEST_POOLTAG010, (PBYTE *)&pTransportResources->pUsbAudioTransportResources); + status = GetSiop(&SIDEBANDAUDIO_PARAMS_SET_USBAUDIO, SIOP_TYPE_USBAUD_EP_USBAUDIO_TRANSPORT_RESOURCES, EpIndex, USBSIDEBANDTEST_POOLTAG010, (PBYTE *)&pTransportResources->pUsbAudioTransportResources); if (!NT_SUCCESS(status)) { DPF(D_ERROR, ("%!FUNC!: GetSiop(SIOP_TYPE_USBAUD_EP_USBAUDIO_TRANSPORT_RESOURCES) failed, 0x%x", status)); @@ -2745,7 +2748,7 @@ UsbHsDevice::GetTransportResources if (pEndpointDescriptor->Capabilities.Feedback) { // SIOP_TYPE_USBAUD_FB_EP_USBD_ENDPOINT_DESCRIPTOR - status = GetSiop(EpIndex, SIOP_TYPE_USBAUD_FB_EP_USBD_ENDPOINT_DESCRIPTOR, USBSIDEBANDTEST_POOLTAG011, (PBYTE *)&pTransportResources->pSyncUsbEndpointDescriptor); + status = GetSiop(&SIDEBANDAUDIO_PARAMS_SET_USBAUDIO, SIOP_TYPE_USBAUD_FB_EP_USBD_ENDPOINT_DESCRIPTOR, EpIndex, USBSIDEBANDTEST_POOLTAG011, (PBYTE *)&pTransportResources->pSyncUsbEndpointDescriptor); if (!NT_SUCCESS(status)) { DPF(D_ERROR, ("%!FUNC!: GetSiop(SIOP_TYPE_USBAUD_FB_EP_USBD_ENDPOINT_DESCRIPTOR) failed, 0x%x", status)); @@ -2753,7 +2756,7 @@ UsbHsDevice::GetTransportResources } // SIOP_TYPE_USBAUD_FB_EP_USBD_ENDPOINT_OFFLOAD_INFORMATION - status = GetSiop(EpIndex, SIOP_TYPE_USBAUD_FB_EP_USBD_ENDPOINT_OFFLOAD_INFORMATION, USBSIDEBANDTEST_POOLTAG012, (PBYTE *)&pTransportResources->pSyncUsbOffloadInformation); + status = GetSiop(&SIDEBANDAUDIO_PARAMS_SET_USBAUDIO, SIOP_TYPE_USBAUD_FB_EP_USBD_ENDPOINT_OFFLOAD_INFORMATION, EpIndex, USBSIDEBANDTEST_POOLTAG012, (PBYTE *)&pTransportResources->pSyncUsbOffloadInformation); if (!NT_SUCCESS(status)) { DPF(D_ERROR, ("%!FUNC!: GetSiop(SIOP_TYPE_USBAUD_FB_EP_USBD_ENDPOINT_OFFLOAD_INFORMATION) failed, 0x%x", status)); @@ -2761,7 +2764,7 @@ UsbHsDevice::GetTransportResources } // SIOP_TYPE_USBAUD_FB_EP_USBAUDIO_TRANSPORT_RESOURCES - status = GetSiop(EpIndex, SIOP_TYPE_USBAUD_FB_EP_USBAUDIO_TRANSPORT_RESOURCES, USBSIDEBANDTEST_POOLTAG013, (PBYTE *)&pTransportResources->pSyncUsbAudioTransportResources); + status = GetSiop(&SIDEBANDAUDIO_PARAMS_SET_USBAUDIO, SIOP_TYPE_USBAUD_FB_EP_USBAUDIO_TRANSPORT_RESOURCES, EpIndex, USBSIDEBANDTEST_POOLTAG013, (PBYTE *)&pTransportResources->pSyncUsbAudioTransportResources); if (!NT_SUCCESS(status)) { DPF(D_ERROR, ("%!FUNC!: GetSiop(SIOP_TYPE_USBAUD_FB_EP_USBAUDIO_TRANSPORT_RESOURCES) failed, 0x%x", status)); @@ -2782,7 +2785,7 @@ Done: NTSTATUS UsbHsDevice::SetTransportResources ( - _In_ PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR pEndpointDescriptor, + _In_ PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 pEndpointDescriptor, _In_ ULONG EpIndex ) { @@ -2931,14 +2934,14 @@ Routine Description: // // Allocate memory needed to hold the info. // - formats = (PSIDEBANDAUDIO_SUPPORTED_FORMATS) ExAllocatePoolWithTag(NonPagedPoolNx, length, USBSIDEBANDTEST_POOLTAG03); + formats = (PSIDEBANDAUDIO_SUPPORTED_FORMATS) ExAllocatePool2(POOL_FLAG_NON_PAGED, length, USBSIDEBANDTEST_POOLTAG03); if (formats == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } IF_FAILED_ACTION_JUMP( ntStatus, - DPF(D_ERROR, ("%!FUNC!: ExAllocatePoolWithTag failed, out of memory")), + DPF(D_ERROR, ("%!FUNC!: ExAllocatePool2 failed, out of memory")), Done); // @@ -3001,7 +3004,7 @@ UsbHsDevice::GetUsbHsVolumePropertyValues ( _In_ ULONG EpIndex, _In_ ULONG Length, - _Out_ PKSPROPERTY_DESCRIPTION *PropValues + _Out_ PKSPROPERTY_DESCRIPTION* PropValues ) /*++ @@ -3014,28 +3017,28 @@ Routine Description: PAGED_CODE(); DPF_ENTER(("[%!FUNC!]")); - NTSTATUS ntStatus = STATUS_SUCCESS; - PKSPROPERTY_DESCRIPTION propValues = NULL; + NTSTATUS ntStatus = STATUS_SUCCESS; + PKSPROPERTY_DESCRIPTION propValues = NULL; *PropValues = NULL; // // Allocate memory. // - propValues = (PKSPROPERTY_DESCRIPTION) ExAllocatePoolWithTag(NonPagedPoolNx, Length, USBSIDEBANDTEST_POOLTAG02); + propValues = (PKSPROPERTY_DESCRIPTION) ExAllocatePool2(POOL_FLAG_NON_PAGED, Length, USBSIDEBANDTEST_POOLTAG02); if (propValues == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } IF_FAILED_ACTION_JUMP( ntStatus, - DPF(D_ERROR, ("%!FUNC!: ExAllocatePoolWithTag failed, out of memory")), + DPF(D_ERROR, ("%!FUNC!: ExAllocatePool2 failed, out of memory")), Done); // // Send EpIndex in same buffer as input param // - *((ULONG *)propValues) = EpIndex; + *((ULONG*)propValues) = EpIndex; // // Get the USB Headset volume property values. @@ -3043,8 +3046,8 @@ Routine Description: ntStatus = SendIoCtrlSynchronously( NULL, IOCTL_SBAUD_GET_VOLUMEPROPERTYVALUES, - sizeof(EpIndex), - Length, + sizeof(EpIndex), + Length, propValues); IF_FAILED_ACTION_JUMP( @@ -3064,7 +3067,7 @@ Done: if (propValues != NULL) { ExFreePoolWithTag(propValues, MINADAPTER_POOLTAG); - } + } } return ntStatus; @@ -3343,8 +3346,8 @@ NTSTATUS UsbHsDevice::GetUsbHsMutePropertyValues ( _In_ ULONG EpIndex, - _In_ PULONG pLength, - _Out_ PKSPROPERTY_DESCRIPTION *PropValues + _In_ ULONG Length, + _Out_ PKSPROPERTY_DESCRIPTION* PropValues ) /*++ @@ -3357,99 +3360,37 @@ Routine Description: PAGED_CODE(); DPF_ENTER(("[%!FUNC!]")); - NTSTATUS ntStatus = STATUS_SUCCESS; - WDFREQUEST req = NULL; - ULONG length = 0; - ULONG_PTR information = 0; - WDF_REQUEST_REUSE_PARAMS reuseParams; - WDF_OBJECT_ATTRIBUTES attributes; - PKSPROPERTY_DESCRIPTION propValues = NULL; + NTSTATUS ntStatus = STATUS_SUCCESS; + PKSPROPERTY_DESCRIPTION propValues = NULL; *PropValues = NULL; // - // Allocate and format a WDF request. - // - WDF_OBJECT_ATTRIBUTES_INIT(&attributes); - attributes.ParentObject = m_Adapter->GetWdfDevice(); - ntStatus = WdfRequestCreate( - &attributes, - m_WdfIoTarget, - &req); - IF_FAILED_ACTION_JUMP( - ntStatus, - DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate failed, 0x%x", ntStatus)), - Done); - - // - // Get the size of the buffer. - // - ntStatus = SendIoCtrlSynchronously( - req, - IOCTL_SBAUD_GET_MUTEPROPERTYVALUES, - sizeof(EpIndex), - 0, - &EpIndex); - - if (ntStatus != STATUS_BUFFER_TOO_SMALL) - { - if (NT_SUCCESS(ntStatus)) - { - ntStatus = STATUS_INVALID_DEVICE_STATE; - } - - DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_MUTEPROPERTYVALUES): failed, 0x%x", ntStatus)); - goto Done; - } - - ntStatus = STATUS_SUCCESS; - - information = WdfRequestGetInformation(req); - if (information == 0 || information > ULONG_MAX) - { - ntStatus = STATUS_INVALID_DEVICE_STATE; - DPF(D_ERROR, ("%!FUNC!: IOCTL_SBAUD_GET_MUTEPROPERTYVALUES buffer too big (%Id): 0x%x", information, ntStatus)); - goto Done; - } - - length = (ULONG)information; - - // - // Allocate memory needed to hold the info. + // Allocate memory. // - propValues = (PKSPROPERTY_DESCRIPTION) ExAllocatePoolWithTag(NonPagedPoolNx, length, USBSIDEBANDTEST_POOLTAG06); + propValues = (PKSPROPERTY_DESCRIPTION) ExAllocatePool2(POOL_FLAG_NON_PAGED, Length, USBSIDEBANDTEST_POOLTAG06); if (propValues == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } IF_FAILED_ACTION_JUMP( ntStatus, - DPF(D_ERROR, ("%!FUNC!: ExAllocatePoolWithTag failed, out of memory")), + DPF(D_ERROR, ("%!FUNC!: ExAllocatePool2 failed, out of memory")), Done); // - // Get the USB Headset Mute Property Values. + // Send EpIndex in same buffer as input param // - WDF_REQUEST_REUSE_PARAMS_INIT( - &reuseParams, - WDF_REQUEST_REUSE_NO_FLAGS, - STATUS_SUCCESS); - - ntStatus = WdfRequestReuse(req, &reuseParams); - IF_FAILED_ACTION_JUMP( - ntStatus, - DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus)), - Done); - - // Same buffer used for input and output - // Cast into ULONG to provide EpIndex - *((ULONG *)propValues) = EpIndex; + *((ULONG*)propValues) = EpIndex; + // + // Get the USB Headset mute property values. + // ntStatus = SendIoCtrlSynchronously( - req, + NULL, IOCTL_SBAUD_GET_MUTEPROPERTYVALUES, sizeof(EpIndex), - length, + Length, propValues); IF_FAILED_ACTION_JUMP( ntStatus, @@ -3460,7 +3401,6 @@ Routine Description: // All done. // *PropValues = propValues; - *pLength = length; ntStatus = STATUS_SUCCESS; Done: @@ -3469,13 +3409,7 @@ Done: if (propValues != NULL) { ExFreePoolWithTag(propValues, USBSIDEBANDTEST_POOLTAG06); - } - } - - if (req != NULL) - { - WdfObjectDelete(req); - req = NULL; + } } return ntStatus; @@ -4157,7 +4091,7 @@ UsbHsDevice::SetUsbHsStreamOpen ( ULONG EpIndex, PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pStreamFormat, - PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR pEndpointDescriptor + PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 pEndpointDescriptor ) /*++ @@ -4169,7 +4103,7 @@ Routine Description: --*/ { PAGED_CODE(); - DPF_ENTER(("[UsbHsDevice::SetA2dpHpStreamOpen]")); + DPF_ENTER(("[UsbHsDevice::SetUsbHsStreamOpen]")); NTSTATUS ntStatus = STATUS_SUCCESS; UNREFERENCED_PARAMETER(pEndpointDescriptor); @@ -4189,13 +4123,13 @@ Routine Description: if (ntStatus == STATUS_DEVICE_BUSY) { // The stream channel is already open. - DPF(D_VERBOSE, ("SetA2dpHpStreamOpen: the stream channel is already open")); + DPF(D_VERBOSE, ("SetUsbHsStreamOpen: the stream channel is already open")); ntStatus = STATUS_SUCCESS; } IF_FAILED_ACTION_JUMP( ntStatus, - DPF(D_ERROR, ("SetA2dpHpStreamOpen: SendIoCtrlSynchronously(IOCTL_SBAUD_STREAM_OPEN) failed, 0x%x", ntStatus)), + DPF(D_ERROR, ("SetUsbHsStreamOpen: SendIoCtrlSynchronously(IOCTL_SBAUD_STREAM_OPEN) failed, 0x%x", ntStatus)), Done); // @@ -4225,7 +4159,7 @@ Routine Description: --*/ { PAGED_CODE(); - DPF_ENTER(("[UsbHsDevice::SetA2dpHpStreamStart]")); + DPF_ENTER(("[UsbHsDevice::SetUsbHsStreamStart]")); NTSTATUS ntStatus = STATUS_SUCCESS; @@ -4239,13 +4173,13 @@ Routine Description: if (ntStatus == STATUS_DEVICE_BUSY) { // The stream channel is already Start. - DPF(D_VERBOSE, ("SetA2dpHpStreamStart: the stream channel is already Start")); + DPF(D_VERBOSE, ("SetUsbHsStreamStart: the stream channel is already Start")); ntStatus = STATUS_SUCCESS; } IF_FAILED_ACTION_JUMP( ntStatus, - DPF(D_ERROR, ("SetA2dpHpStreamStart: SendIoCtrlSynchronously(IOCTL_SBAUD_STREAM_START) failed, 0x%x", ntStatus)), + DPF(D_ERROR, ("SetUsbHsStreamStart: SendIoCtrlSynchronously(IOCTL_SBAUD_STREAM_START) failed, 0x%x", ntStatus)), Done); // @@ -4370,9 +4304,24 @@ Routine Description: DPF(D_ERROR, ("%!FUNC!: GetUsbHsDescriptor: failed to retrieve USB Headset Descriptor, 0x%x", ntStatus)), Done); + // SIDEBANDAUDIO_PARAMS_SET_USB_CONTROLLER + PBOOL pbIsBehindHub = NULL; + ntStatus = GetSiop( + &SIDEBANDAUDIO_PARAMS_SET_USB_CONTROLLER, + SIOP_TYPE_USBAUD_CONTROLLER_CONFIG_INFO_DEVICE_BEHIND_HUB, + (ULONG)-1, + USBSIDEBANDTEST_POOLTAG016, + (PBYTE *)&pbIsBehindHub); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("%!FUNC!: GetSiop(SIOP_TYPE_USBAUD_CONTROLLER_CONFIG_INFO_DEVICE_BEHIND_HUB) failed, %!STATUS!", ntStatus)); + goto Done; + } + SAFE_DELETE_PTR_WITH_TAG(pbIsBehindHub, USBSIDEBANDTEST_POOLTAG016); + for (ULONG i = 0; i < m_Descriptor->NumberOfEndpoints; i++) { - PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR ed; + PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 ed; ntStatus = GetUsbHsEndpointDescriptor(i, &ed); IF_FAILED_ACTION_JUMP( ntStatus, @@ -4441,13 +4390,12 @@ Routine Description: if (m_pSpeakerDescriptor->Capabilities.Mute) { PKSPROPERTY_DESCRIPTION mutePropValues = NULL; - ULONG ulMutePropValuesSize = 0; LONG mute = 0; // Mute settings. ntStatus = GetUsbHsMutePropertyValues( m_SpeakerEpIndex, - &ulMutePropValuesSize, + m_pSpeakerDescriptor->MutePropertyValuesSize, &mutePropValues); IF_FAILED_ACTION_JUMP( @@ -4456,7 +4404,6 @@ Routine Description: Done); m_SpeakerMutePropValues = mutePropValues; - m_ulSpeakerMutePropValuesSize = ulMutePropValuesSize; // Speaker mute. ntStatus = GetUsbHsSpeakerMute(0, &mute); @@ -4581,13 +4528,12 @@ Routine Description: if (m_pMicDescriptor->Capabilities.Mute) { PKSPROPERTY_DESCRIPTION mutePropValues = NULL; - ULONG ulMutePropValuesSize = 0; LONG mute = 0; // Mute settings. ntStatus = GetUsbHsMutePropertyValues( m_MicEpIndex, - &ulMutePropValuesSize, + m_pMicDescriptor->MutePropertyValuesSize, &mutePropValues); IF_FAILED_ACTION_JUMP( @@ -4596,7 +4542,6 @@ Routine Description: Done); m_MicMutePropValues = mutePropValues; - m_ulMicMutePropValuesSize = ulMutePropValuesSize; // Mic mute. ntStatus = GetUsbHsMicMute(0, &mute); @@ -4746,10 +4691,10 @@ UsbHsDevice::CreateCustomEndpointMinipair // cTopoPins = pBaseMinipair->TopoDescriptor->PinCount; cProperties = pBaseMinipair->TopoInterfacePropertyCount + 1; - pProperties = (SYSVAD_DEVPROPERTY*)ExAllocatePoolWithTag(NonPagedPoolNx, cProperties * sizeof(SYSVAD_DEVPROPERTY), SYSVAD_POOLTAG); - pNewMinipair = (ENDPOINT_MINIPAIR*)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(ENDPOINT_MINIPAIR), SYSVAD_POOLTAG); - pNewTopoFilterDesc = (PCFILTER_DESCRIPTOR*)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(PCFILTER_DESCRIPTOR), SYSVAD_POOLTAG); - pNewTopoPins = (PCPIN_DESCRIPTOR*)ExAllocatePoolWithTag(NonPagedPoolNx, cTopoPins * sizeof(PCPIN_DESCRIPTOR), SYSVAD_POOLTAG); + pProperties = (SYSVAD_DEVPROPERTY*)ExAllocatePool2(POOL_FLAG_NON_PAGED, cProperties * sizeof(SYSVAD_DEVPROPERTY), SYSVAD_POOLTAG); + pNewMinipair = (ENDPOINT_MINIPAIR*)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(ENDPOINT_MINIPAIR), SYSVAD_POOLTAG); + pNewTopoFilterDesc = (PCFILTER_DESCRIPTOR*)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(PCFILTER_DESCRIPTOR), SYSVAD_POOLTAG); + pNewTopoPins = (PCPIN_DESCRIPTOR*)ExAllocatePool2(POOL_FLAG_NON_PAGED, cTopoPins * sizeof(PCPIN_DESCRIPTOR), SYSVAD_POOLTAG); if ((pProperties != NULL) && (pNewMinipair != NULL) && (pNewTopoFilterDesc != NULL) && (pNewTopoPins != NULL)) { diff --git a/audio/sysvad/UsbHsDevice.h b/audio/sysvad/UsbHsDevice.h index 625352af..9d45fa7c 100644 --- a/audio/sysvad/UsbHsDevice.h +++ b/audio/sysvad/UsbHsDevice.h @@ -146,18 +146,29 @@ class UsbHsDevice : PUNKNOWN m_UnknownMicWave; PSIDEBANDAUDIO_DEVICE_DESCRIPTOR m_Descriptor; - PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR m_pSpeakerDescriptor; - PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR m_pMicDescriptor; + + // + // Using new Endpoint descriptor structure + // Old: SIDEBANDAUDIO_ENDPOINT_DESCRIPTOR - IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR + // Use SIDEBANDAUDIO_ENDPOINT_DESCRIPTOR for 20H2 and older OS. + // + // New: SIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 - IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR2 + // + // The new struct includes interface properties + // that need to be added to the KS Filter interface + // + // USB Audio class driver supports both old and new IOCTLs + // + PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 m_pSpeakerDescriptor; + PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 m_pMicDescriptor; PKSPROPERTY_DESCRIPTION m_SpeakerVolumePropValues; LONG m_SpeakerVolumeLevel; PKSPROPERTY_DESCRIPTION m_MicVolumePropValues; LONG m_MicVolumeLevel; PKSPROPERTY_DESCRIPTION m_SpeakerMutePropValues; - ULONG m_ulSpeakerMutePropValuesSize; LONG m_SpeakerMute; PKSPROPERTY_DESCRIPTION m_MicMutePropValues; - ULONG m_ulMicMutePropValuesSize; LONG m_MicMute; @@ -363,6 +374,7 @@ class UsbHsDevice : _In_ eDeviceType deviceType ); + _IRQL_requires_max_(DISPATCH_LEVEL) STDMETHODIMP_(BOOL) IsNRECSupported(); STDMETHODIMP_(BOOL) GetNRECDisableStatus(); @@ -415,7 +427,7 @@ class UsbHsDevice : NTSTATUS GetUsbHsEndpointDescriptor ( _In_ ULONG EpIndex, - _Out_ PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR *EpDescriptor + _Out_ PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 *EpDescriptor ); NTSTATUS GetUsbHsVolumePropertyValues @@ -456,7 +468,7 @@ class UsbHsDevice : NTSTATUS GetUsbHsMutePropertyValues ( _In_ ULONG EpIndex, - _Out_ PULONG pLength, + _In_ ULONG Length, _Out_ PKSPROPERTY_DESCRIPTION *PropValues ); @@ -510,39 +522,40 @@ class UsbHsDevice : NTSTATUS GetTransportResources ( _In_ ULONG EpIndex, - _In_ PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR pEndpointDescriptor, + _In_ PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 pEndpointDescriptor, _Out_ PUSBHSDEVICE_EP_TRANSPORT_RESOURCES pTransportResources ); NTSTATUS SetTransportResources ( - _In_ PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR pEndpointDescriptor, + _In_ PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 pEndpointDescriptor, _In_ ULONG EpIndex ); NTSTATUS GetSiop ( - _In_ ULONG EpIndex, - _In_ ULONG SiopTypeId, - _In_ ULONG PoolTag, - _Out_ PBYTE *ppOutputBuffer + _In_ const GUID *ParamSet, + _In_ ULONG SiopTypeId, + _In_ ULONG EpIndex, + _In_ ULONG PoolTag, + _Outptr_ PBYTE *ppOutputBuffer ); static NTSTATUS CreateCustomEndpointMinipair ( - _In_ PENDPOINT_MINIPAIR pBaseMinipair, - _In_ PUNICODE_STRING FriendlyName, - _In_ PGUID pCategory, - _Outptr_ PENDPOINT_MINIPAIR *ppCustomMinipair + _In_ PENDPOINT_MINIPAIR pBaseMinipair, + _In_ PUNICODE_STRING FriendlyName, + _In_ PGUID pCategory, + _Outptr_ PENDPOINT_MINIPAIR *ppCustomMinipair ); static NTSTATUS UpdateCustomEndpointCategory ( - _In_ PPCFILTER_DESCRIPTOR pCustomMinipairTopoFilter, - _In_ PPCPIN_DESCRIPTOR pCustomMinipairTopoPins, - _In_ PGUID pCategory + _In_ PPCFILTER_DESCRIPTOR pCustomMinipairTopoFilter, + _In_ PPCPIN_DESCRIPTOR pCustomMinipairTopoPins, + _In_ PGUID pCategory ); static @@ -569,7 +582,7 @@ class UsbHsDevice : NTSTATUS MicStreamClose(); - NTSTATUS SetUsbHsStreamOpen(ULONG EpIndex, PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pStreamFormat, PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR pEndpointDescriptor); + NTSTATUS SetUsbHsStreamOpen(ULONG EpIndex, PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pStreamFormat, PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 pEndpointDescriptor); NTSTATUS SetUsbHsStreamStart(ULONG EpIndex); diff --git a/audio/sysvad/adapter.cpp b/audio/sysvad/adapter.cpp index cc7960a5..ef48e5f4 100644 --- a/audio/sysvad/adapter.cpp +++ b/audio/sysvad/adapter.cpp @@ -32,6 +32,9 @@ Abstract: #ifdef SYSVAD_USB_SIDEBAND #include "usbhsminipairs.h" #endif // SYSVAD_USB_SIDEBAND +#ifdef SYSVAD_A2DP_SIDEBAND +#include "a2dphpminipairs.h" +#endif // SYSVAD_A2DP_SIDEBAND @@ -269,6 +272,15 @@ DWORD g_DisableBthScoBypass = 0; // default is SCO bypass enabled. DWORD g_DisableUsbSideband = 0; // default is USB bypass enabled. #endif // SYSVAD_USB_SIDEBAND +#ifdef SYSVAD_A2DP_SIDEBAND +// +// This driver listens for arrival/removal of the Bluetooth A2DP Sideband interfaces by +// default. Use the registry value DisableA2dpSideband (DWORD) > 0 to override +// this default. +// +DWORD g_DisableA2dpSideband = 0; // default is A2DP bypass enabled. +#endif + //----------------------------------------------------------------------------- // Functions //----------------------------------------------------------------------------- @@ -276,6 +288,8 @@ DWORD g_DisableUsbSideband = 0; // default is USB bypass enabled. #pragma code_seg("PAGE") void ReleaseRegistryStringBuffer() { + PAGED_CODE(); + if (g_RegistryPath.Buffer != NULL) { ExFreePool(g_RegistryPath.Buffer); @@ -371,15 +385,13 @@ NTSTATUS - SUCCESS if able to configure the framework g_RegistryPath.MaximumLength = RegistryPath->Length + sizeof(WCHAR); - g_RegistryPath.Buffer = (PWCH)ExAllocatePoolWithTag(PagedPool, g_RegistryPath.MaximumLength, MINADAPTER_POOLTAG); + g_RegistryPath.Buffer = (PWCH)ExAllocatePool2(POOL_FLAG_PAGED, g_RegistryPath.MaximumLength, MINADAPTER_POOLTAG); if (g_RegistryPath.Buffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(g_RegistryPath.Buffer, g_RegistryPath.MaximumLength); - RtlAppendUnicodeToString(&g_RegistryPath, RegistryPath->Buffer); return STATUS_SUCCESS; @@ -436,14 +448,12 @@ Returns: parametersPath.MaximumLength = RegistryPath->Length + sizeof(L"\\Parameters") + sizeof(WCHAR); - parametersPath.Buffer = (PWCH) ExAllocatePoolWithTag(PagedPool, parametersPath.MaximumLength, MINADAPTER_POOLTAG); + parametersPath.Buffer = (PWCH) ExAllocatePool2(POOL_FLAG_PAGED, parametersPath.MaximumLength, MINADAPTER_POOLTAG); if (parametersPath.Buffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(parametersPath.Buffer, parametersPath.MaximumLength); - RtlAppendUnicodeToString(¶metersPath, RegistryPath->Buffer); RtlAppendUnicodeToString(¶metersPath, L"\\Parameters"); @@ -1053,7 +1063,7 @@ Return Value: &pUnknownCommon, IID_IAdapterCommon, NULL, - NonPagedPoolNx + POOL_FLAG_NON_PAGED ); IF_FAILED_JUMP(ntStatus, Exit); @@ -1101,6 +1111,15 @@ Return Value: } #endif // SYSVAD_USB_SIDEBAND +#ifdef SYSVAD_A2DP_SIDEBAND + if (!g_DisableA2dpSideband) + { + // Init infrastructure for Bluetooth A2DP sideband devices. + ntStatus = pAdapterCommon->InitA2dpSideband(); + IF_FAILED_JUMP(ntStatus, Exit); + } +#endif + #ifdef _USE_SingleComponentMultiFxStates // // Init single component - multi Fx states diff --git a/audio/sysvad/common.cpp b/audio/sysvad/common.cpp index 9292c96d..9bc3e644 100644 --- a/audio/sysvad/common.cpp +++ b/audio/sysvad/common.cpp @@ -39,6 +39,12 @@ Abstract: #include "UsbHsDevice.h" #endif // SYSVAD_USB_SIDEBAND +#ifdef SYSVAD_A2DP_SIDEBAND +#include <SidebandAudio.h> +#include <A2DPSidebandAudio.h> +#include "A2dpHpMinipairs.h" +#include "A2dpHpDevice.h" +#endif // SYSVAD_A2DP_SIDEBAND //----------------------------------------------------------------------------- // CSaveData statics @@ -57,6 +63,9 @@ class BthHfpDevice; // Forward declaration. class UsbHsDevice; // Forward declaration. #endif // SYSVAD_USB_SIDEBAND +#ifdef SYSVAD_A2DP_SIDEBAND +class A2dpHpDevice; // Forward declaration. +#endif // SYSVAD_A2DP_SIDEBAND /////////////////////////////////////////////////////////////////////////////// // CAdapterCommon @@ -299,6 +308,11 @@ class CAdapterCommon : ); #endif // SYSVAD_USB_SIDEBAND +#ifdef SYSVAD_A2DP_SIDEBAND + STDMETHODIMP_(NTSTATUS) InitA2dpSideband(); + + STDMETHODIMP_(VOID) CleanupA2dpSideband(); +#endif // SYSVAD_A2DP_SIDEBAND STDMETHODIMP_(VOID) Cleanup(); @@ -313,10 +327,7 @@ class CAdapterCommon : _Out_ PUNKNOWN * Unknown, _In_ REFCLSID, _In_opt_ PUNKNOWN UnknownOuter, - _When_((PoolType & NonPagedPoolMustSucceed) != 0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) - _In_ POOL_TYPE PoolType + _In_ POOL_FLAGS PoolFlags ); #ifdef SYSVAD_BTH_BYPASS @@ -396,6 +407,43 @@ class CAdapterCommon : #endif // SYSVAD_USB_SIDEBAND +#ifdef SYSVAD_A2DP_SIDEBAND + //===================================================================== + // A2DP Sideband Audio support. + + private: + PVOID m_A2dpSidebandNotificationHandle; + FAST_MUTEX m_A2dpSidebandFastMutex; // To serialize access. + WDFWORKITEM m_A2dpSidebandWorkItem; // Async work-item. + LIST_ENTRY m_A2dpSidebandWorkTasks; // Work-item's tasks. + LIST_ENTRY m_A2dpSidebandDevices; // A2DP Sideband devices. + NPAGED_LOOKASIDE_LIST m_A2dpSidebandWorkTaskPool; // LookasideList + size_t m_A2dpSidebandWorkTaskPoolElementSize; + BOOL m_A2dpSidebandEnableCleanup; // Do cleanup if true. + + private: + static + DRIVER_NOTIFICATION_CALLBACK_ROUTINE EvtA2dpSidebandInterfaceChange; + + static + EVT_WDF_WORKITEM EvtA2dpSidebandInterfaceWorkItem; + + protected: + A2dpHpDevice * A2dpSidebandDeviceFind + ( + _In_ PUNICODE_STRING SymbolicLinkName + ); + + NTSTATUS A2dpSidebandInterfaceArrival + ( + _In_ PUNICODE_STRING SymbolicLinkName + ); + + NTSTATUS A2dpSidebandInterfaceRemoval + ( + _In_ PUNICODE_STRING SymbolicLinkName + ); +#endif // SYSVAD_A2DP_SIDEBAND private: @@ -505,10 +553,7 @@ NewAdapterCommon _Out_ PUNKNOWN * Unknown, _In_ REFCLSID, _In_opt_ PUNKNOWN UnknownOuter, - _When_((PoolType & NonPagedPoolMustSucceed) != 0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) - _In_ POOL_TYPE PoolType + _In_ POOL_FLAGS PoolFlags ) /*++ @@ -550,7 +595,7 @@ Return Value: // // Allocate an adapter object. // - CAdapterCommon *p = new(PoolType, MINADAPTER_POOLTAG) CAdapterCommon(UnknownOuter); + CAdapterCommon *p = new(PoolFlags, MINADAPTER_POOLTAG) CAdapterCommon(UnknownOuter); if (p == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; @@ -733,6 +778,11 @@ Return Value: m_UsbSidebandEnableCleanup = FALSE; #endif // SYSVAD_USB_SIDEBAND +#ifdef SYSVAD_A2DP_SIDEBAND + m_A2dpSidebandEnableCleanup = FALSE; +#endif // SYSVAD_A2DP_SIDEBAND + + m_pServiceGroupWave = NULL; m_pDeviceObject = DeviceObject; @@ -777,7 +827,7 @@ Return Value: // Initialize HW. // - m_pHW = new (NonPagedPoolNx, SYSVAD_POOLTAG) CSYSVADHW; + m_pHW = new (POOL_FLAG_NON_PAGED, SYSVAD_POOLTAG) CSYSVADHW; if (!m_pHW) { DPF(D_TERSE, ("Insufficient memory for SYSVAD HW")); @@ -1772,7 +1822,7 @@ Return Value: &miniport, MiniportClassId, NULL, - NonPagedPoolNx, + POOL_FLAG_NON_PAGED, adapterCommon, DeviceContext, MiniportPair @@ -2175,7 +2225,7 @@ CAdapterCommon::CacheSubdevice NTSTATUS ntStatus = STATUS_SUCCESS; MINIPAIR_UNKNOWN *pNewSubdevice = NULL; - pNewSubdevice = new(NonPagedPoolNx, MINADAPTER_POOLTAG) MINIPAIR_UNKNOWN; + pNewSubdevice = new(POOL_FLAG_NON_PAGED, MINADAPTER_POOLTAG) MINIPAIR_UNKNOWN; if (!pNewSubdevice) { @@ -2300,6 +2350,13 @@ CAdapterCommon::Cleanup() CleanupUsbSideband(); #endif // SYSVAD_USB_SIDEBAND +#ifdef SYSVAD_A2DP_SIDEBAND + // + // This ensures A2DP Sideband notifications are turned off when port class + // cleanups and unregisters the static subdevices. + // + CleanupA2dpSideband(); +#endif // SYSVAD_A2DP_SIDEBAND EmptySubdeviceCache(); } @@ -2310,6 +2367,8 @@ CAdapterCommon::Cleanup() NTSTATUS CAdapterCommon::UpdatePowerRelations(_In_ PIRP Irp) { + PAGED_CODE(); + PDEVICE_RELATIONS priorRelations = NULL; PDEVICE_RELATIONS newRelations = NULL; ULONG qprPdosCount = 0; @@ -2354,7 +2413,7 @@ CAdapterCommon::UpdatePowerRelations(_In_ PIRP Irp) // space for the extra PDEVICE_OBJECTs we need. // size = sizeof(DEVICE_RELATIONS) + (count - 1) * sizeof(PDEVICE_OBJECT); - newRelations = (PDEVICE_RELATIONS)ExAllocatePoolWithTag(PagedPool, size, USBSIDEBANDTEST_POOLTAG015); + newRelations = (PDEVICE_RELATIONS)ExAllocatePool2(POOL_FLAG_PAGED, size, USBSIDEBANDTEST_POOLTAG015); ASSERT(newRelations); if (NULL == newRelations) { @@ -2367,7 +2426,6 @@ CAdapterCommon::UpdatePowerRelations(_In_ PIRP Irp) // If there was an existing device relations structure, copy // the entries to the new structure. // - RtlZeroMemory(newRelations, size); if (priorRelations != NULL && priorRelations->Count > 0) { size = sizeof(DEVICE_RELATIONS) + (priorRelations->Count - 1) * sizeof(PDEVICE_OBJECT); @@ -2974,7 +3032,7 @@ Return Value: // // Alloc a new structure for this Bluetooth hands-free device. // - bthDevice = new (NonPagedPoolNx, MINADAPTER_POOLTAG) BthHfpDevice(NULL); // NULL -> OuterUnknown + bthDevice = new (POOL_FLAG_NON_PAGED, MINADAPTER_POOLTAG) BthHfpDevice(NULL); // NULL -> OuterUnknown if (NULL == bthDevice) { DPF(D_ERROR, ("BthHfpScoInterfaceArrival: unable to allocate BthHfpDevice, out of memory")); @@ -3582,7 +3640,7 @@ NT status code. // // Alloc a new structure for this USB device. // - usbHsDevice = new (NonPagedPoolNx, MINADAPTER_POOLTAG) UsbHsDevice(NULL); // NULL -> OuterUnknown + usbHsDevice = new (POOL_FLAG_NON_PAGED, MINADAPTER_POOLTAG) UsbHsDevice(NULL); // NULL -> OuterUnknown if (NULL == usbHsDevice) { DPF(D_ERROR, ("UsbSidebandInterfaceArrival: unable to allocate UsbSidebandDevice, out of memory")); @@ -3931,7 +3989,7 @@ CAdapterCommon::AddDeviceAsPowerDependency NTSTATUS status = STATUS_SUCCESS; // allocate SysVadPowerRelationsDo - PSysVadPowerRelationsDo powerDepDo = (PSysVadPowerRelationsDo)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(SysVadPowerRelationsDo), USBSIDEBANDTEST_POOLTAG014); + PSysVadPowerRelationsDo powerDepDo = (PSysVadPowerRelationsDo)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(SysVadPowerRelationsDo), USBSIDEBANDTEST_POOLTAG014); if (NULL == powerDepDo) { status = STATUS_INSUFFICIENT_RESOURCES; @@ -4064,6 +4122,637 @@ Cleanup the USB Sideband environment. } #endif // SYSVAD_USB_SIDEBAND +#ifdef SYSVAD_A2DP_SIDEBAND +// +// CAdapterCommon A2DP Sideband function implementation. +// + +//============================================================================= +#pragma code_seg("PAGE") +VOID +CAdapterCommon::EvtA2dpSidebandInterfaceWorkItem +( + _In_ WDFWORKITEM WorkItem +) +/*++ + +Routine Description: + +The function handles the arrival or removal of a A2DP Sideband interface. + +Arguments: + +WorkItem - WDF work-item object. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[EvtA2dpSidebandInterfaceWorkItem]")); + + CAdapterCommon * This; + + if (WorkItem == NULL) + { + return; + } + + This = GetA2dpHpWorkItemContext(WorkItem)->Adapter; + ASSERT(This != NULL); + + for (;;) + { + PLIST_ENTRY le = NULL; + A2dpHpWorkTask * task = NULL; + + // + // Retrieve a taask. + // + ExAcquireFastMutex(&This->m_A2dpSidebandFastMutex); + if (!IsListEmpty(&This->m_A2dpSidebandWorkTasks)) + { + le = RemoveHeadList(&This->m_A2dpSidebandWorkTasks); + task = CONTAINING_RECORD(le, A2dpHpWorkTask, ListEntry); + InitializeListHead(le); + } + ExReleaseFastMutex(&This->m_A2dpSidebandFastMutex); + + if (task == NULL) + { + break; + } + + ASSERT(task->Device != NULL); + _Analysis_assume_(task->Device != NULL); + + // + // Process the task. + // + switch (task->Action) + { + case eA2dpHpTaskStart: + task->Device->Start(); + break; + + case eA2dpHpTaskStop: + task->Device->Stop(); + break; + + default: + DPF(D_ERROR, ("EvtA2dpSidebandInterfaceWorkItem: invalid action %d", task->Action)); + break; + } + + // + // Release the ref we took on the device when we inserted the task in the queue. + // For a stop operation this may be the last reference. + // + SAFE_RELEASE(task->Device); + + // + // Free the task. + // + ExFreeToNPagedLookasideList(&This->m_A2dpSidebandWorkTaskPool, task); + } +} + +//============================================================================= +#pragma code_seg("PAGE") +A2dpHpDevice * +CAdapterCommon::A2dpSidebandDeviceFind +( + _In_ PUNICODE_STRING SymbolicLinkName +) +/*++ + +Routine Description: + +The function looks for the specified device in the adapter's list. + +Arguments: + +SymbolicLinkName - interface's symbolic link. + +Return Value: + +A2dpSidebandDevice pointer or NULL. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::A2dpSidebandDeviceFind]")); + + PLIST_ENTRY le = NULL; + A2dpHpDevice * a2dpDevice = NULL; + + ExAcquireFastMutex(&m_A2dpSidebandFastMutex); + + for (le = m_A2dpSidebandDevices.Flink; le != &m_A2dpSidebandDevices; le = le->Flink) + { + A2dpHpDevice * tmpA2dpHpDevice = A2dpHpDevice::GetA2dpHpDevice(le); + ASSERT(tmpA2dpHpDevice != NULL); + + PUNICODE_STRING unicodeStr = tmpA2dpHpDevice->GetSymbolicLinkName(); + ASSERT(unicodeStr != NULL); + + if (unicodeStr->Length == SymbolicLinkName->Length && + 0 == wcsncmp(unicodeStr->Buffer, SymbolicLinkName->Buffer, unicodeStr->Length / sizeof(WCHAR))) + { + // Found it! + a2dpDevice = tmpA2dpHpDevice; + a2dpDevice->AddRef(); + break; + } + } + + ExReleaseFastMutex(&m_A2dpSidebandFastMutex); + + return a2dpDevice; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::A2dpSidebandInterfaceArrival +( + _In_ PUNICODE_STRING SymbolicLinkName +) +/*++ + +Routine Description: + +The function handles the arrival of a new A2DP Sideband interface. + +Arguments: + +SymbolicLinkName - new interface's symbolic link. + +Return Value: + +NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::A2dpSidebandInterfaceArrival]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + A2dpHpDevice *a2dpHpDevice = NULL; + A2dpHpWorkTask *a2dpHpWorkTask = NULL; + + DPF(D_VERBOSE, ("A2dpSidebandInterfaceArrival: SymbolicLinkName %wZ", SymbolicLinkName)); + + // + // Check if the A2DP device is already present. + // According to the docs it is possible to receive two notifications for the same + // interface. + // + a2dpHpDevice = A2dpSidebandDeviceFind(SymbolicLinkName); + if (a2dpHpDevice != NULL) + { + DPF(D_VERBOSE, ("A2dpSidebandInterfaceArrival: A2DP device already present")); + SAFE_RELEASE(a2dpHpDevice); + ntStatus = STATUS_SUCCESS; + goto Done; + } + + // + // Alloc a new structure for this A2DP device. + // + a2dpHpDevice = new (POOL_FLAG_NON_PAGED, MINADAPTER_POOLTAG) A2dpHpDevice(NULL); // NULL -> OuterUnknown + if (NULL == a2dpHpDevice) + { + DPF(D_ERROR, ("A2dpSidebandInterfaceArrival: unable to allocate A2dpSidebandDevice, out of memory")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + DPF(D_VERBOSE, ("A2dpSidebandInterfaceArrival: created A2dpSidebandDevice 0x%p ", a2dpHpDevice)); + + // + // Basic initialization of the A2DP Sideband interface. + // The audio miniport creation is done later by the A2dpSidebandDevice.Start() + // which is invoked asynchronously by a worker thread. + // A2dpSidebandDevice->Init() must be invoked just after the creation of the object. + // + ntStatus = a2dpHpDevice->Init(this, SymbolicLinkName); + IF_FAILED_JUMP(ntStatus, Done); + + // + // Get and init a work task. + // + a2dpHpWorkTask = (A2dpHpWorkTask*)ExAllocateFromNPagedLookasideList(&m_A2dpSidebandWorkTaskPool); + if (NULL == a2dpHpWorkTask) + { + DPF(D_ERROR, ("A2dpSidebandInterfaceArrival: unable to allocate A2dpSidebandWorkTask, out of memory")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + // a2dpWorkTask->L.Size is set to sizeof(A2dpSidebandWorkTask) in the Look Aside List configuration +#pragma warning(suppress: 6386) + RtlZeroMemory(a2dpHpWorkTask, sizeof(*a2dpHpWorkTask)); + a2dpHpWorkTask->Action = eA2dpHpTaskStart; + InitializeListHead(&a2dpHpWorkTask->ListEntry); + // Note that a2dpDevice has one reference at this point. + a2dpHpWorkTask->Device = a2dpHpDevice; + + ExAcquireFastMutex(&m_A2dpSidebandFastMutex); + + // + // Insert this new A2DP Sideband device in our list. + // + InsertTailList(&m_A2dpSidebandDevices, a2dpHpDevice->GetListEntry()); + + // + // Add a new task for the worker thread. + // + InsertTailList(&m_A2dpSidebandWorkTasks, &a2dpHpWorkTask->ListEntry); + a2dpHpDevice->AddRef(); // released when task runs. + + // + // Schedule a work-item if not already running. + // + WdfWorkItemEnqueue(m_A2dpSidebandWorkItem); + + ExReleaseFastMutex(&m_A2dpSidebandFastMutex); + +Done: + if (!NT_SUCCESS(ntStatus)) + { + // Release the last ref, this will delete the A2dpSidebandDevice + SAFE_RELEASE(a2dpHpDevice); + + if (a2dpHpWorkTask != NULL) + { + ExFreeToNPagedLookasideList(&m_A2dpSidebandWorkTaskPool, a2dpHpWorkTask); + a2dpHpWorkTask = NULL; + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::A2dpSidebandInterfaceRemoval +( + _In_ PUNICODE_STRING SymbolicLinkName +) +/*++ + +Routine Description: + +The function handles the removal of a A2DP Sideband interface. + +Arguments: + +SymbolicLinkName - interface's symbolic link to remove. + +Return Value: + +NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::A2dpSidebandInterfaceRemoval]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + A2dpHpDevice *a2dpHpDevice = NULL; + A2dpHpWorkTask *a2dpHpWorkTask = NULL; + + DPF(D_VERBOSE, ("A2dpSidebandInterfaceRemoval: SymbolicLinkName %wZ", SymbolicLinkName)); + + // + // Check if the A2DP device is present. + // + a2dpHpDevice = A2dpSidebandDeviceFind(SymbolicLinkName); + if (a2dpHpDevice == NULL) + { + // This can happen if the init/start of the A2dpSidebandDevice failed. + DPF(D_VERBOSE, ("A2dpSidebandInterfaceRemoval: A2DP device not found")); + ntStatus = STATUS_SUCCESS; + goto Done; + } + + // + // Init a work task. + // + a2dpHpWorkTask = (A2dpHpWorkTask*)ExAllocateFromNPagedLookasideList(&m_A2dpSidebandWorkTaskPool); + if (NULL == a2dpHpWorkTask) + { + DPF(D_ERROR, ("A2dpSidebandInterfaceRemoval: unable to allocate A2dpSidebandWorkTask, out of memory")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + // a2dpWorkTask->L.Size is set to sizeof(A2dpSidebandWorkTask) in the Look Aside List configuration +#pragma warning(suppress: 6386) + RtlZeroMemory(a2dpHpWorkTask, sizeof(*a2dpHpWorkTask)); + a2dpHpWorkTask->Action = eA2dpHpTaskStop; + InitializeListHead(&a2dpHpWorkTask->ListEntry); + // Work-item callback will release the reference we got above from A2dpSidebandDeviceFind. + a2dpHpWorkTask->Device = a2dpHpDevice; + + ExAcquireFastMutex(&m_A2dpSidebandFastMutex); + + // + // Remove this A2DP device from our list and release the associated reference. + // + RemoveEntryList(a2dpHpDevice->GetListEntry()); + InitializeListHead(a2dpHpDevice->GetListEntry()); + a2dpHpDevice->Release(); // This is not the last ref. + + // + // Add a new task for the worker thread. + // + InsertTailList(&m_A2dpSidebandWorkTasks, &a2dpHpWorkTask->ListEntry); + + // + // Schedule a work-item if not already running. + // + WdfWorkItemEnqueue(m_A2dpSidebandWorkItem); + + ExReleaseFastMutex(&m_A2dpSidebandFastMutex); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + if (!NT_SUCCESS(ntStatus)) + { + // Release the ref we got in find. + SAFE_RELEASE(a2dpHpDevice); + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::EvtA2dpSidebandInterfaceChange( + _In_ PVOID NotificationPointer, + _Inout_opt_ PVOID Context +) +/*++ + +Routine Description: + +This callback is invoked when a new A2DP Sideband interface is added or removed. + +Arguments: +NotificationPointer - Interface change notification +Context - CAdapterCommon ptr. + +Return Value: + +NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[EvtA2dpSidebandInterfaceChange]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + CAdapterCommon * This = NULL; + PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification = (PDEVICE_INTERFACE_CHANGE_NOTIFICATION)NotificationPointer; + + // + // Make sure this is the interface class we extect. Any other class guid + // is an error, but let it go since it is not fatal to the machine. + // + if (!IsEqualGUID(Notification->InterfaceClassGuid, GUID_DEVINTERFACE_A2DP_SIDEBAND_AUDIO)) + { + DPF(D_VERBOSE, ("EvtA2dpSidebandInterfaceChange: bad interface ClassGuid")); + ASSERTMSG("EvtA2dpSidebandInterfaceChange: bad interface ClassGuid ", FALSE); + + goto Done; + } + + This = (CAdapterCommon *)Context; + ASSERT(This != NULL); + _Analysis_assume_(This != NULL); + + // + // Take action based on the event. Any other event type is an error, + // but let it go since it is not fatal to the machine. + // + if (IsEqualGUID(Notification->Event, GUID_DEVICE_INTERFACE_ARRIVAL)) + { + ntStatus = This->A2dpSidebandInterfaceArrival(Notification->SymbolicLinkName); + } + else if (IsEqualGUID(Notification->Event, GUID_DEVICE_INTERFACE_REMOVAL)) + { + ntStatus = This->A2dpSidebandInterfaceRemoval(Notification->SymbolicLinkName); + } + else + { + DPF(D_VERBOSE, ("EvtA2dpSidebandInterfaceChange: bad " + "GUID_DEVINTERFACE_A2DP_SIDEBAND_AUDIO event")); + ASSERTMSG("EvtA2dpSidebandInterfaceChange: bad " + "GUID_DEVINTERFACE_A2DP_SIDEBAND_AUDIO event ", FALSE); + + goto Done; + } + +Done: + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::InitA2dpSideband() +/*++ + +Routine Description: + +Initialize the A2DP Sideband environment. + +Return Value: + +NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::InitA2dpSideband]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + WDF_WORKITEM_CONFIG wiConfig; + WDF_OBJECT_ATTRIBUTES attributes; + A2dpHpWorkItemContext * wiContext; + DECLARE_CONST_UNICODE_STRING(a2dpSidebandSupportInterface, L"A2dpSidebandSupport"); + UNICODE_STRING sidebandSupportRefString; + RtlInitUnicodeString(&sidebandSupportRefString, NULL); + + // + // Init spin-lock, linked lists, work-item, event, etc. + // Init all members to default values. This basic init should not fail. + // + m_A2dpSidebandWorkItem = NULL; + m_A2dpSidebandNotificationHandle = NULL; + ExInitializeFastMutex(&m_A2dpSidebandFastMutex); + InitializeListHead(&m_A2dpSidebandWorkTasks); + InitializeListHead(&m_A2dpSidebandDevices); + m_A2dpSidebandWorkTaskPoolElementSize = sizeof(A2dpHpWorkTask); + ExInitializeNPagedLookasideList(&m_A2dpSidebandWorkTaskPool, + NULL, + NULL, + POOL_NX_ALLOCATION, + m_A2dpSidebandWorkTaskPoolElementSize, + MINADAPTER_POOLTAG, + 0); + // + // Enable A2DP Sideband Cleanup. + // Do any allocation/initialization that can fail after this point. + // + m_A2dpSidebandEnableCleanup = TRUE; + + // + // Allocate a WDF work-item. + // + WDF_WORKITEM_CONFIG_INIT(&wiConfig, EvtA2dpSidebandInterfaceWorkItem); + wiConfig.AutomaticSerialization = FALSE; + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, A2dpHpWorkItemContext); + attributes.ParentObject = GetWdfDevice(); + ntStatus = WdfWorkItemCreate(&wiConfig, + &attributes, + &m_A2dpSidebandWorkItem); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("InitA2dpSideband: WdfWorkItemCreate failed: 0x%x", ntStatus)), + Done); + + wiContext = GetA2dpHpWorkItemContext(m_A2dpSidebandWorkItem); + wiContext->Adapter = this; // weak ref. + + // + // Publish A2DP Sideband support interface. + // + ntStatus = IoRegisterDeviceInterface( + GetPhysicalDeviceObject(), + &GUID_SIDEBANDAUDIO_A2DP_SUPPORT_INTERFACE, + (PUNICODE_STRING)&a2dpSidebandSupportInterface, + &sidebandSupportRefString); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("InitA2dpSideband: IoRegisterDeviceInterface(GUID_SIDEBANDAUDIO_A2DP_SUPPORT_INTERFACE) failed: 0x%x", ntStatus)), + Done); + + ntStatus = IoSetDeviceInterfaceState(&sidebandSupportRefString, TRUE); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("InitA2dpSideband: IoSetDeviceInterfaceState failed: 0x%x", ntStatus)), + Done); + + // + // Register for A2DP Sideband interface changes. + // + ntStatus = IoRegisterPlugPlayNotification( + EventCategoryDeviceInterfaceChange, + PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, + (PVOID)&GUID_DEVINTERFACE_A2DP_SIDEBAND_AUDIO, + m_pDeviceObject->DriverObject, + EvtA2dpSidebandInterfaceChange, + (PVOID)this, + &m_A2dpSidebandNotificationHandle); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("InitA2dpSideband: IoRegisterPlugPlayNotification(GUID_DEVINTERFACE_A2DP_SIDEBAND_AUDIO) failed: 0x%x", ntStatus)), + Done); + + // + // Initialization completed. + // + ntStatus = STATUS_SUCCESS; + +Done: + RtlFreeUnicodeString(&sidebandSupportRefString); + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +CAdapterCommon::CleanupA2dpSideband() +/*++ + +Routine Description: + +Cleanup the A2DP Sideband environment. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::CleanupA2dpSideband]")); + + // + // Do nothing if A2DP Sideband environment was not correctly initialized. + // + if (m_A2dpSidebandEnableCleanup == FALSE) + { + return; + } + + // + // Unregister for A2DP Sideband interface changes. + // + if (m_A2dpSidebandNotificationHandle != NULL) + { + (void)IoUnregisterPlugPlayNotificationEx(m_A2dpSidebandNotificationHandle); + m_A2dpSidebandNotificationHandle = NULL; + } + + // + // Wait for the A2DP Sideband worker thread to be done. + // + if (m_A2dpSidebandWorkItem != NULL) + { + WdfWorkItemFlush(m_A2dpSidebandWorkItem); + WdfObjectDelete(m_A2dpSidebandWorkItem); + m_A2dpSidebandWorkItem = NULL; + } + + ASSERT(IsListEmpty(&m_A2dpSidebandWorkTasks)); + + // + // Stop and delete all A2dpSidebandDevices. We are the only thread accessing this list, + // so there is no need to acquire the mutex. + // + while (!IsListEmpty(&m_A2dpSidebandDevices)) + { + A2dpHpDevice * a2dpHpDevice = NULL; + PLIST_ENTRY le = NULL; + + le = RemoveHeadList(&m_A2dpSidebandDevices); + + a2dpHpDevice = A2dpHpDevice::GetA2dpHpDevice(le); + InitializeListHead(le); + + // a2dpDevice is invalid after this call. + a2dpHpDevice->Stop(); + + // This should be the last reference. + a2dpHpDevice->Release(); + } + + ASSERT(IsListEmpty(&m_A2dpSidebandDevices)); + + // + // General cleanup. + // + ExDeleteNPagedLookasideList(&m_A2dpSidebandWorkTaskPool); +} +#endif // SYSVAD_A2DP_SIDEBAND + #pragma code_seg("PAGE") NTSTATUS @@ -4089,7 +4778,7 @@ Return Value: PAGED_CODE(); // Allocate the KEY_VALUE_FULL_INFORMATION structure ulFullInfoLength = sizeof(KEY_VALUE_FULL_INFORMATION) + MAX_DEVICE_REG_KEY_LENGTH; - kvFullInfo = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePoolWithTag(NonPagedPoolNx, ulFullInfoLength, MINADAPTER_POOLTAG); + kvFullInfo = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, ulFullInfoLength, MINADAPTER_POOLTAG); IF_TRUE_ACTION_JUMP(kvFullInfo == NULL, ntStatus = STATUS_INSUFFICIENT_RESOURCES, Exit); // Iterate over each value and copy it to the destination @@ -4109,7 +4798,7 @@ Return Value: ulFullInfoLength = ulFullInfoResultLength; - kvFullInfo = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePoolWithTag(NonPagedPoolNx, ulFullInfoLength, MINADAPTER_POOLTAG); + kvFullInfo = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, ulFullInfoLength, MINADAPTER_POOLTAG); IF_TRUE_ACTION_JUMP(kvFullInfo == NULL, ntStatus = STATUS_INSUFFICIENT_RESOURCES, loop_exit); // Try to enumerate the current value again @@ -4125,7 +4814,7 @@ Return Value: } // Allocate the key value name string - pwstrKeyValueName = (PWSTR)ExAllocatePoolWithTag(NonPagedPoolNx, kvFullInfo->NameLength + sizeof(WCHAR)*2, MINADAPTER_POOLTAG); + pwstrKeyValueName = (PWSTR)ExAllocatePool2(POOL_FLAG_NON_PAGED, kvFullInfo->NameLength + sizeof(WCHAR)*2, MINADAPTER_POOLTAG); IF_TRUE_ACTION_JUMP(kvFullInfo == NULL, ntStatus = STATUS_INSUFFICIENT_RESOURCES, loop_exit); // Copy the key value name from the full information struct @@ -4196,7 +4885,7 @@ Return Value: // Allocate the KEY_BASIC_INFORMATION structure ulBasicInfoLength = sizeof(KEY_BASIC_INFORMATION) + MAX_DEVICE_REG_KEY_LENGTH; - kBasicInfo = (PKEY_BASIC_INFORMATION)ExAllocatePoolWithTag(NonPagedPoolNx, ulBasicInfoLength, MINADAPTER_POOLTAG); + kBasicInfo = (PKEY_BASIC_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, ulBasicInfoLength, MINADAPTER_POOLTAG); IF_TRUE_ACTION_JUMP(kBasicInfo == NULL, ntStatus = STATUS_INSUFFICIENT_RESOURCES, Exit); ntStatus = STATUS_SUCCESS; @@ -4215,7 +4904,7 @@ Return Value: // Free and re-allocate the KEY_BASIC_INFORMATION structure with the correct size. ExFreePoolWithTag(kBasicInfo, MINADAPTER_POOLTAG); ulBasicInfoLength = ulBasicInfoResultLength; - kBasicInfo = (PKEY_BASIC_INFORMATION)ExAllocatePoolWithTag(NonPagedPoolNx, ulBasicInfoLength, MINADAPTER_POOLTAG); + kBasicInfo = (PKEY_BASIC_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, ulBasicInfoLength, MINADAPTER_POOLTAG); IF_TRUE_ACTION_JUMP(kBasicInfo == NULL, ntStatus = STATUS_INSUFFICIENT_RESOURCES, loop_exit); // Try to enumerate the current key again. @@ -4231,7 +4920,7 @@ Return Value: } // Allocate the key name string - pwstrKeyName = (PWSTR)ExAllocatePoolWithTag(NonPagedPoolNx, kBasicInfo->NameLength + sizeof(WCHAR), MINADAPTER_POOLTAG); + pwstrKeyName = (PWSTR)ExAllocatePool2(POOL_FLAG_NON_PAGED, kBasicInfo->NameLength + sizeof(WCHAR), MINADAPTER_POOLTAG); IF_TRUE_ACTION_JUMP(kBasicInfo == NULL, ntStatus = STATUS_INSUFFICIENT_RESOURCES, loop_exit); // Copy the key name from the basic information struct @@ -4338,6 +5027,8 @@ Return Value: --*/ { + PAGED_CODE(); + NTSTATUS ntStatus = STATUS_SUCCESS; HANDLE hDeviceInterfaceParametersKey(NULL); HANDLE hTemplateDeviceInterfaceParametersKey(NULL); diff --git a/audio/sysvad/common.h b/audio/sysvad/common.h index 8ce0a866..17e8ebca 100644 --- a/audio/sysvad/common.h +++ b/audio/sysvad/common.h @@ -135,6 +135,14 @@ Abstract: #define USBSIDEBANDTEST_POOLTAG013 'dAyS' #define USBSIDEBANDTEST_POOLTAG014 'eAyS' #define USBSIDEBANDTEST_POOLTAG015 'fAyS' +#define USBSIDEBANDTEST_POOLTAG016 'gAyS' +#define A2DPSIDEBANDTEST_POOLTAG01 'hAyS' +#define A2DPSIDEBANDTEST_POOLTAG02 'iAyS' +#define A2DPSIDEBANDTEST_POOLTAG03 'jAyS' +#define A2DPSIDEBANDTEST_POOLTAG04 'kAyS' +#define A2DPSIDEBANDTEST_POOLTAG05 'lAyS' +#define A2DPSIDEBANDTEST_POOLTAG06 'mAyS' +#define A2DPSIDEBANDTEST_POOLTAG07 'nAyS' typedef enum { @@ -206,6 +214,10 @@ typedef struct _PIN_DEVICE_FORMATS_AND_MODES } PIN_DEVICE_FORMATS_AND_MODES, *PPIN_DEVICE_FORMATS_AND_MODES; +typedef struct _SYSVAD_AUDIOPOSTURE_INFO +{ + BOOL OrientationSupported; +} SYSVAD_AUDIOPOSTURE_INFO, *PSYSVAD_AUDIOPOSTURE_INFO; // // Parameter module handler function prototypes. @@ -336,10 +348,7 @@ typedef HRESULT (*PFNCREATEMINIPORT)( _Out_ PUNKNOWN * Unknown, _In_ REFCLSID, _In_opt_ PUNKNOWN UnknownOuter, - _When_((PoolType & NonPagedPoolMustSucceed) != 0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) - _In_ POOL_TYPE PoolType, + _In_ POOL_FLAGS PoolFlags, _In_ PUNKNOWN UnknownAdapter, _In_opt_ PVOID DeviceContext, _In_ PENDPOINT_MINIPAIR MiniportPair @@ -668,6 +677,10 @@ DECLARE_INTERFACE_(IAdapterCommon, IUnknown) #endif // SYSVAD_USB_SIDEBAND +#ifdef SYSVAD_A2DP_SIDEBAND + STDMETHOD_(NTSTATUS, InitA2dpSideband)(); + +#endif // SYSVAD_A2DP_SIDEBAND STDMETHOD_(VOID, Cleanup)(); @@ -699,10 +712,7 @@ NewAdapterCommon _Out_ PUNKNOWN * Unknown, _In_ REFCLSID, _In_opt_ PUNKNOWN UnknownOuter, - _When_((PoolType & NonPagedPoolMustSucceed) != 0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) - _In_ POOL_TYPE PoolType + _In_ POOL_FLAGS PoolFlags ); @@ -879,6 +889,7 @@ DECLARE_INTERFACE_(ISidebandDeviceCommon, IUnknown) _In_ eDeviceType deviceType ) PURE; + _IRQL_requires_max_(DISPATCH_LEVEL) STDMETHOD_(BOOL, IsNRECSupported) ( THIS_ diff --git a/audio/sysvad/savedata.cpp b/audio/sysvad/savedata.cpp index 7e60cf4d..cb5f5cfc 100644 --- a/audio/sysvad/savedata.cpp +++ b/audio/sysvad/savedata.cpp @@ -510,9 +510,9 @@ CSaveData::Initialize { m_FileName.MaximumLength = (USHORT)((cLen * sizeof(WCHAR)) + sizeof(WCHAR));//convert to wchar and add room for NULL m_FileName.Buffer = (PWSTR) - ExAllocatePoolWithTag + ExAllocatePool2 ( - PagedPool, + POOL_FLAG_PAGED, m_FileName.MaximumLength, SAVEDATA_POOLTAG3 ); @@ -532,9 +532,9 @@ CSaveData::Initialize DPF(D_BLAB, ("[New DataFile -- %S", m_FileName.Buffer)); m_pDataBuffer = (PBYTE) - ExAllocatePoolWithTag + ExAllocatePool2 ( - NonPagedPoolNx, + POOL_FLAG_NON_PAGED, m_ulBufferSize, SAVEDATA_POOLTAG4 ); @@ -543,10 +543,6 @@ CSaveData::Initialize DPF(D_TERSE, ("[Could not allocate memory for Saving Data]")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } - else - { - RtlZeroMemory(m_pDataBuffer, m_ulBufferSize); - } } // Allocate memory for frame usage flags and m_pFilePtr. @@ -554,9 +550,9 @@ CSaveData::Initialize if (NT_SUCCESS(ntStatus)) { m_fFrameUsed = (PBOOL) - ExAllocatePoolWithTag + ExAllocatePool2 ( - NonPagedPoolNx, + POOL_FLAG_NON_PAGED, m_ulFrameCount * sizeof(BOOL) + sizeof(LARGE_INTEGER), SAVEDATA_POOLTAG2 @@ -584,7 +580,6 @@ CSaveData::Initialize // m_pFilePtr = (PLARGE_INTEGER) (((PBYTE) m_fFrameUsed) + m_ulFrameCount * sizeof(BOOL)); - RtlZeroMemory(m_fFrameUsed, m_ulFrameCount * sizeof(BOOL) + sizeof(LARGE_INTEGER)); // Create data file. InitializeObjectAttributes @@ -646,9 +641,9 @@ CSaveData::InitializeWorkItems } m_pWorkItems = (PSAVEWORKER_PARAM) - ExAllocatePoolWithTag + ExAllocatePool2 ( - NonPagedPoolNx, + POOL_FLAG_NON_PAGED, sizeof(SAVEWORKER_PARAM) * MAX_WORKER_ITEM_COUNT, SAVEDATA_POOLTAG ); @@ -775,9 +770,9 @@ CSaveData::SetDataFormat } m_waveFormat = (PWAVEFORMATEX) - ExAllocatePoolWithTag + ExAllocatePool2 ( - NonPagedPoolNx, + POOL_FLAG_NON_PAGED, (pwfx->wFormatTag == WAVE_FORMAT_PCM) ? sizeof( PCMWAVEFORMAT ) : sizeof( WAVEFORMATEX ) + pwfx->cbSize, @@ -830,9 +825,9 @@ CSaveData::SetMaxWriteSize // Alloc memory for buffer. // buffer = (PBYTE) - ExAllocatePoolWithTag + ExAllocatePool2 ( - NonPagedPoolNx, + POOL_FLAG_NON_PAGED, bufferSize, SAVEDATA_POOLTAG4 ); @@ -843,8 +838,6 @@ CSaveData::SetMaxWriteSize goto Done; } - RtlZeroMemory(buffer, bufferSize); - // // Free old one. // diff --git a/audio/sysvad/sysvad.sln b/audio/sysvad/sysvad.sln index 6a58b6df..39041665 100644 --- a/audio/sysvad/sysvad.sln +++ b/audio/sysvad/sysvad.sln @@ -1,7 +1,6 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27703.2042 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31424.327 MinimumVisualStudioVersion = 12.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Package", "Package", "{FA56D96B-290B-4C73-A5E1-02C1861D3224}" EndProject @@ -11,7 +10,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EndpointsCommon", "Endpoint EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TabletAudioSample", "TabletAudioSample", "{C06F6070-75EE-4F57-8D35-4AC2AB08D029}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "package\package.VcxProj", "{830B14D5-0E32-4F9E-AEFA-4C9F6FC13C2A}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{830B14D5-0E32-4F9E-AEFA-4C9F6FC13C2A}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeywordDetectorContosoAdapter", "KeywordDetectorAdapter\KeywordDetectorContosoAdapter.vcxproj", "{E0F02048-78A4-4FE8-B863-66E6CB6A2C37}" EndProject @@ -30,6 +29,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SwapAPO", "APO\SwapAPO\Swap EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KwsAPO", "APO\KWSApo\KwsAPO.vcxproj", "{47358AD6-A48A-465B-965F-0A66F8BDFE23}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AecAPO", "APO\AecApo\AecAPO.vcxproj", "{83A1B9B0-3CB7-4249-ADEC-C336B0EDDE76}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 @@ -124,6 +125,18 @@ Global {47358AD6-A48A-465B-965F-0A66F8BDFE23}.Release|Win32.Build.0 = Release|Win32 {47358AD6-A48A-465B-965F-0A66F8BDFE23}.Release|x64.ActiveCfg = Release|x64 {47358AD6-A48A-465B-965F-0A66F8BDFE23}.Release|x64.Build.0 = Release|x64 + {83A1B9B0-3CB7-4249-ADEC-C336B0EDDE76}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {83A1B9B0-3CB7-4249-ADEC-C336B0EDDE76}.Debug|ARM64.Build.0 = Debug|ARM64 + {83A1B9B0-3CB7-4249-ADEC-C336B0EDDE76}.Debug|Win32.ActiveCfg = Debug|Win32 + {83A1B9B0-3CB7-4249-ADEC-C336B0EDDE76}.Debug|Win32.Build.0 = Debug|Win32 + {83A1B9B0-3CB7-4249-ADEC-C336B0EDDE76}.Debug|x64.ActiveCfg = Debug|x64 + {83A1B9B0-3CB7-4249-ADEC-C336B0EDDE76}.Debug|x64.Build.0 = Debug|x64 + {83A1B9B0-3CB7-4249-ADEC-C336B0EDDE76}.Release|ARM64.ActiveCfg = Release|ARM64 + {83A1B9B0-3CB7-4249-ADEC-C336B0EDDE76}.Release|ARM64.Build.0 = Release|ARM64 + {83A1B9B0-3CB7-4249-ADEC-C336B0EDDE76}.Release|Win32.ActiveCfg = Release|Win32 + {83A1B9B0-3CB7-4249-ADEC-C336B0EDDE76}.Release|Win32.Build.0 = Release|Win32 + {83A1B9B0-3CB7-4249-ADEC-C336B0EDDE76}.Release|x64.ActiveCfg = Release|x64 + {83A1B9B0-3CB7-4249-ADEC-C336B0EDDE76}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -136,6 +149,7 @@ Global {8851AB85-70B3-4798-A149-BA366DF9EC25} = {D86F9A10-C48D-4048-A03C-DD7BCA6E165F} {691F1D0F-CA50-46CD-8BBA-A59E9E8EB601} = {D86F9A10-C48D-4048-A03C-DD7BCA6E165F} {47358AD6-A48A-465B-965F-0A66F8BDFE23} = {D86F9A10-C48D-4048-A03C-DD7BCA6E165F} + {83A1B9B0-3CB7-4249-ADEC-C336B0EDDE76} = {D86F9A10-C48D-4048-A03C-DD7BCA6E165F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6DB4FE19-4FE0-4DC4-B898-C46E01E6BDF4} diff --git a/audio/tests/KSPosTst/KsPosTestTaef.cpp b/audio/tests/KSPosTst/KsPosTestTaef.cpp index b92f9e4f..6ac6329e 100644 --- a/audio/tests/KSPosTst/KsPosTestTaef.cpp +++ b/audio/tests/KSPosTst/KsPosTestTaef.cpp @@ -14,6 +14,7 @@ #include "PreComp.h" #include "KsPosTestTaef.h" #include "tests.h" +#include <TestResourceHelper.h> using namespace WEX::Common; using namespace WEX::Logging; @@ -113,10 +114,18 @@ bool WDMAudio::KsPosTest::KsPosTestSetup() g_pKsPosTst = this; m_pTimer = &g_Timer; // The histograms can be displayed by using the command line parameter checked below - // If the parameter is not specified, the default is to not log anything + // If the parameter is not specified, the default is to log everything. bool param = true; WEX::TestExecution::RuntimeParameters::TryGetValue(L"logHistograms", param); m_fLogHistograms = param; + + // Get the QPC frequency for timing on the tests. + // This can be done here as the frequency does not change. + LARGE_INTEGER liFrequency; + QueryPerformanceFrequency(&liFrequency); + m_qpcFrequency = liFrequency.QuadPart; + LOG(g_pBasicLog, "Frequency: %llu", m_qpcFrequency); + return true; } @@ -168,6 +177,14 @@ bool WDMAudio::KsPosTest::TestCaseSetup() LogWaveFormat(m_pHalf->m_pCurrentFormat.get()); } + // Get a HNS reference from the begining of the test so the HNS from system and driver times won't be so large + LARGE_INTEGER liNow; + QueryPerformanceCounter(&liNow); + + // Convert QPC to microseconds and then HNS. This is to prevent overflows from converting directly to HNS. + m_testBaseHns = (liNow.QuadPart * MICROSECONDS_PER_SECOND) / m_qpcFrequency; + m_testBaseHns *= HNSTIME_UNITS_PER_MICROSECOND; + return true; } @@ -176,6 +193,9 @@ bool WDMAudio::KsPosTest::TestCaseCleanUp() // Stop the timer here, in case any latency test fails NtSetTimerResolution(0, FALSE, &actualTimerResolution); + // Attempt to unregister the thread with MMCSS just in case the test failed before doing so. + m_threadPriority.UnRegisterWithMMCSS(); + if (!CompareWaveFormat(m_pHalf->m_pCurrentFormat.get(), m_pHalf->m_pPreferredFormat.get())) { CloneWaveFormat(m_pHalf->m_pPreferredFormat.get(), wil::out_param(m_pHalf->m_pCurrentFormat)); m_pHalf->m_u32CurrentDefaultPeriodicityInFrames = m_pHalf->m_u32DefaultPeriodicityInFrames; @@ -204,6 +224,12 @@ bool WDMAudio::KsPosTest::TestCaseCleanUp() } // ------------------------------------------------------------------------------- +void WDMAudio::KsPosTestPreTest::TAEF_VerifyAllEndpointsPluggedIn() +{ + VerifyAllEndpointsPluggedIn(); +} + +// ------------------------------------------------------------------------------- void WDMAudio::KsPosTest::TAEF_DriftAndJitter() { RUN_TEST_CASE(Test_DriftAndJitter_Default); diff --git a/audio/tests/KSPosTst/KsPosTestTaef.h b/audio/tests/KSPosTst/KsPosTestTaef.h new file mode 100644 index 00000000..e0c6e862 --- /dev/null +++ b/audio/tests/KSPosTst/KsPosTestTaef.h @@ -0,0 +1,119 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// +// File Name: +// +// KsPosTestTaef.h +// +// Abstract: +// +// Include file for KsPosTestTaef +// +// ------------------------------------------------------------------------------- +#pragma once + +#include <HalfApp.h> + +extern IBasicLog* g_pBasicLog; + +// Define custom test resource properties +namespace WEX { + namespace TestExecution { + namespace TestResourceProperty + { + static const wchar_t c_szMode[] = L"Mode"; + static const wchar_t c_szPin[] = L"Pin"; + } + } +} + +namespace WDMAudio +{ + class KsPosTestPreTest + { + BEGIN_TEST_CLASS(KsPosTestPreTest) + TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client x86") + TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client x64") + TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client ARM") + TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client ARM64") + TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Server x64") + TEST_CLASS_PROPERTY(L"Kits.MinRelease", L"19H1") + TEST_CLASS_PROPERTY(L"Kits.CorePackageComposition", L"Full") + TEST_CLASS_PROPERTY(L"Kits.CorePackageComposition", L"OnecoreUAP") + TEST_CLASS_PROPERTY(L"Kits.IsInProc", L"True") + TEST_CLASS_PROPERTY(L"Kits.PublishingOrganization", L"Microsoft Corporation") + TEST_CLASS_PROPERTY(L"Kits.TestType", L"Development") + TEST_CLASS_PROPERTY(L"Kits.DevelopmentPhase", L"Development and Integration") + TEST_CLASS_PROPERTY(L"ThreadingModel", L"MTA") + END_TEST_CLASS() + + BEGIN_TEST_METHOD(TAEF_VerifyAllEndpointsPluggedIn) + TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature") + END_TEST_METHOD() + }; + + class KsPosTest + { + BEGIN_TEST_CLASS(KsPosTest) + TEST_CLASS_PROPERTY(L"Kits.CorePackageComposition", L"Full") + TEST_CLASS_PROPERTY(L"Kits.CorePackageComposition", L"OnecoreUAP") + TEST_CLASS_PROPERTY(L"Kits.DevelopmentPhase", L"Development and Integration") + TEST_CLASS_PROPERTY(L"Kits.IsInProc", L"True") + TEST_CLASS_PROPERTY(L"Kits.MinRelease", L"19H1") + TEST_CLASS_PROPERTY(L"Kits.Parameter", L"DeviceID") + TEST_CLASS_PROPERTY(L"Kits.Parameter.DeviceID.Description", L"Device id of device under test") + TEST_CLASS_PROPERTY(L"Kits.Parameter", L"DriverVerifierCustomizeConfiguration") + TEST_CLASS_PROPERTY(L"Kits.Parameter.DriverVerifierCustomizeConfiguration.Description", L"Specifies that this test may want to automatically update Driver Verifier settings") + TEST_CLASS_PROPERTY(L"Kits.Parameter.DriverVerifierCustomizeConfiguration.Default", L"True") + TEST_CLASS_PROPERTY(L"Kits.Parameter.DriverVerifierCustomizeConfiguration.IsUserSettable", L"False") + TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client x86") + TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client x64") + TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client ARM") + TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client ARM64") + TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Server x64") + TEST_CLASS_PROPERTY(L"Kits.PublishingOrganization", L"Microsoft Corporation") + TEST_CLASS_PROPERTY(L"Kits.TestType", L"Development") + TEST_CLASS_PROPERTY(L"Subfeature", L"Wave Driver Tests") + TEST_CLASS_PROPERTY(L"ThreadingModel", L"MTA") + + + + + + + + END_TEST_CLASS() + + TEST_CLASS_SETUP(KsPosTestSetup) + TEST_CLASS_CLEANUP(KsPosTestCleanUp) + + TEST_METHOD_SETUP(TestCaseSetup) + TEST_METHOD_CLEANUP(TestCaseCleanUp) + + BEGIN_TEST_METHOD(TAEF_DriftAndJitter) + TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional") + TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification") + TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature") + TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*HOST*' OR @Id = '*LOOPBACK*'") + END_TEST_METHOD() + + BEGIN_TEST_METHOD(TAEF_Latency) + TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional") + TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification") + TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature") + TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*HOST*' OR @Id = '*LOOPBACK*'") + END_TEST_METHOD() + + public: + CHalfApp* m_pHalf; + CHalfApp* m_pHostHalf; // HalfApp corresponding to the host pin of a loopback pin, if any + PTIMER_MECHANISM m_pTimer; + BOOL m_fLogHistograms; + LONGLONG m_qpcFrequency; // QPC frequency. + LONGLONG m_testBaseHns; // Base time, in HNS, used inside the tests to make QPC times smaller. + CRtThreadRegistration m_threadPriority; + }; +} + +extern WDMAudio::KsPosTest* g_pKsPosTst; diff --git a/audio/tests/KSPosTst/PreComp.h b/audio/tests/KSPosTst/PreComp.h index 5fdfc9db..5e7d834d 100644 --- a/audio/tests/KSPosTst/PreComp.h +++ b/audio/tests/KSPosTst/PreComp.h @@ -42,26 +42,24 @@ - - - - - - - - // Number of milliseconds that the test will be delayed to wait for the stream to stabilize #define DELAY_FOR_STABILIZE 100 // Number of milliseconds in a second #define MILLISECONDS_PER_SECOND 1000 +// Number of microseconds in a second +#define MICROSECONDS_PER_SECOND 1000000 + // Number of HNS units in one second #define HNSTIME_UNITS_PER_SECOND 10000000 // Number of HNS units in one millisecond #define HNSTIME_UNITS_PER_MILLISECOND 10000 +// Number of HNS units per microsecond +#define HNSTIME_UNITS_PER_MICROSECOND 10 + typedef double (*TIMEPROC) (void); double tpQPC(void); diff --git a/audio/tests/KSPosTst/pintest.cpp b/audio/tests/KSPosTst/pintest.cpp index 57e35b07..be659896 100644 --- a/audio/tests/KSPosTst/pintest.cpp +++ b/audio/tests/KSPosTst/pintest.cpp @@ -49,10 +49,13 @@ HRESULT CollectSampleData UINT64 u64Position; UINT64 positionHNS; UINT64 u64Frequency; - double testStartHns; LARGE_INTEGER liNow; - LARGE_INTEGER liFrequency; - QueryPerformanceFrequency(&liFrequency); + PLONGLONG frequency = &g_pKsPosTst->m_qpcFrequency; + PLONGLONG pBaseHns = &g_pKsPosTst->m_testBaseHns; + CRtThreadRegistration* threadPriority = &g_pKsPosTst->m_threadPriority; + + // Register with MMCSS + threadPriority->RegisterWithMMCSS(); VERIFY_SUCCEEDED(pHalfApp->InitializeEndpoint()); VERIFY_SUCCEEDED(pHalfApp->CreateSineToneDataBuffer(pHalfApp->m_pCurrentFormat.get())); @@ -61,10 +64,6 @@ HRESULT CollectSampleData // run the adapter sink pin VERIFY_SUCCEEDED(pHalfApp->StartStream()); - // Get a HNS reference from the begining of the test so the HNS from system and driver times won't be so large - QueryPerformanceCounter(&liNow); - testStartHns = (double)liNow.QuadPart * HNSTIME_UNITS_PER_SECOND / (double)liFrequency.QuadPart; - tStart = xGetTime(); Sleep(DELAY_FOR_STABILIZE); // Get the stream frequency in order to determine the time offset @@ -85,10 +84,12 @@ HRESULT CollectSampleData // only count this result if the GetPosition and GetTime happened relatively atomically if ((tPost - tPre) < 1.) { - double testHNS = (double)liNow.QuadPart * HNSTIME_UNITS_PER_SECOND / (double)liFrequency.QuadPart; + // Convert QPC to microseconds and then HNS. This is to prevent overflows from converting directly to HNS. + UINT64 testHNS = liNow.QuadPart * MICROSECONDS_PER_SECOND / *frequency; + testHNS *= HNSTIME_UNITS_PER_MICROSECOND; // In both sampling rate and jitter calculation, the time used is in milliseconds, so we can convert it right here - pPosSet[i].testTime = HNSTIME_TO_MILLISECONDS_DOUBLE(testHNS - testStartHns); - pPosSet[i].positionTime = HNSTIME_TO_MILLISECONDS_DOUBLE(positionHNS - testStartHns); + pPosSet[i].testTime = HNSTIME_TO_MILLISECONDS_DOUBLE(testHNS - *pBaseHns); + pPosSet[i].positionTime = HNSTIME_TO_MILLISECONDS_DOUBLE(positionHNS - *pBaseHns); // We store the values as seconds because it is easier to convert to bytes per second, on sampling rate calculation, and milliseconds, on jitter calculation. pPosSet[i].position = (double)u64Position / (double)u64Frequency; @@ -117,6 +118,9 @@ HRESULT CollectSampleData VERIFY_SUCCEEDED(pHalfApp->StopStream()); + // Unregister with MMCSS + threadPriority->UnRegisterWithMMCSS(); + // Did we get enough data? VERIFY_IS_TRUE(pResults->cPosSets >= 30); @@ -310,7 +314,7 @@ void Test_DriftAndJitter_Main results.perfResults.argPosSets = pPositionSet.get(); // collect the data ~~~~~~~~~~~~~~~~~ - VERIFY_SUCCEEDED(CollectSampleData(pHalfApp, &results, requestedPeriodicity)); + IF_FAILED_JUMP(CollectSampleData(pHalfApp, &results, requestedPeriodicity), Log); // Stop and cleanup the host stream since we are done collecting samples if (isLoopbackPin) @@ -322,10 +326,31 @@ void Test_DriftAndJitter_Main } // calculate drift ~~~~~~~~~~~~~~~~~~ - VERIFY_SUCCEEDED(CalculateSampleRate(&results)); + IF_FAILED_JUMP(CalculateSampleRate(&results), Log); // calculate Jitter ~~~~~~~~~~~~~~~~~ VERIFY_SUCCEEDED(CalculateJitter(&results)); + + return; + +Log: + // If the drift test or the collection of samples fail, + // there will be no logging of data (from inside the jitter test). + // So, log whatever available values here. + if (g_pKsPosTst->m_fLogHistograms) + { + PERF_RESULTS* data = &results.perfResults; + LOG(g_pBasicLog, "Report Reported Position"); + LOG(g_pBasicLog, "time position timestamp"); + LOG(g_pBasicLog, "(ms) (s) (ms)"); + LOG(g_pBasicLog, "========== ========== =========="); + for (UINT i = 0; i < data->cPosSets; i++) + { + LOG(g_pBasicLog, "%10.2f, %10.2f, %10.2f", data->argPosSets[i].testTime, data->argPosSets[i].position, data->argPosSets[i].positionTime); + } + } + + ERR(g_pBasicLog, "Drift and jitter test failed"); } // ------------------------------------------------------------------------------ @@ -367,6 +392,8 @@ void Test_Latency_Main (HNSTIME requestedPeriodicity) CHalfApp* pHostHalfApp = g_pKsPosTst->m_pHostHalf; NTSTATUS lTimerResStatus; BOOL isLoopbackPin = pHalfApp->m_ConnectorType == eLoopbackConnector; + PLONGLONG qpcFrequency = &g_pKsPosTst->m_qpcFrequency; + CRtThreadRegistration* threadPriority = &g_pKsPosTst->m_threadPriority; // Test will ignore Bluetooth devices until proper thresholds are in place. if (pHalfApp->m_bIsBluetooth) @@ -383,6 +410,9 @@ void Test_Latency_Main (HNSTIME requestedPeriodicity) return; } + // Register with MMCSS + threadPriority->RegisterWithMMCSS(); + // If it is a loopback pin, get the host pin and start the stream if (isLoopbackPin) { @@ -414,9 +444,7 @@ void Test_Latency_Main (HNSTIME requestedPeriodicity) double tPosPre = 0.0; double tPosPost = 0.0; - LARGE_INTEGER liFrequency = { 0 }; LARGE_INTEGER timeStamp = { 0 }; - QueryPerformanceFrequency(&liFrequency); // mark the time double tRunPre = xGetTime(); @@ -439,17 +467,27 @@ void Test_Latency_Main (HNSTIME requestedPeriodicity) tPosPost = xGetTime(); if (g_pKsPosTst->m_fLogHistograms) { - LOG(g_pBasicLog, "Testing first non-zero position reported: %u samples, %u HNS at %.03f ms", u64Position, driverHns, tPosPost - tRunPre); + LOG(g_pBasicLog, "Testing first non-zero position reported: %llu samples, %llu HNS at %.03f ms", u64Position, driverHns, tPosPost - tRunPre); } - // Fail if the cursor does not move within 500 ms - VERIFY_IS_TRUE((tPosPost - tRunPost) < 500.0); + // Warn if the cursor does not move within 500 ms. Do not fail the test as this might be because the test overslept. + if ((tPosPost - tRunPost) >= 500.0) + { + WARN(g_pBasicLog, "Pin did not report a position in 500 ms"); + break; + } } // Before stopping, get the frequency of the stream UINT64 u64Frequency; VERIFY_SUCCEEDED(pHalfApp->m_pAudioClock->GetFrequency(&u64Frequency)); + // Stop the endpoint + VERIFY_SUCCEEDED(pHalfApp->StopStream()); + + // Unregister with MMCSS + threadPriority->UnRegisterWithMMCSS(); + // the uncertainty in time is the max possible value minus the min possible value divided by 2 (for +/- purposes) double tMax = tPosPost - tRunPre; double tMin = tPosPre - tRunPost; @@ -460,19 +498,28 @@ void Test_Latency_Main (HNSTIME requestedPeriodicity) double tOffset = MILLISECONDS_PER_SECOND * (double)u64Position / (double)u64Frequency; // Calculate the difference in time of the position time versus the test time - UINT64 testHns = 10000000 * timeStamp.QuadPart / liFrequency.QuadPart; - INT64 timeDiff = testHns - driverHns; + // Convert QPC to microseconds and then HNS. This is to prevent overflows from converting directly to HNS. + UINT64 testHns = MICROSECONDS_PER_SECOND * timeStamp.QuadPart / *qpcFrequency; + testHns *= HNSTIME_UNITS_PER_MICROSECOND; + + // Make the HNS times smaller to avoid overflows (or underflows) + PLONGLONG pBaseHns = &g_pKsPosTst->m_testBaseHns; + INT64 adjustedTestHns = (INT64)(testHns - *pBaseHns); + INT64 adjustedDriverHns = (INT64)(driverHns - *pBaseHns); + + INT64 timeDiff = adjustedTestHns - adjustedDriverHns; // Compensate the positon to take this difference into account tOffset += (double)timeDiff / HNSTIME_UNITS_PER_MILLISECOND; if (g_pKsPosTst->m_fLogHistograms) { - LOG(g_pBasicLog, "Times (HNS): %u, %u", testHns, driverHns); - LOG(g_pBasicLog, "Time difference: %d HNS (%f ms)", timeDiff, (double)timeDiff / HNSTIME_UNITS_PER_MILLISECOND); - LOG(g_pBasicLog, "First non-zero position = %u at %g ms", u64Position, tAve); + LOG(g_pBasicLog, "Base test time: %lld HNS", *pBaseHns); + LOG(g_pBasicLog, "Times (HNS): %llu, %llu", testHns, driverHns); + LOG(g_pBasicLog, "Time difference: %lld HNS (%f ms)", timeDiff, (double)timeDiff / HNSTIME_UNITS_PER_MILLISECOND); + LOG(g_pBasicLog, "First non-zero position = %llu at %g ms", u64Position, tAve); LOG(g_pBasicLog, "Time offset / max time: %f ms / %f ms", tOffset, tMax); - LOG(g_pBasicLog, "Frequency: %lu", u64Frequency); + LOG(g_pBasicLog, "Frequency: %llu", u64Frequency); } double tLatency; @@ -485,16 +532,21 @@ void Test_Latency_Main (HNSTIME requestedPeriodicity) } LOG(g_pBasicLog, "Adjusted latency = %.03f ms (+/- %.3f ms)", tLatency, tUncertainty); - // if the reported position is outside the max time for this call (see tUncertainty calculation above), then fail - // temp fix until we know how far can be the data in front of the - // real time because of buffering - VERIFY_IS_FALSE(tOffset > tMax); + // In case the position reported by the pin (tOffset) is ahead of the run time of the pin (tMax), + // assume that position might have jitter and apply the propper limits instead of failing the test. + if (tOffset > tMax + JITTER_STD_THRESHOLD) + { + WARN(g_pBasicLog, "Pin's time offset is greater than the time it has been in the RUN state. This will become an error in the future."); + return; + } // Verify that the latency is within the threshold - VERIFY_IS_TRUE(tLatency <= LATENCY_THRESHOLD); // ms + if (tLatency > LATENCY_THRESHOLD) + { + WARN(g_pBasicLog, "The latency exceeds the acceptable threshold. This will become an error in the future."); + return; + } - // Stop the endpoint - VERIFY_SUCCEEDED(pHalfApp->StopStream()); // If this was a loopback pin, stop the host stream also if (isLoopbackPin) { diff --git a/audio/tests/KSPosTst/tests.h b/audio/tests/KSPosTst/tests.h deleted file mode 100644 index ec0a68cc..00000000 --- a/audio/tests/KSPosTst/tests.h +++ /dev/null @@ -1,18 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Copyright (C) Microsoft Corporation. All rights reserved. -// -// Module Name: -// -// tests.h -// -// Abstract: -// -// Header for testcases -// -// ------------------------------------------------------------------------------- -#pragma once - -void Test_DriftAndJitter_Default(void); - -void Test_Latency_Default(void); diff --git a/audio/tests/PinResourceHelpers/HalfApp.cpp b/audio/tests/PinResourceHelpers/HalfApp.cpp index 83c9c8f3..154c1e9a 100644 --- a/audio/tests/PinResourceHelpers/HalfApp.cpp +++ b/audio/tests/PinResourceHelpers/HalfApp.cpp @@ -1,6 +1,6 @@ // ------------------------------------------------------------------------------ // -// Copyright (C) Microsoft. All rights reserved. +// Copyright (C) Microsoft Corporation. All rights reserved. // // Module Name: // diff --git a/audio/tests/PinResourceHelpers/HalfApp.h b/audio/tests/PinResourceHelpers/HalfApp.h new file mode 100644 index 00000000..a30bdf50 --- /dev/null +++ b/audio/tests/PinResourceHelpers/HalfApp.h @@ -0,0 +1,233 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft. All rights reserved. +// +// File Name: +// +// HalfApp.h +// +// Abstract: +// +// CHalfApp declarations +// +// ------------------------------------------------------------------------------- +#pragma once + +#define BUF_LEN_IN_MS 1000 // 1 second + +// ------------------------------------------------------------------------------ +// Data flow +enum STACKWISE_DATAFLOW { render, capture }; + + +// ------------------------------------------------------------------------------ +// Structs used to hold results of format records +typedef struct +{ + WAVEFORMATEXTENSIBLE wfxEx; + UINT32 fundamentalPeriodInFrames; + UINT32 defaultPeriodInFrames; + UINT32 minPeriodInFrames; + UINT32 maxPeriodInFrames; + UINT32 maxPeriodInFramesExtended; +} FORMAT_RECORD, * PFORMAT_RECORD; + +struct DeviceDescriptor +{ + IMMDevice* pDevice; + LPWSTR pwstrAudioEndpointId; + LPWSTR pwstrAudioEndpointFriendlyName; + STACKWISE_DATAFLOW dataFlow; + EndpointConnectorType eConnectorType; + UINT uConnectorId; + GUID mode; + ULONG cModes; + GUID* pModes; + ULONG cFormatRecords; + PFORMAT_RECORD pFormatRecords; + PWAVEFORMATEX pPreferredFormat; + UINT32 u32DefaultPeriodicityInFrames; + UINT32 u32FundamentalPeriodicityInFrames; + UINT32 u32MinPeriodicityInFrames; + UINT32 u32MaxPeriodicityInFrames; + bool bIsPortCls; + bool bIsAVStream; + bool bIsBluetooth; + bool bIsSideband; + bool bIsMVA; +}; + +class CHalfApp +{ +public: + // Basic pin infos + wil::com_ptr_nothrow<IMMDevice> m_pDevice; + wil::unique_cotaskmem_string m_pwstrDeviceId; + wil::unique_cotaskmem_string m_pwstrDeviceFriendlyName; + STACKWISE_DATAFLOW m_DataFlow; + EndpointConnectorType m_ConnectorType; + UINT m_uConnectorId; + GUID m_Mode; + ULONG m_cModes; + GUID* m_pModes; // All processing modes + bool m_bIsPortCls; + bool m_bIsAVStream; + bool m_bIsBluetooth; + bool m_bIsSideband; + bool m_bIsMVA; + + // Formats + wil::unique_cotaskmem_ptr<WAVEFORMATEX> m_pPreferredFormat; + UINT32 m_u32DefaultPeriodicityInFrames; + UINT32 m_u32FundamentalPeriodicityInFrames; + UINT32 m_u32MinPeriodicityInFrames; + UINT32 m_u32MaxPeriodicityInFrames; + + PFORMAT_RECORD m_pFormatRecords; + ULONG m_cFormatRecords; + + wil::unique_cotaskmem_ptr<WAVEFORMATEX> m_pCurrentFormat; + UINT32 m_u32CurrentDefaultPeriodicityInFrames; + UINT32 m_u32CurrentFundamentalPeriodicityInFrames; + UINT32 m_u32CurrentMinPeriodicityInFrames; + UINT32 m_u32CurrentMaxPeriodicityInFrames; + + // Audio endpoint interfaces + wil::com_ptr_nothrow<IAudioDeviceEndpoint> m_pAudioDeviceEndpoint; + wil::com_ptr_nothrow<IAudioEndpoint> m_pAudioEndpoint; + wil::com_ptr_nothrow<IAudioEndpointControl> m_pAudioEndpointControl; + wil::com_ptr_nothrow<IAudioEndpointRT> m_pAudioEndpointRT; + wil::com_ptr_nothrow<IAudioClock> m_pAudioClock; + + // Data buffer + wil::unique_hlocal_ptr<BYTE> m_pbSineToneDataBuffer; + DWORD m_dwSineToneDataBufferSize; + DWORD m_dwSineToneDataBufferPosition; + + // Stream + HNSTIME m_hnsPeriod = 0; + FLOAT32 m_f32EndpointFrameRate = 0.F; + HNSTIME m_hnsEndpointLatency = 0; + bool m_bIsEventCapable = false; + bool m_bStreamInitialized = false; + volatile bool m_bStreamThreadTerminate = false; + bool m_bYieldActive = false; + bool m_bIsBackupTimerRequired = true; + + wil::unique_handle m_hStreamThread; + + LPTHREAD_START_ROUTINE m_pStreamRoutine = nullptr; + wil::unique_event_nothrow m_hProcessThreadStartedEvent; + wil::unique_event_nothrow m_hTerminate; + wil::unique_event_nothrow m_hEndpointBufferCompleteEvent; + wil::unique_handle m_hTimer; + + wil::critical_section m_CritSec; + + typedef enum + { + ET_DO_NOTHING = 0x00, + ET_TERMINATE = 0x01, + ET_OBJECT_BUFFER_COMPLETE = 0x02, + ET_CLIENT_RELEASE_BUFFER = 0x04, + ET_TIMER = 0x08, + ET_PAUSE_PUMP = 0x10, + ET_RESUME_PUMP = 0x20, + } TEventType; + + CHalfApp( + _In_ DeviceDescriptor descriptor + ) + : m_DataFlow(descriptor.dataFlow) + , m_ConnectorType(descriptor.eConnectorType) + , m_uConnectorId(descriptor.uConnectorId) + , m_cModes(descriptor.cModes) + , m_cFormatRecords(descriptor.cFormatRecords) + , m_u32DefaultPeriodicityInFrames(descriptor.u32DefaultPeriodicityInFrames) + , m_u32FundamentalPeriodicityInFrames(descriptor.u32FundamentalPeriodicityInFrames) + , m_u32MinPeriodicityInFrames(descriptor.u32MinPeriodicityInFrames) + , m_u32MaxPeriodicityInFrames(descriptor.u32MaxPeriodicityInFrames) + , m_u32CurrentDefaultPeriodicityInFrames(descriptor.u32DefaultPeriodicityInFrames) + , m_u32CurrentFundamentalPeriodicityInFrames(descriptor.u32FundamentalPeriodicityInFrames) + , m_u32CurrentMinPeriodicityInFrames(descriptor.u32MinPeriodicityInFrames) + , m_u32CurrentMaxPeriodicityInFrames(descriptor.u32MaxPeriodicityInFrames) + , m_bIsPortCls(descriptor.bIsPortCls) + , m_bIsAVStream(descriptor.bIsAVStream) + , m_bIsBluetooth(descriptor.bIsBluetooth) + , m_bIsSideband(descriptor.bIsSideband) + , m_bIsMVA(descriptor.bIsMVA) + { + m_pDevice = descriptor.pDevice; + m_pAudioDeviceEndpoint = nullptr; + m_pAudioEndpointControl = nullptr; + + m_pwstrDeviceId = wil::make_cotaskmem_string_nothrow(descriptor.pwstrAudioEndpointId, wcslen(descriptor.pwstrAudioEndpointId)); + m_pwstrDeviceFriendlyName = wil::make_cotaskmem_string_nothrow(descriptor.pwstrAudioEndpointFriendlyName, wcslen(descriptor.pwstrAudioEndpointFriendlyName)); + m_Mode = descriptor.mode; + + m_pModes = new GUID[descriptor.cModes]; + memcpy(m_pModes, descriptor.pModes, sizeof(GUID)*(descriptor.cModes)); + m_pFormatRecords = new FORMAT_RECORD[descriptor.cFormatRecords]; + memcpy(m_pFormatRecords, descriptor.pFormatRecords, sizeof(FORMAT_RECORD)*(descriptor.cFormatRecords)); + + CloneWaveFormat(descriptor.pPreferredFormat, wil::out_param(m_pPreferredFormat)); + CloneWaveFormat(descriptor.pPreferredFormat, wil::out_param(m_pCurrentFormat)); // Use preferred format as current format + } + + ~CHalfApp(void) + { + if (m_pModes) + delete[] m_pModes; + if (m_pFormatRecords) + delete[] m_pFormatRecords; + } + + // Audio endpoint interfaces + HRESULT InitializeEndpoint(); + HRESULT ReleaseEndpoint(); + HRESULT StartEndpoint(); + HRESULT StopEndpoint(); + HRESULT ResetEndpoint(); + + // Sine tone data buffer + HRESULT CreateSineToneDataBuffer(WAVEFORMATEX* pWfx); + HRESULT ReleaseSineToneDataBuffer(); + + + + + // Stream + HRESULT InitializeAndSetBuffer(HNSTIME hnsPeriod, UINT32 u32LatencyCoefficient); + HRESULT InitializeStream(HNSTIME requestedPeriodicity, UINT32 u32LatencyCoefficient); + HRESULT CleanupStream(); + HRESULT StartStream(); + HRESULT StopStream(); + static DWORD CALLBACK RenderStreamRoutine(PVOID lpParameter); + static DWORD CALLBACK CaptureStreamRoutine(PVOID lpParameter); + void SignalAndWaitForThread(); + + + + HRESULT SetTimer(HANDLE timer, HNSTIME timeDuration, bool fireImmediatley); + void CancelTimer(HANDLE timer); + HRESULT GetPosition(UINT64* pu64Position, UINT64* pu64hnsQPCPosition); + + // Multiple pin instances + HRESULT GetCurrentAvailiablePinInstanceCount(UINT32* pAvailablePinInstanceCount); + HRESULT GetSecondHalfApp(AUDIO_SIGNALPROCESSINGMODE secondMode, CHalfApp** ppSecondHalfApp); + + // Only for loopback testcases + HRESULT GetHostHalfApp(CHalfApp** ppHostHalfApp); +}; + +// {571BF784-A9D6-420A-BCA0-C579A8710236} +DEFINE_GUID(IID_IHalfAppContainer, + 0x571bf784, 0xa9d6, 0x420a, 0xbc, 0xa0, 0xc5, 0x79, 0xa8, 071, 0x02, 0x36); + +DECLARE_INTERFACE_IID_( +IHalfAppContainer, +IUnknown, +"571BF784-A9D6-420A-BCA0-C579A8710236") +{ + STDMETHOD(GetHalfApp)(THIS_ CHalfApp ** ppHalfApp) PURE; +}; diff --git a/audio/tests/PinResourceHelpers/PreComp.h b/audio/tests/PinResourceHelpers/PreComp.h index 85fc4f83..d75f0e3f 100644 --- a/audio/tests/PinResourceHelpers/PreComp.h +++ b/audio/tests/PinResourceHelpers/PreComp.h @@ -1,6 +1,6 @@ // ------------------------------------------------------------------------------ // -// Copyright (C) Microsoft. All rights reserved. +// Copyright (C) Microsoft Corporation. All rights reserved. // // File Name: // diff --git a/audio/tests/PinResourceHelpers/PropertyHelper.cpp b/audio/tests/PinResourceHelpers/PropertyHelper.cpp index 6e85e53e..1d5f4548 100644 --- a/audio/tests/PinResourceHelpers/PropertyHelper.cpp +++ b/audio/tests/PinResourceHelpers/PropertyHelper.cpp @@ -1,6 +1,6 @@ // ------------------------------------------------------------------------------ // -// Copyright (C) Microsoft. All rights reserved. +// Copyright (C) Microsoft Corporation. All rights reserved. // // Module Name: // @@ -16,6 +16,11 @@ #include <Functiondiscoverykeys_devpkey.h> #include <devpkey.h> #include <PropertyHelper.h> +#include <imagehlp.h> + +using namespace WEX::Common; +using namespace WEX::Logging; +using namespace WEX::TestExecution; /////////////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -1099,6 +1104,355 @@ HRESULT GetAvailiablePinInstanceCount /////////////////////////////////////////////////////////////////////////////////////////////////////////// // +// GetDriverPathViaService +// +// Get the full path to the driver .sys file by the service name +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT GetDriverPathViaService +( + LPWSTR ServiceName, + LPWSTR DriverFullPath, + UINT cchFullPath +) +{ + HRESULT hr = S_OK; + wil::unique_schandle scManager; + wil::unique_schandle scService; + + scManager = wil::unique_schandle(OpenSCManagerW(nullptr, nullptr, GENERIC_READ)); + if (!VERIFY_IS_NOT_NULL(scManager.get())) + { + hr = E_OUTOFMEMORY; + return hr; + } + + scService = wil::unique_schandle(OpenServiceW(scManager.get(), ServiceName, GENERIC_READ)); + if (!VERIFY_IS_NOT_NULL(scService.get())) + { + hr = E_OUTOFMEMORY; + return hr; + } + + DWORD BytesRequired; + if (!VERIFY_IS_TRUE(!QueryServiceConfigW(scService.get(), nullptr, 0, &BytesRequired) && ERROR_INSUFFICIENT_BUFFER == GetLastError())) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + return hr; + } + + wil::unique_cotaskmem_ptr<BYTE> pBuff; + pBuff.reset((BYTE*)CoTaskMemAlloc(BytesRequired)); + if (!VERIFY_IS_NOT_NULL(pBuff)) + { + hr = E_OUTOFMEMORY; + return hr; + } + + LPQUERY_SERVICE_CONFIGW ServiceConfig = (LPQUERY_SERVICE_CONFIGW)pBuff.get(); + DWORD BytesReturned; + + if (!VERIFY_IS_TRUE(QueryServiceConfigW(scService.get(), ServiceConfig, BytesRequired, &BytesReturned))) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + return hr; + } + + if (!VERIFY_IS_NOT_NULL(ServiceConfig->lpBinaryPathName)) + { + hr = E_UNEXPECTED; + return hr; + } + + if (!VERIFY_IS_TRUE('\0' != ServiceConfig->lpBinaryPathName[0])) + { + hr = E_UNEXPECTED; + return hr; + } + + if (!VERIFY_SUCCEEDED(hr = GetFullPathFromImagePath(ServiceConfig->lpBinaryPathName, DriverFullPath, cchFullPath))) + { + return hr; + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// GetFullPathFromImagePath +// +// Get the full driver path from the image path +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT GetFullPathFromImagePath +( + LPWSTR ImagePath, + LPWSTR DriverFullPath, + UINT cchFullPath +) +{ + HRESULT hr = S_OK; + // + // First, check if the ImagePath uses either of the well-known kernel + // DosDevices prefixes. If so, skip over those first since + // GetFileAttributes would succeed, leading us to think it is a valid DOS + // path, and GetFullPathName handles these incorrectly anyways. Don't + // bother handling the user DosDevices formats since it would be invalid to + // specify that format for the ImagePath of a kernel module. + // + LPWSTR pImagePath = (LPWSTR)ImagePath; + + const wchar_t* DosDevicesPath = L"\\DosDevices\\"; + const wchar_t* QuestionPath = L"\\??\\"; + if (_wcsnicmp(pImagePath, + DosDevicesPath, + static_cast<int>(wcslen(DosDevicesPath))) == 0) + { + pImagePath += wcslen(DosDevicesPath); + } + else if (_wcsnicmp(pImagePath, + QuestionPath, + static_cast<int>(wcslen(QuestionPath))) == 0) + { + pImagePath += wcslen(QuestionPath); + } + + // + // Check if the ImagePath happens to be a valid full path. + // + if (GetFileAttributesW(pImagePath) != 0xFFFFFFFF) + { + if (!VERIFY_IS_TRUE(GetFullPathNameW(pImagePath, cchFullPath, DriverFullPath, nullptr))) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + return hr; + } + return hr; + } + + // + // If the ImagePath starts with "\SystemRoot" or "%SystemRoot%" then + // remove those values. + // + LPWSTR pRelativePath = (LPWSTR)pImagePath; + + const wchar_t* SystemRootPath = L"\\SystemRoot\\"; + const wchar_t* SystemRootVariablePath = L"%SystemRoot%\\"; + + if (_wcsnicmp(pRelativePath, + SystemRootPath, + static_cast<int>(wcslen(SystemRootPath))) == 0) + { + pRelativePath += wcslen(SystemRootPath); + } + else if (_wcsnicmp(pRelativePath, + SystemRootVariablePath, + static_cast<int>(wcslen(SystemRootVariablePath))) == 0) + { + pRelativePath += wcslen(SystemRootVariablePath); + } + + // + // At this point pRelativePath should point to the image path relative to + // the windows directory. + // + WCHAR WindowsPath[MAX_PATH]; + if (!VERIFY_IS_TRUE(GetSystemWindowsDirectoryW(WindowsPath, MAX_PATH))) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + return hr; + } + + if (!VERIFY_SUCCEEDED(hr = StringCchPrintfW(DriverFullPath, cchFullPath, L"%s\\%s", WindowsPath, pRelativePath))) + { + return hr; + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// CheckImports +// +// Check if the driver imports a specific module +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT CheckImports +( + LPWSTR DriverPath, + LPSTR ModuleNameToCheck, + LPSTR MethodNameToCheck, + bool* pIsImported +) +{ + HRESULT hr = S_OK; + + *pIsImported = FALSE; + + CHAR szDriverPath[MAX_PATH]; + size_t bytesConverted = 0; + if (!VERIFY_IS_TRUE(0 == wcstombs_s(&bytesConverted, szDriverPath, MAX_PATH, DriverPath, _TRUNCATE))) + { + hr = E_FAIL; + return hr; + } + + LOADED_IMAGE image; + //Load the image + if (!VERIFY_IS_TRUE(MapAndLoad(szDriverPath, NULL, &image, TRUE, TRUE))) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + return hr; + } + + auto imageCleanup = wil::scope_exit([&]() + { + (VOID)UnMapAndLoad(&image); + }); + + //Get the Import Directory + ULONG importDescriptorSize; + PIMAGE_SECTION_HEADER sectionHeader; + PIMAGE_IMPORT_DESCRIPTOR importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToDataEx(image.MappedAddress, + FALSE, + IMAGE_DIRECTORY_ENTRY_IMPORT, + &importDescriptorSize, + §ionHeader); + if (!VERIFY_IS_NOT_NULL(importDescriptor)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + return hr; + } + + //Iterate through each directory entry + while (!(importDescriptor->Characteristics == 0 && + importDescriptor->TimeDateStamp == 0 && + importDescriptor->ForwarderChain == 0 && + importDescriptor->Name == 0 && + importDescriptor->FirstThunk == 0)) + { + CHAR* str = (PCHAR)ImageRvaToVa(ImageNtHeader(image.MappedAddress), + image.MappedAddress, + importDescriptor->Name, + NULL); + if (!str || !*str) + { + continue; + } + + if (_stricmp(str, ModuleNameToCheck) == 0) + { + // If a specific method name is provided, check for the specific method + if (MethodNameToCheck) + { + PIMAGE_THUNK_DATA thunk; + //Iterate through the INT(Import Name Table) + if (importDescriptor->OriginalFirstThunk == 0) + { + thunk = nullptr; + } + else + { + thunk = (PIMAGE_THUNK_DATA)ImageRvaToVa(ImageNtHeader(image.MappedAddress), + image.MappedAddress, + importDescriptor->OriginalFirstThunk, + NULL); + } + + while (thunk && thunk->u1.Ordinal != 0) + { + if (!IMAGE_SNAP_BY_ORDINAL(thunk->u1.Ordinal)) + { + PIMAGE_IMPORT_BY_NAME importName = (PIMAGE_IMPORT_BY_NAME)ImageRvaToVa(ImageNtHeader(image.MappedAddress), + image.MappedAddress, + (ULONG)thunk->u1.ForwarderString, + NULL); + if (importName && _stricmp(importName->Name, MethodNameToCheck) == 0) + { + *pIsImported = TRUE; + } + } + + thunk++; + } + } + else + { + *pIsImported = TRUE; + } + + break; + } + + importDescriptor++; + } + + return hr; + +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// IsPortCls +// +// Check if audio device is PortCls +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT IsPortCls +( + IMMDevice* pDevice, + bool* pIsPortCls +) +{ + HRESULT hr = S_OK; + wil::com_ptr_nothrow<IMMDevice> spDevnodeDevice; + wil::com_ptr_nothrow<IPropertyStore> spPnpProperties; + wil::unique_prop_variant varDeviceService; + + *pIsPortCls = false; + + // Find the associated PnP device + if (!VERIFY_SUCCEEDED(hr = GetPnpDevnodeFromMMDevice(pDevice, &spDevnodeDevice))) { + return hr; + } + + // Open pnp device property store + if (!VERIFY_SUCCEEDED(hr = spDevnodeDevice->OpenPropertyStore(STGM_READ, &spPnpProperties))) { + return hr; + } + + // Read the DEVPKEY_Device_Service + if (!VERIFY_SUCCEEDED(hr = spPnpProperties->GetValue((REFPROPERTYKEY)DEVPKEY_Device_Service, &varDeviceService))) { + return hr; + } + + // If DEVPKEY_Device_Service is empty, it likely means a raw PDO, which means the device is handled by the parent FDO. + // For practical purposes, this would most likely mean a non-PortCls pin, which is what we were trying to determine, so it is OK. + if (varDeviceService.vt == VT_EMPTY) { + return S_OK; + } + + if (!VERIFY_IS_TRUE(varDeviceService.vt == VT_LPWSTR && varDeviceService.pwszVal != nullptr)) { + hr = E_FAIL; + return hr; + } + + WCHAR FullPath[MAX_PATH]; + if (!VERIFY_SUCCEEDED(hr = GetDriverPathViaService(varDeviceService.pwszVal, FullPath, MAX_PATH))) { + return hr; + } + + if (!VERIFY_SUCCEEDED(hr = CheckImports(FullPath, "portcls.sys", "PcNewPort", pIsPortCls))) { + return hr; + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// // IsAVStream // // Check if audio device is AVStream @@ -1132,13 +1486,24 @@ HRESULT IsAVStream return hr; } + // If DEVPKEY_Device_Service is empty, it likely means a raw PDO, which means the device is handled by the parent FDO. + // For practical purposes, this would most likely mean a non-AVStream pin, which is what we were trying to determine, so it is OK. + if (varDeviceService.vt == VT_EMPTY) { + return S_OK; + } + if (!VERIFY_IS_TRUE(varDeviceService.vt == VT_LPWSTR && varDeviceService.pwszVal != nullptr)) { hr = E_FAIL; return hr; } - if (0 == _wcsicmp(varDeviceService.pwszVal, L"usbaudio") || 0 == _wcsicmp(varDeviceService.pwszVal, L"BthHFAud") || 0 == _wcsicmp(varDeviceService.pwszVal, L"BthA2dp")) { - *pIsAVStream = true; + WCHAR FullPath[MAX_PATH]; + if (!VERIFY_SUCCEEDED(hr = GetDriverPathViaService(varDeviceService.pwszVal, FullPath, MAX_PATH))) { + return hr; + } + + if (!VERIFY_SUCCEEDED(hr = CheckImports(FullPath, "ks.sys", nullptr, pIsAVStream))) { + return hr; } return hr; @@ -1222,6 +1587,78 @@ HRESULT IsSideband /////////////////////////////////////////////////////////////////////////////////////////////////////////// // +// IsMVA +// +// Check if audio device is side band +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT IsMVA +( + EndpointConnectorType eConnectorType, + IMMDevice* pDevice, + bool* pIsMVA +) +{ + HRESULT hr = S_OK; + wil::com_ptr_nothrow<IMMDevice> adapterDevice; + wil::com_ptr_nothrow<IKsControl> ksControl; + ULONG ulReturned = 0; + ULONG propertySupportFlags = 0; + KSSOUNDDETECTORPROPERTY vam2Property = {0}; + + if (!VERIFY_IS_TRUE(pDevice != nullptr)) { + hr = E_FAIL; + return hr; + } + + if (!VERIFY_IS_TRUE(pIsMVA != nullptr)) { + hr = E_FAIL; + return hr; + } + + // If it isn't a keyword connector, it's not MVA, we're done. + if (eConnectorType != eKeywordDetectorConnector) + { + *pIsMVA = FALSE; + return S_OK; + } + + if (!VERIFY_SUCCEEDED(hr = GetAudioFilterAsDevice(pDevice, &adapterDevice))) + { + return hr; + } + + if (!VERIFY_SUCCEEDED(hr = adapterDevice->Activate(__uuidof(IKsControl), CLSCTX_ALL, NULL, (VOID**) &ksControl))) { + return hr; + } + + // At this point we know that it is a keyword detector connector and we have the required interface to call into the driver + // to see if it's MVA. If it's not MVA, it's SVA, so mark it as SVA here. + *pIsMVA = false; + + vam2Property.Property.Set = KSPROPSETID_SoundDetector2; + vam2Property.Property.Id = KSPROPERTY_SOUNDDETECTOR_SUPPORTEDPATTERNS; + vam2Property.Property.Flags = KSPROPERTY_TYPE_BASICSUPPORT; + vam2Property.EventId = GUID_NULL; + + hr = ksControl->KsProperty( + (PKSPROPERTY)&vam2Property, + sizeof(vam2Property), + &propertySupportFlags, + sizeof(propertySupportFlags), + &ulReturned); + if (SUCCEEDED(hr) && + propertySupportFlags == (KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT)) + { + // Success, we've identified this as a connector which supports MVA, mark it. + *pIsMVA = true; + } + + return S_OK; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// // GetPnpDevnodeFromMMDeivce // // Get pnp devnode from mm device @@ -1261,3 +1698,46 @@ HRESULT GetPnpDevnodeFromMMDevice return hr; } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// VerifyAllEndpointsPluggedIn +// +// Verify there is no unplugged endpoint +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +void VerifyAllEndpointsPluggedIn() +{ + wil::com_ptr_nothrow<IMMDeviceEnumerator> spEnumerator; + wil::com_ptr_nothrow<IMMDeviceCollection> spEndpoints; + UINT cDevices = 0; + + SetVerifyOutput verifySettings(VerifyOutputSettings::LogOnlyFailures); + DisableVerifyExceptions disable; + + VERIFY_SUCCEEDED(::CoInitializeEx(NULL, COINIT_MULTITHREADED)); + + // Create IMMDevice Enumerator + VERIFY_SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void **)&spEnumerator)); + + // Enumerate all unplugged endpoints + VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(eAll, DEVICE_STATE_UNPLUGGED, &spEndpoints)); + VERIFY_SUCCEEDED(spEndpoints->GetCount(&cDevices)); + + if (!VERIFY_IS_TRUE(cDevices == 0)) + { + Log::Comment(L"Following unplugged audio device(s) found, please plug in unplugged device(s)."); + + for (UINT i = 0; i < cDevices; i++) { + wil::com_ptr_nothrow<IMMDevice> spEndpoint; + wil::unique_cotaskmem_string id; + wil::unique_cotaskmem_string friendlyName; + + VERIFY_SUCCEEDED(spEndpoints->Item(i, &spEndpoint)); + VERIFY_SUCCEEDED(spEndpoint->GetId(&id)); + VERIFY_SUCCEEDED(GetEndpointFriendlyName(spEndpoint.get(), &friendlyName)); + + Log::Comment(String().Format(L"Device: %s (%s) is unplugged.", friendlyName.get(), id.get())); + } + } +} diff --git a/audio/tests/PinResourceHelpers/PropertyHelper.h b/audio/tests/PinResourceHelpers/PropertyHelper.h new file mode 100644 index 00000000..5680283d --- /dev/null +++ b/audio/tests/PinResourceHelpers/PropertyHelper.h @@ -0,0 +1,260 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft. All rights reserved. +// +// Module Name: +// +// PropertyHelpers.h +// +// Abstract: +// +// Header for common helpers/defines of property +// +// ------------------------------------------------------------------------------- +#pragma once + +#include "HalfApp.h" + +#define kSystemDefaultPeriod 100000 // 10 milliseconds +#define kSystemMinPeriodForNonRTCapableEndpoints 20000 // 2 milliseconds + + + + + + + + + + + + + + + + + + + + + + + + + + + +// ---------------------------------------------------------------------- +HRESULT GetConnectorId +( + IMMDevice* pDevice, + EndpointConnectorType eConnectorType, + bool* hasConnector, + UINT* pConnectorId +); + +// ---------------------------------------------------------------------- +HRESULT GetEndpointFriendlyName +( + IMMDevice* pDevice, + LPWSTR* ppwszEndpointName +); + +// ---------------------------------------------------------------------- +HRESULT GetAudioFilterAsDevice +( + IMMDevice* pDevice, + IMMDevice** ppAudioFilterAsDevice +); + +// ---------------------------------------------------------------------- +HRESULT GetCachedProcessingModes +( + IMMDevice* pDevice, + EndpointConnectorType eConnectorType, + ULONG *pCount, + AUDIO_SIGNALPROCESSINGMODE **ppModes +); + + + + + + + + + + + + +// ---------------------------------------------------------------------- +HRESULT GetProcessingModes +( + IMMDevice* pDevice, + UINT pinId, + ULONG *pCount, + AUDIO_SIGNALPROCESSINGMODE **ppModes +); + +// ---------------------------------------------------------------------- +HRESULT GetCachedSupportedFormatRecords +( + IMMDevice* pDevice, + EndpointConnectorType eConnectorType, + AUDIO_SIGNALPROCESSINGMODE mode, + ULONG *pCount, + FORMAT_RECORD **ppFormatRecords +); + + + + + + + + + + + + + +// ---------------------------------------------------------------------- +HRESULT GetSupportedFormatRecords +( + IMMDevice* pDevice, + UINT pinId, + EndpointConnectorType eConnectorType, + AUDIO_SIGNALPROCESSINGMODE mode, + STACKWISE_DATAFLOW dataFlow, + ULONG *pCount, + FORMAT_RECORD **ppFormatRecords +); + +// ---------------------------------------------------------------------- +HRESULT IsFormatSupported +( + IMMDevice* pDevice, + UINT pinId, + WAVEFORMATEX *pWfx, + BOOL* pbSupported +); + +// ---------------------------------------------------------------------- +HRESULT DiscoverPeriodicityCharacteristicsForFormat +( + IMMDevice* pDevice, + EndpointConnectorType eConnectorType, + AUDIO_SIGNALPROCESSINGMODE mode, + WAVEFORMATEX *pWfx, + STACKWISE_DATAFLOW dataFlow, + UINT32 *pDefaultPeriodicityInFrames, + UINT32 *pFundamentalPeriodicityInFrames, + UINT32 *pMinPeriodicityInFrames, + UINT32 *pMaxPeriodicityInFrames, + UINT32 *pMaxPeriodicityInFramesExtended +); + +// ---------------------------------------------------------------------- +HRESULT CheckConnectorSupportForPeriodicity +( + IMMDevice* pDevice, + EndpointConnectorType eConnectorType, + AUDIO_SIGNALPROCESSINGMODE mode, + WAVEFORMATEX *pWfx, + STACKWISE_DATAFLOW dataFlow, + HNSTIME RequestedPeriodicity, + UINT32 *pActualPeriodicityInFrames +); + +// ---------------------------------------------------------------------- +HRESULT GetCachedDefaultFormat +( + IMMDevice* pDevice, + EndpointConnectorType eConnectorType, + WAVEFORMATEX** ppDefaultFormat +); + +// ---------------------------------------------------------------------- +HRESULT GetProposedFormatForProcessingMode +( + IMMDevice* pDevice, + UINT pinId, + AUDIO_SIGNALPROCESSINGMODE mode, + WAVEFORMATEX **ppProposedFormat +); + +// ---------------------------------------------------------------------- +HRESULT GetAvailiablePinInstanceCount +( + IMMDevice* pDevice, + UINT pinId, + UINT32* pAvailablePinInstanceCount +); + +// ------------------------------------------------------------------- +HRESULT GetDriverPathViaService +( + LPWSTR ServiceName, + LPWSTR DriverFullPath, + UINT cchFullPath +); + +// ------------------------------------------------------------------- +HRESULT GetFullPathFromImagePath +( + LPWSTR ImagePath, + LPWSTR DriverFullPath, + UINT cchFullPath +); + +// ------------------------------------------------------------------- +HRESULT CheckImports +( + LPWSTR DriverPath, + LPSTR ModuleNameToCheck, + LPSTR MethodNameToCheck, + bool* pIsImported +); + +// ---------------------------------------------------------------------- +HRESULT IsPortCls +( + IMMDevice* pDevice, + bool* pIsPortCls +); + +// ---------------------------------------------------------------------- +HRESULT IsAVStream +( + IMMDevice* pDevice, + bool* pIsAVStream +); + +// ---------------------------------------------------------------------- +HRESULT IsBluetooth +( + IMMDevice* pDevice, + bool* pIsBluetooth +); + +// ---------------------------------------------------------------------- +HRESULT IsSideband +( + IMMDevice* pDevice, + bool* pIsSideband +); + +// ---------------------------------------------------------------------- +HRESULT IsMVA +( + EndpointConnectorType eConnectorType, + IMMDevice* pDevice, + bool* pIsMVA +); + +// ---------------------------------------------------------------------- +HRESULT GetPnpDevnodeFromMMDevice +( + IMMDevice* pDevice, + IMMDevice** pDevnodeDevice +); diff --git a/audio/tests/PinResourceHelpers/TestResource.cpp b/audio/tests/PinResourceHelpers/TestResource.cpp index f3762b84..c7e06728 100644 --- a/audio/tests/PinResourceHelpers/TestResource.cpp +++ b/audio/tests/PinResourceHelpers/TestResource.cpp @@ -1,6 +1,6 @@ // ------------------------------------------------------------------------------ // -// Copyright (C) Microsoft. All rights reserved. +// Copyright (C) Microsoft Corporation. All rights reserved. // // File Name: // @@ -95,7 +95,7 @@ CPinTestResource::Initialize } szMode = ModeName(m_spHalfApp->m_Mode); - szPin = PinName(m_spHalfApp->m_ConnectorType); + szPin = PinName(m_spHalfApp->m_ConnectorType, m_spHalfApp->m_bIsMVA); // Id: Combine device Id, pin type and mode m_szId.Attach(W2BSTR(m_spHalfApp->m_pwstrDeviceId.get())); @@ -300,6 +300,7 @@ static const struct { AUDIO_SIGNALPROCESSINGMODE_MEDIA, L"MEDIA" }, { AUDIO_SIGNALPROCESSINGMODE_MOVIE, L"MOVIE" }, { AUDIO_SIGNALPROCESSINGMODE_NOTIFICATION, L"NOTIFICATION" }, + { AUDIO_SIGNALPROCESSINGMODE_FAR_FIELD_SPEECH, L"FAR_FIELD_SPEECH" }, }; @@ -313,7 +314,7 @@ LPWSTR CPinTestResource::ModeName(REFGUID guidMode) return L"UNKNOWN"; } -LPWSTR CPinTestResource::PinName(EndpointConnectorType eConnectorType) +LPWSTR CPinTestResource::PinName(EndpointConnectorType eConnectorType, bool IsMVA) { switch (eConnectorType) { @@ -324,7 +325,7 @@ LPWSTR CPinTestResource::PinName(EndpointConnectorType eConnectorType) case eLoopbackConnector: return L"LOOPBACK"; case eKeywordDetectorConnector: - return L"KEYWORD"; + return IsMVA?L"MVAKEYWORD":L"SVAKEYWORD"; default: return L"UNKNOWN"; } diff --git a/audio/tests/PinResourceHelpers/TestResource.h b/audio/tests/PinResourceHelpers/TestResource.h new file mode 100644 index 00000000..7bc6a792 --- /dev/null +++ b/audio/tests/PinResourceHelpers/TestResource.h @@ -0,0 +1,68 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft. All rights reserved. +// +// File Name: +// +// TestResource.h +// +// Abstract: +// +// TAEF Test Resource +// +// ------------------------------------------------------------------------------- +#pragma once + +#include "HalfApp.h" + +class CPinTestResource : + public WEX::TestExecution::ITestResource, + public IHalfAppContainer +{ +public: + //IUnknown + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + STDMETHODIMP QueryInterface( + __in REFIID riid, + __deref_out VOID **ppvObject + ); + // ITestResouce + STDMETHODIMP GetGuid(GUID* pGuid); + STDMETHODIMP SetGuid(GUID guid); + STDMETHODIMP GetValue(BSTR name, BSTR* pValue); + STDMETHODIMP SetValue(BSTR name, BSTR value); + + //IHalfAppContainer + STDMETHODIMP GetHalfApp(CHalfApp ** ppHalfApp); + + static HRESULT STDMETHODCALLTYPE CreateInstance( + CHalfApp * pHalf, + REFGUID guid, + WEX::TestExecution::ITestResource ** ppOut + ); + +private: + + CPinTestResource(); + ~CPinTestResource(); + + HRESULT STDMETHODCALLTYPE Initialize( + CHalfApp * pHalf, + REFGUID guid + ); + + LPWSTR ModeName(REFGUID guidMode); + LPWSTR PinName(EndpointConnectorType eConnectorType, bool IsMVA); + + CComBSTR m_szType; + CComBSTR m_szName; + CComBSTR m_szId; + CComBSTR m_szMode; + CComBSTR m_szPin; + + CAutoPtr<CHalfApp> m_spHalfApp; + + ULONG m_cRef; + GUID m_guid; +}; diff --git a/audio/tests/KSPosTst/TestResourceBuild.cpp b/audio/tests/PinResourceHelpers/TestResourceBuild.cpp index 93959089..02c1691d 100644 --- a/audio/tests/KSPosTst/TestResourceBuild.cpp +++ b/audio/tests/PinResourceHelpers/TestResourceBuild.cpp @@ -12,8 +12,10 @@ // // ------------------------------------------------------------------------------ #include "PreComp.h" -#include <TestResource.h> + +#include <Functiondiscoverykeys_devpkey.h> #include <PropertyHelper.h> +#include <TestResource.h> using namespace WEX::Common; using namespace WEX::Logging; @@ -33,12 +35,11 @@ HRESULT CreateTestResource DeviceDescriptor descriptor ) { - HRESULT hr = S_OK; - - CComHeapPtr<CHalfApp> spHalfApp; - wil::com_ptr_nothrow<ITestResource> spTestResource; - GUID ResourceGUID; - CComBSTR szResourceName; + HRESULT hr = S_OK; + CComHeapPtr<CHalfApp> spHalfApp; + wil::com_ptr_nothrow<ITestResource> spTestResource; + GUID ResourceGUID; + CComBSTR szResourceName; // Create HalfApp spHalfApp.Attach(new CHalfApp(descriptor)); @@ -57,11 +58,11 @@ HRESULT CreateTestResource } // Add to resource list - spHalfApp.Detach(); - if (!VERIFY_SUCCEEDED(hr = resourceList.Add(spTestResource.get()))) { + if (!VERIFY_SUCCEEDED(hr = spTestResource->GetValue(CComBSTR(TestResourceProperty::c_szName), &szResourceName))) { return hr; } - if (!VERIFY_SUCCEEDED(hr = spTestResource->GetValue(CComBSTR(TestResourceProperty::c_szName), &szResourceName))) { + spHalfApp.Detach(); + if (!VERIFY_SUCCEEDED(hr = resourceList.Add(spTestResource.get()))) { return hr; } Log::Comment(String().Format(L"Test Resource (%s) added", (PCWSTR)szResourceName)); @@ -313,9 +314,11 @@ HRESULT AddTestResourceForConnector UINT32 u32MinPeriodicityInFrames; UINT32 u32MaxPeriodicityInFrames; UINT32 u32MaxPeriodicityInFramesExtended; + bool isPortCls = false; bool isAVStream = false; bool isBluetooth = false; bool isSideband = false; + bool isMVA = false; // Get connector id if (!VERIFY_SUCCEEDED(hr = GetConnectorId(pDevice, eConnectorType, &bHasConnector, &uConnectorId))) { @@ -350,6 +353,11 @@ HRESULT AddTestResourceForConnector return hr; } + // Check if audio endpoint is PortCls + if (!VERIFY_SUCCEEDED(hr = IsPortCls(pDevice, &isPortCls))) { + return hr; + } + // Check if audio endpoint is AVStream if (!VERIFY_SUCCEEDED(hr = IsAVStream(pDevice, &isAVStream))) { return hr; @@ -365,6 +373,11 @@ HRESULT AddTestResourceForConnector return hr; } + // Check if audio endpoint is MVA + if (!VERIFY_SUCCEEDED(hr = IsMVA(eConnectorType, pDevice, &isMVA))) { + return hr; + } + DeviceDescriptor descriptor = { 0 }; descriptor.pDevice = pDevice; descriptor.pwstrAudioEndpointId = deviceId; @@ -382,9 +395,11 @@ HRESULT AddTestResourceForConnector descriptor.u32FundamentalPeriodicityInFrames = u32FundamentalPeriodicityInFrames; descriptor.u32MinPeriodicityInFrames = u32MinPeriodicityInFrames; descriptor.u32MaxPeriodicityInFrames = u32MaxPeriodicityInFrames; + descriptor.bIsPortCls = isPortCls; descriptor.bIsAVStream = isAVStream; descriptor.bIsBluetooth = isBluetooth; descriptor.bIsSideband = isSideband; + descriptor.bIsMVA = isMVA; if (!VERIFY_SUCCEEDED(hr = CreateTestResource(resourceList, descriptor))) { return hr; @@ -440,28 +455,25 @@ HRESULT AddTestResourcesForDevice return hr; } - /* // Identify existence of keyword detector pin. Add test resources for keyword detector pin. if (!VERIFY_SUCCEEDED(hr = AddTestResourceForConnector(resourceList, deviceId, deviceName, spDevice.get(), eKeywordDetectorConnector, dataFlow))) { return hr; } - */ return hr; } -HRESULT __cdecl BuildResourceList(ResourceList& resourceList) +HRESULT AddDevices(ResourceList& resourceList) { HRESULT hr = S_OK; wil::com_ptr_nothrow<IMMDeviceEnumerator> spEnumerator; - wil::com_ptr_nothrow<IMMDeviceCollection> spRenderEndpoints; - wil::com_ptr_nothrow<IMMDeviceCollection> spCaptureEndpoints; + wil::com_ptr_nothrow<IMMDeviceCollection> spEndpoints; wil::com_ptr_nothrow<IMMDevice> spEndpoint; - UINT cRenderDevices = 0; - UINT cCaptureDevices = 0; + UINT cDevices = 0; UINT i = 0; wil::unique_cotaskmem_string id; wil::unique_cotaskmem_string friendlyName; + EDataFlow endpointDataFlow; SetVerifyOutput verifySettings(VerifyOutputSettings::LogOnlyFailures); DisableVerifyExceptions disable; @@ -473,50 +485,78 @@ HRESULT __cdecl BuildResourceList(ResourceList& resourceList) // Create IMMDevice Enumerator VERIFY_SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void **)&spEnumerator)); - // Enumerate all render endpoints - VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &spRenderEndpoints)); - VERIFY_SUCCEEDED(spRenderEndpoints->GetCount(&cRenderDevices)); - - // Enumerate all capture endpoints - VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &spCaptureEndpoints)); - VERIFY_SUCCEEDED(spCaptureEndpoints->GetCount(&cCaptureDevices)); + // Enumerate all endpoints + VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(eAll, DEVICE_STATE_ACTIVE, &spEndpoints)); + VERIFY_SUCCEEDED(spEndpoints->GetCount(&cDevices)); - if (!VERIFY_IS_TRUE(cRenderDevices | cCaptureDevices)) + if (!VERIFY_IS_TRUE(cDevices)) { - hr = E_FAIL; Log::Comment(L"No device was found!"); - goto Exit; + return E_FAIL; } - Log::Comment(String().Format(L"Found %d viable rendering device!", cRenderDevices)); - // Add test resources for render endpoints - if (cRenderDevices) { - for (i = 0; i < cRenderDevices; i++) { - VERIFY_SUCCEEDED(spRenderEndpoints->Item(i, &spEndpoint)); - VERIFY_SUCCEEDED(spEndpoint->GetId(&id)); - VERIFY_SUCCEEDED(GetEndpointFriendlyName(spEndpoint.get(), &friendlyName)); - - Log::Comment(String().Format(L"\\\\ Device: %s (%s)", friendlyName.get(), id.get())); - VERIFY_SUCCEEDED(AddTestResourcesForDevice(resourceList, id.get(), friendlyName.get(), render)); - } + // Check if a device ID was specified. + String instanceId; + BOOL isDeviceSelected = false; + HRESULT res = RuntimeParameters::TryGetValue(L"InstanceId", instanceId); + if (res == S_OK) + { + Log::Comment(String().Format(L"Selected device ID: %s", static_cast<LPCWSTR>(instanceId))); + isDeviceSelected = true; } - Log::Comment(String().Format(L"Found %d viable capture device!", cCaptureDevices)); - // Add test resources for capture endpoints - if (cCaptureDevices) { - for (i = 0; i < cCaptureDevices; i++) { - VERIFY_SUCCEEDED(spCaptureEndpoints->Item(i, &spEndpoint)); - VERIFY_SUCCEEDED(spEndpoint->GetId(&id)); - VERIFY_SUCCEEDED(GetEndpointFriendlyName(spEndpoint.get(), &friendlyName)); + Log::Comment(String().Format(L"Found %d viable endpoint(s)!", cDevices)); + // Add test resources for endpoints + for (i = 0; i < cDevices; i++) { + VERIFY_SUCCEEDED(spEndpoints->Item(i, &spEndpoint)); + VERIFY_SUCCEEDED(spEndpoint->GetId(&id)); + VERIFY_SUCCEEDED(GetEndpointFriendlyName(spEndpoint.get(), &friendlyName)); + + // Check whether it is a render or capture endpoint. + CComPtr<IMMEndpoint> pMmEndpoint; + VERIFY_SUCCEEDED(spEndpoint->QueryInterface(__uuidof(IMMEndpoint), (void**)&pMmEndpoint)); + pMmEndpoint->GetDataFlow(&endpointDataFlow); + + // If there is a device selected, check if the current endpoint belongs to the device. + if (isDeviceSelected) + { + // Get the IDeviceTopology interface of the endpoint. + CComPtr<IDeviceTopology> pEndpointTopology; + if (!VERIFY_SUCCEEDED(hr = spEndpoint->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&pEndpointTopology))) { return hr; } + + // Get the connector inside the device topology object. + CComPtr<IConnector> spConnector; + if (!VERIFY_SUCCEEDED(hr = pEndpointTopology->GetConnector(0, &spConnector))) { return hr; } + + // Get the id of the device adapter this endpoint is connected to. + CComHeapPtr<WCHAR> szFilterId; + if (!VERIFY_SUCCEEDED(hr = spConnector->GetDeviceIdConnectedTo(&szFilterId))) { return hr; } + + // Get the IMMDevice object of this device adapter. + CComPtr<IMMDevice> spDevnode; + if (!VERIFY_SUCCEEDED(hr = spEnumerator->GetDevice(szFilterId, &spDevnode))) { return hr; } + + // Open the property store and get the instance ID of the device adapter. + PROPVARIANT varInstanceId; + PropVariantInit(&varInstanceId); + + CComPtr<IPropertyStore> spDevnodePropertyStore; + if (!VERIFY_SUCCEEDED(hr = spDevnode->OpenPropertyStore(STGM_READ, &spDevnodePropertyStore))) { return hr; } + + if (!VERIFY_SUCCEEDED(hr = spDevnodePropertyStore->GetValue(PKEY_Device_InstanceId, &varInstanceId))) { return hr; } + if (!VERIFY_ARE_EQUAL(varInstanceId.vt, VT_LPWSTR)) { return E_UNEXPECTED; } - Log::Comment(String().Format(L"\\\\ Device: %s (%s)", friendlyName.get(), id.get())); - VERIFY_SUCCEEDED(AddTestResourcesForDevice(resourceList, id.get(), friendlyName.get(), capture)); + // If this endpoint does not belong to the selected device, skip it. + if (0 != _wcsicmp(instanceId, varInstanceId.pwszVal)) + { + continue; + } } + Log::Comment(String().Format(L"\\\\ Device: %s (%s)", friendlyName.get(), id.get())); + VERIFY_SUCCEEDED(AddTestResourcesForDevice(resourceList, id.get(), friendlyName.get(), endpointDataFlow == eRender ? render : capture)); } - Log::Comment(L"Resource enumeration complete."); Log::Comment(String().Format(L"Enumerated %u resources", resourceList.Count())); -Exit: return hr; } diff --git a/audio/tests/PinResourceHelpers/TestResourceHelper.h b/audio/tests/PinResourceHelpers/TestResourceHelper.h index 08fafa23..df5b8720 100644 --- a/audio/tests/PinResourceHelpers/TestResourceHelper.h +++ b/audio/tests/PinResourceHelpers/TestResourceHelper.h @@ -1,6 +1,6 @@ // ------------------------------------------------------------------------------ // -// Copyright (C) Microsoft. All rights reserved. +// Copyright (C) Microsoft Corporation. All rights reserved. // // Module Name: // @@ -71,3 +71,12 @@ HRESULT GetSupportedFormatRecordsForConnector ULONG* pCount, FORMAT_RECORD** ppFormatRecords ); + +// ---------------------------------------------------------------------- +void VerifyAllEndpointsPluggedIn(); + +// ---------------------------------------------------------------------- +HRESULT AddDevices +( + WEX::TestExecution::ResourceList& resourceList +); diff --git a/audio/tests/wavetest/PreComp.h b/audio/tests/wavetest/PreComp.h deleted file mode 100644 index 76210090..00000000 --- a/audio/tests/wavetest/PreComp.h +++ /dev/null @@ -1,47 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Copyright (C) Microsoft. All rights reserved. -// -// File Name: -// -// PreComp.h -// -// Abstract: -// -// Precompiled common headers -// -// ------------------------------------------------------------------------------- - -#include <basiclog.h> -#include <BasicLogHelper.h> -#include <mmsystem.h> -#include <atlbase.h> -#include <atlcom.h> -#include <initguid.h> - -#include <AudioEngineEndpoint.h> -#include <AudioEngineEndpointP.h> -#include <Audioclient.h> -#include <mmdeviceapi.h> -#include <mmdeviceapip.h> -#include <devicetopologyp.h> -#include <propvarutil.h> - -#include <wil\com.h> - -#include <KsLib.h> -#include <Waveutil.h> -#include <aecommon.h> -#include <ilog.h> -#include <shlflags.h> -#include <basicprintf.h> -#include <wextestclass.h> -#include <resourcelist.h> - -// ------------------------------------------------------------------------------ -// Log -#define LOG(log, ...) BasicLogPrintf( log, XMSG, 1, __VA_ARGS__ ) -#define SKIP(log, ...) BasicLogPrintf( log, XSKIP, 1, __VA_ARGS__ ) -#define WARN(log, ...) BasicLogPrintf( log, XWARN, 1, __VA_ARGS__ ) -#define BLOCK(log, ...) BasicLogPrintf( log, XBLOCK, 1, __VA_ARGS__ ) -#define ERR(log, ...) BasicLogPrintf( log, XFAIL, 1, __VA_ARGS__ ) diff --git a/audio/tests/wavetest/TestResourceBuild.cpp b/audio/tests/wavetest/TestResourceBuild.cpp deleted file mode 100644 index 5ca2ff0d..00000000 --- a/audio/tests/wavetest/TestResourceBuild.cpp +++ /dev/null @@ -1,506 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Copyright (C) Microsoft. All rights reserved. -// -// File Name: -// -// TestResourceBuild.cpp -// -// Abstract: -// -// TAEF BuildResourceList implementation -// -// ------------------------------------------------------------------------------ -#include "PreComp.h" -#include <TestResource.h> -#include <PropertyHelper.h> - -using namespace WEX::Common; -using namespace WEX::Logging; -using namespace WEX::TestExecution; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// CreateTestResource -// -// Create the test resource with MMDevice, device id, device name, data flow, connector type, connector id, mode, -// list of formats -// -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -HRESULT CreateTestResource -( - ResourceList& resourceList, - DeviceDescriptor descriptor -) -{ - HRESULT hr = S_OK; - - CComHeapPtr<CHalfApp> spHalfApp; - wil::com_ptr_nothrow<ITestResource> spTestResource; - GUID ResourceGUID; - CComBSTR szResourceName; - - // Create HalfApp - spHalfApp.Attach(new CHalfApp(descriptor)); - if (!VERIFY_IS_NOT_NULL(spHalfApp)) { - hr = E_OUTOFMEMORY; - return hr; - } - - // Create PinTestResource - if (!VERIFY_SUCCEEDED(hr = CoCreateGuid(&ResourceGUID))) { - return hr; - } - if (!VERIFY_SUCCEEDED(hr = CPinTestResource::CreateInstance( - spHalfApp, ResourceGUID, &spTestResource))) { - return hr; - } - - // Add to resource list - spHalfApp.Detach(); - if (!VERIFY_SUCCEEDED(hr = resourceList.Add(spTestResource.get()))) { - return hr; - } - if (!VERIFY_SUCCEEDED(hr = spTestResource->GetValue(CComBSTR(TestResourceProperty::c_szName), &szResourceName))) { - return hr; - } - Log::Comment(String().Format(L"Test Resource (%s) added", (PCWSTR)szResourceName)); - - return hr; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// GetProcessingModesForConnector -// -// For host/keyword detector pin, processing mode info is cached in property store and can be directly read. For offload -// pin, query for ks processing mode property. For loopback pin, it doesn't support any processing modes, return GUID_NULL. -// -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -HRESULT GetProcessingModesForConnector -( - IMMDevice* pDevice, - UINT uConnectorId, - EndpointConnectorType eConnectorType, - ULONG *pCount, - AUDIO_SIGNALPROCESSINGMODE **ppModes -) -{ - HRESULT hr = S_OK; - - *pCount = 0; - *ppModes = NULL; - - if (eConnectorType == eHostProcessConnector || eConnectorType == eKeywordDetectorConnector) { - if (!VERIFY_SUCCEEDED(hr = GetCachedProcessingModes(pDevice, eConnectorType, pCount, ppModes))) { - return hr; - } - } - else if (eConnectorType == eOffloadConnector) { - if (!VERIFY_SUCCEEDED(hr = GetProcessingModes(pDevice, uConnectorId, pCount, ppModes))) { - return hr; - } - } - else if (eConnectorType == eLoopbackConnector) { - // Loopback pin doesn't support any processing modes, so put 1 GUID_NULL - CComHeapPtr<AUDIO_SIGNALPROCESSINGMODE> spModes; - - if (!spModes.Allocate(1)) - return E_OUTOFMEMORY; - - spModes[0] = GUID_NULL; - - *pCount = 1; - *ppModes = spModes.Detach(); - } - - return hr; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// GetDefaultFormatForConnector -// -// Read audio engine device format from property store as default format. -// -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -HRESULT GetDefaultFormatForConnector -( - IMMDevice* pDevice, - EndpointConnectorType eConnectorType, - WAVEFORMATEX **ppDefaultFormat -) -{ - HRESULT hr = S_OK; - - *ppDefaultFormat = NULL; - if (!VERIFY_SUCCEEDED(hr = GetCachedDefaultFormat(pDevice, eConnectorType, ppDefaultFormat))){ - return hr; - } - - return hr; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// GetSupportedFormatsForConnector -// -// For host/keyword detector pin, supported formats info is cached in property store and can be directly read. For offload -// pin, provide with a predefined list of formats and check whether format is supported. For loopback pin, it matches the format -// of host pin so put 0 format record. -// -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -HRESULT GetSupportedFormatRecordsForConnector -( - IMMDevice* pDevice, - UINT uConnectorId, - EndpointConnectorType eConnectorType, - AUDIO_SIGNALPROCESSINGMODE mode, - STACKWISE_DATAFLOW dataFlow, - ULONG *pCount, - FORMAT_RECORD **ppFormatRecords -) -{ - HRESULT hr = S_OK; - - *pCount = 0; - *ppFormatRecords = NULL; - - if (eConnectorType == eHostProcessConnector || eConnectorType == eKeywordDetectorConnector) { - if (!VERIFY_SUCCEEDED(hr = GetCachedSupportedFormatRecords(pDevice, eConnectorType, mode, pCount, ppFormatRecords))) { - return hr; - } - } - else if (eConnectorType == eOffloadConnector) { - if (!VERIFY_SUCCEEDED(hr = GetSupportedFormatRecords(pDevice, uConnectorId, eConnectorType, mode, dataFlow, pCount, ppFormatRecords))) { - return hr; - } - } - else if (eConnectorType == eLoopbackConnector) { - *pCount = 0; - *ppFormatRecords = NULL; - } - - return hr; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// GetPreferredFormatForConnector -// -// If KSPROPERTY_PIN_PROPOSEDATAFORMAT2 is supported, use the proposed format for mode as preferred format for mode. -// Otherwise, use default format as preferred format. -// -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -HRESULT GetPreferredFormatForConnector -( - IMMDevice* pDevice, - UINT uConnectorId, - EndpointConnectorType eConnectorType, - AUDIO_SIGNALPROCESSINGMODE mode, - WAVEFORMATEX **ppPreferredFormat -) -{ - HRESULT hr = S_OK; - wil::unique_cotaskmem_ptr<WAVEFORMATEX> pDefaultFormat; - wil::unique_cotaskmem_ptr<WAVEFORMATEX> pProposedFormat; - - *ppPreferredFormat = NULL; - - - // Get default format for connector - if (!VERIFY_SUCCEEDED(hr = GetDefaultFormatForConnector(pDevice, eConnectorType, wil::out_param(pDefaultFormat)))) { - return hr; - } - - // Return the processing mode specific format proposed by the driver - hr = GetProposedFormatForProcessingMode(pDevice, uConnectorId, mode, wil::out_param(pProposedFormat)); - if (hr == S_OK) { - CloneWaveFormat(pProposedFormat.get(), ppPreferredFormat); - } - else { - CloneWaveFormat(pDefaultFormat.get(), ppPreferredFormat); - hr = S_OK; - } - - return hr; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// GetPreferredFormatPeriodicityCharacteristicsForConnector -// -// Get periodicity characteristics for format. For host/keyword detector pin, periodicity info is cached along with format -// in property store and can be searched from supported format list. For offload pin, it should be calculate from -// DiscoverPeriodicityCharacteristicsForFormat. -// -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -HRESULT GetPreferredFormatPeriodicityCharacteristicsForConnector -( - IMMDevice* pDevice, - EndpointConnectorType eConnectorType, - AUDIO_SIGNALPROCESSINGMODE mode, - STACKWISE_DATAFLOW dataFlow, - WAVEFORMATEX *pPreferredFormat, - ULONG cFormatRecords, - PFORMAT_RECORD pFormatRecords, - UINT32 *pDefaultPeriodicityInFrames, - UINT32 *pFundamentalPeriodicityInFrames, - UINT32 *pMinPeriodicityInFrames, - UINT32 *pMaxPeriodicityInFrames, - UINT32 *pMaxPeriodicityInFramesExtended -) -{ - HRESULT hr = S_OK; - bool bFormatInList = false; - - for (ULONG i = 0; i < cFormatRecords; i++) { - if (CompareWaveFormat((WAVEFORMATEX *)&pFormatRecords[i].wfxEx, pPreferredFormat)) { - bFormatInList = true; - *pDefaultPeriodicityInFrames = pFormatRecords[i].defaultPeriodInFrames; - *pFundamentalPeriodicityInFrames = pFormatRecords[i].fundamentalPeriodInFrames; - *pMinPeriodicityInFrames = pFormatRecords[i].minPeriodInFrames; - *pMaxPeriodicityInFrames = pFormatRecords[i].maxPeriodInFrames; - *pMaxPeriodicityInFramesExtended = pFormatRecords[i].maxPeriodInFramesExtended; - break; - } - } - - if (eConnectorType == eHostProcessConnector || eConnectorType == eKeywordDetectorConnector) { - if (!VERIFY_IS_TRUE(bFormatInList)) { - hr = E_NOTFOUND; - return hr; - } - } - else if (eConnectorType == eOffloadConnector || eConnectorType == eLoopbackConnector) { - if (!VERIFY_SUCCEEDED(hr = DiscoverPeriodicityCharacteristicsForFormat(pDevice, eConnectorType, mode, pPreferredFormat, dataFlow, pDefaultPeriodicityInFrames, pFundamentalPeriodicityInFrames, pMinPeriodicityInFrames, pMaxPeriodicityInFrames, pMaxPeriodicityInFramesExtended))) { - return hr; - } - } - - return hr; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// AddTestResourceForConnector -// -// For each connector, read connector id, get all processing modes, identify default and preferred format for each mode -// and also enumerate a list of supported formats for each mode. Store all the infos in test resource. -// -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -HRESULT AddTestResourceForConnector -( - ResourceList& resourceList, - LPWSTR deviceId, - LPWSTR deviceName, - IMMDevice* pDevice, - EndpointConnectorType eConnectorType, - STACKWISE_DATAFLOW dataFlow -) -{ - HRESULT hr = S_OK; - bool bHasConnector = false; - UINT uConnectorId; - ULONG cModes = 0; - CComHeapPtr<AUDIO_SIGNALPROCESSINGMODE> spModes; - AUDIO_SIGNALPROCESSINGMODE mode; - ULONG cFormatRecords = 0; - CComHeapPtr<FORMAT_RECORD> spFormatRecords; - wil::unique_cotaskmem_ptr<WAVEFORMATEX> pPreferredFormat; - UINT32 u32DefaultPeriodicityInFrames; - UINT32 u32FundamentalPeriodicityInFrames; - UINT32 u32MinPeriodicityInFrames; - UINT32 u32MaxPeriodicityInFrames; - UINT32 u32MaxPeriodicityInFramesExtended; - bool isAVStream = false; - - // Get connector id - if (!VERIFY_SUCCEEDED(hr = GetConnectorId(pDevice, eConnectorType, &bHasConnector, &uConnectorId))) { - return hr; - } - if (!bHasConnector) { - return hr; - } - - Log::Comment(String().Format(L"Adding Test Resource for pin [%u]:", (uConnectorId & PARTID_MASK))); - - - // Get all signal processing modes for connector - if (!VERIFY_SUCCEEDED(hr = GetProcessingModesForConnector(pDevice, uConnectorId, eConnectorType, &cModes, &spModes))) { - return hr; - } - - // Loop through each mode, get preferred format and list of formats and create test resource for each mode - for (ULONG i = 0; i < cModes; i++) { - - mode = spModes[i]; - - if (!VERIFY_SUCCEEDED(hr = GetSupportedFormatRecordsForConnector(pDevice, uConnectorId, eConnectorType, mode, dataFlow, &cFormatRecords, &spFormatRecords))) { - return hr; - } - - if (!VERIFY_SUCCEEDED(hr = GetPreferredFormatForConnector(pDevice, uConnectorId, eConnectorType, mode, wil::out_param(pPreferredFormat)))) { - return hr; - } - - if (!VERIFY_SUCCEEDED(hr = GetPreferredFormatPeriodicityCharacteristicsForConnector(pDevice, eConnectorType, mode, dataFlow, pPreferredFormat.get(), cFormatRecords, spFormatRecords, &u32DefaultPeriodicityInFrames, &u32FundamentalPeriodicityInFrames, &u32MinPeriodicityInFrames, &u32MaxPeriodicityInFrames, &u32MaxPeriodicityInFramesExtended))) { - return hr; - } - - // Check if audio endpoint is AVStream - if (!VERIFY_SUCCEEDED(hr = IsAVStream(pDevice, &isAVStream))) { - return hr; - } - - DeviceDescriptor descriptor = { 0 }; - descriptor.pDevice = pDevice; - descriptor.pwstrAudioEndpointId = deviceId; - descriptor.pwstrAudioEndpointFriendlyName = deviceName; - descriptor.dataFlow = dataFlow; - descriptor.eConnectorType = eConnectorType; - descriptor.uConnectorId = uConnectorId; - descriptor.mode = mode; - descriptor.cModes = cModes; - descriptor.pModes = spModes; - descriptor.cFormatRecords = cFormatRecords; - descriptor.pFormatRecords = spFormatRecords; - descriptor.pPreferredFormat = pPreferredFormat.get(); - descriptor.u32DefaultPeriodicityInFrames = u32DefaultPeriodicityInFrames; - descriptor.u32FundamentalPeriodicityInFrames = u32FundamentalPeriodicityInFrames; - descriptor.u32MinPeriodicityInFrames = u32MinPeriodicityInFrames; - descriptor.u32MaxPeriodicityInFrames = u32MaxPeriodicityInFrames; - descriptor.bIsAVStream = isAVStream; - - if (!VERIFY_SUCCEEDED(hr = CreateTestResource(resourceList, descriptor))) { - return hr; - } - } - - return hr; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// AddTestResourcesForDevice -// -// Identify the existence of all pin types and add test resources for each pin -// -/////////////////////////////////////////////////////////////////////////////////////////////////////////// -HRESULT AddTestResourcesForDevice -( - ResourceList& resourceList, - LPWSTR deviceId, - LPWSTR deviceName, - STACKWISE_DATAFLOW dataFlow -) -{ - HRESULT hr = S_OK; - wil::com_ptr_nothrow<IMMDeviceEnumerator> spEnumerator; - wil::com_ptr_nothrow<IMMDevice> spDevice; - - Log::Comment(String().Format(L"Adding Test Resource for Device [%s]:", deviceName)); - - // Read the default device format from the property store - if (!VERIFY_SUCCEEDED(hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void **)&spEnumerator))) { - return hr; - } - if (!VERIFY_SUCCEEDED(hr = spEnumerator->GetDevice(deviceId, &spDevice))) { - return hr; - } - - // Identify existence of host pin. Add test resources for host pin. - if (!VERIFY_SUCCEEDED(hr = AddTestResourceForConnector(resourceList, deviceId, deviceName, spDevice.get(), eHostProcessConnector, dataFlow))) { - return hr; - } - - // Identify existence of offload pin. Add test resources for offload pin. - if (!VERIFY_SUCCEEDED(hr = AddTestResourceForConnector(resourceList, deviceId, deviceName, spDevice.get(), eOffloadConnector, dataFlow))) { - return hr; - } - - // Identify existence of loopback pin. Add test resources for loopback pin. - if (!VERIFY_SUCCEEDED(hr = AddTestResourceForConnector(resourceList, deviceId, deviceName, spDevice.get(), eLoopbackConnector, capture ))) { - return hr; - } - - /* - // Identify existence of keyword detector pin. Add test resources for keyword detector pin. - if (!VERIFY_SUCCEEDED(hr = AddTestResourceForConnector(resourceList, deviceId, deviceName, spDevice.get(), eKeywordDetectorConnector, dataFlow))) { - return hr; - } - */ - - return hr; -} - -HRESULT __cdecl BuildResourceList(ResourceList& resourceList) -{ - HRESULT hr = S_OK; - wil::com_ptr_nothrow<IMMDeviceEnumerator> spEnumerator; - wil::com_ptr_nothrow<IMMDeviceCollection> spRenderEndpoints; - wil::com_ptr_nothrow<IMMDeviceCollection> spCaptureEndpoints; - wil::com_ptr_nothrow<IMMDevice> spEndpoint; - UINT cRenderDevices = 0; - UINT cCaptureDevices = 0; - UINT i = 0; - wil::unique_cotaskmem_string id; - wil::unique_cotaskmem_string friendlyName; - - SetVerifyOutput verifySettings(VerifyOutputSettings::LogOnlyFailures); - DisableVerifyExceptions disable; - - VERIFY_SUCCEEDED(::CoInitializeEx(NULL, COINIT_MULTITHREADED)); - - Log::Comment(L"In BuildResourceList"); - - // Create IMMDevice Enumerator - VERIFY_SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void **)&spEnumerator)); - - // Enumerate all render endpoints - VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &spRenderEndpoints)); - VERIFY_SUCCEEDED(spRenderEndpoints->GetCount(&cRenderDevices)); - - // Enumerate all capture endpoints - VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &spCaptureEndpoints)); - VERIFY_SUCCEEDED(spCaptureEndpoints->GetCount(&cCaptureDevices)); - - if (!VERIFY_IS_TRUE(cRenderDevices | cCaptureDevices)) - { - hr = E_FAIL; - Log::Comment(L"No device was found!"); - goto Exit; - } - - Log::Comment(String().Format(L"Found %d viable rendering device!", cRenderDevices)); - // Add test resources for render endpoints - if (cRenderDevices) { - for (i = 0; i < cRenderDevices; i++) { - VERIFY_SUCCEEDED(spRenderEndpoints->Item(i, &spEndpoint)); - VERIFY_SUCCEEDED(spEndpoint->GetId(&id)); - VERIFY_SUCCEEDED(GetEndpointFriendlyName(spEndpoint.get(), &friendlyName)); - - Log::Comment(String().Format(L"\\\\ Device: %s (%s)", friendlyName.get(), id.get())); - VERIFY_SUCCEEDED(AddTestResourcesForDevice(resourceList, id.get(), friendlyName.get(), render)); - } - } - - Log::Comment(String().Format(L"Found %d viable capture device!", cCaptureDevices)); - // Add test resources for capture endpoints - if (cCaptureDevices) { - for (i = 0; i < cCaptureDevices; i++) { - VERIFY_SUCCEEDED(spCaptureEndpoints->Item(i, &spEndpoint)); - VERIFY_SUCCEEDED(spEndpoint->GetId(&id)); - VERIFY_SUCCEEDED(GetEndpointFriendlyName(spEndpoint.get(), &friendlyName)); - - Log::Comment(String().Format(L"\\\\ Device: %s (%s)", friendlyName.get(), id.get())); - VERIFY_SUCCEEDED(AddTestResourcesForDevice(resourceList, id.get(), friendlyName.get(), capture)); - } - } - - Log::Comment(L"Resource enumeration complete."); - Log::Comment(String().Format(L"Enumerated %u resources", resourceList.Count())); - -Exit: - return hr; -} diff --git a/audio/tests/wavetest/WaveTestTaef.cpp b/audio/tests/wavetest/WaveTestTaef.cpp index be5d47f3..e4a008d3 100644 --- a/audio/tests/wavetest/WaveTestTaef.cpp +++ b/audio/tests/wavetest/WaveTestTaef.cpp @@ -1,6 +1,6 @@ // ------------------------------------------------------------------------------ // -// Copyright (C) Microsoft. All rights reserved. +// Copyright (C) Microsoft Corporation. All rights reserved. // // File Name: // @@ -14,6 +14,7 @@ #include "PreComp.h" #include "WaveTestTaef.h" #include "tests.h" +#include <TestResourceHelper.h> using namespace WEX::Common; using namespace WEX::Logging; @@ -127,6 +128,12 @@ bool WDMAudio::WaveTest::TestCaseCleanUp() } // ------------------------------------------------------------------------------- +void WDMAudio::WaveTestPreTest::TAEF_VerifyAllEndpointsPluggedIn() +{ + VerifyAllEndpointsPluggedIn(); +} + +// ------------------------------------------------------------------------------- void WDMAudio::WaveTest::TAEF_StreamStateControl() { RUN_TEST_CASE(Test_StreamStateControl) diff --git a/audio/tests/wavetest/WaveTestTaef.h b/audio/tests/wavetest/WaveTestTaef.h index 8c87a7f8..4e9b8c57 100644 --- a/audio/tests/wavetest/WaveTestTaef.h +++ b/audio/tests/wavetest/WaveTestTaef.h @@ -1,6 +1,6 @@ // ------------------------------------------------------------------------------ // -// Copyright (C) Microsoft. All rights reserved. +// Copyright (C) Microsoft Corporation. All rights reserved. // // File Name: // @@ -30,6 +30,28 @@ namespace WEX { namespace WDMAudio { + class WaveTestPreTest + { + BEGIN_TEST_CLASS(WaveTestPreTest) + TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client x86") + TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client x64") + TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client ARM") + TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client ARM64") + TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Server x64") + TEST_CLASS_PROPERTY(L"Kits.MinRelease", L"19H1") + TEST_CLASS_PROPERTY(L"Kits.CorePackageComposition", L"Full") + TEST_CLASS_PROPERTY(L"Kits.CorePackageComposition", L"OnecoreUAP") + TEST_CLASS_PROPERTY(L"Kits.IsInProc", L"True") + TEST_CLASS_PROPERTY(L"Kits.PublishingOrganization", L"Microsoft Corporation") + TEST_CLASS_PROPERTY(L"Kits.TestType", L"Development") + TEST_CLASS_PROPERTY(L"Kits.DevelopmentPhase", L"Development and Integration") + TEST_CLASS_PROPERTY(L"ThreadingModel", L"MTA") + END_TEST_CLASS() + + BEGIN_TEST_METHOD(TAEF_VerifyAllEndpointsPluggedIn) + TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature") + END_TEST_METHOD() + }; class WaveTest { @@ -50,9 +72,13 @@ namespace WDMAudio TEST_CLASS_PROPERTY(L"Kits.DevelopmentPhase", L"Development and Integration") TEST_CLASS_PROPERTY(L"Subfeature", L"Wave Driver Tests") TEST_CLASS_PROPERTY(L"ThreadingModel", L"MTA") - TEST_CLASS_PROPERTY(L"ArtifactUnderTest", L"onecore\\external\\ddk\\inc\\drmk.h") - TEST_CLASS_PROPERTY(L"ArtifactUnderTest", L"onecoreuap\\external\\ddk\\lib\\$(build.arch)\\drmk.lib") - TEST_CLASS_PROPERTY(L"ArtifactUnderTest", L"onecoreuap\\internal\\drivers\\inc\\private\\wdm\\audio\\inf\\wdmaudio.inf") + + + + + + + END_TEST_CLASS() TEST_CLASS_SETUP(WaveTestSetup) @@ -62,57 +88,41 @@ namespace WDMAudio TEST_METHOD_CLEANUP(TestCaseCleanUp) BEGIN_TEST_METHOD(TAEF_StreamStateControl) - TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional") - TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification") TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature") TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*HOST*' OR @Id = '*OFFLOAD*'") END_TEST_METHOD() BEGIN_TEST_METHOD(TAEF_LoopbackStreamStateControl) - TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional") - TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification") TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature") TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*LOOPBACK*'") END_TEST_METHOD() BEGIN_TEST_METHOD(TAEF_StreamMinBufferSize) - TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional") - TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification") TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature") TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*HOST*' OR @Id = '*OFFLOAD*'") END_TEST_METHOD() BEGIN_TEST_METHOD(TAEF_StreamMaxBufferSize) - TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional") - TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification") TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature") TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*HOST*' OR @Id = '*OFFLOAD*'") END_TEST_METHOD() BEGIN_TEST_METHOD(TAEF_StreamDifferentFormat) - TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional") - TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification") TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature") TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*HOST*' OR @Id = '*OFFLOAD*'") END_TEST_METHOD() BEGIN_TEST_METHOD(TAEF_LoopbackStreamDifferentFormat) - TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional") - TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification") TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature") TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*LOOPBACK*'") END_TEST_METHOD() BEGIN_TEST_METHOD(TAEF_StreamMultipleModes) - TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional") - TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification") TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature") TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*RAW' OR @Id = '*DEFAULT'") END_TEST_METHOD() BEGIN_TEST_METHOD(TAEF_VerifyPinWaveRTConformance) - TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional") - TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification") TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature") TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Type = 'Render' OR @Type = 'Capture'") END_TEST_METHOD() diff --git a/audio/tests/wavetest/comptest.cpp b/audio/tests/wavetest/comptest.cpp index db823156..6b0b4e86 100644 --- a/audio/tests/wavetest/comptest.cpp +++ b/audio/tests/wavetest/comptest.cpp @@ -1,6 +1,6 @@ // ------------------------------------------------------------------------------ // -// Copyright (C) Microsoft. All rights reserved. +// Copyright (C) Microsoft Corporation. All rights reserved. // // Module Name: // @@ -28,9 +28,9 @@ void Test_VerifyPinWaveRTConformance(void) VERIFY_SUCCEEDED(pHalfApp->m_pAudioDeviceEndpoint->GetRTCaps(&bIsRTCapable)); - // Check for our new requirement - new drivers must be WaveRT. Here we skip the test on AVStream (UAC1/HFP/A2dp) + // Check for our new requirement - new PortCls drivers must be WaveRT. // For other old drivers that are not WaveRT, may issue Errata to cover the failure. - if (!pHalfApp->m_bIsAVStream) + if (pHalfApp->m_bIsPortCls) { VERIFY_IS_TRUE(bIsRTCapable); } diff --git a/audio/tests/wavetest/pintest.cpp b/audio/tests/wavetest/pintest.cpp index a8b356c9..26924bb7 100644 --- a/audio/tests/wavetest/pintest.cpp +++ b/audio/tests/wavetest/pintest.cpp @@ -1,6 +1,6 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // -// Copyright (C) Microsoft. All rights reserved. +// Copyright (C) Microsoft Corporation. All rights reserved. // // Module Name: // @@ -26,13 +26,14 @@ static const struct }knownModes[] = { { GUID_NULL, L"NO_MODE" }, -{ AUDIO_SIGNALPROCESSINGMODE_RAW, L"RAW" }, -{ AUDIO_SIGNALPROCESSINGMODE_DEFAULT, L"DEFAULT" }, -{ AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, L"COMMUNICATIONS" }, -{ AUDIO_SIGNALPROCESSINGMODE_SPEECH, L"SPEECH" }, -{ AUDIO_SIGNALPROCESSINGMODE_MEDIA, L"MEDIA" }, -{ AUDIO_SIGNALPROCESSINGMODE_MOVIE, L"MOVIE" }, -{ AUDIO_SIGNALPROCESSINGMODE_NOTIFICATION, L"NOTIFICATION" }, + { AUDIO_SIGNALPROCESSINGMODE_RAW, L"RAW" }, + { AUDIO_SIGNALPROCESSINGMODE_DEFAULT, L"DEFAULT" }, + { AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, L"COMMUNICATIONS" }, + { AUDIO_SIGNALPROCESSINGMODE_SPEECH, L"SPEECH" }, + { AUDIO_SIGNALPROCESSINGMODE_MEDIA, L"MEDIA" }, + { AUDIO_SIGNALPROCESSINGMODE_MOVIE, L"MOVIE" }, + { AUDIO_SIGNALPROCESSINGMODE_NOTIFICATION, L"NOTIFICATION" }, + { AUDIO_SIGNALPROCESSINGMODE_FAR_FIELD_SPEECH, L"FAR_FIELD_SPEECH"}, }; @@ -489,4 +490,4 @@ void Test_LoopbackStreamDifferentFormat(void) } delete pHostHalfApp; -}
\ No newline at end of file +} diff --git a/audio/tests/wavetest/tests.h b/audio/tests/wavetest/tests.h deleted file mode 100644 index e3ae0691..00000000 --- a/audio/tests/wavetest/tests.h +++ /dev/null @@ -1,27 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Copyright (C) Microsoft. All rights reserved. -// -// Module Name: -// -// tests.h -// -// Abstract: -// -// Header for testcases -// -// ------------------------------------------------------------------------------- -#pragma once - -// Stream test -void Test_StreamStateControl(void); -void Test_LoopbackStreamStateControl(void); -void Test_StreamMinBufferSize(void); -void Test_StreamMaxBufferSize(void); -void Test_StreamBufferSize(HNSTIME requestedPeriodicity); -void Test_StreamDifferentFormat(void); -void Test_LoopbackStreamDifferentFormat(void); -void Test_StreamMultipleModes(void); - -// Compliance test -void Test_VerifyPinWaveRTConformance(void);
\ No newline at end of file |
