[USBAUDIO]
[reactos.git] / reactos / drivers / usb / usbaudio / pin.c
index e9d5c05..353df9b 100644 (file)
@@ -9,6 +9,645 @@
 
 #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
+UsbAudioSetMuteOff(
+    IN PKSPIN Pin)
+{
+    PURB Urb;
+    PVOID SampleRateBuffer;
+    PPIN_CONTEXT PinContext;
+    NTSTATUS Status;
+
+    /* allocate sample rate buffer */
+    SampleRateBuffer = AllocFunction(sizeof(ULONG));
+    if (!SampleRateBuffer)
+    {
+        /* no memory */
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* allocate urb */
+    Urb = AllocFunction(sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));
+    if (!Urb)
+    {
+        /* no memory */
+        FreeFunction(SampleRateBuffer);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* FIXME: determine controls and format urb */
+    UsbBuildVendorRequest(Urb,
+        URB_FUNCTION_CLASS_INTERFACE,
+        sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST),
+        USBD_TRANSFER_DIRECTION_OUT,
+        0,
+        0x01,
+        0x100,
+        0x300,
+        SampleRateBuffer,
+        NULL,
+        1,
+        NULL);
+
+    /* get pin context */
+    PinContext = Pin->Context;
+
+    /* submit urb */
+    Status = SubmitUrbSync(PinContext->LowerDevice, Urb);
+
+    DPRINT1("UsbAudioSetMuteOff Pin %p Status %x\n", Pin, Status);
+    FreeFunction(Urb);
+    FreeFunction(SampleRateBuffer);
+    return Status;
+}
+
+NTSTATUS
+UsbAudioSetVolume(
+    IN PKSPIN Pin)
+{
+    PURB Urb;
+    PUCHAR SampleRateBuffer;
+    PPIN_CONTEXT PinContext;
+    NTSTATUS Status;
+
+    /* allocate sample rate buffer */
+    SampleRateBuffer = AllocFunction(sizeof(ULONG));
+    if (!SampleRateBuffer)
+    {
+        /* no memory */
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* allocate urb */
+    Urb = AllocFunction(sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));
+    if (!Urb)
+    {
+        /* no memory */
+        FreeFunction(SampleRateBuffer);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* FIXME: determine controls and format urb */
+    UsbBuildVendorRequest(Urb,
+        URB_FUNCTION_CLASS_INTERFACE,
+        sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST),
+        USBD_TRANSFER_DIRECTION_OUT,
+        0,
+        0x01,
+        0x200,
+        0x300,
+        SampleRateBuffer,
+        NULL,
+        2,
+        NULL);
+
+    /* get pin context */
+    PinContext = Pin->Context;
+
+    SampleRateBuffer[0] = 0xC2;
+    SampleRateBuffer[1] = 0xFE;
+
+    /* submit urb */
+    Status = SubmitUrbSync(PinContext->LowerDevice, Urb);
+
+    DPRINT1("UsbAudioSetVolume Pin %p Status %x\n", Pin, Status);
+    FreeFunction(Urb);
+    FreeFunction(SampleRateBuffer);
+    return Status;
+}
+
+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;
+    }
+
+    /* 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,
+        0x81, //FIXME bEndpointAddress
+        SampleRateBuffer,
+        NULL,
+        3,
+        NULL);
+
+    /* get pin context */
+    PinContext = Pin->Context;
+
+    /* 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 PPIN_CONTEXT PinContext,
+    IN PDEVICE_EXTENSION DeviceExtension,
+    IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
+{
+    PURB Urb;
+    PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor;
+    NTSTATUS Status;
+
+    /* 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;
+    }
+
+    /* FIXME selects the first interface with audio streaming and non zero num of endpoints */
+    while (InterfaceDescriptor != NULL)
+    {
+        if (InterfaceDescriptor->bInterfaceSubClass == 0x02 /* AUDIO_STREAMING */ && InterfaceDescriptor->bNumEndpoints > 0) 
+        {
+            break;
+        }
+        InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(ConfigurationDescriptor, (PVOID)((ULONG_PTR)InterfaceDescriptor + InterfaceDescriptor->bLength), -1, -1, USB_DEVICE_CLASS_AUDIO, -1, -1);
+    }
+
+    if (!InterfaceDescriptor)
+    {
+        /* no such interface */
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    Urb = AllocFunction(GET_SELECT_INTERFACE_REQUEST_SIZE(InterfaceDescriptor->bNumEndpoints));
+    if (!Urb)
+    {
+        /* no memory */
+        return USBD_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\n", Status, Urb->UrbSelectInterface.Hdr.Status);
+
+     /* 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);
+}
+
+
+
+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)
+{
+    UNIMPLEMENTED
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+
+
+
 NTSTATUS
 NTAPI
 USBAudioPinCreate(
@@ -18,6 +657,7 @@ USBAudioPinCreate(
     PKSFILTER Filter;
     PFILTER_CONTEXT FilterContext;
     PPIN_CONTEXT PinContext;
+    NTSTATUS Status;
 
     Filter = KsPinGetParentFilter(Pin);
     if (Filter == NULL)
@@ -40,10 +680,44 @@ USBAudioPinCreate(
     /* 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);
+    }
+
+    /* FIXME move to build filter topology*/
+    UsbAudioSetMuteOff(Pin);
+    UsbAudioSetVolume(Pin);
+
+    /* select streaming interface */
+    Status = USBAudioSelectAudioStreamingInterface(PinContext, PinContext->DeviceExtension, PinContext->DeviceExtension->ConfigurationDescriptor);
+    if (!NT_SUCCESS(Status))
+    {
+        /* failed */
+        return Status;
+    }
+
+    if (Pin->DataFlow == KSPIN_DATAFLOW_OUT)
+    {
+        /* init capture pin */
+        Status = InitCapturePin(Pin);
+    }
+    else
+    {
+        /* audio streaming pin*/
+        Status = InitStreamPin(Pin);
+    }
+
     return STATUS_SUCCESS;
 }
 
@@ -57,14 +731,222 @@ USBAudioPinClose(
     return STATUS_NOT_IMPLEMENTED;
 }
 
+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;
+}
+
+NTSTATUS
+PinCaptureProcess(
+    IN PKSPIN Pin)
+{
+    PKSSTREAM_POINTER LeadingStreamPointer;
+    KIRQL OldLevel;
+    PPIN_CONTEXT PinContext;
+    PLIST_ENTRY CurEntry;
+    PIRP Irp;
+    PIO_STACK_LOCATION IoStack;
+    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 */
+        IoStack = IoGetNextIrpStackLocation(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);
+            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)
 {
-    UNIMPLEMENTED
-    return STATUS_SUCCESS;
+    NTSTATUS Status;
+
+    if (Pin->DataFlow == KSPIN_DATAFLOW_OUT)
+    {
+        Status = PinCaptureProcess(Pin);
+    }
+    else
+    {
+        UNIMPLEMENTED;
+        Status = STATUS_NOT_IMPLEMENTED;
+    }
+
+    return Status;
 }
 
 
