mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1476 lines
40 KiB
1476 lines
40 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
mask.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the code that is very specific to open
|
|
and close operations in the modem driver
|
|
|
|
Author:
|
|
|
|
Anthony V. Ercolano 13-Aug-1995
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History :
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
|
|
NTSTATUS
|
|
UniMaskStarter(
|
|
IN PDEVICE_EXTENSION Extension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deal with managing initiating mask operations.
|
|
|
|
Arguments:
|
|
|
|
Extension - The modem device extension.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status of the call
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS firstStatus;
|
|
NTSTATUS status;
|
|
BOOLEAN setFirstStatus = FALSE;
|
|
PIRP newIrp;
|
|
|
|
do {
|
|
|
|
PIO_STACK_LOCATION irpSp =
|
|
IoGetCurrentIrpStackLocation(Extension->CurrentMaskOp);
|
|
PIO_STACK_LOCATION nextSp =
|
|
IoGetNextIrpStackLocation(Extension->CurrentMaskOp);
|
|
PULONG origMask =
|
|
(PULONG)Extension->CurrentMaskOp->AssociatedIrp.SystemBuffer;
|
|
KIRQL origIrql;
|
|
ULONG dcdMask;
|
|
int ownerHandle = irpSp->FileObject->FsContext?CONTROL_HANDLE:CLIENT_HANDLE;
|
|
PMASKSTATE thisMaskState = &Extension->MaskStates[ownerHandle];
|
|
PMASKSTATE otherMaskState = thisMaskState->OtherState;
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_SERIAL_SET_WAIT_MASK) {
|
|
|
|
//
|
|
// First make sure that the mask operation we have is well
|
|
// formed. (The params are ok.)
|
|
//
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(ULONG)) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
Extension->CurrentMaskOp->IoStatus.Status =
|
|
STATUS_BUFFER_TOO_SMALL;
|
|
Extension->CurrentMaskOp->IoStatus.Information = 0L;
|
|
if (!setFirstStatus) {
|
|
setFirstStatus = TRUE;
|
|
firstStatus = status;
|
|
}
|
|
|
|
UniGetNextIrp(
|
|
&Extension->DeviceLock,
|
|
&Extension->CurrentMaskOp,
|
|
&Extension->MaskOps,
|
|
&newIrp,
|
|
TRUE
|
|
);
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// Copy our location information so that the lower
|
|
// level serial driver performs the mask op.
|
|
//
|
|
|
|
nextSp->MajorFunction = irpSp->MajorFunction;
|
|
nextSp->MinorFunction = irpSp->MinorFunction;
|
|
nextSp->Flags = irpSp->Flags;
|
|
nextSp->Parameters = irpSp->Parameters;
|
|
|
|
//
|
|
// Setup so that upon the lower level serial drivers completion
|
|
// we decrement the reference counts and such.
|
|
//
|
|
|
|
IoSetCompletionRoutine(
|
|
Extension->CurrentMaskOp,
|
|
UniGeneralMaskComplete,
|
|
thisMaskState,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
//
|
|
// Save off the actual value of our mask data into
|
|
// argument three of our own stack location. We know
|
|
// that we don't use that memory for anything. We
|
|
// recover it later so that we always know what events
|
|
// this handle is interested in.
|
|
//
|
|
|
|
UNI_SAVE_OLD_SETMASK(Extension->CurrentMaskOp);
|
|
|
|
//
|
|
// Set it up so that lower level serial driver has the
|
|
// client, owner, and DCD (if necessary bits) events.
|
|
//
|
|
|
|
KeAcquireSpinLock(
|
|
&Extension->DeviceLock,
|
|
&origIrql
|
|
);
|
|
|
|
*origMask |= (otherMaskState->Mask |
|
|
((Extension->PassThrough == MODEM_DCDSNIFF)?
|
|
(SERIAL_EV_RLSD | SERIAL_EV_DSR):
|
|
(0)
|
|
));
|
|
|
|
//
|
|
// Increment another reference count in that counts
|
|
// the number of setmasks have been sent down to the
|
|
// lower serial drivr. These will be decremented when
|
|
// the setmask operation completes.
|
|
//
|
|
|
|
thisMaskState->SentDownSetMasks++;
|
|
|
|
//
|
|
// Check to see if we have a shuttled aside wait mask
|
|
// for ourselves (client or owner). If so, complete it
|
|
// before we go on with processing the actual setmask.
|
|
//
|
|
|
|
if (thisMaskState->ShuttledWait) {
|
|
|
|
PIRP savedIrp = thisMaskState->ShuttledWait;
|
|
|
|
thisMaskState->ShuttledWait = NULL;
|
|
|
|
UniRundownShuttledWait(
|
|
Extension,
|
|
&thisMaskState->ShuttledWait,
|
|
UNI_REFERENCE_NORMAL_PATH,
|
|
savedIrp,
|
|
origIrql,
|
|
STATUS_SUCCESS,
|
|
0ul
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// If we don't have a shuttled wait, we might
|
|
// have a passed down wait. If we do then
|
|
// mark it to complete.
|
|
//
|
|
|
|
if (thisMaskState->PassedDownWait) {
|
|
|
|
thisMaskState->CompletePassedDownWait = TRUE;
|
|
|
|
}
|
|
|
|
KeReleaseSpinLock(
|
|
&Extension->DeviceLock,
|
|
origIrql
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Off to the lower serial driver.
|
|
//
|
|
|
|
status = IoCallDriver(
|
|
Extension->AttachedDeviceObject,
|
|
Extension->CurrentMaskOp
|
|
);
|
|
|
|
if (!setFirstStatus) {
|
|
setFirstStatus = TRUE;
|
|
firstStatus = status;
|
|
}
|
|
|
|
UniGetNextIrp(
|
|
&Extension->DeviceLock,
|
|
&Extension->CurrentMaskOp,
|
|
&Extension->MaskOps,
|
|
&newIrp,
|
|
FALSE
|
|
);
|
|
continue;
|
|
|
|
} else {
|
|
|
|
//
|
|
// It wasn't a setmask. So it must be a wait.
|
|
//
|
|
// Verify that it is well formed. We really should
|
|
// do this here because it may never make it down
|
|
// to the lower level serial driver.
|
|
//
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(ULONG)) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
Extension->CurrentMaskOp->IoStatus.Status =
|
|
STATUS_BUFFER_TOO_SMALL;
|
|
Extension->CurrentMaskOp->IoStatus.Information = 0L;
|
|
if (!setFirstStatus) {
|
|
setFirstStatus = TRUE;
|
|
firstStatus = status;
|
|
}
|
|
|
|
UniGetNextIrp(
|
|
&Extension->DeviceLock,
|
|
&Extension->CurrentMaskOp,
|
|
&Extension->MaskOps,
|
|
&newIrp,
|
|
TRUE
|
|
);
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// Make sure that we aren't trying to start a wait
|
|
// when the setmask for this handle is zero. Note
|
|
// that if this is the owner handle, we need to
|
|
// or in the dcd value if dcd sniffing is on.
|
|
//
|
|
|
|
if ((ownerHandle &&
|
|
(!thisMaskState->Mask &&
|
|
(Extension->PassThrough != MODEM_DCDSNIFF)
|
|
)
|
|
) ||
|
|
(!ownerHandle && !thisMaskState->Mask)) {
|
|
|
|
Extension->CurrentMaskOp->IoStatus.Status =
|
|
STATUS_INVALID_PARAMETER;
|
|
Extension->CurrentMaskOp->IoStatus.Information = 0L;
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
if (!setFirstStatus) {
|
|
setFirstStatus = TRUE;
|
|
firstStatus = status;
|
|
}
|
|
|
|
UniGetNextIrp(
|
|
&Extension->DeviceLock,
|
|
&Extension->CurrentMaskOp,
|
|
&Extension->MaskOps,
|
|
&newIrp,
|
|
TRUE
|
|
);
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// If there is already a wait around for this handle
|
|
// (either shuttled or actually down waiting) then
|
|
// this wait fails.
|
|
//
|
|
// At this point we need to take out the lock to
|
|
// prevent anything from moving on.
|
|
//
|
|
|
|
KeAcquireSpinLock(
|
|
&Extension->DeviceLock,
|
|
&origIrql
|
|
);
|
|
|
|
if (thisMaskState->ShuttledWait || thisMaskState->PassedDownWait) {
|
|
|
|
KeReleaseSpinLock(
|
|
&Extension->DeviceLock,
|
|
origIrql
|
|
);
|
|
|
|
Extension->CurrentMaskOp->IoStatus.Status =
|
|
STATUS_INVALID_PARAMETER;
|
|
Extension->CurrentMaskOp->IoStatus.Information = 0L;
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
if (!setFirstStatus) {
|
|
setFirstStatus = TRUE;
|
|
firstStatus = status;
|
|
}
|
|
|
|
UniGetNextIrp(
|
|
&Extension->DeviceLock,
|
|
&Extension->CurrentMaskOp,
|
|
&Extension->MaskOps,
|
|
&newIrp,
|
|
TRUE
|
|
);
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// See if this wait can be satisfied with the last set of
|
|
// events that we saw.
|
|
//
|
|
|
|
if (thisMaskState->HistoryMask) {
|
|
|
|
|
|
PULONG maskValue =
|
|
Extension->CurrentMaskOp->AssociatedIrp.SystemBuffer;
|
|
//
|
|
// A non-zero history mask implies we have something
|
|
// that will statisfy this wait.
|
|
//
|
|
|
|
Extension->CurrentMaskOp->IoStatus.Status = STATUS_SUCCESS;
|
|
Extension->CurrentMaskOp->IoStatus.Information =
|
|
sizeof(ULONG);
|
|
*maskValue = thisMaskState->HistoryMask;
|
|
thisMaskState->HistoryMask = 0UL;
|
|
|
|
KeReleaseSpinLock(
|
|
&Extension->DeviceLock,
|
|
origIrql
|
|
);
|
|
status = STATUS_SUCCESS;
|
|
if (!setFirstStatus) {
|
|
setFirstStatus = TRUE;
|
|
firstStatus = status;
|
|
}
|
|
|
|
UniGetNextIrp(
|
|
&Extension->DeviceLock,
|
|
&Extension->CurrentMaskOp,
|
|
&Extension->MaskOps,
|
|
&newIrp,
|
|
TRUE
|
|
);
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// If the reference counts for our handle (client or
|
|
// owner) indicate more setmasks, then complete it
|
|
// right away since it won't get very far any way.
|
|
//
|
|
|
|
if (thisMaskState->SentDownSetMasks <
|
|
thisMaskState->SetMaskCount) {
|
|
|
|
PULONG maskValue =
|
|
Extension->CurrentMaskOp->AssociatedIrp.SystemBuffer;
|
|
|
|
Extension->CurrentMaskOp->IoStatus.Status = STATUS_SUCCESS;
|
|
Extension->CurrentMaskOp->IoStatus.Information =
|
|
sizeof(ULONG);
|
|
*maskValue = 0UL;
|
|
|
|
KeReleaseSpinLock(
|
|
&Extension->DeviceLock,
|
|
origIrql
|
|
);
|
|
status = STATUS_SUCCESS;
|
|
if (!setFirstStatus) {
|
|
setFirstStatus = TRUE;
|
|
firstStatus = status;
|
|
}
|
|
|
|
UniGetNextIrp(
|
|
&Extension->DeviceLock,
|
|
&Extension->CurrentMaskOp,
|
|
&Extension->MaskOps,
|
|
&newIrp,
|
|
TRUE
|
|
);
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// If the complementry handle already has a wait pending
|
|
// (or because of DCD sniffing)
|
|
// then shuttle this wait off to the side.
|
|
//
|
|
|
|
if (otherMaskState->PassedDownWait ||
|
|
(Extension->PassThrough == MODEM_DCDSNIFF)) {
|
|
|
|
status = UniMakeIrpShuttledWait(
|
|
thisMaskState,
|
|
Extension->CurrentMaskOp,
|
|
origIrql,
|
|
TRUE,
|
|
&newIrp
|
|
);
|
|
|
|
if (!setFirstStatus) {
|
|
|
|
setFirstStatus = TRUE;
|
|
firstStatus = status;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// There was no other wait pendings so send this one down.
|
|
//
|
|
// We want to set the completion routine so that we
|
|
// can shuttle it aside if is pushed out by DCD sniff
|
|
// or a setmask from the other handle.
|
|
//
|
|
|
|
nextSp->MajorFunction = irpSp->MajorFunction;
|
|
nextSp->MinorFunction = irpSp->MinorFunction;
|
|
nextSp->Flags = irpSp->Flags;
|
|
nextSp->Parameters.DeviceIoControl.OutputBufferLength =
|
|
irpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
|
nextSp->Parameters.DeviceIoControl.IoControlCode =
|
|
IOCTL_SERIAL_WAIT_ON_MASK;
|
|
nextSp->Parameters.DeviceIoControl.Type3InputBuffer =
|
|
irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
|
|
//
|
|
// Setup so that upon the lower level serial drivers completion
|
|
// we decrement the reference count and such.
|
|
//
|
|
|
|
IoSetCompletionRoutine(
|
|
Extension->CurrentMaskOp,
|
|
UniGeneralWaitComplete,
|
|
thisMaskState,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
thisMaskState->PassedDownWait = Extension->CurrentMaskOp;
|
|
thisMaskState->CompletePassedDownWait = FALSE;
|
|
|
|
KeReleaseSpinLock(
|
|
&Extension->DeviceLock,
|
|
origIrql
|
|
);
|
|
status = IoCallDriver(
|
|
Extension->AttachedDeviceObject,
|
|
Extension->CurrentMaskOp
|
|
);
|
|
if (!setFirstStatus) {
|
|
setFirstStatus = TRUE;
|
|
firstStatus = status;
|
|
}
|
|
|
|
UniGetNextIrp(
|
|
&Extension->DeviceLock,
|
|
&Extension->CurrentMaskOp,
|
|
&Extension->MaskOps,
|
|
&newIrp,
|
|
FALSE
|
|
);
|
|
continue;
|
|
|
|
}
|
|
|
|
} while (newIrp);
|
|
|
|
return firstStatus;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
UniGeneralWaitComplete(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deal with finishing off the wait function
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object for the modem
|
|
|
|
Irp - The irp being completed.
|
|
|
|
Context - Points to the mask state for the client or control handle
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status of the call
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// This completion routine is invoked when a wait function was
|
|
// actually down in a lower level serial driver and for one
|
|
// reason or another has been completed.
|
|
//
|
|
// A wait will be completed by the lower level serial driver
|
|
// for 3 reasons:
|
|
//
|
|
// 1) The wait reason was actually satisfied.
|
|
//
|
|
// 2) The irp was cancelled.
|
|
//
|
|
// 3) A setmask came in.
|
|
//
|
|
// How do we deal with "1"?
|
|
//
|
|
// We can tell we have reason 1 if the status is successful
|
|
// and the mask value is non-zero.
|
|
//
|
|
// We have subcases here:
|
|
//
|
|
// a) The irp did complete however the reason is because
|
|
// of the complementry mask operation. In this case
|
|
// we want to deal with the complementary mask state
|
|
// and resubmit ourselves down to the lower serial driver.
|
|
//
|
|
// b) The operations is actually satisfied. We need to complete
|
|
// the operation, however we also need to determine if
|
|
// the complementary state needs to be dealt with. This
|
|
// could mean completing a shuttled wait or recording
|
|
// the current events in its history mask.
|
|
//
|
|
// Somewhat of a subcase (we'll call it 1c) is if a dcd sniff
|
|
// sneaked in on us. This could cause a completion with a bad
|
|
// status. However, in this case we simply shuttle aside the wait.
|
|
//
|
|
// How do we deal with "2"?
|
|
//
|
|
// We can tell we have reason 2 because the status in the
|
|
// irp will be cancelled. We will let the irp continue
|
|
// on to completion. HOWEVER, we also need to see if the
|
|
// other handle had an irp that was shuttled aside. If the
|
|
// other irp was shuttled aside, we should cause that irp to
|
|
// be sent down to the lower serial driver.
|
|
//
|
|
// How do we deal with "3"?
|
|
//
|
|
// We know we have reason 3 when we have a successful status
|
|
// but the mask is zero. There are 3 different ways that
|
|
// a setmask can come in.
|
|
//
|
|
// a) A setmask from our own handle.
|
|
//
|
|
// b) A setmask from the modem driver in response to
|
|
// dcd sniff request.
|
|
//
|
|
// c) A setmask from the other handle.
|
|
//
|
|
// What is key to what we are going to do here is that
|
|
// while the IRP may have completed for ANY of "a", "b"
|
|
// or "c" here, ALL we care about is whether our own handle
|
|
// did the setmask. If our handle DIDN'T do a setmask while
|
|
// this irp was passed down, then all we want to do is shuttle
|
|
// the irp aside.
|
|
//
|
|
// What we've done is to mark any passed down irp from our
|
|
// handle when we do a setmask. When we get to here, if it's
|
|
// marked, complete it. If it's not marked, shuttle it aside.
|
|
//
|
|
// Note that the actions for 3 are the same as for "1a"
|
|
//
|
|
|
|
ULONG maskValue = *((PULONG)Irp->AssociatedIrp.SystemBuffer);
|
|
PMASKSTATE thisState = Context;
|
|
PMASKSTATE otherState = thisState->OtherState;
|
|
KIRQL origIrql;
|
|
|
|
|
|
KeAcquireSpinLock(
|
|
&otherState->Extension->DeviceLock,
|
|
&origIrql
|
|
);
|
|
|
|
//
|
|
// Clear ourself out of passed down.
|
|
//
|
|
thisState->PassedDownWait = NULL;
|
|
|
|
//
|
|
// Take care of "1c & 3". This is when the wait completes with an
|
|
// invalid parameters status. This can only occur if another
|
|
// wait sneaked in ahead of us. This only occurs if we switched
|
|
// into the DCD sniff state. We handle this case by making the
|
|
// irp a shuttled wait.
|
|
//
|
|
|
|
if ((Irp->IoStatus.Status == STATUS_INVALID_PARAMETER) ||
|
|
(NT_SUCCESS(Irp->IoStatus.Status) && !maskValue)) {
|
|
|
|
PIRP junk;
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = 0UL;
|
|
|
|
//
|
|
// First make sure that even that it didn't get
|
|
// hit with a setmask trying to kill it also. If
|
|
// it did, then we should run it down.
|
|
//
|
|
|
|
if (thisState->CompletePassedDownWait) {
|
|
|
|
KeReleaseSpinLock(
|
|
&otherState->Extension->DeviceLock,
|
|
origIrql
|
|
);
|
|
|
|
//
|
|
// While we may have bumped into a dcd setting
|
|
// it's probably nicer if we just say we got
|
|
// killed by the setmask. We already adjusted
|
|
// the status above so just make sure that
|
|
// the system buffer is zero. We return
|
|
// STATUS_SUCCESS now so that the iosubsystem
|
|
// will complete this request.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = sizeof(ULONG);
|
|
|
|
*((PULONG)Irp->AssociatedIrp.SystemBuffer) = 0UL;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
UniMakeIrpShuttledWait(
|
|
thisState,
|
|
Irp,
|
|
origIrql,
|
|
FALSE,
|
|
&junk
|
|
);
|
|
|
|
//
|
|
// We say more processing required so that the io subsystem
|
|
// will leave this irp alone. The irp has actually been
|
|
// shuttled (or been completed because of cancelling)
|
|
// at this point.
|
|
//
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} else if (NT_SUCCESS(Irp->IoStatus.Status) && maskValue) {
|
|
|
|
//
|
|
// This is where we deal with scenario 1. The most
|
|
// important feature in this case is that our mask
|
|
// processing code NEVER allows a new regular wait operation
|
|
// down into the lower level serial driver when we have a
|
|
// current wait operation. However it does let down
|
|
// new setmasks. We need to resubmit the wait if the maskValue
|
|
// was for the complementry mask. However, we shouldn't resubmit
|
|
// it if our mask state structure implies that we would actually
|
|
// be completing this irp if an event hadn't occured.
|
|
//
|
|
|
|
if (otherState->Mask & maskValue) {
|
|
|
|
if (otherState->ShuttledWait) {
|
|
|
|
//
|
|
// Rundown the shuttled wait.
|
|
//
|
|
PIRP savedIrp = otherState->ShuttledWait;
|
|
|
|
otherState->ShuttledWait = NULL;
|
|
|
|
//
|
|
// Set this back cause the lock is going to get
|
|
// released and we DON'T want a new irp to
|
|
// slip in.
|
|
//
|
|
thisState->PassedDownWait = Irp;
|
|
|
|
UniRundownShuttledWait(
|
|
otherState->Extension,
|
|
&otherState->ShuttledWait,
|
|
UNI_REFERENCE_NORMAL_PATH,
|
|
savedIrp,
|
|
origIrql,
|
|
STATUS_SUCCESS,
|
|
(ULONG)otherState->Mask & maskValue
|
|
);
|
|
|
|
KeAcquireSpinLock(
|
|
&thisState->Extension->DeviceLock,
|
|
&origIrql
|
|
);
|
|
thisState->PassedDownWait = NULL;
|
|
|
|
} else {
|
|
|
|
//
|
|
// No shuttled wait, update the others
|
|
// history mask.
|
|
//
|
|
|
|
otherState->HistoryMask |= otherState->Mask & maskValue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (thisState->Mask & maskValue) {
|
|
|
|
//
|
|
// If there is a shuttled wait, send it on down if possible.
|
|
//
|
|
// Note that the call will release the spinlock.
|
|
//
|
|
|
|
if (otherState->ShuttledWait) {
|
|
|
|
UniChangeShuttledToPassDown(
|
|
otherState,
|
|
origIrql
|
|
);
|
|
|
|
} else {
|
|
|
|
KeReleaseSpinLock(
|
|
&otherState->Extension->DeviceLock,
|
|
origIrql
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ((thisState->SentDownSetMasks <
|
|
thisState->SetMaskCount) ||
|
|
thisState->CompletePassedDownWait) {
|
|
|
|
*((PULONG)Irp->AssociatedIrp.SystemBuffer) = 0UL;
|
|
|
|
if (otherState->ShuttledWait) {
|
|
|
|
UniChangeShuttledToPassDown(
|
|
otherState,
|
|
origIrql
|
|
);
|
|
|
|
} else {
|
|
|
|
KeReleaseSpinLock(
|
|
&otherState->Extension->DeviceLock,
|
|
origIrql
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(Irp);
|
|
|
|
nextSp->MajorFunction = irpSp->MajorFunction;
|
|
nextSp->MinorFunction = irpSp->MinorFunction;
|
|
nextSp->Flags = irpSp->Flags;
|
|
nextSp->Parameters.DeviceIoControl.OutputBufferLength =
|
|
irpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
|
nextSp->Parameters.DeviceIoControl.IoControlCode =
|
|
IOCTL_SERIAL_WAIT_ON_MASK;
|
|
nextSp->Parameters.DeviceIoControl.Type3InputBuffer =
|
|
irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
|
|
//
|
|
// Setup so that upon the lower level serial drivers completion
|
|
// we decrement the reference count and such.
|
|
//
|
|
|
|
IoSetCompletionRoutine(
|
|
Irp,
|
|
UniGeneralWaitComplete,
|
|
thisState,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
thisState->PassedDownWait = Irp;
|
|
thisState->CompletePassedDownWait = FALSE;
|
|
|
|
KeReleaseSpinLock(
|
|
&thisState->Extension->DeviceLock,
|
|
origIrql
|
|
);
|
|
|
|
IoCallDriver(
|
|
thisState->Extension->AttachedDeviceObject,
|
|
Irp
|
|
);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// The other was all taken care of. We are done with this irp
|
|
// and we return a succesful status so the irp will actually complete.
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} else if (Irp->IoStatus.Status == STATUS_CANCELLED) {
|
|
|
|
//
|
|
// Take care of "2".
|
|
//
|
|
//
|
|
// Ours was cancelled. Just let the cancel go ahead.
|
|
//
|
|
// Try to start off the other wait if there is one.
|
|
// The other wait had a cancel routine and it might
|
|
// be gone or going also.
|
|
//
|
|
|
|
if (otherState->ShuttledWait) {
|
|
|
|
UniChangeShuttledToPassDown(
|
|
otherState,
|
|
origIrql
|
|
);
|
|
|
|
} else {
|
|
|
|
KeReleaseSpinLock(
|
|
&otherState->Extension->DeviceLock,
|
|
origIrql
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// We return success so that the irp finishes off. This does
|
|
// not change the fact that it is cancelled.
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We really should have taken care of everything above.
|
|
//
|
|
|
|
ASSERT(FALSE);
|
|
|
|
KeReleaseSpinLock(
|
|
&otherState->Extension->DeviceLock,
|
|
origIrql
|
|
);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
UniGeneralMaskComplete(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deal with finishing mask function
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object for the modem
|
|
|
|
Irp - The irp being completed.
|
|
|
|
Context - Points to the mask state for the client or control handle
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status of the call
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// Decrement the reference count on the setmasks for this handle
|
|
// under the protection of the lock.
|
|
//
|
|
|
|
PMASKSTATE maskState = Context;
|
|
|
|
KIRQL oldIrql;
|
|
|
|
KeAcquireSpinLock(
|
|
&maskState->Extension->DeviceLock,
|
|
&oldIrql
|
|
);
|
|
|
|
maskState->SetMaskCount--;
|
|
maskState->SentDownSetMasks--;
|
|
|
|
//
|
|
// Additionally we want to clean out any bits in the history
|
|
// mask that we no longer care about from this handle
|
|
// (as long as the setmask was successful).
|
|
//
|
|
|
|
if (NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
|
|
UNI_RESTORE_OLD_SETMASK(Irp);
|
|
maskState->Mask = *((PULONG)Irp->AssociatedIrp.SystemBuffer);
|
|
maskState->HistoryMask &= maskState->Mask;
|
|
|
|
}
|
|
|
|
KeReleaseSpinLock(
|
|
&maskState->Extension->DeviceLock,
|
|
oldIrql
|
|
);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
VOID
|
|
UniRundownShuttledWait(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PIRP *ShuttlePointer,
|
|
IN ULONG ReferenceMask,
|
|
IN PIRP IrpToRunDown,
|
|
IN KIRQL DeviceLockIrql,
|
|
IN NTSTATUS StatusToComplete,
|
|
IN ULONG MaskCompleteValue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine rundowns (and completes) shuttled aside wait irps.
|
|
|
|
Note that we come in assuming that the device lock is held.
|
|
|
|
Note that this routine assumes NO responsibility for starting
|
|
new irps.
|
|
|
|
Arguments:
|
|
|
|
Extension - The device extension for the particular modem.
|
|
|
|
ShuttlePointer - Pointer to the pointer to the irp that we
|
|
will try to rundown.
|
|
|
|
ReferenceMask - Bit to clear in the reference mask for the irp
|
|
we are trying to rundown.
|
|
|
|
IrpToRunDown - The irp that we will actually complete if all the
|
|
references are gone at the end of this routine.
|
|
|
|
DeviceLockIrql - The old irql when the caller acquired the
|
|
device lock.
|
|
|
|
StatusToComplete - The status to use to complete the irp if
|
|
this call can actually complete it.
|
|
|
|
MaskCompleteValue - The value to put in for the completed event
|
|
mask if this routine actually completes the
|
|
irp.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
BOOLEAN actuallyCompleteIt = FALSE;
|
|
KIRQL cancelIrql;
|
|
|
|
//
|
|
// Clear the requested reference bit in the irp.
|
|
//
|
|
|
|
UNI_CLEAR_REFERENCE(
|
|
IrpToRunDown,
|
|
ReferenceMask
|
|
);
|
|
|
|
//
|
|
// We first acquire the cancel spinlock and try to clear out the
|
|
// cancel routine.
|
|
//
|
|
|
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|
|
|
if (IrpToRunDown->CancelRoutine) {
|
|
|
|
IrpToRunDown->CancelRoutine = NULL;
|
|
UNI_CLEAR_REFERENCE(
|
|
IrpToRunDown,
|
|
UNI_REFERENCE_CANCEL_PATH
|
|
);
|
|
|
|
}
|
|
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
|
|
if (*ShuttlePointer) {
|
|
|
|
*ShuttlePointer = NULL;
|
|
UNI_CLEAR_REFERENCE(
|
|
IrpToRunDown,
|
|
UNI_REFERENCE_NORMAL_PATH
|
|
);
|
|
|
|
}
|
|
|
|
actuallyCompleteIt = !UNI_REFERENCE_COUNT(IrpToRunDown);
|
|
|
|
KeReleaseSpinLock(
|
|
&Extension->DeviceLock,
|
|
DeviceLockIrql
|
|
);
|
|
|
|
if (actuallyCompleteIt) {
|
|
|
|
PULONG maskValue = IrpToRunDown->AssociatedIrp.SystemBuffer;
|
|
IrpToRunDown->IoStatus.Status = StatusToComplete;
|
|
IrpToRunDown->IoStatus.Information = sizeof(ULONG);
|
|
*maskValue = MaskCompleteValue;
|
|
IoCompleteRequest(
|
|
IrpToRunDown,
|
|
IO_NO_INCREMENT
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VOID
|
|
UniCancelShuttledWait(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will start the rundown of a wait operation that had
|
|
been shuttled aside and has now been cancelled (for one reason
|
|
or another).
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The device object of the modem.
|
|
|
|
Irp - This is the irp to cancel. Note that this irp will
|
|
have stashed away in it a pointer to the maskstate
|
|
that should be used to cancel this irp.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
KIRQL origIrql;
|
|
PMASKSTATE thisState = UNI_GET_STATE_IN_IRP(Irp);
|
|
//
|
|
// This lets the rest of the world move on.
|
|
//
|
|
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
//
|
|
// Now attempt to rundown this irp. We need to first acquire
|
|
// the device lock. We can get ahold of everything because
|
|
// the state for this irp is hiding in the stack location.
|
|
//
|
|
|
|
KeAcquireSpinLock(
|
|
&thisState->Extension->DeviceLock,
|
|
&origIrql
|
|
);
|
|
|
|
UNI_CLEAR_STATE_IN_IRP(Irp);
|
|
|
|
UniRundownShuttledWait(
|
|
thisState->Extension,
|
|
&thisState->ShuttledWait,
|
|
UNI_REFERENCE_CANCEL_PATH,
|
|
Irp,
|
|
origIrql,
|
|
STATUS_CANCELLED,
|
|
0ul
|
|
);
|
|
|
|
}
|
|
|
|
VOID
|
|
UniChangeShuttledToPassDown(
|
|
IN PMASKSTATE ChangingState,
|
|
IN KIRQL OrigIrql
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is responsible for changing a shuttled aside wait
|
|
into a passed down wait.
|
|
|
|
NOTE: It is called with the device lock held.
|
|
|
|
NOTE: Two things could "abort" the move. One, we catch the irp
|
|
in a cancelled state. Two, we moved into a dcd sniff state.
|
|
|
|
Arguments:
|
|
|
|
ChangingState - The state that the irp who we wish to pass down
|
|
is part of.
|
|
|
|
OrigIrql - The previous irql to when we acquired the device lock.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
KIRQL cancelIrql;
|
|
//
|
|
// We are in here with the lock. If it was cancelled run it down.
|
|
//
|
|
|
|
ASSERT(!ChangingState->PassedDownWait);
|
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|
|
|
if (ChangingState->ShuttledWait->CancelRoutine) {
|
|
|
|
//
|
|
// It hasn't been cancelled yet. Pull it out of the
|
|
// cancellable state.
|
|
//
|
|
|
|
ChangingState->ShuttledWait->CancelRoutine = NULL;
|
|
UNI_CLEAR_REFERENCE(
|
|
ChangingState->ShuttledWait,
|
|
UNI_REFERENCE_CANCEL_PATH
|
|
);
|
|
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
|
|
//
|
|
// It hasn't been cancelled, We should now check if we are in the
|
|
// dcd sniff state. If we aren't then we can change to passed
|
|
// down.
|
|
//
|
|
|
|
if (ChangingState->Extension->PassThrough !=
|
|
MODEM_DCDSNIFF) {
|
|
|
|
PIO_STACK_LOCATION irpSp =
|
|
IoGetCurrentIrpStackLocation(ChangingState->ShuttledWait);
|
|
PIO_STACK_LOCATION nextSp =
|
|
IoGetNextIrpStackLocation(ChangingState->ShuttledWait);
|
|
|
|
//
|
|
// Not in the passthrough state. We can pass it down.
|
|
//
|
|
|
|
UNI_CLEAR_REFERENCE(
|
|
ChangingState->ShuttledWait,
|
|
UNI_REFERENCE_NORMAL_PATH
|
|
);
|
|
UNI_CLEAR_STATE_IN_IRP(ChangingState->ShuttledWait);
|
|
ChangingState->PassedDownWait = ChangingState->ShuttledWait;
|
|
ChangingState->ShuttledWait = NULL;
|
|
ChangingState->CompletePassedDownWait = FALSE;
|
|
|
|
nextSp->MajorFunction = irpSp->MajorFunction;
|
|
nextSp->MinorFunction = irpSp->MinorFunction;
|
|
nextSp->Flags = irpSp->Flags;
|
|
nextSp->Parameters.DeviceIoControl.IoControlCode =
|
|
IOCTL_SERIAL_WAIT_ON_MASK;
|
|
nextSp->Parameters.DeviceIoControl.OutputBufferLength =
|
|
irpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
|
nextSp->Parameters.DeviceIoControl.Type3InputBuffer =
|
|
irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
|
|
IoSetCompletionRoutine(
|
|
ChangingState->PassedDownWait,
|
|
UniGeneralWaitComplete,
|
|
ChangingState,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
//
|
|
// We now can release the device lock and send the irp on
|
|
// down. Note one small glitch here is that between the
|
|
// time that we release the lock and when we send down the
|
|
// irp, we can enter into a DCD sniff state, and the modem
|
|
// drivers wait might make it through ahead of ours. We will
|
|
// be ok through because the completion routine will simply
|
|
// turn this back into a shuttled wait.
|
|
//
|
|
|
|
KeReleaseSpinLock(
|
|
&ChangingState->Extension->DeviceLock,
|
|
OrigIrql
|
|
);
|
|
|
|
IoCallDriver(
|
|
ChangingState->Extension->AttachedDeviceObject,
|
|
ChangingState->PassedDownWait
|
|
);
|
|
|
|
} else {
|
|
|
|
PIRP junk;
|
|
//
|
|
// Well, we (potentially) moved into a dcd sniff while we were figuring
|
|
// stuff out. We should change it back into a shuttled wait.
|
|
//
|
|
|
|
UniMakeIrpShuttledWait(
|
|
ChangingState,
|
|
ChangingState->ShuttledWait,
|
|
OrigIrql,
|
|
FALSE,
|
|
&junk
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Gack! It's been cancelled. Release the cancel lock and
|
|
// run it down.
|
|
//
|
|
|
|
PIRP savedIrp = ChangingState->ShuttledWait;
|
|
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
ChangingState->ShuttledWait = NULL;
|
|
|
|
//
|
|
// Before we actually run this state down, under the presumption
|
|
// that we really want a wait operation down in the lower serial
|
|
// driver, see if the "other" state has a shuttled wait. If it
|
|
// does, try to send it down (we can do this by calling ourself.
|
|
//
|
|
|
|
if (ChangingState->OtherState->ShuttledWait) {
|
|
|
|
KIRQL recallIrql;
|
|
|
|
UniChangeShuttledToPassDown(
|
|
ChangingState->OtherState,
|
|
OrigIrql
|
|
);
|
|
|
|
KeAcquireSpinLock(
|
|
&ChangingState->Extension->DeviceLock,
|
|
&recallIrql
|
|
);
|
|
OrigIrql = recallIrql;
|
|
|
|
}
|
|
|
|
UniRundownShuttledWait(
|
|
ChangingState->Extension,
|
|
&ChangingState->ShuttledWait,
|
|
UNI_REFERENCE_NORMAL_PATH,
|
|
savedIrp,
|
|
OrigIrql,
|
|
STATUS_CANCELLED,
|
|
0UL
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
UniMakeIrpShuttledWait(
|
|
IN PMASKSTATE MaskState,
|
|
IN PIRP Irp,
|
|
IN KIRQL OrigIrql,
|
|
IN BOOLEAN GetNextIrpInQueue,
|
|
OUT PIRP *NewIrp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is responsible for taking an irp and making it
|
|
a shuttled aside wait. It works on the irp regardless of whether
|
|
it had already been shuttled aside or if it's a new irp.
|
|
|
|
NOTE: It is called with the device lock held.
|
|
|
|
NOTE: Note that it can result in the irp being completed because
|
|
it was cancelled.
|
|
|
|
Arguments:
|
|
|
|
MaskState - The mask state that the irp is to become part of.
|
|
|
|
Irp - The irp to make shuttled.
|
|
|
|
OrigIrql - The old irql when the device lock was acquired.
|
|
|
|
GetNextIrpInQueue - When done making the irp shuttled, this
|
|
will be used to determine if we should
|
|
try to get the next irp in the mask list.
|
|
|
|
NewIrp - If we do get the next irp, this points to it.
|
|
|
|
Return Value:
|
|
|
|
If we actually have to complete an irp this will be the status
|
|
of the completion. If we don't complete it, we give make
|
|
STATUS_PENDING because the IRP is left shuttled aside.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
KIRQL cancelIrql;
|
|
|
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|
|
|
//
|
|
// Since we are about to put the irp into a cancelable
|
|
// state we need to make sure that it hasn't already
|
|
// been canceled. If it has, then we should NOT let
|
|
// it proceed.
|
|
//
|
|
|
|
if (Irp->Cancel) {
|
|
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
KeReleaseSpinLock(
|
|
&MaskState->Extension->DeviceLock,
|
|
OrigIrql
|
|
);
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
Irp->IoStatus.Information = 0L;
|
|
|
|
if (GetNextIrpInQueue) {
|
|
|
|
UniGetNextIrp(
|
|
&MaskState->Extension->DeviceLock,
|
|
&MaskState->Extension->CurrentMaskOp,
|
|
&MaskState->Extension->MaskOps,
|
|
NewIrp,
|
|
TRUE
|
|
);
|
|
|
|
}
|
|
|
|
return STATUS_CANCELLED;
|
|
|
|
}
|
|
|
|
//
|
|
// Make this irp a shuttled aside wait.
|
|
//
|
|
|
|
IoMarkIrpPending(Irp);
|
|
MaskState->ShuttledWait = Irp;
|
|
ASSERT(!MaskState->PassedDownWait);
|
|
|
|
UNI_INIT_REFERENCE(
|
|
Irp
|
|
);
|
|
|
|
UNI_SET_REFERENCE(
|
|
Irp,
|
|
UNI_REFERENCE_CANCEL_PATH
|
|
);
|
|
UNI_SET_REFERENCE(
|
|
Irp,
|
|
UNI_REFERENCE_NORMAL_PATH
|
|
);
|
|
UNI_SAVE_STATE_IN_IRP(
|
|
Irp,
|
|
MaskState
|
|
);
|
|
IoSetCancelRoutine(
|
|
Irp,
|
|
UniCancelShuttledWait
|
|
);
|
|
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
KeReleaseSpinLock(
|
|
&MaskState->Extension->DeviceLock,
|
|
OrigIrql
|
|
);
|
|
|
|
if (GetNextIrpInQueue) {
|
|
|
|
UniGetNextIrp(
|
|
&MaskState->Extension->DeviceLock,
|
|
&MaskState->Extension->CurrentMaskOp,
|
|
&MaskState->Extension->MaskOps,
|
|
NewIrp,
|
|
FALSE
|
|
);
|
|
|
|
}
|
|
|
|
return STATUS_PENDING;
|
|
|
|
}
|