Sync with trunk.
[reactos.git] / drivers / sac / driver / chanmgr.c
index 1a655d9..a005a1e 100644 (file)
 /*
- * PROJECT:             ReactOS Boot Loader
- * LICENSE:             BSD - See COPYING.ARM in the top level directory
- * FILE:                drivers/sac/driver/chanmgr.c
- * PURPOSE:             Driver for the Server Administration Console (SAC) for EMS
- * PROGRAMMERS:         ReactOS Portable Systems Group
+ * PROJECT:     ReactOS Drivers
+ * LICENSE:     BSD - See COPYING.ARM in the top level directory
+ * FILE:        drivers/sac/driver/chanmgr.c
+ * PURPOSE:     Driver for the Server Administration Console (SAC) for EMS
+ * PROGRAMMERS: ReactOS Portable Systems Group
  */
 
-/* INCLUDES *******************************************************************/
+/* INCLUDES ******************************************************************/
 
 #include "sacdrv.h"
 
-/* GLOBALS ********************************************************************/
+/* GLOBALS *******************************************************************/
 
-/* FUNCTIONS ******************************************************************/
+SAC_CHANNEL_LOCK ChannelCreateLock;
+BOOLEAN ChannelCreateEnabled;
+PSAC_CHANNEL ChannelArray[SAC_MAX_CHANNELS];
+LONG ChannelRefCount[SAC_MAX_CHANNELS];
+LONG ChannelReaped[SAC_MAX_CHANNELS];
+SAC_CHANNEL_LOCK ChannelSlotLock[SAC_MAX_CHANNELS];
+LONG CurrentChannelRefCount;
+KMUTEX CurrentChannelLock;
 
-NTSTATUS
-ChanMgrInitialize(
-       VOID
-       )
+/* FUNCTIONS *****************************************************************/
+
+#define MAX_REF_COUNT 100
+
+#define CHANNEL_SLOT_IS_IN_USE(x)   (ChannelRefCount[(x)] > 0)
+
+FORCEINLINE
+PSAC_CHANNEL
+ChannelFromIndex(IN ULONG Index)
+{
+    return ChannelArray[Index];
+}
+
+FORCEINLINE
+LONG
+ChannelGetReferenceCount(IN LONG Index)
+{
+    return ChannelRefCount[Index];
+}
+
+FORCEINLINE
+LONG
+ChannelReferenceByIndex(IN LONG Index)
+{
+    if (ChannelGetReferenceCount(Index) > 0)
+    {
+        ASSERT(ChannelRefCount[Index] <= MAX_REF_COUNT);
+        ASSERT(ChannelRefCount[Index] >= 1);
+        _InterlockedIncrement(&ChannelRefCount[Index]);
+        ASSERT(ChannelRefCount[Index] <= MAX_REF_COUNT);
+        ASSERT(ChannelRefCount[Index] >= 2);
+    }
+
+    return ChannelGetReferenceCount(Index);
+}
+
+FORCEINLINE
+LONG
+ChannelReferenceByIndexWithLock(IN LONG Index)
+{
+    LONG RefCount;
+
+    ChannelSlotLock(Index);
+    RefCount = ChannelReferenceByIndex(Index);
+    ChannelSlotUnlock(Index);
+    return RefCount;
+}
+
+FORCEINLINE
+LONG
+ChannelDereferenceByIndex(IN LONG Index)
+{
+    ASSERT(ChannelGetReferenceCount(Index) <= MAX_REF_COUNT);
+    ASSERT(ChannelGetReferenceCount(Index) > 1);
+    _InterlockedDecrement(&ChannelRefCount[Index]);
+    ASSERT(ChannelGetReferenceCount(Index) >= 1);
+    return ChannelGetReferenceCount(Index);
+}
+
+FORCEINLINE
+VOID
+ChannelDereferenceByIndexWithLock(IN LONG Index)
+{
+    ChannelSlotLock(Index);
+    ChannelDereferenceByIndex(Index);
+    ChannelSlotUnlock(Index);
+}
+
+FORCEINLINE
+VOID
+ChannelDereferenceToZeroByIndex(IN LONG Index)
+{
+    ASSERT(ChannelGetReferenceCount(Index) == 1);
+    ASSERT(ChannelIsActive(ChannelFromIndex(Index)) == FALSE);
+    _InterlockedExchange(&ChannelRefCount[Index], 0);
+}
+
+FORCEINLINE
+VOID
+ChannelReferenceToOneByIndex(IN LONG Index)
+{
+    ASSERT(ChannelGetReferenceCount(Index) == 0);
+    _InterlockedExchange(&ChannelRefCount[Index], 1);
+}
+
+FORCEINLINE
+VOID
+ChannelReferenceToOneByIndexWithLock(IN LONG Index)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    ChannelSlotLock(Index);
+    ASSERT(ChannelGetReferenceCount((Index)) == 1);
+    _InterlockedExchange(&ChannelRefCount[Index], 1);
+    ChannelSlotUnlock(Index);
 }
 
 NTSTATUS
