windows-nt/Source/XPSP1/NT/termsrv/drivers/termdd/channel.c

2380 lines
71 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/****************************************************************************/
// channel.c
//
// Terminal Server channel handling.
//
// Copyright (C) 1997-2000 Microsoft Corporation
/****************************************************************************/
#include <precomp.h>
#pragma hdrstop
#include <ntddkbd.h>
#include <ntddmou.h>
#include "ptdrvcom.h"
#define min(a,b) (((a) < (b)) ? (a) : (b))
NTSTATUS
IcaExceptionFilter(
IN PWSTR OutputString,
IN PEXCEPTION_POINTERS pexi
);
NTSTATUS
IcaReadChannel (
IN PICA_CHANNEL pChannel,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
IcaWriteChannel (
IN PICA_CHANNEL pChannel,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
IcaDeviceControlChannel (
IN PICA_CHANNEL pChannel,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
IcaFlushChannel (
IN PICA_CHANNEL pChannel,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
IcaCleanupChannel (
IN PICA_CHANNEL pChannel,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
IcaCloseChannel (
IN PICA_CHANNEL pChannel,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
VOID
IcaFreeAllVcBind(
IN PICA_CONNECTION pConnect
);
NTSTATUS
IcaCancelReadChannel (
IN PICA_CHANNEL pChannel,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
/*
* Local procedure prototypes
*/
NTSTATUS
_IcaReadChannelComplete(
IN PICA_CHANNEL pChannel,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS _IcaQueueReadChannelRequest(
IN PICA_CHANNEL pChannel,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
_IcaCopyDataToUserBuffer(
IN PIRP Irp,
IN PUCHAR pBuffer,
IN ULONG ByteCount
);
VOID
_IcaReadChannelCancelIrp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
VOID _IcaProcessIrpList(
IN PICA_CHANNEL pChannel
);
PICA_CHANNEL
_IcaAllocateChannel(
IN PICA_CONNECTION pConnect,
IN CHANNELCLASS ChannelClass,
IN PVIRTUALCHANNELNAME pVirtualName
);
void _IcaFreeChannel(IN PICA_CHANNEL);
NTSTATUS
_IcaCallStack(
IN PICA_STACK pStack,
IN ULONG ProcIndex,
IN OUT PVOID pParms
);
NTSTATUS
_IcaCallStackNoLock(
IN PICA_STACK pStack,
IN ULONG ProcIndex,
IN OUT PVOID pParms
);
NTSTATUS
_IcaRegisterVcBind(
IN PICA_CONNECTION pConnect,
IN PVIRTUALCHANNELNAME pVirtualName,
IN VIRTUALCHANNELCLASS VirtualClass,
IN ULONG Flags
);
VIRTUALCHANNELCLASS
_IcaFindVcBind(
IN PICA_CONNECTION pConnect,
IN PVIRTUALCHANNELNAME pVirtualName,
OUT PULONG pFlags
);
VOID
_IcaBindChannel(
IN PICA_CHANNEL pChannel,
IN CHANNELCLASS ChannelClass,
IN VIRTUALCHANNELCLASS VirtualClass,
IN ULONG Flags
);
/*
* Dispatch table for ICA channel objects
*/
PICA_DISPATCH IcaChannelDispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1] = {
NULL, // IRP_MJ_CREATE
NULL, // IRP_MJ_CREATE_NAMED_PIPE
IcaCloseChannel, // IRP_MJ_CLOSE
IcaReadChannel, // IRP_MJ_READ
IcaWriteChannel, // IRP_MJ_WRITE
NULL, // IRP_MJ_QUERY_INFORMATION
NULL, // IRP_MJ_SET_INFORMATION
NULL, // IRP_MJ_QUERY_EA
NULL, // IRP_MJ_SET_EA
IcaFlushChannel, // IRP_MJ_FLUSH_BUFFERS
NULL, // IRP_MJ_QUERY_VOLUME_INFORMATION
NULL, // IRP_MJ_SET_VOLUME_INFORMATION
NULL, // IRP_MJ_DIRECTORY_CONTROL
NULL, // IRP_MJ_FILE_SYSTEM_CONTROL
IcaDeviceControlChannel, // IRP_MJ_DEVICE_CONTROL
NULL, // IRP_MJ_INTERNAL_DEVICE_CONTROL
NULL, // IRP_MJ_SHUTDOWN
NULL, // IRP_MJ_LOCK_CONTROL
IcaCleanupChannel, // IRP_MJ_CLEANUP
NULL, // IRP_MJ_CREATE_MAILSLOT
NULL, // IRP_MJ_QUERY_SECURITY
NULL, // IRP_MJ_SET_SECURITY
NULL, // IRP_MJ_SET_POWER
NULL, // IRP_MJ_QUERY_POWER
};
#if DBG
extern PICA_DISPATCH IcaStackDispatchTable[];
#endif
NTSTATUS IcaCreateChannel(
IN PICA_CONNECTION pConnect,
IN PICA_OPEN_PACKET openPacket,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp)
/*++
Routine Description:
This routine is called to create a new ICA_CHANNEL object.
- the reference count is incremented by one
Arguments:
pConnect -- pointer to ICA_CONNECTION object
Irp - Pointer to I/O request packet
IrpSp - pointer to the stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
PICA_CHANNEL pChannel;
CHANNELCLASS ChannelClass;
NTSTATUS Status;
/*
* Validate ChannelClass
*/
ChannelClass = openPacket->TypeInfo.ChannelClass;
if ( !(ChannelClass >= Channel_Keyboard && ChannelClass <= Channel_Virtual) )
return( STATUS_INVALID_PARAMETER );
/*
* Ensure VirtualName has a trailing NULL
*/
if ( !memchr( openPacket->TypeInfo.VirtualName,
'\0',
sizeof( openPacket->TypeInfo.VirtualName ) ) )
return( STATUS_INVALID_PARAMETER );
/*
* Must lock connection object to create new channel.
*/
IcaLockConnection( pConnect );
TRACE(( pConnect, TC_ICADD, TT_API2, "TermDD: IcaCreateChannel: cc %u, vn %s\n",
ChannelClass, openPacket->TypeInfo.VirtualName ));
/*
* Locate channel object
*/
pChannel = IcaFindChannelByName(pConnect,
ChannelClass,
openPacket->TypeInfo.VirtualName);
/*
* See if this channel has already been created.
* If not, then create/initialize it now.
*/
if ( pChannel == NULL ) {
/*
* Allocate a new ICA channel object
*/
pChannel = _IcaAllocateChannel(pConnect,
ChannelClass,
openPacket->TypeInfo.VirtualName);
if (pChannel == NULL) {
IcaUnlockConnection(pConnect);
return( STATUS_INSUFFICIENT_RESOURCES );
}
}
/*
* Increment open count for this channel
*/
if (InterlockedIncrement(&pChannel->OpenCount) <= 0) {
ASSERT( FALSE );
}
/*
* If the CHANNEL_CLOSING flag is set, then we are re-referenceing
* a channel object that was just closed by a previous caller,
* but has not yet been completely dereferenced.
* This can happen if this create call comes in between the
* calls to IcaCleanupChannel and IcaCloseChannel which happen
* when a channel handle is closed.
*/
if ( pChannel->Flags & CHANNEL_CLOSING ) {
/*
* Lock channel while we clear out the CHANNEL_CLOSING flag.
*/
IcaLockChannel(pChannel);
pChannel->Flags &= ~CHANNEL_CLOSING;
IcaUnlockChannel(pChannel);
}
IcaUnlockConnection(pConnect);
/*
* Save a pointer to the channel in the file object
* so that we can find it in future calls.
* - leave the reference on the channel object
*/
IrpSp->FileObject->FsContext = pChannel;
/*
* Exit with the channel reference count incremented by one
*/
return STATUS_SUCCESS;
}
NTSTATUS IcaReadChannel(
IN PICA_CHANNEL pChannel,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp)
/*++
Routine Description:
This is the read routine for ICA channels.
Arguments:
pChannel -- pointer to ICA_CHANNEL object
Irp - Pointer to I/O request packet
IrpSp - pointer to the stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
KIRQL cancelIrql;
NTSTATUS Status = STATUS_PENDING;
ULONG bChannelAlreadyLocked;
/*
* Determine the channel type to see if read is supported.
* Also do read size verification for keyboard/mouse.
*/
switch ( pChannel->ChannelClass ) {
/*
* Make sure input size is a multiple of KEYBOARD_INPUT_DATA
*/
case Channel_Keyboard :
if ( IrpSp->Parameters.Read.Length % sizeof(KEYBOARD_INPUT_DATA) )
Status = STATUS_BUFFER_TOO_SMALL;
break;
/*
* Make sure input size is a multiple of MOUSE_INPUT_DATA
*/
case Channel_Mouse :
if ( IrpSp->Parameters.Read.Length % sizeof(MOUSE_INPUT_DATA) )
Status = STATUS_BUFFER_TOO_SMALL;
break;
/*
* Nothing required for Command/Virtual channels
*/
case Channel_Command :
case Channel_Virtual :
break;
/*
* Read not supported for the following channels
*/
case Channel_Video :
case Channel_Beep :
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
default:
ASSERTMSG( "TermDD: Invalid Channel Class", FALSE );
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
/*
* If read length is 0, or an error is being returned, return now.
*/
if (Status == STATUS_PENDING && IrpSp->Parameters.Read.Length == 0)
Status = STATUS_SUCCESS;
if (Status != STATUS_PENDING) {
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IcaPriorityBoost);
TRACECHANNEL(( pChannel, TC_ICADD, TT_ERROR,
"TermDD: IcaReadChannel, cc %u, vc %d, 0x%x\n",
pChannel->ChannelClass, pChannel->VirtualClass, Status ));
return Status;
}
/*
* Verify user's buffer is valid
*/
if (Irp->RequestorMode != KernelMode) {
try {
ProbeForWrite(Irp->UserBuffer, IrpSp->Parameters.Read.Length, sizeof(BYTE));
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IcaPriorityBoost);
TRACECHANNEL((pChannel, TC_ICADD, TT_ERROR,
"TermDD: IcaReadChannel, cc %u, vc %d, 0x%x\n",
pChannel->ChannelClass, pChannel->VirtualClass, Status));
return Status;
}
}
/*
* Lock the channel while we determine how to handle this read request.
* One of the following will be true:
* 1) Input data is available; copy it to user buffer and complete IRP,
* 2) No data available, IRP cancel is requested; cancel/complete IRP,
* 3) No data; add IRP to pending read list, return STATUS_PENDING.
*/
if (ExIsResourceAcquiredExclusiveLite(&(pChannel->Resource))) {
bChannelAlreadyLocked = TRUE;
IcaReferenceChannel(pChannel);
}
else {
bChannelAlreadyLocked = FALSE;
IcaLockChannel(pChannel);
}
/*
* If the channel is being closed,
* then don't allow any further read requests.
*/
if (pChannel->Flags & CHANNEL_CLOSING) {
Status = STATUS_FILE_CLOSED;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IcaPriorityBoost);
TRACECHANNEL((pChannel, TC_ICADD, TT_ERROR,
"TermDD: IcaReadChannel, cc %u, vc %d, 0x%x\n",
pChannel->ChannelClass, pChannel->VirtualClass, Status));
IcaUnlockChannel(pChannel);
return Status;
}
/*
* If the Winstation is terminating and Reads are cancelled
* then don't allow any further read requests.
*/
if (pChannel->Flags & CHANNEL_CANCEL_READS) {
Status = STATUS_FILE_CLOSED;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IcaPriorityBoost);
TRACECHANNEL((pChannel, TC_ICADD, TT_ERROR,
"TermDD: IcaReadChannel, cc %u, vc %d, 0x%x\n",
pChannel->ChannelClass, pChannel->VirtualClass, Status));
IcaUnlockChannel(pChannel);
return Status;
}
if (InterlockedCompareExchange(&(pChannel->CompletionRoutineCount), 1, 0) == 0) {
/*
* If there is already input data available,
* then use it to satisfy the caller's read request.
*/
if ( !IsListEmpty( &pChannel->InputBufHead ) ) {
_IcaProcessIrpList(pChannel);
if (!IsListEmpty( &pChannel->InputBufHead )) {
Status = _IcaReadChannelComplete( pChannel, Irp, IrpSp );
TRACECHANNEL(( pChannel, TC_ICADD, TT_IN3, "TermDD: IcaReadChannel, cc %u, vc %d, 0x%x\n",
pChannel->ChannelClass, pChannel->VirtualClass, Status ));
_IcaProcessIrpList(pChannel);
}
else {
Status = _IcaQueueReadChannelRequest(pChannel, Irp, IrpSp);
}
}
else {
Status = _IcaQueueReadChannelRequest(pChannel, Irp, IrpSp);
}
InterlockedDecrement(&(pChannel->CompletionRoutineCount));
ASSERT(pChannel->CompletionRoutineCount == 0);
}
else {
Status = _IcaQueueReadChannelRequest(pChannel, Irp, IrpSp);
}
/*
* Unlock channel now
*/
if (bChannelAlreadyLocked) {
IcaDereferenceChannel( pChannel );
}
else {
IcaUnlockChannel(pChannel);
}
return Status;
}
void _IcaProcessIrpList(
IN PICA_CHANNEL pChannel)
{
KIRQL cancelIrql;
PIRP irpFromQueue;
PIO_STACK_LOCATION irpSpFromQueue;
PLIST_ENTRY irpQueueHead;
NTSTATUS irpStatus;
ASSERT( ExIsResourceAcquiredExclusiveLite( &pChannel->Resource ) );
/*
* Acquire IoCancel spinlock while checking InputIrp list
*/
IoAcquireCancelSpinLock( &cancelIrql );
/*
* If there is a pending read IRP, then remove it from the
* list and try to complete it now.
*/
while (!IsListEmpty( &pChannel->InputIrpHead ) &&
!IsListEmpty( &pChannel->InputBufHead )) {
irpQueueHead = RemoveHeadList( &pChannel->InputIrpHead );
irpFromQueue = CONTAINING_RECORD( irpQueueHead, IRP, Tail.Overlay.ListEntry );
irpSpFromQueue = IoGetCurrentIrpStackLocation( irpFromQueue );
/*
* Clear the cancel routine for this IRP
*/
IoSetCancelRoutine( irpFromQueue, NULL );
IoReleaseCancelSpinLock( cancelIrql );
irpStatus = _IcaReadChannelComplete( pChannel, irpFromQueue, irpSpFromQueue );
TRACECHANNEL(( pChannel, TC_ICADD, TT_IN3, "TermDD: IcaReadChannel, cc %u, vc %d, 0x%x\n",
pChannel->ChannelClass, pChannel->VirtualClass, irpStatus ));
/*
* Acquire IoCancel spinlock while checking InputIrp list
*/
IoAcquireCancelSpinLock( &cancelIrql );
}
IoReleaseCancelSpinLock( cancelIrql );
}
NTSTATUS _IcaQueueReadChannelRequest(
IN PICA_CHANNEL pChannel,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp)
{
KIRQL cancelIrql;
NTSTATUS Status = STATUS_PENDING;
ASSERT( ExIsResourceAcquiredExclusiveLite( &pChannel->Resource ) );
/*
* Acquire the IoCancel spinlock.
* We use this spinlock to protect access to the InputIrp list.
*/
IoAcquireCancelSpinLock(&cancelIrql);
/*
* No input data is available.
* Add the Irp to the pending Irp list for this channel.
*/
InsertTailList(&pChannel->InputIrpHead, &Irp->Tail.Overlay.ListEntry);
IoMarkIrpPending(Irp);
/*
* If this IRP is being cancelled, then cancel it now.
* Otherwise, set the cancel routine for this request.
*/
if (Irp->Cancel) {
Irp->CancelIrql = cancelIrql;
_IcaReadChannelCancelIrp(IrpSp->DeviceObject, Irp);
TRACECHANNEL(( pChannel, TC_ICADD, TT_IN3,
"TermDD: _IcaQueueReadChannelRequest, cc %u, vc %d (canceled)\n",
pChannel->ChannelClass, pChannel->VirtualClass));
return STATUS_CANCELLED;
}
IoSetCancelRoutine(Irp, _IcaReadChannelCancelIrp);
IoReleaseCancelSpinLock(cancelIrql);
TRACECHANNEL((pChannel, TC_ICADD, TT_IN3,
"TermDD: _IcaQueueReadChannelRequest, cc %u, vc %d (pending)\n",
pChannel->ChannelClass, pChannel->VirtualClass));
return STATUS_PENDING;
}
NTSTATUS _IcaReadChannelComplete(
IN PICA_CHANNEL pChannel,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp)
{
KIRQL cancelIrql;
PLIST_ENTRY Head;
PINBUF pInBuf;
PVOID pBuffer;
ULONG CopyCount;
NTSTATUS Status;
ASSERT( ExIsResourceAcquiredExclusiveLite( &pChannel->Resource ) );
TRACECHANNEL(( pChannel, TC_ICADD, TT_IN4, "TermDD: _IcaReadChannelComplete, cc %u, vc %d\n",
pChannel->ChannelClass, pChannel->VirtualClass ));
/*
* Get pointer to first input buffer
*/
ASSERT( !IsListEmpty( &pChannel->InputBufHead ) );
Head = pChannel->InputBufHead.Flink;
pInBuf = CONTAINING_RECORD( Head, INBUF, Links );
/*
* Clear the cancel routine for this IRP,
* since one way or the other it will be completed.
*/
IoAcquireCancelSpinLock( &cancelIrql );
IoSetCancelRoutine( Irp, NULL );
IoReleaseCancelSpinLock( cancelIrql );
/*
* If this is a message mode channel, all data from a single input
* buffer must fit in the user buffer, otherwise we return an error.
*/
if (IrpSp->Parameters.Read.Length < pInBuf->ByteCount &&
(pChannel->Flags & CHANNEL_MESSAGE_MODE)) {
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
IoCompleteRequest( Irp, IcaPriorityBoost );
TRACECHANNEL(( pChannel, TC_ICADD, TT_ERROR,
"TermDD: _IcaReadChannelComplete: cc %u, vc %d (buffer too small)\n",
pChannel->ChannelClass, pChannel->VirtualClass ));
return STATUS_BUFFER_TOO_SMALL;
}
/*
* Determine amount of data to copy to user's buffer.
*/
CopyCount = min(IrpSp->Parameters.Read.Length, pInBuf->ByteCount);
/*
* Copy input data to user's buffer
*/
Status = _IcaCopyDataToUserBuffer(Irp, pInBuf->pBuffer, CopyCount);
/*
* Update ICA buffer pointer and bytes remaining.
* If no bytes remain, then unlink the input buffer and free it.
*/
if ( Status == STATUS_SUCCESS ) {
pChannel->InputBufCurSize -= CopyCount;
pInBuf->pBuffer += CopyCount;
pInBuf->ByteCount -= CopyCount;
if ( pInBuf->ByteCount == 0 ) {
RemoveEntryList( &pInBuf->Links );
ICA_FREE_POOL( pInBuf );
}
}
/*
* Mark the Irp complete
*/
Irp->IoStatus.Status = Status;
IoCompleteRequest( Irp, IcaPriorityBoost );
TRACECHANNEL(( pChannel, TC_ICADD, TT_IN3,
"TermDD: _IcaReadChannelComplete: cc %u, vc %d, bc %u, 0x%x\n",
pChannel->ChannelClass, pChannel->VirtualClass, CopyCount, Status ));
return Status;
}
NTSTATUS _IcaCopyDataToUserBuffer(
IN PIRP Irp,
IN PUCHAR pBuffer,
IN ULONG ByteCount)
{
NTSTATUS Status;
/*
* If we are in the context of the original caller's process,
* then just copy the data into the user's buffer directly.
*/
if ( IoGetRequestorProcess( Irp ) == IoGetCurrentProcess() ) {
try {
Status = STATUS_SUCCESS;
RtlCopyMemory( Irp->UserBuffer, pBuffer, ByteCount );
} except( EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
}
/*
* If there is a MDL allocated for this IRP, then copy the data
* directly to the users buffer via the MDL.
*/
} else if ( Irp->MdlAddress ) {
PVOID UserBuffer;
UserBuffer = MmGetSystemAddressForMdl( Irp->MdlAddress );
try {
if (UserBuffer != NULL) {
Status = STATUS_SUCCESS;
RtlCopyMemory( UserBuffer, pBuffer, ByteCount );
}else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
} except( EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
}
/*
* There is no MDL for this request. We must allocate a secondary
* buffer, copy the data to it, and indicate this is a buffered I/O
* request in the IRP. The I/O completion routine will copy the
* data to the user's buffer.
*/
} else {
ASSERT( Irp->AssociatedIrp.SystemBuffer == NULL );
Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag( PagedPool,
ByteCount,
ICA_POOL_TAG );
if ( Irp->AssociatedIrp.SystemBuffer == NULL )
return( STATUS_INSUFFICIENT_RESOURCES );
RtlCopyMemory( Irp->AssociatedIrp.SystemBuffer, pBuffer, ByteCount );
Irp->Flags |= (IRP_BUFFERED_IO |
IRP_DEALLOCATE_BUFFER |
IRP_INPUT_OPERATION);
Status = STATUS_SUCCESS;
}
if ( Status == STATUS_SUCCESS )
Irp->IoStatus.Information = ByteCount;
return Status;
}
VOID _IcaReadChannelCancelIrp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
PIO_STACK_LOCATION IrpSp;
PICA_CHANNEL pChannel;
IrpSp = IoGetCurrentIrpStackLocation( Irp );
pChannel = IrpSp->FileObject->FsContext;
/*
* Remove IRP from channel pending IRP list and release cancel spinlock
*/
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
IoReleaseCancelSpinLock(Irp->CancelIrql);
/*
* Complete the IRP with a cancellation status code.
*/
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(Irp, IcaPriorityBoost);
}
NTSTATUS IcaWriteChannel(
IN PICA_CHANNEL pChannel,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp)
/*++
Routine Description:
This is the write routine for ICA channels.
Arguments:
pChannel -- pointer to ICA_CHANNEL object
Irp - Pointer to I/O request packet. Flags, specific to this
driver, can be specified as a pointer to a ULONG flags value.
The pointer to this value is the first element in the
IRP.Tail.Overlay.DriverContext field.
Currently, only CHANNEL_WRITE_LOWPRIO is supported. Write IRP's with
this flag set will take lower priority than Write IRP's without this
flag set.
IrpSp - pointer to the stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
SD_CHANNELWRITE SdWrite;
NTSTATUS Status = STATUS_PENDING;
/*
* Determine the channel type to see if write is supported.
*/
switch ( pChannel->ChannelClass ) {
case Channel_Virtual :
if ( pChannel->VirtualClass == UNBOUND_CHANNEL ) {
Status = STATUS_INVALID_DEVICE_REQUEST;
}
break;
/*
* Write not supported for the following channels
*/
case Channel_Command :
case Channel_Keyboard :
case Channel_Mouse :
case Channel_Video :
case Channel_Beep :
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
default:
ASSERTMSG( "ICA.SYS: Invalid Channel Class", FALSE );
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
/*
* If the channel is being closed,
* then don't allow any further write requests.
*/
if ( pChannel->Flags & CHANNEL_CLOSING )
Status = STATUS_FILE_CLOSED;
/*
* If write length is 0, or an error is being returned, return now.
*/
if ( Status == STATUS_PENDING && IrpSp->Parameters.Write.Length == 0 )
Status = STATUS_SUCCESS;
if ( Status != STATUS_PENDING ) {
Irp->IoStatus.Status = Status;
IoCompleteRequest( Irp, IcaPriorityBoost );
TRACECHANNEL(( pChannel, TC_ICADD, TT_ERROR, "TermDD: IcaWriteChannel, cc %u, vc %d, 0x%x\n",
pChannel->ChannelClass, pChannel->VirtualClass, Status ));
return( Status );
}
/*
* Verify user's buffer is valid
*/
if ( Irp->RequestorMode != KernelMode ) {
try {
ProbeForRead( Irp->UserBuffer, IrpSp->Parameters.Write.Length, sizeof(BYTE) );
} except( EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
Irp->IoStatus.Status = Status;
IoCompleteRequest( Irp, IcaPriorityBoost );
TRACECHANNEL(( pChannel, TC_ICADD, TT_ERROR, "TermDD: IcaWriteChannel, cc %u, vc %d, 0x%x\n",
pChannel->ChannelClass, pChannel->VirtualClass, Status ));
return( Status );
}
}
/*
* Call the top level stack driver to handle the write
*/
SdWrite.ChannelClass = pChannel->ChannelClass;
SdWrite.VirtualClass = pChannel->VirtualClass;
SdWrite.pBuffer = Irp->UserBuffer;
SdWrite.ByteCount = IrpSp->Parameters.Write.Length;
SdWrite.fScreenData = (BOOLEAN)(pChannel->Flags & CHANNEL_SCREENDATA);
SdWrite.fFlags = 0;
/*
* See if the low prio write flag is set in the IRP.
*
* The flags field is passed to termdd.sys via an IRP_MJ_WRITE
* Irp, as a ULONG pointer in the Irp->Tail.Overlay.DriverContext[0] field.
*/
if (Irp->Tail.Overlay.DriverContext[0] != NULL) {
ULONG flags = *((ULONG *)Irp->Tail.Overlay.DriverContext[0]);
if (flags & CHANNEL_WRITE_LOWPRIO) {
SdWrite.fFlags |= SD_CHANNELWRITE_LOWPRIO;
}
}
Status = IcaCallDriver( pChannel, SD$CHANNELWRITE, &SdWrite );
/*
* Complete the IRP now since all channel writes are synchronous
* (the user data is captured by the stack driver before returning).
*/
Irp->IoStatus.Status = Status;
if ( Status == STATUS_SUCCESS )
Irp->IoStatus.Information = IrpSp->Parameters.Write.Length;
IoCompleteRequest( Irp, IcaPriorityBoost );
TRACECHANNEL(( pChannel, TC_ICADD, TT_OUT3, "TermDD: IcaWriteChannel, cc %u, vc %d, bc %u, 0x%x\n",
pChannel->ChannelClass, pChannel->VirtualClass, SdWrite.ByteCount, Status ));
return Status;
}
NTSTATUS IcaDeviceControlChannel(
IN PICA_CHANNEL pChannel,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp)
{
ULONG code;
PICA_TRACE_BUFFER pTraceBuffer;
NTSTATUS Status;
/*
* If the channel is being closed,
* then don't allow any further requests.
*/
if ( pChannel->Flags & CHANNEL_CLOSING )
return( STATUS_FILE_CLOSED );
/*
* Extract the IOCTL control code and process the request.
*/
code = IrpSp->Parameters.DeviceIoControl.IoControlCode;
#if DBG
if ( code != IOCTL_ICA_CHANNEL_TRACE ) {
TRACECHANNEL(( pChannel, TC_ICADD, TT_API1, "TermDD: IcaDeviceControlChannel, fc %d, ref %u (enter)\n",
(code & 0x3fff) >> 2, pChannel->RefCount ));
}
#endif
/*
* Process generic channel ioctl requests
*/
try {
switch ( code ) {
case IOCTL_ICA_CHANNEL_TRACE :
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength < (ULONG)(FIELD_OFFSET(ICA_TRACE_BUFFER,Data[0])) )
return( STATUS_BUFFER_TOO_SMALL );
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength > sizeof(ICA_TRACE_BUFFER) )
return( STATUS_INVALID_BUFFER_SIZE );
if ( Irp->RequestorMode != KernelMode ) {
ProbeForRead( IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
sizeof(BYTE) );
}
pTraceBuffer = (PICA_TRACE_BUFFER)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
IcaLockConnection( pChannel->pConnect );
IcaTraceFormat( &pChannel->pConnect->TraceInfo,
pTraceBuffer->TraceClass,
pTraceBuffer->TraceEnable,
pTraceBuffer->Data );
IcaUnlockConnection( pChannel->pConnect );
Status = STATUS_SUCCESS;
break;
case IOCTL_ICA_CHANNEL_DISABLE_SESSION_IO:
IcaLockConnection( pChannel->pConnect );
pChannel->Flags |= CHANNEL_SESSION_DISABLEIO;
Status = IcaFlushChannel( pChannel, Irp, IrpSp );
IcaUnlockConnection( pChannel->pConnect );
break;
case IOCTL_ICA_CHANNEL_ENABLE_SESSION_IO:
IcaLockConnection( pChannel->pConnect );
pChannel->Flags &= ~CHANNEL_SESSION_DISABLEIO;
IcaUnlockConnection( pChannel->pConnect );
Status = STATUS_SUCCESS;
break;
case IOCTL_ICA_CHANNEL_CLOSE_COMMAND_CHANNEL :
IcaLockConnection( pChannel->pConnect );
Status = IcaCancelReadChannel(pChannel, Irp, IrpSp);
IcaUnlockConnection( pChannel->pConnect );
break;
case IOCTL_ICA_CHANNEL_ENABLE_SHADOW :
IcaLockConnection( pChannel->pConnect );
pChannel->Flags |= CHANNEL_SHADOW_IO;
IcaUnlockConnection( pChannel->pConnect );
Status = STATUS_SUCCESS;
break;
case IOCTL_ICA_CHANNEL_DISABLE_SHADOW :
IcaLockConnection( pChannel->pConnect );
pChannel->Flags &= ~CHANNEL_SHADOW_IO;
IcaUnlockConnection( pChannel->pConnect );
Status = STATUS_SUCCESS;
break;
case IOCTL_ICA_CHANNEL_END_SHADOW :
{
PLIST_ENTRY Head, Next;
PICA_STACK pStack;
BOOLEAN bShadowEnded = FALSE;
PICA_CHANNEL_END_SHADOW_DATA pData;
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof(ICA_CHANNEL_END_SHADOW_DATA) ) {
Status = STATUS_INVALID_BUFFER_SIZE;
break;
}
if ( Irp->RequestorMode != KernelMode ) {
ProbeForRead( IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
sizeof(BYTE) );
}
pData = (PICA_CHANNEL_END_SHADOW_DATA)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
/*
* Lock the connection object.
* This will serialize all channel calls for this connection.
*/
IcaLockConnection( pChannel->pConnect );
if ( IsListEmpty( &pChannel->pConnect->StackHead ) ) {
IcaUnlockConnection( pChannel->pConnect );
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
/*
* Look for shadow stack(s).
*/
Head = &pChannel->pConnect->StackHead;
for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
pStack = CONTAINING_RECORD( Next, ICA_STACK, StackEntry );
/*
* If this is a shadow stack, end it.
*/
if ( pStack->StackClass == Stack_Shadow ) {
if ( pStack->pBrokenEventObject ) {
KeSetEvent( pStack->pBrokenEventObject, 0, FALSE );
bShadowEnded = TRUE;
}
}
}
/*
* Unlock the connection object now.
*/
IcaUnlockConnection( pChannel->pConnect );
Status = STATUS_SUCCESS;
if (bShadowEnded && pData->bLogError) {
IcaLogError(NULL, pData->StatusCode, NULL, 0, NULL, 0);
}
break;
}
// This IOCTL is not supported by RDP or ICA driver
case IOCTL_VIDEO_ENUM_MONITOR_PDO:
Status = STATUS_DEVICE_NOT_READY;
break;
default :
/*
* Call the appropriate worker routine based on channel type
*/
switch ( pChannel->ChannelClass ) {
case Channel_Keyboard :
Status = IcaDeviceControlKeyboard( pChannel, Irp, IrpSp );
break;
case Channel_Mouse :
Status = IcaDeviceControlMouse( pChannel, Irp, IrpSp );
break;
case Channel_Video :
Status = IcaDeviceControlVideo( pChannel, Irp, IrpSp );
break;
case Channel_Beep :
Status = IcaDeviceControlBeep( pChannel, Irp, IrpSp );
break;
case Channel_Virtual :
Status = IcaDeviceControlVirtual( pChannel, Irp, IrpSp );
break;
case Channel_Command :
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
default:
ASSERTMSG( "ICA.SYS: Invalid Channel Class", FALSE );
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
}
} except( IcaExceptionFilter( L"IcaDeviceControlChannel TRAPPED!!",
GetExceptionInformation() ) ) {
Status = GetExceptionCode();
}
#if DBG
if ( code != IOCTL_ICA_CHANNEL_TRACE ) {
TRACECHANNEL(( pChannel, TC_ICADD, TT_API1, "TermDD: IcaDeviceControlChannel, fc %d, ref %u, 0x%x\n",
(code & 0x3fff) >> 2, pChannel->RefCount, Status ));
}
#endif
return Status;
}
NTSTATUS IcaFlushChannel(
IN PICA_CHANNEL pChannel,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp)
{
KIRQL cancelIrql;
PLIST_ENTRY Head;
PINBUF pInBuf;
TRACECHANNEL((pChannel, TC_ICADD, TT_API2,
"TermDD: IcaFlushChannel, cc %u, vc %d\n",
pChannel->ChannelClass, pChannel->VirtualClass));
/*
* Lock channel while we flush any input buffers.
*/
IcaLockChannel(pChannel);
while (!IsListEmpty( &pChannel->InputBufHead)) {
Head = RemoveHeadList(&pChannel->InputBufHead);
pInBuf = CONTAINING_RECORD(Head, INBUF, Links);
ICA_FREE_POOL(pInBuf);
}
IcaUnlockChannel(pChannel);
return STATUS_SUCCESS;
}
NTSTATUS IcaCleanupChannel(
IN PICA_CHANNEL pChannel,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp)
{
KIRQL cancelIrql;
PLIST_ENTRY Head;
PIRP ReadIrp;
PINBUF pInBuf;
TRACECHANNEL((pChannel, TC_ICADD, TT_API2,
"TermDD: IcaCleanupChannel, cc %u, vc %d\n",
pChannel->ChannelClass, pChannel->VirtualClass));
/*
* Decrement the open count; if it is 0, perform channel cleanup now.
*/
ASSERT(pChannel->OpenCount > 0);
if (InterlockedDecrement( &pChannel->OpenCount) == 0) {
/*
* Lock channel while we clear out any
* pending read IRPs and/or input buffers.
*/
IcaLockChannel(pChannel);
/*
* Indicate this channel is being closed
*/
pChannel->Flags |= CHANNEL_CLOSING;
IoAcquireCancelSpinLock( &cancelIrql );
while ( !IsListEmpty( &pChannel->InputIrpHead ) ) {
Head = pChannel->InputIrpHead.Flink;
ReadIrp = CONTAINING_RECORD( Head, IRP, Tail.Overlay.ListEntry );
ReadIrp->CancelIrql = cancelIrql;
IoSetCancelRoutine( ReadIrp, NULL );
_IcaReadChannelCancelIrp( IrpSp->DeviceObject, ReadIrp );
IoAcquireCancelSpinLock( &cancelIrql );
}
IoReleaseCancelSpinLock( cancelIrql );
while ( !IsListEmpty( &pChannel->InputBufHead ) ) {
Head = RemoveHeadList( &pChannel->InputBufHead );
pInBuf = CONTAINING_RECORD( Head, INBUF, Links );
ICA_FREE_POOL( pInBuf );
}
IcaUnlockChannel(pChannel);
}
return STATUS_SUCCESS;
}
NTSTATUS IcaCloseChannel(
IN PICA_CHANNEL pChannel,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp)
{
PICA_CONNECTION pConnect;
TRACECHANNEL(( pChannel, TC_ICADD, TT_API2, "TermDD: IcaCloseChannel, cc %u, vc %d, vn %s\n",
pChannel->ChannelClass, pChannel->VirtualClass, pChannel->VirtualName ));
pConnect = pChannel->pConnect;
/*
* Remove the file object reference for this channel.
*/
IcaDereferenceChannel(pChannel);
return STATUS_SUCCESS;
}
NTSTATUS IcaChannelInput(
IN PSDCONTEXT pContext,
IN CHANNELCLASS ChannelClass,
IN VIRTUALCHANNELCLASS VirtualClass,
IN PINBUF pInBuf OPTIONAL,
IN PUCHAR pBuffer OPTIONAL,
IN ULONG ByteCount)
/*++
Routine Description:
This is the input (stack callup) routine for ICA channel input.
Arguments:
pContext - Pointer to SDCONTEXT for this Stack Driver
ChannelClass - Channel number for input
VirtualClass - Virtual channel number for input
pInBuf - Pointer to INBUF containing data
pBuffer - Pointer to input data
NOTE: Either pInBuf OR pBuffer must be specified, but not both.
ByteCount - length of data in pBuffer
Return Value:
NTSTATUS -- Indicates whether the request was handled successfully.
--*/
{
PSDLINK pSdLink;
PICA_STACK pStack;
PICA_CONNECTION pConnect;
NTSTATUS Status;
/*
* Use SD passed context to get the SDLINK pointer.
*/
pSdLink = CONTAINING_RECORD( pContext, SDLINK, SdContext );
pStack = pSdLink->pStack; // save stack pointer for use below
pConnect = IcaGetConnectionForStack( pStack );
ASSERT( pSdLink->pStack->Header.Type == IcaType_Stack );
ASSERT( pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable );
TRACESTACK(( pStack, TC_ICADD, TT_API1, "TermDD: IcaChannelInput, bc=%u (enter)\n", ByteCount ));
/*
* Only the stack object should be locked during input.
*/
ASSERT( ExIsResourceAcquiredExclusiveLite( &pStack->Resource ) );
/*
* Walk up the SDLINK list looking for a driver which has specified
* a ChannelInput callup routine. If we find one, then call the
* driver ChannelInput routine to let it handle the call.
*/
while ( (pSdLink = IcaGetPreviousSdLink( pSdLink )) != NULL ) {
ASSERT( pSdLink->pStack == pStack );
if ( pSdLink->SdContext.pCallup->pSdChannelInput ) {
IcaReferenceSdLink( pSdLink );
Status = (pSdLink->SdContext.pCallup->pSdChannelInput)(
pSdLink->SdContext.pContext,
ChannelClass,
VirtualClass,
pInBuf,
pBuffer,
ByteCount );
IcaDereferenceSdLink( pSdLink );
return Status;
}
}
return IcaChannelInputInternal(pStack, ChannelClass, VirtualClass,
pInBuf, pBuffer, ByteCount);
}
NTSTATUS IcaChannelInputInternal(
IN PICA_STACK pStack,
IN CHANNELCLASS ChannelClass,
IN VIRTUALCHANNELCLASS VirtualClass,
IN PINBUF pInBuf OPTIONAL,
IN PCHAR pBuffer OPTIONAL,
IN ULONG ByteCount)
{
PICA_COMMAND_HEADER pHeader;
PICA_CONNECTION pConnect;
PICA_CHANNEL pChannel;
PLIST_ENTRY Head;
PIRP Irp;
PIO_STACK_LOCATION IrpSp;
KIRQL cancelIrql;
ULONG CopyCount;
NTSTATUS Status;
SD_IOCTL SdIoctl;
TRACESTACK(( pStack, TC_ICADD, TT_API2,
"TermDD: IcaChannelInputInternal: cc %u, vc %d, bc %u\n",
ChannelClass, VirtualClass, ByteCount ));
/*
* Check for channel command
*/
switch ( ChannelClass ) {
case Channel_Keyboard :
case Channel_Mouse :
KeQuerySystemTime( &pStack->LastInputTime );
break;
case Channel_Command :
if ( ByteCount < sizeof(ICA_COMMAND_HEADER) ) {
TRACESTACK(( pStack, TC_ICADD, TT_ERROR,
"TermDD: IcaChannelInputInternal: Channel_command bad bytecount\n" ));
break;
}
pHeader = (PICA_COMMAND_HEADER) pBuffer;
switch ( pHeader->Command ) {
case ICA_COMMAND_BROKEN_CONNECTION :
TRACESTACK(( pStack, TC_ICADD, TT_API1,
"TermDD: IcaChannelInputInternal, Broken Connection\n" ));
/* set closing flag */
pStack->fClosing = TRUE;
/*
* Send cancel i/o to stack drivers
* - fClosing flag must be set before issuing cancel i/o
*/
SdIoctl.IoControlCode = IOCTL_ICA_STACK_CANCEL_IO;
(void) _IcaCallStackNoLock( pStack, SD$IOCTL, &SdIoctl );
/*
* If a broken event has been registered for this stack,
* then signal the event now.
* NOTE: In this case we exit without forwarding the
* broken notification to the channel.
*/
if ( pStack->pBrokenEventObject ) {
KeSetEvent( pStack->pBrokenEventObject, 0, FALSE );
ObDereferenceObject( pStack->pBrokenEventObject );
pStack->pBrokenEventObject = NULL;
if ( pInBuf )
ICA_FREE_POOL( pInBuf );
return( STATUS_SUCCESS );
}
break;
}
break;
}
/*
* Get the specified channel for this input packet.
* If not found, we have no choice but to bit-bucket the data.
*/
pConnect = IcaGetConnectionForStack(pStack);
pChannel = IcaFindChannel(pConnect, ChannelClass, VirtualClass);
if (pChannel == NULL) {
if (pInBuf)
ICA_FREE_POOL(pInBuf);
TRACESTACK((pStack, TC_ICADD, TT_ERROR,
"TermDD: IcaChannelInputInternal: channel not found\n" ));
return STATUS_SUCCESS;
}
/*
* Lock channel while processing I/O
*/
IcaLockChannel(pChannel);
/*
* If input is from a shadow stack and this channel should not
* process shadow I/O then bit bucket the data.
* Do the same if the channel is closing or IO are disabled.
*/
if ( (pChannel->Flags & (CHANNEL_SESSION_DISABLEIO | CHANNEL_CLOSING)) ||
(pStack->StackClass == Stack_Shadow &&
!(pChannel->Flags & CHANNEL_SHADOW_IO)) ) {
IcaUnlockChannel(pChannel);
IcaDereferenceChannel(pChannel);
if (pInBuf)
ICA_FREE_POOL(pInBuf);
TRACESTACK((pStack, TC_ICADD, TT_API2,
"TermDD: IcaChannelInputInternal: shadow or closing channel input\n"));
return STATUS_SUCCESS;
}
/*
* If input is from an INBUF, initialize pBuffer and ByteCount
* with values from the buffer header.
*/
if (pInBuf) {
pBuffer = pInBuf->pBuffer;
ByteCount = pInBuf->ByteCount;
}
/*
* If there is a channel filter loaded for this channel,
* then pass the input data through it before going on.
*/
if (pChannel->pFilter) {
PINBUF pFilterBuf;
pChannel->pFilter->InputFilter(pChannel->pFilter, pBuffer, ByteCount,
&pFilterBuf);
if (pInBuf)
ICA_FREE_POOL(pInBuf);
/*
* Refresh INBUF pointer, buffer pointer, and byte count.
*/
pInBuf = pFilterBuf;
pBuffer = pInBuf->pBuffer;
ByteCount = pInBuf->ByteCount;
}
/*
* Process the input data
*/
while ( ByteCount != 0 ) {
/*
* If this is a shadow stack, see if the stack we're shadowing is
* for a console session
*/
if (pStack->StackClass == Stack_Shadow)
{
PICA_STACK pTopStack;
PLIST_ENTRY Head, Next;
Head = &pConnect->StackHead;
Next = Head->Flink;
pTopStack = CONTAINING_RECORD( Next, ICA_STACK, StackEntry );
if (pTopStack->StackClass == Stack_Console)
{
/*
* It is the console, so put on our keyboard/mouse port
* driver hat and inject the input that way
*/
if (ChannelClass == Channel_Mouse)
{
MOUSE_INPUT_DATA *pmInputData;
ULONG count;
pmInputData = (MOUSE_INPUT_DATA *)pBuffer;
count = ByteCount / sizeof(MOUSE_INPUT_DATA);
/*
* This function will always consume all the data
*/
PtSendCurrentMouseInput(MouDeviceObject, pmInputData, count);
ByteCount = 0;
continue;
}
else if (ChannelClass == Channel_Keyboard)
{
KEYBOARD_INPUT_DATA *pkInputData;
ULONG count;
pkInputData = (KEYBOARD_INPUT_DATA *)pBuffer;
count = ByteCount / sizeof(KEYBOARD_INPUT_DATA);
/*
* This function will always consume all the data
*/
PtSendCurrentKeyboardInput(KbdDeviceObject, pkInputData, count);
ByteCount = 0;
continue;
}
}
}
/*
* Acquire IoCancel spinlock while checking InputIrp list
*/
IoAcquireCancelSpinLock( &cancelIrql );
/*
* If there is a pending read IRP, then remove it from the
* list and try to complete it now.
*/
if ( !IsListEmpty( &pChannel->InputIrpHead ) ) {
Head = RemoveHeadList( &pChannel->InputIrpHead );
Irp = CONTAINING_RECORD( Head, IRP, Tail.Overlay.ListEntry );
IrpSp = IoGetCurrentIrpStackLocation( Irp );
/*
* Clear the cancel routine for this IRP
*/
IoSetCancelRoutine( Irp, NULL );
IoReleaseCancelSpinLock( cancelIrql );
/*
* If this is a message mode channel, all data from a single input
* buffer must fit in the user buffer, otherwise we return an error.
*/
if ( IrpSp->Parameters.Read.Length < ByteCount &&
(pChannel->Flags & CHANNEL_MESSAGE_MODE) ) {
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
IoCompleteRequest( Irp, IcaPriorityBoost );
TRACECHANNEL(( pChannel, TC_ICADD, TT_API2,
"TermDD: IcaChannelInputInternal: cc %u, vc %d, (too small)\n",
ChannelClass, VirtualClass ));
continue;
}
/*
* Determine amount of data to copy to user's buffer.
*/
CopyCount = min( IrpSp->Parameters.Read.Length, ByteCount );
/*
* Copy input data to user's buffer
*/
Status = _IcaCopyDataToUserBuffer( Irp, pBuffer, CopyCount );
/*
* Mark the Irp complete and return success
*/
Irp->IoStatus.Status = Status;
IoCompleteRequest( Irp, IcaPriorityBoost );
TRACECHANNEL(( pChannel, TC_ICADD, TT_API2,
"TermDD: IcaChannelInputInternal: cc %u, vc %d, bc %u, 0x%x\n",
ChannelClass, VirtualClass, CopyCount, Status ));
/*
* Update input data pointer and count remaining.
* Note no need to update pChannel->InputBufCurSize since we never
* stored this data.
*/
if ( Status == STATUS_SUCCESS ) {
pBuffer += CopyCount;
ByteCount -= CopyCount;
if ( pInBuf ) {
pInBuf->pBuffer += CopyCount;
pInBuf->ByteCount -= CopyCount;
}
}
/*
* There are no pending IRPs for this channel, so just queue the data.
*/
} else {
IoReleaseCancelSpinLock( cancelIrql );
/*
* Check to see if we need to discard the data (too much data
* backed up). This policy only takes effect when the max size
* is nonzero, which is currently only the case for mouse and
* keyboard inputs which can withstand being dropped.
* Note that the read IRPs sent for channels that can have
* data dropped must request in integral numbers of input
* blocks -- e.g. a mouse read IRP must have a read buffer size
* that is a multiple of sizeof(MOUSE_INPUT_DATA). If this is
* not the case the immediate-copy block above may copy
* partial input blocks before arriving here.
*/
if (pChannel->InputBufMaxSize == 0 ||
(pChannel->InputBufCurSize + ByteCount) <=
pChannel->InputBufMaxSize) {
/*
* If necessary, allocate an input buffer and copy the data
*/
if (pInBuf == NULL) {
/*
* Get input buffer and copy the data
* If this fails, we have no choice but to bail out.
*/
pInBuf = ICA_ALLOCATE_POOL(NonPagedPool, sizeof(INBUF) +
ByteCount);
if (pInBuf != NULL) {
pInBuf->ByteCount = ByteCount;
pInBuf->MaxByteCount = ByteCount;
pInBuf->pBuffer = (PUCHAR)(pInBuf + 1);
RtlCopyMemory(pInBuf->pBuffer, pBuffer, ByteCount);
}
else {
break;
}
}
/*
* Add buffer to tail of input list and clear pInBuf
* to indicate we have no buffer to free when done.
*/
InsertTailList( &pChannel->InputBufHead, &pInBuf->Links );
pChannel->InputBufCurSize += ByteCount;
pInBuf = NULL;
/*
* If any read(s) were posted while we allocated the input
* buffer, then try to complete as many as possible.
*/
IoAcquireCancelSpinLock( &cancelIrql );
while ( !IsListEmpty( &pChannel->InputIrpHead ) &&
!IsListEmpty( &pChannel->InputBufHead ) ) {
Head = RemoveHeadList( &pChannel->InputIrpHead );
Irp = CONTAINING_RECORD( Head, IRP, Tail.Overlay.ListEntry );
IoSetCancelRoutine( Irp, NULL );
IoReleaseCancelSpinLock( cancelIrql );
IrpSp = IoGetCurrentIrpStackLocation( Irp );
Status = _IcaReadChannelComplete( pChannel, Irp, IrpSp );
IoAcquireCancelSpinLock( &cancelIrql );
}
IoReleaseCancelSpinLock( cancelIrql );
}
else {
TRACESTACK(( pStack, TC_ICADD, TT_ERROR,
"TermDD: IcaChannelInputInternal: Dropped %u bytes "
"on channelclass %u\n", ByteCount, ChannelClass));
}
break;
}
}
/*
* Unlock channel now
*/
IcaUnlockChannel(pChannel);
/*
* If we still have an INBUF, free it now.
*/
if (pInBuf)
ICA_FREE_POOL(pInBuf);
/*
* Decrement channel refcount and return
*/
IcaDereferenceChannel(pChannel);
return STATUS_SUCCESS;
}
/****************************************************************************/
// IcaFindChannel
// IcaFindChannelByName
//
// Searches for a given channel in the connection channel list, and returns
// a pointer to it (with an added reference). Returns NULL if not found.
/****************************************************************************/
PICA_CHANNEL IcaFindChannel(
IN PICA_CONNECTION pConnect,
IN CHANNELCLASS ChannelClass,
IN VIRTUALCHANNELCLASS VirtualClass)
{
PICA_CHANNEL pChannel;
KIRQL oldIrql;
NTSTATUS Status;
/*
* Ensure we're not looking for an invalid virtual channel number
*/
ASSERT( ChannelClass != Channel_Virtual ||
(VirtualClass >= 0 && VirtualClass < VIRTUAL_MAXIMUM) );
/*
* If channel does not exist, return NULL.
*/
IcaLockChannelTable(&pConnect->ChannelTableLock);
pChannel = pConnect->pChannel[ ChannelClass + VirtualClass ];
if (pChannel == NULL) {
TRACE(( pConnect, TC_ICADD, TT_API3,
"TermDD: IcaFindChannel, cc %u, vc %d (not found)\n",
ChannelClass, VirtualClass ));
IcaUnlockChannelTable(&pConnect->ChannelTableLock);
return NULL;
}
IcaReferenceChannel(pChannel);
IcaUnlockChannelTable(&pConnect->ChannelTableLock);
TRACE((pConnect, TC_ICADD, TT_API3,
"TermDD: IcaFindChannel, cc %u, vc %d -> %s\n",
ChannelClass, VirtualClass, pChannel->VirtualName));
return pChannel;
}
PICA_CHANNEL IcaFindChannelByName(
IN PICA_CONNECTION pConnect,
IN CHANNELCLASS ChannelClass,
IN PVIRTUALCHANNELNAME pVirtualName)
{
PICA_CHANNEL pChannel;
PLIST_ENTRY Head, Next;
ASSERT( ExIsResourceAcquiredExclusiveLite( &pConnect->Resource ) );
/*
* If this is not a virtual channel use channel class only
*/
if (ChannelClass != Channel_Virtual) {
return IcaFindChannel( pConnect, ChannelClass, 0);
}
/*
* Search the existing channel structures to locate virtual channel name
*/
IcaLockChannelTable(&pConnect->ChannelTableLock);
Head = &pConnect->ChannelHead;
for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
pChannel = CONTAINING_RECORD( Next, ICA_CHANNEL, Links );
if ( (pChannel->ChannelClass == Channel_Virtual) &&
!_stricmp( pChannel->VirtualName, pVirtualName ) ) {
break;
}
}
/*
* If name does not exist, return unbound
*/
if (Next == Head) {
TRACE((pConnect, TC_ICADD, TT_API2,
"TermDD: IcaFindChannelByName: vn %s (not found)\n", pVirtualName));
IcaUnlockChannelTable(&pConnect->ChannelTableLock);
return(NULL);
}
IcaReferenceChannel(pChannel);
IcaUnlockChannelTable(&pConnect->ChannelTableLock);
TRACE((pConnect, TC_ICADD, TT_API2,
"TermDD: IcaFindChannelByName: vn %s, vc %d, ref %u\n",
pVirtualName, pChannel->VirtualClass,
(pChannel != NULL ? pChannel->RefCount : 0)));
return pChannel;
}
VOID IcaReferenceChannel(IN PICA_CHANNEL pChannel)
{
TRACECHANNEL((pChannel, TC_ICADD, TT_API2,
"TermDD: IcaReferenceChannel: cc %u, vc %d, ref %u\n",
pChannel->ChannelClass, pChannel->VirtualClass, pChannel->RefCount));
ASSERT(pChannel->RefCount >= 0);
/*
* Increment the reference count
*/
if (InterlockedIncrement( &pChannel->RefCount) <= 0) {
ASSERT(FALSE);
}
}
VOID IcaDereferenceChannel(
IN PICA_CHANNEL pChannel)
{
BOOLEAN bNeedLock = FALSE;
BOOLEAN bChannelFreed = FALSE;
PERESOURCE pResource = pChannel->pChannelTableLock;
PICA_CONNECTION pConnect = pChannel->pConnect;
TRACECHANNEL((pChannel, TC_ICADD, TT_API2,
"TermDD: IcaDefeferenceChannel: cc %u, vc %d, ref %u\n",
pChannel->ChannelClass, pChannel->VirtualClass,
pChannel->RefCount));
ASSERT(pChannel->RefCount > 0);
/*
* Lock the channel table since a reference going to Zero would cause
* to change table entry.
*/
if (pChannel->RefCount == 1) {
bNeedLock = TRUE;
IcaLockChannelTable(pResource);
}
/*
* Decrement the reference count; if it is 0, free the channel.
*/
if (InterlockedDecrement(&pChannel->RefCount) == 0){
ASSERT(bNeedLock);
_IcaFreeChannel(pChannel);
bChannelFreed = TRUE;
}
if (bNeedLock) {
IcaUnlockChannelTable(pResource);
}
/*
* Remove the reference to the Connection object for this channel.
* moved this here from _IcaFreeChannel because we need to be sure
* the connection object can't go away before the call to IcaUnlockChannelTable
* because the connection object is where the channel table locck live.
*/
if (bChannelFreed) {
IcaDereferenceConnection(pConnect);
}
}
NTSTATUS IcaBindVirtualChannels(IN PICA_STACK pStack)
{
PICA_CONNECTION pConnect;
PSD_VCBIND pSdVcBind = NULL;
SD_VCBIND aSdVcBind[ VIRTUAL_MAXIMUM ];
ULONG SdVcBindCount;
VIRTUALCHANNELCLASS VirtualClass;
PICA_CHANNEL pChannel;
NTSTATUS Status;
ULONG i, Flags;
SD_IOCTL SdIoctl;
pConnect = IcaLockConnectionForStack(pStack);
SdIoctl.IoControlCode = IOCTL_ICA_VIRTUAL_QUERY_BINDINGS;
SdIoctl.InputBuffer = NULL;
SdIoctl.InputBufferLength = 0;
SdIoctl.OutputBuffer = aSdVcBind;
SdIoctl.OutputBufferLength = sizeof(aSdVcBind);
Status = _IcaCallStack(pStack, SD$IOCTL, &SdIoctl);
if (NT_SUCCESS(Status)) {
pSdVcBind = &aSdVcBind[0];
SdVcBindCount = SdIoctl.BytesReturned / sizeof(SD_VCBIND);
for (i = 0; i < SdVcBindCount; i++, pSdVcBind++) {
TRACE((pConnect, TC_ICADD, TT_API2,
"TermDD: IcaBindVirtualChannels: %s -> %d Flags=%x\n",
pSdVcBind->VirtualName, pSdVcBind->VirtualClass, pSdVcBind->Flags));
/*
* Locate virtual class binding
*/
VirtualClass = _IcaFindVcBind(pConnect, pSdVcBind->VirtualName, &Flags);
/*
* If virtual class binding does not exist, create one
*/
if (VirtualClass == UNBOUND_CHANNEL) {
/*
* Allocate a new virtual bind object
*/
Status = _IcaRegisterVcBind(pConnect, pSdVcBind->VirtualName,
pSdVcBind->VirtualClass, pSdVcBind->Flags );
if (!NT_SUCCESS(Status))
goto PostLockConnection;
}
/*
* Locate channel object
*/
pChannel = IcaFindChannelByName(pConnect, Channel_Virtual,
pSdVcBind->VirtualName);
/*
* If we found an existing channel object - update it
*/
if (pChannel != NULL) {
IcaLockChannel(pChannel);
_IcaBindChannel(pChannel, Channel_Virtual, pSdVcBind->VirtualClass, pSdVcBind->Flags);
IcaUnlockChannel(pChannel);
IcaDereferenceChannel(pChannel);
}
}
}
PostLockConnection:
IcaUnlockConnection(pConnect);
return Status;
}
VOID IcaRebindVirtualChannels(IN PICA_CONNECTION pConnect)
{
PLIST_ENTRY Head, Next;
PICA_VCBIND pVcBind;
PICA_CHANNEL pChannel;
Head = &pConnect->VcBindHead;
for (Next = Head->Flink; Next != Head; Next = Next->Flink) {
pVcBind = CONTAINING_RECORD(Next, ICA_VCBIND, Links);
/*
* Locate channel object
*/
pChannel = IcaFindChannelByName(pConnect, Channel_Virtual,
pVcBind->VirtualName);
/*
* If we found an existing channel object - update it
*/
if (pChannel != NULL) {
IcaLockChannel(pChannel);
_IcaBindChannel(pChannel, Channel_Virtual, pVcBind->VirtualClass, pVcBind->Flags);
IcaUnlockChannel(pChannel);
IcaDereferenceChannel(pChannel);
}
}
}
VOID IcaUnbindVirtualChannels(IN PICA_CONNECTION pConnect)
{
PLIST_ENTRY Head, Next;
PICA_CHANNEL pChannel;
KIRQL oldIrql;
/*
* Loop through the channel list and clear the virtual class
* for all virtual channels. Also remove the channel pointer
* from the channel pointers array in the connection object.
*/
IcaLockChannelTable(&pConnect->ChannelTableLock);
Head = &pConnect->ChannelHead;
for (Next = Head->Flink; Next != Head; Next = Next->Flink) {
pChannel = CONTAINING_RECORD(Next, ICA_CHANNEL, Links);
if (pChannel->ChannelClass == Channel_Virtual &&
pChannel->VirtualClass != UNBOUND_CHANNEL) {
pConnect->pChannel[pChannel->ChannelClass +
pChannel->VirtualClass] = NULL;
pChannel->VirtualClass = UNBOUND_CHANNEL;
}
}
IcaUnlockChannelTable(&pConnect->ChannelTableLock);
}
NTSTATUS IcaUnbindVirtualChannel(
IN PICA_CONNECTION pConnect,
IN PVIRTUALCHANNELNAME pVirtualName)
{
PLIST_ENTRY Head, Next;
PICA_CHANNEL pChannel;
PICA_VCBIND pVcBind;
KIRQL oldIrql;
/*
* Loop through the channel list and clear the virtual class
* for the matching virtual channel. Also remove the channel pointer
* from the channel pointers array in the connection object.
*/
IcaLockChannelTable(&pConnect->ChannelTableLock);
Head = &pConnect->ChannelHead;
for (Next = Head->Flink; Next != Head; Next = Next->Flink) {
pChannel = CONTAINING_RECORD(Next, ICA_CHANNEL, Links);
if (pChannel->ChannelClass == Channel_Virtual &&
pChannel->VirtualClass != UNBOUND_CHANNEL &&
!_stricmp( pChannel->VirtualName, pVirtualName)) {
pConnect->pChannel[pChannel->ChannelClass +
pChannel->VirtualClass] = NULL;
pChannel->VirtualClass = UNBOUND_CHANNEL;
break;
}
}
Head = &pConnect->VcBindHead;
for (Next = Head->Flink; Next != Head; Next = Next->Flink) {
pVcBind = CONTAINING_RECORD( Next, ICA_VCBIND, Links );
if (!_stricmp(pVcBind->VirtualName, pVirtualName)) {
RemoveEntryList( &pVcBind->Links );
ICA_FREE_POOL(pVcBind);
IcaUnlockChannelTable(&pConnect->ChannelTableLock);
return STATUS_SUCCESS;
}
}
IcaUnlockChannelTable(&pConnect->ChannelTableLock);
return STATUS_OBJECT_NAME_NOT_FOUND;
}
PICA_CHANNEL _IcaAllocateChannel(
IN PICA_CONNECTION pConnect,
IN CHANNELCLASS ChannelClass,
IN PVIRTUALCHANNELNAME pVirtualName)
{
PICA_CHANNEL pChannel;
VIRTUALCHANNELCLASS VirtualClass;
KIRQL oldIrql;
NTSTATUS Status;
ULONG Flags;
ASSERT(ExIsResourceAcquiredExclusiveLite(&pConnect->Resource));
pChannel = ICA_ALLOCATE_POOL(NonPagedPool, sizeof(*pChannel));
if (pChannel == NULL)
return( NULL );
TRACE((pConnect, TC_ICADD, TT_API2,
"TermDD: _IcaAllocateChannel: cc %u, vn %s, %x\n",
ChannelClass, pVirtualName, pChannel));
RtlZeroMemory(pChannel, sizeof(*pChannel));
/*
* Reference the connection object this channel belongs to
*/
IcaReferenceConnection(pConnect);
pChannel->pConnect = pConnect;
pChannel->pChannelTableLock = &pConnect->ChannelTableLock;
/*
* Initialize channel reference count to 1;
* for the file object reference that will be made by the caller.
*/
pChannel->RefCount = 1;
pChannel->CompletionRoutineCount = 0;
/*
* Initialize the rest of the channel object for non-zero values.
*/
pChannel->Header.Type = IcaType_Channel;
pChannel->Header.pDispatchTable = IcaChannelDispatchTable;
ExInitializeResourceLite(&pChannel->Resource);
InitializeListHead(&pChannel->InputIrpHead);
InitializeListHead(&pChannel->InputBufHead);
IcaLockChannel(pChannel);
if (ChannelClass == Channel_Virtual) {
strncpy(pChannel->VirtualName, pVirtualName, VIRTUALCHANNELNAME_LENGTH);
VirtualClass = _IcaFindVcBind(pConnect, pVirtualName, &Flags);
} else {
VirtualClass = 0;
Flags = 0;
}
_IcaBindChannel(pChannel, ChannelClass, VirtualClass, Flags);
/*
* Link channel object to connect object
*/
IcaLockChannelTable(&pConnect->ChannelTableLock);
InsertHeadList(&pConnect->ChannelHead, &pChannel->Links);
IcaUnlockChannelTable(&pConnect->ChannelTableLock);
/*
* Set channel type specific flags/fields
* (i.e. shadow I/O is implicitly enabled for the video, beep,
* and command channels; the command and all virtual channels
* are message mode channels)
* Also sets throttling values taken from registry (if appropriate,
* plus remember a zeromem done to channel struct above).
*/
switch (ChannelClass) {
case Channel_Keyboard:
pChannel->InputBufMaxSize = SysParams.KeyboardThrottleSize;
break;
case Channel_Mouse :
pChannel->InputBufMaxSize = SysParams.MouseThrottleSize;
break;
case Channel_Video :
case Channel_Beep :
pChannel->Flags |= CHANNEL_SHADOW_IO;
break;
case Channel_Command :
pChannel->Flags |= CHANNEL_SHADOW_IO;
/* fall through */
case Channel_Virtual :
pChannel->Flags |= CHANNEL_MESSAGE_MODE;
if (!_stricmp( pVirtualName, VIRTUAL_THINWIRE)) {
pChannel->Flags |= CHANNEL_SCREENDATA;
}
break;
}
// Per above assert, this function is assumed to be called while the
// connection lock is held.
IcaUnlockChannel(pChannel);
return pChannel;
}
void _IcaFreeChannel(IN PICA_CHANNEL pChannel)
{
KIRQL oldIrql;
ASSERT(pChannel->RefCount == 0);
ASSERT(IsListEmpty(&pChannel->InputIrpHead));
ASSERT(IsListEmpty(&pChannel->InputBufHead));
ASSERT(!ExIsResourceAcquiredExclusiveLite(&pChannel->Resource));
TRACECHANNEL((pChannel, TC_ICADD, TT_API2,
"TermDD: _IcaFreeChannel: cc %u, vn %s, \n",
pChannel->ChannelClass, pChannel->VirtualName));
/*
* Unlink this channel from the channel list for this connection.
* this routine must be called with channel table lock held.
*/
RemoveEntryList(&pChannel->Links);
if (pChannel->VirtualClass != UNBOUND_CHANNEL) {
pChannel->pConnect->pChannel[pChannel->ChannelClass + pChannel->VirtualClass] = NULL;
}
ExDeleteResourceLite(&pChannel->Resource);
ICA_FREE_POOL(pChannel);
}
NTSTATUS _IcaRegisterVcBind(
IN PICA_CONNECTION pConnect,
IN PVIRTUALCHANNELNAME pVirtualName,
IN VIRTUALCHANNELCLASS VirtualClass,
IN ULONG Flags)
{
PICA_VCBIND pVcBind;
NTSTATUS Status;
ASSERT(ExIsResourceAcquiredExclusiveLite(&pConnect->Resource));
TRACE((pConnect, TC_ICADD, TT_API2,
"TermDD: _IcaRegisterVcBind: %s -> %d\n",
pVirtualName, VirtualClass));
/*
* Allocate bind structure
*/
pVcBind = ICA_ALLOCATE_POOL( NonPagedPool, sizeof(*pVcBind) );
if (pVcBind != NULL) {
/*
* Initialize structure
*/
RtlZeroMemory(pVcBind, sizeof(*pVcBind));
strncpy(pVcBind->VirtualName, pVirtualName, VIRTUALCHANNELNAME_LENGTH);
pVcBind->VirtualClass = VirtualClass;
pVcBind->Flags = Flags;
/*
* Link bind structure to connect object
*/
InsertHeadList(&pConnect->VcBindHead, &pVcBind->Links);
return STATUS_SUCCESS;
}
else {
return STATUS_INSUFFICIENT_RESOURCES;
}
}
VOID IcaFreeAllVcBind(IN PICA_CONNECTION pConnect)
{
PICA_VCBIND pVcBind;
PLIST_ENTRY Head;
TRACE(( pConnect, TC_ICADD, TT_API2, "TermDD: IcaFreeAllVcBind\n" ));
/*
* Free all bind structures
*/
while ( !IsListEmpty( &pConnect->VcBindHead ) ) {
Head = RemoveHeadList( &pConnect->VcBindHead );
pVcBind = CONTAINING_RECORD( Head, ICA_VCBIND, Links );
ICA_FREE_POOL( pVcBind );
}
}
VIRTUALCHANNELCLASS _IcaFindVcBind(
IN PICA_CONNECTION pConnect,
IN PVIRTUALCHANNELNAME pVirtualName,
OUT PULONG pFlags)
{
PICA_VCBIND pVcBind;
PLIST_ENTRY Head, Next;
ASSERT( ExIsResourceAcquiredExclusiveLite( &pConnect->Resource ) );
/*
* Search the existing VC bind structures to locate virtual channel name
*/
Head = &pConnect->VcBindHead;
for (Next = Head->Flink; Next != Head; Next = Next->Flink) {
pVcBind = CONTAINING_RECORD(Next, ICA_VCBIND, Links);
if (!_stricmp(pVcBind->VirtualName, pVirtualName)) {
TRACE((pConnect, TC_ICADD, TT_API2,
"TermDD: _IcaFindVcBind: vn %s -> vc %d\n",
pVirtualName, pVcBind->VirtualClass));
*pFlags = pVcBind->Flags;
return pVcBind->VirtualClass;
}
}
/*
* If name does not exist, return UNBOUND_CHANNEL
*/
TRACE(( pConnect, TC_ICADD, TT_API2,
"TermDD: _IcaFindVcBind: vn %s (not found)\n", pVirtualName ));
return UNBOUND_CHANNEL;
}
VOID _IcaBindChannel(
IN PICA_CHANNEL pChannel,
IN CHANNELCLASS ChannelClass,
IN VIRTUALCHANNELCLASS VirtualClass,
IN ULONG Flags)
{
KIRQL oldIrql;
ASSERT(ExIsResourceAcquiredExclusiveLite(&pChannel->Resource));
TRACECHANNEL(( pChannel, TC_ICADD, TT_API2,
"TermDD: _IcaBindChannel: cc %u, vn %s vc %d\n",
ChannelClass, pChannel->VirtualName, VirtualClass ));
pChannel->ChannelClass = ChannelClass;
pChannel->VirtualClass = VirtualClass;
IcaLockChannelTable(pChannel->pChannelTableLock);
if (Flags & SD_CHANNEL_FLAG_SHADOW_PERSISTENT)
pChannel->Flags |= CHANNEL_SHADOW_PERSISTENT;
if (VirtualClass != UNBOUND_CHANNEL) {
ASSERT(pChannel->pConnect->pChannel[ChannelClass + VirtualClass] == NULL);
pChannel->pConnect->pChannel[ChannelClass + VirtualClass] = pChannel;
}
IcaUnlockChannelTable(pChannel->pChannelTableLock);
}
BOOLEAN IcaLockChannelTable(PERESOURCE pResource)
{
KIRQL oldIrql;
BOOLEAN Result;
/*
* lock the channel object
*/
KeEnterCriticalRegion(); // Disable APC calls when holding a resource.
Result = ExAcquireResourceExclusiveLite( pResource, TRUE );
return Result;
}
void IcaUnlockChannelTable(PERESOURCE pResource)
{
ExReleaseResourceLite(pResource);
KeLeaveCriticalRegion(); // Resume APC calls after releasing resource.
}
NTSTATUS IcaCancelReadChannel(
IN PICA_CHANNEL pChannel,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp)
{
KIRQL cancelIrql;
PLIST_ENTRY Head;
PIRP ReadIrp;
PINBUF pInBuf;
TRACECHANNEL((pChannel, TC_ICADD, TT_API2,
"TermDD: IcaCancelReadChannel, cc %u, vc %d\n",
pChannel->ChannelClass, pChannel->VirtualClass));
/*
* Lock channel while we clear out any
* pending read IRPs and/or input buffers.
*/
IcaLockChannel(pChannel);
/*
* Indicate that Reads are cancelled to this channel
*/
pChannel->Flags |= CHANNEL_CANCEL_READS;
IoAcquireCancelSpinLock( &cancelIrql );
while ( !IsListEmpty( &pChannel->InputIrpHead ) ) {
Head = pChannel->InputIrpHead.Flink;
ReadIrp = CONTAINING_RECORD( Head, IRP, Tail.Overlay.ListEntry );
ReadIrp->CancelIrql = cancelIrql;
IoSetCancelRoutine( ReadIrp, NULL );
_IcaReadChannelCancelIrp( IrpSp->DeviceObject, ReadIrp );
IoAcquireCancelSpinLock( &cancelIrql );
}
IoReleaseCancelSpinLock( cancelIrql );
IcaUnlockChannel(pChannel);
return STATUS_SUCCESS;
}