/*
 * COPYRIGHT:       See COPYING in the top level directory
 * PROJECT:         ReactOS Kernel Streaming
 * FILE:            drivers/ksfilter/ks/connectivity.c
 * PURPOSE:         KS Pin functions
 * PROGRAMMER:      Johannes Anderwald
 */


#include "priv.h"

KSPIN_INTERFACE StandardPinInterface = 
{
    {STATIC_KSINTERFACESETID_Standard},
    KSINTERFACE_STANDARD_STREAMING,
    0
};

KSPIN_MEDIUM StandardPinMedium =
{
    {STATIC_KSMEDIUMSETID_Standard},
    KSMEDIUM_TYPE_ANYINSTANCE,
    0
};

const GUID KSDATAFORMAT_SUBTYPE_BDA_MPEG2_TRANSPORT = {0xf4aeb342, 0x0329, 0x4fdd, {0xa8, 0xfd, 0x4a, 0xff, 0x49, 0x26, 0xc9, 0x78}};

/*
    @implemented
*/
KSDDKAPI
NTSTATUS
NTAPI
KsCreatePin(
    IN  HANDLE FilterHandle,
    IN  PKSPIN_CONNECT Connect,
    IN  ACCESS_MASK DesiredAccess,
    OUT PHANDLE ConnectionHandle)
{
    UINT ConnectSize = sizeof(KSPIN_CONNECT);

    PKSDATAFORMAT_WAVEFORMATEX Format = (PKSDATAFORMAT_WAVEFORMATEX)(Connect + 1);
    if (Format->DataFormat.FormatSize == sizeof(KSDATAFORMAT) ||
        Format->DataFormat.FormatSize == sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATEX))
    {
        ConnectSize += Format->DataFormat.FormatSize;
    }

    return KspCreateObjectType(FilterHandle,
                               KSSTRING_Pin,
                               (PVOID)Connect,
                               ConnectSize,
                               DesiredAccess,
                               ConnectionHandle);
}