-ChanMgrGetChannelIndex(
-       IN PSAC_CHANNEL Channel,
-       IN PULONG ChannelIndex
-       )
+NTAPI
+ChanMgrInitialize(VOID)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    ULONG i;
+
+    /* Initialize the channel lock */
+    SacInitializeLock(&ChannelCreateLock);
+    ChannelCreateEnabled = TRUE;
+
+    /* Loop through the channel arrays */
+    for (i = 0; i < SAC_MAX_CHANNELS; i++)
+    {
+        /* Clear and initialize their locks */
+        ChannelArray[i] = NULL;
+        SacInitializeLock(&ChannelSlotLock[i]);
+
+        /* Clear their statuses and reference counts */
+        _InterlockedExchange(&ChannelRefCount[i], 0);
+        _InterlockedExchange(&ChannelReaped[i], 1);
+    }
+
+    /* All good */
+    return STATUS_SUCCESS;
 }
 
 NTSTATUS
-ChanMgrReleaseChannel(
-       IN PSAC_CHANNEL Channel
-       )
+NTAPI
+ChanMgrShutdown(VOID)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    /* FIXME: TODO */
+    return STATUS_NOT_IMPLEMENTED;
 }
 
 NTSTATUS
-ChanMgrGetByHandle(
-       IN PSAC_CHANNEL_ID ChannelId,
-       OUT PSAC_CHANNEL pChannel
-       )
+NTAPI
+ChanMgrGetChannelByName(IN PWCHAR Name,
+                        OUT PSAC_CHANNEL* Channel)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status, Status1;
+    ULONG i;
+    PSAC_CHANNEL CurrentChannel;
+    PWCHAR ChannelName;
+    BOOLEAN Found;
+    CHECK_PARAMETER1(Name);
+    CHECK_PARAMETER2(Channel);
+
+    /* Assume failure */
+    *Channel = NULL;
+    Status = STATUS_NOT_FOUND;
+
+    /* Loop through all channels */
+    for (i = 0; i < SAC_MAX_CHANNELS; i++)
+    {
+        /* Reference this one and check if it's valid */
+        if (ChannelReferenceByIndexWithLock(i) > 0)
+        {
+            /* All good, grab it */
+            CurrentChannel = ChannelFromIndex(i);
+            ASSERT(CurrentChannel != NULL);
+
+            /* Get its name */
+            Status1 = ChannelGetName(CurrentChannel, &ChannelName);
+            ASSERT(NT_SUCCESS(Status1));
+
+            /* Check if this is the name that was passed in */
+            Found = wcsicmp(Name, ChannelName);
+            SacFreePool(ChannelName);
+            if (Found)
+            {
+                /* We found it, return it (with a reference held) */
+                *Channel = CurrentChannel;
+                return STATUS_SUCCESS;
+            }
+
+            /* Not the one we want, dereference this one and keep going */
+            ChannelDereferenceByIndexWithLock(i);
+        }
+    }
+
+    /* No channels with this name were found */
+    return Status;
 }
 
 NTSTATUS
