[KS]
[reactos.git] / reactos / drivers / ksfilter / ks / api.c
index ceee766..2067de5 100644 (file)
@@ -9,8 +9,11 @@
 
 #include "priv.h"
 
+const GUID GUID_NULL              = {0x00000000L, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
+const GUID KSMEDIUMSETID_Standard = {0x4747B320L, 0x62CE, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
+
 /*
-    @unimplemented
+    @implemented
 */
 KSDDKAPI
 NTSTATUS
@@ -19,8 +22,40 @@ KsAcquireResetValue(
     IN  PIRP Irp,
     OUT KSRESET* ResetValue)
 {
-    UNIMPLEMENTED;
-    return STATUS_UNSUCCESSFUL;
+    PIO_STACK_LOCATION IoStack;
+    KSRESET* Value;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    /* get current irp stack */
+    IoStack = IoGetCurrentIrpStackLocation(Irp);
+
+    /* check if there is reset value provided */
+    if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KSRESET))
+        return STATUS_INVALID_PARAMETER;
+
+    if (Irp->RequestorMode == UserMode)
+    {
+        /* need to probe the buffer */
+        _SEH2_TRY
+        {
+            ProbeForRead(IoStack->Parameters.DeviceIoControl.Type3InputBuffer, sizeof(KSRESET), sizeof(UCHAR));
+            Value = (KSRESET*)IoStack->Parameters.DeviceIoControl.Type3InputBuffer;
+            *ResetValue = *Value;
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            /* Exception, get the error code */
+            Status = _SEH2_GetExceptionCode();
+        }
+        _SEH2_END;
+    }
+    else
+    {
+        Value = (KSRESET*)IoStack->Parameters.DeviceIoControl.Type3InputBuffer;
+        *ResetValue = *Value;
+    }
+
+    return Status;
 }
 
 /*
@@ -59,6 +94,8 @@ KsReleaseDeviceSecurityLock(
 {
     PKSIDEVICE_HEADER Header = (PKSIDEVICE_HEADER)DevHeader;
 
+    DPRINT("KsReleaseDevice\n");
+
     ExReleaseResourceLite(&Header->SecurityLock);
     KeLeaveCriticalRegion();
 }
@@ -459,8 +496,8 @@ KspFreeCreateItems(
         Entry = (PCREATE_ITEM_ENTRY)CONTAINING_RECORD(RemoveHeadList(ListHead), CREATE_ITEM_ENTRY, Entry);
 
         /* caller shouldnt have any references */
-        ASSERT(Entry->ReferenceCount == 0);
-        ASSERT(IsListEmpty(&Entry->ObjectItemList));
+        //ASSERT(Entry->ReferenceCount == 0);
+        //ASSERT(IsListEmpty(&Entry->ObjectItemList));
 
         /* does the creator wish notification */
         if (Entry->ItemFreeCallback)
@@ -509,6 +546,8 @@ KsAllocateDeviceHeader(
     InitializeListHead(&Header->TargetDeviceList);
     /* initialize power dispatch list */
     InitializeListHead(&Header->PowerDispatchList);
+    /* initialize object bag lists */
+    InitializeListHead(&Header->ObjectBags);
 
     /* initialize create item list */
     InitializeListHead(&Header->ItemList);
@@ -650,8 +689,7 @@ KsAllocateObjectHeader(
         }
     }
     /* store the object in the file object */
-    ASSERT(IoStack->FileObject->FsContext == NULL);
-    IoStack->FileObject->FsContext = ObjectHeader;
+    IoStack->FileObject->FsContext2 = ObjectHeader;
 
     /* store parent device */
     ObjectHeader->ParentDeviceObject = IoGetRelatedDeviceObject(IoStack->FileObject);
