diff options
| author | Girish Pattabiraman <[email protected]> | 2018-08-13 10:54:19 -0700 |
|---|---|---|
| committer | Adonais Romero González <[email protected]> | 2018-08-13 10:54:19 -0700 |
| commit | 2af3e666d01a60568c2875b4ccbd56b122ad714f (patch) | |
| tree | 29825a7b7cb0f6a9f77f1ab6e5813665d45ca4cc /audio/sysvad/BthhfpDevice.cpp | |
| parent | 181c180357cdf9a04d5e3bb21a8e86dc2155ca69 (diff) | |
Update sample to get ready for RS4 changes - merge pre-RS4 fixes. (#256)
* Format Changes
==============
* Add KSEVENT_PINCAPS_FORMATCHANGE to Bluetooth HFP Endpoints
* Remove 8 bit formats from Bluetooth HFP Endpoints
Name Template
==============
* Bluetooth Endpoints are now based on templates in INF and then customized with endpoints specific data
Sideband Common Interface for future Sideband buses
===================================================
* Add ISidebandDevice
* Add current directory to TabletAudioSample include path. Remove unused compiler flags.
* RS4 bug fixes
Diffstat (limited to 'audio/sysvad/BthhfpDevice.cpp')
| -rw-r--r-- | audio/sysvad/BthhfpDevice.cpp | 3319 |
1 files changed, 3319 insertions, 0 deletions
diff --git a/audio/sysvad/BthhfpDevice.cpp b/audio/sysvad/BthhfpDevice.cpp new file mode 100644 index 00000000..3d0a388f --- /dev/null +++ b/audio/sysvad/BthhfpDevice.cpp @@ -0,0 +1,3319 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + common.cpp + +Abstract: + + Implementation of the BthHfpDevice 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_BTH_BYPASS +#include <limits.h> +#include <bthhfpddi.h> +#include <wdmguid.h> // guild-arrival/removal +#include <devpkey.h> +#include "bthhfpminipairs.h" +#include "BthhfpDevice.h" + +// +// BthHfpDevice implementation. +// + +// # of sec before sync request is cancelled. +#define BTH_HFP_SYNC_REQ_TIMEOUT_IN_SEC 60 +#define BTH_HFP_NOTIFICATION_MAX_ERROR_COUNT 5 + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP +BthHfpDevice::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + +QueryInterface routine for BthHfpDevice + +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_IBthHfpDeviceCommon)) + { + *Object = PVOID(PSIDEBANDDEVICECOMMON(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + PUNKNOWN(*Object)->AddRef(); + return STATUS_SUCCESS; + } + + return STATUS_INVALID_PARAMETER; +} // NonDelegatingQueryInterface + + //============================================================================= + // Dummy stubs to override the default WDF behavior of closing the target + // on query remove. This driver closes and deletes the supporting objects + // when the target removes the BTH HFP SCO Bypass interface. + // + +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::EvtBthHfpTargetQueryRemove +( + _In_ WDFIOTARGET IoTarget +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::EvtBthHfpTargetQueryRemove]")); + + UNREFERENCED_PARAMETER(IoTarget); + return STATUS_SUCCESS; +} + +#pragma code_seg("PAGE") +VOID +BthHfpDevice::EvtBthHfpTargetRemoveCanceled +( + _In_ WDFIOTARGET IoTarget +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::EvtBthHfpTargetRemoveCanceled]")); + + UNREFERENCED_PARAMETER(IoTarget); +} +#pragma code_seg("PAGE") +VOID +BthHfpDevice::EvtBthHfpTargetRemoveComplete +( + _In_ WDFIOTARGET IoTarget +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::EvtBthHfpTargetRemoveComplete]")); + + UNREFERENCED_PARAMETER(IoTarget); +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::Init +( + _In_ IAdapterCommon * Adapter, + _In_ PUNICODE_STRING SymbolicLinkName +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::Init]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationWorkItemContext *wiCtx = NULL; + BthHfpDeviceNotificationReqContext *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 = eBthHfpStateInitializing; + m_Adapter = Adapter; + + // Static config. + m_WdfIoTarget = NULL; + m_SpeakerMiniports = NULL; + m_MicMiniports = NULL; + m_UnknownSpeakerTopology = NULL; + m_UnknownSpeakerWave = NULL; + m_UnknownMicTopology = NULL; + m_UnknownMicWave = NULL; + m_Descriptor = NULL; + m_VolumePropValues = NULL; + + // Notification updates. + m_SpeakerVolumeLevel = 0; + m_MicVolumeLevel = 0; + m_ConnectionStatusLong = FALSE; + m_StreamStatusLong = STATUS_INVALID_DEVICE_STATE; // Sco stream is not open. + m_NRECDisableStatusLong = FALSE; + + m_StreamReq = NULL; + m_SpeakerVolumeReq = NULL; + m_MicVolumeReq = NULL; + m_ConnectionReq = NULL; + m_NRECDisableStatusReq = NULL; + + m_WorkItem = NULL; + m_ReqCollection = NULL; + m_CodecId = 0; + + m_nStreams = 0; + + KeInitializeEvent(&m_StreamStatusEvent, NotificationEvent, TRUE); + + InitializeListHead(&m_ListEntry); + KeInitializeSpinLock(&m_Lock); + + RtlZeroMemory(&m_SymbolicLinkName, sizeof(m_SymbolicLinkName)); + + RtlZeroMemory(&m_SpeakerVolumeCallback, sizeof(m_SpeakerVolumeCallback)); + RtlZeroMemory(&m_SpeakerConnectionStatusCallback, sizeof(m_SpeakerConnectionStatusCallback)); + RtlZeroMemory(&m_MicVolumeCallback, sizeof(m_MicVolumeCallback)); + RtlZeroMemory(&m_MicConnectionStatusCallback, sizeof(m_MicConnectionStatusCallback)); + RtlZeroMemory(&m_SpeakerFormatChangeCallback, sizeof(m_SpeakerFormatChangeCallback)); + RtlZeroMemory(&m_MicFormatChangeCallback, sizeof(m_MicFormatChangeCallback)); + + // + // Allocate a notification WDF work-item. + // + WDF_WORKITEM_CONFIG_INIT(&wiConfig, EvtBthHfpDeviceNotificationStatusWorkItem); + wiConfig.AutomaticSerialization = FALSE; + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationWorkItemContext); + attributes.ParentObject = Adapter->GetWdfDevice(); + ntStatus = WdfWorkItemCreate(&wiConfig, + &attributes, + &m_WorkItem); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfWorkItemCreate failed: 0x%x", ntStatus)), + Done); + + wiCtx = GetBthHfpDeviceNotificationWorkItemContext(m_WorkItem); + wiCtx->BthHfpDevice = 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, ("Init: 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, ("Init: WdfIoTargetCreate failed: 0x%x", ntStatus)), + Done); + + WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME( + &openParams, + SymbolicLinkName, + STANDARD_RIGHTS_ALL); + + openParams.EvtIoTargetQueryRemove = EvtBthHfpTargetQueryRemove; + openParams.EvtIoTargetRemoveCanceled = EvtBthHfpTargetRemoveCanceled; + openParams.EvtIoTargetRemoveComplete = EvtBthHfpTargetRemoveComplete; + + ntStatus = WdfIoTargetOpen(m_WdfIoTarget, &openParams); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: 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) ExAllocatePoolWithTag(NonPagedPoolNx, + SymbolicLinkName->MaximumLength, + MINADAPTER_POOLTAG); + if (m_SymbolicLinkName.Buffer == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: ExAllocatePoolWithTag failed, out of memory")), + Done); + + RtlCopyUnicodeString(&m_SymbolicLinkName, SymbolicLinkName); + + // + // Allocate the WDF requests for status notifications. + // + + // + // IOCTL_BTHHFP_DEVICE_GET_NRECDISABLE_STATUS_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_NRECDisableStatusReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfRequestCreate(Nrec-disable status) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetBthHfpDeviceNotificationReqContext(m_NRECDisableStatusReq); + reqCtx->BthHfpDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.bImmediate), + &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.BoolStatus), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_SpeakerVolumeReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfRequestCreate(Speaker-Volume) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetBthHfpDeviceNotificationReqContext(m_SpeakerVolumeReq); + reqCtx->BthHfpDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.bImmediate), + &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.Volume), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_MicVolumeReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfRequestCreate(Mic-Volume) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetBthHfpDeviceNotificationReqContext(m_MicVolumeReq); + reqCtx->BthHfpDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.bImmediate), + &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.Volume), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext); + 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 = GetBthHfpDeviceNotificationReqContext(m_ConnectionReq); + reqCtx->BthHfpDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.bImmediate), + &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.BoolStatus), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_BTHHFP_STREAM_GET_STATUS_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_StreamReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfRequestCreate(Stream-Status) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetBthHfpDeviceNotificationReqContext(m_StreamReq); + reqCtx->BthHfpDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.bImmediate), + &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.NtStatus), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: 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, ("Init: Creating unqie filter names for the hfp 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 = eBthHfpStateRunning; + + // + // Init successful. + // + ntStatus = STATUS_SUCCESS; + +Done: + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +BthHfpDevice::~BthHfpDevice +( + void +) +/*++ + +Routine Description: + +Destructor for BthHfpDevice. + +Arguments: + +Return Value: + +void + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::~BthHfpDevice]")); + + ASSERT(m_State != eBthHfpStateRunning); + 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; + + DeleteCustomEndpointMinipair(m_MicMiniports); + m_MicMiniports = NULL; + + if (m_Descriptor != NULL) + { + ExFreePoolWithTag(m_Descriptor, MINADAPTER_POOLTAG); + m_Descriptor = NULL; + } + + if (m_VolumePropValues != NULL) + { + ExFreePoolWithTag(m_VolumePropValues, MINADAPTER_POOLTAG); + m_VolumePropValues = NULL; + } + + // + // Free Irps. + // + if (m_SpeakerVolumeReq != NULL) + { + BthHfpDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetBthHfpDeviceNotificationReqContext(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_MicVolumeReq != NULL) + { + BthHfpDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetBthHfpDeviceNotificationReqContext(m_MicVolumeReq); + 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_MicVolumeReq); + m_MicVolumeReq = NULL; + } + + if (m_ConnectionReq != NULL) + { + BthHfpDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetBthHfpDeviceNotificationReqContext(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_StreamReq != NULL) + { + BthHfpDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetBthHfpDeviceNotificationReqContext(m_StreamReq); + 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_StreamReq); + m_StreamReq = 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_UnknownMicTopology == NULL); + SAFE_RELEASE(m_UnknownMicTopology); + + ASSERT(m_UnknownMicWave == NULL); + SAFE_RELEASE(m_UnknownMicWave); + + ASSERT(m_nStreams == 0); + + ASSERT(m_SpeakerVolumeCallback.Handler == NULL); + ASSERT(m_SpeakerConnectionStatusCallback.Handler == NULL); + ASSERT(m_MicVolumeCallback.Handler == NULL); + ASSERT(m_MicConnectionStatusCallback.Handler == NULL); + ASSERT(m_SpeakerFormatChangeCallback.Handler == NULL); + ASSERT(m_MicFormatChangeCallback.Handler == NULL); +} // ~BthHfpDevice + +// +// ISidebandDeviceCommon implementation. +// + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +BthHfpDevice::IsVolumeSupported +( + _In_ eDeviceType deviceType +) +{ + UNREFERENCED_PARAMETER(deviceType); + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::IsVolumeSupported]")); + + return m_Descriptor->SupportsVolume; +} + +//============================================================================= +#pragma code_seg("PAGE") +PVOID +BthHfpDevice::GetVolumeSettings +( + _In_ eDeviceType deviceType, + _Out_ PULONG Size +) +{ + UNREFERENCED_PARAMETER(deviceType); + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetVolumeSettings]")); + + ASSERT(Size != NULL); + + *Size = m_Descriptor->VolumePropertyValuesSize; + + return m_VolumePropValues; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::GetVolume +( + _In_ eDeviceType DeviceType, + _In_ LONG Channel, + _Out_ LONG *pVolume +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Channel); + DPF_ENTER(("[BthHfpDevice::GetSpeakerVolume]")); + + NTSTATUS status = STATUS_SUCCESS; + + ASSERT(pVolume); + + if (eBthHfpSpeakerDevice == DeviceType) + { + status = GetBthHfpSpeakerVolume(pVolume); + IF_FAILED_ACTION_JUMP( + status, + DPF(D_ERROR, ("Start: GetBthHfpSpeakerVolume: failed, 0x%x", status)), + Done); + + InterlockedExchange(&m_SpeakerVolumeLevel, *pVolume); + } + else if(eBthHfpMicDevice == DeviceType) + { + status = GetBthHfpMicVolume(pVolume); + IF_FAILED_ACTION_JUMP( + status, + DPF(D_ERROR, ("Start: GetBthHfpMicVolume: failed, 0x%x", status)), + Done); + + InterlockedExchange(&m_MicVolumeLevel, *pVolume); + } + else + { + ASSERTMSG("Invalid device type", FALSE); + status = STATUS_INVALID_PARAMETER; + } + +Done: + return status; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetVolume +( + _In_ eDeviceType DeviceType, + _In_ LONG Channel, + _In_ LONG Volume +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Channel); + DPF_ENTER(("[BthHfpDevice::SetSpeakerVolume]")); + + if (eBthHfpSpeakerDevice == DeviceType) + { + return SetBthHfpSpeakerVolume(Volume); + } + else if(eBthHfpMicDevice == DeviceType) + { + return SetBthHfpMicVolume(Volume); + } + else + { + ASSERTMSG("Invalid device type", FALSE); + return STATUS_INVALID_PARAMETER; + } +} + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +BthHfpDevice::IsMuteSupported +( + _In_ eDeviceType DeviceType +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(DeviceType); + DPF_ENTER(("[BthHfpDevice::IsMuteSupported]")); + + return FALSE; +} + +//============================================================================= +#pragma code_seg("PAGE") +PVOID +BthHfpDevice::GetMuteSettings +( + _In_ eDeviceType DeviceType, + _Out_ PULONG Size +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(DeviceType); + UNREFERENCED_PARAMETER(Size); + DPF_ENTER(("[BthHfpDevice::GetMuteSettings]")); + + return NULL; +} + +//============================================================================= +#pragma code_seg("PAGE") +LONG +BthHfpDevice::GetMute +( + _In_ eDeviceType DeviceType, + _In_ LONG Channel +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(DeviceType); + UNREFERENCED_PARAMETER(Channel); + DPF_ENTER(("[BthHfpDevice::GetMute]")); + + return 0; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetMute +( + _In_ eDeviceType DeviceType, + _In_ LONG Channel, + _In_ LONG Mute +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(DeviceType); + UNREFERENCED_PARAMETER(Channel); + UNREFERENCED_PARAMETER(Mute); + DPF_ENTER(("[BthHfpDevice::SetMute]")); + + return STATUS_NOT_IMPLEMENTED; +} + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +BthHfpDevice::GetConnectionStatus() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetConnectionStatus]")); + + return (BOOL)InterlockedCompareExchange(&m_ConnectionStatusLong, 0, 0); +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::GetBthHfpCodecId(_Out_ UCHAR * CodecId) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetBthHfpCodecId]")); + + ASSERT(CodecId != NULL); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + union { + HFP_BYPASS_CODEC_ID_V1 CodecIdV1; + HFP_BYPASS_CODEC_ID_VERSION Version; + } value; + + *CodecId = 0; + + value.Version = REQ_HFP_BYPASS_CODEC_ID_V1; + + // + // Get the Bth HFP SCO Codec ID. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_DEVICE_GET_CODEC_ID, + sizeof(value.Version), + sizeof(value.CodecIdV1), + &value); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpCodecId: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_CODEC_ID) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *CodecId = value.CodecIdV1.CodecId; + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::Connect() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::Connect]")); + + return SetBthHfpConnect(); +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::Disconnect() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::Disconnect]")); + + return SetBthHfpDisconnect(); +} + +//============================================================================= +#pragma code_seg() +BOOL +BthHfpDevice::GetStreamStatus(_In_ eDeviceType deviceType) +{ + UNREFERENCED_PARAMETER(deviceType); + DPF_ENTER(("[BthHfpDevice::GetStreamStatus]")); + + NTSTATUS ntStatus; + + ntStatus = (NTSTATUS)InterlockedCompareExchange(&m_StreamStatusLong, 0, 0); + + return NT_SUCCESS(ntStatus) ? TRUE : FALSE; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::StreamOpen(_In_ eDeviceType deviceType) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(deviceType); + DPF_ENTER(("[BthHfpDevice::StreamOpen]")); + + // BTH HFP DDI has stream open and close. + // No start and suspend commands + // START = BTH HFP STREAM OPEN + // SUSPEND = BTH HFP STREAM CLOSE + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::StreamStart +( + _In_ eDeviceType deviceType +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(deviceType); + DPF_ENTER(("[BthHfpDevice::StreamStart]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + LONG nStreams = 0; + + ASSERT(m_nStreams >= 0); + + nStreams = InterlockedIncrement(&m_nStreams); + if (nStreams == 1) + { + BOOLEAN streamStart = FALSE; + + ntStatus = SetBthHfpStreamOpen(); + if (NT_SUCCESS(ntStatus)) + { + streamStart = TRUE; + m_StreamStatus = STATUS_SUCCESS; + ntStatus = EnableBthHfpStreamStatusNotification(); + } + + // + // Cleanup if any error. + // + if (!NT_SUCCESS(ntStatus)) + { + nStreams = InterlockedDecrement(&m_nStreams); + ASSERT(nStreams == 0); + UNREFERENCED_VAR(nStreams); + + if (streamStart) + { + SetBthHfpStreamClose(); + } + + m_StreamStatus = STATUS_INVALID_DEVICE_STATE; + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::StreamSuspend +( + _In_ eDeviceType deviceType +) +{ + UNREFERENCED_PARAMETER(deviceType); + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::StreamSuspend]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + LONG nStreams = 0; + + ASSERT(m_nStreams > 0); + + nStreams = InterlockedDecrement(&m_nStreams); + if (nStreams == 0) + { + ntStatus = SetBthHfpStreamClose(); + + StopBthHfpStreamStatusNotification(); + + m_StreamStatus = STATUS_INVALID_DEVICE_STATE; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::StreamClose +( + _In_ eDeviceType deviceType +) +{ + UNREFERENCED_PARAMETER(deviceType); + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::StreamClose]")); + + // BTH HFP DDI has stream open and close. + // No start and suspend commands + // START = BTH HFP STREAM OPEN + // SUSPEND = BTH HFP STREAM CLOSE + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +GUID +BthHfpDevice::GetContainerId(_In_ eDeviceType deviceType) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetContainerId]")); + + UNREFERENCED_PARAMETER(deviceType); + + return m_Descriptor->ContainerId; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::SetVolumeHandler +( + _In_ eDeviceType deviceType, + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetVolumeHandler]")); + + if (deviceType == eBthHfpSpeakerDevice) + { + ASSERT(EventHandler == NULL || m_SpeakerVolumeCallback.Handler == NULL); + + m_SpeakerVolumeCallback.Handler = EventHandler; // weak ref. + m_SpeakerVolumeCallback.Context = EventHandlerContext; + } + else if (deviceType == eBthHfpMicDevice) + { + ASSERT(EventHandler == NULL || m_MicVolumeCallback.Handler == NULL); + + m_MicVolumeCallback.Handler = EventHandler; // weak ref. + m_MicVolumeCallback.Context = EventHandlerContext; + } +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::SetMuteHandler +( + _In_ eDeviceType deviceType, + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetMuteHandler]")); + + if (deviceType == eBthHfpSpeakerDevice) + { + ASSERT(EventHandler == NULL || m_SpeakerMuteCallback.Handler == NULL); + + m_SpeakerMuteCallback.Handler = EventHandler; // weak ref. + m_SpeakerMuteCallback.Context = EventHandlerContext; + } + else if (deviceType == eBthHfpMicDevice) + { + ASSERT(EventHandler == NULL || m_MicMuteCallback.Handler == NULL); + + m_MicMuteCallback.Handler = EventHandler; // weak ref. + m_MicMuteCallback.Context = EventHandlerContext; + } +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::SetConnectionStatusHandler +( + _In_ eDeviceType deviceType, + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetConnectionStatusHandler]")); + + if (deviceType == eBthHfpSpeakerDevice) + { + ASSERT(EventHandler == NULL || m_SpeakerConnectionStatusCallback.Handler == NULL); + + m_SpeakerConnectionStatusCallback.Handler = EventHandler; // weak ref. + m_SpeakerConnectionStatusCallback.Context = EventHandlerContext; + } + else if (deviceType == eBthHfpMicDevice) + { + ASSERT(EventHandler == NULL || m_MicConnectionStatusCallback.Handler == NULL); + + m_MicConnectionStatusCallback.Handler = EventHandler; // weak ref. + m_MicConnectionStatusCallback.Context = EventHandlerContext; + } + +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::SetFormatChangeHandler +( + _In_ eDeviceType deviceType, + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetFormatChangeHandler]")); + + if (deviceType == eBthHfpSpeakerDevice) + { + ASSERT(EventHandler == NULL || m_SpeakerFormatChangeCallback.Handler == NULL); + + m_SpeakerFormatChangeCallback.Handler = EventHandler; // weak ref. + m_SpeakerFormatChangeCallback.Context = EventHandlerContext; + } + else if (deviceType == eBthHfpMicDevice) + { + ASSERT(EventHandler == NULL || m_MicFormatChangeCallback.Handler == NULL); + + m_MicFormatChangeCallback.Handler = EventHandler; // weak ref. + m_MicFormatChangeCallback.Context = EventHandlerContext; + } + +} + +//============================================================================= +#pragma code_seg("PAGE") +PPIN_DEVICE_FORMATS_AND_MODES +BthHfpDevice::GetFormatsAndModes +( + _In_ eDeviceType deviceType +) +{ + PPIN_DEVICE_FORMATS_AND_MODES pFormatsAndModes = NULL; + ULONG endpointIndex = 0; + + + // CVSD is the narrow band codec for SCO. Wideband codec IDs are any number higher than 1. + // mSBC is the only required wideband codec, though the controller+headset combination may + // support other wideband codecs. + + enum + { + BTHHFP_CODEC_CVSD = 0, + BTHHFP_CODEC_mSBC, + + BTHHFP_CODEC_INVALID + }; + + if (m_CodecId > BTHHFP_CODEC_CVSD) + { + // Use the miniport tables that support 16kHz + endpointIndex = 1; + } + + if (deviceType == eBthHfpSpeakerDevice) + { + pFormatsAndModes = g_BthHfpRenderEndpoints[endpointIndex]->PinDeviceFormatsAndModes; + } + else if (deviceType == eBthHfpMicDevice) + { + pFormatsAndModes = g_BthHfpCaptureEndpoints[endpointIndex]->PinDeviceFormatsAndModes; + } + + return pFormatsAndModes; +} + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +BthHfpDevice::IsNRECSupported() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::IsNRECSupported]")); + + return m_Descriptor->SupportsNREC; +} + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +BthHfpDevice::GetNRECDisableStatus() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetNRECDisableStatus]")); + + // Return TRUE if HF wants to disable the NREC on the AG. + return (BOOL)InterlockedCompareExchange(&m_NRECDisableStatusLong, 0, 0); +} + +// +// Helper functions. +// + +//============================================================================= +#pragma code_seg() +NTSTATUS +BthHfpDevice::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 BTH HFP +SCO Bypass device. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::SendIoCtrlAsynchronously]")); + + 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, ("SendIoCtrlAsynchronously: 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, ("SendIoCtrlAsynchronously: WdfRequestSend(0x%x) failed, 0x%x", IoControlCode, ntStatus)); + goto Done; + } + + // + // All Done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::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 BTH HFP +SCO Bypass device. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SendIoCtrlSynchronously]")); + + 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(BTH_HFP_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, ("SendIoCtrlSynchronously: WdfIoTargetSendIoctlSynchronously(0x%x) failed, 0x%x", IoControlCode, ntStatus)), + Done); + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +VOID +BthHfpDevice::EvtBthHfpDeviceNotificationStatusWorkItem +( + _In_ WDFWORKITEM WorkItem +) +/*++ + +Routine Description: + +The function processes status notification updates. + +Arguments: + +WorkItem - WDF work-item object. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EvtBthHfpDeviceNotificationStatusWorkItem]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDevice * This; + KIRQL oldIrql; + + if (WorkItem == NULL) + { + return; + } + + This = GetBthHfpDeviceNotificationWorkItemContext(WorkItem)->BthHfpDevice; + ASSERT(This != NULL); + + for (;;) + { + BOOL resend = TRUE; + WDFREQUEST req = NULL; + BthHfpDeviceNotificationReqContext * 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 = GetBthHfpDeviceNotificationReqContext(req); + ASSERT(reqCtx != NULL); + + // + // Handle this notification. + // + if (NT_SUCCESS(params.IoStatus.Status)) + { + switch (params.Parameters.Ioctl.IoControlCode) + { + case IOCTL_BTHHFP_DEVICE_GET_NRECDISABLE_STATUS_UPDATE: + { + InterlockedExchange(&This->m_NRECDisableStatusLong, (LONG)reqCtx->Buffer.BoolStatus); + } + break; + + case IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE: + { + LONG oldVolume; + + oldVolume = InterlockedExchange(&This->m_SpeakerVolumeLevel, reqCtx->Buffer.Volume); + if (reqCtx->Buffer.Volume != oldVolume) + { + // Notify audio miniport about this change. + if (This->m_SpeakerVolumeCallback.Handler != NULL) + { + This->m_SpeakerVolumeCallback.Handler( + This->m_SpeakerVolumeCallback.Context); + } + } + } + break; + + case IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE: + { + LONG oldVolume; + + oldVolume = InterlockedExchange(&This->m_MicVolumeLevel, reqCtx->Buffer.Volume); + if (reqCtx->Buffer.Volume != oldVolume) + { + // Notify audio miniport about this change. + if (This->m_MicVolumeCallback.Handler != NULL) + { + This->m_MicVolumeCallback.Handler( + This->m_MicVolumeCallback.Context); + } + } + } + break; + + case IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE: + { + BOOL oldStatus; + // + + // + // Get codec id (if non-zero, connection supports Wideband Speech) + // + UCHAR codecId = 0; + UCHAR oldCodecId = 0; + if (reqCtx->Buffer.BoolStatus) + { + ntStatus = This->GetBthHfpCodecId(&codecId); + if (ntStatus == STATUS_INVALID_DEVICE_REQUEST) + { + // GetBthHfpCodecId fails with STATUS_INVALID_DEVICE_REQUEST if the system doesn't + // support Wideband Speech (currently only Mobile supports this call) + ntStatus = STATUS_SUCCESS; + } + + if (NT_SUCCESS(ntStatus)) + { + oldCodecId = (UCHAR)InterlockedExchange(&This->m_CodecId, codecId); + if (codecId != oldCodecId) + { + // Notify audio miniport about this change. + if (This->m_SpeakerFormatChangeCallback.Handler != NULL) + { + This->m_SpeakerFormatChangeCallback.Handler( + This->m_SpeakerFormatChangeCallback.Context); + } + + if (This->m_MicFormatChangeCallback.Handler != NULL) + { + This->m_MicFormatChangeCallback.Handler( + This->m_MicFormatChangeCallback.Context); + } + } + } + } + + oldStatus = (BOOL)InterlockedExchange(&This->m_ConnectionStatusLong, (LONG)reqCtx->Buffer.BoolStatus); + if (reqCtx->Buffer.BoolStatus != oldStatus) + { + // Notify audio miniport about this change. + if (This->m_SpeakerConnectionStatusCallback.Handler != NULL) + { + This->m_SpeakerConnectionStatusCallback.Handler( + This->m_SpeakerConnectionStatusCallback.Context); + } + + if (This->m_MicConnectionStatusCallback.Handler != NULL) + { + This->m_MicConnectionStatusCallback.Handler( + This->m_MicConnectionStatusCallback.Context); + } + } + } + break; + + default: + // This should never happen. + resend = FALSE; + DPF(D_ERROR, ("EvtBthHfpDeviceNotificationStatusWorkItem: 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, ("EvtBthHfpDeviceNotificationStatusWorkItem: WdfRequestReuse failed, 0x%x", ntStatus)); + break; + } + + // Resend status notification request. + reqCtx->Buffer.bImmediate = FALSE; + + ntStatus = This->SendIoCtrlAsynchronously( + req, + params.Parameters.Ioctl.IoControlCode, + reqCtx->MemIn, + reqCtx->MemOut, + EvtBthHfpDeviceNotificationStatusCompletion, + This); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("EvtBthHfpDeviceNotificationStatusWorkItem: SendIoCtrlAsynchronously" + "(0x%x) failed, 0x%x", + params.Parameters.Ioctl.IoControlCode, ntStatus)); + break; + } + } + } +} + +//============================================================================= +#pragma code_seg() +void +BthHfpDevice::EvtBthHfpDeviceNotificationStatusCompletion +( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, + _In_ WDFCONTEXT Context +) +{ + DPF_ENTER(("[BthHfpDevice::EvtBthHfpDeviceNotificationStatusCompletion]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * ctx = NULL; + BthHfpDevice * This = NULL; + KIRQL oldIrql; + + UNREFERENCED_PARAMETER(Target); + UNREFERENCED_PARAMETER(Context); + + ctx = GetBthHfpDeviceNotificationReqContext(Request); + This = ctx->BthHfpDevice; + ASSERT(This != NULL); + + ntStatus = Params->IoStatus.Status; + if (ntStatus == STATUS_CANCELLED) + { + // BTH HFP device is shutting down. Do not re-send this request. + goto Done; + } + + // + // If something is wrong with the HFP interface, do not loop forever. + // + if (!NT_SUCCESS(ntStatus)) + { + if (++ctx->Errors > BTH_HFP_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 +BthHfpDevice::SetBthHfpDeviceCapabilities +( + _In_ BOOL bSupports16kHzSampling + +) +/*++ + +Routine Description: + +This function synchronously informs the Audio Driver capabilities to BthHfp stack. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetBthHfpDeviceCapabilities]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BTHHFP_AUDIO_DEVICE_CAPABILTIES deviceCaps = { 0 }; + + BTHHFP_AUDIO_DEVICE_CAPABILTIES_INIT(&deviceCaps); + deviceCaps.Supports16kHzSampling = bSupports16kHzSampling; + + // + // Get the Bth HFP SCO Bypass speaker volume. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_DEVICE_INDICATE_AUDIO_DEVICE_CAPABILITIES, + sizeof(BTHHFP_AUDIO_DEVICE_CAPABILTIES), + 0, + &deviceCaps); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetBthHfpDeviceCapabilities: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_INDICATE_AUDIO_DEVICE_CAPABILITIES) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::GetBthHfpDescriptor +( + _Out_ PBTHHFP_DESCRIPTOR2 * Descriptor +) +/*++ + +Routine Description: + +This function synchronously gets the remote Bluetooth Hands-Free Profile SCO +Bypass descriptor. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetBthHfpDescriptor]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + WDFREQUEST req = NULL; + PBTHHFP_DESCRIPTOR2 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, ("GetBthHfpDescriptor: WdfRequestCreate failed, 0x%x", ntStatus)), + Done); + + // + // Get the size of the buffer. + // + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2, + NULL, + NULL, + NULL); + + if (ntStatus != STATUS_BUFFER_TOO_SMALL) + { + if (NT_SUCCESS(ntStatus)) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + } + + DPF(D_ERROR, ("GetBthHfpDescriptor: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2): 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, ("GetBthHfpDescriptor: IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2 buffer too big (%Id): 0x%x", information, ntStatus)); + goto Done; + } + + length = (ULONG)information; + + // + // Allocate memory needed to hold the info. + // + descriptor = (PBTHHFP_DESCRIPTOR2)ExAllocatePoolWithTag(NonPagedPoolNx, length, MINADAPTER_POOLTAG); + if (descriptor == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpDescriptor: ExAllocatePoolWithTag failed, out of memory")), + Done); + + // + // Get the Bth HFP SCO Bypass 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, ("GetBthHfpDescriptor: WdfRequestReuse failed, 0x%x", ntStatus)), + Done); + + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2, + NULL, + length, + descriptor); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpDescriptor: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2) 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() +NTSTATUS +BthHfpDevice::EnableBthHfpNrecDisableStatusNotification() +/*++ + +Routine Description: + +This function registers for Bluetooth Hands-Free Profile SCO Bypass NREC-Disable +status change notification. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EnableBthHfpNrecDisableStatusNotification]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * ctx = NULL; + + ctx = GetBthHfpDeviceNotificationReqContext(m_NRECDisableStatusReq); + + // + // This is a notification request. + // + ctx->Buffer.bImmediate = FALSE; + + // + // Get the Bth HFP SCO Bypass NREC-Disable status (async). + // + ntStatus = SendIoCtrlAsynchronously( + m_NRECDisableStatusReq, + IOCTL_BTHHFP_DEVICE_GET_NRECDISABLE_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtBthHfpDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("EnableBthHfpNrecDisableStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_DEVICE_GET_NRECDISABLE_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::GetBthHfpVolumePropertyValues +( + _In_ ULONG Length, + _Out_ PKSPROPERTY_VALUES * PropValues +) +/*++ + +Routine Description: + +This function synchronously gets the remote Bluetooth Hands-Free Profile SCO +Bypass volume values. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetBthHfpVolumePropertyValues]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PKSPROPERTY_VALUES propValues = NULL; + + *PropValues = NULL; + + // + // Allocate memory. + // + propValues = (PKSPROPERTY_VALUES)ExAllocatePoolWithTag(NonPagedPoolNx, Length, MINADAPTER_POOLTAG); + if (propValues == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpVolumePropertyValues: ExAllocatePoolWithTag failed, out of memory")), + Done); + + // + // Get the Bth HFP SCO Bypass descriptor. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_DEVICE_GET_VOLUMEPROPERTYVALUES, + 0, + Length, + propValues); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpVolumePropertyValues: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_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 +BthHfpDevice::SetBthHfpSpeakerVolume +( + _In_ LONG Volume +) +/*++ + +Routine Description: + +This function synchronously sets the remote Bluetooth Hands-Free Profile SCO +Bypass speaker volume. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetBthHfpSpeakerVolume]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + // + // Get the Bth HFP SCO Bypass speaker volume. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_SPEAKER_SET_VOLUME, + sizeof(Volume), + 0, + &Volume); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetBthHfpSpeakerVolume: SendIoCtrlSynchronously(IOCTL_BTHHFP_SPEAKER_SET_VOLUME) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::GetBthHfpSpeakerVolume +( + _Out_ LONG * Volume +) +/*++ + +Routine Description: + +This function synchronously gets the remote Bluetooth Hands-Free Profile SCO +Bypass speaker volume. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetBthHfpSpeakerVolume]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationBuffer buffer = { 0 }; + + *Volume = 0; + + buffer.bImmediate = TRUE; + + // + // Get the Bth HFP SCO Bypass speaker volume. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE, + sizeof(buffer.bImmediate), + sizeof(buffer.Volume), + &buffer); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpSpeakerVolume: SendIoCtrlSynchronously(IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *Volume = buffer.Volume; + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +BthHfpDevice::EnableBthHfpSpeakerVolumeStatusNotification() +/*++ + +Routine Description: + +This function registers for Bluetooth Hands-Free Profile SCO Bypass speaker +volume change notification. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EnableBthHfpSpeakerVolumeStatusNotification]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * ctx = NULL; + + ctx = GetBthHfpDeviceNotificationReqContext(m_SpeakerVolumeReq); + + // + // This is a notification request. + // + ctx->Buffer.bImmediate = FALSE; + + // + // Register for speaker volume updates. + // + ntStatus = SendIoCtrlAsynchronously( + m_SpeakerVolumeReq, + IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtBthHfpDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("EnableBthHfpSpeakerVolumeStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetBthHfpMicVolume +( + _In_ LONG Volume +) +/*++ + +Routine Description: + +This function synchronously sets the remote Bluetooth Hands-Free Profile SCO +Bypass mic volume. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetBthHfpMicVolume]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + // + // Get the Bth HFP SCO Bypass mic volume. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_MIC_SET_VOLUME, + sizeof(Volume), + 0, + &Volume); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetBthHfpMicVolume: SendIoCtrlSynchronously(IOCTL_BTHHFP_MIC_SET_VOLUME) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::GetBthHfpMicVolume +( + _Out_ LONG * Volume +) +/*++ + +Routine Description: + +This function synchronously gets the remote Bluetooth Hands-Free Profile SCO +Bypass mic volume. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetBthHfpMicVolume]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationBuffer buffer = { 0 }; + + *Volume = 0; + + buffer.bImmediate = TRUE; + + // + // Get the Bth HFP SCO Bypass mic volume. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE, + sizeof(buffer.bImmediate), + sizeof(buffer.Volume), + &buffer); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpMicVolume: SendIoCtrlSynchronously(IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *Volume = buffer.Volume; + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +BthHfpDevice::EnableBthHfpMicVolumeStatusNotification() +/*++ + +Routine Description: + +This function registers for Bluetooth Hands-Free Profile SCO Bypass mic +volume change notification. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EnableBthHfpMicVolumeStatusNotification]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * ctx = NULL; + + ctx = GetBthHfpDeviceNotificationReqContext(m_MicVolumeReq); + + // + // This is a notification request. + // + ctx->Buffer.bImmediate = FALSE; + + // + // Register for mic volume updates. + // + ntStatus = SendIoCtrlAsynchronously( + m_MicVolumeReq, + IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtBthHfpDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("EnableBthHfpMicVolumeStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::GetBthHfpConnectionStatus +( + _Out_ BOOL * ConnectionStatus +) +/*++ + +Routine Description: + +This function synchronously gets the remote Bluetooth Hands-Free Profile SCO +Bypass connection status. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetBthHfpConnectionStatus]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BOOL bValue = TRUE; // In: bImmediate, Out: value. + + *ConnectionStatus = 0; + + // + // Get the Bth HFP SCO Bypass connection status. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE, + sizeof(bValue), + sizeof(bValue), + &bValue); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpConnectionStatus: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *ConnectionStatus = bValue; + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +BthHfpDevice::EnableBthHfpConnectionStatusNotification() +/*++ + +Routine Description: + +This function registers for Bluetooth Hands-Free Profile SCO Bypass +connection status notification. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EnableBthHfpConnectionStatusNotification]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * ctx = NULL; + + ctx = GetBthHfpDeviceNotificationReqContext(m_ConnectionReq); + + // + // Make sure this obj is alive while the IRP is active. + // + ctx->Buffer.bImmediate = FALSE; + + // + // Get the Bth HFP SCO Bypass connection status. + // + ntStatus = SendIoCtrlAsynchronously( + m_ConnectionReq, + IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtBthHfpDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("EnableBthHfpConnectionStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetBthHfpConnect() +/*++ + +Routine Description: + +This function synchronously requests a Bluetooth Hands-Free Profile level +connection to the paired Bluetooth device. + +This request initiates the Service Level Connection establishment procedure +and completes without waiting for the connection procedure to complete. +Connection status can be determined using IOCTL_BTHHFP_GET_CONNECTION_STATUS_UPDATE. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetBthHfpConnect]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_DEVICE_REQUEST_CONNECT, + 0, + 0, + NULL); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetBthHfpConnect: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_REQUEST_CONNECT) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetBthHfpDisconnect() +/*++ + +Routine Description: + +This function synchronously requests a Bluetooth Hands-Free Profile level +connection to the paired Bluetooth device. + +This request initiates disconnection of the Service Level Connection and +completes without waiting for the disconnection to complete. Connection +status can be determined using IOCTL_BTHHFP_GET_CONNECTION_STATUS_UPDATE. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetBthHfpDisconnect]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_DEVICE_REQUEST_DISCONNECT, + 0, + 0, + NULL); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetBthHfpDisconnect: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_REQUEST_DISCONNECT) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +void +BthHfpDevice::EvtBthHfpDeviceStreamStatusCompletion +( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, + _In_ WDFCONTEXT Context +) +/*++ + +Routine Description: + +Completion callback for the Bluetooth Hands-Free Profile SCO Bypass +stream status notification. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EvtBthHfpDeviceStreamStatusCompletion]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * reqCtx = NULL; + BthHfpDevice * This = NULL; + NTSTATUS ntResult = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(Target); + UNREFERENCED_PARAMETER(Context); + + // + // Get the SCO stream status. + // + reqCtx = GetBthHfpDeviceNotificationReqContext(Request); + This = reqCtx->BthHfpDevice; + ASSERT(This != NULL); + + ntStatus = Params->IoStatus.Status; + if (!NT_SUCCESS(ntStatus)) + { + ntResult = STATUS_INVALID_DEVICE_STATE; + } + else + { + ntResult = reqCtx->Buffer.NtStatus; + } + + InterlockedExchange(&This->m_StreamStatusLong, (LONG)ntResult); + + // + // Let the stop routine know we are done. Stop routine will + // re-init the request. + // + KeSetEvent(&This->m_StreamStatusEvent, IO_NO_INCREMENT, FALSE); +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +BthHfpDevice::EnableBthHfpStreamStatusNotification() +/*++ + +Routine Description: + +This function registers for Bluetooth Hands-Free Profile SCO Bypass +stream status notification. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EnableBthHfpStreamStatusNotification]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * ctx = NULL; + + ASSERT(m_nStreams > 0); + + ctx = GetBthHfpDeviceNotificationReqContext(m_StreamReq); + ctx->Buffer.bImmediate = FALSE; + + KeClearEvent(&m_StreamStatusEvent); + + // + // Get the Bth HFP SCO Bypass connection status. + // + ntStatus = SendIoCtrlAsynchronously( + m_StreamReq, + IOCTL_BTHHFP_STREAM_GET_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtBthHfpDeviceStreamStatusCompletion, + this); + + if (!NT_SUCCESS(ntStatus)) + { + KeSetEvent(&m_StreamStatusEvent, IO_NO_INCREMENT, FALSE); + DPF(D_ERROR, ("EnableBthHfpStreamStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_STREAM_GET_STATUS_UPDATE) failed, 0x%x", ntStatus)); + goto Done; + } + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::StopBthHfpStreamStatusNotification() +/*++ + +Routine Description: + +This function stops the Bluetooth Hands-Free Profile SCO Bypass +connection status notification. +The function waits for the request to be done before returning. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::StopBthHfpStreamStatusNotification]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * reqCtx = NULL; + WDF_REQUEST_REUSE_PARAMS reuseParams; + + WdfRequestCancelSentRequest(m_StreamReq); + KeWaitForSingleObject(&m_StreamStatusEvent, Executive, KernelMode, FALSE, NULL); + + reqCtx = GetBthHfpDeviceNotificationReqContext(m_StreamReq); + 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_StreamReq, &reuseParams); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("StopBthHfpStreamStatusNotification: WdfRequestReuse failed, 0x%x", ntStatus)); + } + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetBthHfpStreamOpen() +/*++ + +Routine Description: + +This function synchronously requests an open SCO channel to transmit audio +data over the air. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetBthHfpStreamOpen]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_STREAM_OPEN, + 0, + 0, + NULL); + + if (ntStatus == STATUS_DEVICE_BUSY) + { + // The stream channel is already open. + DPF(D_VERBOSE, ("SetBthHfpStreamOpen: the stream channel is already open")); + ntStatus = STATUS_SUCCESS; + } + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetBthHfpStreamOpen: SendIoCtrlSynchronously(IOCTL_BTHHFP_STREAM_OPEN) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetBthHfpStreamClose() +/*++ + +Routine Description: + +This function synchronously requests to close the SCO channel. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetBthHfpStreamClose]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_STREAM_CLOSE, + 0, + 0, + NULL); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetBthHfpStreamClose: SendIoCtrlSynchronously(IOCTL_BTHHFP_STREAM_CLOSE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::Start() +/*++ + +Routine Description: + +Asynchronously called to start the audio device. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::Start]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BOOL connStatus = FALSE; + UINT bthMiniportsIndex = 0; + + // Indicate audio driver capabilities indicating support for 16KHz sampling rate + ntStatus = SetBthHfpDeviceCapabilities(TRUE); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: SetBthHfpDeviceCapabilities: failed to set Wideband Support, 0x%x", ntStatus)), + Done); + + // + // Get bth hfp descriptor + // + ntStatus = GetBthHfpDescriptor(&m_Descriptor); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: GetBthHfpDescriptor: failed to retrieve BTHHFP_DESCRIPTOR2, 0x%x", ntStatus)), + Done); + + // + // Get valume settings. + // + if (m_Descriptor->SupportsVolume) + { + PKSPROPERTY_VALUES volumePropValues = NULL; + LONG volume = 0; + + // Volume settings. + ntStatus = GetBthHfpVolumePropertyValues( + m_Descriptor->VolumePropertyValuesSize, + &volumePropValues); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: GetBthHfpVolumePropertyValues: failed to retrieve KSPROPERTY_VALUES, 0x%x", ntStatus)), + Done); + + m_VolumePropValues = volumePropValues; + + // Speaker volume. + ntStatus = GetBthHfpSpeakerVolume(&volume); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: GetBthHfpSpeakerVolume: failed, 0x%x", ntStatus)), + Done); + + m_SpeakerVolumeLevel = volume; + + // Mic volume. + ntStatus = GetBthHfpMicVolume(&volume); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: GetBthHfpMicVolume: failed, 0x%x", ntStatus)), + Done); + + m_MicVolumeLevel = volume; + } + + // + // Get connection status. + // + ntStatus = GetBthHfpConnectionStatus(&connStatus); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: GetBthHfpConnectionStatus: failed, 0x%x", ntStatus)), + Done); + + m_ConnectionStatus = connStatus; + + // + // Get codec id (if non-zero, connection supports Wideband Speech) + // + UCHAR codecId = 0; + if (connStatus) + { + ntStatus = GetBthHfpCodecId(&codecId); + if (ntStatus == STATUS_INVALID_DEVICE_REQUEST) + { + // GetBthHfpCodecId fails with STATUS_INVALID_DEVICE_REQUEST if the system doesn't + // support Wideband Speech (currently only Mobile supports this call) + ntStatus = STATUS_SUCCESS; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: GetBthHfpCodecId: failed, 0x%x", ntStatus)), + Done); + } + + m_CodecId = codecId; + + enum + { + BTHHFP_CODEC_CVSD = 0, + BTHHFP_CODEC_mSBC, + + BTHHFP_CODEC_INVALID + }; + + if (codecId > BTHHFP_CODEC_CVSD) + { + // Use the miniport tables that support 16kHz + bthMiniportsIndex = 1; + } + + + // + // Customize the topology/wave descriptors for this instance + // + ntStatus = CreateCustomEndpointMinipair( + g_BthHfpRenderEndpoints[bthMiniportsIndex], + &m_Descriptor->FriendlyName, + &m_Descriptor->OutputPinCategory, + &m_SpeakerMiniports); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: CreateCustomEndpointMinipair for Render: failed, 0x%x", ntStatus)), + Done); + m_SpeakerMiniports->TopoName = m_SpeakerTopologyNameBuffer; + m_SpeakerMiniports->WaveName = m_SpeakerWaveNameBuffer; + + ntStatus = CreateCustomEndpointMinipair( + g_BthHfpCaptureEndpoints[bthMiniportsIndex], + &m_Descriptor->FriendlyName, + &m_Descriptor->InputPinCategory, + &m_MicMiniports); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: CreateCustomEndpointMinipair for Capture: failed, 0x%x", ntStatus)), + Done); + m_MicMiniports->TopoName = m_MicTopologyNameBuffer; + m_MicMiniports->WaveName = m_MicWaveNameBuffer; + + + ASSERT(m_SpeakerMiniports != NULL); + ASSERT(m_MicMiniports != NULL); + _Analysis_assume_(m_SpeakerMiniports != NULL); + _Analysis_assume_(m_MicMiniports != 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, ("Start: InstallEndpointRenderFilters (Bth HFP SCO-Bypass): failed, 0x%x", ntStatus)), + Done); + + ntStatus = m_Adapter->InstallEndpointFilters( + NULL, + m_MicMiniports, + PSIDEBANDDEVICECOMMON(this), + &m_UnknownMicTopology, + &m_UnknownMicWave, NULL, NULL + ); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: InstallEndpointCaptureFilters (Bth HFP SCO-Bypass): failed, 0x%x", ntStatus)), + Done); + + ntStatus = m_Adapter->NotifyEndpointPair( + m_SpeakerTopologyNameBuffer, + SIZEOF_ARRAY(m_SpeakerTopologyNameBuffer), + KSPIN_TOPO_LINEOUT_DEST, + m_MicTopologyNameBuffer, + SIZEOF_ARRAY(m_MicTopologyNameBuffer), + KSPIN_TOPO_MIC_ELEMENTS + ); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: NotifyEndpointPair (Bth HFP SCO-Bypass): failed, 0x%x", ntStatus)), + Done); + + // + // Pend status notifications. + // + + // NREC disable AudioGateway (AG) status. + ntStatus = EnableBthHfpNrecDisableStatusNotification(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: EnableBthHfpNrecDisableStatusNotification: failed, 0x%x", ntStatus)), + Done); + + // Volume speaker status. + ntStatus = EnableBthHfpSpeakerVolumeStatusNotification(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: EnableBthHfpSpeakerVolumeStatusNotification: failed, 0x%x", ntStatus)), + Done); + + // Volume mic status. + ntStatus = EnableBthHfpMicVolumeStatusNotification(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: EnableBthHfpMicVolumeStatusNotification: failed, 0x%x", ntStatus)), + Done); + + // Connection status. + ntStatus = EnableBthHfpConnectionStatusNotification(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: EnableBthHfpConnectionStatusNotification: failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + InterlockedExchange((PLONG)&m_State, eBthHfpStateFailed); + } +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::CreateCustomEndpointMinipair +( + _In_ PENDPOINT_MINIPAIR pBaseMinipair, + _In_ PUNICODE_STRING FriendlyName, + _In_ PGUID pCategory, + _Outptr_ PENDPOINT_MINIPAIR *ppCustomMinipair +) +{ + NTSTATUS ntStatus; + PENDPOINT_MINIPAIR pNewMinipair = NULL; + SYSVAD_DEVPROPERTY* pProperties = NULL; + PPCFILTER_DESCRIPTOR pNewTopoFilterDesc = NULL; + PPCPIN_DESCRIPTOR pNewTopoPins = NULL; + ULONG cProperties; + ULONG cTopoPins; + + PAGED_CODE(); + + // + // This routine will add one more property to whatever the base minipair describes for the topo filter interface properties + // It will also allocate and set up custom filter and pin descriptors to allow changing the KSNODETYPE for the hfp device + // + 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); + + if ((pProperties != NULL) && (pNewMinipair != NULL) && (pNewTopoFilterDesc != NULL) && (pNewTopoPins != NULL)) + { + SYSVAD_DEVPROPERTY *pLastProperty; + + // Copy base minipair properties to new property list + if (pBaseMinipair->TopoInterfacePropertyCount > 0) + { + RtlCopyMemory(pProperties, pBaseMinipair->TopoInterfaceProperties, (cProperties - 1) * sizeof(SYSVAD_DEVPROPERTY)); + } + + // Add friendly name property to the list + NT_ASSERT(FriendlyName->Length + sizeof(UNICODE_NULL) <= FriendlyName->MaximumLength); // Assuming NULL terminated string + pLastProperty = &pProperties[cProperties - 1]; + pLastProperty->PropertyKey = &DEVPKEY_DeviceInterface_FriendlyName; + pLastProperty->Type = DEVPROP_TYPE_STRING_INDIRECT; + pLastProperty->BufferSize = FriendlyName->Length + sizeof(UNICODE_NULL); + pLastProperty->Buffer = FriendlyName->Buffer; + + // 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 = cProperties; + pNewMinipair->TopoInterfaceProperties = pProperties; + + ntStatus = UpdateCustomEndpointCategory(pNewTopoFilterDesc, pNewTopoPins, pCategory); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("UpdateCustomEndpointCategory: failed, 0x%x", ntStatus)); + } + else + { + *ppCustomMinipair = pNewMinipair; + + pProperties = NULL; + pNewMinipair = NULL; + pNewTopoFilterDesc = NULL; + pNewTopoPins = NULL; + + ntStatus = STATUS_SUCCESS; + } + } + else + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + if (pProperties != NULL) + { + ExFreePoolWithTag(pProperties, 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 +BthHfpDevice::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, ("UpdateCustomEndpointCategory: 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, ("UpdateCustomEndpointCategory: 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 +BthHfpDevice::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 +BthHfpDevice::Stop() +/*++ + +Routine Description: + +Asynchronously called to stop the audio device. +After returning from this function, there are no more async notifications +pending (volume, connection, etc.). + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::Stop]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + eBthHfpState state = eBthHfpStateInvalid; + + state = (eBthHfpState)InterlockedExchange((PLONG)&m_State, eBthHfpStateStopping); + ASSERT(state == eBthHfpStateRunning || state == eBthHfpStateFailed); + 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, ("RemoveEndpointFilters (Bth HFP SCO-Bypass Speaker): failed, 0x%x", ntStatus)); + } + } + + // + // Remove the topology and wave capture filters. + // + if (m_UnknownMicTopology || m_UnknownMicWave) + { + ntStatus = m_Adapter->RemoveEndpointFilters( + m_MicMiniports, + m_UnknownMicTopology, + m_UnknownMicWave); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("RemoveEndpointFilters (Bth HFP SCO-Bypass Capture): failed, 0x%x", ntStatus)); + } + } + + // + // Release port/miniport pointers. + // + SAFE_RELEASE(m_UnknownSpeakerTopology); + SAFE_RELEASE(m_UnknownSpeakerWave); + SAFE_RELEASE(m_UnknownMicTopology); + SAFE_RELEASE(m_UnknownMicWave); + + // + // The device is in the stopped state. + // + InterlockedExchange((PLONG)&m_State, eBthHfpStateStopped); + +} + +#pragma code_seg("PAGE") +NTSTATUS BthHfpDevice::CreateFilterNames( + PUNICODE_STRING HfpDeviceSymbolicLinkName +) +/*++ + +Routine Description: + +Creates unique wave and topology filter reference names. + +Since each subdevice representing a HFP sideband interface arrival needs to have a unique +reference string, this function generates a hash on 'HfpDeviceSymbolicLinkName' parameter +and appends to static interface strings. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::CreateAudioInterfaces]")); + + NTSTATUS status = STATUS_SUCCESS; + ULONG hashValue = 0; + + status = RtlHashUnicodeString(HfpDeviceSymbolicLinkName, TRUE, HASH_STRING_ALGORITHM_X65599, &hashValue); + IF_FAILED_ACTION_JUMP( + status, + DPF(D_ERROR, ("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", BTHHFP_SPEAKER_WAVE_NAME, hashValue); + RtlUnicodeStringInit(&m_SpeakerWaveRefString, m_SpeakerWaveNameBuffer); + + // create topology interfaces for speaker + RtlStringCbPrintfW(m_SpeakerTopologyNameBuffer, sizeof(m_SpeakerTopologyNameBuffer), L"%s-%lu", BTHHFP_SPEAKER_TOPO_NAME, hashValue); + RtlUnicodeStringInit(&m_SpeakerTopologyRefString, m_SpeakerTopologyNameBuffer); + + // create wave interfaces for mic + RtlStringCbPrintfW(m_MicWaveNameBuffer, sizeof(m_MicWaveNameBuffer), L"%s-%lu", BTHHFP_MIC_WAVE_NAME, hashValue); + RtlUnicodeStringInit(&m_MicWaveRefString, m_MicWaveNameBuffer); + + // create topology interfaces for mic + RtlStringCbPrintfW(m_MicTopologyNameBuffer, sizeof(m_MicTopologyNameBuffer), L"%s-%lu", BTHHFP_MIC_TOPO_NAME, hashValue); + RtlUnicodeStringInit(&m_MicTopologyRefString, m_MicTopologyNameBuffer); +End: + return status; +} + +#endif // SYSVAD_BTH_BYPASS |