NTSTATUS
KspValidateConnectRequest(
    IN PIRP Irp,
    IN ULONG DescriptorsCount,
    IN PVOID Descriptors,
    IN ULONG DescriptorSize,
    OUT PKSPIN_CONNECT* Connect)
{
    PKSPIN_CONNECT ConnectDetails;
    PKSPIN_INTERFACE Interface;
    PKSPIN_MEDIUM Medium;
    ULONG Size;
    NTSTATUS Status;
    ULONG Index;
    ULONG Count;
    BOOLEAN Found;
    PKSPIN_DESCRIPTOR Descriptor;

    /* did the caller miss the connect parameter */
    if (!Connect)
        return STATUS_INVALID_PARAMETER;

    /* set create param  size */
    Size = sizeof(KSPIN_CONNECT);

    /* fetch create parameters */
    Status = KspCopyCreateRequest(Irp,
                                  KSSTRING_Pin,
                                  &Size,
                                  (PVOID*)&ConnectDetails);

    /* check for success */
    if (!NT_SUCCESS(Status))
        return Status;

    /* is pin id out of bounds */
    if (ConnectDetails->PinId >= DescriptorsCount)
        return STATUS_INVALID_PARAMETER;

    if (DescriptorSize == sizeof(KSPIN_DESCRIPTOR))
    {
        /* standard pin descriptor */
        Descriptor = (PKSPIN_DESCRIPTOR)((ULONG_PTR)Descriptors + sizeof(KSPIN_DESCRIPTOR) * ConnectDetails->PinId);
    }
    else
    {
        /* extended / variable pin descriptor */
        Descriptor = &((PKSPIN_DESCRIPTOR_EX)((ULONG_PTR)Descriptors + DescriptorSize * ConnectDetails->PinId))->PinDescriptor;
    }


    /* does the pin have interface details filled in */
    if (Descriptor->InterfacesCount && Descriptor->Interfaces)
    {
        /* use provided pin interface count */
        Count = Descriptor->InterfacesCount;
        Interface = (PKSPIN_INTERFACE)Descriptor->Interfaces;
    }
    else
    {
        /* use standard pin interface */
        Count = 1;
        Interface = &StandardPinInterface;
    }

    /* now check the interface */
    Found = FALSE;
    Index = 0;
    do
    {
        UNICODE_STRING GuidString, GuidString2;
        RtlStringFromGUID(&Interface[Index].Set, &GuidString);
        RtlStringFromGUID(&ConnectDetails->Interface.Set, &GuidString2);

        DPRINT("Driver Interface %S Id %u\n", GuidString.Buffer, Interface[Index].Id);
        DPRINT("Connect Interface %S Id %u\n", GuidString2.Buffer, ConnectDetails->Interface.Id);

        if (IsEqualGUIDAligned(&Interface[Index].Set, &ConnectDetails->Interface.Set) &&
                               Interface[Index].Id == ConnectDetails->Interface.Id)
        {
            /* found a matching interface */
            Found = TRUE;
            break;
        }
        /* iterate to next interface */
        Index++;
    }while(Index < Count);

    if (!Found)
    {
        /* pin doesnt support this interface */
        return STATUS_NO_MATCH;
    }

    /* does the pin have medium details filled in */
    if (Descriptor->MediumsCount && Descriptor->Mediums)
    {
        /* use provided pin interface count */
        Count = Descriptor->MediumsCount;
        Medium = (PKSPIN_MEDIUM)Descriptor->Mediums;
    }
    else
    {
        /* use standard pin interface */
        Count = 1;
        Medium = &StandardPinMedium;
    }

    /* now check the interface */
    Found = FALSE;
    Index = 0;
    do
    {
        UNICODE_STRING GuidString, GuidString2;
        RtlStringFromGUID(&Medium[Index].Set, &GuidString);
        RtlStringFromGUID(&ConnectDetails->Medium.Set, &GuidString2);

        DPRINT("Driver Medium %S Id %u\n", GuidString.Buffer, Medium[Index].Id);
        DPRINT("Connect Medium %S Id %u\n", GuidString2.Buffer, ConnectDetails->Medium.Id);


        if (IsEqualGUIDAligned(&Medium[Index].Set, &ConnectDetails->Medium.Set) &&
                               Medium[Index].Id == ConnectDetails->Medium.Id)
        {
            /* found a matching interface */
            Found = TRUE;
            break;
        }



        /* iterate to next medium */
        Index++;
    }while(Index < Count);

    if (!Found)
    {
        /* pin doesnt support this medium */
        return STATUS_NO_MATCH;
    }

    /// FIXME
    /// implement format checking

    *Connect = ConnectDetails;
    return STATUS_SUCCESS;
}

/*
    @implemented
*/
KSDDKAPI
NTSTATUS
NTAPI
KsValidateConnectRequest(
    IN  PIRP Irp,
    IN  ULONG DescriptorsCount,
    IN  KSPIN_DESCRIPTOR* Descriptor,
    OUT PKSPIN_CONNECT* Connect)
{
    return KspValidateConnectRequest(Irp, DescriptorsCount, Descriptor, sizeof(KSPIN_DESCRIPTOR), Connect);
}

