Synchronize with trunk revision 59781.
[reactos.git] / drivers / sac / driver / rawchan.c
index 6a38bd0..ad0669a 100644 (file)
 /*
- * PROJECT:             ReactOS Boot Loader
- * LICENSE:             BSD - See COPYING.ARM in the top level directory
- * FILE:                drivers/sac/driver/rawchan.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/rawchan.c
+ * PURPOSE:     Driver for the Server Administration Console (SAC) for EMS
+ * PROGRAMMERS: ReactOS Portable Systems Group
  */
 
-/* INCLUDES *******************************************************************/
+/* INCLUDES ******************************************************************/
 
 #include "sacdrv.h"
 
-/* GLOBALS ********************************************************************/
+/* GLOBALS *******************************************************************/
 
-/* FUNCTIONS ******************************************************************/
+/* FUNCTIONS *****************************************************************/
+
+#define SAC_RAW_OBUFFER_SIZE 0x2000
+#define SAC_RAW_IBUFFER_SIZE 0x2000
 
 NTSTATUS
-RawChannelCreate(
-       IN PSAC_CHANNEL Channel
-       )
+NTAPI
+RawChannelCreate(IN PSAC_CHANNEL Channel)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    CHECK_PARAMETER(Channel);
+
+    /* Allocate the output buffer */
+    Channel->OBuffer = SacAllocatePool(SAC_RAW_OBUFFER_SIZE, GLOBAL_BLOCK_TAG);
+    CHECK_ALLOCATION(Channel->OBuffer);
+
+    /* Allocate the input buffer */
+    Channel->IBuffer = SacAllocatePool(SAC_RAW_IBUFFER_SIZE, GLOBAL_BLOCK_TAG);
+    CHECK_ALLOCATION(Channel->IBuffer);
+
+    /* Reset all flags and return success */
+    Channel->OBufferIndex = 0;
+    Channel->OBufferFirstGoodIndex = 0;
+    Channel->ChannelHasNewIBufferData = FALSE;
+    Channel->ChannelHasNewOBufferData = FALSE;
+    return STATUS_SUCCESS;
 }
 
 NTSTATUS
-RawChannelDestroy(
-       IN PSAC_CHANNEL Channel
-       )
+NTAPI
+RawChannelDestroy(IN PSAC_CHANNEL Channel)
+{
+    CHECK_PARAMETER(Channel);
+
+    /* Free the buffer and then destroy the channel */
+    if (Channel->OBuffer) SacFreePool(Channel->OBuffer);
+    if (Channel->IBuffer) SacFreePool(Channel->IBuffer);
+    return ChannelDestroy(Channel);
+}
+
+FORCEINLINE
+BOOLEAN
+ChannelHasNewOBufferData(IN PSAC_CHANNEL Channel)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    return Channel->ChannelHasNewOBufferData;
 }
 
 NTSTATUS
-RawChannelORead(
-       IN PSAC_CHANNEL Channel,
-       IN PCHAR Buffer,
-       IN ULONG BufferSize,
-       OUT PULONG ByteCount
-       )
+NTAPI
+RawChannelORead(IN PSAC_CHANNEL Channel,
+                IN PCHAR Buffer,
+                IN ULONG BufferSize,
+                OUT PULONG ByteCount)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+    ULONG NextIndex;
+
+    CHECK_PARAMETER1(Channel);
+    CHECK_PARAMETER2(Buffer);
+    CHECK_PARAMETER3(BufferSize > 0);
+    CHECK_PARAMETER4(ByteCount);
+
+    *ByteCount = 0;
+
+    if (ChannelHasNewOBufferData(Channel))
+    {
+        Status = STATUS_SUCCESS;
+
+        while (TRUE)
+        {
+            Buffer[(*ByteCount)++] = Channel->OBuffer[Channel->OBufferFirstGoodIndex];
+
+            NextIndex = (Channel->OBufferFirstGoodIndex + 1) & (SAC_OBUFFER_SIZE - 1);
+            Channel->OBufferFirstGoodIndex = NextIndex;
+
+            if (NextIndex == Channel->OBufferIndex)
+            {
+                _InterlockedExchange(&Channel->ChannelHasNewOBufferData, 0);
+                break;
+            }
+
+            ASSERT(*ByteCount > 0);
+
+            if (*ByteCount >= BufferSize) break;
+        }
+    }
+    else
+    {
+        Status = STATUS_NO_DATA_DETECTED;
+    }
+
+    if (Channel->OBufferFirstGoodIndex == Channel->OBufferIndex)
+    {
+        ASSERT(ChannelHasNewOBufferData(Channel) == FALSE);
+    }
+
+    if (ChannelHasNewOBufferData(Channel) == FALSE)
+    {
+        ASSERT(Channel->OBufferFirstGoodIndex == Channel->OBufferIndex);
+    }
+
+    return Status;
 }
 
 NTSTATUS
