663 lines
16 KiB
C
663 lines
16 KiB
C
/*++
|
||
|
||
Copyright (c) 1989, 1990, 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
action.c
|
||
|
||
Abstract:
|
||
|
||
This module contains support for the TdiAction handler.
|
||
|
||
Author:
|
||
|
||
David Beaver (dbeaver) 2-July-1991
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
|
||
typedef struct _QUERY_INDICATION {
|
||
UCHAR Command;
|
||
USHORT Data2;
|
||
UCHAR DestinationName[16];
|
||
UCHAR SourceName[16];
|
||
} QUERY_INDICATION, *PQUERY_INDICATION;
|
||
|
||
typedef struct _ACTION_QUERY_INDICATION {
|
||
TDI_ACTION_HEADER Header;
|
||
QUERY_INDICATION QueryIndication;
|
||
} ACTION_QUERY_INDICATION, *PACTION_QUERY_INDICATION;
|
||
|
||
|
||
typedef struct _DATAGRAM_INDICATION {
|
||
UCHAR DestinationName[16];
|
||
UCHAR SourceName[16];
|
||
USHORT DatagramBufferLength;
|
||
UCHAR DatagramBuffer[1];
|
||
} DATAGRAM_INDICATION, *PDATAGRAM_INDICATION;
|
||
|
||
typedef struct _ACTION_DATAGRAM_INDICATION {
|
||
TDI_ACTION_HEADER Header;
|
||
DATAGRAM_INDICATION DatagramIndication;
|
||
} ACTION_DATAGRAM_INDICATION, *PACTION_DATAGRAM_INDICATION;
|
||
|
||
|
||
#define QUERY_INDICATION_CODE 1
|
||
#define DATAGRAM_INDICATION_CODE 2
|
||
|
||
|
||
|
||
VOID
|
||
NbfCancelAction(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
|
||
|
||
NTSTATUS
|
||
NbfTdiAction(
|
||
IN PDEVICE_CONTEXT DeviceContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine performs the TdiAction request for the transport
|
||
provider.
|
||
|
||
Arguments:
|
||
|
||
DeviceContext - The device context for the operation
|
||
|
||
Irp - the Irp for the requested operation.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
PIO_STACK_LOCATION irpSp;
|
||
PTDI_ACTION_HEADER ActionHeader;
|
||
LARGE_INTEGER timeout = {0,0};
|
||
PTP_REQUEST tpRequest;
|
||
KIRQL oldirql, cancelirql;
|
||
ULONG BytesRequired;
|
||
|
||
//
|
||
// what type of status do we want?
|
||
//
|
||
|
||
irpSp = IoGetCurrentIrpStackLocation (Irp);
|
||
|
||
if ((!Irp->MdlAddress) ||
|
||
(MmGetMdlByteCount(Irp->MdlAddress) < sizeof(TDI_ACTION_HEADER))) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
ActionHeader = (PTDI_ACTION_HEADER)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
|
||
|
||
if (!ActionHeader) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Make sure we have required number of bytes for this type of request
|
||
//
|
||
|
||
switch (ActionHeader->ActionCode) {
|
||
|
||
case QUERY_INDICATION_CODE:
|
||
BytesRequired = sizeof(ACTION_QUERY_INDICATION);
|
||
break;
|
||
|
||
case DATAGRAM_INDICATION_CODE:
|
||
BytesRequired = sizeof(ACTION_DATAGRAM_INDICATION);
|
||
break;
|
||
|
||
default:
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
if (MmGetMdlByteCount(Irp->MdlAddress) < BytesRequired) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Here the request is one of QUERY_INDICATION or DATAGRAM_INDICATION
|
||
//
|
||
|
||
//
|
||
// These two requests are sent by RAS to "MABF"
|
||
//
|
||
|
||
if (!RtlEqualMemory ((PVOID)(&ActionHeader->TransportId), "MABF", 4)) {
|
||
return STATUS_NOT_SUPPORTED;
|
||
}
|
||
|
||
//
|
||
// They should be sent on the control channel
|
||
//
|
||
|
||
if (irpSp->FileObject->FsContext2 != UlongToPtr(NBF_FILE_TYPE_CONTROL)) {
|
||
return STATUS_NOT_SUPPORTED;
|
||
}
|
||
|
||
|
||
//
|
||
// Create a request to describe this.
|
||
//
|
||
|
||
status = NbfCreateRequest (
|
||
Irp, // IRP for this request.
|
||
DeviceContext, // context.
|
||
REQUEST_FLAGS_DC, // partial flags.
|
||
Irp->MdlAddress,
|
||
MmGetMdlByteCount(Irp->MdlAddress),
|
||
timeout,
|
||
&tpRequest);
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
|
||
NbfReferenceDeviceContext ("Action", DeviceContext, DCREF_REQUEST);
|
||
tpRequest->Owner = DeviceContextType;
|
||
tpRequest->FrameContext = (USHORT)irpSp->FileObject->FsContext;
|
||
|
||
IoAcquireCancelSpinLock(&cancelirql);
|
||
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock,&oldirql);
|
||
|
||
//
|
||
// Disallow these requests on a stopping device.
|
||
//
|
||
|
||
if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) {
|
||
|
||
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock,oldirql);
|
||
IoReleaseCancelSpinLock(cancelirql);
|
||
NbfCompleteRequest (tpRequest, STATUS_DEVICE_NOT_READY, 0);
|
||
|
||
} else {
|
||
|
||
if (ActionHeader->ActionCode == QUERY_INDICATION_CODE) {
|
||
|
||
InsertTailList (
|
||
&DeviceContext->QueryIndicationQueue,
|
||
&tpRequest->Linkage);
|
||
|
||
} else {
|
||
|
||
InsertTailList (
|
||
&DeviceContext->DatagramIndicationQueue,
|
||
&tpRequest->Linkage);
|
||
|
||
}
|
||
|
||
DeviceContext->IndicationQueuesInUse = TRUE;
|
||
|
||
|
||
//
|
||
// If this IRP has been cancelled, then call the
|
||
// cancel routine.
|
||
//
|
||
|
||
if (Irp->Cancel) {
|
||
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock,oldirql);
|
||
Irp->CancelIrql = cancelirql;
|
||
NbfCancelAction((PDEVICE_OBJECT)DeviceContext, Irp);
|
||
return STATUS_PENDING;
|
||
}
|
||
|
||
IoSetCancelRoutine(Irp, NbfCancelAction);
|
||
|
||
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock,oldirql);
|
||
IoReleaseCancelSpinLock(cancelirql);
|
||
|
||
}
|
||
|
||
status = STATUS_PENDING;
|
||
|
||
}
|
||
|
||
return status;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
NbfCancelAction(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by the I/O system to cancel an Action.
|
||
What is done to cancel it is specific to each action.
|
||
|
||
NOTE: This routine is called with the CancelSpinLock held and
|
||
is responsible for releasing it.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to the device object for this driver.
|
||
|
||
Irp - Pointer to the request packet representing the I/O request.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldirql;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
PTP_REQUEST Request;
|
||
PLIST_ENTRY p;
|
||
BOOLEAN Found;
|
||
PTDI_ACTION_HEADER ActionHeader;
|
||
PLIST_ENTRY QueueHead, QueueEnd;
|
||
|
||
PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)DeviceObject;
|
||
|
||
//
|
||
// Get a pointer to the current stack location in the IRP. This is where
|
||
// the function codes and parameters are stored.
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation (Irp);
|
||
|
||
ASSERT ((IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) &&
|
||
(IrpSp->MinorFunction == TDI_ACTION));
|
||
|
||
ActionHeader = (PTDI_ACTION_HEADER)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
|
||
|
||
if (!ActionHeader) {
|
||
return;
|
||
}
|
||
|
||
switch (ActionHeader->ActionCode) {
|
||
|
||
case QUERY_INDICATION_CODE:
|
||
case DATAGRAM_INDICATION_CODE:
|
||
|
||
//
|
||
// Scan through the appropriate queue, looking for this IRP.
|
||
// If we find it, we just remove it from the queue; there
|
||
// is nothing else involved in cancelling.
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
|
||
|
||
if (ActionHeader->ActionCode == QUERY_INDICATION_CODE) {
|
||
QueueHead = DeviceContext->QueryIndicationQueue.Flink;
|
||
QueueEnd = &DeviceContext->QueryIndicationQueue;
|
||
} else {
|
||
QueueHead = DeviceContext->DatagramIndicationQueue.Flink;
|
||
QueueEnd = &DeviceContext->DatagramIndicationQueue;
|
||
}
|
||
|
||
Found = FALSE;
|
||
for (p = QueueHead; p != QueueEnd; p = p->Flink) {
|
||
|
||
Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
|
||
if (Request->IoRequestPacket == Irp) {
|
||
|
||
//
|
||
// Found it, remove it from the list here.
|
||
//
|
||
|
||
RemoveEntryList (p);
|
||
|
||
Found = TRUE;
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
|
||
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
||
|
||
if (Found) {
|
||
|
||
NbfCompleteRequest (Request, STATUS_CANCELLED, 0);
|
||
|
||
} else {
|
||
|
||
#if DBG
|
||
DbgPrint("NBF: Tried to cancel action %lx on %lx, not found\n",
|
||
Irp, DeviceContext);
|
||
#endif
|
||
}
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
||
break;
|
||
|
||
}
|
||
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
NbfStopControlChannel(
|
||
IN PDEVICE_CONTEXT DeviceContext,
|
||
IN USHORT ChannelIdentifier
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when an MJ_CLEANUP IRP is received
|
||
on a control channel. It walks the device context's list of
|
||
pending action requests and cancels those associated with
|
||
this channel (as identified by ChannelIdentifier.
|
||
|
||
Arguments:
|
||
|
||
DeviceContext - Pointer to our device context.
|
||
|
||
ChannelIdentifier - The identifier for this open of the control
|
||
channel, which is stored in Request->FrameContext for requests
|
||
made on this channel.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
KIRQL oldirql, cancelirql;
|
||
PTP_REQUEST Request;
|
||
PLIST_ENTRY p;
|
||
UINT i;
|
||
BOOLEAN FoundRequest;
|
||
PLIST_ENTRY QueueHead, QueueEnd;
|
||
|
||
|
||
//
|
||
// Scan both queues, looking for requests. Since the list
|
||
// may change, we scan until we find one, then remove it
|
||
// and complete it. We then start scanning at the beginning
|
||
// again. We continue until we find none on the queue that
|
||
// belong to this control channel.
|
||
//
|
||
// The outer loop only runs twice; the first time it
|
||
// processes QueryIndicationQueue, the second time
|
||
// DatagramIndicationQueue.
|
||
//
|
||
|
||
for (i = 0; i < 2; i++) {
|
||
|
||
do {
|
||
|
||
//
|
||
// Loop until we do not find a request on this
|
||
// pass through the queue.
|
||
//
|
||
|
||
FoundRequest = FALSE;
|
||
|
||
IoAcquireCancelSpinLock(&cancelirql);
|
||
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
|
||
|
||
if (i == 0) {
|
||
QueueHead = DeviceContext->QueryIndicationQueue.Flink;
|
||
QueueEnd = &DeviceContext->QueryIndicationQueue;
|
||
} else {
|
||
QueueHead = DeviceContext->DatagramIndicationQueue.Flink;
|
||
QueueEnd = &DeviceContext->DatagramIndicationQueue;
|
||
}
|
||
|
||
|
||
//
|
||
// Scan the appropriate queue for a request on this
|
||
// channel.
|
||
//
|
||
|
||
for (p = QueueHead; p != QueueEnd; p = p->Flink) {
|
||
|
||
Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
|
||
if (Request->FrameContext == ChannelIdentifier) {
|
||
|
||
//
|
||
// Found it, remove it from the list here.
|
||
//
|
||
|
||
IoSetCancelRoutine(Request->IoRequestPacket, NULL);
|
||
RemoveEntryList (p);
|
||
|
||
FoundRequest = TRUE;
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
|
||
IoReleaseCancelSpinLock(cancelirql);
|
||
|
||
//
|
||
// If we found a request, then complete it and loop
|
||
// back to the top of the while loop to rescan the
|
||
// list. If not, then we will exit the while loop
|
||
// now.
|
||
//
|
||
|
||
if (FoundRequest) {
|
||
|
||
NbfCompleteRequest (Request, STATUS_CANCELLED, 0);
|
||
|
||
}
|
||
|
||
} while (FoundRequest);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
NbfActionQueryIndication(
|
||
IN PDEVICE_CONTEXT DeviceContext,
|
||
IN PNBF_HDR_CONNECTIONLESS UiFrame
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called after a UI frame of type NAME_QUERY,
|
||
ADD_NAME_QUERY, or ADD_GROUP_NAME_QUERY has been processed.
|
||
It checks if there is a QUERY.INDICATION IRP waiting to
|
||
be completed, and if so completes it.
|
||
|
||
Arguments:
|
||
|
||
DeviceContext - Pointer to our device context.
|
||
|
||
UiFrame - Pointer to the incoming frame. The first byte of
|
||
information is the first byte of the NetBIOS connectionless
|
||
header.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldirql, cancelirql;
|
||
PTP_REQUEST Request;
|
||
PLIST_ENTRY p;
|
||
PMDL Mdl;
|
||
PACTION_QUERY_INDICATION ActionHeader;
|
||
PQUERY_INDICATION QueryIndication;
|
||
|
||
|
||
IoAcquireCancelSpinLock (&cancelirql);
|
||
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
|
||
|
||
if (!IsListEmpty (&DeviceContext->QueryIndicationQueue)) {
|
||
|
||
p = RemoveHeadList (&DeviceContext->QueryIndicationQueue);
|
||
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
|
||
|
||
Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
|
||
IoSetCancelRoutine(Request->IoRequestPacket,NULL);
|
||
IoReleaseCancelSpinLock(cancelirql);
|
||
|
||
Mdl = Request->Buffer2;
|
||
ActionHeader = (PACTION_QUERY_INDICATION)
|
||
(MmGetSystemAddressForMdl(Mdl));
|
||
QueryIndication = &ActionHeader->QueryIndication;
|
||
|
||
//
|
||
// Copy over data from frame (note that dest and source
|
||
// address are copied with one call).
|
||
//
|
||
|
||
QueryIndication->Command = UiFrame->Command;
|
||
RtlCopyMemory ((PUCHAR)(&QueryIndication->Data2), (PUCHAR)(&UiFrame->Data2Low), 2);
|
||
RtlCopyMemory ((PUCHAR)(QueryIndication->DestinationName),
|
||
(PUCHAR)(UiFrame->DestinationName),
|
||
2 * NETBIOS_NAME_LENGTH);
|
||
|
||
NbfCompleteRequest (Request, STATUS_SUCCESS, sizeof(ACTION_QUERY_INDICATION));
|
||
|
||
} else {
|
||
|
||
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
|
||
IoReleaseCancelSpinLock(cancelirql);
|
||
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
NbfActionDatagramIndication(
|
||
IN PDEVICE_CONTEXT DeviceContext,
|
||
IN PNBF_HDR_CONNECTIONLESS UiFrame,
|
||
IN ULONG Length
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called after a datagram frame has been
|
||
received. It checks if there is a DATAGRAM.INDICATION IRP
|
||
waiting to be completed, and if so completes it.
|
||
|
||
Arguments:
|
||
|
||
DeviceContext - Pointer to our device context.
|
||
|
||
UiFrame - Pointer to the incoming frame. The first byte of
|
||
information is the first byte of the NetBIOS connectionless
|
||
header.
|
||
|
||
Length - The length of the frame starting at UiFrame.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldirql, cancelirql;
|
||
PTP_REQUEST Request;
|
||
PLIST_ENTRY p;
|
||
PACTION_DATAGRAM_INDICATION ActionHeader;
|
||
PDATAGRAM_INDICATION DatagramIndication;
|
||
ULONG CopyLength;
|
||
PMDL Mdl;
|
||
NTSTATUS Status;
|
||
|
||
|
||
IoAcquireCancelSpinLock (&cancelirql);
|
||
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
|
||
|
||
if (!IsListEmpty (&DeviceContext->DatagramIndicationQueue)) {
|
||
|
||
p = RemoveHeadList (&DeviceContext->DatagramIndicationQueue);
|
||
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
|
||
|
||
Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
|
||
IoSetCancelRoutine(Request->IoRequestPacket, NULL);
|
||
IoReleaseCancelSpinLock(cancelirql);
|
||
|
||
Mdl = Request->Buffer2;
|
||
ActionHeader = (PACTION_DATAGRAM_INDICATION)
|
||
(MmGetSystemAddressForMdl(Mdl));
|
||
DatagramIndication = &ActionHeader->DatagramIndication;
|
||
|
||
//
|
||
// Copy over data from frame (note that dest and source
|
||
// address are copied with one call).
|
||
//
|
||
|
||
RtlCopyMemory ((PUCHAR)(DatagramIndication->DestinationName),
|
||
(PUCHAR)(UiFrame->DestinationName),
|
||
2 * NETBIOS_NAME_LENGTH);
|
||
|
||
if ((Length-sizeof(NBF_HDR_CONNECTIONLESS)) <=
|
||
(ULONG)DatagramIndication->DatagramBufferLength) {
|
||
|
||
CopyLength = Length - sizeof(NBF_HDR_CONNECTIONLESS);
|
||
Status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
CopyLength = DatagramIndication->DatagramBufferLength;
|
||
Status = STATUS_BUFFER_OVERFLOW;
|
||
|
||
}
|
||
|
||
|
||
RtlCopyMemory(
|
||
(PUCHAR)DatagramIndication->DatagramBuffer,
|
||
((PUCHAR)UiFrame) + sizeof(NBF_HDR_CONNECTIONLESS),
|
||
CopyLength);
|
||
DatagramIndication->DatagramBufferLength = (USHORT)CopyLength;
|
||
|
||
NbfCompleteRequest (Request, Status, CopyLength +
|
||
FIELD_OFFSET (ACTION_DATAGRAM_INDICATION, DatagramIndication.DatagramBuffer[0]));
|
||
|
||
} else {
|
||
|
||
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
|
||
IoReleaseCancelSpinLock(cancelirql);
|
||
|
||
}
|
||
}
|
||
|