Windows2003-3790/drivers/sac/driver/rawchan.c
2020-09-30 16:53:55 +02:00

1009 lines
22 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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);
}
}
}