diff options
| author | Girish Pattabiraman <[email protected]> | 2018-11-12 18:08:19 -0800 |
|---|---|---|
| committer | Adonais Romero González <[email protected]> | 2018-11-12 18:08:19 -0800 |
| commit | 995e20f6dbca7ad10a039fa62a2de69eed092ffc (patch) | |
| tree | 67075f06fd8aaedad732d6c73b682c6e0fda9dba /audio/sysvad/UsbHsDevice.cpp | |
| parent | c9110a3bc1634a5be8e7922119ad383311104095 (diff) | |
RS5 Updates (#311)
* Format Changes
==============
* Add KSEVENT_PINCAPS_FORMATCHANGE to Bluetooth HFP Endpoints
* Remove 8 bit formats from Bluetooth HFP Endpoints
Name Template
==============
* Bluetooth Endpoints are now based on templates in INF and then customized with endpoints specific data
Sideband Common Interface for future Sideband buses
===================================================
* Add ISidebandDevice
* Add current directory to TabletAudioSample include path. Remove unused compiler flags.
* RS4 bug fixes
* Replace FastMutex with Spinlock for DeviceFormatsAndModes to work well at DISPATCH_LEVEL
* Resync with Microsoft-master branch
* Replace FastMutex with Spinlock for DeviceFormatsAndModes to work well at DISPATCH_LEVEL
* RS5 updates:
- USB Sideband implementation
- Power management options: _USE_IPortClsRuntimePower, _USE_SingleComponentMultiFxStates
Diffstat (limited to 'audio/sysvad/UsbHsDevice.cpp')
| -rw-r--r-- | audio/sysvad/UsbHsDevice.cpp | 5036 |
1 files changed, 5036 insertions, 0 deletions
diff --git a/audio/sysvad/UsbHsDevice.cpp b/audio/sysvad/UsbHsDevice.cpp new file mode 100644 index 00000000..6bc42384 --- /dev/null +++ b/audio/sysvad/UsbHsDevice.cpp @@ -0,0 +1,5036 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + UsbHsDevice.cpp + +Abstract: + + Implementation of the UsbHsDevice class. + +--*/ + +#pragma warning (disable : 4127) + +#include <initguid.h> +#include <sysvad.h> +#include "hw.h" +#include "savedata.h" +#include "IHVPrivatePropertySet.h" +#include "simple.h" + +#ifdef SYSVAD_USB_SIDEBAND +#include <limits.h> +#include <usbspec.h> +#include <usb.h> +#include <SidebandAudio.h> +#include <USBSidebandAudio.h> +#include <wdmguid.h> // guild-arrival/removal +#include <devpkey.h> +#include "usbhsminipairs.h" +#include "UsbHsDevice.h" +#include "UsbHsDeviceFormats.h" + +// +// UsbHsDevice implementation. +// + +// # of sec before sync request is cancelled. +#define USB_SIDEBAND_SYNC_REQ_TIMEOUT_IN_SEC 60 +#define USB_SIDEBAND_NOTIFICATION_MAX_ERROR_COUNT 5 + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP +UsbHsDevice::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface routine for UsbHsDevice + +Arguments: + + Interface - + + Object - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(PSIDEBANDDEVICECOMMON(this))); + } + else if (IsEqualGUIDAligned(Interface, IID_IUsbHsDeviceCommon)) + { + *Object = PVOID(PSIDEBANDDEVICECOMMON(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + PUNKNOWN(*Object)->AddRef(); + return STATUS_SUCCESS; + } + + return STATUS_INVALID_PARAMETER; +} // NonDelegatingQueryInterface + +//============================================================================= +// Dummy stubs to override the default WDF behavior of closing the target +// on query remove. This driver closes and deletes the supporting objects +// when the target removes the USB Sideband interface. +// + +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::EvtUsbHsTargetQueryRemove +( + _In_ WDFIOTARGET IoTarget +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + UNREFERENCED_PARAMETER(IoTarget); + return STATUS_SUCCESS; +} + +#pragma code_seg("PAGE") +VOID +UsbHsDevice::EvtUsbHsTargetRemoveCanceled +( + _In_ WDFIOTARGET IoTarget +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + UNREFERENCED_PARAMETER(IoTarget); +} +#pragma code_seg("PAGE") +VOID +UsbHsDevice::EvtUsbHsTargetRemoveComplete +( + _In_ WDFIOTARGET IoTarget +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + UNREFERENCED_PARAMETER(IoTarget); +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::Init +( + _In_ IAdapterCommon * Adapter, + _In_ PUNICODE_STRING SymbolicLinkName +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + UsbHsDeviceNotificationWorkItemContext *wiCtx = NULL; + UsbHsDeviceNotificationReqContext *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 = eUsbHsStateInitializing; + 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_SpeakerVolumePropValues = NULL; + m_MicVolumePropValues = NULL; + m_SpeakerMutePropValues = NULL; + m_MicMutePropValues = NULL; + m_ulSpeakerMutePropValuesSize = 0; + m_ulMicMutePropValuesSize = 0; + + // Notification updates. + m_SpeakerVolumeLevel = 0; + m_MicVolumeLevel = 0; + m_SpeakerMute = 0; + m_MicMute = 0; + m_SpeakerStreamStatusLong = STATUS_SUCCESS; // USB stream is not open. + m_MicStreamStatusLong = STATUS_SUCCESS; // USB stream is not open. + + m_SpeakerStreamReq = NULL; + m_MicStreamReq = NULL; + m_SpeakerVolumeReq = NULL; + m_MicVolumeReq = NULL; + m_SpeakerMuteReq = NULL; + m_MicMuteReq = NULL; + + m_WorkItem = NULL; + m_ReqCollection = NULL; + + m_nSpeakerStreamsOpen = 0; + m_nMicStreamsOpen = 0; + + m_nSpeakerStreamsStart = 0; + m_nMicStreamsStart = 0; + + m_pSpeakerSupportedFormatsIntersection = NULL; + m_pMicSupportedFormatsIntersection = NULL; + + KeInitializeEvent(&m_SpeakerStreamStatusEvent, NotificationEvent, TRUE); + KeInitializeEvent(&m_MicStreamStatusEvent, NotificationEvent, TRUE); + + InitializeListHead(&m_ListEntry); + KeInitializeSpinLock(&m_Lock); + + RtlZeroMemory(&m_SymbolicLinkName, sizeof(m_SymbolicLinkName)); + + RtlZeroMemory(&m_SpeakerVolumeCallback, sizeof(m_SpeakerVolumeCallback)); + RtlZeroMemory(&m_MicVolumeCallback, sizeof(m_MicVolumeCallback)); + + RtlZeroMemory(&m_SpeakerMuteCallback, sizeof(m_SpeakerMuteCallback)); + RtlZeroMemory(&m_MicMuteCallback, sizeof(m_MicMuteCallback)); + + RtlZeroMemory(&m_SpeakerConnectionStatusCallback, sizeof(m_SpeakerConnectionStatusCallback)); + RtlZeroMemory(&m_MicConnectionStatusCallback, sizeof(m_MicConnectionStatusCallback)); + + RtlZeroMemory(&m_SpeakerFormatChangeCallback, sizeof(m_SpeakerFormatChangeCallback)); + RtlZeroMemory(&m_MicFormatChangeCallback, sizeof(m_MicFormatChangeCallback)); + + RtlZeroMemory(&m_SpeakerTransportResources, sizeof(USBHSDEVICE_EP_TRANSPORT_RESOURCES)); + RtlZeroMemory(&m_MicTransportResources , sizeof(USBHSDEVICE_EP_TRANSPORT_RESOURCES)); + + // + // Allocate a notification WDF work-item. + // + WDF_WORKITEM_CONFIG_INIT(&wiConfig, EvtUsbHsDeviceNotificationStatusWorkItem); + wiConfig.AutomaticSerialization = FALSE; + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, UsbHsDeviceNotificationWorkItemContext); + attributes.ParentObject = Adapter->GetWdfDevice(); + ntStatus = WdfWorkItemCreate( &wiConfig, + &attributes, + &m_WorkItem); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfWorkItemCreate failed: 0x%x", ntStatus)), + Done); + + wiCtx = GetUsbHsDeviceNotificationWorkItemContext(m_WorkItem); + wiCtx->UsbHsDevice = this; // weak ref. + + // + // Allocate a collection to hold notification requests for the notification work-item. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfCollectionCreate( + &attributes, + &m_ReqCollection); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfCollectionCreate failed: 0x%x", ntStatus)), + Done); + + // + // Open the target interface. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfIoTargetCreate(m_Adapter->GetWdfDevice(), + &attributes, + &m_WdfIoTarget); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfIoTargetCreate failed: 0x%x", ntStatus)), + Done); + + WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME( + &openParams, + SymbolicLinkName, + STANDARD_RIGHTS_ALL); + + openParams.EvtIoTargetQueryRemove = EvtUsbHsTargetQueryRemove; + openParams.EvtIoTargetRemoveCanceled = EvtUsbHsTargetRemoveCanceled; + openParams.EvtIoTargetRemoveComplete = EvtUsbHsTargetRemoveComplete; + + ntStatus = WdfIoTargetOpen(m_WdfIoTarget, &openParams); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfIoTargetOpen(%wZ) failed: 0x%x", SymbolicLinkName, ntStatus)), + Done); + + PDEVICE_OBJECT pdo = WdfIoTargetWdmGetTargetPhysicalDevice(m_WdfIoTarget); + m_Adapter->AddDeviceAsPowerDependency(pdo); + + // + // Make a copy of the symbolic link name. + // + m_SymbolicLinkName.MaximumLength = SymbolicLinkName->MaximumLength; + m_SymbolicLinkName.Length = SymbolicLinkName->Length; + m_SymbolicLinkName.Buffer = (PWSTR) ExAllocatePoolWithTag(NonPagedPoolNx, + SymbolicLinkName->MaximumLength, + MINADAPTER_POOLTAG); + if (m_SymbolicLinkName.Buffer == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: ExAllocatePoolWithTag failed, out of memory")), + Done); + + RtlCopyUnicodeString(&m_SymbolicLinkName, SymbolicLinkName); + + // + // Allocate the WDF requests for status notifications. + // + + // + // IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE + // SPEAKER + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, UsbHsDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_SpeakerVolumeReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate(Speaker-Volume) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetUsbHsDeviceNotificationReqContext(m_SpeakerVolumeReq); + reqCtx->UsbHsDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.Volume), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.Volume), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE + // MIC + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, UsbHsDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_MicVolumeReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate(Mic-Volume) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetUsbHsDeviceNotificationReqContext(m_MicVolumeReq); + reqCtx->UsbHsDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.Volume), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.Volume), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE + // SPEAKER + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, UsbHsDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_SpeakerMuteReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate(Speaker-Mute) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetUsbHsDeviceNotificationReqContext(m_SpeakerMuteReq); + reqCtx->UsbHsDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.Mute), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.Mute), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE + // MIC + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, UsbHsDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_MicMuteReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate(Mic-Mute) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetUsbHsDeviceNotificationReqContext(m_MicMuteReq); + reqCtx->UsbHsDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.Mute), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.Mute), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_SBAUD_GET_STREAM_STATUS_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, UsbHsDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_SpeakerStreamReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate(Stream-Status) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetUsbHsDeviceNotificationReqContext(m_SpeakerStreamReq); + reqCtx->UsbHsDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.streamStatus), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.streamStatus), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_SBAUD_GET_STREAM_STATUS_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, UsbHsDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_MicStreamReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate(Stream-Status) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetUsbHsDeviceNotificationReqContext(m_MicStreamReq); + reqCtx->UsbHsDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.streamStatus), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.streamStatus), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // Create wave and filter names unique to this USB device + // + ntStatus = CreateFilterNames(SymbolicLinkName); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: Creating unqie filter names for the USB device failed, 0x%x", ntStatus)), + Done); + + // + // This remote device is now in running state. No need to use interlock operations + // b/c at this time this is the only thread accessing this info. + // + m_State = eUsbHsStateRunning; + + // + // Init successful. + // + ntStatus = STATUS_SUCCESS; + +Done: + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +UsbHsDevice::~UsbHsDevice +( + void +) +/*++ + +Routine Description: + + Destructor for UsbHsDevice. + +Arguments: + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + ASSERT(m_State != eUsbHsStateRunning); + ASSERT(IsListEmpty(&m_ListEntry)); + + // + // Release ref to remote stack. + // + if (m_WdfIoTarget != NULL) + { + PDEVICE_OBJECT pdo = WdfIoTargetWdmGetTargetPhysicalDevice(m_WdfIoTarget); + m_Adapter->RemoveDeviceAsPowerDependency(pdo); + + WdfObjectDelete(m_WdfIoTarget); + m_WdfIoTarget = NULL; + } + + // + // Free symbolic links. + // + if (m_SymbolicLinkName.Buffer != NULL) + { + ExFreePoolWithTag(m_SymbolicLinkName.Buffer, MINADAPTER_POOLTAG); + RtlZeroMemory(&m_SymbolicLinkName, sizeof(m_SymbolicLinkName)); + } + + DeleteCustomEndpointMinipair(m_SpeakerMiniports); + m_SpeakerMiniports = NULL; + + DeleteCustomEndpointMinipair(m_MicMiniports); + m_MicMiniports = NULL; + + if (m_Descriptor != NULL) + { + ExFreePoolWithTag(m_Descriptor, MINADAPTER_POOLTAG); + m_Descriptor = NULL; + } + + SAFE_DELETE_PTR_WITH_TAG(m_pSpeakerDescriptor, USBSIDEBANDTEST_POOLTAG02); + SAFE_DELETE_PTR_WITH_TAG(m_pMicDescriptor, USBSIDEBANDTEST_POOLTAG02); + + SAFE_DELETE_PTR_WITH_TAG(m_pSpeakerSupportedFormatsIntersection, USBSIDEBANDTEST_POOLTAG03); + SAFE_DELETE_PTR_WITH_TAG(m_pMicSupportedFormatsIntersection, USBSIDEBANDTEST_POOLTAG03); + + FreeTransportResources(&m_SpeakerTransportResources); + FreeTransportResources(&m_MicTransportResources); + + if (m_SpeakerVolumePropValues != NULL) + { + ExFreePoolWithTag(m_SpeakerVolumePropValues, USBSIDEBANDTEST_POOLTAG02); + m_SpeakerVolumePropValues = NULL; + } + + if (m_MicVolumePropValues != NULL) + { + ExFreePoolWithTag(m_MicVolumePropValues, USBSIDEBANDTEST_POOLTAG02); + m_MicVolumePropValues = NULL; + } + + if (m_SpeakerMutePropValues != NULL) + { + ExFreePoolWithTag(m_SpeakerMutePropValues, USBSIDEBANDTEST_POOLTAG06); + m_SpeakerMutePropValues = NULL; + m_ulSpeakerMutePropValuesSize = 0; + } + + if (m_MicMutePropValues != NULL) + { + ExFreePoolWithTag(m_MicMutePropValues, USBSIDEBANDTEST_POOLTAG06); + m_MicMutePropValues = NULL; + m_ulMicMutePropValuesSize = 0; + } + + // + // Free Irps. + // + if (m_SpeakerVolumeReq != NULL) + { + UsbHsDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetUsbHsDeviceNotificationReqContext(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) + { + UsbHsDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetUsbHsDeviceNotificationReqContext(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_SpeakerMuteReq != NULL) + { + UsbHsDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetUsbHsDeviceNotificationReqContext(m_SpeakerMuteReq); + if (ctx->MemIn != NULL) + { + WdfObjectDelete(ctx->MemIn); + ctx->MemIn = NULL; + } + + if (ctx->MemOut != NULL) + { + WdfObjectDelete(ctx->MemOut); + ctx->MemOut = NULL; + } + + // Delete the request. + WdfObjectDelete(m_SpeakerMuteReq); + m_SpeakerMuteReq = NULL; + } + + if (m_MicMuteReq != NULL) + { + UsbHsDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetUsbHsDeviceNotificationReqContext(m_MicMuteReq); + 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_MicMuteReq); + m_MicMuteReq = NULL; + } + + if (m_SpeakerStreamReq != NULL) + { + UsbHsDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetUsbHsDeviceNotificationReqContext(m_SpeakerStreamReq); + if (ctx->MemIn != NULL) + { + WdfObjectDelete(ctx->MemIn); + ctx->MemIn = NULL; + } + + if (ctx->MemOut != NULL) + { + WdfObjectDelete(ctx->MemOut); + ctx->MemOut = NULL; + } + + // Delete the request. + WdfObjectDelete(m_SpeakerStreamReq); + m_SpeakerStreamReq = NULL; + } + + if (m_MicStreamReq != NULL) + { + UsbHsDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetUsbHsDeviceNotificationReqContext(m_MicStreamReq); + 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_MicStreamReq); + m_MicStreamReq = 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_nSpeakerStreamsOpen == 0); + ASSERT(m_nMicStreamsOpen == 0); + + ASSERT(m_nSpeakerStreamsStart == 0); + ASSERT(m_nMicStreamsStart == 0); + + ASSERT(m_SpeakerVolumeCallback.Handler == NULL); + ASSERT(m_MicVolumeCallback.Handler == NULL); + + ASSERT(m_SpeakerMuteCallback.Handler == NULL); + ASSERT(m_MicMuteCallback.Handler == NULL); + + ASSERT(m_SpeakerConnectionStatusCallback.Handler == NULL); + ASSERT(m_MicConnectionStatusCallback.Handler == NULL); + + ASSERT(m_SpeakerFormatChangeCallback.Handler == NULL); + ASSERT(m_MicFormatChangeCallback.Handler == NULL); +} // ~CAdapterCommon + +// +// ISidebandDeviceCommon implementation. +// + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +UsbHsDevice::IsVolumeSupported +( + _In_ eDeviceType deviceType +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eUsbHsSpeakerDevice) + { + return m_pSpeakerDescriptor->Capabilities.Volume; + } + else if (deviceType == eUsbHsMicDevice) + { + return m_pMicDescriptor->Capabilities.Volume; + } + + return FALSE; +} + +//============================================================================= +#pragma code_seg("PAGE") +PVOID +UsbHsDevice::GetVolumeSettings +( + _In_ eDeviceType deviceType, + _Out_ PULONG Size +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + ASSERT(Size != NULL); + + if (deviceType == eUsbHsSpeakerDevice) + { + *Size = m_pSpeakerDescriptor->VolumePropertyValuesSize; + return m_SpeakerVolumePropValues; + } + else if (deviceType == eUsbHsMicDevice) + { + *Size = m_pMicDescriptor->VolumePropertyValuesSize; + return m_MicVolumePropValues; + } + + return NULL; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::GetVolume +( + _In_ eDeviceType deviceType, + _In_ LONG Channel, + _Out_ LONG *pVolume +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Channel); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS status = STATUS_SUCCESS; + + ASSERT(pVolume); + + if (eUsbHsSpeakerDevice == deviceType) + { + status = GetUsbHsSpeakerVolume(Channel, pVolume); + IF_FAILED_ACTION_JUMP( + status, + DPF(D_ERROR, ("Start: GetUsbHsSpeakerVolume: failed, 0x%x", status)), + Done); + + InterlockedExchange(&m_SpeakerVolumeLevel, *pVolume); + } + else if (eUsbHsMicDevice == deviceType) + { + status = GetUsbHsMicVolume(Channel, pVolume); + IF_FAILED_ACTION_JUMP( + status, + DPF(D_ERROR, ("Start: GetUsbHsMicVolume: failed, 0x%x", status)), + Done); + + InterlockedExchange(&m_MicVolumeLevel, *pVolume); + } + else + { + status = STATUS_INVALID_PARAMETER; + } + +Done: + return status; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::SetVolume +( + _In_ eDeviceType deviceType, + _In_ LONG Channel, + _In_ LONG Volume +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS status = STATUS_SUCCESS; + + if (eUsbHsSpeakerDevice == deviceType) + { + status = SetUsbHsSpeakerVolume(Channel, Volume); + } + else if(eUsbHsMicDevice == deviceType) + { + status = SetUsbHsMicVolume(Channel, Volume); + } + else + { + status = STATUS_INVALID_PARAMETER; + } + + return status; +} + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +UsbHsDevice::IsMuteSupported +( + _In_ eDeviceType deviceType +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eUsbHsSpeakerDevice) + { + return m_pSpeakerDescriptor->Capabilities.Mute; + } + else if (deviceType == eUsbHsMicDevice) + { + return m_pMicDescriptor->Capabilities.Mute; + } + + return FALSE; +} + +//============================================================================= +#pragma code_seg("PAGE") +PVOID +UsbHsDevice::GetMuteSettings +( + _In_ eDeviceType deviceType, + _Out_ PULONG Size +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + ASSERT(Size != NULL); + + if (deviceType == eUsbHsSpeakerDevice) + { + *Size = m_ulSpeakerMutePropValuesSize; + return m_SpeakerMutePropValues; + } + else if (deviceType == eUsbHsMicDevice) + { + *Size = m_ulMicMutePropValuesSize; + return m_MicMutePropValues; + } + + return NULL; +} + +//============================================================================= +#pragma code_seg("PAGE") +LONG +UsbHsDevice::GetMute +( + _In_ eDeviceType deviceType, + _In_ LONG Channel +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Channel); + DPF_ENTER(("[%!FUNC!]")); + + LONG ulMute = 0; + NTSTATUS status = STATUS_SUCCESS; + + if (eUsbHsSpeakerDevice == deviceType) + { + status = GetUsbHsSpeakerMute(Channel, &ulMute); + } + else if (eUsbHsMicDevice == deviceType) + { + status = GetUsbHsMicVolume(Channel, &ulMute); + } + else + { + status = STATUS_INVALID_PARAMETER; + } + + if (NT_SUCCESS(status)) + { + return ulMute; + } + + return 0; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::SetMute +( + _In_ eDeviceType deviceType, + _In_ LONG Channel, + _In_ LONG Mute +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS status = STATUS_SUCCESS; + + if (eUsbHsSpeakerDevice == deviceType) + { + status = SetUsbHsSpeakerMute(Channel, Mute); + } + else if(eUsbHsMicDevice == deviceType) + { + status = SetUsbHsMicMute(Channel, Mute); + } + else + { + status = STATUS_INVALID_PARAMETER; + } + + return status; +} + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +UsbHsDevice::GetConnectionStatus() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + return TRUE; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::Connect() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + return STATUS_DEVICE_FEATURE_NOT_SUPPORTED; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::Disconnect() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + return STATUS_DEVICE_FEATURE_NOT_SUPPORTED; +} + +//============================================================================= +#pragma code_seg() +BOOL +UsbHsDevice::GetStreamStatus(_In_ eDeviceType deviceType) +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (deviceType == eUsbHsSpeakerDevice) + { + ntStatus = (NTSTATUS)InterlockedCompareExchange(&m_SpeakerStreamStatusLong, 0, 0); + } + else if (deviceType == eUsbHsMicDevice) + { + ntStatus = (NTSTATUS)InterlockedCompareExchange(&m_MicStreamStatusLong, 0, 0); + } + + return NT_SUCCESS(ntStatus) ? TRUE : FALSE; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::SpeakerStreamOpen() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + NTSTATUS ntStatus = STATUS_SUCCESS; + LONG nStreams = 0; + + ASSERT(m_nSpeakerStreamsOpen >= 0); + + nStreams = InterlockedIncrement(&m_nSpeakerStreamsOpen); + if (nStreams == 1) + { + BOOLEAN streamOpen = FALSE; + + ntStatus = SetUsbHsStreamOpen(m_SpeakerEpIndex, + &UsbHsSpeakerSupportedDeviceFormats[m_SpeakerSelectedFormat], + m_pSpeakerDescriptor); + + if (NT_SUCCESS(ntStatus)) + { + streamOpen = TRUE; + m_SpeakerStreamStatus = STATUS_SUCCESS; + ntStatus = EnableUsbHsSpeakerStreamStatusNotification(); + } + + // + // Cleanup if any error. + // + if (!NT_SUCCESS(ntStatus)) + { + nStreams = InterlockedDecrement(&m_nSpeakerStreamsOpen); + ASSERT(nStreams == 0); + UNREFERENCED_VAR(nStreams); + + if (streamOpen) + { + SetUsbHsStreamClose(m_SpeakerEpIndex); + } + + m_SpeakerStreamStatus = STATUS_INVALID_DEVICE_STATE; + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::MicStreamOpen() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + NTSTATUS ntStatus = STATUS_SUCCESS; + LONG nStreams = 0; + + ASSERT(m_nMicStreamsOpen >= 0); + + nStreams = InterlockedIncrement(&m_nMicStreamsOpen); + if (nStreams == 1) + { + BOOLEAN streamOpen = FALSE; + + ntStatus = SetUsbHsStreamOpen(m_MicEpIndex, + &UsbHsMicSupportedDeviceFormats[m_MicSelectedFormat], + m_pMicDescriptor); + + if (NT_SUCCESS(ntStatus)) + { + streamOpen = TRUE; + m_MicStreamStatus = STATUS_SUCCESS; + ntStatus = EnableUsbHsMicStreamStatusNotification(); + } + + // + // Cleanup if any error. + // + if (!NT_SUCCESS(ntStatus)) + { + nStreams = InterlockedDecrement(&m_nMicStreamsOpen); + ASSERT(nStreams == 0); + UNREFERENCED_VAR(nStreams); + + if (streamOpen) + { + SetUsbHsStreamClose(m_MicEpIndex); + } + + m_MicStreamStatus = STATUS_INVALID_DEVICE_STATE; + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::StreamOpen(_In_ eDeviceType deviceType) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eUsbHsSpeakerDevice) + { + return SpeakerStreamOpen(); + } + else if (deviceType == eUsbHsMicDevice) + { + return MicStreamOpen(); + } + + return STATUS_INVALID_PARAMETER; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::SpeakerStreamStart() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + NTSTATUS ntStatus = STATUS_SUCCESS; + LONG nStreams = 0; + + ASSERT(m_nSpeakerStreamsStart >= 0); + + nStreams = InterlockedIncrement(&m_nSpeakerStreamsStart); + if (nStreams == 1) + { + BOOLEAN streamStart = FALSE; + + ntStatus = SetTransportResources(m_pSpeakerDescriptor, m_SpeakerEpIndex); + + if (NT_SUCCESS(ntStatus)) + { + ntStatus = SetUsbHsStreamStart(m_SpeakerEpIndex); + } + + if (NT_SUCCESS(ntStatus)) + { + streamStart = TRUE; + } + + if (NT_SUCCESS(ntStatus)) + { + ntStatus = GetTransportResources(m_SpeakerEpIndex, m_pSpeakerDescriptor, &m_SpeakerTransportResources); + } + + // + // Cleanup if any error. + // + if (!NT_SUCCESS(ntStatus)) + { + nStreams = InterlockedDecrement(&m_nSpeakerStreamsStart); + ASSERT(nStreams == 0); + UNREFERENCED_VAR(nStreams); + + if (streamStart) + { + SetUsbHsStreamSuspend(m_SpeakerEpIndex); + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::MicStreamStart() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + NTSTATUS ntStatus = STATUS_SUCCESS; + LONG nStreams = 0; + + ASSERT(m_nMicStreamsStart >= 0); + + nStreams = InterlockedIncrement(&m_nMicStreamsStart); + if (nStreams == 1) + { + BOOLEAN streamStart = FALSE; + + ntStatus = SetTransportResources(m_pMicDescriptor, m_MicEpIndex); + + if (NT_SUCCESS(ntStatus)) + { + ntStatus = SetUsbHsStreamStart(m_MicEpIndex); + } + + if (NT_SUCCESS(ntStatus)) + { + streamStart = TRUE; + } + + if (NT_SUCCESS(ntStatus)) + { + ntStatus = GetTransportResources(m_MicEpIndex, m_pMicDescriptor, &m_MicTransportResources); + } + + // + // Cleanup if any error. + // + if (!NT_SUCCESS(ntStatus)) + { + nStreams = InterlockedDecrement(&m_nMicStreamsStart); + ASSERT(nStreams == 0); + UNREFERENCED_VAR(nStreams); + + if (streamStart) + { + SetUsbHsStreamSuspend(m_MicEpIndex); + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::StreamStart(_In_ eDeviceType deviceType) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eUsbHsSpeakerDevice) + { + return SpeakerStreamStart(); + } + else if (deviceType == eUsbHsMicDevice) + { + return MicStreamStart(); + } + + return STATUS_INVALID_PARAMETER; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::SpeakerStreamSuspend() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + LONG nStreams = 0; + + ASSERT(m_nSpeakerStreamsStart > 0); + + nStreams = InterlockedDecrement(&m_nSpeakerStreamsStart); + if (nStreams == 0) + { + ntStatus = SetUsbHsStreamSuspend(m_SpeakerEpIndex); + + FreeTransportResources(&m_SpeakerTransportResources); + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::MicStreamSuspend() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + LONG nStreams = 0; + + ASSERT(m_nMicStreamsStart > 0); + + nStreams = InterlockedDecrement(&m_nMicStreamsStart); + if (nStreams == 0) + { + ntStatus = SetUsbHsStreamSuspend(m_MicEpIndex); + + FreeTransportResources(&m_MicTransportResources); + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::StreamSuspend(_In_ eDeviceType deviceType) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eUsbHsSpeakerDevice) + { + return SpeakerStreamSuspend(); + } + else if (deviceType == eUsbHsMicDevice) + { + return MicStreamSuspend(); + } + + return STATUS_INVALID_PARAMETER; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::SpeakerStreamClose() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + LONG nStreams = 0; + + ASSERT(m_nSpeakerStreamsOpen > 0); + + nStreams = InterlockedDecrement(&m_nSpeakerStreamsOpen); + if (nStreams == 0) + { + ntStatus = SetUsbHsStreamClose(m_SpeakerEpIndex); + + StopUsbHsSpeakerStreamStatusNotification(); + + m_SpeakerStreamStatus = STATUS_INVALID_DEVICE_STATE; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::MicStreamClose() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + LONG nStreams = 0; + + ASSERT(m_nMicStreamsOpen > 0); + + nStreams = InterlockedDecrement(&m_nMicStreamsOpen); + if (nStreams == 0) + { + ntStatus = SetUsbHsStreamClose(m_MicEpIndex); + + StopUsbHsMicStreamStatusNotification(); + + m_MicStreamStatus = STATUS_INVALID_DEVICE_STATE; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::StreamClose(_In_ eDeviceType deviceType) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eUsbHsSpeakerDevice) + { + return SpeakerStreamClose(); + } + else if (deviceType == eUsbHsMicDevice) + { + return MicStreamClose(); + } + + return STATUS_INVALID_PARAMETER; +} + +//============================================================================= +#pragma code_seg("PAGE") +GUID +UsbHsDevice::GetContainerId(_In_ eDeviceType deviceType) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eUsbHsSpeakerDevice) + { + return m_pSpeakerDescriptor->ContainerId; + } + else if (deviceType == eUsbHsMicDevice) + { + return m_pMicDescriptor->ContainerId; + } + else + return { 0 }; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +UsbHsDevice::SetVolumeHandler +( + _In_ eDeviceType deviceType, + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eUsbHsSpeakerDevice) + { + ASSERT(EventHandler == NULL || m_SpeakerVolumeCallback.Handler == NULL); + + m_SpeakerVolumeCallback.Handler = EventHandler; // weak ref. + m_SpeakerVolumeCallback.Context = EventHandlerContext; + } + else if (deviceType == eUsbHsMicDevice) + { + ASSERT(EventHandler == NULL || m_MicVolumeCallback.Handler == NULL); + + m_MicVolumeCallback.Handler = EventHandler; // weak ref. + m_MicVolumeCallback.Context = EventHandlerContext; + } +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +UsbHsDevice::SetMuteHandler +( + _In_ eDeviceType deviceType, + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eUsbHsSpeakerDevice) + { + ASSERT(EventHandler == NULL || m_SpeakerMuteCallback.Handler == NULL); + + m_SpeakerMuteCallback.Handler = EventHandler; // weak ref. + m_SpeakerMuteCallback.Context = EventHandlerContext; + } + else if (deviceType == eUsbHsMicDevice) + { + ASSERT(EventHandler == NULL || m_MicMuteCallback.Handler == NULL); + + m_MicMuteCallback.Handler = EventHandler; // weak ref. + m_MicMuteCallback.Context = EventHandlerContext; + } +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +UsbHsDevice::SetConnectionStatusHandler +( + _In_ eDeviceType deviceType, + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eUsbHsSpeakerDevice) + { + ASSERT(EventHandler == NULL || m_SpeakerConnectionStatusCallback.Handler == NULL); + + m_SpeakerConnectionStatusCallback.Handler = EventHandler; // weak ref. + m_SpeakerConnectionStatusCallback.Context = EventHandlerContext; + } + else if (deviceType == eUsbHsMicDevice) + { + ASSERT(EventHandler == NULL || m_MicConnectionStatusCallback.Handler == NULL); + + m_MicConnectionStatusCallback.Handler = EventHandler; // weak ref. + m_MicConnectionStatusCallback.Context = EventHandlerContext; + } + +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +UsbHsDevice::SetFormatChangeHandler +( + _In_ eDeviceType deviceType, + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + if (deviceType == eUsbHsSpeakerDevice) + { + ASSERT(EventHandler == NULL || m_SpeakerFormatChangeCallback.Handler == NULL); + + m_SpeakerFormatChangeCallback.Handler = EventHandler; // weak ref. + m_SpeakerFormatChangeCallback.Context = EventHandlerContext; + } + else if (deviceType == eUsbHsMicDevice) + { + ASSERT(EventHandler == NULL || m_MicFormatChangeCallback.Handler == NULL); + + m_MicFormatChangeCallback.Handler = EventHandler; // weak ref. + m_MicFormatChangeCallback.Context = EventHandlerContext; + } + +} + +//============================================================================= +#pragma code_seg("PAGE") +PPIN_DEVICE_FORMATS_AND_MODES +UsbHsDevice::GetFormatsAndModes +( + _In_ eDeviceType deviceType +) +{ + UNREFERENCED_PARAMETER(deviceType); + PPIN_DEVICE_FORMATS_AND_MODES pFormatsAndModes = NULL; + return pFormatsAndModes; +} + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +UsbHsDevice::IsNRECSupported() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + return FALSE; +} + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +UsbHsDevice::GetNRECDisableStatus() +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + // Return TRUE if HF wants to disable the NREC on the AG. + return FALSE; +} + +// +// Helper functions. +// + +//============================================================================= +#pragma code_seg() +NTSTATUS +UsbHsDevice::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 USB Headset + device. + +--*/ +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BOOLEAN fSent = FALSE; + + // + // Format and send the request. + // + ntStatus = WdfIoTargetFormatRequestForIoctl( + m_WdfIoTarget, + Request, + IoControlCode, + MemIn, + NULL, + MemOut, + NULL); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfIoTargetFormatRequestForIoctl(0x%x) failed, 0x%x", IoControlCode, ntStatus)), + Done); + + WdfRequestSetCompletionRoutine( + Request, + CompletionRoutine, + Context); + + fSent = WdfRequestSend(Request, m_WdfIoTarget, NULL); // no options. + if (fSent == FALSE) + { + ntStatus = WdfRequestGetStatus(Request); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + } + + DPF(D_ERROR, ("%!FUNC!: WdfRequestSend(0x%x) failed, 0x%x", IoControlCode, ntStatus)); + goto Done; + } + + // + // All Done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::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 USB headset + device. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PWDF_MEMORY_DESCRIPTOR memInPtr = NULL; + PWDF_MEMORY_DESCRIPTOR memOutPtr = NULL; + WDF_MEMORY_DESCRIPTOR memIn; + WDF_MEMORY_DESCRIPTOR memOut; + WDF_REQUEST_SEND_OPTIONS reqOpts; + + // + // Format and send the request. + // + if (InLength) + { + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memIn, Buffer, InLength); + memInPtr = &memIn; + } + + if (OutLength) + { + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memOut, Buffer, OutLength); + memOutPtr = &memOut; + } + + WDF_REQUEST_SEND_OPTIONS_INIT( + &reqOpts, + WDF_REQUEST_SEND_OPTION_TIMEOUT | + WDF_REQUEST_SEND_OPTION_SYNCHRONOUS); + + reqOpts.Timeout = WDF_REL_TIMEOUT_IN_SEC(USB_SIDEBAND_SYNC_REQ_TIMEOUT_IN_SEC); + + ntStatus = WdfIoTargetSendIoctlSynchronously( + m_WdfIoTarget, + Request, + IoControlCode, + memInPtr, + memOutPtr, + &reqOpts, + NULL); // bytes returned. + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_VERBOSE, ("%!FUNC!: WdfIoTargetSendIoctlSynchronously(0x%x) failed, 0x%x", IoControlCode, ntStatus)), + Done); + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::SendIoCtrlSynchronously +( + _In_opt_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_ ULONG InLength, + _In_ ULONG OutLength, + _When_(InLength > 0, _In_) + _When_(InLength == 0, _In_opt_) + PVOID InBuffer, + _When_(OutLength > 0, _In_) + _When_(OutLength == 0, _In_opt_) + PVOID OutBuffer +) +/*++ + +Routine Description: + + This function inits and synchronously sends an I/O Ctrl request to the USB headset + device. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PWDF_MEMORY_DESCRIPTOR memInPtr = NULL; + PWDF_MEMORY_DESCRIPTOR memOutPtr = NULL; + WDF_MEMORY_DESCRIPTOR memIn; + WDF_MEMORY_DESCRIPTOR memOut; + WDF_REQUEST_SEND_OPTIONS reqOpts; + + // + // Format and send the request. + // + if (InLength) + { + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memIn, InBuffer, InLength); + memInPtr = &memIn; + } + + if (OutLength) + { + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memOut, OutBuffer, OutLength); + memOutPtr = &memOut; + } + + WDF_REQUEST_SEND_OPTIONS_INIT( + &reqOpts, + WDF_REQUEST_SEND_OPTION_TIMEOUT | + WDF_REQUEST_SEND_OPTION_SYNCHRONOUS); + + reqOpts.Timeout = WDF_REL_TIMEOUT_IN_SEC(USB_SIDEBAND_SYNC_REQ_TIMEOUT_IN_SEC); + + ntStatus = WdfIoTargetSendIoctlSynchronously( + m_WdfIoTarget, + Request, + IoControlCode, + memInPtr, + memOutPtr, + &reqOpts, + NULL); // bytes returned. + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_VERBOSE, ("%!FUNC!: WdfIoTargetSendIoctlSynchronously(0x%x) failed, 0x%x", IoControlCode, ntStatus)), + Done); + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +VOID +UsbHsDevice::EvtUsbHsDeviceNotificationStatusWorkItem +( + _In_ WDFWORKITEM WorkItem +) +/*++ + +Routine Description: + + The function processes status notification updates. + +Arguments: + + WorkItem - WDF work-item object. + +--*/ +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + UsbHsDevice * This; + KIRQL oldIrql; + + if (WorkItem == NULL) + { + return; + } + + This = GetUsbHsDeviceNotificationWorkItemContext(WorkItem)->UsbHsDevice; + ASSERT(This != NULL); + + for (;;) + { + BOOL resend = TRUE; + WDFREQUEST req = NULL; + UsbHsDeviceNotificationReqContext * 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 = GetUsbHsDeviceNotificationReqContext(req); + ASSERT(reqCtx != NULL); + + // + // Handle this notification. + // + if (NT_SUCCESS(params.IoStatus.Status)) + { + switch(params.Parameters.Ioctl.IoControlCode) + { + case IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE: + { + LONG oldVolume; + + // Prepare for request resent + reqCtx->Buffer.Volume.Immediate = FALSE; + + if (reqCtx->Buffer.Volume.EpIndex == This->m_SpeakerEpIndex) + { + oldVolume = InterlockedExchange(&This->m_SpeakerVolumeLevel, reqCtx->Buffer.Volume.Value); + if (reqCtx->Buffer.Volume.Value != oldVolume) + { + // Notify audio miniport about this change. + if (This->m_SpeakerVolumeCallback.Handler != NULL) + { + This->m_SpeakerVolumeCallback.Handler( + This->m_SpeakerVolumeCallback.Context); + } + } + } + else if (reqCtx->Buffer.Volume.EpIndex == This->m_MicEpIndex) + { + oldVolume = InterlockedExchange(&This->m_MicVolumeLevel, reqCtx->Buffer.Volume.Value); + if (reqCtx->Buffer.Volume.Value != oldVolume) + { + // Notify audio miniport about this change. + if (This->m_MicVolumeCallback.Handler != NULL) + { + This->m_MicVolumeCallback.Handler( + This->m_MicVolumeCallback.Context); + } + } + } + + } + break; + + case IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE: + { + LONG oldMute; + + // Prepare for request resent + reqCtx->Buffer.Mute.Immediate = FALSE; + + if (reqCtx->Buffer.Mute.EpIndex == This->m_SpeakerEpIndex) + { + oldMute = InterlockedExchange(&This->m_SpeakerMute, reqCtx->Buffer.Mute.Value); + if (reqCtx->Buffer.Mute.Value != oldMute) + { + // Notify audio miniport about this change. + if (This->m_SpeakerMuteCallback.Handler != NULL) + { + This->m_SpeakerMuteCallback.Handler( + This->m_SpeakerMuteCallback.Context); + } + } + } + else if (reqCtx->Buffer.Mute.EpIndex == This->m_MicEpIndex) + { + oldMute = InterlockedExchange(&This->m_MicMute, reqCtx->Buffer.Mute.Value); + if (reqCtx->Buffer.Mute.Value != oldMute) + { + // Notify audio miniport about this change. + if (This->m_MicMuteCallback.Handler != NULL) + { + This->m_MicMuteCallback.Handler( + This->m_MicMuteCallback.Context); + } + } + } + + } + break; + + default: + // This should never happen. + resend = FALSE; + DPF(D_ERROR, ("%!FUNC!: invalid request ctrl 0x%x", + params.Parameters.Ioctl.IoControlCode)); + break; + } + } + + if (resend) + { + WDF_REQUEST_REUSE_PARAMS reuseParams; + + WDF_REQUEST_REUSE_PARAMS_INIT( + &reuseParams, + WDF_REQUEST_REUSE_NO_FLAGS, + STATUS_SUCCESS); + + ntStatus = WdfRequestReuse(req, &reuseParams); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus)); + break; + } + + // Resend status notification request. + ntStatus = This->SendIoCtrlAsynchronously( + req, + params.Parameters.Ioctl.IoControlCode, + reqCtx->MemIn, + reqCtx->MemOut, + EvtUsbHsDeviceNotificationStatusCompletion, + This); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlAsynchronously" + "(0x%x) failed, 0x%x", + params.Parameters.Ioctl.IoControlCode, ntStatus)); + break; + } + } + } +} + +//============================================================================= +#pragma code_seg() +void +UsbHsDevice::EvtUsbHsDeviceNotificationStatusCompletion +( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, + _In_ WDFCONTEXT Context +) +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + UsbHsDeviceNotificationReqContext *ctx = NULL; + UsbHsDevice *This = NULL; + KIRQL oldIrql; + + UNREFERENCED_PARAMETER(Target); + UNREFERENCED_PARAMETER(Context); + + ctx = GetUsbHsDeviceNotificationReqContext(Request); + This = ctx->UsbHsDevice; + ASSERT(This != NULL); + + ntStatus = Params->IoStatus.Status; + if (ntStatus == STATUS_CANCELLED) + { + // USB device is shutting down. Do not re-send this request. + goto Done; + } + + // + // If something is wrong with the USB interface, do not loop forever. + // + if (!NT_SUCCESS(ntStatus)) + { + if (++ctx->Errors > USB_SIDEBAND_NOTIFICATION_MAX_ERROR_COUNT) + { + // Too many errors. Do not re-send this request. + goto Done; + } + } + else + { + // reset the # of errors. + ctx->Errors = 0; + } + + // + // Let the work-item thread process this request. + // + KeAcquireSpinLock(&This->m_Lock, &oldIrql); + + ntStatus = WdfCollectionAdd(This->m_ReqCollection, Request); + if (NT_SUCCESS(ntStatus)) + { + WdfWorkItemEnqueue(This->m_WorkItem); + } + + KeReleaseSpinLock(&This->m_Lock, oldIrql); + +Done:; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::GetUsbHsDescriptor +( + _Out_ PSIDEBANDAUDIO_DEVICE_DESCRIPTOR *Descriptor +) +/*++ + +Routine Description: + + This function synchronously gets the remote USB Headset Device descriptor. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + WDFREQUEST req = NULL; + PSIDEBANDAUDIO_DEVICE_DESCRIPTOR descriptor = NULL; + ULONG length = 0; + ULONG_PTR information = 0; + WDF_REQUEST_REUSE_PARAMS reuseParams; + WDF_OBJECT_ATTRIBUTES attributes; + + *Descriptor = NULL; + + // + // Allocate and format a WDF request. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &req); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate failed, 0x%x", ntStatus)), + Done); + + // + // Get the size of the buffer. + // + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_SBAUD_GET_DEVICE_DESCRIPTOR, + NULL, + NULL, + NULL); + + if (ntStatus != STATUS_BUFFER_TOO_SMALL) + { + if (NT_SUCCESS(ntStatus)) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + } + + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_DEVICE_DESCRIPTOR): failed, 0x%x", ntStatus)); + goto Done; + } + + ntStatus = STATUS_SUCCESS; + + information = WdfRequestGetInformation(req); + if (information == 0 || information > ULONG_MAX) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + DPF(D_ERROR, ("%!FUNC!: IOCTL_SBAUD_GET_DEVICE_DESCRIPTOR buffer too big (%Id): 0x%x", information, ntStatus)); + goto Done; + } + + length = (ULONG)information; + + // + // Allocate memory needed to hold the info. + // + descriptor = (PSIDEBANDAUDIO_DEVICE_DESCRIPTOR) ExAllocatePoolWithTag(NonPagedPoolNx, length, MINADAPTER_POOLTAG); + if (descriptor == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: ExAllocatePoolWithTag failed, out of memory")), + Done); + + // + // Get the USB Headset descriptor. + // + WDF_REQUEST_REUSE_PARAMS_INIT( + &reuseParams, + WDF_REQUEST_REUSE_NO_FLAGS, + STATUS_SUCCESS); + + ntStatus = WdfRequestReuse(req, &reuseParams); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus)), + Done); + + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_SBAUD_GET_DEVICE_DESCRIPTOR, + NULL, + length, + descriptor); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_DEVICE_DESCRIPTOR) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *Descriptor = descriptor; + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + if (descriptor != NULL) + { + ExFreePoolWithTag(descriptor, MINADAPTER_POOLTAG); + } + } + + if (req != NULL) + { + WdfObjectDelete(req); + req = NULL; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::GetUsbHsEndpointDescriptor +( + _In_ ULONG EpIndex, + _Out_ PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR *EpDescriptor +) +/*++ + +Routine Description: + + This function synchronously gets the USB Headset Endpoint Descriptor. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + WDFREQUEST req = NULL; + PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR descriptor = NULL; + ULONG length = 0; + ULONG_PTR information = 0; + WDF_REQUEST_REUSE_PARAMS reuseParams; + WDF_OBJECT_ATTRIBUTES attributes; + + *EpDescriptor = NULL; + + // + // Allocate and format a WDF request. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &req); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate failed, 0x%x", ntStatus)), + Done); + + // + // Get the size of the buffer. + // + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR, + sizeof(EpIndex), + NULL, + &EpIndex); + + if (ntStatus != STATUS_BUFFER_TOO_SMALL) + { + if (NT_SUCCESS(ntStatus)) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + } + + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR): failed, 0x%x", ntStatus)); + goto Done; + } + + ntStatus = STATUS_SUCCESS; + + information = WdfRequestGetInformation(req); + if (information == 0 || information > ULONG_MAX) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + DPF(D_ERROR, ("%!FUNC!: IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR buffer too big (%Id): 0x%x", information, ntStatus)); + goto Done; + } + + length = (ULONG)information; + + // + // Allocate memory needed to hold the info. + // + descriptor = (PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR) ExAllocatePoolWithTag(NonPagedPoolNx, length, MINADAPTER_POOLTAG); + if (descriptor == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: ExAllocatePoolWithTag failed, out of memory")), + Done); + + // + // Get the USB Headset descriptor. + // + WDF_REQUEST_REUSE_PARAMS_INIT( + &reuseParams, + WDF_REQUEST_REUSE_NO_FLAGS, + STATUS_SUCCESS); + + ntStatus = WdfRequestReuse(req, &reuseParams); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus)), + Done); + + // Same buffer used for input and output + // Cast into ULONG to provide EpIndex + *((ULONG *)descriptor) = EpIndex; + + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR, + sizeof(EpIndex), + length, + descriptor); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *EpDescriptor = descriptor; + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + if (descriptor != NULL) + { + ExFreePoolWithTag(descriptor, MINADAPTER_POOLTAG); + } + } + + if (req != NULL) + { + WdfObjectDelete(req); + req = NULL; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::VerifyEndpointFormatCompatibility +( + _In_ PSIDEBANDAUDIO_SUPPORTED_FORMATS EpSupportedFormats, + _In_ PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pHardwareSupportedFormats, + _In_ ULONG NumHardwareSupportedFormats, + _Out_ PULONG pHardwareFormatIndex +) +{ + NTSTATUS status = STATUS_NOT_SUPPORTED; + ULONG iHardwareFormats = 0; + ULONG iEpDataFormats = 0; + BOOL bFound = FALSE; + + // Verify here whether hardware/dsp mixer can support the USB device formats + // This format will be later used to open the ISOCH channel + + for (iHardwareFormats = 0; iHardwareFormats < NumHardwareSupportedFormats; iHardwareFormats++) + { + // for each format supported by hardware, search in EP data formats + for (iEpDataFormats = 0; iEpDataFormats < EpSupportedFormats->NumFormats; iEpDataFormats++) + { + PKSDATAFORMAT pEpDataFormat = (PKSDATAFORMAT)EpSupportedFormats->Formats[iEpDataFormats]; + + // Verify KSDATAFORMAT + if (pEpDataFormat->FormatSize >= sizeof(KSDATAFORMAT)) + { + if (pEpDataFormat->FormatSize >= sizeof(KSDATAFORMAT_WAVEFORMATEX)) + { + // Check other parameters as well + // Ignoring for simplicity + if ((((PKSDATAFORMAT_WAVEFORMATEX)pEpDataFormat)->WaveFormatEx.nSamplesPerSec == pHardwareSupportedFormats[iHardwareFormats].WaveFormatExt.Format.nSamplesPerSec) && + (((PKSDATAFORMAT_WAVEFORMATEX)pEpDataFormat)->WaveFormatEx.nChannels == pHardwareSupportedFormats[iHardwareFormats].WaveFormatExt.Format.nChannels) && + (((PKSDATAFORMAT_WAVEFORMATEX)pEpDataFormat)->WaveFormatEx.wBitsPerSample == pHardwareSupportedFormats[iHardwareFormats].WaveFormatExt.Format.wBitsPerSample)) + { + bFound = TRUE; + *pHardwareFormatIndex = iHardwareFormats; + status = STATUS_SUCCESS; + break; + } + } + } + } + } + + // Iterate through Sideband Device data range to figure out + // intersection with hardware supported formats + + return status; +} + + +NTSTATUS UsbHsDevice::FreeTransportResources +( + _In_ PUSBHSDEVICE_EP_TRANSPORT_RESOURCES pTransportResources +) +{ + SAFE_DELETE_PTR_WITH_TAG(pTransportResources->pUsbInterfaceDescriptor, USBSIDEBANDTEST_POOLTAG07); + SAFE_DELETE_PTR_WITH_TAG(pTransportResources->pUsbEndpointDescriptor, USBSIDEBANDTEST_POOLTAG08); + SAFE_DELETE_PTR_WITH_TAG(pTransportResources->pUsbOffloadInformation, USBSIDEBANDTEST_POOLTAG09); + SAFE_DELETE_PTR_WITH_TAG(pTransportResources->pUsbAudioTransportResources, USBSIDEBANDTEST_POOLTAG010); + SAFE_DELETE_PTR_WITH_TAG(pTransportResources->pSyncUsbEndpointDescriptor, USBSIDEBANDTEST_POOLTAG011); + SAFE_DELETE_PTR_WITH_TAG(pTransportResources->pSyncUsbOffloadInformation, USBSIDEBANDTEST_POOLTAG012); + SAFE_DELETE_PTR_WITH_TAG(pTransportResources->pSyncUsbAudioTransportResources, USBSIDEBANDTEST_POOLTAG013); + RtlZeroMemory(pTransportResources, sizeof(USBHSDEVICE_EP_TRANSPORT_RESOURCES)); + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS UsbHsDevice::GetSiop +( + _In_ ULONG EpIndex, + _In_ ULONG SiopTypeId, + _In_ ULONG PoolTag, + _Out_ PBYTE *ppOutputBuffer +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS status = STATUS_SUCCESS; + WDFREQUEST req = NULL; + PBYTE pOutput = NULL; + ULONG length = 0; + ULONG_PTR information = 0; + WDF_REQUEST_REUSE_PARAMS reuseParams; + WDF_OBJECT_ATTRIBUTES attributes; + SIDEBANDAUDIO_SIOP_REQUEST_PARAM siop = { 0 }; + + *ppOutputBuffer = NULL; + + // + // Allocate and format a WDF request. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + status = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &req); + IF_FAILED_ACTION_JUMP( + status, + DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate failed, 0x%x", status)), + Done); + + siop.EpIndex = EpIndex; + siop.RequestedSiop.ParamSet = SIDEBANDAUDIO_PARAMS_SET_USBAUDIO; + siop.RequestedSiop.TypeId = SiopTypeId; + siop.RequestedSiop.Size = 0; + + // + // Get the size of the buffer. + // + status = SendIoCtrlSynchronously( + req, + IOCTL_SBAUD_GET_SIOP, + sizeof(siop), + 0, + &siop, + NULL); + + if (status != STATUS_BUFFER_TOO_SMALL) + { + if (NT_SUCCESS(status)) + { + status = STATUS_INVALID_DEVICE_STATE; + } + + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_SIOP): failed, 0x%x", status)); + goto Done; + } + + status = STATUS_SUCCESS; + + information = WdfRequestGetInformation(req); + if (information == 0 || information > ULONG_MAX) + { + status = STATUS_INVALID_DEVICE_STATE; + DPF(D_ERROR, ("%!FUNC!: IOCTL_SBAUD_GET_SIOP buffer too big (%Id): 0x%x", information, status)); + goto Done; + } + + length = (ULONG)information; + + // + // Allocate memory needed to hold the info. + // + pOutput = (PBYTE)ExAllocatePoolWithTag(NonPagedPoolNx, length, PoolTag); + if (pOutput == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_ACTION_JUMP( + status, + DPF(D_ERROR, ("%!FUNC!: ExAllocatePoolWithTag failed, out of memory")), + Done); + + // + // Get the Siop + // + WDF_REQUEST_REUSE_PARAMS_INIT( + &reuseParams, + WDF_REQUEST_REUSE_NO_FLAGS, + STATUS_SUCCESS); + + status = WdfRequestReuse(req, &reuseParams); + IF_FAILED_ACTION_JUMP( + status, + DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", status)), + Done); + + // Same buffer used for input and output + // Cast into ULONG to provide EpIndex + + status = SendIoCtrlSynchronously( + req, + IOCTL_SBAUD_GET_SIOP, + sizeof(siop), + length, + &siop, + pOutput); + IF_FAILED_ACTION_JUMP( + status, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_SIOP) failed, 0x%x", status)), + Done); + + // + // All done. + // + *ppOutputBuffer = pOutput; + status = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(status)) + { + if (pOutput != NULL) + { + ExFreePoolWithTag(pOutput, PoolTag); + } + } + + if (req != NULL) + { + WdfObjectDelete(req); + req = NULL; + } + + return status; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::GetTransportResources +( + _In_ ULONG EpIndex, + _In_ PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR pEndpointDescriptor, + _Out_ PUSBHSDEVICE_EP_TRANSPORT_RESOURCES pTransportResources +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS status = STATUS_SUCCESS; + + // SIOP_TYPE_USBAUD_USBD_INTERFACE_DESCRIPTOR + status = GetSiop(EpIndex, SIOP_TYPE_USBAUD_USBD_INTERFACE_DESCRIPTOR, USBSIDEBANDTEST_POOLTAG07, (PBYTE *)&pTransportResources->pUsbInterfaceDescriptor); + if (!NT_SUCCESS(status)) + { + DPF(D_ERROR, ("%!FUNC!: GetSiop(SIOP_TYPE_USBAUD_USBD_INTERFACE_DESCRIPTOR) failed, 0x%x", status)); + goto Done; + } + + // SIOP_TYPE_USBAUD_EP_USBD_ENDPOINT_DESCRIPTOR + status = GetSiop(EpIndex, SIOP_TYPE_USBAUD_EP_USBD_ENDPOINT_DESCRIPTOR, USBSIDEBANDTEST_POOLTAG08, (PBYTE *)&pTransportResources->pUsbEndpointDescriptor); + if (!NT_SUCCESS(status)) + { + DPF(D_ERROR, ("%!FUNC!: GetSiop(SIOP_TYPE_USBAUD_EP_USBD_ENDPOINT_DESCRIPTOR) failed, 0x%x", status)); + goto Done; + } + + // SIOP_TYPE_USBAUD_EP_USBD_ENDPOINT_OFFLOAD_INFORMATION + status = GetSiop(EpIndex, SIOP_TYPE_USBAUD_EP_USBD_ENDPOINT_OFFLOAD_INFORMATION, USBSIDEBANDTEST_POOLTAG09, (PBYTE *)&pTransportResources->pUsbOffloadInformation); + if (!NT_SUCCESS(status)) + { + DPF(D_ERROR, ("%!FUNC!: GetSiop(SIOP_TYPE_USBAUD_EP_USBD_ENDPOINT_OFFLOAD_INFORMATION) failed, 0x%x", status)); + goto Done; + } + + // SIOP_TYPE_USBAUD_EP_USBAUDIO_TRANSPORT_RESOURCES + status = GetSiop(EpIndex, SIOP_TYPE_USBAUD_EP_USBAUDIO_TRANSPORT_RESOURCES, USBSIDEBANDTEST_POOLTAG010, (PBYTE *)&pTransportResources->pUsbAudioTransportResources); + if (!NT_SUCCESS(status)) + { + DPF(D_ERROR, ("%!FUNC!: GetSiop(SIOP_TYPE_USBAUD_EP_USBAUDIO_TRANSPORT_RESOURCES) failed, 0x%x", status)); + goto Done; + } + + if (pEndpointDescriptor->Capabilities.Feedback) + { + // SIOP_TYPE_USBAUD_FB_EP_USBD_ENDPOINT_DESCRIPTOR + status = GetSiop(EpIndex, SIOP_TYPE_USBAUD_FB_EP_USBD_ENDPOINT_DESCRIPTOR, USBSIDEBANDTEST_POOLTAG011, (PBYTE *)&pTransportResources->pSyncUsbEndpointDescriptor); + if (!NT_SUCCESS(status)) + { + DPF(D_ERROR, ("%!FUNC!: GetSiop(SIOP_TYPE_USBAUD_FB_EP_USBD_ENDPOINT_DESCRIPTOR) failed, 0x%x", status)); + goto Done; + } + + // SIOP_TYPE_USBAUD_FB_EP_USBD_ENDPOINT_OFFLOAD_INFORMATION + status = GetSiop(EpIndex, SIOP_TYPE_USBAUD_FB_EP_USBD_ENDPOINT_OFFLOAD_INFORMATION, USBSIDEBANDTEST_POOLTAG012, (PBYTE *)&pTransportResources->pSyncUsbOffloadInformation); + if (!NT_SUCCESS(status)) + { + DPF(D_ERROR, ("%!FUNC!: GetSiop(SIOP_TYPE_USBAUD_FB_EP_USBD_ENDPOINT_OFFLOAD_INFORMATION) failed, 0x%x", status)); + goto Done; + } + + // SIOP_TYPE_USBAUD_FB_EP_USBAUDIO_TRANSPORT_RESOURCES + status = GetSiop(EpIndex, SIOP_TYPE_USBAUD_FB_EP_USBAUDIO_TRANSPORT_RESOURCES, USBSIDEBANDTEST_POOLTAG013, (PBYTE *)&pTransportResources->pSyncUsbAudioTransportResources); + if (!NT_SUCCESS(status)) + { + DPF(D_ERROR, ("%!FUNC!: GetSiop(SIOP_TYPE_USBAUD_FB_EP_USBAUDIO_TRANSPORT_RESOURCES) failed, 0x%x", status)); + goto Done; + } + } + +Done: + if (!NT_SUCCESS(status)) + { + FreeTransportResources(pTransportResources); + } + return status; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::SetTransportResources +( + _In_ PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR pEndpointDescriptor, + _In_ ULONG EpIndex +) +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS status = STATUS_SUCCESS; + + typedef struct _SYSVAD_USBSBAUD_SIOP_RESOURCE_ID + { + SIDEBANDAUDIO_SIOP_REQUEST_PARAM srp; + ULONG resourceId; + }SYSVAD_USBSBAUD_SIOP_RESOURCE_ID, *PSYSVAD_USBSBAUD_SIOP_RESOURCE_ID; + + // + // Set the USB Resource ID for Endpoint + // + SYSVAD_USBSBAUD_SIOP_RESOURCE_ID siopResourceId = { 0 }; + siopResourceId.srp.EpIndex = EpIndex; + siopResourceId.srp.RequestedSiop.ParamSet = SIDEBANDAUDIO_PARAMS_SET_USBAUDIO; + siopResourceId.srp.RequestedSiop.TypeId = SIOP_TYPE_USBAUD_EP_OFFLOAD_RESOURCE_ID; + siopResourceId.srp.RequestedSiop.Size = sizeof(ULONG); + siopResourceId.resourceId = EpIndex + 7;// Just for testing. Real hardware will populate this with the DSP resource ID + + status = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_SET_SIOP, + sizeof(siopResourceId), + 0, + &siopResourceId); + + IF_FAILED_ACTION_JUMP( + status, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_SET_SIOP) failed, 0x%x", status)), + Done); + + if (pEndpointDescriptor->Capabilities.Feedback) + { + // + // Set the USB Resource ID for Feedback Endpoint + // + siopResourceId.srp.RequestedSiop.TypeId = SIOP_TYPE_USBAUD_FB_EP_OFFLOAD_RESOURCE_ID; + siopResourceId.resourceId = EpIndex + 10;// Just for testing. Real hardware will populate this with the DSP resource ID + + status = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_SET_SIOP, + sizeof(siopResourceId), + 0, + &siopResourceId); + + IF_FAILED_ACTION_JUMP( + status, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_SET_SIOP) Feedback failed, 0x%x", status)), + Done); + } + + // + // All done. + // + status = STATUS_SUCCESS; + +Done: + + return status; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::GetUsbHsEndpointFormatsIntersection +( + _In_ PSIDEBANDAUDIO_SUPPORTED_FORMATS pDeviceFormats, + _Out_ PSIDEBANDAUDIO_SUPPORTED_FORMATS *ppSupportedFormatsIntersection +) +/*++ + +Routine Description: + + This function synchronously gets the USB Headset supported formats for + a given endpoint. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + WDFREQUEST req = NULL; + PSIDEBANDAUDIO_SUPPORTED_FORMATS formats = NULL; + ULONG length = 0; + ULONG_PTR information = 0; + WDF_REQUEST_REUSE_PARAMS reuseParams; + WDF_OBJECT_ATTRIBUTES attributes; + + *ppSupportedFormatsIntersection = NULL; + + // + // Allocate and format a WDF request. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &req); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate failed, 0x%x", ntStatus)), + Done); + + // + // Get the size of the buffer. + // + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_SBAUD_GET_SUPPORTED_FORMATS, + pDeviceFormats->CbSize, + 0, + pDeviceFormats, + NULL); + + if (ntStatus != STATUS_BUFFER_TOO_SMALL) + { + if (NT_SUCCESS(ntStatus)) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + } + + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_SUPPORTED_FORMATS): failed, 0x%x", ntStatus)); + goto Done; + } + + ntStatus = STATUS_SUCCESS; + + information = WdfRequestGetInformation(req); + if (information == 0 || information > ULONG_MAX) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + DPF(D_ERROR, ("%!FUNC!: IOCTL_SBAUD_GET_SUPPORTED_FORMATS buffer too big (%Id): 0x%x", information, ntStatus)); + goto Done; + } + + length = (ULONG)information; + + // + // Allocate memory needed to hold the info. + // + formats = (PSIDEBANDAUDIO_SUPPORTED_FORMATS) ExAllocatePoolWithTag(NonPagedPoolNx, length, USBSIDEBANDTEST_POOLTAG03); + if (formats == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: ExAllocatePoolWithTag failed, out of memory")), + Done); + + // + // Get the USB Headset descriptor. + // + WDF_REQUEST_REUSE_PARAMS_INIT( + &reuseParams, + WDF_REQUEST_REUSE_NO_FLAGS, + STATUS_SUCCESS); + + ntStatus = WdfRequestReuse(req, &reuseParams); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus)), + Done); + + // Same buffer used for input and output + // Cast into ULONG to provide EpIndex + + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_SBAUD_GET_SUPPORTED_FORMATS, + pDeviceFormats->CbSize, + length, + pDeviceFormats, + formats); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_SUPPORTED_FORMATS) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *ppSupportedFormatsIntersection = formats; + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + if (formats != NULL) + { + ExFreePoolWithTag(formats, USBSIDEBANDTEST_POOLTAG03); + } + } + + if (req != NULL) + { + WdfObjectDelete(req); + req = NULL; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::GetUsbHsVolumePropertyValues +( + _In_ ULONG EpIndex, + _In_ ULONG Length, + _Out_ PKSPROPERTY_DESCRIPTION *PropValues +) +/*++ + +Routine Description: + + This function synchronously gets the remote USB Headset volume values. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PKSPROPERTY_DESCRIPTION propValues = NULL; + + *PropValues = NULL; + + // + // Allocate memory. + // + propValues = (PKSPROPERTY_DESCRIPTION) ExAllocatePoolWithTag(NonPagedPoolNx, Length, USBSIDEBANDTEST_POOLTAG02); + if (propValues == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: ExAllocatePoolWithTag failed, out of memory")), + Done); + + // + // Send EpIndex in same buffer as input param + // + *((ULONG *)propValues) = EpIndex; + + // + // Get the USB Headset volume property values. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_GET_VOLUMEPROPERTYVALUES, + sizeof(EpIndex), + Length, + propValues); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_VOLUMEPROPERTYVALUES) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *PropValues = propValues; + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + if (propValues != NULL) + { + ExFreePoolWithTag(propValues, MINADAPTER_POOLTAG); + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::SetUsbHsSpeakerVolume +( + _In_ LONG Channel, + _In_ LONG Volume +) +/*++ + +Routine Description: + + This function synchronously sets the remote USB Headset speaker volume. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + SIDEBANDAUDIO_VOLUME_PARAMS vp; + + // + // Set the USB Headset speaker volume. + // + + vp.EpIndex = m_SpeakerEpIndex; + vp.Value = Volume; + vp.Channel = Channel; + vp.Immediate = TRUE; // not required for SET + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_SET_VOLUME, + sizeof(vp), + 0, + &vp); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_SET_VOLUME) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::GetUsbHsSpeakerVolume +( + _In_ LONG Channel, + _Out_ LONG * Volume +) +/*++ + +Routine Description: + + This function synchronously gets the remote USB Headset speaker volume. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + UsbHsDeviceNotificationBuffer buffer = {0}; + + *Volume = 0; + + buffer.Volume.Immediate = TRUE; + buffer.Volume.EpIndex = m_SpeakerEpIndex; + buffer.Volume.Channel = Channel; + buffer.Volume.Value = 0; + + // + // Get the USB headset speaker volume. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE, + sizeof(buffer.Volume), + sizeof(buffer.Volume), + &buffer); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *Volume = buffer.Volume.Value; + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +UsbHsDevice::EnableUsbHsSpeakerVolumeStatusNotification() +/*++ + +Routine Description: + + This function registers for USB Headset speaker + volume change notification. + +--*/ +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + UsbHsDeviceNotificationReqContext *ctx = NULL; + + ctx = GetUsbHsDeviceNotificationReqContext(m_SpeakerVolumeReq); + + // + // This is a notification request. + // + ctx->Buffer.Volume.Immediate = FALSE; + ctx->Buffer.Volume.EpIndex = m_SpeakerEpIndex; + ctx->Buffer.Volume.Value = 0; + + // + // Register for speaker volume updates. + // + ntStatus = SendIoCtrlAsynchronously( + m_SpeakerVolumeReq, + IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtUsbHsDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlAsynchronously(IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::SetUsbHsMicVolume +( + _In_ LONG Channel, + _In_ LONG Volume +) +/*++ + +Routine Description: + +This function synchronously sets the remote USB Headset mic volume. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + SIDEBANDAUDIO_VOLUME_PARAMS vp; + + // + // Get the USB Headset mic volume. + // + + vp.EpIndex = m_MicEpIndex; + vp.Value = Volume; + vp.Channel = Channel; + vp.Immediate = TRUE; // not required for SET + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_SET_VOLUME, + sizeof(vp), + 0, + &vp); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_SET_VOLUME) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::GetUsbHsMicVolume +( + _In_ LONG Channel, + _Out_ LONG * Volume +) +/*++ + +Routine Description: + +This function synchronously gets the remote USB Headset mic volume. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + UsbHsDeviceNotificationBuffer buffer = { 0 }; + + *Volume = 0; + + buffer.Volume.Immediate = TRUE; + buffer.Volume.EpIndex = m_MicEpIndex; + buffer.Volume.Channel = Channel; + buffer.Volume.Value = 0; + + // + // Get the USB Headset mic volume. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE, + sizeof(buffer.Volume), + sizeof(buffer.Volume), + &buffer); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *Volume = buffer.Volume.Value; + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::GetUsbHsMutePropertyValues +( + _In_ ULONG EpIndex, + _In_ PULONG pLength, + _Out_ PKSPROPERTY_DESCRIPTION *PropValues +) +/*++ + +Routine Description: + + This function synchronously gets the remote USB Headset mute values. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + WDFREQUEST req = NULL; + ULONG length = 0; + ULONG_PTR information = 0; + WDF_REQUEST_REUSE_PARAMS reuseParams; + WDF_OBJECT_ATTRIBUTES attributes; + PKSPROPERTY_DESCRIPTION propValues = NULL; + + *PropValues = NULL; + + // + // Allocate and format a WDF request. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &req); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate failed, 0x%x", ntStatus)), + Done); + + // + // Get the size of the buffer. + // + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_SBAUD_GET_MUTEPROPERTYVALUES, + sizeof(EpIndex), + 0, + &EpIndex); + + if (ntStatus != STATUS_BUFFER_TOO_SMALL) + { + if (NT_SUCCESS(ntStatus)) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + } + + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_MUTEPROPERTYVALUES): failed, 0x%x", ntStatus)); + goto Done; + } + + ntStatus = STATUS_SUCCESS; + + information = WdfRequestGetInformation(req); + if (information == 0 || information > ULONG_MAX) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + DPF(D_ERROR, ("%!FUNC!: IOCTL_SBAUD_GET_MUTEPROPERTYVALUES buffer too big (%Id): 0x%x", information, ntStatus)); + goto Done; + } + + length = (ULONG)information; + + // + // Allocate memory needed to hold the info. + // + propValues = (PKSPROPERTY_DESCRIPTION) ExAllocatePoolWithTag(NonPagedPoolNx, length, USBSIDEBANDTEST_POOLTAG06); + if (propValues == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: ExAllocatePoolWithTag failed, out of memory")), + Done); + + // + // Get the USB Headset Mute Property Values. + // + WDF_REQUEST_REUSE_PARAMS_INIT( + &reuseParams, + WDF_REQUEST_REUSE_NO_FLAGS, + STATUS_SUCCESS); + + ntStatus = WdfRequestReuse(req, &reuseParams); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus)), + Done); + + // Same buffer used for input and output + // Cast into ULONG to provide EpIndex + *((ULONG *)propValues) = EpIndex; + + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_SBAUD_GET_MUTEPROPERTYVALUES, + sizeof(EpIndex), + length, + propValues); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_MUTEPROPERTYVALUES) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *PropValues = propValues; + *pLength = length; + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + if (propValues != NULL) + { + ExFreePoolWithTag(propValues, USBSIDEBANDTEST_POOLTAG06); + } + } + + if (req != NULL) + { + WdfObjectDelete(req); + req = NULL; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::SetUsbHsSpeakerMute +( + _In_ LONG Channel, + _In_ LONG Mute +) +/*++ + +Routine Description: + + This function synchronously sets the remote USB Headset speaker mute. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + SIDEBANDAUDIO_MUTE_PARAMS mp; + + // + // Set the USB Headset speaker mute. + // + + mp.EpIndex = m_SpeakerEpIndex; + mp.Value = Mute; + mp.Channel = Channel; + mp.Immediate = TRUE; // not required for SET + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_SET_MUTE, + sizeof(mp), + 0, + &mp); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_SET_MUTE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::GetUsbHsSpeakerMute +( + _In_ LONG Channel, + _Out_ LONG *Mute +) +/*++ + +Routine Description: + + This function synchronously gets the remote USB Headset speaker mute. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + UsbHsDeviceNotificationBuffer buffer = {0}; + + *Mute = 0; + + buffer.Mute.Immediate = TRUE; + buffer.Mute.EpIndex = m_SpeakerEpIndex; + buffer.Mute.Channel = Channel; + buffer.Mute.Value = 0; + + // + // Get the USB headset speaker mute. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE, + sizeof(buffer.Mute), + sizeof(buffer.Mute), + &buffer); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *Mute = buffer.Mute.Value; + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +UsbHsDevice::EnableUsbHsSpeakerMuteStatusNotification() +/*++ + +Routine Description: + + This function registers for USB Headset speaker + mute change notification. + +--*/ +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + UsbHsDeviceNotificationReqContext *ctx = NULL; + + ctx = GetUsbHsDeviceNotificationReqContext(m_SpeakerMuteReq); + + // + // This is a notification request. + // + ctx->Buffer.Mute.Immediate = FALSE; + ctx->Buffer.Mute.EpIndex = m_SpeakerEpIndex; + ctx->Buffer.Mute.Value = 0; + + // + // Register for speaker mute updates. + // + ntStatus = SendIoCtrlAsynchronously( + m_SpeakerMuteReq, + IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtUsbHsDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlAsynchronously(IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::SetUsbHsMicMute +( + _In_ LONG Channel, + _In_ LONG Mute +) +/*++ + +Routine Description: + +This function synchronously sets the remote USB Headset mic mute. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + SIDEBANDAUDIO_MUTE_PARAMS mp; + + // + // Get the USB Headset mic mute. + // + + mp.EpIndex = m_MicEpIndex; + mp.Value = Mute; + mp.Channel = Channel; + mp.Immediate = TRUE; // not required for SET + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_SET_MUTE, + sizeof(mp), + 0, + &mp); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_SET_MUTE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::GetUsbHsMicMute +( + _In_ LONG Channel, + _Out_ LONG *Mute +) +/*++ + +Routine Description: + +This function synchronously gets the remote USB Headset mic mute. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + UsbHsDeviceNotificationBuffer buffer = { 0 }; + + *Mute = 0; + + buffer.Mute.Immediate = TRUE; + buffer.Mute.EpIndex = m_MicEpIndex; + buffer.Mute.Channel = Channel; + buffer.Mute.Value = 0; + + // + // Get the USB Headset mic mute. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE, + sizeof(buffer.Mute), + sizeof(buffer.Mute), + &buffer); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *Mute = buffer.Mute.Value; + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +UsbHsDevice::EnableUsbHsMicMuteStatusNotification() +/*++ + +Routine Description: + + This function registers for USB Headset mic + mute change notification. + +--*/ +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + UsbHsDeviceNotificationReqContext *ctx = NULL; + + ctx = GetUsbHsDeviceNotificationReqContext(m_MicMuteReq); + + // + // This is a notification request. + // + ctx->Buffer.Mute.Immediate = FALSE; + ctx->Buffer.Mute.EpIndex = m_MicEpIndex; + ctx->Buffer.Mute.Value = 0; + + // + // Register for mic mute updates. + // + ntStatus = SendIoCtrlAsynchronously( + m_MicMuteReq, + IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtUsbHsDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlAsynchronously(IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::SetSidebandClaimed(_In_ BOOL bClaimed) +/*++ + +Routine Description: + + This function synchronously requests an open ISOCH channel to transmit audio + data over transport. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_SET_DEVICE_CLAIMED, + sizeof(BOOL), + 0, + &bClaimed); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_SET_DEVICE_CLAIMED) failed, 0x%x", ntStatus)), + Done); + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +UsbHsDevice::EnableUsbHsMicVolumeStatusNotification() +/*++ + +Routine Description: + +This function registers for USB Headset mic +volume change notification. + +--*/ +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + UsbHsDeviceNotificationReqContext *ctx = NULL; + + ctx = GetUsbHsDeviceNotificationReqContext(m_MicVolumeReq); + + // + // This is a notification request. + // + ctx->Buffer.Volume.Immediate = FALSE; + ctx->Buffer.Volume.EpIndex = m_MicEpIndex; + ctx->Buffer.Volume.Value = 0; + + // + // Register for mic volume updates. + // + ntStatus = SendIoCtrlAsynchronously( + m_MicVolumeReq, + IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtUsbHsDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlAsynchronously(IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +void +UsbHsDevice::EvtUsbHsDeviceStreamStatusCompletion +( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, + _In_ WDFCONTEXT Context +) +/*++ + +Routine Description: + + Completion callback for the USB Headset stream status notification. + +--*/ +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + UsbHsDeviceNotificationReqContext *reqCtx = NULL; + UsbHsDevice *This = NULL; + NTSTATUS ntResult = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(Target); + UNREFERENCED_PARAMETER(Context); + + // + // Get the USB stream status. + // + reqCtx = GetUsbHsDeviceNotificationReqContext(Request); + This = reqCtx->UsbHsDevice; + ASSERT(This != NULL); + + ntStatus = Params->IoStatus.Status; + if (!NT_SUCCESS(ntStatus)) + { + ntResult = STATUS_INVALID_DEVICE_STATE; + } + else + { + ntResult = reqCtx->Buffer.streamStatus.Status; + } + + if (reqCtx->Buffer.streamStatus.EpIndex == This->m_SpeakerEpIndex) + { + InterlockedExchange(&This->m_SpeakerStreamStatusLong, (LONG)ntResult); + + // + // Let the stop routine know we are done. Stop routine will + // re-init the request. + // + KeSetEvent(&This->m_SpeakerStreamStatusEvent, IO_NO_INCREMENT, FALSE); + } + else if (reqCtx->Buffer.streamStatus.EpIndex == This->m_MicEpIndex) + { + InterlockedExchange(&This->m_MicStreamStatusLong, (LONG)ntResult); + + // + // Let the stop routine know we are done. Stop routine will + // re-init the request. + // + KeSetEvent(&This->m_MicStreamStatusEvent, IO_NO_INCREMENT, FALSE); + } +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +UsbHsDevice::EnableUsbHsSpeakerStreamStatusNotification() +/*++ + +Routine Description: + + This function registers for USB Headset stream status notification. + +--*/ +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + UsbHsDeviceNotificationReqContext *ctx = NULL; + + ASSERT(m_nSpeakerStreamsOpen > 0); + + ctx = GetUsbHsDeviceNotificationReqContext(m_SpeakerStreamReq); + ctx->Buffer.streamStatus.Immediate = FALSE; + ctx->Buffer.streamStatus.EpIndex = m_SpeakerEpIndex; + + KeClearEvent(&m_SpeakerStreamStatusEvent); + + // + // Get the USB Headset speaker stream status. + // + ntStatus = SendIoCtrlAsynchronously( + m_SpeakerStreamReq, + IOCTL_SBAUD_GET_STREAM_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtUsbHsDeviceStreamStatusCompletion, + this); + + if (!NT_SUCCESS(ntStatus)) + { + KeSetEvent(&m_SpeakerStreamStatusEvent, IO_NO_INCREMENT, FALSE); + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlAsynchronously(IOCTL_SBAUD_GET_STREAM_STATUS_UPDATE) failed, 0x%x", ntStatus)); + goto Done; + } + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +UsbHsDevice::EnableUsbHsMicStreamStatusNotification() +/*++ + +Routine Description: + + This function registers for USB Headset stream status notification. + +--*/ +{ + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + UsbHsDeviceNotificationReqContext *ctx = NULL; + + ASSERT(m_nMicStreamsOpen > 0); + + ctx = GetUsbHsDeviceNotificationReqContext(m_MicStreamReq); + ctx->Buffer.streamStatus.Immediate = FALSE; + ctx->Buffer.streamStatus.EpIndex = m_MicEpIndex; + + KeClearEvent(&m_MicStreamStatusEvent); + + // + // Get the USB Headset mic stream status. + // + ntStatus = SendIoCtrlAsynchronously( + m_MicStreamReq, + IOCTL_SBAUD_GET_STREAM_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtUsbHsDeviceStreamStatusCompletion, + this); + + if (!NT_SUCCESS(ntStatus)) + { + KeSetEvent(&m_MicStreamStatusEvent, IO_NO_INCREMENT, FALSE); + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlAsynchronously(IOCTL_SBAUD_GET_STREAM_STATUS_UPDATE) failed, 0x%x", ntStatus)); + goto Done; + } + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::StopUsbHsSpeakerStreamStatusNotification() +/*++ + +Routine Description: + + This function stops the USB Headset speaker stream status notification. + The function waits for the request to be done before returning. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + UsbHsDeviceNotificationReqContext *reqCtx = NULL; + WDF_REQUEST_REUSE_PARAMS reuseParams; + + WdfRequestCancelSentRequest(m_SpeakerStreamReq); + KeWaitForSingleObject(&m_SpeakerStreamStatusEvent, Executive, KernelMode, FALSE, NULL); + + reqCtx = GetUsbHsDeviceNotificationReqContext(m_SpeakerStreamReq); + ASSERT(reqCtx != NULL); + UNREFERENCED_VAR(reqCtx); + + // + // Re-init the request for later. + // + WDF_REQUEST_REUSE_PARAMS_INIT( + &reuseParams, + WDF_REQUEST_REUSE_NO_FLAGS, + STATUS_SUCCESS); + + ntStatus = WdfRequestReuse(m_SpeakerStreamReq, &reuseParams); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus)); + } + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::StopUsbHsMicStreamStatusNotification() +/*++ + +Routine Description: + + This function stops the USB Headset mic stream status notification. + The function waits for the request to be done before returning. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + UsbHsDeviceNotificationReqContext *reqCtx = NULL; + WDF_REQUEST_REUSE_PARAMS reuseParams; + + WdfRequestCancelSentRequest(m_MicStreamReq); + KeWaitForSingleObject(&m_MicStreamStatusEvent, Executive, KernelMode, FALSE, NULL); + + reqCtx = GetUsbHsDeviceNotificationReqContext(m_MicStreamReq); + 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_MicStreamReq, &reuseParams); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus)); + } + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::SetUsbHsStreamOpen +( + ULONG EpIndex, + PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pStreamFormat, + PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR pEndpointDescriptor +) +/*++ + +Routine Description: + + This function synchronously requests an open ISOCH channel to transmit audio + data over transport. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[UsbHsDevice::SetA2dpHpStreamOpen]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + UNREFERENCED_PARAMETER(pEndpointDescriptor); + + SIDEBANDAUDIO_STREAM_OPEN_PARAMS sp = { 0 }; + sp.EpIndex = EpIndex; + sp.Format = (PKSDATAFORMAT)pStreamFormat; + sp.SiopCount = 0; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_STREAM_OPEN, + sizeof(sp), + 0, + &sp); + + if (ntStatus == STATUS_DEVICE_BUSY) + { + // The stream channel is already open. + DPF(D_VERBOSE, ("SetA2dpHpStreamOpen: the stream channel is already open")); + ntStatus = STATUS_SUCCESS; + } + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetA2dpHpStreamOpen: SendIoCtrlSynchronously(IOCTL_SBAUD_STREAM_OPEN) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::SetUsbHsStreamStart +( + ULONG EpIndex +) +/*++ + +Routine Description: + + This function synchronously requests an Start ISOCH channel to transmit audio + data over transport. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[UsbHsDevice::SetA2dpHpStreamStart]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_STREAM_START, + sizeof(EpIndex), + 0, + &EpIndex); + + if (ntStatus == STATUS_DEVICE_BUSY) + { + // The stream channel is already Start. + DPF(D_VERBOSE, ("SetA2dpHpStreamStart: the stream channel is already Start")); + ntStatus = STATUS_SUCCESS; + } + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetA2dpHpStreamStart: SendIoCtrlSynchronously(IOCTL_SBAUD_STREAM_START) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::SetUsbHsStreamSuspend +( + _In_ ULONG EpIndex +) +/*++ + +Routine Description: + + This function synchronously requests to Suspend the ISOCH channel. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_STREAM_SUSPEND, + sizeof(EpIndex), + 0, + &EpIndex); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_STREAM_SUSPEND) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::SetUsbHsStreamClose +( + _In_ ULONG EpIndex +) +/*++ + +Routine Description: + + This function synchronously requests to close the ISOCH channel. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_SBAUD_STREAM_CLOSE, + sizeof(EpIndex), + 0, + &EpIndex); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_STREAM_CLOSE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +UsbHsDevice::Start() +/*++ + +Routine Description: + + Asynchronously called to start the audio device. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + ULONG speakerEndpoints = 0; + ULONG micEndpoints = 0; + + // + // Get USB headset descriptor + // + ntStatus = GetUsbHsDescriptor(&m_Descriptor); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: GetUsbHsDescriptor: failed to retrieve USB Headset Descriptor, 0x%x", ntStatus)), + Done); + + for (ULONG i = 0; i < m_Descriptor->NumberOfEndpoints; i++) + { + PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR ed; + ntStatus = GetUsbHsEndpointDescriptor(i, &ed); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: GetUsbHsEndpointDescriptor: failed to retrieve USB Headset Endpoint %d Descriptor, 0x%x", i, ntStatus)), + Done); + + if (ed->Direction == KSPIN_DATAFLOW_OUT) + { + speakerEndpoints++; + m_SpeakerEpIndex = i; + m_pSpeakerDescriptor = ed; + } + else if (ed->Direction == KSPIN_DATAFLOW_IN) + { + micEndpoints++; + m_MicEpIndex = i; + m_pMicDescriptor = ed; + } + } + + if (speakerEndpoints > 1 || + micEndpoints > 1) + { + ntStatus = STATUS_DEVICE_FEATURE_NOT_SUPPORTED; + m_SpeakerEpIndex = 0; + m_MicEpIndex = 0; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: Number of endpoints on USB device not supported, 0x%x", ntStatus)), + Done); + + // + // Get volume settings. + // + if (m_pSpeakerDescriptor) + { + if (m_pSpeakerDescriptor->Capabilities.Volume) + { + PKSPROPERTY_DESCRIPTION volumePropValues = NULL; + LONG volume = 0; + + // Volume settings. + ntStatus = GetUsbHsVolumePropertyValues( + m_SpeakerEpIndex, + m_pSpeakerDescriptor->VolumePropertyValuesSize, + &volumePropValues); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: GetUsbHsVolumePropertyValues: failed to retrieve KSPROPERTY_VALUES, 0x%x", ntStatus)), + Done); + + m_SpeakerVolumePropValues = volumePropValues; + + // Speaker volume. + ntStatus = GetUsbHsSpeakerVolume(0, &volume); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: GetUsbHsSpeakerVolume: failed, 0x%x", ntStatus)), + Done); + + m_SpeakerVolumeLevel = volume; + } + + if (m_pSpeakerDescriptor->Capabilities.Mute) + { + PKSPROPERTY_DESCRIPTION mutePropValues = NULL; + ULONG ulMutePropValuesSize = 0; + LONG mute = 0; + + // Mute settings. + ntStatus = GetUsbHsMutePropertyValues( + m_SpeakerEpIndex, + &ulMutePropValuesSize, + &mutePropValues); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: GetUsbHsMutePropertyValues: failed to retrieve KSPROPERTY_DESCRIPTION, 0x%x", ntStatus)), + Done); + + m_SpeakerMutePropValues = mutePropValues; + m_ulSpeakerMutePropValuesSize = ulMutePropValuesSize; + + // Speaker mute. + ntStatus = GetUsbHsSpeakerMute(0, &mute); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: GetUsbHsSpeakerMute: failed, 0x%x", ntStatus)), + Done); + + m_SpeakerMute = mute; + } + + // Speaker Formats + PKSDATAFORMAT speakerFormatsArray[SIZEOF_ARRAY(UsbHsSpeakerSupportedDeviceFormats)]; + for (ULONG i = 0; i < SIZEOF_ARRAY(UsbHsSpeakerSupportedDeviceFormats); i++) + { + speakerFormatsArray[i] = (PKSDATAFORMAT)(&UsbHsSpeakerSupportedDeviceFormats[i]); + } + SIDEBANDAUDIO_SUPPORTED_FORMATS speakerDeviceFormats = + { + sizeof(SIDEBANDAUDIO_SUPPORTED_FORMATS), + m_SpeakerEpIndex, + SIZEOF_ARRAY(UsbHsSpeakerSupportedDeviceFormats), + speakerFormatsArray + }; + + ntStatus = GetUsbHsEndpointFormatsIntersection(&speakerDeviceFormats, &m_pSpeakerSupportedFormatsIntersection); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: GetUsbHsEndpointFormats: failed for speaker, 0x%x", ntStatus)), + Done); + + // + // Customize the topology/wave descriptors for this instance + // + ntStatus = CreateCustomEndpointMinipair( + g_UsbHsRenderEndpoints[0], + &m_pSpeakerDescriptor->FriendlyName, + &m_pSpeakerDescriptor->Category, + &m_SpeakerMiniports); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: CreateCustomEndpointMinipair for Render: failed, 0x%x", ntStatus)), + Done); + m_SpeakerMiniports->TopoName = m_SpeakerTopologyNameBuffer; + m_SpeakerMiniports->WaveName = m_SpeakerWaveNameBuffer; + + // Verify format compat + ntStatus = VerifyEndpointFormatCompatibility( + m_pSpeakerSupportedFormatsIntersection, + UsbHsSpeakerSupportedDeviceFormats, + SIZEOF_ARRAY(UsbHsSpeakerSupportedDeviceFormats), + &m_SpeakerSelectedFormat); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: VerifyEndpointFormatCompatibility : failed for speaker, 0x%x", ntStatus)), + Done); + + ASSERT(m_SpeakerMiniports != NULL); + _Analysis_assume_(m_SpeakerMiniports != NULL); + + // + // Register topology and wave filters. + // + ntStatus = m_Adapter->InstallEndpointFilters( + NULL, + m_SpeakerMiniports, + PSIDEBANDDEVICECOMMON(this), + &m_UnknownSpeakerTopology, + &m_UnknownSpeakerWave, NULL, NULL + ); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: InstallEndpointRenderFilters (USB Headset): failed, 0x%x", ntStatus)), + Done); + + // + // Pend status notifications. + // + + if (m_pSpeakerDescriptor->Capabilities.Volume) + { + // Volume speaker status. + ntStatus = EnableUsbHsSpeakerVolumeStatusNotification(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: EnableUsbHsSpeakerVolumeStatusNotification: failed, 0x%x", ntStatus)), + Done); + } + } + + if (m_pMicDescriptor) + { + if (m_pMicDescriptor->Capabilities.Volume) + { + PKSPROPERTY_DESCRIPTION volumePropValues = NULL; + LONG volume = 0; + + // Volume settings. + ntStatus = GetUsbHsVolumePropertyValues( + m_MicEpIndex, + m_pMicDescriptor->VolumePropertyValuesSize, + &volumePropValues); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: GetUsbHsVolumePropertyValues: failed to retrieve KSPROPERTY_VALUES, 0x%x", ntStatus)), + Done); + + m_MicVolumePropValues = volumePropValues; + + // Mic volume. + ntStatus = GetUsbHsMicVolume(0, &volume); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: GetUsbHsMicVolume: failed, 0x%x", ntStatus)), + Done); + + m_MicVolumeLevel = volume; + } + + if (m_pMicDescriptor->Capabilities.Mute) + { + PKSPROPERTY_DESCRIPTION mutePropValues = NULL; + ULONG ulMutePropValuesSize = 0; + LONG mute = 0; + + // Mute settings. + ntStatus = GetUsbHsMutePropertyValues( + m_MicEpIndex, + &ulMutePropValuesSize, + &mutePropValues); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: GetUsbHsMutePropertyValues: failed to retrieve KSPROPERTY_DESCRIPTION, 0x%x", ntStatus)), + Done); + + m_MicMutePropValues = mutePropValues; + m_ulMicMutePropValuesSize = ulMutePropValuesSize; + + // Mic mute. + ntStatus = GetUsbHsMicMute(0, &mute); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: GetUsbHsMicMute: failed, 0x%x", ntStatus)), + Done); + + m_MicMute = mute; + } + + // Mic Formats + PKSDATAFORMAT micFormatsArray[SIZEOF_ARRAY(UsbHsMicSupportedDeviceFormats)]; + for (ULONG i = 0; i < SIZEOF_ARRAY(UsbHsMicSupportedDeviceFormats); i++) + { + micFormatsArray[i] = (PKSDATAFORMAT)(&UsbHsMicSupportedDeviceFormats[i]); + } + SIDEBANDAUDIO_SUPPORTED_FORMATS micDeviceFormats = + { + sizeof(SIDEBANDAUDIO_SUPPORTED_FORMATS), + m_MicEpIndex, + SIZEOF_ARRAY(UsbHsMicSupportedDeviceFormats), + micFormatsArray + }; + + ntStatus = GetUsbHsEndpointFormatsIntersection(&micDeviceFormats, &m_pMicSupportedFormatsIntersection); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: GetUsbHsEndpointFormats: failed for mic, 0x%x", ntStatus)), + Done); + + ntStatus = CreateCustomEndpointMinipair( + g_UsbHsCaptureEndpoints[0], + &m_pMicDescriptor->FriendlyName, + &m_pMicDescriptor->Category, + &m_MicMiniports); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: CreateCustomEndpointMinipair for Capture: failed, 0x%x", ntStatus)), + Done); + m_MicMiniports->TopoName = m_MicTopologyNameBuffer; + m_MicMiniports->WaveName = m_MicWaveNameBuffer; + + // Verify format compat + ntStatus = VerifyEndpointFormatCompatibility( + m_pMicSupportedFormatsIntersection, + UsbHsMicSupportedDeviceFormats, + SIZEOF_ARRAY(UsbHsMicSupportedDeviceFormats), + &m_MicSelectedFormat); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: VerifyEndpointFormatCompatibility : failed for mic, 0x%x", ntStatus)), + Done); + + ASSERT(m_MicMiniports != NULL); + _Analysis_assume_(m_MicMiniports != NULL); + + ntStatus = m_Adapter->InstallEndpointFilters( + NULL, + m_MicMiniports, + PSIDEBANDDEVICECOMMON(this), + &m_UnknownMicTopology, + &m_UnknownMicWave, NULL, NULL + ); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: InstallEndpointCaptureFilters (USB Headset): failed, 0x%x", ntStatus)), + Done); + + // + // Pend status notifications. + // + + if (m_pMicDescriptor->Capabilities.Volume) + { + // Volume mic status. + ntStatus = EnableUsbHsMicVolumeStatusNotification(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: EnableUsbHsMicVolumeStatusNotification: failed, 0x%x", ntStatus)), + Done); + } + } + + if (speakerEndpoints > 0 && micEndpoints > 0) + { + ntStatus = m_Adapter->NotifyEndpointPair( + m_SpeakerTopologyNameBuffer, + SIZEOF_ARRAY(m_SpeakerTopologyNameBuffer), + KSPIN_TOPO_LINEOUT_DEST, + m_MicTopologyNameBuffer, + SIZEOF_ARRAY(m_MicTopologyNameBuffer), + KSPIN_TOPO_MIC_ELEMENTS + ); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: NotifyEndpointPair: failed, 0x%x", ntStatus)), + Done); + } + + // Set Sideband claimed + ntStatus = SetSidebandClaimed(TRUE); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("%!FUNC!: SetSidebandClaimed TRUE: failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + ntStatus = SetSidebandClaimed(FALSE); + InterlockedExchange((PLONG)&m_State, eUsbHsStateFailed); + } +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::CreateCustomEndpointMinipair +( + _In_ PENDPOINT_MINIPAIR pBaseMinipair, + _In_ PUNICODE_STRING FriendlyName, + _In_ PGUID pCategory, + _Outptr_ PENDPOINT_MINIPAIR *ppCustomMinipair +) +{ + NTSTATUS ntStatus; + PENDPOINT_MINIPAIR pNewMinipair = NULL; + SYSVAD_DEVPROPERTY* pProperties = NULL; + PPCFILTER_DESCRIPTOR pNewTopoFilterDesc = NULL; + PPCPIN_DESCRIPTOR pNewTopoPins = NULL; + ULONG cProperties; + ULONG cTopoPins; + + PAGED_CODE(); + + // + // This routine will add one more property to whatever the base minipair describes for the topo filter interface properties + // It will also allocate and set up custom filter and pin descriptors to allow changing the KSNODETYPE for the usb device + // + cTopoPins = pBaseMinipair->TopoDescriptor->PinCount; + cProperties = pBaseMinipair->TopoInterfacePropertyCount + 1; + pProperties = (SYSVAD_DEVPROPERTY*)ExAllocatePoolWithTag(NonPagedPoolNx, cProperties * sizeof(SYSVAD_DEVPROPERTY), SYSVAD_POOLTAG); + pNewMinipair = (ENDPOINT_MINIPAIR*)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(ENDPOINT_MINIPAIR), SYSVAD_POOLTAG); + pNewTopoFilterDesc = (PCFILTER_DESCRIPTOR*)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(PCFILTER_DESCRIPTOR), SYSVAD_POOLTAG); + pNewTopoPins = (PCPIN_DESCRIPTOR*)ExAllocatePoolWithTag(NonPagedPoolNx, cTopoPins * sizeof(PCPIN_DESCRIPTOR), SYSVAD_POOLTAG); + + if ((pProperties != NULL) && (pNewMinipair != NULL) && (pNewTopoFilterDesc != NULL) && (pNewTopoPins != NULL)) + { + SYSVAD_DEVPROPERTY *pLastProperty; + + // Copy base minipair properties to new property list + if (pBaseMinipair->TopoInterfacePropertyCount > 0) + { + RtlCopyMemory(pProperties, pBaseMinipair->TopoInterfaceProperties, (cProperties - 1) * sizeof(SYSVAD_DEVPROPERTY)); + } + + // Add friendly name property to the list + NT_ASSERT(FriendlyName->Length + sizeof(UNICODE_NULL) <= FriendlyName->MaximumLength); // Assuming NULL terminated string + pLastProperty = &pProperties[cProperties - 1]; + pLastProperty->PropertyKey = &DEVPKEY_DeviceInterface_FriendlyName; + pLastProperty->Type = DEVPROP_TYPE_STRING_INDIRECT; + pLastProperty->BufferSize = FriendlyName->Length + sizeof(UNICODE_NULL); + pLastProperty->Buffer = FriendlyName->Buffer; + + // Copy base minipair structure + RtlCopyMemory(pNewMinipair, pBaseMinipair, sizeof(ENDPOINT_MINIPAIR)); + + RtlCopyMemory(pNewTopoFilterDesc, pBaseMinipair->TopoDescriptor, sizeof(PCFILTER_DESCRIPTOR)); + RtlCopyMemory(pNewTopoPins, pBaseMinipair->TopoDescriptor->Pins, cTopoPins * sizeof(PCPIN_DESCRIPTOR)); + + pNewTopoFilterDesc->Pins = pNewTopoPins; + pNewMinipair->TopoDescriptor = pNewTopoFilterDesc; + + // Update it to point to new property list + pNewMinipair->TopoInterfacePropertyCount = cProperties; + pNewMinipair->TopoInterfaceProperties = pProperties; + + ntStatus = UpdateCustomEndpointCategory(pNewTopoFilterDesc, pNewTopoPins, pCategory); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("%!FUNC!: UpdateCustomEndpointCategory failed, 0x%x", ntStatus)); + } + else + { + *ppCustomMinipair = pNewMinipair; + + pProperties = NULL; + pNewMinipair = NULL; + pNewTopoFilterDesc = NULL; + pNewTopoPins = NULL; + + ntStatus = STATUS_SUCCESS; + } + } + else + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + if (pProperties != NULL) + { + ExFreePoolWithTag(pProperties, SYSVAD_POOLTAG); + } + if (pNewMinipair != NULL) + { + ExFreePoolWithTag(pNewMinipair, SYSVAD_POOLTAG); + } + if (pNewTopoFilterDesc != NULL) + { + ExFreePoolWithTag(pNewTopoFilterDesc, SYSVAD_POOLTAG); + } + if (pNewTopoPins != NULL) + { + ExFreePoolWithTag(pNewTopoPins, SYSVAD_POOLTAG); + } + + return ntStatus; +} +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UsbHsDevice::UpdateCustomEndpointCategory +( + _In_ PPCFILTER_DESCRIPTOR pCustomMinipairTopoFilter, + _In_ PPCPIN_DESCRIPTOR pCustomMinipairTopoPins, + _In_ PGUID pCategory +) +{ + NTSTATUS ntStatus = STATUS_NOT_FOUND; + ULONG cPinCount = 0; + BOOL FoundCategoryAudio = FALSE; + BOOL FoundNodeType = FALSE; + + PAGED_CODE(); + + cPinCount = pCustomMinipairTopoFilter->PinCount; + + // Find the right pin: There should be two pins, one with Category KSCATEGORY_AUDIO, + // and one with a KSNODETYPE_* Category. We need to modify the KSNODETYPE category. + for (ULONG i = 0; i < cPinCount; ++i) + { + if (IsEqualGUID(*pCustomMinipairTopoPins[i].KsPinDescriptor.Category, KSCATEGORY_AUDIO)) + { + ASSERT(FoundCategoryAudio == FALSE); + if (FoundCategoryAudio) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + DPF(D_ERROR, ("%!FUNC!: KSCATEGORY_AUDIO found more than once, 0x%x", ntStatus)); + break; + } + + FoundCategoryAudio = TRUE; + continue; + } + + ASSERT(FoundNodeType == FALSE); + if (FoundNodeType) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + DPF(D_ERROR, ("%!FUNC!: Found more than one applicable Pin, 0x%x", ntStatus)); + break; + } + + pCustomMinipairTopoPins[i].KsPinDescriptor.Category = pCategory; + FoundNodeType = TRUE; + ntStatus = STATUS_SUCCESS; + } + + return ntStatus; +} +//============================================================================= +#pragma code_seg("PAGE") +VOID +UsbHsDevice::DeleteCustomEndpointMinipair +( + _In_ PENDPOINT_MINIPAIR pCustomMinipair +) +{ + PAGED_CODE(); + + if (pCustomMinipair != NULL) + { + if (pCustomMinipair->TopoInterfaceProperties != NULL) + { + ExFreePoolWithTag(const_cast<SYSVAD_DEVPROPERTY*>(pCustomMinipair->TopoInterfaceProperties), SYSVAD_POOLTAG); + pCustomMinipair->TopoInterfaceProperties = NULL; + } + if (pCustomMinipair->TopoDescriptor != NULL) + { + if (pCustomMinipair->TopoDescriptor->Pins != NULL) + { + ExFreePoolWithTag((PVOID)pCustomMinipair->TopoDescriptor->Pins, SYSVAD_POOLTAG); + pCustomMinipair->TopoDescriptor->Pins = NULL; + } + ExFreePoolWithTag(pCustomMinipair->TopoDescriptor, SYSVAD_POOLTAG); + pCustomMinipair->TopoDescriptor = NULL; + } + ExFreePoolWithTag(pCustomMinipair, SYSVAD_POOLTAG); + } +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +UsbHsDevice::Stop() +/*++ + +Routine Description: + + Asynchronously called to stop the audio device. + After returning from this function, there are no more async notifications + pending (volume, etc.). + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + eUsbHsState state = eUsbHsStateInvalid; + + state = (eUsbHsState) InterlockedExchange((PLONG)&m_State, eUsbHsStateStopping); + ASSERT(state == eUsbHsStateRunning || state == eUsbHsStateFailed); + UNREFERENCED_VAR(state); + + // + // Stop async notifications. + // + WdfIoTargetPurge(m_WdfIoTarget, WdfIoTargetPurgeIoAndWait); + + // + // Wait for work-item. + // + WdfWorkItemFlush(m_WorkItem); + + // + // Remove the topology and wave render filters. + // + if (m_UnknownSpeakerTopology || m_UnknownSpeakerWave) + { + ntStatus = m_Adapter->RemoveEndpointFilters( + m_SpeakerMiniports, + m_UnknownSpeakerTopology, + m_UnknownSpeakerWave); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("%!FUNC!: RemoveEndpointFilters (USB Headset 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, ("%!FUNC!: RemoveEndpointFilters (USB Headset 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, eUsbHsStateStopped); + +} + +#pragma code_seg("PAGE") +NTSTATUS UsbHsDevice::CreateFilterNames( + PUNICODE_STRING UsbHsDeviceSymbolicLinkName +) +/*++ + +Routine Description: + +Creates unique wave and topology filter reference names. + +Since each subdevice representing a USB sideband interface arrival needs to have a unique +reference string, this function generates a hash on 'UsbHsDeviceSymbolicLinkName' parameter +and appends to static interface strings. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[%!FUNC!]")); + + NTSTATUS status = STATUS_SUCCESS; + ULONG hashValue = 0; + + status = RtlHashUnicodeString(UsbHsDeviceSymbolicLinkName, TRUE, HASH_STRING_ALGORITHM_X65599, &hashValue); + IF_FAILED_ACTION_JUMP( + status, + DPF(D_ERROR, ("%!FUNC!: Failed to create KSCATEGORY_REALTIME wave interface: 0x%x", status)), + End); + + // create wave interfaces for speaker + RtlStringCbPrintfW(m_SpeakerWaveNameBuffer, sizeof(m_SpeakerWaveNameBuffer), L"%s-%lu", USBHS_SPEAKER_WAVE_NAME, hashValue); + RtlUnicodeStringInit(&m_SpeakerWaveRefString, m_SpeakerWaveNameBuffer); + + // create topology interfaces for speaker + RtlStringCbPrintfW(m_SpeakerTopologyNameBuffer, sizeof(m_SpeakerTopologyNameBuffer), L"%s-%lu", USBHS_SPEAKER_TOPO_NAME, hashValue); + RtlUnicodeStringInit(&m_SpeakerTopologyRefString, m_SpeakerTopologyNameBuffer); + + // create wave interfaces for mic + RtlStringCbPrintfW(m_MicWaveNameBuffer, sizeof(m_MicWaveNameBuffer), L"%s-%lu", USBHS_MIC_WAVE_NAME, hashValue); + RtlUnicodeStringInit(&m_MicWaveRefString, m_MicWaveNameBuffer); + + // create topology interfaces for mic + RtlStringCbPrintfW(m_MicTopologyNameBuffer, sizeof(m_MicTopologyNameBuffer), L"%s-%lu", USBHS_MIC_TOPO_NAME, hashValue); + RtlUnicodeStringInit(&m_MicTopologyRefString, m_MicTopologyNameBuffer); +End: + return status; +} +#endif // SYSVAD_USB_SIDEBAND |