-RawChannelOEcho(
-       IN PSAC_CHANNEL Channel,
-       IN PWCHAR String,
-       IN ULONG Length
-       )
+NTAPI
+RawChannelOEcho(IN PSAC_CHANNEL Channel,
+                IN PCHAR String,
+                IN ULONG Length)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    CHECK_PARAMETER1(Channel);
+    CHECK_PARAMETER2(String);
+
+    if (Length)
+    {
+        Status = ConMgrWriteData(Channel, String, Length);
+        if (NT_SUCCESS(Status)) ConMgrFlushData(Channel);
+    }
+
+    return Status;
 }
 
 NTSTATUS
-RawChannelOWrite2(
-       IN PSAC_CHANNEL Channel,
-       IN PCHAR String,
-       IN ULONG Size
-       )
+NTAPI
+RawChannelOWrite2(IN PSAC_CHANNEL Channel,
+                  IN PCHAR String,
+                  IN ULONG Size)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    BOOLEAN Overflow;
+    ULONG i, NextIndex;
+
+    CHECK_PARAMETER1(Channel);
+    CHECK_PARAMETER2(String);
+
+    Overflow = FALSE;
+
+    for (i = 0; i < Size; i++)
+    {
+        if ((Channel->OBufferIndex == Channel->OBufferFirstGoodIndex) &&
+            ((i) || (ChannelHasNewOBufferData(Channel))))
+        {
+            Overflow = TRUE;
+        }
+
+        ASSERT(Channel->OBufferIndex < SAC_RAW_OBUFFER_SIZE);
+
+        Channel->OBuffer[Channel->OBufferIndex] = String[i];
+
+        NextIndex = (Channel->OBufferIndex + 1) & (SAC_RAW_OBUFFER_SIZE - 1);
+        Channel->OBufferIndex = NextIndex;
+
+        if (Overflow) Channel->OBufferFirstGoodIndex = NextIndex;
+    }
+
+    _InterlockedExchange(&Channel->ChannelHasNewOBufferData, 1);
+
+    return STATUS_SUCCESS;
 }
 
 NTSTATUS
-RawChannelOFlush(
-       IN PSAC_CHANNEL Channel
-       )
+NTAPI
+RawChannelOFlush(IN PSAC_CHANNEL Channel)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+    ULONG ByteCount;
+    CHAR Dummy;
+    CHECK_PARAMETER1(Channel);
+
+    while (ChannelHasNewOBufferData(Channel))
+    {
+        Status = RawChannelORead(Channel, &Dummy, sizeof(Dummy), &ByteCount);
+        if (!NT_SUCCESS(Status)) return Status;
+
+        CHECK_PARAMETER_WITH_STATUS(ByteCount == 1, STATUS_UNSUCCESSFUL);
+
+        Status = ConMgrWriteData(Channel, &Dummy, sizeof(Dummy));
+        if (!NT_SUCCESS(Status)) return Status;
+    }
+
+    return ConMgrFlushData(Channel);
 }
 