@@ -683,6 +721,8 @@ KsFreeObjectHeader(
 {
     PKSIOBJECT_HEADER ObjectHeader = (PKSIOBJECT_HEADER) Header;
 
+    DPRINT("KsFreeObjectHeader Header %p Class %wZ\n", Header, &ObjectHeader->ObjectClass);
+
     if (ObjectHeader->ObjectClass.Buffer)
     {
         /* release object class buffer */
@@ -766,7 +806,7 @@ KsAddObjectCreateItemToDeviceHeader(
 
     Header = (PKSIDEVICE_HEADER)DevHeader;
 
-    DPRINT1("KsAddObjectCreateItemToDeviceHeader entered\n");
+    DPRINT("KsAddObjectCreateItemToDeviceHeader entered\n");
 
      /* check if a device header has been provided */
     if (!DevHeader)
@@ -788,7 +828,7 @@ KsAddObjectCreateItemToDeviceHeader(
         /* increment create item count */
         InterlockedIncrement(&Header->ItemListCount);
     }
-
+    DPRINT("KsAddObjectCreateItemToDeviceHeader Status %x\n", Status);
     return Status;
 }
 
@@ -810,7 +850,7 @@ KsAddObjectCreateItemToObjectHeader(
 
     Header = (PKSIOBJECT_HEADER)ObjectHeader;
 
-    DPRINT1("KsAddObjectCreateItemToDeviceHeader entered\n");
+    DPRINT("KsAddObjectCreateItemToDeviceHeader entered\n");
 
      /* check if a device header has been provided */
     if (!Header)
@@ -861,7 +901,7 @@ KsAllocateObjectCreateItem(
         return STATUS_INVALID_PARAMETER_2;
 
     /* first allocate a create entry */
-    CreateEntry = AllocateItem(NonPagedPool, sizeof(PCREATE_ITEM_ENTRY));
+    CreateEntry = AllocateItem(NonPagedPool, sizeof(CREATE_ITEM_ENTRY));
 
     /* check for allocation success */
     if (!CreateEntry)
@@ -1084,7 +1124,7 @@ KsSynchronousIoControlDevice(
 
 
     /* get object header */
-    ObjectHeader = (PKSIOBJECT_HEADER)FileObject->FsContext;
+    ObjectHeader = (PKSIOBJECT_HEADER)FileObject->FsContext2;
 
     /* check if there is fast device io function */
     if (ObjectHeader && ObjectHeader->DispatchTable.FastDeviceIoControl)
@@ -1092,7 +1132,7 @@ KsSynchronousIoControlDevice(
         IoStatusBlock.Status = STATUS_UNSUCCESSFUL;
         IoStatusBlock.Information = 0;
 
-        /* it is send the request */
+        /* send the request */
         Status = ObjectHeader->DispatchTable.FastDeviceIoControl(FileObject, TRUE, InBuffer, InSize, OutBuffer, OutSize, IoControl, &IoStatusBlock, DeviceObject);
         /* check if the request was handled */
         //DPRINT("Handled %u Status %x Length %u\n", Status, IoStatusBlock.Status, IoStatusBlock.Information);
@@ -1111,10 +1151,26 @@ KsSynchronousIoControlDevice(
     /* create the irp */
     Irp =  IoBuildDeviceIoControlRequest(IoControl, DeviceObject, InBuffer, InSize, OutBuffer, OutSize, FALSE, &Event, &IoStatusBlock);
 
-    /* HACK */
+    if (!Irp)
+    {
+        /* no memory to allocate the irp */
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+
+    /* Store Fileobject */
     IoStack = IoGetNextIrpStackLocation(Irp);
     IoStack->FileObject = FileObject;
 
+    if (IoControl == IOCTL_KS_WRITE_STREAM)
+    {
+        Irp->AssociatedIrp.SystemBuffer = OutBuffer;
+    }
+    else if (IoControl == IOCTL_KS_READ_STREAM)
+    {
+        Irp->AssociatedIrp.SystemBuffer = InBuffer;
+    }
+
     IoSetCompletionRoutine(Irp, KspSynchronousIoControlDeviceCompletion, (PVOID)&IoStatusBlock, TRUE, TRUE, TRUE);
 
     Status = IoCallDriver(DeviceObject, Irp);
@@ -1129,7 +1185,7 @@ KsSynchronousIoControlDevice(
 }
 
 /*
-    @implemented
+    @unimplemented
 */
 KSDDKAPI
 NTSTATUS
@@ -1145,7 +1201,7 @@ KsUnserializeObjectPropertiesFromRegistry(
 
 
 /*
-    @unimplemented
+    @implemented
 */
 KSDDKAPI
 NTSTATUS
@@ -1155,8 +1211,83 @@ KsCacheMedium(
     IN  PKSPIN_MEDIUM Medium,
     IN  ULONG PinDirection)
 {
-    UNIMPLEMENTED;
-    return STATUS_UNSUCCESSFUL;
+    HANDLE hKey;
+    UNICODE_STRING Path;
+    UNICODE_STRING BasePath = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\MediumCache\\");
+    UNICODE_STRING GuidString;
+    NTSTATUS Status;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    BOOLEAN PathAdjusted = FALSE;
+    ULONG Value = 0;
+
+    /* first check if the medium is standard */
+    if (IsEqualGUIDAligned(&KSMEDIUMSETID_Standard, &Medium->Set) ||
+        IsEqualGUIDAligned(&GUID_NULL, &Medium->Set))
+    {
+        /* no need to cache that */
+        return STATUS_SUCCESS;
+    }
+
+    /* convert guid to string */
+    Status = RtlStringFromGUID(&Medium->Set, &GuidString);
+    if (!NT_SUCCESS(Status))
+        return Status;
+
+    /* allocate path buffer */
+    Path.Length = 0;
+    Path.MaximumLength = BasePath.MaximumLength + GuidString.MaximumLength + 10 * sizeof(WCHAR);
+    Path.Buffer = AllocateItem(PagedPool, Path.MaximumLength);
+    if (!Path.Buffer)
+    {
+        /* not enough resources */
+        RtlFreeUnicodeString(&GuidString);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    RtlAppendUnicodeStringToString(&Path, &BasePath);
+    RtlAppendUnicodeStringToString(&Path, &GuidString);
+    RtlAppendUnicodeToString(&Path, L"-");
+    /* FIXME append real instance id */
+    RtlAppendUnicodeToString(&Path, L"0");
+    RtlAppendUnicodeToString(&Path, L"-");
+    /* FIXME append real instance id */
+    RtlAppendUnicodeToString(&Path, L"0");
+
+    /* free guid string */
+    RtlFreeUnicodeString(&GuidString);
+
+    /* initialize object attributes */
+    InitializeObjectAttributes(&ObjectAttributes, &Path, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
+    /* create the key */
+    Status = ZwCreateKey(&hKey, GENERIC_WRITE, &ObjectAttributes, 0, NULL, 0, NULL);
+
+    /* free path buffer */
+    FreeItem(Path.Buffer);
+
+    if (NT_SUCCESS(Status))
+    {
+        /* store symbolic link */
+        if (SymbolicLink->Buffer[1] == L'?' && SymbolicLink->Buffer[2] == L'?')
+        {
+            /* replace kernel path with user mode path */
+            SymbolicLink->Buffer[1] = L'\\';
+            PathAdjusted = TRUE;
+        }
+
+        /* store the key */
+        Status = ZwSetValueKey(hKey, SymbolicLink, 0, REG_DWORD, &Value, sizeof(ULONG));
+
+        if (PathAdjusted)
+        {
+            /* restore kernel path */
+            SymbolicLink->Buffer[1] = L'?';
+        }
+
+        ZwClose(hKey);
+    }
+
+    /* done */
+    return Status;
 }
 
 /*
@@ -1248,7 +1379,7 @@ KopDispatchCreate(
     DriverObjectExtension = (PKO_DRIVER_EXTENSION)IoGetDriverObjectExtension(DeviceObject->DriverObject, (PVOID)KoDriverInitialize);
     if (!DriverObjectExtension)
     {
-        DPRINT1("FileObject not attached!\n");
+        DPRINT1("No DriverObjectExtension!\n");
         Status = STATUS_UNSUCCESSFUL;
         goto cleanup;
     }
@@ -1447,7 +1578,7 @@ KoRelease(
 }
 
 /*
-    @unimplemented
+    @implemented
 */
 KSDDKAPI
 VOID
@@ -1455,9 +1586,33 @@ NTAPI
 KsAcquireControl(
     IN PVOID Object)
 {
-    UNIMPLEMENTED
+    PKSBASIC_HEADER BasicHeader = (PKSBASIC_HEADER)((ULONG_PTR)Object - sizeof(KSBASIC_HEADER));
+
+    /* sanity check */
+    ASSERT(BasicHeader->Type == KsObjectTypeFilter || BasicHeader->Type == KsObjectTypePin);
+
+    KeWaitForSingleObject(BasicHeader->ControlMutex, Executive, KernelMode, FALSE, NULL);
+
+}
+
+/*
+    @implemented
+*/
+VOID
+NTAPI
+KsReleaseControl(
+    IN PVOID  Object)
+{
+    PKSBASIC_HEADER BasicHeader = (PKSBASIC_HEADER)((ULONG_PTR)Object - sizeof(KSBASIC_HEADER));
+
+    /* sanity check */
+    ASSERT(BasicHeader->Type == KsObjectTypeFilter || BasicHeader->Type == KsObjectTypePin);
+
+    KeReleaseMutex(BasicHeader->ControlMutex, FALSE);
 }
 
+
+
 /*
     @implemented
 */
@@ -1468,10 +1623,13 @@ KsAcquireDevice(
     IN PKSDEVICE Device)
 {
     IKsDevice *KsDevice;
-    PKSIDEVICE_HEADER DeviceHeader = (PKSIDEVICE_HEADER)CONTAINING_RECORD(Device, KSIDEVICE_HEADER, KsDevice);
+    PKSIDEVICE_HEADER DeviceHeader;
+
+    DPRINT("KsAcquireDevice\n");
+    DeviceHeader = (PKSIDEVICE_HEADER)CONTAINING_RECORD(Device, KSIDEVICE_HEADER, KsDevice);
 
     /* get device interface*/
-    KsDevice = (IKsDevice*)&DeviceHeader->lpVtblIKsDevice;
+    KsDevice = (IKsDevice*)&DeviceHeader->BasicHeader.OuterUnknown;
 
     /* acquire device mutex */
     KsDevice->lpVtbl->AcquireDevice(KsDevice);
@@ -1489,7 +1647,7 @@ KsReleaseDevice(
     PKSIDEVICE_HEADER DeviceHeader = (PKSIDEVICE_HEADER)CONTAINING_RECORD(Device, KSIDEVICE_HEADER, KsDevice);
 
     /* get device interface*/
-    KsDevice = (IKsDevice*)&DeviceHeader->lpVtblIKsDevice;
+    KsDevice = (IKsDevice*)&DeviceHeader->BasicHeader.OuterUnknown;
 
     /* release device mutex */
     KsDevice->lpVtbl->ReleaseDevice(KsDevice);
@@ -1512,7 +1670,7 @@ KsTerminateDevice(
     DeviceHeader = DeviceExtension->DeviceHeader;
 
     /* get device interface*/
-    KsDevice = (IKsDevice*)&DeviceHeader->lpVtblIKsDevice;
+    KsDevice = (IKsDevice*)&DeviceHeader->BasicHeader.OuterUnknown;
 
     /* now free device header */
     KsFreeDeviceHeader((KSDEVICE_HEADER)DeviceHeader);
@@ -1525,10 +1683,8 @@ KsTerminateDevice(
     }
 }
 
-
-
 /*
-    @unimplemented
+    @implemented
 */
 KSDDKAPI
 VOID
@@ -1536,11 +1692,39 @@ NTAPI
 KsCompletePendingRequest(
     IN PIRP Irp)
 {
+    PIO_STACK_LOCATION IoStack;
+
+    /* get current irp stack location */
+    IoStack = IoGetCurrentIrpStackLocation(Irp);
+
+    /* sanity check */
+    ASSERT(Irp->IoStatus.Status != STATUS_PENDING);
+
+    if (IoStack->MajorFunction != IRP_MJ_CLOSE)
+    {
+        /* can be completed immediately */
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        return;
+    }
+
+    /* did close operation fail */
+    if (!NT_SUCCESS(Irp->IoStatus.Status))
+    {
+        /* closing failed, complete irp */
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        return;
+    }
+
+    /* FIXME 
+     * delete object / device header 
+     * remove dead pin / filter instance
+     */
     UNIMPLEMENTED
+
 }
 
 /*
-    @unimplemented
+    @implemented
 */
 KSDDKAPI
 NTSTATUS
@@ -1553,12 +1737,194 @@ KsCreateBusEnumObject(
     IN REFGUID InterfaceGuid OPTIONAL,
     IN PWCHAR ServiceRelativePath OPTIONAL)
 {
-    UNIMPLEMENTED
-    return STATUS_UNSUCCESSFUL;
+    ULONG Length;
+    NTSTATUS Status = STATUS_SUCCESS;
+    UNICODE_STRING ServiceKeyPath = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\");
+    PBUS_ENUM_DEVICE_EXTENSION BusDeviceExtension;
+    PDEVICE_EXTENSION DeviceExtension;
+
+    /* calculate sizeof bus enum device extension */
+    Length = wcslen(BusIdentifier) * sizeof(WCHAR);
+    Length += sizeof(BUS_ENUM_DEVICE_EXTENSION);
+
+    BusDeviceExtension = ExAllocatePool(NonPagedPool, Length);
+    if (!BusDeviceExtension)
+    {
+        /* not enough memory */
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* zero device extension */
+    RtlZeroMemory(BusDeviceExtension, sizeof(BUS_ENUM_DEVICE_EXTENSION));
+
+    /* initialize bus device extension */
+    wcscpy(BusDeviceExtension->BusIdentifier, BusIdentifier);
+
+    /* allocate service path string */
+    Length = ServiceKeyPath.MaximumLength;
+    Length += BusDeviceObject->DriverObject->DriverExtension->ServiceKeyName.MaximumLength;
+
+    if (ServiceRelativePath)
+    {
+        /* relative path for devices */
+        Length += wcslen(ServiceRelativePath) + 2 * sizeof(WCHAR);
+    }
+
+    BusDeviceExtension->ServicePath.Length = 0;
+    BusDeviceExtension->ServicePath.MaximumLength = Length;
+    BusDeviceExtension->ServicePath.Buffer = ExAllocatePool(NonPagedPool, Length);
+
+    if (!BusDeviceExtension->ServicePath.Buffer)
+    {
+        /* not enough memory */
+        ExFreePool(BusDeviceExtension);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    RtlAppendUnicodeStringToString(&BusDeviceExtension->ServicePath, &ServiceKeyPath);
+    RtlAppendUnicodeStringToString(&BusDeviceExtension->ServicePath, &BusDeviceObject->DriverObject->DriverExtension->ServiceKeyName);
+
+    if (ServiceRelativePath)
+    {
+        RtlAppendUnicodeToString(&BusDeviceExtension->ServicePath, L"\\");
+        RtlAppendUnicodeToString(&BusDeviceExtension->ServicePath, ServiceRelativePath);
+    }
+
+    if (InterfaceGuid)
+    {
+        /* register an device interface */
+        Status = IoRegisterDeviceInterface(PhysicalDeviceObject, InterfaceGuid, NULL, &BusDeviceExtension->SymbolicLinkName);
+
+        /* check for success */
+        if (!NT_SUCCESS(Status))
+        {
+            ExFreePool(BusDeviceExtension->ServicePath.Buffer);
+            ExFreePool(BusDeviceExtension);
+            return Status;
+        }
+
+        /* now enable device interface */
+        Status = IoSetDeviceInterfaceState(&BusDeviceExtension->SymbolicLinkName, TRUE);
+
+        if (!NT_SUCCESS(Status))
+        {
+            ExFreePool(BusDeviceExtension->ServicePath.Buffer);
+            ExFreePool(BusDeviceExtension);
+            return Status;
+        }
+
+        /* set state enabled */
+        BusDeviceExtension->Enabled = TRUE;
+    }
+
+    /* store device objects */
+    BusDeviceExtension->BusDeviceObject = BusDeviceObject;
+    BusDeviceExtension->PnpDeviceObject = PnpDeviceObject;
+    BusDeviceExtension->PhysicalDeviceObject = PhysicalDeviceObject;
+
+    if (!PnpDeviceObject)
+    {
+        BusDeviceExtension->PnpDeviceObject = IoAttachDeviceToDeviceStack(BusDeviceObject, PhysicalDeviceObject);
+
+        if (!BusDeviceExtension->PnpDeviceObject)
+        {
+            /* failed to attach device */
+            if (BusDeviceExtension->Enabled)
+            {
+                IoSetDeviceInterfaceState(&BusDeviceExtension->SymbolicLinkName, FALSE);
+                RtlFreeUnicodeString(&BusDeviceExtension->SymbolicLinkName);
+            }
+
+            /* free device extension */
+            ExFreePool(BusDeviceExtension->ServicePath.Buffer);
+            ExFreePool(BusDeviceExtension);
+
+            return STATUS_DEVICE_REMOVED;
+        }
+    }
+
+    /* attach device extension */
+    DeviceExtension = (PDEVICE_EXTENSION)BusDeviceObject->DeviceExtension;
+    DeviceExtension->DeviceHeader = (PKSIDEVICE_HEADER)BusDeviceExtension;
+
+    /* FIXME scan bus and invalidate device relations */
+    return Status;
+}
+
+ NTSTATUS
+NTAPI
+KspSetGetBusDataCompletion(
+    IN PDEVICE_OBJECT  DeviceObject,
+    IN PIRP  Irp,
+    IN PVOID  Context)
+{
+    /* signal completion */
+    KeSetEvent((PRKEVENT)Context, IO_NO_INCREMENT, FALSE);
+
+    /* more work needs be done, so dont free the irp */
+    return STATUS_MORE_PROCESSING_REQUIRED;
+
+}
+
+NTSTATUS
+KspDeviceSetGetBusData(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN ULONG DataType,
+    IN PVOID Buffer,
+    IN ULONG Offset,
+    IN ULONG Length,
+    IN BOOL bGet)
+{
+    PIO_STACK_LOCATION IoStack;
+    PIRP Irp;
+    NTSTATUS Status;
+    KEVENT Event;
+
+    /* allocate the irp */
+    Irp = IoAllocateIrp(1, /*FIXME */
+                        FALSE);
+
+    if (!Irp)
+        return STATUS_INSUFFICIENT_RESOURCES;
+
+    /* initialize the event */
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    /* get next stack location */
+    IoStack = IoGetNextIrpStackLocation(Irp);
+
+    /* setup a completion routine */
+    IoSetCompletionRoutine(Irp, KspSetGetBusDataCompletion, (PVOID)&Event, TRUE, TRUE, TRUE);
+
+    /* setup parameters */
+    IoStack->Parameters.ReadWriteConfig.Buffer = Buffer;
+    IoStack->Parameters.ReadWriteConfig.Length = Length;
+    IoStack->Parameters.ReadWriteConfig.Offset = Offset;
+    IoStack->Parameters.ReadWriteConfig.WhichSpace = DataType;
+    /* setup function code */
+    IoStack->MajorFunction = IRP_MJ_PNP;
+    IoStack->MinorFunction = (bGet ? IRP_MN_READ_CONFIG : IRP_MN_WRITE_CONFIG);
+
+    /* lets call the driver */
+    Status = IoCallDriver(DeviceObject, Irp);
+
+    /* is the request still pending */
+    if (Status == STATUS_PENDING) 
+    {
+        /* have a nap */
+        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+        /* update status */
+        Status = Irp->IoStatus.Status;
+    }
+
+    /* free the irp */
+    IoFreeIrp(Irp);
+    /* done */
+    return Status;
 }
 
 /*
-    @unimplemented
+    @implemented
 */
 KSDDKAPI
 ULONG
@@ -1570,13 +1936,13 @@ KsDeviceSetBusData(
     IN ULONG Offset,
     IN ULONG Length)
 {
-    UNIMPLEMENTED
-    return 0;
+    return KspDeviceSetGetBusData(Device->PhysicalDeviceObject, /* is this right? */
+                                  DataType, Buffer, Offset, Length, FALSE);
 }
 
 
 /*
-    @unimplemented
+    @implemented
 */
 KSDDKAPI
 ULONG
@@ -1588,12 +1954,13 @@ KsDeviceGetBusData(
     IN ULONG Offset,
     IN ULONG Length)
 {
-    UNIMPLEMENTED
-    return 0;
+    return KspDeviceSetGetBusData(Device->PhysicalDeviceObject, /* is this right? */
+                                  DataType, Buffer, Offset, Length, TRUE);
+
 }
 
 /*
-    @unimplemented
+    @implemented
 */
 KSDDKAPI
 void
@@ -1604,24 +1971,12 @@ KsDeviceRegisterAdapterObject(
     IN ULONG MaxMappingsByteCount,
     IN ULONG MappingTableStride)
 {
-    UNIMPLEMENTED
-}
+    PKSIDEVICE_HEADER DeviceHeader = (PKSIDEVICE_HEADER)CONTAINING_RECORD(Device, KSIDEVICE_HEADER, KsDevice);
+
+    DeviceHeader->AdapterObject = AdapterObject;
+    DeviceHeader->MaxMappingsByteCount = MaxMappingsByteCount;
+    DeviceHeader->MappingTableStride = MappingTableStride;
 
-/*
-    @unimplemented
-*/
-KSDDKAPI
-NTSTATUS
-NTAPI
-_KsEdit(
-    IN KSOBJECT_BAG ObjectBag,
-    IN OUT PVOID* PointerToPointerToItem,
-    IN ULONG NewSize,
-    IN ULONG OldSize,
-    IN ULONG Tag)
-{
-    UNIMPLEMENTED
-    return STATUS_UNSUCCESSFUL;
 }
 
 /*
@@ -1634,6 +1989,7 @@ KsGetBusEnumIdentifier(
     IN PIRP Irp)
 {
     UNIMPLEMENTED
+
     return STATUS_UNSUCCESSFUL;
 }
 
@@ -1666,7 +2022,7 @@ KsGetBusEnumPnpDeviceObject(
 }
 
 /*
-    @unimplemented
+    @implemented
 */
 KSDDKAPI
 PVOID
@@ -1674,12 +2030,19 @@ NTAPI
 KsGetFirstChild(
     IN PVOID Object)
 {
-    UNIMPLEMENTED
-    return NULL;
+    PKSBASIC_HEADER BasicHeader;
+
+    /* get the basic header */
+    BasicHeader = (PKSBASIC_HEADER)((ULONG_PTR)Object - sizeof(KSBASIC_HEADER));
+
+    /* type has to be either a device or a filter factory */
+    ASSERT(BasicHeader->Type == KsObjectTypeDevice || BasicHeader->Type == KsObjectTypeFilterFactory);
+
+    return (PVOID)BasicHeader->FirstChild.Filter;
 }
 
 /*
-    @unimplemented
+    @implemented
 */
 KSDDKAPI
 PVOID
@@ -1687,8 +2050,15 @@ NTAPI
 KsGetNextSibling(
     IN PVOID Object)
 {
-    UNIMPLEMENTED
-    return NULL;
+    PKSBASIC_HEADER BasicHeader;
+
+    /* get the basic header */
+    BasicHeader = (PKSBASIC_HEADER)((ULONG_PTR)Object - sizeof(KSBASIC_HEADER));
+
+    ASSERT(BasicHeader->Type == KsObjectTypeDevice || BasicHeader->Type == KsObjectTypeFilterFactory || 
+           BasicHeader->Type == KsObjectTypeFilter || BasicHeader->Type == KsObjectTypePin);
+
+    return (PVOID)BasicHeader->Next.Pin;
 }
 
 /*
@@ -1718,19 +2088,514 @@ KsIsBusEnumChildDevice(
     return STATUS_UNSUCCESSFUL;
 }
 
-/*
-    @unimplemented
-*/
-NTSTATUS
-NTAPI
-KsMergeAutomationTables(
-    OUT PKSAUTOMATION_TABLE  *AutomationTableAB,
+ULONG
+KspCountMethodSets(
     IN PKSAUTOMATION_TABLE  AutomationTableA OPTIONAL,
-    IN PKSAUTOMATION_TABLE  AutomationTableB OPTIONAL,
+    IN PKSAUTOMATION_TABLE  AutomationTableB OPTIONAL)
+{
+    ULONG Index, SubIndex, Count;
+    BOOL bFound;
+
+    if (!AutomationTableA)
+        return AutomationTableB->MethodSetsCount;
+
+    if (!AutomationTableB)
+        return AutomationTableA->MethodSetsCount;
+
+
+    DPRINT("AutomationTableA MethodItemSize %lu MethodSetsCount %lu\n", AutomationTableA->MethodItemSize, AutomationTableA->MethodSetsCount);
+    DPRINT("AutomationTableB MethodItemSize %lu MethodSetsCount %lu\n", AutomationTableB->MethodItemSize, AutomationTableB->MethodSetsCount);
+
+    if (AutomationTableA->MethodItemSize && AutomationTableB->MethodItemSize)
+    {
+        /* sanity check */
+        ASSERT(AutomationTableA->MethodItemSize  == AutomationTableB->MethodItemSize);
+    }
+
+    /* now iterate all property sets and compare their guids */
+    Count = AutomationTableA->MethodSetsCount;
+
+    for(Index = 0; Index < AutomationTableB->MethodSetsCount; Index++)
+    {
+        /* set found to false */
+        bFound = FALSE;
+
+        for(SubIndex = 0; SubIndex < AutomationTableA->MethodSetsCount; SubIndex++)
+        {
+            if (IsEqualGUIDAligned(AutomationTableB->MethodSets[Index].Set, AutomationTableA->MethodSets[SubIndex].Set))
+            {
+                /* same property set found */
+                bFound = TRUE;
+                break;
+            }
+        }
+
+        if (!bFound)
+            Count++;
+    }
+
+    return Count;
+}
+
+ULONG
+KspCountEventSets(
+    IN PKSAUTOMATION_TABLE  AutomationTableA OPTIONAL,
+    IN PKSAUTOMATION_TABLE  AutomationTableB OPTIONAL)
+{
+    ULONG Index, SubIndex, Count;
+    BOOL bFound;
+
+    if (!AutomationTableA)
+        return AutomationTableB->EventSetsCount;
+
+    if (!AutomationTableB)
+        return AutomationTableA->EventSetsCount;
+
+    DPRINT("AutomationTableA EventItemSize %lu EventSetsCount %lu\n", AutomationTableA->EventItemSize, AutomationTableA->EventSetsCount);
+    DPRINT("AutomationTableB EventItemSize %lu EventSetsCount %lu\n", AutomationTableB->EventItemSize, AutomationTableB->EventSetsCount);
+
+    if (AutomationTableA->EventItemSize && AutomationTableB->EventItemSize)
+    {
+        /* sanity check */
+        ASSERT(AutomationTableA->EventItemSize == AutomationTableB->EventItemSize);
+    }
+
+    /* now iterate all Event sets and compare their guids */
+    Count = AutomationTableA->EventSetsCount;
+
+    for(Index = 0; Index < AutomationTableB->EventSetsCount; Index++)
+    {
+        /* set found to false */
+        bFound = FALSE;
+
+        for(SubIndex = 0; SubIndex < AutomationTableA->EventSetsCount; SubIndex++)
+        {
+            if (IsEqualGUIDAligned(AutomationTableB->EventSets[Index].Set, AutomationTableA->EventSets[SubIndex].Set))
+            {
+                /* same Event set found */
+                bFound = TRUE;
+                break;
+            }
+        }
+
+        if (!bFound)
+            Count++;
+    }
+
+    return Count;
+}
+
+
+ULONG
+KspCountPropertySets(
+    IN PKSAUTOMATION_TABLE  AutomationTableA OPTIONAL,
+    IN PKSAUTOMATION_TABLE  AutomationTableB OPTIONAL)
+{
+    ULONG Index, SubIndex, Count;
+    BOOL bFound;
+
+    if (!AutomationTableA)
+        return AutomationTableB->PropertySetsCount;
+
+    if (!AutomationTableB)
+        return AutomationTableA->PropertySetsCount;
+
+    /* sanity check */
+    DPRINT("AutomationTableA PropertyItemSize %lu PropertySetsCount %lu\n", AutomationTableA->PropertyItemSize, AutomationTableA->PropertySetsCount);
+    DPRINT("AutomationTableB PropertyItemSize %lu PropertySetsCount %lu\n", AutomationTableB->PropertyItemSize, AutomationTableB->PropertySetsCount);
+    ASSERT(AutomationTableA->PropertyItemSize == AutomationTableB->PropertyItemSize);
+
+    /* now iterate all property sets and compare their guids */
+    Count = AutomationTableA->PropertySetsCount;
+
+    for(Index = 0; Index < AutomationTableB->PropertySetsCount; Index++)
+    {
+        /* set found to false */
+        bFound = FALSE;
+
+        for(SubIndex = 0; SubIndex < AutomationTableA->PropertySetsCount; SubIndex++)
+        {
+            if (IsEqualGUIDAligned(AutomationTableB->PropertySets[Index].Set, AutomationTableA->PropertySets[SubIndex].Set))
+            {
+                /* same property set found */
+                bFound = TRUE;
+                break;
+            }
+        }
+
+        if (!bFound)
+            Count++;
+    }
+
+    return Count;
+}
+
+NTSTATUS
+KspCopyMethodSets(
+    OUT PKSAUTOMATION_TABLE  Table,
+    IN PKSAUTOMATION_TABLE  AutomationTableA OPTIONAL,
+    IN PKSAUTOMATION_TABLE  AutomationTableB OPTIONAL)
+{
+    ULONG Index, SubIndex, Count;
+    BOOL bFound;
+
+    if (!AutomationTableA)
+    {
+        /* copy of property set */
+        RtlMoveMemory((PVOID)Table->MethodSets, AutomationTableB->MethodSets, sizeof(KSMETHOD_SET) * AutomationTableB->MethodSetsCount);
+        return STATUS_SUCCESS;
+    }
+    else if (!AutomationTableB)
+    {
+        /* copy of property set */
+        RtlMoveMemory((PVOID)Table->MethodSets, AutomationTableA->MethodSets, sizeof(KSMETHOD_SET) * AutomationTableA->MethodSetsCount);
+        return STATUS_SUCCESS;
+    }
+
+    /* first copy all property items from dominant table */
+    RtlMoveMemory((PVOID)Table->MethodSets, AutomationTableA->MethodSets, sizeof(KSMETHOD_SET) * AutomationTableA->MethodSetsCount);
+    /* set counter */
+    Count = AutomationTableA->MethodSetsCount;
+
+    /* now copy entries which arent available in the dominant table */
+    for(Index = 0; Index < AutomationTableB->MethodSetsCount; Index++)
+    {
+        /* set found to false */
+        bFound = FALSE;
+
+        for(SubIndex = 0; SubIndex < AutomationTableA->MethodSetsCount; SubIndex++)
+        {
+            if (IsEqualGUIDAligned(AutomationTableB->MethodSets[Index].Set, AutomationTableA->MethodSets[SubIndex].Set))
+            {
+                /* same property set found */
+                bFound = TRUE;
+                break;
+            }
+        }
+
+        if (!bFound)
+        {
+            /* copy new property item set */
+            RtlMoveMemory((PVOID)&Table->MethodSets[Count], &AutomationTableB->MethodSets[Index], sizeof(KSMETHOD_SET));
+            Count++;
+        }
+    }
+
+    return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+KspCopyPropertySets(
+    OUT PKSAUTOMATION_TABLE  Table,
+    IN PKSAUTOMATION_TABLE  AutomationTableA OPTIONAL,
+    IN PKSAUTOMATION_TABLE  AutomationTableB OPTIONAL)
+{
+    ULONG Index, SubIndex, Count;
+    BOOL bFound;
+
+    if (!AutomationTableA)
+    {
+        /* copy of property set */
+        RtlMoveMemory((PVOID)Table->PropertySets, AutomationTableB->PropertySets, sizeof(KSPROPERTY_SET) * AutomationTableB->PropertySetsCount);
+        return STATUS_SUCCESS;
+    }
+    else if (!AutomationTableB)
+    {
+        /* copy of property set */
+        RtlMoveMemory((PVOID)Table->PropertySets, AutomationTableA->PropertySets, sizeof(KSPROPERTY_SET) * AutomationTableA->PropertySetsCount);
+        return STATUS_SUCCESS;
+    }
+
+    /* first copy all property items from dominant table */
+    RtlMoveMemory((PVOID)Table->PropertySets, AutomationTableA->PropertySets, sizeof(KSPROPERTY_SET) * AutomationTableA->PropertySetsCount);
+    /* set counter */
+    Count = AutomationTableA->PropertySetsCount;
+
+    /* now copy entries which arent available in the dominant table */
+    for(Index = 0; Index < AutomationTableB->PropertySetsCount; Index++)
+    {
+        /* set found to false */
+        bFound = FALSE;
+
+        for(SubIndex = 0; SubIndex < AutomationTableA->PropertySetsCount; SubIndex++)
+        {
+            if (IsEqualGUIDAligned(AutomationTableB->PropertySets[Index].Set, AutomationTableA->PropertySets[SubIndex].Set))
+            {
+                /* same property set found */
+                bFound = TRUE;
+                break;
+            }
+        }
+
+        if (!bFound)
+        {
+            /* copy new property item set */
+            RtlMoveMemory((PVOID)&Table->PropertySets[Count], &AutomationTableB->PropertySets[Index], sizeof(KSPROPERTY_SET));
+            Count++;
+        }
+    }
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+KspCopyEventSets(
+    OUT PKSAUTOMATION_TABLE  Table,
+    IN PKSAUTOMATION_TABLE  AutomationTableA OPTIONAL,
+    IN PKSAUTOMATION_TABLE  AutomationTableB OPTIONAL)
+{
+    ULONG Index, SubIndex, Count;
+    BOOL bFound;
+
+    if (!AutomationTableA)
+    {
+        /* copy of Event set */
+        RtlMoveMemory((PVOID)Table->EventSets, AutomationTableB->EventSets, sizeof(KSEVENT_SET) * AutomationTableB->EventSetsCount);
+        return STATUS_SUCCESS;
+    }
+    else if (!AutomationTableB)
+    {
+        /* copy of Event set */
+        RtlMoveMemory((PVOID)Table->EventSets, AutomationTableA->EventSets, sizeof(KSEVENT_SET) * AutomationTableA->EventSetsCount);
+        return STATUS_SUCCESS;
+    }
+
+    /* first copy all Event items from dominant table */
+    RtlMoveMemory((PVOID)Table->EventSets, AutomationTableA->EventSets, sizeof(KSEVENT_SET) * AutomationTableA->EventSetsCount);
+    /* set counter */
+    Count = AutomationTableA->EventSetsCount;
+
+    /* now copy entries which arent available in the dominant table */
+    for(Index = 0; Index < AutomationTableB->EventSetsCount; Index++)
+    {
+        /* set found to false */
+        bFound = FALSE;
+
+        for(SubIndex = 0; SubIndex < AutomationTableA->EventSetsCount; SubIndex++)
+        {
+            if (IsEqualGUIDAligned(AutomationTableB->EventSets[Index].Set, AutomationTableA->EventSets[SubIndex].Set))
+            {
+                /* same Event set found */
+                bFound = TRUE;
+                break;
+            }
+        }
+
+        if (!bFound)
+        {
+            /* copy new Event item set */
+            RtlMoveMemory((PVOID)&Table->EventSets[Count], &AutomationTableB->EventSets[Index], sizeof(KSEVENT_SET));
+            Count++;
+        }
+    }
+
+    return STATUS_SUCCESS;
+}
+
+
+/*
+    @implemented
+*/
+NTSTATUS
+NTAPI
+KsMergeAutomationTables(
+    OUT PKSAUTOMATION_TABLE  *AutomationTableAB,
+    IN PKSAUTOMATION_TABLE  AutomationTableA OPTIONAL,
+    IN PKSAUTOMATION_TABLE  AutomationTableB OPTIONAL,
     IN KSOBJECT_BAG  Bag OPTIONAL)
 {
-    UNIMPLEMENTED
-    return STATUS_UNSUCCESSFUL;
+    PKSAUTOMATION_TABLE Table;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    if (!AutomationTableA && !AutomationTableB)
+    {
+        /* nothing to merge */
+        return STATUS_SUCCESS;
+    }
+
+    /* allocate an automation table */
+    Table = AllocateItem(NonPagedPool, sizeof(KSAUTOMATION_TABLE));
+    if (!Table)
+        return STATUS_INSUFFICIENT_RESOURCES;
+
+    if (Bag)
+    {
+        /* add table to object bag */
+        Status = KsAddItemToObjectBag(Bag, Table, NULL);
+        /* check for success */
+        if (!NT_SUCCESS(Status))
+        {
+            /* free table */
+            FreeItem(Table);
+            return Status;
+        }
+    }
+
+    /* count property sets */
+    Table->PropertySetsCount = KspCountPropertySets(AutomationTableA, AutomationTableB);
+
+    if (Table->PropertySetsCount)
+    {
+        if (AutomationTableA)
+        {
+            /* use item size from dominant automation table */
+            Table->PropertyItemSize = AutomationTableA->PropertyItemSize;
+        }
+        else
+        {
+            /* use item size from 2nd automation table */
+            Table->PropertyItemSize = AutomationTableB->PropertyItemSize;
+        }
+
+        /* now allocate the property sets */
+        Table->PropertySets = AllocateItem(NonPagedPool, sizeof(KSPROPERTY_SET) * Table->PropertySetsCount);
+
+        if (!Table->PropertySets)
+        {
+            /* not enough memory */
+            goto cleanup;
+        }
+
+        if (Bag)
+        {
+            /* add set to property bag */
+            Status = KsAddItemToObjectBag(Bag, (PVOID)Table->PropertySets, NULL);
+            /* check for success */
+            if (!NT_SUCCESS(Status))
+            {
+                /* cleanup table */
+                goto cleanup;
+            }
+        }
+        /* now copy the property sets */
+        Status = KspCopyPropertySets(Table, AutomationTableA, AutomationTableB);
+        if(!NT_SUCCESS(Status))
+            goto cleanup;
+
+    }
+
+    /* now count the method sets */
+    Table->MethodSetsCount = KspCountMethodSets(AutomationTableA, AutomationTableB);
+
+    if (Table->MethodSetsCount)
+    {
+        if (AutomationTableA)
+        {
+            /* use item size from dominant automation table */
+            Table->MethodItemSize  = AutomationTableA->MethodItemSize;
+        }
+        else
+        {
+            /* use item size from 2nd automation table */
+            Table->MethodItemSize = AutomationTableB->MethodItemSize;
+        }
+
+        /* now allocate the property sets */
+        Table->MethodSets = AllocateItem(NonPagedPool, sizeof(KSMETHOD_SET) * Table->MethodSetsCount);
+
+        if (!Table->MethodSets)
+        {
+            /* not enough memory */
+            goto cleanup;
+        }
+
+        if (Bag)
+        {
+            /* add set to property bag */
+            Status = KsAddItemToObjectBag(Bag, (PVOID)Table->MethodSets, NULL);
+            /* check for success */
+            if (!NT_SUCCESS(Status))
+            {
+                /* cleanup table */
+                goto cleanup;
+            }
+        }
+        /* now copy the property sets */
+        Status = KspCopyMethodSets(Table, AutomationTableA, AutomationTableB);
+        if(!NT_SUCCESS(Status))
+            goto cleanup;
+    }
+
+
+    /* now count the event sets */
+    Table->EventSetsCount = KspCountEventSets(AutomationTableA, AutomationTableB);
+
+    if (Table->EventSetsCount)
+    {
+        if (AutomationTableA)
+        {
+            /* use item size from dominant automation table */
+            Table->EventItemSize  = AutomationTableA->EventItemSize;
+        }
+        else
+        {
+            /* use item size from 2nd automation table */
+            Table->EventItemSize = AutomationTableB->EventItemSize;
+        }
+
+        /* now allocate the property sets */
+        Table->EventSets = AllocateItem(NonPagedPool, sizeof(KSEVENT_SET) * Table->EventSetsCount);
+
+        if (!Table->EventSets)
+        {
+            /* not enough memory */
+            goto cleanup;
+        }
+
+        if (Bag)
+        {
+            /* add set to property bag */
+            Status = KsAddItemToObjectBag(Bag, (PVOID)Table->EventSets, NULL);
+            /* check for success */
+            if (!NT_SUCCESS(Status))
+            {
+                /* cleanup table */
+                goto cleanup;
+            }
+        }
+        /* now copy the property sets */
+        Status = KspCopyEventSets(Table, AutomationTableA, AutomationTableB);
+        if(!NT_SUCCESS(Status))
+            goto cleanup;
+    }
+
+    /* store result */
+    *AutomationTableAB = Table;
+
+    return Status;
+
+
+cleanup:
+
+    if (Table)
+    {
+        if (Table->PropertySets)
+        {
+            /* clean property sets */
+            if (!Bag || !NT_SUCCESS(KsRemoveItemFromObjectBag(Bag, (PVOID)Table->PropertySets, TRUE)))
+                FreeItem((PVOID)Table->PropertySets);
+        }
+
+        if (Table->MethodSets)
+        {
+            /* clean property sets */
+            if (!Bag || !NT_SUCCESS(KsRemoveItemFromObjectBag(Bag, (PVOID)Table->MethodSets, TRUE)))
+                FreeItem((PVOID)Table->MethodSets);
+        }
+
+        if (Table->EventSets)
+        {
+            /* clean property sets */
+            if (!Bag || !NT_SUCCESS(KsRemoveItemFromObjectBag(Bag, (PVOID)Table->EventSets, TRUE)))
+                FreeItem((PVOID)Table->EventSets);
+        }
+
+        if (!Bag || !NT_SUCCESS(KsRemoveItemFromObjectBag(Bag, Table, TRUE)))
+                FreeItem(Table);
+    }
+
+    return STATUS_INSUFFICIENT_RESOURCES;
 }
 
 /*
@@ -1762,6 +2627,28 @@ KsServiceBusEnumPnpRequest(
     return STATUS_UNSUCCESSFUL;
 }
 
+VOID
+NTAPI
+KspRemoveBusInterface(
+    PVOID Ctx)
+{
+    PKSREMOVE_BUS_INTERFACE_CTX Context =(PKSREMOVE_BUS_INTERFACE_CTX)Ctx;
+
+    /* TODO
+     * get SWENUM_INSTALL_INTERFACE struct
+     * open device key and delete the keys
+     */
+
+    UNIMPLEMENTED
+
+    /* set status */
+    Context->Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
+
+
+    /* signal completion */
+    KeSetEvent(&Context->Event, IO_NO_INCREMENT, FALSE);
+}
+
 /*
     @unimplemented
 */
@@ -1771,10 +2658,42 @@ NTAPI
 KsRemoveBusEnumInterface(
     IN PIRP Irp)
 {
-    UNIMPLEMENTED
-    return STATUS_UNSUCCESSFUL;
-}
+    KPROCESSOR_MODE Mode;
+    LUID luid;
+    KSREMOVE_BUS_INTERFACE_CTX Ctx;
+    WORK_QUEUE_ITEM WorkItem;
+
+    /* get previous mode */
+    Mode = ExGetPreviousMode();
+
+    /* convert to luid */
+    luid = RtlConvertUlongToLuid(SE_LOAD_DRIVER_PRIVILEGE);
 
+    /* perform access check */
+    if (!SeSinglePrivilegeCheck(luid, Mode))
+    {
+        /* insufficient privileges */
+        return STATUS_PRIVILEGE_NOT_HELD;
+    }
+    /* initialize event */
+    KeInitializeEvent(&Ctx.Event, NotificationEvent, FALSE);
+
+    /* store irp in ctx */
+    Ctx.Irp = Irp;
+
+    /* initialize work item */
+    ExInitializeWorkItem(&WorkItem, KspRemoveBusInterface, (PVOID)&Ctx);
+
+    /* now queue the work item */
+    ExQueueWorkItem(&WorkItem, DelayedWorkQueue);
+
+    /* wait for completion */
+    KeWaitForSingleObject(&Ctx.Event, Executive, KernelMode, FALSE, NULL);
+
+    /* return result */
+    return Ctx.Irp->IoStatus.Status;
+
+}
 
 
 /*
@@ -1787,19 +2706,26 @@ KsRegisterAggregatedClientUnknown(
     IN PVOID  Object,
     IN PUNKNOWN  ClientUnknown)
 {
-    UNIMPLEMENTED
-    return NULL;
-}
+    PKSBASIC_HEADER BasicHeader = (PKSBASIC_HEADER)((ULONG_PTR)Object - sizeof(KSBASIC_HEADER));
 
-/*
-    @unimplemented
-*/
-VOID
-NTAPI
-KsReleaseControl(
-    IN PVOID  Object)
-{
-    UNIMPLEMENTED
+    /* sanity check */
+    ASSERT(BasicHeader->Type == KsObjectTypeDevice || BasicHeader->Type == KsObjectTypeFilterFactory || 
+           BasicHeader->Type == KsObjectTypeFilter || BasicHeader->Type == KsObjectTypePin);
+
+    if (BasicHeader->ClientAggregate)
+    {
+        /* release existing aggregate */
+        BasicHeader->ClientAggregate->lpVtbl->Release(BasicHeader->ClientAggregate);
+    }
+
+    /* increment reference count */
+    ClientUnknown->lpVtbl->AddRef(ClientUnknown);
+
+    /* store client aggregate */
+    BasicHeader->ClientAggregate = ClientUnknown;
+
+    /* return objects outer unknown */
+    return BasicHeader->OuterUnknown;
 }
 
 /*
@@ -1815,6 +2741,62 @@ KsRegisterFilterWithNoKSPins(
     IN KSPIN_MEDIUM*  MediumList,
     IN GUID*  CategoryList OPTIONAL)
 {
+    ULONG Size, Index;
+    NTSTATUS Status;
+    PWSTR SymbolicLinkList;
+    //PUCHAR Buffer;
+    HANDLE hKey;
+    UNICODE_STRING InterfaceString;
+    //UNICODE_STRING FilterData = RTL_CONSTANT_STRING(L"FilterData");
+
+    if (!InterfaceClassGUID || !PinCount || !PinDirection || !MediumList)
+    {
+        /* all these parameters are required */
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* calculate filter data value size */
+    Size = PinCount * sizeof(KSPIN_MEDIUM);
+    if (CategoryList)
+    {
+        /* add category list */
+        Size += PinCount * sizeof(GUID);
+    }
+
+    /* FIXME generate filter data blob */
     UNIMPLEMENTED
-    return STATUS_UNSUCCESSFUL;
+
+    /* get symbolic link list */
+    Status = IoGetDeviceInterfaces(InterfaceClassGUID, DeviceObject, DEVICE_INTERFACE_INCLUDE_NONACTIVE, &SymbolicLinkList);
+    if (NT_SUCCESS(Status))
+    {
+        /* initialize first symbolic link */
+        RtlInitUnicodeString(&InterfaceString, SymbolicLinkList);
+
+        /* open first device interface registry key */
+        Status = IoOpenDeviceInterfaceRegistryKey(&InterfaceString, GENERIC_WRITE, &hKey);
+
+        if (NT_SUCCESS(Status))
+        {
+            /* write filter data */
+            //Status = ZwSetValueKey(hKey, &FilterData, 0, REG_BINARY, Buffer, Size);
+
+            /* close the key */
+            ZwClose(hKey);
+        }
+
+        if (PinCount)
+        {
+            /* update medium cache */
+            for(Index = 0; Index < PinCount; Index++)
+            {
+                KsCacheMedium(&InterfaceString, &MediumList[Index], PinDirection[Index]);
+            }
+        }
+
+        /* free the symbolic link list */
+        ExFreePool(SymbolicLinkList);
+    }
+
+    return Status;
 }