-ChanMgrGetByHandleAndFileObject(
-       IN PSAC_CHANNEL_ID ChannelId,
-       IN PFILE_OBJECT FileObject,
-       OUT PSAC_CHANNEL pChannel
-       )
+NTAPI
+ChanMgrGetByHandle(IN SAC_CHANNEL_ID ChannelId,
+                   OUT PSAC_CHANNEL* TargetChannel)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+    ULONG i;
+    PSAC_CHANNEL Channel;
+    CHECK_PARAMETER2(TargetChannel);
+
+    /* Assume failure */
+    *TargetChannel = NULL;
+    Status = STATUS_NOT_FOUND;
+
+    /* Loop through all channels */
+    for (i = 0; i < SAC_MAX_CHANNELS; i++)
+    {
+        /* Reference this one and check if it's valid */
+        if (ChannelReferenceByIndexWithLock(i) > 0)
+        {
+            /* All good, grab it */
+            Channel = ChannelFromIndex(i);
+            ASSERT(Channel != NULL);
+
+            /* Check if the channel ID matches */
+            if (ChannelIsEqual(Channel, &ChannelId))
+            {
+                /* We found it, return it (with a reference held) */
+                *TargetChannel = Channel;
+                return STATUS_SUCCESS;
+            }
+
+            /* Not the one we want, dereference this one and keep going */
+            ChannelDereferenceByIndexWithLock(i);
+        }
+    }
+
+    /* No channels with this ID were found */
+    return Status;
 }
 
 NTSTATUS
-ChanMgrGetChannelByName(
-       IN PWCHAR Name,
-       OUT PSAC_CHANNEL pChannel
-       )
+NTAPI
+ChanMgrReleaseChannel(IN PSAC_CHANNEL Channel)
+{
+    LONG Index;
+    ULONG RefCount;
+    PSAC_CHANNEL ThisChannel;
+    CHECK_PARAMETER(Channel);
+
+    /* Get the index of the channel */
+    Index = ChannelGetIndex(Channel);
+
+    /* Drop a reference -- there should still be at least the keepalive left */
+    ChannelSlotLock(Index);
+    RefCount = ChannelDereferenceByIndex(Index);
+    ASSERT(RefCount > 0);
+
+    /* Do we only have the keep-alive left, and the channel is dead? */
+    if ((RefCount == 1) && !(ChannelIsActive(Channel)))
+    {
+        /* Check if the ??? flag is set, or if there's no output data */
+        ThisChannel = ChannelFromIndex(Index);
+        if (!(ThisChannel->Flags & 1))
+        {
+            /* Nope, we can wipe the references and get rid of it */
+            ChannelDereferenceToZeroByIndex(Index);
+        }
+        else if (!ThisChannel->ChannelHasNewOBufferData)
+        {
+            /* No data, we can wipe the references and get rid of it */
+            ChannelDereferenceToZeroByIndex(Index);
+        }
+    }
+
+    /* We're done, we can unlock the slot now */
+    ChannelSlotUnlock(Index);
+    return STATUS_SUCCESS;
+}
+
+BOOLEAN
+NTAPI
+ChanMgrIsUniqueName(IN PWCHAR ChannelName)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+    BOOLEAN IsUnique = FALSE;
+    PSAC_CHANNEL Channel;
+
+    /* Check if a channel with this name already exists */
+    Status = ChanMgrGetChannelByName(ChannelName, &Channel);
+    if (Status == STATUS_NOT_FOUND) IsUnique = TRUE;
+
+    /* If one did, dereference it, all we wanted was to check uniqueness */
+    if (NT_SUCCESS(Status)) ChanMgrReleaseChannel(Channel);
+
+    /* Return if one was found or not */
+    return IsUnique;
 }
 
 NTSTATUS