-ULONG
-RawChannelGetIBufferIndex(
-       IN PSAC_CHANNEL Channel
-       )
+NTSTATUS
+NTAPI
+RawChannelOWrite(IN PSAC_CHANNEL Channel,
+                 IN PCHAR String,
+                 IN ULONG Length)
 {
-       return 0;
+    CHECK_PARAMETER1(Channel);
+    CHECK_PARAMETER2(String);
+
+    if ((ConMgrIsWriteEnabled(Channel)) && (Channel->WriteEnabled))
+    {
+        return RawChannelOEcho(Channel, String, Length);
+    }
+
+    return RawChannelOWrite2(Channel, String, Length);
 }
 
-VOID
-RawChannelSetIBufferIndex(
-       IN PSAC_CHANNEL Channel,
-       IN ULONG BufferIndex
-       )
+ULONG
+NTAPI
+RawChannelGetIBufferIndex(IN PSAC_CHANNEL Channel)
 {
+    ASSERT(Channel);
+    ASSERT(Channel->IBufferIndex < SAC_RAW_IBUFFER_SIZE);
 
+    /* Return the current buffer index */
+    return Channel->IBufferIndex;
 }
 
-NTSTATUS
-RawChannelOWrite(
-       IN PSAC_CHANNEL Channel,
-       IN PWCHAR String,
-       IN ULONG Length
-       )
+VOID
+NTAPI
+RawChannelSetIBufferIndex(IN PSAC_CHANNEL Channel,
+                          IN ULONG BufferIndex)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+    ASSERT(Channel);
+    ASSERT(Channel->IBufferIndex < SAC_RAW_IBUFFER_SIZE);
+
+    /* Set the new index, and if it's not zero, it means we have data */
+    Channel->IBufferIndex = BufferIndex;
+    _InterlockedExchange(&Channel->ChannelHasNewIBufferData, BufferIndex != 0);
+
+    /* If we have new data, and an event has been registered... */
+    if (!(Channel->IBufferIndex) &&
+        (Channel->Flags & SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT))
+    {
+        /* Go ahead and signal it */
+        ChannelClearEvent(Channel, HasNewDataEvent);
+        UNREFERENCED_PARAMETER(Status);
+    }
 }
 
 NTSTATUS
-RawChannelIRead(
-       IN PSAC_CHANNEL Channel,
-       IN PCHAR Buffer,
-       IN ULONG BufferSize,
-       IN PULONG ReturnBufferSize
-       )
+NTAPI
+RawChannelIRead(IN PSAC_CHANNEL Channel,
+                IN PCHAR Buffer,
+                IN ULONG BufferSize,
+                IN PULONG ReturnBufferSize)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    ULONG CopyChars;
+    CHECK_PARAMETER1(Channel);
+    CHECK_PARAMETER2(Buffer);
+    CHECK_PARAMETER_WITH_STATUS(BufferSize > 0, STATUS_INVALID_BUFFER_SIZE);
+
+    /* Assume failure */
+    *ReturnBufferSize = 0;
+
+    /* Check how many bytes are in the buffer */
+    if (Channel->ChannelInputBufferLength(Channel) == 0)
+    {
+        /* Apparently nothing. Make sure the flag indicates so too */
+        ASSERT(ChannelHasNewIBufferData(Channel) == FALSE);
+    }
+    else
+    {
+        /* Use the smallest number of bytes either in the buffer or requested */
+        CopyChars = min(Channel->ChannelInputBufferLength(Channel), BufferSize);
+        ASSERT(CopyChars <= Channel->ChannelInputBufferLength(Channel));
+
+        /* Copy them into the caller's buffer */
+        RtlCopyMemory(Buffer, Channel->IBuffer, CopyChars);
+
+        /* Update the channel's index past the copied (read) bytes */
+        RawChannelSetIBufferIndex(Channel,
+                                  RawChannelGetIBufferIndex(Channel) - CopyChars);
+
+        /* Are there still bytes that haven't been read yet? */
+        if (Channel->ChannelInputBufferLength(Channel))
+        {
+            /* Shift them up in the buffer */
+            RtlMoveMemory(Channel->IBuffer,
+                          &Channel->IBuffer[CopyChars],
+                          Channel->ChannelInputBufferLength(Channel));
+        }
+
+        /* Return the number of bytes we actually copied */
+        *ReturnBufferSize = CopyChars;
+    }
+
+    /* Return success */
+    return STATUS_SUCCESS;
 }
 
 NTSTATUS