NTSTATUS
KspReadMediaCategory(
    IN LPGUID Category,
    PKEY_VALUE_PARTIAL_INFORMATION *OutInformation)
{
    UNICODE_STRING MediaPath = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\");
    UNICODE_STRING Name = RTL_CONSTANT_STRING(L"Name");
    UNICODE_STRING GuidString, Path;
    NTSTATUS Status;
    OBJECT_ATTRIBUTES ObjectAttributes;
    HANDLE hKey;
    ULONG Size;
    PKEY_VALUE_PARTIAL_INFORMATION KeyInfo;

    /* convert the guid to string */
    Status = RtlStringFromGUID(Category, &GuidString);
    if (!NT_SUCCESS(Status))
        return Status;

    /* allocate buffer for the registry key */
    Path.Length = 0;
    Path.MaximumLength = MediaPath.MaximumLength + GuidString.MaximumLength;
    Path.Buffer = AllocateItem(NonPagedPool, Path.MaximumLength);
    if (!Path.Buffer)
    {
        /* not enough memory */
        RtlFreeUnicodeString(&GuidString);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlAppendUnicodeStringToString(&Path, &MediaPath);
    RtlAppendUnicodeStringToString(&Path, &GuidString);

    /* free guid string */
    RtlFreeUnicodeString(&GuidString);

    /* initialize object attributes */
    InitializeObjectAttributes(&ObjectAttributes, &Path, OBJ_CASE_INSENSITIVE, NULL, NULL);

    /* open the key */
    Status = ZwOpenKey(&hKey, GENERIC_READ, &ObjectAttributes);

    DPRINT("ZwOpenKey() status 0x%08lx %wZ\n", Status, &Path);

    /* free path buffer */
    FreeItem(Path.Buffer);

    /* check for success */
    if (!NT_SUCCESS(Status))
    {
        DPRINT1("ZwOpenKey() failed with status 0x%08lx\n", Status);
        return Status;
    }

    /* query the name size */
    Status = ZwQueryValueKey(hKey, &Name, KeyValuePartialInformation, NULL, 0, &Size);
    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL)
    {
        /* failed to query for name key */
        ZwClose(hKey);
        return Status;
    }

    /* allocate buffer to read key info */
    KeyInfo = (PKEY_VALUE_PARTIAL_INFORMATION) AllocateItem(NonPagedPool, Size);
    if (!KeyInfo)
    {
        /* not enough memory */
        ZwClose(hKey);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    /* now read the info */
    Status = ZwQueryValueKey(hKey, &Name, KeyValuePartialInformation, (PVOID)KeyInfo, Size, &Size);

    /* close the key */
    ZwClose(hKey);

    if (!NT_SUCCESS(Status))
    {
        /* failed to read key */
        FreeItem(KeyInfo);
        return Status;
    }

    /* store key information */
    *OutInformation = KeyInfo;
    return Status;
}

KSDDKAPI
NTSTATUS
NTAPI
KspPinPropertyHandler(
    IN  PIRP Irp,
    IN  PKSPROPERTY Property,
    IN  OUT PVOID Data,
    IN  ULONG DescriptorsCount,
    IN  const KSPIN_DESCRIPTOR* Descriptors,
    IN  ULONG DescriptorSize)
{
    KSP_PIN * Pin;
    KSMULTIPLE_ITEM * Item;
    PIO_STACK_LOCATION IoStack;
    ULONG Size, Index;
    PVOID Buffer;
    PKSDATARANGE_AUDIO *WaveFormatOut;
    PKSDATAFORMAT_WAVEFORMATEX WaveFormatIn;
    PKEY_VALUE_PARTIAL_INFORMATION KeyInfo;
    const KSPIN_DESCRIPTOR *Descriptor;
    NTSTATUS Status = STATUS_NOT_SUPPORTED;
    ULONG Count;
    const PKSDATARANGE* DataRanges;
    LPGUID Guid;

    IoStack = IoGetCurrentIrpStackLocation(Irp);
    Buffer = Data;

    //DPRINT("KsPinPropertyHandler Irp %p Property %p Data %p DescriptorsCount %u Descriptor %p OutputLength %u Id %u\n", Irp, Property, Data, DescriptorsCount, Descriptor, IoStack->Parameters.DeviceIoControl.OutputBufferLength, Property->Id);

    /* convert to PKSP_PIN */
    Pin = (KSP_PIN*)Property;

    if (Property->Id != KSPROPERTY_PIN_CTYPES)
    {
        if (Pin->PinId >= DescriptorsCount)
        {
            /* invalid parameter */
            return STATUS_INVALID_PARAMETER;
        }
    }

    if (DescriptorSize == sizeof(KSPIN_DESCRIPTOR))
    {
        /* it is simple pin descriptor */
        Descriptor = &Descriptors[Pin->PinId];
    }
    else
    {
        /* get offset to pin descriptor */
        Descriptor = &(((PKSPIN_DESCRIPTOR_EX)((ULONG_PTR)Descriptors + Pin->PinId * DescriptorSize))->PinDescriptor);
    }

    switch(Property->Id)
    {
        case KSPROPERTY_PIN_CTYPES:
            (*(PULONG)Buffer) = DescriptorsCount;
            Irp->IoStatus.Information = sizeof(ULONG);
            Status = STATUS_SUCCESS;
            break;
        case KSPROPERTY_PIN_DATAFLOW:

            Size = sizeof(KSPIN_DATAFLOW);
            if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < Size)
            {
                Irp->IoStatus.Information = Size;
                Status = STATUS_BUFFER_TOO_SMALL;
                break;
            }

            *((KSPIN_DATAFLOW*)Buffer) = Descriptor->DataFlow;
            Irp->IoStatus.Information = sizeof(KSPIN_DATAFLOW);
            Status = STATUS_SUCCESS;
            break;

        case KSPROPERTY_PIN_DATARANGES:
        case KSPROPERTY_PIN_CONSTRAINEDDATARANGES:

            Size = sizeof(KSMULTIPLE_ITEM);
            DPRINT("Id %lu PinId %lu DataRangesCount %lu ConstrainedDataRangesCount %lu\n", Property->Id, Pin->PinId, Descriptor->DataRangesCount, Descriptor->ConstrainedDataRangesCount);

            if (Property->Id == KSPROPERTY_PIN_DATARANGES || Descriptor->ConstrainedDataRangesCount == 0)
            {
                DataRanges = Descriptor->DataRanges;
                Count = Descriptor->DataRangesCount;
            }
            else
            {
                DataRanges = Descriptor->ConstrainedDataRanges;
                Count = Descriptor->ConstrainedDataRangesCount;
            }

            for (Index = 0; Index < Count; Index++)
            {
                Size += ((DataRanges[Index]->FormatSize + 0x7) & ~0x7);
            }

            if (IoStack->Parameters.DeviceIoControl.OutputBufferLength == 0)
            {
                /* buffer too small */
                Irp->IoStatus.Information = Size;
                Status = STATUS_BUFFER_OVERFLOW;
                break;
            }

            Item = (KSMULTIPLE_ITEM*)Buffer;

            if (IoStack->Parameters.DeviceIoControl.OutputBufferLength == sizeof(ULONG))
            {
                /* store the result size */
                Item->Size = Size;
                Irp->IoStatus.Information = sizeof(ULONG);
                Status = STATUS_SUCCESS;
                break;
            }

            /* store descriptor size */
            Item->Size = Size;
            Item->Count = Count;

            if (IoStack->Parameters.DeviceIoControl.OutputBufferLength == sizeof(KSMULTIPLE_ITEM))
            {
                Irp->IoStatus.Information = sizeof(KSMULTIPLE_ITEM);
                Status = STATUS_SUCCESS;
                break;
            }

            /* now copy all dataranges */
            Data = (PUCHAR)(Item +1);

            /* alignment assert */
            ASSERT(((ULONG_PTR)Data & 0x7) == 0);

            for (Index = 0; Index < Count; Index++)
            {
                UNICODE_STRING GuidString;
                /* convert the guid to string */
                RtlStringFromGUID(&DataRanges[Index]->MajorFormat, &GuidString);
                DPRINT("Index %lu MajorFormat %S\n", Index, GuidString.Buffer);
                RtlStringFromGUID(&DataRanges[Index]->SubFormat, &GuidString);
                DPRINT("Index %lu SubFormat %S\n", Index, GuidString.Buffer);
                RtlStringFromGUID(&DataRanges[Index]->Specifier, &GuidString);
                DPRINT("Index %lu Specifier %S\n", Index, GuidString.Buffer);
                RtlStringFromGUID(&DataRanges[Index]->Specifier, &GuidString);
                DPRINT("Index %lu FormatSize %lu Flags %lu SampleSize %lu Reserved %lu KSDATAFORMAT %lu\n", Index,
                       DataRanges[Index]->FormatSize, DataRanges[Index]->Flags, DataRanges[Index]->SampleSize, DataRanges[Index]->Reserved, sizeof(KSDATAFORMAT));

                RtlMoveMemory(Data, DataRanges[Index], DataRanges[Index]->FormatSize);
                Data = ((PUCHAR)Data + DataRanges[Index]->FormatSize);
                /* alignment assert */
                ASSERT(((ULONG_PTR)Data & 0x7) == 0);
                Data = (PVOID)(((ULONG_PTR)Data + 0x7) & ~0x7);
            }

            Status = STATUS_SUCCESS;
            Irp->IoStatus.Information = Size;
            break;
        case KSPROPERTY_PIN_INTERFACES:

            if (Descriptor->Interfaces)
            {
                /* use mediums provided by driver */
                return KsHandleSizedListQuery(Irp, Descriptor->InterfacesCount, sizeof(KSPIN_MEDIUM), Descriptor->Interfaces);
            }
            else
            {
                /* use standard medium */
                return KsHandleSizedListQuery(Irp, 1, sizeof(KSPIN_INTERFACE), &StandardPinInterface);
            }
            break;

        case KSPROPERTY_PIN_MEDIUMS:

            if (Descriptor->MediumsCount)
            {
                /* use mediums provided by driver */
                return KsHandleSizedListQuery(Irp, Descriptor->MediumsCount, sizeof(KSPIN_MEDIUM), Descriptor->Mediums);
            }
            else
            {
                /* use standard medium */
                return KsHandleSizedListQuery(Irp, 1, sizeof(KSPIN_MEDIUM), &StandardPinMedium);
            }
            break;

        case KSPROPERTY_PIN_COMMUNICATION:

            Size = sizeof(KSPIN_COMMUNICATION);
            if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < Size)
            {
                Irp->IoStatus.Information = Size;
                Status = STATUS_BUFFER_TOO_SMALL;
                break;
            }

            *((KSPIN_COMMUNICATION*)Buffer) = Descriptor->Communication;

            Status = STATUS_SUCCESS;
            Irp->IoStatus.Information = Size;
            break;

        case KSPROPERTY_PIN_CATEGORY:

            if (!Descriptor->Category)
            {
                /* no pin category */
                return STATUS_NOT_FOUND;
            }

            /* check size */
            Size = sizeof(GUID);
            if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < Size)
            {
                /* buffer too small */
                Irp->IoStatus.Information = Size;
                Status = STATUS_BUFFER_TOO_SMALL;
                break;
            }

            /* copy category guid */
            RtlMoveMemory(Buffer, Descriptor->Category, sizeof(GUID));

            /* save result */
            Status = STATUS_SUCCESS;
            Irp->IoStatus.Information = Size;
            break;

        case KSPROPERTY_PIN_NAME:

            if (Descriptor->Name)
            {
                /* use pin name */
                Guid = (LPGUID)Descriptor->Name;
            }
            else
            {
                /* use pin category as fallback */
                Guid = (LPGUID)Descriptor->Category;
            }

            if (!Guid)
            {
                /* no friendly name available */
                return STATUS_NOT_FOUND;
            }

            /* read friendly name category name */
            Status = KspReadMediaCategory(Guid, &KeyInfo);
            if (!NT_SUCCESS(Status))
            {
                /* failed to read category */
                Irp->IoStatus.Information = 0;
                break;
            }

            /* store required length */
            Irp->IoStatus.Information = KeyInfo->DataLength + sizeof(WCHAR);

            /* check if buffer is too small */
            if (KeyInfo->DataLength + sizeof(WCHAR) > IoStack->Parameters.DeviceIoControl.OutputBufferLength)
            {
                /* buffer too small */
                Status = STATUS_BUFFER_OVERFLOW;
                FreeItem(KeyInfo);
                break;
            }

            /* copy result */
            RtlMoveMemory(Irp->UserBuffer, &KeyInfo->Data, KeyInfo->DataLength);

            /* null terminate name */
            ((LPWSTR)Irp->UserBuffer)[KeyInfo->DataLength / sizeof(WCHAR)] = L'\0';

            /* free key info */
            FreeItem(KeyInfo);
            break;
        case KSPROPERTY_PIN_PROPOSEDATAFORMAT:
            Size = sizeof(KSDATAFORMAT);
            if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < Size)
            {
                Irp->IoStatus.Information = Size;
                Status = STATUS_BUFFER_TOO_SMALL;
                break;
            }
            if (IoStack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(KSDATAFORMAT_WAVEFORMATEX))
            {
                UNIMPLEMENTED
                Status = STATUS_NOT_IMPLEMENTED;
                Irp->IoStatus.Information = 0;
                break;
            }

            WaveFormatIn = (PKSDATAFORMAT_WAVEFORMATEX)Buffer;
            if (!Descriptor->DataRanges || !Descriptor->DataRangesCount)
            {
                Status = STATUS_UNSUCCESSFUL;
                Irp->IoStatus.Information = 0;
                break;
            }
            WaveFormatOut = (PKSDATARANGE_AUDIO*)Descriptor->DataRanges;
            for(Index = 0; Index < Descriptor->DataRangesCount; Index++)
            {
                if (WaveFormatOut[Index]->DataRange.FormatSize != sizeof(KSDATARANGE_AUDIO))
                {
                    UNIMPLEMENTED
                    continue;
                }

                if (WaveFormatOut[Index]->MinimumSampleFrequency > WaveFormatIn->WaveFormatEx.nSamplesPerSec ||
                    WaveFormatOut[Index]->MaximumSampleFrequency < WaveFormatIn->WaveFormatEx.nSamplesPerSec ||
                    WaveFormatOut[Index]->MinimumBitsPerSample > WaveFormatIn->WaveFormatEx.wBitsPerSample ||
                    WaveFormatOut[Index]->MaximumBitsPerSample < WaveFormatIn->WaveFormatEx.wBitsPerSample ||
                    WaveFormatOut[Index]->MaximumChannels < WaveFormatIn->WaveFormatEx.nChannels)
                {
                    Irp->IoStatus.Status = STATUS_NO_MATCH;
                    Irp->IoStatus.Information = 0;
                    return STATUS_NO_MATCH;
                }
                else
                {
                    Irp->IoStatus.Status = STATUS_SUCCESS;
                    Irp->IoStatus.Information = 0;
                    return STATUS_SUCCESS;
                }
            }
            Status = STATUS_NO_MATCH;
            Irp->IoStatus.Information = 0;
            break;
        default:
            DPRINT1("Unhandled property request %x\n", Property->Id);
            Status = STATUS_NOT_IMPLEMENTED;
            Irp->IoStatus.Information = 0;
    }

    return Status;
}