-ChanMgrGetByIndex(
-       IN ULONG TargetIndex,
-       IN PSAC_CHANNEL *TargetChannel
-       )
+NTAPI
+ChanMgrReapChannel(IN ULONG ChannelIndex)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    /* FIXME: TODO */
+    return STATUS_NOT_IMPLEMENTED;
 }
 
 NTSTATUS
-ChanMgrGetNextActiveChannel(
-       IN PSAC_CHANNEL CurrentChannel,
-       IN PULONG TargetIndex,
-       OUT PSAC_CHANNEL *TargetChannel
-       )
+NTAPI
+ChanMgrReapChannels(VOID)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    ULONG i;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    /* Loop all the channels */
+    for (i = 0; i < SAC_MAX_CHANNELS; i++)
+    {
+        /* Lock this index and see if the channel was reaped */
+        ChannelSlotLock(i);
+        if (!ChannelReaped[i])
+        {
+            /* It was not reaped yet, so a channel should still be here */
+            ASSERT(ChannelFromIndex(i) != NULL);
+            if (ChannelGetReferenceCount(i) <= 0)
+            {
+                /* The channel has no more references, so clear the buffer flags */
+                _InterlockedExchange(&ChannelArray[i]->ChannelHasNewIBufferData, 0);
+                _InterlockedExchange(&ChannelArray[i]->ChannelHasNewOBufferData, 0);
+
+                /* And reap it */
+                Status = ChanMgrReapChannel(i);
+            }
+        }
+
+        /* Release the lock, and move on unless reaping failed */
+        ChannelSlotUnlock(i);
+        if (!NT_SUCCESS(Status)) break;
+    }
+
+    /* Return reaping status */
+    return Status;
 }
 
-VOID
-ChanMgrChannelDestroy(
-       IN PSAC_CHANNEL Channel
-       )
+NTSTATUS
+NTAPI
+ChanMgrCreateChannel(OUT PSAC_CHANNEL *Channel,
+                     IN PSAC_CHANNEL_ATTRIBUTES Attributes)
 {
+    NTSTATUS Status;
+    PSAC_CHANNEL NewChannel;
+    SAC_CHANNEL_ID ChanId;
+    ULONG i;
+    CHECK_PARAMETER(Channel);
+    CHECK_PARAMETER2(Attributes);
+
+    /* No other channel create attempts can happen */
+    ChannelLockCreates();
+
+    /* Is the channel manager initialized? */
+    if (ChannelCreateEnabled)
+    {
+        /* Nope, bail out */
+        Status = STATUS_UNSUCCESSFUL;
+        goto ReturnStatus;
+    }
 
+    /* Reap any zombie channels */
+    Status = ChanMgrReapChannels();
+    if (!NT_SUCCESS(Status))
+    {
+        /* Bail out on error */
+        Status = STATUS_UNSUCCESSFUL;
+        goto ReturnStatus;
+    }
+
+    /* Check if we already have a channel with this name */
+    if (!ChanMgrIsUniqueName(Attributes->NameBuffer))
+    {
+        /* We do, fail */
+        Status = STATUS_DUPLICATE_NAME;
+        goto ReturnStatus;
+    }
+
+    /* Allocate this channel */
+    NewChannel = SacAllocatePool(sizeof(SAC_CHANNEL), CHANNEL_BLOCK_TAG);
+    CHECK_PARAMETER_WITH_STATUS(NewChannel, STATUS_NO_MEMORY); // bug
+    RtlZeroMemory(NewChannel, sizeof(SAC_CHANNEL));
+
+    /* Loop channel slots */
+    for (i = 0; i < SAC_MAX_CHANNELS; i++)
+    {
+        /* Find a free spot for it */
+        if (ChannelReaped[i])
+        {
+            /* Free slot found, attempt to use it */
+            ASSERT(!CHANNEL_SLOT_IS_IN_USE(i));
+            _InterlockedCompareExchange((PLONG)&ChannelArray[i], (LONG)NewChannel, 0);
+            if (ChannelArray[i] == NewChannel) break;
+        }
+    }
+
+    /* Did we not find a single free slot? */
+    if (i == SAC_MAX_CHANNELS)
+    {
+        /* Bail out */
+        Status = STATUS_UNSUCCESSFUL;
+        goto ReturnStatus;
+    }
+
+    /* Create an ID for this channel */
+    RtlZeroMemory(&ChanId, sizeof(ChanId));
+    Status = ExUuidCreate(&ChanId.ChannelGuid);
+    if (!NT_SUCCESS(Status))
+    {
+        /* Bail out if we couldn't */
+        SAC_DBG(SAC_DBG_INIT, "SAC Create Channel :: Failed to get GUID\n");
+        goto ReturnStatus;
+    }
+
+    /* Now create the channel proper */
+    Status = ChannelCreate(NewChannel, Attributes, ChanId);
+    if (NT_SUCCESS(Status))
+    {
+        /* Set the channel index */
+        _InterlockedExchange(&NewChannel->Index, i);
+
+        /* Add the initial reference to the channel */
+        ChannelReferenceToOneByIndexWithLock(i);
+
+        /* Return it to the caller */
+        *Channel = NewChannel;
+
+        /* This slot is now occupied */
+        ASSERT(ChannelReaped[i] == 1);
+        _InterlockedExchange(&ChannelReaped[i], 0);
+    }
+    else
+    {
+        /* We couldn't create it, free the buffer */
+        SacFreePool(NewChannel);
+    }
+
+ReturnStatus:
+    /* Return whatever the operation status was */
+    ChannelUnlockCreates();
+    return Status;
 }
 
 NTSTATUS