@@ -85,72 +967,91 @@ USBAudioPinSetDataFormat(
     _In_ const KSDATARANGE* DataRange,
     _In_opt_ const KSATTRIBUTE_LIST* AttributeRange)
 {
-    PURB Urb;
-    PUCHAR SampleRateBuffer;
-    PPIN_CONTEXT PinContext;
-    NTSTATUS Status;
-    PKSDATAFORMAT_WAVEFORMATEX WaveFormatEx;
-
-    /* allocate sample rate buffer */
-    SampleRateBuffer = AllocFunction(sizeof(ULONG));
-    if (!SampleRateBuffer)
+    if (OldFormat == NULL)
     {
-        /* no memory */
-        return STATUS_INSUFFICIENT_RESOURCES;
+        /* TODO: verify connection format */
+        UNIMPLEMENTED
+        return STATUS_SUCCESS;
     }
 
-    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[0] = (WaveFormatEx->WaveFormatEx.nSamplesPerSec >> 16) & 0xFF;
-        SampleRateBuffer[1] = (WaveFormatEx->WaveFormatEx.nSamplesPerSec >> 8) & 0xFF;
-        SampleRateBuffer[2] = (WaveFormatEx->WaveFormatEx.nSamplesPerSec >> 0) & 0xFF;
-    }
-    else
-    {
-        /* not supported yet*/
-        UNIMPLEMENTED;
-        FreeFunction(SampleRateBuffer);
-        return STATUS_INVALID_PARAMETER;
-    }
+    return UsbAudioSetFormat(Pin);
+}
 
-    /* allocate urb */
-    Urb = AllocFunction(sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));
-    if (!Urb)
+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))
     {
-        /* no memory */
-        FreeFunction(SampleRateBuffer);
-        return STATUS_INSUFFICIENT_RESOURCES;
+        /* 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);
+
     }
 
-    /* format urb */
-    UsbBuildVendorRequest(Urb,
-                          URB_FUNCTION_CLASS_ENDPOINT,
-                          sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST),
-                          USBD_TRANSFER_DIRECTION_OUT,
-                          0,
-                          0x01,
-                          0x100,
-                          0x81, //bEndpointAddress
-                          SampleRateBuffer,
-                          NULL,
-                          3,
-                          NULL);
+    /* release lock */
+    KeReleaseSpinLock(&PinContext->IrpListLock, OldLevel);
 
-    /* get pin context */
-    PinContext = Pin->Context;
-    DbgBreakPoint();
-    /* submit urb */
-    Status = SubmitUrbSync(PinContext->LowerDevice, Urb);
+    return STATUS_SUCCESS;
+}
 
-    DPRINT1("USBAudioPinSetDataFormat Pin %p Status %x\n", Pin, Status);
-    FreeFunction(Urb);
-    FreeFunction(SampleRateBuffer);
+NTSTATUS
+CapturePinStateChange(
+    _In_ PKSPIN Pin,
+    _In_ KSSTATE ToState,
+    _In_ KSSTATE FromState)
+{
+    NTSTATUS Status;
+
+    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(
@@ -158,6 +1059,80 @@ USBAudioPinSetDeviceState(
     _In_ KSSTATE ToState,
     _In_ KSSTATE FromState)
 {
-    UNIMPLEMENTED
+    NTSTATUS Status;
+
+    if (Pin->DataFlow == KSPIN_DATAFLOW_OUT)
+    {
+        /* handle capture state changes */
+        Status = CapturePinStateChange(Pin, ToState, FromState);
+    }
+    else
+    {
+        UNIMPLEMENTED
+        Status = STATUS_NOT_IMPLEMENTED;
+    }
+
+    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 = &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;
 }