/*
    @implemented
*/
KSDDKAPI
NTSTATUS
NTAPI
KsPinPropertyHandler(
    IN  PIRP Irp,
    IN  PKSPROPERTY Property,
    IN  OUT PVOID Data,
    IN  ULONG DescriptorsCount,
    IN  const KSPIN_DESCRIPTOR* Descriptor)
{
    return KspPinPropertyHandler(Irp, Property, Data, DescriptorsCount, Descriptor, sizeof(KSPIN_DESCRIPTOR));
}

/*
    @unimplemented
*/
KSDDKAPI NTSTATUS NTAPI
KsPinDataIntersectionEx(
    IN  PIRP Irp,
    IN  PKSP_PIN Pin,
    OUT PVOID Data,
    IN  ULONG DescriptorsCount,
    IN  const KSPIN_DESCRIPTOR* Descriptor,
    IN  ULONG DescriptorSize,
    IN  PFNKSINTERSECTHANDLEREX IntersectHandler OPTIONAL,
    IN  PVOID HandlerContext OPTIONAL)
{
    UNIMPLEMENTED;
    return STATUS_UNSUCCESSFUL;
}

/*
    @implemented
*/
KSDDKAPI
NTSTATUS
NTAPI
KsPinDataIntersection(
    IN  PIRP Irp,
    IN  PKSP_PIN Pin,
    OUT PVOID Data,
    IN  ULONG DescriptorsCount,
    IN  const KSPIN_DESCRIPTOR* Descriptor,
    IN  PFNKSINTERSECTHANDLER IntersectHandler)
{
    KSMULTIPLE_ITEM * Item;
    KSDATARANGE * DataRange;
    PIO_STACK_LOCATION IoStack;
    ULONG Size;
    ULONG Index;
    NTSTATUS Status;

    /* get current irp stack location */
    IoStack = IoGetCurrentIrpStackLocation(Irp);

    /* calculate minimum data size */
    Size = sizeof(KSP_PIN) + sizeof(KSMULTIPLE_ITEM) + sizeof(KSDATARANGE);
    if (IoStack->Parameters.DeviceIoControl.InputBufferLength < Size)
    {
        /* buffer too small */
        Irp->IoStatus.Information = Size;
        Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
        return STATUS_BUFFER_TOO_SMALL;
    }
    /* is pin id out of bounds */
    if (Pin->PinId >= DescriptorsCount)
    {
        /* it is */
        Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
        Irp->IoStatus.Information = 0;
        return STATUS_INVALID_PARAMETER;
    }

    /* get start item */
    Item = (KSMULTIPLE_ITEM*)(Pin + 1);
    /* get first data range */
    DataRange = (KSDATARANGE*)(Item + 1);
    /* iterate through all data ranges */
    for(Index = 0; Index < Item->Count; Index++, DataRange++)
    {
        /* call intersect handler */
        Status = IntersectHandler(Irp, Pin, DataRange, Data);
        if (NT_SUCCESS(Status))
        {
            if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < DataRange->FormatSize)
            {
                /* buffer is too small */
                Irp->IoStatus.Information = DataRange->FormatSize;
                Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
                return STATUS_BUFFER_TOO_SMALL;
            }
            RtlMoveMemory(Irp->UserBuffer, DataRange, sizeof(KSDATARANGE));
            Irp->IoStatus.Information = sizeof(KSDATARANGE);
            Irp->IoStatus.Status = STATUS_SUCCESS;
            return STATUS_SUCCESS;
        }

    }

    Irp->IoStatus.Information = 0;
    Irp->IoStatus.Status = STATUS_NO_MATCH;
    return STATUS_NO_MATCH;
}