-ChanMgrCloseChannel(
-       IN PSAC_CHANNEL Channel
-       )
+NTAPI
+ChanMgrGetByHandleAndFileObject(IN SAC_CHANNEL_ID ChannelId,
+                                IN PFILE_OBJECT FileObject,
+                                OUT PSAC_CHANNEL* TargetChannel)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+    PSAC_CHANNEL FoundChannel;
+
+    /* Lookup the channel by ID first */
+    Status = ChanMgrGetByHandle(ChannelId, &FoundChannel);
+    if (NT_SUCCESS(Status))
+    {
+        /* We found it, now check if the file object matches */
+        if (FoundChannel->FileObject == FileObject)
+        {
+            /* Yep, return success */
+            *TargetChannel = FoundChannel;
+        }
+        else
+        {
+            /* Nope, drop the reference on the channel */
+            ChanMgrReleaseChannel(FoundChannel);
+
+            /* And return failure */
+            *TargetChannel = NULL;
+            Status = STATUS_NOT_FOUND;
+        }
+    }
+
+    /* Return if we found it or not */
+    return Status;
 }
 
 NTSTATUS
-ChanMgrReapChannel(
-       IN ULONG ChannelIndex
-       )
+NTAPI
+ChanMgrGetChannelIndex(IN PSAC_CHANNEL Channel,
+                       IN PLONG ChannelIndex)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    CHECK_PARAMETER1(Channel);
+    CHECK_PARAMETER2(ChannelIndex);
+
+    /* Just return the index of the channel */
+    *ChannelIndex = ChannelGetIndex(Channel);
+    return STATUS_SUCCESS;
 }
 
 NTSTATUS
