diff options
Diffstat (limited to 'audio/sysvad/common.cpp')
| -rw-r--r-- | audio/sysvad/common.cpp | 6376 |
1 files changed, 6376 insertions, 0 deletions
diff --git a/audio/sysvad/common.cpp b/audio/sysvad/common.cpp new file mode 100644 index 00000000..dae94f3d --- /dev/null +++ b/audio/sysvad/common.cpp @@ -0,0 +1,6376 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + common.cpp + +Abstract: + + Implementation of the AdapterCommon 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" + +// # of sec before sync request is cancelled. +#define BTH_HFP_SYNC_REQ_TIMEOUT_IN_SEC 60 +#define BTH_HFP_NOTIFICATION_MAX_ERROR_COUNT 5 + +#endif // SYSVAD_BTH_BYPASS + +//----------------------------------------------------------------------------- +// CSaveData statics +//----------------------------------------------------------------------------- + +PSAVEWORKER_PARAM CSaveData::m_pWorkItems = NULL; +PDEVICE_OBJECT CSaveData::m_pDeviceObject = NULL; + +//============================================================================= +// Classes +//============================================================================= +#ifdef SYSVAD_BTH_BYPASS +class BthHfpDevice; // Forward declaration. +#endif // SYSVAD_BTH_BYPASS + +/////////////////////////////////////////////////////////////////////////////// +// CAdapterCommon +// +class CAdapterCommon : + public IAdapterCommon, + public IAdapterPowerManagement, + public CUnknown +{ + private: + PSERVICEGROUP m_pServiceGroupWave; + PDEVICE_OBJECT m_pDeviceObject; + PDEVICE_OBJECT m_pPhysicalDeviceObject; + WDFDEVICE m_WdfDevice; // Wdf device. + DEVICE_POWER_STATE m_PowerState; + + PCSYSVADHW m_pHW; // Virtual SYSVAD HW object + PPORTCLSETWHELPER m_pPortClsEtwHelper; + + static LONG m_AdapterInstances; // # of adapter objects. + + DWORD m_dwIdleRequests; + + public: + //===================================================================== + // Default CUnknown + DECLARE_STD_UNKNOWN(); + DEFINE_STD_CONSTRUCTOR(CAdapterCommon); + ~CAdapterCommon(); + + //===================================================================== + // Default IAdapterPowerManagement + IMP_IAdapterPowerManagement; + + //===================================================================== + // IAdapterCommon methods + + STDMETHODIMP_(NTSTATUS) Init + ( + _In_ PDEVICE_OBJECT DeviceObject + ); + + STDMETHODIMP_(PDEVICE_OBJECT) GetDeviceObject(void); + + STDMETHODIMP_(PDEVICE_OBJECT) GetPhysicalDeviceObject(void); + + STDMETHODIMP_(WDFDEVICE) GetWdfDevice(void); + + STDMETHODIMP_(void) SetWaveServiceGroup + ( + _In_ PSERVICEGROUP ServiceGroup + ); + + STDMETHODIMP_(BOOL) bDevSpecificRead(); + + STDMETHODIMP_(void) bDevSpecificWrite + ( + _In_ BOOL bDevSpecific + ); + STDMETHODIMP_(INT) iDevSpecificRead(); + + STDMETHODIMP_(void) iDevSpecificWrite + ( + _In_ INT iDevSpecific + ); + STDMETHODIMP_(UINT) uiDevSpecificRead(); + + STDMETHODIMP_(void) uiDevSpecificWrite + ( + _In_ UINT uiDevSpecific + ); + + STDMETHODIMP_(BOOL) MixerMuteRead + ( + _In_ ULONG Index, + _In_ ULONG Channel + ); + + STDMETHODIMP_(void) MixerMuteWrite + ( + _In_ ULONG Index, + _In_ ULONG Channel, + _In_ BOOL Value + ); + + STDMETHODIMP_(ULONG) MixerMuxRead(void); + + STDMETHODIMP_(void) MixerMuxWrite + ( + _In_ ULONG Index + ); + + STDMETHODIMP_(void) MixerReset(void); + + STDMETHODIMP_(LONG) MixerVolumeRead + ( + _In_ ULONG Index, + _In_ ULONG Channel + ); + + STDMETHODIMP_(void) MixerVolumeWrite + ( + _In_ ULONG Index, + _In_ ULONG Channel, + _In_ LONG Value + ); + + STDMETHODIMP_(LONG) MixerPeakMeterRead + ( + _In_ ULONG Index, + _In_ ULONG Channel + ); + + STDMETHODIMP_(NTSTATUS) WriteEtwEvent + ( + _In_ EPcMiniportEngineEvent miniportEventType, + _In_ ULONGLONG ullData1, + _In_ ULONGLONG ullData2, + _In_ ULONGLONG ullData3, + _In_ ULONGLONG ullData4 + ); + + STDMETHODIMP_(VOID) SetEtwHelper + ( + PPORTCLSETWHELPER _pPortClsEtwHelper + ); + + STDMETHODIMP_(NTSTATUS) InstallSubdevice + ( + _In_opt_ PIRP Irp, + _In_ PWSTR Name, + _In_ REFGUID PortClassId, + _In_ REFGUID MiniportClassId, + _In_opt_ PFNCREATEMINIPORT MiniportCreate, + _In_ ULONG cPropertyCount, + _In_reads_opt_(cPropertyCount) const SYSVAD_DEVPROPERTY * pProperties, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PRESOURCELIST ResourceList, + _In_ REFGUID PortInterfaceId, + _Out_opt_ PUNKNOWN * OutPortInterface, + _Out_opt_ PUNKNOWN * OutPortUnknown, + _Out_opt_ PUNKNOWN * OutMiniportUnknown + ); + + STDMETHODIMP_(NTSTATUS) UnregisterSubdevice + ( + _In_opt_ PUNKNOWN UnknownPort + ); + + STDMETHODIMP_(NTSTATUS) ConnectTopologies + ( + _In_ PUNKNOWN UnknownTopology, + _In_ PUNKNOWN UnknownWave, + _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections, + _In_ ULONG PhysicalConnectionCount + ); + + STDMETHODIMP_(NTSTATUS) DisconnectTopologies + ( + _In_ PUNKNOWN UnknownTopology, + _In_ PUNKNOWN UnknownWave, + _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections, + _In_ ULONG PhysicalConnectionCount + ); + + STDMETHODIMP_(NTSTATUS) InstallEndpointFilters + ( + _In_opt_ PIRP Irp, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PVOID DeviceContext, + _Out_opt_ PUNKNOWN * UnknownTopology, + _Out_opt_ PUNKNOWN * UnknownWave + ); + + STDMETHODIMP_(NTSTATUS) RemoveEndpointFilters + ( + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PUNKNOWN UnknownTopology, + _In_opt_ PUNKNOWN UnknownWave + ); + + STDMETHODIMP_(NTSTATUS) GetFilters + ( + _In_ PENDPOINT_MINIPAIR MiniportPair, + _Out_opt_ PUNKNOWN *UnknownTopologyPort, + _Out_opt_ PUNKNOWN *UnknownTopologyMiniport, + _Out_opt_ PUNKNOWN *UnknownWavePort, + _Out_opt_ PUNKNOWN *UnknownWaveMiniport + ); + + STDMETHODIMP_(NTSTATUS) SetIdlePowerManagement + ( + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_ BOOL bEnabled + ); + +#ifdef SYSVAD_BTH_BYPASS + STDMETHODIMP_(NTSTATUS) InitBthScoBypass(); + + STDMETHODIMP_(VOID) CleanupBthScoBypass(); +#endif // SYSVAD_BTH_BYPASS + + STDMETHODIMP_(VOID) Cleanup(); + + //===================================================================== + // friends + friend NTSTATUS NewAdapterCommon + ( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType + ); + +#ifdef SYSVAD_BTH_BYPASS + //===================================================================== + // Bluetooth Hands-free Profile SCO Bypass support. + + private: + PVOID m_BthHfpScoNotificationHandle; + FAST_MUTEX m_BthHfpFastMutex; // To serialize access. + WDFWORKITEM m_BthHfpWorkItem; // Async work-item. + LIST_ENTRY m_BthHfpWorkTasks; // Work-item's tasks. + LIST_ENTRY m_BthHfpDevices; // Bth HFP devices. + NPAGED_LOOKASIDE_LIST m_BhtHfpWorkTaskPool; // LookasideList + size_t m_BhtHfpWorkTaskPoolElementSize; + BOOL m_BthHfpEnableCleanup; // Do cleanup if true. + + private: + static + DRIVER_NOTIFICATION_CALLBACK_ROUTINE EvtBthHfpScoBypassInterfaceChange; + + static + EVT_WDF_WORKITEM EvtBthHfpScoBypassInterfaceWorkItem; + + protected: + BthHfpDevice * BthHfpDeviceFind + ( + _In_ PUNICODE_STRING SymbolicLinkName + ); + + NTSTATUS BthHfpScoInterfaceArrival + ( + _In_ PUNICODE_STRING SymbolicLinkName + ); + + NTSTATUS BthHfpScoInterfaceRemoval + ( + _In_ PUNICODE_STRING SymbolicLinkName + ); +#endif // SYSVAD_BTH_BYPASS + + private: + + LIST_ENTRY m_SubdeviceCache; + + NTSTATUS GetCachedSubdevice + ( + _In_ PWSTR Name, + _Out_opt_ PUNKNOWN *OutUnknownPort, + _Out_opt_ PUNKNOWN *OutUnknownMiniport + ); + + NTSTATUS CacheSubdevice + ( + _In_ PWSTR Name, + _In_ PUNKNOWN UnknownPort, + _In_ PUNKNOWN UnknownMiniport + ); + + NTSTATUS RemoveCachedSubdevice + ( + _In_ PWSTR Name + ); + + VOID EmptySubdeviceCache(); + + NTSTATUS CreateAudioInterfaceWithProperties + ( + _In_ PCWSTR ReferenceString, + _In_ ULONG cPropertyCount, + _In_reads_(cPropertyCount) const SYSVAD_DEVPROPERTY *pProperties, + _Out_ _At_(AudioSymbolicLinkName->Buffer, __drv_allocatesMem(Mem)) PUNICODE_STRING AudioSymbolicLinkName + ); +}; + +typedef struct _MINIPAIR_UNKNOWN +{ + LIST_ENTRY ListEntry; + WCHAR Name[MAX_PATH]; + PUNKNOWN PortInterface; + PUNKNOWN MiniportInterface; + PADAPTERPOWERMANAGEMENT PowerInterface; +} MINIPAIR_UNKNOWN; + +// +// Used to implement the singleton pattern. +// +LONG CAdapterCommon::m_AdapterInstances = 0; + + +#ifdef SYSVAD_BTH_BYPASS + +//===================================================================== +// +// CAdapterCommon: Bluetooth Hands-Free Profile SCO Bypass definitions. +// + +struct BthHfpWorkItemContext +{ + CAdapterCommon * Adapter; +}; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME +( + BthHfpWorkItemContext, + GetBthHfpWorkItemContext +) + +// start/stop the device +enum eBthHfpTaskAction +{ + eBthHfpTaskStart = 1, + eBthHfpTaskStop = 2, +}; + +struct BthHfpWorkTask +{ + LIST_ENTRY ListEntry; + BthHfpDevice * Device; + eBthHfpTaskAction Action; +}; + + +//===================================================================== +// +// Device: Bluetooth Hands-Free Profile SCO Bypass definitions. +// + +// BTH HFP device's notification work-item context. +struct BthHfpDeviceNotificationWorkItemContext +{ + BthHfpDevice * BthHfpDevice; +}; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME +( + BthHfpDeviceNotificationWorkItemContext, + GetBthHfpDeviceNotificationWorkItemContext +) + +// BTH HFP device's notification request context. +union BthHfpDeviceNotificationBuffer +{ + BOOL bImmediate; + LONG Volume; + BOOL BoolStatus; + NTSTATUS NtStatus; +}; + +struct BthHfpDeviceNotificationReqContext +{ + BthHfpDevice * BthHfpDevice; + LONG Errors; + BthHfpDeviceNotificationBuffer Buffer; + WDFMEMORY MemIn; + WDFMEMORY MemOut; +}; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME +( + BthHfpDeviceNotificationReqContext, + GetBthHfpDeviceNotificationReqContext +) + +enum eBthHfpState +{ + eBthHfpStateInvalid = 0, + eBthHfpStateInitializing = 1, + eBthHfpStateRunning = 2, + eBthHfpStateStopping = 3, + eBthHfpStateStopped = 4, + eBthHfpStateFailed = 5, +}; + +// To support event notification. +struct BthHfpEventCallback +{ + PFNEVENTNOTIFICATION Handler; + PVOID Context; +}; + +// +// This class represents a the Bluetooth Hands-Free Profile SCO Bypass device. +// There is one class for each interface (id = symbolic-link-name). +// +class BthHfpDevice : + IBthHfpDeviceCommon, + public CUnknown +{ + private: + eBthHfpState m_State; + + CAdapterCommon * m_Adapter; + WDFIOTARGET m_WdfIoTarget; + + LIST_ENTRY m_ListEntry; + UNICODE_STRING m_SymbolicLinkName; + + // + // The Topo Filter Desc and Topo Pins structures referenced from + // the Miniports structure are deep copies: they start as copies of the + // static structures and are modified to allow per-SCO-Device pin + // categories based on the info obtained from + // IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2 + // + PENDPOINT_MINIPAIR m_SpeakerMiniports; + PENDPOINT_MINIPAIR m_MicMiniports; + + PUNKNOWN m_UnknownSpeakerTopology; + PUNKNOWN m_UnknownSpeakerWave; + PUNKNOWN m_UnknownMicTopology; + PUNKNOWN m_UnknownMicWave; + + PBTHHFP_DESCRIPTOR2 m_Descriptor; + PKSPROPERTY_VALUES m_VolumePropValues; + LONG m_SpeakerVolumeLevel; + LONG m_MicVolumeLevel; + union{ + BOOL m_ConnectionStatus; + LONG m_ConnectionStatusLong; + }; // unnamed. + + union{ + NTSTATUS m_StreamStatus; + LONG m_StreamStatusLong; + }; // unnamed. + + KEVENT m_StreamStatusEvent; + + // + // Set to TRUE when the HF (remote device) wants to disable the + // NR + EC of the AG (local system). + // + LONG m_NRECDisableStatusLong; + + WDFREQUEST m_StreamReq; + WDFREQUEST m_SpeakerVolumeReq; + WDFREQUEST m_MicVolumeReq; + WDFREQUEST m_ConnectionReq; + WDFREQUEST m_NRECDisableStatusReq; + WDFWORKITEM m_WorkItem; + WDFCOLLECTION m_ReqCollection; + KSPIN_LOCK m_Lock; + + LONG m_nStreams; // # of open streams. + + BthHfpEventCallback m_SpeakerVolumeCallback; + BthHfpEventCallback m_SpeakerConnectionStatusCallback; + BthHfpEventCallback m_MicVolumeCallback; + BthHfpEventCallback m_MicConnectionStatusCallback; + + public: + //===================================================================== + // Default CUnknown + DECLARE_STD_UNKNOWN(); + DEFINE_STD_CONSTRUCTOR(BthHfpDevice); + ~BthHfpDevice(); + + NTSTATUS Init + ( + _In_ CAdapterCommon * Adapter, + _In_ PUNICODE_STRING SymbolicLinkName + ); + + public: + //===================================================================== + // + // Public functions used by CAdapterCommon object. + // + VOID Start(); + VOID Stop(); + + PLIST_ENTRY GetListEntry() + { + return &m_ListEntry; + } + + PUNICODE_STRING GetSymbolicLinkName() + { + return &m_SymbolicLinkName; + } + + static + BthHfpDevice * + GetBthHfpDevice + ( + _In_ PLIST_ENTRY le + ) + { + return CONTAINING_RECORD(le, BthHfpDevice, m_ListEntry); + } + + public: + //===================================================================== + // + // IBthHfpDeviceCommon functions. + // + STDMETHODIMP_(BOOL) IsVolumeSupported(); + + STDMETHODIMP_(PKSPROPERTY_VALUES) GetVolumeSettings + ( + _Out_ PULONG Size + ); + + STDMETHODIMP_(LONG) GetSpeakerVolume(); + + STDMETHODIMP_(NTSTATUS) SetSpeakerVolume + ( + _In_ ULONG Volume + ); + + STDMETHODIMP_(LONG) GetMicVolume(); + + STDMETHODIMP_(NTSTATUS) SetMicVolume + ( + _In_ ULONG Volume + ); + + STDMETHODIMP_(BOOL) GetConnectionStatus(); + + STDMETHODIMP_(NTSTATUS) Connect(); + + STDMETHODIMP_(NTSTATUS) Disconnect(); + + STDMETHODIMP_(BOOL) GetStreamStatus(); + + STDMETHODIMP_(NTSTATUS) StreamOpen(); + + STDMETHODIMP_(NTSTATUS) StreamClose(); + + STDMETHODIMP_(GUID) GetContainerId(); + + STDMETHODIMP_(VOID) SetSpeakerVolumeHandler + ( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ); + + STDMETHODIMP_(VOID) SetSpeakerConnectionStatusHandler + ( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ); + + STDMETHODIMP_(VOID) SetMicVolumeHandler + ( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ); + + STDMETHODIMP_(VOID) SetMicConnectionStatusHandler + ( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ); + + STDMETHODIMP_(BOOL) IsNRECSupported(); + + STDMETHODIMP_(BOOL) GetNRECDisableStatus(); + + private: + //===================================================================== + // + // Helper functions. + // + NTSTATUS SendIoCtrlSynchronously + ( + _In_opt_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_ ULONG InLength, + _In_ ULONG OutLength, + _When_(InLength > 0 || OutLength > 0, _In_) + _When_(InLength == 0 && OutLength == 0, _In_opt_) + PVOID Buffer + ); + + NTSTATUS SendIoCtrlAsynchronously + ( + _In_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_opt_ WDFMEMORY MemIn, + _In_opt_ WDFMEMORY MemOut, + _In_ PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine, + _In_ WDFCONTEXT Context + ); + + NTSTATUS GetBthHfpDescriptor + ( + _Out_ PBTHHFP_DESCRIPTOR2 * Descriptor + ); + + NTSTATUS EnableBthHfpNrecDisableStatusNotification(); + + NTSTATUS GetBthHfpVolumePropertyValues + ( + _In_ ULONG Length, + _Out_ PKSPROPERTY_VALUES * PropValues + ); + + NTSTATUS SetBthHfpSpeakerVolume + ( + _In_ LONG Volume + ); + + NTSTATUS GetBthHfpSpeakerVolume + ( + _Out_ LONG * Volume + ); + + NTSTATUS EnableBthHfpSpeakerVolumeStatusNotification(); + + NTSTATUS SetBthHfpMicVolume + ( + _In_ LONG Volume + ); + + NTSTATUS GetBthHfpMicVolume + ( + _Out_ LONG * Volume + ); + + NTSTATUS EnableBthHfpMicVolumeStatusNotification(); + + NTSTATUS GetBthHfpConnectionStatus + ( + _Out_ BOOL * ConnectionStatus + ); + + NTSTATUS EnableBthHfpConnectionStatusNotification(); + + static + NTSTATUS CreateCustomEndpointMinipair + ( + _In_ PENDPOINT_MINIPAIR pBaseMinipair, + _In_ PUNICODE_STRING FriendlyName, + _In_ PGUID pCategory, + _Outptr_ PENDPOINT_MINIPAIR *ppCustomMinipair + ); + + static + NTSTATUS UpdateCustomEndpointCategory + ( + _In_ PPCFILTER_DESCRIPTOR pCustomMinipairTopoFilter, + _In_ PPCPIN_DESCRIPTOR pCustomMinipairTopoPins, + _In_ PGUID pCategory + ); + + static + VOID DeleteCustomEndpointMinipair + ( + _In_ PENDPOINT_MINIPAIR CustomMinipair + ); + + NTSTATUS GetBthHfpCodecId + ( + _Out_ UCHAR * CodecId + ); + + NTSTATUS SetBthHfpConnect(); + + NTSTATUS SetBthHfpDisconnect(); + + NTSTATUS SetBthHfpStreamOpen(); + + NTSTATUS SetBthHfpStreamClose(); + + NTSTATUS EnableBthHfpStreamStatusNotification(); + + NTSTATUS StopBthHfpStreamStatusNotification(); + + // + // WDF I/O Target callback. + // + static + EVT_WDF_IO_TARGET_QUERY_REMOVE EvtBthHfpTargetQueryRemove; + + static + EVT_WDF_IO_TARGET_REMOVE_CANCELED EvtBthHfpTargetRemoveCanceled; + + static + EVT_WDF_IO_TARGET_REMOVE_COMPLETE EvtBthHfpTargetRemoveComplete; + + // + // Status notifications callbacks. + // + static + EVT_WDF_REQUEST_COMPLETION_ROUTINE EvtBthHfpDeviceStreamStatusCompletion; + + static + EVT_WDF_REQUEST_COMPLETION_ROUTINE EvtBthHfpDeviceNotificationStatusCompletion; + + static + EVT_WDF_WORKITEM EvtBthHfpDeviceNotificationStatusWorkItem; +}; +#endif // SYSVAD_BTH_BYPASS + +//----------------------------------------------------------------------------- +// Functions +//----------------------------------------------------------------------------- + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS SysvadIoSetDeviceInterfacePropertyDataMultiple +( + _In_ PUNICODE_STRING SymbolicLinkName, + _In_ ULONG cPropertyCount, + _In_reads_(cPropertyCount) const SYSVAD_DEVPROPERTY *pProperties +) +{ + NTSTATUS ntStatus; + + PAGED_CODE(); + + for (ULONG i = 0; i < cPropertyCount; i++) + { + ntStatus = IoSetDeviceInterfacePropertyData( + SymbolicLinkName, + pProperties[i].PropertyKey, + LOCALE_NEUTRAL, + PLUGPLAY_PROPERTY_PERSISTENT, + pProperties[i].Type, + pProperties[i].BufferSize, + pProperties[i].Buffer); + + if (!NT_SUCCESS(ntStatus)) + { + return ntStatus; + } + } + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +NewAdapterCommon +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType +) +/*++ + +Routine Description: + + Creates a new CAdapterCommon + +Arguments: + + Unknown - + + UnknownOuter - + + PoolType + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Unknown); + + NTSTATUS ntStatus; + + // + // This sample supports only one instance of this object. + // (b/c of CSaveData's static members and Bluetooth HFP logic). + // + if (CAdapterCommon::m_AdapterInstances != 0) + { + ntStatus = STATUS_DEVICE_BUSY; + DPF(D_ERROR, ("NewAdapterCommon failed, only one instance is allowed")); + goto Done; + } + + CAdapterCommon::m_AdapterInstances++; + + // + // Allocate an adapter object. + // + CAdapterCommon *p = new(PoolType, MINADAPTER_POOLTAG) CAdapterCommon(UnknownOuter); + if (p == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + DPF(D_ERROR, ("NewAdapterCommon failed, 0x%x", ntStatus)); + goto Done; + } + + // + // Success. + // + *Unknown = PUNKNOWN((PADAPTERCOMMON)(p)); + (*Unknown)->AddRef(); + ntStatus = STATUS_SUCCESS; + +Done: + return ntStatus; +} // NewAdapterCommon + +//============================================================================= +#pragma code_seg("PAGE") +CAdapterCommon::~CAdapterCommon +( + void +) +/*++ + +Routine Description: + + Destructor for CAdapterCommon. + +Arguments: + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::~CAdapterCommon]")); + + if (m_pHW) + { + delete m_pHW; + m_pHW = NULL; + } + + CSaveData::DestroyWorkItems(); + + SAFE_RELEASE(m_pPortClsEtwHelper); + SAFE_RELEASE(m_pServiceGroupWave); + + if (m_WdfDevice) + { + WdfObjectDelete(m_WdfDevice); + m_WdfDevice = NULL; + } + + CAdapterCommon::m_AdapterInstances--; + ASSERT(CAdapterCommon::m_AdapterInstances == 0); +} // ~CAdapterCommon + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(PDEVICE_OBJECT) +CAdapterCommon::GetDeviceObject +( + void +) +/*++ + +Routine Description: + + Returns the deviceobject + +Arguments: + +Return Value: + + PDEVICE_OBJECT + +--*/ +{ + PAGED_CODE(); + + return m_pDeviceObject; +} // GetDeviceObject + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(PDEVICE_OBJECT) +CAdapterCommon::GetPhysicalDeviceObject +( + void +) +/*++ + +Routine Description: + + Returns the PDO. + +Arguments: + +Return Value: + + PDEVICE_OBJECT + +--*/ +{ + PAGED_CODE(); + + return m_pPhysicalDeviceObject; +} // GetPhysicalDeviceObject + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(WDFDEVICE) +CAdapterCommon::GetWdfDevice +( + void +) +/*++ + +Routine Description: + + Returns the associated WDF miniport device. Note that this is NOT an audio + miniport. The WDF miniport device is the WDF device associated with the + adapter. + +Arguments: + +Return Value: + + WDFDEVICE + +--*/ +{ + PAGED_CODE(); + + return m_WdfDevice; +} // GetWdfDevice + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::Init +( + _In_ PDEVICE_OBJECT DeviceObject +) +/*++ + +Routine Description: + + Initialize adapter common object. + +Arguments: + + DeviceObject - pointer to the device object + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::Init]")); + + ASSERT(DeviceObject); + + NTSTATUS ntStatus = STATUS_SUCCESS; + +#ifdef SYSVAD_BTH_BYPASS + m_BthHfpEnableCleanup = FALSE; +#endif // SYSVAD_BTH_BYPASS + + m_pServiceGroupWave = NULL; + m_pDeviceObject = DeviceObject; + m_pPhysicalDeviceObject = NULL; + m_WdfDevice = NULL; + m_PowerState = PowerDeviceD0; + m_pHW = NULL; + m_pPortClsEtwHelper = NULL; + + InitializeListHead(&m_SubdeviceCache); + + // + // Get the PDO. + // + ntStatus = PcGetPhysicalDeviceObject(DeviceObject, &m_pPhysicalDeviceObject); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("PcGetPhysicalDeviceObject failed, 0x%x", ntStatus)), + Done); + + // + // Create a WDF miniport to represent the adapter. Note that WDF miniports + // are NOT audio miniports. An audio adapter is associated with a single WDF + // miniport. This driver uses WDF to simplify the handling of the Bluetooth + // SCO HFP Bypass interface. + // + ntStatus = WdfDeviceMiniportCreate( WdfGetDriver(), + WDF_NO_OBJECT_ATTRIBUTES, + DeviceObject, // FDO + NULL, // Next device. + NULL, // PDO + &m_WdfDevice); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("WdfDeviceMiniportCreate failed, 0x%x", ntStatus)), + Done); + + // Initialize HW. + // + m_pHW = new (NonPagedPoolNx, SYSVAD_POOLTAG) CSYSVADHW; + if (!m_pHW) + { + DPF(D_TERSE, ("Insufficient memory for SYSVAD HW")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_JUMP(ntStatus, Done); + + m_pHW->MixerReset(); + + // + // Initialize SaveData class. + // + CSaveData::SetDeviceObject(DeviceObject); //device object is needed by CSaveData + ntStatus = CSaveData::InitializeWorkItems(DeviceObject); + IF_FAILED_JUMP(ntStatus, Done); + +Done: + + return ntStatus; +} // Init + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(void) +CAdapterCommon::MixerReset +( + void +) +/*++ + +Routine Description: + + Reset mixer registers from registry. + +Arguments: + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + if (m_pHW) + { + m_pHW->MixerReset(); + } +} // MixerReset + +//============================================================================= +/* Here are the definitions of the standard miniport events. + +Event type : eMINIPORT_IHV_DEFINED +Parameter 1 : Defined and used by IHVs +Parameter 2 : Defined and used by IHVs +Parameter 3 :Defined and used by IHVs +Parameter 4 :Defined and used by IHVs + +Event type: eMINIPORT_BUFFER_COMPLETE +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: Data length completed +Parameter 4:0 + +Event type: eMINIPORT_PIN_STATE +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: Pin State 0->KS_STOP, 1->KS_ACQUIRE, 2->KS_PAUSE, 3->KS_RUN +Parameter 4:0 + +Event type: eMINIPORT_GET_STREAM_POS +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: 0 +Parameter 4:0 + + +Event type: eMINIPORT_SET_WAVERT_BUFFER_WRITE_POS +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: the arget WaveRtBufferWritePosition received from portcls +Parameter 4:0 + +Event type: eMINIPORT_GET_PRESENTATION_POS +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: Presentation position +Parameter 4:0 + +Event type: eMINIPORT_PROGRAM_DMA +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: Starting WaveRt buffer offset +Parameter 4: Data length + +Event type: eMINIPORT_GLITCH_REPORT +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: major glitch code: 1:WaveRT buffer is underrun, + 2:decoder errors, + 3:receive the same wavert buffer two in a row in event driven mode +Parameter 4: minor code for the glitch cause + +Event type: eMINIPORT_LAST_BUFFER_RENDERED +Parameter 1: Current linear buffer position +Parameter 2: the very last WaveRtBufferWritePosition that the driver received +Parameter 3: 0 +Parameter 4: 0 + +*/ +#pragma code_seg() +STDMETHODIMP +CAdapterCommon::WriteEtwEvent +( + _In_ EPcMiniportEngineEvent miniportEventType, + _In_ ULONGLONG ullData1, + _In_ ULONGLONG ullData2, + _In_ ULONGLONG ullData3, + _In_ ULONGLONG ullData4 +) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (m_pPortClsEtwHelper) + { + ntStatus = m_pPortClsEtwHelper->MiniportWriteEtwEvent( miniportEventType, ullData1, ullData2, ullData3, ullData4) ; + } + return ntStatus; +} // WriteEtwEvent + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(void) +CAdapterCommon::SetEtwHelper +( + PPORTCLSETWHELPER _pPortClsEtwHelper +) +{ + PAGED_CODE(); + + SAFE_RELEASE(m_pPortClsEtwHelper); + + m_pPortClsEtwHelper = _pPortClsEtwHelper; + + if (m_pPortClsEtwHelper) + { + m_pPortClsEtwHelper->AddRef(); + } +} // SetEtwHelper + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP +CAdapterCommon::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface routine for AdapterCommon + +Arguments: + + Interface - + + Object - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(PADAPTERCOMMON(this))); + } + else if (IsEqualGUIDAligned(Interface, IID_IAdapterCommon)) + { + *Object = PVOID(PADAPTERCOMMON(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IAdapterPowerManagement)) + { + *Object = PVOID(PADAPTERPOWERMANAGEMENT(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + PUNKNOWN(*Object)->AddRef(); + return STATUS_SUCCESS; + } + + return STATUS_INVALID_PARAMETER; +} // NonDelegatingQueryInterface + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(void) +CAdapterCommon::SetWaveServiceGroup +( + _In_ PSERVICEGROUP ServiceGroup +) +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::SetWaveServiceGroup]")); + + SAFE_RELEASE(m_pServiceGroupWave); + + m_pServiceGroupWave = ServiceGroup; + + if (m_pServiceGroupWave) + { + m_pServiceGroupWave->AddRef(); + } +} // SetWaveServiceGroup + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(BOOL) +CAdapterCommon::bDevSpecificRead() +/*++ + +Routine Description: + + Fetch Device Specific information. + +Arguments: + + N/A + +Return Value: + + BOOL - Device Specific info + +--*/ +{ + if (m_pHW) + { + return m_pHW->bGetDevSpecific(); + } + + return FALSE; +} // bDevSpecificRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::bDevSpecificWrite +( + _In_ BOOL bDevSpecific +) +/*++ + +Routine Description: + + Store the new value in the Device Specific location. + +Arguments: + + bDevSpecific - Value to store + +Return Value: + + N/A. + +--*/ +{ + if (m_pHW) + { + m_pHW->bSetDevSpecific(bDevSpecific); + } +} // DevSpecificWrite + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(INT) +CAdapterCommon::iDevSpecificRead() +/*++ + +Routine Description: + + Fetch Device Specific information. + +Arguments: + + N/A + +Return Value: + + INT - Device Specific info + +--*/ +{ + if (m_pHW) + { + return m_pHW->iGetDevSpecific(); + } + + return 0; +} // iDevSpecificRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::iDevSpecificWrite +( + _In_ INT iDevSpecific +) +/*++ + +Routine Description: + + Store the new value in the Device Specific location. + +Arguments: + + iDevSpecific - Value to store + +Return Value: + + N/A. + +--*/ +{ + if (m_pHW) + { + m_pHW->iSetDevSpecific(iDevSpecific); + } +} // iDevSpecificWrite + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(UINT) +CAdapterCommon::uiDevSpecificRead() +/*++ + +Routine Description: + + Fetch Device Specific information. + +Arguments: + + N/A + +Return Value: + + UINT - Device Specific info + +--*/ +{ + if (m_pHW) + { + return m_pHW->uiGetDevSpecific(); + } + + return 0; +} // uiDevSpecificRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::uiDevSpecificWrite +( + _In_ UINT uiDevSpecific +) +/*++ + +Routine Description: + + Store the new value in the Device Specific location. + +Arguments: + + uiDevSpecific - Value to store + +Return Value: + + N/A. + +--*/ +{ + if (m_pHW) + { + m_pHW->uiSetDevSpecific(uiDevSpecific); + } +} // uiDevSpecificWrite + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(BOOL) +CAdapterCommon::MixerMuteRead +( + _In_ ULONG Index, + _In_ ULONG Channel +) +/*++ + +Routine Description: + + Store the new value in mixer register array. + +Arguments: + + Index - node id + +Return Value: + + BOOL - mixer mute setting for this node + +--*/ +{ + if (m_pHW) + { + return m_pHW->GetMixerMute(Index, Channel); + } + + return 0; +} // MixerMuteRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::MixerMuteWrite +( + _In_ ULONG Index, + _In_ ULONG Channel, + _In_ BOOL Value +) +/*++ + +Routine Description: + + Store the new value in mixer register array. + +Arguments: + + Index - node id + + Value - new mute settings + +Return Value: + + NT status code. + +--*/ +{ + if (m_pHW) + { + m_pHW->SetMixerMute(Index, Channel, Value); + } +} // MixerMuteWrite + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(ULONG) +CAdapterCommon::MixerMuxRead() +/*++ + +Routine Description: + + Return the mux selection + +Arguments: + + Index - node id + + Value - new mute settings + +Return Value: + + NT status code. + +--*/ +{ + if (m_pHW) + { + return m_pHW->GetMixerMux(); + } + + return 0; +} // MixerMuxRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::MixerMuxWrite +( + _In_ ULONG Index +) +/*++ + +Routine Description: + + Store the new mux selection + +Arguments: + + Index - node id + + Value - new mute settings + +Return Value: + + NT status code. + +--*/ +{ + if (m_pHW) + { + m_pHW->SetMixerMux(Index); + } +} // MixerMuxWrite + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(LONG) +CAdapterCommon::MixerVolumeRead +( + _In_ ULONG Index, + _In_ ULONG Channel +) +/*++ + +Routine Description: + + Return the value in mixer register array. + +Arguments: + + Index - node id + + Channel = which channel + +Return Value: + + Byte - mixer volume settings for this line + +--*/ +{ + if (m_pHW) + { + return m_pHW->GetMixerVolume(Index, Channel); + } + + return 0; +} // MixerVolumeRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::MixerVolumeWrite +( + _In_ ULONG Index, + _In_ ULONG Channel, + _In_ LONG Value +) +/*++ + +Routine Description: + + Store the new value in mixer register array. + +Arguments: + + Index - node id + + Channel - which channel + + Value - new volume level + +Return Value: + + void + +--*/ +{ + if (m_pHW) + { + m_pHW->SetMixerVolume(Index, Channel, Value); + } +} // MixerVolumeWrite + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(LONG) +CAdapterCommon::MixerPeakMeterRead +( + _In_ ULONG Index, + _In_ ULONG Channel +) +/*++ + +Routine Description: + + Return the value in mixer register array. + +Arguments: + + Index - node id + + Channel = which channel + +Return Value: + + Byte - mixer sample peak meter settings for this line + +--*/ +{ + if (m_pHW) + { + return m_pHW->GetMixerPeakMeter(Index, Channel); + } + + return 0; +} // MixerVolumeRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::PowerChangeState +( + _In_ POWER_STATE NewState +) +/*++ + +Routine Description: + + +Arguments: + + NewState - The requested, new power state for the device. + +Return Value: + + void + +Note: + From MSDN: + + To assist the driver, PortCls will pause any active audio streams prior to calling + this method to place the device in a sleep state. After calling this method, PortCls + will unpause active audio streams, to wake the device up. Miniports can opt for + additional notification by utilizing the IPowerNotify interface. + + The miniport driver must perform the requested change to the device's power state + before it returns from the PowerChangeState call. If the miniport driver needs to + save or restore any device state before a power-state change, the miniport driver + should support the IPowerNotify interface, which allows it to receive advance warning + of any such change. Before returning from a successful PowerChangeState call, the + miniport driver should cache the new power state. + + While the miniport driver is in one of the sleep states (any state other than + PowerDeviceD0), it must avoid writing to the hardware. The miniport driver must cache + any hardware accesses that need to be deferred until the device powers up again. If + the power state is changing from one of the sleep states to PowerDeviceD0, the + miniport driver should perform any deferred hardware accesses after it has powered up + the device. If the power state is changing from PowerDeviceD0 to a sleep state, the + miniport driver can perform any necessary hardware accesses during the PowerChangeState + call before it powers down the device. + + While powered down, a miniport driver is never asked to create a miniport driver object + or stream object. PortCls always places the device in the PowerDeviceD0 state before + calling the miniport driver's NewStream method. + +--*/ +{ + DPF_ENTER(("[CAdapterCommon::PowerChangeState]")); + + // Notify all registered miniports of a power state change + PLIST_ENTRY le = NULL; + for (le = m_SubdeviceCache.Flink; le != &m_SubdeviceCache; le = le->Flink) + { + MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry); + + if (pRecord->PowerInterface) + { + pRecord->PowerInterface->PowerChangeState(NewState); + } + } + + // is this actually a state change?? + // + if (NewState.DeviceState != m_PowerState) + { + // switch on new state + // + switch (NewState.DeviceState) + { + case PowerDeviceD0: + case PowerDeviceD1: + case PowerDeviceD2: + case PowerDeviceD3: + m_PowerState = NewState.DeviceState; + + DPF + ( + D_VERBOSE, + ("Entering D%u", ULONG(m_PowerState) - ULONG(PowerDeviceD0)) + ); + + break; + + default: + + DPF(D_VERBOSE, ("Unknown Device Power State")); + break; + } + } +} // PowerStateChange + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::QueryDeviceCapabilities +( + _Inout_updates_bytes_(sizeof(DEVICE_CAPABILITIES)) PDEVICE_CAPABILITIES PowerDeviceCaps +) +/*++ + +Routine Description: + + Called at startup to get the caps for the device. This structure provides + the system with the mappings between system power state and device power + state. This typically will not need modification by the driver. + +Arguments: + + PowerDeviceCaps - The device's capabilities. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(PowerDeviceCaps); + + DPF_ENTER(("[CAdapterCommon::QueryDeviceCapabilities]")); + + return (STATUS_SUCCESS); +} // QueryDeviceCapabilities + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::QueryPowerChangeState +( + _In_ POWER_STATE NewStateQuery +) +/*++ + +Routine Description: + + Query to see if the device can change to this power state + +Arguments: + + NewStateQuery - The requested, new power state for the device + +Return Value: + + NT status code. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + + DPF_ENTER(("[CAdapterCommon::QueryPowerChangeState]")); + + // query each miniport for it's power state, we're finished if even one indicates + // it cannot go to this power state. + PLIST_ENTRY le = NULL; + for (le = m_SubdeviceCache.Flink; le != &m_SubdeviceCache && NT_SUCCESS(status); le = le->Flink) + { + MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry); + + if (pRecord->PowerInterface) + { + status = pRecord->PowerInterface->QueryPowerChangeState(NewStateQuery); + } + } + + return status; +} // QueryPowerChangeState + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::CreateAudioInterfaceWithProperties +( + _In_ PCWSTR ReferenceString, + _In_ ULONG cPropertyCount, + _In_reads_(cPropertyCount) const SYSVAD_DEVPROPERTY *pProperties, + _Out_ _At_(AudioSymbolicLinkName->Buffer, __drv_allocatesMem(Mem)) PUNICODE_STRING AudioSymbolicLinkName +) +/*++ + +Routine Description: + +Create the audio interface (in disabled mode). + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::CreateAudioInterfaceWithProperties]")); + + NTSTATUS ntStatus; + UNICODE_STRING referenceString; + + RtlInitUnicodeString(&referenceString, ReferenceString); + + // + // Reset output value. + // + RtlZeroMemory(AudioSymbolicLinkName, sizeof(UNICODE_STRING)); + + // + // Register an audio interface if not already present. + // + ntStatus = IoRegisterDeviceInterface( + GetPhysicalDeviceObject(), + &KSCATEGORY_AUDIO, + &referenceString, + AudioSymbolicLinkName); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("CreateAudioInterfaceWithProperties: IoRegisterDeviceInterface(KSCATEGORY_AUDIO): failed, 0x%x", ntStatus)), + Done); + + // + // Set properties on the interface + // + ntStatus = SysvadIoSetDeviceInterfacePropertyDataMultiple(AudioSymbolicLinkName, cPropertyCount, pProperties); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("CreateAudioInterfaceWithProperties: SysvadIoSetDeviceInterfacePropertyDataMultiple(...): failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + RtlFreeUnicodeString(AudioSymbolicLinkName); + RtlZeroMemory(AudioSymbolicLinkName, sizeof(UNICODE_STRING)); + } + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::InstallSubdevice +( + _In_opt_ PIRP Irp, + _In_ PWSTR Name, + _In_ REFGUID PortClassId, + _In_ REFGUID MiniportClassId, + _In_opt_ PFNCREATEMINIPORT MiniportCreate, + _In_ ULONG cPropertyCount, + _In_reads_opt_(cPropertyCount) const SYSVAD_DEVPROPERTY * pProperties, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PRESOURCELIST ResourceList, + _In_ REFGUID PortInterfaceId, + _Out_opt_ PUNKNOWN * OutPortInterface, + _Out_opt_ PUNKNOWN * OutPortUnknown, + _Out_opt_ PUNKNOWN * OutMiniportUnknown +) +{ +/*++ + +Routine Description: + + This function creates and registers a subdevice consisting of a port + driver, a minport driver and a set of resources bound together. It will + also optionally place a pointer to an interface on the port driver in a + specified location before initializing the port driver. This is done so + that a common ISR can have access to the port driver during + initialization, when the ISR might fire. + +Arguments: + + Irp - pointer to the irp object. + + Name - name of the miniport. Passes to PcRegisterSubDevice + + PortClassId - port class id. Passed to PcNewPort. + + MiniportClassId - miniport class id. Passed to PcNewMiniport. + + MiniportCreate - pointer to a miniport creation function. If NULL, + PcNewMiniport is used. + + DeviceContext - deviceType specific. + + MiniportPair - endpoint configuration info. + + ResourceList - pointer to the resource list. + + PortInterfaceId - GUID that represents the port interface. + + OutPortInterface - pointer to store the port interface + + OutPortUnknown - pointer to store the unknown port interface. + + OutMiniportUnknown - pointer to store the unknown miniport interface + +Return Value: + + NT status code. + +--*/ + PAGED_CODE(); + DPF_ENTER(("[InstallSubDevice %S]", Name)); + + ASSERT(Name != NULL); + ASSERT(m_pDeviceObject != NULL); + + NTSTATUS ntStatus; + PPORT port = NULL; + PUNKNOWN miniport = NULL; + PADAPTERCOMMON adapterCommon = NULL; + UNICODE_STRING symbolicLink = { 0 }; + + adapterCommon = PADAPTERCOMMON(this); + + ntStatus = CreateAudioInterfaceWithProperties(Name, cPropertyCount, pProperties, &symbolicLink); + if (NT_SUCCESS(ntStatus)) + { + // Currently have no use for the symbolic link + RtlFreeUnicodeString(&symbolicLink); + + // Create the port driver object + // + ntStatus = PcNewPort(&port, PortClassId); + } + + // Create the miniport object + // + if (NT_SUCCESS(ntStatus)) + { + if (MiniportCreate) + { + ntStatus = + MiniportCreate + ( + &miniport, + MiniportClassId, + NULL, + NonPagedPoolNx, + adapterCommon, + DeviceContext, + MiniportPair + ); + } + else + { + ntStatus = + PcNewMiniport + ( + (PMINIPORT *) &miniport, + MiniportClassId + ); + } + } + + // Init the port driver and miniport in one go. + // + if (NT_SUCCESS(ntStatus)) + { +#pragma warning(push) + // IPort::Init's annotation on ResourceList requires it to be non-NULL. However, + // for dynamic devices, we may no longer have the resource list and this should + // still succeed. + // +#pragma warning(disable:6387) + ntStatus = + port->Init + ( + m_pDeviceObject, + Irp, + miniport, + adapterCommon, + ResourceList + ); +#pragma warning (pop) + + if (NT_SUCCESS(ntStatus)) + { + // Register the subdevice (port/miniport combination). + // + ntStatus = + PcRegisterSubdevice + ( + m_pDeviceObject, + Name, + port + ); + } + } + + // Deposit the port interfaces if it's needed. + // + if (NT_SUCCESS(ntStatus)) + { + if (OutPortUnknown) + { + ntStatus = + port->QueryInterface + ( + IID_IUnknown, + (PVOID *)OutPortUnknown + ); + } + + if (OutPortInterface) + { + ntStatus = + port->QueryInterface + ( + PortInterfaceId, + (PVOID *) OutPortInterface + ); + } + + if (OutMiniportUnknown) + { + ntStatus = + miniport->QueryInterface + ( + IID_IUnknown, + (PVOID *)OutMiniportUnknown + ); + } + + } + + if (port) + { + port->Release(); + } + + if (miniport) + { + miniport->Release(); + } + + return ntStatus; +} // InstallSubDevice + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::UnregisterSubdevice +( + _In_opt_ PUNKNOWN UnknownPort +) +/*++ + +Routine Description: + + Unregisters and releases the specified subdevice. + +Arguments: + + UnknownPort - Wave or topology port interface. + +Return Value: + + NTSTATUS + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::UnregisterSubdevice]")); + + ASSERT(m_pDeviceObject != NULL); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PUNREGISTERSUBDEVICE unregisterSubdevice = NULL; + + if (NULL == UnknownPort) + { + return ntStatus; + } + + // + // Get the IUnregisterSubdevice interface. + // + ntStatus = UnknownPort->QueryInterface( + IID_IUnregisterSubdevice, + (PVOID *)&unregisterSubdevice); + + // + // Unregister the port object. + // + if (NT_SUCCESS(ntStatus)) + { + ntStatus = unregisterSubdevice->UnregisterSubdevice( + m_pDeviceObject, + UnknownPort); + + // + // Release the IUnregisterSubdevice interface. + // + unregisterSubdevice->Release(); + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::ConnectTopologies +( + _In_ PUNKNOWN UnknownTopology, + _In_ PUNKNOWN UnknownWave, + _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections, + _In_ ULONG PhysicalConnectionCount +) +/*++ + +Routine Description: + + Connects the bridge pins between the wave and mixer topologies. + +Arguments: + +Return Value: + + NTSTATUS + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::ConnectTopologies]")); + + ASSERT(m_pDeviceObject != NULL); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + // + // register wave <=> topology connections + // This will connect bridge pins of wave and topology + // miniports. + // + for (ULONG i = 0; i < PhysicalConnectionCount && NT_SUCCESS(ntStatus); i++) + { + + switch(PhysicalConnections[i].eType) + { + case CONNECTIONTYPE_TOPOLOGY_OUTPUT: + ntStatus = + PcRegisterPhysicalConnection + ( + m_pDeviceObject, + UnknownTopology, + PhysicalConnections[i].ulTopology, + UnknownWave, + PhysicalConnections[i].ulWave + ); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("ConnectTopologies: PcRegisterPhysicalConnection(render) failed, 0x%x", ntStatus)); + } + break; + case CONNECTIONTYPE_WAVE_OUTPUT: + ntStatus = + PcRegisterPhysicalConnection + ( + m_pDeviceObject, + UnknownWave, + PhysicalConnections[i].ulWave, + UnknownTopology, + PhysicalConnections[i].ulTopology + ); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("ConnectTopologies: PcRegisterPhysicalConnection(capture) failed, 0x%x", ntStatus)); + } + break; + } + } + + // + // Cleanup in case of error. + // + if (!NT_SUCCESS(ntStatus)) + { + // disconnect all connections on error, ignore error code because not all + // connections may have been made + DisconnectTopologies(UnknownTopology, UnknownWave, PhysicalConnections, PhysicalConnectionCount); + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::DisconnectTopologies +( + _In_ PUNKNOWN UnknownTopology, + _In_ PUNKNOWN UnknownWave, + _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections, + _In_ ULONG PhysicalConnectionCount +) +/*++ + +Routine Description: + + Disconnects the bridge pins between the wave and mixer topologies. + +Arguments: + +Return Value: + + NTSTATUS + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::DisconnectTopologies]")); + + ASSERT(m_pDeviceObject != NULL); + + NTSTATUS ntStatus = STATUS_SUCCESS; + NTSTATUS ntStatus2 = STATUS_SUCCESS; + PUNREGISTERPHYSICALCONNECTION unregisterPhysicalConnection = NULL; + + // + // Get the IUnregisterPhysicalConnection interface + // + ntStatus = UnknownTopology->QueryInterface( + IID_IUnregisterPhysicalConnection, + (PVOID *)&unregisterPhysicalConnection); + + if (NT_SUCCESS(ntStatus)) + { + for (ULONG i = 0; i < PhysicalConnectionCount; i++) + { + switch(PhysicalConnections[i].eType) + { + case CONNECTIONTYPE_TOPOLOGY_OUTPUT: + ntStatus = + unregisterPhysicalConnection->UnregisterPhysicalConnection( + m_pDeviceObject, + UnknownTopology, + PhysicalConnections[i].ulTopology, + UnknownWave, + PhysicalConnections[i].ulWave + ); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("DisconnectTopologies: UnregisterPhysicalConnection(render) failed, 0x%x", ntStatus)); + } + break; + case CONNECTIONTYPE_WAVE_OUTPUT: + ntStatus = + unregisterPhysicalConnection->UnregisterPhysicalConnection( + m_pDeviceObject, + UnknownWave, + PhysicalConnections[i].ulWave, + UnknownTopology, + PhysicalConnections[i].ulTopology + ); + if (!NT_SUCCESS(ntStatus2)) + { + DPF(D_TERSE, ("DisconnectTopologies: UnregisterPhysicalConnection(capture) failed, 0x%x", ntStatus2)); + } + break; + } + + // cache and return the first error encountered, as it's likely the most relevent + if (NT_SUCCESS(ntStatus)) + { + ntStatus = ntStatus2; + } + } + } + + // + // Release the IUnregisterPhysicalConnection interface. + // + SAFE_RELEASE(unregisterPhysicalConnection); + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::GetCachedSubdevice +( + _In_ PWSTR Name, + _Out_opt_ PUNKNOWN *OutUnknownPort, + _Out_opt_ PUNKNOWN *OutUnknownMiniport +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::GetCachedSubdevice]")); + + // search list, return interface to device if found, fail if not found + PLIST_ENTRY le = NULL; + BOOL bFound = FALSE; + + for (le = m_SubdeviceCache.Flink; le != &m_SubdeviceCache && !bFound; le = le->Flink) + { + MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry); + + if (0 == wcscmp(Name, pRecord->Name)) + { + if (OutUnknownPort) + { + *OutUnknownPort = pRecord->PortInterface; + (*OutUnknownPort)->AddRef(); + } + + if (OutUnknownMiniport) + { + *OutUnknownMiniport = pRecord->MiniportInterface; + (*OutUnknownMiniport)->AddRef(); + } + + bFound = TRUE; + } + } + + return bFound?STATUS_SUCCESS:STATUS_OBJECT_NAME_NOT_FOUND; +} + + + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::CacheSubdevice +( + _In_ PWSTR Name, + _In_ PUNKNOWN UnknownPort, + _In_ PUNKNOWN UnknownMiniport +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::CacheSubdevice]")); + + // add the item with this name/interface to the list + NTSTATUS ntStatus = STATUS_SUCCESS; + MINIPAIR_UNKNOWN *pNewSubdevice = NULL; + + pNewSubdevice = new(NonPagedPoolNx, MINADAPTER_POOLTAG) MINIPAIR_UNKNOWN; + + if (!pNewSubdevice) + { + DPF(D_TERSE, ("Insufficient memory to cache subdevice")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + if (NT_SUCCESS(ntStatus)) + { + memset(pNewSubdevice, 0, sizeof(MINIPAIR_UNKNOWN)); + + ntStatus = RtlStringCchCopyW(pNewSubdevice->Name, SIZEOF_ARRAY(pNewSubdevice->Name), Name); + } + + if (NT_SUCCESS(ntStatus)) + { + pNewSubdevice->PortInterface = UnknownPort; + pNewSubdevice->PortInterface->AddRef(); + + pNewSubdevice->MiniportInterface = UnknownMiniport; + pNewSubdevice->MiniportInterface->AddRef(); + + // cache the IAdapterPowerManagement interface (if available) from the filter. Some endpoints, + // like FM and cellular, have their own power requirements that we must track. If this fails, + // it just means this filter doesn't do power management. + UnknownMiniport->QueryInterface(IID_IAdapterPowerManagement, (PVOID *)&(pNewSubdevice->PowerInterface)); + + InsertTailList(&m_SubdeviceCache, &pNewSubdevice->ListEntry); + } + + if (!NT_SUCCESS(ntStatus)) + { + if (pNewSubdevice) + { + delete pNewSubdevice; + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::RemoveCachedSubdevice +( + _In_ PWSTR Name +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::RemoveCachedSubdevice]")); + + // search list, remove the entry from the list + + PLIST_ENTRY le = NULL; + BOOL bRemoved = FALSE; + + for (le = m_SubdeviceCache.Flink; le != &m_SubdeviceCache && !bRemoved; le = le->Flink) + { + MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry); + + if (0 == wcscmp(Name, pRecord->Name)) + { + SAFE_RELEASE(pRecord->PortInterface); + SAFE_RELEASE(pRecord->MiniportInterface); + SAFE_RELEASE(pRecord->PowerInterface); + memset(pRecord->Name, 0, sizeof(pRecord->Name)); + RemoveEntryList(le); + bRemoved = TRUE; + delete pRecord; + break; + } + } + + return bRemoved?STATUS_SUCCESS:STATUS_OBJECT_NAME_NOT_FOUND; +} + +#pragma code_seg("PAGE") +VOID +CAdapterCommon::EmptySubdeviceCache() +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::EmptySubdeviceCache]")); + + while (!IsListEmpty(&m_SubdeviceCache)) + { + PLIST_ENTRY le = RemoveHeadList(&m_SubdeviceCache); + MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry); + + SAFE_RELEASE(pRecord->PortInterface); + SAFE_RELEASE(pRecord->MiniportInterface); + SAFE_RELEASE(pRecord->PowerInterface); + memset(pRecord->Name, 0, sizeof(pRecord->Name)); + + delete pRecord; + } +} + +#pragma code_seg("PAGE") +VOID +CAdapterCommon::Cleanup() +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::Cleanup]")); + +#ifdef SYSVAD_BTH_BYPASS + // + // This ensures Bluetooth HFP notifications are turned off when port class + // cleanups and unregisters the static subdevices. + // + CleanupBthScoBypass(); +#endif // SYSVAD_BTH_BYPASS + + EmptySubdeviceCache(); +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::InstallEndpointFilters +( + _In_opt_ PIRP Irp, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PVOID DeviceContext, + _Out_opt_ PUNKNOWN * UnknownTopology, + _Out_opt_ PUNKNOWN * UnknownWave +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::InstallEndpointFilters]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PUNKNOWN unknownTopology = NULL; + PUNKNOWN unknownWave = NULL; + BOOL bTopologyCreated = FALSE; + BOOL bWaveCreated = FALSE; + + if (UnknownTopology) + { + *UnknownTopology = NULL; + } + + if (UnknownWave) + { + *UnknownWave = NULL; + } + + ntStatus = GetCachedSubdevice(MiniportPair->TopoName, &unknownTopology, NULL); + if (!NT_SUCCESS(ntStatus) || NULL == unknownTopology) + { + PUNKNOWN unknownMiniportTopology = NULL; + + bTopologyCreated = TRUE; + + // Install SYSVAD topology miniport for the render endpoint. + // + ntStatus = InstallSubdevice(Irp, + MiniportPair->TopoName, // make sure this name matches with SYSVAD.<TopoName>.szPname in the inf's [Strings] section + CLSID_PortTopology, + CLSID_PortTopology, + MiniportPair->TopoCreateCallback, + MiniportPair->TopoInterfacePropertyCount, + MiniportPair->TopoInterfaceProperties, + DeviceContext, + MiniportPair, + NULL, + IID_IPortTopology, + NULL, + &unknownTopology, + &unknownMiniportTopology + ); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = CacheSubdevice(MiniportPair->TopoName, unknownTopology, unknownMiniportTopology); + } + + SAFE_RELEASE(unknownMiniportTopology); + } + + ntStatus = GetCachedSubdevice(MiniportPair->WaveName, &unknownWave, NULL); + if (!NT_SUCCESS(ntStatus) || NULL == unknownWave) + { + PUNKNOWN unknownMiniportWave = NULL; + + bWaveCreated = TRUE; + + // Install SYSVAD wave miniport for the render endpoint. + // + ntStatus = InstallSubdevice(Irp, + MiniportPair->WaveName, // make sure this name matches with SYSVAD.<WaveName>.szPname in the inf's [Strings] section + CLSID_PortWaveRT, + CLSID_PortWaveRT, + MiniportPair->WaveCreateCallback, + MiniportPair->WaveInterfacePropertyCount, + MiniportPair->WaveInterfaceProperties, + DeviceContext, + MiniportPair, + NULL, + IID_IPortWaveRT, + NULL, + &unknownWave, + &unknownMiniportWave + ); + + if (NT_SUCCESS(ntStatus)) + { + ntStatus = CacheSubdevice(MiniportPair->WaveName, unknownWave, unknownMiniportWave); + } + + SAFE_RELEASE(unknownMiniportWave); + } + + if (unknownTopology && unknownWave) + { + // + // register wave <=> topology connections + // This will connect bridge pins of wave and topology + // miniports. + // + ntStatus = ConnectTopologies( + unknownTopology, + unknownWave, + MiniportPair->PhysicalConnections, + MiniportPair->PhysicalConnectionCount); + } + + if (NT_SUCCESS(ntStatus)) + { + // + // Set output parameters. + // + if (UnknownTopology != NULL && unknownTopology != NULL) + { + unknownTopology->AddRef(); + *UnknownTopology = unknownTopology; + } + + if (UnknownWave != NULL && unknownWave != NULL) + { + unknownWave->AddRef(); + *UnknownWave = unknownWave; + } + } + else + { + if (bTopologyCreated && unknownTopology != NULL) + { + UnregisterSubdevice(unknownTopology); + RemoveCachedSubdevice(MiniportPair->TopoName); + } + + if (bWaveCreated && unknownWave != NULL) + { + UnregisterSubdevice(unknownWave); + RemoveCachedSubdevice(MiniportPair->WaveName); + } + } + + SAFE_RELEASE(unknownTopology); + SAFE_RELEASE(unknownWave); + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::RemoveEndpointFilters +( + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PUNKNOWN UnknownTopology, + _In_opt_ PUNKNOWN UnknownWave +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::RemoveEndpointFilters]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (UnknownTopology != NULL && UnknownWave != NULL) + { + ntStatus = DisconnectTopologies( + UnknownTopology, + UnknownWave, + MiniportPair->PhysicalConnections, + MiniportPair->PhysicalConnectionCount); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_VERBOSE, ("RemoveEndpointFilters: DisconnectTopologies failed: 0x%x", ntStatus)); + } + } + + + RemoveCachedSubdevice(MiniportPair->WaveName); + + ntStatus = UnregisterSubdevice(UnknownWave); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_VERBOSE, ("RemoveEndpointFilters: UnregisterSubdevice(wave) failed: 0x%x", ntStatus)); + } + + RemoveCachedSubdevice(MiniportPair->TopoName); + + ntStatus = UnregisterSubdevice(UnknownTopology); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_VERBOSE, ("RemoveEndpointFilters: UnregisterSubdevice(topology) failed: 0x%x", ntStatus)); + } + + // + // All Done. + // + ntStatus = STATUS_SUCCESS; + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::GetFilters +( + _In_ PENDPOINT_MINIPAIR MiniportPair, + _Out_opt_ PUNKNOWN * UnknownTopologyPort, + _Out_opt_ PUNKNOWN * UnknownTopologyMiniport, + _Out_opt_ PUNKNOWN * UnknownWavePort, + _Out_opt_ PUNKNOWN * UnknownWaveMiniport +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::GetFilters]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PUNKNOWN unknownTopologyPort = NULL; + PUNKNOWN unknownTopologyMiniport = NULL; + PUNKNOWN unknownWavePort = NULL; + PUNKNOWN unknownWaveMiniport = NULL; + + // if the client requested the topology filter, find it and return it + if (UnknownTopologyPort != NULL || UnknownTopologyMiniport != NULL) + { + ntStatus = GetCachedSubdevice(MiniportPair->TopoName, &unknownTopologyPort, &unknownTopologyMiniport); + if (NT_SUCCESS(ntStatus)) + { + if (UnknownTopologyPort) + { + *UnknownTopologyPort = unknownTopologyPort; + } + + if (UnknownTopologyMiniport) + { + *UnknownTopologyMiniport = unknownTopologyMiniport; + } + } + } + + // if the client requested the wave filter, find it and return it + if (NT_SUCCESS(ntStatus) && (UnknownWavePort != NULL || UnknownWaveMiniport != NULL)) + { + ntStatus = GetCachedSubdevice(MiniportPair->WaveName, &unknownWavePort, &unknownWaveMiniport); + if (NT_SUCCESS(ntStatus)) + { + if (UnknownWavePort) + { + *UnknownWavePort = unknownWavePort; + } + + if (UnknownWaveMiniport) + { + *UnknownWaveMiniport = unknownWaveMiniport; + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::SetIdlePowerManagement +( + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_ BOOL bEnabled +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::SetIdlePowerManagement]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + IUnknown *pUnknown = NULL; + PPORTCLSPOWER pPortClsPower = NULL; + // refcounting disable requests. Each miniport is responsible for calling this in pairs, + // disable on the first request to disable, enable on the last request to enable. + + // make sure that we always call SetIdlePowerManagment using the IPortClsPower + // from the requesting port, so we don't cache a reference to a port + // indefinitely, preventing it from ever unloading. + ntStatus = GetFilters(MiniportPair, NULL, NULL, &pUnknown, NULL); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = + pUnknown->QueryInterface + ( + IID_IPortClsPower, + (PVOID*) &pPortClsPower + ); + } + + if (NT_SUCCESS(ntStatus)) + { + if (bEnabled) + { + m_dwIdleRequests--; + + if (0 == m_dwIdleRequests) + { + pPortClsPower->SetIdlePowerManagement(m_pDeviceObject, TRUE); + } + } + else + { + if (0 == m_dwIdleRequests) + { + pPortClsPower->SetIdlePowerManagement(m_pDeviceObject, FALSE); + } + + m_dwIdleRequests++; + } + } + + SAFE_RELEASE(pUnknown); + SAFE_RELEASE(pPortClsPower); + + return ntStatus; +} + +#ifdef SYSVAD_BTH_BYPASS +// +// CAdapterCommon Bluetooth Hands-Free Profile function implementation. +// + +//============================================================================= +#pragma code_seg("PAGE") +VOID +CAdapterCommon::EvtBthHfpScoBypassInterfaceWorkItem +( + _In_ WDFWORKITEM WorkItem +) +/*++ + +Routine Description: + + The function handles the arrival or removal of a HFP SCO Bypass interface. + +Arguments: + + WorkItem - WDF work-item object. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[EvtBthHfpScoBypassInterfaceWorkItem]")); + + CAdapterCommon * This; + + if (WorkItem == NULL) + { + return; + } + + This = GetBthHfpWorkItemContext(WorkItem)->Adapter; + ASSERT(This != NULL); + + for (;;) + { + PLIST_ENTRY le = NULL; + BthHfpWorkTask * task = NULL; + + // + // Retrieve a taask. + // + ExAcquireFastMutex(&This->m_BthHfpFastMutex); + if (!IsListEmpty(&This->m_BthHfpWorkTasks)) + { + le = RemoveHeadList(&This->m_BthHfpWorkTasks); + task = CONTAINING_RECORD(le, BthHfpWorkTask, ListEntry); + InitializeListHead(le); + } + ExReleaseFastMutex(&This->m_BthHfpFastMutex); + + if (task == NULL) + { + break; + } + + ASSERT(task->Device != NULL); + _Analysis_assume_(task->Device != NULL); + + // + // Process the task. + // + switch(task->Action) + { + case eBthHfpTaskStart: + task->Device->Start(); + break; + + case eBthHfpTaskStop: + task->Device->Stop(); + break; + + default: + DPF(D_ERROR, ("EvtBthHfpScoBypassInterfaceWorkItem: invalid action %d", task->Action)); + break; + } + + // + // Release the ref we took on the device when we inserted the task in the queue. + // For a stop operation this may be the last reference. + // + SAFE_RELEASE(task->Device); + + // + // Free the task. + // + ExFreeToNPagedLookasideList(&This->m_BhtHfpWorkTaskPool, task); + } +} + +//============================================================================= +#pragma code_seg("PAGE") +BthHfpDevice * +CAdapterCommon::BthHfpDeviceFind +( + _In_ PUNICODE_STRING SymbolicLinkName +) +/*++ + +Routine Description: + + The function looks for the specified device in the adapter's list. + +Arguments: + + SymbolicLinkName - interface's symbolic link. + +Return Value: + + BthHfpDevice pointer or NULL. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::BthHfpDeviceFind]")); + + PLIST_ENTRY le = NULL; + BthHfpDevice * bthDevice = NULL; + + ExAcquireFastMutex(&m_BthHfpFastMutex); + + for (le = m_BthHfpDevices.Flink; le != &m_BthHfpDevices; le = le->Flink) + { + BthHfpDevice * tmpBthDevice = BthHfpDevice::GetBthHfpDevice(le); + ASSERT(tmpBthDevice != NULL); + + PUNICODE_STRING unicodeStr = tmpBthDevice->GetSymbolicLinkName(); + ASSERT(unicodeStr != NULL); + + if (unicodeStr->Length == SymbolicLinkName->Length && + 0 == wcsncmp(unicodeStr->Buffer, SymbolicLinkName->Buffer, unicodeStr->Length/sizeof(WCHAR))) + { + // Found it! + bthDevice = tmpBthDevice; + bthDevice->AddRef(); + break; + } + } + + ExReleaseFastMutex(&m_BthHfpFastMutex); + + return bthDevice; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::BthHfpScoInterfaceArrival +( + _In_ PUNICODE_STRING SymbolicLinkName +) +/*++ + +Routine Description: + + The function handles the arrival of a new HFP SCO Bypass interface. + +Arguments: + + SymbolicLinkName - new interface's symbolic link. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::BthHfpScoInterfaceArrival]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDevice * bthDevice = NULL; + BthHfpWorkTask * bthWorkTask = NULL; + + DPF(D_VERBOSE, ("BthHfpScoInterfaceArrival: SymbolicLinkName %wZ", SymbolicLinkName)); + + // + // Check if the Bluetooth device is already present. + // According to the docs it is possible to receive two notifications for the same + // interface. + // + bthDevice = BthHfpDeviceFind(SymbolicLinkName); + if (bthDevice != NULL) + { + DPF(D_VERBOSE, ("BthHfpScoInterfaceArrival: Bluetooth HFP device already present")); + SAFE_RELEASE(bthDevice); + ntStatus = STATUS_SUCCESS; + goto Done; + } + + // + // Alloc a new structure for this Bluetooth hands-free device. + // + bthDevice = new (NonPagedPoolNx, MINADAPTER_POOLTAG) BthHfpDevice(NULL); // NULL -> OuterUnknown + if (NULL == bthDevice) + { + DPF(D_ERROR, ("BthHfpScoInterfaceArrival: unable to allocate BthHfpDevice, out of memory")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + DPF(D_VERBOSE, ("BthHfpScoInterfaceArrival: created BthHfpDevice 0x%p ", bthDevice)); + + // + // Basic initialization of the Bluetooth Hands-Free Profile interface. + // The audio miniport creation is done later by the BthHfpDevice.Start() + // which is invoked asynchronously by a worker thread. + // BthHfpDevice->Init() must be invoked just after the creation of the object. + // + ntStatus = bthDevice->Init(this, SymbolicLinkName); + IF_FAILED_JUMP(ntStatus, Done); + + // + // Get and init a work task. + // + bthWorkTask = (BthHfpWorkTask*)ExAllocateFromNPagedLookasideList(&m_BhtHfpWorkTaskPool); + if (NULL == bthWorkTask) + { + DPF(D_ERROR, ("BthHfpScoInterfaceArrival: unable to allocate BthHfpWorkTask, out of memory")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + RtlZeroMemory(bthWorkTask, sizeof(*bthWorkTask)); + bthWorkTask->Action = eBthHfpTaskStart; + InitializeListHead(&bthWorkTask->ListEntry); + // Note that bthDevice has one reference at this point. + bthWorkTask->Device = bthDevice; + + ExAcquireFastMutex(&m_BthHfpFastMutex); + + // + // Insert this new Bluetooth HFP device in our list. + // + InsertTailList(&m_BthHfpDevices, bthDevice->GetListEntry()); + + // + // Add a new task for the worker thread. + // + InsertTailList(&m_BthHfpWorkTasks, &bthWorkTask->ListEntry); + bthDevice->AddRef(); // released when task runs. + + // + // Schedule a work-item if not already running. + // + WdfWorkItemEnqueue(m_BthHfpWorkItem); + + ExReleaseFastMutex(&m_BthHfpFastMutex); + +Done: + if (!NT_SUCCESS(ntStatus)) + { + // Release the last ref, this will delete the BthHfpDevice + SAFE_RELEASE(bthDevice); + + if (bthWorkTask != NULL) + { + ExFreeToNPagedLookasideList(&m_BhtHfpWorkTaskPool, bthWorkTask); + bthWorkTask = NULL; + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::BthHfpScoInterfaceRemoval +( + _In_ PUNICODE_STRING SymbolicLinkName +) +/*++ + +Routine Description: + + The function handles the removal of a HFP SCO Bypass interface. + +Arguments: + + SymbolicLinkName - interface's symbolic link to remove. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::BthHfpScoInterfaceRemoval]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDevice * bthDevice = NULL; + BthHfpWorkTask * bthWorkTask = NULL; + + DPF(D_VERBOSE, ("BthHfpScoInterfaceRemoval: SymbolicLinkName %wZ", SymbolicLinkName)); + + // + // Check if the Bluetooth device is present. + // + bthDevice = BthHfpDeviceFind(SymbolicLinkName); + if (bthDevice == NULL) + { + // This can happen if the init/start of the BthHfpDevice failed. + DPF(D_VERBOSE, ("BthHfpScoInterfaceRemoval: Bluetooth HFP device not found")); + ntStatus = STATUS_SUCCESS; + goto Done; + } + + // + // Init a work task. + // + bthWorkTask = (BthHfpWorkTask*)ExAllocateFromNPagedLookasideList(&m_BhtHfpWorkTaskPool); + if (NULL == bthWorkTask) + { + DPF(D_ERROR, ("BthHfpScoInterfaceRemoval: unable to allocate BthHfpWorkTask, out of memory")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + RtlZeroMemory(bthWorkTask, sizeof(*bthWorkTask)); + bthWorkTask->Action = eBthHfpTaskStop; + InitializeListHead(&bthWorkTask->ListEntry); + // Work-item callback will release the reference we got above from BthHfpDeviceFind. + bthWorkTask->Device = bthDevice; + + ExAcquireFastMutex(&m_BthHfpFastMutex); + + // + // Remove this Bluetooth device from our list and release the associated reference. + // + RemoveEntryList(bthDevice->GetListEntry()); + InitializeListHead(bthDevice->GetListEntry()); + bthDevice->Release(); // This is not the last ref. + + // + // Add a new task for the worker thread. + // + InsertTailList(&m_BthHfpWorkTasks, &bthWorkTask->ListEntry); + + // + // Schedule a work-item if not already running. + // + WdfWorkItemEnqueue(m_BthHfpWorkItem); + + ExReleaseFastMutex(&m_BthHfpFastMutex); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + if (!NT_SUCCESS(ntStatus)) + { + // Release the ref we got in find. + SAFE_RELEASE(bthDevice); + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::EvtBthHfpScoBypassInterfaceChange( + _In_ PVOID NotificationPointer, + _Inout_opt_ PVOID Context + ) +/*++ + +Routine Description: + + This callback is invoked when a new HFP SCO Bypass interface is added or removed. + +Arguments: + NotificationPointer - Interface change notification + Context - CAdapterCommon ptr. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[EvtBthHfpScoBypassInterfaceChange]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + CAdapterCommon * This = NULL; + PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification = (PDEVICE_INTERFACE_CHANGE_NOTIFICATION) NotificationPointer; + + // + // Make sure this is the interface class we extect. Any other class guid + // is an error, but let it go since it is not fatal to the machine. + // + if (!IsEqualGUID(Notification->InterfaceClassGuid, GUID_DEVINTERFACE_BLUETOOTH_HFP_SCO_HCIBYPASS)) + { + DPF(D_VERBOSE, ("EvtBthHfpScoBypassInterfaceChange: bad interface ClassGuid")); + ASSERTMSG("EvtBthHfpScoBypassInterfaceChange: bad interface ClassGuid ", FALSE); + + goto Done; + } + + This = (CAdapterCommon *)Context; + ASSERT(This != NULL); + _Analysis_assume_(This != NULL); + + // + // Take action based on the event. Any other event type is an error, + // but let it go since it is not fatal to the machine. + // + if (IsEqualGUID(Notification->Event, GUID_DEVICE_INTERFACE_ARRIVAL)) + { + ntStatus = This->BthHfpScoInterfaceArrival(Notification->SymbolicLinkName); + } + else if (IsEqualGUID(Notification->Event, GUID_DEVICE_INTERFACE_REMOVAL)) + { + ntStatus = This->BthHfpScoInterfaceRemoval(Notification->SymbolicLinkName); + } + else + { + DPF(D_VERBOSE, ("EvtBthHfpScoBypassInterfaceChange: bad " + "GUID_DEVINTERFACE_BLUETOOTH_HFP_SCO_HCIBYPASS event")); + ASSERTMSG("EvtBthHfpScoBypassInterfaceChange: bad " + "GUID_DEVINTERFACE_BLUETOOTH_HFP_SCO_HCIBYPASS event ", FALSE); + + goto Done; + } + +Done: + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::InitBthScoBypass() +/*++ + +Routine Description: + + Initialize the bluetooth bypass environment. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::InitBluetoothBypass]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + WDF_WORKITEM_CONFIG wiConfig; + WDF_OBJECT_ATTRIBUTES attributes; + BthHfpWorkItemContext * wiContext; + + // + // Init spin-lock, linked lists, work-item, event, etc. + // Init all members to default values. This basic init should not fail. + // + m_BthHfpWorkItem = NULL; + m_BthHfpScoNotificationHandle = NULL; + ExInitializeFastMutex(&m_BthHfpFastMutex); + InitializeListHead(&m_BthHfpWorkTasks); + InitializeListHead(&m_BthHfpDevices); + m_BhtHfpWorkTaskPoolElementSize = sizeof(BthHfpWorkTask); + ExInitializeNPagedLookasideList(&m_BhtHfpWorkTaskPool, + NULL, + NULL, + POOL_NX_ALLOCATION, + m_BhtHfpWorkTaskPoolElementSize, + MINADAPTER_POOLTAG, + 0); + // + // Enable Bluetooth HFP SCO-Bypass Cleanup. + // Do any allocation/initialization that can fail after this point. + // + m_BthHfpEnableCleanup = TRUE; + + // + // Allocate a WDF work-item. + // + WDF_WORKITEM_CONFIG_INIT(&wiConfig, EvtBthHfpScoBypassInterfaceWorkItem); + wiConfig.AutomaticSerialization = FALSE; + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpWorkItemContext); + attributes.ParentObject = GetWdfDevice(); + ntStatus = WdfWorkItemCreate( &wiConfig, + &attributes, + &m_BthHfpWorkItem); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("InitBthScoBypass: WdfWorkItemCreate failed: 0x%x", ntStatus)), + Done); + + wiContext = GetBthHfpWorkItemContext(m_BthHfpWorkItem); + wiContext->Adapter = this; // weak ref. + + // + // Register for bluetooth heandsfree profile interface changes. + // + ntStatus = IoRegisterPlugPlayNotification ( + EventCategoryDeviceInterfaceChange, + PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, + (PVOID)&GUID_DEVINTERFACE_BLUETOOTH_HFP_SCO_HCIBYPASS, + m_pDeviceObject->DriverObject, + EvtBthHfpScoBypassInterfaceChange, + (PVOID)this, + &m_BthHfpScoNotificationHandle); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("InitBthScoBypass: IoRegisterPlugPlayNotification(GUID_DEVINTERFACE_BLUETOOTH_HFP_SCO_HCIBYPASS) failed: 0x%x", ntStatus)), + Done); + + // + // Initialization completed. + // + ntStatus = STATUS_SUCCESS; + +Done: + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +CAdapterCommon::CleanupBthScoBypass() +/*++ + +Routine Description: + + Cleanup the bluetooth bypass environment. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::CleanupBthScoBypass]")); + + // + // Do nothing if Bluetooth HFP environment was not correctly initialized. + // + if (m_BthHfpEnableCleanup == FALSE) + { + return; + } + + // + // Unregister for bluetooth heandsfree profile interface changes. + // + if (m_BthHfpScoNotificationHandle != NULL) + { + (void)IoUnregisterPlugPlayNotificationEx(m_BthHfpScoNotificationHandle); + m_BthHfpScoNotificationHandle = NULL; + } + + // + // Wait for the Bluetooth hands-free profile worker thread to be done. + // + if (m_BthHfpWorkItem != NULL) + { + WdfWorkItemFlush(m_BthHfpWorkItem); + WdfObjectDelete(m_BthHfpWorkItem); + m_BthHfpWorkItem = NULL; + } + + ASSERT(IsListEmpty(&m_BthHfpWorkTasks)); + + // + // Stop and delete all BthHfpDevices. We are the only thread accessing this list, + // so there is no need to acquire the mutex. + // + while (!IsListEmpty(&m_BthHfpDevices)) + { + BthHfpDevice * bthDevice = NULL; + PLIST_ENTRY le = NULL; + + le = RemoveHeadList(&m_BthHfpDevices); + + bthDevice = BthHfpDevice::GetBthHfpDevice(le); + InitializeListHead(le); + + // bthDevice is invalid after this call. + bthDevice->Stop(); + + // This should be the last reference. + bthDevice->Release(); + } + + ASSERT(IsListEmpty(&m_BthHfpDevices)); + + // + // General cleanup. + // + ExDeleteNPagedLookasideList(&m_BhtHfpWorkTaskPool); +} +#endif // SYSVAD_BTH_BYPASS + +#ifdef SYSVAD_BTH_BYPASS +// +// BthHfpDevice implementation. +// + +//============================================================================= +#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(PBTHHFPDEVICECOMMON(this))); + } + else if (IsEqualGUIDAligned(Interface, IID_IBthHfpDeviceCommon)) + { + *Object = PVOID(PBTHHFPDEVICECOMMON(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_ CAdapterCommon * 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_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)); + + // + // 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 list. + // + 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); + + // + // 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); +} // ~CAdapterCommon + +// +// IBthHfpDeviceCommon implementation. +// + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +BthHfpDevice::IsVolumeSupported() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::IsVolumeSupported]")); + + return m_Descriptor->SupportsVolume; +} + +//============================================================================= +#pragma code_seg("PAGE") +PKSPROPERTY_VALUES +BthHfpDevice::GetVolumeSettings +( + _Out_ PULONG Size +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetVolumeSettings]")); + + ASSERT(Size != NULL); + + *Size = m_Descriptor->VolumePropertyValuesSize; + + return m_VolumePropValues; +} + +//============================================================================= +#pragma code_seg("PAGE") +LONG +BthHfpDevice::GetSpeakerVolume() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetSpeakerVolume]")); + + return InterlockedCompareExchange(&m_SpeakerVolumeLevel, 0, 0); +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetSpeakerVolume +( + _In_ ULONG Volume +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetSpeakerVolume]")); + + return SetBthHfpSpeakerVolume(Volume); +} + +//============================================================================= +#pragma code_seg("PAGE") +LONG +BthHfpDevice::GetMicVolume() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetMicVolume]")); + + return InterlockedCompareExchange(&m_MicVolumeLevel, 0, 0); +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetMicVolume +( + _In_ ULONG Volume +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetMicVolume]")); + + return SetBthHfpMicVolume(Volume); +} + +//============================================================================= +#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); + + typedef enum _HFP_BYPASS_CODEC_ID_VERSION { + REQ_HFP_BYPASS_CODEC_ID_V1 = 1, + } HFP_BYPASS_CODEC_ID_VERSION, *PHFP_BYPASS_CODEC_ID_VERSION; + + typedef struct _HFP_BYPASS_CODEC_ID_V1 { + UCHAR CodecId; + } HFP_BYPASS_CODEC_ID_V1, *PHFP_BYPASS_CODEC_ID_V1; + + 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() +{ + 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() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::StreamOpen]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + LONG nStreams = 0; + + ASSERT(m_nStreams >= 0); + + nStreams = InterlockedIncrement(&m_nStreams); + if (nStreams == 1) + { + BOOLEAN streamOpen = FALSE; + + ntStatus = SetBthHfpStreamOpen(); + if (NT_SUCCESS(ntStatus)) + { + streamOpen = 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 (streamOpen) + { + SetBthHfpStreamClose(); + } + + m_StreamStatus = STATUS_INVALID_DEVICE_STATE; + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::StreamClose() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::StreamClose]")); + + 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") +GUID +BthHfpDevice::GetContainerId() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetContainerId]")); + + return m_Descriptor->ContainerId; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::SetSpeakerVolumeHandler +( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) + { + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetSpeakerVolumeHandler]")); + + ASSERT(EventHandler == NULL || m_SpeakerVolumeCallback.Handler == NULL); + + m_SpeakerVolumeCallback.Handler = EventHandler; // weak ref. + m_SpeakerVolumeCallback.Context = EventHandlerContext; + } + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::SetSpeakerConnectionStatusHandler +( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetSpeakerConnectionStatusHandler]")); + + ASSERT(EventHandler == NULL || m_SpeakerConnectionStatusCallback.Handler == NULL); + + m_SpeakerConnectionStatusCallback.Handler = EventHandler; // weak ref. + m_SpeakerConnectionStatusCallback.Context = EventHandlerContext; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::SetMicVolumeHandler +( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetMicVolumeHandler]")); + + ASSERT(EventHandler == NULL || m_MicVolumeCallback.Handler == NULL); + + m_MicVolumeCallback.Handler = EventHandler; // weak ref. + m_MicVolumeCallback.Context = EventHandlerContext; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::SetMicConnectionStatusHandler +( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetMicConnectionStatusHandler]")); + + ASSERT(EventHandler == NULL || m_MicConnectionStatusCallback.Handler == NULL); + + m_MicConnectionStatusCallback.Handler = EventHandler; // weak ref. + m_MicConnectionStatusCallback.Context = EventHandlerContext; +} + +//============================================================================= +#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; + + 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::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; + UCHAR codecId = 0; + UINT bthMiniportsIndex = 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. + const UCHAR CODEC_CVSD = 1; + + // + // 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) + // + 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); + + if (codecId > 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); + + 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); + + 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, + PBTHHFPDEVICECOMMON(this), + &m_UnknownSpeakerTopology, + &m_UnknownSpeakerWave + ); + + 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, + PBTHHFPDEVICECOMMON(this), + &m_UnknownMicTopology, + &m_UnknownMicWave + ); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: InstallEndpointCaptureFilters (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, pBaseMinipair->TopoInterfacePropertyCount * 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); + + DeleteCustomEndpointMinipair(m_SpeakerMiniports); + m_SpeakerMiniports = NULL; + + DeleteCustomEndpointMinipair(m_MicMiniports); + m_MicMiniports = NULL; + +} + +#endif // SYSVAD_BTH_BYPASS + + |