-RawChannelIBufferIsFull(
-       IN PSAC_CHANNEL Channel,
-       PBOOLEAN BufferStatus
-       )
+NTAPI
+RawChannelIBufferIsFull(IN PSAC_CHANNEL Channel,
+                        OUT PBOOLEAN BufferStatus)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    CHECK_PARAMETER1(Channel);
+    CHECK_PARAMETER2(BufferStatus);
+
+    /* If the index is beyond the length, the buffer must be full */
+    *BufferStatus = RawChannelGetIBufferIndex(Channel) > SAC_RAW_IBUFFER_SIZE;
+    return STATUS_SUCCESS;
 }
 
 ULONG
-RawChannelIBufferLength(
-       IN PSAC_CHANNEL Channel
-       )
+NTAPI
+RawChannelIBufferLength(IN PSAC_CHANNEL Channel)
 {
-       return 0;
+    ASSERT(Channel);
+
+    /* The index is the current length (since we're 0-based) */
+    return RawChannelGetIBufferIndex(Channel);
 }
 
-CHAR
-RawChannelIReadLast(
-       IN PSAC_CHANNEL Channel
-       )
+WCHAR
+NTAPI
+RawChannelIReadLast(IN PSAC_CHANNEL Channel)
 {
-       return 0;
+    UCHAR LastChar = 0;
+    ASSERT(Channel);
+
+    /* Check if there's anything to read in the buffer */
+    if (Channel->ChannelInputBufferLength(Channel))
+    {
+        /* Go back one character */
+        RawChannelSetIBufferIndex(Channel,
+                                  RawChannelGetIBufferIndex(Channel) - 1);
+
+        /* Read it, and clear its current value */
+        LastChar = Channel->IBuffer[RawChannelGetIBufferIndex(Channel)];
+        Channel->IBuffer[RawChannelGetIBufferIndex(Channel)] = ANSI_NULL;
+    }
+
+    /* Return the last character */
+    return LastChar;
 }
 
 NTSTATUS
-RawChannelIWrite(
-       IN PSAC_CHANNEL Channel,
-       IN PCHAR Buffer,
-       IN ULONG BufferSize
-       )
+NTAPI
+RawChannelIWrite(IN PSAC_CHANNEL Channel,
+                 IN PCHAR Buffer,
+                 IN ULONG BufferSize)
 {
-       return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+    BOOLEAN IsFull;
+    ULONG Index;
+    CHECK_PARAMETER1(Channel);
+    CHECK_PARAMETER2(Buffer);
+    CHECK_PARAMETER_WITH_STATUS(BufferSize > 0, STATUS_INVALID_BUFFER_SIZE);
+
+    /* First, check if the input buffer still has space */
+    Status = RawChannelIBufferIsFull(Channel, &IsFull);
+    if (!NT_SUCCESS(Status)) return Status;
+    if (IsFull) return STATUS_UNSUCCESSFUL;
+
+    /* Get the current buffer index */
+    Index = RawChannelGetIBufferIndex(Channel);
+    if ((SAC_RAW_IBUFFER_SIZE - Index) < BufferSize)
+    {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* Copy the new data */
+    RtlCopyMemory(&Channel->IBuffer[Index], Buffer, BufferSize);
+
+    /* Update the index */
+    RawChannelSetIBufferIndex(Channel, BufferSize + Index);
+
+    /* Signal the event, if one was set */
+    if (Channel->Flags & SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT)
+    {
+        ChannelSetEvent(Channel, HasNewDataEvent);
+    }
+
+    /* All done */
+    return STATUS_SUCCESS;
 }