1634 lines
36 KiB
C
1634 lines
36 KiB
C
/*++
|
||
|
||
Copyright (c) 1999-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
channel.c
|
||
|
||
Abstract:
|
||
|
||
Routines for managing channels in the sac.
|
||
|
||
Author:
|
||
|
||
Brian Guarraci (briangu) March, 2001.
|
||
Sean Selitrennikoff (v-seans) Sept, 2000.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "sac.h"
|
||
|
||
BOOLEAN
|
||
ChannelIsValidType(
|
||
SAC_CHANNEL_TYPE ChannelType
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is a convenience routine to determine if a the given type
|
||
is a valid Channel type
|
||
|
||
Arguments:
|
||
|
||
ChannelType - the type to be investigated
|
||
|
||
Return Value:
|
||
|
||
TRUE - if type is valid
|
||
|
||
otherwise, FALSE
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN isValid;
|
||
|
||
switch(ChannelType) {
|
||
case ChannelTypeVTUTF8:
|
||
case ChannelTypeRaw:
|
||
case ChannelTypeCmd:
|
||
isValid = TRUE;
|
||
break;
|
||
default:
|
||
isValid = FALSE;
|
||
break;
|
||
}
|
||
|
||
return isValid;
|
||
|
||
}
|
||
|
||
BOOLEAN
|
||
ChannelIsActive(
|
||
IN PSAC_CHANNEL Channel
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determine if a channel is active.
|
||
|
||
Arguments:
|
||
|
||
Channel - Channel to see if has new data
|
||
|
||
Return Value:
|
||
|
||
TRUE - if the channel is active
|
||
|
||
otherwise, FALSE
|
||
|
||
--*/
|
||
{
|
||
SAC_CHANNEL_STATUS Status;
|
||
|
||
ChannelGetStatus(
|
||
Channel,
|
||
&Status
|
||
);
|
||
|
||
return (BOOLEAN)(Status == ChannelStatusActive);
|
||
}
|
||
|
||
BOOLEAN
|
||
ChannelIsEqual(
|
||
IN PSAC_CHANNEL Channel,
|
||
IN PSAC_CHANNEL_HANDLE Handle
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determine if a channel is the same as the one in question
|
||
|
||
Note: this is to encapsulate the GUID implementation of
|
||
channel handles
|
||
|
||
Arguments:
|
||
|
||
Channel - Channel to see if has new data
|
||
ChannelHandle - The channel handle in question
|
||
|
||
Return Value:
|
||
|
||
TRUE - if the channel is active
|
||
|
||
otherwise, FALSE
|
||
|
||
--*/
|
||
{
|
||
|
||
return (BOOLEAN)IsEqualGUID(
|
||
&(ChannelGetHandle(Channel).ChannelHandle),
|
||
&(Handle->ChannelHandle)
|
||
);
|
||
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
ChannelIsClosed(
|
||
IN PSAC_CHANNEL Channel
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determine if a channel is available for reuse. The criterion
|
||
for reuse are:
|
||
|
||
1. The channel must be inactive
|
||
2. If the preserve bit is FALSE, then HasNewData must == FALSE
|
||
|
||
Arguments:
|
||
|
||
Channel - Channel to see if has new data
|
||
|
||
Return Value:
|
||
|
||
TRUE - if the channel has unsent data
|
||
|
||
otherwise, FALSE
|
||
|
||
--*/
|
||
{
|
||
SAC_CHANNEL_STATUS Status;
|
||
|
||
ChannelGetStatus(
|
||
Channel,
|
||
&Status
|
||
);
|
||
|
||
return (BOOLEAN)(
|
||
(Status == ChannelStatusInactive) &&
|
||
(ChannelHasNewOBufferData(Channel) == FALSE)
|
||
);
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelInitializeVTable(
|
||
IN PSAC_CHANNEL Channel
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine assigns channel type specific functions.
|
||
|
||
Arguments:
|
||
|
||
Channel - The channel to assign the functions to.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if successful, else the appropriate error code.
|
||
|
||
--*/
|
||
{
|
||
|
||
//
|
||
// Fill out the channel's vtable according to channel type
|
||
//
|
||
|
||
switch(ChannelGetType(Channel)){
|
||
case ChannelTypeVTUTF8:
|
||
|
||
Channel->Create = VTUTF8ChannelCreate;
|
||
Channel->Destroy = VTUTF8ChannelDestroy;
|
||
Channel->OFlush = VTUTF8ChannelOFlush;
|
||
Channel->OEcho = VTUTF8ChannelOEcho;
|
||
Channel->OWrite = VTUTF8ChannelOWrite;
|
||
Channel->ORead = VTUTF8ChannelORead;
|
||
Channel->IWrite = VTUTF8ChannelIWrite;
|
||
Channel->IRead = VTUTF8ChannelIRead;
|
||
Channel->IReadLast = VTUTF8ChannelIReadLast;
|
||
Channel->IBufferIsFull = VTUTF8ChannelIBufferIsFull;
|
||
Channel->IBufferLength = VTUTF8ChannelIBufferLength;
|
||
|
||
break;
|
||
case ChannelTypeRaw:
|
||
|
||
Channel->Create = RawChannelCreate;
|
||
Channel->Destroy = RawChannelDestroy;
|
||
Channel->OFlush = RawChannelOFlush;
|
||
Channel->OEcho = RawChannelOEcho;
|
||
Channel->OWrite = RawChannelOWrite;
|
||
Channel->ORead = RawChannelORead;
|
||
Channel->IWrite = RawChannelIWrite;
|
||
Channel->IRead = RawChannelIRead;
|
||
Channel->IReadLast = RawChannelIReadLast;
|
||
Channel->IBufferIsFull = RawChannelIBufferIsFull;
|
||
Channel->IBufferLength = RawChannelIBufferLength;
|
||
|
||
break;
|
||
|
||
case ChannelTypeCmd:
|
||
|
||
Channel->Create = CmdChannelCreate;
|
||
Channel->Destroy = CmdChannelDestroy;
|
||
Channel->OFlush = CmdChannelOFlush;
|
||
Channel->OEcho = VTUTF8ChannelOEcho;
|
||
Channel->OWrite = CmdChannelOWrite;
|
||
Channel->ORead = CmdChannelORead;
|
||
Channel->IWrite = VTUTF8ChannelIWrite;
|
||
Channel->IRead = VTUTF8ChannelIRead;
|
||
Channel->IReadLast = VTUTF8ChannelIReadLast;
|
||
Channel->IBufferIsFull = VTUTF8ChannelIBufferIsFull;
|
||
Channel->IBufferLength = VTUTF8ChannelIBufferLength;
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
|
||
break;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelCreate(
|
||
OUT PSAC_CHANNEL Channel,
|
||
IN PSAC_CHANNEL_OPEN_ATTRIBUTES Attributes,
|
||
IN SAC_CHANNEL_HANDLE Handle
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine allocates a channel and returns a pointer to it.
|
||
|
||
Arguments:
|
||
|
||
Channel - The resulting channel.
|
||
Attributes - All the parameters for the new channel
|
||
Handle - The new channel's handle
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if successful, else the appropriate error code.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
BOOLEAN b;
|
||
PVOID EventObjectBody;
|
||
PVOID EventWaitObjectBody;
|
||
|
||
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
|
||
ASSERT_STATUS(Attributes, STATUS_INVALID_PARAMETER_2);
|
||
|
||
//
|
||
// Verify that if the user wants to use the CLOSE_EVENT, we received on to use
|
||
//
|
||
if (Attributes->Flags & SAC_CHANNEL_FLAG_CLOSE_EVENT) {
|
||
ASSERT_STATUS(Attributes->CloseEvent != NULL, STATUS_INVALID_PARAMETER);
|
||
} else {
|
||
ASSERT_STATUS(Attributes->CloseEvent == NULL, STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
//
|
||
// Verify that if the user wants to use the HAS_NEW_DATA_EVENT, we received one to use
|
||
//
|
||
if (Attributes->Flags & SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT) {
|
||
ASSERT_STATUS(Attributes->HasNewDataEvent != NULL, STATUS_INVALID_PARAMETER);
|
||
} else {
|
||
ASSERT_STATUS(Attributes->HasNewDataEvent == NULL, STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
#if ENABLE_CHANNEL_LOCKING
|
||
//
|
||
// Verify that if the user wants to use the LOCK_EVENT, we received one to use
|
||
//
|
||
if (Attributes->Flags & SAC_CHANNEL_FLAG_LOCK_EVENT) {
|
||
ASSERT_STATUS(Attributes->LockEvent != NULL, STATUS_INVALID_PARAMETER);
|
||
} else {
|
||
ASSERT_STATUS(Attributes->LockEvent == NULL, STATUS_INVALID_PARAMETER);
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Verify that if the user wants to use the LOCK_EVENT, we received one to use
|
||
//
|
||
if (Attributes->Flags & SAC_CHANNEL_FLAG_REDRAW_EVENT) {
|
||
ASSERT_STATUS(Attributes->RedrawEvent != NULL, STATUS_INVALID_PARAMETER);
|
||
} else {
|
||
ASSERT_STATUS(Attributes->RedrawEvent == NULL, STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
//
|
||
// Initialize the channel structure
|
||
//
|
||
do {
|
||
|
||
//
|
||
// Initialize the channel structure
|
||
//
|
||
RtlZeroMemory(Channel, sizeof(SAC_CHANNEL));
|
||
|
||
//
|
||
// Initialize the channel access locks
|
||
//
|
||
INIT_CHANNEL_LOCKS(Channel);
|
||
|
||
//
|
||
// copy the name and force NULL termination at the end of the buffer
|
||
//
|
||
ChannelSetName(
|
||
Channel,
|
||
Attributes->Name
|
||
);
|
||
|
||
//
|
||
// copy the description and force NULL termination at the end of the buffer
|
||
//
|
||
ChannelSetDescription(
|
||
Channel,
|
||
Attributes->Description
|
||
);
|
||
|
||
//
|
||
// Attempt to get the wait object from the event
|
||
//
|
||
if (Attributes->CloseEvent) {
|
||
|
||
b = VerifyEventWaitable(
|
||
Attributes->CloseEvent,
|
||
&EventObjectBody,
|
||
&EventWaitObjectBody
|
||
);
|
||
|
||
if(!b) {
|
||
Status = STATUS_INVALID_HANDLE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// We successfully got the wait object, so keep it.
|
||
//
|
||
Channel->CloseEvent = Attributes->CloseEvent;
|
||
Channel->CloseEventObjectBody = EventObjectBody;
|
||
Channel->CloseEventWaitObjectBody = EventWaitObjectBody;
|
||
|
||
}
|
||
|
||
//
|
||
// Attempt to get the wait object from the event
|
||
//
|
||
if (Attributes->HasNewDataEvent) {
|
||
|
||
b = VerifyEventWaitable(
|
||
Attributes->HasNewDataEvent,
|
||
&EventObjectBody,
|
||
&EventWaitObjectBody
|
||
);
|
||
|
||
if(!b) {
|
||
Status = STATUS_INVALID_HANDLE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// We successfully got the wait object, so keep it.
|
||
//
|
||
Channel->HasNewDataEvent = Attributes->HasNewDataEvent;
|
||
Channel->HasNewDataEventObjectBody = EventObjectBody;
|
||
Channel->HasNewDataEventWaitObjectBody = EventWaitObjectBody;
|
||
|
||
}
|
||
|
||
#if ENABLE_CHANNEL_LOCKING
|
||
//
|
||
// Attempt to get the wait object from the event
|
||
//
|
||
if (Attributes->LockEvent) {
|
||
|
||
b = VerifyEventWaitable(
|
||
Attributes->LockEvent,
|
||
&EventObjectBody,
|
||
&EventWaitObjectBody
|
||
);
|
||
|
||
if(!b) {
|
||
Status = STATUS_INVALID_HANDLE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// We successfully got the wait object, so keep it.
|
||
//
|
||
Channel->LockEvent = Attributes->LockEvent;
|
||
Channel->LockEventObjectBody = EventObjectBody;
|
||
Channel->LockEventWaitObjectBody = EventWaitObjectBody;
|
||
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Attempt to get the wait object from the event
|
||
//
|
||
if (Attributes->RedrawEvent) {
|
||
|
||
b = VerifyEventWaitable(
|
||
Attributes->RedrawEvent,
|
||
&EventObjectBody,
|
||
&EventWaitObjectBody
|
||
);
|
||
|
||
if(!b) {
|
||
Status = STATUS_INVALID_HANDLE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// We successfully got the wait object, so keep it.
|
||
//
|
||
Channel->RedrawEvent = Attributes->RedrawEvent;
|
||
Channel->RedrawEventObjectBody = EventObjectBody;
|
||
Channel->RedrawEventWaitObjectBody = EventWaitObjectBody;
|
||
|
||
}
|
||
|
||
//
|
||
// Copy the remaining attributes
|
||
//
|
||
// Note: use the channel handle sent to use by the channel manager
|
||
//
|
||
Channel->Handle = Handle;
|
||
Channel->Type = Attributes->Type;
|
||
Channel->Flags = Attributes->Flags;
|
||
|
||
//
|
||
// If we have the ApplicationType,
|
||
// then save it
|
||
//
|
||
if (Attributes->Flags & SAC_CHANNEL_FLAG_APPLICATION_TYPE) {
|
||
Channel->ApplicationType = Attributes->ApplicationType;
|
||
}
|
||
|
||
//
|
||
// Assign the appropriate channel functions base on type
|
||
//
|
||
Status = ChannelInitializeVTable(Channel);
|
||
|
||
if (! NT_SUCCESS(Status)) {
|
||
|
||
IF_SAC_DEBUG(
|
||
SAC_DEBUG_FAILS,
|
||
KdPrint(("SAC Create Channel :: Failed to initialize vtable\n"))
|
||
);
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
//
|
||
// Do Channel type specific initialization
|
||
//
|
||
Status = Channel->Create(Channel);
|
||
|
||
if (! NT_SUCCESS(Status)) {
|
||
|
||
IF_SAC_DEBUG(
|
||
SAC_DEBUG_FAILS,
|
||
KdPrint(("SAC Create Channel :: Failed channel specific initialization\n"))
|
||
);
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
//
|
||
// The channel is now Active
|
||
//
|
||
ChannelSetStatus(Channel, ChannelStatusActive);
|
||
|
||
} while (FALSE);
|
||
|
||
//
|
||
// If the creation failed, destroy the channel object
|
||
//
|
||
if (! NT_SUCCESS(Status)) {
|
||
Channel->Destroy(Channel);
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelDereferenceHandles(
|
||
PSAC_CHANNEL Channel
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine dereferences and NULLs the channel's event handles,
|
||
if appropriate.
|
||
|
||
Note: caller must hold channel mutex
|
||
|
||
Arguments:
|
||
|
||
Channel - the channel to close
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - if the mapping was successful
|
||
|
||
otherwise, error status
|
||
|
||
--*/
|
||
{
|
||
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER);
|
||
|
||
//
|
||
// Release the has new data event if we have it
|
||
//
|
||
if (Channel->HasNewDataEvent) {
|
||
|
||
//
|
||
// validate that the channel's attributes for this event
|
||
// are properly set
|
||
//
|
||
ASSERT(ChannelGetFlags(Channel) & SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT);
|
||
ASSERT(Channel->HasNewDataEventObjectBody);
|
||
ASSERT(Channel->HasNewDataEventWaitObjectBody);
|
||
|
||
if (Channel->HasNewDataEventObjectBody) {
|
||
|
||
ObDereferenceObject(Channel->HasNewDataEventObjectBody);
|
||
|
||
//
|
||
// NULL the event pointers
|
||
//
|
||
ChannelSetFlags(
|
||
Channel,
|
||
ChannelGetFlags(Channel) & ~SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT
|
||
);
|
||
Channel->HasNewDataEvent = NULL;
|
||
Channel->HasNewDataEventObjectBody = NULL;
|
||
Channel->HasNewDataEventWaitObjectBody = NULL;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// do we have the Close Channel Event?
|
||
//
|
||
if (Channel->CloseEvent) {
|
||
|
||
//
|
||
// validate that the channel's attributes for this event
|
||
// are properly set
|
||
//
|
||
ASSERT(ChannelGetFlags(Channel) & SAC_CHANNEL_FLAG_CLOSE_EVENT);
|
||
ASSERT(Channel->CloseEventObjectBody);
|
||
ASSERT(Channel->CloseEventWaitObjectBody);
|
||
|
||
if (Channel->CloseEventObjectBody) {
|
||
|
||
//
|
||
// release the events
|
||
//
|
||
ObDereferenceObject(Channel->CloseEventObjectBody);
|
||
|
||
//
|
||
// NULL the event pointers
|
||
//
|
||
ChannelSetFlags(
|
||
Channel,
|
||
ChannelGetFlags(Channel) & ~SAC_CHANNEL_FLAG_CLOSE_EVENT
|
||
);
|
||
Channel->CloseEvent = NULL;
|
||
Channel->CloseEventObjectBody = NULL;
|
||
Channel->CloseEventWaitObjectBody = NULL;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
#if ENABLE_CHANNEL_LOCKING
|
||
//
|
||
// do we have the Lock Channel Event?
|
||
//
|
||
if (Channel->LockEvent) {
|
||
|
||
//
|
||
// validate that the channel's attributes for this event
|
||
// are properly set
|
||
//
|
||
ASSERT(ChannelGetFlags(Channel) & SAC_CHANNEL_FLAG_LOCK_EVENT);
|
||
ASSERT(Channel->LockEventObjectBody);
|
||
ASSERT(Channel->LockEventWaitObjectBody);
|
||
|
||
if (Channel->LockEventObjectBody) {
|
||
|
||
//
|
||
// release the events
|
||
//
|
||
ObDereferenceObject(Channel->LockEventObjectBody);
|
||
|
||
//
|
||
// NULL the event pointers
|
||
//
|
||
ChannelSetFlags(
|
||
Channel,
|
||
ChannelGetFlags(Channel) & ~SAC_CHANNEL_FLAG_LOCK_EVENT
|
||
);
|
||
Channel->LockEvent = NULL;
|
||
Channel->LockEventObjectBody = NULL;
|
||
Channel->LockEventWaitObjectBody = NULL;
|
||
|
||
}
|
||
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// do we have the Redraw Channel Event?
|
||
//
|
||
if (Channel->RedrawEvent) {
|
||
|
||
//
|
||
// validate that the channel's attributes for this event
|
||
// are properly set
|
||
//
|
||
ASSERT(ChannelGetFlags(Channel) & SAC_CHANNEL_FLAG_REDRAW_EVENT);
|
||
ASSERT(Channel->RedrawEventObjectBody);
|
||
ASSERT(Channel->RedrawEventWaitObjectBody);
|
||
|
||
if (Channel->RedrawEventObjectBody) {
|
||
|
||
//
|
||
// release the events
|
||
//
|
||
ObDereferenceObject(Channel->RedrawEventObjectBody);
|
||
|
||
//
|
||
// NULL the event pointers
|
||
//
|
||
ChannelSetFlags(
|
||
Channel,
|
||
ChannelGetFlags(Channel) & ~SAC_CHANNEL_FLAG_REDRAW_EVENT
|
||
);
|
||
Channel->RedrawEvent = NULL;
|
||
Channel->RedrawEventObjectBody = NULL;
|
||
Channel->RedrawEventWaitObjectBody = NULL;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelClose(
|
||
PSAC_CHANNEL Channel
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine closes the given channel and
|
||
fires the CloseEvent if specified
|
||
|
||
Note: caller must hold channel mutex
|
||
|
||
Arguments:
|
||
|
||
Channel - the channel to close
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - if the mapping was successful
|
||
|
||
otherwise, error status
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER);
|
||
|
||
//
|
||
// Set the channel's state to inactive
|
||
//
|
||
ChannelSetStatus(Channel, ChannelStatusInactive);
|
||
|
||
//
|
||
// do we need to fire the Close Channel Event?
|
||
//
|
||
if (ChannelGetFlags(Channel) & SAC_CHANNEL_FLAG_CLOSE_EVENT) {
|
||
|
||
ASSERT(Channel->CloseEvent);
|
||
ASSERT(Channel->CloseEventObjectBody);
|
||
ASSERT(Channel->CloseEventWaitObjectBody);
|
||
|
||
if (Channel->CloseEventWaitObjectBody) {
|
||
|
||
//
|
||
// Yes, fire the event
|
||
//
|
||
KeSetEvent(
|
||
Channel->CloseEventWaitObjectBody,
|
||
EVENT_INCREMENT,
|
||
FALSE
|
||
);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Let go of any handles this channel may own
|
||
//
|
||
Status = ChannelDereferenceHandles(Channel);
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
ChannelDestroy(
|
||
IN PSAC_CHANNEL Channel
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does the final steps of channel destruction.
|
||
|
||
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);
|
||
|
||
//
|
||
// Let go of any handles this channel may own
|
||
//
|
||
Status = ChannelDereferenceHandles(Channel);
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelOWrite(
|
||
IN PSAC_CHANNEL Channel,
|
||
IN PCUCHAR Buffer,
|
||
IN ULONG BufferSize
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common entry point for all channel owrite methods.
|
||
The purpose of this entry point is to provide a uniform locking
|
||
scheme for the obuffer. Channel apps should not call the owrite method
|
||
directly, but should use this instead.
|
||
|
||
Arguments:
|
||
|
||
Channel - Previously created channel.
|
||
Buffer - The buffer to write
|
||
BufferSize - The size of the buffer to write
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
//
|
||
// Make sure the caller isn't sending an unacceptably
|
||
// large sized buffer. This prevents an app from blocking
|
||
// the console manager from being blocked while a channel
|
||
// dumps it's buffer.
|
||
//
|
||
ASSERT_STATUS(
|
||
BufferSize < CHANNEL_MAX_OWRITE_BUFFER_SIZE,
|
||
STATUS_INVALID_PARAMETER_3
|
||
);
|
||
|
||
LOCK_CHANNEL_OBUFFER(Channel);
|
||
|
||
Status = Channel->OWrite(
|
||
Channel,
|
||
Buffer,
|
||
BufferSize
|
||
);
|
||
|
||
UNLOCK_CHANNEL_OBUFFER(Channel);
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelOFlush(
|
||
IN PSAC_CHANNEL Channel
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common entry point for all channel OFlush methods.
|
||
The purpose of this entry point is to provide a uniform locking
|
||
scheme for the obuffer. Channel apps should not call the OFlush method
|
||
directly, but should use this instead.
|
||
|
||
Arguments:
|
||
|
||
Channel - Previously created channel.
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
LOCK_CHANNEL_OBUFFER(Channel);
|
||
|
||
Status = Channel->OFlush(Channel);
|
||
|
||
UNLOCK_CHANNEL_OBUFFER(Channel);
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelOEcho(
|
||
IN PSAC_CHANNEL Channel,
|
||
IN PCUCHAR Buffer,
|
||
IN ULONG BufferSize
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common entry point for all channel OEcho methods.
|
||
The purpose of this entry point is to provide a uniform locking
|
||
scheme for the obuffer. Channel apps should not call the OEcho method
|
||
directly, but should use this instead.
|
||
|
||
Arguments:
|
||
|
||
Channel - Previously created channel.
|
||
Buffer - The buffer to write
|
||
BufferSize - The size of the buffer to write
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
LOCK_CHANNEL_OBUFFER(Channel);
|
||
|
||
Status = Channel->OEcho(
|
||
Channel,
|
||
Buffer,
|
||
BufferSize
|
||
);
|
||
|
||
UNLOCK_CHANNEL_OBUFFER(Channel);
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelORead(
|
||
IN PSAC_CHANNEL Channel,
|
||
IN PUCHAR Buffer,
|
||
IN ULONG BufferSize,
|
||
OUT PULONG ByteCount
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common entry point for all channel ORead methods.
|
||
The purpose of this entry point is to provide a uniform locking
|
||
scheme for the obuffer. Channel apps should not call the ORead method
|
||
directly, but should use this instead.
|
||
|
||
Arguments:
|
||
|
||
Channel - Previously created channel.
|
||
Buffer - The buffer to write
|
||
BufferSize - The size of the buffer to write
|
||
ByteCount - The number bytes read
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
LOCK_CHANNEL_OBUFFER(Channel);
|
||
|
||
Status = Channel->ORead(
|
||
Channel,
|
||
Buffer,
|
||
BufferSize,
|
||
ByteCount
|
||
);
|
||
|
||
UNLOCK_CHANNEL_OBUFFER(Channel);
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelIWrite(
|
||
IN PSAC_CHANNEL Channel,
|
||
IN PCUCHAR Buffer,
|
||
IN ULONG BufferSize
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common entry point for all channel iwrite methods.
|
||
The purpose of this entry point is to provide a uniform locking
|
||
scheme for the ibuffer. Channel apps should not call the iwrite method
|
||
directly, but should use this instead.
|
||
|
||
Arguments:
|
||
|
||
Channel - Previously created channel.
|
||
Buffer - The buffer to write
|
||
BufferSize - The size of the buffer to write
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
LOCK_CHANNEL_IBUFFER(Channel);
|
||
|
||
Status = Channel->IWrite(
|
||
Channel,
|
||
Buffer,
|
||
BufferSize
|
||
);
|
||
|
||
UNLOCK_CHANNEL_IBUFFER(Channel);
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelIRead(
|
||
IN PSAC_CHANNEL Channel,
|
||
IN PUCHAR Buffer,
|
||
IN ULONG BufferSize,
|
||
OUT PULONG ByteCount
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common entry point for all channel iread methods.
|
||
The purpose of this entry point is to provide a uniform locking
|
||
scheme for the ibuffer. Channel apps should not call the iread method
|
||
directly, but should use this instead.
|
||
|
||
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
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
LOCK_CHANNEL_IBUFFER(Channel);
|
||
|
||
Status = Channel->IRead(
|
||
Channel,
|
||
Buffer,
|
||
BufferSize,
|
||
ByteCount
|
||
);
|
||
|
||
UNLOCK_CHANNEL_IBUFFER(Channel);
|
||
|
||
return Status;
|
||
}
|
||
|
||
WCHAR
|
||
ChannelIReadLast(
|
||
IN PSAC_CHANNEL Channel
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common entry point for all channel ireadlast methods.
|
||
The purpose of this entry point is to provide a uniform locking
|
||
scheme for the ibuffer. Channel apps should not call the ireadlast method
|
||
directly, but should use this instead.
|
||
|
||
Arguments:
|
||
|
||
Channel - Previously created channel.
|
||
|
||
Return Value:
|
||
|
||
The last character, otherwise UNICODE_NULL
|
||
|
||
--*/
|
||
{
|
||
WCHAR wch;
|
||
|
||
LOCK_CHANNEL_IBUFFER(Channel);
|
||
|
||
wch = Channel->IReadLast(Channel);
|
||
|
||
UNLOCK_CHANNEL_IBUFFER(Channel);
|
||
|
||
return wch;
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelIBufferIsFull(
|
||
IN PSAC_CHANNEL Channel,
|
||
OUT BOOLEAN* BufferStatus
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common entry point for all channel IBufferIsFull methods.
|
||
The purpose of this entry point is to provide a uniform locking
|
||
scheme for the ibuffer. Channel apps should not call the IBufferIsFull method
|
||
directly, but should use this instead.
|
||
|
||
Arguments:
|
||
|
||
Channel - Previously created channel.
|
||
BufferStatus - The query result
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
LOCK_CHANNEL_IBUFFER(Channel);
|
||
|
||
Status = Channel->IBufferIsFull(
|
||
Channel,
|
||
BufferStatus
|
||
);
|
||
|
||
UNLOCK_CHANNEL_IBUFFER(Channel);
|
||
|
||
return Status;
|
||
}
|
||
|
||
ULONG
|
||
ChannelIBufferLength(
|
||
IN PSAC_CHANNEL Channel
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common entry point for all channel IBufferLength methods.
|
||
The purpose of this entry point is to provide a uniform locking
|
||
scheme for the ibuffer. Channel apps should not call the IBufferLength method
|
||
directly, but should use this instead.
|
||
|
||
Arguments:
|
||
|
||
Channel - Previously created channel.
|
||
|
||
Return Value:
|
||
|
||
The last character, otherwise UNICODE_NULL
|
||
|
||
--*/
|
||
{
|
||
ULONG Length;
|
||
|
||
LOCK_CHANNEL_IBUFFER(Channel);
|
||
|
||
Length = Channel->IBufferLength(Channel);
|
||
|
||
UNLOCK_CHANNEL_IBUFFER(Channel);
|
||
|
||
return Length;
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelGetName(
|
||
IN PSAC_CHANNEL Channel,
|
||
OUT PWSTR* Name
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine retrieves the channel's description and stores it
|
||
in a newly allocated buffer
|
||
|
||
Note: the caller is responsible for releasing the memory holding
|
||
the description
|
||
|
||
Arguments:
|
||
|
||
Channel - Previously created channel.
|
||
Name - the retrieved name
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
|
||
ASSERT_STATUS(Name, STATUS_INVALID_PARAMETER_2);
|
||
|
||
*Name = ALLOCATE_POOL(SAC_MAX_CHANNEL_NAME_SIZE, GENERAL_POOL_TAG);
|
||
ASSERT_STATUS(*Name, STATUS_NO_MEMORY);
|
||
|
||
LOCK_CHANNEL_ATTRIBUTES(Channel);
|
||
|
||
SAFE_WCSCPY(
|
||
SAC_MAX_CHANNEL_NAME_SIZE,
|
||
*Name,
|
||
Channel->Name
|
||
);
|
||
|
||
UNLOCK_CHANNEL_ATTRIBUTES(Channel);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelSetName(
|
||
IN PSAC_CHANNEL Channel,
|
||
IN PCWSTR Name
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets the channel's name.
|
||
|
||
Arguments:
|
||
|
||
Channel - Previously created channel.
|
||
Name - The new channel name
|
||
|
||
Note: this routine does not check to see if the name is unique
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
|
||
ASSERT_STATUS(Name, STATUS_INVALID_PARAMETER_2);
|
||
|
||
LOCK_CHANNEL_ATTRIBUTES(Channel);
|
||
|
||
SAFE_WCSCPY(
|
||
SAC_MAX_CHANNEL_NAME_SIZE,
|
||
Channel->Name,
|
||
Name
|
||
);
|
||
|
||
Channel->Name[SAC_MAX_CHANNEL_NAME_LENGTH] = UNICODE_NULL;
|
||
|
||
UNLOCK_CHANNEL_ATTRIBUTES(Channel);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelGetDescription(
|
||
IN PSAC_CHANNEL Channel,
|
||
OUT PWSTR* Description
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine retrieves the channel's description and stores it
|
||
in a newly allocated buffer
|
||
|
||
Note: the caller is responsible for releasing the memory holding
|
||
the description
|
||
|
||
Arguments:
|
||
|
||
Channel - Previously created channel.
|
||
Description - the retrieved description
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
|
||
ASSERT_STATUS(Description, STATUS_INVALID_PARAMETER_2);
|
||
|
||
*Description = ALLOCATE_POOL(SAC_MAX_CHANNEL_DESCRIPTION_SIZE, GENERAL_POOL_TAG);
|
||
ASSERT_STATUS(*Description, STATUS_NO_MEMORY);
|
||
|
||
LOCK_CHANNEL_ATTRIBUTES(Channel);
|
||
|
||
SAFE_WCSCPY(
|
||
SAC_MAX_CHANNEL_DESCRIPTION_SIZE,
|
||
*Description,
|
||
Channel->Description
|
||
);
|
||
|
||
UNLOCK_CHANNEL_ATTRIBUTES(Channel);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelSetDescription(
|
||
IN PSAC_CHANNEL Channel,
|
||
IN PCWSTR Description
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets the channels description.
|
||
|
||
Arguments:
|
||
|
||
Channel - Previously created channel.
|
||
Description - The new description
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
|
||
|
||
LOCK_CHANNEL_ATTRIBUTES(Channel);
|
||
|
||
if (Description != NULL) {
|
||
|
||
SAFE_WCSCPY(
|
||
SAC_MAX_CHANNEL_DESCRIPTION_SIZE,
|
||
Channel->Description,
|
||
Description
|
||
);
|
||
|
||
//
|
||
// Force a null termination at the end of the description
|
||
//
|
||
Channel->Description[SAC_MAX_CHANNEL_DESCRIPTION_LENGTH] = UNICODE_NULL;
|
||
|
||
} else {
|
||
|
||
//
|
||
// make the description 0 length
|
||
//
|
||
Channel->Description[0] = UNICODE_NULL;
|
||
|
||
}
|
||
|
||
UNLOCK_CHANNEL_ATTRIBUTES(Channel);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelSetStatus(
|
||
IN PSAC_CHANNEL Channel,
|
||
IN SAC_CHANNEL_STATUS Status
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets the channels status.
|
||
|
||
Arguments:
|
||
|
||
Channel - Previously created channel.
|
||
Status - The channel's new status
|
||
|
||
Return Value:
|
||
|
||
NTStatus
|
||
|
||
--*/
|
||
{
|
||
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
|
||
|
||
LOCK_CHANNEL_ATTRIBUTES(Channel);
|
||
|
||
Channel->Status = Status;
|
||
|
||
UNLOCK_CHANNEL_ATTRIBUTES(Channel);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelGetStatus(
|
||
IN PSAC_CHANNEL Channel,
|
||
OUT SAC_CHANNEL_STATUS* Status
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine Gets the channels status.
|
||
|
||
Arguments:
|
||
|
||
Channel - Previously created channel.
|
||
Status - The channel's new status
|
||
|
||
Return Value:
|
||
|
||
NTStatus
|
||
|
||
--*/
|
||
{
|
||
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
|
||
|
||
LOCK_CHANNEL_ATTRIBUTES(Channel);
|
||
|
||
*Status = Channel->Status;
|
||
|
||
UNLOCK_CHANNEL_ATTRIBUTES(Channel);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelSetApplicationType(
|
||
IN PSAC_CHANNEL Channel,
|
||
IN GUID ApplicationType
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets the channel's application type.
|
||
|
||
Arguments:
|
||
|
||
Channel - Previously created channel.
|
||
ApplicationType - Application type
|
||
|
||
Return Value:
|
||
|
||
NTStatus
|
||
|
||
--*/
|
||
{
|
||
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
|
||
|
||
LOCK_CHANNEL_ATTRIBUTES(Channel);
|
||
|
||
Channel->ApplicationType = ApplicationType;
|
||
|
||
UNLOCK_CHANNEL_ATTRIBUTES(Channel);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelGetApplicationType(
|
||
IN PSAC_CHANNEL Channel,
|
||
OUT GUID* ApplicationType
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine gets the channel's application type.
|
||
|
||
Arguments:
|
||
|
||
Channel - Previously created channel.
|
||
ApplicationType - Application type
|
||
|
||
Return Value:
|
||
|
||
NTStatus
|
||
|
||
--*/
|
||
{
|
||
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
|
||
|
||
LOCK_CHANNEL_ATTRIBUTES(Channel);
|
||
|
||
*ApplicationType = Channel->ApplicationType;
|
||
|
||
UNLOCK_CHANNEL_ATTRIBUTES(Channel);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
#if ENABLE_CHANNEL_LOCKING
|
||
|
||
NTSTATUS
|
||
ChannelSetLockEvent(
|
||
IN PSAC_CHANNEL Channel
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Set the channel lock event
|
||
|
||
Arguments:
|
||
|
||
Channel - The channel to fire the event for
|
||
|
||
Return Value:
|
||
|
||
NTStatus
|
||
|
||
--*/
|
||
{
|
||
|
||
ASSERT_STATUS(Channel->LockEvent, STATUS_UNSUCCESSFUL);
|
||
ASSERT_STATUS(Channel->LockEventObjectBody, STATUS_UNSUCCESSFUL);
|
||
ASSERT_STATUS(Channel->LockEventWaitObjectBody, STATUS_UNSUCCESSFUL);
|
||
|
||
if (Channel->LockEventWaitObjectBody) {
|
||
|
||
//
|
||
// fire the event
|
||
//
|
||
KeSetEvent(
|
||
Channel->LockEventWaitObjectBody,
|
||
EVENT_INCREMENT,
|
||
FALSE
|
||
);
|
||
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
#endif
|
||
|
||
NTSTATUS
|
||
ChannelSetRedrawEvent(
|
||
IN PSAC_CHANNEL Channel
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Set the channel redraw event
|
||
|
||
Arguments:
|
||
|
||
Channel - The channel to fire the event for
|
||
|
||
Return Value:
|
||
|
||
NTStatus
|
||
|
||
--*/
|
||
{
|
||
|
||
ASSERT_STATUS(Channel->RedrawEvent, STATUS_UNSUCCESSFUL);
|
||
ASSERT_STATUS(Channel->RedrawEventObjectBody, STATUS_UNSUCCESSFUL);
|
||
ASSERT_STATUS(Channel->RedrawEventWaitObjectBody, STATUS_UNSUCCESSFUL);
|
||
|
||
if (Channel->RedrawEventWaitObjectBody) {
|
||
|
||
//
|
||
// fire the event
|
||
//
|
||
KeSetEvent(
|
||
Channel->RedrawEventWaitObjectBody,
|
||
EVENT_INCREMENT,
|
||
FALSE
|
||
);
|
||
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelClearRedrawEvent(
|
||
IN PSAC_CHANNEL Channel
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Clear the channel redraw event
|
||
|
||
Arguments:
|
||
|
||
Channel - The channel to fire the event for
|
||
|
||
Return Value:
|
||
|
||
NTStatus
|
||
|
||
--*/
|
||
{
|
||
|
||
ASSERT_STATUS(Channel->RedrawEvent, STATUS_UNSUCCESSFUL);
|
||
ASSERT_STATUS(Channel->RedrawEventObjectBody, STATUS_UNSUCCESSFUL);
|
||
ASSERT_STATUS(Channel->RedrawEventWaitObjectBody, STATUS_UNSUCCESSFUL);
|
||
|
||
if (Channel->RedrawEventWaitObjectBody) {
|
||
|
||
//
|
||
// clear the event
|
||
//
|
||
KeClearEvent( Channel->RedrawEventWaitObjectBody );
|
||
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
ChannelHasRedrawEvent(
|
||
IN PSAC_CHANNEL Channel,
|
||
OUT PBOOLEAN Present
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines if the channel has the Redraw event present.
|
||
|
||
Arguments:
|
||
|
||
Channel - the channel to query
|
||
|
||
Return Value:
|
||
|
||
TRUE - the channel has the event
|
||
FALSE - Otherwise
|
||
|
||
--*/
|
||
{
|
||
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
|
||
ASSERT_STATUS(Present, STATUS_INVALID_PARAMETER_2);
|
||
|
||
*Present = (ChannelGetFlags(Channel) & SAC_CHANNEL_FLAG_REDRAW_EVENT) ? TRUE : FALSE;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|