/*++ Copyright (c) 1999-2000 Microsoft Corporation Module Name: rawchan.c Abstract: Routines for managing channels in the sac. Author: Sean Selitrennikoff (v-seans) Sept, 2000. Brian Guarraci (briangu) March, 2001. Revision History: --*/ #include "sac.h" VOID RawChannelSetIBufferIndex( IN PSAC_CHANNEL Channel, IN ULONG IBufferIndex ); ULONG RawChannelGetIBufferIndex( IN PSAC_CHANNEL Channel ); NTSTATUS RawChannelCreate( IN OUT PSAC_CHANNEL Channel ) /*++ Routine Description: This routine allocates a channel and returns a pointer to it. Arguments: Channel - The resulting channel. OpenChannelCmd - All the parameters for the new channel Return Value: STATUS_SUCCESS if successful, else the appropriate error code. --*/ { ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER); Channel->OBuffer = (PUCHAR)ALLOCATE_POOL(SAC_RAW_OBUFFER_SIZE, GENERAL_POOL_TAG); ASSERT_STATUS(Channel->OBuffer, STATUS_NO_MEMORY); Channel->IBuffer = (PUCHAR)ALLOCATE_POOL(SAC_RAW_IBUFFER_SIZE, GENERAL_POOL_TAG); ASSERT_STATUS(Channel->IBuffer, STATUS_NO_MEMORY); Channel->OBufferIndex = 0; Channel->OBufferFirstGoodIndex = 0; ChannelSetIBufferHasNewData(Channel, FALSE); ChannelSetOBufferHasNewData(Channel, FALSE); return STATUS_SUCCESS; } NTSTATUS RawChannelDestroy( IN OUT PSAC_CHANNEL Channel ) /*++ Routine Description: This routine closes a channel. Arguments: Channel - The channel to be closed Return Value: STATUS_SUCCESS if successful, else the appropriate error code. --*/ { NTSTATUS Status; ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER); // // Free the dynamically allocated memory // if (Channel->OBuffer) { FREE_POOL(&(Channel->OBuffer)); Channel->OBuffer = NULL; } if (Channel->IBuffer) { FREE_POOL(&(Channel->IBuffer)); Channel->IBuffer = NULL; } // // Now that we've done our channel specific destroy, // Call the general channel destroy // Status = ChannelDestroy(Channel); return Status; } NTSTATUS RawChannelORead( IN PSAC_CHANNEL Channel, IN PUCHAR Buffer, IN ULONG BufferSize, OUT PULONG ByteCount ) /*++ Routine Description: This routine attempts to read BufferSize characters from the output buffer. Arguments: Channel - Previously created channel. Buffer - Outgoing buffer BufferSize - Outgoing buffer size ByteCount - The number of bytes actually read Note: if the buffered data stored in the channel has now been sent. If Channel is also in the Inactive state, the channel will now be qualified for removal. Return Value: Status --*/ { NTSTATUS Status; PUCHAR RawBuffer; ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1); ASSERT_STATUS(Buffer, STATUS_INVALID_PARAMETER_2); ASSERT_STATUS(BufferSize > 0, STATUS_INVALID_PARAMETER_3); ASSERT_STATUS(ByteCount, STATUS_INVALID_PARAMETER_4); do { // // We read 0 characters // *ByteCount = 0; // // if there is no data to read, // then report this to the caller // else read as much data as we can // if (! ChannelHasNewOBufferData(Channel)) { // // We are out of data // Status = STATUS_NO_DATA_DETECTED; break; } // // Get the raw channel obuffer // RawBuffer = (PUCHAR)Channel->OBuffer; // // default: we succeded to copy data // Status = STATUS_SUCCESS; // // Attempt to read the buffer // do { // // Do a byte-wise copy of the OBuffer to the destination buffer // // Note: doing a byte-wise copy rather than an RtlCopyMemory is // ok here since in general, this routine is called with // a small buffer size. Naturally, if the use of Raw Channels // changes and becomes dependent on a faster ORead, this // will have to change. // // // copy the char // Buffer[*ByteCount] = RawBuffer[Channel->OBufferFirstGoodIndex]; // // increment the byte count to what we actually read // *ByteCount += 1; // // advance the pointer to the next good index // Channel->OBufferFirstGoodIndex = (Channel->OBufferFirstGoodIndex + 1) % SAC_RAW_OBUFFER_SIZE; // // Make sure we don't pass the end of the buffer // if (Channel->OBufferFirstGoodIndex == Channel->OBufferIndex) { // // we have no new data // ChannelSetOBufferHasNewData(Channel, FALSE); break; } // // confirm the obvious // ASSERT(*ByteCount > 0); } while(*ByteCount < BufferSize); } while ( FALSE ); #if DBG // // More sanity checking // if (Channel->OBufferFirstGoodIndex == Channel->OBufferIndex) { ASSERT(ChannelHasNewOBufferData(Channel) == FALSE); } if (ChannelHasNewOBufferData(Channel) == FALSE) { ASSERT(Channel->OBufferFirstGoodIndex == Channel->OBufferIndex); } #endif return Status; } NTSTATUS RawChannelOEcho( IN PSAC_CHANNEL Channel, IN PCUCHAR String, IN ULONG Size ) /*++ Routine Description: This routine puts the string out the headless port. Arguments: Channel - Previously created channel. String - Output string. Length - The # of String bytes to process Return Value: STATUS_SUCCESS if successful, otherwise status --*/ { NTSTATUS Status; ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1); ASSERT_STATUS(String, STATUS_INVALID_PARAMETER_2); ASSERT(FIELD_OFFSET(HEADLESS_CMD_PUT_STRING, String) == 0); // ASSERT if anyone changes this structure. // // Default: we succeeded // Status = STATUS_SUCCESS; // // Only echo if the buffer has something to send // if (Size > 0) { // // Send the bytes // Status = IoMgrWriteData( Channel, String, Size ); // // If we were successful, flush the channel's data in the iomgr // if (NT_SUCCESS(Status)) { Status = IoMgrFlushData(Channel); } } return Status; } NTSTATUS RawChannelOWrite( IN PSAC_CHANNEL Channel, IN PCUCHAR String, IN ULONG Size ) /*++ Routine Description: This routine takes a string and prints it to the specified channel. If the channel is the currently active channel, it puts the string out the headless port as well. Note: Current Channel lock must be held by caller Arguments: Channel - Previously created channel. String - Output string. Length - The # of String bytes to process Return Value: STATUS_SUCCESS if successful, otherwise status --*/ { NTSTATUS Status; ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1); ASSERT_STATUS(String, STATUS_INVALID_PARAMETER_2); ASSERT(FIELD_OFFSET(HEADLESS_CMD_PUT_STRING, String) == 0); // ASSERT if anyone changes this structure. do { // // if the current channel is the active channel and the user has selected // to display this channel, relay the output directly to the user // if (IoMgrIsWriteEnabled(Channel) && ChannelSentToScreen(Channel)){ Status = RawChannelOEcho( Channel, String, Size ); if (! NT_SUCCESS(Status)) { break; } } else { // // Write the data to the channel's obuffer // Status = RawChannelOWrite2( Channel, String, Size ); if (! NT_SUCCESS(Status)) { break; } } } while ( FALSE ); return Status; } NTSTATUS RawChannelOWrite2( IN PSAC_CHANNEL Channel, IN PCUCHAR String, IN ULONG Size ) /*++ Routine Description: This routine takes a string and prints it into directly into the screen buffer with NO translation. Arguments: Channel - Previously created channel. String - String to print. Size - the # of bytes to write. Note: If String is a character string, the Size = strlen(String), otherwise, Size = the # of bytes to process. Return Value: STATUS_SUCCESS if successful, otherwise status --*/ { ULONG i; BOOLEAN TrackIndex; PUCHAR RawBuffer; ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1); ASSERT_STATUS(String, STATUS_INVALID_PARAMETER_2); // // If size == 0, then we are done // if (Size == 0) { return STATUS_SUCCESS; } // // Get the raw channel obuffer // RawBuffer = (PUCHAR)Channel->OBuffer; // // We are not in direct IO mode, so we need to buffer the string // TrackIndex = FALSE; for (i = 0; i < Size; i++) { // // Did we span over Good Data? If so, then we need to // move the First Good pointer. The new First Good pointer position // is immediately after the newest data entry in the buffer. // // Note: Since both indices start at the same position, // we need to skip the case when RawBufferIndex == RawBufferFirstGoodIndex // and there is no data in the buffer ((i == 0) && RawBufferHasNewData == FALSE), // otherwise the RawBufferFirstGoodIndex will always track the RawBufferIndex. // We need to let RawBufferIndex go around the ring buffer once before we enable // tracking. // if ((Channel->OBufferIndex == Channel->OBufferFirstGoodIndex) && ((i > 0) || (ChannelHasNewOBufferData(Channel) == TRUE)) ) { TrackIndex = TRUE; } ASSERT(Channel->OBufferIndex < SAC_RAW_OBUFFER_SIZE); RawBuffer[Channel->OBufferIndex] = String[i]; Channel->OBufferIndex = (Channel->OBufferIndex + 1) % SAC_RAW_OBUFFER_SIZE; if (TrackIndex) { Channel->OBufferFirstGoodIndex = Channel->OBufferIndex; } } ChannelSetOBufferHasNewData(Channel, TRUE); return STATUS_SUCCESS; } NTSTATUS RawChannelOFlush( IN PSAC_CHANNEL Channel ) /*++ Routine Description: Send all the data in the raw buffer since the channel was last active (or since the channel was created) Arguments: Channel - Previously created channel. Return Value: STATUS_SUCCESS if successful, otherwise status --*/ { NTSTATUS Status; PUCHAR RawBuffer; UCHAR ch; ULONG ByteCount; ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1); // // Get the raw channel obuffer // RawBuffer = (PUCHAR)Channel->OBuffer; // // default: we succeeded // Status = STATUS_SUCCESS; // // Send the Obuffer out to the headless port // while ( ChannelHasNewOBufferData(Channel) == TRUE ) { // // get a byte from the OBuffer // Status = RawChannelORead( Channel, &ch, sizeof(ch), &ByteCount ); if (! NT_SUCCESS(Status)) { break; } ASSERT(ByteCount == 1); if (ByteCount != 1) { Status = STATUS_UNSUCCESSFUL; break; } // // Send the byte // Status = IoMgrWriteData( Channel, &ch, sizeof(ch) ); if (! NT_SUCCESS(Status)) { break; } } // // If we were successful, flush the channel's data in the iomgr // if (NT_SUCCESS(Status)) { Status = IoMgrFlushData(Channel); } return Status; } NTSTATUS RawChannelIWrite( IN PSAC_CHANNEL Channel, IN PCUCHAR Buffer, IN ULONG BufferSize ) /*++ Routine Description: This routine takes a single character and adds it to the buffered input for this channel. Arguments: Channel - Previously created channel. Buffer - Incoming buffer of UCHARs BufferSize - Incoming buffer size Return Value: STATUS_SUCCESS if successful, otherwise status --*/ { NTSTATUS Status; BOOLEAN IBufferStatus; ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1); ASSERT_STATUS(Buffer, STATUS_INVALID_PARAMETER_2); ASSERT_STATUS(BufferSize > 0, STATUS_INVALID_BUFFER_SIZE); // // Make sure we aren't full // Status = RawChannelIBufferIsFull( Channel, &IBufferStatus ); if (! NT_SUCCESS(Status)) { return Status; } // // If there is no more room, then fail // if (IBufferStatus == TRUE) { return STATUS_UNSUCCESSFUL; } // // make sure there is enough room for the buffer // // Note: this prevents us from writing a portion of the buffer // and then failing, leaving the caller in the state where // it doesn't know how much of the buffer was written. // if ((SAC_RAW_IBUFFER_SIZE - RawChannelGetIBufferIndex(Channel)) < BufferSize) { return STATUS_INSUFFICIENT_RESOURCES; } // // default: we succeeded // Status = STATUS_SUCCESS; // // Copy the new data to the ibuffer // RtlCopyMemory( &Channel->IBuffer[RawChannelGetIBufferIndex(Channel)], Buffer, BufferSize ); // // Account for the newly appended data // RawChannelSetIBufferIndex( Channel, RawChannelGetIBufferIndex(Channel) + BufferSize ); // // Fire the HasNewData event if specified // if (Channel->Flags & SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT) { ASSERT(Channel->HasNewDataEvent); ASSERT(Channel->HasNewDataEventObjectBody); ASSERT(Channel->HasNewDataEventWaitObjectBody); KeSetEvent( Channel->HasNewDataEventWaitObjectBody, EVENT_INCREMENT, FALSE ); } return Status; } NTSTATUS RawChannelIRead( IN PSAC_CHANNEL Channel, IN PUCHAR Buffer, IN ULONG BufferSize, OUT PULONG ByteCount ) /*++ Routine Description: This routine takes the first character in the input buffer, removes and returns it. If there is none, it returns 0x0. Arguments: Channel - Previously created channel. Buffer - The buffer to read into BufferSize - The size of the buffer ByteCount - The # of bytes read Return Value: Status --*/ { ULONG CopyChars; ULONG CopySize; ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1); ASSERT_STATUS(Buffer, STATUS_INVALID_PARAMETER_2); ASSERT_STATUS(BufferSize > 0, STATUS_INVALID_BUFFER_SIZE); // // initialize // CopyChars = 0; CopySize = 0; // // Default: no bytes were read // *ByteCount = 0; // // If there is nothing to send, // then return that we read 0 bytes // if (Channel->IBufferLength(Channel) == 0) { ASSERT(ChannelHasNewIBufferData(Channel) == FALSE); return STATUS_SUCCESS; } // // Caclulate the largest buffer size we can use (and need), and then calculate // the number of characters this refers to. // CopySize = Channel->IBufferLength(Channel) * sizeof(UCHAR); CopySize = CopySize > BufferSize ? BufferSize : CopySize; CopyChars = CopySize / sizeof(UCHAR); // // We need to recalc the CopySize in case there was a rounding down when // computing CopyChars // CopySize = CopyChars * sizeof(UCHAR); ASSERT(CopyChars <= Channel->IBufferLength(Channel)); // // Do a block copy of the ibuffer to the destination buffer // RtlCopyMemory( Buffer, Channel->IBuffer, CopySize ); // // subtract the # of characters copied from the character counter // RawChannelSetIBufferIndex( Channel, RawChannelGetIBufferIndex(Channel) - CopyChars ); // // If there is remaining data left in the Channel input buffer, // shift it to the beginning // if (Channel->IBufferLength(Channel) > 0) { RtlMoveMemory(&(Channel->IBuffer[0]), &(Channel->IBuffer[CopyChars]), Channel->IBufferLength(Channel) * sizeof(Channel->IBuffer[0]) ); } // // Send back the # of bytes read // *ByteCount = CopySize; return STATUS_SUCCESS; } NTSTATUS RawChannelIBufferIsFull( IN PSAC_CHANNEL Channel, OUT BOOLEAN* BufferStatus ) /*++ Routine Description: Determine if the IBuffer is full Arguments: Channel - Previously created channel. BufferStatus - on exit, TRUE if the buffer is full, otherwise FALSE Return Value: Status --*/ { ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1); ASSERT_STATUS(BufferStatus, STATUS_INVALID_PARAMETER_2); *BufferStatus = (BOOLEAN)(RawChannelGetIBufferIndex(Channel) >= (SAC_RAW_IBUFFER_SIZE-1)); return STATUS_SUCCESS; } ULONG RawChannelIBufferLength( IN PSAC_CHANNEL Channel ) /*++ Routine Description: This routine determines the length of the input buffer, treating the input buffer contents as a string Arguments: Channel - Previously created channel. Return Value: The length of the current input buffer --*/ { ASSERT(Channel); return (RawChannelGetIBufferIndex(Channel) / sizeof(UCHAR)); } WCHAR RawChannelIReadLast( IN PSAC_CHANNEL Channel ) /*++ Routine Description: This routine takes the last character in the input buffer, removes and returns it. If there is none, it returns 0x0. Arguments: Channel - Previously created channel. Return Value: Last character in the input buffer. --*/ { WCHAR Char; ASSERT(Channel); // // default: no character was read // Char = UNICODE_NULL; if (Channel->IBufferLength(Channel) > 0) { RawChannelSetIBufferIndex( Channel, RawChannelGetIBufferIndex(Channel) - sizeof(UCHAR) ); Char = Channel->IBuffer[RawChannelGetIBufferIndex(Channel)]; Channel->IBuffer[RawChannelGetIBufferIndex(Channel)] = UNICODE_NULL; } return Char; } ULONG RawChannelGetIBufferIndex( IN PSAC_CHANNEL Channel ) /*++ Routine Description: Get teh ibuffer index Arguments: Channel - the channel to get the ibuffer index from Environment: The ibuffer index --*/ { ASSERT(Channel); // // Make sure the ibuffer index is atleast aligned to a WCHAR // ASSERT((Channel->IBufferIndex % sizeof(UCHAR)) == 0); // // Make sure the ibuffer index is in bounds // ASSERT(Channel->IBufferIndex < SAC_RAW_IBUFFER_SIZE); return Channel->IBufferIndex; } VOID RawChannelSetIBufferIndex( IN PSAC_CHANNEL Channel, IN ULONG IBufferIndex ) /*++ Routine Description: Set the ibuffer index Arguments: Channel - the channel to get the ibuffer index from IBufferIndex - the new inbuffer index Environment: None --*/ { ASSERT(Channel); // // Make sure the ibuffer index is atleast aligned to a WCHAR // ASSERT((Channel->IBufferIndex % sizeof(UCHAR)) == 0); // // Make sure the ibuffer index is in bounds // ASSERT(Channel->IBufferIndex < SAC_RAW_IBUFFER_SIZE); // // Set the index // Channel->IBufferIndex = IBufferIndex; // // Set the has new data flag accordingly // ChannelSetIBufferHasNewData( Channel, Channel->IBufferIndex == 0 ? FALSE : TRUE ); // // Additional checking if the index == 0 // if (Channel->IBufferIndex == 0) { // // Clear the Has New Data event if specified // if (Channel->Flags & SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT) { ASSERT(Channel->HasNewDataEvent); ASSERT(Channel->HasNewDataEventObjectBody); ASSERT(Channel->HasNewDataEventWaitObjectBody); KeClearEvent(Channel->HasNewDataEventWaitObjectBody); } } }