-ChanMgrReapChannels(
-       VOID
-       )
+NTAPI
+ChanMgrGetByIndex(IN LONG TargetIndex,
+                  IN PSAC_CHANNEL* TargetChannel)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+    CHECK_PARAMETER1(TargetIndex < SAC_MAX_CHANNELS);
+    CHECK_PARAMETER2(TargetChannel);
+
+    /* Assume failure */
+    *TargetChannel = NULL;
+    Status = STATUS_NOT_FOUND;
+
+    /* Reference this one and check if it's valid */
+    if (ChannelReferenceByIndexWithLock(TargetIndex) > 0)
+    {
+        /* We found it, return it (with a reference held) */
+        *TargetChannel = ChannelFromIndex(TargetIndex);
+        return STATUS_SUCCESS;
+    }
+
+    /* No channels with this ID were found */
+    return Status;
 }
 
 NTSTATUS
-ChanMgrGetChannelCount(
-       OUT PULONG ChannelCount
-       )
+NTAPI
+ChanMgrGetNextActiveChannel(IN PSAC_CHANNEL CurrentChannel,
+                            IN PULONG TargetIndex,
+                            OUT PSAC_CHANNEL *TargetChannel)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+    ULONG i;
+    LONG ChannelIndex, StartIndex;
+    PSAC_CHANNEL FoundChannel;
+    BOOLEAN ChannelFound;
+    CHECK_PARAMETER1(CurrentChannel);
+    CHECK_PARAMETER2(TargetIndex);
+    CHECK_PARAMETER3(TargetChannel);
+
+    /* Get the current channel index */
+    Status = ChanMgrGetChannelIndex(CurrentChannel, &ChannelIndex);
+    if (!NT_SUCCESS(Status)) return Status;
+
+    /* Assume failure */
+    ChannelFound = FALSE;
+
+    /* Loop through all the possible active channels */
+    StartIndex = (ChannelIndex + 1) % SAC_MAX_CHANNELS;
+    for (i = StartIndex; i != StartIndex; i = (i + 1) % SAC_MAX_CHANNELS)
+    {
+        /* Get the channel and see if it exists*/
+        Status = ChanMgrGetByIndex(i, &FoundChannel);
+        if (Status != STATUS_NOT_FOUND)
+        {
+            /* Bail out if we failed for some reason */
+            if (!NT_SUCCESS(Status)) return Status;
+
+            /* It exists -- is it active? Or, does it have output data? */
+            if ((ChannelIsActive(FoundChannel)) ||
+                (!(ChannelIsActive(FoundChannel)) &&
+                  (FoundChannel->ChannelHasNewOBufferData)))
+            {
+                /* It's active or has output data, return with it */
+                ChannelFound = TRUE;
+                break;
+            }
+
+            /* Drop the reference on this channel and try the next one */
+            Status = ChanMgrReleaseChannel(FoundChannel);
+            if (!NT_SUCCESS(Status)) return Status;
+        }
+    }
+
+    /* Check if we successfully found a channel */
+    if ((NT_SUCCESS(Status)) && (ChannelFound))
+    {
+        /* Return it and its indexed. Remember we still hold the reference */
+        *TargetIndex = i;
+        *TargetChannel = FoundChannel;
+    }
+
+    /* All done */
+    return Status;
 }
 
 NTSTATUS
-ChanMgrIsFull(
-       OUT PBOOLEAN IsFull
-       )
+NTAPI
+ChanMgrChannelDestroy(IN PSAC_CHANNEL Channel)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    CHECK_PARAMETER1(Channel);
+    CHECK_PARAMETER(ChannelGetReferenceCount(Channel->Index) > 0);
+
+    /* Destroy the channel */
+    return Channel->ChannelDestroy(Channel);
 }
 
 NTSTATUS
-ChanMgrShutdown(
-       VOID
-       )
+NTAPI
+ChanMgrCloseChannel(IN PSAC_CHANNEL Channel)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+    CHECK_PARAMETER(Channel);
+
+    /* Check if the channel is active */
+    if (ChannelIsActive(Channel))
+    {
+        /* Yep, close it */
+        Status = ChannelClose(Channel);
+    }
+    else
+    {
+        /* Nothing to do */
+        Status = STATUS_ALREADY_DISCONNECTED;
+    }
+
+    /* Handle the channel close */
+    ConMgrHandleEvent(TRUE, Channel, &Status);
+    return Status;
 }
 