/*
    @implemented
*/

KSDDKAPI
NTSTATUS
NTAPI
KsHandleSizedListQuery(
    IN  PIRP Irp,
    IN  ULONG DataItemsCount,
    IN  ULONG DataItemSize,
    IN  const VOID* DataItems)
{
    ULONG Size;
    PIO_STACK_LOCATION IoStack;
    PKSMULTIPLE_ITEM Item;

    /* get current irp stack location */
    IoStack = IoGetCurrentIrpStackLocation(Irp);

    /* calculate size */
    Size = DataItemSize * DataItemsCount + sizeof(KSMULTIPLE_ITEM);

    /* get multiple item */
    Item = (PKSMULTIPLE_ITEM)Irp->AssociatedIrp.SystemBuffer;

    if (IoStack->Parameters.DeviceIoControl.OutputBufferLength == 0)
    {
        /* buffer too small */
        Irp->IoStatus.Information = Size;

        return STATUS_BUFFER_OVERFLOW;
    }

    if (IoStack->Parameters.DeviceIoControl.OutputBufferLength == sizeof(ULONG))
    {
        /* store just the size */
        Item->Size = Size;
        Irp->IoStatus.Information = sizeof(ULONG);

        return STATUS_SUCCESS;
    }


    if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KSMULTIPLE_ITEM))
    {
        /* buffer too small */
        return STATUS_BUFFER_TOO_SMALL;
    }

    Item->Count = DataItemsCount;
    Item->Size = DataItemSize;

    if (IoStack->Parameters.DeviceIoControl.OutputBufferLength == sizeof(KSMULTIPLE_ITEM))
    {
        /* buffer can only hold the length descriptor */
        Irp->IoStatus.Information = sizeof(KSMULTIPLE_ITEM);
        return STATUS_SUCCESS;
    }

    if (IoStack->Parameters.DeviceIoControl.OutputBufferLength >= Size)
    {
        /* copy items */
        RtlMoveMemory((PVOID)(Item + 1), DataItems, DataItemSize * DataItemsCount);
        /* store result */
        Irp->IoStatus.Information = Size;
        /* done */
        return STATUS_SUCCESS;
    }
    else
    {
        /* buffer too small */
        return STATUS_BUFFER_TOO_SMALL;
    }
}

