/* * PROJECT: ReactOS Universal Audio Class Driver * LICENSE: GPL - See COPYING in the top level directory * FILE: drivers/usb/usbaudio/pin.c * PURPOSE: USB Audio device driver. * PROGRAMMERS: * Johannes Anderwald (johannes.anderwald@reactos.org) */ #include "usbaudio.h" #define PACKET_COUNT 10 NTSTATUS GetMaxPacketSizeForInterface( IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, IN PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor, KSPIN_DATAFLOW DataFlow) { PUSB_COMMON_DESCRIPTOR CommonDescriptor; PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor; /* loop descriptors */ CommonDescriptor = (PUSB_COMMON_DESCRIPTOR)((ULONG_PTR)InterfaceDescriptor + InterfaceDescriptor->bLength); ASSERT(InterfaceDescriptor->bNumEndpoints > 0); while (CommonDescriptor) { if (CommonDescriptor->bDescriptorType == USB_ENDPOINT_DESCRIPTOR_TYPE) { EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)CommonDescriptor; return EndpointDescriptor->wMaxPacketSize; } if (CommonDescriptor->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE) { /* reached next interface descriptor */ break; } if ((ULONG_PTR)CommonDescriptor + CommonDescriptor->bLength >= ((ULONG_PTR)ConfigurationDescriptor + ConfigurationDescriptor->wTotalLength)) break; CommonDescriptor = (PUSB_COMMON_DESCRIPTOR)((ULONG_PTR)CommonDescriptor + CommonDescriptor->bLength); } /* default to 100 */ return 100; } NTSTATUS UsbAudioAllocCaptureUrbIso( IN USBD_PIPE_HANDLE PipeHandle, IN ULONG MaxPacketSize, IN PVOID Buffer, IN ULONG BufferLength, OUT PURB * OutUrb) { PURB Urb; ULONG UrbSize; ULONG Index; /* calculate urb size*/ UrbSize = GET_ISO_URB_SIZE(PACKET_COUNT); /* allocate urb */ Urb = AllocFunction(UrbSize); if (!Urb) { /* no memory */ return STATUS_INSUFFICIENT_RESOURCES; } /* init urb */ Urb->UrbIsochronousTransfer.Hdr.Function = URB_FUNCTION_ISOCH_TRANSFER; Urb->UrbIsochronousTransfer.Hdr.Length = UrbSize; Urb->UrbIsochronousTransfer.PipeHandle = PipeHandle; Urb->UrbIsochronousTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_IN | USBD_START_ISO_TRANSFER_ASAP; Urb->UrbIsochronousTransfer.TransferBufferLength = BufferLength; Urb->UrbIsochronousTransfer.TransferBuffer = Buffer; Urb->UrbIsochronousTransfer.NumberOfPackets = PACKET_COUNT; for (Index = 0; Index < PACKET_COUNT; Index++) { Urb->UrbIsochronousTransfer.IsoPacket[Index].Offset = Index * MaxPacketSize; } *OutUrb = Urb; return STATUS_SUCCESS; } NTSTATUS UsbAudioSetFormat( IN PKSPIN Pin) { PURB Urb; PUCHAR SampleRateBuffer; PPIN_CONTEXT PinContext; NTSTATUS Status; PKSDATAFORMAT_WAVEFORMATEX WaveFormatEx; /* allocate sample rate buffer */ SampleRateBuffer = AllocFunction(sizeof(ULONG)); if (!SampleRateBuffer) { /* no memory */ return STATUS_INSUFFICIENT_RESOURCES; } if (IsEqualGUIDAligned(&Pin->ConnectionFormat->MajorFormat, &KSDATAFORMAT_TYPE_AUDIO) && IsEqualGUIDAligned(&Pin->ConnectionFormat->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM) && IsEqualGUIDAligned(&Pin->ConnectionFormat->Specifier, &KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)) { WaveFormatEx = (PKSDATAFORMAT_WAVEFORMATEX)Pin->ConnectionFormat; SampleRateBuffer[2] = (WaveFormatEx->WaveFormatEx.nSamplesPerSec >> 16) & 0xFF; SampleRateBuffer[1] = (WaveFormatEx->WaveFormatEx.nSamplesPerSec >> 8) & 0xFF; SampleRateBuffer[0] = (WaveFormatEx->WaveFormatEx.nSamplesPerSec >> 0) & 0xFF; /* TODO: verify connection format */ } else { /* not supported yet*/ UNIMPLEMENTED; FreeFunction(SampleRateBuffer); return STATUS_INVALID_PARAMETER; } /* allocate urb */ Urb = AllocFunction(sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST)); if (!Urb) { /* no memory */ FreeFunction(SampleRateBuffer); return STATUS_INSUFFICIENT_RESOURCES; } /* get pin context */ PinContext = Pin->Context; /* FIXME: determine controls and format urb */ UsbBuildVendorRequest(Urb, URB_FUNCTION_CLASS_ENDPOINT, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST), USBD_TRANSFER_DIRECTION_OUT, 0, 0x01, // SET_CUR 0x100, PinContext->DeviceExtension->InterfaceInfo->Pipes[0].EndpointAddress, SampleRateBuffer, NULL, 3, NULL); /* submit urb */ Status = SubmitUrbSync(PinContext->LowerDevice, Urb); DPRINT1("USBAudioPinSetDataFormat Pin %p Status %x\n", Pin, Status); FreeFunction(Urb); FreeFunction(SampleRateBuffer); return Status; } NTSTATUS USBAudioSelectAudioStreamingInterface( IN PKSPIN Pin, IN PPIN_CONTEXT PinContext, IN PDEVICE_EXTENSION DeviceExtension, IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, IN ULONG FormatDescriptorIndex) { PURB Urb; PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor; NTSTATUS Status; ULONG Found, Index; PUSB_AUDIO_STREAMING_INTERFACE_DESCRIPTOR StreamingInterfaceDescriptor; PUSB_AUDIO_CONTROL_INPUT_TERMINAL_DESCRIPTOR TerminalDescriptor = NULL; /* search for terminal descriptor of that irp sink / irp source */ TerminalDescriptor = UsbAudioGetStreamingTerminalDescriptorByIndex(DeviceExtension->ConfigurationDescriptor, Pin->Id); ASSERT(TerminalDescriptor != NULL); /* grab interface descriptor */ InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(ConfigurationDescriptor, ConfigurationDescriptor, -1, -1, USB_DEVICE_CLASS_AUDIO, -1, -1); if (!InterfaceDescriptor) { /* no such interface */ return STATUS_INVALID_PARAMETER; } Found = FALSE; Index = 0; /* selects the interface which has an audio streaming interface descriptor associated to the input / output terminal at the given format index */ while (InterfaceDescriptor != NULL) { if (InterfaceDescriptor->bInterfaceSubClass == 0x02 /* AUDIO_STREAMING */ && InterfaceDescriptor->bNumEndpoints > 0) { StreamingInterfaceDescriptor = (PUSB_AUDIO_STREAMING_INTERFACE_DESCRIPTOR)USBD_ParseDescriptors(ConfigurationDescriptor, ConfigurationDescriptor->wTotalLength, InterfaceDescriptor, USB_AUDIO_CONTROL_TERMINAL_DESCRIPTOR_TYPE); if (StreamingInterfaceDescriptor != NULL) { ASSERT(StreamingInterfaceDescriptor->bDescriptorSubtype == 0x01); ASSERT(StreamingInterfaceDescriptor->wFormatTag == WAVE_FORMAT_PCM); if (StreamingInterfaceDescriptor->bTerminalLink == TerminalDescriptor->bTerminalID) { if (FormatDescriptorIndex == Index) { Found = TRUE; break; } Index++; } } } InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(ConfigurationDescriptor, (PVOID)((ULONG_PTR)InterfaceDescriptor + InterfaceDescriptor->bLength), -1, -1, USB_DEVICE_CLASS_AUDIO, -1, -1); } if (!Found) { /* no such interface */ DPRINT1("No Interface found\n"); return STATUS_INVALID_PARAMETER; } Urb = AllocFunction(GET_SELECT_INTERFACE_REQUEST_SIZE(InterfaceDescriptor->bNumEndpoints)); if (!Urb) { /* no memory */ return STATUS_INSUFFICIENT_RESOURCES; } /* now prepare interface urb */ UsbBuildSelectInterfaceRequest(Urb, GET_SELECT_INTERFACE_REQUEST_SIZE(InterfaceDescriptor->bNumEndpoints), DeviceExtension->ConfigurationHandle, InterfaceDescriptor->bInterfaceNumber, InterfaceDescriptor->bAlternateSetting); /* now select the interface */ Status = SubmitUrbSync(DeviceExtension->LowerDevice, Urb); DPRINT1("USBAudioSelectAudioStreamingInterface Status %x UrbStatus %x InterfaceNumber %x AlternateSetting %x\n", Status, Urb->UrbSelectInterface.Hdr.Status, InterfaceDescriptor->bInterfaceNumber, InterfaceDescriptor->bAlternateSetting); /* did it succeeed */ if (NT_SUCCESS(Status)) { /* free old interface info */ if (DeviceExtension->InterfaceInfo) { /* free old info */ FreeFunction(DeviceExtension->InterfaceInfo); } /* alloc interface info */ DeviceExtension->InterfaceInfo = AllocFunction(Urb->UrbSelectInterface.Interface.Length); if (DeviceExtension->InterfaceInfo == NULL) { /* no memory */ FreeFunction(Urb); return STATUS_INSUFFICIENT_RESOURCES; } /* copy interface info */ RtlCopyMemory(DeviceExtension->InterfaceInfo, &Urb->UrbSelectInterface.Interface, Urb->UrbSelectInterface.Interface.Length); PinContext->InterfaceDescriptor = InterfaceDescriptor; } /* free urb */ FreeFunction(Urb); return Status; } VOID NTAPI CaptureGateOnWorkItem( _In_ PVOID Context) { PKSPIN Pin; PPIN_CONTEXT PinContext; PKSGATE Gate; ULONG Count; /* get pin */ Pin = Context; /* get pin context */ PinContext = Pin->Context; do { /* acquire processing mutex */ KsPinAcquireProcessingMutex(Pin); /* get pin control gate */ Gate = KsPinGetAndGate(Pin); /* turn input on */ KsGateTurnInputOn(Gate); /* schedule processing */ KsPinAttemptProcessing(Pin, TRUE); /* release processing mutex */ KsPinReleaseProcessingMutex(Pin); /* decrement worker count */ Count = KsDecrementCountedWorker(PinContext->CaptureWorker); } while (Count); } NTSTATUS RenderInitializeUrbAndIrp( IN PKSPIN Pin, IN PPIN_CONTEXT PinContext, IN OUT PIRP Irp, IN PVOID TransferBuffer, IN ULONG TransferBufferSize, IN ULONG PacketSize) { ULONG Index, PacketCount; PURB Urb; PIO_STACK_LOCATION IoStack; /* initialize irp */ IoInitializeIrp(Irp, IoSizeOfIrp(PinContext->DeviceExtension->LowerDevice->StackSize), PinContext->DeviceExtension->LowerDevice->StackSize); /* set irp members */ Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; Irp->IoStatus.Information = 0; Irp->Flags = 0; Irp->UserBuffer = NULL; /* init stack location */ IoStack = IoGetNextIrpStackLocation(Irp); IoStack->DeviceObject = PinContext->DeviceExtension->LowerDevice; IoStack->Parameters.Others.Argument2 = NULL; IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; /* set completion routine */ IoSetCompletionRoutine(Irp, UsbAudioRenderComplete, Pin, TRUE, TRUE, TRUE); /* calculate packet count */ PacketCount = TransferBufferSize / PacketSize; ASSERT(TransferBufferSize % PacketSize == 0); /* lets allocate urb */ Urb = (PURB)AllocFunction(GET_ISO_URB_SIZE(PacketCount)); if (!Urb) { /* no memory */ return STATUS_INSUFFICIENT_RESOURCES; } /* init urb */ Urb->UrbIsochronousTransfer.Hdr.Function = URB_FUNCTION_ISOCH_TRANSFER; Urb->UrbIsochronousTransfer.Hdr.Length = GET_ISO_URB_SIZE(PacketCount); Urb->UrbIsochronousTransfer.PipeHandle = PinContext->DeviceExtension->InterfaceInfo->Pipes[0].PipeHandle; Urb->UrbIsochronousTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_OUT | USBD_START_ISO_TRANSFER_ASAP; Urb->UrbIsochronousTransfer.TransferBufferLength = TransferBufferSize; Urb->UrbIsochronousTransfer.TransferBuffer = TransferBuffer; Urb->UrbIsochronousTransfer.NumberOfPackets = PacketCount; Urb->UrbIsochronousTransfer.StartFrame = 0; for (Index = 0; Index < PacketCount; Index++) { Urb->UrbIsochronousTransfer.IsoPacket[Index].Offset = Index * PacketSize; } /* store urb */ IoStack->Parameters.Others.Argument1 = Urb; Irp->Tail.Overlay.DriverContext[0] = Urb; /* done */ return STATUS_SUCCESS; } VOID CaptureInitializeUrbAndIrp( IN PKSPIN Pin, IN PIRP Irp) { PIO_STACK_LOCATION IoStack; PURB Urb; PUCHAR TransferBuffer; ULONG Index; PPIN_CONTEXT PinContext; /* get pin context */ PinContext = Pin->Context; /* backup urb and transferbuffer */ Urb = Irp->Tail.Overlay.DriverContext[0]; TransferBuffer = Urb->UrbIsochronousTransfer.TransferBuffer; /* initialize irp */ IoInitializeIrp(Irp, IoSizeOfIrp(PinContext->DeviceExtension->LowerDevice->StackSize), PinContext->DeviceExtension->LowerDevice->StackSize); Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; Irp->IoStatus.Information = 0; Irp->Flags = 0; Irp->UserBuffer = NULL; Irp->Tail.Overlay.DriverContext[0] = Urb; Irp->Tail.Overlay.DriverContext[1] = NULL; /* init stack location */ IoStack = IoGetNextIrpStackLocation(Irp); IoStack->DeviceObject = PinContext->DeviceExtension->LowerDevice; IoStack->Parameters.Others.Argument1 = Urb; IoStack->Parameters.Others.Argument2 = NULL; IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; IoSetCompletionRoutine(Irp, UsbAudioCaptureComplete, Pin, TRUE, TRUE, TRUE); RtlZeroMemory(Urb, GET_ISO_URB_SIZE(PACKET_COUNT)); /* init urb */ Urb->UrbIsochronousTransfer.Hdr.Function = URB_FUNCTION_ISOCH_TRANSFER; Urb->UrbIsochronousTransfer.Hdr.Length = GET_ISO_URB_SIZE(10); Urb->UrbIsochronousTransfer.PipeHandle = PinContext->DeviceExtension->InterfaceInfo->Pipes[0].PipeHandle; Urb->UrbIsochronousTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_IN | USBD_START_ISO_TRANSFER_ASAP; Urb->UrbIsochronousTransfer.TransferBufferLength = PinContext->DeviceExtension->InterfaceInfo->Pipes[0].MaximumPacketSize * 10; Urb->UrbIsochronousTransfer.TransferBuffer = TransferBuffer; Urb->UrbIsochronousTransfer.NumberOfPackets = PACKET_COUNT; Urb->UrbIsochronousTransfer.StartFrame = 0; for (Index = 0; Index < PACKET_COUNT; Index++) { Urb->UrbIsochronousTransfer.IsoPacket[Index].Offset = Index * PinContext->DeviceExtension->InterfaceInfo->Pipes[0].MaximumPacketSize; } } VOID NTAPI CaptureAvoidPipeStarvationWorker( _In_ PVOID Context) { PKSPIN Pin; PPIN_CONTEXT PinContext; KIRQL OldLevel; PLIST_ENTRY CurEntry; PIRP Irp; /* get pin */ Pin = Context; /* get pin context */ PinContext = Pin->Context; /* acquire spin lock */ KeAcquireSpinLock(&PinContext->IrpListLock, &OldLevel); if (!IsListEmpty(&PinContext->IrpListHead)) { /* sanity check */ ASSERT(!IsListEmpty(&PinContext->IrpListHead)); /* remove entry from list */ CurEntry = RemoveHeadList(&PinContext->IrpListHead); /* release lock */ KeReleaseSpinLock(&PinContext->IrpListLock, OldLevel); /* get irp offset */ Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry); /* reinitialize irp and urb */ CaptureInitializeUrbAndIrp(Pin, Irp); KsDecrementCountedWorker(PinContext->StarvationWorker); /* call driver */ IoCallDriver(PinContext->DeviceExtension->LowerDevice, Irp); } else { /* release lock */ KeReleaseSpinLock(&PinContext->IrpListLock, OldLevel); KsDecrementCountedWorker(PinContext->StarvationWorker); } } NTSTATUS InitCapturePin( IN PKSPIN Pin) { NTSTATUS Status; ULONG Index; ULONG BufferSize; ULONG MaximumPacketSize; PIRP Irp; PURB Urb; PPIN_CONTEXT PinContext; PIO_STACK_LOCATION IoStack; PKSALLOCATOR_FRAMING_EX Framing; PKSGATE Gate; /* set sample rate */ Status = UsbAudioSetFormat(Pin); if (!NT_SUCCESS(Status)) { /* failed */ return Status; } /* get pin context */ PinContext = Pin->Context; /* lets get maximum packet size */ MaximumPacketSize = GetMaxPacketSizeForInterface(PinContext->DeviceExtension->ConfigurationDescriptor, PinContext->InterfaceDescriptor, Pin->DataFlow); /* initialize work item for capture worker */ ExInitializeWorkItem(&PinContext->CaptureWorkItem, CaptureGateOnWorkItem, (PVOID)Pin); /* register worker */ Status = KsRegisterCountedWorker(CriticalWorkQueue, &PinContext->CaptureWorkItem, &PinContext->CaptureWorker); if (!NT_SUCCESS(Status)) { /* failed */ return Status; } /* initialize work item */ ExInitializeWorkItem(&PinContext->StarvationWorkItem, CaptureAvoidPipeStarvationWorker, (PVOID)Pin); /* register worker */ Status = KsRegisterCountedWorker(CriticalWorkQueue, &PinContext->StarvationWorkItem, &PinContext->StarvationWorker); if (!NT_SUCCESS(Status)) { /* failed */ KsUnregisterWorker(PinContext->CaptureWorker); } /* lets edit framing struct */ Framing = (PKSALLOCATOR_FRAMING_EX)Pin->Descriptor->AllocatorFraming; Framing->FramingItem[0].PhysicalRange.MinFrameSize = Framing->FramingItem[0].PhysicalRange.MaxFrameSize = Framing->FramingItem[0].FramingRange.Range.MinFrameSize = Framing->FramingItem[0].FramingRange.Range.MaxFrameSize = MaximumPacketSize; /* calculate buffer size 8 irps * 10 iso packets * max packet size */ BufferSize = 8 * PACKET_COUNT * MaximumPacketSize; /* allocate pin capture buffer */ PinContext->BufferSize = BufferSize; PinContext->Buffer = AllocFunction(BufferSize); if (!PinContext->Buffer) { /* no memory */ return STATUS_INSUFFICIENT_RESOURCES; } KsAddItemToObjectBag(Pin->Bag, PinContext->Buffer, ExFreePool); /* init irps */ for (Index = 0; Index < 8; Index++) { /* allocate irp */ Irp = AllocFunction(IoSizeOfIrp(PinContext->DeviceExtension->LowerDevice->StackSize)); if (!Irp) { /* no memory */ return STATUS_INSUFFICIENT_RESOURCES; } /* initialize irp */ IoInitializeIrp(Irp, IoSizeOfIrp(PinContext->DeviceExtension->LowerDevice->StackSize), PinContext->DeviceExtension->LowerDevice->StackSize); Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; Irp->IoStatus.Information = 0; Irp->Flags = 0; Irp->UserBuffer = NULL; IoStack = IoGetNextIrpStackLocation(Irp); IoStack->DeviceObject = PinContext->DeviceExtension->LowerDevice; IoStack->Parameters.Others.Argument2 = NULL; IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; IoSetCompletionRoutine(Irp, UsbAudioCaptureComplete, Pin, TRUE, TRUE, TRUE); /* insert into irp list */ InsertTailList(&PinContext->IrpListHead, &Irp->Tail.Overlay.ListEntry); /* add to object bag*/ KsAddItemToObjectBag(Pin->Bag, Irp, ExFreePool); /* FIXME select correct pipe handle */ Status = UsbAudioAllocCaptureUrbIso(PinContext->DeviceExtension->InterfaceInfo->Pipes[0].PipeHandle, MaximumPacketSize, &PinContext->Buffer[MaximumPacketSize * PACKET_COUNT * Index], MaximumPacketSize * PACKET_COUNT, &Urb); DPRINT1("InitCapturePin Irp %p Urb %p\n", Irp, Urb); if (NT_SUCCESS(Status)) { /* get next stack location */ IoStack = IoGetNextIrpStackLocation(Irp); /* store urb */ IoStack->Parameters.Others.Argument1 = Urb; Irp->Tail.Overlay.DriverContext[0] = Urb; } else { /* failed */ return Status; } } /* get process control gate */ Gate = KsPinGetAndGate(Pin); /* turn input off */ KsGateTurnInputOff(Gate); return Status; } NTSTATUS InitStreamPin( IN PKSPIN Pin) { ULONG Index; PIRP Irp; PPIN_CONTEXT PinContext; PKSDATAFORMAT_WAVEFORMATEX WaveFormatEx; PIO_STACK_LOCATION IoStack; DPRINT1("InitStreamPin\n"); /* get pin context */ PinContext = Pin->Context; /* allocate 1 sec buffer */ WaveFormatEx = (PKSDATAFORMAT_WAVEFORMATEX)Pin->ConnectionFormat; PinContext->Buffer = AllocFunction(WaveFormatEx->WaveFormatEx.nAvgBytesPerSec); if (!PinContext->Buffer) { /* no memory */ return STATUS_INSUFFICIENT_RESOURCES; } /* init buffer size*/ PinContext->BufferSize = WaveFormatEx->WaveFormatEx.nAvgBytesPerSec; PinContext->BufferOffset = 0; PinContext->BufferLength = 0; /* init irps */ for (Index = 0; Index < 12; Index++) { /* allocate irp */ Irp = AllocFunction(IoSizeOfIrp(PinContext->DeviceExtension->LowerDevice->StackSize)); if (!Irp) { /* no memory */ return STATUS_INSUFFICIENT_RESOURCES; } DPRINT1("InitStreamPin Irp %p\n", Irp); /* initialize irp */ IoInitializeIrp(Irp, IoSizeOfIrp(PinContext->DeviceExtension->LowerDevice->StackSize), PinContext->DeviceExtension->LowerDevice->StackSize); Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; Irp->IoStatus.Information = 0; Irp->Flags = 0; Irp->UserBuffer = NULL; IoStack = IoGetNextIrpStackLocation(Irp); IoStack->DeviceObject = PinContext->DeviceExtension->LowerDevice; IoStack->Parameters.Others.Argument2 = NULL; IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; IoSetCompletionRoutine(Irp, UsbAudioRenderComplete, Pin, TRUE, TRUE, TRUE); /* insert into irp list */ InsertTailList(&PinContext->IrpListHead, &Irp->Tail.Overlay.ListEntry); /* add to object bag*/ KsAddItemToObjectBag(Pin->Bag, Irp, ExFreePool); } return STATUS_SUCCESS; } ULONG GetDataRangeIndexForFormat( IN PKSDATARANGE ConnectionFormat, IN const PKSDATARANGE * DataRanges, IN ULONG DataRangesCount) { ULONG Index; PKSDATARANGE CurrentDataRange; PKSDATARANGE_AUDIO CurrentAudioDataRange; PKSDATAFORMAT_WAVEFORMATEX ConnectionDataFormat; if (ConnectionFormat->FormatSize != sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATEX)) { /* unsupported connection format */ DPRINT1("GetDataRangeIndexForFormat expected KSDATARANGE_AUDIO\n"); return MAXULONG; } /* cast to right type */ ConnectionDataFormat = (PKSDATAFORMAT_WAVEFORMATEX)ConnectionFormat; for (Index = 0; Index < DataRangesCount; Index++) { /* get current data range */ CurrentDataRange = DataRanges[Index]; /* compare guids */ if (!IsEqualGUIDAligned(&CurrentDataRange->MajorFormat, &ConnectionFormat->MajorFormat) || !IsEqualGUIDAligned(&CurrentDataRange->SubFormat, &ConnectionFormat->SubFormat) || !IsEqualGUIDAligned(&CurrentDataRange->Specifier, &ConnectionFormat->Specifier)) { /* no match */ continue; } /* all pin data ranges are KSDATARANGE_AUDIO */ CurrentAudioDataRange = (PKSDATARANGE_AUDIO)CurrentDataRange; /* check if number of channel match */ if (CurrentAudioDataRange->MaximumChannels != ConnectionDataFormat->WaveFormatEx.nChannels) { /* number of channels mismatch */ continue; } if (CurrentAudioDataRange->MinimumSampleFrequency > ConnectionDataFormat->WaveFormatEx.nSamplesPerSec) { /* channel frequency too low */ continue; } if (CurrentAudioDataRange->MaximumSampleFrequency < ConnectionDataFormat->WaveFormatEx.nSamplesPerSec) { /* channel frequency too high */ continue; } /* FIXME add checks for bitrate / sample size etc */ return Index; } /* no datarange found */ return MAXULONG; } NTSTATUS NTAPI USBAudioPinCreate( _In_ PKSPIN Pin, _In_ PIRP Irp) { PKSFILTER Filter; PFILTER_CONTEXT FilterContext; PPIN_CONTEXT PinContext; NTSTATUS Status; ULONG FormatIndex; Filter = KsPinGetParentFilter(Pin); if (Filter == NULL) { /* invalid parameter */ return STATUS_INVALID_PARAMETER; } /* get filter context */ FilterContext = Filter->Context; /* allocate pin context */ PinContext = AllocFunction(sizeof(PIN_CONTEXT)); if (!PinContext) { /* no memory*/ return STATUS_INSUFFICIENT_RESOURCES; } /* init pin context */ PinContext->DeviceExtension = FilterContext->DeviceExtension; PinContext->LowerDevice = FilterContext->LowerDevice; InitializeListHead(&PinContext->IrpListHead); InitializeListHead(&PinContext->DoneIrpListHead); KeInitializeSpinLock(&PinContext->IrpListLock); /* store pin context*/ Pin->Context = PinContext; /* lets edit allocator framing struct */ Status = _KsEdit(Pin->Bag, (PVOID*)&Pin->Descriptor, sizeof(KSPIN_DESCRIPTOR_EX), sizeof(KSPIN_DESCRIPTOR_EX), USBAUDIO_TAG); if (NT_SUCCESS(Status)) { Status = _KsEdit(Pin->Bag, (PVOID*)&Pin->Descriptor->AllocatorFraming, sizeof(KSALLOCATOR_FRAMING_EX), sizeof(KSALLOCATOR_FRAMING_EX), USBAUDIO_TAG); ASSERT(Status == STATUS_SUCCESS); } /* choose correct dataformat */ FormatIndex = GetDataRangeIndexForFormat(Pin->ConnectionFormat, Pin->Descriptor->PinDescriptor.DataRanges, Pin->Descriptor->PinDescriptor.DataRangesCount); if (FormatIndex == MAXULONG) { /* no format match */ return STATUS_NO_MATCH; } /* select streaming interface */ Status = USBAudioSelectAudioStreamingInterface(Pin, PinContext, PinContext->DeviceExtension, PinContext->DeviceExtension->ConfigurationDescriptor, FormatIndex); if (!NT_SUCCESS(Status)) { /* failed */ DPRINT1("USBAudioSelectAudioStreamingInterface failed with %x\n", Status); return Status; } if (Pin->DataFlow == KSPIN_DATAFLOW_OUT) { /* init capture pin */ Status = InitCapturePin(Pin); } else { /* audio streaming pin*/ Status = InitStreamPin(Pin); } return Status; } NTSTATUS NTAPI USBAudioPinClose( _In_ PKSPIN Pin, _In_ PIRP Irp) { UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; } NTSTATUS NTAPI UsbAudioRenderComplete( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) { PKSPIN Pin; PPIN_CONTEXT PinContext; KIRQL OldLevel; PKSSTREAM_POINTER StreamPointerClone; NTSTATUS Status; PURB Urb; /* get pin context */ Pin = Context; PinContext = Pin->Context; /* get status */ Status = Irp->IoStatus.Status; /* get streampointer */ StreamPointerClone = Irp->Tail.Overlay.DriverContext[1]; /* get urb */ Urb = Irp->Tail.Overlay.DriverContext[0]; /* and free it */ FreeFunction(Urb); /* acquire lock */ KeAcquireSpinLock(&PinContext->IrpListLock, &OldLevel); /* insert entry into ready list */ InsertTailList(&PinContext->IrpListHead, &Irp->Tail.Overlay.ListEntry); /* release lock */ KeReleaseSpinLock(&PinContext->IrpListLock, OldLevel); if (!NT_SUCCESS(Status) && StreamPointerClone) { /* set status code because it failed */ KsStreamPointerSetStatusCode(StreamPointerClone, STATUS_DEVICE_DATA_ERROR); DPRINT1("UsbAudioRenderComplete failed with %x\n", Status); } if (StreamPointerClone) { /* lets delete the stream pointer clone */ KsStreamPointerDelete(StreamPointerClone); } /* done */ return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS NTAPI UsbAudioCaptureComplete( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) { PKSPIN Pin; PPIN_CONTEXT PinContext; KIRQL OldLevel; PURB Urb; /* get pin context */ Pin = Context; PinContext = Pin->Context; /* get urb */ Urb = Irp->Tail.Overlay.DriverContext[0]; /* acquire lock */ KeAcquireSpinLock(&PinContext->IrpListLock, &OldLevel); if (!NT_SUCCESS(Urb->UrbIsochronousTransfer.Hdr.Status)) { //DPRINT("UsbAudioCaptureComplete Irp %p Urb %p Status %x Packet Status %x\n", Irp, Urb, Urb->UrbIsochronousTransfer.Hdr.Status, Urb->UrbIsochronousTransfer.IsoPacket[0].Status); /* insert entry into ready list */ InsertTailList(&PinContext->IrpListHead, &Irp->Tail.Overlay.ListEntry); /* release lock */ KeReleaseSpinLock(&PinContext->IrpListLock, OldLevel); KsIncrementCountedWorker(PinContext->StarvationWorker); } else { /* insert entry into done list */ InsertTailList(&PinContext->DoneIrpListHead, &Irp->Tail.Overlay.ListEntry); /* release lock */ KeReleaseSpinLock(&PinContext->IrpListLock, OldLevel); KsIncrementCountedWorker(PinContext->CaptureWorker); } /* done */ return STATUS_MORE_PROCESSING_REQUIRED; } PIRP PinGetIrpFromReadyList( IN PKSPIN Pin) { PPIN_CONTEXT PinContext; PLIST_ENTRY CurEntry; KIRQL OldLevel; PIRP Irp = NULL; /* get pin context */ PinContext = Pin->Context; /* acquire spin lock */ KeAcquireSpinLock(&PinContext->IrpListLock, &OldLevel); if (!IsListEmpty(&PinContext->IrpListHead)) { /* remove entry from list */ CurEntry = RemoveHeadList(&PinContext->IrpListHead); /* get irp offset */ Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry); } /* release lock */ KeReleaseSpinLock(&PinContext->IrpListLock, OldLevel); return Irp; } NTSTATUS PinRenderProcess( IN PKSPIN Pin) { PKSSTREAM_POINTER LeadingStreamPointer; PKSSTREAM_POINTER CloneStreamPointer; NTSTATUS Status; PPIN_CONTEXT PinContext; ULONG PacketCount, TotalPacketSize, Offset; PKSDATAFORMAT_WAVEFORMATEX WaveFormatEx; PUCHAR TransferBuffer; PIRP Irp = NULL; //DPRINT1("PinRenderProcess\n"); LeadingStreamPointer = KsPinGetLeadingEdgeStreamPointer(Pin, KSSTREAM_POINTER_STATE_LOCKED); if (LeadingStreamPointer == NULL) { return STATUS_SUCCESS; } if (NULL == LeadingStreamPointer->StreamHeader->Data) { Status = KsStreamPointerAdvance(LeadingStreamPointer); DPRINT1("Advancing Streampointer\n"); } /* get pin context */ PinContext = Pin->Context; /* get irp from ready list */ Irp = PinGetIrpFromReadyList(Pin); if (!Irp) { /* no irps available */ DPRINT1("No irps available"); KsStreamPointerUnlock(LeadingStreamPointer, TRUE); return STATUS_SUCCESS; } /* clone stream pointer */ Status = KsStreamPointerClone(LeadingStreamPointer, NULL, 0, &CloneStreamPointer); if (!NT_SUCCESS(Status)) { /* failed */ KsStreamPointerUnlock(LeadingStreamPointer, TRUE); DPRINT1("Leaking Irp %p\n", Irp); return STATUS_SUCCESS; } /* calculate packet count */ /* FIXME support various sample rates */ WaveFormatEx = (PKSDATAFORMAT_WAVEFORMATEX)Pin->ConnectionFormat; TotalPacketSize = WaveFormatEx->WaveFormatEx.nAvgBytesPerSec / 1000; /* init transfer buffer*/ TransferBuffer = CloneStreamPointer->StreamHeader->Data; Offset = 0; /* are there bytes from previous request*/ if (PinContext->BufferLength) { ASSERT(PinContext->BufferLength < TotalPacketSize); /* calculate offset*/ Offset = TotalPacketSize - PinContext->BufferLength; if (PinContext->BufferOffset + TotalPacketSize >= PinContext->BufferSize) { RtlMoveMemory(PinContext->Buffer, &PinContext->Buffer[PinContext->BufferOffset - PinContext->BufferLength], PinContext->BufferLength); PinContext->BufferOffset = PinContext->BufferLength; } /* copy audio bytes */ RtlCopyMemory(&PinContext->Buffer[PinContext->BufferOffset], TransferBuffer, Offset); /* init irp*/ Status = RenderInitializeUrbAndIrp(Pin, PinContext, Irp, &PinContext->Buffer[PinContext->BufferOffset-PinContext->BufferLength], TotalPacketSize, TotalPacketSize); if (NT_SUCCESS(Status)) { /* render audio bytes */ Status = IoCallDriver(PinContext->LowerDevice, Irp); } else { ASSERT(FALSE); } PinContext->BufferLength = 0; PinContext->BufferOffset += Offset; /* get new irp from ready list */ Irp = PinGetIrpFromReadyList(Pin); ASSERT(Irp); } /* FIXME correct MaximumPacketSize ? */ PacketCount = (CloneStreamPointer->OffsetIn.Remaining - Offset) / TotalPacketSize; Status = RenderInitializeUrbAndIrp(Pin, PinContext, Irp, &TransferBuffer[Offset], PacketCount * TotalPacketSize, TotalPacketSize); if (NT_SUCCESS(Status)) { /* store in irp context */ Irp->Tail.Overlay.DriverContext[1] = CloneStreamPointer; if ((PacketCount * TotalPacketSize) + Offset < CloneStreamPointer->OffsetIn.Remaining) { /* calculate remaining buffer bytes */ PinContext->BufferLength = CloneStreamPointer->OffsetIn.Remaining - ((PacketCount * TotalPacketSize) + Offset); /* check for overflow */ if (PinContext->BufferOffset + TotalPacketSize >= PinContext->BufferSize) { /* reset buffer offset*/ PinContext->BufferOffset = 0; } RtlCopyMemory(&PinContext->Buffer[PinContext->BufferOffset], &TransferBuffer[(PacketCount * TotalPacketSize) + Offset], PinContext->BufferLength); PinContext->BufferOffset += PinContext->BufferLength; } /* render audio bytes */ Status = IoCallDriver(PinContext->LowerDevice, Irp); } /* unlock stream pointer and finish*/ KsStreamPointerUnlock(LeadingStreamPointer, TRUE); return STATUS_PENDING; } NTSTATUS PinCaptureProcess( IN PKSPIN Pin) { PKSSTREAM_POINTER LeadingStreamPointer; KIRQL OldLevel; PPIN_CONTEXT PinContext; PLIST_ENTRY CurEntry; PIRP Irp; PURB Urb; PUCHAR TransferBuffer, OutBuffer; ULONG Offset, Length; NTSTATUS Status; PKSGATE Gate; //DPRINT1("PinCaptureProcess\n"); LeadingStreamPointer = KsPinGetLeadingEdgeStreamPointer(Pin, KSSTREAM_POINTER_STATE_LOCKED); if (LeadingStreamPointer == NULL) { /* get process control gate */ Gate = KsPinGetAndGate(Pin); /* shutdown processing */ KsGateTurnInputOff(Gate); return STATUS_SUCCESS; } /* get pin context */ PinContext = Pin->Context; /* acquire spin lock */ KeAcquireSpinLock(&PinContext->IrpListLock, &OldLevel); while (!IsListEmpty(&PinContext->DoneIrpListHead)) { /* remove entry from list */ CurEntry = RemoveHeadList(&PinContext->DoneIrpListHead); /* release lock */ KeReleaseSpinLock(&PinContext->IrpListLock, OldLevel); /* get irp offset */ Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry); /* get urb from irp */ Urb = (PURB)Irp->Tail.Overlay.DriverContext[0]; ASSERT(Urb); Offset = PtrToUlong(Irp->Tail.Overlay.DriverContext[1]); /* get transfer buffer */ TransferBuffer = Urb->UrbIsochronousTransfer.TransferBuffer; /* get target buffer */ OutBuffer = (PUCHAR)LeadingStreamPointer->StreamHeader->Data; /* calculate length */ Length = min(LeadingStreamPointer->OffsetOut.Count - LeadingStreamPointer->StreamHeader->DataUsed, Urb->UrbIsochronousTransfer.TransferBufferLength - Offset); /* FIXME copy each packet extra */ /* copy audio bytes */ RtlCopyMemory((PUCHAR)&OutBuffer[LeadingStreamPointer->StreamHeader->DataUsed], &TransferBuffer[Offset], Length); //DPRINT1("Irp %p Urb %p OutBuffer %p TransferBuffer %p Offset %lu Remaining %lu TransferBufferLength %lu Length %lu\n", Irp, Urb, OutBuffer, TransferBuffer, Offset, LeadingStreamPointer->OffsetOut.Remaining, Urb->UrbIsochronousTransfer.TransferBufferLength, Length); /* adjust streampointer */ LeadingStreamPointer->StreamHeader->DataUsed += Length; if (Length == LeadingStreamPointer->OffsetOut.Remaining) { KsStreamPointerAdvanceOffsetsAndUnlock(LeadingStreamPointer, 0, Length, TRUE); /* acquire spin lock */ KeAcquireSpinLock(&PinContext->IrpListLock, &OldLevel); /* adjust offset */ Irp->Tail.Overlay.DriverContext[1] = UlongToPtr(Length); /* reinsert into processed list */ InsertHeadList(&PinContext->DoneIrpListHead, &Irp->Tail.Overlay.ListEntry); /* release lock */ KeReleaseSpinLock(&PinContext->IrpListLock, OldLevel); LeadingStreamPointer = KsPinGetLeadingEdgeStreamPointer(Pin, KSSTREAM_POINTER_STATE_LOCKED); if (LeadingStreamPointer == NULL) { /* no more work to be done*/ return STATUS_PENDING; } else { /* resume work on this irp */ continue; } } else { Status = KsStreamPointerAdvanceOffsets(LeadingStreamPointer, 0, Length, FALSE); NT_ASSERT(NT_SUCCESS(Status)); ASSERT(Length == Urb->UrbIsochronousTransfer.TransferBufferLength - Offset); } /* acquire spin lock */ KeAcquireSpinLock(&PinContext->IrpListLock, &OldLevel); InsertTailList(&PinContext->IrpListHead, &Irp->Tail.Overlay.ListEntry); } while (!IsListEmpty(&PinContext->IrpListHead)) { /* remove entry from list */ CurEntry = RemoveHeadList(&PinContext->IrpListHead); /* release lock */ KeReleaseSpinLock(&PinContext->IrpListLock, OldLevel); /* get irp offset */ Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry); /* reinitialize irp and urb */ CaptureInitializeUrbAndIrp(Pin, Irp); IoCallDriver(PinContext->DeviceExtension->LowerDevice, Irp); /* acquire spin lock */ KeAcquireSpinLock(&PinContext->IrpListLock, &OldLevel); } /* release lock */ KeReleaseSpinLock(&PinContext->IrpListLock, OldLevel); if (LeadingStreamPointer != NULL) KsStreamPointerUnlock(LeadingStreamPointer, FALSE); /* get process control gate */ Gate = KsPinGetAndGate(Pin); /* shutdown processing */ KsGateTurnInputOff(Gate); return STATUS_PENDING; } NTSTATUS NTAPI USBAudioPinProcess( _In_ PKSPIN Pin) { NTSTATUS Status; if (Pin->DataFlow == KSPIN_DATAFLOW_OUT) { Status = PinCaptureProcess(Pin); } else { Status = PinRenderProcess(Pin); } return Status; } VOID NTAPI USBAudioPinReset( _In_ PKSPIN Pin) { UNIMPLEMENTED; } NTSTATUS NTAPI USBAudioPinSetDataFormat( _In_ PKSPIN Pin, _In_opt_ PKSDATAFORMAT OldFormat, _In_opt_ PKSMULTIPLE_ITEM OldAttributeList, _In_ const KSDATARANGE* DataRange, _In_opt_ const KSATTRIBUTE_LIST* AttributeRange) { if (OldFormat == NULL) { /* TODO: verify connection format */ UNIMPLEMENTED; return STATUS_SUCCESS; } return UsbAudioSetFormat(Pin); } NTSTATUS StartCaptureIsocTransfer( IN PKSPIN Pin) { PPIN_CONTEXT PinContext; PLIST_ENTRY CurEntry; PIRP Irp; KIRQL OldLevel; /* get pin context */ PinContext = Pin->Context; /* acquire spin lock */ KeAcquireSpinLock(&PinContext->IrpListLock, &OldLevel); while(!IsListEmpty(&PinContext->IrpListHead)) { /* remove entry from list */ CurEntry = RemoveHeadList(&PinContext->IrpListHead); /* get irp offset */ Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry); /* release lock */ KeReleaseSpinLock(&PinContext->IrpListLock, OldLevel); /* reinitialize irp and urb */ CaptureInitializeUrbAndIrp(Pin, Irp); DPRINT("StartCaptureIsocTransfer Irp %p\n", Irp); IoCallDriver(PinContext->DeviceExtension->LowerDevice, Irp); /* acquire spin lock */ KeAcquireSpinLock(&PinContext->IrpListLock, &OldLevel); } /* release lock */ KeReleaseSpinLock(&PinContext->IrpListLock, OldLevel); return STATUS_SUCCESS; } NTSTATUS CapturePinStateChange( _In_ PKSPIN Pin, _In_ KSSTATE ToState, _In_ KSSTATE FromState) { NTSTATUS Status = STATUS_SUCCESS; if (FromState != ToState) { if (ToState) { if (ToState == KSSTATE_PAUSE) { if (FromState == KSSTATE_RUN) { /* wait until pin processing is finished*/ } } else { if (ToState == KSSTATE_RUN) { Status = StartCaptureIsocTransfer(Pin); } } } } return Status; } NTSTATUS NTAPI USBAudioPinSetDeviceState( _In_ PKSPIN Pin, _In_ KSSTATE ToState, _In_ KSSTATE FromState) { NTSTATUS Status; if (Pin->DataFlow == KSPIN_DATAFLOW_OUT) { /* handle capture state changes */ Status = CapturePinStateChange(Pin, ToState, FromState); } else { UNIMPLEMENTED; Status = STATUS_SUCCESS; } return Status; } NTSTATUS NTAPI UsbAudioPinDataIntersect( _In_ PVOID Context, _In_ PIRP Irp, _In_ PKSP_PIN Pin, _In_ PKSDATARANGE DataRange, _In_ PKSDATARANGE MatchingDataRange, _In_ ULONG DataBufferSize, _Out_ PVOID Data, _Out_ PULONG DataSize) { PKSFILTER Filter; PKSPIN_DESCRIPTOR_EX PinDescriptor; PKSDATAFORMAT_WAVEFORMATEX DataFormat; PKSDATARANGE_AUDIO DataRangeAudio; /* get filter from irp*/ Filter = KsGetFilterFromIrp(Irp); if (!Filter) { /* no match*/ return STATUS_NO_MATCH; } /* get pin descriptor */ PinDescriptor = (PKSPIN_DESCRIPTOR_EX)&Filter->Descriptor->PinDescriptors[Pin->PinId]; *DataSize = sizeof(KSDATAFORMAT_WAVEFORMATEX); if (DataBufferSize == 0) { /* buffer too small */ return STATUS_BUFFER_OVERFLOW; } /* sanity checks*/ ASSERT(PinDescriptor->PinDescriptor.DataRangesCount >= 0); ASSERT(PinDescriptor->PinDescriptor.DataRanges[0]->FormatSize == sizeof(KSDATARANGE_AUDIO)); DataRangeAudio = (PKSDATARANGE_AUDIO)PinDescriptor->PinDescriptor.DataRanges[0]; DataFormat = Data; DataFormat->WaveFormatEx.wFormatTag = WAVE_FORMAT_PCM; DataFormat->WaveFormatEx.nChannels = DataRangeAudio->MaximumChannels; DataFormat->WaveFormatEx.nSamplesPerSec = DataRangeAudio->MaximumSampleFrequency; DataFormat->WaveFormatEx.nAvgBytesPerSec = DataRangeAudio->MaximumSampleFrequency * (DataRangeAudio->MaximumBitsPerSample / 8) * DataRangeAudio->MaximumChannels; DataFormat->WaveFormatEx.nBlockAlign = (DataRangeAudio->MaximumBitsPerSample / 8) * DataRangeAudio->MaximumChannels; DataFormat->WaveFormatEx.wBitsPerSample = DataRangeAudio->MaximumBitsPerSample; DataFormat->WaveFormatEx.cbSize = 0; DataFormat->DataFormat.FormatSize = sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATEX); DataFormat->DataFormat.Flags = 0; DataFormat->DataFormat.Reserved = 0; DataFormat->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO; DataFormat->DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; DataFormat->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX; DataFormat->DataFormat.SampleSize = (DataRangeAudio->MaximumBitsPerSample / 8) * DataRangeAudio->MaximumChannels; return STATUS_SUCCESS; }