-BOOLEAN
-ChanMgrIsUniqueName(
-       IN PWCHAR ChannelName
-       )
+NTSTATUS
+NTAPI
+ChanMgrGetChannelCount(OUT PULONG ChannelCount)
 {
-       return FALSE;
+    ULONG i;
+    PSAC_CHANNEL Channel;
+    NTSTATUS Status;
+    CHECK_PARAMETER(ChannelCount);
+
+    /* Assume no channels */
+    *ChannelCount = 0;
+
+    /* Loop every channel */
+    for (i = 0; i < SAC_MAX_CHANNELS; i++)
+    {
+        /* See if this one exists */
+        Status = ChanMgrGetByIndex(i, &Channel);
+        if (Status != STATUS_NOT_FOUND)
+        {
+            /* Sanity checks*/
+            ASSERT(NT_SUCCESS(Status));
+            ASSERT(Channel != NULL);
+
+            /* It exists -- is it active? Or, does it have output data? */
+            if ((ChannelIsActive(Channel)) ||
+                (!(ChannelIsActive(Channel)) &&
+                 (Channel->ChannelHasNewOBufferData)))
+            {
+                /* It's active or has output data, increase the count */
+                ++*ChannelCount;
+                break;
+            }
+
+            /* Drop the reference on this channel and try the next one */
+            Status = ChanMgrReleaseChannel(Channel);
+            if (!NT_SUCCESS(Status)) return Status;
+        }
+        else
+        {
+            /* Channel doesn't exist, nothing wrong with that, keep going */
+            Status = STATUS_SUCCESS;
+        }
+    }
+
+    /* We should always succeed if we get here */
+    ASSERT(NT_SUCCESS(Status));
+    return Status;
 }
 
 NTSTATUS
-ChanMgrGenerateUniqueCmdName(
-       IN PWCHAR ChannelName
-       )
+NTAPI
+ChanMgrIsFull(OUT PBOOLEAN IsFull)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+    ULONG Count;
+
+    /* Count the channels */
+    Status = ChanMgrGetChannelCount(&Count);
+    CHECK_PARAMETER(Status == STATUS_SUCCESS);
+
+    /* Return if we hit the limit */
+    *IsFull = (Count == SAC_MAX_CHANNELS);
+    return Status;
 }
 
 NTSTATUS
-ChanMgrCreateChannel(
-       OUT PSAC_CHANNEL *Channel,
-       IN PSAC_CHANNEL_ATTRIBUTES Attributes
-       )
+NTAPI
+ChanMgrCloseChannelsWithFileObject(IN PFILE_OBJECT FileObject)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    PSAC_CHANNEL Channel;
+    ULONG i;
+    NTSTATUS Status;
+    CHECK_PARAMETER1(FileObject);
+
+    /* Loop all channels */
+    for (i = 0; i < SAC_MAX_CHANNELS; i++)
+    {
+        /* Try to get this one */
+        Status = ChanMgrGetByIndex(i, &Channel);
+        if (!NT_SUCCESS(Status)) break;
+
+        /* Check if the FO matches, if so, close the channel */
+        if (Channel->FileObject == FileObject) ChanMgrCloseChannel(Channel);
+
+        /* Drop the reference and try the next channel(s) */
+        Status = ChanMgrReleaseChannel(Channel);
+        if (!NT_SUCCESS(Status)) break;
+    }
+
+    /* All done */
+    return Status;
 }
 
 NTSTATUS
-ChanMgrCloseChannelsWithFileObject(
-       IN PFILE_OBJECT FileObject
-       )
+NTAPI
+ChanMgrGenerateUniqueCmdName(IN PWCHAR ChannelName)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    return STATUS_NOT_IMPLEMENTED;
 }