Sample Code
Windows Driver Samples/ Microsoft slate system virtual audio device driver sample/ C++/ common.cpp/
/*++ Copyright (c) Microsoft Corporation All Rights Reserved Module Name: common.cpp Abstract: Implementation of the AdapterCommon class. --*/ #pragma warning (disable : 4127) #include <sysvad.h> #include "hw.h" #include "savedata.h" #include "IHVPrivatePropertySet.h" #include "simple.h" #ifdef SYSVAD_BTH_BYPASS #include <limits.h> #include <initguid.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. 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_opt_ PVOID DeviceContext, _In_ PENDPOINT_MINIPAIR MiniportPair, _In_opt_ PRESOURCELIST ResourceList, _In_ REFGUID PortInterfaceId, _Out_opt_ PUNKNOWN * OutPortInterface, _Out_opt_ PUNKNOWN * OutPortUnknown ); STDMETHODIMP_(NTSTATUS) UnregisterSubdevice ( _In_opt_ PUNKNOWN UnknownPort ); STDMETHODIMP_(NTSTATUS) ConnectTopologies ( _In_ PUNKNOWN UnknownTopology, _In_ PUNKNOWN UnknownWave, _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections ); STDMETHODIMP_(NTSTATUS) DisconnectTopologies ( _In_ PUNKNOWN UnknownTopology, _In_ PUNKNOWN UnknownWave, _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections ); 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 ); #ifdef SYSVAD_BTH_BYPASS STDMETHODIMP_(NTSTATUS) InitBthScoBypass(); STDMETHODIMP_(VOID) CleanupBthScoBypass(); #endif // SYSVAD_BTH_BYPASS //===================================================================== // 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 }; // // 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; UNICODE_STRING m_SpeakerAudioSymbolicLinkName; UNICODE_STRING m_MicAudioSymbolicLinkName; 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(); NTSTATUS CreateAudioInterface ( _Inout_ PWSTR TopologyName, _Out_ PUNICODE_STRING AudioSymbolicLinkName ); 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 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; // // 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]")); // 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. --*/ { UNREFERENCED_PARAMETER(NewStateQuery); DPF_ENTER(("[CAdapterCommon::QueryPowerChangeState]")); return STATUS_SUCCESS; } // QueryPowerChangeState //============================================================================= #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_opt_ PVOID DeviceContext, _In_ PENDPOINT_MINIPAIR MiniportPair, _In_opt_ PRESOURCELIST ResourceList, _In_ REFGUID PortInterfaceId, _Out_opt_ PUNKNOWN * OutPortInterface, _Out_opt_ PUNKNOWN * OutPortUnknown ) { /*++ 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. 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; adapterCommon = PADAPTERCOMMON(this); // 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 ); } // We don't need the miniport any more. Either the port has it, // or we've failed, and it should be deleted. // miniport->Release(); } // 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 (port) { port->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 ) /*++ 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; BOOLEAN fPhyConnTopoToWave = FALSE; BOOLEAN fPhyConnWaveToTopo = FALSE; // // register wave <=> topology connections // This will connect bridge pins of wave and topology // miniports. // if ((PhysicalConnections->ulTopologyOut != (ULONG)-1) && (PhysicalConnections->ulWaveIn != (ULONG)-1)) { ntStatus = PcRegisterPhysicalConnection ( m_pDeviceObject, UnknownTopology, PhysicalConnections->ulTopologyOut, UnknownWave, PhysicalConnections->ulWaveIn ); if (NT_SUCCESS(ntStatus)) { fPhyConnTopoToWave = TRUE; } } if (NT_SUCCESS(ntStatus)) { if ((PhysicalConnections->ulWaveOut != (ULONG)-1) && (PhysicalConnections->ulTopologyIn != (ULONG)-1)) { ntStatus = PcRegisterPhysicalConnection ( m_pDeviceObject, UnknownWave, PhysicalConnections->ulWaveOut, UnknownTopology, PhysicalConnections->ulTopologyIn ); if (NT_SUCCESS(ntStatus)) { // just in case more code is added into this function later on. fPhyConnWaveToTopo = TRUE; } } } // // Cleanup in case of error. // if (!NT_SUCCESS(ntStatus) && (fPhyConnWaveToTopo || fPhyConnTopoToWave)) { PUNREGISTERPHYSICALCONNECTION unregisterPhysicalConnection = NULL; NTSTATUS ntStatus2; ASSERT(UnknownTopology != NULL); // // Get the IUnregisterPhysicalConnection interface // ntStatus2 = UnknownTopology->QueryInterface( IID_IUnregisterPhysicalConnection, (PVOID *)&unregisterPhysicalConnection); if (NT_SUCCESS(ntStatus2)) { // // Remove the render physical connection // if (fPhyConnWaveToTopo) { ntStatus2 = unregisterPhysicalConnection->UnregisterPhysicalConnection( m_pDeviceObject, UnknownWave, PhysicalConnections->ulWaveOut, UnknownTopology, PhysicalConnections->ulTopologyIn); } // // Remove the capture physical connection // if (fPhyConnTopoToWave) { ntStatus2 = unregisterPhysicalConnection->UnregisterPhysicalConnection( m_pDeviceObject, UnknownTopology, PhysicalConnections->ulTopologyOut, UnknownWave, PhysicalConnections->ulWaveIn); } } UNREFERENCED_VAR(ntStatus2); // // Release the IUnregisterPhysicalConnection interface. // SAFE_RELEASE(unregisterPhysicalConnection); } return ntStatus; } //============================================================================= #pragma code_seg("PAGE") STDMETHODIMP_(NTSTATUS) CAdapterCommon::DisconnectTopologies ( _In_ PUNKNOWN UnknownTopology, _In_ PUNKNOWN UnknownWave, _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections ) /*++ 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)) { // // Remove the render physical connection // if ((PhysicalConnections->ulWaveOut != (ULONG)-1) && (PhysicalConnections->ulTopologyIn != (ULONG)-1)) { ntStatus = unregisterPhysicalConnection->UnregisterPhysicalConnection( m_pDeviceObject, UnknownWave, PhysicalConnections->ulWaveOut, UnknownTopology, PhysicalConnections->ulTopologyIn); if(!NT_SUCCESS(ntStatus)) { DPF(D_TERSE, ("DisconnectTopologies: UnregisterPhysicalConnection(render) failed, 0x%x", ntStatus)); } } // // Remove the capture physical connection // if ((PhysicalConnections->ulTopologyOut != (ULONG)-1) && (PhysicalConnections->ulWaveIn != (ULONG)-1)) { ntStatus = unregisterPhysicalConnection->UnregisterPhysicalConnection( m_pDeviceObject, UnknownTopology, PhysicalConnections->ulTopologyOut, UnknownWave, PhysicalConnections->ulWaveIn); if(!NT_SUCCESS(ntStatus2)) { DPF(D_TERSE, ("DisconnectTopologies: UnregisterPhysicalConnection(capture) failed, 0x%x", ntStatus2)); if (NT_SUCCESS(ntStatus)) { ntStatus = ntStatus2; } } } } // // Release the IUnregisterPhysicalConnection interface. // SAFE_RELEASE(unregisterPhysicalConnection); return ntStatus; } //============================================================================= #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; if (UnknownTopology) { *UnknownTopology = NULL; } if (UnknownWave) { *UnknownWave = NULL; } // 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, DeviceContext, MiniportPair, NULL, IID_IPortTopology, NULL, &unknownTopology ); // Install SYSVAD wave miniport for the render endpoint. // if (NT_SUCCESS(ntStatus)) { 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, DeviceContext, MiniportPair, NULL, IID_IPortWaveRT, NULL, &unknownWave ); } if (unknownTopology && unknownWave) { // // register wave <=> topology connections // This will connect bridge pins of wave and topology // miniports. // ntStatus = ConnectTopologies( unknownTopology, unknownWave, MiniportPair->PhysicalConnections); } 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 (unknownTopology != NULL) { UnregisterSubdevice(unknownTopology); } if (unknownWave != NULL) { UnregisterSubdevice(unknownWave); } } 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); if (!NT_SUCCESS(ntStatus)) { DPF(D_VERBOSE, ("RemoveEndpointFilters: DisconnectTopologies failed: 0x%x", ntStatus)); } } ntStatus = UnregisterSubdevice(UnknownWave); if (!NT_SUCCESS(ntStatus)) { DPF(D_VERBOSE, ("RemoveEndpointFilters: UnregisterSubdevice(wave) failed: 0x%x", ntStatus)); } ntStatus = UnregisterSubdevice(UnknownTopology); if (!NT_SUCCESS(ntStatus)) { DPF(D_VERBOSE, ("RemoveEndpointFilters: UnregisterSubdevice(topology) failed: 0x%x", ntStatus)); } // // All Done. // ntStatus = STATUS_SUCCESS; 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, 0, 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_SpeakerAudioSymbolicLinkName, sizeof(m_SpeakerAudioSymbolicLinkName)); RtlZeroMemory(&m_MicAudioSymbolicLinkName, sizeof(m_MicAudioSymbolicLinkName)); 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)); } if (m_SpeakerAudioSymbolicLinkName.Buffer != NULL) { RtlFreeUnicodeString(&m_SpeakerAudioSymbolicLinkName); // yes, this must use RtlFreeUnicodeString. RtlZeroMemory(&m_SpeakerAudioSymbolicLinkName, sizeof(m_SpeakerAudioSymbolicLinkName)); } if (m_MicAudioSymbolicLinkName.Buffer != NULL) { RtlFreeUnicodeString(&m_MicAudioSymbolicLinkName); // yes, this must use RtlFreeUnicodeString. RtlZeroMemory(&m_MicAudioSymbolicLinkName, sizeof(m_MicAudioSymbolicLinkName)); } 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::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("PAGE") NTSTATUS BthHfpDevice::CreateAudioInterface ( _Inout_ PWSTR TopologyName, _Out_ PUNICODE_STRING AudioSymbolicLinkName ) /*++ Routine Description: Create the audio interface (in disabled mode). --*/ { PAGED_CODE(); DPF_ENTER(("[BthHfpDevice::CreateAudioInterface]")); NTSTATUS ntStatus = STATUS_SUCCESS; UNICODE_STRING topoName; RtlInitUnicodeString(&topoName, TopologyName); // // Reset output value. // RtlInitUnicodeString(AudioSymbolicLinkName, NULL); // // Register an audio interface if not already present. // ntStatus = IoRegisterDeviceInterface( m_Adapter->GetPhysicalDeviceObject(), &KSCATEGORY_AUDIO, &topoName, AudioSymbolicLinkName); IF_FAILED_ACTION_JUMP( ntStatus, DPF(D_ERROR, ("CreateAudioInterface: IoRegisterDeviceInterface(KSCATEGORY_AUDIO): failed, 0x%x", ntStatus)), Done); // // Add the friendly name to the interface. // ntStatus = IoSetDeviceInterfacePropertyData( AudioSymbolicLinkName, &DEVPKEY_DeviceInterface_FriendlyName, LOCALE_NEUTRAL, PLUGPLAY_PROPERTY_PERSISTENT, DEVPROP_TYPE_STRING_INDIRECT, m_Descriptor->FriendlyName.Length + sizeof(UNICODE_NULL), m_Descriptor->FriendlyName.Buffer); IF_FAILED_ACTION_JUMP( ntStatus, DPF(D_ERROR, ("CreateAudioInterface: IoSetDeviceInterfacePropertyData(DEVPKEY_DeviceInterface_FriendlyName): failed, 0x%x", ntStatus)), Done); // // All done. // ntStatus = STATUS_SUCCESS; Done: return ntStatus; } //============================================================================= #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, ("GetBthHfpDescriptor: 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_DESCRIPTOR): failed, 0x%x", ntStatus)); goto Done; } ntStatus = STATUS_SUCCESS; information = WdfRequestGetInformation(req); if (information == 0 || information > ULONG_MAX) { ntStatus = STATUS_INVALID_DEVICE_STATE; DPF(D_ERROR, ("GetBthHfpDescriptor: IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR 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_DESCRIPTOR, NULL, length, descriptor); IF_FAILED_ACTION_JUMP( ntStatus, DPF(D_ERROR, ("GetBthHfpDescriptor: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR) failed, 0x%x", ntStatus)), Done); // // All done. // *Descriptor = descriptor; ntStatus = STATUS_SUCCESS; Done: if (!NT_SUCCESS(ntStatus)) { if (descriptor != NULL) { ExFreePoolWithTag(descriptor, MINADAPTER_POOLTAG); } } if (req != NULL) { WdfObjectDelete(req); req = NULL; } return ntStatus; } //============================================================================= #pragma code_seg() 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; // // 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 the topology/wave descriptors. // m_SpeakerMiniports = g_BthHfpRenderEndpoints[0]; m_MicMiniports = g_BthHfpCaptureEndpoints[0]; ASSERT(m_SpeakerMiniports != NULL); ASSERT(m_MicMiniports != NULL); _Analysis_assume_(m_SpeakerMiniports != NULL); _Analysis_assume_(m_MicMiniports != NULL); // // Create the audio interface (in disabled mode). // ntStatus = CreateAudioInterface(m_SpeakerMiniports->TopoName, &m_SpeakerAudioSymbolicLinkName); IF_FAILED_ACTION_JUMP( ntStatus, DPF(D_ERROR, ("Start: CreateAudioInterface(KSCATEGORY_AUDIO) for speaker: failed, 0x%x", ntStatus)), Done); ntStatus = CreateAudioInterface(m_MicMiniports->TopoName, &m_MicAudioSymbolicLinkName); IF_FAILED_ACTION_JUMP( ntStatus, DPF(D_ERROR, ("Start: CreateAudioInterface(KSCATEGORY_AUDIO) for mic: failed, 0x%x", ntStatus)), Done); // // 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") 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); } #endif // SYSVAD_BTH_BYPASS
Our Services
-
What our customers say about us?
Read our customer testimonials to find out why our clients keep returning for their projects.
View Testimonials