summaryrefslogtreecommitdiff
path: root/audio/sysvad/BthhfpDevice.cpp
diff options
context:
space:
mode:
authorGirish Pattabiraman <[email protected]>2018-08-13 10:54:19 -0700
committerAdonais Romero González <[email protected]>2018-08-13 10:54:19 -0700
commit2af3e666d01a60568c2875b4ccbd56b122ad714f (patch)
tree29825a7b7cb0f6a9f77f1ab6e5813665d45ca4cc /audio/sysvad/BthhfpDevice.cpp
parent181c180357cdf9a04d5e3bb21a8e86dc2155ca69 (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.cpp3319
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(&params);
+ WdfRequestGetCompletionParams(req, &params);
+
+ 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